• Jens Axboe's avatar
    io_uring: fix assuming triggered poll waitqueue is the single poll · d89a4fac
    Jens Axboe authored
    syzbot reports a recent regression:
    
    BUG: KASAN: use-after-free in __wake_up_common+0x637/0x650 kernel/sched/wait.c:101
    Read of size 8 at addr ffff888011e8a130 by task syz-executor413/3618
    
    CPU: 0 PID: 3618 Comm: syz-executor413 Tainted: G        W         5.17.0-syzkaller-01402-g8565d644 #0
    Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011
    Call Trace:
     <TASK>
     __dump_stack lib/dump_stack.c:88 [inline]
     dump_stack_lvl+0xcd/0x134 lib/dump_stack.c:106
     print_address_description.constprop.0.cold+0x8d/0x303 mm/kasan/report.c:255
     __kasan_report mm/kasan/report.c:442 [inline]
     kasan_report.cold+0x83/0xdf mm/kasan/report.c:459
     __wake_up_common+0x637/0x650 kernel/sched/wait.c:101
     __wake_up_common_lock+0xd0/0x130 kernel/sched/wait.c:138
     tty_release+0x657/0x1200 drivers/tty/tty_io.c:1781
     __fput+0x286/0x9f0 fs/file_table.c:317
     task_work_run+0xdd/0x1a0 kernel/task_work.c:164
     exit_task_work include/linux/task_work.h:32 [inline]
     do_exit+0xaff/0x29d0 kernel/exit.c:806
     do_group_exit+0xd2/0x2f0 kernel/exit.c:936
     __do_sys_exit_group kernel/exit.c:947 [inline]
     __se_sys_exit_group kernel/exit.c:945 [inline]
     __x64_sys_exit_group+0x3a/0x50 kernel/exit.c:945
     do_syscall_x64 arch/x86/entry/common.c:50 [inline]
     do_syscall_64+0x35/0xb0 arch/x86/entry/common.c:80
     entry_SYSCALL_64_after_hwframe+0x44/0xae
    RIP: 0033:0x7f439a1fac69
    
    which is due to leaving the request on the waitqueue mistakenly. The
    reproducer is using a tty device, which means we end up arming the same
    poll queue twice (it uses the same poll waitqueue for both), but in
    io_poll_wake() we always just clear REQ_F_SINGLE_POLL regardless of which
    entry triggered. This leaves one waitqueue potentially armed after we're
    done, which then blows up in tty when the waitqueue is attempted removed.
    
    We have no room to store this information, so simply encode it in the
    wait_queue_entry->private where we store the io_kiocb request pointer.
    
    Fixes: 91eac1c6 ("io_uring: cache poll/double-poll state with a request flag")
    Reported-by: syzbot+09ad4050dd3a120bfccd@syzkaller.appspotmail.com
    Signed-off-by: default avatarJens Axboe <axboe@kernel.dk>
    d89a4fac
io_uring.c 296 KB