diff options
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 | */ |