fix(bindings): add missing null checks for invalid this in crypto/stream C++ bindings#28076
Conversation
…ings - JSBufferList: add missing early return after throwTypeError in length getter (was falling through to deref null pointer) - JSHmac: add null check in digest before dereferencing hmac->m_finalized - JSDiffieHellmanGroup: use ERR_INVALID_THIS instead of generic TypeError to match Node.js error code
Claude Code ReviewThis repository is configured for manual code reviews. Comment |
|
Updated 5:35 PM PT - Mar 13th, 2026
❌ @Jarred-Sumner, your commit 9ac7b91 has 1 failures in 🧪 To try this PR locally: bunx bun-pr 28076That installs a local version of the PR into your bun-28076 --bun |
WalkthroughThree Node.js crypto binding files now include defensive null-checks and enhanced error handling for invalid this-values. The verifyError getter in DiffieHellmanGroup returns a structured error instead of throwing, JSHmac.digest validates the Hmac object, JSBufferList uses explicit early returns after throws, and a new test file verifies these behaviors don't cause segmentation faults. Changes
🚥 Pre-merge checks | ✅ 2✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. 📝 Coding Plan
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/bun.js/bindings/node/crypto/JSHmac.cpp (1)
117-123:⚠️ Potential issue | 🔴 Critical
jsHmacProtoFuncUpdatestill dereferences nullthisLine 118 can return
nullptrfor invalid receivers, but Line 121 dereferenceshmacunconditionally. This leaves a remaining segfault path (native.update.call({})) even after fixingdigest.Suggested fix
JSValue thisHmac = callFrame->thisValue(); JSHmac* hmac = jsDynamicCast<JSHmac*>(thisHmac); + if (!hmac) [[unlikely]] { + return Bun::ERR::INVALID_THIS(scope, globalObject, "Hmac"_s); + } // Check if the HMAC is already finalized if (hmac->m_finalized) { return Bun::ERR::CRYPTO_HASH_FINALIZED(scope, globalObject); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/bun.js/bindings/node/crypto/JSHmac.cpp` around lines 117 - 123, The update method (jsHmacProtoFuncUpdate) can get a null/invalid receiver because jsDynamicCast<JSHmac*>(callFrame->thisValue()) may return nullptr, but the code dereferences hmac immediately to check m_finalized; add a null check after obtaining hmac and return the appropriate crypto/invalid-receiver error (the same pattern used in digest) before accessing hmac->m_finalized. Locate jsHmacProtoFuncUpdate, the call to callFrame->thisValue()/jsDynamicCast<JSHmac*>, and ensure you guard hmac == nullptr and return Bun::ERR::CRYPTO_HASH_FINALIZED or the matching invalid-receiver error path (consistent with existing error helpers) instead of dereferencing a null pointer.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@test/js/node/crypto/crypto-invalid-this.test.ts`:
- Around line 6-9: The getNativeHandle helper currently assumes the "kHandle"
symbol exists and will produce a confusing runtime error when it doesn't; update
the function (getNativeHandle) to explicitly check the result of
Object.getOwnPropertySymbols(obj).find(s => s.description === "kHandle") and
throw or assert with a clear message (e.g. "kHandle symbol not found on object")
when sym is undefined so failures point directly to handle discovery rather than
later when accessing obj[sym].
---
Outside diff comments:
In `@src/bun.js/bindings/node/crypto/JSHmac.cpp`:
- Around line 117-123: The update method (jsHmacProtoFuncUpdate) can get a
null/invalid receiver because jsDynamicCast<JSHmac*>(callFrame->thisValue()) may
return nullptr, but the code dereferences hmac immediately to check m_finalized;
add a null check after obtaining hmac and return the appropriate
crypto/invalid-receiver error (the same pattern used in digest) before accessing
hmac->m_finalized. Locate jsHmacProtoFuncUpdate, the call to
callFrame->thisValue()/jsDynamicCast<JSHmac*>, and ensure you guard hmac ==
nullptr and return Bun::ERR::CRYPTO_HASH_FINALIZED or the matching
invalid-receiver error path (consistent with existing error helpers) instead of
dereferencing a null pointer.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: da50bb31-f1e6-4e1f-970a-d3dd64c27c18
📒 Files selected for processing (4)
src/bun.js/bindings/JSBufferList.cppsrc/bun.js/bindings/node/crypto/JSDiffieHellmanGroup.cppsrc/bun.js/bindings/node/crypto/JSHmac.cpptest/js/node/crypto/crypto-invalid-this.test.ts
|
@robobun adopt |
robobun
left a comment
There was a problem hiding this comment.
All gates pass.
Gate 1: CI
Build #39467 — all test jobs green across Linux (x64, aarch64, musl, baseline), macOS (x64, aarch64), Windows (x64, baseline), Alpine, Ubuntu, and ASAN. The only failure is upload-benchmark.mjs which also fails on main (infra flake). windows-11-aarch64-test-bun is stuck pending — this check doesn't exist on main, likely a new/unavailable runner.
Gate 2: Classification
Bug fix — three null dereference segfaults in C++ bindings.
Gate 3: Test proof
- Baked binary (main) FAILS ✅:
build/debug/bun-debug test crypto-invalid-this.test.ts→UndefinedBehaviorSanitizer: undefined-behavior JSHmac.cpp:177:15 — member access within null pointer of type 'JSHmac' - PR binary PASSES ✅: CI ran the new test on all platforms including ASAN — all passed.
- New test file
test/js/node/crypto/crypto-invalid-this.test.tscovers Hmac.digest and DiffieHellmanGroup.verifyError with invalidthisvalues ({},null,42).
Gate 4: Diff
No TODO/FIXME/HACK. All three fixes are minimal and correct:
JSHmac.cpp: Added missing null check +return ERR::INVALID_THISJSDiffieHellmanGroup.cpp: Replaced no-returnthrowVMTypeErrorwithreturn ERR::INVALID_THISJSBufferList.cpp: Added missingreturn {}after throw
Fixes match the root cause described in the PR body.
Gate 5: Bot convergence
One coderabbit nitpick (add expect(sym).toBeDefined() in test helper) — resolved as noise: test helper, not production code, failure already clear.
Gate 6: Hygiene
Clean PR body with repro, table of bugs, and correct scope. No scope creep.
…tream C++ bindings (oven-sh#28076) Follow-up sweep after oven-sh#28072 — three more places where `jsDynamicCast` on `thisValue()` can return null and the code dereferences it anyway. ## Bugs fixed | File | Function | Problem | |---|---|---| | `JSHmac.cpp` | `jsHmacProtoFuncDigest` | No null check before `hmac->m_finalized` | | `JSDiffieHellmanGroup.cpp` | `jsDiffieHellmanGroupGetter_verifyError` | Had `if (!thisObject) { throwVMTypeError(...); }` but no `return` — fell through to `thisObject->getImpl()` | | `JSBufferList.cpp` | `JSBufferList_getLength` | Same — `if (!bufferList) throwTypeError(...);` with no `return`, fell through to `bufferList->length()` | ## Repro (segfaults on current Bun) ```js const { createHmac } = require("crypto"); const hmac = createHmac("sha256", "key"); const sym = Object.getOwnPropertySymbols(hmac).find(s => s.description === "kHandle"); hmac[sym].digest.call({}); // Segmentation fault at address 0x20 ``` Now throws `TypeError [ERR_INVALID_THIS]: Value of "this" must be of type Hmac`. --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
…tream C++ bindings (oven-sh#28076) Follow-up sweep after oven-sh#28072 — three more places where `jsDynamicCast` on `thisValue()` can return null and the code dereferences it anyway. ## Bugs fixed | File | Function | Problem | |---|---|---| | `JSHmac.cpp` | `jsHmacProtoFuncDigest` | No null check before `hmac->m_finalized` | | `JSDiffieHellmanGroup.cpp` | `jsDiffieHellmanGroupGetter_verifyError` | Had `if (!thisObject) { throwVMTypeError(...); }` but no `return` — fell through to `thisObject->getImpl()` | | `JSBufferList.cpp` | `JSBufferList_getLength` | Same — `if (!bufferList) throwTypeError(...);` with no `return`, fell through to `bufferList->length()` | ## Repro (segfaults on current Bun) ```js const { createHmac } = require("crypto"); const hmac = createHmac("sha256", "key"); const sym = Object.getOwnPropertySymbols(hmac).find(s => s.description === "kHandle"); hmac[sym].digest.call({}); // Segmentation fault at address 0x20 ``` Now throws `TypeError [ERR_INVALID_THIS]: Value of "this" must be of type Hmac`. --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Follow-up sweep after #28072 — three more places where
jsDynamicCastonthisValue()can return null and the code dereferences it anyway.Bugs fixed
JSHmac.cppjsHmacProtoFuncDigesthmac->m_finalizedJSDiffieHellmanGroup.cppjsDiffieHellmanGroupGetter_verifyErrorif (!thisObject) { throwVMTypeError(...); }but noreturn— fell through tothisObject->getImpl()JSBufferList.cppJSBufferList_getLengthif (!bufferList) throwTypeError(...);with noreturn, fell through tobufferList->length()Repro (segfaults on current Bun)
Now throws
TypeError [ERR_INVALID_THIS]: Value of "this" must be of type Hmac.