Commit 171e4e7eb6 for openssl.org

commit 171e4e7eb64f243f78a1040f725ed80a31df7702
Author: Jakub Zelenka <jakub.zelenka@openssl.foundation>
Date:   Tue Jul 7 16:37:49 2026 +0200

    rand: add mfail tests for generation and seeding

    Add memory-failure injection coverage to rand_test for the full
    RAND_bytes_ex/RAND_priv_bytes_ex stack on a fresh library context, the
    SEED-SRC entropy acquisition and the CTR-DRBG operations with a
    TEST-RAND parent.  The cipher fetches used by the DRBG setup are warmed
    up outside the injection window so that the injection targets the RAND
    machinery itself.

    Assisted-by: Claude:claude-fable-5

    Reviewed-by: Paul Dale <paul.dale@oracle.com>
    Reviewed-by: Nikola Pajkovsky <nikolap@openssl.org>
    MergeDate: Fri Jul 10 15:47:16 2026
    (Merged from https://github.com/openssl/openssl/pull/31885)

diff --git a/test/rand_test.c b/test/rand_test.c
index 0fbd89542f..e5c889a9e9 100644
--- a/test/rand_test.c
+++ b/test/rand_test.c
@@ -275,6 +275,142 @@ err:
     return res;
 }

+/* Warm up the DRBG cipher fetch caches outside the mfail injection window */
+static int rand_drbg_fetch_warmup(EVP_RAND *drbg_alg)
+{
+    EVP_RAND_CTX *warm;
+    OSSL_PARAM params[3];
+    int ret;
+
+    params[0] = OSSL_PARAM_construct_utf8_string(OSSL_DRBG_PARAM_CIPHER,
+        (char *)"AES-256-CTR", 0);
+    params[1] = OSSL_PARAM_construct_utf8_string(OSSL_PROV_PARAM_CORE_PROV_NAME,
+        (char *)"default", 0);
+    params[2] = OSSL_PARAM_construct_end();
+
+    if (!TEST_ptr(warm = EVP_RAND_CTX_new(drbg_alg, NULL)))
+        return 0;
+    ret = TEST_true(EVP_RAND_CTX_set_params(warm, params));
+    EVP_RAND_CTX_free(warm);
+    return ret;
+}
+
+/*
+ * Memory-failure coverage for the whole random generation stack on a fresh
+ * library context: the seed source and DRBG chain creation and seeding.
+ */
+static int test_rand_bytes_mfail(int idx)
+{
+    OSSL_LIB_CTX *ctx = NULL;
+    EVP_RAND *drbg = NULL, *seed = NULL;
+    unsigned char buf[16];
+    int rc = -1;
+
+    if (!TEST_ptr(ctx = OSSL_LIB_CTX_new())
+        || !TEST_ptr(drbg = EVP_RAND_fetch(ctx, "CTR-DRBG", NULL))
+        || !rand_drbg_fetch_warmup(drbg))
+        goto end;
+    /* The default seed source may be unavailable in some configurations */
+    ERR_set_mark();
+    seed = EVP_RAND_fetch(ctx, "SEED-SRC", NULL);
+    ERR_pop_to_mark();
+
+    MFAIL_start();
+    rc = (idx == 0 ? RAND_bytes_ex(ctx, buf, sizeof(buf), 0)
+                   : RAND_priv_bytes_ex(ctx, buf, sizeof(buf), 0))
+        > 0;
+    MFAIL_end();
+
+end:
+    EVP_RAND_free(seed);
+    EVP_RAND_free(drbg);
+    OSSL_LIB_CTX_free(ctx);
+    return rc;
+}
+
+/* Memory-failure coverage for the seed source entropy acquisition. */
+static int test_rand_seed_src_mfail(void)
+{
+    OSSL_LIB_CTX *ctx = NULL;
+    EVP_RAND *rand = NULL;
+    EVP_RAND_CTX *seed = NULL;
+    unsigned char buf[64];
+    int rc = -1;
+
+    if (!TEST_ptr(ctx = OSSL_LIB_CTX_new())
+        || !TEST_ptr(rand = EVP_RAND_fetch(ctx, "SEED-SRC", NULL)))
+        goto end;
+
+    MFAIL_start();
+    rc = (seed = EVP_RAND_CTX_new(rand, NULL)) != NULL
+        && EVP_RAND_instantiate(seed, 0, 0, NULL, 0, NULL)
+        && EVP_RAND_generate(seed, buf, sizeof(buf), 0, 0, NULL, 0);
+    MFAIL_end();
+
+end:
+    EVP_RAND_CTX_free(seed);
+    EVP_RAND_free(rand);
+    OSSL_LIB_CTX_free(ctx);
+    return rc;
+}
+
+/* Memory-failure coverage for the DRBG operations with a TEST-RAND parent */
+static int test_rand_drbg_mfail(void)
+{
+    OSSL_LIB_CTX *ctx = NULL;
+    EVP_RAND *parent_alg = NULL, *drbg_alg = NULL;
+    EVP_RAND_CTX *parent = NULL, *drbg = NULL;
+    unsigned int strength = 256, generate = 1;
+    unsigned char entropy[128];
+    unsigned char buf[32];
+    OSSL_PARAM parent_params[4], drbg_params[3];
+    size_t i;
+    int rc = -1;
+
+    for (i = 0; i < sizeof(entropy); i++)
+        entropy[i] = 0xff & i;
+    parent_params[0] = OSSL_PARAM_construct_uint(OSSL_RAND_PARAM_STRENGTH,
+        &strength);
+    parent_params[1] = OSSL_PARAM_construct_uint(OSSL_RAND_PARAM_GENERATE,
+        &generate);
+    parent_params[2] = OSSL_PARAM_construct_octet_string(
+        OSSL_RAND_PARAM_TEST_ENTROPY, entropy, sizeof(entropy));
+    parent_params[3] = OSSL_PARAM_construct_end();
+
+    drbg_params[0] = OSSL_PARAM_construct_utf8_string(OSSL_DRBG_PARAM_CIPHER,
+        (char *)"AES-256-CTR", 0);
+    drbg_params[1] = OSSL_PARAM_construct_utf8_string(
+        OSSL_PROV_PARAM_CORE_PROV_NAME, (char *)"default", 0);
+    drbg_params[2] = OSSL_PARAM_construct_end();
+
+    if (!TEST_ptr(ctx = OSSL_LIB_CTX_new())
+        || !TEST_ptr(parent_alg = EVP_RAND_fetch(ctx, "TEST-RAND", NULL))
+        || !TEST_ptr(drbg_alg = EVP_RAND_fetch(ctx, "CTR-DRBG", NULL))
+        || !rand_drbg_fetch_warmup(drbg_alg)
+        || !TEST_ptr(parent = EVP_RAND_CTX_new(parent_alg, NULL))
+        || !TEST_true(EVP_RAND_instantiate(parent, 0, 0, NULL, 0,
+            parent_params)))
+        goto end;
+
+    MFAIL_start();
+    rc = (drbg = EVP_RAND_CTX_new(drbg_alg, parent)) != NULL
+        && EVP_RAND_instantiate(drbg, 0, 0, (unsigned char *)"abc", 3,
+            drbg_params)
+        && EVP_RAND_generate(drbg, buf, sizeof(buf), 0, 0, NULL, 0)
+        && EVP_RAND_reseed(drbg, 0, NULL, 0, (unsigned char *)"xyz", 3)
+        && EVP_RAND_generate(drbg, buf, sizeof(buf), 0, 0,
+            (unsigned char *)"adin", 4);
+    MFAIL_end();
+
+end:
+    EVP_RAND_CTX_free(drbg);
+    EVP_RAND_CTX_free(parent);
+    EVP_RAND_free(parent_alg);
+    EVP_RAND_free(drbg_alg);
+    OSSL_LIB_CTX_free(ctx);
+    return rc;
+}
+
 int setup_tests(void)
 {
     if (!test_skip_common_options()) {
@@ -301,5 +437,9 @@ int setup_tests(void)
     if (!OSSL_PROVIDER_available(NULL, "fips")
         || fips_provider_version_ge(NULL, 3, 5, 1))
         ADD_TEST(test_rand_get0_primary);
+
+    ADD_MFAIL_ALL_TESTS(test_rand_bytes_mfail, 2);
+    ADD_MFAIL_TEST(test_rand_seed_src_mfail);
+    ADD_MFAIL_TEST(test_rand_drbg_mfail);
     return 1;
 }