Scudo

Scudo est un répartiteur de mémoire dynamique en mode utilisateur, ou répartiteur de tas , conçu pour résister aux vulnérabilités liées au tas (telles que le dépassement de mémoire tampon basé sur le tas, l'utilisation après la 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 plus une atténuation qu'un détecteur d'erreur 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é). Au moment de l'exécution, toutes les allocations et désallocations de tas natives sont gérées par Scudo pour tous les exécutables et leurs dépendances de bibliothèque, et 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 . Le runtime Scudo est livré dans le cadre de la chaîne d'outils Android et la prise en charge a été ajoutée à Soong et Make pour permettre une activation facile de l'allocateur dans un binaire.

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

Personnalisation

Certains paramètres de l'allocateur peuvent être définis processus 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() .
  • 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 faite via __scudo_default_options .

Les options suivantes sont disponibles.

Option 64 bits par défaut 32 bits par défaut La description
QuarantineSizeKb 256 64 La 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. Définir à la fois ceci et ThreadLocalQuarantineSizeKb sur zéro désactive entièrement la quarantaine.
QuarantineChunksUpToSize 2048 512 La taille (en octets) jusqu'à laquelle les blocs peuvent être mis en quarantaine.
ThreadLocalQuarantineSizeKb 64 16 La 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. Définir à la fois ceci et QuarantineSizeKb sur zéro désactive entièrement la quarantaine.
DeallocationTypeMismatch false false Active le rapport d'erreur sur malloc/delete, new/free, new/delete[]
DeleteSizeMismatch true true Active le rapport d'erreur sur la non-concordance entre les tailles de nouveau et de suppression.
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'allocateur peut renvoyer null lorsqu'une erreur récupérable se produit, au lieu de mettre fin au processus.
hard_rss_limit_mb 0 0 Lorsque le flux RSS du processus atteint cette limite, le processus se termine.
soft_rss_limit_mb 0 0 Lorsque le RSS du processus atteint cette limite, d'autres allocations échouent ou renvoient null (selon la valeur de allocator_may_return_null ), jusqu'à ce que le RSS redescende pour permettre de nouvelles allocations.
allocator_release_to_os_interval_ms N / A 5000 N'affecte qu'un alternateur 64 bits. S'il est défini, essaie de libérer de la mémoire inutilisée vers 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 pour le système d'exploitation.
abort_on_error true true S'il est défini, l'outil appelle abort() au lieu de _exit() après avoir affiché le message d'erreur.

Validation

Actuellement, il n'y a pas de tests CTS spécifiquement pour Scudo. Au lieu de cela, assurez-vous que les tests CTS réussissent avec ou sans Scudo activé pour un binaire donné afin de vérifier qu'il n'affecte pas 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 conduisent à l'arrêt sont ajoutées dans le journal système. La sortie commence généralement par Scudo ERROR: suivi d'un bref résumé du problème avec des pointeurs.

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 de bloc a échoué. Cela est probablement dû à l'une des deux choses suivantes : l'en-tête a été écrasé (partiellement ou totalement) ou le pointeur passé à 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. Ceci est généralement symptomatique d'une condition de concurrence ou d'un manque général de verrouillage lors de l'exécution d'opérations sur ce morceau.
  • 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é lors de la tentative de libération, ou il n'est pas mis en quarantaine lors de la tentative de recyclage. Un double free 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 passé à nos fonctions ne correspond pas à celles-ci, le pointeur passé à 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 morceau doit correspondre au type de fonction qui a été appelée pour l'allouer. Ce type de non-concordance peut introduire des problèmes de sécurité.
  • invalid sized delete : lorsque l'opérateur de suppression de taille 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 compilateur ou d'une confusion de type sur l'objet désalloué.
  • RSS limit exhausted : Le RSS maximal éventuellement spécifié a été dépassé.

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

,

Scudo est un répartiteur de mémoire dynamique en mode utilisateur, ou répartiteur de tas , conçu pour résister aux vulnérabilités liées au tas (telles que le dépassement de mémoire tampon basé sur le tas, l'utilisation après la 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 plus une atténuation qu'un détecteur d'erreur 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é). Au moment de l'exécution, toutes les allocations et désallocations de tas natives sont gérées par Scudo pour tous les exécutables et leurs dépendances de bibliothèque, et 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 . Le runtime Scudo est livré dans le cadre de la chaîne d'outils Android et la prise en charge a été ajoutée à Soong et Make pour permettre une activation facile de l'allocateur dans un binaire.

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

Personnalisation

Certains paramètres de l'allocateur peuvent être définis processus 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() .
  • 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 faite via __scudo_default_options .

Les options suivantes sont disponibles.

Option 64 bits par défaut 32 bits par défaut La description
QuarantineSizeKb 256 64 La 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. Définir à la fois ceci et ThreadLocalQuarantineSizeKb sur zéro désactive entièrement la quarantaine.
QuarantineChunksUpToSize 2048 512 La taille (en octets) jusqu'à laquelle les blocs peuvent être mis en quarantaine.
ThreadLocalQuarantineSizeKb 64 16 La 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. Définir à la fois ceci et QuarantineSizeKb sur zéro désactive entièrement la quarantaine.
DeallocationTypeMismatch false false Active le rapport d'erreur sur malloc/delete, new/free, new/delete[]
DeleteSizeMismatch true true Active le rapport d'erreur sur la non-concordance entre les tailles de nouveau et de suppression.
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'allocateur peut renvoyer null lorsqu'une erreur récupérable se produit, au lieu de mettre fin au processus.
hard_rss_limit_mb 0 0 Lorsque le flux RSS du processus atteint cette limite, le processus se termine.
soft_rss_limit_mb 0 0 Lorsque le RSS du processus atteint cette limite, d'autres allocations échouent ou renvoient null (selon la valeur de allocator_may_return_null ), jusqu'à ce que le RSS redescende pour permettre de nouvelles allocations.
allocator_release_to_os_interval_ms N / A 5000 N'affecte qu'un alternateur 64 bits. S'il est défini, essaie de libérer de la mémoire inutilisée vers 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 pour le système d'exploitation.
abort_on_error true true S'il est défini, l'outil appelle abort() au lieu de _exit() après avoir affiché le message d'erreur.

Validation

Actuellement, il n'y a pas de tests CTS spécifiquement pour Scudo. Au lieu de cela, assurez-vous que les tests CTS réussissent avec ou sans Scudo activé pour un binaire donné afin de vérifier qu'il n'affecte pas 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 conduisent à l'arrêt sont ajoutées dans le journal système. La sortie commence généralement par Scudo ERROR: suivi d'un bref résumé du problème avec des pointeurs.

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 de bloc a échoué. Cela est probablement dû à l'une des deux choses suivantes : l'en-tête a été écrasé (partiellement ou totalement) ou le pointeur passé à 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. Ceci est généralement symptomatique d'une condition de concurrence ou d'un manque général de verrouillage lors de l'exécution d'opérations sur ce morceau.
  • 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é lors de la tentative de libération, ou il n'est pas mis en quarantaine lors de la tentative de recyclage. Un double free 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 passé à nos fonctions ne correspond pas à celles-ci, le pointeur passé à 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 morceau doit correspondre au type de fonction qui a été appelée pour l'allouer. Ce type de non-concordance peut introduire des problèmes de sécurité.
  • invalid sized delete : lorsque l'opérateur de suppression de taille 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 compilateur ou d'une confusion de type sur l'objet désalloué.
  • RSS limit exhausted : Le RSS maximal éventuellement spécifié a été dépassé.

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