Coverage Report

Created: 2026-09-01 06:52

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.26k
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
37
3.26k
    if (size < 6)
38
4
        return 0;
39
40
    /* Use first 2 bytes as control */
41
3.25k
    uint8_t mode = data[0];
42
3.25k
    uint8_t extra = data[1];
43
3.25k
    data += 2;
44
3.25k
    size -= 2;
45
46
3.25k
    struct evhttp *http_val = evhttp_new(NULL);
47
3.25k
    if (!http_val)
48
0
        return 0;
49
50
    /* Set up a fake connection context for request parsing */
51
3.25k
    struct evhttp_connection evcon;
52
3.25k
    memset(&evcon, 0, sizeof(evcon));
53
3.25k
    evcon.ext_method_cmp = NULL;
54
3.25k
    evcon.max_headers_size = (extra % 4 == 0) ? 256 : 8192;
55
3.25k
    evcon.http_server = http_val;
56
57
    /* === Parse as HTTP request with proper firstline -> headers flow === */
58
3.25k
    if (mode & 0x01) {
59
2.65k
        struct evhttp_request *req = evhttp_request_new(NULL, NULL);
60
2.65k
        if (req) {
61
2.65k
            req->kind = EVHTTP_REQUEST;
62
2.65k
            req->evcon = &evcon;
63
64
2.65k
            struct evbuffer *buf = evbuffer_new();
65
2.65k
            if (buf) {
66
2.65k
                evbuffer_add(buf, data, size);
67
68
2.65k
                enum message_read_status s = evhttp_parse_firstline_(req, buf);
69
2.65k
                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
45
                        evhttp_request_get_host(req);
75
45
                        evhttp_request_get_uri(req);
76
45
                        evhttp_request_get_command(req);
77
78
45
                        struct evkeyvalq *hdrs = evhttp_request_get_input_headers(req);
79
45
                        if (hdrs) {
80
45
                            evhttp_find_header(hdrs, "Content-Length");
81
45
                            evhttp_find_header(hdrs, "Transfer-Encoding");
82
45
                            evhttp_find_header(hdrs, "Connection");
83
45
                            evhttp_find_header(hdrs, "Host");
84
45
                            evhttp_find_header(hdrs, "Expect");
85
45
                            evhttp_find_header(hdrs, "Content-Type");
86
45
                        }
87
88
45
                        const struct evhttp_uri *uri = evhttp_request_get_evhttp_uri(req);
89
45
                        if (uri) {
90
45
                            evhttp_uri_get_scheme(uri);
91
45
                            evhttp_uri_get_host(uri);
92
45
                            evhttp_uri_get_port(uri);
93
45
                            evhttp_uri_get_path(uri);
94
45
                            evhttp_uri_get_query(uri);
95
45
                            evhttp_uri_get_fragment(uri);
96
45
                            evhttp_uri_get_userinfo(uri);
97
45
                        }
98
99
                        /* Move remaining data as body */
100
45
                        size_t rem = evbuffer_get_length(buf);
101
45
                        if (rem > 0) {
102
26
                            struct evbuffer *body = evhttp_request_get_input_buffer(req);
103
26
                            if (body)
104
26
                                evbuffer_add_buffer(body, buf);
105
26
                        }
106
45
                    }
107
127
                }
108
2.65k
                evbuffer_free(buf);
109
2.65k
            }
110
2.65k
            evhttp_request_free(req);
111
2.65k
        }
112
2.65k
    }
113
114
    /* === Parse as HTTP response === */
115
3.25k
    if (mode & 0x02) {
116
1.82k
        struct evhttp_request *req = evhttp_request_new(NULL, NULL);
117
1.82k
        if (req) {
118
1.82k
            req->kind = EVHTTP_RESPONSE;
119
1.82k
            req->evcon = &evcon;
120
121
1.82k
            struct evbuffer *buf = evbuffer_new();
122
1.82k
            if (buf) {
123
1.82k
                evbuffer_add(buf, data, size);
124
125
1.82k
                enum message_read_status s = evhttp_parse_firstline_(req, buf);
126
1.82k
                if (s == ALL_DATA_READ) {
127
88
                    s = evhttp_parse_headers_(req, buf);
128
88
                    if (s == ALL_DATA_READ) {
129
12
                        int code = evhttp_request_get_response_code(req);
130
12
                        (void)code;
131
12
                        const char *reason = evhttp_request_get_response_code_line(req);
132
12
                        (void)reason;
133
134
12
                        struct evkeyvalq *hdrs = evhttp_request_get_input_headers(req);
135
12
                        if (hdrs) {
136
12
                            evhttp_find_header(hdrs, "Content-Length");
137
12
                            evhttp_find_header(hdrs, "Transfer-Encoding");
138
12
                            evhttp_find_header(hdrs, "Set-Cookie");
139
12
                            evhttp_find_header(hdrs, "Location");
140
12
                        }
141
12
                    }
142
88
                }
143
1.82k
                evbuffer_free(buf);
144
1.82k
            }
145
1.82k
            evhttp_request_free(req);
146
1.82k
        }
147
1.82k
    }
148
149
    /* === Parse request with prepended valid method lines to stress header parsing === */
150
3.25k
    if (mode & 0x04) {
151
2.11k
        static const char *methods[] = {
152
2.11k
            "POST / HTTP/1.1\r\n",
153
2.11k
            "PUT /resource HTTP/1.1\r\n",
154
2.11k
            "DELETE /item HTTP/1.1\r\n",
155
2.11k
            "PATCH /update HTTP/1.1\r\n",
156
2.11k
            "OPTIONS * HTTP/1.1\r\n",
157
2.11k
            "PROPFIND /dav HTTP/1.1\r\n",
158
2.11k
            "PROPPATCH /dav HTTP/1.1\r\n",
159
2.11k
            "MKCOL /dir HTTP/1.1\r\n",
160
2.11k
            "LOCK /file HTTP/1.1\r\n",
161
2.11k
            "UNLOCK /file HTTP/1.1\r\n",
162
2.11k
            "COPY /src HTTP/1.1\r\n",
163
2.11k
            "MOVE /src HTTP/1.1\r\n",
164
2.11k
        };
165
2.11k
        const char *method = methods[extra % 12];
166
167
2.11k
        struct evhttp_request *req = evhttp_request_new(NULL, NULL);
168
2.11k
        if (req) {
169
2.11k
            req->kind = EVHTTP_REQUEST;
170
2.11k
            req->evcon = &evcon;
171
172
2.11k
            struct evbuffer *buf = evbuffer_new();
173
2.11k
            if (buf) {
174
2.11k
                evbuffer_add(buf, method, strlen(method));
175
2.11k
                evbuffer_add(buf, data, size);
176
177
2.11k
                enum message_read_status s = evhttp_parse_firstline_(req, buf);
178
2.11k
                if (s == ALL_DATA_READ) {
179
2.11k
                    s = evhttp_parse_headers_(req, buf);
180
2.11k
                    if (s == ALL_DATA_READ) {
181
88
                        evhttp_request_get_host(req);
182
88
                        evhttp_request_get_command(req);
183
88
                    }
184
2.11k
                }
185
2.11k
                evbuffer_free(buf);
186
2.11k
            }
187
2.11k
            evhttp_request_free(req);
188
2.11k
        }
189
2.11k
    }
190
191
    /* === Parse response with prepended valid status line to stress header parsing === */
192
3.25k
    if (mode & 0x08) {
193
2.25k
        static const char *status_lines[] = {
194
2.25k
            "HTTP/1.1 200 OK\r\n",
195
2.25k
            "HTTP/1.0 404 Not Found\r\n",
196
2.25k
            "HTTP/1.1 301 Moved Permanently\r\n",
197
2.25k
            "HTTP/1.1 500 Internal Server Error\r\n",
198
2.25k
            "HTTP/1.1 100 Continue\r\n",
199
2.25k
            "HTTP/1.1 204 No Content\r\n",
200
2.25k
            "HTTP/1.0 302 Found\r\n",
201
2.25k
            "HTTP/1.1 403 Forbidden\r\n",
202
2.25k
        };
203
2.25k
        const char *status = status_lines[extra % 8];
204
205
2.25k
        struct evhttp_request *req = evhttp_request_new(NULL, NULL);
206
2.25k
        if (req) {
207
2.25k
            req->kind = EVHTTP_RESPONSE;
208
2.25k
            req->evcon = &evcon;
209
210
2.25k
            struct evbuffer *buf = evbuffer_new();
211
2.25k
            if (buf) {
212
2.25k
                evbuffer_add(buf, status, strlen(status));
213
2.25k
                evbuffer_add(buf, data, size);
214
215
2.25k
                enum message_read_status s = evhttp_parse_firstline_(req, buf);
216
2.25k
                if (s == ALL_DATA_READ) {
217
2.25k
                    s = evhttp_parse_headers_(req, buf);
218
2.25k
                    if (s == ALL_DATA_READ) {
219
85
                        evhttp_request_get_response_code(req);
220
85
                        evhttp_request_get_response_code_line(req);
221
222
85
                        struct evkeyvalq *hdrs = evhttp_request_get_input_headers(req);
223
85
                        if (hdrs) {
224
85
                            evhttp_find_header(hdrs, "Content-Type");
225
85
                            evhttp_find_header(hdrs, "Server");
226
85
                        }
227
85
                    }
228
2.25k
                }
229
2.25k
                evbuffer_free(buf);
230
2.25k
            }
231
2.25k
            evhttp_request_free(req);
232
2.25k
        }
233
2.25k
    }
234
235
    /* === Exercise evhttp_decode_uri with percent-encoded data === */
236
3.25k
    if (mode & 0x10) {
237
2.63k
        char *uri_str = (char *)malloc(size + 1);
238
2.63k
        if (uri_str) {
239
2.63k
            memcpy(uri_str, data, size);
240
2.63k
            uri_str[size] = '\0';
241
242
2.63k
            char *decoded = evhttp_decode_uri(uri_str);
243
2.63k
            if (decoded)
244
2.63k
                free(decoded);
245
246
2.63k
            char *encoded = evhttp_encode_uri(uri_str);
247
2.63k
            if (encoded)
248
2.63k
                free(encoded);
249
            
250
2.63k
            char *escaped = evhttp_htmlescape(uri_str);
251
2.63k
            if (escaped)
252
2.63k
                free(escaped);
253
254
2.63k
            struct evhttp_uri *uri = evhttp_uri_parse(uri_str);
255
2.63k
            if (uri) {
256
762
                char uri_buf[256];
257
762
                evhttp_uri_join(uri, uri_buf, sizeof(uri_buf));
258
762
                evhttp_uri_free(uri);
259
762
            }
260
261
2.63k
            free(uri_str);
262
2.63k
        }
263
2.63k
    }
264
265
    /* === Exercise evhttp_parse_query and evhttp_parse_query_str_flags === */
266
3.25k
    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
605
                evhttp_clear_headers(&params);
278
605
            }
279
            
280
2.50k
            int flags = (extra & 0x0F);
281
2.50k
            if (evhttp_parse_query_str_flags(query, &params, flags) == 0) {
282
1.63k
                evhttp_clear_headers(&params);
283
1.63k
            }
284
285
2.50k
            free(query);
286
2.50k
        }
287
2.50k
    }
288
289
    /* === Exercise header add/remove/clear operations === */
290
3.25k
    if (mode & 0x40) {
291
2.09k
        struct evkeyvalq headers;
292
2.09k
        TAILQ_INIT(&headers);
293
294
        /* Split fuzz data into key/value pairs to add as headers */
295
2.09k
        size_t pos = 0;
296
2.09k
        int count = 0;
297
1.26M
        while (pos < size && count < 32) {
298
            /* Find a separator for key */
299
1.26M
            size_t key_end = pos;
300
25.1M
            while (key_end < size && data[key_end] != ':' && data[key_end] != '\0')
301
23.8M
                key_end++;
302
1.26M
            if (key_end >= size) break;
303
304
1.26M
            size_t val_start = key_end + 1;
305
1.26M
            size_t val_end = val_start;
306
44.0M
            while (val_end < size && data[val_end] != '\n' && data[val_end] != '\0')
307
42.7M
                val_end++;
308
309
1.26M
            if (key_end > pos) {
310
2.72k
                char *key = (char *)malloc(key_end - pos + 1);
311
2.72k
                char *val = (char *)malloc(val_end - val_start + 1);
312
2.72k
                if (key && val) {
313
2.72k
                    memcpy(key, data + pos, key_end - pos);
314
2.72k
                    key[key_end - pos] = '\0';
315
2.72k
                    memcpy(val, data + val_start, val_end - val_start);
316
2.72k
                    val[val_end - val_start] = '\0';
317
318
2.72k
                    evhttp_add_header(&headers, key, val);
319
2.72k
                    count++;
320
2.72k
                }
321
2.72k
                free(key);
322
2.72k
                free(val);
323
2.72k
            }
324
1.26M
            pos = val_end + 1;
325
1.26M
        }
326
327
        /* Try to find some headers */
328
2.09k
        if (count > 0) {
329
921
            evhttp_find_header(&headers, "Content-Type");
330
921
            evhttp_find_header(&headers, "Host");
331
921
        }
332
333
2.09k
        evhttp_clear_headers(&headers);
334
2.09k
    }
335
336
3.25k
    evhttp_free(http_val);
337
3.25k
    return 0;
338
3.25k
}