Skip to content

Slice.evalSandbox from guest code: memory cap ignored for small values and replaced by a wrong ceiling for large ones #42

Closed

Description

@siktec-lab

The memory option of Slice.evalSandbox is not applied correctly when the sandbox is created from guest code. Small caps are ignored entirely, and larger caps are replaced by a different, smaller ceiling. The same caps behave correctly through slice mcp (slice_eval_ts), so the problem looks specific to the guest-side Slice.evalSandbox path.

Related to #11, which is closed: the cap is now enforced through slice mcp, but not here.

Repro (mem.js)

for (const memory of [65536, 1048576, 16777216]) {
  const r = Slice.evalSandbox('const a = new Array(1000000).fill(7); a.length', { memory });
  console.log(memory + ": ok=" + r.ok + " used=" + r.memoryUsedBytes + " violation=" + r.violation);
}
const t = Slice.evalSandbox('1+1', { memory: 65536 });
console.log("tiny 64KB: ok=" + t.ok + " violation=" + t.violation);
slice run mem.js

Expected

  • 65536 and 1048576: ok=false with a memory ceiling violation (the array needs about 8 MB).
  • 16777216: ok=true (8 MB fits in 16 MB).
  • tiny 64KB: ok=true.

Actual

65536: ok=true used=8004096 violation=null
1048576: ok=true used=8004096 violation=null
16777216: ok=false used=4096 violation=Resource Cage Exhausted: Compartment 'tenant-cli:sub' exceeded memory ceiling (8000000 > 768938 bytes)
tiny 64KB: ok=true violation=null

So the result is inverted: the two small caps let 8 MB through, and the 16 MB cap rejects it against a ceiling of 768,938 bytes, a value that matches none of the inputs.

Comparison through slice mcp (correct):

{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","clientInfo":{"name":"repro","version":"1"}}}
{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"slice_eval_ts","arguments":{"code":"const a = new Array(1000000).fill(7); a.length","memory":65536}}}

returns ok: false with exceeded memory ceiling (8000000 > 61440 bytes), and the same call with "memory":16777216 returns 1000000.

Impact: code running in a sandbox can start nested sandboxes that are not memory-bounded, so the per-sandbox memory ceiling can be bypassed by untrusted code.

Possible cause: the child limit may be computed from the parent's remaining memory budget rather than as min(requested, parent remaining), or the requested value may be dropped when it is below some threshold.

Environment

  • Version: v0.10.6, commit c6b3e77
  • OS: Windows 11 x64, release build (cargo build --release)

Activity

  1. siktec-lab commented on Nov 10, 2026

    @siktec-lab
    CollaboratorAuthor

    Fixed in #46 (2c3e16a):

    1. Added memory and maxMemoryBytes serde aliases to SandboxEvalOptions::memory_bytes (previously only memoryBytes was recognized on the guest Slice.evalSandbox options object, causing { memory: ... } to fall back to the 16 MB default).
    2. Stopped permanently deducting freed child JS heap allocations from the parent realm's BOA_KV_MEM_BUDGET after the child Slice.evalSandbox isolate exits, while preserving parent_rem_mem clamping during child execution.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions