mirror of
https://gitlab.gnome.org/GNOME/libxml2
synced 2025-03-28 21:33:13 +00:00

Implement a custom mutator that takes a list of fixed-size chunks which are mutated with a given probability. This makes sure that values like parser options or failure position are mutated regularly even as the fuzz data grows large. Values can also be adjusted temporarily to make the fuzzer focus on failure injection, for example. Thanks to David Kilzer for the idea.
3609 lines
108 KiB
C
3609 lines
108 KiB
C
/*
|
|
* 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>
|
|
|
|
#ifndef XML_DEPRECATED
|
|
#define XML_DEPRECATED
|
|
#endif
|
|
|
|
#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);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
LLVMFuzzerTestOneInput(const char *data, size_t size) {
|
|
size_t failurePos;
|
|
int i;
|
|
|
|
if (size > 1000)
|
|
return 0;
|
|
|
|
memset(vars, 0, sizeof(*vars));
|
|
|
|
xmlFuzzDataInit(data, size);
|
|
|
|
failurePos = xmlFuzzReadInt(4) % (size * 50 + 10);
|
|
xmlFuzzInjectFailure(failurePos);
|
|
|
|
/*
|
|
* 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 */
|
|
int ioReport = 0;
|
|
|
|
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);
|
|
xmlFuzzResetFailure();
|
|
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);
|
|
xmlFuzzResetFailure();
|
|
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);
|
|
xmlFuzzResetFailure();
|
|
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);
|
|
xmlFuzzResetFailure();
|
|
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);
|
|
xmlFuzzResetFailure();
|
|
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);
|
|
xmlFuzzResetFailure();
|
|
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);
|
|
xmlFuzzResetFailure();
|
|
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 oldDoc, oldNodeDoc;
|
|
|
|
startOp("xmlReplaceNode");
|
|
old = getNode(0);
|
|
node = getNode(1);
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
old = old ? old->parent : NULL;
|
|
oldDoc = old ? old->doc : NULL;
|
|
if (old != NULL &&
|
|
(isDtdChild(old) ||
|
|
(old->type == XML_DTD_NODE &&
|
|
oldDoc != NULL &&
|
|
((xmlDtdPtr) old == oldDoc->intSubset ||
|
|
(xmlDtdPtr) old == oldDoc->extSubset))))
|
|
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);
|
|
|
|
xmlFuzzResetFailure();
|
|
/* 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 && value[0] != 0 && 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 && value[0] != 0 && list == NULL);
|
|
xmlFreeNodeList(list);
|
|
endOp();
|
|
break;
|
|
}
|
|
|
|
case OP_XML_NODE_LIST_GET_STRING: {
|
|
xmlDocPtr doc;
|
|
xmlNodePtr list;
|
|
xmlChar *string;
|
|
|
|
startOp("xmlNodeListGetString");
|
|
incStrIdx();
|
|
doc = getDoc(0);
|
|
list = getNode(1);
|
|
string = xmlNodeListGetString(
|
|
doc,
|
|
list,
|
|
getInt(0));
|
|
oomReport = (list != NULL && string == NULL);
|
|
moveStr(0, string);
|
|
endOp();
|
|
break;
|
|
}
|
|
|
|
case OP_XML_NODE_LIST_GET_RAW_STRING: {
|
|
xmlDocPtr doc;
|
|
xmlNodePtr list;
|
|
xmlChar *string;
|
|
|
|
startOp("xmlNodeListGetRawString");
|
|
incStrIdx();
|
|
doc = getDoc(0);
|
|
list = getNode(1);
|
|
string = xmlNodeListGetRawString(
|
|
doc,
|
|
list,
|
|
getInt(0));
|
|
oomReport = (list != NULL && 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();
|
|
xmlFuzzResetFailure();
|
|
|
|
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();
|
|
xmlFuzzResetFailure();
|
|
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 = xmlOutputBufferCreateIO(xmlFuzzOutputWrite,
|
|
xmlFuzzOutputClose, NULL, NULL);
|
|
xmlFuzzResetFailure();
|
|
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;
|
|
else
|
|
ioReport = -1;
|
|
moveStr(0, NULL);
|
|
} else {
|
|
if (argsOk && !output->error)
|
|
copyStr(0, xmlBufContent(output->buffer));
|
|
else
|
|
moveStr(0, NULL);
|
|
res = xmlOutputBufferClose(output);
|
|
oomReport = (res == -XML_ERR_NO_MEMORY);
|
|
ioReport = (res == -XML_IO_EIO);
|
|
}
|
|
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;
|
|
}
|
|
|
|
xmlFuzzCheckFailureReport(vars->opName, oomReport, ioReport);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
xmlFuzzInjectFailure(0);
|
|
xmlFuzzDataCleanup();
|
|
xmlResetLastError();
|
|
return(0);
|
|
}
|
|
|
|
size_t
|
|
LLVMFuzzerCustomMutator(char *data, size_t size, size_t maxSize,
|
|
unsigned seed) {
|
|
static const xmlFuzzChunkDesc chunks[] = {
|
|
{ 4, XML_FUZZ_PROB_ONE / 10 }, /* failurePos */
|
|
{ 0, 0 }
|
|
};
|
|
|
|
return xmlFuzzMutateChunks(chunks, data, size, maxSize, seed,
|
|
LLVMFuzzerMutate);
|
|
}
|
|
|