Repository: bitcoin/bitcoin (language: cpp)

Issue:
GetRandBytes() Hangs on Samsung Galaxy S25 and OnePlus 13
### Is there an existing issue for this?

- [x] I have searched the existing issues

### Current behaviour

We have been using Bitcoin Core natively in the Nunchuk Bitcoin wallet, available on both desktop and mobile. On Samsung Galaxy S25 and OnePlus 13 (both running Android 15 with the SM8750-AB Snapdragon 8 Elite 3nm processor), the app fails to initialize due to an issue in `GetRandBytes()`.

The problem occurs when `GetRandBytes()` calls `GetRNDRRS()`, resulting in an infinite loop without retrieving entropy from the hardware.

```
uint64_t GetRNDRRS() noexcept
{
    uint8_t ok;
    uint64_t r1;
    do {
        // https://developer.arm.com/documentation/ddi0601/2022-12/AArch64-Registers/RNDRRS--Reseeded-Random-Number
        __asm__ volatile("mrs %0, s3_3_c2_c4_1; cset %w1, ne;"
                         : "=r"(r1), "=r"(ok)::"cc");
        if (ok) break;
        __asm__ volatile("yield");
    } while (true);
    return r1;
}
```

Build with: Android NDK 25.1.8937393 & 27.2.12479018

Related PR: https://github.com/bitcoin/bitcoin/pull/26839

Tested on commit: `57b47c47ef0bd36e1c32d709c62998c51dc76f34`

### Expected behaviour

`GetRandBytes()` should return entropy without hanging.

### Steps to reproduce

- Build Bitcoin Core for Android on master or 57b47c47ef0bd36e1c32d709c62998c51dc76f34
- Call `GetRandBytes`
- Function gets stuck

### Relevant log output

_No response_

### How did you obtain Bitcoin Core

Compiled from source

### What version of Bitcoin Core are you using?

master@57b47c47ef0bd36e1c32d709c62998c51dc76f34

### Operating system and version

Android 15

### Machine specifications

_No response_


Discussion / hints:

Fix patch:
diff --git a/src/random.cpp b/src/random.cpp
index 5b605e988d0c5..6acbbcc210a15 100644
--- a/src/random.cpp
+++ b/src/random.cpp
@@ -192,11 +192,13 @@ uint64_t GetRdSeed() noexcept
 #elif defined(__aarch64__) && defined(HWCAP2_RNG)
 
 bool g_rndr_supported = false;
+bool g_rndrrs_supported = false;
 
 void InitHardwareRand()
 {
     if (getauxval(AT_HWCAP2) & HWCAP2_RNG) {
         g_rndr_supported = true;
+        g_rndrrs_supported = VerifyRNDRRS();
     }
 }
 
@@ -204,8 +206,10 @@ void ReportHardwareRand()
 {
     // This must be done in a separate function, as InitHardwareRand() may be indirectly called
     // from global constructors, before logging is initialized.
-    if (g_rndr_supported) {
+    if (g_rndr_supported && g_rndrrs_supported) {
         LogPrintf("Using RNDR and RNDRRS as additional entropy sources\n");
+    } else if (g_rndr_supported) {
+        LogPrintf("Using RNDR as an additional entropy source\n");
     }
 }
 
@@ -227,24 +231,43 @@ uint64_t GetRNDR() noexcept
     return r1;
 }
 
-/** Read 64 bits of entropy using rndrrs.
- *
+// Helper function to retrieve random value using RNDRRS
+bool GetRNDRRSInternal(uint64_t &r1) noexcept
+{
+    uint8_t ok = 0;
+    __asm__ volatile("mrs %0, s3_3_c2_c4_1; cset %w1, ne;"
+                     : "=r"(r1), "=r"(ok)::"cc");
+    return ok != 0;
+}
+
+
+/** Read 64 bits of entropy using RNDRRS.
  * Must only be called when RNDRRS is supported.
  */
 uint64_t GetRNDRRS() noexcept
 {
-    uint8_t ok = 0;
     uint64_t r1;
-    do {
-        // https://developer.arm.com/documentation/ddi0601/2022-12/AArch64-Registers/RNDRRS--Reseeded-Random-Number
-        __asm__ volatile("mrs %0, s3_3_c2_c4_1; cset %w1, ne;"
-                         : "=r"(r1), "=r"(ok)::"cc");
-        if (ok) break;
+    while (!GetRNDRRSInternal(r1)) {
         __asm__ volatile("yield");
-    } while (true);
+    }
     return r1;
 }
 
+/** Verify if RNDRRS is supported and functional.
+ * Return true if it works within the retry limit.
+ */
+bool VerifyRNDRRS() noexcept
+{
+    uint64_t test;
+    for (int retry = 0; retry < 10; ++retry) {
+        if (GetRNDRRSInternal(test)) {
+            return true;
+        }
+        __asm__ volatile("yield");
+    }
+    return false;
+}
+
 #else
 /* Access to other hardware random number generators could be added here later,
  * assuming it is sufficiently fast (in the order of a few hundred CPU cycles).
@@ -295,7 +318,7 @@ void SeedHardwareSlow(CSHA512& hasher) noexcept {
         return;
     }
 #elif defined(__aarch64__) && defined(HWCAP2_RNG)
-    if (g_rndr_supported) {
+    if (g_rndrrs_supported) {
         for (int i = 0; i < 4; ++i) {
             uint64_t out = GetRNDRRS();
             hasher.Write((const unsigned char*)&out, sizeof(out));


Test patch: