KVM: x86: Access to LDT/GDT that wraparound is incorrect
authorNadav Amit <namit@cs.technion.ac.il>
Thu, 25 Dec 2014 00:52:23 +0000 (02:52 +0200)
committerPaolo Bonzini <pbonzini@redhat.com>
Thu, 8 Jan 2015 21:48:08 +0000 (22:48 +0100)
When access to descriptor in LDT/GDT wraparound outside long-mode, the address
of the descriptor should be truncated to 32-bit.  Citing Intel SDM 2.1.1.1
"Global and Local Descriptor Tables in IA-32e Mode": "GDTR and LDTR registers
are expanded to 64-bits wide in both IA-32e sub-modes (64-bit mode and
compatibility mode)."

So in other cases, we need to truncate. Creating new function to return a
pointer to descriptor table to avoid too much code duplication.

Signed-off-by: Nadav Amit <namit@cs.technion.ac.il>
[Wrap 64-bit check with #ifdef CONFIG_X86_64, to avoid a "right shift count
 >= width of type" warning and consequent undefined behavior. - Paolo]
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
arch/x86/kvm/emulate.c

index e36e1fc5bf85174dde0c44402f2fba0f4174dc5f..d949287ed01033dc5e1ff09fbc91c07cead58a2a 100644 (file)
@@ -1444,10 +1444,8 @@ static void get_descriptor_table_ptr(struct x86_emulate_ctxt *ctxt,
                ops->get_gdt(ctxt, dt);
 }
 
-/* allowed just for 8 bytes segments */
-static int read_segment_descriptor(struct x86_emulate_ctxt *ctxt,
-                                  u16 selector, struct desc_struct *desc,
-                                  ulong *desc_addr_p)
+static int get_descriptor_ptr(struct x86_emulate_ctxt *ctxt,
+                             u16 selector, ulong *desc_addr_p)
 {
        struct desc_ptr dt;
        u16 index = selector >> 3;
@@ -1458,8 +1456,34 @@ static int read_segment_descriptor(struct x86_emulate_ctxt *ctxt,
        if (dt.size < index * 8 + 7)
                return emulate_gp(ctxt, selector & 0xfffc);
 
-       *desc_addr_p = addr = dt.address + index * 8;
-       return ctxt->ops->read_std(ctxt, addr, desc, sizeof *desc,
+       addr = dt.address + index * 8;
+
+#ifdef CONFIG_X86_64
+       if (addr >> 32 != 0) {
+               u64 efer = 0;
+
+               ctxt->ops->get_msr(ctxt, MSR_EFER, &efer);
+               if (!(efer & EFER_LMA))
+                       addr &= (u32)-1;
+       }
+#endif
+
+       *desc_addr_p = addr;
+       return X86EMUL_CONTINUE;
+}
+
+/* allowed just for 8 bytes segments */
+static int read_segment_descriptor(struct x86_emulate_ctxt *ctxt,
+                                  u16 selector, struct desc_struct *desc,
+                                  ulong *desc_addr_p)
+{
+       int rc;
+
+       rc = get_descriptor_ptr(ctxt, selector, desc_addr_p);
+       if (rc != X86EMUL_CONTINUE)
+               return rc;
+
+       return ctxt->ops->read_std(ctxt, *desc_addr_p, desc, sizeof(*desc),
                                   &ctxt->exception);
 }
 
@@ -1467,16 +1491,13 @@ static int read_segment_descriptor(struct x86_emulate_ctxt *ctxt,
 static int write_segment_descriptor(struct x86_emulate_ctxt *ctxt,
                                    u16 selector, struct desc_struct *desc)
 {
-       struct desc_ptr dt;
-       u16 index = selector >> 3;
+       int rc;
        ulong addr;
 
-       get_descriptor_table_ptr(ctxt, selector, &dt);
-
-       if (dt.size < index * 8 + 7)
-               return emulate_gp(ctxt, selector & 0xfffc);
+       rc = get_descriptor_ptr(ctxt, selector, &addr);
+       if (rc != X86EMUL_CONTINUE)
+               return rc;
 
-       addr = dt.address + index * 8;
        return ctxt->ops->write_std(ctxt, addr, desc, sizeof *desc,
                                    &ctxt->exception);
 }