deps: update libarchive to v3.8.9 - #2
Open
github-actions[bot] wants to merge 1 commit into
Open
Conversation
github-actions
Bot
force-pushed
the
deps/update-libarchive
branch
from
March 15, 2026 03:58
ded2535 to
36c8c73
Compare
github-actions
Bot
force-pushed
the
deps/update-libarchive
branch
from
April 19, 2026 04:04
36c8c73 to
a592f88
Compare
igorls
pushed a commit
that referenced
this pull request
Apr 25, 2026
…-sh#29330) ## What Adds an early-return at the top of `ResumableSink.cancel()` when `status == .done`, so `onEnd` fires at most once. Fixes oven-sh#20740 Fixes oven-sh#21463 ## Why When a `fetch()` with a `ReadableStream` request body is aborted, `ResumableSink.cancel()` is called from `FetchTasklet.abortListener()` (FetchTasklet.zig:1203). The HTTP thread then completes with failure and `onProgressUpdate`'s reject path calls `sink.cancel()` a second time at FetchTasklet.zig:576 (and `onBodyReceived` at :342) — `this.sink` is only nulled in `clearSink←clearData←deinit`. `cancel()` (ResumableSink.zig:228) guarded against re-entry only for `status == .piped`. For the JS-route sink it relied on `#js_this.tryGet()` returning null after `detachJS()`, but `JSRef.downgrade()` (JSRef.zig:153-160) preserves the wrapper value as `.weak = <wrapper>`, and `tryGet()` (JSRef.zig:111) returns non-null for any non-empty weak. So the second `cancel()` re-enters the block and re-invokes `onEnd` → `FetchTasklet.writeEndRequest` → unconditional `defer this.deref()` (FetchTasklet.zig:1286). That second deref releases the single ref taken in `startRequestStream()` (FetchTasklet.zig:300) twice. Ref-count math: init(1) + queue(1) + startRequestStream(1) = 3 → cancel#1 deref → 2 → derefFromThread → 1 → cancel#2 deref → 0 → `deinit()`/`destroy()` runs *inside* `onProgressUpdate`, then its defer at :471-477 does `this.mutex.unlock()` + `this.deref()` on freed memory. `jsEnd()` already has an `isDetached()` guard for the same reason; `cancel()` was missing the equivalent. Using `status == .done` (rather than `isDetached()`) keeps the `.piped` branch reachable since piped sinks never set `#js_this` to `.strong`. ## Test `test/js/web/fetch/fetch-abort-stream-body.test.ts` reproduces the use-after-free in a debug/ASAN build: ``` [fetchtasklet] abortListener [fetchtasklet] writeEndRequest hasError? true <- cancel #1 [fetchtasklet] callback success=false ... [fetchtasklet] onProgressUpdate [fetchtasklet] onReject [fetchtasklet] writeEndRequest hasError? true <- cancel #2 (over-deref) [fetchtasklet] deinit ==40720==ERROR: AddressSanitizer: use-after-poison ... in onProgressUpdate ```
igorls
pushed a commit
that referenced
this pull request
Apr 25, 2026
…yToRoot (oven-sh#29483) Fuzzilli found a use-after-poison in the runtime auto-install path. `enqueueDependencyToRoot` passed `&lockfile.buffers.dependencies.items[dep_id]` into `enqueueDependencyWithMainAndSuccessFn`. When the manifest for the requested package is already cached (on disk or in memory) but the extracted tarball is not, control reaches `getOrPutResolvedPackageWithFindResult`, which calls `Lockfile.Package.fromNPM`. That grows `buffers.dependencies` via `ensureUnusedCapacity` to make room for the package's own dependencies, reallocating the backing storage. The subsequent `.extract` branch then read `dependency.behavior.isRequired()` from the freed buffer. ``` #0 getOrPutResolvedPackageWithFindResult PackageManagerEnqueue.zig:1520 dependency.behavior.isRequired() #1 getOrPutResolvedPackage PackageManagerEnqueue.zig:1778 #2 enqueueDependencyWithMainAndSuccessFn PackageManagerEnqueue.zig:523 #3 enqueueDependencyToRoot PackageManagerEnqueue.zig:321 #4 Resolver.enqueueDependencyToResolve resolver.zig:2356 ... oven-sh#14 Bun__resolveSync oven-sh#15 functionImportMeta__resolveSyncPrivate (runtime require() path) ``` Two changes: - `enqueueDependencyToRoot` now copies the `Dependency` to the stack before taking its address, matching every other caller of `enqueueDependencyWithMainAndSuccessFn` (`processDependencyListItem`, `processPeerDependencyList`, etc.). - The one read that ran after `fromNPM` now uses the `behavior` parameter that was already passed by value, instead of re-dereferencing `dependency`. Repro (debug/ASAN only): auto-install a package with a warm on-disk manifest but no extracted tarball — `fromNPM` appending even a single dependency forces a realloc of the one-entry buffer. The new test warms the cache, removes the extracted tarballs, and runs `require()` via `-e` so it goes through `Bun__resolveSync` → `enqueueDependencyToRoot`.
igorls
pushed a commit
that referenced
this pull request
Apr 25, 2026
`ResolveMessage.create` stored the `referrer` path via `Fs.Path.init`
without cloning. Every caller passes a temporary buffer — the `toUTF8()`
of a `bun.String` that is `deinit()`'d on return — so reading
`.referrer` after the creating frame unwound was a use-after-free.
Found by Fuzzilli as a flaky `use-after-poison` via `vi.mock()` →
`Bun__resolveSyncWithSource` → `resolveMaybeNeedsTrailingSlash`, but it
reproduces deterministically under ASAN with any non-ASCII source path:
```js
let err;
try {
Bun.resolveSync("./does-not-exist", "/tmp/café-🎉/file.js");
} catch (e) { err = e; }
Bun.gc(true);
err.referrer; // use-after-poison
```
```
==3080==ERROR: AddressSanitizer: use-after-poison on address 0x77cca9db0000 ...
READ of size 44 at 0x77cca9db0000 thread T0
#0 in __asan_memcpy
#1 in Zig::toStringCopy(ZigString) helpers.h:217
#2 in ZigString__toValueGC bindings.cpp:3402
#3 in ZigString.toJS ZigString.zig:57
#4 in ResolveMessage.getReferrer ResolveMessage.zig:221
```
In release builds the first 8 bytes of the returned referrer are
overwritten by mimalloc's free-list pointer instead of crashing.
Clone the referrer in `create()` and free it in `finalize()`. Also
`deinit()` the `toUTF8()` temporaries in `processFetchLog` now that
`create()` copies.
Co-authored-by: robobun <robobun@users.noreply.github.com>
igorls
pushed a commit
that referenced
this pull request
May 6, 2026
…en-sh#29910) ## What `Blob.dupeWithContentType` guarded its content_type handling on `duped.isHeapAllocated()` immediately *after* calling `duped.setNotHeapAllocated()`, so both branches were dead. When the source Blob's `content_type` is heap-allocated, the bitwise-copied dupe aliased the same allocation while both sides had `content_type_allocated == true`. This is a regression from oven-sh#23015: the pre-refactor code checked `duped.allocator != null` *before* clearing it at the end of the function; the refactor moved the clear to the top but left the (renamed) guard in place. ## Repro ```js const file = Bun.file(path, { type: "application/x-custom-type-not-in-registry-abcdefghijklm" }); const response = new Response(file); // body holds a dupe that aliases file.content_type await file.write("hello", { type: "application/x-..." }); // frees file.content_type response.headers.get("content-type"); // reads freed memory ``` On ASAN builds: ``` ==716==ERROR: AddressSanitizer: use-after-poison on address 0x71df454301c0 #1 in Zig::toStringCopy(ZigString) helpers.h:217 #2 in WebCore__FetchHeaders__put bindings.cpp:2082 #5 in bun.js.webcore.Response.getOrCreateHeaders Response.zig:358 ``` On release builds the freed slot gets reused and the read produces garbage: ``` TypeError: Header '25' has invalid value: 'ion/x-custom-type-not-in-registry-abcdefghijklm' ``` ## Fix Drop the `isHeapAllocated()` guard and always deep-copy an allocated `content_type` in `dupeWithContentType`. The old `!include_content_type` branch's "resolve to static mime or fall back to empty" is gone — it would have dropped FormData's `multipart/form-data; boundary=...` (and any non-registry type) on `Response.clone()`, and the branch itself was marked `// TODO: fix this / this is a bug`. The `include_content_type` parameter is now a no-op. Since every dupe now owns its `content_type` copy, `Blob.deinit()` frees it. That in turn required closing a few places that held a bitwise-copied Blob alongside the live owner: - `fromJSWithoutDeferGC` `move=true`: deep-copy `name`/`content_type` into the moved-out value so the source JS Blob keeps sole ownership; the BuildArtifact arm now `dupe()`s (its "move" only nulled the store on a local copy). - `getSliceFrom()`: free the dupe's copy before overwriting it with the slice's own type. - `doWrite`/`getWriter`: clear `content_type_allocated` after the in-place free so a registry-resolved static string isn't later freed by `deinit()`. - `BlobOrStringOrBuffer.deinitAndUnprotect`: only deref the store (matching its `deinit()`) since `.blob` is a raw view of a live JS Blob. ## Verified - `bun bd test test/js/web/fetch/blob.test.ts` — 16/16 pass - New UAF test fails on both debug/ASAN (use-after-poison) and system bun (garbage header) without the fix, passes with it - New clone test guards against dropping FormData's boundary on `Response.clone()` - ASAN stress: 1k× `Response.clone`/`blob.slice`/`createObjectURL`+revoke/`new Response([blob])`/`write({type})` — no double-free - RSS is flat across 50k × 1KB-type `Response.clone()` and `blob.slice()` --------- Co-authored-by: robobun <robobun@users.noreply.github.com> Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
igorls
pushed a commit
that referenced
this pull request
May 6, 2026
Closes oven-sh#29925 Closes oven-sh#22808 Closes oven-sh#24019 ## What does this PR do? Fixes a bug where `Bun.RedisClient` would permanently reject every command with `Connection has failed` after the client entered the failed state (via reconnect exhaustion, manual `close()`, or a fatal socket error). Calling `client.connect()` did not recover the client — the process had to be restarted. ## Root cause Pre-refactor (before oven-sh#23141) `.failed` was a connection status and `doConnect` handled it explicitly: ```zig .failed => { this.client.flags.is_reconnecting = true; this.client.retry_attempts = 0; this.reconnect(); }, ``` The refactor folded `.failed` into `.disconnected` and a new `flags.failed` boolean but never wired up the reset path. Two things stayed sticky: 1. **`flags.failed`** — `send()` short-circuits on this and immediately rejects with `Connection has failed`. Once set in `failWithJSValue`, nothing ever cleared it. 2. **`flags.is_authenticated`** — kept `true` from the prior successful session, so when the new socket's HELLO response arrived, `handleResponse` skipped `handleHelloResponse` (which is guarded by `if (!this.flags.is_authenticated)`) and silently discarded the response. The client never transitioned back to `.connected` and `connect()` would hang until the connection timeout fired. ## Fix Two small resets: - `doConnect` (src/valkey/js_valkey.zig) clears `flags.failed` alongside `is_manually_closed` so an explicit `connect()` hands the client a clean slate. - `onOpen` (src/valkey/valkey.zig) clears `flags.failed`, `is_authenticated`, and `is_selecting_db_internal` so a fresh socket properly replays the HELLO handshake — matching what `onClose`'s auto-reconnect branch already does at L502–504. ## Verification New regression test at `test/regression/issue/29925.test.ts` spawns a local `redis-server` on a random port, drives the client into the failed state via `close()` (same terminal state as max-retries exhaustion), then asserts `connect()` recovers the client and subsequent commands complete round-trip. Gate check confirms the test times out without the fix. Also manually verified: - oven-sh#22808: tight `close()` + `connect()` + `send("FLUSHALL", ["SYNC"])` loop that previously locked up on iter 1 now runs cleanly across many iterations. - oven-sh#24019: after max-retries exhaustion during a redis restart, `client.connect()` recovers the client instead of returning `connected: true` while the next command still rejects. Reproduction from oven-sh#29925: ``` $ bun /tmp/repro.ts first set: Max reconnection attempts reached subsequent #0: Connection has failed ← forever subsequent #1: Connection has failed subsequent #2: Connection has failed ``` After the fix, `await client.connect()` brings the client back online and the next `set`/`get` pair succeeds. --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: robobun <robobun@users.noreply.github.com>
igorls
pushed a commit
that referenced
this pull request
May 6, 2026
…double-free in deinit (oven-sh#29988) ## Repro Dev server with a directory watch that has two pending resolution-failure dependencies (`./sub/a` at index 0, `./sub/b` at index 1). Create `sub/a.ts` so dep 0 resolves; because it is not the tail slot, `freeDependencyIndex(0)` pushes index 0 onto `dependencies_free_list`. Shut the server down. ``` ==ERROR: AddressSanitizer: negative-size-param: (size=-6148914691236517206) #1 mem.Allocator.free #2 bake.DevServer.deinit /workspace/bun/src/bake/DevServer.zig:686 #3 bun.js.api.server.NewServer(.http,.debug).deinitIfWeCan Address 0xaaaaaaaaaaaaaaaa is a wild pointer ``` ## Cause `DirectoryWatchStore.freeDependencyIndex` frees `dep.specifier` and (in debug) sets the whole slot to `undefined`, then pushes the index onto `dependencies_free_list`. The slot stays in `dependencies.items`. `DevServer.deinit` iterates every `dependencies.items` slot and calls `alloc.free(watcher.specifier)` without consulting the free list, so free-list slots are freed a second time. In debug builds the `undefined` (0xAA…) slice trips ASAN's negative-size check; in release it is a straight double-free. `memoryCost` has the same blind iteration and would read `.len` from freed memory. ## Fix After freeing, write an empty slice back into `specifier` so the slot is safe to revisit: `alloc.free(&.{})` is a no-op and `.len == 0`. ## Verification New test `deinit with a free-list slot in DirectoryWatchStore.dependencies` in `test/bake/dev/bundle.test.ts` arranges the free-list slot and lets the harness's graceful-exit call `deinit`. - `git stash -- src/ && bun bd test … -t 'deinit with a free-list slot'` → 3/3 **fail** (ASAN abort at DevServer.zig:686) - with fix → 3/3 **pass** - adjacent `removing 'use client' from a component with a pending resolution failure` test still passes --------- Co-authored-by: robobun <robobun@users.noreply.github.com>
igorls
pushed a commit
that referenced
this pull request
May 6, 2026
…ven-sh#29971) ## What `FileSystemRouter`'s constructor (and `reload()`) initialize the error log with the arena allocator: ```zig const allocator = arena.allocator(); ... var log = Log.Log.init(allocator); ``` When route loading produces errors, the error paths did: ```zig arena.deinit(); globalThis.allocator().destroy(arena); return globalThis.throwValue(try log.toJS(...)); // reads arena-backed msgs.items ``` `log.msgs.items` is backed by the arena, so `log.toJS()` reads freed memory. ASAN reports `use-after-poison` in `logger.Log.toJS`. ## Repro ```js // pages/[foo.tsx — missing closing bracket new Bun.FileSystemRouter({ style: "nextjs", dir: "./pages", fileExtensions: [".tsx"] }); ``` Debug (ASAN) build: ``` AddressSanitizer: use-after-poison ... #1 in logger.Log.toJS (src/logger.zig:733) #2 in FileSystemRouter.constructor (src/bun.js/api/filesystem_router.zig:149) ``` ## Fix Build the JS error value first (while the arena is still live — `BuildMessage.create` / `ResolveMessage.create` clone the msg into `globalThis.allocator()`), then free the arena, then throw. Applied to all four `log.toJS()` call sites across `constructor()` and `reload()`. ## Verification - `git stash -- src/ && bun bd test filesystem_router.test.ts -t 'invalid route'` → **fail** (ASAN crash in subprocess) - `git stash pop && bun bd test filesystem_router.test.ts -t 'invalid route'` → **pass**, error message is `Route is missing a closing bracket]` - All 19 existing `filesystem_router.test.ts` tests pass. --------- Co-authored-by: robobun <robobun@users.noreply.github.com>
igorls
pushed a commit
that referenced
this pull request
May 6, 2026
…es (oven-sh#30077) ## What When a chunked (or HTTP/3) request body exceeds `maxRequestBodySize`, `onBufferedBodyChunk` writes the 413 directly on the raw uWS response: ```zig resp.writeStatus("413 Payload Too Large"); resp.endWithoutBody(comptime !http3); ``` `internalEnd` → `markDone()` nulls `onAborted`, so when the socket closes no abort ever fires to detach `ctx.resp` or release the base ref. `this.resp` is left pointing at a completed response whose socket is about to be freed by `us_internal_free_closed_sockets`. If the fetch handler returned a pending Promise: - **resolve**: `handleResolve` → `isAbortedOrEnded()` is false (`this.resp != null`) → `render()` → `runCorkedWithType` corks the freed socket → **heap-use-after-free** (ASAN trace below). - **reject**: `handleReject` reads `resp.hasResponded()` off freed memory, sees `true`, skips the error handler, and returns without ever releasing the base ref → **RequestContext leaks** (`server.pendingRequests` never returns to 0). ## Fix Route through `this.endWithoutBody()` (the `RequestContext` wrapper) instead of the raw `resp.endWithoutBody()`. That path does `detachResponse()` (nulls `this.resp`, clears `onData`/`onAborted`/`onTimeout`) and `deref()` (releases the base ref), matching every other end path in this file. The body promise is rejected with the specific `"Request body exceeded maxRequestBodySize"` error *before* `endWithoutBody()` so `endRequestStreaming()` doesn't overwrite it with a generic `ConnectionClosed`. `has_written_status` is set so any later `renderMissing`/`renderMetadata` knows the status line is already committed. ## Repro ``` ==ERROR: AddressSanitizer: heap-use-after-free #0 us_socket_group socket.c:77 #1 uWS::AsyncSocket<false>::getLoopData() AsyncSocket.h:69 #2 uWS::AsyncSocket<false>::isCorked() AsyncSocket.h:141 #3 uWS::HttpResponse<false>::cork(...) HttpResponse.h:647 #4 uws_res_cork libuwsockets.cpp:1740 #5 ...runCorkedWithType Response.zig:299 #6 ...doRenderBlob RequestContext.zig:1942 ... oven-sh#11 ...handleResolve RequestContext.zig:220 oven-sh#12 ...onResolve RequestContext.zig:154 freed by: #1 us_poll_free epoll_kqueue.c:73 #2 us_internal_free_closed_sockets loop.c:305 ``` ## Test `test/js/bun/http/serve-pending-promise-abort-leak.test.ts` — new case sends a raw `Transfer-Encoding: chunked` POST exceeding `maxRequestBodySize` with a handler that holds its resolve/reject, waits for the socket to be reclaimed, then settles the Promise. Asserts `pendingRequests` returns to 0 for both paths, the body was rejected with the right message, and a follow-up request still works. Without the fix: ASAN heap-use-after-free on the resolve path; on release builds the reject path shows `pendingAfterReject: 1` (leak). Co-authored-by: robobun <robobun@users.noreply.github.com>
igorls
pushed a commit
that referenced
this pull request
May 6, 2026
…to prevent UAF (oven-sh#30057) ## What `UDPSocket.sendMany()` and `UDPSocket.send()` both captured raw pointers into the payload's ArrayBuffer backing store (or borrowed `WTFStringImpl` storage for Latin-1 strings) and then hit JSC safepoints before handing those pointers to `bsd_sendmmsg`: - **`sendMany`**: subsequent loop iterations call `iter.next()` (slow path → `JSObject.getIndex`), `coerceToInt32` on the port, and `toBunString` on the address - **`send`**: `parseAddr` calls `coerceToInt32` on the port and `toBunString` on the address after the payload is captured Any of these can run user JS that detaches an earlier payload's ArrayBuffer via `.transfer(newLen)` (which synchronously frees the old backing store) or drops the last reference to a JSString, leaving the captured pointer dangling. ## Repro ```js const buf = new ArrayBuffer(4096); const payload = new Uint8Array(buf); const evilPort = { valueOf() { buf.transfer(0); // synchronously frees the 4096-byte backing store return server.port; }, }; client.sendMany([payload, evilPort, "127.0.0.1"]); // or client.send(payload, evilPort, "127.0.0.1") // bsd_sendmmsg reads 4096 bytes from the freed region ``` Under ASAN (with `Malloc=1` so bmalloc routes through the system heap): ``` ==…==ERROR: AddressSanitizer: heap-use-after-free on address … at pc … READ of size 4096 at … thread T0 #0 … in read_iovec(…) #2 … in sendmmsg #3 … in bsd_sendmmsg packages/bun-usockets/src/bsd.c:123 freed by thread T0 here: … oven-sh#14 … in JSC::arrayBufferCopyAndDetach(…) JSArrayBufferPrototype.cpp:365 … oven-sh#30 … in JSC::JSValue::toInt32(…) ← parseAddr's coerceToInt32 ``` ## Fix - **`sendMany`**: root every payload JSValue in a `MarkedArgumentBuffer` for the duration of the call and split the loop into two phases. Phase 1 collects/validates payload JSValues and runs all user-JS re-entrance (`iter.next`, `parseAddr`). Phase 2 borrows byte slices from the rooted JSValues once no more user JS sits between capture and `socket.send`. GC cannot collect a rooted payload; an ArrayBuffer that was detached during phase 1 reports a zero-length slice instead of a dangling pointer. No payload bytes are copied. - **`send`**: reorder so `parseAddr` runs before the payload pointer is captured. `payload_arg` stays rooted in the callframe, and nothing between capture and `socket.send` hits a JSC safepoint — so no copy is needed. ## Verification - **Without fix:** `bun bd test test/js/bun/udp/udp_socket.test.ts -t 'detaching an ArrayBuffer'` → ASAN heap-use-after-free in `read_iovec` → `bsd_sendmmsg` for both `send` and `sendMany`, tests fail - **With fix:** both tests pass; received bytes match the original payload - Full `test/js/bun/udp/` suite (207 tests) passes - `zig:check-all` passes on all targets --------- Co-authored-by: robobun <robobun@users.noreply.github.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
igorls
pushed a commit
that referenced
this pull request
May 6, 2026
## Problem
`MarkedArrayBuffer.destroy()` did two things:
```zig
allocator.free(content.buffer.slice()); // free the bytes
allocator.destroy(this); // free *this
```
Every constructor that is actually used (`fromString`, `fromBytes`,
`fromJS`, `fromTypedArray`, `fromArrayBuffer`) returns
`MarkedArrayBuffer` **by value**, so `this` is never an individually
heap-allocated struct — it's a stack local, an embedded field, or an
ArrayList slot. The `allocator.destroy(this)` call passes that interior
pointer to mimalloc.
In the readdir Buffer error-cleanup path (`readdirWithEntries` /
`readdirInner`), entries are appended by value via
`Buffer.fromString()`:
- `allocator.destroy(&entries.items[0])` frees `entries.items.ptr`
- the next loop iteration reads `this.*` from poisoned memory
- `entries.deinit()` frees the same pointer again
## Repro
```js
const fs = require('fs');
// dir contains regular files + a self-referential symlink 'loop -> loop'
fs.readdirSync(dir, { encoding: 'buffer', recursive: true });
```
The recursive walk collects Buffer entries for the root, then fails with
`ELOOP` opening the symlink (not in the swallowed `NOENT/NOTDIR/PERM`
set), and enters the cleanup loop. Under ASAN:
```
==3593==ERROR: AddressSanitizer: use-after-poison on address 0x737ec6e50040
READ of size 64 at 0x737ec6e50040 thread T0
#1 MarkedArrayBuffer.destroy array_buffer.zig:591
#2 NodeFS.readdirInner node_fs.zig:5013
#3 NodeFS.readdir node_fs.zig:4518
```
## Fix
- Drop `allocator.destroy(this)` from `MarkedArrayBuffer.destroy()`. The
struct is passed/stored by value; callers own its storage.
- Remove the unused `MarkedArrayBuffer.init()` (the only function that
heap-allocated the struct, zero callers) so there's no pairing that
would leak.
- The readdir call sites keep calling `.destroy()`, which still checks
`this.allocator` before freeing bytes — JS-owned buffers remain
untouched.
Also fixed the adjacent `Dirent` arm of the recursive-sync error
cleanup: `result.name.deref()` → `result.deref()` so `Dirent.path` is
released too (matching the non-recursive and async cleanup sites).
## Verification
New test in `test/js/node/fs/fs.test.ts` creates a temp dir with files +
a self-referential symlink, spawns a subprocess that calls
`readdirSync({encoding:'buffer', recursive:true})`, and asserts it
throws `ELOOP` and exits 0.
```
# without fix
(fail) readdirSync({encoding: 'buffer', recursive: true}) frees entries safely ...
{ exitCode: 134, stdout: "" } # SIGABRT from ASAN
# with fix
(pass) readdirSync({encoding: 'buffer', recursive: true}) frees entries safely ... [1.5s]
{ exitCode: 0, stdout: "ELOOP" }
```
`zig:check-all` passes on all targets.
---------
Co-authored-by: robobun <robobun@users.noreply.github.com>
igorls
pushed a commit
that referenced
this pull request
May 6, 2026
…ss (oven-sh#30181) ## What Surveyed ~40 recent CI builds for bake test flakes and fixed the underlying causes. ### Flake #1 (93 hits): `dev-and-prod-12: hmr handles rapid consecutive edits` Two modes: - **Windows**: `Bun.write` is `open(O_TRUNC)` then async write with a JS-thread round-trip in between. The watcher fires on the 0-byte truncation, bundles an empty module that never calls `accept()`, and the next update falls through to `fullReload()` → client exits `unexpectedReload`. - **All platforms**: after the final drain `client.messages.length = 0`, a late hot_update lands during the following `await client.js\`…\`` and trips the unread-messages disposal check. **Fix (test):** use `fs.writeFileSync` for the rapid burst (microsecond truncate window), write identical content so same-`sourceMapId` duplicates are deterministic on every platform, and follow with a synchronized sentinel write — once the sentinel arrives over the ordered WS, every prior hot_update has been applied and nothing can leak into disposal. ### Flake #2 (29 hits): `Timeout waiting for line "… socket connected"` Across `react-spa`, `html`, `ssg-pages-router`, `bundle`, `hot`, `esm`, `css`, `incremental-graph-edge-deletion`. `waitForLine()`'s default timeout was **1 second** on non-Windows release builds — the Node client has to start, import happy-dom, fetch, parse HTML, run the bundle, and open a WebSocket in that window. The `ASAN_TIMEOUT_MULTIPLIER` constant existed but was never applied. **Fix (harness):** raise the base and apply a unified `WAIT_MULTIPLIER` (debug × ASAN × CI). Apply the same multiplier to `expectMessage` / `expectReload` / `getStringMessage` / `getMostRecentHmrChunk` (all hardcoded 1000 ms), and raise the per-test base accordingly. Also make `waitForLine` scan already-buffered lines via the previously-dead `cursor` field so an `await` between stream creation and the call can't drop the match. ### Underlying DevServer bugs found while stress-testing - **`IncrementalGraph.invalidate` use-after-poison**: the incoming `path` (a slice into `HotReloadEvent.extra_files`) was stored in `entry_points`, but the event is reset — and its `extra_files` may be reallocated by the watcher thread — before `entry_points` is consumed by `startAsyncBundle` / `TestingBatch`. Since `getIndex(path)` already succeeded, store the graph-owned `keys[index]` instead. - **`TestingBatch.append`** stored the same borrowed slices as persistent keys across multiple `HotReloadEvent.run` calls. Dupe keys on insert; free them in `TestingBatch.deinit`. - **`onFileUpdate` (Linux)** indexed only `changed_files[event.name_off]` for a merged directory `WatchEvent`. When an atomic-save editor (vim/emacs/IntelliJ) lands `CREATE tmp` + `MOVED_TO target` in one coalesced inotify batch, the rename target was dropped and never re-watched. Forward every name via `event.names()`. ### Harness robustness - `waitForHotReload` used `clientWaits === connectedClients.size`; straggler HMR events from prior unsynchronized writes could push the count past, so it never matched. Use `>=`. - `waitForHotReload` now rejects on dev-server panic instead of hanging to the test timeout. - Detect `AddressSanitizer` / `ThreadSanitizer` / `==ABORTING` in subprocess output as a panic. ## How verified - New `hot.test.ts` case floods a watched directory (32 decoy creates + unlink + rename-over) to force inotify coalescing: - **without** `src/` changes → 3/3 fail under ASAN (use-after-poison in `TestingBatch.append` via `wyhash`) - **with** `src/` changes → 10/10 pass - `dev-and-prod.test.ts -t "rapid consecutive edits"` → 10/10 pass - Full runs of `hot`, `dev-and-prod`, `bundle`, `css`, `html`, `esm`, `stress`, `ssg-pages-router`, `incremental-graph-edge-deletion`, `plugins`, `sourcemap`, `server-sourcemap`, `vfile`, `framework-router`, `deinitialization` → all green (esm-11 is a pre-existing `skip: ["ci"]`) - `zig:check-all` passes on all targets Supersedes oven-sh#29575 and oven-sh#28211. Fixes oven-sh#19732 --------- Co-authored-by: robobun <robobun@users.noreply.github.com>
igorls
pushed a commit
that referenced
this pull request
May 6, 2026
…en-sh#30196) ## What does this PR do? Fixes a use-after-free in `HTMLRewriter.transform()` that caused flaky SIGSEGV crashes found by fuzzing. When transforming a string or ArrayBuffer, the body is buffered synchronously and fed to lol-html via `write()` followed by `end()`. If a document/element handler returns a rejected promise for the final `lastInTextNode` chunk (emitted from `end()`), the `end() catch` branch in `BufferOutputSink.runOutputSink` would call `response.finalize()` directly on the output `Response`. That `Response` is already owned by its JS wrapper cell (created earlier in `init()` via `sink.response.toJS()`), so destroying it in-place left the wrapper's `m_ctx` pointing at freed memory. When GC later swept the wrapper, its destructor invoked `Response.finalize()` again on that freed pointer: ``` AddressSanitizer: use-after-poison #0 bun.js.bindings.JSRef.JSRef.deinit src/bun.js/bindings/JSRef.zig:188 #1 bun.js.bindings.JSRef.JSRef.finalize src/bun.js/bindings/JSRef.zig:200 #2 bun.js.webcore.Response.finalize src/bun.js/webcore/Response.zig:474 #3 ResponseClass__finalize codegen/ZigGeneratedClasses.zig:17250 #4 WebCore::JSResponse::~JSResponse() codegen/ZigGeneratedClasses.cpp:54979 ``` The `write()` error path (just above it) already handled this correctly by returning the error and letting the JS wrapper own the Response lifetime. This PR makes the `end()` error path do the same — drop the manual `response.finalize()` and `sink.response = undefined`. ## How did you verify your code works? Minimal repro that reliably triggers the ASAN error before the fix and passes cleanly after: ```js const rewriter = new HTMLRewriter(); rewriter.onDocument({ text(chunk) { if (chunk.lastInTextNode) { return Promise.reject(new Error("boom")); } }, }); try { rewriter.transform(new Uint8Array([97, 98, 99]).buffer); } catch (e) {} Bun.gc(true); ``` Added regression tests in `test/js/workerd/html-rewriter.test.js` covering both ArrayBuffer and string inputs. All existing HTMLRewriter tests pass. --------- Co-authored-by: robobun <robobun@users.noreply.github.com>
igorls
pushed a commit
that referenced
this pull request
May 6, 2026
…0174) ## What `RequestContext` stored `response_ptr: ?*Response` and, for plain `Blob`/`InternalBlob`/`WTFStringImpl` bodies, left the Response JSValue unprotected. `renderBytes()` → `tryEnd()` can hit backpressure and register an `onWritable` callback, unwinding with `response_ptr` still set. Nothing rooted the Response (`RequestContext` is a pool struct, not GC-visited), so GC could finalize it. If the client then aborted while the request body was still `.Locked`, `onAbort()` dereferenced a freed `*Response` — heap-use-after-free under ASAN at `RequestContext.zig:692`. ## Repro ``` POST → handler returns new Response(8MB string) sync → tryEnd() backpressure (client paused) → onWritable registered, return → Bun.gc(true) → Response collected, response_ptr dangles → client.destroy() → onAbort → deref response_ptr → UAF ``` ASAN trace (unpatched): ``` ==ERROR: AddressSanitizer: use-after-poison #0 bun.js.bindings.JSRef.JSRef.tryGet #1 bun.js.webcore.Response.getBodyReadableStream #2 RequestContext.onAbort src/bun.js/api/server/RequestContext.zig:693 #3 uWS::HttpContext<false>::onClose ``` ## Fix Give `Response` a `weak_ptr_data` field (mirroring `Request.WeakRef`) and replace `response_ptr: ?*Response` with `response_weakref: Response.WeakRef` via `bun.ptr.WeakPtr`. `Response.destroy()` now defers freeing the allocation until outstanding weak refs drop; `WeakRef.get()` returns null once the contents are gone. `onAbort` / `handleResolveStream` / `handleRejectStream` call `.get()` and simply skip the readable-stream cleanup when it's null — a no-op for in-memory bodies anyway, since the body was already extracted via `useAsAnyBlobAllowNonUTF8String()` before backpressure. File-backed and `.Locked` bodies continue to `protect()` `response_jsvalue` as before; those paths need the Response's status/headers alive across the async hop for `renderMetadata()`. The hot path (small in-memory responses) no longer needs `protect()`/`unprotect()`. The two redundant `ctx.response_ptr = response` assignments right before `ctx.render(response)` are dropped — `render()` already sets the weak ref. ## Verification `test/js/bun/http/serve-response-gc-backpressure-abort.test.ts` (ASAN/debug-only): POST with incomplete chunked body so `request_body` stays `.Locked`, handler returns a large string Response, client pauses so `tryEnd()` stalls, `Bun.gc(true)` loop, then client closes. - **without fix**: `AddressSanitizer: use-after-poison` in `onAbort` → `Response.getBodyReadableStream` - **with fix**: passes, `abortCount === iterations`, `pendingRequests === 0` --------- Co-authored-by: robobun <robobun@users.noreply.github.com>
igorls
pushed a commit
that referenced
this pull request
May 6, 2026
…before write (oven-sh#30155) ## Repro ```js Bun.serve({ port: 0, fetch: () => new Response("hello", { headers: [ ["Transfer-Encoding", "gzip"], ["Transfer-Encoding", "chunked"], ], }), }); // HEAD / → ASAN heap-use-after-free in uWS::HttpResponse::writeHeader ``` The duplicate entries make `FetchHeaders` combine them via `makeString()`, producing a `StringImpl` held only by the header map — the minimal condition for the free to actually happen. StringImpl is allocated via bmalloc which ASAN doesn't instrument by default; with `Malloc=1` (bmalloc → system heap) the debug build reports: ``` AddressSanitizer: heap-use-after-free READ of size 13 #2 uWS::HttpResponse<false>::writeHeader #5 doRenderHeadResponse RequestContext.zig:1378 freed by: oven-sh#23 HTTPHeaderMap::remove oven-sh#28 doWriteHeaders RequestContext.zig:2303 oven-sh#29 renderMetadata RequestContext.zig:2209 oven-sh#30 doRenderHeadResponse RequestContext.zig:1377 ``` ## Cause `doRenderHeadResponse()` calls `headers.fastGet(.TransferEncoding)`, which returns a `ZigString` that **borrows** the header map entry's `StringImpl` bytes (no ref taken). For an ASCII value, `toSlice()` also borrows rather than copying. It then calls `this.renderMetadata()`, whose `doWriteHeaders()` does `headers.fastRemove(.TransferEncoding)` (and `renderMetadata` also `swapInitHeaders()` + `deref()`s the whole `FetchHeaders`). When the map held the only reference to the `StringImpl`, it's destroyed right there — and the very next line `resp.writeHeader("transfer-encoding", transfer_encoding_str.slice())` writes the freed bytes to the socket. The adjacent `Content-Length` branch has the same bug: `std.fmt.parseInt()` runs on the borrowed slice *after* `renderMetadata()` has already `fastRemove(.ContentLength)`'d it. ## Fix - **Transfer-Encoding**: use `toSliceClone()` instead of `toSlice()` so the value is owned and survives `renderMetadata()`. - **Content-Length**: parse the integer *before* `renderMetadata()` (and drop the slice immediately), so the borrowed bytes are never touched after the header entry is removed. No extra allocation needed since only the parsed `usize` is used afterwards. ## Verification New test in `test/js/bun/http/bun-server.test.ts` (inside the existing `HEAD requests oven-sh#15355` block) spawns a subprocess with `Malloc=1` (non-Windows), serves HEAD responses whose Transfer-Encoding / Content-Length values are `makeString()`-combined (sole-owner StringImpl), and asserts the raw wire output. ``` git stash push -- src/ → test fails with "AddressSanitizer: heap-use-after-free" in stderr git stash pop → test passes ``` All other tests in the `HEAD requests oven-sh#15355` describe block continue to pass. Co-authored-by: robobun <robobun@users.noreply.github.com>
igorls
pushed a commit
that referenced
this pull request
May 31, 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`) - oven-sh#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 - oven-sh#10 — Invalid lockfile tag causes panic DoS — replace `unreachable!()` with logged error + `Tag::Uninitialized` (`dependency.rs`) - oven-sh#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`) - oven-sh#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 - oven-sh#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 - oven-sh#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`) - oven-sh#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`) - oven-sh#23 — Process umask cleared and never restored — restore umask after probing it in `ensure_umask` (`bin.rs`) ### Parser correctness - oven-sh#22 — Sign-prefixed scalar misparsed as infinity — fix Zig→Rust `&&`/`||` precedence transliteration (`yaml.rs`)
igorls
pushed a commit
that referenced
this pull request
May 31, 2026
… stack pointer (oven-sh#31020) ## What `resolveMaybeNeedsTrailingSlash` swaps `vm.log` / `resolver.log` to a stack-local `Log` for the duration of `_resolve`, then restores them via a drop guard. The Zig original also swaps and restores `transpiler.linker.log` and `resolver.package_manager.log`; the Rust port had those behind a `TODO(b2-cycle)` and only handled `vm.log` + `resolver.log`. When auto-install is enabled and the resolver lazily creates the `PackageManager` during `_resolve`, `Resolver::get_package_manager` seeds `pm.log` from `resolver.log` — which at that point is the **stack-local** `Log`. Because the restore guard never touched `pm.log`, it was left pointing into a dead stack frame after the function returned. The next resolve at a different stack depth that routes through the auto-install task runner dereferenced that stale pointer in `Log::add_error_fmt`, tripping ASAN's `stack-use-after-scope` (or segfaulting / executing garbage in release builds). Stack at the fault: ``` #0 bun_ast::Log::add_formatted_msg #1 bun_ast::Log::add_error_fmt #2 bun_install::…::run_tasks #7 bun_install::…::enqueue_dependency_to_root #9 bun_resolver::Resolver::enqueue_dependency_to_resolve oven-sh#14 bun_resolver::Resolver::resolve_and_auto_install oven-sh#15 bun_jsc::VirtualMachine::_resolve oven-sh#16 bun_jsc::VirtualMachine::resolve_maybe_needs_trailing_slash::<true> ``` ## Fix Swap and restore `linker.log` and (when present) `package_manager.log` in both copies of the resolve log guard (`VirtualMachine::resolve_maybe_needs_trailing_slash` and `jsc_hooks::resolve_hook`), matching `VirtualMachine.zig`. The restore re-checks `resolver.package_manager` at drop time so a PM that was lazily created during `_resolve` is also pointed back at the VM log. Also adds the missing `<cassert>` include in `wtf-bindings.cpp`, which stopped being pulled in transitively. ## Repro ```js // run from an empty dir with // BUN_CONFIG_INSTALL=fallback BUN_CONFIG_REGISTRY=http://127.0.0.1:1 const realm = new ShadowRealm(); const variants = [ () => realm.importValue("pkg-not-found-a", "x"), () => (() => realm.importValue("pkg-not-found-b", "x"))(), () => (() => (() => realm.importValue("pkg-not-found-c", "x"))())(), () => import("pkg-not-found-f"), ]; for (let i = 0; i < 100; i++) for (const v of variants) try { v()?.catch?.(() => {}); } catch {} ``` Segfaults on `main`, clean after this change. Fixes oven-sh#14432 Fixes oven-sh#22407 --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
igorls
pushed a commit
that referenced
this pull request
May 31, 2026
…s longer than the comparand (oven-sh#31264) ### What does this PR do? Fixes an ASAN `global-buffer-overflow` found by fuzzing the CSS parser: ``` asan:global-buffer-overflow:strncasecmp|eql_case_insensitive_ascii|eql_case_insensitive_ascii|bun_core::string::immutable::eql_case_insensitive_ascii_ignore_length ``` **Repro** ```sh BUN_FEATURE_FLAG_INTERNAL_FOR_TESTING=1 bun -e 'require("bun:internal-for-testing").cssInternals.minifyTest(":nth-child(Nn", "")' ``` ``` ==ERROR: AddressSanitizer: global-buffer-overflow READ of size 2 ... #0 strncasecmp #1 bun_core::strings_impl::eql_case_insensitive_ascii src/bun_core/lib.rs #2 bun_core::string::immutable::eql_case_insensitive_ascii_ignore_length src/bun_core/string/immutable.rs #3 bun_css::css_parser::nth::parse_nth src/css/css_parser.rs #4 bun_css::selectors::parser::parse_nth_pseudo_class src/css/selectors/parser.rs ``` **Cause** `strings_impl::eql_case_insensitive_ascii(a, b, check_len)` defers to `strncasecmp(a, b, a.len())`, which reads up to `a.len()` bytes from *both* buffers. The Zig original (`strings.eqlCaseInsensitiveASCII`) compared against NUL-terminated comptime literals, so `strncasecmp` stopped at the sentinel and reported a mismatch whenever `a` was longer than `b`. Rust byte-string literals carry no terminator, so the An+B parser's ident branch (`parse_nth`), which compares an arbitrary user ident against the keywords `"even" / "odd" / "n" / "-n" / "n-" / "-n-"` with the ignore-length variant, reads past the end of the keyword literal as soon as the ident is longer than the keyword and shares its prefix (`Nn` vs `n`, `n-3` vs `n`, …). Besides the OOB read, the comparison result depended on whatever byte happens to follow the literal in rodata. **Fix** Reject `b.len() < a.len()` up front in `eql_case_insensitive_ascii` before calling `strncasecmp` — the same result the NUL sentinel produced in Zig, so observable behavior is unchanged for every in-bounds input (all other callers of the ignore-length variant already pass equal-length slices). `strncasecmp` now only ever reads within both slices. **Verification** - `bun bd test test/js/bun/css/nth-anplusb-ident.test.ts` without the fix (src/ stashed): aborts with the ASAN global-buffer-overflow above. - With the fix: passes. The new test covers valid `n-<digits>` idents that are longer than the `n`/`n-` keywords (`:nth-child(n-3)`, `:nth-child(N-3)`, `:nth-last-child(n- 42)`), keyword case-insensitivity (`:nth-child(N)`), an invalid ident (`:nth-child(NN)` → parse error), and the exact fuzzer-minimized input run in a subprocess. - `bun bd test test/js/bun/css/css.test.ts`: 1032 pass, 0 fail (no behavior change for the existing suite). - A second fuzz report hits the same overflow through `Bun.build` with a CSS entrypoint containing `:nth-child(Nn`; that path goes through the same `parse_nth` comparison and is covered by this fix (`Bun.build` now reports a parse error instead of aborting). - The `build-rust` CI failures on this PR (unused label / unnecessary `unsafe` warnings in `src/spawn`, `src/install`, `src/crash_handler`, `src/runtime/ffi`, `src/runtime/dns_jsc`) are present on current `main` commits that don't include this change and come from files this PR doesn't touch.
github-actions
Bot
force-pushed
the
deps/update-libarchive
branch
from
June 28, 2026 04:31
a592f88 to
59b73c2
Compare
github-actions
Bot
force-pushed
the
deps/update-libarchive
branch
from
August 2, 2026 04:13
59b73c2 to
6218a6e
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.
What does this PR do?
Updates libarchive to version v3.8.9
Compare: libarchive/libarchive@9525f90...27cbc78
Auto-updated by this workflow