Memoria di sola esecuzione (XOM) per file binari AArch64

Le sezioni di codice eseguibile per i file binari del sistema AArch64 sono contrassegnate per impostazione predefinita di sola esecuzione (non leggibili) come mitigazione rafforzata contro gli attacchi di riutilizzo del codice just-in-time. Il codice che mescola dati e codice insieme e il codice che ispeziona intenzionalmente queste sezioni (senza prima rimappare i segmenti di memoria come leggibili) non funzionano più. Le app con un SDK di destinazione pari a 10 (livello API 29 o superiore) vengono interessate se l'app tenta di leggere sezioni di codice delle librerie di sistema abilitate per la memoria di sola esecuzione (XOM) in memoria senza prima contrassegnare la sezione come leggibile.

Per beneficiare appieno di questa mitigazione, sono necessari il supporto sia dell'hardware che del kernel. Senza questo supporto, la mitigazione potrebbe essere applicata solo parzialmente. Il kernel comune di Android 4.9 contiene le patch appropriate per fornire il pieno supporto sui dispositivi ARMv8.2.

Implementazione

I file binari AArch64 generati dal compilatore presuppongono che codice e dati non siano mescolati. L'abilitazione di questa funzione non influisce negativamente sulle prestazioni del dispositivo.

Per il codice che deve eseguire un'introspezione intenzionale della memoria sui suoi segmenti eseguibili, è consigliabile chiamare mprotect sui segmenti di codice che richiedono l'ispezione per consentire loro di essere leggibili, quindi rimuovere la leggibilità una volta completata l'ispezione.
Questa implementazione fa sì che le letture nei segmenti di memoria contrassegnati come di sola esecuzione provochino un errore di segmentazione ( SEGFAULT ). Ciò potrebbe verificarsi a causa di un bug, di una vulnerabilità, di dati mescolati con il codice (pooling letterale) o di un'introspezione intenzionale della memoria.

Supporto e impatto del dispositivo

I dispositivi con hardware o kernel precedenti (inferiori a 4.9) senza le patch richieste potrebbero non supportare completamente o trarre vantaggio da questa funzionalità. I dispositivi senza supporto del kernel potrebbero non imporre l'accesso degli utenti alla memoria di sola esecuzione, tuttavia il codice del kernel che controlla esplicitamente se una pagina è leggibile può comunque imporre questa proprietà, come process_vm_readv() .

Il flag del kernel CONFIG_ARM64_UAO deve essere impostato nel kernel per garantire che il kernel rispetti le pagine dello spazio utente contrassegnate come di sola esecuzione. I dispositivi ARMv8 precedenti o i dispositivi ARMv8.2 con User Access Override (UAO) disabilitato potrebbero non trarne pieno vantaggio e potrebbero comunque essere in grado di leggere pagine di sola esecuzione utilizzando le chiamate di sistema.

Refactoring del codice esistente

Il codice trasferito da AArch32 potrebbe contenere dati e codice mescolati, causando l'insorgere di problemi. In molti casi, risolvere questi problemi è semplice come spostare le costanti in una sezione .data nel file assembly.

Potrebbe essere necessario eseguire il refactoring dell'assembly scritto a mano per separare le costanti raggruppate localmente.

Esempi:

I file binari generati dal compilatore Clang non dovrebbero avere problemi con i dati mescolati nel codice. Se è incluso il codice generato dalla raccolta del compilatore GNU (GCC) (da una libreria statica), ispezionare il file binario di output per garantire che le costanti non siano state raggruppate in sezioni di codice.

Se è necessaria l'introspezione del codice sulle sezioni di codice eseguibile, chiamare prima mprotect per contrassegnare il codice leggibile. Quindi, al termine dell'operazione, richiamare nuovamente mprotect per contrassegnarlo come illeggibile.

Abilitare

La sola esecuzione è abilitata per impostazione predefinita per tutti i file binari a 64 bit nel sistema di compilazione.

Disabilitazione

Puoi disabilitare la sola esecuzione a livello di modulo, per un intero albero di sottodirectory o globalmente per un'intera build.

XOM può essere disabilitato per singoli moduli che non possono essere sottoposti a refactoring o che necessitano di leggere il relativo codice eseguibile, impostando le variabili LOCAL_XOM e xom su false .

// Android.mk
LOCAL_XOM := false

// Android.bp
cc_binary { // or other module types
   ...
   xom: false,
}

Se la memoria di sola esecuzione è disabilitata in una libreria statica, il sistema di compilazione lo applica a tutti i moduli dipendenti di quella libreria statica. Puoi sovrascriverlo usando xom: true, .

Per disabilitare la memoria di sola esecuzione in una particolare sottodirectory (ad esempio, foo/bar/), passare il valore a XOM_EXCLUDE_PATHS .

make -j XOM_EXCLUDE_PATHS=foo/bar

In alternativa, puoi impostare la variabile PRODUCT_XOM_EXCLUDE_PATHS nella configurazione del prodotto.

Puoi disabilitare i binari di sola esecuzione a livello globale passando ENABLE_XOM=false al comando make .

make -j ENABLE_XOM=false

Validazione

Non sono disponibili test CTS o di verifica per la memoria di sola esecuzione. Puoi verificare manualmente i binari utilizzando readelf e controllando i flag del segmento.