Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

KVM: Fix stack-out-of-bounds read in write_mmio

Reported by syzkaller:

BUG: KASAN: stack-out-of-bounds in write_mmio+0x11e/0x270 [kvm]
Read of size 8 at addr ffff8803259df7f8 by task syz-executor/32298

CPU: 6 PID: 32298 Comm: syz-executor Tainted: G OE 4.15.0-rc2+ #18
Hardware name: LENOVO ThinkCentre M8500t-N000/SHARKBAY, BIOS FBKTC1AUS 02/16/2016
Call Trace:
dump_stack+0xab/0xe1
print_address_description+0x6b/0x290
kasan_report+0x28a/0x370
write_mmio+0x11e/0x270 [kvm]
emulator_read_write_onepage+0x311/0x600 [kvm]
emulator_read_write+0xef/0x240 [kvm]
emulator_fix_hypercall+0x105/0x150 [kvm]
em_hypercall+0x2b/0x80 [kvm]
x86_emulate_insn+0x2b1/0x1640 [kvm]
x86_emulate_instruction+0x39a/0xb90 [kvm]
handle_exception+0x1b4/0x4d0 [kvm_intel]
vcpu_enter_guest+0x15a0/0x2640 [kvm]
kvm_arch_vcpu_ioctl_run+0x549/0x7d0 [kvm]
kvm_vcpu_ioctl+0x479/0x880 [kvm]
do_vfs_ioctl+0x142/0x9a0
SyS_ioctl+0x74/0x80
entry_SYSCALL_64_fastpath+0x23/0x9a

The path of patched vmmcall will patch 3 bytes opcode 0F 01 C1(vmcall)
to the guest memory, however, write_mmio tracepoint always prints 8 bytes
through *(u64 *)val since kvm splits the mmio access into 8 bytes. This
leaks 5 bytes from the kernel stack (CVE-2017-17741). This patch fixes
it by just accessing the bytes which we operate on.

Before patch:

syz-executor-5567 [007] .... 51370.561696: kvm_mmio: mmio write len 3 gpa 0x10 val 0x1ffff10077c1010f

After patch:

syz-executor-13416 [002] .... 51302.299573: kvm_mmio: mmio write len 3 gpa 0x10 val 0xc1010f

Reported-by: Dmitry Vyukov <dvyukov@google.com>
Reviewed-by: Darren Kenny <darren.kenny@oracle.com>
Reviewed-by: Marc Zyngier <marc.zyngier@arm.com>
Tested-by: Marc Zyngier <marc.zyngier@arm.com>
Cc: Paolo Bonzini <pbonzini@redhat.com>
Cc: Radim Krčmář <rkrcmar@redhat.com>
Cc: Marc Zyngier <marc.zyngier@arm.com>
Cc: Christoffer Dall <christoffer.dall@linaro.org>
Signed-off-by: Wanpeng Li <wanpeng.li@hotmail.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>

authored by

Wanpeng Li and committed by
Paolo Bonzini
e39d200f f2981033

+12 -9
+4 -4
arch/x86/kvm/x86.c
··· 4384 4384 addr, n, v)) 4385 4385 && kvm_io_bus_read(vcpu, KVM_MMIO_BUS, addr, n, v)) 4386 4386 break; 4387 - trace_kvm_mmio(KVM_TRACE_MMIO_READ, n, addr, *(u64 *)v); 4387 + trace_kvm_mmio(KVM_TRACE_MMIO_READ, n, addr, v); 4388 4388 handled += n; 4389 4389 addr += n; 4390 4390 len -= n; ··· 4643 4643 { 4644 4644 if (vcpu->mmio_read_completed) { 4645 4645 trace_kvm_mmio(KVM_TRACE_MMIO_READ, bytes, 4646 - vcpu->mmio_fragments[0].gpa, *(u64 *)val); 4646 + vcpu->mmio_fragments[0].gpa, val); 4647 4647 vcpu->mmio_read_completed = 0; 4648 4648 return 1; 4649 4649 } ··· 4665 4665 4666 4666 static int write_mmio(struct kvm_vcpu *vcpu, gpa_t gpa, int bytes, void *val) 4667 4667 { 4668 - trace_kvm_mmio(KVM_TRACE_MMIO_WRITE, bytes, gpa, *(u64 *)val); 4668 + trace_kvm_mmio(KVM_TRACE_MMIO_WRITE, bytes, gpa, val); 4669 4669 return vcpu_mmio_write(vcpu, gpa, bytes, val); 4670 4670 } 4671 4671 4672 4672 static int read_exit_mmio(struct kvm_vcpu *vcpu, gpa_t gpa, 4673 4673 void *val, int bytes) 4674 4674 { 4675 - trace_kvm_mmio(KVM_TRACE_MMIO_READ_UNSATISFIED, bytes, gpa, 0); 4675 + trace_kvm_mmio(KVM_TRACE_MMIO_READ_UNSATISFIED, bytes, gpa, NULL); 4676 4676 return X86EMUL_IO_NEEDED; 4677 4677 } 4678 4678
+5 -2
include/trace/events/kvm.h
··· 211 211 { KVM_TRACE_MMIO_WRITE, "write" } 212 212 213 213 TRACE_EVENT(kvm_mmio, 214 - TP_PROTO(int type, int len, u64 gpa, u64 val), 214 + TP_PROTO(int type, int len, u64 gpa, void *val), 215 215 TP_ARGS(type, len, gpa, val), 216 216 217 217 TP_STRUCT__entry( ··· 225 225 __entry->type = type; 226 226 __entry->len = len; 227 227 __entry->gpa = gpa; 228 - __entry->val = val; 228 + __entry->val = 0; 229 + if (val) 230 + memcpy(&__entry->val, val, 231 + min_t(u32, sizeof(__entry->val), len)); 229 232 ), 230 233 231 234 TP_printk("mmio %s len %u gpa 0x%llx val 0x%llx",
+3 -3
virt/kvm/arm/mmio.c
··· 112 112 } 113 113 114 114 trace_kvm_mmio(KVM_TRACE_MMIO_READ, len, run->mmio.phys_addr, 115 - data); 115 + &data); 116 116 data = vcpu_data_host_to_guest(vcpu, data, len); 117 117 vcpu_set_reg(vcpu, vcpu->arch.mmio_decode.rt, data); 118 118 } ··· 182 182 data = vcpu_data_guest_to_host(vcpu, vcpu_get_reg(vcpu, rt), 183 183 len); 184 184 185 - trace_kvm_mmio(KVM_TRACE_MMIO_WRITE, len, fault_ipa, data); 185 + trace_kvm_mmio(KVM_TRACE_MMIO_WRITE, len, fault_ipa, &data); 186 186 kvm_mmio_write_buf(data_buf, len, data); 187 187 188 188 ret = kvm_io_bus_write(vcpu, KVM_MMIO_BUS, fault_ipa, len, 189 189 data_buf); 190 190 } else { 191 191 trace_kvm_mmio(KVM_TRACE_MMIO_READ_UNSATISFIED, len, 192 - fault_ipa, 0); 192 + fault_ipa, NULL); 193 193 194 194 ret = kvm_io_bus_read(vcpu, KVM_MMIO_BUS, fault_ipa, len, 195 195 data_buf);