Commit afaa70c1a6 for openssl.org

commit afaa70c1a60f9e682a9b00323fc9bc7116d41614
Author: mat <mateiignat03@gmail.com>
Date:   Wed Apr 29 09:09:24 2026 +0300

    test: add regression test for ciphersuite_cb() with empty list elements

    Cover the three cases where CONF_parse_list() produces a NULL/empty
    element: leading separator, trailing separator, and consecutive
    separators (double colon).  Before the fix these would crash via a
    NULL memcpy inside ciphersuite_cb().

    Each case also verifies via SSL_CTX_get_ciphers() that the valid
    ciphersuite(s) in the same string were actually applied, not just
    that the call returned without crashing.

    Reviewed-by: Daniel Kubec <kubec@openssl.foundation>
    Reviewed-by: Eugene Syromiatnikov <esyr@openssl.org>
    MergeDate: Tue May 26 08:56:53 2026
    (Merged from https://github.com/openssl/openssl/pull/31023)

diff --git a/ssl/ssl_ciph.c b/ssl/ssl_ciph.c
index 80fa976f47..61e8c4abbe 100644
--- a/ssl/ssl_ciph.c
+++ b/ssl/ssl_ciph.c
@@ -1234,7 +1234,7 @@ static int ciphersuite_cb(const char *elem, int len, void *arg)
     /* Arbitrary sized temp buffer for the cipher name. Should be big enough */
     char name[80];

-    /* CONF_parse_list signals empty elements with elem==NULL; skip them */
+    /* CONF_parse_list signals empty elements with elem == NULL; skip them */
     if (elem == NULL || len == 0)
         return 1;

diff --git a/test/cipherlist_test.c b/test/cipherlist_test.c
index 9874e6bad6..a0e1704d49 100644
--- a/test/cipherlist_test.c
+++ b/test/cipherlist_test.c
@@ -258,11 +258,66 @@ end:
     return result;
 }

+/*
+ * SSL_CTX_set_ciphersuites() must not crash on empty list elements.
+ * CONF_parse_list() signals them with elem=NULL; ciphersuite_cb() must skip
+ * such entries rather than passing NULL to memcpy().
+ */
+#ifndef OPENSSL_NO_TLS1_3
+static int cipher_in_ctx(const SSL_CTX *ctx, uint32_t id)
+{
+    const STACK_OF(SSL_CIPHER) *sk = SSL_CTX_get_ciphers(ctx);
+    int i;
+
+    for (i = 0; i < sk_SSL_CIPHER_num(sk); i++)
+        if (SSL_CIPHER_get_id(sk_SSL_CIPHER_value(sk, i)) == id)
+            return 1;
+    return 0;
+}
+
+static int test_set_ciphersuites_empty_elem(void)
+{
+    SSL_CTX *ctx = NULL;
+    int result = 0;
+
+    if (!TEST_ptr(ctx = SSL_CTX_new(TLS_method())))
+        goto end;
+
+    /* Double colon: both surrounding valid suites must be applied */
+    if (!TEST_true(SSL_CTX_set_ciphersuites(ctx,
+            "TLS_AES_128_GCM_SHA256::TLS_AES_256_GCM_SHA384")))
+        goto end;
+    if (!TEST_true(cipher_in_ctx(ctx, TLS1_3_CK_AES_128_GCM_SHA256))
+        || !TEST_true(cipher_in_ctx(ctx, TLS1_3_CK_AES_256_GCM_SHA384)))
+        goto end;
+
+    /* Leading separator: empty first element, one valid suite must apply */
+    if (!TEST_true(SSL_CTX_set_ciphersuites(ctx, ":TLS_AES_128_GCM_SHA256")))
+        goto end;
+    if (!TEST_true(cipher_in_ctx(ctx, TLS1_3_CK_AES_128_GCM_SHA256)))
+        goto end;
+
+    /* Trailing separator: empty last element, one valid suite must apply */
+    if (!TEST_true(SSL_CTX_set_ciphersuites(ctx, "TLS_AES_128_GCM_SHA256:")))
+        goto end;
+    if (!TEST_true(cipher_in_ctx(ctx, TLS1_3_CK_AES_128_GCM_SHA256)))
+        goto end;
+
+    result = 1;
+end:
+    SSL_CTX_free(ctx);
+    return result;
+}
+#endif
+
 int setup_tests(void)
 {
     ADD_TEST(test_default_cipherlist_implicit);
     ADD_TEST(test_default_cipherlist_explicit);
     ADD_TEST(test_default_cipherlist_clear);
+#ifndef OPENSSL_NO_TLS1_3
+    ADD_TEST(test_set_ciphersuites_empty_elem);
+#endif
     ADD_TEST(test_stdname_cipherlist);
     return 1;
 }