From f98c3ea74ecd155eb7ad7b4c1b2de22eadfc2272 Mon Sep 17 00:00:00 2001 From: Konstantin Belousov Date: Tue, 2 Sep 2008 17:52:11 +0000 Subject: [PATCH] - When executing FreeBSD/amd64 binaries from FreeBSD/i386 or Linux/i386 processes, clear PCB_32BIT and PCB_GS32BIT bits [1]. - Reread the fs and gs bases from the msr unconditionally, not believing the values in pcb_fsbase and pcb_gsbase, since usermode may reload segment registers, invalidating the cache. [2]. Both problems resulted in the wrong fs base, causing wrong tls pointer be dereferenced in the usermode. Reported and tested by: Vyacheslav Bocharov [1] Reported by: Bernd Walter , Artem Belevich [2] Reviewed by: peter MFC after: 3 days --- sys/amd64/amd64/cpu_switch.S | 20 ++++++++++++++++++-- sys/amd64/amd64/machdep.c | 1 + sys/amd64/ia32/ia32_signal.c | 1 + 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/sys/amd64/amd64/cpu_switch.S b/sys/amd64/amd64/cpu_switch.S index f34b0cccee3..a0b11f82500 100644 --- a/sys/amd64/amd64/cpu_switch.S +++ b/sys/amd64/amd64/cpu_switch.S @@ -109,8 +109,24 @@ ENTRY(cpu_switch) movq %rsp,PCB_RSP(%r8) movq %rbx,PCB_RBX(%r8) movq %rax,PCB_RIP(%r8) - movq PCB_FSBASE(%r8),%r9 - movq PCB_GSBASE(%r8),%r10 + + /* + * Reread fs and gs bases. Explicit fs segment register load + * by the usermode code may change actual fs base without + * updating pcb_{fs,gs}base. + * + * %rdx still contains the mtx, save %rdx around rdmsr. + */ + movq %rdx,%r11 + movl $MSR_FSBASE,%ecx + rdmsr + shlq $32,%rdx + leaq (%rax,%rdx),%r9 + movl $MSR_KGSBASE,%ecx + rdmsr + shlq $32,%rdx + leaq (%rax,%rdx),%r10 + movq %r11,%rdx testl $PCB_32BIT,PCB_FLAGS(%r8) jnz store_seg diff --git a/sys/amd64/amd64/machdep.c b/sys/amd64/amd64/machdep.c index 06c0803b959..f3c41f7b7d7 100644 --- a/sys/amd64/amd64/machdep.c +++ b/sys/amd64/amd64/machdep.c @@ -734,6 +734,7 @@ exec_setregs(td, entry, stack, ps_strings) pcb->pcb_fsbase = 0; pcb->pcb_gsbase = 0; critical_exit(); + pcb->pcb_flags &= ~(PCB_32BIT | PCB_GS32BIT); load_ds(_udatasel); load_es(_udatasel); load_fs(_udatasel); diff --git a/sys/amd64/ia32/ia32_signal.c b/sys/amd64/ia32/ia32_signal.c index 9e98656e18f..162dcf99831 100644 --- a/sys/amd64/ia32/ia32_signal.c +++ b/sys/amd64/ia32/ia32_signal.c @@ -742,5 +742,6 @@ ia32_setregs(td, entry, stack, ps_strings) /* Return via doreti so that we can change to a different %cs */ pcb->pcb_flags |= PCB_FULLCTX | PCB_32BIT; + pcb->pcb_flags &= ~PCB_GS32BIT; td->td_retval[1] = 0; }