‹ BackHN Continuity

Thread

AMD's random number generator can't generate a 0?

288 points · 220 comments · BruceEel

  1. jstanley · · focus · HN ↗
    This is not the first RNG bug on Zen 2, I recall after I first got mine that some application or other would quit immediately at startup because rdrand always returned -1, i.e. all 1s. It was fixed with a microcode update.

    Do we now learn that they fixed "always generate all 1s" with "never generate all 0s"??

    EDIT: I've been unable to reproduce the problem on my CPU, FWIW. It's a Ryzen 5 3600.

    EDIT2: OK, update, I can reproduce it with rdrand16, rdrand32 is fine but rdrand16 can never generate all 0s. So my CPU does have this problem!

    1. 0x000xca0xfe · · focus · HN ↗
      I can reproduce it too with rdrand16 on Zen2.

      But it looks like the rdrand16 instruction can produce zeros just fine, it just sets CF=0 erroneously (indicating an error and that the user program should retry).

      So keep that in mind when you try to reproduce it too and use some abstraction that could implement retries internally.

      1. dooglius · · focus · HN ↗
        Good observation, that seems like the most likely explanation. Do you ever see "true" CF=0 (with nonzero arg) or did they just take the lazy approach?
        1. 0x000xca0xfe · · focus · HN ↗
          No, CF=0 occurences seem to be happen frequently and uniformely distributed like valid results at ~1/65536, not clustered. Under a minute-long all-core load CF=0 always produces zero, but that's to be expected according to the manual.

          Here are some stats:

              Rounds (N): 1000000000
              Failed (F): 15312
              Valid  (V): 999984688
              N/65536: 15258.789
              V/65536: 15258.555
              Failed, result was zero: 15312
              Failed, result non-zero: 0
              Bucket value for      0: 15312
              Bucket value for      1: 15290
              Bucket value for  65535: 15223
              Min bucket value: 14670
              Max bucket value: 15835
          
          I used this C program to collect them:

              #include <stdio.h>
              #include <stdint.h>
              #include <stdbool.h>
          
              const size_t N = 1000000000; // 1e9
          
              struct rdrand16_result {
                  uint16_t n;
                  bool ok;
              };
          
              static inline struct rdrand16_result rdrand16()
              {
                  struct rdrand16_result result;
                  __asm__ __volatile__( "rdrand %0" : "=r" (result.n), "=@ccc" (result.ok) );
                  return result;
              }
          
              int main()
              {
                  size_t buckets[0xFFFF + 1] = { 0 };
                  size_t notok = 0, notok_zero = 0, notok_nonz = 0;
                  for (size_t i = 0; i < N; ++i) {
                      struct rdrand16_result result = rdrand16();
                      ++buckets[result.n];
                      if (! result.ok) {
                          ++notok;
                          notok_zero += result.n == 0;
                          notok_nonz += result.n != 0;
                      }
                  }
                  size_t max = 0, min = N;
                  for (size_t i = 0; i <= 0xFFFF; ++i) {
                      size_t n = buckets[i];
                      min = n < min ? n : min;
                      max = n > max ? n : max;
                  }
                  printf("Rounds (N): %zu\n", N);
                  printf("Failed (F): %zu\n", notok);
                  printf("Valid  (V): %zu\n", N - notok);
                  printf("N/65536: %.3f\n", (double)N / 65536);
                  printf("V/65536: %.3f\n", (double)(N - notok) / 65536);
                  printf("Failed, result was zero: %zu\n", notok_zero);
                  printf("Failed, result non-zero: %zu\n", notok_nonz);
                  printf("Bucket value for      0: %zu\n", buckets[0]);
                  printf("Bucket value for      1: %zu\n", buckets[1]);
                  printf("Bucket value for  65535: %zu\n", buckets[0xFFFF]);
                  printf("Min bucket value: %zu\n", min);
                  printf("Max bucket value: %zu\n", max);
                  return 0;
              }
          1. eigenform · · focus · HN ↗
            > but that's to be expected according to the manual

            Confusingly, the AMD programming manual (Rev. 3.38 - July 2026) only explicitly states this ("that the result is always zero when CF=0") in the description of RDSEED, but the Intel SDM mentions this in the description of both instructions.

Open on Hacker News to reply ↗

Unofficial Hacker News client; not affiliated with Y Combinator.