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);