Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Documentation/dev-tools/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ Documentation/process/debugging/index.rst
kfence
kselftest
kunit/index
kfuzztest
ktap
checkuapi
gpio-sloppy-logic-analyzer
Expand Down
152 changes: 152 additions & 0 deletions Documentation/dev-tools/kfuzztest.rst
Original file line number Diff line number Diff line change
@@ -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 ``<file-name>_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/<test-name>``) 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.
7 changes: 7 additions & 0 deletions MAINTAINERS
Original file line number Diff line number Diff line change
Expand Up @@ -14057,6 +14057,13 @@ F: include/linux/kfifo.h
F: lib/kfifo.c
F: samples/kfifo/

KFUZZTEST
M: Ethan Graham <ethan.w.s.graham@gmail.com>
R: Alexander Potapenko <glider@google.com>
F: include/linux/kfuzztest.h
F: lib/kfuzztest/
F: Documentation/dev-tools/kfuzztest.rst

KGDB / KDB /debug_core
M: Jason Wessel <jason.wessel@windriver.com>
M: Daniel Thompson <danielt@kernel.org>
Expand Down
2 changes: 2 additions & 0 deletions crypto/asymmetric_keys/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
#
Expand Down
4 changes: 4 additions & 0 deletions crypto/asymmetric_keys/tests/Makefile
Original file line number Diff line number Diff line change
@@ -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
18 changes: 18 additions & 0 deletions crypto/asymmetric_keys/tests/pkcs7_kfuzz.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* PKCS#7 parser KFuzzTest target.
*
* Copyright 2025 Google LLC
*/
#include <crypto/pkcs7.h>
#include <linux/kfuzztest.h>

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;
}
24 changes: 24 additions & 0 deletions crypto/asymmetric_keys/tests/rsa_helper_kfuzz.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* RSA key extract helper KFuzzTest targets.
*
* Copyright 2025 Google LLC
*/
#include <crypto/internal/rsa.h>
#include <linux/kfuzztest.h>

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;
}
14 changes: 13 additions & 1 deletion include/asm-generic/vmlinux.lds.h
Original file line number Diff line number Diff line change
Expand Up @@ -391,7 +391,8 @@
TRACE_PRINTKS() \
BPF_RAW_TP() \
TRACEPOINT_STR() \
KUNIT_TABLE()
KUNIT_TABLE() \
KFUZZTEST_TABLE()

/*
* Data section helpers
Expand Down Expand Up @@ -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); \
Expand Down
90 changes: 90 additions & 0 deletions include/linux/kfuzztest.h
Original file line number Diff line number Diff line change
@@ -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 <linux/fs.h>
#include <linux/printk.h>
#include <linux/types.h>

#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 */
1 change: 1 addition & 0 deletions lib/Kconfig.debug
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
2 changes: 2 additions & 0 deletions lib/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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/

Expand Down
16 changes: 16 additions & 0 deletions lib/kfuzztest/Kconfig
Original file line number Diff line number Diff line change
@@ -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.
4 changes: 4 additions & 0 deletions lib/kfuzztest/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# SPDX-License-Identifier: GPL-2.0

obj-$(CONFIG_KFUZZTEST) += kfuzztest.o
kfuzztest-objs := main.o input.o
Loading