Coverage Report

Created: 2025-10-10 06:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/fuzz_connection.cpp
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
}