1 /*
2 * xml.c: a libFuzzer target to test several XML parser interfaces.
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 <libxml/xmlreader.h>
12 #include <libxml/xmlsave.h>
13 #include "fuzz.h"
14
15 #include <string.h>
16
17 #if 0
18 #define DEBUG
19 #endif
20
21 typedef enum {
22 OP_READ = 1,
23 OP_READ_INNER_XML,
24 OP_READ_OUTER_XML,
25 OP_READ_STRING,
26 OP_READ_ATTRIBUTE_VALUE,
27 OP_ATTRIBUTE_COUNT,
28 OP_DEPTH,
29 OP_HAS_ATTRIBUTES,
30 OP_HAS_VALUE,
31 OP_IS_DEFAULT,
32 OP_IS_EMPTY_ELEMENT,
33 OP_NODE_TYPE,
34 OP_QUOTE_CHAR,
35 OP_READ_STATE,
36 OP_IS_NAMESPACE_DECL,
37 OP_CONST_BASE_URI,
38 OP_CONST_LOCAL_NAME,
39 OP_CONST_NAME,
40 OP_CONST_NAMESPACE_URI,
41 OP_CONST_PREFIX,
42 OP_CONST_XML_LANG,
43 OP_CONST_VALUE,
44 OP_BASE_URI,
45 OP_LOCAL_NAME,
46 OP_NAME,
47 OP_NAMESPACE_URI,
48 OP_PREFIX,
49 OP_XML_LANG,
50 OP_VALUE,
51 OP_CLOSE,
52 OP_GET_ATTRIBUTE_NO,
53 OP_GET_ATTRIBUTE,
54 OP_GET_ATTRIBUTE_NS,
55 OP_GET_REMAINDER,
56 OP_LOOKUP_NAMESPACE,
57 OP_MOVE_TO_ATTRIBUTE_NO,
58 OP_MOVE_TO_ATTRIBUTE,
59 OP_MOVE_TO_ATTRIBUTE_NS,
60 OP_MOVE_TO_FIRST_ATTRIBUTE,
61 OP_MOVE_TO_NEXT_ATTRIBUTE,
62 OP_MOVE_TO_ELEMENT,
63 OP_NORMALIZATION,
64 OP_CONST_ENCODING,
65 OP_GET_PARSER_PROP,
66 OP_CURRENT_NODE,
67 OP_GET_PARSER_LINE_NUMBER,
68 OP_GET_PARSER_COLUMN_NUMBER,
69 OP_PRESERVE,
70 OP_CURRENT_DOC,
71 OP_EXPAND,
72 OP_NEXT,
73 OP_NEXT_SIBLING,
74 OP_IS_VALID,
75 OP_CONST_XML_VERSION,
76 OP_STANDALONE,
77 OP_BYTE_CONSUMED,
78
79 OP_MAX
80 } opType;
81
82 static void
startOp(const char * name)83 startOp(const char *name) {
84 (void) name;
85 #ifdef DEBUG
86 fprintf(stderr, "%s\n", name);
87 #endif
88 }
89
90 int
LLVMFuzzerInitialize(int * argc ATTRIBUTE_UNUSED,char *** argv ATTRIBUTE_UNUSED)91 LLVMFuzzerInitialize(int *argc ATTRIBUTE_UNUSED,
92 char ***argv ATTRIBUTE_UNUSED) {
93 xmlFuzzMemSetup();
94 xmlInitParser();
95 #ifdef LIBXML_CATALOG_ENABLED
96 xmlInitializeCatalog();
97 xmlCatalogSetDefaults(XML_CATA_ALLOW_NONE);
98 #endif
99
100 return 0;
101 }
102
103 int
LLVMFuzzerTestOneInput(const char * data,size_t size)104 LLVMFuzzerTestOneInput(const char *data, size_t size) {
105 xmlTextReaderPtr reader;
106 xmlDocPtr doc = NULL;
107 const xmlError *error;
108 const char *docBuffer;
109 const unsigned char *program;
110 size_t maxAlloc, docSize, programSize, i;
111 size_t totalStringSize = 0;
112 int opts;
113 int oomReport = 0;
114
115 xmlFuzzDataInit(data, size);
116 opts = (int) xmlFuzzReadInt(4);
117 maxAlloc = xmlFuzzReadInt(4) % (size + 100);
118
119 program = (const unsigned char *) xmlFuzzReadString(&programSize);
120 if (programSize > 1000)
121 programSize = 1000;
122
123 xmlFuzzReadEntities();
124 docBuffer = xmlFuzzMainEntity(&docSize);
125 if (docBuffer == NULL)
126 goto exit;
127
128 #ifdef DEBUG
129 fprintf(stderr, "Input document (%d bytes):\n", (int) docSize);
130 for (i = 0; (size_t) i < docSize; i++) {
131 int c = (unsigned char) docBuffer[i];
132
133 if ((c == '\n' || (c >= 0x20 && c <= 0x7E)))
134 putc(c, stderr);
135 else
136 fprintf(stderr, "\\x%02X", c);
137 }
138 fprintf(stderr, "\nEOF\n");
139 #endif
140
141 xmlFuzzMemSetLimit(maxAlloc);
142 reader = xmlReaderForMemory(docBuffer, docSize, NULL, NULL, opts);
143 if (reader == NULL)
144 goto exit;
145
146 xmlTextReaderSetStructuredErrorHandler(reader, xmlFuzzSErrorFunc, NULL);
147 xmlTextReaderSetResourceLoader(reader, xmlFuzzResourceLoader, NULL);
148
149 i = 0;
150 while (i < programSize) {
151 int op = program[i++];
152
153 #define READ_BYTE() (i < programSize ? program[i++] : 0)
154 #define FREE_STRING(str) \
155 do { \
156 if (str != NULL) { \
157 totalStringSize += strlen((char *) str); \
158 xmlFree(str); \
159 } \
160 } while (0)
161
162 switch (op & 0x3F) {
163 case OP_READ:
164 default:
165 startOp("Read");
166 xmlTextReaderRead(reader);
167 break;
168
169 case OP_READ_INNER_XML: {
170 xmlChar *result;
171
172 startOp("ReadInnerXml");
173 result = xmlTextReaderReadInnerXml(reader);
174 FREE_STRING(result);
175 break;
176 }
177
178 case OP_READ_OUTER_XML: {
179 xmlChar *result;
180
181 startOp("ReadOuterXml");
182 result = xmlTextReaderReadOuterXml(reader);
183 FREE_STRING(result);
184 break;
185 }
186
187 case OP_READ_STRING: {
188 xmlChar *result;
189
190 startOp("ReadString");
191 result = xmlTextReaderReadString(reader);
192 FREE_STRING(result);
193 break;
194 }
195
196 case OP_READ_ATTRIBUTE_VALUE:
197 startOp("ReadAttributeValue");
198 xmlTextReaderReadAttributeValue(reader);
199 break;
200
201 case OP_ATTRIBUTE_COUNT:
202 startOp("AttributeCount");
203 xmlTextReaderAttributeCount(reader);
204 break;
205
206 case OP_DEPTH:
207 startOp("Depth");
208 xmlTextReaderDepth(reader);
209 break;
210
211 case OP_HAS_ATTRIBUTES:
212 startOp("HasAttributes");
213 xmlTextReaderHasAttributes(reader);
214 break;
215
216 case OP_HAS_VALUE:
217 startOp("HasValue");
218 xmlTextReaderHasValue(reader);
219 break;
220
221 case OP_IS_DEFAULT:
222 startOp("IsDefault");
223 xmlTextReaderIsDefault(reader);
224 break;
225
226 case OP_IS_EMPTY_ELEMENT:
227 startOp("IsEmptyElement");
228 xmlTextReaderIsEmptyElement(reader);
229 break;
230
231 case OP_NODE_TYPE:
232 startOp("NodeType");
233 xmlTextReaderNodeType(reader);
234 break;
235
236 case OP_QUOTE_CHAR:
237 startOp("QuoteChar");
238 xmlTextReaderQuoteChar(reader);
239 break;
240
241 case OP_READ_STATE:
242 startOp("ReadState");
243 xmlTextReaderReadState(reader);
244 break;
245
246 case OP_IS_NAMESPACE_DECL:
247 startOp("IsNamespaceDecl");
248 xmlTextReaderIsNamespaceDecl(reader);
249 break;
250
251 case OP_CONST_BASE_URI:
252 startOp("ConstBaseUri");
253 xmlTextReaderConstBaseUri(reader);
254 break;
255
256 case OP_CONST_LOCAL_NAME:
257 startOp("ConstLocalName");
258 xmlTextReaderConstLocalName(reader);
259 break;
260
261 case OP_CONST_NAME:
262 startOp("ConstName");
263 xmlTextReaderConstName(reader);
264 break;
265
266 case OP_CONST_NAMESPACE_URI:
267 startOp("ConstNamespaceUri");
268 xmlTextReaderConstNamespaceUri(reader);
269 break;
270
271 case OP_CONST_PREFIX:
272 startOp("ConstPrefix");
273 xmlTextReaderConstPrefix(reader);
274 break;
275
276 case OP_CONST_XML_LANG:
277 startOp("ConstXmlLang");
278 xmlTextReaderConstXmlLang(reader);
279 oomReport = -1;
280 break;
281
282 case OP_CONST_VALUE:
283 startOp("ConstValue");
284 xmlTextReaderConstValue(reader);
285 break;
286
287 case OP_BASE_URI: {
288 xmlChar *result;
289
290 startOp("BaseUri");
291 result = xmlTextReaderBaseUri(reader);
292 FREE_STRING(result);
293 break;
294 }
295
296 case OP_LOCAL_NAME: {
297 xmlChar *result;
298
299 startOp("LocalName");
300 result = xmlTextReaderLocalName(reader);
301 FREE_STRING(result);
302 break;
303 }
304
305 case OP_NAME: {
306 xmlChar *result;
307
308 startOp("Name");
309 result = xmlTextReaderName(reader);
310 FREE_STRING(result);
311 break;
312 }
313
314 case OP_NAMESPACE_URI: {
315 xmlChar *result;
316
317 startOp("NamespaceUri");
318 result = xmlTextReaderNamespaceUri(reader);
319 FREE_STRING(result);
320 break;
321 }
322
323 case OP_PREFIX: {
324 xmlChar *result;
325
326 startOp("Prefix");
327 result = xmlTextReaderPrefix(reader);
328 FREE_STRING(result);
329 break;
330 }
331
332 case OP_XML_LANG: {
333 xmlChar *result;
334
335 startOp("XmlLang");
336 result = xmlTextReaderXmlLang(reader);
337 oomReport = -1;
338 FREE_STRING(result);
339 break;
340 }
341
342 case OP_VALUE: {
343 xmlChar *result;
344
345 startOp("Value");
346 result = xmlTextReaderValue(reader);
347 FREE_STRING(result);
348 break;
349 }
350
351 case OP_CLOSE:
352 startOp("Close");
353 if (doc == NULL)
354 doc = xmlTextReaderCurrentDoc(reader);
355 xmlTextReaderClose(reader);
356 break;
357
358 case OP_GET_ATTRIBUTE_NO: {
359 xmlChar *result;
360 int no = READ_BYTE();
361
362 startOp("GetAttributeNo");
363 result = xmlTextReaderGetAttributeNo(reader, no);
364 FREE_STRING(result);
365 break;
366 }
367
368 case OP_GET_ATTRIBUTE: {
369 const xmlChar *name = xmlTextReaderConstName(reader);
370 xmlChar *result;
371
372 startOp("GetAttribute");
373 result = xmlTextReaderGetAttribute(reader, name);
374 FREE_STRING(result);
375 break;
376 }
377
378 case OP_GET_ATTRIBUTE_NS: {
379 const xmlChar *localName, *namespaceUri;
380 xmlChar *result;
381
382 startOp("GetAttributeNs");
383 localName = xmlTextReaderConstLocalName(reader);
384 namespaceUri = xmlTextReaderConstNamespaceUri(reader);
385 result = xmlTextReaderGetAttributeNs(reader, localName,
386 namespaceUri);
387 FREE_STRING(result);
388 break;
389 }
390
391 case OP_GET_REMAINDER:
392 startOp("GetRemainder");
393 if (doc == NULL)
394 doc = xmlTextReaderCurrentDoc(reader);
395 xmlFreeParserInputBuffer(xmlTextReaderGetRemainder(reader));
396 break;
397
398 case OP_LOOKUP_NAMESPACE: {
399 const xmlChar *prefix = xmlTextReaderConstPrefix(reader);
400 xmlChar *result;
401
402 startOp("LookupNamespace");
403 result = xmlTextReaderLookupNamespace(reader, prefix);
404 FREE_STRING(result);
405 break;
406 }
407
408 case OP_MOVE_TO_ATTRIBUTE_NO: {
409 int no = READ_BYTE();
410
411 startOp("MoveToAttributeNo");
412 xmlTextReaderMoveToAttributeNo(reader, no);
413 break;
414 }
415
416 case OP_MOVE_TO_ATTRIBUTE: {
417 const xmlChar *name = xmlTextReaderConstName(reader);
418
419 startOp("MoveToAttribute");
420 xmlTextReaderMoveToAttribute(reader, name);
421 break;
422 }
423
424 case OP_MOVE_TO_ATTRIBUTE_NS: {
425 const xmlChar *localName, *namespaceUri;
426
427 startOp("MoveToAttributeNs");
428 localName = xmlTextReaderConstLocalName(reader);
429 namespaceUri = xmlTextReaderConstNamespaceUri(reader);
430 xmlTextReaderMoveToAttributeNs(reader, localName,
431 namespaceUri);
432 break;
433 }
434
435 case OP_MOVE_TO_FIRST_ATTRIBUTE:
436 startOp("MoveToFirstAttribute");
437 xmlTextReaderMoveToFirstAttribute(reader);
438 break;
439
440 case OP_MOVE_TO_NEXT_ATTRIBUTE:
441 startOp("MoveToNextAttribute");
442 xmlTextReaderMoveToNextAttribute(reader);
443 break;
444
445 case OP_MOVE_TO_ELEMENT:
446 startOp("MoveToElement");
447 xmlTextReaderMoveToElement(reader);
448 break;
449
450 case OP_NORMALIZATION:
451 startOp("Normalization");
452 xmlTextReaderNormalization(reader);
453 break;
454
455 case OP_CONST_ENCODING:
456 startOp("ConstEncoding");
457 xmlTextReaderConstEncoding(reader);
458 break;
459
460 case OP_GET_PARSER_PROP: {
461 int prop = READ_BYTE();
462
463 startOp("GetParserProp");
464 xmlTextReaderGetParserProp(reader, prop);
465 break;
466 }
467
468 case OP_CURRENT_NODE:
469 startOp("CurrentNode");
470 xmlTextReaderCurrentNode(reader);
471 break;
472
473 case OP_GET_PARSER_LINE_NUMBER:
474 startOp("GetParserLineNumber");
475 xmlTextReaderGetParserLineNumber(reader);
476 break;
477
478 case OP_GET_PARSER_COLUMN_NUMBER:
479 startOp("GetParserColumnNumber");
480 xmlTextReaderGetParserColumnNumber(reader);
481 break;
482
483 case OP_PRESERVE:
484 startOp("Preserve");
485 xmlTextReaderPreserve(reader);
486 break;
487
488 case OP_CURRENT_DOC: {
489 xmlDocPtr result;
490
491 startOp("CurrentDoc");
492 result = xmlTextReaderCurrentDoc(reader);
493 if (doc == NULL)
494 doc = result;
495 break;
496 }
497
498 case OP_EXPAND:
499 startOp("Expand");
500 xmlTextReaderExpand(reader);
501 break;
502
503 case OP_NEXT:
504 startOp("Next");
505 xmlTextReaderNext(reader);
506 break;
507
508 case OP_NEXT_SIBLING:
509 startOp("NextSibling");
510 xmlTextReaderNextSibling(reader);
511 break;
512
513 case OP_IS_VALID:
514 startOp("IsValid");
515 xmlTextReaderIsValid(reader);
516 break;
517
518 case OP_CONST_XML_VERSION:
519 startOp("ConstXmlVersion");
520 xmlTextReaderConstXmlVersion(reader);
521 break;
522
523 case OP_STANDALONE:
524 startOp("Standalone");
525 xmlTextReaderStandalone(reader);
526 break;
527
528 case OP_BYTE_CONSUMED:
529 startOp("ByteConsumed");
530 xmlTextReaderByteConsumed(reader);
531 oomReport = -1;
532 break;
533 }
534
535 if (totalStringSize > docSize * 2)
536 break;
537 }
538
539 error = xmlTextReaderGetLastError(reader);
540 if (error->code == XML_ERR_NO_MEMORY)
541 oomReport = 1;
542 xmlFuzzCheckMallocFailure("reader", oomReport);
543
544 xmlFreeTextReader(reader);
545
546 if (doc != NULL)
547 xmlFreeDoc(doc);
548
549 exit:
550 xmlFuzzMemSetLimit(0);
551 xmlFuzzDataCleanup();
552 xmlResetLastError();
553 return(0);
554 }
555
556