Commit 0afbd6bfb3 for openssl.org

commit 0afbd6bfb3f6e95c6039c509135ff26b3b946502
Author: Simo Sorce <simo@redhat.com>
Date:   Thu Dec 4 23:14:47 2025 -0500

    Add dependency handling for FIPS self-tests

    Some FIPS Known Answer Tests (KATs) rely on other cryptographic algorithms
    that also have their own KATs. This change introduces a formal mechanism to
    ensure these dependencies are met before a test is run.

    A `depends_on` field is added to the self-test definition to declare
    prerequisites. A new recursive function, `FIPS_kat_deferred_execute`,
    traverses this dependency chain, executing any required tests first.

    This new logic also prevents tests from being run multiple times if they are a
    dependency for several other tests. The `FIPS_kat_deferred` function is
    updated to use this new dependency-aware execution function.

    Signed-off-by: Simo Sorce <simo@redhat.com>

    Reviewed-by: Dmitry Belyavskiy <beldmit@gmail.com>
    (Merged from https://github.com/openssl/openssl/pull/29222)

diff --git a/providers/fips/fipsprov.c b/providers/fips/fipsprov.c
index 933125a9b9..3808eb5ad5 100644
--- a/providers/fips/fipsprov.c
+++ b/providers/fips/fipsprov.c
@@ -1315,6 +1315,43 @@ static void deferred_deinit(void)
     }
 }

+static int FIPS_kat_deferred_execute(OSSL_SELF_TEST *st, OSSL_LIB_CTX *libctx,
+    self_test_id_t id)
+{
+    /*
+     * Dependency chains may cause a test to be referenced multiple times
+     * immediately return if any state is present
+     */
+    if (st_all_tests[id].state != SELF_TEST_STATE_INIT)
+        return 1;
+
+    /* Mark test as in progress */
+    st_all_tests[id].state = SELF_TEST_STATE_IN_PROGRESS;
+
+    /* check if there are dependent tests to run */
+    if (st_all_tests[id].depends_on) {
+        for (int i = 0; st_all_tests[id].depends_on[i] != ST_ID_MAX; i++) {
+            self_test_id_t dep_id = st_all_tests[id].depends_on[i];
+
+            FIPS_kat_deferred_execute(st, libctx, dep_id);
+            switch (st_all_tests[dep_id].state) {
+            case SELF_TEST_STATE_PASSED:
+            case SELF_TEST_STATE_IN_PROGRESS:
+                continue;
+            default:
+                return 0;
+            }
+        }
+    }
+
+    /* may have already been run as a dependency, recheck before executing */
+    if (st_all_tests[id].state == SELF_TEST_STATE_IN_PROGRESS)
+        if (!SELF_TEST_kats_single(st, libctx, id))
+            return 0;
+
+    return 1;
+}
+
 static int FIPS_kat_deferred(OSSL_LIB_CTX *libctx, self_test_id_t id)
 {
     int *rt = NULL;
@@ -1373,7 +1410,7 @@ static int FIPS_kat_deferred(OSSL_LIB_CTX *libctx, self_test_id_t id)

         /* mark that we are executing a test on the local thread */
         if (!CRYPTO_THREAD_set_local_ex(CRYPTO_THREAD_LOCAL_FIPS_DEFERRED_KEY,
-                libctx, (void *)0xC001))
+                libctx, rt))
             goto done;

         unset_key = true;
@@ -1384,19 +1421,9 @@ static int FIPS_kat_deferred(OSSL_LIB_CTX *libctx, self_test_id_t id)
         if ((st = OSSL_SELF_TEST_new(cb, cb_arg)) == NULL)
             goto done;

-        /*
-         * Dependency chains may cause a test to be referenced multiple times
-         * immediately return if any state is present
-         */
-        if (st_all_tests[id].state == SELF_TEST_STATE_INIT) {
-
-            /* Mark test as in progress */
-            st_all_tests[id].state = SELF_TEST_STATE_IN_PROGRESS;
-
-            /* execute test */
-            if (!SELF_TEST_kats_single(st, libctx, id))
-                goto done;
-        }
+        /* Handles dependencies via recursion */
+        if (!(ret = FIPS_kat_deferred_execute(st, libctx, id)))
+            goto done;

         /*
          * now mark as passed all the algorithms that have been executed by
@@ -1482,7 +1509,7 @@ int ossl_deferred_self_test(OSSL_LIB_CTX *libctx, self_test_id_t id)
      * in FIPS_kat_deferred() so this race is of no real consequence.
      */
     ret = FIPS_kat_deferred(libctx, id);
-    if (!ret)
+    if (!ret || st_all_tests[id].state == SELF_TEST_STATE_FAILED)
         deferred_test_error(st_all_tests[id].category);
     return ret;
 }
diff --git a/providers/fips/self_test.h b/providers/fips/self_test.h
index 2c19653b66..cff506915e 100644
--- a/providers/fips/self_test.h
+++ b/providers/fips/self_test.h
@@ -163,6 +163,7 @@ typedef struct self_test_st {
         ST_KAT_KAS kas;
         ST_KAT_DRBG drbg;
     } u;
+    self_test_id_t *depends_on;
 } ST_DEFINITION;

 extern ST_DEFINITION st_all_tests[ST_ID_MAX];