diff options
| author | Mounir IDRASSI <[email protected]> | 2026-08-12 11:53:22 +0200 |
|---|---|---|
| committer | Tomas Mraz <[email protected]> | 2026-10-21 18:24:43 +0200 |
| commit | ced1ca3a0be6f1c4d512805cba16ad4655004fd5 (patch) | |
| tree | 8d2287d56a276b48842c4e926a0dc0d03ad63b08 /test/unit | |
| parent | Adds a test that tests missing integrity only ciphers effect on TLS cipher su... (diff) | |
| download | openssl-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.info | 6 | ||||
| -rw-r--r-- | test/unit/ssl/test_ssl_sess.c | 136 |
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); +} |
