Chrome · Scheduling
CVE-2026-17956
Logic Error in Scheduling
Overview
Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
TEST_Fthird_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc |
modified | |
TEST_Fthird_party/blink/renderer/platform/scheduler/worker/worker_scheduler_impl_unittest.cc |
modified |
Files Changed
third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.ccthird_party/blink/renderer/platform/scheduler/worker/worker_scheduler_impl.ccthird_party/blink/renderer/platform/scheduler/worker/worker_scheduler_impl_unittest.cc
Patch
From 6c4922974ad2d71f2c139464ea35ce8b5715e9ca Mon Sep 17 00:00:00 2001 From: Scott Haseley <[email protected]> Date: Fri, 26 Jun 2026 12:33:49 -0700 Subject: [PATCH] [scheduler] Fix Worker QueueEnabledVoter init for web scheduling queues This CL ensures web scheduling queues that are created while the worker scheduler is paused are initially paused. This is only an issue if self.scheduler APIs are called while paused, which typically should not happen while paused (since JS should be running). But there could be races with web locks task while paused in devtools, for example, that could cause this. Also, the same hooks for aborting script or pausing microtasks for BFCache aren't present on Workers, so this at least ensures tasks scheduled via self.scheduler won't continue to run. Note: this was already handled on the main thread, and this CL adds a unit test for that case as well. Fixed: 517436171 Change-Id: I6456e234cfbdf08e7f175368fb089e020b767bac Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8007687 Reviewed-by: Etienne Pierre-Doray <[email protected]> Commit-Queue: Scott Haseley <[email protected]> Cr-Commit-Position: refs/heads/main@{#1653366} --- diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc index 5d751c96..808b6f2 100644 --- a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc +++ b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc @@ -895,6 +895,13 @@ UnpausableTaskQueue()->GetTaskRunnerWithDefaultTaskType()->PostTask( FROM_HERE, base::BindOnce(&IncrementCounter, base::Unretained(&counter))); + std::unique_ptr<WebSchedulingTaskQueue> web_scheduling_task_queue = + frame_scheduler_->CreateWebSchedulingTaskQueue( + WebSchedulingQueueType::kTaskQueue, + WebSchedulingPriority::kUserVisiblePriority); + web_scheduling_task_queue->GetTaskRunner()->PostTask( + FROM_HERE, base::BindOnce(&IncrementCounter, base::Unretained(&counter))); + frame_scheduler_->SetPaused(true); EXPECT_EQ(0, counter); @@ -905,7 +912,30 @@ EXPECT_EQ(1, counter); base::RunLoop().RunUntilIdle(); - EXPECT_EQ(5, counter); + EXPECT_EQ(6, counter); +} + +TEST_F(FrameSchedulerImplTest, PauseBeforeWebSchedulingQueueCreated) { + int counter = 0; + + frame_scheduler_->SetPaused(true); + + std::unique_ptr<WebSchedulingTaskQueue> web_scheduling_task_queue = + frame_scheduler_->CreateWebSchedulingTaskQueue( + WebSchedulingQueueType::kTaskQueue, + WebSchedulingPriority::kUserVisiblePriority); + web_scheduling_task_queue->GetTaskRunner()->PostTask( + FROM_HERE, base::BindOnce(&IncrementCounter, base::Unretained(&counter))); + + EXPECT_EQ(0, counter); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(0, counter); + + frame_scheduler_->SetPaused(false); + + EXPECT_EQ(0, counter); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(1, counter); } namespace { diff --git a/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler_impl.cc b/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler_impl.cc index a676152..0d51b5a6 100644 --- a/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler_impl.cc +++ b/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler_impl.cc @@ -361,13 +361,22 @@ WorkerSchedulerImpl::CreateWebSchedulingTaskQueue( WebSchedulingQueueType queue_type, WebSchedulingPriority priority) { + // These queues are created from JavaScript, and JS should not be running + // after the worker is disposed. + CHECK(!is_disposed_); + scoped_refptr<NonMainThreadTaskQueue> task_queue = thread_scheduler_->CreateTaskQueue( base::sequence_manager::QueueName::WORKER_WEB_SCHEDULING_TQ, NonMainThreadTaskQueue::QueueCreationParams() .SetWebSchedulingQueueType(queue_type) .SetWebSchedulingPriority(priority)); - task_runners_.insert(task_queue, task_queue->CreateQueueEnabledVoter()); + std::unique_ptr<base::sequence_manager::TaskQueue::QueueEnabledVoter> voter = + task_queue->CreateQueueEnabledVoter(); + // TODO(crbug.com/528232589): Consider plumbing the initial enabled state to + // the QueueEnabledVoter constructor. + voter->SetVoteToEnable(paused_count_ == 0); + task_runners_.insert(task_queue, std::move(voter)); return std::make_unique<NonMainThreadWebSchedulingTaskQueueImpl>( GetWeakPtr(), std::move(task_queue)); } diff --git a/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler_impl_unittest.cc b/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler_impl_unittest.cc index 5b675db3..4b30cd1 100644 --- a/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler_impl_unittest.cc +++ b/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler_impl_unittest.cc @@ -692,7 +692,38 @@ worker_scheduler_.reset(); } -TEST_F(WorkerSchedulerImplTest, WebSchedulerTaskQueueDestruction) { +TEST_F(NonMainThreadWebSchedulingTaskQueueTest, + WebSchedulingTaskQueuesAreInitializedBasedOnPauseState) { + int counter = 0; + + std::unique_ptr<WebSchedulingTaskQueue> queue = + worker_scheduler_->CreateWebSchedulingTaskQueue( + WebSchedulingQueueType::kTaskQueue, + WebSchedulingPriority::kUserVisiblePriority); + { + std::unique_ptr<WorkerScheduler::PauseHandle> pause_handle( + worker_scheduler_->Pause()); + + queue->GetTaskRunner()->PostTask( + FROM_HERE, + base::BindOnce(&IncrementCounter, base::Unretained(&counter))); + RunUntilIdle(); + EXPECT_EQ(counter, 0); + } + + RunUntilIdle(); + EXPECT_EQ(counter, 1); + + queue = worker_scheduler_->CreateWebSchedulingTaskQueue( + WebSchedulingQueueType::kTaskQueue, + WebSchedulingPriority::kUserVisiblePriority); + queue->GetTaskRunner()->PostTask( + FROM_HERE, base::BindOnce(&IncrementCounter, base::Unretained(&counter))); + RunUntilIdle(); + EXPECT_EQ(counter, 2); +} + +TEST_F(WorkerSchedulerImplTest, WebSchedulingTaskQueueDestruction) { // This just makes sure that destroying queues before and after disposal // doesn't trigger any CHECKs or other issues. std::unique_ptr<WebSchedulingTaskQueue> queue1 =
Loading diff…
Regression Test / PoC
shipped with the fix
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc
index 5d751c96..808b6f2 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc
@@ -895,6 +895,13 @@
UnpausableTaskQueue()->GetTaskRunnerWithDefaultTaskType()->PostTask(
FROM_HERE, base::BindOnce(&IncrementCounter, base::Unretained(&counter)));
+ std::unique_ptr<WebSchedulingTaskQueue> web_scheduling_task_queue =
+ frame_scheduler_->CreateWebSchedulingTaskQueue(
+ WebSchedulingQueueType::kTaskQueue,
+ WebSchedulingPriority::kUserVisiblePriority);
+ web_scheduling_task_queue->GetTaskRunner()->PostTask(
+ FROM_HERE, base::BindOnce(&IncrementCounter, base::Unretained(&counter)));
+
frame_scheduler_->SetPaused(true);
EXPECT_EQ(0, counter);
@@ -905,7 +912,30 @@
EXPECT_EQ(1, counter);
base::RunLoop().RunUntilIdle();
- EXPECT_EQ(5, counter);
+ EXPECT_EQ(6, counter);
+}
+
+TEST_F(FrameSchedulerImplTest, PauseBeforeWebSchedulingQueueCreated) {
+ int counter = 0;
+
+ frame_scheduler_->SetPaused(true);
+
+ std::unique_ptr<WebSchedulingTaskQueue> web_scheduling_task_queue =
+ frame_scheduler_->CreateWebSchedulingTaskQueue(
+ WebSchedulingQueueType::kTaskQueue,
+ WebSchedulingPriority::kUserVisiblePriority);
+ web_scheduling_task_queue->GetTaskRunner()->PostTask(
+ FROM_HERE, base::BindOnce(&IncrementCounter, base::Unretained(&counter)));
+
+ EXPECT_EQ(0, counter);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(0, counter);
+
+ frame_scheduler_->SetPaused(false);
+
+ EXPECT_EQ(0, counter);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(1, counter);
}
namespace {
diff --git a/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler_impl_unittest.cc b/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler_impl_unittest.cc
index 5b675db3..4b30cd1 100644
--- a/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler_impl_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler_impl_unittest.cc
@@ -692,7 +692,38 @@
worker_scheduler_.reset();
}
-TEST_F(WorkerSchedulerImplTest, WebSchedulerTaskQueueDestruction) {
+TEST_F(NonMainThreadWebSchedulingTaskQueueTest,
+ WebSchedulingTaskQueuesAreInitializedBasedOnPauseState) {
+ int counter = 0;
+
+ std::unique_ptr<WebSchedulingTaskQueue> queue =
+ worker_scheduler_->CreateWebSchedulingTaskQueue(
+ WebSchedulingQueueType::kTaskQueue,
+ WebSchedulingPriority::kUserVisiblePriority);
+ {
+ std::unique_ptr<WorkerScheduler::PauseHandle> pause_handle(
+ worker_scheduler_->Pause());
+
+ queue->GetTaskRunner()->PostTask(
+ FROM_HERE,
+ base::BindOnce(&IncrementCounter, base::Unretained(&counter)));
+ RunUntilIdle();
+ EXPECT_EQ(counter, 0);
+ }
+
+ RunUntilIdle();
+ EXPECT_EQ(counter, 1);
+
+ queue = worker_scheduler_->CreateWebSchedulingTaskQueue(
+ WebSchedulingQueueType::kTaskQueue,
+ WebSchedulingPriority::kUserVisiblePriority);
+ queue->GetTaskRunner()->PostTask(
+ FROM_HERE, base::BindOnce(&IncrementCounter, base::Unretained(&counter)));
+ RunUntilIdle();
+ EXPECT_EQ(counter, 2);
+}
+
+TEST_F(WorkerSchedulerImplTest, WebSchedulingTaskQueueDestruction) {
// This just makes sure that destroying queues before and after disposal
// doesn't trigger any CHECKs or other issues.
std::unique_ptr<WebSchedulingTaskQueue> queue1 =
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