Skip to content

fix: enforce permission checks for allow_other mounts#11

Open
ShukantPal wants to merge 1 commit intomasterfrom
fix/issue-15-permission-cache-invalidation
Open

fix: enforce permission checks for allow_other mounts#11
ShukantPal wants to merge 1 commit intomasterfrom
fix/issue-15-permission-cache-invalidation

Conversation

@ShukantPal
Copy link
Copy Markdown
Owner

@ShukantPal ShukantPal commented Mar 11, 2026

Closes libfuse#15

When default_permissions is not set and allow_other is enabled, the kernel's fuse_permission() returns 0 for most permission checks without consulting userspace. This means that after a chmod 000, cached dentries still allow access by other users — the kernel never sends FUSE_ACCESS to verify permissions.

This is a two-layer problem requiring fixes in both the kernel and the FUSE daemon:

Daemon fix (example/passthrough.c):
Fix xmp_access() to check permissions as the requesting user (via fuse_get_context()) rather than as root. Uses raw SYS_setresuid/SYS_setresgid syscalls for per-thread credential switching, since glibc wrappers synchronize across all threads.

Associated kernel patch (fs/fuse/dir.c fuse_permission):
When allow_other is set and default_permissions is not, add an else-if branch that sends FUSE_ACCESS to userspace for all permission checks. Includes a MAY_NOT_BLOCK guard that returns -ECHILD to force retry outside RCU walk mode, since fuse_access() sends a blocking request to userspace.

--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -1607,6 +1607,17 @@ static int fuse_permission(...)
      } else if (mask & (MAY_ACCESS | MAY_CHDIR)) {
          err = fuse_access(inode, mask);
+     } else if (fc->allow_other) {
+         /*
+          * With allow_other, multiple users can access this mount.
+          * Verify permissions via userspace to prevent user A from
+          * accessing files/directories owned by user B with
+          * restrictive permissions.
+          */
+         if (mask & MAY_NOT_BLOCK)
+             return -ECHILD;
+
+         err = fuse_access(inode, mask);
      } else if ((mask & MAY_EXEC) && S_ISREG(inode->i_mode)) {

Tests (xfail without kernel patch):

  • test_perm_cache_dir_traversal: chmod 000 dir, verify files inside are denied
  • test_perm_cache_file_truncate: chmod 000 file, verify truncate is denied
  • test_perm_cache_dir_read_after_noexec: remove dir execute bit, verify traversal is denied

@bolt-new-by-stackblitz
Copy link
Copy Markdown

Review PR in StackBlitz Codeflow Run & review this pull request in StackBlitz Codeflow.

@ShukantPal ShukantPal force-pushed the fix/issue-15-permission-cache-invalidation branch 2 times, most recently from de12e39 to db9066c Compare March 13, 2026 02:44
When default_permissions is not set and allow_other is enabled, the
kernel's fuse_permission() returns 0 for most permission checks without
consulting userspace. This means that after a chmod 000, cached dentries
still allow access by other users — the kernel never sends FUSE_ACCESS
to verify permissions.

This is a two-layer problem requiring fixes in both the kernel and the
FUSE daemon:

Daemon fix (example/passthrough.c):
  Fix xmp_access() to check permissions as the requesting user (via
  fuse_get_context()) rather than as root. Uses raw SYS_setresuid/
  SYS_setresgid syscalls for per-thread credential switching, since
  glibc wrappers synchronize across all threads.

Associated kernel patch (fs/fuse/dir.c fuse_permission):
  When allow_other is set and default_permissions is not, add an
  else-if branch that sends FUSE_ACCESS to userspace for all
  permission checks. Includes a MAY_NOT_BLOCK guard that returns
  -ECHILD to force retry outside RCU walk mode, since fuse_access()
  sends a blocking request to userspace.

  --- a/fs/fuse/dir.c
  +++ b/fs/fuse/dir.c
  @@ -1607,6 +1607,17 @@ static int fuse_permission(...)
        } else if (mask & (MAY_ACCESS | MAY_CHDIR)) {
            err = fuse_access(inode, mask);
  +     } else if (fc->allow_other) {
  +         /*
  +          * With allow_other, multiple users can access this mount.
  +          * Verify permissions via userspace to prevent user A from
  +          * accessing files/directories owned by user B with
  +          * restrictive permissions.
  +          */
  +         if (mask & MAY_NOT_BLOCK)
  +             return -ECHILD;
  +
  +         err = fuse_access(inode, mask);
        } else if ((mask & MAY_EXEC) && S_ISREG(inode->i_mode)) {

Tests (xfail without kernel patch):
- test_perm_cache_dir_traversal: chmod 000 dir, verify files inside
  are denied
- test_perm_cache_file_truncate: chmod 000 file, verify truncate is
  denied
- test_perm_cache_dir_read_after_noexec: remove dir execute bit,
  verify traversal is denied

Closes: libfuse#15
@ShukantPal ShukantPal force-pushed the fix/issue-15-permission-cache-invalidation branch from db9066c to 5a5a61a Compare March 13, 2026 02:50
@ShukantPal ShukantPal changed the title fix: invalidate cached permissions on chmod/chown (issue #15) fix: enforce permission checks for allow_other mounts Mar 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Without default_permissions, cached permissions are only checked on first access

1 participant