Coverage Report

Created: 2025-07-18 06:56

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