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) | |
download | linux-stable-5c598b3428c372a1209597cee99a70da20625876.tar.gz linux-stable-5c598b3428c372a1209597cee99a70da20625876.tar.bz2 linux-stable-5c598b3428c372a1209597cee99a70da20625876.zip |
[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
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) @@ -200,6 +208,16 @@ static int expand_fdtable(struct files_struct *files, int nr) 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 */ |