From 2fac22c647a36b7b38bd415d74c242e017437d27 Mon Sep 17 00:00:00 2001 From: Balasubramania Pillai Date: Thu, 12 Mar 2026 14:24:52 -0400 Subject: [PATCH 01/12] TASK-214337 debug code --- src/lgc.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/src/lgc.c b/src/lgc.c index 116a058..f366d6b 100644 --- a/src/lgc.c +++ b/src/lgc.c @@ -1656,6 +1656,8 @@ void luaC_inherit_thread(lua_State *L, lua_State *th) ck_sequence_write_end(&th->memlock); luaE_flush_stringtable(th); + validate_heap_objects(L, "inherit_thread:before_transfer"); + TAILQ_FOREACH_SAFE(steal, &th->heap->objects, allocd, tmp) { /* Update owner before removing from source heap. This ensures that * if any concurrent reader sees this object, it will either: @@ -1670,6 +1672,9 @@ void luaC_inherit_thread(lua_State *L, lua_State *th) make_grey(L, steal); } + + validate_heap_objects(L, "inherit_thread:after_transfer"); + TAILQ_REMOVE(&G(L)->all_heaps, th->heap, heaps); unlock_all_threads(); @@ -1954,6 +1959,43 @@ static void sanity_check_mark_status(lua_State *L) } } +static void validate_heap_objects(lua_State *L, const char *where) +{ + GCheader *o; + int count = 0; + const uint64_t poison = 0x5a5a5a5a5a5a5a5aULL; + + TAILQ_FOREACH(o, &L->heap->objects, allocd) { + if ((uintptr_t)o == poison || + ((uintptr_t)o & 0x7) != 0 || + o->tt > 11 || + (o->marked & FREEDBIT)) { + fprintf(stderr, + "thrlua HEAP CORRUPT [%s] L=%p heap=%p count=%d bad_node=%p" + " (tt=%d marked=0x%x)\n", + where, (void*)L, (void*)L->heap, count, (void*)o, + (uintptr_t)o != poison ? o->tt : -1, + (uintptr_t)o != poison ? o->marked : 0xff); + abort(); + } + if (o->owner != L->heap) { + fprintf(stderr, + "thrlua HEAP CORRUPT [%s] L=%p heap=%p count=%d node=%p" + " owner=%p (expected %p) tt=%d\n", + where, (void*)L, (void*)L->heap, count, (void*)o, + (void*)o->owner, (void*)L->heap, o->tt); + abort(); + } + count++; + if (count > 10000000) { + fprintf(stderr, + "thrlua HEAP CORRUPT [%s] L=%p heap=%p: list cycle detected\n", + where, (void*)L, (void*)L->heap); + abort(); + } + } +} + static int local_collection(lua_State *L, int type) { int reclaimed; @@ -1974,6 +2016,8 @@ static int local_collection(lua_State *L, int type) * while we are in this function and manipulating our string tables or heap */ block_collector(L, pt); + validate_heap_objects(L, "local_collection:entry"); + /* prune out excess string table entries. * We don't want to be too aggressive, as we'd like to see some benefit * from string interning. We remove the head of each chain and repeat @@ -2020,6 +2064,8 @@ static int local_collection(lua_State *L, int type) check_references(L); } + validate_heap_objects(L, "local_collection:after_mark"); + /* run any finalizers; may turn some objects grey again */ run_finalize(L); @@ -2035,11 +2081,15 @@ static int local_collection(lua_State *L, int type) /* remove collected weak values from weak tables */ fixup_weak_refs(L); + validate_heap_objects(L, "local_collection:before_reclaim"); + /* and now we can free whatever is left in White. Note that we're still * blocked here so we are pulling white out of the heap and placing them * in another list that will free them when we unblock the collector. */ reclaimed = reclaim_white(L, 0); + validate_heap_objects(L, "local_collection:after_reclaim"); + /* White is the new Black */ L->black = !L->black; @@ -2052,9 +2102,13 @@ static int local_collection(lua_State *L, int type) /* Finalize deferred objects */ finalize_deferred(L); + validate_heap_objects(L, "local_collection:after_finalize"); + /* Free any objects that were white */ free_deferred_white(L); + validate_heap_objects(L, "local_collection:after_free"); + /* Free any deferred stringtable nodes */ while (tofree) { n = tofree; @@ -2117,9 +2171,20 @@ static void global_trace_obj(lua_State *L, GCheader *lval, GCheader *rval) static void trace_heap(GCheap *h) { GCheader *o; + const uint64_t poison = 0x5a5a5a5a5a5a5a5aULL; ck_pr_store_32(&h->owner->xref_count, 0); TAILQ_FOREACH(o, &h->objects, allocd) { + if ((uintptr_t)o == poison || ((uintptr_t)o & 0x7) != 0 || + o->tt > 11 || (o->marked & FREEDBIT)) { + fprintf(stderr, + "thrlua HEAP CORRUPT [trace_heap] heap=%p owner=%p bad_node=%p" + " (tt=%d marked=0x%x)\n", + (void*)h, (void*)h->owner, (void*)o, + (uintptr_t)o != poison ? o->tt : -1, + (uintptr_t)o != poison ? o->marked : 0xff); + abort(); + } global_trace_obj(h->owner, &h->owner->gch, o); } From 86aa78084518be7d9df7328db84c19898c2c2818 Mon Sep 17 00:00:00 2001 From: Balasubramania Pillai Date: Thu, 12 Mar 2026 14:29:42 -0400 Subject: [PATCH 02/12] TASK-214337 fix compile error --- src/lgc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lgc.c b/src/lgc.c index f366d6b..ffe6218 100644 --- a/src/lgc.c +++ b/src/lgc.c @@ -106,6 +106,7 @@ static uint32_t trace_heaps = 0; static int local_collection(lua_State *L, int type); static int global_trace(lua_State *L); static void unblock_mutators(lua_State *L); +static void validate_heap_objects(lua_State *L, const char *where); static INLINE int is_black(lua_State *L, GCheader *obj) { From b5a53a3144febd5a46e6094eb1988e3449689b8a Mon Sep 17 00:00:00 2001 From: Balasubramania Pillai Date: Thu, 12 Mar 2026 14:36:29 -0400 Subject: [PATCH 03/12] TASK-214337 send to paniclog --- src/lgc.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/lgc.c b/src/lgc.c index ffe6218..b11d154 100644 --- a/src/lgc.c +++ b/src/lgc.c @@ -1969,9 +1969,9 @@ static void validate_heap_objects(lua_State *L, const char *where) TAILQ_FOREACH(o, &L->heap->objects, allocd) { if ((uintptr_t)o == poison || ((uintptr_t)o & 0x7) != 0 || - o->tt > 11 || + o->tt > LUA_TGLOBAL || (o->marked & FREEDBIT)) { - fprintf(stderr, + thrlua_log(L, DCRITICAL, "thrlua HEAP CORRUPT [%s] L=%p heap=%p count=%d bad_node=%p" " (tt=%d marked=0x%x)\n", where, (void*)L, (void*)L->heap, count, (void*)o, @@ -1980,7 +1980,7 @@ static void validate_heap_objects(lua_State *L, const char *where) abort(); } if (o->owner != L->heap) { - fprintf(stderr, + thrlua_log(L, DCRITICAL, "thrlua HEAP CORRUPT [%s] L=%p heap=%p count=%d node=%p" " owner=%p (expected %p) tt=%d\n", where, (void*)L, (void*)L->heap, count, (void*)o, @@ -1989,7 +1989,7 @@ static void validate_heap_objects(lua_State *L, const char *where) } count++; if (count > 10000000) { - fprintf(stderr, + thrlua_log(L, DCRITICAL, "thrlua HEAP CORRUPT [%s] L=%p heap=%p: list cycle detected\n", where, (void*)L, (void*)L->heap); abort(); @@ -2177,8 +2177,8 @@ static void trace_heap(GCheap *h) ck_pr_store_32(&h->owner->xref_count, 0); TAILQ_FOREACH(o, &h->objects, allocd) { if ((uintptr_t)o == poison || ((uintptr_t)o & 0x7) != 0 || - o->tt > 11 || (o->marked & FREEDBIT)) { - fprintf(stderr, + o->tt > LUA_TGLOBAL || (o->marked & FREEDBIT)) { + thrlua_log(h->owner, DCRITICAL, "thrlua HEAP CORRUPT [trace_heap] heap=%p owner=%p bad_node=%p" " (tt=%d marked=0x%x)\n", (void*)h, (void*)h->owner, (void*)o, From 165dba1507e4f3d84d4852b6b0820ee043010726 Mon Sep 17 00:00:00 2001 From: Balasubramania Pillai Date: Thu, 12 Mar 2026 14:56:07 -0400 Subject: [PATCH 04/12] TASK-214337 improvements --- src/lgc.c | 52 +++++++++++++++++++++++++++++----------------------- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/src/lgc.c b/src/lgc.c index b11d154..292fd4c 100644 --- a/src/lgc.c +++ b/src/lgc.c @@ -1960,26 +1960,36 @@ static void sanity_check_mark_status(lua_State *L) } } +static int is_bad_gc_ptr(uintptr_t p) +{ + return p == 0x5a5a5a5a5a5a5a5aULL || p == 0 || (p & 0x7) != 0; +} + static void validate_heap_objects(lua_State *L, const char *where) { GCheader *o; int count = 0; - const uint64_t poison = 0x5a5a5a5a5a5a5a5aULL; TAILQ_FOREACH(o, &L->heap->objects, allocd) { - if ((uintptr_t)o == poison || - ((uintptr_t)o & 0x7) != 0 || - o->tt > LUA_TGLOBAL || + if (is_bad_gc_ptr((uintptr_t)o) || + o->tt < LUA_TSTRING || o->tt > LUA_TGLOBAL || (o->marked & FREEDBIT)) { + fprintf(stderr, + "thrlua HEAP CORRUPT [%s] L=%p heap=%p count=%d bad_node=%p\n", + where, (void*)L, (void*)L->heap, count, (void*)o); + if (!is_bad_gc_ptr((uintptr_t)o)) + fprintf(stderr, " tt=%d marked=0x%x\n", o->tt, o->marked); thrlua_log(L, DCRITICAL, - "thrlua HEAP CORRUPT [%s] L=%p heap=%p count=%d bad_node=%p" - " (tt=%d marked=0x%x)\n", - where, (void*)L, (void*)L->heap, count, (void*)o, - (uintptr_t)o != poison ? o->tt : -1, - (uintptr_t)o != poison ? o->marked : 0xff); + "thrlua HEAP CORRUPT [%s] L=%p heap=%p count=%d bad_node=%p\n", + where, (void*)L, (void*)L->heap, count, (void*)o); abort(); } if (o->owner != L->heap) { + fprintf(stderr, + "thrlua HEAP CORRUPT [%s] L=%p heap=%p count=%d node=%p" + " owner=%p (expected %p) tt=%d\n", + where, (void*)L, (void*)L->heap, count, (void*)o, + (void*)o->owner, (void*)L->heap, o->tt); thrlua_log(L, DCRITICAL, "thrlua HEAP CORRUPT [%s] L=%p heap=%p count=%d node=%p" " owner=%p (expected %p) tt=%d\n", @@ -1988,12 +1998,6 @@ static void validate_heap_objects(lua_State *L, const char *where) abort(); } count++; - if (count > 10000000) { - thrlua_log(L, DCRITICAL, - "thrlua HEAP CORRUPT [%s] L=%p heap=%p: list cycle detected\n", - where, (void*)L, (void*)L->heap); - abort(); - } } } @@ -2172,18 +2176,20 @@ static void global_trace_obj(lua_State *L, GCheader *lval, GCheader *rval) static void trace_heap(GCheap *h) { GCheader *o; - const uint64_t poison = 0x5a5a5a5a5a5a5a5aULL; ck_pr_store_32(&h->owner->xref_count, 0); TAILQ_FOREACH(o, &h->objects, allocd) { - if ((uintptr_t)o == poison || ((uintptr_t)o & 0x7) != 0 || - o->tt > LUA_TGLOBAL || (o->marked & FREEDBIT)) { + if (is_bad_gc_ptr((uintptr_t)o) || + o->tt < LUA_TSTRING || o->tt > LUA_TGLOBAL || + (o->marked & FREEDBIT)) { + fprintf(stderr, + "thrlua HEAP CORRUPT [trace_heap] heap=%p owner=%p bad_node=%p\n", + (void*)h, (void*)h->owner, (void*)o); + if (!is_bad_gc_ptr((uintptr_t)o)) + fprintf(stderr, " tt=%d marked=0x%x\n", o->tt, o->marked); thrlua_log(h->owner, DCRITICAL, - "thrlua HEAP CORRUPT [trace_heap] heap=%p owner=%p bad_node=%p" - " (tt=%d marked=0x%x)\n", - (void*)h, (void*)h->owner, (void*)o, - (uintptr_t)o != poison ? o->tt : -1, - (uintptr_t)o != poison ? o->marked : 0xff); + "thrlua HEAP CORRUPT [trace_heap] heap=%p owner=%p bad_node=%p\n", + (void*)h, (void*)h->owner, (void*)o); abort(); } global_trace_obj(h->owner, &h->owner->gch, o); From 7d07c5a997fc0542765e438548d191c4e90646f0 Mon Sep 17 00:00:00 2001 From: Balasubramania Pillai Date: Thu, 12 Mar 2026 15:11:39 -0400 Subject: [PATCH 05/12] TASK-214337 improvements --- src/lgc.c | 35 ++++++++++------------------------- 1 file changed, 10 insertions(+), 25 deletions(-) diff --git a/src/lgc.c b/src/lgc.c index 292fd4c..65d9478 100644 --- a/src/lgc.c +++ b/src/lgc.c @@ -1974,22 +1974,19 @@ static void validate_heap_objects(lua_State *L, const char *where) if (is_bad_gc_ptr((uintptr_t)o) || o->tt < LUA_TSTRING || o->tt > LUA_TGLOBAL || (o->marked & FREEDBIT)) { - fprintf(stderr, - "thrlua HEAP CORRUPT [%s] L=%p heap=%p count=%d bad_node=%p\n", - where, (void*)L, (void*)L->heap, count, (void*)o); if (!is_bad_gc_ptr((uintptr_t)o)) - fprintf(stderr, " tt=%d marked=0x%x\n", o->tt, o->marked); - thrlua_log(L, DCRITICAL, - "thrlua HEAP CORRUPT [%s] L=%p heap=%p count=%d bad_node=%p\n", - where, (void*)L, (void*)L->heap, count, (void*)o); + thrlua_log(L, DCRITICAL, + "thrlua HEAP CORRUPT [%s] L=%p heap=%p count=%d bad_node=%p" + " tt=%d marked=0x%x\n", + where, (void*)L, (void*)L->heap, count, (void*)o, + o->tt, o->marked); + else + thrlua_log(L, DCRITICAL, + "thrlua HEAP CORRUPT [%s] L=%p heap=%p count=%d bad_node=%p\n", + where, (void*)L, (void*)L->heap, count, (void*)o); abort(); } if (o->owner != L->heap) { - fprintf(stderr, - "thrlua HEAP CORRUPT [%s] L=%p heap=%p count=%d node=%p" - " owner=%p (expected %p) tt=%d\n", - where, (void*)L, (void*)L->heap, count, (void*)o, - (void*)o->owner, (void*)L->heap, o->tt); thrlua_log(L, DCRITICAL, "thrlua HEAP CORRUPT [%s] L=%p heap=%p count=%d node=%p" " owner=%p (expected %p) tt=%d\n", @@ -2178,20 +2175,8 @@ static void trace_heap(GCheap *h) GCheader *o; ck_pr_store_32(&h->owner->xref_count, 0); + validate_heap_objects(h->owner, "trace_heap"); TAILQ_FOREACH(o, &h->objects, allocd) { - if (is_bad_gc_ptr((uintptr_t)o) || - o->tt < LUA_TSTRING || o->tt > LUA_TGLOBAL || - (o->marked & FREEDBIT)) { - fprintf(stderr, - "thrlua HEAP CORRUPT [trace_heap] heap=%p owner=%p bad_node=%p\n", - (void*)h, (void*)h->owner, (void*)o); - if (!is_bad_gc_ptr((uintptr_t)o)) - fprintf(stderr, " tt=%d marked=0x%x\n", o->tt, o->marked); - thrlua_log(h->owner, DCRITICAL, - "thrlua HEAP CORRUPT [trace_heap] heap=%p owner=%p bad_node=%p\n", - (void*)h, (void*)h->owner, (void*)o); - abort(); - } global_trace_obj(h->owner, &h->owner->gch, o); } From 53b84ba85fb71c7c4234cd91bd412fe72ceada8b Mon Sep 17 00:00:00 2001 From: Balasubramania Pillai Date: Fri, 13 Mar 2026 15:13:07 -0400 Subject: [PATCH 06/12] TASK-214337 delay unblocking global trace during local gc --- src/lgc.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lgc.c b/src/lgc.c index 65d9478..64254de 100644 --- a/src/lgc.c +++ b/src/lgc.c @@ -2097,15 +2097,15 @@ static int local_collection(lua_State *L, int type) sanity_check_mark_status(L); - /* Now we can un-block the global collector, as we are done with our string - * tables and our heap. */ - unblock_collector(L, pt); - /* Finalize deferred objects */ finalize_deferred(L); validate_heap_objects(L, "local_collection:after_finalize"); + /* Now we can un-block the global collector, as we are done with our string + * tables and our heap. */ + unblock_collector(L, pt); + /* Free any objects that were white */ free_deferred_white(L); From 01bee96734a5bdb426af590f5c033d9a629d652c Mon Sep 17 00:00:00 2001 From: Balasubramania Pillai Date: Mon, 16 Mar 2026 15:00:38 -0400 Subject: [PATCH 07/12] TASK-214337 additional debug --- src/lgc.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/lgc.c b/src/lgc.c index 64254de..a9ee7be 100644 --- a/src/lgc.c +++ b/src/lgc.c @@ -1968,6 +1968,7 @@ static int is_bad_gc_ptr(uintptr_t p) static void validate_heap_objects(lua_State *L, const char *where) { GCheader *o; + GCheader *prev = NULL; int count = 0; TAILQ_FOREACH(o, &L->heap->objects, allocd) { @@ -1977,23 +1978,29 @@ static void validate_heap_objects(lua_State *L, const char *where) if (!is_bad_gc_ptr((uintptr_t)o)) thrlua_log(L, DCRITICAL, "thrlua HEAP CORRUPT [%s] L=%p heap=%p count=%d bad_node=%p" - " tt=%d marked=0x%x\n", + " prev=%p tt=%d marked=0x%x\n", where, (void*)L, (void*)L->heap, count, (void*)o, - o->tt, o->marked); + (void*)prev, o->tt, o->marked); else thrlua_log(L, DCRITICAL, - "thrlua HEAP CORRUPT [%s] L=%p heap=%p count=%d bad_node=%p\n", - where, (void*)L, (void*)L->heap, count, (void*)o); + "thrlua HEAP CORRUPT [%s] L=%p heap=%p count=%d bad_node=%p" + " prev=%p\n", + where, (void*)L, (void*)L->heap, count, (void*)o, + (void*)prev); abort(); } if (o->owner != L->heap) { thrlua_log(L, DCRITICAL, "thrlua HEAP CORRUPT [%s] L=%p heap=%p count=%d node=%p" - " owner=%p (expected %p) tt=%d\n", + " prev=%p owner=%p owner->L=%p (expected heap %p) tt=%d" + " marked=0x%x xref=%u ref=%u\n", where, (void*)L, (void*)L->heap, count, (void*)o, - (void*)o->owner, (void*)L->heap, o->tt); + (void*)prev, (void*)o->owner, (void*)o->owner->owner, + (void*)L->heap, o->tt, o->marked, + o->xref, o->ref); abort(); } + prev = o; count++; } } From c16bb5d1545e7d8a8b9bc284aeb7e094fd7217aa Mon Sep 17 00:00:00 2001 From: Balasubramania Pillai Date: Mon, 16 Mar 2026 15:37:54 -0400 Subject: [PATCH 08/12] TASK-214337 additional debug --- src/lgc.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/lgc.c b/src/lgc.c index a9ee7be..b72fe0a 100644 --- a/src/lgc.c +++ b/src/lgc.c @@ -1990,12 +1990,15 @@ static void validate_heap_objects(lua_State *L, const char *where) abort(); } if (o->owner != L->heap) { + void *owner_L = NULL; + if (!is_bad_gc_ptr((uintptr_t)o->owner)) + owner_L = (void*)o->owner->owner; thrlua_log(L, DCRITICAL, "thrlua HEAP CORRUPT [%s] L=%p heap=%p count=%d node=%p" " prev=%p owner=%p owner->L=%p (expected heap %p) tt=%d" " marked=0x%x xref=%u ref=%u\n", where, (void*)L, (void*)L->heap, count, (void*)o, - (void*)prev, (void*)o->owner, (void*)o->owner->owner, + (void*)prev, (void*)o->owner, owner_L, (void*)L->heap, o->tt, o->marked, o->xref, o->ref); abort(); From 95cb977e9264b14fc25421d02f4ff8e40cd1c867 Mon Sep 17 00:00:00 2001 From: Balasubramania Pillai Date: Tue, 17 Mar 2026 14:59:55 -0400 Subject: [PATCH 09/12] TASK-214337 add more debug --- src/lgc.c | 86 +++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 61 insertions(+), 25 deletions(-) diff --git a/src/lgc.c b/src/lgc.c index b72fe0a..7a4245b 100644 --- a/src/lgc.c +++ b/src/lgc.c @@ -107,6 +107,9 @@ static int local_collection(lua_State *L, int type); static int global_trace(lua_State *L); static void unblock_mutators(lua_State *L); static void validate_heap_objects(lua_State *L, const char *where); +static void tailq_abort(lua_State *L, const char *where, const char *what, + GCheader *o, GCheader *prev, int count, + GCheader *tqe_next, GCheader **tqe_prev); static INLINE int is_black(lua_State *L, GCheader *obj) { @@ -1489,6 +1492,11 @@ static GCheader *new_obj(lua_State *L, enum lua_obj_type tt, * before it becomes visible to the GC via the grey stack. */ block_collector(L, pt); TAILQ_INSERT_HEAD(&L->heap->objects, o, allocd); + if (o->allocd.tqe_next != NULL && + o->allocd.tqe_next->allocd.tqe_prev != &o->allocd.tqe_next) { + tailq_abort(L, "new_obj", "insert_backlink_broken", o, NULL, -1, + o->allocd.tqe_next, o->allocd.tqe_prev); + } make_grey(L, o); unblock_collector(L, pt); @@ -1670,6 +1678,11 @@ void luaC_inherit_thread(lua_State *L, lua_State *th) TAILQ_REMOVE(&th->heap->objects, steal, allocd); TAILQ_INSERT_HEAD(&L->heap->objects, steal, allocd); + if (steal->allocd.tqe_next != NULL && + steal->allocd.tqe_next->allocd.tqe_prev != &steal->allocd.tqe_next) { + tailq_abort(L, "inherit_thread", "insert_backlink_broken", steal, NULL, -1, + steal->allocd.tqe_next, steal->allocd.tqe_prev); + } make_grey(L, steal); } @@ -1965,6 +1978,40 @@ static int is_bad_gc_ptr(uintptr_t p) return p == 0x5a5a5a5a5a5a5a5aULL || p == 0 || (p & 0x7) != 0; } +static void tailq_abort(lua_State *L, const char *where, const char *what, + GCheader *o, GCheader *prev, int count, + GCheader *tqe_next, GCheader **tqe_prev) +{ + void *owner_L = NULL; + void *prev_owner = NULL; + void *prev_owner_L = NULL; + if (o && !is_bad_gc_ptr((uintptr_t)o->owner)) + owner_L = (void*)o->owner->owner; + if (prev && !is_bad_gc_ptr((uintptr_t)prev->owner)) + prev_owner_L = (void*)prev->owner->owner; + thrlua_log(L, DCRITICAL, + "thrlua HEAP CORRUPT [%s] %s L=%p heap=%p count=%d" + " node=%p node.next=%p node.prev=%p" + " prev=%p prev.next=%p prev.owner=%p prev.owner->L=%p" + " node.owner=%p node.owner->L=%p" + " tt=%d marked=0x%x xref=%u ref=%u\n", + where, what, (void*)L, (void*)L->heap, count, + (void*)o, + o ? (void*)o->allocd.tqe_next : NULL, + o ? (void*)o->allocd.tqe_prev : NULL, + (void*)prev, + prev ? (void*)prev->allocd.tqe_next : NULL, + prev ? (void*)prev->owner : NULL, + prev_owner_L, + o ? (void*)o->owner : NULL, + owner_L, + o ? o->tt : -1, + o ? o->marked : 0, + o ? o->xref : 0, + o ? o->ref : 0); + abort(); +} + static void validate_heap_objects(lua_State *L, const char *where) { GCheader *o; @@ -1975,33 +2022,22 @@ static void validate_heap_objects(lua_State *L, const char *where) if (is_bad_gc_ptr((uintptr_t)o) || o->tt < LUA_TSTRING || o->tt > LUA_TGLOBAL || (o->marked & FREEDBIT)) { - if (!is_bad_gc_ptr((uintptr_t)o)) - thrlua_log(L, DCRITICAL, - "thrlua HEAP CORRUPT [%s] L=%p heap=%p count=%d bad_node=%p" - " prev=%p tt=%d marked=0x%x\n", - where, (void*)L, (void*)L->heap, count, (void*)o, - (void*)prev, o->tt, o->marked); - else - thrlua_log(L, DCRITICAL, - "thrlua HEAP CORRUPT [%s] L=%p heap=%p count=%d bad_node=%p" - " prev=%p\n", - where, (void*)L, (void*)L->heap, count, (void*)o, - (void*)prev); - abort(); + tailq_abort(L, where, is_bad_gc_ptr((uintptr_t)o) ? "bad_ptr" : "bad_tt_or_freed", + is_bad_gc_ptr((uintptr_t)o) ? NULL : o, prev, count, NULL, NULL); } if (o->owner != L->heap) { - void *owner_L = NULL; - if (!is_bad_gc_ptr((uintptr_t)o->owner)) - owner_L = (void*)o->owner->owner; - thrlua_log(L, DCRITICAL, - "thrlua HEAP CORRUPT [%s] L=%p heap=%p count=%d node=%p" - " prev=%p owner=%p owner->L=%p (expected heap %p) tt=%d" - " marked=0x%x xref=%u ref=%u\n", - where, (void*)L, (void*)L->heap, count, (void*)o, - (void*)prev, (void*)o->owner, owner_L, - (void*)L->heap, o->tt, o->marked, - o->xref, o->ref); - abort(); + tailq_abort(L, where, "wrong_owner", o, prev, count, + o->allocd.tqe_next, o->allocd.tqe_prev); + } + if (o->allocd.tqe_prev == NULL || + *(o->allocd.tqe_prev) != o) { + tailq_abort(L, where, "tqe_prev_broken", o, prev, count, + o->allocd.tqe_next, o->allocd.tqe_prev); + } + if (o->allocd.tqe_next != NULL && + o->allocd.tqe_next->allocd.tqe_prev != &o->allocd.tqe_next) { + tailq_abort(L, where, "tqe_next_backlink_broken", o, prev, count, + o->allocd.tqe_next, o->allocd.tqe_prev); } prev = o; count++; From 3e9c7bcb37db8eb9898d7432e889e6c81e07e33b Mon Sep 17 00:00:00 2001 From: Balasubramania Pillai Date: Tue, 17 Mar 2026 15:20:16 -0400 Subject: [PATCH 10/12] TASK-214337 fix review feedback --- src/lgc.c | 81 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 44 insertions(+), 37 deletions(-) diff --git a/src/lgc.c b/src/lgc.c index 7a4245b..3ddf84d 100644 --- a/src/lgc.c +++ b/src/lgc.c @@ -108,8 +108,7 @@ static int global_trace(lua_State *L); static void unblock_mutators(lua_State *L); static void validate_heap_objects(lua_State *L, const char *where); static void tailq_abort(lua_State *L, const char *where, const char *what, - GCheader *o, GCheader *prev, int count, - GCheader *tqe_next, GCheader **tqe_prev); + GCheader *o, GCheader *prev, int count); static INLINE int is_black(lua_State *L, GCheader *obj) { @@ -1493,9 +1492,9 @@ static GCheader *new_obj(lua_State *L, enum lua_obj_type tt, block_collector(L, pt); TAILQ_INSERT_HEAD(&L->heap->objects, o, allocd); if (o->allocd.tqe_next != NULL && + !is_bad_gc_ptr((uintptr_t)o->allocd.tqe_next) && o->allocd.tqe_next->allocd.tqe_prev != &o->allocd.tqe_next) { - tailq_abort(L, "new_obj", "insert_backlink_broken", o, NULL, -1, - o->allocd.tqe_next, o->allocd.tqe_prev); + tailq_abort(L, "new_obj", "insert_backlink_broken", o, NULL, -1); } make_grey(L, o); unblock_collector(L, pt); @@ -1679,9 +1678,9 @@ void luaC_inherit_thread(lua_State *L, lua_State *th) TAILQ_REMOVE(&th->heap->objects, steal, allocd); TAILQ_INSERT_HEAD(&L->heap->objects, steal, allocd); if (steal->allocd.tqe_next != NULL && + !is_bad_gc_ptr((uintptr_t)steal->allocd.tqe_next) && steal->allocd.tqe_next->allocd.tqe_prev != &steal->allocd.tqe_next) { - tailq_abort(L, "inherit_thread", "insert_backlink_broken", steal, NULL, -1, - steal->allocd.tqe_next, steal->allocd.tqe_prev); + tailq_abort(L, "inherit_thread", "insert_backlink_broken", steal, NULL, -1); } make_grey(L, steal); @@ -1978,17 +1977,36 @@ static int is_bad_gc_ptr(uintptr_t p) return p == 0x5a5a5a5a5a5a5a5aULL || p == 0 || (p & 0x7) != 0; } +static void *safe_deref_owner_L(void *owner_ptr) +{ + if (!owner_ptr || is_bad_gc_ptr((uintptr_t)owner_ptr)) + return NULL; + return (void*)((GCheap *)owner_ptr)->owner; +} + static void tailq_abort(lua_State *L, const char *where, const char *what, - GCheader *o, GCheader *prev, int count, - GCheader *tqe_next, GCheader **tqe_prev) -{ - void *owner_L = NULL; - void *prev_owner = NULL; - void *prev_owner_L = NULL; - if (o && !is_bad_gc_ptr((uintptr_t)o->owner)) - owner_L = (void*)o->owner->owner; - if (prev && !is_bad_gc_ptr((uintptr_t)prev->owner)) - prev_owner_L = (void*)prev->owner->owner; + GCheader *o, GCheader *prev, int count) +{ + void *o_next = NULL, *o_prev = NULL, *o_owner = NULL, *o_owner_L = NULL; + void *p_next = NULL, *p_owner = NULL, *p_owner_L = NULL; + int tt = -1; + unsigned marked = 0, xref = 0, ref = 0; + + if (o) { + o_next = (void*)o->allocd.tqe_next; + o_prev = (void*)o->allocd.tqe_prev; + o_owner = (void*)o->owner; + o_owner_L = safe_deref_owner_L(o_owner); + tt = o->tt; + marked = o->marked; + xref = o->xref; + ref = o->ref; + } + if (prev) { + p_next = (void*)prev->allocd.tqe_next; + p_owner = (void*)prev->owner; + p_owner_L = safe_deref_owner_L(p_owner); + } thrlua_log(L, DCRITICAL, "thrlua HEAP CORRUPT [%s] %s L=%p heap=%p count=%d" " node=%p node.next=%p node.prev=%p" @@ -1996,19 +2014,10 @@ static void tailq_abort(lua_State *L, const char *where, const char *what, " node.owner=%p node.owner->L=%p" " tt=%d marked=0x%x xref=%u ref=%u\n", where, what, (void*)L, (void*)L->heap, count, - (void*)o, - o ? (void*)o->allocd.tqe_next : NULL, - o ? (void*)o->allocd.tqe_prev : NULL, - (void*)prev, - prev ? (void*)prev->allocd.tqe_next : NULL, - prev ? (void*)prev->owner : NULL, - prev_owner_L, - o ? (void*)o->owner : NULL, - owner_L, - o ? o->tt : -1, - o ? o->marked : 0, - o ? o->xref : 0, - o ? o->ref : 0); + (void*)o, o_next, o_prev, + (void*)prev, p_next, p_owner, p_owner_L, + o_owner, o_owner_L, + tt, marked, xref, ref); abort(); } @@ -2023,21 +2032,19 @@ static void validate_heap_objects(lua_State *L, const char *where) o->tt < LUA_TSTRING || o->tt > LUA_TGLOBAL || (o->marked & FREEDBIT)) { tailq_abort(L, where, is_bad_gc_ptr((uintptr_t)o) ? "bad_ptr" : "bad_tt_or_freed", - is_bad_gc_ptr((uintptr_t)o) ? NULL : o, prev, count, NULL, NULL); + is_bad_gc_ptr((uintptr_t)o) ? NULL : o, prev, count); } if (o->owner != L->heap) { - tailq_abort(L, where, "wrong_owner", o, prev, count, - o->allocd.tqe_next, o->allocd.tqe_prev); + tailq_abort(L, where, "wrong_owner", o, prev, count); } - if (o->allocd.tqe_prev == NULL || + if (is_bad_gc_ptr((uintptr_t)o->allocd.tqe_prev) || *(o->allocd.tqe_prev) != o) { - tailq_abort(L, where, "tqe_prev_broken", o, prev, count, - o->allocd.tqe_next, o->allocd.tqe_prev); + tailq_abort(L, where, "tqe_prev_broken", o, prev, count); } if (o->allocd.tqe_next != NULL && + !is_bad_gc_ptr((uintptr_t)o->allocd.tqe_next) && o->allocd.tqe_next->allocd.tqe_prev != &o->allocd.tqe_next) { - tailq_abort(L, where, "tqe_next_backlink_broken", o, prev, count, - o->allocd.tqe_next, o->allocd.tqe_prev); + tailq_abort(L, where, "tqe_next_backlink_broken", o, prev, count); } prev = o; count++; From 99db8c3f17e065bac07986aeb02e7eeab4bbd676 Mon Sep 17 00:00:00 2001 From: Balasubramania Pillai Date: Tue, 17 Mar 2026 15:37:40 -0400 Subject: [PATCH 11/12] TASK-214337 missing declaration --- src/lgc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lgc.c b/src/lgc.c index 3ddf84d..f899885 100644 --- a/src/lgc.c +++ b/src/lgc.c @@ -107,6 +107,7 @@ static int local_collection(lua_State *L, int type); static int global_trace(lua_State *L); static void unblock_mutators(lua_State *L); static void validate_heap_objects(lua_State *L, const char *where); +static int is_bad_gc_ptr(uintptr_t p); static void tailq_abort(lua_State *L, const char *where, const char *what, GCheader *o, GCheader *prev, int count); From b47a5f5ea7cbfd96bf1ace7c6d5acbb4aeb026a0 Mon Sep 17 00:00:00 2001 From: Balasubramania Pillai Date: Wed, 18 Mar 2026 12:31:42 -0400 Subject: [PATCH 12/12] TASK-214337 additional debug --- src/lgc.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/lgc.c b/src/lgc.c index f899885..63fa9a6 100644 --- a/src/lgc.c +++ b/src/lgc.c @@ -1990,7 +1990,8 @@ static void tailq_abort(lua_State *L, const char *where, const char *what, { void *o_next = NULL, *o_prev = NULL, *o_owner = NULL, *o_owner_L = NULL; void *p_next = NULL, *p_owner = NULL, *p_owner_L = NULL; - int tt = -1; + void *nn_next = NULL, *nn_prev = NULL, *nn_owner = NULL, *nn_owner_L = NULL; + int tt = -1, nn_tt = -1; unsigned marked = 0, xref = 0, ref = 0; if (o) { @@ -2002,6 +2003,14 @@ static void tailq_abort(lua_State *L, const char *where, const char *what, marked = o->marked; xref = o->xref; ref = o->ref; + if (o->allocd.tqe_next && !is_bad_gc_ptr((uintptr_t)o->allocd.tqe_next)) { + GCheader *nn = o->allocd.tqe_next; + nn_next = (void*)nn->allocd.tqe_next; + nn_prev = (void*)nn->allocd.tqe_prev; + nn_owner = (void*)nn->owner; + nn_owner_L = safe_deref_owner_L(nn_owner); + nn_tt = nn->tt; + } } if (prev) { p_next = (void*)prev->allocd.tqe_next; @@ -2013,12 +2022,14 @@ static void tailq_abort(lua_State *L, const char *where, const char *what, " node=%p node.next=%p node.prev=%p" " prev=%p prev.next=%p prev.owner=%p prev.owner->L=%p" " node.owner=%p node.owner->L=%p" - " tt=%d marked=0x%x xref=%u ref=%u\n", + " tt=%d marked=0x%x xref=%u ref=%u" + " nn=%p nn.next=%p nn.prev=%p nn.owner=%p nn.owner->L=%p nn.tt=%d\n", where, what, (void*)L, (void*)L->heap, count, (void*)o, o_next, o_prev, (void*)prev, p_next, p_owner, p_owner_L, o_owner, o_owner_L, - tt, marked, xref, ref); + tt, marked, xref, ref, + o_next, nn_next, nn_prev, nn_owner, nn_owner_L, nn_tt); abort(); }