Scudo è un allocatore di memoria in modalità utente dinamico, o allocatore heap, progettato per essere resiliente alle vulnerabilità correlate all'heap (come overflow del buffer basato sull'heap, use after free e double free) mantenendo le prestazioni. Fornisce le primitive di allocazione e deallocazione C standard (come malloc e free), nonché le primitive C++ (come new e delete).
Scudo è più una mitigazione che un rilevatore di errori di memoria vero e proprio come AddressSanitizer (ASan).
A partire dalla release di Android 11, scudo viene utilizzato per tutto il codice nativo (tranne che sui dispositivi con poca memoria, dove viene ancora utilizzato jemalloc). In fase di runtime, tutte le allocazioni e deallocazioni dell'heap nativo vengono gestite da Scudo per tutti gli eseguibili e le relative dipendenze della libreria e il processo viene interrotto se viene rilevato un danneggiamento o un comportamento sospetto nell'heap.
Scudo è open source e fa parte del progetto compiler-rt di LLVM. La documentazione è disponibile all'indirizzo https://llvm.org/docs/ScudoHardenedAllocator.html. Il runtime di Scudo viene fornito come parte della toolchain Android e il supporto è stato aggiunto a Soong e Make per consentire l'attivazione semplice dell'allocatore in un binario.
Puoi attivare o disattivare la mitigazione aggiuntiva all'interno dell'allocatore utilizzando le opzioni descritte di seguito.
Personalizzazione
Alcuni parametri dell'allocatore possono essere definiti in base al processo in diversi modi:
- Staticamente:definisci una funzione
__scudo_default_options
nel programma che restituisce la stringa di opzioni da analizzare. Questa funzione deve avere il seguente prototipo:extern "C" const char *__scudo_default_options()
. - In modo dinamico:utilizza la variabile di ambiente
SCUDO_OPTIONS
contenente la stringa di opzioni da analizzare. Le opzioni definite in questo modo ignorano qualsiasi definizione effettuata tramite__scudo_default_options
.
Sono disponibili le seguenti opzioni.
Opzione | Valore predefinito a 64 bit | Valore predefinito a 32 bit | Descrizione |
---|---|---|---|
QuarantineSizeKb |
256 |
64 |
La dimensione (in KB) della quarantena utilizzata per ritardare la deallocazione effettiva dei
chunk. Un valore inferiore potrebbe ridurre l'utilizzo della memoria, ma diminuire l'efficacia
della mitigazione; un valore negativo ripristina i valori predefiniti. Se imposti
sia questo valore che ThreadLocalQuarantineSizeKb su zero, la
quarantena viene disattivata completamente. |
QuarantineChunksUpToSize |
2048 |
512 |
Le dimensioni (in byte) fino alle quali i blocchi possono essere messi in quarantena. |
ThreadLocalQuarantineSizeKb |
64 |
16 |
Le dimensioni (in KB) della cache per thread utilizzata per scaricare la quarantena globale.
Un valore inferiore può ridurre l'utilizzo della memoria, ma potrebbe aumentare la contesa nella
quarantena globale. Se imposti sia questa opzione sia QuarantineSizeKb su zero,
la quarantena viene disattivata completamente. |
DeallocationTypeMismatch |
false |
false |
Attiva la segnalazione degli errori su malloc/delete, new/free, new/delete[] |
DeleteSizeMismatch |
true |
true |
Attiva la segnalazione di errori in caso di mancata corrispondenza tra le dimensioni di new e delete. |
ZeroContents |
false |
false |
Consente di abilitare i contenuti di zero chunk in fase di allocazione e deallocazione. |
allocator_may_return_null |
false |
false |
Specifica che l'allocatore può restituire null quando si verifica un errore recuperabile anziché terminare il processo. |
hard_rss_limit_mb |
0 |
0 |
Quando l'RSS del processo raggiunge questo limite, il processo termina. |
soft_rss_limit_mb |
0 |
0 |
Quando l'RSS del processo raggiunge questo limite, le allocazioni successive non vanno a buon fine o
restituiscono null (a seconda del valore di allocator_may_return_null ), finché
l'RSS non torna a un livello che consente nuove allocazioni. |
allocator_release_to_os_interval_ms |
5000 |
N/D | Interessa solo un allocatore a 64 bit. Se impostato, tenta di liberare la memoria inutilizzata per il sistema operativo, ma non più spesso di questo intervallo (in millisecondi). Se il valore è negativo, la memoria non viene rilasciata al sistema operativo. |
abort_on_error |
true |
true |
Se impostato, lo strumento chiama abort() anziché _exit()
dopo aver stampato il messaggio di errore. |
Convalida
Al momento non sono disponibili test CTS specifici per Scudo. Assicurati invece che i test CTS vengano superati con o senza Scudo abilitato per un determinato binario per verificare che non influisca sul dispositivo.
Risoluzione dei problemi
Se viene rilevato un problema non recuperabile, l'allocatore
visualizza un messaggio di errore nel descrittore di errore standard e poi termina il processo.
Le analisi dello stack che portano alla terminazione vengono aggiunte nel log di sistema.
L'output di solito inizia con Scudo ERROR:
seguito da un
breve riepilogo del problema e da eventuali suggerimenti.
Di seguito è riportato un elenco dei messaggi di errore attuali e delle loro potenziali cause:
corrupted chunk header
: La verifica della checksum dell'intestazione del chunk non è riuscita. Ciò è probabilmente dovuto a uno dei due motivi seguenti: l'intestazione è stata sovrascritta (parzialmente o totalmente) oppure il puntatore passato alla funzione non è un blocco.race on chunk header
: Due thread diversi tentano di manipolare la stessa intestazione contemporaneamente. Questo di solito è sintomatico di una condizione di competizione o di una mancanza generale di blocco durante l'esecuzione di operazioni su quel blocco.invalid chunk state
: il blocco non si trova nello stato previsto per una determinata operazione, ad esempio non è allocato quando si tenta di liberarlo o non è in quarantena quando si tenta di riciclarlo. Un doppio free è il motivo tipico di questo errore.misaligned pointer
: i requisiti di allineamento di base sono applicati rigorosamente: 8 byte sulle piattaforme a 32 bit e 16 byte su quelle a 64 bit. Se un puntatore passato alle nostre funzioni non corrisponde a questi, il puntatore passato a una delle funzioni è disallineato.allocation type mismatch
: se questa opzione è attivata, una funzione di deallocazione chiamata su un blocco deve corrispondere al tipo di funzione chiamata per allocarlo. Questo tipo di mancata corrispondenza può introdurre problemi di sicurezza.invalid sized delete
: Quando viene utilizzato l'operatore di eliminazione dimensionato C++14 e il controllo facoltativo è abilitato, si verifica una mancata corrispondenza tra le dimensioni passate durante la deallocazione di un blocco e le dimensioni richieste durante l'allocazione. In genere si tratta di un problema del compilatore o di una confusione di tipi nell'oggetto in fase di deallocazione.RSS limit exhausted
: Il feed RSS massimo specificato facoltativamente è stato superato.
Se stai eseguendo il debug di un arresto anomalo nel sistema operativo stesso, puoi utilizzare una build del sistema operativo HWASan. Se stai eseguendo il debug di un arresto anomalo in un'app, puoi utilizzare anche una build dell'app HWASan.