Skip to content
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion src/lfunc.c
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,12 @@ static void unlinkupval (UpVal *uv) {

void luaF_freeupval(lua_State *L, UpVal *uv)
{
if (uv->v != &uv->u.value) /* is it open? */
if (uv->v != &uv->u.value) { /* is it open? */
/* Block collector to prevent race with global trace traversing openupval */
luaC_blockcollector(L);
unlinkupval(uv); /* remove from open list */
luaC_unblockcollector(L);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GC thread can deadlock blocking collector

High Severity

luaF_freeupval now calls luaC_blockcollector when freeing an open UpVal. This function is invoked from the GC reclamation path (reclaim_object for LUA_TUPVAL), so the collector thread can end up blocking on itself while a global trace is active (eg. intend_to_stop set / trace_rwlock held), causing a hard deadlock.

Fix in Cursor Fix in Web

luaM_free(L, LUA_MEM_UPVAL, uv); /* free upvalue */
}

Expand All @@ -144,6 +148,8 @@ void luaF_close (lua_State *L, StkId level) {
#if DEBUG_UPVAL
printf("close upval >= level %p\n", level);
#endif
/* Block collector to prevent race with global trace traversing openupval */
luaC_blockcollector(L);
while (L->openupval.u.l.next != &L->openupval &&
(uv = L->openupval.u.l.next)->v >= level) {
lua_assert(uv->v != &uv->u.value);
Expand All @@ -156,6 +162,7 @@ void luaF_close (lua_State *L, StkId level) {
// setobj(L, &uv->u.value, uv->v);
// luaC_linkupval(L, uv); /* link upvalue into `gcroot' list */
}
luaC_unblockcollector(L);
#if DEBUG_UPVAL
dumpopenupvals(L);
#endif
Expand Down