summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorgavinp <gavinp@chromium.org>2016-02-25 09:20:36 -0800
committerCommit bot <commit-bot@chromium.org>2016-02-25 17:21:36 +0000
commitb7129635933594a6eb91e82e8139235e6a843a05 (patch)
tree7c78b4943fb743eed84d60924d221e978241e849
parentd53461d6e82d2052b9a50a06554ef99e1f910a7e (diff)
downloadchromium_src-b7129635933594a6eb91e82e8139235e6a843a05.zip
chromium_src-b7129635933594a6eb91e82e8139235e6a843a05.tar.gz
chromium_src-b7129635933594a6eb91e82e8139235e6a843a05.tar.bz2
Fix use after free in memory only backend.
Writing to a sparse entry could result that entry being evicted while the write was still in progress. This fix considers sparse entries in use if any of their children are in use; this can drive up memory use but avoids tickling the dragon by expanding the hand rolled refcounting implementation in the in memory cache. R=mmenke@chromium.org BUG=589186 Review URL: https://codereview.chromium.org/1725363005 Cr-Commit-Position: refs/heads/master@{#377598}
-rw-r--r--net/disk_cache/backend_unittest.cc38
-rw-r--r--net/disk_cache/memory/mem_entry_impl.cc12
2 files changed, 41 insertions, 9 deletions
diff --git a/net/disk_cache/backend_unittest.cc b/net/disk_cache/backend_unittest.cc
index ebd2f57..7f2259d 100644
--- a/net/disk_cache/backend_unittest.cc
+++ b/net/disk_cache/backend_unittest.cc
@@ -2947,6 +2947,44 @@ TEST_F(DiskCacheBackendTest, MemoryOnlyBackendEviction) {
// TODO(gavinp): Enable BackendEviction test for simple cache after performance
// problems are addressed. See crbug.com/588184 for more information.
+// This overly specific looking test is a regression test aimed at
+// crbug.com/589186.
+TEST_F(DiskCacheBackendTest, MemoryOnlyUseAfterFree) {
+ SetMemoryOnlyMode();
+
+ const int kMaxSize = 200 * 1024;
+ const int kMaxEntryCount = 20;
+ const int kWriteSize = kMaxSize / kMaxEntryCount;
+
+ SetMaxSize(kMaxSize);
+ InitCache();
+
+ scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kWriteSize));
+ CacheTestFillBuffer(buffer->data(), kWriteSize, false);
+
+ // Create an entry to be our sparse entry that gets written later.
+ disk_cache::Entry* entry;
+ ASSERT_EQ(net::OK, CreateEntry("first parent", &entry));
+ disk_cache::ScopedEntryPtr first_parent(entry);
+
+ // Create a ton of entries, and keep them open, to put the cache well above
+ // its eviction threshhold.
+ const int kTooManyEntriesCount = kMaxEntryCount * 2;
+ std::list<disk_cache::ScopedEntryPtr> open_entries;
+ std::string key_prefix("prefix");
+ for (int i = 0; i < kTooManyEntriesCount; ++i) {
+ ASSERT_EQ(net::OK, CreateEntry(key_prefix + base::IntToString(i), &entry));
+ EXPECT_EQ(kWriteSize,
+ WriteData(entry, 1, 0, buffer.get(), kWriteSize, false));
+ open_entries.push_back(disk_cache::ScopedEntryPtr(entry));
+ }
+ EXPECT_LT(kMaxSize, CalculateSizeOfAllEntries());
+
+ // Writing this sparse data should not crash.
+ EXPECT_EQ(1024, first_parent->WriteSparseData(32768, buffer.get(), 1024,
+ net::CompletionCallback()));
+}
+
TEST_F(DiskCacheTest, Backend_UsageStatsTimer) {
MessageLoopHelper helper;
diff --git a/net/disk_cache/memory/mem_entry_impl.cc b/net/disk_cache/memory/mem_entry_impl.cc
index c28a7fa..bcbebb3 100644
--- a/net/disk_cache/memory/mem_entry_impl.cc
+++ b/net/disk_cache/memory/mem_entry_impl.cc
@@ -103,16 +103,10 @@ void MemEntryImpl::Open() {
}
bool MemEntryImpl::InUse() const {
- if (type() == PARENT_ENTRY) {
- return ref_count_ > 0;
- } else {
- // TODO(gavinp): Can't this just be a DCHECK? How would ref_count_ not be
- // zero?
+ if (type() == CHILD_ENTRY)
+ return parent_->InUse();
- // A child entry is never in use. Thus one can always be evicted, even while
- // its parent entry is open and in use.
- return false;
- }
+ return ref_count_ > 0;
}
int MemEntryImpl::GetStorageSize() const {