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 <stdint.h> |
17 | | #include <stddef.h> |
18 | | #include <string> |
19 | | #include <stdlib.h> |
20 | | #include <string.h> |
21 | | |
22 | | #include "fuzzer/FuzzedDataProvider.h" |
23 | | #include "connection_helper.h" |
24 | | |
25 | | extern "C" { |
26 | | #include "daemon_funcs.h" |
27 | | #include "mempool_funcs.h" |
28 | | #include "post_parser_funcs.h" |
29 | | #include "stream_funcs.h" |
30 | | #include "stream_process_request.h" |
31 | | } |
32 | | |
33 | | // Helper to fuzz mhd_stream_process_post_finish |
34 | 2.45k | static void fuzz_mhd_stream_process_post_finish(MHD_Connection& connection, MHD_Daemon& daemon, const std::string& body) { |
35 | 2.45k | struct mhd_PostParserData *p = &connection.rq.u_proc.post; |
36 | 2.45k | size_t pos = p->next_parse_pos; |
37 | 2.45k | size_t need = ((p->lbuf_used > pos) ? p->lbuf_used : pos) + 1; // +1 for NUL |
38 | 2.45k | if (connection.rq.cntn.lbuf.size < need) { |
39 | 751 | size_t delta = need - connection.rq.cntn.lbuf.size; |
40 | 751 | if (delta != 0) |
41 | 751 | mhd_daemon_extend_lbuf_up_to(&daemon, delta, &connection.rq.cntn.lbuf); |
42 | 751 | } |
43 | | |
44 | 2.45k | if (connection.rq.cntn.lbuf.data == nullptr && !body.empty()) { |
45 | 0 | size_t to_copy = (body.size() < (size_t)p->lbuf_limit) ? body.size() : (size_t)p->lbuf_limit; |
46 | 0 | size_t min_needed = to_copy + 1; // 1 byte for \0 terminator |
47 | 0 | if (connection.rq.cntn.lbuf.size < min_needed) { |
48 | 0 | size_t delta = min_needed - connection.rq.cntn.lbuf.size; |
49 | 0 | if (delta != 0) |
50 | 0 | mhd_daemon_extend_lbuf_up_to(&daemon, delta, &connection.rq.cntn.lbuf); |
51 | 0 | } |
52 | 0 | if (connection.rq.cntn.lbuf.data != nullptr) { |
53 | 0 | memcpy(connection.rq.cntn.lbuf.data, body.data(), to_copy); |
54 | 0 | p->lbuf_used = to_copy; |
55 | 0 | } |
56 | 0 | } |
57 | | |
58 | | // Fail back to Text encoding |
59 | 2.45k | if (p->enc == MHD_HTTP_POST_ENCODING_OTHER) { |
60 | 227 | p->enc = MHD_HTTP_POST_ENCODING_TEXT_PLAIN; |
61 | 227 | } |
62 | 2.45k | mhd_stream_prepare_for_post_parse(&connection); |
63 | 2.45k | mhd_stream_process_post_finish(&connection); |
64 | | |
65 | 2.45k | bool can_finish = (connection.rq.cntn.lbuf.data != nullptr); |
66 | 2.45k | if (can_finish && connection.rq.u_proc.post.enc == MHD_HTTP_POST_ENCODING_FORM_URLENCODED) { |
67 | 0 | size_t pos = connection.rq.u_proc.post.next_parse_pos; |
68 | 0 | if (pos >= connection.rq.cntn.lbuf.size) { |
69 | 0 | mhd_daemon_extend_lbuf_up_to(&daemon, 1, &connection.rq.cntn.lbuf); |
70 | 0 | if (pos >= connection.rq.cntn.lbuf.size) |
71 | 0 | can_finish = false; |
72 | 0 | } |
73 | 0 | } |
74 | | |
75 | 2.45k | if (can_finish) { |
76 | 0 | if (connection.rq.u_proc.post.enc == MHD_HTTP_POST_ENCODING_OTHER) { |
77 | 0 | if (connection.rq.app_act.head_act.data.post_parse.enc == MHD_HTTP_POST_ENCODING_OTHER) |
78 | 0 | connection.rq.app_act.head_act.data.post_parse.enc = MHD_HTTP_POST_ENCODING_TEXT_PLAIN; |
79 | 0 | mhd_stream_prepare_for_post_parse(&connection); |
80 | 0 | } |
81 | 0 | mhd_stream_process_post_finish(&connection); |
82 | 0 | } |
83 | 2.45k | } |
84 | | |
85 | | |
86 | | // Initialising the memory pool |
87 | 2 | extern "C" int LLVMFuzzerInitialize() { |
88 | 2 | g_pool = mhd_pool_create(g_pool_size, MHD_MEMPOOL_ZEROING_ON_RESET); |
89 | 2 | atexit(destroy_global_pool); |
90 | 2 | return 0; |
91 | 2 | } |
92 | | |
93 | 2.74k | extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { |
94 | 2.74k | if (size == 0 || g_pool == nullptr) { |
95 | 0 | return 0; |
96 | 0 | } |
97 | | |
98 | 2.74k | FuzzedDataProvider fdp(data, size); |
99 | | |
100 | | // Reseting the memory pool for each iteartion |
101 | 2.74k | mhd_pool_destroy(g_pool); |
102 | 2.74k | g_pool = mhd_pool_create(g_pool_size, MHD_MEMPOOL_ZEROING_ON_RESET); |
103 | | |
104 | | // Initialising the daemon, connection and other MHD components |
105 | 2.74k | MHD_Daemon daemon; |
106 | 2.74k | MHD_Connection connection; |
107 | 2.74k | init_daemon_connection(fdp, daemon, connection); |
108 | 2.74k | init_parsing_configuration(fdp, connection); |
109 | 2.74k | init_connection_buffer(fdp, connection); |
110 | 2.74k | prepare_headers_and_parse(connection, size); |
111 | | |
112 | | // Randomly choose how many targets to fuzz |
113 | 2.74k | std::vector<int> selectors; |
114 | 7.51k | for (int i = 0; i < fdp.ConsumeIntegralInRange<int>(1, 8); i++) { |
115 | 4.77k | selectors.push_back(fdp.ConsumeIntegralInRange<int>(0, 9)); |
116 | 4.77k | } |
117 | | |
118 | | // Generate random body and stream it in the connection |
119 | 2.74k | bool use_stream_body = fdp.ConsumeBool(); |
120 | | |
121 | | // Use remaining bytes to generate random body for fuzzing |
122 | 2.74k | std::string body = fdp.ConsumeRemainingBytesAsString(); |
123 | 2.74k | size_t body_size = body.size(); |
124 | 2.74k | if (body_size == 0) { |
125 | 287 | return 0; |
126 | 287 | } |
127 | 2.45k | prepare_body_and_process(connection, body, body_size, use_stream_body); |
128 | | |
129 | | // Fuzz random round of target functions |
130 | 4.44k | for (int selector : selectors) { |
131 | 4.44k | switch (selector) { |
132 | 1.13k | case 0: { |
133 | 1.13k | mhd_stream_is_timeout_expired(&connection); |
134 | 1.13k | break; |
135 | 0 | } |
136 | 372 | case 1: { |
137 | 372 | mhd_stream_switch_to_rq_headers_proc(&connection); |
138 | 372 | break; |
139 | 0 | } |
140 | 379 | case 2: { |
141 | 379 | const struct MHD_UploadAction* act = &kContinueAction; |
142 | 379 | mhd_stream_process_upload_action(&connection, &kContinueAction, false); |
143 | 379 | mhd_stream_process_upload_action(&connection, &kSuspend, false); |
144 | 379 | mhd_stream_process_upload_action(&connection, &kAbort, true); |
145 | 379 | break; |
146 | 0 | } |
147 | 225 | case 3: { |
148 | 225 | connection.stage = mhd_HTTP_STAGE_REQ_RECV_FINISHED; |
149 | 225 | mhd_stream_process_req_recv_finished(&connection); |
150 | 225 | break; |
151 | 0 | } |
152 | 190 | case 4: { |
153 | 190 | mhd_stream_reset_rq_hdr_proc_state(&connection); |
154 | 190 | break; |
155 | 0 | } |
156 | 811 | case 5: { |
157 | 811 | mhd_stream_alloc_memory(&connection, 1024); |
158 | 811 | break; |
159 | 0 | } |
160 | 303 | case 6: { |
161 | | // Safe guard for out of buffer space |
162 | 303 | if (connection.write_buffer_send_offset > connection.write_buffer_append_offset) { |
163 | 0 | connection.write_buffer_send_offset = connection.write_buffer_append_offset; |
164 | 0 | } |
165 | 303 | mhd_stream_maximize_write_buffer(&connection); |
166 | 303 | break; |
167 | 0 | } |
168 | 380 | case 7: { |
169 | | // Safe guard for out of buffer space |
170 | 380 | connection.write_buffer_send_offset = connection.write_buffer_append_offset; |
171 | 380 | mhd_stream_release_write_buffer(&connection); |
172 | 380 | break; |
173 | 0 | } |
174 | 304 | case 8: { |
175 | | // Safe guard for out of buffer read |
176 | 304 | if (connection.read_buffer_offset > connection.read_buffer_size) { |
177 | 0 | connection.read_buffer_offset = connection.read_buffer_size; |
178 | 0 | } |
179 | 304 | mhd_stream_shrink_read_buffer(&connection); |
180 | 304 | break; |
181 | 0 | } |
182 | 349 | case 9: { |
183 | 349 | mhd_stream_switch_from_recv_to_send(&connection); |
184 | 349 | break; |
185 | 0 | } |
186 | 4.44k | } |
187 | 4.44k | } |
188 | | |
189 | | // Fuzz mhd_stream_process_post_finish |
190 | 2.45k | fuzz_mhd_stream_process_post_finish(connection, daemon, body); |
191 | | |
192 | | // Final cleanup to avoid memory leak |
193 | 2.45k | final_cleanup(connection, daemon); |
194 | | |
195 | 2.45k | return 0; |
196 | 2.45k | } |