fixes #25992; fix GC tracing of stale bytes in case objects during reset

This commit is contained in:
ringabout
2026-07-14 21:00:56 +08:00
parent 74cd4cbf3c
commit 03acd33cda
2 changed files with 73 additions and 0 deletions

View File

@@ -75,6 +75,23 @@ proc specializeResetT(p: BProc, accessor: Rope, typ: PType) =
cSizeof(getTypeDesc(p.module, typ)))
else:
specializeResetN(p, accessor, typ.n, typ)
if isCaseObj(typ.n):
# The active branch was released above. Clear the complete object so
# stale bytes from overlapping branches cannot be traced by the GC.
# type
# Foo = object
# case kind: bool
# of true:
# a: ref Bar # 8 bytes (pointer)
# of false:
# b: int # 4 bytes
# specializeResetT for b emits accessor.b = 0 — writes 4 bytes
# But the union is 8 bytes wide (sized by the largest branch)
# The remaining 4 bytes where a used to live are untouched
# Those stale bytes could contain a heap pointer the GC traces → crash
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "nimZeroMem"),
cCast(CPointer, cAddr(accessor)),
cSizeof(getTypeDesc(p.module, typ)))
of tyTuple:
let typ = getUniqueType(typ)
for i, a in typ.ikids:

View File

@@ -0,0 +1,56 @@
discard """
matrix: "--mm:refc; --mm:orc"
"""
type
A = object of RootObj
V = object
case g: bool
of true:
v: A
of false:
e: string
var r = V(g: true, v: A())
discard move r
GC_fullCollect()
type
Kind = enum nested, other
Nested = object
case kind: Kind
of nested:
case enabled: bool
of true: payload: A
of false: message: string
of other:
discard
var n = Nested(kind: nested, enabled: true, payload: A())
discard move n
GC_fullCollect()
# Moving from the other branch must keep its value alive and leave the source
# in the default state.
var s = V(g: false, e: "hello")
let moved = move s
doAssert moved.e == "hello"
doAssert not s.g
doAssert s.e.len == 0
# Reinitializing the zeroed value must also restore embedded object type
# headers.
type W = object
a: A
value: V
text: string
var w = W(a: A(), value: V(g: true, v: A()), text: "content")
let movedW = move w
doAssert movedW.text == "content"
doAssert cast[ptr pointer](addr w.a)[] != nil
doAssert not w.value.g
doAssert w.value.e.len == 0
doAssert w.text.len == 0
GC_fullCollect()