Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
885914b
pkg/kcov: add pkg/kcov
Aug 24, 2025
2fe3264
kfuzztest: add pseudo syscall, attribute, and serialization
Aug 24, 2025
83dccf3
prog: add Extend method
Aug 24, 2025
95170f3
pkg/kfuzztest: add pkg/kfuzztest
Aug 24, 2025
b3c287d
syz-kfuzztest: add syz-kfuzztest tool
Aug 24, 2025
a1ae170
kfuzztest: add special dedicated and update manager config
Aug 24, 2025
d832b41
tools: add kfuzztest-gen tool
Aug 24, 2025
e5eea1c
docs: add kfuzztest and syz-kfuzztest documentation
Aug 24, 2025
ce0452b
sys/linux: add no_generate attribute to syz_kfuzztest_run
Aug 25, 2025
92a6ff7
prog/encodingexec: fix KFuzzTest ID updates
Aug 25, 2025
0bfd1a1
syz-manager: update enabledCalls earlier
Aug 25, 2025
e231400
prog: add ignoreLengths parameter to getMutationPrio interface
Aug 26, 2025
369c83e
pkg/mgrconfig: extract KFuzzTest targets in during Load
Aug 26, 2025
9635501
prog/target: cache syz_kfuzztest_run ID
Aug 26, 2025
438b969
pkg/kfuzztest: return astError
Aug 26, 2025
7bbf416
prog: forcibly null-terminate strings for KFuzzTest calls
Aug 26, 2025
fa70baa
prog: only generate one call per KFuzzTest program
Aug 26, 2025
55f0813
syz-kfuzztest: small changes, use fuzzer stats instead of custom
Sep 1, 2025
065ad6f
syz-kfuzztest: remove references to callhistogram, no context as field
Sep 1, 2025
44d57b9
pkg/mgrconfig: remove logFunc from parameters
Sep 1, 2025
264ea65
pkg/kfuzztest,pkg/kfuzztest-manager: move manager to new separate pkg
Sep 1, 2025
ad12623
pkg,prog: add standard headers to new files
Sep 1, 2025
a9f198a
pkg/kfuzztest: merge helpers.go into kfuzztest.go
Sep 1, 2025
d8a201c
prog: update null-termination for KFuzzTest targets
Sep 1, 2025
eb252e9
pkg/kfuzztest: propagate errors when reading elf strings
Sep 1, 2025
33d7314
executor: return negative error code in syz_kfuzztest_run failure paths
Sep 1, 2025
90cc9b1
executor: check for bytes_written != input_data_size in kfuzztest call
Sep 1, 2025
5e3621a
pkg/kfuzztest: remove unused log import
Sep 1, 2025
ec83a81
sys/linux: add meta arches for syz-kfuzztest-run, and license header
Sep 1, 2025
002f494
prog: ignore return value when caching syz_kfuzztest_run ID in lazyInit
Sep 1, 2025
1637b32
pkg/kfuzztest: validate length of data in fromBytes
Sep 1, 2025
316e5c5
tools/kfuzztest-gen: add license header
Sep 1, 2025
14a3ec7
pkg/kfuzztest: implement String() method for ExtractAllResult
Sep 1, 2025
dd8b054
tools: handle error returned by ActivateKFuzzTestTargets
Sep 1, 2025
e7226c8
kfuzztest: move target extraction back into manager, add caching
Sep 2, 2025
85f1512
pkg/kfuzztest-manager: fix coverage collection + small changes
Sep 2, 2025
9b48b81
pkg/kfuzztest: remove unused SupportsKFuzzTest()
Sep 2, 2025
b5372eb
pkg/fuzzer: implement RecommendedCalls() method for fuzzer
Sep 2, 2025
89c3400
pkg/kfuzztest: fix coverage collection and output
Sep 2, 2025
ad13bd5
tools/syz-prog2c: remove a log statement
Sep 2, 2025
2850032
tools/kfuzztest-gen: update logging / exiting situation
Sep 2, 2025
5f1ea62
tools/kfuzztest-gen: add usage, simplify args parsing
Sep 2, 2025
fb0c571
pkg/vminfo: add checker function for syz_kfuzztest_run
Sep 2, 2025
b40d484
docs: update usage in readme for syz-kfuzztest
Sep 2, 2025
ea1d0b7
pkg/kfuzztest: add a golden tests for description generation
Sep 2, 2025
f0707eb
pkg/kfuzztest: small fixes that came to light during testing
Sep 2, 2025
ae805f2
prog: move KFuzzTest null termination to encoding logic
Sep 3, 2025
e5b9888
prog/mutation: disable special pointers for KFuzzTest calls
Sep 4, 2025
7904f92
pkg/kfuzztest: add more types to builder
Sep 4, 2025
dadb4de
pkg/kfuzztest: use dwarf type in syscall description generation
Sep 4, 2025
68853b8
pkg/kfuzztest: remove unused log import
Sep 5, 2025
c9bf9cf
various: Fix lint errors on CI
Sep 5, 2025
2c2a6ad
docs/kfuzztest: remove trailing whitespace
Sep 8, 2025
d0a7a4e
prog/kfuzztest: use minalignment property introduced into KFuzzTest
Sep 8, 2025
ee5cc32
pkg/kfuzztest: fix nil pointer deref and annotations processing
Sep 9, 2025
fe3ca97
prog/kfuzztest: align payload to kFuzzTestPoisonSize
Sep 9, 2025
0e8eba3
pkg/kfuzztest: fix lint
Sep 9, 2025
250e138
pkg/kfuzztest: remove C++ syntax from common.h
Sep 10, 2025
d6cdb29
pkg/vminfo: make kfuzztest_run always supported
Sep 10, 2025
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
7 changes: 5 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ ifeq ("$(TARGETOS)", "trusty")
endif

.PHONY: all clean host target \
manager executor ci hub \
manager executor kfuzztest ci hub \
execprog mutate prog2c trace2syz repro upgrade db \
usbgen symbolize cover kconf syz-build crush \
bin/syz-extract bin/syz-fmt \
Expand All @@ -117,7 +117,7 @@ endif
presubmit_arch_executor presubmit_dashboard presubmit_race presubmit_race_dashboard presubmit_old

all: host target
host: manager repro mutate prog2c db upgrade
host: manager repro mutate prog2c db upgrade kfuzztest
target: execprog executor

executor: descriptions
Expand Down Expand Up @@ -217,6 +217,9 @@ syz-build:
bisect: descriptions
GOOS=$(HOSTOS) GOARCH=$(HOSTARCH) $(HOSTGO) build $(GOHOSTFLAGS) -o ./bin/syz-bisect github.com/google/syzkaller/tools/syz-bisect

kfuzztest: descriptions
GOOS=$(HOSTOS) GOARCH=$(HOSTARCH) $(HOSTGO) build $(GOHOSTFLAGS) -o ./bin/syz-kfuzztest github.com/google/syzkaller/syz-kfuzztest

verifier: descriptions
# TODO: switch syz-verifier to use syz-executor.
# GOOS=$(HOSTOS) GOARCH=$(HOSTARCH) $(HOSTGO) build $(GOHOSTFLAGS) -o ./bin/syz-verifier github.com/google/syzkaller/syz-verifier
Expand Down
138 changes: 138 additions & 0 deletions docs/kfuzztest.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
# KFuzzTest Integration With syzkaller

KFuzzTest, introduced initially in [this RFC](https://lore.kernel.org/all/[email protected]/)
is a framework for exposing internal kernel functions to a userspace fuzzing
engine like syzkaller. As the kernel docs put it:

> 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.

This document introduces how syzkaller integrates with KFuzzTest.

## Getting Started

Firstly, ensure that the KFuzzTest patch series has been applied to your Linux
tree.

As of the 22nd of August 2025, the most up-to-date version can be found in
[this Linux Kernel RFC](https://lore.kernel.org/all/[email protected]/).

Once this is done, KFuzzTest targets can be defined on arbitrary kernel
functions using the `FUZZ_TEST` macro as described in the kernel docs in
`Documentation/dev-tools/kfuzztest.rst`.

### Configuration Options

Ensure that the following KConfig options are enabled for your kernel image:

- `CONFIG_DEBUG_FS` (used as a communication interface by KFuzzTest).
- `CONFIG_DEBUG_KERNEL`.
- `CONFIG_KFUZZTEST`.

It is also **highly** recommended to enable the following KConfig options for
more effective fuzzing.

- `CONFIG_KASAN` (catch memory bugs such as out-of-bounds-accesses).
- `CONFIG_KCOV` (to enable coverage guided fuzzing).

## Fuzzing KFuzzTest Targets

Syzkaller implements three ways to fuzz KFuzzTest targets:

1. `syz-manager` integration with static targets
2. `syz-manager` with dynamic targets
3. `syz-kfuzztest`: a standalone tool that runs inside a VM, discovers KFuzzTest
targets dynamically, and fuzzes them.

### 1. `syz-manager` with static targets

Configuration for this method is identical to `syz-manager`, and is designed to
make it easy to integrate KFuzzTest fuzzing into existing continuous fuzzing
deployments.

One must first write a syzlang description for the KFuzzTest target(s) of
interest, for example in `/sys/linux/my_kfuzztest_target.txt`. Each target
should have the following format:

```
some_buffer {
buf ptr[inout, array[int8]]
buflen len[buf, int64]
}

kfuzztest_underflow_on_buffer(name ptr[in, string["test_underflow_on_buffer"]], data ptr[in, some_buffer], len bytesize[data]) (kfuzz_test)
```

Where:

- The first argument should be a string pointer to the name of the fuzz target,
i.e,. the name of its `debugfs` input directory in the kernel.
- The second should be a pointer to a struct of the type that the fuzz
target accepts as input.
- The third should be the size in bytes of the input argument.
- The call is annotated with attribute `kfuzz_test`.

For more information on writing syzkaller descriptions attributes, consult the
[syscall description](syscall_descriptions.md) and [syscall description syntax](syscall_descriptions_syntax.md)
documentation files.

To facilitate the tedious task of writing `syz_kfuzztest_run` descriptions, a
tool (`tools/kfuzztest-gen`) is provided to automatically generate these from a
`vmlinux` binary. One can run the tool and paste the output into a syzlang file.

```sh
go run ./tools/kfuzztest-gen --vmlinux=path/to/vmlinux
```

After writing these descriptions to a file under the `/sys/linux/` directory
(for example, `/sys/linux/my_fuzz_targets.txt`), they need to be compiled with
`make descriptions`.

Finally, the targets can be enabled in `syz-manager` config file in the
`enable_syscalls` field, e.g.

```json
{
"enable_syscalls": [ "syz_kfuzztest_run$test_underflow_on_buffer" ]
}
```

### 2. `syz-manager` with dynamic discovery

This feature greatly reduces the amount of setup needed for fuzzing KFuzzTest
targets, by discovering them all dynamically at launch.

This approach is considered less stable than the previous as it involves
generating descriptions for KFuzzTest targets without human input and then
immediately fuzzing them. It does, however, better reflect our intentions for
KFuzzTest: continuously fuzzing the kernel with a dynamically changing set of
targets with little intervention from syzkaller maintainers.

To enable this feature, configure the experimental `enable_kfuzztest` option in
the manager configuration, which enables all discovered KFuzzTest targets by
default.

```json
{
"enable_kfuzztest": true
}
```

**IMPORTANT:** for this to work, it is essential for the kernel image pointed to
by the manager configuration is built with `CONFIG_DWARF4` or `CONFIG_DWARF5`
enabled, as dynamic target discovery depends on these symbols being emitted.

### 3. `syz-kfuzztest`, an in-VM standalone tool

In contrast with `syz-manager`, `syz-kfuzztest` is designed to perform coverage
guided fuzzing from within a VM directly rather than orchestrating a fleet of
VMs. It is primarily targetted at development-time fuzzing, rather than longterm
continuous fuzzing.

For more information, consult [the `syz-kfuzztest` documentation](syz-kfuzztest.md).
1 change: 1 addition & 0 deletions docs/syscall_descriptions_syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ Call attributes are:
"fsck": the content of the compressed buffer argument for this syscall is a file system and the
string argument is a fsck-like command that will be called to verify the filesystem
"remote_cover": wait longer to collect remote coverage for this call.
"kfuzz_test": the call is a kfuzztest target
```

## Ints
Expand Down
106 changes: 106 additions & 0 deletions docs/syz-kfuzztest.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# `syz-kfuzztest`

`syz-kfuzztest` is a standalone tool for fuzzing KFuzzTest targets from within
the kernel being fuzzed (e.g., a VM).

It is intended to be used for development-time fuzzing rather than continuous
fuzzing like `syz-manager`.

For more information on KFuzzTest, consult the [dedicated readme](kfuzztest.md)
or the Kernel documentation.

## Usage (in-VM fuzzing)

### Getting the Kernel Ready

It is important that the target Kernel image has the correct KConfig options
enabled. Namely

- `CONFIG_KFUZZTEST`
- `CONFIG_DEBUG_FS`
- `CONFIG_DEBUG_KERNEL`
- `CONFIG_KCOV`
- `CONFIG_DWARF4` or `CONFIG_DWARF5`
- `CONFIG_KASAN` _(optional, choose your favorite sanitizers for a better shot
at finding bugs!)_

Furthermore, as you will need to connect to the VM being tested through SSH and
launch `syz-kfuzztest` _(a Go binary with LIBC dependencies)_, it is recommended
to create an image for the kernel being fuzzed (e.g., a Debian Bullseye image).
Detailed instructions on how to do this can be found in
[this setup guide](linux/setup_ubuntu-host_qemu-vm_x86-64-kernel.md).

### Building and Launching the Binary

The `syz-kfuzztest` binary is built with `make syz-kfuzztest`, and is intended
to run on the Kernel fuzzed. The common case for this is within a VM _(after
all, the tool is trying to make the Kernel crash)_.

Then, ensure that the `syz-kfuzztest` binary and `vmlinux` image are copied
over into the VM. E.g.,

```sh
scp $KERNEL/vmlinux root@my-vm:~/syz-kfuzztest/vmlinux
scp $SYZKALLER/bin/syz-kfuzztest root@lmy-vm:~/syz-kfuzztest/syz-kfuzztest
```

Then launched like this:

```
usage: ./bin/syz-kfuzztest [flags] [enabled targets]

Args:
One fuzz test name per enabled fuzz test arg. If empty, defaults to
all discovered targets.
Example:
./syz-kfuzztest -vmlinux ~/kernel/vmlinux fuzz_target_0 fuzz_target_1
Flags:
-display int
Number of seconds between console outputs (default 5)
-threads int
Number of threads (default 2)
-timeout int
Timeout between program executions in seconds (default 0)
-vmlinux string
Path to vmlinux binary (default "vmlinux")
-vv int
verbosity
```

The enabled targets, which are listed after the flag arguments, are the names of
the enabled fuzz targets. For example given some KFuzzTest targets:

```c
FUZZ_TEST(kfuzztest_target_1, struct input_arg_type)
{
/* ... */
}

FUZZ_TEST(kfuzztest_target_2, struct input_arg_type)
{
/* ... */
}

```

Can be fuzzed with:

```bash
./syz-kfuzztest -vmlinux path/to/vmlinux -threads 4 kfuzztest_target_1 kfuzztest_target_2
```

If the enabled targets list is left empty, `syz-kfuzztest` will fuzz all
discovered targets in the kernel.

On exit, `syz-kfuzztest` will write the collected program counters (which are
collected with KCOV) into a file called `pcs.out`. These program counters can
be fed into [`syz-cover`](../tools/syz-cover/syz-cover.go) to generate an HTML
visualization of the lines that were covered during fuzzing. It is recommended
to do this on the host machine rather than the VM.

For example:

```sh
scp root@my-vm:~/syz-kfuzztest/pcs.out .
go run tools/syz-cover -config my.cfg pcs.out # May require the -force flag.
```
54 changes: 54 additions & 0 deletions executor/common_linux.h
Original file line number Diff line number Diff line change
Expand Up @@ -5852,3 +5852,57 @@
}

#endif

#if SYZ_EXECUTOR || __NR_syz_kfuzztest_run

#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

static long syz_kfuzztest_run(volatile long test_name_ptr, volatile long input_data,

Check failure on line 5867 in executor/common_linux.h

View workflow job for this annotation

GitHub Actions / build

error: 'long int syz_kfuzztest_run(long int, long int, long int)' defined but not used [-Werror=unused-function]

Check failure on line 5867 in executor/common_linux.h

View workflow job for this annotation

GitHub Actions / race

error: 'long int syz_kfuzztest_run(long int, long int, long int)' defined but not used [-Werror=unused-function]

Check failure on line 5867 in executor/common_linux.h

View workflow job for this annotation

GitHub Actions / race

error: 'long int syz_kfuzztest_run(long int, long int, long int)' defined but not used [-Werror=unused-function]
volatile long input_data_size)
{
const char* test_name = (const char*)test_name_ptr;
if (!test_name) {
debug("syz_kfuzztest_run: test name was NULL\n");
return -1;
}
if (!input_data || input_data_size == 0) {
debug("syz_kfuzztest_run: input data was NULL\n");
return -1;
}

char buf[256];
int ret = snprintf(buf, sizeof(buf), "/sys/kernel/debug/kfuzztest/%s/input", test_name);
if (ret < 0 || (unsigned long)ret >= sizeof(buf)) {
debug("syz_kfuzztest_run: constructed path is too long or snprintf failed\n");
return -1;
}

int fd = openat(AT_FDCWD, buf, O_WRONLY, 0);
if (fd < 0) {
debug("syz_kfuzztest_run: failed to open %s\n", buf);
return -1;
}

ssize_t bytes_written = write(fd, (void*)input_data, (size_t)input_data_size);
if (bytes_written != input_data_size) {
debug("syz_kfuzztest_run: failed to write to %s, reason: %s\n", buf, strerror(errno));
close(fd);
return -1;
}

if (close(fd) != 0) {
debug("syz_kfuzztest_run: failed to close file\n");
return -1;
}

return 0;
}

#endif
4 changes: 4 additions & 0 deletions pkg/corpus/corpus.go
Original file line number Diff line number Diff line change
Expand Up @@ -260,3 +260,7 @@ func (corpus *Corpus) ProgsPerArea() map[string]int {
}
return ret
}

func (corpus *Corpus) Cover() []uint64 {
return corpus.cover.Serialize()
}
10 changes: 10 additions & 0 deletions pkg/fuzzer/fuzzer.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ type Fuzzer struct {
ctMu sync.Mutex // TODO: use RWLock.
ctRegenerate chan struct{}

modeKFuzzTest bool

execQueues
}

Expand Down Expand Up @@ -72,6 +74,13 @@ func NewFuzzer(ctx context.Context, cfg *Config, rnd *rand.Rand,
return f
}

func (fuzzer *Fuzzer) RecommendedCalls() int {
if fuzzer.modeKFuzzTest {
return prog.RecommendedCallsKFuzzTest
}
return prog.RecommendedCalls
}

type execQueues struct {
triageCandidateQueue *queue.DynamicOrderer
candidateQueue *queue.PlainQueue
Expand Down Expand Up @@ -214,6 +223,7 @@ type Config struct {
FetchRawCover bool
NewInputFilter func(call string) bool
PatchTest bool
ModeKFuzzTest bool
}

func (fuzzer *Fuzzer) triageProgCall(p *prog.Prog, info *flatrpc.CallInfo, call int, triage *map[int]*triageCall) {
Expand Down
2 changes: 1 addition & 1 deletion pkg/fuzzer/job.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func (ji *JobInfo) ID() string {

func genProgRequest(fuzzer *Fuzzer, rnd *rand.Rand) *queue.Request {
p := fuzzer.target.Generate(rnd,
prog.RecommendedCalls,
fuzzer.RecommendedCalls(),
fuzzer.ChoiceTable())
return &queue.Request{
Prog: p,
Expand Down
Loading
Loading