Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Dans cette procédure pas à pas, créez des builds vérifiées qui recherchent et signalent des erreurs de sécurité de la mémoire.
Les erreurs de sécurité de la mémoire telles que les lectures et les écritures de mémoire hors limites, l’utilisation de la mémoire après sa libération, NULL les déréférencements de pointeur, et ainsi de suite, constituent une préoccupation majeure pour le code C/C++. AddressSanitizer (ASAN) est une technologie de compilateur et d’exécution qui expose ces types de bogues difficiles à trouver, et le fait avec zéro faux positifs. Pour obtenir une vue d’ensemble d’ASAN, consultez AddressSanitizer.
Continue On Error (COE) est une nouvelle fonctionnalité ASAN qui diagnostique et signale automatiquement les erreurs de sécurité de la mémoire à mesure que votre application s’exécute. Lorsque votre programme se termine, un résumé des erreurs de sécurité de mémoire uniques est généré stdoutvers , stderrou dans un fichier journal de votre choix. Lorsque vous créez une build cochée C++ standard avec -fsanitizer=address, les appels aux allocators, les deallocators tels que free, memcpy, memsetetc. sont transférés au runtime ASAN. Le runtime ASAN fournit la même sémantique pour ces fonctions, mais surveille ce qui se passe avec la mémoire. ASAN diagnostique et signale les erreurs de sécurité de mémoire masquées, avec zéro faux positifs, à mesure que votre application s’exécute.
L’un des avantages importants du centre d’excellence est que, contrairement au comportement ASAN précédent, votre programme n’arrête pas de s’exécuter lorsque la première erreur de mémoire est trouvée. Au lieu de cela, ASAN note l’erreur et votre application continue à s’exécuter. Une fois votre application terminée, un résumé de tous les problèmes de mémoire est généré.
Il est recommandé de créer une build vérifiée de votre application C ou C++ avec ASAN activé, puis d’exécuter votre application dans votre harnais de test. À mesure que vos tests effectuent l’exercice des chemins de code dans votre application à la recherche de bogues, vous découvrirez également si ces chemins de code présentent des problèmes de sécurité de mémoire sans interférer avec les tests.
Une fois votre application terminée, vous obtenez un résumé des problèmes de mémoire. Avec COE, vous pouvez compiler et déployer une application existante en production limitée pour trouver des problèmes de sécurité de la mémoire. Vous pouvez exécuter la build vérifiée pendant des jours pour exercer entièrement le code, bien que l’application s’exécute plus lentement en raison de l’instrumentation ASAN.
Vous pouvez utiliser cette fonctionnalité pour créer une porte d’expédition. Si tous vos tests existants réussissent, mais que coE signale une erreur de sécurité de la mémoire ou une fuite, n’envoyez pas le nouveau code ou intégrez-le dans une branche parente.
Ne déployez pas de build avec COE activé en production ! Le CENTRE d’excellence est destiné à être utilisé uniquement dans les environnements de test et de développement. Vous ne devez pas utiliser une build ASAN activée en production en raison de l’impact sur les performances de l’instrumentation ajoutée pour détecter les erreurs de mémoire, le risque d’exposer l’implémentation interne si des erreurs sont signalées et d’éviter d’augmenter la surface de surface des attaques de sécurité possibles en expédiant les fonctions de bibliothèque que l’ASAN remplace par l’allocation de mémoire, la libération, et ainsi de suite.
Dans les exemples suivants, vous créez des builds vérifiées et définissez une variable d’environnement pour générer les informations d’assainissement de l’adresse pour stdout voir les erreurs de sécurité de la mémoire que l’ASAN signale.
Prérequis
Pour effectuer cette procédure pas à pas, vous avez besoin de Visual Studio 2022 17.6 ou version ultérieure avec le développement Desktop avec la charge de travail C++ installée.
Exemple double gratuit
Dans cet exemple, vous créez une build avec ASAN activée pour tester ce qui se passe quand la mémoire est double libérée. ASAN détecte cette erreur et le signale. Dans cet exemple, le programme continue à s’exécuter une fois l’erreur détectée, ce qui entraîne une deuxième erreur à l’aide de la mémoire libérée. Un résumé des erreurs est généré stdout au moment de la sortie du programme.
Créez l’exemple :
Ouvrez une invite de commandes développeur : ouvrez le menu Démarrer , tapez Développeur et sélectionnez l’invite de commandes la plus récente, telle que l’invite de commandes développeur pour VS 2022 dans la liste des correspondances.
Créez un répertoire sur votre ordinateur pour exécuter cet exemple. Par exemple :
%USERPROFILE%\Desktop\COE.Dans ce répertoire, créez un fichier source vide. Par exemple,
doublefree.cppCollez le code suivant dans le fichier :
#include <stdio.h> #include <stdlib.h> void BadFunction(int *pointer) { free(pointer); free(pointer); // double-free! } int main(int argc, const char *argv[]) { int *pointer = static_cast<int *>(malloc(4)); BadFunction(pointer); // Normally we'd crash before this, but with COE we can see heap-use-after-free error as well printf("\n\n******* Pointer value: %d\n", *pointer); return 1; }
Dans le code précédent, pointer est libéré deux fois. Il s’agit d’un exemple contrif, mais les doubles libres sont une erreur facile à faire dans du code C++ plus complexe.
Créez une build du code précédent avec COE activé avec les étapes suivantes :
- Compilez le code dans l’invite de commandes développeur que vous avez ouverte précédemment :
cl -fsanitize=address -Zi doublefree.cpp. Le-fsanitize=addresscommutateur active ASAN et-Zicrée un fichier PDB distinct que AddressSanitizer utilise pour afficher les informations d’emplacement d’erreur de mémoire. - Envoyez la sortie ASAN en
stdoutdéfinissant laASAN_OPTIONSvariable d’environnement dans l’invite de commandes développeur comme suit :set ASAN_OPTIONS=continue_on_error=1 - Exécutez le code de test avec :
doublefree.exe
La sortie indique qu’il y a eu une erreur double libre et la pile des appels où elle s’est produite. Le rapport commence par une pile d’appels qui affiche l’erreur dans BadFunction:
==22976==ERROR: AddressSanitizer: attempting double-free on 0x01e03550 in thread T0:
#0 free D:\a\_work\1\s\src\vctools\asan\llvm\compiler-rt\lib\asan\asan_malloc_win_thunk.cpp(69)
#1 BadFunction C:\Users\xxx\Desktop\COE\doublefree.cpp(8)
#2 main C:\Users\xxx\Desktop\COE\doublefree.cpp(14)
#3 __scrt_common_main_seh D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl(288)
#4 BaseThreadInitThunk Windows
#5 RtlInitializeExceptionChain Windows
Ensuite, il existe des informations sur la mémoire libérée et une pile d’appels pour laquelle la mémoire a été allouée :
0x01e03550 is located 0 bytes inside of 4-byte region [0x01e03550,0x01e03554)
freed by thread T0 here:
#0 free D:\a\_work\1\s\src\vctools\asan\llvm\compiler-rt\lib\asan\asan_malloc_win_thunk.cpp(69)
#1 BadFunction C:\Users\xxx\Desktop\COE\doublefree.cpp(7)
#2 main C:\Users\xxx\Desktop\COE\doublefree.cpp(14)
#3 __scrt_common_main_seh D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl(288)
#4 BaseThreadInitThunk Windows
#5 RtlInitializeExceptionChain Windows
previously allocated by thread T0 here:
#0 malloc D:\a\_work\1\s\src\vctools\asan\llvm\compiler-rt\lib\asan\asan_malloc_win_thunk.cpp(85)
#1 main C:\Users\xxx\Desktop\COE\doublefree.cpp(13)
#2 __scrt_common_main_seh D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl(288)
#3 BaseThreadInitThunk Windows
#4 RtlInitializeExceptionChain Windows
Ensuite, il existe des informations sur l’erreur tas-use-after-free. Cela fait référence à l’utilisation *pointer dans l’appel printf() , car la mémoire pointer fait référence a été libérée précédemment. La pile des appels où l’erreur se produit est répertoriée, comme les piles d’appels où cette mémoire a été allouée et libérée :
==35680==ERROR: AddressSanitizer: heap-use-after-free on address 0x02a03550 at pc 0x00e91097 bp 0x012ffc64 sp 0x012ffc58READ of size 4 at 0x02a03550 thread T0
#0 main C:\Users\xxx\Desktop\Projects\ASAN\doublefree.cpp(18)
#1 __scrt_common_main_seh D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl(288)
#2 BaseThreadInitThunk Windows
#3 RtlInitializeExceptionChain Windows
0x02a03550 is located 0 bytes inside of 4-byte region [0x02a03550,0x02a03554)
freed by thread T0 here:
#0 free D:\a\_work\1\s\src\vctools\asan\llvm\compiler-rt\lib\asan\asan_malloc_win_thunk.cpp(69)
#1 BadFunction C:\Users\xxx\Desktop\Projects\ASAN\doublefree.cpp(7)
#2 main C:\Users\xxx\Desktop\Projects\ASAN\doublefree.cpp(14)
#3 __scrt_common_main_seh D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl(288)
#4 BaseThreadInitThunk Windows
#5 RtlInitializeExceptionChain Windows
previously allocated by thread T0 here:
#0 malloc D:\a\_work\1\s\src\vctools\asan\llvm\compiler-rt\lib\asan\asan_malloc_win_thunk.cpp(85)
#1 main C:\Users\xxx\Desktop\Projects\ASAN\doublefree.cpp(13)
#2 __scrt_common_main_seh D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl(288)
#3 BaseThreadInitThunk Windows
#4 RtlInitializeExceptionChain Windows
Ensuite, il existe des informations sur les octets d’ombre à proximité du dépassement de mémoire tampon. Pour plus d’informations sur les octets d’ombre, consultez Les octets d’ombre AddressSanitizer.
Après les informations d’octets instantanés, vous verrez la sortie du programme, qui indique qu’elle a continué à s’exécuter après que l’ASAN a détecté l’erreur :
******* Pointer value: xxx
Il existe ensuite un résumé des fichiers sources où l’erreur de mémoire s’est produite. Il est trié par les piles d’appels uniques pour les erreurs de mémoire dans ce fichier. Une pile d’appels unique est déterminée par le type d’erreur et la pile des appels où l’erreur s’est produite.
Ce tri hiérarchise les problèmes de sécurité de la mémoire qui peuvent être les plus importants. Par exemple, cinq piles d’appels uniques menant à différentes erreurs de sécurité de mémoire dans le même fichier sont potentiellement plus préoccupantes qu’une erreur qui atteint plusieurs fois. Le résumé ressemble à ceci :
=== Files in priority order ===
File: D:\a\_work\1\s\src\vctools\asan\llvm\compiler-rt\lib\asan\asan_malloc_win_thunk.cpp Unique call stacks: 1
File: C:\Users\xxx\Desktop\COE\doublefree.cpp Unique call stacks: 1
Enfin, le rapport contient un résumé de l’endroit où les erreurs de mémoire se sont produites :
=== Source Code Details: Unique errors caught at instruction offset from source line number, in functions, in the same file. ===
File: D:\a\_work\1\s\src\vctools\asan\llvm\compiler-rt\lib\asan\asan_malloc_win_thunk.cpp
Func: free()
Line: 69 Unique call stacks (paths) leading to error at line 69 : 1
Bug: double-free at instr 19 bytes from start of line
File: C:\Users\xxx\Desktop\COE\doublefree.cpp
Func: main()
Line: 18 Unique call stacks (paths) leading to error at line 18 : 1
Bug: heap-use-after-free at instr 55 bytes from start of line
>>>Total: 2 Unique Memory Safety Issues (based on call stacks not source position) <<<
#0 C:\Users\xxx\Desktop\COE\doublefree.cpp Function: main(Line:18)
Raw HitCnt: 1 On Reference: 4-byte-read-heap-use-after-free
#1 D:\a\_work\1\s\src\vctools\asan\llvm\compiler-rt\lib\asan\asan_malloc_win_thunk.cpp Function: free(Line:69)
Raw HitCnt: 1
Exemple d’accès à la mémoire hors limites
Dans cet exemple, vous créez une build avec ASAN activée pour tester ce qui se passe lorsqu’une application accède à la mémoire hors limites. ASAN détecte cette erreur et signale un résumé des erreurs stdout au moment de la fermeture du programme.
Créez l’exemple :
Ouvrez une invite de commandes développeur : ouvrez le menu Démarrer , tapez Développeur et sélectionnez l’invite de commandes la plus récente, telle que l’invite de commandes développeur pour VS 2022 dans la liste des correspondances.
Créez un répertoire sur votre ordinateur pour exécuter cet exemple. Par exemple :
%USERPROFILE%\Desktop\COE.Dans ce répertoire, créez un fichier source, par exemple,
coe.cppet collez le code suivant :#include <stdlib.h> char* func(char* buf, size_t sz) { char* local = (char*)malloc(sz); for (auto ii = 0; ii <= sz; ii++) // bad loop exit test { local[ii] = ~buf[ii]; // Two memory safety errors } return local; } char buffer[10] = {0,1,2,3,4,5,6,7,8,9}; int main() { char* inverted_buf= func(buffer, 10); }
Dans le code précédent, le paramètre sz est 10 et la mémoire tampon d’origine est de 10 octets. Il existe deux erreurs de sécurité de la mémoire :
- une charge hors limites à partir de
buflaforboucle - un magasin hors limites dans
locallaforboucle
Le dépassement de mémoire tampon est dû au test <=szde sortie de la boucle. Lorsque cet exemple s’exécute, il est sécurisé par coïncidence. Cela est dû à l’allocation et à l’alignement effectués par la plupart des implémentations du runtime C++. Quand sz % 16 == 0, l’écriture finale pour local[ii] endommager la mémoire. D’autres cas sont uniquement en lecture/écriture dans le « malloc slop », qui est une mémoire supplémentaire allouée en raison de la façon dont les pavés C Runtime (CRT) sont alloués à une limite de 0 mod 16.
Les erreurs ne sont observables que si la page qui suit l’allocation n’est pas mappée ou lors de l’utilisation de données endommagées. Tous les autres cas sont silencieux dans cet exemple. Avec l’erreur Continue On, les erreurs sont rendues visibles dans le résumé après l’exécution du programme.
Créez une build du code précédent avec coE activé :
- Compilez le code avec
cl -fsanitize=address -Zi coe.cpp. Le-fsanitize=addresscommutateur active ASAN et-Zicrée un fichier PDB distinct que AddressSanitizer utilise pour afficher les informations d’emplacement d’erreur de mémoire. - Envoyez la sortie ASAN en
stdoutdéfinissant laASAN_OPTIONSvariable d’environnement dans l’invite de commandes développeur comme suit :set ASAN_OPTIONS=continue_on_error=1 - Exécutez le code de test avec :
coe.exe
La sortie indique qu’il y avait deux erreurs de dépassement de mémoire tampon et fournit la pile des appels pour l’endroit où elles se sont produites. Le rapport commence comme suit :
==9776==ERROR: AddressSanitizer: global-buffer-overflow on address 0x0047b08a at pc 0x003c121b bp 0x012ffaec sp 0x012ffae0
READ of size 1 at 0x0047b08a thread T0
#0 func C:\Users\xxx\Desktop\COE\coe.cpp(8)
#1 main C:\Users\xxx\Desktop\COE\coe.cpp(18)
#2 __scrt_common_main_seh D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl(288)
#3 BaseThreadInitThunk Windows
#4 RtlInitializeExceptionChain Windows
Ensuite, il existe des informations sur les octets d’ombre à proximité du dépassement de mémoire tampon. Pour plus d’informations sur les octets d’ombre, consultez Les octets d’ombre AddressSanitizer.
Après le rapport d’octets instantanés, il existe un résumé des fichiers sources où les erreurs de mémoire se sont produites. Il est trié par les piles d’appels uniques pour les erreurs de mémoire dans ce fichier. Une pile d’appels unique est déterminée par le type d’erreur et la pile des appels où l’erreur s’est produite.
Ce tri hiérarchise les problèmes de sécurité de la mémoire qui peuvent être les plus importants. Par exemple, cinq piles d’appels uniques menant à différentes erreurs de sécurité de mémoire dans le même fichier sont potentiellement plus préoccupantes qu’une erreur qui atteint plusieurs fois.
Le résumé ressemble à ceci :
=== Files in priority order ===
File: C:\Users\xxx\Desktop\COE\coe.cpp Unique call stacks: 2
Enfin, le rapport contient un résumé de l’endroit où les erreurs de mémoire se sont produites. Continue On Error signale deux erreurs distinctes qui se produisent sur la même ligne source. La première erreur lit la mémoire à une adresse globale dans la .data section, et les autres écritures dans la mémoire allouées à partir du tas.
Le rapport ressemble à ceci :
=== Source Code Details: Unique errors caught at instruction offset from source line number, in functions, in the same file. ===
File: C:\Users\xxx\Desktop\COE\coe.cpp
Func: func()
Line: 8 Unique call stacks (paths) leading to error at line 8 : 2
Bug: heap-buffer-overflow at instr 124 bytes from start of line
>>>Total: 2 Unique Memory Safety Issues (based on call stacks not source position) <<<
#0 C:\Users\xxx\Desktop\COE\coe.cpp Function: func(Line:8)
Raw HitCnt: 1 On Reference: 1-byte-read-global-buffer-overflow
#1 C:\Users\xxx\Desktop\COE\coe.cpp Function: func(Line:8)
Raw HitCnt: 1 On Reference: 1-byte-write-heap-buffer-overflow
Le comportement d’exécution AddressSanitizer par défaut met fin à l’application après avoir signalé la première erreur qu’elle trouve. Il n’autorise pas l’exécution de l’instruction de machine « incorrecte ». Le nouveau runtime AddressSanitizer diagnostique et signale les erreurs, mais exécute ensuite les instructions suivantes.
COE tente de retourner automatiquement le contrôle à l’application après avoir signalé chaque erreur de sécurité de la mémoire. Il existe des situations où il ne peut pas, par exemple s’il existe une violation d’accès à la mémoire (AV) ou une allocation de mémoire ayant échoué. Le centre d’excellence ne continue pas après les violations d’accès que la gestion structurée des exceptions du programme ne intercepte pas. Si le centre d’excellence ne peut pas retourner l’exécution à l’application, un CONTINUE CANCELLED - Deadly Signal. Shutting down. message est généré.
Sélectionnez où envoyer la sortie ASAN
Utilisez la variable d’environnement ASAN_OPTIONS pour déterminer où envoyer la sortie ASAN comme suit :
- Sortie vers stdout :
set ASAN_OPTIONS=continue_on_error=1 - Sortie vers stderr :
set ASAN_OPTIONS=continue_on_error=2 - Sortie dans un fichier journal de votre choix :
set COE_LOG_FILE=yourfile.log
Gestion du comportement non défini
Le runtime ASAN n’imite pas tous les comportements non définis des fonctions d’allocation/de désallocation C et C++. L’exemple suivant montre comment la version ASAN de _alloca diffère de la version du runtime C :
#include <cstdio>
#include <cstring>
#include <malloc.h>
#include <excpt.h>
#include <windows.h>
#define RET_FINISH 0
#define RET_STACK_EXCEPTION 1
#define RET_OTHER_EXCEPTION 2
int foo_redundant(unsigned long arg_var)
{
char *a;
int ret = -1;
__try
{
if ((arg_var+3) > arg_var)
{
// Call to _alloca using parameter from main
a = (char *) _alloca(arg_var);
memset(a, 0, 10);
}
ret = RET_FINISH;
}
__except(1)
{
ret = RET_OTHER_EXCEPTION;
int i = GetExceptionCode();
if (i == EXCEPTION_STACK_OVERFLOW)
{
ret = RET_STACK_EXCEPTION;
}
}
return ret;
}
int main()
{
int cnt = 0;
if (foo_redundant(0xfffffff0) == RET_STACK_EXCEPTION)
{
cnt++;
}
if (cnt == 1)
{
printf("pass\n");
}
else
{
printf("fail\n");
}
}
Dans main() un grand nombre est passé à foo_redundant, qui est finalement passé à _alloca(), ce qui provoque _alloca() l’échec.
Cet exemple génère une pass sortie lorsqu’elle est compilée sans ASAN (autrement dit, aucun -fsanitize=address commutateur), mais les sorties fail lorsqu’elles sont compilées avec ASAN activée (autrement dit, avec le -fsanitize=address commutateur). C’est parce que sans ASAN, le code d’exception correspond RET_STACK_EXCEPTIONcnt à 1. Il se comporte différemment lorsqu’il est compilé avec ASAN, car l’exception levée est une erreur AddressSanitizer à la place : dynamic-stack-buffer-overflow. Cela signifie que le code retourne RET_OTHER_EXCEPTION au lieu de RET_STACK_EXCEPTION cela cnt n’est pas défini sur 1.
Autres avantages
Avec le nouveau runtime ASAN, aucun binaire supplémentaire n’a besoin d’être déployé avec votre application. Cela facilite encore l’utilisation d’ASAN avec votre harnais de test normal, car vous n’avez pas besoin de gérer des fichiers binaires supplémentaires.
Voir aussi
Billet de blog AddressSanitizer Continue on Error
Exemples d’erreurs de sécurité de la mémoire
Indicateur du compilateur -Zi
-fsanitize=indicateur du compilateur d’adresses
Plus de 25 faiblesses logicielles les plus dangereuses