|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#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); |
|
|
|
|
|
|
|
|
|
|
|
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 |
|
|
|
|
|
saveOpts = 0; |
|
|
saveEncoding = NULL; |
|
|
#endif |
|
|
|
|
|
xmlFuzzReadEntities(); |
|
|
docBuffer = xmlFuzzMainEntity(&docSize); |
|
|
docUrl = xmlFuzzMainUrl(); |
|
|
if (docBuffer == NULL) |
|
|
goto exit; |
|
|
|
|
|
|
|
|
|
|
|
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(); |
|
|
|
|
|
|
|
|
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); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
#ifdef LIBXML_PUSH_ENABLED |
|
|
xmlFuzzInjectFailure(failurePos); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 }, |
|
|
{ 4, XML_FUZZ_PROB_ONE / 10 }, |
|
|
{ 4, XML_FUZZ_PROB_ONE / 10 }, |
|
|
{ 0, 0 } |
|
|
}; |
|
|
|
|
|
return xmlFuzzMutateChunks(chunks, data, size, maxSize, seed, |
|
|
LLVMFuzzerMutate); |
|
|
} |
|
|
|
|
|
|