[openssl] openssl asynch_mode 使用libasan时的OOM问题

 

 [classic_tong: https://www.cnblogs.com/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的主要用途:  实现协程,  实现信号处理, 实现异常处理.

 

posted on 2021-01-04 19:26  toong  阅读(799)  评论(0编辑  收藏  举报