fix(tpv): 空类 FieldCount=0 中和重建 + 移除 [ATTR]/[DET] 调试打印

1) WIN32_FIND_DATAA 等空类(FieldCount=0)字段访问:模块限定路径补 FC==0 容忍(返回 None 交 pending,对齐裸名路径);ClassGenDef 空类不再 pending 延迟(多遍死锁),改为中和旧 entry 走完整注册重建。2) 用户要求移除 [ATTR](FuncGenBase/FuncGenDef)与 [DET](CallGenCtor)无条件调试打印。3) 拓宽 [DBG-REG]/[DBG-ADDF] 过滤器到 WIN32_FIND_DATAA 定位空类注册来源。
This commit is contained in:
TermiNexus committed 2026-08-29 10:44:43 +08:00
1 parent a99db2a29b
commit b7eeb504ee
5 files changed
+28 -104

No files matched your search

-43
View File
@@ -119,49 +119,6 @@ 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)
+13 -11
View File
@@ -78,20 +78,22 @@ class ClassGenDef(ClassGenBase.ClassGenBase):
# 检查是否已注册(用 SHA1 区分跨模块同名类)
existing: GenTypes.StructEntry | t.CPtr = self.Gen.StructGen.find_struct_by_module(
class_name, trans.ModuleSha1)
# 空类注册不完整检测:existing FieldCount==0 但源类体声明了字段
# (vpi 注入/注册缺陷,win32base SECURITY_ATTRIBUTES / win32file
# WIN32_FIND_DATAA 根因)。此前标记 _deps_pending 延迟 → 空 entry 持久
# 存在导致多遍死锁不收敛。改为中和旧 entry(置空 Name + Ty=None,使其
# 不可再被 find_struct_by_module 命中),走下方完整注册路径重建。
needs_rereg: int = 0
if existing is not None:
# BUG 5 方案1修改点2:existing 时字段完整性检查
# 结构体已注册,但需验证字段信息是否完整(防止 i32 回退残留或注册不完整)
# 注意:无字段类(接口/标记类,类体无 AnnAssign)合法(空 struct + 方法),
# 仅当"类体声明了字段但 FieldCount==0"才算注册不完整(Phase A-pre 漏注册)。
if existing.FieldCount == 0 and trans._declare_only == 0:
if self.Gen.ClassGenBase._class_body_has_field(cd) != 0:
# [DBG-EMP] 临时诊断:定位空类注册来源(win32base SECURITY_ATTRIBUTES)。
# 报错期间改为 pending 延迟(不 fatal 中断多遍收敛),配合
# register_struct/add_field 的 [DBG-EMP] 打印定位谁注册了空类。
VLogger.info(f"[DBG-EMP] class={class_name} FC=0 ty={t.CUInt64T(existing.Ty)} 类体有字段", "DBG")
if trans is not None:
trans._deps_pending = 1
return 0
VLogger.info(f"[DBG-EMP] class={class_name} FC=0 ty={t.CUInt64T(existing.Ty)} 类体有字段,中和重建", "DBG")
if existing.Name is not None:
existing.Name[0] = '\0'
existing.Ty = None
existing.FieldCount = 0
needs_rereg = 1
if existing is not None and needs_rereg == 0:
# 结构体已注册(Phase A-pre 多遍扫描或 Phase B 重新翻译),但仍需在当前模块中翻译方法
if existing.Ty is not None:
# Phase 1b(declare_only=0):existing.Ty 是 Phase 1a 注册的 struct_ty,
-43
View File
@@ -46,100 +46,58 @@ class FuncGenBase(CallGenBase.CallGenBase):
attrs_buf[0] = '\0'
found_any: int = 0
# [ATTR-DIAG] 运行时诊断:定位 @c.Attribute 属性为何未进入 IR(stackrealign)
cur_file: str = self.Gen.TypeGenResolve.get_current_file()
if cur_file is None:
cur_file = "?"
for di in range(dn):
deco: ast.AST | t.CPtr = decorator_list.get(di)
if deco is None:
stdio.printf("[ATTR] deco[%d]=None\n", di)
continue
dk: int = deco.kind()
stdio.printf("[ATTR] deco[%d] kind=%d\n", di, dk)
if dk != ast.ASTKind.Call:
continue
call_node: ast.Call | t.CPtr = (ast.Call | t.CPtr)(deco)
if call_node.func is None:
stdio.printf("[ATTR] call.func=None\n")
continue
cfk: int = call_node.func.kind()
stdio.printf("[ATTR] call.func kind=%d\n", cfk)
if cfk != ast.ASTKind.Attribute:
continue
# 检测 c.Attribute
func_attr: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(call_node.func)
if func_attr.attr is None:
stdio.printf("[ATTR] func_attr.attr=None\n")
continue
stdio.printf("[ATTR] func_attr.attr='%s'\n", func_attr.attr)
if string.strcmp(func_attr.attr, "Attribute") != 0:
continue
if func_attr.value is None:
stdio.printf("[ATTR] func_attr.value=None\n")
continue
fvk: int = func_attr.value.kind()
stdio.printf("[ATTR] func_attr.value kind=%d\n", fvk)
if fvk != ast.ASTKind.Name:
continue
c_name: ast.Name | t.CPtr = (ast.Name | t.CPtr)(func_attr.value)
if c_name.id is None:
stdio.printf("[ATTR] c_name.id=None\n")
continue
stdio.printf("[ATTR] c_name.id='%s'\n", c_name.id)
if string.strcmp(c_name.id, "c") != 0:
continue
imp_c: int = self.Gen.ImportsGen.is_module_imported(imported_modules, "c")
stdio.printf("[ATTR] is_module_imported('c')=%d\n", imp_c)
if imp_c == 0:
continue
# 遍历参数提取属性
if call_node.args is None:
stdio.printf("[ATTR] call.args=None\n")
continue
dbuf: bytes = pool.alloc(1024)
if dbuf is not None:
dbuf[0] = '\0'
dpos: t.CSizeT = deco.dump(dbuf, 1024, 0)
if dpos < 1024:
dbuf[dpos] = '\0'
stdio.printf("[ATTR] deco.dump='%s'\n", dbuf)
args_list: list[ast.AST | t.CPtr] | t.CPtr = call_node.args
an: t.CSizeT = len(args_list)
stdio.printf("[ATTR] args_len=%d\n", an)
if call_node.keywords is not None:
kwl: t.CSizeT = len(call_node.keywords)
stdio.printf("[ATTR] keywords_len=%d\n", kwl)
for kwi in range(kwl):
kwnd: ast.AST | t.CPtr = call_node.keywords.get(kwi)
if kwnd is not None:
stdio.printf("[ATTR] kw[%d] kind=%d\n", kwi, kwnd.kind())
for ai in range(an):
arg_node: ast.AST | t.CPtr = args_list.get(ai)
if arg_node is None:
stdio.printf("[ATTR] arg[%d]=None\n", ai)
continue
stdio.printf("[ATTR] arg[%d] kind=%d\n", ai, arg_node.kind())
# 提取属性名(t.attr.xxx / t.attr.xxx() / t.attr.llvm.xxx)
attr_name: str = self._get_attr_name_from_node(arg_node)
if attr_name is None:
stdio.printf("[ATTR] arg[%d] name=None\n", ai)
continue
# 映射到 LLVM 属性名并追加
ar: int = self._append_llvm_attr(attrs_buf, attr_name)
stdio.printf("[ATTR] arg[%d] name='%s' append=%d buf='%s' blen=%zu b0=0x%02x\n",
ai, attr_name, ar, attrs_buf, string.strlen(attrs_buf), attrs_buf[0])
if ar != 0:
found_any = 1
# 追加成功后,如果不是最后一个属性,添加空格分隔
# 在下一次追加前由 _append_str 处理
stdio.printf("[ATTR] found_any=%d return='%s'\n", found_any, attrs_buf)
if found_any == 0:
return None
return attrs_buf
@@ -226,7 +184,6 @@ class FuncGenBase(CallGenBase.CallGenBase):
max_len = 256
cur_len: t.CSizeT = string.strlen(buf)
s_len: t.CSizeT = string.strlen(s)
stdio.printf("[ATTR] _append_str: max_len=%zu cur=%zu slen=%zu s='%s'\n", max_len, cur_len, s_len, s)
if cur_len + s_len + 1 > max_len:
return # 无剩余容量:丢弃本次追加,避免越界写
string.strcpy(buf + cur_len, s)
-3
View File
@@ -467,7 +467,6 @@ class FuncGenDef(CallGenBase.CallGenBase):
llvmlite.add_param(pool, func, param_ty_e, pname_e)
# 提取 @c.Attribute 装饰器属性并设置到函数
stdio.printf("[ATTR] call@decl: func='%s'\n", fd.name)
func_attrs_e: str = self.Gen.FuncGenBase.extract_func_attrs(pool, fd.decorator_list, imported_modules)
if func_attrs_e is not None:
llvmlite.function_set_attrs(func, func_attrs_e)
@@ -486,7 +485,6 @@ class FuncGenDef(CallGenBase.CallGenBase):
# 复用前向声明:清除 IsDeclared 标记,转为 define
func.IsDeclared = 0
# 装饰器属性设置(前向声明时未设置)
stdio.printf("[ATTR] call@reuse: func='%s' mod_sha1='%s'\n", fd.name, trans.ModuleSha1)
func_attrs_reuse: str = self.Gen.FuncGenBase.extract_func_attrs(pool, fd.decorator_list, imported_modules)
if func_attrs_reuse is not None:
llvmlite.function_set_attrs(func, func_attrs_reuse)
@@ -498,7 +496,6 @@ class FuncGenDef(CallGenBase.CallGenBase):
return 0
# 提取 @c.Attribute 装饰器属性并设置到函数
stdio.printf("[ATTR] call@new: func='%s'\n", fd.name)
func_attrs: str = self.Gen.FuncGenBase.extract_func_attrs(pool, fd.decorator_list, imported_modules)
if func_attrs is not None:
llvmlite.function_set_attrs(func, func_attrs)
+15 -4
View File
@@ -221,8 +221,10 @@ class StructGenMixin:
entry.Ty = struct_ty
entry.FieldCount = 0
# [DBG-EMP] 临时诊断:定位空类注册来源(win32base SECURITY_ATTRIBUTES)
if name is not None and string.strstr(name, "SECURITY_ATTRIBUTES") is not None:
# [DBG-EMP] 临时诊断:定位空类注册来源(win32base SECURITY_ATTRIBUTES /
# win32file WIN32_FIND_DATAA FieldCount=0)
if name is not None and (string.strstr(name, "SECURITY_ATTRIBUTES") is not None
or string.strstr(name, "WIN32_FIND_DATAA") is not None):
sha1_s: str = "?"
if sha1 is not None:
sha1_s = sha1
@@ -357,8 +359,10 @@ class StructGenMixin:
return -1
idx: int = struct_entry.FieldCount
# [DBG-EMP] 临时诊断:定位空类字段添加(win32base SECURITY_ATTRIBUTES)
if struct_entry.Name is not None and string.strstr(struct_entry.Name, "SECURITY_ATTRIBUTES") is not None:
# [DBG-EMP] 临时诊断:定位空类字段添加(win32base SECURITY_ATTRIBUTES /
# win32file WIN32_FIND_DATAA FieldCount=0)
if struct_entry.Name is not None and (string.strstr(struct_entry.Name, "SECURITY_ATTRIBUTES") is not None
or string.strstr(struct_entry.Name, "WIN32_FIND_DATAA") is not None):
VLogger.info(f"[DBG-ADDF] {struct_entry.Name} field={field_name} idx={idx}", "DBG")
fe: GenTypes.FieldEntry | t.CPtr = self._get_field_entry(struct_entry, idx)
if fe is None:
@@ -878,6 +882,13 @@ class StructGenMixin:
mod_entry_q: GenTypes.StructEntry | t.CPtr = \
self.find_struct_by_module(bare_q, mod_sha1_q)
if mod_entry_q is not None:
# 依赖未就绪容忍(对齐裸名路径 L926):FieldCount==0 的
# 空类(注册不完整/源模块未编译)→ 返回 None 让上层标记
# _deps_pending 下遍重译,不在此 fatal(WIN32_FIND_DATAA
# 字段访问 cFileName 报 FieldCount=0 根因之一)。
if mod_entry_q.FieldCount == 0:
stdlib.free(mod_buf_q)
return None
for fi_q in range(mod_entry_q.FieldCount):
fe_q: GenTypes.FieldEntry | t.CPtr = \
self._get_field_entry(mod_entry_q, fi_q)