/* * xml.c: a libFuzzer target to test several XML parser interfaces. * * See Copyright for the status of this software. */ #include #include #include #include #include #include #include "fuzz.h" #include #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); 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); }