From ade56554a6103f7de26c16d4348f6efadb2d8b97 Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Tue, 24 Mar 2026 18:38:01 +0100 Subject: [PATCH 01/15] Revert "ssl_server2.c: DTLS: Attempt to read the response to the close notification" This reverts commit 2e9b9681e60ff52d69a3a68b4c7be0bcbab9191b. Signed-off-by: Ronald Cron --- programs/ssl/ssl_server2.c | 50 +------------------ tests/compat.sh | 1 - tests/scripts/components-configuration-tls.sh | 1 - 3 files changed, 1 insertion(+), 51 deletions(-) diff --git a/programs/ssl/ssl_server2.c b/programs/ssl/ssl_server2.c index f2e8eff47..79cbad877 100644 --- a/programs/ssl/ssl_server2.c +++ b/programs/ssl/ssl_server2.c @@ -4131,55 +4131,7 @@ close_notify: } while (ret == MBEDTLS_ERR_SSL_WANT_WRITE); ret = 0; - /* - * In the DTLS case, attempt to read a possible response to the close - * notification. This avoids reconnecting to the same client when we - * reset and later receive its close-notification response during - * step 3 (waiting for a client to connect). - * - * Stop waiting for the response if the connection has already ended. - * - * The waiting loop below relies on mbedtls_ssl_read() returning regularly - * in order to keep the total waiting time approximately bounded to 1s. If - * no read timeout is configured (see the read_timeout option), or if the - * configured timeout is close to or larger than 1s, the total waiting time - * may exceed 1s by a significant margin. - */ -#if defined(MBEDTLS_SSL_PROTO_DTLS) && defined(MBEDTLS_HAVE_TIME) - if (opt.transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM) { - mbedtls_ms_time_t start = mbedtls_ms_time(); - for (;;) { - ret = mbedtls_ssl_read(&ssl, buf, opt.buffer_size); - /* - * mbedtls_ssl_read() returned some data or timed out, loop if we - * have not spent already too much time, quite arbitrarily 1s. - */ - if ((ret > 0) || (ret == MBEDTLS_ERR_SSL_TIMEOUT)) { - if ((mbedtls_ms_time() - start) < 1000) { - continue; - } - } - - if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) { - mbedtls_printf(" done, received client close notification.\n"); - } else { - /* ret = 0, silent transport EOF or ret < 0 except - * MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY. Note that we do not - * handle specifically the non-fatal error codes like - * MBEDTLS_ERR_SSL_WANT_READ as we do not really expect them - * here. - */ - mbedtls_printf(" done\n"); - } - break; - } - ret = 0; - } else -#endif /* MBEDTLS_SSL_PROTO_DTLS && MBEDTLS_HAVE_TIME */ - { - mbedtls_printf(" done\n"); - } - fflush(stdout); + mbedtls_printf(" done\n"); #if defined(MBEDTLS_SSL_CACHE_C) if (opt.cache_remove > 0) { diff --git a/tests/compat.sh b/tests/compat.sh index 3f44c984f..2b6f45412 100755 --- a/tests/compat.sh +++ b/tests/compat.sh @@ -557,7 +557,6 @@ setup_arguments() # with OpenSSL 1.0.1h, -www, -WWW and -HTTP break DTLS handshakes if is_dtls "$MODE"; then O_SERVER_ARGS="$O_SERVER_ARGS" - M_SERVER_ARGS="$M_SERVER_ARGS read_timeout=1000" else O_SERVER_ARGS="$O_SERVER_ARGS -www" fi diff --git a/tests/scripts/components-configuration-tls.sh b/tests/scripts/components-configuration-tls.sh index d017eef18..5a77c4def 100644 --- a/tests/scripts/components-configuration-tls.sh +++ b/tests/scripts/components-configuration-tls.sh @@ -165,7 +165,6 @@ component_test_tls1_2_ccm_psk_dtls () { msg "build: configs/config-ccm-psk-dtls1_2.h" MBEDTLS_CONFIG="configs/config-ccm-psk-dtls1_2.h" CRYPTO_CONFIG="configs/crypto-config-ccm-psk-tls1_2.h" - tf-psa-crypto/scripts/config.py -f "$CRYPTO_CONFIG" set MBEDTLS_HAVE_TIME CC=$ASAN_CC cmake -DMBEDTLS_CONFIG_FILE="$MBEDTLS_CONFIG" -DTF_PSA_CRYPTO_CONFIG_FILE="$CRYPTO_CONFIG" -D CMAKE_BUILD_TYPE:String=Asan . make From 315c970fbeb4ac13446c56f16dd4c8f4c3df0222 Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Fri, 20 Mar 2026 11:21:56 +0100 Subject: [PATCH 02/15] dtls: Fix debug log Signed-off-by: Ronald Cron --- library/ssl_msg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/ssl_msg.c b/library/ssl_msg.c index abb5a5696..86c23d766 100644 --- a/library/ssl_msg.c +++ b/library/ssl_msg.c @@ -3618,7 +3618,7 @@ static int ssl_parse_record_header(mbedtls_ssl_context const *ssl, ( "datagram of length %u too small to hold DTLS record header of length %u", (unsigned) len, - (unsigned) (rec_hdr_len_len + rec_hdr_len_len))); + (unsigned) (rec_hdr_len_offset + rec_hdr_len_len))); return MBEDTLS_ERR_SSL_INVALID_RECORD; } From 676d74e4c74ce71a38b321f4567dfa8a20f30ff7 Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Fri, 20 Mar 2026 17:19:10 +0100 Subject: [PATCH 03/15] dtls: Error out on invalid/unexpected record header Error out on invalid/unexpected record header when reading the DTLS 1.2 ClientHello. Signed-off-by: Ronald Cron --- library/ssl_msg.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/library/ssl_msg.c b/library/ssl_msg.c index 86c23d766..65609b8ff 100644 --- a/library/ssl_msg.c +++ b/library/ssl_msg.c @@ -4750,6 +4750,30 @@ static int ssl_get_next_record(mbedtls_ssl_context *ssl) ret = MBEDTLS_ERR_SSL_UNEXPECTED_RECORD; } +#if defined(MBEDTLS_SSL_SRV_C) + /* + * When retrieving the DTLS ClientHello on server side, error out + * when detecting an invalid or unexpected record. + */ + if ((ssl->conf->endpoint == MBEDTLS_SSL_IS_SERVER) && + (ssl->state == MBEDTLS_SSL_CLIENT_HELLO) +#if defined(MBEDTLS_SSL_RENEGOTIATION) + && (ssl->renego_status == MBEDTLS_SSL_INITIAL_HANDSHAKE) +#endif + ) { + /* + * For backward compatibility, return + * MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE rather than + * MBEDTLS_ERR_SSL_UNEXPECTED_RECORD. + */ + if (ret == MBEDTLS_ERR_SSL_UNEXPECTED_RECORD) { + return MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE; + } else { + return ret; + } + } +#endif /* MBEDTLS_SSL_SRV_C */ + if (ret == MBEDTLS_ERR_SSL_UNEXPECTED_RECORD) { #if defined(MBEDTLS_SSL_DTLS_CLIENT_PORT_REUSE) && defined(MBEDTLS_SSL_SRV_C) /* Reset in pointers to default state for TLS/DTLS records, From 16c5dd99b3278f3ed0582f76baa9a1bc031b5187 Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Wed, 18 Mar 2026 12:06:59 +0100 Subject: [PATCH 04/15] Introduce ssl_buffering_shift_slots Signed-off-by: Ronald Cron --- library/ssl_msg.c | 57 +++++++++++++++++++++++++++++++---------------- tests/ssl-opt.sh | 2 +- 2 files changed, 39 insertions(+), 20 deletions(-) diff --git a/library/ssl_msg.c b/library/ssl_msg.c index 65609b8ff..7eb91031d 100644 --- a/library/ssl_msg.c +++ b/library/ssl_msg.c @@ -275,6 +275,7 @@ exit: /* Forward declarations for functions related to message buffering. */ static void ssl_buffering_free_slot(mbedtls_ssl_context *ssl, uint8_t slot); +static void ssl_buffering_shift_slots(mbedtls_ssl_context *ssl, unsigned shift); static void ssl_free_buffered_record(mbedtls_ssl_context *ssl); MBEDTLS_CHECK_RETURN_CRITICAL static int ssl_load_buffered_message(mbedtls_ssl_context *ssl); @@ -3180,28 +3181,10 @@ int mbedtls_ssl_update_handshake_status(mbedtls_ssl_context *ssl) #if defined(MBEDTLS_SSL_PROTO_DTLS) if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM && ssl->handshake != NULL) { - unsigned offset; - mbedtls_ssl_hs_buffer *hs_buf; /* Increment handshake sequence number */ hs->in_msg_seq++; - - /* - * Clear up handshake buffering and reassembly structure. - */ - - /* Free first entry */ - ssl_buffering_free_slot(ssl, 0); - - /* Shift all other entries */ - for (offset = 0, hs_buf = &hs->buffering.hs[0]; - offset + 1 < MBEDTLS_SSL_MAX_BUFFERED_HS; - offset++, hs_buf++) { - *hs_buf = *(hs_buf + 1); - } - - /* Create a fresh last entry */ - memset(hs_buf, 0, sizeof(mbedtls_ssl_hs_buffer)); + ssl_buffering_shift_slots(ssl, 1); } #endif return 0; @@ -6158,6 +6141,42 @@ static void ssl_buffering_free_slot(mbedtls_ssl_context *ssl, } } +/* + * Shift the buffering slots to the left by `shift` positions. + * After the operation, slot i contains the previous slot i + shift. + */ +static void ssl_buffering_shift_slots(mbedtls_ssl_context *ssl, + unsigned shift) +{ + mbedtls_ssl_handshake_params * const hs = ssl->handshake; + unsigned offset; + + if (shift == 0) { + return; + } + + if (shift >= MBEDTLS_SSL_MAX_BUFFERED_HS) { + shift = MBEDTLS_SSL_MAX_BUFFERED_HS; + } + + /* Free discarded entries */ + for (offset = 0; offset < shift; offset++) { + ssl_buffering_free_slot(ssl, offset); + } + + /* Shift remaining entries left */ + for (offset = 0; offset + shift < MBEDTLS_SSL_MAX_BUFFERED_HS; offset++) { + hs->buffering.hs[offset] = hs->buffering.hs[offset + shift]; + } + + /* Reset the remaining entries at the end. It may have been already + * partially done by the loop freing the discarded entries but that is + * simpler and safer. + */ + for (; offset < MBEDTLS_SSL_MAX_BUFFERED_HS; offset++) { + memset(&hs->buffering.hs[offset], 0, sizeof(hs->buffering.hs[offset])); + } +} #endif /* MBEDTLS_SSL_PROTO_DTLS */ /* diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh index a999c94f5..b5e968b75 100755 --- a/tests/ssl-opt.sh +++ b/tests/ssl-opt.sh @@ -2168,7 +2168,7 @@ run_test "Default, TLS 1.2" \ requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 requires_ciphersuite_enabled TLS-ECDHE-RSA-WITH-CHACHA20-POLY1305-SHA256 run_test "Default, DTLS" \ - "$P_SRV dtls=1" \ + "$P_SRV debug_level=5 dtls=1" \ "$P_CLI dtls=1" \ 0 \ -s "Protocol is DTLSv1.2" \ From 912ef74195a105650bb95c8296dc880c241d4369 Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Wed, 18 Mar 2026 12:15:52 +0100 Subject: [PATCH 05/15] Update buffering when adapting to ClientHello message_seq Signed-off-by: Ronald Cron --- library/ssl_msg.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/library/ssl_msg.c b/library/ssl_msg.c index 7eb91031d..6a9d88d85 100644 --- a/library/ssl_msg.c +++ b/library/ssl_msg.c @@ -2986,17 +2986,21 @@ int mbedtls_ssl_prepare_handshake_record(mbedtls_ssl_context *ssl) * expected `message_seq` for the incoming and outgoing * handshake messages. */ - ssl->handshake->in_msg_seq = recv_msg_seq; - ssl->handshake->out_msg_seq = recv_msg_seq; + if ((ssl->handshake->in_msg_seq == 0) && (recv_msg_seq > 0)) { + MBEDTLS_SSL_DEBUG_MSG(1, ("shift slots by %u", recv_msg_seq)); + ssl_buffering_shift_slots(ssl, recv_msg_seq); + ssl->handshake->in_msg_seq = recv_msg_seq; + ssl->handshake->out_msg_seq = recv_msg_seq; - /* Epoch should be 0 for initial handshakes */ - if (ssl->in_ctr[0] != 0 || ssl->in_ctr[1] != 0) { - MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message")); - return MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER; + /* Epoch should be 0 for initial handshakes */ + if (ssl->in_ctr[0] != 0 || ssl->in_ctr[1] != 0) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message")); + return MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER; + } + + memcpy(&ssl->cur_out_ctr[2], ssl->in_ctr + 2, + sizeof(ssl->cur_out_ctr) - 2); } - - memcpy(&ssl->cur_out_ctr[2], ssl->in_ctr + 2, - sizeof(ssl->cur_out_ctr) - 2); } else if (mbedtls_ssl_is_handshake_over(ssl) == 1) { /* In case of a post-handshake ClientHello that initiates a * renegotiation check that the handshake message sequence From 0c301a686aa296433d01f4d5db7f4612a1da4aaa Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Mon, 23 Mar 2026 15:40:02 +0100 Subject: [PATCH 06/15] dtls: Improve comment Signed-off-by: Ronald Cron --- library/ssl_msg.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/ssl_msg.c b/library/ssl_msg.c index 6a9d88d85..287b0bf50 100644 --- a/library/ssl_msg.c +++ b/library/ssl_msg.c @@ -6174,8 +6174,8 @@ static void ssl_buffering_shift_slots(mbedtls_ssl_context *ssl, } /* Reset the remaining entries at the end. It may have been already - * partially done by the loop freing the discarded entries but that is - * simpler and safer. + * done for the first ones by the loop freing the discarded entries but + * that is simpler and safer. */ for (; offset < MBEDTLS_SSL_MAX_BUFFERED_HS; offset++) { memset(&hs->buffering.hs[offset], 0, sizeof(hs->buffering.hs[offset])); From f9b7441542734d91bcd52cb234ccd76409559745 Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Mon, 23 Mar 2026 18:23:03 +0100 Subject: [PATCH 07/15] dtls: Keep invalid/unexpected record header error code Signed-off-by: Ronald Cron --- library/ssl_msg.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/library/ssl_msg.c b/library/ssl_msg.c index 287b0bf50..812f578ba 100644 --- a/library/ssl_msg.c +++ b/library/ssl_msg.c @@ -4748,16 +4748,7 @@ static int ssl_get_next_record(mbedtls_ssl_context *ssl) && (ssl->renego_status == MBEDTLS_SSL_INITIAL_HANDSHAKE) #endif ) { - /* - * For backward compatibility, return - * MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE rather than - * MBEDTLS_ERR_SSL_UNEXPECTED_RECORD. - */ - if (ret == MBEDTLS_ERR_SSL_UNEXPECTED_RECORD) { - return MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE; - } else { - return ret; - } + return ret; } #endif /* MBEDTLS_SSL_SRV_C */ From 140ebea442ce2436f2f8ce59b554df6a70baad2f Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Tue, 24 Mar 2026 00:48:28 +0100 Subject: [PATCH 08/15] dtls: parse_client_hello: Adapt mbedtls_ssl_read_record() error code Signed-off-by: Ronald Cron --- library/ssl_tls12_server.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/library/ssl_tls12_server.c b/library/ssl_tls12_server.c index 94e61a8ac..5dbdd3854 100644 --- a/library/ssl_tls12_server.c +++ b/library/ssl_tls12_server.c @@ -879,6 +879,31 @@ static int ssl_parse_client_hello(mbedtls_ssl_context *ssl) */ if ((ret = mbedtls_ssl_read_record(ssl, 0)) != 0) { MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_read_record ", ret); + + /* + * In the case of an alert message corresponding to the termination of + * a previous connection, `ssl_parse_record_header()` and then + * `mbedtls_ssl_read_record()` may return + * MBEDTLS_ERR_SSL_UNEXPECTED_RECORD because of a non zero epoch. + * + * Historically, the library has returned + * MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE in this situation. + * The sample program dtls_server.c relies on this behavior + * (see + * https://github.com/Mbed-TLS/mbedtls/blob/d5e35a376bee23fad0b17f2e3e94a32ce4017c64/programs/ssl/dtls_server.c#L295), + * and user applications may rely on it as well. + * + * For compatibility, map MBEDTLS_ERR_SSL_UNEXPECTED_RECORD + * to MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE here. + * + * MBEDTLS_ERR_SSL_UNEXPECTED_RECORD does not appear to be + * used to detect a specific error condition, so this mapping + * should not remove any meaningful distinction. + */ + if (ret == MBEDTLS_ERR_SSL_UNEXPECTED_RECORD) { + ret = MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE; + } + return ret; } From c9264ad227d1e70780bfd6a1e4db3fd70a7155b3 Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Mon, 23 Mar 2026 16:25:33 +0100 Subject: [PATCH 09/15] dtls: Fix log level Signed-off-by: Ronald Cron --- library/ssl_msg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/ssl_msg.c b/library/ssl_msg.c index 812f578ba..c0c2825c4 100644 --- a/library/ssl_msg.c +++ b/library/ssl_msg.c @@ -2987,7 +2987,7 @@ int mbedtls_ssl_prepare_handshake_record(mbedtls_ssl_context *ssl) * handshake messages. */ if ((ssl->handshake->in_msg_seq == 0) && (recv_msg_seq > 0)) { - MBEDTLS_SSL_DEBUG_MSG(1, ("shift slots by %u", recv_msg_seq)); + MBEDTLS_SSL_DEBUG_MSG(3, ("shift slots by %u", recv_msg_seq)); ssl_buffering_shift_slots(ssl, recv_msg_seq); ssl->handshake->in_msg_seq = recv_msg_seq; ssl->handshake->out_msg_seq = recv_msg_seq; From f285018fa368ae832d43309e14703501038fee90 Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Tue, 24 Mar 2026 10:03:21 +0100 Subject: [PATCH 10/15] Disable "DTLS proxy: 3d, (openssl|gnutls) client, fragmentation" tests The tests fail intermittently on the CI with a frequency that significantly impacts CI throughput. Signed-off-by: Ronald Cron --- tests/scripts/analyze_outcomes.py | 9 +++++++++ tests/ssl-opt.sh | 13 +++++++++++++ 2 files changed, 22 insertions(+) diff --git a/tests/scripts/analyze_outcomes.py b/tests/scripts/analyze_outcomes.py index 2bd4bd816..b6f18c5b8 100755 --- a/tests/scripts/analyze_outcomes.py +++ b/tests/scripts/analyze_outcomes.py @@ -50,6 +50,15 @@ class CoverageTask(outcome_analysis.CoverageTask): # TLS doesn't use restartable ECDH yet. # https://github.com/Mbed-TLS/mbedtls/issues/7294 re.compile(r'EC restart:.*no USE_PSA.*'), + # The following test fails intermittently on the CI with a frequency + # that significantly impacts CI throughput. They are thus disabled + # for the time being. See + # https://github.com/Mbed-TLS/mbedtls/issues/10652 for more + # information. + 'DTLS proxy: 3d, openssl client, fragmentation', + 'DTLS proxy: 3d, openssl client, fragmentation, nbio', + 'DTLS proxy: 3d, gnutls client, fragmentation', + 'DTLS proxy: 3d, gnutls client, fragmentation, nbio=2', ], 'test_suite_config.mbedtls_boolean': [ # Missing coverage of test configurations. diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh index b5e968b75..7abcd4b96 100755 --- a/tests/ssl-opt.sh +++ b/tests/ssl-opt.sh @@ -12169,6 +12169,9 @@ run_test "DTLS proxy: 3d, openssl client" \ 0 \ -s "HTTP/1.0 200 OK" +# The following test fails intermittently on the CI with a frequency that +# significantly impacts CI throughput. Disable it for the time being. +skip_next_test requires_openssl_next client_needs_more_time 8 not_with_valgrind # risk of non-mbedtls peer timing out @@ -12182,6 +12185,9 @@ run_test "DTLS proxy: 3d, openssl client, fragmentation" \ -s "found fragmented DTLS handshake message" \ -s "Certificate handshake message has been buffered and reassembled" +# The following test fails intermittently on the CI with a frequency that +# significantly impacts CI throughput. Disable it for the time being. +skip_next_test requires_openssl_next client_needs_more_time 8 not_with_valgrind # risk of non-mbedtls peer timing out @@ -12250,6 +12256,10 @@ run_test "DTLS proxy: 3d, gnutls client" \ # fragmentation to remain the case across GnuTLS version updates. Avoid using a # smaller MTU, as the smaller the MTU, the more likely the handshake is to fail # in this very unreliable connection emulation. + +# The following test fails intermittently on the CI with a frequency that +# significantly impacts CI throughput. Disable it for the time being. +skip_next_test requires_gnutls client_needs_more_time 8 not_with_valgrind # risk of non-mbedtls peer timing out @@ -12262,6 +12272,9 @@ run_test "DTLS proxy: 3d, gnutls client, fragmentation" \ -s "HTTP/1.0 200 OK" \ -s "ClientHello handshake message has been buffered and reassembled" +# The following test fails intermittently on the CI with a frequency that +# significantly impacts CI throughput. Disable it for the time being. +skip_next_test requires_gnutls client_needs_more_time 8 not_with_valgrind # risk of non-mbedtls peer timing out From fbe388dc287bfe3414b565c0a009c879172c744e Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Tue, 24 Mar 2026 12:25:34 +0100 Subject: [PATCH 11/15] ssl-opt.sh: Fix log checks in some "DTLS reassembly" tests In DTLS reassembly tests, the server may receive a close_notify alert at the end of a test. In this case, the Mbed TLS server logs an error, so these tests should not check for the absence of the string "error" in the server logs. Signed-off-by: Ronald Cron --- tests/ssl-opt.sh | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh index 7abcd4b96..aa339a9ea 100755 --- a/tests/ssl-opt.sh +++ b/tests/ssl-opt.sh @@ -9978,7 +9978,7 @@ run_test "DTLS reassembly: no fragmentation (gnutls client)" \ "$G_NEXT_CLI -u --mtu 2048 --insecure 127.0.0.1" \ 0 \ -S "found fragmented DTLS handshake message" \ - -S "error" + -s "HTTP/1.0 200 OK" requires_gnutls requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 @@ -9988,7 +9988,7 @@ run_test "DTLS reassembly: some fragmentation (gnutls client)" \ 0 \ -s "found fragmented DTLS handshake message" \ -s "Certificate handshake message has been buffered and reassembled" \ - -S "error" + -s "HTTP/1.0 200 OK" # Set the MTU to 128 bytes. The minimum size of a DTLS 1.2 record # containing a ClientHello handshake message is 69 bytes, without any cookie, @@ -10003,7 +10003,7 @@ run_test "DTLS reassembly: more fragmentation (gnutls client)" \ "$G_NEXT_CLI -u --mtu 103 --insecure 127.0.0.1" \ 0 \ -s "ClientHello handshake message has been buffered and reassembled" \ - -S "error" + -s "HTTP/1.0 200 OK" requires_gnutls requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 @@ -10012,7 +10012,7 @@ run_test "DTLS reassembly: more fragmentation, nbio (gnutls client)" \ "$G_NEXT_CLI -u --mtu 103 --insecure 127.0.0.1" \ 0 \ -s "ClientHello handshake message has been buffered and reassembled" \ - -S "error" + -s "HTTP/1.0 200 OK" # No fragmentation and renegotiation tests with GnuTLS client as the feature # does not work properly. @@ -10053,7 +10053,7 @@ run_test "DTLS reassembly: no fragmentation (openssl client)" \ "$O_NEXT_CLI -dtls -mtu 2048 -cert $DATA_FILES_PATH/server5.crt -key $DATA_FILES_PATH/server5.key" \ 0 \ -S "found fragmented DTLS handshake message" \ - -S "error" + -s "HTTP/1.0 200 OK" # Minimum possible MTU for OpenSSL server: 256 bytes. # We expect the client Certificate handshake message to be fragmented and @@ -10068,7 +10068,7 @@ run_test "DTLS reassembly: some fragmentation (openssl client)" \ 0 \ -s "found fragmented DTLS handshake message" \ -s "Certificate handshake message has been buffered and reassembled" \ - -S "error" + -s "HTTP/1.0 200 OK" requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 run_test "DTLS reassembly: fragmentation, nbio (openssl client)" \ @@ -10077,7 +10077,7 @@ run_test "DTLS reassembly: fragmentation, nbio (openssl client)" \ 0 \ -s "found fragmented DTLS handshake message" \ -s "Certificate handshake message has been buffered and reassembled" \ - -S "error" + -s "HTTP/1.0 200 OK" # Tests for sending fragmented handshake messages with DTLS # From f2f44a9c9f7c3c3d66324029d1131f5bc1d5910e Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Tue, 24 Mar 2026 15:42:42 +0100 Subject: [PATCH 12/15] Restrict mapping of UNEXPECTED_RECORD to UNEXPECTED_MESSAGE Signed-off-by: Ronald Cron --- library/ssl_tls12_server.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/library/ssl_tls12_server.c b/library/ssl_tls12_server.c index 5dbdd3854..26ba8590a 100644 --- a/library/ssl_tls12_server.c +++ b/library/ssl_tls12_server.c @@ -880,6 +880,7 @@ static int ssl_parse_client_hello(mbedtls_ssl_context *ssl) if ((ret = mbedtls_ssl_read_record(ssl, 0)) != 0) { MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_read_record ", ret); +#if defined(MBEDTLS_SSL_PROTO_DTLS) /* * In the case of an alert message corresponding to the termination of * a previous connection, `ssl_parse_record_header()` and then @@ -900,9 +901,16 @@ static int ssl_parse_client_hello(mbedtls_ssl_context *ssl) * used to detect a specific error condition, so this mapping * should not remove any meaningful distinction. */ - if (ret == MBEDTLS_ERR_SSL_UNEXPECTED_RECORD) { - ret = MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE; + if ((ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM) +#if defined(MBEDTLS_SSL_RENEGOTIATION) + && (ssl->renego_status == MBEDTLS_SSL_INITIAL_HANDSHAKE) +#endif + ) { + if (ret == MBEDTLS_ERR_SSL_UNEXPECTED_RECORD) { + ret = MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE; + } } +#endif /* MBEDTLS_SSL_PROTO_DTLS */ return ret; } From 1141cd0fb634c754a4ad9a3572621b0656511247 Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Tue, 24 Mar 2026 15:47:55 +0100 Subject: [PATCH 13/15] Improve comments Signed-off-by: Ronald Cron --- library/ssl_msg.c | 20 +++++++++++++++----- tests/ssl-opt.sh | 4 ++++ 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/library/ssl_msg.c b/library/ssl_msg.c index c0c2825c4..0799a0067 100644 --- a/library/ssl_msg.c +++ b/library/ssl_msg.c @@ -4739,8 +4739,18 @@ static int ssl_get_next_record(mbedtls_ssl_context *ssl) #if defined(MBEDTLS_SSL_SRV_C) /* - * When retrieving the DTLS ClientHello on server side, error out - * when detecting an invalid or unexpected record. + * In DTLS, invalid records are usually ignored because it is easy + * for an attacker to inject UDP datagrams, and we do not want such + * packets to disrupt the entire connection. + * + * However, when expecting the ClientHello, we reject invalid or + * unexpected records. This avoids waiting for further records + * before receiving at least one valid message. Such records could + * be leftover messages from a previous connection, accidental + * input, or part of a DoS attempt. + * + * Since no valid message has been received yet, immediately + * closing the connection does not result in any loss. */ if ((ssl->conf->endpoint == MBEDTLS_SSL_IS_SERVER) && (ssl->state == MBEDTLS_SSL_CLIENT_HELLO) @@ -6164,9 +6174,9 @@ static void ssl_buffering_shift_slots(mbedtls_ssl_context *ssl, hs->buffering.hs[offset] = hs->buffering.hs[offset + shift]; } - /* Reset the remaining entries at the end. It may have been already - * done for the first ones by the loop freing the discarded entries but - * that is simpler and safer. + /* Reset the remaining entries at the end. Some may already have been + * cleared by the loop freeing the discarded entries, but resetting all + * of them is simpler and avoids tracking which ones were already handled. */ for (; offset < MBEDTLS_SSL_MAX_BUFFERED_HS; offset++) { memset(&hs->buffering.hs[offset], 0, sizeof(hs->buffering.hs[offset])); diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh index aa339a9ea..2b0341ebe 100755 --- a/tests/ssl-opt.sh +++ b/tests/ssl-opt.sh @@ -12171,6 +12171,7 @@ run_test "DTLS proxy: 3d, openssl client" \ # The following test fails intermittently on the CI with a frequency that # significantly impacts CI throughput. Disable it for the time being. +# See https://github.com/Mbed-TLS/mbedtls/issues/10652 for more information. skip_next_test requires_openssl_next client_needs_more_time 8 @@ -12187,6 +12188,7 @@ run_test "DTLS proxy: 3d, openssl client, fragmentation" \ # The following test fails intermittently on the CI with a frequency that # significantly impacts CI throughput. Disable it for the time being. +# See https://github.com/Mbed-TLS/mbedtls/issues/10652 for more information. skip_next_test requires_openssl_next client_needs_more_time 8 @@ -12259,6 +12261,7 @@ run_test "DTLS proxy: 3d, gnutls client" \ # The following test fails intermittently on the CI with a frequency that # significantly impacts CI throughput. Disable it for the time being. +# See https://github.com/Mbed-TLS/mbedtls/issues/10652 for more information. skip_next_test requires_gnutls client_needs_more_time 8 @@ -12274,6 +12277,7 @@ run_test "DTLS proxy: 3d, gnutls client, fragmentation" \ # The following test fails intermittently on the CI with a frequency that # significantly impacts CI throughput. Disable it for the time being. +# See https://github.com/Mbed-TLS/mbedtls/issues/10652 for more information. skip_next_test requires_gnutls client_needs_more_time 8 From 7a8fbc2100fc2d04e558526c1cc5b7f3c18e58b5 Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Tue, 24 Mar 2026 15:49:25 +0100 Subject: [PATCH 14/15] Remove debug leftover Signed-off-by: Ronald Cron --- tests/ssl-opt.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh index 2b0341ebe..4d0b6f608 100755 --- a/tests/ssl-opt.sh +++ b/tests/ssl-opt.sh @@ -2168,7 +2168,7 @@ run_test "Default, TLS 1.2" \ requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 requires_ciphersuite_enabled TLS-ECDHE-RSA-WITH-CHACHA20-POLY1305-SHA256 run_test "Default, DTLS" \ - "$P_SRV debug_level=5 dtls=1" \ + "$P_SRV dtls=1" \ "$P_CLI dtls=1" \ 0 \ -s "Protocol is DTLSv1.2" \ From 1330606ca1ea4d9296fc97ed320735075293e2f6 Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Tue, 24 Mar 2026 16:49:34 +0100 Subject: [PATCH 15/15] dtls: Fix adaptation to first ClientHello For each received ClientHello fragment, check that its epoch is zero and update the record-level sequence number. Signed-off-by: Ronald Cron --- library/ssl_msg.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/library/ssl_msg.c b/library/ssl_msg.c index 0799a0067..87d64788b 100644 --- a/library/ssl_msg.c +++ b/library/ssl_msg.c @@ -2991,16 +2991,17 @@ int mbedtls_ssl_prepare_handshake_record(mbedtls_ssl_context *ssl) ssl_buffering_shift_slots(ssl, recv_msg_seq); ssl->handshake->in_msg_seq = recv_msg_seq; ssl->handshake->out_msg_seq = recv_msg_seq; - - /* Epoch should be 0 for initial handshakes */ - if (ssl->in_ctr[0] != 0 || ssl->in_ctr[1] != 0) { - MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message")); - return MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER; - } - - memcpy(&ssl->cur_out_ctr[2], ssl->in_ctr + 2, - sizeof(ssl->cur_out_ctr) - 2); } + + /* Epoch should be 0 for initial handshakes */ + if (ssl->in_ctr[0] != 0 || ssl->in_ctr[1] != 0) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message")); + return MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER; + } + + memcpy(&ssl->cur_out_ctr[2], ssl->in_ctr + 2, + sizeof(ssl->cur_out_ctr) - 2); + } else if (mbedtls_ssl_is_handshake_over(ssl) == 1) { /* In case of a post-handshake ClientHello that initiates a * renegotiation check that the handshake message sequence