CVE-2026-13788
Overview
Background
- `ExclusiveAccessManagerAndroid`
- the C++ object (in
chrome/browser/ui/android/exclusive_access/exclusive_access_manager_android.cc) that manages exclusive-access UI such as fullscreen on Android and is paired with a Java counterpart. - JNI native-pointer pattern
- an Android Java object holds a raw
longpointer to its C++ peer, and the C++ peer is expected to null out or otherwise invalidate that pointer in Java before the C++ object is freed. - `eac_`
- a member of
ExclusiveAccessManagerAndroidthat, per the patch, exposes aDestroy(JNIEnv*)method used to tear down an associated object before the manager deletes itself (its concrete type is not shown in the diff) (inferred).
Root Cause Analysis
Before the patch, ExclusiveAccessManagerAndroid::Destroy(JNIEnv* env) immediately executed delete this, freeing the C++ object without informing any peer that still held a pointer to it. The added comment states the correct behavior: the “Java counterpart” must be notified to “drop its native pointer before the C++ object is destroyed, preventing a use-after-free,” which confirms that a live reference to the freed C++ object survived the deletion.
The fix inserts if (eac_) { eac_->Destroy(env); } ahead of delete this, so the dependent object (and, via it, the Java layer) invalidates its native pointer while the C++ object is still valid. Because the invalidation now runs strictly before the memory is released, subsequent calls through the stale native pointer no longer reach freed memory. The invariant violated was that no external holder may retain a usable pointer to ExclusiveAccessManagerAndroid after Destroy frees it.
Destroy freed the C++ object with delete this while a peer still held a native pointer to it, and the fix simply notifies that peer (via eac_->Destroy(env)) to relinquish the pointer before the free occurs.Attack Path
- Trigger teardown
The attacker’s page drives the fullscreen/exclusive-access flow on Android such that
ExclusiveAccessManagerAndroid::Destroy(JNIEnv*)is invoked, which under the old code runsdelete thisand frees the manager. - Retain the stale pointer
The Java counterpart (and/or
eac_) is left holding the now-dangling native pointer because it was never told to drop it before the free (inferred: the diff shows the missing notification but not the specific Java field). - Dereference after free
A later exclusive-access operation dispatches through that stale native pointer into the freed
ExclusiveAccessManagerAndroid, producing the use-after-free the advisory describes (inferred as to the exact call site, which is not in the diff). - Escalation Controlling the freed-then-reallocated memory could yield a corrupted-object read/write primitive and further compromise of the browser process (inferred; the patch demonstrates only the UAF, not any exploitation primitive).
Impact Assessment
ExclusiveAccessManagerAndroid C++ object in the browser process, reached through a Java-held native pointer that was not invalidated before delete this. Realizing it requires driving the manager’s Destroy path and then causing a subsequent access through the stale pointer. Any further consequence — memory-corruption primitive, controllable read/write, or code execution — is (inferred) and not demonstrated by the diff; the advisory characterizes the issue as a use-after-free in Fullscreen and does not, in the provided metadata, report in-the-wild exploitation.Changed Functions
| Function | Change | Notes |
|---|---|---|
ifchrome/browser/ui/android/exclusive_access/exclusive_access_manager_android.cc |
modified |
Files Changed
chrome/browser/ui/android/exclusive_access/exclusive_access_manager_android.cc
Audit Directions
- `delete this` in JNI-backed objectsAudit every C++ object that Java references via a native pointer and whose destroy method calls
delete this; confirm the Java (or peer) pointer is invalidated before the free, not after or never. - Ordering in teardown pathsLook for destructors and
Destroy/Shutdownmethods where peer-notification and memory release both occur, and verify notification is sequenced strictly before the release on all exit paths. - Null-guarded cleanup membersSearch for cleanup members like
eac_whose invalidation call is missing or conditionally skipped during teardown, since an omitted or mis-orderedmember->Destroy()reproduces this UAF pattern.
Patch
From 3c800c80b475241ebb081b77f401c49c0b3e1a84 Mon Sep 17 00:00:00 2001
From: Muyao Xu <muyaoxu@google.com>
Date: Mon, 15 Jun 2026 17:28:58 -0700
Subject: [PATCH] [Fullscreen] Fix potential UaF in ExclusiveAccessManagerAndroid
This CL ensures the Java layer is notified when the C++ object is
destroyed.
Bug: 523119897
Change-Id: Iceb87e1d207251957346627ef82b0f500531db53
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7947577
Commit-Queue: Muyao Xu <muyaoxu@google.com>
Reviewed-by: Mike Wasserman <msw@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1647206}
---
diff --git a/chrome/browser/ui/android/exclusive_access/exclusive_access_manager_android.cc b/chrome/browser/ui/android/exclusive_access/exclusive_access_manager_android.cc
index c4ac856..de667dd 100644
--- a/chrome/browser/ui/android/exclusive_access/exclusive_access_manager_android.cc
+++ b/chrome/browser/ui/android/exclusive_access/exclusive_access_manager_android.cc
@@ -163,6 +163,11 @@
}
void ExclusiveAccessManagerAndroid::Destroy(JNIEnv* env) {
+ if (eac_) {
+ // Notify the Java counterpart to drop its native pointer before
+ // the C++ object is destroyed, preventing a use-after-free.
+ eac_->Destroy(env);
+ }
delete this;
}
Original Bug Report
Potential Use-After-Free in ExclusiveAccessContextAndroid during tab reparenting
Flapjack, 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. Please see https://chromium.googlesource.com/chromium/src/+/main/docs/security/ai-generated-security-bugs-faq.md for more information.
Overview: A potential Use-After-Free vulnerability exists in the Android Browser process due to incomplete JNI lifecycle synchronization. When ExclusiveAccessManagerAndroid is destroyed, its associated ExclusiveAccessContextAndroid is freed but fails to nullify its Java counterpart’s native pointer. Subsequent touch events on a surviving Tab can trigger JNI calls using this dangling pointer.
Affected files:
chrome/browser/ui/android/exclusive_access/exclusive_access_context_android.ccchrome/android/java/src/org/chromium/chrome/browser/ui/ExclusiveAccessContext.javachrome/browser/ui/android/exclusive_access/exclusive_access_manager_android.cc
Estimated timestamp from git blame: 2026-05-20
Summary
A potential Use-After-Free (UAF) vulnerability exists in the Android implementation of ExclusiveAccessContextAndroid. When the ExclusiveAccessManagerAndroid is destroyed (e.g., during Activity teardown), it frees its ExclusiveAccessContextAndroid member. However, it fails to invoke the Destroy(JNIEnv*) method to notify the Java layer. Consequently, the Java ExclusiveAccessContext retains a dangling pointer to the freed C++ memory and leaks an ActivityTabTabObserver attached to the current Tab.
Because Tab objects frequently outlive the Activity (such as during screen rotation, multi-window moves, or tab reparenting), the leaked observer remains active. If the user touches the preserved tab, the observer’s onTouchDown() callback fires and makes a JNI call (onExclusiveAccessUserInput) using the dangling pointer, resulting in a UAF in the privileged Browser process.
Potential Trigger Steps
Note: These are suggested steps based on code analysis; our tooling agent does not have the ability to run a working Proof of Concept (PoC).
- An attacker hosts a malicious webpage and entices a user to visit it.
- The webpage requests fullscreen mode (
document.documentElement.requestFullscreen()), triggering the creation of the JavaExclusiveAccessManagerand its C++ counterparts. - During initialization, a raw C++ pointer is passed to Java as a
jlong(mNativeExclusiveAccessContextAndroid), and anActivityTabTabObserveris registered on the activeTab. - The attacker triggers a configuration change or tab reparenting event that causes the current
Activityto be destroyed while preserving theTab. ExclusiveAccessManagerAndroid::Destroydeletes itself and itsstd::unique_ptr<ExclusiveAccessContextAndroid> eac_, freeing the C++ context. However, it misses a call toeac_->Destroy(env).- The Java
ExclusiveAccessContext.destroy()is never called. ThemNativeExclusiveAccessContextAndroidpointer remains non-zero, and the observer is never unregistered from theTab. - The attacker uses JavaScript in the surviving Tab to perform heap grooming, replacing the freed
ExclusiveAccessContextAndroidwith attacker-controlled data. - The attacker prompts the user to interact with the webpage (e.g., touching a button).
- The touch event triggers
onTouchDown()on the leaked Java observer, which makes a JNI call using the dangling pointer. - The JNI wrapper casts the
jlongto a raw C++ pointer and callsExclusiveAccessContextAndroid::OnExclusiveAccessUserInput(JNIEnv*). - The method accesses the attacker-forged object, dereferences its
exclusive_access_bubble_pointer, and calls the virtual methodShow(), allowing the attacker to hijack the vtable and achieve Remote Code Execution (RCE).
Impact
This vulnerability allows for arbitrary Remote Code Execution (RCE) in the privileged Browser process, effectively bypassing the sandbox. MiraclePtr (BackupRefPtr) does not protect against this UAF because the reference crossing the JNI boundary is held as a primitive jlong rather than a raw_ptr, and the C++ side drops all raw_ptr references upon destruction, allowing PartitionAlloc to fully free the memory.
Suggested Fix
Explicitly invoke the Destroy method on the ExclusiveAccessContextAndroid object before it is deleted.
In chrome/browser/ui/android/exclusive_access/exclusive_access_manager_android.cc:
void ExclusiveAccessManagerAndroid::Destroy(JNIEnv* env) {
if (eac_) {
eac_->Destroy(env);
}
delete this;
}
Alternatively, call Destroy() from within ExclusiveAccessContextAndroid::~ExclusiveAccessContextAndroid(), but ensure a valid JNIEnv is correctly attached or retrieved.
Evaluated with Chrome root at commit: 2155cb00003ec35716a76ed3246eae995f87b7ff
Results so far have been promising, but there can be wrong deductions. Feel free to adjust as follows:
- If you are familiar with the severity guidelines, you may adjust the severity.
- If this is a false positive, and there’s no work to be done, please close as WAI.
- If there is work to do here but not a vulnerability, please change the issue type to Task/Bug/FR.
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.