Skip to content

Commit 553d103

Browse files
committed
Merge branch 'bugfix/nvs_blob_overwrite' into 'master'
Bugfix of old NVS Blobs handling when legacy compatibility option is set See merge request espressif/esp-idf!41741
2 parents 4680b97 + 96f4f78 commit 553d103

File tree

3 files changed

+62
-20
lines changed

3 files changed

+62
-20
lines changed

components/nvs_flash/host_test/nvs_host_test/main/test_nvs.cpp

Lines changed: 33 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -608,7 +608,7 @@ TEST_CASE("nvs api tests", "[nvs]")
608608
nvs_handle_t handle_2;
609609
TEST_ESP_OK(nvs_open("namespace2", NVS_READWRITE, &handle_2));
610610
TEST_ESP_OK(nvs_set_i32(handle_2, "foo", 0x3456789a));
611-
const char *str = "value 0123456789abcdef0123456789abcdef";
611+
char str[] = "value 0123456789abcdef0123456789abcdef";
612612
TEST_ESP_OK(nvs_set_str(handle_2, "key", str));
613613

614614
int32_t v1;
@@ -619,7 +619,7 @@ TEST_CASE("nvs api tests", "[nvs]")
619619
TEST_ESP_OK(nvs_get_i32(handle_2, "foo", &v2));
620620
CHECK(0x3456789a == v2);
621621

622-
char buf[strlen(str) + 1];
622+
char buf[sizeof(str)];
623623
size_t buf_len = sizeof(buf);
624624

625625
size_t buf_len_needed;
@@ -1270,13 +1270,15 @@ class RandomTest {
12701270
} else {
12711271
blobBufLen = largeBlobLen ;
12721272
}
1273-
uint8_t buf[blobBufLen];
1273+
uint8_t* buf = new uint8_t[blobBufLen];
12741274
memset(buf, 0, blobBufLen);
12751275

12761276
size_t len = blobBufLen;
12771277
auto err = nvs_get_blob(handle, keys[index], buf, &len);
1278+
auto eval_result = evaluate(delayCount, err, types[index], written[index], potentially_written[index], buf, values[index], future_values[index], blobBufLen);
1279+
delete [] buf;
12781280

1279-
REQUIRE(evaluate(delayCount, err, types[index], written[index], potentially_written[index], buf, values[index], future_values[index], blobBufLen) == true);
1281+
REQUIRE(eval_result == true);
12801282
break;
12811283
}
12821284

@@ -1371,7 +1373,7 @@ class RandomTest {
13711373
} else {
13721374
blobBufLen = largeBlobLen ;
13731375
}
1374-
uint8_t buf[blobBufLen];
1376+
uint8_t* buf = new uint8_t[blobBufLen];
13751377
memset(buf, 0, blobBufLen);
13761378
size_t blobLen = gen() % blobBufLen;
13771379
std::generate_n(buf, blobLen, [&]() -> uint8_t {
@@ -1386,16 +1388,19 @@ class RandomTest {
13861388
if (err == ESP_ERR_FLASH_OP_FAIL) {
13871389
// mark potentially written
13881390
potentially_written[index] = true;
1391+
delete [] buf;
13891392
return err;
13901393
}
13911394
if (err == ESP_ERR_NVS_REMOVE_FAILED) {
13921395
written[index] = true;
13931396
memcpy(reinterpret_cast<uint8_t *>(values[index]), buf, blobBufLen);
1397+
delete [] buf;
13941398
return ESP_ERR_FLASH_OP_FAIL;
13951399
}
13961400
REQUIRE(err == ESP_OK);
13971401
written[index] = true;
13981402
memcpy(reinterpret_cast<char *>(values[index]), buf, blobBufLen);
1403+
delete [] buf;
13991404
break;
14001405
}
14011406

@@ -2889,16 +2894,20 @@ TEST_CASE("inconsistent fields in item header with correct crc are handled for m
28892894
// initial values
28902895
uint32_t uval1 = 1;
28912896
uint32_t uval2 = 2;
2892-
uint8_t blob_data1[blob_key1_chunk_0_len + blob_key1_chunk_1_len];
2893-
uint8_t blob_data2[blob_key2_chunk_len];
2897+
size_t blob_data1_len = blob_key1_chunk_0_len + blob_key1_chunk_1_len;
2898+
size_t blob_data2_len = blob_key2_chunk_len;
2899+
uint8_t* blob_data1 = new uint8_t[blob_data1_len];
2900+
uint8_t* blob_data2 = new uint8_t[blob_data2_len];
28942901

28952902
// value buffers
28962903
uint32_t read_u32_1;
28972904
uint32_t read_u32_2;
2898-
uint8_t read_blob1[sizeof(blob_data1)];
2899-
uint8_t read_blob2[sizeof(blob_data2)];
2900-
size_t read_blob1_size = sizeof(read_blob1);
2901-
size_t read_blob2_size = sizeof(read_blob2);
2905+
2906+
size_t read_blob1_size = blob_data1_len;
2907+
size_t read_blob2_size = blob_data2_len;
2908+
uint8_t* read_blob1 = new uint8_t[read_blob1_size];
2909+
uint8_t* read_blob2 = new uint8_t[read_blob2_size];
2910+
29022911

29032912
// Skip one page, 2 entries of page header and 3 entries of valueblob1's BLOB_DATA entries
29042913
const size_t blob_index_offset = 4096 + 32 * 2 + 32 * 3;
@@ -2921,10 +2930,10 @@ TEST_CASE("inconsistent fields in item header with correct crc are handled for m
29212930
// initialize buffers
29222931
read_u32_1 = 0;
29232932
read_u32_2 = 0;
2924-
memset(blob_data1, 0x55, sizeof(blob_data1));
2925-
memset(blob_data2, 0xaa, sizeof(blob_data2));
2926-
memset(read_blob1, 0, sizeof(read_blob1));
2927-
memset(read_blob2, 0, sizeof(read_blob2));
2933+
memset(blob_data1, 0x55, blob_data1_len);
2934+
memset(blob_data2, 0xaa, blob_data2_len);
2935+
memset(read_blob1, 0, read_blob1_size);
2936+
memset(read_blob2, 0, read_blob2_size);
29282937

29292938
// Write initial data to the nvs partition
29302939
{
@@ -2933,8 +2942,8 @@ TEST_CASE("inconsistent fields in item header with correct crc are handled for m
29332942
nvs_handle_t handle;
29342943
TEST_ESP_OK(nvs_open("test", NVS_READWRITE, &handle));
29352944
TEST_ESP_OK(nvs_set_u32(handle, ukey1, uval1));
2936-
TEST_ESP_OK(nvs_set_blob(handle, blob_key1, blob_data1, sizeof(blob_data1)));
2937-
TEST_ESP_OK(nvs_set_blob(handle, blob_key2, blob_data2, sizeof(blob_data2)));
2945+
TEST_ESP_OK(nvs_set_blob(handle, blob_key1, blob_data1, blob_data1_len));
2946+
TEST_ESP_OK(nvs_set_blob(handle, blob_key2, blob_data2, blob_data2_len));
29382947
TEST_ESP_OK(nvs_set_u32(handle, ukey2, uval2));
29392948
nvs_close(handle);
29402949

@@ -3067,16 +3076,17 @@ TEST_CASE("inconsistent fields in item header with correct crc are handled for m
30673076
nvs_handle_t handle;
30683077
TEST_ESP_OK(nvs_open("test", NVS_READWRITE, &handle));
30693078
TEST_ESP_OK(nvs_get_u32(handle, ukey1, &read_u32_1));
3070-
TEST_ESP_ERR(nvs_get_blob(handle, blob_key1, &read_blob1, &read_blob1_size), ESP_ERR_NVS_NOT_FOUND);
3071-
TEST_ESP_OK(nvs_get_blob(handle, blob_key2, &read_blob2, &read_blob2_size));
3079+
TEST_ESP_ERR(nvs_get_blob(handle, blob_key1, read_blob1, &read_blob1_size), ESP_ERR_NVS_NOT_FOUND);
3080+
TEST_ESP_OK(nvs_get_blob(handle, blob_key2, read_blob2, &read_blob2_size));
30723081
TEST_ESP_OK(nvs_get_u32(handle, ukey2, &read_u32_2));
30733082
nvs_close(handle);
30743083
TEST_ESP_OK(nvs_flash_deinit_partition(f.part()->get_partition_name()));
30753084

30763085
// validate the data
30773086
CHECK(read_u32_1 == uval1);
30783087
CHECK(read_u32_2 == uval2);
3079-
CHECK(memcmp(read_blob2, blob_data2, sizeof(blob_data2)) == 0);
3088+
CHECK(read_blob2_size == blob_data2_len);
3089+
CHECK(memcmp(read_blob2, blob_data2, read_blob2_size) == 0);
30803090
}
30813091
}
30823092

@@ -3808,6 +3818,8 @@ TEST_CASE("nvs multiple write with same key but different types", "[nvs]")
38083818
TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME));
38093819
}
38103820

3821+
#ifndef CONFIG_NVS_LEGACY_DUP_KEYS_COMPATIBILITY
3822+
// Following test case is not valid for new behavior not leading to multiple active values under the same key
38113823
TEST_CASE("nvs multiple write with same key blob and string involved", "[nvs]")
38123824
{
38133825
PartitionEmulationFixture f(0, 10);
@@ -3883,6 +3895,7 @@ TEST_CASE("nvs multiple write with same key blob and string involved", "[nvs]")
38833895

38843896
TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME));
38853897
}
3898+
#endif // !CONFIG_NVS_LEGACY_DUP_KEYS_COMPATIBILITY
38863899

38873900
TEST_CASE("nvs find key tests", "[nvs]")
38883901
{

components/nvs_flash/host_test/nvs_host_test/pytest_nvs_host_linux.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,14 @@
66

77

88
@pytest.mark.host_test
9+
@pytest.mark.parametrize(
10+
'config',
11+
[
12+
'default_set_key',
13+
'legacy_set_key',
14+
],
15+
indirect=True,
16+
)
917
@idf_parametrize('target', ['linux'], indirect=['target'])
1018
def test_nvs_host_linux(dut: Dut) -> None:
1119
dut.expect_exact('All tests passed', timeout=60)

components/nvs_flash/src/nvs_storage.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,27 @@ esp_err_t Storage::writeItem(uint8_t nsIndex, ItemType datatype, const char* key
407407
if(err == ESP_OK && findPage != nullptr) {
408408
matchedTypePageFound = true;
409409
}
410+
#ifdef CONFIG_NVS_LEGACY_DUP_KEYS_COMPATIBILITY
411+
// In legacy mode, we also try to find the item as BLOB if not found as BLOB_INDEX.
412+
// In this mode, it is possible to have multiple active values under the same (logical) key.
413+
// For BLOBs (which may have different physical representations in V1 it is BLOB, in V2 it is BLOB_INDEX) it in turn means
414+
// that we have to check both datatypes to find the old value.
415+
// The general case for compatibility flag disabled is below and handles all datatypes including BLOB.
416+
// To save some cycles, we do not compile both findItem calls in this case.
417+
if(err == ESP_ERR_NVS_NOT_FOUND) {
418+
// If not found as BLOB_INDEX, try to find it as BLOB (legacy support).
419+
err = findItem(nsIndex, ItemType::BLOB, key, findPage, item, Page::CHUNK_ANY, VerOffset::VER_ANY, &itemIndex);
420+
if(err == ESP_OK && findPage != nullptr) {
421+
matchedTypePageFound = false; // datatype does not match, we cannot extract chunkStart from the item
422+
423+
// keep the sequence number of the page where the item was found for later check of relocation
424+
err = findPage->getSeqNumber(findPageSeqNumber);
425+
if(err != ESP_OK) {
426+
return err;
427+
}
428+
}
429+
}
430+
#endif
410431
} else {
411432
// Handle all other data types than BLOB
412433
err = findItem(nsIndex, datatype, key, findPage, item, Page::CHUNK_ANY, VerOffset::VER_ANY, &itemIndex);

0 commit comments

Comments
 (0)