Documentation/dev-tools/kcov-dataflow.rst | 282 +++++++++++++++++++ include/linux/sched.h | 8 + kernel/Makefile | 3 + kernel/kcov.c | 307 +++++++++++++++++++++ lib/Kconfig.debug | 43 +++ rust/Makefile | 1 + scripts/Makefile.kcov | 6 + scripts/Makefile.lib | 7 + tools/kcov-dataflow/.gitignore | 12 + tools/kcov-dataflow/deep_module/Makefile | 2 + tools/kcov-dataflow/deep_module/deep_chain_mod.c | 224 +++++++++++++++ tools/kcov-dataflow/eight_args_c/Makefile | 3 + tools/kcov-dataflow/eight_args_c/eight_args_mod.c | 95 +++++++ tools/kcov-dataflow/eight_args_rust/Makefile | 2 + .../eight_args_rust/eight_args_rust.rs | 114 ++++++++ tools/kcov-dataflow/kcov-view.py | 272 ++++++++++++++++++ tools/kcov-dataflow/trigger.c | 125 +++++++++ 17 files changed, 1506 insertions(+)
Introduces a new KCOV exetened feature that captures function arguments and
return values at kernel function boundaries, enabling per-process visibility
into runtime dataflow.
Motivation
==========
Even for highly experienced developers, it is not straightforward to
determine, at any given moment, which specific kernel paths a user
process is executing or how function arguments and return values evolve
during execution. This lack of visibility makes debugging and security
auditing significantly more challenging.
Limitations of existing tools in per-task dataflow extraction:
- ftrace/kprobes provide dynamic tracing at specific probe points
- eBPF enables programmable in-kernel analysis but requires manual
specification of struct layouts rather than automatic extraction
from compiler debug metadata
- perf provides statistical sampling of hardware/software events,
inherently lossy and designed for performance profiling rather
than deterministic data-flow capture
This is NOT a performance tool. The purpose is auditing and contract
verification — confirming that kernel functions receive and return
expected values at runtime.
Real-World Result: Android Binder Vulnerabilities
=================================================
Using kcov-dataflow with CONFIG_KCOV_DATAFLOW_INSTRUMENT_ALL, I
audited the Android binder driver (both C and Rust implementations)
and discovered two exploitable logic bugs:
Bug 1: BINDER_SET_MAX_THREADS accepts 0xFFFFFFFF without validation.
kcov-dataflow showed: set_max_threads(max=0xffffffff) → return 0
Impact: bypasses RLIMIT_NPROC, OOM from unprivileged userspace.
Bug 2: BC_ENTER_LOOPER accepted twice without error.
kcov-dataflow showed: looper_enter() called with ENTERED already set,
no rejection, return=0 on both calls.
Impact: thread pool state corruption.
These bugs are invisible to:
- KASAN: no memory corruption occurs
- Edge coverage: same code paths for valid/invalid values
- ftrace: shows "function called" but not argument values
Only by capturing the actual runtime values at function boundaries
could I detect that 0xFFFFFFFF passes through without rejection, or
that the same state-mutating command succeeds twice.
The fixes are submitted separately:
[PATCH 1/4] binder: cap BINDER_SET_MAX_THREADS at RLIMIT_NPROC
[PATCH 2/4] binder: reject duplicate BC_ENTER_LOOPER commands
[PATCH 3/4] rust_binder: cap set_max_threads at RLIMIT_NPROC
[PATCH 4/4] rust_binder: reject duplicate BC_ENTER_LOOPER in looper_enter
Approach
========
Rather than tracing individual probe points, this patch set enables
continuous per-task extraction of data flow across all instrumented
function boundaries — capturing how argument values enter and return
values exit each function as execution progresses through a subsystem.
The key insight is that function boundaries are natural observation
points: arguments at entry reveal what data enters a subsystem, and
return values reveal what comes out.
The compiler (clang with a SanitizerCoverage extension) inserts
callbacks at function entry/exit that record argument values into a
per-task mmap'd buffer. The kernel backend reads struct fields safely
via copy_from_kernel_nofault(). When not enabled for a task, the
overhead is a single boolean check per instrumented function.
Design
======
- Completely independent from legacy /sys/kernel/debug/kcov
- Separate device: /sys/kernel/debug/kcov_dataflow
- Separate ioctl namespace ('d'), separate per-task buffer
- Per-module opt-in: KCOV_DATAFLOW_file.o := y
- Optional global enablement: CONFIG_KCOV_DATAFLOW_INSTRUMENT_ALL
- Supports both C and Rust kernel modules
- Safe in process context; rejects interrupt/NMI via in_task() guard
- Recursion guard via sequence counter bit 31
- Requires clang with -fsanitize-coverage=dataflow-args,dataflow-ret
(Kconfig uses cc-option to verify compiler support)
Performance Note
================
This feature is designed for auditing and security analysis, NOT for
production use or performance measurement. It should not be compared
to runtime tracing tools optimized for low overhead.
Per-module instrumentation (recording active):
~27ns per callback (dominated by LOCK XADD + copy_from_kernel_nofault)
Global instrumentation (INSTRUMENT_ALL, recording disabled):
.text: +9.5%, .data: +44%, boot: +71%, syscall latency: +133%
CONFIG_KCOV_DATAFLOW_INSTRUMENT_ALL instruments every function in the
kernel. This incurs significant overhead comparable to KMSAN and is
intended exclusively for:
- Fuzzer-driven whole-kernel auditing (syzkaller integration)
- Full-subsystem contract verification (as demonstrated with binder)
- Capturing complete call-flow data for post-mortem analysis
For targeted auditing, use per-module opt-in (KCOV_DATAFLOW_file.o := y)
which limits overhead to the specific subsystem under investigation.
Patches
=======
1/6: Core kernel implementation (kernel/kcov.c, sched.h, Kconfig)
2/6: Build system support (Makefile.kcov, Makefile.lib)
3/6: CONFIG_KCOV_DATAFLOW_INSTRUMENT_ALL and NO_INLINE
4/6: Userspace tools and test modules
5/6: Harden kcov_df_write() against interrupt reentry
6/6: Recursion guard and documentation
Prerequisites / Toolchain
=========================
This kernel patch relies on a custom LLVM SanitizerCoverage pass that
emits __sanitizer_cov_trace_args() and __sanitizer_cov_trace_ret()
callbacks at function boundaries, extracting struct field layouts from
DWARF debug metadata at compile time.
To build and test this patchset, compile the kernel using the modified
toolchain:
1. LLVM/Clang (adds -fsanitize-coverage=dataflow-args,dataflow-ret):
https://github.com/llvm/llvm-project/pull/201410
2. Rust (rustc 1.98 built against the above LLVM 23, for Rust module support):
https://github.com/yskzalloc/rust
Build instructions:
# Build the modified clang
cd llvm-project && cmake -G Ninja -S llvm -B build \
-DLLVM_ENABLE_PROJECTS="clang;lld" -DCMAKE_BUILD_TYPE=Release
ninja -C build clang
# Build the kernel with dataflow support
export PATH=$HOME/llvm-project/build/bin:$PATH
export RUSTC=$HOME/rust/build/x86_64-unknown-linux-gnu/stage1/bin/rustc
export RUST_LIB_SRC=$HOME/rust/library
make LLVM=1 defconfig
scripts/config --enable KCOV \
--enable KCOV_DATAFLOW_ARGS \
--enable KCOV_DATAFLOW_RET
make LLVM=1 olddefconfig
make LLVM=1 -j$(nproc)
Note: CONFIG_KCOV_DATAFLOW_ARGS and CONFIG_KCOV_DATAFLOW_RET depend on
CONFIG_KCOV and use $(cc-option) to verify the compiler supports the
new flags. With standard (unpatched) clang, these options will not
appear in menuconfig and silently remain disabled.
Optional configs:
--enable KCOV_DATAFLOW_INSTRUMENT_ALL (instrument entire kernel)
--enable KCOV_DATAFLOW_NO_INLINE (enabled by default)
--set-val FRAME_WARN 4096 (needed for INSTRUMENT_ALL)
--disable KASAN (conflicts with INSTRUMENT_ALL)
Testing
=======
Tested on linux-next 7.1.0-rc5 with custom clang/LLVM 23 and
rustc 1.98-nightly (built against the same LLVM). Verified under
virtme-ng (QEMU/KVM, 1GB RAM):
- Per-module C (eight_args_mod): all 8 functions captured
- Per-module C (deep_chain_mod): 10-deep call chain captured
- Per-module Rust (eight_args_rust): all 8 rfunc functions captured
- Interrupt safety: in_task() + recursion guard prevents corruption
- Binder auditing: discovered 2 exploitable bugs (patches separate)
- Standard clang (without patch): Kconfig options correctly hidden
- Global enablement (INSTRUMENT_ALL): kernel boots, 104K records
captured during single copy.fail exploit reproduction
https://github.com/yskzalloc/kcov-dataflow/blob/main/copy.fail/origin/converted.txt
Signed-off-by: Yunseong Kim <yunseong.kim@est.tech>
---
Changes in v2:
- EDITME: describe what is new in this series revision.
- EDITME: use bulletpoints and terse descriptions.
- Link to v1: https://patch.msgid.link/20260603-kcov-dataflow-next-20260603-v1-0-e64300bf17cf@est.tech
To: Ingo Molnar <mingo@redhat.com>
To: Peter Zijlstra <peterz@infradead.org>
To: Juri Lelli <juri.lelli@redhat.com>
To: Vincent Guittot <vincent.guittot@linaro.org>
To: Dietmar Eggemann <dietmar.eggemann@arm.com>
To: Steven Rostedt <rostedt@goodmis.org>
To: Ben Segall <bsegall@google.com>
To: Mel Gorman <mgorman@suse.de>
To: Valentin Schneider <vschneid@redhat.com>
To: K Prateek Nayak <kprateek.nayak@amd.com>
To: Dmitry Vyukov <dvyukov@google.com>
To: Andrey Konovalov <andreyknvl@gmail.com>
To: Andrew Morton <akpm@linux-foundation.org>
To: Nathan Chancellor <nathan@kernel.org>
To: Nick Desaulniers <nick.desaulniers+lkml@gmail.com>
To: Bill Wendling <morbo@google.com>
To: Justin Stitt <justinstitt@google.com>
To: Nicolas Schier <nsc@kernel.org>
To: Miguel Ojeda <ojeda@kernel.org>
To: Boqun Feng <boqun@kernel.org>
To: Gary Guo <gary@garyguo.net>
To: Björn Roy Baron <bjorn3_gh@protonmail.com>
To: Benno Lossin <lossin@kernel.org>
To: Andreas Hindborg <a.hindborg@kernel.org>
To: Alice Ryhl <aliceryhl@google.com>
To: Trevor Gross <tmgross@umich.edu>
To: Danilo Krummrich <dakr@kernel.org>
To: Jonathan Corbet <corbet@lwn.net>
To: Shuah Khan <skhan@linuxfoundation.org>
Cc: linux-kernel@vger.kernel.org
Cc: kasan-dev@googlegroups.com
Cc: llvm@lists.linux.dev
Cc: linux-kbuild@vger.kernel.org
Cc: rust-for-linux@vger.kernel.org
Cc: workflows@vger.kernel.org
Cc: linux-doc@vger.kernel.org
---
Yunseong Kim (6):
kcov: add per-task dataflow tracking for function arguments/return values
kcov: add build system support for dataflow instrumentation
kcov: add CONFIG_KCOV_DATAFLOW_INSTRUMENT_ALL and NO_INLINE
tools/kcov-dataflow: add userspace consumer and test modules
kcov: add interrupt context guard to kcov_df_write()
kcov: add recursion guard and documentation for kcov-dataflow
Documentation/dev-tools/kcov-dataflow.rst | 282 +++++++++++++++++++
include/linux/sched.h | 8 +
kernel/Makefile | 3 +
kernel/kcov.c | 307 +++++++++++++++++++++
lib/Kconfig.debug | 43 +++
rust/Makefile | 1 +
scripts/Makefile.kcov | 6 +
scripts/Makefile.lib | 7 +
tools/kcov-dataflow/.gitignore | 12 +
tools/kcov-dataflow/deep_module/Makefile | 2 +
tools/kcov-dataflow/deep_module/deep_chain_mod.c | 224 +++++++++++++++
tools/kcov-dataflow/eight_args_c/Makefile | 3 +
tools/kcov-dataflow/eight_args_c/eight_args_mod.c | 95 +++++++
tools/kcov-dataflow/eight_args_rust/Makefile | 2 +
.../eight_args_rust/eight_args_rust.rs | 114 ++++++++
tools/kcov-dataflow/kcov-view.py | 272 ++++++++++++++++++
tools/kcov-dataflow/trigger.c | 125 +++++++++
17 files changed, 1506 insertions(+)
---
base-commit: f7af91adc230aa99e23330ecf85bc9badd9780ad
change-id: 20260603-kcov-dataflow-next-20260603-8bf628f98086
Best regards,
--
Yunseong Kim <yunseong.kim@est.tech>
On Wed, Jun 3, 2026 at 7:43 PM Yunseong Kim <yunseong.kim@est.tech> wrote: > > Introduces a new KCOV exetened feature that captures function arguments and > return values at kernel function boundaries, enabling per-process visibility > into runtime dataflow. Some high-level comments: - Make sure your code can run on every platform supported by kcov (namely ARM64) - Check out Sashiko findings: https://sashiko.dev/#/patchset/20260603-kcov-dataflow-next-20260603-v2-0-fee0939de2c4%40est.tech, at least some of them seem to make sense - Please consolidate changes to the same file into a single patch - There seem to be two tools (one in C and one in Python) with overlapping functionality, can you keep only one? - The test modules seem to be used only in manual testing. Can you convert them to kselftests or remove them? - At this point, long dashes in the kernel codebase are quite rare, and I don't see a reason to add more.
Hi,
I would like to make a correction to my cover letter.
Note: v1 was sent as "RFC v2" due to my b4 misconfiguration.
On 6/3/26 19:43, Yunseong Kim wrote:
> Introduces a new KCOV exetened feature that captures function arguments and
> return values at kernel function boundaries, enabling per-process visibility
> into runtime dataflow.
>
> [snip...]
kcov-dataflow are not in conflict with "KASAN_GENERIC + KCOV_DATAFLOW_INSTRUMENT_ALL"
> Prerequisites / Toolchain
> =========================
>
> This kernel patch relies on a custom LLVM SanitizerCoverage pass that
> emits __sanitizer_cov_trace_args() and __sanitizer_cov_trace_ret()
> callbacks at function boundaries, extracting struct field layouts from
> DWARF debug metadata at compile time.
>
> To build and test this patchset, compile the kernel using the modified
> toolchain:
>
> 1. LLVM/Clang (adds -fsanitize-coverage=dataflow-args,dataflow-ret):
> https://github.com/llvm/llvm-project/pull/201410
>
> 2. Rust (rustc 1.98 built against the above LLVM 23, for Rust module support):
> https://github.com/yskzalloc/rust
>
> Build instructions:
>
> # Build the modified clang
> cd llvm-project && cmake -G Ninja -S llvm -B build \
> -DLLVM_ENABLE_PROJECTS="clang;lld" -DCMAKE_BUILD_TYPE=Release
> ninja -C build clang
>
> # Build the kernel with dataflow support
> export PATH=$HOME/llvm-project/build/bin:$PATH
> export RUSTC=$HOME/rust/build/x86_64-unknown-linux-gnu/stage1/bin/rustc
> export RUST_LIB_SRC=$HOME/rust/library
>
> make LLVM=1 defconfig
> scripts/config --enable KCOV \
> --enable KCOV_DATAFLOW_ARGS \
> --enable KCOV_DATAFLOW_RET
> make LLVM=1 olddefconfig
> make LLVM=1 -j$(nproc)
>
> Note: CONFIG_KCOV_DATAFLOW_ARGS and CONFIG_KCOV_DATAFLOW_RET depend on
> CONFIG_KCOV and use $(cc-option) to verify the compiler supports the
> new flags. With standard (unpatched) clang, these options will not
> appear in menuconfig and silently remain disabled.
>
> Optional configs:
> --enable KCOV_DATAFLOW_INSTRUMENT_ALL (instrument entire kernel)
> --enable KCOV_DATAFLOW_NO_INLINE (enabled by default)
I checked, and it’s better to use CONFIG_FRAME_WARN=0 when using KASAN
together regarding flood of -Wframe-larger-than warnings.
> --set-val FRAME_WARN 4096 (needed for INSTRUMENT_ALL)
> --disable KASAN (conflicts with INSTRUMENT_ALL) --enable KASAN (no conflicts; works well, but slower, a lot of dataflow)
What I meant by that conflicts is that I was using it for kernel-space
behavior only for the "offensive kernel vulnerability auditing",
and in that case, sometime it’s better to disable KASAN.
Tested CONFIG_KASAN=y + CONFIG_KCOV_DATAFLOW_INSTRUMENT_ALL=y together
(FRAME_WARN=4096, virtme-ng 1 vCPU 1GB on my Intel CPU Dell Pro 14
Premium PA14250):
- Builds clean, no frame size warnings
- Boots and runs without KASAN errors
- kcov-dataflow captures correctly under KASAN:
getpid(): 6,682 records
open+close: 16,382 records (filled 64K buffer)
Time to "Freeing unused kernel image (initmem)":
KCOV_DATAFLOW_INSTRUMENT_ALL only: ~0.95s (+40%)
KASAN + KCOV_DATAFLOW_INSTRUMENT_ALL: ~2.02s (+197%)
Sorry for any confusion the earlier wording may have caused.
Thank you!
Kind regards,
Yunseong
On Wed, Jun 03, 2026 at 07:43:27PM +0200, Yunseong Kim wrote: > CONFIG_KCOV_DATAFLOW_INSTRUMENT_ALL instruments every function in the > kernel. Well, I would hope it would very much not instrument noinstr functions.
© 2016 - 2026 Red Hat, Inc.