diff --git a/src/task.c b/src/task.c index 7bba3ee3d83b1..780188b1163b8 100644 --- a/src/task.c +++ b/src/task.c @@ -189,6 +189,7 @@ static void NOINLINE save_stack(jl_ptls_t ptls, jl_task_t *lastt, jl_task_t **pt if (lastt->ctx.bufsz < nb) { asan_free_copy_stack(lastt->ctx.stkbuf, lastt->ctx.bufsz); buf = (void*)jl_gc_alloc_buf(ptls, nb); + jl_gc_wb(lastt, buf); lastt->ctx.stkbuf = buf; lastt->ctx.bufsz = nb; } @@ -1593,7 +1594,7 @@ jl_task_t *jl_init_root_task(jl_ptls_t ptls, void *stack_lo, void *stack_hi) if (always_copy_stacks) { // when this is set, we will attempt to corrupt the process stack to switch tasks, // although this is unreliable, and thus not recommended - ptls->stackbase = jl_get_frame_addr(); + ptls->stackbase = (void*)((uintptr_t)jl_get_frame_addr() & ~(uintptr_t)15); ptls->stacksize = (char*)ptls->stackbase - (char*)stack_lo; } else { diff --git a/test/threads.jl b/test/threads.jl index 541459d04617a..10d579923f9db 100644 --- a/test/threads.jl +++ b/test/threads.jl @@ -587,3 +587,21 @@ let once = OncePerTask{Int}(() -> error("expected")) @test_throws ErrorException("expected") once() @test_throws ErrorException("expected") once() end + +# Exercise `save_stack`/`restore_stack` under GC pressure: a missing write-barrier +# when storing the freshly-allocated copy-stack buffer into `lastt->ctx.stkbuf` +# let the GC collect the buffer while the (old) task still referenced it. +let code = """ + function f(x) + x in (0, 1) && return x + a = Threads.@spawn f(x - 2) + b = Threads.@spawn f(x - 1) + return fetch(a) + fetch(b) + end + print(@sync f(25)) + """ + cmd = addenv(`$(Base.julia_cmd()) --depwarn=error --startup-file=no -t4 -e $code`, + "JULIA_COPY_STACKS" => "1") + # JULIA_COPY_STACKS is broken on Windows (#35147) + @test read(cmd, String) == "75025" skip=Sys.iswindows() +end