Commit 4c49f3f110 for openssl.org

commit 4c49f3f1103fc731cf1ec64fec5b744fa031241e
Author: Minkyung Park <minkyungp@unist.ac.kr>
Date:   Tue Aug 4 02:05:46 2026 -0500

    Fix use-after-free in EC_GROUP_get_ecpkparameters()

    When a caller-supplied ECPKPARAMETERS object is passed in, the CHOICE
    member it already holds is released on entry, but the union pointer
    was left untouched.  If a later step then failed, the error exit called
    ECPKPARAMETERS_free() on that object, which followed the still valid
    selector and freed the already released member a second time.

    Clear the pointer once it has been released, so that the error exit
    has nothing left to free.

    This does not change the fact that a caller supplied object is freed
    on error.  That behaviour differs from the sibling function
    EC_GROUP_get_ecparameters(), which only frees the object it allocated
    itself, but it is left as-is here.

    The new test only fails on a sanitizer build;  the ECPKPARAMETERS layout
    is private to ec_asn1.c, so the state cannot be inspected directly.

    The regression test and the pull request description were written
    with AI assistance.

    Fixes: 012c86ab74ae "move ECC ASN1 that is not specific to ECDSA into crypto/ec/, and make some appropriate changes to the EC library."
    Co-authored-by: Jakub Zelenka <bukka@php.net>
    Assisted-by: Claude:claude-fable-5
    Reviewed-by: Jakub Zelenka <jakub.zelenka@openssl.foundation>
    Reviewed-by: Frederik Wedel-Heinen <fwh.openssl@gmail.com>
    Reviewed-by: Eugene Syromiatnikov <esyr@openssl.org>
    Merge-date: Sun Oct 18 15:26:51 2026
    Merged-from: https://github.com/openssl/openssl/pull/32169

diff --git a/crypto/ec/ec_asn1.c b/crypto/ec/ec_asn1.c
index ab9ab45ed8..3d32c13616 100644
--- a/crypto/ec/ec_asn1.c
+++ b/crypto/ec/ec_asn1.c
@@ -466,11 +466,14 @@ ECPKPARAMETERS *EC_GROUP_get_ecpkparameters(const EC_GROUP *group,
             return NULL;
         }
     } else {
-        if (ret->type == ECPKPARAMETERS_TYPE_NAMED)
+        if (ret->type == ECPKPARAMETERS_TYPE_NAMED) {
             ASN1_OBJECT_free(ret->value.named_curve);
-        else if (ret->type == ECPKPARAMETERS_TYPE_EXPLICIT
-            && ret->value.parameters != NULL)
+            ret->value.named_curve = NULL;
+        } else if (ret->type == ECPKPARAMETERS_TYPE_EXPLICIT
+            && ret->value.parameters != NULL) {
             ECPARAMETERS_free(ret->value.parameters);
+            ret->value.parameters = NULL;
+        }
     }

     if (EC_GROUP_get_asn1_flag(group) == OPENSSL_EC_NAMED_CURVE) {
diff --git a/test/ec_internal_test.c b/test/ec_internal_test.c
index 751bc9c2b9..287618b8cf 100644
--- a/test/ec_internal_test.c
+++ b/test/ec_internal_test.c
@@ -1,5 +1,5 @@
 /*
- * Copyright 2019-2025 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 2019-2026 The OpenSSL Project Authors. All Rights Reserved.
  *
  * Licensed under the Apache License 2.0 (the "License").  You may not use
  * this file except in compliance with the License.  You can obtain a copy
@@ -436,6 +436,41 @@ err:
     return testresult;
 }

+/*
+ * Test that reusing an ECPKPARAMETERS object for a call that fails does not
+ * touch the CHOICE member that was released on entry.
+ */
+static int ecpkparams_reuse_error_test(void)
+{
+    EC_GROUP *grp = NULL;
+    ECPKPARAMETERS *params = NULL;
+    int testresult = 0;
+
+    if (!TEST_ptr(grp = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)))
+        goto err;
+
+    /* Populate the explicit parameters arm of the CHOICE. */
+    EC_GROUP_set_asn1_flag(grp, OPENSSL_EC_EXPLICIT_CURVE);
+    if (!TEST_ptr(params = EC_GROUP_get_ecpkparameters(grp, NULL)))
+        goto err;
+
+    /* Take the named curve arm and make it fail on the undefined NID. */
+    EC_GROUP_set_curve_name(grp, NID_undef);
+    EC_GROUP_set_asn1_flag(grp, OPENSSL_EC_NAMED_CURVE);
+
+    /* On failure the passed in object is consumed, so do not free it again. */
+    params = EC_GROUP_get_ecpkparameters(grp, params);
+    if (!TEST_ptr_null(params))
+        goto err;
+    testresult = 1;
+
+err:
+    ECPKPARAMETERS_free(params);
+    EC_GROUP_free(grp);
+
+    return testresult;
+}
+
 static int ecpkparams_i2d2i_test(int n)
 {
     EC_GROUP *g1 = NULL, *g2 = NULL;
@@ -556,6 +591,7 @@ int setup_tests(void)
 #endif
     ADD_TEST(set_private_key);
     ADD_TEST(decoded_flag_test);
+    ADD_TEST(ecpkparams_reuse_error_test);
     ADD_ALL_TESTS(ecpkparams_i2d2i_test, (int)crv_len);
     ADD_TEST(named_group_creation_test);