Coverage Report

Created: 2025-11-24 06:35

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