Skip to content

crypto: avoid reusing randomInt() cache bytes - #66597

Open
gengjiawen wants to merge 1 commit into
nodejs:mainfrom
gengjiawen:crypto-randomint-cache-reuse
Open

gengjiawen wants to merge 1 commit into
nodejs:mainfrom
gengjiawen:crypto-randomint-cache-reuse

Conversation

@gengjiawen

@gengjiawen gengjiawen commented Oct 8, 2026 •

Copy link
Copy Markdown
Member

When an asynchronous crypto.randomInt() call finds the cache empty, it queues itself and refills the shared cache with randomFill(). A synchronous call made before the refill callback runs refills the same buffer with randomFillSync() and takes values from offset 0. The callback then reset the offset to 0, so the queued call and later calls got the bytes that synchronous calls had already returned. The threadpool job and randomFillSync() could also write the buffer at the same time.

The asynchronous refill now fills a separate buffer, allocated on first use and reused after that. The callback copies it into the cache before resetting the offset. Synchronous calls and the threadpool never touch the same memory, and resetting the offset only hands out bytes nobody has used. The synchronous path is unchanged. The asynchronous path does one extra 6 KiB copy per refill (every 1024 values).

The new test makes the first randomInt() call in a process asynchronous. It wraps ondone of the refill job, which runs only after the threadpool has written the buffer, and makes synchronous calls there before the refill callback runs. It then checks that the async value and the values after the callback differ from the synchronous ones. Without the fix it fails every time, also when the threadpool is busy.

Fixes: #66595
Refs: #35110

AI disclosure: written with the help of Claude (claude:opus-5.5). I checked the bug against the source and the latest nightly, and reviewed the fix and the test.

@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Review requested:

  • @nodejs/crypto

@nodejs-github-bot nodejs-github-bot added crypto Issues and PRs related to the crypto subsystem. needs-ci PRs that need a full CI run. labels Oct 8, 2026
@codecov

codecov Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 90.48%. Comparing base (c3189d4) to head (360f18a).
⚠️ Report is 72 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main   #66597      +/-   ##
==========================================
+ Coverage   90.45%   90.48%   +0.02%     
==========================================
  Files         791      791              
  Lines      276562   277097     +535     
  Branches    53111    53278     +167     
==========================================
+ Hits       250154   250718     +564     
+ Misses      16801    16799       -2     
+ Partials     9607     9580      -27     
Files with missing lines Coverage Δ
lib/internal/crypto/random.js 96.45% <100.00%> (+0.04%) ⬆️

... and 63 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Comment thread test/parallel/test-crypto-randomint-cache.js Outdated
When an asynchronous randomInt() call finds the cache empty, it queues
itself and starts randomFill() on the shared cache buffer. A synchronous
call made before that job completes refills the same buffer with
randomFillSync() and returns values from offset 0. The completion
callback then reset the offset to 0, so the queued call and later calls
returned the bytes that synchronous calls had already used. The
threadpool job and randomFillSync() could also write the buffer at the
same time.

Fill a separate buffer asynchronously and copy it into the cache once
the job is done.

Fixes: nodejs#66595
Refs: nodejs#35110
Signed-off-by: Jiawen Geng <technicalcute@gmail.com>
Assisted-by: claude:opus-5.5
@gengjiawen
gengjiawen force-pushed the crypto-randomint-cache-reuse branch from 59e0b84 to 360f18a Compare October 13, 2026 07:16
@gengjiawen
gengjiawen requested a review from panva October 13, 2026 07:22
@panva panva added author ready PRs with CI started, the required approvals, and no outstanding review comments. request-ci Add this label to start a Jenkins CI on a PR. Only starts once the PR has an approving review. labels Oct 13, 2026
@github-actions github-actions Bot removed the request-ci Add this label to start a Jenkins CI on a PR. Only starts once the PR has an approving review. label Oct 13, 2026
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@panva panva added the resume-ci Add this label to resume the latest eligible Jenkins CI run on a PR with an approving review. label Oct 13, 2026
@github-actions github-actions Bot removed the resume-ci Add this label to resume the latest eligible Jenkins CI run on a PR with an approving review. label Oct 13, 2026
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@panva panva added the resume-ci Add this label to resume the latest eligible Jenkins CI run on a PR with an approving review. label Oct 13, 2026
@github-actions github-actions Bot removed the resume-ci Add this label to resume the latest eligible Jenkins CI run on a PR with an approving review. label Oct 13, 2026
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

author ready PRs with CI started, the required approvals, and no outstanding review comments. crypto Issues and PRs related to the crypto subsystem. needs-ci PRs that need a full CI run.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

crypto.randomInt() can return the same value to a sync and an async caller

3 participants