Coverage Report

Created: 2025-07-11 06:36

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