Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 77e69dac authored by Al Viro's avatar Al Viro
Browse files

[PATCH] fix races and leaks in vfs_quota_on() users



* 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: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 1b7e190b
Loading
Loading
Loading
Loading
+20 −13
Original line number Original line Diff line number Diff line
@@ -1793,6 +1793,21 @@ static int vfs_quota_on_remount(struct super_block *sb, int type)
	return ret;
	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() */
/* Actual function called from quotactl() */
int vfs_quota_on(struct super_block *sb, int type, int format_id, char *path,
int vfs_quota_on(struct super_block *sb, int type, int format_id, char *path,
		 int remount)
		 int remount)
@@ -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);
		return vfs_quota_on_remount(sb, type);


	error = path_lookup(path, LOOKUP_FOLLOW, &nd);
	error = path_lookup(path, LOOKUP_FOLLOW, &nd);
	if (error < 0)
	if (!error) {
		return error;
		error = vfs_quota_on_path(sb, type, format_id, &nd.path);
	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);
		path_put(&nd.path);
	}
	return error;
	return error;
}
}


@@ -2185,6 +2191,7 @@ EXPORT_SYMBOL(unregister_quota_format);
EXPORT_SYMBOL(dqstats);
EXPORT_SYMBOL(dqstats);
EXPORT_SYMBOL(dq_data_lock);
EXPORT_SYMBOL(dq_data_lock);
EXPORT_SYMBOL(vfs_quota_on);
EXPORT_SYMBOL(vfs_quota_on);
EXPORT_SYMBOL(vfs_quota_on_path);
EXPORT_SYMBOL(vfs_quota_on_mount);
EXPORT_SYMBOL(vfs_quota_on_mount);
EXPORT_SYMBOL(vfs_quota_off);
EXPORT_SYMBOL(vfs_quota_off);
EXPORT_SYMBOL(vfs_quota_sync);
EXPORT_SYMBOL(vfs_quota_sync);
+2 −1
Original line number Original line Diff line number Diff line
@@ -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);
		journal_unlock_updates(EXT3_SB(sb)->s_journal);
	}
	}


	err = vfs_quota_on_path(sb, type, format_id, &nd.path);
	path_put(&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
/* Read data from quotafile - avoid pagecache and such because we cannot afford
+2 −1
Original line number Original line Diff line number Diff line
@@ -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);
		jbd2_journal_unlock_updates(EXT4_SB(sb)->s_journal);
	}
	}


	err = vfs_quota_on_path(sb, type, format_id, &nd.path);
	path_put(&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
/* Read data from quotafile - avoid pagecache and such because we cannot afford
+9 −7
Original line number Original line Diff line number Diff line
@@ -2076,8 +2076,8 @@ static int reiserfs_quota_on(struct super_block *sb, int type, int format_id,
		return err;
		return err;
	/* Quotafile not on the same filesystem? */
	/* Quotafile not on the same filesystem? */
	if (nd.path.mnt->mnt_sb != sb) {
	if (nd.path.mnt->mnt_sb != sb) {
		path_put(&nd.path);
		err = -EXDEV;
		return -EXDEV;
		goto out;
	}
	}
	inode = nd.path.dentry->d_inode;
	inode = nd.path.dentry->d_inode;
	/* We must not pack tails for quota files on reiserfs for quota IO to work */
	/* We must not pack tails for quota files on reiserfs for quota IO to work */
@@ -2087,8 +2087,8 @@ static int reiserfs_quota_on(struct super_block *sb, int type, int format_id,
			reiserfs_warning(sb,
			reiserfs_warning(sb,
				"reiserfs: Unpacking tail of quota file failed"
				"reiserfs: Unpacking tail of quota file failed"
				" (%d). Cannot turn on quotas.", err);
				" (%d). Cannot turn on quotas.", err);
			path_put(&nd.path);
			err = -EINVAL;
			return -EINVAL;
			goto out;
		}
		}
		mark_inode_dirty(inode);
		mark_inode_dirty(inode);
	}
	}
@@ -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 */
		/* Just start temporary transaction and finish it */
		err = journal_begin(&th, sb, 1);
		err = journal_begin(&th, sb, 1);
		if (err)
		if (err)
			return err;
			goto out;
		err = journal_end_sync(&th, sb, 1);
		err = journal_end_sync(&th, sb, 1);
		if (err)
		if (err)
			return err;
			goto out;
	}
	}
	err = vfs_quota_on_path(sb, type, format_id, &nd.path);
out:
	path_put(&nd.path);
	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
/* Read data from quotafile - avoid pagecache and such because we cannot afford
+2 −0
Original line number Original line Diff line number Diff line
@@ -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,
int vfs_quota_on(struct super_block *sb, int type, int format_id,
 	char *path, int remount);
 	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 vfs_quota_on_mount(struct super_block *sb, char *qf_name,
 	int format_id, int type);
 	int format_id, int type);
int vfs_quota_off(struct super_block *sb, int type, int remount);
int vfs_quota_off(struct super_block *sb, int type, int remount);