mirror of
https://github.com/pocoproject/poco.git
synced 2024-12-12 18:20:26 +01:00
fix(XML): fuzzing stack overflow (#4629). Limit maximum XML element depth.
This commit is contained in:
parent
3a8c6a72b1
commit
3b4a8ea6e7
@ -46,9 +46,14 @@ class XML_API DOMBuilder: protected DTDHandler, protected ContentHandler, protec
|
||||
/// must be supplied to the DOMBuilder.
|
||||
{
|
||||
public:
|
||||
DOMBuilder(XMLReader& xmlReader, NamePool* pNamePool = 0);
|
||||
DOMBuilder(XMLReader& xmlReader, NamePool* pNamePool = 0, std::size_t maxDepth = 0);
|
||||
/// Creates a DOMBuilder using the given XMLReader.
|
||||
/// If a NamePool is given, it becomes the Document's NamePool.
|
||||
///
|
||||
/// The maxDepth parameter can be used to limit the maximum element depth of the
|
||||
/// document. This can be used to prevent excessive element depth, which
|
||||
/// could lead to a stack overflow when destroying the document.
|
||||
/// A maxDepth of 0 does not limit the depth.
|
||||
|
||||
virtual ~DOMBuilder();
|
||||
/// Destroys the DOMBuilder.
|
||||
@ -98,11 +103,13 @@ private:
|
||||
|
||||
XMLReader& _xmlReader;
|
||||
NamePool* _pNamePool;
|
||||
std::size_t _maxDepth;
|
||||
Document* _pDocument;
|
||||
AbstractContainerNode* _pParent;
|
||||
AbstractNode* _pPrevious;
|
||||
bool _inCDATA;
|
||||
bool _namespaces;
|
||||
std::size_t _depth;
|
||||
};
|
||||
|
||||
|
||||
|
@ -99,12 +99,30 @@ public:
|
||||
void setEntityResolver(EntityResolver* pEntityResolver);
|
||||
/// Sets the entity resolver on the underlying SAXParser.
|
||||
|
||||
void setMaxElementDepth(std::size_t limit);
|
||||
/// Limits the maximum element depth of the XML document to be loaded.
|
||||
/// Setting the limit to zero disables the limit.
|
||||
///
|
||||
/// This can be used to prevent excessive element depth, which
|
||||
/// could lead to a stack overflow when destroying the document.
|
||||
///
|
||||
/// The default limit is 256.
|
||||
|
||||
std::size_t getMaxElementDepth() const;
|
||||
/// Returns the maximum element depth.
|
||||
|
||||
static const XMLString FEATURE_FILTER_WHITESPACE;
|
||||
|
||||
enum
|
||||
{
|
||||
DEFAULT_MAX_ELEMENT_DEPTH = 256
|
||||
};
|
||||
|
||||
private:
|
||||
SAXParser _saxParser;
|
||||
NamePool* _pNamePool;
|
||||
bool _filterWhitespace;
|
||||
bool _filterWhitespace = false;
|
||||
std::size_t _maxElementDepth = DEFAULT_MAX_ELEMENT_DEPTH;
|
||||
};
|
||||
|
||||
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include "Poco/DOM/AutoPtr.h"
|
||||
#include "Poco/SAX/XMLReader.h"
|
||||
#include "Poco/SAX/AttributesImpl.h"
|
||||
#include "Poco/XML/XMLException.h"
|
||||
|
||||
|
||||
namespace Poco {
|
||||
@ -37,14 +38,16 @@ namespace XML {
|
||||
const XMLString DOMBuilder::EMPTY_STRING;
|
||||
|
||||
|
||||
DOMBuilder::DOMBuilder(XMLReader& xmlReader, NamePool* pNamePool):
|
||||
DOMBuilder::DOMBuilder(XMLReader& xmlReader, NamePool* pNamePool, std::size_t maxDepth):
|
||||
_xmlReader(xmlReader),
|
||||
_pNamePool(pNamePool),
|
||||
_maxDepth(maxDepth),
|
||||
_pDocument(0),
|
||||
_pParent(0),
|
||||
_pPrevious(0),
|
||||
_inCDATA(false),
|
||||
_namespaces(true)
|
||||
_namespaces(true),
|
||||
_depth(0)
|
||||
{
|
||||
_xmlReader.setContentHandler(this);
|
||||
_xmlReader.setDTDHandler(this);
|
||||
@ -188,6 +191,9 @@ void DOMBuilder::endDocument()
|
||||
|
||||
void DOMBuilder::startElement(const XMLString& uri, const XMLString& localName, const XMLString& qname, const Attributes& attributes)
|
||||
{
|
||||
++_depth;
|
||||
if (_maxDepth > 0 && _depth > _maxDepth) throw XMLException("Maximum element depth exceeded");
|
||||
|
||||
AutoPtr<Element> pElem = _namespaces ? _pDocument->createElementNS(uri, qname.empty() ? localName : qname) : _pDocument->createElement(qname);
|
||||
|
||||
const AttributesImpl& attrs = dynamic_cast<const AttributesImpl&>(attributes);
|
||||
@ -204,6 +210,8 @@ void DOMBuilder::startElement(const XMLString& uri, const XMLString& localName,
|
||||
|
||||
void DOMBuilder::endElement(const XMLString& uri, const XMLString& localName, const XMLString& qname)
|
||||
{
|
||||
--_depth;
|
||||
|
||||
_pPrevious = _pParent;
|
||||
_pParent = static_cast<AbstractContainerNode*>(_pParent->parentNode());
|
||||
}
|
||||
|
@ -28,8 +28,7 @@ const XMLString DOMParser::FEATURE_FILTER_WHITESPACE = toXMLString("http://www.a
|
||||
|
||||
|
||||
DOMParser::DOMParser(NamePool* pNamePool):
|
||||
_pNamePool(pNamePool),
|
||||
_filterWhitespace(false)
|
||||
_pNamePool(pNamePool)
|
||||
{
|
||||
if (_pNamePool) _pNamePool->duplicate();
|
||||
_saxParser.setFeature(XMLReader::FEATURE_NAMESPACES, true);
|
||||
@ -38,8 +37,7 @@ DOMParser::DOMParser(NamePool* pNamePool):
|
||||
|
||||
|
||||
DOMParser::DOMParser(unsigned long namePoolSize):
|
||||
_pNamePool(new NamePool(namePoolSize)),
|
||||
_filterWhitespace(false)
|
||||
_pNamePool(new NamePool(namePoolSize))
|
||||
{
|
||||
_saxParser.setFeature(XMLReader::FEATURE_NAMESPACES, true);
|
||||
_saxParser.setFeature(XMLReader::FEATURE_NAMESPACE_PREFIXES, true);
|
||||
@ -93,12 +91,12 @@ Document* DOMParser::parse(const XMLString& uri)
|
||||
if (_filterWhitespace)
|
||||
{
|
||||
WhitespaceFilter filter(&_saxParser);
|
||||
DOMBuilder builder(filter, _pNamePool);
|
||||
DOMBuilder builder(filter, _pNamePool, _maxElementDepth);
|
||||
return builder.parse(uri);
|
||||
}
|
||||
else
|
||||
{
|
||||
DOMBuilder builder(_saxParser, _pNamePool);
|
||||
DOMBuilder builder(_saxParser, _pNamePool, _maxElementDepth);
|
||||
return builder.parse(uri);
|
||||
}
|
||||
}
|
||||
@ -109,12 +107,12 @@ Document* DOMParser::parse(InputSource* pInputSource)
|
||||
if (_filterWhitespace)
|
||||
{
|
||||
WhitespaceFilter filter(&_saxParser);
|
||||
DOMBuilder builder(filter, _pNamePool);
|
||||
DOMBuilder builder(filter, _pNamePool, _maxElementDepth);
|
||||
return builder.parse(pInputSource);
|
||||
}
|
||||
else
|
||||
{
|
||||
DOMBuilder builder(_saxParser, _pNamePool);
|
||||
DOMBuilder builder(_saxParser, _pNamePool, _maxElementDepth);
|
||||
return builder.parse(pInputSource);
|
||||
}
|
||||
}
|
||||
@ -131,12 +129,12 @@ Document* DOMParser::parseMemory(const char* xml, std::size_t size)
|
||||
if (_filterWhitespace)
|
||||
{
|
||||
WhitespaceFilter filter(&_saxParser);
|
||||
DOMBuilder builder(filter, _pNamePool);
|
||||
DOMBuilder builder(filter, _pNamePool, _maxElementDepth);
|
||||
return builder.parseMemoryNP(xml, size);
|
||||
}
|
||||
else
|
||||
{
|
||||
DOMBuilder builder(_saxParser, _pNamePool);
|
||||
DOMBuilder builder(_saxParser, _pNamePool, _maxElementDepth);
|
||||
return builder.parseMemoryNP(xml, size);
|
||||
}
|
||||
}
|
||||
@ -154,4 +152,16 @@ void DOMParser::setEntityResolver(EntityResolver* pEntityResolver)
|
||||
}
|
||||
|
||||
|
||||
void DOMParser::setMaxElementDepth(std::size_t limit)
|
||||
{
|
||||
_maxElementDepth = limit;
|
||||
}
|
||||
|
||||
|
||||
std::size_t DOMParser::getMaxElementDepth() const
|
||||
{
|
||||
return _maxElementDepth;
|
||||
}
|
||||
|
||||
|
||||
} } // namespace Poco::XML
|
||||
|
@ -104,6 +104,23 @@ void ParserWriterTest::testParseWriteSimple()
|
||||
}
|
||||
|
||||
|
||||
void ParserWriterTest::testMaxElementDepth()
|
||||
{
|
||||
DOMParser parser;
|
||||
parser.setFeature(XMLReader::FEATURE_NAMESPACE_PREFIXES, false);
|
||||
parser.setMaxElementDepth(2);
|
||||
try
|
||||
{
|
||||
AutoPtr<Document> pDoc = parser.parseString(XHTML);
|
||||
fail("max element depth exceeded - must throw");
|
||||
}
|
||||
catch (const Poco::Exception&)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ParserWriterTest::setUp()
|
||||
{
|
||||
}
|
||||
@ -121,6 +138,7 @@ CppUnit::Test* ParserWriterTest::suite()
|
||||
CppUnit_addTest(pSuite, ParserWriterTest, testParseWriteXHTML);
|
||||
CppUnit_addTest(pSuite, ParserWriterTest, testParseWriteXHTML2);
|
||||
CppUnit_addTest(pSuite, ParserWriterTest, testParseWriteSimple);
|
||||
CppUnit_addTest(pSuite, ParserWriterTest, testMaxElementDepth);
|
||||
|
||||
return pSuite;
|
||||
}
|
||||
|
@ -26,8 +26,8 @@ public:
|
||||
|
||||
void testParseWriteXHTML();
|
||||
void testParseWriteXHTML2();
|
||||
void testParseWriteWSDL();
|
||||
void testParseWriteSimple();
|
||||
void testMaxElementDepth();
|
||||
|
||||
void setUp();
|
||||
void tearDown();
|
||||
|
Loading…
Reference in New Issue
Block a user