Fix an extremely subtle concurrency bug triggered by running on 32-thread

POWER8 systems. During thread switch, there was a very small window when
the stack pointer was set to the stack pointer of the outgoing thread, but
after the lock on that thread had already been released.

If, during that window, the outgoing thread were rescheduled on another CPU
and begin execution and an exception were taken on the original CPU, the
trap handler and the outgoing thread would simultaneously execute on the same
stack, causing memory corruption. Fix this by making sure to release the
old thread only after cpu_switch() is done with its stack.

MFC after:	2 weeks
Sponsored by:	FreeBSD Foundation
This commit is contained in:
nwhitehorn 2015-02-09 02:17:21 +00:00
parent 04847b0c51
commit 492377c13f

View File

@ -72,6 +72,8 @@ TOC_ENTRY(blocked_lock)
*/
ENTRY(cpu_throw)
mr %r13, %r4
li %r14,0 /* Tell cpu_switchin not to release a thread */
b cpu_switchin
/*
@ -139,10 +141,7 @@ ENTRY(cpu_switch)
bl pmap_deactivate /* Deactivate the current pmap */
nop
addi %r1,%r1,48
sync /* Make sure all of that finished */
std %r16,TD_LOCK(%r14) /* ULE: update old thread's lock */
cpu_switchin:
#if defined(SMP) && defined(SCHED_ULE)
@ -154,14 +153,20 @@ blocked_loop:
beq- blocked_loop
isync
#endif
ld %r17,TD_PCB(%r13) /* Get new PCB */
ld %r1,PCB_SP(%r17) /* Load the stack pointer */
mfsprg %r7,0 /* Get the pcpu pointer */
/* Release old thread now that we have a stack pointer set up */
cmpdi %r14,0
beq- 1f
std %r16,TD_LOCK(%r14) /* ULE: update old thread's lock */
1: mfsprg %r7,0 /* Get the pcpu pointer */
std %r13,PC_CURTHREAD(%r7) /* Store new current thread */
ld %r17,TD_PCB(%r13) /* Store new current PCB */
std %r17,PC_CURPCB(%r7)
stdu %r1,-48(%r1)
mr %r3,%r13 /* Get new thread ptr */
bl pmap_activate /* Activate the new address space */
nop