Skip to content

deps: upgrade BoringSSL to upstream 606d3a344 - #32521

Merged
Jarred-Sumner merged 2 commits into
mainfrom
claude/boringssl-606d3a344
Jun 20, 2026
Merged

deps: upgrade BoringSSL to upstream 606d3a344#32521
Jarred-Sumner merged 2 commits into
mainfrom
claude/boringssl-606d3a344

Conversation

@Jarred-Sumner

Copy link
Copy Markdown
Collaborator

Bumps oven-sh/boringssl to 1a41b9025, a merge of google/boringssl@606d3a344 — 343 commits since the previous merge-base beafe3db1.

Fork patches preserved unchanged (SHA-512/224, SHA3 EVP_MD, BLAKE2b-512, RIPEMD160 EVP, HMAC-SHA3, EVP_PBE_validate_scrypt_params, Electron SSL_want/cipher). All conflicts were upstream's |...|`...` doc-comment restyle landing adjacent to our additions.

gen/sources.json delta: +crypto/evp/p_xwing.cc, -crypto/rand/passive.cc.

Upstream highlights

TLS / libssl

  • ML-DSA signature support in TLS, Raw Public Key (RPK) credentials, the Server Padding extension, SSL_get_signature_algorithm_used, SSL_CTX_set1_available_trust_anchors; Merkle Tree Certificate (plants-04) verification in libpki.
  • Restored SSL_OP_LEGACY_SERVER_CONNECT (and fixed it for TLS 1.3), added SSL_OP_ALL defaults, removed SSL_set_enforce_rsa_key_usage, added TLS_RSA_WITH_AES_256_GCM_SHA384 to the CNSA 1.0 compliance policy.
  • SSL_CTX, SSL_ECH_KEYS, SSL_CREDENTIAL are now opaque structs; CRYPTO_BUFFER_POOL is now reference-counted.

Crypto primitives

  • Post-quantum: significant ML-DSA / ML-KEM speedups (vectorized double-Keccak, unrolled NTTs); X-Wing wired into EVP_KEM; new generic EVP_PKEY KEM encap/decap adapter; EVP_HPKE_KEY_derive.
  • AES-GCM / AES-GCM-SIV: fixed IV-resize state handling, tightened size limits, fixed uninitialized read in GCM-SIV asm, require PCLMUL for GCM-SIV; new EVP_CIPHER_CTX_max_next_update/_max_final.

ASN.1 / X.509 / PEM

  • Rewrote the template ASN.1 encoder/decoder on CBS/CBB; new ASN1_BIT_STRING_set1/_unused_bits (implicit truncation removed); tightened URI name-constraint matching; libpki now defaults to a path-building iteration limit of 20.
  • PEM: const-corrected PEM_read/PEM_write, unexported IMPLEMENT_PEM_*, removed PEM_TYPE_*, capped PEM data at 1 GiB.

Security / correctness

  • Fixed RC2 heap overflow with very large keys, an off-by-one allowing an unauthenticated handshake abort, max_early_data_size > 2^16 handling, beeu_mod_inverse_vartime on aarch64, EVP_AEAD_CTX_seal_scatter bounds check, several X.509/OBJ/BN edge-case bugs.

API / misc

  • BIO_write_ex, CBS_get_u48, CBS_peek_any_asn1_tag, sk_FOO_sort_and_dedup; fixed non-blocking connect BIOs, BIO_find_type, BIO_set_retry_special.
  • FIPS module no longer uses entropy-injected mode; fork detection extended to FreeBSD/OpenBSD rfork().
  • libcrypto's C++ runtime dependency restored behind a build flag; BORINGSSL_API_VERSION bumped.

Bun-side changes

src/runtime/socket/UpgradedDuplex.rs — fixes a latent UAF the upgrade exposed. handle_readingtrigger_close_callbackon_closeteardown() ran self.wrapper = None while an SSLWrapper::handle_traffic frame was still live with a *mut into the Some payload. The Option assignment runs Drop (which correctly nulls ssl), then memmoves a fresh None value over the slot — whose payload bytes are stack garbage — so the in-flight frame's Self::r(this).ssl read junk and flush_pending_events called SSL_get_ex_data on it (SEGV in OPENSSL_sk_value). WindowsNamedPipe already guards this with WRAPPER_BUSY; here the simpler fix is to deinit() in place so the ssl=None/closed_notified guards stay readable, and let the Option drop when DuplexUpgradeContext frees on the next tick.

test/js/node/tls/tls-connect-socket-churn.test.ts — raises the ASAN/debug RSS bound (64 → 192 MB). The sslCtxLiveCount regression guard and LSAN are both clean; the new BoringSSL just has more per-handshake allocation churn (PQ key shares, transcript buffers) that sits in the ASAN quarantine when this file runs after the rest of test/js/node/tls/.

.claude/commands/upgrade-boringssl.md — documents this procedure for next time.

Bumps oven-sh/boringssl to 1a41b9025 (merge of google/boringssl@606d3a344,
343 commits since beafe3db1). Fork patches preserved unchanged: SHA-512/224,
SHA3 EVP_MD, BLAKE2b-512, RIPEMD160 EVP, HMAC-SHA3, scrypt validation,
Electron SSL_want/cipher.

Source-list delta from gen/sources.json: +crypto/evp/p_xwing.cc,
-crypto/rand/passive.cc.

Fixes a latent UAF in UpgradedDuplex::teardown() that the new error-path
behavior exposed: handle_reading -> trigger_close_callback -> on_close ->
teardown() ran 'self.wrapper = None' while an SSLWrapper::handle_traffic
frame was still live with a *mut into the Some payload. The Option assignment
runs Drop (which correctly nulls ssl), then memmoves a fresh None value over
the slot — whose payload bytes are stack garbage — so the in-flight frame's
'Self::r(this).ssl' read junk and flush_pending_events called SSL_get_ex_data
on it. Neuter via deinit() in place instead so the closed_notified/ssl=None
guards stay readable; the Option drops for real when DuplexUpgradeContext
frees on the next tick. WindowsNamedPipe already handles this with
WRAPPER_BUSY.

Raises the ASAN/debug RSS bound in tls-connect-socket-churn.test.ts: the
sslCtxLiveCount regression guard and LSAN are clean, but BoringSSL's larger
per-handshake allocation churn (PQ key shares) sits in the ASAN quarantine
when this file runs after the rest of the tls/ suite.

Adds .claude/commands/upgrade-boringssl.md documenting the procedure.
@robobun

robobun commented Jun 20, 2026

Copy link
Copy Markdown
Collaborator
Updated 9:07 PM PT - Jun 19th, 2026

@Jarred-Sumner, your commit d3ac55b221c82571b1f09b80e2e0ddf1890ab62f passed in Build #63548! 🎉


🧪   To try this PR locally:

bunx bun-pr 32521

That installs a local version of the PR into your bun-32521 executable, so you can run:

bun-32521 --bun

@github-actions

Copy link
Copy Markdown
Contributor

Found 1 issue this PR may fix:

  1. AES-GCM incorrect on bun-windows-x64 with OPENSSL_ia32cap=~0x200000000000000; baseline and Node are correct #32126 - AES-GCM incorrect output on Windows x64 with OPENSSL_ia32cap — the BoringSSL upstream commits in this upgrade include AES-GCM IV-resize state handling fixes and an uninitialized read fix in GCM-SIV asm, which directly address this class of cipher correctness bug.

If this is helpful, copy the block below into the PR description to auto-close this issue on merge.

Fixes #32126

🤖 Generated with Claude Code

@coderabbitai

coderabbitai Bot commented Jun 20, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 14ed16a3-d44c-40f3-9c8d-c71be4b509e1

📥 Commits

Reviewing files that changed from the base of the PR and between 295d4e1 and d3ac55b.

📒 Files selected for processing (3)
  • .claude/commands/upgrade-boringssl.md
  • test/js/node/process/process.test.js
  • test/js/node/tls/tls-connect-socket-churn.test.ts

Walkthrough

Bumps the pinned BoringSSL commit SHA, updates the compiled crypto source list (adds p_xwing.cc, removes passive.cc), fixes UpgradedDuplex::teardown to call SSLWrapper::deinit() instead of dropping the wrapper in-place to prevent re-entrancy hazards, updates the expected boringssl hash in the process versions test, widens the ASAN/debug RSS bound in the TLS churn test, and adds a BoringSSL upgrade runbook.

Changes

BoringSSL Upgrade

Layer / File(s) Summary
BoringSSL commit bump and source list changes
scripts/build/deps/boringssl.ts
Bumps BORINGSSL_COMMIT to a new upstream SHA, adds crypto/evp/p_xwing.cc to the crypto source list, and removes crypto/rand/passive.cc from the random source list.
SSLWrapper teardown: deinit() instead of drop
src/runtime/socket/UpgradedDuplex.rs
Replaces self.wrapper = None with wrapper.deinit() in teardown() to avoid a use-after-free re-entrancy hazard where an in-flight TLS traffic handler could hold stale pointers into the dropped wrapper.
Test expectations and RSS tolerance
test/js/node/process/process.test.js, test/js/node/tls/tls-connect-socket-churn.test.ts
Updates the expected boringssl commit hash in the process versions test. Widens the rssBound for ASAN/debug builds in the TLS churn test to accommodate increased allocation churn.
BoringSSL upgrade runbook and documentation
.claude/commands/upgrade-boringssl.md
Adds step-by-step BoringSSL upgrade documentation covering the fork-merge workflow, conflict resolution guidance for expected preserved patches, CMake verification, source regeneration, and historical failure modes to monitor during future upgrades.

Possibly related PRs

  • oven-sh/bun#31959: Both PRs modify SSLWrapper lifecycle and teardown behavior—the retrieved PR fixes close-callback triggering in SSLWrapper::shutdown, while this PR fixes wrapper deinit ordering in UpgradedDuplex::teardown to prevent stale-pointer access during re-entrant TLS close handling.
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: upgrading BoringSSL to a specific upstream commit version.
Description check ✅ Passed The description provides comprehensive context about what was upgraded, why, and how verification was done, though it exceeds the basic template structure.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 @.claude/commands/upgrade-boringssl.md:
- Around line 52-56: The runbook step in upgrade-boringssl.md currently uses the
bun -e flag directly, which bypasses the repository's documented debug-build
command flow. Replace the bun -e invocation with bun bd -e to conform to the
coding guidelines that require all bun commands to use the debug-build command
wrapper instead of executing bun directly.
🪄 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: 129b8bb3-b460-489e-a1af-219a03648b17

📥 Commits

Reviewing files that changed from the base of the PR and between 25e32c1 and 295d4e1.

📒 Files selected for processing (4)
  • .claude/commands/upgrade-boringssl.md
  • scripts/build/deps/boringssl.ts
  • src/runtime/socket/UpgradedDuplex.rs
  • test/js/node/tls/tls-connect-socket-churn.test.ts

Comment on lines +52 to +56
bun -e 'const j=require("./vendor/boringssl/gen/sources.json");
const f=l=>l.map(JSON.stringify).join(", ");
for(const k of ["bcm","crypto","ssl","decrepit"]) console.log(k,"\n",f(j[k].srcs));
console.log("asm\n",f([...j.bcm.asm,...j.crypto.asm]));
console.log("nasm\n",f([...j.bcm.nasm,...j.crypto.nasm]))'

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use bun bd -e in this runbook step.

This snippet currently uses bun -e, which bypasses the repo’s documented debug-build command flow.

Suggested doc fix
-  bun -e 'const j=require("./vendor/boringssl/gen/sources.json");
+  bun bd -e 'const j=require("./vendor/boringssl/gen/sources.json");
           const f=l=>l.map(JSON.stringify).join(", ");
           for(const k of ["bcm","crypto","ssl","decrepit"]) console.log(k,"\n",f(j[k].srcs));
           console.log("asm\n",f([...j.bcm.asm,...j.crypto.asm]));
           console.log("nasm\n",f([...j.bcm.nasm,...j.crypto.nasm]))'

As per coding guidelines: “Never use bun test or bun <file> directly - always use bun bd ....”

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
bun -e 'const j=require("./vendor/boringssl/gen/sources.json");
const f=l=>l.map(JSON.stringify).join(", ");
for(const k of ["bcm","crypto","ssl","decrepit"]) console.log(k,"\n",f(j[k].srcs));
console.log("asm\n",f([...j.bcm.asm,...j.crypto.asm]));
console.log("nasm\n",f([...j.bcm.nasm,...j.crypto.nasm]))'
bun bd -e 'const j=require("./vendor/boringssl/gen/sources.json");
const f=l=>l.map(JSON.stringify).join(", ");
for(const k of ["bcm","crypto","ssl","decrepit"]) console.log(k,"\n",f(j[k].srcs));
console.log("asm\n",f([...j.bcm.asm,...j.crypto.asm]));
console.log("nasm\n",f([...j.bcm.nasm,...j.crypto.nasm]))'
🤖 Prompt for 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.

In @.claude/commands/upgrade-boringssl.md around lines 52 - 56, The runbook step
in upgrade-boringssl.md currently uses the bun -e flag directly, which bypasses
the repository's documented debug-build command flow. Replace the bun -e
invocation with bun bd -e to conform to the coding guidelines that require all
bun commands to use the debug-build command wrapper instead of executing bun
directly.

Source: Coding guidelines

@github-actions

Copy link
Copy Markdown
Contributor

This PR may be a duplicate of:

  1. tls: defer upgraded-duplex teardown to the owning context's close task #31364 - Also fixes the SSLWrapper use-after-free in UpgradedDuplex.rs during on_close teardown, but uses a deferred close-task approach instead of in-place deinit

🤖 Generated with Claude Code

Comment thread test/js/node/tls/tls-connect-socket-churn.test.ts Outdated
… bound to 2x headroom

Also adds the process.test.js step to the upgrade-boringssl skill so it
isn't missed next time.
import { depSourceDir } from "../source.ts";

const BORINGSSL_COMMIT = "0c5fce43b7ed5eb6001487ee48ac65766f5ddcd1";
const BORINGSSL_COMMIT = "1a41b9025c2c0a37edd07ff10f6944f03e028522";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 This bump restores SSL_OP_LEGACY_SERVER_CONNECT as a real flag (0x4) and defines SSL_OP_ALL = SSL_OP_LEGACY_SERVER_CONNECT, but Bun's hand-mirrored constants in src/boringssl_sys/boringssl.rs:552-554 and src/boringssl_sys/boringssl.zig:18214,18218 still hardcode 0 (with a now-incorrect "BoringSSL defines this as 0 (no-op flag)" comment). There's no runtime regression today because the new BoringSSL applies this flag by default at SSL_CTX_new, so configure_http_client_with_alpn's SSL_clear_options(ssl, 0)/SSL_set_options(ssl, 0) no-op is harmless — but the comment is now wrong, crypto.constants.SSL_OP_LEGACY_SERVER_CONNECT (C++ #includes the real header) will expose 4 while the Rust/Zig FFI uses 0, and upstream's TODO(crbug.com/41393419) to disable the flag by default would turn this into a real fetch() break on a future bump. Suggest updating both constants to 0x4 and adding an "SSL_OP_* constant mirrors" bullet to the new runbook's §Things that have broken before.

Extended reasoning...

What changed upstream

This PR bumps BORINGSSL_COMMIT from 0c5fce43b to 1a41b9025 (a merge of google/boringssl@606d3a344). One of the upstream changes — called out in this PR's own description as "Restored SSL_OP_LEGACY_SERVER_CONNECT (and fixed it for TLS 1.3), added SSL_OP_ALL defaults" — moved two macros out of the "deprecated, defined as zero" block in include/openssl/ssl.h:

Macro Old (0c5fce43b, ssl.h:5831/5835) New (1a41b9025, ssl.h:605/622)
SSL_OP_LEGACY_SERVER_CONNECT 0 0x00000004L
SSL_OP_ALL 0 SSL_OP_LEGACY_SERVER_CONNECT

What's stale in Bun

Bun hand-mirrors these constants in two places that this PR did not touch:

  • src/boringssl_sys/boringssl.rs:552-554:

    /// `SSL_OP_LEGACY_SERVER_CONNECT` — BoringSSL defines this as 0 (no-op flag);
    /// kept so callers can mirror the OpenSSL clear/set dance verbatim.
    pub const SSL_OP_LEGACY_SERVER_CONNECT: u32 = 0;

    The comment is now factually wrong.

  • src/boringssl_sys/boringssl.zig:18214,18218:

    pub const SSL_OP_ALL = @as(c_int, 0);
    pub const SSL_OP_LEGACY_SERVER_CONNECT = @as(c_int, 0);

These flow into configure_http_client_with_alpn — run on every outgoing fetch()/WebSocket TLS open — at src/http/lib.rs:940-941 and src/boringssl_sys/boringssl.zig:19068-19069:

boringssl::c::SSL_clear_options(ssl, boringssl::c::SSL_OP_LEGACY_SERVER_CONNECT);
boringssl::c::SSL_set_options(ssl, boringssl::c::SSL_OP_LEGACY_SERVER_CONNECT);

The function's doc comment says it "Sets … the legacy-server-connect option", but with the constant still 0 this is now SSL_clear_options(ssl, 0) + SSL_set_options(ssl, 0) — a pure no-op.

Why there's no runtime regression today

The new ssl.h doc for SSL_OP_LEGACY_SERVER_CONNECT explicitly states "This option is enabled by default", and the new SSL_OP_ALL doc says it "is not necessary to pass". Concretely, ssl/internal.h:3985 now initializes SSL_CTX::options = SSL_OP_ALL, and ssl_lib.cc:476 inherits ctx options into each SSL. So:

  1. SSL_CTX_new() sets bit 0x4 on the context.
  2. SSL_new() copies it onto the connection.
  3. Bun's configure_http_client_with_alpn calls SSL_clear_options(ssl, 0) → clears nothing → bit 0x4 stays set.
  4. SSL_set_options(ssl, 0) → ORs in nothing → bit 0x4 stays set.
  5. Handshake against a non-RFC5746 server proceeds exactly as before.

No SSL_CTX_clear_options caller elsewhere in Bun clears bit 4, so the inherited default survives. fetch() to legacy servers continues to work — the Rust/Zig code is correct only by coincidence of upstream's current default.

What is real

  1. Wrong commentboringssl.rs:552 now lies about what BoringSSL defines.
  2. Internal/external divergencecrypto.constants.SSL_OP_LEGACY_SERVER_CONNECT is exposed via NodeConstantsModule.h:794 / ProcessBindingConstants.cpp:822, which #include the real openssl/ssl.h and will now return 4 to JS, while the Rust/Zig FFI passes 0 internally. This is cosmetic today (JS-supplied secureOptions reaches BoringSSL via the C path with the real value), but it's exactly the kind of skew the bindings file is supposed to prevent.
  3. Latent break on the next bump — the new header carries TODO(crbug.com/41393419): Disable SSL_OP_LEGACY_SERVER_CONNECT by default at ssl.h:621. When upstream lands that, step (1) above stops setting bit 0x4, Bun's no-op clear/set still does nothing, and fetch() against non-RFC5746 servers starts failing with unsafe legacy renegotiation disabled — even though Bun's code intends to allow it.
  4. Runbook gap — this PR's new .claude/commands/upgrade-boringssl.md §"Things that have broken before" lists the asn1_string_st/GENERAL_NAME_st struct mirrors but not the hardcoded SSL_OP_* constants in boringssl.rs/boringssl.zig. This is precisely the silent-drift class that section is meant to catch.

Suggested fix

// src/boringssl_sys/boringssl.rs
/// `SSL_OP_LEGACY_SERVER_CONNECT` — allow initial connections to servers that
/// don't support RFC 5746 secure renegotiation. BoringSSL applies this in
/// `SSL_OP_ALL` by default; mirrored so callers can clear/set explicitly.
pub const SSL_OP_LEGACY_SERVER_CONNECT: u32 = 0x00000004;
// src/boringssl_sys/boringssl.zig
pub const SSL_OP_LEGACY_SERVER_CONNECT = @as(c_int, 0x00000004);
pub const SSL_OP_ALL = SSL_OP_LEGACY_SERVER_CONNECT;

And add to upgrade-boringssl.md §"Things that have broken before":

- **`SSL_OP_*` constant values** — Bun mirrors a handful in `src/boringssl_sys/boringssl.{rs,zig}`; diff `include/openssl/ssl.h` for value changes (`SSL_OP_LEGACY_SERVER_CONNECT` went 0→4 in the 606d3a344 bump).

Marking as a nit since there's no behavioral regression in this PR — but worth a one-line cleanup commit so the next BoringSSL bump doesn't silently break fetch().

@Jarred-Sumner
Jarred-Sumner merged commit 46c54cd into main Jun 20, 2026
78 checks passed
@Jarred-Sumner
Jarred-Sumner deleted the claude/boringssl-606d3a344 branch June 20, 2026 04:23
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.

2 participants