/src/libxml2/fuzz/valid.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * valid.c: a libFuzzer target to test DTD validation. |
3 | | * |
4 | | * See Copyright for the status of this software. |
5 | | */ |
6 | | |
7 | | #include <libxml/catalog.h> |
8 | | #include <libxml/parser.h> |
9 | | #include <libxml/tree.h> |
10 | | #include <libxml/xmlerror.h> |
11 | | #include "fuzz.h" |
12 | | |
13 | | int |
14 | | LLVMFuzzerInitialize(int *argc ATTRIBUTE_UNUSED, |
15 | 20 | char ***argv ATTRIBUTE_UNUSED) { |
16 | 20 | xmlFuzzMemSetup(); |
17 | 20 | xmlInitParser(); |
18 | 20 | #ifdef LIBXML_CATALOG_ENABLED |
19 | 20 | xmlInitializeCatalog(); |
20 | 20 | xmlCatalogSetDefaults(XML_CATA_ALLOW_NONE); |
21 | 20 | #endif |
22 | | |
23 | 20 | return 0; |
24 | 20 | } |
25 | | |
26 | | int |
27 | 27.1k | LLVMFuzzerTestOneInput(const char *data, size_t size) { |
28 | 27.1k | xmlParserCtxtPtr ctxt; |
29 | 27.1k | xmlDocPtr doc; |
30 | 27.1k | const char *docBuffer, *docUrl; |
31 | 27.1k | size_t failurePos, docSize; |
32 | 27.1k | int opts; |
33 | | |
34 | 27.1k | xmlFuzzDataInit(data, size); |
35 | 27.1k | opts = (int) xmlFuzzReadInt(4); |
36 | 27.1k | opts |= XML_PARSE_DTDVALID; |
37 | 27.1k | failurePos = xmlFuzzReadInt(4) % (size + 100); |
38 | | |
39 | 27.1k | xmlFuzzReadEntities(); |
40 | 27.1k | docBuffer = xmlFuzzMainEntity(&docSize); |
41 | 27.1k | docUrl = xmlFuzzMainUrl(); |
42 | 27.1k | if (docBuffer == NULL) |
43 | 82 | goto exit; |
44 | | |
45 | | /* Pull parser */ |
46 | | |
47 | 27.0k | xmlFuzzInjectFailure(failurePos); |
48 | 27.0k | ctxt = xmlNewParserCtxt(); |
49 | 27.0k | if (ctxt != NULL) { |
50 | 27.0k | xmlCtxtSetErrorHandler(ctxt, xmlFuzzSErrorFunc, NULL); |
51 | 27.0k | xmlCtxtSetResourceLoader(ctxt, xmlFuzzResourceLoader, NULL); |
52 | 27.0k | doc = xmlCtxtReadMemory(ctxt, docBuffer, docSize, docUrl, NULL, opts); |
53 | 27.0k | xmlFuzzCheckFailureReport("xmlCtxtReadMemory", |
54 | 27.0k | ctxt->errNo == XML_ERR_NO_MEMORY, |
55 | 27.0k | ctxt->errNo == XML_IO_EIO); |
56 | 27.0k | xmlFreeDoc(doc); |
57 | 27.0k | xmlFreeParserCtxt(ctxt); |
58 | 27.0k | } |
59 | | |
60 | | /* Post validation */ |
61 | | |
62 | 27.0k | xmlFuzzInjectFailure(failurePos); |
63 | 27.0k | ctxt = xmlNewParserCtxt(); |
64 | 27.0k | if (ctxt != NULL) { |
65 | 27.0k | xmlCtxtSetErrorHandler(ctxt, xmlFuzzSErrorFunc, NULL); |
66 | 27.0k | xmlCtxtSetResourceLoader(ctxt, xmlFuzzResourceLoader, NULL); |
67 | 27.0k | doc = xmlCtxtReadMemory(ctxt, docBuffer, docSize, docUrl, NULL, |
68 | 27.0k | opts & ~XML_PARSE_DTDVALID); |
69 | 27.0k | xmlFuzzCheckFailureReport("xmlCtxtReadMemory", |
70 | 27.0k | doc == NULL && ctxt->errNo == XML_ERR_NO_MEMORY, |
71 | 27.0k | doc == NULL && ctxt->errNo == XML_IO_EIO); |
72 | 27.0k | if (doc != NULL) { |
73 | 14.8k | int valid = xmlCtxtValidateDocument(ctxt, doc); |
74 | | |
75 | 14.8k | xmlFuzzCheckFailureReport("xmlCtxtValidateDocument", |
76 | 14.8k | !valid && ctxt->errNo == XML_ERR_NO_MEMORY, |
77 | 14.8k | !valid && ctxt->errNo == XML_IO_EIO); |
78 | 14.8k | } |
79 | 27.0k | xmlFreeDoc(doc); |
80 | 27.0k | xmlFreeParserCtxt(ctxt); |
81 | 27.0k | } |
82 | | |
83 | | /* Push parser */ |
84 | | |
85 | 27.0k | #ifdef LIBXML_PUSH_ENABLED |
86 | 27.0k | { |
87 | 27.0k | static const size_t maxChunkSize = 128; |
88 | 27.0k | size_t consumed, chunkSize; |
89 | | |
90 | 27.0k | xmlFuzzInjectFailure(failurePos); |
91 | | /* |
92 | | * FIXME: xmlCreatePushParserCtxt can still report OOM errors |
93 | | * to stderr. |
94 | | */ |
95 | 27.0k | xmlSetGenericErrorFunc(NULL, xmlFuzzErrorFunc); |
96 | 27.0k | ctxt = xmlCreatePushParserCtxt(NULL, NULL, NULL, 0, docUrl); |
97 | 27.0k | xmlSetGenericErrorFunc(NULL, NULL); |
98 | 27.0k | if (ctxt != NULL) { |
99 | 27.0k | xmlCtxtSetErrorHandler(ctxt, xmlFuzzSErrorFunc, NULL); |
100 | 27.0k | xmlCtxtSetResourceLoader(ctxt, xmlFuzzResourceLoader, NULL); |
101 | 27.0k | xmlCtxtUseOptions(ctxt, opts); |
102 | | |
103 | 1.48M | for (consumed = 0; consumed < docSize; consumed += chunkSize) { |
104 | 1.45M | chunkSize = docSize - consumed; |
105 | 1.45M | if (chunkSize > maxChunkSize) |
106 | 1.43M | chunkSize = maxChunkSize; |
107 | 1.45M | xmlParseChunk(ctxt, docBuffer + consumed, chunkSize, 0); |
108 | 1.45M | } |
109 | | |
110 | 27.0k | xmlParseChunk(ctxt, NULL, 0, 1); |
111 | 27.0k | xmlFuzzCheckFailureReport("xmlParseChunk", |
112 | 27.0k | ctxt->errNo == XML_ERR_NO_MEMORY, |
113 | 27.0k | ctxt->errNo == XML_IO_EIO); |
114 | 27.0k | xmlFreeDoc(ctxt->myDoc); |
115 | 27.0k | xmlFreeParserCtxt(ctxt); |
116 | 27.0k | } |
117 | 27.0k | } |
118 | 27.0k | #endif |
119 | | |
120 | 27.1k | exit: |
121 | 27.1k | xmlFuzzInjectFailure(0); |
122 | 27.1k | xmlFuzzDataCleanup(); |
123 | 27.1k | xmlResetLastError(); |
124 | 27.1k | return(0); |
125 | 27.0k | } |
126 | | |
127 | | size_t |
128 | | LLVMFuzzerCustomMutator(char *data, size_t size, size_t maxSize, |
129 | 0 | unsigned seed) { |
130 | 0 | static const xmlFuzzChunkDesc chunks[] = { |
131 | 0 | { 4, XML_FUZZ_PROB_ONE / 10 }, /* opts */ |
132 | 0 | { 4, XML_FUZZ_PROB_ONE / 10 }, /* failurePos */ |
133 | 0 | { 0, 0 } |
134 | 0 | }; |
135 | |
|
136 | 0 | return xmlFuzzMutateChunks(chunks, data, size, maxSize, seed, |
137 | 0 | LLVMFuzzerMutate); |
138 | 0 | } |
139 | | |