Maison > Article > développement back-end > Comment effectuer le débogage de la mémoire en php
Ce chapitre est une brève introduction au débogage de la mémoire du code source PHP. Ce cours n'est pas complet : le débogage de la mémoire n'est pas difficile, mais vous aurez besoin d'une certaine expérience pour l'utiliser, et beaucoup de pratique est probablement ce que vous devrez faire lors de la conception de tout code écrit en C. Nous allons présenter ici un débogueur de mémoire très connu : valgrind et comment l'utiliser avec PHP pour déboguer les problèmes de mémoire.
Recommandations d'apprentissage associées : Programmation PHP de l'entrée à la maîtrise
Valgrind est utilisé dans de nombreux environnements Unix Outil bien connu pour déboguer de nombreux problèmes de mémoire courants dans tout logiciel écrit en C/C++. Valgrind est un outil frontal polyvalent pour le débogage de la mémoire. L'outil de bas niveau le plus couramment utilisé est appelé "memcheck". La façon dont cela fonctionne consiste à remplacer l'allocation de tas de chaque libc par sa propre allocation de tas et à suivre ce que vous en faites. Vous pourriez également être intéressé par "massif" : il s'agit d'un outil de suivi de la mémoire utile pour comprendre l'utilisation générale de la mémoire tas d'un programme.
Remarque
Vous devriez lire la documentation Valgrind pour en savoir plus. C'est bien écrit, avec de bons exemples.
Pour effectuer le remplacement de l'allocation de mémoire, vous devez exécuter le programme que vous souhaitez analyser (PHP dans ce cas) via valgrind, c'est-à-dire démarrer le binaire valgrind.
Lorsque valgrind remplace et suit toutes les allocations de tas de la libc, il a tendance à ralentir considérablement le débogueur. Pour PHP, vous le remarquerez. Bien que le ralentissement de PHP ne soit pas aussi drastique, il est quand même clairement perceptible si vous le remarquez, ne vous inquiétez pas, c'est normal ;
Valgrind n'est pas le seul outil que vous pouvez utiliser, mais c'est le plus courant. Il existe d'autres outils comme Dr.Memory, LeakSanitizer, Electric Fence, AddressSanitizer.
Voici les étapes requises pour avoir une bonne expérience du débogage de la mémoire, atténuer les risques de détection de défauts et réduire le temps de débogage :
- Vous devez toujours utiliser Version de débogage de PHP. Essayer de déboguer la mémoire dans les versions de production n'est pas pertinent.
- Vous devez toujours démarrer le débogueur avec USE_ZEND_ALLOC = 0 . Comme vous l'avez peut-être appris dans le chapitre Zend Memory Manager, cette variable d'environnement désactive ZendMM lorsque le processus en cours démarre. Il est fortement recommandé de le faire lors du démarrage du débogueur de mémoire. Cela permet de contourner complètement ZendMM pour comprendre les traces générées par valgrind.
- Il est fortement recommandé de démarrer le débogueur mémoire avec l'environnement ZEND_DONT_UNLOAD_MODULES = 1 . Cela empêche PHP de décharger le fichier .so de l'extension à la fin du processus. Ceci permet un meilleur suivi des rapports valgrind ; si PHP décharge l'extension lorsque valgrind est sur le point d'afficher ses erreurs, elle sera incomplète plus tard car le fichier à partir duquel il a obtenu les informations ne fait plus partie de l'image mémoire du processus.
- Vous aurez peut-être besoin d'une certaine suppression. Lorsque vous dites à PHP de ne pas décharger son extension à la fin du processus, vous risquez de recevoir des faux positifs dans la sortie de valgrind. Les extensions PHP seront vérifiées pour détecter les fuites et si vous obtenez des faux positifs sur votre plate-forme, vous pouvez utiliser la suppression pour les désactiver de cette manière. N'hésitez pas à écrire le vôtre en vous basant sur des exemples comme celui-ci.
- Comparé à Zend Memory Manager, Valgrind est clairement un meilleur outil pour rechercher les fuites et autres problèmes liés à la mémoire. Vous devez toujours exécuter valgrind sur votre code, c'est une étape que pratiquement tous les programmeurs C doivent effectuer. Que ce soit parce qu'il plante et que vous voulez le trouver et le déboguer, ou que vous l'exécutez comme un outil de haute qualité qui semble ne pas pouvoir faire de mal, valgrind est l'outil qui signale les défauts cachés, prêt à les faire exploser. une fois ou plus tard. Utilisez-le même si vous pensez que tout semble bien avec votre code : vous pourriez être surpris.
Attention
Vous devez utiliser valgrind (ou tout autre débogueur de mémoire) sur votre programme. Comme pour tout programme C puissant, il est impossible d’être sûr à 100 % sans déboguer la mémoire. Les erreurs de mémoire peuvent entraîner des problèmes de sécurité nuisibles et les plantages de programmes dépendent souvent de nombreux paramètres, souvent de manière aléatoire.
Valgrind est un débogueur de mémoire tas complet. Il peut également déboguer des cartes mémoire procédurales et des piles de fonctions. Veuillez obtenir plus d’informations dans sa documentation.
Détectons les fuites de mémoire dynamique et essayons une fuite simple et la plus courante :
PHP_RINIT_FUNCTION(pib) { void *foo = emalloc(128); }
Le code ci-dessus perd 128 octets par requête car il n'a aucun efree()
appels liés à un tel tampon. Puisqu'il s'agit d'un appel à emalloc()
, il passe par le Zend Memory Manager, nous en serons donc prévenus plus tard comme nous l'avons vu dans le chapitre ZendMM. Nous voulons également voir si valgrind peut remarquer la fuite :
> ZEND_DONT_UNLOAD_MODULES=1 USE_ZEND_ALLOC=0 valgrind --leak-check=full --suppressions=/path/to/suppression --show-reachable=yes --track-origins=yes ~/myphp/bin/php -dextension=pib.so /tmp/foo.php
Nous utilisons valgrind pour démarrer le processus PHP-CLI. Supposons ici une extension appelée "pib". Voici le résultat :
==28104== 128 bytes in 1 blocks are definitely lost in loss record 1 of 1 ==28104== at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==28104== by 0xA3701E: __zend_malloc (zend_alloc.c:2820) ==28104== by 0xA362E7: _emalloc (zend_alloc.c:2413) ==28104== by 0xE896F99: zm_activate_pib (pib.c:1880) ==28104== by 0xA79F1B: zend_activate_modules (zend_API.c:2537) ==28104== by 0x9D31D3: php_request_startup (main.c:1673) ==28104== by 0xB5909A: do_cli (php_cli.c:964) ==28104== by 0xB5A423: main (php_cli.c:1381) ==28104== LEAK SUMMARY: ==28104== definitely lost: 128 bytes in 1 blocks ==28104== indirectly lost: 0 bytes in 0 blocks ==28104== possibly lost: 0 bytes in 0 blocks ==28104== still reachable: 0 bytes in 0 blocks ==28104== suppressed: 7,883 bytes in 40 blocks
À notre avis, "absolument perdu" est ce sur quoi nous devons nous concentrer.
Remarque
Pour plus de détails sur les différents champs générés par memcheck, veuillez consulter.
Remarque
Nous utilisons
USE_ZEND_ALLOC = 0
pour désactiver et contourner complètement Zend Memory Manager. Chaque appel à son API (par exempleemalloc()
) entraînera directement un appel à la libc, comme nous pouvons le voir sur le cadre de la pile de sortie de Calgrind.
Valgrind a détecté notre vulnérabilité.
Facile, nous pouvons désormais utiliser l'allocation persistante (c'est-à-dire contourner ZendMM et utiliser l'allocation de mémoire dynamique de la libc traditionnelle) pour générer des fuites. Allez :
PHP_RINIT_FUNCTION(pib) { void *foo = malloc(128); }
Voici le rapport :
==28758== 128 bytes in 1 blocks are definitely lost in loss record 1 of 1 ==28758== at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==28758== by 0xE896F82: zm_activate_pib (pib.c:1880) ==28758== by 0xA79F1B: zend_activate_modules (zend_API.c:2537) ==28758== by 0x9D31D3: php_request_startup (main.c:1673) ==28758== by 0xB5909A: do_cli (php_cli.c:964) ==28758== by 0xB5A423: main (php_cli.c:1381)
Attrapé aussi.
Remarque
Valgrind capture vraiment tout. Chaque petit octet oublié quelque part dans l'énorme carte mémoire du processus est signalé par les yeux de valgrind. Vous ne pouvez pas passer.
Il s'agit d'une configuration plus complexe. Pouvez-vous repérer la fuite dans le code ci-dessous ?
static zend_array ar; PHP_MINIT_FUNCTION(pib) { zend_string *str; zval string; str = zend_string_init("yo", strlen("yo"), 1); ZVAL_STR(&string, str); zend_hash_init(&ar, 8, NULL, ZVAL_PTR_DTOR, 1); zend_hash_next_index_insert(&ar, &string); }
Deux fuites ici. Tout d'abord, nous allouons une zend_string, mais nous ne la libérons pas. Deuxièmement, nous allouons un nouveau zend_hash, mais nous ne le libérons pas non plus. Commençons par valgrind et voyons les résultats :
==31316== 296 (264 direct, 32 indirect) bytes in 1 blocks are definitely lost in loss record 1 of 2 ==32006== by 0xA3701E: __zend_malloc (zend_alloc.c:2820) ==32006== by 0xA814B2: zend_hash_real_init_ex (zend_hash.c:133) ==32006== by 0xA816D2: zend_hash_check_init (zend_hash.c:161) ==32006== by 0xA83552: _zend_hash_index_add_or_update_i (zend_hash.c:714) ==32006== by 0xA83D58: _zend_hash_next_index_insert (zend_hash.c:841) ==32006== by 0xE896AF4: zm_startup_pib (pib.c:1781) ==32006== by 0xA774F7: zend_startup_module_ex (zend_API.c:1843) ==32006== by 0xA77559: zend_startup_module_zval (zend_API.c:1858) ==32006== by 0xA85AF5: zend_hash_apply (zend_hash.c:1508) ==32006== by 0xA77B25: zend_startup_modules (zend_API.c:1969) ==31316== 32 bytes in 1 blocks are indirectly lost in loss record 2 of 2 ==31316== by 0xA3701E: __zend_malloc (zend_alloc.c:2820) ==31316== by 0xE880B0D: zend_string_alloc (zend_string.h:122) ==31316== by 0xE880B76: zend_string_init (zend_string.h:158) ==31316== by 0xE896F9D: zm_activate_pib (pib.c:1781) ==31316== by 0xA79F1B: zend_activate_modules (zend_API.c:2537) ==31316== by 0x9D31D3: php_request_startup (main.c:1673) ==31316== by 0xB5909A: do_cli (php_cli.c:964) ==31316== by 0xB5A423: main (php_cli.c:1381) ==31316== LEAK SUMMARY: ==31316== definitely lost: 328 bytes in 2 blocks
Comme prévu, les deux fuites sont signalées. Comme vous pouvez le constater, valgrind est précis et place votre œil là où il doit être.
Corrigez-les maintenant :
PHP_MSHUTDOWN_FUNCTION(pib) { zend_hash_destroy(&ar); }
Nous avons détruit les tableaux persistants dans MSHUTDOWN à la fin du programme PHP. Lorsque nous le créons, nous le transmettons comme destructeur à ZVAL_PTR_DTOR
et il exécutera ce rappel sur tous les éléments insérés. C'est le destructeur de zval qui détruira zval en analysant son contenu. Pour les types IS_STRING
, le destructeur libérera le zend_string
et le libérera si nécessaire. Terminé
Remarque
Comme vous pouvez le constater, PHP - comme tout programme fort en C - regorge de pointeurs imbriqués.
zend_string
est encapsulé danszval
et fait lui-même partie dezend_array
. Une fuite du tableau entraînera évidemment une fuite dezval
etzend_string
, maiszvals
n'est pas alloué sur le tas (nous allouons sur la pile), donc aucune fuite n'est signalée. Vous devez vous habituer au fait qu'oublier de libérer/libérer des structures composées commezend_array
peut provoquer beaucoup de fuites, car les structures ont souvent des structures imbriquées, des structures imbriquées, etc.
Les fuites de mémoire sont mauvaises. Cela amènera votre programme à déclencher le MOO à un moment ou à un autre, et ralentira considérablement l'hôte puisque ce dernier aura de moins en moins de mémoire libre au fil du temps. C'est le signe d'une fuite de mémoire.
Mais pire : accès au tampon hors limites. L'accès à des pointeurs au-delà des limites d'allocation est à l'origine de nombreuses opérations néfastes (telles que l'obtention d'un shell root sur votre ordinateur), vous devez donc absolument les empêcher. Des accès mineurs hors limites peuvent également souvent provoquer le blocage des programmes en raison d'une corruption de la mémoire. Cependant, tout dépend du matériel de la machine cible, du compilateur et des options utilisés, de la disposition de la mémoire du système d'exploitation, de la libc utilisée, etc... de nombreux facteurs.
Les accès hors limites sont donc très ennuyeux, ce sont des bombes qui peuvent ou non exploser, soit en une minute, soit si vous êtes très chanceux, elles mettent une éternité. N'exploseront pas.
Regardons un exemple simple :
PHP_MINIT_FUNCTION(pib) { char *foo = malloc(16); foo[16] = 'a'; foo[-1] = 'a'; }
Ce code alloue un tampon et écrit intentionnellement les données un octet après la limite et un octet après la limite. Désormais, si vous exécutez un code comme celui-ci, vous avez environ une chance sur deux de planter immédiatement, puis de manière aléatoire. Vous avez peut-être également créé une faille de sécurité dans PHP, mais elle n'est peut-être pas exploitable à distance (ce comportement est rare).
Avertissement
L'accès hors limites entraîne un comportement indéfini. Il n'y a aucun moyen de prédire ce qui va se passer, mais assurez-vous que ce soit mauvais (plantage immédiat) ou terrible (problème de sécurité). Souviens-toi.
Demandons à Valgrind, démarrez-le en utilisant exactement la même ligne de commande qu'avant, rien n'a changé sauf la sortie :
==12802== Invalid write of size 1 ==12802== at 0xE896A98: zm_startup_pib (pib.c:1772) ==12802== by 0xA774F7: zend_startup_module_ex (zend_API.c:1843) ==12802== by 0xA77559: zend_startup_module_zval (zend_API.c:1858) ==12802== by 0xA85AF5: zend_hash_apply (zend_hash.c:1508) ==12802== by 0xA77B25: zend_startup_modules (zend_API.c:1969) ==12802== by 0x9D4541: php_module_startup (main.c:2260) ==12802== by 0xB5802F: php_cli_startup (php_cli.c:427) ==12802== by 0xB5A367: main (php_cli.c:1348) ==12802== Address 0xeb488f0 is 0 bytes after a block of size 16 alloc'd ==12802== at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==12802== by 0xE896A85: zm_startup_pib (pib.c:1771) ==12802== by 0xA774F7: zend_startup_module_ex (zend_API.c:1843) ==12802== by 0xA77559: zend_startup_module_zval (zend_API.c:1858) ==12802== by 0xA85AF5: zend_hash_apply (zend_hash.c:1508) ==12802== by 0xA77B25: zend_startup_modules (zend_API.c:1969) ==12802== by 0x9D4541: php_module_startup (main.c:2260) ==12802== by 0xB5802F: php_cli_startup (php_cli.c:427) ==12802== by 0xB5A367: main (php_cli.c:1348) ==12802== ==12802== Invalid write of size 1 ==12802== at 0xE896AA6: zm_startup_pib (pib.c:1773) ==12802== by 0xA774F7: zend_startup_module_ex (zend_API.c:1843) ==12802== by 0xA77559: zend_startup_module_zval (zend_API.c:1858) ==12802== by 0xA85AF5: zend_hash_apply (zend_hash.c:1508) ==12802== by 0xA77B25: zend_startup_modules (zend_API.c:1969) ==12802== by 0x9D4541: php_module_startup (main.c:2260) ==12802== by 0xB5802F: php_cli_startup (php_cli.c:427) ==12802== by 0xB5A367: main (php_cli.c:1348) ==12802== Address 0xeb488df is 1 bytes before a block of size 16 alloc'd ==12802== at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==12802== by 0xE896A85: zm_startup_pib (pib.c:1771) ==12802== by 0xA774F7: zend_startup_module_ex (zend_API.c:1843) ==12802== by 0xA77559: zend_startup_module_zval (zend_API.c:1858) ==12802== by 0xA85AF5: zend_hash_apply (zend_hash.c:1508) ==12802== by 0xA77B25: zend_startup_modules (zend_API.c:1969) ==12802== by 0x9D4541: php_module_startup (main.c:2260) ==12802== by 0xB5802F: php_cli_startup (php_cli.c:427) ==12802== by 0xB5A367: main (php_cli.c:1348)
Ces deux entrées d'écriture invalides ont été détectées, maintenant votre objectif est de les retrouver et de les réparer.
在这里,我们使用了一个示例,其中我们超出范围地写入内存,这是最糟糕的情况,因为您的写入操作成功后(可能会立即导致SIGSEGV)将覆盖该指针旁边的一些关键区域。当我们使用libc的malloc()
进行分配时,我们将覆盖libc用于管理和跟踪其分配的关键头尾块。取决于许多因素(平台,使用的libc,如何编译等等),这将导致崩溃。
Valgrind也可能报告无效读取。这意味着您将在分配的指针的范围之外执行内存读取操作。更好的情况是块被覆盖,但您仍然不应该访问内存区域,在这种情况下又可能会导致立即崩溃,或者稍后崩溃,或者永远不会访问?不要那样做
Note
一旦您在valgrind的输出中读取“ Invalid”,那对您来说真的很不好。无论是无效的读取还是写入,您的代码中都存在问题,因此您应该将这个问题视为高风险:现在就真正修复它。
这是有关字符串连接的第二个示例:
char *foo = strdup("foo"); char *bar = strdup("bar"); char *foobar = malloc(strlen("foo") + strlen("bar")); memcpy(foobar, foo, strlen(foo)); memcpy(foobar + strlen("foo"), bar, strlen(bar)); fprintf(stderr, "%s", foobar); free(foo); free(bar); free(foobar);
你能发现问题吗?
让我们问一下valgrind:
==13935== Invalid read of size 1 ==13935== at 0x4C30F74: strlen (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==13935== by 0x768203E: fputs (iofputs.c:33) ==13935== by 0xE896B91: zm_startup_pib (pib.c:1779) ==13935== by 0xA774F7: zend_startup_module_ex (zend_API.c:1843) ==13935== by 0xA77559: zend_startup_module_zval (zend_API.c:1858) ==13935== by 0xA85AF5: zend_hash_apply (zend_hash.c:1508) ==13935== by 0xA77B25: zend_startup_modules (zend_API.c:1969) ==13935== by 0x9D4541: php_module_startup (main.c:2260) ==13935== by 0xB5802F: php_cli_startup (php_cli.c:427) ==13935== by 0xB5A367: main (php_cli.c:1348) ==13935== Address 0xeb48986 is 0 bytes after a block of size 6 alloc'd ==13935== at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==13935== by 0xE896B14: zm_startup_pib (pib.c:1774) ==13935== by 0xA774F7: zend_startup_module_ex (zend_API.c:1843) ==13935== by 0xA77559: zend_startup_module_zval (zend_API.c:1858) ==13935== by 0xA85AF5: zend_hash_apply (zend_hash.c:1508) ==13935== by 0xA77B25: zend_startup_modules (zend_API.c:1969) ==13935== by 0x9D4541: php_module_startup (main.c:2260) ==13935== by 0xB5802F: php_cli_startup (php_cli.c:427) ==13935== by 0xB5A367: main (php_cli.c:1348)
第1779行指向fprintf()
调用。该调用确实要求fputs()
,其本身称为strlen()
(均来自libc),在这里strlen()
读取1个字节无效。
我们只是忘记了\ 0
来终止我们的字符串。我们传递fprintf()
无效的字符串。它首先尝试计算调用strlen()
的字符串的长度。然后strlen()
将扫描缓冲区,直到找到\ 0
,并且它将扫描缓冲区的边界,因为我们忘记了对其进行零终止。我们在这里很幸运,strlen()
仅从末尾传递一个字节。那可能更多,并且可能崩溃了,因为我们真的不知道下一个\ 0
在内存中的位置,这是随机的。
解:
size_t len = strlen("foo") + strlen("bar") + 1; /* note the +1 for \0 */ char *foobar = malloc(len); /* ... ... same code ... ... */ foobar[len - 1] = '\0'; /* terminate the string properly */
Note
上述错误是C语言中最常见的错误之一。它们被称为一次性错误:您忘记仅分配一个字节,但是由于以下原因,您将在代码中产生大量问题那。
最后,这里是最后一个示例,展示了一个有余使用的场景。这也是C编程中的一个非常常见的错误,与错误的内存访问一样严重:它创建了安全缺陷,可能导致非常讨厌的行为。显然,valgrind可以检测到无用后使用。这是一个:
char *foo = strdup("foo"); free(foo); memcpy(foo, "foo", sizeof("foo"));
同样,这里是一个与PHP无关的PHP场景。我们释放一个指针,然后再使用它。这是一个大错误。让我们问一下valgrind:
==14594== Invalid write of size 1 ==14594== at 0x4C3245C: memcpy@GLIBC_2.2.5 (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==14594== by 0xE896AA1: zm_startup_pib (pib.c:1774) ==14594== by 0xA774F7: zend_startup_module_ex (zend_API.c:1843) ==14594== by 0xA77559: zend_startup_module_zval (zend_API.c:1858) ==14594== by 0xA85AF5: zend_hash_apply (zend_hash.c:1508) ==14594== by 0xA77B25: zend_startup_modules (zend_API.c:1969) ==14594== by 0x9D4541: php_module_startup (main.c:2260) ==14594== by 0xB5802F: php_cli_startup (php_cli.c:427) ==14594== by 0xB5A367: main (php_cli.c:1348) ==14594== Address 0xeb488e0 is 0 bytes inside a block of size 4 free'd ==14594== at 0x4C2EDEB: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==14594== by 0xE896A86: zm_startup_pib (pib.c:1772) ==14594== by 0xA774F7: zend_startup_module_ex (zend_API.c:1843) ==14594== by 0xA77559: zend_startup_module_zval (zend_API.c:1858) ==14594== by 0xA85AF5: zend_hash_apply (zend_hash.c:1508) ==14594== by 0xA77B25: zend_startup_modules (zend_API.c:1969) ==14594== by 0x9D4541: php_module_startup (main.c:2260) ==14594== by 0xB5802F: php_cli_startup (php_cli.c:427) ==14594== by 0xB5A367: main (php_cli.c:1348) ==14594== Block was alloc'd at ==14594== at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==14594== by 0x769E8D9: strdup (strdup.c:42) ==14594== by 0xE896A70: zm_startup_pib (pib.c:1771) ==14594== by 0xA774F7: zend_startup_module_ex (zend_API.c:1843) ==14594== by 0xA77559: zend_startup_module_zval (zend_API.c:1858) ==14594== by 0xA85AF5: zend_hash_apply (zend_hash.c:1508) ==14594== by 0xA77B25: zend_startup_modules (zend_API.c:1969) ==14594== by 0x9D4541: php_module_startup (main.c:2260) ==14594== by 0xB5802F: php_cli_startup (php_cli.c:427) ==14594== by 0xB5A367: main (php_cli.c:1348)
这里的一切再次变得清晰。
在投入生产之前,请使用内存调试器。正如您在本章中学到的那样,您在计算中忘记的小字节可能导致可利用的安全漏洞。它还经常(非常频繁地)导致简单的崩溃。这意味着您的扩展很酷,可以减少整个服务器(服务器)及其每个客户端的数量。
C是一种非常严格的编程语言。您将获得数十亿字节的内存来进行编程,并且必须安排这些内存来执行一些计算。但是请不要搞砸这种强大的功能:在最好的情况下(罕见),什么都不会发生,在更坏的情况下(非常常见),您会在这里和那里随机崩溃,在最坏的情况下,您会创建一个漏洞在恰好可以被远程利用的程序中...
您的工具娴熟,聪明,请确实照顾机器内存。
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!