[PATCH 2/2] Override drivers/char/random only after FIPS-mode RNGs become available

Jay Wang
2025-06-28T04:29:51Z

This commit fixes a timing issue introduced in the previous commit
"crypto: rng - Override drivers/char/random in FIPS mode" where the crypto RNG
was attempting to override the drivers/char/random interface before the default
RNG became available. The previous implementation would immediately register the
external RNG during module initialization, which could fail if the default RNG
wasn't ready.

Changes:
- Introduce workqueue-based initialization for FIPS mode
- Add crypto_rng_register_work_func() to wait for default RNG availability
- Move random_register_extrng() call to the work function with proper error handling

This ensures the crypto ext RNG is properly registered only after all dependencies
are satisfied, preventing potential boot failures in FIPS-enabled environments.

Cc: stable@vger.kernel.org
Signed-off-by: Jay Wang <wanjay@amazon.com>
---
crypto/rng.c | 25 +++++++++++++++++++++++--
1 file changed, 23 insertions(+), 2 deletions(-)

diff --git a/crypto/rng.c b/crypto/rng.c
index cdba806846e2..250166d67fd0 100644
--- a/crypto/rng.c
+++ b/crypto/rng.c
@@ -22,6 +22,7 @@
#include <linux/sched/signal.h>
#include <linux/slab.h>
#include <linux/string.h>
+#include <linux/workqueue.h>
#include <net/netlink.h>

#include "internal.h"
@@ -273,15 +274,35 @@ static const struct random_extrng crypto_devrandom_rng = {
.owner = THIS_MODULE,
};

+static struct work_struct crypto_rng_register_work;
+
+static void crypto_rng_register_work_func(struct work_struct *work)
+{
+ /* Wait until default rng becomes avaiable, then
+ Overwrite the extrng.
+ */
+ int ret = crypto_get_default_rng();
+ if (ret){
+ printk(KERN_ERR "crypto_rng: Failed to get default RNG (error %d)\n", ret);
+ return;
+ }
+ printk(KERN_INFO "Overwrite extrng\n");
+ random_register_extrng(&crypto_devrandom_rng);
+}
+
static int __init crypto_rng_init(void)
{
- if (fips_enabled)
- random_register_extrng(&crypto_devrandom_rng);
+ if (fips_enabled) {
+ INIT_WORK(&crypto_rng_register_work, crypto_rng_register_work_func);
+ schedule_work(&crypto_rng_register_work);
+ }
+
return 0;
}

static void __exit crypto_rng_exit(void)
{
+ cancel_work_sync(&crypto_rng_register_work);
random_unregister_extrng();
}

--
2.47.1