{"record":{"id":"7f4d2a166f388fa0","repo":"RightNow-AI/openfang","slug":"host-call-request-out-of-bounds","errorCode":null,"errorMessage":"host_call: request out of bounds","messagePattern":"host_call: request out of bounds","errorType":"validation","errorClass":"SandboxError","httpStatus":null,"severity":"error","filePath":"crates/openfang-runtime/src/sandbox.rs","lineNumber":307,"sourceCode":"        linker\n            .func_wrap(\n                \"openfang\",\n                \"host_call\",\n                |mut caller: Caller<'_, GuestState>,\n                 request_ptr: i32,\n                 request_len: i32|\n                 -> Result<i64, Error> {\n                    // Read request from guest memory\n                    let memory = caller\n                        .get_export(\"memory\")\n                        .and_then(|e| e.into_memory())\n                        .ok_or_else(|| format_err!(\"no memory export\"))?;\n\n                    let data = memory.data(&caller);\n                    let start = request_ptr as usize;\n                    let end = start + request_len as usize;\n                    if end > data.len() {\n                        bail!(\"host_call: request out of bounds\");\n                    }\n                    let request_bytes = data[start..end].to_vec();\n\n                    // Parse request\n                    let request: serde_json::Value = serde_json::from_slice(&request_bytes)?;\n                    let method = request\n                        .get(\"method\")\n                        .and_then(|m| m.as_str())\n                        .unwrap_or(\"\")\n                        .to_string();\n                    let params = request\n                        .get(\"params\")\n                        .cloned()\n                        .unwrap_or(serde_json::Value::Null);\n\n                    // Dispatch to capability-checked handler\n                    let response = host_functions::dispatch(caller.data(), &method, &params);\n","sourceCodeStart":289,"sourceCodeEnd":325,"githubUrl":"https://github.com/RightNow-AI/openfang/blob/acf2587e46be174c10200489c9a2d23a39a98aeb/crates/openfang-runtime/src/sandbox.rs#L289-L325","documentation":"The WASM guest module called the host function openfang.host_call with a (request_ptr, request_len) pair whose byte range [ptr, ptr+len) extends past the end of the guest's linear memory. The host refuses to read out-of-bounds memory rather than trap or read garbage, so it fails the call with this error. It is a guest-side ABI/pointer bug surfaced through the host.","triggerScenarios":"Calling host_call with a pointer or length computed incorrectly in the guest: wrong endianness or negative i32 cast to usize, a stale pointer into a region already freed/reclaimed by the guest allocator, an oversized request_len, or a guest module whose memory shrinks via memory.grow/replace semantics.","commonSituations":"Hand-written guest bindings in Rust/C/WASM modules; a custom allocator returning bogus pointers (e.g. forgetting to grow memory before alloc); passing a length in bytes vs elements; forgetting the 4GB usize cast wraps for negative pointers; guests built against a different ABI version than the host linker expects.","solutions":["Fix the guest code so the pointer/length describe a valid in-bounds region: verify ptr and len are the exact values returned by the guest alloc call for the serialized request buffer","Check that request_len is a byte length (use the serialized JSON's len(), not an element count) and that the buffer was not freed or overwritten before calling host_call","Ensure the guest module exports a 'memory' export whose size actually covers the buffer (call memory.grow if needed before writing)","Add a guest-side bounds assertion (ptr + len <= memory size) before invoking host_call so the failure is caught inside the module with better diagnostics","Rebuild the guest to the current openfang guest ABI and re-run; stale prebuilt .wasm binaries often carry old struct layouts"],"exampleFix":"// before (guest Rust, len in elements)\nlet ptr = alloc(request.len() as i32);\nhost_call(ptr as i32, request.len() as i32);\n// after (len in bytes of the serialized buffer)\nlet bytes = serde_json::to_vec(&request).unwrap();\nlet ptr = alloc(bytes.len() as i32);\nunsafe { core::slice::from_raw_parts_mut(ptr as *mut u8, bytes.len()) }.copy_from_slice(&bytes);\nhost_call(ptr as i32, bytes.len() as i32);","handlingStrategy":"try-catch","validationCode":"// guest-side, before calling host_call\nlet mem_size = memory.size(&store) as usize * 65536;\nlet (start, len) = (request_ptr as usize, request_len as usize);\nassert!(start.checked_add(len).is_some() && start + len <= mem_size, \"request buffer out of bounds\");","typeGuard":"fn in_bounds(ptr: i32, len: i32, mem_len: usize) -> bool {\n    ptr >= 0 && len >= 0 && (ptr as u64) + (len as u64) <= mem_len as u64\n}","tryCatchPattern":"// guest module call, catch the trap/error from host_call\nmatch sandbox.call(&input) {\n    Err(e) if e.to_string().contains(\"request out of bounds\") => {\n        eprintln!(\"guest ABI bug: bad request_ptr/request_len: {e}\");\n        // recompile/fix guest, do not retry blindly\n    }\n    r => r?,\n}","preventionTips":["Always pass byte lengths (buf.len()), never element or char counts","Use the allocator's returned pointer immediately; never cache pointers across allocations","Assert ptr + len <= memory size in the guest before every host_call","Pin the guest ABI version and rebuild guests whenever the host runtime changes","Enable debug logging on the sandbox during development to surface pointer/length values"],"tags":["wasm","wasmtime","memory-bounds","abi"],"backgroundTag":"wasm-guest-memory-out-of-bounds","analyzedSha":"acf2587e46be174c10200489c9a2d23a39a98aeb","analyzedAt":"2026-09-02T22:42:28.464Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}