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