| /* |
| * 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 printf |
| #else |
| #define DEBUG noop |
| #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 |
| noop(const char *fmt, ...) { |
| (void) fmt; |
| } |
| |
| static void |
| startOp(const char *name) { |
| (void) name; |
| DEBUG("%s\n", name); |
| } |
| |
| int |
| LLVMFuzzerInitialize(int *argc ATTRIBUTE_UNUSED, |
| char ***argv ATTRIBUTE_UNUSED) { |
| xmlFuzzMemSetup(); |
| xmlInitParser(); |
| #ifdef LIBXML_CATALOG_ENABLED |
| xmlInitializeCatalog(); |
| xmlCatalogSetDefaults(XML_CATA_ALLOW_NONE); |
| #endif |
| xmlSetGenericErrorFunc(NULL, xmlFuzzErrorFunc); |
| xmlSetExternalEntityLoader(xmlFuzzEntityLoader); |
| |
| 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; |
| |
| xmlFuzzMemSetLimit(maxAlloc); |
| reader = xmlReaderForMemory(docBuffer, docSize, NULL, NULL, opts); |
| if (reader == NULL) |
| goto exit; |
| |
| 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); |
| } |
| |