Coverage Report

Created: 2026-09-03 06:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libxml2/fuzz/valid.c
Line
Count
Source
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
2
                     char ***argv ATTRIBUTE_UNUSED) {
16
2
    xmlFuzzMemSetup();
17
2
    xmlInitParser();
18
2
#ifdef LIBXML_CATALOG_ENABLED
19
2
    xmlInitializeCatalog();
20
2
    xmlCatalogSetDefaults(XML_CATA_ALLOW_NONE);
21
2
#endif
22
23
2
    return 0;
24
2
}
25
26
int
27
30.6k
LLVMFuzzerTestOneInput(const char *data, size_t size) {
28
30.6k
    xmlParserCtxtPtr ctxt;
29
30.6k
    xmlDocPtr doc;
30
30.6k
    const char *docBuffer, *docUrl;
31
30.6k
    size_t failurePos, docSize;
32
30.6k
    int opts;
33
34
30.6k
    xmlFuzzDataInit(data, size);
35
30.6k
    opts = (int) xmlFuzzReadInt(4);
36
30.6k
    opts |= XML_PARSE_DTDVALID;
37
30.6k
    failurePos = xmlFuzzReadInt(4) % (size + 100);
38
39
30.6k
    xmlFuzzReadEntities();
40
30.6k
    docBuffer = xmlFuzzMainEntity(&docSize);
41
30.6k
    docUrl = xmlFuzzMainUrl();
42
30.6k
    if (docBuffer == NULL)
43
62
        goto exit;
44
45
    /* Pull parser */
46
47
30.5k
    xmlFuzzInjectFailure(failurePos);
48
30.5k
    ctxt = xmlNewParserCtxt();
49
30.5k
    if (ctxt != NULL) {
50
30.5k
        xmlCtxtSetErrorHandler(ctxt, xmlFuzzSErrorFunc, NULL);
51
30.5k
        xmlCtxtSetResourceLoader(ctxt, xmlFuzzResourceLoader, NULL);
52
30.5k
        doc = xmlCtxtReadMemory(ctxt, docBuffer, docSize, docUrl, NULL, opts);
53
30.5k
        xmlFuzzCheckFailureReport("xmlCtxtReadMemory",
54
30.5k
                                  ctxt->errNo == XML_ERR_NO_MEMORY,
55
30.5k
                                  ctxt->errNo == XML_IO_EIO);
56
30.5k
        xmlFreeDoc(doc);
57
30.5k
        xmlFreeParserCtxt(ctxt);
58
30.5k
    }
59
60
    /* Post validation */
61
62
30.5k
    xmlFuzzInjectFailure(failurePos);
63
30.5k
    ctxt = xmlNewParserCtxt();
64
30.5k
    if (ctxt != NULL) {
65
30.5k
        xmlCtxtSetErrorHandler(ctxt, xmlFuzzSErrorFunc, NULL);
66
30.5k
        xmlCtxtSetResourceLoader(ctxt, xmlFuzzResourceLoader, NULL);
67
30.5k
        doc = xmlCtxtReadMemory(ctxt, docBuffer, docSize, docUrl, NULL,
68
30.5k
                                opts & ~XML_PARSE_DTDVALID);
69
30.5k
        xmlFuzzCheckFailureReport("xmlCtxtReadMemory",
70
30.5k
                doc == NULL && ctxt->errNo == XML_ERR_NO_MEMORY,
71
30.5k
                doc == NULL && ctxt->errNo == XML_IO_EIO);
72
30.5k
        if (doc != NULL) {
73
17.6k
            int valid = xmlCtxtValidateDocument(ctxt, doc);
74
75
17.6k
            xmlFuzzCheckFailureReport("xmlCtxtValidateDocument",
76
17.6k
                    !valid && ctxt->errNo == XML_ERR_NO_MEMORY,
77
17.6k
                    !valid && ctxt->errNo == XML_IO_EIO);
78
17.6k
        }
79
30.5k
        xmlFreeDoc(doc);
80
30.5k
        xmlFreeParserCtxt(ctxt);
81
30.5k
    }
82
83
    /* Push parser */
84
85
30.5k
#ifdef LIBXML_PUSH_ENABLED
86
30.5k
    {
87
30.5k
        static const size_t maxChunkSize = 128;
88
30.5k
        size_t consumed, chunkSize;
89
90
30.5k
        xmlFuzzInjectFailure(failurePos);
91
        /*
92
         * FIXME: xmlCreatePushParserCtxt can still report OOM errors
93
         * to stderr.
94
         */
95
30.5k
        xmlSetGenericErrorFunc(NULL, xmlFuzzErrorFunc);
96
30.5k
        ctxt = xmlCreatePushParserCtxt(NULL, NULL, NULL, 0, docUrl);
97
30.5k
        xmlSetGenericErrorFunc(NULL, NULL);
98
30.5k
        if (ctxt != NULL) {
99
30.5k
            xmlCtxtSetErrorHandler(ctxt, xmlFuzzSErrorFunc, NULL);
100
30.5k
            xmlCtxtSetResourceLoader(ctxt, xmlFuzzResourceLoader, NULL);
101
30.5k
            xmlCtxtUseOptions(ctxt, opts);
102
103
3.21M
            for (consumed = 0; consumed < docSize; consumed += chunkSize) {
104
3.18M
                chunkSize = docSize - consumed;
105
3.18M
                if (chunkSize > maxChunkSize)
106
3.15M
                    chunkSize = maxChunkSize;
107
3.18M
                xmlParseChunk(ctxt, docBuffer + consumed, chunkSize, 0);
108
3.18M
            }
109
110
30.5k
            xmlParseChunk(ctxt, NULL, 0, 1);
111
30.5k
            xmlFuzzCheckFailureReport("xmlParseChunk",
112
30.5k
                                      ctxt->errNo == XML_ERR_NO_MEMORY,
113
30.5k
                                      ctxt->errNo == XML_IO_EIO);
114
30.5k
            xmlFreeDoc(ctxt->myDoc);
115
30.5k
            xmlFreeParserCtxt(ctxt);
116
30.5k
        }
117
30.5k
    }
118
30.5k
#endif
119
120
30.6k
exit:
121
30.6k
    xmlFuzzInjectFailure(0);
122
30.6k
    xmlFuzzDataCleanup();
123
30.6k
    xmlResetLastError();
124
30.6k
    return(0);
125
30.5k
}
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