From a86340be8985848c7c6e9736eda31594e11aab42 Mon Sep 17 00:00:00 2001 From: Tpt Date: Sun, 11 Jun 2023 15:35:23 +0200 Subject: [PATCH] Fails on empty doctype The parser was crashing with debug assertions enabled but passing with debug assertions disabled Closes #608 --- Changelog.md | 3 +++ src/errors.rs | 5 +++++ src/reader/parser.rs | 4 +++- tests/fuzzing.rs | 20 +++++++++++++++----- 4 files changed, 26 insertions(+), 6 deletions(-) diff --git a/Changelog.md b/Changelog.md index f1bf2801..1cb0acce 100644 --- a/Changelog.md +++ b/Changelog.md @@ -21,6 +21,8 @@ - [#603]: Fix a regression from [#581] that an XML comment or a processing instruction between a and the root element in the file brokes deserialization of structs by returning `DeError::ExpectedStart` +- [#608]: Return a new error `Error::EmptyDocType` on empty doctype instead + of crashing because of a debug assertion. ### Misc Changes @@ -28,6 +30,7 @@ [#601]: https://github.com/tafia/quick-xml/pull/601 [#603]: https://github.com/tafia/quick-xml/pull/603 [#606]: https://github.com/tafia/quick-xml/pull/606 +[#608]: https://github.com/tafia/quick-xml/issues/608 ## 0.28.2 -- 2023-04-12 diff --git a/src/errors.rs b/src/errors.rs index 265855ee..14cd7a5c 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -37,6 +37,10 @@ pub enum Error { /// `Event::BytesDecl` must start with *version* attribute. Contains the attribute /// that was found or `None` if an xml declaration doesn't contain attributes. XmlDeclWithoutVersion(Option), + /// Empty `Event::DocType`. `` is correct but ` is not. + /// + /// See + EmptyDocType, /// Attribute parsing error InvalidAttr(AttrError), /// Escape error @@ -109,6 +113,7 @@ impl fmt::Display for Error { "XmlDecl must start with 'version' attribute, found {:?}", e ), + Error::EmptyDocType => write!(f, "DOCTYPE declaration must not be empty"), Error::InvalidAttr(e) => write!(f, "error while parsing attribute: {}", e), Error::EscapeError(e) => write!(f, "{}", e), Error::UnknownPrefix(prefix) => { diff --git a/src/reader/parser.rs b/src/reader/parser.rs index 1cb60867..b69df6f7 100644 --- a/src/reader/parser.rs +++ b/src/reader/parser.rs @@ -117,7 +117,9 @@ impl Parser { .iter() .position(|b| !is_whitespace(*b)) .unwrap_or(len - 8); - debug_assert!(start < len - 8, "DocType must have a name"); + if start + 8 >= len { + return Err(Error::EmptyDocType); + } Ok(Event::DocType(BytesText::wrap( &buf[8 + start..], self.decoder(), diff --git a/tests/fuzzing.rs b/tests/fuzzing.rs index 69f3b315..9189d661 100644 --- a/tests/fuzzing.rs +++ b/tests/fuzzing.rs @@ -2,15 +2,14 @@ use quick_xml::events::Event; use quick_xml::reader::Reader; -use std::io::Cursor; +use quick_xml::Error; #[test] fn fuzz_53() { let data: &[u8] = b"\xe9\x00\x00\x00\x00\x00\x00\x00\x00\ \x00\x00\x00\x00\n(\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\ \x00<>\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00<<\x00\x00\x00"; - let cursor = Cursor::new(data); - let mut reader = Reader::from_reader(cursor); + let mut reader = Reader::from_reader(data); let mut buf = vec![]; loop { match reader.read_event_into(&mut buf) { @@ -25,8 +24,7 @@ fn fuzz_101() { let data: &[u8] = b"\x00\x00<\x00\x00\x0a>�?#\x0a413518\ #\x0a\x0a\x0a;<:<)(<:\x0a\x0a\x0a\x0a;<:\x0a\x0a\ <:\x0a\x0a\x0a\x0a\x0a<\x00*\x00\x00\x00\x00"; - let cursor = Cursor::new(data); - let mut reader = Reader::from_reader(cursor); + let mut reader = Reader::from_reader(data); let mut buf = vec![]; loop { match reader.read_event_into(&mut buf) { @@ -50,3 +48,15 @@ fn fuzz_101() { buf.clear(); } } + +#[test] +fn fuzz_empty_doctype() { + let data: &[u8] = b""; + let mut reader = Reader::from_reader(data); + let mut buf = Vec::new(); + assert!(matches!( + reader.read_event_into(&mut buf).unwrap_err(), + Error::EmptyDocType + )); + assert_eq!(reader.read_event_into(&mut buf).unwrap(), Event::Eof); +}