Coverage Report

Created: 2025-11-16 07:29

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