Commit 992ab88f authored by Maks Orlovich's avatar Maks Orlovich Committed by Chromium LUCI CQ

Blockfile cache: avoid LRU corruption from oosync in-mem aliases

In some case there are aliases to a node being inserted into a rankings
chain already in-memory (e.g. due to an iterator active while an entry
is being moved between different eviction list), but Ranking::Insert
failed to update them, making things confused. Fix that.

Bug: 1156288
Change-Id: I1a1bd5fb7aedf5ee6e45071606825b266e5e3b9c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2639218Reviewed-by: default avatarJosh Karlin <jkarlin@chromium.org>
Commit-Queue: Maksim Orlovich <morlovich@chromium.org>
Cr-Commit-Position: refs/heads/master@{#846408}
parent a4699cad
...@@ -5333,3 +5333,76 @@ TEST_F(DiskCacheBackendTest, BlockFileDelayedWriteFailureRecovery) { ...@@ -5333,3 +5333,76 @@ TEST_F(DiskCacheBackendTest, BlockFileDelayedWriteFailureRecovery) {
entry->Close(); entry->Close();
} }
TEST_F(DiskCacheBackendTest, BlockFileInsertAliasing) {
// Test for not having rankings corruption due to aliasing between iterator
// and other ranking list copies during insertion operations.
//
// https://crbug.com/1156288
// Need to disable weird extra sync behavior to hit the bug.
CreateBackend(disk_cache::kNone);
SetNewEviction(); // default, but integrity check doesn't realize that.
const char kKey[] = "Key0";
const char kKeyA[] = "KeyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA41";
disk_cache::Entry* entry = nullptr;
ASSERT_THAT(CreateEntry(kKey, &entry), IsOk());
const int kBufSize = 61188;
scoped_refptr<net::IOBuffer> buffer =
base::MakeRefCounted<net::IOBuffer>(kBufSize);
CacheTestFillBuffer(buffer->data(), kBufSize, true);
net::TestCompletionCallback cb_write64;
EXPECT_EQ(net::ERR_IO_PENDING,
entry->WriteSparseData(8, buffer.get(), 64, cb_write64.callback()));
net::TestCompletionCallback cb_write61k;
EXPECT_EQ(net::ERR_IO_PENDING,
entry->WriteSparseData(16773118, buffer.get(), 61188,
cb_write61k.callback()));
EXPECT_EQ(64, cb_write64.WaitForResult());
EXPECT_EQ(61188, cb_write61k.WaitForResult());
EXPECT_EQ(4128, WriteSparseData(entry, 2147479550, buffer.get(), 4128));
std::unique_ptr<TestIterator> iter = CreateIterator();
EXPECT_EQ(4128, WriteSparseData(entry, 2147479550, buffer.get(), 4128));
EXPECT_EQ(64, WriteSparseData(entry, 8, buffer.get(), 64));
disk_cache::Entry* itEntry1 = nullptr;
ASSERT_EQ(net::OK, iter->OpenNextEntry(&itEntry1));
// These are actually child nodes for range.
entry->Close();
disk_cache::Entry* itEntry2 = nullptr;
ASSERT_EQ(net::OK, iter->OpenNextEntry(&itEntry2));
net::TestCompletionCallback doom_cb;
EXPECT_EQ(net::ERR_IO_PENDING, cache_->DoomAllEntries(doom_cb.callback()));
TestEntryResultCompletionCallback cb_create1;
disk_cache::EntryResult result =
cache_->CreateEntry(kKey, net::HIGHEST, cb_create1.callback());
EXPECT_EQ(net::OK, doom_cb.WaitForResult());
result = cb_create1.WaitForResult();
EXPECT_EQ(net::OK, result.net_error());
entry = result.ReleaseEntry();
disk_cache::Entry* entryA = nullptr;
ASSERT_THAT(CreateEntry(kKeyA, &entryA), IsOk());
entryA->Close();
disk_cache::Entry* itEntry3 = nullptr;
EXPECT_EQ(net::OK, iter->OpenNextEntry(&itEntry3));
EXPECT_EQ(net::OK, DoomEntry(kKeyA));
itEntry1->Close();
entry->Close();
itEntry2->Close();
if (itEntry3)
itEntry3->Close();
}
...@@ -280,6 +280,8 @@ void Rankings::Insert(CacheRankingsBlock* node, bool modified, List list) { ...@@ -280,6 +280,8 @@ void Rankings::Insert(CacheRankingsBlock* node, bool modified, List list) {
UpdateTimes(node, modified); UpdateTimes(node, modified);
node->Store(); node->Store();
// Make sure other aliased in-memory copies get synchronized.
UpdateIterators(node);
GenerateCrash(ON_INSERT_3); GenerateCrash(ON_INSERT_3);
// The last thing to do is move our head to point to a node already stored. // The last thing to do is move our head to point to a node already stored.
...@@ -871,7 +873,8 @@ void Rankings::UpdateIterators(CacheRankingsBlock* node) { ...@@ -871,7 +873,8 @@ void Rankings::UpdateIterators(CacheRankingsBlock* node) {
for (auto it = iterators_.begin(); it != iterators_.end(); ++it) { for (auto it = iterators_.begin(); it != iterators_.end(); ++it) {
if (it->first == address && it->second->HasData()) { if (it->first == address && it->second->HasData()) {
CacheRankingsBlock* other = it->second; CacheRankingsBlock* other = it->second;
*other->Data() = *node->Data(); if (other != node)
*other->Data() = *node->Data();
} }
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment