1、二次釋放
二次釋放簡單理解就是對同一個指針指向的內存釋放了兩次,針對c語言源代碼,對同一個指針進行兩次?free()?操作,可能導致二次釋放,本文3.1章節的缺陷代碼就是對這類情況的描述。在c++語言中,淺拷貝操作不當是導致二次釋放常見原因之一。如:調用一次賦值運算符或拷貝構造函數將會導致兩個對象的數據成員指向相同的動態內存。此時引用計數機制變得非常重要,當引用計數不當時,一個對象超出作用域時,析構函數將會釋放這兩個對象共享的內存。另一個對象中對應的數據成員將會指向已經釋放的內存地址,而當這個對象也超出作用域時,它的析構函數試圖再次釋放這塊內存,導致二次釋放問題。詳細請參見cwe id 415: double free。
2、 二次釋放的危害
二次釋放內存可能導致應用程序崩潰、拒絕服務攻擊等問題,是 C/C++ 中常見的漏洞之一。2018年1月至11月,CVE中共有38條漏洞信息與其相關。部分漏洞如下:
CVE 編號 | 概述 |
---|---|
CVE-2018-18751 | GNU gettext 0.19.8 版本中的 read-catalog.c 文件的 ‘defaultaddmessage’ 函數存在二次釋放漏洞。 |
CVE-2018-17097 | Olli Parviainen SoundTouch 2.0 版本中的 WavFile.cpp 文件的 WavFileBase 類存在安全漏洞,遠程攻擊者可利用該漏洞造成拒絕服務(二次釋放)。 |
CVE-2018-16425 | OpenSC 0.19.0-rc1 之前版本中的 libopensc/pkcs15-sc-hsm.c 文件的 ‘scpkcs15emuschsminit’ 函數存在二次釋放漏洞。攻擊者可借助特制的智能卡利用該漏洞造成拒絕服務(應用程序崩潰)。 |
CVE-2018-16402 | elfutils 0.173 版本中的 libelf/elf_end.c 文件存在安全問題,遠程攻擊者可利用該漏洞造成拒絕服務(二次釋放和應用程序崩潰)。 |
3、示例代碼
示例源于Samate Juliet Test Suite for C/C++ v1.3 (https://samate.nist.gov/SARD/testsuite.php),源文件名:CWE415_Double_Free__malloc_free_char_17.c。
3.1缺陷代碼
在上述示例代碼中,在第32行使用? malloc()?進行內存分配,并在第36行使用?free()?對分配的內存進行了釋放,在第38行 for 循環語句中,又對已經釋放的內存 data 進行了一次釋放,導致二次釋放問題。
立即學習“C語言免費學習筆記(深入)”;
使用360代碼衛士對上述示例代碼進行檢測,可以檢出“二次釋放”缺陷,顯示等級為中。如圖1所示:
圖1:二次釋放檢測示例
3.2 修復代碼
在上述修復代碼中,Samate 給出的修復方式為: 在第32行使用 malloc() 進行內存分配,并在第36行處使用 free() 進行釋放,釋放后不在對該內存進行釋放操作。
使用360代碼衛士對修復后的代碼進行檢測,可以看到已不存在“二次釋放”缺陷。如圖2:
圖2:修復后檢測結果
4?、如何避免二次釋放
要避免二次釋放,需要注意以下幾點:
(1)野指針是導致二次釋放和釋放后使用的重要原因之一,消除野指針的有效方式是在釋放指針之后立即把它設置為?NULL 或者設置為指向另一個合法的對象。(2)針對 C++ 淺拷貝導致的二次釋放問題,始終執行深拷貝是不錯的解決方案。(3)使用源代碼靜態分析工具,可以自動化的發現程序中可能存在的二次釋放問題。