hostfs: fix races in dentry_name() and inode_name()
calculating size, then doing allocation, then filling the path is a Bad Idea(tm), since the ancestors can be renamed, leading to buffer overrun. Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
parent
c103135c14
commit
e9193059b1
|
@ -90,44 +90,58 @@ __uml_setup("hostfs=", hostfs_args,
|
||||||
);
|
);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static char *__dentry_name(struct dentry *dentry, char *name)
|
||||||
|
{
|
||||||
|
char *p = __dentry_path(dentry, name, PATH_MAX);
|
||||||
|
char *root;
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
spin_unlock(&dcache_lock);
|
||||||
|
|
||||||
|
root = dentry->d_sb->s_fs_info;
|
||||||
|
len = strlen(root);
|
||||||
|
if (IS_ERR(p)) {
|
||||||
|
__putname(name);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
strncpy(name, root, PATH_MAX);
|
||||||
|
if (len > p - name) {
|
||||||
|
__putname(name);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (p > name + len) {
|
||||||
|
char *s = name + len;
|
||||||
|
while ((*s++ = *p++) != '\0')
|
||||||
|
;
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
static char *dentry_name(struct dentry *dentry)
|
static char *dentry_name(struct dentry *dentry)
|
||||||
{
|
{
|
||||||
struct dentry *parent;
|
char *name = __getname();
|
||||||
char *root, *name;
|
if (!name)
|
||||||
int len;
|
|
||||||
|
|
||||||
len = 0;
|
|
||||||
parent = dentry;
|
|
||||||
while (parent->d_parent != parent) {
|
|
||||||
len += parent->d_name.len + 1;
|
|
||||||
parent = parent->d_parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
root = parent->d_sb->s_fs_info;
|
|
||||||
len += strlen(root);
|
|
||||||
name = kmalloc(len + 1, GFP_KERNEL);
|
|
||||||
if (name == NULL)
|
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
name[len] = '\0';
|
spin_lock(&dcache_lock);
|
||||||
parent = dentry;
|
return __dentry_name(dentry, name); /* will unlock */
|
||||||
while (parent->d_parent != parent) {
|
|
||||||
len -= parent->d_name.len + 1;
|
|
||||||
name[len] = '/';
|
|
||||||
strncpy(&name[len + 1], parent->d_name.name,
|
|
||||||
parent->d_name.len);
|
|
||||||
parent = parent->d_parent;
|
|
||||||
}
|
|
||||||
strncpy(name, root, strlen(root));
|
|
||||||
return name;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *inode_name(struct inode *ino)
|
static char *inode_name(struct inode *ino)
|
||||||
{
|
{
|
||||||
struct dentry *dentry;
|
struct dentry *dentry;
|
||||||
|
char *name = __getname();
|
||||||
|
if (!name)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
dentry = list_entry(ino->i_dentry.next, struct dentry, d_alias);
|
spin_lock(&dcache_lock);
|
||||||
return dentry_name(dentry);
|
if (list_empty(&ino->i_dentry)) {
|
||||||
|
spin_unlock(&dcache_lock);
|
||||||
|
__putname(name);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
dentry = list_first_entry(&ino->i_dentry, struct dentry, d_alias);
|
||||||
|
return __dentry_name(dentry, name); /* will unlock */
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *follow_link(char *link)
|
static char *follow_link(char *link)
|
||||||
|
@ -272,7 +286,7 @@ int hostfs_readdir(struct file *file, void *ent, filldir_t filldir)
|
||||||
if (name == NULL)
|
if (name == NULL)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
dir = open_dir(name, &error);
|
dir = open_dir(name, &error);
|
||||||
kfree(name);
|
__putname(name);
|
||||||
if (dir == NULL)
|
if (dir == NULL)
|
||||||
return -error;
|
return -error;
|
||||||
next = file->f_pos;
|
next = file->f_pos;
|
||||||
|
@ -318,7 +332,7 @@ int hostfs_file_open(struct inode *ino, struct file *file)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
fd = open_file(name, r, w, append);
|
fd = open_file(name, r, w, append);
|
||||||
kfree(name);
|
__putname(name);
|
||||||
if (fd < 0)
|
if (fd < 0)
|
||||||
return fd;
|
return fd;
|
||||||
FILE_HOSTFS_I(file)->fd = fd;
|
FILE_HOSTFS_I(file)->fd = fd;
|
||||||
|
@ -532,7 +546,7 @@ int hostfs_create(struct inode *dir, struct dentry *dentry, int mode,
|
||||||
else
|
else
|
||||||
error = read_name(inode, name);
|
error = read_name(inode, name);
|
||||||
|
|
||||||
kfree(name);
|
__putname(name);
|
||||||
if (error)
|
if (error)
|
||||||
goto out_put;
|
goto out_put;
|
||||||
|
|
||||||
|
@ -567,7 +581,7 @@ struct dentry *hostfs_lookup(struct inode *ino, struct dentry *dentry,
|
||||||
|
|
||||||
err = read_name(inode, name);
|
err = read_name(inode, name);
|
||||||
|
|
||||||
kfree(name);
|
__putname(name);
|
||||||
if (err == -ENOENT) {
|
if (err == -ENOENT) {
|
||||||
iput(inode);
|
iput(inode);
|
||||||
inode = NULL;
|
inode = NULL;
|
||||||
|
@ -594,12 +608,12 @@ int hostfs_link(struct dentry *to, struct inode *ino, struct dentry *from)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
to_name = dentry_name(to);
|
to_name = dentry_name(to);
|
||||||
if (to_name == NULL) {
|
if (to_name == NULL) {
|
||||||
kfree(from_name);
|
__putname(from_name);
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
err = link_file(to_name, from_name);
|
err = link_file(to_name, from_name);
|
||||||
kfree(from_name);
|
__putname(from_name);
|
||||||
kfree(to_name);
|
__putname(to_name);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -614,7 +628,7 @@ int hostfs_unlink(struct inode *ino, struct dentry *dentry)
|
||||||
return -EPERM;
|
return -EPERM;
|
||||||
|
|
||||||
err = unlink_file(file);
|
err = unlink_file(file);
|
||||||
kfree(file);
|
__putname(file);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -626,7 +640,7 @@ int hostfs_symlink(struct inode *ino, struct dentry *dentry, const char *to)
|
||||||
if ((file = dentry_name(dentry)) == NULL)
|
if ((file = dentry_name(dentry)) == NULL)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
err = make_symlink(file, to);
|
err = make_symlink(file, to);
|
||||||
kfree(file);
|
__putname(file);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -638,7 +652,7 @@ int hostfs_mkdir(struct inode *ino, struct dentry *dentry, int mode)
|
||||||
if ((file = dentry_name(dentry)) == NULL)
|
if ((file = dentry_name(dentry)) == NULL)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
err = do_mkdir(file, mode);
|
err = do_mkdir(file, mode);
|
||||||
kfree(file);
|
__putname(file);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -650,7 +664,7 @@ int hostfs_rmdir(struct inode *ino, struct dentry *dentry)
|
||||||
if ((file = dentry_name(dentry)) == NULL)
|
if ((file = dentry_name(dentry)) == NULL)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
err = do_rmdir(file);
|
err = do_rmdir(file);
|
||||||
kfree(file);
|
__putname(file);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -673,13 +687,13 @@ int hostfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
|
||||||
|
|
||||||
init_special_inode(inode, mode, dev);
|
init_special_inode(inode, mode, dev);
|
||||||
err = do_mknod(name, mode, MAJOR(dev), MINOR(dev));
|
err = do_mknod(name, mode, MAJOR(dev), MINOR(dev));
|
||||||
if (err)
|
if (!err)
|
||||||
goto out_free;
|
goto out_free;
|
||||||
|
|
||||||
err = read_name(inode, name);
|
err = read_name(inode, name);
|
||||||
|
__putname(name);
|
||||||
if (err)
|
if (err)
|
||||||
goto out_put;
|
goto out_put;
|
||||||
kfree(name);
|
|
||||||
if (err)
|
if (err)
|
||||||
goto out_put;
|
goto out_put;
|
||||||
|
|
||||||
|
@ -687,7 +701,7 @@ int hostfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
out_free:
|
out_free:
|
||||||
kfree(name);
|
__putname(name);
|
||||||
out_put:
|
out_put:
|
||||||
iput(inode);
|
iput(inode);
|
||||||
out:
|
out:
|
||||||
|
@ -703,12 +717,12 @@ int hostfs_rename(struct inode *from_ino, struct dentry *from,
|
||||||
if ((from_name = dentry_name(from)) == NULL)
|
if ((from_name = dentry_name(from)) == NULL)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
if ((to_name = dentry_name(to)) == NULL) {
|
if ((to_name = dentry_name(to)) == NULL) {
|
||||||
kfree(from_name);
|
__putname(from_name);
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
err = rename_file(from_name, to_name);
|
err = rename_file(from_name, to_name);
|
||||||
kfree(from_name);
|
__putname(from_name);
|
||||||
kfree(to_name);
|
__putname(to_name);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -729,7 +743,7 @@ int hostfs_permission(struct inode *ino, int desired)
|
||||||
err = 0;
|
err = 0;
|
||||||
else
|
else
|
||||||
err = access_file(name, r, w, x);
|
err = access_file(name, r, w, x);
|
||||||
kfree(name);
|
__putname(name);
|
||||||
if (!err)
|
if (!err)
|
||||||
err = generic_permission(ino, desired, NULL);
|
err = generic_permission(ino, desired, NULL);
|
||||||
return err;
|
return err;
|
||||||
|
@ -790,7 +804,7 @@ int hostfs_setattr(struct dentry *dentry, struct iattr *attr)
|
||||||
if (name == NULL)
|
if (name == NULL)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
err = set_attr(name, &attrs, fd);
|
err = set_attr(name, &attrs, fd);
|
||||||
kfree(name);
|
__putname(name);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
|
@ -845,7 +859,7 @@ static void *hostfs_follow_link(struct dentry *dentry, struct nameidata *nd)
|
||||||
int err = hostfs_do_readlink(path, link, PATH_MAX);
|
int err = hostfs_do_readlink(path, link, PATH_MAX);
|
||||||
if (err == PATH_MAX)
|
if (err == PATH_MAX)
|
||||||
err = -E2BIG;
|
err = -E2BIG;
|
||||||
kfree(path);
|
__putname(path);
|
||||||
}
|
}
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
__putname(link);
|
__putname(link);
|
||||||
|
|
Loading…
Reference in New Issue