lkml.org 
[lkml]   [2022]   [Mar]   [14]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 5.16 105/121] watch_queue: Fix filter limit check
    Date
    From: David Howells <dhowells@redhat.com>

    commit c993ee0f9f81caf5767a50d1faeba39a0dc82af2 upstream.

    In watch_queue_set_filter(), there are a couple of places where we check
    that the filter type value does not exceed what the type_filter bitmap
    can hold. One place calculates the number of bits by:

    if (tf[i].type >= sizeof(wfilter->type_filter) * 8)

    which is fine, but the second does:

    if (tf[i].type >= sizeof(wfilter->type_filter) * BITS_PER_LONG)

    which is not. This can lead to a couple of out-of-bounds writes due to
    a too-large type:

    (1) __set_bit() on wfilter->type_filter
    (2) Writing more elements in wfilter->filters[] than we allocated.

    Fix this by just using the proper WATCH_TYPE__NR instead, which is the
    number of types we actually know about.

    The bug may cause an oops looking something like:

    BUG: KASAN: slab-out-of-bounds in watch_queue_set_filter+0x659/0x740
    Write of size 4 at addr ffff88800d2c66bc by task watch_queue_oob/611
    ...
    Call Trace:
    <TASK>
    dump_stack_lvl+0x45/0x59
    print_address_description.constprop.0+0x1f/0x150
    ...
    kasan_report.cold+0x7f/0x11b
    ...
    watch_queue_set_filter+0x659/0x740
    ...
    __x64_sys_ioctl+0x127/0x190
    do_syscall_64+0x43/0x90
    entry_SYSCALL_64_after_hwframe+0x44/0xae

    Allocated by task 611:
    kasan_save_stack+0x1e/0x40
    __kasan_kmalloc+0x81/0xa0
    watch_queue_set_filter+0x23a/0x740
    __x64_sys_ioctl+0x127/0x190
    do_syscall_64+0x43/0x90
    entry_SYSCALL_64_after_hwframe+0x44/0xae

    The buggy address belongs to the object at ffff88800d2c66a0
    which belongs to the cache kmalloc-32 of size 32
    The buggy address is located 28 bytes inside of
    32-byte region [ffff88800d2c66a0, ffff88800d2c66c0)

    Fixes: c73be61cede5 ("pipe: Add general notification queue support")
    Reported-by: Jann Horn <jannh@google.com>
    Signed-off-by: David Howells <dhowells@redhat.com>
    Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
    ---
    include/linux/watch_queue.h | 3 ++-
    kernel/watch_queue.c | 4 ++--
    2 files changed, 4 insertions(+), 3 deletions(-)

    --- a/include/linux/watch_queue.h
    +++ b/include/linux/watch_queue.h
    @@ -28,7 +28,8 @@ struct watch_type_filter {
    struct watch_filter {
    union {
    struct rcu_head rcu;
    - unsigned long type_filter[2]; /* Bitmask of accepted types */
    + /* Bitmask of accepted types */
    + DECLARE_BITMAP(type_filter, WATCH_TYPE__NR);
    };
    u32 nr_filters; /* Number of filters */
    struct watch_type_filter filters[];
    --- a/kernel/watch_queue.c
    +++ b/kernel/watch_queue.c
    @@ -320,7 +320,7 @@ long watch_queue_set_filter(struct pipe_
    tf[i].info_mask & WATCH_INFO_LENGTH)
    goto err_filter;
    /* Ignore any unknown types */
    - if (tf[i].type >= sizeof(wfilter->type_filter) * 8)
    + if (tf[i].type >= WATCH_TYPE__NR)
    continue;
    nr_filter++;
    }
    @@ -336,7 +336,7 @@ long watch_queue_set_filter(struct pipe_

    q = wfilter->filters;
    for (i = 0; i < filter.nr_filters; i++) {
    - if (tf[i].type >= sizeof(wfilter->type_filter) * BITS_PER_LONG)
    + if (tf[i].type >= WATCH_TYPE__NR)
    continue;

    q->type = tf[i].type;

    \
     
     \ /
      Last update: 2022-03-14 13:35    [W:2.647 / U:0.004 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site