[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20251204141250.21114-4-ethan.w.s.graham@gmail.com>
Date: Thu, 4 Dec 2025 15:12:42 +0100
From: Ethan Graham <ethan.w.s.graham@...il.com>
To: ethan.w.s.graham@...il.com,
glider@...gle.com
Cc: andreyknvl@...il.com,
andy@...nel.org,
andy.shevchenko@...il.com,
brauner@...nel.org,
brendan.higgins@...ux.dev,
davem@...emloft.net,
davidgow@...gle.com,
dhowells@...hat.com,
dvyukov@...gle.com,
elver@...gle.com,
herbert@...dor.apana.org.au,
ignat@...udflare.com,
jack@...e.cz,
jannh@...gle.com,
johannes@...solutions.net,
kasan-dev@...glegroups.com,
kees@...nel.org,
kunit-dev@...glegroups.com,
linux-crypto@...r.kernel.org,
linux-kernel@...r.kernel.org,
linux-mm@...ck.org,
lukas@...ner.de,
rmoar@...gle.com,
shuah@...nel.org,
sj@...nel.org,
tarasmadan@...gle.com
Subject: [PATCH 03/10] kfuzztest: introduce the FUZZ_TEST_SIMPLE macro
The serialization format required by a KFuzzTest target defined with the
FUZZ_TEST macro is overkill for simpler cases, in particular the very
common pattern of kernel interfaces taking a (data, datalen) pair.
Introduce the FUZZ_TEST_SIMPLE for defining simple targets that accept
a simpler binary interface without any required serialization. The aim
is to make simple targets compatible with a wide variety of userspace
fuzzing engines out of the box.
A FUZZ_TEST_SIMPLE target also defines an equivalent FUZZ_TEST macro in
its expansion maintaining compatibility with the default KFuzzTest
interface, using a shared `struct kfuzztest_simple_arg` as input type.
In essence, the following equivalence holds:
FUZZ_TEST_SIMPLE(test) === FUZZ_TEST(test, struct kfuzztest_simple_arg)
Constraints and annotation metadata for `struct kfuzztest_simple_arg` is
defined statically in the header file to avoid duplicate definitions in
the compiled vmlinux image.
Signed-off-by: Ethan Graham <ethan.w.s.graham@...il.com>
---
include/asm-generic/vmlinux.lds.h | 4 ++
include/linux/kfuzztest.h | 87 +++++++++++++++++++++++++++++++
2 files changed, 91 insertions(+)
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index 9afe569d013b..2736dd41fba0 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -974,6 +974,10 @@ defined(CONFIG_AUTOFDO_CLANG) || defined(CONFIG_PROPELLER_CLANG)
KEEP(*(.kfuzztest_target)); \
__kfuzztest_targets_end = .; \
. = ALIGN(PAGE_SIZE); \
+ __kfuzztest_simple_targets_start = .; \
+ KEEP(*(.kfuzztest_simple_target)); \
+ __kfuzztest_simple_targets_end = .; \
+ . = ALIGN(PAGE_SIZE); \
__kfuzztest_constraints_start = .; \
KEEP(*(.kfuzztest_constraint)); \
__kfuzztest_constraints_end = .; \
diff --git a/include/linux/kfuzztest.h b/include/linux/kfuzztest.h
index 1839fcfeabf5..284142fa4300 100644
--- a/include/linux/kfuzztest.h
+++ b/include/linux/kfuzztest.h
@@ -483,4 +483,91 @@ fail_early: \
} \
static void kfuzztest_logic_##test_name(test_arg_type *arg)
+struct kfuzztest_simple_target {
+ const char *name;
+ ssize_t (*write_input_cb)(struct file *filp, const char __user *buf, size_t len, loff_t *off);
+} __aligned(32);
+
+struct kfuzztest_simple_arg {
+ char *data;
+ size_t datalen;
+};
+
+/* Define constraint and annotation metadata for reused kfuzztest_simple_arg. */
+__KFUZZTEST_CONSTRAINT(kfuzztest_simple_arg, data, NULL, 0x0, EXPECT_NE);
+__KFUZZTEST_ANNOTATE(kfuzztest_simple_arg, data, NULL, ATTRIBUTE_ARRAY);
+__KFUZZTEST_ANNOTATE(kfuzztest_simple_arg, datalen, data, ATTRIBUTE_LEN);
+
+/**
+ * FUZZ_TEST_SIMPLE - defines a simple KFuzzTest target
+ *
+ * @test_name: the unique identifier for the fuzz test, which is used to name
+ * the debugfs entry.
+ *
+ * This macro function nearly identically to the standard FUZZ_TEST target, the
+ * key difference being that a simple fuzz target is constrained to inputs of
+ * the form `(char *data, size_t datalen)` - a common pattern in kernel APIs.
+ *
+ * The FUZZ_TEST_SIMPLE macro expands to define an equivalent FUZZ_TEST,
+ * effectively creating two debugfs input files for the fuzz target. In essence,
+ * on top of creating an input file under kfuzztest/@...t_name/input, a new
+ * simple input file is created under kfuzztest/@...t_name/input_simple. This
+ * debugfs file takes raw byte buffers as input and doesn't require any special
+ * serialization.
+ *
+ * User-provided Logic:
+ * The developer must provide the body of the fuzz test logic within the curly
+ * braces following the macro invocation. Within this scope, the framework
+ * provides the `data` and `datalen` variables, where `datalen == len(data)`.
+ *
+ * Example Usage:
+ *
+ * // 1. The kernel function that we wnat to fuzz.
+ * int process_data(const char *data, size_t datalen);
+ *
+ * // 2. Define a fuzz target using the FUZZ_TEST_SIMPLE macro.
+ * FUZZ_TEST_SIMPLE(test_process_data)
+ * {
+ * // Call the function under test using the `data` and `datalen`
+ * // variables.
+ * process_data(data, datalen);
+ * }
+ *
+ */
+#define FUZZ_TEST_SIMPLE(test_name) \
+ static ssize_t kfuzztest_simple_write_cb_##test_name(struct file *filp, const char __user *buf, size_t len, \
+ loff_t *off); \
+ static void kfuzztest_simple_logic_##test_name(char *data, size_t datalen); \
+ static const struct kfuzztest_simple_target __fuzz_test_simple__##test_name __section( \
+ ".kfuzztest_simple_target") __used = { \
+ .name = #test_name, \
+ .write_input_cb = kfuzztest_simple_write_cb_##test_name, \
+ }; \
+ FUZZ_TEST(test_name, struct kfuzztest_simple_arg) \
+ { \
+ /* We don't use the KFUZZTEST_EXPECT macro to define the
+ * non-null constraint on `arg->data` as we only want metadata
+ * to be emitted once, so we enforce it here manually. */ \
+ if (arg->data == NULL) \
+ return; \
+ kfuzztest_simple_logic_##test_name(arg->data, arg->datalen); \
+ } \
+ static ssize_t kfuzztest_simple_write_cb_##test_name(struct file *filp, const char __user *buf, size_t len, \
+ loff_t *off) \
+ { \
+ void *buffer; \
+ int ret; \
+ \
+ ret = kfuzztest_write_cb_common(filp, buf, len, off, &buffer); \
+ if (ret < 0) \
+ goto out; \
+ kfuzztest_simple_logic_##test_name(buffer, len); \
+ record_invocation(); \
+ ret = len; \
+ kfree(buffer); \
+out: \
+ return ret; \
+ } \
+ static void kfuzztest_simple_logic_##test_name(char *data, size_t datalen)
+
#endif /* KFUZZTEST_H */
--
2.51.0
Powered by blists - more mailing lists