[openssl] openssl asynch_mode 使用libasan時的OOM問題
[classic_tong: http://www.rzrgm.cn/hugetong/p/14231782.html]
概述
openssl支持async mode. 在定位越界問題時,我使用了libasan, 之后就OOM了, 能夠看見在這個地方:

原因是因為, memset的size參數特別大, (程序本身的虛擬內存就占了很多, 隨著memset的寫入,會不斷申請更多的真實內存,最后OOM ?? )
接下來將重點分析為什么size會這么大
使用
openssl 使用libasan的方法
config時,使用選項 enable-asan
./Configure --prefix=/root/debug/ shared enable-asan \ LDFLAGS="-Wl,-rpath,/root/debug/lib" \ linux-x86_64
用戶程序使用libasan的方法:
因為我的程序是用來了openssl, 所以兩邊都要同時加, 實踐過程中, 如果用戶程序不加只在openssl上加是不行的.
LDFLAGS也要叫兩個-f參數, 不然會coredump, 我也不知道為什么.
CFLAGS+=-fsanitize=address -fno-omit-frame-pointer LDFLAGS+= -lssl -lcrypto LDFLAGS+=-fsanitize=address -fno-omit-frame-pointer LDFLAGS+= -lasan
分析
一 libasan
libasan怎么檢測longjmp API, 以下是gcc4.8.5里邊的代碼



大概意思是, gcc使用libasan之后, hock了longjmp, 在真正進入longjmp之前調用函數__asan_handle_no_return() 進行內存檢測.
在該函數里, top是之前保存的調用棧的棧頂. &local_stack是取了臨時變量的地址也就是當前使用的調用棧的棧底地址.
然后會memset, 把整個調用棧(maybe?) 寫0, 而我當前出現的問題就是臨時變量的地址沒有取到pthread的調用棧的地址上.
所以兩個一減, size就特別大.

我們知道調用棧的地址是0x7fff是正常的, 這個local_stack的0x609200值,很明顯是不正常的, 從而導致了size不正常, 接下來繼續分析
這個值為什么是這樣的.
二 async
上文中調用棧的值的變化,主要是由api setcontext 導致的, 見如下:

上文中提到的OOM復現過程是這樣的, openssl進入SSL_accept函數之后, 在61行進行了第一次上下文切換, 之后在第59行進行了如上文調用棧圖片所示的,
有qatengine觸發的第二次上下文切換. 于是進入了libasan的__asan_handle_no_return() 并觸發OOM.
setcontext函數切換了整個調用棧的棧指針, 所以會有上文0x60開頭的棧地址,而不是0x7f, 如下圖的registers的rsp可見:
函數調用前:

函數調用后:

擴展
longjmp與setcontext都是用來做上下文切換的API, 功能可以相互替換.
setcontext之所以使用了新的地址,而不是就得0x7f地址, 推測是為了提高效率防止拷貝. 帶來的一個隱形壞處是gdb不能繼續單步執行, 單步執行的下一步就是它
切回來的時候, 繼續使用gdb的方法是, 在即將切換的那個函數上加一個斷點.
longjmp沒有這個gdb的困擾問題, 推測它每次切換都會做一次棧拷貝.
此類API的主要用途: 實現協程, 實現信號處理, 實現異常處理.
浙公網安備 33010602011771號