Coverage Report

Created: 2026-03-12 06:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/fuzz_response.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 <unistd.h>
19
#include <sys/types.h>
20
21
#include <vector>
22
#include <algorithm>
23
24
#include "mhd_helper.h"
25
26
static void request_ended_cb(void *cls,
27
                             const struct MHD_RequestEndedData *data,
28
0
                             void *request_app_context) {
29
  // Do nothing
30
0
}
31
32
2.25k
static enum MHD_HTTP_StatusCode pick_status_code(FuzzedDataProvider &fdp) {
33
  // Randomly pick a valid or invalid HTTP response status code
34
2.25k
  return fdp.ConsumeBool()
35
2.25k
      ? fdp.PickValueInArray({
36
2.03k
          MHD_HTTP_STATUS_OK, MHD_HTTP_STATUS_CREATED, MHD_HTTP_STATUS_NO_CONTENT,
37
2.03k
          MHD_HTTP_STATUS_PARTIAL_CONTENT, MHD_HTTP_STATUS_BAD_REQUEST,
38
2.03k
          MHD_HTTP_STATUS_UNAUTHORIZED, MHD_HTTP_STATUS_FORBIDDEN,
39
2.03k
          MHD_HTTP_STATUS_NOT_FOUND, MHD_HTTP_STATUS_INTERNAL_SERVER_ERROR })
40
2.25k
      : (enum MHD_HTTP_StatusCode)(fdp.ConsumeIntegralInRange<int>(0, 999));
41
2.25k
}
42
43
static MHD_Response* create_response(FuzzedDataProvider &fdp,
44
2.25k
                                     enum MHD_HTTP_StatusCode sc) {
45
2.25k
  struct MHD_Response* r = nullptr;
46
47
  // Generate random response body
48
2.25k
  const size_t body_len = fdp.ConsumeIntegralInRange<size_t>(
49
2.25k
      0, std::min<size_t>(fdp.remaining_bytes(), 8192));
50
2.25k
  std::string body = fdp.ConsumeBytesAsString(body_len);
51
52
53
  // Randomly select which constructing function to use for respone object creation
54
2.25k
  enum CtorKind {
55
2.25k
    EMPTY, BUF_STATIC, BUF_COPY, IOVEC, FROM_FD, FROM_PIPE
56
2.25k
  };
57
2.25k
  CtorKind ctor = fdp.PickValueInArray<CtorKind>(
58
2.25k
      {EMPTY, BUF_STATIC, BUF_COPY, IOVEC, FROM_FD, FROM_PIPE});
59
60
2.25k
  switch (ctor) {
61
0
    default:
62
1.21k
    case EMPTY: {
63
      // Create empty response
64
1.21k
      r = MHD_response_from_empty(sc);
65
1.21k
      break;
66
0
    }
67
169
    case BUF_STATIC: {
68
      // Create response with random body and static buffer status
69
169
      r = MHD_response_from_buffer_static(sc, body.size(), body.c_str());
70
169
      break;
71
0
    }
72
394
    case BUF_COPY: {
73
      // Create response with random body and copy buffer in
74
394
      r = MHD_response_from_buffer_copy(sc, body.size(), body.data());
75
394
      break;
76
0
    }
77
202
    case IOVEC: {
78
      // Create response from random IO vector
79
202
      unsigned cnt = fdp.ConsumeIntegralInRange<unsigned>(0, 6);
80
202
      std::vector<MHD_IoVec> iov(cnt);
81
202
      std::vector<std::string> chunks; chunks.reserve(cnt);
82
825
      for (unsigned i=0;i<cnt;i++) {
83
623
        chunks.push_back(fdp.ConsumeBytesAsString(
84
623
                           fdp.ConsumeIntegralInRange<size_t>(0, 1024)));
85
623
        iov[i].iov_base = chunks.back().data();
86
623
        iov[i].iov_len  = chunks.back().size();
87
623
      }
88
202
      r = MHD_response_from_iovec(sc, cnt ? cnt : 0, cnt ? iov.data() : nullptr, nullptr, nullptr);
89
202
      break;
90
0
    }
91
148
    case FROM_FD: {
92
      // Create response from file with random data
93
148
      char path[] = "/tmp/mhdrespXXXXXX";
94
148
      int fd = mkstemp(path);
95
148
      if (fd >= 0) {
96
148
        unlink(path);
97
148
        if (!body.empty()) (void) ::write(fd, body.data(), body.size());
98
148
        uint64_t sz = (uint64_t)body.size();
99
148
        uint64_t off = 0;
100
148
        if (fdp.ConsumeBool()) {
101
66
          off = std::min<uint64_t>(sz, fdp.ConsumeIntegral<uint64_t>() % (sz + 1));
102
66
        }
103
148
        uint64_t len = (sz > off) ? (fdp.ConsumeIntegral<uint64_t>() % (sz - off + 1)) : 0;
104
148
        r = MHD_response_from_fd(sc, fd, off, len);
105
148
        if (!r) {
106
8
          close(fd);
107
8
        }
108
148
      }
109
148
      break;
110
0
    }
111
128
    case FROM_PIPE: {
112
      // Create response by piping in random data
113
128
      int pfd[2];
114
128
      if (0 == pipe(pfd)) {
115
128
        std::string pbytes = fdp.ConsumeBytesAsString(
116
128
                               fdp.ConsumeIntegralInRange<size_t>(0, 2048));
117
128
        if (!pbytes.empty()) {
118
61
          ::write(pfd[1], pbytes.data(), pbytes.size());
119
61
        }
120
128
        close(pfd[1]);
121
128
        r = MHD_response_from_pipe(sc, pfd[0]);
122
128
        if (!r) {
123
7
          close(pfd[0]);
124
7
        }
125
128
      }
126
128
      break;
127
0
    }
128
2.25k
  }
129
130
2.25k
  return r;
131
2.25k
}
132
133
2.17k
static void add_headers(FuzzedDataProvider &fdp, MHD_Response *r) {
134
2.17k
  const char* ct = fdp.ConsumeBool() ? "text/plain" : "application/octet-stream";
135
2.17k
  MHD_response_add_header(r, "Content-Type", ct);
136
137
  // Add random standard headers
138
2.17k
  size_t num_headers = fdp.ConsumeIntegralInRange<size_t>(0, 10);
139
4.82k
  for (size_t i = 0; i < num_headers; i++) {
140
2.64k
    std::string name = safe_ascii(fdp.ConsumeRandomLengthString(20), false);
141
2.64k
    std::string val  = safe_ascii(fdp.ConsumeRandomLengthString(60), true);
142
2.64k
    MHD_response_add_header(r, name.c_str(), val.c_str());
143
2.64k
  }
144
145
  // Add random predefined header
146
2.17k
  enum MHD_PredefinedHeader which =
147
2.17k
    fdp.PickValueInArray<enum MHD_PredefinedHeader>(
148
2.17k
      { MHD_PREDEF_ACCEPT_CHARSET, MHD_PREDEF_ACCEPT_LANGUAGE });
149
2.17k
  std::string value = safe_ascii(fdp.ConsumeRandomLengthString(32));
150
2.17k
  MHD_response_add_predef_header(r, which, value.c_str());
151
2.17k
}
152
153
2.17k
static void randomise_response_options(FuzzedDataProvider &fdp, MHD_Response *r) {
154
2.17k
  if (fdp.ConsumeBool()) {
155
138
    auto o = MHD_R_OPTION_REUSABLE(ToMhdBool(fdp.ConsumeBool()));
156
138
    MHD_response_set_option(r, &o);
157
138
  }
158
2.17k
  if (fdp.ConsumeBool()) {
159
124
    auto o = MHD_R_OPTION_HEAD_ONLY_RESPONSE(ToMhdBool(fdp.ConsumeBool()));
160
124
    MHD_response_set_option(r, &o);
161
124
  }
162
2.17k
  if (fdp.ConsumeBool()) {
163
127
    auto o = MHD_R_OPTION_CHUNKED_ENC(ToMhdBool(fdp.ConsumeBool()));
164
127
    MHD_response_set_option(r, &o);
165
127
  }
166
2.17k
  if (fdp.ConsumeBool()) {
167
128
    auto o = MHD_R_OPTION_CONN_CLOSE(ToMhdBool(fdp.ConsumeBool()));
168
128
    MHD_response_set_option(r, &o);
169
128
  }
170
2.17k
  if (fdp.ConsumeBool()) {
171
126
    auto o = MHD_R_OPTION_HTTP_1_0_SERVER(ToMhdBool(fdp.ConsumeBool()));
172
126
    MHD_response_set_option(r, &o);
173
126
  }
174
2.17k
  if (fdp.ConsumeBool()) {
175
111
    auto o = MHD_R_OPTION_HTTP_1_0_COMPATIBLE_STRICT(ToMhdBool(fdp.ConsumeBool()));
176
111
    MHD_response_set_option(r, &o);
177
111
  }
178
2.17k
  if (fdp.ConsumeBool()) {
179
124
    auto o = MHD_R_OPTION_INSANITY_HEADER_CONTENT_LENGTH(ToMhdBool(fdp.ConsumeBool()));
180
124
    MHD_response_set_option(r, &o);
181
124
  }
182
2.17k
  if (fdp.ConsumeBool()) {
183
386
    auto o = MHD_R_OPTION_TERMINATION_CALLBACK(&request_ended_cb, nullptr);
184
386
    MHD_response_set_option(r, &o);
185
386
  }
186
2.17k
}
187
188
static void add_auth(FuzzedDataProvider &fdp, MHD_Response *r,
189
2.17k
                     enum MHD_HTTP_StatusCode sc) {
190
2.17k
  if (sc == MHD_HTTP_STATUS_UNAUTHORIZED) {
191
    // Randomly add different challenge under 401 status code
192
1.00k
    if (fdp.ConsumeBool()) {
193
      // Use digest challenge
194
384
      std::string realm = safe_ascii(fdp.ConsumeRandomLengthString(24));
195
384
      MHD_response_add_auth_basic_challenge(r, realm.c_str(), ToMhdBool(fdp.ConsumeBool()));
196
384
      if (fdp.ConsumeBool()) {
197
319
        std::string drealm = safe_ascii(fdp.ConsumeRandomLengthString(24));
198
319
        const char* opaque = fdp.ConsumeBool() ? "opaque" : nullptr;
199
319
        const char* domain = fdp.ConsumeBool() ? "/a /b"  : nullptr;
200
319
        enum MHD_DigestAuthMultiQOP mqop =
201
319
          fdp.ConsumeBool()? MHD_DIGEST_AUTH_MULT_QOP_AUTH : MHD_DIGEST_AUTH_MULT_QOP_AUTH_INT;
202
319
        enum MHD_DigestAuthMultiAlgo malgo =
203
319
          fdp.ConsumeBool()? MHD_DIGEST_AUTH_MULT_ALGO_ANY : MHD_DIGEST_AUTH_MULT_ALGO_MD5;
204
319
        MHD_response_add_auth_digest_challenge(
205
319
            r, drealm.c_str(), opaque, domain, ToMhdBool(fdp.ConsumeBool()),
206
319
            mqop, malgo, ToMhdBool(fdp.ConsumeBool()), ToMhdBool(fdp.ConsumeBool()));
207
319
      }
208
619
    } else {
209
      // Use basic challenge
210
619
      std::string realm = safe_ascii(fdp.ConsumeRandomLengthString(24));
211
619
      const char* opaque = fdp.ConsumeBool() ? "opaque" : nullptr;
212
619
      const char* domain = fdp.ConsumeBool() ? "/a /b"  : nullptr;
213
619
      enum MHD_DigestAuthMultiQOP mqop =
214
619
        fdp.ConsumeBool()? MHD_DIGEST_AUTH_MULT_QOP_AUTH : MHD_DIGEST_AUTH_MULT_QOP_AUTH_INT;
215
619
      enum MHD_DigestAuthMultiAlgo malgo =
216
619
        fdp.ConsumeBool()? MHD_DIGEST_AUTH_MULT_ALGO_ANY : MHD_DIGEST_AUTH_MULT_ALGO_MD5;
217
619
      MHD_response_add_auth_digest_challenge(
218
619
          r, realm.c_str(), opaque, domain, ToMhdBool(fdp.ConsumeBool()),
219
619
          mqop, malgo, ToMhdBool(fdp.ConsumeBool()),
220
619
          ToMhdBool(fdp.ConsumeBool()));
221
619
      if (fdp.ConsumeBool()) {
222
290
        std::string brealm = safe_ascii(fdp.ConsumeRandomLengthString(24));
223
290
        MHD_response_add_auth_basic_challenge(r, brealm.c_str(), ToMhdBool(fdp.ConsumeBool()));
224
290
      }
225
619
    }
226
1.17k
  } else {
227
    // For all other status code, randomly determine if challenges are added
228
229
    // Randomly choose if basic challenge is used or not
230
1.17k
    if (fdp.ConsumeBool()) {
231
188
      std::string realm = safe_ascii(fdp.ConsumeRandomLengthString(24));
232
188
      MHD_response_add_auth_basic_challenge(r, realm.c_str(), ToMhdBool(fdp.ConsumeBool()));
233
188
    }
234
235
    // Randomly choose if disgest challenge is used or not
236
1.17k
    if (fdp.ConsumeBool()) {
237
357
      std::string realm = safe_ascii(fdp.ConsumeRandomLengthString(24));
238
357
      const char* opaque = fdp.ConsumeBool() ? "opaque" : nullptr;
239
357
      const char* domain = fdp.ConsumeBool() ? "/a /b"  : nullptr;
240
357
      enum MHD_DigestAuthMultiQOP mqop =
241
357
        fdp.ConsumeBool()? MHD_DIGEST_AUTH_MULT_QOP_AUTH : MHD_DIGEST_AUTH_MULT_QOP_AUTH_INT;
242
357
      enum MHD_DigestAuthMultiAlgo malgo =
243
357
        fdp.ConsumeBool()? MHD_DIGEST_AUTH_MULT_ALGO_ANY : MHD_DIGEST_AUTH_MULT_ALGO_MD5;
244
357
      MHD_response_add_auth_digest_challenge(
245
357
          r, realm.c_str(), opaque, domain, ToMhdBool(fdp.ConsumeBool()),
246
357
          mqop, malgo, ToMhdBool(fdp.ConsumeBool()), ToMhdBool(fdp.ConsumeBool()));
247
357
    }
248
1.17k
  }
249
2.17k
}
250
251
2.25k
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
252
2.25k
  if (size == 0) {
253
0
    return 0;
254
0
  }
255
2.25k
  FuzzedDataProvider fdp(data, size);
256
257
  // Pick a random response status code
258
2.25k
  enum MHD_HTTP_StatusCode sc = pick_status_code(fdp);
259
260
  // Create a random response object
261
2.25k
  struct MHD_Response* r = create_response(fdp, sc);
262
2.25k
  if (!r) {
263
75
    return 0;
264
75
  }
265
266
  // Add random headers into the response object
267
2.17k
  add_headers(fdp, r);
268
269
  // Set random options for the response object
270
2.17k
  randomise_response_options(fdp, r);
271
272
  // Add authentication challenges to response
273
2.17k
  add_auth(fdp, r, sc);
274
275
  // Fuzz additional targets on response status
276
2.17k
  MHD_HTTP_status_code_to_string(sc);
277
2.17k
  MHD_status_code_to_string((enum MHD_StatusCode)sc);
278
279
  // Destory the response object
280
2.17k
  MHD_response_destroy(r);
281
2.17k
  return 0;
282
2.25k
}