Coverage Report

Created: 2026-06-10 06:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libxml2/fuzz/reader.c
Line
Count
Source
1
/*
2
 * reader.c: a libFuzzer target to test the XML Reader API.
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
83
688k
startOp(const char *name) {
84
688k
    (void) name;
85
#ifdef DEBUG
86
    fprintf(stderr, "%s\n", name);
87
#endif
88
688k
}
89
90
int
91
LLVMFuzzerInitialize(int *argc ATTRIBUTE_UNUSED,
92
2
                     char ***argv ATTRIBUTE_UNUSED) {
93
2
    xmlFuzzMemSetup();
94
2
    xmlInitParser();
95
2
#ifdef LIBXML_CATALOG_ENABLED
96
2
    xmlInitializeCatalog();
97
2
    xmlCatalogSetDefaults(XML_CATA_ALLOW_NONE);
98
2
#endif
99
100
2
    return 0;
101
2
}
102
103
int
104
31.7k
LLVMFuzzerTestOneInput(const char *data, size_t size) {
105
31.7k
    xmlTextReaderPtr reader;
106
31.7k
    xmlDocPtr doc = NULL;
107
31.7k
    const xmlError *error;
108
31.7k
    const char *docBuffer;
109
31.7k
    const unsigned char *program;
110
31.7k
    size_t failurePos, docSize, programSize, i;
111
31.7k
    size_t totalStringSize = 0;
112
31.7k
    int opts;
113
31.7k
    int oomReport = 0;
114
115
31.7k
    xmlFuzzDataInit(data, size);
116
31.7k
    opts = (int) xmlFuzzReadInt(4);
117
31.7k
    failurePos = xmlFuzzReadInt(4) % (size + 100);
118
119
31.7k
    program = (const unsigned char *) xmlFuzzReadString(&programSize);
120
31.7k
    if (programSize > 1000)
121
43
        programSize = 1000;
122
123
31.7k
    xmlFuzzReadEntities();
124
31.7k
    docBuffer = xmlFuzzMainEntity(&docSize);
125
31.7k
    if (docBuffer == NULL)
126
100
        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
31.6k
    xmlFuzzInjectFailure(failurePos);
142
31.6k
    reader = xmlReaderForMemory(docBuffer, docSize, NULL, NULL, opts);
143
31.6k
    if (reader == NULL)
144
48
        goto exit;
145
146
31.5k
    xmlTextReaderSetStructuredErrorHandler(reader, xmlFuzzSErrorFunc, NULL);
147
31.5k
    xmlTextReaderSetResourceLoader(reader, xmlFuzzResourceLoader, NULL);
148
149
31.5k
    i = 0;
150
719k
    while (i < programSize) {
151
688k
        int op = program[i++];
152
153
688k
#define READ_BYTE() (i < programSize ? program[i++] : 0)
154
688k
#define FREE_STRING(str) \
155
688k
    do { \
156
154k
        if (str != NULL) { \
157
47.4k
            totalStringSize += strlen((char *) str); \
158
47.4k
            xmlFree(str); \
159
47.4k
        } \
160
154k
    } while (0)
161
162
688k
        switch (op & 0x3F) {
163
43.5k
            case OP_READ:
164
227k
            default:
165
227k
                startOp("Read");
166
227k
                xmlTextReaderRead(reader);
167
227k
                break;
168
169
12.5k
            case OP_READ_INNER_XML: {
170
12.5k
                xmlChar *result;
171
172
12.5k
                startOp("ReadInnerXml");
173
12.5k
                result = xmlTextReaderReadInnerXml(reader);
174
12.5k
                FREE_STRING(result);
175
12.5k
                break;
176
43.5k
            }
177
178
31.3k
            case OP_READ_OUTER_XML: {
179
31.3k
                xmlChar *result;
180
181
31.3k
                startOp("ReadOuterXml");
182
31.3k
                result = xmlTextReaderReadOuterXml(reader);
183
31.3k
                FREE_STRING(result);
184
31.3k
                break;
185
43.5k
            }
186
187
8.38k
            case OP_READ_STRING: {
188
8.38k
                xmlChar *result;
189
190
8.38k
                startOp("ReadString");
191
8.38k
                result = xmlTextReaderReadString(reader);
192
8.38k
                FREE_STRING(result);
193
8.38k
                break;
194
43.5k
            }
195
196
9.09k
            case OP_READ_ATTRIBUTE_VALUE:
197
9.09k
                startOp("ReadAttributeValue");
198
9.09k
                xmlTextReaderReadAttributeValue(reader);
199
9.09k
                break;
200
201
8.25k
            case OP_ATTRIBUTE_COUNT:
202
8.25k
                startOp("AttributeCount");
203
8.25k
                xmlTextReaderAttributeCount(reader);
204
8.25k
                break;
205
206
3.18k
            case OP_DEPTH:
207
3.18k
                startOp("Depth");
208
3.18k
                xmlTextReaderDepth(reader);
209
3.18k
                break;
210
211
3.15k
            case OP_HAS_ATTRIBUTES:
212
3.15k
                startOp("HasAttributes");
213
3.15k
                xmlTextReaderHasAttributes(reader);
214
3.15k
                break;
215
216
11.1k
            case OP_HAS_VALUE:
217
11.1k
                startOp("HasValue");
218
11.1k
                xmlTextReaderHasValue(reader);
219
11.1k
                break;
220
221
5.54k
            case OP_IS_DEFAULT:
222
5.54k
                startOp("IsDefault");
223
5.54k
                xmlTextReaderIsDefault(reader);
224
5.54k
                break;
225
226
8.72k
            case OP_IS_EMPTY_ELEMENT:
227
8.72k
                startOp("IsEmptyElement");
228
8.72k
                xmlTextReaderIsEmptyElement(reader);
229
8.72k
                break;
230
231
10.0k
            case OP_NODE_TYPE:
232
10.0k
                startOp("NodeType");
233
10.0k
                xmlTextReaderNodeType(reader);
234
10.0k
                break;
235
236
3.03k
            case OP_QUOTE_CHAR:
237
3.03k
                startOp("QuoteChar");
238
3.03k
                xmlTextReaderQuoteChar(reader);
239
3.03k
                break;
240
241
5.17k
            case OP_READ_STATE:
242
5.17k
                startOp("ReadState");
243
5.17k
                xmlTextReaderReadState(reader);
244
5.17k
                break;
245
246
3.89k
            case OP_IS_NAMESPACE_DECL:
247
3.89k
                startOp("IsNamespaceDecl");
248
3.89k
                xmlTextReaderIsNamespaceDecl(reader);
249
3.89k
                break;
250
251
40.3k
            case OP_CONST_BASE_URI:
252
40.3k
                startOp("ConstBaseUri");
253
40.3k
                xmlTextReaderConstBaseUri(reader);
254
40.3k
                break;
255
256
2.01k
            case OP_CONST_LOCAL_NAME:
257
2.01k
                startOp("ConstLocalName");
258
2.01k
                xmlTextReaderConstLocalName(reader);
259
2.01k
                break;
260
261
7.55k
            case OP_CONST_NAME:
262
7.55k
                startOp("ConstName");
263
7.55k
                xmlTextReaderConstName(reader);
264
7.55k
                break;
265
266
5.29k
            case OP_CONST_NAMESPACE_URI:
267
5.29k
                startOp("ConstNamespaceUri");
268
5.29k
                xmlTextReaderConstNamespaceUri(reader);
269
5.29k
                break;
270
271
11.8k
            case OP_CONST_PREFIX:
272
11.8k
                startOp("ConstPrefix");
273
11.8k
                xmlTextReaderConstPrefix(reader);
274
11.8k
                break;
275
276
6.25k
            case OP_CONST_XML_LANG:
277
6.25k
                startOp("ConstXmlLang");
278
6.25k
                xmlTextReaderConstXmlLang(reader);
279
6.25k
                oomReport = -1;
280
6.25k
                break;
281
282
5.93k
            case OP_CONST_VALUE:
283
5.93k
                startOp("ConstValue");
284
5.93k
                xmlTextReaderConstValue(reader);
285
5.93k
                break;
286
287
7.81k
            case OP_BASE_URI: {
288
7.81k
                xmlChar *result;
289
290
7.81k
                startOp("BaseUri");
291
7.81k
                result = xmlTextReaderBaseUri(reader);
292
7.81k
                FREE_STRING(result);
293
7.81k
                break;
294
43.5k
            }
295
296
5.20k
            case OP_LOCAL_NAME: {
297
5.20k
                xmlChar *result;
298
299
5.20k
                startOp("LocalName");
300
5.20k
                result = xmlTextReaderLocalName(reader);
301
5.20k
                FREE_STRING(result);
302
5.20k
                break;
303
43.5k
            }
304
305
5.60k
            case OP_NAME: {
306
5.60k
                xmlChar *result;
307
308
5.60k
                startOp("Name");
309
5.60k
                result = xmlTextReaderName(reader);
310
5.60k
                FREE_STRING(result);
311
5.60k
                break;
312
43.5k
            }
313
314
3.44k
            case OP_NAMESPACE_URI: {
315
3.44k
                xmlChar *result;
316
317
3.44k
                startOp("NamespaceUri");
318
3.44k
                result = xmlTextReaderNamespaceUri(reader);
319
3.44k
                FREE_STRING(result);
320
3.44k
                break;
321
43.5k
            }
322
323
4.26k
            case OP_PREFIX: {
324
4.26k
                xmlChar *result;
325
326
4.26k
                startOp("Prefix");
327
4.26k
                result = xmlTextReaderPrefix(reader);
328
4.26k
                FREE_STRING(result);
329
4.26k
                break;
330
43.5k
            }
331
332
2.97k
            case OP_XML_LANG: {
333
2.97k
                xmlChar *result;
334
335
2.97k
                startOp("XmlLang");
336
2.97k
                result = xmlTextReaderXmlLang(reader);
337
2.97k
                oomReport = -1;
338
2.97k
                FREE_STRING(result);
339
2.97k
                break;
340
43.5k
            }
341
342
9.59k
            case OP_VALUE: {
343
9.59k
                xmlChar *result;
344
345
9.59k
                startOp("Value");
346
9.59k
                result = xmlTextReaderValue(reader);
347
9.59k
                FREE_STRING(result);
348
9.59k
                break;
349
43.5k
            }
350
351
1.65k
            case OP_CLOSE:
352
1.65k
                startOp("Close");
353
1.65k
                if (doc == NULL)
354
524
                    doc = xmlTextReaderCurrentDoc(reader);
355
1.65k
                xmlTextReaderClose(reader);
356
1.65k
                break;
357
358
4.97k
            case OP_GET_ATTRIBUTE_NO: {
359
4.97k
                xmlChar *result;
360
4.97k
                int no = READ_BYTE();
361
362
4.97k
                startOp("GetAttributeNo");
363
4.97k
                result = xmlTextReaderGetAttributeNo(reader, no);
364
4.97k
                FREE_STRING(result);
365
4.97k
                break;
366
43.5k
            }
367
368
26.2k
            case OP_GET_ATTRIBUTE: {
369
26.2k
                const xmlChar *name = xmlTextReaderConstName(reader);
370
26.2k
                xmlChar *result;
371
372
26.2k
                startOp("GetAttribute");
373
26.2k
                result = xmlTextReaderGetAttribute(reader, name);
374
26.2k
                FREE_STRING(result);
375
26.2k
                break;
376
43.5k
            }
377
378
23.7k
            case OP_GET_ATTRIBUTE_NS: {
379
23.7k
                const xmlChar *localName, *namespaceUri;
380
23.7k
                xmlChar *result;
381
382
23.7k
                startOp("GetAttributeNs");
383
23.7k
                localName = xmlTextReaderConstLocalName(reader);
384
23.7k
                namespaceUri = xmlTextReaderConstNamespaceUri(reader);
385
23.7k
                result = xmlTextReaderGetAttributeNs(reader, localName,
386
23.7k
                                                     namespaceUri);
387
23.7k
                FREE_STRING(result);
388
23.7k
                break;
389
43.5k
            }
390
391
3.55k
            case OP_GET_REMAINDER:
392
3.55k
                startOp("GetRemainder");
393
3.55k
                if (doc == NULL)
394
1.61k
                    doc = xmlTextReaderCurrentDoc(reader);
395
3.55k
                xmlFreeParserInputBuffer(xmlTextReaderGetRemainder(reader));
396
3.55k
                break;
397
398
8.12k
            case OP_LOOKUP_NAMESPACE: {
399
8.12k
                const xmlChar *prefix = xmlTextReaderConstPrefix(reader);
400
8.12k
                xmlChar *result;
401
402
8.12k
                startOp("LookupNamespace");
403
8.12k
                result = xmlTextReaderLookupNamespace(reader, prefix);
404
8.12k
                FREE_STRING(result);
405
8.12k
                break;
406
43.5k
            }
407
408
6.60k
            case OP_MOVE_TO_ATTRIBUTE_NO: {
409
6.60k
                int no = READ_BYTE();
410
411
6.60k
                startOp("MoveToAttributeNo");
412
6.60k
                xmlTextReaderMoveToAttributeNo(reader, no);
413
6.60k
                break;
414
43.5k
            }
415
416
12.0k
            case OP_MOVE_TO_ATTRIBUTE: {
417
12.0k
                const xmlChar *name = xmlTextReaderConstName(reader);
418
419
12.0k
                startOp("MoveToAttribute");
420
12.0k
                xmlTextReaderMoveToAttribute(reader, name);
421
12.0k
                break;
422
43.5k
            }
423
424
8.38k
            case OP_MOVE_TO_ATTRIBUTE_NS: {
425
8.38k
                const xmlChar *localName, *namespaceUri;
426
427
8.38k
                startOp("MoveToAttributeNs");
428
8.38k
                localName = xmlTextReaderConstLocalName(reader);
429
8.38k
                namespaceUri = xmlTextReaderConstNamespaceUri(reader);
430
8.38k
                xmlTextReaderMoveToAttributeNs(reader, localName,
431
8.38k
                                               namespaceUri);
432
8.38k
                break;
433
43.5k
            }
434
435
10.8k
            case OP_MOVE_TO_FIRST_ATTRIBUTE:
436
10.8k
                startOp("MoveToFirstAttribute");
437
10.8k
                xmlTextReaderMoveToFirstAttribute(reader);
438
10.8k
                break;
439
440
6.89k
            case OP_MOVE_TO_NEXT_ATTRIBUTE:
441
6.89k
                startOp("MoveToNextAttribute");
442
6.89k
                xmlTextReaderMoveToNextAttribute(reader);
443
6.89k
                break;
444
445
5.38k
            case OP_MOVE_TO_ELEMENT:
446
5.38k
                startOp("MoveToElement");
447
5.38k
                xmlTextReaderMoveToElement(reader);
448
5.38k
                break;
449
450
2.71k
            case OP_NORMALIZATION:
451
2.71k
                startOp("Normalization");
452
2.71k
                xmlTextReaderNormalization(reader);
453
2.71k
                break;
454
455
3.74k
            case OP_CONST_ENCODING:
456
3.74k
                startOp("ConstEncoding");
457
3.74k
                xmlTextReaderConstEncoding(reader);
458
3.74k
                break;
459
460
7.03k
            case OP_GET_PARSER_PROP: {
461
7.03k
                int prop = READ_BYTE();
462
463
7.03k
                startOp("GetParserProp");
464
7.03k
                xmlTextReaderGetParserProp(reader, prop);
465
7.03k
                break;
466
43.5k
            }
467
468
11.7k
            case OP_CURRENT_NODE:
469
11.7k
                startOp("CurrentNode");
470
11.7k
                xmlTextReaderCurrentNode(reader);
471
11.7k
                break;
472
473
5.12k
            case OP_GET_PARSER_LINE_NUMBER:
474
5.12k
                startOp("GetParserLineNumber");
475
5.12k
                xmlTextReaderGetParserLineNumber(reader);
476
5.12k
                break;
477
478
8.82k
            case OP_GET_PARSER_COLUMN_NUMBER:
479
8.82k
                startOp("GetParserColumnNumber");
480
8.82k
                xmlTextReaderGetParserColumnNumber(reader);
481
8.82k
                break;
482
483
7.39k
            case OP_PRESERVE:
484
7.39k
                startOp("Preserve");
485
7.39k
                xmlTextReaderPreserve(reader);
486
7.39k
                break;
487
488
5.96k
            case OP_CURRENT_DOC: {
489
5.96k
                xmlDocPtr result;
490
491
5.96k
                startOp("CurrentDoc");
492
5.96k
                result = xmlTextReaderCurrentDoc(reader);
493
5.96k
                if (doc == NULL)
494
2.46k
                    doc = result;
495
5.96k
                break;
496
43.5k
            }
497
498
7.77k
            case OP_EXPAND:
499
7.77k
                startOp("Expand");
500
7.77k
                xmlTextReaderExpand(reader);
501
7.77k
                break;
502
503
17.9k
            case OP_NEXT:
504
17.9k
                startOp("Next");
505
17.9k
                xmlTextReaderNext(reader);
506
17.9k
                break;
507
508
7.11k
            case OP_NEXT_SIBLING:
509
7.11k
                startOp("NextSibling");
510
7.11k
                xmlTextReaderNextSibling(reader);
511
7.11k
                break;
512
513
3.31k
            case OP_IS_VALID:
514
3.31k
                startOp("IsValid");
515
3.31k
                xmlTextReaderIsValid(reader);
516
3.31k
                break;
517
518
3.77k
            case OP_CONST_XML_VERSION:
519
3.77k
                startOp("ConstXmlVersion");
520
3.77k
                xmlTextReaderConstXmlVersion(reader);
521
3.77k
                break;
522
523
3.58k
            case OP_STANDALONE:
524
3.58k
                startOp("Standalone");
525
3.58k
                xmlTextReaderStandalone(reader);
526
3.58k
                break;
527
528
5.37k
            case OP_BYTE_CONSUMED:
529
5.37k
                startOp("ByteConsumed");
530
5.37k
                xmlTextReaderByteConsumed(reader);
531
5.37k
                oomReport = -1;
532
5.37k
                break;
533
688k
        }
534
535
688k
        if (totalStringSize > docSize * 2)
536
1.09k
            break;
537
688k
    }
538
539
31.5k
    error = xmlTextReaderGetLastError(reader);
540
31.5k
    if (error->code == XML_ERR_NO_MEMORY)
541
7.80k
        oomReport = 1;
542
31.5k
    xmlFuzzCheckFailureReport("reader", oomReport, error->code == XML_IO_EIO);
543
544
31.5k
    xmlFreeTextReader(reader);
545
546
31.5k
    if (doc != NULL)
547
3.22k
        xmlFreeDoc(doc);
548
549
31.7k
exit:
550
31.7k
    xmlFuzzInjectFailure(0);
551
31.7k
    xmlFuzzDataCleanup();
552
31.7k
    xmlResetLastError();
553
31.7k
    return(0);
554
31.5k
}
555
556
size_t
557
LLVMFuzzerCustomMutator(char *data, size_t size, size_t maxSize,
558
0
                        unsigned seed) {
559
0
    static const xmlFuzzChunkDesc chunks[] = {
560
0
        { 4, XML_FUZZ_PROB_ONE / 10 }, /* opts */
561
0
        { 4, XML_FUZZ_PROB_ONE / 10 }, /* failurePos */
562
0
        { 0, 0 }
563
0
    };
564
565
0
    return xmlFuzzMutateChunks(chunks, data, size, maxSize, seed,
566
0
                               LLVMFuzzerMutate);
567
0
}
568