Skip to content

Commit 8291538

Browse files
authoredDec 18, 2023
fix(http1): reject chunked headers missing a digit (#3494)
Previously, hyper would decode `\r\n\r\n` as `0\r\n\r\n`. This fixes hyper to require a digit to be present before starting at 0. Reported-by: Ben Kallus <benjamin.p.kallus.gr@dartmouth.edu>
1 parent 21aa2f1 commit 8291538

File tree

1 file changed

+56
-15
lines changed

1 file changed

+56
-15
lines changed
 

‎src/proto/h1/decode.rs

+56-15
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ enum Kind {
4747

4848
#[derive(Debug, PartialEq, Clone, Copy)]
4949
enum ChunkedState {
50+
Start,
5051
Size,
5152
SizeLws,
5253
Extension,
@@ -72,7 +73,7 @@ impl Decoder {
7273

7374
pub(crate) fn chunked() -> Decoder {
7475
Decoder {
75-
kind: Kind::Chunked(ChunkedState::Size, 0),
76+
kind: Kind::Chunked(ChunkedState::new(), 0),
7677
}
7778
}
7879

@@ -180,7 +181,22 @@ macro_rules! byte (
180181
})
181182
);
182183

184+
macro_rules! or_overflow {
185+
($e:expr) => (
186+
match $e {
187+
Some(val) => val,
188+
None => return Poll::Ready(Err(io::Error::new(
189+
io::ErrorKind::InvalidData,
190+
"invalid chunk size: overflow",
191+
))),
192+
}
193+
)
194+
}
195+
183196
impl ChunkedState {
197+
fn new() -> ChunkedState {
198+
ChunkedState::Start
199+
}
184200
fn step<R: MemRead>(
185201
&self,
186202
cx: &mut Context<'_>,
@@ -190,6 +206,7 @@ impl ChunkedState {
190206
) -> Poll<Result<ChunkedState, io::Error>> {
191207
use self::ChunkedState::*;
192208
match *self {
209+
Start => ChunkedState::read_start(cx, body, size),
193210
Size => ChunkedState::read_size(cx, body, size),
194211
SizeLws => ChunkedState::read_size_lws(cx, body),
195212
Extension => ChunkedState::read_extension(cx, body),
@@ -204,25 +221,46 @@ impl ChunkedState {
204221
End => Poll::Ready(Ok(ChunkedState::End)),
205222
}
206223
}
207-
fn read_size<R: MemRead>(
224+
225+
fn read_start<R: MemRead>(
208226
cx: &mut Context<'_>,
209227
rdr: &mut R,
210228
size: &mut u64,
211229
) -> Poll<Result<ChunkedState, io::Error>> {
212-
trace!("Read chunk hex size");
230+
trace!("Read chunk start");
213231

214-
macro_rules! or_overflow {
215-
($e:expr) => (
216-
match $e {
217-
Some(val) => val,
218-
None => return Poll::Ready(Err(io::Error::new(
219-
io::ErrorKind::InvalidData,
220-
"invalid chunk size: overflow",
221-
))),
222-
}
223-
)
232+
let radix = 16;
233+
match byte!(rdr, cx) {
234+
b @ b'0'..=b'9' => {
235+
*size = or_overflow!(size.checked_mul(radix));
236+
*size = or_overflow!(size.checked_add((b - b'0') as u64));
237+
}
238+
b @ b'a'..=b'f' => {
239+
*size = or_overflow!(size.checked_mul(radix));
240+
*size = or_overflow!(size.checked_add((b + 10 - b'a') as u64));
241+
}
242+
b @ b'A'..=b'F' => {
243+
*size = or_overflow!(size.checked_mul(radix));
244+
*size = or_overflow!(size.checked_add((b + 10 - b'A') as u64));
245+
}
246+
_ => {
247+
return Poll::Ready(Err(io::Error::new(
248+
io::ErrorKind::InvalidInput,
249+
"Invalid chunk size line: missing size digit",
250+
)));
251+
}
224252
}
225253

254+
Poll::Ready(Ok(ChunkedState::Size))
255+
}
256+
257+
fn read_size<R: MemRead>(
258+
cx: &mut Context<'_>,
259+
rdr: &mut R,
260+
size: &mut u64,
261+
) -> Poll<Result<ChunkedState, io::Error>> {
262+
trace!("Read chunk hex size");
263+
226264
let radix = 16;
227265
match byte!(rdr, cx) {
228266
b @ b'0'..=b'9' => {
@@ -478,7 +516,7 @@ mod tests {
478516
use std::io::ErrorKind::{InvalidData, InvalidInput, UnexpectedEof};
479517

480518
async fn read(s: &str) -> u64 {
481-
let mut state = ChunkedState::Size;
519+
let mut state = ChunkedState::new();
482520
let rdr = &mut s.as_bytes();
483521
let mut size = 0;
484522
loop {
@@ -495,7 +533,7 @@ mod tests {
495533
}
496534

497535
async fn read_err(s: &str, expected_err: io::ErrorKind) {
498-
let mut state = ChunkedState::Size;
536+
let mut state = ChunkedState::new();
499537
let rdr = &mut s.as_bytes();
500538
let mut size = 0;
501539
loop {
@@ -532,6 +570,9 @@ mod tests {
532570
// Missing LF or CRLF
533571
read_err("F\rF", InvalidInput).await;
534572
read_err("F", UnexpectedEof).await;
573+
// Missing digit
574+
read_err("\r\n\r\n", InvalidInput).await;
575+
read_err("\r\n", InvalidInput).await;
535576
// Invalid hex digit
536577
read_err("X\r\n", InvalidInput).await;
537578
read_err("1X\r\n", InvalidInput).await;

0 commit comments

Comments
 (0)
Please sign in to comment.