Arm v9 introduce la funzionalità Memory Tagging Extension (MTE) di Arm, un'implementazione hardware della memoria con tag.
A un livello generale, MTE contrassegna ogni allocazione/deallocazione della memoria con metadati aggiuntivi. Assegna un tag a una posizione in memoria, che può essere associata a puntatori che fanno riferimento a quella posizione in memoria. In fase di esecuzione, la CPU controlla che i tag del puntatore e dei metadati corrispondano a ogni caricamento e memorizzazione.
In Android 12, l'allocatore di memoria dell'heap del kernel e dello spazio utente può aumentare ogni allocazione con metadati. In questo modo è possibile rilevare i bug di uso dopo svuotamento e di overflow del buffer, che sono la fonte più comune di bug di sicurezza della memoria nelle nostre basi di codice.
Modalità di funzionamento del MTE
MTE ha tre modalità di funzionamento:
- 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 preciso dei bug, quando un sovraccarico delle prestazioni più elevato è accettabile. Se abilitata, MTE SYNC funge da misura di mitigazione della sicurezza.
In caso di mancata corrispondenza dei 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 con errore.
Ti consigliamo di utilizzare questa modalità durante i test come alternativa a HWASan/KASAN o in produzione quando il processo target rappresenta una superficie di attacco vulnerabile. Inoltre, quando la modalità ASYNC ha indicato la presenza di un bug, è possibile ottenere un report accurato 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 le dealocare 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 facilitano la tracciabilità e la correzione dei bug.
Modalità asincrona (ASYNC)
Questa modalità è ottimizzata per le prestazioni rispetto all'accuratezza dei report di bug e può essere utilizzata come rilevamento a basso overhead per i bug di sicurezza della memoria.
In caso di mancata corrispondenza del tag, il processore continua l'esecuzione fino all'istruzione del kernel più vicina (ad esempio un'interruzione syscall o timer), dove termina il processo con SIGSEGV
(codice SEGV_MTEAERR
) senza registrare l'indirizzo o l'accesso alla memoria con errore.
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 della memoria e il controllo asincrono delle scritture della memoria, con prestazioni simili a quelle della modalità ASYNC. Nella maggior parte dei casi, questa modalità rappresenta un miglioramento rispetto alla modalità ASYNC e ti consigliamo di utilizzarla al posto di ASYNC, se disponibile.
Per questo motivo, nessuna delle API descritte di seguito menziona la modalità asimmetrica. Tuttavia, il sistema operativo può 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 e le app di sistema. L'MTE è disattivato per impostazione predefinita, a meno che non sia impostata una delle opzioni riportate di seguito per un determinato processo (vedi di seguito per scoprire per quali componenti è attivato l'MTE).
Attivare MTE utilizzando il sistema di compilazione
In quanto proprietà a livello di processo, l'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 nella struttura ad albero della sorgente. 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 determinato progetto:
Modalità MTE | Impostazione |
---|---|
MTE asincrona | 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 nella struttura ad albero dell'origine utilizzando una variabile del prodotto:
Modalità MTE | Elenco incluso | Elenco esclusi |
---|---|---|
asinc | 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 |
oppure
Modalità MTE | Impostazione |
---|---|
MTE asincrona | MEMTAG_HEAP_ASYNC_INCLUDE_PATHS |
MTE sincrono | MEMTAG_HEAP_SYNC_INCLUDE_PATHS |
oppure specificando il percorso di esclusione di un file eseguibile:
Modalità MTE | Impostazione |
---|---|
MTE asincrona | 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 compilazione riportate sopra possono essere sostituite in fase di esecuzione impostando la seguente proprietà di sistema:
arm64.memtag.process.<basename> = (off|sync|async)
dove basename
indica il nome base dell'eseguibile.
Ad esempio, per impostare /system/bin/ping
o /data/local/tmp/ping
in modo da utilizzare MTE asincrona, utilizza adb shell setprop arm64.memtag.process.ping async
.
Attivare MTE utilizzando una variabile di ambiente
Un altro modo per ignorare l'impostazione di compilazione è definire la variabile di ambiente: MEMTAG_OPTIONS=(off|sync|async)
Se sono definite sia la variabile di ambiente sia la proprietà di sistema, la variabile ha la precedenza.
Attivare MTE per le app
Se non specificato, l'MTE è disabilitato per impostazione predefinita, ma le app che vogliono utilizzarlo possono farlo impostando android:memtagMode
nel tag <application>
o
<process>
in 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 ignorato
per i singoli processi impostando il tag
<process>
.
Per la sperimentazione, le modifiche di compatibilità possono essere utilizzate per impostare il valore predefinito dell'attributo memtagMode
per un'app che non specifica alcun valore nel file manifest (o specifica default
).
Queste modifiche sono disponibili 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, puoi impostarla utilizzando il comando am
come segue:
$ adb shell am compat enable NATIVE_MEMTAG_[A]SYNC my.app.name
Creare un'immagine di sistema MTE
Ti consigliamo vivamente di attivare MTE su tutti i binari nativi durante lo sviluppo e la messa in servizio. In questo modo è possibile rilevare in anticipo i bug di sicurezza della memoria e fornire una copertura degli utenti 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 impiegata come variabile di ambiente o impostazione make
(ad esempio in un file product.mk
).
Tieni presente che in questo modo l'MTE viene attivato per tutti i processi nativi, ma non per le app (che sono derivate da zygote64
) per le quali l'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 addirittura SYNC possono essere simili a quelle di ASYNC. Pertanto, vale la pena attivare controlli più rigidi 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 i svantaggi delle prestazioni.
Per impostazione predefinita, i processi configurati per l'esecuzione in modalità ASYNC verranno eseguiti in modalità ASYNC su tutte le CPU. Per configurare il kernel in modo che esegua queste procedure in modalità SYNC su CPU specifiche, la sincronizzazione dei valori deve essere scritta nell'elemento 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'utilizzo in modalità ASYMM, è possibile aggiungere quanto segue alla clausola di inizializzazione di uno script di inizializzazione 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
Le tombstone dei processi in 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 tracce dello stack sono disponibili solo se il processo è configurato per l'esecuzione in modalità SYNC.
int mallopt(M_THREAD_DISABLE_MEM_INIT, level)
dove level
è 0 o 1.
Disattiva l'inizializzazione della memoria in malloc ed evita di modificare i tag della memoria
a meno che non sia necessario per la correttezza.
int mallopt(M_MEMTAG_TUNING, level)
dove level
è:
M_MEMTAG_TUNING_BUFFER_OVERFLOW
M_MEMTAG_TUNING_UAF
Consente di selezionare 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 di uso dopo svuotamento perché solo metà dei possibili valori dei tag è disponibile per ogni posizione di memoria. Tieni presente che MTE non può rilevare gli overflow all'interno dello stesso granulo del tag (chunk allineato di 16 byte) e può perdere piccoli overflow anche in questa modalità. Questo overflow non può essere la causa di una corruzione della memoria, perché la memoria all'interno di un granulo non viene mai utilizzata per più allocazioni.M_MEMTAG_TUNING_UAF
- abilita i tag con randomizzazione indipendente per una probabilità uniforme del 93% di rilevare bug sia spaziali (overflow del buffer) sia temporali (utilizzo dopo la gratuità).
Oltre alle API descritte sopra, gli utenti esperti potrebbero voler essere informati di quanto segue:
- L'impostazione del registro hardware
PSTATE.TCO
può eliminare temporaneamente il controllo dei tag (esempio). Ad esempio, quando si copia un intervallo di memoria con contenuti di tag sconosciuti o si risolve un collo di bottiglia delle prestazioni in un loop 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 attivata passando il flagSA_EXPOSE_TAGBITS
quando si imposta il gestore degli indicatori. A qualsiasi programma che imposta il proprio gestore di indicatori e delega gli arresti anomali sconosciuti al sistema è consigliato di fare lo stesso.
MTE nel kernel
Per attivare KASAN accelerato da MTE per il kernel, configura il kernel con
CONFIG_KASAN=y
, CONFIG_KASAN_HW_TAGS=y
. Queste configurazioni
sono abilitate per impostazione predefinita nei 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 (valore predefinito:on
)kasan.mode=[sync|async]
- scegli tra modalità sincrona e asincrona (valore predefinito:sync
)kasan.stacktrace=[on|off]
- indica se raccogliere le tracce dello stack (valore predefinito:on
)- La raccolta della traccia dello stack richiede anche
stack_depot_disable=off
.
- La raccolta della traccia dello stack richiede anche
kasan.fault=[report|panic]
: indica se stampare solo il report o anche generare un panico nel kernel (valore predefinito:report
). Indipendentemente da questa opzione, il controllo dei tag viene disattivato dopo il primo errore segnalato.
Utilizzo consigliato
Ti consigliamo vivamente di utilizzare la modalità SYNC durante la messa in servizio, lo sviluppo e il test. Questa opzione deve essere attivata 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 all'inizio del processo di sviluppo, la base di codice viene stabilizzata più rapidamente e viene evitato il costo del rilevamento dei bug in un secondo momento in produzione.
Consigliamo vivamente di utilizzare la modalità ASYNC in produzione. Fornisce uno strumento con un 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ò utilizzare 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à Asymm ha in genere le stesse caratteristiche di prestazioni di ASYNC ed è quasi sempre preferibile. I core in-order di piccole dimensioni spesso mostrano un rendimento simile in tutte e tre le modalità e possono essere configurati in modo da dare la preferenza a SYNC.
Gli sviluppatori devono verificare la presenza di arresti anomali controllando
/data/tombstones
,
logcat
o monitorando la pipeline DropboxManager
del fornitore per rilevare 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 gli arresti anomali degli utenti finali e per 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
- Daemon
statsd
system_server
zygote64
(per consentire alle app di attivare l'utilizzo delle MTE)
Questi target sono stati selezionati in base ai seguenti criteri:
- Un processo con privilegi (definito come un processo che ha accesso a qualcosa che il dominio SELinux unprivileged_app non ha)
- Elabora input non attendibili (Regola di due)
- Rallentamento delle prestazioni accettabile (il rallentamento non genera latenza visibile dall'utente)
Incoraggiamo i fornitori ad attivare l'MTE in produzione per più componenti,
seguendo i criteri sopra indicati. Durante lo sviluppo, ti consigliamo di testare questi componenti utilizzando la modalità SYNC per rilevare facilmente i bug e valutare l'impatto di ASYNC sul loro rendimento.
In futuro, Android prevede di espandere l'elenco dei componenti di sistema su cui è attivato il monitoraggio dei dispositivi mobili, in base alle caratteristiche di prestazioni dei progetti hardware futuri.