Revive & harden the V8-imports WASIX lane (ECO-414 + bridge fixes) - #42
Merged
Arshia001 merged 7 commits intoAug 7, 2026
Merged
Conversation
This was referenced Jul 24, 2026
Arshia001
force-pushed
the
napi-v8-imports-bridge-fixes
branch
from
July 28, 2026 14:11
f7c54ec to
3ebc068
Compare
The value-limit/budget work pulls dependency versions that need rustc 1.94; the wasmer workspace already pins it, but standalone builds (cargo-standalone.sh, used by the edgejs harness) picked up whatever rustup default was active. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The guest header passes unofficial_napi_js_source by pointer (one wasm param), but the four Rust import handlers expected text/bytecode split into two params, so any edgejs.wasm built since the struct API landed failed instantiation with an import-type mismatch. Read the struct from guest memory instead. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The native edgejs build links unofficial_napi.cc without the Rust host, so the extern budget-hook symbols were unresolved. The hook pointer is never installed in that configuration, so weak no-op definitions are never called; napi_wasmer's strong Rust exports override them. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The guest-side import handler discards finalize_cb (a wasm function pointer the host cannot invoke) but still forwarded ref_out to the host napi_wrap, which per N-API semantics rejects a ref request without a finalizer (napi_invalid_arg). The object was silently never wrapped, so every subsequent method's napi_unwrap failed and edgejs bindings threw "TypeError: Illegal invocation" - first hit during bootstrap by the signal wrap created for process.stdout, breaking every V8-imports guest since the check landed (2026-05-14). Wrap without a ref instead, and mint the caller's ref separately as a weak (refcount-0) reference, matching what napi_wrap would return. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…h (ECO-414, ECO-416) The wasmer CLI (and any embedder) may call exit() as soon as the guest's exit code is known, which runs C++ static destructors on the main thread while WASIX env teardown (NapiEnv drop -> snapi_bridge_unofficial_release_env -> Isolate::Delete) is still running on a tokio thread. The SharedRuntime static destructor destroyed EdgeV8Platform (and with it the fallback platform's PageAllocator and TracingController), turning the concurrent isolate teardown into a use-after-free. Observed as: - SIGSEGV in BoundedPageAllocator::FreePages during PagePool::ReleaseOnTearDown (the cage allocator's underlying platform PageAllocator was sitting on a glibc tcache freelist), and - SIGSEGV in TraceEventHelper::GetTracingController during Heap::StartTearDown (freed platform vtable), and - the ECO-414 "double free or corruption (fasttop)" abort in the static destructor chain itself -- all the same race from different sides. Fix: make the runtime singleton and every isolate/env registry map a heap allocation that is intentionally never freed, so exit-time destructors can no longer pull the platform out from under a live teardown. The OS reclaims everything at _exit; this matches the standard V8 embedder rule of avoiding exit-time destructors. Verified: test-http2-respond-file-fd host segfault 4/6 runs before -> 0/10 after (wasmer CLI --experimental-napi lane); ECO-414 conformance repro (napi_wasmer array_test x40) 0 crashes, previously ~1 in 5-30. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…t-copy coherence (ECO-416)
Two root causes behind the wasix-lane data-integrity cluster (http2
respondWithFD serving garbage, string_decoder losing buffered state,
zlib/manifest truncation symptoms):
1. napi_create_external_buffer parameter order. The C ABI is
(env, length, data, ...) — unlike napi_create_external_arraybuffer,
which is (env, data, length, ...). The Rust import read (data, length),
so every guest external-buffer creation built a host Buffer whose data
pointer was the LENGTH value (an address in the wasm data segment — the
embedded builtin catalog, hence deterministic lexer.js bytes on the
wire) and whose length was the guest POINTER (~100MB). This corrupted
every consumer of the StreamPipe fd path.
2. Host-allocated buffer copy coherence. Guest access to host-allocated
buffers goes through per-frame guest copies with write-back, and three
defects could lose or resurrect data:
- flush wrote back EVERY copy, so a stale unmodified copy flushed late
clobbered newer host-side writes (write-backs now compare against
the bytes captured at copy creation and skip unmodified copies);
- cache-hit reuse of an existing copy never registered a flush
obligation, so mutations through a reused copy were never written
back (hits now adopt the copy into the current frame, refreshing it
from live host bytes on first per-frame use);
- host->guest callback returns did not flush, deferring write-back to
the outermost import unwind — far too late for JS that reads the
buffer right after the callback (e.g. StringDecoder's state checks);
the callback trampoline now flushes copies created during the call;
- the mid-execution coherency flush dropped all copy entries while
active frames still held pointers into them, orphaning later
mutations; it now writes back and re-bases entries instead.
Also removes the long-lived backing-store-token registrations for
guest-memory-backed externals (exact base-relative translation resolves
them statelessly) — raw-pointer tokens recycle after GC and permanent
entries could alias unrelated buffers.
Verified on the edgejs V8-imports wasix lane: test-http2-respond-file{,-fd,
-range,-fd-range}, test-string-decoder{,-end} now pass (previously
garbage/truncation + NGHTTP2_PROTOCOL_ERROR); js-native-api conformance
34/34 clean.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ABI fix (ECO-416) The copy-coherence changes from the previous commit (pristine-compare write-back skip, cache-hit adoption, per-callback flush) fixed the string_decoder state-loss bug and unlocked ~35 skipped wasix tests, but regressed the async zlib pipeline: draining copy entries at callback boundaries orphans output buffers that zlib worker threads fill after the creating callback returns (test-zlib-from-string/from-gzip/dictionary/ flush and friends failed, some with segfaults, none of them previously skipped). Iterations on the flush policy (rebase instead of drain, modified-only drain) traded one cluster for the other, so the coherence work is reverted to the original semantics until it can be redesigned around explicit copy lifetimes (tracked in ECO-416; the write-up on the issue has the full analysis). What stays: - the napi_create_external_buffer parameter-order fix — the C ABI is (env, length, data, ...), unlike napi_create_external_arraybuffer's (env, data, length, ...); the import read (data, length), corrupting every StreamPipe payload into catalog bytes with a bogus ~100MB length. Verified: zlib suite back to baseline (from-string/from-gzip/dictionary/ flush/brotli-from-brotli/random-byte-pipes all pass); the fd/sendfile respond-file tests that don't depend on the string_decoder bug now pass (respond-file-range, respond-file-fd-range, respond-file-compat, respond-file-with-pipe, generic-streams-sendfile). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Arshia001
force-pushed
the
napi-v8-imports-bridge-fixes
branch
from
July 28, 2026 14:52
3ebc068 to
ed95533
Compare
syrusakbary
approved these changes
Aug 5, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stack 3/4 (napi). Base:
napi-scope-bound-lifetimes. → next:napi-value-scope-rework.The bridge fixes that made the V8-imports (imports-provider) WASIX lane boot and run again, plus the ECO-414 crash fix: