/src/bind9/fuzz/dns_rdata_fromwire_text.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (C) Internet Systems Consortium, Inc. ("ISC") |
3 | | * |
4 | | * SPDX-License-Identifier: MPL-2.0 |
5 | | * |
6 | | * This Source Code Form is subject to the terms of the Mozilla Public |
7 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
8 | | * file, you can obtain one at https://mozilla.org/MPL/2.0/. |
9 | | * |
10 | | * See the COPYRIGHT file distributed with this work for additional |
11 | | * information regarding copyright ownership. |
12 | | */ |
13 | | |
14 | | #include <assert.h> |
15 | | #include <stddef.h> |
16 | | #include <stdint.h> |
17 | | #include <string.h> |
18 | | |
19 | | #include <isc/buffer.h> |
20 | | #include <isc/lex.h> |
21 | | #include <isc/mem.h> |
22 | | #include <isc/result.h> |
23 | | #include <isc/util.h> |
24 | | |
25 | | #include <dns/callbacks.h> |
26 | | #include <dns/compress.h> |
27 | | #include <dns/master.h> |
28 | | #include <dns/rdata.h> |
29 | | #include <dns/rdatatype.h> |
30 | | |
31 | | #include "fuzz.h" |
32 | | |
33 | | bool debug = false; |
34 | | |
35 | | /* |
36 | | * Fuzz input to dns_rdata_fromwire(). Then convert the result |
37 | | * to text, back to wire format, to multiline text, and back to wire |
38 | | * format again, checking for consistency throughout the sequence. |
39 | | */ |
40 | | |
41 | | static isc_mem_t *mctx = NULL; |
42 | | static isc_lex_t *lex = NULL; |
43 | | |
44 | | int |
45 | 18 | LLVMFuzzerInitialize(int *argc ISC_ATTR_UNUSED, char ***argv ISC_ATTR_UNUSED) { |
46 | 18 | isc_lexspecials_t specials; |
47 | | |
48 | 18 | isc_mem_create(&mctx); |
49 | 18 | isc_lex_create(mctx, 64, &lex); |
50 | | |
51 | 18 | memset(specials, 0, sizeof(specials)); |
52 | 18 | specials[0] = 1; |
53 | 18 | specials['('] = 1; |
54 | 18 | specials[')'] = 1; |
55 | 18 | specials['"'] = 1; |
56 | 18 | isc_lex_setspecials(lex, specials); |
57 | 18 | isc_lex_setcomments(lex, ISC_LEXCOMMENT_DNSMASTERFILE); |
58 | | |
59 | 18 | return (0); |
60 | 18 | } |
61 | | |
62 | | static void |
63 | 0 | nullmsg(dns_rdatacallbacks_t *cb, const char *fmt, ...) { |
64 | 0 | va_list args; |
65 | |
|
66 | 0 | UNUSED(cb); |
67 | |
|
68 | 0 | if (debug) { |
69 | 0 | va_start(args, fmt); |
70 | 0 | vfprintf(stderr, fmt, args); |
71 | 0 | fprintf(stderr, "\n"); |
72 | 0 | va_end(args); |
73 | 0 | } |
74 | 0 | } |
75 | | |
76 | | int |
77 | 6.26k | LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { |
78 | 6.26k | char totext[64 * 1044 * 4]; |
79 | 6.26k | dns_compress_t cctx; |
80 | 6.26k | dns_rdatatype_t rdtype; |
81 | 6.26k | dns_rdataclass_t rdclass; |
82 | 6.26k | dns_rdatatype_t typelist[256] = { 1000 }; /* unknown */ |
83 | 6.26k | dns_rdataclass_t classlist[] = { dns_rdataclass_in, dns_rdataclass_hs, |
84 | 6.26k | dns_rdataclass_ch, dns_rdataclass_any, |
85 | 6.26k | 60 }; |
86 | 6.26k | dns_rdata_t rdata1 = DNS_RDATA_INIT, rdata2 = DNS_RDATA_INIT, |
87 | 6.26k | rdata3 = DNS_RDATA_INIT; |
88 | 6.26k | dns_rdatacallbacks_t callbacks; |
89 | 6.26k | isc_buffer_t source, target; |
90 | 6.26k | isc_result_t result; |
91 | 6.26k | unsigned char fromtext[1024]; |
92 | 6.26k | unsigned char fromwire[1024]; |
93 | 6.26k | unsigned char towire[1024]; |
94 | 6.26k | unsigned int classes = (sizeof(classlist) / sizeof(classlist[0])); |
95 | 6.26k | unsigned int types = 1, flags, t; |
96 | | |
97 | | /* |
98 | | * First 2 bytes are used to select type and class. |
99 | | * dns_rdata_fromwire() only accepts input up to 2^16-1 octets. |
100 | | */ |
101 | 6.26k | if (size < 2 || size > 0xffff + 2) { |
102 | 10 | return (0); |
103 | 10 | } |
104 | | |
105 | | /* |
106 | | * Append known types to list. |
107 | | */ |
108 | 409M | for (t = 1; t <= 0x10000; t++) { |
109 | 409M | char typebuf[256]; |
110 | 409M | if (dns_rdatatype_ismeta(t)) { |
111 | 806k | continue; |
112 | 806k | } |
113 | 408M | dns_rdatatype_format(t, typebuf, sizeof(typebuf)); |
114 | 408M | if (strncmp(typebuf, "TYPE", 4) != 0) { |
115 | | /* Assert when we need to grow typelist. */ |
116 | 506k | assert(types < sizeof(typelist) / sizeof(typelist[0])); |
117 | 506k | typelist[types++] = t; |
118 | 506k | } |
119 | 408M | } |
120 | | |
121 | | /* |
122 | | * Random type and class from a limited set. |
123 | | */ |
124 | 6.25k | rdtype = typelist[(*data++) % types]; |
125 | 6.25k | size--; |
126 | 6.25k | rdclass = classlist[(*data++) % classes]; |
127 | 6.25k | size--; |
128 | | |
129 | 6.25k | if (debug) { |
130 | 0 | fprintf(stderr, "type=%u, class=%u\n", rdtype, rdclass); |
131 | 0 | } |
132 | | |
133 | 6.25k | dns_rdatacallbacks_init(&callbacks); |
134 | 6.25k | callbacks.warn = callbacks.error = nullmsg; |
135 | | |
136 | 6.25k | isc_buffer_constinit(&source, data, size); |
137 | 6.25k | isc_buffer_add(&source, size); |
138 | 6.25k | isc_buffer_setactive(&source, size); |
139 | | |
140 | 6.25k | isc_buffer_init(&target, fromwire, sizeof(fromwire)); |
141 | | |
142 | | /* |
143 | | * Reject invalid rdata. (Disallow decompression as we are |
144 | | * reading a packet) |
145 | | */ |
146 | 6.25k | CHECK(dns_rdata_fromwire(&rdata1, rdclass, rdtype, &source, |
147 | 6.25k | DNS_DECOMPRESS_NEVER, &target)); |
148 | 3.54k | assert(rdata1.length == size); |
149 | | |
150 | | /* |
151 | | * Convert to text from wire. |
152 | | */ |
153 | 3.54k | isc_buffer_init(&target, totext, sizeof(totext) - 1); |
154 | 3.54k | result = dns_rdata_totext(&rdata1, NULL, &target); |
155 | 3.54k | assert(result == ISC_R_SUCCESS); |
156 | | |
157 | | /* |
158 | | * Make debugging easier by NUL terminating. |
159 | | */ |
160 | 3.54k | totext[isc_buffer_usedlength(&target)] = 0; |
161 | | |
162 | | /* |
163 | | * Convert to wire from text. |
164 | | */ |
165 | 3.54k | isc_buffer_constinit(&source, totext, isc_buffer_usedlength(&target)); |
166 | 3.54k | isc_buffer_add(&source, isc_buffer_usedlength(&target)); |
167 | 3.54k | CHECK(isc_lex_openbuffer(lex, &source)); |
168 | | |
169 | 3.54k | isc_buffer_init(&target, fromtext, sizeof(fromtext)); |
170 | 3.54k | result = dns_rdata_fromtext(&rdata2, rdclass, rdtype, lex, dns_rootname, |
171 | 3.54k | 0, mctx, &target, &callbacks); |
172 | 3.54k | if (debug && result != ISC_R_SUCCESS) { |
173 | 0 | fprintf(stderr, "'%s'\n", totext); |
174 | 0 | } |
175 | 3.54k | assert(result == ISC_R_SUCCESS); |
176 | 3.54k | assert(rdata2.length == size); |
177 | 3.54k | assert(!memcmp(rdata2.data, data, size)); |
178 | | |
179 | | /* |
180 | | * Convert to multi-line text from wire. |
181 | | */ |
182 | 3.54k | isc_buffer_init(&target, totext, sizeof(totext)); |
183 | 3.54k | flags = dns_master_styleflags(&dns_master_style_default); |
184 | 3.54k | result = dns_rdata_tofmttext(&rdata1, dns_rootname, flags, 80 - 32, 4, |
185 | 3.54k | "\n", &target); |
186 | 3.54k | assert(result == ISC_R_SUCCESS); |
187 | | |
188 | | /* |
189 | | * Convert to wire from text. |
190 | | */ |
191 | 3.54k | isc_buffer_constinit(&source, totext, isc_buffer_usedlength(&target)); |
192 | 3.54k | isc_buffer_add(&source, isc_buffer_usedlength(&target)); |
193 | 3.54k | CHECK(isc_lex_openbuffer(lex, &source)); |
194 | | |
195 | 3.54k | isc_buffer_init(&target, fromtext, sizeof(fromtext)); |
196 | 3.54k | result = dns_rdata_fromtext(&rdata3, rdclass, rdtype, lex, dns_rootname, |
197 | 3.54k | 0, mctx, &target, &callbacks); |
198 | 3.54k | assert(result == ISC_R_SUCCESS); |
199 | 3.54k | assert(rdata3.length == size); |
200 | 3.54k | assert(!memcmp(rdata3.data, data, size)); |
201 | | |
202 | | /* |
203 | | * Convert rdata back to wire. |
204 | | */ |
205 | 3.54k | dns_compress_init(&cctx, mctx, DNS_COMPRESS_DISABLED); |
206 | 3.54k | isc_buffer_init(&target, towire, sizeof(towire)); |
207 | 3.54k | result = dns_rdata_towire(&rdata1, &cctx, &target); |
208 | 3.54k | dns_compress_invalidate(&cctx); |
209 | 3.54k | assert(result == ISC_R_SUCCESS); |
210 | 3.54k | assert(target.used == size); |
211 | 3.54k | assert(!memcmp(target.base, data, size)); |
212 | | |
213 | 3.54k | return (0); |
214 | 3.54k | } |