fix(s3): avoid double-freeing path when presign throws after store creation - #30495
fix(s3): avoid double-freeing path when presign throws after store creation#30495robobun wants to merge 4 commits into
Conversation
…r store creation Once the S3 blob store is constructed it owns the PathLike. If a subsequent step (e.g. signing with missing credentials, or validating expiresIn) returned an error, the blob's defer cleanup freed the path and then the outer errdefer freed it again, tripping a refcount assert in debug builds. Clear the caller's path after ownership transfers to the store so the errdefer becomes a no-op. Same pattern as findOrCreateFileFromPath.
|
Updated 8:08 AM PT - May 11th, 2026
❌ @robobun, your commit ec8bbc1 has 3 failures in
🧪 To try this PR locally: bunx bun-pr 30495That installs a local version of the PR into your bun-30495 --bun |
|
This PR may be a duplicate of:
🤖 Generated with Claude Code |
…nitS3 If a throwing 'type' getter fires after initS3 moved the path into the store, the constructor's errdefer would free the path and then the caller's errdefer would free it again. Clear the store's pathlike before store.deinit() in the error path so the caller retains ownership.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/runtime/webcore/S3File.zig`:
- Around line 271-274: constructInternal() currently calls PathLike.fromJS(...)
and then performs operations (like a type getter) that can throw, but there is
no errdefer to deinit the caller-owned PathLike on those failure paths, so a
thrown error bypasses store.deinit() and leaks path; fix by adding an errdefer
that deinitializes the local path (call path.deinit()) immediately after
PathLike.fromJS(...) and before any further throwing calls, and ensure the
existing errdefer that resets store.data.s3.pathlike and calls store.deinit()
remains in place (also mirror the same errdefer pattern for the other occurrence
around lines 318-321), so any early error cleans up the PathLike and prevents
ownership leak while preserving normal ownership handoff on success.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: a0f11231-6860-427c-97b9-f117f96576d9
📒 Files selected for processing (2)
src/runtime/webcore/S3File.zigtest/js/bun/s3/s3-presign-error-path-free.test.ts
constructInternal, staticFile, and the s3:// branch of constructBunFile were missing errdefer path.deinit(). With the previous change leaving path ownership to the caller on constructor error, these would have leaked the path instead of freeing it.
|
CI status on ec8bbc1:
The HTTP close-on-abort timeout is a pre-existing Windows flake unrelated to this S3 change — it also fails on other PR builds (e.g. #53335) and there's a fix in progress on the |
|
Superseded by #30567, which implements the |
What does this PR do?
Fixes a crash (debug assertion / heap corruption) when S3 operations throw an error after the blob store has already taken ownership of the path.
Root cause
Blob.Store.initS3moves thePathLikeinto the store without adding a reference. Callers inS3File.zigandS3Client.zigwere written as:When the final call threw (missing credentials, invalid
expiresIn, invalid method, etc.),defer blob.deinit()freed the path via the store's destructor, and then the outererrdefer path.deinit()freed the samePathLikeagain — over-derefing theWTFStringImpl(latin1 case) or double-freeing the allocation (encoded_slicecase), hittingbun.assertinWTFStringImpl.deref.Fix
Clear the caller's path right after the store takes ownership so the
errdeferbecomes a no-op. This is the same pattern already used inBlob.findOrCreateFileFromPath.Applied to:
presign,unlink,write,size,exists,stat(static and instance variants).How did you verify your code works?
Bun.S3Client.presign("NFC")panics on debug builds before, throwsERR_S3_MISSING_CREDENTIALScleanly after.{ expiresIn: -1 }to deterministically hit the post-construction error path regardless of env credentials, covering static,Bun.s3, instance, and non-latin1 path variants.Fuzzer fingerprint:
5308c20fced315a7