Chrome · Mobile
CVE-2026-85044
Logic Error in Mobile
Overview
Medium
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifandroid_webview/java/src/org/chromium/android_webview/contextmenu/AwContextMenuCoordinator.java |
modified | |
ifchrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHelper.java |
modified |
Files Changed
android_webview/browser/aw_context_menu_helper.ccandroid_webview/java/src/org/chromium/android_webview/contextmenu/AwContextMenuCoordinator.javaandroid_webview/java/src/org/chromium/android_webview/contextmenu/AwContextMenuHelper.javachrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHelper.javachrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/ContextMenuLoadUrlParamsTest.javachrome/browser/ui/android/context_menu_helper.cc
Patch
From a9de56747eeb6b737003a1c4d1ef5bbca617ba69 Mon Sep 17 00:00:00 2001 From: Jenna Himawan <[email protected]> Date: Mon, 13 Jul 2026 12:45:59 -0700 Subject: [PATCH] Plumb InitiatorNavigationState for Android context menu navigations Bypass-Check-License: Moving files, not creating new files Bug: 517482830 Change-Id: I7cfa48e6d171461c5ad49afd3b610ce94d575962 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8001092 Commit-Queue: Jenna Himawan <[email protected]> Reviewed-by: Bo Liu <[email protected]> Reviewed-by: Calder Kitagawa <[email protected]> Cr-Commit-Position: refs/heads/main@{#1661311} --- diff --git a/android_webview/browser/aw_context_menu_helper.cc b/android_webview/browser/aw_context_menu_helper.cc index a8c4c0d..e8cc7148 100644 --- a/android_webview/browser/aw_context_menu_helper.cc +++ b/android_webview/browser/aw_context_menu_helper.cc @@ -6,7 +6,7 @@ #include "android_webview/browser_jni_headers/AwContextMenuHelper_jni.h" #include "components/embedder_support/android/contextmenu/context_menu_builder.h" -#include "content/public/browser/render_process_host.h" +#include "content/public/browser/render_frame_host.h" #include "ui/android/view_android.h" using base::android::JavaRef; @@ -33,9 +33,8 @@ gfx::NativeView view = GetWebContents().GetNativeView(); Java_AwContextMenuHelper_showContextMenu( env, java_obj_, - context_menu::BuildJavaContextMenuParams( - params, nullptr, render_frame_host.GetProcess()->GetID(), - render_frame_host.GetFrameToken().value()), + context_menu::BuildJavaContextMenuParams(params, nullptr, + render_frame_host), view->GetContainerView()); } diff --git a/android_webview/java/src/org/chromium/android_webview/contextmenu/AwContextMenuCoordinator.java b/android_webview/java/src/org/chromium/android_webview/contextmenu/AwContextMenuCoordinator.java index c3520c0e..adef660 100644 --- a/android_webview/java/src/org/chromium/android_webview/contextmenu/AwContextMenuCoordinator.java +++ b/android_webview/java/src/org/chromium/android_webview/contextmenu/AwContextMenuCoordinator.java @@ -68,6 +68,7 @@ private WebContentsObserver mWebContentsObserver; private final boolean mIsDragDropEnabled; private final boolean mUsePopupWindow; + private boolean mDismissed; AwContextMenuCoordinator( WindowAndroid windowAndroid, @@ -97,6 +98,9 @@ } public void dismiss() { + if (mDismissed) return; + mDismissed = true; + if (mWebContentsObserver != null) { mWebContentsObserver.observe(null); } @@ -115,6 +119,8 @@ mCurrentPopulator.onMenuClosed(); mCurrentPopulator = null; } + + mParams.destroy(); } void displayMenu() { diff --git a/android_webview/java/src/org/chromium/android_webview/contextmenu/AwContextMenuHelper.java b/android_webview/java/src/org/chromium/android_webview/contextmenu/AwContextMenuHelper.java index c4c61e2..591f796 100644 --- a/android_webview/java/src/org/chromium/android_webview/contextmenu/AwContextMenuHelper.java +++ b/android_webview/java/src/org/chromium/android_webview/contextmenu/AwContextMenuHelper.java @@ -64,6 +64,7 @@ || windowAndroid.getActivity().get() == null || windowAndroid.getContext().get() == null) { Log.w(TAG, "Could not create context menu"); + params.destroy(); return false; } diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHelper.java index 0e42eb0..84bb744d 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHelper.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHelper.java @@ -85,6 +85,8 @@ dismissContextMenu(); if (mCurrentNativeDelegate != null) mCurrentNativeDelegate.destroy(); if (mPopulatorFactory != null) mPopulatorFactory.onDestroy(); + destroyContextMenuParams(mCurrentContextMenuParams); + mCurrentContextMenuParams = null; var removedValue = sContextMenuHelperMap.remove(mNativeContextMenuHelper); assert removedValue == this; mNativeContextMenuHelper = 0; @@ -94,6 +96,8 @@ private void setPopulatorFactory(ContextMenuPopulatorFactory populatorFactory) { dismissContextMenu(); if (mCurrentNativeDelegate != null) mCurrentNativeDelegate.destroy(); + destroyContextMenuParams(mCurrentContextMenuParams); + mCurrentContextMenuParams = null; mCurrentPopulator = null; if (mPopulatorFactory != null) mPopulatorFactory.onDestroy(); mPopulatorFactory = populatorFactory; @@ -101,6 +105,7 @@ /** * Starts showing a context menu for {@code view} based on {@code params}. + * * @param params The {@link ContextMenuParams} that indicate what menu items to show. * @param renderFrameHost {@link RenderFrameHost} to get the encoded images from. * @param view container view for the menu. @@ -112,7 +117,10 @@ RenderFrameHost renderFrameHost, View view, float topContentOffsetPx) { - if (params.isFile()) return; + if (params.isFile()) { + destroyContextMenuParams(params); + return; + } final WindowAndroid windowAndroid = mWebContents.getTopLevelNativeWindow(); @@ -124,6 +132,7 @@ || mPopulatorFactory == null || !mPopulatorFactory.isEnabled() || mCurrentContextMenu != null) { + destroyContextMenuParams(params); return; } @@ -162,6 +171,8 @@ // Has no effect if the classification already succeeded. mChipDelegate.onMenuClosed(); } + destroyContextMenuParams(mCurrentContextMenuParams); + mCurrentContextMenuParams = null; if (mNativeContextMenuHelper == 0) return; ContextMenuHelperJni.get().onContextMenuClosed(mNativeContextMenuHelper); }; @@ -263,6 +274,12 @@ return assertNonNull(sContextMenuHelperMap.get(nativeContextMenuHelper)); } + private static void destroyContextMenuParams(@Nullable ContextMenuParams params) { + if (params != null) { + params.destroy(); + } + } + @NativeMethods interface Natives { void onContextMenuClosed(long nativeContextMenuHelper); diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/ContextMenuLoadUrlParamsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/ContextMenuLoadUrlParamsTest.java index 03cb17b..c812319 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/ContextMenuLoadUrlParamsTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/ContextMenuLoadUrlParamsTest.java @@ -9,7 +9,6 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; import android.app.Activity; import android.content.Context; @@ -195,7 +194,6 @@ AdditionalNavigationParams navigationParams = sOpenNewTabLoadUrlParams.getAdditionalNavigationParams(); assertNotNull(navigationParams); - assertTrue(navigationParams.getInitiatorProcessId().isValid()); } /** diff --git a/chrome/browser/ui/android/context_menu_helper.cc b/chrome/browser/ui/android/context_menu_helper.cc index 2d50e5fc..2205a9b 100644 --- a/chrome/browser/ui/android/context_menu_helper.cc +++ b/chrome/browser/ui/android/context_menu_helper.cc @@ -16,7 +16,6 @@ #include "components/embedder_support/android/contextmenu/context_menu_builder.h" #include "content/public/browser/context_menu_params.h" #include "content/public/browser/render_frame_host.h" -#include "content/public/browser/render_process_host.h" #include "extensions/buildflags/buildflags.h" #include "third_party/blink/public/mojom/context_menu/context_menu.mojom.h" #include "ui/android/view_android.h" @@ -72,10 +71,8 @@ Java_ContextMenuHelper_showContextMenu( env, GetJavaObject(env), - context_menu::BuildJavaContextMenuParams( - context_menu_params_, model_ptr, - render_frame_host.GetProcess()->GetID(), - render_frame_host.GetFrameToken().value()), + context_menu::BuildJavaContextMenuParams(context_menu_params_, model_ptr, + render_frame_host), render_frame_host.GetJavaRenderFrameHost(), view->GetContainerView(), view->content_offset() * view->GetDipScale()); }
Loading diff…
Regression Test / PoC
shipped with the fix
diff --git a/content/browser/android/additional_navigation_params_browsertest.cc b/content/browser/android/additional_navigation_params_browsertest.cc
new file mode 100644
index 0000000..afad9807
--- /dev/null
+++ b/content/browser/android/additional_navigation_params_browsertest.cc
@@ -0,0 +1,98 @@
+// 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 "content/browser/android/additional_navigation_params.h"
+
+#include "base/android/jni_android.h"
+#include "base/functional/callback_helpers.h"
+#include "base/run_loop.h"
+#include "base/test/run_until.h"
+#include "content/browser/renderer_host/render_frame_host_impl.h"
+#include "content/browser/storage_partition_impl.h"
+#include "content/public/browser/android/additional_navigation_params_android.h"
+#include "content/public/browser/initiator_navigation_state.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/common/child_process_id.h"
+#include "content/public/test/browser_test.h"
+#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/content_browser_test.h"
+#include "content/public/test/content_browser_test_utils.h"
+#include "content/public/test/test_utils.h"
+#include "content/shell/browser/shell.h"
+#include "net/dns/mock_host_resolver.h"
+#include "third_party/blink/public/common/tokens/tokens.h"
+#include "url/gurl.h"
+
+namespace content {
+
+class AdditionalNavigationParamsBrowserTest : public ContentBrowserTest {
+ public:
+ void SetUpOnMainThread() override {
+ host_resolver()->AddRule("*", "127.0.0.1");
+ ASSERT_TRUE(embedded_test_server()->Start());
+ ContentBrowserTest::SetUpOnMainThread();
+ }
+
+ WebContents* web_contents() { return shell()->web_contents(); }
+};
+
+// AdditionalNavigationParams carries the initiator frame token across an
+// arbitrary delay before it is consumed by NavigationController::LoadUrl. The
+// initiator frame may be detached during that delay, so creating the params
+// must keep the frame's PolicyContainerHost reachable for the lifetime of the
+// returned keep-alive handle.
+IN_PROC_BROWSER_TEST_F(AdditionalNavigationParamsBrowserTest,
+ InitiatorPolicyContainerHostKeptAliveAfterFrameDetach) {
+ ASSERT_TRUE(NavigateToURL(
+ shell(), embedded_test_server()->GetURL(
+ "a.com", "/cross_site_iframe_factory.html?a.com(b.com)")));
+
+ RenderFrameHostImpl* child_rfh =
+ static_cast<RenderFrameHostImpl*>(ChildFrameAt(web_contents(), 0));
+ ASSERT_TRUE(child_rfh);
+
+ const blink::LocalFrameToken child_token = child_rfh->GetFrameToken();
+ const ChildProcessId child_process_id = child_rfh->GetProcess()->GetID();
+
+ base::android::ScopedJavaLocalRef<jobject> java_params =
+ CreateJavaAdditionalNavigationParams(
+ base::android::AttachCurrentThread(), *child_rfh,
+ /*attribution_src_token=*/std::nullopt);
+ ASSERT_TRUE(java_params);
+
+ // Detach the initiator frame while still holding the params.
+ RenderFrameDeletedObserver deleted_observer(child_rfh);
+ ASSERT_TRUE(
+ ExecJs(web_contents(), "document.querySelector('iframe').remove();"));
+ ASSERT_TRUE(deleted_observer.WaitUntilDeleted());
+ ASSERT_FALSE(
+ RenderFrameHostImpl::FromFrameToken(child_process_id, child_token));
+
+ // Verify round-trip extraction of frame token and process ID even after frame
+ // detachment.
+ std::optional<blink::LocalFrameToken> extracted_frame_token =
+ GetInitiatorFrameTokenFromJavaAdditionalNavigationParams(
+ base::android::AttachCurrentThread(), java_params);
+ EXPECT_TRUE(extracted_frame_token.has_value());
+ EXPECT_EQ(extracted_frame_token.value(), child_token);
+
+ ChildProcessId extracted_process_id =
+ GetInitiatorProcessIdFromJavaAdditionalNavigationParams(
+ base::android::AttachCurrentThread(), java_params);
+ EXPECT_FALSE(extracted_process_id.is_null());
+ EXPECT_EQ(extracted_process_id, child_process_id);
+
+ // We can get the state from the Java object.
+ scoped_refptr<InitiatorNavigationState> taken_state =
+ TakeNativeStateFromJavaAdditionalNavigationParams(
+ base::android::AttachCurrentThread(), java_params);
+ EXPECT_TRUE(taken_state);
+
+ // Clean up the Java object to satisfy LifetimeAssert and release native
+ // state.
+ DestroyJavaAdditionalNavigationParams(base::android::AttachCurrentThread(),
+ java_params);
+}
+
+} // namespace content
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index a2b70da..6dfdf63 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -2299,6 +2299,7 @@
sources += [
"../browser/accessibility/android_granularity_movement_browsertest.cc",
"../browser/accessibility/ime_accessibility_browsertest.cc",
+ "../browser/android/additional_navigation_params_browsertest.cc",
"../browser/android/nfc_host_browsertest.cc",
"../browser/android/render_widget_host_connector_browsertest.cc",
"../browser/android/render_widget_host_connector_browsertest.h",
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