Coverage Report

Created: 2026-09-02 06:54

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/xmlsec/apps/oss-fuzz/xmlsec_keyinfo_target.c
Line
Count
Source
1
/*
2
 * xmlsec <dsig:KeyInfo /> reader fuzz target.
3
 *
4
 * xmlsec_target.c only parses XML (xmlSecParseMemory), and the dsig target
5
 * needs a whole well-formed signature before it reaches any key handling. This
6
 * target calls xmlSecKeyInfoNodeRead() on a <KeyInfo> element directly, which
7
 * is reachable from a very small document, so a mutator makes progress from the
8
 * first input.
9
 *
10
 * The code it drives is the largest cold area in the library: keysdata_helpers.c
11
 * (the KeyValue, X509Data and EncryptedKey structure readers), keyinfo.c,
12
 * x509_helpers.c and the OpenSSL key-data implementations.
13
 *
14
 * The keys manager is NULL on purpose. A manager only adds trusted-key lookup,
15
 * which needs key material this target does not supply, and it brings in
16
 * application-level initialisation that this target does not need. Structure
17
 * parsing, the part that reads attacker-supplied bytes, runs either way.
18
 *
19
 * External fetches are disabled, so the target stays offline.
20
 */
21
#include <stdint.h>
22
#include <stddef.h>
23
#include <limits.h>
24
25
int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size);
26
27
#include <libxml/parser.h>
28
#include <libxml/tree.h>
29
#include <libxml/xmlerror.h>
30
31
#include <xmlsec/xmlsec.h>
32
#include <xmlsec/xmltree.h>
33
#include <xmlsec/keys.h>
34
#include <xmlsec/keyinfo.h>
35
#include <xmlsec/keysdata.h>
36
#include <xmlsec/transforms.h>
37
#include <xmlsec/errors.h>
38
#include <xmlsec/strings.h>
39
40
#include <xmlsec/openssl/app.h>
41
#include <xmlsec/openssl/crypto.h>
42
43
static int g_initialized = 0;
44
/* Set when do_init() fails, so a failed one-time init is not retried on
45
 * every input. */
46
static int g_init_failed = 0;
47
48
3.79M
static void ignore_error(void* ctx, const char* msg, ...) {
49
3.79M
    (void)ctx; (void)msg;
50
3.79M
}
51
52
static void ignore_xmlsec_error(const char* file, int line, const char* func,
53
                                const char* errorObject, const char* errorSubject,
54
98.1k
                                int reason, const char* msg) {
55
98.1k
    (void)file; (void)line; (void)func;
56
98.1k
    (void)errorObject; (void)errorSubject; (void)reason; (void)msg;
57
98.1k
}
58
59
1
static int do_init(void) {
60
1
    xmlInitParser();
61
62
1
    if (xmlSecInit() < 0) {
63
0
        return -1;
64
0
    }
65
1
    if (xmlSecCheckVersion() != 1) {
66
0
        return -1;
67
0
    }
68
1
    if (xmlSecOpenSSLAppInit(NULL) < 0) {
69
0
        return -1;
70
0
    }
71
1
    if (xmlSecOpenSSLInit() < 0) {
72
0
        return -1;
73
0
    }
74
75
1
    xmlSetGenericErrorFunc(NULL, &ignore_error);
76
1
    xmlSecErrorsSetCallback(&ignore_xmlsec_error);
77
1
    return 0;
78
1
}
79
80
24.2k
int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
81
24.2k
    xmlDocPtr doc = NULL;
82
24.2k
    xmlNodePtr root = NULL;
83
24.2k
    xmlNodePtr node = NULL;
84
24.2k
    xmlSecKeyPtr key = NULL;
85
24.2k
    xmlSecKeyInfoCtxPtr keyInfoCtx = NULL;
86
87
24.2k
    if (!g_initialized) {
88
1
        g_init_failed = (do_init() < 0);
89
1
        g_initialized = 1;
90
1
    }
91
    /* Skip inputs that cannot be represented as the int length expected by
92
     * xmlReadMemory(). */
93
24.2k
    if (g_init_failed || size == 0 || size > (size_t)INT_MAX) {
94
0
        return 0;
95
0
    }
96
97
    /* NONET and NOENT stop external fetches and entity expansion. */
98
24.2k
    doc = xmlReadMemory((const char*)data, (int)size, "fuzz.xml", NULL,
99
24.2k
                        XML_PARSE_NONET | XML_PARSE_NOENT);
100
24.2k
    if (doc == NULL) {
101
17.0k
        return 0;
102
17.0k
    }
103
7.15k
    root = xmlDocGetRootElement(doc);
104
7.15k
    if (root == NULL) {
105
0
        xmlFreeDoc(doc);
106
0
        return 0;
107
0
    }
108
109
    /* Accept a bare <KeyInfo> document as well as one that contains it, so a
110
     * seed does not have to carry a signature wrapper. */
111
7.15k
    if (xmlSecCheckNodeName(root, xmlSecNodeKeyInfo, xmlSecDSigNs)) {
112
2.59k
        node = root;
113
4.55k
    } else {
114
4.55k
        node = xmlSecFindNode(root, xmlSecNodeKeyInfo, xmlSecDSigNs);
115
4.55k
    }
116
7.15k
    if (node == NULL) {
117
114
        xmlFreeDoc(doc);
118
114
        return 0;
119
114
    }
120
121
7.03k
    key = xmlSecKeyCreate();
122
7.03k
    if (key == NULL) {
123
0
        xmlFreeDoc(doc);
124
0
        return 0;
125
0
    }
126
127
7.03k
    keyInfoCtx = xmlSecKeyInfoCtxCreate(NULL);
128
7.03k
    if (keyInfoCtx == NULL) {
129
0
        xmlSecKeyDestroy(key);
130
0
        xmlFreeDoc(doc);
131
0
        return 0;
132
0
    }
133
134
7.03k
    keyInfoCtx->mode = xmlSecKeyInfoModeRead;
135
    /* Accept any key so the first successfully read child element satisfies
136
     * the requirement (the reader then stops, skipping later siblings). */
137
7.03k
    keyInfoCtx->keyReq.keyId = xmlSecKeyDataIdUnknown;
138
7.03k
    keyInfoCtx->keyReq.keyType = xmlSecKeyDataTypeAny;
139
7.03k
    keyInfoCtx->keyReq.keyUsage = xmlSecKeyUsageAny;
140
    /* <RetrievalMethod> and <KeyInfoReference> must not fetch remote or local
141
     * data. This is the no-network guard. */
142
7.03k
    keyInfoCtx->retrievalMethodCtx.enabledUris =
143
7.03k
        xmlSecTransformUriTypeEmpty | xmlSecTransformUriTypeSameDocument;
144
7.03k
    keyInfoCtx->keyInfoReferenceCtx.enabledUris =
145
7.03k
        xmlSecTransformUriTypeEmpty | xmlSecTransformUriTypeSameDocument;
146
147
    /* The return value does not matter. The parsing paths are the target. */
148
7.03k
    (void)xmlSecKeyInfoNodeRead(node, key, keyInfoCtx);
149
150
7.03k
    xmlSecKeyInfoCtxDestroy(keyInfoCtx);
151
7.03k
    xmlSecKeyDestroy(key);
152
7.03k
    xmlFreeDoc(doc);
153
7.03k
    return 0;
154
7.03k
}