Skip to content

AMCC (Apple Memory Cache Controller) RoRgn

Apple'ın memory controller'ı (AMCC) tarafından zorlanan hardware read-only region: controller'ın tüm write'ları sessizce düşürdüğü bir DRAM page range; bu KTRR'a, kernel code'unun ve read-only data'sının asla değiştirilemeyeceğine dair ikinci, MMU-independent bir garanti verir.

Mechanism

Invariant

Software W^X korumaları (page-table permission bit'leri), page table'lara ya da translation'ı yöneten system register'lara erişebilen herkes tarafından geri alınabilir. KTRR bu boşluğu, read-only kernel region'ını iki bağımsız hardware katmanında zorlayarak kapatır. Bir katman CPU'nun MMU'sunda yaşar (KTRR_LOWER_EL1 / KTRR_UPPER_EL1 / KTRR_LOCK_EL1 register'ları, EL1'deki instruction fetch'leri locked range dışında fail etmeye zorlar). Diğer katman AMCC'dir — CPU ile DRAM arasında oturan memory controller. AMCC fiziksel bir Read-Only Region (RoRgn) ile konfigüre edilir: start page, end page ve bir lock. Bir kez locked olunca controller, page table'lardan bağımsız, EL'den bağımsız ve hangi core'un issue ettiğinden bağımsız olarak, fiziksel adresi region içine düşen her write'ı reddeder. Check MMU'nun altında olduğu için, hiçbir miktarda page-table ya da translation-register manipülasyonu kernel text'e başarılı bir write üretemez. Lock tek yönlüdür: lock register yazıldıktan sonra base/end/lock alanları bir sonraki reset'e kadar immutable'dır.

RoRgn, kernelcache'in contiguous read-only span'ini kapsar; __PRELINK_TEXT segment'inden __LAST'a kadar. Executable subset (MMU'nun ayrıca r-x olarak işaretlediği şey) __PRELINK_TEXT'ten __TEXT_EXEC'e kadar uzanır; __LAST.__pinst RoRgn'in içinde oturur ama MMU açılınca non-executable yapılır. KTRR, boot'un erken aşamasında iBoot/XNU tarafından kurulur ve untrusted code çalışmadan önce locked edilir.

Walkthrough

RoRgn, AMCC register block'undaki düz MMIO'dur. Siguza'nın KTRR analizine göre üç alan, AMCC base'inden sabit offset'lerde yaşar:

Field Offset Meaning
rRORGNBASEADDR +0x7e4 region'ın start page number'ı
rRORGNENDADDR +0x7e8 region'ın end page number'ı
rRORGNLOCK +0x7ec üç alanı da lock'lamak için 1 yaz

XNU'nun lockdown sequence'ı kavramsal olarak şöyledir:

// illustrative — physical writes to the AMCC MMIO block (amcc_base)
rRORGNBASEADDR = atop(rorgn_begin);   // __PRELINK_TEXT, page number
rRORGNENDADDR  = atop(rorgn_end);     // __LAST, page number
rRORGNLOCK     = 1;                   // one-way lock: fields now frozen
__asm__ volatile("isb sy");

// then the MMU side:
__builtin_arm_wsr64("ARM64_REG_KTRR_LOWER_EL1", rorgn_begin);
__builtin_arm_wsr64("ARM64_REG_KTRR_UPPER_EL1", rorgn_end);
__builtin_arm_wsr64("ARM64_REG_KTRR_LOCK_EL1",  1);

Lock'tan sonra iki katman şöyle davranır:

  • AMCC katmanı: [rRORGNBASEADDR, rRORGNENDADDR] aralığındaki herhangi bir fiziksel adrese yapılan bir store, controller tarafından kabul edilir ve atılır — write sessizce yok olur, DRAM'deki data değişmez. Hiçbir fault olmaz.
  • MMU/KTRR katmanı: MMU enable iken EL1'de, KTRR executable range dışındaki bir adresi hedefleyen bir instruction fetch fault verir, dolayısıyla başka yere inject edilmiş code kernel olarak çalışamaz.
Lock'u bir kernel R/W primitive ile doğrulama

Yaygın bir bring-up check'i (jailbreak/RE tooling'inde kullanılan), bir physical-write primitive elde etmek, bilinen bir kernel-text page'ine bir sentinel yazmak ve sonra onu geri okumaktır:

// pseudo: with kernel phys read/write
pa = kvtophys(addr_in_kernel_text);
phys_write64(pa, 0x4141414141414141);
val = phys_read64(pa);
// val == original instruction, NOT 0x41414141...  -> AMCC dropped the write

Başarılı bir write, RoRgn lock'unun devreye girmediği anlamına gelirdi (örn. patch'lenmemiş pre-KTRR bir cihaz ya da RoRgn'siz bir controller). KTRR-enforcing cihazlarda write bir no-op'tur.

Bu bir software toggle değildir

KTRR/CTRR hardware'inde, compromise olmuş bir EL1 kernel'ın lock'tan sonra write'ları yeniden enable etmek için flip edebileceği bir register yoktur. Bypass'lar lock'tan önceki configuration window'unu (örn. boot-chain compromise) ya da hardware glitching'i hedef alır — locked state'i değil.

Detection

  • Kernel text'e yapılan ve değişmemiş olarak geri okunan bir physical write (yukarıdaki sentinel test'i), AMCC RoRgn'in aktif olduğunu doğrular.
  • A12+ üzerinde eşdeğer in-core memory-controller koruması CTRR register'ları üzerinden açığa çıkar; aynı "write'lar düşürülür, range dışı fetch'ler fault verir" davranışı geçerlidir.

Mitigation

RoRgn zaten mitigation'dır. MMU tarafını değiştirmez, tamamlar: AMCC, DRAM'deki data-at-rest'i savunur (write yok) ve KTRR'ın MMU register'ları execution'ı savunur (unlocked page'lerin EL1 code olarak fetch edilmemesi). İkisi de boot'un erken aşamasında tek yönlü locked edildiği için, tek başına runtime kernel R/W kernel text'i yeniden açamaz — bitişik per-page / pointer-integrity katmanları için aprr ve pac'e, pre-lock boot window'unu hedef alan saldırılar için secure-boot-bypass'a bak.

References