【缺陷周話】第22期:錯誤的記憶體釋放物件
1、錯誤的記憶體釋放物件
C/C++程式記憶體分配方式有三種:
(1)靜態儲存區域分配,靜態儲存區域主要存放全域性變數、static變數,這部分記憶體在程式編譯時已經進行分配且在程式的整個執行期間不會被回收。
(2)棧上分配,由編譯器自動分配,用於存放函式的引數值、區域性變數等,函式執行結束時這些儲存單元自動被釋放,需要注意的是alloca()是向棧申請記憶體的。
(3)堆上分配,也就是動態分配的記憶體,動態分配的記憶體是由程式設計師負責釋放的。
上述三種情況,只有第(3)種情況是需要程式設計師手動進行釋放,如果對(1)和(2)非動態分配的記憶體進行釋放,則會導致錯誤的記憶體釋放物件問題。
本篇文章分析錯誤的記憶體釋放物件產生的原因以及修復方法。 詳細請參見 CWE-590: Free of Memory not on the Heap (3.2)。
2、 錯誤的記憶體釋放物件的危害
通過對錯誤的記憶體釋放物件原理的分析,釋放非動態分配的記憶體會導致程式的記憶體資料結構損壞,從而導致程式崩潰或拒絕服務攻擊,在某些情況下,攻擊者可以利用這個漏洞修改關鍵程式變數或執行惡意程式碼。
CVE中也有一些與之相關的漏洞資訊,從2018年1月至2019年2月,CVE中就有9條相關漏洞資訊。漏洞資訊如下:
CVE 編號 | 概述 |
---|---|
CVE-2018-7554 | sam2p 0.49.4 之前版本中的 input-bmp.ci 檔案的 ‘ReadImage’ 函式存在安全漏洞。攻擊者可藉助特製的輸入利用該漏洞造成拒絕服務(無效釋放和段錯誤)。 |
CVE-2018-7552 | sam2p 0.49.4 版本中的 mapping.cpp 檔案的 ‘Mapping::DoubleHash::clear’ 函式存在安全漏洞。攻擊者可藉助特製的輸入利用該漏洞造成拒絕服務(無效釋放和段錯誤)。 |
CVE-2018-7551 | sam2p 0.49.4 版本中的 minips.cpp 檔案的 ‘MiniPS::delete0’ 函式存在安全漏洞。攻擊者可藉助特製的輸入利用該漏洞造成拒絕服務(無效釋放和段錯誤)。 |
CVE-2018-15857 | xkbcommon 0.8.1 之前版本中的 xkbcomp/ast-build.c 檔案的 ‘ExprAppendMultiKeysymList’ 函式存在無效釋放漏洞。本地攻擊者可通過提交特製的keymap 檔案利用該漏洞造成 xkbcommon 解析器崩潰。 |
3、示例程式碼
示例源於 Samate Juliet Test Suite for C/C++ v1.3 (https://samate.nist.gov/SARD/testsuite.php),原始檔名:CWE590_Free_Memory_Not_on_Heap__delete_array_char_alloca_01.cpp。
3.1缺陷程式碼
在上述示例程式碼中,第32行使用 alloca() 函式申請記憶體,在第39行使用 delete 進行釋放,由於 alloca() 函式申請的記憶體在棧上,無需手動釋放,因此存在“錯誤的記憶體釋放物件”問題。
使用360程式碼衛士對上述示例程式碼進行檢測,可以檢出“錯誤的記憶體釋放物件”缺陷,顯示等級為高。如圖1所示:
圖1:“錯誤的記憶體釋放物件”的檢測示例
3.2 修復程式碼
在上述修復程式碼中,Samate 給出的修復方式為: 在第33行通過 new[] 動態分配記憶體,並在第40行使用 delete[] 進行釋放。從而避免了錯誤的記憶體釋放物件。
使用360程式碼衛士對修復後的程式碼進行檢測,可以看到已不存在“錯誤的記憶體釋放物件”缺陷。如圖2:
圖2:修復後檢測結果
4 、如何避免錯誤的記憶體釋放物件
要避免錯誤的記憶體釋放物件,需要注意以下幾點:
(1)不要對非動態分配的記憶體進行手動釋放;
(2)當程式結構複雜時(如條件分支較多),進行釋放時需要確認釋放的記憶體是否只來自於動態分配;
(3)明確一些函式的實現,如 alloc() 申請的記憶體在棧上,避免由於不清楚函式實現導致錯誤的記憶體釋放。
(4)realloc() 函式的原型為 void*realloc(void*ptr,size_tsize),其中第一個引數 ptr 為指標指向一個要重新分配記憶體的記憶體塊,該記憶體塊是通過呼叫 malloc、 calloc 或 realloc 進行分配記憶體的。如果向 realloc() 提供一個指向非動態記憶體分配函式分配的指標時,也會導致程式未定義行為,在使用時也需要額外注意。