feat(callgenctor): callee 入口 can 无条件打印,区分 ABI 错位 vs 栈槽复用

caller 侧 can=0(断点证实)但 callee 收到 0x12A1_0000_0000=[cargs高32][count低32]——入口打印可区分:入口即垃圾=ABI 栈参数错位 4 字节;入口正常但槽位悬空处垃圾=函数内 can 被栈槽复用覆盖。
This commit is contained in:
TermiNexus committed 2026-08-25 17:39:21 +08:00
1 parent b3546b05a5
commit 8cc6996577
1 file changed
+75 -28
+75 -28
View File
@@ -83,7 +83,7 @@ class CallGenCtor(CallGenBase.CallGenBase):
builder, pool, mod, field_ptr_arr2, fe.DefaultVal, trans)
continue
default_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, fe.DefaultVal, None, 0, trans)
builder, pool, mod, fe.DefaultVal, None, 0, trans, fe.Ty)
if default_val is None:
# fast-fail: 默认值翻译失败(如引用了未定义符号)
VLogger.error(
@@ -127,6 +127,49 @@ class CallGenCtor(CallGenBase.CallGenBase):
OOP 结构体:alloca → call __before_init__(ptr) → call __init__(ptr, args) 或 store 参数 → load
普通结构体:alloca → zero + defaults → store 参数 → load
"""
# [DET] callee 入口 can 核对(无条件首条打印):与槽位悬空处 can 对比——
# 入口即垃圾 = ABI 栈参数错位(caller 存/callee 读错位 4 字节);
# 入口正常但槽位悬空处垃圾 = 函数内部 can 被改写(栈槽复用)。
# 无条件打印仅在进入结构体构造时执行一次(每 ctor 一条,量可控)。
stdio.printf("[DET] ctor入口: can=%llu cargs=%llu\n",
t.CUInt64T(can), t.CUInt64T(cargs))
# [DET] 读取侧存活校验(detection 模式启用):cargs(cl.args)必须指向
# 存活块——不在记录表(或已 free)= use-after-free 读取(SlotsBugTest
# KwEntry cargs 悬空根因定位)。is_alive 内部处理 detection==0 时不拦截,
# 默认关闭无开销。补 _det_probe 写入侧检测的盲区。
if cargs is not None and pool is not None:
if pool.is_alive(cargs) == 0:
# 打印 cargs 指向的块头:0=已 free(UAF 实锤) 奇数=活跃(记录表
# 异常) 其它=非 pool 块(stdlib.malloc/垃圾指针)——定位悬空源
blk_c: t.CUInt64T = t.CUInt64T(cargs) - 2 - 8
hdr_c: t.CUInt64T = t.CUInt64T(c.Deref(t.CUInt64T(blk_c, t.CPtr)))
stdio.printf("[DET] 读取悬空: struct-ctor cargs=%llu block=%llu hdr=0x%llX(0=free 奇数=活跃 其它=非pool块)\n",
t.CUInt64T(cargs), blk_c, hdr_c)
stdlib.exit(1)
# [DET] 扩展:cargs 列表存活但槽位/数据可能未初始化或悬空(SlotsBugTest
# ASAN 崩溃 node=0xCDCDCDCDCDCDCDCD——cargs.get(ai) 返回 0xCDCD 填充值:
# 列表 __init__ 未执行(__data__/__count__=0xCDCD,MEMBUDDY_FILL_GUARD
# 无条件填充)或 __data__ 悬空。列表头:__data__@0 __count__@8
# __capacity__@16。命中 0xCDCD 即打印定位并 fail-fast。
if cargs is not None:
cd_fill: t.CUInt64T = (t.CUInt64T(0xCDCDCDCD) << 32) | t.CUInt64T(0xCDCDCDCD)
lh_c: t.CUInt64T | t.CPtr = (t.CUInt64T | t.CPtr)(t.CVoid(cargs, t.CPtr))
ldd_c: t.CUInt64T = lh_c[0]
ldc_c: t.CUInt64T = lh_c[1]
ldcap_c: t.CUInt64T = lh_c[2]
if ldd_c == cd_fill or ldc_c == cd_fill:
stdio.printf("[DET] 构造参数列表未初始化: cargs=%llu data=%llu count=%llu cap=%llu can=%llu\n",
t.CUInt64T(cargs), ldd_c, ldc_c, ldcap_c, t.CUInt64T(can))
stdlib.exit(1)
ai_c: t.CSizeT = 0
while ai_c < can:
nd_c: ast.AST | t.CPtr = cargs.get(ai_c)
if nd_c is not None and t.CUInt64T(nd_c) == cd_fill:
stdio.printf("[DET] 构造参数槽位悬空: ai=%zu can=%llu node=%llu cargs=%llu data=%llu count=%llu cap=%llu\n",
ai_c, t.CUInt64T(can), t.CUInt64T(nd_c), t.CUInt64T(cargs),
ldd_c, ldc_c, ldcap_c)
stdlib.exit(1)
ai_c += 1
# 创建临时 alloca
# 确保跨模块结构体的完整定义在当前模块中可用(供 alloca 分配空间)
self.Gen.StructGen.ensure_struct_def_in_module(pool, mod, struct_ty)
@@ -248,14 +291,8 @@ class CallGenCtor(CallGenBase.CallGenBase):
arg: ast.AST | t.CPtr = cargs.get(ai)
if arg is None:
continue
arg_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, arg, None, 0, trans)
if arg_val is None:
# fast-fail: 参数翻译失败
VLogger.error(
f"构造函数参数翻译失败: {class_name}",
"HandlesExprCall")
continue
# 先取字段(fe.Ty 用于 char 字面量 → str 字段的物化判定,
# 见 translate_constant;否则 coerce inttoptr 字符值当指针崩溃)
fe: GenTypes.FieldEntry | t.CPtr = None
if ctor_entry is not None:
# OOP 结构体字段表第 0 个字段是 __vtable__(vtable 指针占位),
@@ -285,6 +322,14 @@ class CallGenCtor(CallGenBase.CallGenBase):
f"构造函数参数过多,字段索引越界: {class_name}",
"HandlesExprCall")
continue
arg_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, arg, None, 0, trans, fe.Ty)
if arg_val is None:
# fast-fail: 参数翻译失败
VLogger.error(
f"构造函数参数翻译失败: {class_name}",
"HandlesExprCall")
continue
field_ptr: llvmlite.Value | t.CPtr = llvmlite.build_gep_struct(
builder, struct_ty, fe.Ty, storage_ptr, fe.Index)
if field_ptr is None:
@@ -311,15 +356,7 @@ class CallGenCtor(CallGenBase.CallGenBase):
arg: ast.AST | t.CPtr = cargs.get(ai)
if arg is None:
continue
arg_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, arg, None, 0, trans)
if arg_val is None:
# fast-fail: 参数翻译失败
VLogger.error(
f"构造函数参数翻译失败: {class_name}",
"HandlesExprCall")
continue
# 获取字段信息(用 ctor_entry 规避跨模块同名 find_struct 找错)
# 先取字段(fe.Ty 用于 char 字面量 → str 字段的物化判定,见 translate_constant)
fe: GenTypes.FieldEntry | t.CPtr = None
if ctor_entry is not None:
fe = self.Gen.StructGen._get_field_entry(ctor_entry, ai)
@@ -331,6 +368,14 @@ class CallGenCtor(CallGenBase.CallGenBase):
f"构造函数参数过多,字段索引越界: {class_name}",
"HandlesExprCall")
continue
arg_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, arg, None, 0, trans, fe.Ty)
if arg_val is None:
# fast-fail: 参数翻译失败
VLogger.error(
f"构造函数参数翻译失败: {class_name}",
"HandlesExprCall")
continue
# GEP 到字段 ai
field_ptr: llvmlite.Value | t.CPtr = llvmlite.build_gep_struct(
builder, struct_ty, fe.Ty, tmp, fe.Index)
@@ -437,11 +482,8 @@ class CallGenCtor(CallGenBase.CallGenBase):
arg: ast.AST | t.CPtr = cargs.get(ai)
if arg is None:
continue
arg_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, arg, None, 0, trans)
if arg_val is None:
continue
# payload 字段索引 = ai + 1(跳过 __tag)
# payload 字段索引 = ai + 1(跳过 __tag);先取字段(pfe.Ty 用于
# char 字面量 → str 字段的物化判定,见 translate_constant)
payload_idx: int = ai + 1
if payload_idx >= renum_entry.FieldCount:
break
@@ -449,6 +491,10 @@ class CallGenCtor(CallGenBase.CallGenBase):
renum_entry, payload_idx)
if pfe is None:
continue
arg_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, arg, None, 0, trans, pfe.Ty)
if arg_val is None:
continue
payload_ptr: llvmlite.Value | t.CPtr = llvmlite.build_gep_struct(
builder, renum_ty, pfe.Ty, tmp, payload_idx)
if payload_ptr is None:
@@ -637,10 +683,7 @@ class CallGenCtor(CallGenBase.CallGenBase):
arg: ast.AST | t.CPtr = cargs.get(ai)
if arg is None:
continue
arg_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, arg, None, 0, trans)
if arg_val is None:
continue
# 先取字段(fe.Ty 用于 char 字面量 → str 字段的物化判定,见 translate_constant)
fe: GenTypes.FieldEntry | t.CPtr = None
if kw_entry is not None:
fe = self.Gen.StructGen._get_field_entry(kw_entry, ai)
@@ -651,6 +694,10 @@ class CallGenCtor(CallGenBase.CallGenBase):
self.Gen.TypeGenErr.fatal_error(
arg, f"结构体 '{class_name}' 位置参数越界(无对应字段)", "TypeError")
continue
arg_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, arg, None, 0, trans, fe.Ty)
if arg_val is None:
continue
field_ptr: llvmlite.Value | t.CPtr = llvmlite.build_gep_struct(
builder, struct_ty, fe.Ty, tmp, fe.Index)
if field_ptr is None:
@@ -675,7 +722,7 @@ class CallGenCtor(CallGenBase.CallGenBase):
kw, f"结构体 '{class_name}' 无字段 '{kw.arg}'", "AttributeError")
continue
kw_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, kw.value, None, 0, trans)
builder, pool, mod, kw.value, None, 0, trans, fe.Ty)
if kw_val is None:
continue
field_ptr: llvmlite.Value | t.CPtr = llvmlite.build_gep_struct(