Skip to content

Navigation Menu

Sign in
Sign up

deps: update sqlite to 3.490.100 - #17

Open
github-actions[bot] wants to merge 1 commit into
main from
deps/update-sqlite-3.490.100
Open

deps: update sqlite to 3.490.100 #17
github-actions[bot] wants to merge 1 commit into
main from
deps/update-sqlite-3.490.100

Conversation

@github-actions

@github-actions github-actions Bot commented Feb 23, 2025

Copy link
Copy Markdown

What does this PR do?

Updates SQLite to version 3.490.100

Auto-updated by this workflow

sroussey pushed a commit that referenced this pull request Jul 17, 2026
...s, http (oven-sh#30722)
Hardens 36 reachable security findings across the runtime, package
manager, parsers, HTTP client/server, and SQL drivers. Three
auto-applied fixes (oven-sh#61 SSL exception leak, oven-sh#68 YAML merge dedup, oven-sh#104
archive overwrite precheck) were dropped: oven-sh#61 introduced a
use-after-free, oven-sh#68 stored a non-`'static` byte view in a `'static`
field, and oven-sh#104 added dead gating that did not close the traversal.
### Memory safety / lifetime
- #2 — Dangling proxy slice across reentrant JS getter — copy
`process.env` proxy href to an owned `Vec` before reentrant getters can
free the env map (`Blob.rs`)
- #15 — Rollback restores dangling editor name pointer — preserve and
restore `name_storage` on `detect_editor` failure (`BunObject.rs`)
- oven-sh#81 — Reentrant reconnect frees live handlers — only free previous
handlers when `active_connections == 0` (`Listener.rs`)
- oven-sh#110 — Async randomFill uses stale resizable buffer pointer — fill a
worker-owned scratch buffer; copy back on the JS thread after
re-validating bounds (`node_crypto_binding.rs`)
- oven-sh#119 — Null zero-length slice UB in DOMJIT fast path — use
`ffi::slice` which tolerates `(null, 0)` (`Crypto.rs`)
- oven-sh#67 — Raw serialization reads struct padding bytes — add explicit
`_padding_*` fields with `offset_of!` proof asserts (`npm.rs`)
- oven-sh#74 — TLS rejection path leaks websocket refcount — route SSL/auth
failures through `self.fail()` which clears `outgoing_websocket`
(`websocket_client.rs`)
- oven-sh#108 — FD-backed fetch body leaks duplicated descriptor — close
`opened_fd` unconditionally after `read_file` (`fetch.rs`)
### Untrusted-input bounds / panics
- #10 — Invalid lockfile tag causes panic DoS — replace `unreachable!()`
with logged error + `Tag::Uninitialized` (`dependency.rs`)
- #20 — Unchecked lockfile string offsets cause OOB slice — bounds-check
non-inline `String` pointers against `ctx.buffer` (`dependency.rs`)
- oven-sh#91 — Panic on unvalidated resolution tag — validate `ResolutionTag`
discriminants on lockfile load (`Package.rs`)
- #24 — Unwrap panic on unexpected 304 response — return
`UnexpectedNotModified` when no cached manifest exists (`npm.rs`)
- oven-sh#44 — UDP port getter unwrap panic on transient state — return
`undefined` when `socket` is `None` (`udp_socket.rs`)
- oven-sh#36 — Close reason length mismatch causes panic — clamp `body_len` to
125 and bail on overlong UTF-8 transcode (`websocket_client.rs`)
- oven-sh#100 — Windows pipe name length panic DoS — `debug_assert` → real
bounds check (`Listener.rs`)
- oven-sh#60 / oven-sh#111 — Windows shim stack buffer overflows — bounds-check
argument and filename writes against `BUF1_LEN`/`BUF2_U16_LEN` before
`copy_nonoverlapping` (`bun_shim_impl.rs`)
- oven-sh#76 / oven-sh#101 — Unchecked bin name/entry name copies — bounds-check
before slicing into `abs_dest_buf` (`bin.rs`)
- oven-sh#79 — `if` keyword misclassification causes parser panic — require a
delimiter token before classifying (`shell_parser/parse.rs`)
- oven-sh#32 — Bounds check occurs after UTF-16 write — pre-flight key/value
lengths before `convert_utf8_to_utf16_in_buffer` (`env_loader.rs`)
- oven-sh#95 — PBKDF2 digest validation allows panic-only algorithm — reject
digests with no `EVP_MD` (`PBKDF2.rs`)
### DoS / resource caps
- #17 — Unbounded recursion on deep TOML dotted keys — cap dotted-key
segments at 512 (`toml.rs`)
- oven-sh#39 — Unbounded brace expansion preallocation — cap expansion count at
65536 in `Bun.$` and `Bun.braces` (`BunObject.rs`, `Expansion.rs`)
- oven-sh#31 — SCRAM PBKDF2 parameters accepted from server — clamp iteration
count to `[4096, 10M]`, salt length to `[1, 1024]`
(`PostgresSQLConnection.rs`)
### Auth / injection / traversal
- #19 — Cleartext password sent after TLS downgrade — require
`TLSStatus::SslOk`, not just `ssl_mode != Disable`
(`MySQLConnection.rs`)
- oven-sh#83 — Strict TLS request reuses lax-verified pooled socket — track
`established_with_reject_unauthorized` and refuse pool reuse for strict
callers (`HTTPContext.rs`, `lib.rs`, `ClientSession.rs`)
- oven-sh#73 — IPv6 loopback prefix auth bypass — exact-match `::1` instead of
`starts_with` (`server_body.rs`)
- oven-sh#56 — Unsanitized filename injects response headers — reject
`\r`/`\n`/NUL/`"` in `content-disposition` filenames
(`RequestContext.rs`)
- oven-sh#43 — Missing CRLF checks for signed host/auth headers — also validate
`region`, `access_key_id`, and `host` (`s3_signing/credentials.rs`)
- oven-sh#34 — Bucket slash enables S3 host confusion — reject buckets
containing `/` (`s3_signing/credentials.rs`)
- #25 — Lexical symlink check permits extraction escape — track created
symlinks during extraction and refuse paths that traverse them
(`libarchive/lib.rs`)
- oven-sh#71 — bunx executes untrusted temp-cache binary — `lstat` cached
binary; refuse symlinks and other-uid files (`bunx_command.rs`)
### Permission hygiene
- #6 — Bin target chmod always sets mode 0777 — `0o777 & !umask` instead
of `umask | 0o777` (`bin.rs`)
- #23 — Process umask cleared and never restored — restore umask after
probing it in `ensure_umask` (`bin.rs`)
### Parser correctness
- #22 — Sign-prefixed scalar misparsed as infinity — fix Zig→Rust
`&&`/`||` precedence transliteration (`yaml.rs`)
sroussey pushed a commit that referenced this pull request Jul 17, 2026
...h#33186)
### Repro
```sh
printf '{"name":"x","version":"1.0.0"}' > package.json
bun pm pkg set 'contributors[0]=alice'
```
On a release build (1.4.0 and current `main`) this exits 0 and writes
freed heap bytes into `package.json` as the property key:
```json
{
"name": "x",
"version": "1.0.0",
"P\x01\x00\x00\x00tors": {
 "\x00": "alice"
}
}
```
Depending on what was in the freed allocation the result is often not
valid JSON at all. Any `bun pm pkg set` key path containing `[index]`
hits it.
Under ASAN it is a deterministic `heap-use-after-free`:
```
ERROR: AddressSanitizer: heap-use-after-free
READ of size 1
 #0 bun_js_printer::write_pre_quoted_string_inner src/js_printer/lib.rs:1014
 #7 PmPkgCommand::save_package_json src/runtime/cli/pm_pkg_command.rs:909
freed by thread T0 here:
 #7 <Box<[u8]> as Drop>::drop
 #12 PmPkgCommand::set_value src/runtime/cli/pm_pkg_command.rs:661
previously allocated by thread T0 here:
 #10 <Box<[u8]> as From<&[u8]>>::from
 #11 PmPkgCommand::parse_key_path src/runtime/cli/pm_pkg_command.rs:583
```
<details>
<summary>full ASAN report</summary>
```
=================================================================
==16563==ERROR: AddressSanitizer: heap-use-after-free on address 0x73423c7c0670 at pc 0x00000f583cc5 bp 0x7fff2667e950 sp 0x7fff2667e948
READ of size 1 at 0x73423c7c0670 thread T0
 #0 0x00000f583cc4 in _RINvCs59Hqei94dXF_14bun_js_printer29write_pre_quoted_string_innerINtB2_16StdWriterAdapterQINtB2_6WriterNtB2_12BufferWriterEEKVNtNtB2_8Encoding4Utf8UECsgBGN0jRPILJ_11bun_bundler /workspace/bun/src/js_printer/lib.rs:1014:79
 #1 0x00000ebda439 in <bun_js_printer::__gated_printer::Printer<&mut bun_js_printer::Writer<bun_js_printer::BufferWriter>, false, false, false, true, false>>::print_string_characters_utf8 /workspace/bun/src/js_printer/lib.rs:2641:21
 #2 0x00000ebdb7a7 in <bun_js_printer::__gated_printer::Printer<&mut bun_js_printer::Writer<bun_js_printer::BufferWriter>, false, false, false, true, false>>::print_string_characters_e_string /workspace/bun/src/js_printer/lib.rs:4546:22
 #3 0x00000ebdb238 in <bun_js_printer::__gated_printer::Printer<&mut bun_js_printer::Writer<bun_js_printer::BufferWriter>, false, false, false, true, false>>::print_string_literal_e_string /workspace/bun/src/js_printer/lib.rs:3018:18
 #4 0x00000ebd2be2 in <bun_js_printer::__gated_printer::Printer<&mut bun_js_printer::Writer<bun_js_printer::BufferWriter>, false, false, false, true, false>>::print_property /workspace/bun/src/js_printer/lib.rs:4807:34
 #5 0x00000ebc0166 in <bun_js_printer::__gated_printer::Printer<&mut bun_js_printer::Writer<bun_js_printer::BufferWriter>, false, false, false, true, false>>::print_expr /workspace/bun/src/js_printer/lib.rs:3962:38
 #6 0x00000ee574c1 in bun_js_printer::print_json::<&mut bun_js_printer::Writer<bun_js_printer::BufferWriter>> /workspace/bun/src/js_printer/lib.rs:8071:13
 #7 0x00000c05c270 in <bun_runtime::cli::pm_pkg_command::PmPkgCommand>::save_package_json /workspace/bun/src/runtime/cli/pm_pkg_command.rs:909:25
 #8 0x00000c060cfb in <bun_runtime::cli::pm_pkg_command::PmPkgCommand>::exec_set /workspace/bun/src/runtime/cli/pm_pkg_command.rs:330:13
 #9 0x00000c05d333 in <bun_runtime::cli::pm_pkg_command::PmPkgCommand>::exec /workspace/bun/src/runtime/cli/pm_pkg_command.rs:73:32
 #10 0x00000bf919d0 in <bun_runtime::cli::package_manager_command::PackageManagerCommand>::exec /workspace/bun/src/runtime/cli/package_manager_command.rs:704:13
 #11 0x00000c3fbb87 in bun_runtime::cli::command::exec_pm /workspace/bun/src/runtime/cli/mod.rs:1591:34
 #12 0x00000c3f2b86 in bun_runtime::cli::command::start /workspace/bun/src/runtime/cli/mod.rs:1309:43
 #13 0x00000bfad16c in bun_runtime::cli::cli::start /workspace/bun/src/runtime/cli/mod.rs:573:27
 #14 0x00000bb3c034 in main /workspace/bun/src/bun_bin/lib.rs:230:5
 #15 0x77223ccc7ca7 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
 #16 0x77223ccc7d64 in __libc_start_main csu/../csu/libc-start.c:360:3
 #17 0x0000099d1d1d in __wrap___libc_start_main /workspace/bun/build/debug/../../src/jsc/bindings/workaround-missing-symbols.cpp:487:12
0x73423c7c0670 is located 0 bytes inside of 12-byte region [0x73423c7c0670,0x73423c7c067c)
freed by thread T0 here:
 #0 0x000007ae192a in free crtstuff.c
 #1 0x00000bb3c5a7 in <std::alloc::System as core::alloc::global::GlobalAlloc>::dealloc /root/.rustup/toolchains/nightly-2026年05月06日-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/sys/alloc/unix.rs:48:18
 #2 0x00000bb3be9a in __rustc::__rust_dealloc /workspace/bun/src/bun_bin/lib.rs:56:15
 #3 0x00001258b05f in alloc::alloc::dealloc_nonnull /root/.rustup/toolchains/nightly-2026年05月06日-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/alloc.rs:128:14
 #4 0x0000125872fe in <alloc::alloc::Global>::deallocate_impl_runtime /root/.rustup/toolchains/nightly-2026年05月06日-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/alloc.rs:229:22
 #5 0x000012586364 in <alloc::alloc::Global>::deallocate_impl /root/.rustup/toolchains/nightly-2026年05月06日-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/alloc.rs:344:9
 #6 0x00001258d79c in <alloc::alloc::Global as core::alloc::Allocator>::deallocate /root/.rustup/toolchains/nightly-2026年05月06日-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/alloc.rs:462:23
 #7 0x000012582946 in <alloc::boxed::Box<[u8]> as core::ops::drop::Drop>::drop /root/.rustup/toolchains/nightly-2026年05月06日-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/boxed.rs:1956:24
 #8 0x000012572e44 in core::ptr::drop_in_place::<alloc::boxed::Box<[u8]>> /root/.rustup/toolchains/nightly-2026年05月06日-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ptr/mod.rs:809:1
 #9 0x000011f8d429 in core::ptr::drop_in_place::<[alloc::boxed::Box<[u8]>]> /root/.rustup/toolchains/nightly-2026年05月06日-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ptr/mod.rs:809:1
 #10 0x00000ef6b73a in <alloc::vec::Vec<alloc::boxed::Box<[u8]>> as core::ops::drop::Drop>::drop /root/.rustup/toolchains/nightly-2026年05月06日-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/vec/mod.rs:4258:13
 #11 0x00000ef69e64 in core::ptr::drop_in_place::<alloc::vec::Vec<alloc::boxed::Box<[u8]>>> /root/.rustup/toolchains/nightly-2026年05月06日-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ptr/mod.rs:809:1
 #12 0x00000c061846 in <bun_runtime::cli::pm_pkg_command::PmPkgCommand>::set_value /workspace/bun/src/runtime/cli/pm_pkg_command.rs:661:5
 #13 0x00000c061038 in <bun_runtime::cli::pm_pkg_command::PmPkgCommand>::exec_set /workspace/bun/src/runtime/cli/pm_pkg_command.rs:325:13
 #14 0x00000c05d333 in <bun_runtime::cli::pm_pkg_command::PmPkgCommand>::exec /workspace/bun/src/runtime/cli/pm_pkg_command.rs:73:32
 #15 0x00000bf919d0 in <bun_runtime::cli::package_manager_command::PackageManagerCommand>::exec /workspace/bun/src/runtime/cli/package_manager_command.rs:704:13
 #16 0x00000c3fbb87 in bun_runtime::cli::command::exec_pm /workspace/bun/src/runtime/cli/mod.rs:1591:34
 #17 0x00000c3f2b86 in bun_runtime::cli::command::start /workspace/bun/src/runtime/cli/mod.rs:1309:43
 #18 0x00000bfad16c in bun_runtime::cli::cli::start /workspace/bun/src/runtime/cli/mod.rs:573:27
 #19 0x00000bb3c034 in main /workspace/bun/src/bun_bin/lib.rs:230:5
 #20 0x77223ccc7ca7 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
previously allocated by thread T0 here:
 #0 0x000007ae1bc8 in malloc crtstuff.c
 #1 0x00000bb3c520 in <std::alloc::System as core::alloc::global::GlobalAlloc>::alloc /root/.rustup/toolchains/nightly-2026年05月06日-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/sys/alloc/unix.rs:14:22
 #2 0x00000bb3be30 in __rustc::__rust_alloc /workspace/bun/src/bun_bin/lib.rs:56:15
 #3 0x00001258b335 in alloc::alloc::alloc /root/.rustup/toolchains/nightly-2026年05月06日-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/alloc.rs:101:9
 #4 0x000012586b81 in <alloc::alloc::Global>::alloc_impl_runtime /root/.rustup/toolchains/nightly-2026年05月06日-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/alloc.rs:210:73
 #5 0x0000125862b6 in <alloc::alloc::Global>::alloc_impl /root/.rustup/toolchains/nightly-2026年05月06日-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/alloc.rs:332:9
 #6 0x00001258d86a in <alloc::alloc::Global as core::alloc::Allocator>::allocate /root/.rustup/toolchains/nightly-2026年05月06日-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/alloc.rs:449:14
 #7 0x00001257dbd3 in <alloc::boxed::Box<[u8]>>::try_clone_from_ref_in /root/.rustup/toolchains/nightly-2026年05月06日-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/boxed.rs:881:29
 #8 0x00001257da49 in <alloc::boxed::Box<[u8]>>::clone_from_ref_in /root/.rustup/toolchains/nightly-2026年05月06日-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/boxed.rs:840:15
 #9 0x00001257d3f4 in <alloc::boxed::Box<[u8]>>::clone_from_ref /root/.rustup/toolchains/nightly-2026年05月06日-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/boxed.rs:793:9
 #10 0x000012581e34 in <alloc::boxed::Box<[u8]> as core::convert::From<&[u8]>>::from /root/.rustup/toolchains/nightly-2026年05月06日-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/boxed/convert.rs:77:9
 #11 0x00000c05a47f in <bun_runtime::cli::pm_pkg_command::PmPkgCommand>::parse_key_path /workspace/bun/src/runtime/cli/pm_pkg_command.rs:583:37
 #12 0x00000c061608 in <bun_runtime::cli::pm_pkg_command::PmPkgCommand>::set_value /workspace/bun/src/runtime/cli/pm_pkg_command.rs:643:30
 #13 0x00000c061038 in <bun_runtime::cli::pm_pkg_command::PmPkgCommand>::exec_set /workspace/bun/src/runtime/cli/pm_pkg_command.rs:325:13
 #14 0x00000c05d333 in <bun_runtime::cli::pm_pkg_command::PmPkgCommand>::exec /workspace/bun/src/runtime/cli/pm_pkg_command.rs:73:32
```
</details>
### Cause
`parse_key_path` returned a `Vec<Box<[u8]>>`, and `set_value` /
`set_nested` inserted those boxed segments into the manifest AST by
reference: `E::Object::put` constructs `EString::init(key)`, whose
documented contract is that `key` is arena-owned (it records the slice,
it does not copy it). The vector is a local of `set_value`, so it
dropped before `exec_set` reached `save_package_json`, and the JSON
printer then read the dangling keys.
The non-bracket path in `set_value` did not have the bug: it borrowed
its segments straight out of the argv key, which outlives the whole
command. The bracket path differed only by the unnecessary boxing.
### Fix
`parse_key_path` now returns `Vec<&[u8]>`. Every segment is a literal
sub-slice of the input key, so nothing ever needed owning. With the
boxing gone, `set_value`'s separate non-bracket branch and its
`set_nested_simple` helper (which existed only to avoid the allocation)
were exact duplicates of the bracket path, so they are deleted and all
keys route through `parse_key_path` + `set_nested`.
`set_nested_simple`'s trailing `root.put(current_key, nested)` was a
no-op: `ExprData::EObject` is a `StoreRef` handle, so mutating the copy
returned by `root.get()` already mutates the stored object, and the put
re-stores the same handle. Dropping it with the function changes nothing
(and the prior bracket path, `set_nested`, never had it).
Intentionally not changed here: `set 'contributors[0]=alice'` produces
`"contributors": {"0": "alice"}`, an object keyed by the digit string,
rather than the array npm's `pkg set` creates, and `set 'array[]=x'`
still errors with `InvalidPath` instead of appending. Both are the npm
compat gap tracked in oven-sh#22035, which is separate from the memory safety
of the key names and is not closed by this PR.
### Verification
New test in `test/cli/install/bun-pm-pkg.test.ts` reparses the written
file and asserts the exact object. Without the fix it fails on release
(`SyntaxError: JSON Parse error: Invalid escape character x`) and on the
ASAN debug build (the child aborts on the use-after-free). With the fix
the full `bun-pm-pkg.test.ts` suite passes (74 pass, 0 fail).
sroussey pushed a commit that referenced this pull request Jul 17, 2026
...ry rewrite (oven-sh#34271)
`test/js/bun/util/filesystem_router.test.ts` went red on alpine x64 in
build [73276](https://buildkite.com/bun/bun/builds/73276): the `reload()
while Bun.build() resolves the same directory` subprocess segfaulted in
`bust_dir_cache_recursive`, inlined from `NonNull::new`.
## Cause
`RealFS::entries_at` (`src/resolver/lib.rs`) replaces a cached
`DirEntry` in place when the caller's resolver generation is newer than
the cached listing's. The replacement at `*e_ptr = new_entry` drops the
old `DirEntry`, which drops its `data: StringHashMap<*mut Entry>` and
frees the hashmap's bucket allocation. The function's comment says
`entries_mutex held by caller`, but that is only true on one of the five
paths that reach it: `dir_info_uncached`, when entered from
`dir_info_cached_miss`. The other callers (`finalize_result`,
`handle_esm_resolution`, `load_index_with_extension`,
`Transpiler::run_env_loader`) all reach `entries_at` after
`dir_info_cached_maybe_log` has already returned and released both
`RESOLVER_MUTEX` and `entries_mutex`.
`FileSystemRouter::reload()` and `RouteLoader::load` iterate the same
`DirEntry.data` map under `entries_mutex` (the snapshot pattern oven-sh#33056
introduced for exactly this kind of concurrent rewrite). With
`entries_at`'s rewrite unsynchronized, a `Bun.build()` on the bundler
thread can drop the map while `reload()` on the JS thread is
mid-iteration.
The generation mismatch is what makes `entries_at` enter its rewrite
branch, so the window only opens once the bundle thread has processed at
least one batch (it bumps its own generation after every queue drain);
every subsequent `Bun.build()` then re-reads any directory that
`reload()` just refreshed to generation 0.
ASAN catches it as a heap-use-after-free with the two sides of the race
laid out exactly:
```
READ of size 16 (thread T0):
 #6 HashMap::values
 #7 StringHashMap<*mut Entry>::values src/collections/array_hash_map.rs:1864
 #8 FileSystemRouter::bust_dir_cache_recursive src/runtime/api/filesystem_router.rs:395
 #9 FileSystemRouter::bust_dir_cache src/runtime/api/filesystem_router.rs:451
 #10 FileSystemRouter::reload src/runtime/api/filesystem_router.rs:476
freed by thread T11 (Bundler):
 #11 drop_in_place<bun_resolver::fs_full::DirEntry>
 #12 bun_resolver::fs::RealFS::entries_at src/resolver/lib.rs:1639
 #13 DirInfo::get_entries_ref src/resolver/dir_info.rs:266
 #14 Resolver::finalize_result src/resolver/resolver.rs:1714
 #15 Resolver::resolve_and_auto_install src/resolver/resolver.rs:1485
 ...
 #23 BundleThread::generate_in_new_thread src/bundler/BundleThread.rs:276
previously allocated by thread T0:
 #17 HashMap::reserve
 #18 Resolver::dir_info_cached_miss src/resolver/resolver.rs:4591
 #19 Resolver::dir_info_cached_maybe_log src/resolver/resolver.rs:4201
 #20 Resolver::read_dir_info src/resolver/resolver.rs:4118
 #21 FileSystemRouter::reload src/runtime/api/filesystem_router.rs:492
```
(The use side is sometimes `RouteLoader::load` at
`src/router/lib.rs:816` instead; same map, same lock.)
This has been the shape of `entries_at` since the Rust port; oven-sh#33056
narrowed the race by snapshotting under the lock but assumed the rewrite
side already held it.
## Fix
`entries_at` now takes `entries_mutex` itself, matching
`read_directory_with_iterator` which already does. The one call path
that reaches it with the lock already held (`dir_info_cached_miss` ->
`dir_info_uncached` -> `parent_.get_entries_ref`) routes through a new
`entries_at_locked` / `get_entries_ref_locked` pair so the non-recursive
mutex is not re-entered. That path is the only one that passes a
non-`None` parent to `dir_info_uncached`; the other caller
(`dir_info_for_resolution`) passes `None`, so the parent branch
containing the accessor never runs there.
## Test
The existing concurrency test now awaits one `Bun.build()` first, so the
bundle thread's generation is already past zero when the concurrent
rounds start, and then runs forty reload/build rounds instead of one.
That is the shape that reaches the stale-generation rewrite at all; the
original single-round fixture usually completes with every build still
on generation 0.
The race is scheduling-dependent. Pinning the fixture to a single core
reproduces the ASAN use-after-free on roughly 3 in 10 runs against an
unpatched debug build and 0 in 15 with this change; with all 16 cores
available the unpatched build reproduces at roughly 1 in 30. The
assertions are otherwise the same as before, so the test continues to
cover the behavior oven-sh#33056 added.
Also ran the full `filesystem_router.test.ts`,
`test/bundler/bun-build-api.test.ts` (including the thousands-of-builds
test that exercises the generation path heavily),
`test/js/bun/resolve/resolve.test.ts`, `test/cli/hot/hot.test.ts`,
`test/cli/watch/watch.test.ts`, `test/bake/framework-router.test.ts`,
and `bun run rust:check-all` (10/10 targets).
<!-- robobun:evidence:begin -->
---
**no test proof** · iteration 0 · Platform-specific test(s) that do not
run on this machine. Deferring to CI, which covers all platforms:
test/js/bun/util/filesystem_router.test.ts
<!-- robobun:evidence:end -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

No reviews

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

1 participant

AltStyle によって変換されたページ (->オリジナル) /