How does Permissive MTE mode work in Android system services to record tombstones without crashing the process?

Permissive MTE mode allows critical Android system services to detect memory safety bugs and generate a full debugger tombstone log without crashing or restarting the daemon.
This mode is designed specifically for system services (not third-party apps) to audit native code for memory vulnerabilities during OS bring-up or production rollouts without triggering bootloops or system instability.
Technical Mechanism: How It Works
When a system service configured in Permissive MTE mode experiences a tag mismatch, Android handles the fault through a four-step kernel and debuggerd handler routine:
+-------------------------------------------------------------------------+
| 1. Hardware Memory Tag Mismatch |
| • CPU raises SIGSEGV (SEGV_MTESERR or SEGV_MTEAERR) |
+-------------------------------------------------------------------------+
|
v
+-------------------------------------------------------------------------+
| 2. Signal Interception & Tombstone Fork |
| • debuggerd_signal_handler catches SIGSEGV |
| • Forks child process to write crash dump to /data/tombstones/ |
+-------------------------------------------------------------------------+
|
v
+-------------------------------------------------------------------------+
| 3. Dynamic Hardware Disable via prctl() |
| • Calls prctl(PR_SET_TAGGED_ADDR_CTRL, ..., PR_MTE_TCF_NONE) |
| • Switches CPU Tag Check Fault (TCF) mode to NONE for the process |
+-------------------------------------------------------------------------+
|
v
+-------------------------------------------------------------------------+
| 4. Clean Signal Return & Resumed Execution |
| • Handler logs: "MTE ERROR DETECTED... CONTINUING." |
| • Returns cleanly; process continues running without crashing |
+-------------------------------------------------------------------------+
1. Signal Interception
When an instruction attempts to access memory with a mismatched tag, the ARM CPU raises a SIGSEGV signal (SEGV_MTESERR for synchronous mode or SEGV_MTEAERR for asynchronous mode). The process's debuggerd_signal_handler catches this signal before it reaches default OS termination routines.
2. Async Tombstone Generation
The handler forks a debuggerd worker process to snapshot the thread's CPU registers, stack backtrace, allocation history, and surrounding memory tags. It writes a standard tombstone file to /data/tombstones/.
3. Disabling Tag Checking via prctl
In standard Enforcing mode, debuggerd re-sends the signal or exits to terminate the process. In Permissive mode, debuggerd_handler.cpp executes a kernel system call to disable MTE hardware enforcement for that process on the fly:
C++
// Query current tagged address control
int tagged_addr_ctrl = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
// Clear the Tag Check Fault (TCF) mask and set it to NONE
tagged_addr_ctrl = (tagged_addr_ctrl & ~PR_MTE_TCF_MASK) | PR_MTE_TCF_NONE;
prctl(PR_SET_TAGGED_ADDR_CTRL, tagged_addr_ctrl, 0, 0, 0);
async_safe_format_log(ANDROID_LOG_ERROR, "libc",
"MTE ERROR DETECTED BUT RUNNING IN PERMISSIVE MODE. CONTINUING.");
By changing the TCF mode to PR_MTE_TCF_NONE, the CPU stops generating hardware exceptions for future tag mismatches in that process.
4. Returning Cleanly
Instead of calling exit() or passing the signal back to the OS, the handler returns 0 cleanly from the signal frame. The CPU resumes instruction execution immediately following the faulting access.
Periodic Re-enable Timers
To prevent a service from running indefinitely without MTE protection after an initial fault, Android includes a Re-enable Timer mechanism (persist.sys.mte.permissive_reenable_timer.default or process-specific properties).
-
After a designated period (e.g., $60$ seconds), a background timer thread issues a new
prctl(PR_SET_TAGGED_ADDR_CTRL, ..., PR_MTE_TCF_SYNC)call to turn hardware tag checking back on. -
If the underlying memory bug reoccurs, another tombstone is recorded, and the process is again temporarily set to
PR_MTE_TCF_NONE.
Key Caveats & Limitations
-
Kernel System Call Failures: Disabling userspace tag checks via
PR_MTE_TCF_NONEonly silences CPU load/store instruction traps. If the process passes an invalid tagged pointer to a kernel system call (such asread()orwrite()), the kernel's internal pointer validation will still fail and returnEFAULT(-1). -
System Services Only: Permissive mode is enabled via device configuration properties (
persist.sys.mte.permissive) and is restricted to native system daemons. Third-party Android apps cannot run in permissive MTE mode.
Comments
Post a Comment
Do not insert clickable links or your comment will be deleted. Checkbox Send me notifications to be notified of new comments via email.