aboutsummaryrefslogtreecommitdiffstats
path: root/test/unit
diff options
context:
space:
mode:
authorMounir IDRASSI <[email protected]>2026-08-12 11:53:22 +0200
committerTomas Mraz <[email protected]>2026-10-21 18:24:43 +0200
commitced1ca3a0be6f1c4d512805cba16ad4655004fd5 (patch)
tree8d2287d56a276b48842c4e926a0dc0d03ad63b08 /test/unit
parentAdds a test that tests missing integrity only ciphers effect on TLS cipher su... (diff)
downloadopenssl-ced1ca3a0be6f1c4d512805cba16ad4655004fd5.tar.gz
openssl-ced1ca3a0be6f1c4d512805cba16ad4655004fd5.zip
ssl: handle session ID cache lock failure
SSL_has_matching_session_id() returns 0 both when no matching session exists and when the cache lock cannot be acquired. Its use in the built-in generator's retry loop and the final uniqueness check can therefore treat a lock failure as proof that no conflict exists. Add an internal tri-state lookup. The final uniqueness check uses it as a backstop for IDs produced by both built-in and application callbacks, recording an internal fatal error when the cache cannot be checked. The retry loop also uses it to prevent the built-in generator from returning an ID after its own lookup failed, through the existing callback failure path. Preserve the public API's boolean contract. Correct the callback-selection cache lock failure reason from SSL_R_SESSION_ID_CONTEXT_UNINITIALIZED to ERR_R_INTERNAL_ERROR. Remove a redundant caller-side SSLfatal() that appended a generic ERR_R_INTERNAL_ERROR after the specific error already recorded. Add a deterministic unit test for final cache lookup failure. Fixes #32358 Assisted-by: Codex:gpt-5.6-sol Reviewed-by: Tomas Mraz <[email protected]> Reviewed-by: Dmitry Belyavskiy <[email protected]> Merge-date: Wed Oct 21 16:25:29 2026 Merged-from: https://github.com/openssl/openssl/pull/32359
Diffstat (limited to 'test/unit')
-rw-r--r--test/unit/build.info6
-rw-r--r--test/unit/ssl/test_ssl_sess.c136
2 files changed, 142 insertions, 0 deletions
diff --git a/test/unit/build.info b/test/unit/build.info
index 76598e358ec..e086f0ed3d6 100644
--- a/test/unit/build.info
+++ b/test/unit/build.info
@@ -68,6 +68,12 @@ IF[{- $config{target} =~ /^(?:linux|BSD)/ -}]
WRAP[crypto/bio/test_bss_sock]=read write BIO_closesocket
INCLUDE[crypto/bio/test_bss_sock]=../../include ../../crypto/bio
DEPEND[crypto/bio/test_bss_sock]=../../libcrypto.a
+
+ PROGRAMS{noinst}=ssl/test_ssl_sess
+ SOURCE[ssl/test_ssl_sess]=ssl/test_ssl_sess.c
+ INCLUDE[ssl/test_ssl_sess]=../../include ../../ssl
+ DEPEND[ssl/test_ssl_sess]=../../libssl.a ../../libcrypto.a
+ WRAP[ssl/test_ssl_sess]=CRYPTO_THREAD_read_lock
ENDIF
IF[{- $config{target} =~ /^VC-/ -}]
diff --git a/test/unit/ssl/test_ssl_sess.c b/test/unit/ssl/test_ssl_sess.c
new file mode 100644
index 00000000000..7a7c7d4cc3d
--- /dev/null
+++ b/test/unit/ssl/test_ssl_sess.c
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2026 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <stdlib.h>
+#include <string.h>
+#include <cmocka.h>
+
+#include <openssl/crypto.h>
+#include <openssl/err.h>
+#include <openssl/ssl.h>
+
+#include "internal/ssl_unwrap.h"
+#include "ssl_local.h"
+
+typedef struct {
+ SSL_CTX *ctx;
+ SSL *ssl;
+ SSL_CONNECTION *sc;
+ SSL_SESSION *session;
+} SESSION_ID_FIXTURE;
+
+/*
+ * Fail one explicitly armed lock and pass all other lock calls through. The
+ * callback arms the next cache read, which is the final collision check.
+ */
+static CRYPTO_RWLOCK *read_lock_to_fail;
+
+/* wraps */
+
+int __real_CRYPTO_THREAD_read_lock(CRYPTO_RWLOCK *lock);
+int __wrap_CRYPTO_THREAD_read_lock(CRYPTO_RWLOCK *lock);
+
+int __wrap_CRYPTO_THREAD_read_lock(CRYPTO_RWLOCK *lock)
+{
+ if (lock != NULL && lock == read_lock_to_fail) {
+ read_lock_to_fail = NULL;
+ return 0;
+ }
+
+ return __real_CRYPTO_THREAD_read_lock(lock);
+}
+
+/* fault injection */
+
+static void fail_next_read_lock(CRYPTO_RWLOCK *lock)
+{
+ assert_null(read_lock_to_fail);
+ assert_non_null(lock);
+ read_lock_to_fail = lock;
+}
+
+/* helpers */
+
+static int generate_session_id_then_fail_cache_lock(SSL *ssl,
+ unsigned char *id, unsigned int *id_len)
+{
+ SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(ssl);
+
+ memset(id, 0x5a, *id_len);
+ fail_next_read_lock(sc->session_ctx->lock);
+ return 1;
+}
+
+/* fixtures */
+
+static int setup(void **state)
+{
+ SESSION_ID_FIXTURE *fixture = calloc(1, sizeof(*fixture));
+
+ assert_non_null(fixture);
+ fixture->ctx = SSL_CTX_new(TLS_server_method());
+ assert_non_null(fixture->ctx);
+ fixture->ssl = SSL_new(fixture->ctx);
+ assert_non_null(fixture->ssl);
+ fixture->sc = SSL_CONNECTION_FROM_SSL(fixture->ssl);
+ assert_non_null(fixture->sc);
+ fixture->session = SSL_SESSION_new();
+ assert_non_null(fixture->session);
+
+ fixture->sc->version = TLS1_2_VERSION;
+ read_lock_to_fail = NULL;
+ ERR_clear_error();
+ *state = fixture;
+ return 0;
+}
+
+static int teardown(void **state)
+{
+ SESSION_ID_FIXTURE *fixture = *state;
+
+ assert_null(read_lock_to_fail);
+ SSL_SESSION_free(fixture->session);
+ SSL_free(fixture->ssl);
+ SSL_CTX_free(fixture->ctx);
+ free(fixture);
+ ERR_clear_error();
+ return 0;
+}
+
+static void test_generate_session_id_final_cache_lock_failure(void **state)
+{
+ SESSION_ID_FIXTURE *fixture = *state;
+ unsigned long err;
+
+ assert_true(SSL_set_generate_session_id(fixture->ssl,
+ generate_session_id_then_fail_cache_lock));
+
+ assert_false(ssl_generate_session_id(fixture->sc, fixture->session));
+ /* Inspect the first error recorded for this failure. */
+ err = ERR_peek_error();
+ assert_true(ossl_statem_in_error(fixture->sc));
+ assert_int_equal(ERR_GET_LIB(err), ERR_LIB_SSL);
+ assert_int_equal(ERR_GET_REASON(err), ERR_R_INTERNAL_ERROR);
+}
+
+int main(void)
+{
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test_setup_teardown(
+ test_generate_session_id_final_cache_lock_failure, setup,
+ teardown),
+ };
+
+ cmocka_set_message_output(CM_OUTPUT_TAP);
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}