Merge branch 'bugfix/memory-safety-and-validation_v6.0' into 'release/v6.0'

fix(security): findings from project Vanessa (v6.0)

See merge request espressif/esp-idf!50391
This commit is contained in:
Jiang Jiang Jian
2026-07-22 10:28:14 +08:00
15 changed files with 207 additions and 22 deletions

View File

@@ -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
*/
@@ -240,6 +240,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
new_entry = esp_ota_init_entry(partition);
if (new_entry == NULL) {
return ESP_ERR_NO_MEM;
@@ -337,7 +352,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;
}
@@ -494,6 +512,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;

View File

@@ -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)) {

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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);

View File

@@ -895,7 +895,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&param2=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;
}
@@ -978,7 +980,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;
}
@@ -1171,7 +1175,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;
}
@@ -1295,6 +1302,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;
}

View File

@@ -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 {

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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, &reg_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, &reg_size)) {
return NULL;
}
void *ptr = esp_tee_heap_aligned_alloc(reg_size, alignment);
if (ptr != NULL) {

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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 ) {

View File

@@ -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);