Commit 97e3ce610b for openssl.org

commit 97e3ce610ba8b3610f572fcf510e26869ba0e710
Author: jdymitarai <o10040115@gmail.com>
Date:   Sat Oct 17 16:27:33 2026 +0900

    crypto/x509/x509_vpm.c: copy string verify parameters as strings

    When X509_VERIFY_PARAM_inherit() duplicates verification parameter
    buffer stacks through replace_buffer_stack(), all the entries were
    copied using buffer_from_bytes() since commit f584ae959cbc "Let's
    support multiple names for certificate verification".  Because
    buffer_from_bytes() allocates only the exact byte count
    via OPENSSL_memdup() without an extra trailing byte
    for NUL-termination, string entries originally created
    by buffer_from_string() (such as hostnames and email addresses)
    lost their trailing NUL byte upon being copied.

    When X509_STORE_CTX_print_verify_cb() or other consumers read these
    copied entries as C strings through X509_VERIFY_PARAM_get0_host()
    or X509_VERIFY_PARAM_get0_email(), callers like printf() or BIO_printf()
    read past the allocated buffer bounds, triggering an AddressSanitizer
    heap-buffer-overflow.

    Differentiate buffer stack duplication in X509_VERIFY_PARAM_inherit():
    duplicate string-backed fields (hosts, rfc822s, smtputf8s) using
    buffer_copy_string(), and duplicate byte-backed fields (ips) using
    buffer_copy_bytes().  Add test_vpm_copy_string_nul_term()
    to verify_extra_test to guard against regressions.

    Resolves: https://github.com/openssl/openssl/issues/33236
    Fixes: f584ae959cbc "Let's support multiple names for certificate verification"
    Assisted-by: Gemini:gemini-3.8-flash
    Reviewed-by: Tomas Mraz <tomas@openssl.foundation>
    Reviewed-by: Nikola Pajkovsky <nikolap@openssl.org>
    Reviewed-by: Eugene Syromiatnikov <esyr@openssl.org>
    Merge-date: Wed Oct 28 11:10:37 2026
    Merged-from: https://github.com/openssl/openssl/pull/33238

diff --git a/crypto/x509/x509_vpm.c b/crypto/x509/x509_vpm.c
index 44b116923b..ebc969c35f 100644
--- a/crypto/x509/x509_vpm.c
+++ b/crypto/x509/x509_vpm.c
@@ -78,11 +78,16 @@ err:
     return ret;
 }

-static X509_BUFFER *buffer_copy(const X509_BUFFER *b)
+static X509_BUFFER *buffer_copy_bytes(const X509_BUFFER *b)
 {
     return buffer_from_bytes(b->data, b->len);
 }

+static X509_BUFFER *buffer_copy_string(const X509_BUFFER *b)
+{
+    return buffer_from_string(b->data, b->len);
+}
+
 static void buffer_free(X509_BUFFER *b)
 {
     if (b == NULL)
@@ -91,13 +96,26 @@ static void buffer_free(X509_BUFFER *b)
     OPENSSL_free(b);
 }

-static int replace_buffer_stack(STACK_OF(X509_BUFFER) **dest,
+static int replace_buffer_stack_bytes(STACK_OF(X509_BUFFER) **dest,
+    STACK_OF(X509_BUFFER) *const *src)
+{
+    sk_X509_BUFFER_pop_free(*dest, buffer_free);
+    *dest = NULL;
+    if (*src != NULL) {
+        *dest = sk_X509_BUFFER_deep_copy(*src, buffer_copy_bytes, buffer_free);
+        if (*dest == NULL)
+            return 0;
+    }
+    return 1;
+}
+
+static int replace_buffer_stack_string(STACK_OF(X509_BUFFER) **dest,
     STACK_OF(X509_BUFFER) *const *src)
 {
     sk_X509_BUFFER_pop_free(*dest, buffer_free);
     *dest = NULL;
     if (*src != NULL) {
-        *dest = sk_X509_BUFFER_deep_copy(*src, buffer_copy, buffer_free);
+        *dest = sk_X509_BUFFER_deep_copy(*src, buffer_copy_string, buffer_free);
         if (*dest == NULL)
             return 0;
     }
@@ -466,25 +484,25 @@ int X509_VERIFY_PARAM_inherit(X509_VERIFY_PARAM *dest,
     x509_verify_param_copy(hostflags, 0);

     if (test_x509_verify_param_copy(hosts, NULL)) {
-        if (!replace_buffer_stack(&dest->hosts, &src->hosts))
+        if (!replace_buffer_stack_string(&dest->hosts, &src->hosts))
             return 0;
     }
     x509_verify_param_copy(validate_host, NULL);

     if (test_x509_verify_param_copy(ips, NULL)) {
-        if (!replace_buffer_stack(&dest->ips, &src->ips))
+        if (!replace_buffer_stack_bytes(&dest->ips, &src->ips))
             return 0;
     }
     x509_verify_param_copy(validate_ip, NULL);

     if (test_x509_verify_param_copy(rfc822s, NULL)) {
-        if (!replace_buffer_stack(&dest->rfc822s, &src->rfc822s))
+        if (!replace_buffer_stack_string(&dest->rfc822s, &src->rfc822s))
             return 0;
     }
     x509_verify_param_copy(validate_rfc822, NULL);

     if (test_x509_verify_param_copy(smtputf8s, NULL)) {
-        if (!replace_buffer_stack(&dest->smtputf8s, &src->smtputf8s))
+        if (!replace_buffer_stack_string(&dest->smtputf8s, &src->smtputf8s))
             return 0;
     }
     x509_verify_param_copy(validate_smtputf8, NULL);
diff --git a/test/verify_extra_test.c b/test/verify_extra_test.c
index 474f87b4c9..9021e31a5a 100644
--- a/test/verify_extra_test.c
+++ b/test/verify_extra_test.c
@@ -680,6 +680,41 @@ err:
     return ret;
 }

+static int test_vpm_copy_string_nul_term(void)
+{
+    X509_VERIFY_PARAM *src = NULL, *dest = NULL;
+    const char *test_host = "test.example.com";
+    const char *test_email = "user@example.com";
+    const char *ret_host, *ret_email;
+    int ret = 0;
+
+    if (!TEST_ptr(src = X509_VERIFY_PARAM_new())
+        || !TEST_ptr(dest = X509_VERIFY_PARAM_new()))
+        goto err;
+
+    if (!TEST_true(X509_VERIFY_PARAM_set1_host(src, test_host, 0))
+        || !TEST_true(X509_VERIFY_PARAM_set1_email(src, test_email, 0)))
+        goto err;
+
+    if (!TEST_true(X509_VERIFY_PARAM_set1(dest, src)))
+        goto err;
+
+    if (!TEST_ptr(ret_host = X509_VERIFY_PARAM_get0_host(dest, 0))
+        || !TEST_str_eq(ret_host, test_host))
+        goto err;
+
+    if (!TEST_ptr(ret_email = X509_VERIFY_PARAM_get0_email(dest))
+        || !TEST_str_eq(ret_email, test_email))
+        goto err;
+
+    ret = 1;
+
+err:
+    X509_VERIFY_PARAM_free(src);
+    X509_VERIFY_PARAM_free(dest);
+    return ret;
+}
+
 static int test_self_signed_good(void)
 {
     return test_self_signed(root_f, 1, 1);
@@ -1078,6 +1113,7 @@ int setup_tests(void)
     ADD_TEST(test_purpose_any);
     ADD_TEST(test_multiname_selfsigned);
     ADD_TEST(test_vpm_input_validation);
+    ADD_TEST(test_vpm_copy_string_nul_term);
     ADD_ALL_TESTS(test_pathlen_chain, OSSL_NELEM(pathlen_chains));
     return 1;
 err: