/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 | 24.7k | #define CHECK_EQ(expr, res) if ( (expr) != (res) ) { goto end; } |
21 | | #define CHECK_NE(expr, res) if ( (expr) == (res) ) { goto end; } |
22 | | |
23 | 5.59k | #define BADPTR ((void*)0x12) |
24 | 2.74k | #define MAX_TOPICS 50 |
25 | | |
26 | 16.2k | #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 | 9.17k | ds(ds) |
39 | 9.17k | { } |
40 | | |
41 | 9.17k | Base::~Base() { } |
42 | | |
43 | 3.66k | MqttQoS Base::GetQoS(void) const { |
44 | 3.66k | switch ( ds.Get<uint8_t>() % 3 ) { |
45 | 2.73k | case 0: |
46 | 2.73k | return MQTT_QOS_0; |
47 | 427 | case 1: |
48 | 427 | return MQTT_QOS_1; |
49 | 449 | case 2: |
50 | 449 | return MQTT_QOS_2; |
51 | 0 | default: |
52 | | /* Silence compiler warning */ |
53 | 0 | abort(); |
54 | 3.66k | } |
55 | 3.66k | } |
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 | 3.63k | Base(ds) |
70 | 3.63k | { } |
71 | | |
72 | 3.63k | Topic::~Topic() { } |
73 | | |
74 | 3.63k | bool Topic::Generate(void) { |
75 | 3.63k | bool ret; |
76 | | |
77 | 3.63k | memset(&topic, 0, sizeof(topic)); |
78 | | |
79 | 3.63k | strings.push_back( ds.Get<std::string>() ); |
80 | 3.63k | topic.topic_filter = strings.back().c_str(); |
81 | | |
82 | 3.63k | topic.qos = GetQoS(); |
83 | | |
84 | 3.63k | ret = true; |
85 | 3.63k | end: |
86 | 2.75k | return ret; |
87 | 3.63k | } |
88 | | |
89 | 2.69k | MqttTopic Topic::Get(void) { |
90 | 2.69k | return topic; |
91 | 2.69k | } |
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 | 2.74k | Base(ds) |
106 | 2.74k | { } |
107 | | |
108 | 2.74k | Topics::~Topics() { |
109 | 3.63k | for (auto& t : topics) { |
110 | 3.63k | delete t; |
111 | 3.63k | } |
112 | 2.74k | } |
113 | | |
114 | 2.74k | bool Topics::Generate(void) { |
115 | 2.74k | bool ret; |
116 | | |
117 | 2.74k | try { |
118 | 2.74k | const auto numTopics = ds.Get<uint16_t>() % (MAX_TOPICS+1); |
119 | | |
120 | 6.38k | for (size_t i = 0; i < numTopics; i++) { |
121 | 3.63k | topics.push_back(new Topic(ds)); |
122 | 3.63k | CHECK_EQ(topics.back()->Generate(), true); |
123 | 3.63k | } |
124 | | |
125 | 2.74k | ret = true; |
126 | 2.74k | } catch ( ... ) { } |
127 | | |
128 | 2.74k | end: |
129 | 2.74k | return ret; |
130 | 2.74k | } |
131 | | |
132 | 2.44k | MqttTopic* Topics::ToArray(void) { |
133 | 2.44k | auto ret = new MqttTopic[topics.size()]; |
134 | | |
135 | 5.14k | for (size_t i = 0; i < Size(); i++) { |
136 | 2.69k | ret[i] = topics[i]->Get(); |
137 | 2.69k | } |
138 | 2.44k | return ret; |
139 | 2.44k | } |
140 | | |
141 | 7.58k | size_t Topics::Size(void) const { |
142 | 7.58k | return topics.size(); |
143 | 7.58k | } |
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 | 2.64k | { |
178 | 2.64k | (void)context; |
179 | 2.64k | (void)host; |
180 | 2.64k | (void)port; |
181 | 2.64k | (void)timeout_ms; |
182 | | |
183 | 2.64k | return MQTT_CODE_SUCCESS; |
184 | 2.64k | } |
185 | | |
186 | | static int mqtt_recv(void *context, byte* buf, int buf_len, int timeout_ms) |
187 | 8.63k | { |
188 | 8.63k | (void)context; |
189 | 8.63k | (void)timeout_ms; |
190 | | |
191 | 8.63k | auto fuzzer = static_cast<wolfMQTTFuzzer*>(context); |
192 | 8.63k | return fuzzer->recv(buf, buf_len); |
193 | 8.63k | } |
194 | | |
195 | | static int mqtt_write(void *context, const byte* buf, int buf_len, int timeout_ms) |
196 | 4.64k | { |
197 | 4.64k | (void)context; |
198 | 4.64k | (void)timeout_ms; |
199 | 4.64k | (void)buf; |
200 | | |
201 | 4.64k | auto fuzzer = static_cast<wolfMQTTFuzzer*>(context); |
202 | 4.64k | return fuzzer->write(buf_len); |
203 | 4.64k | } |
204 | | |
205 | | static int mqtt_disconnect(void *context) |
206 | 226 | { |
207 | 226 | (void)context; |
208 | | |
209 | 226 | return MQTT_CODE_SUCCESS; |
210 | 226 | } |
211 | | |
212 | | static int mqtt_message_cb(MqttClient *client, MqttMessage *msg, byte msg_new, byte msg_done) |
213 | 0 | { |
214 | 0 | return MQTT_CODE_SUCCESS; |
215 | 0 | } |
216 | | |
217 | 5.50k | void* wolfMQTTFuzzer::malloc(const size_t n) { |
218 | 5.50k | return n == 0 ? BADPTR : ::malloc(n); |
219 | 5.50k | } |
220 | | |
221 | 5.59k | void wolfMQTTFuzzer::free(void* ptr) { |
222 | 5.59k | if ( ptr == BADPTR ) { |
223 | 0 | return; |
224 | 0 | } |
225 | | |
226 | 5.59k | ::free(ptr); |
227 | 5.59k | } |
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 | 3.38k | word16 wolfMQTTFuzzer::GetPacketId(void) const { |
240 | 3.38k | return ds.Get<word16>(); |
241 | 3.38k | } |
242 | | |
243 | 2.25k | bool wolfMQTTFuzzer::subscribe(void) { |
244 | 2.25k | MqttTopic* topicsArray = nullptr; |
245 | | |
246 | 2.25k | bool ret = false; |
247 | | |
248 | 2.25k | try { |
249 | 2.25k | Topics topics(ds); |
250 | 2.25k | CHECK_EQ(topics.Generate(), true); |
251 | | |
252 | 2.25k | MqttSubscribe subscribe; |
253 | | |
254 | 2.25k | memset(&subscribe, 0, sizeof(subscribe)); |
255 | | |
256 | 2.25k | subscribe.packet_id = GetPacketId(); |
257 | 2.25k | topicsArray = topics.ToArray(); |
258 | 2.25k | subscribe.topic_count = topics.Size(); |
259 | 2.25k | subscribe.topics = topicsArray; |
260 | | |
261 | 2.25k | CHECK_EQ(MqttClient_Subscribe(&client, &subscribe), MQTT_CODE_SUCCESS); |
262 | | |
263 | 338 | ret = true; |
264 | 338 | } catch ( ... ) { } |
265 | | |
266 | 2.25k | end: |
267 | 2.25k | if ( topicsArray ) { |
268 | 2.00k | delete[] topicsArray; |
269 | 2.00k | } |
270 | 2.25k | return ret; |
271 | 2.25k | } |
272 | | |
273 | 494 | bool wolfMQTTFuzzer::unsubscribe(void) { |
274 | 494 | MqttTopic* topicsArray = nullptr; |
275 | | |
276 | 494 | bool ret = false; |
277 | | |
278 | 494 | try { |
279 | 494 | Topics topics(ds); |
280 | 494 | CHECK_EQ(topics.Generate(), true); |
281 | | |
282 | 494 | MqttUnsubscribe unsubscribe; |
283 | | |
284 | 494 | memset(&unsubscribe, 0, sizeof(unsubscribe)); |
285 | | |
286 | 494 | unsubscribe.packet_id = GetPacketId(); |
287 | 494 | topicsArray = topics.ToArray(); |
288 | 494 | unsubscribe.topic_count = topics.Size(); |
289 | 494 | unsubscribe.topics = topicsArray; |
290 | | |
291 | 494 | CHECK_EQ(MqttClient_Unsubscribe(&client, &unsubscribe), MQTT_CODE_SUCCESS); |
292 | | |
293 | 73 | ret = true; |
294 | 73 | } catch ( ... ) { } |
295 | | |
296 | 494 | end: |
297 | 494 | if ( topicsArray ) { |
298 | 445 | delete[] topicsArray; |
299 | 445 | } |
300 | 494 | return ret; |
301 | 494 | } |
302 | | |
303 | 861 | bool wolfMQTTFuzzer::publish(void) { |
304 | 861 | bool ret = false; |
305 | | |
306 | 861 | try { |
307 | 861 | MqttPublish publish; |
308 | | |
309 | 861 | memset(&publish, 0, sizeof(publish)); |
310 | | |
311 | 861 | publish.retain = ds.Get<bool>() ? 1 : 0; |
312 | 861 | publish.qos = GetQoS(); |
313 | 861 | publish.duplicate = ds.Get<bool>() ? 1 : 0; |
314 | | |
315 | 861 | const auto topic_str = ds.Get<std::string>(); |
316 | 861 | publish.topic_name = topic_str.c_str(); |
317 | | |
318 | 861 | publish.packet_id = GetPacketId(); |
319 | | |
320 | 861 | auto buffer = ds.GetData(0); |
321 | 861 | publish.buffer = buffer.data(); |
322 | 861 | publish.total_len = buffer.size(); |
323 | | |
324 | 861 | if ( DEBUG ) { |
325 | 0 | printf("publish: topic name size: %zu\n", strlen(topic_str.c_str())); |
326 | 0 | } |
327 | | |
328 | 861 | CHECK_EQ(MqttClient_Publish(&client, &publish), MQTT_CODE_SUCCESS); |
329 | | |
330 | 366 | ret = true; |
331 | 366 | } catch ( ... ) { } |
332 | | |
333 | 861 | end: |
334 | 861 | return ret; |
335 | 861 | } |
336 | | |
337 | 910 | bool wolfMQTTFuzzer::ping(void) { |
338 | 910 | bool ret = false; |
339 | | |
340 | 910 | MqttPing ping; |
341 | | |
342 | 910 | memset(&ping, 0, sizeof(ping)); |
343 | | |
344 | 910 | CHECK_EQ(MqttClient_Ping_ex(&client, &ping), true); |
345 | |
|
346 | 0 | ret = true; |
347 | |
|
348 | 910 | end: |
349 | 910 | return ret; |
350 | 0 | } |
351 | | |
352 | 2.66k | bool wolfMQTTFuzzer::wait(void) { |
353 | 2.66k | bool ret = false; |
354 | | |
355 | 2.66k | CHECK_EQ(MqttClient_WaitMessage(&client, 1000), MQTT_CODE_SUCCESS); |
356 | | |
357 | 15 | ret = true; |
358 | | |
359 | 2.66k | end: |
360 | 2.66k | return ret; |
361 | 15 | } |
362 | | |
363 | | wolfMQTTFuzzer::wolfMQTTFuzzer(fuzzing::datasource::Datasource& ds) : |
364 | 2.79k | Base(ds) |
365 | 2.79k | { } |
366 | | |
367 | 2.79k | wolfMQTTFuzzer::~wolfMQTTFuzzer() { |
368 | 2.79k | this->free(tx_buf); |
369 | 2.79k | this->free(rx_buf); |
370 | 2.79k | } |
371 | | |
372 | 2.79k | bool wolfMQTTFuzzer::Initialize(void) { |
373 | 2.79k | bool ret = false; |
374 | | |
375 | 2.79k | try { |
376 | | /* net */ |
377 | 2.79k | { |
378 | 2.79k | memset(&net, 0, sizeof(net)); |
379 | | |
380 | 2.79k | net.connect = mqtt_connect; |
381 | 2.79k | net.read = mqtt_recv; |
382 | 2.79k | net.write = mqtt_write; |
383 | 2.79k | net.disconnect = mqtt_disconnect; |
384 | 2.79k | net.context = this; |
385 | 2.79k | } |
386 | | |
387 | | /* client */ |
388 | 2.79k | { |
389 | 2.79k | memset(&client, 0, sizeof(client)); |
390 | | |
391 | 2.79k | tx_size = ds.Get<uint16_t>(); |
392 | 2.79k | tx_size = 4096; |
393 | 2.79k | tx_buf = (uint8_t*)this->malloc(tx_size); |
394 | 2.79k | rx_size = ds.Get<uint16_t>(); |
395 | 2.79k | rx_size = 4096; |
396 | 2.79k | rx_buf = (uint8_t*)this->malloc(rx_size); |
397 | 2.79k | memset(tx_buf, 0, tx_size); |
398 | 2.79k | memset(rx_buf, 0, rx_size); |
399 | | |
400 | 2.79k | client.msg_cb = mqtt_message_cb; |
401 | 2.79k | client.tx_buf = tx_buf; |
402 | 2.79k | client.tx_buf_len = tx_size; |
403 | 2.79k | client.rx_buf = rx_buf; |
404 | 2.79k | client.rx_buf_len = rx_size; |
405 | 2.79k | client.cmd_timeout_ms = 1000; |
406 | 2.79k | } |
407 | | |
408 | | /* connect */ |
409 | 2.79k | MqttMessage lwt_msg; |
410 | 2.79k | { |
411 | 2.79k | memset(&connect, 0, sizeof(connect)); |
412 | | |
413 | 2.79k | connect.keep_alive_sec = 1; |
414 | 2.79k | connect.clean_session = ds.Get<bool>() ? 1 : 0; |
415 | 2.79k | client_id = ds.Get<std::string>(); |
416 | 2.79k | connect.client_id = client_id.c_str(); |
417 | 2.79k | connect.enable_lwt = ds.Get<bool>() ? 1 : 0; |
418 | 2.79k | } |
419 | | |
420 | 2.79k | std::string lwt_topic_name; |
421 | 2.79k | std::vector<uint8_t> lwt_buffer; |
422 | | |
423 | 2.79k | if ( connect.enable_lwt ) { |
424 | 30 | lwt_topic_name = ds.Get<std::string>(); |
425 | 30 | lwt_buffer = ds.GetData(0); |
426 | | |
427 | 30 | connect.lwt_msg = &lwt_msg; |
428 | 30 | lwt_msg.qos = GetQoS(); |
429 | 30 | lwt_msg.retain = ds.Get<bool>() ? 1 : 0; |
430 | 30 | lwt_msg.topic_name = lwt_topic_name.c_str(); |
431 | 30 | lwt_msg.buffer = lwt_buffer.data(); |
432 | 30 | lwt_msg.total_len = lwt_buffer.size(); |
433 | 30 | } |
434 | | |
435 | 2.79k | CHECK_EQ(MqttSocket_Init(&client, &net), MQTT_CODE_SUCCESS); |
436 | | |
437 | | #if 0 |
438 | | if ( ds.Get<bool>() ) { |
439 | | //CHECK_EQ(MqttClient_SetPropertyCallback(&client, mqtt_property_cb, NULL); |
440 | | } |
441 | | #endif |
442 | | |
443 | 2.79k | CHECK_EQ(MqttClient_NetConnect(&client, "dummy", 12345, 1000, 0, NULL), MQTT_CODE_SUCCESS); |
444 | 2.79k | CHECK_EQ(MqttClient_Connect(&client, &connect), MQTT_CODE_SUCCESS); |
445 | | |
446 | 1.83k | ret = true; |
447 | | |
448 | 1.83k | } catch ( ... ) { |
449 | 155 | return false; |
450 | 155 | } |
451 | | |
452 | 2.64k | end: |
453 | 2.64k | return ret; |
454 | 2.79k | } |
455 | | |
456 | 1.68k | void wolfMQTTFuzzer::Run(void) { |
457 | 1.68k | try { |
458 | 1.68k | const auto numActions = ds.Get<uint8_t>() % 20; |
459 | | |
460 | 10.6k | for (size_t i = 0; i < numActions; i++) { |
461 | 10.4k | switch ( ds.Get<uint8_t>() ) { |
462 | 2.25k | case 0: |
463 | 2.25k | subscribe(); |
464 | 2.25k | break; |
465 | 494 | case 1: |
466 | 494 | unsubscribe(); |
467 | 494 | break; |
468 | 861 | case 2: |
469 | 861 | publish(); |
470 | 861 | break; |
471 | 910 | case 3: |
472 | 910 | ping(); |
473 | 910 | break; |
474 | 2.66k | case 4: |
475 | 2.66k | wait(); |
476 | 2.66k | break; |
477 | 10.4k | } |
478 | 10.4k | } |
479 | | |
480 | 237 | MqttClient_NetDisconnect(&client); |
481 | 1.45k | } catch ( ... ) { } |
482 | 1.68k | } |
483 | | |
484 | 8.63k | int wolfMQTTFuzzer::recv(byte* buf, const int buf_len) { |
485 | 8.63k | try { |
486 | 8.63k | const auto data = ds.GetData(0); |
487 | 8.63k | const size_t copySize = buf_len > data.size() ? data.size() : buf_len; |
488 | 8.63k | if ( copySize ) { |
489 | 5.74k | memcpy(buf, data.data(), copySize); |
490 | 5.74k | } |
491 | 8.63k | if ( DEBUG ) |
492 | 0 | { |
493 | 0 | printf("Recv: %zu bytes (%d requested)\n", copySize, buf_len); |
494 | 0 | for (size_t i = 0; i < copySize; i++) { |
495 | 0 | printf("%02X ", data[i]); |
496 | 0 | } |
497 | 0 | printf("\n"); |
498 | 0 | } |
499 | 8.63k | return copySize; |
500 | 8.63k | } catch ( ... ) { |
501 | 2.06k | if ( DEBUG ) printf("Recv: -1\n"); |
502 | 2.06k | return -1; |
503 | 2.06k | } |
504 | 8.63k | } |
505 | | |
506 | 4.64k | int wolfMQTTFuzzer::write(const int buf_len) { |
507 | 4.64k | try { |
508 | 4.64k | if ( ds.Get<bool>() == true ) { |
509 | 427 | if ( DEBUG ) printf("write: -1\n"); |
510 | 427 | return -1; |
511 | 427 | } |
512 | | |
513 | 4.21k | const auto ret = (int)(ds.Get<uint32_t>() % (buf_len+1)); |
514 | 4.21k | if ( DEBUG ) printf("write: %d bytes (%d requested)\n", ret, buf_len); |
515 | 4.21k | return ret; |
516 | 4.64k | } catch ( ... ) { |
517 | 235 | return -1; |
518 | 235 | } |
519 | 4.64k | } |
520 | | |
521 | 2.79k | extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { |
522 | 2.79k | fuzzing::datasource::Datasource ds(data, size); |
523 | 2.79k | wolfMQTTFuzzer fuzzer(ds); |
524 | | |
525 | 2.79k | CHECK_EQ(fuzzer.Initialize(), true); |
526 | | |
527 | 1.68k | fuzzer.Run(); |
528 | | |
529 | 2.79k | end: |
530 | 2.79k | return 0; |
531 | 1.68k | } |