fix(includes): ASan 集成支持 + 越界写封堵 + TypePrint 类型名截断 40→255

1. Phase2Translator 增加 TPC_ASAN 支持(opt asan 插桩+链接 -fsanitize=address),配合 clang64 定位 0xbe 崩溃。2. llvmlite/memhub/viperio/viperlib 多处理无界 strcpy/strcat/下溢/递归守卫/栈数组钳制。3. memhub alloc 块尾 canary(MEMBUDDY_CANARY)+check_alive_blocks 校验+chk_stack realloc 扩容。4. TypePrint Ptr(Struct) 类型名截断上限 40→255:含 sha1 前缀类型名(如 bbdf3bbd4c3bc28c.CONSOLE_SCREEN_BUFFER_INFO=42)被截断产生未定义类型引用(llc undefined type)。
This commit is contained in:
TermiNexus committed 2026-08-10 18:09:13 +08:00
1 parent cb557c84ce
commit d2d319eb21
10 files changed
+195 -49

No files matched your search

+4
View File
@@ -1813,6 +1813,10 @@ def _append_cstr(dst: t.CChar | t.CPtr, dst_size: t.CSizeT, src: t.CChar | t.CPt
"""将 C 字符串 src 追加到 dst 末尾"""
if dst is None or src is None: return
dlen: t.CSizeT = string.strlen(dst)
# 无符号下溢防护:dlen 超过缓冲大小时(缓冲已被破坏/悬空)直接返回,
# 否则 remain = dst_size - dlen 下溢成巨值会把 dst[dlen+i] 写穿到缓冲外
if dlen >= dst_size:
return
slen: t.CSizeT = string.strlen(src)
remain: t.CSizeT = dst_size - dlen
if remain <= 0: return
+23 -16
View File
@@ -489,24 +489,27 @@ def _replace_ssa_numbers(text: t.CChar | t.CPtr,
nxt: t.CChar | t.CPtr = src_p + 1
if nxt is not None and _is_digit_ch(nxt[0]) != 0:
num: t.CInt = string.atoi(nxt)
new_num: t.CInt = _map_get_i32(map_arr, num)
if num >= 0 and num <= max_n and new_num != -1:
# 写入 '%'
new_buf[dst_idx] = '%'
dst_idx += 1
# 写入新编号
num_buf[0] = '\0'
viperlib.snprintf(num_buf, 16, "%d", new_num)
k: t.CSizeT = 0
while num_buf[k] != '\0' and dst_idx + 1 < new_size:
new_buf[dst_idx] = num_buf[k]
# 先做范围检查再读 map_arr:num 超 max_n 时 map_arr 只有
# (max_n+1)*4 字节,先读后查会越界读远端内存
if num >= 0 and num <= max_n:
new_num: t.CInt = _map_get_i32(map_arr, num)
if new_num != -1:
# 写入 '%'
new_buf[dst_idx] = '%'
dst_idx += 1
k += 1
# 跳过旧数字字符
src_p = src_p + 1
while src_p is not None and _is_digit_ch(src_p[0]) != 0:
# 写入新编号
num_buf[0] = '\0'
viperlib.snprintf(num_buf, 16, "%d", new_num)
k: t.CSizeT = 0
while num_buf[k] != '\0' and dst_idx + 1 < new_size:
new_buf[dst_idx] = num_buf[k]
dst_idx += 1
k += 1
# 跳过旧数字字符
src_p = src_p + 1
continue
while src_p is not None and _is_digit_ch(src_p[0]) != 0:
src_p = src_p + 1
continue
# 普通字符:直接复制
new_buf[dst_idx] = src_p[0]
dst_idx += 1
@@ -713,6 +716,10 @@ def _append_cstr(dst: t.CChar | t.CPtr, dst_size: t.CSizeT, src: t.CChar | t.CPt
"""将 C 字符串 src 追加到 dst 末尾"""
if dst is None or src is None: return
dlen: t.CSizeT = string.strlen(dst)
# 无符号下溢防护:dlen 超过缓冲大小时(缓冲已被破坏/悬空)直接返回,
# 否则 remain = dst_size - dlen 下溢成巨值会把 dst[dlen+i] 写穿到缓冲外
if dlen >= dst_size:
return
slen: t.CSizeT = string.strlen(src)
remain: t.CSizeT = dst_size - dlen
if remain <= 0: return
+14 -5
View File
@@ -405,10 +405,15 @@ def _is_type_defined_in_module(mod: LLVMModule | t.CPtr, name: t.CChar | t.CPtr)
def _scan_type_for_cross_module_refs(pool: memhub.MemBuddy | t.CPtr,
mod: LLVMModule | t.CPtr,
ty: LLVMType | t.CPtr):
ty: LLVMType | t.CPtr,
depth: t.CInt = 0):
"""递归扫描类型树,为跨模块结构体引用添加 opaque 声明"""
if ty is None or mod is None or pool is None:
return
# 深度守卫:自引用结构体(链表/树节点,如 Node{...; Ptr(Node) next})
# 会无限递归栈溢出,与其他递归(TypePrint/DeepCopyType)一致设上限
if depth >= 100:
return
match ty:
case LLVMType.Ptr(pointee):
if pointee is not None:
@@ -429,21 +434,21 @@ def _scan_type_for_cross_module_refs(pool: memhub.MemBuddy | t.CPtr,
fld: ParamNode | t.CPtr = sfields
while fld is not None:
if fld.Ty is not None:
_scan_type_for_cross_module_refs(pool, mod, fld.Ty)
_scan_type_for_cross_module_refs(pool, mod, fld.Ty, depth + 1)
fld = fld.Next
case _:
_scan_type_for_cross_module_refs(pool, mod, pointee)
_scan_type_for_cross_module_refs(pool, mod, pointee, depth + 1)
case LLVMType.Struct(sfields2, sfcount2, sname2):
# 顶层结构体(非指针):递归扫描字段
if sfields2 is not None:
fld2: ParamNode | t.CPtr = sfields2
while fld2 is not None:
if fld2.Ty is not None:
_scan_type_for_cross_module_refs(pool, mod, fld2.Ty)
_scan_type_for_cross_module_refs(pool, mod, fld2.Ty, depth + 1)
fld2 = fld2.Next
case LLVMType.Array(elem_ty, acount):
if elem_ty is not None:
_scan_type_for_cross_module_refs(pool, mod, elem_ty)
_scan_type_for_cross_module_refs(pool, mod, elem_ty, depth + 1)
case _:
pass
@@ -590,6 +595,10 @@ def _append_cstr(dst: t.CChar | t.CPtr, dst_size: t.CSizeT, src: t.CChar | t.CPt
"""将 C 字符串 src 追加到 dst 末尾"""
if dst is None or src is None: return
dlen: t.CSizeT = string.strlen(dst)
# 无符号下溢防护:dlen 超过缓冲大小时(缓冲已被破坏/悬空)直接返回,
# 否则 remain = dst_size - dlen 下溢成巨值会把 dst[dlen+i] 写穿到缓冲外
if dlen >= dst_size:
return
slen: t.CSizeT = string.strlen(src)
remain: t.CSizeT = dst_size - dlen
if remain <= 0: return
+27 -16
View File
@@ -416,26 +416,33 @@ def _TypePrintDepth(buf: t.CChar | t.CPtr, size: t.CSizeT,
match pointee:
case LLVMType.Struct(sfields, sfcount, sname):
if sname is not None:
# 悬空防护:sname 可能指向被 reset 回收/部分复用的 pool 内存,
# 用受限打印避免读越界崩溃;完整名在 vtable 生成后由
# TypeStr 兜底(stdlib 深拷贝,跨 reset 存活)
viperlib.snprintf(buf, size, "%%\"%.40s\"*", sname)
# 悬空防护:sname 可能指向被 reset 回收/部分复用的 pool 内存。
# viperlib 的 %s 未实现 precision(%.40s 会全量读 sname),
# 手动 strncpy 限长拷贝后再打印,读侧限制在缓冲内。
# 类型名含 sha1 前缀可超 40 字符(如
# "bbdf3bbd4c3bc28c.CONSOLE_SCREEN_BUFFER_INFO"=42),
# 原 40 字节截断会产生未定义类型引用(llc: undefined type)。
sn_cp: t.CChar | t.CPtr = pool.alloc(256)
if sn_cp is not None:
string.strncpy(sn_cp, sname, 255)
sn_cp[255] = '\0'
viperlib.snprintf(buf, size, "%%\"%s\"*", sn_cp)
return
case LLVMType.Void():
# Ptr(Void) → i8*(LLVM IR void* 非法)
string.strcpy(buf, "i8*")
viperlib.snprintf(buf, size, "%s", "i8*")
return
case LLVMType.Label():
string.strcpy(buf, "i8*")
viperlib.snprintf(buf, size, "%s", "i8*")
return
case LLVMType.Metadata():
string.strcpy(buf, "i8*")
viperlib.snprintf(buf, size, "%s", "i8*")
return
case _:
pass
# pointee == None 时也兜底 Ptr(Int8) 打印 i8*
if pointee is None:
string.strcpy(buf, "i8*")
viperlib.snprintf(buf, size, "%s", "i8*")
return
# 通用路径: 递归打印 pointee 后追加 '*'
_TypePrintDepth(buf, size, pointee, pool, depth + 1)
@@ -484,24 +491,24 @@ def _TypePrintDepth(buf: t.CChar | t.CPtr, size: t.CSizeT,
first2 = 0
_strappend(buf, size, ")")
case LLVMType.Void():
string.strcpy(buf, "void")
viperlib.snprintf(buf, size, "%s", "void")
case LLVMType.Label():
string.strcpy(buf, "label")
viperlib.snprintf(buf, size, "%s", "label")
case LLVMType.Metadata():
string.strcpy(buf, "metadata")
viperlib.snprintf(buf, size, "%s", "metadata")
case LLVMType.Float(bits):
if bits == 16:
string.strcpy(buf, "half")
viperlib.snprintf(buf, size, "%s", "half")
elif bits == 32:
string.strcpy(buf, "float")
viperlib.snprintf(buf, size, "%s", "float")
elif bits == 64:
string.strcpy(buf, "double")
viperlib.snprintf(buf, size, "%s", "double")
elif bits == 128:
string.strcpy(buf, "fp128")
viperlib.snprintf(buf, size, "%s", "fp128")
else:
viperlib.snprintf(buf, size, "float%x", bits)
case _:
string.strcpy(buf, "void")
viperlib.snprintf(buf, size, "%s", "void")
# ============================================================
@@ -511,6 +518,10 @@ def _strappend(dst: t.CChar | t.CPtr, dst_size: t.CSizeT, src: t.CChar | t.CPtr)
"""将 src 追加到 dst 末尾(不超过 dst_size)"""
if dst is None or src is None: return
dlen: t.CSizeT = string.strlen(dst)
# 无符号下溢防护:dlen 超过缓冲大小时(缓冲已被破坏/悬空)直接返回,
# 否则 remain = dst_size - dlen 下溢成巨值会把 dst[dlen+i] 写穿到 GB 外
if dlen >= dst_size:
return
slen: t.CSizeT = string.strlen(src)
remain: t.CSizeT = dst_size - dlen
if remain <= 0: return
+4
View File
@@ -220,6 +220,10 @@ def _append_str(dst: t.CChar | t.CPtr, dst_size: t.CSizeT, src: t.CChar | t.CPtr
"""将 src 追加到 dst 末尾"""
if dst is None or src is None: return
dlen: t.CSizeT = string.strlen(dst)
# 无符号下溢防护:dlen 超过缓冲大小时(缓冲已被破坏/悬空)直接返回,
# 否则 remain = dst_size - dlen 下溢成巨值会把 dst[dlen+i] 写穿到缓冲外
if dlen >= dst_size:
return
slen: t.CSizeT = string.strlen(src)
remain: t.CSizeT = dst_size - dlen
if remain <= 0: return
+4
View File
@@ -82,6 +82,10 @@ def _append_cstr(dst: t.CChar | t.CPtr, dst_size: t.CSizeT, src: t.CChar | t.CPt
"""将 src 追加到 dst 末尾"""
if dst is None or src is None: return
dlen: t.CSizeT = string.strlen(dst)
# 无符号下溢防护:dlen 超过缓冲大小时(缓冲已被破坏/悬空)直接返回,
# 否则 remain = dst_size - dlen 下溢成巨值会把 dst[dlen+i] 写穿到缓冲外
if dlen >= dst_size:
return
slen: t.CSizeT = string.strlen(src)
remain: t.CSizeT = dst_size - dlen
if remain <= 0: return