/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 | 23.7k | #define CHECK_EQ(expr, res) if ( (expr) != (res) ) { goto end; } |
7 | | #define CHECK_NE(expr, res) if ( (expr) == (res) ) { goto end; } |
8 | | |
9 | 5.57k | #define BADPTR ((void*)0x12) |
10 | 2.02k | #define MAX_TOPICS 50 |
11 | | |
12 | 31.2k | #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.55k | ds(ds) |
25 | 8.55k | { } |
26 | | |
27 | 8.55k | Base::~Base() { } |
28 | | |
29 | 4.16k | MqttQoS Base::GetQoS(void) const { |
30 | 4.16k | switch ( ds.Get<uint8_t>() % 3 ) { |
31 | 3.33k | case 0: |
32 | 3.33k | return MQTT_QOS_0; |
33 | 412 | case 1: |
34 | 412 | return MQTT_QOS_1; |
35 | 375 | case 2: |
36 | 375 | return MQTT_QOS_2; |
37 | 0 | default: |
38 | | /* Silence compiler warning */ |
39 | 0 | abort(); |
40 | 4.16k | } |
41 | 4.16k | } |
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 | 3.74k | Base(ds) { |
56 | 3.74k | topic = new MqttTopic; |
57 | 3.74k | } |
58 | | |
59 | 3.74k | Topic::~Topic() { |
60 | 3.74k | delete topic; |
61 | 3.74k | } |
62 | | |
63 | 3.74k | bool Topic::Generate(void) { |
64 | 3.74k | bool ret; |
65 | | |
66 | 3.74k | memset(topic, 0, sizeof(*topic)); |
67 | | |
68 | 3.74k | strings.push_back( ds.Get<std::string>() ); |
69 | 3.74k | topic->topic_filter = strings.back().c_str(); |
70 | | |
71 | 3.74k | topic->qos = GetQoS(); |
72 | | |
73 | 3.74k | ret = true; |
74 | 3.74k | end: |
75 | 3.21k | return ret; |
76 | 3.74k | } |
77 | | |
78 | 1.63k | MqttTopic Topic::Get(void) { |
79 | 1.63k | return *topic; |
80 | 1.63k | } |
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 | 2.02k | Base(ds) |
95 | 2.02k | { } |
96 | | |
97 | 2.02k | Topics::~Topics() { |
98 | 3.74k | for (auto& t : topics) { |
99 | 3.74k | delete t; |
100 | 3.74k | } |
101 | 2.02k | } |
102 | | |
103 | 2.02k | bool Topics::Generate(void) { |
104 | 2.02k | bool ret = false; |
105 | | |
106 | 2.02k | try { |
107 | 2.02k | const auto numTopics = ds.Get<uint16_t>() % (MAX_TOPICS+1); |
108 | | |
109 | 5.76k | for (size_t i = 0; i < numTopics; i++) { |
110 | 3.74k | topics.push_back(new Topic(ds)); |
111 | 3.74k | CHECK_EQ(topics.back()->Generate(), true); |
112 | 3.74k | } |
113 | | |
114 | 2.02k | ret = true; |
115 | 2.02k | } catch ( ... ) { } |
116 | | |
117 | 2.02k | end: |
118 | 2.02k | return ret; |
119 | 2.02k | } |
120 | | |
121 | 1.42k | MqttTopic* Topics::ToArray(void) { |
122 | 1.42k | auto ret = new MqttTopic[topics.size()]; |
123 | | |
124 | 3.06k | for (size_t i = 0; i < Size(); i++) { |
125 | 1.63k | ret[i] = topics[i]->Get(); |
126 | 1.63k | } |
127 | 1.42k | return ret; |
128 | 1.42k | } |
129 | | |
130 | 4.48k | size_t Topics::Size(void) const { |
131 | 4.48k | return topics.size(); |
132 | 4.48k | } |
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.61k | { |
167 | 2.61k | (void)context; |
168 | 2.61k | (void)host; |
169 | 2.61k | (void)port; |
170 | 2.61k | (void)timeout_ms; |
171 | | |
172 | 2.61k | return MQTT_CODE_SUCCESS; |
173 | 2.61k | } |
174 | | |
175 | | static int mqtt_recv(void *context, byte* buf, int buf_len, int timeout_ms) |
176 | 19.5k | { |
177 | 19.5k | (void)context; |
178 | 19.5k | (void)timeout_ms; |
179 | | |
180 | 19.5k | auto fuzzer = static_cast<wolfMQTTFuzzer*>(context); |
181 | 19.5k | return fuzzer->recv(buf, buf_len); |
182 | 19.5k | } |
183 | | |
184 | | static int mqtt_write(void *context, const byte* buf, int buf_len, int timeout_ms) |
185 | 8.95k | { |
186 | 8.95k | (void)context; |
187 | 8.95k | (void)timeout_ms; |
188 | 8.95k | (void)buf; |
189 | | |
190 | 8.95k | auto fuzzer = static_cast<wolfMQTTFuzzer*>(context); |
191 | 8.95k | return fuzzer->write(buf_len); |
192 | 8.95k | } |
193 | | |
194 | | static int mqtt_disconnect(void *context) |
195 | 132 | { |
196 | 132 | (void)context; |
197 | | |
198 | 132 | return MQTT_CODE_SUCCESS; |
199 | 132 | } |
200 | | |
201 | | static int mqtt_message_cb(MqttClient *client, MqttMessage *msg, byte msg_new, byte msg_done) |
202 | 4.33k | { |
203 | 4.33k | return MQTT_CODE_SUCCESS; |
204 | 4.33k | } |
205 | | |
206 | 5.45k | void* wolfMQTTFuzzer::malloc(const size_t n) { |
207 | 5.45k | return n == 0 ? BADPTR : ::malloc(n); |
208 | 5.45k | } |
209 | | |
210 | 5.57k | void wolfMQTTFuzzer::free(void* ptr) { |
211 | 5.57k | if ( ptr == BADPTR ) { |
212 | 0 | return; |
213 | 0 | } |
214 | | |
215 | 5.57k | ::free(ptr); |
216 | 5.57k | } |
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.18k | word16 wolfMQTTFuzzer::GetPacketId(void) const { |
229 | 2.18k | return ds.Get<word16>(); |
230 | 2.18k | } |
231 | | |
232 | 1.49k | bool wolfMQTTFuzzer::subscribe(void) { |
233 | 1.49k | MqttTopic* topicsArray = nullptr; |
234 | 1.49k | MqttSubscribe* subscribe = nullptr; |
235 | | |
236 | 1.49k | bool ret = false; |
237 | | |
238 | 1.49k | try { |
239 | 1.49k | Topics topics(ds); |
240 | 1.49k | CHECK_EQ(topics.Generate(), true); |
241 | | |
242 | 1.03k | subscribe = new MqttSubscribe; |
243 | 1.03k | memset(subscribe, 0, sizeof(*subscribe)); |
244 | | |
245 | 1.03k | subscribe->packet_id = GetPacketId(); |
246 | 1.03k | topicsArray = topics.ToArray(); |
247 | 1.03k | subscribe->topic_count = topics.Size(); |
248 | 1.03k | subscribe->topics = topicsArray; |
249 | | |
250 | 1.03k | CHECK_EQ(MqttClient_Subscribe(client, subscribe), MQTT_CODE_SUCCESS); |
251 | | |
252 | 338 | ret = true; |
253 | 338 | } catch ( ... ) { } |
254 | | |
255 | 1.49k | end: |
256 | 1.49k | if ( topicsArray ) { |
257 | 1.01k | delete[] topicsArray; |
258 | 1.01k | } |
259 | | |
260 | 1.49k | if ( subscribe ) { |
261 | 1.03k | delete subscribe; |
262 | 1.03k | } |
263 | 1.49k | return ret; |
264 | 1.49k | } |
265 | | |
266 | 531 | bool wolfMQTTFuzzer::unsubscribe(void) { |
267 | 531 | MqttTopic* topicsArray = nullptr; |
268 | 531 | MqttUnsubscribe* unsubscribe = nullptr; |
269 | | |
270 | 531 | bool ret = false; |
271 | | |
272 | 531 | try { |
273 | 531 | Topics topics(ds); |
274 | 531 | CHECK_EQ(topics.Generate(), true); |
275 | | |
276 | 422 | unsubscribe = new MqttUnsubscribe; |
277 | 422 | memset(unsubscribe, 0, sizeof(*unsubscribe)); |
278 | | |
279 | 422 | unsubscribe->packet_id = GetPacketId(); |
280 | 422 | topicsArray = topics.ToArray(); |
281 | 422 | unsubscribe->topic_count = topics.Size(); |
282 | 422 | unsubscribe->topics = topicsArray; |
283 | | |
284 | 422 | CHECK_EQ(MqttClient_Unsubscribe(client, unsubscribe), MQTT_CODE_SUCCESS); |
285 | | |
286 | 66 | ret = true; |
287 | 66 | } catch ( ... ) { } |
288 | | |
289 | 531 | end: |
290 | 531 | if ( topicsArray ) { |
291 | 414 | delete[] topicsArray; |
292 | 414 | } |
293 | | |
294 | 531 | if ( unsubscribe ) { |
295 | 422 | delete unsubscribe; |
296 | 422 | } |
297 | | |
298 | 531 | return ret; |
299 | 531 | } |
300 | | |
301 | 884 | bool wolfMQTTFuzzer::publish(void) { |
302 | 884 | bool ret = false; |
303 | 884 | MqttPublish* publish = nullptr; |
304 | | |
305 | 884 | try { |
306 | 884 | publish = new MqttPublish; |
307 | 884 | memset(publish, 0, sizeof(*publish)); |
308 | | |
309 | 884 | publish->retain = ds.Get<bool>() ? 1 : 0; |
310 | 884 | publish->qos = GetQoS(); |
311 | 884 | publish->duplicate = ds.Get<bool>() ? 1 : 0; |
312 | | |
313 | 884 | const auto topic_str = ds.Get<std::string>(); |
314 | 884 | publish->topic_name = topic_str.c_str(); |
315 | | |
316 | 884 | publish->packet_id = GetPacketId(); |
317 | | |
318 | 884 | auto buffer = ds.GetData(0); |
319 | 884 | publish->buffer = buffer.data(); |
320 | 884 | publish->total_len = buffer.size(); |
321 | | |
322 | 884 | if ( DEBUG ) { |
323 | 0 | printf("publish: topic name size: %zu\n", strlen(topic_str.c_str())); |
324 | 0 | } |
325 | | |
326 | 884 | CHECK_EQ(MqttClient_Publish(client, publish), MQTT_CODE_SUCCESS); |
327 | | |
328 | 383 | ret = true; |
329 | 383 | } catch ( ... ) { } |
330 | | |
331 | 884 | end: |
332 | 884 | if ( publish ) { |
333 | 884 | delete publish; |
334 | 884 | } |
335 | | |
336 | 884 | return ret; |
337 | 884 | } |
338 | | |
339 | 850 | bool wolfMQTTFuzzer::ping(void) { |
340 | 850 | bool ret = false; |
341 | | |
342 | 850 | MqttPing* ping = new MqttPing; |
343 | 850 | memset(ping, 0, sizeof(*ping)); |
344 | | |
345 | 850 | CHECK_EQ(MqttClient_Ping_ex(client, ping), true); |
346 | |
|
347 | 0 | ret = true; |
348 | |
|
349 | 850 | end: |
350 | 850 | delete ping; |
351 | | |
352 | 850 | return ret; |
353 | 0 | } |
354 | | |
355 | 3.67k | bool wolfMQTTFuzzer::wait(void) { |
356 | 3.67k | bool ret = false; |
357 | | |
358 | 3.67k | CHECK_EQ(MqttClient_WaitMessage(client, 1000), MQTT_CODE_SUCCESS); |
359 | | |
360 | 835 | ret = true; |
361 | | |
362 | 3.67k | end: |
363 | 3.67k | return ret; |
364 | 835 | } |
365 | | |
366 | | wolfMQTTFuzzer::wolfMQTTFuzzer(fuzzing::datasource::Datasource& ds) : |
367 | 2.78k | Base(ds) { |
368 | 2.78k | client = new MqttClient; |
369 | 2.78k | net = new MqttNet; |
370 | 2.78k | connect = new MqttConnect; |
371 | 2.78k | } |
372 | | |
373 | 2.78k | wolfMQTTFuzzer::~wolfMQTTFuzzer() { |
374 | 2.78k | this->free(tx_buf); |
375 | 2.78k | this->free(rx_buf); |
376 | 2.78k | delete client; |
377 | 2.78k | delete net; |
378 | 2.78k | delete connect; |
379 | 2.78k | } |
380 | | |
381 | 2.78k | bool wolfMQTTFuzzer::Initialize(void) { |
382 | 2.78k | bool ret = false; |
383 | 2.78k | MqttMessage* lwt_msg = nullptr; |
384 | | |
385 | 2.78k | try { |
386 | | /* net */ |
387 | 2.78k | { |
388 | 2.78k | memset(net, 0, sizeof(*net)); |
389 | | |
390 | 2.78k | net->connect = mqtt_connect; |
391 | 2.78k | net->read = mqtt_recv; |
392 | 2.78k | net->write = mqtt_write; |
393 | 2.78k | net->disconnect = mqtt_disconnect; |
394 | 2.78k | net->context = this; |
395 | 2.78k | } |
396 | | |
397 | | /* client */ |
398 | 2.78k | { |
399 | 2.78k | memset(client, 0, sizeof(*client)); |
400 | | |
401 | 2.78k | tx_size = ds.Get<uint16_t>(); |
402 | 2.78k | tx_size = 4096; |
403 | 2.78k | tx_buf = (uint8_t*)this->malloc(tx_size); |
404 | 2.78k | rx_size = ds.Get<uint16_t>(); |
405 | 2.78k | rx_size = 4096; |
406 | 2.78k | rx_buf = (uint8_t*)this->malloc(rx_size); |
407 | 2.78k | memset(tx_buf, 0, tx_size); |
408 | 2.78k | memset(rx_buf, 0, rx_size); |
409 | | |
410 | 2.78k | client->msg_cb = mqtt_message_cb; |
411 | 2.78k | client->tx_buf = tx_buf; |
412 | 2.78k | client->tx_buf_len = tx_size; |
413 | 2.78k | client->rx_buf = rx_buf; |
414 | 2.78k | client->rx_buf_len = rx_size; |
415 | 2.78k | client->cmd_timeout_ms = 1000; |
416 | 2.78k | } |
417 | | |
418 | | /* connect */ |
419 | 2.78k | { |
420 | 2.78k | memset(connect, 0, sizeof(*connect)); |
421 | | |
422 | 2.78k | connect->keep_alive_sec = 1; |
423 | 2.78k | connect->clean_session = ds.Get<bool>() ? 1 : 0; |
424 | 2.78k | client_id = ds.Get<std::string>(); |
425 | 2.78k | connect->client_id = client_id.c_str(); |
426 | 2.78k | connect->enable_lwt = ds.Get<bool>() ? 1 : 0; |
427 | 2.78k | } |
428 | | |
429 | 2.78k | std::string lwt_topic_name; |
430 | 2.78k | std::vector<uint8_t> lwt_buffer; |
431 | | |
432 | 2.78k | if ( connect->enable_lwt ) { |
433 | 47 | lwt_topic_name = ds.Get<std::string>(); |
434 | 47 | lwt_buffer = ds.GetData(0); |
435 | | |
436 | 47 | lwt_msg = new MqttMessage; |
437 | 47 | memset(lwt_msg, 0, sizeof(*lwt_msg)); |
438 | | |
439 | 47 | connect->lwt_msg = lwt_msg; |
440 | 47 | lwt_msg->qos = GetQoS(); |
441 | 47 | lwt_msg->retain = ds.Get<bool>() ? 1 : 0; |
442 | 47 | lwt_msg->topic_name = lwt_topic_name.c_str(); |
443 | 47 | lwt_msg->buffer = lwt_buffer.data(); |
444 | 47 | lwt_msg->total_len = lwt_buffer.size(); |
445 | 47 | } |
446 | | |
447 | 2.78k | 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.78k | CHECK_EQ(MqttClient_NetConnect(client, "dummy", 12345, 1000, 0, NULL), MQTT_CODE_SUCCESS); |
456 | 2.78k | 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.78k | end: |
464 | 2.78k | if ( lwt_msg ) { |
465 | 36 | delete lwt_msg; |
466 | 36 | } |
467 | 2.78k | return ret; |
468 | 2.78k | } |
469 | | |
470 | 1.79k | void wolfMQTTFuzzer::Run(void) { |
471 | 1.79k | try { |
472 | 1.79k | const auto numActions = ds.Get<uint8_t>() % 20; |
473 | | |
474 | 10.1k | for (size_t i = 0; i < numActions; i++) { |
475 | 10.0k | switch ( ds.Get<uint8_t>() ) { |
476 | 1.49k | case 0: |
477 | 1.49k | subscribe(); |
478 | 1.49k | break; |
479 | 531 | case 1: |
480 | 531 | unsubscribe(); |
481 | 531 | break; |
482 | 884 | case 2: |
483 | 884 | publish(); |
484 | 884 | break; |
485 | 850 | case 3: |
486 | 850 | ping(); |
487 | 850 | break; |
488 | 3.67k | case 4: |
489 | 3.67k | wait(); |
490 | 3.67k | break; |
491 | 10.0k | } |
492 | 10.0k | } |
493 | | |
494 | 154 | MqttClient_NetDisconnect(client); |
495 | 1.66k | } catch ( ... ) { } |
496 | 1.79k | } |
497 | | |
498 | 19.5k | int wolfMQTTFuzzer::recv(byte* buf, const int buf_len) { |
499 | 19.5k | try { |
500 | 19.5k | const auto data = ds.GetData(0); |
501 | 19.5k | const size_t copySize = buf_len > data.size() ? data.size() : buf_len; |
502 | 19.5k | if ( copySize ) { |
503 | 14.3k | memcpy(buf, data.data(), copySize); |
504 | 14.3k | } |
505 | 19.5k | 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 | 19.5k | return copySize; |
514 | 19.5k | } catch ( ... ) { |
515 | 1.88k | if ( DEBUG ) printf("Recv: -1\n"); |
516 | 1.88k | return -1; |
517 | 1.88k | } |
518 | 19.5k | } |
519 | | |
520 | 8.95k | int wolfMQTTFuzzer::write(const int buf_len) { |
521 | 8.95k | try { |
522 | 8.95k | if ( ds.Get<bool>() == true ) { |
523 | 844 | if ( DEBUG ) printf("write: -1\n"); |
524 | 844 | return -1; |
525 | 844 | } |
526 | | |
527 | 8.11k | const auto ret = (int)(ds.Get<uint32_t>() % (buf_len+1)); |
528 | 8.11k | if ( DEBUG ) printf("write: %d bytes (%d requested)\n", ret, buf_len); |
529 | 8.11k | return ret; |
530 | 8.95k | } catch ( ... ) { |
531 | 625 | return -1; |
532 | 625 | } |
533 | 8.95k | } |
534 | | |
535 | 2.78k | extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { |
536 | 2.78k | fuzzing::datasource::Datasource ds(data, size); |
537 | 2.78k | wolfMQTTFuzzer fuzzer(ds); |
538 | | |
539 | 2.78k | CHECK_EQ(fuzzer.Initialize(), true); |
540 | | |
541 | 1.79k | fuzzer.Run(); |
542 | | |
543 | 2.78k | end: |
544 | 2.78k | return 0; |
545 | 1.79k | } |