Home | History | Annotate | Line # | Download | only in fuzz
      1 /*	$NetBSD: dns_qp.c,v 1.2 2025/01/26 16:25:20 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 <err.h>
     18 #include <stdbool.h>
     19 #include <stdint.h>
     20 
     21 #include <isc/random.h>
     22 #include <isc/refcount.h>
     23 #include <isc/rwlock.h>
     24 #include <isc/urcu.h>
     25 #include <isc/util.h>
     26 
     27 #include <dns/qp.h>
     28 #include <dns/types.h>
     29 
     30 #include "fuzz.h"
     31 #include "qp_p.h"
     32 
     33 #include <tests/qp.h>
     34 
     35 bool debug = false;
     36 
     37 #if 0
     38 #define TRACE(...) warnx(__VA_ARGS__)
     39 #else
     40 #define TRACE(...)
     41 #endif
     42 
     43 #if 0
     44 #define ASSERT(p)                                               \
     45 	do {                                                    \
     46 		warnx("%s:%d: %s (%s)", __func__, __LINE__, #p, \
     47 		      (p) ? "OK" : "FAIL");                     \
     48 		ok = ok && (p);                                 \
     49 	} while (0)
     50 #else
     51 #define ASSERT(p) assert(p)
     52 #endif
     53 
     54 static struct {
     55 	uint32_t refcount;
     56 	bool exists;
     57 	uint8_t len;
     58 	dns_qpkey_t key;
     59 	dns_qpkey_t ascii;
     60 } item[256 * 256 / 4];
     61 
     62 static void
     63 fuzz_attach(void *ctx, void *pval, uint32_t ival) {
     64 	assert(ctx == NULL);
     65 	assert(pval == &item[ival]);
     66 	item[ival].refcount++;
     67 }
     68 
     69 static void
     70 fuzz_detach(void *ctx, void *pval, uint32_t ival) {
     71 	assert(ctx == NULL);
     72 	assert(pval == &item[ival]);
     73 	item[ival].refcount--;
     74 }
     75 
     76 static size_t
     77 fuzz_makekey(dns_qpkey_t key, void *ctx, void *pval, uint32_t ival) {
     78 	assert(ctx == NULL);
     79 	assert(pval == &item[ival]);
     80 	memmove(key, item[ival].key, item[ival].len);
     81 	return item[ival].len;
     82 }
     83 
     84 static void
     85 fuzz_triename(void *ctx, char *buf, size_t size) {
     86 	assert(ctx == NULL);
     87 	strlcpy(buf, "fuzz", size);
     88 }
     89 
     90 const dns_qpmethods_t fuzz_methods = {
     91 	fuzz_attach,
     92 	fuzz_detach,
     93 	fuzz_makekey,
     94 	fuzz_triename,
     95 };
     96 
     97 static uint8_t
     98 random_byte(void) {
     99 	return isc_random_uniform(SHIFT_OFFSET - SHIFT_NOBYTE) + SHIFT_NOBYTE;
    100 }
    101 
    102 int
    103 LLVMFuzzerInitialize(int *argc, char ***argv) {
    104 	UNUSED(argc);
    105 	UNUSED(argv);
    106 
    107 	for (size_t i = 0; i < ARRAY_SIZE(item); i++) {
    108 		size_t len = isc_random_uniform(100) + 16;
    109 		item[i].len = len;
    110 		for (size_t off = 0; off < len; off++) {
    111 			item[i].key[off] = random_byte();
    112 		}
    113 		memmove(item[i].ascii, item[i].key, len);
    114 		qp_test_keytoascii(item[i].ascii, len);
    115 	}
    116 
    117 	return 0;
    118 }
    119 
    120 int
    121 LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
    122 	isc_result_t result;
    123 
    124 	TRACE("------------------------------------------------");
    125 
    126 	isc_mem_t *mctx = NULL;
    127 	isc_mem_create(&mctx);
    128 	isc_mem_setdestroycheck(mctx, true);
    129 
    130 	dns_qp_t *qp = NULL;
    131 	dns_qp_create(mctx, &fuzz_methods, NULL, &qp);
    132 
    133 	/* avoid overrun */
    134 	size = size & ~1;
    135 
    136 	size_t count = 0;
    137 
    138 	for (size_t in = 0; in < size; in += 2) {
    139 		size_t what = data[in] + data[in + 1] * 256;
    140 		size_t i = (what / 4) % (count * 2 + 2);
    141 		bool exists = item[i].exists;
    142 		uint32_t refcount = item[i].refcount;
    143 		bool ok = true;
    144 		if (what & 2) {
    145 			void *pval = NULL;
    146 			uint32_t ival = ~0U;
    147 			result = dns_qp_getkey(qp, item[i].key, item[i].len,
    148 					       &pval, &ival);
    149 			TRACE("count %zu get %s %zu >%s<", count,
    150 			      isc_result_toid(result), i, item[i].ascii);
    151 			if (result == ISC_R_SUCCESS) {
    152 				ASSERT(pval == &item[i]);
    153 				ASSERT(ival == i);
    154 				ASSERT(item[i].refcount == 1);
    155 				ASSERT(item[i].exists == true);
    156 			} else if (result == ISC_R_NOTFOUND) {
    157 				ASSERT(pval == NULL);
    158 				ASSERT(ival == ~0U);
    159 				ASSERT(item[i].refcount == 0);
    160 				ASSERT(item[i].exists == false);
    161 			} else {
    162 				UNREACHABLE();
    163 			}
    164 		} else if (what & 1) {
    165 			result = dns_qp_insert(qp, &item[i], i);
    166 			TRACE("count %zu ins %s %zu >%s<", count,
    167 			      isc_result_toid(result), i, item[i].ascii);
    168 			if (result == ISC_R_SUCCESS) {
    169 				item[i].exists = true;
    170 				ASSERT(exists == false);
    171 				ASSERT(refcount == 0);
    172 				ASSERT(item[i].refcount == 1);
    173 				count += 1;
    174 				ASSERT(qp->leaf_count == count);
    175 			} else if (result == ISC_R_EXISTS) {
    176 				ASSERT(exists == true);
    177 				ASSERT(refcount == 1);
    178 				ASSERT(item[i].refcount == 1);
    179 				ASSERT(qp->leaf_count == count);
    180 			} else {
    181 				UNREACHABLE();
    182 			}
    183 		} else {
    184 			result = dns_qp_deletekey(qp, item[i].key, item[i].len,
    185 						  NULL, NULL);
    186 			TRACE("count %zu del %s %zu >%s<", count,
    187 			      isc_result_toid(result), i, item[i].ascii);
    188 			if (result == ISC_R_SUCCESS) {
    189 				item[i].exists = false;
    190 				ASSERT(exists == true);
    191 				ASSERT(refcount == 1);
    192 				ASSERT(item[i].refcount == 0);
    193 				count -= 1;
    194 				ASSERT(qp->leaf_count == count);
    195 			} else if (result == ISC_R_NOTFOUND) {
    196 				ASSERT(exists == false);
    197 				ASSERT(refcount == 0);
    198 				ASSERT(item[i].refcount == 0);
    199 				ASSERT(qp->leaf_count == count);
    200 			} else {
    201 				UNREACHABLE();
    202 			}
    203 		}
    204 		if (!ok) {
    205 			qp_test_dumpqp(qp);
    206 			qp_test_dumptrie(qp);
    207 		}
    208 		assert(ok);
    209 	}
    210 
    211 	for (size_t i = 0; i < ARRAY_SIZE(item); i++) {
    212 		assert(item[i].exists == (item[i].refcount != 0));
    213 	}
    214 
    215 	dns_qp_destroy(&qp);
    216 	isc_mem_destroy(&mctx);
    217 	isc_mem_checkdestroyed(stderr);
    218 
    219 	for (size_t i = 0; i < ARRAY_SIZE(item); i++) {
    220 		item[i].exists = false;
    221 		assert(item[i].refcount == 0);
    222 	}
    223 
    224 	return 0;
    225 }
    226