diff options
Diffstat (limited to 'fs/file.c')
-rw-r--r-- | fs/file.c | 23 |
1 files changed, 21 insertions, 2 deletions
@@ -12,6 +12,7 @@ | |||
12 | #include <linux/slab.h> | 12 | #include <linux/slab.h> |
13 | #include <linux/vmalloc.h> | 13 | #include <linux/vmalloc.h> |
14 | #include <linux/file.h> | 14 | #include <linux/file.h> |
15 | #include <linux/fdtable.h> | ||
15 | #include <linux/bitops.h> | 16 | #include <linux/bitops.h> |
16 | #include <linux/interrupt.h> | 17 | #include <linux/interrupt.h> |
17 | #include <linux/spinlock.h> | 18 | #include <linux/spinlock.h> |
@@ -149,8 +150,16 @@ static struct fdtable * alloc_fdtable(unsigned int nr) | |||
149 | nr /= (1024 / sizeof(struct file *)); | 150 | nr /= (1024 / sizeof(struct file *)); |
150 | nr = roundup_pow_of_two(nr + 1); | 151 | nr = roundup_pow_of_two(nr + 1); |
151 | nr *= (1024 / sizeof(struct file *)); | 152 | nr *= (1024 / sizeof(struct file *)); |
152 | if (nr > sysctl_nr_open) | 153 | /* |
153 | 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; | ||
154 | 163 | ||
155 | fdt = kmalloc(sizeof(struct fdtable), GFP_KERNEL); | 164 | fdt = kmalloc(sizeof(struct fdtable), GFP_KERNEL); |
156 | if (!fdt) | 165 | if (!fdt) |
@@ -199,6 +208,16 @@ static int expand_fdtable(struct files_struct *files, int nr) | |||
199 | if (!new_fdt) | 208 | if (!new_fdt) |
200 | return -ENOMEM; | 209 | return -ENOMEM; |
201 | /* | 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 | /* | ||
202 | * 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 |
203 | * we dropped the lock | 222 | * we dropped the lock |
204 | */ | 223 | */ |