背景,不同場景,內在原理,參考文獻
一、背景
1.1 問題背景
1 在做leetcode時,總是會遇到關於“heap-buffer-overflow”的問題;
2 下面對這些場景進行歸納,提出潛在檢查點,以及嘗試揭示出其內在原理;
1.2 AddressSanitizer
1 快速內存錯誤檢測工具;
2 這裏還提供一種方法,進行本地安裝和調試;
3 那麼在之後如果leetcode不過,然後給出的堆溢出信息又不全的情況下,可以使用這種方法進行調試;
二、場景分析
2.1 自己做leetcode時,遇到的問題
1 報錯現象
==46==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x603000000030 at pc 0x000000405074 bp 0x7ffe12fc3280 sp 0x7ffe12fc3270
R
READ of size 8 at 0x603000000030 thread T0
#2 0x7f68203db82f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
0
0x603000000030 is located 0 bytes to the right of 32-byte region [0x603000000010,0x603000000030)
a
allocated by thread T0 here:
#0 0x7f68213f6f88 in (/usr/lib/x86_64-linux-gnu/libasan.so.5+0x10bf88)
#3 0x7f68203db82f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
S
Shadow bytes around the buggy address:
char ** stringMatching(char ** words, int wordsSize, int* returnSize){
returnSize = (int *)malloc(sizeof(int));
int i, j, k;
for (i = 0; i < wordsSize; i++) {
printf("%d, %s, %p", wordsSize, words[i], &(words[i]));
}
*returnSize = wordsSize;
return words;
2 READ,指出線程名是T0,操作數是READ,大小是8個byte;
‘read of size 1 at address…’
It means you read single byte form the given address
3 原因是傳入的是一個二重指針,確實是可以通過訪問words[i],找到其指向的內容;
但是在返回時,由於words二重指針的內容是分配在棧中,返回之後,就會刪除,因此該指針返回之後,沒有任何價值;
4 應該的做法是:將該指針所指向的字符串內容,重新malloc一塊內存進行存儲;
(64位操作系統 int和指針的長度都是八個字節)
2.2 之前同學遇到一個類似問題
1 在leetcode上做題,測試用例是過的,但是在跑全部測試用例時,出現問題,然後通過查看代碼,是因爲對非法入參沒有判空導致;
2 然後通過修改之後,可以順利通過;
2.3 csdn博客1
1 AddressSanitizer: heap-buffer-overflow on address 0x602000000040 at pc 0x000000406b5e bp 0x7ffc15cc0320 sp 0x7ffc15cc0318
2 LeetCode使用了AddressSanitizer檢查了是否存在內存非法訪問;數組訪問越界,也是絕大部分的內存訪問題
3 原因:把for循環內的i <= nums.size() 修改成 i < nums.size()即可
4 此外,有其他幾位同學也是類似的情況,即數組越界導致;
綜上,有兩種途徑解決類似問題
1 着重排查通過申請內存,來給指針賦值的方式,獲取的數組,查看這些數組的被訪問地方是否存在越界;
2 在本地安裝addressSanitizer,進行本地調試,獲取全部調試信息;
三、內在原理
3.1 函數入參的傳遞
1 傳入的值,在函數所屬的棧上,新分配一塊空間,用於傳遞;
2 比如int型,那麼傳入的這個值,位於棧中,類似局部變量,函數退出時會釋放,不會傳遞給外層;
3 比如int*, 傳入的是整型數值所存儲的地址,這樣可以通過在內部修改傳入指針所執行地址的值,來實現出參的傳遞;
4 同理,如果需要修改結構體的值,那麼也需要傳入能夠訪問到這個結構體位置的地址的值,而由於能夠訪問到這個結構體的位置,是一個指針,所以傳入的是一個二重指針,其實本質,與int *是一致的;
3.2 malloc分配及memset的使用
int value = (int)malloc(n * sizeof(int));
(void)memset(value, 0, sizeof(n * sizeof(int));
3.3 通過strncpy拷貝字符串後,是否需要加上’\0’
strncpy舉例
需要在後面,加上’\0’,不然會出現亂碼,但是用strncpy_s就不會出現;
四、參考文獻
1 介紹AddressSanitize
2 使用AddressSanitize在linux下進行安裝和使用
3 csdn案例一
4 stackoverflow介紹告警字段含義
5 CSDN另一個總結