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 = &copy->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);