Coverage Report

Created: 2026-07-16 07:07

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