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_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
7.88k
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
51
    // Initialise logging
52
7.88k
    setenv("KEA_LOGGER_DESTINATION", "/dev/null", 0);
53
7.88k
    setenv("KEA_LOCKFILE_DIR", "/tmp", 0);
54
7.88k
    setenv("KEA_PIDFILE_DIR", "/tmp", 0);
55
7.88k
    setenv("KEA_LFC_EXECUTABLE", "/bin/true", 0);
56
7.88k
    try {
57
7.88k
        isc::log::initLogger("fuzzer");
58
7.88k
        isc::process::Daemon::loggerInit("fuzzer", false);
59
7.88k
        isc::process::Daemon::setDefaultLoggerName("fuzzer");
60
7.88k
    } catch (const isc::Exception&) {
61
        // Early exit if logging initialisation failed
62
0
        return 0;
63
0
    }
64
65
    // Set variables
66
7.88k
    FuzzedDataProvider fdp(data, size);
67
7.88k
    Parser6Context ctx;
68
7.88k
    ControlledDhcpv6Srv srv(0, 0);
69
70
    // Get random flags
71
7.88k
    const bool checkOnly = fdp.ConsumeBool();
72
7.88k
    const bool extraChecks = fdp.ConsumeBool();
73
74
    // Get random type and command
75
7.88k
    Parser6Context::ParserType type = parserTypes[fdp.ConsumeIntegralInRange<int>(0, 13)];
76
7.88k
    std::string cmdStr = std::string(cmds[fdp.ConsumeIntegralInRange<int>(0, 19)]);
77
78
    // If no more remaining bytes, early exit
79
7.88k
    if (fdp.remaining_bytes() <= 0) {
80
28
      return 0;
81
28
    }
82
83
    // Provide two type of payload with different length to avoid
84
    // timeout from parsing trusted configuration file
85
7.86k
    std::string limit_payload = fdp.ConsumeRandomLengthString(25600);
86
7.86k
    std::string full_payload(reinterpret_cast<const char*>(data), size);
87
88
    // Perform an evaluation of the raw data.
89
7.86k
    try {
90
        // General parsing
91
7.86k
        ElementPtr rawTree = ctx.parseString(limit_payload, Parser6Context::PARSER_JSON);
92
93
        // Configure the server with valid tree
94
7.86k
        if (rawTree) {
95
1.40k
                configureDhcp6Server(srv, rawTree, false, true);
96
1.40k
                ControlledDhcpv6Srv::checkConfig(rawTree);
97
1.40k
                ControlledDhcpv6Srv::processConfig(rawTree);
98
1.40k
        }
99
7.86k
    } catch(const isc::Exception&){}
100
101
    // Generate random string
102
7.86k
    try {
103
        // General parsing
104
7.86k
        ElementPtr tree = ctx.parseString(limit_payload, type);
105
106
        // Configure the server with valid tree
107
7.86k
        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
7.86k
    } catch(const isc::Exception&){}
115
116
7.86k
    try {
117
        // File base parsing
118
7.86k
        std::string path = fuzz::writeTempFile(limit_payload, "json");
119
7.86k
        if (!path.empty()) {
120
7.86k
            ElementPtr fileTree = ctx.parseFile(path, Parser6Context::PARSER_DHCP6);
121
7.86k
            if (fileTree) {
122
0
                configureDhcp6Server(srv, fileTree, checkOnly, extraChecks);
123
0
                ControlledDhcpv6Srv::checkConfig(fileTree);
124
0
                ControlledDhcpv6Srv::processConfig(fileTree);
125
0
            }
126
7.86k
            unlink(path.c_str());
127
7.86k
        }
128
7.86k
    }
129
7.86k
    catch (const isc::Exception&){}
130
131
7.86k
    try{
132
        // Command parsing
133
7.86k
        ElementPtr args = fuzz::parseJSON(full_payload);
134
7.86k
        ElementPtr cmd = Element::create(cmdStr);
135
136
        // Configure root element
137
7.86k
        ElementPtr root = Element::createMap();
138
7.86k
        root->set("command", cmd);
139
7.86k
        root->set("arguments", args);
140
141
        // Transform to const element
142
7.86k
        ConstElementPtr cmd_const = cmd;
143
7.86k
        ConstElementPtr root_const = root;
144
145
7.86k
        parseCommand(cmd_const, root_const);
146
147
        // Response answer parsing
148
7.86k
        int status = 0;
149
7.86k
        parseAnswer(status, fuzz::parseJSON(full_payload));
150
7.86k
    } catch (const isc::Exception&) {
151
        // Known exceptions
152
7.86k
    }
153
154
    // Try fuzzing specific deeper fuzzers directly
155
156
    // Subnets6ListConfigParser
157
7.86k
    try {
158
7.86k
        ElementPtr elem = fuzz::parseJSON(full_payload);
159
7.86k
        SrvConfigPtr srv = SrvConfigPtr(new SrvConfig());
160
7.86k
        Subnets6ListConfigParser parser(fdp.ConsumeBool());
161
7.86k
        parser.parse(srv, elem, fdp.ConsumeBool());
162
7.86k
    } catch (const isc::Exception&) {
163
        // Known exceptions
164
7.85k
    }
165
166
    // RelayInfoParser
167
7.86k
    try {
168
7.86k
        Option::Universe opt = Option::V6;
169
7.86k
        ElementPtr elem = fuzz::parseJSON(full_payload);
170
7.86k
        Network::RelayInfoPtr info = Network::RelayInfoPtr(new Network::RelayInfo());
171
7.86k
        RelayInfoParser parser(opt);
172
7.86k
        parser.parse(info, elem);
173
7.86k
    } catch (const isc::Exception&) {
174
        // Known exceptions
175
7.86k
    }
176
177
    // Pool6Parser
178
7.86k
    try {
179
7.86k
        ElementPtr elem = fuzz::parseJSON(full_payload);
180
7.86k
        PoolStoragePtr pools(new PoolStorage());
181
7.86k
        Pool6Parser parser = Pool6Parser();
182
7.86k
        parser.parse(pools, elem, AF_INET6, fdp.ConsumeBool());
183
7.86k
    } catch (const isc::Exception&) {
184
        // Known exceptions
185
7.86k
    }
186
187
    // CompatibilityParser
188
7.86k
    try {
189
7.86k
        ElementPtr elem = fuzz::parseJSON(full_payload);
190
7.86k
        SrvConfig srv = SrvConfig();
191
7.86k
        CompatibilityParser parser = CompatibilityParser();
192
7.86k
        parser.parse(elem, srv);
193
7.86k
    } catch (const isc::Exception&) {
194
        // Known exceptions
195
699
    }
196
197
7.86k
    return 0;
198
7.86k
}
199