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
|
#include "helper.h"
#include <glib.h>
#include <stdlib.h>
#include "../src/libslirp.h"
#include "../src/ip6.h"
#include "slirp_base_fuzz.h"
#define MIN_NUMBER_OF_RUNS 1
#define EXIT_TEST_SKIP 77
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
struct in6_addr ip6_host;
struct in6_addr ip6_dns;
/// Function to compute the checksum of the ip header, should be compatible with
/// TCP and UDP checksum calculation too.
uint16_t compute_checksum(uint8_t *Data, size_t Size)
{
uint32_t sum = 0;
uint16_t *Data_as_u16 = (uint16_t *)Data;
for (size_t i = 0; i < Size / 2; i++) {
uint16_t val = ntohs(*(Data_as_u16 + i));
sum += val;
}
if (Size % 2 == 1)
sum += Data[Size - 1] << 8;
uint16_t carry = sum >> 16;
uint32_t sum_val = carry + (sum & 0xFFFF);
uint16_t result = (sum_val >> 16) + (sum_val & 0xFFFF);
return ~result;
}
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
{
/* FIXME: fail on some addr? */
return 0;
}
int listen(int sockfd, int backlog)
{
return 0;
}
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
{
/* FIXME: fail on some addr? */
return 0;
}
ssize_t send(int sockfd, const void *buf, size_t len, int flags)
{
/* FIXME: partial send? */
return len;
}
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen)
{
/* FIXME: partial send? */
return len;
}
ssize_t recv(int sockfd, void *buf, size_t len, int flags)
{
memset(buf, 0, len);
return len / 2;
}
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen)
{
memset(buf, 0, len);
memset(src_addr, 0, *addrlen);
return len / 2;
}
int setsockopt(int sockfd, int level, int optname, const void *optval,
socklen_t optlen)
{
return 0;
}
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
static void empty_logging_func(const gchar *log_domain,
GLogLevelFlags log_level, const gchar *message,
gpointer user_data)
{
}
#endif
/* Disables logging for oss-fuzz. Must be used with each target. */
static void fuzz_set_logging_func(void)
{
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
g_log_set_default_handler(empty_logging_func, NULL);
#endif
}
static ssize_t send_packet(const void *pkt, size_t pkt_len, void *opaque)
{
return pkt_len;
}
static int64_t clock_get_ns(void *opaque)
{
return 0;
}
static void *timer_new(SlirpTimerCb cb, void *cb_opaque, void *opaque)
{
return NULL;
}
static void timer_mod(void *timer, int64_t expire_timer, void *opaque)
{
}
static void timer_free(void *timer, void *opaque)
{
}
static void guest_error(const char *msg, void *opaque)
{
}
static void register_poll_socket(slirp_os_socket fd, void *opaque)
{
}
static void unregister_poll_socket(slirp_os_socket fd, void *opaque)
{
}
static void notify(void *opaque)
{
}
static const SlirpCb slirp_cb = {
.send_packet = send_packet,
.guest_error = guest_error,
.clock_get_ns = clock_get_ns,
.timer_new = timer_new,
.timer_mod = timer_mod,
.timer_free = timer_free,
.register_poll_socket = register_poll_socket,
.unregister_poll_socket = unregister_poll_socket,
.notify = notify,
};
#define MAX_EVID 1024
static int fake_events[MAX_EVID];
static int add_poll_cb(slirp_os_socket fd, int events, void *opaque)
{
g_assert(fd < G_N_ELEMENTS(fake_events));
fake_events[fd] = events;
return fd;
}
static int get_revents_cb(int idx, void *opaque)
{
return fake_events[idx] & ~(SLIRP_POLL_ERR | SLIRP_POLL_HUP);
}
// Fuzzing strategy is the following :
// LLVMFuzzerTestOneInput :
// - build a slirp instance,
// - extract the packets from the pcap one by one,
// - send the data to `slirp_input`
// - call `slirp_pollfds_fill` and `slirp_pollfds_poll` to advance slirp
// - cleanup slirp when the whole pcap has been unwrapped.
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
{
Slirp *slirp = NULL;
struct in_addr net = { .s_addr = htonl(0x0a000200) }; /* 10.0.2.0 */
struct in_addr mask = { .s_addr = htonl(0xffffff00) }; /* 255.255.255.0 */
struct in_addr host = { .s_addr = htonl(0x0a000202) }; /* 10.0.2.2 */
struct in_addr fwd = { .s_addr = htonl(0x0a000205) }; /* 10.0.2.5 */
struct in_addr dhcp = { .s_addr = htonl(0x0a00020f) }; /* 10.0.2.15 */
struct in_addr dns = { .s_addr = htonl(0x0a000203) }; /* 10.0.2.3 */
struct in6_addr ip6_prefix;
int ret, vprefix6_len;
const pcap_hdr_t *hdr = (const void *)data;
const pcaprec_hdr_t *rec = NULL;
uint32_t timeout = 0;
if (size < sizeof(pcap_hdr_t)) {
return 0;
}
data += sizeof(*hdr);
size -= sizeof(*hdr);
if (hdr->magic_number == 0xd4c3b2a1) {
g_debug("FIXME: byteswap fields");
return 0;
} /* else assume native pcap file */
if (hdr->network != 1) {
return 0;
}
setenv("SLIRP_FUZZING", "1", 0);
fuzz_set_logging_func();
ret = inet_pton(AF_INET6, "fec0::", &ip6_prefix);
vprefix6_len = 64;
g_assert_cmpint(ret, ==, 1);
ip6_host = ip6_prefix;
ip6_host.s6_addr[15] |= 2;
ip6_dns = ip6_prefix;
ip6_dns.s6_addr[15] |= 3;
SlirpConfig cfg = {
.version = 6,
.restricted = false,
.in_enabled = true,
.vnetwork = net,
.vnetmask = mask,
.vhost = host,
.in6_enabled = true,
.vprefix_addr6 = ip6_prefix,
.vprefix_len = vprefix6_len,
.vhost6 = ip6_host,
.tftp_path = "fuzzing/tftp",
.vdhcp_start = dhcp,
.vnameserver = dns,
.vnameserver6 = ip6_dns,
};
slirp = slirp_new(&cfg, &slirp_cb, NULL);
slirp_add_exec(slirp, "cat", &fwd, 1234);
for ( ; size > sizeof(*rec); data += rec->incl_len, size -= rec->incl_len) {
rec = (const void *)data;
data += sizeof(*rec);
size -= sizeof(*rec);
if (rec->incl_len != rec->orig_len) {
g_debug("unsupported rec->incl_len != rec->orig_len");
break;
}
if (rec->incl_len > size) {
break;
}
if (rec->incl_len >= 14) {
if (data[12] == 0x08 && data[13] == 0x00) {
/* IPv4 */
if (rec->incl_len >= 14 + 16) {
uint32_t ipsource = * (uint32_t*) (data + 14 + 12);
// This an answer, which we will produce, so don't receive
if (ipsource == htonl(0x0a000202) || ipsource == htonl(0x0a000203))
continue;
}
} else if (data[12] == 0x86 && data[13] == 0xdd) {
if (rec->incl_len >= 14 + 24) {
struct in6_addr *ipsource = (struct in6_addr *) (data + 14 + 8);
// This an answer, which we will produce, so don't receive
if (in6_equal(ipsource, &ip6_host) || in6_equal(ipsource, &ip6_dns))
continue;
}
}
}
slirp_input(slirp, data, rec->incl_len);
slirp_pollfds_fill_socket(slirp, &timeout, add_poll_cb, NULL);
slirp_pollfds_poll(slirp, 0, get_revents_cb, NULL);
}
slirp_cleanup(slirp);
return 0;
}
|