/src/mongoose/test/fuzz.c
Line | Count | Source |
1 | | // https://llvm.org/docs/LibFuzzer.html |
2 | | |
3 | | #define MG_ENABLE_SOCKET 0 |
4 | | #define MG_ENABLE_LOG 0 |
5 | | #define MG_ENABLE_LINES 1 |
6 | | #define MG_ENABLE_TCPIP 1 |
7 | 4.34k | #define MG_IO_SIZE (32 * 1024 * 1024) // Big IO size for fast resizes |
8 | | |
9 | | #include "mongoose.c" |
10 | | |
11 | | #include "driver_mock.c" |
12 | | |
13 | | #ifdef __cplusplus |
14 | | extern "C" int LLVMFuzzerTestOneInput(const uint8_t *, size_t); |
15 | | #else |
16 | | int LLVMFuzzerTestOneInput(const uint8_t *, size_t); |
17 | | #endif |
18 | | |
19 | 173k | static void fn(struct mg_connection *c, int ev, void *ev_data) { |
20 | 173k | struct mg_http_serve_opts opts = {.root_dir = "."}; |
21 | 173k | if (ev == MG_EV_HTTP_MSG) { |
22 | 75.3k | mg_http_serve_dir(c, (struct mg_http_message *) ev_data, &opts); |
23 | 75.3k | } |
24 | 173k | } |
25 | | |
26 | 4.34k | int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { |
27 | 4.34k | mg_log_set(MG_LL_INFO); |
28 | | |
29 | 4.34k | struct mg_dns_message dm; |
30 | 4.34k | mg_dns_parse(data, size, &dm); |
31 | 4.34k | mg_dns_parse(NULL, 0, &dm); |
32 | | |
33 | 4.34k | struct mg_http_message hm; |
34 | 4.34k | if (mg_http_parse((const char *) data, size, &hm) > 0) { |
35 | 1.73k | mg_crc32(0, hm.method.buf, hm.method.len); |
36 | 1.73k | mg_crc32(0, hm.uri.buf, hm.uri.len); |
37 | 1.73k | mg_crc32(0, hm.uri.buf, hm.uri.len); |
38 | 53.6k | for (size_t i = 0; i < sizeof(hm.headers) / sizeof(hm.headers[0]); i++) { |
39 | 51.9k | struct mg_str *k = &hm.headers[i].name, *v = &hm.headers[i].value; |
40 | 51.9k | mg_crc32(0, k->buf, k->len); |
41 | 51.9k | mg_crc32(0, v->buf, v->len); |
42 | 51.9k | } |
43 | 1.73k | } |
44 | 4.34k | mg_http_parse(NULL, 0, &hm); |
45 | | |
46 | 4.34k | struct mg_str body = mg_str_n((const char *) data, size); |
47 | 4.34k | char tmp[256]; |
48 | 4.34k | mg_http_get_var(&body, "key", tmp, sizeof(tmp)); |
49 | 4.34k | mg_http_get_var(&body, "key", NULL, 0); |
50 | 4.34k | mg_url_decode((char *) data, size, tmp, sizeof(tmp), 1); |
51 | 4.34k | mg_url_decode((char *) data, size, tmp, 1, 1); |
52 | 4.34k | mg_url_decode(NULL, 0, tmp, 1, 1); |
53 | | |
54 | 4.34k | struct mg_mqtt_message mm; |
55 | 4.34k | if (mg_mqtt_parse(data, size, 0, &mm) == MQTT_OK) { |
56 | 1.85k | mg_crc32(0, mm.topic.buf, mm.topic.len); |
57 | 1.85k | mg_crc32(0, mm.data.buf, mm.data.len); |
58 | 1.85k | mg_crc32(0, mm.dgram.buf, mm.dgram.len); |
59 | 1.85k | } |
60 | 4.34k | mg_mqtt_parse(NULL, 0, 0, &mm); |
61 | 4.34k | if (mg_mqtt_parse(data, size, 5, &mm) == MQTT_OK) { |
62 | 1.84k | mg_crc32(0, mm.topic.buf, mm.topic.len); |
63 | 1.84k | mg_crc32(0, mm.data.buf, mm.data.len); |
64 | 1.84k | mg_crc32(0, mm.dgram.buf, mm.dgram.len); |
65 | 1.84k | { |
66 | 1.84k | struct mg_mqtt_prop prop; |
67 | 1.84k | size_t ofs = 0; |
68 | 14.7k | while ((ofs = mg_mqtt_next_prop(&mm, &prop, ofs)) > 0) { |
69 | 12.8k | mg_crc32(0, prop.key.buf, prop.key.len); |
70 | 12.8k | mg_crc32(0, prop.val.buf, prop.val.len); |
71 | 12.8k | } |
72 | 1.84k | } |
73 | 1.84k | } |
74 | 4.34k | mg_mqtt_parse(NULL, 0, 5, &mm); |
75 | | |
76 | 4.34k | mg_sntp_parse(data, size); |
77 | 4.34k | mg_sntp_parse(NULL, 0); |
78 | | |
79 | 4.34k | char buf[size * 4 / 3 + 5]; // At least 4 chars and nul termination |
80 | 4.34k | mg_base64_decode((char *) data, size, buf, sizeof(buf)); |
81 | 4.34k | mg_base64_decode(NULL, 0, buf, sizeof(buf)); |
82 | 4.34k | mg_base64_encode(data, size, buf, sizeof(buf)); |
83 | 4.34k | mg_base64_encode(NULL, 0, buf, sizeof(buf)); |
84 | | |
85 | 4.34k | mg_match(mg_str_n((char *) data, size), mg_str_n((char *) data, size), NULL); |
86 | | |
87 | 4.34k | struct mg_str entry, s = mg_str_n((char *) data, size); |
88 | 447k | while (mg_span(s, &entry, &s, ',')) entry.len = 0; |
89 | | |
90 | 4.34k | int n; |
91 | 4.34k | mg_json_get(mg_str_n((char *) data, size), "$", &n); |
92 | 4.34k | mg_json_get(mg_str_n((char *) data, size), "$.a.b", &n); |
93 | 4.34k | mg_json_get(mg_str_n((char *) data, size), "$[0]", &n); |
94 | | |
95 | | // Test built-in TCP/IP stack |
96 | 4.34k | if (size > 0) { |
97 | 4.34k | struct mg_tcpip_if mif = {.ip = 1, |
98 | 4.34k | .mask = 255, |
99 | 4.34k | .gw = 1, |
100 | 4.34k | .gw_ready = true, |
101 | 4.34k | .state = MG_TCPIP_STATE_READY, |
102 | | #if MG_ENABLE_IPV6 |
103 | | .ip6[0] = 1; |
104 | | .prefix[0] = 1; |
105 | | .prefix_len = 64; |
106 | | .gw6[0] = 1; |
107 | | .gw6_ready = true; |
108 | | .state6 = MG_TCPIP_STATE_READY; // so mg_send() works and RS stops |
109 | | #endif |
110 | 4.34k | .driver = &mg_tcpip_driver_mock}; |
111 | 4.34k | struct mg_mgr mgr; |
112 | 4.34k | mg_mgr_init(&mgr); |
113 | 4.34k | mg_tcpip_init(&mgr, &mif); |
114 | | |
115 | | // Make a copy of the random data, in order to modify it |
116 | 4.34k | void *pkt = malloc(size); |
117 | 4.34k | struct eth *eth = (struct eth *) pkt; |
118 | 4.34k | memcpy(pkt, data, size); |
119 | 4.34k | if (size > sizeof(*eth)) { |
120 | 3.02k | static size_t i; |
121 | | // eth_types[] exists in l2_eth.c |
122 | 3.02k | memcpy(eth->dst, mif.mac, 6); // Set valid destination MAC |
123 | | // send all handled eth types, then 2 random ones |
124 | 3.02k | if (i >= (sizeof(eth_types) / sizeof(eth_types[0]) + 2)) i = 0; |
125 | 3.02k | if (i < (sizeof(eth_types) / sizeof(eth_types[0]))) eth->type = mg_htons(eth_types[i++]); |
126 | | // build proper layer-3 datagrams, to be able to exercise layers above |
127 | 3.02k | if (eth->type == mg_htons(0x800) && size > (sizeof(*eth) + sizeof(struct ip))) { // IPv4 |
128 | 152 | static size_t j; |
129 | 152 | uint8_t ip_protos[] = {1, 6, 17}; // ICMP, TCP, UDP |
130 | 152 | struct ip *ip4 = (struct ip *) (eth + 1); |
131 | 152 | ip4->ver = (ip4->ver & ~0xf0) | (4 << 4); |
132 | | // send all handled IP protos, then 2 random ones |
133 | 152 | if (j >= (sizeof(ip_protos) / sizeof(ip_protos[0]) + 2)) j = 0; |
134 | 152 | if (j < (sizeof(ip_protos) / sizeof(ip_protos[0]))) ip4->proto = (ip_protos[j++]); |
135 | 152 | if (ip4->proto == 1) { // ICMP |
136 | 131 | } else if (ip4->proto == 6) { // TCP |
137 | 127 | } else if (ip4->proto == 17) { // UDP |
138 | 35 | if (size > (sizeof(*eth) + sizeof(struct ip) + sizeof(struct udp))) { |
139 | 33 | static size_t k; |
140 | 33 | uint16_t udp_ports[] = {67, 68}; // DHCP server and client |
141 | 33 | struct udp *udp = (struct udp *) (ip4 + 1); |
142 | | // send all handled UDP ports, then 2 random ones |
143 | 33 | if (k >= (sizeof(udp_ports) / sizeof(udp_ports[0]) + 2)) k = 0; |
144 | 33 | if (k < (sizeof(udp_ports) / sizeof(udp_ports[0]))) udp->dport = mg_htons(udp_ports[k++]); |
145 | 33 | } |
146 | 35 | } |
147 | 2.87k | } else if (eth->type == mg_htons(0x806)) { // ARP |
148 | | |
149 | 2.78k | } else if (eth->type == mg_htons(0x86dd) && size > (sizeof(*eth) + sizeof(struct ip6))) { // IPv6 |
150 | 23 | static size_t j; |
151 | 23 | uint8_t ip6_protos[] = {6, 17, 58}; // TCP, UDP, ICMPv6 |
152 | 23 | struct ip6 *ip6 = (struct ip6 *) (eth + 1); |
153 | 23 | ip6->ver = (ip6->ver & ~0xf0) | (6 << 4); |
154 | | // send all handled IPv6 "next headers", then 2 random ones |
155 | 23 | if (j >= (sizeof(ip6_protos) / sizeof(ip6_protos[0]) + 2)) j = 0; |
156 | 23 | if (j < (sizeof(ip6_protos) / sizeof(ip6_protos[0]))) ip6->next = (ip6_protos[j++]); |
157 | 23 | if (ip6->next == 6) { // TCP |
158 | 22 | } else if (ip6->next == 17) { // UDP |
159 | 21 | } else if (ip6->next == 58) { // ICMPv6 |
160 | 9 | if (size >= (sizeof(*eth) + sizeof(struct ip6) + sizeof(struct icmp6))) { |
161 | 8 | static size_t k; |
162 | 8 | uint8_t icmp6_types[] = {128, 134, 135, 136}; // Echo Request, RA, NS, NA |
163 | 8 | struct icmp6 *icmp6 = (struct icmp6 *) (ip6 + 1); |
164 | | // send all handled ICMPv6 types, then 2 random ones |
165 | 8 | if (k >= (sizeof(icmp6_types) / sizeof(icmp6_types[0]) + 2)) k = 0; |
166 | 8 | if (k < (sizeof(icmp6_types) / sizeof(icmp6_types[0]))) icmp6->type = icmp6_types[k++]; |
167 | 8 | } |
168 | 9 | } |
169 | 23 | } |
170 | 3.02k | } |
171 | | |
172 | | #if defined(MAIN) |
173 | | printf("Sending to net_builtin:\n"); |
174 | | mg_hexdump(pkt, size); |
175 | | #endif |
176 | 4.34k | mg_tcpip_rx(&mif, pkt, size); |
177 | | |
178 | | // Test HTTP serving (via our built-in TCP/IP stack) |
179 | 4.34k | const char *url = "http://localhost:12345"; |
180 | 4.34k | struct mg_connection *c = mg_http_connect(&mgr, url, fn, NULL); |
181 | 4.34k | mg_iobuf_add(&c->recv, 0, data, size); |
182 | 4.34k | c->pfn(c, MG_EV_READ, NULL); // manually invoke protocol event handler |
183 | | |
184 | 4.34k | free(pkt); |
185 | 4.34k | mg_mgr_free(&mgr); |
186 | 4.34k | } |
187 | | |
188 | 4.34k | return 0; |
189 | 4.34k | } |
190 | | |
191 | | #if defined(MAIN) |
192 | | int main(int argc, char *argv[]) { |
193 | | int res = EXIT_FAILURE; |
194 | | if (argc > 1) { |
195 | | struct mg_str data = mg_file_read(&mg_fs_posix, argv[1]); |
196 | | if (data.buf != NULL) { |
197 | | LLVMFuzzerTestOneInput((uint8_t *) data.buf, data.len); |
198 | | res = EXIT_SUCCESS; |
199 | | } |
200 | | free(data.buf); |
201 | | } |
202 | | return res; |
203 | | } |
204 | | #endif |