diff --git a/core/box/carve_push_box.c b/core/box/carve_push_box.c index 93c04b08..e0710a38 100644 --- a/core/box/carve_push_box.c +++ b/core/box/carve_push_box.c @@ -113,15 +113,31 @@ uint32_t box_carve_and_push(int class_idx, uint32_t want) { #endif // Pop the blocks we just pushed + uint32_t pop_failed = 0; for (uint32_t j = 0; j < pushed; j++) { void* popped; if (tls_sll_pop(class_idx, &popped)) { // Return to freelist tiny_next_write(class_idx, popped, meta->freelist); meta->freelist = popped; + } else { + // Pop failed - block remains orphaned in SLL! + pop_failed++; + #if !HAKMEM_BUILD_RELEASE + fprintf(stderr, "[BOX_CARVE_PUSH_ROLLBACK] Pop failed for block %u/%u (cls=%d)\n", + j, pushed, class_idx); + fprintf(stderr, "[BOX_CARVE_PUSH_ROLLBACK] Block orphaned in TLS SLL - potential double-free risk!\n"); + #endif } } + #if !HAKMEM_BUILD_RELEASE + if (pop_failed > 0) { + fprintf(stderr, "[BOX_CARVE_PUSH_ROLLBACK] WARNING: %u/%u blocks orphaned in SLL (cls=%d)\n", + pop_failed, pushed, class_idx); + } + #endif + // Return remaining unpushed blocks to freelist while (node) { void* next_unpushed = tiny_next_read(class_idx, node); diff --git a/core/box/tls_sll_box.h b/core/box/tls_sll_box.h index 591ccfd4..ac9d638d 100644 --- a/core/box/tls_sll_box.h +++ b/core/box/tls_sll_box.h @@ -346,19 +346,27 @@ static inline bool tls_sll_push(int class_idx, void* ptr, uint32_t capacity) #if !HAKMEM_BUILD_RELEASE // Optional double-free detection: scan a bounded prefix of the list. + // Increased from 64 to 256 to catch orphaned blocks deeper in the chain. { void* scan = g_tls_sll[class_idx].head; uint32_t scanned = 0; - const uint32_t limit = (g_tls_sll[class_idx].count < 64) + const uint32_t limit = (g_tls_sll[class_idx].count < 256) ? g_tls_sll[class_idx].count - : 64; + : 256; while (scan && scanned < limit) { if (scan == ptr) { fprintf(stderr, - "[TLS_SLL_PUSH] FATAL double-free: cls=%d ptr=%p already in SLL\n", - class_idx, ptr); - ptr_trace_dump_now("double_free"); - abort(); + "[TLS_SLL_PUSH_DUP] cls=%d ptr=%p head=%p count=%u scanned=%u last_push=%p last_writer=%s\n", + class_idx, + ptr, + g_tls_sll[class_idx].head, + g_tls_sll[class_idx].count, + scanned, + s_tls_sll_last_push[class_idx], + g_tls_sll_last_writer[class_idx] ? g_tls_sll_last_writer[class_idx] : "(null)"); + ptr_trace_dump_now("tls_sll_dup"); + // Treat as already free; do not push again. + return true; } void* next; PTR_NEXT_READ("tls_sll_scan", class_idx, scan, 0, next); diff --git a/core/tiny_refill_opt.h b/core/tiny_refill_opt.h index a84436ab..f43bc2c3 100644 --- a/core/tiny_refill_opt.h +++ b/core/tiny_refill_opt.h @@ -132,11 +132,37 @@ static inline void trc_splice_to_sll(int class_idx, TinyRefillChain* c, (void)sll_count; // Suppress unused warning (void)sll_head; // Suppress unused warning - // If splice was partial, warn (should not happen in refill path) + // If splice was partial, warn about orphaned nodes + // Note: Orphaned nodes remain in chain but are not in SLL. + // Caller must handle cleanup if needed (typically by returning to freelist). if (__builtin_expect(moved < c->count, 0)) { - fprintf(stderr, "[SPLICE_WARNING] Only moved %u/%u blocks (SLL capacity limit)\n", - moved, c->count); + uint32_t orphaned = c->count - moved; + fprintf(stderr, "[SPLICE_PARTIAL] CRITICAL: Only moved %u/%u blocks (cls=%d)\n", + moved, c->count, class_idx); + fprintf(stderr, "[SPLICE_PARTIAL] %u blocks orphaned - potential memory leak!\n", + orphaned); + fprintf(stderr, "[SPLICE_PARTIAL] Orphan chain starts at node %u in original chain\n", + moved); + + // Log orphan chain head for debugging + void* scan = c->head; + for (uint32_t i = 0; i < moved && scan; i++) { + void* next = tiny_next_read(class_idx, scan); + if (i == moved - 1) { + void* orphan_head = tiny_next_read(class_idx, scan); + fprintf(stderr, "[SPLICE_PARTIAL] Orphan chain head: %p (after node %u)\n", + orphan_head, i); + break; + } + scan = next; + } fflush(stderr); + + // In debug builds, consider aborting on orphaned nodes + #if !HAKMEM_BUILD_RELEASE + fprintf(stderr, "[SPLICE_PARTIAL] Aborting to prevent memory corruption\n"); + abort(); + #endif } }