Commit 8e7db02686 for openssl.org
commit 8e7db026861afeec4bbd18842e2d87a95ee5217f
Author: Bob Beck <beck@openssl.org>
Date: Sun Sep 6 01:15:52 2026 -0600
Discard the cached encoding when it cannot be saved
ossl_asn1_enc_save() frees the old encoding before allocating its
replacement. A zero input length or a failed allocation left the
item with a NULL encoding, its old length and a clear modified
flag, so ossl_asn1_enc_restore() accepted the cache and copied
from a NULL pointer.
Mark the encoding stale before attempting the save, so a failure
leaves the item with no cached encoding and the next encode is
built from its fields.
Reviewed-by: Mounir Idrassi <mounir.idrassi@idrix.fr>
Reviewed-by: Nikola Pajkovsky <nikolap@openssl.org>
Merge-date: Thu Sep 17 16:44:29 2026
Merged-from: https://github.com/openssl/openssl/pull/32686
diff --git a/crypto/asn1/tasn_utl.c b/crypto/asn1/tasn_utl.c
index ab1bb67b30..e0bab2dec7 100644
--- a/crypto/asn1/tasn_utl.c
+++ b/crypto/asn1/tasn_utl.c
@@ -169,11 +169,14 @@ int ossl_asn1_enc_save(ASN1_VALUE **pval, const unsigned char *in, long inlen,
if (enc == NULL)
return 1;
+ /* A failure below leaves the item without a cached encoding */
OPENSSL_free(enc->enc);
- if (inlen <= 0) {
- enc->enc = NULL;
+ enc->enc = NULL;
+ enc->len = 0;
+ enc->modified = 1;
+
+ if (inlen <= 0)
return 0;
- }
if ((enc->enc = OPENSSL_malloc(inlen)) == NULL)
return 0;
memcpy(enc->enc, in, inlen);
diff --git a/test/x509_internal_test.c b/test/x509_internal_test.c
index 31b2bdd9eb..1e91b5bbd4 100644
--- a/test/x509_internal_test.c
+++ b/test/x509_internal_test.c
@@ -20,6 +20,7 @@
#include "internal/nelem.h"
#include "crypto/x509.h"
#include "crypto/evp.h"
+#include "../crypto/asn1/asn1_local.h"
/**********************************************************************
*
@@ -285,6 +286,61 @@ err:
return ret;
}
+/*
+ * A failed encoding save discards the cached encoding, and the item is
+ * encoded from its fields afterwards.
+ */
+static int test_enc_save_failure(void)
+{
+ EVP_PKEY *pkey = NULL;
+ X509_NAME *name = NULL;
+ X509_CRL *crl = NULL, *copy = NULL;
+ X509_CRL_INFO *info = NULL;
+ ASN1_TIME *tm = NULL;
+ unsigned char *der = NULL, *der_copy = NULL;
+ unsigned char buf[1] = { 0 };
+ int len, len_copy, ret = 0;
+
+ if (!TEST_ptr(pkey = EVP_PKEY_Q_keygen(NULL, NULL, "RSA", (size_t)2048))
+ || !TEST_ptr(name = X509_NAME_new())
+ || !TEST_true(X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC,
+ (const unsigned char *)"enc save test", -1, -1, 0))
+ || !TEST_ptr(tm = ASN1_TIME_set(NULL, 0))
+ || !TEST_ptr(crl = X509_CRL_new())
+ || !TEST_true(X509_CRL_set_issuer_name(crl, name))
+ || !TEST_true(X509_CRL_set1_lastUpdate(crl, tm))
+ || !TEST_int_gt(X509_CRL_sign(crl, pkey, EVP_sha256()), 0)
+ || !TEST_ptr(copy = X509_CRL_dup(crl))
+ || !TEST_false(copy->crl.enc.modified)
+ || !TEST_ptr(copy->crl.enc.enc))
+ goto err;
+
+ /* A zero input length is a failure */
+ info = ©->crl;
+ if (!TEST_false(ossl_asn1_enc_save((ASN1_VALUE **)&info, buf, 0,
+ ASN1_ITEM_rptr(X509_CRL_INFO)))
+ || !TEST_ptr_null(copy->crl.enc.enc)
+ || !TEST_int_eq(copy->crl.enc.len, 0)
+ || !TEST_true(copy->crl.enc.modified))
+ goto err;
+
+ if (!TEST_int_gt(len = i2d_X509_CRL(crl, &der), 0)
+ || !TEST_int_gt(len_copy = i2d_X509_CRL(copy, &der_copy), 0)
+ || !TEST_mem_eq(der, (size_t)len, der_copy, (size_t)len_copy))
+ goto err;
+
+ ret = 1;
+err:
+ OPENSSL_free(der_copy);
+ OPENSSL_free(der);
+ X509_CRL_free(copy);
+ X509_CRL_free(crl);
+ ASN1_TIME_free(tm);
+ X509_NAME_free(name);
+ EVP_PKEY_free(pkey);
+ return ret;
+}
+
static int ck_purp(ossl_unused const X509_PURPOSE *purpose,
ossl_unused const X509 *x, int ca)
{
@@ -1223,6 +1279,7 @@ int setup_tests(void)
ADD_ALL_TESTS(test_a2i_ipaddress, OSSL_NELEM(a2i_ipaddress_tests));
ADD_ALL_TESTS(test_ipaddr_to_asc, OSSL_NELEM(ipaddr_to_asc_tests));
ADD_TEST(test_crl_add_ext_modifies);
+ ADD_TEST(test_enc_save_failure);
ADD_TEST(tests_X509_PURPOSE);
ADD_TEST(tests_X509_check_time);
ADD_TEST(tests_X509_check_crypto);