-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsysfuzz.h
167 lines (146 loc) · 7.03 KB
/
sysfuzz.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
#ifndef __SYSFUZZ_H
#define __SYSFUZZ_H
#pragma once
#include <unistd.h>
#include <sched.h>
#include <sys/mman.h>
#ifdef __linux__
# include <linux/sched.h>
#endif
#ifndef PAGE_SIZE
# define PAGE_SIZE getpagesize()
#endif
#ifndef CLONE_IO
# define CLONE_IO 0
#endif
// Custom errno values, must be <= 0. These are used to represent errors
// outside of errno, such as a process exited, or a timeout expiring.
enum {
ESUCCESS = 0, // No error.
ETIMEOUT = -1, // Timeout expired.
EEXITED = -2, // Fuzzer exited.
EKILLED = -3, // Fuzzer was killed.
};
// Quick strerror wrapper to add support for my custom errno values.
static inline const gchar * custom_strerror_wrapper(gint errnum)
{
switch (errnum) {
case ESUCCESS: return "Operation successful";
case ETIMEOUT: return "Operation timed out";
case EEXITED: return "Caller exited";
case EKILLED: return "Caller was killed";
default: return g_strerror(errnum);
}
g_assert_not_reached();
}
// Flags that modify the behaviour of a fuzzer.
// If you modify this list, remember to update any pretty printers that dump
// these flags, like list_fuzzer_names().
enum {
SYS_NONE = 0,
SYS_DISABLED = 1 << 0, // Fuzzer is disabled.
SYS_FAIL = 1 << 1, // Failure is expected, warn on success.
SYS_TIMEOUT = 1 << 2, // Timeout is expected.
SYS_VOID = 1 << 3, // Fuzzer does not return a useful value. (e.g. exit).
SYS_BORING = 1 << 4, // Fuzzer expected to always return the same value.
SYS_SAFE = 1 << 5, // Fuzzer is safe to run without separation.
};
// Some convenience clone combinations.
enum {
// Of course this is not really a fork, but how else can i get the 64bit return code back on x64?
#if defined(__linux__)
CLONE_FORK = CLONE_VM,
CLONE_DEFAULT = CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_SYSVSEM | CLONE_IO,
CLONE_SAFER = CLONE_FS | CLONE_FILES | CLONE_IO,
#elif defined(__FreeBSD__) || defined(__OpenBSD__)
CLONE_FORK = RFMEM | RFPROC | RFFDG,
CLONE_DEFAULT = RFMEM | RFPROC,
CLONE_SAFER = RFPROC,
#else
# error please define some clone combinations for your operating system
#endif
};
// No way any syscall can return more than this number of errors.
#define MAX_ERROR_CODES 128
typedef struct {
gulong error; // Errno value
gulong count; // Number of times seen.
} error_record_t;
typedef struct {
glong (*callback)(gpointer); // Fuzzer subroutine.
gchar *name; // System call or fuzzer name
guint flags; // Fuzzer flags.
guint total; // Total number of executions.
guint failures; // Total number of failures.
guint shared; // Flags for clone(2) describing what it's safe share.
guint number; // Syscall number, used for debugging.
guint timeout; // Microseconds allowed to execute for.
gdouble average; // Average time fuzzer takes to execute.
gsize numerrors; // Unique error codes recorded.
error_record_t errors[MAX_ERROR_CODES]; // error statistics.
} syscall_fuzzer_t;
// Wrapper function around syscall() to return errno.
#define syscall_fast(_number...) \
( \
errno = 0, /* Reset error code */ \
syscall(_number), /* Execute syscall */ \
errno /* Return error code */ \
)
// The same, but if we want the return value as well.
#define syscall_fast_ret(_dest, _number...) \
( \
errno = 0, /* Reset errno */ \
*((glong *)(_dest)) = syscall(_number), /* Execute syscall */ \
errno /* Return error code */ \
)
// Record the highest syscall number for this architecture.
#if defined(__linux__)
# if defined(__i386__)
# define MAX_SYSCALL_NUM 347
# elif defined(__x86_64__)
# define MAX_SYSCALL_NUM 309
# else
# warning please define a real MAX_SYSCALL_NUMBER for this architecure
# define MAX_SYSCALL_NUM 300
# endif
#elif defined(SYS_MAXSYSCALL)
# define MAX_SYSCALL_NUM SYS_MAXSYSCALL
#else
# error please define a MAX_SYSCALL_NUMBER for this architecture
#endif
#define MAX_PROCESS_NUM 32
extern syscall_fuzzer_t *system_call_fuzzers;
extern gint semid; // Semaphore set for syscall fuzzers.
// Alocate space for the system_call_fuzzer table in shared memory.
static inline void allocate_sycall_fuzzer_table(void)
{
g_assert(system_call_fuzzers == NULL);
system_call_fuzzers = mmap(NULL, sizeof(syscall_fuzzer_t) * MAX_SYSCALL_NUM,
PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANON,
-1,
0);
}
#define SYSFUZZ(_name, _syscall, _flags, _cloneflags, _timeout) \
static glong __fuzz__ ## _name (gpointer ignored); \
static void __constructor __const__ ## _name (void) \
{ \
/* Verify the system call table is ready */ \
if (system_call_fuzzers == NULL) \
allocate_sycall_fuzzer_table(); \
\
/* Verify this slot is empty */ \
g_assert_cmpstr(system_call_fuzzers[_syscall].name, ==, NULL); \
\
system_call_fuzzers[_syscall].callback = __fuzz__ ## _name; \
system_call_fuzzers[_syscall].name = # _name; \
system_call_fuzzers[_syscall].flags = _flags; \
system_call_fuzzers[_syscall].shared = _cloneflags; \
system_call_fuzzers[_syscall].timeout = _timeout; \
system_call_fuzzers[_syscall].number = _syscall; \
return; \
} \
static glong __fuzz__ ## _name (gpointer this)
#else
# warning sysfuzz.h included twice
#endif