From 0b936bf60e266fcb91f53de313afdcd0cb560c2a Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Sat, 7 Nov 2026 15:29:40 -0800 Subject: [PATCH] JavaScript: the script a realm runs does not count against the re-entry limit Limits::reentry bounds runs inside an operation (getters, setters, conversions, indirect eval). The top-level script took one of those slots, so a limit of 0 refused every script and a limit of 1 left no room for a getter. Only indirect eval now counts when it starts a global run. --- crates/js/src/interp.rs | 9 ++++++--- crates/js/src/interp_tests.rs | 11 +++++++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/crates/js/src/interp.rs b/crates/js/src/interp.rs index 6a8be82a2..7df501699 100644 --- a/crates/js/src/interp.rs +++ b/crates/js/src/interp.rs @@ -1810,7 +1810,10 @@ impl Interp { /// Indirect eval and scripts: runs `code` in the global environment and returns its /// completion value. pub(crate) fn run_global(&mut self, code: Rc, is_eval: bool) -> JsResult { - if self.reentry >= self.limits.reentry { + // Indirect eval runs inside an operation and counts against the re-entry limit; the + // script a realm runs does not. + let nested = usize::from(is_eval); + if is_eval && self.reentry >= self.limits.reentry { return Err(Abrupt::Limit(LimitKind::Reentry)); } self.charge_code(&code, declaration_bytes(&code).saturating_add(ENV_BYTES))?; @@ -1823,9 +1826,9 @@ impl Interp { }; self.instantiate_global(&code, var_env, is_eval)?; self.push_code_frame(code, env, var_env, Value::Object(global), true)?; - self.reentry += 1; + self.reentry += nested; let r = self.execute(); - self.reentry -= 1; + self.reentry -= nested; r } diff --git a/crates/js/src/interp_tests.rs b/crates/js/src/interp_tests.rs index 0634f6cf9..30c84d857 100644 --- a/crates/js/src/interp_tests.rs +++ b/crates/js/src/interp_tests.rs @@ -515,6 +515,17 @@ fn reentry_through_getters_ends_with_the_reentry_limit() { } } +#[test] +fn the_script_itself_does_not_count_against_the_reentry_limit() { + let mut realm = small(Limits { reentry: 0, ..Limits::default() }); + assert_eq!(realm.run("1 + 1"), Ok(JsValue::Number(2.0))); + assert_eq!(limit(&mut realm, "var o = { get x() { return 1; } }; o.x"), LimitKind::Reentry); + realm.set_limits(Limits { reentry: 1, ..Limits::default() }); + assert_eq!(realm.run("o.x"), Ok(JsValue::Number(1.0))); + assert_eq!(realm.run("(0, eval)('2 + 2')"), Ok(JsValue::Number(4.0))); + assert_eq!(limit(&mut realm, "var p = { get x() { return o.x; } }; p.x"), LimitKind::Reentry); +} + #[test] fn memory_bombs_end_with_the_heap_limit() { let limits = Limits { heap_bytes: 4 << 20, ..Limits::default() };