diff --git a/components/wear_levelling/WL_Flash.cpp b/components/wear_levelling/WL_Flash.cpp index 626445c467b..1b5c31111c9 100644 --- a/components/wear_levelling/WL_Flash.cpp +++ b/components/wear_levelling/WL_Flash.cpp @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -127,7 +127,7 @@ esp_err_t WL_Flash::init() WL_RESULT_CHECK(result); int check_size = WL_STATE_CRC_LEN_V2; - // Chech CRC and recover state + // Check CRC and recover state uint32_t crc1 = crc32::crc32_le(WL_CFG_CRC_CONST, (uint8_t *)&this->state, check_size); uint32_t crc2 = crc32::crc32_le(WL_CFG_CRC_CONST, (uint8_t *)state_copy, check_size); @@ -325,7 +325,7 @@ esp_err_t WL_Flash::updateV1_V2() // Check crc for old version and old version ESP_LOGV(TAG, "%s start", __func__); int check_size = WL_STATE_CRC_LEN_V1; - // Chech CRC and recover state + // Check CRC and recover state uint32_t crc1 = crc32::crc32_le(WL_CFG_CRC_CONST, (uint8_t *)&this->state, check_size); wl_state_t sa_copy; wl_state_t *state_copy = &sa_copy; @@ -578,6 +578,10 @@ esp_err_t WL_Flash::write(size_t dest_addr, const void *src, size_t size) if (!this->initialized) { return ESP_ERR_INVALID_STATE; } + if (size == 0) { + // size==0: (size-1) unsigned underflow would OOB the caller buffer. + return ESP_OK; + } ESP_LOGD(TAG, "%s - dest_addr= 0x%08" PRIx32 ", size= 0x%08" PRIx32 , __func__, (uint32_t) dest_addr, (uint32_t) size); uint32_t count = (size - 1) / this->cfg.wl_page_size; for (size_t i = 0; i < count; i++) { @@ -597,6 +601,10 @@ esp_err_t WL_Flash::read(size_t src_addr, void *dest, size_t size) if (!this->initialized) { return ESP_ERR_INVALID_STATE; } + if (size == 0) { + // Same size==0 guard as write(); avoid (size-1) underflow below. + return ESP_OK; + } ESP_LOGD(TAG, "%s - src_addr= 0x%08" PRIx32 ", size= 0x%08" PRIx32 , __func__, (uint32_t) src_addr, (uint32_t) size); uint32_t count = (size - 1) / this->cfg.wl_page_size; for (size_t i = 0; i < count; i++) { diff --git a/components/wear_levelling/host_test/main/test_wl.cpp b/components/wear_levelling/host_test/main/test_wl.cpp index ebf23830a04..23c1c260130 100644 --- a/components/wear_levelling/host_test/main/test_wl.cpp +++ b/components/wear_levelling/host_test/main/test_wl.cpp @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2016-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2016-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -99,6 +99,34 @@ TEST_CASE("write and read back data", "[wear_levelling]") free(read); } +TEST_CASE("write and read with zero size are safe no-ops", "[wear_levelling]") +{ + esp_err_t result; + wl_handle_t wl_handle; + + const esp_partition_t *partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "storage"); + + // Mount wear-levelled partition + result = wl_mount(partition, &wl_handle); + REQUIRE(result == ESP_OK); + + // Zero-length wl_write/read must be no-ops; size==0 used to underflow (size-1) and OOB the buffer. + uint8_t dummy = 0xAA; + result = wl_write(wl_handle, 0, &dummy, 0); + REQUIRE(result == ESP_OK); + + uint8_t read_back = 0x55; + result = wl_read(wl_handle, 0, &read_back, 0); + REQUIRE(result == ESP_OK); + + // Untouched by a genuine zero-length read. + REQUIRE(read_back == 0x55); + + // Unmount + result = wl_unmount(wl_handle); + REQUIRE(result == ESP_OK); +} + TEST_CASE("power down test", "[wear_levelling]") { esp_err_t result; @@ -279,7 +307,7 @@ void calculate_wl_state_address_info(const esp_partition_t *partition, size_t *o void calculate_wl_state_crc(WL_State_s *state_ptr) { int check_size = WL_STATE_CRC_LEN_V2; - // Chech CRC and recover state + // Check CRC and recover state state_ptr->crc32 = crc32::crc32_le(WL_CFG_CRC_CONST, (uint8_t *)state_ptr, check_size); }