/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 | } |