// peerChain
ucred, err = unix.GetsockoptUcred(int(fd), unix.SOL_SOCKET, unix.SO_PEERCRED)
// procStatus
b, err := os.ReadFile(fmt.Sprintf("/proc/%d/status", pid))
There's a TOCTOU window between GetsockoptUcred and ReadFile where the original process can exit and the PID can be reused.
I thought that pidfd would trivially solve this by holding on to the pidfd, doing a pidfd_getpid, and using /proc. However, holding the pidfd doesn't actually guarantee the does not get reused.
I believe that the race-free option is sort of like this past Linux 6.13, although these need further verification. In pseudo-C-ish (omitting sizeof(info) and such extensibility fields, not real C syntax, but hopefully illustrating the concepts):
ioctl(pidfd, PIDFD_GET_INFO, info = &pidfd_info{ .request_mask = PIDFD_INFO_PID })
pid = info.pid (or replace this step with something like pidfd_getpid)
statx(pidfd, "", AT_EMPTY_PATH, STATX_INO, &sx0) /* pidfs inode is never reused */
procfs_dirfd = open("/proc/%d", O_PATH | O_DIRECTORY | O_CLOEXEC)
v = pidfd_open(pid, 0)
statx(v, "", AT_EMPTY_PATH, STATX_INO, &sx1)
if (sx0.stx_ino != sx1.stx_ino) error;
- Then use the procfs_dirfd normally.
Magic links might ESRCH if the process dies though.
See also https://www.corsix.org/content/what-is-a-pidfd.
Now, I think the more central issue is the framing of the UI/UX itself.
Allow use of key $name?
Key fingerprint $fp.
Requested by ssh (1234) ← git ← zsh ← kitty
This reads to a non-expert user as "we have authoritatively identified the application", in a way that any mechanism on the same UNIX user without using containers/namespaces/flatpak/etc, could not support: the comm field is settable by the process itself via prctl, or iirc by writing to /proc/self/comm. I think we need at least something like "Note: any program running as your user can present arbitrary information here."
Note that same-UID Linux has several ways to completely defeat any inter-process introspection scheme:
LD_PRELOAD, so checking proc_exe carefully won't work;
ptrace can inject arbitrary code
pidfd_getfd
- Some other
prctl schenanigans
cc @Foxboron @Mic92
Disclosed publicly with permission
There's a TOCTOU window between
GetsockoptUcredandReadFilewhere the original process can exit and the PID can be reused.I thought that
pidfdwould trivially solve this by holding on to thepidfd, doing apidfd_getpid, and using/proc. However, holding thepidfddoesn't actually guarantee the does not get reused.I believe that the race-free option is sort of like this past Linux 6.13, although these need further verification. In pseudo-C-ish (omitting
sizeof(info)and such extensibility fields, not real C syntax, but hopefully illustrating the concepts):ioctl(pidfd, PIDFD_GET_INFO, info = &pidfd_info{ .request_mask = PIDFD_INFO_PID })pid = info.pid(or replace this step with something like pidfd_getpid)statx(pidfd, "", AT_EMPTY_PATH, STATX_INO, &sx0) /* pidfs inode is never reused */procfs_dirfd = open("/proc/%d", O_PATH | O_DIRECTORY | O_CLOEXEC)v = pidfd_open(pid, 0)statx(v, "", AT_EMPTY_PATH, STATX_INO, &sx1)if (sx0.stx_ino != sx1.stx_ino) error;Magic links might ESRCH if the process dies though.
See also https://www.corsix.org/content/what-is-a-pidfd.
Now, I think the more central issue is the framing of the UI/UX itself.
This reads to a non-expert user as "we have authoritatively identified the application", in a way that any mechanism on the same UNIX user without using containers/namespaces/flatpak/etc, could not support: the comm field is settable by the process itself via
prctl, or iirc by writing to/proc/self/comm. I think we need at least something like "Note: any program running as your user can present arbitrary information here."Note that same-UID Linux has several ways to completely defeat any inter-process introspection scheme:
LD_PRELOAD, so checkingproc_execarefully won't work;ptracecan inject arbitrary codepidfd_getfdprctlschenaniganscc @Foxboron @Mic92
Disclosed publicly with permission