Coverage Report

Created: 2026-10-15 06:50

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
0
removeServerComponent(void *application, UA_ServerComponent *sc) {
20
0
    UA_assert(sc->state == UA_LIFECYCLESTATE_STOPPED);
21
0
    sc->clear(sc);
22
0
    UA_free(sc);
23
0
    return NULL;
24
0
}
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
        : server(NULL), bpm(NULL), cm(NULL), nonePolicy(NULL), listenerContext(NULL),
48
          nextConnectionId(2) {
49
        UA_ServerConfig config;
50
        memset(&config, 0, sizeof(config));
51
        config.logging = &silentLogger;
52
        if(UA_ServerConfig_setDefault(&config) != UA_STATUSCODE_GOOD) {
53
            UA_ServerConfig_clear(&config);
54
            return;
55
        }
56
        config.allowEmptyVariables = UA_RULEHANDLING_ACCEPT;
57
        config.securityPolicyNoneDiscoveryOnly = false;
58
59
        server = UA_Server_newWithConfig(&config);
60
        if(!server)
61
            return;
62
63
        ZIP_ITER(UA_ServerComponentTree, &server->serverComponents,
64
                 removeServerComponent, server);
65
        ZIP_INIT(&server->serverComponents);
66
        bpm = UA_BinaryProtocolManager_new(server);
67
        if(!bpm)
68
            return;
69
        addServerComponent(server, bpm, NULL);
70
71
        for(size_t i = 0; i < server->config.securityPoliciesSize; i++) {
72
            if(UA_String_equal(&server->config.securityPolicies[i].policyUri,
73
                               &UA_SECURITY_POLICY_NONE_URI)) {
74
                nonePolicy = &server->config.securityPolicies[i];
75
                break;
76
            }
77
        }
78
79
        cm = TestConnectionManager_new("tcp", NULL);
80
        if(!cm)
81
            return;
82
83
        /* Keep the server socket and protocol manager for the fuzz process. */
84
        serverNetworkCallback(cm, 1, bpm, &listenerContext,
85
                              UA_CONNECTIONSTATE_ESTABLISHED,
86
                              &UA_KEYVALUEMAP_NULL, UA_BYTESTRING_NULL);
87
    }
88
89
1
    ~FuzzServer() {
90
1
        UA_NodeId_clear(&unsafe_fuzz_authenticationToken);
91
1
        if(listenerContext)
92
1
            serverNetworkCallback(cm, 1, bpm, &listenerContext,
93
1
                                  UA_CONNECTIONSTATE_CLOSING,
94
1
                                  &UA_KEYVALUEMAP_NULL, UA_BYTESTRING_NULL);
95
1
        if(server)
96
1
            UA_Server_delete(server);
97
1
        if(cm)
98
1
            cm->eventSource.free(&cm->eventSource);
99
1
    }
100
101
    uintptr_t
102
5.86k
    getConnectionId() {
103
5.86k
        uintptr_t connectionId = nextConnectionId++;
104
5.86k
        if(nextConnectionId <= 1)
105
0
            nextConnectionId = 2;
106
5.86k
        return connectionId;
107
5.86k
    }
108
};
109
110
static FuzzServer &
111
3.38k
getFuzzServer(void) {
112
3.38k
    static FuzzServer fuzzServer;
113
3.38k
    return fuzzServer;
114
3.38k
}
115
116
static void
117
3.38k
clearSessions(UA_Server *server) {
118
3.38k
    lockServer(server);
119
3.38k
    session_list_entry *entry;
120
6.76k
    while((entry = LIST_FIRST(&server->sessions)))
121
3.38k
        UA_Session_remove(server, &entry->session, UA_SHUTDOWNREASON_CLOSE);
122
3.38k
    unlockServer(server);
123
124
    /* Session memory is released with delayed callbacks. */
125
3.38k
    UA_Server_run_iterate(server, false);
126
3.38k
    UA_Server_run_iterate(server, false);
127
3.38k
}
128
129
static void
130
16.9k
writeUInt32(UA_Byte *buf, size_t *offset, UA_UInt32 value) {
131
16.9k
    buf[(*offset)++] = (UA_Byte)value;
132
16.9k
    buf[(*offset)++] = (UA_Byte)(value >> 8);
133
16.9k
    buf[(*offset)++] = (UA_Byte)(value >> 16);
134
16.9k
    buf[(*offset)++] = (UA_Byte)(value >> 24);
135
16.9k
}
136
137
static UA_ByteString
138
makeMSG(const UA_SecureChannel *channel, const uint8_t *payload,
139
3.38k
        size_t payloadSize) {
140
3.38k
    const size_t headerSize = 8 + 4 + 4 + 8;
141
3.38k
    UA_ByteString message = UA_BYTESTRING_NULL;
142
3.38k
    if(payloadSize > UA_UINT32_MAX - headerSize)
143
0
        return message;
144
3.38k
    const size_t messageSize = headerSize + payloadSize;
145
3.38k
    if(UA_ByteString_allocBuffer(&message, messageSize) != UA_STATUSCODE_GOOD)
146
0
        return message;
147
148
3.38k
    size_t offset = 0;
149
3.38k
    memcpy(&message.data[offset], "MSGF", 4);
150
3.38k
    offset += 4;
151
3.38k
    writeUInt32(message.data, &offset, (UA_UInt32)messageSize);
152
3.38k
    writeUInt32(message.data, &offset, channel->securityToken.channelId);
153
3.38k
    writeUInt32(message.data, &offset, channel->securityToken.tokenId);
154
3.38k
    writeUInt32(message.data, &offset, 1); /* SequenceNumber */
155
3.38k
    writeUInt32(message.data, &offset, 1); /* RequestId */
156
3.38k
    if(payloadSize > 0)
157
3.37k
        memcpy(&message.data[offset], payload, payloadSize);
158
3.38k
    return message;
159
3.38k
}
160
161
extern "C" int
162
3.38k
LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
163
3.38k
    UA_NodeId_clear(&unsafe_fuzz_authenticationToken);
164
165
    /* Generated corpus entries contain complete MSG chunks plus an optional
166
     * four-byte memory-limit trailer. Extract their service payload. */
167
3.38k
    if(size >= 24 && memcmp(data, "MSG", 3) == 0) {
168
73
        UA_UInt32 declaredSize = (UA_UInt32)data[4] |
169
73
            ((UA_UInt32)data[5] << 8) | ((UA_UInt32)data[6] << 16) |
170
73
            ((UA_UInt32)data[7] << 24);
171
73
        if(declaredSize >= 24 && declaredSize <= size) {
172
20
            data += 24;
173
20
            size = declaredSize - 24;
174
20
        }
175
73
    }
176
177
3.38k
    FuzzServer &fuzzServer = getFuzzServer();
178
3.38k
    if(!fuzzServer.bpm || !fuzzServer.nonePolicy ||
179
3.38k
       !fuzzServer.listenerContext)
180
0
        return 0;
181
182
    /* Create only disposable channel and session state for this iteration. */
183
3.38k
    uintptr_t connectionId = fuzzServer.getConnectionId();
184
3.38k
    void *connectionContext = fuzzServer.listenerContext;
185
3.38k
    serverNetworkCallback(fuzzServer.cm, connectionId,
186
3.38k
                          fuzzServer.bpm, &connectionContext,
187
3.38k
                          UA_CONNECTIONSTATE_ESTABLISHED,
188
3.38k
                          &UA_KEYVALUEMAP_NULL, UA_BYTESTRING_NULL);
189
3.38k
    UA_SecureChannel *channel = (UA_SecureChannel*)connectionContext;
190
191
    /* Configure an open SecurityPolicy None channel with a current token. */
192
3.38k
    UA_ByteString noCertificate = UA_BYTESTRING_NULL;
193
3.38k
    if(!channel ||
194
3.38k
       UA_SecureChannel_setSecurityPolicy(channel, fuzzServer.nonePolicy,
195
3.38k
                                          &noCertificate) !=
196
3.38k
           UA_STATUSCODE_GOOD) {
197
0
        if(connectionContext)
198
0
            serverNetworkCallback(fuzzServer.cm, connectionId,
199
0
                                  fuzzServer.bpm, &connectionContext,
200
0
                                  UA_CONNECTIONSTATE_CLOSING,
201
0
                                  &UA_KEYVALUEMAP_NULL, UA_BYTESTRING_NULL);
202
0
        return 0;
203
0
    }
204
3.38k
    channel->securityMode = UA_MESSAGESECURITYMODE_NONE;
205
3.38k
    channel->securityToken.tokenId = 1;
206
3.38k
    channel->securityToken.createdAt = UA_DateTime_nowMonotonic();
207
3.38k
    channel->securityToken.revisedLifetime = 600000;
208
3.38k
    channel->state = UA_SECURECHANNELSTATE_OPEN;
209
210
    /* Attach an activated session. The fuzz-only request-token rewrite in
211
     * processRequest makes decoded requests select this session. */
212
3.38k
    UA_CreateSessionRequest createRequest;
213
3.38k
    UA_CreateSessionRequest_init(&createRequest);
214
3.38k
    createRequest.requestedSessionTimeout = 600000;
215
3.38k
    UA_Session *session = NULL;
216
3.38k
    lockServer(fuzzServer.server);
217
3.38k
    UA_StatusCode res =
218
3.38k
        UA_Session_create(fuzzServer.server, channel, &createRequest, &session);
219
3.38k
    if(res == UA_STATUSCODE_GOOD) {
220
3.38k
        UA_NodeId_clear(&session->authenticationToken);
221
3.38k
        session->authenticationToken = UA_NODEID_NUMERIC(1, 1);
222
3.38k
        session->activated = true;
223
3.38k
        UA_NodeId_copy(&session->authenticationToken,
224
3.38k
                       &unsafe_fuzz_authenticationToken);
225
3.38k
    }
226
3.38k
    unlockServer(fuzzServer.server);
227
3.38k
    if(res != UA_STATUSCODE_GOOD) {
228
0
        serverNetworkCallback(fuzzServer.cm, connectionId,
229
0
                              fuzzServer.bpm, &connectionContext,
230
0
                              UA_CONNECTIONSTATE_CLOSING,
231
0
                              &UA_KEYVALUEMAP_NULL, UA_BYTESTRING_NULL);
232
0
        clearSessions(fuzzServer.server);
233
0
        return 0;
234
0
    }
235
236
3.38k
    UA_ByteString msg = makeMSG(channel, data, size);
237
3.38k
    if(msg.data) {
238
3.38k
        serverNetworkCallback(fuzzServer.cm, connectionId,
239
3.38k
                              fuzzServer.bpm, &connectionContext,
240
3.38k
                              UA_CONNECTIONSTATE_ESTABLISHED,
241
3.38k
                              &UA_KEYVALUEMAP_NULL, msg);
242
3.38k
        UA_ByteString_clear(&msg);
243
3.38k
    }
244
245
3.38k
    UA_NodeId_clear(&unsafe_fuzz_authenticationToken);
246
3.38k
    serverNetworkCallback(fuzzServer.cm, connectionId,
247
3.38k
                          fuzzServer.bpm, &connectionContext,
248
3.38k
                          UA_CONNECTIONSTATE_CLOSING,
249
3.38k
                          &UA_KEYVALUEMAP_NULL, UA_BYTESTRING_NULL);
250
3.38k
    clearSessions(fuzzServer.server);
251
3.38k
    return 0;
252
3.38k
}