blob: 3ac5c1fc02ae49c21047aee164201bd6a7daf905 [file] [log] [blame]
/*
* 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 <stdlib.h>
#include <string.h>
#define XML_DEPRECATED
#include <libxml/catalog.h>
#include <libxml/HTMLtree.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
#include <libxml/xmlerror.h>
#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);
xmlSetExternalEntityLoader(xmlFuzzEntityLoader);
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,
&copy,
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);
}