mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
fix(storage/nvs): Fixed hadling of inconsistent values in NVS entry header
feat(storage/nvs): Added test cases for damaged entries with correct CRC
This commit is contained in:
parent
d2cfb78d31
commit
b937cb7549
@ -588,7 +588,6 @@ TEST_CASE("nvs api tests", "[nvs]")
|
||||
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3;
|
||||
TEMPORARILY_DISABLED(f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN);)
|
||||
|
||||
|
||||
TEST_ESP_ERR(nvs_open("namespace1", NVS_READWRITE, &handle_1), ESP_ERR_NVS_NOT_INITIALIZED);
|
||||
for (uint16_t i = NVS_FLASH_SECTOR; i < NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN; ++i) {
|
||||
f.erase(i);
|
||||
@ -758,8 +757,7 @@ TEST_CASE("nvs iterators tests", "[nvs]")
|
||||
int count = 0;
|
||||
nvs_iterator_t it = nullptr;
|
||||
esp_err_t res = nvs_entry_find(part, name, type, &it);
|
||||
for (count = 0; res == ESP_OK; count++)
|
||||
{
|
||||
for (count = 0; res == ESP_OK; count++) {
|
||||
res = nvs_entry_next(&it);
|
||||
}
|
||||
CHECK(res == ESP_ERR_NVS_NOT_FOUND); // after finishing the loop or if no entry was found to begin with,
|
||||
@ -773,8 +771,7 @@ TEST_CASE("nvs iterators tests", "[nvs]")
|
||||
int count = 0;
|
||||
nvs_iterator_t it = nullptr;
|
||||
esp_err_t res = nvs_entry_find_in_handle(handle, type, &it);
|
||||
for (count = 0; res == ESP_OK; count++)
|
||||
{
|
||||
for (count = 0; res == ESP_OK; count++) {
|
||||
res = nvs_entry_next(&it);
|
||||
}
|
||||
CHECK(res == ESP_ERR_NVS_NOT_FOUND); // after finishing the loop or if no entry was found to begin with,
|
||||
@ -828,7 +825,6 @@ TEST_CASE("nvs iterators tests", "[nvs]")
|
||||
CHECK(entry_count(NVS_DEFAULT_PART_NAME, NULL, NVS_TYPE_U64) == 1);
|
||||
}
|
||||
|
||||
|
||||
SECTION("Number of entries found for specified handle and type is correct") {
|
||||
CHECK(entry_count_handle(handle_1, NVS_TYPE_ANY) == 11);
|
||||
CHECK(entry_count_handle(handle_1, NVS_TYPE_I32) == 3);
|
||||
@ -892,7 +888,6 @@ TEST_CASE("nvs iterators tests", "[nvs]")
|
||||
nvs_release_iterator(it);
|
||||
}
|
||||
|
||||
|
||||
SECTION("Iterating over multiple pages works correctly") {
|
||||
nvs_handle_t handle_3;
|
||||
const char *name_3 = "namespace3";
|
||||
@ -1242,8 +1237,7 @@ public:
|
||||
static_assert(nKeys == sizeof(future_values) / sizeof(future_values[0]), "");
|
||||
|
||||
auto randomRead = [&](size_t index) -> esp_err_t {
|
||||
switch (types[index])
|
||||
{
|
||||
switch (types[index]) {
|
||||
case nvs::ItemType::I32: {
|
||||
int32_t val;
|
||||
auto err = nvs_get_i32(handle, keys[index], &val);
|
||||
@ -1290,8 +1284,7 @@ public:
|
||||
};
|
||||
|
||||
auto randomWrite = [&](size_t index) -> esp_err_t {
|
||||
switch (types[index])
|
||||
{
|
||||
switch (types[index]) {
|
||||
case nvs::ItemType::I32: {
|
||||
int32_t val = static_cast<int32_t>(gen());
|
||||
|
||||
@ -1910,7 +1903,6 @@ TEST_CASE("Check that orphaned blobs are erased during init", "[nvs]")
|
||||
|
||||
TEST_ESP_OK(storage.writeItem(1, nvs::ItemType::BLOB, "key", blob, sizeof(blob)));
|
||||
|
||||
|
||||
TEST_ESP_OK(storage.init(0, 5));
|
||||
/* Check that multi-page item is still available.**/
|
||||
TEST_ESP_OK(storage.readItem(1, nvs::ItemType::BLOB, "key", blob, sizeof(blob)));
|
||||
@ -2162,7 +2154,6 @@ TEST_CASE("Recovery from power-off during modification of blob present in old-fo
|
||||
size_t buflen = sizeof(hexdata);
|
||||
uint8_t buf[nvs::Page::CHUNK_MAX_SIZE];
|
||||
|
||||
|
||||
/* Power-off when blob was being written on the different page where its old version in old format
|
||||
* was present*/
|
||||
nvs::Page p;
|
||||
@ -2639,6 +2630,499 @@ TEST_CASE("crc error in variable length item is handled", "[nvs]")
|
||||
}
|
||||
}
|
||||
|
||||
// handle damaged item header's span=0 even if crc is correct
|
||||
TEST_CASE("zero span in item header with correct crc is handled", "[nvs]")
|
||||
{
|
||||
PartitionEmulationFixture f(0, 3);
|
||||
nvs::Storage storage(f.part());
|
||||
// prepare some data
|
||||
TEST_ESP_OK(storage.init(0, 3));
|
||||
TEST_ESP_OK(storage.writeItem(0, "ns1", static_cast<uint8_t>(1)));
|
||||
TEST_ESP_OK(storage.writeItem(1, "value1", static_cast<uint32_t>(1)));
|
||||
TEST_ESP_OK(storage.writeItem(1, "value2", static_cast<uint32_t>(2)));
|
||||
|
||||
// damage item header of value1 to introduce span==0 error, recalculate crc
|
||||
{
|
||||
uint8_t new_span = 0;
|
||||
size_t entry_offset = 32 * 3; // 2x page header + 1x ns1
|
||||
uint8_t buff[sizeof(nvs::Item)] = {0};
|
||||
TEST_ESP_OK(esp_partition_read(f.get_esp_partition(), entry_offset, &buff, sizeof(buff)));
|
||||
|
||||
nvs::Item* item = (nvs::Item*)buff;
|
||||
|
||||
// set span to 0
|
||||
item->span = new_span;
|
||||
// recalculate crc same way as nvs::Item::calculateCrc32 does
|
||||
item->crc32 = ((nvs::Item*)buff)->calculateCrc32();
|
||||
|
||||
// allow overwriting smaller portion of flash than whole 4k page
|
||||
((esp_partition_t*) f.get_esp_partition())->erase_size = 1;
|
||||
TEST_ESP_OK(esp_partition_erase_range(f.get_esp_partition(), entry_offset, sizeof(buff)));
|
||||
TEST_ESP_OK(esp_partition_write(f.get_esp_partition(), entry_offset, &buff, sizeof(buff)));
|
||||
}
|
||||
|
||||
// check that storage can recover
|
||||
uint32_t val = 0;
|
||||
|
||||
TEST_ESP_OK(storage.init(0, 3));
|
||||
TEST_ESP_OK(storage.readItem(1, "value2", val));
|
||||
CHECK(val == 2);
|
||||
// check that the damaged item is no longer present
|
||||
TEST_ESP_ERR(ESP_ERR_NVS_NOT_FOUND, storage.readItem(1, "value1", val));
|
||||
|
||||
// add more items named "item_0" till "item_125" to make the page full
|
||||
for (size_t i = 0; i < nvs::Page::ENTRY_COUNT; ++i) {
|
||||
char item_name[nvs::Item::MAX_KEY_LENGTH + 1];
|
||||
snprintf(item_name, sizeof(item_name), "item_%ld", (long int)i);
|
||||
TEST_ESP_OK(storage.writeItem(1, item_name, static_cast<uint32_t>(i)));
|
||||
}
|
||||
|
||||
// damage item header of item_125 to introduce span==0 error, recalculate crc
|
||||
{
|
||||
uint8_t new_span = 0;
|
||||
size_t entry_offset = 32 * (2 + 1 + 1 + 128) ; // 2x page header + 1x ns1 + 1x value1 + whole page
|
||||
uint8_t buff[sizeof(nvs::Item)] = {0};
|
||||
TEST_ESP_OK(esp_partition_read(f.get_esp_partition(), entry_offset, &buff, sizeof(buff)));
|
||||
|
||||
nvs::Item* item = (nvs::Item*)buff;
|
||||
// set span to 0
|
||||
item->span = new_span;
|
||||
// recalculate crc same way as nvs::Item::calculateCrc32 does
|
||||
item->crc32 = ((nvs::Item*)buff)->calculateCrc32();
|
||||
|
||||
// allow overwriting smaller portion of flash than whole 4k page
|
||||
((esp_partition_t*) f.get_esp_partition())->erase_size = 1;
|
||||
TEST_ESP_OK(esp_partition_erase_range(f.get_esp_partition(), entry_offset, sizeof(buff)));
|
||||
TEST_ESP_OK(esp_partition_write(f.get_esp_partition(), entry_offset, &buff, sizeof(buff)));
|
||||
}
|
||||
|
||||
// check that storage can recover
|
||||
TEST_ESP_OK(storage.init(0, 3));
|
||||
// check that the damaged item is no longer present
|
||||
TEST_ESP_ERR(ESP_ERR_NVS_NOT_FOUND, storage.readItem(1, "item_125", val));
|
||||
}
|
||||
|
||||
// test case for damaged item header with correct crc using string.
|
||||
// first sub-case, span goes over the remaining entries in the page
|
||||
// second sub-case, span goes over the number of entries required to store the string
|
||||
// third sub-case, span goes below the number of entries required to store the string
|
||||
// fourth sub-case, indicated variable data length goes over the remaining space in the page
|
||||
TEST_CASE("inconsistent fields in item header with correct crc are handled for string", "[nvs]")
|
||||
{
|
||||
PartitionEmulationFixture f(0, 3);
|
||||
nvs::Storage storage(f.part());
|
||||
// prepare some data
|
||||
TEST_ESP_OK(storage.init(0, 3));
|
||||
TEST_ESP_OK(storage.writeItem(0, "ns1", static_cast<uint8_t>(1)));
|
||||
const char str[] = "String67890123456789012345678901String67890123456789012345678901String6789012345678901234567890"; // 95 + 1 bytes data occupy 3 entries, overhead 1
|
||||
|
||||
TEST_ESP_OK(storage.writeItem(1, nvs::ItemType::SZ, "valuestr1", str, strlen(str)));
|
||||
TEST_ESP_OK(storage.writeItem(1, nvs::ItemType::SZ, "valuestr2", str, strlen(str)));
|
||||
TEST_ESP_OK(storage.writeItem(1, nvs::ItemType::SZ, "valuestr3", str, strlen(str)));
|
||||
TEST_ESP_OK(storage.writeItem(1, nvs::ItemType::SZ, "valuestr4", str, strlen(str)));
|
||||
TEST_ESP_OK(storage.writeItem(1, "valueu32", static_cast<uint32_t>(2)));
|
||||
|
||||
// read the values back
|
||||
char read_str[sizeof(str)] = {0};
|
||||
size_t read_str_size = sizeof(read_str);
|
||||
uint32_t val = 0;
|
||||
|
||||
TEST_ESP_OK(storage.readItem(1, nvs::ItemType::SZ, "valuestr1", (void*)read_str, read_str_size));
|
||||
CHECK(strcmp(read_str, str) == 0);
|
||||
TEST_ESP_OK(storage.readItem(1, nvs::ItemType::SZ, "valuestr2", (void*)read_str, read_str_size));
|
||||
CHECK(strcmp(read_str, str) == 0);
|
||||
TEST_ESP_OK(storage.readItem(1, nvs::ItemType::SZ, "valuestr3", (void*)read_str, read_str_size));
|
||||
CHECK(strcmp(read_str, str) == 0);
|
||||
TEST_ESP_OK(storage.readItem(1, nvs::ItemType::SZ, "valuestr4", (void*)read_str, read_str_size));
|
||||
CHECK(strcmp(read_str, str) == 0);
|
||||
|
||||
// default values for re-check after sections of the test
|
||||
esp_err_t exp_err_str1 = ESP_OK;
|
||||
esp_err_t exp_err_str2 = ESP_OK;
|
||||
esp_err_t exp_err_str3 = ESP_OK;
|
||||
esp_err_t exp_err_str4 = ESP_OK;
|
||||
|
||||
TEST_ESP_OK(storage.readItem(1, "valueu32", val));
|
||||
CHECK(val == 2);
|
||||
|
||||
// allow overwriting smaller portion of flash than whole 4k page
|
||||
((esp_partition_t*) f.get_esp_partition())->erase_size = 1;
|
||||
|
||||
SECTION("damage item header of valuestr1 to introduce span exceeding remaining page size error") {
|
||||
// span of the valstr1 is 4, there are 126 - 1 = 125 entries left in the page, set damaged span to 126
|
||||
uint8_t new_span = 126;
|
||||
|
||||
const uint8_t entry_index = 3; // 2x page header + 1x ns1
|
||||
|
||||
size_t entry_offset = 32 * entry_index;
|
||||
uint8_t buff[sizeof(nvs::Item)] = {0};
|
||||
TEST_ESP_OK(esp_partition_read(f.get_esp_partition(), entry_offset, &buff, sizeof(buff)));
|
||||
|
||||
nvs::Item* item = (nvs::Item*)buff;
|
||||
|
||||
item->span = new_span;
|
||||
// recalculate crc same way as nvs::Item::calculateCrc32 does
|
||||
item->crc32 = ((nvs::Item*)buff)->calculateCrc32();
|
||||
|
||||
TEST_ESP_OK(esp_partition_erase_range(f.get_esp_partition(), entry_offset, sizeof(buff)));
|
||||
TEST_ESP_OK(esp_partition_write(f.get_esp_partition(), entry_offset, &buff, sizeof(buff)));
|
||||
|
||||
// expect error when trying to read the item
|
||||
exp_err_str1 = ESP_ERR_NVS_NOT_FOUND;
|
||||
}
|
||||
|
||||
SECTION("damage item header of valuestr2 to introduce span exceeding the number of entries required to store the string") {
|
||||
// span of the valstr2 is 4, there are 126 - (1 + 4) = 121 entries left in the page, set damaged span to value below 121 and above 4
|
||||
uint8_t new_span = 10;
|
||||
|
||||
const uint8_t entry_index = 7; // 2x page header + 1x ns1 + 4x valuestr1
|
||||
|
||||
size_t entry_offset = 32 * entry_index;
|
||||
uint8_t buff[sizeof(nvs::Item)] = {0};
|
||||
TEST_ESP_OK(esp_partition_read(f.get_esp_partition(), entry_offset, &buff, sizeof(buff)));
|
||||
|
||||
nvs::Item* item = (nvs::Item*)buff;
|
||||
|
||||
item->span = new_span;
|
||||
// recalculate crc same way as nvs::Item::calculateCrc32 does
|
||||
item->crc32 = ((nvs::Item*)buff)->calculateCrc32();
|
||||
|
||||
TEST_ESP_OK(esp_partition_erase_range(f.get_esp_partition(), entry_offset, sizeof(buff)));
|
||||
TEST_ESP_OK(esp_partition_write(f.get_esp_partition(), entry_offset, &buff, sizeof(buff)));
|
||||
|
||||
// expect error when trying to read the item
|
||||
exp_err_str2 = ESP_ERR_NVS_NOT_FOUND;
|
||||
}
|
||||
|
||||
SECTION("damage item header of valuestr3 to introduce span lower than the number of entries required to store the string") {
|
||||
// span of the valstr3 is 4, set to 3
|
||||
uint8_t new_span = 3;
|
||||
|
||||
const uint8_t entry_index = 11; // 2x page header + 1x ns1 + 4x valuestr1 + 4x valuestr2
|
||||
|
||||
size_t entry_offset = 32 * entry_index;
|
||||
uint8_t buff[sizeof(nvs::Item)] = {0};
|
||||
TEST_ESP_OK(esp_partition_read(f.get_esp_partition(), entry_offset, &buff, sizeof(buff)));
|
||||
|
||||
nvs::Item* item = (nvs::Item*)buff;
|
||||
|
||||
item->span = new_span;
|
||||
// recalculate crc same way as nvs::Item::calculateCrc32 does
|
||||
item->crc32 = ((nvs::Item*)buff)->calculateCrc32();
|
||||
|
||||
TEST_ESP_OK(esp_partition_erase_range(f.get_esp_partition(), entry_offset, sizeof(buff)));
|
||||
TEST_ESP_OK(esp_partition_write(f.get_esp_partition(), entry_offset, &buff, sizeof(buff)));
|
||||
|
||||
// expect error when trying to read the item
|
||||
exp_err_str3 = ESP_ERR_NVS_NOT_FOUND;
|
||||
}
|
||||
|
||||
SECTION("damage item header of valuestr4 to introduce indicated variable data length going over the remaining space in the page") {
|
||||
const uint8_t entry_index = 15; // 2x page header + 1x ns1 + 4x valuestr1 + 4x valuestr2 + 4x valuestr3
|
||||
|
||||
size_t entry_offset = 32 * entry_index;
|
||||
// we are at entry 15, there are 126 - 15 = 111 entries left in the page, set data size to 112 * 32 bytes
|
||||
uint16_t new_size = 112 * 32;
|
||||
|
||||
uint8_t buff[sizeof(nvs::Item)] = {0};
|
||||
TEST_ESP_OK(esp_partition_read(f.get_esp_partition(), entry_offset, &buff, sizeof(buff)));
|
||||
|
||||
nvs::Item* item = (nvs::Item*)buff;
|
||||
|
||||
item->varLength.dataSize = new_size;
|
||||
// recalculate crc same way as nvs::Item::calculateCrc32 does
|
||||
item->crc32 = ((nvs::Item*)buff)->calculateCrc32();
|
||||
|
||||
TEST_ESP_OK(esp_partition_erase_range(f.get_esp_partition(), entry_offset, sizeof(buff)));
|
||||
TEST_ESP_OK(esp_partition_write(f.get_esp_partition(), entry_offset, &buff, sizeof(buff)));
|
||||
|
||||
// expect error when trying to read the item
|
||||
exp_err_str4 = ESP_ERR_NVS_NOT_FOUND;
|
||||
}
|
||||
|
||||
// check that storage can recover
|
||||
|
||||
TEST_ESP_OK(storage.init(0, 3));
|
||||
TEST_ESP_OK(storage.readItem(1, "valueu32", val));
|
||||
CHECK(val == 2);
|
||||
|
||||
// check that the damaged items are no longer present
|
||||
char read_buff[sizeof(str)];
|
||||
TEST_ESP_ERR(exp_err_str1, storage.readItem(1, nvs::ItemType::SZ, "valuestr1", read_buff, sizeof(read_buff)));
|
||||
TEST_ESP_ERR(exp_err_str2, storage.readItem(1, nvs::ItemType::SZ, "valuestr2", read_buff, sizeof(read_buff)));
|
||||
TEST_ESP_ERR(exp_err_str3, storage.readItem(1, nvs::ItemType::SZ, "valuestr3", read_buff, sizeof(read_buff)));
|
||||
TEST_ESP_ERR(exp_err_str4, storage.readItem(1, nvs::ItemType::SZ, "valuestr4", read_buff, sizeof(read_buff)));
|
||||
}
|
||||
|
||||
// Inconsistent fields in item header with correct crc are handled for blobs
|
||||
|
||||
// Before each sub case damaging the blob with key = "valueblob1", following scheme will be used to store the blob and do the test
|
||||
// Page 1
|
||||
// 1x namespace entry
|
||||
// 1x U32 entry, key = "valueu32_1" before data
|
||||
// 124x BLOB_DATA entries, key = "valueblob1", chunk index = 0 containing 123*32 bytes of data
|
||||
|
||||
// Page 2
|
||||
// 3x BLOB_DATA entries, key = "valueblob1", chunk index = 1 containing 2*32 bytes of data
|
||||
// 1x BLOB_INDEX entry key = "valueblob1", chunkVersion = 0, chunkCount = 2
|
||||
// 3x BLOB_DATA entries, key = "valueblob2", chunk index = 0 containing 2*32 bytes of data
|
||||
// 1x BLOB_INDEX entry key = "valueblob2", chunkVersion = 0, chunkCount = 1
|
||||
// 1x U32 entry key = "valueu32_2" after data
|
||||
TEST_CASE("inconsistent fields in item header with correct crc are handled for multi page blob", "[nvs]")
|
||||
{
|
||||
PartitionEmulationFixture f(0, 3);
|
||||
|
||||
// keys
|
||||
char ukey1[] = "valueu32_1";
|
||||
char ukey2[] = "valueu32_2";
|
||||
char blob_key1[] = "valueblob1";
|
||||
char blob_key2[] = "valueblob2";
|
||||
|
||||
// planned chunk lengths
|
||||
size_t blob_key1_chunk_0_len = 123 * 32;
|
||||
size_t blob_key1_chunk_1_len = 2 * 32;
|
||||
size_t blob_key2_chunk_len = 2 * 32;
|
||||
|
||||
// initial values
|
||||
uint32_t uval1 = 1;
|
||||
uint32_t uval2 = 2;
|
||||
uint8_t blob_data1[blob_key1_chunk_0_len + blob_key1_chunk_1_len];
|
||||
uint8_t blob_data2[blob_key2_chunk_len];
|
||||
|
||||
// value buffers
|
||||
uint32_t read_u32_1;
|
||||
uint32_t read_u32_2;
|
||||
uint8_t read_blob1[sizeof(blob_data1)];
|
||||
uint8_t read_blob2[sizeof(blob_data2)];
|
||||
size_t read_blob1_size = sizeof(read_blob1);
|
||||
size_t read_blob2_size = sizeof(read_blob2);
|
||||
|
||||
// Skip one page, 2 entries of page header and 3 entries of valueblob1's BLOB_DATA entries
|
||||
const size_t blob_index_offset = 4096 + 32 * 2 + 32 * 3;
|
||||
|
||||
// Skip 2 entries of page header 1 entry of namespace and 1 entry of valueu32_1
|
||||
const size_t blob_data_chunk0_offset = 32 * 2 + 32 * 1 + 32 * 1;
|
||||
|
||||
// returns length of BLOB_DATA of blob_key1 on page 1 within the partition
|
||||
// Skip 2 entries of page header 1 entry of namespace and 1 entry of valueu32_1
|
||||
const uint16_t blob_data_chunk0_len = blob_key1_chunk_0_len;
|
||||
|
||||
// returns length of BLOB_DATA of blob_key1 on page 1 within the partition
|
||||
// Skip 2 entries of page header 1 entry of namespace and 1 entry of valueu32_1
|
||||
const uint16_t blob_data_chunk1_len = blob_key1_chunk_1_len;
|
||||
|
||||
// returns offset of BLOB_DATA of blob_key1 on page 1 within the partition
|
||||
// Skip one page, 2 entries of page header
|
||||
const size_t blob_data_chunk1_offset = 4096 + 32 * 2;
|
||||
|
||||
// initialize buffers
|
||||
read_u32_1 = 0;
|
||||
read_u32_2 = 0;
|
||||
memset(blob_data1, 0x55, sizeof(blob_data1));
|
||||
memset(blob_data2, 0xaa, sizeof(blob_data2));
|
||||
memset(read_blob1, 0, sizeof(read_blob1));
|
||||
memset(read_blob2, 0, sizeof(read_blob2));
|
||||
|
||||
// Write initial data to the nvs partition
|
||||
{
|
||||
TEST_ESP_OK(nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), 0, 3));
|
||||
|
||||
nvs_handle_t handle;
|
||||
TEST_ESP_OK(nvs_open("test", NVS_READWRITE, &handle));
|
||||
TEST_ESP_OK(nvs_set_u32(handle, ukey1, uval1));
|
||||
TEST_ESP_OK(nvs_set_blob(handle, blob_key1, blob_data1, sizeof(blob_data1)));
|
||||
TEST_ESP_OK(nvs_set_blob(handle, blob_key2, blob_data2, sizeof(blob_data2)));
|
||||
TEST_ESP_OK(nvs_set_u32(handle, ukey2, uval2));
|
||||
nvs_close(handle);
|
||||
|
||||
TEST_ESP_OK(nvs_flash_deinit_partition(f.part()->get_partition_name()));
|
||||
}
|
||||
|
||||
// during the test, we will make changes of the data in more granular way than the whole 4k page
|
||||
// allow overwriting smaller portion of flash than whole 4k page
|
||||
((esp_partition_t*) f.get_esp_partition())->erase_size = 1;
|
||||
|
||||
// Sub test cases
|
||||
SECTION("damage BLOB_IDX - span set to 0") {
|
||||
// Damage BLOB_IDX of valueblob1, modify span == 1 to 0
|
||||
size_t entry_offset = blob_index_offset;
|
||||
uint8_t expected_span = 1;
|
||||
uint8_t new_span = 0;
|
||||
|
||||
uint8_t buff[sizeof(nvs::Item)] = {0};
|
||||
TEST_ESP_OK(esp_partition_read(f.get_esp_partition(), entry_offset, &buff, sizeof(buff)));
|
||||
nvs::Item* item = (nvs::Item*)buff;
|
||||
|
||||
CHECK(item->span == expected_span);
|
||||
item->span = new_span;
|
||||
// recalculate crc same way as nvs::Item::calculateCrc32 does
|
||||
item->crc32 = ((nvs::Item*)buff)->calculateCrc32();
|
||||
TEST_ESP_OK(esp_partition_erase_range(f.get_esp_partition(), entry_offset, sizeof(buff)));
|
||||
TEST_ESP_OK(esp_partition_write(f.get_esp_partition(), entry_offset, &buff, sizeof(buff)));
|
||||
}
|
||||
|
||||
SECTION("damage BLOB_IDX - chunkIndex set to 111 instead of CHUNK_ANY") {
|
||||
// Damage BLOB_IDX of valueblob1, modify chunkIndex == 111
|
||||
size_t entry_offset = blob_index_offset;
|
||||
uint8_t expected_chunk_index = nvs::Item::CHUNK_ANY;
|
||||
uint8_t new_chunk_index = 111;
|
||||
|
||||
uint8_t buff[sizeof(nvs::Item)] = {0};
|
||||
TEST_ESP_OK(esp_partition_read(f.get_esp_partition(), entry_offset, &buff, sizeof(buff)));
|
||||
nvs::Item* item = (nvs::Item*)buff;
|
||||
|
||||
CHECK(item->chunkIndex == expected_chunk_index);
|
||||
item->chunkIndex = new_chunk_index;
|
||||
// recalculate crc same way as nvs::Item::calculateCrc32 does
|
||||
item->crc32 = ((nvs::Item*)buff)->calculateCrc32();
|
||||
TEST_ESP_OK(esp_partition_erase_range(f.get_esp_partition(), entry_offset, sizeof(buff)));
|
||||
TEST_ESP_OK(esp_partition_write(f.get_esp_partition(), entry_offset, &buff, sizeof(buff)));
|
||||
}
|
||||
|
||||
SECTION("damage BLOB_IDX - blobIndex.dataSize over theoretical limit of 32*125*127 (entry size * max payload entries in page * max # of chunks)") {
|
||||
// Damage BLOB_IDX of valueblob1, modify blobIndex.dataSize
|
||||
size_t entry_offset = blob_index_offset;
|
||||
uint32_t expected_data_size = blob_data_chunk0_len + blob_data_chunk1_len;
|
||||
uint32_t new_data_size = (32 * 125 * 127) + 1;
|
||||
|
||||
uint8_t buff[sizeof(nvs::Item)] = {0};
|
||||
TEST_ESP_OK(esp_partition_read(f.get_esp_partition(), entry_offset, &buff, sizeof(buff)));
|
||||
nvs::Item* item = (nvs::Item*)buff;
|
||||
|
||||
CHECK(item->blobIndex.dataSize == expected_data_size);
|
||||
item->chunkIndex = new_data_size;
|
||||
// recalculate crc same way as nvs::Item::calculateCrc32 does
|
||||
item->crc32 = ((nvs::Item*)buff)->calculateCrc32();
|
||||
TEST_ESP_OK(esp_partition_erase_range(f.get_esp_partition(), entry_offset, sizeof(buff)));
|
||||
TEST_ESP_OK(esp_partition_write(f.get_esp_partition(), entry_offset, &buff, sizeof(buff)));
|
||||
}
|
||||
|
||||
SECTION("damage BLOB_DATA - chunkIndex set to invalid value of CHUNK_ANY") {
|
||||
// Damage BLOB_DATA of valueblob1, modify chunkIndex to CHUNK_ANY
|
||||
size_t entry_offset = blob_data_chunk0_offset;
|
||||
uint8_t unexpected_chunk_index = nvs::Item::CHUNK_ANY;
|
||||
uint8_t new_chunk_index = nvs::Item::CHUNK_ANY;
|
||||
|
||||
uint8_t buff[sizeof(nvs::Item)] = {0};
|
||||
TEST_ESP_OK(esp_partition_read(f.get_esp_partition(), entry_offset, &buff, sizeof(buff)));
|
||||
nvs::Item* item = (nvs::Item*)buff;
|
||||
|
||||
CHECK(item->chunkIndex != unexpected_chunk_index);
|
||||
item->chunkIndex = new_chunk_index;
|
||||
|
||||
// recalculate crc same way as nvs::Item::calculateCrc32 does
|
||||
item->crc32 = ((nvs::Item*)buff)->calculateCrc32();
|
||||
TEST_ESP_OK(esp_partition_erase_range(f.get_esp_partition(), entry_offset, sizeof(buff)));
|
||||
TEST_ESP_OK(esp_partition_write(f.get_esp_partition(), entry_offset, &buff, sizeof(buff)));
|
||||
}
|
||||
|
||||
SECTION("damage BLOB_DATA - varLength.dataSize is exceeding the maximum capacity available till the end of the page") {
|
||||
// Damage BLOB_DATA of valueblob1, set new data size to be beyond remaining capacity in the page.
|
||||
// Even if all entries are used for payload, there will be one overhead entry required
|
||||
size_t entry_offset = blob_data_chunk1_offset; // let's take chunk1 for this test
|
||||
uint16_t expected_data_size = blob_data_chunk1_len;
|
||||
uint16_t new_data_size = 32 * 126;
|
||||
|
||||
uint8_t buff[sizeof(nvs::Item)] = {0};
|
||||
TEST_ESP_OK(esp_partition_read(f.get_esp_partition(), entry_offset, &buff, sizeof(buff)));
|
||||
nvs::Item* item = (nvs::Item*)buff;
|
||||
|
||||
CHECK(item->varLength.dataSize == expected_data_size);
|
||||
item->varLength.dataSize = new_data_size;
|
||||
|
||||
// recalculate crc same way as nvs::Item::calculateCrc32 does
|
||||
item->crc32 = ((nvs::Item*)buff)->calculateCrc32();
|
||||
TEST_ESP_OK(esp_partition_erase_range(f.get_esp_partition(), entry_offset, sizeof(buff)));
|
||||
TEST_ESP_OK(esp_partition_write(f.get_esp_partition(), entry_offset, &buff, sizeof(buff)));
|
||||
}
|
||||
|
||||
SECTION("damage BLOB_DATA - span > maxAvailablePageSpan") {
|
||||
// Damage BLOB_DATA of valueblob1, set span to the value larger than the rest of entries in the page but smaller than maximum of entries .
|
||||
// page can accommodate
|
||||
// chunk0 starts at entry index 2 so max span is 126 - 2 = 124. Use 125
|
||||
size_t entry_offset = blob_data_chunk0_offset;
|
||||
uint8_t expected_span = (blob_data_chunk0_len + 31) / 32 + 1;
|
||||
uint8_t new_span = 125;
|
||||
|
||||
uint8_t buff[sizeof(nvs::Item)] = {0};
|
||||
TEST_ESP_OK(esp_partition_read(f.get_esp_partition(), entry_offset, &buff, sizeof(buff)));
|
||||
nvs::Item* item = (nvs::Item*)buff;
|
||||
|
||||
CHECK(item->span == expected_span);
|
||||
item->span = new_span;
|
||||
|
||||
// recalculate crc same way as nvs::Item::calculateCrc32 does
|
||||
item->crc32 = ((nvs::Item*)buff)->calculateCrc32();
|
||||
TEST_ESP_OK(esp_partition_erase_range(f.get_esp_partition(), entry_offset, sizeof(buff)));
|
||||
TEST_ESP_OK(esp_partition_write(f.get_esp_partition(), entry_offset, &buff, sizeof(buff)));
|
||||
}
|
||||
|
||||
// check that storage can recover and validate the remaining data
|
||||
{
|
||||
TEST_ESP_OK(nvs::NVSPartitionManager::get_instance()->init_custom(f.part(), 0, 3));
|
||||
|
||||
nvs_handle_t handle;
|
||||
TEST_ESP_OK(nvs_open("test", NVS_READWRITE, &handle));
|
||||
TEST_ESP_OK(nvs_get_u32(handle, ukey1, &read_u32_1));
|
||||
TEST_ESP_ERR(nvs_get_blob(handle, blob_key1, &read_blob1, &read_blob1_size), ESP_ERR_NVS_NOT_FOUND);
|
||||
TEST_ESP_OK(nvs_get_blob(handle, blob_key2, &read_blob2, &read_blob2_size));
|
||||
TEST_ESP_OK(nvs_get_u32(handle, ukey2, &read_u32_2));
|
||||
nvs_close(handle);
|
||||
TEST_ESP_OK(nvs_flash_deinit_partition(f.part()->get_partition_name()));
|
||||
|
||||
// validate the data
|
||||
CHECK(read_u32_1 == uval1);
|
||||
CHECK(read_u32_2 == uval2);
|
||||
CHECK(memcmp(read_blob2, blob_data2, sizeof(blob_data2)) == 0);
|
||||
}
|
||||
}
|
||||
|
||||
// header entry of u32 entry will be damaged in a way that data type will be invalid
|
||||
// while crc will be correct
|
||||
TEST_CASE("invalid data type in item header with correct crc is handled", "[nvs]")
|
||||
{
|
||||
PartitionEmulationFixture f(0, 3);
|
||||
nvs::Storage storage(f.part());
|
||||
// prepare some data
|
||||
TEST_ESP_OK(storage.init(0, 3));
|
||||
TEST_ESP_OK(storage.writeItem(0, "ns1", static_cast<uint8_t>(1)));
|
||||
TEST_ESP_OK(storage.writeItem(1, "value1", static_cast<uint32_t>(1)));
|
||||
TEST_ESP_OK(storage.writeItem(1, "value2", static_cast<uint32_t>(2)));
|
||||
TEST_ESP_OK(storage.writeItem(1, "value3", static_cast<uint32_t>(3)));
|
||||
|
||||
// damage item header of value2 to introduce data type error, recalculate crc
|
||||
{
|
||||
nvs::ItemType new_data_type = (nvs::ItemType) 0xfe;
|
||||
size_t entry_offset = 32 * 4; // 2x page header + 1x ns1 + 1x value1
|
||||
uint8_t buff[sizeof(nvs::Item)] = {0};
|
||||
TEST_ESP_OK(esp_partition_read(f.get_esp_partition(), entry_offset, &buff, sizeof(buff)));
|
||||
|
||||
nvs::Item* item = (nvs::Item*)buff;
|
||||
|
||||
// set span to U8
|
||||
item->datatype = new_data_type;
|
||||
// recalculate crc same way as nvs::Item::calculateCrc32 does
|
||||
item->crc32 = ((nvs::Item*)buff)->calculateCrc32();
|
||||
|
||||
// allow overwriting smaller portion of flash than whole 4k page
|
||||
((esp_partition_t*) f.get_esp_partition())->erase_size = 1;
|
||||
TEST_ESP_OK(esp_partition_erase_range(f.get_esp_partition(), entry_offset, sizeof(buff)));
|
||||
TEST_ESP_OK(esp_partition_write(f.get_esp_partition(), entry_offset, &buff, sizeof(buff)));
|
||||
}
|
||||
|
||||
// check that storage can recover
|
||||
uint32_t val = 0;
|
||||
|
||||
TEST_ESP_OK(storage.init(0, 3));
|
||||
TEST_ESP_OK(storage.readItem(1, "value1", val));
|
||||
CHECK(val == 1);
|
||||
// check that the damaged item is no longer present
|
||||
TEST_ESP_ERR(ESP_ERR_NVS_NOT_FOUND, storage.readItem(1, "value2", val));
|
||||
|
||||
TEST_ESP_OK(storage.readItem(1, "value3", val));
|
||||
CHECK(val == 3);
|
||||
}
|
||||
|
||||
// leaks memory
|
||||
TEST_CASE("Recovery from power-off when the entry being erased is not on active page", "[nvs]")
|
||||
{
|
||||
@ -3085,7 +3569,6 @@ TEST_CASE("check and read data from partition generated via manufacturing utilit
|
||||
3,
|
||||
"testdata/sample_singlepage_blob.bin");
|
||||
|
||||
|
||||
childpid = fork();
|
||||
if (childpid == 0) {
|
||||
exit(execlp("bash", " bash",
|
||||
@ -3335,7 +3818,6 @@ TEST_CASE("nvs find key tests", "[nvs]")
|
||||
const uint32_t NVS_FLASH_SECTOR = 6;
|
||||
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 13;
|
||||
|
||||
|
||||
TEST_ESP_ERR(nvs_open("namespace1", NVS_READWRITE, &handle_1), ESP_ERR_NVS_NOT_INITIALIZED);
|
||||
for (uint16_t i = NVS_FLASH_SECTOR; i < NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN; ++i) {
|
||||
f.erase(i);
|
||||
@ -3375,11 +3857,15 @@ TEST_CASE("nvs find key tests", "[nvs]")
|
||||
uint8_t *p_buff = (uint8_t *) malloc(buff_len);
|
||||
CHECK(p_buff != nullptr);
|
||||
TEST_ESP_ERR(nvs_find_key(handle_1, "foo2", &datatype_found), ESP_ERR_NVS_NOT_FOUND);
|
||||
for(size_t i=0; i<buff_len; i++) p_buff[i] = (uint8_t) (i%0xff);
|
||||
for (size_t i = 0; i < buff_len; i++) {
|
||||
p_buff[i] = (uint8_t)(i % 0xff);
|
||||
}
|
||||
TEST_ESP_OK(nvs_set_blob(handle_1, "foo2", p_buff, buff_len));
|
||||
TEST_ESP_OK(nvs_find_key(handle_1, "foo2", &datatype_found));
|
||||
CHECK(datatype_found == NVS_TYPE_BLOB);
|
||||
for(size_t i=0; i<buff_len; i++) p_buff[i] = (uint8_t) ((buff_len-i-1)%0xff);
|
||||
for (size_t i = 0; i < buff_len; i++) {
|
||||
p_buff[i] = (uint8_t)((buff_len - i - 1) % 0xff);
|
||||
}
|
||||
TEST_ESP_OK(nvs_set_blob(handle_1, "foo2", p_buff, buff_len));
|
||||
TEST_ESP_OK(nvs_find_key(handle_1, "foo2", &datatype_found));
|
||||
CHECK(datatype_found == NVS_TYPE_BLOB);
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -10,8 +10,7 @@
|
||||
#include <cstring>
|
||||
#include "nvs_internal.h"
|
||||
|
||||
namespace nvs
|
||||
{
|
||||
namespace nvs {
|
||||
|
||||
Page::Page() : mPartition(nullptr) { }
|
||||
|
||||
@ -46,7 +45,9 @@ esp_err_t Page::load(Partition *partition, uint32_t sectorNumber)
|
||||
const int BLOCK_SIZE = 128;
|
||||
uint32_t* block = new (std::nothrow) uint32_t[BLOCK_SIZE];
|
||||
|
||||
if (!block) return ESP_ERR_NO_MEM;
|
||||
if (!block) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < SPI_FLASH_SEC_SIZE; i += 4 * BLOCK_SIZE) {
|
||||
rc = mPartition->read_raw(mBaseAddress + i, block, 4 * BLOCK_SIZE);
|
||||
@ -101,7 +102,6 @@ esp_err_t Page::writeEntry(const Item& item)
|
||||
}
|
||||
err = mPartition->write(phyAddr, &item, sizeof(item));
|
||||
|
||||
|
||||
if (err != ESP_OK) {
|
||||
mState = PageState::INVALID;
|
||||
return err;
|
||||
@ -386,7 +386,7 @@ esp_err_t Page::eraseEntryAndSpan(size_t index)
|
||||
if (rc != ESP_OK) {
|
||||
return rc;
|
||||
}
|
||||
if (item.calculateCrc32() != item.crc32) {
|
||||
if (!item.checkHeaderConsistency(index)) {
|
||||
mHashList.erase(index);
|
||||
rc = alterEntryState(index, EntryState::ERASED);
|
||||
--mUsedEntryCount;
|
||||
@ -508,7 +508,10 @@ esp_err_t Page::copyItems(Page& other)
|
||||
NVS_ASSERT_OR_RETURN(end <= ENTRY_COUNT, ESP_FAIL);
|
||||
|
||||
for (size_t i = readEntryIndex + 1; i < end; ++i) {
|
||||
readEntry(i, entry);
|
||||
err = readEntry(i, entry);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
err = other.writeEntry(entry);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
@ -599,8 +602,7 @@ esp_err_t Page::mLoadEntryTable()
|
||||
--mUsedEntryCount;
|
||||
}
|
||||
++mErasedEntryCount;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -642,7 +644,7 @@ esp_err_t Page::mLoadEntryTable()
|
||||
return err;
|
||||
}
|
||||
|
||||
if (item.crc32 != item.calculateCrc32()) {
|
||||
if (!item.checkHeaderConsistency(i)) {
|
||||
err = eraseEntryAndSpan(i);
|
||||
if (err != ESP_OK) {
|
||||
mState = PageState::INVALID;
|
||||
@ -722,7 +724,7 @@ esp_err_t Page::mLoadEntryTable()
|
||||
return err;
|
||||
}
|
||||
|
||||
if (item.crc32 != item.calculateCrc32()) {
|
||||
if (!item.checkHeaderConsistency(i)) {
|
||||
err = eraseEntryAndSpan(i);
|
||||
if (err != ESP_OK) {
|
||||
mState = PageState::INVALID;
|
||||
@ -762,7 +764,6 @@ esp_err_t Page::mLoadEntryTable()
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
esp_err_t Page::initialize()
|
||||
{
|
||||
NVS_ASSERT_OR_RETURN(mState == PageState::UNINITIALIZED, ESP_FAIL);
|
||||
@ -910,8 +911,7 @@ esp_err_t Page::findItem(uint8_t nsIndex, ItemType datatype, const char* key, si
|
||||
return rc;
|
||||
}
|
||||
|
||||
auto crc32 = item.calculateCrc32();
|
||||
if (item.crc32 != crc32) {
|
||||
if (!item.checkHeaderConsistency(i)) {
|
||||
rc = eraseEntryAndSpan(i);
|
||||
if (rc != ESP_OK) {
|
||||
mState = PageState::INVALID;
|
||||
@ -975,7 +975,6 @@ esp_err_t Page::findItem(uint8_t nsIndex, ItemType datatype, const char* key, si
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (datatype != ItemType::ANY && item.datatype != datatype) {
|
||||
if (key == nullptr && nsIndex == NS_ANY && chunkIdx == CHUNK_ANY) {
|
||||
continue; // continue for bruteforce search on blob indices.
|
||||
@ -1001,7 +1000,6 @@ esp_err_t Page::getSeqNumber(uint32_t& seqNumber) const
|
||||
return ESP_ERR_NVS_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
|
||||
esp_err_t Page::setSeqNumber(uint32_t seqNumber)
|
||||
{
|
||||
if (mState != PageState::UNINITIALIZED) {
|
||||
|
@ -1,22 +1,16 @@
|
||||
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "nvs_types.hpp"
|
||||
|
||||
#include "nvs_page.hpp"
|
||||
#include "esp_log.h"
|
||||
#include "esp_rom_crc.h"
|
||||
|
||||
namespace nvs
|
||||
{
|
||||
#define TAG "nvs"
|
||||
|
||||
namespace nvs {
|
||||
uint32_t Item::calculateCrc32() const
|
||||
{
|
||||
uint32_t result = 0xffffffff;
|
||||
@ -46,4 +40,114 @@ uint32_t Item::calculateCrc32(const uint8_t* data, size_t size)
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Item::checkHeaderConsistency(const uint8_t entryIndex) const
|
||||
{
|
||||
// calculate and check the crc32
|
||||
if (crc32 != calculateCrc32()) {
|
||||
ESP_LOGD(TAG, "CRC32 mismatch for entry %d", entryIndex);
|
||||
return false;
|
||||
}
|
||||
|
||||
// validate the datatype and check the rest of the header fields
|
||||
switch (datatype) {
|
||||
// Entries occupying just one entry
|
||||
case ItemType::U8:
|
||||
case ItemType::I8:
|
||||
case ItemType::U16:
|
||||
case ItemType::I16:
|
||||
case ItemType::U32:
|
||||
case ItemType::I32:
|
||||
case ItemType::U64:
|
||||
case ItemType::I64: {
|
||||
if (span != 1) {
|
||||
ESP_LOGD(TAG, "Invalid span %u for datatype %#04x", (unsigned int)span, (unsigned int)datatype);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Special case for BLOB_IDX
|
||||
case ItemType::BLOB_IDX: {
|
||||
// span must be 1
|
||||
if (span != 1) {
|
||||
ESP_LOGD(TAG, "Invalid span %u for BLOB_IDX", (unsigned int)span);
|
||||
return false;
|
||||
}
|
||||
|
||||
// chunkIndex must be CHUNK_ANY
|
||||
if (chunkIndex != CHUNK_ANY) {
|
||||
ESP_LOGD(TAG, "Invalid chunk index %u for BLOB_IDX", (unsigned int)chunkIndex);
|
||||
return false;
|
||||
}
|
||||
|
||||
// check maximum data length
|
||||
// the maximal data length is determined by:
|
||||
// maximum number of chunks. Chunks are stored in uin8_t, but are logically divided into two "VerOffset" ranges of values (0 based and 128 based)
|
||||
// maximum theoretical number of entries in the chunk (Page::ENTRY_COUNT - 1) and the number of bytes entry can store (Page::ENTRY_SIZE)
|
||||
const uint32_t maxDataSize = (uint32_t)((UINT8_MAX / 2) * (Page::ENTRY_COUNT - 1) * Page::ENTRY_SIZE);
|
||||
if (blobIndex.dataSize > maxDataSize) {
|
||||
ESP_LOGD(TAG, "Data size %u bytes exceeds maximum possible size %u bytes for BLOB_IDX", (unsigned int)blobIndex.dataSize, (unsigned int)maxDataSize);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Entries with variable length data
|
||||
case ItemType::SZ:
|
||||
case ItemType::BLOB:
|
||||
case ItemType::BLOB_DATA: {
|
||||
uint16_t maxAvailableVDataSize;
|
||||
uint8_t maxAvailablePageSpan;
|
||||
uint8_t spanCalcFromLen;
|
||||
|
||||
// for BLOB_DATA, chunkIndex must NOT be CHUNK_ANY as this value is used to search ALL chunks in findItem
|
||||
if (datatype == ItemType::BLOB_DATA) {
|
||||
// chunkIndex must not be CHUNK_ANY
|
||||
if (chunkIndex == CHUNK_ANY) {
|
||||
ESP_LOGD(TAG, "Invalid chunk index %u for BLOB_DATA", (unsigned int)chunkIndex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// variable length and span checks
|
||||
|
||||
// based on the entryIndex determine the maximum variable length capacity in bytes to the end of the page
|
||||
maxAvailableVDataSize = ((Page::ENTRY_COUNT - entryIndex) - 1) * Page::ENTRY_SIZE;
|
||||
|
||||
// check if the variable data length is not exceeding the maximum capacity available till the end of the page
|
||||
if (varLength.dataSize > maxAvailableVDataSize) {
|
||||
ESP_LOGD(TAG, "Variable data length %u bytes exceeds page boundary. Maximum calculated from the current entry position within page is %u bytes for datatype %#04x ", (unsigned int)varLength.dataSize, (unsigned int)maxAvailableVDataSize, (unsigned int)datatype);
|
||||
return false;
|
||||
}
|
||||
|
||||
// based on the entryIndex determine the maximum possible span up to the end of the page
|
||||
maxAvailablePageSpan = Page::ENTRY_COUNT - entryIndex;
|
||||
|
||||
// this check ensures no data is read beyond the end of the page
|
||||
if (span > maxAvailablePageSpan) {
|
||||
ESP_LOGD(TAG, "Span %u exceeds page boundary. Maximum calculated from the current entry position within page is %u for datatype %#04x ", (unsigned int)span, (unsigned int)maxAvailablePageSpan, (unsigned int)datatype);
|
||||
return false;
|
||||
}
|
||||
|
||||
// here we have both span and varLength.dataSize within the page boundary. Check if these values are consistent
|
||||
spanCalcFromLen = (uint8_t)(((size_t) varLength.dataSize + Page::ENTRY_SIZE - 1) / Page::ENTRY_SIZE);
|
||||
spanCalcFromLen ++; // add overhead entry
|
||||
|
||||
// this check ensures that the span is equal to the number of entries required to store the data plus the overhead entry
|
||||
if (span != spanCalcFromLen) {
|
||||
ESP_LOGD(TAG, "Span %i does not match span %u calculated from variable data length %u bytes for datatype %#04x", (unsigned int)span, (unsigned int)spanCalcFromLen, (unsigned int)varLength.dataSize, (unsigned int)datatype);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Invalid datatype
|
||||
default: {
|
||||
ESP_LOGD(TAG, "Invalid datatype %#04x", (unsigned int)datatype);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace nvs
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -108,6 +108,14 @@ public:
|
||||
dst = *reinterpret_cast<T*>(data);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// Returns true if item's header:
|
||||
// crc32 matches the calculated crc32
|
||||
// and datatype is one of the supported types
|
||||
// and span is within the allowed range for the datatype and below the maximum calculated from the entryIndex
|
||||
//
|
||||
// Parameter entryIndex is used to calculate the maximum span for the given entry
|
||||
bool checkHeaderConsistency(const uint8_t entryIndex) const;
|
||||
};
|
||||
|
||||
} // namespace nvs
|
||||
|
@ -499,7 +499,6 @@ components/nvs_flash/src/nvs_pagemanager.hpp
|
||||
components/nvs_flash/src/nvs_partition_lookup.cpp
|
||||
components/nvs_flash/src/nvs_partition_lookup.hpp
|
||||
components/nvs_flash/src/nvs_test_api.h
|
||||
components/nvs_flash/src/nvs_types.cpp
|
||||
components/nvs_flash/test_nvs_host/main.cpp
|
||||
components/nvs_flash/test_nvs_host/sdkconfig.h
|
||||
components/nvs_flash/test_nvs_host/test_intrusive_list.cpp
|
||||
|
Loading…
Reference in New Issue
Block a user