]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
tools/testing/radix-tree: Add maple tree fuzzer
authorLiam R. Howlett <Liam.Howlett@oracle.com>
Wed, 23 Aug 2023 16:13:52 +0000 (12:13 -0400)
committerLiam R. Howlett <Liam.Howlett@Oracle.com>
Sat, 30 Nov 2024 14:47:57 +0000 (09:47 -0500)
This is the introduction of the maple tree fuzzer into the radix-tree
test suite, based heavily on the work of Vasily Gorbik sent in March
2022.

The tester uses the LLVM fuzzer to automate testing of the maple tree by
randomly inserting, storing, deleting, and resetting the tree.  Testing
has been expanded to test both allocation and basic trees.

After building the fuzz-maple target with clang, just run the resulting
executable.  The llvm libfuzzer supports minimizing the steps to
reproduce a crash from a crash file.

Using V=1 on the minimized crash will result in a testcase that can be
added to the lib/test_maple_tree.c test suite.  Using V=2 can help
figure out what is happening to cause the crash.

Cc: Vasily Gorbik <gor@linux.ibm.com>
Signed-off-by: Liam R. Howlett <Liam.Howlett@oracle.com>
tools/testing/radix-tree/.gitignore
tools/testing/radix-tree/Makefile
tools/testing/radix-tree/fuzz-maple.c [new file with mode: 0644]

index ce167a7619818e8d1693d54a5988a71613713b76..7e9488a1faa8acf9a7f6ee8b673dcfdab8152e2a 100644 (file)
@@ -9,4 +9,7 @@ multiorder
 radix-tree.c
 xarray
 maple
+fuzz-maple
+crash-*
+minimized-*
 ma_xa_benchmark
index 8b3591a51e1fa670bcb99b1602aebe0654da472e..d4421c513229a103e0c4b379bf4a32c34d8873e5 100644 (file)
@@ -21,6 +21,18 @@ xarray: $(CORE_OFILES) xarray.o
 
 maple: $(CORE_OFILES) maple.o
 
+ifeq "$(CC)" "clang"
+fuzz-maple: $(CORE_OFILES) fuzz-maple.c
+       $(CC) -fsanitize=fuzzer,address $(CFLAGS) $(CORE_OFILES) $(LDLIBS) -o fuzz-maple fuzz-maple.c
+
+coverage-maple: $(CORE_OFILES) fuzz-maple.c
+       $(CC) -fprofile-instr-generate -fcoverage-mapping $(CFLAGS) $(CORE_OFILES) $(LDLIBS) -o coverage-maple maple.c
+else
+.PHONY: fuzz-maple
+fuzz-maple:
+       @echo "fuzz-maple requires the use of the clang complier."
+endif
+
 multiorder: multiorder.o $(CORE_OFILES)
 
 clean:
diff --git a/tools/testing/radix-tree/fuzz-maple.c b/tools/testing/radix-tree/fuzz-maple.c
new file mode 100644 (file)
index 0000000..2406dd7
--- /dev/null
@@ -0,0 +1,165 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#define CONFIG_DEBUG_MAPLE_TREE
+#define CONFIG_MAPLE_SEARCH
+#include "test.h"
+
+#define dump_stack()   assert(0)
+
+#include "../shared/linux/maple_tree.h"
+#include "../../../include/linux/maple_tree.h"
+
+static int count;
+
+struct data {
+       const uint8_t *data;
+       size_t size;
+       size_t position;
+};
+
+static unsigned long get_ul(struct data *info)
+{
+       uint8_t tmp;
+       unsigned long ret = 0;
+       int i = 8;
+
+       while (i--) {
+               if (info->position >= info->size)
+                       info->position = 0;
+
+               tmp = info->data[info->position++];
+               ret += tmp << i;
+       }
+       return ret * count;
+}
+
+static uint8_t get_uint(struct data *info)
+{
+       if (info->position >= info->size)
+               info->position = 0;
+
+       return info->data[info->position++];
+}
+
+#define v(x)                           (xa_mk_value(x & LONG_MAX))
+//#define trace(...)           pr_info(__VA_ARGS__)
+#define trace(...)                     printv(1, __VA_ARGS__)
+#define TREE_FLAGS                     MT_FLAGS_ALLOC_RANGE
+
+DEFINE_MTREE(tree);
+
+static void maple_tree_tests(struct data *info)
+{
+       unsigned long l1, l2;
+       void *e, *rl;
+       int r;
+
+       while (count--) {
+               if (test_verbose) {
+                       pr_info("------------------------------------------------\n");
+                       mt_dump(&tree, mt_dump_hex);
+               }
+
+               l1 = get_ul(info);
+               l2 = l1 + get_ul(info);
+               e = v(l1);
+
+               trace("execute with %lx %lx (%p)\n", l1, l2, e);
+               switch (get_uint(info) % 8) {
+               case 3:
+                       trace("\tmtree_load(mt, 0x%lx);", l1);
+                       rl = mtree_load(&tree, l1);
+                       trace("// -> %p\n", rl);
+                       break;
+#if 1
+               case 1:
+                       trace("\tmtree_insert(mt, 0x%lx, (void *)%p, GFP_KERNEL);",
+                             l1, e);
+                       r = mtree_insert(&tree, l1, e, GFP_KERNEL);
+                       trace("// ret %d\n", r);
+                       break;
+               case 2:
+                       trace("\tmtree_insert_range(mt, 0x%lx, 0x%lx, (void *)%p, GFP_KERNEL);",
+                             l1, l2, e);
+                       r = mtree_insert_range(&tree, l1, l2, e, GFP_KERNEL);
+                       trace("// %d\n", r);
+                       break;
+#endif
+               default:
+               case 0:
+                       trace("\tmtree_store_range(mt, 0x%lx, 0x%lx, (void *)%p, GFP_KERNEL);",
+                             l1, l2, e);
+                       r = mtree_store_range(&tree, l1, l2, e, GFP_KERNEL);
+                       trace(" // %d\n", r);
+                       break;
+               case 4:
+                       trace("\tmtree_store(mt, 1x%lx, (void *)%p, GFP_KERNEL);",
+                             l1, e);
+                       r = mtree_store(&tree, l1, e, GFP_KERNEL);
+                       trace(" // %d\n", r);
+                       break;
+               case 5:
+                       trace("\tmtree_erase(mt, 0x%lx);", l1);
+                       rl = mtree_erase(&tree, l1);
+                       trace(" // %p\n", rl);
+                       break;
+#if 0
+               case 6:
+                       mt_validate(&tree);
+                       trace("mtree_destroy(mt);\n");
+                       mtree_destroy(&tree);
+                       trace("mt_init_flags(mt, 0x%x);\n", TREE_FLAGS);
+                       mt_init_flags(&tree, TREE_FLAGS);
+                       break;
+               case 7:
+                       mt_validate(&tree);
+                       trace("mtree_destroy(mt);\n");
+                       mtree_destroy(&tree);
+                       trace("mt_init_flags(mt, 0x%x);\n", 0);
+                       mt_init_flags(&tree, 0);
+                       break;
+#endif
+               }
+
+               mt_validate(&tree);
+       }
+
+       mt_validate(&tree);
+       trace("mtree_destroy(mt);\n");
+       mtree_destroy(&tree);
+       trace("mt_init_flags(mt, 0x%x);\n", TREE_FLAGS);
+       mt_init_flags(&tree, TREE_FLAGS);
+}
+
+extern int LLVMFuzzerInitialize(int* argc, char*** argv)
+{
+       char *v;
+
+       srand(time(NULL));
+       v = getenv("V");
+       if (v)
+               test_verbose = 1;
+
+       maple_tree_init();
+       mt_init_flags(&tree, TREE_FLAGS);
+       trace("mt_init_flags(mt, 0x%x);\n", TREE_FLAGS);
+       return 0;
+}
+
+extern int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+       struct data info;
+
+       if (!size)
+               return 0;
+
+       info.data = data;
+       info.size = size;
+       info.position = 0;
+       count = 125 * get_uint(&info);
+
+       //printk("count %lu and size %zu\n", count, size);
+       maple_tree_tests(&info);
+       rcu_barrier();
+       return 0;
+}