UndefinedBehaviorSanitizer

UndefinedBehaviorSanitizer (UBSan) effectue une instrumentation au moment de la compilation pour vérifier différents types de comportement non défini. Alors qu'UBSan est capable de détecter de nombreux bugs de comportement non définis , Android prend en charge :

  • alignement
  • bouffon
  • bornes
  • énumération
  • débordement flottant
  • diviser par zéro
  • nombre entier divisé par zéro
  • attribut non nul
  • nul
  • retour
  • renvoie un attribut non nul
  • base de changement
  • décalage d'exposant
  • débordement d'entier signé
  • inaccessible
  • débordement d'entier non signé
  • à destination de la vla

unsigned-integer-overflow, bien qu'il ne s'agisse pas d'un comportement techniquement indéfini, est inclus dans le désinfectant et utilisé dans de nombreux modules Android, y compris les composants du serveur multimédia, pour éliminer toute vulnérabilité latente de débordement d'entier.

Mise en œuvre

Dans le système de build Android, vous pouvez activer UBSan globalement ou localement. Pour activer UBSan à l'échelle mondiale, définissez SANITIZE_TARGET dans Android.mk. Pour activer UBSan au niveau de chaque module, définissez LOCAL_SANITIZE et spécifiez les comportements non définis que vous souhaitez rechercher dans Android.mk. Par exemple:

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)

Et la configuration du plan équivalent (Android.bp) :

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",
            ],
        },
    },

}

Raccourcis UBSan

Android dispose également de deux raccourcis, integer et default-ub , pour activer un ensemble de désinfectants en même temps. integer active integer-divide-by-zero , signed-integer-overflow et unsigned-integer-overflow . default-ub active les vérifications qui présentent des problèmes minimes de performances du compilateur : bool, integer-divide-by-zero, return, returns-nonnull-attribute, shift-exponent, unreachable and vla-bound . La classe de désinfectant entière peut être utilisée avec SANITIZE_TARGET et LOCAL_SANITIZE, tandis que default-ub ne peut être utilisée qu'avec SANITIZE_TARGET.

Meilleur rapport d'erreurs

L'implémentation UBSan par défaut d'Android appelle une fonction spécifiée lorsqu'un comportement non défini est rencontré. Par défaut, cette fonction est abandonnée. Cependant, depuis octobre 2016, UBSan sur Android dispose d'une bibliothèque d'exécution facultative qui fournit des rapports d'erreurs plus détaillés, notamment le type de comportement non défini rencontré, les informations sur les fichiers et les lignes de code source. Pour activer ce rapport d'erreurs avec des vérifications d'entiers, ajoutez ce qui suit à un fichier Android.mk :

LOCAL_SANITIZE:=integer
LOCAL_SANITIZE_DIAG:=integer

La valeur LOCAL_SANITIZE active le désinfectant pendant la génération. LOCAL_SANITIZE_DIAG active le mode de diagnostic pour le désinfectant spécifié. Il est possible de définir LOCAL_SANITIZE et LOCAL_SANITIZE_DIAG sur des valeurs différentes, mais seules les vérifications dans LOCAL_SANITIZE sont activées. Si une vérification n'est pas spécifiée dans LOCAL_SANITIZE, mais est spécifiée dans LOCAL_SANITIZE_DIAG, la vérification n'est pas activée et les messages de diagnostic ne sont pas émis.

Voici un exemple des informations fournies par la bibliothèque d'exécution 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')

Désinfection par débordement entier

Les dépassements involontaires d'entiers peuvent entraîner une corruption de la mémoire ou des vulnérabilités de divulgation d'informations dans les variables associées aux accès à la mémoire ou aux allocations de mémoire. Pour lutter contre cela, nous avons ajouté les désinfectants de débordement d'entiers signés et non signés UndefinedBehaviorSanitizer (UBSan) de Clang pour renforcer le cadre multimédia dans Android 7.0. Dans Android 9, nous avons étendu UBSan pour couvrir davantage de composants et amélioré la prise en charge du système de construction.

Ceci est conçu pour ajouter des contrôles autour des opérations/instructions arithmétiques (qui pourraient déborder) afin d'abandonner en toute sécurité un processus si un débordement se produit. Ces désinfectants peuvent atténuer toute une classe de vulnérabilités de corruption de mémoire et de divulgation d’informations dont la cause première est un débordement d’entier, comme la vulnérabilité originale Stagefright.

Exemples et source

Integer Overflow Sanitization (IntSan) est fourni par le compilateur et ajoute une instrumentation dans le binaire pendant la compilation pour détecter les débordements arithmétiques. Il est activé par défaut dans divers composants de la plateforme, par exemple /platform/external/libnl/Android.bp .

Mise en œuvre

IntSan utilise les désinfectants de débordement d'entiers signés et non signés d'UBSan. Cette atténuation est activée au niveau de chaque module. Il permet de sécuriser les composants critiques d’Android et ne doit pas être désactivé.

Nous vous encourageons fortement à activer Integer Overflow Sanitization pour les composants supplémentaires. Les candidats idéaux sont le code natif privilégié ou le code natif qui analyse les entrées utilisateur non fiables. Il existe une petite surcharge de performances associée au désinfectant qui dépend de l'utilisation du code et de la prévalence des opérations arithmétiques. Attendez-vous à un petit pourcentage de frais généraux et testez si les performances sont un problème.

Prise en charge d'IntSan dans les makefiles

Pour activer IntSan dans un makefile, ajoutez :

LOCAL_SANITIZE := integer_overflow
    # Optional features
    LOCAL_SANITIZE_DIAG := integer_overflow
    LOCAL_SANITIZE_BLOCKLIST := modulename_BLOCKLIST.txt
  • LOCAL_SANITIZE prend une liste de désinfectants séparés par des virgules, integer_overflow étant un ensemble pré-emballé d'options pour les désinfectants de débordement d'entiers signés et non signés individuels avec une BLOCKLIST par défaut .
  • LOCAL_SANITIZE_DIAG active le mode de diagnostic pour les désinfectants. Utilisez le mode de diagnostic uniquement pendant les tests, car cela n'abandonnera pas en cas de débordement, annulant ainsi complètement l'avantage de sécurité de l'atténuation. Voir Dépannage pour plus de détails.
  • LOCAL_SANITIZE_BLOCKLIST vous permet de spécifier un fichier BLOCKLIST pour empêcher le nettoyage des fonctions et des fichiers sources. Voir Dépannage pour plus de détails.

Si vous souhaitez un contrôle plus granulaire, activez les désinfectants individuellement à l'aide d'un ou des deux indicateurs :

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

Prise en charge d'IntSan dans les fichiers de plans

Pour activer la désinfection par débordement d'entier dans un fichier Blueprint, tel que /platform/external/libnl/Android.bp , ajoutez :

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

Comme pour les fichiers make, la propriété integer_overflow est un ensemble pré-emballé d'options pour les désinfectants de débordement d'entiers individuels signés et non signés avec une BLOCKLIST par défaut .

L'ensemble de propriétés diag active le mode de diagnostic pour les désinfectants. Utilisez le mode de diagnostic uniquement pendant les tests. Le mode Diagnostics n'abandonne pas en cas de débordement, ce qui annule complètement l'avantage de sécurité de l'atténuation dans les builds utilisateur. Voir Dépannage pour plus de détails.

La propriété BLOCKLIST permet de spécifier un fichier BLOCKLIST qui permet aux développeurs d'empêcher le nettoyage des fonctions et des fichiers sources. Voir Dépannage pour plus de détails.

Pour activer les désinfectants individuellement, utilisez :

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

Dépannage

Si vous activez la désinfection par débordement d'entier dans de nouveaux composants, ou si vous comptez sur des bibliothèques de plate-forme dotées d'une désinfection par débordement d'entier, vous pouvez rencontrer quelques problèmes liés à des débordements d'entiers bénins provoquant des abandons. Vous devez tester les composants avec la désinfection activée pour garantir que des débordements bénins peuvent être détectés.

Pour rechercher les abandons provoqués par la désinfection dans les builds utilisateur, recherchez les plantages SIGABRT avec des messages d'abandon indiquant un débordement détecté par UBSan, tels que :

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

La trace de pile doit inclure la fonction à l'origine de l'abandon. Toutefois, les débordements qui se produisent dans les fonctions en ligne peuvent ne pas être évidents dans la trace de pile.

Pour déterminer plus facilement la cause première, activez les diagnostics dans la bibliothèque déclenchant l'abandon et tentez de reproduire l'erreur. Une fois les diagnostics activés, le processus n'abandonnera pas et continuera à s'exécuter. Ne pas abandonner permet de maximiser le nombre de débordements bénins dans un chemin d'exécution particulier sans avoir à recompiler après avoir corrigé chaque bug. Les diagnostics génèrent un message d'erreur qui inclut le numéro de ligne et le fichier source à l'origine de l'abandon :

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

Une fois l'opération arithmétique problématique localisée, assurez-vous que le débordement est inoffensif et intentionnel (par exemple, n'a aucune implication en matière de sécurité). Vous pouvez résoudre l’abandon du désinfectant en :

  • Refactoriser le code pour éviter le débordement ( exemple )
  • Débordement explicite via les fonctions __builtin_*_overflow de Clang ( exemple )
  • Désactiver la désinfection dans la fonction en spécifiant l'attribut no_sanitize ( exemple )
  • Désactivation de la désinfection d'une fonction ou d'un fichier source via un fichier BLOCKLIST ( exemple )

Vous devez utiliser la solution la plus granulaire possible. Par exemple, pour une fonction volumineuse comportant de nombreuses opérations arithmétiques et une seule opération de débordement, l'opération unique devrait être refactorisée plutôt que la fonction entière BLOCKLIST.

Les schémas courants pouvant entraîner des débordements bénins comprennent :

  • Castages implicites où un débordement non signé se produit avant d'être converti en un type signé ( exemple )
  • Suppressions de liste chaînée qui décrémente l'index de boucle lors de la suppression ( exemple )
  • Attribuer un type non signé à -1 au lieu de spécifier la valeur maximale réelle ( exemple )
  • Boucles qui décrémentent un entier non signé dans la condition ( exemple , exemple )

Il est recommandé aux développeurs de s'assurer que, dans les cas où le désinfectant détecte un débordement, celui-ci est effectivement bénin, sans effets secondaires involontaires ni implications en matière de sécurité, avant de désactiver la désinfection.

Désactiver IntSan

Vous pouvez désactiver IntSan avec des BLOCKLIST ou des attributs de fonction. Désactivez-le avec parcimonie et uniquement lorsque la refactorisation du code est par ailleurs déraisonnable ou en cas de surcharge de performances problématique.

Consultez la documentation Clang en amont pour plus d'informations sur la désactivation d'IntSan avec les attributs de fonction et le formatage du fichier BLOCKLIST . La BLOCKLISTing doit être limitée au désinfectant particulier en utilisant des noms de section spécifiant le désinfectant cible pour éviter d'avoir un impact sur d'autres désinfectants.

Validation

Actuellement, il n’existe aucun test CTS spécifiquement pour la désinfection par débordement entier. Au lieu de cela, assurez-vous que les tests CTS réussissent avec ou sans IntSan activé pour vérifier qu'il n'a pas d'impact sur l'appareil.

Désinfection des limites

BoundsSanitizer (BoundSan) ajoute une instrumentation aux binaires pour insérer des contrôles de limites autour des accès aux tableaux. Ces vérifications sont ajoutées si le compilateur ne peut pas prouver au moment de la compilation que l'accès sera sécurisé et si la taille du tableau sera connue au moment de l'exécution, afin de pouvoir la vérifier. Android 10 déploie BoundSan en Bluetooth et en codecs. BoundSan est fourni par le compilateur et est activé par défaut dans divers composants de la plateforme.

Mise en œuvre

BoundSan utilise le désinfectant pour limites d'UBSan . Cette atténuation est activée au niveau de chaque module. Il permet de sécuriser les composants critiques d’Android et ne doit pas être désactivé.

Nous vous encourageons fortement à activer BoundSan pour des composants supplémentaires. Les candidats idéaux sont un code natif privilégié ou un code natif complexe qui analyse les entrées utilisateur non fiables. La surcharge de performances associée à l'activation de BoundSan dépend du nombre d'accès à la baie dont la sécurité ne peut pas être prouvée. Attendez-vous en moyenne à un petit pourcentage de frais généraux et testez si les performances sont un problème.

Activation de BoundSan dans les fichiers de plans

BoundSan peut être activé dans les fichiers Blueprint en ajoutant "bounds" à la propriété misc_undefined sanitize pour les modules binaires et de bibliothèque :

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

La propriété diag active le mode de diagnostic pour les désinfectants. Utilisez le mode de diagnostic uniquement pendant les tests. Le mode Diagnostics n’abandonne pas en cas de débordement, ce qui annule l’avantage de sécurité de l’atténuation et entraîne une surcharge de performances plus élevée. Il n’est donc pas recommandé pour les versions de production.

LISTE DE BLOCAGE

La propriété BLOCKLIST permet de spécifier un fichier BLOCKLIST que les développeurs peuvent utiliser pour empêcher le nettoyage des fonctions et des fichiers sources. Utilisez cette propriété uniquement si les performances sont un problème et que les fichiers/fonctions ciblés y contribuent de manière substantielle. Auditez manuellement ces fichiers/fonctions pour vous assurer que les accès à la baie sont sécurisés. Voir Dépannage pour plus de détails.

Activation de BoundSan dans les makefiles

BoundSan peut être activé dans les makefiles en ajoutant "bounds" à la variable LOCAL_SANITIZE pour les modules binaires et bibliothèques :

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

LOCAL_SANITIZE accepte une liste de désinfectants séparés par une virgule.

LOCAL_SANITIZE_DIAG active le mode de diagnostic. Utilisez le mode de diagnostic uniquement pendant les tests. Le mode Diagnostics n’abandonne pas en cas de débordement, ce qui annule l’avantage de sécurité de l’atténuation et entraîne une surcharge de performances plus élevée. Il n’est donc pas recommandé pour les versions de production.

LOCAL_SANITIZE_BLOCKLIST permet de spécifier un fichier BLOCKLIST qui permet aux développeurs d'empêcher le nettoyage des fonctions et des fichiers sources. Utilisez cette propriété uniquement si les performances sont un problème et que les fichiers/fonctions ciblés y contribuent de manière substantielle. Auditez manuellement ces fichiers/fonctions pour vous assurer que les accès à la baie sont sécurisés. Voir Dépannage pour plus de détails.

Désactivation de BoundSan

Vous pouvez désactiver BoundSan dans les fonctions et les fichiers sources avec des BLOCKLIST ou des attributs de fonction. Il est préférable de garder BoundSan activé, donc de le désactiver uniquement si la fonction ou le fichier crée une surcharge de performances importante et que la source a été examinée manuellement.

Pour plus d'informations sur la désactivation de BoundSan avec les attributs de fonction et le formatage du fichier BLOCKLIST , reportez-vous à la documentation Clang LLVM. Étendez la BLOCKLISTing au désinfectant particulier en utilisant des noms de section spécifiant le désinfectant cible pour éviter d'avoir un impact sur d'autres désinfectants.

Validation

Il n'existe pas de test CTS spécifiquement pour BoundSan. Au lieu de cela, assurez-vous que les tests CTS réussissent avec ou sans BoundSan activé pour vérifier qu'il n'a pas d'impact sur l'appareil.

Dépannage

Testez minutieusement les composants après avoir activé BoundSan pour vous assurer que tous les accès hors limites non détectés auparavant sont traités.

Les erreurs BoundSan peuvent être facilement identifiées car elles incluent le message d'abandon de base suivant :

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

Lors de l'exécution en mode diagnostic, le fichier source, le numéro de ligne et la valeur d'index sont imprimés dans logcat . Par défaut, ce mode ne génère pas de message d'abandon. Consultez logcat pour rechercher d’éventuelles erreurs.

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