From 301451cb8bc98073d71df47c0eafc91f3cb41e77 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Tue, 25 Aug 2026 10:17:50 +0000 Subject: [PATCH] Add depth limit to parse_json_value to prevent stack overflows The function `parse_json_value` was recursive but unbounded. Parsing an extremely nested JSON payload could result in stack overflows. We've added a `depth` parameter with a maximum allowed depth of 128. If that depth is reached, an explicit RuntimeError (`JSON depth limit exceeded`) is returned. A new test case has been added to verify that deep structures no longer crash the process. Co-authored-by: Tcode-Motion <188012755+Tcode-Motion@users.noreply.github.com> --- compiler/llvm_backend/src/jit.rs | 4 ++-- stdlib/src/json.rs | 39 ++++++++++++++++++++------------ stdlib/tests/stdlib_tests.rs | 19 ++++++++++++++++ 3 files changed, 46 insertions(+), 16 deletions(-) diff --git a/compiler/llvm_backend/src/jit.rs b/compiler/llvm_backend/src/jit.rs index 9005daa3..e05cbfe8 100644 --- a/compiler/llvm_backend/src/jit.rs +++ b/compiler/llvm_backend/src/jit.rs @@ -5,8 +5,8 @@ #![cfg(feature = "llvm")] use llvm_sys::core::*; -use llvm_sys::orc2::*; use llvm_sys::orc2::lljit::*; +use llvm_sys::orc2::*; use std::collections::HashMap; use std::ffi::CString; use std::ptr; @@ -60,7 +60,7 @@ impl LLVMJitEngine { // 3. Set host target triple let _host_triple = LLVMOrcLLJITGetExecutionSession(self.jit); // session triple fallback - // We can just keep the default LLVM target triple + // We can just keep the default LLVM target triple // 4. Wrap Module in ThreadSafeModule let tsm = LLVMOrcCreateNewThreadSafeModule(ctx.module, self.ts_ctx); diff --git a/stdlib/src/json.rs b/stdlib/src/json.rs index 8a5a51ca..99f9135a 100644 --- a/stdlib/src/json.rs +++ b/stdlib/src/json.rs @@ -39,7 +39,7 @@ impl StdlibRegistry { None, ) })?; - Ok(parse_json_value(v)) + parse_json_value(v, 0) }, }), ); @@ -92,36 +92,47 @@ pub fn stringify_value(val: &RuntimeValue) -> Result { } } -pub fn parse_json_value(v: serde_json::Value) -> RuntimeValue { +pub fn parse_json_value(v: serde_json::Value, depth: usize) -> Result { + if depth > 128 { + return Err(RuntimeError::new( + RuntimeErrorKind::InvalidOperation("JSON depth limit exceeded".to_string()), + None, + None, + )); + } + match v { - serde_json::Value::Null => RuntimeValue::Null, - serde_json::Value::Bool(b) => RuntimeValue::Bool(b), + serde_json::Value::Null => Ok(RuntimeValue::Null), + serde_json::Value::Bool(b) => Ok(RuntimeValue::Bool(b)), serde_json::Value::Number(num) => { if let Some(i) = num.as_i64() { - RuntimeValue::Int(i) + Ok(RuntimeValue::Int(i)) } else if let Some(f) = num.as_f64() { - RuntimeValue::Float(f) + Ok(RuntimeValue::Float(f)) } else { - RuntimeValue::Null + Ok(RuntimeValue::Null) } } - serde_json::Value::String(s) => RuntimeValue::Str(s), + serde_json::Value::String(s) => Ok(RuntimeValue::Str(s)), serde_json::Value::Array(arr) => { - let items = arr.into_iter().map(parse_json_value).collect::>(); - RuntimeValue::List { + let mut items = Vec::new(); + for item in arr { + items.push(parse_json_value(item, depth + 1)?); + } + Ok(RuntimeValue::List { items: Rc::new(RefCell::new(items)), is_const: false, - } + }) } serde_json::Value::Object(obj) => { let mut entries = IndexMap::new(); for (k, v) in obj { - entries.insert(k, parse_json_value(v)); + entries.insert(k, parse_json_value(v, depth + 1)?); } - RuntimeValue::Map { + Ok(RuntimeValue::Map { entries: Rc::new(RefCell::new(entries)), is_const: false, - } + }) } } } diff --git a/stdlib/tests/stdlib_tests.rs b/stdlib/tests/stdlib_tests.rs index 7d530343..fb243ac9 100644 --- a/stdlib/tests/stdlib_tests.rs +++ b/stdlib/tests/stdlib_tests.rs @@ -186,6 +186,25 @@ fn test_json_module() { } else { panic!("JSON parse result was not a Map"); } + + // Test depth limit + let mut nested_json_str = String::new(); + for _ in 0..200 { + nested_json_str.push_str("{\"nested\":"); + } + nested_json_str.push_str("null"); + for _ in 0..200 { + nested_json_str.push('}'); + } + + let result = parse.call(&mut ctx, vec![RuntimeValue::Str(nested_json_str)]); + assert!(result.is_err()); + if let Err(e) = result { + assert!( + e.message.contains("JSON depth limit exceeded") + || e.message.contains("JSON parse error") + ); + } } #[test]