sdk: reserve an 8 MiB main-thread shadow stack for linked programs#842
Open
brandonpayton wants to merge 1 commit into
Open
sdk: reserve an 8 MiB main-thread shadow stack for linked programs#842brandonpayton wants to merge 1 commit into
brandonpayton wants to merge 1 commit into
Conversation
Phase B-1 matrix build status —
|
| Package | Arch | Status | Sha |
|---|---|---|---|
| libcurl | wasm32 | built | 154948cf |
| libcxx | wasm32 | built | b7260c81 |
| libcxx | wasm64 | built | 9af5a9bd |
| libpng | wasm32 | built | 1a170103 |
| libxml2 | wasm32 | built | ffe2c666 |
| libxml2 | wasm64 | built | 7a8e8bfe |
| openssl | wasm32 | built | ff4739c9 |
| openssl | wasm64 | built | 0fdc9f3d |
| sqlite | wasm32 | built | 2c18c826 |
| sqlite | wasm64 | built | 6f78fe50 |
| zlib | wasm32 | built | ba5d2d85 |
| zlib | wasm64 | built | 273f9a3e |
| bc | wasm32 | built | d3c11758 |
| bzip2 | wasm32 | built | b602bb54 |
| coreutils | wasm32 | built | f4e7516c |
| curl | wasm32 | built | f1933aef |
| dash | wasm32 | built | 21c59835 |
| diffutils | wasm32 | built | a526e448 |
| dinit | wasm32 | built | 9a9333b2 |
| fbdoom | wasm32 | built | 7d715c01 |
| file | wasm32 | built | 4ad59319 |
| findutils | wasm32 | built | f14d56e3 |
| gawk | wasm32 | built | 43167c9c |
| git | wasm32 | built | 5c60cc86 |
| grep | wasm32 | built | 8e8ff483 |
| gzip | wasm32 | built | 7cac4250 |
| hello | wasm32 | built | 40e207ba |
| kandelo-sdk | wasm32 | built | fc71b9ad |
| kernel | wasm32 | built | f84a5360 |
| less | wasm32 | built | 39fdd424 |
| lsof | wasm32 | built | 40f59d6d |
| m4 | wasm32 | built | 2b187906 |
| make | wasm32 | built | 85da34e9 |
| mariadb | wasm32 | built | 055dcb38 |
| mariadb | wasm64 | built | f0db5a84 |
| modeset | wasm32 | built | 0f57ba86 |
| msmtpd | wasm32 | built | 0d664123 |
| nano | wasm32 | built | 8b11aad8 |
| ncurses | wasm32 | built | 5eadfd4f |
| netcat | wasm32 | built | f51e98ef |
| nginx | wasm32 | built | c7fdf80a |
| php | wasm32 | built | ed4b6e00 |
| posix-utils-lite | wasm32 | built | d4bb5dce |
| sed | wasm32 | built | 0de52b10 |
| spidermonkey | wasm32 | built | 2bc880bf |
| tar | wasm32 | built | 0131f136 |
| tcl | wasm32 | built | b6069068 |
| unzip | wasm32 | built | 3c3e994b |
| userspace | wasm32 | built | 74cfbf92 |
| vim | wasm32 | built | a30fa39c |
| wget | wasm32 | built | d1cfb3a9 |
| xz | wasm32 | built | 3fc3a70a |
| zip | wasm32 | built | efc3b71e |
| zstd | wasm32 | built | 07e6d0d4 |
| bash | wasm32 | built | 64270ef7 |
| mariadb-test | wasm32 | built | 8951e082 |
| mariadb-vfs | wasm32 | built | 76192e32 |
| mariadb-vfs | wasm64 | built | 07c2f83a |
| nethack | wasm32 | built | 2ccbaf57 |
| node | wasm32 | built | fe3afc28 |
| spidermonkey-node | wasm32 | built | dbcbaf9a |
| vim-browser-bundle | wasm32 | built | a66884fc |
| nethack-browser-bundle | wasm32 | built | d598cdef |
| rootfs | wasm32 | built | 90036c7c |
| shell | wasm32 | built | 5388ab5f |
| lamp | wasm32 | built | f930e23e |
| node-vfs | wasm32 | built | 3fdb3c1a |
| wordpress | wasm32 | built | 33fed5e9 |
Auto-generated; replaced on each push. Raw data in the publish-status workflow artifact.
977371e to
cdff91f
Compare
wasm-ld's default shadow stack is only ~64 KiB, and WebAssembly has no stack guard page, so a program that overflows it does not fault at the overflow: __stack_pointer silently decrements past __data_end and overwrites the top of .bss. On this platform that region holds the pthread/TLS globals (__wasm_tp_storage -- the main thread's struct pthread -- and __pthread_tsd_main), so an overflow corrupts thread-local storage and later surfaces as a spurious "memory access out of bounds" in an unrelated function (e.g. __pthread_getspecific or pthread_mutex_lock), far from the real fault. Deep call chains in ordinary libraries hit this easily; GTK's gdk_pixbuf_new_from_file -> GObject type registration -> glib chain is a confirmed case. Reserve 8 MiB instead. POSIX leaves the default stack size implementation-defined (RLIMIT_STACK governs the main thread), but 8 MiB is the de-facto Linux/glibc default that mainstream C software is written and tested against, so matching it maximizes portability. Because a wasm shadow stack cannot grow at runtime, the reservation is fixed at link time. This sizes the main thread only (pthreads use musl __default_stacksize, 128 KiB) and costs ~8 MiB of initial linear memory per process (it raises __heap_base 1:1). Added to both SDK cc implementations (bash wasm32posix-cc exe_link_flags and the TS linkFlags()), kept in sync, plus a LINK_FLAGS test and a docs/sdk-guide.md rationale. No ABI change: the host reads __heap_base/__stack_pointer dynamically per module, so existing 64 KiB binaries and new 8 MiB binaries run on the same kernel unchanged; the change touches only user-program link flags, not the kernel, crates/shared, syscall numbers, or struct layouts. Verified: linking the exact crashing gdk_pixbuf_new_from_file case with the new default decodes the PNG (PASS 32x32) where the 64 KiB default traps; 1/8/16 MiB all pass while 64 KiB fails. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
cdff91f to
2dc2115
Compare
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.
Problem
wasm-ld's default main-thread shadow stack is about 64 KiB, and WebAssembly has no stack guard page. An overflow therefore writes below
__data_endinto allocator and pthread/TLS globals before failing later in an unrelated function.This has two concrete package-level reproductions:
gdk_pixbuf_new_from_filecall chain corrupts TLS under the default stack.setupterm()deterministically crosses the 64 KiB boundary and corrupts__malloc_contextbeforestrdup()traps.These are SDK memory-layout failures, not GTK or ncurses compatibility defects.
Fix
Reserve an 8 MiB main-thread shadow stack in both SDK compiler drivers:
sdk/src/lib/flags.tsfor the TypeScript host SDKsdk/kandelo/bin/wasm32posix-ccfor the in-Kandelo SDKThe SDK treats 8 MiB as a floor. Smaller direct decimal
-Wl,-z,stack-size=<bytes>requests are raised to 8 MiB, while larger requirements remain intact. Directly referenced linker response files are inspected for the equivalent lld option. In particular, SpiderMonkey's explicit 16 MiB request still produces a 16 MiB stack.The native driver parses response files with ordinary file redirection and pure Bash matching; it does not depend on process substitution or FIFO support.
POSIX leaves the default stack size implementation-defined. 8 MiB matches the common Linux/glibc main-thread default. This affects only the initial thread; pthread stacks continue to use musl's separate default.
ABI
No ABI change. The host reads each module's exported
__heap_baseand__stack_pointerdynamically. Existing 64 KiB binaries and newly linked 8 MiB binaries remain compatible with the same kernel, and the ABI snapshot is unchanged.Validation
Local validation on commit
2dc2115a6:npm --prefix sdk test: 61 tests passed, including a new test that executes the packaged Bash driver with fake LLVM tools.screen-256colorentry and returnedcolors=256.bash scripts/check-abi-version.sh: snapshot and generated bindings remained in sync.The previous head completed the full 68-package staging matrix and all test suites. The current rebased head intentionally triggers CI again for current package-build evidence.