flat assembler
Message board for the users of flat assembler.

Index > Linux > AMD's random number generator can't generate a 0 (+ app)

Goto page Previous  1, 2
Author
Thread Post new topic Reply to topic
Sellyme



Joined: 22 Sep 2026
Posts: 1
Sellyme 22 Sep 2026, 10:59
Jessé wrote:

You can help me figure out this by using my application to seek for any 0 number generated, and maybe post your results at this forum thread...

I tested this on a Ryzen 5 1600 (Zen 1) and a Ryzen 9 9950X3D (Zen 5). The results for both are attached.

The 1600 experienced exactly the same bugs as your 4800HS, so this is not a problem exclusive to the Zen 2 microarchitecture.

The 9950X3D was able to generate 0s, but also exposed a different bug that affects Zen 5 processors where 16- and 32-bit RDSEED generates 0s instead of a failure state when out of entropy (CVE-2025-62626). That said, the 16-bit form of RDRAND gives results that exonerate this CPU of the bug you've described.

(As an aside, the generation performance for the 9950X3D was absolutely terrible. I suspect that this is due to the large number of "false zeroes" causing constant screen writes, but I might be wrong on that)

EDIT: A friend (who may post here themselves) is reporting that their Ryzen 7 5700X (Zen 3) passes. So it looks like this process does affect more processors than was initially thought, but is likely fixed in newer ones.


Description: Ryzen 9 9950X3D
Filesize: 23.85 KB
Viewed: 3049 Time(s)

2026-09-22_19-54-53.png


Description: Ryzen 5 1600
Filesize: 31.07 KB
Viewed: 3049 Time(s)

2026-09-22_20-23-11.png


Post 22 Sep 2026, 10:59
View user's profile Send private message Reply with quote
matja



Joined: 22 Sep 2026
Posts: 1
matja 22 Sep 2026, 11:53
I'm getting 16-bit and 32-bit zeros on my Zen 3. I'll leave it running to gather more samples.
Code:
┌─────────────────────────────────────────────────────────────────────────────┐
│  CPU Hardware Random Generator Fryer Application                            │
╞══════╤══════════════════════════════════════════════════════╤═══════════════╡
│ CPU: │ AMD EPYC 74F3 24-Core Processor                      │ RDRAND RDSEED │
├──────┴───┬─────┬─────────┬──────────────────────────────────┴───────────────┤
│ Threads: │  48 │ Status: │ CPU has passed 'zero generate' test              │
╞═════╤════╧═════╧╤════════╧══╤═══════════╤═══════════╤═══════════╤═══════════╡
│  #  │  Rand 16  │  Rand 32  │  Rand 64  │  Seed 16  │  Seed 32  │  Seed 64  │
├─────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┤
│ +1: │      58610│          3│          0│      59215│          1│          0│
├─────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┤
│  0: │      59186│          2│          0│      59084│          0│          0│
├─────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┤
│ -1: │      58686│          1│          0│      59160│          0│          0│
╞═════╧═══════════╧═══════════╧═══════════╧═══════════╧═══════════╧═══════════╡
│ Frying time: 0d 01:33:39.0                           23324.72Mi │  4.09Mn/s │
└─────────────────────────────────────────────────────────────────────────────┘
    
Post 22 Sep 2026, 11:53
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 21154
Location: In your JS exploiting you and your system
revolution 22 Sep 2026, 12:58
Sellyme wrote:
... exposed a different bug that affects Zen 5 processors where 16- and 32-bit RDSEED generates 0s instead of a failure state when out of entropy (CVE-2025-62626).
I've seen bad code that assumed a zero return value was a failure state, and didn't detect that CF returned false upon failure.

BTW: The docs state this equivalent alorithm
Code:
IF HW_NRND_GEN.ready = 1
    THEN
        CASE of
            operand size is 64: DEST[63:0] := HW_NRND_GEN.data;
            operand size is 32: DEST[31:0] := HW_NRND_GEN.data;
            operand size is 16: DEST[15:0] := HW_NRND_GEN.data;
        ESAC;
        CF := 1;
    ELSE
        CASE of
            operand size is 64: DEST[63:0] := 0;
            operand size is 32: DEST[31:0] := 0;
            operand size is 16: DEST[15:0] := 0;
        ESAC;
        CF := 0;
FI;
OF, SF, ZF, AF, PF := 0;    
So a zero return value is required upon failure, but zero doesn't guarantee it failed because zero is also a valid output from the RNG.

Note that the docs show the exact same algorithm for RDSEED and RDRAND, with the only difference being NRND is replace with RND.
Post 22 Sep 2026, 12:58
View user's profile Send private message Visit poster's website Reply with quote
eliotlencelot



Joined: 24 Sep 2026
Posts: 1
Location: Marseille / France
eliotlencelot 24 Sep 2026, 13:55
Hey Jessé,
my Zen + CPU have also failed to generate any zero.

I am here because this post has been relayed on Hacker News (YC).

Here is the result for my laptop CPU, with more than 100M iterations:
Code:
┌─────────────────────────────────────────────────────────────────────────────┐
│  CPU Hardware Random Generator Fryer Application                            │
╞══════╤══════════════════════════════════════════════════════╤═══════════════╡
│ CPU: │ AMD Ryzen 5 3500U with Radeon Vega Mobile Gfx        │ RDRAND RDSEED │
├──────┴───┬─────┬─────────┬──────────────────────────────────┴───────────────┤
│ Threads: │   8 │ Status: │ CPU has failed to generate zero number           │
╞═════╤════╧═════╧╤════════╧══╤═══════════╤═══════════╤═══════════╤═══════════╡
│  #  │  Rand 16  │  Rand 32  │  Rand 64  │  Seed 16  │  Seed 32  │  Seed 64  │
├─────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┤
│ +1: │        308│          0│          0│        305│          0│          0│
├─────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┤
│  0: │          0│          0│          0│          0│          0│          0│
├─────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┤
│ -1: │        295│          0│          0│        312│          0│          0│
╞═════╧═══════════╧═══════════╧═══════════╧═══════════╧═══════════╧═══════════╡
│ Finished. Iterations done: 121538740.                                       │
└─────────────────────────────────────────────────────────────────────────────┘
    


As far, as I can read here and on HN, at least these families seem to be affected by this specific 16-bit missing zero bug:
    Zen 1 (as shown with a Ryzen 1600, per Sellyme)
    Zen + (as shown with a Ryzen 3500U, per my own test eliotlencelot)
    Zen 2 Renoir (as shown with a Ryzen 4800HS, per Jessé)


And also, these families seem to be not affected by this specific 16-bit missing zero bug:
    Zen 3 (as shown with an EPYC 74F3, per matja)
    Zen 5 (as shown with Ryzen 9 9950X3D, per Sellyme)
Post 24 Sep 2026, 13:55
View user's profile Send private message Visit poster's website Reply with quote
Jessé



Joined: 03 May 2025
Posts: 155
Location: Brazil
Jessé 26 Sep 2026, 01:06
Hello there,

I must admit that I almost (I said almost) forgot about this subject, but, so far, you came here to remember it...

Well, there are some important points to let you all know, and, thanks to your tests and the shared results, I think we now get a more precise approach to this specific situation:

- The code I use strictly follows the only rule needed to use RNG unit (either Intel or AMD): check carry flag for 1, and then proceed, otherwise, try again. I only add a 'pause' instruction at every loop, so the RNG has a little extra delay on retries, and also the processor receives a hint to lower its power consumption during the wait of a valid generated number, because RNG unit is slow, and it is only one for the entire node of processors, a shared resource, therefore. My guess;
- AMD, since that time when I discovered this issue (and figure out by doing some search on web if another one has not stated this before), so far, has never properly replied me about this. They only said that they're still investigating, so, I resign with them, and stop contacting them by e-mail;
- The failure, as partially stated by you and me, seems to affect Zen+ and Zen2 processors, but maybe not all of them; and also, this application can also spot that RDSEED problem which, in turn, gaves too many zeroes instead of none: quite unfair distributions of zeroes across AMD families, might I say Wink ;
- The problem, which can be deducted from every size result, starting with 16-bit which is easier to spot, is: no 0 number can be generated at a defined size: either 16, 32 and/or 64 bit, it will never be 0 fitting the requested size: it is not only related to 16-bit. I have this running for more than 8 days, and, with 2.6 million numbers per second, I have no zeroes under 32 bit sizes, too. And running on Intel, with 1.1 Mn/sec, a healthy amount of 32 bit zeroes, similar to 1's and -1's;
- This is not a huge problem, and went unnoticed since forever, until someone (like me, on an arbitrary statistical test) really accounts for a 0 in its project/test;
- I have only tested this on 2 Zen2 AMD processors, with the same exact result; and then, I've decided to make it public at my GitHub so others can also test for themselves. With the main goal of helping AMD acknowledge this issue and handle it properly, either by fixing their RNG unit on future processors, or issuing a microcode update (maybe?) so this can be fixed. I didn't mention Intel processors, because they don't have this issue (always instantly approved).

Following, is the main number gathering part that is responsible to extract valid numbers off RNG unit under my test application:

Code:
                           ; ... some internal stuff before start poking RNG unit
                    @1      pause                ; tell processor that this is a spin-wait loop
                            test        [flags], FLAG_MUST_EXIT ; internal exit flag
                            jnz         .end                    ; also avoid deadlocks if RNG fails
                            rdrand      dx                      ; try obtain a random number
                            jnc         @1b                     ; and start again if CF=0
                            lock inc    [Count.benchmark]       ; count successful iterations per second
                            ; ... proceed with result processing and accounting
    


Regards, and thanks for sharing your thoughts...
Post 26 Sep 2026, 01:06
View user's profile Send private message Visit poster's website Reply with quote
sylware



Joined: 23 Oct 2020
Posts: 679
Location: Marseille/France
sylware 26 Sep 2026, 10:16
A bit on the side: do you know if this AMD RNG unit is used for linux /dev/random /dev/urandom?
Post 26 Sep 2026, 10:16
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 21154
Location: In your JS exploiting you and your system
revolution 26 Sep 2026, 11:31
sylware wrote:
A bit on the side: do you know if this AMD RNG unit is used for linux /dev/random /dev/urandom?
Yes, it is. It is one of the sources, and it is classed as a low trust source.
Post 26 Sep 2026, 11:31
View user's profile Send private message Visit poster's website Reply with quote
sylware



Joined: 23 Oct 2020
Posts: 679
Location: Marseille/France
sylware 27 Sep 2026, 12:13
@revolution

OMG. Even if it is a low trust source, on my very "quiet" linux system, I could bet this very low trust source is going to sky rocket in 'trust'.

I must remove it. If you already know where it is in the linux source code, if you could just tell me that would be nice, if not, no worries, I'll go in very soon.
Post 27 Sep 2026, 12:13
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 21154
Location: In your JS exploiting you and your system
revolution 27 Sep 2026, 18:08
sylware wrote:
Even if it is a low trust source, on my very "quiet" linux system, I could bet this very low trust source is going to sky rocket in 'trust'.
A low trust source just means that fewer bits are logged into the entropy collector for each call to update. The trust level doesn't rise.
Post 27 Sep 2026, 18:08
View user's profile Send private message Visit poster's website Reply with quote
sylware



Joined: 23 Oct 2020
Posts: 679
Location: Marseille/France
sylware 28 Sep 2026, 10:43
Namely those "bits" will never be zero?

I'll dive in the next few days to remove it.
Post 28 Sep 2026, 10:43
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 21154
Location: In your JS exploiting you and your system
revolution 28 Sep 2026, 11:52
sylware wrote:
Namely those "bits" will never be zero?

I'll dive in the next few days to remove it.
I don't see the problem. A single missing value out of 2^64 doesn't affect much at all. It is extremely unlikely to ever give all-zero even if it was working perfectly.

/dev/[u]random can still give all possible values, including all-zero, because it doesn't use the sources directly. This is true even if rdrand always gave all-zero all the time (i.e. it was completely broken). It is only used for seeding the entropy store. Linux does a good job of managing available randomness and accumulating it into the store.
Post 28 Sep 2026, 11:52
View user's profile Send private message Visit poster's website Reply with quote
sylware



Joined: 23 Oct 2020
Posts: 679
Location: Marseille/France
sylware 28 Sep 2026, 13:56
So AMD has not fixed this.

I'll try to have a deeper look in linux code.

But if it is using those 16bits, I'll probably patch this source away (unless there is a dynamic way to disable it). I run on Zen2.
Post 28 Sep 2026, 13:56
View user's profile Send private message Reply with quote
AsmGuru62



Joined: 28 Jan 2004
Posts: 1827
Location: Toronto, Canada
AsmGuru62 28 Sep 2026, 19:05
Interesting fact: I use the WELL512 generator to generate 32-bit values.
In the run of 30,453,443,826 numbers (test took around 10 minutes):
- 0x00000000 was generated 6 times
- 0xFFFFFFFF was generated 7 times
So, all zero bits are sometimes possible.
Post 28 Sep 2026, 19:05
View user's profile Send private message Send e-mail Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 21154
Location: In your JS exploiting you and your system
revolution 28 Sep 2026, 19:13
AsmGuru62 wrote:
So, all zero bits are sometimes possible.
As it should be.

LFSRs are famous for not outputting zeros, ever; or outputting zeros always, depending upon the value of the state variable.
Post 28 Sep 2026, 19:13
View user's profile Send private message Visit poster's website Reply with quote
Jessé



Joined: 03 May 2025
Posts: 155
Location: Brazil
Jessé 29 Sep 2026, 07:55
sylware wrote:
So AMD has not fixed this.

I'll try to have a deeper look in linux code.

But if it is using those 16bits, I'll probably patch this source away (unless there is a dynamic way to disable it). I run on Zen2.


revolution wrote:
sylware wrote:
Namely those "bits" will never be zero?

I'll dive in the next few days to remove it.
I don't see the problem. A single missing value out of 2^64 doesn't affect much at all. It is extremely unlikely to ever give all-zero even if it was working perfectly.

/dev/[u]random can still give all possible values, including all-zero, because it doesn't use the sources directly. This is true even if rdrand always gave all-zero all the time (i.e. it was completely broken). It is only used for seeding the entropy store. Linux does a good job of managing available randomness and accumulating it into the store.


For any explicit size, either 16, 32, or 64 bit will never be zero on single 'rdrand' or 'rdseed' instructions on affected processors. I think my bad english mislead people elsewhere, but this is the real extent of the problem: any explicit size, rdrand or rdseed alone outputs no zero. Not only 16 bit. Also affects 32 and 64 bit. However, human lives are too short for us to see a zero on a 64-bit random number anyway; maybe, universe lifespan is also too short...

So, don't overestimate or exaggerate this issue as something that will compromise the entire world (as some people are already doing): this is a problem with something that, in practice, has an almost non existent impact on anything (including security). But it is a problem...

Example below shows how easy is to get zero again from such "problematic" sources, also working for good RNG units unmodified (example below will generate zeroes regardless if its sources are good or bad), once one developing already knows that zero could be a problem:

Code:
format ELF64

include 'fastcall.inc'
include 'stdio.inc'

_data   flags           db 0

_code   Start entry:    xor     ebx, ebx
                        xor     ebp, ebp

                        signal(SIGINT, .break);

                        fprintf(stdout, "Starting rdrand + rdseed number polling..."\n \
                                "Press 'CTRL-C' to quit..."\n);
                        fflush(stdout);

            .iterate:   inc     rbp

                @@      pause
                        test    [flags], 1
                        jnz     .end
                        rdrand  ax
                        jnc     @b

                @@      pause
                        test    [flags], 1
                        jnz     .end
                        rdseed  r9w
                        jnc     @b

                        xor     r9w, ax         ; Combining two sources heals any sick RNG result
                        lea     rcx, [rbx+1]
                        cmovz   rbx, rcx
                        jz      @1f

                        mov     r8, 10'000
                        lea     rax, [rbp-1]
                        cqo
                        div     r8
                        test    rdx, rdx
                        jnz     @f

                @1      wait
                        push    1'000'000
                        push    rbp
                        fninit
                        fild    qword [rsp]
                        fidiv   dword [rsp+8]
                        fstp    tword [rsp]
                        fprintf(stdout, <27,"[2K",13,"Iterations: %.2LfM ", \
                                "| Found %lu zeroes so far... ",0>, rbx);
                        fflush(stdout);
                        add     rsp, 16

                @@      usleep(100);
                        test    [flags], 1
                        jz      .iterate

            .end:       fprintf(stdout, <27,"[2K",13, \
                                "Iterations done: %lu Found: %lu zeroes.",10,0>, rbp, rbx);
                        exit(0);

            .break:     lock or [flags], 1
                        ret
    


Just doing an 'xor' between two numbers already balance chaos back to normal. But, very important, one must know about the problem to fix it with one of the endless possible corrections (this is one tiny example).

This example blends both enthropic sources into one result, knowing that they follow different standards, which creates its own "quality" (maybe).

In conclusion: a problem is a problem, but there are always solutions for those who start by recognize and admit the problem first. AMD, I'm looking at you! Is never too late to properly reply to that long forgotten (by you) e-mail I've sent 3 days after spotting this... 😅

Cheers, and long live assembly!


Description: Binaries about example code for the adventurous one that wants to test this.
Download
Filename: rand_seed.tar.gz
Filesize: 2.41 KB
Downloaded: 31 Time(s)


_________________
jesse6
Post 29 Sep 2026, 07:55
View user's profile Send private message Visit poster's website Reply with quote
bitRAKE



Joined: 21 Jul 2003
Posts: 4640
Location: vpcmpistri
bitRAKE 29 Sep 2026, 08:11
I think this is by design, but AMD didn't document it.
Many generators expect their state to not be zero.

There was a paper in the last couple years about merging low-quality random sources being sufficient, but I couldn't find it. It was an argument for keeping low quality sources separate until needed, iirc.

Edit: found it.

Explicit two-source extractors and resilient functions
By Eshan Chattopadhyay and David Zuckerman


Last edited by bitRAKE on 29 Sep 2026, 10:14; edited 1 time in total
Post 29 Sep 2026, 08:11
View user's profile Send private message Visit poster's website Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 21154
Location: In your JS exploiting you and your system
revolution 29 Sep 2026, 08:21
Jessé wrote:
However, human lives are too short for us to see a zero on a 64-bit random number anyway; ...
The same is true for any chosen value.

All of these values below are unlikely to be seen over any short timespan.
Code:
0x9334adf7fd8de20b
0x0000000000000000
0x2abb73ef25d9f42a
0x5cc5071b10f88726
0xffffffffffffffff
0xc9341623dcdee0b0    
Jessé wrote:
... maybe, universe lifespan is also too short...
Assuming the RNG is functioning correctly then given enough CPUs generating enough values then all values are expected to be seen eventually. "Eventually" isn't as long as one might think.

To generate 2^64 outputs at 1M/s in one year would only require 585k CPUs.
Code:
2⁶⁴÷(1×10⁶×365×24×60×60) = 584942.4...    
Post 29 Sep 2026, 08:21
View user's profile Send private message Visit poster's website Reply with quote
sylware



Joined: 23 Oct 2020
Posts: 679
Location: Marseille/France
sylware 29 Sep 2026, 11:42
My system is very quiet, I wonder what are the other sources which have a "higher priority" than the AMD hardware.

Need to gather motivation and energy to dive into linux to look for that.

EDIT:

Here is what I *think* I understood:

This is a multi-stage RNG generator: the core stage will gather the core entropy bits, to generate some blake2s hash based data, which in turn will be used for some chacha state generation which will finally be used to generate actual random bytes.

(in my memory, chacha should be avoided)

Some linux sub-components do chain core entropy bits.

As I said my system is very quiet, and very "periodic", namely the sources actually running on my system are really NOT convincing: input, interrupts, and some static data from my hardware (TPM chips have a RNG source and is used if present, and many "server" related sub-components).

But the base of the core entropy bits, the ones fed to blake2s, well well well... everything is from AMD RNG and in the case it has no RNG data available, it uses the hardware time stamp counter or in the worst case scenario the basic monotonic clock ("fallback").

So I believe my RNG depends mostly on AMD RNG more than anything else (I don't really believe in any super butterfly effect from that combination of blake2s and chacha on my poor linux sub-component RNG sources).

But the unit of the data is a long, namely 64bits, then not getting ever a zero should have only a neglictible impact.

BUT, if my AMD RNG cannot keep up (speed) producing enough bytes and if there is some greedy RNG consumer (for instance a very RNG based game), the core of my RNG might be basically the monotonic CPU timestamp counter.......

Anybody generating a crypto key with that is fired Smile
Post 29 Sep 2026, 11:42
View user's profile Send private message Reply with quote
Display posts from previous:
Post new topic Reply to topic

Jump to:  
Goto page Previous  1, 2

< Last Thread | Next Thread >
Forum Rules:
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Copyright © 1999-2026, Tomasz Grysztar. Also on GitHub, YouTube.

Website powered by rwasa.