Commit 87da7e66a40532b743cd50972fcf85a1f15b14ea

Authored by Xiao Guangrong
Committed by Marcelo Tosatti
1 parent 35fd3dc58d

KVM: x86: fix vcpu->mmio_fragments overflow

After commit b3356bf0dbb349 (KVM: emulator: optimize "rep ins" handling),
the pieces of io data can be collected and write them to the guest memory
or MMIO together

Unfortunately, kvm splits the mmio access into 8 bytes and store them to
vcpu->mmio_fragments. If the guest uses "rep ins" to move large data, it
will cause vcpu->mmio_fragments overflow

The bug can be exposed by isapc (-M isapc):

[23154.818733] general protection fault: 0000 [#1] SMP DEBUG_PAGEALLOC
[ ......]
[23154.858083] Call Trace:
[23154.859874]  [<ffffffffa04f0e17>] kvm_get_cr8+0x1d/0x28 [kvm]
[23154.861677]  [<ffffffffa04fa6d4>] kvm_arch_vcpu_ioctl_run+0xcda/0xe45 [kvm]
[23154.863604]  [<ffffffffa04f5a1a>] ? kvm_arch_vcpu_load+0x17b/0x180 [kvm]

Actually, we can use one mmio_fragment to store a large mmio access then
split it when we pass the mmio-exit-info to userspace. After that, we only
need two entries to store mmio info for the cross-mmio pages access

Signed-off-by: Xiao Guangrong <xiaoguangrong@linux.vnet.ibm.com>
Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com>

Showing 2 changed files with 36 additions and 39 deletions Side-by-side Diff

... ... @@ -3779,7 +3779,7 @@
3779 3779 {
3780 3780 struct kvm_mmio_fragment *frag = &vcpu->mmio_fragments[0];
3781 3781  
3782   - memcpy(vcpu->run->mmio.data, frag->data, frag->len);
  3782 + memcpy(vcpu->run->mmio.data, frag->data, min(8u, frag->len));
3783 3783 return X86EMUL_CONTINUE;
3784 3784 }
3785 3785  
... ... @@ -3832,18 +3832,11 @@
3832 3832 bytes -= handled;
3833 3833 val += handled;
3834 3834  
3835   - while (bytes) {
3836   - unsigned now = min(bytes, 8U);
3837   -
3838   - frag = &vcpu->mmio_fragments[vcpu->mmio_nr_fragments++];
3839   - frag->gpa = gpa;
3840   - frag->data = val;
3841   - frag->len = now;
3842   -
3843   - gpa += now;
3844   - val += now;
3845   - bytes -= now;
3846   - }
  3835 + WARN_ON(vcpu->mmio_nr_fragments >= KVM_MAX_MMIO_FRAGMENTS);
  3836 + frag = &vcpu->mmio_fragments[vcpu->mmio_nr_fragments++];
  3837 + frag->gpa = gpa;
  3838 + frag->data = val;
  3839 + frag->len = bytes;
3847 3840 return X86EMUL_CONTINUE;
3848 3841 }
3849 3842  
... ... @@ -3890,7 +3883,7 @@
3890 3883 vcpu->mmio_needed = 1;
3891 3884 vcpu->mmio_cur_fragment = 0;
3892 3885  
3893   - vcpu->run->mmio.len = vcpu->mmio_fragments[0].len;
  3886 + vcpu->run->mmio.len = min(8u, vcpu->mmio_fragments[0].len);
3894 3887 vcpu->run->mmio.is_write = vcpu->mmio_is_write = ops->write;
3895 3888 vcpu->run->exit_reason = KVM_EXIT_MMIO;
3896 3889 vcpu->run->mmio.phys_addr = gpa;
3897 3890  
3898 3891  
3899 3892  
3900 3893  
... ... @@ -5522,28 +5515,44 @@
5522 5515 *
5523 5516 * read:
5524 5517 * for each fragment
5525   - * write gpa, len
5526   - * exit
5527   - * copy data
  5518 + * for each mmio piece in the fragment
  5519 + * write gpa, len
  5520 + * exit
  5521 + * copy data
5528 5522 * execute insn
5529 5523 *
5530 5524 * write:
5531 5525 * for each fragment
5532   - * write gpa, len
5533   - * copy data
5534   - * exit
  5526 + * for each mmio piece in the fragment
  5527 + * write gpa, len
  5528 + * copy data
  5529 + * exit
5535 5530 */
5536 5531 static int complete_emulated_mmio(struct kvm_vcpu *vcpu)
5537 5532 {
5538 5533 struct kvm_run *run = vcpu->run;
5539 5534 struct kvm_mmio_fragment *frag;
  5535 + unsigned len;
5540 5536  
5541 5537 BUG_ON(!vcpu->mmio_needed);
5542 5538  
5543 5539 /* Complete previous fragment */
5544   - frag = &vcpu->mmio_fragments[vcpu->mmio_cur_fragment++];
  5540 + frag = &vcpu->mmio_fragments[vcpu->mmio_cur_fragment];
  5541 + len = min(8u, frag->len);
5545 5542 if (!vcpu->mmio_is_write)
5546   - memcpy(frag->data, run->mmio.data, frag->len);
  5543 + memcpy(frag->data, run->mmio.data, len);
  5544 +
  5545 + if (frag->len <= 8) {
  5546 + /* Switch to the next fragment. */
  5547 + frag++;
  5548 + vcpu->mmio_cur_fragment++;
  5549 + } else {
  5550 + /* Go forward to the next mmio piece. */
  5551 + frag->data += len;
  5552 + frag->gpa += len;
  5553 + frag->len -= len;
  5554 + }
  5555 +
5547 5556 if (vcpu->mmio_cur_fragment == vcpu->mmio_nr_fragments) {
5548 5557 vcpu->mmio_needed = 0;
5549 5558 if (vcpu->mmio_is_write)
5550 5559  
... ... @@ -5551,13 +5560,12 @@
5551 5560 vcpu->mmio_read_completed = 1;
5552 5561 return complete_emulated_io(vcpu);
5553 5562 }
5554   - /* Initiate next fragment */
5555   - ++frag;
  5563 +
5556 5564 run->exit_reason = KVM_EXIT_MMIO;
5557 5565 run->mmio.phys_addr = frag->gpa;
5558 5566 if (vcpu->mmio_is_write)
5559   - memcpy(run->mmio.data, frag->data, frag->len);
5560   - run->mmio.len = frag->len;
  5567 + memcpy(run->mmio.data, frag->data, min(8u, frag->len));
  5568 + run->mmio.len = min(8u, frag->len);
5561 5569 run->mmio.is_write = vcpu->mmio_is_write;
5562 5570 vcpu->arch.complete_userspace_io = complete_emulated_mmio;
5563 5571 return 0;
include/linux/kvm_host.h
... ... @@ -42,19 +42,8 @@
42 42 */
43 43 #define KVM_MEMSLOT_INVALID (1UL << 16)
44 44  
45   -/*
46   - * If we support unaligned MMIO, at most one fragment will be split into two:
47   - */
48   -#ifdef KVM_UNALIGNED_MMIO
49   -# define KVM_EXTRA_MMIO_FRAGMENTS 1
50   -#else
51   -# define KVM_EXTRA_MMIO_FRAGMENTS 0
52   -#endif
53   -
54   -#define KVM_USER_MMIO_SIZE 8
55   -
56   -#define KVM_MAX_MMIO_FRAGMENTS \
57   - (KVM_MMIO_SIZE / KVM_USER_MMIO_SIZE + KVM_EXTRA_MMIO_FRAGMENTS)
  45 +/* Two fragments for cross MMIO pages. */
  46 +#define KVM_MAX_MMIO_FRAGMENTS 2
58 47  
59 48 /*
60 49 * For the normal pfn, the highest 12 bits should be zero,