Coverage Report

Created: 2026-09-27 07:10

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