Coverage Report

Created: 2025-12-31 07:33

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/kea-fuzzer/fuzz_dhcp_parser6.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/data.h>
11
#include <cc/command_interpreter.h>
12
13
#include <dhcp6/parser_context.h>
14
#include <dhcp6/json_config_parser.h>
15
#include <dhcp6/ctrl_dhcp6_srv.h>
16
#include <process/daemon.h>
17
#include <log/logger_support.h>
18
19
#include "helper_func.h"
20
21
#include <array>
22
#include <vector>
23
#include <string>
24
#include <cstdlib>
25
#include <unistd.h>
26
27
using namespace isc::config;
28
using namespace isc::data;
29
using namespace isc::dhcp;
30
31
using ControlledDhcpvSrv = ControlledDhcpv6Srv;
32
static constexpr Parser6Context::ParserType parserTypes[] = {
33
    Parser6Context::PARSER_JSON, Parser6Context::PARSER_INTERFACES,
34
    Parser6Context::PARSER_OPTION_DATA, Parser6Context::PARSER_OPTION_DEF,
35
    Parser6Context::PARSER_OPTION_DEFS, Parser6Context::PARSER_HOST_RESERVATION,
36
    Parser6Context::PARSER_HOOKS_LIBRARY, Parser6Context::PARSER_DHCP_DDNS,
37
    Parser6Context::PARSER_CONFIG_CONTROL, Parser6Context::PARSER_HOST_RESERVATION,
38
    Parser6Context::PARSER_DHCP6, Parser6Context::SUBPARSER_DHCP6,
39
    Parser6Context::PARSER_SUBNET6, Parser6Context::PARSER_POOL6,
40
};
41
42
static const char* cmds[] = {
43
    "config-get","config-hash-get","config-write","config-set","config-test",
44
    "config-reload","dhcp-disable","dhcp-enable","version-get","build-report",
45
    "leases-reclaim","server-tag-get","config-backend-pull","status-get",
46
    "statistic-set-max-sample-count-all","statistic-set-max-sample-age-all",
47
    "subnet6-select-test","subnet6o6-select-test","lfc-start","shutdown"
48
};
49
50
12.3k
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
51
    // Initialise logging
52
12.3k
    setenv("KEA_LOGGER_DESTINATION", "/dev/null", 0);
53
12.3k
    setenv("KEA_LOCKFILE_DIR", "/tmp", 0);
54
12.3k
    setenv("KEA_PIDFILE_DIR", "/tmp", 0);
55
12.3k
    setenv("KEA_LFC_EXECUTABLE", "/bin/true", 0);
56
12.3k
    try {
57
12.3k
        isc::log::initLogger("fuzzer");
58
12.3k
        isc::process::Daemon::loggerInit("fuzzer", false);
59
12.3k
        isc::process::Daemon::setDefaultLoggerName("fuzzer");
60
12.3k
    } catch (const isc::Exception&) {
61
        // Early exit if logging initialisation failed
62
0
        return 0;
63
0
    }
64
65
    // Set variables
66
12.3k
    FuzzedDataProvider fdp(data, size);
67
12.3k
    Parser6Context ctx;
68
12.3k
    ControlledDhcpv6Srv srv(0, 0);
69
70
    // Get random flags
71
12.3k
    const bool checkOnly = fdp.ConsumeBool();
72
12.3k
    const bool extraChecks = fdp.ConsumeBool();
73
74
    // Get random type and command
75
12.3k
    Parser6Context::ParserType type = parserTypes[fdp.ConsumeIntegralInRange<int>(0, 13)];
76
12.3k
    std::string cmdStr = std::string(cmds[fdp.ConsumeIntegralInRange<int>(0, 19)]);
77
78
    // If no more remaining bytes, early exit
79
12.3k
    if (fdp.remaining_bytes() <= 0) {
80
33
      return 0;
81
33
    }
82
83
    // Provide two type of payload with different length to avoid
84
    // timeout from parsing trusted configuration file
85
12.3k
    std::string limit_payload = fdp.ConsumeRandomLengthString(25600);
86
12.3k
    std::string full_payload(reinterpret_cast<const char*>(data), size);
87
88
    // Perform an evaluation of the raw data.
89
12.3k
    try {
90
        // General parsing
91
12.3k
        ElementPtr rawTree = ctx.parseString(limit_payload, Parser6Context::PARSER_JSON);
92
93
        // Configure the server with valid tree
94
12.3k
        if (rawTree) {
95
3.78k
                configureDhcp6Server(srv, rawTree, false, true);
96
3.78k
                ControlledDhcpv6Srv::checkConfig(rawTree);
97
3.78k
                ControlledDhcpv6Srv::processConfig(rawTree);
98
3.78k
        }
99
12.3k
    } catch(const isc::Exception&){}
100
101
    // Generate random string
102
12.3k
    try {
103
        // General parsing
104
12.3k
        ElementPtr tree = ctx.parseString(limit_payload, type);
105
106
        // Configure the server with valid tree
107
12.3k
        if (tree) {
108
0
            if (type == Parser6Context::PARSER_JSON || type == Parser6Context::PARSER_DHCP6){
109
0
                configureDhcp6Server(srv, tree, checkOnly, extraChecks);
110
0
                ControlledDhcpv6Srv::checkConfig(tree);
111
0
                ControlledDhcpv6Srv::processConfig(tree);
112
0
            }
113
0
        }
114
12.3k
    } catch(const isc::Exception&){}
115
116
12.3k
    try {
117
        // File base parsing
118
12.3k
        std::string path = fuzz::writeTempFile(limit_payload, "json");
119
12.3k
        if (!path.empty()) {
120
12.3k
            ElementPtr fileTree = ctx.parseFile(path, Parser6Context::PARSER_DHCP6);
121
12.3k
            if (fileTree) {
122
0
                configureDhcp6Server(srv, fileTree, checkOnly, extraChecks);
123
0
                ControlledDhcpv6Srv::checkConfig(fileTree);
124
0
                ControlledDhcpv6Srv::processConfig(fileTree);
125
0
            }
126
12.3k
            unlink(path.c_str());
127
12.3k
        }
128
12.3k
    }
129
12.3k
    catch (const isc::Exception&){}
130
131
12.3k
    try{
132
        // Command parsing
133
12.3k
        ElementPtr args = fuzz::parseJSON(full_payload);
134
12.3k
        ElementPtr cmd = Element::create(cmdStr);
135
136
        // Configure root element
137
12.3k
        ElementPtr root = Element::createMap();
138
12.3k
        root->set("command", cmd);
139
12.3k
        root->set("arguments", args);
140
141
        // Transform to const element
142
12.3k
        ConstElementPtr cmd_const = cmd;
143
12.3k
        ConstElementPtr root_const = root;
144
145
12.3k
        parseCommand(cmd_const, root_const);
146
147
        // Response answer parsing
148
12.3k
        int status = 0;
149
12.3k
        parseAnswer(status, fuzz::parseJSON(full_payload));
150
12.3k
    } catch (const isc::Exception&) {
151
        // Known exceptions
152
12.3k
    }
153
154
    // Try fuzzing specific deeper fuzzers directly
155
156
    // Subnets6ListConfigParser
157
12.3k
    try {
158
12.3k
        ElementPtr elem = fuzz::parseJSON(full_payload);
159
12.3k
        SrvConfigPtr srv = SrvConfigPtr(new SrvConfig());
160
12.3k
        Subnets6ListConfigParser parser(fdp.ConsumeBool());
161
12.3k
        parser.parse(srv, elem, fdp.ConsumeBool());
162
12.3k
    } catch (const isc::Exception&) {
163
        // Known exceptions
164
12.3k
    }
165
166
    // RelayInfoParser
167
12.3k
    try {
168
12.3k
        Option::Universe opt = Option::V6;
169
12.3k
        ElementPtr elem = fuzz::parseJSON(full_payload);
170
12.3k
        Network::RelayInfoPtr info = Network::RelayInfoPtr(new Network::RelayInfo());
171
12.3k
        RelayInfoParser parser(opt);
172
12.3k
        parser.parse(info, elem);
173
12.3k
    } catch (const isc::Exception&) {
174
        // Known exceptions
175
12.3k
    }
176
177
    // Pool6Parser
178
12.3k
    try {
179
12.3k
        ElementPtr elem = fuzz::parseJSON(full_payload);
180
12.3k
        PoolStoragePtr pools(new PoolStorage());
181
12.3k
        Pool6Parser parser = Pool6Parser();
182
12.3k
        parser.parse(pools, elem, AF_INET6, fdp.ConsumeBool());
183
12.3k
    } catch (const isc::Exception&) {
184
        // Known exceptions
185
12.2k
    }
186
187
    // CompatibilityParser
188
12.3k
    try {
189
12.3k
        ElementPtr elem = fuzz::parseJSON(full_payload);
190
12.3k
        SrvConfig srv = SrvConfig();
191
12.3k
        CompatibilityParser parser = CompatibilityParser();
192
12.3k
        parser.parse(elem, srv);
193
12.3k
    } catch (const isc::Exception&) {
194
        // Known exceptions
195
1.09k
    }
196
197
12.3k
    return 0;
198
12.3k
}
199