Repository navigation
Precompiled WASM 2.5.0 fails in stable browsers due to compact import encoding #252
Description
Precompiled WASM 2.5.0 fails in stable browsers due to compact import encoding
Description
The precompiled WASM bundle from:
sqlite3mc-2.5.0-sqlite-3.53.4-wasm.zip
cannot be loaded by stable Chromium or Firefox because its sqlite3.wasm
uses the experimental WebAssembly compact-import encoding.
Reproduction
Extract sqlite3.wasm from the release archive and run:
const response = await fetch("./sqlite3.wasm");
const bytes = await response.arrayBuffer();
await WebAssembly.compile(bytes);Actual result
Chromium reports:
CompileError: WebAssembly.compile(): unknown import kind 0x7f
Firefox reports an equivalent error:
CompileError: unsupported import kind
The encoding is not enabled by default in current stable Chromium or Firefox.
Expected result
The precompiled WASM bundle should use the standard WebAssembly import-section
encoding and load without experimental browser flags.
Investigation
The generated sqlite3.mjs identifies the build toolchain as:
Emscripten SDK: 6.0.5
This appears to be caused by a short-lived Emscripten/Binaryen toolchain
regression:
- Binaryen 132 started opportunistically emitting compact imports:
Opportunisticly emit compact imports WebAssembly/binaryen#8926 - Emscripten subsequently stopped invoking
wasm-aswith all features because
enabling compact imports caused them to be emitted in almost all binaries:
Avoid running wasm-as with--all-featuresemscripten-core/emscripten#27431
SQLite3MC's WASM release workflow currently installs version: latest through
setup-emsdk, so the release appears to have picked up the affected toolchain
combination:
https://github.com/utelle/SQLite3MultipleCiphers/blob/main/.github/workflows/build-reusable.yml
Other projects have encountered the same unknown import kind 0x7f error with
Binaryen 132:
- 0.9.15 V8 CompileError: unknown import kind 0x7f ongjin/garu#7
- ci: pin binaryen — 132 emits WASM that no engine can load (the live playground is broken) Cantara/kcp-agent#151
Verification
As a local workaround, I expanded the compact import groups into semantically
equivalent standard import entries. After that transformation, the same
SQLite3MC module loaded successfully in both Chromium and Firefox and reported:
sqlite_version() = 3.53.4
sqlite3mc_version() = SQLite3 Multiple Ciphers 2.5.0
This indicates that the SQLite3MC code itself is working and that only the
binary import-section encoding is incompatible.
Suggested fix
- Rebuild the WASM release with a corrected Emscripten toolchain, or explicitly
disable compact imports. - Pin the Emscripten/Binaryen toolchain used for published releases instead of
usingversion: latest. - Add a release smoke test that runs
WebAssembly.compile()or initializes
sqlite3.mjsin stable Chromium and Firefox before publishing the archive.
Activity
Add wasm tests to build workflow (issue #252)
utelle commentedon Aug 26, 2026
Thank you for reporting this issue in such detail.
In the meantime I added a wasm smoke test to the build workflow and pinned the emscripten version. At first I used version 6.0.2 instead of latest, but I had to find out that 6.0.2 produced a different failure. Now I tested version 6.0.3 - and that version seems to work properly.
However, the "official" SQLite wasm distribution is based on version 5.0.1. And the previous SQLite3MC-wasm-build was based on version 6.0.0. So, I'm a bit undecided to which version I should pin emscripten.
matbech commentedon Aug 26, 2026
The following patch is required when using Emscripten 6.x:
utelle commentedon Aug 27, 2026
Thanks for the hint. I will apply the patch and will pin to version 6.0.3.
Add patch to keep WASM imports in std encoding
utelle commentedon Aug 27, 2026
The patch is now applied in the release workflow. According to my own tests the WASM build should now work as expected. I released version 2.5.1. Therefore closing this issue. (Reopen if necessary.)
matbech commentedon Aug 27, 2026
Thank you. I ended up with a custom WASM build. It includes chacha20 SIMD, optimizations for speed (-O2 etc) and stripped legacy crypto and other unnecessary SQLite features like multiple vfs backends etc.
utelle commentedon Aug 27, 2026
The requirements in the WASM environment are certainly different from those in other areas of application. In that regard, it’s certainly not a bad idea to exclude encryption methods that are no longer recommended for use in new applications anyway, thereby reducing the size of the binaries.
If you have specific recommendations on what should and shouldn’t be included, please let me know.
I find your mention of chacha20 SIMD interesting. Are you using an implementation that’s available under a license similar to MIT’s? If so, which one? Other users could also benefit from improved runtime performance.
matbech commentedon Aug 29, 2026
Thanks for following up. Regarding the ChaCha20 SIMD code: because it was heavily LLM-assisted, the licensing and copyright status is ambiguous, so it likely isn't suitable for upstreaming or standard open-source licensing.
However, if you're interested in writing a clean, permissively licensed version yourself, here is how the implementation was structured and what produced the gains:
Implementation Details:
Benchmark Takeaways:
ChaCha20: 2.545× throughput, 624.48 MiB/s → 1.55 GiB/s
Complete 4 KiB page encryption: 1.804×, 456.61 → 823.83 MiB/s
Hopefully, those implementation details and benchmark points help guide your optimization efforts for the WASM build.
utelle commentedon Aug 29, 2026
Thank you for the clear explanation. In fact, I would need verified license and copyright information.
I’d really like to avoid developing everything from scratch. So I did a little research to see what established ChaCha20 implementations are out there. In the process, I took a look at libsodium. Since this is a well-known project licensed under a license compatible with the MIT License, adopting its ChaCha20 implementation would be a realistic option.
The implementation in libsodium is available in four variants, each based on a different level of hardware support: SSSE3, AVX2, AVX512, and NEON.
In the meantime, I've already extracted the relevant code and adapted it for SQLite3MC. But it will still take some time to test what performance gains can be achieved with it.
I'm curious to see how the libsodium implementation will measure up in comparison.
Thank you for sharing your insights. This reinforces my decision not to make any changes to the Poly1305 implementation. While libsodium also has a Poly1305 implementation with hardware acceleration, it is only supported for SSSE3 and, out of the box, can only be compiled with GCC or clang, since it uses 128-bit integers that are not supported by Visual C++.
Thank you very much for the detailed and helpful feedback.
If my experiments with the libsodium implementation are successful, I will include this option in one of the next SQLite3MC releases.
utelle commentedon Sep 2, 2026
In branch hwaccel I have added the ChaCha20 implementation with hardware acceleration from libsodium. AFAICT this implementation shows a performance comparable to the numbers you showed from your implementation. Maybe you could give it a try and compare results with your implementation.
There are 2 additional pragma commands, you can use to specify which hardware feature you want to use.
displays the CPU features detected.
displays the currently used CPU feature used by the libsodium implementation.
allows to select the feature you'd like to use, and returns the feature actually selected.
Supported values are
off,ssse3,avx2,avx512f,neon,auto,max.off- no hardware accelerationssse3,avx2,avx512f- corresponding CPU feature on x86 systemsneon- corresponding feature on ARM systemsauto- highest feature officially supported by libsodium (currrentlyavx2(x86) respoff(ARM))max- highest feature available (currentlyavx512f(x86),neon(ARM))matbech commentedon Sep 2, 2026
Thanks for the update.
I wasn't able to review the libsodium integration, but Emscripten provides drop-in replacement headers for x86 SSE intrinsics that compile directly down to WebAssembly SIMD opcodes. So the SSSE3 implementation in libsodium can be used for WASM SIMD.
What I'm curious though is why you pulled in all the stream abstraction (crypto_stream_chacha20_implementation) and not just the implementation for chacha20_xor?
Do you use the following Emscripten flags?
If you plan to do a native translation from SSSE3:
I assume this is the exact translation the Emscripten header already does.
utelle commentedon Sep 3, 2026
Yes, that was/is the intention. However, I haven't tested to build the WASM variant yet.
The first step was to adapt the libsodium code more or less "as is", so that it could compiled within the source amalgamation. The second step was to test that it works properly. And that's where I stand at the moment. A third step will be to remove unused parts. However, in that course I will check what parts may be useful in the future, because I consider to add a XChaCha20-based encryption scheme in the future.
I learned that it is not possible in WASM to detect and select hardware support at runtime. So, it has to be decided whether a single build supporting SIMD is enough, or whether 2 different builds would be required - with and without SIMD support.
I'm not sure whether it's worth the effort, if Emscripten can transform SSSE3 functions automatically.
BTW, I saw your PRs #254, #255, #256, and #257. Please allow a few days while I look into these in detail, as I'm currently dealing with a few personal matters.