diff options
Diffstat (limited to 'net/disk_cache')
-rw-r--r-- | net/disk_cache/DIR_METADATA | 15 | ||||
-rw-r--r-- | net/disk_cache/backend_unittest.cc | 317 | ||||
-rw-r--r-- | net/disk_cache/blockfile/bitmap.h | 6 | ||||
-rw-r--r-- | net/disk_cache/blockfile/sparse_control.cc | 28 | ||||
-rw-r--r-- | net/disk_cache/blockfile/stats.cc | 6 | ||||
-rw-r--r-- | net/disk_cache/blockfile/storage_block-inl.h | 33 | ||||
-rw-r--r-- | net/disk_cache/blockfile/storage_block.h | 1 | ||||
-rw-r--r-- | net/disk_cache/disk_cache_fuzzer.cc | 2 | ||||
-rw-r--r-- | net/disk_cache/disk_cache_test_util.cc | 8 | ||||
-rw-r--r-- | net/disk_cache/disk_cache_test_util.h | 11 | ||||
-rw-r--r-- | net/disk_cache/entry_unittest.cc | 5 | ||||
-rw-r--r-- | net/disk_cache/simple/DIR_METADATA | 15 | ||||
-rw-r--r-- | net/disk_cache/simple/simple_backend_impl.cc | 6 | ||||
-rw-r--r-- | net/disk_cache/simple/simple_backend_impl.h | 1 | ||||
-rw-r--r-- | net/disk_cache/simple/simple_entry_impl.cc | 56 | ||||
-rw-r--r-- | net/disk_cache/simple/simple_entry_impl.h | 28 | ||||
-rw-r--r-- | net/disk_cache/simple/simple_synchronous_entry.h | 6 | ||||
-rw-r--r-- | net/disk_cache/simple/simple_util.cc | 14 |
18 files changed, 349 insertions, 209 deletions
diff --git a/net/disk_cache/DIR_METADATA b/net/disk_cache/DIR_METADATA index 65a7d593c..ab24ecc66 100644 --- a/net/disk_cache/DIR_METADATA +++ b/net/disk_cache/DIR_METADATA @@ -1,11 +1,6 @@ -# Metadata information for this directory. -# -# For more information on DIR_METADATA files, see: -# https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/README.md -# -# For the schema of this file, see Metadata message: -# https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/proto/dir_metadata.proto - -monorail { +monorail: { component: "Internals>Network>Cache" -}
\ No newline at end of file +} +buganizer_public: { + component_id: 1456578 +} diff --git a/net/disk_cache/backend_unittest.cc b/net/disk_cache/backend_unittest.cc index 5ff45407e..53b43ceb3 100644 --- a/net/disk_cache/backend_unittest.cc +++ b/net/disk_cache/backend_unittest.cc @@ -5518,105 +5518,120 @@ TEST_F(DiskCacheBackendTest, SimpleDoomIter) { run_loop.Run(); } -// See https://crbug.com/1486958 +// See https://crbug.com/1486958 for non-corrupting version, +// https://crbug.com/1510452 for corrupting one. TEST_F(DiskCacheBackendTest, SimpleOpenIter) { constexpr int kEntries = 50; SetSimpleCacheMode(); - // Note: this test relies on InitCache() making sure the index is ready. - InitCache(); - // We create a whole bunch of entries so that deleting them will hopefully - // finish after the iteration, in order to reproduce timing for the bug. - for (int i = 0; i < kEntries; ++i) { - disk_cache::Entry* entry = nullptr; - ASSERT_THAT(CreateEntry(base::NumberToString(i), &entry), IsOk()); - entry->Close(); - } - RunUntilIdle(); // Make sure close completes. - EXPECT_EQ(kEntries, cache_->GetEntryCount()); + for (bool do_corrupt : {false, true}) { + SCOPED_TRACE(do_corrupt); - // Iterate once to get the order. - base::queue<std::string> keys; - auto iterator = cache_->CreateIterator(); - base::RunLoop run_loop; - base::RepeatingCallback<void(EntryResult)> collect_entry_key = - base::BindLambdaForTesting([&](disk_cache::EntryResult result) { - if (result.net_error() == net::ERR_FAILED) { - run_loop.Quit(); - return; // iteration complete. - } - ASSERT_EQ(result.net_error(), net::OK); - disk_cache::Entry* entry = result.ReleaseEntry(); - keys.push(entry->GetKey()); - entry->Close(); - result = iterator->OpenNextEntry(collect_entry_key); - EXPECT_EQ(result.net_error(), net::ERR_IO_PENDING); - }); - - disk_cache::EntryResult result = iterator->OpenNextEntry(collect_entry_key); - ASSERT_EQ(result.net_error(), net::ERR_IO_PENDING); - run_loop.Run(); + // Note: this test relies on InitCache() making sure the index is ready. + InitCache(); - // Open all entries with iterator... - int opened = 0; - int iter_opened = 0; - bool iter_done = false; - auto all_done = [&]() { return opened == kEntries && iter_done; }; + // We create a whole bunch of entries so that deleting them will hopefully + // finish after the iteration, in order to reproduce timing for the bug. + for (int i = 0; i < kEntries; ++i) { + disk_cache::Entry* entry = nullptr; + ASSERT_THAT(CreateEntry(base::NumberToString(i), &entry), IsOk()); + entry->Close(); + } + RunUntilIdle(); // Make sure close completes. + EXPECT_EQ(kEntries, cache_->GetEntryCount()); + + // Iterate once to get the order. + std::list<std::string> keys; + auto iterator = cache_->CreateIterator(); + base::RunLoop run_loop; + base::RepeatingCallback<void(EntryResult)> collect_entry_key = + base::BindLambdaForTesting([&](disk_cache::EntryResult result) { + if (result.net_error() == net::ERR_FAILED) { + run_loop.Quit(); + return; // iteration complete. + } + ASSERT_EQ(result.net_error(), net::OK); + disk_cache::Entry* entry = result.ReleaseEntry(); + keys.push_back(entry->GetKey()); + entry->Close(); + result = iterator->OpenNextEntry(collect_entry_key); + EXPECT_EQ(result.net_error(), net::ERR_IO_PENDING); + }); + + disk_cache::EntryResult result = iterator->OpenNextEntry(collect_entry_key); + ASSERT_EQ(result.net_error(), net::ERR_IO_PENDING); + run_loop.Run(); + + // Corrupt all the files, if we're exercising that. + if (do_corrupt) { + for (const auto& key : keys) { + EXPECT_TRUE(disk_cache::simple_util::CreateCorruptFileForTests( + key, cache_path_)); + } + } - iterator = cache_->CreateIterator(); - base::RunLoop run_loop2; - base::RepeatingCallback<void(EntryResult)> handle_entry = - base::BindLambdaForTesting([&](disk_cache::EntryResult result) { - ++iter_opened; - if (result.net_error() == net::ERR_FAILED) { - EXPECT_EQ(iter_opened - 1, kEntries); - iter_done = true; + // Open all entries with iterator... + int opened = 0; + int iter_opened = 0; + bool iter_done = false; + auto all_done = [&]() { return opened == kEntries && iter_done; }; + + iterator = cache_->CreateIterator(); + base::RunLoop run_loop2; + base::RepeatingCallback<void(EntryResult)> handle_entry = + base::BindLambdaForTesting([&](disk_cache::EntryResult result) { + ++iter_opened; + if (result.net_error() == net::ERR_FAILED) { + EXPECT_EQ(iter_opened - 1, do_corrupt ? 0 : kEntries); + iter_done = true; + if (all_done()) { + run_loop2.Quit(); + } + return; // iteration complete. + } + EXPECT_EQ(result.net_error(), net::OK); + result = iterator->OpenNextEntry(handle_entry); + EXPECT_EQ(result.net_error(), net::ERR_IO_PENDING); + }); + + result = iterator->OpenNextEntry(handle_entry); + ASSERT_EQ(result.net_error(), net::ERR_IO_PENDING); + + // ... while simultaneously opening them via name. + auto handle_open_result = + base::BindLambdaForTesting([&](disk_cache::EntryResult result) { + int expected_status = do_corrupt ? net::ERR_FAILED : net::OK; + if (result.net_error() == expected_status) { + ++opened; + } if (all_done()) { run_loop2.Quit(); } - return; // iteration complete. - } - EXPECT_EQ(result.net_error(), net::OK); - result = iterator->OpenNextEntry(handle_entry); - EXPECT_EQ(result.net_error(), net::ERR_IO_PENDING); - }); - - result = iterator->OpenNextEntry(handle_entry); - ASSERT_EQ(result.net_error(), net::ERR_IO_PENDING); - - // ... while simultaneously opening them via name. - auto handle_open_result = - base::BindLambdaForTesting([&](disk_cache::EntryResult result) { - if (result.net_error() == net::OK) { - ++opened; - } - if (all_done()) { - run_loop2.Quit(); - } - }); - - base::RepeatingClosure open_one_entry = base::BindLambdaForTesting([&]() { - std::string key = keys.front(); - keys.pop(); - disk_cache::EntryResult result = - cache_->OpenEntry(key, net::DEFAULT_PRIORITY, handle_open_result); - if (result.net_error() != net::ERR_IO_PENDING) { - handle_open_result.Run(std::move(result)); - } - - if (!keys.empty()) { - base::SequencedTaskRunner::GetCurrentDefault()->PostTask(FROM_HERE, - open_one_entry); - } - }); - base::SequencedTaskRunner::GetCurrentDefault()->PostTask(FROM_HERE, - open_one_entry); - - run_loop2.Run(); - - // Should not have eaten any entries. - EXPECT_EQ(kEntries, cache_->GetEntryCount()); + }); + + base::RepeatingClosure open_one_entry = base::BindLambdaForTesting([&]() { + std::string key = keys.front(); + keys.pop_front(); + disk_cache::EntryResult result = + cache_->OpenEntry(key, net::DEFAULT_PRIORITY, handle_open_result); + if (result.net_error() != net::ERR_IO_PENDING) { + handle_open_result.Run(std::move(result)); + } + + if (!keys.empty()) { + base::SequencedTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, open_one_entry); + } + }); + base::SequencedTaskRunner::GetCurrentDefault()->PostTask(FROM_HERE, + open_one_entry); + + run_loop2.Run(); + + // Should not have eaten any entries, if not corrupting them. + EXPECT_EQ(do_corrupt ? 0 : kEntries, cache_->GetEntryCount()); + } } // Make sure that if we close an entry in callback from open/create we do not @@ -5636,3 +5651,129 @@ TEST_F(DiskCacheBackendTest, BlockFileImmediateCloseNoDangle) { EXPECT_EQ(result.net_error(), net::ERR_IO_PENDING); run_loop.Run(); } + +// Test that when a write causes a doom, it doesn't result in wrong delivery +// order of callbacks due to re-entrant operation execution. +TEST_F(DiskCacheBackendTest, SimpleWriteOrderEviction) { + SetSimpleCacheMode(); + SetMaxSize(4096); + InitCache(); + + // Writes of [1, 2, ..., kMaxSize] are more than enough to trigger eviction, + // as (1 + 80)*80/2 * 2 = 6480 (last * 2 since two streams are written). + constexpr int kMaxSize = 80; + + scoped_refptr<net::IOBufferWithSize> buffer = + CacheTestCreateAndFillBuffer(kMaxSize, /*no_nulls=*/false); + + disk_cache::Entry* entry = nullptr; + ASSERT_THAT(CreateEntry("key", &entry), IsOk()); + ASSERT_TRUE(entry); + + bool expected_next_write_stream_1 = true; + int expected_next_write_size = 1; + int next_offset = 0; + base::RunLoop run_loop; + for (int size = 1; size <= kMaxSize; ++size) { + entry->WriteData(/*index=*/1, /*offset = */ next_offset, buffer.get(), + /*buf_len=*/size, + base::BindLambdaForTesting([&](int result) { + EXPECT_TRUE(expected_next_write_stream_1); + EXPECT_EQ(result, expected_next_write_size); + expected_next_write_stream_1 = false; + }), + /*truncate=*/true); + // Stream 0 writes are used here because unlike with stream 1 ones, + // WriteDataInternal can succeed and queue response callback immediately. + entry->WriteData(/*index=*/0, /*offset = */ next_offset, buffer.get(), + /*buf_len=*/size, + base::BindLambdaForTesting([&](int result) { + EXPECT_FALSE(expected_next_write_stream_1); + EXPECT_EQ(result, expected_next_write_size); + expected_next_write_stream_1 = true; + ++expected_next_write_size; + if (expected_next_write_size == (kMaxSize + 1)) { + run_loop.Quit(); + } + }), + /*truncate=*/true); + next_offset += size; + } + + entry->Close(); + run_loop.Run(); +} + +// Test that when a write causes a doom, it doesn't result in wrong delivery +// order of callbacks due to re-entrant operation execution. Variant that +// uses stream 0 ops only. +TEST_F(DiskCacheBackendTest, SimpleWriteOrderEvictionStream0) { + SetSimpleCacheMode(); + SetMaxSize(4096); + InitCache(); + + // Writes of [1, 2, ..., kMaxSize] are more than enough to trigger eviction, + // as (1 + 120)*120/2 = 7260. + constexpr int kMaxSize = 120; + + scoped_refptr<net::IOBufferWithSize> buffer = + CacheTestCreateAndFillBuffer(kMaxSize, /*no_nulls=*/false); + + disk_cache::Entry* entry = nullptr; + ASSERT_THAT(CreateEntry("key", &entry), IsOk()); + ASSERT_TRUE(entry); + + int expected_next_write_size = 1; + int next_offset = 0; + base::RunLoop run_loop; + for (int size = 1; size <= kMaxSize; ++size) { + // Stream 0 writes are used here because unlike with stream 1 ones, + // WriteDataInternal can succeed and queue response callback immediately. + entry->WriteData(/*index=*/0, /*offset = */ next_offset, buffer.get(), + /*buf_len=*/size, + base::BindLambdaForTesting([&](int result) { + EXPECT_EQ(result, expected_next_write_size); + ++expected_next_write_size; + if (expected_next_write_size == (kMaxSize + 1)) { + run_loop.Quit(); + } + }), + /*truncate=*/true); + next_offset += size; + } + + entry->Close(); + run_loop.Run(); +} + +// Test to make sure that if entry creation triggers eviction, a queued up +// close (possible with optimistic ops) doesn't run from within creation +// completion handler (which is indirectly detected as a dangling pointer). +TEST_F(DiskCacheBackendTest, SimpleNoCloseFromWithinCreate) { + SetSimpleCacheMode(); + SetMaxSize(4096); + InitCache(); + + // Make entries big enough to force their eviction. + constexpr int kDataSize = 4097; + + auto buffer = base::MakeRefCounted<net::IOBufferWithSize>(kDataSize); + CacheTestFillBuffer(buffer->data(), kDataSize, false); + + for (int i = 0; i < 100; ++i) { + std::string key = base::NumberToString(i); + EntryResult entry_result = + cache_->CreateEntry(key, net::HIGHEST, base::DoNothing()); + ASSERT_EQ(entry_result.net_error(), net::OK); + disk_cache::Entry* entry = entry_result.ReleaseEntry(); + // Doing stream 0 write to avoid need for thread round-trips for it to take + // effect if SimpleEntryImpl runs it. + entry->WriteData(/*index=*/0, /*offset = */ 0, buffer.get(), + /*buf_len=*/kDataSize, + base::BindLambdaForTesting( + [&](int result) { EXPECT_EQ(kDataSize, result); }), + /*truncate=*/true); + entry->Close(); + } + RunUntilIdle(); +} diff --git a/net/disk_cache/blockfile/bitmap.h b/net/disk_cache/blockfile/bitmap.h index 14fc503e3..4638aa2a5 100644 --- a/net/disk_cache/blockfile/bitmap.h +++ b/net/disk_cache/blockfile/bitmap.h @@ -10,6 +10,7 @@ #include <memory> +#include "base/containers/span.h" #include "base/memory/raw_ptr.h" #include "net/base/net_export.h" @@ -75,6 +76,11 @@ class NET_EXPORT_PRIVATE Bitmap { // Gets a pointer to the internal map. const uint32_t* GetMap() const { return map_; } + // Gets a span describing the internal map. + base::span<const uint32_t> GetSpan() const { + return base::make_span(GetMap(), static_cast<size_t>(ArraySize())); + } + // Sets a range of bits to |value|. void SetRange(int begin, int end, bool value); diff --git a/net/disk_cache/blockfile/sparse_control.cc b/net/disk_cache/blockfile/sparse_control.cc index 8341de259..877521f64 100644 --- a/net/disk_cache/blockfile/sparse_control.cc +++ b/net/disk_cache/blockfile/sparse_control.cc @@ -412,7 +412,7 @@ int SparseControl::CreateSparseEntry() { // Save the header. The bitmap is saved in the destructor. scoped_refptr<net::IOBuffer> buf = base::MakeRefCounted<net::WrappedIOBuffer>( - reinterpret_cast<char*>(&sparse_header_), sizeof(sparse_header_)); + base::as_chars(base::make_span(&sparse_header_, 1u))); int rv = entry_->WriteData(kSparseIndex, 0, buf.get(), sizeof(sparse_header_), CompletionOnceCallback(), false); @@ -442,7 +442,7 @@ int SparseControl::OpenSparseEntry(int data_len) { return net::ERR_CACHE_OPERATION_NOT_SUPPORTED; scoped_refptr<net::IOBuffer> buf = base::MakeRefCounted<net::WrappedIOBuffer>( - reinterpret_cast<char*>(&sparse_header_), sizeof(sparse_header_)); + base::as_chars(base::span(&sparse_header_, 1u))); // Read header. int rv = entry_->ReadData(kSparseIndex, 0, buf.get(), sizeof(sparse_header_), @@ -496,9 +496,8 @@ bool SparseControl::OpenChild() { child_->GetDataSize(kSparseIndex) < static_cast<int>(sizeof(child_data_))) return KillChildAndContinue(key, false); - scoped_refptr<net::WrappedIOBuffer> buf = - base::MakeRefCounted<net::WrappedIOBuffer>( - reinterpret_cast<char*>(&child_data_), sizeof(child_data_)); + auto buf = base::MakeRefCounted<net::WrappedIOBuffer>( + base::as_chars(base::make_span(&child_data_, 1u))); // Read signature. int rv = child_->ReadData(kSparseIndex, 0, buf.get(), sizeof(child_data_), @@ -521,9 +520,8 @@ bool SparseControl::OpenChild() { } void SparseControl::CloseChild() { - scoped_refptr<net::WrappedIOBuffer> buf = - base::MakeRefCounted<net::WrappedIOBuffer>( - reinterpret_cast<char*>(&child_data_), sizeof(child_data_)); + auto buf = base::MakeRefCounted<net::WrappedIOBuffer>( + base::as_chars(base::make_span(&child_data_, 1u))); // Save the allocation bitmap before closing the child entry. int rv = child_->WriteData(kSparseIndex, 0, buf.get(), sizeof(child_data_), @@ -590,13 +588,12 @@ void SparseControl::SetChildBit(bool value) { } void SparseControl::WriteSparseData() { - int len = children_map_.ArraySize() * 4; - scoped_refptr<net::IOBuffer> buf = base::MakeRefCounted<net::WrappedIOBuffer>( - reinterpret_cast<const char*>(children_map_.GetMap()), len); + auto buf = base::MakeRefCounted<net::WrappedIOBuffer>( + base::as_chars(children_map_.GetSpan())); int rv = entry_->WriteData(kSparseIndex, sizeof(sparse_header_), buf.get(), - len, CompletionOnceCallback(), false); - if (rv != len) { + buf->size(), CompletionOnceCallback(), false); + if (rv != buf->size()) { DLOG(ERROR) << "Unable to save sparse map"; } } @@ -688,9 +685,8 @@ void SparseControl::InitChildData() { memset(&child_data_, 0, sizeof(child_data_)); child_data_.header = sparse_header_; - scoped_refptr<net::WrappedIOBuffer> buf = - base::MakeRefCounted<net::WrappedIOBuffer>( - reinterpret_cast<char*>(&child_data_), sizeof(child_data_)); + auto buf = base::MakeRefCounted<net::WrappedIOBuffer>( + base::as_chars(base::make_span(&child_data_, 1u))); int rv = child_->WriteData(kSparseIndex, 0, buf.get(), sizeof(child_data_), CompletionOnceCallback(), false); diff --git a/net/disk_cache/blockfile/stats.cc b/net/disk_cache/blockfile/stats.cc index 912d46090..faaab657b 100644 --- a/net/disk_cache/blockfile/stats.cc +++ b/net/disk_cache/blockfile/stats.cc @@ -4,7 +4,9 @@ #include "net/disk_cache/blockfile/stats.h" -#include "base/bits.h" +#include <bit> +#include <cstdint> + #include "base/check.h" #include "base/format_macros.h" #include "base/strings/string_util.h" @@ -261,7 +263,7 @@ int Stats::GetStatsBucket(int32_t size) { return (size - 20 * 1024) / 4096 + 11; // From this point on, use a logarithmic scale. - int result = base::bits::Log2Floor(size) + 1; + int result = std::bit_width<uint32_t>(size); static_assert(kDataSizesLength > 16, "update the scale"); if (result >= kDataSizesLength) diff --git a/net/disk_cache/blockfile/storage_block-inl.h b/net/disk_cache/blockfile/storage_block-inl.h index cbb810cd8..161b81fa7 100644 --- a/net/disk_cache/blockfile/storage_block-inl.h +++ b/net/disk_cache/blockfile/storage_block-inl.h @@ -10,6 +10,8 @@ #include <stddef.h> #include <stdint.h> +#include <type_traits> + #include "base/hash/hash.h" #include "base/logging.h" #include "base/notreached.h" @@ -19,9 +21,9 @@ namespace disk_cache { template <typename T> StorageBlock<T>::StorageBlock(MappedFile* file, Addr address) : file_(file), address_(address) { - if (address.num_blocks() > 1) - extended_ = true; - DCHECK(!address.is_initialized() || sizeof(*data_) == address.BlockSize()) + static_assert(std::is_trivial_v<T>); // T is loaded as bytes from a file. + DCHECK_NE(address.num_blocks(), 0); + DCHECK(!address.is_initialized() || sizeof(T) == address.BlockSize()) << address.value(); } @@ -37,7 +39,6 @@ void StorageBlock<T>::CopyFrom(StorageBlock<T>* other) { DCHECK(!other->modified_); Discard(); address_ = other->address_; - extended_ = other->extended_; file_ = other->file_; *Data() = *other->Data(); } @@ -47,9 +48,7 @@ template<typename T> void* StorageBlock<T>::buffer() const { } template<typename T> size_t StorageBlock<T>::size() const { - if (!extended_) - return sizeof(*data_); - return address_.num_blocks() * sizeof(*data_); + return address_.num_blocks() * sizeof(T); } template<typename T> int StorageBlock<T>::offset() const { @@ -64,10 +63,7 @@ template<typename T> bool StorageBlock<T>::LazyInit(MappedFile* file, } file_ = file; address_.set_value(address.value()); - if (address.num_blocks() > 1) - extended_ = true; - - DCHECK(sizeof(*data_) == address.BlockSize()); + DCHECK(sizeof(T) == address.BlockSize()); return true; } @@ -87,7 +83,6 @@ template<typename T> void StorageBlock<T>::Discard() { DeleteData(); data_ = nullptr; modified_ = false; - extended_ = false; } template<typename T> void StorageBlock<T>::StopSharingData() { @@ -185,23 +180,13 @@ template<typename T> bool StorageBlock<T>::Store(FileIOCallback* callback, template<typename T> void StorageBlock<T>::AllocateData() { DCHECK(!data_); - if (!extended_) { - data_ = new T; - } else { - void* buffer = new char[address_.num_blocks() * sizeof(*data_)]; - data_ = new(buffer) T; - } + data_ = new T[address_.num_blocks()]; own_data_ = true; } template<typename T> void StorageBlock<T>::DeleteData() { if (own_data_) { - if (!extended_) { - data_.ClearAndDelete(); - } else { - data_->~T(); - delete[] reinterpret_cast<char*>(data_.ExtractAsDangling().get()); - } + data_.ClearAndDeleteArray(); own_data_ = false; } } diff --git a/net/disk_cache/blockfile/storage_block.h b/net/disk_cache/blockfile/storage_block.h index e17b1733a..1ccb23a73 100644 --- a/net/disk_cache/blockfile/storage_block.h +++ b/net/disk_cache/blockfile/storage_block.h @@ -102,7 +102,6 @@ class StorageBlock : public FileBlock { bool modified_ = false; // Is data_ owned by this object or shared with someone else. bool own_data_ = false; - bool extended_ = false; // Used to store an entry of more than one block. }; } // namespace disk_cache diff --git a/net/disk_cache/disk_cache_fuzzer.cc b/net/disk_cache/disk_cache_fuzzer.cc index a0dcbdf55..0f2155aec 100644 --- a/net/disk_cache/disk_cache_fuzzer.cc +++ b/net/disk_cache/disk_cache_fuzzer.cc @@ -105,7 +105,7 @@ struct InitGlobals { // Disable noisy logging as per "libFuzzer in Chrome" documentation: // testing/libfuzzer/getting_started.md#Disable-noisy-error-message-logging. - logging::SetMinLogLevel(logging::LOG_FATAL); + logging::SetMinLogLevel(logging::LOGGING_FATAL); // Re-using this buffer for write operations may technically be against // IOBuffer rules but it shouldn't cause any actual problems. diff --git a/net/disk_cache/disk_cache_test_util.cc b/net/disk_cache/disk_cache_test_util.cc index c358160a0..7957874d8 100644 --- a/net/disk_cache/disk_cache_test_util.cc +++ b/net/disk_cache/disk_cache_test_util.cc @@ -41,6 +41,14 @@ void CacheTestFillBuffer(char* buffer, size_t len, bool no_nulls) { buffer[0] = 'g'; } +scoped_refptr<net::IOBufferWithSize> CacheTestCreateAndFillBuffer( + size_t len, + bool no_nulls) { + auto buffer = base::MakeRefCounted<net::IOBufferWithSize>(len); + CacheTestFillBuffer(buffer->data(), len, no_nulls); + return buffer; +} + bool CreateCacheTestFile(const base::FilePath& name) { int flags = base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_READ | base::File::FLAG_WRITE; diff --git a/net/disk_cache/disk_cache_test_util.h b/net/disk_cache/disk_cache_test_util.h index e4f7db938..d4228cb43 100644 --- a/net/disk_cache/disk_cache_test_util.h +++ b/net/disk_cache/disk_cache_test_util.h @@ -12,12 +12,17 @@ #include "base/files/file_path.h" #include "base/memory/raw_ptr.h" +#include "base/memory/scoped_refptr.h" #include "base/run_loop.h" #include "base/timer/timer.h" #include "build/build_config.h" #include "net/base/test_completion_callback.h" #include "net/disk_cache/disk_cache.h" +namespace net { +class IOBufferWithSize; +} // namespace net + // Re-creates a given test file inside the cache test folder. bool CreateCacheTestFile(const base::FilePath& name); @@ -27,6 +32,12 @@ bool DeleteCache(const base::FilePath& path); // Fills buffer with random values (may contain nulls unless no_nulls is true). void CacheTestFillBuffer(char* buffer, size_t len, bool no_nulls); +// Creates a buffer of size `len`, and fills in with random values, which +// may contain 0 unless `no_nulls` is true. +scoped_refptr<net::IOBufferWithSize> CacheTestCreateAndFillBuffer( + size_t len, + bool no_nulls); + // Generates a random key of up to 200 bytes. std::string GenerateKey(bool same_length); diff --git a/net/disk_cache/entry_unittest.cc b/net/disk_cache/entry_unittest.cc index 9a56851af..c8a4832e9 100644 --- a/net/disk_cache/entry_unittest.cc +++ b/net/disk_cache/entry_unittest.cc @@ -2042,9 +2042,8 @@ TEST_F(DiskCacheEntryTest, MemoryOnlyMisalignedSparseIO) { // This loop writes back to back starting from offset 0 and 9000. for (int i = 0; i < kSize; i += 1024) { - scoped_refptr<net::WrappedIOBuffer> buf_3 = - base::MakeRefCounted<net::WrappedIOBuffer>(buf_1->data() + i, - kSize - i); + auto buf_3 = + base::MakeRefCounted<net::WrappedIOBuffer>(buf_1->span().subspan(i)); VerifySparseIO(entry, i, buf_3.get(), 1024, buf_2.get()); VerifySparseIO(entry, 9000 + i, buf_3.get(), 1024, buf_2.get()); } diff --git a/net/disk_cache/simple/DIR_METADATA b/net/disk_cache/simple/DIR_METADATA index 8004f8fa1..8722368bf 100644 --- a/net/disk_cache/simple/DIR_METADATA +++ b/net/disk_cache/simple/DIR_METADATA @@ -1,11 +1,6 @@ -# Metadata information for this directory. -# -# For more information on DIR_METADATA files, see: -# https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/README.md -# -# For the schema of this file, see Metadata message: -# https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/proto/dir_metadata.proto - -monorail { +monorail: { component: "Internals>Network>Cache>Simple" -}
\ No newline at end of file +} +buganizer_public: { + component_id: 1456938 +} diff --git a/net/disk_cache/simple/simple_backend_impl.cc b/net/disk_cache/simple/simple_backend_impl.cc index c31d50380..172694a8a 100644 --- a/net/disk_cache/simple/simple_backend_impl.cc +++ b/net/disk_cache/simple/simple_backend_impl.cc @@ -864,9 +864,8 @@ EntryResult SimpleBackendImpl::OpenEntryFromHash(uint64_t entry_hash, simple_entry->SetActiveEntryProxy( ActiveEntryProxy::Create(entry_hash, this)); post_open_by_hash_waiting_->OnOperationStart(entry_hash); - callback = - base::BindOnce(&SimpleBackendImpl::OnEntryOpenedFromHash, AsWeakPtr(), - entry_hash, simple_entry, std::move(callback)); + callback = base::BindOnce(&SimpleBackendImpl::OnEntryOpenedFromHash, + AsWeakPtr(), entry_hash, std::move(callback)); } // Note: the !did_insert case includes when another OpenEntryFromHash is @@ -904,7 +903,6 @@ net::Error SimpleBackendImpl::DoomEntryFromHash( void SimpleBackendImpl::OnEntryOpenedFromHash( uint64_t hash, - const scoped_refptr<SimpleEntryImpl>& simple_entry, EntryResultCallback callback, EntryResult result) { post_open_by_hash_waiting_->OnOperationComplete(hash); diff --git a/net/disk_cache/simple/simple_backend_impl.h b/net/disk_cache/simple/simple_backend_impl.h index 41e0a36a0..7032253ab 100644 --- a/net/disk_cache/simple/simple_backend_impl.h +++ b/net/disk_cache/simple/simple_backend_impl.h @@ -244,7 +244,6 @@ class NET_EXPORT_PRIVATE SimpleBackendImpl : public Backend, // hash alone - this resumes operations that were waiting on the key // information to have been loaded. void OnEntryOpenedFromHash(uint64_t hash, - const scoped_refptr<SimpleEntryImpl>& simple_entry, EntryResultCallback callback, EntryResult result); diff --git a/net/disk_cache/simple/simple_entry_impl.cc b/net/disk_cache/simple/simple_entry_impl.cc index 13e312d27..41ce42f3f 100644 --- a/net/disk_cache/simple/simple_entry_impl.cc +++ b/net/disk_cache/simple/simple_entry_impl.cc @@ -455,8 +455,12 @@ int SimpleEntryImpl::WriteData(int stream_index, // Stream 0 data is kept in memory, so can be written immediatly if there are // no IO operations pending. if (stream_index == 0 && state_ == STATE_READY && - pending_operations_.size() == 0) - return SetStream0Data(buf, offset, buf_len, truncate); + pending_operations_.size() == 0) { + state_ = STATE_IO_PENDING; + SetStream0Data(buf, offset, buf_len, truncate); + state_ = STATE_READY; + return buf_len; + } // We can only do optimistic Write if there is no pending operations, so // that we are sure that the next call to RunNextOperationIfNeeded will @@ -1012,16 +1016,20 @@ int SimpleEntryImpl::ReadDataInternal(bool sync_possible, // Since stream 0 data is kept in memory, it is read immediately. if (stream_index == 0) { - int rv = ReadFromBuffer(stream_0_data_.get(), offset, buf_len, buf); - return PostToCallbackIfNeeded(sync_possible, std::move(callback), rv); + state_ = STATE_IO_PENDING; + ReadFromBuffer(stream_0_data_.get(), offset, buf_len, buf); + state_ = STATE_READY; + return PostToCallbackIfNeeded(sync_possible, std::move(callback), buf_len); } // Sometimes we can read in-ram prefetched stream 1 data immediately, too. if (stream_index == 1) { if (stream_1_prefetch_data_) { - int rv = - ReadFromBuffer(stream_1_prefetch_data_.get(), offset, buf_len, buf); - return PostToCallbackIfNeeded(sync_possible, std::move(callback), rv); + state_ = STATE_IO_PENDING; + ReadFromBuffer(stream_1_prefetch_data_.get(), offset, buf_len, buf); + state_ = STATE_READY; + return PostToCallbackIfNeeded(sync_possible, std::move(callback), + buf_len); } } @@ -1090,10 +1098,12 @@ void SimpleEntryImpl::WriteDataInternal(int stream_index, // Since stream 0 data is kept in memory, it will be written immediatly. if (stream_index == 0) { - int ret_value = SetStream0Data(buf, offset, buf_len, truncate); + state_ = STATE_IO_PENDING; + SetStream0Data(buf, offset, buf_len, truncate); + state_ = STATE_READY; if (!callback.is_null()) { base::SequencedTaskRunner::GetCurrentDefault()->PostTask( - FROM_HERE, base::BindOnce(std::move(callback), ret_value)); + FROM_HERE, base::BindOnce(std::move(callback), buf_len)); } return; } @@ -1407,7 +1417,6 @@ void SimpleEntryImpl::CreationOperationComplete( if (backend_ && doom_state_ == DOOM_NONE) backend_->index()->Insert(entry_hash_); - state_ = STATE_READY; synchronous_entry_ = in_results->sync_entry; // Copy over any pre-fetched data and its CRCs. @@ -1459,6 +1468,8 @@ void SimpleEntryImpl::CreationOperationComplete( // ultimately release `in_results->sync_entry`, and thus leading to having a // dangling pointer here. in_results = nullptr; + + state_ = STATE_READY; if (result_state == SimpleEntryOperation::ENTRY_NEEDS_CALLBACK) { ReturnEntryToCallerAsync(!created, std::move(completion_callback)); } @@ -1474,8 +1485,8 @@ void SimpleEntryImpl::UpdateStateAfterOperationComplete( state_ = STATE_FAILURE; MarkAsDoomed(DOOM_COMPLETED); } else { - state_ = STATE_READY; UpdateDataFromEntryStat(entry_stat); + state_ = STATE_READY; } } @@ -1637,7 +1648,10 @@ void SimpleEntryImpl::UpdateDataFromEntryStat( const SimpleEntryStat& entry_stat) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(synchronous_entry_); - DCHECK_EQ(STATE_READY, state_); + // We want to only be called in STATE_IO_PENDING so that if call to + // SimpleIndex::UpdateEntrySize() ends up triggering eviction and queuing + // Dooms it doesn't also run any queued operations. + CHECK_EQ(state_, STATE_IO_PENDING); last_used_ = entry_stat.last_used(); last_modified_ = entry_stat.last_modified(); @@ -1662,23 +1676,22 @@ int64_t SimpleEntryImpl::GetDiskUsage() const { return file_size; } -int SimpleEntryImpl::ReadFromBuffer(net::GrowableIOBuffer* in_buf, - int offset, - int buf_len, - net::IOBuffer* out_buf) { +void SimpleEntryImpl::ReadFromBuffer(net::GrowableIOBuffer* in_buf, + int offset, + int buf_len, + net::IOBuffer* out_buf) { DCHECK_GE(buf_len, 0); std::copy(in_buf->data() + offset, in_buf->data() + offset + buf_len, out_buf->data()); UpdateDataFromEntryStat(SimpleEntryStat(base::Time::Now(), last_modified_, data_size_, sparse_data_size_)); - return buf_len; } -int SimpleEntryImpl::SetStream0Data(net::IOBuffer* buf, - int offset, - int buf_len, - bool truncate) { +void SimpleEntryImpl::SetStream0Data(net::IOBuffer* buf, + int offset, + int buf_len, + bool truncate) { // Currently, stream 0 is only used for HTTP headers, and always writes them // with a single, truncating write. Detect these writes and record the size // changes of the headers. Also, support writes to stream 0 that have @@ -1717,7 +1730,6 @@ int SimpleEntryImpl::SetStream0Data(net::IOBuffer* buf, UpdateDataFromEntryStat( SimpleEntryStat(modification_time, modification_time, data_size_, sparse_data_size_)); - return buf_len; } } // namespace disk_cache diff --git a/net/disk_cache/simple/simple_entry_impl.h b/net/disk_cache/simple/simple_entry_impl.h index 5703935ab..c7816fdff 100644 --- a/net/disk_cache/simple/simple_entry_impl.h +++ b/net/disk_cache/simple/simple_entry_impl.h @@ -343,20 +343,21 @@ class NET_EXPORT_PRIVATE SimpleEntryImpl : public Entry, int64_t GetDiskUsage() const; // Completes a read from the stream data kept in memory, logging metrics - // and updating metadata. Returns the # of bytes read successfully. - // This asumes the caller has already range-checked offset and buf_len - // appropriately. - int ReadFromBuffer(net::GrowableIOBuffer* in_buf, - int offset, - int buf_len, - net::IOBuffer* out_buf); + // and updating metadata. This assumes the caller has already range-checked + // offset and buf_len appropriately, and therefore always reads `buf_len` + // bytes. + void ReadFromBuffer(net::GrowableIOBuffer* in_buf, + int offset, + int buf_len, + net::IOBuffer* out_buf); // Copies data from |buf| to the internal in-memory buffer for stream 0. If // |truncate| is set to true, the target buffer will be truncated at |offset| // + |buf_len| before being written. - int SetStream0Data(net::IOBuffer* buf, - int offset, int buf_len, - bool truncate); + void SetStream0Data(net::IOBuffer* buf, + int offset, + int buf_len, + bool truncate); // We want all async I/O on entries to complete before recycling the dir. scoped_refptr<BackendCleanupTracker> cleanup_tracker_; @@ -421,12 +422,7 @@ class NET_EXPORT_PRIVATE SimpleEntryImpl : public Entry, // an operation is being executed no one owns the synchronous entry. Therefore // SimpleEntryImpl should not be deleted while an operation is running as that // would leak the SimpleSynchronousEntry. - // This dangling raw_ptr occurred in: - // content_unittests: - // GeneratedCodeCacheTest/GeneratedCodeCacheTest.StressVeryLargeEntries/1 - // https://ci.chromium.org/ui/p/chromium/builders/try/linux-rel/1425125/test-results?q=ExactID%3Aninja%3A%2F%2Fcontent%2Ftest%3Acontent_unittests%2FGeneratedCodeCacheTest.StressVeryLargeEntries%2FGeneratedCodeCacheTest.1+VHash%3Ab3ba0803668e9981&sortby=&groupby= - raw_ptr<SimpleSynchronousEntry, FlakyDanglingUntriaged> synchronous_entry_ = - nullptr; + raw_ptr<SimpleSynchronousEntry> synchronous_entry_ = nullptr; scoped_refptr<net::PrioritizedTaskRunner> prioritized_task_runner_; diff --git a/net/disk_cache/simple/simple_synchronous_entry.h b/net/disk_cache/simple/simple_synchronous_entry.h index 112b5f8dd..0d24aa038 100644 --- a/net/disk_cache/simple/simple_synchronous_entry.h +++ b/net/disk_cache/simple/simple_synchronous_entry.h @@ -105,11 +105,7 @@ struct SimpleEntryCreationResults { explicit SimpleEntryCreationResults(SimpleEntryStat entry_stat); ~SimpleEntryCreationResults(); - // This dangling raw_ptr occurred in: - // content_unittests: - // GeneratedCodeCacheTest/GeneratedCodeCacheTest.StressVeryLargeEntries/1 - // https://ci.chromium.org/ui/p/chromium/builders/try/linux-rel/1425125/test-results?q=ExactID%3Aninja%3A%2F%2Fcontent%2Ftest%3Acontent_unittests%2FGeneratedCodeCacheTest.StressVeryLargeEntries%2FGeneratedCodeCacheTest.1+VHash%3Ab3ba0803668e9981&sortby=&groupby= - raw_ptr<SimpleSynchronousEntry, FlakyDanglingUntriaged> sync_entry; + raw_ptr<SimpleSynchronousEntry> sync_entry; // This is set when `sync_entry` is null. std::unique_ptr<UnboundBackendFileOperations> unbound_file_operations; diff --git a/net/disk_cache/simple/simple_util.cc b/net/disk_cache/simple/simple_util.cc index d823335b5..369412a2e 100644 --- a/net/disk_cache/simple/simple_util.cc +++ b/net/disk_cache/simple/simple_util.cc @@ -4,6 +4,8 @@ #include "net/disk_cache/simple/simple_util.h" +#include <string.h> + #include <limits> #include "base/check_op.h" @@ -49,13 +51,13 @@ bool GetEntryHashKeyFromHexString(base::StringPiece hash_key, } uint64_t GetEntryHashKey(const std::string& key) { - union { - unsigned char sha_hash[base::kSHA1Length]; - uint64_t key_hash; - } u; + unsigned char sha_hash[base::kSHA1Length]; + base::SHA1HashBytes(reinterpret_cast<const unsigned char*>(key.data()), - key.size(), u.sha_hash); - return u.key_hash; + key.size(), sha_hash); + uint64_t as_uint64; + memcpy(&as_uint64, sha_hash, sizeof(as_uint64)); + return as_uint64; } std::string GetFilenameFromEntryFileKeyAndFileIndex( |