check for overly large header field in send_headers

This commit is contained in:
Sean McArthur
2019-05-29 14:40:28 -07:00
parent e512b6ccb6
commit 91819bf25e
5 changed files with 63 additions and 2 deletions

View File

@@ -35,6 +35,9 @@ pub enum UserError {
/// The payload size is too big
PayloadTooBig,
/// A header size is too big
HeaderTooBig,
/// The application attempted to initiate too many streams to remote.
Rejected,
@@ -131,6 +134,7 @@ impl error::Error for UserError {
InactiveStreamId => "inactive stream",
UnexpectedFrameType => "unexpected frame type",
PayloadTooBig => "payload too big",
HeaderTooBig => "header too big",
Rejected => "rejected",
ReleaseCapacityTooBig => "release capacity too big",
OverflowedStreamId => "stream ID overflowed",

View File

@@ -12,6 +12,10 @@ use string::String;
use std::fmt;
use std::io::Cursor;
// Minimum MAX_FRAME_SIZE is 16kb, so save some arbitrary space for frame
// head and other header bits.
const MAX_HEADER_LENGTH: usize = 1024 * 16 - 100;
/// Header frame
///
/// This could be either a request or a response.
@@ -232,6 +236,10 @@ impl Headers {
self.header_block.is_over_size
}
pub(crate) fn has_too_big_field(&self) -> bool {
self.header_block.has_too_big_field()
}
pub fn into_parts(self) -> (Pseudo, HeaderMap) {
(self.header_block.pseudo, self.header_block.fields)
}
@@ -844,6 +852,46 @@ impl HeaderBlock {
.map(|(name, value)| decoded_header_size(name.as_str().len(), value.len()))
.sum::<usize>()
}
/// Iterate over all pseudos and headers to see if any individual pair
/// would be too large to encode.
pub(crate) fn has_too_big_field(&self) -> bool {
macro_rules! pseudo_size {
($name:ident) => ({
self.pseudo
.$name
.as_ref()
.map(|m| decoded_header_size(stringify!($name).len() + 1, m.as_str().len()))
.unwrap_or(0)
});
}
if pseudo_size!(method) > MAX_HEADER_LENGTH {
return true;
}
if pseudo_size!(scheme) > MAX_HEADER_LENGTH {
return true;
}
if pseudo_size!(authority) > MAX_HEADER_LENGTH {
return true;
}
if pseudo_size!(path) > MAX_HEADER_LENGTH {
return true;
}
// skip :status, its never going to be too big
for (name, value) in &self.fields {
if decoded_header_size(name.as_str().len(), value.len()) > MAX_HEADER_LENGTH {
return true;
}
}
false
}
}
fn decoded_header_size(name: usize, value: usize) -> usize {

View File

@@ -1,6 +1,6 @@
mod encoder;
mod decoder;
mod header;
pub(crate) mod header;
mod huffman;
mod table;

View File

@@ -85,6 +85,10 @@ impl Send {
}
}
if frame.has_too_big_field() {
return Err(UserError::HeaderTooBig);
}
let end_stream = frame.is_end_stream();
// Update the state
@@ -216,6 +220,10 @@ impl Send {
return Err(UserError::UnexpectedFrameType);
}
if frame.has_too_big_field() {
return Err(UserError::HeaderTooBig);
}
stream.state.send_close();
trace!("send_trailers -- queuing; frame={:?}", frame);

View File

@@ -180,8 +180,9 @@ where
Ok(()) => Ok(()),
Err(RecvHeaderBlockError::Oversize(resp)) => {
if let Some(resp) = resp {
let _ = actions.send.send_headers(
let sent = actions.send.send_headers(
resp, send_buffer, stream, counts, &mut actions.task);
debug_assert!(sent.is_ok(), "oversize response should not fail");
actions.send.schedule_implicit_reset(
stream,