mirror of
https://github.com/nim-lang/Nim.git
synced 2026-09-01 19:33:42 +00:00
fixes #25992 ```nim 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 Add nimZeroMem after specializeResetN for case objects to clear the entire union including unused branch bytes. (cherry picked from commit16920b56d1) (cherry picked from commit84a5613c35)
128 lines
5.0 KiB
Nim
128 lines
5.0 KiB
Nim
#
|
|
#
|
|
# The Nim Compiler
|
|
# (c) Copyright 2020 Andreas Rumpf
|
|
#
|
|
# See the file "copying.txt", included in this
|
|
# distribution, for details about the copyright.
|
|
#
|
|
|
|
# included from cgen.nim
|
|
|
|
## Code specialization instead of the old, incredibly slow 'genericReset'
|
|
## implementation.
|
|
|
|
proc specializeResetT(p: BProc, accessor: Rope, typ: PType)
|
|
|
|
proc specializeResetN(p: BProc, accessor: Rope, n: PNode;
|
|
typ: PType) =
|
|
if n == nil: return
|
|
case n.kind
|
|
of nkRecList:
|
|
for i in 0..<n.len:
|
|
specializeResetN(p, accessor, n[i], typ)
|
|
of nkRecCase:
|
|
if (n[0].kind != nkSym): internalError(p.config, n.info, "specializeResetN")
|
|
let disc = n[0].sym
|
|
if disc.loc.snippet == "": fillObjectFields(p.module, typ)
|
|
if disc.loc.t == nil:
|
|
internalError(p.config, n.info, "specializeResetN()")
|
|
lineF(p, cpsStmts, "switch ($1.$2) {$n", [accessor, disc.loc.snippet])
|
|
for i in 1..<n.len:
|
|
let branch = n[i]
|
|
assert branch.kind in {nkOfBranch, nkElse}
|
|
if branch.kind == nkOfBranch:
|
|
genCaseRange(p, branch)
|
|
else:
|
|
lineF(p, cpsStmts, "default:$n", [])
|
|
specializeResetN(p, accessor, lastSon(branch), typ)
|
|
lineF(p, cpsStmts, "break;$n", [])
|
|
lineF(p, cpsStmts, "} $n", [])
|
|
specializeResetT(p, "$1.$2" % [accessor, disc.loc.snippet], disc.loc.t)
|
|
of nkSym:
|
|
let field = n.sym
|
|
if field.typ.kind == tyVoid: return
|
|
if field.loc.snippet == "": fillObjectFields(p.module, typ)
|
|
if field.loc.t == nil:
|
|
internalError(p.config, n.info, "specializeResetN()")
|
|
specializeResetT(p, "$1.$2" % [accessor, field.loc.snippet], field.loc.t)
|
|
else: internalError(p.config, n.info, "specializeResetN()")
|
|
|
|
proc specializeResetT(p: BProc, accessor: Rope, typ: PType) =
|
|
if typ == nil: return
|
|
|
|
case typ.kind
|
|
of tyGenericInst, tyGenericBody, tyTypeDesc, tyAlias, tyDistinct, tyInferred,
|
|
tySink, tyOwned:
|
|
specializeResetT(p, accessor, skipModifier(typ))
|
|
of tyArray:
|
|
let arraySize = lengthOrd(p.config, typ.indexType)
|
|
var i: TLoc = getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt))
|
|
linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n",
|
|
[i.snippet, arraySize])
|
|
specializeResetT(p, ropecg(p.module, "$1[$2]", [accessor, i.snippet]), typ.elementType)
|
|
lineF(p, cpsStmts, "}$n", [])
|
|
of tyObject:
|
|
var x = typ.baseClass
|
|
if x != nil: x = x.skipTypes(skipPtrs)
|
|
specializeResetT(p, accessor.parentObj(p.module), x)
|
|
if typ.n != nil:
|
|
if typ.sym != nil and sfImportc in typ.sym.flags:
|
|
# imported C struct, nimZeroMem
|
|
lineCg(p, cpsStmts, "#nimZeroMem((void**)&$1, sizeof($2));$n",
|
|
[accessor, 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
|
|
lineCg(p, cpsStmts, "#nimZeroMem((void**)&$1, sizeof($2));$n",
|
|
[accessor, getTypeDesc(p.module, typ)])
|
|
of tyTuple:
|
|
let typ = getUniqueType(typ)
|
|
for i, a in typ.ikids:
|
|
specializeResetT(p, ropecg(p.module, "$1.Field$2", [accessor, i]), a)
|
|
|
|
of tyString, tyRef, tySequence:
|
|
lineCg(p, cpsStmts, "#unsureAsgnRef((void**)&$1, NIM_NIL);$n", [accessor])
|
|
|
|
of tyProc:
|
|
if typ.callConv == ccClosure:
|
|
lineCg(p, cpsStmts, "#unsureAsgnRef((void**)&$1.ClE_0, NIM_NIL);$n", [accessor])
|
|
lineCg(p, cpsStmts, "$1.ClP_0 = NIM_NIL;$n", [accessor])
|
|
else:
|
|
lineCg(p, cpsStmts, "$1 = NIM_NIL;$n", [accessor])
|
|
of tyChar, tyBool, tyEnum, tyRange, tyInt..tyUInt64:
|
|
lineCg(p, cpsStmts, "$1 = 0;$n", [accessor])
|
|
of tyCstring, tyPointer, tyPtr, tyVar, tyLent:
|
|
lineCg(p, cpsStmts, "$1 = NIM_NIL;$n", [accessor])
|
|
of tySet:
|
|
case mapSetType(p.config, typ)
|
|
of ctArray:
|
|
lineCg(p, cpsStmts, "#nimZeroMem($1, sizeof($2));$n",
|
|
[accessor, getTypeDesc(p.module, typ)])
|
|
of ctInt8, ctInt16, ctInt32, ctInt64:
|
|
lineCg(p, cpsStmts, "$1 = 0;$n", [accessor])
|
|
else:
|
|
raiseAssert "unexpected set type kind"
|
|
of tyNone, tyEmpty, tyNil, tyUntyped, tyTyped, tyGenericInvocation,
|
|
tyGenericParam, tyOrdinal, tyOpenArray, tyForward, tyVarargs,
|
|
tyUncheckedArray, tyError, tyBuiltInTypeClass, tyUserTypeClass,
|
|
tyUserTypeClassInst, tyCompositeTypeClass, tyAnd, tyOr, tyNot,
|
|
tyAnything, tyStatic, tyFromExpr, tyConcept, tyVoid, tyIterable:
|
|
discard
|
|
|
|
proc specializeReset(p: BProc, a: TLoc) =
|
|
specializeResetT(p, rdLoc(a), a.t)
|