diff --git a/fuzz/.gitignore b/fuzz/.gitignore index fe9ca76c5d..f9325d3e03 100644 --- a/fuzz/.gitignore +++ b/fuzz/.gitignore @@ -5,6 +5,8 @@ /dns_message_parse /dns_name_fromtext_target /dns_name_fromwire +/dns_qp +/dns_qpkey_name /dns_rdata_fromtext /dns_rdata_fromwire_text /isc_lex_getmastertoken diff --git a/fuzz/Makefile.am b/fuzz/Makefile.am index d265c6c05f..b7a7a9aafc 100644 --- a/fuzz/Makefile.am +++ b/fuzz/Makefile.am @@ -13,8 +13,8 @@ AM_LDFLAGS += \ LDADD += \ libfuzzmain.la \ - $(LIBISC_LIBS) \ - $(LIBDNS_LIBS) + $(LIBDNS_LIBS) \ + $(LIBISC_LIBS) check_LTLIBRARIES = libfuzzmain.la libfuzzmain_la_SOURCES = \ @@ -38,6 +38,8 @@ EXTRA_DIST = \ dns_message_parse.in \ dns_name_fromtext_target.in \ dns_name_fromwire.in \ + dns_qp.in \ + dns_qpkey_name.in \ dns_rdata_fromtext.in \ dns_rdata_fromwire_text.in \ isc_lex_getmastertoken.in \ @@ -48,6 +50,24 @@ dns_name_fromwire_SOURCES = \ old.c \ old.h +if HAVE_CMOCKA + +check_PROGRAMS += \ + dns_qp \ + dns_qpkey_name + +AM_CPPFLAGS += \ + -I$(top_srcdir)/lib/dns \ + -I$(top_srcdir)/lib/isc \ + -I$(top_srcdir)/tests/include + +# libisc needs to appear after libtest +LDADD += \ + $(top_builddir)/tests/libtest/libtest.la \ + $(LIBISC_LIBS) + +endif HAVE_CMOCKA + TESTS = $(check_PROGRAMS) if HAVE_FUZZ_LOG_COMPILER diff --git a/fuzz/dns_qp.c b/fuzz/dns_qp.c new file mode 100644 index 0000000000..64a88db8c1 --- /dev/null +++ b/fuzz/dns_qp.c @@ -0,0 +1,221 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include "fuzz.h" +#include "qp_p.h" + +#include + +bool debug = false; + +#if 0 +#define TRACE(...) warnx(__VA_ARGS__) +#else +#define TRACE(...) +#endif + +#if 0 +#define ASSERT(p) \ + do { \ + warnx("%s:%d: %s (%s)", __func__, __LINE__, #p, \ + (p) ? "OK" : "FAIL"); \ + ok = ok && (p); \ + } while (0) +#else +#define ASSERT(p) assert(p) +#endif + +static struct { + uint32_t refcount; + bool exists; + uint8_t len; + dns_qpkey_t key; + dns_qpkey_t ascii; +} item[256 * 256 / 4]; + +static void +fuzz_attach(void *ctx, void *pval, uint32_t ival) { + assert(ctx == NULL); + assert(pval == &item[ival]); + item[ival].refcount++; +} + +static void +fuzz_detach(void *ctx, void *pval, uint32_t ival) { + assert(ctx == NULL); + assert(pval == &item[ival]); + item[ival].refcount--; +} + +static size_t +fuzz_makekey(dns_qpkey_t key, void *ctx, void *pval, uint32_t ival) { + assert(ctx == NULL); + assert(pval == &item[ival]); + memmove(key, item[ival].key, item[ival].len); + return (item[ival].len); +} + +static void +fuzz_triename(void *ctx, char *buf, size_t size) { + assert(ctx == NULL); + strlcpy(buf, "fuzz", size); +} + +const struct dns_qpmethods fuzz_methods = { + fuzz_attach, + fuzz_detach, + fuzz_makekey, + fuzz_triename, +}; + +static uint8_t +random_byte(void) { + return (isc_random_uniform(SHIFT_OFFSET - SHIFT_NOBYTE) + SHIFT_NOBYTE); +} + +int +LLVMFuzzerInitialize(int *argc, char ***argv) { + UNUSED(argc); + UNUSED(argv); + + for (size_t i = 0; i < ARRAY_SIZE(item); i++) { + size_t len = isc_random_uniform(100) + 16; + item[i].len = len; + for (size_t off = 0; off < len; off++) { + item[i].key[off] = random_byte(); + } + memmove(item[i].ascii, item[i].key, len); + qp_test_keytoascii(item[i].ascii, len); + } + + return (0); +} + +int +LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + isc_result_t result; + + TRACE("------------------------------------------------"); + + isc_mem_t *mctx = NULL; + isc_mem_create(&mctx); + isc_mem_setdestroycheck(mctx, true); + + dns_qp_t *qp = NULL; + dns_qp_create(mctx, &fuzz_methods, NULL, &qp); + + /* avoid overrun */ + size = size & ~1; + + size_t count = 0; + + for (size_t in = 0; in < size; in += 2) { + size_t what = data[in] + data[in + 1] * 256; + size_t i = (what / 4) % (count * 2 + 2); + bool exists = item[i].exists; + uint32_t refcount = item[i].refcount; + bool ok = true; + if (what & 2) { + void *pval = NULL; + uint32_t ival = ~0U; + result = dns_qp_getkey(qp, item[i].key, item[i].len, + &pval, &ival); + TRACE("count %zu get %s %zu >%s<", count, + isc_result_toid(result), i, item[i].ascii); + if (result == ISC_R_SUCCESS) { + ASSERT(pval == &item[i]); + ASSERT(ival == i); + ASSERT(item[i].refcount == 1); + ASSERT(item[i].exists == true); + } else if (result == ISC_R_NOTFOUND) { + ASSERT(pval == NULL); + ASSERT(ival == ~0U); + ASSERT(item[i].refcount == 0); + ASSERT(item[i].exists == false); + } else { + UNREACHABLE(); + } + } else if (what & 1) { + result = dns_qp_insert(qp, &item[i], i); + TRACE("count %zu ins %s %zu >%s<", count, + isc_result_toid(result), i, item[i].ascii); + if (result == ISC_R_SUCCESS) { + item[i].exists = true; + ASSERT(exists == false); + ASSERT(refcount == 0); + ASSERT(item[i].refcount == 1); + count += 1; + ASSERT(qp->leaf_count == count); + } else if (result == ISC_R_EXISTS) { + ASSERT(exists == true); + ASSERT(refcount == 1); + ASSERT(item[i].refcount == 1); + ASSERT(qp->leaf_count == count); + } else { + UNREACHABLE(); + } + } else { + result = dns_qp_deletekey(qp, item[i].key, item[i].len); + TRACE("count %zu del %s %zu >%s<", count, + isc_result_toid(result), i, item[i].ascii); + if (result == ISC_R_SUCCESS) { + item[i].exists = false; + ASSERT(exists == true); + ASSERT(refcount == 1); + ASSERT(item[i].refcount == 0); + count -= 1; + ASSERT(qp->leaf_count == count); + } else if (result == ISC_R_NOTFOUND) { + ASSERT(exists == false); + ASSERT(refcount == 0); + ASSERT(item[i].refcount == 0); + ASSERT(qp->leaf_count == count); + } else { + UNREACHABLE(); + } + } + if (!ok) { + qp_test_dumpqp(qp); + qp_test_dumptrie(qp); + } + assert(ok); + } + + for (size_t i = 0; i < ARRAY_SIZE(item); i++) { + assert(item[i].exists == (item[i].refcount != 0)); + } + + dns_qp_destroy(&qp); + isc_mem_destroy(&mctx); + isc_mem_checkdestroyed(stderr); + + for (size_t i = 0; i < ARRAY_SIZE(item); i++) { + item[i].exists = false; + assert(item[i].refcount == 0); + } + + return (0); +} diff --git a/fuzz/dns_qp.in/bytecodes b/fuzz/dns_qp.in/bytecodes new file mode 100644 index 0000000000..a9304a2069 Binary files /dev/null and b/fuzz/dns_qp.in/bytecodes differ diff --git a/fuzz/dns_qpkey_name.c b/fuzz/dns_qpkey_name.c new file mode 100644 index 0000000000..7cf6f26457 --- /dev/null +++ b/fuzz/dns_qpkey_name.c @@ -0,0 +1,77 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include "fuzz.h" + +#include + +bool debug = false; + +int +LLVMFuzzerInitialize(int *argc, char ***argv) { + UNUSED(argc); + UNUSED(argv); + return (0); +} + +int +LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + dns_fixedname_t fixedin, fixedout, fixedcmp; + dns_name_t *namein, *nameout, *namecmp; + isc_buffer_t buf; + dns_qpkey_t key, cmp; + + namein = dns_fixedname_initname(&fixedin); + nameout = dns_fixedname_initname(&fixedout); + namecmp = dns_fixedname_initname(&fixedcmp); + + isc_buffer_constinit(&buf, data, size); + isc_buffer_add(&buf, size); + isc_buffer_setactive(&buf, size); + + CHECK(dns_name_fromwire(namein, &buf, DNS_DECOMPRESS_NEVER, NULL)); + + /* verify round-trip conversion of first name */ + size_t keylen = dns_qpkey_fromname(key, namein); + qp_test_keytoname(key, nameout); + + assert(dns_name_equal(namein, nameout)); + + /* is there a second name? */ + CHECK(dns_name_fromwire(namecmp, &buf, DNS_DECOMPRESS_NEVER, NULL)); + + size_t cmplen = dns_qpkey_fromname(cmp, namecmp); + size_t len = ISC_MIN(keylen, cmplen); + + int namerel = dns_name_compare(namein, namecmp); + int keyrel = memcmp(key, cmp, len + 1); + + assert((namerel < 0) == (keyrel < 0)); + assert((namerel == 0) == (keyrel == 0)); + assert((namerel > 0) == (keyrel > 0)); + + return (0); +} diff --git a/fuzz/dns_qpkey_name.in/021976d644b75207ffa3389e7ab30f5555f74dda b/fuzz/dns_qpkey_name.in/021976d644b75207ffa3389e7ab30f5555f74dda new file mode 100644 index 0000000000..defd440ccf Binary files /dev/null and b/fuzz/dns_qpkey_name.in/021976d644b75207ffa3389e7ab30f5555f74dda differ diff --git a/fuzz/dns_qpkey_name.in/13a8fb7162940fd0eeeb7597e47d6a95041f3396 b/fuzz/dns_qpkey_name.in/13a8fb7162940fd0eeeb7597e47d6a95041f3396 new file mode 100644 index 0000000000..8f2ab8a165 Binary files /dev/null and b/fuzz/dns_qpkey_name.in/13a8fb7162940fd0eeeb7597e47d6a95041f3396 differ diff --git a/fuzz/dns_qpkey_name.in/2149aa9e07dda9bbf502e088d8d0a38e8fb94f2e b/fuzz/dns_qpkey_name.in/2149aa9e07dda9bbf502e088d8d0a38e8fb94f2e new file mode 100644 index 0000000000..e7754cae5a --- /dev/null +++ b/fuzz/dns_qpkey_name.in/2149aa9e07dda9bbf502e088d8d0a38e8fb94f2e @@ -0,0 +1 @@ +À \ No newline at end of file diff --git a/fuzz/dns_qpkey_name.in/30a5288c64e9c856005fceb8b9155f8854d7368e b/fuzz/dns_qpkey_name.in/30a5288c64e9c856005fceb8b9155f8854d7368e new file mode 100644 index 0000000000..0767ba703e Binary files /dev/null and b/fuzz/dns_qpkey_name.in/30a5288c64e9c856005fceb8b9155f8854d7368e differ diff --git a/fuzz/dns_qpkey_name.in/404a051635872ce8de8846ad112a96cb72e13fe5 b/fuzz/dns_qpkey_name.in/404a051635872ce8de8846ad112a96cb72e13fe5 new file mode 100644 index 0000000000..8c513d56fd Binary files /dev/null and b/fuzz/dns_qpkey_name.in/404a051635872ce8de8846ad112a96cb72e13fe5 differ diff --git a/fuzz/dns_qpkey_name.in/562a1a46610aa93786f3e04cf2cd8044eff2a2b4 b/fuzz/dns_qpkey_name.in/562a1a46610aa93786f3e04cf2cd8044eff2a2b4 new file mode 100644 index 0000000000..6db54071c1 Binary files /dev/null and b/fuzz/dns_qpkey_name.in/562a1a46610aa93786f3e04cf2cd8044eff2a2b4 differ diff --git a/fuzz/dns_qpkey_name.in/62f2b90d4c70b3427355e6fed351855e89a1a97f b/fuzz/dns_qpkey_name.in/62f2b90d4c70b3427355e6fed351855e89a1a97f new file mode 100644 index 0000000000..0dd49e0c67 Binary files /dev/null and b/fuzz/dns_qpkey_name.in/62f2b90d4c70b3427355e6fed351855e89a1a97f differ diff --git a/fuzz/dns_qpkey_name.in/70ea4ca2c2211b2b2ee3400c09eac2302031475e b/fuzz/dns_qpkey_name.in/70ea4ca2c2211b2b2ee3400c09eac2302031475e new file mode 100644 index 0000000000..e44514783c Binary files /dev/null and b/fuzz/dns_qpkey_name.in/70ea4ca2c2211b2b2ee3400c09eac2302031475e differ diff --git a/fuzz/dns_qpkey_name.in/824cbc20333bbac1dafb93a3605cea366bb24b2d b/fuzz/dns_qpkey_name.in/824cbc20333bbac1dafb93a3605cea366bb24b2d new file mode 100644 index 0000000000..e6c7426bd4 Binary files /dev/null and b/fuzz/dns_qpkey_name.in/824cbc20333bbac1dafb93a3605cea366bb24b2d differ diff --git a/fuzz/dns_qpkey_name.in/9100388c033912183ac53ea771286103eda8c296 b/fuzz/dns_qpkey_name.in/9100388c033912183ac53ea771286103eda8c296 new file mode 100644 index 0000000000..e91fb99b87 Binary files /dev/null and b/fuzz/dns_qpkey_name.in/9100388c033912183ac53ea771286103eda8c296 differ diff --git a/fuzz/dns_qpkey_name.in/a218f85946b5cf6b60a00c1d4e69dd81ae63fe71 b/fuzz/dns_qpkey_name.in/a218f85946b5cf6b60a00c1d4e69dd81ae63fe71 new file mode 100644 index 0000000000..d0a1b967dc --- /dev/null +++ b/fuzz/dns_qpkey_name.in/a218f85946b5cf6b60a00c1d4e69dd81ae63fe71 @@ -0,0 +1 @@ +þ \ No newline at end of file diff --git a/fuzz/dns_qpkey_name.in/b06bf7d26ac11fde5ab24e77786bd28f89cf661d b/fuzz/dns_qpkey_name.in/b06bf7d26ac11fde5ab24e77786bd28f89cf661d new file mode 100644 index 0000000000..8eda0a26a1 Binary files /dev/null and b/fuzz/dns_qpkey_name.in/b06bf7d26ac11fde5ab24e77786bd28f89cf661d differ diff --git a/fuzz/dns_qpkey_name.in/c2580a278455cfa2234b87d19aab87082b1fa5f3 b/fuzz/dns_qpkey_name.in/c2580a278455cfa2234b87d19aab87082b1fa5f3 new file mode 100644 index 0000000000..e42bdf535e Binary files /dev/null and b/fuzz/dns_qpkey_name.in/c2580a278455cfa2234b87d19aab87082b1fa5f3 differ diff --git a/fuzz/dns_qpkey_name.in/d0f27368167150098f7e6afee11facff57d39c94 b/fuzz/dns_qpkey_name.in/d0f27368167150098f7e6afee11facff57d39c94 new file mode 100644 index 0000000000..09d2081ba3 Binary files /dev/null and b/fuzz/dns_qpkey_name.in/d0f27368167150098f7e6afee11facff57d39c94 differ diff --git a/fuzz/dns_qpkey_name.in/e09c3be9a268cf1f3a2118e9c07dac830904ea4f b/fuzz/dns_qpkey_name.in/e09c3be9a268cf1f3a2118e9c07dac830904ea4f new file mode 100644 index 0000000000..d6dd41072b Binary files /dev/null and b/fuzz/dns_qpkey_name.in/e09c3be9a268cf1f3a2118e9c07dac830904ea4f differ diff --git a/fuzz/dns_qpkey_name.in/f22d3ec38bf6cd6cc2776e376e3ec4ceca9fee39 b/fuzz/dns_qpkey_name.in/f22d3ec38bf6cd6cc2776e376e3ec4ceca9fee39 new file mode 100644 index 0000000000..224639a31d Binary files /dev/null and b/fuzz/dns_qpkey_name.in/f22d3ec38bf6cd6cc2776e376e3ec4ceca9fee39 differ