diff --git a/Documentation/dev-tools/index.rst b/Documentation/dev-tools/index.rst index 4b8425e348abd1..32b634ebea918e 100644 --- a/Documentation/dev-tools/index.rst +++ b/Documentation/dev-tools/index.rst @@ -33,6 +33,7 @@ Documentation/process/debugging/index.rst kfence kselftest kunit/index + kfuzztest ktap checkuapi gpio-sloppy-logic-analyzer diff --git a/Documentation/dev-tools/kfuzztest.rst b/Documentation/dev-tools/kfuzztest.rst new file mode 100644 index 00000000000000..f5ccf545d45d8b --- /dev/null +++ b/Documentation/dev-tools/kfuzztest.rst @@ -0,0 +1,152 @@ +.. SPDX-License-Identifier: GPL-2.0 +.. Copyright 2025 Google LLC + +========================================= +Kernel Fuzz Testing Framework (KFuzzTest) +========================================= + +Overview +======== + +The Kernel Fuzz Testing Framework (KFuzzTest) is a framework designed to expose +internal kernel functions to a userspace fuzzing engine. + +It is intended for testing stateless or low-state functions that are difficult +to reach from the system call interface, such as routines involved in file +format parsing or complex data transformations. This provides a method for +in-situ fuzzing of kernel code without requiring that it be built as a separate +userspace library or that its dependencies be stubbed out. + +The framework consists of two main components: + +1. An API, based on the ``FUZZ_TEST_SIMPLE`` macro, for defining test targets + directly in the kernel tree. +2. A ``debugfs`` interface through which a userspace fuzzer submits raw + binary test inputs. + +.. warning:: + KFuzzTest is a debugging and testing tool. It exposes internal kernel + functions to userspace with minimal sanitization and is designed for + use in controlled test environments only. It must **NEVER** be enabled + in production kernels. + +Supported Architectures +======================= + +KFuzzTest is designed for generic architecture support. It has only been +explicitly tested on x86_64. + +Usage +===== + +To enable KFuzzTest, configure the kernel with:: + + CONFIG_KFUZZTEST=y + +which depends on ``CONFIG_DEBUGFS`` for receiving userspace inputs, and +``CONFIG_DEBUG_KERNEL`` as an additional guardrail for preventing KFuzzTest +from finding its way into a production build accidentally. + +The KFuzzTest sample fuzz targets can be built in with +``CONFIG_SAMPLE_KFUZZTEST``. + +KFuzzTest currently only supports targets that are built into the kernel, as the +core module's startup process discovers fuzz targets from a dedicated ELF +section during startup. + +Defining a KFuzzTest target +--------------------------- + +A fuzz target should be defined in a .c file. The recommended place to define +this is under the subsystem's ``/tests`` directory in a ``_kfuzz.c`` +file, following the convention used by KUnit. The only strict requirement is +that the function being fuzzed is visible to the fuzz target. + +Use the ``FUZZ_TEST_SIMPLE`` macro to define a fuzz target. This macro is +designed for functions that accept a buffer and its length (e.g., +``(const char *data, size_t datalen)``). + +This macro provides ``data`` and ``datalen`` variables implicitly to the test +body. + +.. code-block:: c + + /* 1. The kernel function that we want to fuzz. */ + int process_data(const char *data, size_t len); + + /* 2. Define the fuzz target with the FUZZ_TEST_SIMPLE macro. */ + FUZZ_TEST_SIMPLE(test_process_data) + { + /* 3. Call the kernel function with the provided input. */ + process_data(data, datalen); + } + +A ``FUZZ_TEST_SIMPLE`` target creates a debugfs directory +(``/sys/kernel/debug/kfuzztest/``) containing a single write-only +file ``input_simple``: writing a raw blob to this file will invoke the fuzz +target, passing the blob as ``(data, datalen)``. + +Basic Usage +^^^^^^^^^^^ + +Because the interface accepts raw binary data, targets can be smoke-tested or +fuzzed naively using standard command-line tools without any external +dependencies. + +For example, to feed 128 bytes of random data to the target defined above: + +.. code-block:: sh + + head -c 128 /dev/urandom > \ + /sys/kernel/debug/kfuzztest/test_process_data/input_simple + +Integration with Fuzzers +^^^^^^^^^^^^^^^^^^^^^^^^ + +The simple interface makes it easy to integrate with userspace fuzzers (e.g., +LibFuzzer, AFL++, honggfuzz). A LibFuzzer, for example, harness may look like +so: + +.. code-block:: c + + /* Path to the simple target's input file */ + const char *filepath = "/sys/kernel/debug/kfuzztest/test_process_data/input_simple"; + + extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + FILE *f = fopen(filepath, "w"); + if (!f) { + return 0; /* Fuzzer should not stop. */ + } + /* Write the raw fuzzer input directly. */ + fwrite(Data, 1, Size, f); + fclose(f); + return 0; + } + +Note that while it is simple to feed inputs to KFuzzTest targets, kernel +coverage collection is key for the effectiveness of a coverage-guided fuzzer; +setup of KCOV or other coverage mechanisms is outside of KFuzzTest's scope. + +Metadata +-------- + +The ``FUZZ_TEST_SIMPLE`` macro embeds metadata into a dedicated section within +the main ``.data`` section of the final ``vmlinux`` binary: +``.kfuzztest_simple_target``, delimited by ``__kfuzztest_simple_targets_start`` +and ``__kfuzztest_simple_targets_end``. + +The metadata serves two purposes: + +1. The core module uses the ``.kfuzztest_simple_target`` section at boot to + discover every test instance and create its ``debugfs`` directory and + ``input_simple`` file. +2. Tooling can use this section for offline discovery. While available fuzz + targets can be trivially enumerated at runtime by listing the directories + under ``/sys/kernel/debug/kfuzztest``, the metadata allows fuzzing + orchestrators to index available fuzz targets directly from the ``vmlinux`` + binary without needing to boot the kernel. + +This metadata consists of an array of ``struct kfuzztest_simple_target``. The +``name`` field within this struct references data in other locations of the +``vmlinux`` binary, and therefore a userspace tool that parses the ELF must +resolve these pointers to read the underlying data. diff --git a/MAINTAINERS b/MAINTAINERS index 0d044a58cbfe0f..465ba325e34c4c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14057,6 +14057,13 @@ F: include/linux/kfifo.h F: lib/kfifo.c F: samples/kfifo/ +KFUZZTEST +M: Ethan Graham +R: Alexander Potapenko +F: include/linux/kfuzztest.h +F: lib/kfuzztest/ +F: Documentation/dev-tools/kfuzztest.rst + KGDB / KDB /debug_core M: Jason Wessel M: Daniel Thompson diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile index bc65d3b98dcbfa..77b825aee6b24f 100644 --- a/crypto/asymmetric_keys/Makefile +++ b/crypto/asymmetric_keys/Makefile @@ -67,6 +67,8 @@ obj-$(CONFIG_PKCS7_TEST_KEY) += pkcs7_test_key.o pkcs7_test_key-y := \ pkcs7_key_type.o +obj-y += tests/ + # # Signed PE binary-wrapped key handling # diff --git a/crypto/asymmetric_keys/tests/Makefile b/crypto/asymmetric_keys/tests/Makefile new file mode 100644 index 00000000000000..b43aa769e2ce65 --- /dev/null +++ b/crypto/asymmetric_keys/tests/Makefile @@ -0,0 +1,4 @@ +pkcs7-kfuzz-y := $(and $(CONFIG_KFUZZTEST),$(filter y, $(CONFIG_PKCS7_MESSAGE_PARSER))) +rsa-helper-kfuzz-y := $(and $(CONFIG_KFUZZTEST),$(filter y, $(CONFIG_CRYPTO_RSA))) +obj-$(pkcs7-kfuzz-y) += pkcs7_kfuzz.o +obj-$(rsa-helper-kfuzz-y) += rsa_helper_kfuzz.o diff --git a/crypto/asymmetric_keys/tests/pkcs7_kfuzz.c b/crypto/asymmetric_keys/tests/pkcs7_kfuzz.c new file mode 100644 index 00000000000000..2e1a59fb60357d --- /dev/null +++ b/crypto/asymmetric_keys/tests/pkcs7_kfuzz.c @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * PKCS#7 parser KFuzzTest target. + * + * Copyright 2025 Google LLC + */ +#include +#include + +FUZZ_TEST_SIMPLE(test_pkcs7_parse_message) +{ + struct pkcs7_message *msg; + + msg = pkcs7_parse_message(data, datalen); + if (msg && !IS_ERR(msg)) + pkcs7_free_message(msg); + return 0; +} diff --git a/crypto/asymmetric_keys/tests/rsa_helper_kfuzz.c b/crypto/asymmetric_keys/tests/rsa_helper_kfuzz.c new file mode 100644 index 00000000000000..e45e8fa5319005 --- /dev/null +++ b/crypto/asymmetric_keys/tests/rsa_helper_kfuzz.c @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * RSA key extract helper KFuzzTest targets. + * + * Copyright 2025 Google LLC + */ +#include +#include + +FUZZ_TEST_SIMPLE(test_rsa_parse_pub_key) +{ + struct rsa_key out; + + rsa_parse_pub_key(&out, data, datalen); + return 0; +} + +FUZZ_TEST_SIMPLE(test_rsa_parse_priv_key) +{ + struct rsa_key out; + + rsa_parse_priv_key(&out, data, datalen); + return 0; +} diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index 8ca130af301fc5..a99084c6475427 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -391,7 +391,8 @@ TRACE_PRINTKS() \ BPF_RAW_TP() \ TRACEPOINT_STR() \ - KUNIT_TABLE() + KUNIT_TABLE() \ + KFUZZTEST_TABLE() /* * Data section helpers @@ -985,6 +986,17 @@ 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 00000000000000..4f210c5ec9199a --- /dev/null +++ b/include/linux/kfuzztest.h @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * The Kernel Fuzz Testing Framework (KFuzzTest) API for defining fuzz targets + * for internal kernel functions. + * + * For more information please see Documentation/dev-tools/kfuzztest.rst. + * + * 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 ba36939fda79bf..ad3257301b131b 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -1943,6 +1943,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/Makefile b/lib/Makefile index aaf677cf4527e8..3acdf76ca979e8 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -324,6 +324,8 @@ obj-$(CONFIG_GENERIC_LIB_CMPDI2) += cmpdi2.o obj-$(CONFIG_GENERIC_LIB_UCMPDI2) += ucmpdi2.o obj-$(CONFIG_OBJAGG) += objagg.o +obj-$(CONFIG_KFUZZTEST) += kfuzztest/ + # pldmfw library obj-$(CONFIG_PLDMFW) += pldmfw/ diff --git a/lib/kfuzztest/Kconfig b/lib/kfuzztest/Kconfig new file mode 100644 index 00000000000000..d8e9caaac1088a --- /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. diff --git a/lib/kfuzztest/Makefile b/lib/kfuzztest/Makefile new file mode 100644 index 00000000000000..3cf5da5597a478 --- /dev/null +++ b/lib/kfuzztest/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_KFUZZTEST) += kfuzztest.o +kfuzztest-objs := main.o input.o diff --git a/lib/kfuzztest/input.c b/lib/kfuzztest/input.c new file mode 100644 index 00000000000000..aae966ea76b33c --- /dev/null +++ b/lib/kfuzztest/input.c @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * KFuzzTest input handling. + * + * Copyright 2025 Google LLC + */ +#include + +int kfuzztest_write_cb_common(struct file *filp, const char __user *buf, size_t len, loff_t *off, void **test_buffer) +{ + void *buffer; + ssize_t ret; + + /* + * Enforce a zero-offset to ensure that all data is passed down in a + * single contiguous blob and not fragmented across multiple write + * system calls. + */ + if (*off) + return -EINVAL; + + /* + * Taint the kernel on the first fuzzing invocation. The debugfs + * interface provides a high-risk entry point for userspace to + * call kernel functions with untrusted input. + */ + if (!test_taint(TAINT_TEST)) + add_taint(TAINT_TEST, LOCKDEP_STILL_OK); + + if (len > KFUZZTEST_MAX_INPUT_SIZE) { + pr_warn("kfuzztest: user input of size %zu is too large", len); + return -EINVAL; + } + + buffer = kzalloc(len, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + ret = simple_write_to_buffer(buffer, len, off, buf, len); + if (ret != len) { + kfree(buffer); + return -EFAULT; + } + + *test_buffer = buffer; + return 0; +} diff --git a/lib/kfuzztest/main.c b/lib/kfuzztest/main.c new file mode 100644 index 00000000000000..40a9e56c81ada4 --- /dev/null +++ b/lib/kfuzztest/main.c @@ -0,0 +1,142 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * KFuzzTest core module initialization and debugfs interface. + * + * Copyright 2025 Google LLC + */ +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Ethan Graham "); +MODULE_DESCRIPTION("Kernel Fuzz Testing Framework (KFuzzTest)"); + +extern const struct kfuzztest_simple_target __kfuzztest_simple_targets_start[]; +extern const struct kfuzztest_simple_target __kfuzztest_simple_targets_end[]; + +struct target_fops { + struct file_operations target_simple; +}; + +/** + * struct kfuzztest_state - global state for the KFuzzTest module + * + * @kfuzztest_dir: The root debugfs directory, /sys/kernel/debug/kfuzztest/. + * @num_targets: number of registered targets. + * @target_fops: array of file operations for each registered target. + */ +struct kfuzztest_state { + struct dentry *kfuzztest_dir; + struct target_fops *target_fops; + size_t num_targets; +}; + +static struct kfuzztest_state state; + +static void cleanup_kfuzztest_state(struct kfuzztest_state *st) +{ + debugfs_remove_recursive(st->kfuzztest_dir); + st->num_targets = 0; + kfree(st->target_fops); + st->target_fops = NULL; +} + +static const umode_t KFUZZTEST_INPUT_PERMS = 0222; + +static int initialize_target_dir(struct kfuzztest_state *st, const struct kfuzztest_simple_target *targ, + struct target_fops *fops) +{ + struct dentry *dir, *input_simple; + int err = 0; + + dir = debugfs_create_dir(targ->name, st->kfuzztest_dir); + if (!dir) + err = -ENOMEM; + else if (IS_ERR(dir)) + err = PTR_ERR(dir); + if (err) { + pr_info("kfuzztest: failed to create /kfuzztest/%s dir", targ->name); + goto out; + } + + input_simple = debugfs_create_file("input_simple", KFUZZTEST_INPUT_PERMS, dir, NULL, &fops->target_simple); + if (!input_simple) + err = -ENOMEM; + else if (IS_ERR(input_simple)) + err = PTR_ERR(input_simple); + if (err) + pr_info("kfuzztest: failed to create /kfuzztest/%s/input_simple", targ->name); +out: + return err; +} + +/** + * kfuzztest_init - initializes the debug filesystem for KFuzzTest + * + * Each registered target in the ".kfuzztest_simple_target" section gets its own + * subdirectory under "/sys/kernel/debug/kfuzztest/" containing one + * write-only "input_simple" file used for receiving binary inputs from + * userspace. + * + * @return 0 on success or an error + */ +static int __init kfuzztest_init(void) +{ + const struct kfuzztest_simple_target *targ; + int err = 0; + int i = 0; + + state.num_targets = __kfuzztest_simple_targets_end - __kfuzztest_simple_targets_start; + state.target_fops = kzalloc(sizeof(struct target_fops) * state.num_targets, GFP_KERNEL); + if (!state.target_fops) + return -ENOMEM; + + /* Create the main "kfuzztest" directory in /sys/kernel/debug. */ + state.kfuzztest_dir = debugfs_create_dir("kfuzztest", NULL); + if (!state.kfuzztest_dir) { + pr_warn("kfuzztest: could not create 'kfuzztest' debugfs directory"); + return -ENOMEM; + } + if (IS_ERR(state.kfuzztest_dir)) { + pr_warn("kfuzztest: could not create 'kfuzztest' debugfs directory"); + err = PTR_ERR(state.kfuzztest_dir); + state.kfuzztest_dir = NULL; + return err; + } + + for (targ = __kfuzztest_simple_targets_start; targ < __kfuzztest_simple_targets_end; targ++, i++) { + state.target_fops[i].target_simple = (struct file_operations){ + .owner = THIS_MODULE, + .write = targ->write_input_cb, + }; + err = initialize_target_dir(&state, targ, &state.target_fops[i]); + /* + * Bail out if a single target fails to initialize. This avoids + * partial setup, and a failure here likely indicates an issue + * with debugfs. + */ + if (err) + goto cleanup_failure; + pr_info("kfuzztest: registered target %s", targ->name); + } + return 0; + +cleanup_failure: + cleanup_kfuzztest_state(&state); + return err; +} + +static void __exit kfuzztest_exit(void) +{ + pr_info("kfuzztest: exiting"); + cleanup_kfuzztest_state(&state); +} + +module_init(kfuzztest_init); +module_exit(kfuzztest_exit); diff --git a/samples/Kconfig b/samples/Kconfig index 5bc7c9e5a59e0a..ff5cf3d07102a3 100644 --- a/samples/Kconfig +++ b/samples/Kconfig @@ -320,6 +320,13 @@ config SAMPLE_HUNG_TASK Reading these files with multiple processes triggers hung task detection by holding locks for a long time (256 seconds). +config SAMPLE_KFUZZTEST + bool "Build KFuzzTest sample targets" + depends on KFUZZTEST + help + Build KFuzzTest sample targets that serve as selftests for raw input + delivery and KASAN out-of-bounds detection. + source "samples/rust/Kconfig" source "samples/damon/Kconfig" diff --git a/samples/Makefile b/samples/Makefile index 07641e177bd8bb..3a0e7f744f445d 100644 --- a/samples/Makefile +++ b/samples/Makefile @@ -44,4 +44,5 @@ obj-$(CONFIG_SAMPLE_DAMON_WSSE) += damon/ obj-$(CONFIG_SAMPLE_DAMON_PRCL) += damon/ obj-$(CONFIG_SAMPLE_DAMON_MTIER) += damon/ obj-$(CONFIG_SAMPLE_HUNG_TASK) += hung_task/ +obj-$(CONFIG_SAMPLE_KFUZZTEST) += kfuzztest/ obj-$(CONFIG_SAMPLE_TSM_MR) += tsm-mr/ diff --git a/samples/kfuzztest/Makefile b/samples/kfuzztest/Makefile new file mode 100644 index 00000000000000..2dc5d424824d63 --- /dev/null +++ b/samples/kfuzztest/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only + +obj-$(CONFIG_SAMPLE_KFUZZTEST) += underflow_on_buffer.o diff --git a/samples/kfuzztest/underflow_on_buffer.c b/samples/kfuzztest/underflow_on_buffer.c new file mode 100644 index 00000000000000..5568c5e6be7adb --- /dev/null +++ b/samples/kfuzztest/underflow_on_buffer.c @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * This file contains a KFuzzTest example target that ensures that a buffer + * underflow on a region triggers a KASAN OOB access report. + * + * Copyright 2025 Google LLC + */ + +/** + * test_underflow_on_buffer - a sample fuzz target + * + * This sample fuzz target serves to illustrate the usage of the + * FUZZ_TEST_SIMPLE macro, as well as provide a sort of self-test that KFuzzTest + * functions correctly for trivial fuzz targets. In KASAN builds, fuzzing this + * harness should trigger a report for every input (provided that its length is + * greater than 0 and less than KFUZZTEST_MAX_INPUT_SIZE). + * + * This harness can be invoked (naively) like so: + * head -c 128 /dev/urandom > \ + * /sys/kernel/debug/kfuzztest/test_underflow_on_buffer/input_simple + */ +#include + +static void underflow_on_buffer(char *buf, size_t buflen) +{ + size_t i; + + /* + * Print the address range of `buf` to allow correlation with the + * subsequent KASAN report. + */ + pr_info("buf = [%px, %px)", buf, buf + buflen); + + /* First ensure that all bytes in `buf` are accessible. */ + for (i = 0; i < buflen; i++) + READ_ONCE(buf[i]); + /* + * Provoke a buffer underflow on the first byte preceding `buf`, + * triggering a KASAN report. + */ + READ_ONCE(*((char *)buf - 1)); +} + +/** + * Define the fuzz target. This wrapper ensures that the `underflow_on_buffer` + * function is invoked with the data provided from userspace. + */ +FUZZ_TEST_SIMPLE(test_underflow_on_buffer) +{ + underflow_on_buffer(data, datalen); + return 0; +}