Coverage Report

Created: 2025-12-08 07:54

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/kea-fuzzer/fuzz_dns.cc
Line
Count
Source
1
// Copyright (C) 2025 Ada Logics 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
8
#include <config.h>
9
#include <fuzzer/FuzzedDataProvider.h>
10
11
#include <dns/exceptions.h>
12
#include <dns/message.h>
13
#include <dns/messagerenderer.h>
14
#include <dns/name.h>
15
#include <dns/opcode.h>
16
#include <dns/question.h>
17
#include <dns/rcode.h>
18
#include <dns/rdata.h>
19
#include <dns/rdataclass.h>
20
#include <dns/rrclass.h>
21
#include <dns/rrset.h>
22
#include <dns/rrttl.h>
23
#include <dns/rrtype.h>
24
#include <dns/tsig.h>
25
#include <dns/tsigkey.h>
26
#include <dns/tsigrecord.h>
27
#include <dns/master_lexer.h>
28
#include <dns/master_loader.h>
29
#include <util/buffer.h>
30
31
#include <cstddef>
32
#include <cstdint>
33
#include <memory>
34
#include <sstream>
35
#include <string>
36
#include <vector>
37
38
using namespace isc::dns;
39
using namespace isc::util;
40
41
3.09k
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
42
3.09k
    if (size < 2) {
43
1
        return 0;
44
1
    }
45
46
3.09k
    FuzzedDataProvider fdp(data, size);
47
    
48
    // Get a choice for which fuzzing path to take
49
3.09k
    uint8_t choice = fdp.ConsumeIntegral<uint8_t>();
50
    
51
    // Reserve some data for different operations
52
3.09k
    std::vector<uint8_t> wire_data = fdp.ConsumeBytes<uint8_t>(fdp.remaining_bytes() / 2);
53
3.09k
    std::string string_data = fdp.ConsumeRemainingBytesAsString();
54
    
55
    // Fuzz DNS Name parsing from string
56
3.09k
    if (choice % 8 == 0 && !string_data.empty()) {
57
297
        try {
58
297
            Name name(string_data);
59
            // Try various Name operations
60
297
            try {
61
297
                std::string text = name.toText();
62
297
                OutputBuffer buffer(0);
63
297
                name.toWire(buffer);
64
                
65
                // Try splitting at different positions
66
297
                if (name.getLabelCount() > 0) {
67
205
                    Name stripped = name.split(0);
68
205
                    Name reversed = name.reverse();
69
205
                }
70
                
71
                // Try comparison operations
72
297
                Name root = Name::ROOT_NAME();
73
297
                name.compare(root);
74
                
75
297
            } catch (const std::exception&) {
76
                // Ignore exceptions from operations
77
0
            }
78
297
        } catch (const std::exception&) {
79
            // Ignore exceptions from parsing
80
92
        }
81
297
    }
82
    
83
    // Fuzz DNS Name parsing from wire format
84
3.09k
    if (choice % 8 == 1 && !wire_data.empty()) {
85
43
        try {
86
43
            InputBuffer buffer(&wire_data[0], wire_data.size());
87
43
            Name name(buffer);
88
            
89
            // Try operations on the parsed name
90
43
            try {
91
43
                name.toText();
92
43
                name.getLabelCount();
93
43
                name.getLength();
94
43
            } catch (const std::exception&) {
95
                // Ignore exceptions
96
0
            }
97
43
        } catch (const std::exception&) {
98
            // Ignore exceptions from parsing
99
36
        }
100
43
    }
101
    
102
    // Fuzz DNS Message parsing from wire
103
3.09k
    if (choice % 8 == 2 && !wire_data.empty()) {
104
1.14k
        try {
105
1.14k
            InputBuffer buffer(&wire_data[0], wire_data.size());
106
1.14k
            Message message(Message::PARSE);
107
1.14k
            message.fromWire(buffer);
108
            
109
            // Try various Message operations
110
1.14k
            try {
111
1.14k
                message.getHeaderFlag(Message::HEADERFLAG_AA);
112
1.14k
                message.getRcode();
113
1.14k
                message.getQid();
114
1.14k
                message.getRRCount(Message::SECTION_ANSWER);
115
                
116
                // Try iterating through sections
117
1.14k
                for (int sec = Message::SECTION_QUESTION;
118
1.27k
                     sec <= Message::SECTION_ADDITIONAL;
119
1.14k
                     ++sec) {
120
124
                    Message::Section section = static_cast<Message::Section>(sec);
121
124
                    try {
122
124
                        auto it = message.beginSection(section);
123
124
                        auto it_end = message.endSection(section);
124
1.15k
                        while (it != it_end) {
125
1.03k
                            ++it;
126
1.03k
                        }
127
124
                    } catch (const std::exception&) {
128
                        // Ignore iteration exceptions
129
31
                    }
130
124
                }
131
                
132
                // Try rendering back to wire
133
1.14k
                MessageRenderer renderer;
134
1.14k
                try {
135
1.14k
                    message.toWire(renderer);
136
1.14k
                } catch (const std::exception&) {
137
                    // Ignore rendering exceptions
138
31
                }
139
                
140
1.14k
            } catch (const std::exception&) {
141
                // Ignore operation exceptions
142
0
            }
143
1.14k
        } catch (const std::exception&) {
144
            // Ignore parsing exceptions
145
1.11k
        }
146
1.14k
    }
147
    
148
    // Fuzz Question parsing
149
3.09k
    if (choice % 8 == 3 && !wire_data.empty()) {
150
69
        try {
151
69
            InputBuffer buffer(&wire_data[0], wire_data.size());
152
69
            Question question(buffer);
153
            
154
69
            try {
155
69
                question.toText();
156
69
                question.getName();
157
69
                question.getType();
158
69
                question.getClass();
159
                
160
69
                OutputBuffer out_buffer(0);
161
69
                question.toWire(out_buffer);
162
69
            } catch (const std::exception&) {
163
                // Ignore operation exceptions
164
0
            }
165
69
        } catch (const std::exception&) {
166
            // Ignore parsing exceptions
167
24
        }
168
69
    }
169
    
170
    // Fuzz RRset operations
171
3.09k
    if (choice % 8 == 4 && !string_data.empty() && !wire_data.empty()) {
172
75
        try {
173
75
            Name name(string_data);
174
75
            RRsetPtr rrset = RRsetPtr(new RRset(name, RRClass::IN(), 
175
75
                                                 RRType::A(), RRTTL(3600)));
176
            
177
            // Try parsing RDATA from wire
178
75
            try {
179
75
                InputBuffer buffer(&wire_data[0], wire_data.size());
180
75
                if (wire_data.size() >= 4) {
181
30
                    rdata::ConstRdataPtr rdata = 
182
30
                        rdata::createRdata(RRType::A(), RRClass::IN(), 
183
30
                                          buffer, wire_data.size());
184
30
                    rrset->addRdata(rdata);
185
30
                }
186
75
            } catch (const std::exception&) {
187
                // Ignore RDATA parsing exceptions
188
19
            }
189
            
190
            // Try RRset operations
191
75
            try {
192
38
                rrset->toText();
193
38
                rrset->getRdataCount();
194
                
195
38
                OutputBuffer out_buffer(0);
196
38
                rrset->toWire(out_buffer);
197
38
            } catch (const std::exception&) {
198
                // Ignore operation exceptions
199
27
            }
200
38
        } catch (const std::exception&) {
201
            // Ignore exceptions
202
37
        }
203
75
    }
204
    
205
    // Fuzz TSIG operations
206
3.09k
    if (choice % 8 == 5 && !string_data.empty() && wire_data.size() >= 16) {
207
821
        try {
208
            // Try creating a TSIG key
209
821
            TSIGKey key(string_data + ":secret");
210
            
211
            // Try creating TSIG RDATA and then a TSIG record
212
821
            try {
213
821
                InputBuffer buffer(&wire_data[0], wire_data.size());
214
                // Try to parse TSIG RDATA
215
821
                rdata::ConstRdataPtr rdata = 
216
821
                    rdata::createRdata(RRType::TSIG(), RRClass::ANY(), 
217
821
                                      buffer, wire_data.size());
218
821
                const rdata::any::TSIG& tsig_rdata = 
219
821
                    dynamic_cast<const rdata::any::TSIG&>(*rdata);
220
                
221
                // Create a TSIGRecord
222
821
                Name key_name(string_data);
223
821
                TSIGRecord tsig(key_name, tsig_rdata);
224
821
                tsig.toText();
225
                
226
821
                OutputBuffer out_buffer(0);
227
821
                tsig.toWire(out_buffer);
228
821
            } catch (const std::exception&) {
229
                // Ignore TSIG parsing exceptions
230
294
            }
231
            
232
            // Try TSIG context operations (sign operation is public)
233
821
            try {
234
427
                TSIGContext ctx(key);
235
                // Try signing some data
236
427
                if (!wire_data.empty()) {
237
427
                    ConstTSIGRecordPtr tsig_record = ctx.sign(0, &wire_data[0], wire_data.size());
238
427
                }
239
427
            } catch (const std::exception&) {
240
                // Ignore context exceptions
241
153
            }
242
427
        } catch (const std::exception&) {
243
            // Ignore key creation exceptions
244
394
        }
245
821
    }
246
    
247
    // Fuzz MasterLexer with string input
248
3.09k
    if (choice % 8 == 6 && !string_data.empty()) {
249
511
        try {
250
511
            std::istringstream iss(string_data);
251
511
            MasterLexer lexer;
252
511
            lexer.pushSource(iss);
253
            
254
            // Try tokenizing (loop until we hit EOF token)
255
8.74k
            for (int i = 0; i < 100; ++i) {
256
8.68k
                try {
257
8.68k
                    const MasterToken& token = lexer.getNextToken();
258
                    
259
                    // Stop if we hit EOF
260
8.68k
                    if (token.getType() == MasterToken::END_OF_FILE) {
261
455
                        break;
262
455
                    }
263
                    
264
                    // Access token properties based on type
265
8.22k
                    if (token.getType() == MasterToken::STRING ||
266
5.73k
                        token.getType() == MasterToken::QSTRING) {
267
5.73k
                        token.getString();
268
5.73k
                        token.getStringRegion();
269
5.73k
                    } else if (token.getType() == MasterToken::NUMBER) {
270
0
                        token.getNumber();
271
2.49k
                    } else if (token.getType() == MasterToken::ERROR) {
272
1.27k
                        token.getErrorCode();
273
1.27k
                        token.getErrorText();
274
1.27k
                    }
275
8.22k
                } catch (const std::exception&) {
276
0
                    break;
277
0
                }
278
8.68k
            }
279
511
        } catch (const std::exception&) {
280
            // Ignore lexer exceptions
281
0
        }
282
511
    }
283
    
284
    // Fuzz Message rendering operations
285
3.09k
    if (choice % 8 == 7 && !string_data.empty()) {
286
119
        try {
287
119
            Message message(Message::RENDER);
288
119
            message.setQid(fdp.ConsumeIntegral<uint16_t>());
289
119
            message.setOpcode(Opcode::QUERY());
290
119
            message.setRcode(Rcode::NOERROR());
291
            
292
            // Try setting various flags
293
119
            message.setHeaderFlag(Message::HEADERFLAG_AA, 
294
119
                                 fdp.ConsumeBool());
295
119
            message.setHeaderFlag(Message::HEADERFLAG_RD, 
296
119
                                 fdp.ConsumeBool());
297
119
            message.setHeaderFlag(Message::HEADERFLAG_RA, 
298
119
                                 fdp.ConsumeBool());
299
            
300
            // Try adding a question
301
119
            try {
302
119
                Name qname(string_data);
303
119
                QuestionPtr question(new Question(qname, RRClass::IN(), 
304
119
                                                   RRType::A()));
305
119
                message.addQuestion(question);
306
119
            } catch (const std::exception&) {
307
                // Ignore question addition exceptions
308
23
            }
309
            
310
            // Try rendering
311
119
            try {
312
119
                MessageRenderer renderer;
313
119
                message.toWire(renderer);
314
119
            } catch (const std::exception&) {
315
                // Ignore rendering exceptions
316
0
            }
317
119
        } catch (const std::exception&) {
318
            // Ignore message creation exceptions
319
0
        }
320
119
    }
321
    
322
3.09k
    return 0;
323
3.09k
}