【積土成山】AddressSanitizer: heap-buffer-overflow on address

背景,不同場景,內在原理,參考文獻

一、背景

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另一個總結

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章