Skip to content

deps: update libarchive to v3.8.9 - #6

Open
github-actions[bot] wants to merge 1 commit into
mainfrom
deps/update-libarchive
Open

github-actions[bot] wants to merge 1 commit into
mainfrom
deps/update-libarchive

Conversation

@github-actions

@github-actions github-actions Bot commented Jun 28, 2026 •

Copy link
Copy Markdown

What does this PR do?

Updates libarchive to version v3.8.9

Compare: libarchive/libarchive@ded8229...27cbc78

Auto-updated by this workflow

@github-actions
github-actions Bot force-pushed the deps/update-libarchive branch from 969e632 to 86c3690 Compare August 2, 2026 04:14
@github-actions github-actions Bot changed the title deps: update libarchive to v3.8.8 deps: update libarchive to v3.8.9 Aug 2, 2026
Seantheprogrammer93 pushed a commit that referenced this pull request Sep 27, 2026
…h#36545)

## Repro

Under a debug/ASAN build:

```js
const result = Bun.spawnSync(["cat"], { stdin: "ignore" });
const copy = { ...result };
```

```
ASSERTION FAILED: hasInlineStorage()
JSObject.h(437) : PropertyStorage JSC::JSObject::inlineStorage()
  frame #4: JSC::JSObject::inlineStorage
  frame #5: tryCreateObjectViaCloning (ObjectConstructorInlines.h:281)
  frame #6: globalFuncCloneObject (JSGlobalObjectFunctions.cpp:1006)
```

The same assert fires for a spread of an `S3Client.list()` result or one
of its `contents` entries, for `const x = structuredClone({}); x.a = 1;
({ ...x })`, and for `const m = new
(require("node:module").Module)("x"); m.exports.a = 1; ({ ...m.exports
})`. Release builds are unaffected: the pointer returned by
`inlineStorage()` is fed into a copy loop bounded by `inlineCapacity`,
which is zero, so nothing is dereferenced.

## Cause

Several bindings call `constructEmptyObject(globalObject,
objectPrototype(), 0)` (either as a literal `0` or as a computed size
that can be `0`), which yields a `JSFinalObject` with `inlineCapacity()
== 0` and therefore `hasInlineStorage() == false`. Every property added
to such an object lands out of line in the butterfly.

The object-spread fast path `tryCreateObjectViaCloning` in the current
WebKit build calls `source->inlineStorage()` unconditionally to seed
`createWithButterflyCopyingInlineStorage`, and `inlineStorage()` asserts
`hasInlineStorage()` in debug builds. No object produced by JavaScript
has zero inline capacity (`{}` gets
`JSFinalObject::defaultInlineCapacity`, and `ObjectAllocationProfile`
asserts `inlineCapacity > 0`), so only host-created objects with an
explicit `0` hint reach this state.

## Fix

Treat a zero hint as "unsized" and route it through
`constructEmptyObject(globalObject)`, which produces the same
`defaultInlineCapacity` structure as a JS `{}` literal and gives the
first handful of properties inline slots instead of forcing a butterfly.

- `JSC__JSValue__createEmptyObject` and `JSC__JSObject__create` (the two
Rust FFI entry points) gain an `if (!initialCapacity)` branch. This
covers every Rust caller of `JSValue::create_empty_object(_, 0)` (25
call sites on main, `Bun.spawnSync` among them) and
`JSObject::create_with_initializer(_, _, 0)`. The non-zero branch now
clamps the `size_t` hint to `maxInlineCapacity` before it narrows to
`unsigned`, so a hint that is a multiple of 2^32 cannot narrow to zero
either.
- The direct C++ callers that pass `0` (literal or computed) switch to
the default-capacity overload: `SQLClient.cpp` row fallback,
`ZigGlobalObject.cpp` worker serialized-env when empty (Windows only on
current main), `bindings.cpp` `SystemError` `info`,
`NodeModuleModule.cpp` `new Module().exports`, the two
`SerializedScriptValue.cpp` fast-path deserializer sites
(`structuredClone({})` / `structuredClone([{}])`), the `_NativeModule.h`
`INIT_NATIVE_MODULE` macro (applied to the uncached construction), the
`JSEnvironmentVariableMap.cpp` `process.env` target when `count == 0`,
and the `JSONRowsToJS.cpp` object builder when the parsed object has no
properties (reaches user code through `Bun.JSONC.parse`,
`Bun.TOML.parse`, JSON5, and XML; this file landed on main after the
first audit and review caught it).

Two sites from the first version of this PR are gone after the merge
with main: `ProcessBindingUV.cpp` got the same fix in oven-sh#34660, and the
two `NodeHTTP.cpp` `assignHeaders*` sites were removed when request
headers became lazy.

The remaining computed-capacity
`constructEmptyObject(.*objectPrototype(),` sites in `src/` are
`fromEntries` (already guarded), `JSFetchHeaders.cpp` (early-returns on
`size == 0`), `JSDOMFormData.cpp` / `JSURLSearchParams.cpp` (`size +
1`), and `JSSQLStatement.cpp` (`initializeColumnNames` returns early
when `count < 1`). Everything else passes a literal nonzero constant.

## Verification

New spread tests guarding each confirmed-aborting surface:

- `test/js/bun/spawn/spawnSync.test.ts`: subprocess spreads the
`Bun.spawnSync` result.
- `test/js/bun/s3/s3-list-objects.test.ts`: subprocess spreads the
top-level `list()` result, a `contents` entry, its `owner`, and a
`commonPrefixes` entry.
- `test/js/node/module/node-module-module.test.js`: subprocess spreads
`new Module().exports` after a put.
- `test/js/web/structured-clone-fastpath.test.ts`: spreads
`structuredClone({})` and `structuredClone([{}])[0]` after adding a
property.
- `test/js/node/process-binding.test.ts`: spreads
`process.binding("uv")` (the fix for this one is already on main, the
test pins it).
- `test/js/sql/sql.test.ts`: spreads the wide-row result on the
null-structure fallback path.
- `test/js/bun/jsonc/jsonc.test.ts`: spreads a parsed nested empty `{}`
(JSONC) and an empty TOML table after adding a property.

Each aborts (or, for the subprocess tests, produces empty stdout)
against main's `src/` and passes with the fix. The assertion only exists
in debug builds of JavaScriptCore, so these tests cannot fail against a
release binary.

The remaining sites are defensive rather than confirmed aborts and ship
without a dedicated test:
- `SystemError` `info`: its `DontDelete` properties bail
`checkStructureForClone`.
- `JSC__JSObject__create`: its sole caller early-returns through the
guarded helper when the count is 0.
- Worker serialized-env: the object is consumed before user code can
spread it.
- `INIT_NATIVE_MODULE(0)` (`node:constants`): 216 default-attribute
properties push the structure to `Dictionary`, which bails the fast
path.
- `JSEnvironmentVariableMap.cpp` `process.env` target: unconditional
`CustomAccessor` puts for TZ et al. bail `checkStructureForClone`.

<!-- 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/node/module/node-module-module.test.js,
test/js/bun/spawn/spawnSync.test.ts

<!-- robobun:evidence:end -->

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Seantheprogrammer93 pushed a commit that referenced this pull request Sep 27, 2026
…back closes the session (oven-sh#42647)

### Problem
- A server `RST_STREAM` can make the HTTP/2 fetch client read and free a
stream twice. A debug ASAN build of main (09bb546) reports
`AddressSanitizer: heap-use-after-free` in `ClientSession::handle_data`
(`src/http/h2_client/ClientSession.rs:848`, the `s.state !=
StreamState::Closed` read). The stream was freed by `drop_stream` in
`ClientSession::fail_streams` (`ClientSession.rs:943`). A release build
has no report, only heap corruption.
- It needs a request with a custom TLS context (for example `tls: {
serverName }`) whose entry left the 60-entry context cache. That request
then holds the last ref. Its terminal callback drops the context, the
drop closes the session's socket, and `on_close` runs `fail_streams`
inside the deliver loop.

### Fix
- While `delivering` is set, `fail_streams` fails each client and leaves
the streams in `streams` and `by_http_id`. The deliver loop already
removes a stream that has no client, through `remove_stream`, so each
stream is freed once.
- Outside the loop `fail_streams` behaves as before.
- Correct because the loop's tail is safe on a session that is already
closed: `maybe_release` returns at the `registry_index` sentinel, and a
write to the closed socket writes nothing.
- Verified: `test/js/web/fetch/fetch-http2-client.test.ts` (two new
ASAN-only tests, 67 pass). With `src/` at main the RST test fails. Also
`fetch-http2-leak.test.ts` (7 pass) and
`fetch-http2-adversarial.test.ts` (20 pass).

### Background
- `ClientSession` is one HTTP/2 connection of the fetch client.
`streams` maps a stream id to a heap `Stream`. Each `Stream` points at
the `HTTPClient` of one `fetch()`.
- `handle_data` parses frames, then delivers each ready stream to its
client in a loop, with `delivering` set. A terminal delivery runs the
result callback at once, on the HTTP thread.
- Custom TLS contexts are refcounted. The cache holds one ref and each
request holds one.

<details><summary>Notes</summary>

This is the part of oven-sh#31788 that main still needs. oven-sh#31788 was closed as
stale, and all five of its `src/` files conflict with main now.

- Its first defect, `abort_by_http_id` with no ref on the session, is
fixed on main: since oven-sh#37870 every entry point goes through
`ClientSession::enter`, which holds a `RefPtr`. The abort test from
oven-sh#31788 passes on main. It is included here because main has no test that
evicts a TLS context.
- Its third part, a `ctx` field on `PendingConnect`, has no reproduction
and is not included.

ASAN report on main, trimmed:

```
ERROR: AddressSanitizer: heap-use-after-free READ of size 1 thread T2 (HTTP Client)
    #0 <State as PartialEq>::eq            src/http/h2_client/Stream.rs:104
    #2 ClientSession::handle_data          src/http/h2_client/ClientSession.rs:848
    #4 ClientSession::enter                src/http/h2_client/ClientSession.rs:242
    #6 Handler<true>::on_data              src/http/HTTPContext.rs:1386
freed by thread T2 (HTTP Client) here:
    oven-sh#12 drop_stream                        src/http/h2_client/ClientSession.rs:211
    oven-sh#13 ClientSession::fail_streams        src/http/h2_client/ClientSession.rs:943
```

The two tests share one helper. The child holds a stream open on a
context made by `serverName`, runs 61 more TLS configs to evict it,
writes `evicted` to stderr, and then ends the held request: one test
aborts it, the other has the server send `RST_STREAM(CANCEL)`. Both
check that a later `fetch()` still works. They run only under ASAN and
carry a 30 s timeout, because each child makes 62 TLS handshakes and a
regression needs time to print its report. Without the longer timeout
the failing run on main shows a timeout at 5 s, not the report.

The tests are serial on purpose: each one fills the context cache, which
would evict the context that a concurrent test depends on.

One older test in the same file changed: `concurrent requests multiplex
on one h2 session`. Its server held each stream for 100 ms and the test
expected 8 open at once. On a loaded machine it saw 7 (2 of 9 runs of
the file). The server now answers when the eighth stream is open, so the
test uses no timer. After the change the file passed 4 of 4 runs on the
same loaded machine.
</details>

<!-- robobun:evidence:begin -->

---

**[human-review]** gate passed · iteration 1 · 2 files touched

<details><summary>fails on main (without fix)</summary>

```console
ASAN without fix: 1 FAILED
$ BUN_DEBUG_QUIET_LOGS=1 bun scripts/build.ts --profile=debug --quiet test "--reporter=junit" "--reporter-outfile=/tmp/pr_gate.xml" "test/js/web/fetch/fetch-http2-client.test.ts"
bun test v1.4.3 (b993710)

test/js/web/fetch/fetch-http2-client.test.ts:
(pass) fetch() over HTTP/2 (BUN_FEATURE_FLAG_EXPERIMENTAL_HTTP2_CLIENT) > GET: status, headers and body round-trip [1309.07ms]
(pass) fetch() over HTTP/2 (BUN_FEATURE_FLAG_EXPERIMENTAL_HTTP2_CLIENT) > response body larger than one DATA frame [1215.67ms]
(pass) fetch() over HTTP/2 (BUN_FEATURE_FLAG_EXPERIMENTAL_HTTP2_CLIENT) > concurrent requests multiplex on one h2 session [1497.63ms]
(pass) fetch() over HTTP/2 (BUN_FEATURE_FLAG_EXPERIMENTAL_HTTP2_CLIENT) > POST with ReadableStream body streams as raw DATA frames [730.67ms]
(pass) fetch() over HTTP/2 (BUN_FEATURE_FLAG_EXPERIMENTAL_HTTP2_CLIENT) > gzip content-encoding is decompressed [2353.52ms]
(pass) fetch() over HTTP/2 (BUN_FEATURE_FLAG_EXPERIMENTAL_HTTP2_CLIENT) > POST: request body is delivered as DATA frames [2514.64ms]
(pass) fetch() over HTTP/2 (BUN_FEATURE_FLAG_EXPERIMENTAL_HTTP2_CLIENT) > POST with ReadableStream body larger than initial send window [927.77
... (truncated)

release without fix: 2 skipped
bun test v1.4.3-canary.1 (f92be71)

test/js/web/fetch/fetch-http2-client.test.ts:
(pass) fetch() over HTTP/2 (BUN_FEATURE_FLAG_EXPERIMENTAL_HTTP2_CLIENT) > concurrent requests multiplex on one h2 session [110.34ms]
(pass) fetch() over HTTP/2 (BUN_FEATURE_FLAG_EXPERIMENTAL_HTTP2_CLIENT) > gzip content-encoding is decompressed [115.04ms]
(pass) fetch() over HTTP/2 (BUN_FEATURE_FLAG_EXPERIMENTAL_HTTP2_CLIENT) > response trailers are consumed without breaking the body [99.30ms]
(pass) fetch() over HTTP/2 (BUN_FEATURE_FLAG_EXPERIMENTAL_HTTP2_CLIENT) > connection-specific request headers are stripped before HPACK [111.10ms]
(pass) fetch() over HTTP/2 (BUN_FEATURE_FLAG_EXPERIMENTAL_HTTP2_CLIENT) > cold-start: parallel requests coalesce onto one TLS connect [135.66ms]
(pass) fetch() over HTTP/2 (BUN_FEATURE_FLAG_EXPERIMENTAL_HTTP2_CLIENT) > response body larger than one DATA frame [147.00ms]
(pass) fetch() over HTTP/2 (BUN_FEATURE_FLAG_EXPERIMENTAL_HTTP2_CLIENT) > GET: status, headers and body round-trip [160.06ms]
(pass) fetch() over HTTP/2 (BUN_FEATURE_FLAG_EXPERIMENTAL_HTTP2_CLIENT) > keep-alive: sequential requests reuse one h2 session [139.51ms]
(pass) fetch() over H
... (truncated)
```

</details>

<details><summary>passes on PR (with fix)</summary>

```console
ASAN with fix: all passed
$ BUN_DEBUG_QUIET_LOGS=1 bun scripts/build.ts --profile=debug --quiet test "--reporter=junit" "--reporter-outfile=/tmp/pr_gate.xml" "test/js/web/fetch/fetch-http2-client.test.ts"
bun test v1.4.3 (b993710)

test/js/web/fetch/fetch-http2-client.test.ts:
(pass) fetch() over HTTP/2 (BUN_FEATURE_FLAG_EXPERIMENTAL_HTTP2_CLIENT) > GET: status, headers and body round-trip [1532.08ms]
(pass) fetch() over HTTP/2 (BUN_FEATURE_FLAG_EXPERIMENTAL_HTTP2_CLIENT) > response body larger than one DATA frame [1265.37ms]
(pass) fetch() over HTTP/2 (BUN_FEATURE_FLAG_EXPERIMENTAL_HTTP2_CLIENT) > POST: request body is delivered as DATA frames [1731.76ms]
(pass) fetch() over HTTP/2 (BUN_FEATURE_FLAG_EXPERIMENTAL_HTTP2_CLIENT) > POST with ReadableStream body streams as raw DATA frames [501.02ms]
(pass) fetch() over HTTP/2 (BUN_FEATURE_FLAG_EXPERIMENTAL_HTTP2_CLIENT) > gzip content-encoding is decompressed [1802.42ms]
(pass) fetch() over HTTP/2 (BUN_FEATURE_FLAG_EXPERIMENTAL_HTTP2_CLIENT) > concurrent requests multiplex on one h2 session [1743.84ms]
(pass) fetch() over HTTP/2 (BUN_FEATURE_FLAG_EXPERIMENTAL_HTTP2_CLIENT) > concurrent ReadableStream uploads route each chunk to its own stream 
... (truncated)

release with fix: 2 skipped
$ bun scripts/build.ts --profile=release
[configured] bun-profile → bun (stripped) in 710ms (unchanged)
ninja: Entering directory `/workspace/bun/build/release'
[0/5] cargo bun_runtime → libbun_runtime.a
�[1m�[92m   Compiling�[0m bun_core v0.0.0 (/workspace/bun/src/bun_core)
�[1m�[92m   Compiling�[0m bun_errno v0.0.0 (/workspace/bun/src/errno)
�[1m�[92m   Compiling�[0m bun_ptr v0.0.0 (/workspace/bun/src/ptr)
�[1m�[92m   Compiling�[0m bun_boringssl_sys v0.0.0 (/workspace/bun/src/boringssl_sys)
�[1m�[92m   Compiling�[0m bun_safety v0.0.0 (/workspace/bun/src/safety)
�[1m�[92m   Compiling�[0m bun_base64 v0.0.0 (/workspace/bun/src/base64)
�[1m�[92m   Compiling�[0m bun_cares_sys v0.0.0 (/workspace/bun/src/cares_sys)
�[1m�[92m   Compiling�[0m bun_zlib_sys v0.0.0 (/workspace/bun/src/zlib_sys)
�[1m�[92m   Compiling�[0m bun_zstd v0.0.0 (/workspace/bun/src/zstd)
�[1m�[92m   Compiling�[0m bun_picohttp v0.0.0 (/workspace/bun/src/picohttp)
�[1m�[92m   Compiling�[0m bun_brotli v0.0.0 (/workspace/bun/src/brotli)
�[1m�[92m   Compiling�[0m bun_output v0.0.0 (/workspace/bun/src/output)
�[1m�[92m   Compiling�[0m bun_clap v0.0.0 (/workspace/bun/src/clap)
�[1m�[92m   Compiling�[0m bu
... (truncated)
```

</details>

<details><summary>diff hotspot</summary>

```
src/http/h2_client/ClientSession.rs          |  12 ++-
 test/js/web/fetch/fetch-http2-client.test.ts | 118 ++++++++++++++++++++++++---
 2 files changed, 117 insertions(+), 13 deletions(-)
```

</details>

**gate history** · 1 passed · 1 rejected · iteration 1

<details><summary>evidence per changed file</summary>

```
file                                          reads  edits  tests
src/http/h2_client/ClientSession.rs               2      1     24
test/js/web/fetch/fetch-http2-client.test.ts      1      2     24
```

</details>

<!-- robobun:evidence:end -->
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