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.
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/.