fs: create proper filename objects using getname_kernel()
authorPaul Moore <pmoore@redhat.com>
Thu, 22 Jan 2015 05:00:03 +0000 (00:00 -0500)
committerAl Viro <viro@zeniv.linux.org.uk>
Fri, 23 Jan 2015 05:22:20 +0000 (00:22 -0500)
There are several areas in the kernel that create temporary filename
objects using the following pattern:

int func(const char *name)
{
struct filename *file = { .name = name };
...
return 0;
}

... which for the most part works okay, but it causes havoc within the
audit subsystem as the filename object does not persist beyond the
lifetime of the function.  This patch converts all of these temporary
filename objects into proper filename objects using getname_kernel()
and putname() which ensure that the filename object persists until the
audit subsystem is finished with it.

Also, a special thanks to Al Viro, Guenter Roeck, and Sabrina Dubroca
for helping resolve a difficult kernel panic on boot related to a
use-after-free problem in kern_path_create(); the thread can be seen
at the link below:

 * https://lkml.org/lkml/2015/1/20/710

This patch includes code that was either based on, or directly written
by Al in the above thread.

CC: viro@zeniv.linux.org.uk
CC: linux@roeck-us.net
CC: sd@queasysnail.net
CC: linux-fsdevel@vger.kernel.org
Signed-off-by: Paul Moore <pmoore@redhat.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
fs/exec.c
fs/namei.c
fs/open.c

index ad8798e26be9f6e40d15607d0971bc2143611fcc..c7f9b733406dc22165647a8f415ce00324ae7daf 100644 (file)
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -794,8 +794,14 @@ exit:
 
 struct file *open_exec(const char *name)
 {
-       struct filename tmp = { .name = name };
-       return do_open_execat(AT_FDCWD, &tmp, 0);
+       struct filename *filename = getname_kernel(name);
+       struct file *f = ERR_CAST(filename);
+
+       if (!IS_ERR(filename)) {
+               f = do_open_execat(AT_FDCWD, filename, 0);
+               putname(filename);
+       }
+       return f;
 }
 EXPORT_SYMBOL(open_exec);
 
index 71dc1cc23b1a08f7467c686028e0f1d6ddae7b72..0bc7742a591dfd786504382d23abef2975cb6b03 100644 (file)
@@ -2042,32 +2042,47 @@ static int filename_lookup(int dfd, struct filename *name,
 static int do_path_lookup(int dfd, const char *name,
                                unsigned int flags, struct nameidata *nd)
 {
-       struct filename filename = { .name = name };
+       struct filename *filename = getname_kernel(name);
+       int retval = PTR_ERR(filename);
 
-       return filename_lookup(dfd, &filename, flags, nd);
+       if (!IS_ERR(filename)) {
+               retval = filename_lookup(dfd, filename, flags, nd);
+               putname(filename);
+       }
+       return retval;
 }
 
 /* does lookup, returns the object with parent locked */
 struct dentry *kern_path_locked(const char *name, struct path *path)
 {
+       struct filename *filename = getname_kernel(name);
        struct nameidata nd;
        struct dentry *d;
-       struct filename filename = {.name = name};
-       int err = filename_lookup(AT_FDCWD, &filename, LOOKUP_PARENT, &nd);
-       if (err)
-               return ERR_PTR(err);
+       int err;
+
+       if (IS_ERR(filename))
+               return ERR_CAST(filename);
+
+       err = filename_lookup(AT_FDCWD, filename, LOOKUP_PARENT, &nd);
+       if (err) {
+               d = ERR_PTR(err);
+               goto out;
+       }
        if (nd.last_type != LAST_NORM) {
                path_put(&nd.path);
-               return ERR_PTR(-EINVAL);
+               d = ERR_PTR(-EINVAL);
+               goto out;
        }
        mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
        d = __lookup_hash(&nd.last, nd.path.dentry, 0);
        if (IS_ERR(d)) {
                mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
                path_put(&nd.path);
-               return d;
+               goto out;
        }
        *path = nd.path;
+out:
+       putname(filename);
        return d;
 }
 
@@ -2399,8 +2414,14 @@ int
 kern_path_mountpoint(int dfd, const char *name, struct path *path,
                        unsigned int flags)
 {
-       struct filename s = {.name = name};
-       return filename_mountpoint(dfd, &s, path, flags);
+       struct filename *s = getname_kernel(name);
+       int retval = PTR_ERR(s);
+
+       if (!IS_ERR(s)) {
+               retval = filename_mountpoint(dfd, s, path, flags);
+               putname(s);
+       }
+       return retval;
 }
 EXPORT_SYMBOL(kern_path_mountpoint);
 
@@ -3280,7 +3301,7 @@ struct file *do_file_open_root(struct dentry *dentry, struct vfsmount *mnt,
 {
        struct nameidata nd;
        struct file *file;
-       struct filename filename = { .name = name };
+       struct filename *filename;
        int flags = op->lookup_flags | LOOKUP_ROOT;
 
        nd.root.mnt = mnt;
@@ -3289,11 +3310,16 @@ struct file *do_file_open_root(struct dentry *dentry, struct vfsmount *mnt,
        if (d_is_symlink(dentry) && op->intent & LOOKUP_OPEN)
                return ERR_PTR(-ELOOP);
 
-       file = path_openat(-1, &filename, &nd, op, flags | LOOKUP_RCU);
+       filename = getname_kernel(name);
+       if (unlikely(IS_ERR(filename)))
+               return ERR_CAST(filename);
+
+       file = path_openat(-1, filename, &nd, op, flags | LOOKUP_RCU);
        if (unlikely(file == ERR_PTR(-ECHILD)))
-               file = path_openat(-1, &filename, &nd, op, flags);
+               file = path_openat(-1, filename, &nd, op, flags);
        if (unlikely(file == ERR_PTR(-ESTALE)))
-               file = path_openat(-1, &filename, &nd, op, flags | LOOKUP_REVAL);
+               file = path_openat(-1, filename, &nd, op, flags | LOOKUP_REVAL);
+       putname(filename);
        return file;
 }
 
@@ -3370,8 +3396,14 @@ out:
 struct dentry *kern_path_create(int dfd, const char *pathname,
                                struct path *path, unsigned int lookup_flags)
 {
-       struct filename filename = {.name = pathname};
-       return filename_create(dfd, &filename, path, lookup_flags);
+       struct filename *filename = getname_kernel(pathname);
+       struct dentry *res;
+
+       if (IS_ERR(filename))
+               return ERR_CAST(filename);
+       res = filename_create(dfd, filename, path, lookup_flags);
+       putname(filename);
+       return res;
 }
 EXPORT_SYMBOL(kern_path_create);
 
index 813be037b412907e040fe2b94861cfcbf14fc91f..d36c42ff019d425e43dd2bdf21ac66842919ee54 100644 (file)
--- a/fs/open.c
+++ b/fs/open.c
@@ -971,8 +971,14 @@ struct file *file_open_name(struct filename *name, int flags, umode_t mode)
  */
 struct file *filp_open(const char *filename, int flags, umode_t mode)
 {
-       struct filename name = {.name = filename};
-       return file_open_name(&name, flags, mode);
+       struct filename *name = getname_kernel(filename);
+       struct file *file = ERR_CAST(name);
+       
+       if (!IS_ERR(name)) {
+               file = file_open_name(name, flags, mode);
+               putname(name);
+       }
+       return file;
 }
 EXPORT_SYMBOL(filp_open);