Skip to content

Commit

Permalink
debugfs: fix create mutex racy fops and private data
Browse files Browse the repository at this point in the history
Setting fops and private data outside of the mutex at debugfs file
creation introduces a race where the files can be opened with the wrong
file operations and private data.  It is easy to trigger with a process
waiting on file creation notification.

Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@polymtl.ca>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Cc: stable <stable@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
  • Loading branch information
Mathieu Desnoyers authored and Greg Kroah-Hartman committed Dec 11, 2009
1 parent 18ef545 commit d3a3b0a
Showing 1 changed file with 32 additions and 23 deletions.
55 changes: 32 additions & 23 deletions fs/debugfs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ static struct vfsmount *debugfs_mount;
static int debugfs_mount_count;
static bool debugfs_registered;

static struct inode *debugfs_get_inode(struct super_block *sb, int mode, dev_t dev)
static struct inode *debugfs_get_inode(struct super_block *sb, int mode, dev_t dev,
void *data, const struct file_operations *fops)

{
struct inode *inode = new_inode(sb);

Expand All @@ -44,14 +46,18 @@ static struct inode *debugfs_get_inode(struct super_block *sb, int mode, dev_t d
init_special_inode(inode, mode, dev);
break;
case S_IFREG:
inode->i_fop = &debugfs_file_operations;
inode->i_fop = fops ? fops : &debugfs_file_operations;
inode->i_private = data;
break;
case S_IFLNK:
inode->i_op = &debugfs_link_operations;
inode->i_fop = fops;
inode->i_private = data;
break;
case S_IFDIR:
inode->i_op = &simple_dir_inode_operations;
inode->i_fop = &simple_dir_operations;
inode->i_fop = fops ? fops : &simple_dir_operations;
inode->i_private = data;

/* directory inodes start off with i_nlink == 2
* (for "." entry) */
Expand All @@ -64,15 +70,16 @@ static struct inode *debugfs_get_inode(struct super_block *sb, int mode, dev_t d

/* SMP-safe */
static int debugfs_mknod(struct inode *dir, struct dentry *dentry,
int mode, dev_t dev)
int mode, dev_t dev, void *data,
const struct file_operations *fops)
{
struct inode *inode;
int error = -EPERM;

if (dentry->d_inode)
return -EEXIST;

inode = debugfs_get_inode(dir->i_sb, mode, dev);
inode = debugfs_get_inode(dir->i_sb, mode, dev, data, fops);
if (inode) {
d_instantiate(dentry, inode);
dget(dentry);
Expand All @@ -81,31 +88,34 @@ static int debugfs_mknod(struct inode *dir, struct dentry *dentry,
return error;
}

static int debugfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
static int debugfs_mkdir(struct inode *dir, struct dentry *dentry, int mode,
void *data, const struct file_operations *fops)
{
int res;

mode = (mode & (S_IRWXUGO | S_ISVTX)) | S_IFDIR;
res = debugfs_mknod(dir, dentry, mode, 0);
res = debugfs_mknod(dir, dentry, mode, 0, data, fops);
if (!res) {
inc_nlink(dir);
fsnotify_mkdir(dir, dentry);
}
return res;
}

static int debugfs_link(struct inode *dir, struct dentry *dentry, int mode)
static int debugfs_link(struct inode *dir, struct dentry *dentry, int mode,
void *data, const struct file_operations *fops)
{
mode = (mode & S_IALLUGO) | S_IFLNK;
return debugfs_mknod(dir, dentry, mode, 0);
return debugfs_mknod(dir, dentry, mode, 0, data, fops);
}

static int debugfs_create(struct inode *dir, struct dentry *dentry, int mode)
static int debugfs_create(struct inode *dir, struct dentry *dentry, int mode,
void *data, const struct file_operations *fops)
{
int res;

mode = (mode & S_IALLUGO) | S_IFREG;
res = debugfs_mknod(dir, dentry, mode, 0);
res = debugfs_mknod(dir, dentry, mode, 0, data, fops);
if (!res)
fsnotify_create(dir, dentry);
return res;
Expand Down Expand Up @@ -139,7 +149,9 @@ static struct file_system_type debug_fs_type = {

static int debugfs_create_by_name(const char *name, mode_t mode,
struct dentry *parent,
struct dentry **dentry)
struct dentry **dentry,
void *data,
const struct file_operations *fops)
{
int error = 0;

Expand All @@ -164,13 +176,16 @@ static int debugfs_create_by_name(const char *name, mode_t mode,
if (!IS_ERR(*dentry)) {
switch (mode & S_IFMT) {
case S_IFDIR:
error = debugfs_mkdir(parent->d_inode, *dentry, mode);
error = debugfs_mkdir(parent->d_inode, *dentry, mode,
data, fops);
break;
case S_IFLNK:
error = debugfs_link(parent->d_inode, *dentry, mode);
error = debugfs_link(parent->d_inode, *dentry, mode,
data, fops);
break;
default:
error = debugfs_create(parent->d_inode, *dentry, mode);
error = debugfs_create(parent->d_inode, *dentry, mode,
data, fops);
break;
}
dput(*dentry);
Expand Down Expand Up @@ -221,19 +236,13 @@ struct dentry *debugfs_create_file(const char *name, mode_t mode,
if (error)
goto exit;

error = debugfs_create_by_name(name, mode, parent, &dentry);
error = debugfs_create_by_name(name, mode, parent, &dentry,
data, fops);
if (error) {
dentry = NULL;
simple_release_fs(&debugfs_mount, &debugfs_mount_count);
goto exit;
}

if (dentry->d_inode) {
if (data)
dentry->d_inode->i_private = data;
if (fops)
dentry->d_inode->i_fop = fops;
}
exit:
return dentry;
}
Expand Down

0 comments on commit d3a3b0a

Please sign in to comment.