Add regression test for scrypt non-determinism (#28607)#28905
Draft
Add regression test for scrypt non-determinism (#28607)#28905
Conversation
Collaborator
Author
|
Updated 1:44 AM PT - Apr 6th, 2026
❌ @robobun, your commit 6a10324 has 2 failures in
🧪 To try this PR locally: bunx bun-pr 28905That installs a local version of the PR into your bun-28905 --bun |
This was referenced Apr 6, 2026
scrypt was non-deterministic on Arch Linux (Bun built with Clang 22) because
WebKit's IntegralTypedArrayAdaptor::toNativeFromDouble() did:
int32_t result = static_cast<int32_t>(value);
if (static_cast<double>(result) != value) result = toInt32(value);
return static_cast<Type>(result);
static_cast<int32_t>(double) is undefined behavior in C++ when value is
outside [INT32_MIN, INT32_MAX]. Clang 22 legally optimizes the subsequent
range check away, silently turning every out-of-range typed-array store into
INT32_MIN. That corrupted @noble/hashes/scrypt — and therefore Better Auth's
hashPassword — producing different output for the same input across runs.
Picks up oven-sh/WebKit#179, which always routes through toInt32(): on
x86_64 that uses a cvttsd2si inline-asm fast path with a portable fallback,
matching the existing FJCVTZS ARM code path.
Adds a regression test at test/regression/issue/28607.test.ts that exercises
Int32Array / Uint32Array / Int16Array stores of out-of-range values and
verifies the scrypt-style add + rotate + xor mixing pattern is
deterministic.
dc1771f to
93c8131
Compare
The Bun-side fix for #28607 requires oven-sh/WebKit#179 to merge (or its Preview Build workflow to be approved so the prebuilt binary publishes). Neither has happened yet, and pointing WEBKIT_VERSION at a non-existent preview tag breaks the prebuilt fetch on every CI build lane. Keep the regression test at test/regression/issue/28607.test.ts so the bug's observable behavior is captured. Once oven-sh/WebKit#179 lands and a WebKit version bump picks it up, this test will guard against re-regression.
Previous gate run hit a compile error in bindings.cpp because an earlier attempt to seed the WebKit build cache left fc9f2fa7 header files under the c2010c47 cache slot — JSPromiseReaction.h is a c2010c47-only header. Cache has been re-extracted from the correct prebuilt tarball. The PR itself is unchanged: test/regression/issue/28607.test.ts only. WEBKIT_VERSION stays at main's default (c2010c47). Pushing an empty commit to re-run the gate.
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.
Regression test for #28607. The actual fix is oven-sh/WebKit#179 — which needs to merge upstream before this bug is closed. See below for why the Bun-side change is just a test.
Repro
Root cause
Undefined behavior in WebKit's
IntegralTypedArrayAdaptor::toNativeFromDouble()(Source/JavaScriptCore/runtime/TypedArrayAdaptors.h):static_cast<int32_t>(double)is UB in C++ whenvalueis outside[INT32_MIN, INT32_MAX]. Clang 22 legally optimizes the subsequent range check away, silently turning every out-of-range typed-array store intoINT32_MIN. That corrupts@noble/hashes/scrypt— and therefore Better Auth'shashPassword— producing different output for the same input across runs.Diagnosed upstream by @carlsmedstad (Arch Linux maintainer) in #28607.
Fix
The fix lives in oven-sh/WebKit#179, which always routes through
toInt32():cvttsd2siinline-asm fast path with a portable fallback totoIntImpl<int32_t>()when truncation isn't exact — avoiding the UB entirely.FJCVTZScode path (which was already correct).TypedArrayAdaptors.his removed.This PR does not bump
WEBKIT_VERSION. The earlier attempt (preview-pr-179-3f10db79) broke every C++ build lane because the WebKit Preview Build workflow on that PR is still inaction_requiredstate (the PR is from a fork) — no prebuilt binary exists. A WebKit maintainer needs to click Approve and run on https://github.com/oven-sh/WebKit/actions/runs/23983158093 before the tag publishes. Once that lands and oven-sh/WebKit#179 merges tomain, a regular WebKit bump will pick up the fix and this test will guard it.Test
test/regression/issue/28607.test.tsexercises the JS-visible behavior so the regression is caught by CI the next time a clang version triggers the UB:Int32Arraystores of values> 2**31must matchv | 0(ECMA-262 ToInt32 evaluated via a different WebKit code path).Uint32Arraystores of values> 2**32must matchv >>> 0.Int16Arraystores of values> 2**15must wrap mod 2^16 with sign adjustment.Int32Arrayoutputs across 50 repeated runs.add + rotate + xormixing pattern on a 16-laneInt32Array(the exact shape that@noble/hashes/scryptuses) must be deterministic across 50 runs.Local
bun bd test test/regression/issue/28607.test.tspasses all 5 tests / 54 expects.Staying draft until WebKit PR #179 is merged so a version bump can pick it up.