feat(vm): add call stack depth guard to MirInterpreter

This commit is contained in:
nyash-codex
2025-11-17 18:33:40 +09:00
parent f3cd815c77
commit c551131941
2 changed files with 22 additions and 0 deletions

View File

@ -13,6 +13,23 @@ impl MirInterpreter {
func: &MirFunction,
arg_vals: Option<&[VMValue]>,
) -> Result<VMValue, VMError> {
// Safety valve: cap nested exec_function_inner depth to avoid Rust stack overflow
// on accidental infinite recursion in MIR (e.g., self-recursive call chains).
const MAX_CALL_DEPTH: usize = 1024;
self.call_depth = self.call_depth.saturating_add(1);
if self.call_depth > MAX_CALL_DEPTH {
eprintln!(
"[vm-call-depth] exceeded {} in fn={} (depth={})",
MAX_CALL_DEPTH,
func.signature.name,
self.call_depth
);
self.call_depth = self.call_depth.saturating_sub(1);
return Err(VMError::InvalidInstruction(format!(
"vm call stack depth exceeded (max_depth={}, fn={})",
MAX_CALL_DEPTH, func.signature.name
)));
}
// Phase 1: delegate cross-class reroute / narrow fallbacks to method_router
if let Some(r) = super::method_router::pre_exec_reroute(self, func, arg_vals) { return r; }
let saved_regs = mem::take(&mut self.regs);
@ -96,6 +113,7 @@ impl MirInterpreter {
BlockOutcome::Return(result) => {
self.cur_fn = saved_fn;
self.regs = saved_regs;
self.call_depth = self.call_depth.saturating_sub(1);
return Ok(result);
}
BlockOutcome::Next {

View File

@ -41,6 +41,9 @@ pub struct MirInterpreter {
pub(super) inst_count: u64,
pub(super) branch_count: u64,
pub(super) compare_count: u64,
/// Call stack depth (exec_function_inner nesting). Used as a safety valve
/// to prevent Rust stack overflow on accidental infinite recursion in MIR.
pub(super) call_depth: usize,
}
impl MirInterpreter {
@ -58,6 +61,7 @@ impl MirInterpreter {
inst_count: 0,
branch_count: 0,
compare_count: 0,
call_depth: 0,
}
}