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