UndefinedBehaviorSanitizer (UBSan) effectue une instrumentation au moment de la compilation pour rechercher différents types de comportements non définis. Bien que UBSan soit capable de détecter de nombreux bugs de comportement non définis, Android est compatible avec les éléments suivants:
- alignement
- bool
- limites
- enum
- float-cast-overflow
- float-divide-by-zero
- integer-divide-by-zero
- nonnull-attribute
- null
- retour
- returns-nonnull-attribute
- shift-base
- shift-exponent
- signed-integer-overflow
- injoignable
- unsigned-integer-overflow
- vla-bound
Le débordement d'entier non signé, bien que techniquement non défini, est inclus dans le nettoyeur et utilisé dans de nombreux modules Android, y compris les composants mediaserver, pour éliminer toute vulnérabilité latente de débordement 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 de chaque module, définissez LOCAL_SANITIZE et spécifiez les comportements non dé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)
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 propose également deux raccourcis, integer
et default-ub
, pour activer un ensemble de nettoyeurs en même temps. L'entier 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 du compilateur minimes: bool, integer-divide-by-zero, return,
returns-nonnull-attribute, shift-exponent, unreachable and vla-bound
. La classe de nettoyage des entiers peut être utilisée avec SANITIZE_TARGET et LOCAL_SANITIZE, tandis que default-ub ne peut être utilisée qu'avec SANITIZE_TARGET.
Rapports d'erreur améliorés
L'implémentation par défaut d'UBSan d'Android appelle une fonction spécifiée lorsqu'un comportement non défini est détecté. Par défaut, cette fonction est "abort". Toutefois, à partir d'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 non défini rencontré, les informations sur le fichier et la ligne de code source. Pour activer ce rapport 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 nettoyeur lors de la compilation. LOCAL_SANITIZE_DIAG active le mode diagnostic pour le nettoyeur spécifié. Vous pouvez définir LOCAL_SANITIZE et LOCAL_SANITIZE_DIAG sur des valeurs différentes, mais seules les vérifications de 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, la vérification n'est pas activée et les messages de diagnostic ne sont pas fournis.
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 valeurs d'entiers en cas de dépassement
Les débordements d'entiers involontaires peuvent entraîner une corruption de la mémoire ou des failles de divulgation d'informations dans les variables associées aux accès ou aux allocations de mémoire. Pour y remédier, nous avons ajouté les outils de nettoyage des débordements d'entiers signés et non signés UndefinedBehaviorSanitizer (UBSan) de Clang pour durcir le framework multimédia dans Android 7.0. Sous Android 9, nous avons étendu UBSan pour couvrir davantage de composants et amélioré la prise en charge de ce système de compilation.
Il est conçu pour ajouter des vérifications autour des opérations arithmétiques/instructions (qui peuvent déborder) afin d'arrêter de manière sécurisée un processus en cas de dépassement. Ces outils de nettoyage peuvent atténuer toute une classe de failles de corruption de mémoire et de divulgation d'informations dont la cause est un débordement d'entier, comme la faille Stagefright d'origine.
Exemples et source
La validation de l'overflow d'entier (IntSan) est fournie par le compilateur et ajoute une instrumentation au binaire au moment de la compilation pour détecter les dépassements arithmétiques. Il est activé par défaut dans divers composants de la plate-forme, par exemple /platform/external/libnl/Android.bp
.
Implémentation
IntSan utilise les nettoyeurs d'erreurs d'inondation d'entiers signés et non signés d'UBSan. Cette atténuation est activée au niveau du module. Il permet de sécuriser les composants critiques d'Android et ne doit pas être désactivé.
Nous vous encourageons vivement à activer la validation de l'overflow d'entier 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 approuvées. Un léger surcoût de performances est associé au nettoyeur, qui dépend de l'utilisation du code et de la prévalence 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 make, ajoutez:
LOCAL_SANITIZE := integer_overflow # Optional features LOCAL_SANITIZE_DIAG := integer_overflow LOCAL_SANITIZE_BLOCKLIST := modulename_BLOCKLIST.txt
LOCAL_SANITIZE
prend une liste de nettoyeurs séparés par une virgule,integer_overflow
étant un ensemble d'options préemballées pour les nettoyeurs d'erreurs de débordement d'entiers signés et non signés avec une BLOCKLIST par défaut.LOCAL_SANITIZE_DIAG
active le mode diagnostic pour les nettoyeurs. N'utilisez le mode de diagnostic que pendant les tests, car il n'est pas arrêté en cas de débordement, ce qui annule complètement l'avantage de sécurité de l'atténuation. Pour en savoir plus, consultez la section Dépannage.LOCAL_SANITIZE_BLOCKLIST
vous permet de spécifier un fichier BLOCKLIST pour empêcher la purification 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 nettoyeurs individuellement à l'aide de l'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 de plan
Pour activer la désinfection des débordements d'entiers dans un fichier de modèle, 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éemballées pour les nettoyeurs d'erreurs d'inondation d'entiers signés et non signés individuels avec une liste de blocage par défaut.
L'ensemble de propriétés diag
active le mode de diagnostic pour les nettoyeurs. N'utilisez le mode diagnostic que pendant les tests. Le mode Diagnostic ne s'arrête pas en cas de débordement, 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 purification des fonctions et des fichiers sources. Pour en savoir plus, consultez la section Dépannage.
Pour activer les nettoyeurs 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ébordements d'entiers dans de nouveaux composants ou si vous vous appuyez sur des bibliothèques de plate-forme qui ont été désinfectées, vous pouvez rencontrer quelques problèmes avec des débordements d'entiers bénins qui provoquent des arrêts. Vous devez tester les composants avec la validation activée pour vous assurer que les débordements bénins peuvent être détectés.
Pour rechercher les arrêts dus à la désinfection dans les builds utilisateur, recherchez les plantages SIGABRT
avec des messages d'arrêt indiquant un débordement 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 la pile doit inclure la fonction à l'origine de l'arrêt, mais les débordements qui se produisent dans les fonctions intégrées peuvent ne pas être évidents dans la trace de la pile.
Pour déterminer plus facilement la cause du problème, activez les diagnostics dans la bibliothèque qui déclenche l'abandon, puis essayez de reproduire l'erreur. Lorsque les diagnostics sont activés, le processus ne s'arrête pas et continue de s'exécuter. Ne pas interrompre l'exécution 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'arrêt:
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 bénin et intentionnel (par exemple, qu'il n'a aucune incidence sur la sécurité). Pour résoudre l'interruption du nettoyeur:
- Refactorisation du code pour éviter le débordement (exemple)
- Dépassement 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é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 précise possible. Par exemple, pour une fonction volumineuse comportant de nombreuses opérations arithmétiques et une seule opération de débordement, il est préférable de refactorer cette seule opération plutôt que de placer l'ensemble de la fonction sur la liste de blocage.
Voici quelques exemples de modèles courants pouvant entraîner des débordements bénins:
- Casts implicites où un débordement non signé se produit avant d'être converti en type signé (exemple)
- Suppressions de listes liées qui réduisent l'indice 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 diminuent un entier non signé dans la condition (exemple, exemple)
Il est recommandé aux développeurs de s'assurer que les cas où le nettoyeur détecte un débordement sont effectivement bénins, sans effets secondaires ni implications de sécurité involontaires, avant de désactiver le nettoyage.
Désactiver IntSan
Vous pouvez désactiver IntSan avec des listes de blocage ou des attributs de fonction. Désactivez-le avec parcimonie et uniquement lorsque le refactoring du code est autrement déraisonnable ou s'il existe un surcoût de performances problématique.
Pour en savoir plus sur la désactivation d'IntSan avec les attributs de fonction et la mise en forme de fichier BLOCKLIST, consultez la documentation Clang en amont. La liste de blocage doit être limitée au nettoyeur spécifique en utilisant des noms de sections spécifiant le nettoyeur cible pour éviter d'affecter les autres nettoyeurs.
Validation
Pour le moment, il n'existe aucun test CTS spécifique à la validation des valeurs numériques. Assurez-vous plutôt que les tests CTS réussissent avec ou sans IntSan activé pour vérifier qu'il n'a aucun impact sur l'appareil.
Nettoyage des limites
BoundsSanitizer (BoundSan) ajoute une instrumentation aux binaires pour insérer des vérifications 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 qu'elle puisse être vérifiée. Android 10 déploie BoundSan dans le 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 nettoyeur de limites d'UBSan. Cette atténuation est activée au niveau du module. Il permet de sécuriser les composants critiques d'Android et ne doit pas être désactivé.
Nous vous encourageons vivement à activer BoundSan pour d'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. Le coût supplémentaire en termes de performances associé à l'activation de BoundSan dépend du nombre d'accès aux tableaux qui ne peuvent pas être prouvés comme sécurisés. Attendez-vous à un faible pourcentage de frais généraux 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 de modèle en ajoutant "bounds"
à la propriété de nettoyage misc_undefined
pour les modules binaires et les bibliothèques:
sanitize: { misc_undefined: ["bounds"], diag: { misc_undefined: ["bounds"], }, BLOCKLIST: "modulename_BLOCKLIST.txt",
diag
La propriété diag
active le mode diagnostic pour les nettoyeurs.
N'utilisez le mode diagnostic que pendant les tests. Le mode Diagnostic n'est pas arrêté en cas de débordement, ce qui annule l'avantage de sécurité de l'atténuation et entraîne un coût supplémentaire en termes de performances. Il n'est donc pas recommandé pour les builds 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 purification 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 significative. Effectuez un audit manuel de 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 make
BoundSan peut être activé dans les fichiers de compilation en ajoutant "bounds"
à la variable LOCAL_SANITIZE
pour les modules binaires et les bibliothèques:
LOCAL_SANITIZE := bounds # Optional features LOCAL_SANITIZE_DIAG := bounds LOCAL_SANITIZE_BLOCKLIST := modulename_BLOCKLIST.txt
LOCAL_SANITIZE
accepte une liste de nettoyeurs séparés par une virgule.
LOCAL_SANITIZE_DIAG
active le mode diagnostic. N'utilisez le mode diagnostic que pendant les tests. Le mode de diagnostic n'est pas arrêté en cas de débordement, ce qui annule l'avantage de sécurité de l'atténuation et entraîne un coût supplémentaire en termes de performances. Il n'est donc pas recommandé pour les builds de production.
LOCAL_SANITIZE_BLOCKLIST
permet de spécifier un fichier BLOCKLIST qui permet aux développeurs d'empêcher la purification 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 significative. Effectuez un audit manuel de 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 listes de blocage 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 grande quantité de surcharge de performances 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 formatage de fichier BLOCKLIST, consultez la documentation Clang LLVM. Délimitez la liste de blocage au nettoyeur spécifique à l'aide de noms de sections spécifiant le nettoyeur cible pour éviter d'affecter les autres nettoyeurs.
Validation
Il n'existe aucun test CTS spécifique à BoundSan. Assurez-vous plutôt que les tests CTS réussissent avec ou sans BoundSan activé pour vérifier qu'il n'a aucun 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 précédemment sont résolus.
Les erreurs BoundSan peuvent être facilement identifiées, car elles incluent le message d'abandon de la table d'indexation suivant:
pid: ###, tid: ###, name: Binder:### >>> /system/bin/foobar <<< signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr -------- Abort message: 'ubsan: out-of-bounds'
Lorsque l'exécution se fait 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]'