Add the foundational user-facing components for the KFuzzTest framework. This includes the main API header , the Kconfig option to enable the feature, and the required linker script changes which introduce a new ELF section in vmlinux. Note that KFuzzTest is intended strictly for debug builds only, and should never be enabled in a production build. The fact that it exposes internal kernel functions and state directly to userspace may constitute a serious security vulnerability if used for any reason other than testing. The header defines: - The FUZZ_TEST_SIMPLE() macro for creating test targets. - The `struct kfuzztest_simple_target` structure used to register tests. - The linker section (.kfuzztest_simple_target) where test metadata is stored for discovery by the framework. This patch only adds the public interface and build integration; no runtime logic is included. Signed-off-by: Ethan Graham --- PR v4: - Remove the complex FUZZ_TEST macro and associated dependencies, including domain constraints, annotations, and de-serialization, dramatically simplifying the flow. - Drop unused ELF sections (.kfuzztest_constraint, etc...) from the linker script, keeping only .kfuzztest_simple_target. PR v3: - Reorder definitions in kfuzztest.h for better flow and readability. - Introduce __KFUZZTEST_CONSTRAINT macro in preparation for the introduction of the FUZZ_TEST_SIMPLE macro in the following patch, which uses it for manually emitting constraint metadata. PR v1: - Move KFuzzTest metadata definitions to generic vmlinux linkage so that the framework isn't bound to x86_64. - Return -EFAULT when simple_write_to_buffer returns a value not equal to the input length in the main FUZZ_TEST macro. - Enforce a maximum input size of 64KiB in the main FUZZ_TEST macro, returning -EINVAL when it isn't respected. - Refactor KFUZZTEST_ANNOTATION_* macros. - Taint the kernel with TAINT_TEST inside the FUZZ_TEST macro when a fuzz target is invoked for the first time. --- --- include/asm-generic/vmlinux.lds.h | 14 ++++- include/linux/kfuzztest.h | 88 +++++++++++++++++++++++++++++++ lib/Kconfig.debug | 1 + lib/kfuzztest/Kconfig | 16 ++++++ 4 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 include/linux/kfuzztest.h create mode 100644 lib/kfuzztest/Kconfig diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index ae2d2359b79e..5aa46dbbc9b2 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -373,7 +373,8 @@ defined(CONFIG_AUTOFDO_CLANG) || defined(CONFIG_PROPELLER_CLANG) TRACE_PRINTKS() \ BPF_RAW_TP() \ TRACEPOINT_STR() \ - KUNIT_TABLE() + KUNIT_TABLE() \ + KFUZZTEST_TABLE() /* * Data section helpers @@ -966,6 +967,17 @@ defined(CONFIG_AUTOFDO_CLANG) || defined(CONFIG_PROPELLER_CLANG) BOUNDED_SECTION_POST_LABEL(.kunit_init_test_suites, \ __kunit_init_suites, _start, _end) +#ifdef CONFIG_KFUZZTEST +#define KFUZZTEST_TABLE() \ + . = ALIGN(PAGE_SIZE); \ + __kfuzztest_simple_targets_start = .; \ + KEEP(*(.kfuzztest_simple_target)); \ + __kfuzztest_simple_targets_end = .; \ + +#else /* CONFIG_KFUZZTEST */ +#define KFUZZTEST_TABLE() +#endif /* CONFIG_KFUZZTEST */ + #ifdef CONFIG_BLK_DEV_INITRD #define INIT_RAM_FS \ . = ALIGN(4); \ diff --git a/include/linux/kfuzztest.h b/include/linux/kfuzztest.h new file mode 100644 index 000000000000..62fce9267761 --- /dev/null +++ b/include/linux/kfuzztest.h @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * The Kernel Fuzz Testing Framework (KFuzzTest) API for defining fuzz targets + * for internal kernel functions. + * + * Copyright 2025 Google LLC + */ +#ifndef KFUZZTEST_H +#define KFUZZTEST_H + +#include +#include +#include + +#define KFUZZTEST_MAX_INPUT_SIZE (PAGE_SIZE * 16) + +/* Common code for receiving inputs from userspace. */ +int kfuzztest_write_cb_common(struct file *filp, const char __user *buf, size_t len, loff_t *off, void **test_buffer); + +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); +}; + +/** + * FUZZ_TEST_SIMPLE - defines a KFuzzTest target + * + * @test_name: the unique identifier for the fuzz test, which is used to name + * the debugfs entry. + * + * This macro defines a fuzz target entry point that accepts raw byte buffers + * from userspace. It registers a struct kfuzztest_simple_target which the + * framework exposes via debugfs. + * + * When userspace writes to the corresponding debugfs file, the framework + * allocates a kernel buffer, copies the user data, and passes it to the + * logic defined in the macro body. + * + * 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 + * implicitly defines the following variables: + * + * - `char *data`: A pointer to the raw input data. + * - `size_t datalen`: The length of the input data. + * + * Example Usage: + * + * // 1. The kernel function that we want 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 ssize_t 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, \ + }; \ + 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; \ + ret = kfuzztest_simple_logic_##test_name(buffer, len); \ + if (ret == 0) \ + ret = len; \ + kfree(buffer); \ +out: \ + return ret; \ + } \ + static ssize_t kfuzztest_simple_logic_##test_name(char *data, size_t datalen) + +#endif /* KFUZZTEST_H */ diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index dc0e0c6ed075..49a1748b9f24 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -1947,6 +1947,7 @@ endmenu menu "Kernel Testing and Coverage" source "lib/kunit/Kconfig" +source "lib/kfuzztest/Kconfig" config NOTIFIER_ERROR_INJECTION tristate "Notifier error injection" diff --git a/lib/kfuzztest/Kconfig b/lib/kfuzztest/Kconfig new file mode 100644 index 000000000000..d8e9caaac108 --- /dev/null +++ b/lib/kfuzztest/Kconfig @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config KFUZZTEST + bool "KFuzzTest - enable support for internal fuzz targets" + depends on DEBUG_FS && DEBUG_KERNEL + help + Enables support for the kernel fuzz testing framework (KFuzzTest), an + interface for exposing internal kernel functions to a userspace fuzzing + engine. KFuzzTest targets are exposed via a debugfs interface that + accepts raw binary inputs from userspace, and is designed to make it + easier to fuzz deeply nested kernel code that is hard to reach from + the system call boundary. Using a simple macro-based API, developers + can add a new fuzz target with minimal boilerplate code. + + WARNING: This exposes internal kernel functions directly to userspace + and must NEVER be enabled in production builds. -- 2.51.0