跪拜 Guibai
← All articles
Android · Reverse Engineering

A Samsung OneUI Bug Leaked `top` Processes for Years, Crashing Banking and Government Apps Across China

By k3x1n ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

The bug had been silently crashing Chinese banking, tax, railway, and carrier apps on Samsung devices for years without generating crash reports, because the kill came from AMS rather than an app-level exception. It demonstrates how a single unclosed subprocess in a vendor-customized APEX module can degrade an entire device, and why shelling out to command-line tools from a system service is a pattern worth auditing in any Android skin.

Summary

Samsung's Chinese OneUI builds contain a CPU-monitoring routine in `service-samsung-shell.jar` that calls `top -b -n 1` via `Runtime.exec`. The reader loop exits after 8 lines without ever calling `destroy()` or `close()`, leaving the `top` process orphaned as a phantom process owned by `system_server`. Each cold start or app install triggers the call when free storage drops below 3 GB, with a minimum 5-minute cooldown between invocations.

Once 32 phantom processes accumulate, Android's `trimPhantomProcessesIfNecessary` begins killing them in order of parent oom_adj. Because `system_server` runs at oom_adj -900, the cleanup logic preferentially terminates phantom processes belonging to ordinary apps. Apps protected by the enterprise version of the Bangcle packer fork a detection child process; when that child is killed, the parent app exits immediately, producing a crash that APM platforms never see because it is not a native crash.

The bug only affects mainland China firmware, where a region-gated `mIpmAntiAgingController` enables the code path. OneUI 8.5 replaces the `top` call with a proc-filesystem read, closing the leak. Users who enabled "Disable child process restrictions" merely masked the symptom while `system_server` accumulated hundreds of leaked processes and file descriptors, sometimes causing spontaneous reboots.

Takeaways
`Runtime.getRuntime().exec("top -b -n 1")` in `service-samsung-shell.jar` creates a `top` process that is never destroyed or closed after the reader loop exits early at 8 lines.
Each leaked `top` process is recorded as a phantom process owned by `system_server` (oom_adj -900), so Android's phantom-process trimmer kills app-owned phantom processes first.
Apps using the enterprise version of the Bangcle packer fork a child process for environment detection; when that child is killed, the parent app exits immediately, mimicking a crash.
The trigger is gated on available storage falling below 3 GB and only activates on mainland China firmware builds, where `mIpmAntiAgingController` is assigned.
Cold-starting an app or installing a new app invokes the buggy code path, but a cooldown of at least 5 minutes (30 seconds per GB of free space, minimum 300 seconds) spaces out the leaks.
OneUI 8.5 replaces the `top` call with a direct read from `/proc`, eliminating the process leak.
Enabling "Disable child process restrictions" in Developer Options stops the crashes but allows `system_server` to accumulate hundreds of leaked processes and file descriptors, eventually causing spontaneous reboots.
Conclusions

A single unclosed subprocess in a vendor APEX module evaded detection for years because the resulting app exits were not crashes — AMS killed the process, so APM tools never collected a stack trace.

Parsing `top` output by hardcoded column index is fragile across Android versions; the code also reads column 10 (MEM) instead of column 9 (CPU), making the CPU measurement useless even when it runs.

The phantom-process trimmer sorts by parent oom_adj, which means a leak in a high-priority system process punishes ordinary apps rather than the leaker — a design that turns a resource leak into a user-visible denial of service.

Region-gating the bug behind a China-only code path, combined with Samsung's low Chinese market share, meant the issue received almost no engineering attention despite affecting essential government and financial apps.

Concepts & terms
Phantom process
A child process forked by an Android app (not by Zygote) that the framework cannot directly manage. Android 12+ limits phantom processes to 32 by default and trims the excess based on the parent process's oom_adj.
APEX module
Introduced in Android 10, APEX (Android Pony EXpress) packages system components into separately updatable modules stored under /apex, allowing OEMs and Google to update system code without a full OTA.
oom_adj
A per-process priority score used by Android's low-memory killer. Lower values mean higher priority; system_server runs at -900, foreground apps at 0, and background apps at higher positive values.
Bangcle enterprise packer
A Chinese app-hardening solution that encrypts DEX and native code. Its enterprise version forks a child process for environment integrity checks; if that child is killed, the parent app exits.
From the discussion

Praise for the author's debugging skill dominates, with one commenter admitting they had misattributed the crashes to individual apps. A separate technical reply surfaces two specific crash signatures—SIGABRT with Scudo memory corruption and SIGSEGV from getAndExecuteCommand—and notes these are predominantly reported on Samsung devices running Android 16.

Misdiagnosis was common: the crashes were widely blamed on the apps themselves rather than the firmware, since only some apps were affected.
Two distinct crash patterns are circulating on Samsung Android 16 devices: a Scudo memory corruption SIGABRT and a SIGSEGV from getAndExecuteCommand returning an unexpected error.
Featured comments
菲尼克斯大大 1 likes

Impressive. I always thought it was the app's problem, because other apps don't crash. [thumbs up]

maybee

Have you encountered either of these two issues? Currently it's mostly Samsung Android 16 reporting them. 6 (SIGABRT) Scudo ERROR: corrupted chunk header at address 0x20000791b8d8050: most likely due to memory corruption The other kind 11 (SIGSEGV) getAndExecuteCommand(fd=86) returned unexpected error -2147483648, aborting

See top comments, translated →
Source: juejin.cn ↗ Google Translate ↗ Backup ↗