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
File diff suppressed because it is too large
Load Diff
@ -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,16 +10,15 @@
|
||||
#include <cstring>
|
||||
#include "nvs_internal.h"
|
||||
|
||||
namespace nvs
|
||||
{
|
||||
namespace nvs {
|
||||
|
||||
Page::Page() : mPartition(nullptr) { }
|
||||
|
||||
uint32_t Page::Header::calculateCrc32()
|
||||
{
|
||||
return esp_rom_crc32_le(0xffffffff,
|
||||
reinterpret_cast<uint8_t*>(this) + offsetof(Header, mSeqNumber),
|
||||
offsetof(Header, mCrc32) - offsetof(Header, mSeqNumber));
|
||||
reinterpret_cast<uint8_t*>(this) + offsetof(Header, mSeqNumber),
|
||||
offsetof(Header, mCrc32) - offsetof(Header, mSeqNumber));
|
||||
}
|
||||
|
||||
esp_err_t Page::load(Partition *partition, uint32_t sectorNumber)
|
||||
@ -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);
|
||||
@ -67,7 +68,7 @@ esp_err_t Page::load(Partition *partition, uint32_t sectorNumber)
|
||||
} else {
|
||||
mState = header.mState;
|
||||
mSeqNumber = header.mSeqNumber;
|
||||
if(header.mVersion < NVS_VERSION) {
|
||||
if (header.mVersion < NVS_VERSION) {
|
||||
return ESP_ERR_NVS_NEW_VERSION_FOUND;
|
||||
} else {
|
||||
mVersion = header.mVersion;
|
||||
@ -92,7 +93,7 @@ esp_err_t Page::load(Partition *partition, uint32_t sectorNumber)
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t Page::writeEntry(const Item& item)
|
||||
esp_err_t Page::writeEntry(const Item &item)
|
||||
{
|
||||
uint32_t phyAddr;
|
||||
esp_err_t err = getEntryAddress(mNextFreeEntry, &phyAddr);
|
||||
@ -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;
|
||||
@ -190,7 +190,7 @@ esp_err_t Page::writeItem(uint8_t nsIndex, ItemType datatype, const char* key, c
|
||||
|
||||
// primitive types should fit into one entry
|
||||
NVS_ASSERT_OR_RETURN(totalSize == ENTRY_SIZE ||
|
||||
isVariableLengthType(datatype), ESP_ERR_NVS_VALUE_TOO_LONG);
|
||||
isVariableLengthType(datatype), ESP_ERR_NVS_VALUE_TOO_LONG);
|
||||
|
||||
if (mNextFreeEntry == INVALID_ENTRY || mNextFreeEntry + entriesCount > ENTRY_COUNT) {
|
||||
// page will not fit this amount of data
|
||||
@ -283,12 +283,12 @@ esp_err_t Page::readItem(uint8_t nsIndex, ItemType datatype, const char* key, vo
|
||||
return rc;
|
||||
}
|
||||
size_t willCopy = ENTRY_SIZE;
|
||||
willCopy = (left < willCopy)?left:willCopy;
|
||||
willCopy = (left < willCopy) ? left : willCopy;
|
||||
memcpy(dst, ditem.rawData, willCopy);
|
||||
left -= willCopy;
|
||||
dst += willCopy;
|
||||
}
|
||||
if (Item::calculateCrc32(reinterpret_cast<uint8_t*>(data), item.varLength.dataSize) != item.varLength.dataCrc32) {
|
||||
if (Item::calculateCrc32(reinterpret_cast<uint8_t * >(data), item.varLength.dataSize) != item.varLength.dataCrc32) {
|
||||
rc = eraseEntryAndSpan(index);
|
||||
if (rc != ESP_OK) {
|
||||
return rc;
|
||||
@ -336,14 +336,14 @@ esp_err_t Page::cmpItem(uint8_t nsIndex, ItemType datatype, const char* key, con
|
||||
return rc;
|
||||
}
|
||||
size_t willCopy = ENTRY_SIZE;
|
||||
willCopy = (left < willCopy)?left:willCopy;
|
||||
willCopy = (left < willCopy) ? left : willCopy;
|
||||
if (memcmp(dst, ditem.rawData, willCopy)) {
|
||||
return ESP_ERR_NVS_CONTENT_DIFFERS;
|
||||
}
|
||||
left -= willCopy;
|
||||
dst += willCopy;
|
||||
}
|
||||
if (Item::calculateCrc32(reinterpret_cast<const uint8_t*>(data), item.varLength.dataSize) != item.varLength.dataCrc32) {
|
||||
if (Item::calculateCrc32(reinterpret_cast<const uint8_t * >(data), item.varLength.dataSize) != item.varLength.dataCrc32) {
|
||||
return ESP_ERR_NVS_NOT_FOUND;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
@ -460,7 +460,7 @@ esp_err_t Page::updateFirstUsedEntry(size_t index, size_t span)
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t Page::copyItems(Page& other)
|
||||
esp_err_t Page::copyItems(Page &other)
|
||||
{
|
||||
if (mFirstUsedEntry == INVALID_ENTRY) {
|
||||
return ESP_ERR_NVS_NOT_FOUND;
|
||||
@ -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;
|
||||
@ -527,7 +530,7 @@ esp_err_t Page::mLoadEntryTable()
|
||||
mState == PageState::FULL ||
|
||||
mState == PageState::FREEING) {
|
||||
auto rc = mPartition->read_raw(mBaseAddress + ENTRY_TABLE_OFFSET, mEntryTable.data(),
|
||||
mEntryTable.byteSize());
|
||||
mEntryTable.byteSize());
|
||||
if (rc != ESP_OK) {
|
||||
mState = PageState::INVALID;
|
||||
return rc;
|
||||
@ -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);
|
||||
@ -794,7 +795,7 @@ esp_err_t Page::alterEntryState(size_t index, EntryState state)
|
||||
size_t wordToWrite = mEntryTable.getWordIndex(index);
|
||||
uint32_t word = mEntryTable.data()[wordToWrite];
|
||||
err = mPartition->write_raw(mBaseAddress + ENTRY_TABLE_OFFSET + static_cast<uint32_t>(wordToWrite) * 4,
|
||||
&word, sizeof(word));
|
||||
&word, sizeof(word));
|
||||
if (err != ESP_OK) {
|
||||
mState = PageState::INVALID;
|
||||
return err;
|
||||
@ -810,7 +811,7 @@ esp_err_t Page::alterEntryRangeState(size_t begin, size_t end, EntryState state)
|
||||
esp_err_t err;
|
||||
for (ptrdiff_t i = end - 1; i >= static_cast<ptrdiff_t>(begin); --i) {
|
||||
err = mEntryTable.set(i, state);
|
||||
if (err != ESP_OK){
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
size_t nextWordIndex;
|
||||
@ -822,7 +823,7 @@ esp_err_t Page::alterEntryRangeState(size_t begin, size_t end, EntryState state)
|
||||
if (nextWordIndex != wordIndex) {
|
||||
uint32_t word = mEntryTable.data()[wordIndex];
|
||||
auto rc = mPartition->write_raw(mBaseAddress + ENTRY_TABLE_OFFSET + static_cast<uint32_t>(wordIndex) * 4,
|
||||
&word, 4);
|
||||
&word, 4);
|
||||
if (rc != ESP_OK) {
|
||||
return rc;
|
||||
}
|
||||
@ -844,7 +845,7 @@ esp_err_t Page::alterPageState(PageState state)
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t Page::readEntry(size_t index, Item& dst) const
|
||||
esp_err_t Page::readEntry(size_t index, Item &dst) const
|
||||
{
|
||||
uint32_t phyAddr;
|
||||
esp_err_t rc = getEntryAddress(index, &phyAddr);
|
||||
@ -858,7 +859,7 @@ esp_err_t Page::readEntry(size_t index, Item& dst) const
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t Page::findItem(uint8_t nsIndex, ItemType datatype, const char* key, size_t &itemIndex, Item& item, uint8_t chunkIdx, VerOffset chunkStart)
|
||||
esp_err_t Page::findItem(uint8_t nsIndex, ItemType datatype, const char* key, size_t &itemIndex, Item &item, uint8_t chunkIdx, VerOffset chunkStart)
|
||||
{
|
||||
if (mState == PageState::CORRUPT || mState == PageState::INVALID || mState == PageState::UNINITIALIZED) {
|
||||
return ESP_ERR_NVS_NOT_FOUND;
|
||||
@ -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.
|
||||
@ -992,7 +991,7 @@ esp_err_t Page::findItem(uint8_t nsIndex, ItemType datatype, const char* key, si
|
||||
return ESP_ERR_NVS_NOT_FOUND;
|
||||
}
|
||||
|
||||
esp_err_t Page::getSeqNumber(uint32_t& seqNumber) const
|
||||
esp_err_t Page::getSeqNumber(uint32_t &seqNumber) const
|
||||
{
|
||||
if (mState != PageState::UNINITIALIZED && mState != PageState::INVALID && mState != PageState::CORRUPT) {
|
||||
seqNumber = mSeqNumber;
|
||||
@ -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) {
|
||||
@ -1060,40 +1058,40 @@ size_t Page::getVarDataTailroom() const
|
||||
return 0;
|
||||
}
|
||||
/* Skip one entry for blob data item processing the data */
|
||||
return ((mNextFreeEntry < (ENTRY_COUNT-1)) ? ((ENTRY_COUNT - mNextFreeEntry - 1) * ENTRY_SIZE): 0);
|
||||
return ((mNextFreeEntry < (ENTRY_COUNT - 1)) ? ((ENTRY_COUNT - mNextFreeEntry - 1) * ENTRY_SIZE) : 0);
|
||||
}
|
||||
|
||||
const char* Page::pageStateToName(PageState ps)
|
||||
{
|
||||
switch (ps) {
|
||||
case PageState::CORRUPT:
|
||||
return "CORRUPT";
|
||||
case PageState::CORRUPT:
|
||||
return "CORRUPT";
|
||||
|
||||
case PageState::ACTIVE:
|
||||
return "ACTIVE";
|
||||
case PageState::ACTIVE:
|
||||
return "ACTIVE";
|
||||
|
||||
case PageState::FREEING:
|
||||
return "FREEING";
|
||||
case PageState::FREEING:
|
||||
return "FREEING";
|
||||
|
||||
case PageState::FULL:
|
||||
return "FULL";
|
||||
case PageState::FULL:
|
||||
return "FULL";
|
||||
|
||||
case PageState::INVALID:
|
||||
return "INVALID";
|
||||
case PageState::INVALID:
|
||||
return "INVALID";
|
||||
|
||||
case PageState::UNINITIALIZED:
|
||||
return "UNINITIALIZED";
|
||||
case PageState::UNINITIALIZED:
|
||||
return "UNINITIALIZED";
|
||||
|
||||
default:
|
||||
assert(0 && "invalid state value");
|
||||
return "";
|
||||
default:
|
||||
assert(0 && "invalid state value");
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
void Page::debugDump() const
|
||||
{
|
||||
printf("state=%" PRIx32 " (%s) addr=%" PRIx32 " seq=%" PRIu32 "\nfirstUsed=%" PRIu32 " nextFree=%" PRIu32 " used=%" PRIu16 " erased=%" PRIu16 "\n",
|
||||
static_cast<uint32_t>(mState), pageStateToName(mState), mBaseAddress, mSeqNumber, static_cast<uint32_t>(mFirstUsedEntry), static_cast<uint32_t>(mNextFreeEntry), mUsedEntryCount, mErasedEntryCount);
|
||||
static_cast<uint32_t>(mState), pageStateToName(mState), mBaseAddress, mSeqNumber, static_cast<uint32_t>(mFirstUsedEntry), static_cast<uint32_t>(mNextFreeEntry), mUsedEntryCount, mErasedEntryCount);
|
||||
size_t skip = 0;
|
||||
for (size_t i = 0; i < ENTRY_COUNT; ++i) {
|
||||
printf("%3d: ", static_cast<int>(i));
|
||||
@ -1111,7 +1109,7 @@ void Page::debugDump() const
|
||||
readEntry(i, item);
|
||||
if (skip == 0) {
|
||||
printf("W ns=%2" PRIu8 " type=%2" PRIu8 " span=%3" PRIu8 " key=\"%s\" chunkIdx=%" PRIu8 " len=%" PRIi32 "\n",
|
||||
item.nsIndex, static_cast<uint8_t>(item.datatype), item.span, item.key, item.chunkIndex, (item.span != 1)?(static_cast<int32_t>(item.varLength.dataSize)):(-1));
|
||||
item.nsIndex, static_cast<uint8_t>(item.datatype), item.span, item.key, item.chunkIndex, (item.span != 1) ? (static_cast<int32_t>(item.varLength.dataSize)) : (-1));
|
||||
if (item.span > 0 && item.span <= ENTRY_COUNT - i) {
|
||||
skip = item.span - 1;
|
||||
} else {
|
||||
@ -1132,24 +1130,24 @@ esp_err_t Page::calcEntries(nvs_stats_t &nvsStats)
|
||||
nvsStats.total_entries += ENTRY_COUNT;
|
||||
|
||||
switch (mState) {
|
||||
case PageState::UNINITIALIZED:
|
||||
case PageState::CORRUPT:
|
||||
nvsStats.free_entries += ENTRY_COUNT;
|
||||
break;
|
||||
case PageState::UNINITIALIZED:
|
||||
case PageState::CORRUPT:
|
||||
nvsStats.free_entries += ENTRY_COUNT;
|
||||
break;
|
||||
|
||||
case PageState::FULL:
|
||||
case PageState::ACTIVE:
|
||||
nvsStats.used_entries += mUsedEntryCount;
|
||||
nvsStats.free_entries += ENTRY_COUNT - mUsedEntryCount; // it's equivalent free + erase entries.
|
||||
break;
|
||||
case PageState::FULL:
|
||||
case PageState::ACTIVE:
|
||||
nvsStats.used_entries += mUsedEntryCount;
|
||||
nvsStats.free_entries += ENTRY_COUNT - mUsedEntryCount; // it's equivalent free + erase entries.
|
||||
break;
|
||||
|
||||
case PageState::INVALID:
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
break;
|
||||
case PageState::INVALID:
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(false && "Unhandled state");
|
||||
break;
|
||||
default:
|
||||
assert(false && "Unhandled state");
|
||||
break;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
@ -1,28 +1,22 @@
|
||||
// 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;
|
||||
const uint8_t* p = reinterpret_cast<const uint8_t*>(this);
|
||||
result = esp_rom_crc32_le(result, p + offsetof(Item, nsIndex),
|
||||
offsetof(Item, crc32) - offsetof(Item, nsIndex));
|
||||
offsetof(Item, crc32) - offsetof(Item, nsIndex));
|
||||
result = esp_rom_crc32_le(result, p + offsetof(Item, key), sizeof(key));
|
||||
result = esp_rom_crc32_le(result, p + offsetof(Item, data), sizeof(data));
|
||||
return result;
|
||||
@ -33,7 +27,7 @@ uint32_t Item::calculateCrc32WithoutValue() const
|
||||
uint32_t result = 0xffffffff;
|
||||
const uint8_t* p = reinterpret_cast<const uint8_t*>(this);
|
||||
result = esp_rom_crc32_le(result, p + offsetof(Item, nsIndex),
|
||||
offsetof(Item, datatype) - offsetof(Item, nsIndex));
|
||||
offsetof(Item, datatype) - offsetof(Item, nsIndex));
|
||||
result = esp_rom_crc32_le(result, p + offsetof(Item, key), sizeof(key));
|
||||
result = esp_rom_crc32_le(result, p + offsetof(Item, chunkIndex), sizeof(chunkIndex));
|
||||
return result;
|
||||
@ -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