Year after year, Apple continues to quietly (or not so quietly) tighten the screws, introducing new security mitigations into iOS. Many of these have already been documented by researchers. But today, I'd like to talk about something new: TPRO regions. They were introduced way back in ~22C152, yet somehow managed to stay under the radar all this time!

TPRO (presumably standing for Trusted Path Read-Only) is an SPRR-based data protection mechanism designed to prevent memory overwrites via arbitrary writes. The easiest way to think of it is as a trampoline that temporarily makes memory writable within a critical section. A perfect example to illustrate this is one of the functions in dyld.

Disassembly of _dyld_call_with_writable_tpro_memory
_dyld_call_with_writable_tpro_memory (23C5033h)

The trampoline's first step is to check the commpage at @0xfffffc10c to verify if the target device has hardware support for TPRO. This most likely corresponds to the cp_aprr_shadow_supported field from the open-source headers. Additionally, the commpage at @0xfffffc0d0 stores two values for the S3_6_C15_C1_5 register, which differ by only a single bit in the ninth attribute.

Memory dump of commpage TPRO register values
x/xg commpage

Combining this with our knowledge of SPRR (where S3_6_C15_C1_5 stores a value split into 16 attributes, each dictating page-type permissions for different Exception Levels [EL]), we can deduce that the first value represents RW permissions (3 = 0011), while the second represents RO (2 = 0010).

SPRR value to permissions mapping table
Value to permissions table

The next step is to replace the value in S3_6_C15_C1_5 with cp_aprr_shadow_tpro_rw from the commpage. Once the isb instruction is executed, this immediately alters the permissions across all TPRO pages.

From this point on, the memory becomes writable. A callback is then executed, and upon its completion, a similar procedure reverts the permissions to their normal state. If any write attempts are made after this, the kernel will kill the process with OS_REASON_SELF_RESTRICT.

And that's the entire mitigation! On the one hand, it's quite primitive (though it definitely complicates the lives of WebKit pwners), but on the other hand, it's elegant and holds a lot of potential.