Skip to content

Commit 8294a96

Browse files
authored
Merge branch 'master' into arvo-contribution
2 parents 6218b69 + 1e77a78 commit 8294a96

File tree

4 files changed

+199
-8
lines changed

4 files changed

+199
-8
lines changed

projects/libmicrohttpd2/build.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ ASAN_OPTIONS=detect_leaks=0 make -j$(nproc)
2525
make install
2626

2727
# Compile fuzzer
28-
FUZZERS="fuzz_response fuzz_daemon fuzz_mhd2 fuzz_str fuzz_crypto_int fuzz_libinfo fuzz_connection"
28+
FUZZERS="fuzz_response fuzz_daemon fuzz_mhd2 fuzz_str fuzz_crypto_int fuzz_libinfo fuzz_connection fuzz_daemon_connection"
2929

3030
for fuzzer in $FUZZERS; do
3131
extra_src=""
@@ -35,7 +35,7 @@ for fuzzer in $FUZZERS; do
3535
;;
3636
esac
3737
case "$fuzzer" in
38-
fuzz_connection)
38+
fuzz_connection|fuzz_daemon_connection)
3939
extra_src="$SRC/connection_helper.cpp"
4040
;;
4141
esac

projects/libmicrohttpd2/connection_helper.cpp

Lines changed: 77 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
////////////////////////////////////////////////////////////////////////////////
1616
#include "connection_helper.h"
1717
#include <cstring>
18+
#include <unordered_set>
1819

1920
extern "C" {
2021
#include "mhd_action.h"
@@ -32,11 +33,30 @@ struct mhd_MemoryPool *g_pool = nullptr;
3233
const size_t g_pool_size = 14 * 1024;
3334
std::string g_mpart_boundary;
3435

36+
// Body status
37+
static std::unordered_set<const MHD_Connection*> g_post_parse_ready;
38+
3539
// Helper to clear memory pool
3640
void destroy_global_pool() {
3741
if (g_pool) { mhd_pool_destroy(g_pool); g_pool = nullptr; }
3842
}
3943

44+
45+
// Helper to set body parsing ready
46+
void mark_post_parse_ready(MHD_Connection& c) {
47+
g_post_parse_ready.insert(&c);
48+
}
49+
50+
// Helper to check parse body status
51+
bool is_post_parse_ready(const MHD_Connection& c) {
52+
return g_post_parse_ready.find(&c) != g_post_parse_ready.end();
53+
}
54+
55+
// Helper to clear parse body status
56+
void clear_post_parse_ready(const MHD_Connection& c) {
57+
g_post_parse_ready.erase(&c);
58+
}
59+
4060
// Helper to destroy error response
4161
static bool destroy_error_response(MHD_Connection c) {
4262
if (c.stage == mhd_HTTP_STAGE_START_REPLY) {
@@ -75,6 +95,23 @@ static std::string pick_http_version(FuzzedDataProvider& fdp) {
7595
}
7696
}
7797

98+
// Helper to check and expand buffer capcaity
99+
bool ensure_lbuf_capacity(MHD_Connection& c, size_t min_needed) {
100+
if (!c.daemon) {
101+
return false;
102+
}
103+
104+
if (c.rq.cntn.lbuf.data && c.rq.cntn.lbuf.size >= min_needed) {
105+
return true;
106+
}
107+
size_t have = c.rq.cntn.lbuf.size;
108+
size_t need = (min_needed > have) ? (min_needed - have) : 0;
109+
if (need) {
110+
mhd_daemon_extend_lbuf_up_to(c.daemon, need, &c.rq.cntn.lbuf);
111+
}
112+
return (c.rq.cntn.lbuf.data != nullptr) && (c.rq.cntn.lbuf.size >= min_needed);
113+
}
114+
78115
// Dummy upload actions
79116
const struct MHD_UploadAction kContinueAction = {
80117
mhd_UPLOAD_ACTION_CONTINUE, { nullptr }
@@ -101,6 +138,16 @@ dummy_done(struct MHD_Request*, void*, enum MHD_PostParseResult) {
101138
return &kContinueAction;
102139
}
103140

141+
// Dummy request callback function
142+
static const struct MHD_Action*
143+
dummy_request_cb(void* cls,
144+
struct MHD_Request* request,
145+
const struct MHD_String* path,
146+
enum MHD_HTTP_Method method,
147+
uint_fast64_t upload_size) {
148+
return nullptr;
149+
}
150+
104151
void init_daemon_connection(FuzzedDataProvider& fdp,
105152
MHD_Daemon& d, MHD_Connection& c) {
106153
// Basic initialisation
@@ -130,6 +177,10 @@ void init_daemon_connection(FuzzedDataProvider& fdp,
130177
c.suspended = false;
131178
c.connection_timeout_ms = fdp.ConsumeIntegralInRange<uint32_t>(0, 4096);
132179
c.last_activity = 0;
180+
181+
// Add dummy callback function
182+
d.req_cfg.cb = dummy_request_cb;
183+
d.req_cfg.cb_cls = nullptr;
133184
}
134185

135186
void init_connection_buffer(FuzzedDataProvider& fdp, MHD_Connection& c) {
@@ -268,7 +319,11 @@ void init_parsing_configuration(FuzzedDataProvider& fdp, MHD_Connection& c) {
268319
MHD_HTTP_PostEncoding enc;
269320

270321
// Configure connection encoding abd methods
271-
c.rq.app_act.head_act.act = mhd_ACTION_POST_PARSE;
322+
if (fdp.ConsumeBool()) {
323+
c.rq.app_act.head_act.act = mhd_ACTION_POST_PARSE;
324+
} else {
325+
c.rq.app_act.head_act.act = mhd_ACTION_NO_ACTION;
326+
}
272327
if (fdp.ConsumeBool()) {
273328
enc = MHD_HTTP_POST_ENCODING_TEXT_PLAIN;
274329
} else if (fdp.ConsumeBool()) {
@@ -366,6 +421,7 @@ void prepare_body_and_process(MHD_Connection& connection, std::string& body, siz
366421
// Fuzz mhd_stream_prepare_for_post_parse once and mhd_stream_post_parse
367422
mhd_stream_prepare_for_post_parse(&connection);
368423
mhd_stream_post_parse(&connection, &body_size, &body[0]);
424+
mark_post_parse_ready(connection);
369425
} else {
370426
// Try prepare the body by streaming connection buffer
371427
const bool want_chunked = (connection.rq.have_chunked_upload == MHD_YES);
@@ -422,15 +478,27 @@ void prepare_body_and_process(MHD_Connection& connection, std::string& body, siz
422478

423479
if (staged) {
424480
// Use stream body approach if success
425-
connection.stage = mhd_HTTP_STAGE_BODY_RECEIVING;
426-
connection.rq.have_chunked_upload = MHD_NO;
427-
connection.rq.cntn.cntn_size = (uint64_t) body_size;
428-
mhd_stream_prepare_for_post_parse(&connection);
429-
mhd_stream_process_request_body(&connection);
481+
const size_t min_needed = body_size + 1;
482+
if (ensure_lbuf_capacity(connection, min_needed)) {
483+
// Only post parse the request if buffer is enough
484+
connection.stage = mhd_HTTP_STAGE_BODY_RECEIVING;
485+
connection.rq.have_chunked_upload = MHD_NO;
486+
connection.rq.cntn.cntn_size = (uint64_t) body_size;
487+
mhd_stream_prepare_for_post_parse(&connection);
488+
mhd_stream_process_request_body(&connection);
489+
mark_post_parse_ready(connection);
490+
} else {
491+
// Fall back if not enough buffer
492+
size_t tmp = body_size;
493+
mhd_stream_prepare_for_post_parse(&connection);
494+
mhd_stream_post_parse(&connection, &tmp, body.data());
495+
mark_post_parse_ready(connection);
496+
}
430497
} else {
431498
// Use post prase approach if stream body failed
432499
mhd_stream_prepare_for_post_parse(&connection);
433500
mhd_stream_post_parse(&connection, &body_size, &body[0]);
501+
mark_post_parse_ready(connection);
434502
}
435503
}
436504
}
@@ -445,4 +513,7 @@ void final_cleanup(MHD_Connection& connection, MHD_Daemon& daemon) {
445513
if (connection.rq.cntn.lbuf.data != nullptr || connection.rq.cntn.lbuf.size != 0) {
446514
mhd_daemon_free_lbuf(&daemon, &connection.rq.cntn.lbuf);
447515
}
516+
517+
// Clean post parse body status
518+
clear_post_parse_ready(connection);
448519
}

projects/libmicrohttpd2/connection_helper.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,7 @@ void init_parsing_configuration(FuzzedDataProvider& fdp, MHD_Connection& c);
4545
void prepare_headers_and_parse(MHD_Connection& connection, size_t size);
4646
void prepare_body_and_process(MHD_Connection& connection, std::string& body, size_t body_size, bool use_stream_body);
4747
void final_cleanup(MHD_Connection& connection, MHD_Daemon& daemon);
48+
49+
void mark_post_parse_ready(MHD_Connection& connection);
50+
bool is_post_parse_ready(const MHD_Connection& connection);
51+
void clear_post_parse_ready(const MHD_Connection& connection);
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
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 <vector>
20+
#include <cstring>
21+
22+
#include "fuzzer/FuzzedDataProvider.h"
23+
#include "connection_helper.h"
24+
25+
extern "C" {
26+
#include "conn_tls_check.h"
27+
#include "mempool_funcs.h"
28+
#include "mhd_send.h"
29+
#include "stream_process_request.h"
30+
#include "stream_process_states.h"
31+
}
32+
33+
// Initialising the memory pool
34+
extern "C" int LLVMFuzzerInitialize() {
35+
g_pool = mhd_pool_create(g_pool_size, MHD_MEMPOOL_ZEROING_ON_RESET);
36+
atexit(destroy_global_pool);
37+
return 0;
38+
}
39+
40+
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
41+
if (size == 0 || g_pool == nullptr) {
42+
return 0;
43+
}
44+
45+
FuzzedDataProvider fdp(data, size);
46+
47+
// Reseting the memory pool for each iteartion
48+
mhd_pool_destroy(g_pool);
49+
g_pool = mhd_pool_create(g_pool_size, MHD_MEMPOOL_ZEROING_ON_RESET);
50+
51+
// Initialising the daemon, connection and other MHD components
52+
MHD_Daemon daemon;
53+
MHD_Connection connection;
54+
init_daemon_connection(fdp, daemon, connection);
55+
init_parsing_configuration(fdp, connection);
56+
init_connection_buffer(fdp, connection);
57+
prepare_headers_and_parse(connection, size);
58+
59+
// Randomly choose how many targets to fuzz
60+
std::vector<int> selectors;
61+
for (int i = 0; i < fdp.ConsumeIntegralInRange<int>(1, 8); i++) {
62+
selectors.push_back(fdp.ConsumeIntegralInRange<int>(0, 5));
63+
}
64+
65+
// Generate random flags
66+
bool use_stream_body = fdp.ConsumeBool();
67+
bool is_nodelay = fdp.ConsumeBool();
68+
bool is_cork = fdp.ConsumeBool();
69+
70+
// Use remaining bytes to generate random body for fuzzing
71+
std::string body = fdp.ConsumeRemainingBytesAsString();
72+
size_t body_size = body.size();
73+
if (body_size == 0) {
74+
return 0;
75+
}
76+
prepare_body_and_process(connection, body, body_size, use_stream_body);
77+
78+
for (int selector : selectors) {
79+
switch (selector) {
80+
case 0: {
81+
mhd_conn_event_loop_state_update(&connection);
82+
break;
83+
}
84+
case 1: {
85+
if (connection.rq.app_act.head_act.act == mhd_ACTION_NO_ACTION &&
86+
connection.daemon && connection.daemon->req_cfg.cb) {
87+
mhd_stream_call_app_request_cb(&connection);
88+
}
89+
break;
90+
}
91+
case 2: {
92+
if (connection.rq.app_act.head_act.act == mhd_ACTION_POST_PARSE &&
93+
connection.rq.app_act.head_act.data.post_parse.done_cb != nullptr &&
94+
is_post_parse_ready(connection)) {
95+
mhd_stream_process_req_recv_finished(&connection);
96+
}
97+
break;
98+
}
99+
case 3: {
100+
mhd_conn_tls_check(&connection);
101+
break;
102+
}
103+
case 4: {
104+
mhd_connection_set_nodelay_state(&connection, is_nodelay);
105+
break;
106+
}
107+
default: case 5: {
108+
mhd_connection_set_cork_state(&connection, is_cork);
109+
break;
110+
}
111+
}
112+
}
113+
114+
final_cleanup(connection, daemon);
115+
return 0;
116+
}

0 commit comments

Comments
 (0)