CVE-2026-14037
Overview
Background
- Viz / `CompositorFrameSinkSupport`
- the GPU-process component that services a renderer’s compositor frame sink over Mojo, including layer-context requests.
- `BindLayerContext()`
- the Mojo-exposed method that constructs a
LayerContextImplfrom a renderer-suppliedPendingLayerContext, wiring up the “trees in viz” layer-context pipeline. - `features::kTreesInViz`
- the runtime feature flag gating the TreesInViz functionality that
LayerContextImplimplements. - `LayerContextImpl`
- the object created and owned via
layer_context_that drives the layer-context path once bound.
Root Cause Analysis
CompositorFrameSinkSupport::BindLayerContext unconditionally created a LayerContextImpl from the renderer-provided PendingLayerContext without checking whether the kTreesInViz feature was enabled. This meant the TreesInViz layer-context code path could be reached and instantiated in the GPU process even when the feature was supposed to be disabled, so the feature flag did not actually gate the functionality it guarded.
The fix adds an early return when base::FeatureList::IsEnabled(features::kTreesInViz) is false, so BindLayerContext no longer constructs layer_context_ for disabled builds/configurations. The regression test confirms that with the feature disabled, BindLayerContext leaves layer_context_for_testing() null.
kTreesInViz enablement check at the entry of BindLayerContext so the layer context is only bound when the feature is actually on.Attack Path
- Send a layer-context bind request
A renderer (or compromised renderer) issues the Mojo call that reaches
CompositorFrameSinkSupport::BindLayerContextwith a craftedPendingLayerContext, even in a configuration wherekTreesInVizis disabled. - Unguarded instantiation
Pre-patch,
BindLayerContextcreates aLayerContextImplregardless of the flag, activating the TreesInViz layer-context pipeline that policy intended to keep off. - Exercise the unintended path The renderer can then drive the layer-context surface in the GPU process that should have been unreachable while the feature is disabled.
- Downstream consequences Any memory-safety or additional attack surface exposed by the prematurely enabled layer-context code is (inferred); the patch itself only demonstrates the policy/gating failure, not a specific corruption sink.
Impact Assessment
kTreesInViz layer-context code path in the GPU process even when the feature flag is disabled, defeating the intended gating of that functionality. The precondition is the ability to issue the layer-context bind Mojo call to Viz, available to a renderer. The advisory rates this as insufficient policy enforcement in GPU; any resulting memory-safety or sandbox impact from the exposed surface is (inferred) and no exploited-in-the-wild status is stated.Files Changed
components/viz/service/frame_sinks/compositor_frame_sink_support.cccomponents/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc
Audit Directions
- Feature-flag gaps at Mojo entry pointsAudit browser/GPU-process Mojo handlers that instantiate flag-gated subsystems to confirm the enablement check is performed before construction, not merely at the caller.
- Renderer-reachable constructorsReview methods that build objects from renderer-supplied
Pending*interfaces to ensure their feature/policy preconditions are validated server-side. - Flag checked in some paths onlyWhere a feature is checked in initialization or UI paths, verify every service-side entry that activates the feature repeats the check.
Patch
From 8b19dcd1ffef5628f8b82b461884ef195470ffa2 Mon Sep 17 00:00:00 2001
From: Zhenyao Mo <zmo@chromium.org>
Date: Wed, 20 May 2026 08:59:31 -0700
Subject: [PATCH] viz: Support TreesInViz check in BindLayerContext
Bug: 496522611
Change-Id: I149e96384d9c161c6353a936ca90863accf1400c
TEST=viz_unittests
Change-Id: I149e96384d9c161c6353a936ca90863accf1400c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7861920
Auto-Submit: Zhenyao Mo <zmo@chromium.org>
Commit-Queue: Vasiliy Telezhnikov <vasilyt@chromium.org>
Reviewed-by: Vasiliy Telezhnikov <vasilyt@chromium.org>
Commit-Queue: Zhenyao Mo <zmo@chromium.org>
Reviewed-by: Tzarial <zork@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1633621}
---
diff --git a/components/viz/service/frame_sinks/compositor_frame_sink_support.cc b/components/viz/service/frame_sinks/compositor_frame_sink_support.cc
index d81eba6..8e3368626 100644
--- a/components/viz/service/frame_sinks/compositor_frame_sink_support.cc
+++ b/components/viz/service/frame_sinks/compositor_frame_sink_support.cc
@@ -624,6 +624,9 @@
void CompositorFrameSinkSupport::BindLayerContext(
mojom::PendingLayerContext& context,
mojom::LayerContextSettingsPtr settings) {
+ if (!base::FeatureList::IsEnabled(features::kTreesInViz)) {
+ return;
+ }
layer_context_ =
std::make_unique<LayerContextImpl>(this, context, std::move(settings));
}
diff --git a/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc b/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc
index bec1b6f..b18e905 100644
--- a/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc
+++ b/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc
@@ -2929,4 +2929,28 @@
support->SendCompositorFrameAck();
}
+TEST_P(AckOnSurfaceActivationWhenInteractiveTest,
+ TreesInVizDisabledDoesNotBindLayerContext) {
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitAndDisableFeature(features::kTreesInViz);
+ manager_->RegisterFrameSinkId(kAnotherArbitraryFrameSinkId,
+ true /* report_activation */);
+ MockCompositorFrameSinkClient mock_client;
+ auto support = std::make_unique<CompositorFrameSinkSupport>(
+ &mock_client, manager_.get(), kAnotherArbitraryFrameSinkId, kIsRoot);
+
+ auto context = mojom::PendingLayerContext::New();
+ mojo::AssociatedRemote<mojom::LayerContext> layer_context;
+ context->receiver = layer_context.BindNewEndpointAndPassReceiver();
+ MockLayerContextClient mock_layer_context_client;
+ mojo::AssociatedReceiver<mojom::LayerContextClient>
+ layer_context_client_receiver(
+ &mock_layer_context_client,
+ context->client.InitWithNewEndpointAndPassReceiver());
+
+ auto settings = mojom::LayerContextSettings::New();
+ support->BindLayerContext(*context, std::move(settings));
+ EXPECT_EQ(support->layer_context_for_testing(), nullptr);
+}
+
} // namespace viz
Regression Test / PoC
diff --git a/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc b/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc
index bec1b6f..b18e905 100644
--- a/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc
+++ b/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc
@@ -2929,4 +2929,28 @@
support->SendCompositorFrameAck();
}
+TEST_P(AckOnSurfaceActivationWhenInteractiveTest,
+ TreesInVizDisabledDoesNotBindLayerContext) {
+ base::test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitAndDisableFeature(features::kTreesInViz);
+ manager_->RegisterFrameSinkId(kAnotherArbitraryFrameSinkId,
+ true /* report_activation */);
+ MockCompositorFrameSinkClient mock_client;
+ auto support = std::make_unique<CompositorFrameSinkSupport>(
+ &mock_client, manager_.get(), kAnotherArbitraryFrameSinkId, kIsRoot);
+
+ auto context = mojom::PendingLayerContext::New();
+ mojo::AssociatedRemote<mojom::LayerContext> layer_context;
+ context->receiver = layer_context.BindNewEndpointAndPassReceiver();
+ MockLayerContextClient mock_layer_context_client;
+ mojo::AssociatedReceiver<mojom::LayerContextClient>
+ layer_context_client_receiver(
+ &mock_layer_context_client,
+ context->client.InitWithNewEndpointAndPassReceiver());
+
+ auto settings = mojom::LayerContextSettings::New();
+ support->BindLayerContext(*context, std::move(settings));
+ EXPECT_EQ(support->layer_context_for_testing(), nullptr);
+}
+
} // namespace viz