OPENSSL:内存泄漏检测机制

工作的某个项目通过对openssl改造,在测试过程中发现内存泄漏,定位困难,由于是开源程序,内存分配的接口又没有使用项目提供的,分析原因不是很方便,通过搜索openssl的文章,原来自身就有其工具可以参考,这里记录下如何使用以及其运作的机制,在其他项目是个调试的手段。

程序用例

利用openssl的库进行程序编写,只使用其内存分配、读写文件接口。

#include <openssl/crypto.h>
#include <openssl/bio.h>

int main()
{
    char *p = NULL;
    BIO *b = NULL;

    CRYPTO_malloc_debug_init();
    CRYPTO_set_mem_debug_options(V_CRYPTO_MDEBUG_ALL);

    CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON);
    p = (char*)OPENSSL_malloc(4);
    
    CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_OFF);
    b = BIO_new_file("leak.log","w");
    CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON);
    CRYPTO_mem_leaks(b);

    OPENSSL_free(p);
    BIO_free(b);

    return 0;
}

编译与测试

gcc openssl_checkleak.c -lcrypto -o test_leak
./test_leak

cat leak.log
[16:54:32]     0 file=openssl_checkleak.c, line=13, thread=140667925382848, number=4, address=0160B010
4 bytes leaked in 1 chunks

程序接口说明

对于内存的跟踪并不需要重新编译openssl的库,程序中显示调用CRYPTO_malloc_debug_init,会重新配置内存相关的接口,内存跟踪方法会介入。

如果不加入 CRYPTO_set_mem_debug_options(V_CRYPTO_MDEBUG_ALL);显示如下,没有时间与线程的debug

cat leak.log
    0 file=openssl_checkleak.c, line=14, number=4, address=0208B010
4 bytes leaked in 1 chunks

开启跟踪内存 CRYPTO_MEM_CHECK_ON
关闭跟踪内存 CRYPTO_MEM_CHECK_OFF

用例中打开文件也使用了openssl的内存分配,这块内存是常驻使用的,所以在使用前将内存跟踪关闭,不对其跟踪,以免在leak文件中产生干扰。

通过CRYPTO_mem_leaks接口将系统正在使用的内存输出到文件。

更详细的使用方法可以通过man来获取。

内部实现

正常使用openssl内存管理的接口为CRYPTO_malloc_init(),内部初始化如下
默认使用malloc、realloc、free的标准接口。如果没有调用此接口进行初始化,系统也会提供默认的malloc。

# define CRYPTO_malloc_init()    CRYPTO_set_mem_functions(\
        malloc, realloc, free)

int CRYPTO_set_mem_functions(void *(*m) (size_t), void *(*r) (void *, size_t), void (*f) (void *))
{
	...
	malloc_func = m; //即 malloc 接口
	malloc_ex_func = default_malloc_ex;
	...
}

调试的接口初始化为NULL

static void (*malloc_debug_func) (void *, int, const char *, int, int) = NULL;
static void (*realloc_debug_func) (void *, void *, int, const char *, int,
                                   int)
    = NULL;
static void (*free_debug_func) (void *, int) = NULL;
static void (*set_debug_options_func) (long) = NULL;
static long (*get_debug_options_func) (void) = NULL;

如果在编译时启用了CRYPTO_MDEBUG 宏,则默认开启检测机制,不需要配置调试接口。

static void (*malloc_debug_func) (void *, int, const char *, int, int)    
    = CRYPTO_dbg_malloc;                                                  
static void (*realloc_debug_func) (void *, void *, int, const char *, int, int)                                   
    = CRYPTO_dbg_realloc;                                                 
static void (*free_debug_func) (void *, int) = CRYPTO_dbg_free;           
static void (*set_debug_options_func) (long) = CRYPTO_dbg_set_options;    
static long (*get_debug_options_func) (void) = CRYPTO_dbg_get_options;  

通过CRYPTO_malloc_debug_init接口在程序中调用调试接口

# define CRYPTO_malloc_debug_init()      do {\
        CRYPTO_set_mem_debug_functions(\       
                CRYPTO_dbg_malloc,\            
                CRYPTO_dbg_realloc,\           
                CRYPTO_dbg_free,\              
                CRYPTO_dbg_set_options,\       
                CRYPTO_dbg_get_options);\      
        } while(0)                             

内存分配OPENSSL_malloc 记录分配信息的处理机制

# define OPENSSL_malloc(num)     CRYPTO_malloc((int)num,__FILE__,__LINE__)

利用宏来实现文件与行的定位。(可以再增加被哪个函数调用的信息)

CRYPTO_malloc
    malloc_debug_func(NULL, num, file, line, 0);
    ret = malloc_ex_func(num, file, line);
    malloc_debug_func(ret, num, file, line, 1);

malloc_ex_func是真正分配内存的接口

static void *default_malloc_ex(size_t num, const char *file, int line)
{
    return malloc_func(num);
}

malloc_func被赋值为malloc

malloc_debug_func 接口在分配内存前后各调用了两次,对于malloc来说,第一次其实没有作用,直接返回,realloc会涉及到其中的作用。

CRYPTO_dbg_malloc 内部处理

  • is_MemCheck_on 状态处理,即开启记录内存信息。
  • MemCheck_off() 代码关闭记录分配,因为内部要分配内存,否则陷入无限循环
  • 分配一个 MEM 结构
  • 存储 地址addr、文件file、行号line、大小num
  • 记录序号 order
  • lh_MEM_insert 将MEM结构记录到hash中
  • MemCheck_on 重新开启
# define lh_MEM_insert(lh,inst) LHM_lh_insert(MEM,lh,inst)

void *lh_insert(_LHASH *lh, void *data) 插入到hash表中。

OPENSSL_free操作

# define OPENSSL_free(addr)      CRYPTO_free(addr)
free_debug_func(str, 0);
free_func(str);
free_debug_func(NULL, 1);

释放后的free_debug_func,参数为1,直接返回,不会处理。

  • 在 is_MemCheck_on 状态处理
  • MemCheck_off() 后面要释放内存,否则陷入无限循环
  • m.addr = addr; mp = lh_MEM_delete(mh, &m); 通过addr作为key value,找到hash node,然后删除,释放存储分配信息的内存
  • MemCheck_on 重新开启
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值