[RFC PATCH v2 13/14] selftests/kcov_dataflow: add binderfs ioctl capture test

1 view
Skip to first unread message

Yunseong Kim

unread,
Jun 11, 2026, 12:22:03 PMJun 11
to Ingo Molnar, Peter Zijlstra, Juri Lelli, Vincent Guittot, Dietmar Eggemann, Steven Rostedt, Ben Segall, Mel Gorman, Valentin Schneider, K Prateek Nayak, Andrey Konovalov, Alexander Potapenko, Dmitry Vyukov, Andrew Morton, Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross, Danilo Krummrich, Nathan Chancellor, Nicolas Schier, Nick Desaulniers, Bill Wendling, Justin Stitt, Kees Cook, David Hildenbrand, Lorenzo Stoakes, Liam R. Howlett, Vlastimil Babka, Mike Rapoport, Suren Baghdasaryan, Michal Hocko, Shuah Khan, Jonathan Corbet, Shuah Khan, Yunseong Kim, linux-...@vger.kernel.org, kasa...@googlegroups.com, rust-fo...@vger.kernel.org, linux-...@vger.kernel.org, ll...@lists.linux.dev, linu...@kvack.org, linux-k...@vger.kernel.org, work...@vger.kernel.org, linu...@vger.kernel.org, Yeoreum Yun
Exercise the binder driver via binderfs with kcov_dataflow recording
active. Verifies that function argument records are captured at binder
ioctl boundaries (BINDER_VERSION, BINDER_SET_MAX_THREADS).

Requires CONFIG_ANDROID_BINDER_IPC=y and CONFIG_ANDROID_BINDERFS=y.
Gracefully skips if binderfs is not available.

Build and test:

export PATH=$PWD/../llvm-project/build/bin:$PATH
vng --build \
--configitem CONFIG_KCOV=y \
--configitem CONFIG_KCOV_DATAFLOW_ARGS=y \
--configitem CONFIG_KCOV_DATAFLOW_RET=y \
--configitem CONFIG_KCOV_DATAFLOW_INSTRUMENT_ALL=y \
--configitem CONFIG_DEBUG_INFO=y \
--configitem CONFIG_ANDROID_BINDER_IPC=y \
--configitem CONFIG_ANDROID_BINDERFS=y \
LLVM=1 CC=clang

make -C tools/testing/selftests/kcov_dataflow/binderfs
vng --user root --exec \
tools/testing/selftests/kcov_dataflow/binderfs/binderfs_test

Result:

TAP version 13
1..3
ok 1 kcov_dataflow.binderfs_setup
ok 2 kcov_dataflow.binderfs_captured # 636 words
ok 3 kcov_dataflow.binderfs_valid_records
# Totals: pass:3 fail:0 skip:0
#
# Captured call records:
# ENTRY pc=0xffffffff... arg=0x4 (fd)
# ENTRY pc=0xffffffff... arg=0xc0046209 (BINDER_VERSION)
# ENTRY pc=0xffffffff... arg=0x0 (binder_get_thread)
# RET pc=0xffffffff... ret=0x0 (success)
# ENTRY pc=0xffffffff... arg=0x40046205 (SET_MAX_THREADS)
# ENTRY pc=0xffffffff... arg=0x4 (_copy_from_user size)

Cc: Alexander Potapenko <gli...@google.com>
Assisted-by: Claude:claude-opus-4-6 [kiro-chat]
Signed-off-by: Yunseong Kim <yunseo...@est.tech>
---
tools/testing/selftests/kcov_dataflow/.gitignore | 1 +
.../selftests/kcov_dataflow/binderfs/Makefile | 4 +
.../kcov_dataflow/binderfs/binderfs_test.c | 177 +++++++++++++++++++++
.../selftests/kcov_dataflow/run_binderfs.sh | 13 ++
4 files changed, 195 insertions(+)

diff --git a/tools/testing/selftests/kcov_dataflow/.gitignore b/tools/testing/selftests/kcov_dataflow/.gitignore
index f71fc89580f8..da4c189ad3be 100644
--- a/tools/testing/selftests/kcov_dataflow/.gitignore
+++ b/tools/testing/selftests/kcov_dataflow/.gitignore
@@ -1,5 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
user_ioctl/user_ioctl
+binderfs/binderfs_test
*.o
*.ko
*.mod
diff --git a/tools/testing/selftests/kcov_dataflow/binderfs/Makefile b/tools/testing/selftests/kcov_dataflow/binderfs/Makefile
new file mode 100644
index 000000000000..9f1588512dba
--- /dev/null
+++ b/tools/testing/selftests/kcov_dataflow/binderfs/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
+TEST_GEN_PROGS := binderfs_test
+CFLAGS += -Wall -O2
+include ../../lib.mk
diff --git a/tools/testing/selftests/kcov_dataflow/binderfs/binderfs_test.c b/tools/testing/selftests/kcov_dataflow/binderfs/binderfs_test.c
new file mode 100644
index 000000000000..ce9b49aa0b9f
--- /dev/null
+++ b/tools/testing/selftests/kcov_dataflow/binderfs/binderfs_test.c
@@ -0,0 +1,177 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * binderfs selftest for kcov_dataflow
+ *
+ * Exercises the binder driver via binderfs with kcov_dataflow recording
+ * active, then verifies that function argument records were captured at
+ * binder ioctl boundaries.
+ *
+ * Requires: CONFIG_ANDROID_BINDER_IPC=y (or _RUST), CONFIG_ANDROID_BINDERFS=y
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <linux/android/binder.h>
+#include <linux/android/binderfs.h>
+
+#define KCOV_DF_INIT_TRACK _IOR('d', 1, unsigned long)
+#define KCOV_DF_ENABLE _IO('d', 100)
+#define KCOV_DF_DISABLE _IO('d', 101)
+
+#define BUF_SIZE (1 << 20)
+#define BINDERFS_PATH "/tmp/binderfs_test"
+#define BINDER_DEV BINDERFS_PATH "/my_binder"
+
+static int setup_binderfs(void)
+{
+ struct binderfs_device dev = {};
+
+ mkdir(BINDERFS_PATH, 0755);
+
+ if (mount("binder", BINDERFS_PATH, "binder", 0, NULL)) {
+ if (errno == ENODEV || errno == ENOENT) {
+ printf("SKIP: binderfs not available\n");
+ return -1;
+ }
+ perror("mount binderfs");
+ return -1;
+ }
+
+ /* Create a binder device via BINDER_CTL_ADD ioctl */
+ int ctl_fd;
+
+ ctl_fd = open(BINDERFS_PATH "/binder-control", O_RDONLY);
+ if (ctl_fd < 0) {
+ perror("open binder-control");
+ umount(BINDERFS_PATH);
+ return -1;
+ }
+
+ strcpy(dev.name, "my_binder");
+ if (ioctl(ctl_fd, BINDER_CTL_ADD, &dev) && errno != EEXIST) {
+ perror("BINDER_CTL_ADD");
+ close(ctl_fd);
+ umount(BINDERFS_PATH);
+ return -1;
+ }
+ close(ctl_fd);
+ return 0;
+}
+
+static void cleanup_binderfs(void)
+{
+ umount(BINDERFS_PATH);
+ rmdir(BINDERFS_PATH);
+}
+
+int main(void)
+{
+ uint64_t *buf;
+ int df_fd, binder_fd;
+ uint64_t total;
+ int valid = 0;
+
+ printf("TAP version 13\n");
+ printf("1..3\n");
+
+ /* Setup binderfs */
+ if (setup_binderfs()) {
+ printf("ok 1 # SKIP binderfs not available\n");
+ printf("ok 2 # SKIP\n");
+ printf("ok 3 # SKIP\n");
+ return 0;
+ }
+
+ /* Open kcov_dataflow */
+ df_fd = open("/sys/kernel/debug/kcov_dataflow", O_RDWR);
+ if (df_fd < 0) {
+ printf("not ok 1 cannot open kcov_dataflow\n");
+ cleanup_binderfs();
+ return 1;
+ }
+
+ if (ioctl(df_fd, KCOV_DF_INIT_TRACK, BUF_SIZE)) {
+ printf("not ok 1 INIT_TRACK failed\n");
+ close(df_fd);
+ cleanup_binderfs();
+ return 1;
+ }
+
+ buf = mmap(NULL, BUF_SIZE * sizeof(uint64_t),
+ PROT_READ | PROT_WRITE, MAP_SHARED, df_fd, 0);
+ if (buf == MAP_FAILED) {
+ printf("not ok 1 mmap failed\n");
+ close(df_fd);
+ cleanup_binderfs();
+ return 1;
+ }
+
+ printf("ok 1 kcov_dataflow.binderfs_setup\n");
+
+ /* Open binder device */
+ binder_fd = open(BINDER_DEV, O_RDWR | O_CLOEXEC);
+ if (binder_fd < 0) {
+ printf("not ok 2 cannot open %s: %s\n", BINDER_DEV,
+ strerror(errno));
+ munmap(buf, BUF_SIZE * sizeof(uint64_t));
+ close(df_fd);
+ cleanup_binderfs();
+ return 1;
+ }
+
+ /* Enable recording and exercise binder ioctls */
+ ioctl(df_fd, KCOV_DF_ENABLE, 0);
+ __atomic_store_n(&buf[0], 0, __ATOMIC_RELAXED);
+
+ /* BINDER_VERSION - simple ioctl that exercises the binder path */
+ struct binder_version ver = {};
+
+ ioctl(binder_fd, BINDER_VERSION, &ver);
+
+ /* BINDER_SET_MAX_THREADS */
+ uint32_t max_threads = 4;
+
+ ioctl(binder_fd, BINDER_SET_MAX_THREADS, &max_threads);
+
+ ioctl(df_fd, KCOV_DF_DISABLE, 0);
+
+ total = __atomic_load_n(&buf[0], __ATOMIC_RELAXED);
+ close(binder_fd);
+
+ if (total > 0)
+ printf("ok 2 kcov_dataflow.binderfs_captured # %lu words\n",
+ (unsigned long)total);
+ else
+ printf("not ok 2 kcov_dataflow.binderfs_captured # 0 words\n");
+
+ /* Verify at least one record has valid header (type 0xE or 0xF) */
+
+ if (total > 3) {
+ uint64_t hdr = buf[1];
+ uint32_t type = (hdr >> 28) & 0xF;
+
+ if (type == 0xE || type == 0xF)
+ valid = 1;
+ }
+
+ if (valid)
+ printf("ok 3 kcov_dataflow.binderfs_valid_records\n");
+ else
+ printf("not ok 3 kcov_dataflow.binderfs_valid_records\n");
+
+ printf("# Totals: pass:%d fail:%d skip:0\n",
+ valid ? 3 : 2, valid ? 0 : 1);
+
+ munmap(buf, BUF_SIZE * sizeof(uint64_t));
+ close(df_fd);
+ cleanup_binderfs();
+ return valid ? 0 : 1;
+}
diff --git a/tools/testing/selftests/kcov_dataflow/run_binderfs.sh b/tools/testing/selftests/kcov_dataflow/run_binderfs.sh
new file mode 100755
index 000000000000..5376e5350061
--- /dev/null
+++ b/tools/testing/selftests/kcov_dataflow/run_binderfs.sh
@@ -0,0 +1,13 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Test binderfs ioctl capture via kcov_dataflow
+DIR="$(dirname "$0")"
+BIN="$DIR/binderfs/binderfs_test"
+
+if [ ! -f "$BIN" ]; then
+ echo "SKIP: $BIN not found"
+ echo "Build: make -C tools/testing/selftests/kcov_dataflow/binderfs"
+ exit 4
+fi
+
+exec "$BIN"

--
2.43.0

Reply all
Reply to author
Forward
0 new messages