diff options
author | Federico Mena Quintero <federico@gnome.org> | 2023-03-13 17:13:52 -0600 |
---|---|---|
committer | Federico Mena Quintero <federico@gnome.org> | 2023-03-13 20:59:18 -0600 |
commit | 61053d9c32dbafe756c38af3fd0bd0005371e153 (patch) | |
tree | 3b166e799fa4d368df9a23d32f353fd4bf3fcbbb | |
parent | a4fe5d9f3c1692d4d36af30947c506d9273fd44f (diff) | |
download | librsvg-61053d9c32dbafe756c38af3fd0bd0005371e153.tar.gz |
(#942): Fix crash when XML files get recursively included through XInclude
There is a new limit, MAX_XINCLUDE_DEPTH, which is a constant with the
maximum level of nesting for XInclude.
We keep a counter of the current nesting level in XmlStateInner, and
check against the limit every time we need to xinclude another XML
document.
The sample file has <xi:include parse="xml" href=""/> which properly
causes the *same* file to be included, per https://www.w3.org/TR/xinclude-11/#include_element
The href attribute is optional; the absence of this attribute is
the same as specifying href="", that is, the reference is to the
same document.
Fixes https://gitlab.gnome.org/GNOME/librsvg/-/issues/942
-rw-r--r-- | src/limits.rs | 10 | ||||
-rw-r--r-- | src/xml/mod.rs | 34 | ||||
-rw-r--r-- | tests/fixtures/crash/bug942-xinclude-recursion.svg | 3 |
3 files changed, 45 insertions, 2 deletions
diff --git a/src/limits.rs b/src/limits.rs index 89e33da0..e287767b 100644 --- a/src/limits.rs +++ b/src/limits.rs @@ -40,3 +40,13 @@ pub const MAX_LOADED_ELEMENTS: usize = 1_000_000; /// of attributes that the SVG standard ascribes meaning to are lower than /// this limit. pub const MAX_LOADED_ATTRIBUTES: usize = u16::MAX as usize; + +/// Maximum level of nesting for XInclude (XML Include) files. +/// +/// See <https://gitlab.gnome.org/GNOME/librsvg/-/issues/942>. With +/// the use of XML like `<xi:include parse="xml" href="foo.xml"/>`, an +/// SVG document can recursively include other XML files. This value +/// defines a maximum level of nesting for XInclude, to prevent cases +/// where the base document is included within itself, or when two +/// documents recursively include each other. +pub const MAX_XINCLUDE_DEPTH: usize = 20; diff --git a/src/xml/mod.rs b/src/xml/mod.rs index a9ccffac..c7493455 100644 --- a/src/xml/mod.rs +++ b/src/xml/mod.rs @@ -25,7 +25,7 @@ use crate::document::{Document, DocumentBuilder}; use crate::error::{ImplementationLimit, LoadingError}; use crate::handle::LoadOptions; use crate::io::{self, IoError}; -use crate::limits::MAX_LOADED_ELEMENTS; +use crate::limits::{MAX_LOADED_ELEMENTS, MAX_XINCLUDE_DEPTH}; use crate::node::{Node, NodeBorrow}; use crate::style::StyleType; use crate::url_resolver::AllowedUrl; @@ -106,6 +106,7 @@ struct XmlStateInner { weak: Option<Weak<XmlState>>, document_builder: Option<DocumentBuilder>, num_loaded_elements: usize, + xinclude_depth: usize, context_stack: Vec<Context>, current_node: Option<Node>, @@ -144,6 +145,7 @@ impl XmlState { weak: None, document_builder: Some(document_builder), num_loaded_elements: 0, + xinclude_depth: 0, context_stack: vec![Context::Start], current_node: None, entities: HashMap::new(), @@ -546,7 +548,7 @@ impl XmlState { // the absence of a default value declaration). Values // other than "xml" and "text" are a fatal error." match parse { - None | Some("xml") => self.acquire_xml(&aurl), + None | Some("xml") => self.include_xml(&aurl), Some("text") => self.acquire_text(&aurl, encoding), @@ -565,6 +567,34 @@ impl XmlState { } } + fn include_xml(&self, aurl: &AllowedUrl) -> Result<(), AcquireError> { + self.increase_xinclude_depth(aurl)?; + + let result = self.acquire_xml(aurl); + + self.decrease_xinclude_depth(); + + result + } + + fn increase_xinclude_depth(&self, aurl: &AllowedUrl) -> Result<(), AcquireError> { + let mut inner = self.inner.borrow_mut(); + + if inner.xinclude_depth == MAX_XINCLUDE_DEPTH { + Err(AcquireError::FatalError(format!( + "exceeded maximum level of nested xinclude in {aurl}" + ))) + } else { + inner.xinclude_depth += 1; + Ok(()) + } + } + + fn decrease_xinclude_depth(&self) { + let mut inner = self.inner.borrow_mut(); + inner.xinclude_depth -= 1; + } + fn acquire_text(&self, aurl: &AllowedUrl, encoding: Option<&str>) -> Result<(), AcquireError> { let session = self .inner diff --git a/tests/fixtures/crash/bug942-xinclude-recursion.svg b/tests/fixtures/crash/bug942-xinclude-recursion.svg new file mode 100644 index 00000000..039639da --- /dev/null +++ b/tests/fixtures/crash/bug942-xinclude-recursion.svg @@ -0,0 +1,3 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xi="http://www.w3.org/2001/XInclude"> + <xi:include parse="xml" href=""/> +</svg> |