-
Notifications
You must be signed in to change notification settings - Fork 15
/
epoll_fuzzer.h
751 lines (555 loc) · 15.5 KB
/
epoll_fuzzer.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
/* Welcome to libEpollFuzzer - a mock implementation of the epoll/socket syscalls */
/* Current implementation is extremely experimental and trashy, mind you */
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <stdarg.h>
//#include <threads.h>
#include <sys/timerfd.h>
#include <sys/epoll.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <errno.h>
// todo: add connect, donät pass invalid-FD to real syscalls
// getaddrinfo should return inet6 somtimes and sometimes wrong family (done)
// accept4 should produce inet6 sometimes (done)
// socket syscall should fail with given invalid family (done)
// listen syscall should fail sometimes (done)
/* Currently read, close, fcntl are wrapped to real syscalls */
/* TODO: Our FDs should start at 1024 while actual real FDs should be reserved from 0 to 1023 and passed to actual
* real syscalls so that we can co-exist with overlapping syscalls like read, open, write, close */
//#define PRINTF_DEBUG
/* The test case */
void test();
void teardown();
#ifdef __cplusplus
extern "C" {
#endif
struct file {
/* Every file has a type; socket, event, timer, epoll */
int type;
/* We assume there can only be one event-loop at any given point in time,
* so every file holds its own epoll_event */
struct epoll_event epev;
/* A file may be added to an epfd by linking it in a list */
struct file *prev, *next;
};
/* If FD is less than this, it should be passed to REAL syscall.
* We never produce FDs lower than this (except for -1 on error) */
const int RESERVED_SYSTEM_FDS = 1024;
/* Map from some collection of integers to a shared extensible struct of data */
const int MAX_FDS = 1000;
struct file *fd_to_file[MAX_FDS];
const int FD_TYPE_EPOLL = 0;
const int FD_TYPE_TIMER = 1;
const int FD_TYPE_EVENT = 2;
const int FD_TYPE_SOCKET = 3;
int num_fds = 0;
/* Keeping track of cunsumable data */
unsigned char *consumable_data;
int consumable_data_length;
void set_consumable_data(const unsigned char *new_data, int new_length) {
consumable_data = (unsigned char *) new_data;
consumable_data_length = new_length;
}
/* Returns non-null on error */
int consume_byte(unsigned char *b) {
if (consumable_data_length) {
*b = consumable_data[0];
consumable_data++;
consumable_data_length--;
return 0;
}
return -1;
}
/* Keeping track of FDs */
/* Returns -1 on error, or RESERVED_SYSTEM_FDS and above */
int allocate_fd() {
// this can be massively optimized by having a list of free blocks or the like
for (int fd = 0; fd < MAX_FDS; fd++) {
if (!fd_to_file[fd]) {
num_fds++;
return fd + RESERVED_SYSTEM_FDS;
}
}
return -1;
}
/* This one should set the actual file for this FD */
void init_fd(int fd, int type, struct file *f) {
if (fd >= RESERVED_SYSTEM_FDS) {
fd_to_file[fd - RESERVED_SYSTEM_FDS] = f;
fd_to_file[fd - RESERVED_SYSTEM_FDS]->type = type;
fd_to_file[fd - RESERVED_SYSTEM_FDS]->next = NULL;
fd_to_file[fd - RESERVED_SYSTEM_FDS]->prev = NULL;
}
}
struct file *map_fd(int fd) {
if (fd >= RESERVED_SYSTEM_FDS && fd < MAX_FDS + RESERVED_SYSTEM_FDS) {
return fd_to_file[fd - RESERVED_SYSTEM_FDS];
}
return NULL;
}
/* This one should remove the FD from any pollset by calling epoll_ctl remove */
int free_fd(int fd) {
if (fd >= RESERVED_SYSTEM_FDS && fd < MAX_FDS + RESERVED_SYSTEM_FDS) {
if (fd_to_file[fd - RESERVED_SYSTEM_FDS]) {
fd_to_file[fd - RESERVED_SYSTEM_FDS] = 0;
num_fds--;
return 0;
}
}
return -1;
}
/* The epoll syscalls */
struct epoll_file {
struct file base;
/* A doubly linked list for polls awaiting events */
struct file *poll_set_head, *poll_set_tail;
};
/* This function is O(n) and does not consume any fuzz data, but will fail if run out of FDs */
int __wrap_epoll_create1(int flags) {
/* Todo: check that we do not allocate more than one epoll FD */
int fd = allocate_fd();
if (fd != -1) {
struct epoll_file *ef = (struct epoll_file *)malloc(sizeof(struct epoll_file));
/* Init the epoll_file */
ef->poll_set_head = NULL;
ef->poll_set_tail = NULL;
init_fd(fd, FD_TYPE_EPOLL, (struct file *)ef);
}
#ifdef PRINTF_DEBUG
printf("epoll_create1 returning epfd: %d\n", fd);
#endif
return fd;
}
// this function cannot be called inside an iteration! it changes the list
/* This function is O(1) and does not consume any fuzz data */
int __wrap_epoll_ctl(int epfd, int op, int fd, struct epoll_event *event) {
struct epoll_file *ef = (struct epoll_file *)map_fd(epfd);
if (!ef) {
return -1;
}
struct file *f = (struct file *)map_fd(fd);
if (!f) {
return -1;
}
/* We add new polls in the head */
if (op == EPOLL_CTL_ADD) {
// if there is a head already
if (ef->poll_set_head) {
ef->poll_set_head->prev = f;
// then it will be our next
f->next = ef->poll_set_head;
} else {
// if there was no head then we became the tail also
ef->poll_set_tail = f;
}
// we are now the head in any case
ef->poll_set_head = f;
f->epev = *event;
} else if (op == EPOLL_CTL_MOD) {
/* Modifying is simply changing the file itself */
f->epev = *event;
} else if (op == EPOLL_CTL_DEL) {
if (f->prev) {
f->prev->next = f->next;
} else {
ef->poll_set_head = f->next;
}
if (f->next) {
f->next->prev = f->prev;
} else {
// tail ska vara vår.prev
ef->poll_set_tail = f->prev;
}
// a file that is not in the list should be reset to NULL
f->prev = NULL;
f->next = NULL;
}
/* You have to poll for errors and hangups */
f->epev.events |= EPOLLERR | EPOLLHUP;
return 0;
}
/* This function is O(n) and consumes fuzz data and might trigger teardown callback */
int __wrap_epoll_wait(int epfd, struct epoll_event *events,
int maxevents, int timeout) {
//printf("epoll_wait: %d\n", 0);
#ifdef PRINTF_DEBUG
printf("Calling epoll_wait\n");
#endif
struct epoll_file *ef = (struct epoll_file *)map_fd(epfd);
if (!ef) {
return -1;
}
if (consumable_data_length) {
int ready_events = 0;
for (struct file *f = ef->poll_set_head; f; f = f->next) {
/* Consume one fuzz byte, AND it with the event */
if (!consumable_data_length) {
// break if we have no data
break;
}
// here we have the main condition that drives everything
int ready_event = consumable_data[0] & f->epev.events;
// consume the byte
consumable_data_length--;
consumable_data++;
if (ready_event) {
if (ready_events < maxevents) {
events[ready_events] = f->epev;
// todo: the event should be masked by the byte, not everything it wants shold be given all the time!
events[ready_events++].events = ready_event;
} else {
// we are full, break
break;
}
}
}
return ready_events;
} else {
#ifdef PRINTF_DEBUG
printf("Calling teardown\n");
#endif
teardown();
// after shutting down the listen socket we clear the whole list (the bug in epoll_ctl remove)
// so the below loop doesn't work - we never close anything more than the listen socket!
/* You don't really need to emit teardown, you could simply emit error on every poll */
int ready_events = 0;
#ifdef PRINTF_DEBUG
printf("Emitting error on every remaining FD\n");
#endif
for (struct file *f = ef->poll_set_head; f; f = f->next) {
if (f->type == FD_TYPE_SOCKET) {
if (ready_events < maxevents) {
events[ready_events] = f->epev;
// todo: the event should be masked by the byte, not everything it wants shold be given all the time!
events[ready_events++].events = EPOLLERR | EPOLLHUP;
} else {
// we are full, break
break;
}
}
}
#ifdef PRINTF_DEBUG
printf("Ready events: %d\n", ready_events);
#endif
return ready_events;
}
}
/* The socket syscalls */
struct socket_file {
struct file base;
/* We store socket addresses created in accept4 */
union {
struct sockaddr_in6 in6;
struct sockaddr_in in;
} addr;
/* The size of sockaddr_in6 or sockaddr_in as a whole */
socklen_t len;
};
extern int __real_read(int fd, void *buf, size_t count);
int __wrap_read(int fd, void *buf, size_t count) {
if (fd < RESERVED_SYSTEM_FDS) {
return __real_read(fd, buf, count);
}
#ifdef PRINTF_DEBUG
printf("Wrapped read\n");
#endif
/* Let's try and clear the buffer first */
//memset(buf, 0, count);
struct file *f = map_fd(fd);
if (!f) {
return -1;
}
errno = 0;
if (f->type == FD_TYPE_SOCKET) {
if (!consumable_data_length) {
errno = EWOULDBLOCK;
return -1;
} else {
int data_available = (unsigned char) consumable_data[0];
consumable_data_length--;
consumable_data++;
if (consumable_data_length < data_available) {
data_available = consumable_data_length;
}
if (count < data_available) {
data_available = count;
}
memcpy(buf, consumable_data, data_available);
consumable_data_length -= data_available;
consumable_data += data_available;
return data_available;
}
}
if (f->type == FD_TYPE_EVENT) {
memset(buf, 1, 8);
return 8;
}
if (f->type == FD_TYPE_TIMER) {
memset(buf, 1, 8);
return 8;
}
return -1;
}
/* We just ignore the extra flag here */
int __wrap_recv(int sockfd, void *buf, size_t len, int flags) {
return __wrap_read(sockfd, buf, len);
}
int __wrap_send(int sockfd, const void *buf, size_t len, int flags) {
if (consumable_data_length) {
/* We can send len scaled by the 1 byte */
unsigned char scale = consumable_data[0];
consumable_data++;
consumable_data_length--;
int written = float(scale) / 255.0f * len;
if (written == 0) {
errno = EWOULDBLOCK;
} else {
errno = 0;
}
return written;
} else {
return -1;
}
}
int __wrap_sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen) {
return __wrap_send(sockfd, buf, len, flags);
}
int __wrap_bind() {
return 0;
}
int __wrap_setsockopt() {
return 0;
}
extern int __real_fcntl(int fd, int cmd, ... /* arg */ );
int __wrap_fcntl(int fd, int cmd, ... /* arg */) {
if (fd < RESERVED_SYSTEM_FDS) {
va_list args;
va_start(args, cmd);
int ret = __real_fcntl(fd, cmd, args);
va_end(args);
return ret;
}
return 0;
}
/* Addrinfo */
int __wrap_getaddrinfo(const char *node, const char *service,
const struct addrinfo *hints,
struct addrinfo **res) {
//printf("Wrapped getaddrinfo\n");
struct addrinfo default_hints = {};
if (!hints) {
hints = &default_hints;
}
unsigned char b;
if (consume_byte(&b)) {
return -1;
}
/* This one should be thread_local */
static /*thread_local*/ struct addrinfo ai;
ai.ai_flags = hints->ai_flags;
ai.ai_socktype = hints->ai_socktype;
ai.ai_protocol = hints->ai_protocol;
if (b > 127) {
ai.ai_family = AF_INET;//hints->ai_family;
} else {
ai.ai_family = AF_INET6;//hints->ai_family;
}
/* This one is for generating the wrong family (maybe invalid?) */
if (b == 0) {
ai.ai_family = hints->ai_family;
}
ai.ai_next = NULL;
ai.ai_canonname = NULL; // fel
// these should depend on inet6 or inet */
ai.ai_addrlen = 4; // fel
ai.ai_addr = NULL; // ska peka på en sockaddr!
// we need to return an addrinfo with family AF_INET6
*res = &ai;
return 0;
}
int __wrap_freeaddrinfo() {
return 0;
}
/* This one should return the same address as accept4 did produce */
int __wrap_getpeername(int sockfd, struct sockaddr *addr, socklen_t *addrlen) {
struct file *f = map_fd(sockfd);
if (!f) {
return -1;
}
// todo: this could fail with -1 also (consume a byte)?
if (f->type == FD_TYPE_SOCKET) {
struct socket_file *sf = (struct socket_file *) f;
if (addr) {
memcpy(addr, &sf->addr, sf->len);
*addrlen = sf->len;
}
return 0;
}
return -1;
}
int __wrap_accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen) {
/* We must end with -1 since we are called in a loop */
unsigned char b;
if (consume_byte(&b)) {
return -1;
}
/* This rule might change, anything below 10 is accepted */
if (b < 10) {
int fd = allocate_fd();
if (fd != -1) {
/* Allocate the file */
struct socket_file *sf = (struct socket_file *) malloc(sizeof(struct socket_file));
/* Init the file */
/* Here we need to create a socket FD and return */
init_fd(fd, FD_TYPE_SOCKET, (struct file *)sf);
/* We need to provide an addr */
/* Begin by setting it to an empty in6 address */
memset(&sf->addr, 0, sizeof(struct sockaddr_in6));
sf->len = sizeof(struct sockaddr_in6);
sf->addr.in6.sin6_family = AF_INET6;
/* Opt-in to ipv4 */
if (b < 5) {
memset(&sf->addr, 0, sizeof(struct sockaddr_in6));
sf->len = sizeof(struct sockaddr_in);
sf->addr.in.sin_family = AF_INET;
}
if (addr) {
/* Copy from socket to addr */
memcpy(addr, &sf->addr, sf->len);
}
}
return fd;
}
return -1;
}
int __wrap_listen() {
/* Listen consumes one byte and fails on -1 */
unsigned char b;
if (consume_byte(&b)) {
return -1;
}
if (b) {
return 0;
}
return -1;
}
/* This one is similar to accept4 and has to return a valid FD of type socket */
int __wrap_socket(int domain, int type, int protocol) {
/* Only accept valid families */
if (domain != AF_INET && domain != AF_INET6) {
return -1;
}
int fd = allocate_fd();
if (fd != -1) {
struct socket_file *sf = (struct socket_file *)malloc(sizeof(struct socket_file));
/* Init the file */
init_fd(fd, FD_TYPE_SOCKET, (struct file *)sf);
}
#ifdef PRINTF_DEBUG
printf("socket returning fd: %d\n", fd);
#endif
return fd;
}
int __wrap_shutdown() {
//printf("Wrapped shutdown\n");
return 0;
}
/* The timerfd syscalls */
struct timer_file {
struct file base;
};
int __wrap_timerfd_create(int clockid, int flags) {
int fd = allocate_fd();
if (fd != -1) {
struct timer_file *tf = (struct timer_file *)malloc(sizeof(struct timer_file));
/* Init the file */
init_fd(fd, FD_TYPE_TIMER, (struct file *)tf);
}
#ifdef PRINTF_DEBUG
printf("timerfd_create returning fd: %d\n", fd);
#endif
return fd;
}
int __wrap_timerfd_settime(int fd, int flags,
const struct itimerspec *new_value,
struct itimerspec *old_value) {
//printf("timerfd_settime: %d\n", fd);
return 0;
}
/* The eventfd syscalls */
struct event_file {
struct file base;
};
int __wrap_eventfd() {
int fd = allocate_fd();
if (fd != -1) {
struct event_file *ef = (struct event_file *)malloc(sizeof(struct event_file));
/* Init the file */
init_fd(fd, FD_TYPE_EVENT, (struct file *)ef);
//printf("eventfd: %d\n", fd);
}
#ifdef PRINTF_DEBUG
printf("eventfd returning fd: %d\n", fd);
#endif
return fd;
}
// timerfd_settime
/* File descriptors exist in a shared dimension, and has to know its type */
extern int __real_close(int fd);
int __wrap_close(int fd) {
if (fd < RESERVED_SYSTEM_FDS) {
return __real_close(fd);
}
struct file *f = map_fd(fd);
if (!f) {
return -1;
}
if (f->type == FD_TYPE_EPOLL) {
#ifdef PRINTF_DEBUG
printf("Closing epoll FD: %d\n", fd);
#endif
free(f);
return free_fd(fd);
} else if (f->type == FD_TYPE_TIMER) {
#ifdef PRINTF_DEBUG
printf("Closing timer fd: %d\n", fd);
#endif
free(f);
return free_fd(fd);
} else if (f->type == FD_TYPE_EVENT) {
#ifdef PRINTF_DEBUG
printf("Closing event fd: %d\n", fd);
#endif
free(f);
return free_fd(fd);
} else if (f->type == FD_TYPE_SOCKET) {
#ifdef PRINTF_DEBUG
printf("Closing socket fd: %d\n", fd);
#endif
// we should call epoll_ctl remove here
free(f);
int ret = free_fd(fd);
#ifdef PRINTF_DEBUG
printf("Ret: %d\n", ret);
#endif
//free(-1);
return ret;
}
return -1;
}
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
set_consumable_data(data, size);
test();
if (num_fds) {
printf("ERROR! Cannot leave open FDs after test!\n");
}
return 0;
}
#ifdef __cplusplus
}
#endif