Skip to content

bug: Extism leaks wazero runtimes — no Close() API, plus compiler leak on validation-error paths #162

Description

@robbyt

Summary

The Extism engine has no reachable way to release a compiled plugin's wazero runtime, and the compiler leaks the runtime on two validation-error paths. Applications that build evaluators dynamically (per-tenant or per-script) accumulate wazero runtimes — mmapped JIT code, WASI FS handles, host modules — with no deterministic reclamation.

1. No public Close/Shutdown API

compiler.Executable.Close (engines/extism/compiler/executable.go:70) closes the wazero runtime, but nothing reachable calls it:

  • evaluator.Evaluator.execUnit is unexported with no getter (engines/extism/evaluator/evaluator.go:22-48).
  • platform/script.ExecutableUnit has no Close.
  • platform.Evaluator (platform/evaluator.go:25) has no Close.

Executable.Close is invoked only from its own unit test. Reclamation therefore relies entirely on wazero's GC finalizers.

Fix: add Close(ctx) error to evaluator.Evaluator (delegating to execUnit.GetContent().(*compiler.Executable).Close) and to ExecutableUnit; consider adding an optional io.Closer-style method to platform.Evaluator so callers can release resources.

2. Compiler leaks the plugin on validation-error paths

engines/extism/compiler/compiler.go:95-115: after c.compileFn succeeds (L85, creating a wazero runtime inside extismSDK.NewCompiledPlugin), the error returns at L97 (test-instance creation failure) and L110 (entry point not found) exit without calling plugin.Close. The success path transfers ownership to Executable, but these failures leave the runtime unreferenced until GC.

Scenario: a service that retries compiling user modules with a wrong entry-point name accrues un-closed runtimes.

Fix: on those error paths, plugin.Close(context.WithoutCancel(ctx)) before returning (e.g. via a named-error defer).

Note

ErrExecutableClosed (executable.go:13) is dead code and GetExtismByteCode doesn't check closed — both become relevant once a Close path exists (item 1).

Impact

Medium to high for long-running services that create Extism evaluators dynamically; low for a process that builds a fixed set once at startup.

Fix

Expose Close on the evaluator and executable unit; close the plugin on the compiler's two validation-error paths. Add tests asserting Close releases the runtime and that a failed compile doesn't leak (e.g. via a spy on the plugin's Close).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions