Scudo

Scudo est un outil d'allocation de mémoire dynamique en mode utilisateur, ou outil d'allocation de tas, conçu pour résister aux failles liées au tas (telles que le dépassement de mémoire 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 standard (telles que malloc et free), ainsi que les primitives C++ (telles que new et delete).

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

Depuis la version 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 natifs sont géré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 sur https://llvm.org/docs/ScudoHardenedAllocator.html. L'environnement d'exécution Scudo est fourni dans le cadre de la chaîne d'outils Android. La prise en charge a été ajoutée à Soong et Make pour permettre d'activer facilement l'outil d'allocation dans un binaire.

Vous pouvez activer ou désactiver l'atténuation supplémentaire dans l'outil d'allocation à l'aide des options décrites ci-dessous.

Personnalisation

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

  • Statiquement : 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().
  • Dynamiquement : 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 Valeur par défaut 64 bits Valeur par défaut 32 bits 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 option et ThreadLocalQuarantineSizeKb sur zéro, la quarantaine est entièrement désactivée.
QuarantineChunksUpToSize 2048 512 Taille (en octets) jusqu'à laquelle les blocs peuvent être mis en quarantaine.
ThreadLocalQuarantineSizeKb 64 16 Taille (en Ko) du cache par thread utilisé pour décharger la 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 option et QuarantineSizeKb sur zéro, la quarantaine est entièrement désactivée.
DeallocationTypeMismatch false false Active la création de rapports d'erreurs sur malloc/delete, new/free, new/delete[]
DeleteSizeMismatch true true Active la création de rapports d'erreurs en cas de non-concordance entre les tailles de new et delete.
ZeroContents false false Active le contenu de bloc zéro lors de l'allocation et de la désallocation.
allocator_may_return_null false false Spécifie que l'outil d'allocation peut renvoyer la valeur nulle 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 suivantes échouent ou renvoient null (en fonction de 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 N'affecte qu'un outil d'allocation 64 bits. Si cette option est définie, elle tente de libérer la mémoire inutilisée dans le système d'exploitation, mais pas plus souvent que cet intervalle (en millisecondes). Si la valeur est négative, la mémoire n'est pas libérée dans le système d'exploitation.
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

Actuellement, 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'outil d'allocation affiche un message d'erreur dans le descripteur d'erreur standard, puis met fin au processus. Les traces de pile qui mènent à la fin sont ajoutées au journal système. La sortie commence généralement par Scudo ERROR:, suivie d'un bref résumé du problème et de pointeurs.

Voici la 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 de 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. Il s'agit généralement d'un symptôme de condition de concurrence 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é lorsque vous essayez de le libérer, ou il n'est pas mis en quarantaine lorsque vous essayez 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 exigences, le pointeur transmis à l'une des fonctions n'est pas aligné.
  • 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 de non-concordance peut entraîner des problèmes de sécurité.
  • invalid sized delete: lorsque l'opérateur de suppression dimensionnée C++14 est utilisé et que la vérification facultative est activée, il existe une non-concordance 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 compilateur ou d'une confusion de type sur l'objet en cours de désallocation.
  • RSS limit exhausted: la RSS maximale spécifiée en option a été dépassée.

Si vous déboguez un plantage dans le système d'exploitation lui-même, vous pouvez utiliser une version du système d'exploitation HWASan. Si vous déboguez un plantage dans une application, il est également possible d'utiliser une version d'application HWASan aussi.