hpack/decoder: add bound check to peek_u8 (#175)
This updates `peek_u8` in hpack decoder to internally perform bound checking. This ensures it cannot access bytes out of range once reaching the end of the buffer.
This commit is contained in:
@@ -177,11 +177,11 @@ impl Decoder {
|
|||||||
|
|
||||||
trace!("decode");
|
trace!("decode");
|
||||||
|
|
||||||
while src.has_remaining() {
|
while let Some(ty) = peek_u8(src) {
|
||||||
// At this point we are always at the beginning of the next block
|
// At this point we are always at the beginning of the next block
|
||||||
// within the HPACK data. The type of the block can always be
|
// within the HPACK data. The type of the block can always be
|
||||||
// determined from the first byte.
|
// determined from the first byte.
|
||||||
match Representation::load(peek_u8(src))? {
|
match Representation::load(ty)? {
|
||||||
Indexed => {
|
Indexed => {
|
||||||
trace!(" Indexed; rem={:?}", src.remaining());
|
trace!(" Indexed; rem={:?}", src.remaining());
|
||||||
can_resize = false;
|
can_resize = false;
|
||||||
@@ -278,12 +278,11 @@ impl Decoder {
|
|||||||
fn decode_string(&mut self, buf: &mut Cursor<Bytes>) -> Result<Bytes, DecoderError> {
|
fn decode_string(&mut self, buf: &mut Cursor<Bytes>) -> Result<Bytes, DecoderError> {
|
||||||
const HUFF_FLAG: u8 = 0b10000000;
|
const HUFF_FLAG: u8 = 0b10000000;
|
||||||
|
|
||||||
if !buf.has_remaining() {
|
|
||||||
return Err(DecoderError::UnexpectedEndOfStream);
|
|
||||||
}
|
|
||||||
|
|
||||||
// The first bit in the first byte contains the huffman encoded flag.
|
// The first bit in the first byte contains the huffman encoded flag.
|
||||||
let huff = peek_u8(buf) & HUFF_FLAG == HUFF_FLAG;
|
let huff = match peek_u8(buf) {
|
||||||
|
Some(hdr) => (hdr & HUFF_FLAG) == HUFF_FLAG,
|
||||||
|
None => return Err(DecoderError::UnexpectedEndOfStream),
|
||||||
|
};
|
||||||
|
|
||||||
// Decode the string length using 7 bit prefix
|
// Decode the string length using 7 bit prefix
|
||||||
let len = decode_int(buf, 7)?;
|
let len = decode_int(buf, 7)?;
|
||||||
@@ -405,8 +404,12 @@ fn decode_int<B: Buf>(buf: &mut B, prefix_size: u8) -> Result<usize, DecoderErro
|
|||||||
Err(DecoderError::IntegerUnderflow)
|
Err(DecoderError::IntegerUnderflow)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn peek_u8<B: Buf>(buf: &mut B) -> u8 {
|
fn peek_u8<B: Buf>(buf: &mut B) -> Option<u8> {
|
||||||
buf.bytes()[0]
|
if buf.has_remaining() {
|
||||||
|
Some(buf.bytes()[0])
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn take(buf: &mut Cursor<Bytes>, n: usize) -> Bytes {
|
fn take(buf: &mut Cursor<Bytes>, n: usize) -> Bytes {
|
||||||
@@ -762,3 +765,28 @@ pub fn get_static(idx: usize) -> Header {
|
|||||||
fn from_static(s: &'static str) -> String<Bytes> {
|
fn from_static(s: &'static str) -> String<Bytes> {
|
||||||
unsafe { String::from_utf8_unchecked(Bytes::from_static(s.as_bytes())) }
|
unsafe { String::from_utf8_unchecked(Bytes::from_static(s.as_bytes())) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_peek_u8() {
|
||||||
|
let b = 0xff;
|
||||||
|
let mut buf = Cursor::new(vec![b]);
|
||||||
|
assert_eq!(peek_u8(&mut buf), Some(b));
|
||||||
|
assert_eq!(buf.get_u8(), b);
|
||||||
|
assert_eq!(peek_u8(&mut buf), None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_decode_string_empty() {
|
||||||
|
let mut de = Decoder::new(0);
|
||||||
|
let buf = Bytes::new();
|
||||||
|
let err = de.decode_string(&mut Cursor::new(buf)).unwrap_err();
|
||||||
|
assert_eq!(err, DecoderError::UnexpectedEndOfStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_decode_empty() {
|
||||||
|
let mut de = Decoder::new(0);
|
||||||
|
let buf = Bytes::new();
|
||||||
|
let empty = de.decode(&mut Cursor::new(buf), |_| {}).unwrap();
|
||||||
|
assert_eq!(empty, ());
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user