Chrome · ServiceWorker
CVE-2026-79136
Logic Error in ServiceWorker
Overview
Medium
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
switchthird_party/blink/renderer/core/workers/shared_worker_content_settings_proxy.cc |
modified | |
GetServicethird_party/blink/renderer/core/workers/shared_worker_content_settings_proxy.cc |
modified | |
SharedWorkerContentSettingsProxythird_party/blink/renderer/core/workers/shared_worker_content_settings_proxy.h |
modified | |
CORE_EXPORTthird_party/blink/renderer/core/workers/shared_worker_content_settings_proxy.h |
modified |
Files Changed
content/browser/worker_host/shared_worker_content_settings_proxy_impl.ccthird_party/blink/public/mojom/worker/worker_content_settings_proxy.mojomthird_party/blink/renderer/core/workers/shared_worker_content_settings_proxy.ccthird_party/blink/renderer/core/workers/shared_worker_content_settings_proxy.h
Patch
From ae8261c0b4f7a8527e3565b6ee72f63413c14bc6 Mon Sep 17 00:00:00 2001
From: Yoshisto Yanagisawa <yyanagisawa@chromium.org>
Date: Sun, 16 Aug 2026 23:37:29 -0700
Subject: [PATCH] Implement async AllowStorageAccess in Service and Shared Worker content settings proxies
Previously, ServiceWorkerContentSettingsProxy and
SharedWorkerContentSettingsProxy only overrode the synchronous
AllowStorageAccessSync method from WebContentSettingsClient, relying on
the base default implementation for asynchronous queries. Additionally,
synchronous checks for CacheStorage and WebLocks were not routed through
the IPC proxy in ServiceWorkerContentSettingsProxy.
This CL implements the asynchronous AllowStorageAccess method in both
proxy classes to route IndexedDB, CacheStorage, and WebLocks queries to
the browser process via Mojo IPC. This ensures consistent and accurate
content settings evaluation across both synchronous and asynchronous
APIs. It also adds the missing synchronous IPC routings for CacheStorage
and WebLocks in ServiceWorkerContentSettingsProxy.
A unit test is added to verify that storage access queries in
ServiceWorkerContentSettingsProxy correctly propagate responses from
the browser process.
TAG=agy
CONV=afbe35fe-485c-4ef6-853f-1874ce409f0e
Bug: 503624894
Change-Id: I4d9e862a2b88c71f628afba61ab706d0180c1e4d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8062387
Reviewed-by: Hiroki Nakagawa <nhiroki@chromium.org>
Commit-Queue: Yoshisato Yanagisawa <yyanagisawa@chromium.org>
Reviewed-by: Hidehiko Abe <hidehiko@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1680367}
---
diff --git a/content/browser/worker_host/shared_worker_content_settings_proxy_impl.cc b/content/browser/worker_host/shared_worker_content_settings_proxy_impl.cc
index 3750cf8e..5e5c8b6 100644
--- a/content/browser/worker_host/shared_worker_content_settings_proxy_impl.cc
+++ b/content/browser/worker_host/shared_worker_content_settings_proxy_impl.cc
@@ -16,7 +16,7 @@
const GURL& script_url,
SharedWorkerHost* owner,
mojo::PendingReceiver<blink::mojom::WorkerContentSettingsProxy> receiver)
- : origin_(url::Origin::Create(script_url)),
+ : origin_(owner->GetWorkerStorageKey().origin()),
owner_(owner),
receiver_(this, std::move(receiver)) {}
diff --git a/third_party/blink/public/mojom/worker/worker_content_settings_proxy.mojom b/third_party/blink/public/mojom/worker/worker_content_settings_proxy.mojom
index 98899298..17a3e82 100644
--- a/third_party/blink/public/mojom/worker/worker_content_settings_proxy.mojom
+++ b/third_party/blink/public/mojom/worker/worker_content_settings_proxy.mojom
@@ -24,6 +24,7 @@
AllowWebLocks() => (bool result);
// Returns whether the worker is allowed access to the file system.
+ // TODO(crbug.com/503624894): Rename to AllowFileSystem in a follow-up.
[Sync]
RequestFileSystemAccessSync() => (bool result);
};
diff --git a/third_party/blink/renderer/core/workers/shared_worker_content_settings_proxy.cc b/third_party/blink/renderer/core/workers/shared_worker_content_settings_proxy.cc
index db16874..db8efa0 100644
--- a/third_party/blink/renderer/core/workers/shared_worker_content_settings_proxy.cc
+++ b/third_party/blink/renderer/core/workers/shared_worker_content_settings_proxy.cc
@@ -8,18 +8,53 @@
#include <utility>
#include "base/metrics/histogram_macros.h"
+#include "base/task/single_thread_task_runner.h"
#include "third_party/blink/public/mojom/worker/worker_content_settings_proxy.mojom-blink.h"
-#include "third_party/blink/renderer/platform/wtf/thread_specific.h"
+#include "third_party/blink/renderer/platform/wtf/functional.h"
namespace blink {
SharedWorkerContentSettingsProxy::SharedWorkerContentSettingsProxy(
mojo::PendingRemote<mojom::blink::WorkerContentSettingsProxy> host_info)
- : host_info_(std::move(host_info)) {}
-SharedWorkerContentSettingsProxy::~SharedWorkerContentSettingsProxy() = default;
+ : host_info_(std::move(host_info)) {
+ DETACH_FROM_THREAD(worker_thread_checker_);
+}
+
+SharedWorkerContentSettingsProxy::~SharedWorkerContentSettingsProxy() {
+ DCHECK_CALLED_ON_VALID_THREAD(worker_thread_checker_);
+}
+
+void SharedWorkerContentSettingsProxy::AllowStorageAccess(
+ StorageType storage_type,
+ base::OnceCallback<void(bool)> callback) {
+ DCHECK_CALLED_ON_VALID_THREAD(worker_thread_checker_);
+ switch (storage_type) {
+ case StorageType::kIndexedDB:
+ GetService()->AllowIndexedDB(std::move(callback));
+ return;
+ case StorageType::kCacheStorage:
+ GetService()->AllowCacheStorage(std::move(callback));
+ return;
+ case StorageType::kWebLocks:
+ GetService()->AllowWebLocks(std::move(callback));
+ return;
+ case StorageType::kFileSystem:
+ // TODO(crbug.com/503624894): Rename RequestFileSystemAccessSync to
+ // AllowFileSystem in a follow-up.
+ GetService()->RequestFileSystemAccessSync(std::move(callback));
+ return;
+ default:
+ // TODO(crbug.com/40103756): Revisit this default in the future.
+ base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
+ FROM_HERE, blink::BindOnce(std::move(callback), true));
+ return;
+ }
+}
bool SharedWorkerContentSettingsProxy::AllowStorageAccessSync(
StorageType storage_type) {
+ DCHECK_CALLED_ON_VALID_THREAD(worker_thread_checker_);
+ // TODO(crbug.com/503624894): Remove obsolete UMA histograms in a follow-up.
bool result = false;
switch (storage_type) {
case StorageType::kIndexedDB: {
@@ -43,7 +78,7 @@
break;
}
default: {
- // TODO(shuagga@microsoft.com): Revisit this default in the future.
+ // TODO(crbug.com/40103756): Revisit this default in the future.
return true;
}
}
@@ -51,19 +86,14 @@
return result;
}
-// Use ThreadSpecific to ensure that |content_settings_instance_host| is
-// destructed on worker thread.
-// Each worker has a dedicated thread so this is safe.
mojo::Remote<mojom::blink::WorkerContentSettingsProxy>&
SharedWorkerContentSettingsProxy::GetService() {
- DEFINE_THREAD_SAFE_STATIC_LOCAL(
- ThreadSpecific<mojo::Remote<mojom::blink::WorkerContentSettingsProxy>>,
- content_settings_instance_host, ());
- if (!content_settings_instance_host.IsSet()) {
+ DCHECK_CALLED_ON_VALID_THREAD(worker_thread_checker_);
+ if (!host_remote_.is_bound()) {
DCHECK(host_info_.is_valid());
- content_settings_instance_host->Bind(std::move(host_info_));
+ host_remote_.Bind(std::move(host_info_));
}
- return *content_settings_instance_host;
+ return host_remote_;
}
} // namespace blink
diff --git a/third_party/blink/renderer/core/workers/shared_worker_content_settings_proxy.h b/third_party/blink/renderer/core/workers/shared_worker_content_settings_proxy.h
index 90b8b53..9e0ad03 100644
--- a/third_party/blink/renderer/core/workers/shared_worker_content_settings_proxy.h
+++ b/third_party/blink/renderer/core/workers/shared_worker_content_settings_proxy.h
@@ -5,36 +5,42 @@
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_WORKERS_SHARED_WORKER_CONTENT_SETTINGS_PROXY_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_WORKERS_SHARED_WORKER_CONTENT_SETTINGS_PROXY_H_
+#include "base/threading/thread_checker.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/remote.h"
-#include "third_party/blink/public/mojom/worker/worker_content_settings_proxy.mojom-blink-forward.h"
+#include "third_party/blink/public/mojom/worker/worker_content_settings_proxy.mojom-blink.h"
#include "third_party/blink/public/platform/web_content_settings_client.h"
#include "third_party/blink/public/platform/web_security_origin.h"
+#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
namespace blink {
// SharedWorkerContentSettingsProxy provides content settings information.
-// This is created on the main thread and then called on the worker thread.
-// Information is requested via a Mojo connection to the browser process.
-class SharedWorkerContentSettingsProxy : public WebContentSettingsClient {
+// This is created on the main thread, passed to the worker thread via
+// GlobalScopeCreationParams, and then called and destroyed on the worker
+// thread. Information is requested via a Mojo connection to the browser
+// process.
+class CORE_EXPORT SharedWorkerContentSettingsProxy
+ : public WebContentSettingsClient {
public:
SharedWorkerContentSettingsProxy(
mojo::PendingRemote<mojom::blink::WorkerContentSettingsProxy> host_info);
~SharedWorkerContentSettingsProxy() override;
// WebContentSettingsClient overrides.
+ void AllowStorageAccess(StorageType storage_type,
+ base::OnceCallback<void(bool)> callback) override;
bool AllowStorageAccessSync(StorageType storage_type) override;
private:
- // To ensure the returned pointer is destructed on the same thread
- // that it was constructed on, this uses ThreadSpecific.
mojo::Remote<mojom::blink::WorkerContentSettingsProxy>& GetService();
- // This is set on the main thread at the ctor,
Loading diff…
Regression Test / PoC
shipped with the fix
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_content_settings_proxy_test.cc b/third_party/blink/renderer/modules/service_worker/service_worker_content_settings_proxy_test.cc
new file mode 100644
index 0000000..2b11c403
--- /dev/null
+++ b/third_party/blink/renderer/modules/service_worker/service_worker_content_settings_proxy_test.cc
@@ -0,0 +1,204 @@
+// Copyright 2026 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/modules/service_worker/service_worker_content_settings_proxy.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/functional/bind.h"
+#include "base/run_loop.h"
+#include "base/task/bind_post_task.h"
+#include "base/task/single_thread_task_runner.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/mojom/worker/worker_content_settings_proxy.mojom-blink.h"
+#include "third_party/blink/renderer/platform/scheduler/public/non_main_thread.h"
+#include "third_party/blink/renderer/platform/scheduler/public/thread_type.h"
+#include "third_party/blink/renderer/platform/testing/task_environment.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
+
+namespace blink {
+namespace {
+
+class MockWorkerContentSettingsProxy
+ : public mojom::blink::WorkerContentSettingsProxy {
+ public:
+ MockWorkerContentSettingsProxy() = default;
+ ~MockWorkerContentSettingsProxy() override = default;
+
+ mojo::PendingRemote<mojom::blink::WorkerContentSettingsProxy> CreateRemote() {
+ return receiver_.BindNewPipeAndPassRemote();
+ }
+
+ void set_allow_indexed_db(bool allow) { allow_indexed_db_ = allow; }
+ void set_allow_cache_storage(bool allow) { allow_cache_storage_ = allow; }
+ void set_allow_web_locks(bool allow) { allow_web_locks_ = allow; }
+
+ // mojom::blink::WorkerContentSettingsProxy implementation:
+ void AllowIndexedDB(AllowIndexedDBCallback callback) override {
+ std::move(callback).Run(allow_indexed_db_);
+ }
+ void AllowCacheStorage(AllowCacheStorageCallback callback) override {
+ std::move(callback).Run(allow_cache_storage_);
+ }
+ void AllowWebLocks(AllowWebLocksCallback callback) override {
+ std::move(callback).Run(allow_web_locks_);
+ }
+ void RequestFileSystemAccessSync(
+ RequestFileSystemAccessSyncCallback callback) override {
+ std::move(callback).Run(false);
+ }
+
+ private:
+ mojo::Receiver<mojom::blink::WorkerContentSettingsProxy> receiver_{this};
+ bool allow_indexed_db_ = true;
+ bool allow_cache_storage_ = true;
+ bool allow_web_locks_ = true;
+};
+
+// Tests for ServiceWorkerContentSettingsProxy.
+// The proxy calls ServiceWorkerContentSettingsProxy::GetService(), which stores
+// a mojo::Remote in ThreadSpecific storage. Running tests on a dedicated worker
+// thread ensures that ThreadSpecific state is cleanly destroyed when the thread
+// stops after each test, preventing state leakage across multiple tests.
+class ServiceWorkerContentSettingsProxyTest : public testing::Test {
+ protected:
+ void SetUp() override {
+ worker_thread_ = NonMainThread::CreateThread(
+ ThreadCreationParams(ThreadType::kTestThread)
+ .SetThreadNameForTest("worker_thread"));
+ mock_proxy_ = std::make_unique<MockWorkerContentSettingsProxy>();
+ proxy_ = std::make_unique<ServiceWorkerContentSettingsProxy>(
+ mock_proxy_->CreateRemote());
+ }
+
+ void TearDown() override {
+ // Delete the proxy on the worker thread, then stop the worker thread to
+ // ensure ThreadSpecific storage is cleaned up.
+ worker_thread_->GetTaskRunner()->PostTask(
+ FROM_HERE,
+ base::BindOnce(
+ [](std::unique_ptr<ServiceWorkerContentSettingsProxy>) {},
+ std::move(proxy_)));
+ worker_thread_.reset();
+ mock_proxy_.reset();
+ }
+
+ bool RunAllowStorageAccess(
+ WebContentSettingsClient::StorageType storage_type) {
+ bool result = false;
+ base::RunLoop loop;
+ worker_thread_->GetTaskRunner()->PostTask(
+ FROM_HERE,
+ base::BindOnce(
+ [](ServiceWorkerContentSettingsProxy* proxy,
+ WebContentSettingsClient::StorageType storage_type,
+ base::OnceCallback<void(bool)> callback) {
+ proxy->AllowStorageAccess(storage_type, std::move(callback));
+ },
+ proxy_.get(), storage_type,
+ base::BindPostTask(
+ base::SingleThreadTaskRunner::GetCurrentDefault(),
+ base::BindOnce(
+ [](bool* out_result, base::OnceClosure quit_closure,
+ bool allow) {
+ *out_result = allow;
+ std::move(quit_closure).Run();
+ },
+ &result, loop.QuitClosure()))));
+ loop.Run();
+ return result;
+ }
+
+ bool RunAllowStorageAccessSync(
+ WebContentSettingsClient::StorageType storage_type) {
+ bool result = false;
+ base::RunLoop loop;
+ worker_thread_->GetTaskRunner()->PostTask(
+ FROM_HERE, base::BindOnce(
+ [](ServiceWorkerContentSettingsProxy* proxy,
+ WebContentSettingsClient::StorageType storage_type,
+ base::OnceCallback<void(bool)> reply_callback) {
+ bool allowed =
+ proxy->AllowStorageAccessSync(storage_type);
+ std::move(reply_callback).Run(allowed);
+ },
+ proxy_.get(), storage_type,
+ base::BindPostTask(
+ base::SingleThreadTaskRunner::GetCurrentDefault(),
+ base::BindOnce(
+ [](bool* out_result,
+ base::OnceClosure quit_closure, bool allow) {
+ *out_result = allow;
+ std::move(quit_closure).Run();
+ },
+ &result, loop.QuitClosure()))));
+ loop.Run();
+ return result;
+ }
+
+ test::TaskEnvironment task_environment_;
+ std::unique_ptr<NonMainThread> worker_thread_;
+ std::unique_ptr<MockWorkerContentSettingsProxy> mock_proxy_;
+ std::unique_ptr<ServiceWorkerContentSettingsProxy> proxy_;
+};
+
+TEST_F(ServiceWorkerContentSettingsProxyTest, AllowIndexedDB) {
+ // Test IndexedDB async
+ mock_proxy_->set_allow_indexed_db(false);
+ EXPECT_FALSE(
+ RunAllowStorageAccess(WebContentSettingsClient::StorageType::kIndexedDB));
+ mock_proxy_->set_allow_indexed_db(true);
+ EXPECT_TRUE(
+ RunAllowStorageAccess(WebContentSettingsClient::StorageType::kIndexedDB));
+
+ // Test IndexedDB sync
+ mock_proxy_->set_allow_indexed_db(false);
+ EXPECT_FALSE(RunAllowStorageAccessSync(
+ WebContentSettingsClient::StorageType::kIndexedDB));
+ mock_proxy_->set_allow_indexed_db(true);
+ EXPECT_TRUE(RunAllowStorageAccessSync(
+ WebContentSettingsClient::StorageType::kIndexedDB));
+}
+
+TEST_F(ServiceWorkerContentSettingsProxyTest, AllowCacheStorage) {
+ // Test CacheStorage async
+ mock_proxy_->set_allow_cache_storage(false);
+ EXPECT_FALSE(RunAllowStorageAccess(
+ WebContentSettingsClient::StorageType::kCacheStorage));
+ mock_proxy_->set_allow_cache_storage(true);
+ EXPECT_TRUE(RunAllowStorageAccess(
+ WebContentSettingsClient::StorageType::kCacheStorage));
+
+ // Test CacheStorage sync
+ mock_proxy_->set_allow_cache_storage(false);
+ EXPECT_FALSE(RunAllowStorageAccessSync(
+ WebContentSettingsClient::StorageType::kCacheStorage));
+ mock_proxy_->set_allow_cache_storage(true);
+ EXPECT_TRUE(RunAllowStorageAccessSync(
+ WebContentSettingsClient::StorageType::kCacheStorage));
+}
+
+TEST_F(ServiceWorkerContentSettingsProxyTest, AllowWebLocks) {
+ // Test WebLocks async
+ mock_proxy_->set_allow_web_locks(false);
+ EXPECT_FALSE(
+ RunAllowStorageAccess(WebContentSettingsClient::StorageType::kWebLocks));
+ mock_proxy_->set_allow_web_locks(true);
+ EXPECT_TRUE(
+ RunAllowStorageAccess(WebContentSettingsClient::StorageType::kWebLocks));
+
+ // Test WebLocks sync
+ mock_proxy_->set_allow_web_locks(false);
+ EXPECT_FALSE(RunAllowStorageAccessSync(
+ WebContentSettingsClient::StorageType::kWebLocks));
+ mock_proxy_->set_allow_web_locks(true);
+ EXPECT_TRUE(RunAllowStorageAccessSync(
+ WebContentSettingsClient::StorageType::kWebLocks));
+}
+
+} // namespace
+} // namespace blink
diff --git a/third_party/blink/web_tests/external/wpt/IndexedDB/idbfactory_open.any.js b/third_party/blink/web_tests/external/wpt/IndexedDB/idbfactory_open.any.js
index aadfc5c..aed8f18 100644
--- a/third_party/blink/web_tests/external/wpt/IndexedDB/idbfactory_open.any.js
+++ b/third_party/blink/web_tests/external/wpt/IndexedDB/idbfactory_open.any.js
@@ -192,7 +192,7 @@
function should_work(val, expected_version) {
let name = format_value(val);
- let dbname = 'test-db-does-not-exist';
+ let dbname = 'test-db-does-not-exist-' + name;
async_test(function (t) {
indexedDB.deleteDatabase(dbname);
let rq = indexedDB.open(dbname, val);
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