Coverage Report

Created: 2025-08-26 06:29

/src/fuzz_mhd2.cpp
Line
Count
Source (jump to first uncovered line)
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 <algorithm>
21
#include <cstdarg>
22
#include <unistd.h>
23
#include <fcntl.h>
24
#include <sys/stat.h>
25
#include <sys/types.h>
26
27
#include "microhttpd2.h"
28
#include "fuzzer/FuzzedDataProvider.h"
29
30
7.12k
static inline enum MHD_Bool ToMhdBool(bool b) {
31
7.12k
  return b ? MHD_YES : MHD_NO;
32
7.12k
}
33
34
0
static void dummy_log(void*, enum MHD_StatusCode, const char*, va_list) {
35
  // Do nothing
36
0
}
37
38
static MHD_FN_PAR_NONNULL_(2) MHD_FN_PAR_NONNULL_(3)
39
const struct MHD_Action* req_cb(void* cls,
40
                                struct MHD_Request* request,
41
                                const struct MHD_String* path,
42
                                enum MHD_HTTP_Method method,
43
0
                                uint_fast64_t upload_size) {
44
45
0
  const std::string* body = static_cast<const std::string*>(cls);
46
0
  struct MHD_Response* r = MHD_response_from_buffer(
47
0
      MHD_HTTP_STATUS_OK, body->size(), body->c_str(),
48
0
      nullptr, nullptr);
49
0
  if (!r) {
50
0
    return nullptr;
51
0
  }
52
0
  return MHD_action_from_response(request, r);
53
0
}
54
55
903
void fuzz_digest_auth_calc(FuzzedDataProvider& fdp) {
56
903
  std::string realm  = fdp.ConsumeRandomLengthString(40);
57
903
  std::string user   = fdp.ConsumeRandomLengthString(24);
58
903
  std::string pass   = fdp.ConsumeRandomLengthString(24);
59
60
903
  enum MHD_DigestAuthAlgo alg =
61
903
      fdp.PickValueInArray<enum MHD_DigestAuthAlgo>({
62
903
        MHD_DIGEST_AUTH_ALGO_MD5,
63
903
        MHD_DIGEST_AUTH_ALGO_SHA256,
64
903
        MHD_DIGEST_AUTH_ALGO_SHA512_256
65
903
      });
66
903
  size_t hsz = MHD_digest_get_hash_size(alg);
67
903
  if (hsz > 0 && hsz <= 128) {
68
903
    std::vector<uint8_t> bin(hsz);
69
903
    std::vector<uint8_t> ud(hsz);
70
903
    std::vector<char>    hex(hsz * 2 + 1);
71
903
    (void) MHD_digest_auth_calc_userhash(alg, user.c_str(), realm.c_str(), bin.size(), bin.data());
72
903
    (void) MHD_digest_auth_calc_userhash_hex(alg, user.c_str(), realm.c_str(), hex.size(), hex.data());
73
903
    (void) MHD_digest_auth_calc_userdigest(alg, user.c_str(), realm.c_str(), pass.c_str(), ud.size(), ud.data());
74
903
  }
75
903
}
76
77
903
struct MHD_Response* fuzz_response_creation(FuzzedDataProvider& fdp) {
78
  // Create random body string for response
79
903
  const size_t body_len = fdp.ConsumeIntegralInRange<size_t>(0, std::min<size_t>(fdp.remaining_bytes(), 2048));
80
903
  std::string body = fdp.ConsumeBytesAsString(body_len);
81
82
903
  struct MHD_Response* r = nullptr;
83
903
  enum MHD_HTTP_StatusCode sc =
84
903
      fdp.PickValueInArray<enum MHD_HTTP_StatusCode>({
85
903
        MHD_HTTP_STATUS_OK, MHD_HTTP_STATUS_CREATED, MHD_HTTP_STATUS_NO_CONTENT,
86
903
        MHD_HTTP_STATUS_PARTIAL_CONTENT, MHD_HTTP_STATUS_BAD_REQUEST,
87
903
        MHD_HTTP_STATUS_UNAUTHORIZED, MHD_HTTP_STATUS_FORBIDDEN,
88
903
        MHD_HTTP_STATUS_NOT_FOUND, MHD_HTTP_STATUS_INTERNAL_SERVER_ERROR
89
903
      });
90
91
903
  if (fdp.ConsumeBool()) {
92
659
    r = MHD_response_from_buffer(sc, body.size(), body.data(), nullptr, nullptr);
93
659
  } else if (fdp.ConsumeBool()) {
94
24
    r = MHD_response_from_buffer_static(sc, body.size(), body.c_str());
95
220
  } else {
96
220
    r = MHD_response_from_empty(sc);
97
220
  }
98
99
903
  return r;
100
903
}
101
102
903
void fuzz_response_config(FuzzedDataProvider& fdp, struct MHD_Response* r) {
103
903
  std::string header1 = fdp.ConsumeRandomLengthString(24); if (header1.empty()) header1 = "H1";
104
903
  std::string header2 = fdp.ConsumeRandomLengthString(24); if (header2.empty()) header2 = "H2";
105
903
  std::string header3 = fdp.ConsumeRandomLengthString(24); if (header3.empty()) header3 = "H3";
106
903
  std::string val1 = fdp.ConsumeRandomLengthString(64); if (val1.empty()) val1 = "V1";
107
903
  std::string val2 = fdp.ConsumeRandomLengthString(64); if (val2.empty()) val2 = "V2";
108
903
  std::string val3 = fdp.ConsumeRandomLengthString(64); if (val3.empty()) val3 = "V3";
109
110
  // Set random headers
111
903
  MHD_response_add_header(r, "Content-Type", fdp.ConsumeBool() ? "text/plain" : "application/octet-stream");
112
903
  MHD_response_add_header(r, header1.c_str(), val1.c_str());
113
903
  MHD_response_add_header(r, header2.c_str(), val2.c_str());
114
903
  MHD_response_add_header(r, header3.c_str(), val3.c_str());
115
116
  // Set predefined headers
117
903
  MHD_response_add_predef_header(
118
903
    r, fdp.PickValueInArray<enum MHD_PredefinedHeader>({MHD_PREDEF_ACCEPT_CHARSET, MHD_PREDEF_ACCEPT_LANGUAGE}),
119
903
    fdp.ConsumeRandomLengthString(32).c_str()
120
903
  );
121
122
  // Set boolean configurations
123
903
  { auto opt = MHD_R_OPTION_REUSABLE( ToMhdBool(fdp.ConsumeBool())); (void) MHD_response_set_option(r, &opt); }
124
903
  { auto opt = MHD_R_OPTION_HEAD_ONLY_RESPONSE( ToMhdBool(fdp.ConsumeBool())); (void) MHD_response_set_option(r, &opt); }
125
903
  { auto opt = MHD_R_OPTION_CHUNKED_ENC( ToMhdBool(fdp.ConsumeBool())); (void) MHD_response_set_option(r, &opt); }
126
903
  { auto opt = MHD_R_OPTION_CONN_CLOSE( ToMhdBool(fdp.ConsumeBool())); (void) MHD_response_set_option(r, &opt); }
127
128
  // Create random data for response generation
129
903
  char tmpl[] = "/tmp/mhd2_fuzz_XXXXXX";
130
903
  int fd = mkstemp(tmpl);
131
903
  if (fd >= 0) {
132
903
    std::string bytes = fdp.ConsumeRandomLengthString(512);
133
903
    if (!bytes.empty()) {
134
216
      write(fd, bytes.data(), bytes.size());
135
216
    }
136
137
903
    off_t sz = lseek(fd, 0, SEEK_END);
138
903
    if (sz > 0) {
139
216
      uint_fast64_t off = fdp.ConsumeIntegralInRange<uint_fast64_t>(0, (uint_fast64_t)sz);
140
216
      uint_fast64_t len = fdp.ConsumeIntegralInRange<uint_fast64_t>(0, (uint_fast64_t)(sz - off));
141
216
      struct MHD_Response* rf = MHD_response_from_fd(
142
216
          fdp.PickValueInArray<enum MHD_HTTP_StatusCode>({
143
216
            MHD_HTTP_STATUS_OK, MHD_HTTP_STATUS_PARTIAL_CONTENT, MHD_HTTP_STATUS_NO_CONTENT
144
216
          }),
145
216
          fd, off, len);
146
216
      if (rf) MHD_response_destroy(rf); else close(fd);
147
687
    } else {
148
687
      close(fd);
149
687
    }
150
903
    unlink(tmpl);
151
903
  }
152
153
  // Pipe random response
154
903
  int pfd[2];
155
903
  if (0 == pipe(pfd)) {
156
903
    std::string pbytes = fdp.ConsumeRandomLengthString(256);
157
903
    if (!pbytes.empty()) (void)!write(pfd[1], pbytes.data(), pbytes.size());
158
903
    close(pfd[1]);
159
903
    struct MHD_Response* rp = MHD_response_from_pipe(
160
903
        fdp.PickValueInArray<enum MHD_HTTP_StatusCode>({
161
903
          MHD_HTTP_STATUS_OK, MHD_HTTP_STATUS_NO_CONTENT
162
903
        }),
163
903
        pfd[0]);
164
903
    if (rp) {
165
903
      MHD_response_destroy(rp);
166
903
    } else {
167
0
      close(pfd[0]);
168
0
    }
169
903
  }
170
903
}
171
172
3.51k
void daemon_configuration(FuzzedDataProvider& fdp, MHD_Daemon* d) {
173
3.51k
  using PollEnum = decltype(MHD_SPS_AUTO);
174
3.51k
  static constexpr PollEnum kPollChoices[] = {
175
3.51k
      MHD_SPS_AUTO, MHD_SPS_SELECT, MHD_SPS_POLL, MHD_SPS_EPOLL,
176
3.51k
  };
177
3.51k
  PollEnum ps = fdp.PickValueInArray(kPollChoices);
178
3.51k
  auto opt1 = MHD_D_OPTION_POLL_SYSCALL(ps);
179
3.51k
  MHD_daemon_set_option(d, &opt1);
180
181
3.51k
  using AddrEnum = decltype(MHD_AF_NONE);
182
3.51k
  static constexpr AddrEnum kAddrChoices[] = {
183
3.51k
      MHD_AF_NONE, MHD_AF_AUTO, MHD_AF_INET4, MHD_AF_INET6,
184
3.51k
  };
185
3.51k
  uint_least16_t port = fdp.ConsumeIntegralInRange<uint_least16_t>(0, 65535);
186
3.51k
  AddrEnum af = fdp.PickValueInArray(kAddrChoices);
187
3.51k
  auto opt2 = MHD_D_OPTION_BIND_PORT(af, port);
188
3.51k
  (void) MHD_daemon_set_option(d, &opt2);
189
190
3.51k
  auto opt3 = MHD_D_OPTION_DEFAULT_TIMEOUT(fdp.ConsumeIntegralInRange<unsigned>(0, 10));
191
3.51k
  MHD_daemon_set_option(d, &opt3);
192
193
3.51k
  auto opt4 = MHD_D_OPTION_CONN_MEMORY_LIMIT(fdp.ConsumeIntegralInRange<size_t>(0, 1<<16));
194
3.51k
  MHD_daemon_set_option(d, &opt4);
195
196
3.51k
  auto opt5 = MHD_D_OPTION_LOG_CALLBACK(&dummy_log, nullptr);
197
3.51k
  MHD_daemon_set_option(d, &opt5);
198
199
3.51k
  std::vector<uint8_t> ent = fdp.ConsumeBytes<uint8_t>(fdp.ConsumeIntegralInRange<size_t>(0, 32));
200
3.51k
  auto opt6 = MHD_D_OPTION_RANDOM_ENTROPY(ent.size(),
201
3.51k
      const_cast<void*>(static_cast<const void*>(ent.data()))
202
3.51k
  );
203
3.51k
  MHD_daemon_set_option(d, &opt6);
204
205
3.51k
  auto opt7 = MHD_D_OPTION_REREGISTER_ALL(ToMhdBool(fdp.ConsumeBool()));
206
3.51k
  MHD_daemon_set_option(d, &opt7);
207
3.51k
}
208
209
903
void fuzz_daemon_lifecycle(FuzzedDataProvider& fdp) {
210
  // Create random body string for response
211
903
  const size_t body_len = fdp.ConsumeIntegralInRange<size_t>(0, std::min<size_t>(fdp.remaining_bytes(), 2048));
212
903
  std::string body = fdp.ConsumeBytesAsString(body_len);
213
214
903
  struct MHD_Daemon* d = MHD_daemon_create(&req_cb, &body);
215
903
  if (!d) {
216
0
    return;
217
0
  }
218
219
  // Fuzz with random fixed queries
220
903
  union MHD_DaemonInfoFixedData dfix{};
221
903
  const int n = fdp.ConsumeIntegralInRange<int>(1, 6);
222
903
  using FixedEnum = decltype(MHD_DAEMON_INFO_FIXED_POLL_SYSCALL);
223
903
  static constexpr FixedEnum kFixedChoices[] = {
224
903
      MHD_DAEMON_INFO_FIXED_POLL_SYSCALL,
225
903
      MHD_DAEMON_INFO_FIXED_AGGREAGATE_FD,
226
903
      MHD_DAEMON_INFO_FIXED_NUM_WORK_THREADS,
227
903
      MHD_DAEMON_INFO_FIXED_BIND_PORT,
228
903
      MHD_DAEMON_INFO_FIXED_LISTEN_SOCKET,
229
903
      MHD_DAEMON_INFO_FIXED_TLS_BACKEND,
230
903
      MHD_DAEMON_INFO_FIXED_DEFAULT_TIMEOUT,
231
903
      MHD_DAEMON_INFO_FIXED_GLOBAL_CONNECTION_LIMIT,
232
903
      MHD_DAEMON_INFO_FIXED_PER_IP_LIMIT,
233
903
      MHD_DAEMON_INFO_FIXED_SUPPRESS_DATE_HEADER,
234
903
      MHD_DAEMON_INFO_FIXED_CONN_MEMORY_LIMIT,
235
903
      MHD_DAEMON_INFO_FIXED_FD_NUMBER_LIMIT,
236
903
  };
237
2.67k
  for (int i = 0; i < n; ++i) {
238
1.77k
    daemon_configuration(fdp, d);
239
1.77k
    FixedEnum which = fdp.PickValueInArray(kFixedChoices);
240
1.77k
    MHD_daemon_get_info_fixed(d, which, &dfix);
241
1.77k
  }
242
243
  // Fuzz with random dynamic queries
244
903
  union MHD_DaemonInfoDynamicData ddyn{};
245
903
  const int m = fdp.ConsumeIntegralInRange<int>(1, 6);
246
903
  using DynEnum = decltype(MHD_DAEMON_INFO_DYNAMIC_MAX_TIME_TO_WAIT);
247
903
  static constexpr DynEnum kDynChoices[] = {
248
903
      MHD_DAEMON_INFO_DYNAMIC_MAX_TIME_TO_WAIT,
249
903
      MHD_DAEMON_INFO_DYNAMIC_HAS_CONNECTIONS,
250
903
  };
251
2.64k
  for (int i = 0; i < m; ++i) {
252
1.73k
    daemon_configuration(fdp, d);
253
1.73k
    DynEnum which = fdp.PickValueInArray(kDynChoices);
254
1.73k
    MHD_daemon_get_info_dynamic(d, which, &ddyn);
255
1.73k
  }
256
257
903
  MHD_daemon_destroy(d);
258
903
}
259
260
903
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
261
903
  if (size <= 0) {
262
0
    return 0;
263
0
  }
264
903
  FuzzedDataProvider fdp(data, size);
265
266
  // Fuzz digest_auth_calc targets
267
903
  fuzz_digest_auth_calc(fdp);
268
269
  // Create responses with random choices
270
903
  struct MHD_Response* r = fuzz_response_creation(fdp);
271
272
  // Fuzz response configurations
273
903
  if (r) {
274
903
    fuzz_response_config(fdp, r);
275
903
    MHD_response_destroy(r);
276
903
  }
277
278
  // Fuzz daemon lifecycle
279
903
  fuzz_daemon_lifecycle(fdp);
280
281
903
  return 0;
282
903
}