mirror of
https://gitlab.gnome.org/GNOME/libxml2.git
synced 2025-01-12 09:17:37 +03:00
556 lines
15 KiB
C
556 lines
15 KiB
C
/*
|
|
* xml.c: a libFuzzer target to test several XML parser interfaces.
|
|
*
|
|
* See Copyright for the status of this software.
|
|
*/
|
|
|
|
#include <libxml/catalog.h>
|
|
#include <libxml/parser.h>
|
|
#include <libxml/tree.h>
|
|
#include <libxml/xmlerror.h>
|
|
#include <libxml/xmlreader.h>
|
|
#include <libxml/xmlsave.h>
|
|
#include "fuzz.h"
|
|
|
|
#include <string.h>
|
|
|
|
#if 0
|
|
#define DEBUG
|
|
#endif
|
|
|
|
typedef enum {
|
|
OP_READ = 1,
|
|
OP_READ_INNER_XML,
|
|
OP_READ_OUTER_XML,
|
|
OP_READ_STRING,
|
|
OP_READ_ATTRIBUTE_VALUE,
|
|
OP_ATTRIBUTE_COUNT,
|
|
OP_DEPTH,
|
|
OP_HAS_ATTRIBUTES,
|
|
OP_HAS_VALUE,
|
|
OP_IS_DEFAULT,
|
|
OP_IS_EMPTY_ELEMENT,
|
|
OP_NODE_TYPE,
|
|
OP_QUOTE_CHAR,
|
|
OP_READ_STATE,
|
|
OP_IS_NAMESPACE_DECL,
|
|
OP_CONST_BASE_URI,
|
|
OP_CONST_LOCAL_NAME,
|
|
OP_CONST_NAME,
|
|
OP_CONST_NAMESPACE_URI,
|
|
OP_CONST_PREFIX,
|
|
OP_CONST_XML_LANG,
|
|
OP_CONST_VALUE,
|
|
OP_BASE_URI,
|
|
OP_LOCAL_NAME,
|
|
OP_NAME,
|
|
OP_NAMESPACE_URI,
|
|
OP_PREFIX,
|
|
OP_XML_LANG,
|
|
OP_VALUE,
|
|
OP_CLOSE,
|
|
OP_GET_ATTRIBUTE_NO,
|
|
OP_GET_ATTRIBUTE,
|
|
OP_GET_ATTRIBUTE_NS,
|
|
OP_GET_REMAINDER,
|
|
OP_LOOKUP_NAMESPACE,
|
|
OP_MOVE_TO_ATTRIBUTE_NO,
|
|
OP_MOVE_TO_ATTRIBUTE,
|
|
OP_MOVE_TO_ATTRIBUTE_NS,
|
|
OP_MOVE_TO_FIRST_ATTRIBUTE,
|
|
OP_MOVE_TO_NEXT_ATTRIBUTE,
|
|
OP_MOVE_TO_ELEMENT,
|
|
OP_NORMALIZATION,
|
|
OP_CONST_ENCODING,
|
|
OP_GET_PARSER_PROP,
|
|
OP_CURRENT_NODE,
|
|
OP_GET_PARSER_LINE_NUMBER,
|
|
OP_GET_PARSER_COLUMN_NUMBER,
|
|
OP_PRESERVE,
|
|
OP_CURRENT_DOC,
|
|
OP_EXPAND,
|
|
OP_NEXT,
|
|
OP_NEXT_SIBLING,
|
|
OP_IS_VALID,
|
|
OP_CONST_XML_VERSION,
|
|
OP_STANDALONE,
|
|
OP_BYTE_CONSUMED,
|
|
|
|
OP_MAX
|
|
} opType;
|
|
|
|
static void
|
|
startOp(const char *name) {
|
|
(void) name;
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "%s\n", name);
|
|
#endif
|
|
}
|
|
|
|
int
|
|
LLVMFuzzerInitialize(int *argc ATTRIBUTE_UNUSED,
|
|
char ***argv ATTRIBUTE_UNUSED) {
|
|
xmlFuzzMemSetup();
|
|
xmlInitParser();
|
|
#ifdef LIBXML_CATALOG_ENABLED
|
|
xmlInitializeCatalog();
|
|
xmlCatalogSetDefaults(XML_CATA_ALLOW_NONE);
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
LLVMFuzzerTestOneInput(const char *data, size_t size) {
|
|
xmlTextReaderPtr reader;
|
|
xmlDocPtr doc = NULL;
|
|
const xmlError *error;
|
|
const char *docBuffer;
|
|
const unsigned char *program;
|
|
size_t maxAlloc, docSize, programSize, i;
|
|
size_t totalStringSize = 0;
|
|
int opts;
|
|
int oomReport = 0;
|
|
|
|
xmlFuzzDataInit(data, size);
|
|
opts = (int) xmlFuzzReadInt(4);
|
|
maxAlloc = xmlFuzzReadInt(4) % (size + 100);
|
|
|
|
program = (const unsigned char *) xmlFuzzReadString(&programSize);
|
|
if (programSize > 1000)
|
|
programSize = 1000;
|
|
|
|
xmlFuzzReadEntities();
|
|
docBuffer = xmlFuzzMainEntity(&docSize);
|
|
if (docBuffer == NULL)
|
|
goto exit;
|
|
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "Input document (%d bytes):\n", (int) docSize);
|
|
for (i = 0; (size_t) i < docSize; i++) {
|
|
int c = (unsigned char) docBuffer[i];
|
|
|
|
if ((c == '\n' || (c >= 0x20 && c <= 0x7E)))
|
|
putc(c, stderr);
|
|
else
|
|
fprintf(stderr, "\\x%02X", c);
|
|
}
|
|
fprintf(stderr, "\nEOF\n");
|
|
#endif
|
|
|
|
xmlFuzzMemSetLimit(maxAlloc);
|
|
reader = xmlReaderForMemory(docBuffer, docSize, NULL, NULL, opts);
|
|
if (reader == NULL)
|
|
goto exit;
|
|
|
|
xmlTextReaderSetStructuredErrorHandler(reader, xmlFuzzSErrorFunc, NULL);
|
|
xmlTextReaderSetResourceLoader(reader, xmlFuzzResourceLoader, NULL);
|
|
|
|
i = 0;
|
|
while (i < programSize) {
|
|
int op = program[i++];
|
|
|
|
#define READ_BYTE() (i < programSize ? program[i++] : 0)
|
|
#define FREE_STRING(str) \
|
|
do { \
|
|
if (str != NULL) { \
|
|
totalStringSize += strlen((char *) str); \
|
|
xmlFree(str); \
|
|
} \
|
|
} while (0)
|
|
|
|
switch (op & 0x3F) {
|
|
case OP_READ:
|
|
default:
|
|
startOp("Read");
|
|
xmlTextReaderRead(reader);
|
|
break;
|
|
|
|
case OP_READ_INNER_XML: {
|
|
xmlChar *result;
|
|
|
|
startOp("ReadInnerXml");
|
|
result = xmlTextReaderReadInnerXml(reader);
|
|
FREE_STRING(result);
|
|
break;
|
|
}
|
|
|
|
case OP_READ_OUTER_XML: {
|
|
xmlChar *result;
|
|
|
|
startOp("ReadOuterXml");
|
|
result = xmlTextReaderReadOuterXml(reader);
|
|
FREE_STRING(result);
|
|
break;
|
|
}
|
|
|
|
case OP_READ_STRING: {
|
|
xmlChar *result;
|
|
|
|
startOp("ReadString");
|
|
result = xmlTextReaderReadString(reader);
|
|
FREE_STRING(result);
|
|
break;
|
|
}
|
|
|
|
case OP_READ_ATTRIBUTE_VALUE:
|
|
startOp("ReadAttributeValue");
|
|
xmlTextReaderReadAttributeValue(reader);
|
|
break;
|
|
|
|
case OP_ATTRIBUTE_COUNT:
|
|
startOp("AttributeCount");
|
|
xmlTextReaderAttributeCount(reader);
|
|
break;
|
|
|
|
case OP_DEPTH:
|
|
startOp("Depth");
|
|
xmlTextReaderDepth(reader);
|
|
break;
|
|
|
|
case OP_HAS_ATTRIBUTES:
|
|
startOp("HasAttributes");
|
|
xmlTextReaderHasAttributes(reader);
|
|
break;
|
|
|
|
case OP_HAS_VALUE:
|
|
startOp("HasValue");
|
|
xmlTextReaderHasValue(reader);
|
|
break;
|
|
|
|
case OP_IS_DEFAULT:
|
|
startOp("IsDefault");
|
|
xmlTextReaderIsDefault(reader);
|
|
break;
|
|
|
|
case OP_IS_EMPTY_ELEMENT:
|
|
startOp("IsEmptyElement");
|
|
xmlTextReaderIsEmptyElement(reader);
|
|
break;
|
|
|
|
case OP_NODE_TYPE:
|
|
startOp("NodeType");
|
|
xmlTextReaderNodeType(reader);
|
|
break;
|
|
|
|
case OP_QUOTE_CHAR:
|
|
startOp("QuoteChar");
|
|
xmlTextReaderQuoteChar(reader);
|
|
break;
|
|
|
|
case OP_READ_STATE:
|
|
startOp("ReadState");
|
|
xmlTextReaderReadState(reader);
|
|
break;
|
|
|
|
case OP_IS_NAMESPACE_DECL:
|
|
startOp("IsNamespaceDecl");
|
|
xmlTextReaderIsNamespaceDecl(reader);
|
|
break;
|
|
|
|
case OP_CONST_BASE_URI:
|
|
startOp("ConstBaseUri");
|
|
xmlTextReaderConstBaseUri(reader);
|
|
break;
|
|
|
|
case OP_CONST_LOCAL_NAME:
|
|
startOp("ConstLocalName");
|
|
xmlTextReaderConstLocalName(reader);
|
|
break;
|
|
|
|
case OP_CONST_NAME:
|
|
startOp("ConstName");
|
|
xmlTextReaderConstName(reader);
|
|
break;
|
|
|
|
case OP_CONST_NAMESPACE_URI:
|
|
startOp("ConstNamespaceUri");
|
|
xmlTextReaderConstNamespaceUri(reader);
|
|
break;
|
|
|
|
case OP_CONST_PREFIX:
|
|
startOp("ConstPrefix");
|
|
xmlTextReaderConstPrefix(reader);
|
|
break;
|
|
|
|
case OP_CONST_XML_LANG:
|
|
startOp("ConstXmlLang");
|
|
xmlTextReaderConstXmlLang(reader);
|
|
oomReport = -1;
|
|
break;
|
|
|
|
case OP_CONST_VALUE:
|
|
startOp("ConstValue");
|
|
xmlTextReaderConstValue(reader);
|
|
break;
|
|
|
|
case OP_BASE_URI: {
|
|
xmlChar *result;
|
|
|
|
startOp("BaseUri");
|
|
result = xmlTextReaderBaseUri(reader);
|
|
FREE_STRING(result);
|
|
break;
|
|
}
|
|
|
|
case OP_LOCAL_NAME: {
|
|
xmlChar *result;
|
|
|
|
startOp("LocalName");
|
|
result = xmlTextReaderLocalName(reader);
|
|
FREE_STRING(result);
|
|
break;
|
|
}
|
|
|
|
case OP_NAME: {
|
|
xmlChar *result;
|
|
|
|
startOp("Name");
|
|
result = xmlTextReaderName(reader);
|
|
FREE_STRING(result);
|
|
break;
|
|
}
|
|
|
|
case OP_NAMESPACE_URI: {
|
|
xmlChar *result;
|
|
|
|
startOp("NamespaceUri");
|
|
result = xmlTextReaderNamespaceUri(reader);
|
|
FREE_STRING(result);
|
|
break;
|
|
}
|
|
|
|
case OP_PREFIX: {
|
|
xmlChar *result;
|
|
|
|
startOp("Prefix");
|
|
result = xmlTextReaderPrefix(reader);
|
|
FREE_STRING(result);
|
|
break;
|
|
}
|
|
|
|
case OP_XML_LANG: {
|
|
xmlChar *result;
|
|
|
|
startOp("XmlLang");
|
|
result = xmlTextReaderXmlLang(reader);
|
|
oomReport = -1;
|
|
FREE_STRING(result);
|
|
break;
|
|
}
|
|
|
|
case OP_VALUE: {
|
|
xmlChar *result;
|
|
|
|
startOp("Value");
|
|
result = xmlTextReaderValue(reader);
|
|
FREE_STRING(result);
|
|
break;
|
|
}
|
|
|
|
case OP_CLOSE:
|
|
startOp("Close");
|
|
if (doc == NULL)
|
|
doc = xmlTextReaderCurrentDoc(reader);
|
|
xmlTextReaderClose(reader);
|
|
break;
|
|
|
|
case OP_GET_ATTRIBUTE_NO: {
|
|
xmlChar *result;
|
|
int no = READ_BYTE();
|
|
|
|
startOp("GetAttributeNo");
|
|
result = xmlTextReaderGetAttributeNo(reader, no);
|
|
FREE_STRING(result);
|
|
break;
|
|
}
|
|
|
|
case OP_GET_ATTRIBUTE: {
|
|
const xmlChar *name = xmlTextReaderConstName(reader);
|
|
xmlChar *result;
|
|
|
|
startOp("GetAttribute");
|
|
result = xmlTextReaderGetAttribute(reader, name);
|
|
FREE_STRING(result);
|
|
break;
|
|
}
|
|
|
|
case OP_GET_ATTRIBUTE_NS: {
|
|
const xmlChar *localName, *namespaceUri;
|
|
xmlChar *result;
|
|
|
|
startOp("GetAttributeNs");
|
|
localName = xmlTextReaderConstLocalName(reader);
|
|
namespaceUri = xmlTextReaderConstNamespaceUri(reader);
|
|
result = xmlTextReaderGetAttributeNs(reader, localName,
|
|
namespaceUri);
|
|
FREE_STRING(result);
|
|
break;
|
|
}
|
|
|
|
case OP_GET_REMAINDER:
|
|
startOp("GetRemainder");
|
|
if (doc == NULL)
|
|
doc = xmlTextReaderCurrentDoc(reader);
|
|
xmlFreeParserInputBuffer(xmlTextReaderGetRemainder(reader));
|
|
break;
|
|
|
|
case OP_LOOKUP_NAMESPACE: {
|
|
const xmlChar *prefix = xmlTextReaderConstPrefix(reader);
|
|
xmlChar *result;
|
|
|
|
startOp("LookupNamespace");
|
|
result = xmlTextReaderLookupNamespace(reader, prefix);
|
|
FREE_STRING(result);
|
|
break;
|
|
}
|
|
|
|
case OP_MOVE_TO_ATTRIBUTE_NO: {
|
|
int no = READ_BYTE();
|
|
|
|
startOp("MoveToAttributeNo");
|
|
xmlTextReaderMoveToAttributeNo(reader, no);
|
|
break;
|
|
}
|
|
|
|
case OP_MOVE_TO_ATTRIBUTE: {
|
|
const xmlChar *name = xmlTextReaderConstName(reader);
|
|
|
|
startOp("MoveToAttribute");
|
|
xmlTextReaderMoveToAttribute(reader, name);
|
|
break;
|
|
}
|
|
|
|
case OP_MOVE_TO_ATTRIBUTE_NS: {
|
|
const xmlChar *localName, *namespaceUri;
|
|
|
|
startOp("MoveToAttributeNs");
|
|
localName = xmlTextReaderConstLocalName(reader);
|
|
namespaceUri = xmlTextReaderConstNamespaceUri(reader);
|
|
xmlTextReaderMoveToAttributeNs(reader, localName,
|
|
namespaceUri);
|
|
break;
|
|
}
|
|
|
|
case OP_MOVE_TO_FIRST_ATTRIBUTE:
|
|
startOp("MoveToFirstAttribute");
|
|
xmlTextReaderMoveToFirstAttribute(reader);
|
|
break;
|
|
|
|
case OP_MOVE_TO_NEXT_ATTRIBUTE:
|
|
startOp("MoveToNextAttribute");
|
|
xmlTextReaderMoveToNextAttribute(reader);
|
|
break;
|
|
|
|
case OP_MOVE_TO_ELEMENT:
|
|
startOp("MoveToElement");
|
|
xmlTextReaderMoveToElement(reader);
|
|
break;
|
|
|
|
case OP_NORMALIZATION:
|
|
startOp("Normalization");
|
|
xmlTextReaderNormalization(reader);
|
|
break;
|
|
|
|
case OP_CONST_ENCODING:
|
|
startOp("ConstEncoding");
|
|
xmlTextReaderConstEncoding(reader);
|
|
break;
|
|
|
|
case OP_GET_PARSER_PROP: {
|
|
int prop = READ_BYTE();
|
|
|
|
startOp("GetParserProp");
|
|
xmlTextReaderGetParserProp(reader, prop);
|
|
break;
|
|
}
|
|
|
|
case OP_CURRENT_NODE:
|
|
startOp("CurrentNode");
|
|
xmlTextReaderCurrentNode(reader);
|
|
break;
|
|
|
|
case OP_GET_PARSER_LINE_NUMBER:
|
|
startOp("GetParserLineNumber");
|
|
xmlTextReaderGetParserLineNumber(reader);
|
|
break;
|
|
|
|
case OP_GET_PARSER_COLUMN_NUMBER:
|
|
startOp("GetParserColumnNumber");
|
|
xmlTextReaderGetParserColumnNumber(reader);
|
|
break;
|
|
|
|
case OP_PRESERVE:
|
|
startOp("Preserve");
|
|
xmlTextReaderPreserve(reader);
|
|
break;
|
|
|
|
case OP_CURRENT_DOC: {
|
|
xmlDocPtr result;
|
|
|
|
startOp("CurrentDoc");
|
|
result = xmlTextReaderCurrentDoc(reader);
|
|
if (doc == NULL)
|
|
doc = result;
|
|
break;
|
|
}
|
|
|
|
case OP_EXPAND:
|
|
startOp("Expand");
|
|
xmlTextReaderExpand(reader);
|
|
break;
|
|
|
|
case OP_NEXT:
|
|
startOp("Next");
|
|
xmlTextReaderNext(reader);
|
|
break;
|
|
|
|
case OP_NEXT_SIBLING:
|
|
startOp("NextSibling");
|
|
xmlTextReaderNextSibling(reader);
|
|
break;
|
|
|
|
case OP_IS_VALID:
|
|
startOp("IsValid");
|
|
xmlTextReaderIsValid(reader);
|
|
break;
|
|
|
|
case OP_CONST_XML_VERSION:
|
|
startOp("ConstXmlVersion");
|
|
xmlTextReaderConstXmlVersion(reader);
|
|
break;
|
|
|
|
case OP_STANDALONE:
|
|
startOp("Standalone");
|
|
xmlTextReaderStandalone(reader);
|
|
break;
|
|
|
|
case OP_BYTE_CONSUMED:
|
|
startOp("ByteConsumed");
|
|
xmlTextReaderByteConsumed(reader);
|
|
oomReport = -1;
|
|
break;
|
|
}
|
|
|
|
if (totalStringSize > docSize * 2)
|
|
break;
|
|
}
|
|
|
|
error = xmlTextReaderGetLastError(reader);
|
|
if (error->code == XML_ERR_NO_MEMORY)
|
|
oomReport = 1;
|
|
xmlFuzzCheckMallocFailure("reader", oomReport);
|
|
|
|
xmlFreeTextReader(reader);
|
|
|
|
if (doc != NULL)
|
|
xmlFreeDoc(doc);
|
|
|
|
exit:
|
|
xmlFuzzMemSetLimit(0);
|
|
xmlFuzzDataCleanup();
|
|
xmlResetLastError();
|
|
return(0);
|
|
}
|
|
|