diff options
author | Konstantin Belousov <kib@FreeBSD.org> | 2008-09-02 17:52:11 +0000 |
---|---|---|
committer | Konstantin Belousov <kib@FreeBSD.org> | 2008-09-02 17:52:11 +0000 |
commit | f98c3ea74ecd155eb7ad7b4c1b2de22eadfc2272 (patch) | |
tree | 6ebcf1585c96d0e1d8f54111b10eb1820d727ffe /sys/amd64 | |
parent | ffffa83b609cf6460d065cb0a21ac3ad865bb602 (diff) | |
download | src-f98c3ea74ecd155eb7ad7b4c1b2de22eadfc2272.tar.gz src-f98c3ea74ecd155eb7ad7b4c1b2de22eadfc2272.zip |
- 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 <adeepv at gmail com> [1]
Reported by: Bernd Walter <ticsoat cicely7 cicely de>,
Artem Belevich <fbsdlist at src cx>[2]
Reviewed by: peter
MFC after: 3 days
Notes
Notes:
svn path=/head/; revision=182684
Diffstat (limited to 'sys/amd64')
-rw-r--r-- | sys/amd64/amd64/cpu_switch.S | 20 | ||||
-rw-r--r-- | sys/amd64/amd64/machdep.c | 1 | ||||
-rw-r--r-- | 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 f34b0cccee31..a0b11f82500f 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 06c0803b959e..f3c41f7b7d7d 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 9e98656e18f5..162dcf99831b 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; } |