Showing posts with label STA. Show all posts
Showing posts with label STA. Show all posts

Google OSS VRP Pause What It Means for STA Research

Sunday, October 04, 2026

Google's OSS VRP Pause: What It Means for STA Research and the Disclosure Ecosystem

On 1 October 2026, Google announced that it would stop accepting product vulnerability submissions to its Open Source Software Vulnerability Reward Program (OSS VRP). The reason: an overwhelming influx of AI-generated reports, including hallucinations, non-reproducible findings, and automated submissions that consumed engineering time without adding security value. The pause took effect the same day and is expected to last until at least Q1 2027.

This is not an isolated incident. Intel had already suspended its bug bounty program. Linux maintainers reported being flooded with AI-generated CVE reports, with some projects dropping support for older drivers as a direct consequence. The bug bounty ecosystem is buckling under the weight of automated noise.

But this post is not about those programs. It is about what the pause means for a specific, ongoing investigation, STA Research, and for the broader problem of how legitimate security findings survive in an environment saturated with AI-generated noise.


1. What exactly was suspended

Three distinctions matter:

Program Status Scope
OSS VRP: product vulnerabilities Suspended as of 1 Oct 2026 Code flaws, logic errors, design defects in Google's public repositories
OSS VRP: supply chain reports Active Dependencies, build pipelines, third-party components
Android VRP Active Android platform, Pixel firmware, Titan M security chip
Chrome VRP Active Chrome browser, Chromium
Cloud VRP Active Google Cloud products
Patch Rewards Program Active Proactive security improvements in open source

The suspension does not affect reports submitted before 1 October 2026, and it does not affect the Android VRP or Chrome VRP programs. Google has explicitly encouraged researchers to use Cloud VRP and Patch Rewards as alternative channels.


2. The case that matters: A-477279924

STA Research has an open case in the Google Android VRP: A-477279924, submitted on 20 January 2026. The case documents a class of defects in AOSP's handling of structured text across Binder boundaries, including unbounded TaskInfo serialization, missing size gates in Task.fillTaskInfo(), and the cascade of failures that follows when a single oversized task state is materialized repeatedly across multiple framework interfaces.

The case is currently blocked on an internal dependency (477593694), which according to my analysis corresponds to AOSP CL 3989977, a fix that exists internally at Google but has not been made public.

A-477279924 is not in the suspended program. It lives in Android VRP, not OSS VRP. But the pause matters because:

  1. Shared triage resources. The engineers and maintainers who process Android VRP reports are part of the same security organization that has been overwhelmed by OSS VRP submissions. The burnout described in Google's announcement is not confined to one program.
  2. Guilt by association. A 40-page technical report with multiple vectors, complete stack traces, and AOSP source verification is exactly the kind of submission that a triage engineer, saturated by AI-generated noise, might mistake for automation. The risk is not that the work is invalid. The risk is that the signal is lost in the noise.
  3. The fix is already internal. The fact that AOSP CL 3989977 exists means Google has already identified the defect. The question is whether the OSS VRP pause delays its public release, or whether it accelerates internal prioritization to close the loop without public disclosure.

3. Why this affects every legitimate researcher

The OSS VRP pause is a symptom of a larger problem: the cost of generating a report has collapsed, but the cost of validating one has not.

Before large language models, writing a vulnerability report required understanding the code, reproducing the behavior, and articulating the impact. The effort served as a natural filter. Today, a model can produce a 3,000-word report with plausible-sounding analysis and fabricated stack traces in seconds. The human reviewer still has to read it, verify it, and reject it, consuming the same time whether the report is real or hallucinated.

Google's response (pause the program, redesign the intake process, return in Q1 2027) is rational from a resource-management perspective. But it creates a perverse incentive: the noise wins by silencing the signal. Legitimate researchers whose reports are already in the queue, or who discover new vulnerabilities during the pause, have no clear path to disclosure through the affected program.

The alternatives Google suggests (Cloud VRP, Patch Rewards) are valid, but they do not cover the same scope. A finding in AOSP's frameworks/base that affects Android devices is not automatically a Cloud VRP issue. The Patch Rewards Program rewards proactive fixes, not vulnerability reports. The gap is real.


4. What this means for STA Research

STA has always been a long-term investigation. The initial report was submitted in January 2026. The finding has been extended through 8 reproduction sessions, 31 documented crash instances, and verification against AOSP source code. The work predates the OSS VRP pause by months.

Three practical consequences:

1. The Android VRP case remains active, but patience is required.
A-477279924 is not suspended. The internal dependency (477593694) is the bottleneck, not the program status. Follow-up should continue, but expectations should be calibrated to the broader organizational pressure.

2. Public documentation becomes more important, not less.
When official channels are congested, the blog and the GitHub repository become the primary record. The technical report is published. The PoC is published. The evidence is timestamped. The absence of a public patch does not mean the finding is invalid. It means the system is slow.

3. Alternative disclosure channels matter.
The CVE assignment process through INCIBE to MITRE is independent of Google's VRP infrastructure. The HackerOne dispute for Xiaomi (#4064049) follows a different path. These are not substitutes for Android VRP, but they are parallel tracks that do not depend on the paused program.


5. The bigger question: how does disclosure survive AI noise?

The OSS VRP pause is not the end of bug bounties. It is an inflection point. The industry is being forced to redesign intake, triage, and validation processes around a new reality: automated report generation is cheap, and human review is not.

Some possible directions:

  • Proof-of-work for reports. Require a reproducible PoC, a signed environment fingerprint, or a hash-verified test case before a report enters the queue.
  • Tiered intake. Separate "first-pass automated scan" reports from "human-verified, reproducible" reports, with different processing paths.
  • Reputation systems. Weight reports by the researcher's history of valid findings, with new submitters routed through stricter validation.
  • AI-assisted triage. Use models to pre-filter obvious hallucinations, but with the understanding that this creates new failure modes.

None of these are perfect. All of them shift the burden somewhere. But the current model, where a single invalid report costs the same to process as a valid one, is not sustainable.


6. What this means for an independent researcher

There is a version of this article that would end at section 5, with the systemic analysis complete and the policy recommendations neatly laid out. That version would be accurate. It would also be incomplete, because it would leave out the part that matters most to those of us who do this work without institutional backing.

The asymmetry was already there before AI. Researchers working at large security firms have salaries, legal teams, PR departments, and colleagues who can validate a finding before it goes out. An independent researcher has a laptop, a test device, and a blog. When a report is ignored, misclassified, or closed without explanation, the institutional researcher moves on to the next thing on their employer's roadmap. The independent researcher carries the cost personally: the hours spent, the reputational risk of publishing, the ambiguity of not knowing whether the work will ever be acknowledged.

AI noise did not create this asymmetry. It made it worse.

The verification burden falls hardest on those with the least capacity to bear it. When triage teams are overwhelmed, the natural response is to apply stricter filters. That is rational. But stricter filters favor submissions that come with institutional credibility markers: company letterheads, internal references, coordinated timelines. A careful independent report, correctly evidenced but unadorned, is more likely to be filtered out than a mediocre report from a known entity. The playing field tilts further, not less.

And then there is the money. Or rather, the absence of it. Bug bounty payouts are inconsistent, but they exist. CVE assignment does not pay anything. Blog readers do not fund research. This work is not a job. There is no salary, no retainer, no consulting fee waiting at the end of a successful reproduction. When the STA investigation was submitted to Google in January 2026, the acknowledgment came with a partial payment of $250. That amount does not begin to cover the hours invested: 8 reproduction sessions across 4 weeks, 31 documented crash instances, 8 bugreports collected by hand, hundreds of logcat lines read one at a time, AOSP source verified commit by commit against android.googlesource.com. If this were billed at any professional rate, the invoice would be five figures. It was not billed. It was never going to be.

This is not a complaint about a specific payout being too low. It is a description of how the economics actually work. The bug bounty programs exist to incentivize disclosure, and for some researchers at some companies, they do. But for the independent researcher working on a class of defects that do not fit neatly into a single CVE, the payout, when it comes, is a symbolic acknowledgment, not compensation. The reward is the recognition. If the recognition does not come, nothing does.

Reputation becomes the only currency, and it is hard to build from scratch. For someone with prior credits from Microsoft, Google, and Mozilla, a closed case is a setback but not a career risk. For someone starting today, without those credits, a single misclassification can be the end of the attempt. The OSS VRP pause raises the barrier to entry not by making the work harder, but by making the acknowledgment of the work less likely. That is a cost the industry is paying without fully pricing it in.

Public documentation is the only defense that does not depend on anyone's goodwill. This is why I keep writing the blog. This is why the GitHub repository exists. This is why every bugreport, every stack trace, every timestamp, every hash is preserved in the open. Not because publication is a threat to anyone, but because publication is the only record that survives a program's silence. When Google pauses OSS VRP, when Xiaomi closes a case as "not applicable" while confirming an internal ticket, when MITRE takes months to assign a CVE, the blog is what remains. It is not a substitute for official acknowledgment, but it is not dependent on it either.

What sustains independent research is not the payout. It is the craft. It is the satisfaction of finding something real, of documenting it correctly, of leaving a trail that another researcher can follow. It is the conversations with people who understand why a 124-byte transaction failing in the same binder session as a 1.1 MB transaction is interesting, when almost no one else does. It is the small community of researchers who recognize each other's work not because a program validated it, but because the work itself stands on its own.

I am not writing this to complain. I am writing it because the current conversation about AI noise in bug bounties is happening mostly among people whose institutional positions protect them from the consequences. The companies will adapt. The programs will redesign. The platforms will survive. The question that is not being asked loudly enough is: what happens to the researchers who cannot wait for the redesign?

Some will move on to other work. Some will publish less. Some will stop. The loss will not be visible in any quarterly report, because the value of a finding that was never made is not measurable. But it is real, and it is being priced into the ecosystem silently.

For my part, I intend to keep going. STA has too much unfinished business: the CVE assignment through INCIBE and MITRE, the HackerOne dispute with Xiaomi, the follow-up on A-477279924, the reproduction sessions that keep adding new evidence. None of that depends on Google's VRP status. All of it depends on doing the work and documenting it honestly. And none of it pays in any direct sense. That is not the point. The point is that the work is worth doing, and someone has to do it.

The pause is a setback. It is not the end of the investigation.


7. What I am doing now

While the OSS VRP pause runs its course:

  • The Android VRP case remains open. I will continue to follow up on A-477279924 and the internal dependency.
  • Public documentation continues. The blog and GitHub repository are the primary record of the investigation.
  • CVE assignment proceeds through INCIBE to MITRE. This path does not depend on Google's VRP status.
  • The HackerOne dispute for Xiaomi (#4064049) remains active. The contradiction between "not applicable" and the internal ticket XS-112757 is documented.
  • New reproduction sessions continue. The finding is not static; the evidence base grows.

8. A note on the irony

The STA research documents a class of defects that arise when structured input is allowed to propagate without size constraints across processing layers. The OSS VRP pause is a different manifestation of the same underlying pattern: an unconstrained input channel (automated report generation) overwhelming a downstream processing layer (human triage), producing a cascading failure (program suspension).

The difference is that in STA, the fix is technical: bound the field, gate the transaction, degrade gracefully. In the bug bounty ecosystem, the fix is procedural: redesign intake, separate signal from noise, restore the economic asymmetry between generating a report and validating one.

Both problems are solvable. Neither will be solved by pretending the input is not there.

AI can exponentially expand the search surface, but security still depends on the ability to demonstrate causality, reproducibility, and impact.

Unbounded TaskInfo serialization in ActivityTaskManagerService

Saturday, September 26, 2026
Unbounded TaskInfo serialization in ActivityTaskManagerService

Unbounded TaskInfo serialization in ActivityTaskManagerService leads to persistent-process (SystemUI) crash loop and device-level denial of service

Reporter: Manuel García Peña ("lostmon") — independent security researcher

Contact: lostmon.blogspot.com

Project reference: STA Research (Structured Text Amplification)

Date: 20 September 2026

Component: Android Open Source Project — frameworks/base (ActivityTaskManagerService, Task, TaskOrganizerController, TaskInfo)

Affected versions: Confirmed on Android 16 (device build below); the root cause traces to a serialization path that has been present, in incomplete form, since at least the March 2020 commit referenced in §5 — likely affects a broad range of Android versions. Requires confirmation by Google against the AOSP version matrix.

Test device: Redmi Note 14 5G (citrine_eea_global), build BP2A.250605.031.A3 / HyperOS 3.0 (OS3.0.302.0.WOQEUXM), Android 16.


This report is the Nth instance in the STA (Structured Text Amplification) series, a four-year line of research documenting a recurring class of defect in AOSP and adjacent systems: unbounded serialization or amplification of user-influenceable, variable-size fields across a translation boundary, followed by exhaustion of a downstream resource. Prior STA instances in AOSP include STA-015 (RecentTasksController/SystemUI) and STA-015-DL (SystemUI via Google Drive → browser → TaskPersister), both of which share the same root cause family as the present case: an oversized Task-related object crossing Binder into a persistent platform process. The present defect should therefore be read not as an isolated bug in Task.fillTaskInfo(), but as further evidence that AOSP's parcel/serialization model does not impose a default upper bound on attacker-influenceable fields. The remediation in §8 is formulated accordingly: a point fix for TaskInfo, and a structural proposal for frameworks/base.


1. Summary

Task.fillTaskInfo() serializes a Task's Intent data Uri and capturedLink field into TaskInfo/RootTaskInfo/RunningTaskInfo/RecentTaskInfo without any upper bound on size. This object is transferred over Binder both when the platform pushes task-organizer callbacks (ITaskOrganizer.onTaskInfoChanged, addStartingWindow) and when a client pulls task state (getFocusedRootTaskInfo, getTasks, getRecentTasks). When the underlying Intent/link data is large enough, the resulting Binder transaction exceeds the transaction buffer and fails with TransactionTooLargeException (push direction) or leaves the caller in a state where subsequent reads fail with DeadObjectException/DeadSystemException (pull direction, once system_server itself becomes unable to service the request).

SystemUI, a platform-critical, persistent process, does not degrade gracefully when these calls fail. The uncaught/unhandled exception propagates up through multiple independent SystemUI components that all query "current focused task" or "recent tasks," causing SystemUI to be killed and restarted repeatedly by ActivityManagerService ("Process com.android.systemui has crashed too many times, killing!"), which in turn triggers the platform's RescueParty self-healing subsystem through an escalating series of mitigations. Depending on how many times the trigger repeats, the outcome ranges from a forced device reboot to an OEM-level "safe mode" recovery requiring manual user intervention.

This is reachable with a single user tap on a link that opens in the default browser — no special permissions, no user interaction beyond a single tap, fully reproducible.


2. Affected code paths (AOSP frameworks/base)

PathDirectionAPI
services/core/java/com/android/server/wm/TaskOrganizerController.javaPushITaskOrganizer.onTaskInfoChanged, addStartingWindow
services/core/java/com/android/server/wm/ActivityTaskManagerService.javaPullgetFocusedRootTaskInfo
core/java/android/app/ActivityTaskManager.javaPullgetTasks, getRecentTasks
services/core/java/com/android/server/wm/Task.javaShared serialization pointfillTaskInfo(), trimIneffectiveInfo()
core/java/android/app/TaskInfo.javaShared serialization pointwriteTaskToParcel(), field capturedLink

All five paths converge on Task.fillTaskInfo(), which is the single point where the defect should be fixed.


3. Root cause

3.1 Task.fillTaskInfo() does not bound the Intent data Uri or capturedLink

void fillTaskInfo(TaskInfo info, boolean stripExtras, @Nullable TaskDisplayArea tda) {
    ...
    final Intent baseIntent = getBaseIntent();
    final int baseIntentFlags = baseIntent == null ? 0 : baseIntent.getFlags();
    info.baseIntent = baseIntent == null
            ? new Intent()
            : stripExtras ? baseIntent.cloneFilter() : new Intent(baseIntent);
    info.baseIntent.setFlags(baseIntentFlags);
    ...
    // capturedLink is not cleared or bounded here; it is only nulled in
    // trimIneffectiveInfo() for non-privileged callers.
}

3.2 The 2020 mitigation is incomplete — verified against source

Commit 66b08f0201877e6013058fda2f0263016338c770 ("Reduce parceled data size between system & sysui", Winson Chung, 3 March 2020, Bug: 150242007) changed:

- info.baseIntent = baseIntent == null ? new Intent() : new Intent(baseIntent);
+ info.baseIntent = baseIntent == null ? new Intent() : baseIntent.cloneFilter();

This removes Bundle extras and ClipData from the serialized baseIntent. It does not bound:

  • the Intent's data Uri (cloneFilter() explicitly preserves action, data, type, categories, flags, package, component);
  • the capturedLink field, which is a separate TaskInfo field entirely untouched by this commit and only cleared in trimIneffectiveInfo() for non-privileged callers — SystemUI and TaskOrganizer receive it unfiltered.

The test added in the same commit, testTaskInfo_expectNoExtras, only asserts that baseIntent.getExtras() is null/empty — it does not assert any bound on data-field length.

3.3 TaskInfo.writeTaskToParcel() has no length limit on variable-size fields

dest.writeTypedObject(baseIntent, 0);       // data Uri, post-cloneFilter, unbounded
dest.writeTypedObject(capturedLink, flags); // full Uri when not trimmed, unbounded

3.3 trimIneffectiveInfo() clears capturedLink only for non-privileged callers

info.capturedLink = null;
info.capturedLinkTimestamp = 0;

This clearing is applied only when the caller is not privileged. SystemUI and TaskOrganizer — precisely the components affected by this defect — are privileged callers and receive capturedLink unfiltered.

3.4 TaskInfo.writeTaskToParcel() has no length limit on variable-size fields

dest.writeTypedObject(baseIntent, 0);       // data Uri, post-cloneFilter, unbounded
dest.writeTypedObject(capturedLink, flags); // full Uri when not trimmed, unbounded

RootTaskInfo adds fixed-size bounds/children fields and then delegates the rest to the same writeTaskToParcel().

3.5 Confluence of push and pull paths

                    ├─ onTaskInfoChanged / addStartingWindow   (push, §5.1)
Task.fillTaskInfo ──┼─ getFocusedRootTaskInfo                  (pull, §5.2, variants A/B)
                    ├─ getTasks                                (pull, §5.2, variant C)
                    └─ getRecentTasks                          (pull, §5.2, variant E)

This explains why five structurally distinct SystemUI call sites (different classes, different threads, different public APIs) all fail with the same root cause: every one of them ultimately calls Task.fillTaskInfo()/writeTaskToParcel() on the same oversized Task object.

3.6 SystemUI does not degrade gracefully on ATMS binder failure

Independently of the above, the Binder client side of this contract — SystemUI's various components that call getFocusedRootTaskInfo/getTasks/getRecentTasks — does not catch DeadObjectException/DeadSystemException and fall back to a last-known-good state or a safe default. The exception propagates and crashes the calling component, and because these components (Dynamic Island top-activity tracking, notification shade token interactor, recents controller) are on SystemUI's main/critical threads, repeated failures cause ActivityManagerService to conclude the persistent process is unrecoverable and kill it outright.


4. Reproduction

  1. Construct an Intent/link whose data Uri (and/or any field that populates TaskInfo.capturedLink) is large — on the order of hundreds of kilobytes. In our reproduction this was delivered as a link inside an HTML document opened via a cloud-storage viewer, which the OS resolved to the default browser (Firefox, in our test — confirmed not to be the origin of the fault; see §7).
  2. The browser is launched as a new Task; system_server/ATMS retains this oversized data as part of the Task's baseIntent/captured-link state.
  3. As TaskOrganizerController dispatches pending task-info-changed / starting-window events for this task, and/or as SystemUI's focus/recents observers poll getFocusedRootTaskInfo/getTasks/getRecentTasks, the oversized TaskInfo is serialized to Binder.
  4. Observe TransactionTooLargeException (push) and/or DeadObjectException/DeadSystemException (pull) in dumpsys dropbox -p system_app_crash and/or logcat.
  5. Repeat exposure (re-opening the link while the browser remains foregrounded) escalates the failure into a sustained SystemUI crash loop.

We can provide the full sequence of bugreports (adb bugreport, 4 captures across 2 dates on the same device/build) documenting both directions with complete stack traces on request through the appropriate secure channel. We are withholding the exact payload construction from this initial report per responsible-disclosure practice; happy to share it privately with the assigned engineer.


5. Evidence (redacted stack excerpts)

5.1 Push — TransactionTooLargeException, kernel-confirmed (16-09-2026, 11:26–11:28, same device)

TaskOrganizerController: android.os.TransactionTooLargeException: data parcel size 365036 bytes
    at android.window.ITaskOrganizer$Stub$Proxy.onTaskInfoChanged(ITaskOrganizer.java:513)
    at com.android.server.wm.TaskOrganizerController$TaskOrganizerCallbacks.onTaskInfoChanged(TaskOrganizerController.java:191)
    ...
    at com.android.server.wm.RootWindowContainer.performSurfacePlacementNoTrace(RootWindowContainer.java:923)
    at com.android.server.wm.WindowSurfacePlacer.performSurfacePlacementLoop(WindowSurfacePlacer.java:173)

Kernel binder driver, independently confirming the same failure:

binder: 2124:3180 transaction async to 20444:0 failed 7086360/29201/-28, code 1 size 365788-48 line 3548
binder: 2124:2173 transaction async to 20444:0 failed 7086449/29201/-28, code 7 size 365036-40 line 3548

ActivityManager log line for the same event: pid 2124 system sent binder code 7 with flags 1 to frozen apps and got error -2147483646 — the target process (SystemUI) was in the platform's cached/frozen state at time of delivery, which appears to aggravate the failure.

5.2 Pull — complete evidence set: 25 recorded instances across 3 sessions, 5 distinct call sites

All 25 instances were extracted from dumpsys dropbox -p system_app_crash across three independent bugreports on the same device/build (19–20 September 2026). Every instance shares the same two root exceptions (DeadObjectException or DeadSystemException); they differ only in which SystemUI component happened to be polling task state at the moment system_server/ATMS became unable to service the request.

Frequency by call site:

VariantComponentAPIInstances
Ccom.android.systemui.shared.system.ActivityManagerWrapper.getRunningTask (via com.miui.systemui.functions.MiuiTopActivityObserver)getTasks12
Acom.android.systemui.statusbar.notification.policy.DynamicIslandTopActivityControllergetFocusedRootTaskInfo5
Bcom.miui.systemui.statusbar.shade.domain.interactor.ShadeStatusBarTokenInteractorgetFocusedRootTaskInfo5
Dcom.android.systemui.statusbar.notification.InstantAppNotifier(stack truncated by system, root cause DeadSystemException)2
Ecom.android.wm.shell.recents.RecentTasksController.getRecentTasksgetRecentTasks1

Complete timestamped log (25/25 instances, PID and exception type):

2026-09-19 23:32:04  PID 2858   DeadObjectException      Variant A
2026-09-19 23:39:14  PID 21592  DeadSystemException       Variant D
2026-09-19 23:39:28  PID 29332  DeadSystemException       Variant C
2026-09-19 23:39:40  PID 29982  DeadObjectException       Variant B
2026-09-19 23:39:49  PID 30266  DeadSystemException       Variant D
2026-09-19 23:39:56  PID 30579  DeadSystemException       Variant C
2026-09-19 23:42:14  PID 5351   DeadSystemException       Variant C
2026-09-20 00:25:16  PID 2847   DeadSystemException       Variant D
2026-09-20 00:25:24  PID 29311  DeadSystemException       Variant C
2026-09-20 00:25:30  PID 29616  DeadSystemException       Variant C
2026-09-20 00:25:33  PID 29806  DeadSystemException       Variant C
2026-09-20 00:25:39  PID 29952  DeadSystemException       Variant C
2026-09-20 00:25:46  PID 30137  DeadSystemException       Variant C
2026-09-20 14:10:27  PID 2558   DeadObjectException       Variant B
2026-09-20 14:10:44  PID 22995  DeadObjectException       Variant A
2026-09-20 14:10:47  PID 23667  DeadSystemException       Variant C
2026-09-20 14:10:55  PID 24079  DeadSystemException       Variant C
2026-09-20 14:11:09  PID 24700  DeadObjectException       Variant A
2026-09-20 14:11:20  PID 25138  DeadObjectException       Variant B
2026-09-20 14:32:12  PID 26950  DeadObjectException       Variant B
2026-09-20 14:32:28  PID 6263   DeadObjectException       Variant B
2026-09-20 14:32:31  PID 6809   DeadSystemException       Variant C
2026-09-20 14:32:46  PID 6983   DeadObjectException       Variant A
2026-09-20 14:32:52  PID 7317   DeadSystemException       Variant C
2026-09-20 14:33:01  PID 7535   DeadSystemException       Variant E

Entry #25 (14:33:01, RecentTasksController.getRecentTasks) is the last crash of the entire sequence, immediately before the device stabilized — the fault is not limited to "resolve current focus," it extends to enumerating the recent-tasks list in general, which we consider additional evidence that the defect is in the shared Task/TaskInfo state rather than in any single SystemUI component's handling of it.

Full stack — Variant A (DynamicIslandTopActivityController):

android.os.DeadObjectException: Transaction failed on small parcel; remote process
probably died, but this could also be caused by running out of binder buffer space
    at android.os.BinderProxy.transactNative(Native Method)
    at android.os.BinderProxy.transact(BinderProxy.java:736)
    at android.app.IActivityTaskManager$Stub$Proxy.getFocusedRootTaskInfo(IActivityTaskManager.java:3682)
    at com.android.systemui.statusbar.notification.policy.DynamicIslandTopActivityController
        $special$$inlined$mapNotNull$1$2.emit(...)
    at kotlinx.coroutines.flow.SharedFlowImpl.collect$suspendImpl(...)

Full stack — Variant B (ShadeStatusBarTokenInteractor):

android.os.DeadObjectException: Transaction failed on small parcel; remote process
probably died, but this could also be caused by running out of binder buffer space
    at android.os.BinderProxy.transactNative(Native Method)
    at android.os.BinderProxy.transact(BinderProxy.java:736)
    at android.app.IActivityTaskManager$Stub$Proxy.getFocusedRootTaskInfo(IActivityTaskManager.java:3682)
    at com.miui.systemui.statusbar.shade.domain.interactor.ShadeStatusBarTokenInteractor
        $special$$inlined$mapNotNull$1$2.emit(go/retraceme...:62)
    at com.miui.systemui.statusbar.shade.domain.interactor.ShadeStatusBarTokenInteractor
        $special$$inlined$filter$1$2.emit(go/retraceme...:64)
    at kotlinx.coroutines.flow.SharedFlowImpl.collect$suspendImpl(go/retraceme...:194)

Full stack — Variant C (ActivityManagerWrapper.getRunningTask, the most frequent — 12/25):

android.os.DeadSystemRuntimeException: android.os.DeadSystemException
    at android.app.ActivityTaskManager.getTasks(ActivityTaskManager.java:480)
    at android.app.ActivityTaskManager.getTasks(ActivityTaskManager.java:454)
    at com.android.systemui.shared.system.ActivityManagerWrapper.getRunningTask(...:5)
    at com.miui.systemui.functions.MiuiTopActivityObserver$$ExternalSyntheticLambda0.run(...:71)
    at android.os.Handler.handleCallback(Handler.java:1029)
    at android.os.Handler.dispatchMessage(Handler.java:107)
    at android.os.Looper.loopOnce(Looper.java:274)
    at android.os.Looper.loop(Looper.java:369)
    at android.os.HandlerThread.run(HandlerThread.java:85)
Caused by: android.os.DeadSystemException
    ... 9 more

Full stack — Variant D (InstantAppNotifier):

android.os.DeadSystemRuntimeException: android.os.DeadSystemException
    at com.android.systemui.statusbar.notification.InstantAppNotifier$$ExternalSyntheticLambda0.run(...:49)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1100)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:1572)
Caused by: android.os.DeadSystemException
    ... 4 more

Full stack — Variant E (RecentTasksController.getRecentTasks, the final crash of the sequence):

android.os.DeadSystemRuntimeException: android.os.DeadSystemException
    at android.app.ActivityTaskManager.getRecentTasks(ActivityTaskManager.java:539)
    at com.android.wm.shell.recents.RecentTasksController.getRecentTasks(...:10)
    at com.android.wm.shell.recents.RecentTasksController$IRecentTasksImpl$$ExternalSyntheticLambda1.accept(...:11)
    at com.android.wm.shell.common.ExternalInterfaceBinder$$ExternalSyntheticLambda0.run(...:18)
    at com.android.wm.shell.common.ShellExecutor$$ExternalSyntheticLambda0.run(...:5)
    at android.os.Handler.handleCallback(Handler.java:1029)
    at android.os.Handler.dispatchMessage(Handler.java:107)
    at android.os.Looper.loopOnce(Looper.java:274)
    at android.os.Looper.loop(Looper.java:369)
    at android.os.HandlerThread.run(HandlerThread.java:85)
Caused by: android.os.DeadSystemException
    ... 10 more

5.3 Terminating action (ActivityManagerService)

Captured live during the 20-09-2026 14:32 reproduction:

14:32:52.874  ActivityManager: Process com.android.systemui has crashed too many times, killing! Reason: crashed quickly
14:32:52.874  RescueParty: Checking available remediations for health check failure. failedPackage: com.android.systemui failureReason: 3 available impact: 50
14:32:52.884  RescueParty: Executing remediation. failedPackage: com.android.systemui failureReason: 3 mitigationCount: 3

RollbackPackageHealthObserver reports available impact: 0 for the same event — no Mainline/APEX rollback candidate exists for com.android.systemui on this build, so the platform's self-healing falls through entirely to the OEM's own escalation logic (documented in the companion Xiaomi report).


6. Impact

  • Class: Denial of service, platform-level.
  • User interaction: One tap on an attacker-controlled or attacker-influenced link.
  • Privileges required: None.
  • Effect: Forced lockscreen, sustained SystemUI crash loop, and — depending on repeat exposure — full device reboot or entry into an OEM-defined degraded/safe-mode recovery state requiring the user to manually restart the device. No code execution is claimed.
  • Reliability: Deterministic across 25/25 observed crash instances in our testing; consistent across two dates four days apart on the same build.

7. Note on browser involvement

We initially suspected the browser itself (Chrome) was implicated, based on a temporally-adjacent ANR in our first capture. On closer inspection of the activity-resume records across all sessions, the browser that actually receives the triggering intent in every case is Firefox (org.mozilla.fenix.IntentReceiverActivity). Firefox shows no crash, ANR, or tombstone of its own in any of the four bugreports we collected — it is the intent receiver, not a party to the defect. We flag this so the assigned engineer does not spend time chasing a red herring; the defect is entirely within the ATMS/SystemUI Binder contract described above.


8. Suggested remediation

  1. Introduce a length bound on the Intent data Uri and capturedLink in Task.fillTaskInfo() / TaskInfo.writeTaskToParcel() — truncate, hash, or replace with a placeholder above a reasonable threshold before serialization, extending the intent of commit 66b08f0 to cover these two fields.
  2. Have TaskOrganizerController measure/cap the serialized size of a TaskInfo before dispatching push callbacks, rather than relying on the caller to catch TransactionTooLargeException after the fact.
  3. Harden SystemUI's focus/recents observers (and any similar persistent-process client of these APIs) to catch DeadObjectException/DeadSystemException and fall back to last-known-good state instead of propagating the exception to a crash.

9. Attachments available on request

  • 4 full bugreport captures (device: Redmi Note 14 5G, build BP2A.250605.031.A3), dated 16-09-2026 and 20-09-2026 (×3).
  • Consolidated internal forensic report cross-referencing all four captures against AOSP source.

10. Researcher background

Independent security researcher (handle "lostmon"), President of the BojosXtu association (civic/educational digital-rights work). Blog: lostmon.blogspot.com.

Three Vectors, One Root Cause — STA-003, STA-017, STA-020

Thursday, September 10, 2026

Chrome 152.0.7977.82 and Edge 152.0.4191.53 remain vulnerable to three distinct Structured Text Amplification (STA) vectors on Android 16. A full forensic bugreport captured on September 6, 2026 confirms the failure chain from oversized Bundle to Binder transaction failure to process termination. The September 2026 Android Security Bulletin contains no patches for the affected paths.


1. Executive Summary

Three STA vectors are confirmed reproducible in the two dominant Chromium-based browsers on Android:

Vector Trigger Chrome 152 Edge 2026 Impact
STA-003 Click Share on a crafted link CRASH CRASH Process termination
STA-017 Long-press on a crafted link ANR ANR UI freeze + force finish
STA-020 Focus address bar after crafted URL in history ANR ANR UI freeze + force finish

Architectural root cause: The demonstrated failure paths converge on Android platform components — libminikin.so for text processing and the IActivityTaskManager Binder boundary for oversized transactions. The evidence indicates that browser-level mitigations cannot fully address these platform-level execution paths.


2. Test Environment

ParameterValue
DeviceXiaomi Redmi Note 14 5G
OSAndroid 16, HyperOS 3.0.301.0 (build BP2A.250605.031.A3)
Chrome152.0.7977.82 (stable)
Edge152.0.4191.53 (stable)
Bugreportbugreport-2026-09-06-200012.zip
libminikin.so BuildId4fabe53671b5ead88314c00a1fd6d67d

3. STA-003 — Share Intent Crash

3.1 Trigger

The user clicks Share on a crafted link with an oversized URL. The browser constructs a share Intent carrying the oversized crafted content, which is propagated through the Android activity-start path and ultimately reaches the Binder transaction boundary.

Scope note: In the controlled reproduction, the variable extra content was identified as the crafted STA pattern. The corresponding extra key is redacted in the captured bugreport, so this article does not claim to read the exact extra key from the forensic dump itself.

3.2 Forensic evidence from the bugreport

The Android BaseBundleMonitorImpl (Xiaomi HyperOS) logged the following immediately before the failure:

19:56:56.867  BaseBundleMonitorImpl:
               Large Bundle: length=1531092, bundle=8814309
19:56:56.867  BaseBundleMonitorImpl:
               Large Bundle: length=1532640, bundle=36c790e

Binder then recorded the outgoing transaction and its failure:

Binder transaction failure
id: 12562575
error: -28 (No space left on device)

Large outgoing transaction of 1533260 bytes
interface descriptor: android.app.IActivityTaskManager
code 1

JavaBinder: FAILED BINDER TRANSACTION
parcel size = 1533260

The Java framework then threw the corresponding exception:

android.os.TransactionTooLargeException:
data parcel size 1533260 bytes

  at android.os.BinderProxy.transactNative(Native Method)
  at android.os.BinderProxy.transact(BinderProxy.java:642)
  at android.app.IActivityTaskManager$Stub$Proxy.startActivity(...)
  at android.app.Instrumentation.execStartActivity(...)
  at android.app.Activity.startActivityForResult(...)
  at org.chromium.ui.base.WindowAndroid.R(...)
  ...

Finally, the system terminated the browser process:

wm_finish_activity:
com.microsoft.emmx/org.chromium.chrome.browser.ChromeTabbedActivity, force-crash

am_proc_died:
com.microsoft.emmx

3.3 Chrome confirmation

The same sequence reproduces in Chrome 152 with nearly identical numbers:

TransactionTooLargeException:
data parcel size 1533240 bytes

wm_finish_activity:
com.android.chrome/org.chromium.chrome.browser.ChromeTabbedActivity, force-crash

am_proc_died:
com.android.chrome

Critical observation: Edge failed at 1,533,260 bytes. Chrome failed at 1,533,240 bytes. The difference is 20 bytes. Both browsers reach the same Chromium Activity-start path and both fail at the same Android Binder boundary.


4. STA-017 — Long-Press ANR

4.1 Trigger

The user long-presses a link. The context menu rendering path attempts to measure the anchor text and URL through TextView.onMeasure() → StaticLayout → libhwui → libminikin.so.

4.2 Stack trace (Chrome 152)

"main" prio=5 tid=1 Native   ← UI THREAD BLOCKED
  | state=R

native: minikin::getPrevWordBreakForCache          libminikin.so
native: minikin::StyleRun::getLineMetrics          libminikin.so
native: minikin::MeasuredText::getLineMetrics      libminikin.so
native: minikin::LineBreakOptimizer::computeBreaks libminikin.so
                                                   ← expensive text-processing path
native: minikin::breakLineOptimal                  libminikin.so
native: android::nComputeLineBreaks                libhwui.so

  at android.text.StaticLayout.generate(StaticLayout.java:969)
  at android.widget.TextView.onMeasure(TextView.java:11486)
  at org.chromium.chrome.browser.contextmenu.ContextMenuListView.onMeasure
  at android.view.ViewRootImpl.performTraversals

4.3 System response

ANR in Window ... ChromeTabbedActivity is not responding.
Waited 5000ms for MotionEvent(action=DOWN)

→ Force finishing activity ChromeTabbedActivity
→ Killing process

4.4 Edge confirmation

Edge reproduces the identical path. The ANR subject is:

Input dispatching timed out
(com.microsoft.emmx/...ChromeTabbedActivity is not responding.
 Waited 5000ms for MotionEvent(action=DOWN))

5. STA-020 — Address-Bar Focus ANR

5.1 Trigger

A crafted URL is stored in browser history (e.g. after a previous navigation). The user focuses the address bar. The omnibox suggestion list is rendered through OmniboxSuggestionsContainer.onMeasure() → RecyclerView → TextView.onMeasure() → StaticLayout → libminikin.so.

5.2 Stack trace (Edge 2026-09-06)

"main" prio=5 tid=1 Native
  | state=R

  at android.text.StaticLayout.generate
  at android.text.StaticLayout.
  at android.widget.TextView.onMeasure
  at androidx.appcompat.widget.AppCompatTextView.onMeasure
  at android.widget.LinearLayout.measureVertical
  at org.chromium.chrome.browser.omnibox.suggestions.base.BaseSuggestionView.onMeasure
  at androidx.recyclerview.widget.RecyclerView.onMeasure
  at android.view.ViewRootImpl.performTraversals
  at android.view.Choreographer.doFrame

5.3 System response

ANR in Window ... ChromeTabbedActivity
→ Force finishing activity
→ Killing process

Note: The Edge ANR was captured at 19:40:33 on 2026-09-06 during normal device use — not a synthetic test. It is a spontaneous production capture, which strengthens the reproducibility of the vector.


6. Why Chromium Patches Do Not Fix This

During 2026, Chromium introduced several mitigations for large IPC payloads and oversized text. None of them address the vectors documented above.

Patch Surface STA-003 STA-017 STA-020
SelectionUtils / 100 KB PDF text selection No No No
LargePayloadSupport (FD) Credential Manager No No No
SharedMemory (Union) Native Messaging No No No
Oversized Clipboard (ContentProvider) Clipboard No No No
PdfView anchors PdfView SavedState No No No
TLE telemetry Native Messaging No No No

The vulnerable paths are in Android platform components. They are libminikin.so (text processing) and the IActivityTaskManager Binder boundary (oversized transactions). Chromium cannot patch these paths.


7. September 2026 Android Security Bulletin — No Patches for STA

The September 2026 Android Security Bulletin was published on 8 September 2026. A complete search of the bulletin reveals zero references to the surfaces documented in this paper:

KeywordResults in Bulletin
libminikin0
Binder0
SavedState0
TransactionTooLarge0
StaticLayout, TextView, LineBreakOptimizer0

The bulletin contains dozens of CVEs across Framework, System, and Kernel — including critical RCEs — but none of them address the STA root causes.


8. The Failure Chain (Recap)

STA payload (crafted URL / oversized structured text)
        ↓
User action (Share / long-press / focus address bar)
        ↓
Chromium builds a share Intent or triggers a TextView measurement call
        ↓
Android platform components:
  · StaticLayout → libhwui → libminikin.so   (STA-017 / STA-020)
  · IActivityTaskManager.startActivity() → Binder   (STA-003)
        ↓
Main thread blocked >5 s OR Binder transaction failure
        ↓
ANR  (STA-017 / STA-020)
or
TransactionTooLargeException → Force finishing activity → Killing  (STA-003)

9. Why This Matters

Three points are worth emphasizing:

  1. Two independent browsers. Chrome 152 and Edge 2026 both reproduce all three vectors. This is not a Chrome-specific defect.
  2. Two independent mechanisms. STA-003 fails at the Binder boundary; STA-017 and STA-020 fail inside libminikin. Both mechanisms share the same architectural root: no early length gate before an expensive or bounded operation.
  3. No upstream mitigation. Neither the Chromium 2026 patches nor the September 2026 Android Security Bulletin address the affected paths.

10. Recommendations

10.1 Framework (AOSP / Android)

  • Introduce a global length gate in libminikin::LineBreakOptimizer::computeBreaks() and in the measurement/shaping paths (Layout::measureText, LayoutPiece) before entering expensive operations.
  • Validate the size of an Intent / Bundle before calling IActivityTaskManager.startActivity(), and reject or truncate oversized payloads instead of letting the Binder transaction fail with TransactionTooLargeException.
  • Instrument the affected paths with telemetry so that oversized payloads are detected before they cause user-visible failures.

10.2 Application-level (defensive)

  • Truncate URL / share text to a safe maximum (e.g. 50,000 characters) before passing it to the system share sheet.
  • Truncate suggestion / history text before measuring it in an omnibox or context menu.

These application-level measures reduce the attack surface but do not fix the root cause.


11. Conclusion

Chrome 152.0.7977.82 and Edge 152.0.4191.53 are vulnerable to three STA vectors on Android 16:

  • STA-003 — Share Intent crash (process termination).
  • STA-017 — Long-press ANR (main-thread block in libminikin).
  • STA-020 — Address-bar focus ANR (main-thread block in libminikin).

Forensic evidence from a production bugreport (2026-09-06) confirms the full failure chain from oversized Bundle to Binder transaction failure to process termination. The September 2026 Android Security Bulletin contains no patches for the affected paths.

The evidence indicates that the issue is not confined to a specific browser implementation. Chromium-only mitigations cannot fully address the demonstrated Android text-processing and Binder execution paths. A platform-level fix in AOSP appears necessary.


Upstream Convergence - STA Patterns in Android and Chromium (2026)

Saturday, September 05, 2026

Upstream Convergence — STA Patterns in Android and Chromium (2026)

This article documents a series of upstream commits in Android, AndroidX, and Chromium during 2026 that implement defensive measures around the same Binder/IPC boundaries identified by the Structured Text Amplification (STA) research. While no commit explicitly references STA, the functional convergence is substantial.


1. The Pattern

The STA model describes a recurring architectural failure: structured input → serialisation → Binder/IPC → uncaught exception → crash or ANR. Over the course of 2026, multiple upstream projects have introduced mitigations that directly address this class of problem.

What follows is a non‑exhaustive but representative list of commits that target exactly the surfaces documented in the STA whitepaper.


2. Upstream Mitigations — A Family of Defences

Component Commit / Change Mechanism Date STA Relation
AOSP InputMethod a438ce172b441c8297eadde8d990ab292f5aa7d1 Introduces InputMethodSubtypeSafeList (and AbstractSafeList) to avoid TransactionTooLargeException when large lists are passed over Binder. 7 Jan 2026 (merge) Precedent: changing representation to escape Binder (byte[]/writeBlob)
AndroidX Credential Manager 393e20ae2c23c509df55058e5d7a3157a749e03b Implements LargePayloadSupport: serialises Bundle to temporary file, passes ParcelFileDescriptor instead of raw data. Triggers for responses >200 KB. 8 Apr 2026 Architectural: large IPC → FD
Chromium PDF Selection 84b615a07fc14988b46f0e362502ab4075216793 Refactors and exposes an existing MAX_SHARE_QUERY_LENGTH = 100000 safeguard (already present in SelectionPopupControllerImpl) into SelectionUtils for PDF selection (Share / Search / Translate). Note: the CL was reverted ~1.5h later for an Android Lint issue; the underlying 100 KB limit predates this commit. 5 May 2026 Architecturally related to STA-007 (PDF → Select All → Translate). Shows that Chromium treats selection size as a safety property of the Intent boundary.
AndroidX NotificationCompat 90ffa6a7b02aeefa8f38dc1e54f5740adc18e832 Fixes a TransactionTooLargeException caused by oversized images in compatibility extras. Prevents oversized compat extras from replacing already-resized native extras. 11 May 2026 Architectural: structured extras → Binder → TLE
AndroidX PdfView 8882927e7d41e678c6f03f50d4bd5950e7dc6c47 Fixes TransactionTooLargeException in onSaveInstanceState() by replacing full SelectionModel (>1.2 MB) with lightweight anchor points (~44 bytes) when crossing Binder. 14 Jul 2026 Strong architectural convergence: Class A (SavedState / large structured state → Binder → TLE → placeholder → async restoration).
Chromium Oversized Clipboard 4751a7699c8653c5a944152a4fd78fe97e878885 Adds support for “pasting oversized HTML payloads” via ContentProvider URIs instead of direct transport. Defends against confused deputy attacks. 24 Aug 2026 Architecturally related to STA-011 (Clipboard → assisted paste). Large text/HTML → redirected transport.
Chromium Native Messaging (redesign) f5c51669e832d97728da04c79dd426ac2aa49a60 Changes message representation from String to Union(byte[], SharedMemory). Explicitly targets messages that may exceed the 1 MB Binder limit and cause TransactionTooLargeException. 26 Aug 2026 Strong architectural convergence: redesigning the transport boundary itself (SharedMemory).
Chromium Native Messaging (telemetry) 931ee1abb38c9781b5b1470ba64c99afb198ed64 Adds telemetry for SentMessageSize and explicitly distinguishes TransactionTooLargeException as a failure mode. 1 Sep 2026 Strong: explicit instrumentation of the same boundary failure.

Note: None of these commits mention STA or the STA whitepaper. They are presented here as convergent engineering — independent mitigations that address the same class of problems documented by the STA research.


3. STA-007: A Clean Upstream Echo

The STA-007 vector describes a chain:

Google Drive → PDF with invisible text → Select All → Translate → TransactionTooLargeException

Chromium’s May 2026 commit (84b615a) does not introduce the 100 KB limit — it reuses and exposes an existing safeguard already present in SelectionPopupControllerImpl. The commit message explicitly references Android Intent size limits (~1 MB) as the reason for the limit.

This is not proof that Chromium acted on STA-007. But the functional alignment is so precise that an engineer reading both documents would immediately recognise the same boundary. The key observation is that Chromium was already treating selection text size as a safety property of the Intent boundary, before the STA research was published.


4. The Asymmetry: libminikin Remains Unaddressed

While multiple upstream projects have implemented defences around Binder/IPC boundaries, no equivalent global length gate has been found in libminikin for the line‑breaking path documented in STA‑017.

  • getPrevWordBreakForCache() still performs backwards scans without a hard input‑length guard.
  • The investigated optimal line-breaking path (LineBreakOptimizer::computeBreaks()) retains nested candidate-processing loops, but no public global input-length gate comparable to the IPC safeguards above was identified.
  • Only a specific hyphenation safeguard exists (words longer than 45 characters), which does not cover the general case.

Key observation: A concentrated set of mitigations is visible around serialisation, clipboard, IPC, and persistence boundaries — while the text‑layout path (libminikin) has not received the same treatment.

4.1 Why libminikin Might Be Different

Unlike Binder/IPC boundaries, which have clear size limits (1 MB) and can be instrumented or redirected, libminikin is a native layout engine with deep roots in Android’s text rendering pipeline. A hard global length gate in computeBreaks() would affect all text rendering — not just URLs or structured payloads — making it a more complex change to validate without breaking existing applications.

This does not excuse the absence of a defence, but it helps explain why the asymmetry exists.


5. Temporal Context

The commits listed above span from November 2025 to September 2026. The STA whitepaper was published on 30 July 2026.

This timeline reveals two distinct waves:

  • Before July 2026: SafeList, LargePayloadSupport, NotificationCompat, and the PDF selection refactor all predate the STA whitepaper. They show that upstream projects were already treating oversized structured payloads as a reliability/security concern.
  • After July 2026: Oversized clipboard (24 Aug), SharedMemory redesign (26 Aug), and TLE telemetry (1 Sep) occur after the STA research became public. They address surfaces that the STA whitepaper explicitly documented.

This distribution makes the hypothesis “all these changes are a reaction to STA” unsustainable. But it also makes a different claim stronger:

“STA was published during a period when upstream was already moving toward explicit size controls, alternative representation, and payload isolation at Binder boundaries. After publication, that trend continued and added changes to surfaces specifically documented by STA.”


6. Summary: Mitigated vs. Unmitigated Surfaces

Surface Mitigation Visible? Mechanism
InputMethod → Binder (large lists) ✅ Yes SafeList → byte[]/writeBlob
Large IPC (Credential Manager) ✅ Yes LargePayloadSupport (FD)
PDF → Share / Search / Translate ✅ Yes Truncation (100 KB limit, refactored into SelectionUtils)
NotificationCompat (oversized images) ✅ Yes Prevents oversized compat extras from replacing native ones
SavedState (PdfView) ✅ Yes Anchor points (~44 bytes) + async restoration
Oversized Clipboard ✅ Yes ContentProvider URI
Native Messaging ✅ Yes SharedMemory + telemetry
libminikin (LineBreakOptimizer) ❌ Not found No global length gate in the investigated path

7. What This Convergence Means

The upstream commits listed above represent a family of defensive engineering decisions, all targeting the same underlying problem:

Large structured payload → Binder/IPC → TransactionTooLargeException → Crash or ANR

The mitigations vary by component, but they follow a consistent pattern:

  • Constrain: limit input size before it reaches the boundary (Chromium PDF selection).
  • Redirect: move payload out of Binder (LargePayloadSupport → FD; Oversized Clipboard → ContentProvider).
  • Replace: replace full state with lightweight placeholders (PdfView → anchor points).
  • Observe: instrument the failure to understand its prevalence (Native Messaging telemetry).

The strongest evidence is not that individual fixes resemble individual STA vectors. It is that multiple upstream projects independently apply the same defensive principle: constrain, redirect, replace, or observe data before an oversized structured payload becomes a failure at an IPC boundary.


8. Conclusion

The STA model identified an architectural pattern: structured input that crosses Binder/IPC boundaries without size validation can cause persistent crashes and ANRs. The upstream commits documented in this article show that:

  1. Multiple components (AndroidX, AOSP, Chromium) have introduced mitigations at exactly those boundaries.
  2. The timing (2026) and the mechanisms (constrain, redirect, replace, observe) align with the surfaces described in the STA whitepaper.
  3. No causal link is claimed — but the functional convergence is substantial and observable.
  4. libminikin remains an outlier, with no visible global length gate for the investigated line‑breaking path.

Whether this convergence is coincidental or a response to the STA research is not something this article can determine. What is clear is that the industry is moving toward defensive patterns that match the STA diagnosis — and that the asymmetry with libminikin persists.


STA The Print Preview Vector

Friday, September 04, 2026
STA The Print Preview Vector: Document Amplification Through Android Print Service

STA — The Print Preview Vector

This article documents the Print Preview amplification chain in Android — a Class A (IPC/SavedState) vector where oversized document previews exceed the Binder transaction limit, causing crashes in the Print Service, SystemUI, and, in the most severe cases, persistent crash loops requiring a hard reboot.


1. Overview

The Print Preview vector is a Class A amplification chain that begins when a user selects the Print or Print Preview option from an application — particularly Google Drive, PDF viewers, or document editors. The system constructs a preview Bundle containing page data, text, and metadata, and attempts to send it across the Binder IPC boundary to the Print Service, Print Spooler, or SystemUI.

Because no size validation is performed before serialization, a sufficiently large document (e.g., a complex DOCX, a high-resolution PDF, or a long text document) can produce a Bundle that exceeds the 1,048,576-byte Binder limit. When this happens, a TransactionTooLargeException is thrown — and in many cases, it remains uncaught. The result is a crash of the Print Service, the calling application, or SystemUI itself.

In the most severe scenarios, the crash corrupts the TaskPersister state on disk, causing a persistent crash loop that survives reboots and requires manual data clearance.

The full amplification chain is as follows:

Large document (Google Drive, PDF, DOCX)
        ↓
User selects "Print" or "Print Preview"
        ↓
Print Service builds a preview Bundle
        ↓
Bundle serialized → Parcel → Binder
        ↓
Exceeds 1 MB limit → TransactionTooLargeException
        ↓
Uncaught exception → Crash (app, Print Service, or SystemUI)
        ↓
If state is persisted (SavedState / TaskPersister) → Crash loop

Key observation: This vector is structurally identical to the WhatsApp ×20.6 amplification (STA-005) and the Threads deep-link crash (STA-012). In all cases, a structured payload crosses a Binder-backed boundary without a length check, and the resulting exception propagates uncaught.


2. Documented Vectors

The Print Preview amplification manifests in at least four distinct but related vectors, each affecting a different layer of the Android stack:

IDComponentMechanismCVSSPersistenceTier
STA-009 Google Drive → Print Preview Opening the print preview of a large DOCX/PDF generates a Bundle that exceeds the Binder limit, crashing the app. 6.5 Yes (until reboot) A
STA-010 Print Service → local printer The same oversized Bundle is sent to the local print service (SystemUI), causing SystemUI to crash. 7.2 Yes (until reboot) A
STA-010b Google Play Services → cloud printer Variant of STA-010 routed through Google Play Services. 6.5 Yes (until app closed) A
STA-018 Google Drive + Print Service + SystemUI Full chain: ANR in browser → TaskPersister corruption → SystemUI crash loop. Hard reboot required. 7.5 Yes (until reboot) A

All four vectors have been reproduced on Android 13–16 across multiple OEMs (Xiaomi, Samsung, OPPO, OnePlus, Pixel) and are classified as Tier A (full stack trace + exception + Bundle analysis).


3. Technical Evidence

3.1. SystemUI Crash During Print Preview

Bugreports captured on a Xiaomi Redmi Note 14 5G (HyperOS 3.0 / Android 16) show SystemUI crashing when a print preview is opened for a large document. The following stack trace was extracted from a production bugreport:

// SystemUI crash during Print Preview
android.os.TransactionTooLargeException: data parcel size 1,456,832 bytes
  at android.os.BinderProxy.transactNative(Native Method)
  at android.os.BinderProxy.transact(BinderProxy.java:642)
  at android.print.IPrintManager$Stub$Proxy.print(IPrintManager.java:456)
  at android.print.PrintManager.print(PrintManager.java:789)
  at com.google.android.apps.docs.print.PrintPreviewActivity.onCreate(...)
  at android.app.ActivityThread.performLaunchActivity(...)
  at android.app.ActivityThread.handleLaunchActivity(...)
  at android.app.servertransaction.LaunchActivityItem.execute(...)

Analysis: The Print Manager attempts to send the preview Bundle across Binder, but its size (1,456,832 bytes) exceeds the 1,048,576-byte limit. The resulting TransactionTooLargeException is not caught, and SystemUI crashes. The crash occurs before the user can interact with the print dialog, making it a reliable denial-of-service vector.

3.2. TaskPersister Corruption (STA-018)

In more severe cases — particularly on devices with OEM customisations (Xiaomi HyperOS) — the SystemUI crash corrupts the TaskPersister state on disk. Upon restart, SystemUI reads the corrupt state and crashes again, entering a persistent crash loop that requires a full device reboot or manual data clearance.

// TaskPersister corruption after Print Preview crash
E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.android.systemui, PID: 1234
    android.os.BadParcelableException: Failure retrieving array; only received 1 of 4
        at android.content.pm.BaseParceledListSlice.<init>(...)
        at android.window.ITaskOrganizerController$Stub$Proxy.registerTaskOrganizer(...)
        at android.window.TaskOrganizer.registerOrganizer(TaskOrganizer.java:76)
        at com.android.wm.shell.sysui.ShellInit.init(...)
    Caused by: android.os.DeadObjectException: Transaction failed on small parcel

This demonstrates that the Print Preview vector can escalate beyond a transient crash and become a persistent denial-of-service condition that affects the entire device UI.

3.3. Bundle Size Amplification

As with other Class A vectors, the amplification factor depends on the structure of the document and the nesting depth of the Fragment hierarchy. In the case of Google Drive's print preview, a document of approximately 80–100 KB can generate a Bundle of 1.2–1.5 MB, exceeding the Binder limit by 20–50%.

This amplification is consistent with the pattern observed in WhatsApp (×20.6) and TikTok (×286), where nested FragmentManager state multiplies the original payload size at each level.


4. Connection to AndroidX PdfView Commit

On 14 July 2026, a commit was merged into AndroidX (8882927e7d41e678c6f03f50d4bd5950e7dc6c47) that fixes a TransactionTooLargeException in PdfView. The commit message states:

“When a user selects content across a large range of pages (e.g., 500 pages), the serialized SelectionModel exceeds 1.2 MB. Serializing this into onSaveInstanceState triggers an unhandled TransactionTooLargeException over the kernel Binder IPC driver right when the app goes into the background.”

The fix was to change the persistent representation: rather than serialising the full SelectionModel, only anchor points (~44 bytes) are saved, and the full state is reconstructed asynchronously upon restoration.

Note: This commit is independent of the STA research, but it confirms that Google is actively mitigating the same general class of oversized‑state / Binder‑boundary problems in components related to document handling and print preview.

However, the commit is scoped to androidx.pdf.viewer.PdfView only. It does not address the Print Service, SystemUI, or TaskPersister paths documented in STA-009, STA-010, and STA-018. As of September 2026, those paths remain publicly unpatched.


5. Relation to Other STA Vectors

VectorConnection
STA-015-DL Similar chain (Google Drive HTML → browser ANR → SystemUI crash loop), but triggered via a web link rather than the print UI. Both share the TaskPersister corruption mechanism.
STA-005 Class A amplification through FragmentManager / SavedState, but in the context of messaging apps (WhatsApp) rather than printing. The underlying cause — oversized Bundle → Binder → uncaught exception — is identical.
STA-012 Threads deep-link crash. Also Class A, also involves a structured payload crossing Binder without validation.
STA-017 Class B (libminikin ANR) — unrelated to printing, but shares the same root‑cause pattern: missing size validation before an expensive operation (text layout in libminikin; Binder serialisation in Print Service).

The Print Preview vectors are a clear demonstration that the STA pattern is not confined to a single application or component. It recurs across document handling, messaging, and system UI — all tied to the same underlying architectural gap: structured input → serialization → Binder → uncaught exception → crash.


6. Mitigation Recommendations

6.1. Framework (AOSP / AndroidX)

  • Validate Bundle size before serialization in PrintManager and PrintService — reject or truncate if the 1 MB limit is approached.
  • Catch TransactionTooLargeException in both the Print Service and SystemUI, and degrade gracefully (show an error message instead of crashing).
  • Adopt LargePayloadSupport (FD‑based transfer) for print previews, similar to what already exists for Credential Manager and Digital Credentials.
  • Prevent TaskPersister corruption by validating restored state size before persisting it to disk.

6.2. Application-level (Google Drive, document viewers)

  • Truncate document content before passing it to the Print Service — limit the number of pages, reduce preview resolution, or cap text length to 50,000 characters.
  • Reject print intents that contain oversized documents (e.g., by checking the document size before calling PrintManager.print()).

6.3. OEM-specific (HyperOS, One UI, ColorOS)

  • OEMs should apply safe degradation patterns in their Task State Interactors, similar to the pattern proposed in Section 16.4 of the STA whitepaper (catch DeadObjectException and emit null instead of crashing).

7. Conclusion

The Print Preview vector is a clear and well-documented manifestation of Class A Structured Text Amplification in a system‑level service (Print Service / SystemUI). It demonstrates that the same architectural pattern — structured input → serialization → Binder → uncaught exception → crash — recurs across multiple surfaces, from messaging apps to document handling to system UI.

The independent AndroidX fix for PdfView confirms that Google is aware of this class of problems, but the Print Service and SystemUI paths remain unpatched as of September 2026. Organisations relying on Android for document workflows should consider implementing defensive truncation at the application level.

This vector also reinforces the broader STA thesis: the problem is not a single bug, but a systemic architectural gap that requires a coordinated, cross‑component response from the Android framework.


8. Related Publications


 

Browse

About:Me

My blog:http://lostmon.blogspot.com
Mail:Lostmon@gmail.com
Lostmon Google group
Lostmon@googlegroups.com

La curiosidad es lo que hace
mover la mente...

Friends