The WASM Execution Model and Sandboxing Boundary TODO
Concept
WebAssembly is an instruction set for a stack-based virtual machine, where control flow is expressed only through structured forms — blocks, loops, and branches — rather than arbitrary jumps, letting the validator statically confirm types and flow. Memory is one contiguous byte array called linear memory, and every access goes through a bounds check, so a module can never read or write outside its own memory. The call stack and function addresses are managed by the engine, and indirect calls are only possible through a type-checked table index, which rules out classic stack smashing or jumping to an arbitrary code address at the root. A module can't reach the outside world — files, the network, the clock — except through host functions it explicitly imports, so the sandbox boundary is essentially its import list. That said, memory corruption within the boundary is still possible, and some aspects, like execution timing or floating-point NaN bit patterns, aren't fully deterministic.
Plugin systems, edge runtimes, and alternative smart-contract VMs that need to run untrusted code safely all rest on this model, so knowing exactly where the boundary sits is necessary to draw an accurate threat model.
Code & Formula
# WASM 실행 모델과 샌드박싱 경계 — 선형 메모리에 경계 검사를 강제하고, 벗어나면 트랩을 내며,
# import 목록에 없는 호스트 함수는 절대 호출할 수 없게 한다.
class Trap(Exception):
pass
class WasmModule:
def __init__(self, memory_pages=1, page_size=65536, imports=None):
self.memory = bytearray(memory_pages * page_size)
self.imports = imports or {} # 명시적으로 허용된 호스트 함수만 호출 가능
def load(self, addr, size=4):
if addr < 0 or addr + size > len(self.memory):
raise Trap(f"out-of-bounds load @ {addr} (memory size={len(self.memory)})")
return int.from_bytes(self.memory[addr:addr + size], "little")
def store(self, addr, value, size=4):
if addr < 0 or addr + size > len(self.memory):
raise Trap(f"out-of-bounds store @ {addr} (memory size={len(self.memory)})")
self.memory[addr:addr + size] = int(value).to_bytes(size, "little")
def call_import(self, name, *args):
if name not in self.imports: # import 목록 밖은 바깥세상에 절대 닿지 못함
raise Trap(f"unauthorized host call: {name}")
return self.imports[name](*args)
mod = WasmModule(memory_pages=1, imports={"log": lambda x: f"host-logged({x})"})
mod.store(0, 42)
print("정상 store/load:", mod.load(0))
print("허용된 import 호출:", mod.call_import("log", 42))
for addr in (len(mod.memory) - 2, -1):
try:
mod.load(addr, size=4)
print(f"addr={addr}: 트랩 없이 통과 (버그)")
except Trap as e:
print(f"addr={addr}: 트랩 발생 -> {e}")
try:
mod.call_import("read_file", "/etc/passwd")
except Trap as e:
print(f"미허용 import 호출 차단: {e}")
docs/code/algorithms/algorithms-28.py
Exercise
Hand-write or compile a simple function into WAT, read through the text format, and observe what trap the runtime raises when you attempt a load outside the linear memory's range.
Practical Connection
Since discussions of alternative execution environments in the Ethereum ecosystem and several non-EVM chains are WASM-based, it's worth comparing where the EVM's determinism and gas-metering requirements overlap with — and diverge from — WASM's sandbox model to sharpen your sense of execution-layer design.
Where it lands in Jayverse
- Dark Horse: if security-hole research or a Base App mini-app ever runs third-party or untrusted code, use "the sandbox boundary is the import list" directly. Audit exactly which host functions are exposed.
- OFA: if solvers are ever allowed to submit executable strategy code rather than just bids, treat that code like WASM-hosted code. Require an explicit, minimal capability list rather than trusting the auction to vet it.
- Auditor: record "the boundary equals the import list" as a standing methodology check. Apply it to any future non-EVM or plugin-style execution surface Jayverse adds.
Key expressions
| Expression | 뜻 · 쓰이는 자리 |
|---|---|
| rest on | ~에 기반하다, ~위에 성립하다 · 여러 시스템이 공통 원리에 의존할 때. "all rest on this model" |
| rule out | ~을 배제하다, 불가능하게 만들다 · 특정 취약점 유형 자체를 원천 차단할 때. "which rules out classic stack smashing" |
| at the root | 근본적으로, 뿌리에서부터 · 가장 기초적인 수준에서 막혀 있을 때. "jumping to an arbitrary code address at the root" |
| sharpen (one's sense of) | 감각이나 이해를 예리하게 다듬다 · 비교를 통해 판단력을 정교하게 할 때. "sharpen your sense of execution-layer design" |
| diverge from | ~와 어긋나다, 갈라지다 · 두 설계가 어느 지점에서 달라지는지 볼 때. "diverge from WASM's sandbox model" |
| raise (a trap) | 예외나 트랩을 발생시키다 · 잘못된 접근 시 런타임이 오류를 일으킬 때. "what trap the runtime raises when you attempt a load" |
| overlap with | ~와 겹치다, 공통되다 · 두 설계의 공통 지점을 확인할 때. "worth comparing where the EVM's determinism and gas-metering" |
| WAT | WebAssembly 텍스트 포맷(WebAssembly Text format) · WASM 바이트코드를 사람이 읽을 수 있게 표현한 형식. "compile a simple function into WAT, read" |
| stack smashing | 스택 스매싱(콜스택을 덮어써 실행 흐름을 탈취하는 고전적 공격 기법) · WASM 구조상 원천적으로 불가능함을 설명할 때. "classic stack smashing or jumping to an arbitrary" |
If you study this on a given day, add a note link and a ✅ to this line in the source curriculum (docs/knowledge/dev-100-curriculum.md) and this spot will lead straight to the note body. You can also write directly on this page — but regenerating overwrites it, so it's safer to keep anything you want to save as markdown under docs/algorithms/.