oss-sec mailing list archives

Linux kernel: x86/kvm: null-ptr-deref in kvm_dirty_ring_push


From: kangel <kangel () zju edu cn>
Date: Thu, 7 Apr 2022 10:15:42 +0800 (GMT+08:00)




-----原始邮件-----
发件人:kangel <kangel () zju edu cn>
发送时间:2022-04-06 21:11:39 (星期三)
收件人: security () kernel org, linux-distros () vs openwall org
抄送: secalert () redhat com, pbonzini () redhat com, pgn () zju edu cn, qiuhao () sysec org
主题: [vs] x86/kvm: null-ptr-deref in kvm_dirty_ring_push


Hi developers,
    We found a null-ptr-deref in the kvm module which can lead to DoS. This flaw is in kvm_dirty_ring_push in 
virt/kvm/dirty_ring.c. The linux kernel version is 5.17.0-rc8. We would appreciate a CVE ID if this is a security issue.
------------[ Description ]------------
    When we call kvm_vcpu_release(), it will call kvm_dirty_ring_free() which will free ring->dirty_gfns and set it to 
NULL. Then if we can set kvm->dirty_ring_size != NULL[1] and make vcpu->arch.st.preempt to NULL[2], it will call 
kvm_dirty_ring_push() and lead to null-ptr-deref in virt/kvm/dirty_ring.c:159.
    The condition of [1] can be set by do ioctl$KVM_CAP_DIRTY_LOG_RING and the condition of [2] can be set by race of 
doing ioctf$KVM_RUN.
------------[ Reproducer ]------------
qemu run:
qemu-system-x86_64 -m 512M -smp 2 -kernel /home/zju/linux-5.17-rc8/arch/x86/boot/bzImage -append "console=ttyS0 
root=/dev/sda earlyprintk=serial net.ifnames=0 nokaslr" -drive file=/home/zju/script/stretch2.img,format=raw -net 
user,host=10.0.2.10,hostfwd=tcp:127.0.0.1:10021-:22 -net nic,model=e1000 -enable-kvm -nographic 
poc.c is attached( run in qemu).
gcc poc.c -static -o poc -lpthread

------------[ Credits ]------------
Yongkang Jia (Zhejiang University)
Gaoning Pan (Zhejiang University)
Qiuhao Li (Harbin Institute of Technology)
------------[ Backtrace ]------------
KASAN: null-ptr-deref in range [0x0000000000000030-0x0000000000000037]
CPU: 0 PID: 453 Comm: syz-executor425 Not tainted 5.17.0 #3
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.10.2-1ubuntu1~cloud0 04/01/2014
RIP: 0010:kvm_dirty_ring_push+0x10c/0x2e0 arch/x86/kvm/../../../virt/kvm/dirty_ring.c:159
Code: 0f 8e 8e 01 00 00 48 b8 00 00 00 00 00 fc ff df 41 83 ec 01 44 23 65 00 49 c1 e4 04 4c 01 e3 48 8d 7b 04 48 89 fa 
48 c1 ea 03 <0f> b6 14 02 48 89 f8 83 e0 07 83 c0 03 38 d0 7c 08 84 d2 0f 85 47
RSP: 0018:ffff88800812fb88 EFLAGS: 00010207
RAX: dffffc0000000000 RBX: 0000000000000030 RCX: ffffffffa5a929d4
RDX: 0000000000000006 RSI: 0000000000000000 RDI: 0000000000000034
RBP: ffff888004d12118 R08: 0000000000000001 R09: fffffbfff5104469
R10: 0000000000000000 R11: fffffbfff5104468 R12: 0000000000000030
R13: 0000000000000000 R14: 0000000000000000 R15: ffff888004d10dd0
FS:  0000000000868880(0000) GS:ffff88806d200000(0000) knlGS:0000000000000000
CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 0000000020ffe010 CR3: 0000000005ba4002 CR4: 00000000003726f0
DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
Call Trace:
 <TASK>
 mark_page_dirty_in_slot+0x192/0x270 arch/x86/kvm/../../../virt/kvm/kvm_main.c:3171
 kvm_steal_time_set_preempted arch/x86/kvm/x86.c:4600 [inline]
 kvm_arch_vcpu_put+0x34e/0x5b0 arch/x86/kvm/x86.c:4618
 vcpu_put+0x1b/0x70 arch/x86/kvm/../../../virt/kvm/kvm_main.c:211
 vmx_free_vcpu+0xcb/0x130 arch/x86/kvm/vmx/vmx.c:6985
 kvm_arch_vcpu_destroy+0x76/0x290 arch/x86/kvm/x86.c:11219
 kvm_vcpu_destroy arch/x86/kvm/../../../virt/kvm/kvm_main.c:441 [inline]
 kvm_destroy_vcpus+0x119/0x280 arch/x86/kvm/../../../virt/kvm/kvm_main.c:460
 kvm_free_vcpus arch/x86/kvm/x86.c:11659 [inline]
 kvm_arch_destroy_vm+0x22a/0x380 arch/x86/kvm/x86.c:11769
 kvm_destroy_vm arch/x86/kvm/../../../virt/kvm/kvm_main.c:1217 [inline]
 kvm_put_kvm+0x3ff/0x900 arch/x86/kvm/../../../virt/kvm/kvm_main.c:1250
 kvm_vcpu_release+0x4d/0x70 arch/x86/kvm/../../../virt/kvm/kvm_main.c:3668
 __fput+0x21b/0x940 fs/file_table.c:317
 task_work_run+0xde/0x180 kernel/task_work.c:164
 tracehook_notify_resume include/linux/tracehook.h:188 [inline]
 exit_to_user_mode_loop kernel/entry/common.c:175 [inline]
 exit_to_user_mode_prepare+0x14d/0x150 kernel/entry/common.c:207
 __syscall_exit_to_user_mode_work kernel/entry/common.c:289 [inline]
 syscall_exit_to_user_mode+0x1d/0x40 kernel/entry/common.c:300
 do_syscall_64+0x48/0x90 arch/x86/entry/common.c:86
 entry_SYSCALL_64_after_hwframe+0x44/0xae
------------[ Patch ]------------
We try to do a patch, which can not make the poc trigger this flaw.
diff --git a/virt/kvm/dirty_ring.c b/virt/kvm/dirty_ring.c.patch
index 222ecc8..38f1b66 100644
--- a/virt/kvm/dirty_ring.c
+++ b/virt/kvm/dirty_ring.c.patch
@@ -154,6 +154,8 @@ void kvm_dirty_ring_push(struct kvm_dirty_ring *ring, u32 slot, u64 offset)
        /* It should never get full */
        WARN_ON_ONCE(kvm_dirty_ring_full(ring));

+       if (!ring->dirty_gfns)
+               return;
        entry = &ring->dirty_gfns[ring->dirty_index & (ring->size - 1)];

        entry->slot = slot;


------------[ Cut here ]------------
C repro and kernel config are attached.
Best regards.
    Yongkang Jia of Zhejiang University

Attachment: poc.c
Description:

Attachment: config
Description:


Current thread: