/src/haproxy/fuzz_h1_parse.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright 2026 Google LLC |
3 | | * |
4 | | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | | * you may not use this file except in compliance with the License. |
6 | | * You may obtain a copy of the License at |
7 | | * |
8 | | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | | * |
10 | | * Unless required by applicable law or agreed to in writing, software |
11 | | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | | * See the License for the specific language governing permissions and |
14 | | * limitations under the License. |
15 | | */ |
16 | | |
17 | | /* |
18 | | * Fuzzer for HAProxy's HTTP/1 message parser (h1_headers_to_hdr_list). |
19 | | * |
20 | | * This targets the core HTTP/1.1 request and response parsing state machine |
21 | | * in src/h1.c, which is HAProxy's primary attack surface: every HTTP/1 |
22 | | * request from an untrusted client passes through this parser. |
23 | | * |
24 | | * The parser handles: |
25 | | * - Request line parsing (method, URI, version) |
26 | | * - Response status line parsing |
27 | | * - Header field parsing with folding/continuation |
28 | | * - Transfer-Encoding validation (chunked smuggling detection) |
29 | | * - Content-Length parsing and duplicate detection |
30 | | * - Connection and Upgrade header processing |
31 | | * - HTTP/1.0 vs 1.1 semantics |
32 | | * |
33 | | * We use the first byte of fuzz input to select between request and |
34 | | * response parsing modes, maximizing code coverage. |
35 | | */ |
36 | | |
37 | | #include <haproxy/h1.h> |
38 | | #include <haproxy/http-hdr.h> |
39 | | #include <haproxy/global.h> |
40 | | |
41 | | #include <stdint.h> |
42 | | #include <string.h> |
43 | | #include <stdlib.h> |
44 | | |
45 | 0 | #define MAX_HDR_NUM 101 |
46 | | |
47 | 0 | int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { |
48 | 0 | struct h1m h1m; |
49 | 0 | union h1_sl h1sl; |
50 | 0 | struct http_hdr hdrs[MAX_HDR_NUM]; |
51 | 0 | char *buf; |
52 | |
|
53 | 0 | if (size < 2) |
54 | 0 | return 0; |
55 | | |
56 | | /* Use first byte to select request vs response parsing */ |
57 | 0 | int parse_response = data[0] & 1; |
58 | 0 | data++; |
59 | 0 | size--; |
60 | | |
61 | | /* h1_headers_to_hdr_list modifies the buffer in-place (it writes |
62 | | * the sentinel \0 bytes), so we need a mutable copy. */ |
63 | 0 | buf = (char *)malloc(size + 1); |
64 | 0 | if (!buf) |
65 | 0 | return 0; |
66 | 0 | memcpy(buf, data, size); |
67 | 0 | buf[size] = '\0'; |
68 | | |
69 | | /* Ensure global.tune.max_http_hdr is set */ |
70 | 0 | if (global.tune.max_http_hdr == 0) |
71 | 0 | global.tune.max_http_hdr = MAX_HDR_NUM; |
72 | |
|
73 | 0 | if (parse_response) { |
74 | 0 | h1m_init_res(&h1m); |
75 | 0 | } else { |
76 | 0 | h1m_init_req(&h1m); |
77 | 0 | } |
78 | |
|
79 | 0 | h1_headers_to_hdr_list(buf, buf + size, |
80 | 0 | hdrs, MAX_HDR_NUM, |
81 | 0 | &h1m, &h1sl); |
82 | |
|
83 | 0 | free(buf); |
84 | 0 | return 0; |
85 | 0 | } |