From 451be82b32dbdb54f4a071218ee2dd855f0566fc Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Mon, 10 Aug 2026 18:09:23 +0800 Subject: [PATCH] =?UTF-8?q?fix(gen):=20=E6=B8=85=E7=90=86=20ASan=20?= =?UTF-8?q?=E4=BA=8C=E5=88=86=E8=B0=83=E8=AF=95=E6=A3=80=E6=9F=A5=20+=20St?= =?UTF-8?q?dlibAlloc=20=E8=B7=A8=20Mixin=20=E5=A7=94=E6=89=98=20+=20?= =?UTF-8?q?=E8=B0=83=E8=AF=95=E9=93=BE=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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/越界封堵等)。 --- App/lib/StubGen/VpiReader.vp | 14 +++- App/lib/core/BuildPipeline.vp | 2 - App/lib/core/Gen/CallGenMixin.vp | 35 ++++++---- App/lib/core/Gen/ClassGenMixin.vp | 37 ++++++++-- App/lib/core/Gen/FuncGenMixin.vp | 8 ++- App/lib/core/Gen/StructGenMixin.vp | 82 +++++++++++++++++++++-- App/lib/core/Gen/TypeGenMixin.vp | 32 +-------- App/lib/core/Handles/HandlesImports.vp | 16 +++-- App/lib/core/Handles/HandlesStruct.vp | 9 +++ App/lib/core/Handles/HandlesTranslator.vp | 2 - App/lib/core/Handles/HandlesType.vp | 7 +- App/lib/core/Phase1.vp | 42 ++++++++++++ 12 files changed, 217 insertions(+), 69 deletions(-) diff --git a/App/lib/StubGen/VpiReader.vp b/App/lib/StubGen/VpiReader.vp index 1bdaa84..eb3736f 100644 --- a/App/lib/StubGen/VpiReader.vp +++ b/App/lib/StubGen/VpiReader.vp @@ -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: diff --git a/App/lib/core/BuildPipeline.vp b/App/lib/core/BuildPipeline.vp index d4f7357..0b35370 100644 --- a/App/lib/core/BuildPipeline.vp +++ b/App/lib/core/BuildPipeline.vp @@ -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 diff --git a/App/lib/core/Gen/CallGenMixin.vp b/App/lib/core/Gen/CallGenMixin.vp index b1cc92d..56fe776 100644 --- a/App/lib/core/Gen/CallGenMixin.vp +++ b/App/lib/core/Gen/CallGenMixin.vp @@ -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) diff --git a/App/lib/core/Gen/ClassGenMixin.vp b/App/lib/core/Gen/ClassGenMixin.vp index 87bc87a..5955758 100644 --- a/App/lib/core/Gen/ClassGenMixin.vp +++ b/App/lib/core/Gen/ClassGenMixin.vp @@ -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 diff --git a/App/lib/core/Gen/FuncGenMixin.vp b/App/lib/core/Gen/FuncGenMixin.vp index f79fd82..dc7992a 100644 --- a/App/lib/core/Gen/FuncGenMixin.vp +++ b/App/lib/core/Gen/FuncGenMixin.vp @@ -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) # ============================================================ diff --git a/App/lib/core/Gen/StructGenMixin.vp b/App/lib/core/Gen/StructGenMixin.vp index dff5a1e..df9916d 100644 --- a/App/lib/core/Gen/StructGenMixin.vp +++ b/App/lib/core/Gen/StructGenMixin.vp @@ -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: diff --git a/App/lib/core/Gen/TypeGenMixin.vp b/App/lib/core/Gen/TypeGenMixin.vp index 22eab28..742fae4 100644 --- a/App/lib/core/Gen/TypeGenMixin.vp +++ b/App/lib/core/Gen/TypeGenMixin.vp @@ -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: diff --git a/App/lib/core/Handles/HandlesImports.vp b/App/lib/core/Handles/HandlesImports.vp index 8235330..be0d9ae 100644 --- a/App/lib/core/Handles/HandlesImports.vp +++ b/App/lib/core/Handles/HandlesImports.vp @@ -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 diff --git a/App/lib/core/Handles/HandlesStruct.vp b/App/lib/core/Handles/HandlesStruct.vp index 443bb14..ca452eb 100644 --- a/App/lib/core/Handles/HandlesStruct.vp +++ b/App/lib/core/Handles/HandlesStruct.vp @@ -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 diff --git a/App/lib/core/Handles/HandlesTranslator.vp b/App/lib/core/Handles/HandlesTranslator.vp index 2eb8de5..81380e3 100644 --- a/App/lib/core/Handles/HandlesTranslator.vp +++ b/App/lib/core/Handles/HandlesTranslator.vp @@ -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: diff --git a/App/lib/core/Handles/HandlesType.vp b/App/lib/core/Handles/HandlesType.vp index 7ed5cb4..231d3bc 100644 --- a/App/lib/core/Handles/HandlesType.vp +++ b/App/lib/core/Handles/HandlesType.vp @@ -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, diff --git a/App/lib/core/Phase1.vp b/App/lib/core/Phase1.vp index 1811c42..650b827 100644 --- a/App/lib/core/Phase1.vp +++ b/App/lib/core/Phase1.vp @@ -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: