Skip to content

Fix mmap accounting under partial munmap churn - #769

Closed
brandonpayton wants to merge 15 commits into
mainfrom
gascity/kd-cfo/kd-cfo.4-debug-wave34-browser-memsubsys-readonly-sqlite-failures
Closed

Fix mmap accounting under partial munmap churn#769
brandonpayton wants to merge 15 commits into
mainfrom
gascity/kd-cfo/kd-cfo.4-debug-wave34-browser-memsubsys-readonly-sqlite-failures

Conversation

@brandonpayton

Copy link
Copy Markdown
Member

Summary

  • Round munmap lengths up to the Wasm page size before kernel bookkeeping and host mmap cleanup checks.
  • Coalesce adjacent compatible mmap regions and avoid allocating a temporary occupied-region vector during gap search.
  • Restore fork-deserialized mappings in sorted/coalesced order so the gap-search invariant is preserved.

Root cause

SQLite memsubsys1/altermalloc.test repeatedly mapped roughly 0x30000-byte regions and unmapped caller lengths around 0x29000. Kandelo rounded mmap to 64 KiB pages but used the literal munmap length in memory bookkeeping, leaving tiny tail mappings. Under mmap churn, those tails inflated the mapping list and the allocator path eventually hit RuntimeError: unreachable in focused Node/browser SQLite lanes.

Validation

  • cargo test -p kandelo --target aarch64-apple-darwin --lib: 964 pass / 0 fail / 0 skip. Artifact: /Users/brandon/src/kandelo-gascity/worktrees/kandelo/kd-cfo.4-debug-wave34-browser-memsubsys-readonly-sqlite-failures/test-runs/kd-cfo.4/kernel-full-lib-after-current-rebuild-20260623T213359Z
  • cd host && npx vitest run: 752 pass / 0 fail / 108 skip, exit 0. Exact gate artifact: /Users/brandon/src/kandelo-gascity/worktrees/kandelo/kd-cfo.4-debug-wave34-browser-memsubsys-readonly-sqlite-failures/test-runs/kd-cfo.4/host-vitest-after-hello64-20260623T213936Z; JSON outcome lists: /Users/brandon/src/kandelo-gascity/worktrees/kandelo/kd-cfo.4-debug-wave34-browser-memsubsys-readonly-sqlite-failures/test-runs/kd-cfo.4/host-vitest-json-outcomes-20260623T214029Z
  • scripts/run-libc-tests.sh: 303 pass / 0 fail / 0 skip, plus 1 flake-pass and 20 documented XFAIL, exit 0. Artifact: /Users/brandon/src/kandelo-gascity/worktrees/kandelo/kd-cfo.4-debug-wave34-browser-memsubsys-readonly-sqlite-failures/test-runs/kd-cfo.4/libc-tests-after-submodule-init-20260623T214334Z
  • scripts/run-posix-tests.sh: 174 pass / 0 fail / 2 skip, plus 3 documented XFAIL, exit 0. Artifact: /Users/brandon/src/kandelo-gascity/worktrees/kandelo/kd-cfo.4-debug-wave34-browser-memsubsys-readonly-sqlite-failures/test-runs/kd-cfo.4/posix-tests-after-munmap-fix-20260623T215119Z
  • bash scripts/check-abi-version.sh: passed with dev-shell nightly plus explicit bzip2/xz library paths. Artifact: /Users/brandon/src/kandelo-gascity/worktrees/kandelo/kd-cfo.4-debug-wave34-browser-memsubsys-readonly-sqlite-failures/test-runs/kd-cfo.4/abi-check-dev-shell-nightly-bzip2-path-after-munmap-fix-20260623T213546Z
  • Focused SQLite browser memsubsys cluster after rebuild: 4 pass / 0 fail / 0 skip / 0 incomplete. Artifact: /Users/brandon/src/kandelo-gascity/worktrees/kandelo/kd-cfo.4-debug-wave34-browser-memsubsys-readonly-sqlite-failures/test-runs/kd-cfo.4/focused-browser-memsubsys1-cluster4-after-current-rebuild-20260623T213021Z
  • Focused SQLite browser readonly representative cluster after rebuild: 8 pass / 0 fail / 0 skip / 0 incomplete. Artifact: /Users/brandon/src/kandelo-gascity/worktrees/kandelo/kd-cfo.4-debug-wave34-browser-memsubsys-readonly-sqlite-failures/test-runs/kd-cfo.4/focused-browser-readonly-unableopen8-after-current-rebuild-20260623T213224Z

Notes

  • cargo fmt -p kandelo --check was attempted and is blocked by pre-existing formatting drift in unrelated parts of syscalls.rs and wasm_api.rs; no broad rustfmt was applied to avoid unrelated churn.
  • The focused long Node SQLite DB row passes after this fix but the wrapper process exits 132 with RuntimeError: unreachable after the done row; follow-up bead kd-cfo.5 tracks that separate post-pass runtime issue.
  • No ABI snapshot/version change is needed; the ABI check reports generated bindings and snapshot up to date.

@github-actions

github-actions Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Phase B-1 matrix build status — pr-769-staging

ABI v15. 67 built, 0 failed, 67 total.

Package Arch Status Sha
libcurl wasm32 built bec46d0b
libcxx wasm32 built c331e742
libcxx wasm64 built c844f6bb
libpng wasm32 built 641c37b6
libxml2 wasm32 built be6e19ba
libxml2 wasm64 built a6885aab
openssl wasm32 built e31a4e3f
openssl wasm64 built 03668e32
sqlite wasm32 built 3f626c20
sqlite wasm64 built 57ef076d
zlib wasm32 built 728c7909
zlib wasm64 built a3dc0e46
bc wasm32 built af5a6ede
bzip2 wasm32 built fa17baf1
coreutils wasm32 built b6d8b93b
curl wasm32 built 1b8bc0c7
dash wasm32 built c2cd98b6
diffutils wasm32 built 47b7c330
dinit wasm32 built c24f75d5
fbdoom wasm32 built 2bfcce51
file wasm32 built 6089029f
findutils wasm32 built 9f2625fe
gawk wasm32 built a29ca70e
git wasm32 built 09108998
grep wasm32 built f4ee8e96
gzip wasm32 built 38fb8ddf
kandelo-sdk wasm32 built fffef875
kernel wasm32 built f269adc7
less wasm32 built ccaa6c7d
lsof wasm32 built 0a4102c9
m4 wasm32 built 0770227d
make wasm32 built ad203925
mariadb wasm32 built 8027a3be
mariadb wasm64 built cf4ef8a7
modeset wasm32 built 6e66cfe5
msmtpd wasm32 built 60d1dfe8
nano wasm32 built 75164b0c
ncurses wasm32 built ffd48fef
netcat wasm32 built 3b05b016
nginx wasm32 built 4fd44cf0
php wasm32 built 3199182a
posix-utils-lite wasm32 built f280f251
sed wasm32 built 122e4e69
spidermonkey wasm32 built 1e70624f
tar wasm32 built dbb8b117
tcl wasm32 built 0376be28
unzip wasm32 built 5699385b
userspace wasm32 built d4f9fe28
vim wasm32 built 25e9ff9c
wget wasm32 built f79e283d
xz wasm32 built 03a0a427
zip wasm32 built 27175dfb
zstd wasm32 built d0933168
bash wasm32 built 15989fa1
mariadb-test wasm32 built d837a7e8
mariadb-vfs wasm32 built daf28d6f
mariadb-vfs wasm64 built 1b8ca84e
nethack wasm32 built a700934f
node wasm32 built 1604ae9b
spidermonkey-node wasm32 built bdd092b9
vim-browser-bundle wasm32 built 62531f22
nethack-browser-bundle wasm32 built bb8c62ae
rootfs wasm32 built 9f2461ad
shell wasm32 built 4139368d
lamp wasm32 built 0c58abee
node-vfs wasm32 built aac70179
wordpress wasm32 built c130690c

Auto-generated; replaced on each push. Raw data in the publish-status workflow artifact.

(cherry picked from commit da16dcf)
SQLite no_mutex_try incrvacuum3 exposed a case where adjacent anonymous mappings were coalesced in kernel metadata. A later mremap could report in-place growth success without extending exact mapping metadata, allowing the next mmap to reuse pages that the guest allocator believed were reserved.

Keep adjacent mapping boundaries intact, make extend_mapping report whether it updated exact metadata, and return EINVAL from mremap when the grow check cannot be materialized. Add a regression test for mremap growth followed by mmap reuse.
@brandonpayton

Copy link
Copy Markdown
Member Author

Extracted the zero-byte syscall copy-back work from source commit 5e1da94 into #924. It was rebuilt directly on current main and keeps only the shared host marshalling fix plus focused EOF, partial-read, and copyRetvalAdd tests; none of the other #769 ancestry was carried over.

@brandonpayton

Copy link
Copy Markdown
Member Author

Disposition for commit 4318995: extracted into the rebuilt #782. I rewrote #782 directly from current main instead of replaying the stale composite ancestry. It now contains only the host-owned browser setImmediate helper, the normal and network-worker wiring, and focused regressions for cancellation retention, timeout-handle collisions, and macrotask ordering. Final head: 3f133f9. The rest of #769 is not included in that PR.

@brandonpayton

Copy link
Copy Markdown
Member Author

Extracted the initial-credentials work from 6d16dd548fe802eb5befce9895a188bdb0b9a8f7 into focused PR #926 on current main.

I recorded #769 at head de64e6bca2e77eef49b1693dc3f9a9eb0b449d10 before extraction. #926 keeps only the run-example.ts UID/GID wiring, documentation, and an end-to-end guest credential regression. It does not carry SQLite/package work or the mmap/munmap work already landed through #907.

The old parser was not replayed verbatim: it accepted 0xffffffff, which the existing host/kernel protocol reserves as “leave unchanged” and could therefore silently leave a requested credential as root. #926 rejects that sentinel and verifies the largest usable ID.

Disposition for this source commit: extracted into #926 with the reserved-sentinel correction.

@brandonpayton

Copy link
Copy Markdown
Member Author

Commit 1a9e7325b5a45002da9899da2ea20a2ae8a58b32 is now accounted for:

This disposition does not close #769; its other commits still need their own ownership decisions.

@brandonpayton

Copy link
Copy Markdown
Member Author

Lock-table disposition: commit 19a9435fda1c2ce8dddf49bd9042367b29ca0248 is rejected as written because increasing the fixed table from 256 to 4096 only moves an undefined failure boundary. The focused replacement is #929: it keeps the existing capacity and reports exhaustion as ENOLCK instead of EAGAIN or an endless F_SETLKW retry.

@brandonpayton

Copy link
Copy Markdown
Member Author

Directory-fsync disposition: the useful part of f76741d59352a29bdcca96234a6ad7118e24d043 is now extracted from this composite PR as #930.

The focused replacement is based directly on current main and has one commit, c9f9a1a649c5551f02a24ab5487becccabb6068b. I did not cherry-pick the old design. #930 delegates directory fsync() to the active backend so Node can issue its native barrier, while documenting and testing the memory and OPFS host boundaries. It drops all mmap/munmap ancestry, SQLite-specific framing, package work, and the old unconditional kernel no-op.

Ownership for this row can now be recorded as extracted to #930, not merged through #907 and not retained in #769.

@brandonpayton

Copy link
Copy Markdown
Member Author

Stat metadata slice disposition:

  • e56cde47 and 2c3d702 are rejected as written, not treated as merged. Their st_blocks = ceil(st_size / 512) result reports logical length as allocated storage, which is false for sparse SharedFS files; a fixed 4096-byte block size is also not truthful across backends.
  • The independently valid hardening is extracted in libc: zero stat fields the kernel does not report #931: libc now zero-initializes the ABI 39 fields the kernel does not report, with a regression that proves main previously copied stack garbage.
  • Track real stat block metadata across filesystem backends #928 tracks the real backend-metadata and ABI design for st_rdev, st_blksize, and st_blocks.
  • No SQLite patch or harness workaround was retained in this slice.

@brandonpayton

Copy link
Copy Markdown
Member Author

Two more SQLite patch rows now have explicit ownership decisions:

  • a314114ff6f8b5c0c16a1e0e64b89558f4863c02 is rejected as written. Its -end 2 change removes nearly all of SQLite's transient-OOM fault positions solely to shorten the run. That can hide Kandelo allocator, process, or VFS defects and is not an explicit WebAssembly compatibility boundary. If this test needs a faster schedule, that belongs in an opt-in runner profile; the official slow coverage should stay intact.
  • de64e6bca2e77eef49b1693dc3f9a9eb0b449d10 is retained as the focused test-only SQLite tests: use the UTF-16 STAT4 allocation range #932. The utf16 permutation really allocates roughly 2,000 bytes for the two STAT4 samples instead of roughly 1,000, so SQLite tests: use the UTF-16 STAT4 allocation range #932 keeps the same SCHEMA_USED assertion with the encoding-correct range. It skips nothing, changes no production behavior or ABI, and does not bump the shipped SQLite library revision.

Focused validation for #932 rebuilt the testfixture and passed config=utf16 analyze9.test on Node and Chromium: 147 cases and 0 errors on each host. No other #769 patch or runtime ancestry is included.

@brandonpayton

Copy link
Copy Markdown
Member Author

The post-#907 split is complete. Here is the final ownership map for every
commit in this composite PR.

#769 commit Decision Current owner
eab0e5c82 Extracted and narrowed #780 and #783
43189956c Extracted #782
93fe82b28 Superseded by the mmap/munmap work in #907; not merged verbatim #907
5e1da94cf Extracted #924
167b9c613 Extracted with truthful pipe/outcome handling #783
19a9435fd The capacity bump was rejected; the lock-table contract was fixed instead #929
bfe74476d Extracted without the SQLite/browser harness #784
9e0811dd3 The no_mutex workarounds were rejected; the real TestRecover lifetime fix was extracted #933
e56cde47d Rejected because synthetic block counts would not reflect backend state #931 zeroes unreported fields; #928 tracks truthful allocation metadata
f76741d59 Extracted #930
2c3d70265 Rejected with the same stat decision #931 and #928
a314114ff Rejected because broadly omitting pager-fault coverage only hides the test cost No code retained
6d16dd548 Extracted #926
1a9e7325b Extracted and narrowed to metadata-consistent growth #927
de64e6bca Extracted as the focused UTF-16 STAT4 test correction #932

#907 already landed the actual mmap/munmap platform work. The focused PRs above
keep the remaining contracts reviewable without carrying #769's 15-commit
ancestry or the deliberately rejected test workarounds.

No #769 commit remains unclassified. I am closing this PR as decomposed, not as
merged verbatim.

@brandonpayton

Copy link
Copy Markdown
Member Author

Every commit is now extracted, superseded by #907, replaced by a contract-focused follow-up, or explicitly rejected in the matrix above. Closing this composite PR as decomposed, not merged verbatim.

@brandonpayton
brandonpayton deleted the gascity/kd-cfo/kd-cfo.4-debug-wave34-browser-memsubsys-readonly-sqlite-failures branch July 13, 2026 20:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant