| | |
| | |
| | |
| | |
| | |
| |
|
| | #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 failurePos, docSize, programSize, i; |
| | size_t totalStringSize = 0; |
| | int opts; |
| | int oomReport = 0; |
| |
|
| | xmlFuzzDataInit(data, size); |
| | opts = (int) xmlFuzzReadInt(4); |
| | failurePos = 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 |
| |
|
| | xmlFuzzInjectFailure(failurePos); |
| | 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; |
| | xmlFuzzCheckFailureReport("reader", oomReport, error->code == XML_IO_EIO); |
| |
|
| | xmlFreeTextReader(reader); |
| |
|
| | if (doc != NULL) |
| | xmlFreeDoc(doc); |
| |
|
| | exit: |
| | xmlFuzzInjectFailure(0); |
| | xmlFuzzDataCleanup(); |
| | xmlResetLastError(); |
| | return(0); |
| | } |
| |
|
| | size_t |
| | LLVMFuzzerCustomMutator(char *data, size_t size, size_t maxSize, |
| | unsigned seed) { |
| | static const xmlFuzzChunkDesc chunks[] = { |
| | { 4, XML_FUZZ_PROB_ONE / 10 }, |
| | { 4, XML_FUZZ_PROB_ONE / 10 }, |
| | { 0, 0 } |
| | }; |
| |
|
| | return xmlFuzzMutateChunks(chunks, data, size, maxSize, seed, |
| | LLVMFuzzerMutate); |
| | } |
| |
|
| |
|