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

Yunseong Kim <[email protected]>
Newsgroups org.kernel.vger.linux-kbuild,dev.linux.lists.llvm,org.kernel.vger.linux-doc,org.kernel.vger.linux-kernel,org.kernel.vger.linux-kselftest,org.kernel.vger.rust-for-linux,org.kernel.vger.workflows,org.kvack.linux-mm
Message-ID <[email protected]>
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 <[email protected]>
Assisted-by: Claude:claude-opus-4-6 [kiro-chat]
Signed-off-by: Yunseong Kim <[email protected]>
---
 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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.