|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#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); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
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; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#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; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
|
|
|
|
|
|
|
|
|
|
|
#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 }, |
|
|
{ 4, XML_FUZZ_PROB_ONE / 10 }, |
|
|
{ 0, 0 } |
|
|
}; |
|
|
|
|
|
return xmlFuzzMutateChunks(chunks, data, size, maxSize, seed, |
|
|
LLVMFuzzerMutate); |
|
|
} |
|
|
|
|
|
|