Line | Count | Source |
1 | | // Copyright 2025 Google LLC |
2 | | // |
3 | | // Licensed under the Apache License, Version 2.0 (the "License"); |
4 | | // you may not use this file except in compliance with the License. |
5 | | // You may obtain a copy of the License at |
6 | | // |
7 | | // http://www.apache.org/licenses/LICENSE-2.0 |
8 | | // |
9 | | // Unless required by applicable law or agreed to in writing, software |
10 | | // distributed under the License is distributed on an "AS IS" BASIS, |
11 | | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | | // See the License for the specific language governing permissions and |
13 | | // limitations under the License. |
14 | | // |
15 | | //////////////////////////////////////////////////////////////////////////////// |
16 | | #include <cstring> |
17 | | #include <stddef.h> |
18 | | #include <stdint.h> |
19 | | #include <string.h> |
20 | | #include <stdlib.h> |
21 | | #include <algorithm> |
22 | | #include <vector> |
23 | | |
24 | | #include <fuzzer/FuzzedDataProvider.h> |
25 | | extern "C" { |
26 | | #include "mhd_str.h" |
27 | | #include "microhttpd2.h" |
28 | | } |
29 | | |
30 | 1.55k | static void fuzz_tokens(FuzzedDataProvider& fdp) { |
31 | | // Prepare random string for string comparison |
32 | 1.55k | std::string payload1 = fdp.ConsumeRandomLengthString(1024); |
33 | 1.55k | std::string payload2 = fdp.ConsumeRandomLengthString(1024); |
34 | 1.55k | std::string payload3 = fdp.ConsumeRandomLengthString(1024); |
35 | 1.55k | const char *payload_str1 = payload1.c_str(); |
36 | 1.55k | const char *payload_str2 = payload2.c_str(); |
37 | 1.55k | const char *payload_str3 = payload3.c_str(); |
38 | 1.55k | size_t payload_size1 = payload1.size(); |
39 | 1.55k | size_t payload_size2 = payload2.size(); |
40 | 1.55k | size_t payload_size3 = payload3.size(); |
41 | | |
42 | | // Fuzz mhd_str_equal_caseless |
43 | 1.55k | mhd_str_equal_caseless(payload_str1, payload_str2); |
44 | | |
45 | | // Fuzz mhd_str_equal_caseless_n |
46 | 1.55k | mhd_str_equal_caseless_n(payload_str1, payload_str2, fdp.ConsumeIntegral<size_t>()); |
47 | | |
48 | | // Fuzz mhd_str_equal_caseless_bin_n |
49 | 1.55k | const size_t min_len = std::min(payload_size1, payload_size2); |
50 | 1.55k | if (min_len) { |
51 | 396 | mhd_str_equal_caseless_bin_n(payload_str1, payload_str2, min_len); |
52 | 396 | } |
53 | | |
54 | | // Fuzz mhd_str_has_token_caseless |
55 | 1.55k | mhd_str_has_token_caseless(payload_str1, payload_str2, payload_size1); |
56 | 1.55k | mhd_str_has_token_caseless(payload_str1, payload_str2, payload_size2); |
57 | | |
58 | | // Fuzz mhd_str_remove_token_caseless |
59 | 1.55k | ssize_t out_sz = (ssize_t)fdp.ConsumeIntegralInRange<int>(1, 1024); |
60 | 1.55k | char *out_buf = (char*) malloc((size_t)out_sz); |
61 | 1.55k | mhd_str_remove_token_caseless(payload_str1, payload_size1, payload_str2, payload_size2, |
62 | 1.55k | out_buf, &out_sz); |
63 | 1.55k | free(out_buf); |
64 | | |
65 | | // Fuzz mhd_str_starts_with_token_opt_param |
66 | 1.55k | struct MHD_String mhd_str1 { |
67 | 1.55k | payload_size1, payload_str1 |
68 | 1.55k | }; |
69 | 1.55k | struct MHD_String mhd_str2 { |
70 | 1.55k | payload_size2, payload_str2 |
71 | 1.55k | }; |
72 | 1.55k | mhd_str_starts_with_token_opt_param(&mhd_str1, &mhd_str2); |
73 | | |
74 | | // Fuzz mhd_str_starts_with_token_req_param |
75 | 1.55k | bool needs_uni = fdp.ConsumeBool(); |
76 | 1.55k | struct MHD_String mhd_str3 { |
77 | 1.55k | payload_size3, payload_str3 |
78 | 1.55k | }; |
79 | 1.55k | struct mhd_BufferConst str3_buf { 0, nullptr }; |
80 | 1.55k | mhd_str_starts_with_token_req_param(&mhd_str1, &mhd_str2, &mhd_str3, &str3_buf, &needs_uni); |
81 | 1.55k | } |
82 | | |
83 | 1.52k | static void fuzz_conversion(FuzzedDataProvider& fdp) { |
84 | | // Prepare random string for string/int conversion |
85 | 1.52k | std::string payload = fdp.ConsumeRandomLengthString(1024); |
86 | 1.52k | const char *payload_str = payload.c_str(); |
87 | 1.52k | size_t payload_size = payload.size(); |
88 | | |
89 | 1.52k | uint_fast32_t u32 = 0; |
90 | 1.52k | uint_fast64_t u64 = 0; |
91 | 1.52k | char small[4], big[128]; |
92 | 1.52k | size_t max_len = fdp.ConsumeIntegralInRange<size_t>(0, payload_size); |
93 | | |
94 | | // Fuzz conversion between string and uint64 with random payload |
95 | 1.52k | mhd_str_to_uint64(payload_str, &u64); |
96 | 1.52k | mhd_str_to_uint64_n(payload_str, max_len, &u64); |
97 | 1.52k | mhd_strx_to_uint64(payload_str, &u64); |
98 | 1.52k | mhd_strx_to_uint64_n(payload_str, max_len, &u64); |
99 | 1.52k | mhd_uint64_to_str((uint_fast64_t)fdp.ConsumeIntegral<uint64_t>(), small, sizeof(small)); |
100 | 1.52k | mhd_uint64_to_str((uint_fast64_t)fdp.ConsumeIntegral<uint64_t>(), big, sizeof(big)); |
101 | | |
102 | | // Fuzz string to uint32 conversion with random payload string |
103 | 1.52k | mhd_strx_to_uint32(payload_str, &u32); |
104 | 1.52k | mhd_strx_to_uint32_n(payload_str, max_len, &u32); |
105 | 1.52k | mhd_uint32_to_strx((uint_fast32_t)fdp.ConsumeIntegral<uint32_t>(), small, sizeof(small)); |
106 | 1.52k | mhd_uint32_to_strx((uint_fast32_t)fdp.ConsumeIntegral<uint32_t>(), big, sizeof(big)); |
107 | | |
108 | | // Fuzz uint16 to string conversion with random payload |
109 | 1.52k | mhd_uint16_to_str((uint_least16_t)fdp.ConsumeIntegralInRange<unsigned>(0, 65535), small, sizeof(small)); |
110 | 1.52k | mhd_uint16_to_str((uint_least16_t)fdp.ConsumeIntegralInRange<unsigned>(0, 65535), big, sizeof(big)); |
111 | | |
112 | | // Fuzz uint8 to string conversion with random payload |
113 | 1.52k | uint8_t min_digits = fdp.ConsumeIntegralInRange<uint8_t>(0, 5); |
114 | 1.52k | mhd_uint8_to_str_pad((uint8_t)fdp.ConsumeIntegral<uint8_t>(), min_digits, small, sizeof(small)); |
115 | 1.52k | mhd_uint8_to_str_pad((uint8_t)fdp.ConsumeIntegral<uint8_t>(), min_digits, big, sizeof(big)); |
116 | 1.52k | } |
117 | | |
118 | 381 | static void fuzz_decode(FuzzedDataProvider& fdp) { |
119 | | // Prepare random data for string decode |
120 | 381 | bool ignored = false; |
121 | 381 | std::string payload = fdp.ConsumeRandomLengthString(1024); |
122 | 381 | char *payload_str = payload.data(); |
123 | 381 | size_t payload_size = payload.size(); |
124 | | |
125 | | // Fuzz decode functions with random payload |
126 | 381 | char *out1 = (char*) malloc(payload_size); |
127 | 381 | char *out2 = (char*) malloc(payload_size); |
128 | 381 | if (out1) { |
129 | 381 | mhd_str_pct_decode_strict_n(payload_str, payload_size, out1, payload_size); |
130 | 381 | } |
131 | 381 | if (out2) { |
132 | 381 | mhd_str_pct_decode_lenient_n(payload_str, payload_size, out2, payload_size, &ignored); |
133 | 381 | } |
134 | | |
135 | | // Fuzz decode in place functions with random payload |
136 | 381 | mhd_str_pct_decode_in_place_strict(payload_str); |
137 | 381 | mhd_str_pct_decode_in_place_lenient(payload_str, &ignored); |
138 | | |
139 | 381 | free(out1); |
140 | 381 | free(out2); |
141 | 381 | } |
142 | | |
143 | 508 | static void fuzz_quoted(FuzzedDataProvider& fdp) { |
144 | | // Prepare random data for quote and equality check |
145 | 508 | std::string payload1 = fdp.ConsumeRandomLengthString(1024); |
146 | 508 | std::string payload2 = fdp.ConsumeRandomLengthString(1024); |
147 | 508 | const char *payload_str1 = payload1.c_str(); |
148 | 508 | const char *payload_str2 = payload2.c_str(); |
149 | 508 | size_t payload_size1 = payload1.size(); |
150 | 508 | size_t payload_size2 = payload2.size(); |
151 | | |
152 | | // Fuzz mhd_str_equal_quoted_bin_n with random string payload as binary |
153 | 508 | mhd_str_equal_quoted_bin_n(payload_str1, payload_size1, payload_str2, payload_size2); |
154 | | |
155 | | // Fuzz mhd_str_equal_caseless_quoted_bin_n with random string payload as binary |
156 | 508 | mhd_str_equal_caseless_quoted_bin_n(payload_str1, payload_size1, payload_str2, payload_size2); |
157 | | |
158 | | // Fuzz mhd_str_quote and mhd_str_unquote with random string payload |
159 | 508 | size_t max_out = payload_size1 * 2; |
160 | 508 | char *out = (char*) malloc(max_out); |
161 | 508 | if (out) { |
162 | 508 | mhd_str_quote(payload_str1, payload_size1, out, max_out); |
163 | 508 | mhd_str_unquote(payload_str1, payload_size1, out); |
164 | 508 | } |
165 | 508 | free(out); |
166 | | |
167 | 508 | max_out = payload_size2 * 2; |
168 | 508 | out = (char*) malloc(max_out); |
169 | 508 | if (out) { |
170 | 508 | mhd_str_quote(payload_str2, payload_size2, out, max_out); |
171 | 508 | mhd_str_unquote(payload_str2, payload_size2, out); |
172 | 508 | } |
173 | 508 | free(out); |
174 | 508 | } |
175 | | |
176 | 327 | static void fuzz_base64(FuzzedDataProvider& fdp) { |
177 | | // Prepare random data for base64 conversion |
178 | 327 | std::string payload = fdp.ConsumeRandomLengthString(1024); |
179 | 327 | char *payload_str = payload.data(); |
180 | 327 | size_t payload_size = payload.size(); |
181 | | |
182 | | // Prepare a valid base64 string from random payload |
183 | 327 | static const char valid_chars[] = |
184 | 327 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; |
185 | 85.8k | for (size_t i = 0; i < payload_size; i++) { |
186 | 85.4k | payload_str[i] = valid_chars[((uint8_t)i) % 64]; |
187 | 85.4k | } |
188 | | |
189 | | // Fuzz mhd_base64_to_bin_n with the random base64 string |
190 | 327 | size_t max_out = (payload.size() / 4) * 4; |
191 | 327 | uint8_t* out = (uint8_t*) malloc(payload_size); |
192 | 327 | if (out) { |
193 | 327 | mhd_base64_to_bin_n(payload_str, payload_size, out, max_out); |
194 | 327 | free(out); |
195 | 327 | } |
196 | 327 | } |
197 | | |
198 | 167 | static void fuzz_transformation(FuzzedDataProvider& fdp) { |
199 | | // Fuzz targets in multiple rounds |
200 | 740 | for (int i = 0; i < fdp.ConsumeIntegralInRange<unsigned>(1, 8); i++) { |
201 | | // Generate random integer |
202 | 573 | int value = fdp.ConsumeIntegral<int>(); |
203 | | |
204 | | // Fuzz conversion functions |
205 | 573 | MHD_http_method_to_string(static_cast<MHD_HTTP_Method>(value)); |
206 | 573 | MHD_predef_header_to_string(static_cast<MHD_PredefinedHeader>(value)); |
207 | 573 | MHD_protocol_version_to_string(static_cast<MHD_HTTP_ProtocolVersion>(value)); |
208 | 573 | } |
209 | 167 | } |
210 | | |
211 | 267 | static void fuzz_hex_conversion(FuzzedDataProvider& fdp) { |
212 | | // Prepare random data for hex conversion |
213 | 267 | std::string payload = fdp.ConsumeRandomLengthString(1024); |
214 | 267 | char *payload_str = payload.data(); |
215 | 267 | size_t payload_size = payload.size(); |
216 | | |
217 | | // Fuzz mhd_hex_to_bin with random payload |
218 | 267 | uint8_t *bin_out = (uint8_t*) malloc(payload_size); |
219 | 267 | if (bin_out) { |
220 | 267 | mhd_hex_to_bin(payload_str, payload_size, bin_out); |
221 | 267 | free(bin_out); |
222 | 267 | } |
223 | | |
224 | | // Fuzz mhd_bin_to_hex with random payload |
225 | 267 | char *hex_out = (char *) malloc(payload_size * 2); |
226 | 267 | if (hex_out) { |
227 | 267 | if (!payload.empty()) { |
228 | 206 | mhd_bin_to_hex(payload_str, payload_size, hex_out); |
229 | 206 | } |
230 | 267 | free(hex_out); |
231 | 267 | } |
232 | | |
233 | 267 | char *hexz_out = (char *) malloc(payload_size * 2 + 1); |
234 | 267 | if (hexz_out) { |
235 | 267 | if (!payload.empty()) { |
236 | 206 | mhd_bin_to_hex_z(payload_str, payload_size, hexz_out); |
237 | 206 | } |
238 | 267 | free(hexz_out); |
239 | 267 | } |
240 | 267 | } |
241 | | |
242 | 2.96k | extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { |
243 | 2.96k | FuzzedDataProvider fdp(data, size); |
244 | | |
245 | 7.80k | for (int i = 0; i < fdp.ConsumeIntegralInRange<unsigned>(1, 6); i++) { |
246 | 4.83k | switch (fdp.ConsumeIntegralInRange<int>(0, 7)) { |
247 | 1.55k | case 0: fuzz_tokens(fdp); break; |
248 | 1.52k | case 1: fuzz_conversion(fdp); break; |
249 | 381 | case 2: fuzz_decode(fdp); break; |
250 | 508 | case 3: fuzz_quoted(fdp); break; |
251 | 327 | case 4: fuzz_base64(fdp); break; |
252 | 167 | case 5: fuzz_transformation(fdp); break; |
253 | 267 | case 6: fuzz_hex_conversion(fdp); break; |
254 | 4.83k | } |
255 | 4.83k | } |
256 | 2.96k | return 0; |
257 | 2.96k | } |