Coverage Report

Created: 2026-04-01 06:31

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