Commit 15a9155f authored by Al Viro's avatar Al Viro

fix race in audit_get_nd()

don't rely on pathname resolution ending up twice at the same point...
Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 586ce098
...@@ -144,9 +144,9 @@ int audit_watch_compare(struct audit_watch *watch, unsigned long ino, dev_t dev) ...@@ -144,9 +144,9 @@ int audit_watch_compare(struct audit_watch *watch, unsigned long ino, dev_t dev)
} }
/* Initialize a parent watch entry. */ /* Initialize a parent watch entry. */
static struct audit_parent *audit_init_parent(struct nameidata *ndp) static struct audit_parent *audit_init_parent(struct path *path)
{ {
struct inode *inode = ndp->path.dentry->d_inode; struct inode *inode = path->dentry->d_inode;
struct audit_parent *parent; struct audit_parent *parent;
int ret; int ret;
...@@ -353,53 +353,40 @@ static void audit_remove_parent_watches(struct audit_parent *parent) ...@@ -353,53 +353,40 @@ static void audit_remove_parent_watches(struct audit_parent *parent)
} }
/* Get path information necessary for adding watches. */ /* Get path information necessary for adding watches. */
static int audit_get_nd(char *path, struct nameidata **ndp, struct nameidata **ndw) static int audit_get_nd(struct audit_watch *watch, struct path *parent)
{ {
struct nameidata *ndparent, *ndwatch; struct nameidata nd;
struct dentry *d;
int err; int err;
ndparent = kmalloc(sizeof(*ndparent), GFP_KERNEL); err = path_lookup(watch->path, LOOKUP_PARENT, &nd);
if (unlikely(!ndparent)) if (err)
return -ENOMEM; return err;
ndwatch = kmalloc(sizeof(*ndwatch), GFP_KERNEL); if (nd.last_type != LAST_NORM) {
if (unlikely(!ndwatch)) { path_put(&nd.path);
kfree(ndparent); return -EINVAL;
return -ENOMEM;
} }
err = path_lookup(path, LOOKUP_PARENT, ndparent); mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
if (err) { d = lookup_one_len(nd.last.name, nd.path.dentry, nd.last.len);
kfree(ndparent); if (IS_ERR(d)) {
kfree(ndwatch); mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
return err; path_put(&nd.path);
return PTR_ERR(d);
} }
if (d->d_inode) {
err = path_lookup(path, 0, ndwatch); /* update watch filter fields */
if (err) { watch->dev = d->d_inode->i_sb->s_dev;
kfree(ndwatch); watch->ino = d->d_inode->i_ino;
ndwatch = NULL;
} }
mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
*ndp = ndparent; *parent = nd.path;
*ndw = ndwatch; dput(d);
return 0; return 0;
} }
/* Release resources used for watch path information. */
static void audit_put_nd(struct nameidata *ndp, struct nameidata *ndw)
{
if (ndp) {
path_put(&ndp->path);
kfree(ndp);
}
if (ndw) {
path_put(&ndw->path);
kfree(ndw);
}
}
/* Associate the given rule with an existing parent. /* Associate the given rule with an existing parent.
* Caller must hold audit_filter_mutex. */ * Caller must hold audit_filter_mutex. */
static void audit_add_to_parent(struct audit_krule *krule, static void audit_add_to_parent(struct audit_krule *krule,
...@@ -440,31 +427,24 @@ int audit_add_watch(struct audit_krule *krule, struct list_head **list) ...@@ -440,31 +427,24 @@ int audit_add_watch(struct audit_krule *krule, struct list_head **list)
{ {
struct audit_watch *watch = krule->watch; struct audit_watch *watch = krule->watch;
struct audit_parent *parent; struct audit_parent *parent;
struct nameidata *ndp = NULL, *ndw = NULL; struct path parent_path;
int h, ret = 0; int h, ret = 0;
mutex_unlock(&audit_filter_mutex); mutex_unlock(&audit_filter_mutex);
/* Avoid calling path_lookup under audit_filter_mutex. */ /* Avoid calling path_lookup under audit_filter_mutex. */
ret = audit_get_nd(watch->path, &ndp, &ndw); ret = audit_get_nd(watch, &parent_path);
if (ret) {
/* caller expects mutex locked */
mutex_lock(&audit_filter_mutex);
goto error;
}
/* caller expects mutex locked */
mutex_lock(&audit_filter_mutex); mutex_lock(&audit_filter_mutex);
/* update watch filter fields */ if (ret)
if (ndw) { return ret;
watch->dev = ndw->path.dentry->d_inode->i_sb->s_dev;
watch->ino = ndw->path.dentry->d_inode->i_ino;
}
/* either find an old parent or attach a new one */ /* either find an old parent or attach a new one */
parent = audit_find_parent(ndp->path.dentry->d_inode); parent = audit_find_parent(parent_path.dentry->d_inode);
if (!parent) { if (!parent) {
parent = audit_init_parent(ndp); parent = audit_init_parent(&parent_path);
if (IS_ERR(parent)) { if (IS_ERR(parent)) {
ret = PTR_ERR(parent); ret = PTR_ERR(parent);
goto error; goto error;
...@@ -479,9 +459,8 @@ int audit_add_watch(struct audit_krule *krule, struct list_head **list) ...@@ -479,9 +459,8 @@ int audit_add_watch(struct audit_krule *krule, struct list_head **list)
h = audit_hash_ino((u32)watch->ino); h = audit_hash_ino((u32)watch->ino);
*list = &audit_inode_hash[h]; *list = &audit_inode_hash[h];
error: error:
audit_put_nd(ndp, ndw); /* NULL args OK */ path_put(&parent_path);
return ret; return ret;
} }
void audit_remove_watch_rule(struct audit_krule *krule) void audit_remove_watch_rule(struct audit_krule *krule)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment