Coverage Report

Created: 2025-06-23 06:10

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