From d80aae55b1af0420bfcdecb2c8515b48e3e0e641 Mon Sep 17 00:00:00 2001 From: Pyfisch Date: Sun, 5 Mar 2017 12:26:19 +0100 Subject: [PATCH] fix(headers): prevent 2 panics in QualityItem parsing 1. index out of bounds if semicolon is the last character 2. not a char boundary on non-ASCII input (only allow ASCII now) Bugs found using `cargo fuzz` --- src/header/common/accept.rs | 5 +++++ src/header/shared/quality_item.rs | 13 +++++++++++++ 2 files changed, 18 insertions(+) diff --git a/src/header/common/accept.rs b/src/header/common/accept.rs index f58a143855..5b38ded158 100644 --- a/src/header/common/accept.rs +++ b/src/header/common/accept.rs @@ -114,6 +114,11 @@ header! { SubLevel::Plain, vec![(Attr::Charset, Value::Utf8)]), Quality(500)), ]))); + test_header!( + test_fuzzing1, + vec![b"chunk#;e"], + None + ); } } diff --git a/src/header/shared/quality_item.rs b/src/header/shared/quality_item.rs index e3cb7a27e4..497df37a0d 100644 --- a/src/header/shared/quality_item.rs +++ b/src/header/shared/quality_item.rs @@ -1,3 +1,4 @@ +use std::ascii::AsciiExt; use std::cmp; use std::default::Default; use std::fmt; @@ -73,12 +74,18 @@ impl fmt::Display for QualityItem { impl str::FromStr for QualityItem { type Err = ::Error; fn from_str(s: &str) -> ::Result> { + if !s.is_ascii() { + return Err(::Error::Header); + } // Set defaults used if parsing fails. let mut raw_item = s; let mut quality = 1f32; let parts: Vec<&str> = s.rsplitn(2, ';').map(|x| x.trim()).collect(); if parts.len() == 2 { + if parts[0].len() < 2 { + return Err(::Error::Header); + } let start = &parts[0][0..2]; if start == "q=" || start == "Q=" { let q_part = &parts[0][2..parts[0].len()]; @@ -212,4 +219,10 @@ mod tests { fn test_quality_invalid2() { q(2.0); } + + #[test] + fn test_fuzzing_bugs() { + assert!("99999;".parse::>().is_err()); + assert!("\x0d;;;=\u{d6aa}==".parse::>().is_err()) + } }