UndefinedBehaviorSanitizer

UndefinedBehaviorSanitizer (UBSan) esegue la misurazione in fase di compilazione per verificare la presenza di vari tipi di comportamento non definito. Sebbene UBSan sia in grado di rilevare molti bug di comportamento non definito, Android supporta:

  • allineamento
  • bool
  • limiti
  • enum
  • float-cast-overflow
  • divisione-per-zero-in-virgola-mobile
  • integer-divide-by-zero
  • nonnull-attribute
  • null
  • ritorno
  • returns-nonnull-attribute
  • shift-base
  • shift-exponent
  • overflow-intero-firmato
  • non raggiungibile
  • overflow-numero-intero-non-firmato
  • vla-bound

Anche se non è tecnicamente un comportamento non definito, il controllo unsigned-integer-overflow è incluso nel programma di sanificazione e utilizzato in molti moduli Android, inclusi i componenti di mediaserver, per eliminare eventuali vulnerabilità latenti di overflow di interi.

Implementazione

Nel sistema di compilazione Android, puoi attivare UBSan a livello globale o locale. Per attivare UBSan a livello globale, imposta SANITIZE_TARGET in Android.mk. Per attivare UBSan a livello di modulo, imposta LOCAL_SANITIZE e specifica i comportamenti non definiti che vuoi cercare in Android.mk. Ad esempio:

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

LOCAL_CFLAGS := -std=c11 -Wall -Werror -O0

LOCAL_SRC_FILES:= sanitizer-status.c

LOCAL_MODULE:= sanitizer-status

LOCAL_SANITIZE := alignment bounds null unreachable integer
LOCAL_SANITIZE_DIAG := alignment bounds null unreachable integer

include $(BUILD_EXECUTABLE)

E la configurazione del progetto iniziale (Android.bp) equivalente:

cc_binary {

    cflags: [
        "-std=c11",
        "-Wall",
        "-Werror",
        "-O0",
    ],

    srcs: ["sanitizer-status.c"],

    name: "sanitizer-status",

    sanitize: {
        misc_undefined: [
            "alignment",
            "bounds",
            "null",
            "unreachable",
            "integer",
        ],
        diag: {
            misc_undefined: [
                "alignment",
                "bounds",
                "null",
                "unreachable",
                "integer",
            ],
        },
    },

}

Scorciatoie per UBSan

Android dispone anche di due scorciatoie, integer e default-ub, per attivare contemporaneamente un insieme di disinfettanti. il valore intero attiva integer-divide-by-zero, signed-integer-overflow e unsigned-integer-overflow. default-ub attiva i controlli con problemi di rendimento minimo del compilatore: bool, integer-divide-by-zero, return, returns-nonnull-attribute, shift-exponent, unreachable and vla-bound. La classe di convalida degli interi può essere utilizzata con SANITIZE_TARGET e LOCAL_SANITIZE, mentre default-ub può essere utilizzata solo con SANITIZE_TARGET.

Report sugli errori migliorati

L'implementazione predefinita di UBSan di Android richiama una funzione specificata quando viene rilevato un comportamento non definito. Per impostazione predefinita, questa funzione è abort. Tuttavia, a partire da ottobre 2016, UBSan su Android dispone di una libreria di runtime facoltativa che fornisce report sugli errori più dettagliati, tra cui il tipo di comportamento non definito incontrato, informazioni su file e righe di codice sorgente. Per attivare questo reporting degli errori con controlli di interi, aggiungi quanto segue a un file Android.mk:

LOCAL_SANITIZE:=integer
LOCAL_SANITIZE_DIAG:=integer

Il valore LOCAL_SANITIZE attiva lo strumento di convalida durante la compilazione. LOCAL_SANITIZE_DIAG attiva la modalità di diagnostica per lo sterilizzatore specificato. È possibile impostare LOCAL_SANITIZE e LOCAL_SANITIZE_DIAG su valori diversi, ma vengono attivati solo i controlli in LOCAL_SANITIZE. Se un controllo non è specificato in LOCAL_SANITIZE, ma è specificato in LOCAL_SANITIZE_DIAG, il controllo non è abilitato e non vengono visualizzati messaggi di diagnostica.

Ecco un esempio delle informazioni fornite dalla libreria di runtime UBSan:

pixel-xl:/ # sanitizer-status ubsan
sanitizer-status/sanitizer-status.c:53:6: runtime error: unsigned integer overflow: 18446744073709551615 + 1 cannot be represented in type 'size_t' (aka 'unsigned long')

Sanitizzazione dell'overflow di numeri interi

Gli overflow di interi involontari possono causare danneggiamenti della memoria o vulnerabilità di divulgazione di informazioni nelle variabili associate agli accessi alla memoria o alle allocazioni di memoria. Per contrastare questo problema, abbiamo aggiunto i sanitizzatori di overflow di interi firmati e non firmati di UndefinedBehaviorSanitizer (UBSan) di Clang per rafforzare il framework multimediale in Android 7.0. In Android 9 abbiamo ampliato UBSan per coprire più componenti e migliorato il supporto del sistema di build.

È progettato per aggiungere controlli alle operazioni matematiche/alle istruzioni che potrebbero verificarsi in caso di overflow per interrompere in sicurezza un processo in caso di overflow. Questi sterilizzatori possono mitigare un'intera classe di vulnerabilità di compromissione della memoria e di divulgazione di informazioni in cui la causa principale è un overflow di interi, come la vulnerabilità Stagefright originale.

Esempi e origine

La sanitizzazione degli overflow di interi (IntSan) è fornita dal compilatore e aggiunge instrumentation al codice binario in fase di compilazione per rilevare gli overflow di operazioni aritmetiche. È abilitato per impostazione predefinita in vari componenti della piattaforma, ad esempio /platform/external/libnl/Android.bp.

Implementazione

IntSan utilizza gli strumenti di sanitizzazione degli overflow di interi con segno e senza segno di UBSan. Questa misura di mitigazione è abilitata a livello di modulo. Contribuisce a proteggere i componenti critici di Android e non deve essere disattivato.

Ti consigliamo vivamente di attivare la convalida degli overflow di interi per altri componenti. I candidati ideali sono il codice nativo con privilegi o il codice nativo che analizza l'input utente non attendibile. È associato al sanificatore un piccolo overhead di rendimento che dipende dall'utilizzo del codice e dalla prevalenza delle operazioni aritmetiche. Aspettati una piccola percentuale di overhead e verifica se il rendimento è un problema.

Supporto di IntSan nei file make

Per attivare IntSan in un file make, aggiungi:

LOCAL_SANITIZE := integer_overflow
    # Optional features
    LOCAL_SANITIZE_DIAG := integer_overflow
    LOCAL_SANITIZE_BLOCKLIST := modulename_BLOCKLIST.txt
  • LOCAL_SANITIZE accetta un elenco separato da virgole di sanitazzari, dove integer_overflow è un insieme precompilato di opzioni per i singoli sanitazzari di overflow di interi con segno e senza segno con un elenco bloccato predefinito.
  • LOCAL_SANITIZE_DIAG attiva la modalità di diagnostica per gli disinfettanti. Utilizza la modalità di diagnostica solo durante i test perché non interromperà il funzionamento in caso di overflow, annullando completamente il vantaggio in termini di sicurezza della mitigazione. Per ulteriori dettagli, consulta la sezione Risoluzione dei problemi.
  • LOCAL_SANITIZE_BLOCKLIST ti consente di specificare un file BLOCKLIST per impedire la sanitizzazione di funzioni e file di origine. Per ulteriori dettagli, consulta la sezione Risoluzione dei problemi.

Per un controllo più granulare, attiva i disinfettanti singolarmente utilizzando uno o entrambi i flag:

LOCAL_SANITIZE := signed-integer-overflow, unsigned-integer-overflow
    LOCAL_SANITIZE_DIAG := signed-integer-overflow, unsigned-integer-overflow

Supporto di IntSan nei file blueprint

Per attivare la sanitizzazione degli overflow di interi in un file blueprint, ad esempio /platform/external/libnl/Android.bp, Aggiungi:

   sanitize: {
          integer_overflow: true,
          diag: {
              integer_overflow: true,
          },
          BLOCKLIST: "modulename_BLOCKLIST.txt",
       },

Come per i file make, la proprietà integer_overflow è un insieme precompilato di opzioni per i singoli sanitizzatori di overflow di interi con segno e senza segno con una BLOCKLIST predefinita.

L'insieme di proprietà diag attiva la modalità di diagnostica per gli sterilizzatori. Utilizza la modalità di diagnostica solo durante i test. La modalità di diagnostica non si interrompe in caso di overflow, il che annulla completamente il vantaggio in termini di sicurezza della mitigazione nelle build dell'utente. Per ulteriori dettagli, consulta la sezione Risoluzione dei problemi.

La proprietà BLOCKLIST consente di specificare un file BLOCKLIST che consente agli sviluppatori di impedire la sanitizzazione di funzioni e file di origine. Per ulteriori dettagli, consulta la sezione Risoluzione dei problemi.

Per attivare i disinfettanti singolarmente, utilizza:

   sanitize: {
          misc_undefined: ["signed-integer-overflow", "unsigned-integer-overflow"],
          diag: {
              misc_undefined: ["signed-integer-overflow",
                               "unsigned-integer-overflow",],
          },
          BLOCKLIST: "modulename_BLOCKLIST.txt",
       },

Risoluzione dei problemi

Se attivi la sanitizzazione degli overflow di interi nei nuovi componenti o fai affidamento su librerie della piattaforma che hanno subito la sanitizzazione degli overflow di interi, potresti riscontrare alcuni problemi con overflow di interi benigni che causano interruzioni. Dovresti testare i componenti con la sanitizzazione abilitata per assicurarti che gli overflow benigni possano essere visualizzati.

Per trovare gli aborti causati dalla sanitizzazione nelle build dell'utente, cerca SIGABRT arresti anomali con messaggi di interruzione che indicano un overflow rilevato da UBSan, ad esempio:

pid: ###, tid: ###, name: Binder:###  >>> /system/bin/surfaceflinger <<<
    signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
    Abort message: 'ubsan: sub-overflow'

La traccia dello stack dovrebbe includere la funzione che causa l'interruzione, tuttavia, gli overflow che si verificano nelle funzioni in linea potrebbero non essere evidenti nella traccia dello stack.

Per determinare più facilmente la causa principale, attiva la diagnostica nella raccolta che attiva l'interruzione e prova a riprodurre l'errore. Con la diagnostica attivata, il processo non verrà interrotto e continuerà a essere eseguito. L'interruzione dell'esecuzione consente di massimizzare il numero di overflow benigni in un determinato percorso di esecuzione senza dover eseguire nuovamente la compilazione dopo la correzione di ogni bug. La diagnostica genera un messaggio di errore che include il numero di riga e il file di origine che causa l'interruzione:

frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp:2188:32: runtime error: unsigned integer overflow: 0 - 1 cannot be represented in type 'size_t' (aka 'unsigned long')

Una volta individuata l'operazione aritmetica problematica, assicurati che l'overflow sia benigno e intenzionale (ad es. non abbia implicazioni per la sicurezza). Puoi risolvere il problema di interruzione del programma di pulizia:

  • Rifactorizzazione del codice per evitare il sovraccarico (esempio)
  • Esegui un overflow esplicito tramite le funzioni __builtin_*_overflow di Clang (esempio)
  • Disattivazione della sanitizzazione nella funzione specificando l'attributo no_sanitize (esempio)
  • Disattivazione della sanitizzazione di una funzione o di un file di origine tramite un file BLOCKLIST (esempio)

Devi utilizzare la soluzione più granulare possibile. Ad esempio, per una funzione di grandi dimensioni con molte operazioni aritmetiche e una singola operazione di overflow, è necessario eseguire il refactoring della singola operazione anziché inserire l'intera funzione nella lista bloccata.

Alcuni pattern comuni che possono causare overflow benigni sono:

  • Trasformazioni implicite in cui si verifica un overflow senza segno prima di essere eseguita la conversione in un tipo con segno (esempio)
  • Eliminazioni di liste concatenate che decrementano l'indice del ciclo al momento dell'eliminazione (esempio)
  • Assegnare un tipo non firmato a -1 anziché specificare il valore massimo effettivo (esempio)
  • Loop che decrementano un numero intero senza segno nella condizione (example, example)

Prima di disattivare la sanitizzazione, gli sviluppatori sono invitati ad assicurarsi che i casi in cui lo strumento di sanitizzazione rileva un overflow siano effettivamente benigni, senza effetti collaterali indesiderati o implicazioni per la sicurezza.

Disattiva IntSan

Puoi disattivare IntSan con le liste di blocco o gli attributi di funzione. Disattiva con parsimonia e solo quando il refactoring del codice non è ragionevole o se esiste un overhead delle prestazioni problematico.

Per ulteriori informazioni su come disattivare IntSan con gli attributi funzione e la formattazione del file BLOCKLIST, consulta la documentazione di Clang upstream. L'elenco bloccato deve essere limitato al particolare pulitore utilizzando i nomi delle sezioni che specificano il pulitore di destinazione per evitare ripercussioni su altri pulitori.

Convalida

Al momento non sono disponibili test CTS specifici per la convalida degli overflow di interi. Assicurati invece che i test CTS vengano superati con o senza IntSan abilitato per verificare che non influisca sul dispositivo.

Sanitizzazione dei limiti

BoundsSanitizer (BoundSan) aggiunge la misurazione ai binari per inserire controlli dei limiti intorno agli accessi all'array. Questi controlli vengono aggiunti se il compilatore non può dimostrare in fase di compilazione che l'accesso sarà sicuro e se le dimensioni dell'array saranno note in fase di esecuzione, in modo da poter essere verificate. Android 10 implementa BoundSan in Bluetooth e codec. BoundSan è fornito dal compilatore ed è attivato per impostazione predefinita in vari componenti della piattaforma.

Implementazione

BoundSan utilizza lo stato di controllo dei limiti di UBSan. Questa mitigazione è attivata a livello di modulo. Contribuisce a mantenere al sicuro i componenti critici di Android e non deve essere disattivato.

Ti consigliamo vivamente di attivare BoundSan per componenti aggiuntivi. I candidati ideali sono il codice nativo con privilegi o il codice nativo complesso che analizza input utente non attendibili. Il sovraccarico delle prestazioni associato all'attivazione di BoundSan dipende dal numero di accessi all'array che non possono essere dimostrati sicuri. Aspettati una percentuale di overhead media ridotta e verifica se il rendimento è un problema.

Attivare BoundSan nei file blueprint

BoundSan può essere attivato nei file blueprint aggiungendo "bounds" alla proprietà di sanitizzazione misc_undefined per i moduli di librerie e binari:

    sanitize: {
       misc_undefined: ["bounds"],
       diag: {
          misc_undefined: ["bounds"],
       },
       BLOCKLIST: "modulename_BLOCKLIST.txt",
diag

La proprietà diag attiva la modalità di diagnostica per gli sterilizzatori. Utilizza la modalità di diagnostica solo durante i test. La modalità di diagnostica non viene interrotta in caso di overflow, il che annulla il vantaggio in termini di sicurezza della mitigazione e comporta un overhead delle prestazioni più elevato, pertanto non è consigliata per le build di produzione.

LISTA BLOCCATA

La proprietà BLOCKLIST consente di specificare un file BLOCKLIST che gli sviluppatori possono utilizzare per impedire la sanitizzazione di funzioni e file di origine. Utilizza questa proprietà solo se il rendimento è un problema e i file/le funzioni scelti come target contribuiscono in modo significativo. Controlla manualmente questi file/funzioni per assicurarti che gli accessi all'array siano sicuri. Per ulteriori dettagli, consulta la sezione Risoluzione dei problemi.

Attivare BoundSan nei file make

BoundSan può essere attivato nei file make aggiungendo "bounds" alla variabile LOCAL_SANITIZE per i moduli di librerie e binari:

    LOCAL_SANITIZE := bounds
    # Optional features
    LOCAL_SANITIZE_DIAG := bounds
    LOCAL_SANITIZE_BLOCKLIST := modulename_BLOCKLIST.txt

LOCAL_SANITIZE accetta un elenco di disinfettanti separati da una virgola.

LOCAL_SANITIZE_DIAG attiva la modalità di diagnostica. Utilizza la modalità diagnostica solo durante i test. La modalità di diagnostica non viene interrotta in caso di overflow, il che annulla il vantaggio in termini di sicurezza della mitigazione e comporta un overhead delle prestazioni più elevato, pertanto non è consigliata per le build di produzione.

LOCAL_SANITIZE_BLOCKLIST consente di specificare un file BLOCKLIST che consente agli sviluppatori di impedire la sanitizzazione di funzioni e file di origine. Utilizza questa proprietà solo se il rendimento è un problema e i file/le funzioni scelti come target contribuiscono in modo significativo. Controlla manualmente questi file/funzioni per assicurarti che gli accessi all'array siano sicuri. Per ulteriori dettagli, consulta la sezione Risoluzione dei problemi.

Disattivare BoundSan

Puoi disattivare BoundSan nelle funzioni e nei file di origine con liste bloccate o attributi di funzione. È preferibile mantenere abilitato BoundSan, quindi disattivalo solo se la funzione o il file generano un elevato overhead delle prestazioni e il codice sorgente è stato esaminato manualmente.

Per ulteriori informazioni sulla disattivazione di BoundSan con gli attributi della funzione e la formattazione del file BLOCKLIST, consulta la documentazione di Clang LLVM. Limita la lista bloccata al particolare pulitore utilizzando i nomi delle sezioni che specificano il pulitore di destinazione per evitare ripercussioni su altri pulitori.

Convalida

Non sono disponibili test CTS specifici per BoundSan. Assicurati invece che i test CTS superino con o senza BoundSan abilitato per verificare che non influisca sul dispositivo.

Risoluzione dei problemi

Testa accuratamente i componenti dopo aver attivato BoundSan per assicurarti che eventuali accessi fuori ambito non rilevati in precedenza siano stati risolti.

Gli errori BoundSan possono essere facilmente identificati perché includono il seguente messaggio di interruzione della tomba:

    pid: ###, tid: ###, name: Binder:###  >>> /system/bin/foobar <<<
    signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
    Abort message: 'ubsan: out-of-bounds'

Quando viene eseguito in modalità di diagnostica, il file di origine, il numero di riga e il valore dell'indice vengono stampati in logcat. Per impostazione predefinita, questa modalità non genera un messaggio di interruzione. Controlla logcat per verificare la presenza di eventuali errori.

    external/foo/bar.c:293:13: runtime error: index -1 out of bounds for type 'int [24]'