UndefinedBehaviorSanitizer (UBSan) effectue une instrumentation au moment de la compilation pour vérifier différents types de comportements indéfinis. Bien qu'UBSan soit capable de détecter de nombreux bugs de comportement indéfini, Android prend en charge :
- alignement
- bool
- limites
- enum
- float-cast-overflow
- float-divide-by-zero
- integer-divide-by-zero
- attribut non nul
- null
- retour
- returns-nonnull-attribute
- shift-base
- shift-exponent
- signed-integer-overflow
- injoignable
- unsigned-integer-overflow (dépassement de capacité d'un entier non signé)
- vla-bound
Le dépassement de capacité d'entier non signé, bien qu'il ne s'agisse pas techniquement d'un comportement indéfini, est inclus dans le programme d'assainissement et utilisé dans de nombreux modules Android, y compris les composants mediaserver, pour éliminer toute vulnérabilité latente de dépassement de capacité d'entier.
Implémentation
Dans le système de compilation Android, vous pouvez activer UBSan de manière globale ou locale. Pour activer UBSan de manière globale, définissez SANITIZE_TARGET dans Android.mk. Pour activer UBSan au niveau d'un module, définissez LOCAL_SANITIZE et spécifiez les comportements indéfinis que vous souhaitez rechercher dans Android.mk. 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)
Voici la configuration Blueprint (Android.bp) équivalente :
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 propose également deux raccourcis, integer et default-ub, pour activer un ensemble de vérificateurs 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 de performances minimes au niveau du compilateur : bool, integer-divide-by-zero, return,
returns-nonnull-attribute, shift-exponent, unreachable and vla-bound. La classe d'assainisseur d'entiers peut être utilisée avec SANITIZE_TARGET et LOCAL_SANITIZE, tandis que default-ub ne peut être utilisé qu'avec SANITIZE_TARGET.
Meilleure signalisation des erreurs
L'implémentation UBSan par défaut d'Android appelle une fonction spécifiée lorsqu'un comportement indéfini est rencontré. Par défaut, cette fonction est "abort". Toutefois, depuis octobre 2016, UBSan sur Android dispose d'une bibliothèque d'exécution facultative qui fournit des rapports d'erreur plus détaillés, y compris le type de comportement indéfini rencontré, ainsi que des informations sur le fichier et la ligne de code source. Pour activer ce signalement d'erreur 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 vérificateur lors de la compilation. LOCAL_SANITIZE_DIAG active le mode diagnostic pour le vérificateur 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 l'est dans LOCAL_SANITIZE_DIAG, elle n'est pas activée et aucun message de diagnostic n'est fourni.
Voici un exemple d'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')
Nettoyage des dépassements d'entiers
Les dépassements de capacité d'entiers involontaires peuvent entraîner des failles de corruption de mémoire ou de divulgation d'informations dans les variables associées aux accès ou aux allocations de mémoire. Pour lutter contre ce problème, nous avons ajouté les outils de vérification des dépassements de capacité des entiers signés et non signés UndefinedBehaviorSanitizer (UBSan) de Clang à harden the media framework 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 compilation.
Cette fonctionnalité est conçue pour ajouter des vérifications autour des opérations et instructions arithmétiques (qui peuvent entraîner un dépassement) afin d'abandonner un processus en toute sécurité en cas de dépassement. Ces outils de désinfection peuvent atténuer toute une classe de failles de corruption de mémoire et de divulgation d'informations dont la cause première est un dépassement de capacité d'entier, comme la faille Stagefright d'origine.
Exemples et source
Integer Overflow Sanitization (IntSan) est fourni par le compilateur et ajoute une instrumentation au fichier binaire lors de la compilation pour détecter les dépassements arithmétiques. Elle est activée par défaut dans différents composants de la plate-forme, par exemple /platform/external/libnl/Android.bp.
Implémentation
IntSan utilise les outils de désinfection des dépassements de capacité des 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 vivement à activer la désinfection des dépassements d'entiers pour d'autres composants. Les candidats idéaux sont le code natif privilégié ou le code natif qui analyse les entrées utilisateur non fiables. Le désinfectant est associé à une légère surcharge de performances qui dépend de l'utilisation du code et de la fréquence des opérations arithmétiques. Attendez-vous à un faible pourcentage de surcharge et testez si les performances sont un problème.
Prise en charge d'IntSan dans les fichiers makefile
Pour activer IntSan dans un fichier makefile, ajoutez :
LOCAL_SANITIZE := integer_overflow # Optional features LOCAL_SANITIZE_DIAG := integer_overflow LOCAL_SANITIZE_BLOCKLIST := modulename_BLOCKLIST.txt
LOCAL_SANITIZEaccepte une liste de vérificateurs séparés par des virgules,integer_overflowétant un ensemble d'options préemballées pour les vérificateurs de dépassement de capacité d'entiers signés et non signés individuels avec une BLOCKLIST par défaut.LOCAL_SANITIZE_DIAGactive le mode diagnostic pour les outils de désinfection. N'utilisez le mode diagnostic que pendant les tests, car il n'annulera pas les dépassements, ce qui annulera complètement l'avantage de sécurité de la mesure d'atténuation. Pour en savoir plus, consultez la section Dépannage.LOCAL_SANITIZE_BLOCKLISTvous permet de spécifier un fichier BLOCKLIST pour empêcher la désinfection des fonctions et des fichiers sources. Pour en savoir plus, consultez la section Dépannage.
Si vous souhaitez un contrôle plus précis, activez les outils de désinfection individuellement à l'aide d'un ou des deux indicateurs suivants :
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 Blueprint
Pour activer la désinfection du dépassement de capacité d'entier dans un fichier de plan, 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 d'options prédéfinies pour les outils de vérification des dépassements de capacité des entiers signés et non signés avec une BLOCKLIST par défaut.
L'ensemble de propriétés diag active le mode diagnostic pour les outils de désinfection. N'utilisez le mode diagnostic que pendant les tests. Le mode Diagnostics n'abandonne pas en cas de dépassement de capacité, ce qui annule complètement l'avantage de sécurité de la mitigation dans les builds utilisateur. Pour en savoir plus, consultez la section Dépannage.
La propriété BLOCKLIST permet de spécifier un fichier BLOCKLIST qui permet aux développeurs d'empêcher la désinfection des fonctions et des fichiers sources. Pour en savoir plus, consultez la section Dépannage.
Pour activer les outils de désinfection 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 des dépassements d'entiers dans de nouveaux composants ou si vous vous appuyez sur des bibliothèques de plate-forme qui ont été désinfectées contre les dépassements d'entiers, vous pouvez rencontrer quelques problèmes liés à des dépassements d'entiers bénins entraînant des abandons. Vous devez tester les composants avec la désinfection activée pour vous assurer que les débordements bénins peuvent être détectés.
Pour trouver les abandons causés par la désinfection dans les builds utilisateur, recherchez les plantages SIGABRT avec des messages d'abandon indiquant un dépassement de capacité détecté par UBSan, par exemple :
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épassements de capacité qui se produisent dans les fonctions intégrées 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 qui déclenche l'abandon et essayez de reproduire l'erreur. Si les diagnostics sont activés, le processus ne s'arrête pas, mais continue de s'exécuter. Le fait de ne pas abandonner permet de maximiser le nombre de dépassements 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épassement de capacité est bénin et intentionnel (par exemple, qu'il n'a aucune implication en termes de sécurité). Vous pouvez résoudre l'arrêt du vérificateur en procédant comme suit :
- Refactoriser le code pour éviter le débordement (exemple)
- Dépassement de capacité explicite via les fonctions __builtin_*_overflow de Clang (exemple)
- Désactivation de la désinfection dans la fonction en spécifiant l'attribut
no_sanitize(exemple) - Désactiver l'assainissement d'une fonction ou d'un fichier source à l'aide d'un fichier BLOCKLIST (exemple)
Vous devez utiliser la solution la plus précise possible. Par exemple, une grande fonction comportant de nombreuses opérations arithmétiques et une seule opération de dépassement de capacité doit être refactorisée au niveau de l'opération unique plutôt que d'être entièrement BLOCKLISTée.
Voici quelques exemples de configurations courantes pouvant entraîner des dépassements bénins :
- Casts implicites où un dépassement de capacité non signé se produit avant d'être casté en type signé (exemple)
- Suppression d'une liste chaînée qui décrémente l'index de boucle lors de la suppression (exemple)
- Attribution d'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)
Avant de désactiver la désinfection, il est recommandé aux développeurs de s'assurer que les cas où le désinfectant détecte un dépassement sont effectivement bénins, sans effets secondaires ni implications pour la sécurité indésirables.
Désactiver IntSan
Vous pouvez désactiver IntSan avec des BLOCKLIST ou des attributs de fonction. Désactivez-les avec parcimonie et uniquement lorsque la refactorisation du code est déraisonnable ou s'il existe une surcharge de performances problématique.
Pour en savoir plus sur la désactivation d'IntSan avec les attributs de fonction et le format du fichier BLOCKLIST, consultez la documentation Clang en amont. BLOCKLISTing should be scoped to the particular sanitizer by using section names specifying the target sanitizer to avoid impacting other sanitizers.
Validation
Il n'existe actuellement aucun test CTS spécifique à la désinfection du dépassement d'entier. Assurez-vous plutôt que les tests CTS réussissent avec ou sans IntSan activé pour vérifier que cela n'a pas d'incidence sur l'appareil.
Nettoyage des limites
BoundsSanitizer (BoundSan) ajoute une instrumentation aux binaires afin de vérifier les 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 qu'elle puisse être vérifiée. Android 10 déploie BoundSan dans Bluetooth et les codecs. BoundSan est fourni par le compilateur et est activé par défaut dans divers composants de la plate-forme.
Implémentation
BoundSan utilise le bounds sanitizer d'UBSan. Cette atténuation est activée au niveau de chaque module. Il permet de sécuriser les composants essentiels d'Android et ne doit pas être désactivé.
Nous vous encourageons vivement à activer BoundSan pour les autres composants. Les candidats idéaux sont le code natif privilégié ou le 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 au tableau qui ne peuvent pas être prouvés comme sûrs. Attendez-vous à un faible pourcentage de surcharge en moyenne et testez si les performances sont un problème.
Activer BoundSan dans les fichiers de plan
BoundSan peut être activé dans les fichiers blueprint en ajoutant "bounds" à la propriété sanitize misc_undefined pour les modules binaires et de bibliothèque :
sanitize: {
misc_undefined: ["bounds"],
diag: {
misc_undefined: ["bounds"],
},
BLOCKLIST: "modulename_BLOCKLIST.txt",diag
La propriété diag active le mode diagnostic pour les outils de désinfection.
N'utilisez le mode diagnostic que pendant les tests. Le mode Diagnostics n'abandonne pas les dépassements, 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 la désinfection des fonctions et des fichiers sources. N'utilisez cette propriété que 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 aux tableaux sont sécurisés. Pour en savoir plus, consultez la section Dépannage.
Activer BoundSan dans les fichiers makefile
BoundSan peut être activé dans les fichiers make en ajoutant "bounds" à la variable LOCAL_SANITIZE pour les modules binaires et de bibliothèque :
LOCAL_SANITIZE := bounds # Optional features LOCAL_SANITIZE_DIAG := bounds LOCAL_SANITIZE_BLOCKLIST := modulename_BLOCKLIST.txt
LOCAL_SANITIZE accepte une liste de composants de désinfection séparés par une virgule.
LOCAL_SANITIZE_DIAG active le mode diagnostic. N'utilisez le mode diagnostic que pendant les tests. Le mode Diagnostics n'abandonne pas en cas de dépassement de capacité, 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 la désinfection des fonctions et des fichiers sources. N'utilisez cette propriété que 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 aux tableaux sont sécurisés. Pour en savoir plus, consultez la section Dépannage.
Désactiver 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 laisser BoundSan activé. Ne le désactivez que si la fonction ou le fichier génère une surcharge de performances importante et que la source a été examinée manuellement.
Pour en savoir plus sur la désactivation de BoundSan avec les attributs de fonction et le format du fichier BLOCKLIST, consultez la documentation de Clang LLVM. Limitez la mise sur liste de blocage au nettoyeur spécifique en utilisant des noms de section qui spécifient le nettoyeur cible pour éviter d'impacter les autres nettoyeurs.
Validation
Il n'existe pas de test CTS spécifique à BoundSan. Assurez-vous plutôt que les tests CTS réussissent avec ou sans BoundSan activé pour vérifier que cela 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 précédemment non détectés sont traités.
Les erreurs BoundSan sont facilement identifiables, car elles incluent le message d'abandon suivant :
pid: ###, tid: ###, name: Binder:### >>> /system/bin/foobar <<< signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr -------- Abort message: 'ubsan: out-of-bounds'
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. Examinez logcat pour détecter d'éventuelles erreurs.
external/foo/bar.c:293:13: runtime error: index -1 out of bounds for type 'int [24]'