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 <shaseley@chromium.org>
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 <etiennep@chromium.org>
Commit-Queue: Scott Haseley <shaseley@chromium.org>
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