Rust module exercising 1-8 argument functions plus struct pointer. Verifies register-passed (1-6) and stack-passed (7-8) arguments. Test: make LLVM=1 CC=clang RUSTC=$RUSTC RUST_LIB_SRC=$RUST_LIB_SRC \ M=tools/testing/selftests/kcov_dataflow/eight_args_rust modules vng --user root --exec \ "python3 tools/testing/selftests/kcov_dataflow/trigger-view.py \ eight_args_rust -C 8 --ko \ tools/testing/selftests/kcov_dataflow/eight_args_rust/eight_args_rust.ko" Result: ksys_write(0x0, 0x1) fdget_pos(0x4) 0xffff891481d2bc00 = fdget_pos() 0x0 = vfs_write() vfs_write(0x0, 0x1, 0x0) 0x0 = _RNvCs3p16QzTwthP_15eight_args_rust13write_handler [eight_args_rust]() _RNvCs3p16QzTwthP_15eight_args_rust13write_handler [eight_args_rust](0x0, 0x1, 0x0) rdf_func2 [eight_args_rust](0x11, 0x22) 0x33 = rdf_func2 [eight_args_rust]() rdf_func3 [eight_args_rust](0x11, 0x22, 0x33) 0x66 = rdf_func3 [eight_args_rust]() rdf_func4 [eight_args_rust](0x11, 0x22, 0x33, 0x44) 0xaa = rdf_func4 [eight_args_rust]() rdf_func5 [eight_args_rust](0x11, 0x22, 0x33, 0x44, 0x55) 0xff = rdf_func5 [eight_args_rust]() rdf_func6 [eight_args_rust](0x11, 0x22, 0x33, 0x44, 0x55, 0x66) 0x165 = rdf_func6 [eight_args_rust]() rdf_func7 [eight_args_rust](0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77) 0x1dc = rdf_func7 [eight_args_rust]() rdf_func8 [eight_args_rust](0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88) 0x264 = rdf_func8 [eight_args_rust]() rdf_func_struct [eight_args_rust](0xaaaa) 0x16665 = rdf_func_struct [eight_args_rust]() 0x1 = _RNvCs3p16QzTwthP_15eight_args_rust13write_handler [eight_args_rust]() 0x1 = vfs_write() 0x1 = ksys_write() 0x1 = __x64_sys_write() 0x0 = fpregs_assert_state_consistent() 0xba5748 = __x64_sys_close() file_close_fd(0x4) 0x0 = file_close_fd() 0x0 = filp_flush() Cc: Alexander Potapenko Assisted-by: Claude:claude-opus-4-6 [kiro-chat] Link: https://github.com/yskzalloc/kcov-dataflow/actions Signed-off-by: Yunseong Kim --- tools/testing/selftests/kcov_dataflow/README.rst | 7 + .../kcov_dataflow/eight_args_rust/Makefile | 3 + .../eight_args_rust/eight_args_rust.rs | 143 +++++++++++++++++++++ .../selftests/kcov_dataflow/run_eight_args_rust.sh | 35 +++++ 4 files changed, 188 insertions(+) diff --git a/tools/testing/selftests/kcov_dataflow/README.rst b/tools/testing/selftests/kcov_dataflow/README.rst index e93b4e573504..61a41f3bd596 100644 --- a/tools/testing/selftests/kcov_dataflow/README.rst +++ b/tools/testing/selftests/kcov_dataflow/README.rst @@ -41,3 +41,10 @@ eight_args_c/ make LLVM=1 CC=clang M=tools/testing/selftests/kcov_dataflow/eight_args_c modules python3 trigger-view.py eight_args_c + +eight_args_rust/ + Rust equivalent of eight_args_c. Captures arguments at -O2 where + drgn/vmcore cannot. Requires CONFIG_RUST:: + + make LLVM=1 CC=clang M=tools/testing/selftests/kcov_dataflow/eight_args_rust modules + python3 trigger-view.py eight_args_rust diff --git a/tools/testing/selftests/kcov_dataflow/eight_args_rust/Makefile b/tools/testing/selftests/kcov_dataflow/eight_args_rust/Makefile new file mode 100644 index 000000000000..c1e9ea2c5622 --- /dev/null +++ b/tools/testing/selftests/kcov_dataflow/eight_args_rust/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-m := eight_args_rust.o +KCOV_DATAFLOW_eight_args_rust.o := y diff --git a/tools/testing/selftests/kcov_dataflow/eight_args_rust/eight_args_rust.rs b/tools/testing/selftests/kcov_dataflow/eight_args_rust/eight_args_rust.rs new file mode 100644 index 000000000000..3026265cda97 --- /dev/null +++ b/tools/testing/selftests/kcov_dataflow/eight_args_rust/eight_args_rust.rs @@ -0,0 +1,143 @@ +// SPDX-License-Identifier: GPL-2.0 +//! Verify kcov_dataflow captures 1-8 argument Rust functions at -O2. +//! +//! This is the Rust equivalent of eight_args_c. Since rustc elides DWARF +//! variable locations at -O2, drgn/vmcore cannot observe these arguments. +//! kcov_dataflow captures them via the post-compilation pipeline. +//! +//! Write to /sys/kernel/debug/kcov_dataflow_test/trigger_rust to invoke. + +#![allow(missing_docs)] + +use kernel::prelude::*; +use kernel::c_str; + +module! { + type: EightArgsRust, + name: "eight_args_rust", + authors: ["kcov-dataflow"], + description: "1-8 arg Rust verification for kcov_dataflow", + license: "GPL", +} + +#[repr(C)] +pub struct Pair { + pub x: u32, + pub y: u32, +} + +#[no_mangle] +#[inline(never)] +pub extern "C" fn rdf_func1(a1: u64) -> u64 { a1 } + +#[no_mangle] +#[inline(never)] +pub extern "C" fn rdf_func2(a1: u64, a2: u64) -> u64 { a1 + a2 } + +#[no_mangle] +#[inline(never)] +pub extern "C" fn rdf_func3(a1: u64, a2: u64, a3: u64) -> u64 { + a1 + a2 + a3 +} + +#[no_mangle] +#[inline(never)] +pub extern "C" fn rdf_func4(a1: u64, a2: u64, a3: u64, a4: u64) -> u64 { + a1 + a2 + a3 + a4 +} + +#[no_mangle] +#[inline(never)] +pub extern "C" fn rdf_func5(a1: u64, a2: u64, a3: u64, a4: u64, a5: u64) -> u64 { + a1 + a2 + a3 + a4 + a5 +} + +#[no_mangle] +#[inline(never)] +pub extern "C" fn rdf_func6( + a1: u64, a2: u64, a3: u64, a4: u64, a5: u64, a6: u64, +) -> u64 { + a1 + a2 + a3 + a4 + a5 + a6 +} + +#[no_mangle] +#[inline(never)] +pub extern "C" fn rdf_func7( + a1: u64, a2: u64, a3: u64, a4: u64, a5: u64, a6: u64, a7: u64, +) -> u64 { + a1 + a2 + a3 + a4 + a5 + a6 + a7 +} + +#[no_mangle] +#[inline(never)] +pub extern "C" fn rdf_func8( + a1: u64, a2: u64, a3: u64, a4: u64, a5: u64, a6: u64, a7: u64, a8: u64, +) -> u64 { + a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 +} + +#[no_mangle] +#[inline(never)] +pub extern "C" fn rdf_func_struct(p: *const Pair) -> u64 { + unsafe { (*p).x as u64 + (*p).y as u64 } +} + +unsafe extern "C" fn write_handler( + _file: *mut kernel::bindings::file, + _buf: *const core::ffi::c_char, + count: usize, + _ppos: *mut kernel::bindings::loff_t, +) -> kernel::ffi::c_long { + let p = Pair { x: 0xAAAA, y: 0xBBBB }; + + let mut sum: u64 = 0; + sum = sum.wrapping_add(rdf_func1(0x11)); + sum = sum.wrapping_add(rdf_func2(0x11, 0x22)); + sum = sum.wrapping_add(rdf_func3(0x11, 0x22, 0x33)); + sum = sum.wrapping_add(rdf_func4(0x11, 0x22, 0x33, 0x44)); + sum = sum.wrapping_add(rdf_func5(0x11, 0x22, 0x33, 0x44, 0x55)); + sum = sum.wrapping_add(rdf_func6(0x11, 0x22, 0x33, 0x44, 0x55, 0x66)); + sum = sum.wrapping_add(rdf_func7(0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77)); + sum = sum.wrapping_add(rdf_func8(0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88)); + sum = sum.wrapping_add(rdf_func_struct(&p as *const Pair)); + core::hint::black_box(sum); + + count as kernel::ffi::c_long +} + +#[repr(transparent)] +struct SyncFops(kernel::bindings::file_operations); +unsafe impl Sync for SyncFops {} + +static FOPS: SyncFops = SyncFops(kernel::bindings::file_operations { + write: Some(unsafe { core::mem::transmute(write_handler as *const ()) }), + ..unsafe { core::mem::zeroed() } +}); + +struct EightArgsRust { + d: *mut kernel::bindings::dentry, +} + +impl kernel::Module for EightArgsRust { + fn init(_module: &'static ThisModule) -> Result { + let d = unsafe { + kernel::bindings::debugfs_create_file_unsafe( + c_str!("trigger_rust").as_char_ptr(), + 0o222, + core::ptr::null_mut(), + core::ptr::null_mut(), + &FOPS.0, + ) + }; + Ok(Self { d }) + } +} + +impl Drop for EightArgsRust { + fn drop(&mut self) { + unsafe { kernel::bindings::debugfs_remove(self.d) }; + } +} + +unsafe impl Send for EightArgsRust {} +unsafe impl Sync for EightArgsRust {} diff --git a/tools/testing/selftests/kcov_dataflow/run_eight_args_rust.sh b/tools/testing/selftests/kcov_dataflow/run_eight_args_rust.sh new file mode 100755 index 000000000000..c5f11866e19d --- /dev/null +++ b/tools/testing/selftests/kcov_dataflow/run_eight_args_rust.sh @@ -0,0 +1,35 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +# Test eight_args_rust module capture via kcov_dataflow +DIR="$(dirname "$0")" +KO="$DIR/eight_args_rust/eight_args_rust.ko" + +if [ ! -f "$KO" ]; then + echo "SKIP: $KO not found" + echo "Build: make LLVM=1 CC=clang RUSTC=\$RUSTC M=...eight_args_rust modules"" + exit 4 # kselftest SKIP +fi + +if [ ! -e /sys/kernel/debug/kcov_dataflow ]; then + echo "SKIP: kcov_dataflow not available" + exit 4 +fi + +OUTPUT=$(python3 "$DIR/trigger-view.py" eight_args_rust --ko "$KO" --raw 2>&1) +RC=$? + +if [ $RC -ne 0 ]; then + echo "FAIL: trigger-and-view exited with $RC" + echo "$OUTPUT" + exit 1 +fi + +RECORDS=$(echo "$OUTPUT" | grep -c "^\[ENTRY\]\|^\[RET") +if [ "$RECORDS" -gt 0 ]; then + echo "PASS: captured $RECORDS records from eight_args_rust" + exit 0 +else + echo "FAIL: no records captured" + echo "$OUTPUT" + exit 1 +fi -- 2.43.0