From 36d75df56bfe03f7b25472e1fd5cfa4b389c33b4 Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Fri, 20 Mar 2026 20:42:10 +0100 Subject: [PATCH 01/16] Revert "ssl_server2.c: DTLS: Attempt to read the response to the close notification" This reverts commit 0a8c35d273257d7102ef8af1e351fa9c7bb688d9. 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 b01fcfcb05..82c01e1168 100644 --- a/programs/ssl/ssl_server2.c +++ b/programs/ssl/ssl_server2.c @@ -4235,55 +4235,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 1bbed59929..22da5ee4ed 100755 --- a/tests/compat.sh +++ b/tests/compat.sh @@ -660,7 +660,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 731f695bd5..78a6a8ffbe 100644 --- a/tests/scripts/components-configuration-tls.sh +++ b/tests/scripts/components-configuration-tls.sh @@ -385,7 +385,6 @@ component_test_tls1_2_ccm_psk_psa () { component_test_tls1_2_ccm_psk_dtls_legacy () { msg "build: configs/config-ccm-psk-dtls1_2.h" cp configs/config-ccm-psk-dtls1_2.h "$CONFIG_H" - scripts/config.py set MBEDTLS_HAVE_TIME # test-ref-configs works by overwriting mbedtls_config.h; this makes cmake # want to re-generate generated files that depend on it, quite correctly. # However this doesn't work as the generation script expects a specific From f8dd49e047d05bed30c592290a31ac74403968de Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Fri, 20 Mar 2026 11:21:56 +0100 Subject: [PATCH 02/16] 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 7a7182c2be..7c74de0cb2 100644 --- a/library/ssl_msg.c +++ b/library/ssl_msg.c @@ -3910,7 +3910,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 072ddb0346844870ad524d3e501c1849c325741e Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Fri, 20 Mar 2026 17:19:10 +0100 Subject: [PATCH 03/16] 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 7c74de0cb2..6eeb70fb73 100644 --- a/library/ssl_msg.c +++ b/library/ssl_msg.c @@ -5042,6 +5042,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 7fabb42524c81b116d4816e00af9678344261c0b Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Wed, 18 Mar 2026 12:06:59 +0100 Subject: [PATCH 04/16] 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 6eeb70fb73..54900307a3 100644 --- a/library/ssl_msg.c +++ b/library/ssl_msg.c @@ -376,6 +376,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); @@ -3472,28 +3473,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; @@ -6494,6 +6477,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 6e91581cc3..509fb0e980 100755 --- a/tests/ssl-opt.sh +++ b/tests/ssl-opt.sh @@ -2256,7 +2256,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 7a50711061fc46a5a5d0d2f106230c05cdc8309b Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Wed, 18 Mar 2026 12:15:52 +0100 Subject: [PATCH 05/16] Update buffering when adapting to ClientHello message_seq Credit to OSS-Fuzz for detecting the problem. 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 54900307a3..89c1ab9325 100644 --- a/library/ssl_msg.c +++ b/library/ssl_msg.c @@ -3280,17 +3280,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 82be755414441a504f3f0a227a58bff5f379a5b8 Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Mon, 23 Mar 2026 15:40:02 +0100 Subject: [PATCH 06/16] 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 89c1ab9325..6211b8d6ae 100644 --- a/library/ssl_msg.c +++ b/library/ssl_msg.c @@ -6510,8 +6510,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 4decf929566e7b60b8eda2c0dfd17863b8ab3f32 Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Mon, 23 Mar 2026 18:23:03 +0100 Subject: [PATCH 07/16] 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 6211b8d6ae..829df7f051 100644 --- a/library/ssl_msg.c +++ b/library/ssl_msg.c @@ -5040,16 +5040,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 243a28c7fd0caa982c61dee7bb4bdd70026e4975 Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Tue, 24 Mar 2026 00:48:28 +0100 Subject: [PATCH 08/16] 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 0fbe519ce1..d9addc7424 100644 --- a/library/ssl_tls12_server.c +++ b/library/ssl_tls12_server.c @@ -922,6 +922,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 db25da0f325770b043bec2a42e5fce3f24c08cbb Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Mon, 23 Mar 2026 16:25:33 +0100 Subject: [PATCH 09/16] 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 829df7f051..d48985a958 100644 --- a/library/ssl_msg.c +++ b/library/ssl_msg.c @@ -3281,7 +3281,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 ed156d0699fa88b07674bc92818729d4c82f9388 Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Tue, 24 Mar 2026 10:03:21 +0100 Subject: [PATCH 10/16] 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 0266f1457c..f6921d5238 100755 --- a/tests/scripts/analyze_outcomes.py +++ b/tests/scripts/analyze_outcomes.py @@ -53,6 +53,15 @@ class CoverageTask(outcome_analysis.CoverageTask): # https://github.com/Mbed-TLS/mbedtls/issues/9581 'Opaque key for server authentication: invalid key: decrypt with ECC key, no async', 'Opaque key for server authentication: invalid key: ecdh with RSA key, no async', + # 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': [ # We never test with CBC/PKCS5/PKCS12 enabled but diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh index 509fb0e980..535388d902 100755 --- a/tests/ssl-opt.sh +++ b/tests/ssl-opt.sh @@ -13177,6 +13177,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 @@ -13190,6 +13193,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 @@ -13258,6 +13264,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 @@ -13270,6 +13280,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 42dec1f97cc908f9fd4682093c1f6aa451d4e47c Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Tue, 24 Mar 2026 12:25:34 +0100 Subject: [PATCH 11/16] 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 535388d902..d32880d046 100755 --- a/tests/ssl-opt.sh +++ b/tests/ssl-opt.sh @@ -10989,7 +10989,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 @@ -10999,7 +10999,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, @@ -11014,7 +11014,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 @@ -11023,7 +11023,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. @@ -11064,7 +11064,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 @@ -11079,7 +11079,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)" \ @@ -11088,7 +11088,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 9be94ba6aeca1f7430a1498b69472b098b4e9ecb Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Tue, 24 Mar 2026 15:42:42 +0100 Subject: [PATCH 12/16] 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 d9addc7424..f5d0d79632 100644 --- a/library/ssl_tls12_server.c +++ b/library/ssl_tls12_server.c @@ -923,6 +923,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 @@ -943,9 +944,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 3a3d1d3dcd684812e78604e3dd2d790cc34f44f0 Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Tue, 24 Mar 2026 15:47:55 +0100 Subject: [PATCH 13/16] 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 d48985a958..469afd538d 100644 --- a/library/ssl_msg.c +++ b/library/ssl_msg.c @@ -5031,8 +5031,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) @@ -6500,9 +6510,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 d32880d046..8bc62f9796 100755 --- a/tests/ssl-opt.sh +++ b/tests/ssl-opt.sh @@ -13179,6 +13179,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 @@ -13195,6 +13196,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 @@ -13267,6 +13269,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 @@ -13282,6 +13285,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 953c584eec7ef6dcbfeea9288858ff71e9a24e28 Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Tue, 24 Mar 2026 15:49:25 +0100 Subject: [PATCH 14/16] 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 8bc62f9796..6ea8db1e01 100755 --- a/tests/ssl-opt.sh +++ b/tests/ssl-opt.sh @@ -2256,7 +2256,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 eb1cdf45cdeccb19973ff73a20e5006e22d207da Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Tue, 24 Mar 2026 16:49:34 +0100 Subject: [PATCH 15/16] 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 469afd538d..b0ab608350 100644 --- a/library/ssl_msg.c +++ b/library/ssl_msg.c @@ -3285,16 +3285,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 From 0c718e863a5cc1895205e4ea470945e28c79001e Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Wed, 25 Mar 2026 15:05:53 +0100 Subject: [PATCH 16/16] dtls: Log mapping of UNEXPECTED_RECORD to UNEXPECTED_MESSAGE Signed-off-by: Ronald Cron --- library/ssl_tls12_server.c | 1 + 1 file changed, 1 insertion(+) diff --git a/library/ssl_tls12_server.c b/library/ssl_tls12_server.c index f5d0d79632..d2ee9a0760 100644 --- a/library/ssl_tls12_server.c +++ b/library/ssl_tls12_server.c @@ -950,6 +950,7 @@ static int ssl_parse_client_hello(mbedtls_ssl_context *ssl) #endif ) { if (ret == MBEDTLS_ERR_SSL_UNEXPECTED_RECORD) { + MBEDTLS_SSL_DEBUG_MSG(1, ("mapping UNEXPECTED_RECORD to UNEXPECTED_MESSAGE")); ret = MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE; } }