linux下關於棧的一些有意思的地方「stack」

linux C/C++ 開發過程中避免不了對程序棧的使用,函數參數傳遞、局部自動變量都會在用戶空間的棧上佔有一席之地。我們都知道linux 我們是可以設置stack大小一般情況下默認是8M,那麼我們來驗證下,請看下面的代碼:(測試的docker實例中ulimit -s 是8192)

#include <stdio.h>
#include <unistd.h>
void test(){
static int i = 0;
char t[1024*256] = { 0 };
printf("t=%p index=%d\\n",&t,++i);
test();
}
int main() {
test();
return 0;
}
/<unistd.h>/<stdio.h>

輸出結果:

linux下關於棧的一些有意思的地方「stack」

這段代碼主要是為了演示在linux平臺下配置了線程棧8M的情況下大概有多少棧內存可以使用,可以看到這裡沒有分配到8M,因為進程進入到main過程中還有一些調用、遞歸調用的返回地址等的開銷。

有時候可能開發過程中由於bug導致棧溢出,那麼我們如何來判斷是否是棧溢出導致的core

dump?其實很簡單,通過gdb獲取到訪問的內存地址,然後查看進程對應的棧地址空間映射範圍就知道是否是因為棧溢出導致的:

方法1:通過採用pmap pid 這裡以pid=35為例

pmap 35
Address Kbytes RSS Dirty Mode Mapping
0000000000400000 4 4 0 r-x-- a.out
0000000000600000 4 4 4 r---- a.out
0000000000601000 4 4 4 rw--- a.out
00007f0869b26000 1804 984 0 r-x-- libc-2.17.so
......
00007f086a935000 4 4 4 rw--- [ anon ]
00007ffe390d7000 8192 16 16 rw--- [ stack ] 00007ffe39170000 8 0 0 r---- [ anon ]
00007ffe39172000 8 4 0 r-x-- [ anon ]
ffffffffff600000 4 0 0 r-x-- [ anon ]

看到mapping是stack一行,通過address + size 得到棧的地址空間範圍,然後跟gdb中獲取到的地址比對,如果超過了該範圍,那就表示棧溢出了。

方法2:通過 proc文件系統看

cat /proc/35/smaps

......
7ffe390d7000-7ffe398d7000 rw-p 00000000 00:00 0 [stack]
Size: 132 kB
........

對應的stack一行就有對應的棧起始、結束地址映射範圍。

另外關於棧溢出攻擊的案例也很多,那麼我們是否有一些方案來避免這些問題的發生呢?方法總比困難多:

1)代碼設計時,時刻提醒自己棧、棧、棧合理利用,做好寫入字符串與目標內存長度的校驗,避免異常溢出

2)是人總會有犯錯的時候,不翻車只是時機不到,那麼我們可以利用編譯器層面在編譯時植入一些檢查代碼來避免這些問題:gcc/g++ 編譯時帶上參數-fstack-protector(只對局部變量進行檢查)、-fstack-protector-all(為所有函數插入檢查代碼),下面我們來比對下有這個參數和沒有這個參數的彙編級代碼區別:

linux下關於棧的一些有意思的地方「stack」

Fig 未開啟-fstack-protector

linux下關於棧的一些有意思的地方「stack」

Fig 開啟-fstack-protector

大家可以對比下可以發現開啟了之後多了一些檢驗的代碼,由於開啟該選項會多一些指令,性能會有寫損耗,所以大家使用時需要評估一下,是否值得引入,利大於弊是建議還是使用。這裡摘錄 GCC的一個解釋:

-fstack-protector
Emit extra code to check for buffer overflows, such as stack smashing attacks. This is done by adding a guard variable to functions with vulnerable objects. This includes functions that call alloca, and functions with buffers larger than 8 bytes. The guards are initialized when a function is entered and then checked when the function exits. If a guard check fails, an error message is printed and the program exits.

-fstack-protector-all
Like -fstack-protector except that all functions are protected.


人從事多年linux後臺開發,曾在幾家大型互聯網做過研發工作,歡迎新手、老鳥在評論區留言交流、探討學習~


分享到:


相關文章: