Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[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: Al Viro <viro@zeniv.linux.org.uk>

Al Viro 77e69dac 1b7e190b

+35 -22
+20 -13
fs/dquot.c
··· 1793 1793 return ret; 1794 1794 } 1795 1795 1796 + int vfs_quota_on_path(struct super_block *sb, int type, int format_id, 1797 + struct path *path) 1798 + { 1799 + int error = security_quota_on(path->dentry); 1800 + if (error) 1801 + return error; 1802 + /* Quota file not on the same filesystem? */ 1803 + if (path->mnt->mnt_sb != sb) 1804 + error = -EXDEV; 1805 + else 1806 + error = vfs_quota_on_inode(path->dentry->d_inode, type, 1807 + format_id); 1808 + return error; 1809 + } 1810 + 1796 1811 /* Actual function called from quotactl() */ 1797 1812 int vfs_quota_on(struct super_block *sb, int type, int format_id, char *path, 1798 1813 int remount) ··· 1819 1804 return vfs_quota_on_remount(sb, type); 1820 1805 1821 1806 error = path_lookup(path, LOOKUP_FOLLOW, &nd); 1822 - if (error < 0) 1823 - return error; 1824 - error = security_quota_on(nd.path.dentry); 1825 - if (error) 1826 - goto out_path; 1827 - /* Quota file not on the same filesystem? */ 1828 - if (nd.path.mnt->mnt_sb != sb) 1829 - error = -EXDEV; 1830 - else 1831 - error = vfs_quota_on_inode(nd.path.dentry->d_inode, type, 1832 - format_id); 1833 - out_path: 1834 - path_put(&nd.path); 1807 + if (!error) { 1808 + error = vfs_quota_on_path(sb, type, format_id, &nd.path); 1809 + path_put(&nd.path); 1810 + } 1835 1811 return error; 1836 1812 } 1837 1813 ··· 2191 2185 EXPORT_SYMBOL(dqstats); 2192 2186 EXPORT_SYMBOL(dq_data_lock); 2193 2187 EXPORT_SYMBOL(vfs_quota_on); 2188 + EXPORT_SYMBOL(vfs_quota_on_path); 2194 2189 EXPORT_SYMBOL(vfs_quota_on_mount); 2195 2190 EXPORT_SYMBOL(vfs_quota_off); 2196 2191 EXPORT_SYMBOL(vfs_quota_sync);
+2 -1
fs/ext3/super.c
··· 2810 2810 journal_unlock_updates(EXT3_SB(sb)->s_journal); 2811 2811 } 2812 2812 2813 + err = vfs_quota_on_path(sb, type, format_id, &nd.path); 2813 2814 path_put(&nd.path); 2814 - return vfs_quota_on(sb, type, format_id, path, remount); 2815 + return err; 2815 2816 } 2816 2817 2817 2818 /* Read data from quotafile - avoid pagecache and such because we cannot afford
+2 -1
fs/ext4/super.c
··· 3352 3352 jbd2_journal_unlock_updates(EXT4_SB(sb)->s_journal); 3353 3353 } 3354 3354 3355 + err = vfs_quota_on_path(sb, type, format_id, &nd.path); 3355 3356 path_put(&nd.path); 3356 - return vfs_quota_on(sb, type, format_id, path, remount); 3357 + return err; 3357 3358 } 3358 3359 3359 3360 /* Read data from quotafile - avoid pagecache and such because we cannot afford
+9 -7
fs/reiserfs/super.c
··· 2076 2076 return err; 2077 2077 /* Quotafile not on the same filesystem? */ 2078 2078 if (nd.path.mnt->mnt_sb != sb) { 2079 - path_put(&nd.path); 2080 - return -EXDEV; 2079 + err = -EXDEV; 2080 + goto out; 2081 2081 } 2082 2082 inode = nd.path.dentry->d_inode; 2083 2083 /* We must not pack tails for quota files on reiserfs for quota IO to work */ ··· 2087 2087 reiserfs_warning(sb, 2088 2088 "reiserfs: Unpacking tail of quota file failed" 2089 2089 " (%d). Cannot turn on quotas.", err); 2090 - path_put(&nd.path); 2091 - return -EINVAL; 2090 + err = -EINVAL; 2091 + goto out; 2092 2092 } 2093 2093 mark_inode_dirty(inode); 2094 2094 } ··· 2109 2109 /* Just start temporary transaction and finish it */ 2110 2110 err = journal_begin(&th, sb, 1); 2111 2111 if (err) 2112 - return err; 2112 + goto out; 2113 2113 err = journal_end_sync(&th, sb, 1); 2114 2114 if (err) 2115 - return err; 2115 + goto out; 2116 2116 } 2117 + err = vfs_quota_on_path(sb, type, format_id, &nd.path); 2118 + out: 2117 2119 path_put(&nd.path); 2118 - return vfs_quota_on(sb, type, format_id, path, 0); 2120 + return err; 2119 2121 } 2120 2122 2121 2123 /* Read data from quotafile - avoid pagecache and such because we cannot afford
+2
include/linux/quotaops.h
··· 43 43 44 44 int vfs_quota_on(struct super_block *sb, int type, int format_id, 45 45 char *path, int remount); 46 + int vfs_quota_on_path(struct super_block *sb, int type, int format_id, 47 + struct path *path); 46 48 int vfs_quota_on_mount(struct super_block *sb, char *qf_name, 47 49 int format_id, int type); 48 50 int vfs_quota_off(struct super_block *sb, int type, int remount);