/* * api.c: a libFuzzer target to test node-related API functions. * * See Copyright for the status of this software. * * This is a simple virtual machine which runs fuzz data as a program. * An important design goal is to execute as many API calls as possible * per input byte. * * We use a fixed number of registers for basic types like integers * or strings as well as libxml2 objects like xmlNode. The opcodes are * single bytes which typically result in a call to an API function * using the freshest registers for each argument type and storing the * result in the stalest register. This can be implemented using a ring * buffer. * * There are a few other opcodes to initialize or duplicate registers, * so all kinds of API calls can potentially be generated from fuzz * data. * * This architecture is similar to stack machine and benefits from * great code density. The main difference is that values aren't * destroyed when popping arguments from the stack and that the bottom * of the stack is eventually overwritten if the ring buffer overflows. * * The main complication is memory management of nodes. Whenever a * reference between two nodes is removed, whether by an API call or * the VM clearing a register, we must check whether this leaves * unreferenced nodes which can then be freed. There are no opcodes * to free a node explicitly. The FIFO patterns generated by * overflowing the ring buffer and freeing the registers at the end of * a program seem to do a good enough job. */ #include #include #define XML_DEPRECATED #include #include #include #include #include #include "fuzz.h" #if 0 #define DEBUG printf #else #define DEBUG(...) #endif #define MAX_CONTENT 100 #define MAX_COPY_NODES 50 #define MAX_COPY_OPS 20 typedef enum { /* Basic operations */ OP_CREATE_INTEGER, OP_CREATE_STRING, OP_DUP_INTEGER, OP_DUP_STRING, OP_DUP_NODE, /*** tree.h ***/ /* Tree constructors */ OP_XML_NEW_DOC, OP_XML_NEW_NODE, OP_XML_NEW_NODE_EAT_NAME, OP_XML_NEW_DOC_NODE, OP_XML_NEW_DOC_NODE_EAT_NAME, OP_XML_NEW_DOC_RAW_NODE, OP_XML_NEW_CHILD, OP_XML_NEW_TEXT_CHILD, OP_XML_NEW_PROP, OP_XML_NEW_DOC_PROP, OP_XML_NEW_NS_PROP, OP_XML_NEW_NS_PROP_EAT_NAME, OP_XML_NEW_TEXT, OP_XML_NEW_TEXT_LEN, OP_XML_NEW_DOC_TEXT, OP_XML_NEW_DOC_TEXT_LEN, OP_XML_NEW_PI, OP_XML_NEW_DOC_PI, OP_XML_NEW_COMMENT, OP_XML_NEW_DOC_COMMENT, OP_XML_NEW_CDATA_BLOCK, OP_XML_NEW_CHAR_REF, OP_XML_NEW_REFERENCE, OP_XML_NEW_DOC_FRAGMENT, OP_XML_CREATE_INT_SUBSET, OP_XML_NEW_DTD, /* Node copying */ OP_XML_COPY_DOC, OP_XML_COPY_NODE, OP_XML_COPY_NODE_LIST, OP_XML_DOC_COPY_NODE, OP_XML_DOC_COPY_NODE_LIST, OP_XML_COPY_PROP, OP_XML_COPY_PROP_LIST, OP_XML_COPY_DTD, /* Node accessors */ OP_NODE_PARENT, OP_NODE_NEXT_SIBLING, OP_NODE_PREV_SIBLING, OP_NODE_FIRST_CHILD, OP_XML_GET_LAST_CHILD, OP_NODE_NAME, OP_XML_NODE_SET_NAME, OP_XML_NODE_GET_CONTENT, OP_XML_NODE_SET_CONTENT, OP_XML_NODE_SET_CONTENT_LEN, OP_XML_NODE_ADD_CONTENT, OP_XML_NODE_ADD_CONTENT_LEN, OP_XML_GET_INT_SUBSET, OP_XML_GET_LINE_NO, OP_XML_GET_NODE_PATH, OP_XML_DOC_GET_ROOT_ELEMENT, OP_XML_DOC_SET_ROOT_ELEMENT, OP_XML_NODE_IS_TEXT, OP_XML_NODE_GET_ATTR_VALUE, OP_XML_NODE_GET_LANG, OP_XML_NODE_SET_LANG, OP_XML_NODE_GET_SPACE_PRESERVE, OP_XML_NODE_SET_SPACE_PRESERVE, OP_XML_NODE_GET_BASE, OP_XML_NODE_GET_BASE_SAFE, OP_XML_NODE_SET_BASE, OP_XML_IS_BLANK_NODE, /* Attributes */ OP_XML_HAS_PROP, OP_XML_HAS_NS_PROP, OP_XML_GET_PROP, OP_XML_GET_NS_PROP, OP_XML_GET_NO_NS_PROP, OP_XML_SET_PROP, OP_XML_SET_NS_PROP, OP_XML_REMOVE_PROP, OP_XML_UNSET_PROP, OP_XML_UNSET_NS_PROP, /* Namespaces */ OP_XML_NEW_NS, OP_XML_SEARCH_NS, OP_XML_SEARCH_NS_BY_HREF, OP_XML_GET_NS_LIST, OP_XML_GET_NS_LIST_SAFE, OP_XML_SET_NS, OP_XML_COPY_NAMESPACE, OP_XML_COPY_NAMESPACE_LIST, /* Tree manipulation */ OP_XML_UNLINK_NODE, OP_XML_ADD_CHILD, OP_XML_ADD_CHILD_LIST, OP_XML_REPLACE_NODE, OP_XML_ADD_SIBLING, OP_XML_ADD_PREV_SIBLING, OP_XML_ADD_NEXT_SIBLING, /* String output */ OP_XML_DOC_DUMP_MEMORY, OP_XML_DOC_DUMP_MEMORY_ENC, OP_XML_DOC_DUMP_FORMAT_MEMORY, OP_XML_DOC_DUMP_FORMAT_MEMORY_ENC, /* FILE output, TODO, use fmemopen */ OP_XML_DOC_DUMP, OP_XML_DOC_FORMAT_DUMP, OP_XML_ELEM_DUMP, /* xmlBuf output, TODO, no public API */ OP_XML_BUF_NODE_DUMP, OP_XML_BUF_GET_NODE_CONTENT, /* xmlBuffer output */ OP_XML_NODE_DUMP, OP_XML_NODE_BUF_GET_CONTENT, OP_XML_ATTR_SERIALIZE_TXT_CONTENT, OP_XML_DUMP_ELEMENT_DECL, OP_XML_DUMP_ELEMENT_TABLE, OP_XML_DUMP_ATTRIBUTE_DECL, OP_XML_DUMP_ATTRIBUTE_TABLE, OP_XML_DUMP_NOTATION_DECL, OP_XML_DUMP_NOTATION_TABLE, OP_XML_DUMP_ENTITY_DECL, OP_XML_DUMP_ENTITIES_TABLE, /* xmlOutputBuffer */ OP_XML_SAVE_FILE_TO, OP_XML_SAVE_FORMAT_FILE_TO, OP_XML_NODE_DUMP_OUTPUT, /* Misc */ OP_XML_TEXT_MERGE, OP_XML_TEXT_CONCAT, OP_XML_STRING_GET_NODE_LIST, OP_XML_STRING_LEN_GET_NODE_LIST, OP_XML_NODE_LIST_GET_STRING, OP_XML_NODE_LIST_GET_RAW_STRING, OP_XML_IS_XHTML, /* DOM */ OP_XML_DOM_WRAP_RECONCILE_NAMESPACES, OP_XML_DOM_WRAP_ADOPT_NODE, OP_XML_DOM_WRAP_REMOVE_NODE, OP_XML_DOM_WRAP_CLONE_NODE, OP_XML_CHILD_ELEMENT_COUNT, OP_XML_FIRST_ELEMENT_CHILD, OP_XML_LAST_ELEMENT_CHILD, OP_XML_NEXT_ELEMENT_SIBLING, OP_XML_PREVIOUS_ELEMENT_SIBLING, /*** parser.h ***/ OP_PARSE_DOCUMENT, /*** valid.h ***/ OP_XML_ADD_ELEMENT_DECL, OP_XML_ADD_ATTRIBUTE_DECL, OP_XML_ADD_NOTATION_DECL, OP_XML_GET_DTD_ELEMENT_DESC, OP_XML_GET_DTD_QELEMENT_DESC, OP_XML_GET_DTD_ATTR_DESC, OP_XML_GET_DTD_QATTR_DESC, OP_XML_GET_DTD_NOTATION_DESC, OP_XML_ADD_ID, OP_XML_ADD_ID_SAFE, OP_XML_GET_ID, OP_XML_IS_ID, OP_XML_REMOVE_ID, OP_XML_ADD_REF, OP_XML_GET_REFS, OP_XML_IS_REF, OP_XML_REMOVE_REF, OP_XML_IS_MIXED_ELEMENT, OP_VALIDATE, OP_XML_VALIDATE_ATTRIBUTE_VALUE, OP_XML_VALIDATE_DTD, OP_XML_VALIDATE_NOTATION_USE, OP_XML_VALIDATE_NAME_VALUE, OP_XML_VALIDATE_NAMES_VALUE, OP_XML_VALIDATE_NMTOKEN_VALUE, OP_XML_VALIDATE_NMTOKENS_VALUE, OP_XML_VALID_NORMALIZE_ATTRIBUTE_VALUE, OP_XML_VALID_CTXT_NORMALIZE_ATTRIBUTE_VALUE, OP_XML_VALID_GET_POTENTIAL_CHILDREN, OP_XML_VALID_GET_VALID_ELEMENTS, /*** entities.h ***/ OP_XML_NEW_ENTITY, OP_XML_ADD_ENTITY, OP_XML_ADD_DOC_ENTITY, OP_XML_ADD_DTD_ENTITY, OP_XML_GET_PREDEFINED_ENTITY, OP_XML_GET_DOC_ENTITY, OP_XML_GET_DTD_ENTITY, OP_XML_GET_PARAMETER_ENTITY, OP_XML_ENCODE_ENTITIES_REENTRANT, OP_XML_ENCODE_SPECIAL_CHARS, /*** HTMLtree.h ***/ OP_HTML_NEW_DOC, OP_HTML_NEW_DOC_NO_DTD, OP_HTML_GET_META_ENCODING, OP_HTML_SET_META_ENCODING, OP_HTML_IS_BOOLEAN_ATTR, OP_HTML_DOC_DUMP_MEMORY, OP_HTML_DOC_DUMP_MEMORY_FORMAT, OP_HTML_DOC_DUMP, OP_HTML_NODE_DUMP_FILE, OP_HTML_NODE_DUMP_FILE_FORMAT, OP_HTML_NODE_DUMP, OP_HTML_DOC_CONTENT_DUMP_OUTPUT, OP_HTML_DOC_CONTENT_DUMP_FORMAT_OUTPUT, OP_HTML_NODE_DUMP_OUTPUT, OP_HTML_NODE_DUMP_FORMAT_OUTPUT, OP_MAX } opType; #define NODE_MASK_TEXT_CONTENT ( \ (1 << XML_TEXT_NODE) | \ (1 << XML_CDATA_SECTION_NODE) | \ (1 << XML_COMMENT_NODE) | \ (1 << XML_PI_NODE)) #define CHILD_MASK_DOCUMENT ( \ (1 << XML_ELEMENT_NODE) | \ (1 << XML_PI_NODE) | \ (1 << XML_COMMENT_NODE)) #define CHILD_MASK_CONTENT ( \ (1 << XML_ELEMENT_NODE) | \ (1 << XML_TEXT_NODE) | \ (1 << XML_CDATA_SECTION_NODE) | \ (1 << XML_ENTITY_REF_NODE) | \ (1 << XML_PI_NODE) | \ (1 << XML_COMMENT_NODE)) #define CHILD_MASK_ELEMENT ( \ CHILD_MASK_CONTENT | \ (1 << XML_ATTRIBUTE_NODE)) #define CHILD_MASK_ATTRIBUTE ( \ (1 << XML_TEXT_NODE) | \ (1 << XML_ENTITY_REF_NODE)) #define CHILD_MASK_DTD ( \ (1 << XML_ELEMENT_DECL) | \ (1 << XML_ATTRIBUTE_DECL) | \ (1 << XML_ENTITY_DECL)) static const int childMasks[] = { 0, CHILD_MASK_ELEMENT, /* XML_ELEMENT_NODE */ CHILD_MASK_ATTRIBUTE, /* XML_ATTRIBUTE_NODE */ 0, /* XML_TEXT_NODE */ 0, /* XML_CDATA_SECTION_NODE */ 0, /* XML_ENTITY_REF_NODE */ 0, /* XML_ENTITY_NODE */ 0, /* XML_PI_NODE */ 0, /* XML_COMMENT_NODE */ CHILD_MASK_DOCUMENT, /* XML_DOCUMENT_NODE */ 0, /* XML_DOCUMENT_TYPE_NODE */ CHILD_MASK_CONTENT, /* XML_DOCUMENT_FRAG_NODE */ 0, /* XML_NOTATION_NODE */ CHILD_MASK_DOCUMENT, /* XML_HTML_DOCUMENT_NODE */ 0, /* XML_DTD_NODE */ 0, /* XML_ELEMENT_DECL */ 0, /* XML_ATTRIBUTE_DECL */ 0, /* XML_ENTITY_DECL */ 0, /* XML_NAMESPACE_DECL */ 0, /* XML_XINCLUDE_START */ 0, /* XML_XINCLUDE_END */ CHILD_MASK_DOCUMENT /* XML_DOCB_DOCUMENT_NODE */ }; #define REG_MAX 8 #define REG_MASK (REG_MAX - 1) typedef struct { /* Indexes point beyond the most recent item */ int intIdx; int stringIdx; int nodeIdx; int numCopyOps; const char *opName; /* Registers */ int integers[REG_MAX]; xmlChar *strings[REG_MAX]; xmlNodePtr nodes[REG_MAX]; } xmlFuzzApiVars; static xmlFuzzApiVars varsStruct; static xmlFuzzApiVars *const vars = &varsStruct; /* Debug output */ static void startOp(const char *name) { vars->opName = name; DEBUG("%s(", name); } static void endOp(void) { DEBUG(" )\n"); } /* Integers */ static int getInt(int offset) { int idx = (vars->intIdx - offset - 1) & REG_MASK; DEBUG(" %d", vars->integers[idx]); return vars->integers[idx]; } static void setInt(int offset, int n) { int idx = (vars->intIdx - offset - 1) & REG_MASK; vars->integers[idx] = n; } static void incIntIdx(void) { vars->intIdx = (vars->intIdx + 1) & REG_MASK; } /* Strings */ static const xmlChar * getStr(int offset) { int idx = (vars->stringIdx - offset - 1) & REG_MASK; const xmlChar *str = vars->strings[idx]; if (str == NULL) DEBUG(" NULL"); else DEBUG(" \"%.20s\"", str); return str; } static const char * getCStr(int offset) { return (const char *) getStr(offset); } static void setStr(int offset, xmlChar *str) { xmlChar **strings = vars->strings; int idx = (vars->stringIdx - offset - 1) & REG_MASK; xmlChar *oldString = strings[idx]; strings[idx] = str; if (oldString) xmlFree(oldString); } static void moveStr(int offset, xmlChar *str) { if (xmlStrlen(str) > 1000) { setStr(offset, NULL); xmlFree(str); } else { setStr(offset, str); } } /* * This doesn't use xmlMalloc and can't fail because of malloc failure * injection. */ static xmlChar * uncheckedStrndup(const xmlChar *str, int size) { xmlChar *copy; if (str == NULL) return NULL; copy = BAD_CAST strndup((const char *) str, size); if (copy == NULL) { fprintf(stderr, "out of memory\n"); abort(); } return copy; } static xmlChar * uncheckedStrdup(const xmlChar *str) { return uncheckedStrndup(str, MAX_CONTENT); } static void copyStr(int offset, const xmlChar *str) { setStr(offset, uncheckedStrdup(str)); } static void incStrIdx(void) { vars->stringIdx = (vars->stringIdx + 1) & REG_MASK; } /* Nodes */ static void dropNode(xmlNodePtr node); static xmlNodePtr getNode(int offset) { int idx = (vars->nodeIdx - offset - 1) & REG_MASK; if (vars->nodes[idx]) DEBUG(" n%d", idx); else DEBUG(" NULL"); fflush(stdout); return vars->nodes[idx]; } static xmlDocPtr getDoc(int offset) { xmlNodePtr node = getNode(offset); if (node == NULL) return NULL; return node->doc; } static xmlAttrPtr getAttr(int offset) { xmlNodePtr node = getNode(offset); if (node == NULL) return NULL; if (node->type == XML_ATTRIBUTE_NODE) return (xmlAttrPtr) node; if (node->type == XML_ELEMENT_NODE) return node->properties; return NULL; } static xmlDtdPtr getDtd(int offset) { xmlNodePtr node = getNode(offset); xmlDocPtr doc; if (node == NULL) return NULL; if (node->type == XML_DTD_NODE) return (xmlDtdPtr) node; doc = node->doc; if (doc == NULL) return NULL; if (doc->intSubset != NULL) return doc->intSubset; return doc->extSubset; } static void setNode(int offset, xmlNodePtr node) { int idx = (vars->nodeIdx - offset - 1) & REG_MASK; xmlNodePtr oldNode = vars->nodes[idx]; if (node != oldNode) { vars->nodes[idx] = node; dropNode(oldNode); } if (node == NULL) DEBUG(" ) /* NULL */\n"); else DEBUG(" ) -> n%d\n", idx); } static void incNodeIdx(void) { xmlNodePtr oldNode; int idx; idx = vars->nodeIdx & REG_MASK; vars->nodeIdx = (idx + 1) & REG_MASK; oldNode = vars->nodes[idx]; if (oldNode != NULL) { vars->nodes[idx] = NULL; dropNode(oldNode); } } static int isValidChildType(xmlNodePtr parent, int childType) { return ((1 << childType) & childMasks[parent->type]) != 0; } static int isValidChild(xmlNodePtr parent, xmlNodePtr child) { xmlNodePtr cur; if (child == NULL || parent == NULL) return 1; if (parent == child) return 0; if (((1 << child->type) & childMasks[parent->type]) == 0) return 0; if (child->children == NULL) return 1; for (cur = parent->parent; cur != NULL; cur = cur->parent) if (cur == child) return 0; return 1; } static int isTextContentNode(xmlNodePtr child) { if (child == NULL) return 0; return ((1 << child->type) & NODE_MASK_TEXT_CONTENT) != 0; } static int isDtdChild(xmlNodePtr child) { if (child == NULL) return 0; return ((1 << child->type) & CHILD_MASK_DTD) != 0; } static xmlNodePtr nodeGetTree(xmlNodePtr node) { xmlNodePtr cur = node; while (cur->parent) cur = cur->parent; return cur; } /* * This function is called whenever a reference to a node is removed. * It checks whether the node is still reachable and frees unreferenced * nodes. * * A node is reachable if its tree, identified by the root node, * is reachable. If a non-document tree is unreachable, it can be * freed. * * Multiple trees can share the same document, so a document tree * can only be freed if no other trees reference the document. */ static void dropNode(xmlNodePtr node) { xmlNodePtr *nodes = vars->nodes; xmlNodePtr tree; xmlDocPtr doc; int docReferenced = 0; int i; if (node == NULL) return; tree = nodeGetTree(node); doc = node->doc; for (i = 0; i < REG_MAX; i++) { xmlNodePtr other; other = nodes[i]; if (other == NULL) continue; /* * Return if tree is referenced from another node */ if (nodeGetTree(other) == tree) return; if (doc != NULL && other->doc == doc) docReferenced = 1; } if (tree != (xmlNodePtr) doc && !isDtdChild(tree)) { if (doc == NULL || tree->type != XML_DTD_NODE || ((xmlDtdPtr) tree != doc->intSubset && (xmlDtdPtr) tree != doc->extSubset)) xmlFreeNode(tree); } /* * Also free document if it isn't referenced from other nodes */ if (doc != NULL && !docReferenced) xmlFreeDoc(doc); } /* * removeNode and removeChildren remove all references to a node * or its children from the registers. These functions should be * called if an API function destroys nodes, for example by merging * text nodes. */ static void removeNode(xmlNodePtr node) { int i; for (i = 0; i < REG_MAX; i++) if (vars->nodes[i] == node) vars->nodes[i] = NULL; } static void removeChildren(xmlNodePtr parent, int self) { int i; if (parent == NULL || (!self && parent->children == NULL)) return; for (i = 0; i < REG_MAX; i++) { xmlNodePtr node = vars->nodes[i]; if (node == parent) { if (self) vars->nodes[i] = NULL; continue; } while (node != NULL) { node = node->parent; if (node == parent) { vars->nodes[i] = NULL; break; } } } } static xmlNsPtr nodeGetNs(xmlNodePtr node, int k) { int i = 0; xmlNsPtr ns, next; if (node == NULL || node->type != XML_ELEMENT_NODE) return NULL; ns = NULL; next = node->nsDef; while (1) { while (next == NULL) { node = node->parent; if (node == NULL || node->type != XML_ELEMENT_NODE) break; next = node->nsDef; } if (next == NULL) break; ns = next; if (i == k) break; next = ns->next; i += 1; } return ns; } /* * It's easy for programs to exhibit exponential growth patterns. * For example, a tree being copied and added to the original source * node doubles memory usage with two operations. Repeating these * operations leads to 2^n nodes. Similar issues can arise when * concatenating strings. * * We simply ignore tree copies or truncate text if they grow too * large. */ static void checkContent(xmlNodePtr node) { if (node != NULL && (node->type == XML_TEXT_NODE || node->type == XML_CDATA_SECTION_NODE || node->type == XML_ENTITY_NODE || node->type == XML_PI_NODE || node->type == XML_COMMENT_NODE || node->type == XML_NOTATION_NODE) && xmlStrlen(node->content) > MAX_CONTENT) { xmlNodeSetContent(node, NULL); node->content = uncheckedStrdup(BAD_CAST ""); } } static int countNodes(xmlNodePtr node) { xmlNodePtr cur; int numNodes; if (node == NULL) return 0; cur = node; numNodes = 0; while (1) { numNodes += 1; if (cur->children != NULL && cur->type != XML_ENTITY_REF_NODE) { cur = cur->children; } else { while (cur->next == NULL) { if (cur == node) goto done; cur = cur->parent; } cur = cur->next; } } done: return numNodes; } static xmlNodePtr checkCopy(xmlNodePtr copy) { vars->numCopyOps += 1; if (copy != NULL && (vars->numCopyOps > MAX_COPY_OPS || countNodes(copy) > MAX_COPY_NODES)) { if (copy->type == XML_DOCUMENT_NODE || copy->type == XML_HTML_DOCUMENT_NODE) xmlFreeDoc((xmlDocPtr) copy); else xmlFreeNode(copy); copy = NULL; } return copy; } /* * Fix namespaces, for example after unlinking a node. This makes * sure that the node only references namespaces declared in ancestor * nodes. */ static int fixNs(xmlNodePtr node) { if (node == NULL) return 0; if (node->type == XML_ELEMENT_NODE) { return xmlReconciliateNs(node->doc, node); } else if (node->type == XML_ATTRIBUTE_NODE) { xmlNodePtr parent = node->parent; if (parent != NULL) return xmlReconciliateNs(parent->doc, parent); else node->ns = NULL; } return 0; } /* Node operations */ static void opNodeAccessor(int op) { xmlNodePtr node; switch (op) { case OP_NODE_PARENT: startOp("parent"); break; case OP_NODE_NEXT_SIBLING: startOp("next"); break; case OP_NODE_PREV_SIBLING: startOp("prev"); break; case OP_NODE_FIRST_CHILD: startOp("children"); break; case OP_XML_GET_LAST_CHILD: startOp("xmlGetLastChild"); break; case OP_XML_GET_INT_SUBSET: startOp("xmlGetIntSubset"); break; case OP_XML_DOC_GET_ROOT_ELEMENT: startOp("xmlDocGetRootElement"); break; default: break; } incNodeIdx(); node = getNode(1); if (node != NULL) { switch (op) { case OP_NODE_PARENT: node = node->parent; break; case OP_NODE_NEXT_SIBLING: node = node->next; break; case OP_NODE_PREV_SIBLING: node = node->prev; break; case OP_NODE_FIRST_CHILD: node = node->children; break; case OP_XML_GET_LAST_CHILD: node = xmlGetLastChild(node); break; case OP_XML_GET_INT_SUBSET: node = (xmlNodePtr) xmlGetIntSubset(node->doc); break; case OP_XML_DOC_GET_ROOT_ELEMENT: node = xmlDocGetRootElement(node->doc); break; default: break; } /* * Don't descend into predefined entities */ if (node != NULL && node->type == XML_ENTITY_DECL) { xmlEntityPtr ent = (xmlEntityPtr) node; if (ent->etype == XML_INTERNAL_PREDEFINED_ENTITY) node = NULL; } } setNode(0, node); } static void opDup(int op) { int offset; switch (op) { case OP_DUP_INTEGER: incIntIdx(); break; case OP_DUP_STRING: incStrIdx(); break; case OP_DUP_NODE: incNodeIdx(); break; default: break; } offset = (xmlFuzzReadInt(1) + 1) & REG_MASK; if (offset != 0) { startOp("dup"); switch (op) { case OP_DUP_INTEGER: setInt(0, getInt(offset)); endOp(); break; case OP_DUP_STRING: copyStr(0, getStr(offset)); endOp(); break; case OP_DUP_NODE: setNode(0, getNode(offset)); break; default: break; } } } 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); return 0; } int LLVMFuzzerTestOneInput(const char *data, size_t size) { size_t maxAlloc; int i; if (size > 1000) return 0; memset(vars, 0, sizeof(*vars)); xmlFuzzDataInit(data, size); maxAlloc = xmlFuzzReadInt(4) % (size * 50 + 10); xmlFuzzMemSetLimit(maxAlloc); /* * Interpreter loop * * Processing an opcode typically involves * * - startOp for debugging * - increase output register index if non-void * - get arguments from input registers * - invoke API function * - set oomReport * - set output register * - memory management and other adjustments * - endOp for void functions */ while (xmlFuzzBytesRemaining()) { size_t readSize; int op = xmlFuzzReadInt(1); int oomReport = -1; /* -1 means unknown */ vars->opName = "[unset]"; switch (op) { case OP_CREATE_INTEGER: incIntIdx(); setInt(0, (int) xmlFuzzReadInt(4)); break; case OP_CREATE_STRING: incStrIdx(); copyStr(0, BAD_CAST xmlFuzzReadString(&readSize)); break; case OP_DUP_INTEGER: case OP_DUP_STRING: case OP_DUP_NODE: opDup(op); break; case OP_PARSE_DOCUMENT: /* * We don't really want to test the parser but exposing * xmlReadDoc seems like a useful way generate or * round-trip documents. * * This also creates documents with a dictionary which * is crucial to hit some code paths. */ startOp("xmlReadDoc"); incNodeIdx(); setNode(0, (xmlNodePtr) xmlReadDoc( getStr(0), getCStr(1), getCStr(2), getInt(0))); break; case OP_XML_NEW_DOC: { xmlDocPtr doc; /* * TODO: There's no public API function to generate a * document with a dictionary. We should add an extra * opcode that sets doc->dict. */ startOp("xmlNewDoc"); incNodeIdx(); doc = xmlNewDoc(getStr(0)); oomReport = (doc == NULL); setNode(0, (xmlNodePtr) doc); break; } case OP_XML_NEW_NODE: { xmlNodePtr node; const xmlChar *name; startOp("xmlNewNode"); incNodeIdx(); node = xmlNewNode( nodeGetNs(getNode(1), getInt(0)), name = getStr(0)); oomReport = (name != NULL && node == NULL); if (fixNs(node) < 0) oomReport = 1; setNode(0, node); break; } case OP_XML_NEW_NODE_EAT_NAME: { xmlNodePtr node; xmlChar *name; startOp("xmlNewNodeEatName"); incNodeIdx(); node = xmlNewNodeEatName( nodeGetNs(getNode(1), getInt(0)), name = uncheckedStrdup(getStr(0))); oomReport = (name != NULL && node == NULL); if (fixNs(node) < 0) oomReport = 1; setNode(0, node); break; } case OP_XML_NEW_DOC_NODE: { xmlNodePtr node; const xmlChar *name; startOp("xmlNewDocNode"); incNodeIdx(); node = xmlNewDocNode( getDoc(1), nodeGetNs(getNode(2), getInt(0)), name = getStr(0), getStr(1)); oomReport = (name != NULL && node == NULL); if (fixNs(node) < 0) oomReport = 1; setNode(0, node); break; } case OP_XML_NEW_DOC_NODE_EAT_NAME: { xmlNodePtr node; xmlChar *name; startOp("xmlNewDocNodeEatName"); incNodeIdx(); node = xmlNewDocNodeEatName( getDoc(1), nodeGetNs(getNode(2), getInt(0)), name = uncheckedStrdup(getStr(0)), getStr(1)); oomReport = (name != NULL && node == NULL); if (fixNs(node) < 0) oomReport = 1; setNode(0, node); break; } case OP_XML_NEW_DOC_RAW_NODE: { xmlNodePtr node; const xmlChar *name; startOp("xmlNewDocRawNode"); incNodeIdx(); node = xmlNewDocRawNode( getDoc(1), nodeGetNs(getNode(2), getInt(0)), name = getStr(0), getStr(1)); oomReport = (name != NULL && node == NULL); if (fixNs(node) < 0) oomReport = 1; setNode(0, node); break; } case OP_XML_NEW_CHILD: { xmlNodePtr parent, node; const xmlChar *name; startOp("xmlNewChild"); incNodeIdx(); /* Use parent namespace without fixup */ node = xmlNewChild( parent = getNode(1), nodeGetNs(getNode(1), getInt(0)), name = getStr(0), getStr(1)); oomReport = (parent != NULL && isValidChildType(parent, XML_ELEMENT_NODE) && name != NULL && node == NULL); setNode(0, node); break; } case OP_XML_NEW_TEXT_CHILD: { xmlNodePtr parent, node; const xmlChar *name; startOp("xmlNewTextChild"); incNodeIdx(); /* Use parent namespace without fixup */ node = xmlNewTextChild( parent = getNode(1), nodeGetNs(getNode(1), getInt(0)), name = getStr(0), getStr(1)); oomReport = (parent != NULL && isValidChildType(parent, XML_ELEMENT_NODE) && name != NULL && node == NULL); setNode(0, node); break; } case OP_XML_NEW_PROP: { xmlNodePtr parent; xmlAttrPtr attr; const xmlChar *name; startOp("xmlNewProp"); incNodeIdx(); attr = xmlNewProp( parent = getNode(1), name = getStr(0), getStr(1)); oomReport = ((parent == NULL || parent->type == XML_ELEMENT_NODE) && name != NULL && attr == NULL); setNode(0, (xmlNodePtr) attr); break; } case OP_XML_NEW_DOC_PROP: { xmlAttrPtr attr; const xmlChar *name; startOp("xmlNewDocProp"); incNodeIdx(); attr = xmlNewDocProp( getDoc(1), name = getStr(0), getStr(1)); oomReport = (name != NULL && attr == NULL); setNode(0, (xmlNodePtr) attr); break; } case OP_XML_NEW_NS_PROP: { xmlAttrPtr attr; startOp("xmlNewNsProp"); incNodeIdx(); attr = xmlNewNsProp( getNode(1), nodeGetNs(getNode(1), getInt(0)), getStr(0), getStr(1)); /* xmlNewNsProp returns NULL on duplicate prefixes. */ if (attr != NULL) oomReport = 0; setNode(0, (xmlNodePtr) attr); break; } case OP_XML_NEW_NS_PROP_EAT_NAME: { xmlAttrPtr attr; startOp("xmlNewNsPropEatName"); incNodeIdx(); attr = xmlNewNsPropEatName( getNode(1), nodeGetNs(getNode(1), getInt(0)), uncheckedStrdup(getStr(0)), getStr(1)); if (attr != NULL) oomReport = 0; setNode(0, (xmlNodePtr) attr); break; } case OP_XML_NEW_TEXT: { xmlNodePtr node; startOp("xmlNewText"); incNodeIdx(); node = xmlNewText(getStr(0)); oomReport = (node == NULL); setNode(0, node); break; } case OP_XML_NEW_TEXT_LEN: { xmlNodePtr node; const xmlChar *text; startOp("xmlNewTextLen"); incNodeIdx(); text = getStr(0); node = xmlNewTextLen(text, xmlStrlen(text)); oomReport = (node == NULL); setNode(0, node); break; } case OP_XML_NEW_DOC_TEXT: { xmlNodePtr node; startOp("xmlNewDocText"); incNodeIdx(); node = xmlNewDocText(getDoc(1), getStr(0)); oomReport = (node == NULL); setNode(0, node); break; } case OP_XML_NEW_DOC_TEXT_LEN: { xmlDocPtr doc; xmlNodePtr node; const xmlChar *text; startOp("xmlNewDocTextLen"); incNodeIdx(); doc = getDoc(1); text = getStr(0); node = xmlNewDocTextLen(doc, text, xmlStrlen(text)); oomReport = (node == NULL); setNode(0, node); break; } case OP_XML_NEW_PI: { xmlNodePtr node; const xmlChar *name; startOp("xmlNewPI"); incNodeIdx(); node = xmlNewPI( name = getStr(0), getStr(1)); oomReport = (name != NULL && node == NULL); setNode(0, node); break; } case OP_XML_NEW_DOC_PI: { xmlNodePtr node; const xmlChar *name; startOp("xmlNewDocPI"); incNodeIdx(); node = xmlNewDocPI( getDoc(1), name = getStr(0), getStr(1)); oomReport = (name != NULL && node == NULL); setNode(0, node); break; } case OP_XML_NEW_COMMENT: { xmlNodePtr node; startOp("xmlNewComment"); incNodeIdx(); node = xmlNewComment(getStr(0)); oomReport = (node == NULL); setNode(0, node); break; } case OP_XML_NEW_DOC_COMMENT: { xmlNodePtr node; startOp("xmlNewDocComment"); incNodeIdx(); node = xmlNewDocComment( getDoc(1), getStr(0)); oomReport = (node == NULL); setNode(0, node); break; } case OP_XML_NEW_CDATA_BLOCK: { xmlDocPtr doc; xmlNodePtr node; const xmlChar *text; startOp("xmlNewCDataBlock"); incNodeIdx(); doc = getDoc(1); text = getStr(0); node = xmlNewDocTextLen( doc, text, xmlStrlen(text)); oomReport = (node == NULL); setNode(0, node); break; } case OP_XML_NEW_CHAR_REF: { xmlNodePtr node; const xmlChar *name; startOp("xmlNewCharRef"); incNodeIdx(); node = xmlNewCharRef( getDoc(1), name = getStr(0)); oomReport = (name != NULL && node == NULL); setNode(0, node); break; } case OP_XML_NEW_REFERENCE: { xmlNodePtr node; const xmlChar *name; startOp("xmlNewReference"); incNodeIdx(); node = xmlNewReference( getDoc(1), name = getStr(0)); oomReport = (name != NULL && node == NULL); setNode(0, node); break; } case OP_XML_NEW_DOC_FRAGMENT: { xmlNodePtr node; startOp("xmlNewDocFragment"); incNodeIdx(); node = xmlNewDocFragment(getDoc(1)); oomReport = (node == NULL); setNode(0, node); break; } case OP_XML_CREATE_INT_SUBSET: { xmlDocPtr doc; xmlDtdPtr dtd = NULL; startOp("xmlCreateIntSubset"); incNodeIdx(); doc = getDoc(1); if (doc == NULL || doc->intSubset == NULL) { dtd = xmlCreateIntSubset( doc, getStr(0), getStr(1), getStr(2)); oomReport = (dtd == NULL); } setNode(0, (xmlNodePtr) dtd); break; } case OP_XML_NEW_DTD: { xmlDocPtr doc; xmlDtdPtr dtd = NULL; startOp("xmlNewDtd"); incNodeIdx(); doc = getDoc(1); if (doc == NULL || doc->extSubset == NULL) { dtd = xmlNewDtd( doc, getStr(0), getStr(1), getStr(2)); oomReport = (dtd == NULL); } setNode(0, (xmlNodePtr) dtd); break; } case OP_XML_COPY_DOC: { xmlDocPtr copy; startOp("xmlCopyDoc"); incNodeIdx(); copy = xmlCopyDoc( getDoc(1), getInt(0)); /* * TODO: Copying DTD nodes without a document can * result in an empty list. */ if (copy != NULL) oomReport = 0; setNode(0, checkCopy((xmlNodePtr) copy)); break; } case OP_XML_COPY_NODE: { xmlNodePtr copy; startOp("xmlCopyNode"); incNodeIdx(); copy = xmlCopyNode( getNode(1), getInt(0)); if (copy != NULL) oomReport = 0; setNode(0, checkCopy((xmlNodePtr) copy)); break; } case OP_XML_COPY_NODE_LIST: { xmlNodePtr copy; startOp("xmlCopyNodeList"); copy = xmlCopyNodeList(getNode(0)); if (copy != NULL) oomReport = 0; xmlFreeNodeList(copy); endOp(); break; } case OP_XML_DOC_COPY_NODE: { xmlNodePtr node, copy; xmlDocPtr doc; startOp("xmlDocCopyNode"); incNodeIdx(); copy = xmlDocCopyNode( node = getNode(1), doc = getDoc(2), getInt(0)); if (copy != NULL) oomReport = 0; setNode(0, checkCopy((xmlNodePtr) copy)); break; } case OP_XML_DOC_COPY_NODE_LIST: { xmlNodePtr copy; startOp("xmlDocCopyNodeList"); copy = xmlDocCopyNodeList( getDoc(0), getNode(1)); if (copy != NULL) oomReport = 0; xmlFreeNodeList(copy); endOp(); break; } case OP_XML_COPY_PROP: { xmlAttrPtr copy; startOp("xmlCopyProp"); incNodeIdx(); copy = xmlCopyProp( getNode(1), getAttr(2)); /* * TODO: Copying attributes can result in an empty list * if there's a duplicate namespace prefix. */ if (copy != NULL) oomReport = 0; if (copy != NULL) { /* Quirk */ copy->parent = NULL; /* Fix namespace */ copy->ns = NULL; } setNode(0, checkCopy((xmlNodePtr) copy)); break; } case OP_XML_COPY_PROP_LIST: { xmlAttrPtr copy; startOp("xmlCopyPropList"); copy = xmlCopyPropList( getNode(0), getAttr(1)); if (copy != NULL) oomReport = 0; xmlFreePropList(copy); endOp(); break; } case OP_XML_COPY_DTD: { xmlDtdPtr dtd, copy; startOp("xmlCopyDtd"); incNodeIdx(); copy = xmlCopyDtd( dtd = getDtd(1)); oomReport = (dtd != NULL && copy == NULL); setNode(0, checkCopy((xmlNodePtr) copy)); break; } case OP_NODE_PARENT: case OP_NODE_NEXT_SIBLING: case OP_NODE_PREV_SIBLING: case OP_NODE_FIRST_CHILD: case OP_XML_GET_LAST_CHILD: case OP_XML_GET_INT_SUBSET: case OP_XML_DOC_GET_ROOT_ELEMENT: opNodeAccessor(op); oomReport = 0; break; case OP_NODE_NAME: { xmlNodePtr node; startOp("name"); incStrIdx(); node = getNode(0); copyStr(0, node ? node->name : NULL); oomReport = 0; endOp(); break; } case OP_XML_NODE_SET_NAME: startOp("xmlNodeSetName"); xmlNodeSetName( getNode(0), getStr(0)); endOp(); break; case OP_XML_NODE_GET_CONTENT: { xmlChar *content; incStrIdx(); startOp("xmlNodeGetContent"); content = xmlNodeGetContent(getNode(0)); if (content != NULL) oomReport = 0; moveStr(0, content); endOp(); break; } case OP_XML_NODE_SET_CONTENT: { xmlNodePtr node; int res; startOp("xmlNodeSetContent"); node = getNode(0); removeChildren(node, 0); res = xmlNodeSetContent( node, getStr(0)); oomReport = (res < 0); endOp(); break; } case OP_XML_NODE_SET_CONTENT_LEN: { xmlNodePtr node; const xmlChar *content; int res; startOp("xmlNodeSetContentLen"); node = getNode(0); content = getStr(0); removeChildren(node, 0); res = xmlNodeSetContentLen( node, content, xmlStrlen(content)); oomReport = (res < 0); endOp(); break; } case OP_XML_NODE_ADD_CONTENT: { xmlNodePtr node, text; int res; startOp("xmlNodeAddContent"); node = getNode(0); res = xmlNodeAddContent( node, getStr(0)); oomReport = (res < 0); if (node != NULL) { if (node->type == XML_ELEMENT_NODE || node->type == XML_DOCUMENT_FRAG_NODE) text = node->last; else text = node; checkContent(text); } endOp(); break; } case OP_XML_NODE_ADD_CONTENT_LEN: { xmlNodePtr node, text; const xmlChar *content; int res; startOp("xmlNodeAddContentLen"); node = getNode(0); content = getStr(0); res = xmlNodeAddContentLen( node, content, xmlStrlen(content)); oomReport = res < 0; if (node != NULL) { if (node->type == XML_ELEMENT_NODE || node->type == XML_DOCUMENT_FRAG_NODE) text = node->last; else text = node; checkContent(text); } endOp(); break; } case OP_XML_GET_LINE_NO: incIntIdx(); startOp("xmlGetLineNo"); setInt(0, xmlGetLineNo(getNode(0))); oomReport = 0; endOp(); break; case OP_XML_GET_NODE_PATH: { xmlChar *path; incStrIdx(); startOp("xmlGetNodePath"); path = xmlGetNodePath(getNode(0)); if (path != NULL) oomReport = 0; moveStr(0, path); endOp(); break; } case OP_XML_DOC_SET_ROOT_ELEMENT: { xmlDocPtr oldDoc, doc; xmlNodePtr oldRoot, oldParent, root; startOp("xmlDocSetRootElement"); incNodeIdx(); doc = getDoc(1); root = getNode(2); if (doc != NULL && doc->parent != NULL) doc = NULL; if (!isValidChild((xmlNodePtr) doc, root)) root = NULL; oldDoc = root ? root->doc : NULL; oldParent = root ? root->parent : NULL; oldRoot = xmlDocSetRootElement(doc, root); /* We can't really know whether xmlSetTreeDoc failed */ if (oldRoot != NULL || root == NULL || root->doc == oldDoc) oomReport = 0; setNode(0, oldRoot); if (root && (root->parent != oldParent || root->doc != oldDoc)) { if (fixNs(root) < 0) oomReport = 1; if (oldParent != NULL) dropNode(oldParent); else dropNode((xmlNodePtr) oldDoc); } endOp(); break; } case OP_XML_NODE_IS_TEXT: incIntIdx(); startOp("xmlNodeIsText"); setInt(0, xmlNodeIsText(getNode(0))); oomReport = 0; endOp(); break; case OP_XML_NODE_GET_ATTR_VALUE: { xmlChar *value = NULL; int res; incStrIdx(); startOp("xmlNodeGetAttrValue"); res = xmlNodeGetAttrValue( getNode(0), getStr(1), getStr(2), &value); oomReport = (res < 0); moveStr(0, value); endOp(); break; } case OP_XML_NODE_GET_LANG: { xmlChar *lang; incStrIdx(); startOp("xmlNodeGetLang"); lang = xmlNodeGetLang(getNode(0)); if (lang != NULL) oomReport = 0; moveStr(0, lang); endOp(); break; } case OP_XML_NODE_SET_LANG: { xmlNodePtr node; xmlAttrPtr attr; int res; startOp("xmlNodeSetLang"); node = getNode(0); attr = xmlHasNsProp( node, BAD_CAST "lang", XML_XML_NAMESPACE); xmlFuzzResetMallocFailed(); removeChildren((xmlNodePtr) attr, 0); res = xmlNodeSetLang( node, getStr(0)); oomReport = (res < 0); endOp(); break; } case OP_XML_NODE_GET_SPACE_PRESERVE: { int res; incIntIdx(); startOp("xmlNodeGetSpacePreserve"); res = xmlNodeGetSpacePreserve(getNode(0)); if (res >= 0) oomReport = 0; setInt(0, res); endOp(); break; } case OP_XML_NODE_SET_SPACE_PRESERVE: { xmlNodePtr node; xmlAttrPtr attr; int res; startOp("xmlNodeSetSpacePreserve"); node = getNode(0); attr = xmlHasNsProp( node, BAD_CAST "space", XML_XML_NAMESPACE); xmlFuzzResetMallocFailed(); removeChildren((xmlNodePtr) attr, 0); res = xmlNodeSetSpacePreserve( node, getInt(0)); oomReport = (res < 0); endOp(); break; } case OP_XML_NODE_GET_BASE: { xmlChar *base; incStrIdx(); startOp("xmlNodeGetBase"); base = xmlNodeGetBase( getDoc(0), getNode(1)); if (base != NULL) oomReport = 0; moveStr(0, base); endOp(); break; } case OP_XML_NODE_GET_BASE_SAFE: { xmlChar *base; int res; startOp("xmlNodeGetBaseSafe"); incStrIdx(); res = xmlNodeGetBaseSafe( getDoc(0), getNode(1), &base); oomReport = (res < 0); moveStr(0, base); endOp(); break; } case OP_XML_NODE_SET_BASE: { xmlNodePtr node; xmlAttrPtr attr; int res; startOp("xmlNodeSetBase"); node = getNode(0); attr = xmlHasNsProp( node, BAD_CAST "base", XML_XML_NAMESPACE); xmlFuzzResetMallocFailed(); removeChildren((xmlNodePtr) attr, 0); res = xmlNodeSetBase( node, getStr(0)); if (res == 0) oomReport = 0; endOp(); break; } case OP_XML_IS_BLANK_NODE: startOp("xmlIsBlankNode"); incNodeIdx(); setInt(0, xmlIsBlankNode(getNode(0))); oomReport = 0; break; case OP_XML_HAS_PROP: { xmlNodePtr node; xmlAttrPtr attr; startOp("xmlHasProp"); incNodeIdx(); attr = xmlHasProp( node = getNode(1), getStr(0)); if (node != NULL && node->doc != NULL && node->doc->intSubset != NULL) { /* * xmlHasProp tries to look up default attributes, * requiring a memory allocation which isn't * checked. */ if (attr != NULL) oomReport = 0; } else { oomReport = 0; } setNode(0, (xmlNodePtr) attr); break; } case OP_XML_HAS_NS_PROP: { xmlNodePtr node; xmlAttrPtr attr; startOp("xmlHasNsProp"); incNodeIdx(); attr = xmlHasNsProp( node = getNode(1), getStr(0), getStr(1)); if (node != NULL && node->doc != NULL && node->doc->intSubset != NULL) { if (attr != NULL) oomReport = 0; } else { oomReport = 0; } setNode(0, (xmlNodePtr) attr); break; } case OP_XML_GET_PROP: { xmlChar *content; startOp("xmlGetProp"); incStrIdx(); content = xmlGetProp( getNode(0), getStr(1)); if (content != NULL) oomReport = 0; moveStr(0, content); endOp(); break; } case OP_XML_GET_NS_PROP: { xmlChar *content; startOp("xmlGetNsProp"); incStrIdx(); content = xmlGetNsProp( getNode(0), getStr(1), getStr(2)); if (content != NULL) oomReport = 0; moveStr(0, content); endOp(); break; } case OP_XML_GET_NO_NS_PROP: { xmlChar *content; startOp("xmlGetNoNsProp"); incStrIdx(); content = xmlGetNoNsProp( getNode(0), getStr(1)); if (content != NULL) oomReport = 0; moveStr(0, content); endOp(); break; } case OP_XML_SET_PROP: { xmlNodePtr node; xmlAttrPtr oldAttr, attr; xmlNsPtr ns = NULL; const xmlChar *name, *value, *localName; xmlChar *prefix; int prefixLen; startOp("xmlSetProp"); incNodeIdx(); node = getNode(1); name = getStr(0); value = getStr(1); /* * Find the old attribute node which will be deleted. */ localName = xmlSplitQName3(name, &prefixLen); if (localName != NULL) { prefix = uncheckedStrndup(name, prefixLen); ns = xmlSearchNs(NULL, node, prefix); xmlFree(prefix); } if (ns == NULL) oldAttr = xmlHasNsProp(node, name, NULL); else oldAttr = xmlHasNsProp(node, localName, ns->href); xmlFuzzResetMallocFailed(); if (oldAttr != NULL) removeChildren((xmlNodePtr) oldAttr, 0); attr = xmlSetProp(node, name, value); oomReport = (node != NULL && node->type == XML_ELEMENT_NODE && name != NULL && attr == NULL); setNode(0, (xmlNodePtr) attr); break; } case OP_XML_SET_NS_PROP: { xmlNodePtr node; xmlNsPtr ns; xmlAttrPtr oldAttr, attr; const xmlChar *name, *value; startOp("xmlSetNsProp"); incNodeIdx(); node = getNode(1); ns = nodeGetNs(getNode(2), getInt(0)); name = getStr(0); value = getStr(1); oldAttr = xmlHasNsProp(node, name, ns ? ns->href : NULL); xmlFuzzResetMallocFailed(); if (oldAttr != NULL) removeChildren((xmlNodePtr) oldAttr, 0); attr = xmlSetNsProp(node, ns, name, value); oomReport = ((node == NULL || node->type == XML_ELEMENT_NODE) && (ns == NULL || ns->href != NULL) && name != NULL && attr == NULL); setNode(0, (xmlNodePtr) attr); if (ns != NULL) { if (fixNs((xmlNodePtr) attr) < 0) oomReport = 1; } break; } case OP_XML_REMOVE_PROP: { xmlNodePtr attr, parent = NULL; startOp("xmlRemoveProp"); incIntIdx(); attr = getNode(0); if (attr != NULL) { if (attr->parent != NULL && attr->type == XML_ATTRIBUTE_NODE) removeChildren(attr, 1); else attr = NULL; } if (attr != NULL) parent = attr->parent; setInt(0, xmlRemoveProp((xmlAttrPtr) attr)); oomReport = 0; dropNode(parent); endOp(); break; } case OP_XML_UNSET_PROP: { xmlNodePtr node; xmlAttrPtr attr; const xmlChar *name; startOp("xmlUnsetProp"); incIntIdx(); node = getNode(0); name = getStr(0); attr = xmlHasNsProp(node, name, NULL); xmlFuzzResetMallocFailed(); if (attr != NULL) removeChildren((xmlNodePtr) attr, 1); setInt(0, xmlUnsetProp(node, name)); oomReport = 0; dropNode(node); endOp(); break; } case OP_XML_UNSET_NS_PROP: { xmlNodePtr node; xmlNsPtr ns; xmlAttrPtr attr; const xmlChar *name; startOp("xmlUnsetNsProp"); incIntIdx(); node = getNode(0); ns = nodeGetNs(getNode(1), getInt(1)); name = getStr(0); attr = xmlHasNsProp(node, name, ns ? ns->href : NULL); xmlFuzzResetMallocFailed(); if (attr != NULL) removeChildren((xmlNodePtr) attr, 1); setInt(0, xmlUnsetNsProp(node, ns, name)); oomReport = 0; dropNode(node); endOp(); break; } case OP_XML_NEW_NS: { xmlNodePtr node; xmlNsPtr ns; startOp("xmlNewNs"); ns = xmlNewNs( node = getNode(0), getStr(0), getStr(1)); if (ns != NULL) oomReport = 0; if (node == NULL) xmlFreeNs(ns); endOp(); break; } case OP_XML_SEARCH_NS: { xmlNsPtr ns; startOp("xmlSearchNs"); ns = xmlSearchNs( getDoc(0), getNode(1), getStr(0)); if (ns != NULL) oomReport = 0; endOp(); break; } case OP_XML_SEARCH_NS_BY_HREF: { xmlNsPtr ns; startOp("xmlSearchNsByHref"); ns = xmlSearchNsByHref( getDoc(0), getNode(1), getStr(0)); if (ns != NULL) oomReport = 0; endOp(); break; } case OP_XML_GET_NS_LIST: { xmlNsPtr *list; startOp("xmlGetNsList"); list = xmlGetNsList( getDoc(0), getNode(1)); if (list != NULL) oomReport = 0; xmlFree(list); endOp(); break; } case OP_XML_GET_NS_LIST_SAFE: { xmlNsPtr *list; int res; startOp("xmlGetNsList"); res = xmlGetNsListSafe( getDoc(0), getNode(1), &list); oomReport = (res < 0); xmlFree(list); endOp(); break; } case OP_XML_SET_NS: { xmlNodePtr node; xmlNsPtr ns; startOp("xmlSetNs"); node = getNode(0), ns = nodeGetNs(getNode(1), getInt(0)); xmlSetNs(node, ns); oomReport = 0; if (ns != NULL) { if (fixNs(node) < 0) oomReport = 1; } endOp(); break; } case OP_XML_COPY_NAMESPACE: { xmlNsPtr ns, copy; startOp("xmlCopyNamespace"); copy = xmlCopyNamespace( ns = nodeGetNs(getNode(0), getInt(0))); oomReport = (ns != NULL && copy == NULL); xmlFreeNs(copy); endOp(); break; } case OP_XML_COPY_NAMESPACE_LIST: { xmlNsPtr list, copy; startOp("xmlCopyNamespaceList"); copy = xmlCopyNamespaceList( list = nodeGetNs(getNode(0), getInt(0))); oomReport = (list != NULL && copy == NULL); xmlFreeNsList(copy); endOp(); break; } case OP_XML_UNLINK_NODE: { xmlNodePtr node, oldParent; xmlDocPtr doc; startOp("xmlUnlinkNode"); node = getNode(0); doc = node ? node->doc : NULL; /* * Unlinking DTD children can cause invalid references * which would be expensive to fix. * * Don't unlink DTD if it is the internal or external * subset of the document. */ if (node != NULL && (isDtdChild(node) || (node->type == XML_DTD_NODE && doc != NULL && ((xmlDtdPtr) node == doc->intSubset || (xmlDtdPtr) node == doc->extSubset)))) node = NULL; oldParent = node ? node->parent : NULL; xmlUnlinkNode(node); oomReport = 0; if (node != NULL && node->parent != oldParent) { if (fixNs(node) < 0) oomReport = 1; dropNode(oldParent); } endOp(); break; } case OP_XML_REPLACE_NODE: { xmlNodePtr old, oldParent, node, oldNodeParent, result; xmlDocPtr oldNodeDoc; startOp("xmlReplaceNode"); old = getNode(0); node = getNode(1); /* * Unlinking DTD children can cause invalid references * which would be expensive to fix. */ if (isDtdChild(old)) old = NULL; if (old != NULL && !isValidChild(old->parent, node)) node = NULL; oldParent = old ? old->parent : NULL; oldNodeParent = node ? node->parent : NULL; oldNodeDoc = node ? node->doc : NULL; result = xmlReplaceNode(old, node); oomReport = (old != NULL && old->parent != NULL && node != NULL && old != node && result == NULL); if (old != NULL && old->parent != oldParent) { if (fixNs(old) < 0) oomReport = 1; } if (node == NULL) { /* Old node was unlinked */ dropNode(oldParent); } else if (node->parent != oldNodeParent || node->doc != oldNodeDoc) { if (fixNs(node) < 0) oomReport = 1; /* Drop old parent of new node */ if (oldNodeParent != NULL) dropNode(oldNodeParent); else dropNode((xmlNodePtr) oldNodeDoc); } endOp(); break; } case OP_XML_ADD_CHILD: case OP_XML_ADD_SIBLING: case OP_XML_ADD_PREV_SIBLING: case OP_XML_ADD_NEXT_SIBLING: { xmlNodePtr target, parent, node, oldNodeParent, result; xmlDocPtr oldNodeDoc; int argsOk; switch (op) { case OP_XML_ADD_CHILD: startOp("xmlAddChild"); break; case OP_XML_ADD_SIBLING: startOp("xmlAddSibling"); break; case OP_XML_ADD_PREV_SIBLING: startOp("xmlAddPrevSibling"); break; case OP_XML_ADD_NEXT_SIBLING: startOp("xmlAddNextSibling"); break; } if (op == OP_XML_ADD_CHILD) { target = NULL; parent = getNode(0); } else { target = getNode(0); parent = target ? target->parent : NULL; } node = getNode(1); /* Don't append to root node */ if (target != NULL && parent == NULL) node = NULL; /* Check tree structure */ if (isDtdChild(node) || !isValidChild(parent, node)) node = NULL; /* Attributes */ if (node != NULL && node->type == XML_ATTRIBUTE_NODE) { if ((op == OP_XML_ADD_CHILD) || ((target != NULL && (target->type == XML_ATTRIBUTE_NODE)))) { xmlAttrPtr attr = xmlHasNsProp(parent, node->name, node->ns ? node->ns->href : NULL); xmlFuzzResetMallocFailed(); /* Attribute might be replaced */ if (attr != NULL && attr != (xmlAttrPtr) node) removeChildren((xmlNodePtr) attr, 1); } else { target = NULL; } } else if (target != NULL && target->type == XML_ATTRIBUTE_NODE) { node = NULL; } oldNodeParent = node ? node->parent : NULL; oldNodeDoc = node ? node->doc : NULL; argsOk = (target != NULL && node != NULL && target != node); switch (op) { case OP_XML_ADD_CHILD: argsOk = (parent != NULL && node != NULL); result = xmlAddChild(parent, node); break; case OP_XML_ADD_SIBLING: result = xmlAddSibling(target, node); break; case OP_XML_ADD_PREV_SIBLING: result = xmlAddPrevSibling(target, node); break; case OP_XML_ADD_NEXT_SIBLING: result = xmlAddNextSibling(target, node); break; } oomReport = (argsOk && result == NULL); if (result != NULL && result != node) { /* Text node was merged */ removeNode(node); checkContent(result); /* Drop old parent of node */ if (oldNodeParent != NULL) dropNode(oldNodeParent); else dropNode((xmlNodePtr) oldNodeDoc); } else if (node != NULL && (node->parent != oldNodeParent || node->doc != oldNodeDoc)) { if (fixNs(node) < 0) oomReport = 1; /* Drop old parent of node */ if (oldNodeParent != NULL) dropNode(oldNodeParent); else dropNode((xmlNodePtr) oldNodeDoc); } endOp(); break; } case OP_XML_TEXT_MERGE: { xmlNodePtr first, second, parent = NULL, res; int argsOk; startOp("xmlTextMerge"); first = getNode(0); second = getNode(1); argsOk = (first != NULL && first->type == XML_TEXT_NODE && second != NULL && second->type == XML_TEXT_NODE && first != second && first->name == second->name); if (argsOk) { if (second->parent != NULL) parent = second->parent; else parent = (xmlNodePtr) second->doc; } res = xmlTextMerge(first, second); oomReport = (argsOk && res == NULL); if (res != NULL) { removeNode(second); dropNode(parent); checkContent(first); } endOp(); break; } case OP_XML_TEXT_CONCAT: { xmlNodePtr node; const xmlChar *text; int res; startOp("xmlTextConcat"); node = getNode(0); text = getStr(0); res = xmlTextConcat( node, text, xmlStrlen(text)); oomReport = (isTextContentNode(node) && res < 0); checkContent(node); endOp(); break; } case OP_XML_STRING_GET_NODE_LIST: { xmlNodePtr list; const xmlChar *value; startOp("xmlStringGetNodeList"); list = xmlStringGetNodeList( getDoc(0), value = getStr(0)); oomReport = (value != NULL && list == NULL); xmlFreeNodeList(list); endOp(); break; } case OP_XML_STRING_LEN_GET_NODE_LIST: { xmlDocPtr doc; xmlNodePtr list; const xmlChar *value; startOp("xmlStringLenGetNodeList"); doc = getDoc(0); value = getStr(0); list = xmlStringLenGetNodeList( doc, value, xmlStrlen(value)); oomReport = (value != NULL && list == NULL); xmlFreeNodeList(list); endOp(); break; } case OP_XML_NODE_LIST_GET_STRING: { xmlChar *string; startOp("xmlNodeListGetString"); incStrIdx(); string = xmlNodeListGetString( getDoc(0), getNode(1), getInt(0)); oomReport = (string == NULL); moveStr(0, string); endOp(); break; } case OP_XML_NODE_LIST_GET_RAW_STRING: { xmlChar *string; startOp("xmlNodeListGetRawString"); incStrIdx(); string = xmlNodeListGetRawString( getDoc(0), getNode(1), getInt(0)); oomReport = (string == NULL); moveStr(0, string); endOp(); break; } case OP_XML_IS_XHTML: startOp("xmlIsXHTML"); incIntIdx(); setInt(0, xmlIsXHTML( getStr(0), getStr(1))); oomReport = 0; break; case OP_XML_ADD_ELEMENT_DECL: { xmlElementPtr decl; startOp("xmlAddElementDecl"); incNodeIdx(); decl = xmlAddElementDecl( NULL, getDtd(1), getStr(0), (xmlElementTypeVal) getInt(0), NULL); if (decl != NULL) oomReport = 0; setNode(0, (xmlNodePtr) decl); break; } case OP_XML_ADD_ATTRIBUTE_DECL: { xmlAttributePtr decl; startOp("xmlAddAttributeDecl"); incNodeIdx(); decl = xmlAddAttributeDecl( NULL, getDtd(1), getStr(0), getStr(1), getStr(2), (xmlAttributeType) getInt(0), (xmlAttributeDefault) getInt(1), getStr(3), NULL); if (decl != NULL) oomReport = 0; setNode(0, (xmlNodePtr) decl); break; } case OP_XML_ADD_NOTATION_DECL: { xmlNotationPtr decl; startOp("xmlAddNotationDecl"); decl = xmlAddNotationDecl( NULL, getDtd(1), getStr(0), getStr(1), getStr(2)); if (decl != NULL) oomReport = 0; endOp(); break; } case OP_XML_GET_DTD_ELEMENT_DESC: { xmlElementPtr elem; startOp("xmlGetDtdElementDesc"); incNodeIdx(); elem = xmlGetDtdElementDesc( getDtd(1), getStr(0)); if (elem != NULL) oomReport = 0; /* * Don't reference XML_ELEMENT_TYPE_UNDEFINED dummy * declarations. */ if (elem != NULL && elem->parent == NULL) elem = NULL; setNode(0, (xmlNodePtr) elem); break; } case OP_XML_GET_DTD_QELEMENT_DESC: { xmlElementPtr elem; startOp("xmlGetDtdQElementDesc"); incNodeIdx(); elem = xmlGetDtdQElementDesc( getDtd(1), getStr(0), getStr(1)); oomReport = 0; if (elem != NULL && elem->parent == NULL) elem = NULL; setNode(0, (xmlNodePtr) elem); break; } case OP_XML_GET_DTD_ATTR_DESC: { xmlAttributePtr decl; startOp("xmlGetDtdAttrDesc"); incNodeIdx(); decl = xmlGetDtdAttrDesc( getDtd(1), getStr(0), getStr(1)); if (decl != NULL) oomReport = 0; setNode(0, (xmlNodePtr) decl); break; } case OP_XML_GET_DTD_QATTR_DESC: { xmlAttributePtr decl; startOp("xmlGetDtdQAttrDesc"); incNodeIdx(); decl = xmlGetDtdQAttrDesc( getDtd(1), getStr(0), getStr(1), getStr(2)); oomReport = 0; setNode(0, (xmlNodePtr) decl); break; } case OP_XML_GET_DTD_NOTATION_DESC: startOp("xmlGetDtdNotationDesc"); xmlGetDtdNotationDesc( getDtd(1), getStr(0)); oomReport = 0; endOp(); break; case OP_XML_ADD_ID: startOp("xmlAddID"); xmlAddID( NULL, getDoc(0), getStr(0), getAttr(1)); endOp(); break; case OP_XML_ADD_ID_SAFE: { int res; startOp("xmlAddIDSafe"); res = xmlAddIDSafe( getAttr(0), getStr(0)); oomReport = (res < 0); endOp(); break; } case OP_XML_GET_ID: startOp("xmlGetID"); incNodeIdx(); setNode(0, (xmlNodePtr) xmlGetID( getDoc(1), getStr(0))); oomReport = 0; break; case OP_XML_IS_ID: { int res; startOp("xmlIsID"); res = xmlIsID( getDoc(2), getNode(1), getAttr(0)); oomReport = (res < 0); endOp(); break; } case OP_XML_REMOVE_ID: startOp("xmlRemoveID"); xmlRemoveID( getDoc(1), getAttr(0)); oomReport = 0; endOp(); break; case OP_XML_ADD_REF: { xmlDocPtr doc; xmlAttrPtr attr; xmlRefPtr ref; const xmlChar *value; startOp("xmlAddRef"); ref = xmlAddRef( NULL, doc = getDoc(0), value = getStr(0), attr = getAttr(1)); oomReport = (doc != NULL && value != NULL && attr != NULL && ref == NULL); endOp(); break; } case OP_XML_GET_REFS: startOp("xmlGetRefs"); xmlGetRefs( getDoc(1), getStr(0)); oomReport = 0; endOp(); break; case OP_XML_IS_REF: startOp("xmlIsRef"); xmlIsRef( getDoc(2), getNode(1), getAttr(0)); oomReport = 0; endOp(); break; case OP_XML_REMOVE_REF: { int res; startOp("xmlRemoveRef"); res = xmlRemoveRef( getDoc(1), getAttr(0)); if (res == 0) oomReport = 0; endOp(); break; } case OP_XML_NEW_ENTITY: { xmlDocPtr doc; xmlEntityPtr ent; startOp("xmlNewEntity"); incNodeIdx(); ent = xmlNewEntity( doc = getDoc(1), getStr(0), getInt(0), getStr(1), getStr(2), getStr(3)); if (ent != NULL) oomReport = 0; if (doc == NULL || doc->intSubset == NULL) { xmlFreeEntity(ent); ent = NULL; } setNode(0, (xmlNodePtr) ent); break; } case OP_XML_ADD_ENTITY: { xmlEntityPtr ent; int res; startOp("xmlAddEntity"); incNodeIdx(); res = xmlAddEntity( getDoc(1), getInt(0), getStr(0), getInt(1), getStr(1), getStr(2), getStr(3), &ent); oomReport = (res == XML_ERR_NO_MEMORY); setNode(0, (xmlNodePtr) ent); break; } case OP_XML_ADD_DOC_ENTITY: { xmlEntityPtr ent; startOp("xmlAddDocEntity"); incNodeIdx(); ent = xmlAddDocEntity( getDoc(1), getStr(0), getInt(1), getStr(1), getStr(2), getStr(3)); if (ent != NULL) oomReport = 0; setNode(0, (xmlNodePtr) ent); break; } case OP_XML_ADD_DTD_ENTITY: { xmlEntityPtr ent; startOp("xmlAddDtdEntity"); incNodeIdx(); ent = xmlAddDtdEntity( getDoc(1), getStr(0), getInt(1), getStr(1), getStr(2), getStr(3)); setNode(0, (xmlNodePtr) ent); break; } case OP_XML_GET_PREDEFINED_ENTITY: startOp("xmlGetPredefinedEntity"); incNodeIdx(); setNode(0, (xmlNodePtr) xmlGetPredefinedEntity( getStr(0))); oomReport = 0; break; case OP_XML_GET_DOC_ENTITY: startOp("xmlGetDocEntity"); incNodeIdx(); setNode(0, (xmlNodePtr) xmlGetDocEntity( getDoc(1), getStr(0))); oomReport = 0; break; case OP_XML_GET_DTD_ENTITY: startOp("xmlGetDtdEntity"); incNodeIdx(); setNode(0, (xmlNodePtr) xmlGetDtdEntity( getDoc(1), getStr(0))); oomReport = 0; break; case OP_XML_GET_PARAMETER_ENTITY: startOp("xmlGetParameterEntity"); incNodeIdx(); setNode(0, (xmlNodePtr) xmlGetParameterEntity( getDoc(1), getStr(0))); oomReport = 0; break; case OP_XML_ENCODE_ENTITIES_REENTRANT: { const xmlChar *string; xmlChar *encoded; startOp("xmlEncodeEntitiesReentrant"); incStrIdx(); encoded = xmlEncodeEntitiesReentrant( getDoc(0), string = getStr(1)); oomReport = (string != NULL && encoded == NULL); moveStr(0, encoded); endOp(); break; } case OP_XML_ENCODE_SPECIAL_CHARS: { const xmlChar *string; xmlChar *encoded; startOp("xmlEncodespecialChars"); incStrIdx(); encoded = xmlEncodeSpecialChars( getDoc(0), string = getStr(1)); oomReport = (string != NULL && encoded == NULL); moveStr(0, encoded); endOp(); break; } #ifdef LIBXML_HTML_ENABLED case OP_HTML_NEW_DOC: { htmlDocPtr doc; startOp("htmlNewDoc"); incNodeIdx(); doc = htmlNewDoc( getStr(0), getStr(1)); oomReport = (doc == NULL); setNode(0, (xmlNodePtr) doc); break; } case OP_HTML_NEW_DOC_NO_DTD: { htmlDocPtr doc; startOp("htmlNewDocNoDtD"); incNodeIdx(); doc = htmlNewDocNoDtD( getStr(0), getStr(1)); oomReport = (doc == NULL); setNode(0, (xmlNodePtr) doc); break; } case OP_HTML_GET_META_ENCODING: { const xmlChar *encoding; startOp("htmlGetMetaEncoding"); incStrIdx(); encoding = htmlGetMetaEncoding(getDoc(0)); if (encoding != NULL) oomReport = 0; copyStr(0, encoding); break; } case OP_HTML_SET_META_ENCODING: /* TODO (can destroy inner text) */ break; case OP_HTML_IS_BOOLEAN_ATTR: startOp("htmlIsBooleanAttr"); htmlIsBooleanAttr(getStr(0)); oomReport = 0; endOp(); break; #endif #ifdef LIBXML_VALID_ENABLED case OP_VALIDATE: { xmlNodePtr node; int type; int res = 1; startOp("validate"); incIntIdx(); node = getNode(0); type = node ? node->type : 0; xmlValidCtxtPtr vctxt = xmlNewValidCtxt(); xmlFuzzResetMallocFailed(); switch (type) { case XML_DOCUMENT_NODE: case XML_HTML_DOCUMENT_NODE: res = xmlValidateDocument(vctxt, (xmlDocPtr) node); break; case XML_ELEMENT_DECL: res = xmlValidateElementDecl(vctxt, node->doc, (xmlElementPtr) node); break; case XML_ATTRIBUTE_DECL: res = xmlValidateAttributeDecl(vctxt, node->doc, (xmlAttributePtr) node); break; case XML_ELEMENT_NODE: res = xmlValidateElement(vctxt, node->doc, node); break; default: break; } if (res != 0) oomReport = 0; xmlFreeValidCtxt(vctxt); setInt(0, res); endOp(); break; } case OP_XML_VALIDATE_DTD: { xmlValidCtxtPtr vctxt; int res; startOp("xmlValidateDtd"); incIntIdx(); vctxt = xmlNewValidCtxt(); res = xmlValidateDtd( vctxt, getDoc(0), getDtd(1)); if (res != 0) oomReport = 0; xmlFreeValidCtxt(vctxt); setInt(0, res); endOp(); break; } #endif /* LIBXML_VALID_ENABLED */ #ifdef LIBXML_OUTPUT_ENABLED case OP_XML_DOC_DUMP_MEMORY: case OP_XML_DOC_DUMP_MEMORY_ENC: case OP_XML_DOC_DUMP_FORMAT_MEMORY: case OP_XML_DOC_DUMP_FORMAT_MEMORY_ENC: case OP_HTML_DOC_DUMP_MEMORY: case OP_HTML_DOC_DUMP_MEMORY_FORMAT: { xmlDocPtr doc; xmlChar *out = NULL; int outSize = 0; switch (op) { case OP_XML_DOC_DUMP_MEMORY: startOp("xmlDocDumpMemory"); break; case OP_XML_DOC_DUMP_MEMORY_ENC: startOp("xmlDocDumpMemoryEnc"); break; case OP_XML_DOC_DUMP_FORMAT_MEMORY: startOp("xmlDocDumpFormatMemory"); break; case OP_XML_DOC_DUMP_FORMAT_MEMORY_ENC: startOp("xmlDocDumpFormatMemoryEnc"); break; case OP_HTML_DOC_DUMP_MEMORY: startOp("htmlDocDumpMemory"); break; case OP_HTML_DOC_DUMP_MEMORY_FORMAT: startOp("htmlDocDumpMemoryFormat"); break; } incStrIdx(); doc = getDoc(0); switch (op) { case OP_XML_DOC_DUMP_MEMORY: xmlDocDumpMemory(doc, &out, &outSize); break; case OP_XML_DOC_DUMP_MEMORY_ENC: xmlDocDumpMemoryEnc(doc, &out, &outSize, (const char *) getStr(1)); break; case OP_XML_DOC_DUMP_FORMAT_MEMORY: xmlDocDumpFormatMemory(doc, &out, &outSize, getInt(0)); break; case OP_XML_DOC_DUMP_FORMAT_MEMORY_ENC: xmlDocDumpFormatMemoryEnc(doc, &out, &outSize, (const char *) getStr(1), getInt(0)); break; #ifdef LIBXML_HTML_ENABLED case OP_HTML_DOC_DUMP_MEMORY: htmlDocDumpMemory(doc, &out, &outSize); break; case OP_HTML_DOC_DUMP_MEMORY_FORMAT: htmlDocDumpMemoryFormat(doc, &out, &outSize, getInt(0)); break; #endif /* LIBXML_HTML_ENABLED */ } /* Could be an unknown encoding */ if (out != NULL) oomReport = 0; moveStr(0, out); endOp(); break; } case OP_XML_NODE_DUMP: case OP_XML_NODE_BUF_GET_CONTENT: case OP_XML_ATTR_SERIALIZE_TXT_CONTENT: case OP_XML_DUMP_ELEMENT_DECL: case OP_XML_DUMP_ELEMENT_TABLE: case OP_XML_DUMP_ATTRIBUTE_DECL: case OP_XML_DUMP_ATTRIBUTE_TABLE: case OP_XML_DUMP_ENTITY_DECL: case OP_XML_DUMP_ENTITIES_TABLE: case OP_XML_DUMP_NOTATION_DECL: case OP_XML_DUMP_NOTATION_TABLE: case OP_HTML_NODE_DUMP: { xmlNodePtr node; xmlDocPtr doc; xmlBufferPtr buffer; xmlChar *dump; int level, format, res; switch (op) { case OP_XML_NODE_DUMP: startOp("xmlNodeDump"); break; case OP_XML_NODE_BUF_GET_CONTENT: startOp("xmlNodeBufGetContent"); break; case OP_XML_ATTR_SERIALIZE_TXT_CONTENT: startOp("xmlAttrSerializeTxtContent"); break; case OP_XML_DUMP_ELEMENT_DECL: startOp("xmlDumpElementDecl"); break; case OP_XML_DUMP_ELEMENT_TABLE: startOp("xmlDumpElementTable"); break; case OP_XML_DUMP_ATTRIBUTE_DECL: startOp("xmlDumpAttributeDecl"); break; case OP_XML_DUMP_ATTRIBUTE_TABLE: startOp("xmlDumpAttributeTable"); break; case OP_XML_DUMP_ENTITY_DECL: startOp("xmlDumpEntityDecl"); break; case OP_XML_DUMP_ENTITIES_TABLE: startOp("xmlDumpEntitiesTable"); break; case OP_XML_DUMP_NOTATION_DECL: startOp("xmlDumpNotationDecl"); break; case OP_XML_DUMP_NOTATION_TABLE: startOp("xmlDumpNotationTable"); break; case OP_HTML_NODE_DUMP: startOp("htmlNodeDump"); break; } incStrIdx(); buffer = xmlBufferCreate(); xmlFuzzResetMallocFailed(); node = getNode(0); doc = node ? node->doc : NULL; level = getInt(0); format = getInt(0); res = 0; switch (op) { case OP_XML_NODE_DUMP: res = xmlNodeDump(buffer, doc, node, level, format); break; case OP_XML_NODE_BUF_GET_CONTENT: res = xmlNodeBufGetContent(buffer, node); break; case OP_XML_ATTR_SERIALIZE_TXT_CONTENT: if (node != NULL && node->type != XML_ATTRIBUTE_NODE) node = NULL; xmlAttrSerializeTxtContent( buffer, doc, (xmlAttrPtr) node, getStr(1)); break; case OP_XML_DUMP_ELEMENT_DECL: if (node != NULL && node->type != XML_ELEMENT_DECL) node = NULL; xmlDumpElementDecl(buffer, (xmlElementPtr) node); break; case OP_XML_DUMP_ATTRIBUTE_DECL: if (node != NULL && node->type != XML_ATTRIBUTE_DECL) node = NULL; xmlDumpAttributeDecl(buffer, (xmlAttributePtr) node); break; case OP_XML_DUMP_NOTATION_DECL: /* TODO */ break; case OP_XML_DUMP_ENTITY_DECL: if (node != NULL && node->type != XML_ENTITY_DECL) node = NULL; xmlDumpEntityDecl(buffer, (xmlEntityPtr) node); break; case OP_XML_DUMP_ELEMENT_TABLE: { xmlElementTablePtr table; table = node != NULL && node->type == XML_DTD_NODE ? ((xmlDtdPtr) node)->elements : NULL; xmlDumpElementTable(buffer, table); break; } case OP_XML_DUMP_ATTRIBUTE_TABLE: { xmlAttributeTablePtr table; table = node != NULL && node->type == XML_DTD_NODE ? ((xmlDtdPtr) node)->attributes : NULL; xmlDumpAttributeTable(buffer, table); break; } case OP_XML_DUMP_NOTATION_TABLE: { xmlNotationTablePtr table; table = node != NULL && node->type == XML_DTD_NODE ? ((xmlDtdPtr) node)->notations : NULL; xmlDumpNotationTable(buffer, table); break; } case OP_XML_DUMP_ENTITIES_TABLE: { xmlEntitiesTablePtr table; table = node != NULL && node->type == XML_DTD_NODE ? ((xmlDtdPtr) node)->entities : NULL; xmlDumpEntitiesTable(buffer, table); break; } #ifdef LIBXML_HTML_ENABLED case OP_HTML_NODE_DUMP: res = htmlNodeDump(buffer, doc, node); break; #endif /* LIBXML_HTML_ENABLED */ } dump = xmlBufferDetach(buffer); if (res == 0 && dump != NULL) oomReport = 0; moveStr(0, dump); xmlBufferFree(buffer); endOp(); break; } case OP_XML_SAVE_FILE_TO: case OP_XML_SAVE_FORMAT_FILE_TO: case OP_XML_NODE_DUMP_OUTPUT: case OP_HTML_DOC_CONTENT_DUMP_OUTPUT: case OP_HTML_DOC_CONTENT_DUMP_FORMAT_OUTPUT: case OP_HTML_NODE_DUMP_OUTPUT: case OP_HTML_NODE_DUMP_FORMAT_OUTPUT: { xmlNodePtr node; xmlDocPtr doc; xmlOutputBufferPtr output; const char *encoding; int level, format, argsOk, res, closed; switch (op) { case OP_XML_SAVE_FILE_TO: startOp("xmlSaveFileTo"); break; case OP_XML_SAVE_FORMAT_FILE_TO: startOp("xmlSaveFormatFileTo"); break; case OP_XML_NODE_DUMP_OUTPUT: startOp("xmlNodeDumpOutput"); break; case OP_HTML_DOC_CONTENT_DUMP_OUTPUT: startOp("htmlDocContentDumpOutput"); break; case OP_HTML_DOC_CONTENT_DUMP_FORMAT_OUTPUT: startOp("htmlDocContentDumpFormatOutput"); break; case OP_HTML_NODE_DUMP_OUTPUT: startOp("htmlNodeDumpOutput"); break; case OP_HTML_NODE_DUMP_FORMAT_OUTPUT: startOp("htmlNodeDumpFormatOutput"); break; } incStrIdx(); output = xmlAllocOutputBuffer(NULL); xmlFuzzResetMallocFailed(); node = getNode(0); doc = node ? node->doc : NULL; encoding = (const char *) getStr(1); level = getInt(0); format = getInt(0); argsOk = (output != NULL); res = 0; closed = 0; switch (op) { case OP_XML_SAVE_FILE_TO: argsOk &= (doc != NULL); res = xmlSaveFileTo(output, doc, encoding); closed = 1; break; case OP_XML_SAVE_FORMAT_FILE_TO: argsOk &= (doc != NULL); res = xmlSaveFormatFileTo(output, doc, encoding, format); closed = 1; break; case OP_XML_NODE_DUMP_OUTPUT: argsOk &= (node != NULL); xmlNodeDumpOutput(output, doc, node, level, format, encoding); break; #ifdef LIBXML_HTML_ENABLED case OP_HTML_DOC_CONTENT_DUMP_OUTPUT: argsOk &= (doc != NULL); htmlDocContentDumpOutput(output, doc, encoding); break; case OP_HTML_DOC_CONTENT_DUMP_FORMAT_OUTPUT: argsOk &= (doc != NULL); htmlDocContentDumpFormatOutput(output, doc, encoding, format); break; case OP_HTML_NODE_DUMP_OUTPUT: argsOk &= (node != NULL); htmlNodeDumpOutput(output, doc, node, encoding); break; case OP_HTML_NODE_DUMP_FORMAT_OUTPUT: argsOk &= (node != NULL); htmlNodeDumpFormatOutput(output, doc, node, encoding, format); break; #endif /* LIBXML_HTML_ENABLED */ } if (closed) { if (res >= 0) oomReport = 0; moveStr(0, NULL); } else { oomReport = (output != NULL && output->error == XML_ERR_NO_MEMORY); if (argsOk && !output->error) copyStr(0, xmlBufContent(output->buffer)); else moveStr(0, NULL); xmlOutputBufferClose(output); } endOp(); break; } #endif /* LIBXML_OUTPUT_ENABLED */ case OP_XML_DOM_WRAP_RECONCILE_NAMESPACES: { xmlNodePtr node; int res; startOp("xmlDOMWrapReconcileNamespaces"); res = xmlDOMWrapReconcileNamespaces( NULL, node = getNode(0), getInt(0)); oomReport = (node != NULL && node->doc != NULL && node->type == XML_ELEMENT_NODE && res < 0); endOp(); break; } case OP_XML_DOM_WRAP_ADOPT_NODE: { xmlDOMWrapCtxtPtr ctxt; xmlDocPtr doc, destDoc, oldDoc; xmlNodePtr node, destParent, oldParent; int res; startOp("xmlDOMWrapAdoptNode"); ctxt = xmlDOMWrapNewCtxt(); doc = getDoc(0); node = getNode(1); destDoc = getDoc(2); destParent = getNode(3); if (!isValidChild(destParent, node)) destParent = NULL; oldParent = node ? node->parent : NULL; oldDoc = node ? node->doc : NULL; res = xmlDOMWrapAdoptNode( ctxt, doc, node, destDoc, destParent, getInt(0)); if (ctxt == NULL) oomReport = 1; else if (res == 0) oomReport = 0; if (node != NULL) { /* Node can reference destParent's namespaces */ if (destParent != NULL && node->parent == NULL && node->doc == destParent->doc) { if (node->type == XML_ATTRIBUTE_NODE) { xmlNodePtr prop; /* Insert without removing duplicates */ node->parent = destParent; prop = (xmlNodePtr) destParent->properties; node->next = prop; if (prop != NULL) prop->prev = node; destParent->properties = (xmlAttrPtr) node; } else if (node->type != XML_TEXT_NODE) { xmlAddChild(destParent, node); } } /* Node can be unlinked and moved to a new document. */ if (oldParent != NULL && node->parent != oldParent) dropNode(oldParent); else if (node->doc != oldDoc) dropNode((xmlNodePtr) oldDoc); } xmlDOMWrapFreeCtxt(ctxt); endOp(); break; } case OP_XML_DOM_WRAP_REMOVE_NODE: { xmlDocPtr doc; xmlNodePtr node, oldParent; int res; startOp("xmlDOMWrapRemoveNode"); doc = getDoc(0); node = getNode(1); oldParent = node ? node->parent : NULL; res = xmlDOMWrapRemoveNode(NULL, doc, node, 0); oomReport = (node != NULL && doc != NULL && node->doc == doc && res < 0); if (node != NULL && node->parent != oldParent) { if (fixNs(node) < 0) oomReport = 1; dropNode(oldParent); } endOp(); break; } case OP_XML_DOM_WRAP_CLONE_NODE: { xmlDOMWrapCtxtPtr ctxt; xmlDocPtr doc, destDoc; xmlNodePtr node, destParent, copy = NULL; int res; startOp("xmlDOMWrapCloneNode"); incNodeIdx(); ctxt = xmlDOMWrapNewCtxt(); doc = getDoc(1); node = getNode(2); destDoc = getDoc(3); destParent = getNode(4); if (destParent != NULL && node != NULL && !isValidChildType(destParent, node->type)) destParent = NULL; /* xmlDOMWrapCloneNode returns a garbage node on error. */ res = xmlDOMWrapCloneNode( ctxt, doc, node, ©, destDoc, destParent, getInt(0), 0); if (ctxt == NULL) oomReport = 1; else if (res == 0) oomReport = 0; copy = checkCopy(copy); /* Copy can reference destParent's namespaces */ if (destParent != NULL && copy != NULL) { if (copy->type == XML_ATTRIBUTE_NODE) { xmlNodePtr prop; /* Insert without removing duplicates */ copy->parent = destParent; prop = (xmlNodePtr) destParent->properties; copy->next = prop; if (prop != NULL) prop->prev = copy; destParent->properties = (xmlAttrPtr) copy; } else if (copy->type != XML_TEXT_NODE) { xmlAddChild(destParent, copy); } } xmlDOMWrapFreeCtxt(ctxt); setNode(0, copy); break; } case OP_XML_CHILD_ELEMENT_COUNT: startOp("xmlChildElementCount"); incIntIdx(); setInt(0, xmlChildElementCount(getNode(0))); oomReport = 0; break; case OP_XML_FIRST_ELEMENT_CHILD: startOp("xmlFirstElementChild"); incNodeIdx(); setNode(0, xmlFirstElementChild(getNode(1))); oomReport = 0; break; case OP_XML_LAST_ELEMENT_CHILD: startOp("xmlLastElementChild"); incNodeIdx(); setNode(0, xmlLastElementChild(getNode(1))); oomReport = 0; break; case OP_XML_NEXT_ELEMENT_SIBLING: startOp("xmlNextElementSibling"); incNodeIdx(); setNode(0, xmlNextElementSibling(getNode(1))); oomReport = 0; break; case OP_XML_PREVIOUS_ELEMENT_SIBLING: startOp("xmlPreviousElementSibling"); incNodeIdx(); setNode(0, xmlPreviousElementSibling(getNode(1))); oomReport = 0; break; default: break; } xmlFuzzCheckMallocFailure(vars->opName, oomReport); } for (i = 0; i < REG_MAX; i++) xmlFree(vars->strings[i]); for (i = 0; i < REG_MAX; i++) { xmlNodePtr node = vars->nodes[i]; vars->nodes[i] = NULL; dropNode(node); } xmlFuzzMemSetLimit(0); xmlFuzzDataCleanup(); xmlResetLastError(); return(0); }