Sandbox Model
Threat model
Image decoders are historically among the most exploited code paths in software. A malformed JPEG or PNG can trigger heap overflows, integer overflows, or out-of-bounds reads — all reachable before a single pixel is rendered. termimage's sandbox constrains what damage a successful exploit can do.
How it works
When Sandboxed: true:
- The parent resolves the source (file /
data:URI /http(s)://URL). Remote fetches and base64 decode happen here, outside the sandbox. - The parent spawns
os.Executable()as a subprocess withTERMIMAGE_WORKER=1andTERMIMAGE_WORKER_MODE=path|stdinin the environment. For path mode it also setsTERMIMAGE_WORKER_PATH=<path>; for stdin mode it pipes the bytes to the child's stdin. - The child calls
MaybeRunWorker(), which detects the env var and takes over. - Before touching any input, the child applies OS restrictions.
- The child decodes and writes raw RGBA pixels (
width[4] + height[4] + pixels) to stdout. - The parent reads the pixel data over the pipe and renders it.
The sandboxed child re-execs your binary, not a dedicated helper. If your
binary performs side effects during init() (network calls, file writes,
etc.), those will run in the child before MaybeRunWorker() intercepts.
Keep init() clean or move side effects behind a flag check.
Landlock (Linux ≥5.13)
In path mode (local file), Landlock restricts filesystem access to the target file only, read-only:
landlock.V3.BestEffort().RestrictPaths(landlock.ROFiles(path))
In stdin mode (remote URL / data URI), no file access is needed — the worker reads bytes from the pipe — so Landlock is configured with no granted paths, denying all filesystem access:
landlock.V3.BestEffort().RestrictPaths() // empty = deny-all
BestEffort() silently degrades on older kernels — the binary still runs, just
without Landlock protection.
Network access for remote URLs happens in the parent, before the sandboxed child is spawned. The child itself never gets to make network calls — the bytes are already in memory by the time it starts.
Seccomp (upcoming)
A syscall allowlist via BPF is in progress. The allowlist must accommodate the Go runtime scheduler (futex, mmap, brk, etc.) plus stb_image's memory usage pattern. Until it ships, only filesystem access is restricted.
Even without seccomp, Landlock alone eliminates file-system exfiltration —
the most common post-exploit primitive. A compromised decoder cannot read
/etc/passwd, SSH keys, or any file other than the target image.
Wire protocol
The parent–child pipe carries a minimal binary protocol:
offset size field
0 4 width (little-endian uint32)
4 4 height (little-endian uint32)
8 w*h*4 RGBA pixels (4 bytes per pixel, row-major)