UndefinedBehaviorSanitizer

UndefinedBehaviorSanitizer (UBSan) esegue l'instrumentazione in fase di compilazione per verificare vari tipi di comportamento indefinito. Sebbene UBSan sia in grado di rilevare molti bug di comportamento indefinito, Android supporta:

  • allineamento
  • bool
  • limiti
  • enum
  • float-cast-overflow
  • float-divide-by-zero
  • integer-divide-by-zero
  • attributo nonnull
  • null
  • ritorno
  • returns-nonnull-attribute
  • shift-base
  • shift-exponent
  • signed-integer-overflow
  • non raggiungibile
  • unsigned-integer-overflow
  • vla-bound

unsigned-integer-overflow, anche se tecnicamente non è un comportamento indefinito, è incluso nel sanitizer e utilizzato in molti moduli Android, inclusi i componenti mediaserver, per eliminare qualsiasi vulnerabilità latente di integer overflow.

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 abilitare 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 UBSan

Android ha anche due scorciatoie, integer e default-ub, per attivare contemporaneamente un insieme di sanificatori. L'intero abilita integer-divide-by-zero, signed-integer-overflow e unsigned-integer-overflow. default-ub attiva i controlli che presentano problemi di rendimento del compilatore minimi: bool, integer-divide-by-zero, return, returns-nonnull-attribute, shift-exponent, unreachable and vla-bound. La classe integer sanitizer può essere utilizzata con SANITIZE_TARGET e LOCAL_SANITIZE, mentre default-ub può essere utilizzata solo con SANITIZE_TARGET.

Migliore segnalazione degli errori

L'implementazione UBSan predefinita di Android richiama una funzione specificata quando viene rilevato un comportamento indefinito. Per impostazione predefinita, questa funzione viene interrotta. 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 indefinito riscontrato, informazioni sul file e sulla riga di codice sorgente. Per attivare la segnalazione di questo errore con i controlli degli interi, aggiungi quanto segue a un file Android.mk:

LOCAL_SANITIZE:=integer
LOCAL_SANITIZE_DIAG:=integer

Il valore LOCAL_SANITIZE attiva il sanitizer durante la build. LOCAL_SANITIZE_DIAG attiva la modalità diagnostica per il sanitizer specificato. È possibile impostare LOCAL_SANITIZE e LOCAL_SANITIZE_DIAG su valori diversi, ma vengono abilitati 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 diagnostici.

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')

Sanificazione dell'overflow di numeri interi

L'overflow di interi non intenzionale può causare il danneggiamento della memoria o vulnerabilità di divulgazione delle informazioni nelle variabili associate agli accessi alla memoria o alle allocazioni di memoria. Per contrastare questo problema, abbiamo aggiunto i sanitizer per l'overflow di interi con segno e senza segno di UndefinedBehaviorSanitizer (UBSan) di Clang per rafforzare il framework multimediale in Android 7.0. In Android 9, abbiamo esteso UBSan per coprire più componenti e abbiamo migliorato il supporto del sistema di compilazione.

Questa funzionalità è progettata per aggiungere controlli alle operazioni e alle istruzioni aritmetiche, che potrebbero causare overflow, per interrompere in modo sicuro un processo in caso di overflow. Questi strumenti di sanificazione possono mitigare un'intera classe di vulnerabilità di danneggiamento della memoria e divulgazione di informazioni in cui la causa principale è un overflow di numeri interi, come la vulnerabilità Stagefright originale.

Esempi e fonte

Integer Overflow Sanitization (IntSan) viene fornito dal compilatore e aggiunge strumentazione al file binario durante la compilazione per rilevare gli overflow aritmetici. È abilitato per impostazione predefinita in vari componenti della piattaforma, ad esempio /platform/external/libnl/Android.bp.

Implementazione

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

Ti consigliamo vivamente di abilitare la sanificazione dell'overflow di interi per componenti aggiuntivi. I candidati ideali sono codice nativo privilegiato o codice nativo che analizza l'input utente non attendibile. È presente un piccolo overhead di rendimento associato al sanitizer, che dipende dall'utilizzo del codice e dalla prevalenza di operazioni aritmetiche. Prevedi una piccola percentuale di overhead e verifica se il rendimento è un problema.

Supportare IntSan nei makefile

Per attivare IntSan in un makefile, 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 sanificatori, con integer_overflow che è un insieme preconfigurato di opzioni per i singoli sanificatori di overflow di numeri interi con segno e senza segno con un'impostazione predefinita BLOCKLIST.
  • LOCAL_SANITIZE_DIAG attiva la modalità di diagnostica per i sanitizzatori. Utilizza la modalità di diagnostica solo durante i test, perché non interrompe in caso di overflow, annullando completamente il vantaggio 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 sanificazione di funzioni e file di origine. Per ulteriori dettagli, consulta la sezione Risoluzione dei problemi.

Se vuoi un controllo più granulare, attiva i sanificatori 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 di progetto

Per attivare la sanificazione dell'overflow di numeri 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 preconfigurato di opzioni per i singoli sanificatori di overflow di numeri interi con segno e senza segno con una BLOCKLIST predefinita.

Il set di proprietà diag attiva la modalità di diagnostica per i sanitizzatori. Utilizza la modalità di diagnostica solo durante i test. La modalità di diagnostica non interrompe in caso di overflow, il che annulla completamente il vantaggio di sicurezza della mitigazione nelle build 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 sanificazione di funzioni e file di origine. Per ulteriori dettagli, consulta la sezione Risoluzione dei problemi.

Per attivare i sanificatori 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 sanificazione dell'overflow di interi nei nuovi componenti o ti affidi a librerie della piattaforma che hanno subito la sanificazione dell'overflow di interi, potresti riscontrare alcuni problemi con overflow di interi benigni che causano interruzioni. Devi testare i componenti con la sanificazione abilitata per assicurarti che possano essere rilevati overflow benigni.

Per trovare gli arresti anomali causati dalla sanificazione nelle build utente, cerca SIGABRT 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 deve includere la funzione che causa l'interruzione, tuttavia, i traboccamenti che si verificano nelle funzioni inline potrebbero non essere evidenti nella traccia dello stack.

Per determinare più facilmente la causa principale, attiva la diagnostica nella libreria che attiva l'interruzione e tenta di riprodurre l'errore. Con la diagnostica attivata, la procedura non verrà interrotta e continuerà a essere eseguita. Il mancato annullamento consente di massimizzare il numero di overflow benigni in un percorso di esecuzione specifico senza dover ricompilare dopo aver corretto ogni bug. La diagnostica produce 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 dell'interruzione del sanificatore:

  • Refactoring del codice per evitare l'overflow (esempio)
  • Overflow esplicito tramite le funzioni __builtin_*_overflow di Clang (esempio)
  • Disattivazione della sanificazione nella funzione specificando l'attributo no_sanitize (esempio)
  • Disattivazione della sanificazione di una funzione o di un file sorgente tramite un file BLOCKLIST (esempio)

Devi utilizzare la soluzione più granulare possibile. Ad esempio, una funzione di grandi dimensioni con molte operazioni aritmetiche e una singola operazione di overflow deve essere sottoposta a refactoring della singola operazione anziché dell'intera funzione BLOCKLISTed.

I pattern comuni che possono causare overflow benigni includono:

  • Cast impliciti in cui si verifica un overflow senza segno prima del cast a un tipo con segno (esempio)
  • Eliminazioni di elenchi collegati che decrementano l'indice del ciclo all'eliminazione (esempio)
  • Assegnazione di un tipo senza segno a -1 anziché specificare il valore massimo effettivo (esempio)
  • Cicli che decrementano un numero intero senza segno nella condizione (esempio, esempio)

Prima di disattivare la sanificazione, è consigliabile che gli sviluppatori si assicurino che i casi in cui il sanificatore rileva un overflow siano effettivamente benigni e non abbiano effetti collaterali o implicazioni di sicurezza indesiderati.

Disattiva IntSan

Puoi disattivare IntSan con le BLOCKLIST o gli attributi delle funzioni. Disattiva con parsimonia e solo quando il refactoring del codice è altrimenti irragionevole o se si verifica un overhead delle prestazioni problematico.

Per saperne di più sulla disattivazione di IntSan con attributi delle funzioni e formattazione dei file BLOCKLIST, consulta la documentazione di Clang upstream. L'inserimento nella BLOCKLIST deve essere limitato al particolare sanitizer utilizzando i nomi delle sezioni che specificano il sanitizer di destinazione per evitare di influire su altri sanitizer.

Convalida

Al momento, non esiste un test CTS specifico per la sanificazione dell'overflow di numeri interi. Assicurati invece che i test CTS vengano superati con o senza IntSan abilitato per verificare che non influisca sul dispositivo.

Sanificazione dei limiti

BoundsSanitizer (BoundSan) aggiunge la strumentazione ai file binari per inserire controlli dei limiti intorno agli accessi agli array. Questi controlli vengono aggiunti se il compilatore non può dimostrare in fase di compilazione che l'accesso sarà sicuro e se la dimensione dell'array sarà nota in fase di runtime, in modo che possa essere verificata. Android 10 esegue il deployment di BoundSan in Bluetooth e codec. BoundSan viene fornito dal compilatore ed è abilitato per impostazione predefinita in vari componenti della piattaforma.

Implementazione

BoundSan utilizza il bounds sanitizer UBSan. Questa mitigazione è abilitata a livello di modulo. Contribuisce a proteggere i componenti critici di Android e non deve essere disattivato.

Ti consigliamo vivamente di attivare BoundSan per i componenti aggiuntivi. I candidati ideali sono codice nativo privilegiato o codice nativo complesso che analizza l'input utente non attendibile. L'overhead delle prestazioni associato all'attivazione di BoundSan dipende dal numero di accessi agli array per i quali non è possibile dimostrare la sicurezza. In media, prevedi una piccola percentuale di overhead e verifica se il rendimento è un problema.

Attivare BoundSan nei file blueprint

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

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

La proprietà diag attiva la modalità di diagnostica per i sanificatori. 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 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 sanificazione di funzioni e file sorgente. Utilizza questa proprietà solo se le prestazioni sono un problema e i file/le funzioni di destinazione contribuiscono in modo sostanziale. Controlla manualmente questi file/funzioni per assicurarti che gli accessi agli array siano sicuri. Per ulteriori dettagli, consulta la sezione Risoluzione dei problemi.

Attivare BoundSan nei makefile

BoundSan può essere abilitato nei makefile aggiungendo "bounds" alla variabile LOCAL_SANITIZE per i moduli binari e delle librerie:

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

LOCAL_SANITIZE accetta un elenco di sanificatori 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 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 sanificazione di funzioni e file di origine. Utilizza questa proprietà solo se le prestazioni sono un problema e i file/le funzioni di destinazione contribuiscono in modo sostanziale. Controlla manualmente questi file/funzioni per assicurarti che gli accessi agli array siano sicuri. Per ulteriori dettagli, consulta la sezione Risoluzione dei problemi.

Disattiva BoundSan

Puoi disattivare BoundSan nelle funzioni e nei file di origine con le BLOCKLIST o gli attributi delle funzioni. È consigliabile mantenere attivato BoundSan, quindi disattivarlo solo se la funzione o il file crea un overhead delle prestazioni elevato e l'origine è stata esaminata manualmente.

Per maggiori informazioni sulla disattivazione di BoundSan con gli attributi della funzione e la formattazione del file BLOCKLIST, consulta la documentazione di Clang LLVM. Limita l'inserimento nella lista bloccata al sanificatore specifico utilizzando i nomi delle sezioni che specificano il sanificatore di destinazione per evitare di influire su altri sanificatori.

Convalida

Non esiste un test CTS specifico per BoundSan. Assicurati invece che i test CTS vengano superati con o senza BoundSan abilitato per verificare che non influisca sul dispositivo.

Risoluzione dei problemi

Testa a fondo i componenti dopo aver attivato BoundSan per assicurarti che gli accessi fuori dai limiti precedentemente non rilevati vengano risolti.

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

    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 errori.

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