Arm v9 introduce l'estensione di tagging della memoria (MTE), un'implementazione hardware della memoria con tag.
A livello generale, MTE assegna tag a ogni allocazione/deallocazione di memoria con metadati aggiuntivi. Assegna un tag a una posizione di memoria, che può poi essere associata a puntatori che fanno riferimento a quella posizione di memoria. In fase di runtime, la CPU verifica che il puntatore e i tag dei metadati corrispondano a ogni caricamento e archiviazione.
In Android 12, l'allocatore di memoria heap del kernel e dello spazio utente può aumentare ogni allocazione con metadati. Ciò consente di rilevare bug di tipo use-after-free e buffer overflow, che sono la fonte più comune di bug di sicurezza della memoria nelle nostre codebase.
Modalità operative MTE
MTE ha tre modalità operative:
- Modalità sincrona (SYNC)
- Modalità asincrona (ASYNC)
- Modalità asimmetrica (ASYMM)
Modalità sincrona (SYNC)
Questa modalità è ottimizzata per la correttezza del rilevamento dei bug rispetto alle prestazioni e
può essere utilizzata come strumento di rilevamento dei bug preciso, quando un sovraccarico delle prestazioni più elevato è
accettabile. Se abilitata, MTE SYNC funge da mitigazione della sicurezza.
In caso di mancata corrispondenza del tag, il processore interrompe immediatamente l'esecuzione e
termina il processo con SIGSEGV
(codice
SEGV_MTESERR
) e informazioni complete sull'accesso alla memoria e
sull'indirizzo di errore.
Ti consigliamo di utilizzare questa modalità durante i test come alternativa a HWASan/KASAN o in produzione quando il processo di destinazione rappresenta una superficie di attacco vulnerabile. Inoltre, quando la modalità ASYNC ha indicato la presenza di un bug, è possibile ottenere un report accurato sui bug utilizzando le API di runtime per passare all'esecuzione in modalità SYNC.
Quando viene eseguito in modalità SYNC, l'allocatore Android registra le tracce dello stack per tutte le allocazioni e deallocazioni e le utilizza per fornire report sugli errori migliori che includono una spiegazione di un errore di memoria, come use-after-free o overflow del buffer, e le tracce dello stack degli eventi di memoria pertinenti. Questi report forniscono informazioni più contestuali e rendono più facile tracciare e correggere i bug.
Modalità asincrona (ASYNC)
Questa modalità è ottimizzata per le prestazioni rispetto all'accuratezza dei report sui bug e può
essere utilizzata come rilevamento a basso overhead per i bug di sicurezza della memoria.
In caso di mancata corrispondenza dei tag, il processore continua l'esecuzione fino alla voce del kernel più vicina (ad esempio, una chiamata di sistema o un interrupt del timer), dove termina il processo con SIGSEGV
(codice SEGV_MTEAERR
) senza registrare l'indirizzo di errore o l'accesso alla memoria.
Ti consigliamo di utilizzare questa modalità in produzione su codebase ben testate in cui
la densità di bug di sicurezza della memoria è nota per essere bassa, il che si ottiene utilizzando
la modalità SYNC durante i test.
Modalità asimmetrica (ASYMM)
Una funzionalità aggiuntiva in Arm v8.7-A, la modalità MTE asimmetrica fornisce il controllo sincrono delle letture di memoria e il controllo asincrono delle scritture di memoria, con prestazioni simili a quelle della modalità ASYNC. Nella maggior parte dei casi, questa modalità è un miglioramento rispetto alla modalità ASYNC e ti consigliamo di utilizzarla al posto di ASYNC ogni volta che è disponibile.
Per questo motivo, nessuna delle API descritte di seguito menziona la modalità Asymmetric. Il sistema operativo può invece essere configurato per utilizzare sempre la modalità asimmetrica quando viene richiesta la modalità asincrona. Per ulteriori informazioni, consulta la sezione "Configurazione del livello MTE preferito specifico per la CPU".
MTE nello spazio utente
Le sezioni seguenti descrivono come attivare MTE per i processi di sistema e le app. MTE è disattivato per impostazione predefinita, a meno che una delle opzioni riportate di seguito non sia impostata per un determinato processo (vedi per quali componenti è attivato MTE di seguito).
Attiva MTE utilizzando il sistema di compilazione
In quanto proprietà a livello di processo, MTE è controllata dall'impostazione del tempo di compilazione dell'eseguibile principale. Le seguenti opzioni consentono di modificare questa impostazione per singoli eseguibili o per intere sottodirectory nell'albero delle origini. L'impostazione viene ignorata nelle librerie o in qualsiasi target che non sia eseguibile né un test.
1. Attivazione di MTE in Android.bp
(esempio),
per un progetto specifico:
Modalità MTE | Impostazione |
---|---|
MTE asincrono | sanitize: { memtag_heap: true, } |
MTE sincrono | sanitize: { memtag_heap: true, diag: { memtag_heap: true, }, } |
o in Android.mk:
Modalità MTE | Impostazione |
---|---|
Asynchronous MTE |
LOCAL_SANITIZE := memtag_heap |
Synchronous MTE |
LOCAL_SANITIZE := memtag_heap LOCAL_SANITIZE_DIAG := memtag_heap |
2. Attivazione di MTE in una sottodirectory dell'albero delle origini utilizzando una variabile di prodotto:
Modalità MTE | Elenco di inclusione | Elenco di esclusione |
---|---|---|
async | PRODUCT_MEMTAG_HEAP_ASYNC_INCLUDE_PATHS
MEMTAG_HEAP_ASYNC_INCLUDE_PATHS |
PRODUCT_MEMTAG_HEAP_EXCLUDE_PATHS
MEMTAG_HEAP_EXCLUDE_PATHS |
sincronizzazione | PRODUCT_MEMTAG_HEAP_SYNC_INCLUDE_PATHS
MEMTAG_HEAP_SYNC_INCLUDE_PATHS |
o
Modalità MTE | Impostazione |
---|---|
MTE asincrono | MEMTAG_HEAP_ASYNC_INCLUDE_PATHS |
MTE sincrono | MEMTAG_HEAP_SYNC_INCLUDE_PATHS |
o specificando il percorso di esclusione di un eseguibile:
Modalità MTE | Impostazione |
---|---|
MTE asincrono | PRODUCT_MEMTAG_HEAP_EXCLUDE_PATHS
MEMTAG_HEAP_EXCLUDE_PATHS |
MTE sincrono |
Esempio (utilizzo simile a PRODUCT_CFI_INCLUDE_PATHS
)
PRODUCT_MEMTAG_HEAP_SYNC_INCLUDE_PATHS=vendor/$(vendor) PRODUCT_MEMTAG_HEAP_EXCLUDE_PATHS=vendor/$(vendor)/projectA \ vendor/$(vendor)/projectB
Attivare MTE utilizzando le proprietà di sistema
Le impostazioni di build riportate sopra possono essere sostituite in fase di runtime impostando la seguente proprietà di sistema:
arm64.memtag.process.<basename> = (off|sync|async)
Dove basename
sta per il nome di base dell'eseguibile.
Ad esempio, per impostare /system/bin/ping
o /data/local/tmp/ping
in modo da utilizzare MTE asincrono, utilizza adb shell setprop arm64.memtag.process.ping async
.
Attivare MTE utilizzando una variabile di ambiente
Un altro modo per eseguire l'override dell'impostazione di build per i processi nativi (non app)
è definire la variabile di ambiente: MEMTAG_OPTIONS=(off|sync|async)
Se vengono definite sia la variabile di ambiente sia la proprietà di sistema, la
variabile ha la precedenza.
Attivare MTE per le app
Se non specificato, MTE è disattivato per impostazione predefinita, ma
le app che vogliono utilizzare MTE possono farlo impostando android:memtagMode
nel tag <application>
o
<process>
nel
AndroidManifest.xml
.
android:memtagMode=(off|default|sync|async)
Se impostato sul tag <application>
, l'attributo
influisce su tutti i processi utilizzati dall'app e può essere sostituito
per i singoli processi impostando il tag
<process>
.
Per la sperimentazione, le modifiche
alla compatibilità possono essere utilizzate per impostare il valore predefinito
dell'attributo memtagMode
per un'app che
non specifica alcun valore nel manifest (o specifica
default
).
Queste modifiche si trovano in System > Advanced > Developer options
> App Compatibility Changes
nel menu delle impostazioni globali. L'impostazione
NATIVE_MEMTAG_ASYNC
o NATIVE_MEMTAG_SYNC
attiva MTE
per una determinata app.
In alternativa, questa impostazione può essere configurata utilizzando il comando am
come segue:
$ adb shell am compat enable NATIVE_MEMTAG_[A]SYNC my.app.name
Crea un'immagine di sistema MTE
Ti consigliamo vivamente di attivare MTE su tutti i binari nativi durante lo sviluppo e l'avvio. Ciò consente di rilevare in anticipo i bug di sicurezza della memoria e fornisce una copertura utente realistica, se abilitata nelle build di test.
Ti consigliamo vivamente di attivare MTE in modalità sincrona su tutti i binari nativi durante lo sviluppo
SANITIZE_TARGET=memtag_heap SANITIZE_TARGET_DIAG=memtag_heap m
Come per qualsiasi variabile nel sistema di compilazione, SANITIZE_TARGET
può essere
utilizzata come variabile di ambiente o come impostazione make
(ad esempio, in
un file product.mk
).
Tieni presente che questa operazione attiva MTE per tutti i processi nativi, ma non per
le app (che sono fork di zygote64
) per le quali MTE può essere
attivato seguendo le istruzioni sopra.
Configura il livello MTE preferito specifico per la CPU
Su alcune CPU, le prestazioni di MTE nelle modalità ASYMM o SYNC potrebbero essere simili a quelle di ASYNC. Ciò rende utile attivare
controlli più rigorosi su queste CPU quando viene richiesta una modalità di controllo meno rigorosa, in
modo da ottenere i vantaggi del rilevamento degli errori dei controlli più rigorosi senza
gli svantaggi in termini di prestazioni.
Per impostazione predefinita, i processi configurati per l'esecuzione in modalità ASYNC vengono eseguiti in modalità ASYNC
su tutte le CPU. Per configurare il kernel in modo che esegua questi processi in modalità SYNC su CPU specifiche, il valore sync deve essere scritto nella voce sysfs
/sys/devices/system/cpu/cpu<N>/mte_tcf_preferred
al momento dell'avvio. Questa operazione può essere eseguita con uno script di inizializzazione. Ad esempio, per configurare le CPU 0-1
per eseguire i processi in modalità ASYNC in modalità SYNC e le CPU 2-3 per l'esecuzione in modalità ASYMM,
è possibile aggiungere quanto segue alla clausola init di uno script init del fornitore:
write /sys/devices/system/cpu/cpu0/mte_tcf_preferred sync write /sys/devices/system/cpu/cpu1/mte_tcf_preferred sync write /sys/devices/system/cpu/cpu2/mte_tcf_preferred asymm write /sys/devices/system/cpu/cpu3/mte_tcf_preferred asymm
I segnali di eliminazione delle modalità ASYNC in esecuzione in modalità SYNC conterranno una traccia dello stack precisa della posizione dell'errore di memoria. Tuttavia, non includeranno una traccia dello stack di allocazione o deallocazione. Queste analisi dello stack sono disponibili solo se il processo è configurato per essere eseguito in modalità SYNC.
int mallopt(M_THREAD_DISABLE_MEM_INIT, level)
dove level
è 0 o 1.
Disabilita l'inizializzazione della memoria in malloc ed evita di modificare i tag di memoria
se non necessario per la correttezza.
int mallopt(M_MEMTAG_TUNING, level)
dove level
è:
M_MEMTAG_TUNING_BUFFER_OVERFLOW
M_MEMTAG_TUNING_UAF
Seleziona la strategia di allocazione dei tag.
- L'impostazione predefinita è
M_MEMTAG_TUNING_BUFFER_OVERFLOW
. M_MEMTAG_TUNING_BUFFER_OVERFLOW
: consente il rilevamento deterministico di bug di overflow e underflow del buffer lineare assegnando valori di tag distinti alle allocazioni adiacenti. Questa modalità ha una probabilità leggermente ridotta di rilevare bug use-after-free perché solo la metà dei possibili valori dei tag è disponibile per ogni posizione di memoria. Tieni presente che MTE non può rilevare l'overflow all'interno dello stesso granulare del tag (chunk allineato a 16 byte) e può perdere piccoli overflow anche in questa modalità. Questo overflow non può essere la causa del danneggiamento della memoria, perché la memoria all'interno di un granulo non viene mai utilizzata per più allocazioni.M_MEMTAG_TUNING_UAF
: consente tag randomizzati in modo indipendente per una probabilità uniforme di circa il 93% di rilevare bug spaziali (buffer overflow) e temporali (use after free).
Oltre alle API descritte sopra, gli utenti esperti potrebbero voler conoscere quanto segue:
- L'impostazione del registro hardware
PSTATE.TCO
può sopprimere temporaneamente il controllo dei tag (esempio). Ad esempio, quando copi un intervallo di memoria con contenuti di tag sconosciuti o quando risolvi un collo di bottiglia delle prestazioni in un ciclo caldo. - Quando utilizzi
M_HEAP_TAGGING_LEVEL_SYNC
, il gestore degli arresti anomali del sistema fornisce informazioni aggiuntive, come le analisi dello stack di allocazione e deallocazione. Questa funzionalità richiede l'accesso ai bit del tag e viene abilitata passando il flagSA_EXPOSE_TAGBITS
durante l'impostazione del gestore dei segnali. È consigliabile che qualsiasi programma che imposta il proprio gestore di segnali e delega gli arresti anomali sconosciuti a quello di sistema faccia lo stesso.
MTE nel kernel
Per attivare KASAN con accelerazione MTE per il kernel, configura il kernel con
CONFIG_KASAN=y
, CONFIG_KASAN_HW_TAGS=y
. Queste configurazioni
sono abilitate per impostazione predefinita sui kernel GKI a partire da Android
12-5.10
.
Questo può essere controllato all'avvio utilizzando i seguenti argomenti della riga di comando:
kasan=[on|off]
: attiva o disattiva KASAN (impostazione predefinita:on
)kasan.mode=[sync|async]
- scegli tra la modalità sincrona e asincrona (impostazione predefinita:sync
)kasan.stacktrace=[on|off]
: indica se raccogliere stack trace (valore predefinito:on
)- La raccolta di stack trace richiede anche
stack_depot_disable=off
.
- La raccolta di stack trace richiede anche
kasan.fault=[report|panic]
: indica se stampare solo il report o anche il kernel panic (impostazione predefinita:report
). Indipendentemente da questa opzione, il controllo dei tag viene disattivato dopo il primo errore segnalato.
Utilizzo consigliato
Consigliamo vivamente di utilizzare la modalità SYNC durante l'avvio, lo sviluppo e il test. Questa opzione deve essere abilitata a livello globale per tutti i processi che utilizzano la variabile di ambiente o con il sistema di compilazione. In questa modalità, i bug vengono rilevati nelle prime fasi del processo di sviluppo, la base di codice viene stabilizzata più rapidamente e si evita il costo del rilevamento dei bug nelle fasi successive della produzione.
Ti consigliamo vivamente di utilizzare la modalità ASYNC in produzione. Questo fornisce uno strumento a basso overhead per rilevare la presenza di bug di sicurezza della memoria in un processo, nonché un'ulteriore difesa in profondità. Una volta rilevato un bug, lo sviluppatore può sfruttare le API di runtime per passare alla modalità SYNC e ottenere una traccia dello stack accurata da un insieme di utenti campionati.
Ti consigliamo vivamente di configurare il livello MTE preferito specifico per la CPU per il SoC. La modalità asimmetrica in genere ha le stesse caratteristiche di rendimento di ASYNC ed è quasi sempre preferibile. I piccoli core in ordine spesso mostrano prestazioni simili in tutte e tre le modalità e possono essere configurati per preferire SYNC.
Gli sviluppatori devono verificare la presenza di arresti anomali controllando
/data/tombstones
,
logcat
o monitorando la pipeline del fornitore DropboxManager
per i bug degli utenti finali. Per ulteriori informazioni sul debug del codice nativo Android, consulta
le informazioni qui.
Componenti della piattaforma abilitati per MTE
In Android 12, diversi componenti di sistema critici per la sicurezza utilizzano MTE ASYNC per rilevare i blocchi degli utenti finali e fungere da ulteriore livello di difesa in profondità. Questi componenti sono:
- Daemon e utilità di rete (ad eccezione di
netd
) - Bluetooth, SecureElement, HAL NFC e app di sistema
statsd
daemonsystem_server
zygote64
(per consentire alle app di attivare l'utilizzo di MTE)
Questi target sono stati selezionati in base ai seguenti criteri:
- Un processo privilegiato (definito come un processo che ha accesso a qualcosa a cui il dominio SELinux unprivileged_app non ha accesso)
- Elabora input non attendibili (regola dei due)
- Rallentamento accettabile delle prestazioni (il rallentamento non crea latenza visibile all'utente)
Incoraggiamo i fornitori ad attivare MTE in produzione per un maggior numero di componenti,
seguendo i criteri menzionati sopra. Durante lo sviluppo, ti consigliamo di testare
questi componenti utilizzando la modalità SYNC, per rilevare facilmente i bug correggibili e valutare
l'impatto di ASYNC sul loro rendimento.
In futuro, Android prevede di ampliare l'elenco dei componenti di sistema su cui è abilitata la funzionalità MTE, in base alle caratteristiche di rendimento dei futuri progetti hardware.