Scudo

Scudo est un allocateur de mémoire en mode utilisateur dynamique, ou allocateur de tas, conçu pour résister aux failles liées au tas (telles que le dépassement de tampon basé sur le tas, l'utilisation après libération et la double libération) tout en maintenant les performances. Il fournit les primitives d'allocation et de désallocation C standards (telles que malloc et free), ainsi que les primitives C++ (telles que new et delete).

Scudo est davantage un outil d'atténuation qu'un détecteur d'erreurs de mémoire à part entière comme AddressSanitizer (ASan).

Depuis la sortie d'Android 11, scudo est utilisé pour tout le code natif (sauf sur les appareils à faible mémoire, où jemalloc est toujours utilisé). Lors de l'exécution, toutes les allocations et désallocations de tas natif sont traitées par Scudo pour tous les exécutables et leurs dépendances de bibliothèque. Le processus est abandonné si une corruption ou un comportement suspect est détecté dans le tas.

Scudo est Open Source et fait partie du projet compiler-rt de LLVM. La documentation est disponible à l'adresse https://llvm.org/docs/ScudoHardenedAllocator.html. Le runtime Scudo est fourni avec la chaîne d'outils Android. La prise en charge a été ajoutée à Soong et Make pour permettre l'activation facile de l'allocateur dans un binaire.

Vous pouvez activer ou désactiver les mesures d'atténuation supplémentaires dans l'allocateur à l'aide des options décrites ci-dessous.

Personnalisation

Certains paramètres de l'allocateur peuvent être définis par processus de plusieurs manières :

  • De manière statique : définissez une fonction __scudo_default_options dans le programme qui renvoie la chaîne d'options à analyser. Cette fonction doit avoir le prototype suivant : extern "C" const char *__scudo_default_options().
  • De manière dynamique : utilisez la variable d'environnement SCUDO_OPTIONS contenant la chaîne d'options à analyser. Les options définies de cette manière remplacent toute définition effectuée via __scudo_default_options.

Les options suivantes sont disponibles.

Option 64 bits par défaut 32 bits par défaut Description
QuarantineSizeKb 256 64 Taille (en Ko) de la quarantaine utilisée pour retarder la désallocation réelle des blocs. Une valeur inférieure peut réduire l'utilisation de la mémoire, mais diminuer l'efficacité de l'atténuation. Une valeur négative revient aux valeurs par défaut. Si vous définissez cette valeur et ThreadLocalQuarantineSizeKb sur zéro, la mise en quarantaine sera entièrement désactivée.
QuarantineChunksUpToSize 2048 512 Taille (en octets) maximale des blocs pouvant être mis en quarantaine.
ThreadLocalQuarantineSizeKb 64 16 Taille (en Ko) de l'utilisation du cache par thread pour décharger la mise en quarantaine globale. Une valeur inférieure peut réduire l'utilisation de la mémoire, mais peut augmenter la contention sur la quarantaine globale. Si vous définissez cette valeur et QuarantineSizeKb sur zéro, la mise en quarantaine est entièrement désactivée.
DeallocationTypeMismatch false false Active le signalement des erreurs sur malloc/delete, new/free, new/delete[]
DeleteSizeMismatch true true Active le signalement des erreurs en cas d'incohérence entre la taille des nouveaux éléments et celle des éléments supprimés.
ZeroContents false false Permet de vider le contenu des blocs lors de l'allocation et de la désallocation.
allocator_may_return_null false false Indique que l'allocateur peut renvoyer la valeur Null lorsqu'une erreur récupérable se produit, au lieu de mettre fin au processus.
hard_rss_limit_mb 0 0 Lorsque la RSS du processus atteint cette limite, le processus se termine.
soft_rss_limit_mb 0 0 Lorsque la RSS du processus atteint cette limite, les allocations supplémentaires échouent ou renvoient null (selon la valeur de allocator_may_return_null), jusqu'à ce que la RSS redescende pour permettre de nouvelles allocations.
allocator_release_to_os_interval_ms 5000 N/A Cela n'affecte qu'un allocateur 64 bits. Si cette option est définie, elle tente de libérer la mémoire inutilisée pour l'OS, mais pas plus souvent que cet intervalle (en millisecondes). Si la valeur est négative, la mémoire n'est pas libérée pour l'OS.
abort_on_error true true Si cette option est définie, l'outil appelle abort() au lieu de _exit() après avoir imprimé le message d'erreur.

Validation

Pour le moment, il n'existe aucun test CTS spécifique à Scudo. Assurez-vous plutôt que les tests CTS réussissent avec ou sans Scudo activé pour un binaire donné afin de vérifier qu'il n'a pas d'impact sur l'appareil.

Dépannage

Si un problème non récupérable est détecté, l'allocateur affiche un message d'erreur dans le descripteur d'erreur standard, puis met fin au processus. Les traces de pile qui entraînent l'arrêt sont ajoutées au journal système. Le résultat commence généralement par Scudo ERROR:, suivi d'un bref résumé du problème et de conseils.

Voici une liste des messages d'erreur actuels et de leurs causes potentielles :

  • corrupted chunk header : la vérification de la somme de contrôle de l'en-tête du bloc a échoué. Cela est probablement dû à l'une des deux raisons suivantes : l'en-tête a été écrasé (partiellement ou totalement) ou le pointeur transmis à la fonction n'est pas un bloc.
  • race on chunk header : Deux threads différents tentent de manipuler le même en-tête en même temps. Cela est généralement symptomatique d'une condition de course ou d'un manque général de verrouillage lors de l'exécution d'opérations sur ce bloc.
  • invalid chunk state : le bloc n'est pas dans l'état attendu pour une opération donnée (par exemple, il n'est pas alloué lorsqu'on essaie de le libérer ou il n'est pas mis en quarantaine lorsqu'on essaie de le recycler). Une double libération est la raison typique de cette erreur.
  • misaligned pointer : les exigences d'alignement de base sont fortement appliquées : 8 octets sur les plates-formes 32 bits et 16 octets sur les plates-formes 64 bits. Si un pointeur transmis à nos fonctions ne correspond pas à ces critères, le pointeur transmis à l'une des fonctions est désaligné.
  • allocation type mismatch : lorsque cette option est activée, une fonction de désallocation appelée sur un bloc doit correspondre au type de fonction qui a été appelée pour l'allouer. Ce type d'incohérence peut entraîner des problèmes de sécurité.
  • invalid sized delete : lorsque l'opérateur de suppression dimensionné C++14 est utilisé et que la vérification facultative est activée, il existe une incohérence entre la taille transmise lors de la désallocation d'un bloc et la taille demandée lors de son allocation. Il s'agit généralement d'un problème de compilation ou d'une confusion de type sur l'objet en cours de désallocation.
  • RSS limit exhausted : La RSS maximale éventuellement spécifiée a été dépassée.

Si vous déboguez un plantage dans l'OS lui-même, vous pouvez utiliser une version OS HWASan. Si vous déboguez un plantage dans une application, vous pouvez également utiliser un build d'application HWASan.