Coverage Report

Created: 2026-05-16 07:13

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