Re: [PATCH] proc: protect ptrace_may_access() with exec_update_lock (part 1)
Jann Horn <[email protected]> Mon, 6 Jul 2026 19:37:13 +0200
| Newsgroups | dev.linux.lists.regressions,org.kernel.vger.linux-fsdevel,org.kernel.vger.linux-kernel,org.kernel.vger.stable |
|---|---|
| Message-ID | <CAG48ez0ebrMy8QGKLuz0Qwao_Eiav6e5pAJ5f6GrUPJLRkwNnw@mail.gmail.com> |
On Mon, Jul 6, 2026 at 7:07 PM Magnus Lindholm <[email protected]> wrote: > > Hi, > > while testing my Alpha generic-entry series on top of v7.2-rc1, I noticed > that several strace --pidns-translation tests started failing. The same > generic-entry series on top of v7.1-rc1 passes these tests. > > I bisected the regression between v7.1-rc1 and v7.2-rc1, always applying > the same generic-entry series before testing, and the first bad commit is: > > 6650527444dadc63d84aa939d14ecba4fadb2f69 > proc: protect ptrace_may_access() with exec_update_lock (part 1) > > Examples of failing strace tests include: > > signal_receive--pidns-translation.gen.test > so_peercred--pidns-translation.gen.test > tgkill--pidns-translation.gen.test > tkill--pidns-translation.gen.test > fcntl--pidns-translation.gen.test > xet_robust_list--pidns-translation.gen.test > xetpgid--pidns-translation.gen.test > xetpriority--pidns-translation.gen.test > > One simple reproducer is: > > cd strace/tests > ./xetpgid--pidns-translation.gen.test > > The failure looks like this: > > ../../src/strace: NS_* ioctl commands are not supported by the kernel > > and the decoded output lacks the expected pidns translation comments, e.g.: > > - getpgid(2 /* 6 in strace's PID NS */) = 0 > + getpgid(2) = 0 > > Looking at the patch, the relevant part seems to be the change in > fs/proc/namespaces.c: proc_ns_get_link() and proc_ns_readlink() now take > task->signal->exec_update_lock around the ptrace_may_access() check and > namespace link/readlink handling. strace's --decode-pids=pidns code appears > to rely on accessing /proc/<pid>/ns/pid for short-lived tracees in a nested > PID namespace, so this looks like a plausible connection to the failure. > > The kernel has the relevant namespace options enabled: > > CONFIG_NAMESPACES=y > CONFIG_USER_NS=y > CONFIG_PID_NS=y > CONFIG_CHECKPOINT_RESTORE=y > CONFIG_PROC_FS=y > > I also tested the basic nsfs ioctls with a small standalone program, both > outside and inside "unshare -Urpf", and NS_GET_NSTYPE, > NS_GET_PID_IN_PIDNS and NS_GET_PID_FROM_PIDNS all work there. So the failure > does not look like missing namespace support or a simple ioctl-number issue; > it seems specific to the proc/ns access pattern used by strace's pidns > translation code. > > #regzbot introduced: 6650527444dadc63d84aa939d14ecba4fadb2f69 Hmm... running strace on strace, I see this: 669 openat(AT_FDCWD, "/proc/469/ns/pid", O_RDONLY) = 5 669 fstat(5, {st_dev=makedev(0, 0x15), st_ino=1792, st_mode=S_IFDIR|0511, st_nlink=2, st_uid=1000, st_gid=1000, st_blksize=1024, st_blocks= 0, st_size=0, st_atime=1783357140 /* 2026-07-06T16:59:00.713036792+0000 */, st_atime_nsec=713036792, st_mtime=1783357140 /* 2026-07-06T16:59: 00.713036792+0000 */, st_mtime_nsec=713036792, st_ctime=1783357140 /* 2026-07-06T16:59:00.713036792+0000 */, st_ctime_nsec=713036792}) = 0 669 ioctl(5, NS_GET_PARENT) = -1 ENOTTY (Inappropriate ioctl for device) 669 write(2, ", child_tidptr=0x7faa62d54a10) = 7", 34) = 34 669 write(2, "strace: NS_* ioctl commands are not supported by the kernel\n", 60) = 60 So... /proc/469/ns/pid resolves to a directory somehow? That seems very wrong. root@vm:~# cd /proc/496/ns/pid/ root@vm:/proc/496/ns/pid# AAaaah, darn, ok, I see. The patch contains this change: ``` static const char *proc_ns_get_link(struct dentry *dentry, struct inode *inode, struct delayed_call *done) { const struct proc_ns_operations *ns_ops = PROC_I(inode)->ns_ops; struct task_struct *task; struct path ns_path; int error = -EACCES; if (!dentry) return ERR_PTR(-ECHILD); task = get_proc_task(inode); if (!task) return ERR_PTR(-EACCES); + error = down_read_killable(&task->signal->exec_update_lock); + if (error) + goto out_put_task; + if (!ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS)) goto out; ``` and that is wrong because previously, the ptrace_may_access() check relied on "error" still being "error = -EACCES" from the initialization at the top of the function, but now it is 0 from down_read_killable(), so now a ptrace permission check failure causes us to return with 0 without actually having called nd_jump_link(), which I think means we end up staying in /proc/$pid/ns? Sigh, I will send a fix.