Skip to content

Commit 61e888c

Browse files
committed
ARM: fiber context switch can segfault
We need a store-store barrier between pushing the registers to the current stack and setting the resumable flag of the current fiber, otherwise the CPU is allowed to reorder the instructions at runtime and to store the resumable flag before or while storing the registers. This can happen for example: thread 1: enqueues current fiber A therad 1: swapcontext -> store resumable thread 1: is preempted thread 2: steals fiber A thread 2: resumes fiber A thread 2: loads registers => reads garbage => segfaults thread 1: stores registers (too late)
1 parent 4eed8ab commit 61e888c

File tree

1 file changed

+17
-0
lines changed

1 file changed

+17
-0
lines changed

src/fiber/context/arm.cr

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,15 @@ class Fiber
2929
#
3030
# Eventually reset LR to zero to avoid the ARM unwinder to mistake the
3131
# context switch as a regular call.
32+
#
33+
# NOTE: depending on the ARM architecture (v7, v6 or older) LLVM uses
34+
# different strategies for atomics. By default it uses the "older"
35+
# architecture that relies on the libgcc __sync_* symbols; when an armv6 CPU
36+
# or +v6 feature is specified it uses the coprocessor instruction as used
37+
# below; for armv7 / +v7 it uses the `dmb ish` instruction.
38+
#
39+
# TODO: we should do the same, but we don't know the list of CPU features
40+
# for the current target machine (and LLVM won't tell us).
3241

3342
{% if compare_versions(Crystal::LLVM_VERSION, "9.0.0") >= 0 %}
3443
asm("
@@ -42,6 +51,10 @@ class Fiber
4251
vstmdb sp!, {d8-d15} // push FPU registers
4352
{% end %}
4453
str sp, [r0, #0] // current_context.stack_top = sp
54+
{% if flag?(:execution_context) %}
55+
mov r4, #0 // barrier: ensure registers are stored
56+
mcr p15, #0, r4, c7, c10, #5
57+
{% end %}
4558
mov r4, #1 // current_context.resumable = 1
4659
str r4, [r0, #4]
4760
@@ -72,6 +85,10 @@ class Fiber
7285
vstmdb sp!, {d8-d15} // push FPU registers
7386
{% end %}
7487
str sp, [$0, #0] // current_context.stack_top = sp
88+
{% if flag?(:execution_context) %}
89+
mov r4, #0 // barrier: ensure registers are stored
90+
mcr p15, #0, r4, c7, c10, #5
91+
{% end %}
7592
mov r4, #1 // current_context.resumable = 1
7693
str r4, [$0, #4]
7794

0 commit comments

Comments
 (0)