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_pkt_process4.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 <dhcp/dhcp4.h>
11
#include <dhcp/pkt4.h>
12
#include <dhcp/libdhcp++.h>
13
#include <dhcp4/ctrl_dhcp4_srv.h>
14
#include <dhcpsrv/callout_handle_store.h>
15
#include <dhcpsrv/lease_mgr_factory.h>
16
#include <log/logger_support.h>
17
#include <util/filesystem.h>
18
19
#include <cstddef>
20
#include <cstdint>
21
#include <vector>
22
#include <list>
23
#include <memory>
24
#include <iostream>
25
#include <filesystem>
26
#include <fstream>
27
#include <string>
28
#include <cstdio>
29
30
#include "helper_func.h"
31
32
using namespace isc::dhcp;
33
using namespace isc::hooks;
34
using namespace isc::util;
35
36
extern "C" int lease4_offer(CalloutHandle& handle);
37
extern "C" int leases4_committed(CalloutHandle& handle);
38
39
namespace isc {
40
    namespace dhcp {
41
        class MyDhcpv4Srv : public ControlledDhcpv4Srv {
42
            public:
43
1.21k
                bool fuzz_accept(const Pkt4Ptr& pkt) {
44
1.21k
                    return accept(pkt);
45
1.21k
                }
46
47
1.21k
                static void fuzz_sanityCheck(const Pkt4Ptr& query) {
48
1.21k
                    ControlledDhcpv4Srv::sanityCheck(query);
49
1.21k
                }
50
51
6.36k
                void fuzz_classifyPacket(const Pkt4Ptr& pkt) {
52
6.36k
                    classifyPacket(pkt);
53
6.36k
                }
54
55
                ConstSubnet4Ptr fuzz_selectSubnet(const Pkt4Ptr& query,
56
                                                  bool& drop,
57
2.43k
                                                  bool allow_answer_park = true) {
58
2.43k
                    return selectSubnet(query, drop, allow_answer_park);
59
2.43k
                }
60
        };
61
    }
62
}
63
64
2.79k
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
65
2.79k
    if (size < 236) {
66
        // package size requires at least 236 bytes
67
15
        return 0;
68
15
    }
69
70
    // Disable validatePath checking to allow writing configuration file to /tmp
71
2.78k
    isc::util::file::PathChecker::enableEnforcement(false);
72
73
    // Initialise logging
74
2.78k
    setenv("KEA_LOGGER_DESTINATION", "/dev/null", 0);
75
2.78k
    setenv("KEA_LOCKFILE_DIR", "/tmp", 0);
76
2.78k
    setenv("KEA_PIDFILE_DIR", "/tmp", 0);
77
2.78k
    setenv("KEA_LFC_EXECUTABLE", "/bin/true", 0);
78
2.78k
    try {
79
2.78k
        isc::log::initLogger("fuzzer");
80
2.78k
        isc::process::Daemon::loggerInit("fuzzer", false);
81
2.78k
        isc::process::Daemon::setDefaultLoggerName("fuzzer");
82
2.78k
    } catch (...) {
83
        // Early exit if logging initialisation failed
84
0
        return 0;
85
0
    }
86
87
    // Creating temp config file
88
2.78k
    std::string path = fuzz::writeTempConfig(true);
89
2.78k
    if (path.empty()) {
90
        // Early exit if configuration file creation failed
91
0
        fuzz::deleteTempFile(path);
92
0
        return 0;
93
0
    }
94
95
    // Creating temp lease file
96
2.78k
    std::string lease_path = fuzz::writeTempLease(true);
97
98
2.78k
    Pkt4Ptr pkt;
99
2.78k
    std::unique_ptr<MyDhcpv4Srv> srv;
100
101
    // Package parsing
102
2.78k
    try {
103
        // Add fixed magic cookie and correct hardware address
104
2.78k
        std::vector<uint8_t> buf(data, data + size);
105
2.78k
        if (size >= 240) {
106
            // Max hardware address length is 20
107
2.75k
            buf[2] = 20;
108
109
            // Magic cookie fixed value 0x63825363
110
2.75k
            buf[236] = 0x63;
111
2.75k
            buf[237] = 0x82;
112
2.75k
            buf[238] = 0x53;
113
2.75k
            buf[239] = 0x63;
114
2.75k
        }
115
116
2.78k
        pkt = Pkt4Ptr(new Pkt4(buf.data(), buf.size()));
117
2.78k
        pkt->unpack();
118
2.78k
    } catch (...) {
119
        // Early exit if package parsing failed.
120
1.56k
        return 0;
121
1.56k
    }
122
123
    // Configure random value in packet
124
1.21k
    FuzzedDataProvider fdp(data, size);
125
1.21k
    uint8_t typeChoice = fdp.ConsumeIntegralInRange<uint8_t>(0, 8);
126
1.21k
    pkt->setType(static_cast<DHCPMessageType>(typeChoice));
127
128
    // Server initialisation
129
1.21k
    try {
130
1.21k
        srv.reset(new MyDhcpv4Srv());
131
1.21k
        srv->init(path);
132
1.21k
    } catch (...) {
133
        // Early exit if server initialisation failed.
134
1
        return 0;
135
1
    }
136
137
1.21k
    if (!srv) {
138
        // Early exit if server initialisation failed.
139
0
        return 0;
140
0
    }
141
142
    // Call classifyPacket for packet checking
143
1.21k
    try {
144
1.21k
        srv->fuzz_classifyPacket(pkt);
145
1.21k
    } catch (const isc::Exception& e) {
146
        // Slient exceptions
147
0
    } catch (const boost::exception& e) {
148
        // Slient exceptions
149
0
    }
150
151
    // Call accept for packet checking
152
1.21k
    try {
153
1.21k
        srv->fuzz_accept(pkt);
154
1.21k
    } catch (const isc::Exception& e) {
155
        // Slient exceptions
156
0
    } catch (const boost::exception& e) {
157
        // Slient exceptions
158
0
    }
159
160
    // Call sanityCheck for packet checking
161
1.21k
    try {
162
1.21k
        MyDhcpv4Srv::fuzz_sanityCheck(pkt);
163
1.21k
    } catch (const isc::Exception& e) {
164
        // Slient exceptions
165
5
    } catch (const boost::exception& e) {
166
        // Slient exceptions
167
0
    }
168
169
    // Prepare client context
170
1.21k
    AllocEngine::ClientContext4Ptr ctx(new AllocEngine::ClientContext4());
171
172
    // Call earlyGHRLookup
173
1.21k
    try {
174
1.21k
        srv->earlyGHRLookup(pkt, ctx);
175
1.21k
    } catch (const isc::Exception& e) {
176
        // Slient exceptions
177
0
    } catch (const boost::exception& e) {
178
        // Slient exceptions
179
0
    }
180
181
    // Call select subnet
182
1.21k
    try {
183
1.21k
        bool drop = false;
184
1.21k
        if (!ctx) {
185
0
            ctx.reset(new AllocEngine::ClientContext4());
186
0
        }
187
1.21k
        if (ctx) {
188
1.21k
            ctx->subnet_ = srv->fuzz_selectSubnet(pkt, drop, false);
189
1.21k
        }
190
1.21k
    } catch (const isc::Exception& e) {
191
        // Slient exceptions
192
0
    } catch (const boost::exception& e) {
193
        // Slient exceptions
194
0
    }
195
196
    // Call processLocalizedQuery4
197
1.21k
    try {
198
1.21k
        srv->processLocalizedQuery4(ctx, false);
199
1.21k
    } catch (const isc::Exception& e) {
200
        // Slient exceptions
201
0
    } catch (const boost::exception& e) {
202
        // Slient exceptions
203
0
    }
204
205
    // Prepare callout handle
206
1.21k
    CalloutHandlePtr handle = getCalloutHandle(pkt);
207
1.21k
    Pkt4Ptr rsp;
208
209
    // Call lease4_offer
210
1.21k
    try {
211
1.21k
        uint8_t mac_addr[6];
212
8.51k
        for (size_t i = 0; i < 6; ++i) {
213
7.30k
            mac_addr[i] = fdp.ConsumeIntegral<uint8_t>();
214
7.30k
        }
215
1.21k
        HWAddr hw(mac_addr, sizeof(mac_addr), HTYPE_ETHER);
216
1.21k
        Lease4Collection leases = LeaseMgrFactory::instance().getLease4(hw);
217
1.21k
        handle->setArgument("leases4", leases);
218
1.21k
        if (!ctx) {
219
230
            ctx.reset(new AllocEngine::ClientContext4());
220
230
        }
221
1.21k
        if (ctx) {
222
1.21k
            handle->setArgument("offer_lifetime", ctx->offer_lft_);
223
1.21k
            handle->setArgument("old_lease", ctx->old_lease_);
224
1.21k
            handle->setArgument("host", ctx->currentHost());
225
1.21k
        }
226
1.21k
        handle->setArgument("query4", pkt);
227
1.21k
        handle->setArgument("response4", rsp);
228
229
1.21k
        lease4_offer(*handle);
230
1.21k
    } catch (const isc::Exception& e) {
231
        // Slient exceptions
232
0
    } catch (const boost::exception& e) {
233
        // Slient exceptions
234
0
    }
235
236
    // Clean up to avoid mem leak
237
1.21k
    if (handle) {
238
1.21k
        handle->deleteAllArguments();
239
1.21k
    }
240
241
    // Call lease4_committed
242
1.21k
    try {
243
1.21k
        uint8_t mac_addr[6];
244
8.51k
        for (size_t i = 0; i < 6; ++i) {
245
7.30k
            mac_addr[i] = fdp.ConsumeIntegral<uint8_t>();
246
7.30k
        }
247
1.21k
        HWAddr hw(mac_addr, sizeof(mac_addr), HTYPE_ETHER);
248
1.21k
        Lease4Collection leases = LeaseMgrFactory::instance().getLease4(hw);
249
1.21k
        handle->setArgument("leases4", leases);
250
1.21k
        handle->setArgument("query4", pkt);
251
1.21k
        handle->setArgument("response4", rsp);
252
253
1.21k
        leases4_committed(*handle);
254
1.21k
    } catch (const isc::Exception& e) {
255
        // Slient exceptions
256
0
    } catch (const boost::exception& e) {
257
        // Slient exceptions
258
0
    }
259
260
    // Clean up to avoid mem leak
261
1.21k
    if (handle) {
262
1.21k
        handle->deleteAllArguments();
263
1.21k
    }
264
265
1.21k
    srv.reset();
266
267
    // Remove temp files
268
1.21k
    fuzz::deleteTempFile(path);
269
1.21k
    fuzz::deleteTempFile(lease_path);
270
1.21k
    return 0;
271
1.21k
}