]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Fuzz testing the qp-trie
authorTony Finch <fanf@isc.org>
Sun, 12 Jun 2022 14:52:35 +0000 (15:52 +0100)
committerTony Finch <fanf@isc.org>
Mon, 27 Feb 2023 13:47:25 +0000 (13:47 +0000)
Ensure dns_qpkey_fromname() and dns_qpkey_toname() are inverses.

Excercise a single-threaded dns_qp_t with a fixed set of random keys
and a small chunk size. Use the table of names to ensure that the trie
is behaving as expected. This is (in effect) randomized testing like
the `qpmulti` unit test, but making use of coverage-guided fuzzing
and (in principle) test case minimization.

21 files changed:
fuzz/.gitignore
fuzz/Makefile.am
fuzz/dns_qp.c [new file with mode: 0644]
fuzz/dns_qp.in/bytecodes [new file with mode: 0644]
fuzz/dns_qpkey_name.c [new file with mode: 0644]
fuzz/dns_qpkey_name.in/021976d644b75207ffa3389e7ab30f5555f74dda [new file with mode: 0644]
fuzz/dns_qpkey_name.in/13a8fb7162940fd0eeeb7597e47d6a95041f3396 [new file with mode: 0644]
fuzz/dns_qpkey_name.in/2149aa9e07dda9bbf502e088d8d0a38e8fb94f2e [new file with mode: 0644]
fuzz/dns_qpkey_name.in/30a5288c64e9c856005fceb8b9155f8854d7368e [new file with mode: 0644]
fuzz/dns_qpkey_name.in/404a051635872ce8de8846ad112a96cb72e13fe5 [new file with mode: 0644]
fuzz/dns_qpkey_name.in/562a1a46610aa93786f3e04cf2cd8044eff2a2b4 [new file with mode: 0644]
fuzz/dns_qpkey_name.in/62f2b90d4c70b3427355e6fed351855e89a1a97f [new file with mode: 0644]
fuzz/dns_qpkey_name.in/70ea4ca2c2211b2b2ee3400c09eac2302031475e [new file with mode: 0644]
fuzz/dns_qpkey_name.in/824cbc20333bbac1dafb93a3605cea366bb24b2d [new file with mode: 0644]
fuzz/dns_qpkey_name.in/9100388c033912183ac53ea771286103eda8c296 [new file with mode: 0644]
fuzz/dns_qpkey_name.in/a218f85946b5cf6b60a00c1d4e69dd81ae63fe71 [new file with mode: 0644]
fuzz/dns_qpkey_name.in/b06bf7d26ac11fde5ab24e77786bd28f89cf661d [new file with mode: 0644]
fuzz/dns_qpkey_name.in/c2580a278455cfa2234b87d19aab87082b1fa5f3 [new file with mode: 0644]
fuzz/dns_qpkey_name.in/d0f27368167150098f7e6afee11facff57d39c94 [new file with mode: 0644]
fuzz/dns_qpkey_name.in/e09c3be9a268cf1f3a2118e9c07dac830904ea4f [new file with mode: 0644]
fuzz/dns_qpkey_name.in/f22d3ec38bf6cd6cc2776e376e3ec4ceca9fee39 [new file with mode: 0644]

index fe9ca76c5dee3a50b703cf3051104fe41c79b492..f9325d3e03325c08501752f8e6992b828dd8909b 100644 (file)
@@ -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
index d265c6c05f8ba04cc48aaaf25636bdab30c6c546..b7a7a9aafcecce10f32a6781d715cb7ad2a35142 100644 (file)
@@ -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 (file)
index 0000000..64a88db
--- /dev/null
@@ -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 <assert.h>
+#include <err.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <isc/random.h>
+#include <isc/refcount.h>
+#include <isc/rwlock.h>
+#include <isc/util.h>
+
+#include <dns/qp.h>
+#include <dns/types.h>
+
+#include "fuzz.h"
+#include "qp_p.h"
+
+#include <tests/qp.h>
+
+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 (file)
index 0000000..a9304a2
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 (file)
index 0000000..7cf6f26
--- /dev/null
@@ -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 <assert.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <isc/buffer.h>
+#include <isc/util.h>
+
+#include <dns/compress.h>
+#include <dns/fixedname.h>
+#include <dns/name.h>
+#include <dns/qp.h>
+
+#include "fuzz.h"
+
+#include <tests/qp.h>
+
+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 (file)
index 0000000..defd440
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 (file)
index 0000000..8f2ab8a
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 (file)
index 0000000..e7754ca
--- /dev/null
@@ -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 (file)
index 0000000..0767ba7
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 (file)
index 0000000..8c513d5
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 (file)
index 0000000..6db5407
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 (file)
index 0000000..0dd49e0
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 (file)
index 0000000..e445147
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 (file)
index 0000000..e6c7426
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 (file)
index 0000000..e91fb99
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 (file)
index 0000000..d0a1b96
--- /dev/null
@@ -0,0 +1 @@
+\ 1\ 2\ 1\ 1\ 1\ 1\ 1\ 2\ 1\ 1\ 1\ 1\ 1\ 1\ 1\ 1\ 1\ 1\ 1\ 1\ 1\ 1\ 1\ 1\ 1\ 1\ 1\ 1\ 1þ\ 2
\ 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 (file)
index 0000000..8eda0a2
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 (file)
index 0000000..e42bdf5
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 (file)
index 0000000..09d2081
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 (file)
index 0000000..d6dd410
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 (file)
index 0000000..224639a
Binary files /dev/null and b/fuzz/dns_qpkey_name.in/f22d3ec38bf6cd6cc2776e376e3ec4ceca9fee39 differ