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 @@ -30,6 +30,7 @@ Documentation/process/debugging/index.rst
kmemleak
kcsan
kfence
kfuzztest
kselftest
kunit/index
ktap
Expand Down
187 changes: 187 additions & 0 deletions Documentation/dev-tools/kfuzztest.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
.. 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 four main components:

1. An API, based on the ``FUZZ_TEST`` macro, for defining test targets
directly in the kernel tree.
2. A binary serialization format for passing complex, pointer-rich data
structures from userspace to the kernel.
3. A ``debugfs`` interface through which a userspace fuzzer submits
serialized test inputs.
4. Metadata embedded in dedicated ELF sections of the ``vmlinux`` binary to
allow for the discovery of available fuzz targets by external tooling.

.. 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 currently only supported for x86_64.

Usage
-----

To enable KFuzzTest, configure the kernel with::

CONFIG_KFUZZTEST=y

which depends on ``CONFIG_DEBUGFS`` for receiving userspace inputs.

Declaring a KFuzzTest target
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

A fuzz target is defined directly in a .c file, typically alongside the function
being tested. This process involves three main parts: defining an input
structure, writing the test body using the ``FUZZ_TEST`` macro, and optionally
adding metadata for the fuzzer.

The following example illustrates how to create a fuzz target for a function
``int process_data(const char *data, size_t len)``.

.. code-block:: c

// 1. Define a struct to model the inputs for the function under test.
// Each field corresponds to an argument needed by the function.
struct process_data_inputs {
const char *data;
size_t len;
};

// 2. Define the fuzz target using the FUZZ_TEST macro.
// The first parameter is a unique name for the target.
// The second parameter is the input struct defined above.
FUZZ_TEST(test_process_data, struct process_data_inputs)
{
// Within this body, the 'arg' variable is a pointer to a
// fully initialized 'struct process_data_inputs'.

// 3. (Optional) Add constraints to define preconditions.
// This check ensures 'arg->data' is not NULL. If the condition
// is not met, the test exits early. This also creates metadata
// to inform the fuzzer.
KFUZZTEST_EXPECT_NOT_NULL(process_data_inputs, data);

// 4. (Optional) Add annotations to provide semantic hints.
// This annotation informs the fuzzer that the 'len' field
// is the length of the buffer pointed to by 'data'.
// Annotations do not add any runtime checks.
KFUZZTEST_ANNOTATE_LEN(process_data_inputs, len, data);

// 5. Call the kernel function with the provided inputs.
// Memory errors like out-of-bounds accesses on 'arg->data' will
// be detected by KASAN or other memory error detection tools.
process_data(arg->data, arg->len);
}

KFuzzTest provides two families of macros to improve the quality of fuzzing:

- ``KFUZZTEST_EXPECT_*``: These macros define constraints, which are
preconditions that must be true for the test to proceed. They are enforced
with a runtime check in the kernel. If a check fails, the current test run is
aborted. This metadata helps the userspace fuzzer avoid generating invalid
inputs.

- ``KFUZZTEST_ANNOTATE_*``: These macros define annotations, which are purely
semantic hints for the fuzzer. They do not add any runtime checks and exist
only to help the fuzzer generate more intelligent and structurally correct
inputs. For example, KFUZZTEST_ANNOTATE_LEN links a size field to a pointer
field, which is a common pattern in C APIs.

Input Format
------------

KFuzzTest targets receive their inputs from userspace via a write to a dedicated
debugfs ``/sys/kernel/debug/kfuzztest/<test-name>/input``.

The data written to this file must be a single binary blob that follows a
specific serialization format. This format is designed to allow complex,
pointer-rich C structures to be represented in a flat buffer, requiring only a
single kernel allocation and copy from userspace.

The format consists of three main parts laid out sequentially: a region array,
a relocation table, and the payload.::

+----------------+---------------------+-----------+----------------+
| region array | relocation table | padding | payload |
+----------------+---------------------+-----------+----------------+

Region Array
~~~~~~~~~~~~

This component is a header that describes how the raw data in the Payload is
partitioned into logical memory regions. It consists of a count of regions
followed by an array of ``struct reloc_region``, where each entry defines a
single region with its size and offset from the start of the payload.

.. code-block:: c

struct reloc_region {
uint32_t offset;
uint32_t size;
};

struct reloc_region_array {
uint32_t num_regions;
struct reloc_region regions[];
};

By convention, region 0 represents the top-level input struct that is passed
as the arg variable to the FUZZ_TEST body. Subsequent regions typically
represent data buffers pointed to by fields within that struct. Region array
entries must be ordered by offset ascending, and must not overlap with one
another.

Relocation Table
~~~~~~~~~~~~~~~~

The relocation table provides the instructions for the kernel to "hydrate" the
payload by patching pointer fields. It contains an array of
``struct reloc_entry`` items. Each entry acts as a linking instruction,
specifying:

- The location of a pointer that needs to be patched (identified by a region
ID and an offset within that region).

- The target region that the pointer should point to (identified by the
target's region ID) or ``KFUZZTEST_REGIONID_NULL`` if the pointer is ``NULL``.

This table also specifies the amount of padding between its end and the start
of the payload, which should be at least 8 bytes.

Payload
~~~~~~~

The payload contains the raw binary data for all regions, concatenated together
according to their specified offsets.

- Alignment: The start of the payload must be aligned to the most restrictive
alignment requirement of all its constituent regions. The framework ensures
that each region within the payload is then placed at an offset that respects
its own type's alignment.

- Padding and Poisoning: The space between the end of one region's data and the
beginning of the next must be sufficient for padding. In KASAN builds,
KFuzzTest poisons this unused padding, allowing for precise detection of
out-of-bounds memory accesses between adjacent buffers. This padding should
be at least ``KFUZZTEST_POISON_SIZE`` bytes as defined in
`include/linux/kfuzztest.h``.
22 changes: 22 additions & 0 deletions arch/x86/kernel/vmlinux.lds.S
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,26 @@ ASSERT(__relocate_kernel_end - __relocate_kernel_start <= KEXEC_CONTROL_CODE_MAX
#else
#define KEXEC_RELOCATE_KERNEL
#endif

#ifdef CONFIG_KFUZZTEST
#define KFUZZTEST_TABLE \
. = ALIGN(PAGE_SIZE); \
__kfuzztest_targets_start = .; \
KEEP(*(.kfuzztest_target)); \
__kfuzztest_targets_end = .; \
. = ALIGN(PAGE_SIZE); \
__kfuzztest_constraints_start = .; \
KEEP(*(.kfuzztest_constraint)); \
__kfuzztest_constraints_end = .; \
. = ALIGN(PAGE_SIZE); \
__kfuzztest_annotations_start = .; \
KEEP(*(.kfuzztest_annotation)); \
__kfuzztest_annotations_end = .;

#else /* CONFIG_KFUZZTEST */
#define KFUZZTEST_TABLE
#endif /* CONFIG_KFUZZTEST */

PHDRS {
text PT_LOAD FLAGS(5); /* R_E */
data PT_LOAD FLAGS(6); /* RW_ */
Expand Down Expand Up @@ -199,6 +219,8 @@ SECTIONS
CONSTRUCTORS
KEXEC_RELOCATE_KERNEL

KFUZZTEST_TABLE

/* rarely changed data like cpu maps */
READ_MOSTLY_DATA(INTERNODE_CACHE_BYTES)

Expand Down
28 changes: 28 additions & 0 deletions include/linux/kasan.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,34 @@ static __always_inline bool kasan_unpoison_pages(struct page *page,
return false;
}

#ifdef CONFIG_KASAN_GENERIC

/**
* kasan_poison_last_granule - mark the last granule of the memory range as
* inaccessible
* @address: range start address, must be aligned to KASAN_GRANULE_SIZE
* @size: range size
*
* This function is only available for the generic mode, as it's the only mode
* that has partially poisoned memory granules.
*/
void kasan_poison_last_granule(const void *address, size_t size);

#else /* CONFIG_KASAN_GENERIC */

static inline void kasan_poison_last_granule(const void *address, size_t size) { }

#endif /* CONFIG_KASAN_GENERIC */

/**
* kasan_poison - mark the memory range as inaccessible
* @addr: range start address, must be aligned to KASAN_GRANULE_SIZE
* @size: range size, must be aligned to KASAN_GRANULE_SIZE
* @value: value that's written to metadata for the range
* @init: whether to initialize the memory range (only for hardware tag-based)
*/
void kasan_poison(const void *addr, size_t size, u8 value, bool init);

void __kasan_poison_slab(struct slab *slab);
static __always_inline void kasan_poison_slab(struct slab *slab)
{
Expand Down
Loading