Coverage Report

Created: 2025-12-31 07:33

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