X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=fs%2Ffile.c;h=4c6f0ea12c419c52154558c271586b7887dabf13;hb=34b4a8731f50fb6fe772f1e47432bfb1da1f4edd;hp=5110acb1c9ef59bb046a69f79fa601a07218b61a;hpb=2dd550b90b03d5f236a18ae491bf6e70798469a8;p=linux-2.6 diff --git a/fs/file.c b/fs/file.c index 5110acb1c9..4c6f0ea12c 100644 --- a/fs/file.c +++ b/fs/file.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -149,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) @@ -198,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