• Eddie.Horng's avatar
    cap_inode_getsecurity: use d_find_any_alias() instead of d_find_alias() · 5a842ecc
    Eddie.Horng authored
    commit 355139a8dba446cc11a424cddbf7afebc3041ba1 upstream.
    
    The code in cap_inode_getsecurity(), introduced by commit 8db6c34f
    ("Introduce v3 namespaced file capabilities"), should use
    d_find_any_alias() instead of d_find_alias() do handle unhashed dentry
    correctly. This is needed, for example, if execveat() is called with an
    open but unlinked overlayfs file, because overlayfs unhashes dentry on
    unlink.
    This is a regression of real life application, first reported at
    https://www.spinics.net/lists/linux-unionfs/msg05363.html
    
    Below reproducer and setup can reproduce the case.
      const char* exec="echo";
      const char *newargv[] = { "echo", "hello", NULL};
      const char *newenviron[] = { NULL };
      int fd, err;
    
      fd = open(exec, O_PATH);
      unlink(exec);
      err = syscall(322/*SYS_execveat*/, fd, "", newargv, newenviron,
    AT_EMPTY_PATH);
      if(err<0)
        fprintf(stderr, "execveat: %s\n", strerror(errno));
    
    gcc compile into ~/test/a.out
    mount -t overlay -orw,lowerdir=/mnt/l,upperdir=/mnt/u,workdir=/mnt/w
    none /mnt/m
    cd /mnt/m
    cp /bin/echo .
    ~/test/a.out
    
    Expected result:
    hello
    Actually result:
    execveat: Invalid argument
    dmesg:
    Invalid argument reading file caps for /dev/fd/3
    
    The 2nd reproducer and setup emulates similar case but for
    regular filesystem:
      const char* exec="echo";
      int fd, err;
      char buf[256];
    
      fd = open(exec, O_RDONLY);
      unlink(exec);
      err = fgetxattr(fd, "security.capability", buf, 256);
      if(err<0)
        fprintf(stderr, "fgetxattr: %s\n", strerror(errno));
    
    gcc compile into ~/test_fgetxattr
    
    cd /tmp
    cp /bin/echo .
    ~/test_fgetxattr
    
    Result:
    fgetxattr: Invalid argument
    
    On regular filesystem, for example, ext4 read xattr from
    disk and return to execveat(), will not trigger this issue, however,
    the overlay attr handler pass real dentry to vfs_getxattr() will.
    This reproducer calls fgetxattr() with an unlinked fd, involkes
    vfs_getxattr() then reproduced the case that d_find_alias() in
    cap_inode_getsecurity() can't find the unlinked dentry.
    Suggested-by: 's avatarAmir Goldstein <amir73il@gmail.com>
    Acked-by: 's avatarAmir Goldstein <amir73il@gmail.com>
    Acked-by: 's avatarSerge E. Hallyn <serge@hallyn.com>
    Fixes: 8db6c34f ("Introduce v3 namespaced file capabilities")
    Cc: <stable@vger.kernel.org> # v4.14
    Signed-off-by: 's avatarEddie Horng <eddie.horng@mediatek.com>
    Signed-off-by: 's avatarEric W. Biederman <ebiederm@xmission.com>
    Signed-off-by: 's avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
    5a842ecc