Skip to content

Commit bd8350b

Browse files
authored
Fix memory corruption if task is launched inside finalizer (#50597)
In #48919, the tid selection logic inside `enq_task` gained a `!GC.in_finalizer()` condition. However, this made it possible for `workqueue_at` to be reached with `tid==0`, which would attempt and out-of-bounds write under `@inbounds`, corrupting memory. This was not caught in the test suite despite `--check-bounds=yes`, because our `--check-bounds=yes` is currently best effort. That would be fixed by #50239, which exposed this bug. This PR attempts to fix this by marking any tasks launched inside a finalizer as not sticky. Finalizers don't have any thread they run on semantically, so i don't think there's a meaningful sense in which tasks launched inside finalizers could be sticky.
1 parent 077efd4 commit bd8350b

File tree

1 file changed

+12
-4
lines changed

1 file changed

+12
-4
lines changed

base/task.jl

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -776,17 +776,25 @@ function enq_work(t::Task)
776776
# Sticky tasks go into their thread's work queue.
777777
if t.sticky
778778
tid = Threads.threadid(t)
779-
if tid == 0 && !GC.in_finalizer()
779+
if tid == 0
780780
# The task is not yet stuck to a thread. Stick it to the current
781781
# thread and do the same to the parent task (the current task) so
782782
# that the tasks are correctly co-scheduled (issue #41324).
783783
# XXX: Ideally we would be able to unset this.
784-
tid = Threads.threadid()
785-
ccall(:jl_set_task_tid, Cint, (Any, Cint), t, tid-1)
786-
current_task().sticky = true
784+
if GC.in_finalizer()
785+
# The task was launched in a finalizer. There is no thread to sticky it
786+
# to, so just allow it to run anywhere as if it had been non-sticky.
787+
t.sticky = false
788+
@goto not_sticky
789+
else
790+
tid = Threads.threadid()
791+
ccall(:jl_set_task_tid, Cint, (Any, Cint), t, tid-1)
792+
current_task().sticky = true
793+
end
787794
end
788795
push!(workqueue_for(tid), t)
789796
else
797+
@label not_sticky
790798
tp = Threads.threadpool(t)
791799
if Threads.threadpoolsize(tp) == 1
792800
# There's only one thread in the task's assigned thread pool;

0 commit comments

Comments
 (0)