Coverage Report

Created: 2025-08-26 06:30

/src/fuzz_daemon.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 "mhd_helper.h"
17
18
static constexpr uint16_t kPort = 54321;
19
static uint16_t g_listen_port = kPort;
20
21
static struct MHD_Daemon* g_daemon = nullptr;
22
static std::once_flag g_start_once;
23
24
1
static void start_daemon_once() {
25
1
  if (g_daemon) {
26
0
    return;
27
0
  }
28
29
  // Add deterministic entropy
30
1
  unsigned char entropy[32];
31
33
  for (size_t i = 0; i < sizeof(entropy); ++i) {
32
32
    entropy[i] = (unsigned char)(0xA5 ^ (i * 17));
33
32
  }
34
35
  // Try start the daemon and map to a designated port
36
  // Retry to next port if mapping to port fails
37
1
  bool started = false;
38
2
  for (int i = 0; i < 8 && !started; ++i) {
39
1
    const uint16_t try_port = static_cast<uint16_t>(kPort + i);
40
41
1
    if (g_fdp->ConsumeBool()) {
42
0
      g_daemon = MHD_daemon_create(&req_cb, NULL);
43
1
    } else if (g_fdp->ConsumeBool()) {
44
0
      g_daemon = MHD_daemon_create(&req_cb_stream, NULL);
45
1
    } else {
46
1
      g_daemon = MHD_daemon_create(&req_cb_process, NULL);
47
1
    }
48
1
    if (!g_daemon) {
49
0
      continue;
50
0
    }
51
52
    // Configure daemon starting options
53
1
    auto sc = MHD_DAEMON_SET_OPTIONS(
54
1
                g_daemon,
55
1
                MHD_D_OPTION_BIND_PORT(MHD_AF_INET4, try_port),
56
1
                MHD_D_OPTION_RANDOM_ENTROPY(sizeof(entropy), entropy)
57
1
              );
58
59
    // Safeguard if daemon starting or mapping to port fails and try on next port
60
1
    if (sc != MHD_SC_OK) {
61
0
      MHD_daemon_destroy(g_daemon);
62
0
      g_daemon = nullptr;
63
0
      continue;
64
0
    }
65
1
    if (MHD_daemon_start(g_daemon) != MHD_SC_OK) {
66
0
      MHD_daemon_destroy(g_daemon);
67
0
      g_daemon = nullptr;
68
0
      continue;
69
0
    }
70
71
    // Store the final port used
72
1
    g_listen_port = try_port;
73
1
    started = true;
74
1
  }
75
1
}
76
77
412
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
78
412
  size_t reqs = 0;
79
80
  // Put FuzzedDataProvider to global
81
412
  {
82
412
    std::lock_guard<std::mutex> lk(g_fdp_mu);
83
412
    g_fdp = std::make_unique<FuzzedDataProvider>(data, size);
84
412
  }
85
86
  // Start daemon
87
412
  std::call_once(g_start_once, start_daemon_once);
88
412
  if (!g_daemon) {
89
0
    return 0;
90
0
  }
91
92
  // Generate random number to determine number of random request to be sent
93
412
  {
94
412
    std::lock_guard<std::mutex> lk(g_fdp_mu);
95
412
    if (g_fdp) {
96
412
      reqs = 1 + g_fdp->ConsumeIntegralInRange<size_t>(0, 7);
97
412
    }
98
412
  }
99
100
  // Send multiple random requests to daemon
101
1.30k
  for (size_t i = 0; i < reqs; ++i) {
102
1.19k
    std::string method = "GET", path, user, pass, body;
103
1.19k
    bool garble_auth = false;
104
105
    // Prepare HTTP request in locked session with random data
106
1.19k
    {
107
1.19k
      std::lock_guard<std::mutex> lk(g_fdp_mu);
108
1.19k
      if (g_fdp) {
109
1.19k
        static const char* kCommon[] = {"GET","POST","PUT","DELETE","HEAD","PATCH","OPTIONS"};
110
1.19k
        if (g_fdp->ConsumeBool()) {
111
648
          method = g_fdp->PickValueInArray(kCommon);
112
648
        } else {
113
551
          method = g_fdp->ConsumeRandomLengthString(8);
114
551
          if (method.empty()) {
115
204
            method = "GET";
116
204
          }
117
551
        }
118
119
1.19k
        path = g_fdp->ConsumeRandomLengthString(
120
1.19k
                 g_fdp->ConsumeIntegralInRange<size_t>(0, 64));
121
11.5k
        for (char &c : path) {
122
11.5k
          if (c == ' ') {
123
288
            c = '_';
124
288
          }
125
11.5k
        }
126
127
1.19k
        user = g_fdp->ConsumeRandomLengthString(16);
128
1.19k
        pass = g_fdp->ConsumeRandomLengthString(16);
129
1.19k
        garble_auth = g_fdp->ConsumeBool();
130
131
1.19k
        if (method == "POST" || g_fdp->ConsumeBool()) {
132
573
          body = g_fdp->ConsumeRandomLengthString(2048);
133
573
        }
134
1.19k
      }
135
1.19k
    }
136
137
    // Send request
138
1.19k
    send_http_request_blocking(g_listen_port, method, path, user, pass, body, garble_auth);
139
140
    // Early exit if failed to load more random data
141
1.19k
    {
142
1.19k
      std::lock_guard<std::mutex> lk(g_fdp_mu);
143
1.19k
      if (!g_fdp || g_fdp->remaining_bytes() < 8) {
144
302
        break;
145
302
      }
146
1.19k
    }
147
1.19k
  }
148
149
  // Free FDP
150
412
  {
151
412
    std::lock_guard<std::mutex> lk(g_fdp_mu);
152
412
    g_fdp.reset();
153
412
  }
154
155
412
  return 0;
156
412
}