You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
crypto.randomInt() can return the same value to a sync and an async caller #66595
v27.0.0-nightly20261007aa1bf2cc37 (main at 6d5e309), also v24.21.0
Platform
Linux x64
Subsystem
crypto
What steps will reproduce the bug?
constcrypto=require('node:crypto');constmax=2**48-1;constsyncValues=[];// The first randomInt() call finds the cache empty, so it is queued// while the cache is refilled asynchronously.crypto.randomInt(max,(err,n)=>{console.log('async value:',n);console.log('sync values:',syncValues);});// Let the refill job finish before the synchronous calls below.conststart=Date.now();while(Date.now()-start<50);for(leti=0;i<3;i++)syncValues.push(crypto.randomInt(max));
How often does it reproduce? Is there a required condition?
Every time with the snippet above. In general it needs an asynchronous randomInt() call that finds the cache empty (the first call in the process, or after the 1024 cached values are used up) followed by synchronous randomInt() calls before the refill callback runs.
What is the expected behavior? Why is that the expected behavior?
The async value is independent of the sync values. Every randomInt() call should get its own CSPRNG bytes.
The async callback receives the same value as the first sync call. Calls made after the callback also repeat the second and third sync values.
Additional information
randomInt() keeps a 6 KiB cache. An async call that finds it empty queues itself and runs randomFill() on that cache. A sync call made before the job's callback runs sees the cache as still empty, refills the same buffer with randomFillSync(), and reads from offset 0. The callback then sets the offset back to 0 and replays the queued call, which reads those bytes again. The threadpool job and randomFillSync() can also write the buffer at the same time.
The 50 ms wait only makes the threadpool job finish before the sync refill. Without it the result depends on timing. This has been there since the cache was added in #35110.
Activity
crypto: avoid reusing randomInt() cache bytes
crypto: avoid reusing randomInt() cache bytes