lkml.org 
[lkml]   [2018]   [Jun]   [7]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Date
    Subject[PATCH 3.16 207/410] pipe: avoid round_pipe_size() nr_pages overflow on 32-bit
    3.16.57-rc1 review patch.  If anyone has any objections, please let me know.

    ------------------

    From: Joe Lawrence <joe.lawrence@redhat.com>

    commit d3f14c485867cfb2e0c48aa88c41d0ef4bf5209c upstream.

    round_pipe_size() contains a right-bit-shift expression which may
    overflow, which would cause undefined results in a subsequent
    roundup_pow_of_two() call.

    static inline unsigned int round_pipe_size(unsigned int size)
    {
    unsigned long nr_pages;

    nr_pages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
    return roundup_pow_of_two(nr_pages) << PAGE_SHIFT;
    }

    PAGE_SIZE is defined as (1UL << PAGE_SHIFT), so:
    - 4 bytes wide on 32-bit (0 to 0xffffffff)
    - 8 bytes wide on 64-bit (0 to 0xffffffffffffffff)

    That means that 32-bit round_pipe_size(), nr_pages may overflow to 0:

    size=0x00000000 nr_pages=0x0
    size=0x00000001 nr_pages=0x1
    size=0xfffff000 nr_pages=0xfffff
    size=0xfffff001 nr_pages=0x0 << !
    size=0xffffffff nr_pages=0x0 << !

    This is bad because roundup_pow_of_two(n) is undefined when n == 0!

    64-bit is not a problem as the unsigned int size is 4 bytes wide
    (similar to 32-bit) and the larger, 8 byte wide unsigned long, is
    sufficient to handle the largest value of the bit shift expression:

    size=0xffffffff nr_pages=100000

    Modify round_pipe_size() to return 0 if n == 0 and updates its callers to
    handle accordingly.

    Link: http://lkml.kernel.org/r/1507658689-11669-3-git-send-email-joe.lawrence@redhat.com
    Signed-off-by: Joe Lawrence <joe.lawrence@redhat.com>
    Reported-by: Mikulas Patocka <mpatocka@redhat.com>
    Reviewed-by: Mikulas Patocka <mpatocka@redhat.com>
    Cc: Al Viro <viro@zeniv.linux.org.uk>
    Cc: Jens Axboe <axboe@kernel.dk>
    Cc: Michael Kerrisk <mtk.manpages@gmail.com>
    Cc: Randy Dunlap <rdunlap@infradead.org>
    Cc: Josh Poimboeuf <jpoimboe@redhat.com>
    Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
    Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
    [bwh: Backported to 3.16: adjust context]
    Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
    ---
    fs/pipe.c | 17 +++++++++++++++--
    1 file changed, 15 insertions(+), 2 deletions(-)

    --- a/fs/pipe.c
    +++ b/fs/pipe.c
    @@ -1006,13 +1006,19 @@ const struct file_operations pipefifo_fo

    /*
    * Currently we rely on the pipe array holding a power-of-2 number
    - * of pages.
    + * of pages. Returns 0 on error.
    */
    static inline unsigned int round_pipe_size(unsigned int size)
    {
    unsigned long nr_pages;

    + if (size < pipe_min_size)
    + size = pipe_min_size;
    +
    nr_pages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
    + if (nr_pages == 0)
    + return 0;
    +
    return roundup_pow_of_two(nr_pages) << PAGE_SHIFT;
    }

    @@ -1028,6 +1034,8 @@ static long pipe_set_size(struct pipe_in
    long ret = 0;

    size = round_pipe_size(arg);
    + if (size == 0)
    + return -EINVAL;
    nr_pages = size >> PAGE_SHIFT;

    if (!nr_pages)
    @@ -1110,13 +1118,18 @@ out_revert_acct:
    int pipe_proc_fn(struct ctl_table *table, int write, void __user *buf,
    size_t *lenp, loff_t *ppos)
    {
    + unsigned int rounded_pipe_max_size;
    int ret;

    ret = proc_dointvec_minmax(table, write, buf, lenp, ppos);
    if (ret < 0 || !write)
    return ret;

    - pipe_max_size = round_pipe_size(pipe_max_size);
    + rounded_pipe_max_size = round_pipe_size(pipe_max_size);
    + if (rounded_pipe_max_size == 0)
    + return -EINVAL;
    +
    + pipe_max_size = rounded_pipe_max_size;
    return ret;
    }

    \
     
     \ /
      Last update: 2018-06-07 17:27    [W:2.169 / U:1.160 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site