fix(fstr): f-string 改回每站点栈缓冲 + 禁止逃逸,逃逸点改 stdlib 拷贝
ExprGenFstr._fstr_finalize:结果缓冲改回每站点栈 alloca(线程安全、零运行时依赖;每站点各建一个,站点之间不再共享),docstring 与文件头写明语义:f-string 结果是当前函数内的临时值。 GenApi 新增唯一判据 fail_if_fstring_escape 与唯一拷贝出口 escape_fstring_copy(stdlib.malloc + string.strcpy);逃逸位置接护栏:translate_return(函数返回值)、translate_assign(字段/元素/全局变量赋值)、handle_module_level_var(模块级 AnnAssign 初始化)。 编译器自身 7 处逃逸点(ExprGenBase._cast_fail_where 6 处、AssignGenMixin.const_assign_msg 1 处)改为 escape_fstring_copy 显式拷贝——正是造成 ASan stack-buffer-underflow in vsnprintf 的那条路径。 残留(已在 GenApi 头注释诚实记录):间接逃逸(把 f-string 传进会保存它的函数、或放进容器后带走容器)静态判不出;类字段默认值路径未接护栏。全仓自查:TransPyV/App、includes、ViperApps 均无 容器/字段/全局 存 f-string 的写法,也无残留 return f-string。
This commit is contained in:
1 parent
72a3c483e4
commit
4255ab9d32
6 files changed
+140
-54
No files matched your search
@@ -4,6 +4,7 @@ import ast
|
||||
import llvmlite
|
||||
import memhub
|
||||
import string
|
||||
import stdlib
|
||||
import lib.core.Gen.GenTypes as GenTypes
|
||||
import lib.core.Gen.Generator as Generator
|
||||
import lib.core.Gen.Translator as HT
|
||||
@@ -505,9 +506,78 @@ def forward_declare_functions(trans: HT.Translator*, tree: ast.AST*) -> int:
|
||||
"""委托到 gen.forward_declare_functions(原 Handles 壳)"""
|
||||
if _g_gen is not None:
|
||||
return _g_gen.FuncGenDef.forward_declare_functions(trans, tree)
|
||||
|
||||
|
||||
# ============================================================
|
||||
# f-string 逃逸规则(判据 + 拷贝出口,本文件为唯一实现)
|
||||
#
|
||||
# f-string 的结果指针指向**当前函数的栈缓冲**(见 ExprGenFstr._fstr_finalize):
|
||||
# · 栈缓冲 ⇒ 线程安全(每线程各自栈帧)+ 零运行时依赖(内核无 C-ABI 分配器);
|
||||
# · 函数返回即失效 ⇒ 把该指针带出函数的写法都是悬空读。历史事故:调用方
|
||||
# `return f"..."` 后由上层当 %s 喂 snprintf ⇒ ASan stack-buffer-underflow
|
||||
# in vsnprintf(READ size 1,地址落在栈左红区)。
|
||||
#
|
||||
# 因此:逃逸位置一律 fail-fast;确需逃逸时用 escape_fstring_copy 显式拷贝。
|
||||
#
|
||||
# 覆盖范围与残留(诚实记录):本判据覆盖 return / 赋值到字段、元素、全局这类
|
||||
# **直接逃逸**;把 f-string 传进"会保存它"的函数、或放进容器后带走容器,属于
|
||||
# 间接逃逸,静态判不出,仍靠规则约束(用 escape_fstring_copy 显式拷贝)。
|
||||
# ============================================================
|
||||
def fail_if_fstring_escape(node: ast.AST*, where: str) -> int:
|
||||
"""value 位置是 f-string(JoinedStr / 裸 FormattedValue,含元组元素)却处于逃逸位置 → fail-fast
|
||||
|
||||
where: 逃逸位置描述(如 "函数返回值" / "字段赋值" / "全局变量初始化")。
|
||||
恒返回 0(命中即终止编译,不返回非 0 值)。
|
||||
"""
|
||||
if node is None:
|
||||
return 0
|
||||
hit: int = 0
|
||||
k: int = node.kind()
|
||||
if k == ast.ASTKind.JoinedStr or k == ast.ASTKind.FormattedValue:
|
||||
hit = 1
|
||||
elif k == ast.ASTKind.Tuple:
|
||||
tp: ast.Tuple* = ast.Tuple*(node)
|
||||
if tp.elts is not None:
|
||||
i: sizet = 0
|
||||
en: sizet = len(tp.elts)
|
||||
while i < en:
|
||||
e: ast.AST* = tp.elts.get(i)
|
||||
if e is not None:
|
||||
ek: int = e.kind()
|
||||
if ek == ast.ASTKind.JoinedStr or ek == ast.ASTKind.FormattedValue:
|
||||
hit = 1
|
||||
break
|
||||
i += 1
|
||||
if hit == 0:
|
||||
return 0
|
||||
if _g_gen is not None:
|
||||
_g_gen.TypeGenErr.fatal_error(node,
|
||||
f"f-string 结果不能用于{where}(逃逸):f-string 指向**当前函数的栈缓冲**,"
|
||||
f"函数返回即失效,把这个指针带出函数会读到已弹栈的帧"
|
||||
f"(历史事故:ASan stack-buffer-underflow in vsnprintf)。"
|
||||
f"需要带出函数时显式分配后拷贝:"
|
||||
f"tmp: str = f\"...\";out: str = GenApi.escape_fstring_copy(tmp);return out。",
|
||||
"TypeError")
|
||||
return 0
|
||||
|
||||
|
||||
def escape_fstring_copy(src: str) -> str:
|
||||
"""把 f-string 结果拷贝到 stdlib 堆并返回(f-string 逃逸的**唯一**出口)。
|
||||
|
||||
f-string 本身是函数内临时值(栈缓冲);要把它带出函数(return / 存字段 /
|
||||
存全局 / 放进长期容器)必须显式拷贝。分配用 stdlib.malloc(CRT),
|
||||
生命周期由调用方决定——编译器自身的报文构造只在错误路径使用,允许不回收。
|
||||
"""
|
||||
if src is None:
|
||||
return None
|
||||
n: sizet = string.strlen(src)
|
||||
out: str = stdlib.malloc(n + 1)
|
||||
if out is None:
|
||||
return None
|
||||
string.strcpy(out, src)
|
||||
return out
|
||||
|
||||
|
||||
# ---------------- create_wrapper_main(委托 TranslatorGenMixin.create_wrapper_main) ----------------
|
||||
def create_wrapper_main(trans: HT.Translator*, tree: ast.AST*, i32_ty: llvmlite.LLVMType*) -> int:
|
||||
"""委托到 gen.create_wrapper_main(原 Handles 壳)"""
|
||||
|
||||
Reference in new issue
Block a user