Skip to content

WASM Secure Sandbox - Implementation Guide

WASM Secure Sandbox - Implementation Guide

Target Audience: Coder Agents (Day 4-5 Implementation) Priority: HIGH - Critical Security Fixes Time Estimate: 16 hours (2 days)


Implementation Overview

What Already Exists

The security infrastructure is already partially in place:

/heliosdb-wasm/src/resource_limits.rs
- WasmResourceLimits struct
- ResourceLimiter implementation
- Secure engine creation
- Comprehensive tests
/heliosdb-wasm/src/pointer_validation.rs
- Pointer validation functions
- EscapeDetector
- Safe slice creation
- Validation tests
/heliosdb-wasm/src/allocation_tracker.rs
- AllocationTracker struct
- Double-free prevention
- Memory leak detection
- Tracking tests
/heliosdb-procedures/src/wasm_sandbox.rs
- SecurityProfile system
- Capability model
- SandboxContext
- Resource tracking
/heliosdb-procedures/src/wasm_runtime.rs
- WasmRuntime implementation
- Module compilation
- Instance management
- Execution with limits

What Needs to Be Done 🚧

Critical Fixes (from security audit):

  1. Fix 15 Critical Pointer Operations (8 hours)

    • heliosdb-wasm-sdk/src/host.rs (15 operations)
    • heliosdb-wasm-sdk/src/loader.rs (7 operations)
    • heliosdb-wasm-sdk/src/edge.rs (8 operations)
    • heliosdb-wasm/src/module.rs (1 operation)
  2. Implement Safe Memory Bridge (4 hours)

    • Create SafeWasmMemory trait
    • Implement safe host function wrappers
    • Replace unsafe operations with safe API
  3. Integration & Testing (4 hours)

    • Integration with existing runtime
    • Security test suite
    • Performance benchmarks

Phase 1: Critical Pointer Operation Fixes

File 1: heliosdb-wasm-sdk/src/host.rs

Current Status: 15 unsafe pointer operations Location: /home/claude/HeliosDB/heliosdb-wasm-sdk/src/host.rs

Fix 1: Database Query Function (CRITICAL-01)

Current Code (UNSAFE):

#[no_mangle]
pub extern "C" fn heliosdb_db_query(
query_ptr: *const u8,
query_len: usize,
out_ptr: *mut *mut u8,
out_len: *mut usize,
) -> i32 {
unsafe {
// Line 111: CRITICAL - No bounds validation
let query = std::slice::from_raw_parts(query_ptr, query_len);
let query_str = std::str::from_utf8_unchecked(query);
// Execute query...
let result = execute_query(query_str);
// Line 159: CRITICAL - Direct pointer write
*out_len = result.len();
}
}

Fixed Code (SAFE):

use crate::validation::{validate_and_create_slice, validate_and_read};
use crate::allocation::{ALLOCATION_TRACKER};
#[no_mangle]
pub extern "C" fn heliosdb_db_query(
query_ptr: *const u8,
query_len: usize,
out_ptr: *mut *mut u8,
out_len: *mut usize,
) -> i32 {
// Get WASM memory bounds from context
let (memory_base, memory_size) = match get_wasm_memory_bounds() {
Ok(bounds) => bounds,
Err(_) => return -1, // Error: Cannot get memory bounds
};
// SAFE: Full validation before creating slice
let query = unsafe {
match validate_and_create_slice(
query_ptr,
query_len,
memory_base,
memory_size,
) {
Ok(slice) => slice,
Err(e) => {
tracing::error!("Query pointer validation failed: {}", e);
return -2; // Error: Invalid pointer
}
}
};
// SAFE: UTF-8 validation
let query_str = match std::str::from_utf8(query) {
Ok(s) => s,
Err(_) => {
tracing::error!("Query is not valid UTF-8");
return -3; // Error: Invalid UTF-8
}
};
// Validate query length
if query_str.len() > MAX_QUERY_LENGTH {
tracing::error!("Query too long: {} > {}", query_str.len(), MAX_QUERY_LENGTH);
return -4; // Error: Query too long
}
// Execute query with timeout
let result = match execute_query(query_str) {
Ok(data) => data,
Err(e) => {
tracing::error!("Query execution failed: {}", e);
return -5; // Error: Query failed
}
};
// SAFE: Validate output pointers before writing
unsafe {
// Validate out_ptr is not null
if out_ptr.is_null() {
tracing::error!("Output pointer is null");
return -6;
}
// Validate out_len is not null
if out_len.is_null() {
tracing::error!("Output length pointer is null");
return -7;
}
// Allocate result buffer (tracked)
let result_size = result.len();
let result_ptr = match heliosdb_alloc(result_size) {
ptr if !ptr.is_null() => ptr,
_ => return -8, // Error: Allocation failed
};
// Copy result data
std::ptr::copy_nonoverlapping(
result.as_ptr(),
result_ptr,
result_size,
);
// Write output pointers (validated above)
*out_ptr = result_ptr;
*out_len = result_size;
}
0 // Success
}

Fix 2: Allocation Function (CRITICAL-03)

Current Code (UNSAFE - Memory Leak):

#[no_mangle]
pub extern "C" fn heliosdb_alloc(size: usize) -> *mut u8 {
let mut buf = Vec::with_capacity(size);
let ptr = buf.as_mut_ptr();
std::mem::forget(buf); // LEAK!
ptr
}

Fixed Code (SAFE - Tracked):

use crate::allocation::ALLOCATION_TRACKER;
#[no_mangle]
pub extern "C" fn heliosdb_alloc(size: usize) -> *mut u8 {
// Enforce maximum allocation size
if size > MAX_WASM_ALLOCATION {
tracing::error!(
"Allocation too large: {} > {}",
size,
MAX_WASM_ALLOCATION
);
return std::ptr::null_mut();
}
// Allocate buffer
let mut buf = Vec::with_capacity(size);
buf.resize(size, 0); // Zero-initialize for security
let ptr = buf.as_mut_ptr();
// Calculate WASM offset
let (memory_base, _) = match get_wasm_memory_bounds() {
Ok(bounds) => bounds,
Err(_) => return std::ptr::null_mut(),
};
let offset = (ptr as usize - memory_base) as u32;
// Track allocation (prevents leaks and double-frees)
if let Err(e) = ALLOCATION_TRACKER.register(offset, size) {
tracing::error!("Failed to track allocation: {}", e);
// Don't leak - drop the buffer
drop(buf);
return std::ptr::null_mut();
}
// Intentionally forget (ownership transferred to WASM)
std::mem::forget(buf);
tracing::debug!("Allocated {} bytes at offset 0x{:x}", size, offset);
ptr
}

Fix 3: Deallocation Function (CRITICAL-04)

Current Code (UNSAFE - Double-Free Risk):

#[no_mangle]
pub extern "C" fn heliosdb_dealloc(ptr: *mut u8, size: usize) {
unsafe {
let _ = Vec::from_raw_parts(ptr, size, size);
}
}

Fixed Code (SAFE - Tracked):

use crate::allocation::ALLOCATION_TRACKER;
#[no_mangle]
pub extern "C" fn heliosdb_dealloc(ptr: *mut u8, size: usize) {
// Calculate WASM offset
let (memory_base, _) = match get_wasm_memory_bounds() {
Ok(bounds) => bounds,
Err(_) => {
tracing::error!("Cannot get memory bounds for deallocation");
return;
}
};
let offset = (ptr as usize - memory_base) as u32;
// Validate this is a known allocation (prevents double-free)
match ALLOCATION_TRACKER.unregister(offset, size) {
Ok(info) => {
tracing::debug!(
"Deallocated {} bytes at offset 0x{:x} (age: {:?})",
size,
offset,
info.timestamp.elapsed()
);
}
Err(e) => {
tracing::error!(
"Deallocation failed for offset 0x{:x}: {}",
offset,
e
);
// Don't proceed with deallocation - could be double-free
return;
}
}
// Safe to deallocate (validated above)
unsafe {
let _ = Vec::from_raw_parts(ptr, size, size);
}
}

File 2: heliosdb-wasm-sdk/src/loader.rs

Location: /home/claude/HeliosDB/heliosdb-wasm-sdk/src/loader.rs Issues: 7 unsafe pointer operations

Fix 4: Module Load Function (CRITICAL-05)

Current Code:

pub fn load_module(name: &str, json_config: &str) -> Result<()> {
// Line 228: CRITICAL - No validation on pointers
unsafe {
let result = heliosdb_module_load(
name.as_ptr(),
name.len(),
json_config.as_ptr(),
json_config.len(),
);
}
}

Fixed Code:

pub fn load_module(name: &str, json_config: &str) -> Result<()> {
// Validate input lengths
if name.len() > MAX_MODULE_NAME_LENGTH {
return Err(Error::NameTooLong(name.len()));
}
if json_config.len() > MAX_CONFIG_SIZE {
return Err(Error::ConfigTooLarge(json_config.len()));
}
// Ensure strings are valid for the lifetime of the call
let name_bytes = name.as_bytes();
let config_bytes = json_config.as_bytes();
// Validate JSON before passing to C
let _: serde_json::Value = serde_json::from_str(json_config)
.map_err(|e| Error::InvalidJson(e.to_string()))?;
unsafe {
// Pointers are valid because:
// 1. name_bytes and config_bytes live for duration of call
// 2. as_ptr() on &[u8] is always valid
// 3. Lengths are validated above
let result = heliosdb_module_load(
name_bytes.as_ptr(),
name_bytes.len(),
config_bytes.as_ptr(),
config_bytes.len(),
);
if result != 0 {
return Err(Error::ModuleLoadFailed(result));
}
}
tracing::info!("Loaded module: {}", name);
Ok(())
}

File 3: heliosdb-wasm-sdk/src/edge.rs

Location: /home/claude/HeliosDB/heliosdb-wasm-sdk/src/edge.rs Issues: 8 unsafe pointer operations (lines 169, 236, 241-248, 293, 331-342, 368)

Fix 5: Edge Function Invocation (CRITICAL-08 through CRITICAL-15)

Current Code:

pub fn invoke_edge_function(
function_name: &str,
region: Option<&str>,
payload: &[u8],
) -> Result<Vec<u8>> {
let region_ptr = region.map(|r| r.as_ptr()).unwrap_or(std::ptr::null());
let region_len = region.map(|r| r.len()).unwrap_or(0);
let mut out_buf = vec![0u8; 1024 * 1024];
let mut out_len: usize = out_buf.len();
unsafe {
let result = heliosdb_edge_invoke(
function_name.as_ptr(),
function_name.len(),
region_ptr,
region_len,
payload.as_ptr(),
payload.len(),
out_buf.as_mut_ptr(),
&mut out_len as *mut usize,
);
}
}

Fixed Code:

pub fn invoke_edge_function(
function_name: &str,
region: Option<&str>,
payload: &[u8],
) -> Result<Vec<u8>> {
// === Input Validation ===
// Validate function name
if function_name.is_empty() {
return Err(Error::InvalidParameter("Empty function name"));
}
if function_name.len() > MAX_FUNCTION_NAME_LENGTH {
return Err(Error::NameTooLong(function_name.len()));
}
// Validate payload size
if payload.len() > MAX_EDGE_PAYLOAD_SIZE {
return Err(Error::PayloadTooLarge(payload.len()));
}
// Validate region if provided
if let Some(r) = region {
if r.len() > MAX_REGION_NAME_LENGTH {
return Err(Error::InvalidRegion);
}
}
// === Prepare Safe Pointers ===
let function_bytes = function_name.as_bytes();
let region_bytes = region.map(|r| r.as_bytes());
let region_ptr = region_bytes
.as_ref()
.map(|r| r.as_ptr())
.unwrap_or(std::ptr::null());
let region_len = region_bytes
.as_ref()
.map(|r| r.len())
.unwrap_or(0);
// Allocate output buffer (capped at reasonable size)
let max_response_size = std::cmp::min(
1024 * 1024, // 1MB default
MAX_EDGE_RESPONSE_SIZE
);
let mut out_buf = vec![0u8; max_response_size];
let mut out_len: usize = out_buf.len();
// === Execute with Safety Checks ===
let result = unsafe {
// Defensive: validate pointers are not null before call
debug_assert!(!function_bytes.as_ptr().is_null());
debug_assert!(!payload.as_ptr().is_null());
debug_assert!(!out_buf.as_mut_ptr().is_null());
heliosdb_edge_invoke(
function_bytes.as_ptr(),
function_bytes.len(),
region_ptr,
region_len,
payload.as_ptr(),
payload.len(),
out_buf.as_mut_ptr(),
&mut out_len as *mut usize,
)
};
// === Validate Response ===
if result != 0 {
return Err(Error::EdgeInvokeFailed(result));
}
// Validate out_len was not corrupted
if out_len > out_buf.len() {
tracing::error!(
"🚨 Response length corrupted: {} > {}",
out_len,
out_buf.len()
);
return Err(Error::CorruptedResponse);
}
// Truncate to actual response size
out_buf.truncate(out_len);
tracing::debug!(
"Edge function '{}' returned {} bytes",
function_name,
out_len
);
Ok(out_buf)
}

File 4: heliosdb-wasm/src/module.rs

Location: /home/claude/HeliosDB/heliosdb-wasm/src/module.rs Issue: Unsafe module deserialization (CRITICAL-06)

Fix 6: Module Deserialization

Current Code:

// Line 181: CRITICAL - Deserializes untrusted data
pub fn deserialize(engine: &Engine, bytes: &[u8]) -> Result<WasmModule> {
let module = unsafe {
WasmtimeModule::deserialize(engine, bytes)
.map_err(|e| WasmError::InternalError(format!("Failed: {}", e)))?
};
Ok(WasmModule { module, /* ... */ })
}

Fixed Code:

use sha2::{Sha256, Digest};
pub fn deserialize(
engine: &Engine,
bytes: &[u8],
expected_hash: Option<&str>,
) -> Result<WasmModule> {
// Validate module size
if bytes.len() > MAX_MODULE_SIZE {
return Err(WasmError::ModuleTooLarge {
size: bytes.len(),
max: MAX_MODULE_SIZE,
});
}
// Verify module hash if provided (signature verification)
if let Some(expected) = expected_hash {
let mut hasher = Sha256::new();
hasher.update(bytes);
let computed = format!("{:x}", hasher.finalize());
if computed != expected {
tracing::error!(
"🚨 Module hash mismatch! Expected: {}, Got: {}",
expected,
computed
);
return Err(WasmError::UntrustedModule);
}
}
// Validate WASM magic number
if bytes.len() < 4 || &bytes[0..4] != b"\0asm" {
return Err(WasmError::InvalidModule("Missing WASM magic number"));
}
// Deserialize with Wasmtime's built-in validation
// Note: Still unsafe but Wasmtime performs extensive validation internally
let module = unsafe {
WasmtimeModule::deserialize(engine, bytes)
.map_err(|e| {
tracing::error!("Module deserialization failed: {}", e);
WasmError::DeserializationFailed(e.to_string())
})?
};
tracing::info!(
"Deserialized module: {} bytes, hash: {}",
bytes.len(),
expected_hash.unwrap_or("none")
);
Ok(WasmModule {
module,
hash: expected_hash.map(|s| s.to_string()),
// ... other fields
})
}

Phase 2: Safe Memory Bridge Implementation

Step 1: Create Safe Memory Trait

File: /home/claude/HeliosDB/heliosdb-wasm/src/safe_memory.rs

//! Safe Memory Access Bridge for WASM-Host Boundary
use crate::error::{WasmError, Result};
use crate::pointer_validation::{
validate_and_create_slice,
validate_and_create_slice_mut,
validate_and_read,
};
use crate::allocation_tracker::ALLOCATION_TRACKER;
/// Safe memory access interface (no raw pointers exposed)
pub trait SafeWasmMemory {
/// Get WASM linear memory bounds
fn memory_bounds(&self) -> Result<(usize, usize)>;
/// Read bytes from WASM memory with full validation
fn read_bytes(&self, offset: u32, len: u32) -> Result<Vec<u8>> {
let (memory_base, memory_size) = self.memory_bounds()?;
unsafe {
let ptr = (memory_base + offset as usize) as *const u8;
let slice = validate_and_create_slice(
ptr,
len as usize,
memory_base,
memory_size,
)?;
Ok(slice.to_vec())
}
}
/// Write bytes to WASM memory with full validation
fn write_bytes(&mut self, offset: u32, data: &[u8]) -> Result<()> {
let (memory_base, memory_size) = self.memory_bounds()?;
unsafe {
let ptr = (memory_base + offset as usize) as *mut u8;
let slice = validate_and_create_slice_mut(
ptr,
data.len(),
memory_base,
memory_size,
)?;
slice.copy_from_slice(data);
}
Ok(())
}
/// Read UTF-8 string with validation
fn read_string(&self, offset: u32, len: u32) -> Result<String> {
// Maximum string length: 64KB
const MAX_STRING_LENGTH: u32 = 64 * 1024;
if len > MAX_STRING_LENGTH {
return Err(WasmError::StringTooLarge {
len: len as usize,
max: MAX_STRING_LENGTH as usize,
});
}
let bytes = self.read_bytes(offset, len)?;
// UTF-8 validation
String::from_utf8(bytes)
.map_err(|e| WasmError::InvalidUtf8(e.to_string()))
}
/// Write UTF-8 string
fn write_string(&mut self, offset: u32, s: &str) -> Result<()> {
self.write_bytes(offset, s.as_bytes())
}
/// Read typed value with alignment validation
fn read_typed<T: Copy>(&self, offset: u32) -> Result<T> {
let (memory_base, memory_size) = self.memory_bounds()?;
unsafe {
let ptr = (memory_base + offset as usize) as *const T;
validate_and_read(ptr, memory_base, memory_size)
}
}
/// Write typed value
fn write_typed<T: Copy>(&mut self, offset: u32, value: T) -> Result<()> {
let (memory_base, memory_size) = self.memory_bounds()?;
let size = std::mem::size_of::<T>();
unsafe {
let ptr = (memory_base + offset as usize) as *mut T;
// Validate alignment and bounds
crate::pointer_validation::validate_alignment(ptr)?;
crate::pointer_validation::validate_wasm_memory_bounds(
ptr as *const u8,
size,
memory_base,
memory_size,
)?;
std::ptr::write(ptr, value);
}
Ok(())
}
/// Allocate memory in WASM linear memory
fn allocate(&mut self, size: usize) -> Result<u32>;
/// Deallocate memory
fn deallocate(&mut self, offset: u32, size: usize) -> Result<()>;
}

Step 2: Implement for WasmInstance

impl SafeWasmMemory for WasmInstance {
fn memory_bounds(&self) -> Result<(usize, usize)> {
let memory = self.get_memory()
.ok_or(WasmError::MemoryNotFound)?;
let base = memory.data_ptr(&self.store) as usize;
let size = memory.data_size(&self.store);
Ok((base, size))
}
fn allocate(&mut self, size: usize) -> Result<u32> {
// Call WASM allocator function
let alloc_fn = self.get_function("__wasm_allocate")
.ok_or(WasmError::AllocatorNotFound)?;
let result = alloc_fn.call(
&mut self.store,
&[Val::I32(size as i32)],
)?;
match result.get(0) {
Some(Val::I32(ptr)) if *ptr != 0 => Ok(*ptr as u32),
_ => Err(WasmError::AllocationFailed),
}
}
fn deallocate(&mut self, offset: u32, size: usize) -> Result<()> {
// Call WASM deallocator function
let dealloc_fn = self.get_function("__wasm_deallocate")
.ok_or(WasmError::DeallocatorNotFound)?;
dealloc_fn.call(
&mut self.store,
&[Val::I32(offset as i32), Val::I32(size as i32)],
)?;
Ok(())
}
}

Phase 3: Integration & Testing

Security Test Suite

File: /home/claude/HeliosDB/heliosdb-wasm/tests/security_tests.rs

#[cfg(test)]
mod security_tests {
use super::*;
#[test]
fn test_buffer_overflow_prevention() {
let runtime = create_test_runtime();
// Attempt to read beyond memory bounds
let result = runtime.memory_bridge.read_bytes(
0xFFFFFF00, // Near end of 32-bit address space
0x1000, // 4KB - will overflow
);
assert!(matches!(result, Err(WasmError::OutOfBounds { .. })));
}
#[test]
fn test_null_pointer_rejection() {
let validator = PointerValidator::new();
let result = unsafe {
validate_and_create_slice(
std::ptr::null(),
100,
0x1000,
0x10000,
)
};
assert!(matches!(result, Err(WasmError::NullPointer)));
}
#[test]
fn test_allocation_limit_enforcement() {
let tracker = AllocationTracker::new(
1024 * 1024, // 1MB total
512 * 1024, // 512KB single
);
// Attempt to allocate beyond single limit
let result = tracker.register(0x1000, 1024 * 1024);
assert!(result.is_err());
// Allocate within limits
tracker.register(0x1000, 256 * 1024).unwrap();
tracker.register(0x2000, 256 * 1024).unwrap();
// Attempt to exceed total limit
let result = tracker.register(0x3000, 600 * 1024);
assert!(result.is_err());
}
#[test]
fn test_double_free_prevention() {
let tracker = AllocationTracker::new(1024 * 1024, 1024);
tracker.register(0x1000, 256).unwrap();
tracker.unregister(0x1000, 256).unwrap();
// Second free should fail
let result = tracker.unregister(0x1000, 256);
assert!(matches!(result, Err(WasmError::DoubleFree(0x1000))));
}
#[test]
fn test_execution_timeout() {
let runtime = WasmRuntime::new_with_limits(
WasmResourceLimits {
max_execution_time_ms: 100, // 100ms timeout
..Default::default()
}
);
// Load infinite loop module
let module = compile_wat(r#"
(module
(func (export "infinite_loop")
(loop $l (br $l))
)
)
"#);
let result = runtime.execute(&module, "infinite_loop", vec![]);
assert!(matches!(result, Err(WasmError::Timeout(100))));
}
#[test]
fn test_memory_limit_enforcement() {
let runtime = WasmRuntime::new_with_limits(
WasmResourceLimits {
max_memory_pages: 16, // 1MB (16 pages * 64KB)
..Default::default()
}
);
// Attempt to allocate 10MB (exceeds limit)
let module = compile_wat(r#"
(module
(memory (export "memory") 160) ;; 160 pages = 10MB
)
"#);
let result = runtime.load_module(module);
assert!(matches!(result, Err(WasmError::MemoryLimitExceeded { .. })));
}
#[test]
fn test_capability_enforcement() {
let runtime = WasmRuntime::new();
let profile = SecurityProfile::minimal(); // No capabilities
// Attempt file read without capability
let result = runtime.execute_with_profile(
&file_reader_module,
"read_file",
vec!["/etc/passwd".into()],
profile,
);
assert!(matches!(
result,
Err(WasmError::CapabilityDenied { .. })
));
}
}

Integration Tests

File: /home/claude/HeliosDB/heliosdb-wasm/tests/integration_tests.rs

#[tokio::test]
async fn test_query_executor_integration() {
let executor = WasmQueryExecutorIntegration::new();
// Load test module
let module_code = include_bytes!("fixtures/test_procedure.wasm");
executor.load_module("test_procedure", module_code).await.unwrap();
// Execute with database-only profile
let result = executor.execute_stored_procedure(
"test_procedure",
"process_order",
vec![json!({"order_id": 123, "amount": 99.99})],
SecurityProfile::database_only(),
).await.unwrap();
assert_eq!(result.len(), 1);
}
#[tokio::test]
async fn test_event_trigger_integration() {
let trigger_system = WasmEventTriggerIntegration::new();
// Register trigger
let trigger_id = trigger_system.register_trigger(
"validate_insert",
"validator_module",
"validate",
EventType::BeforeInsert,
).await.unwrap();
// Fire event
let event = Event::Insert {
table: "orders".to_string(),
data: json!({"total": -10.0}), // Invalid: negative amount
};
let result = trigger_system.fire_event(event).await;
assert!(result.is_err()); // Should reject negative amount
}

Implementation Checklist

Day 4 (8 hours)

Hour 1-2: Setup & Infrastructure

  • Review existing code in /heliosdb-wasm/src/
  • Verify pointer_validation.rs, allocation_tracker.rs, resource_limits.rs
  • Add any missing utility functions
  • Set up test infrastructure

Hour 3-4: Fix Critical Host Functions

  • Fix heliosdb_db_query (host.rs:111)
  • Fix heliosdb_db_execute (host.rs:159)
  • Fix heliosdb_alloc (host.rs:346)
  • Fix heliosdb_dealloc (host.rs:355)
  • Add tests for each fix

Hour 5-6: Fix Module Loading

  • Fix load_module pointer operations (loader.rs:228)
  • Add module signature verification (module.rs:181)
  • Implement hash validation
  • Add malicious module detection tests

Hour 7-8: Documentation

  • Document all safety invariants
  • Add inline safety comments
  • Create migration guide for unsafe code

Day 5 (8 hours)

Hour 1-3: Fix Edge Functions

  • Fix register_edge_function (edge.rs:169)
  • Fix invoke_edge_function (edge.rs:236-248)
  • Fix async invocation safety (edge.rs:331-342)
  • Fix region list operations (edge.rs:368)
  • Add comprehensive edge function tests

Hour 4-5: Implement Safe Memory Bridge

  • Create SafeWasmMemory trait
  • Implement for WasmInstance
  • Add comprehensive type conversion
  • Test all memory operations

Hour 6-7: Security Test Suite

  • Implement all security tests from checklist
  • Run fuzzing tests (if time permits)
  • Performance benchmarks
  • Verify <5% overhead target

Hour 8: Final Integration

  • Integration testing with query executor
  • Integration testing with event system
  • Integration testing with CDC pipeline
  • Final documentation review
  • Create handoff summary

Success Criteria

Security Criteria

  • All 15 critical pointer operations have validation
  • Zero unsafe operations without safety comments
  • Allocation tracker prevents double-free
  • Resource limits prevent exhaustion
  • Execution timeout prevents infinite loops
  • Security test suite 100% pass rate
  • No new security warnings from audit tools

Performance Criteria

  • Overhead < 5% for typical workload
  • Pointer validation < 100μs per call
  • Allocation tracking < 50μs per allocation
  • Memory usage acceptable (< 10% increase)

Integration Criteria

  • Query executor integration works
  • Event trigger integration works
  • CDC pipeline integration works
  • Existing tests still pass
  • New security tests pass

Troubleshooting

Common Issues

Issue: “Cannot get memory bounds”

  • Cause: WASM instance not fully initialized
  • Fix: Ensure memory is exported from module
  • Prevention: Add validation in module loader

Issue: “Validation overhead too high”

  • Cause: Validating on every access
  • Fix: Cache validation results for sequential accesses
  • Prevention: Use bulk operations where possible

Issue: “Allocation tracker out of sync”

  • Cause: Missed deallocation or double-free
  • Fix: Review allocation/deallocation call pairs
  • Prevention: Use RAII patterns, ensure cleanup

Support & Resources

Code References

  • Security audit: /docs/WASM_SECURITY_AUDIT.md
  • Architecture: /docs/architecture/WASM_SECURE_SANDBOX_DESIGN.md
  • Existing validation: /heliosdb-wasm/src/pointer_validation.rs
  • Resource limits: /heliosdb-wasm/src/resource_limits.rs

Testing

  • Run security tests: cargo test -p heliosdb-wasm security
  • Run integration tests: cargo test -p heliosdb-wasm integration
  • Run all tests: cargo test

Getting Help

  • Review architecture document for design decisions
  • Check existing implementations for patterns
  • Reference Wasmtime docs: https://docs.wasmtime.dev/

Ready to implement! Follow this guide step-by-step for a secure, production-ready WASM sandbox.

Last Updated: 2025-11-10 Version: 1.0