Chrome · CacheStorage › CacheStorage
CVE-2026-119270
Input Validation in CacheStorage (CacheStorage)
Overview
High
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
TEST_Fcontent/browser/cache_storage/cache_storage_manager_unittest.cc |
modified |
Files Changed
content/browser/cache_storage/cache_storage.cccontent/browser/cache_storage/cache_storage_manager_unittest.cc
Patch
From ff5f2bcd571e33a322513ac72b841041e7e57687 Mon Sep 17 00:00:00 2001
From: Eriko Kurimoto <elkurin@chromium.org>
Date: Sun, 25 Oct 2026 20:10:40 -0700
Subject: [PATCH] [CacheStorage] Remove obsolete legacy cache_dir migration
`proto::CacheStorageIndex::Cache::cache_dir` was added in Chrome M48
(crrev.com/1414033002, October 2015), and its migration unit test was
removed in Chrome M56 (crrev.com/2407733004, October 2016).
Remove the obsolete `!cache.has_cache_dir()` migration branch in
`SimpleCacheLoader::ReadAndMigrateIndexInPool()` that moved legacy
SHA-1-named directories via `base::Move()`, and instead return an empty
`proto::CacheStorageIndex` so `CacheStorage` starts fresh when an index
entry is missing `cache_dir`. Also remove the now-unused `HexedHash()`
helper and dangling `MigratedLegacyCacheDirectoryNameTest` friend
declaration.
Bug: 577333729
Change-Id: I506c5635db3403c6548af07986ce50cd619ea7b3
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8650158
Auto-Submit: Eriko Kurimoto <elkurin@chromium.org>
Reviewed-by: Ming-Ying Chung <mych@chromium.org>
Commit-Queue: Ming-Ying Chung <mych@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1726719}
---
diff --git a/content/browser/cache_storage/cache_storage.cc b/content/browser/cache_storage/cache_storage.cc
index 718fae7..eca2205 100644
--- a/content/browser/cache_storage/cache_storage.cc
+++ b/content/browser/cache_storage/cache_storage.cc
@@ -22,14 +22,12 @@
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/functional/concurrent_closures.h"
-#include "base/hash/sha1.h"
#include "base/location.h"
#include "base/memory/ptr_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_refptr.h"
#include "base/numerics/safe_conversions.h"
-#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/string_view_util.h"
#include "base/strings/utf_string_conversions.h"
@@ -64,15 +62,6 @@
namespace content {
-namespace {
-
-std::string HexedHash(const std::string& value) {
- std::string value_hash = base::SHA1HashString(value);
- return base::HexEncodeLower(value_hash);
-}
-
-} // namespace
-
const char CacheStorage::kIndexFileName[] = "index.txt";
constexpr char16_t kReplacementCharacter = 0xFFFD;
@@ -533,7 +522,6 @@
}
private:
- friend class MigratedLegacyCacheDirectoryNameTest;
~SimpleCacheLoader() override = default;
// Iterates over the caches and deletes any directory not found in
@@ -577,50 +565,28 @@
index_last_modified = file_info.last_modified;
bool index_modified = false;
- // Look for caches that have no cache_dir. Give any such caches a directory
- // with a random name and move them there. Then, rewrite the index file.
- // Additionally invalidate the size of any index entries where the cache was
- // modified after the index (making it out-of-date). We'll assume that any
- // unmigrated index files predate the buckets integration and leave them in
- // the directory for first-party Cache Storage instances in the default
- // bucket.
+ // Invalidate the size of any index entries where the cache was modified
+ // after the index (making it out-of-date). We'll assume that any unmigrated
+ // index files predate the buckets integration and leave them in the
+ // directory for first-party Cache Storage instances in the default bucket.
for (int i = 0, max = index.cache_size(); i < max; ++i) {
const proto::CacheStorageIndex::Cache& cache = index.cache(i);
- if (cache.has_cache_dir()) {
- if (cache.has_size()) {
- base::FilePath cache_dir =
- directory_path.AppendASCII(cache.cache_dir());
- if (!GetFileInfo(cache_dir, &file_info) ||
- index_last_modified <= file_info.last_modified) {
- // Index is older than this cache, so invalidate index entries that
- // may change as a result of cache operations.
- index.mutable_cache(i)->clear_size();
- }
+ if (!cache.has_cache_dir()) {
+ // If `cache_dir` is missing, the index is in a bad state. Return an
+ // empty index so that the `CacheStorage` can start fresh. The
+ // unreferenced caches will be discarded later in initialization.
+ return proto::CacheStorageIndex();
+ }
+ if (cache.has_size()) {
+ base::FilePath cache_dir =
+ directory_path.AppendASCII(cache.cache_dir());
+ if (!GetFileInfo(cache_dir, &file_info) ||
+ index_last_modified <= file_info.last_modified) {
+ // Index is older than this cache, so invalidate index entries that
+ // may change as a result of cache operations.
+ index.mutable_cache(i)->clear_size();
}
- } else {
- // Find a new home for the caches that don't have a directory (legacy
- // caches) since they predate the change where u16string `cache_names`
- // were added.
- base::FilePath legacy_cache_path =
- directory_path.AppendASCII(HexedHash(cache.name()));
- std::string cache_dir;
- base::FilePath cache_path;
- do {
- cache_dir = base::Uuid::GenerateRandomV4().AsLowercaseString();
- cache_path = directory_path.AppendASCII(cache_dir);
- } while (base::PathExists(cache_path));
-
- if (!base::Move(legacy_cache_path, cache_path)) {
- // If the move fails then the cache is in a bad state. Return an empty
- // index so that the CacheStorage can start fresh. The unreferenced
- // caches will be discarded later in initialization.
- return proto::CacheStorageIndex();
- }
-
- index.mutable_cache(i)->set_cache_dir(cache_dir);
- index.mutable_cache(i)->clear_size();
- index_modified = true;
}
}
diff --git a/content/browser/cache_storage/cache_storage_manager_unittest.cc b/content/browser/cache_storage/cache_storage_manager_unittest.cc
index 9ef6005..520431f 100644
--- a/content/browser/cache_storage/cache_storage_manager_unittest.cc
+++ b/content/browser/cache_storage/cache_storage_manager_unittest.cc
@@ -1758,6 +1758,49 @@
EXPECT_EQ(kOpaqueResponsePadding, index.cache(0).padding());
}
+// Check that an index entry missing `cache_dir` is treated as invalid so
+// `CacheStorage` starts fresh rather than migrating a legacy directory name.
+TEST_F(CacheStorageManagerTest, IndexMissingCacheDirStartsFresh) {
+ const std::u16string kCacheName = u"foo";
+ const GURL kFooURL = storage_key1_.origin().GetURL().Resolve("foo");
+
+ ASSERT_TRUE(Open(bucket_locator1_, kCacheName));
+ ASSERT_TRUE(CachePut(callback_cache_handle_.value(), kFooURL));
+ const base::FilePath cache_path =
+ CacheStorageCache::From(callback_cache_handle_)->path();
+ const base::FilePath storage_dir = cache_path.DirName();
+ const base::FilePath index_path =
+ storage_dir.AppendASCII(CacheStorage::kIndexFileName);
+ ASSERT_TRUE(FlushCacheStorageIndex(bucket_locator1_));
+ DestroyStorageManager();
+ disk_cache::FlushCacheThreadForTesting();
+
+ // Move the cache directory to the legacy SHA-1 hex path for "foo" and clear
+ // `cache_dir` from the index entry.
+ const base::FilePath legacy_cache_path =
+ storage_dir.AppendASCII("0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33");
+ ASSERT_TRUE(base::Move(cache_path, legacy_cache_path));
+
+ std::string serialized_index;
+ proto::CacheStorageIndex index;
+ ASSERT_TRUE(base::ReadFileToString(index_path, &serialized_index));
+ ASSERT_TRUE(index.ParseFromString(serialized_index));
+ ASSERT_EQ(1, index.cache_size());
+ ASSERT_TRUE(index.cache(0).has_cache_dir());
+ index.mutable_cache(0)->clear_cache_dir();
+ ASSERT_TRUE(index.SerializeToString(&serialized_index));
+ ASSERT_TRUE(base::WriteFile(index_path, serialized_index));
+
+ // Reopen the manager and verify `CacheStorage` starts fresh and cleans up the
+ // unreferenced legacy directory.
+ CreateStorageManager();
+ EXPECT_EQ(0u, Keys(bucket_locator1_));
+ EXPECT_FALSE(Has(bucket_locator1_, kCacheName));
+ disk_cache::FlushCacheThreadForTesting();
+ EXPECT_TRUE(base::test::RunUntil(
+ [&]() { return !base::PathExists(legacy_cache_path); }));
+}
+
TEST_F(CacheStorageManagerTest, QuotaCorrectAfterReopen) {
const std::u16string kCacheName = u"foo";
Loading diff…
Original Bug Report
The reporter's bug is still restricted on the tracker. Chrome de-restricts security bugs ~30–90 days after the fix ships; a later run will backfill it here.
References
On This Page