Scudo

Scudo è un allocatore di memoria dinamico in modalità utente o allocatore di heap, progettato per essere resiliente alle vulnerabilità correlate all'heap (come overflow del buffer basato sull'heap, uso dopo il rilascio e doppio rilascio) mantenendo le prestazioni. Fornisce le primitive di allocazione e deallocazione C standard (ad esempio malloc e free), nonché le primitive C++ (ad esempio new ed delete).

Scudo è più una misura di mitigazione che un rilevatore di errori di memoria completo come AddressSanitizer (ASan).

Dalla release di Android 11, scudo viene utilizzato per tutto il codice nativo (tranne sui dispositivi con poca memoria, dove viene ancora utilizzato jemalloc). In fase di esecuzione, tutte le allocazioni e le dealocation dell'heap nativo vengono gestite da Scudo per tutti gli eseguibili e le relative dipendenze della libreria e il processo viene interrotto se viene rilevata una corruzione 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 Scudo viene fornito come parte della Toolchain di Android e il supporto è stato aggiunto a Soong e Make per consentire una facile abilitazione dell'allocatore in un file binario.

Puoi abilitare o disabilitare 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:

  • In modo statico: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 sostituiscono 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 Le dimensioni (in KB) della quarantena utilizzata per ritardare la deallocazione effettiva dei chunk. Un valore inferiore può ridurre l'utilizzo della memoria, ma diminuire l'efficacia del mitigatore. Un valore negativo restituisce i valori predefiniti. Impostare su zero sia questo valore sia ThreadLocalQuarantineSizeKb disattiva completamente la quarantena.
QuarantineChunksUpToSize 2048 512 Le dimensioni (in byte) fino a cui è possibile mettere in quarantena i chunk.
ThreadLocalQuarantineSizeKb 64 16 Le dimensioni (in kB) della cache per thread utilizzate per non sovraccaricare la quarantena globale. Un valore inferiore potrebbe ridurre l'utilizzo della memoria, ma potrebbe aumentare le contese sulla quarantena globale. Impostare su zero sia questo valore sia QuarantineSizeKb disattiva completamente la quarantena.
DeallocationTypeMismatch false false Attiva la segnalazione degli errori su malloc/delete, new/free, new/delete[]
DeleteSizeMismatch true true Attiva la generazione di report sugli errori relativi alla mancata corrispondenza tra le dimensioni di operazione di inserimento e di eliminazione.
ZeroContents false false Abilita contenuti zero-chunk in fase di allocazione e deallocation.
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, ulteriori allocazioni non vanno a buon fine o restituiscono null (a seconda del valore di allocator_may_return_null), finché l'RSS non torna indietro per consentire nuove allocazioni.
allocator_release_to_os_interval_ms N/D 5000 Interessa solo un allocatore a 64 bit. Se impostato, tenta di rilasciare la memoria inutilizzata al 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 file 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 termina il processo. Le tracce dello stack che portano all'interruzione 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 relative potenziali cause:

  • corrupted chunk header: la verifica della checksum dell'intestazione del frammento non è riuscita. Ciò è probabilmente dovuto a uno dei due motivi: l'intestazione è stata sovrascritta (in parte o del tutto) oppure il puntatore passato alla funzione non è un blocco.
  • race on chunk header: due thread diversi tentano di manipolare la stessa intestazione contemporaneamente. In genere, si tratta di un sintomo di una condizione di gara o di una mancanza generale di blocco durante l'esecuzione di operazioni su quel blocco.
  • invalid chunk state: il chunk non è nello stato previsto per una determinata operazione, ad esempio non è allocato quando si tenta di liberarlo o non è messo in quarantena quando si tenta di riciclarlo. In genere questo errore è causato da un doppio carattere libero.
  • misaligned pointer: i requisiti di allineamento di base sono fortemente applicati: 8 byte su piattaforme a 32 bit e 16 byte su piattaforme a 64 bit. Se un puntatore passato alle nostre funzioni non è adatto, il puntatore passato a una delle funzioni non è allineato.
  • allocation type mismatch: quando questa opzione è abilitata, una funzione di deallocation chiamata in un blocco deve corrispondere al tipo di funzione chiamata per l'allocazione. Questo tipo di mancata corrispondenza può comportare problemi di sicurezza.
  • invalid sized delete: quando si utilizza l'operatore di eliminazione delle dimensioni C++14 e il controllo facoltativo è abilitato, si verifica una mancata corrispondenza tra la dimensione che è stata passata durante l'allocazione di un blocco e quella richiesta al momento dell'allocazione. Di solito si tratta di un problema del compilatore o di confusione di tipo sull'oggetto da deallocare.
  • RSS limit exhausted: il numero massimo di RSS 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.