Re: [RFC 0/2] arm64: kprobes: Fix single-step fault and reentry handling
From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Date: 2026-07-04 14:47:46
Also in:
linux-arm-kernel, lkml
Hi Pu Hu, Can you update this by rebasing on top of arm64 tree (git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git) and update your signed-off-by with your name. Thank you, On Thu, 2 Jul 2026 10:09:51 +0000 Pu Hu [off-list ref] wrote:
quoted hunk ↗ jump to hunk
On 7/2/2026 6:07 PM, hupu wrote:quoted
On 7/1/2026 9:56 PM, Pu Hu wrote:quoted
On 7/1/2026 9:43 PM, Masami Hiramatsu wrote:quoted
On Wed, 1 Jul 2026 12:14:54 +0000 Pu Hu [off-list ref] wrote:quoted
From: hupu <redacted>... ...>quoted
I will send the complete test case in a follow-up email. Thanks, hupuHi maintainers, As mentioned in my previous email, below is the complete test case I used to reproduce the arm64 kprobe crash on mainline. It contains: - a small kprobe module that probes folio_wait_bit_common() - a userspace program that repeatedly triggers file-backed page faults - a Makefile to build both parts Depending on the local build environment, the following variables in the Makefile may need to be adjusted: CROSS_COMPILE KERN_DIR DEST_PATH Thanks, Pu Hu ---diff --git a/misc/kprobe/Makefile b/misc/kprobe/Makefile new file mode 100755 index 0000000..14c00c0 --- /dev/null +++ b/misc/kprobe/Makefile@@ -0,0 +1,36 @@ +PWD := $(shell pwd) +ARCH ?= arm64 +CROSS_COMPILE ?= aarch64-dumpstack-linux-gnu- +KERN_DIR ?= $(PWD)/../../output/build-mainline +DEST_PATH ?= $(PWD)/../../output +Q := @ + +UNIT_TEST := fault_stress +UNIT_TEST_SRC := fault_stress.c + +KP_MOD := kp_folio +obj-m := $(KP_MOD).o + +USER_CFLAGS := -static -g -O0 -fno-omit-frame-pointer -fasynchronous-unwind-tables +USER_LIBS := -lm -lpthread +EXTRA_CFLAGS += -I$(KERN_DIR) + +.PHONY: all modules user clean + +all: modules user install + +modules: + $(Q)$(MAKE) -C $(KERN_DIR) M=$(PWD)EXTRA_CFLAGS="$(EXTRA_CFLAGS)" ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) modules + +user: + $(Q)$(CROSS_COMPILE)gcc $(USER_CFLAGS) $(UNIT_TEST_SRC) -o $(UNIT_TEST) $(USER_LIBS) + +install: + $(Q)mkdir -p $(DEST_PATH) + $(Q)cp -f *.ko $(DEST_PATH)/ + $(Q)cp -f $(UNIT_TEST) $(DEST_PATH)/ + +clean: + $(Q)$(MAKE) -C $(KERN_DIR) M=$(PWD) clean + $(Q)rm -f $(UNIT_TEST) + $(Q)rm -f $(DEST_PATH)/$(UNIT_TEST) $(DEST_PATH)/*.kodiff --git a/misc/kprobe/fault_stress.c b/misc/kprobe/fault_stress.c new file mode 100755 index 0000000..10150ff --- /dev/null +++ b/misc/kprobe/fault_stress.c@@ -0,0 +1,96 @@ +#define _GNU_SOURCE +#include <fcntl.h> +#include <pthread.h> +#include <sched.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#define FILE_SIZE (256UL * 1024 * 1024) +#define NR_THREADS 8 + +static void deep_call(int n) +{ + volatile char buf[4096]; + + memset((void *)buf, n, sizeof(buf)); + + if (n > 0) + deep_call(n - 1); + else + sched_yield(); +} + +static void *worker(void *arg) +{ + const char *path = arg; + int fd; + char *map; + unsigned long i; + volatile unsigned long sum = 0; + + fd = open(path, O_RDONLY); + if (fd < 0) { + perror("open"); + return NULL; + } + + map = mmap(NULL, FILE_SIZE, PROT_READ, MAP_PRIVATE, fd, 0); + if (map == MAP_FAILED) { + perror("mmap"); + close(fd); + return NULL; + } + + for (;;) { + /* + * Drop the pages backing this mapping from the currentprocess. + * Subsequent accesses are more likely to trigger file-backed + * page faults again. + */ + madvise(map, FILE_SIZE, MADV_DONTNEED); + + for (i = 0; i < FILE_SIZE; i += 4096 * 17) { + sum += map[i]; + deep_call(64); + } + } + + munmap(map, FILE_SIZE); + close(fd); + return NULL; +} + +int main(void) +{ + pthread_t th[NR_THREADS]; + const char *path = "/tmp/fault_stress_file"; + int fd; + int i; + + fd = open(path, O_CREAT | O_RDWR, 0644); + if (fd < 0) { + perror("open file"); + return 1; + } + + if (ftruncate(fd, FILE_SIZE) < 0) { + perror("ftruncate"); + return 1; + } + + close(fd); + + for (i = 0; i < NR_THREADS; i++) + pthread_create(&th[i], NULL, worker, (void *)path); + + for (i = 0; i < NR_THREADS; i++) + pthread_join(th[i], NULL); + + return 0; +} +diff --git a/misc/kprobe/kp_folio.c b/misc/kprobe/kp_folio.c new file mode 100755 index 0000000..c8f3e1d --- /dev/null +++ b/misc/kprobe/kp_folio.c@@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/kprobes.h> +#include <linux/sched.h> +#include <linux/atomic.h> +#include <linux/ratelimit.h> + +static atomic64_t kp_hit_count = ATOMIC64_INIT(0); + +static int folio_wait_bit_common_handler( + struct kprobe *p,^M + struct pt_regs *regs) +{ + unsigned long hit; + + hit = atomic64_inc_return(&kp_hit_count); + + pr_info("kp_folio: hit=%lu comm=%s tgid=%d tid=%d\n", + hit, current->comm, current->tgid, current->pid); + + return 0; +} + +static struct kprobe kp_folio_wait_bit_common = { + .symbol_name = "folio_wait_bit_common", + .pre_handler = folio_wait_bit_common_handler, +}; + +static int __init kp_folio_init(void) +{ + int ret; + + ret = register_kprobe(&kp_folio_wait_bit_common); + if (ret < 0) { + pr_err("kp_folio: register_kprobe failed, ret=%d\n", ret); + return ret; + } + + pr_info("kp_folio: kprobe registered at %pS, addr=%px\n", + kp_folio_wait_bit_common.addr, + kp_folio_wait_bit_common.addr); + + return 0; +} + +static void __exit kp_folio_exit(void) +{ + unregister_kprobe(&kp_folio_wait_bit_common); + + pr_info("kp_folio: kprobe unregistered, total hits=%lld\n", + atomic64_read(&kp_hit_count)); +} + +module_init(kp_folio_init); +module_exit(kp_folio_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("hupu <hupu@transsion.com>"); +MODULE_DESCRIPTION("simple kprobe reproducer for folio_wait_bit_common");
-- Masami Hiramatsu (Google) [off-list ref]