Coverage Report

Created: 2025-11-16 07:29

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/kea-fuzzer/fuzz_dhcp_parser4.cc
Line
Count
Source
1
// Copyright (C) 2025 Ada Logcis Ltd.
2
//
3
// This Source Code Form is subject to the terms of the Mozilla Public
4
// License, v. 2.0. If a copy of the MPL was not distributed with this
5
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
////////////////////////////////////////////////////////////////////////////////
7
#include "config.h"
8
#include <fuzzer/FuzzedDataProvider.h>
9
10
#include <cc/command_interpreter.h>
11
#include <cc/data.h>
12
13
#include <dhcp4/ctrl_dhcp4_srv.h>
14
#include <dhcp4/json_config_parser.h>
15
#include <dhcp4/parser_context.h>
16
#include <log/logger_support.h>
17
#include <process/daemon.h>
18
19
#include "helper_func.h"
20
21
#include <array>
22
#include <cstdlib>
23
#include <string>
24
#include <unistd.h>
25
#include <vector>
26
27
using namespace isc::config;
28
using namespace isc::data;
29
using namespace isc::dhcp;
30
31
using ControlledDhcpvSrv = ControlledDhcpv4Srv;
32
static constexpr Parser4Context::ParserType parserTypes[] = {
33
    Parser4Context::PARSER_JSON,
34
    Parser4Context::PARSER_INTERFACES,
35
    Parser4Context::PARSER_OPTION_DATA,
36
    Parser4Context::PARSER_OPTION_DEF,
37
    Parser4Context::PARSER_OPTION_DEFS,
38
    Parser4Context::PARSER_HOST_RESERVATION,
39
    Parser4Context::PARSER_HOOKS_LIBRARY,
40
    Parser4Context::PARSER_DHCP_DDNS,
41
    Parser4Context::PARSER_CONFIG_CONTROL,
42
    Parser4Context::PARSER_HOST_RESERVATION,
43
    Parser4Context::PARSER_DHCP4,
44
    Parser4Context::SUBPARSER_DHCP4,
45
    Parser4Context::PARSER_SUBNET4,
46
    Parser4Context::PARSER_POOL4,
47
};
48
49
static const char *cmds[] = {"config-get",
50
                             "config-hash-get",
51
                             "config-write",
52
                             "config-set",
53
                             "config-test",
54
                             "config-reload",
55
                             "dhcp-disable",
56
                             "dhcp-enable",
57
                             "version-get",
58
                             "build-report",
59
                             "leases-reclaim",
60
                             "server-tag-get",
61
                             "config-backend-pull",
62
                             "status-get",
63
                             "statistic-set-max-sample-count-all",
64
                             "statistic-set-max-sample-age-all",
65
                             "subnet4-select-test",
66
                             "subnet4o6-select-test",
67
                             "lfc-start",
68
                             "shutdown"};
69
70
7.88k
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
71
  // Initialise logging
72
7.88k
  setenv("KEA_LOGGER_DESTINATION", "/dev/null", 0);
73
7.88k
  setenv("KEA_LOCKFILE_DIR", "/tmp", 0);
74
7.88k
  setenv("KEA_PIDFILE_DIR", "/tmp", 0);
75
7.88k
  setenv("KEA_LFC_EXECUTABLE", "/bin/true", 0);
76
7.88k
  try {
77
7.88k
    isc::log::initLogger("fuzzer");
78
7.88k
    isc::process::Daemon::loggerInit("fuzzer", false);
79
7.88k
    isc::process::Daemon::setDefaultLoggerName("fuzzer");
80
7.88k
  } catch (...) {
81
    // Early exit if logging initialisation failed
82
0
    return 0;
83
0
  }
84
85
  // Set variables
86
7.88k
  FuzzedDataProvider fdp(data, size);
87
7.88k
  Parser4Context ctx;
88
7.88k
  ControlledDhcpv4Srv srv(0, 0);
89
90
  // Get random flags
91
7.88k
  const bool checkOnly = fdp.ConsumeBool();
92
7.88k
  const bool extraChecks = fdp.ConsumeBool();
93
94
  // Get random type and command
95
7.88k
  Parser4Context::ParserType type =
96
7.88k
      parserTypes[fdp.ConsumeIntegralInRange<int>(0, 13)];
97
7.88k
  std::string cmdStr =
98
7.88k
      std::string(cmds[fdp.ConsumeIntegralInRange<int>(0, 19)]);
99
100
  // If no more remaining bytes, early exit
101
7.88k
  if (fdp.remaining_bytes() <= 0) {
102
28
    return 0;
103
28
  }
104
105
  // Provide two type of payload with different length to avoid
106
  // timeout from parsing trusted configuration file
107
7.86k
  std::string limit_payload = fdp.ConsumeRandomLengthString(25600);
108
7.86k
  std::string full_payload(reinterpret_cast<const char*>(data), size);
109
110
  // First target based on the raw payload entire. This makes seeding a lot
111
  // easier.
112
7.86k
  try {
113
7.86k
    ElementPtr rawTree = ctx.parseString(limit_payload, Parser4Context::PARSER_JSON);
114
115
    // Configure the server with valid tree
116
7.86k
    if (rawTree) {
117
1.40k
        configureDhcp4Server(srv, rawTree, false, extraChecks);
118
1.40k
        ControlledDhcpv4Srv::checkConfig(rawTree);
119
1.40k
        ControlledDhcpv4Srv::processConfig(rawTree);
120
1.40k
    }
121
7.86k
  } catch (const isc::Exception&) {
122
6.45k
  }
123
124
7.86k
  try {
125
7.86k
    ElementPtr tree = ctx.parseString(limit_payload, type);
126
127
    // Configure the server with valid tree
128
7.86k
    if (tree) {
129
0
      if (type == Parser4Context::PARSER_JSON ||
130
0
          type == Parser4Context::PARSER_DHCP4) {
131
0
        configureDhcp4Server(srv, tree, checkOnly, extraChecks);
132
0
        ControlledDhcpv4Srv::checkConfig(tree);
133
0
        ControlledDhcpv4Srv::processConfig(tree);
134
0
      }
135
0
    }
136
7.86k
  } catch (const isc::Exception&) {
137
7.86k
  }
138
139
  // File base parsing
140
7.86k
  try {
141
7.86k
    std::string path = fuzz::writeTempFile(limit_payload, "json");
142
7.86k
    if (!path.empty()) {
143
7.86k
      ElementPtr fileTree = ctx.parseFile(path, Parser4Context::PARSER_DHCP4);
144
7.86k
      if (fileTree) {
145
0
        configureDhcp4Server(srv, fileTree, checkOnly, extraChecks);
146
0
        ControlledDhcpv4Srv::checkConfig(fileTree);
147
0
        ControlledDhcpv4Srv::processConfig(fileTree);
148
0
      }
149
7.86k
      unlink(path.c_str());
150
7.86k
    }
151
7.86k
  } catch (const isc::Exception&) {
152
7.86k
  }
153
154
  // Command parsing
155
7.86k
  try {
156
7.86k
    ElementPtr args = fuzz::parseJSON(full_payload);
157
7.86k
    ElementPtr cmd = Element::create(cmdStr);
158
159
    // Configure root element
160
7.86k
    ElementPtr root = Element::createMap();
161
7.86k
    root->set("command", cmd);
162
7.86k
    root->set("arguments", args);
163
164
    // Transform to const element
165
7.86k
    ConstElementPtr cmd_const = cmd;
166
7.86k
    ConstElementPtr root_const = root;
167
168
7.86k
    parseCommand(cmd_const, root_const);
169
170
    // Response answer parsing
171
7.86k
    int status = 0;
172
7.86k
    parseAnswer(status, fuzz::parseJSON(full_payload));
173
7.86k
  } catch(const isc::Exception&) {}
174
175
    // Try fuzzing specific deeper fuzzers directly
176
177
    // Subnets6ListConfigParser
178
7.86k
    try {
179
7.86k
        ElementPtr elem = fuzz::parseJSON(full_payload);
180
7.86k
        SrvConfigPtr srv = SrvConfigPtr(new SrvConfig());
181
7.86k
        Subnets6ListConfigParser parser(fdp.ConsumeBool());
182
7.86k
        parser.parse(srv, elem, fdp.ConsumeBool());
183
7.86k
    } catch (const isc::Exception&) {
184
        // Known exceptions
185
7.85k
    }
186
187
    // RelayInfoParser
188
7.86k
    try {
189
7.86k
        Option::Universe opt = Option::V4;
190
7.86k
        ElementPtr elem = fuzz::parseJSON(full_payload);
191
7.86k
        Network::RelayInfoPtr info = Network::RelayInfoPtr(new Network::RelayInfo());
192
7.86k
        RelayInfoParser parser(opt);
193
7.86k
        parser.parse(info, elem);
194
7.86k
    } catch (const isc::Exception&) {
195
        // Known exceptions
196
7.86k
    }
197
198
    // PdPoolParser
199
7.86k
    try {
200
7.86k
        ElementPtr elem = fuzz::parseJSON(full_payload);
201
7.86k
        PoolStoragePtr pools(new PoolStorage());
202
7.86k
        PdPoolParser parser = PdPoolParser();
203
7.86k
        parser.parse(pools, elem, fdp.ConsumeBool());
204
7.86k
    } catch (const isc::Exception&) {
205
        // Known exceptions
206
7.86k
    }
207
208
    // CompatibilityParser
209
7.86k
    try {
210
7.86k
        ElementPtr elem = fuzz::parseJSON(full_payload);
211
7.86k
        SrvConfig srv = SrvConfig();
212
7.86k
        CompatibilityParser parser = CompatibilityParser();
213
7.86k
        parser.parse(elem, srv);
214
7.86k
    } catch (const isc::Exception&) {
215
        // Known exceptions
216
699
    }
217
218
7.86k
  return 0;
219
7.86k
}