Commit b86b1098fd for openssl.org

commit b86b1098fd7ec47e1c9d43a7566bf4e287b1d390
Author: Eugene Syromiatnikov <esyr@openssl.org>
Date:   Wed Oct 7 15:14:31 2026 +0200

    crypto/x509/x509_lu.c: avoid freeing X509_OBJECTs in X509_STORE_get0_objects()

    The implementation of hash table conversion in commit 04589b59ef50
    "x509store: reduce lock contention in X509_STORE" introduced a potential
    use-after-free condition when X509_STORE_get0_objects() is called during
    X.509 verification.  Avoid that by re-using existing X509_OBJECTs
    instead of duplicating them.

    Resolves: https://github.com/openssl/openssl/issues/33063
    Fixes: 04589b59ef50 "x509store: reduce lock contention in X509_STORE"
    Signed-off-by: Eugene Syromiatnikov <esyr@openssl.org>
    Reviewed-by: Mounir Idrassi <mounir.idrassi@idrix.fr>
    Reviewed-by: Nikola Pajkovsky <nikolap@openssl.org>
    Merge-date: Tue Oct 13 13:45:39 2026
    Merged-from: https://github.com/openssl/openssl/pull/33147

diff --git a/crypto/x509/x509_lu.c b/crypto/x509/x509_lu.c
index e166110c9b..08c116bf07 100644
--- a/crypto/x509/x509_lu.c
+++ b/crypto/x509/x509_lu.c
@@ -761,47 +761,83 @@ static X509_OBJECT *x509_object_dup(const X509_OBJECT *obj)
     return ret;
 }

-static int obj_ht_foreach_object(HT_VALUE *v, void *arg)
+#ifndef OPENSSL_NO_DEPRECATED_4_0
+static int obj_ht_to_stack(HT_VALUE *v, void *arg)
 {
     STACK_OF(X509_OBJECT) **sk = arg;
     STACK_OF(X509_OBJECT) *objs = v->value;
-    X509_OBJECT *dup, *obj;
+    X509_OBJECT *obj;
     int i;

     for (i = 0; i < sk_X509_OBJECT_num(objs); i++) {
         obj = sk_X509_OBJECT_value(objs, i);
-        dup = x509_object_dup(obj);
-        if (dup == NULL)
-            goto err;
-        if (sk_X509_OBJECT_push(*sk, dup) == 0)
+        if (sk_X509_OBJECT_push(*sk, obj) == 0)
             goto err;
     }

     return 1;

 err:
-    X509_OBJECT_free(dup);
-    sk_X509_OBJECT_pop_free(*sk, X509_OBJECT_free);
-    *sk = NULL;
+    sk_X509_OBJECT_zero(*sk);

     return 0;
 }

-#ifndef OPENSSL_NO_DEPRECATED_4_0
+static int obj_ht_zero_stacks(HT_VALUE *v, void *arg)
+{
+    STACK_OF(X509_OBJECT) *objs = v->value;
+
+    sk_X509_OBJECT_zero(objs);
+
+    return 1;
+}
+
 STACK_OF(X509_OBJECT) *X509_STORE_get0_objects(const X509_STORE *xs)
 {
     X509_STORE *store = (X509_STORE *)xs;

     if (xs->objs_ht != NULL) {
-        ossl_ht_foreach_until(xs->objs_ht, obj_ht_foreach_object, &store->objs);
-        ossl_ht_free(xs->objs_ht);
-        store->objs_ht = NULL;
+        ossl_ht_foreach_until(xs->objs_ht, obj_ht_to_stack, &store->objs);
+        if (sk_X509_OBJECT_num(store->objs) > 0
+            || ossl_ht_count(xs->objs_ht) == 0) {
+            ossl_ht_foreach_until(xs->objs_ht, obj_ht_zero_stacks, NULL);
+            ossl_ht_free(xs->objs_ht);
+            store->objs_ht = NULL;
+        } else {
+            return NULL;
+        }
     }

     return xs->objs;
 }
 #endif

+static int obj_ht_foreach_object(HT_VALUE *v, void *arg)
+{
+    STACK_OF(X509_OBJECT) **sk = arg;
+    STACK_OF(X509_OBJECT) *objs = v->value;
+    X509_OBJECT *dup, *obj;
+    int i;
+
+    for (i = 0; i < sk_X509_OBJECT_num(objs); i++) {
+        obj = sk_X509_OBJECT_value(objs, i);
+        dup = x509_object_dup(obj);
+        if (dup == NULL)
+            goto err;
+        if (sk_X509_OBJECT_push(*sk, dup) == 0)
+            goto err;
+    }
+
+    return 1;
+
+err:
+    X509_OBJECT_free(dup);
+    sk_X509_OBJECT_pop_free(*sk, X509_OBJECT_free);
+    *sk = NULL;
+
+    return 0;
+}
+
 STACK_OF(X509_OBJECT) *X509_STORE_get1_objects(X509_STORE *store)
 {
     STACK_OF(X509_OBJECT) *objs;
diff --git a/doc/man3/X509_STORE_get0_param.pod b/doc/man3/X509_STORE_get0_param.pod
index cb0cd40d9d..411fd26db7 100644
--- a/doc/man3/X509_STORE_get0_param.pod
+++ b/doc/man3/X509_STORE_get0_param.pod
@@ -55,7 +55,8 @@ X509_STORE_set1_param() returns 1 for success and 0 for failure.
 X509_STORE_get1_objects() returns a pointer to a stack of the retrieved
 objects on success, else NULL.

-X509_STORE_get0_objects() returns a pointer to a stack of B<X509_OBJECT>.
+X509_STORE_get0_objects() returns a pointer to a stack of B<X509_OBJECT>
+or NULL if the stack construction has failed.

 X509_STORE_get1_all_certs() returns a pointer to a stack of the retrieved
 certificates on success, else NULL.