[BUG] general protection fault in path_put

From: Jaeyoung Chung

Date: Mon Aug 24 2026 - 12:13:52 EST


Hello,

We found a "general protection fault in path_put" on Linux v7.2.
The issue was found by our own race fuzzer. We have not analyzed the root cause,
so we do not have a proposed fix to offer.

To reproduce the race reliably, we applied the delay patch below to the
kernel and ran the C reproducer as root inside an x86_64 QEMU guest. The
crash log we observed, the delay patch and the reproducer are all included
below.

The following kernel config options are required to reproduce the issue:
CONFIG_UPROBES=y
CONFIG_UPROBE_EVENTS=y
CONFIG_TRACING=y
CONFIG_DEBUG_FS=y
CONFIG_FAULT_INJECTION=y
CONFIG_FAILSLAB=y
CONFIG_FAULT_INJECTION_DEBUG_FS=y
CONFIG_KASAN=y

We hope this report is useful. Please let us know if any further
information would help.

Reported-by: Eulgyu Kim <eulgyukim@xxxxxxxxx>
Reported-by: Jaeyoung Chung <jjy600901@xxxxxxxxx>

Kernel delay patch:
==================================================================
diff --git a/kernel/trace/trace_uprobe.c b/kernel/trace/trace_uprobe.c
index c274346853d1..203ff3d601dc 100644
--- a/kernel/trace/trace_uprobe.c
+++ b/kernel/trace/trace_uprobe.c
@@ -10,6 +10,7 @@
#include <linux/bpf-cgroup.h>
#include <linux/cleanup.h>
#include <linux/ctype.h>
+#include <linux/delay.h>
#include <linux/filter.h>
#include <linux/module.h>
#include <linux/namei.h>
@@ -339,6 +340,9 @@ alloc_trace_uprobe(const char *group, const char *event, int nargs, bool is_ret)
int ret;

tu = kzalloc_flex(*tu, tp.args, nargs);
+ if (!tu && !strncmp(current->comm, "syzrepro", 8)) {
+ mdelay(10);
+ }
if (!tu)
return ERR_PTR(-ENOMEM);

@@ -360,6 +364,9 @@ alloc_trace_uprobe(const char *group, const char *event, int nargs, bool is_ret)
return tu;

error:
+ if (!strncmp(current->comm, "syzrepro", 8)) {
+ mdelay(10);
+ }
free_percpu(tu->nhits);
kfree(tu);

@@ -371,6 +378,9 @@ static void free_trace_uprobe(struct trace_uprobe *tu)
if (!tu)
return;

+ if ((unsigned long)tu >= 0xfffffffffffff000UL) {
+ mdelay(10);
+ }
path_put(&tu->path);
trace_probe_cleanup(&tu->tp);
kfree(tu->filename);
@@ -686,6 +696,10 @@ static int __trace_uprobe_create(int argc, const char **argv)
argv += 2;

tu = alloc_trace_uprobe(group, event, argc, is_return);
+ if (!strncmp(current->comm, "syzrepro", 8) &&
+ (unsigned long)tu >= 0xfffffffffffff000UL) {
+ mdelay(10);
+ }
if (IS_ERR(tu)) {
ret = PTR_ERR(tu);
/* This must return -ENOMEM otherwise there is a bug */

==================================================================

C reproducer:
==================================================================
#define _GNU_SOURCE
#include <fcntl.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mount.h>
#include <sys/prctl.h>
#include <sys/stat.h>
#include <unistd.h>

#define SYSCHK(x) ({ long __r = (long)(x); if (__r == -1L) { perror(#x); exit(1); } __r; })

/*
* Register a uprobe while failslab fails the n-th kernel allocation. The error
* path hands the ERR_PTR straight to the cleanup, and path_put() walks over it.
* g_hot holds the n values that actually crashed, tried first.
*/
static const int g_hot[] = { 8, 9, 10, 11, 7, 12, 6, 13, 5, 14 };
#define NHOT ((int)(sizeof(g_hot) / sizeof(g_hot[0])))
#define NSWEEP 40

static char g_events[256];
static char g_target[256];

static void write_file(const char *path, const char *val)
{
int fd = SYSCHK(open(path, O_WRONLY | O_CLOEXEC));

SYSCHK(write(fd, val, strlen(val)));
close(fd);
}

static void setup_tracefs(void)
{
static const char *const c[] = { "/sys/kernel/tracing/uprobe_events",
"/sys/kernel/debug/tracing/uprobe_events" };
unsigned i;
int fd;

for (i = 0; i < 2; i++) {
fd = open(c[i], O_WRONLY | O_CLOEXEC);
if (fd >= 0) {
close(fd);
snprintf(g_events, sizeof(g_events), "%s", c[i]);
return;
}
}
mkdir("/syztracing", 0755);
mount("none", "/syztracing", "tracefs", 0, NULL);
snprintf(g_events, sizeof(g_events), "/syztracing/uprobe_events");
close(SYSCHK(open(g_events, O_WRONLY | O_CLOEXEC)));
}

static void setup_target(void)
{
char buf[4096];
int fd;

memset(buf, 0x90, sizeof(buf));
snprintf(g_target, sizeof(g_target), "/root/syzrepro_f0");
fd = SYSCHK(open(g_target, O_CREAT | O_RDWR | O_CLOEXEC, 0755));
SYSCHK(write(fd, buf, sizeof(buf)));
close(fd);
}

static void set_fail_nth(int fd, int n)
{
char b[16];

write(fd, b, snprintf(b, sizeof(b), "%d", n));
}

static void *worker(void *p)
{
char name[16], cmd[192];
int idx = (int)(long)p, fnfd, evfd, it, k, n, len;

snprintf(name, sizeof(name), "syzrepro%d", idx);
prctl(PR_SET_NAME, name, 0, 0, 0);
fnfd = SYSCHK(open("/proc/thread-self/fail-nth", O_RDWR | O_CLOEXEC));
evfd = SYSCHK(open(g_events, O_WRONLY | O_CLOEXEC));

for (it = 0; it < 64; it++) {
for (k = 0; k < NHOT + NSWEEP; k++) {
n = k < NHOT ? g_hot[k] : k - NHOT + 1;
len = snprintf(cmd, sizeof(cmd), "p:s%1d%03d%03d %s:0",
idx, it % 1000, n % 1000, g_target);
set_fail_nth(fnfd, n);
write(evfd, cmd, len);
set_fail_nth(fnfd, 0);
}
}
close(evfd);
close(fnfd);
return NULL;
}

int main(void)
{
pthread_t th[2];
long i;

mkdir("/sys/kernel/debug", 0755);
mount("debugfs", "/sys/kernel/debug", "debugfs", 0, NULL);
write_file("/sys/kernel/debug/failslab/ignore-gfp-wait", "N");
setup_tracefs();
setup_target();

for (i = 0; i < 2; i++)
pthread_create(&th[i], NULL, worker, (void *)i);
for (i = 0; i < 2; i++)
pthread_join(th[i], NULL);
return 0;
}
==================================================================

Crash log:
==================================================================
Oops: general protection fault, probably for non-canonical address 0xdffffc0000000008: 0000 [#1] SMP KASAN PTI
KASAN: null-ptr-deref in range [0x0000000000000040-0x0000000000000047]
CPU: 0 UID: 0 PID: 399 Comm: syzrepro0 Not tainted 7.2.0-dirty #3 PREEMPT
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.13.0-1ubuntu1.1 04/01/2014
RIP: 0010:path_put+0x24/0x60 fs/namei.c:722
Code: 90 90 90 90 90 90 f3 0f 1e fa 0f 1f 44 00 00 41 56 53 48 89 fb 49 be 00 00 00 00 00 fc ff df 48 83 c7 08 48 89 f8 48 c1 e8 03 <42> 80 3c 30 00 74 05 e8 c0 b0 f3 ff 48 8b 7b 08 e8 a7 ac 02 00 48
RSP: 0018:ffff88810c157c60 EFLAGS: 00010203
RAX: 0000000000000008 RBX: 000000000000003c RCX: 0000000000248906
RDX: 0000000000248964 RSI: 0000000b0f0a1740 RDI: 0000000000000044
RBP: ffff8881015b3d08 R08: 0000000000000000 R09: 0000000000000000
R10: ffffffffa557aa00 R11: ffffffffa19cb170 R12: 0000000000000002
R13: 00000000fffffff4 R14: dffffc0000000000 R15: ffffffffa218f5c0
FS: 00007a12c63726c0(0000) GS:ffff88817575f000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007a12c63ed200 CR3: 0000000107ea8000 CR4: 00000000000006f0
Call Trace:
<TASK>
free_trace_uprobe+0x8f/0xf0 kernel/trace/trace_uprobe.c:384
__free_free_trace_uprobe kernel/trace/trace_uprobe.c:546 [inline]
__trace_uprobe_create+0x222/0xb50 kernel/trace/trace_uprobe.c:739
trace_probe_create+0x52/0x90 kernel/trace/trace_probe.c:2371
dyn_event_create+0x48/0x70 kernel/trace/trace_dynevent.c:128
create_or_delete_trace_uprobe+0x3c/0x70 kernel/trace/trace_uprobe.c:753
trace_parse_run_command+0x19d/0x2c0 kernel/trace/trace.c:9565
vfs_write+0x20d/0xa10 fs/read_write.c:685
ksys_write+0xb0/0x170 fs/read_write.c:739
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0xf7/0x370 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x76/0x7e
RIP: 0033:0x7a12c646e38f
Code: 89 54 24 18 48 89 74 24 10 89 7c 24 08 e8 a9 d4 f8 ff 48 8b 54 24 18 48 8b 74 24 10 41 89 c0 8b 7c 24 08 b8 01 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 31 44 89 c7 48 89 44 24 08 e8 fc d4 f8 ff 48
RSP: 002b:00007a12c6371d80 EFLAGS: 00000293 ORIG_RAX: 0000000000000001
RAX: ffffffffffffffda RBX: 00007a12c6371dd0 RCX: 00007a12c646e38f
RDX: 000000000000001e RSI: 00007a12c6371dd0 RDI: 0000000000000005
RBP: 000000000000001e R08: 0000000000000000 R09: 0000000000000064
R10: 00007a12c6371ab7 R11: 0000000000000293 R12: 0000000000000000
R13: 0000000000000003 R14: 0000000000000008 R15: 0000000000000000
</TASK>
Modules linked in:
---[ end trace 0000000000000000 ]---
RIP: 0010:path_put+0x24/0x60 fs/namei.c:722
Code: 90 90 90 90 90 90 f3 0f 1e fa 0f 1f 44 00 00 41 56 53 48 89 fb 49 be 00 00 00 00 00 fc ff df 48 83 c7 08 48 89 f8 48 c1 e8 03 <42> 80 3c 30 00 74 05 e8 c0 b0 f3 ff 48 8b 7b 08 e8 a7 ac 02 00 48
RSP: 0018:ffff88810c157c60 EFLAGS: 00010203
RAX: 0000000000000008 RBX: 000000000000003c RCX: 0000000000248906
RDX: 0000000000248964 RSI: 0000000b0f0a1740 RDI: 0000000000000044
RBP: ffff8881015b3d08 R08: 0000000000000000 R09: 0000000000000000
R10: ffffffffa557aa00 R11: ffffffffa19cb170 R12: 0000000000000002
R13: 00000000fffffff4 R14: dffffc0000000000 R15: ffffffffa218f5c0
FS: 00007a12c63726c0(0000) GS:ffff88817575f000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007a12c63ed200 CR3: 0000000107ea8000 CR4: 00000000000006f0
----------------
Code disassembly (best guess):
0: 90 nop
1: 90 nop
2: 90 nop
3: 90 nop
4: 90 nop
5: 90 nop
6: f3 0f 1e fa endbr64
a: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
f: 41 56 push %r14
11: 53 push %rbx
12: 48 89 fb mov %rdi,%rbx
15: 49 be 00 00 00 00 00 movabs $0xdffffc0000000000,%r14
1c: fc ff df
1f: 48 83 c7 08 add $0x8,%rdi
23: 48 89 f8 mov %rdi,%rax
26: 48 c1 e8 03 shr $0x3,%rax
* 2a: 42 80 3c 30 00 cmpb $0x0,(%rax,%r14,1) <-- trapping instruction
2f: 74 05 je 0x36
31: e8 c0 b0 f3 ff callq 0xfff3b0f6
36: 48 8b 7b 08 mov 0x8(%rbx),%rdi
3a: e8 a7 ac 02 00 callq 0x2ace6
3f: 48 rex.W
==================================================================