Coverage Report

Created: 2025-08-11 06:23

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