generated from sethub/template
[libxml2]Add libxml2 library
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
api
|
||||
corpus/
|
||||
genSeed
|
||||
html
|
||||
lint
|
||||
reader
|
||||
regexp
|
||||
schema
|
||||
seed/
|
||||
testFuzzer
|
||||
uri
|
||||
valid
|
||||
xinclude
|
||||
xml
|
||||
xpath
|
||||
@@ -0,0 +1,239 @@
|
||||
AUTOMAKE_OPTIONS = -Wno-syntax
|
||||
EXTRA_PROGRAMS = genSeed \
|
||||
api html lint reader regexp schema uri valid xinclude \
|
||||
xml xpath
|
||||
check_PROGRAMS = testFuzzer
|
||||
EXTRA_DIST = html.dict regexp.dict schema.dict xml.dict xpath.dict \
|
||||
static_seed/uri static_seed/regexp fuzz.h
|
||||
CLEANFILES = $(EXTRA_PROGRAMS)
|
||||
AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include
|
||||
DEPENDENCIES = $(top_builddir)/libxml2.la
|
||||
LDADD = $(top_builddir)/libxml2.la
|
||||
|
||||
XML_MAX_LEN = 80000
|
||||
# Single quotes to avoid wildcard expansion by the shell
|
||||
XML_SEED_CORPUS_SRC = \
|
||||
'$(top_srcdir)/test/*' \
|
||||
'$(top_srcdir)/test/errors/*.xml' \
|
||||
'$(top_srcdir)/test/errors10/*.xml' \
|
||||
'$(top_srcdir)/test/namespaces/*' \
|
||||
'$(top_srcdir)/test/recurse/*.xml' \
|
||||
'$(top_srcdir)/test/SVG/*.xml' \
|
||||
'$(top_srcdir)/test/valid/*.xml' \
|
||||
'$(top_srcdir)/test/VC/*' \
|
||||
'$(top_srcdir)/test/VCM/*' \
|
||||
'$(top_srcdir)/test/XInclude/docs/*' \
|
||||
'$(top_srcdir)/test/XInclude/without-reader/*' \
|
||||
'$(top_srcdir)/test/xmlid/*'
|
||||
|
||||
testFuzzer_SOURCES = testFuzzer.c fuzz.c
|
||||
|
||||
.PHONY: corpus clean-corpus
|
||||
|
||||
corpus: seed/html.stamp seed/lint.stamp seed/reader.stamp seed/regexp.stamp \
|
||||
seed/schema.stamp seed/uri.stamp seed/valid.stamp seed/xinclude.stamp \
|
||||
seed/xml.stamp seed/xpath.stamp
|
||||
|
||||
check-local: corpus
|
||||
./testFuzzer$(EXEEXT)
|
||||
|
||||
clean-corpus:
|
||||
rm -rf seed
|
||||
|
||||
clean-local: clean-corpus
|
||||
|
||||
# Seed corpus
|
||||
|
||||
genSeed_SOURCES = genSeed.c fuzz.c
|
||||
|
||||
# XML fuzzer
|
||||
|
||||
seed/xml.stamp: genSeed$(EXEEXT)
|
||||
@mkdir -p seed/xml
|
||||
./genSeed$(EXEEXT) xml $(XML_SEED_CORPUS_SRC)
|
||||
@touch seed/xml.stamp
|
||||
|
||||
xml_SOURCES = xml.c fuzz.c
|
||||
xml_LDFLAGS = $(AM_LDFLAGS) -fsanitize=fuzzer
|
||||
|
||||
fuzz-xml: xml$(EXEEXT) seed/xml.stamp
|
||||
@mkdir -p corpus/xml
|
||||
./xml$(EXEEXT) \
|
||||
-dict=$(srcdir)/xml.dict \
|
||||
-max_len=$(XML_MAX_LEN) \
|
||||
$$XML_FUZZ_OPTIONS \
|
||||
corpus/xml seed/xml
|
||||
|
||||
# DTD validation fuzzer
|
||||
|
||||
seed/valid.stamp: genSeed$(EXEEXT)
|
||||
@mkdir -p seed/valid
|
||||
./genSeed$(EXEEXT) valid $(XML_SEED_CORPUS_SRC)
|
||||
@touch seed/valid.stamp
|
||||
|
||||
valid_SOURCES = valid.c fuzz.c
|
||||
valid_LDFLAGS = $(AM_LDFLAGS) -fsanitize=fuzzer
|
||||
|
||||
fuzz-valid: valid$(EXEEXT) seed/valid.stamp
|
||||
@mkdir -p corpus/valid
|
||||
./valid$(EXEEXT) \
|
||||
-dict=$(srcdir)/xml.dict \
|
||||
-max_len=$(XML_MAX_LEN) \
|
||||
$$XML_FUZZ_OPTIONS \
|
||||
corpus/valid seed/valid
|
||||
|
||||
# XInclude fuzzer
|
||||
|
||||
seed/xinclude.stamp: genSeed$(EXEEXT)
|
||||
@mkdir -p seed/xinclude
|
||||
./genSeed$(EXEEXT) xinclude $(XML_SEED_CORPUS_SRC)
|
||||
@touch seed/xinclude.stamp
|
||||
|
||||
xinclude_SOURCES = xinclude.c fuzz.c
|
||||
xinclude_LDFLAGS = $(AM_LDFLAGS) -fsanitize=fuzzer
|
||||
|
||||
fuzz-xinclude: xinclude$(EXEEXT) seed/xinclude.stamp
|
||||
@mkdir -p corpus/xinclude
|
||||
./xinclude$(EXEEXT) \
|
||||
-dict=$(srcdir)/xml.dict \
|
||||
-max_len=$(XML_MAX_LEN) \
|
||||
$$XML_FUZZ_OPTIONS \
|
||||
corpus/xinclude seed/xinclude
|
||||
|
||||
# HTML fuzzer
|
||||
|
||||
seed/html.stamp: genSeed$(EXEEXT)
|
||||
@mkdir -p seed/html
|
||||
./genSeed$(EXEEXT) html '$(top_srcdir)/test/HTML/*'
|
||||
@touch seed/html.stamp
|
||||
|
||||
html_SOURCES = html.c fuzz.c
|
||||
html_LDFLAGS = $(AM_LDFLAGS) -fsanitize=fuzzer
|
||||
|
||||
fuzz-html: html$(EXEEXT) seed/html.stamp
|
||||
@mkdir -p corpus/html
|
||||
./html$(EXEEXT) \
|
||||
-dict=$(srcdir)/html.dict \
|
||||
-max_len=1000000 \
|
||||
$$XML_FUZZ_OPTIONS \
|
||||
corpus/html seed/html
|
||||
|
||||
# Reader fuzzer
|
||||
|
||||
seed/reader.stamp: genSeed$(EXEEXT)
|
||||
@mkdir -p seed/reader
|
||||
./genSeed$(EXEEXT) reader $(XML_SEED_CORPUS_SRC)
|
||||
@touch seed/reader.stamp
|
||||
|
||||
reader_SOURCES = reader.c fuzz.c
|
||||
reader_LDFLAGS = $(AM_LDFLAGS) -fsanitize=fuzzer
|
||||
|
||||
fuzz-reader: reader$(EXEEXT) seed/reader.stamp
|
||||
@mkdir -p corpus/reader
|
||||
./reader$(EXEEXT) \
|
||||
-dict=$(srcdir)/xml.dict \
|
||||
-max_len=$(XML_MAX_LEN) \
|
||||
$$XML_FUZZ_OPTIONS \
|
||||
corpus/reader seed/reader
|
||||
|
||||
# xmllint fuzzer
|
||||
|
||||
seed/lint.stamp: genSeed$(EXEEXT)
|
||||
@mkdir -p seed/lint
|
||||
./genSeed$(EXEEXT) lint $(XML_SEED_CORPUS_SRC)
|
||||
@touch seed/lint.stamp
|
||||
|
||||
lint_SOURCES = lint.c fuzz.c ../xmllint.c ../shell.c
|
||||
lint_LDFLAGS = -fsanitize=fuzzer
|
||||
|
||||
fuzz-lint: lint$(EXEEXT) seed/lint.stamp
|
||||
@mkdir -p corpus/lint
|
||||
./lint$(EXEEXT) \
|
||||
-dict=$(srcdir)/xml.dict \
|
||||
-max_len=$(XML_MAX_LEN) \
|
||||
$$XML_FUZZ_OPTIONS \
|
||||
corpus/lint seed/lint
|
||||
|
||||
# API fuzzer
|
||||
|
||||
api_SOURCES = api.c fuzz.c
|
||||
api_LDFLAGS = -fsanitize=fuzzer
|
||||
|
||||
fuzz-api: api$(EXEEXT)
|
||||
@mkdir -p corpus/api
|
||||
./api$(EXEEXT) \
|
||||
-max_len=100 \
|
||||
$$XML_FUZZ_OPTIONS \
|
||||
corpus/api
|
||||
|
||||
# Regexp fuzzer
|
||||
|
||||
seed/regexp.stamp:
|
||||
@mkdir -p seed/regexp
|
||||
cp -r $(srcdir)/static_seed/regexp seed
|
||||
@touch seed/regexp.stamp
|
||||
|
||||
regexp_SOURCES = regexp.c fuzz.c
|
||||
regexp_LDFLAGS = $(AM_LDFLAGS) -fsanitize=fuzzer
|
||||
|
||||
fuzz-regexp: regexp$(EXEEXT) seed/regexp.stamp
|
||||
@mkdir -p corpus/regexp
|
||||
./regexp$(EXEEXT) \
|
||||
-dict=$(srcdir)/regexp.dict \
|
||||
-max_len=200 \
|
||||
$$XML_FUZZ_OPTIONS \
|
||||
corpus/regexp seed/regexp
|
||||
|
||||
# URI fuzzer
|
||||
|
||||
seed/uri.stamp:
|
||||
@mkdir -p seed/uri
|
||||
cp -r $(srcdir)/static_seed/uri seed
|
||||
@touch seed/uri.stamp
|
||||
|
||||
uri_SOURCES = uri.c fuzz.c
|
||||
uri_LDFLAGS = $(AM_LDFLAGS) -fsanitize=fuzzer
|
||||
|
||||
fuzz-uri: uri$(EXEEXT) seed/uri.stamp
|
||||
@mkdir -p corpus/uri
|
||||
./uri$(EXEEXT) \
|
||||
-max_len=10000 \
|
||||
$$XML_FUZZ_OPTIONS \
|
||||
corpus/uri seed/uri
|
||||
|
||||
# XML Schema fuzzer
|
||||
|
||||
seed/schema.stamp: genSeed$(EXEEXT)
|
||||
@mkdir -p seed/schema
|
||||
./genSeed$(EXEEXT) schema '$(top_srcdir)/test/schemas/*.xsd'
|
||||
@touch seed/schema.stamp
|
||||
|
||||
schema_SOURCES = schema.c fuzz.c
|
||||
schema_LDFLAGS = $(AM_LDFLAGS) -fsanitize=fuzzer
|
||||
|
||||
fuzz-schema: schema$(EXEEXT) seed/schema.stamp
|
||||
@mkdir -p corpus/schema
|
||||
./schema$(EXEEXT) \
|
||||
-dict=$(srcdir)/schema.dict \
|
||||
-max_len=$(XML_MAX_LEN) \
|
||||
$$XML_FUZZ_OPTIONS \
|
||||
corpus/schema seed/schema
|
||||
|
||||
# XPath fuzzer
|
||||
|
||||
seed/xpath.stamp: genSeed$(EXEEXT)
|
||||
@mkdir -p seed/xpath
|
||||
./genSeed$(EXEEXT) xpath '$(top_srcdir)/test/XPath'
|
||||
@touch seed/xpath.stamp
|
||||
|
||||
xpath_SOURCES = xpath.c fuzz.c
|
||||
xpath_LDFLAGS = $(AM_LDFLAGS) -fsanitize=fuzzer
|
||||
|
||||
fuzz-xpath: xpath$(EXEEXT) seed/xpath.stamp
|
||||
@mkdir -p corpus/xpath
|
||||
./xpath$(EXEEXT) \
|
||||
-dict=$(srcdir)/xpath.dict \
|
||||
-max_len=10000 \
|
||||
$$XML_FUZZ_OPTIONS \
|
||||
corpus/xpath seed/xpath
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
libFuzzer instructions for libxml2
|
||||
==================================
|
||||
|
||||
Set compiler and options. Make sure to enable at least basic optimizations
|
||||
to avoid excessive stack usage. Also enable some debug output to get
|
||||
meaningful stack traces.
|
||||
|
||||
export CC=clang
|
||||
export CFLAGS=" \
|
||||
-O1 -gline-tables-only \
|
||||
-fsanitize=fuzzer-no-link,address,undefined \
|
||||
-fno-sanitize-recover=all \
|
||||
-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION"
|
||||
|
||||
Since llvm-symbolizer can use libxml2 itself, you may need the following
|
||||
wrapper to make sure that it doesn't use the instrumented version of
|
||||
libxml2:
|
||||
|
||||
export ASAN_SYMBOLIZER_PATH="$(pwd)/.gitlab-ci/llvm-symbolizer"
|
||||
|
||||
Other options that can improve stack traces:
|
||||
|
||||
-fno-omit-frame-pointer
|
||||
-fno-inline
|
||||
-fno-optimize-sibling-calls (disables tail call optimization)
|
||||
|
||||
Build libxml2 with instrumentation:
|
||||
|
||||
./configure --without-python
|
||||
make
|
||||
|
||||
Run fuzzers:
|
||||
|
||||
make -C fuzz fuzz-xml
|
||||
|
||||
The environment variable XML_FUZZ_OPTIONS can be used to pass additional
|
||||
flags to the fuzzer.
|
||||
|
||||
Malloc failure injection
|
||||
------------------------
|
||||
|
||||
Most fuzzers inject malloc failures to cover code paths handling these
|
||||
errors. This can lead to surprises when debugging crashes. You can set
|
||||
the macro XML_FUZZ_MALLOC_ABORT in fuzz/fuzz.c to make the fuzz target
|
||||
abort at the malloc invocation which would fail. This tells you if
|
||||
and where a malloc failure was injected.
|
||||
|
||||
Some fuzzers also test whether malloc failures are reported. To debug
|
||||
failures which aren't reported, it's helpful to enable
|
||||
XML_FUZZ_MALLOC_ABORT to see which allocation failed. Debugging
|
||||
failures which are erroneously reported can be harder. If the report
|
||||
goes through xmlRaiseMemoryError, you can abort() there to get a
|
||||
stack trace.
|
||||
|
||||
Bugs related to handling of malloc failures are not considered
|
||||
security-critical by the libxml2 maintainers. Nevertheless, we'd like
|
||||
to see such issues reported.
|
||||
+3614
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,603 @@
|
||||
/*
|
||||
* fuzz.c: Common functions for fuzzing.
|
||||
*
|
||||
* See Copyright for the status of this software.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <libxml/hash.h>
|
||||
#include <libxml/parser.h>
|
||||
#include <libxml/parserInternals.h>
|
||||
#include <libxml/tree.h>
|
||||
#include <libxml/xmlIO.h>
|
||||
#include "fuzz.h"
|
||||
|
||||
typedef struct {
|
||||
const char *data;
|
||||
size_t size;
|
||||
} xmlFuzzEntityInfo;
|
||||
|
||||
/* Single static instance for now */
|
||||
static struct {
|
||||
/* Original data */
|
||||
const char *data;
|
||||
size_t size;
|
||||
|
||||
/* Remaining data */
|
||||
const char *ptr;
|
||||
size_t remaining;
|
||||
|
||||
/* Buffer for unescaped strings */
|
||||
char *outBuf;
|
||||
char *outPtr; /* Free space at end of buffer */
|
||||
|
||||
xmlHashTablePtr entities; /* Maps URLs to xmlFuzzEntityInfos */
|
||||
|
||||
/* The first entity is the main entity. */
|
||||
const char *mainUrl;
|
||||
xmlFuzzEntityInfo *mainEntity;
|
||||
const char *secondaryUrl;
|
||||
xmlFuzzEntityInfo *secondaryEntity;
|
||||
} fuzzData;
|
||||
|
||||
size_t fuzzNumAttempts;
|
||||
size_t fuzzFailurePos;
|
||||
int fuzzAllocFailed;
|
||||
int fuzzIoFailed;
|
||||
|
||||
/**
|
||||
* xmlFuzzErrorFunc:
|
||||
*
|
||||
* An error function that simply discards all errors.
|
||||
*/
|
||||
void
|
||||
xmlFuzzErrorFunc(void *ctx ATTRIBUTE_UNUSED, const char *msg ATTRIBUTE_UNUSED,
|
||||
...) {
|
||||
}
|
||||
|
||||
/**
|
||||
* xmlFuzzSErrorFunc:
|
||||
*
|
||||
* A structured error function that simply discards all errors.
|
||||
*/
|
||||
void
|
||||
xmlFuzzSErrorFunc(void *ctx ATTRIBUTE_UNUSED,
|
||||
const xmlError *error ATTRIBUTE_UNUSED) {
|
||||
}
|
||||
|
||||
/*
|
||||
* Failure injection.
|
||||
*
|
||||
* To debug issues involving injected failures, it's often helpful to set
|
||||
* FAILURE_ABORT to 1. This should provide a backtrace of the failed
|
||||
* operation.
|
||||
*/
|
||||
|
||||
#define XML_FUZZ_FAILURE_ABORT 0
|
||||
|
||||
void
|
||||
xmlFuzzInjectFailure(size_t failurePos) {
|
||||
fuzzNumAttempts = 0;
|
||||
fuzzFailurePos = failurePos;
|
||||
fuzzAllocFailed = 0;
|
||||
fuzzIoFailed = 0;
|
||||
}
|
||||
|
||||
static int
|
||||
xmlFuzzTryMalloc(void) {
|
||||
if (fuzzFailurePos > 0) {
|
||||
fuzzNumAttempts += 1;
|
||||
if (fuzzNumAttempts == fuzzFailurePos) {
|
||||
#if XML_FUZZ_FAILURE_ABORT
|
||||
abort();
|
||||
#endif
|
||||
fuzzAllocFailed = 1;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
xmlFuzzTryIo(void) {
|
||||
if (fuzzFailurePos > 0) {
|
||||
fuzzNumAttempts += 1;
|
||||
if (fuzzNumAttempts == fuzzFailurePos) {
|
||||
#if XML_FUZZ_FAILURE_ABORT
|
||||
abort();
|
||||
#endif
|
||||
fuzzIoFailed = 1;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *
|
||||
xmlFuzzMalloc(size_t size) {
|
||||
void *ret;
|
||||
|
||||
if (xmlFuzzTryMalloc() < 0)
|
||||
return NULL;
|
||||
|
||||
ret = malloc(size);
|
||||
if (ret == NULL)
|
||||
fuzzAllocFailed = 1;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void *
|
||||
xmlFuzzRealloc(void *ptr, size_t size) {
|
||||
void *ret;
|
||||
|
||||
if (xmlFuzzTryMalloc() < 0)
|
||||
return NULL;
|
||||
|
||||
ret = realloc(ptr, size);
|
||||
if (ret == NULL)
|
||||
fuzzAllocFailed = 1;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
xmlFuzzMemSetup(void) {
|
||||
xmlMemSetup(free, xmlFuzzMalloc, xmlFuzzRealloc, xmlMemStrdup);
|
||||
}
|
||||
|
||||
int
|
||||
xmlFuzzMallocFailed(void) {
|
||||
return fuzzAllocFailed;
|
||||
}
|
||||
|
||||
void
|
||||
xmlFuzzResetFailure(void) {
|
||||
fuzzAllocFailed = 0;
|
||||
fuzzIoFailed = 0;
|
||||
}
|
||||
|
||||
void
|
||||
xmlFuzzCheckFailureReport(const char *func, int oomReport, int ioReport) {
|
||||
if (oomReport >= 0 && fuzzAllocFailed != oomReport) {
|
||||
fprintf(stderr, "%s: malloc failure %s reported\n",
|
||||
func, fuzzAllocFailed ? "not" : "erroneously");
|
||||
abort();
|
||||
}
|
||||
if (ioReport >= 0 && fuzzIoFailed != ioReport) {
|
||||
fprintf(stderr, "%s: IO failure %s reported\n",
|
||||
func, fuzzIoFailed ? "not" : "erroneously");
|
||||
abort();
|
||||
}
|
||||
fuzzAllocFailed = 0;
|
||||
fuzzIoFailed = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* xmlFuzzDataInit:
|
||||
*
|
||||
* Initialize fuzz data provider.
|
||||
*/
|
||||
void
|
||||
xmlFuzzDataInit(const char *data, size_t size) {
|
||||
fuzzData.data = data;
|
||||
fuzzData.size = size;
|
||||
fuzzData.ptr = data;
|
||||
fuzzData.remaining = size;
|
||||
|
||||
fuzzData.outBuf = xmlMalloc(size + 1);
|
||||
fuzzData.outPtr = fuzzData.outBuf;
|
||||
|
||||
fuzzData.entities = xmlHashCreate(8);
|
||||
fuzzData.mainUrl = NULL;
|
||||
fuzzData.mainEntity = NULL;
|
||||
fuzzData.secondaryUrl = NULL;
|
||||
fuzzData.secondaryEntity = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* xmlFuzzDataFree:
|
||||
*
|
||||
* Cleanup fuzz data provider.
|
||||
*/
|
||||
void
|
||||
xmlFuzzDataCleanup(void) {
|
||||
xmlFree(fuzzData.outBuf);
|
||||
xmlHashFree(fuzzData.entities, xmlHashDefaultDeallocator);
|
||||
}
|
||||
|
||||
/**
|
||||
* xmlFuzzWriteInt:
|
||||
* @out: output file
|
||||
* @v: integer to write
|
||||
* @size: size of integer in bytes
|
||||
*
|
||||
* Write an integer to the fuzz data.
|
||||
*/
|
||||
void
|
||||
xmlFuzzWriteInt(FILE *out, size_t v, int size) {
|
||||
int shift;
|
||||
|
||||
while (size > (int) sizeof(size_t)) {
|
||||
putc(0, out);
|
||||
size--;
|
||||
}
|
||||
|
||||
shift = size * 8;
|
||||
while (shift > 0) {
|
||||
shift -= 8;
|
||||
putc((v >> shift) & 255, out);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* xmlFuzzReadInt:
|
||||
* @size: size of integer in bytes
|
||||
*
|
||||
* Read an integer from the fuzz data.
|
||||
*/
|
||||
size_t
|
||||
xmlFuzzReadInt(int size) {
|
||||
size_t ret = 0;
|
||||
|
||||
while ((size > 0) && (fuzzData.remaining > 0)) {
|
||||
unsigned char c = (unsigned char) *fuzzData.ptr++;
|
||||
fuzzData.remaining--;
|
||||
ret = (ret << 8) | c;
|
||||
size--;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* xmlFuzzBytesRemaining:
|
||||
*
|
||||
* Return number of remaining bytes in fuzz data.
|
||||
*/
|
||||
size_t
|
||||
xmlFuzzBytesRemaining(void) {
|
||||
return(fuzzData.remaining);
|
||||
}
|
||||
|
||||
/**
|
||||
* xmlFuzzReadRemaining:
|
||||
* @size: size of string in bytes
|
||||
*
|
||||
* Read remaining bytes from fuzz data.
|
||||
*/
|
||||
const char *
|
||||
xmlFuzzReadRemaining(size_t *size) {
|
||||
const char *ret = fuzzData.ptr;
|
||||
|
||||
*size = fuzzData.remaining;
|
||||
fuzzData.ptr += fuzzData.remaining;
|
||||
fuzzData.remaining = 0;
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* xmlFuzzWriteString:
|
||||
* @out: output file
|
||||
* @str: string to write
|
||||
*
|
||||
* Write a random-length string to file in a format similar to
|
||||
* FuzzedDataProvider. Backslash followed by newline marks the end of the
|
||||
* string. Two backslashes are used to escape a backslash.
|
||||
*/
|
||||
void
|
||||
xmlFuzzWriteString(FILE *out, const char *str) {
|
||||
for (; *str; str++) {
|
||||
int c = (unsigned char) *str;
|
||||
putc(c, out);
|
||||
if (c == '\\')
|
||||
putc(c, out);
|
||||
}
|
||||
putc('\\', out);
|
||||
putc('\n', out);
|
||||
}
|
||||
|
||||
/**
|
||||
* xmlFuzzReadString:
|
||||
* @size: size of string in bytes
|
||||
*
|
||||
* Read a random-length string from the fuzz data.
|
||||
*
|
||||
* The format is similar to libFuzzer's FuzzedDataProvider but treats
|
||||
* backslash followed by newline as end of string. This makes the fuzz data
|
||||
* more readable. A backslash character is escaped with another backslash.
|
||||
*
|
||||
* Returns a zero-terminated string or NULL if the fuzz data is exhausted.
|
||||
*/
|
||||
const char *
|
||||
xmlFuzzReadString(size_t *size) {
|
||||
const char *out = fuzzData.outPtr;
|
||||
|
||||
while (fuzzData.remaining > 0) {
|
||||
int c = *fuzzData.ptr++;
|
||||
fuzzData.remaining--;
|
||||
|
||||
if ((c == '\\') && (fuzzData.remaining > 0)) {
|
||||
int c2 = *fuzzData.ptr;
|
||||
|
||||
if (c2 == '\n') {
|
||||
fuzzData.ptr++;
|
||||
fuzzData.remaining--;
|
||||
if (size != NULL)
|
||||
*size = fuzzData.outPtr - out;
|
||||
*fuzzData.outPtr++ = '\0';
|
||||
return(out);
|
||||
}
|
||||
if (c2 == '\\') {
|
||||
fuzzData.ptr++;
|
||||
fuzzData.remaining--;
|
||||
}
|
||||
}
|
||||
|
||||
*fuzzData.outPtr++ = c;
|
||||
}
|
||||
|
||||
if (fuzzData.outPtr > out) {
|
||||
if (size != NULL)
|
||||
*size = fuzzData.outPtr - out;
|
||||
*fuzzData.outPtr++ = '\0';
|
||||
return(out);
|
||||
}
|
||||
|
||||
if (size != NULL)
|
||||
*size = 0;
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* xmlFuzzReadEntities:
|
||||
*
|
||||
* Read entities like the main XML file, external DTDs, external parsed
|
||||
* entities from fuzz data.
|
||||
*/
|
||||
void
|
||||
xmlFuzzReadEntities(void) {
|
||||
size_t num = 0;
|
||||
|
||||
while (1) {
|
||||
const char *url, *entity;
|
||||
size_t urlSize, entitySize;
|
||||
xmlFuzzEntityInfo *entityInfo;
|
||||
|
||||
url = xmlFuzzReadString(&urlSize);
|
||||
if (url == NULL) break;
|
||||
|
||||
entity = xmlFuzzReadString(&entitySize);
|
||||
if (entity == NULL) break;
|
||||
|
||||
/*
|
||||
* Cap URL size to avoid quadratic behavior when generating
|
||||
* error messages or looking up entities.
|
||||
*/
|
||||
if (urlSize < 50 &&
|
||||
xmlHashLookup(fuzzData.entities, (xmlChar *)url) == NULL) {
|
||||
entityInfo = xmlMalloc(sizeof(xmlFuzzEntityInfo));
|
||||
if (entityInfo == NULL)
|
||||
break;
|
||||
entityInfo->data = entity;
|
||||
entityInfo->size = entitySize;
|
||||
|
||||
xmlHashAddEntry(fuzzData.entities, (xmlChar *)url, entityInfo);
|
||||
|
||||
if (num == 0) {
|
||||
fuzzData.mainUrl = url;
|
||||
fuzzData.mainEntity = entityInfo;
|
||||
} else if (num == 1) {
|
||||
fuzzData.secondaryUrl = url;
|
||||
fuzzData.secondaryEntity = entityInfo;
|
||||
}
|
||||
|
||||
num++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* xmlFuzzMainUrl:
|
||||
*
|
||||
* Returns the main URL.
|
||||
*/
|
||||
const char *
|
||||
xmlFuzzMainUrl(void) {
|
||||
return(fuzzData.mainUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* xmlFuzzMainEntity:
|
||||
* @size: size of the main entity in bytes
|
||||
*
|
||||
* Returns the main entity.
|
||||
*/
|
||||
const char *
|
||||
xmlFuzzMainEntity(size_t *size) {
|
||||
if (fuzzData.mainEntity == NULL)
|
||||
return(NULL);
|
||||
*size = fuzzData.mainEntity->size;
|
||||
return(fuzzData.mainEntity->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* xmlFuzzSecondaryUrl:
|
||||
*
|
||||
* Returns the secondary URL.
|
||||
*/
|
||||
const char *
|
||||
xmlFuzzSecondaryUrl(void) {
|
||||
return(fuzzData.secondaryUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* xmlFuzzSecondaryEntity:
|
||||
* @size: size of the secondary entity in bytes
|
||||
*
|
||||
* Returns the secondary entity.
|
||||
*/
|
||||
const char *
|
||||
xmlFuzzSecondaryEntity(size_t *size) {
|
||||
if (fuzzData.secondaryEntity == NULL)
|
||||
return(NULL);
|
||||
*size = fuzzData.secondaryEntity->size;
|
||||
return(fuzzData.secondaryEntity->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* xmlFuzzResourceLoader:
|
||||
*
|
||||
* The resource loader for fuzz data.
|
||||
*/
|
||||
xmlParserErrors
|
||||
xmlFuzzResourceLoader(void *data ATTRIBUTE_UNUSED, const char *URL,
|
||||
const char *ID ATTRIBUTE_UNUSED,
|
||||
xmlResourceType type ATTRIBUTE_UNUSED,
|
||||
xmlParserInputFlags flags ATTRIBUTE_UNUSED,
|
||||
xmlParserInputPtr *out) {
|
||||
xmlParserInputPtr input;
|
||||
xmlFuzzEntityInfo *entity;
|
||||
|
||||
entity = xmlHashLookup(fuzzData.entities, (xmlChar *) URL);
|
||||
if (entity == NULL)
|
||||
return(XML_IO_ENOENT);
|
||||
|
||||
/* IO failure injection */
|
||||
if (xmlFuzzTryIo() < 0)
|
||||
return(XML_IO_EIO);
|
||||
|
||||
input = xmlNewInputFromMemory(URL, entity->data, entity->size,
|
||||
XML_INPUT_BUF_STATIC |
|
||||
XML_INPUT_BUF_ZERO_TERMINATED);
|
||||
if (input == NULL)
|
||||
return(XML_ERR_NO_MEMORY);
|
||||
|
||||
*out = input;
|
||||
return(XML_ERR_OK);
|
||||
}
|
||||
|
||||
char *
|
||||
xmlSlurpFile(const char *path, size_t *sizeRet) {
|
||||
FILE *file;
|
||||
struct stat statbuf;
|
||||
char *data;
|
||||
size_t size;
|
||||
|
||||
if ((stat(path, &statbuf) != 0) || (!S_ISREG(statbuf.st_mode)))
|
||||
return(NULL);
|
||||
size = statbuf.st_size;
|
||||
file = fopen(path, "rb");
|
||||
if (file == NULL)
|
||||
return(NULL);
|
||||
data = xmlMalloc(size + 1);
|
||||
if (data != NULL) {
|
||||
if (fread(data, 1, size, file) != size) {
|
||||
xmlFree(data);
|
||||
data = NULL;
|
||||
} else {
|
||||
data[size] = 0;
|
||||
if (sizeRet != NULL)
|
||||
*sizeRet = size;
|
||||
}
|
||||
}
|
||||
fclose(file);
|
||||
|
||||
return(data);
|
||||
}
|
||||
|
||||
int
|
||||
xmlFuzzOutputWrite(void *ctxt ATTRIBUTE_UNUSED,
|
||||
const char *buffer ATTRIBUTE_UNUSED, int len) {
|
||||
if (xmlFuzzTryIo() < 0)
|
||||
return -XML_IO_EIO;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
int
|
||||
xmlFuzzOutputClose(void *ctxt ATTRIBUTE_UNUSED) {
|
||||
if (xmlFuzzTryIo() < 0)
|
||||
return XML_IO_EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* xmlFuzzMutateChunks:
|
||||
* @chunks: array of chunk descriptions
|
||||
* @data: fuzz data (from LLVMFuzzerCustomMutator)
|
||||
* @size: data size (from LLVMFuzzerCustomMutator)
|
||||
* @maxSize: max data size (from LLVMFuzzerCustomMutator)
|
||||
* @seed: seed (from LLVMFuzzerCustomMutator)
|
||||
* @mutator: mutator function, use LLVMFuzzerMutate
|
||||
*
|
||||
* Mutates one of several chunks with a given probability.
|
||||
*
|
||||
* Probability is a value between 0 and XML_FUZZ_PROB_ONE.
|
||||
*
|
||||
* The last chunk has flexible size and must have size and
|
||||
* mutateProb set to 0.
|
||||
*
|
||||
* Returns the size of the mutated data like LLVMFuzzerCustomMutator.
|
||||
*/
|
||||
size_t
|
||||
xmlFuzzMutateChunks(const xmlFuzzChunkDesc *chunks,
|
||||
char *data, size_t size, size_t maxSize, unsigned seed,
|
||||
xmlFuzzMutator mutator) {
|
||||
size_t off = 0;
|
||||
size_t ret, chunkSize, maxChunkSize, mutSize;
|
||||
unsigned prob = seed % XML_FUZZ_PROB_ONE;
|
||||
unsigned descSize = 0;
|
||||
int i = 0;
|
||||
|
||||
while (1) {
|
||||
unsigned descProb;
|
||||
|
||||
descSize = chunks[i].size;
|
||||
descProb = chunks[i].mutateProb;
|
||||
|
||||
if (descSize == 0 ||
|
||||
off + descSize > size ||
|
||||
off + descSize >= maxSize ||
|
||||
prob < descProb)
|
||||
break;
|
||||
|
||||
off += descSize;
|
||||
prob -= descProb;
|
||||
i += 1;
|
||||
}
|
||||
|
||||
chunkSize = size - off;
|
||||
maxChunkSize = maxSize - off;
|
||||
|
||||
if (descSize != 0) {
|
||||
if (chunkSize > descSize)
|
||||
chunkSize = descSize;
|
||||
if (maxChunkSize > descSize)
|
||||
maxChunkSize = descSize;
|
||||
}
|
||||
|
||||
mutSize = mutator(data + off, chunkSize, maxChunkSize);
|
||||
|
||||
if (size > off + chunkSize) {
|
||||
size_t j;
|
||||
|
||||
for (j = mutSize; j < chunkSize; j++)
|
||||
data[off + j] = 0;
|
||||
|
||||
ret = size;
|
||||
} else {
|
||||
ret = off + mutSize;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* fuzz.h: Common functions and macros for fuzzing.
|
||||
*
|
||||
* See Copyright for the status of this software.
|
||||
*/
|
||||
|
||||
#ifndef __XML_FUZZERCOMMON_H__
|
||||
#define __XML_FUZZERCOMMON_H__
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <libxml/parser.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if __GNUC__ * 100 + __GNUC_MINOR__ >= 207 || defined(__clang__)
|
||||
#define ATTRIBUTE_UNUSED __attribute__((unused))
|
||||
#else
|
||||
#define ATTRIBUTE_UNUSED
|
||||
#endif
|
||||
|
||||
#if defined(LIBXML_HTML_ENABLED)
|
||||
#define HAVE_HTML_FUZZER
|
||||
#endif
|
||||
#if 1
|
||||
#define HAVE_LINT_FUZZER
|
||||
#endif
|
||||
#if defined(LIBXML_READER_ENABLED)
|
||||
#define HAVE_READER_FUZZER
|
||||
#endif
|
||||
#if defined(LIBXML_REGEXP_ENABLED)
|
||||
#define HAVE_REGEXP_FUZZER
|
||||
#endif
|
||||
#if defined(LIBXML_SCHEMAS_ENABLED)
|
||||
#define HAVE_SCHEMA_FUZZER
|
||||
#endif
|
||||
#if 1
|
||||
#define HAVE_URI_FUZZER
|
||||
#endif
|
||||
#if defined(LIBXML_VALID_ENABLED)
|
||||
#define HAVE_VALID_FUZZER
|
||||
#endif
|
||||
#if defined(LIBXML_XINCLUDE_ENABLED)
|
||||
#define HAVE_XINCLUDE_FUZZER
|
||||
#endif
|
||||
#if 1
|
||||
#define HAVE_XML_FUZZER
|
||||
#endif
|
||||
#if defined(LIBXML_XPTR_ENABLED)
|
||||
#define HAVE_XPATH_FUZZER
|
||||
#endif
|
||||
|
||||
#define XML_FUZZ_PROB_ONE (1u << 16)
|
||||
|
||||
typedef size_t
|
||||
(*xmlFuzzMutator)(char *data, size_t size, size_t maxSize);
|
||||
|
||||
typedef struct {
|
||||
unsigned size;
|
||||
unsigned mutateProb;
|
||||
} xmlFuzzChunkDesc;
|
||||
|
||||
int
|
||||
LLVMFuzzerInitialize(int *argc, char ***argv);
|
||||
|
||||
int
|
||||
LLVMFuzzerTestOneInput(const char *data, size_t size);
|
||||
|
||||
size_t
|
||||
LLVMFuzzerMutate(char *data, size_t size, size_t maxSize);
|
||||
|
||||
size_t
|
||||
LLVMFuzzerCustomMutator(char *data, size_t size, size_t maxSize,
|
||||
unsigned seed);
|
||||
|
||||
void
|
||||
xmlFuzzErrorFunc(void *ctx, const char *msg, ...);
|
||||
|
||||
void
|
||||
xmlFuzzSErrorFunc(void *ctx, const xmlError *error);
|
||||
|
||||
void
|
||||
xmlFuzzMemSetup(void);
|
||||
|
||||
void
|
||||
xmlFuzzInjectFailure(size_t failurePos);
|
||||
|
||||
int
|
||||
xmlFuzzMallocFailed(void);
|
||||
|
||||
void
|
||||
xmlFuzzResetFailure(void);
|
||||
|
||||
void
|
||||
xmlFuzzCheckFailureReport(const char *func, int oomReport, int ioReport);
|
||||
|
||||
void
|
||||
xmlFuzzDataInit(const char *data, size_t size);
|
||||
|
||||
void
|
||||
xmlFuzzDataCleanup(void);
|
||||
|
||||
void
|
||||
xmlFuzzWriteInt(FILE *out, size_t v, int size);
|
||||
|
||||
size_t
|
||||
xmlFuzzReadInt(int size);
|
||||
|
||||
size_t
|
||||
xmlFuzzBytesRemaining(void);
|
||||
|
||||
const char *
|
||||
xmlFuzzReadRemaining(size_t *size);
|
||||
|
||||
void
|
||||
xmlFuzzWriteString(FILE *out, const char *str);
|
||||
|
||||
const char *
|
||||
xmlFuzzReadString(size_t *size);
|
||||
|
||||
void
|
||||
xmlFuzzReadEntities(void);
|
||||
|
||||
const char *
|
||||
xmlFuzzMainUrl(void);
|
||||
|
||||
const char *
|
||||
xmlFuzzMainEntity(size_t *size);
|
||||
|
||||
const char *
|
||||
xmlFuzzSecondaryUrl(void);
|
||||
|
||||
const char *
|
||||
xmlFuzzSecondaryEntity(size_t *size);
|
||||
|
||||
xmlParserErrors
|
||||
xmlFuzzResourceLoader(void *data, const char *URL, const char *ID,
|
||||
xmlResourceType type, xmlParserInputFlags flags,
|
||||
xmlParserInputPtr *out);
|
||||
|
||||
char *
|
||||
xmlSlurpFile(const char *path, size_t *size);
|
||||
|
||||
int
|
||||
xmlFuzzOutputWrite(void *ctxt, const char *buffer, int len);
|
||||
|
||||
int
|
||||
xmlFuzzOutputClose(void *ctxt);
|
||||
|
||||
size_t
|
||||
xmlFuzzMutateChunks(const xmlFuzzChunkDesc *chunks,
|
||||
char *data, size_t size, size_t maxSize, unsigned seed,
|
||||
xmlFuzzMutator mutator);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __XML_FUZZERCOMMON_H__ */
|
||||
|
||||
@@ -0,0 +1,658 @@
|
||||
/*
|
||||
* xmlSeed.c: Generate the XML seed corpus for fuzzing.
|
||||
*
|
||||
* See Copyright for the status of this software.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <glob.h>
|
||||
#include <libgen.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <direct.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <libxml/parser.h>
|
||||
#include <libxml/parserInternals.h>
|
||||
#include <libxml/HTMLparser.h>
|
||||
#include <libxml/xinclude.h>
|
||||
#include <libxml/xmlschemas.h>
|
||||
#include "fuzz.h"
|
||||
|
||||
#define PATH_SIZE 500
|
||||
#define SEED_BUF_SIZE 16384
|
||||
#define EXPR_SIZE 4500
|
||||
|
||||
#define FLAG_READER (1 << 0)
|
||||
#define FLAG_LINT (1 << 1)
|
||||
#define FLAG_PUSH_CHUNK_SIZE (1 << 2)
|
||||
|
||||
typedef int
|
||||
(*fileFunc)(const char *base, FILE *out);
|
||||
|
||||
typedef int
|
||||
(*mainFunc)(const char *arg);
|
||||
|
||||
static struct {
|
||||
FILE *out;
|
||||
xmlHashTablePtr entities; /* Maps URLs to xmlFuzzEntityInfos */
|
||||
xmlExternalEntityLoader oldLoader;
|
||||
fileFunc processFile;
|
||||
const char *fuzzer;
|
||||
int counter;
|
||||
char cwd[PATH_SIZE];
|
||||
int flags;
|
||||
} globalData;
|
||||
|
||||
#if defined(HAVE_SCHEMA_FUZZER) || \
|
||||
defined(HAVE_XML_FUZZER)
|
||||
/*
|
||||
* A custom resource loader that writes all external DTDs or entities to a
|
||||
* single file in the format expected by xmlFuzzResourceLoader.
|
||||
*/
|
||||
static xmlParserErrors
|
||||
fuzzResourceRecorder(void *data ATTRIBUTE_UNUSED, const char *URL,
|
||||
const char *ID ATTRIBUTE_UNUSED,
|
||||
xmlResourceType type ATTRIBUTE_UNUSED,
|
||||
xmlParserInputFlags flags,
|
||||
xmlParserInputPtr *out) {
|
||||
*out = NULL;
|
||||
|
||||
if (globalData.entities == NULL ||
|
||||
xmlHashLookup(globalData.entities, BAD_CAST URL) == NULL) {
|
||||
data = xmlSlurpFile(URL, NULL);
|
||||
|
||||
if (globalData.entities == NULL)
|
||||
globalData.entities = xmlHashCreate(4);
|
||||
|
||||
xmlHashAddEntry(globalData.entities, (const xmlChar *) URL, data);
|
||||
}
|
||||
|
||||
return(xmlNewInputFromUrl(URL, flags, out));
|
||||
}
|
||||
|
||||
static void
|
||||
fuzzRecorderInit(FILE *out) {
|
||||
globalData.out = out;
|
||||
globalData.entities = xmlHashCreate(8);
|
||||
globalData.oldLoader = xmlGetExternalEntityLoader();
|
||||
}
|
||||
|
||||
static void
|
||||
fuzzRecorderWriteAndFree(void *entry, const xmlChar *file) {
|
||||
char *data = entry;
|
||||
xmlFuzzWriteString(globalData.out, (const char *) file);
|
||||
xmlFuzzWriteString(globalData.out, data);
|
||||
xmlFree(data);
|
||||
}
|
||||
|
||||
static void
|
||||
fuzzRecorderWrite(const char *file) {
|
||||
xmlHashRemoveEntry(globalData.entities, (const xmlChar *) file,
|
||||
fuzzRecorderWriteAndFree);
|
||||
}
|
||||
|
||||
static void
|
||||
fuzzRecorderCleanup(void) {
|
||||
/* Write remaining entities (in random order). */
|
||||
xmlHashFree(globalData.entities, fuzzRecorderWriteAndFree);
|
||||
globalData.out = NULL;
|
||||
globalData.entities = NULL;
|
||||
globalData.oldLoader = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_XML_FUZZER
|
||||
static int
|
||||
processXml(const char *docFile, FILE *out) {
|
||||
int opts = XML_PARSE_NOENT | XML_PARSE_DTDLOAD;
|
||||
xmlParserCtxtPtr ctxt;
|
||||
xmlDocPtr doc;
|
||||
|
||||
if (globalData.flags & FLAG_LINT) {
|
||||
/* Switches */
|
||||
xmlFuzzWriteInt(out, 0, 4);
|
||||
xmlFuzzWriteInt(out, 0, 4);
|
||||
/* maxmem */
|
||||
xmlFuzzWriteInt(out, 0, 4);
|
||||
/* max-ampl */
|
||||
xmlFuzzWriteInt(out, 0, 1);
|
||||
/* pretty */
|
||||
xmlFuzzWriteInt(out, 0, 1);
|
||||
/* encode */
|
||||
xmlFuzzWriteString(out, "");
|
||||
/* pattern */
|
||||
xmlFuzzWriteString(out, "");
|
||||
/* xpath */
|
||||
xmlFuzzWriteString(out, "");
|
||||
} else {
|
||||
/* Parser options. */
|
||||
xmlFuzzWriteInt(out, opts, 4);
|
||||
/* Max allocations. */
|
||||
xmlFuzzWriteInt(out, 0, 4);
|
||||
|
||||
if (globalData.flags & FLAG_PUSH_CHUNK_SIZE) {
|
||||
/* Chunk size for push parser */
|
||||
xmlFuzzWriteInt(out, 256, 4);
|
||||
}
|
||||
|
||||
if (globalData.flags & FLAG_READER) {
|
||||
/* Initial reader program with a couple of OP_READs */
|
||||
xmlFuzzWriteString(out, "\x01\x01\x01\x01\x01\x01\x01\x01");
|
||||
}
|
||||
}
|
||||
|
||||
fuzzRecorderInit(out);
|
||||
|
||||
ctxt = xmlNewParserCtxt();
|
||||
xmlCtxtSetErrorHandler(ctxt, xmlFuzzSErrorFunc, NULL);
|
||||
xmlCtxtSetResourceLoader(ctxt, fuzzResourceRecorder, NULL);
|
||||
doc = xmlCtxtReadFile(ctxt, docFile, NULL, opts);
|
||||
#ifdef LIBXML_XINCLUDE_ENABLED
|
||||
{
|
||||
xmlXIncludeCtxtPtr xinc = xmlXIncludeNewContext(doc);
|
||||
|
||||
xmlXIncludeSetErrorHandler(xinc, xmlFuzzSErrorFunc, NULL);
|
||||
xmlXIncludeSetResourceLoader(xinc, fuzzResourceRecorder, NULL);
|
||||
xmlXIncludeSetFlags(xinc, opts);
|
||||
xmlXIncludeProcessNode(xinc, (xmlNodePtr) doc);
|
||||
xmlXIncludeFreeContext(xinc);
|
||||
}
|
||||
#endif
|
||||
xmlFreeDoc(doc);
|
||||
xmlFreeParserCtxt(ctxt);
|
||||
|
||||
fuzzRecorderWrite(docFile);
|
||||
fuzzRecorderCleanup();
|
||||
|
||||
return(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_HTML_FUZZER
|
||||
static int
|
||||
processHtml(const char *docFile, FILE *out) {
|
||||
char buf[SEED_BUF_SIZE];
|
||||
FILE *file;
|
||||
size_t size;
|
||||
|
||||
/* Parser options. */
|
||||
xmlFuzzWriteInt(out, 0, 4);
|
||||
/* Max allocations. */
|
||||
xmlFuzzWriteInt(out, 0, 4);
|
||||
|
||||
/* Copy file */
|
||||
file = fopen(docFile, "rb");
|
||||
if (file == NULL) {
|
||||
fprintf(stderr, "couldn't open %s\n", docFile);
|
||||
return(0);
|
||||
}
|
||||
do {
|
||||
size = fread(buf, 1, SEED_BUF_SIZE, file);
|
||||
if (size > 0)
|
||||
fwrite(buf, 1, size, out);
|
||||
} while (size == SEED_BUF_SIZE);
|
||||
fclose(file);
|
||||
|
||||
return(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_HTML_FUZZER) || \
|
||||
defined(HAVE_XML_FUZZER)
|
||||
static int
|
||||
processPattern(const char *pattern) {
|
||||
glob_t globbuf;
|
||||
int ret = 0;
|
||||
int res;
|
||||
size_t i;
|
||||
|
||||
res = glob(pattern, 0, NULL, &globbuf);
|
||||
if (res == GLOB_NOMATCH)
|
||||
return(0);
|
||||
if (res != 0) {
|
||||
fprintf(stderr, "couldn't match pattern %s\n", pattern);
|
||||
return(-1);
|
||||
}
|
||||
|
||||
for (i = 0; i < globbuf.gl_pathc; i++) {
|
||||
struct stat statbuf;
|
||||
char outPath[PATH_SIZE];
|
||||
char *dirBuf = NULL;
|
||||
char *baseBuf = NULL;
|
||||
const char *path, *dir, *base;
|
||||
FILE *out = NULL;
|
||||
int dirChanged = 0;
|
||||
size_t size;
|
||||
|
||||
path = globbuf.gl_pathv[i];
|
||||
|
||||
if ((stat(path, &statbuf) != 0) || (!S_ISREG(statbuf.st_mode)))
|
||||
continue;
|
||||
|
||||
dirBuf = (char *) xmlCharStrdup(path);
|
||||
baseBuf = (char *) xmlCharStrdup(path);
|
||||
if ((dirBuf == NULL) || (baseBuf == NULL)) {
|
||||
fprintf(stderr, "memory allocation failed\n");
|
||||
ret = -1;
|
||||
goto error;
|
||||
}
|
||||
dir = dirname(dirBuf);
|
||||
base = basename(baseBuf);
|
||||
|
||||
size = snprintf(outPath, sizeof(outPath), "seed/%s/%s",
|
||||
globalData.fuzzer, base);
|
||||
if (size >= PATH_SIZE) {
|
||||
fprintf(stderr, "creating path failed\n");
|
||||
ret = -1;
|
||||
goto error;
|
||||
}
|
||||
out = fopen(outPath, "wb");
|
||||
if (out == NULL) {
|
||||
fprintf(stderr, "couldn't open %s for writing\n", outPath);
|
||||
ret = -1;
|
||||
goto error;
|
||||
}
|
||||
if (chdir(dir) != 0) {
|
||||
fprintf(stderr, "couldn't chdir to %s\n", dir);
|
||||
ret = -1;
|
||||
goto error;
|
||||
}
|
||||
dirChanged = 1;
|
||||
if (globalData.processFile(base, out) != 0)
|
||||
ret = -1;
|
||||
|
||||
error:
|
||||
if (out != NULL)
|
||||
fclose(out);
|
||||
xmlFree(dirBuf);
|
||||
xmlFree(baseBuf);
|
||||
if ((dirChanged) && (chdir(globalData.cwd) != 0)) {
|
||||
fprintf(stderr, "couldn't chdir to %s\n", globalData.cwd);
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
globfree(&globbuf);
|
||||
return(ret);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_SCHEMA_FUZZER)
|
||||
static int
|
||||
processSchema(const char *xsdFile, const char *xmlFile, FILE *out) {
|
||||
xmlSchemaPtr schema;
|
||||
xmlSchemaParserCtxtPtr pctxt;
|
||||
|
||||
/* Max allocations. */
|
||||
xmlFuzzWriteInt(out, 0, 4);
|
||||
|
||||
fuzzRecorderInit(out);
|
||||
|
||||
pctxt = xmlSchemaNewParserCtxt(xsdFile);
|
||||
xmlSchemaSetParserStructuredErrors(pctxt, xmlFuzzSErrorFunc, NULL);
|
||||
xmlSchemaSetResourceLoader(pctxt, fuzzResourceRecorder, NULL);
|
||||
schema = xmlSchemaParse(pctxt);
|
||||
xmlSchemaFreeParserCtxt(pctxt);
|
||||
|
||||
if (schema != NULL) {
|
||||
xmlSchemaValidCtxtPtr vctxt;
|
||||
xmlParserCtxtPtr ctxt;
|
||||
xmlDocPtr doc;
|
||||
|
||||
ctxt = xmlNewParserCtxt();
|
||||
xmlCtxtSetErrorHandler(ctxt, xmlFuzzSErrorFunc, NULL);
|
||||
xmlCtxtSetResourceLoader(ctxt, fuzzResourceRecorder, NULL);
|
||||
doc = xmlCtxtReadFile(ctxt, xmlFile, NULL, XML_PARSE_NOENT);
|
||||
xmlFreeParserCtxt(ctxt);
|
||||
|
||||
vctxt = xmlSchemaNewValidCtxt(schema);
|
||||
xmlSchemaSetValidStructuredErrors(vctxt, xmlFuzzSErrorFunc, NULL);
|
||||
xmlSchemaValidateDoc(vctxt, doc);
|
||||
xmlSchemaFreeValidCtxt(vctxt);
|
||||
|
||||
xmlFreeDoc(doc);
|
||||
xmlSchemaFree(schema);
|
||||
}
|
||||
|
||||
fuzzRecorderWrite(xsdFile);
|
||||
fuzzRecorderWrite(xmlFile);
|
||||
fuzzRecorderCleanup();
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
static int
|
||||
processSchemaPattern(const char *pattern) {
|
||||
glob_t globbuf;
|
||||
int ret = 0;
|
||||
int res;
|
||||
size_t i;
|
||||
|
||||
res = glob(pattern, 0, NULL, &globbuf);
|
||||
if (res == GLOB_NOMATCH)
|
||||
return(0);
|
||||
if (res != 0) {
|
||||
fprintf(stderr, "couldn't match pattern %s\n", pattern);
|
||||
return(-1);
|
||||
}
|
||||
|
||||
for (i = 0; i < globbuf.gl_pathc; i++) {
|
||||
glob_t globbuf2;
|
||||
struct stat statbuf;
|
||||
char xmlPattern[PATH_SIZE];
|
||||
char *dirBuf = NULL;
|
||||
char *baseBuf = NULL;
|
||||
const char *path, *dir, *base;
|
||||
size_t size, dirLen, baseLen, len, j;
|
||||
|
||||
path = globbuf.gl_pathv[i];
|
||||
|
||||
if ((stat(path, &statbuf) != 0) || (!S_ISREG(statbuf.st_mode)))
|
||||
continue;
|
||||
|
||||
dirBuf = (char *) xmlCharStrdup(path);
|
||||
baseBuf = (char *) xmlCharStrdup(path);
|
||||
if ((dirBuf == NULL) || (baseBuf == NULL)) {
|
||||
fprintf(stderr, "memory allocation failed\n");
|
||||
ret = -1;
|
||||
goto error;
|
||||
}
|
||||
dir = dirname(dirBuf);
|
||||
dirLen = strlen(dir);
|
||||
base = basename(baseBuf);
|
||||
baseLen = strlen(base);
|
||||
|
||||
len = strlen(path);
|
||||
if (len <= 5)
|
||||
continue;
|
||||
/* Strip .xsl or _0.xsd suffix */
|
||||
if (len > 6 && path[len - 6] == '_')
|
||||
len -= 6;
|
||||
else
|
||||
len -= 4;
|
||||
size = snprintf(xmlPattern, sizeof(xmlPattern), "%.*s_*.xml",
|
||||
(int) len, path);
|
||||
if (size >= PATH_SIZE) {
|
||||
fprintf(stderr, "creating path failed\n");
|
||||
ret = -1;
|
||||
goto error;
|
||||
}
|
||||
|
||||
res = glob(xmlPattern, 0, NULL, &globbuf2);
|
||||
if (res == GLOB_NOMATCH)
|
||||
goto error;
|
||||
if (res != 0) {
|
||||
fprintf(stderr, "couldn't match pattern %s\n", xmlPattern);
|
||||
ret = -1;
|
||||
goto error;
|
||||
}
|
||||
|
||||
for (j = 0; j < globbuf2.gl_pathc; j++) {
|
||||
char outPath[PATH_SIZE];
|
||||
const char *xmlFile;
|
||||
FILE *out = NULL;
|
||||
|
||||
xmlFile = globbuf2.gl_pathv[j];
|
||||
|
||||
len = strlen(xmlFile);
|
||||
if (len < dirLen + 7)
|
||||
continue;
|
||||
if (len >= 6 && xmlFile[len - 6] == '_')
|
||||
size = snprintf(outPath, sizeof(outPath), "seed/%s/%.*s_%c",
|
||||
globalData.fuzzer, (int) baseLen - 4, base,
|
||||
xmlFile[len - 5]);
|
||||
else
|
||||
size = snprintf(outPath, sizeof(outPath), "seed/%s/%.*s",
|
||||
globalData.fuzzer, (int) baseLen - 4, base);
|
||||
|
||||
if (size >= PATH_SIZE) {
|
||||
fprintf(stderr, "creating path failed\n");
|
||||
ret = -1;
|
||||
continue;
|
||||
}
|
||||
out = fopen(outPath, "wb");
|
||||
if (out == NULL) {
|
||||
fprintf(stderr, "couldn't open %s for writing\n", outPath);
|
||||
ret = -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (chdir(dir) != 0) {
|
||||
fprintf(stderr, "couldn't chdir to %s\n", dir);
|
||||
ret = -1;
|
||||
} else {
|
||||
if (processSchema(base, xmlFile + dirLen + 1, out) != 0)
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
fclose(out);
|
||||
|
||||
if (chdir(globalData.cwd) != 0) {
|
||||
fprintf(stderr, "couldn't chdir to %s\n", globalData.cwd);
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
globfree(&globbuf2);
|
||||
|
||||
error:
|
||||
xmlFree(dirBuf);
|
||||
xmlFree(baseBuf);
|
||||
}
|
||||
|
||||
globfree(&globbuf);
|
||||
return(ret);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_XPATH_FUZZER
|
||||
static int
|
||||
processXPath(const char *testDir, const char *prefix, const char *name,
|
||||
const char *data, const char *subdir, int xptr) {
|
||||
char pattern[PATH_SIZE];
|
||||
glob_t globbuf;
|
||||
size_t i, size;
|
||||
int ret = 0, res;
|
||||
|
||||
size = snprintf(pattern, sizeof(pattern), "%s/%s/%s*",
|
||||
testDir, subdir, prefix);
|
||||
if (size >= PATH_SIZE)
|
||||
return(-1);
|
||||
res = glob(pattern, 0, NULL, &globbuf);
|
||||
if (res == GLOB_NOMATCH)
|
||||
return(0);
|
||||
if (res != 0) {
|
||||
fprintf(stderr, "couldn't match pattern %s\n", pattern);
|
||||
return(-1);
|
||||
}
|
||||
|
||||
for (i = 0; i < globbuf.gl_pathc; i++) {
|
||||
char *path = globbuf.gl_pathv[i];
|
||||
struct stat statbuf;
|
||||
FILE *in;
|
||||
char expr[EXPR_SIZE];
|
||||
|
||||
if ((stat(path, &statbuf) != 0) || (!S_ISREG(statbuf.st_mode)))
|
||||
continue;
|
||||
|
||||
in = fopen(path, "rb");
|
||||
if (in == NULL) {
|
||||
ret = -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
while (fgets(expr, EXPR_SIZE, in) != NULL) {
|
||||
char outPath[PATH_SIZE];
|
||||
FILE *out;
|
||||
int j;
|
||||
|
||||
for (j = 0; expr[j] != 0; j++)
|
||||
if (expr[j] == '\r' || expr[j] == '\n')
|
||||
break;
|
||||
expr[j] = 0;
|
||||
|
||||
size = snprintf(outPath, sizeof(outPath), "seed/xpath/%s-%d",
|
||||
name, globalData.counter);
|
||||
if (size >= PATH_SIZE) {
|
||||
ret = -1;
|
||||
continue;
|
||||
}
|
||||
out = fopen(outPath, "wb");
|
||||
if (out == NULL) {
|
||||
ret = -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Max allocations. */
|
||||
xmlFuzzWriteInt(out, 0, 4);
|
||||
|
||||
if (xptr) {
|
||||
xmlFuzzWriteString(out, expr);
|
||||
} else {
|
||||
char xptrExpr[EXPR_SIZE+100];
|
||||
|
||||
/* Wrap XPath expressions as XPointer */
|
||||
snprintf(xptrExpr, sizeof(xptrExpr), "xpointer(%s)", expr);
|
||||
xmlFuzzWriteString(out, xptrExpr);
|
||||
}
|
||||
|
||||
xmlFuzzWriteString(out, data);
|
||||
|
||||
fclose(out);
|
||||
globalData.counter++;
|
||||
}
|
||||
|
||||
fclose(in);
|
||||
}
|
||||
|
||||
globfree(&globbuf);
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
static int
|
||||
processXPathDir(const char *testDir) {
|
||||
char pattern[PATH_SIZE];
|
||||
glob_t globbuf;
|
||||
size_t i, size;
|
||||
int ret = 0;
|
||||
|
||||
globalData.counter = 1;
|
||||
if (processXPath(testDir, "", "expr", "<d></d>", "expr", 0) != 0)
|
||||
ret = -1;
|
||||
|
||||
size = snprintf(pattern, sizeof(pattern), "%s/docs/*", testDir);
|
||||
if (size >= PATH_SIZE)
|
||||
return(1);
|
||||
if (glob(pattern, 0, NULL, &globbuf) != 0)
|
||||
return(1);
|
||||
|
||||
for (i = 0; i < globbuf.gl_pathc; i++) {
|
||||
char *path = globbuf.gl_pathv[i];
|
||||
char *data;
|
||||
const char *docFile;
|
||||
|
||||
data = xmlSlurpFile(path, NULL);
|
||||
if (data == NULL) {
|
||||
ret = -1;
|
||||
continue;
|
||||
}
|
||||
docFile = basename(path);
|
||||
|
||||
globalData.counter = 1;
|
||||
if (processXPath(testDir, docFile, docFile, data, "tests", 0) != 0)
|
||||
ret = -1;
|
||||
if (processXPath(testDir, docFile, docFile, data, "xptr", 1) != 0)
|
||||
ret = -1;
|
||||
if (processXPath(testDir, docFile, docFile, data, "xptr-xp1", 1) != 0)
|
||||
ret = -1;
|
||||
|
||||
xmlFree(data);
|
||||
}
|
||||
|
||||
globfree(&globbuf);
|
||||
|
||||
return(ret);
|
||||
}
|
||||
#endif
|
||||
|
||||
int
|
||||
main(int argc, const char **argv) {
|
||||
mainFunc processArg = NULL;
|
||||
const char *fuzzer;
|
||||
int ret = 0;
|
||||
int i;
|
||||
|
||||
if (argc < 3) {
|
||||
fprintf(stderr, "usage: seed [FUZZER] [PATTERN...]\n");
|
||||
return(1);
|
||||
}
|
||||
|
||||
fuzzer = argv[1];
|
||||
if (strcmp(fuzzer, "html") == 0) {
|
||||
#ifdef HAVE_HTML_FUZZER
|
||||
processArg = processPattern;
|
||||
globalData.flags |= FLAG_PUSH_CHUNK_SIZE;
|
||||
globalData.processFile = processHtml;
|
||||
#endif
|
||||
} else if (strcmp(fuzzer, "lint") == 0) {
|
||||
#ifdef HAVE_LINT_FUZZER
|
||||
processArg = processPattern;
|
||||
globalData.flags |= FLAG_LINT;
|
||||
globalData.processFile = processXml;
|
||||
#endif
|
||||
} else if (strcmp(fuzzer, "reader") == 0) {
|
||||
#ifdef HAVE_READER_FUZZER
|
||||
processArg = processPattern;
|
||||
globalData.flags |= FLAG_READER;
|
||||
globalData.processFile = processXml;
|
||||
#endif
|
||||
} else if (strcmp(fuzzer, "schema") == 0) {
|
||||
#ifdef HAVE_SCHEMA_FUZZER
|
||||
processArg = processSchemaPattern;
|
||||
#endif
|
||||
} else if (strcmp(fuzzer, "valid") == 0) {
|
||||
#ifdef HAVE_VALID_FUZZER
|
||||
processArg = processPattern;
|
||||
globalData.processFile = processXml;
|
||||
#endif
|
||||
} else if (strcmp(fuzzer, "xinclude") == 0) {
|
||||
#ifdef HAVE_XINCLUDE_FUZZER
|
||||
processArg = processPattern;
|
||||
globalData.processFile = processXml;
|
||||
#endif
|
||||
} else if (strcmp(fuzzer, "xml") == 0) {
|
||||
#ifdef HAVE_XML_FUZZER
|
||||
processArg = processPattern;
|
||||
globalData.flags |= FLAG_PUSH_CHUNK_SIZE;
|
||||
globalData.processFile = processXml;
|
||||
#endif
|
||||
} else if (strcmp(fuzzer, "xpath") == 0) {
|
||||
#ifdef HAVE_XPATH_FUZZER
|
||||
processArg = processXPathDir;
|
||||
#endif
|
||||
} else {
|
||||
fprintf(stderr, "unknown fuzzer %s\n", fuzzer);
|
||||
return(1);
|
||||
}
|
||||
globalData.fuzzer = fuzzer;
|
||||
|
||||
if (getcwd(globalData.cwd, PATH_SIZE) == NULL) {
|
||||
fprintf(stderr, "couldn't get current directory\n");
|
||||
return(1);
|
||||
}
|
||||
|
||||
if (processArg != NULL)
|
||||
for (i = 2; i < argc; i++)
|
||||
processArg(argv[i]);
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,236 @@
|
||||
/*
|
||||
* html.c: a libFuzzer target to test several HTML parser interfaces.
|
||||
*
|
||||
* See Copyright for the status of this software.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <libxml/HTMLparser.h>
|
||||
#include <libxml/HTMLtree.h>
|
||||
#include <libxml/catalog.h>
|
||||
#include "fuzz.h"
|
||||
|
||||
int
|
||||
LLVMFuzzerInitialize(int *argc ATTRIBUTE_UNUSED,
|
||||
char ***argv ATTRIBUTE_UNUSED) {
|
||||
xmlFuzzMemSetup();
|
||||
xmlInitParser();
|
||||
#ifdef LIBXML_CATALOG_ENABLED
|
||||
xmlInitializeCatalog();
|
||||
xmlCatalogSetDefaults(XML_CATA_ALLOW_NONE);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
LLVMFuzzerTestOneInput(const char *data, size_t size) {
|
||||
xmlParserCtxtPtr ctxt;
|
||||
htmlDocPtr doc;
|
||||
const char *docBuffer;
|
||||
size_t failurePos, docSize, maxChunkSize;
|
||||
int opts, errorCode;
|
||||
#ifdef LIBXML_OUTPUT_ENABLED
|
||||
xmlOutputBufferPtr out = NULL;
|
||||
#endif
|
||||
|
||||
xmlFuzzDataInit(data, size);
|
||||
opts = (int) xmlFuzzReadInt(4);
|
||||
failurePos = xmlFuzzReadInt(4) % (size + 100);
|
||||
|
||||
maxChunkSize = xmlFuzzReadInt(4) % (size + size / 8 + 1);
|
||||
if (maxChunkSize == 0)
|
||||
maxChunkSize = 1;
|
||||
|
||||
docBuffer = xmlFuzzReadRemaining(&docSize);
|
||||
if (docBuffer == NULL) {
|
||||
xmlFuzzDataCleanup();
|
||||
return(0);
|
||||
}
|
||||
|
||||
/* Pull parser */
|
||||
|
||||
xmlFuzzInjectFailure(failurePos);
|
||||
ctxt = htmlNewParserCtxt();
|
||||
if (ctxt == NULL) {
|
||||
errorCode = XML_ERR_NO_MEMORY;
|
||||
} else {
|
||||
xmlCtxtSetErrorHandler(ctxt, xmlFuzzSErrorFunc, NULL);
|
||||
doc = htmlCtxtReadMemory(ctxt, docBuffer, docSize, NULL, NULL, opts);
|
||||
errorCode = ctxt->errNo;
|
||||
xmlFuzzCheckFailureReport("htmlCtxtReadMemory",
|
||||
errorCode == XML_ERR_NO_MEMORY,
|
||||
errorCode == XML_IO_EIO);
|
||||
|
||||
if (doc != NULL) {
|
||||
xmlDocPtr copy;
|
||||
|
||||
#ifdef LIBXML_OUTPUT_ENABLED
|
||||
const xmlChar *content;
|
||||
|
||||
/*
|
||||
* Also test the serializer. Call htmlDocContentDumpOutput with our
|
||||
* own buffer to avoid encoding the output. The HTML encoding is
|
||||
* excruciatingly slow (see htmlEntityValueLookup).
|
||||
*/
|
||||
out = xmlAllocOutputBuffer(NULL);
|
||||
htmlDocContentDumpOutput(out, doc, NULL);
|
||||
content = xmlOutputBufferGetContent(out);
|
||||
xmlFuzzCheckFailureReport("htmlDocContentDumpOutput",
|
||||
content == NULL, 0);
|
||||
if (content == NULL) {
|
||||
xmlOutputBufferClose(out);
|
||||
out = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
copy = xmlCopyDoc(doc, 1);
|
||||
xmlFuzzCheckFailureReport("xmlCopyNode", copy == NULL, 0);
|
||||
xmlFreeDoc(copy);
|
||||
|
||||
xmlFreeDoc(doc);
|
||||
}
|
||||
|
||||
htmlFreeParserCtxt(ctxt);
|
||||
}
|
||||
|
||||
|
||||
/* Push parser */
|
||||
|
||||
#ifdef LIBXML_PUSH_ENABLED
|
||||
xmlFuzzInjectFailure(failurePos);
|
||||
ctxt = htmlCreatePushParserCtxt(NULL, NULL, NULL, 0, NULL,
|
||||
XML_CHAR_ENCODING_NONE);
|
||||
|
||||
if (ctxt != NULL) {
|
||||
size_t consumed;
|
||||
int errorCodePush, numChunks, maxChunks;
|
||||
|
||||
xmlCtxtSetErrorHandler(ctxt, xmlFuzzSErrorFunc, NULL);
|
||||
htmlCtxtUseOptions(ctxt, opts);
|
||||
|
||||
consumed = 0;
|
||||
numChunks = 0;
|
||||
maxChunks = 50 + docSize / 100;
|
||||
while (numChunks == 0 ||
|
||||
(consumed < docSize && numChunks < maxChunks)) {
|
||||
size_t chunkSize;
|
||||
int terminate;
|
||||
|
||||
numChunks += 1;
|
||||
chunkSize = docSize - consumed;
|
||||
|
||||
if (numChunks < maxChunks && chunkSize > maxChunkSize) {
|
||||
chunkSize = maxChunkSize;
|
||||
terminate = 0;
|
||||
} else {
|
||||
terminate = 1;
|
||||
}
|
||||
|
||||
htmlParseChunk(ctxt, docBuffer + consumed, chunkSize, terminate);
|
||||
consumed += chunkSize;
|
||||
}
|
||||
|
||||
errorCodePush = ctxt->errNo;
|
||||
xmlFuzzCheckFailureReport("htmlParseChunk",
|
||||
errorCodePush == XML_ERR_NO_MEMORY,
|
||||
errorCodePush == XML_IO_EIO);
|
||||
doc = ctxt->myDoc;
|
||||
|
||||
/*
|
||||
* Push and pull parser differ in when exactly they
|
||||
* stop parsing, and the error code is the *last* error
|
||||
* reported, so we can't check whether the codes match.
|
||||
*/
|
||||
if (errorCode != XML_ERR_NO_MEMORY &&
|
||||
errorCode != XML_IO_EIO &&
|
||||
errorCodePush != XML_ERR_NO_MEMORY &&
|
||||
errorCodePush != XML_IO_EIO &&
|
||||
(errorCode == XML_ERR_OK) != (errorCodePush == XML_ERR_OK)) {
|
||||
fprintf(stderr, "pull/push parser error mismatch: %d != %d\n",
|
||||
errorCode, errorCodePush);
|
||||
#if 0
|
||||
FILE *f = fopen("c.html", "wb");
|
||||
fwrite(docBuffer, docSize, 1, f);
|
||||
fclose(f);
|
||||
fprintf(stderr, "opts: %X\n", opts);
|
||||
#endif
|
||||
abort();
|
||||
}
|
||||
|
||||
#ifdef LIBXML_OUTPUT_ENABLED
|
||||
/*
|
||||
* Verify that pull and push parser produce the same result.
|
||||
*
|
||||
* The NOBLANKS option doesn't work reliably in push mode.
|
||||
*/
|
||||
if ((opts & XML_PARSE_NOBLANKS) == 0 &&
|
||||
errorCode == XML_ERR_OK &&
|
||||
errorCodePush == XML_ERR_OK &&
|
||||
out != NULL) {
|
||||
xmlOutputBufferPtr outPush;
|
||||
const xmlChar *content, *contentPush;
|
||||
|
||||
outPush = xmlAllocOutputBuffer(NULL);
|
||||
htmlDocContentDumpOutput(outPush, doc, NULL);
|
||||
content = xmlOutputBufferGetContent(out);
|
||||
contentPush = xmlOutputBufferGetContent(outPush);
|
||||
|
||||
if (content != NULL && contentPush != NULL) {
|
||||
size_t outSize = xmlOutputBufferGetSize(out);
|
||||
|
||||
if (outSize != xmlOutputBufferGetSize(outPush) ||
|
||||
memcmp(content, contentPush, outSize) != 0) {
|
||||
fprintf(stderr, "pull/push parser roundtrip "
|
||||
"mismatch\n");
|
||||
#if 0
|
||||
FILE *f = fopen("c.html", "wb");
|
||||
fwrite(docBuffer, docSize, 1, f);
|
||||
fclose(f);
|
||||
fprintf(stderr, "opts: %X\n", opts);
|
||||
fprintf(stderr, "---\n%s\n---\n%s\n---\n",
|
||||
xmlOutputBufferGetContent(out),
|
||||
xmlOutputBufferGetContent(outPush));
|
||||
#endif
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
xmlOutputBufferClose(outPush);
|
||||
}
|
||||
#endif
|
||||
|
||||
xmlFreeDoc(doc);
|
||||
htmlFreeParserCtxt(ctxt);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Cleanup */
|
||||
|
||||
#ifdef LIBXML_OUTPUT_ENABLED
|
||||
xmlOutputBufferClose(out);
|
||||
#endif
|
||||
|
||||
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 }, /* opts */
|
||||
{ 4, XML_FUZZ_PROB_ONE / 10 }, /* failurePos */
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
return xmlFuzzMutateChunks(chunks, data, size, maxSize, seed,
|
||||
LLVMFuzzerMutate);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
elem_a="<a></a>"
|
||||
elem_abbr="<abbr></abbr>"
|
||||
elem_acronym="<acronym></acronym>"
|
||||
elem_address="<address></address>"
|
||||
elem_applet="<applet></applet>"
|
||||
elem_area="<area>"
|
||||
elem_b="<b></b>"
|
||||
elem_base="<base>"
|
||||
elem_basefont="<basefont>"
|
||||
elem_bdo="<bdo></bdo>"
|
||||
elem_big="<big></big>"
|
||||
elem_blockquote="<blockquote></blockquote>"
|
||||
elem_body="<body></body>"
|
||||
elem_br="<br>"
|
||||
elem_button="<button></button>"
|
||||
elem_caption="<caption></caption>"
|
||||
elem_center="<center></center>"
|
||||
elem_cite="<cite></cite>"
|
||||
elem_code="<code></code>"
|
||||
elem_col="<col>"
|
||||
elem_colgroup="<colgroup></colgroup>"
|
||||
elem_dd="<dd></dd>"
|
||||
elem_del="<del></del>"
|
||||
elem_dfn="<dfn></dfn>"
|
||||
elem_dir="<dir></dir>"
|
||||
elem_div="<div></div>"
|
||||
elem_dl="<dl></dl>"
|
||||
elem_dt="<dt></dt>"
|
||||
elem_em="<em></em>"
|
||||
elem_embed="<embed></embed>"
|
||||
elem_fieldset="<fieldset></fieldset>"
|
||||
elem_font="<font></font>"
|
||||
elem_form="<form></form>"
|
||||
elem_frame="<frame>"
|
||||
elem_frameset="<frameset></frameset>"
|
||||
elem_h1="<h1></h1>"
|
||||
elem_h2="<h2></h2>"
|
||||
elem_h3="<h3></h3>"
|
||||
elem_h4="<h4></h4>"
|
||||
elem_h5="<h5></h5>"
|
||||
elem_h6="<h6></h6>"
|
||||
elem_head="<head></head>"
|
||||
elem_hr="<hr>"
|
||||
elem_html="<html></html>"
|
||||
elem_i="<i></i>"
|
||||
elem_iframe="<iframe></iframe>"
|
||||
elem_img="<img>"
|
||||
elem_input="<input>"
|
||||
elem_ins="<ins></ins>"
|
||||
elem_isindex="<isindex>"
|
||||
elem_kbd="<kbd></kbd>"
|
||||
elem_label="<label></label>"
|
||||
elem_legend="<legend></legend>"
|
||||
elem_li="<li></li>"
|
||||
elem_link="<link>"
|
||||
elem_map="<map></map>"
|
||||
elem_menu="<menu></menu>"
|
||||
elem_meta="<meta>"
|
||||
elem_noframes="<noframes></noframes>"
|
||||
elem_noscript="<noscript></noscript>"
|
||||
elem_object="<object></object>"
|
||||
elem_ol="<ol></ol>"
|
||||
elem_optgroup="<optgroup></optgroup>"
|
||||
elem_option="<option></option>"
|
||||
elem_p="<p></p>"
|
||||
elem_param="<param>"
|
||||
elem_pre="<pre></pre>"
|
||||
elem_q="<q></q>"
|
||||
elem_s="<s></s>"
|
||||
elem_samp="<samp></samp>"
|
||||
elem_script="<script></script>"
|
||||
elem_select="<select></select>"
|
||||
elem_small="<small></small>"
|
||||
elem_span="<span></span>"
|
||||
elem_strike="<strike></strike>"
|
||||
elem_strong="<strong></strong>"
|
||||
elem_style="<style></style>"
|
||||
elem_sub="<sub></sub>"
|
||||
elem_sup="<sup></sup>"
|
||||
elem_table="<table></table>"
|
||||
elem_tbody="<tbody></tbody>"
|
||||
elem_td="<td></td>"
|
||||
elem_textarea="<textarea></textarea>"
|
||||
elem_tfoot="<tfoot></tfoot>"
|
||||
elem_th="<th></th>"
|
||||
elem_thead="<thead></thead>"
|
||||
elem_title="<title></title>"
|
||||
elem_tr="<tr></tr>"
|
||||
elem_tt="<tt></tt>"
|
||||
elem_u="<u></u>"
|
||||
elem_ul="<ul></ul>"
|
||||
elem_var="<var></var>"
|
||||
|
||||
attr_id=" id=\"\""
|
||||
attr_style=" style=\"\""
|
||||
|
||||
comment="<!-- -->"
|
||||
|
||||
doctype="<!DOCTYPE d>"
|
||||
doctype_system="<!DOCTYPE s SYSTEM \"u\">"
|
||||
doctype_public="<!DOCTYPE p PUBLIC \"i\" \"u\">"
|
||||
|
||||
pi="<?a?>"
|
||||
|
||||
ref_lt="<"
|
||||
ref_gt=">"
|
||||
ref_amp="&"
|
||||
ref_apos="'"
|
||||
ref_quot="""
|
||||
ref_dec="	"
|
||||
ref_hex="
"
|
||||
|
||||
cs_utf8="UTF-8"
|
||||
cs_utf16="UTF-16"
|
||||
cs_utf16le="UTF-16LE"
|
||||
cs_utf16be="UTF-16BE"
|
||||
cs_ucs2="UCS-2"
|
||||
cs_ucs4="UCS-4"
|
||||
cs_latin1="ISO-8859-1"
|
||||
cs_ascii="ASCII"
|
||||
cs_ebcdic="EBCDIC"
|
||||
cs_iso2022jp="ISO-2022-JP"
|
||||
cs_shift_jis="SHIFT_JIS"
|
||||
cs_euc_jp="EUC-JP"
|
||||
@@ -0,0 +1,256 @@
|
||||
/*
|
||||
* lint.c: a libFuzzer target to test the xmllint executable.
|
||||
*
|
||||
* See Copyright for the status of this software.
|
||||
*/
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <libxml/catalog.h>
|
||||
#include <libxml/parser.h>
|
||||
#include <libxml/xmlerror.h>
|
||||
#include <libxml/xmlmemory.h>
|
||||
|
||||
#include "private/lint.h"
|
||||
|
||||
#include "fuzz.h"
|
||||
|
||||
/*
|
||||
* Untested options:
|
||||
*
|
||||
* --memory: Requires temp file
|
||||
*
|
||||
* --catalogs: Requires XML catalogs
|
||||
*
|
||||
* --dtdvalid:
|
||||
* --dtdvalidfpi: Requires an external DTD
|
||||
*
|
||||
* --output: Writes to disk
|
||||
*
|
||||
* --path: Requires cooperation with resource loader
|
||||
*
|
||||
* --relaxng:
|
||||
* --schema:
|
||||
* --schematron: Requires schemas
|
||||
*
|
||||
* --shell: We could pipe fuzz data to stdin but this is probably
|
||||
* not worth it.
|
||||
*/
|
||||
|
||||
static const char *const switches[] = {
|
||||
"--auto",
|
||||
"--c14n",
|
||||
"--c14n11",
|
||||
"--compress",
|
||||
"--copy",
|
||||
"--debug",
|
||||
NULL,
|
||||
"--dropdtd",
|
||||
"--dtdattr",
|
||||
"--exc-c14n",
|
||||
"--format",
|
||||
NULL,
|
||||
"--huge",
|
||||
"--insert",
|
||||
"--loaddtd",
|
||||
"--load-trace",
|
||||
NULL,
|
||||
"--noblanks",
|
||||
"--nocdata",
|
||||
"--nocompact",
|
||||
"--nodefdtd",
|
||||
"--nodict",
|
||||
"--noenc",
|
||||
"--noent",
|
||||
"--nofixup-base-uris",
|
||||
"--nonet",
|
||||
"--noout",
|
||||
"--nowarning",
|
||||
NULL,
|
||||
"--noxincludenode",
|
||||
"--nsclean",
|
||||
"--oldxml10",
|
||||
"--pedantic",
|
||||
"--postvalid",
|
||||
"--push",
|
||||
"--pushsmall",
|
||||
"--quiet",
|
||||
"--recover",
|
||||
"--repeat",
|
||||
"--sax1",
|
||||
NULL,
|
||||
"--timing",
|
||||
"--valid",
|
||||
"--version",
|
||||
"--walker",
|
||||
"--xinclude",
|
||||
"--xmlout"
|
||||
};
|
||||
static const size_t numSwitches = sizeof(switches) / sizeof(switches[0]);
|
||||
|
||||
struct {
|
||||
const char **argv;
|
||||
size_t argi;
|
||||
} vars;
|
||||
|
||||
static void
|
||||
pushArg(const char *str) {
|
||||
vars.argv[vars.argi++] = str;
|
||||
}
|
||||
|
||||
int
|
||||
LLVMFuzzerInitialize(int *argc ATTRIBUTE_UNUSED,
|
||||
char ***argv ATTRIBUTE_UNUSED) {
|
||||
int fd;
|
||||
|
||||
/* Redirect stdout to /dev/null */
|
||||
fd = open("/dev/null", O_WRONLY);
|
||||
if (fd == -1) {
|
||||
perror("/dev/null");
|
||||
abort();
|
||||
}
|
||||
if (dup2(fd, STDOUT_FILENO) == -1) {
|
||||
perror("dup2");
|
||||
abort();
|
||||
}
|
||||
close(fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
LLVMFuzzerTestOneInput(const char *data, size_t size) {
|
||||
char maxmemBuf[20];
|
||||
char maxAmplBuf[20];
|
||||
char prettyBuf[20];
|
||||
const char *sval, *docBuffer, *docUrl;
|
||||
size_t ssize, docSize, i;
|
||||
unsigned uval;
|
||||
int ival;
|
||||
|
||||
if (xmlMemUsed() != 0) {
|
||||
fprintf(stderr, "Undetected leak in previous iteration\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
vars.argv = malloc((numSwitches + 5 + 6 * 2) * sizeof(vars.argv[0]));
|
||||
vars.argi = 0;
|
||||
pushArg("xmllint"),
|
||||
pushArg("--nocatalogs");
|
||||
|
||||
xmlFuzzDataInit(data, size);
|
||||
|
||||
for (i = 0; i < numSwitches; i++) {
|
||||
if (i % 32 == 0)
|
||||
uval = xmlFuzzReadInt(4);
|
||||
if ((uval & 1) && (switches[i] != NULL))
|
||||
pushArg(switches[i]);
|
||||
uval >>= 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Use four main parsing modes with equal probability
|
||||
*/
|
||||
switch (uval & 3) {
|
||||
case 0:
|
||||
/* XML parser */
|
||||
break;
|
||||
case 1:
|
||||
/* HTML parser */
|
||||
pushArg("--html");
|
||||
break;
|
||||
case 2:
|
||||
/* XML reader */
|
||||
pushArg("--stream");
|
||||
break;
|
||||
case 3:
|
||||
/* SAX parser */
|
||||
pushArg("--sax");
|
||||
break;
|
||||
}
|
||||
|
||||
uval = xmlFuzzReadInt(4);
|
||||
if (uval > 0) {
|
||||
if (size <= (INT_MAX - 2000) / 20)
|
||||
uval %= size * 20 + 2000;
|
||||
else
|
||||
uval %= INT_MAX;
|
||||
snprintf(maxmemBuf, 20, "%u", uval);
|
||||
pushArg("--maxmem");
|
||||
pushArg(maxmemBuf);
|
||||
}
|
||||
|
||||
ival = xmlFuzzReadInt(1);
|
||||
if (ival >= 1 && ival <= 5) {
|
||||
snprintf(maxAmplBuf, 20, "%d", ival);
|
||||
pushArg("--max-ampl");
|
||||
pushArg(maxAmplBuf);
|
||||
}
|
||||
|
||||
ival = xmlFuzzReadInt(1);
|
||||
if (ival != 0) {
|
||||
snprintf(prettyBuf, 20, "%d", ival % 4);
|
||||
pushArg("--pretty");
|
||||
pushArg(prettyBuf);
|
||||
}
|
||||
|
||||
sval = xmlFuzzReadString(&ssize);
|
||||
if (ssize > 0) {
|
||||
pushArg("--encode");
|
||||
pushArg(sval);
|
||||
}
|
||||
|
||||
sval = xmlFuzzReadString(&ssize);
|
||||
if (ssize > 0) {
|
||||
pushArg("--pattern");
|
||||
pushArg(sval);
|
||||
}
|
||||
|
||||
sval = xmlFuzzReadString(&ssize);
|
||||
if (ssize > 0) {
|
||||
pushArg("--xpath");
|
||||
pushArg(sval);
|
||||
}
|
||||
|
||||
xmlFuzzReadEntities();
|
||||
docBuffer = xmlFuzzMainEntity(&docSize);
|
||||
docUrl = xmlFuzzMainUrl();
|
||||
if (docBuffer == NULL || docUrl[0] == '-')
|
||||
goto exit;
|
||||
pushArg(docUrl);
|
||||
|
||||
pushArg(NULL);
|
||||
|
||||
xmlSetGenericErrorFunc(NULL, xmlFuzzErrorFunc);
|
||||
#ifdef LIBXML_CATALOG_ENABLED
|
||||
xmlCatalogSetDefaults(XML_CATA_ALLOW_NONE);
|
||||
#endif
|
||||
|
||||
xmllintMain(vars.argi - 1, vars.argv, stdout, xmlFuzzResourceLoader);
|
||||
|
||||
xmlMemSetup(free, malloc, realloc, xmlMemStrdup);
|
||||
|
||||
exit:
|
||||
xmlFuzzDataCleanup();
|
||||
free(vars.argv);
|
||||
return(0);
|
||||
}
|
||||
|
||||
size_t
|
||||
LLVMFuzzerCustomMutator(char *data, size_t size, size_t maxSize,
|
||||
unsigned seed) {
|
||||
static const xmlFuzzChunkDesc chunks[] = {
|
||||
{ 8, XML_FUZZ_PROB_ONE / 10 }, /* switches */
|
||||
{ 4, XML_FUZZ_PROB_ONE / 10 }, /* maxmem */
|
||||
{ 1, XML_FUZZ_PROB_ONE / 100 }, /* maxAmpl */
|
||||
{ 1, XML_FUZZ_PROB_ONE / 100 }, /* pretty */
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
return xmlFuzzMutateChunks(chunks, data, size, maxSize, seed,
|
||||
LLVMFuzzerMutate);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,192 @@
|
||||
xml_decl="<?xml version='1.0'?>"
|
||||
xml_decl_standalone="<?xml version='1.0' standalone='1'?>"
|
||||
xml_decl_latin1="<?xml version='1.0' encoding='ISO-8859-1'?>"
|
||||
|
||||
elem_start_end="<a></a>"
|
||||
elem_empty="<a/>"
|
||||
elem_ns_start_end="<a:a xmlns:a='a'></a:a>"
|
||||
elem_ns_empty="<a:a xmlns:a='a'/>"
|
||||
|
||||
attr=" a='a'"
|
||||
|
||||
ns_decl=" xmlns:a='a'"
|
||||
ns_default=" xmlns='a'"
|
||||
ns_prefix="a:"
|
||||
|
||||
cdata_section="<![CDATA[ ]]>"
|
||||
|
||||
comment="<!-- -->"
|
||||
|
||||
pi="<?a?>"
|
||||
|
||||
doctype="<!DOCTYPE a []>"
|
||||
doctype_system="<!DOCTYPE a SYSTEM 'a' []>"
|
||||
doctype_system="<!DOCTYPE a PUBLIC 'a' 'a' []>"
|
||||
|
||||
elem_decl_any="<!ELEMENT a ANY>"
|
||||
elem_decl_empty="<!ELEMENT a EMPTY>"
|
||||
elem_decl_children="<!ELEMENT a (a)>"
|
||||
elem_decl_mixed="<!ELEMENT a (#PCDATA|a)>"
|
||||
elem_children_choice="|a"
|
||||
elem_children_seq=",a"
|
||||
elem_children_sub_choice="|(a)"
|
||||
elem_children_sub_seq=",(a)"
|
||||
elem_quant_any="*"
|
||||
elem_quant_opt="?"
|
||||
elem_quant_some="+"
|
||||
|
||||
attr_decl_cdata_req="<!ATTLIST a a CDATA #REQUIRED>"
|
||||
attr_decl_cdata_imp="<!ATTLIST a a CDATA #IMPLIED>"
|
||||
attr_decl_cdata_def="<!ATTLIST a a CDATA 'a'>"
|
||||
attr_decl_cdata_fix="<!ATTLIST a a CDATA #FIXED 'a'>"
|
||||
attr_decl_id_imp="<!ATTLIST a a ID #IMPLIED>"
|
||||
attr_decl_id_req="<!ATTLIST a a ID #REQUIRED>"
|
||||
attr_decl_id_def="<!ATTLIST a a ID 'a'>"
|
||||
attr_decl_id_fix="<!ATTLIST a a ID #FIXED 'a'>"
|
||||
attr_decl_idref_imp="<!ATTLIST a a IDREF #IMPLIED>"
|
||||
attr_decl_idref_req="<!ATTLIST a a IDREF #REQUIRED>"
|
||||
attr_decl_idref_def="<!ATTLIST a a IDREF 'a'>"
|
||||
attr_decl_idref_fix="<!ATTLIST a a IDREF #FIXED 'a'>"
|
||||
attr_decl_idrefs_imp="<!ATTLIST a a IDREFS #IMPLIED>"
|
||||
attr_decl_idrefs_req="<!ATTLIST a a IDREFS #REQUIRED>"
|
||||
attr_decl_idrefs_def="<!ATTLIST a a IDREFS 'a'>"
|
||||
attr_decl_idrefs_fix="<!ATTLIST a a IDREFS #FIXED 'a'>"
|
||||
attr_decl_entity_imp="<!ATTLIST a a ENTITY #IMPLIED>"
|
||||
attr_decl_entity_req="<!ATTLIST a a ENTITY #REQUIRED>"
|
||||
attr_decl_entity_def="<!ATTLIST a a ENTITY 'a'>"
|
||||
attr_decl_entity_fix="<!ATTLIST a a ENTITY #FIXED 'a'>"
|
||||
attr_decl_entities_imp="<!ATTLIST a a ENTITIES #IMPLIED>"
|
||||
attr_decl_entities_req="<!ATTLIST a a ENTITIES #REQUIRED>"
|
||||
attr_decl_entities_def="<!ATTLIST a a ENTITIES 'a'>"
|
||||
attr_decl_entities_fix="<!ATTLIST a a ENTITIES #FIXED 'a'>"
|
||||
attr_decl_nmtoken_imp="<!ATTLIST a a NMTOKEN #IMPLIED>"
|
||||
attr_decl_nmtoken_req="<!ATTLIST a a NMTOKEN #REQUIRED>"
|
||||
attr_decl_nmtoken_def="<!ATTLIST a a NMTOKEN 'a'>"
|
||||
attr_decl_nmtoken_fix="<!ATTLIST a a NMTOKEN #FIXED 'a'>"
|
||||
attr_decl_nmtokens_imp="<!ATTLIST a a NMTOKENS #IMPLIED>"
|
||||
attr_decl_nmtokens_req="<!ATTLIST a a NMTOKENS #REQUIRED>"
|
||||
attr_decl_nmtokens_def="<!ATTLIST a a NMTOKENS 'a'>"
|
||||
attr_decl_nmtokens_fix="<!ATTLIST a a NMTOKENS #FIXED 'a'>"
|
||||
attr_decl_enum_imp="<!ATTLIST a a (a) #IMPLIED>"
|
||||
attr_decl_enum_req="<!ATTLIST a a (a) #REQUIRED>"
|
||||
attr_decl_enum_def="<!ATTLIST a a (a) 'a'>"
|
||||
attr_decl_enum_fix="<!ATTLIST a a (a) #FIXED 'a'>"
|
||||
attr_decl_notation_imp="<!ATTLIST a a NOTATION (a) #IMPLIED>"
|
||||
attr_decl_notation_req="<!ATTLIST a a NOTATION (a) #REQUIRED>"
|
||||
attr_decl_notation_def="<!ATTLIST a a NOTATION (a) 'a'>"
|
||||
attr_decl_notation_fix="<!ATTLIST a a NOTATION (a) #FIXED 'a'>"
|
||||
|
||||
include_sect="<![INCLUDE[ ]]>"
|
||||
ignore_sect="<![IGNORE[ ]]>"
|
||||
|
||||
ge_decl="<!ENTITY a 'a'>"
|
||||
ge_decl_system="<!ENTITY a SYSTEM 'a'>"
|
||||
ge_decl_system_ndata="<!ENTITY a SYSTEM 'a' NDATA a>"
|
||||
ge_decl_public="<!ENTITY a PUBLIC 'a' 'a'>"
|
||||
ge_decl_public_ndata="<!ENTITY a PUBLIC 'a' 'a' NDATA a>"
|
||||
|
||||
pe_decl="<!ENTITY % a 'a'>"
|
||||
pe_decl_system="<!ENTITY % a SYSTEM 'a'>"
|
||||
pe_decl_public="<!ENTITY % a PUBLIC 'a' 'a'>"
|
||||
|
||||
char_ref_dec="<"
|
||||
char_ref_hex="<"
|
||||
char_ref_quoted="&#60;"
|
||||
|
||||
ge_ref_lt="<"
|
||||
ge_ref_gt=">"
|
||||
ge_ref_amp="&"
|
||||
ge_ref_apos="'"
|
||||
ge_ref_quot="""
|
||||
ge_ref="&a;"
|
||||
ge_ref_quoted="&a;"
|
||||
|
||||
pe_ref="%a;"
|
||||
pe_ref_quoted="%a;"
|
||||
|
||||
notation_decl_public="<!NOTATION a PUBLIC 'a'>"
|
||||
notation_decl_system="<!NOTATION a SYSTEM 'a'>"
|
||||
|
||||
cs_utf8="UTF-8"
|
||||
cs_utf16="UTF-16"
|
||||
cs_utf16le="UTF-16LE"
|
||||
cs_utf16be="UTF-16BE"
|
||||
cs_ucs2="UCS-2"
|
||||
cs_ucs4="UCS-4"
|
||||
cs_latin1="ISO-8859-1"
|
||||
cs_ascii="ASCII"
|
||||
cs_ebcdic="EBCDIC"
|
||||
cs_iso2022jp="ISO-2022-JP"
|
||||
cs_shift_jis="SHIFT_JIS"
|
||||
cs_euc_jp="EUC-JP"
|
||||
|
||||
# XPath
|
||||
|
||||
axis_ancestor="ancestor::"
|
||||
axis_ancestor_or_self="ancestor-or-self::"
|
||||
axis_attribute="attribute::"
|
||||
axis_attribute_abbrev="@"
|
||||
axis_child="child::"
|
||||
axis_descendant="descendant::"
|
||||
axis_descendant_or_self="descendant-or-self::"
|
||||
axis_following="following::"
|
||||
axis_following_sibling="following-sibling::"
|
||||
axis_namespace="namespace::"
|
||||
axis_parent="parent::"
|
||||
axis_preceding="preceding::"
|
||||
axis_preceding_siblings="preceding-sibling::"
|
||||
axis_self="self::"
|
||||
|
||||
node_test_ns="a:"
|
||||
|
||||
val_num="=(1.0)"
|
||||
val_str_sq="=('a')"
|
||||
val_str_dq="=(\"a\")"
|
||||
val_node_set="=(*)"
|
||||
val_elem="=(b)"
|
||||
|
||||
step_root="/"
|
||||
step_descendant="//"
|
||||
step_any="//*"
|
||||
step_any_l="*//"
|
||||
step_elem="//b"
|
||||
step_ns_elem="//a:a"
|
||||
step_comment="//comment()"
|
||||
step_node="//node()"
|
||||
step_node_l="node()//"
|
||||
step_pi="//processing-instruction()"
|
||||
step_text="//text()"
|
||||
step_parent="../"
|
||||
|
||||
op_plus="+1"
|
||||
op_minus=" - 1"
|
||||
op_neg="-"
|
||||
op_mul="*1"
|
||||
op_div=" div 1"
|
||||
op_mod=" mod 1"
|
||||
op_and=" and 1"
|
||||
op_or=" or 1"
|
||||
op_ne="!=1"
|
||||
op_lt="<1"
|
||||
op_gt=">1"
|
||||
op_le="<=1"
|
||||
op_ge=">=1"
|
||||
op_predicate_num="[1]"
|
||||
op_predicate_last="[last()]"
|
||||
op_predicate_str="['a']"
|
||||
op_predicate="[1=1]"
|
||||
op_arg_num=",1"
|
||||
op_arg_str=",'a'"
|
||||
op_arg_node=",*"
|
||||
op_union="|//b"
|
||||
|
||||
var_num="=$f"
|
||||
var_bool="=$b"
|
||||
var_str="=$s"
|
||||
var_node_set="=$n"
|
||||
|
||||
# Unicode
|
||||
|
||||
utf8_2="\xC3\x84"
|
||||
utf8_3="\xE2\x80\x9C"
|
||||
utf8_4="\xF0\x9F\x98\x80"
|
||||
@@ -0,0 +1,62 @@
|
||||
#!/bin/bash -eu
|
||||
|
||||
# OSS-Fuzz integration, see
|
||||
# https://github.com/google/oss-fuzz/tree/master/projects/libxml2
|
||||
|
||||
# Add extra UBSan checks
|
||||
if [ "$SANITIZER" = undefined ]; then
|
||||
extra_checks="integer,float-divide-by-zero"
|
||||
extra_cflags="-fsanitize=$extra_checks -fno-sanitize-recover=$extra_checks"
|
||||
export CFLAGS="$CFLAGS $extra_cflags"
|
||||
export CXXFLAGS="$CXXFLAGS $extra_cflags"
|
||||
fi
|
||||
|
||||
# Don't enable zlib with MSan
|
||||
if [ "$SANITIZER" = memory ]; then
|
||||
CONFIG=''
|
||||
else
|
||||
CONFIG='--with-zlib'
|
||||
fi
|
||||
|
||||
# Workaround for a LeakSanitizer crashes,
|
||||
# see https://github.com/google/oss-fuzz/issues/11798.
|
||||
if [ "$ARCHITECTURE" = "aarch64" ]; then
|
||||
export ASAN_OPTIONS=detect_leaks=0
|
||||
fi
|
||||
|
||||
export V=1
|
||||
|
||||
./autogen.sh \
|
||||
--disable-shared \
|
||||
--without-debug \
|
||||
--without-http \
|
||||
--without-python \
|
||||
$CONFIG
|
||||
make -j$(nproc)
|
||||
|
||||
cd fuzz
|
||||
make clean-corpus
|
||||
make fuzz.o
|
||||
|
||||
for fuzzer in \
|
||||
api html lint reader regexp schema uri valid xinclude xml xpath
|
||||
do
|
||||
OBJS="$fuzzer.o"
|
||||
if [ "$fuzzer" = lint ]; then
|
||||
OBJS="$OBJS ../xmllint.o ../shell.o"
|
||||
fi
|
||||
make $OBJS
|
||||
# Link with $CXX
|
||||
$CXX $CXXFLAGS \
|
||||
$OBJS fuzz.o \
|
||||
-o $OUT/$fuzzer \
|
||||
$LIB_FUZZING_ENGINE \
|
||||
../.libs/libxml2.a -Wl,-Bstatic -lz -Wl,-Bdynamic
|
||||
|
||||
if [ $fuzzer != api ]; then
|
||||
[ -e seed/$fuzzer ] || make seed/$fuzzer.stamp
|
||||
zip -j $OUT/${fuzzer}_seed_corpus.zip seed/$fuzzer/*
|
||||
fi
|
||||
done
|
||||
|
||||
cp *.dict *.options $OUT/
|
||||
@@ -0,0 +1,568 @@
|
||||
/*
|
||||
* reader.c: a libFuzzer target to test the XML Reader API.
|
||||
*
|
||||
* See Copyright for the status of this software.
|
||||
*/
|
||||
|
||||
#include <libxml/catalog.h>
|
||||
#include <libxml/parser.h>
|
||||
#include <libxml/tree.h>
|
||||
#include <libxml/xmlerror.h>
|
||||
#include <libxml/xmlreader.h>
|
||||
#include <libxml/xmlsave.h>
|
||||
#include "fuzz.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#if 0
|
||||
#define DEBUG
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
OP_READ = 1,
|
||||
OP_READ_INNER_XML,
|
||||
OP_READ_OUTER_XML,
|
||||
OP_READ_STRING,
|
||||
OP_READ_ATTRIBUTE_VALUE,
|
||||
OP_ATTRIBUTE_COUNT,
|
||||
OP_DEPTH,
|
||||
OP_HAS_ATTRIBUTES,
|
||||
OP_HAS_VALUE,
|
||||
OP_IS_DEFAULT,
|
||||
OP_IS_EMPTY_ELEMENT,
|
||||
OP_NODE_TYPE,
|
||||
OP_QUOTE_CHAR,
|
||||
OP_READ_STATE,
|
||||
OP_IS_NAMESPACE_DECL,
|
||||
OP_CONST_BASE_URI,
|
||||
OP_CONST_LOCAL_NAME,
|
||||
OP_CONST_NAME,
|
||||
OP_CONST_NAMESPACE_URI,
|
||||
OP_CONST_PREFIX,
|
||||
OP_CONST_XML_LANG,
|
||||
OP_CONST_VALUE,
|
||||
OP_BASE_URI,
|
||||
OP_LOCAL_NAME,
|
||||
OP_NAME,
|
||||
OP_NAMESPACE_URI,
|
||||
OP_PREFIX,
|
||||
OP_XML_LANG,
|
||||
OP_VALUE,
|
||||
OP_CLOSE,
|
||||
OP_GET_ATTRIBUTE_NO,
|
||||
OP_GET_ATTRIBUTE,
|
||||
OP_GET_ATTRIBUTE_NS,
|
||||
OP_GET_REMAINDER,
|
||||
OP_LOOKUP_NAMESPACE,
|
||||
OP_MOVE_TO_ATTRIBUTE_NO,
|
||||
OP_MOVE_TO_ATTRIBUTE,
|
||||
OP_MOVE_TO_ATTRIBUTE_NS,
|
||||
OP_MOVE_TO_FIRST_ATTRIBUTE,
|
||||
OP_MOVE_TO_NEXT_ATTRIBUTE,
|
||||
OP_MOVE_TO_ELEMENT,
|
||||
OP_NORMALIZATION,
|
||||
OP_CONST_ENCODING,
|
||||
OP_GET_PARSER_PROP,
|
||||
OP_CURRENT_NODE,
|
||||
OP_GET_PARSER_LINE_NUMBER,
|
||||
OP_GET_PARSER_COLUMN_NUMBER,
|
||||
OP_PRESERVE,
|
||||
OP_CURRENT_DOC,
|
||||
OP_EXPAND,
|
||||
OP_NEXT,
|
||||
OP_NEXT_SIBLING,
|
||||
OP_IS_VALID,
|
||||
OP_CONST_XML_VERSION,
|
||||
OP_STANDALONE,
|
||||
OP_BYTE_CONSUMED,
|
||||
|
||||
OP_MAX
|
||||
} opType;
|
||||
|
||||
static void
|
||||
startOp(const char *name) {
|
||||
(void) name;
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "%s\n", name);
|
||||
#endif
|
||||
}
|
||||
|
||||
int
|
||||
LLVMFuzzerInitialize(int *argc ATTRIBUTE_UNUSED,
|
||||
char ***argv ATTRIBUTE_UNUSED) {
|
||||
xmlFuzzMemSetup();
|
||||
xmlInitParser();
|
||||
#ifdef LIBXML_CATALOG_ENABLED
|
||||
xmlInitializeCatalog();
|
||||
xmlCatalogSetDefaults(XML_CATA_ALLOW_NONE);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
LLVMFuzzerTestOneInput(const char *data, size_t size) {
|
||||
xmlTextReaderPtr reader;
|
||||
xmlDocPtr doc = NULL;
|
||||
const xmlError *error;
|
||||
const char *docBuffer;
|
||||
const unsigned char *program;
|
||||
size_t failurePos, docSize, programSize, i;
|
||||
size_t totalStringSize = 0;
|
||||
int opts;
|
||||
int oomReport = 0;
|
||||
|
||||
xmlFuzzDataInit(data, size);
|
||||
opts = (int) xmlFuzzReadInt(4);
|
||||
failurePos = xmlFuzzReadInt(4) % (size + 100);
|
||||
|
||||
program = (const unsigned char *) xmlFuzzReadString(&programSize);
|
||||
if (programSize > 1000)
|
||||
programSize = 1000;
|
||||
|
||||
xmlFuzzReadEntities();
|
||||
docBuffer = xmlFuzzMainEntity(&docSize);
|
||||
if (docBuffer == NULL)
|
||||
goto exit;
|
||||
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "Input document (%d bytes):\n", (int) docSize);
|
||||
for (i = 0; (size_t) i < docSize; i++) {
|
||||
int c = (unsigned char) docBuffer[i];
|
||||
|
||||
if ((c == '\n' || (c >= 0x20 && c <= 0x7E)))
|
||||
putc(c, stderr);
|
||||
else
|
||||
fprintf(stderr, "\\x%02X", c);
|
||||
}
|
||||
fprintf(stderr, "\nEOF\n");
|
||||
#endif
|
||||
|
||||
xmlFuzzInjectFailure(failurePos);
|
||||
reader = xmlReaderForMemory(docBuffer, docSize, NULL, NULL, opts);
|
||||
if (reader == NULL)
|
||||
goto exit;
|
||||
|
||||
xmlTextReaderSetStructuredErrorHandler(reader, xmlFuzzSErrorFunc, NULL);
|
||||
xmlTextReaderSetResourceLoader(reader, xmlFuzzResourceLoader, NULL);
|
||||
|
||||
i = 0;
|
||||
while (i < programSize) {
|
||||
int op = program[i++];
|
||||
|
||||
#define READ_BYTE() (i < programSize ? program[i++] : 0)
|
||||
#define FREE_STRING(str) \
|
||||
do { \
|
||||
if (str != NULL) { \
|
||||
totalStringSize += strlen((char *) str); \
|
||||
xmlFree(str); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
switch (op & 0x3F) {
|
||||
case OP_READ:
|
||||
default:
|
||||
startOp("Read");
|
||||
xmlTextReaderRead(reader);
|
||||
break;
|
||||
|
||||
case OP_READ_INNER_XML: {
|
||||
xmlChar *result;
|
||||
|
||||
startOp("ReadInnerXml");
|
||||
result = xmlTextReaderReadInnerXml(reader);
|
||||
FREE_STRING(result);
|
||||
break;
|
||||
}
|
||||
|
||||
case OP_READ_OUTER_XML: {
|
||||
xmlChar *result;
|
||||
|
||||
startOp("ReadOuterXml");
|
||||
result = xmlTextReaderReadOuterXml(reader);
|
||||
FREE_STRING(result);
|
||||
break;
|
||||
}
|
||||
|
||||
case OP_READ_STRING: {
|
||||
xmlChar *result;
|
||||
|
||||
startOp("ReadString");
|
||||
result = xmlTextReaderReadString(reader);
|
||||
FREE_STRING(result);
|
||||
break;
|
||||
}
|
||||
|
||||
case OP_READ_ATTRIBUTE_VALUE:
|
||||
startOp("ReadAttributeValue");
|
||||
xmlTextReaderReadAttributeValue(reader);
|
||||
break;
|
||||
|
||||
case OP_ATTRIBUTE_COUNT:
|
||||
startOp("AttributeCount");
|
||||
xmlTextReaderAttributeCount(reader);
|
||||
break;
|
||||
|
||||
case OP_DEPTH:
|
||||
startOp("Depth");
|
||||
xmlTextReaderDepth(reader);
|
||||
break;
|
||||
|
||||
case OP_HAS_ATTRIBUTES:
|
||||
startOp("HasAttributes");
|
||||
xmlTextReaderHasAttributes(reader);
|
||||
break;
|
||||
|
||||
case OP_HAS_VALUE:
|
||||
startOp("HasValue");
|
||||
xmlTextReaderHasValue(reader);
|
||||
break;
|
||||
|
||||
case OP_IS_DEFAULT:
|
||||
startOp("IsDefault");
|
||||
xmlTextReaderIsDefault(reader);
|
||||
break;
|
||||
|
||||
case OP_IS_EMPTY_ELEMENT:
|
||||
startOp("IsEmptyElement");
|
||||
xmlTextReaderIsEmptyElement(reader);
|
||||
break;
|
||||
|
||||
case OP_NODE_TYPE:
|
||||
startOp("NodeType");
|
||||
xmlTextReaderNodeType(reader);
|
||||
break;
|
||||
|
||||
case OP_QUOTE_CHAR:
|
||||
startOp("QuoteChar");
|
||||
xmlTextReaderQuoteChar(reader);
|
||||
break;
|
||||
|
||||
case OP_READ_STATE:
|
||||
startOp("ReadState");
|
||||
xmlTextReaderReadState(reader);
|
||||
break;
|
||||
|
||||
case OP_IS_NAMESPACE_DECL:
|
||||
startOp("IsNamespaceDecl");
|
||||
xmlTextReaderIsNamespaceDecl(reader);
|
||||
break;
|
||||
|
||||
case OP_CONST_BASE_URI:
|
||||
startOp("ConstBaseUri");
|
||||
xmlTextReaderConstBaseUri(reader);
|
||||
break;
|
||||
|
||||
case OP_CONST_LOCAL_NAME:
|
||||
startOp("ConstLocalName");
|
||||
xmlTextReaderConstLocalName(reader);
|
||||
break;
|
||||
|
||||
case OP_CONST_NAME:
|
||||
startOp("ConstName");
|
||||
xmlTextReaderConstName(reader);
|
||||
break;
|
||||
|
||||
case OP_CONST_NAMESPACE_URI:
|
||||
startOp("ConstNamespaceUri");
|
||||
xmlTextReaderConstNamespaceUri(reader);
|
||||
break;
|
||||
|
||||
case OP_CONST_PREFIX:
|
||||
startOp("ConstPrefix");
|
||||
xmlTextReaderConstPrefix(reader);
|
||||
break;
|
||||
|
||||
case OP_CONST_XML_LANG:
|
||||
startOp("ConstXmlLang");
|
||||
xmlTextReaderConstXmlLang(reader);
|
||||
oomReport = -1;
|
||||
break;
|
||||
|
||||
case OP_CONST_VALUE:
|
||||
startOp("ConstValue");
|
||||
xmlTextReaderConstValue(reader);
|
||||
break;
|
||||
|
||||
case OP_BASE_URI: {
|
||||
xmlChar *result;
|
||||
|
||||
startOp("BaseUri");
|
||||
result = xmlTextReaderBaseUri(reader);
|
||||
FREE_STRING(result);
|
||||
break;
|
||||
}
|
||||
|
||||
case OP_LOCAL_NAME: {
|
||||
xmlChar *result;
|
||||
|
||||
startOp("LocalName");
|
||||
result = xmlTextReaderLocalName(reader);
|
||||
FREE_STRING(result);
|
||||
break;
|
||||
}
|
||||
|
||||
case OP_NAME: {
|
||||
xmlChar *result;
|
||||
|
||||
startOp("Name");
|
||||
result = xmlTextReaderName(reader);
|
||||
FREE_STRING(result);
|
||||
break;
|
||||
}
|
||||
|
||||
case OP_NAMESPACE_URI: {
|
||||
xmlChar *result;
|
||||
|
||||
startOp("NamespaceUri");
|
||||
result = xmlTextReaderNamespaceUri(reader);
|
||||
FREE_STRING(result);
|
||||
break;
|
||||
}
|
||||
|
||||
case OP_PREFIX: {
|
||||
xmlChar *result;
|
||||
|
||||
startOp("Prefix");
|
||||
result = xmlTextReaderPrefix(reader);
|
||||
FREE_STRING(result);
|
||||
break;
|
||||
}
|
||||
|
||||
case OP_XML_LANG: {
|
||||
xmlChar *result;
|
||||
|
||||
startOp("XmlLang");
|
||||
result = xmlTextReaderXmlLang(reader);
|
||||
oomReport = -1;
|
||||
FREE_STRING(result);
|
||||
break;
|
||||
}
|
||||
|
||||
case OP_VALUE: {
|
||||
xmlChar *result;
|
||||
|
||||
startOp("Value");
|
||||
result = xmlTextReaderValue(reader);
|
||||
FREE_STRING(result);
|
||||
break;
|
||||
}
|
||||
|
||||
case OP_CLOSE:
|
||||
startOp("Close");
|
||||
if (doc == NULL)
|
||||
doc = xmlTextReaderCurrentDoc(reader);
|
||||
xmlTextReaderClose(reader);
|
||||
break;
|
||||
|
||||
case OP_GET_ATTRIBUTE_NO: {
|
||||
xmlChar *result;
|
||||
int no = READ_BYTE();
|
||||
|
||||
startOp("GetAttributeNo");
|
||||
result = xmlTextReaderGetAttributeNo(reader, no);
|
||||
FREE_STRING(result);
|
||||
break;
|
||||
}
|
||||
|
||||
case OP_GET_ATTRIBUTE: {
|
||||
const xmlChar *name = xmlTextReaderConstName(reader);
|
||||
xmlChar *result;
|
||||
|
||||
startOp("GetAttribute");
|
||||
result = xmlTextReaderGetAttribute(reader, name);
|
||||
FREE_STRING(result);
|
||||
break;
|
||||
}
|
||||
|
||||
case OP_GET_ATTRIBUTE_NS: {
|
||||
const xmlChar *localName, *namespaceUri;
|
||||
xmlChar *result;
|
||||
|
||||
startOp("GetAttributeNs");
|
||||
localName = xmlTextReaderConstLocalName(reader);
|
||||
namespaceUri = xmlTextReaderConstNamespaceUri(reader);
|
||||
result = xmlTextReaderGetAttributeNs(reader, localName,
|
||||
namespaceUri);
|
||||
FREE_STRING(result);
|
||||
break;
|
||||
}
|
||||
|
||||
case OP_GET_REMAINDER:
|
||||
startOp("GetRemainder");
|
||||
if (doc == NULL)
|
||||
doc = xmlTextReaderCurrentDoc(reader);
|
||||
xmlFreeParserInputBuffer(xmlTextReaderGetRemainder(reader));
|
||||
break;
|
||||
|
||||
case OP_LOOKUP_NAMESPACE: {
|
||||
const xmlChar *prefix = xmlTextReaderConstPrefix(reader);
|
||||
xmlChar *result;
|
||||
|
||||
startOp("LookupNamespace");
|
||||
result = xmlTextReaderLookupNamespace(reader, prefix);
|
||||
FREE_STRING(result);
|
||||
break;
|
||||
}
|
||||
|
||||
case OP_MOVE_TO_ATTRIBUTE_NO: {
|
||||
int no = READ_BYTE();
|
||||
|
||||
startOp("MoveToAttributeNo");
|
||||
xmlTextReaderMoveToAttributeNo(reader, no);
|
||||
break;
|
||||
}
|
||||
|
||||
case OP_MOVE_TO_ATTRIBUTE: {
|
||||
const xmlChar *name = xmlTextReaderConstName(reader);
|
||||
|
||||
startOp("MoveToAttribute");
|
||||
xmlTextReaderMoveToAttribute(reader, name);
|
||||
break;
|
||||
}
|
||||
|
||||
case OP_MOVE_TO_ATTRIBUTE_NS: {
|
||||
const xmlChar *localName, *namespaceUri;
|
||||
|
||||
startOp("MoveToAttributeNs");
|
||||
localName = xmlTextReaderConstLocalName(reader);
|
||||
namespaceUri = xmlTextReaderConstNamespaceUri(reader);
|
||||
xmlTextReaderMoveToAttributeNs(reader, localName,
|
||||
namespaceUri);
|
||||
break;
|
||||
}
|
||||
|
||||
case OP_MOVE_TO_FIRST_ATTRIBUTE:
|
||||
startOp("MoveToFirstAttribute");
|
||||
xmlTextReaderMoveToFirstAttribute(reader);
|
||||
break;
|
||||
|
||||
case OP_MOVE_TO_NEXT_ATTRIBUTE:
|
||||
startOp("MoveToNextAttribute");
|
||||
xmlTextReaderMoveToNextAttribute(reader);
|
||||
break;
|
||||
|
||||
case OP_MOVE_TO_ELEMENT:
|
||||
startOp("MoveToElement");
|
||||
xmlTextReaderMoveToElement(reader);
|
||||
break;
|
||||
|
||||
case OP_NORMALIZATION:
|
||||
startOp("Normalization");
|
||||
xmlTextReaderNormalization(reader);
|
||||
break;
|
||||
|
||||
case OP_CONST_ENCODING:
|
||||
startOp("ConstEncoding");
|
||||
xmlTextReaderConstEncoding(reader);
|
||||
break;
|
||||
|
||||
case OP_GET_PARSER_PROP: {
|
||||
int prop = READ_BYTE();
|
||||
|
||||
startOp("GetParserProp");
|
||||
xmlTextReaderGetParserProp(reader, prop);
|
||||
break;
|
||||
}
|
||||
|
||||
case OP_CURRENT_NODE:
|
||||
startOp("CurrentNode");
|
||||
xmlTextReaderCurrentNode(reader);
|
||||
break;
|
||||
|
||||
case OP_GET_PARSER_LINE_NUMBER:
|
||||
startOp("GetParserLineNumber");
|
||||
xmlTextReaderGetParserLineNumber(reader);
|
||||
break;
|
||||
|
||||
case OP_GET_PARSER_COLUMN_NUMBER:
|
||||
startOp("GetParserColumnNumber");
|
||||
xmlTextReaderGetParserColumnNumber(reader);
|
||||
break;
|
||||
|
||||
case OP_PRESERVE:
|
||||
startOp("Preserve");
|
||||
xmlTextReaderPreserve(reader);
|
||||
break;
|
||||
|
||||
case OP_CURRENT_DOC: {
|
||||
xmlDocPtr result;
|
||||
|
||||
startOp("CurrentDoc");
|
||||
result = xmlTextReaderCurrentDoc(reader);
|
||||
if (doc == NULL)
|
||||
doc = result;
|
||||
break;
|
||||
}
|
||||
|
||||
case OP_EXPAND:
|
||||
startOp("Expand");
|
||||
xmlTextReaderExpand(reader);
|
||||
break;
|
||||
|
||||
case OP_NEXT:
|
||||
startOp("Next");
|
||||
xmlTextReaderNext(reader);
|
||||
break;
|
||||
|
||||
case OP_NEXT_SIBLING:
|
||||
startOp("NextSibling");
|
||||
xmlTextReaderNextSibling(reader);
|
||||
break;
|
||||
|
||||
case OP_IS_VALID:
|
||||
startOp("IsValid");
|
||||
xmlTextReaderIsValid(reader);
|
||||
break;
|
||||
|
||||
case OP_CONST_XML_VERSION:
|
||||
startOp("ConstXmlVersion");
|
||||
xmlTextReaderConstXmlVersion(reader);
|
||||
break;
|
||||
|
||||
case OP_STANDALONE:
|
||||
startOp("Standalone");
|
||||
xmlTextReaderStandalone(reader);
|
||||
break;
|
||||
|
||||
case OP_BYTE_CONSUMED:
|
||||
startOp("ByteConsumed");
|
||||
xmlTextReaderByteConsumed(reader);
|
||||
oomReport = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (totalStringSize > docSize * 2)
|
||||
break;
|
||||
}
|
||||
|
||||
error = xmlTextReaderGetLastError(reader);
|
||||
if (error->code == XML_ERR_NO_MEMORY)
|
||||
oomReport = 1;
|
||||
xmlFuzzCheckFailureReport("reader", oomReport, error->code == XML_IO_EIO);
|
||||
|
||||
xmlFreeTextReader(reader);
|
||||
|
||||
if (doc != NULL)
|
||||
xmlFreeDoc(doc);
|
||||
|
||||
exit:
|
||||
xmlFuzzInjectFailure(0);
|
||||
xmlFuzzDataCleanup();
|
||||
xmlResetLastError();
|
||||
return(0);
|
||||
}
|
||||
|
||||
size_t
|
||||
LLVMFuzzerCustomMutator(char *data, size_t size, size_t maxSize,
|
||||
unsigned seed) {
|
||||
static const xmlFuzzChunkDesc chunks[] = {
|
||||
{ 4, XML_FUZZ_PROB_ONE / 10 }, /* opts */
|
||||
{ 4, XML_FUZZ_PROB_ONE / 10 }, /* failurePos */
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
return xmlFuzzMutateChunks(chunks, data, size, maxSize, seed,
|
||||
LLVMFuzzerMutate);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
[libfuzzer]
|
||||
dict = xml.dict
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* regexp.c: a libFuzzer target to test the regexp module.
|
||||
*
|
||||
* See Copyright for the status of this software.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <libxml/xmlregexp.h>
|
||||
#include "fuzz.h"
|
||||
|
||||
int
|
||||
LLVMFuzzerInitialize(int *argc ATTRIBUTE_UNUSED,
|
||||
char ***argv ATTRIBUTE_UNUSED) {
|
||||
xmlFuzzMemSetup();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
LLVMFuzzerTestOneInput(const char *data, size_t size) {
|
||||
xmlRegexpPtr regexp;
|
||||
size_t failurePos;
|
||||
const char *str1;
|
||||
|
||||
if (size > 200)
|
||||
return(0);
|
||||
|
||||
xmlFuzzDataInit(data, size);
|
||||
failurePos = xmlFuzzReadInt(4) % (size * 8 + 100);
|
||||
str1 = xmlFuzzReadString(NULL);
|
||||
|
||||
xmlFuzzInjectFailure(failurePos);
|
||||
regexp = xmlRegexpCompile(BAD_CAST str1);
|
||||
if (xmlFuzzMallocFailed() && regexp != NULL) {
|
||||
fprintf(stderr, "malloc failure not reported\n");
|
||||
abort();
|
||||
}
|
||||
/* xmlRegexpExec has pathological performance in too many cases. */
|
||||
#if 0
|
||||
xmlRegexpExec(regexp, BAD_CAST str2);
|
||||
#endif
|
||||
xmlRegFreeRegexp(regexp);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
quant_any="*"
|
||||
quant_opt="?"
|
||||
quant_some="+"
|
||||
quant_num="{1,2}"
|
||||
|
||||
dot="."
|
||||
branch="|a"
|
||||
parens="()"
|
||||
parens_inner=")("
|
||||
pos_group="[a]"
|
||||
neg_group="[^a]"
|
||||
class_subtraction="[a-[b]]"
|
||||
|
||||
esc_space="\\s"
|
||||
esc_initial="\\i"
|
||||
esc_name="\\c"
|
||||
esc_digit="\\d"
|
||||
esc_word="\\w"
|
||||
|
||||
cat_letter="\\p{L}"
|
||||
cat_mark="\\p{M}"
|
||||
cat_number="\\p{N}"
|
||||
cat_punct="\\p{P}"
|
||||
cat_sym="\\p{S}"
|
||||
cat_sep="\\p{Z}"
|
||||
cat_other="\\p{C}"
|
||||
|
||||
block_aegean_numbers="\\p{IsAegeanNumbers}"
|
||||
block_alphabetic_presentation_forms="\\p{IsAlphabeticPresentationForms}"
|
||||
block_arabic="\\p{IsArabic}"
|
||||
block_arabic_presentation_forms_a="\\p{IsArabicPresentationFormsA}"
|
||||
block_arabic_presentation_forms_b="\\p{IsArabicPresentationFormsB}"
|
||||
block_armenian="\\p{IsArmenian}"
|
||||
block_arrows="\\p{IsArrows}"
|
||||
block_basic_latin="\\p{IsBasicLatin}"
|
||||
block_bengali="\\p{IsBengali}"
|
||||
block_block_elements="\\p{IsBlockElements}"
|
||||
block_bopomofo="\\p{IsBopomofo}"
|
||||
block_bopomofo_extended="\\p{IsBopomofoExtended}"
|
||||
block_box_drawing="\\p{IsBoxDrawing}"
|
||||
block_braille_patterns="\\p{IsBraillePatterns}"
|
||||
block_buhid="\\p{IsBuhid}"
|
||||
block_byzantine_musical_symbols="\\p{IsByzantineMusicalSymbols}"
|
||||
block_c_j_k_compatibility="\\p{IsCJKCompatibility}"
|
||||
block_c_j_k_compatibility_forms="\\p{IsCJKCompatibilityForms}"
|
||||
block_c_j_k_compatibility_ideographs="\\p{IsCJKCompatibilityIdeographs}"
|
||||
block_c_j_k_compatibility_ideographs_supplement="\\p{IsCJKCompatibilityIdeographsSupplement}"
|
||||
block_c_j_k_radicals_supplement="\\p{IsCJKRadicalsSupplement}"
|
||||
block_c_j_k_symbolsand_punctuation="\\p{IsCJKSymbolsandPunctuation}"
|
||||
block_c_j_k_unified_ideographs="\\p{IsCJKUnifiedIdeographs}"
|
||||
block_c_j_k_unified_ideographs_extension_a="\\p{IsCJKUnifiedIdeographsExtensionA}"
|
||||
block_cjk_unified_ideographs_extension_b="\\p{IsCJKUnifiedIdeographsExtensionB}"
|
||||
block_cherokee="\\p{IsCherokee}"
|
||||
block_combining_diacritical_marks="\\p{IsCombiningDiacriticalMarks}"
|
||||
block_combining_diacritical_marksfor_symbols="\\p{IsCombiningDiacriticalMarksforSymbols}"
|
||||
block_combining_half_marks="\\p{IsCombiningHalfMarks}"
|
||||
block_combining_marksfor_symbols="\\p{IsCombiningMarksforSymbols}"
|
||||
block_control_pictures="\\p{IsControlPictures}"
|
||||
block_currency_symbols="\\p{IsCurrencySymbols}"
|
||||
block_cypriot_syllabary="\\p{IsCypriotSyllabary}"
|
||||
block_cyrillic="\\p{IsCyrillic}"
|
||||
block_cyrillic_supplement="\\p{IsCyrillicSupplement}"
|
||||
block_deseret="\\p{IsDeseret}"
|
||||
block_devanagari="\\p{IsDevanagari}"
|
||||
block_dingbats="\\p{IsDingbats}"
|
||||
block_enclosed_alphanumerics="\\p{IsEnclosedAlphanumerics}"
|
||||
block_enclosed_cjk_lettersand_months="\\p{IsEnclosedCJKLettersandMonths}"
|
||||
block_ethiopic="\\p{IsEthiopic}"
|
||||
block_general_punctuation="\\p{IsGeneralPunctuation}"
|
||||
block_geometric_shapes="\\p{IsGeometricShapes}"
|
||||
block_georgian="\\p{IsGeorgian}"
|
||||
block_gothic="\\p{IsGothic}"
|
||||
block_greek="\\p{IsGreek}"
|
||||
block_greek_extended="\\p{IsGreekExtended}"
|
||||
block_greekand_coptic="\\p{IsGreekandCoptic}"
|
||||
block_gujarati="\\p{IsGujarati}"
|
||||
block_gurmukhi="\\p{IsGurmukhi}"
|
||||
block_halfwidthand_fullwidth_forms="\\p{IsHalfwidthandFullwidthForms}"
|
||||
block_hangul_compatibility_jamo="\\p{IsHangulCompatibilityJamo}"
|
||||
block_hangul_jamo="\\p{IsHangulJamo}"
|
||||
block_hangul_syllables="\\p{IsHangulSyllables}"
|
||||
block_hanunoo="\\p{IsHanunoo}"
|
||||
block_hebrew="\\p{IsHebrew}"
|
||||
block_high_private_use_surrogates="\\p{IsHighPrivateUseSurrogates}"
|
||||
block_high_surrogates="\\p{IsHighSurrogates}"
|
||||
block_hiragana="\\p{IsHiragana}"
|
||||
block_ipa_extensions="\\p{IsIPAExtensions}"
|
||||
block_ideographic_description_characters="\\p{IsIdeographicDescriptionCharacters}"
|
||||
block_kanbun="\\p{IsKanbun}"
|
||||
block_kangxi_radicals="\\p{IsKangxiRadicals}"
|
||||
block_kannada="\\p{IsKannada}"
|
||||
block_katakana="\\p{IsKatakana}"
|
||||
block_katakana_phonetic_extensions="\\p{IsKatakanaPhoneticExtensions}"
|
||||
block_khmer="\\p{IsKhmer}"
|
||||
block_khmer_symbols="\\p{IsKhmerSymbols}"
|
||||
block_lao="\\p{IsLao}"
|
||||
block_latin1Supplement="\\p{IsLatin1Supplement}"
|
||||
block_latin_extended_a="\\p{IsLatinExtendedA}"
|
||||
block_latin_extended_b="\\p{IsLatinExtendedB}"
|
||||
block_latin_extended_additional="\\p{IsLatinExtendedAdditional}"
|
||||
block_letterlike_symbols="\\p{IsLetterlikeSymbols}"
|
||||
block_limbu="\\p{IsLimbu}"
|
||||
block_linear_b_ideograms="\\p{IsLinearBIdeograms}"
|
||||
block_linear_b_syllabary="\\p{IsLinearBSyllabary}"
|
||||
block_low_surrogates="\\p{IsLowSurrogates}"
|
||||
block_malayalam="\\p{IsMalayalam}"
|
||||
block_mathematical_alphanumeric_symbols="\\p{IsMathematicalAlphanumericSymbols}"
|
||||
block_mathematical_operators="\\p{IsMathematicalOperators}"
|
||||
block_miscellaneous_mathematical_symbols_a="\\p{IsMiscellaneousMathematicalSymbolsA}"
|
||||
block_miscellaneous_mathematical_symbols_b="\\p{IsMiscellaneousMathematicalSymbolsB}"
|
||||
block_miscellaneous_symbols="\\p{IsMiscellaneousSymbols}"
|
||||
block_miscellaneous_symbolsand_arrows="\\p{IsMiscellaneousSymbolsandArrows}"
|
||||
block_miscellaneous_technical="\\p{IsMiscellaneousTechnical}"
|
||||
block_mongolian="\\p{IsMongolian}"
|
||||
block_musical_symbols="\\p{IsMusicalSymbols}"
|
||||
block_myanmar="\\p{IsMyanmar}"
|
||||
block_number_forms="\\p{IsNumberForms}"
|
||||
block_ogham="\\p{IsOgham}"
|
||||
block_old_italic="\\p{IsOldItalic}"
|
||||
block_optical_character_recognition="\\p{IsOpticalCharacterRecognition}"
|
||||
block_oriya="\\p{IsOriya}"
|
||||
block_osmanya="\\p{IsOsmanya}"
|
||||
block_phonetic_extensions="\\p{IsPhoneticExtensions}"
|
||||
block_private_use="\\p{IsPrivateUse}"
|
||||
block_private_use_area="\\p{IsPrivateUseArea}"
|
||||
block_runic="\\p{IsRunic}"
|
||||
block_shavian="\\p{IsShavian}"
|
||||
block_sinhala="\\p{IsSinhala}"
|
||||
block_small_form_variants="\\p{IsSmallFormVariants}"
|
||||
block_spacing_modifier_letters="\\p{IsSpacingModifierLetters}"
|
||||
block_specials="\\p{IsSpecials}"
|
||||
block_superscriptsand_subscripts="\\p{IsSuperscriptsandSubscripts}"
|
||||
block_supplemental_arrows_a="\\p{IsSupplementalArrowsA}"
|
||||
block_supplemental_arrows_b="\\p{IsSupplementalArrowsB}"
|
||||
block_supplemental_mathematical_operators="\\p{IsSupplementalMathematicalOperators}"
|
||||
block_supplementary_private_use_area_a="\\p{IsSupplementaryPrivateUseAreaA}"
|
||||
block_supplementary_private_use_area_b="\\p{IsSupplementaryPrivateUseAreaB}"
|
||||
block_syriac="\\p{IsSyriac}"
|
||||
block_tagalog="\\p{IsTagalog}"
|
||||
block_tagbanwa="\\p{IsTagbanwa}"
|
||||
block_tags="\\p{IsTags}"
|
||||
block_tai_le="\\p{IsTaiLe}"
|
||||
block_tai_xuan_jing_symbols="\\p{IsTaiXuanJingSymbols}"
|
||||
block_tamil="\\p{IsTamil}"
|
||||
block_telugu="\\p{IsTelugu}"
|
||||
block_thaana="\\p{IsThaana}"
|
||||
block_thai="\\p{IsThai}"
|
||||
block_tibetan="\\p{IsTibetan}"
|
||||
block_ugaritic="\\p{IsUgaritic}"
|
||||
block_unified_canadian_aboriginal_syllabics="\\p{IsUnifiedCanadianAboriginalSyllabics}"
|
||||
block_variation_selectors="\\p{IsVariationSelectors}"
|
||||
block_variation_selectors_supplement="\\p{IsVariationSelectorsSupplement}"
|
||||
block_yi_radicals="\\p{IsYiRadicals}"
|
||||
block_yi_syllables="\\p{IsYiSyllables}"
|
||||
block_yijing_hexagram_symbols="\\p{IsYijingHexagramSymbols}"
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* schema.c: a libFuzzer target to test the XML Schema processor.
|
||||
*
|
||||
* See Copyright for the status of this software.
|
||||
*/
|
||||
|
||||
#ifndef XML_DEPRECATED
|
||||
#define XML_DEPRECATED
|
||||
#endif
|
||||
|
||||
#include <libxml/catalog.h>
|
||||
#include <libxml/xmlschemas.h>
|
||||
#include <libxml/xmlschemastypes.h>
|
||||
#include "fuzz.h"
|
||||
|
||||
int
|
||||
LLVMFuzzerInitialize(int *argc ATTRIBUTE_UNUSED,
|
||||
char ***argv ATTRIBUTE_UNUSED) {
|
||||
xmlFuzzMemSetup();
|
||||
xmlInitParser();
|
||||
#ifdef LIBXML_CATALOG_ENABLED
|
||||
xmlInitializeCatalog();
|
||||
xmlCatalogSetDefaults(XML_CATA_ALLOW_NONE);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
LLVMFuzzerTestOneInput(const char *data, size_t size) {
|
||||
xmlSchemaParserCtxtPtr pctxt;
|
||||
xmlSchemaPtr schema;
|
||||
size_t failurePos;
|
||||
|
||||
if (size > 200000)
|
||||
return(0);
|
||||
|
||||
xmlFuzzDataInit(data, size);
|
||||
|
||||
failurePos = xmlFuzzReadInt(4) % (size + 100);
|
||||
|
||||
xmlFuzzReadEntities();
|
||||
|
||||
xmlFuzzInjectFailure(failurePos);
|
||||
pctxt = xmlSchemaNewParserCtxt(xmlFuzzMainUrl());
|
||||
xmlSchemaSetParserStructuredErrors(pctxt, xmlFuzzSErrorFunc, NULL);
|
||||
xmlSchemaSetResourceLoader(pctxt, xmlFuzzResourceLoader, NULL);
|
||||
schema = xmlSchemaParse(pctxt);
|
||||
xmlSchemaFreeParserCtxt(pctxt);
|
||||
|
||||
if (schema != NULL) {
|
||||
xmlSchemaValidCtxtPtr vctxt;
|
||||
xmlParserCtxtPtr ctxt;
|
||||
xmlDocPtr doc;
|
||||
|
||||
ctxt = xmlNewParserCtxt();
|
||||
xmlCtxtSetErrorHandler(ctxt, xmlFuzzSErrorFunc, NULL);
|
||||
xmlCtxtSetResourceLoader(ctxt, xmlFuzzResourceLoader, NULL);
|
||||
doc = xmlCtxtReadFile(ctxt, xmlFuzzSecondaryUrl(), NULL,
|
||||
XML_PARSE_NOENT);
|
||||
xmlFreeParserCtxt(ctxt);
|
||||
|
||||
vctxt = xmlSchemaNewValidCtxt(schema);
|
||||
xmlSchemaSetValidStructuredErrors(vctxt, xmlFuzzSErrorFunc, NULL);
|
||||
xmlSchemaValidateDoc(vctxt, doc);
|
||||
xmlSchemaFreeValidCtxt(vctxt);
|
||||
|
||||
xmlFreeDoc(doc);
|
||||
xmlSchemaFree(schema);
|
||||
}
|
||||
|
||||
xmlFuzzInjectFailure(0);
|
||||
xmlFuzzDataCleanup();
|
||||
xmlResetLastError();
|
||||
xmlSchemaCleanupTypes();
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
# TODO: Add more language elements
|
||||
|
||||
xs_annotation="<xs:annotation></xs:annotation>"
|
||||
|
||||
xs_attribute="<xs:attribute name='a'></xs:attribute>"
|
||||
xs_attribute_required="<xs:attribute name='a' use='required'></xs:attribute>"
|
||||
xs_element="<xs:element name='e'></xs:element>"
|
||||
|
||||
# Primitive datatypes
|
||||
type_string=" type='xs:string'"
|
||||
type_boolean=" type='xs:boolean'"
|
||||
type_decimal=" type='xs:decimal'"
|
||||
type_float=" type='xs:float'"
|
||||
type_double=" type='xs:double'"
|
||||
type_date_time=" type='xs:dateTime'"
|
||||
type_time=" type='xs:time'"
|
||||
type_date=" type='xs:date'"
|
||||
type_g_year_month=" type='xs:gYearMonth'"
|
||||
type_g_year=" type='xs:gYear'"
|
||||
type_g_month_day=" type='xs:gMonthDay'"
|
||||
type_g_day=" type='xs:gDay'"
|
||||
type_g_month=" type='xs:gMonth'"
|
||||
type_hex_binary=" type='xs:hexBinary'"
|
||||
type_base64_binary=" type='xs:base64Binary'"
|
||||
type_any_uri=" type='xs:anyURI'"
|
||||
type_qname=" type='xs:QName'"
|
||||
type_notation=" type='xs:NOTATION'"
|
||||
|
||||
# Occurs
|
||||
occurs_min=" minOccurs='1'"
|
||||
occurs_max=" maxOccurs='9'"
|
||||
occurs_max_unbounded=" maxOccurs='unbounded'"
|
||||
|
||||
# Simple type
|
||||
xs_restriction_integer="<xs:simpleType><xs:restriction base='xs:integer'></xs:restriction></xs:simpleType>"
|
||||
xs_restriction_string="<xs:simpleType><xs:restriction base='xs:string'></xs:restriction></xs:simpleType>"
|
||||
xs_list="<xs:simpleType><xs:list></xs:list></xs:simpleType>"
|
||||
xs_union="<xs:simpleType><xs:union></xs:union></xs:simpleType>"
|
||||
|
||||
# Restrictions
|
||||
xs_min_exclusive="<xs:minExclusive value='0'/>"
|
||||
xs_min_inclusive="<xs:minInclusive value='0'/>"
|
||||
xs_max_exclusive="<xs:maxExclusive value='9'/>"
|
||||
xs_max_inclusive="<xs:maxInclusive value='9'/>"
|
||||
xs_total_digits="<xs:totalDigits value='3'/>"
|
||||
xs_fraction_digits="<xs:fractionDigits value='3'/>"
|
||||
xs_length="<xs:length value='3'/>"
|
||||
xs_min_length="<xs:minLength value='3'/>"
|
||||
xs_max_length="<xs:maxLength value='3'/>"
|
||||
xs_enumeration="<xs:enumeration value='a'/>"
|
||||
xs_white_space_collapse="<xs:whiteSpace value='collapse'/>"
|
||||
xs_white_space_preserve="<xs:whiteSpace value='preserve'/>"
|
||||
xs_white_space_replace="<xs:whiteSpace value='replace'/>"
|
||||
xs_pattern="<xs:pattern value='a'/>"
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
fuzzer="$1"
|
||||
|
||||
if [ -z "$fuzzer" ]; then
|
||||
echo usage: $0 fuzzer
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Rebuild the project with coverage enabled
|
||||
|
||||
make distclean
|
||||
export CC=clang
|
||||
export CXX=clang++
|
||||
export CFLAGS=" \
|
||||
-O1 -gline-tables-only \
|
||||
-fsanitize=fuzzer-no-link \
|
||||
-fprofile-instr-generate -fcoverage-mapping \
|
||||
-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION"
|
||||
sh autogen.sh --without-python
|
||||
make -j5
|
||||
rm default.profraw
|
||||
|
||||
# Process corpus once
|
||||
|
||||
cd fuzz
|
||||
make $fuzzer
|
||||
|
||||
./$fuzzer -runs=1 corpus/$fuzzer
|
||||
|
||||
# Generate HTML report
|
||||
|
||||
llvm-profdata merge default.profraw -o default.profdata
|
||||
llvm-cov show -format=html -output-dir=report \
|
||||
-instr-profile default.profdata \
|
||||
../.libs/libxml2.so
|
||||
rm default.profraw default.profdata
|
||||
@@ -0,0 +1,21 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
fuzzer="$1"
|
||||
|
||||
if [ -z "$fuzzer" ]; then
|
||||
echo usage: $0 fuzzer
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cd fuzz
|
||||
make $fuzzer
|
||||
|
||||
if [ ! -e corpus/${fuzzer}_ ]; then
|
||||
mv corpus/$fuzzer corpus/${fuzzer}_
|
||||
fi
|
||||
|
||||
mkdir -p corpus/$fuzzer
|
||||
./$fuzzer -merge=1 -use_value_profile=1 corpus/$fuzzer corpus/${fuzzer}_
|
||||
rm -rf corpus/${fuzzer}_
|
||||
@@ -0,0 +1,324 @@
|
||||
/*
|
||||
* testFuzzer.c: Test program for the custom entity loader used to fuzz
|
||||
* with multiple inputs.
|
||||
*
|
||||
* See Copyright for the status of this software.
|
||||
*/
|
||||
|
||||
#ifndef XML_DEPRECATED
|
||||
#define XML_DEPRECATED
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
#include <glob.h>
|
||||
#include <libxml/parser.h>
|
||||
#include <libxml/tree.h>
|
||||
#include <libxml/xmlstring.h>
|
||||
#include "fuzz.h"
|
||||
|
||||
size_t
|
||||
LLVMFuzzerMutate(char *data, size_t size, size_t maxSize) {
|
||||
(void) data;
|
||||
(void) maxSize;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
#ifdef HAVE_HTML_FUZZER
|
||||
int fuzzHtmlInit(int *argc, char ***argv);
|
||||
int fuzzHtml(const char *data, size_t size);
|
||||
size_t fuzzHtmlMutate(char *data, size_t size, size_t maxSize,
|
||||
unsigned seed);
|
||||
#define LLVMFuzzerInitialize fuzzHtmlInit
|
||||
#define LLVMFuzzerTestOneInput fuzzHtml
|
||||
#define LLVMFuzzerCustomMutator fuzzHtmlMutate
|
||||
#include "html.c"
|
||||
#undef LLVMFuzzerInitialize
|
||||
#undef LLVMFuzzerTestOneInput
|
||||
#undef LLVMFuzzerCustomMutator
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_READER_FUZZER
|
||||
int fuzzReaderInit(int *argc, char ***argv);
|
||||
int fuzzReader(const char *data, size_t size);
|
||||
size_t fuzzReaderMutate(char *data, size_t size, size_t maxSize,
|
||||
unsigned seed);
|
||||
#define LLVMFuzzerInitialize fuzzReaderInit
|
||||
#define LLVMFuzzerTestOneInput fuzzReader
|
||||
#define LLVMFuzzerCustomMutator fuzzReaderMutate
|
||||
#include "reader.c"
|
||||
#undef LLVMFuzzerInitialize
|
||||
#undef LLVMFuzzerTestOneInput
|
||||
#undef LLVMFuzzerCustomMutator
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_REGEXP_FUZZER
|
||||
int fuzzRegexpInit(int *argc, char ***argv);
|
||||
int fuzzRegexp(const char *data, size_t size);
|
||||
size_t fuzzRegexpMutate(char *data, size_t size, size_t maxSize,
|
||||
unsigned seed);
|
||||
#define LLVMFuzzerInitialize fuzzRegexpInit
|
||||
#define LLVMFuzzerTestOneInput fuzzRegexp
|
||||
#define LLVMFuzzerCustomMutator fuzzRegexpMutate
|
||||
#include "regexp.c"
|
||||
#undef LLVMFuzzerInitialize
|
||||
#undef LLVMFuzzerTestOneInput
|
||||
#undef LLVMFuzzerCustomMutator
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SCHEMA_FUZZER
|
||||
int fuzzSchemaInit(int *argc, char ***argv);
|
||||
int fuzzSchema(const char *data, size_t size);
|
||||
size_t fuzzSchemaMutate(char *data, size_t size, size_t maxSize,
|
||||
unsigned seed);
|
||||
#define LLVMFuzzerInitialize fuzzSchemaInit
|
||||
#define LLVMFuzzerTestOneInput fuzzSchema
|
||||
#define LLVMFuzzerCustomMutator fuzzSchemaMutate
|
||||
#include "schema.c"
|
||||
#undef LLVMFuzzerInitialize
|
||||
#undef LLVMFuzzerTestOneInput
|
||||
#undef LLVMFuzzerCustomMutator
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_URI_FUZZER
|
||||
int fuzzUriInit(int *argc, char ***argv);
|
||||
int fuzzUri(const char *data, size_t size);
|
||||
size_t fuzzUriMutate(char *data, size_t size, size_t maxSize,
|
||||
unsigned seed);
|
||||
#define LLVMFuzzerInitialize fuzzUriInit
|
||||
#define LLVMFuzzerTestOneInput fuzzUri
|
||||
#define LLVMFuzzerCustomMutator fuzzUriMutate
|
||||
#include "uri.c"
|
||||
#undef LLVMFuzzerInitialize
|
||||
#undef LLVMFuzzerTestOneInput
|
||||
#undef LLVMFuzzerCustomMutator
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_VALID_FUZZER
|
||||
int fuzzValidInit(int *argc, char ***argv);
|
||||
int fuzzValid(const char *data, size_t size);
|
||||
size_t fuzzValidMutate(char *data, size_t size, size_t maxSize,
|
||||
unsigned seed);
|
||||
#define LLVMFuzzerInitialize fuzzValidInit
|
||||
#define LLVMFuzzerTestOneInput fuzzValid
|
||||
#define LLVMFuzzerCustomMutator fuzzValidMutate
|
||||
#include "valid.c"
|
||||
#undef LLVMFuzzerInitialize
|
||||
#undef LLVMFuzzerTestOneInput
|
||||
#undef LLVMFuzzerCustomMutator
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_XINCLUDE_FUZZER
|
||||
int fuzzXIncludeInit(int *argc, char ***argv);
|
||||
int fuzzXInclude(const char *data, size_t size);
|
||||
size_t fuzzXIncludeMutate(char *data, size_t size, size_t maxSize,
|
||||
unsigned seed);
|
||||
#define LLVMFuzzerInitialize fuzzXIncludeInit
|
||||
#define LLVMFuzzerTestOneInput fuzzXInclude
|
||||
#define LLVMFuzzerCustomMutator fuzzXIncludeMutate
|
||||
#include "xinclude.c"
|
||||
#undef LLVMFuzzerInitialize
|
||||
#undef LLVMFuzzerTestOneInput
|
||||
#undef LLVMFuzzerCustomMutator
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_XML_FUZZER
|
||||
int fuzzXmlInit(int *argc, char ***argv);
|
||||
int fuzzXml(const char *data, size_t size);
|
||||
size_t fuzzXmlMutate(char *data, size_t size, size_t maxSize,
|
||||
unsigned seed);
|
||||
#define LLVMFuzzerInitialize fuzzXmlInit
|
||||
#define LLVMFuzzerTestOneInput fuzzXml
|
||||
#define LLVMFuzzerCustomMutator fuzzXmlMutate
|
||||
#include "xml.c"
|
||||
#undef LLVMFuzzerInitialize
|
||||
#undef LLVMFuzzerTestOneInput
|
||||
#undef LLVMFuzzerCustomMutator
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_XPATH_FUZZER
|
||||
int fuzzXPathInit(int *argc, char ***argv);
|
||||
int fuzzXPath(const char *data, size_t size);
|
||||
size_t fuzzXPathMutate(char *data, size_t size, size_t maxSize,
|
||||
unsigned seed);
|
||||
#define LLVMFuzzerInitialize fuzzXPathInit
|
||||
#define LLVMFuzzerTestOneInput fuzzXPath
|
||||
#define LLVMFuzzerCustomMutator fuzzXPathMutate
|
||||
#include "xpath.c"
|
||||
#undef LLVMFuzzerInitialize
|
||||
#undef LLVMFuzzerTestOneInput
|
||||
#undef LLVMFuzzerCustomMutator
|
||||
#endif
|
||||
|
||||
typedef int
|
||||
(*initFunc)(int *argc, char ***argv);
|
||||
typedef int
|
||||
(*fuzzFunc)(const char *data, size_t size);
|
||||
typedef size_t
|
||||
(*mutateFunc)(char *data, size_t size, size_t maxSize, unsigned seed);
|
||||
|
||||
int numInputs;
|
||||
|
||||
static int
|
||||
testFuzzer(initFunc init, fuzzFunc fuzz, mutateFunc mutate,
|
||||
const char *pattern) {
|
||||
glob_t globbuf;
|
||||
int ret = -1;
|
||||
size_t i;
|
||||
|
||||
(void) mutate;
|
||||
|
||||
if (glob(pattern, 0, NULL, &globbuf) != 0) {
|
||||
fprintf(stderr, "pattern %s matches no files\n", pattern);
|
||||
return(-1);
|
||||
}
|
||||
|
||||
if (init != NULL)
|
||||
init(NULL, NULL);
|
||||
|
||||
for (i = 0; i < globbuf.gl_pathc; i++) {
|
||||
const char *path = globbuf.gl_pathv[i];
|
||||
char *data;
|
||||
size_t size;
|
||||
|
||||
data = xmlSlurpFile(path, &size);
|
||||
if (data == NULL) {
|
||||
fprintf(stderr, "couldn't read %s\n", path);
|
||||
goto error;
|
||||
}
|
||||
fuzz(data, size);
|
||||
xmlFree(data);
|
||||
|
||||
numInputs++;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
error:
|
||||
globfree(&globbuf);
|
||||
return(ret);
|
||||
}
|
||||
|
||||
#ifdef HAVE_XML_FUZZER
|
||||
static int
|
||||
testEntityLoader(void) {
|
||||
xmlParserCtxtPtr ctxt;
|
||||
static const char data[] =
|
||||
"doc.xml\\\n"
|
||||
"<!DOCTYPE doc SYSTEM \"doc.dtd\">\n"
|
||||
"<doc>&ent;</doc>\\\n"
|
||||
"doc.dtd\\\n"
|
||||
"<!ELEMENT doc (#PCDATA)>\n"
|
||||
"<!ENTITY ent SYSTEM \"ent.txt\">\\\n"
|
||||
"ent.txt\\\n"
|
||||
"Hello, world!\\\n";
|
||||
const char *docBuffer, *url;
|
||||
size_t docSize;
|
||||
xmlDocPtr doc;
|
||||
int ret = 0;
|
||||
|
||||
xmlFuzzDataInit(data, sizeof(data) - 1);
|
||||
xmlFuzzReadEntities();
|
||||
|
||||
url = xmlFuzzMainUrl();
|
||||
if (strcmp(url, "doc.xml") != 0) {
|
||||
fprintf(stderr, "unexpected main url: %s\n", url);
|
||||
ret = 1;
|
||||
}
|
||||
|
||||
url = xmlFuzzSecondaryUrl();
|
||||
if (strcmp(url, "doc.dtd") != 0) {
|
||||
fprintf(stderr, "unexpected secondary url: %s\n", url);
|
||||
ret = 1;
|
||||
}
|
||||
|
||||
docBuffer = xmlFuzzMainEntity(&docSize);
|
||||
ctxt = xmlNewParserCtxt();
|
||||
xmlCtxtSetResourceLoader(ctxt, xmlFuzzResourceLoader, NULL);
|
||||
doc = xmlCtxtReadMemory(ctxt, docBuffer, docSize, NULL, NULL,
|
||||
XML_PARSE_NOENT | XML_PARSE_DTDLOAD);
|
||||
xmlFreeParserCtxt(ctxt);
|
||||
|
||||
#ifdef LIBXML_OUTPUT_ENABLED
|
||||
{
|
||||
static xmlChar expected[] =
|
||||
"<?xml version=\"1.0\"?>\n"
|
||||
"<!DOCTYPE doc SYSTEM \"doc.dtd\">\n"
|
||||
"<doc>Hello, world!</doc>\n";
|
||||
xmlChar *out;
|
||||
|
||||
xmlDocDumpMemory(doc, &out, NULL);
|
||||
if (xmlStrcmp(out, expected) != 0) {
|
||||
fprintf(stderr, "Expected:\n%sGot:\n%s", expected, out);
|
||||
ret = 1;
|
||||
}
|
||||
xmlFree(out);
|
||||
}
|
||||
#endif
|
||||
|
||||
xmlFreeDoc(doc);
|
||||
xmlFuzzDataCleanup();
|
||||
|
||||
return(ret);
|
||||
}
|
||||
#endif
|
||||
|
||||
int
|
||||
main(void) {
|
||||
int ret = 0;
|
||||
|
||||
#ifdef HAVE_XML_FUZZER
|
||||
if (testEntityLoader() != 0)
|
||||
ret = 1;
|
||||
#endif
|
||||
#ifdef HAVE_HTML_FUZZER
|
||||
if (testFuzzer(fuzzHtmlInit, fuzzHtml, fuzzHtmlMutate,
|
||||
"seed/html/*") != 0)
|
||||
ret = 1;
|
||||
#endif
|
||||
#ifdef HAVE_READER_FUZZER
|
||||
if (testFuzzer(fuzzReaderInit, fuzzReader, fuzzReaderMutate,
|
||||
"seed/reader/*") != 0)
|
||||
ret = 1;
|
||||
#endif
|
||||
#ifdef HAVE_REGEXP_FUZZER
|
||||
if (testFuzzer(fuzzRegexpInit, fuzzRegexp, fuzzRegexpMutate,
|
||||
"seed/regexp/*") != 0)
|
||||
ret = 1;
|
||||
#endif
|
||||
#ifdef HAVE_SCHEMA_FUZZER
|
||||
if (testFuzzer(fuzzSchemaInit, fuzzSchema, fuzzSchemaMutate,
|
||||
"seed/schema/*") != 0)
|
||||
ret = 1;
|
||||
#endif
|
||||
#ifdef HAVE_URI_FUZZER
|
||||
if (testFuzzer(fuzzUriInit, fuzzUri, fuzzUriMutate,
|
||||
"seed/uri/*") != 0)
|
||||
ret = 1;
|
||||
#endif
|
||||
#ifdef HAVE_VALID_FUZZER
|
||||
if (testFuzzer(fuzzValidInit, fuzzValid, fuzzValidMutate,
|
||||
"seed/valid/*") != 0)
|
||||
ret = 1;
|
||||
#endif
|
||||
#ifdef HAVE_XINCLUDE_FUZZER
|
||||
if (testFuzzer(fuzzXIncludeInit, fuzzXInclude, fuzzXIncludeMutate,
|
||||
"seed/xinclude/*") != 0)
|
||||
ret = 1;
|
||||
#endif
|
||||
#ifdef HAVE_XML_FUZZER
|
||||
if (testFuzzer(fuzzXmlInit, fuzzXml, fuzzXmlMutate,
|
||||
"seed/xml/*") != 0)
|
||||
ret = 1;
|
||||
#endif
|
||||
#ifdef HAVE_XPATH_FUZZER
|
||||
if (testFuzzer(fuzzXPathInit, fuzzXPath, fuzzXPathMutate,
|
||||
"seed/xpath/*") != 0)
|
||||
ret = 1;
|
||||
#endif
|
||||
|
||||
if (ret == 0)
|
||||
printf("Successfully tested %d inputs\n", numInputs);
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* uri.c: a libFuzzer target to test the URI module.
|
||||
*
|
||||
* See Copyright for the status of this software.
|
||||
*/
|
||||
|
||||
#include <libxml/uri.h>
|
||||
#include "fuzz.h"
|
||||
|
||||
int
|
||||
LLVMFuzzerInitialize(int *argc ATTRIBUTE_UNUSED,
|
||||
char ***argv ATTRIBUTE_UNUSED) {
|
||||
xmlFuzzMemSetup();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
LLVMFuzzerTestOneInput(const char *data, size_t size) {
|
||||
xmlURIPtr uri;
|
||||
size_t failurePos;
|
||||
const char *str1, *str2;
|
||||
char *copy;
|
||||
xmlChar *strRes;
|
||||
int intRes;
|
||||
|
||||
if (size > 10000)
|
||||
return(0);
|
||||
|
||||
xmlFuzzDataInit(data, size);
|
||||
failurePos = xmlFuzzReadInt(4) % (size * 8 + 100);
|
||||
str1 = xmlFuzzReadString(NULL);
|
||||
str2 = xmlFuzzReadString(NULL);
|
||||
|
||||
xmlFuzzInjectFailure(failurePos);
|
||||
|
||||
xmlFuzzResetFailure();
|
||||
intRes = xmlParseURISafe(str1, &uri);
|
||||
xmlFuzzCheckFailureReport("xmlParseURISafe", intRes == -1, 0);
|
||||
|
||||
if (uri != NULL) {
|
||||
xmlFuzzResetFailure();
|
||||
strRes = xmlSaveUri(uri);
|
||||
xmlFuzzCheckFailureReport("xmlSaveURI", strRes == NULL, 0);
|
||||
xmlFree(strRes);
|
||||
xmlFreeURI(uri);
|
||||
}
|
||||
|
||||
xmlFreeURI(xmlParseURI(str1));
|
||||
|
||||
uri = xmlParseURIRaw(str1, 1);
|
||||
xmlFree(xmlSaveUri(uri));
|
||||
xmlFreeURI(uri);
|
||||
|
||||
xmlFuzzResetFailure();
|
||||
strRes = BAD_CAST xmlURIUnescapeString(str1, -1, NULL);
|
||||
xmlFuzzCheckFailureReport("xmlURIUnescapeString",
|
||||
str1 != NULL && strRes == NULL, 0);
|
||||
xmlFree(strRes);
|
||||
|
||||
xmlFree(xmlURIEscape(BAD_CAST str1));
|
||||
|
||||
xmlFuzzResetFailure();
|
||||
strRes = xmlCanonicPath(BAD_CAST str1);
|
||||
xmlFuzzCheckFailureReport("xmlCanonicPath",
|
||||
str1 != NULL && strRes == NULL, 0);
|
||||
xmlFree(strRes);
|
||||
|
||||
xmlFuzzResetFailure();
|
||||
strRes = xmlPathToURI(BAD_CAST str1);
|
||||
xmlFuzzCheckFailureReport("xmlPathToURI",
|
||||
str1 != NULL && strRes == NULL, 0);
|
||||
xmlFree(strRes);
|
||||
|
||||
xmlFuzzResetFailure();
|
||||
intRes = xmlBuildURISafe(BAD_CAST str2, BAD_CAST str1, &strRes);
|
||||
xmlFuzzCheckFailureReport("xmlBuildURISafe", intRes == -1, 0);
|
||||
xmlFree(strRes);
|
||||
|
||||
xmlFree(xmlBuildURI(BAD_CAST str2, BAD_CAST str1));
|
||||
|
||||
xmlFuzzResetFailure();
|
||||
intRes = xmlBuildRelativeURISafe(BAD_CAST str2, BAD_CAST str1, &strRes);
|
||||
xmlFuzzCheckFailureReport("xmlBuildRelativeURISafe", intRes == -1, 0);
|
||||
xmlFree(strRes);
|
||||
|
||||
xmlFree(xmlBuildRelativeURI(BAD_CAST str2, BAD_CAST str1));
|
||||
|
||||
xmlFuzzResetFailure();
|
||||
strRes = xmlURIEscapeStr(BAD_CAST str1, BAD_CAST str2);
|
||||
xmlFuzzCheckFailureReport("xmlURIEscapeStr",
|
||||
str1 != NULL && strRes == NULL, 0);
|
||||
xmlFree(strRes);
|
||||
|
||||
copy = (char *) xmlCharStrdup(str1);
|
||||
xmlNormalizeURIPath(copy);
|
||||
xmlFree(copy);
|
||||
|
||||
xmlFuzzInjectFailure(0);
|
||||
xmlFuzzDataCleanup();
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* valid.c: a libFuzzer target to test DTD validation.
|
||||
*
|
||||
* See Copyright for the status of this software.
|
||||
*/
|
||||
|
||||
#include <libxml/catalog.h>
|
||||
#include <libxml/parser.h>
|
||||
#include <libxml/tree.h>
|
||||
#include <libxml/xmlerror.h>
|
||||
#include "fuzz.h"
|
||||
|
||||
int
|
||||
LLVMFuzzerInitialize(int *argc ATTRIBUTE_UNUSED,
|
||||
char ***argv ATTRIBUTE_UNUSED) {
|
||||
xmlFuzzMemSetup();
|
||||
xmlInitParser();
|
||||
#ifdef LIBXML_CATALOG_ENABLED
|
||||
xmlInitializeCatalog();
|
||||
xmlCatalogSetDefaults(XML_CATA_ALLOW_NONE);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
LLVMFuzzerTestOneInput(const char *data, size_t size) {
|
||||
xmlParserCtxtPtr ctxt;
|
||||
xmlDocPtr doc;
|
||||
const char *docBuffer, *docUrl;
|
||||
size_t failurePos, docSize;
|
||||
int opts;
|
||||
|
||||
xmlFuzzDataInit(data, size);
|
||||
opts = (int) xmlFuzzReadInt(4);
|
||||
opts |= XML_PARSE_DTDVALID;
|
||||
failurePos = xmlFuzzReadInt(4) % (size + 100);
|
||||
|
||||
xmlFuzzReadEntities();
|
||||
docBuffer = xmlFuzzMainEntity(&docSize);
|
||||
docUrl = xmlFuzzMainUrl();
|
||||
if (docBuffer == NULL)
|
||||
goto exit;
|
||||
|
||||
/* Pull parser */
|
||||
|
||||
xmlFuzzInjectFailure(failurePos);
|
||||
ctxt = xmlNewParserCtxt();
|
||||
if (ctxt != NULL) {
|
||||
xmlCtxtSetErrorHandler(ctxt, xmlFuzzSErrorFunc, NULL);
|
||||
xmlCtxtSetResourceLoader(ctxt, xmlFuzzResourceLoader, NULL);
|
||||
doc = xmlCtxtReadMemory(ctxt, docBuffer, docSize, docUrl, NULL, opts);
|
||||
xmlFuzzCheckFailureReport("xmlCtxtReadMemory",
|
||||
ctxt->errNo == XML_ERR_NO_MEMORY,
|
||||
ctxt->errNo == XML_IO_EIO);
|
||||
xmlFreeDoc(doc);
|
||||
xmlFreeParserCtxt(ctxt);
|
||||
}
|
||||
|
||||
/* Post validation */
|
||||
|
||||
xmlFuzzInjectFailure(failurePos);
|
||||
ctxt = xmlNewParserCtxt();
|
||||
if (ctxt != NULL) {
|
||||
xmlCtxtSetErrorHandler(ctxt, xmlFuzzSErrorFunc, NULL);
|
||||
xmlCtxtSetResourceLoader(ctxt, xmlFuzzResourceLoader, NULL);
|
||||
doc = xmlCtxtReadMemory(ctxt, docBuffer, docSize, docUrl, NULL,
|
||||
opts & ~XML_PARSE_DTDVALID);
|
||||
xmlFuzzCheckFailureReport("xmlCtxtReadMemory",
|
||||
doc == NULL && ctxt->errNo == XML_ERR_NO_MEMORY,
|
||||
doc == NULL && ctxt->errNo == XML_IO_EIO);
|
||||
if (doc != NULL) {
|
||||
int valid = xmlCtxtValidateDocument(ctxt, doc);
|
||||
|
||||
xmlFuzzCheckFailureReport("xmlCtxtValidateDocument",
|
||||
!valid && ctxt->errNo == XML_ERR_NO_MEMORY,
|
||||
!valid && ctxt->errNo == XML_IO_EIO);
|
||||
}
|
||||
xmlFreeDoc(doc);
|
||||
xmlFreeParserCtxt(ctxt);
|
||||
}
|
||||
|
||||
/* Push parser */
|
||||
|
||||
#ifdef LIBXML_PUSH_ENABLED
|
||||
{
|
||||
static const size_t maxChunkSize = 128;
|
||||
size_t consumed, chunkSize;
|
||||
|
||||
xmlFuzzInjectFailure(failurePos);
|
||||
/*
|
||||
* FIXME: xmlCreatePushParserCtxt can still report OOM errors
|
||||
* to stderr.
|
||||
*/
|
||||
xmlSetGenericErrorFunc(NULL, xmlFuzzErrorFunc);
|
||||
ctxt = xmlCreatePushParserCtxt(NULL, NULL, NULL, 0, docUrl);
|
||||
xmlSetGenericErrorFunc(NULL, NULL);
|
||||
if (ctxt != NULL) {
|
||||
xmlCtxtSetErrorHandler(ctxt, xmlFuzzSErrorFunc, NULL);
|
||||
xmlCtxtSetResourceLoader(ctxt, xmlFuzzResourceLoader, NULL);
|
||||
xmlCtxtUseOptions(ctxt, opts);
|
||||
|
||||
for (consumed = 0; consumed < docSize; consumed += chunkSize) {
|
||||
chunkSize = docSize - consumed;
|
||||
if (chunkSize > maxChunkSize)
|
||||
chunkSize = maxChunkSize;
|
||||
xmlParseChunk(ctxt, docBuffer + consumed, chunkSize, 0);
|
||||
}
|
||||
|
||||
xmlParseChunk(ctxt, NULL, 0, 1);
|
||||
xmlFuzzCheckFailureReport("xmlParseChunk",
|
||||
ctxt->errNo == XML_ERR_NO_MEMORY,
|
||||
ctxt->errNo == XML_IO_EIO);
|
||||
xmlFreeDoc(ctxt->myDoc);
|
||||
xmlFreeParserCtxt(ctxt);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
exit:
|
||||
xmlFuzzInjectFailure(0);
|
||||
xmlFuzzDataCleanup();
|
||||
xmlResetLastError();
|
||||
return(0);
|
||||
}
|
||||
|
||||
size_t
|
||||
LLVMFuzzerCustomMutator(char *data, size_t size, size_t maxSize,
|
||||
unsigned seed) {
|
||||
static const xmlFuzzChunkDesc chunks[] = {
|
||||
{ 4, XML_FUZZ_PROB_ONE / 10 }, /* opts */
|
||||
{ 4, XML_FUZZ_PROB_ONE / 10 }, /* failurePos */
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
return xmlFuzzMutateChunks(chunks, data, size, maxSize, seed,
|
||||
LLVMFuzzerMutate);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
[libfuzzer]
|
||||
dict = xml.dict
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* xinclude.c: a libFuzzer target to test the XInclude engine.
|
||||
*
|
||||
* See Copyright for the status of this software.
|
||||
*/
|
||||
|
||||
#include <libxml/catalog.h>
|
||||
#include <libxml/parser.h>
|
||||
#include <libxml/tree.h>
|
||||
#include <libxml/xmlerror.h>
|
||||
#include <libxml/xinclude.h>
|
||||
#include "fuzz.h"
|
||||
|
||||
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) {
|
||||
xmlParserCtxtPtr ctxt;
|
||||
xmlDocPtr doc;
|
||||
const char *docBuffer, *docUrl;
|
||||
size_t failurePos, docSize;
|
||||
int opts;
|
||||
|
||||
xmlFuzzDataInit(data, size);
|
||||
opts = (int) xmlFuzzReadInt(4);
|
||||
opts &= ~XML_PARSE_DTDVALID &
|
||||
~XML_PARSE_SAX1;
|
||||
failurePos = xmlFuzzReadInt(4) % (size + 100);
|
||||
|
||||
xmlFuzzReadEntities();
|
||||
docBuffer = xmlFuzzMainEntity(&docSize);
|
||||
docUrl = xmlFuzzMainUrl();
|
||||
if (docBuffer == NULL)
|
||||
goto exit;
|
||||
|
||||
/* Pull parser */
|
||||
|
||||
xmlFuzzInjectFailure(failurePos);
|
||||
ctxt = xmlNewParserCtxt();
|
||||
if (ctxt != NULL) {
|
||||
xmlXIncludeCtxtPtr xinc;
|
||||
xmlDocPtr copy;
|
||||
|
||||
xmlCtxtSetResourceLoader(ctxt, xmlFuzzResourceLoader, NULL);
|
||||
|
||||
doc = xmlCtxtReadMemory(ctxt, docBuffer, docSize, docUrl, NULL, opts);
|
||||
xmlFuzzCheckFailureReport("xmlCtxtReadMemory",
|
||||
doc == NULL && ctxt->errNo == XML_ERR_NO_MEMORY,
|
||||
doc == NULL && ctxt->errNo == XML_IO_EIO);
|
||||
|
||||
xinc = xmlXIncludeNewContext(doc);
|
||||
xmlXIncludeSetResourceLoader(xinc, xmlFuzzResourceLoader, NULL);
|
||||
xmlXIncludeSetFlags(xinc, opts);
|
||||
xmlXIncludeProcessNode(xinc, (xmlNodePtr) doc);
|
||||
if (doc != NULL) {
|
||||
xmlFuzzCheckFailureReport("xmlXIncludeProcessNode",
|
||||
xinc == NULL ||
|
||||
xmlXIncludeGetLastError(xinc) == XML_ERR_NO_MEMORY,
|
||||
xinc != NULL &&
|
||||
xmlXIncludeGetLastError(xinc) == XML_IO_EIO);
|
||||
}
|
||||
xmlXIncludeFreeContext(xinc);
|
||||
|
||||
xmlFuzzResetFailure();
|
||||
copy = xmlCopyDoc(doc, 1);
|
||||
if (doc != NULL)
|
||||
xmlFuzzCheckFailureReport("xmlCopyNode", copy == NULL, 0);
|
||||
xmlFreeDoc(copy);
|
||||
|
||||
xmlFreeDoc(doc);
|
||||
xmlFreeParserCtxt(ctxt);
|
||||
}
|
||||
|
||||
exit:
|
||||
xmlFuzzInjectFailure(0);
|
||||
xmlFuzzDataCleanup();
|
||||
xmlResetLastError();
|
||||
return(0);
|
||||
}
|
||||
|
||||
size_t
|
||||
LLVMFuzzerCustomMutator(char *data, size_t size, size_t maxSize,
|
||||
unsigned seed) {
|
||||
static const xmlFuzzChunkDesc chunks[] = {
|
||||
{ 4, XML_FUZZ_PROB_ONE / 10 }, /* opts */
|
||||
{ 4, XML_FUZZ_PROB_ONE / 10 }, /* failurePos */
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
return xmlFuzzMutateChunks(chunks, data, size, maxSize, seed,
|
||||
LLVMFuzzerMutate);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
[libfuzzer]
|
||||
dict = lint.dict
|
||||
@@ -0,0 +1,263 @@
|
||||
/*
|
||||
* xml.c: a libFuzzer target to test several XML parser interfaces.
|
||||
*
|
||||
* See Copyright for the status of this software.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <libxml/catalog.h>
|
||||
#include <libxml/parser.h>
|
||||
#include <libxml/tree.h>
|
||||
#include <libxml/xmlerror.h>
|
||||
#include <libxml/xmlsave.h>
|
||||
#include "fuzz.h"
|
||||
|
||||
int
|
||||
LLVMFuzzerInitialize(int *argc ATTRIBUTE_UNUSED,
|
||||
char ***argv ATTRIBUTE_UNUSED) {
|
||||
xmlFuzzMemSetup();
|
||||
xmlInitParser();
|
||||
#ifdef LIBXML_CATALOG_ENABLED
|
||||
xmlInitializeCatalog();
|
||||
xmlCatalogSetDefaults(XML_CATA_ALLOW_NONE);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
LLVMFuzzerTestOneInput(const char *data, size_t size) {
|
||||
xmlParserCtxtPtr ctxt;
|
||||
xmlDocPtr doc;
|
||||
const char *docBuffer, *docUrl;
|
||||
size_t failurePos, docSize, maxChunkSize;
|
||||
int opts;
|
||||
int errorCode;
|
||||
#ifdef LIBXML_OUTPUT_ENABLED
|
||||
xmlBufferPtr outbuf = NULL;
|
||||
const char *saveEncoding;
|
||||
int saveOpts;
|
||||
#endif
|
||||
|
||||
xmlFuzzDataInit(data, size);
|
||||
opts = (int) xmlFuzzReadInt(4);
|
||||
/*
|
||||
* Disable options that are known to cause timeouts
|
||||
*/
|
||||
opts &= ~XML_PARSE_DTDVALID &
|
||||
~XML_PARSE_SAX1;
|
||||
failurePos = xmlFuzzReadInt(4) % (size + 100);
|
||||
|
||||
maxChunkSize = xmlFuzzReadInt(4) % (size + size / 8 + 1);
|
||||
if (maxChunkSize == 0)
|
||||
maxChunkSize = 1;
|
||||
|
||||
#ifdef LIBXML_OUTPUT_ENABLED
|
||||
/* TODO: Take from fuzz data */
|
||||
saveOpts = 0;
|
||||
saveEncoding = NULL;
|
||||
#endif
|
||||
|
||||
xmlFuzzReadEntities();
|
||||
docBuffer = xmlFuzzMainEntity(&docSize);
|
||||
docUrl = xmlFuzzMainUrl();
|
||||
if (docBuffer == NULL)
|
||||
goto exit;
|
||||
|
||||
/* Pull parser */
|
||||
|
||||
xmlFuzzInjectFailure(failurePos);
|
||||
ctxt = xmlNewParserCtxt();
|
||||
if (ctxt == NULL) {
|
||||
errorCode = XML_ERR_NO_MEMORY;
|
||||
} else {
|
||||
xmlCtxtSetErrorHandler(ctxt, xmlFuzzSErrorFunc, NULL);
|
||||
xmlCtxtSetResourceLoader(ctxt, xmlFuzzResourceLoader, NULL);
|
||||
doc = xmlCtxtReadMemory(ctxt, docBuffer, docSize, docUrl, NULL, opts);
|
||||
errorCode = ctxt->errNo;
|
||||
xmlFuzzCheckFailureReport("xmlCtxtReadMemory",
|
||||
doc == NULL && errorCode == XML_ERR_NO_MEMORY,
|
||||
doc == NULL && errorCode == XML_IO_EIO);
|
||||
|
||||
if (doc != NULL) {
|
||||
#ifdef LIBXML_OUTPUT_ENABLED
|
||||
xmlSaveCtxtPtr save;
|
||||
|
||||
outbuf = xmlBufferCreate();
|
||||
|
||||
/* Also test the serializer. */
|
||||
save = xmlSaveToBuffer(outbuf, saveEncoding, saveOpts);
|
||||
|
||||
if (save == NULL) {
|
||||
xmlBufferFree(outbuf);
|
||||
outbuf = NULL;
|
||||
} else {
|
||||
int saveErr;
|
||||
|
||||
xmlSaveDoc(save, doc);
|
||||
saveErr = xmlSaveFinish(save);
|
||||
xmlFuzzCheckFailureReport("xmlSaveToBuffer",
|
||||
saveErr == XML_ERR_NO_MEMORY,
|
||||
saveErr == XML_IO_EIO);
|
||||
if (saveErr != XML_ERR_OK) {
|
||||
xmlBufferFree(outbuf);
|
||||
outbuf = NULL;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
xmlFreeDoc(doc);
|
||||
}
|
||||
|
||||
xmlFreeParserCtxt(ctxt);
|
||||
}
|
||||
|
||||
/* Push parser */
|
||||
|
||||
#ifdef LIBXML_PUSH_ENABLED
|
||||
xmlFuzzInjectFailure(failurePos);
|
||||
/*
|
||||
* FIXME: xmlCreatePushParserCtxt can still report OOM errors
|
||||
* to stderr.
|
||||
*/
|
||||
xmlSetGenericErrorFunc(NULL, xmlFuzzErrorFunc);
|
||||
ctxt = xmlCreatePushParserCtxt(NULL, NULL, NULL, 0, docUrl);
|
||||
xmlSetGenericErrorFunc(NULL, NULL);
|
||||
|
||||
if (ctxt != NULL) {
|
||||
size_t consumed;
|
||||
int errorCodePush, numChunks, maxChunks;
|
||||
|
||||
xmlCtxtSetErrorHandler(ctxt, xmlFuzzSErrorFunc, NULL);
|
||||
xmlCtxtSetResourceLoader(ctxt, xmlFuzzResourceLoader, NULL);
|
||||
xmlCtxtUseOptions(ctxt, opts);
|
||||
|
||||
consumed = 0;
|
||||
numChunks = 0;
|
||||
maxChunks = 50 + docSize / 100;
|
||||
while (numChunks == 0 ||
|
||||
(consumed < docSize && numChunks < maxChunks)) {
|
||||
size_t chunkSize;
|
||||
int terminate;
|
||||
|
||||
numChunks += 1;
|
||||
chunkSize = docSize - consumed;
|
||||
|
||||
if (numChunks < maxChunks && chunkSize > maxChunkSize) {
|
||||
chunkSize = maxChunkSize;
|
||||
terminate = 0;
|
||||
} else {
|
||||
terminate = 1;
|
||||
}
|
||||
|
||||
xmlParseChunk(ctxt, docBuffer + consumed, chunkSize, terminate);
|
||||
consumed += chunkSize;
|
||||
}
|
||||
|
||||
errorCodePush = ctxt->errNo;
|
||||
xmlFuzzCheckFailureReport("xmlParseChunk",
|
||||
errorCodePush == XML_ERR_NO_MEMORY,
|
||||
errorCodePush == XML_IO_EIO);
|
||||
doc = ctxt->myDoc;
|
||||
|
||||
/*
|
||||
* Push and pull parser differ in when exactly they
|
||||
* stop parsing, and the error code is the *last* error
|
||||
* reported, so we can't check whether the codes match.
|
||||
*/
|
||||
if (errorCode != XML_ERR_NO_MEMORY &&
|
||||
errorCode != XML_IO_EIO &&
|
||||
errorCodePush != XML_ERR_NO_MEMORY &&
|
||||
errorCodePush != XML_IO_EIO &&
|
||||
(errorCode == XML_ERR_OK) != (errorCodePush == XML_ERR_OK)) {
|
||||
fprintf(stderr, "pull/push parser error mismatch: %d != %d\n",
|
||||
errorCode, errorCodePush);
|
||||
#if 0
|
||||
FILE *f = fopen("c.xml", "wb");
|
||||
fwrite(docBuffer, docSize, 1, f);
|
||||
fclose(f);
|
||||
#endif
|
||||
abort();
|
||||
}
|
||||
|
||||
#ifdef LIBXML_OUTPUT_ENABLED
|
||||
/*
|
||||
* Verify that pull and push parser produce the same result.
|
||||
*
|
||||
* The NOBLANKS option doesn't work reliably in push mode.
|
||||
*/
|
||||
if ((opts & XML_PARSE_NOBLANKS) == 0 &&
|
||||
errorCode == XML_ERR_OK &&
|
||||
errorCodePush == XML_ERR_OK &&
|
||||
outbuf != NULL) {
|
||||
xmlBufferPtr outbufPush;
|
||||
xmlSaveCtxtPtr save;
|
||||
|
||||
outbufPush = xmlBufferCreate();
|
||||
|
||||
save = xmlSaveToBuffer(outbufPush, saveEncoding, saveOpts);
|
||||
|
||||
if (save != NULL) {
|
||||
int saveErr;
|
||||
|
||||
xmlSaveDoc(save, doc);
|
||||
saveErr = xmlSaveFinish(save);
|
||||
|
||||
if (saveErr == XML_ERR_OK) {
|
||||
int outbufSize = xmlBufferLength(outbuf);
|
||||
|
||||
if (outbufSize != xmlBufferLength(outbufPush) ||
|
||||
memcmp(xmlBufferContent(outbuf),
|
||||
xmlBufferContent(outbufPush),
|
||||
outbufSize) != 0) {
|
||||
fprintf(stderr, "pull/push parser roundtrip "
|
||||
"mismatch\n");
|
||||
#if 0
|
||||
FILE *f = fopen("c.xml", "wb");
|
||||
fwrite(docBuffer, docSize, 1, f);
|
||||
fclose(f);
|
||||
fprintf(stderr, "opts: %X\n", opts);
|
||||
fprintf(stderr, "---\n%s\n---\n%s\n---\n",
|
||||
xmlBufferContent(outbuf),
|
||||
xmlBufferContent(outbufPush));
|
||||
#endif
|
||||
abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
xmlBufferFree(outbufPush);
|
||||
}
|
||||
#endif
|
||||
|
||||
xmlFreeDoc(doc);
|
||||
xmlFreeParserCtxt(ctxt);
|
||||
}
|
||||
#endif
|
||||
|
||||
exit:
|
||||
#ifdef LIBXML_OUTPUT_ENABLED
|
||||
xmlBufferFree(outbuf);
|
||||
#endif
|
||||
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 }, /* opts */
|
||||
{ 4, XML_FUZZ_PROB_ONE / 10 }, /* failurePos */
|
||||
{ 4, XML_FUZZ_PROB_ONE / 10 }, /* maxChunkSize */
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
return xmlFuzzMutateChunks(chunks, data, size, maxSize, seed,
|
||||
LLVMFuzzerMutate);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
xml_decl="<?xml version='1.0'?>"
|
||||
xml_decl_standalone="<?xml version='1.0' standalone='1'?>"
|
||||
xml_decl_latin1="<?xml version='1.0' encoding='ISO-8859-1'?>"
|
||||
|
||||
elem_start_end="<a></a>"
|
||||
elem_empty="<a/>"
|
||||
elem_ns_start_end="<a:a xmlns:a='a'></a:a>"
|
||||
elem_ns_empty="<a:a xmlns:a='a'/>"
|
||||
|
||||
attr=" a='a'"
|
||||
|
||||
ns_decl=" xmlns:a='a'"
|
||||
ns_default=" xmlns='a'"
|
||||
ns_prefix="a:"
|
||||
|
||||
cdata_section="<![CDATA[ ]]>"
|
||||
|
||||
comment="<!-- -->"
|
||||
|
||||
pi="<?a?>"
|
||||
|
||||
doctype="<!DOCTYPE a []>"
|
||||
doctype_system="<!DOCTYPE a SYSTEM 'a' []>"
|
||||
doctype_system="<!DOCTYPE a PUBLIC 'a' 'a' []>"
|
||||
|
||||
elem_decl_any="<!ELEMENT a ANY>"
|
||||
elem_decl_empty="<!ELEMENT a EMPTY>"
|
||||
elem_decl_children="<!ELEMENT a (a)>"
|
||||
elem_decl_mixed="<!ELEMENT a (#PCDATA|a)>"
|
||||
elem_children_choice="|a"
|
||||
elem_children_seq=",a"
|
||||
elem_children_sub_choice="|(a)"
|
||||
elem_children_sub_seq=",(a)"
|
||||
elem_quant_any="*"
|
||||
elem_quant_opt="?"
|
||||
elem_quant_some="+"
|
||||
|
||||
attr_decl_cdata_req="<!ATTLIST a a CDATA #REQUIRED>"
|
||||
attr_decl_cdata_imp="<!ATTLIST a a CDATA #IMPLIED>"
|
||||
attr_decl_cdata_def="<!ATTLIST a a CDATA 'a'>"
|
||||
attr_decl_cdata_fix="<!ATTLIST a a CDATA #FIXED 'a'>"
|
||||
attr_decl_id_imp="<!ATTLIST a a ID #IMPLIED>"
|
||||
attr_decl_id_req="<!ATTLIST a a ID #REQUIRED>"
|
||||
attr_decl_id_def="<!ATTLIST a a ID 'a'>"
|
||||
attr_decl_id_fix="<!ATTLIST a a ID #FIXED 'a'>"
|
||||
attr_decl_idref_imp="<!ATTLIST a a IDREF #IMPLIED>"
|
||||
attr_decl_idref_req="<!ATTLIST a a IDREF #REQUIRED>"
|
||||
attr_decl_idref_def="<!ATTLIST a a IDREF 'a'>"
|
||||
attr_decl_idref_fix="<!ATTLIST a a IDREF #FIXED 'a'>"
|
||||
attr_decl_idrefs_imp="<!ATTLIST a a IDREFS #IMPLIED>"
|
||||
attr_decl_idrefs_req="<!ATTLIST a a IDREFS #REQUIRED>"
|
||||
attr_decl_idrefs_def="<!ATTLIST a a IDREFS 'a'>"
|
||||
attr_decl_idrefs_fix="<!ATTLIST a a IDREFS #FIXED 'a'>"
|
||||
attr_decl_entity_imp="<!ATTLIST a a ENTITY #IMPLIED>"
|
||||
attr_decl_entity_req="<!ATTLIST a a ENTITY #REQUIRED>"
|
||||
attr_decl_entity_def="<!ATTLIST a a ENTITY 'a'>"
|
||||
attr_decl_entity_fix="<!ATTLIST a a ENTITY #FIXED 'a'>"
|
||||
attr_decl_entities_imp="<!ATTLIST a a ENTITIES #IMPLIED>"
|
||||
attr_decl_entities_req="<!ATTLIST a a ENTITIES #REQUIRED>"
|
||||
attr_decl_entities_def="<!ATTLIST a a ENTITIES 'a'>"
|
||||
attr_decl_entities_fix="<!ATTLIST a a ENTITIES #FIXED 'a'>"
|
||||
attr_decl_nmtoken_imp="<!ATTLIST a a NMTOKEN #IMPLIED>"
|
||||
attr_decl_nmtoken_req="<!ATTLIST a a NMTOKEN #REQUIRED>"
|
||||
attr_decl_nmtoken_def="<!ATTLIST a a NMTOKEN 'a'>"
|
||||
attr_decl_nmtoken_fix="<!ATTLIST a a NMTOKEN #FIXED 'a'>"
|
||||
attr_decl_nmtokens_imp="<!ATTLIST a a NMTOKENS #IMPLIED>"
|
||||
attr_decl_nmtokens_req="<!ATTLIST a a NMTOKENS #REQUIRED>"
|
||||
attr_decl_nmtokens_def="<!ATTLIST a a NMTOKENS 'a'>"
|
||||
attr_decl_nmtokens_fix="<!ATTLIST a a NMTOKENS #FIXED 'a'>"
|
||||
attr_decl_enum_imp="<!ATTLIST a a (a) #IMPLIED>"
|
||||
attr_decl_enum_req="<!ATTLIST a a (a) #REQUIRED>"
|
||||
attr_decl_enum_def="<!ATTLIST a a (a) 'a'>"
|
||||
attr_decl_enum_fix="<!ATTLIST a a (a) #FIXED 'a'>"
|
||||
attr_decl_notation_imp="<!ATTLIST a a NOTATION (a) #IMPLIED>"
|
||||
attr_decl_notation_req="<!ATTLIST a a NOTATION (a) #REQUIRED>"
|
||||
attr_decl_notation_def="<!ATTLIST a a NOTATION (a) 'a'>"
|
||||
attr_decl_notation_fix="<!ATTLIST a a NOTATION (a) #FIXED 'a'>"
|
||||
|
||||
include_sect="<![INCLUDE[ ]]>"
|
||||
ignore_sect="<![IGNORE[ ]]>"
|
||||
|
||||
ge_decl="<!ENTITY a 'a'>"
|
||||
ge_decl_system="<!ENTITY a SYSTEM 'a'>"
|
||||
ge_decl_system_ndata="<!ENTITY a SYSTEM 'a' NDATA a>"
|
||||
ge_decl_public="<!ENTITY a PUBLIC 'a' 'a'>"
|
||||
ge_decl_public_ndata="<!ENTITY a PUBLIC 'a' 'a' NDATA a>"
|
||||
|
||||
pe_decl="<!ENTITY % a 'a'>"
|
||||
pe_decl_system="<!ENTITY % a SYSTEM 'a'>"
|
||||
pe_decl_public="<!ENTITY % a PUBLIC 'a' 'a'>"
|
||||
|
||||
char_ref_dec="<"
|
||||
char_ref_hex="<"
|
||||
char_ref_quoted="&#60;"
|
||||
|
||||
ge_ref_lt="<"
|
||||
ge_ref_gt=">"
|
||||
ge_ref_amp="&"
|
||||
ge_ref_apos="'"
|
||||
ge_ref_quot="""
|
||||
ge_ref="&a;"
|
||||
ge_ref_quoted="&a;"
|
||||
|
||||
pe_ref="%a;"
|
||||
pe_ref_quoted="%a;"
|
||||
|
||||
notation_decl_public="<!NOTATION a PUBLIC 'a'>"
|
||||
notation_decl_system="<!NOTATION a SYSTEM 'a'>"
|
||||
|
||||
cs_utf8="UTF-8"
|
||||
cs_utf16="UTF-16"
|
||||
cs_utf16le="UTF-16LE"
|
||||
cs_utf16be="UTF-16BE"
|
||||
cs_ucs2="UCS-2"
|
||||
cs_ucs4="UCS-4"
|
||||
cs_latin1="ISO-8859-1"
|
||||
cs_ascii="ASCII"
|
||||
cs_ebcdic="EBCDIC"
|
||||
cs_iso2022jp="ISO-2022-JP"
|
||||
cs_shift_jis="SHIFT_JIS"
|
||||
cs_euc_jp="EUC-JP"
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* xpath.c: a libFuzzer target to test XPath and XPointer expressions.
|
||||
*
|
||||
* See Copyright for the status of this software.
|
||||
*/
|
||||
|
||||
#include <libxml/catalog.h>
|
||||
#include <libxml/parser.h>
|
||||
#include <libxml/xpointer.h>
|
||||
#include "fuzz.h"
|
||||
|
||||
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) {
|
||||
xmlDocPtr doc;
|
||||
const char *expr, *xml;
|
||||
size_t failurePos, exprSize, xmlSize;
|
||||
|
||||
if (size > 10000)
|
||||
return(0);
|
||||
|
||||
xmlFuzzDataInit(data, size);
|
||||
|
||||
failurePos = xmlFuzzReadInt(4) % (size + 100);
|
||||
expr = xmlFuzzReadString(&exprSize);
|
||||
xml = xmlFuzzReadString(&xmlSize);
|
||||
|
||||
/* Recovery mode allows more input to be fuzzed. */
|
||||
doc = xmlReadMemory(xml, xmlSize, NULL, NULL, XML_PARSE_RECOVER);
|
||||
if (doc != NULL) {
|
||||
xmlXPathContextPtr xpctxt;
|
||||
|
||||
xmlFuzzInjectFailure(failurePos);
|
||||
|
||||
xpctxt = xmlXPathNewContext(doc);
|
||||
if (xpctxt != NULL) {
|
||||
int res;
|
||||
|
||||
/* Operation limit to avoid timeout */
|
||||
xpctxt->opLimit = 500000;
|
||||
|
||||
res = xmlXPathContextSetCache(xpctxt, 1, 4, 0);
|
||||
xmlFuzzCheckFailureReport("xmlXPathContextSetCache", res == -1, 0);
|
||||
|
||||
xmlFuzzResetFailure();
|
||||
xmlXPathFreeObject(xmlXPtrEval(BAD_CAST expr, xpctxt));
|
||||
xmlFuzzCheckFailureReport("xmlXPtrEval",
|
||||
xpctxt->lastError.code == XML_ERR_NO_MEMORY, 0);
|
||||
xmlXPathFreeContext(xpctxt);
|
||||
}
|
||||
|
||||
xmlFuzzInjectFailure(0);
|
||||
xmlFreeDoc(doc);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
# XML
|
||||
|
||||
elem_a="<a></a>"
|
||||
elem_b="<b></b>"
|
||||
elem_c="<c></c>"
|
||||
elem_d="<d></d>"
|
||||
elem_empty="<a/>"
|
||||
elem_ns_a="<a:a xmlns:a='a'></a:a>"
|
||||
elem_ns_b="<b:b xmlns:b='b'></b:b>"
|
||||
|
||||
attr_a=" a='a'"
|
||||
attr_b=" b='b'"
|
||||
|
||||
ns_decl=" xmlns:a='a'"
|
||||
ns_default=" xmlns='a'"
|
||||
ns_prefix_a="a:"
|
||||
ns_prefix_b="b:"
|
||||
|
||||
cdata_section="<![CDATA[ ]]>"
|
||||
|
||||
comment="<!-- -->"
|
||||
|
||||
pi="<?a?>"
|
||||
|
||||
# XPath
|
||||
|
||||
axis_ancestor="ancestor::"
|
||||
axis_ancestor_or_self="ancestor-or-self::"
|
||||
axis_attribute="attribute::"
|
||||
axis_attribute_abbrev="@"
|
||||
axis_child="child::"
|
||||
axis_descendant="descendant::"
|
||||
axis_descendant_or_self="descendant-or-self::"
|
||||
axis_following="following::"
|
||||
axis_following_sibling="following-sibling::"
|
||||
axis_namespace="namespace::"
|
||||
axis_parent="parent::"
|
||||
axis_preceding="preceding::"
|
||||
axis_preceding_siblings="preceding-sibling::"
|
||||
axis_self="self::"
|
||||
|
||||
node_test_ns="a:"
|
||||
|
||||
val_num="=(1.0)"
|
||||
val_str_sq="=('a')"
|
||||
val_str_dq="=(\"a\")"
|
||||
val_node_set="=(*)"
|
||||
val_elem="=(b)"
|
||||
|
||||
step_root="/"
|
||||
step_descendant="//"
|
||||
step_any="//*"
|
||||
step_any_l="*//"
|
||||
step_elem="//b"
|
||||
step_ns_elem="//a:a"
|
||||
step_comment="//comment()"
|
||||
step_node="//node()"
|
||||
step_node_l="node()//"
|
||||
step_pi="//processing-instruction()"
|
||||
step_text="//text()"
|
||||
step_parent="../"
|
||||
|
||||
op_plus="+1"
|
||||
op_minus=" - 1"
|
||||
op_neg="-"
|
||||
op_mul="*1"
|
||||
op_div=" div 1"
|
||||
op_mod=" mod 1"
|
||||
op_and=" and 1"
|
||||
op_or=" or 1"
|
||||
op_ne="!=1"
|
||||
op_lt="<1"
|
||||
op_gt=">1"
|
||||
op_le="<=1"
|
||||
op_ge=">=1"
|
||||
op_predicate_num="[1]"
|
||||
op_predicate_last="[last()]"
|
||||
op_predicate_str="['a']"
|
||||
op_predicate="[1=1]"
|
||||
op_arg_num=",1"
|
||||
op_arg_str=",'a'"
|
||||
op_arg_node=",*"
|
||||
op_union="|//b"
|
||||
|
||||
var_num="=$f"
|
||||
var_bool="=$b"
|
||||
var_str="=$s"
|
||||
var_node_set="=$n"
|
||||
|
||||
# Unicode
|
||||
|
||||
utf8_2="\xC3\x84"
|
||||
utf8_3="\xE2\x80\x9C"
|
||||
utf8_4="\xF0\x9F\x98\x80"
|
||||
Reference in New Issue
Block a user