From: Jan Kiszka <[email protected]>

arch_cpu_restore can run concurrently on multiple CPUs, but our
modifications to the GDT in order to reload Linux TR requires that a
couple of instructions run atomically. Namely, loading TR will set the
busy flag in the descriptor again which may race with another CPU just
trying to load from that descriptor as well. Putting everything under a
spinlock resolves the race.

The effect of this bug were sporadic general protection faults (#13)
while disabling Jailhouse or when enabling it failed.

Signed-off-by: Jan Kiszka <[email protected]>
---
 hypervisor/arch/x86/setup.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/hypervisor/arch/x86/setup.c b/hypervisor/arch/x86/setup.c
index 20ac7410..d3777584 100644
--- a/hypervisor/arch/x86/setup.c
+++ b/hypervisor/arch/x86/setup.c
@@ -243,6 +243,7 @@ void __attribute__((noreturn)) arch_cpu_activate_vmm(struct 
per_cpu *cpu_data)
 
 void arch_cpu_restore(struct per_cpu *cpu_data, int return_code)
 {
+       static spinlock_t tss_lock;
        unsigned int tss_idx;
        u64 *linux_gdt;
 
@@ -260,13 +261,20 @@ void arch_cpu_restore(struct per_cpu *cpu_data, int 
return_code)
        /*
         * Copy Linux TSS descriptor into our GDT, clearing the busy flag,
         * then reload TR from it. We can't use Linux' GDT as it is r/o.
+        * Access can happen concurrently on multiple CPUs, so we have to
+        * serialize the critical section.
         */
        linux_gdt = (u64 *)cpu_data->linux_gdtr.base;
        tss_idx = cpu_data->linux_tss.selector / 8;
+
+       spin_lock(&tss_lock);
+
        gdt[tss_idx] = linux_gdt[tss_idx] & ~DESC_TSS_BUSY;
        gdt[tss_idx + 1] = linux_gdt[tss_idx + 1];
        asm volatile("ltr %%ax" : : "a" (cpu_data->linux_tss.selector));
 
+       spin_unlock(&tss_lock);
+
        asm volatile("lgdtq %0" : : "m" (cpu_data->linux_gdtr));
        asm volatile("lidtq %0" : : "m" (cpu_data->linux_idtr));
 
-- 
2.12.3

-- 
You received this message because you are subscribed to the Google Groups 
"Jailhouse" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Reply via email to