Skip to content

Commit

Permalink
[PATCH] fix races and leaks in vfs_quota_on() users
Browse files Browse the repository at this point in the history
* new helper: vfs_quota_on_path(); equivalent of vfs_quota_on() sans the
  pathname resolution.
* callers of vfs_quota_on() that do their own pathname resolution and
  checks based on it are switched to vfs_quota_on_path(); that way we
  avoid the races.
* reiserfs leaked dentry/vfsmount references on several failure exits.

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
  • Loading branch information
Al Viro committed Aug 1, 2008
1 parent 1b7e190 commit 77e69da
Show file tree
Hide file tree
Showing 5 changed files with 35 additions and 22 deletions.
33 changes: 20 additions & 13 deletions fs/dquot.c
Original file line number Diff line number Diff line change
Expand Up @@ -1793,6 +1793,21 @@ static int vfs_quota_on_remount(struct super_block *sb, int type)
return ret;
}

int vfs_quota_on_path(struct super_block *sb, int type, int format_id,
struct path *path)
{
int error = security_quota_on(path->dentry);
if (error)
return error;
/* Quota file not on the same filesystem? */
if (path->mnt->mnt_sb != sb)
error = -EXDEV;
else
error = vfs_quota_on_inode(path->dentry->d_inode, type,
format_id);
return error;
}

/* Actual function called from quotactl() */
int vfs_quota_on(struct super_block *sb, int type, int format_id, char *path,
int remount)
Expand All @@ -1804,19 +1819,10 @@ int vfs_quota_on(struct super_block *sb, int type, int format_id, char *path,
return vfs_quota_on_remount(sb, type);

error = path_lookup(path, LOOKUP_FOLLOW, &nd);
if (error < 0)
return error;
error = security_quota_on(nd.path.dentry);
if (error)
goto out_path;
/* Quota file not on the same filesystem? */
if (nd.path.mnt->mnt_sb != sb)
error = -EXDEV;
else
error = vfs_quota_on_inode(nd.path.dentry->d_inode, type,
format_id);
out_path:
path_put(&nd.path);
if (!error) {
error = vfs_quota_on_path(sb, type, format_id, &nd.path);
path_put(&nd.path);
}
return error;
}

Expand Down Expand Up @@ -2185,6 +2191,7 @@ EXPORT_SYMBOL(unregister_quota_format);
EXPORT_SYMBOL(dqstats);
EXPORT_SYMBOL(dq_data_lock);
EXPORT_SYMBOL(vfs_quota_on);
EXPORT_SYMBOL(vfs_quota_on_path);
EXPORT_SYMBOL(vfs_quota_on_mount);
EXPORT_SYMBOL(vfs_quota_off);
EXPORT_SYMBOL(vfs_quota_sync);
Expand Down
3 changes: 2 additions & 1 deletion fs/ext3/super.c
Original file line number Diff line number Diff line change
Expand Up @@ -2810,8 +2810,9 @@ static int ext3_quota_on(struct super_block *sb, int type, int format_id,
journal_unlock_updates(EXT3_SB(sb)->s_journal);
}

err = vfs_quota_on_path(sb, type, format_id, &nd.path);
path_put(&nd.path);
return vfs_quota_on(sb, type, format_id, path, remount);
return err;
}

/* Read data from quotafile - avoid pagecache and such because we cannot afford
Expand Down
3 changes: 2 additions & 1 deletion fs/ext4/super.c
Original file line number Diff line number Diff line change
Expand Up @@ -3352,8 +3352,9 @@ static int ext4_quota_on(struct super_block *sb, int type, int format_id,
jbd2_journal_unlock_updates(EXT4_SB(sb)->s_journal);
}

err = vfs_quota_on_path(sb, type, format_id, &nd.path);
path_put(&nd.path);
return vfs_quota_on(sb, type, format_id, path, remount);
return err;
}

/* Read data from quotafile - avoid pagecache and such because we cannot afford
Expand Down
16 changes: 9 additions & 7 deletions fs/reiserfs/super.c
Original file line number Diff line number Diff line change
Expand Up @@ -2076,8 +2076,8 @@ static int reiserfs_quota_on(struct super_block *sb, int type, int format_id,
return err;
/* Quotafile not on the same filesystem? */
if (nd.path.mnt->mnt_sb != sb) {
path_put(&nd.path);
return -EXDEV;
err = -EXDEV;
goto out;
}
inode = nd.path.dentry->d_inode;
/* We must not pack tails for quota files on reiserfs for quota IO to work */
Expand All @@ -2087,8 +2087,8 @@ static int reiserfs_quota_on(struct super_block *sb, int type, int format_id,
reiserfs_warning(sb,
"reiserfs: Unpacking tail of quota file failed"
" (%d). Cannot turn on quotas.", err);
path_put(&nd.path);
return -EINVAL;
err = -EINVAL;
goto out;
}
mark_inode_dirty(inode);
}
Expand All @@ -2109,13 +2109,15 @@ static int reiserfs_quota_on(struct super_block *sb, int type, int format_id,
/* Just start temporary transaction and finish it */
err = journal_begin(&th, sb, 1);
if (err)
return err;
goto out;
err = journal_end_sync(&th, sb, 1);
if (err)
return err;
goto out;
}
err = vfs_quota_on_path(sb, type, format_id, &nd.path);
out:
path_put(&nd.path);
return vfs_quota_on(sb, type, format_id, path, 0);
return err;
}

/* Read data from quotafile - avoid pagecache and such because we cannot afford
Expand Down
2 changes: 2 additions & 0 deletions include/linux/quotaops.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ int dquot_mark_dquot_dirty(struct dquot *dquot);

int vfs_quota_on(struct super_block *sb, int type, int format_id,
char *path, int remount);
int vfs_quota_on_path(struct super_block *sb, int type, int format_id,
struct path *path);
int vfs_quota_on_mount(struct super_block *sb, char *qf_name,
int format_id, int type);
int vfs_quota_off(struct super_block *sb, int type, int remount);
Expand Down

0 comments on commit 77e69da

Please sign in to comment.