溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

如何進(jìn)行php內(nèi)存調(diào)試

發(fā)布時(shí)間:2020-07-30 10:24:36 來源:億速云 閱讀:156 作者:Leah 欄目:編程語(yǔ)言

如何進(jìn)行php內(nèi)存調(diào)試?很多新手對(duì)此不是很清楚,為了幫助大家解決這個(gè)難題,下面小編將為大家詳細(xì)講解,有這方面需求的人可以來學(xué)習(xí)下,希望你能有所收獲。

                                               

內(nèi)存調(diào)試

本章是有關(guān)PHP源代碼的內(nèi)存調(diào)試的簡(jiǎn)要介紹。 這不是一門完整的課程:內(nèi)存調(diào)試并不難, 但是你需要一些它的使用經(jīng)驗(yàn),大量的練習(xí)可能是你在設(shè)計(jì)任何C編寫的代碼時(shí)都必須要做的事情。我們將在這里介紹一個(gè)非常著名的內(nèi)存調(diào)試器: valgrind; 以及如何將其與PHP一起使用來調(diào)試內(nèi)存問題。


Valgrind簡(jiǎn)介

Valgrind是許多Unix環(huán)境下使用的知名工具,可以在任何C/C++編寫的軟件中調(diào)試許多常見的內(nèi)存問題。 Valgrind 是有關(guān)內(nèi)存調(diào)試的多功能前端工具。最常用的底層工具稱為 “memcheck”。它的工作方式是用自己的堆分配替換每個(gè)libc的堆分配,并跟蹤你對(duì)它們所做的事情。你可能還會(huì)對(duì) “massif” 感興趣: 它是一個(gè)內(nèi)存跟蹤器,對(duì)于了解程序的常規(guī)堆內(nèi)存使用情況非常有用。

注意

你應(yīng)該閱讀Valgrind文檔,以進(jìn)一步了解。 它寫得很好,帶有一些典型的例子。

為了進(jìn)行內(nèi)存分配替換,你需要通過 valgrind 運(yùn)行要分析的程序(此處為PHP),也就是啟動(dòng) valgrind 二進(jìn)制文件。

當(dāng) valgrind 替換并跟蹤所有 libc 的堆分配時(shí),它往往會(huì)大大降低調(diào)試程序的速度。對(duì)于PHP,你會(huì)注意到它。盡管 PHP 的速度下降并不那么劇烈,但是仍然可以清楚地感覺到;如果你注意到它,不用擔(dān)心,這是正常的。

Valgrind 不是你可能會(huì)使用的唯一工具,但是是最常用的工具。還有其他工具,例如 Dr.Memory、LeakSanitizer、Electric Fence、AddressSanitizer。

在開始之前

以下是在存儲(chǔ)器調(diào)試方面具有良好經(jīng)驗(yàn)并減輕發(fā)現(xiàn)缺陷并減少調(diào)試時(shí)間的機(jī)會(huì)所需的步驟:

-您應(yīng)始終使用PHP的調(diào)試版本。嘗試調(diào)試生產(chǎn)版本中的內(nèi)存是無關(guān)緊要的。
-您應(yīng)該始終在 USE_ZEND_ALLOC = 0 環(huán)境下啟動(dòng)調(diào)試器。您可能已經(jīng)在Zend Memory Manager章節(jié)中了解到,此環(huán)境var會(huì)在當(dāng)前進(jìn)程啟動(dòng)時(shí)禁用ZendMM。強(qiáng)烈建議在啟動(dòng)內(nèi)存調(diào)試器時(shí)這樣做。完全繞過ZendMM有助于了解valgrind生成的跟蹤。
-強(qiáng)烈建議在環(huán)境 ZEND_DONT_UNLOAD_MODULES = 1 下啟動(dòng)內(nèi)存調(diào)試器。這樣可以防止PHP在過程結(jié)束時(shí)卸載擴(kuò)展程序的.so文件。這是為了獲得更好的valgrind報(bào)告跟蹤;如果在valgrind將要顯示其錯(cuò)誤時(shí)PHP將卸載擴(kuò)展名,則稍后將不完整,因?yàn)閺闹蝎@取信息的文件不再是進(jìn)程內(nèi)存映像的一部分。
-您可能需要一些抑制措施。當(dāng)您告訴PHP在過程結(jié)束時(shí)不要卸載其擴(kuò)展名時(shí),可能會(huì)在valgrind輸出中給您誤報(bào)。將檢查PHP擴(kuò)展是否泄漏,如果您在平臺(tái)上誤報(bào),則可以使用抑制功能將其關(guān)閉像這樣??梢愿鶕?jù)這樣的示例隨意編寫自己的文件。
-與Zend Memory Manager相比,Valgrind顯然是更好的工具,可以查找泄漏和其他與內(nèi)存相關(guān)的問題。您應(yīng)該始終在代碼上運(yùn)行valgrind,這實(shí)際上是每個(gè)C程序員都必須執(zhí)行的步驟。無論是因?yàn)楸罎⒍胍业讲⒄{(diào)試它,還是作為看起來好像沒有任何壞處的高質(zhì)量工具來運(yùn)行它,valgrind都是這種工具,它可以指出隱藏的瑕疵,準(zhǔn)備好將其吹拂一次或以后。即使您認(rèn)為代碼似乎一切都很好,也可以使用它:您可能會(huì)感到驚訝。

Warning

您**必須在程序上使用valgrind(或任何內(nèi)存調(diào)試器)。對(duì)于每個(gè)強(qiáng)大的C程序,要不調(diào)試內(nèi)存就不可能100%充滿信心。內(nèi)存錯(cuò)誤會(huì)導(dǎo)致有害的安全問題,并且程序崩潰通常取決于許多參數(shù),通常是隨機(jī)的。

內(nèi)存泄漏檢測(cè)示例

入門

Valgrind是一個(gè)完整的堆內(nèi)存調(diào)試器。它還可以調(diào)試過程內(nèi)存映射和功能堆棧。請(qǐng)?jiān)谄湮臋n中獲取更多信息。

讓我們?nèi)z測(cè)動(dòng)態(tài)內(nèi)存泄漏,并嘗試一個(gè)簡(jiǎn)單的,最常見的泄漏:

PHP_RINIT_FUNCTION(pib)
{
    void *foo = emalloc(128);
}

上面的代碼每次請(qǐng)求都會(huì)泄漏128字節(jié),因?yàn)樗鼪]有與此類緩沖區(qū)有關(guān)的efree()相關(guān)調(diào)用。由于它是對(duì)emalloc()的調(diào)用,因此會(huì)通過Zend Memory Manager,因此稍后會(huì)警告我們就像我們?cè)赯endMM章節(jié)中看到的那樣。我們還要看看valgrind是否可以注意到泄漏:

> 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

我們使用valgrind啟動(dòng)PHP-CLI進(jìn)程。我們?cè)谶@里假設(shè)一個(gè)名為“ pib”的擴(kuò)展名。這是輸出:

==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

在我們看來,“絕對(duì)失落”是我們必須關(guān)注的。

Note

有關(guān)memcheck輸出的不同字段的詳細(xì)信息,請(qǐng)查看。

Note

我們使用USE_ZEND_ALLOC = 0禁用并完全繞過Zend Memory Manager。對(duì)其API的每次調(diào)用(例如emalloc())將直接導(dǎo)致libc調(diào)用,就像我們?cè)赾algrind輸出堆棧幀上可以看到的那樣。

Valgrind抓住了我們的漏洞。

很容易,現(xiàn)在我們可以使用持久分配(也就是繞過ZendMM并使用傳統(tǒng)libc的動(dòng)態(tài)內(nèi)存分配)來產(chǎn)生泄漏。走:

PHP_RINIT_FUNCTION(pib)
{
    void *foo = malloc(128);
}

這是報(bào)告:

==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)

也抓到了。

Note

Valgrind確實(shí)可以捕獲所有內(nèi)容。巨大的進(jìn)程內(nèi)存映射中某個(gè)地方的每一個(gè)被遺忘的小字節(jié)都會(huì)被valgrind的眼睛報(bào)告。您無法通過。

更復(fù)雜的用例

這是一個(gè)更復(fù)雜的設(shè)置。您可以在下面的代碼中發(fā)現(xiàn)泄漏嗎?

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);
}

這里有兩個(gè)泄漏。首先,我們分配一個(gè)zend_string,但我們沒有釋放它。其次,我們分配一個(gè)新的zend_hash,但是我們也不釋放它。讓我們用valgrind啟動(dòng)它,然后查看結(jié)果:

==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

如預(yù)期的那樣,兩個(gè)泄漏都被報(bào)告。如您所見,valgrind是準(zhǔn)確的,它將您的眼睛放在需要的地方。

現(xiàn)在修復(fù)它們:

PHP_MSHUTDOWN_FUNCTION(pib)
{
    zend_hash_destroy(&ar);
}

我們?cè)赑HP程序結(jié)束時(shí)在MSHUTDOWN中銷毀了持久數(shù)組。創(chuàng)建它時(shí),我們將其作為析構(gòu)函數(shù)傳遞給ZVAL_PTR_DTOR,它將在插入的所有項(xiàng)目上運(yùn)行該回調(diào)。這是zval的析構(gòu)函數(shù),它將破壞zval分析它們的內(nèi)容。對(duì)于IS_STRING類型,析構(gòu)函數(shù)將釋放zend_string并在必要時(shí)釋放它。做完了

Note

如您所見,PHP-像任何C語(yǔ)言強(qiáng)程序一樣-充滿了嵌套的指針。zend_string封裝在zval中,其本身是zend_array的一部分。泄漏數(shù)組顯然會(huì)泄漏zvalzend_string,但是zvals沒有分配堆(我們?cè)诙褩I戏峙?,因此沒有泄漏報(bào)告。您應(yīng)該習(xí)慣這樣一個(gè)事實(shí),即忘記釋放/釋放諸如zend_array之類的復(fù)合結(jié)構(gòu)會(huì)導(dǎo)致大量泄漏,因?yàn)榻Y(jié)構(gòu)經(jīng)常會(huì)嵌入結(jié)構(gòu),嵌入結(jié)構(gòu)等。

緩沖區(qū)上溢/下溢檢測(cè)

內(nèi)存泄漏很糟糕。這將導(dǎo)致您的程序一次或以后觸發(fā)OOM,并且將大大降低主機(jī)的速度,因?yàn)殡S著時(shí)間的流逝,后者將獲得越來越少的可用內(nèi)存。這是內(nèi)存泄漏的征兆。

但是更糟的是:緩沖區(qū)越界訪問。訪問超出分配限制的指針是許多邪惡操作(例如在計(jì)算機(jī)上獲得root shell)的根源,因此您絕對(duì)應(yīng)該防止它們。較輕的越界訪問也經(jīng)常會(huì)由于內(nèi)存損壞而導(dǎo)致程序崩潰。但是,這全部取決于硬件目標(biāo)計(jì)算機(jī),使用的編譯器和選項(xiàng),操作系統(tǒng)內(nèi)存布局,使用的libc等……許多因素。

因此,越界訪問非常令人討厭,它們是炸彈,可能會(huì)爆炸,也可能不會(huì)爆炸,或者在一分鐘內(nèi),或者如果您非常幸運(yùn),它們將永遠(yuǎn)不會(huì)爆炸。

  • Valgrind *是一個(gè)內(nèi)存調(diào)試器,因此能夠檢測(cè)到來自任何內(nèi)存區(qū)域(堆和堆棧)的任何越界訪問。這與查找泄漏使用的是相同的memcheck工具。

讓我們看一個(gè)簡(jiǎn)單的例子:

PHP_MINIT_FUNCTION(pib)
{
    char *foo = malloc(16);
    foo[16] = 'a';
    foo[-1] = 'a';
}

這段代碼分配了一個(gè)緩沖區(qū),并故意在邊界后一個(gè)字節(jié)和邊界后一個(gè)字節(jié)寫入數(shù)據(jù)?,F(xiàn)在,如果您運(yùn)行這樣的代碼,您就有大約兩次機(jī)會(huì)中有一次立即崩潰,然后隨機(jī)崩潰。您可能還已經(jīng)在PHP中創(chuàng)建了一個(gè)安全漏洞,但是它可能無法被遠(yuǎn)程利用(這種行為很少見)。

Warning

越界訪問導(dǎo)致不確定的行為。無法預(yù)料會(huì)發(fā)生什么,但是請(qǐng)確保它不好(立即崩潰)或可怕(安全問題)。記得。

讓我們問一下valgrind,使用與之前完全相同的命令行來啟動(dòng)它,除了輸出內(nèi)容外,其他都沒有改變:

==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)

這兩個(gè)無效的寫入都已被檢測(cè)到,現(xiàn)在您的目標(biāo)是跟蹤并修復(fù)它們。

在這里,我們使用了一個(gè)示例,其中我們超出范圍地寫入內(nèi)存,這是最糟糕的情況,因?yàn)槟膶懭氩僮鞒晒?可能會(huì)立即導(dǎo)致SIGSEGV)將覆蓋該指針旁邊的一些關(guān)鍵區(qū)域。當(dāng)我們使用libc的malloc()進(jìn)行分配時(shí),我們將覆蓋libc用于管理和跟蹤其分配的關(guān)鍵頭尾塊。取決于許多因素(平臺(tái),使用的libc,如何編譯等等),這將導(dǎo)致崩潰。

Valgrind也可能報(bào)告無效讀取。這意味著您將在分配的指針的范圍之外執(zhí)行內(nèi)存讀取操作。更好的情況是塊被覆蓋,但您仍然不應(yīng)該訪問內(nèi)存區(qū)域,在這種情況下又可能會(huì)導(dǎo)致立即崩潰,或者稍后崩潰,或者永遠(yuǎn)不會(huì)訪問?不要那樣做

Note

一旦您在valgrind的輸出中讀取“ Invalid”,那對(duì)您來說真的很不好。無論是無效的讀取還是寫入,您的代碼中都存在問題,因此您應(yīng)該將這個(gè)問題視為高風(fēng)險(xiǎn):現(xiàn)在就真正修復(fù)它。

這是有關(guān)字符串連接的第二個(gè)示例:

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);

你能發(fā)現(xiàn)問題嗎?

讓我們問一下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()調(diào)用。該調(diào)用確實(shí)要求fputs(),其本身稱為strlen()(均來自libc),在這里strlen()讀取1個(gè)字節(jié)無效。

我們只是忘記了\ 0來終止我們的字符串。我們傳遞fprintf()無效的字符串。它首先嘗試計(jì)算調(diào)用strlen()的字符串的長(zhǎng)度。然后strlen()將掃描緩沖區(qū),直到找到\ 0,并且它將掃描緩沖區(qū)的邊界,因?yàn)槲覀兺浟藢?duì)其進(jìn)行零終止。我們?cè)谶@里很幸運(yùn),strlen()僅從末尾傳遞一個(gè)字節(jié)。那可能更多,并且可能崩潰了,因?yàn)槲覀冋娴牟恢老乱粋€(gè)\ 0在內(nèi)存中的位置,這是隨機(jī)的。

解:

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

上述錯(cuò)誤是C語(yǔ)言中最常見的錯(cuò)誤之一。它們被稱為一次性錯(cuò)誤:您忘記僅分配一個(gè)字節(jié),但是由于以下原因,您將在代碼中產(chǎn)生大量問題那。

最后,這里是最后一個(gè)示例,展示了一個(gè)有余使用的場(chǎng)景。這也是C編程中的一個(gè)非常常見的錯(cuò)誤,與錯(cuò)誤的內(nèi)存訪問一樣嚴(yán)重:它創(chuàng)建了安全缺陷,可能導(dǎo)致非常討厭的行為。顯然,valgrind可以檢測(cè)到無用后使用。這是一個(gè):

char *foo = strdup("foo");
free(foo);

memcpy(foo, "foo", sizeof("foo"));

同樣,這里是一個(gè)與PHP無關(guān)的PHP場(chǎng)景。我們釋放一個(gè)指針,然后再使用它。這是一個(gè)大錯(cuò)誤。讓我們問一下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)

這里的一切再次變得清晰。

結(jié)論

在投入生產(chǎn)之前,請(qǐng)使用內(nèi)存調(diào)試器。正如您在本章中學(xué)到的那樣,您在計(jì)算中忘記的小字節(jié)可能導(dǎo)致可利用的安全漏洞。它還經(jīng)常(非常頻繁地)導(dǎo)致簡(jiǎn)單的崩潰。這意味著您的擴(kuò)展很酷,可以減少整個(gè)服務(wù)器(服務(wù)器)及其每個(gè)客戶端的數(shù)量。

C是一種非常嚴(yán)格的編程語(yǔ)言。您將獲得數(shù)十億字節(jié)的內(nèi)存來進(jìn)行編程,并且必須安排這些內(nèi)存來執(zhí)行一些計(jì)算。但是請(qǐng)不要搞砸這種強(qiáng)大的功能:在最好的情況下(罕見),什么都不會(huì)發(fā)生,在更壞的情況下(非常常見),您會(huì)在這里和那里隨機(jī)崩潰,在最壞的情況下,您會(huì)創(chuàng)建一個(gè)漏洞在恰好可以被遠(yuǎn)程利用的程序中...

您的工具嫻熟,聰明,請(qǐng)確實(shí)照顧機(jī)器內(nèi)存。

看完上述內(nèi)容是否對(duì)您有幫助呢?如果還想對(duì)相關(guān)知識(shí)有進(jìn)一步的了解或閱讀更多相關(guān)文章,請(qǐng)關(guān)注億速云行業(yè)資訊頻道,感謝您對(duì)億速云的支持。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI