Skip to content

Commit 51be1ba

Browse files
Validate tree depth in XML parsing (AcademySoftwareFoundation#2232)
This changelist adds validation for the tree depth in XML parsing, preventing an invalid document from triggering a stack overflow. A new unit test has been added to parse a document exceeding the maximum tree depth, verifying that the correct exception is thrown.
1 parent cf87592 commit 51be1ba

File tree

3 files changed

+31
-2
lines changed

3 files changed

+31
-2
lines changed

source/MaterialXFormat/XmlIo.cpp

+10-2
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,16 @@ MATERIALX_NAMESPACE_BEGIN
1919

2020
const string MTLX_EXTENSION = "mtlx";
2121

22+
const int MAX_XML_TREE_DEPTH = 256;
23+
2224
namespace
2325
{
2426

2527
const string XINCLUDE_TAG = "xi:include";
2628
const string XINCLUDE_NAMESPACE = "xmlns:xi";
2729
const string XINCLUDE_URL = "http://www.w3.org/2001/XInclude";
2830

29-
void elementFromXml(const xml_node& xmlNode, ElementPtr elem, const XmlReadOptions* readOptions)
31+
void elementFromXml(const xml_node& xmlNode, ElementPtr elem, const XmlReadOptions* readOptions, int depth = 1)
3032
{
3133
// Store attributes in element.
3234
for (const xml_attribute& xmlAttr : xmlNode.attributes())
@@ -58,9 +60,15 @@ void elementFromXml(const xml_node& xmlNode, ElementPtr elem, const XmlReadOptio
5860
continue;
5961
}
6062

63+
// Enforce maximum tree depth.
64+
if (depth >= MAX_XML_TREE_DEPTH)
65+
{
66+
throw ExceptionParseError("Maximum tree depth exceeded.");
67+
}
68+
6169
// Create the new element.
6270
ElementPtr child = elem->addChildOfCategory(category, name);
63-
elementFromXml(xmlChild, child, readOptions);
71+
elementFromXml(xmlChild, child, readOptions, depth + 1);
6472

6573
// Handle the interpretation of XML comments and newlines.
6674
if (readOptions && category.empty())

source/MaterialXFormat/XmlIo.h

+2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ class XmlReadOptions;
2222

2323
extern MX_FORMAT_API const string MTLX_EXTENSION;
2424

25+
extern MX_FORMAT_API const int MAX_XML_TREE_DEPTH;
26+
2527
/// A standard function that reads from an XML file into a Document, with
2628
/// optional search path and read options.
2729
using XmlReadFunction = std::function<void(DocumentPtr, const FilePath&, const FileSearchPath&, const XmlReadOptions*)>;

source/MaterialXTest/MaterialXFormat/XmlIo.cpp

+19
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,25 @@ TEST_CASE("Comments and newlines", "[xmlio]")
262262
REQUIRE(origXml == newXml);
263263
}
264264

265+
TEST_CASE("Maximum tree depth", "[xmlio]")
266+
{
267+
// Create a document that exceeds the maximum tree depth.
268+
mx::DocumentPtr doc = mx::createDocument();
269+
mx::ElementPtr elem = doc;
270+
for (int i = 0; i < mx::MAX_XML_TREE_DEPTH + 1; i++)
271+
{
272+
elem = elem->addChild<mx::NodeGraph>();
273+
}
274+
275+
// Write the document to a string buffer.
276+
std::string xmlString = mx::writeToXmlString(doc);
277+
278+
// Read the string buffer as a document, verifying that the correct
279+
// exception is thrown.
280+
mx::DocumentPtr newDoc = mx::createDocument();
281+
REQUIRE_THROWS_AS(mx::readFromXmlString(newDoc, xmlString), mx::ExceptionParseError);
282+
}
283+
265284
TEST_CASE("Fuzz testing", "[xmlio]")
266285
{
267286
mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath();

0 commit comments

Comments
 (0)