1
0
mirror of https://gitlab.gnome.org/GNOME/libxml2.git synced 2025-03-27 18:50:07 +03:00

fuzz: Mutate fuzz data chunks separately

Implement a custom mutator that takes a list of fixed-size chunks which
are mutated with a given probability. This makes sure that values like
parser options or failure position are mutated regularly even as the
fuzz data grows large. Values can also be adjusted temporarily to make
the fuzzer focus on failure injection, for example.

Thanks to David Kilzer for the idea.
This commit is contained in:
Nick Wellnhofer 2024-12-11 16:24:23 +01:00
parent f5257d92bf
commit c6c6d8afef
14 changed files with 303 additions and 10 deletions

View File

@ -3594,3 +3594,15 @@ LLVMFuzzerTestOneInput(const char *data, size_t size) {
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);
}

View File

@ -529,3 +529,74 @@ xmlFuzzOutputClose(void *ctxt ATTRIBUTE_UNUSED) {
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;
}

View File

@ -52,12 +52,29 @@ extern "C" {
#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, ...);
@ -131,6 +148,11 @@ 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

View File

@ -221,3 +221,16 @@ LLVMFuzzerTestOneInput(const char *data, size_t size) {
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);
}

View File

@ -233,3 +233,19 @@ exit:
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);
}

View File

@ -553,3 +553,16 @@ exit:
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);
}

View File

@ -49,3 +49,15 @@ LLVMFuzzerTestOneInput(const char *data, size_t size) {
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);
}

View File

@ -77,3 +77,15 @@ LLVMFuzzerTestOneInput(const char *data, size_t size) {
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);
}

View File

@ -16,109 +16,158 @@
#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, const char *pattern) {
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);
@ -222,39 +271,48 @@ main(void) {
ret = 1;
#endif
#ifdef HAVE_HTML_FUZZER
if (testFuzzer(fuzzHtmlInit, fuzzHtml, "seed/html/*") != 0)
if (testFuzzer(fuzzHtmlInit, fuzzHtml, fuzzHtmlMutate,
"seed/html/*") != 0)
ret = 1;
#endif
#ifdef HAVE_READER_FUZZER
if (testFuzzer(fuzzReaderInit, fuzzReader, "seed/reader/*") != 0)
if (testFuzzer(fuzzReaderInit, fuzzReader, fuzzReaderMutate,
"seed/reader/*") != 0)
ret = 1;
#endif
#ifdef HAVE_REGEXP_FUZZER
if (testFuzzer(fuzzRegexpInit, fuzzRegexp, "seed/regexp/*") != 0)
if (testFuzzer(fuzzRegexpInit, fuzzRegexp, fuzzRegexpMutate,
"seed/regexp/*") != 0)
ret = 1;
#endif
#ifdef HAVE_SCHEMA_FUZZER
if (testFuzzer(fuzzSchemaInit, fuzzSchema, "seed/schema/*") != 0)
if (testFuzzer(fuzzSchemaInit, fuzzSchema, fuzzSchemaMutate,
"seed/schema/*") != 0)
ret = 1;
#endif
#ifdef HAVE_URI_FUZZER
if (testFuzzer(fuzzUriInit, fuzzUri, "seed/uri/*") != 0)
if (testFuzzer(fuzzUriInit, fuzzUri, fuzzUriMutate,
"seed/uri/*") != 0)
ret = 1;
#endif
#ifdef HAVE_VALID_FUZZER
if (testFuzzer(fuzzValidInit, fuzzValid, "seed/valid/*") != 0)
if (testFuzzer(fuzzValidInit, fuzzValid, fuzzValidMutate,
"seed/valid/*") != 0)
ret = 1;
#endif
#ifdef HAVE_XINCLUDE_FUZZER
if (testFuzzer(fuzzXIncludeInit, fuzzXInclude, "seed/xinclude/*") != 0)
if (testFuzzer(fuzzXIncludeInit, fuzzXInclude, fuzzXIncludeMutate,
"seed/xinclude/*") != 0)
ret = 1;
#endif
#ifdef HAVE_XML_FUZZER
if (testFuzzer(fuzzXmlInit, fuzzXml, "seed/xml/*") != 0)
if (testFuzzer(fuzzXmlInit, fuzzXml, fuzzXmlMutate,
"seed/xml/*") != 0)
ret = 1;
#endif
#ifdef HAVE_XPATH_FUZZER
if (testFuzzer(fuzzXPathInit, fuzzXPath, "seed/xpath/*") != 0)
if (testFuzzer(fuzzXPathInit, fuzzXPath, fuzzXPathMutate,
"seed/xpath/*") != 0)
ret = 1;
#endif

View File

@ -102,3 +102,15 @@ LLVMFuzzerTestOneInput(const char *data, size_t size) {
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);
}

View File

@ -125,3 +125,16 @@ exit:
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);
}

View File

@ -89,3 +89,16 @@ exit:
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);
}

View File

@ -248,3 +248,17 @@ exit:
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);
}

View File

@ -72,3 +72,15 @@ LLVMFuzzerTestOneInput(const char *data, size_t size) {
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);
}