luagc: fix upvalue value read (dereference v pointer, not inline offset)

Open upvalues have v pointing to the Lua stack. Closed upvalues have v
pointing to the inline copy at upval+0x10. Reading upval+0x10 directly
was wrong for open upvalues -- gave stale/garbage data, causing
markObject to be called with invalid pointers (e.g. 0x3).

Fix: dereference upval->v at +0x08 to get the actual TValue location.
Also: volatile reads on all readU8/readU32/writeU8/writeU32 (correct
for foreign memory access via @ptrFromInt).
This commit is contained in:
MarcelineVQ
2026-04-10 00:21:49 -07:00
parent e996796d39
commit 1ec97731d5
+16 -10
View File
@@ -239,19 +239,19 @@ const native_shrink_memory: native_checkSizes_fn = @ptrFromInt(offsets.lua_gc_sh
// =============================================================================
inline fn readU32(addr: u32) u32 {
return @as(*const u32, @ptrFromInt(addr)).*;
return @as(*const volatile u32, @ptrFromInt(addr)).*;
}
inline fn writeU32(addr: u32, val: u32) void {
@as(*u32, @ptrFromInt(addr)).* = val;
@as(*volatile u32, @ptrFromInt(addr)).* = val;
}
inline fn readU8(addr: u32) u8 {
return @as(*const u8, @ptrFromInt(addr)).*;
return @as(*const volatile u8, @ptrFromInt(addr)).*;
}
inline fn writeU8(addr: u32, val: u8) void {
@as(*u8, @ptrFromInt(addr)).* = val;
@as(*volatile u8, @ptrFromInt(addr)).* = val;
}
fn getGlobalState(L: u32) u32 {
@@ -600,16 +600,22 @@ fn traverseClosure(cl: u32) void {
if (upv == 0) continue;
// Make upvalue non-white (gray) so it survives sweep.
// Also serves as "processed" flag (native TEST AL,AL sees non-zero).
if (isWhite(upv)) {
white2gray(upv);
}
// Mark the upvalue's held value
const val_tt = readU8(upv + offsets.UPVAL_value_tt);
if (val_tt >= offsets.LUA_TSTRING) {
const val_gc = readU32(upv + offsets.UPVAL_value_gcptr);
if (val_gc != 0) _ = markObject(val_gc);
// Mark the upvalue's held value. Must dereference upval->v (+0x08)
// which points to the ACTUAL value location:
// - Open upvalues: v points into the Lua stack
// - Closed upvalues: v points to the inline copy at upval+0x10
// Reading upval+0x10 directly is WRONG for open upvalues (stale data).
const v_ptr = readU32(upv + 0x08); // upval->v
if (v_ptr != 0) {
const val_tt = readU8(v_ptr + offsets.TVALUE_tt);
if (val_tt >= offsets.LUA_TSTRING) {
const val_gc = readU32(v_ptr + offsets.TVALUE_gcptr);
if (val_gc != 0) _ = markObject(val_gc);
}
}
}
}