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
parent: 2030a42cecd4dd1985a2ab03e25f3cd6106a5ca8
Commit Summary:
Diffstat:
1 file changed, 20 insertions, 2 deletions
diff --git a/fs/file.c b/fs/file.c
index f6fbcb49faf7..4c6f0ea12c41 100644
--- a/fs/file.c
+++ b/fs/file.c
@@ -150,8 +150,16 @@ static struct fdtable * alloc_fdtable(unsigned int nr)
nr /= (1024 / sizeof(struct file *));
nr = roundup_pow_of_two(nr + 1);
nr *= (1024 / sizeof(struct file *));
- if (nr > sysctl_nr_open)
- nr = sysctl_nr_open;
+ /*
+ * Note that this can drive nr *below* what we had passed if sysctl_nr_open
+ * had been set lower between the check in expand_files() and here. Deal
+ * with that in caller, it's cheaper that way.
+ *
+ * We make sure that nr remains a multiple of BITS_PER_LONG - otherwise
+ * bitmaps handling below becomes unpleasant, to put it mildly...
+ */
+ if (unlikely(nr > sysctl_nr_open))
+ nr = ((sysctl_nr_open - 1) | (BITS_PER_LONG - 1)) + 1;
fdt = kmalloc(sizeof(struct fdtable), GFP_KERNEL);
if (!fdt)
@@ -199,6 +207,16 @@ static int expand_fdtable(struct files_struct *files, int nr)
spin_lock(&files->file_lock);
if (!new_fdt)
return -ENOMEM;
+ /*
+ * extremely unlikely race - sysctl_nr_open decreased between the check in
+ * caller and alloc_fdtable(). Cheaper to catch it here...
+ */
+ if (unlikely(new_fdt->max_fds <= nr)) {
+ free_fdarr(new_fdt);
+ free_fdset(new_fdt);
+ kfree(new_fdt);
+ return -EMFILE;
+ }
/*
* Check again since another task may have expanded the fd table while
* we dropped the lock