Workspace IndexAlgorithms › Day 28

The WASM Execution Model and Sandboxing Boundary TODO

Algorithms · Day 28 / 100 · B. Compilers, Runtimes & VMs (Day 20-35)

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}")

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


한국어

WASM 실행 모델과 샌드박싱 경계 TODO

Algorithms · Day 28 / 100 · B. 컴파일러·런타임·VM (Day 20–35)

개념

WebAssembly는 스택 기반 가상 머신의 명령 집합으로, 제어 흐름이 임의 점프가 아니라 블록·루프·분기라는 구조적 형태로만 표현되어 검증기가 정적으로 타입과 흐름을 확인할 수 있다. 메모리는 선형 메모리라 불리는 하나의 연속된 바이트 배열이고 모든 접근이 경계 검사를 거치므로, 모듈은 자기 메모리 밖을 절대 읽거나 쓸 수 없다. 호출 스택과 함수 주소는 엔진이 관리하며 간접 호출은 타입이 검사된 테이블 인덱스로만 가능해서, 전통적인 스택 스매싱이나 임의 코드 주소로의 점프가 원천적으로 막힌다. 모듈은 명시적으로 import한 호스트 함수 외에는 파일, 네트워크, 시계 등 바깥세상에 닿을 수 없으므로 샌드박싱 경계는 곧 import 목록이 된다. 다만 경계 안의 메모리 손상은 여전히 가능하고, 실행 시간이나 부동소수 NaN 비트 패턴 같은 일부 요소는 완전한 결정성을 보장하지 않는다.

신뢰할 수 없는 코드를 안전하게 실행해야 하는 플러그인 시스템, 엣지 런타임, 대체 스마트 컨트랙트 VM이 모두 이 모델 위에 서 있어서, 경계가 어디까지인지 정확히 알아야 위협 모델을 제대로 그릴 수 있다.

코드 · 수식

# 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}")

연습

간단한 함수를 WAT로 직접 작성하거나 컴파일해 텍스트 포맷을 읽어 보고, 선형 메모리 범위를 벗어나는 로드를 시도했을 때 런타임이 어떤 트랩을 내는지 관찰하라.

실무 · Verex 연결

이더리움 생태계의 대체 실행 환경 논의와 여러 비EVM 체인이 WASM 기반이므로, EVM의 결정성·가스 미터링 요구사항과 WASM의 샌드박스 모델이 어디서 겹치고 어디서 어긋나는지 비교해 두면 실행 계층 설계 감각이 는다.

공부한 날 원본 커리큘럼(docs/knowledge/dev-100-curriculum.md)의 이 줄에 노트 링크와 ✅ 를 붙이면, 이 자리는 노트 본문으로 바로 이어집니다. 노트 없이 이 페이지에 바로 적어도 됩니다 — 다만 다시 생성하면 덮어쓰이므로, 남길 글은 docs/algorithms/ 의 마크다운으로 쓰는 편이 안전합니다.

← 27. EVM 인터프리터 내부29. GC 심화 →