Skip to content

[go-fan] Go Module Review: wazero #5666

@github-actions

Description

@github-actions

🐹 Go Fan Report: tetratelabs/wazero

Module Overview

wazero is a zero-dependency, pure-Go WebAssembly runtime. It executes WASM modules in a secure sandbox without CGO or external C libraries. In this project it powers the WasmGuard system — the runtime enforcement layer for DIFC (Decentralized Information Flow Control) security policies.

Current version: v1.11.0

Current Usage in gh-aw-mcpg

  • Files: 4 files (wasm.go, wasm_parse.go, wasm_test.go, cmd/proxy.go)
  • Import Count: 4 sub-packages (wazero, wazero/api, wazero/imports/wasi_snapshot_preview1, wazero/sys)
  • Key APIs Used:
    • NewRuntimeConfigCompiler() + WithCloseOnContextDone(true) — JIT with context cleanup
    • NewCompilationCache() / NewCompilationCacheWithDir() — in-memory and disk-backed JIT caching
    • NewHostModuleBuilder("env") — host function injection (call_backend, host_log)
    • wasi_snapshot_preview1.Instantiate — WASI support for TinyGo-compiled guards
    • NewModuleConfig().WithStartFunctions().WithStdin(...) — module isolation
    • api.Memory.Read/Write/Grow/ReadUint32Le — linear memory bridge
    • sys.ExitError — distinguishing WASI process exits from traps

Research Findings

The project uses wazero at a sophisticated level: dual memory management strategies (guard allocator vs. raw memory fallback), adaptive output buffers with guard-signalled size hints (return code -2), trap poisoning for corrupted module detection, and context-based cleanup. Overall this is excellent usage of wazero's capabilities.

Recent Updates

  • v1.11.0: JIT compiler performance improvements; WASI preview2 work in progress
  • Disk-backed compilation cache API has been stable since v1.0
  • context.WithoutCancel pattern for dealloc cleanup (already used correctly here)

Best Practices Already Applied ✅

  • WithCloseOnContextDone(true) for server use
  • Host modules named "env" per WASM convention
  • Compilation cache shared process-wide
  • WithStartFunctions() to suppress auto-start
  • Stdin isolated with WithStdin(strings.NewReader(""))

Improvement Opportunities

🏃 Quick Wins

1. Race condition in ConfigureGlobalCompilationCache (wasm.go ~lines 42–71)

The current code reads oldCache under the lock, releases it, then re-acquires to conditionally set the new value. Two concurrent callers could race on the replacement:

// Current (racy):
globalCompilationCacheMu.Lock()
oldCache := globalCompilationCache
globalCompilationCacheMu.Unlock()   // lock released here
// another goroutine could set globalCompilationCache here...
if oldCache == nil {
    globalCompilationCacheMu.Lock()
    globalCompilationCache = cache   // ...causing a lost update
    globalCompilationCacheMu.Unlock()
}

// Suggested fix: set atomically before releasing
globalCompilationCacheMu.Lock()
oldCache := globalCompilationCache
globalCompilationCache = cache
globalCompilationCacheMu.Unlock()
// Close oldCache outside the lock
if oldCache != nil {
    if err := oldCache.Close(ctx); err != nil { ... }
}

Note: the function is documented as "called during process startup" which reduces practical risk, but the locking should still be correct.

2. isWasmTrap string matching (wasm_parse.go line 87)

strings.Contains(err.Error(), "wasm error:") relies on wazero's internal error string format, which is not a stable API contract. Add a version annotation:

// Matches wazero's internal error prefix as of v1.x; re-verify on wazero upgrades.
return strings.Contains(err.Error(), "wasm error:")

3. interface{}any (wasm.go multiple locations)

The WASM bridge uses interface{} in several function signatures. The project targets Go 1.25 where any is the idiomatic alias. Simple find-and-replace within the guard package.

✨ Feature Opportunities

1. WASI Preview2 / Component Model
wazero is building preview2 support. Future guard SDKs could use the component model for a structured, type-safe ABI instead of the current manual JSON-over-linear-memory bridge. Worth tracking.

2. Interpreter mode in tests
Test WASM modules in wasm_test.go use the default compiler config. wazero.NewRuntimeConfigInterpreter() in tests would avoid JIT overhead and improve portability across CPU architectures.

3. Typed memory helpers
wazero's api.Memory has ReadUint32Le (already used at line 223). Audit for any remaining manual 4-byte raw reads that could use this typed helper.

📐 Best Practice Alignment

WithName inline closure (wasm.go line 193–198)

WithName(func() string {
    if name == "" { return "guard" }
    return name
}())

Minor: simplify with a one-liner or helper for readability.

Test WAT source comments
minimalGuardWasm in wasm_test.go is raw hex with no WAT source comment. blockingGuardWasm correctly documents its WAT source inline. Add the same for minimalGuardWasm.

🔧 General Improvements

Shutdown sequencing documentation: CloseGlobalCompilationCache requires all WasmGuard runtimes to be closed first. Consider adding a cross-reference to where in the graceful shutdown path Registry.Close() is called, so the ordering constraint is visible from both sides.

Recommendations (Prioritized)

  1. High: Fix the race in ConfigureGlobalCompilationCache — hold the lock across read-and-set
  2. Medium: Add wazero version annotation to isWasmTrap's string-matching fallback
  3. Low: Modernize interface{}any in guard package
  4. Low: Add WAT source comment to minimalGuardWasm in tests
  5. Watch: Track wazero WASI preview2 progress for future guard ABI improvements

Next Steps

  • Review and fix ConfigureGlobalCompilationCache race condition
  • Add wazero version annotation to isWasmTrap string check
  • Modernize interface{}any in guard package
  • Document shutdown sequencing between Registry.Close() and CloseGlobalCompilationCache

Generated by Go Fan 🐹

References:

Note

🔒 Integrity filter blocked 9 items

The following items were blocked because they don't meet the GitHub integrity level.

To allow these resources, lower min-integrity in your GitHub frontmatter:

tools:
  github:
    min-integrity: approved  # merged | approved | unapproved | none

Generated by Go Fan · ● 875.4K ·

  • expires on May 21, 2026, 8:04 AM UTC

Metadata

Metadata

Assignees

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions