Commit ae8d50f211 for openssl.org

commit ae8d50f211231c8aa9fcb3bd0a6c38604dbd6de4
Author: Bob Beck <beck@openssl.org>
Date:   Mon Mar 2 11:46:39 2026 -0700

    Make X509_up_ref and X509_free take const X509 *

    Reviewed-by: Neil Horman <nhorman@openssl.org>
    Reviewed-by: Matt Caswell <matt@openssl.org>
    Reviewed-by: Saša NedvÄ›dický <sashan@openssl.org>
    Reviewed-by: Tomas Mraz <tomas@openssl.org>
    MergeDate: Wed Mar  4 16:43:39 2026
    (Merged from https://github.com/openssl/openssl/pull/30235)

diff --git a/crypto/x509/x509_set.c b/crypto/x509/x509_set.c
index cd196ff4ca..3551fb6514 100644
--- a/crypto/x509/x509_set.c
+++ b/crypto/x509/x509_set.c
@@ -113,11 +113,11 @@ int X509_set_pubkey(X509 *x, EVP_PKEY *pkey)
     return 1;
 }

-int X509_up_ref(X509 *x)
+int X509_up_ref(const X509 *x)
 {
     int i;

-    if (CRYPTO_UP_REF(&x->references, &i) <= 0)
+    if (CRYPTO_UP_REF(&((X509 *)x)->references, &i) <= 0)
         return 0;

     REF_PRINT_COUNT("X509", i, x);
diff --git a/crypto/x509/x_x509.c b/crypto/x509/x_x509.c
index 08435a4517..701937b4c6 100644
--- a/crypto/x509/x_x509.c
+++ b/crypto/x509/x_x509.c
@@ -129,7 +129,7 @@ ASN1_SEQUENCE_ref(X509, x509_cb) = {
     ASN1_EMBED(X509, signature, ASN1_BIT_STRING)
 } ASN1_SEQUENCE_END_ref(X509, X509)

-IMPLEMENT_ASN1_FUNCTIONS(X509)
+IMPLEMENT_ASN1_FUNCTIONS_CONSTFREE(X509)
 IMPLEMENT_ASN1_DUP_FUNCTION(X509)

 /*
diff --git a/doc/man3/X509_new.pod b/doc/man3/X509_new.pod
index 264767e834..d8dd1179cc 100644
--- a/doc/man3/X509_new.pod
+++ b/doc/man3/X509_new.pod
@@ -14,8 +14,8 @@ OSSL_STACK_OF_X509_free

  X509 *X509_new(void);
  X509 *X509_new_ex(OSSL_LIB_CTX *libctx, const char *propq);
- void X509_free(X509 *a);
- int X509_up_ref(X509 *a);
+ void X509_free(const X509 *a);
+ int X509_up_ref(const X509 *a);
  STACK_OF(X509) *X509_chain_up_ref(STACK_OF(X509) *x);
  void OSSL_STACK_OF_X509_free(STACK_OF(X509) *certs);

diff --git a/doc/man7/ossl-guide-migration.pod b/doc/man7/ossl-guide-migration.pod
index 37d5d4c187..8fc148f9be 100644
--- a/doc/man7/ossl-guide-migration.pod
+++ b/doc/man7/ossl-guide-migration.pod
@@ -169,6 +169,8 @@ X509_to_X509_REQ
 X509_TRUST_add
 X509v3_addr_validate_resource_set
 X509v3_asid_validate_resource_set
+X509_up_ref
+X509_free

 The following two functions we "un-constified" As they were documented as returning
 an explicitly mutable pointer from within an B<X509> object:
diff --git a/include/openssl/asn1t.h.in b/include/openssl/asn1t.h.in
index ca8bbc2517..aff58e0577 100644
--- a/include/openssl/asn1t.h.in
+++ b/include/openssl/asn1t.h.in
@@ -775,6 +775,8 @@ typedef struct ASN1_STREAM_ARG_st {

 #define IMPLEMENT_ASN1_FUNCTIONS(stname) IMPLEMENT_ASN1_FUNCTIONS_fname(stname, stname, stname)

+#define IMPLEMENT_ASN1_FUNCTIONS_CONSTFREE(stname) IMPLEMENT_ASN1_FUNCTIONS_fname_constfree(stname, stname, stname)
+
 #define IMPLEMENT_ASN1_FUNCTIONS_name(stname, itname) IMPLEMENT_ASN1_FUNCTIONS_fname(stname, itname, itname)

 #define IMPLEMENT_ASN1_FUNCTIONS_ENCODE_name(stname, itname) \
@@ -806,10 +808,24 @@ typedef struct ASN1_STREAM_ARG_st {
         ASN1_item_free((ASN1_VALUE *)a, ASN1_ITEM_rptr(itname));    \
     }

+#define IMPLEMENT_ASN1_ALLOC_FUNCTIONS_fname_constfree(stname, itname, fname) \
+    stname *fname##_new(void)                                                 \
+    {                                                                         \
+        return (stname *)ASN1_item_new(ASN1_ITEM_rptr(itname));               \
+    }                                                                         \
+    void fname##_free(const stname *a)                                        \
+    {                                                                         \
+        ASN1_item_free((ASN1_VALUE *)a, ASN1_ITEM_rptr(itname));              \
+    }
+
 #define IMPLEMENT_ASN1_FUNCTIONS_fname(stname, itname, fname)    \
     IMPLEMENT_ASN1_ENCODE_FUNCTIONS_fname(stname, itname, fname) \
     IMPLEMENT_ASN1_ALLOC_FUNCTIONS_fname(stname, itname, fname)

+#define IMPLEMENT_ASN1_FUNCTIONS_fname_constfree(stname, itname, fname) \
+    IMPLEMENT_ASN1_ENCODE_FUNCTIONS_fname(stname, itname, fname)        \
+    IMPLEMENT_ASN1_ALLOC_FUNCTIONS_fname_constfree(stname, itname, fname)
+
 #define IMPLEMENT_ASN1_ENCODE_FUNCTIONS_fname(stname, itname, fname)                       \
     stname *d2i_##fname(stname **a, const unsigned char **in, long len)                    \
     {                                                                                      \
diff --git a/include/openssl/safestack.h.in b/include/openssl/safestack.h.in
index 42c3ec95ff..f81dd58888 100644
--- a/include/openssl/safestack.h.in
+++ b/include/openssl/safestack.h.in
@@ -35,50 +35,6 @@ extern "C" {

 #define STACK_OF(type) struct stack_st_##type

-/* Helper macro for internal use */
-#define SKM_DEFINE_STACK_OF_INTERNAL(t1, t2, t3)                                                                         \
-    STACK_OF(t1);                                                                                                        \
-    typedef int (*sk_##t1##_compfunc)(const t3 *const *a, const t3 *const *b);                                           \
-    typedef void (*sk_##t1##_freefunc)(t3 * a);                                                                          \
-    typedef t3 *(*sk_##t1##_copyfunc)(const t3 *a);                                                                      \
-    static ossl_inline void sk_##t1##_freefunc_thunk(OPENSSL_sk_freefunc freefunc_arg, void *ptr)                        \
-    {                                                                                                                    \
-        sk_##t1##_freefunc freefunc = (sk_##t1##_freefunc)freefunc_arg;                                                  \
-        freefunc((t3 *)ptr);                                                                                             \
-    }                                                                                                                    \
-    static ossl_inline int sk_##t1##_cmpfunc_thunk(int (*cmp)(const void *, const void *), const void *a, const void *b) \
-    {                                                                                                                    \
-        int (*realcmp)(const t3 *const *a, const t3 *const *b) = (int (*)(const t3 *const *a, const t3 *const *b))(cmp); \
-        const t3 *const *at = (const t3 *const *)a;                                                                      \
-        const t3 *const *bt = (const t3 *const *)b;                                                                      \
-                                                                                                                         \
-        return realcmp(at, bt);                                                                                          \
-    }                                                                                                                    \
-    static ossl_unused ossl_inline t2 *ossl_check_##t1##_type(t2 *ptr)                                                   \
-    {                                                                                                                    \
-        return ptr;                                                                                                      \
-    }                                                                                                                    \
-    static ossl_unused ossl_inline const OPENSSL_STACK *ossl_check_const_##t1##_sk_type(const STACK_OF(t1) *sk)          \
-    {                                                                                                                    \
-        return (const OPENSSL_STACK *)sk;                                                                                \
-    }                                                                                                                    \
-    static ossl_unused ossl_inline OPENSSL_STACK *ossl_check_##t1##_sk_type(STACK_OF(t1) *sk)                            \
-    {                                                                                                                    \
-        return (OPENSSL_STACK *)sk;                                                                                      \
-    }                                                                                                                    \
-    static ossl_unused ossl_inline OPENSSL_sk_compfunc ossl_check_##t1##_compfunc_type(sk_##t1##_compfunc cmp)           \
-    {                                                                                                                    \
-        return (OPENSSL_sk_compfunc)cmp;                                                                                 \
-    }                                                                                                                    \
-    static ossl_unused ossl_inline OPENSSL_sk_copyfunc ossl_check_##t1##_copyfunc_type(sk_##t1##_copyfunc cpy)           \
-    {                                                                                                                    \
-        return (OPENSSL_sk_copyfunc)cpy;                                                                                 \
-    }                                                                                                                    \
-    static ossl_unused ossl_inline OPENSSL_sk_freefunc ossl_check_##t1##_freefunc_type(sk_##t1##_freefunc fr)            \
-    {                                                                                                                    \
-        return (OPENSSL_sk_freefunc)fr;                                                                                  \
-    }
-
 #define SKM_DEFINE_STACK_OF(t1, t2, t3)                                                                                    \
     STACK_OF(t1);                                                                                                          \
     typedef int (*sk_##t1##_compfunc)(const t3 *const *a, const t3 *const *b);                                             \
diff --git a/include/openssl/x509.h.in b/include/openssl/x509.h.in
index b5a104a3aa..84d65269cc 100644
--- a/include/openssl/x509.h.in
+++ b/include/openssl/x509.h.in
@@ -571,7 +571,11 @@ DECLARE_ASN1_FUNCTIONS(X509_NAME)
 int X509_NAME_set(X509_NAME **xn, const X509_NAME *name);

 DECLARE_ASN1_FUNCTIONS(X509_CINF)
-DECLARE_ASN1_FUNCTIONS(X509)
+X509 *X509_new(void);
+void X509_free(const X509 *a);
+X509 *d2i_X509(X509 **a, const unsigned char **in, long len);
+int i2d_X509(const X509 *a, unsigned char **out);
+const ASN1_ITEM *X509_it(void);
 X509 *X509_new_ex(OSSL_LIB_CTX *libctx, const char *propq);
 DECLARE_ASN1_FUNCTIONS(X509_CERT_AUX)

@@ -672,7 +676,7 @@ int X509_set1_notBefore(X509 *x, const ASN1_TIME *tm);
 const ASN1_TIME *X509_get0_notAfter(const X509 *x);
 ASN1_TIME *X509_getm_notAfter(X509 *x);
 int X509_set1_notAfter(X509 *x, const ASN1_TIME *tm);
-int X509_up_ref(X509 *x);
+int X509_up_ref(const X509 *x);
 int X509_get_signature_type(const X509 *x);

 #ifndef OPENSSL_NO_DEPRECATED_1_1_0
diff --git a/util/libcrypto.num b/util/libcrypto.num
index 6b83d53b62..b694a335c5 100644
--- a/util/libcrypto.num
+++ b/util/libcrypto.num
@@ -4495,11 +4495,6 @@ i2d_X509_CINF                           ?	4_0_0	EXIST::FUNCTION:
 X509_CINF_free                          ?	4_0_0	EXIST::FUNCTION:
 X509_CINF_new                           ?	4_0_0	EXIST::FUNCTION:
 X509_CINF_it                            ?	4_0_0	EXIST::FUNCTION:
-d2i_X509                                ?	4_0_0	EXIST::FUNCTION:
-i2d_X509                                ?	4_0_0	EXIST::FUNCTION:
-X509_free                               ?	4_0_0	EXIST::FUNCTION:
-X509_new                                ?	4_0_0	EXIST::FUNCTION:
-X509_it                                 ?	4_0_0	EXIST::FUNCTION:
 X509_new_ex                             ?	4_0_0	EXIST::FUNCTION:
 d2i_X509_CERT_AUX                       ?	4_0_0	EXIST::FUNCTION:
 i2d_X509_CERT_AUX                       ?	4_0_0	EXIST::FUNCTION:
@@ -5714,3 +5709,8 @@ OSSL_ENCODER_CTX_ctrl_string            ?	4_0_0	EXIST::FUNCTION:
 OPENSSL_sk_set_cmp_thunks               ?	4_0_0	EXIST::FUNCTION:
 ASN1_BIT_STRING_set1                    ?	4_0_0	EXIST::FUNCTION:
 OSSL_ESS_check_signing_certs_ex         ?	4_0_0	EXIST::FUNCTION:
+X509_new                                ?	4_0_0	EXIST::FUNCTION:
+X509_free                               ?	4_0_0	EXIST::FUNCTION:
+d2i_X509                                ?	4_0_0	EXIST::FUNCTION:
+i2d_X509                                ?	4_0_0	EXIST::FUNCTION:
+X509_it                                 ?	4_0_0	EXIST::FUNCTION:
diff --git a/util/perl/OpenSSL/stackhash.pm b/util/perl/OpenSSL/stackhash.pm
index 0c7c129705..9ef5a338af 100644
--- a/util/perl/OpenSSL/stackhash.pm
+++ b/util/perl/OpenSSL/stackhash.pm
@@ -10,7 +10,6 @@ package OpenSSL::stackhash;

 use strict;
 use warnings;
-
 require Exporter;
 our @ISA = qw(Exporter);
 our @EXPORT_OK = qw(generate_stack_macros generate_const_stack_macros
@@ -23,9 +22,51 @@ sub generate_stack_macros_int {
     my $nametype = shift;
     my $realtype = shift;
     my $plaintype = shift;
-
+    my $const_free = "";
+    if ($nametype eq "X509") {
+	$const_free = "const ";
+    }
     my $macros = <<END_MACROS;
-SKM_DEFINE_STACK_OF_INTERNAL(${nametype}, ${realtype}, ${plaintype})
+STACK_OF(${nametype});
+typedef int (*sk_${nametype}_compfunc)(const ${plaintype} *const *a, const ${plaintype} *const *b);
+typedef void (*sk_${nametype}_freefunc)(${const_free}${plaintype} *a);
+typedef ${plaintype} *(*sk_${nametype}_copyfunc)(const ${plaintype} *a);
+static ossl_inline void sk_${nametype}_freefunc_thunk(OPENSSL_sk_freefunc freefunc_arg, void *ptr)
+{
+    sk_${nametype}_freefunc freefunc = (sk_${nametype}_freefunc)freefunc_arg;
+    freefunc((${const_free}${plaintype} *)ptr);
+}
+static ossl_inline int sk_${nametype}_cmpfunc_thunk(int (*cmp)(const void *, const void *), const void *a, const void *b)
+{
+    int (*realcmp)(const ${plaintype} *const *a, const ${plaintype} *const *b) = (int (*)(const ${plaintype} *const *a, const ${plaintype} *const *b))(cmp);
+    const ${plaintype} *const *at = (const ${plaintype} *const *)a;
+    const ${plaintype} *const *bt = (const ${plaintype} *const *)b;
+    return realcmp(at, bt);
+}
+static ossl_unused ossl_inline ${realtype} *ossl_check_${nametype}_type(${realtype} *ptr)
+{
+    return ptr;
+}
+static ossl_unused ossl_inline const OPENSSL_STACK *ossl_check_const_${nametype}_sk_type(const STACK_OF(${nametype}) *sk)
+{
+    return (const OPENSSL_STACK *)sk;
+}
+static ossl_unused ossl_inline OPENSSL_STACK *ossl_check_${nametype}_sk_type(STACK_OF(${nametype}) *sk)
+{
+    return (OPENSSL_STACK *)sk;
+}
+static ossl_unused ossl_inline OPENSSL_sk_compfunc ossl_check_${nametype}_compfunc_type(sk_${nametype}_compfunc cmp)
+{
+    return (OPENSSL_sk_compfunc)cmp;
+}
+static ossl_unused ossl_inline OPENSSL_sk_copyfunc ossl_check_${nametype}_copyfunc_type(sk_${nametype}_copyfunc cpy)
+{
+    return (OPENSSL_sk_copyfunc)cpy;
+}
+static ossl_unused ossl_inline OPENSSL_sk_freefunc ossl_check_${nametype}_freefunc_type(sk_${nametype}_freefunc fr)
+{
+    return (OPENSSL_sk_freefunc)fr;
+}
 #define sk_${nametype}_num(sk) OPENSSL_sk_num(ossl_check_const_${nametype}_sk_type(sk))
 #define sk_${nametype}_value(sk, idx) ((${realtype} *)OPENSSL_sk_value(ossl_check_const_${nametype}_sk_type(sk), (idx)))
 #define sk_${nametype}_new(cmp) ((STACK_OF(${nametype}) *)OPENSSL_sk_set_cmp_thunks(OPENSSL_sk_new(ossl_check_${nametype}_compfunc_type(cmp)), sk_${nametype}_cmpfunc_thunk))