fix(gen): 清理 ASan 二分调试检查 + StdlibAlloc 跨 Mixin 委托 + 调试链修复

1. 移除 ClassGenMixin 泛型判断前/模板注册后与 Phase1 五处 gen 完整性二分检查(GlobalFuncs 调试残留,Mixin 方法体访问非自身字段违反块内索引约束)。2. ClassGenMixin._register_struct 读 self.StdlibAlloc(StructGenMixin 字段)改经 HandlesStruct.get_stdlib_alloc 委托,新增 StructGenMixin.get_stdlib_alloc。3. 含 P5 后累积修复(VpiReader ParamCount/越界封堵等)。
This commit is contained in:
TermiNexus committed 2026-08-10 18:09:23 +08:00
1 parent ba3f22702e
commit 451be82b32
12 files changed
+217 -69

No files matched your search

+13 -1
View File
@@ -316,12 +316,24 @@ def _subst_template_arg(annot: str, tparam: str, targ: str) -> str:
tglen: t.CSizeT = string.strlen(targ)
if tplen == 0 or tplen > alen:
return annot
out: bytes = stdlib.malloc(alen + tglen * 8 + 16)
# 容量 = alen + tglen*8 + 16 只覆盖 ≤8 次替换;替换次数无上限时
# opos 会越界写穿 stdlib 堆(哨兵盲区 → CRT 堆损坏后任意 malloc/free
# 静默段错误、崩溃点漂移)。按最坏上界分配:每替换消耗 tplen 写 tglen,
# 最坏全部被替换 → alen + (alen/tplen)*tglen + 1。
max_sub: t.CSizeT = alen / tplen
if max_sub == 0:
max_sub = 1
out_size: t.CSizeT = alen + max_sub * tglen + 1
out: bytes = stdlib.malloc(out_size)
if out is None:
return None
opos: t.CSizeT = 0
p: t.CSizeT = 0
while p < alen:
# 防御:opos 超分配即 fail-fast 返回 None(绝不越界写)
if opos >= out_size:
stdlib.free(out)
return None
matched: int = 0
if annot[p] == tparam[0] and p + tplen <= alen:
if string.strncmp(annot + p, tparam, tplen) == 0:
-2
View File
@@ -171,8 +171,6 @@ def TranslateFileGetTrans(mb: memhub.MemBuddy | t.CPtr, file_path: str,
if string.strstr(file_path, "includes") is not None:
strict_mode = 0
HandlesStruct.reset_visible_structs(mb, strict_mode)
# CHK-FLOAT: translate 前检查浮点 Constant(二分定位覆写阶段)
HandlesType._dbg_check_ast_floats(mb, tree, "PRE-TR")
ret: int = tr.translate(tree)
if ret != 0:
return None
+23 -12
View File
@@ -158,9 +158,12 @@ class CallGenMixin:
string.memset(self.CexportFuncArr, 0, _CEXPORT_MAX * 64)
if self.CexportCount < _CEXPORT_MAX:
idx: t.CSizeT = t.CSizeT(self.CexportCount) * 17
string.strcpy(self.CexportSha1Arr + idx, sha1)
# 槽位定长 17/64 字节,strcpy 无界会越界写相邻槽(stdlib 堆区)
string.strncpy(self.CexportSha1Arr + idx, sha1, 16)
self.CexportSha1Arr[idx + 16] = '\0'
idx2: t.CSizeT = t.CSizeT(self.CexportCount) * 64
string.strcpy(self.CexportFuncArr + idx2, func_name)
string.strncpy(self.CexportFuncArr + idx2, func_name, 63)
self.CexportFuncArr[idx2 + 63] = '\0'
self.CexportCount += 1
return 1
return 0
@@ -1744,7 +1747,9 @@ class CallGenMixin:
elif tk == ast.ASTKind.Constant:
cn2: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(first_arg)
if cn2.str_val is not None:
string.strcpy(text, cn2.str_val)
# 裸 strcpy 无界:模板常量串 ≥ LLVMIR_TEMPLATE_BUF 会越界写 pool 块,
# 改用与 JoinedStr 分支一致的有界追加
self._asm_append_cstr(text, LLVMIR_TEMPLATE_BUF, cn2.str_val)
# ============================================================
# 2. 直接嵌入模板文本(不做指令解析)+ LOut 写回
@@ -2937,7 +2942,7 @@ class CallGenMixin:
# 构造搜索模式: "{sha1}.{class_name}.{method_name}"
cls_len: t.CSizeT = string.strlen(class_name)
meth_len: t.CSizeT = string.strlen(method_name)
search_len: t.CSizeT = sha1_len + cls_len + meth_len + 3
search_len: t.CSizeT = sha1_len + cls_len + meth_len + 4
search_buf: bytes = stdlib.malloc(search_len)
if search_buf is None:
stdlib.free(read_buf)
@@ -3407,7 +3412,7 @@ class CallGenMixin:
base_buf_g[base_len_g] = '\0'
string.strncpy(arg_buf_g, lbkt_g + 1, arg_len_g)
arg_buf_g[arg_len_g] = '\0'
ta_list_g: list[str] | t.CPtr = list[str](pool, 4)
ta_list_g: list[str] | t.CPtr = list[str](pool, 8)
if ta_list_g is not None:
# 逗号分隔多类型实参(list[X] / dict[K, V])
cur_arg_g: str = arg_buf_g
@@ -4228,6 +4233,11 @@ class CallGenMixin:
ai: t.CSizeT = 0
while ai < can:
if ai >= 32:
# 定长 8*32 槽:超过 32 个实参继续写会越界污染 pool 相邻块,fail-fast
HandlesType.fatal_error(
cl, "泛型构造实参超过 32 个上限", "TypeError")
break
arg_node: ast.AST | t.CPtr = cargs.get(ai)
if arg_node is not None:
arg_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
@@ -5793,7 +5803,8 @@ class CallGenMixin:
if self.FuncptrCount >= MAX_FUNC_PTR_SIGS:
return 1
idx: t.CSizeT = t.CSizeT(self.FuncptrCount) * 64
string.strcpy(self.FuncptrNames + idx, name)
# 槽位定长 64 字节,与 register_closure_name 一致用 strncpy 钳制防越界
string.strncpy(self.FuncptrNames + idx, name, 63)
self.FuncptrRets[self.FuncptrCount] = t.CUInt64T(ret_ty)
self.FuncptrCount += 1
return 0
@@ -6421,25 +6432,25 @@ class CallGenMixin:
if ret_part is not None and ret_ty is not None:
ret_part[0] = '\0'
llvmlite.TypePrint(ret_part, 512, ret_ty, pool)
string.strcat(ty_buf, ret_part)
string.strcat(ty_buf, " (")
self._asm_append_cstr(ty_buf, 1024, ret_part)
self._asm_append_cstr(ty_buf, 1024, " (")
# 参数类型
# 参数类型(全部经 _asm_append_cstr 有界追加,防止 strcat 无界写穿 1024 缓冲)
cur: llvmlite.Param | t.CPtr = llvmlite.function_get_param_head(func)
first: int = 1
param_part: t.CChar | t.CPtr = pool.alloc(512)
while cur is not None:
if first == 0:
string.strcat(ty_buf, ", ")
self._asm_append_cstr(ty_buf, 1024, ", ")
if param_part is not None:
param_part[0] = '\0'
param_ty: llvmlite.LLVMType | t.CPtr = llvmlite.param_get_ty(cur)
if param_ty is not None:
llvmlite.TypePrint(param_part, 512, param_ty, pool)
string.strcat(ty_buf, param_part)
self._asm_append_cstr(ty_buf, 1024, param_part)
cur = llvmlite.param_get_next(cur)
first = 0
string.strcat(ty_buf, ")")
self._asm_append_cstr(ty_buf, 1024, ")")
# 深拷贝到 stdlib 区(TypeStr 跨文件永久存活)
ts_len: t.CSizeT = string.strlen(ty_buf)
+33 -4
View File
@@ -316,7 +316,9 @@ class ClassGenMixin:
stdlib.free(key_buf)
return
if self.MethodMetaCount < META_MAX:
string.strcpy(self.MethodMetaKeys + self.MethodMetaCount * META_KEY_LEN, key_buf)
# 槽位定长 META_KEY_LEN=128 字节,key 超长时 strncpy 钳制防越界写相邻槽
string.strncpy(self.MethodMetaKeys + self.MethodMetaCount * META_KEY_LEN,
key_buf, META_KEY_LEN - 1)
flags_ptr2: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(
t.CUInt64T(self.MethodMetaFlags) + self.MethodMetaCount * 4, t.CPtr))
flags_ptr2[0] = meta
@@ -1353,7 +1355,11 @@ class ClassGenMixin:
for pos in range(1, total_positions):
pos_max: int = 0
for vi in range(variant_count):
if vi >= 64:
break # variant_field_sizes 64 类槽上限,防读越界
field_pos: int = pos - 1 # 变体字段从位置 0 开始
if field_pos >= 16:
continue # 超出每变体 16 字段槽位:未写入(memset 为 0),跳过
offset2: t.CSizeT = vi * 16 + field_pos
fsize_val: t.CSizeT = variant_field_sizes[offset2]
if fsize_val > pos_max:
@@ -1510,6 +1516,14 @@ class ClassGenMixin:
for ci in range(cn):
if tag >= variant_count:
break
if tag >= RENUM_VARIANT_PER_CLASS:
# 每类固定 32 槽:变体超过上限继续写会越界污染相邻类槽,fail-fast 报错
fb_rv: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb_rv is not None:
viperlib.snprintf(fb_rv, 1024,
"REnum 变体数超过上限 %d: %s", RENUM_VARIANT_PER_CLASS, class_name)
VLogger.error(fb_rv, "RENUM")
break
stmt: ast.AST | t.CPtr = children.get(ci)
if stmt is None:
continue
@@ -2598,7 +2612,17 @@ class ClassGenMixin:
if is_novtable_deco == 1:
entry.IsNoVTable = 1
if parent_name is not None:
entry.ParentName = parent_name
# 1b 阶段父类名是 pool 字符串(本文件 AST/1a 预存),reset 后悬空;
# _translate_oop_methods/_detect_vtable_status 会在后续文件
# find_struct(ParentName) strcmp 解引用 → 用 stdlib 拷贝保证跨文件存活
if HandlesStruct.get_stdlib_alloc() != 0:
pn_len: t.CSizeT = string.strlen(parent_name)
pn_buf: t.CChar | t.CPtr = stdlib.malloc(pn_len + 1)
if pn_buf is not None:
string.strcpy(pn_buf, parent_name)
entry.ParentName = pn_buf
else:
entry.ParentName = parent_name
# 多继承:设置父类数组(供 MRO/字段/vtable 查询,stdlib 深拷贝跨文件存活)
if parents_count > 0 and parents_buf is not None:
HandlesStruct.set_parent_names(class_name, parents_buf, parents_count)
@@ -3268,9 +3292,14 @@ class ClassGenMixin:
return 0
viperlib.snprintf(method_name_buf, 128, "%s", fd.name)
if func_meta & META_PROP_SETTER != 0:
string.strcat(method_name_buf, "$set")
# 128 字节缓冲:追加 "$set" 前检查剩余容量,防 strcat 无界写穿
mn_len2: t.CSizeT = string.strlen(method_name_buf)
if mn_len2 + 5 < 128:
string.strcat(method_name_buf, "$set")
elif func_meta & META_PROP_DELETER != 0:
string.strcat(method_name_buf, "$del")
mn_len2: t.CSizeT = string.strlen(method_name_buf)
if mn_len2 + 5 < 128:
string.strcat(method_name_buf, "$del")
method_name = method_name_buf
is_static_m: int = 0
+6 -2
View File
@@ -188,11 +188,15 @@ class FuncGenMixin:
self._append_str(attrs_buf, llvm_name)
return 1
def _append_str(self, buf: t.CChar | t.CPtr, s: str) -> None:
"""将字符串追加到 buf 末尾(用 strlen+strcpy 模拟 strcat)"""
def _append_str(self, buf: t.CChar | t.CPtr, s: str,
max_len: t.CSizeT = 256) -> None:
"""将字符串追加到 buf 末尾(有界,防 strcpy 越界写穿定长缓冲)"""
if buf is None or s is None:
return
cur_len: t.CSizeT = string.strlen(buf)
s_len: t.CSizeT = string.strlen(s)
if cur_len + s_len + 1 > max_len:
return # 无剩余容量:丢弃本次追加,避免越界写
string.strcpy(buf + cur_len, s)
# ============================================================
+77 -5
View File
@@ -79,6 +79,11 @@ class StructGenMixin:
"""设置 OOM 回收分配模式:1=1b 阶段(新条目用 stdlib),0=注册阶段(用 pool)"""
self.StdlibAlloc = flag
def get_stdlib_alloc(self) -> int:
"""读取 OOM 回收分配模式(供其他 Mixin 经 HandlesStruct 委托访问,
避免跨 Mixin 直接读 self.StdlibAlloc——Mixin 方法体按自身块内索引编译)"""
return self.StdlibAlloc
def reset_visible_structs(self, pool: memhub.MemBuddy | t.CPtr, strict: int):
"""重置可见性状态(每个文件翻译前调用)
@@ -277,13 +282,76 @@ class StructGenMixin:
fe.Name = name_buf
fe.Index = idx
fe.Ty = field_ty
fe.DefaultVal = default_val
fe.AnnotClassName = annot_class_name
# 1b 阶段(StdlibAlloc=1)字段类型/注解类名必须深拷贝到 stdlib:
# register_struct 已迁 Fields/Name/ModuleSha1/Ty(translate_class_def L2566
# DeepCopyType),但字段级 Ty/AnnotClassName 仍存 pool 指针,本文件
# reset_to(mark1) 回收后,跨文件读取(字段展平、多继承 this 调整、
# AnnotClassName 回退、_apply_struct_defaults 的 build_gep_struct 用 fe.Ty
# 作 GEP 结果类型)解引用悬空内存 → 崩溃点漂移(json/string/builtins 之间)。
if self.StdlibAlloc != 0:
ty_copy: llvmlite.LLVMType | t.CPtr = llvmlite.DeepCopyType(pool, field_ty)
if ty_copy is not None:
fe.Ty = ty_copy
else:
fe.Ty = field_ty
if annot_class_name is not None:
annot_len: t.CSizeT = string.strlen(annot_class_name)
annot_buf: t.CChar | t.CPtr = stdlib.malloc(annot_len + 1)
if annot_buf is not None:
string.strcpy(annot_buf, annot_class_name)
fe.AnnotClassName = annot_buf
# DefaultVal:跨文件结构体构造(_translate_struct_ctor→_apply_struct_defaults)
# 在调用方文件翻译时读取字段默认值,原 pool AST 已 reset 回收 → 常量深拷贝
fe.DefaultVal = self._deep_copy_default_ast(default_val)
else:
fe.Ty = field_ty
fe.AnnotClassName = annot_class_name
fe.DefaultVal = default_val
struct_entry.FieldCount = idx + 1
return idx
def _deep_copy_default_ast(self, default_val: ast.AST | t.CPtr) -> ast.AST | t.CPtr:
"""深拷贝字段默认值 AST 到 stdlib(仅常量节点),跨 1b reset 存活。
字段默认值(fe.DefaultVal)除定义文件内 _generate_before_init 使用外,
跨文件结构体构造(_translate_struct_ctor → _apply_struct_defaults)会在
调用方文件翻译时读取——原 pool AST 已 reset 回收必须深拷贝。
非常量默认值(Name/BinOp 等引用表达式)无法安全深拷贝:返回 None
(调用方 fe.DefaultVal 保持 None,仅定义文件内 _generate_before_init
仍持有原始 pool 指针,不受影响)。
"""
if default_val is None:
return None
if default_val.kind() != ast.ASTKind.Constant:
return None
src_c: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(default_val)
dst_c: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(
stdlib.malloc(ast.Constant.__sizeof__()))
if dst_c is None:
return None
string.memset(dst_c, 0, ast.Constant.__sizeof__())
# 拷贝 vtable 指针(AST 是 @t.CVTable,kind() 虚调用依赖首字段 vptr)
vt_ptr: t.CVoid | t.CPtr = t.CVoid(
c.Deref(t.CUInt64T(default_val, t.CPtr), t.CPtr), t.CPtr)
c.DerefAs(dst_c, vt_ptr)
# 拷贝位置字段(报错定位用)
dst_c.lineno = src_c.lineno
dst_c.col_offset = src_c.col_offset
dst_c.end_lineno = src_c.end_lineno
dst_c.end_col_offset = src_c.end_col_offset
# 拷贝常量值(int/float/str/bool 由 const_kind 区分)
dst_c.const_kind = src_c.const_kind
dst_c.int_val = src_c.int_val
dst_c.float_val = src_c.float_val
if src_c.str_val is not None:
s_len: t.CSizeT = string.strlen(src_c.str_val)
s_copy: t.CChar | t.CPtr = stdlib.malloc(s_len + 1)
if s_copy is not None:
string.strcpy(s_copy, src_c.str_val)
dst_c.str_val = s_copy
return (ast.AST | t.CPtr)(dst_c)
def get_struct_count(self) -> int:
"""返回当前已注册的结构体数量"""
return self.StructCount
@@ -962,8 +1030,12 @@ class StructGenMixin:
sha1_buf: t.CChar | t.CPtr = pool.alloc(32)
if sha1_buf is None:
return
string.strncpy(sha1_buf, sname, sha1_len)
sha1_buf[sha1_len] = '\0'
# 32 字节缓冲:strncpy 不截断,前缀超过 31 字符会越界写 + 手动 NUL 再越界
sha1_cp: t.CSizeT = sha1_len
if sha1_cp > 31:
sha1_cp = 31
string.strncpy(sha1_buf, sname, sha1_cp)
sha1_buf[sha1_cp] = '\0'
# 按类名 + SHA1 查找结构体定义
entry: HandlesStruct.StructEntry | t.CPtr = self.find_struct_by_module(class_name, sha1_buf)
if entry is None:
+2 -30
View File
@@ -182,36 +182,8 @@ class TypeGenMixin:
def _dbg_check_ast_floats(self, mb: memhub.MemBuddy | t.CPtr,
tree: ast.AST | t.CPtr, tag: str):
"""CHK-FLOAT: 遍历 AST 检查所有浮点 Constant 节点"""
if tree is None:
return
if tree.kind() == ast.ASTKind.Constant:
cn_f: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(tree)
if cn_f.const_kind == ast.CONST_FLOAT:
fb_chk: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb_chk is not None:
stdio.snprintf(fb_chk, 1024, "[CHK-FLOAT] %s ptr=%p float_val=%.17g",
tag, t.CSizeT(cn_f), cn_f.float_val)
VLogger.info(fb_chk, "CHK")
return
# AnnAssign: value/annotation/target 不在 children 里,单独遍历
if tree.kind() == ast.ASTKind.AnnAssign:
aa_f: ast.AnnAssign | t.CPtr = (ast.AnnAssign | t.CPtr)(tree)
if aa_f.value is not None:
self._dbg_check_ast_floats(mb, aa_f.value, tag)
if aa_f.annotation is not None:
self._dbg_check_ast_floats(mb, aa_f.annotation, tag)
if aa_f.target is not None:
self._dbg_check_ast_floats(mb, aa_f.target, tag)
return
ch_f: list[ast.AST | t.CPtr] | t.CPtr = tree.children
if ch_f is not None:
n_f: t.CSizeT = ch_f.__len__()
i_f: t.CSizeT
for i_f in range(n_f):
c_f: ast.AST | t.CPtr = ch_f.get(i_f)
if c_f is not None:
self._dbg_check_ast_floats(mb, c_f, tag)
"""已移除调试插桩(占位保留签名,避免外部调用引用失败)"""
pass
def register_cdefine_constant(self, pool: memhub.MemBuddy | t.CPtr,
name: str, value: int) -> None:
+10 -6
View File
@@ -316,15 +316,19 @@ def register_reexport(mod_sha1: str, func_name: str, source_module: str) -> int:
fidx: t.CSizeT = t.CSizeT(i) * 64
if string.strcmp(_g_reexport_sha1s + sidx, mod_sha1) == 0:
if string.strcmp(_g_reexport_funcs + fidx, func_name) == 0:
# 已存在,更新 source_module(以防变化)
string.strcpy(_g_reexport_srcs + fidx, source_module)
# 已存在,更新 source_module(以防变化;64 字节槽,strncpy 钳制)
string.strncpy(_g_reexport_srcs + fidx, source_module, 63)
_g_reexport_srcs[fidx + 63] = '\0'
return 0
# 添加新条目
# 添加新条目(槽位定长 17/64 字节,strcpy 无界会越界写相邻槽,全部 strncpy 钳制)
idx2: t.CSizeT = t.CSizeT(_g_reexport_count) * 17
fidx2: t.CSizeT = t.CSizeT(_g_reexport_count) * 64
string.strcpy(_g_reexport_sha1s + idx2, mod_sha1)
string.strcpy(_g_reexport_funcs + fidx2, func_name)
string.strcpy(_g_reexport_srcs + fidx2, source_module)
string.strncpy(_g_reexport_sha1s + idx2, mod_sha1, 16)
_g_reexport_sha1s[idx2 + 16] = '\0'
string.strncpy(_g_reexport_funcs + fidx2, func_name, 63)
_g_reexport_funcs[fidx2 + 63] = '\0'
string.strncpy(_g_reexport_srcs + fidx2, source_module, 63)
_g_reexport_srcs[fidx2 + 63] = '\0'
_g_reexport_count += 1
return 0
+9
View File
@@ -103,6 +103,15 @@ def set_stdlib_alloc(flag: int):
g.set_stdlib_alloc(flag)
def get_stdlib_alloc() -> int:
"""读取 OOM 回收分配模式(委托到 gen.get_stdlib_alloc)"""
import lib.core.Gen.Generator as Generator
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g.get_stdlib_alloc()
return 0
def reset_visible_structs(pool: memhub.MemBuddy | t.CPtr, strict: int):
"""重置可见性状态(每个文件翻译前调用,委托到 gen.reset_visible_structs)"""
import lib.core.Gen.Generator as Generator
@@ -296,8 +296,6 @@ class Translator:
break
if self._declare_only != 0:
# CHK-FLOAT: 模块级翻译前检查(二分定位覆写阶段)
HandlesType._dbg_check_ast_floats(pool, tree, "PRE-ML")
# Phase 1a-pre(2=import扫描) / Phase 1a(1=struct注册): 只处理模块级,不创建 main 函数和 builder
self._translate_module_level(pool, mod, tree)
elif has_user_main == 0:
+2 -5
View File
@@ -134,11 +134,8 @@ def set_current_module_sha1(sha1: str) -> None:
def _dbg_check_ast_floats(mb: memhub.MemBuddy | t.CPtr,
tree: ast.AST | t.CPtr, tag: str):
"""CHK-FLOAT: 遍历 AST 检查所有浮点 Constant 节点(委托到 gen._dbg_check_ast_floats)"""
import lib.core.Gen.Generator as Generator
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
g._dbg_check_ast_floats(mb, tree, tag)
"""已移除调试插桩(占位保留签名,避免外部调用引用失败)"""
pass
def register_cdefine_constant(pool: memhub.MemBuddy | t.CPtr,
+42
View File
@@ -18,6 +18,7 @@ import lib.core.Handles.HandlesImports as HandlesImports
import lib.core.Handles.HandlesClassDef as HandlesClassDef
import lib.core.IncludesScanner as IncludesScanner
import lib.core.StubMerger as StubMerger
import lib.core.Gen.Generator as GenGenerator
import lib.core.BuildPipeline as BuildPipeline
import lib.Projectrans.Config as Config
import lib.StubGen.Converter as StubConverter
@@ -837,6 +838,7 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
HandlesType.clear_cdefine_constants()
HandlesType.clear_ctypedefs()
HandlesStruct.reset_visible_structs(mb, 0)
gen_tr.translate(gen_tree)
if gen_tr._global_names is not None:
@@ -1199,6 +1201,19 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
HandlesType.clear_cdefine_constants()
HandlesType.clear_ctypedefs()
HandlesStruct.reset_visible_structs(mb, 0)
# 翻译前哨兵:AST 解析(lexer/tokenize/parse_tokens)同样在 pool 分配,
# 若解析器越界写破坏了存活块 header,此处立即定位到当前文件
# (崩溃点漂移排查:把"翻译过程中随机崩"变成确定性定位)
pre_rc: t.CInt = mb.check_alive_blocks(mark1)
if pre_rc != 0:
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb is not None:
viperlib.snprintf(fb, 1024,
"内存损坏(翻译前存活块, rc=%d): %s", pre_rc, file_path)
VLogger.error(fb, "Phase1")
stdlib.exit(1)
ret: int = tr.translate(tree)
if ret != 0:
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
@@ -1286,7 +1301,34 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
# OOM 回收:回收本文件翻译产生的瞬时数据(AST/tokens/lexer/IR/Handler/dump 临时)
# 持久表(struct/enum/REnum/spec 缓存/全局函数名等)已迁出 reset 区间(stdlib),
# reset_to(mark1) 只回收 mark1 之后分配、未被单独 free 的块。
#
# 内存哨兵(崩溃点漂移排查):reset 前检查存活块 header(抓越界写),
# reset 后检查 free list 完整性(抓链表损坏)。任一损坏立即终止,
# 把"随机崩溃漂移"变成"确定性定位到当前文件"。
# rc 语义(check_alive_blocks):1=块地址低于 usable,2=高于 usable+size,
# 3=order 为负,4=order 超 max_order,5=块未对齐到 order 大小,
# 6=块尾 canary 损坏(越界写穿数据区——事发地定位)
# rc 语义(self_check):1/2=越界,3=对齐错,4=free list 成环/超步数
chk_rc: t.CInt = mb.check_alive_blocks(mark1)
if chk_rc != 0:
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb is not None:
viperlib.snprintf(fb, 1024,
"内存损坏(存活块 header, rc=%d): %s", chk_rc, file_path)
VLogger.error(fb, "Phase1")
stdlib.exit(1)
# A/B 实验结论(已确认):禁用 reset_to 后 stdarg 仍崩 → 越界写是根因,
# 与回收/UAF 无关。恢复 reset_to,改用块尾 canary(check_alive_blocks 校验)
# 定位越界写事发地到具体文件。
mb.reset_to(mark1)
sc_rc: t.CInt = mb.self_check()
if sc_rc != 0:
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb is not None:
viperlib.snprintf(fb, 1024,
"内存损坏(free list, rc=%d): %s", sc_rc, file_path)
VLogger.error(fb, "Phase1")
stdlib.exit(1)
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb is not None: