From ed7153ef41bba1d01b1fb437a40ff1bbefdbe734 Mon Sep 17 00:00:00 2001 From: "harshal.patil" Date: Mon, 24 Aug 2026 11:47:34 +0530 Subject: [PATCH 1/2] fix(mbedtls): validate cert header extent before reading it in bundle check esp_crt_check_bundle() read the 4-byte certificate header (name_len, key_len) via esp_crt_get_len() after only checking that the cert's start offset lies inside the bundle, so a crafted bundle whose first or last certificate starts within the final 3 bytes caused a transient out-of-bounds read of up to 3 bytes before the extent check rejected it. Require the whole header to lie inside the bundle before reading it. --- components/mbedtls/esp_crt_bundle/esp_crt_bundle.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/components/mbedtls/esp_crt_bundle/esp_crt_bundle.c b/components/mbedtls/esp_crt_bundle/esp_crt_bundle.c index 5fa07f678a8..38394021b24 100644 --- a/components/mbedtls/esp_crt_bundle/esp_crt_bundle.c +++ b/components/mbedtls/esp_crt_bundle/esp_crt_bundle.c @@ -442,6 +442,9 @@ static bool esp_crt_check_bundle(const uint8_t* const x509_bundle, const size_t // Check all offsets for consistency with certificate data for (uint32_t i = 0; i < num_certs - 1; ++i) { const uint32_t off = esp_crt_get_cert_offset(x509_bundle, i); + if (unlikely((uint64_t)off + CRT_HEADER_SIZE > bundle_size)) { + return false; + } cert_t cert = x509_bundle + off; // The next offset in the list must point to right after the current cert const uint32_t expected_next_offset = off + esp_crt_get_len(cert); @@ -455,7 +458,7 @@ static bool esp_crt_check_bundle(const uint8_t* const x509_bundle, const size_t // The loop above stops at num_certs - 1, so the final certificate's extent is never // validated; check it explicitly so its key data cannot run past the bundle (CWE-125). const uint32_t last_off = esp_crt_get_cert_offset(x509_bundle, num_certs - 1); - if (unlikely(last_off >= bundle_size)) { + if (unlikely((uint64_t)last_off + CRT_HEADER_SIZE > bundle_size)) { return false; } const uint32_t last_len = esp_crt_get_len(x509_bundle + last_off); From 8bbec504498ef50ff08eca667896130a2600950c Mon Sep 17 00:00:00 2001 From: "harshal.patil" Date: Thu, 27 Aug 2026 16:24:08 +0530 Subject: [PATCH 2/2] test(mbedtls): cover certificate extent checks in bundle bound checking Add a bound-checking case for the two cert-extent rejections in esp_crt_check_bundle(): a certificate whose declared name/key lengths run past the end of the bundle, and a last certificate placed so close to the end that its 4-byte header would straddle the bundle boundary. --- .../mbedtls_ut/main/test_esp_crt_bundle.c | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/components/mbedtls/test_apps/mbedtls_ut/main/test_esp_crt_bundle.c b/components/mbedtls/test_apps/mbedtls_ut/main/test_esp_crt_bundle.c index 0b01c220012..215a6afbdeb 100644 --- a/components/mbedtls/test_apps/mbedtls_ut/main/test_esp_crt_bundle.c +++ b/components/mbedtls/test_apps/mbedtls_ut/main/test_esp_crt_bundle.c @@ -621,6 +621,34 @@ TEST_CASE("custom certificate bundle init API - bound checking - Incorrect certi TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, esp_ret); } +TEST_CASE("custom certificate bundle init API - bound checking - Certificate extent beyond end of bundle", "[mbedtls]") +{ + uint8_t test_bundle[1024] = {0}; + const size_t bundle_size = 64; + esp_err_t esp_ret; + + /* Check that the esp_crt_bundle_set API will not accept a bundle whose only + certificate declares name/key lengths that run past the end of the bundle */ + *((uint32_t*) &test_bundle[0]) = sizeof(uint32_t); // 1 cert, starting right after the offset list + *((uint16_t*) &test_bundle[4]) = 100; // Cert 1 name len + *((uint16_t*) &test_bundle[6]) = 100; // Cert 1 key len: 4 + 100 + 100 > bundle_size + + esp_ret = esp_crt_bundle_set(test_bundle, bundle_size); + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, esp_ret); + + /* Check that the esp_crt_bundle_set API will not accept a bundle where the last + certificate starts so close to the end that its 4-byte header (name len + + key len) would straddle the end of the bundle */ + memset(test_bundle, 0, sizeof(test_bundle)); + *((uint32_t*) &test_bundle[0]) = 2 * sizeof(uint32_t); // 2 certs + *((uint32_t*) &test_bundle[4]) = bundle_size - 2; // Cert 2 offset: 2 bytes before the end + *((uint16_t*) &test_bundle[8]) = 25; // Cert 1 name len + *((uint16_t*) &test_bundle[10]) = 25; // Cert 1 key len: cert 2 expected at 8 + 4 + 25 + 25 = bundle_size - 2 + + esp_ret = esp_crt_bundle_set(test_bundle, bundle_size); + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, esp_ret); +} + #if defined(CONFIG_MBEDTLS_HAVE_TIME_DATE) TEST_CASE("certificate bundle - expired cert rejected with time-date check", "[mbedtls]") {