Coverage Report

Created: 2026-06-10 06:54

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/http_message_fuzzer.cc
Line
Count
Source
1
/* Copyright 2026 Google LLC
2
Licensed under the Apache License, Version 2.0 (the "License");
3
you may not use this file except in compliance with the License.
4
You may obtain a copy of the License at
5
      http://www.apache.org/licenses/LICENSE-2.0
6
Unless required by applicable law or agreed to in writing, software
7
distributed under the License is distributed on an "AS IS" BASIS,
8
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9
See the License for the specific language governing permissions and
10
limitations under the License.
11
*/
12
13
/* Fuzzer for the full HTTP parsing state machine:
14
 * - Request line and response line parsing with all HTTP methods
15
 * - Header parsing including continuation lines
16
 * - Body reading with Content-Length
17
 * - Chunked transfer encoding
18
 * - Trailer headers after chunked body
19
 * - URI parsing with various flags
20
 */
21
22
#include <stddef.h>
23
#include <stdint.h>
24
#include <stdlib.h>
25
#include <string.h>
26
27
extern "C" {
28
#include "libevent/include/event2/event.h"
29
#include "libevent/include/event2/buffer.h"
30
#include "libevent/include/event2/http.h"
31
#include "libevent/include/event2/http_struct.h"
32
#include "libevent/include/event2/keyvalq_struct.h"
33
#include "libevent/http-internal.h"
34
}
35
36
3.24k
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
37
3.24k
    if (size < 6)
38
4
        return 0;
39
40
    /* Use first 2 bytes as control */
41
3.23k
    uint8_t mode = data[0];
42
3.23k
    uint8_t extra = data[1];
43
3.23k
    data += 2;
44
3.23k
    size -= 2;
45
46
3.23k
    struct evhttp *http_val = evhttp_new(NULL);
47
3.23k
    if (!http_val)
48
0
        return 0;
49
50
    /* Set up a fake connection context for request parsing */
51
3.23k
    struct evhttp_connection evcon;
52
3.23k
    memset(&evcon, 0, sizeof(evcon));
53
3.23k
    evcon.ext_method_cmp = NULL;
54
3.23k
    evcon.max_headers_size = (extra % 4 == 0) ? 256 : 8192;
55
3.23k
    evcon.http_server = http_val;
56
57
    /* === Parse as HTTP request with proper firstline -> headers flow === */
58
3.23k
    if (mode & 0x01) {
59
2.81k
        struct evhttp_request *req = evhttp_request_new(NULL, NULL);
60
2.81k
        if (req) {
61
2.81k
            req->kind = EVHTTP_REQUEST;
62
2.81k
            req->evcon = &evcon;
63
64
2.81k
            struct evbuffer *buf = evbuffer_new();
65
2.81k
            if (buf) {
66
2.81k
                evbuffer_add(buf, data, size);
67
68
2.81k
                enum message_read_status s = evhttp_parse_firstline_(req, buf);
69
2.81k
                if (s == ALL_DATA_READ) {
70
                    /* Successfully parsed request line, now parse headers */
71
127
                    s = evhttp_parse_headers_(req, buf);
72
127
                    if (s == ALL_DATA_READ) {
73
                        /* Successfully parsed headers, examine parsed data */
74
36
                        evhttp_request_get_host(req);
75
36
                        evhttp_request_get_uri(req);
76
36
                        evhttp_request_get_command(req);
77
78
36
                        struct evkeyvalq *hdrs = evhttp_request_get_input_headers(req);
79
36
                        if (hdrs) {
80
36
                            evhttp_find_header(hdrs, "Content-Length");
81
36
                            evhttp_find_header(hdrs, "Transfer-Encoding");
82
36
                            evhttp_find_header(hdrs, "Connection");
83
36
                            evhttp_find_header(hdrs, "Host");
84
36
                            evhttp_find_header(hdrs, "Expect");
85
36
                            evhttp_find_header(hdrs, "Content-Type");
86
36
                        }
87
88
36
                        const struct evhttp_uri *uri = evhttp_request_get_evhttp_uri(req);
89
36
                        if (uri) {
90
36
                            evhttp_uri_get_scheme(uri);
91
36
                            evhttp_uri_get_host(uri);
92
36
                            evhttp_uri_get_port(uri);
93
36
                            evhttp_uri_get_path(uri);
94
36
                            evhttp_uri_get_query(uri);
95
36
                            evhttp_uri_get_fragment(uri);
96
36
                            evhttp_uri_get_userinfo(uri);
97
36
                        }
98
99
                        /* Move remaining data as body */
100
36
                        size_t rem = evbuffer_get_length(buf);
101
36
                        if (rem > 0) {
102
20
                            struct evbuffer *body = evhttp_request_get_input_buffer(req);
103
20
                            if (body)
104
20
                                evbuffer_add_buffer(body, buf);
105
20
                        }
106
36
                    }
107
127
                }
108
2.81k
                evbuffer_free(buf);
109
2.81k
            }
110
2.81k
            evhttp_request_free(req);
111
2.81k
        }
112
2.81k
    }
113
114
    /* === Parse as HTTP response === */
115
3.23k
    if (mode & 0x02) {
116
1.96k
        struct evhttp_request *req = evhttp_request_new(NULL, NULL);
117
1.96k
        if (req) {
118
1.96k
            req->kind = EVHTTP_RESPONSE;
119
1.96k
            req->evcon = &evcon;
120
121
1.96k
            struct evbuffer *buf = evbuffer_new();
122
1.96k
            if (buf) {
123
1.96k
                evbuffer_add(buf, data, size);
124
125
1.96k
                enum message_read_status s = evhttp_parse_firstline_(req, buf);
126
1.96k
                if (s == ALL_DATA_READ) {
127
93
                    s = evhttp_parse_headers_(req, buf);
128
93
                    if (s == ALL_DATA_READ) {
129
11
                        int code = evhttp_request_get_response_code(req);
130
11
                        (void)code;
131
11
                        const char *reason = evhttp_request_get_response_code_line(req);
132
11
                        (void)reason;
133
134
11
                        struct evkeyvalq *hdrs = evhttp_request_get_input_headers(req);
135
11
                        if (hdrs) {
136
11
                            evhttp_find_header(hdrs, "Content-Length");
137
11
                            evhttp_find_header(hdrs, "Transfer-Encoding");
138
11
                            evhttp_find_header(hdrs, "Set-Cookie");
139
11
                            evhttp_find_header(hdrs, "Location");
140
11
                        }
141
11
                    }
142
93
                }
143
1.96k
                evbuffer_free(buf);
144
1.96k
            }
145
1.96k
            evhttp_request_free(req);
146
1.96k
        }
147
1.96k
    }
148
149
    /* === Parse request with prepended valid method lines to stress header parsing === */
150
3.23k
    if (mode & 0x04) {
151
2.07k
        static const char *methods[] = {
152
2.07k
            "POST / HTTP/1.1\r\n",
153
2.07k
            "PUT /resource HTTP/1.1\r\n",
154
2.07k
            "DELETE /item HTTP/1.1\r\n",
155
2.07k
            "PATCH /update HTTP/1.1\r\n",
156
2.07k
            "OPTIONS * HTTP/1.1\r\n",
157
2.07k
            "PROPFIND /dav HTTP/1.1\r\n",
158
2.07k
            "PROPPATCH /dav HTTP/1.1\r\n",
159
2.07k
            "MKCOL /dir HTTP/1.1\r\n",
160
2.07k
            "LOCK /file HTTP/1.1\r\n",
161
2.07k
            "UNLOCK /file HTTP/1.1\r\n",
162
2.07k
            "COPY /src HTTP/1.1\r\n",
163
2.07k
            "MOVE /src HTTP/1.1\r\n",
164
2.07k
        };
165
2.07k
        const char *method = methods[extra % 12];
166
167
2.07k
        struct evhttp_request *req = evhttp_request_new(NULL, NULL);
168
2.07k
        if (req) {
169
2.07k
            req->kind = EVHTTP_REQUEST;
170
2.07k
            req->evcon = &evcon;
171
172
2.07k
            struct evbuffer *buf = evbuffer_new();
173
2.07k
            if (buf) {
174
2.07k
                evbuffer_add(buf, method, strlen(method));
175
2.07k
                evbuffer_add(buf, data, size);
176
177
2.07k
                enum message_read_status s = evhttp_parse_firstline_(req, buf);
178
2.07k
                if (s == ALL_DATA_READ) {
179
2.07k
                    s = evhttp_parse_headers_(req, buf);
180
2.07k
                    if (s == ALL_DATA_READ) {
181
97
                        evhttp_request_get_host(req);
182
97
                        evhttp_request_get_command(req);
183
97
                    }
184
2.07k
                }
185
2.07k
                evbuffer_free(buf);
186
2.07k
            }
187
2.07k
            evhttp_request_free(req);
188
2.07k
        }
189
2.07k
    }
190
191
    /* === Parse response with prepended valid status line to stress header parsing === */
192
3.23k
    if (mode & 0x08) {
193
2.36k
        static const char *status_lines[] = {
194
2.36k
            "HTTP/1.1 200 OK\r\n",
195
2.36k
            "HTTP/1.0 404 Not Found\r\n",
196
2.36k
            "HTTP/1.1 301 Moved Permanently\r\n",
197
2.36k
            "HTTP/1.1 500 Internal Server Error\r\n",
198
2.36k
            "HTTP/1.1 100 Continue\r\n",
199
2.36k
            "HTTP/1.1 204 No Content\r\n",
200
2.36k
            "HTTP/1.0 302 Found\r\n",
201
2.36k
            "HTTP/1.1 403 Forbidden\r\n",
202
2.36k
        };
203
2.36k
        const char *status = status_lines[extra % 8];
204
205
2.36k
        struct evhttp_request *req = evhttp_request_new(NULL, NULL);
206
2.36k
        if (req) {
207
2.36k
            req->kind = EVHTTP_RESPONSE;
208
2.36k
            req->evcon = &evcon;
209
210
2.36k
            struct evbuffer *buf = evbuffer_new();
211
2.36k
            if (buf) {
212
2.36k
                evbuffer_add(buf, status, strlen(status));
213
2.36k
                evbuffer_add(buf, data, size);
214
215
2.36k
                enum message_read_status s = evhttp_parse_firstline_(req, buf);
216
2.36k
                if (s == ALL_DATA_READ) {
217
2.36k
                    s = evhttp_parse_headers_(req, buf);
218
2.36k
                    if (s == ALL_DATA_READ) {
219
82
                        evhttp_request_get_response_code(req);
220
82
                        evhttp_request_get_response_code_line(req);
221
222
82
                        struct evkeyvalq *hdrs = evhttp_request_get_input_headers(req);
223
82
                        if (hdrs) {
224
82
                            evhttp_find_header(hdrs, "Content-Type");
225
82
                            evhttp_find_header(hdrs, "Server");
226
82
                        }
227
82
                    }
228
2.36k
                }
229
2.36k
                evbuffer_free(buf);
230
2.36k
            }
231
2.36k
            evhttp_request_free(req);
232
2.36k
        }
233
2.36k
    }
234
235
    /* === Exercise evhttp_decode_uri with percent-encoded data === */
236
3.23k
    if (mode & 0x10) {
237
2.58k
        char *uri_str = (char *)malloc(size + 1);
238
2.58k
        if (uri_str) {
239
2.58k
            memcpy(uri_str, data, size);
240
2.58k
            uri_str[size] = '\0';
241
242
2.58k
            char *decoded = evhttp_decode_uri(uri_str);
243
2.58k
            if (decoded)
244
2.58k
                free(decoded);
245
246
2.58k
            char *encoded = evhttp_encode_uri(uri_str);
247
2.58k
            if (encoded)
248
2.58k
                free(encoded);
249
            
250
2.58k
            char *escaped = evhttp_htmlescape(uri_str);
251
2.58k
            if (escaped)
252
2.58k
                free(escaped);
253
254
2.58k
            struct evhttp_uri *uri = evhttp_uri_parse(uri_str);
255
2.58k
            if (uri) {
256
675
                char uri_buf[256];
257
675
                evhttp_uri_join(uri, uri_buf, sizeof(uri_buf));
258
675
                evhttp_uri_free(uri);
259
675
            }
260
261
2.58k
            free(uri_str);
262
2.58k
        }
263
2.58k
    }
264
265
    /* === Exercise evhttp_parse_query and evhttp_parse_query_str_flags === */
266
3.23k
    if (mode & 0x20) {
267
2.50k
        char *query = (char *)malloc(size + 1);
268
2.50k
        if (query) {
269
2.50k
            memcpy(query, data, size);
270
2.50k
            query[size] = '\0';
271
272
2.50k
            struct evkeyvalq params;
273
2.50k
            TAILQ_INIT(&params);
274
            
275
            /* Exercise both the simple and flagged versions */
276
2.50k
            if (evhttp_parse_query(query, &params) == 0) {
277
574
                evhttp_clear_headers(&params);
278
574
            }
279
            
280
2.50k
            int flags = (extra & 0x0F);
281
2.50k
            if (evhttp_parse_query_str_flags(query, &params, flags) == 0) {
282
1.55k
                evhttp_clear_headers(&params);
283
1.55k
            }
284
285
2.50k
            free(query);
286
2.50k
        }
287
2.50k
    }
288
289
    /* === Exercise header add/remove/clear operations === */
290
3.23k
    if (mode & 0x40) {
291
2.21k
        struct evkeyvalq headers;
292
2.21k
        TAILQ_INIT(&headers);
293
294
        /* Split fuzz data into key/value pairs to add as headers */
295
2.21k
        size_t pos = 0;
296
2.21k
        int count = 0;
297
37.5k
        while (pos < size && count < 32) {
298
            /* Find a separator for key */
299
36.8k
            size_t key_end = pos;
300
22.8M
            while (key_end < size && data[key_end] != ':' && data[key_end] != '\0')
301
22.7M
                key_end++;
302
36.8k
            if (key_end >= size) break;
303
304
35.3k
            size_t val_start = key_end + 1;
305
35.3k
            size_t val_end = val_start;
306
26.0M
            while (val_end < size && data[val_end] != '\n' && data[val_end] != '\0')
307
25.9M
                val_end++;
308
309
35.3k
            if (key_end > pos) {
310
3.96k
                char *key = (char *)malloc(key_end - pos + 1);
311
3.96k
                char *val = (char *)malloc(val_end - val_start + 1);
312
3.96k
                if (key && val) {
313
3.96k
                    memcpy(key, data + pos, key_end - pos);
314
3.96k
                    key[key_end - pos] = '\0';
315
3.96k
                    memcpy(val, data + val_start, val_end - val_start);
316
3.96k
                    val[val_end - val_start] = '\0';
317
318
3.96k
                    evhttp_add_header(&headers, key, val);
319
3.96k
                    count++;
320
3.96k
                }
321
3.96k
                free(key);
322
3.96k
                free(val);
323
3.96k
            }
324
35.3k
            pos = val_end + 1;
325
35.3k
        }
326
327
        /* Try to find some headers */
328
2.21k
        if (count > 0) {
329
948
            evhttp_find_header(&headers, "Content-Type");
330
948
            evhttp_find_header(&headers, "Host");
331
948
        }
332
333
2.21k
        evhttp_clear_headers(&headers);
334
2.21k
    }
335
336
3.23k
    evhttp_free(http_val);
337
3.23k
    return 0;
338
3.23k
}