diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2008-04-27 20:04:15 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2008-05-01 13:08:57 -0400 |
commit | 5c598b3428c372a1209597cee99a70da20625876 (patch) | |
tree | 2ab9df5d60471e28074e97dca3c97b8c37d626b3 /fs/file.c | |
parent | 2030a42cecd4dd1985a2ab03e25f3cd6106a5ca8 (diff) |
[PATCH] fix sysctl_nr_open bugs
* if luser with root sets it to something that is not a multiple of
BITS_PER_LONG, the system is screwed.
* if it gets decreased at the wrong time, we can get expand_files()
returning success and _not_ increasing the size of table as asked.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs/file.c')
-rw-r--r-- | fs/file.c | 22 |
1 files changed, 20 insertions, 2 deletions
@@ -150,8 +150,16 @@ static struct fdtable * alloc_fdtable(unsigned int nr) | |||
150 | nr /= (1024 / sizeof(struct file *)); | 150 | nr /= (1024 / sizeof(struct file *)); |
151 | nr = roundup_pow_of_two(nr + 1); | 151 | nr = roundup_pow_of_two(nr + 1); |
152 | nr *= (1024 / sizeof(struct file *)); | 152 | nr *= (1024 / sizeof(struct file *)); |
153 | if (nr > sysctl_nr_open) | 153 | /* |
154 | nr = sysctl_nr_open; | 154 | * Note that this can drive nr *below* what we had passed if sysctl_nr_open |
155 | * had been set lower between the check in expand_files() and here. Deal | ||
156 | * with that in caller, it's cheaper that way. | ||
157 | * | ||
158 | * We make sure that nr remains a multiple of BITS_PER_LONG - otherwise | ||
159 | * bitmaps handling below becomes unpleasant, to put it mildly... | ||
160 | */ | ||
161 | if (unlikely(nr > sysctl_nr_open)) | ||
162 | nr = ((sysctl_nr_open - 1) | (BITS_PER_LONG - 1)) + 1; | ||
155 | 163 | ||
156 | fdt = kmalloc(sizeof(struct fdtable), GFP_KERNEL); | 164 | fdt = kmalloc(sizeof(struct fdtable), GFP_KERNEL); |
157 | if (!fdt) | 165 | if (!fdt) |
@@ -200,6 +208,16 @@ static int expand_fdtable(struct files_struct *files, int nr) | |||
200 | if (!new_fdt) | 208 | if (!new_fdt) |
201 | return -ENOMEM; | 209 | return -ENOMEM; |
202 | /* | 210 | /* |
211 | * extremely unlikely race - sysctl_nr_open decreased between the check in | ||
212 | * caller and alloc_fdtable(). Cheaper to catch it here... | ||
213 | */ | ||
214 | if (unlikely(new_fdt->max_fds <= nr)) { | ||
215 | free_fdarr(new_fdt); | ||
216 | free_fdset(new_fdt); | ||
217 | kfree(new_fdt); | ||
218 | return -EMFILE; | ||
219 | } | ||
220 | /* | ||
203 | * Check again since another task may have expanded the fd table while | 221 | * Check again since another task may have expanded the fd table while |
204 | * we dropped the lock | 222 | * we dropped the lock |
205 | */ | 223 | */ |