Coverage Report

Created: 2026-02-14 07:22

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