VersionCountingBlockStore is initialized with a flag, saying whether missing blocks are integrity errors. This flag is meant to be activated in a single-client scenario to have this additional integrity check.

This commit is contained in:
Sebastian Messmer 2016-06-25 15:28:16 -07:00
parent e02021ecdc
commit c84a230afd
6 changed files with 63 additions and 7 deletions

View File

@ -195,5 +195,15 @@ void KnownBlockVersions::markBlockAsDeleted(const Key &key) {
_lastUpdateClientId[key] = CLIENT_ID_FOR_DELETED_BLOCK;
}
bool KnownBlockVersions::blockShouldExist(const Key &key) const {
auto found = _lastUpdateClientId.find(key);
if (found == _lastUpdateClientId.end()) {
// We've never seen (i.e. loaded) this block. So we can't say it has to exist.
return false;
}
// We've seen the block before. If we didn't delete it, it should exist (only works for single-client scenario).
return found->second != CLIENT_ID_FOR_DELETED_BLOCK;
}
}
}

View File

@ -27,6 +27,8 @@ namespace blockstore {
void markBlockAsDeleted(const Key &key);
bool blockShouldExist(const Key &key) const;
uint64_t getBlockVersion(uint32_t clientId, const Key &key) const;
uint32_t myClientId() const;

View File

@ -14,7 +14,7 @@ namespace versioncounting {
class VersionCountingBlockStore final: public BlockStore {
public:
VersionCountingBlockStore(cpputils::unique_ref<BlockStore> baseBlockStore, const boost::filesystem::path &integrityFilePath);
VersionCountingBlockStore(cpputils::unique_ref<BlockStore> baseBlockStore, const boost::filesystem::path &integrityFilePath, bool missingBlockIsIntegrityViolation);
Key createKey() override;
boost::optional<cpputils::unique_ref<Block>> tryCreate(const Key &key, cpputils::Data data) override;
@ -32,13 +32,14 @@ public:
private:
cpputils::unique_ref<BlockStore> _baseBlockStore;
KnownBlockVersions _knownBlockVersions;
const bool _missingBlockIsIntegrityViolation;
DISALLOW_COPY_AND_ASSIGN(VersionCountingBlockStore);
};
inline VersionCountingBlockStore::VersionCountingBlockStore(cpputils::unique_ref<BlockStore> baseBlockStore, const boost::filesystem::path &integrityFilePath)
: _baseBlockStore(std::move(baseBlockStore)), _knownBlockVersions(integrityFilePath) {
inline VersionCountingBlockStore::VersionCountingBlockStore(cpputils::unique_ref<BlockStore> baseBlockStore, const boost::filesystem::path &integrityFilePath, bool missingBlockIsIntegrityViolation)
: _baseBlockStore(std::move(baseBlockStore)), _knownBlockVersions(integrityFilePath), _missingBlockIsIntegrityViolation(missingBlockIsIntegrityViolation) {
}
inline Key VersionCountingBlockStore::createKey() {
@ -57,6 +58,9 @@ inline boost::optional<cpputils::unique_ref<Block>> VersionCountingBlockStore::t
inline boost::optional<cpputils::unique_ref<Block>> VersionCountingBlockStore::load(const Key &key) {
auto block = _baseBlockStore->load(key);
if (block == boost::none) {
if (_missingBlockIsIntegrityViolation && _knownBlockVersions.blockShouldExist(key)) {
throw IntegrityViolationError("A block that should exist wasn't found. Did an attacker delete it?");
}
return boost::none;
}
return boost::optional<cpputils::unique_ref<Block>>(VersionCountingBlock::Load(std::move(*block), &_knownBlockVersions));

View File

@ -89,7 +89,7 @@ unique_ref<BlockStore> CryDevice::CreateVersionCountingEncryptedBlockStore(uniqu
}
#endif
return make_unique_ref<VersionCountingBlockStore>(std::move(encryptedBlockStore), integrityFilePath);
return make_unique_ref<VersionCountingBlockStore>(std::move(encryptedBlockStore), integrityFilePath, false);
}
Key CryDevice::CreateRootBlobAndReturnKey() {

View File

@ -17,14 +17,16 @@ using cpputils::make_unique_ref;
using cpputils::unique_ref;
using cpputils::TempFile;
template<bool SINGLECLIENT>
class VersionCountingBlockStoreTestFixture: public BlockStoreTestFixture {
public:
VersionCountingBlockStoreTestFixture() :stateFile(false) {}
TempFile stateFile;
unique_ref<BlockStore> createBlockStore() override {
return make_unique_ref<VersionCountingBlockStore>(make_unique_ref<FakeBlockStore>(), stateFile.path());
return make_unique_ref<VersionCountingBlockStore>(make_unique_ref<FakeBlockStore>(), stateFile.path(), SINGLECLIENT);
}
};
INSTANTIATE_TYPED_TEST_CASE_P(VersionCounting, BlockStoreTest, VersionCountingBlockStoreTestFixture);
INSTANTIATE_TYPED_TEST_CASE_P(VersionCounting_multiclient, BlockStoreTest, VersionCountingBlockStoreTestFixture<false>);
INSTANTIATE_TYPED_TEST_CASE_P(VersionCounting_singleclient, BlockStoreTest, VersionCountingBlockStoreTestFixture<true>);

View File

@ -13,6 +13,8 @@ using cpputils::unique_ref;
using cpputils::make_unique_ref;
using cpputils::TempFile;
using boost::none;
using std::make_unique;
using std::unique_ptr;
using blockstore::testfake::FakeBlockStore;
@ -24,7 +26,7 @@ public:
VersionCountingBlockStoreTest():
stateFile(false),
baseBlockStore(new FakeBlockStore),
blockStore(make_unique_ref<VersionCountingBlockStore>(std::move(cpputils::nullcheck(std::unique_ptr<FakeBlockStore>(baseBlockStore)).value()), stateFile.path())),
blockStore(make_unique_ref<VersionCountingBlockStore>(std::move(cpputils::nullcheck(std::unique_ptr<FakeBlockStore>(baseBlockStore)).value()), stateFile.path(), false)),
data(DataFixture::generate(BLOCKSIZE)) {
}
TempFile stateFile;
@ -32,6 +34,18 @@ public:
unique_ref<VersionCountingBlockStore> blockStore;
Data data;
std::pair<FakeBlockStore *, unique_ptr<VersionCountingBlockStore>> makeBlockStoreWithDeletionPrevention() {
FakeBlockStore *baseBlockStore = new FakeBlockStore;
auto blockStore = make_unique<VersionCountingBlockStore>(std::move(cpputils::nullcheck(std::unique_ptr<FakeBlockStore>(baseBlockStore)).value()), stateFile.path(), true);
return std::make_pair(baseBlockStore, std::move(blockStore));
}
std::pair<FakeBlockStore *, unique_ptr<VersionCountingBlockStore>> makeBlockStoreWithoutDeletionPrevention() {
FakeBlockStore *baseBlockStore = new FakeBlockStore;
auto blockStore = make_unique<VersionCountingBlockStore>(std::move(cpputils::nullcheck(std::unique_ptr<FakeBlockStore>(baseBlockStore)).value()), stateFile.path(), false);
return std::make_pair(baseBlockStore, std::move(blockStore));
}
blockstore::Key CreateBlockReturnKey() {
return CreateBlockReturnKey(data);
}
@ -174,9 +188,33 @@ TEST_F(VersionCountingBlockStoreTest, RollbackPrevention_AllowsReintroducingDele
EXPECT_NE(boost::none, blockStore->load(key));
}
// Check that in a multi-client scenario, missing blocks are not integrity errors, because another client might have deleted them.
TEST_F(VersionCountingBlockStoreTest, DeletionPrevention_AllowsDeletingBlocksWhenDeactivated) {
FakeBlockStore *baseBlockStore;
unique_ptr<VersionCountingBlockStore> blockStore;
std::tie(baseBlockStore, blockStore) = makeBlockStoreWithoutDeletionPrevention();
auto key = blockStore->create(Data(0))->key();
baseBlockStore->remove(baseBlockStore->load(key).value());
EXPECT_EQ(boost::none, blockStore->load(key));
}
// Check that in a single-client scenario, missing blocks are integrity errors.
TEST_F(VersionCountingBlockStoreTest, DeletionPrevention_DoesntAllowDeletingBlocksWhenActivated) {
FakeBlockStore *baseBlockStore;
unique_ptr<VersionCountingBlockStore> blockStore;
std::tie(baseBlockStore, blockStore) = makeBlockStoreWithDeletionPrevention();
auto key = blockStore->create(Data(0))->key();
baseBlockStore->remove(baseBlockStore->load(key).value());
EXPECT_THROW(
blockStore->load(key),
IntegrityViolationError
);
}
// TODO Test more integrity cases:
// - RollbackPrevention_DoesntAllowReintroducingDeletedBlocks with different client id (i.e. trying to re-introduce the newest block of a different client)
// - RollbackPrevention_AllowsReintroducingDeletedBlocksWithNewVersionNumber with different client id
// - Think about more...
TEST_F(VersionCountingBlockStoreTest, PhysicalBlockSize_zerophysical) {
EXPECT_EQ(0u, blockStore->blockSizeFromPhysicalBlockSize(0));