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
| | // SPDX-License-Identifier: GPL-2.0-or-later
/* fuzz.c - AFL++ fuzzing support: deterministic wrappers for
* clock_gettime(), getrandom(), getsockopt(), recv(),
* recvmsg(), recvfrom() and recvmmsg()
*
* Copyright Red Hat
* Author: Anshu Kumari <anskuma@redhat.com>
*/
#ifdef FUZZING
#include <string.h>
#include <time.h>
#include <errno.h>
#include <netinet/tcp.h>
#include "passt.h"
#include "fuzz.h"
/* Undo macros so definitions here call the real syscalls */
#undef clock_gettime
#undef getrandom
#undef getsockopt
#undef recv
#undef recvmsg
#undef recvfrom
#undef recvmmsg
const unsigned char *fuzz_recv_data;
int fuzz_recv_data_len;
#define FUZZ_CLOCK_BASE_SEC 10000
static struct timespec fuzz_clock;
/**
* fuzz_clock_reset() - Reset clock to fixed baseline
*
* Called at the start of every __AFL_LOOP iteration so
* the clock is identical regardless of iteration number.
*/
void fuzz_clock_reset(void)
{
fuzz_clock.tv_sec = FUZZ_CLOCK_BASE_SEC;
fuzz_clock.tv_nsec = 0;
}
/**
* fuzz_clock_gettime() - Return deterministic time
* @clk: Clock ID
* @tp: Output timespec
*
* Return: 0 (always succeeds)
*/
int fuzz_clock_gettime(clockid_t clk, struct timespec *tp)
{
(void)clk;
*tp = fuzz_clock;
/* increment the timestamp by 1 micro sec monotonically */
fuzz_clock.tv_nsec += 1000;
if (fuzz_clock.tv_nsec >= 1000000000) {
fuzz_clock.tv_sec++;
fuzz_clock.tv_nsec -= 1000000000;
}
return 0;
}
/**
* fuzz_getrandom() - Return static deterministic bytes
* @buf: Output buffer
* @buflen: Bytes to fill
* @flags: Ignored
*
* Fills buffer with a repeating 0x41 pattern. Every call with the
* same length returns identical bytes, eliminating randomness
*
* Return: buflen (always succeeds)
*/
ssize_t fuzz_getrandom(void *buf, size_t buflen, unsigned int flags)
{
(void)flags;
memset(buf, 0x41, buflen);
return buflen;
}
/**
* fuzz_getsockopt() - Deterministic getsockopt wrapper
* @fd: Socket file descriptor
* @level: Protocol level
* @optname: Option name
* @optval: Output buffer
* @optlen: In/out option length
*
* For TCP_INFO, SO_ERROR, SO_RCVBUF, SO_SNDBUF returns determinstic
* values. For all other options: calls the real getsockopt.
*
* Return: 0 on success, -1 on error
*/
int fuzz_getsockopt(int fd, int level, int optname, void *optval,
socklen_t *optlen)
{
if (level == SOL_SOCKET) {
if (optname == SO_ERROR) {
*(int *)optval = 0;
return 0;
}
if (optname == SO_RCVBUF || optname == SO_SNDBUF) {
*(int *)optval = 212992; /* default linux buff size */
return 0;
}
}
/* intercept SOL_TCP option: TCP_INFO */
if (level == SOL_TCP && optname == TCP_INFO) {
size_t fill = *optlen;
memset(optval, 0, fill);
if (fill >= sizeof(struct tcp_info)) {
struct tcp_info *ti = optval;
ti->tcpi_state = 1; /* TCP_ESTABLISHED */
ti->tcpi_rto = 200000; /* 200ms */
ti->tcpi_rtt = 1000; /* 1ms RTT */
ti->tcpi_rttvar = 500;
ti->tcpi_snd_mss = 1460;
ti->tcpi_rcv_mss = 1460;
ti->tcpi_snd_cwnd = 10;
ti->tcpi_advmss = 1460;
ti->tcpi_pmtu = 1500;
*optlen = sizeof(struct tcp_info);
}
return 0;
}
return getsockopt(fd, level, optname, optval, optlen);
}
/**
* fuzz_recv() - recv wrapper
* @fd: File descriptor
* @buf: Output buffer
* @len: Max bytes
* @flags: recv flags (passed through for fd_tap)
*
* real recv() for fd_tap, AFL++ data for everything else
*
* Return: bytes read, or -1
*/
ssize_t fuzz_recv(int fd, void *buf, size_t len, int flags)
{
size_t n;
/* fd is TAP socket for UNIX connection */
if (fd == passt_ctx.fd_tap)
return recv(fd, buf, len, flags);
if (!fuzz_recv_data || fuzz_recv_data_len <= 0) {
errno = EAGAIN;
return -1;
}
n = (len < (size_t)fuzz_recv_data_len) ?
len : (size_t)fuzz_recv_data_len;
memcpy(buf, fuzz_recv_data, n);
fuzz_recv_data += n;
fuzz_recv_data_len -= n;
return n;
}
/**
* fuzz_recvmsg() - recvmsg wrapper
* @fd: File descriptor
* @msg: Message header
* @flags: recvmsg flags
*
* For fd_tap: calls real recvmsg. For all other fds: fills each
* buffer sequentially from the AFL++ shared memory stream.
*
* Return: total bytes read across all iovecs, or -1
*/
ssize_t fuzz_recvmsg(int fd, struct msghdr *msg, int flags)
{
size_t total = 0;
size_t i;
if (fd == passt_ctx.fd_tap)
return recvmsg(fd, msg, flags);
if (!fuzz_recv_data || fuzz_recv_data_len <= 0) {
errno = EAGAIN;
return -1;
}
for (i = 0; i < (size_t)msg->msg_iovlen &&
fuzz_recv_data_len > 0; i++) {
size_t n = msg->msg_iov[i].iov_len;
if ((int)n > fuzz_recv_data_len)
n = fuzz_recv_data_len;
memcpy(msg->msg_iov[i].iov_base, fuzz_recv_data, n);
fuzz_recv_data += n;
fuzz_recv_data_len -= n;
total += n;
}
return total;
}
/**
* fuzz_recvfrom() - recvfrom wrapper
* @fd: File descriptor
* @buf: Output buffer
* @len: Max bytes
* @flags: recv flags
* @src: Source address output
* @addrlen: Source address length
*
* For fd_tap: calls real recvfrom(). For all other fds: zeroes the
* source address (so callers see a deterministic sender) and
* delegates to fuzz_recv() for the payload.
*
* Return: bytes read, or -1
*/
ssize_t fuzz_recvfrom(int fd, void *buf, size_t len, int flags,
struct sockaddr *src, socklen_t *addrlen)
{
if (fd == passt_ctx.fd_tap)
return recvfrom(fd, buf, len, flags, src, addrlen);
if (src && addrlen)
memset(src, 0, *addrlen);
return fuzz_recv(fd, buf, len, flags);
}
/**
* fuzz_recvmmsg() - recvmmsg wrapper
* @fd: File descriptor
* @mmh: Array of mmsghdr structures to fill
* @vlen: Number of mmsghdr entries available
* @flags: recv flags
* @timeout: Timeout
*
* For fd_tap: calls real recvmmsg(). For all other fds: fills only
* the first message from the AFL++ buffer via fuzz_recvmsg() and
* returns 1.
*
* Return: number of messages received (0 or 1), or -1
*/
int fuzz_recvmmsg(int fd, struct mmsghdr *mmh, unsigned int vlen,
int flags, struct timespec *timeout)
{
ssize_t n;
if (fd == passt_ctx.fd_tap)
return recvmmsg(fd, mmh, vlen, flags, timeout);
if (!vlen || !fuzz_recv_data || fuzz_recv_data_len <= 0) {
errno = EAGAIN;
return -1;
}
n = fuzz_recvmsg(fd, &mmh[0].msg_hdr, flags);
if (n < 0)
return -1;
mmh[0].msg_len = n;
return 1;
}
#endif
|