Commit 64f09bd826 for openssl.org

commit 64f09bd826e8fdefae67aea68ec1bfacefa1d5f4
Author: Nikola Pajkovsky <nikolap@openssl.org>
Date:   Tue Jun 30 08:55:09 2026 +0200

    crypto/x509/x509_lu.c: fix memory leak in obj_ht_foreach_object()

    when sk_X509_OBJECT_push() fails after x509_object_dup() has already
    allocated the duplicate, the dup is neither stored on the destination
    stack nor freed: the error path only pop_free()s the stack the dup was
    never pushed onto, so it is leaked.

    Set env ASAN_OPTIONS in test explicitly to detect_leaks=1 to force
    ASAN to fail the test. Otherwise, the test reports ok even with valid
    leak.

    Fixes: 08cecb4448e9 "Add X509_STORE_get1_objects"
    Fixes: https://github.com/openssl/openssl/issues/31771
    Signed-off-by: Nikola Pajkovsky <nikolap@openssl.org>

    Reviewed-by: Norbert Pocs <norbertp@openssl.org>
    Reviewed-by: Bob Beck <beck@openssl.org>
    MergeDate: Tue Jul  7 07:42:17 2026
    (Merged from https://github.com/openssl/openssl/pull/31784)

diff --git a/crypto/x509/x509_lu.c b/crypto/x509/x509_lu.c
index e9cc7145e3..01e1969d19 100644
--- a/crypto/x509/x509_lu.c
+++ b/crypto/x509/x509_lu.c
@@ -764,6 +764,7 @@ static int obj_ht_foreach_object(HT_VALUE *v, void *arg)
     return 1;

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

diff --git a/test/recipes/60-test_x509_load_cert_file.t b/test/recipes/60-test_x509_load_cert_file.t
index e329d7675c..15e9908ce1 100644
--- a/test/recipes/60-test_x509_load_cert_file.t
+++ b/test/recipes/60-test_x509_load_cert_file.t
@@ -8,6 +8,8 @@

 use OpenSSL::Test qw/:DEFAULT srctop_file/;

+$ENV{ASAN_OPTIONS} = "detect_leaks=1";
+
 setup("test_load_cert_file");

 plan tests => 1;
diff --git a/test/x509_load_cert_file_test.c b/test/x509_load_cert_file_test.c
index 0df654559a..f9656aaa31 100644
--- a/test/x509_load_cert_file_test.c
+++ b/test/x509_load_cert_file_test.c
@@ -198,6 +198,38 @@ err:
     return ret;
 }

+static int test_x509_get1_objects_mfail(void)
+{
+    X509 *cert1 = NULL, *cert2 = NULL;
+    X509_STORE *store = NULL;
+    STACK_OF(X509_OBJECT) *objs = NULL;
+    int ret = 0;
+
+    if (!TEST_ptr(cert1 = X509_from_strings(cn_cert1))
+        || !TEST_ptr(cert2 = X509_from_strings(cn_cert2)))
+        goto err;
+
+    store = X509_STORE_new();
+    if (!TEST_ptr(store))
+        goto err;
+    if (!TEST_true(X509_STORE_add_cert(store, cert1))
+        || !TEST_true(X509_STORE_add_cert(store, cert2)))
+        goto err;
+
+    MFAIL_start();
+    objs = X509_STORE_get1_objects(store);
+    MFAIL_end();
+
+    ret = (objs != NULL);
+
+err:
+    sk_X509_OBJECT_pop_free(objs, X509_OBJECT_free);
+    X509_STORE_free(store);
+    X509_free(cert1);
+    X509_free(cert2);
+    return ret;
+}
+
 OPT_TEST_DECLARE_USAGE("cert.pem [crl.pem]\n")

 int setup_tests(void)
@@ -216,6 +248,7 @@ int setup_tests(void)
     ADD_TEST(test_load_cert_file);
     ADD_TEST(test_load_same_cn_certs);
     ADD_MFAIL_TEST(test_x509_store_add_mfail);
+    ADD_MFAIL_TEST(test_x509_get1_objects_mfail);

     return 1;
 }