CVE-2026-10884
Overview
Background
- `MojoRendererService`
- A browser/GPU-side Mojo endpoint that wraps a concrete
media::Rendererand exposes itsInitialize/playback methods to a (potentially untrusted) renderer over IPC. - `StarboardRenderer`
- The Cast/Chromecast
media::Rendererimplementation that allocates and drives anSbPlayerthrough itsplayer_manager_. - `GeometryChangeHandler`
- The Starboard component that applies video-geometry updates (e.g.
SetPlayerBounds) to the live player and therefore holds a pointer that must track the player’s lifetime. - `DCHECK` vs `CHECK`
DCHECK_EQcompiles to a no-op in release builds, so a precondition enforced only byDCHECKis effectively unenforced in shipped Chrome.
Root Cause Analysis
MojoRendererService::Initialize guarded against re-initialization only with DCHECK_EQ(state_, STATE_UNINITIALIZED), which is stripped from release builds, so a compromised renderer could invoke Initialize a second time over Mojo. The first call successfully stands up a StarboardRenderer/SbPlayer, but a second call could fail during player creation and tear the underlying player down, while objects such as GeometryChangeHandler continued to hold a pointer to the now-destroyed player — a use-after-free.
The fix converts the soft precondition into hard, always-on enforcement: MojoRendererService::Initialize now treats a repeat call as a protocol violation and reports a bad message (the added include of mojo/public/cpp/bindings/message.h and the removed DCHECK_EQ line confirm the direction, though the exact replacement statement is cut off in the diff), and StarboardRenderer::Initialize gains CHECK(!player_manager_) so a second initialization aborts before it can disturb an already-allocated player. This makes double-initialization impossible in release builds rather than relying on a debug-only assertion, closing the window in which a dangling player pointer could be used.
DCHECK, leaving release builds willing to re-enter Initialize and destroy a live player out from under its observers; the fix re-establishes the invariant with release-build enforcement (a bad-message report in MojoRendererService and a CHECK in StarboardRenderer).Attack Path
- Compromised renderer
An attacker who already controls a renderer process obtains the
mojom::Rendererinterface backed byMojoRendererServicein the Cast browser process. - First Initialize
The renderer calls
Initializeonce;StarboardRendererallocates anSbPlayerviaplayer_manager_and wires upGeometryChangeHandler. - Second Initialize
Because the only guard was a release-stripped
DCHECK_EQ, the renderer callsInitializeagain; the re-initialization path fails player creation and destroys the underlying player. - Dangling pointer use
Subsequent geometry activity reaches
GeometryChangeHandler, which still references the freed player, producing a use-after-free (the regression testDoesNotSetSbPlayerOnInitializationFailureencodes that no bounds must be set after a failed init). - Downstream control
Corruption of the freed player object could be leveraged toward further memory corruption or code execution in the browser process
(inferred).
Impact Assessment
(inferred).Changed Functions
| Function | Change | Notes |
|---|---|---|
TEST_Fchromecast/starboard/media/renderer/starboard_renderer_test.cc |
modified | |
TEST_Fmedia/mojo/clients/mojo_renderer_unittest.cc |
modified |
Files Changed
chromecast/starboard/media/renderer/starboard_renderer.ccchromecast/starboard/media/renderer/starboard_renderer_test.ccmedia/mojo/clients/BUILD.gnmedia/mojo/clients/mojo_renderer_unittest.ccmedia/mojo/services/mojo_renderer_service.cc
Audit Directions
- Debug-only invariants on IPC entry pointsAudit Mojo service methods reachable from the renderer that enforce call-ordering or once-only semantics with
DCHECK/DCHECK_EQ; these are unenforced in release and should useCHECK,mojo::ReportBadMessage, or explicit state handling. - Failed-initialization cleanupLook for
media::Renderer/player wrappers where a failedInitializetears down a backing object while leaving observers or handlers (e.g. geometry, overlay, metrics) holding pointers to it. - Re-initialization of stateful media objectsReview components that assume
Initializeruns exactly once (player_manager_, decoder, or session objects) for behavior when called twice from an untrusted process.
Patch
From 2dfbd25331c40ab4bd4454ea20c8d03d3898e334 Mon Sep 17 00:00:00 2001
From: Andrew Paseltiner <apaseltiner@chromium.org>
Date: Mon, 27 Apr 2026 18:06:39 -0700
Subject: [PATCH] media: Fix Use-After-Free in StarboardRenderer and MojoRendererService
A compromised renderer could trigger a Use-After-Free in the Cast
browser process by initializing a MojoRendererService twice. The first
initialization successfully allocates an SbPlayer, but a second call
(allowed in release builds due to a debug-only DCHECK) could fail,
destroying the underlying player while leaving a dangling pointer in
GeometryChangeHandler.
This CL fixes the issue by:
1. Adding a hard check in MojoRendererService::Initialize to prevent
re-initialization and reporting a bad message if attempted.
2. Adding a CHECK in StarboardRenderer::Initialize to ensure it is only
called once, providing defense-in-depth.
Fixed: 503617302
Change-Id: Iffd137bfe533ae35848916667faaf5f22372b002
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7772958
Reviewed-by: Simeon Anfinrud <sanfin@chromium.org>
Reviewed-by: Colin Blundell <blundell@chromium.org>
Commit-Queue: Andrew Paseltiner <apaseltiner@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1621456}
---
diff --git a/chromecast/starboard/media/renderer/starboard_renderer.cc b/chromecast/starboard/media/renderer/starboard_renderer.cc
index f456c8b6..7fb4693 100644
--- a/chromecast/starboard/media/renderer/starboard_renderer.cc
+++ b/chromecast/starboard/media/renderer/starboard_renderer.cc
@@ -55,6 +55,7 @@
::media::PipelineStatusCallback init_cb) {
CHECK(media_task_runner_->RunsTasksInCurrentSequence());
CHECK(client);
+ CHECK(!player_manager_);
::media::DemuxerStream* audio_stream =
media_resource->GetFirstStream(::media::DemuxerStream::Type::AUDIO);
diff --git a/chromecast/starboard/media/renderer/starboard_renderer_test.cc b/chromecast/starboard/media/renderer/starboard_renderer_test.cc
index 36cfdd1e..cca300bc 100644
--- a/chromecast/starboard/media/renderer/starboard_renderer_test.cc
+++ b/chromecast/starboard/media/renderer/starboard_renderer_test.cc
@@ -649,6 +649,56 @@
EXPECT_EQ(renderer.GetMediaTime(), kMediaTime);
}
+// Regression test for crbug.com/503617302.
+TEST_F(StarboardRendererTest, DoesNotSetSbPlayerOnInitializationFailure) {
+ const gfx::RectF geometry(0, 0, 1920, 1080);
+ const gfx::OverlayTransform transform =
+ gfx::OverlayTransform::OVERLAY_TRANSFORM_NONE;
+
+ // Set up the mock to fail player creation.
+ EXPECT_CALL(*starboard_, CreatePlayer(_, _)).WillOnce(Return(nullptr));
+
+ // The player's bounds should NOT be set after a failed initialization, even
+ // if geometry changes occur.
+ EXPECT_CALL(*starboard_, SetPlayerBounds(_, _, _, _, _, _)).Times(0);
+
+ StarboardRenderer renderer(std::move(starboard_), task_runner_, plane_id_,
+ /*enable_buffering=*/true,
+ &geometry_setter_service_, &cast_metrics_helper_);
+ RunPendingTasks();
+
+ EXPECT_CALL(
+ pipeline_status_fn_,
+ Call(HasStatusCode(
+ ::media::PipelineStatusCodes::PIPELINE_ERROR_INITIALIZATION_FAILED)))
+ .Times(1);
+ renderer.Initialize(
+ &media_resource_, &client_,
+ base::BindLambdaForTesting(pipeline_status_fn_.AsStdFunction()));
+ RunPendingTasks();
+
+ // This geometry change should be ignored by the geometry_change_handler_,
+ // which was never initialized with a player.
+ static_cast<mojom::VideoGeometrySetter*>(&geometry_setter_service_)
+ ->SetVideoGeometry(geometry, transform, plane_id_);
+ RunPendingTasks();
+}
+
+TEST_F(StarboardRendererTest, Initialize_Twice_Crashes) {
+ StarboardRenderer renderer(std::move(starboard_), task_runner_, plane_id_,
+ /*enable_buffering=*/true,
+ &geometry_setter_service_, &cast_metrics_helper_);
+
+ renderer.Initialize(
+ &media_resource_, &client_,
+ base::BindLambdaForTesting(pipeline_status_fn_.AsStdFunction()));
+ RunPendingTasks();
+
+ EXPECT_CHECK_DEATH(renderer.Initialize(
+ &media_resource_, &client_,
+ base::BindLambdaForTesting(pipeline_status_fn_.AsStdFunction())));
+}
+
TEST_F(StarboardRendererTest, SetPlaybackRateReportsMetric) {
// Ignore unrelated metrics calls.
EXPECT_CALL(cast_metrics_helper_, RecordApplicationEvent(_))
diff --git a/media/mojo/clients/BUILD.gn b/media/mojo/clients/BUILD.gn
index 66e1242..9fa9618 100644
--- a/media/mojo/clients/BUILD.gn
+++ b/media/mojo/clients/BUILD.gn
@@ -157,6 +157,7 @@
"//gpu/ipc/common:test_support",
"//media:test_support",
"//media/mojo:test_support",
+ "//mojo/public/cpp/test_support:test_utils",
"//testing/gmock",
"//testing/gtest",
]
diff --git a/media/mojo/clients/mojo_renderer_unittest.cc b/media/mojo/clients/mojo_renderer_unittest.cc
index 1814cf0..3723b2f 100644
--- a/media/mojo/clients/mojo_renderer_unittest.cc
+++ b/media/mojo/clients/mojo_renderer_unittest.cc
@@ -2,11 +2,14 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "media/mojo/clients/mojo_renderer.h"
+
#include <stdint.h>
#include <memory>
#include "base/functional/bind.h"
+#include "base/functional/callback_helpers.h"
#include "base/memory/ptr_util.h"
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
@@ -21,7 +24,6 @@
#include "media/base/test_helpers.h"
#include "media/cdm/clear_key_cdm_common.h"
#include "media/cdm/default_cdm_factory.h"
-#include "media/mojo/clients/mojo_renderer.h"
#include "media/mojo/common/media_type_converters.h"
#include "media/mojo/mojom/content_decryption_module.mojom.h"
#include "media/mojo/mojom/renderer.mojom.h"
@@ -33,6 +35,7 @@
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
+#include "mojo/public/cpp/test_support/test_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
@@ -232,6 +235,34 @@
Initialize();
}
+// Regression test for crbug.com/503617302.
+TEST_F(MojoRendererTest, Initialize_Twice) {
+ // Create a service directly to bypass client-side checks in MojoRenderer.
+ auto mock_renderer = std::make_unique<StrictMock<MockRenderer>>();
+ mojo::Remote<mojom::Renderer> remote;
+ auto receiver_ref = MojoRendererService::Create(
+ &mojo_cdm_service_context_, std::move(mock_renderer),
+ remote.BindNewPipeAndPassReceiver());
+
+ mojo::PendingAssociatedRemote<mojom::RendererClient> client_remote_1;
+ auto client_receiver_1 = client_remote_1.InitWithNewEndpointAndPassReceiver();
+ std::vector<mojo::PendingRemote<mojom::DemuxerStream>> streams;
+ mojo::PendingRemote<mojom::DemuxerStream> stream_remote;
+ auto stream_receiver = stream_remote.InitWithNewPipeAndPassReceiver();
+ streams.push_back(std::move(stream_remote));
+ remote->Initialize(std::move(client_remote_1), std::move(streams),
+ base::DoNothing());
+
+ mojo::PendingAssociatedRemote<mojom::RendererClient> client_remote_2;
+ auto client_receiver_2 = client_remote_2.InitWithNewEndpointAndPassReceiver();
+
+ mojo::test::BadMessageObserver bad_message_observer;
+ remote->Initialize(std::move(client_remote_2), std::nullopt,
+ base::DoNothing());
+
+ EXPECT_EQ("MojoRendererService is already initialized",
+ bad_message_observer.WaitForBadMessage());
+}
TEST_F(MojoRendererTest, Initialize_Failure) {
CreateAudioStream();
// Mojo Renderer only expects a boolean result, which will be translated
diff --git a/media/mojo/services/mojo_renderer_service.cc b/media/mojo/services/mojo_renderer_service.cc
index 90d4ae6..c0697a0 100644
--- a/media/mojo/services/mojo_renderer_service.cc
+++ b/media/mojo/services/mojo_renderer_service.cc
@@ -16,6 +16,7 @@
#include "media/mojo/common/media_type_converters.h"
#include "media/mojo/services/media_resource_shim.h"
#include "media/mojo/services/mojo_cdm_service_context.h"
+#include "mojo/public/cpp/bindings/message.h"
namespace media {
@@ -58,7 +59,11 @@
streams,
InitializeCallback callback) {
DVLOG(1) << __func__;
- DCHECK_EQ(state_, STATE_UNINITIALIZED);
Regression Test / PoC
diff --git a/chromecast/starboard/media/renderer/starboard_renderer_test.cc b/chromecast/starboard/media/renderer/starboard_renderer_test.cc
index 36cfdd1e..cca300bc 100644
--- a/chromecast/starboard/media/renderer/starboard_renderer_test.cc
+++ b/chromecast/starboard/media/renderer/starboard_renderer_test.cc
@@ -649,6 +649,56 @@
EXPECT_EQ(renderer.GetMediaTime(), kMediaTime);
}
+// Regression test for crbug.com/503617302.
+TEST_F(StarboardRendererTest, DoesNotSetSbPlayerOnInitializationFailure) {
+ const gfx::RectF geometry(0, 0, 1920, 1080);
+ const gfx::OverlayTransform transform =
+ gfx::OverlayTransform::OVERLAY_TRANSFORM_NONE;
+
+ // Set up the mock to fail player creation.
+ EXPECT_CALL(*starboard_, CreatePlayer(_, _)).WillOnce(Return(nullptr));
+
+ // The player's bounds should NOT be set after a failed initialization, even
+ // if geometry changes occur.
+ EXPECT_CALL(*starboard_, SetPlayerBounds(_, _, _, _, _, _)).Times(0);
+
+ StarboardRenderer renderer(std::move(starboard_), task_runner_, plane_id_,
+ /*enable_buffering=*/true,
+ &geometry_setter_service_, &cast_metrics_helper_);
+ RunPendingTasks();
+
+ EXPECT_CALL(
+ pipeline_status_fn_,
+ Call(HasStatusCode(
+ ::media::PipelineStatusCodes::PIPELINE_ERROR_INITIALIZATION_FAILED)))
+ .Times(1);
+ renderer.Initialize(
+ &media_resource_, &client_,
+ base::BindLambdaForTesting(pipeline_status_fn_.AsStdFunction()));
+ RunPendingTasks();
+
+ // This geometry change should be ignored by the geometry_change_handler_,
+ // which was never initialized with a player.
+ static_cast<mojom::VideoGeometrySetter*>(&geometry_setter_service_)
+ ->SetVideoGeometry(geometry, transform, plane_id_);
+ RunPendingTasks();
+}
+
+TEST_F(StarboardRendererTest, Initialize_Twice_Crashes) {
+ StarboardRenderer renderer(std::move(starboard_), task_runner_, plane_id_,
+ /*enable_buffering=*/true,
+ &geometry_setter_service_, &cast_metrics_helper_);
+
+ renderer.Initialize(
+ &media_resource_, &client_,
+ base::BindLambdaForTesting(pipeline_status_fn_.AsStdFunction()));
+ RunPendingTasks();
+
+ EXPECT_CHECK_DEATH(renderer.Initialize(
+ &media_resource_, &client_,
+ base::BindLambdaForTesting(pipeline_status_fn_.AsStdFunction())));
+}
+
TEST_F(StarboardRendererTest, SetPlaybackRateReportsMetric) {
// Ignore unrelated metrics calls.
EXPECT_CALL(cast_metrics_helper_, RecordApplicationEvent(_))
diff --git a/media/mojo/clients/mojo_renderer_unittest.cc b/media/mojo/clients/mojo_renderer_unittest.cc
index 1814cf0..3723b2f 100644
--- a/media/mojo/clients/mojo_renderer_unittest.cc
+++ b/media/mojo/clients/mojo_renderer_unittest.cc
@@ -2,11 +2,14 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "media/mojo/clients/mojo_renderer.h"
+
#include <stdint.h>
#include <memory>
#include "base/functional/bind.h"
+#include "base/functional/callback_helpers.h"
#include "base/memory/ptr_util.h"
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
@@ -21,7 +24,6 @@
#include "media/base/test_helpers.h"
#include "media/cdm/clear_key_cdm_common.h"
#include "media/cdm/default_cdm_factory.h"
-#include "media/mojo/clients/mojo_renderer.h"
#include "media/mojo/common/media_type_converters.h"
#include "media/mojo/mojom/content_decryption_module.mojom.h"
#include "media/mojo/mojom/renderer.mojom.h"
@@ -33,6 +35,7 @@
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
+#include "mojo/public/cpp/test_support/test_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
@@ -232,6 +235,34 @@
Initialize();
}
+// Regression test for crbug.com/503617302.
+TEST_F(MojoRendererTest, Initialize_Twice) {
+ // Create a service directly to bypass client-side checks in MojoRenderer.
+ auto mock_renderer = std::make_unique<StrictMock<MockRenderer>>();
+ mojo::Remote<mojom::Renderer> remote;
+ auto receiver_ref = MojoRendererService::Create(
+ &mojo_cdm_service_context_, std::move(mock_renderer),
+ remote.BindNewPipeAndPassReceiver());
+
+ mojo::PendingAssociatedRemote<mojom::RendererClient> client_remote_1;
+ auto client_receiver_1 = client_remote_1.InitWithNewEndpointAndPassReceiver();
+ std::vector<mojo::PendingRemote<mojom::DemuxerStream>> streams;
+ mojo::PendingRemote<mojom::DemuxerStream> stream_remote;
+ auto stream_receiver = stream_remote.InitWithNewPipeAndPassReceiver();
+ streams.push_back(std::move(stream_remote));
+ remote->Initialize(std::move(client_remote_1), std::move(streams),
+ base::DoNothing());
+
+ mojo::PendingAssociatedRemote<mojom::RendererClient> client_remote_2;
+ auto client_receiver_2 = client_remote_2.InitWithNewEndpointAndPassReceiver();
+
+ mojo::test::BadMessageObserver bad_message_observer;
+ remote->Initialize(std::move(client_remote_2), std::nullopt,
+ base::DoNothing());
+
+ EXPECT_EQ("MojoRendererService is already initialized",
+ bad_message_observer.WaitForBadMessage());
+}
TEST_F(MojoRendererTest, Initialize_Failure) {
CreateAudioStream();
// Mojo Renderer only expects a boolean result, which will be translated
Original Bug Report
Potential Use-After-Free in Cast StarboardRenderer via double initialization
Project Fortify, an experimental security project, has identified the following potential security issue. If you’re a feature owner CC-ed on this bug, please do your best to review these reports without the Chrome Security team. Please see go/chrome-ai-generated-security-bugs-faq for more information.
Overview: A compromised renderer can trigger a Use-After-Free in the Cast browser process by initializing a StarboardRenderer twice via its Mojo interface. If the second initialization fails, the underlying player is freed but a dangling pointer remains in GeometryChangeHandler, which can later be dereferenced by submitting a specific compositor frame.
Affected files:
chromecast/starboard/media/renderer/starboard_renderer.ccchromecast/starboard/media/renderer/geometry_change_handler.ccchromecast/starboard/media/renderer/geometry_change_handler.hchromecast/starboard/media/renderer/starboard_player_manager.ccmedia/mojo/services/mojo_renderer_service.ccchromecast/starboard/media/media/starboard_api_wrapper_base.cc
Estimated timestamp from git blame: 2025-10-06
Summary
A potential Use-After-Free (UAF) vulnerability exists in the Cast browser process on Starboard builds. The MojoRendererService uses a DCHECK to prevent re-initialization, which is compiled out in release builds. This allows a compromised renderer to call Initialize multiple times. If a subsequent initialization fails, StarboardRenderer destroys the existing StarboardPlayerManager (freeing the SbPlayer) but fails to clear the sb_player_ pointer in its GeometryChangeHandler. A subsequent geometry update via a VideoHoleDrawQuad will then write attacker-controlled bounds to the freed SbPlayer memory.
Vulnerability Details
MojoRendererService::Initialize(inmedia/mojo/services/mojo_renderer_service.cc) uses a debug-onlyDCHECK_EQ(state_, STATE_UNINITIALIZED)to ensure it is only called once. In release builds, a compromised renderer can call this method multiple times on the same remote.- When
Initializeis called a second time, it eventually invokesStarboardRenderer::Initialize(inchromecast/starboard/media/renderer/starboard_renderer.cc). StarboardRenderer::Initializeattempts to create a newStarboardPlayerManagerby callingStarboardPlayerManager::Create(...).- If the attacker provides an unsupported media configuration (e.g., using
AudioCodec::kAMR_NB),Createfails and returnsnullptr. - The assignment
player_manager_ = StarboardPlayerManager::Create(...)evaluates tonullptr. Becauseplayer_manager_is astd::unique_ptr, this destroys the oldStarboardPlayerManagerfrom the first initialization, which in turn callsSbPlayerDestroyto free the underlying vendor-allocatedSbPlayer. - Because
player_manager_is now null,StarboardRenderer::Initializereturns early with an error:if (!player_manager_) { // Early return on failure media_task_runner_->PostTask(..., PIPELINE_ERROR_INITIALIZATION_FAILED); return; } - Crucially, the early return skips the subsequent call to
geometry_change_handler_.SetSbPlayer(...). As a result, theGeometryChangeHandlerretains a danglingraw_ptr<void> sb_player_pointing to the freed memory. - The attacker can then trigger
GeometryChangeHandler::OnVideoGeometryChangeby submitting a compositor frame from the renderer containing aVideoHoleDrawQuadwith the originaloverlay_plane_idand attacker-controlled bounds. OnVideoGeometryChangecallsSetPlayerBounds, which invokes the vendor Starboard APISbPlayerSetBoundswith the danglingsb_player_pointer and the attacker-controlled coordinates, resulting in a highly controllable Write-After-Free in the browser process.
Note: MiraclePtr (BackupRefPtr) does not mitigate this vulnerability because SbPlayer is allocated by an external vendor library, not PartitionAlloc.
Potential Reproduction Steps
Note: These are suggested steps for an attacker; we do not have a working proof of concept.
- From a compromised renderer, call
media.mojom.InterfaceFactory::CreateCastRendererwith a generatedoverlay_plane_idto obtain a browser-processmedia.mojom.Rendererremote. - Call
Renderer::Initializewith a valid media configuration (e.g., AAC/H264) and wait for the success callback. This successfully allocates theSbPlayer. - Call
Renderer::Initializeagain on the same remote, this time providing an invalid configuration (e.g., advertisingAudioCodec::kAMR_NB). This frees theSbPlayerbut leaves a dangling pointer inGeometryChangeHandler. - Submit a compositor frame from the renderer containing a
VideoHoleDrawQuad. Set itsoverlay_plane_idto match the one from Step 1, and set its bounds to arbitrary values payload. - The Viz process will forward the bounds to the browser process, triggering a write to the freed
SbPlayermemory.
Suggested Fix
There are two places where this should be addressed for defense-in-depth:
- Prevent re-initialization at the Mojo boundary: In
MojoRendererService::Initialize, replace or supplement theDCHECKwith a hard check that terminates the renderer if it attempts to initialize twice:if (state_ != STATE_UNINITIALIZED) { mojo::ReportBadMessage("MojoRendererService cannot be initialized multiple times"); return; } - Clear the dangling pointer on failure: In
StarboardRenderer::Initialize, ensure theGeometryChangeHandler’s pointer is cleared if player creation fails:if (!player_manager_) { geometry_change_handler_.SetSbPlayer(nullptr); // ADD THIS LINE LOG(ERROR) << "Unable to create StarboardPlayerManager"; // ... return; }
Evaluated with Chrome root at commit: 661452647ddb2827305122ff3273bd5dea403f09
Results so far have been promising, but there can be wrong deductions. If this proves to be a false positive, please close as WAI; data from false positives will be used to improve accuracy over time. And please feel free to reach out to me directly if you have concerns or feedback on the project.