Add an out_cid case to the ssl_context_load OOB length test

Cover the serialized out_cid length with the same construction as the
in_cid one, and rename the existing case to IN_CID accordingly. Give
the client and server sentinel CIDs different lengths so the two
fields can be told apart in the serialized data. Skip the CID cases
under configurations of MBEDTLS_SSL_CID_IN_LEN_MAX and
MBEDTLS_SSL_CID_OUT_LEN_MAX that the test does not support.

Signed-off-by: Naveed <naveed@bugqore.com>
This commit is contained in:
Naveed 2026-09-22 17:27:35 +05:30
parent a39d101718
commit fbc06d7909
2 changed files with 56 additions and 29 deletions

View File

@ -3054,9 +3054,13 @@ 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_PROTO_TLS1_3:MBEDTLS_SSL_SESSION_TICKETS:MBEDTLS_SSL_SRV_C:MBEDTLS_SSL_EARLY_DATA:MBEDTLS_SSL_ALPN
ssl_session_load_rejects_bad_serialized_data:MBEDTLS_SSL_VERSION_TLS1_3:MBEDTLS_SSL_IS_SERVER:TEST_TLS13_SESSION_LOAD_ALPN_NO_NUL ssl_session_load_rejects_bad_serialized_data:MBEDTLS_SSL_VERSION_TLS1_3:MBEDTLS_SSL_IS_SERVER:TEST_TLS13_SESSION_LOAD_ALPN_NO_NUL
SSL context serialization rejects out-of-bounds DTLS CID length SSL context serialization rejects out-of-bounds DTLS in_cid length
depends_on:MBEDTLS_SSL_DTLS_CONNECTION_ID depends_on:MBEDTLS_SSL_DTLS_CONNECTION_ID
ssl_context_load_rejects_oob_length:TEST_SSL_CONTEXT_LOAD_OOB_CID ssl_context_load_rejects_oob_length:TEST_SSL_CONTEXT_LOAD_OOB_IN_CID
SSL context serialization rejects out-of-bounds DTLS out_cid length
depends_on:MBEDTLS_SSL_DTLS_CONNECTION_ID
ssl_context_load_rejects_oob_length:TEST_SSL_CONTEXT_LOAD_OOB_OUT_CID
SSL context serialization rejects out-of-bounds ALPN length SSL context serialization rejects out-of-bounds ALPN length
depends_on:MBEDTLS_SSL_ALPN depends_on:MBEDTLS_SSL_ALPN

View File

@ -33,8 +33,9 @@
#define TEST_TLS12_SESSION_LOAD_OOB_ID_LEN 3 #define TEST_TLS12_SESSION_LOAD_OOB_ID_LEN 3
/* Corrupted length fields for ssl_context_load_rejects_oob_length */ /* Corrupted length fields for ssl_context_load_rejects_oob_length */
#define TEST_SSL_CONTEXT_LOAD_OOB_CID 0 #define TEST_SSL_CONTEXT_LOAD_OOB_IN_CID 0
#define TEST_SSL_CONTEXT_LOAD_OOB_ALPN 1 #define TEST_SSL_CONTEXT_LOAD_OOB_OUT_CID 1
#define TEST_SSL_CONTEXT_LOAD_OOB_ALPN 2
#if defined(MBEDTLS_SSL_PROTO_TLS1_2) && \ #if defined(MBEDTLS_SSL_PROTO_TLS1_2) && \
defined(MBEDTLS_SSL_SESSION_TICKETS) && \ defined(MBEDTLS_SSL_SESSION_TICKETS) && \
@ -3046,12 +3047,16 @@ exit:
void ssl_context_load_rejects_oob_length(int field) void ssl_context_load_rejects_oob_length(int field)
{ {
#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) #if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID)
/* Sentinel CID so we can find its location inside the serialized buffer. */ /* Sentinel CIDs so we can find their location inside the serialized
* buffer. The server's own CID is serialized as in_cid and the client's
* as out_cid; they have different lengths so that the two fields can be
* told apart. */
const unsigned char server_cid[] = { const unsigned char server_cid[] = {
0xAB, 0xCD, 0xEF, 0x12, 0x34, 0x56, 0x78, 0x9A 0xAB, 0xCD, 0xEF, 0x12, 0x34, 0x56, 0x78, 0x9A
}; };
const unsigned char client_cid[] = { const unsigned char client_cid[] = {
0x9A, 0x78, 0x56, 0x34, 0x12, 0xEF, 0xCD, 0xAB 0x9A, 0x78, 0x56, 0x34, 0x12, 0xEF,
0xCD, 0xAB, 0x10, 0x32, 0x54, 0x76
}; };
#endif #endif
#if defined(MBEDTLS_SSL_ALPN) #if defined(MBEDTLS_SSL_ALPN)
@ -3094,10 +3099,17 @@ void ssl_context_load_rejects_oob_length(int field)
switch (field) { switch (field) {
#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) #if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID)
case TEST_SSL_CONTEXT_LOAD_OOB_CID: case TEST_SSL_CONTEXT_LOAD_OOB_IN_CID:
case TEST_SSL_CONTEXT_LOAD_OOB_OUT_CID:
/* Each peer's own CID has to fit in MBEDTLS_SSL_CID_IN_LEN_MAX and
* its peer's CID in MBEDTLS_SSL_CID_OUT_LEN_MAX. */
TEST_ASSUME(MBEDTLS_SSL_CID_IN_LEN_MAX >= sizeof(server_cid) &&
MBEDTLS_SSL_CID_IN_LEN_MAX >= sizeof(client_cid));
TEST_ASSUME(MBEDTLS_SSL_CID_OUT_LEN_MAX >= sizeof(server_cid) &&
MBEDTLS_SSL_CID_OUT_LEN_MAX >= sizeof(client_cid));
/* Configure CID with unique patterns so we can locate the length /* Configure CID with unique patterns so we can locate the length
* byte in the serialized context. Both peers must agree on the CID * bytes in the serialized context. On each peer the length passed
* length via conf_cid() before set_cid() will accept the value. */ * to conf_cid() must match the one passed to set_cid(). */
TEST_EQUAL(mbedtls_ssl_conf_cid( TEST_EQUAL(mbedtls_ssl_conf_cid(
&client_ep.conf, sizeof(client_cid), &client_ep.conf, sizeof(client_cid),
MBEDTLS_SSL_UNEXPECTED_CID_FAIL), 0); MBEDTLS_SSL_UNEXPECTED_CID_FAIL), 0);
@ -3110,15 +3122,24 @@ void ssl_context_load_rejects_oob_length(int field)
TEST_EQUAL(mbedtls_ssl_set_cid( TEST_EQUAL(mbedtls_ssl_set_cid(
&server_ep.ssl, MBEDTLS_SSL_CID_ENABLED, &server_ep.ssl, MBEDTLS_SSL_CID_ENABLED,
server_cid, sizeof(server_cid)), 0); server_cid, sizeof(server_cid)), 0);
sentinel = server_cid; /* The corrupted length is the smallest one that overflows the
sentinel_len = sizeof(server_cid); * in_cid or out_cid array in the SSL transform, so the size check
/* The smallest length that overflows the in_cid array in the SSL * on that array, not the in-buffer bounds check before it, is
* transform, so the size check on that array, not the in-buffer * what must reject it. The loaded data gets the field grown to
* bounds check before it, is what must reject the corrupted * this many bytes (see the construction of load_buf below) so
* length. The loaded data gets the in_cid field grown to this * that the in-buffer bounds check indeed passes. A one-byte
* many bytes (see the construction of load_buf below) so that * serialized length cannot overflow a 255-byte CID array. */
* the in-buffer bounds check indeed passes. */ if (field == TEST_SSL_CONTEXT_LOAD_OOB_IN_CID) {
corrupt_len = (unsigned char) (MBEDTLS_SSL_CID_IN_LEN_MAX + 1); TEST_ASSUME(MBEDTLS_SSL_CID_IN_LEN_MAX < 255);
sentinel = server_cid;
sentinel_len = sizeof(server_cid);
corrupt_len = (unsigned char) (MBEDTLS_SSL_CID_IN_LEN_MAX + 1);
} else {
TEST_ASSUME(MBEDTLS_SSL_CID_OUT_LEN_MAX < 255);
sentinel = client_cid;
sentinel_len = sizeof(client_cid);
corrupt_len = (unsigned char) (MBEDTLS_SSL_CID_OUT_LEN_MAX + 1);
}
break; break;
#endif #endif
#if defined(MBEDTLS_SSL_ALPN) #if defined(MBEDTLS_SSL_ALPN)
@ -3181,13 +3202,14 @@ void ssl_context_load_rejects_oob_length(int field)
field_off = (size_t) (len_p + 1 - context_buf); field_off = (size_t) (len_p + 1 - context_buf);
if (field == TEST_SSL_CONTEXT_LOAD_OOB_CID) { if (field == TEST_SSL_CONTEXT_LOAD_OOB_IN_CID ||
/* The in_cid field sits in the middle of the serialized data, so a field == TEST_SSL_CONTEXT_LOAD_OOB_OUT_CID) {
/* The CID fields sit in the middle of the serialized data, so a
* corrupted length cannot reach past the end of the buffer. Instead * corrupted length cannot reach past the end of the buffer. Instead
* grow the field itself: keep its bytes, pad it to corrupt_len bytes * grow the field itself: keep its bytes, pad it to corrupt_len bytes
* and append the untouched rest of the serialized data, so that the * and append the untouched rest of the serialized data, so that the
* in-buffer bounds check passes and the size check on the in_cid * in-buffer bounds check passes and the size check on the CID array
* array is reached. */ * is reached. */
load_len = context_buf_len + corrupt_len - sentinel_len; load_len = context_buf_len + corrupt_len - sentinel_len;
TEST_CALLOC(load_buf, load_len); TEST_CALLOC(load_buf, load_len);
memcpy(load_buf, context_buf, field_off + sentinel_len); memcpy(load_buf, context_buf, field_off + sentinel_len);
@ -3197,8 +3219,9 @@ void ssl_context_load_rejects_oob_length(int field)
context_buf + field_off + sentinel_len, context_buf + field_off + sentinel_len,
context_buf_len - field_off - sentinel_len); context_buf_len - field_off - sentinel_len);
/* Double-check that corrupt_len bytes of field data plus the next /* Double-check that corrupt_len bytes of field data plus the next
* length byte are present, which is what the in-buffer bounds check * byte are present, which is the most the in-buffer bounds checks
* requires. */ * require (the in_cid one also accounts for the out_cid length
* byte). */
TEST_ASSERT(load_len - field_off >= (size_t) corrupt_len + 1); TEST_ASSERT(load_len - field_off >= (size_t) corrupt_len + 1);
} else { } else {
/* The ALPN field is the last one saved, so the buffer already ends /* The ALPN field is the last one saved, so the buffer already ends
@ -3224,11 +3247,11 @@ void ssl_context_load_rejects_oob_length(int field)
* out-of-bounds bytes are unlikely to match a configured protocol; what * out-of-bounds bytes are unlikely to match a configured protocol; what
* the missing check leaks is the out-of-bounds read in memcmp() itself, * the missing check leaks is the out-of-bounds read in memcmp() itself,
* so that case catches a regression only when the test suite runs under * so that case catches a regression only when the test suite runs under
* ASan or Valgrind. In the CID case, without the size check the * ASan or Valgrind. In the CID cases, without the size check the
* memcpy() overflows the in_cid array inside the SSL transform (which * memcpy() overflows the in_cid or out_cid array inside the SSL
* stays within the enclosing struct, invisible to ASan), the rest of * transform (which stays within the enclosing struct, invisible to
* the data then parses correctly aligned and the load succeeds, so the * ASan), the rest of the data then parses correctly aligned and the
* check below catches a regression on its own. */ * load succeeds, so the check below catches a regression on its own. */
TEST_EQUAL(mbedtls_ssl_context_load(&server_ep.ssl, load_buf, load_len), TEST_EQUAL(mbedtls_ssl_context_load(&server_ep.ssl, load_buf, load_len),
MBEDTLS_ERR_SSL_BAD_INPUT_DATA); MBEDTLS_ERR_SSL_BAD_INPUT_DATA);