From 3ab4fce1a064d22c95eb4baf741b7372df106cdf Mon Sep 17 00:00:00 2001 From: Nadav0077 <18245584+Nadav0077@users.noreply.github.com> Date: Sat, 25 Apr 2026 15:19:30 +0300 Subject: [PATCH 1/5] Harden TLS 1.3 serialized session loading Signed-off-by: Nadav0077 <18245584+Nadav0077@users.noreply.github.com> --- ChangeLog.d/tls13-session-load-hardening.txt | 3 + library/ssl_tls.c | 24 +++++-- tests/suites/test_suite_ssl.data | 12 ++++ tests/suites/test_suite_ssl.function | 68 ++++++++++++++++++++ 4 files changed, 102 insertions(+), 5 deletions(-) create mode 100644 ChangeLog.d/tls13-session-load-hardening.txt diff --git a/ChangeLog.d/tls13-session-load-hardening.txt b/ChangeLog.d/tls13-session-load-hardening.txt new file mode 100644 index 0000000000..d56e14cfac --- /dev/null +++ b/ChangeLog.d/tls13-session-load-hardening.txt @@ -0,0 +1,3 @@ +Bugfix + * Reject malformed TLS 1.3 serialized sessions instead of accepting + unterminated strings or extra trailing data. diff --git a/library/ssl_tls.c b/library/ssl_tls.c index e8a44f834c..8b4f64bc96 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c @@ -3573,6 +3573,7 @@ static int ssl_tls13_session_load(mbedtls_ssl_session *session, { const unsigned char *p = buf; const unsigned char *end = buf + len; + int ret; if (end - p < 6) { return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; @@ -3634,7 +3635,12 @@ static int ssl_tls13_session_load(mbedtls_ssl_session *session, } if (alpn_len > 0) { - int ret = mbedtls_ssl_session_set_ticket_alpn(session, (char *) p); + if (p[alpn_len - 1] != '\0' || + memchr(p, '\0', alpn_len - 1) != NULL) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + ret = mbedtls_ssl_session_set_ticket_alpn(session, (char *) p); if (ret != 0) { return ret; } @@ -3659,11 +3665,15 @@ static int ssl_tls13_session_load(mbedtls_ssl_session *session, return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; } if (hostname_len > 0) { - session->hostname = mbedtls_calloc(1, hostname_len); - if (session->hostname == NULL) { - return MBEDTLS_ERR_SSL_ALLOC_FAILED; + if (p[hostname_len - 1] != '\0' || + memchr(p, '\0', hostname_len - 1) != NULL) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + ret = mbedtls_ssl_session_set_hostname(session, (const char *) p); + if (ret != 0) { + return ret; } - memcpy(session->hostname, p, hostname_len); p += hostname_len; } #endif /* MBEDTLS_SSL_SERVER_NAME_INDICATION */ @@ -3701,6 +3711,10 @@ static int ssl_tls13_session_load(mbedtls_ssl_session *session, } #endif /* MBEDTLS_SSL_CLI_C */ + if (p != end) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + return 0; } diff --git a/tests/suites/test_suite_ssl.data b/tests/suites/test_suite_ssl.data index 881d502ed5..fd90f0606e 100644 --- a/tests/suites/test_suite_ssl.data +++ b/tests/suites/test_suite_ssl.data @@ -3028,6 +3028,18 @@ TLS 1.3: SRV: Session serialization, load buffer size depends_on:MBEDTLS_SSL_PROTO_TLS1_3:MBEDTLS_SSL_SESSION_TICKETS:MBEDTLS_SSL_SRV_C ssl_serialize_session_load_buf_size:0:"":MBEDTLS_SSL_IS_SERVER:MBEDTLS_SSL_VERSION_TLS1_3 +TLS 1.3: Session serialization rejects trailing data +depends_on:MBEDTLS_SSL_PROTO_TLS1_3:MBEDTLS_SSL_SESSION_TICKETS:MBEDTLS_SSL_CLI_C +ssl_tls13_session_load_rejects_bad_serialized_data:MBEDTLS_SSL_IS_CLIENT:0 + +TLS 1.3: Session serialization rejects hostname without terminator +depends_on:MBEDTLS_SSL_PROTO_TLS1_3:MBEDTLS_SSL_SESSION_TICKETS:MBEDTLS_SSL_CLI_C:MBEDTLS_SSL_SERVER_NAME_INDICATION +ssl_tls13_session_load_rejects_bad_serialized_data:MBEDTLS_SSL_IS_CLIENT:1 + +TLS 1.3: Session serialization rejects ALPN without terminator +depends_on:MBEDTLS_SSL_PROTO_TLS1_3:MBEDTLS_SSL_SESSION_TICKETS:MBEDTLS_SSL_SRV_C:MBEDTLS_SSL_EARLY_DATA:MBEDTLS_SSL_ALPN +ssl_tls13_session_load_rejects_bad_serialized_data:MBEDTLS_SSL_IS_SERVER:2 + Test configuration of EC groups through mbedtls_ssl_conf_groups() conf_group: diff --git a/tests/suites/test_suite_ssl.function b/tests/suites/test_suite_ssl.function index 39457c9047..af63d6dbca 100644 --- a/tests/suites/test_suite_ssl.function +++ b/tests/suites/test_suite_ssl.function @@ -2733,6 +2733,74 @@ exit: } /* END_CASE */ +/* BEGIN_CASE depends_on:MBEDTLS_SSL_PROTO_TLS1_3:MBEDTLS_SSL_SESSION_TICKETS */ +void ssl_tls13_session_load_rejects_bad_serialized_data(int endpoint_type, + int mutation) +{ + mbedtls_ssl_session session, restored; + unsigned char *buf = NULL; + size_t len, bad_len, i; + unsigned char *field = NULL; + const unsigned char hostname[] = "hostname example"; + const unsigned char alpn[] = "ALPNExample"; + const unsigned char *string_to_corrupt = NULL; + size_t string_len = 0; + + mbedtls_ssl_session_init(&session); + mbedtls_ssl_session_init(&restored); + USE_PSA_INIT(); + + TEST_EQUAL(mbedtls_test_ssl_tls13_populate_session( + &session, 42, endpoint_type), 0); + + TEST_EQUAL(mbedtls_ssl_session_save(&session, NULL, 0, &len), + MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL); + TEST_CALLOC(buf, len + 1); + TEST_EQUAL(mbedtls_ssl_session_save(&session, buf, len, &len), 0); + + bad_len = len; + switch (mutation) { + case 0: + bad_len = len + 1; + buf[len] = 0; + break; + + case 1: + string_to_corrupt = hostname; + string_len = sizeof(hostname); + break; + + case 2: + string_to_corrupt = alpn; + string_len = sizeof(alpn); + break; + + default: + TEST_ASSERT(0); + } + + if (string_to_corrupt != NULL) { + for (i = 0; i + string_len <= len; i++) { + if (memcmp(buf + i, string_to_corrupt, string_len) == 0) { + field = buf + i; + break; + } + } + TEST_ASSERT(field != NULL); + field[string_len - 1] = 'X'; + } + + TEST_EQUAL(mbedtls_ssl_session_load(&restored, buf, bad_len), + MBEDTLS_ERR_SSL_BAD_INPUT_DATA); + +exit: + mbedtls_ssl_session_free(&session); + mbedtls_ssl_session_free(&restored); + mbedtls_free(buf); + USE_PSA_DONE(); +} +/* END_CASE */ + /* BEGIN_CASE */ void ssl_session_serialize_version_check(int corrupt_major, int corrupt_minor, From 3f972d014dde8cf4b47f64192bc80b7d6d46b591 Mon Sep 17 00:00:00 2001 From: Nadav0077 <18245584+Nadav0077@users.noreply.github.com> Date: Tue, 12 May 2026 19:32:50 +0300 Subject: [PATCH 2/5] Use block-local ret to avoid unused-variable in restricted configs Signed-off-by: Nadav0077 <18245584+Nadav0077@users.noreply.github.com> --- library/ssl_tls.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/library/ssl_tls.c b/library/ssl_tls.c index 8b4f64bc96..ef368db8c2 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c @@ -3573,7 +3573,6 @@ static int ssl_tls13_session_load(mbedtls_ssl_session *session, { const unsigned char *p = buf; const unsigned char *end = buf + len; - int ret; if (end - p < 6) { return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; @@ -3640,7 +3639,7 @@ static int ssl_tls13_session_load(mbedtls_ssl_session *session, return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; } - ret = mbedtls_ssl_session_set_ticket_alpn(session, (char *) p); + int ret = mbedtls_ssl_session_set_ticket_alpn(session, (char *) p); if (ret != 0) { return ret; } @@ -3670,7 +3669,7 @@ static int ssl_tls13_session_load(mbedtls_ssl_session *session, return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; } - ret = mbedtls_ssl_session_set_hostname(session, (const char *) p); + int ret = mbedtls_ssl_session_set_hostname(session, (const char *) p); if (ret != 0) { return ret; } From fea00c3e517dd122947684623d852862609508a6 Mon Sep 17 00:00:00 2001 From: Nadav0077 <18245584+Nadav0077@users.noreply.github.com> Date: Tue, 19 May 2026 14:15:01 +0300 Subject: [PATCH 3/5] Reject out-of-bounds DTLS CID length in ssl_context_load Signed-off-by: Nadav0077 <18245584+Nadav0077@users.noreply.github.com> --- ChangeLog.d/serialized-data-load-hardening.txt | 5 +++++ ChangeLog.d/tls13-session-load-hardening.txt | 3 --- library/ssl_tls.c | 8 ++++++++ 3 files changed, 13 insertions(+), 3 deletions(-) create mode 100644 ChangeLog.d/serialized-data-load-hardening.txt delete mode 100644 ChangeLog.d/tls13-session-load-hardening.txt diff --git a/ChangeLog.d/serialized-data-load-hardening.txt b/ChangeLog.d/serialized-data-load-hardening.txt new file mode 100644 index 0000000000..e068988dac --- /dev/null +++ b/ChangeLog.d/serialized-data-load-hardening.txt @@ -0,0 +1,5 @@ +Bugfix + * Reject malformed TLS 1.3 serialized sessions instead of accepting + unterminated strings or extra trailing data. + * Reject serialized SSL contexts whose DTLS Connection ID length exceeds + the maximum supported size, instead of overflowing the internal buffer. diff --git a/ChangeLog.d/tls13-session-load-hardening.txt b/ChangeLog.d/tls13-session-load-hardening.txt deleted file mode 100644 index d56e14cfac..0000000000 --- a/ChangeLog.d/tls13-session-load-hardening.txt +++ /dev/null @@ -1,3 +0,0 @@ -Bugfix - * Reject malformed TLS 1.3 serialized sessions instead of accepting - unterminated strings or extra trailing data. diff --git a/library/ssl_tls.c b/library/ssl_tls.c index ef368db8c2..cbb3959c6e 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c @@ -5012,6 +5012,10 @@ static int ssl_context_load(mbedtls_ssl_context *ssl, return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; } + if (ssl->transform->in_cid_len > sizeof(ssl->transform->in_cid)) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + memcpy(ssl->transform->in_cid, p, ssl->transform->in_cid_len); p += ssl->transform->in_cid_len; @@ -5021,6 +5025,10 @@ static int ssl_context_load(mbedtls_ssl_context *ssl, return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; } + if (ssl->transform->out_cid_len > sizeof(ssl->transform->out_cid)) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + memcpy(ssl->transform->out_cid, p, ssl->transform->out_cid_len); p += ssl->transform->out_cid_len; #endif /* MBEDTLS_SSL_DTLS_CONNECTION_ID */ From 72f767da310045a31e15a2c2c5be80afc4102f79 Mon Sep 17 00:00:00 2001 From: Nadav0077 <18245584+Nadav0077@users.noreply.github.com> Date: Tue, 19 May 2026 14:15:02 +0300 Subject: [PATCH 4/5] Address review comments on serialized data hardening Signed-off-by: Nadav0077 <18245584+Nadav0077@users.noreply.github.com> --- library/ssl_tls.c | 10 ++++++---- tests/suites/test_suite_ssl.data | 6 +++--- tests/suites/test_suite_ssl.function | 6 ++++++ 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/library/ssl_tls.c b/library/ssl_tls.c index cbb3959c6e..cb14275d1e 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c @@ -3634,8 +3634,9 @@ static int ssl_tls13_session_load(mbedtls_ssl_session *session, } if (alpn_len > 0) { - if (p[alpn_len - 1] != '\0' || - memchr(p, '\0', alpn_len - 1) != NULL) { + /* The data is about to be used as a null-terminated string, so + * check that it actually is one. */ + if (p[alpn_len - 1] != '\0') { return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; } @@ -3664,8 +3665,9 @@ static int ssl_tls13_session_load(mbedtls_ssl_session *session, return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; } if (hostname_len > 0) { - if (p[hostname_len - 1] != '\0' || - memchr(p, '\0', hostname_len - 1) != NULL) { + /* The data is about to be used as a null-terminated string, so + * check that it actually is one. */ + if (p[hostname_len - 1] != '\0') { return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; } diff --git a/tests/suites/test_suite_ssl.data b/tests/suites/test_suite_ssl.data index fd90f0606e..eec4902929 100644 --- a/tests/suites/test_suite_ssl.data +++ b/tests/suites/test_suite_ssl.data @@ -3029,15 +3029,15 @@ depends_on:MBEDTLS_SSL_PROTO_TLS1_3:MBEDTLS_SSL_SESSION_TICKETS:MBEDTLS_SSL_SRV_ ssl_serialize_session_load_buf_size:0:"":MBEDTLS_SSL_IS_SERVER:MBEDTLS_SSL_VERSION_TLS1_3 TLS 1.3: Session serialization rejects trailing data -depends_on:MBEDTLS_SSL_PROTO_TLS1_3:MBEDTLS_SSL_SESSION_TICKETS:MBEDTLS_SSL_CLI_C +depends_on:MBEDTLS_SSL_CLI_C ssl_tls13_session_load_rejects_bad_serialized_data:MBEDTLS_SSL_IS_CLIENT:0 TLS 1.3: Session serialization rejects hostname without terminator -depends_on:MBEDTLS_SSL_PROTO_TLS1_3:MBEDTLS_SSL_SESSION_TICKETS:MBEDTLS_SSL_CLI_C:MBEDTLS_SSL_SERVER_NAME_INDICATION +depends_on:MBEDTLS_SSL_CLI_C:MBEDTLS_SSL_SERVER_NAME_INDICATION ssl_tls13_session_load_rejects_bad_serialized_data:MBEDTLS_SSL_IS_CLIENT:1 TLS 1.3: Session serialization rejects ALPN without terminator -depends_on:MBEDTLS_SSL_PROTO_TLS1_3:MBEDTLS_SSL_SESSION_TICKETS:MBEDTLS_SSL_SRV_C:MBEDTLS_SSL_EARLY_DATA:MBEDTLS_SSL_ALPN +depends_on:MBEDTLS_SSL_SRV_C:MBEDTLS_SSL_EARLY_DATA:MBEDTLS_SSL_ALPN ssl_tls13_session_load_rejects_bad_serialized_data:MBEDTLS_SSL_IS_SERVER:2 Test configuration of EC groups through mbedtls_ssl_conf_groups() diff --git a/tests/suites/test_suite_ssl.function b/tests/suites/test_suite_ssl.function index af63d6dbca..f5a047098a 100644 --- a/tests/suites/test_suite_ssl.function +++ b/tests/suites/test_suite_ssl.function @@ -2758,6 +2758,12 @@ void ssl_tls13_session_load_rejects_bad_serialized_data(int endpoint_type, TEST_CALLOC(buf, len + 1); TEST_EQUAL(mbedtls_ssl_session_save(&session, buf, len, &len), 0); + /* The serialized data must load successfully before we corrupt it, + * otherwise the test below would not prove anything. */ + TEST_EQUAL(mbedtls_ssl_session_load(&restored, buf, len), 0); + mbedtls_ssl_session_free(&restored); + mbedtls_ssl_session_init(&restored); + bad_len = len; switch (mutation) { case 0: From fc34e72fc2cafe0ef4116d011f70d3efadfc95cc Mon Sep 17 00:00:00 2001 From: Nadav0077 <18245584+Nadav0077@users.noreply.github.com> Date: Thu, 21 May 2026 13:58:17 +0300 Subject: [PATCH 5/5] Address review: symbolic mutation constants and CID bounds-check test Signed-off-by: Nadav0077 <18245584+Nadav0077@users.noreply.github.com> --- tests/suites/test_suite_ssl.data | 10 ++- tests/suites/test_suite_ssl.function | 101 ++++++++++++++++++++++++++- 2 files changed, 105 insertions(+), 6 deletions(-) diff --git a/tests/suites/test_suite_ssl.data b/tests/suites/test_suite_ssl.data index eec4902929..aba17ff145 100644 --- a/tests/suites/test_suite_ssl.data +++ b/tests/suites/test_suite_ssl.data @@ -3030,15 +3030,19 @@ ssl_serialize_session_load_buf_size:0:"":MBEDTLS_SSL_IS_SERVER:MBEDTLS_SSL_VERSI TLS 1.3: Session serialization rejects trailing data depends_on:MBEDTLS_SSL_CLI_C -ssl_tls13_session_load_rejects_bad_serialized_data:MBEDTLS_SSL_IS_CLIENT:0 +ssl_tls13_session_load_rejects_bad_serialized_data:MBEDTLS_SSL_IS_CLIENT:TEST_TLS13_SESSION_LOAD_TRAILING_DATA TLS 1.3: Session serialization rejects hostname without terminator depends_on:MBEDTLS_SSL_CLI_C:MBEDTLS_SSL_SERVER_NAME_INDICATION -ssl_tls13_session_load_rejects_bad_serialized_data:MBEDTLS_SSL_IS_CLIENT:1 +ssl_tls13_session_load_rejects_bad_serialized_data:MBEDTLS_SSL_IS_CLIENT:TEST_TLS13_SESSION_LOAD_HOSTNAME_NO_NUL TLS 1.3: Session serialization rejects ALPN without terminator depends_on:MBEDTLS_SSL_SRV_C:MBEDTLS_SSL_EARLY_DATA:MBEDTLS_SSL_ALPN -ssl_tls13_session_load_rejects_bad_serialized_data:MBEDTLS_SSL_IS_SERVER:2 +ssl_tls13_session_load_rejects_bad_serialized_data:MBEDTLS_SSL_IS_SERVER:TEST_TLS13_SESSION_LOAD_ALPN_NO_NUL + +SSL context serialization rejects out-of-bounds DTLS CID length +depends_on:MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED:MBEDTLS_SSL_PROTO_TLS1_2:MBEDTLS_SSL_PROTO_DTLS:MBEDTLS_SSL_DTLS_CONNECTION_ID:MBEDTLS_SSL_CONTEXT_SERIALIZATION:PSA_HAVE_ALG_SOME_RSA_SIGN:PSA_WANT_ECC_SECP_R1_384:PSA_WANT_ALG_SHA_256:MBEDTLS_CAN_HANDLE_RSA_TEST_KEY:TEST_GCM_OR_CHACHAPOLY_ENABLED +ssl_context_load_rejects_oob_cid_length: Test configuration of EC groups through mbedtls_ssl_conf_groups() conf_group: diff --git a/tests/suites/test_suite_ssl.function b/tests/suites/test_suite_ssl.function index f5a047098a..2000eddc3c 100644 --- a/tests/suites/test_suite_ssl.function +++ b/tests/suites/test_suite_ssl.function @@ -25,6 +25,11 @@ #define TEST_EARLY_DATA_NO_INITIAL_ALPN 6 #define TEST_EARLY_DATA_NO_LATER_ALPN 7 +/* Mutations for ssl_tls13_session_load_rejects_bad_serialized_data */ +#define TEST_TLS13_SESSION_LOAD_TRAILING_DATA 0 +#define TEST_TLS13_SESSION_LOAD_HOSTNAME_NO_NUL 1 +#define TEST_TLS13_SESSION_LOAD_ALPN_NO_NUL 2 + #if (!defined(MBEDTLS_SSL_PROTO_TLS1_2)) && \ defined(MBEDTLS_SSL_EARLY_DATA) && defined(MBEDTLS_SSL_CLI_C) && \ defined(MBEDTLS_SSL_SRV_C) && defined(MBEDTLS_DEBUG_C) && \ @@ -2766,17 +2771,17 @@ void ssl_tls13_session_load_rejects_bad_serialized_data(int endpoint_type, bad_len = len; switch (mutation) { - case 0: + case TEST_TLS13_SESSION_LOAD_TRAILING_DATA: bad_len = len + 1; buf[len] = 0; break; - case 1: + case TEST_TLS13_SESSION_LOAD_HOSTNAME_NO_NUL: string_to_corrupt = hostname; string_len = sizeof(hostname); break; - case 2: + case TEST_TLS13_SESSION_LOAD_ALPN_NO_NUL: string_to_corrupt = alpn; string_len = sizeof(alpn); break; @@ -2807,6 +2812,96 @@ exit: } /* END_CASE */ +/* BEGIN_CASE depends_on:MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED:MBEDTLS_SSL_PROTO_TLS1_2:MBEDTLS_SSL_PROTO_DTLS:MBEDTLS_SSL_DTLS_CONNECTION_ID:MBEDTLS_SSL_CONTEXT_SERIALIZATION:PSA_HAVE_ALG_SOME_RSA_SIGN:PSA_WANT_ECC_SECP_R1_384:PSA_WANT_ALG_SHA_256:MBEDTLS_CAN_HANDLE_RSA_TEST_KEY:TEST_GCM_OR_CHACHAPOLY_ENABLED */ +void ssl_context_load_rejects_oob_cid_length(void) +{ + /* Sentinel CID so we can find its location inside the serialized buffer. */ + const unsigned char server_cid[] = { + 0xAB, 0xCD, 0xEF, 0x12, 0x34, 0x56, 0x78, 0x9A + }; + const unsigned char client_cid[] = { + 0x9A, 0x78, 0x56, 0x34, 0x12, 0xEF, 0xCD, 0xAB + }; + + mbedtls_test_handshake_test_options options; + mbedtls_test_ssl_endpoint client_ep, server_ep; + unsigned char *context_buf = NULL; + size_t context_buf_len, i; + unsigned char *cid_len_p = NULL; + + memset(&client_ep, 0, sizeof(client_ep)); + memset(&server_ep, 0, sizeof(server_ep)); + + MD_OR_USE_PSA_INIT(); + mbedtls_test_init_handshake_options(&options); + + options.dtls = 1; + options.expected_negotiated_version = MBEDTLS_SSL_VERSION_TLS1_2; + + TEST_EQUAL(mbedtls_test_ssl_endpoint_init( + &client_ep, MBEDTLS_SSL_IS_CLIENT, &options), 0); + TEST_EQUAL(mbedtls_test_ssl_endpoint_init( + &server_ep, MBEDTLS_SSL_IS_SERVER, &options), 0); + + /* Configure CID with unique patterns so we can locate the length byte in + * the serialized context. Both peers must agree on the CID length via + * conf_cid() before set_cid() will accept the value. */ + TEST_EQUAL(mbedtls_ssl_conf_cid(&client_ep.conf, sizeof(client_cid), + MBEDTLS_SSL_UNEXPECTED_CID_FAIL), 0); + TEST_EQUAL(mbedtls_ssl_conf_cid(&server_ep.conf, sizeof(server_cid), + MBEDTLS_SSL_UNEXPECTED_CID_FAIL), 0); + TEST_EQUAL(mbedtls_ssl_set_cid(&client_ep.ssl, MBEDTLS_SSL_CID_ENABLED, + client_cid, sizeof(client_cid)), 0); + TEST_EQUAL(mbedtls_ssl_set_cid(&server_ep.ssl, MBEDTLS_SSL_CID_ENABLED, + server_cid, sizeof(server_cid)), 0); + + TEST_EQUAL(mbedtls_test_ssl_dtls_join_endpoints(&client_ep, &server_ep), 0); + + TEST_ASSERT(mbedtls_test_ssl_perform_connection(&options, &client_ep, + &server_ep)); + + /* Serialize the post-handshake server context. */ + TEST_EQUAL(mbedtls_ssl_context_save(&server_ep.ssl, NULL, 0, + &context_buf_len), + MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL); + TEST_CALLOC(context_buf, context_buf_len); + TEST_EQUAL(mbedtls_ssl_context_save(&server_ep.ssl, context_buf, + context_buf_len, &context_buf_len), 0); + + /* The serialized format writes [in_cid_len][in_cid bytes]; locate the + * length byte by searching for our sentinel CID. */ + for (i = 1; i + sizeof(server_cid) <= context_buf_len; i++) { + if (context_buf[i - 1] == (unsigned char) sizeof(server_cid) && + memcmp(context_buf + i, server_cid, sizeof(server_cid)) == 0) { + cid_len_p = context_buf + i - 1; + break; + } + } + TEST_ASSERT(cid_len_p != NULL); + + /* Corrupt the length to the smallest value strictly greater than + * sizeof(in_cid), so we exercise the new bounds check rather than the + * pre-existing buffer-size check that would also trip on huge values. */ + *cid_len_p = (unsigned char) (MBEDTLS_SSL_CID_IN_LEN_MAX + 1); + + /* Reinitialise the server SSL context so we can load into a fresh one. */ + mbedtls_ssl_free(&server_ep.ssl); + mbedtls_ssl_init(&server_ep.ssl); + TEST_EQUAL(mbedtls_ssl_setup(&server_ep.ssl, &server_ep.conf), 0); + + TEST_EQUAL(mbedtls_ssl_context_load(&server_ep.ssl, context_buf, + context_buf_len), + MBEDTLS_ERR_SSL_BAD_INPUT_DATA); + +exit: + mbedtls_test_ssl_endpoint_free(&client_ep); + mbedtls_test_ssl_endpoint_free(&server_ep); + mbedtls_test_free_handshake_options(&options); + mbedtls_free(context_buf); + MD_OR_USE_PSA_DONE(); +} +/* END_CASE */ + /* BEGIN_CASE */ void ssl_session_serialize_version_check(int corrupt_major, int corrupt_minor,