十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Monty如何实现递归限制?堆上帧栈与原生栈双重护栏机制

Monty如何实现递归限制?堆上帧栈与原生栈双重护栏机制 Monty如何实现递归限制堆上帧栈与原生栈双重护栏机制【免费下载链接】montyA minimal, secure Python interpreter written in Rust for use by AI项目地址: https://gitcode.com/GitHub_Trending/monty3/montyMonty是一个用 Rust 编写的极简安全 Python 解释器专为运行 AI 生成的代码而设计。它通过堆上帧栈和原生栈双重护栏来精准实现递归限制前者用可配置的 1000 帧深度上限拦截普通函数递归后者用仅 12 级的固定天花板拦住底层回调重入确保恶意代码永远无法耗尽栈空间导致进程崩溃。为什么沙箱必须限制递归AI 代理写的代码可能包含死递归比如def f(): return f() f()在 CPython 里这只会触发RecursionError但在一个嵌入式沙箱里无限制的递归会直接撑爆原生调用栈、让整个宿主进程崩溃。对 Monty 这样的进程内解释器来说这就是致命漏洞。所以 Monty 的答案不是限制得越多越好而是分层设防哪类递归走哪条护栏各管各的。护栏一堆上帧栈 1000 帧可配置上限Monty 的 VM 不用原生调用栈来执行字节码而是把每次函数调用压成一个CallFrame存储在堆上分配的帧向量里见 crates/monty/src/bytecode/vm/mod.rs。VM 结构体内维护一个深度计数器recursion_depth每压栈一帧先问一遍资源追踪器// crates/monty-types/src/resource.rs pub const DEFAULT_MAX_RECURSION_DEPTH: usize 1000; pub fn check_recursion_depth(self, current_depth: usize) - Result(), ResourceError { let limit self.active_recursion_limit(); if current_depth limit { return Err(ResourceError::Recursion { limit, depth: current_depth 1 }); } Ok(()) }这带来三个新手友好的特性✅上限可配置通过max_recursion_depth参数按会话设置见 docs/resource-limits.md✅默认 1000 帧第 1001 次嵌套调用触发RecursionError与 CPython 行为一致✅异常可捕获与其他内存/时间类终结性错误不同RecursionError是沙箱内可try/except的捕获后执行可继续堆状态保持完整。这一区分逻辑就在 crates/monty/src/resource_checks.rs 中。另外由于深度计数放在VM 上而非堆上容器对象的repr、eq、hash、isinstance、json 编码等同样会计入深度——任何隐式递归都逃不过这个计数器。护栏二原生栈 12 级天花板拦死回调重入有些回调是解释器同步地、原地重入执行的——map()、sorted(key...)、min/max(key...)、递归的__repr__等。它们不会切换 VM 帧而是真实地嵌套调用VM::run消耗的是原生 Rust 调用栈。这类重入被单独封顶源码在 crates/monty/src/bytecode/vm/recursion.rs// 硬编码安全常量沙箱代码无法修改 pub(crate) const MAX_RUN_REENTRY_DEPTH: u8 12;这个 12 是怎么来的注释里写得很直白修复递归回调崩溃时观测到最小原生栈环境macOS/arm64 调试构建约 2 MiB 栈在深度 19 处崩溃于是把上限调低到 12 并留出安全余量。它不经过资源追踪器、不是 Python 可见设置——这是硬安全边界而非用户配置项。巧思RAII 守卫让深度只增不漏双重护栏能可靠工作靠的是 Rust 的 RAII 惯用法。recursion.rs中提供了两种释放机制机制用途释放时机RecursionGuard词法作用域内的递归如容器repr守卫离开作用域时自动decr_recursion()包括错误路径RecursionToken需要活过当前作用域的预约如容器迭代器迭代器持有令牌经defer_drop!清理机制统一释放这保证了无论代码走正常返回还是?提前退出深度计数都不会泄漏——计数虚高会让合法的嵌套调用误报RecursionError虚低则让护栏形同虚设。如何配置递归限制在 Python 侧按会话传入即可limits {max_recursion_depth: 100} with Monty() as pool: with pool.checkout(limitslimits) as session: session.feed_run(...)注意两点只能调低不能调高即使启用sys.setrecursionlimit仅限测试钩子也只能收紧天花板沙箱代码无法抬高宿主设定的上限crates/monty-types/src/resource.rs 中lower_recursion_limit会拒绝更大的值。await不放大深度每个await边界只计一帧await链不会指数级消耗深度预算。完整的限制行为说明可查阅 docs/resource-limits.md 与 limitations/resource_limits.md行为测试用例见 crates/monty/test_cases/recursion__limit_depth.py 和 crates/monty/test_cases/traceback__recursion_error.py。总结双重护栏的设计哲学护栏管什么上限可配置触发异常堆上帧栈普通函数调用递归默认 1000 帧✅max_recursion_depthRecursionError可捕获原生栈map/sorted/repr等回调重入固定 12 级❌ 硬编码常量RecursionError可捕获Monty 的递归限制给沙箱设计者提供了一个清晰范式不要指望单一限制值覆盖所有递归路径——显式的函数调用和隐式的原生回调要分别设防用 RAII 保证计数永不泄漏用可捕获的 RecursionError 不可突破的宿主天花板同时兼顾用户体验与进程安全。【免费下载链接】montyA minimal, secure Python interpreter written in Rust for use by AI项目地址: https://gitcode.com/GitHub_Trending/monty3/monty创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表