Coverage Report

Created: 2026-11-12 06:54

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/open62541_15/tests/fuzz/fuzz_msg_message.cc
Line
Count
Source
1
/* This Source Code Form is subject to the terms of the Mozilla Public
2
 * License, v. 2.0. If a copy of the MPL was not distributed with this
3
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5
#include <open62541/server_config_default.h>
6
7
#include "ua_server_internal.h"
8
#include "testing_networklayers.h"
9
10
#include <stdint.h>
11
#include <stdarg.h>
12
#include <string.h>
13
14
/* Feed fuzzed service payloads through a fully configured SecurityPolicy None
15
 * channel. The fixed symmetric envelope gets mutations through channel and
16
 * token validation to the request type and service decoders. */
17
18
static void *
19
3
removeServerComponent(void *application, UA_ServerComponent *sc) {
20
3
    UA_assert(sc->state == UA_LIFECYCLESTATE_STOPPED);
21
3
    sc->clear(sc);
22
3
    UA_free(sc);
23
3
    return NULL;
24
3
}
25
26
static void
27
silentLog(void *context, UA_LogLevel level, UA_LogCategory category,
28
0
          const char *message, va_list args) {
29
0
    (void)context;
30
0
    (void)level;
31
0
    (void)category;
32
0
    (void)message;
33
0
    (void)args;
34
0
}
35
36
static UA_Logger silentLogger = {silentLog, NULL, NULL};
37
38
struct FuzzServer {
39
    UA_Server *server;
40
    UA_ServerComponent *bpm;
41
    UA_ConnectionManager *cm;
42
    UA_SecurityPolicy *nonePolicy;
43
    void *listenerContext;
44
    uintptr_t nextConnectionId;
45
46
    FuzzServer()
47
1
        : server(NULL), bpm(NULL), cm(NULL), nonePolicy(NULL), listenerContext(NULL),
48
1
          nextConnectionId(2) {
49
1
        UA_ServerConfig config;
50
1
        memset(&config, 0, sizeof(config));
51
1
        config.logging = &silentLogger;
52
1
        if(UA_ServerConfig_setDefault(&config) != UA_STATUSCODE_GOOD) {
53
0
            UA_ServerConfig_clear(&config);
54
0
            return;
55
0
        }
56
1
        config.allowEmptyVariables = UA_RULEHANDLING_ACCEPT;
57
1
        config.securityPolicyNoneDiscoveryOnly = false;
58
59
1
        server = UA_Server_newWithConfig(&config);
60
1
        if(!server)
61
0
            return;
62
63
1
        lockServer(server);
64
1
        ZIP_ITER(UA_ServerComponentTree, &server->serverComponents,
65
1
                 removeServerComponent, server);
66
1
        ZIP_INIT(&server->serverComponents);
67
1
        unlockServer(server);
68
1
        bpm = UA_BinaryProtocolManager_new(server);
69
1
        if(!bpm)
70
0
            return;
71
1
        addServerComponent(server, bpm, NULL);
72
73
1
        for(size_t i = 0; i < server->config.securityPoliciesSize; i++) {
74
1
            if(UA_String_equal(&server->config.securityPolicies[i].policyUri,
75
1
                               &UA_SECURITY_POLICY_NONE_URI)) {
76
1
                nonePolicy = &server->config.securityPolicies[i];
77
1
                break;
78
1
            }
79
1
        }
80
81
1
        cm = TestConnectionManager_new("tcp", NULL);
82
1
        if(!cm)
83
0
            return;
84
85
        /* Keep the server socket and protocol manager for the fuzz process. */
86
1
        serverNetworkCallback(cm, 1, bpm, &listenerContext,
87
1
                              UA_CONNECTIONSTATE_ESTABLISHED,
88
1
                              &UA_KEYVALUEMAP_NULL, UA_BYTESTRING_NULL);
89
1
    }
90
91
3
    ~FuzzServer() {
92
3
        UA_NodeId_clear(&unsafe_fuzz_authenticationToken);
93
3
        if(listenerContext)
94
3
            serverNetworkCallback(cm, 1, bpm, &listenerContext,
95
3
                                  UA_CONNECTIONSTATE_CLOSING,
96
3
                                  &UA_KEYVALUEMAP_NULL, UA_BYTESTRING_NULL);
97
3
        if(server)
98
3
            UA_Server_delete(server);
99
3
        if(cm)
100
3
            cm->eventSource.free(&cm->eventSource);
101
3
    }
102
103
    uintptr_t
104
12.7k
    getConnectionId() {
105
12.7k
        uintptr_t connectionId = nextConnectionId++;
106
12.7k
        if(nextConnectionId <= 1)
107
0
            nextConnectionId = 2;
108
12.7k
        return connectionId;
109
12.7k
    }
110
};
111
112
static FuzzServer &
113
7.66k
getFuzzServer(void) {
114
7.66k
    static FuzzServer fuzzServer;
115
7.66k
    return fuzzServer;
116
7.66k
}
117
118
static void
119
7.66k
clearSessions(UA_Server *server) {
120
7.66k
    lockServer(server);
121
7.66k
    session_list_entry *entry;
122
15.2k
    while((entry = LIST_FIRST(&server->sessions)))
123
7.63k
        UA_Session_remove(server, &entry->session, UA_SHUTDOWNREASON_CLOSE);
124
7.66k
    unlockServer(server);
125
126
    /* Session memory is released with delayed callbacks. */
127
7.66k
    UA_Server_run_iterate(server, false);
128
7.66k
    UA_Server_run_iterate(server, false);
129
7.66k
}
130
131
static void
132
38.3k
writeUInt32(UA_Byte *buf, size_t *offset, UA_UInt32 value) {
133
38.3k
    buf[(*offset)++] = (UA_Byte)value;
134
38.3k
    buf[(*offset)++] = (UA_Byte)(value >> 8);
135
38.3k
    buf[(*offset)++] = (UA_Byte)(value >> 16);
136
38.3k
    buf[(*offset)++] = (UA_Byte)(value >> 24);
137
38.3k
}
138
139
static UA_ByteString
140
makeMSG(const UA_SecureChannel *channel, const uint8_t *payload,
141
7.66k
        size_t payloadSize) {
142
7.66k
    const size_t headerSize = 8 + 4 + 4 + 8;
143
7.66k
    UA_ByteString message = UA_BYTESTRING_NULL;
144
7.66k
    if(payloadSize > UA_UINT32_MAX - headerSize)
145
0
        return message;
146
7.66k
    const size_t messageSize = headerSize + payloadSize;
147
7.66k
    if(UA_ByteString_allocBuffer(&message, messageSize) != UA_STATUSCODE_GOOD)
148
0
        return message;
149
150
7.66k
    size_t offset = 0;
151
7.66k
    memcpy(&message.data[offset], "MSGF", 4);
152
7.66k
    offset += 4;
153
7.66k
    writeUInt32(message.data, &offset, (UA_UInt32)messageSize);
154
7.66k
    writeUInt32(message.data, &offset, channel->securityToken.channelId);
155
7.66k
    writeUInt32(message.data, &offset, channel->securityToken.tokenId);
156
7.66k
    writeUInt32(message.data, &offset, 1); /* SequenceNumber */
157
7.66k
    writeUInt32(message.data, &offset, 1); /* RequestId */
158
7.66k
    if(payloadSize > 0)
159
7.64k
        memcpy(&message.data[offset], payload, payloadSize);
160
7.66k
    return message;
161
7.66k
}
162
163
extern "C" int
164
7.66k
LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
165
7.66k
    UA_NodeId_clear(&unsafe_fuzz_authenticationToken);
166
167
    /* Generated corpus entries contain complete MSG chunks plus an optional
168
     * four-byte memory-limit trailer. Extract their service payload. */
169
7.66k
    if(size >= 24 && memcmp(data, "MSG", 3) == 0) {
170
135
        UA_UInt32 declaredSize = (UA_UInt32)data[4] |
171
135
            ((UA_UInt32)data[5] << 8) | ((UA_UInt32)data[6] << 16) |
172
135
            ((UA_UInt32)data[7] << 24);
173
135
        if(declaredSize >= 24 && declaredSize <= size) {
174
38
            data += 24;
175
38
            size = declaredSize - 24;
176
38
        }
177
135
    }
178
179
7.66k
    FuzzServer &fuzzServer = getFuzzServer();
180
7.66k
    if(!fuzzServer.bpm || !fuzzServer.nonePolicy ||
181
7.66k
       !fuzzServer.listenerContext)
182
0
        return 0;
183
184
    /* Create only disposable channel and session state for this iteration. */
185
7.66k
    uintptr_t connectionId = fuzzServer.getConnectionId();
186
7.66k
    void *connectionContext = fuzzServer.listenerContext;
187
7.66k
    serverNetworkCallback(fuzzServer.cm, connectionId,
188
7.66k
                          fuzzServer.bpm, &connectionContext,
189
7.66k
                          UA_CONNECTIONSTATE_ESTABLISHED,
190
7.66k
                          &UA_KEYVALUEMAP_NULL, UA_BYTESTRING_NULL);
191
7.66k
    UA_SecureChannel *channel = (UA_SecureChannel*)connectionContext;
192
193
    /* Configure an open SecurityPolicy None channel with a current token. */
194
7.66k
    UA_ByteString noCertificate = UA_BYTESTRING_NULL;
195
7.66k
    if(!channel ||
196
7.66k
       UA_SecureChannel_setSecurityPolicy(channel, fuzzServer.nonePolicy,
197
7.66k
                                          &noCertificate) !=
198
7.66k
           UA_STATUSCODE_GOOD) {
199
0
        if(connectionContext)
200
0
            serverNetworkCallback(fuzzServer.cm, connectionId,
201
0
                                  fuzzServer.bpm, &connectionContext,
202
0
                                  UA_CONNECTIONSTATE_CLOSING,
203
0
                                  &UA_KEYVALUEMAP_NULL, UA_BYTESTRING_NULL);
204
0
        return 0;
205
0
    }
206
7.66k
    channel->securityMode = UA_MESSAGESECURITYMODE_NONE;
207
7.66k
    channel->securityToken.tokenId = 1;
208
7.66k
    channel->securityToken.createdAt = UA_DateTime_nowMonotonic();
209
7.66k
    channel->securityToken.revisedLifetime = 600000;
210
7.66k
    channel->state = UA_SECURECHANNELSTATE_OPEN;
211
212
    /* Attach an activated session. The fuzz-only request-token rewrite in
213
     * processRequest makes decoded requests select this session. */
214
7.66k
    UA_CreateSessionRequest createRequest;
215
7.66k
    UA_CreateSessionRequest_init(&createRequest);
216
7.66k
    createRequest.requestedSessionTimeout = 600000;
217
7.66k
    UA_Session *session = NULL;
218
7.66k
    lockServer(fuzzServer.server);
219
7.66k
    UA_StatusCode res =
220
7.66k
        UA_Session_create(fuzzServer.server, channel, &createRequest, &session);
221
7.66k
    if(res == UA_STATUSCODE_GOOD) {
222
7.66k
        UA_NodeId_clear(&session->authenticationToken);
223
7.66k
        session->authenticationToken = UA_NODEID_NUMERIC(1, 1);
224
7.66k
        session->activated = true;
225
7.66k
        UA_NodeId_copy(&session->authenticationToken,
226
7.66k
                       &unsafe_fuzz_authenticationToken);
227
7.66k
    }
228
7.66k
    unlockServer(fuzzServer.server);
229
7.66k
    if(res != UA_STATUSCODE_GOOD) {
230
0
        serverNetworkCallback(fuzzServer.cm, connectionId,
231
0
                              fuzzServer.bpm, &connectionContext,
232
0
                              UA_CONNECTIONSTATE_CLOSING,
233
0
                              &UA_KEYVALUEMAP_NULL, UA_BYTESTRING_NULL);
234
0
        clearSessions(fuzzServer.server);
235
0
        return 0;
236
0
    }
237
238
7.66k
    UA_ByteString msg = makeMSG(channel, data, size);
239
7.66k
    if(msg.data) {
240
7.66k
        serverNetworkCallback(fuzzServer.cm, connectionId,
241
7.66k
                              fuzzServer.bpm, &connectionContext,
242
7.66k
                              UA_CONNECTIONSTATE_ESTABLISHED,
243
7.66k
                              &UA_KEYVALUEMAP_NULL, msg);
244
7.66k
        UA_ByteString_clear(&msg);
245
7.66k
    }
246
247
7.66k
    UA_NodeId_clear(&unsafe_fuzz_authenticationToken);
248
7.66k
    serverNetworkCallback(fuzzServer.cm, connectionId,
249
7.66k
                          fuzzServer.bpm, &connectionContext,
250
7.66k
                          UA_CONNECTIONSTATE_CLOSING,
251
7.66k
                          &UA_KEYVALUEMAP_NULL, UA_BYTESTRING_NULL);
252
7.66k
    clearSessions(fuzzServer.server);
253
7.66k
    return 0;
254
7.66k
}