Merge pull request #1084 from pyfisch/fuzzing

[WIP] fix bugs in header field parsers
This commit is contained in:
Sean McArthur
2017-03-13 10:15:21 -07:00
committed by GitHub
4 changed files with 28 additions and 3 deletions

View File

@@ -114,6 +114,13 @@ header! {
SubLevel::Plain, vec![(Attr::Charset, Value::Utf8)]), SubLevel::Plain, vec![(Attr::Charset, Value::Utf8)]),
Quality(500)), Quality(500)),
]))); ])));
#[test]
fn test_fuzzing1() {
let raw: Raw = "chunk#;e".into();
let header = Accept::parse_header(&raw);
assert!(header.is_ok());
}
} }
} }

View File

@@ -83,6 +83,9 @@ header! {
test_header!(test14, test_header!(test14,
vec![b"matched-\"dquotes\""], vec![b"matched-\"dquotes\""],
None::<ETag>); None::<ETag>);
test_header!(test15,
vec![b"\""],
None::<ETag>);
} }
} }

View File

@@ -123,15 +123,17 @@ impl FromStr for EntityTag {
let length: usize = s.len(); let length: usize = s.len();
let slice = &s[..]; let slice = &s[..];
// Early exits if it doesn't terminate in a DQUOTE. // Early exits if it doesn't terminate in a DQUOTE.
if !slice.ends_with('"') { if !slice.ends_with('"') || slice.len() < 2 {
return Err(::Error::Header); return Err(::Error::Header);
} }
// The etag is weak if its first char is not a DQUOTE. // The etag is weak if its first char is not a DQUOTE.
if slice.starts_with('"') && check_slice_validity(&slice[1..length-1]) { if slice.len() >= 2 && slice.starts_with('"')
&& check_slice_validity(&slice[1..length-1]) {
// No need to check if the last char is a DQUOTE, // No need to check if the last char is a DQUOTE,
// we already did that above. // we already did that above.
return Ok(EntityTag { weak: false, tag: slice[1..length-1].to_owned() }); return Ok(EntityTag { weak: false, tag: slice[1..length-1].to_owned() });
} else if slice.starts_with("W/\"") && check_slice_validity(&slice[3..length-1]) { } else if slice.len() >= 4 && slice.starts_with("W/\"")
&& check_slice_validity(&slice[3..length-1]) {
return Ok(EntityTag { weak: true, tag: slice[3..length-1].to_owned() }); return Ok(EntityTag { weak: true, tag: slice[3..length-1].to_owned() });
} }
Err(::Error::Header) Err(::Error::Header)

View File

@@ -1,3 +1,4 @@
use std::ascii::AsciiExt;
use std::cmp; use std::cmp;
use std::default::Default; use std::default::Default;
use std::fmt; use std::fmt;
@@ -73,12 +74,18 @@ impl<T: fmt::Display> fmt::Display for QualityItem<T> {
impl<T: str::FromStr> str::FromStr for QualityItem<T> { impl<T: str::FromStr> str::FromStr for QualityItem<T> {
type Err = ::Error; type Err = ::Error;
fn from_str(s: &str) -> ::Result<QualityItem<T>> { fn from_str(s: &str) -> ::Result<QualityItem<T>> {
if !s.is_ascii() {
return Err(::Error::Header);
}
// Set defaults used if parsing fails. // Set defaults used if parsing fails.
let mut raw_item = s; let mut raw_item = s;
let mut quality = 1f32; let mut quality = 1f32;
let parts: Vec<&str> = s.rsplitn(2, ';').map(|x| x.trim()).collect(); let parts: Vec<&str> = s.rsplitn(2, ';').map(|x| x.trim()).collect();
if parts.len() == 2 { if parts.len() == 2 {
if parts[0].len() < 2 {
return Err(::Error::Header);
}
let start = &parts[0][0..2]; let start = &parts[0][0..2];
if start == "q=" || start == "Q=" { if start == "q=" || start == "Q=" {
let q_part = &parts[0][2..parts[0].len()]; let q_part = &parts[0][2..parts[0].len()];
@@ -212,4 +219,10 @@ mod tests {
fn test_quality_invalid2() { fn test_quality_invalid2() {
q(2.0); q(2.0);
} }
#[test]
fn test_fuzzing_bugs() {
assert!("99999;".parse::<QualityItem<String>>().is_err());
assert!("\x0d;;;=\u{d6aa}==".parse::<QualityItem<String>>().is_err())
}
} }