Skip to content

Commit

Permalink
fcntl: Add 32bit filesystem mode
Browse files Browse the repository at this point in the history
It was brought to my attention that this bug from 2018 was
still unresolved: 32 bit emulators like QEMU were given
64 bit hashes when running 32 bit emulation on 64 bit systems.

This adds a flag to the fcntl() F_GETFD and F_SETFD operations
to set the underlying filesystem into 32bit mode even if the
file handle was opened using 64bit mode without the compat
syscalls.

Programs that need the 32 bit file system behavior need to
issue a fcntl() system call such as in this example:

  #define FD_32BIT_MODE 2

  int main(int argc, char** argv) {
    DIR* dir;
    int err;
    int mode;
    int fd;

    dir = opendir("/boot");
    fd = dirfd(dir);
    mode = fcntl(fd, F_GETFD);
    mode |= FD_32BIT_MODE;
    err = fcntl(fd, F_SETFD, mode);
    if (err) {
      printf("fcntl() failed! err=%d\n", err);
      return 1;
    }
    printf("dir=%p\n", dir);
    printf("readdir(dir)=%p\n", readdir(dir));
    printf("errno=%d: %s\n", errno, strerror(errno));
    return 0;
  }

This can be pretty hard to test since C libraries and linux
userspace security extensions aggressively filter the parameters
that are passed down and allowed to commit into actual system
calls.

Cc: Florian Weimer <[email protected]>
Cc: Peter Maydell <[email protected]>
Cc: Andy Lutomirski <[email protected]>
Cc: Eric Blake <[email protected]>
Reported-by: 罗勇刚(Yonggang Luo) <[email protected]>
Suggested-by: Theodore Ts'o <[email protected]>
Link: https://bugs.launchpad.net/qemu/+bug/1805913
Link: https://lore.kernel.org/lkml/[email protected]/
Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=205957
Signed-off-by: Linus Walleij <[email protected]>
  • Loading branch information
linusw authored and 0x011011110 committed Apr 20, 2022
1 parent 2b10189 commit 4bcc0b2
Show file tree
Hide file tree
Showing 2 changed files with 15 additions and 0 deletions.
7 changes: 7 additions & 0 deletions fs/fcntl.c
Original file line number Diff line number Diff line change
Expand Up @@ -344,10 +344,17 @@ static long do_fcntl(int fd, unsigned int cmd, unsigned long arg,
break;
case F_GETFD:
err = get_close_on_exec(fd) ? FD_CLOEXEC : 0;
/* Report 32bit file system mode */
if (filp->f_mode & FMODE_32BITHASH)
err |= FD_32BIT_MODE;
break;
case F_SETFD:
err = 0;
set_close_on_exec(fd, arg & FD_CLOEXEC);
if (arg & FD_32BIT_MODE)
filp->f_mode |= FMODE_32BITHASH;
else
filp->f_mode &= ~FMODE_32BITHASH;
break;
case F_GETFL:
err = filp->f_flags;
Expand Down
8 changes: 8 additions & 0 deletions include/uapi/asm-generic/fcntl.h
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,14 @@ struct f_owner_ex {

/* for F_[GET|SET]FL */
#define FD_CLOEXEC 1 /* actually anything with low bit set goes */
/*
* This instructs the kernel to provide 32bit semantics (such as hashes) from
* the file system layer, when running a userland that depend on 32bit
* semantics on a kernel that supports 64bit userland, but does not use the
* compat ioctl() for e.g. open(), so that the kernel would otherwise assume
* that the userland process is capable of dealing with 64bit semantics.
*/
#define FD_32BIT_MODE 2

/* for posix fcntl() and lockf() */
#ifndef F_RDLCK
Expand Down

0 comments on commit 4bcc0b2

Please sign in to comment.