1
0
Fork 0

Merge branch 'getname2' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs

Pull getname/putname updates from Al Viro:
 "Rework of getname/getname_kernel/etc., mostly from Paul Moore.  Gets
  rid of quite a pile of kludges between namei and audit..."

* 'getname2' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs:
  audit: replace getname()/putname() hacks with reference counters
  audit: fix filename matching in __audit_inode() and __audit_inode_child()
  audit: enable filename recording via getname_kernel()
  simpler calling conventions for filename_mountpoint()
  fs: create proper filename objects using getname_kernel()
  fs: rework getname_kernel to handle up to PATH_MAX sized filenames
  cut down the number of do_path_lookup() callers
patches-4.0
Linus Torvalds 2015-02-17 15:27:47 -08:00
commit 05016b0f0a
7 changed files with 145 additions and 218 deletions

View File

@ -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);

View File

@ -118,15 +118,6 @@
* POSIX.1 2.4: an empty pathname is invalid (ENOENT).
* PATH_MAX includes the nul terminator --RR.
*/
void final_putname(struct filename *name)
{
if (name->separate) {
__putname(name->name);
kfree(name);
} else {
__putname(name);
}
}
#define EMBEDDED_NAME_MAX (PATH_MAX - sizeof(struct filename))
@ -145,6 +136,7 @@ getname_flags(const char __user *filename, int flags, int *empty)
result = __getname();
if (unlikely(!result))
return ERR_PTR(-ENOMEM);
result->refcnt = 1;
/*
* First, try to embed the struct filename inside the names_cache
@ -179,6 +171,7 @@ recopy:
}
result->name = kname;
result->separate = true;
result->refcnt = 1;
max = PATH_MAX;
goto recopy;
}
@ -202,7 +195,7 @@ recopy:
return result;
error:
final_putname(result);
putname(result);
return err;
}
@ -212,43 +205,56 @@ getname(const char __user * filename)
return getname_flags(filename, 0, NULL);
}
/*
* The "getname_kernel()" interface doesn't do pathnames longer
* than EMBEDDED_NAME_MAX. Deal with it - you're a kernel user.
*/
struct filename *
getname_kernel(const char * filename)
{
struct filename *result;
char *kname;
int len;
len = strlen(filename);
if (len >= EMBEDDED_NAME_MAX)
return ERR_PTR(-ENAMETOOLONG);
int len = strlen(filename) + 1;
result = __getname();
if (unlikely(!result))
return ERR_PTR(-ENOMEM);
kname = (char *)result + sizeof(*result);
result->name = kname;
if (len <= EMBEDDED_NAME_MAX) {
result->name = (char *)(result) + sizeof(*result);
result->separate = false;
} else if (len <= PATH_MAX) {
struct filename *tmp;
tmp = kmalloc(sizeof(*tmp), GFP_KERNEL);
if (unlikely(!tmp)) {
__putname(result);
return ERR_PTR(-ENOMEM);
}
tmp->name = (char *)result;
tmp->separate = true;
result = tmp;
} else {
__putname(result);
return ERR_PTR(-ENAMETOOLONG);
}
memcpy((char *)result->name, filename, len);
result->uptr = NULL;
result->aname = NULL;
result->separate = false;
result->refcnt = 1;
audit_getname(result);
strlcpy(kname, filename, EMBEDDED_NAME_MAX);
return result;
}
#ifdef CONFIG_AUDITSYSCALL
void putname(struct filename *name)
{
if (unlikely(!audit_dummy_context()))
return audit_putname(name);
final_putname(name);
BUG_ON(name->refcnt <= 0);
if (--name->refcnt > 0)
return;
if (name->separate) {
__putname(name->name);
kfree(name);
} else
__putname(name);
}
#endif
static int check_acl(struct inode *inode, int mask)
{
@ -2036,31 +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;
int err = do_path_lookup(AT_FDCWD, name, 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;
}
@ -2351,13 +2373,17 @@ static int
filename_mountpoint(int dfd, struct filename *s, struct path *path,
unsigned int flags)
{
int error = path_mountpoint(dfd, s->name, path, flags | LOOKUP_RCU);
int error;
if (IS_ERR(s))
return PTR_ERR(s);
error = path_mountpoint(dfd, s->name, path, flags | LOOKUP_RCU);
if (unlikely(error == -ECHILD))
error = path_mountpoint(dfd, s->name, path, flags);
if (unlikely(error == -ESTALE))
error = path_mountpoint(dfd, s->name, path, flags | LOOKUP_REVAL);
if (likely(!error))
audit_inode(s, path->dentry, 0);
putname(s);
return error;
}
@ -2379,21 +2405,14 @@ int
user_path_mountpoint_at(int dfd, const char __user *name, unsigned int flags,
struct path *path)
{
struct filename *s = getname(name);
int error;
if (IS_ERR(s))
return PTR_ERR(s);
error = filename_mountpoint(dfd, s, path, flags);
putname(s);
return error;
return filename_mountpoint(dfd, getname(name), path, flags);
}
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);
return filename_mountpoint(dfd, getname_kernel(name), path, flags);
}
EXPORT_SYMBOL(kern_path_mountpoint);
@ -3273,7 +3292,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;
@ -3282,15 +3301,20 @@ 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;
}
struct dentry *kern_path_create(int dfd, const char *pathname,
static struct dentry *filename_create(int dfd, struct filename *name,
struct path *path, unsigned int lookup_flags)
{
struct dentry *dentry = ERR_PTR(-EEXIST);
@ -3305,7 +3329,7 @@ struct dentry *kern_path_create(int dfd, const char *pathname,
*/
lookup_flags &= LOOKUP_REVAL;
error = do_path_lookup(dfd, pathname, LOOKUP_PARENT|lookup_flags, &nd);
error = filename_lookup(dfd, name, LOOKUP_PARENT|lookup_flags, &nd);
if (error)
return ERR_PTR(error);
@ -3359,6 +3383,19 @@ out:
path_put(&nd.path);
return dentry;
}
struct dentry *kern_path_create(int dfd, const char *pathname,
struct path *path, unsigned int 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);
void done_path_create(struct path *path, struct dentry *dentry)
@ -3377,7 +3414,7 @@ struct dentry *user_path_create(int dfd, const char __user *pathname,
struct dentry *res;
if (IS_ERR(tmp))
return ERR_CAST(tmp);
res = kern_path_create(dfd, tmp->name, path, lookup_flags);
res = filename_create(dfd, tmp, path, lookup_flags);
putname(tmp);
return res;
}

View File

@ -968,8 +968,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);

View File

@ -127,7 +127,6 @@ extern void __audit_syscall_entry(int major, unsigned long a0, unsigned long a1,
extern void __audit_syscall_exit(int ret_success, long ret_value);
extern struct filename *__audit_reusename(const __user char *uptr);
extern void __audit_getname(struct filename *name);
extern void audit_putname(struct filename *name);
#define AUDIT_INODE_PARENT 1 /* dentry represents the parent */
#define AUDIT_INODE_HIDDEN 2 /* audit record should be hidden */
@ -352,8 +351,6 @@ static inline struct filename *audit_reusename(const __user char *name)
}
static inline void audit_getname(struct filename *name)
{ }
static inline void audit_putname(struct filename *name)
{ }
static inline void __audit_inode(struct filename *name,
const struct dentry *dentry,
unsigned int flags)

View File

@ -2141,6 +2141,7 @@ struct filename {
const char *name; /* pointer to actual string */
const __user char *uptr; /* original userland pointer */
struct audit_names *aname;
int refcnt;
bool separate; /* should "name" be freed? */
};
@ -2162,6 +2163,7 @@ extern int filp_close(struct file *, fl_owner_t id);
extern struct filename *getname_flags(const char __user *, int, int *);
extern struct filename *getname(const char __user *);
extern struct filename *getname_kernel(const char *);
extern void putname(struct filename *name);
enum {
FILE_CREATED = 1,
@ -2182,15 +2184,8 @@ extern void __init vfs_caches_init(unsigned long);
extern struct kmem_cache *names_cachep;
extern void final_putname(struct filename *name);
#define __getname() kmem_cache_alloc(names_cachep, GFP_KERNEL)
#define __putname(name) kmem_cache_free(names_cachep, (void *)(name))
#ifndef CONFIG_AUDITSYSCALL
#define putname(name) final_putname(name)
#else
extern void putname(struct filename *name);
#endif
#ifdef CONFIG_BLOCK
extern int register_blkdev(unsigned int, const char *);

View File

@ -24,12 +24,6 @@
#include <linux/skbuff.h>
#include <uapi/linux/mqueue.h>
/* 0 = no checking
1 = put_count checking
2 = verbose put_count checking
*/
#define AUDIT_DEBUG 0
/* AUDIT_NAMES is the number of slots we reserve in the audit_context
* for saving names from getname(). If we get more names we will allocate
* a name dynamically and also add those to the list anchored by names_list. */
@ -74,9 +68,8 @@ struct audit_cap_data {
};
};
/* When fs/namei.c:getname() is called, we store the pointer in name and
* we don't let putname() free it (instead we free all of the saved
* pointers at syscall exit time).
/* When fs/namei.c:getname() is called, we store the pointer in name and bump
* the refcnt in the associated filename struct.
*
* Further, in fs/namei.c:path_lookup() we store the inode and device.
*/
@ -86,7 +79,6 @@ struct audit_names {
struct filename *name;
int name_len; /* number of chars to log */
bool hidden; /* don't log this record */
bool name_put; /* call __putname()? */
unsigned long ino;
dev_t dev;
@ -208,11 +200,6 @@ struct audit_context {
};
int fds[2];
struct audit_proctitle proctitle;
#if AUDIT_DEBUG
int put_count;
int ino_count;
#endif
};
extern u32 audit_ever_enabled;

View File

@ -866,33 +866,10 @@ static inline void audit_free_names(struct audit_context *context)
{
struct audit_names *n, *next;
#if AUDIT_DEBUG == 2
if (context->put_count + context->ino_count != context->name_count) {
int i = 0;
pr_err("%s:%d(:%d): major=%d in_syscall=%d"
" name_count=%d put_count=%d ino_count=%d"
" [NOT freeing]\n", __FILE__, __LINE__,
context->serial, context->major, context->in_syscall,
context->name_count, context->put_count,
context->ino_count);
list_for_each_entry(n, &context->names_list, list) {
pr_err("names[%d] = %p = %s\n", i++, n->name,
n->name->name ?: "(null)");
}
dump_stack();
return;
}
#endif
#if AUDIT_DEBUG
context->put_count = 0;
context->ino_count = 0;
#endif
list_for_each_entry_safe(n, next, &context->names_list, list) {
list_del(&n->list);
if (n->name && n->name_put)
final_putname(n->name);
if (n->name)
putname(n->name);
if (n->should_free)
kfree(n);
}
@ -1711,9 +1688,6 @@ static struct audit_names *audit_alloc_name(struct audit_context *context,
list_add_tail(&aname->list, &context->names_list);
context->name_count++;
#if AUDIT_DEBUG
context->ino_count++;
#endif
return aname;
}
@ -1734,8 +1708,10 @@ __audit_reusename(const __user char *uptr)
list_for_each_entry(n, &context->names_list, list) {
if (!n->name)
continue;
if (n->name->uptr == uptr)
if (n->name->uptr == uptr) {
n->name->refcnt++;
return n->name;
}
}
return NULL;
}
@ -1752,19 +1728,8 @@ void __audit_getname(struct filename *name)
struct audit_context *context = current->audit_context;
struct audit_names *n;
if (!context->in_syscall) {
#if AUDIT_DEBUG == 2
pr_err("%s:%d(:%d): ignoring getname(%p)\n",
__FILE__, __LINE__, context->serial, name);
dump_stack();
#endif
if (!context->in_syscall)
return;
}
#if AUDIT_DEBUG
/* The filename _must_ have a populated ->name */
BUG_ON(!name->name);
#endif
n = audit_alloc_name(context, AUDIT_TYPE_UNKNOWN);
if (!n)
@ -1772,56 +1737,13 @@ void __audit_getname(struct filename *name)
n->name = name;
n->name_len = AUDIT_NAME_FULL;
n->name_put = true;
name->aname = n;
name->refcnt++;
if (!context->pwd.dentry)
get_fs_pwd(current->fs, &context->pwd);
}
/* audit_putname - intercept a putname request
* @name: name to intercept and delay for putname
*
* If we have stored the name from getname in the audit context,
* then we delay the putname until syscall exit.
* Called from include/linux/fs.h:putname().
*/
void audit_putname(struct filename *name)
{
struct audit_context *context = current->audit_context;
BUG_ON(!context);
if (!name->aname || !context->in_syscall) {
#if AUDIT_DEBUG == 2
pr_err("%s:%d(:%d): final_putname(%p)\n",
__FILE__, __LINE__, context->serial, name);
if (context->name_count) {
struct audit_names *n;
int i = 0;
list_for_each_entry(n, &context->names_list, list)
pr_err("name[%d] = %p = %s\n", i++, n->name,
n->name->name ?: "(null)");
}
#endif
final_putname(name);
}
#if AUDIT_DEBUG
else {
++context->put_count;
if (context->put_count > context->name_count) {
pr_err("%s:%d(:%d): major=%d in_syscall=%d putname(%p)"
" name_count=%d put_count=%d\n",
__FILE__, __LINE__,
context->serial, context->major,
context->in_syscall, name->name,
context->name_count, context->put_count);
dump_stack();
}
}
#endif
}
/**
* __audit_inode - store the inode and device from a lookup
* @name: name being audited
@ -1842,10 +1764,6 @@ void __audit_inode(struct filename *name, const struct dentry *dentry,
if (!name)
goto out_alloc;
#if AUDIT_DEBUG
/* The struct filename _must_ have a populated ->name */
BUG_ON(!name->name);
#endif
/*
* If we have a pointer to an audit_names entry already, then we can
* just use it directly if the type is correct.
@ -1863,7 +1781,17 @@ void __audit_inode(struct filename *name, const struct dentry *dentry,
}
list_for_each_entry_reverse(n, &context->names_list, list) {
if (!n->name || strcmp(n->name->name, name->name))
if (n->ino) {
/* valid inode number, use that for the comparison */
if (n->ino != inode->i_ino ||
n->dev != inode->i_sb->s_dev)
continue;
} else if (n->name) {
/* inode number has not been set, check the name */
if (strcmp(n->name->name, name->name))
continue;
} else
/* no inode and no name (?!) ... this is odd ... */
continue;
/* match the correct record type */
@ -1882,44 +1810,11 @@ out_alloc:
n = audit_alloc_name(context, AUDIT_TYPE_UNKNOWN);
if (!n)
return;
/* unfortunately, while we may have a path name to record with the
* inode, we can't always rely on the string lasting until the end of
* the syscall so we need to create our own copy, it may fail due to
* memory allocation issues, but we do our best */
if (name) {
/* we can't use getname_kernel() due to size limits */
size_t len = strlen(name->name) + 1;
struct filename *new = __getname();
if (unlikely(!new))
goto out;
if (len <= (PATH_MAX - sizeof(*new))) {
new->name = (char *)(new) + sizeof(*new);
new->separate = false;
} else if (len <= PATH_MAX) {
/* this looks odd, but is due to final_putname() */
struct filename *new2;
new2 = kmalloc(sizeof(*new2), GFP_KERNEL);
if (unlikely(!new2)) {
__putname(new);
goto out;
}
new2->name = (char *)new;
new2->separate = true;
new = new2;
} else {
/* we should never get here, but let's be safe */
__putname(new);
goto out;
}
strlcpy((char *)new->name, name->name, len);
new->uptr = NULL;
new->aname = n;
n->name = new;
n->name_put = true;
n->name = name;
name->refcnt++;
}
out:
if (parent) {
n->name_len = n->name ? parent_len(n->name->name) : AUDIT_NAME_FULL;
@ -1970,11 +1865,16 @@ void __audit_inode_child(const struct inode *parent,
/* look for a parent entry first */
list_for_each_entry(n, &context->names_list, list) {
if (!n->name || n->type != AUDIT_TYPE_PARENT)
if (!n->name ||
(n->type != AUDIT_TYPE_PARENT &&
n->type != AUDIT_TYPE_UNKNOWN))
continue;
if (n->ino == parent->i_ino &&
!audit_compare_dname_path(dname, n->name->name, n->name_len)) {
if (n->ino == parent->i_ino && n->dev == parent->i_sb->s_dev &&
!audit_compare_dname_path(dname,
n->name->name, n->name_len)) {
if (n->type == AUDIT_TYPE_UNKNOWN)
n->type = AUDIT_TYPE_PARENT;
found_parent = n;
break;
}
@ -1983,11 +1883,8 @@ void __audit_inode_child(const struct inode *parent,
/* is there a matching child entry? */
list_for_each_entry(n, &context->names_list, list) {
/* can only match entries that have a name */
if (!n->name || n->type != type)
continue;
/* if we found a parent, make sure this one is a child of it */
if (found_parent && (n->name != found_parent->name))
if (!n->name ||
(n->type != type && n->type != AUDIT_TYPE_UNKNOWN))
continue;
if (!strcmp(dname, n->name->name) ||
@ -1995,6 +1892,8 @@ void __audit_inode_child(const struct inode *parent,
found_parent ?
found_parent->name_len :
AUDIT_NAME_FULL)) {
if (n->type == AUDIT_TYPE_UNKNOWN)
n->type = type;
found_child = n;
break;
}
@ -2019,10 +1918,10 @@ void __audit_inode_child(const struct inode *parent,
if (found_parent) {
found_child->name = found_parent->name;
found_child->name_len = AUDIT_NAME_FULL;
/* don't call __putname() */
found_child->name_put = false;
found_child->name->refcnt++;
}
}
if (inode)
audit_copy_inode(found_child, dentry, inode);
else