diff --git a/components/app_update/esp_ota_ops.c b/components/app_update/esp_ota_ops.c index dcf81bc38e0..4ee7814beaf 100644 --- a/components/app_update/esp_ota_ops.c +++ b/components/app_update/esp_ota_ops.c @@ -258,6 +258,21 @@ esp_err_t esp_ota_resume(const esp_partition_t *partition, const size_t erase_si return ESP_ERR_OTA_PARTITION_CONFLICT; } +#ifdef CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE + // Mirror esp_ota_begin(): refuse to resume an OTA into an app slot while the running + // app is still pending verification, otherwise the rollback target could be + // overwritten during the unconfirmed window. + if (partition->type == ESP_PARTITION_TYPE_APP) { + esp_ota_img_states_t ota_state_running_part; + if (esp_ota_get_state_partition(running_partition, &ota_state_running_part) == ESP_OK) { + if (ota_state_running_part == ESP_OTA_IMG_PENDING_VERIFY) { + ESP_LOGE(TAG, "Running app has not confirmed state (ESP_OTA_IMG_PENDING_VERIFY)"); + return ESP_ERR_OTA_ROLLBACK_INVALID_STATE; + } + } + } +#endif + // Check if there's already an ongoing OTA operation on this partition if (esp_ota_check_partition_conflict(partition)) { ESP_LOGE(TAG, "OTA operation already in progress on partition %s", partition->label); @@ -361,7 +376,10 @@ esp_err_t esp_ota_write(esp_ota_handle_t handle, const void *data, size_t size) } } else if (it->partition.final->type == ESP_PARTITION_TYPE_PARTITION_TABLE) { - if (*(uint16_t*)data_bytes != (uint16_t)ESP_PARTITION_MAGIC) { + /* Read the 2-byte magic word only if the caller-supplied buffer is large + * enough; otherwise this would read past a short (e.g. 1-byte) first chunk. + * A too-short chunk is still fully validated later by esp_partition_table_verify(). */ + if (size >= sizeof(uint16_t) && *(uint16_t*)data_bytes != (uint16_t)ESP_PARTITION_MAGIC) { ESP_LOGE(TAG, "Partition table image has invalid magic word (expected 0x50AA, saw 0x%04x)", *(uint16_t*)data_bytes); return ESP_ERR_OTA_VALIDATE_FAILED; } @@ -518,6 +536,15 @@ static esp_err_t ota_verify_data_partition_signature(const esp_partition_t *part esp_err_t err = ESP_FAIL; uint8_t digest[ESP_SECURE_BOOT_DIGEST_LEN] = {0}; + /* The written image must hold at least one sector of data plus the trailing + * signature sector. Without this check, a total_written_size below one sector makes + * the subtraction below underflow uint32_t (CWE-191), driving the hash/read with a + * wild length and offset. Reject undersized (caller-controlled) input up front. */ + if (total_written_size < (2 * SPI_FLASH_SEC_SIZE)) { + ESP_LOGE(TAG, "Written size %lu too small for a signed data partition", (unsigned long)total_written_size); + return ESP_ERR_INVALID_SIZE; + } + /* Calculate data length by excluding the signature sector from total written size */ uint32_t data_length = ((total_written_size) & ~((SPI_FLASH_SEC_SIZE) - 1)) - SPI_FLASH_SEC_SIZE; diff --git a/components/bootloader_support/src/secure_boot_v2/secure_boot_signatures_app.c b/components/bootloader_support/src/secure_boot_v2/secure_boot_signatures_app.c index c5f96006180..5f9a5b17fa3 100644 --- a/components/bootloader_support/src/secure_boot_v2/secure_boot_signatures_app.c +++ b/components/bootloader_support/src/secure_boot_v2/secure_boot_signatures_app.c @@ -314,7 +314,13 @@ esp_err_t esp_secure_boot_verify_with_efuse_digest_index(int efuse_digest_index, // Read key digests from efuse esp_secure_boot_key_digests_t efuse_key_digests; memset(&efuse_key_digests, 0, sizeof(esp_secure_boot_key_digests_t)); - esp_secure_boot_read_key_digests(&efuse_key_digests); + /* A non-revoked slot can still be unprovisioned, leaving its digest pointer NULL even + * when the read returns ESP_OK; comparing it would dereference NULL in memcmp(). Check + * both the read result and the specific slot (matches get_secure_boot_key_digests()). */ + if (esp_secure_boot_read_key_digests(&efuse_key_digests) != ESP_OK || + efuse_key_digests.key_digests[efuse_digest_index] == NULL) { + return ESP_FAIL; + } for (int i = 0; i < img_key_digests.num_digests; i++) { if (!memcmp(img_key_digests.key_digests[i], efuse_key_digests.key_digests[efuse_digest_index], ESP_SECURE_BOOT_KEY_DIGEST_LEN)) { diff --git a/components/esp-tls/esp_tls.c b/components/esp-tls/esp_tls.c index 1ca30e44dd2..dc453f69204 100644 --- a/components/esp-tls/esp_tls.c +++ b/components/esp-tls/esp_tls.c @@ -572,7 +572,7 @@ static int esp_tls_low_level_conn(const char *hostname, int hostlen, int port, c */ esp_err_t esp_tls_plain_tcp_connect(const char *host, int hostlen, int port, const esp_tls_cfg_t *cfg, esp_tls_error_handle_t error_handle, int *sockfd) { - if (sockfd == NULL || error_handle == NULL) { + if (sockfd == NULL || error_handle == NULL || host == NULL || hostlen < 0) { return ESP_ERR_INVALID_ARG; } return tcp_connect(host, hostlen, port, cfg, error_handle, sockfd); @@ -654,6 +654,10 @@ int esp_tls_conn_http_new_sync(const char *url, const esp_tls_cfg_t *cfg, esp_tl */ int esp_tls_conn_http_new_async(const char *url, const esp_tls_cfg_t *cfg, esp_tls_t *tls) { + if (!url || !cfg || !tls) { + return -1; + } + /* Parse URI */ struct http_parser_url u; http_parser_url_init(&u); diff --git a/components/esp_hal_security/aes_hal.c b/components/esp_hal_security/aes_hal.c index 75b3534c1de..c42abcf6015 100644 --- a/components/esp_hal_security/aes_hal.c +++ b/components/esp_hal_security/aes_hal.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2020-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -125,6 +125,11 @@ void aes_hal_gcm_read_tag(uint8_t *tag, size_t tag_len) { uint8_t tag_res[TAG_BYTES]; aes_ll_gcm_read_tag(tag_res); + /* The GCM tag is at most TAG_BYTES (16). Clamp the caller-supplied length so an oversized + * tag_len cannot over-read tag_res or over-write the caller's tag buffer (CWE-125). */ + if (tag_len > TAG_BYTES) { + tag_len = TAG_BYTES; + } memcpy(tag, tag_res, tag_len); } diff --git a/components/esp_http_client/esp_http_client.c b/components/esp_http_client/esp_http_client.c index b9d7e975bed..9299a27167a 100644 --- a/components/esp_http_client/esp_http_client.c +++ b/components/esp_http_client/esp_http_client.c @@ -474,10 +474,17 @@ esp_err_t esp_http_client_set_username(esp_http_client_handle_t client, const ch ESP_LOGE(TAG, "client must not be NULL"); return ESP_ERR_INVALID_ARG; } + /* Duplicate first so that passing the current username back in (e.g. the pointer + * returned by esp_http_client_get_username()) is safe: the old buffer is freed only + * after the copy succeeds, avoiding a use-after-free on self-aliasing (CWE-416). */ + char *new_username = username ? strdup(username) : NULL; + if (username != NULL && new_username == NULL) { + return ESP_ERR_NO_MEM; + } if (client->connection_info.username != NULL) { free(client->connection_info.username); } - client->connection_info.username = username ? strdup(username) : NULL; + client->connection_info.username = new_username; return ESP_OK; } @@ -520,11 +527,19 @@ esp_err_t esp_http_client_set_password(esp_http_client_handle_t client, const ch ESP_LOGE(TAG, "client must not be NULL"); return ESP_ERR_INVALID_ARG; } + /* Duplicate first so that passing the current password back in (e.g. the pointer + * returned by esp_http_client_get_password()) is safe: zeroize and free the old buffer + * only after the copy succeeds, avoiding a use-after-free / credential corruption + * (CWE-416) caused by memset zeroing the source before strdup reads it. */ + char *new_password = password ? strdup(password) : NULL; + if (password != NULL && new_password == NULL) { + return ESP_ERR_NO_MEM; + } if (client->connection_info.password != NULL) { memset(client->connection_info.password, 0, strlen(client->connection_info.password)); free(client->connection_info.password); } - client->connection_info.password = password ? strdup(password) : NULL; + client->connection_info.password = new_password; return ESP_OK; } @@ -1720,14 +1735,17 @@ static esp_err_t esp_http_client_connect(esp_http_client_handle_t client) } } client->state = HTTP_STATE_CONNECTED; - http_dispatch_event(client, HTTP_EVENT_ON_CONNECTED, NULL, 0); - http_dispatch_event_to_event_loop(HTTP_EVENT_ON_CONNECTED, &client, sizeof(esp_http_client_handle_t)); #ifdef CONFIG_ESP_TLS_CLIENT_SESSION_TICKETS + /* Perform handle-dependent session-ticket bookkeeping before dispatching the user + * callback: a synchronous HTTP_EVENT_ON_CONNECTED handler is permitted to destroy + * the client, so dereferencing the handle afterwards would be a UAF (CWE-416). */ if (client->session_ticket_state != SESSION_TICKET_UNUSED) { esp_transport_ssl_session_ticket_operation(client->transport, ESP_TRANSPORT_SESSION_TICKET_SAVE); client->session_ticket_state = SESSION_TICKET_SAVED; } #endif + http_dispatch_event(client, HTTP_EVENT_ON_CONNECTED, NULL, 0); + http_dispatch_event_to_event_loop(HTTP_EVENT_ON_CONNECTED, &client, sizeof(esp_http_client_handle_t)); } return ESP_OK; diff --git a/components/esp_http_client/lib/http_auth.c b/components/esp_http_client/lib/http_auth.c index 9e5477cd1f1..23aee240c11 100644 --- a/components/esp_http_client/lib/http_auth.c +++ b/components/esp_http_client/lib/http_auth.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -201,11 +201,18 @@ char *http_auth_digest(const char *username, const char *password, esp_http_auth if (rc < 0) { ESP_LOGE(TAG, "asprintf() returned: %d", rc); ret = ESP_FAIL; + free(auth_str); + auth_str = NULL; goto _digest_exit; } + /* http_utils_append_string() realloc()s auth_str; on failure it returns NULL + * without freeing the original buffer, so keep a handle to free it here. */ + char *prev_auth_str = auth_str; auth_str = http_utils_append_string(&auth_str, temp_auth_str, strlen(temp_auth_str)); if (!auth_str) { ret = ESP_FAIL; + free(prev_auth_str); + free(temp_auth_str); goto _digest_exit; } free(temp_auth_str); diff --git a/components/esp_http_server/src/httpd_parse.c b/components/esp_http_server/src/httpd_parse.c index 0ae44779f24..0f806707230 100644 --- a/components/esp_http_server/src/httpd_parse.c +++ b/components/esp_http_server/src/httpd_parse.c @@ -929,7 +929,9 @@ bool httpd_validate_req_ptr(httpd_req_t *r) /* Helper function to get a URL query tag from a query string of the type param1=val1¶m2=val2 */ esp_err_t httpd_query_key_value(const char *qry_str, const char *key, char *val, size_t val_size) { - if (qry_str == NULL || key == NULL || val == NULL) { + /* Reject a zero-size output buffer: val_size - 1 below would underflow to SIZE_MAX, + * defeating the truncation check and overflowing the caller's buffer (CWE-191/CWE-787). */ + if (qry_str == NULL || key == NULL || val == NULL || val_size == 0) { return ESP_ERR_INVALID_ARG; } @@ -1012,7 +1014,9 @@ size_t httpd_req_get_url_query_len(httpd_req_t *r) esp_err_t httpd_req_get_url_query_str(httpd_req_t *r, char *buf, size_t buf_len) { - if (r == NULL || buf == NULL) { + /* Reject a zero-size output buffer: buf_len - 1 below would underflow to SIZE_MAX, + * defeating the truncation check and overflowing the caller's buffer (CWE-191/CWE-787). */ + if (r == NULL || buf == NULL || buf_len == 0) { return ESP_ERR_INVALID_ARG; } @@ -1205,7 +1209,10 @@ esp_err_t httpd_req_get_hdr_value_str_ptr(httpd_req_t *r, const char *field, con /* Helper function to get a cookie value from a cookie string of the type "cookie1=val1; cookie2=val2" */ esp_err_t static httpd_cookie_key_value(const char *cookie_str, const char *key, char *val, size_t *val_size) { - if (cookie_str == NULL || key == NULL || val == NULL) { + /* Reject a NULL or zero-size output buffer: *val_size - 1 below would underflow to + * SIZE_MAX, defeating the truncation check and overflowing the caller's buffer + * (CWE-191/CWE-787). val_size is also dereferenced below, so it must be non-NULL. */ + if (cookie_str == NULL || key == NULL || val == NULL || val_size == NULL || *val_size == 0) { return ESP_ERR_INVALID_ARG; } @@ -1329,6 +1336,10 @@ esp_err_t httpd_get_raw_req_data(httpd_req_t *req, char *buf, size_t buf_len) return ESP_ERR_INVALID_ARG; } struct httpd_req_aux *ra = req->aux; - memcpy(buf, ra->scratch, buf_len); + /* The caller controls buf_len; a value larger than the valid scratch data would read + * past the scratch allocation (CWE-125). Clamp to scratch_cur_size. Callers should query + * the available length with httpd_get_raw_req_data_len() before calling this. */ + size_t copy_len = MIN(buf_len, ra->scratch_cur_size); + memcpy(buf, ra->scratch, copy_len); return ESP_OK; } diff --git a/components/esp_http_server/src/httpd_uri.c b/components/esp_http_server/src/httpd_uri.c index 006538e82c9..75b45561436 100644 --- a/components/esp_http_server/src/httpd_uri.c +++ b/components/esp_http_server/src/httpd_uri.c @@ -159,6 +159,7 @@ esp_err_t httpd_register_uri_handler(httpd_handle_t handle, if (hd->hd_calls[i]->uri == NULL) { /* Failed to allocate memory */ free(hd->hd_calls[i]); + hd->hd_calls[i] = NULL; return ESP_ERR_HTTPD_ALLOC_MEM; } @@ -181,6 +182,7 @@ esp_err_t httpd_register_uri_handler(httpd_handle_t handle, /* Failed to allocate memory */ free((void *)hd->hd_calls[i]->uri); free(hd->hd_calls[i]); + hd->hd_calls[i] = NULL; return ESP_ERR_HTTPD_ALLOC_MEM; } } else { diff --git a/components/esp_https_server/src/https_server.c b/components/esp_https_server/src/https_server.c index 2184228087a..7424f5effdb 100644 --- a/components/esp_https_server/src/https_server.c +++ b/components/esp_https_server/src/https_server.c @@ -198,6 +198,10 @@ static esp_err_t httpd_ssl_open(httpd_handle_t server, int sockfd) esp_https_server_last_error_t last_error = {0}; last_error.last_error = ESP_ERR_NO_MEM; http_dispatch_event_to_event_loop(HTTPS_SERVER_EVENT_ERROR, &last_error, sizeof(last_error)); + /* The TLS session (and its underlying socket fd) is already established; free it + * before returning so a failed connection under memory pressure does not leak the + * SSL context and the socket (CWE-401 / CWE-772). */ + esp_tls_server_session_delete(tls); return ESP_ERR_NO_MEM; } transport_ctx->tls = tls; diff --git a/components/esp_tee/subproject/components/attestation/esp_att_utils_crypto.c b/components/esp_tee/subproject/components/attestation/esp_att_utils_crypto.c index 51e7fbbdb5f..6ac7557f2ab 100644 --- a/components/esp_tee/subproject/components/attestation/esp_att_utils_crypto.c +++ b/components/esp_tee/subproject/components/attestation/esp_att_utils_crypto.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -246,6 +246,11 @@ esp_err_t esp_att_utils_ecdsa_get_sign(const esp_att_ecdsa_keypair_t *keypair, c return ESP_ERR_INVALID_SIZE; } + /* Initialise the out-params up front so the error path at 'exit' can free them safely even + * when we bail out (e.g. signature generation fails) before they are allocated. */ + *sign_r_hexstr = NULL; + *sign_s_hexstr = NULL; + esp_err_t err = ESP_FAIL; unsigned char sign_r[SECP256R1_ECDSA_KEY_LEN] = {0}, sign_s[SECP256R1_ECDSA_KEY_LEN] = {0}; @@ -284,5 +289,12 @@ esp_err_t esp_att_utils_ecdsa_get_sign(const esp_att_ecdsa_keypair_t *keypair, c err = ESP_OK; exit: + if (err != ESP_OK) { + /* free(NULL) is a no-op, so this is safe whether or not the buffers were allocated. */ + free(*sign_r_hexstr); + *sign_r_hexstr = NULL; + free(*sign_s_hexstr); + *sign_s_hexstr = NULL; + } return err; } diff --git a/components/esp_tee/subproject/main/common/multi_heap.c b/components/esp_tee/subproject/main/common/multi_heap.c index 0eccfdd5c36..e735ec73037 100644 --- a/components/esp_tee/subproject/main/common/multi_heap.c +++ b/components/esp_tee/subproject/main/common/multi_heap.c @@ -107,7 +107,10 @@ void *esp_tee_heap_malloc(size_t size) void *esp_tee_heap_calloc(size_t n, size_t size) { - size_t reg_size = n * size; + size_t reg_size; + if (__builtin_mul_overflow(n, size, ®_size)) { + return NULL; + } void *ptr = esp_tee_heap_malloc(reg_size); if (ptr != NULL) { memset(ptr, 0x00, reg_size); @@ -239,7 +242,10 @@ void *heap_caps_aligned_alloc(size_t alignment, size_t size, uint32_t caps) void *heap_caps_aligned_calloc(size_t alignment, size_t n, size_t size, uint32_t caps) { (void) caps; - uint32_t reg_size = n * size; + size_t reg_size; + if (__builtin_mul_overflow(n, size, ®_size)) { + return NULL; + } void *ptr = esp_tee_heap_aligned_alloc(reg_size, alignment); if (ptr != NULL) { diff --git a/components/mbedtls/esp_crt_bundle/esp_crt_bundle.c b/components/mbedtls/esp_crt_bundle/esp_crt_bundle.c index 0b271b73dc0..49db59d6e9e 100644 --- a/components/mbedtls/esp_crt_bundle/esp_crt_bundle.c +++ b/components/mbedtls/esp_crt_bundle/esp_crt_bundle.c @@ -101,9 +101,11 @@ static const uint8_t* esp_crt_get_key(const cert_t cert) return esp_crt_get_name(cert) + esp_crt_get_name_len(cert); } -static uint16_t esp_crt_get_len(const cert_t cert) +static uint32_t esp_crt_get_len(const cert_t cert) { - return CRT_HEADER_SIZE + esp_crt_get_name_len(cert) + esp_crt_get_key_len(cert); + /* Widened to uint32_t: name_len and key_len are each uint16_t, so their sum plus the + * header can exceed UINT16_MAX and would otherwise wrap, under-reporting the cert size. */ + return (uint32_t)CRT_HEADER_SIZE + (uint32_t)esp_crt_get_name_len(cert) + (uint32_t)esp_crt_get_key_len(cert); } static uint32_t esp_crt_get_cert_offset(const bundle_t bundle, const uint32_t index) @@ -428,6 +430,11 @@ static bool esp_crt_check_bundle(const uint8_t* const x509_bundle, const size_t return false; } + if (unlikely(num_certs == 0)) { + // No certificates: the loops below compute num_certs - 1, which would underflow. + return false; + } + // Check all offsets for consistency with certificate data for (uint32_t i = 0; i < num_certs - 1; ++i) { const uint32_t off = offsets[i]; @@ -440,6 +447,17 @@ 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 = offsets[num_certs - 1]; + if (unlikely(last_off >= bundle_size)) { + return false; + } + const uint32_t last_len = esp_crt_get_len(x509_bundle + last_off); + if (unlikely((uint64_t)last_off + last_len > bundle_size)) { + return false; + } + // All checks passed. return true; } diff --git a/components/mbedtls/port/aes/esp_aes.c b/components/mbedtls/port/aes/esp_aes.c index 39a803d08b4..826882c06b2 100644 --- a/components/mbedtls/port/aes/esp_aes.c +++ b/components/mbedtls/port/aes/esp_aes.c @@ -6,7 +6,7 @@ * * SPDX-License-Identifier: Apache-2.0 * - * SPDX-FileContributor: 2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileContributor: 2025-2026 Espressif Systems (Shanghai) CO LTD */ /* * The AES block cipher was designed by Vincent Rijmen and Joan Daemen. @@ -459,6 +459,15 @@ int esp_aes_crypt_cfb128(esp_aes_context *ctx, n = *iv_off; + /* iv[] is a fixed 16-byte buffer and n indexes it directly (before the modulo-16 update), + * so a caller-supplied *iv_off > 15 -- attacker-controlled via the TEE secure service -- + * is an out-of-bounds read/write of iv[] in TEE context (CWE-787 / CWE-125). Bound it here, + * matching the guard already present in esp_aes_crypt_ofb(). */ + if (n >= AES_BLOCK_BYTES) { + ESP_LOGE(TAG, "IV offset out of bounds"); + return MBEDTLS_ERR_AES_BAD_INPUT_DATA; + } + #if SOC_AES_SUPPORT_DMA #if CONFIG_MBEDTLS_AES_HW_SMALL_DATA_LEN_OPTIM if (length > AES_DMA_MODE_THRESHOLD) { @@ -583,6 +592,10 @@ int esp_aes_crypt_ofb(esp_aes_context *ctx, } n = *iv_off; + if (n >= AES_BLOCK_BYTES) { + ESP_LOGE(TAG, "IV offset out of bounds"); + return MBEDTLS_ERR_AES_BAD_INPUT_DATA; + } #if SOC_AES_SUPPORT_DMA #if CONFIG_MBEDTLS_AES_HW_SMALL_DATA_LEN_OPTIM @@ -687,6 +700,10 @@ int esp_aes_crypt_ctr(esp_aes_context *ctx, } n = *nc_off; + if (n >= AES_BLOCK_BYTES) { + ESP_LOGE(TAG, "IV offset out of bounds"); + return MBEDTLS_ERR_AES_BAD_INPUT_DATA; + } #if SOC_AES_SUPPORT_DMA #if CONFIG_MBEDTLS_AES_HW_SMALL_DATA_LEN_OPTIM diff --git a/components/mbedtls/port/aes/esp_aes_gcm.c b/components/mbedtls/port/aes/esp_aes_gcm.c index e915259ea8f..845785ab914 100644 --- a/components/mbedtls/port/aes/esp_aes_gcm.c +++ b/components/mbedtls/port/aes/esp_aes_gcm.c @@ -471,6 +471,14 @@ int esp_aes_gcm_update( esp_gcm_context *ctx, return PSA_ERROR_INVALID_ARGUMENT; } + /* Honor the documented contract: the output buffer must hold input_length bytes, which are + * written unconditionally below; without this check an undersized buffer overflows (CWE-20 + * -> CWE-787). MBEDTLS_ERR_GCM_BAD_INPUT is #defined to PSA_ERROR_INVALID_ARGUMENT. */ + if ( output_size < input_length ) { + ESP_LOGE(TAG, "Output buffer too small"); + return PSA_ERROR_INVALID_ARGUMENT; + } + if ( output > input && (size_t) ( output - input ) < input_length ) { return ( PSA_ERROR_INVALID_ARGUMENT ); } @@ -525,6 +533,10 @@ int esp_aes_gcm_finish( esp_gcm_context *ctx, uint8_t len_block[AES_BLOCK_BYTES] = {0}; uint8_t stream[AES_BLOCK_BYTES] = {0}; + (void)output; + (void)output_size; + *output_length = 0; + if ( tag_len > 16 || tag_len < 4 ) { return ( PSA_ERROR_INVALID_ARGUMENT ); } @@ -595,7 +607,7 @@ static int esp_aes_gcm_crypt_and_tag_partial_hw( esp_gcm_context *ctx, return ( ret ); } - if ( ( ret = esp_aes_gcm_update( ctx, input, length, output, 0, &olen ) ) != 0 ) { + if ( ( ret = esp_aes_gcm_update( ctx, input, length, output, length, &olen ) ) != 0 ) { return ( ret ); } @@ -622,6 +634,12 @@ int esp_aes_gcm_crypt_and_tag( esp_gcm_context *ctx, ESP_LOGE(TAG, "No AES context supplied"); return PSA_ERROR_INVALID_ARGUMENT; } + /* GCM tags are 4..16 bytes. Validate here so the hardware path also rejects an invalid + * tag_len (the software path enforces this in esp_aes_gcm_finish()); otherwise the HAL tag + * read would be driven with an out-of-range length (CWE-125 / CWE-787). */ + if ( tag_len < 4 || tag_len > 16 ) { + return PSA_ERROR_INVALID_ARGUMENT; + } #if CONFIG_MBEDTLS_HARDWARE_GCM int ret; size_t remainder_bit; @@ -717,6 +735,13 @@ int esp_aes_gcm_auth_decrypt( esp_gcm_context *ctx, size_t i; int diff; + /* Validate tag_len before use: a zero tag_len makes the constant-time comparison loop + * below run zero iterations, so diff stays 0 and any forged ciphertext is accepted as + * authentic (CWE-347). Enforce the same 4..16 range as esp_aes_gcm_finish(). */ + if ( tag_len > 16 || tag_len < 4 ) { + return PSA_ERROR_INVALID_ARGUMENT; + } + if ( ( ret = esp_aes_gcm_crypt_and_tag( ctx, ESP_AES_DECRYPT, length, iv, iv_len, aad, aad_len, input, output, tag_len, check_tag ) ) != 0 ) { diff --git a/components/mbedtls/port/ecc/esp_ecc.c b/components/mbedtls/port/ecc/esp_ecc.c index 43060022fe5..59a558fd2e7 100644 --- a/components/mbedtls/port/ecc/esp_ecc.c +++ b/components/mbedtls/port/ecc/esp_ecc.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -33,6 +33,17 @@ int esp_ecc_point_multiply(const ecc_point_t *point, const uint8_t *scalar, ecc_ uint16_t len = point->len; ecc_mode_t work_mode = verify_first ? ECC_MODE_VERIFY_THEN_POINT_MUL : ECC_MODE_POINT_MUL; + /* len is used as the HW read/write byte count for the fixed-size ecc_point_t buffers; + * reject any value that is not a supported curve length before touching hardware. On the + * TEE secure-service path this field is attacker-controlled (CWE-20 -> OOB read/write). */ + if (len != P192_LEN && len != P256_LEN +#if SOC_ECC_SUPPORT_CURVE_P384 + && len != P384_LEN +#endif + ) { + return -1; + } + esp_ecc_acquire_hardware(); ecc_hal_write_mul_param(scalar, point->x, point->y, len); @@ -65,6 +76,18 @@ int esp_ecc_point_verify(const ecc_point_t *point) { int result; + /* point->len drives a fixed-stride MMIO write loop in the HAL; an unvalidated oversized + * value (attacker-controlled via the TEE secure service) walks past the ECC register block + * and can reach other peripheral registers (CWE-787). Reject non-curve lengths up front and + * return 0 (point not verified) -- the fail-safe value for this routine. */ + if (point->len != P192_LEN && point->len != P256_LEN +#if SOC_ECC_SUPPORT_CURVE_P384 + && point->len != P384_LEN +#endif + ) { + return 0; + } + esp_ecc_acquire_hardware(); ecc_hal_write_verify_param(point->x, point->y, point->len); ecc_hal_set_mode(ECC_MODE_VERIFY);