Deadlock on stat when PATH=/bin
wentasah opened this issue · 5 comments
When I run envfs
as follows:
sudo strace -e file envfs -o debug -f /bin
and in another terminal:
PATH=/bin sh -c uname
The result is a deadlock, which can be seen in the strace output produce by the first command above:
mount("envfs", "/bin", "fuse", MS_RDONLY|MS_NOSUID|MS_NODEV, "fd=3,rootmode=40755,user_id=0,gr"...) = 0
DEBUG - FUSE( 2) ino 0x0000000000000000 INIT kernel ABI 7.39, capabilities 0x73fffffb, max readahead 131072
DEBUG - INIT response: ABI 7.8, flags 0x1, max readahead 131072, max write 16777216
DEBUG - FUSE( 4) ino 0x0000000000000001 GETATTR
DEBUG - FUSE( 6) ino 0x0000000000000001 GETATTR
DEBUG - FUSE( 8) ino 0x0000000000000001 GETATTR
DEBUG - FUSE( 10) ino 0x0000000000000001 LOOKUP name "sh"
openat(AT_FDCWD, "/proc/92512/environ", O_RDONLY|O_CLOEXEC) = 4
openat(AT_FDCWD, "/proc/92512/syscall", O_RDONLY|O_CLOEXEC) = 4
statx(4, "", AT_STATX_SYNC_AS_STAT|AT_EMPTY_PATH, STATX_ALL, {stx_mask=STATX_BASIC_STATS|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFREG|0400, stx_size=0, ...}) = 0
openat(AT_FDCWD, "/proc/92512/mem", O_RDONLY|O_CLOEXEC) = 4
statx(AT_FDCWD, "/bin", AT_STATX_SYNC_AS_STAT|AT_SYMLINK_NOFOLLOW, STATX_ALL, <unfinished ...>) = ?
The reason is this stat call, which tries to prevent recursion. I guess that previously, envfs
was multi-threaded and usually there was another thread which could handle that stat
request, but now envfs
is single-threaded and cannot handle the request recursively.
I know that somewhere it's written not to set PATH to /bin, but unfortunately that's what some software does on its own and envfs
should survive that. The problem is that after the deadlock happens, everything using /bin/sh
or /usr/bin/env
blocks as well.
Ah. There should be really checks for filtering out its own mountpoint from PATH lookup. I thought I added something like this.
Ah. The check if it is its own filesytem already blocks...
Yeah, exactly. I like the idea of using nlinks for this check instead of hardcoding the paths, because the former should work if you bind-mount the file system anywhere. But it cannot work this way in a single threaded app. And multiple threads only decrease the chance of the deadlock, not eliminate it (unless you create threads on demand).
I was trying to increase TTL for the root directory, hoping for the kernel to cache the entry and handle the stat call without calling envfs again, but it didn't work. But I have very little understanding of how FUSE works, so I might have done it wrong. Hopefully, you can come up with some idea how to solve it elegantly :-)