x86: Fixes TLS bug causing kernel page faults
authorBarret Rhoden <brho@cs.berkeley.edu>
Wed, 3 Mar 2010 19:51:42 +0000 (11:51 -0800)
committerKevin Klues <klueska@cs.berkeley.edu>
Thu, 3 Nov 2011 00:35:39 +0000 (17:35 -0700)
When loading the gs register (or any segmentation register), the
hardware will fill in the internal segmentation registers by looking up
the segment, which can page fault.  The kernel was doing this for
unabandoned, yet unmapped, LDTs from old processes when it tried to
return from an interrupt.

Note that the current TLS implementation is vulnerable to a malicious
process mucking with the LDT.

kern/arch/i686/process.c
kern/arch/i686/trap.c
kern/arch/sparc/process.c
kern/include/process.h
kern/src/process.c

index 19cfc55..f5c7724 100644 (file)
@@ -40,3 +40,16 @@ void proc_set_syscall_retval(trapframe_t *SAFE tf, intreg_t value)
 {
        tf->tf_regs.reg_eax = value;
 }
+
+/* Called when we are currently running an address space on our core and want to
+ * abandon it.  We need a known good pgdir before releasing the old one.  We
+ * decref, since current no longer tracks the proc (and current no longer
+ * protects the cr3).  We also need to clear out the TLS registers (before
+ * unmapping the address space!) */
+void __abandon_core(void)
+{
+       asm volatile ("movw %%ax,%%gs; lldt %%ax" :: "a"(0));
+       lcr3(boot_cr3);
+       proc_decref(current, 1);
+       set_current_proc(NULL);
+}
index b0fb60e..08734ea 100644 (file)
@@ -155,19 +155,22 @@ print_trapframe(trapframe_t *tf)
        static spinlock_t ptf_lock;
 
        spin_lock_irqsave(&ptf_lock);
-       cprintf("TRAP frame at %p on core %d\n", tf, core_id());
+       printk("TRAP frame at %p on core %d\n", tf, core_id());
        print_regs(&tf->tf_regs);
-       cprintf("  gs   0x----%04x\n", tf->tf_gs);
-       cprintf("  fs   0x----%04x\n", tf->tf_fs);
-       cprintf("  es   0x----%04x\n", tf->tf_es);
-       cprintf("  ds   0x----%04x\n", tf->tf_ds);
-       cprintf("  trap 0x%08x %s\n", tf->tf_trapno, trapname(tf->tf_trapno));
-       cprintf("  err  0x%08x\n", tf->tf_err);
-       cprintf("  eip  0x%08x\n", tf->tf_eip);
-       cprintf("  cs   0x----%04x\n", tf->tf_cs);
-       cprintf("  flag 0x%08x\n", tf->tf_eflags);
-       cprintf("  esp  0x%08x\n", tf->tf_esp);
-       cprintf("  ss   0x----%04x\n", tf->tf_ss);
+       printk("  gs   0x----%04x\n", tf->tf_gs);
+       printk("  fs   0x----%04x\n", tf->tf_fs);
+       printk("  es   0x----%04x\n", tf->tf_es);
+       printk("  ds   0x----%04x\n", tf->tf_ds);
+       printk("  trap 0x%08x %s\n", tf->tf_trapno, trapname(tf->tf_trapno));
+       printk("  err  0x%08x\n", tf->tf_err);
+       printk("  eip  0x%08x\n", tf->tf_eip);
+       printk("  cs   0x----%04x\n", tf->tf_cs);
+       printk("  flag 0x%08x\n", tf->tf_eflags);
+       /* Prevents us from thinking these mean something for nested interrupts. */
+       if (tf->tf_cs != GD_KT) {
+               printk("  esp  0x%08x\n", tf->tf_esp);
+               printk("  ss   0x----%04x\n", tf->tf_ss);
+       }
        spin_unlock_irqsave(&ptf_lock);
 }
 
index a97eb9a..5a6a355 100644 (file)
@@ -34,3 +34,14 @@ void proc_set_syscall_retval(trapframe_t *SAFE tf, intreg_t value)
 {
        tf->gpr[8] = value;
 }
+
+/* Called when we are currently running an address space on our core and want to
+ * abandon it.  We need a known good pgdir before releasing the old one.  We
+ * decref, since current no longer tracks the proc (and current no longer
+ * protects the cr3). */
+void __abandon_core(void)
+{
+       lcr3(boot_cr3);
+       proc_decref(current, 1);
+       set_current_proc(NULL);
+}
index d93877b..349685b 100644 (file)
@@ -149,6 +149,7 @@ void __death(trapframe_t *tf, uint32_t srcid, void * a0, void * a1,
 void proc_init_trapframe(trapframe_t *SAFE tf, uint32_t vcoreid,
                          uint32_t entryp, uint32_t stack_top);
 void proc_set_syscall_retval(trapframe_t *SAFE tf, intreg_t value);
+void __abandon_core(void);
 
 /* Degubbing */
 void print_idlecoremap(void);
index c278608..a85e88d 100644 (file)
@@ -998,14 +998,8 @@ void __startcore(trapframe_t *tf, uint32_t srcid, void * a0, void * a1,
  * process's context. */
 void abandon_core(void)
 {
-       /* If we are currently running an address space on our core, we need a known
-        * good pgdir before releasing the old one.  We decref, since current no
-        * longer tracks the proc (and current no longer protects the cr3). */
-       if (current) {
-               lcr3(boot_cr3);
-               proc_decref(current, 1);
-               set_current_proc(NULL);
-       }
+       if (current)
+               __abandon_core();
        smp_idle();
 }