1 /* Copyright 2021 Google LLC
2 Licensed under the Apache License, Version 2.0 (the "License");
3 you may not use this file except in compliance with the License.
4 You may obtain a copy of the License at
5 http://www.apache.org/licenses/LICENSE-2.0
6 Unless required by applicable law or agreed to in writing, software
7 distributed under the License is distributed on an "AS IS" BASIS,
8 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 See the License for the specific language governing permissions and
10 limitations under the License.
11 */
12
13 #include "dnsmasq.h"
14
15 extern void fuzz_blockdata_cleanup();
16
17 // Simple garbage collector
18 #define GB_SIZE 100
19
20 void *pointer_arr[GB_SIZE];
21 static int pointer_idx = 0;
22
23 // If the garbage collector is used then this must be called as first thing
24 // during a fuzz run.
gb_init()25 void gb_init() {
26 pointer_idx = 0;
27
28 for (int i = 0; i < GB_SIZE; i++) {
29 pointer_arr[i] = NULL;
30 }
31 }
32
gb_cleanup()33 void gb_cleanup() {
34 for(int i = 0; i < GB_SIZE; i++) {
35 if (pointer_arr[i] != NULL) {
36 free(pointer_arr[i]);
37 }
38 }
39 }
40
get_null_terminated(const uint8_t ** data,size_t * size)41 char *get_null_terminated(const uint8_t **data, size_t *size) {
42 #define STR_SIZE 75
43 if (*size < STR_SIZE || (int)*size < 0) {
44 return NULL;
45 }
46
47 char *new_s = malloc(STR_SIZE + 1);
48 memcpy(new_s, *data, STR_SIZE);
49 new_s[STR_SIZE] = '\0';
50
51 *data = *data+STR_SIZE;
52 *size -= STR_SIZE;
53 return new_s;
54 }
55
gb_get_random_data(const uint8_t ** data,size_t * size,size_t to_get)56 char *gb_get_random_data(const uint8_t **data, size_t *size, size_t to_get) {
57 if (*size < to_get || (int)*size < 0) {
58 return NULL;
59 }
60
61 char *new_s = malloc(to_get);
62 memcpy(new_s, *data, to_get);
63
64 pointer_arr[pointer_idx++] = (void*)new_s;
65
66 *data = *data + to_get;
67 *size -= to_get;
68
69 return new_s;
70 }
71
gb_get_null_terminated(const uint8_t ** data,size_t * size)72 char *gb_get_null_terminated(const uint8_t **data, size_t *size) {
73
74 char *nstr = get_null_terminated(data, size);
75 if (nstr == NULL) {
76 return NULL;
77 }
78 pointer_arr[pointer_idx++] = (void*)nstr;
79 return nstr;
80 }
81
gb_alloc_data(size_t len)82 char *gb_alloc_data(size_t len) {
83 char *ptr = calloc(1, len);
84 pointer_arr[pointer_idx++] = (void*)ptr;
85
86 return ptr;
87 }
88
get_short(const uint8_t ** data,size_t * size)89 short get_short(const uint8_t **data, size_t *size) {
90 if (*size <= 0) return 0;
91 short c = (short)(*data)[0];
92 *data += 1;
93 *size-=1;
94 return c;
95 }
96
get_int(const uint8_t ** data,size_t * size)97 int get_int(const uint8_t **data, size_t *size) {
98 if (*size <= 4) return 0;
99 const uint8_t *ptr = *data;
100 int val = *((int*)ptr);
101 *data += 4;
102 *size -= 4;
103 return val;
104 }
105 // end simple garbage collector.
106
107 const uint8_t *syscall_data = NULL;
108 size_t syscall_size = 0;
109
110
fuzz_ioctl(int fd,unsigned long request,void * arg)111 int fuzz_ioctl(int fd, unsigned long request, void *arg) {
112 int fd2 = fd;
113 unsigned long request2 = request;
114 void *arg_ptr = arg;
115
116 // SIOCGSTAMP
117 if (request == SIOCGSTAMP) {
118 struct timeval *tv = (struct timeval*)arg_ptr;
119 if (tv == NULL) {
120 return 0;
121 }
122
123 char *rand_tv = gb_get_random_data(&syscall_data, &syscall_size, sizeof(struct timeval));
124 if (rand_tv == NULL) {
125 return -1;
126 }
127
128 memcpy(tv, rand_tv, sizeof(struct timeval));
129 return 0;
130 }
131
132 if (request == SIOCGIFNAME) {
133 //printf("We got a SIOCGIFNAME\n");
134 struct ifreq *ifr = (struct ifreq*)arg_ptr;
135 if (ifr == NULL) {
136 return -1;
137 }
138 for (int i = 0; i < IF_NAMESIZE; i++) {
139 if (syscall_size > 0 && syscall_data != NULL) {
140 ifr->ifr_name[i] = (char)*syscall_data;
141 syscall_data += 1;
142 syscall_size -= 1;
143 }
144 else {
145 ifr->ifr_name[i] = 'A';
146 }
147 }
148 ifr->ifr_name[IF_NAMESIZE-1] = '\0';
149 return 0;
150 //return -1;
151 }
152 if (request == SIOCGIFFLAGS) {
153 return 0;
154 }
155 if (request == SIOCGIFADDR) {
156 return 0;
157 }
158
159 //
160 int retval = ioctl(fd2, request2, arg_ptr);
161 return retval;
162 }
163
164
165 // Sysytem call wrappers
166 static char v = 0;
fuzz_recvmsg(int sockfd,struct msghdr * msg,int flags)167 ssize_t fuzz_recvmsg(int sockfd, struct msghdr *msg, int flags) {
168
169 struct iovec *target = msg->msg_iov;
170
171 //printf("recvmsg 1 \n");
172 if (syscall_size > 1) {
173 char r = *syscall_data;
174 syscall_data += 1;
175 syscall_size -= 1;
176
177 if (r == 12) {
178 //printf("recvmsg 2\n");
179 return -1;
180 }
181 }
182
183 int j = 0;
184 if (msg->msg_control != NULL) {
185 for (;j < CMSG_SPACE(sizeof(struct in_pktinfo)); j++)
186 {
187 if (syscall_size > 0 && syscall_data != NULL) {
188 ((char*)msg->msg_control)[j] = *syscall_data;
189 syscall_data += 1;
190 syscall_size -= 1;
191 }
192 else {
193 ((char*)msg->msg_control)[j] = 'A';
194 }
195 }
196 }
197
198 int i = 0;
199 for (; i < target->iov_len; i++) {
200 if (syscall_size > 0 && syscall_data != NULL) {
201 ((char*)target->iov_base)[i] = *syscall_data;
202 syscall_data += 1;
203 syscall_size -= 1;
204 }
205 else {
206 ((char*)target->iov_base)[i] = 'A';
207 }
208 }
209
210 if (msg->msg_namelen > 0) {
211 memset(msg->msg_name, 0, msg->msg_namelen);
212 }
213
214 return i;
215 }
216
217
218 // dnsmasq specific stuff
init_daemon(const uint8_t ** data2,size_t * size2)219 int init_daemon(const uint8_t **data2, size_t *size2) {
220 const uint8_t *data = *data2;
221 size_t size = *size2;
222
223 int retval = 0;
224
225 #define CLEAN_IF_NULL(arg) if (arg == NULL) goto cleanup;
226
227 // Initialize daemon
228 daemon = (struct daemon*)gb_alloc_data(sizeof(struct daemon));
229 CLEAN_IF_NULL(daemon)
230
231 // daemon misc
232 daemon->max_ttl = get_int(&data, &size);
233 daemon->neg_ttl = get_int(&data, &size);
234 daemon->local_ttl = get_int(&data, &size);
235 daemon->min_cache_ttl = get_int(&data, &size);
236
237 // daemon->namebuff.
238 char *daemon_namebuff = gb_get_null_terminated(&data, &size);
239 daemon->namebuff = daemon_namebuff;
240
241 // daemon->naptr
242 struct naptr *naptr_ptr = (struct naptr*)gb_alloc_data(sizeof(struct naptr));
243 char *naptr_name = gb_get_null_terminated(&data, &size);
244 char *naptr_replace = gb_get_null_terminated(&data, &size);
245 char *naptr_regexp = gb_get_null_terminated(&data, &size);
246 char *naptr_services = gb_get_null_terminated(&data, &size);
247 char *naptr_flags = gb_get_null_terminated(&data, &size);
248
249 CLEAN_IF_NULL(naptr_ptr)
250 CLEAN_IF_NULL(naptr_name)
251 CLEAN_IF_NULL(naptr_replace)
252 CLEAN_IF_NULL(naptr_regexp)
253 CLEAN_IF_NULL(naptr_services)
254 CLEAN_IF_NULL(naptr_flags)
255
256 naptr_ptr->name = naptr_name;
257 naptr_ptr->replace = naptr_replace;
258 naptr_ptr->regexp = naptr_regexp;
259 naptr_ptr->services = naptr_services;
260 naptr_ptr->flags = naptr_flags;
261
262 daemon->naptr = naptr_ptr;
263
264 // daemon->int_names
265 struct interface_name *int_namses = (struct interface_name*)gb_alloc_data(sizeof(struct interface_name));
266
267 char *int_name = gb_get_null_terminated(&data, &size);
268 char *int_intr = gb_get_null_terminated(&data, &size);
269 CLEAN_IF_NULL(int_namses)
270 CLEAN_IF_NULL(int_name)
271 CLEAN_IF_NULL(int_intr)
272 int_namses->name = int_name;
273 int_namses->intr = int_intr;
274
275 struct addrlist *d_addrlist = (struct addrlist*)gb_alloc_data(sizeof(struct addrlist));
276 CLEAN_IF_NULL(d_addrlist)
277 d_addrlist->flags = get_int(&data, &size);
278 d_addrlist->prefixlen = get_int(&data, &size);
279 int_namses->addr = d_addrlist;
280
281 daemon->int_names = int_namses;
282
283 if (size > *size2) {
284 goto cleanup;
285 }
286
287 // daemon->addrbuf
288 char *adbuf = gb_alloc_data(200);
289 CLEAN_IF_NULL(adbuf)
290 daemon->addrbuff = adbuf;
291
292 // daemon->auth_zones
293 struct auth_zone *d_az = (struct auth_zone*)gb_alloc_data(sizeof(struct auth_zone));
294 char *auth_domain = gb_get_null_terminated(&data, &size);
295
296 CLEAN_IF_NULL(d_az)
297 CLEAN_IF_NULL(auth_domain)
298 d_az->domain = auth_domain;
299 daemon->auth_zones = d_az;
300
301 // deamon->mxnames
302 struct mx_srv_record *mx_srv_rec = (struct mx_srv_record*)gb_alloc_data(sizeof(struct mx_srv_record));
303 char *mx_name = gb_get_null_terminated(&data, &size);
304 char *mx_target = gb_get_null_terminated(&data, &size);
305
306 CLEAN_IF_NULL(mx_srv_rec)
307 CLEAN_IF_NULL(mx_target)
308 CLEAN_IF_NULL(mx_name)
309
310 mx_srv_rec->next = daemon->mxnames;
311 daemon->mxnames = mx_srv_rec;
312 mx_srv_rec->name = mx_name;
313 mx_srv_rec->target = mx_target;
314 mx_srv_rec->issrv = get_int(&data, &size);
315 mx_srv_rec->weight = get_int(&data, &size);
316 mx_srv_rec->priority = get_int(&data, &size);
317 mx_srv_rec->srvport = get_int(&data, &size);
318 //data += 40;
319 //size -= 40;
320
321 if (size > *size2) {
322 goto cleanup;
323 }
324
325 // daemon->txt
326 struct txt_record *txt_record = (struct txt_record *)gb_alloc_data(sizeof(struct txt_record));
327 char *txt_record_name = gb_get_null_terminated(&data, &size);
328 char *txt_record_txt = gb_get_null_terminated(&data, &size);
329
330 CLEAN_IF_NULL(txt_record)
331 CLEAN_IF_NULL(txt_record_name)
332 CLEAN_IF_NULL(txt_record_txt)
333
334 txt_record->name = txt_record_name;
335 txt_record->txt = (unsigned char*)txt_record_txt;
336 txt_record->class2 = (get_short(&data, &size) % 10);
337 daemon->txt = txt_record;
338
339 // daemon->rr
340 struct txt_record *rr_record = (struct txt_record *)gb_alloc_data(sizeof(struct txt_record));
341 char *rr_record_name = gb_get_null_terminated(&data, &size);
342 char *rr_record_txt = gb_get_null_terminated(&data, &size);
343
344 CLEAN_IF_NULL(rr_record)
345 CLEAN_IF_NULL(rr_record_name)
346 CLEAN_IF_NULL(rr_record_txt)
347
348 rr_record->name = rr_record_name;
349 rr_record->txt = (unsigned char*)rr_record_txt;
350 rr_record->class2 = (get_short(&data, &size) % 10);
351 daemon->rr = rr_record;
352
353 if (size > *size2) {
354 goto cleanup;
355 }
356
357 // daemon->relay4
358 //struct dhcp_relay *dr = (struct dhcp_relay*)gb_alloc_data(sizeof(struct dhcp_relay));
359 struct dhcp_relay *dr = (struct dhcp_relay*)gb_get_random_data(&data, &size, sizeof(struct dhcp_relay));
360 char *dr_interface = gb_get_null_terminated(&data, &size);
361
362 CLEAN_IF_NULL(dr)
363 CLEAN_IF_NULL(dr_interface)
364 dr->interface = dr_interface;
365 dr->next = NULL;
366 dr->current = NULL;
367 daemon->relay4 = dr;
368
369 // deamon->bridges
370 struct dhcp_bridge *db = (struct dhcp_bridge*)gb_alloc_data(sizeof(struct dhcp_bridge));
371 char *db_interface = gb_get_null_terminated(&data, &size);
372
373 CLEAN_IF_NULL(db)
374 CLEAN_IF_NULL(db_interface)
375
376 if (strlen(db_interface) > IF_NAMESIZE) {
377 for (int i = 0; i < IF_NAMESIZE; i++) {
378 db->iface[i] = db_interface[i];
379 }
380 } else {
381 for (int i = 0; i < strlen(db_interface); i++) {
382 db->iface[i] = db_interface[i];
383 }
384 }
385
386
387 struct dhcp_bridge *db_alias = (struct dhcp_bridge*)gb_alloc_data(sizeof(struct dhcp_bridge));
388 //struct dhcp_bridge *db_alias = (struct dhcp_bridge*)gb_get_random_data(&data, &size, sizeof(struct dhcp_bridge));
389 char *db_alias_interface = gb_get_null_terminated(&data, &size);
390
391 CLEAN_IF_NULL(db_alias)
392 CLEAN_IF_NULL(db_alias_interface)
393
394 if (strlen(db_alias_interface) > IF_NAMESIZE) {
395 for (int i = 0; i < IF_NAMESIZE; i++) {
396 db_alias->iface[i] = db_alias_interface[i];
397 }
398 } else {
399 for (int i = 0; i < strlen(db_alias_interface); i++) {
400 db_alias->iface[i] = db_alias_interface[i];
401 }
402 }
403 db->alias = db_alias;
404 daemon->bridges = db;
405
406 // daemon->if_names
407 struct iname *in = (struct iname*)gb_get_random_data(&data, &size, sizeof(struct iname));
408 char *iname_name = gb_get_null_terminated(&data, &size);
409
410 CLEAN_IF_NULL(in)
411 CLEAN_IF_NULL(iname_name)
412
413 in->name = iname_name;
414 in->next = NULL;
415
416 daemon->if_names = in;
417
418 // daemon->if_addrs
419 struct iname *in_addr = (struct iname*)gb_get_random_data(&data, &size, sizeof(struct iname));
420 char *iname_name_addr = gb_get_null_terminated(&data, &size);
421
422 CLEAN_IF_NULL(in_addr)
423 CLEAN_IF_NULL(iname_name_addr)
424
425 in_addr->name = iname_name_addr;
426 in_addr->next = NULL;
427
428 daemon->if_addrs = in_addr;
429
430 // daemon->if_except
431 struct iname *in_except = (struct iname*)gb_get_random_data(&data, &size, sizeof(struct iname));
432 char *iname_name_except = gb_get_null_terminated(&data, &size);
433
434 CLEAN_IF_NULL(in_except)
435 CLEAN_IF_NULL(iname_name_except)
436
437 in_except->name = iname_name_except;
438 in_except->next = NULL;
439
440 daemon->if_except = in_except;
441
442 // daemon->dhcp_except
443 struct iname *except = (struct iname*)gb_get_random_data(&data, &size, sizeof(struct iname));
444 char *name_except = gb_get_null_terminated(&data, &size);
445
446 CLEAN_IF_NULL(except)
447 CLEAN_IF_NULL(name_except)
448
449 except->name = name_except;
450 except->next = NULL;
451
452 daemon->dhcp_except = except;
453
454 // daemon->authinterface
455 struct iname *auth_interface = (struct iname*)gb_get_random_data(&data, &size, sizeof(struct iname));
456 char *auth_name = gb_get_null_terminated(&data, &size);
457
458 CLEAN_IF_NULL(auth_interface)
459 CLEAN_IF_NULL(auth_name)
460
461 auth_interface->name = auth_name;
462 auth_interface->next = NULL;
463
464 daemon->authinterface = auth_interface;
465
466
467 // daemon->cnames
468 struct cname *cn = (struct cname*)gb_alloc_data(sizeof(struct cname));
469 char *cname_alias = gb_get_null_terminated(&data, &size);
470 char *cname_target = gb_get_null_terminated(&data, &size);
471
472 CLEAN_IF_NULL(cn)
473 CLEAN_IF_NULL(cname_alias)
474 CLEAN_IF_NULL(cname_target)
475
476 cn->alias = cname_alias;
477 cn->target = cname_target;
478 daemon->cnames = cn;
479
480
481 // daemon->ptr
482 struct ptr_record *ptr = (struct ptr_record *)gb_alloc_data(sizeof(struct ptr_record));
483 CLEAN_IF_NULL(ptr)
484
485 char *ptr_name = gb_get_null_terminated(&data, &size);
486 CLEAN_IF_NULL(ptr_name)
487 ptr->name = ptr_name;
488 daemon->ptr = ptr;
489
490 if (size > *size2) {
491 goto cleanup;
492 }
493
494 // daemon->dhcp
495 struct dhcp_context *dhcp_c = (struct dhcp_context *) gb_get_random_data(&data, &size, sizeof(struct dhcp_context));
496
497 char *dhcp_c_temp_in = gb_get_null_terminated(&data, &size);
498
499 struct dhcp_netid *dhcp_c_netid = (struct dhcp_netid *) gb_alloc_data(sizeof(struct dhcp_netid));
500 char *dhcp_netid_net = gb_get_null_terminated(&data, &size);
501
502 CLEAN_IF_NULL(dhcp_c)
503 CLEAN_IF_NULL(dhcp_c_temp_in)
504 CLEAN_IF_NULL(dhcp_c_netid)
505 CLEAN_IF_NULL(dhcp_netid_net)
506
507 dhcp_c->next = NULL;
508 dhcp_c->current = NULL;
509 dhcp_c_netid->net = dhcp_netid_net;
510 dhcp_c->filter = dhcp_c_netid;
511 dhcp_c->template_interface = dhcp_c_temp_in;
512
513 daemon->dhcp = dhcp_c;
514
515
516 // daemon->dhcp6
517 struct dhcp_context *dhcp6_c = (struct dhcp_context *) gb_get_random_data(&data, &size, sizeof(struct dhcp_context));
518
519 char *dhcp6_c_temp_in = gb_get_null_terminated(&data, &size);
520
521 struct dhcp_netid *dhcp6_c_netid = (struct dhcp_netid *) gb_alloc_data(sizeof(struct dhcp_netid));
522 char *dhcp6_netid_net = gb_get_null_terminated(&data, &size);
523
524 CLEAN_IF_NULL(dhcp6_c)
525 CLEAN_IF_NULL(dhcp6_c_temp_in)
526 CLEAN_IF_NULL(dhcp6_c_netid)
527 CLEAN_IF_NULL(dhcp6_netid_net)
528
529 dhcp6_c->next = NULL;
530 dhcp6_c->current = NULL;
531 dhcp6_c_netid->net = dhcp6_netid_net;
532 dhcp6_c->filter = dhcp6_c_netid;
533 dhcp6_c->template_interface = dhcp6_c_temp_in;
534
535 daemon->dhcp6 = dhcp6_c;
536
537 // daemon->doing_dhcp6
538 daemon->doing_dhcp6 = 1;
539
540 // daemon->dhcp_buffs
541 char *dhcp_buff = gb_alloc_data(DHCP_BUFF_SZ);
542 char *dhcp_buff2 = gb_alloc_data(DHCP_BUFF_SZ);
543 char *dhcp_buff3 = gb_alloc_data(DHCP_BUFF_SZ);
544
545 CLEAN_IF_NULL(dhcp_buff)
546 CLEAN_IF_NULL(dhcp_buff2)
547 CLEAN_IF_NULL(dhcp_buff3)
548
549 daemon->dhcp_buff = dhcp_buff;
550 daemon->dhcp_buff2 = dhcp_buff2;
551 daemon->dhcp_buff3 = dhcp_buff3;
552
553
554
555 // daemon->ignore_addr
556 struct bogus_addr *bb = (struct bogus_addr *)gb_alloc_data(sizeof(struct bogus_addr));
557 CLEAN_IF_NULL(bb)
558
559 daemon->ignore_addr = bb;
560
561 // daemon->doctors
562 if (size > *size2) {
563 goto cleanup;
564 }
565
566 struct doctor *doctors = (struct doctor *)gb_alloc_data(sizeof(struct doctor));
567 CLEAN_IF_NULL(doctors)
568
569 doctors->next = NULL;
570 daemon->doctors = doctors;
571
572 retval = 0;
573 goto ret;
574 cleanup:
575 retval = -1;
576
577 ret:
578 return retval;
579 }
580