Merge branch 'work.namei' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
authorLinus Torvalds <torvalds@linux-foundation.org>
Mon, 9 Apr 2018 19:48:05 +0000 (12:48 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Mon, 9 Apr 2018 19:48:05 +0000 (12:48 -0700)
Pull vfs namei updates from Al Viro:

 - make lookup_one_len() safe with parent locked only shared(incoming
   afs series wants that)

 - fix of getname_kernel() regression from 2015 (-stable fodder, that
   one).

* 'work.namei' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs:
  getname_kernel() needs to make sure that ->name != ->iname in long case
  make lookup_one_len() safe to use with directory locked shared
  new helper: __lookup_slow()
  merge common parts of lookup_one_len{,_unlocked} into common helper

1  2 
fs/namei.c

diff --combined fs/namei.c
@@@ -39,7 -39,6 +39,7 @@@
  #include <linux/bitops.h>
  #include <linux/init_task.h>
  #include <linux/uaccess.h>
 +#include <linux/build_bug.h>
  
  #include "internal.h"
  #include "mount.h"
@@@ -131,7 -130,6 +131,7 @@@ getname_flags(const char __user *filena
        struct filename *result;
        char *kname;
        int len;
 +      BUILD_BUG_ON(offsetof(struct filename, iname) % sizeof(long) != 0);
  
        result = audit_reusename(filename);
        if (result)
@@@ -224,9 -222,10 +224,10 @@@ getname_kernel(const char * filename
        if (len <= EMBEDDED_NAME_MAX) {
                result->name = (char *)result->iname;
        } else if (len <= PATH_MAX) {
+               const size_t size = offsetof(struct filename, iname[1]);
                struct filename *tmp;
  
-               tmp = kmalloc(sizeof(*tmp), GFP_KERNEL);
+               tmp = kmalloc(size, GFP_KERNEL);
                if (unlikely(!tmp)) {
                        __putname(result);
                        return ERR_PTR(-ENOMEM);
@@@ -561,10 -560,9 +562,10 @@@ static int __nd_alloc_stack(struct name
  static bool path_connected(const struct path *path)
  {
        struct vfsmount *mnt = path->mnt;
 +      struct super_block *sb = mnt->mnt_sb;
  
 -      /* Only bind mounts can have disconnected paths */
 -      if (mnt->mnt_root == mnt->mnt_sb->s_root)
 +      /* Bind mounts and multi-root filesystems can have disconnected paths */
 +      if (!(sb->s_iflags & SB_I_MULTIROOT) && (mnt->mnt_root == sb->s_root))
                return true;
  
        return is_subdir(path->dentry, mnt->mnt_root);
@@@ -929,8 -927,7 +930,8 @@@ static inline int may_follow_link(struc
        if (nd->flags & LOOKUP_RCU)
                return -ECHILD;
  
 -      audit_log_link_denied("follow_link", &nd->stack[0].link);
 +      audit_inode(nd->name, nd->stack[0].link.dentry, 0);
 +      audit_log_link_denied("follow_link");
        return -EACCES;
  }
  
@@@ -996,7 -993,7 +997,7 @@@ static int may_linkat(struct path *link
        if (safe_hardlink_source(inode) || inode_owner_or_capable(inode))
                return 0;
  
 -      audit_log_link_denied("linkat", link);
 +      audit_log_link_denied("linkat");
        return -EPERM;
  }
  
@@@ -1597,22 -1594,21 +1598,21 @@@ static int lookup_fast(struct nameidat
  }
  
  /* Fast lookup failed, do it the slow way */
- static struct dentry *lookup_slow(const struct qstr *name,
-                                 struct dentry *dir,
-                                 unsigned int flags)
+ static struct dentry *__lookup_slow(const struct qstr *name,
+                                   struct dentry *dir,
+                                   unsigned int flags)
  {
-       struct dentry *dentry = ERR_PTR(-ENOENT), *old;
+       struct dentry *dentry, *old;
        struct inode *inode = dir->d_inode;
        DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
  
-       inode_lock_shared(inode);
        /* Don't go there if it's already dead */
        if (unlikely(IS_DEADDIR(inode)))
-               goto out;
+               return ERR_PTR(-ENOENT);
  again:
        dentry = d_alloc_parallel(dir, name, &wq);
        if (IS_ERR(dentry))
-               goto out;
+               return dentry;
        if (unlikely(!d_in_lookup(dentry))) {
                if (!(flags & LOOKUP_NO_REVAL)) {
                        int error = d_revalidate(dentry, flags);
                        dentry = old;
                }
        }
- out:
-       inode_unlock_shared(inode);
        return dentry;
  }
  
+ static struct dentry *lookup_slow(const struct qstr *name,
+                                 struct dentry *dir,
+                                 unsigned int flags)
+ {
+       struct inode *inode = dir->d_inode;
+       struct dentry *res;
+       inode_lock_shared(inode);
+       res = __lookup_slow(name, dir, flags);
+       inode_unlock_shared(inode);
+       return res;
+ }
  static inline int may_lookup(struct nameidata *nd)
  {
        if (nd->flags & LOOKUP_RCU) {
@@@ -2421,56 -2427,63 +2431,63 @@@ int vfs_path_lookup(struct dentry *dent
  }
  EXPORT_SYMBOL(vfs_path_lookup);
  
- /**
-  * lookup_one_len - filesystem helper to lookup single pathname component
-  * @name:     pathname component to lookup
-  * @base:     base directory to lookup from
-  * @len:      maximum length @len should be interpreted to
-  *
-  * Note that this routine is purely a helper for filesystem usage and should
-  * not be called by generic code.
-  *
-  * The caller must hold base->i_mutex.
-  */
- struct dentry *lookup_one_len(const char *name, struct dentry *base, int len)
+ static int lookup_one_len_common(const char *name, struct dentry *base,
+                                int len, struct qstr *this)
  {
-       struct qstr this;
-       unsigned int c;
-       int err;
-       WARN_ON_ONCE(!inode_is_locked(base->d_inode));
-       this.name = name;
-       this.len = len;
-       this.hash = full_name_hash(base, name, len);
+       this->name = name;
+       this->len = len;
+       this->hash = full_name_hash(base, name, len);
        if (!len)
-               return ERR_PTR(-EACCES);
+               return -EACCES;
  
        if (unlikely(name[0] == '.')) {
                if (len < 2 || (len == 2 && name[1] == '.'))
-                       return ERR_PTR(-EACCES);
+                       return -EACCES;
        }
  
        while (len--) {
-               c = *(const unsigned char *)name++;
+               unsigned int c = *(const unsigned char *)name++;
                if (c == '/' || c == '\0')
-                       return ERR_PTR(-EACCES);
+                       return -EACCES;
        }
        /*
         * See if the low-level filesystem might want
         * to use its own hash..
         */
        if (base->d_flags & DCACHE_OP_HASH) {
-               int err = base->d_op->d_hash(base, &this);
+               int err = base->d_op->d_hash(base, this);
                if (err < 0)
-                       return ERR_PTR(err);
+                       return err;
        }
  
-       err = inode_permission(base->d_inode, MAY_EXEC);
+       return inode_permission(base->d_inode, MAY_EXEC);
+ }
+ /**
+  * lookup_one_len - filesystem helper to lookup single pathname component
+  * @name:     pathname component to lookup
+  * @base:     base directory to lookup from
+  * @len:      maximum length @len should be interpreted to
+  *
+  * Note that this routine is purely a helper for filesystem usage and should
+  * not be called by generic code.
+  *
+  * The caller must hold base->i_mutex.
+  */
+ struct dentry *lookup_one_len(const char *name, struct dentry *base, int len)
+ {
+       struct dentry *dentry;
+       struct qstr this;
+       int err;
+       WARN_ON_ONCE(!inode_is_locked(base->d_inode));
+       err = lookup_one_len_common(name, base, len, &this);
        if (err)
                return ERR_PTR(err);
  
-       return __lookup_hash(&this, base, 0);
+       dentry = lookup_dcache(&this, base, 0);
+       return dentry ? dentry : __lookup_slow(&this, base, 0);
  }
  EXPORT_SYMBOL(lookup_one_len);
  
@@@ -2490,37 -2503,10 +2507,10 @@@ struct dentry *lookup_one_len_unlocked(
                                       struct dentry *base, int len)
  {
        struct qstr this;
-       unsigned int c;
        int err;
        struct dentry *ret;
  
-       this.name = name;
-       this.len = len;
-       this.hash = full_name_hash(base, name, len);
-       if (!len)
-               return ERR_PTR(-EACCES);
-       if (unlikely(name[0] == '.')) {
-               if (len < 2 || (len == 2 && name[1] == '.'))
-                       return ERR_PTR(-EACCES);
-       }
-       while (len--) {
-               c = *(const unsigned char *)name++;
-               if (c == '/' || c == '\0')
-                       return ERR_PTR(-EACCES);
-       }
-       /*
-        * See if the low-level filesystem might want
-        * to use its own hash..
-        */
-       if (base->d_flags & DCACHE_OP_HASH) {
-               int err = base->d_op->d_hash(base, &this);
-               if (err < 0)
-                       return ERR_PTR(err);
-       }
-       err = inode_permission(base->d_inode, MAY_EXEC);
+       err = lookup_one_len_common(name, base, len, &this);
        if (err)
                return ERR_PTR(err);
  
@@@ -3377,7 -3363,9 +3367,7 @@@ finish_open_created
                goto out;
        *opened |= FILE_OPENED;
  opened:
 -      error = open_check_o_direct(file);
 -      if (!error)
 -              error = ima_file_check(file, op->acc_mode, *opened);
 +      error = ima_file_check(file, op->acc_mode, *opened);
        if (!error && will_truncate)
                error = handle_truncate(file);
  out:
@@@ -3457,6 -3445,9 +3447,6 @@@ static int do_tmpfile(struct nameidata 
        error = finish_open(file, child, NULL, opened);
        if (error)
                goto out2;
 -      error = open_check_o_direct(file);
 -      if (error)
 -              fput(file);
  out2:
        mnt_drop_write(path.mnt);
  out:
@@@ -3720,8 -3711,8 +3710,8 @@@ static int may_mknod(umode_t mode
        }
  }
  
 -SYSCALL_DEFINE4(mknodat, int, dfd, const char __user *, filename, umode_t, mode,
 -              unsigned, dev)
 +long do_mknodat(int dfd, const char __user *filename, umode_t mode,
 +              unsigned int dev)
  {
        struct dentry *dentry;
        struct path path;
        return error;
  }
  
 +SYSCALL_DEFINE4(mknodat, int, dfd, const char __user *, filename, umode_t, mode,
 +              unsigned int, dev)
 +{
 +      return do_mknodat(dfd, filename, mode, dev);
 +}
 +
  SYSCALL_DEFINE3(mknod, const char __user *, filename, umode_t, mode, unsigned, dev)
  {
 -      return sys_mknodat(AT_FDCWD, filename, mode, dev);
 +      return do_mknodat(AT_FDCWD, filename, mode, dev);
  }
  
  int vfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
  }
  EXPORT_SYMBOL(vfs_mkdir);
  
 -SYSCALL_DEFINE3(mkdirat, int, dfd, const char __user *, pathname, umode_t, mode)
 +long do_mkdirat(int dfd, const char __user *pathname, umode_t mode)
  {
        struct dentry *dentry;
        struct path path;
@@@ -3826,14 -3811,9 +3816,14 @@@ retry
        return error;
  }
  
 +SYSCALL_DEFINE3(mkdirat, int, dfd, const char __user *, pathname, umode_t, mode)
 +{
 +      return do_mkdirat(dfd, pathname, mode);
 +}
 +
  SYSCALL_DEFINE2(mkdir, const char __user *, pathname, umode_t, mode)
  {
 -      return sys_mkdirat(AT_FDCWD, pathname, mode);
 +      return do_mkdirat(AT_FDCWD, pathname, mode);
  }
  
  int vfs_rmdir(struct inode *dir, struct dentry *dentry)
@@@ -3875,7 -3855,7 +3865,7 @@@ out
  }
  EXPORT_SYMBOL(vfs_rmdir);
  
 -static long do_rmdir(int dfd, const char __user *pathname)
 +long do_rmdir(int dfd, const char __user *pathname)
  {
        int error = 0;
        struct filename *name;
@@@ -4111,8 -4091,8 +4101,8 @@@ int vfs_symlink(struct inode *dir, stru
  }
  EXPORT_SYMBOL(vfs_symlink);
  
 -SYSCALL_DEFINE3(symlinkat, const char __user *, oldname,
 -              int, newdfd, const char __user *, newname)
 +long do_symlinkat(const char __user *oldname, int newdfd,
 +                const char __user *newname)
  {
        int error;
        struct filename *from;
@@@ -4142,15 -4122,9 +4132,15 @@@ out_putname
        return error;
  }
  
 +SYSCALL_DEFINE3(symlinkat, const char __user *, oldname,
 +              int, newdfd, const char __user *, newname)
 +{
 +      return do_symlinkat(oldname, newdfd, newname);
 +}
 +
  SYSCALL_DEFINE2(symlink, const char __user *, oldname, const char __user *, newname)
  {
 -      return sys_symlinkat(oldname, AT_FDCWD, newname);
 +      return do_symlinkat(oldname, AT_FDCWD, newname);
  }
  
  /**
@@@ -4242,8 -4216,8 +4232,8 @@@ EXPORT_SYMBOL(vfs_link)
   * with linux 2.0, and to avoid hard-linking to directories
   * and other special files.  --ADM
   */
 -SYSCALL_DEFINE5(linkat, int, olddfd, const char __user *, oldname,
 -              int, newdfd, const char __user *, newname, int, flags)
 +int do_linkat(int olddfd, const char __user *oldname, int newdfd,
 +            const char __user *newname, int flags)
  {
        struct dentry *new_dentry;
        struct path old_path, new_path;
        return error;
  }
  
 +SYSCALL_DEFINE5(linkat, int, olddfd, const char __user *, oldname,
 +              int, newdfd, const char __user *, newname, int, flags)
 +{
 +      return do_linkat(olddfd, oldname, newdfd, newname, flags);
 +}
 +
  SYSCALL_DEFINE2(link, const char __user *, oldname, const char __user *, newname)
  {
 -      return sys_linkat(AT_FDCWD, oldname, AT_FDCWD, newname, 0);
 +      return do_linkat(AT_FDCWD, oldname, AT_FDCWD, newname, 0);
  }
  
  /**
@@@ -4493,8 -4461,8 +4483,8 @@@ out
  }
  EXPORT_SYMBOL(vfs_rename);
  
 -SYSCALL_DEFINE5(renameat2, int, olddfd, const char __user *, oldname,
 -              int, newdfd, const char __user *, newname, unsigned int, flags)
 +static int do_renameat2(int olddfd, const char __user *oldname, int newdfd,
 +                      const char __user *newname, unsigned int flags)
  {
        struct dentry *old_dentry, *new_dentry;
        struct dentry *trap;
@@@ -4636,21 -4604,15 +4626,21 @@@ exit
        return error;
  }
  
 +SYSCALL_DEFINE5(renameat2, int, olddfd, const char __user *, oldname,
 +              int, newdfd, const char __user *, newname, unsigned int, flags)
 +{
 +      return do_renameat2(olddfd, oldname, newdfd, newname, flags);
 +}
 +
  SYSCALL_DEFINE4(renameat, int, olddfd, const char __user *, oldname,
                int, newdfd, const char __user *, newname)
  {
 -      return sys_renameat2(olddfd, oldname, newdfd, newname, 0);
 +      return do_renameat2(olddfd, oldname, newdfd, newname, 0);
  }
  
  SYSCALL_DEFINE2(rename, const char __user *, oldname, const char __user *, newname)
  {
 -      return sys_renameat2(AT_FDCWD, oldname, AT_FDCWD, newname, 0);
 +      return do_renameat2(AT_FDCWD, oldname, AT_FDCWD, newname, 0);
  }
  
  int vfs_whiteout(struct inode *dir, struct dentry *dentry)