Coverage Report

Created: 2026-11-12 06:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wolfmqtt-fuzzers/fuzzer.cpp
Line
Count
Source
1
// Copyright 2026 Google LLC.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//     http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
15
#include <fuzzing/datasource/datasource.hpp>
16
#include <wolfmqtt/mqtt_client.h>
17
#include <wolfmqtt/mqtt_packet.h>
18
#include <optional>
19
20
34.5k
#define CHECK_EQ(expr, res) if ( (expr) != (res) ) { goto end; }
21
#define CHECK_NE(expr, res) if ( (expr) == (res) ) { goto end; }
22
23
7.73k
#define BADPTR ((void*)0x12)
24
3.11k
#define MAX_TOPICS 50
25
26
54.0k
#define DEBUG 0
27
28
class Base {
29
    protected:
30
        fuzzing::datasource::Datasource& ds;
31
        MqttQoS GetQoS(void) const;
32
    public:
33
        Base(fuzzing::datasource::Datasource& ds);
34
        ~Base();
35
};
36
37
Base::Base(fuzzing::datasource::Datasource& ds) :
38
14.5k
    ds(ds)
39
14.5k
{ }
40
41
14.5k
Base::~Base() { }
42
43
8.90k
MqttQoS Base::GetQoS(void) const {
44
8.90k
    switch ( ds.Get<uint8_t>() % 3 ) {
45
4.02k
        case    0:
46
4.02k
            return MQTT_QOS_0;
47
3.26k
        case    1:
48
3.26k
            return MQTT_QOS_1;
49
1.57k
        case    2:
50
1.57k
            return MQTT_QOS_2;
51
0
        default:
52
            /* Silence compiler warning */
53
0
            abort();
54
8.90k
    }
55
8.90k
}
56
57
class Topic : public Base {
58
    private:
59
        MqttTopic topic;
60
        std::vector<std::string> strings;
61
    public:
62
        Topic(fuzzing::datasource::Datasource& ds);
63
        ~Topic();
64
        bool Generate(void);
65
        MqttTopic Get(void);
66
};
67
68
Topic::Topic(fuzzing::datasource::Datasource& ds) :
69
7.58k
    Base(ds)
70
7.58k
{ }
71
72
7.58k
Topic::~Topic() { }
73
74
7.58k
bool Topic::Generate(void) {
75
7.58k
    bool ret;
76
77
7.58k
    memset(&topic, 0, sizeof(topic));
78
79
7.58k
    strings.push_back( ds.Get<std::string>() );
80
7.58k
    topic.topic_filter = strings.back().c_str();
81
82
7.58k
    topic.qos = GetQoS();
83
84
7.58k
    ret = true;
85
7.58k
end:
86
6.81k
    return ret;
87
7.58k
}
88
89
6.61k
MqttTopic Topic::Get(void) {
90
6.61k
    return topic;
91
6.61k
}
92
93
class Topics : public Base {
94
    private:
95
        std::vector<Topic*> topics;
96
    public:
97
        Topics(fuzzing::datasource::Datasource& ds);
98
        ~Topics();
99
        bool Generate(void);
100
        MqttTopic* ToArray(void);
101
        size_t Size(void) const;
102
};
103
104
Topics::Topics(fuzzing::datasource::Datasource& ds) :
105
3.11k
    Base(ds)
106
3.11k
{ }
107
108
3.11k
Topics::~Topics() {
109
7.58k
    for (auto& t : topics) {
110
7.58k
        delete t;
111
7.58k
    }
112
3.11k
}
113
        
114
3.11k
bool Topics::Generate(void) {
115
3.11k
    bool ret;
116
117
3.11k
    try {
118
3.11k
        const auto numTopics = ds.Get<uint16_t>() % (MAX_TOPICS+1);
119
120
10.6k
        for (size_t i = 0; i < numTopics; i++) {
121
7.58k
            topics.push_back(new Topic(ds));
122
7.58k
            CHECK_EQ(topics.back()->Generate(), true);
123
7.58k
        }
124
125
3.11k
        ret = true;
126
3.11k
    } catch ( ... ) { }
127
128
3.11k
end:
129
3.11k
    return ret;
130
3.11k
}
131
132
2.95k
MqttTopic* Topics::ToArray(void) {
133
2.95k
    auto ret = new MqttTopic[topics.size()];
134
135
9.56k
    for (size_t i = 0; i < Size(); i++) {
136
6.61k
        ret[i] = topics[i]->Get();
137
6.61k
    }
138
2.95k
    return ret;
139
2.95k
}
140
        
141
12.5k
size_t Topics::Size(void) const {
142
12.5k
    return topics.size();
143
12.5k
}
144
145
class wolfMQTTFuzzer : public Base {
146
        MqttClient client;
147
        MqttNet net;
148
        MqttConnect connect;
149
150
        uint8_t* tx_buf = nullptr, *rx_buf = nullptr;
151
        size_t tx_size = 0, rx_size = 0;
152
153
        std::string client_id;
154
155
        void* malloc(const size_t n);
156
        void free(void* ptr);
157
158
        word16 GetPacketId(void) const;
159
        std::optional<Topic> GetTopic(void) const;
160
161
        bool subscribe(void);
162
        bool unsubscribe(void);
163
        bool publish(void);
164
        bool ping(void);
165
        bool wait(void);
166
    public:
167
        wolfMQTTFuzzer(fuzzing::datasource::Datasource& ds);
168
        ~wolfMQTTFuzzer();
169
        bool Initialize(void);
170
        void Run(void);
171
        int recv(byte* buf, const int buf_len);
172
        int write(const int buf_len);
173
174
};
175
176
static int mqtt_connect(void *context, const char* host, word16 port, int timeout_ms)
177
3.70k
{
178
3.70k
    (void)context;
179
3.70k
    (void)host;
180
3.70k
    (void)port;
181
3.70k
    (void)timeout_ms;
182
183
3.70k
    return MQTT_CODE_SUCCESS;
184
3.70k
}
185
186
static int mqtt_recv(void *context, byte* buf, int buf_len, int timeout_ms)
187
35.4k
{
188
35.4k
    (void)context;
189
35.4k
    (void)timeout_ms;
190
191
35.4k
    auto fuzzer = static_cast<wolfMQTTFuzzer*>(context);
192
35.4k
    return fuzzer->recv(buf, buf_len);
193
35.4k
}
194
195
static int mqtt_write(void *context, const byte* buf, int buf_len, int timeout_ms)
196
14.8k
{
197
14.8k
    (void)context;
198
14.8k
    (void)timeout_ms;
199
14.8k
    (void)buf;
200
201
14.8k
    auto fuzzer = static_cast<wolfMQTTFuzzer*>(context);
202
14.8k
    return fuzzer->write(buf_len);
203
14.8k
}
204
205
static int mqtt_disconnect(void *context)
206
122
{
207
122
    (void)context;
208
209
122
    return MQTT_CODE_SUCCESS;
210
122
}
211
212
static int mqtt_message_cb(MqttClient *client, MqttMessage *msg, byte msg_new, byte msg_done)
213
1.89k
{
214
1.89k
    return MQTT_CODE_SUCCESS;
215
1.89k
}
216
217
7.64k
void* wolfMQTTFuzzer::malloc(const size_t n) {
218
7.64k
    return n == 0 ? BADPTR : ::malloc(n);
219
7.64k
}
220
221
7.73k
void wolfMQTTFuzzer::free(void* ptr) {
222
7.73k
    if ( ptr == BADPTR ) {
223
0
        return;
224
0
    }
225
226
7.73k
    ::free(ptr);
227
7.73k
}
228
229
0
std::optional<Topic> wolfMQTTFuzzer::GetTopic(void) const {
230
0
    Topic topic(ds);
231
232
0
    if ( topic.Generate() == false ) {
233
0
        return std::nullopt;
234
0
    }
235
236
0
    return topic;
237
0
}
238
239
4.86k
word16 wolfMQTTFuzzer::GetPacketId(void) const {
240
4.86k
    return ds.Get<word16>();
241
4.86k
}
242
243
2.04k
bool wolfMQTTFuzzer::subscribe(void) {
244
2.04k
    MqttTopic* topicsArray = nullptr;
245
246
2.04k
    bool ret = false;
247
248
2.04k
    try {
249
2.04k
        Topics topics(ds);
250
2.04k
        CHECK_EQ(topics.Generate(), true);
251
252
2.04k
        MqttSubscribe subscribe;
253
254
2.04k
        memset(&subscribe, 0, sizeof(subscribe));
255
256
2.04k
        subscribe.packet_id = GetPacketId();
257
2.04k
        topicsArray = topics.ToArray();
258
2.04k
        subscribe.topic_count = topics.Size();
259
2.04k
        subscribe.topics = topicsArray;
260
261
2.04k
        CHECK_EQ(MqttClient_Subscribe(&client, &subscribe), MQTT_CODE_SUCCESS);
262
263
183
        ret = true;
264
183
    } catch ( ... ) { }
265
266
2.04k
end:
267
2.04k
    if ( topicsArray ) {
268
1.90k
        delete[] topicsArray;
269
1.90k
    }
270
2.04k
    return ret;
271
2.04k
}
272
273
1.07k
bool wolfMQTTFuzzer::unsubscribe(void) {
274
1.07k
    MqttTopic* topicsArray = nullptr;
275
276
1.07k
    bool ret = false;
277
278
1.07k
    try {
279
1.07k
        Topics topics(ds);
280
1.07k
        CHECK_EQ(topics.Generate(), true);
281
282
1.07k
        MqttUnsubscribe unsubscribe;
283
284
1.07k
        memset(&unsubscribe, 0, sizeof(unsubscribe));
285
286
1.07k
        unsubscribe.packet_id = GetPacketId();
287
1.07k
        topicsArray = topics.ToArray();
288
1.07k
        unsubscribe.topic_count = topics.Size();
289
1.07k
        unsubscribe.topics = topicsArray;
290
291
1.07k
        CHECK_EQ(MqttClient_Unsubscribe(&client, &unsubscribe), MQTT_CODE_SUCCESS);
292
293
73
        ret = true;
294
73
    } catch ( ... ) { }
295
296
1.07k
end:
297
1.07k
    if ( topicsArray ) {
298
1.04k
        delete[] topicsArray;
299
1.04k
    }
300
1.07k
    return ret;
301
1.07k
}
302
303
1.96k
bool wolfMQTTFuzzer::publish(void) {
304
1.96k
    bool ret = false;
305
306
1.96k
    try {
307
1.96k
        MqttPublish publish;
308
309
1.96k
        memset(&publish, 0, sizeof(publish));
310
311
1.96k
        publish.retain = ds.Get<bool>() ? 1 : 0;
312
1.96k
        publish.qos = GetQoS();
313
1.96k
        publish.duplicate = ds.Get<bool>() ? 1 : 0;
314
315
1.96k
        const auto topic_str = ds.Get<std::string>();
316
1.96k
        publish.topic_name = topic_str.c_str();
317
318
1.96k
        publish.packet_id = GetPacketId();
319
320
1.96k
        auto buffer = ds.GetData(0);
321
1.96k
        publish.buffer = buffer.data();
322
1.96k
        publish.total_len = buffer.size();
323
324
1.96k
        if ( DEBUG ) {
325
0
            printf("publish: topic name size: %zu\n", strlen(topic_str.c_str()));
326
0
        }
327
328
1.96k
        CHECK_EQ(MqttClient_Publish(&client, &publish), MQTT_CODE_SUCCESS);
329
330
416
        ret = true;
331
416
    } catch ( ... ) { }
332
333
1.96k
end:
334
1.96k
    return ret;
335
1.96k
}
336
337
529
bool wolfMQTTFuzzer::ping(void) {
338
529
    bool ret = false;
339
340
529
    MqttPing ping;
341
342
529
    memset(&ping, 0, sizeof(ping));
343
344
529
    CHECK_EQ(MqttClient_Ping_ex(&client, &ping), true);
345
346
0
    ret = true;
347
348
529
end:
349
529
    return ret;
350
0
}
351
352
2.77k
bool wolfMQTTFuzzer::wait(void) {
353
2.77k
    bool ret = false;
354
355
2.77k
    CHECK_EQ(MqttClient_WaitMessage(&client, 1000), MQTT_CODE_SUCCESS);
356
357
203
    ret = true;
358
359
2.77k
end:
360
2.77k
    return ret;
361
203
}
362
363
wolfMQTTFuzzer::wolfMQTTFuzzer(fuzzing::datasource::Datasource& ds) :
364
3.86k
    Base(ds)
365
3.86k
{ }
366
367
3.86k
wolfMQTTFuzzer::~wolfMQTTFuzzer() {
368
3.86k
    this->free(tx_buf);
369
3.86k
    this->free(rx_buf);
370
3.86k
}
371
372
3.86k
bool wolfMQTTFuzzer::Initialize(void) {
373
3.86k
    bool ret = false;
374
375
3.86k
    try {
376
        /* net */
377
3.86k
        {
378
3.86k
            memset(&net, 0, sizeof(net));
379
380
3.86k
            net.connect = mqtt_connect;
381
3.86k
            net.read = mqtt_recv;
382
3.86k
            net.write = mqtt_write;
383
3.86k
            net.disconnect = mqtt_disconnect;
384
3.86k
            net.context = this;
385
3.86k
        }
386
387
        /* client */
388
3.86k
        {
389
3.86k
            memset(&client, 0, sizeof(client));
390
391
3.86k
            tx_size = ds.Get<uint16_t>();
392
3.86k
            tx_size = 4096;
393
3.86k
            tx_buf = (uint8_t*)this->malloc(tx_size);
394
3.86k
            rx_size = ds.Get<uint16_t>();
395
3.86k
            rx_size = 4096;
396
3.86k
            rx_buf = (uint8_t*)this->malloc(rx_size);
397
3.86k
            memset(tx_buf, 0, tx_size);
398
3.86k
            memset(rx_buf, 0, rx_size);
399
400
3.86k
            client.msg_cb = mqtt_message_cb;
401
3.86k
            client.tx_buf = tx_buf;
402
3.86k
            client.tx_buf_len = tx_size;
403
3.86k
            client.rx_buf = rx_buf;
404
3.86k
            client.rx_buf_len = rx_size;
405
3.86k
            client.cmd_timeout_ms = 1000;
406
3.86k
        }
407
408
        /* connect */
409
        /* Zero-initialize: only a subset of lwt_msg's fields are set below when
410
         * LWT is enabled, and the MQTT v5 encoder in MqttEncode_Connect reads
411
         * other members (e.g. props). Leaving them uninitialized is a harness
412
         * bug that MSAN reports as a use-of-uninitialized-value inside
413
         * MqttEncode_Connect. */
414
3.86k
        MqttMessage lwt_msg;
415
3.86k
        memset(&lwt_msg, 0, sizeof(lwt_msg));
416
3.86k
        {
417
3.86k
            memset(&connect, 0, sizeof(connect));
418
419
3.86k
            connect.keep_alive_sec = 1;
420
3.86k
            connect.clean_session = ds.Get<bool>() ? 1 : 0;
421
3.86k
            client_id = ds.Get<std::string>();
422
3.86k
            connect.client_id = client_id.c_str();
423
3.86k
            connect.enable_lwt = ds.Get<bool>() ? 1 : 0;
424
3.86k
        }
425
            
426
3.86k
        std::string lwt_topic_name;
427
3.86k
        std::vector<uint8_t> lwt_buffer;
428
429
3.86k
        if ( connect.enable_lwt ) {
430
114
            lwt_topic_name = ds.Get<std::string>();
431
114
            lwt_buffer = ds.GetData(0);
432
433
114
            connect.lwt_msg = &lwt_msg;
434
114
            lwt_msg.qos = GetQoS();
435
114
            lwt_msg.retain = ds.Get<bool>() ? 1 : 0;
436
114
            lwt_msg.topic_name = lwt_topic_name.c_str();
437
114
            lwt_msg.buffer = lwt_buffer.data();
438
114
            lwt_msg.total_len = lwt_buffer.size();
439
114
        }
440
441
3.86k
        CHECK_EQ(MqttSocket_Init(&client, &net), MQTT_CODE_SUCCESS);
442
443
#if 0
444
        if ( ds.Get<bool>() ) {
445
            //CHECK_EQ(MqttClient_SetPropertyCallback(&client, mqtt_property_cb, NULL);
446
        }
447
#endif
448
449
3.86k
        CHECK_EQ(MqttClient_NetConnect(&client, "dummy", 12345, 1000, 0, NULL), MQTT_CODE_SUCCESS);
450
3.86k
        CHECK_EQ(MqttClient_Connect(&client, &connect), MQTT_CODE_SUCCESS);
451
452
2.33k
        ret = true;
453
454
2.33k
    } catch ( ... ) {
455
160
        return false;
456
160
    }
457
458
3.70k
end:
459
3.70k
    return ret;
460
3.86k
}
461
462
2.17k
void wolfMQTTFuzzer::Run(void) {
463
2.17k
    try {
464
2.17k
        const auto numActions = ds.Get<uint8_t>() % 20;
465
466
12.0k
        for (size_t i = 0; i < numActions; i++) {
467
11.8k
            switch ( ds.Get<uint8_t>() ) {
468
2.04k
                case    0:
469
2.04k
                    subscribe();
470
2.04k
                    break;
471
1.07k
                case    1:
472
1.07k
                    unsubscribe();
473
1.07k
                    break;
474
1.96k
                case    2:
475
1.96k
                    publish();
476
1.96k
                    break;
477
529
                case    3:
478
529
                    ping();
479
529
                    break;
480
2.77k
                case    4:
481
2.77k
                    wait();
482
2.77k
                    break;
483
11.8k
            }
484
11.8k
        }
485
486
142
        MqttClient_NetDisconnect(&client);
487
2.05k
    } catch ( ... ) { }
488
2.17k
}
489
490
35.4k
int wolfMQTTFuzzer::recv(byte* buf, const int buf_len) {
491
35.4k
    try {
492
35.4k
        const auto data = ds.GetData(0);
493
35.4k
        const size_t copySize = buf_len > data.size() ? data.size() : buf_len;
494
35.4k
        if ( copySize ) {
495
32.5k
            memcpy(buf, data.data(), copySize);
496
32.5k
        }
497
35.4k
        if ( DEBUG )
498
0
        {
499
0
            printf("Recv: %zu bytes (%d requested)\n", copySize, buf_len);
500
0
            for (size_t i = 0; i < copySize; i++) {
501
0
                printf("%02X ", data[i]);
502
0
            }
503
0
            printf("\n");
504
0
        }
505
35.4k
        return copySize;
506
35.4k
    } catch ( ... ) {
507
1.75k
        if ( DEBUG ) printf("Recv: -1\n");
508
1.75k
        return -1;
509
1.75k
    }
510
35.4k
}
511
512
14.8k
int wolfMQTTFuzzer::write(const int buf_len) {
513
14.8k
    try {
514
14.8k
        if ( ds.Get<bool>() == true ) {
515
1.21k
            if ( DEBUG ) printf("write: -1\n");
516
1.21k
            return -1;
517
1.21k
        }
518
519
13.6k
        const auto ret = (int)(ds.Get<uint32_t>() % (buf_len+1));
520
13.6k
        if ( DEBUG ) printf("write: %d bytes (%d requested)\n", ret, buf_len);
521
13.6k
        return ret;
522
14.8k
    } catch ( ... ) {
523
1.00k
        return -1;
524
1.00k
    }
525
14.8k
}
526
527
3.86k
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
528
3.86k
    fuzzing::datasource::Datasource ds(data, size);
529
3.86k
    wolfMQTTFuzzer fuzzer(ds);
530
531
3.86k
    CHECK_EQ(fuzzer.Initialize(), true);
532
533
2.17k
    fuzzer.Run();
534
535
3.86k
end:
536
3.86k
    return 0;
537
2.17k
}