在OpenSSL的功能EVP_EncryptFinal_ex内存泄漏 [英] Memory leak in OpenSSL function EVP_EncryptFinal_ex

查看:863
本文介绍了在OpenSSL的功能EVP_EncryptFinal_ex内存泄漏的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我实现了基于教程加密过程:

http://www.openssl.org/docs/crypto/EVP_EncryptInit.html #

当我运行它低谷valgring,得到了以下报告:

  == == 2371 176 1块字节仍处于负的战绩3可达6
== == 2371在0x4C2B6CD:的malloc(在/ usr / lib中/的valgrind / VG preload_memcheck-amd64- linux.so)
== == 2371通过0x56CA133:CRYPTO_malloc(以/lib/x86_64-linux-gnu/libcrypto.so.1.0.0)
== == 2371通过0x575280F:lh_new(以/lib/x86_64-linux-gnu/libcrypto.so.1.0.0)
== == 2371通过0x5754D4F:??? (在/lib/x86_64-linux-gnu/libcrypto.so.1.0.0)
== == 2371通过0x575503E:??? (在/lib/x86_64-linux-gnu/libcrypto.so.1.0.0)
== == 2371通过0x5755A1D:ERR_get_state(以/lib/x86_64-linux-gnu/libcrypto.so.1.0.0)
== == 2371通过0x5755E5E:ERR_put_error(以/lib/x86_64-linux-gnu/libcrypto.so.1.0.0)
== == 2371通过0x5757E38:EVP_DecryptFinal_ex(以/lib/x86_64-linux-gnu/libcrypto.so.1.0.0)
== == 2371通过0x46DA2A:unmangleUrl(标准::字符串常量和放大器;,标准::字符串常量和放大器;,标准::字符串常量和放大器;)(mangle_url.cpp:84)
== == 2371通过0x46621C:主(main.cpp中:348)

我下载了OpenSSL源$ C ​​$ C。里面ERR_put_error我看到ERR_get_state内存分配了,里面err_clear_data释放内存,但累积里面err_clear_data的逻辑。它得到了释放只有标志ERR_TXT_MALLOCED,我没有看到谁在升旗。

我在做什么错了?

我使用Ubuntu 12.04

  $猫的/ etc /问题
Ubuntu的LTS 12.04.2 \\ n \\升

和OpenSSL的版本是

  $ OpenSSL的版本
OpenSSL的1.0.1 2012年3月14日

我的code段mangle_url.cpp:

 的std ::字符串unmangleUrl(常量标准::字符串&放大器;键,常量标准::字符串&放大器;四,常量标准::字符串&安培; URL)
{
    标准::字符串binUrl = hexToBin(URL);
    标准::字符串资源;
    res.resize(binUrl.size()* 2);
    EVP_C​​IPHER_CTX CTX;
    EVP_C​​IPHER_CTX_init(安培; CTX);
    EVP_DecryptInit_ex(安培; CTX,EVP_bf_cbc(),NULL,(const的无符号字符*)及键[0],(const的无符号字符*)及IV [0]);
    INT LEN;
    如果(EVP_DecryptUpdate(安培;!CTX(无符号字符*)及资源[0],和放大器; LEN(const的无符号字符*)及binUrl [0],binUrl.size()))
    {
        EVP_C​​IPHER_CTX_cleanup(安培; CTX);
        抛出std :: runtime_error(无法解密URL);
    }
    INT tmpLen;
    如果(EVP_DecryptFinal_ex(安培;!CTX(无符号字符*)及资源[LEN],&安培; tmpLen))
    {
        EVP_C​​IPHER_CTX_cleanup(安培; CTX);
        抛出std :: runtime_error(无法解密URL);
    }
    LEN + = tmpLen;
    EVP_C​​IPHER_CTX_cleanup(安培; CTX);
    res.resize(LEN);    返回水库;
}

从OpenSSL的err.c中code:

...

 无效ERR_put_error(INT lib下,INT FUNC,诠释原因,为const char *文件,
         INT线)
    {
    ERR_STATE * ES;#IFDEF _OSD_POSIX
    / *在BS2000-OSD POSIX子系统,编译器生成
     *形式的路径名* POSIX(/ etc / passwd文件)。
     *这个肮脏的黑客他们去掉的东西明智的。
     * @@@我们不应该修改常量字符串,虽然。
     * /
    如果(STRNCMP(文件,* POSIX(,sizeof的(* POSIX() - 1)== 0){
        字符*结束;        / *跳过* POSIX(preFIX * /
        文件+ = sizeof的(* POSIX() - 1;
        最终=安培;文件[strlen的(文件)-1];
        如果(*年底==')')
            *结束='\\ 0';
        / *可选:仅使用路径的基本名称。 * /
        如果((完= strrchr(文件,/))!= NULL)
            文件=安培;结束[1];
    }
#万一
    ES = ERR_get_state(); < - 内存分配得到    ES-GT&;顶=(ES>顶+ 1)%ERR_NUM_ERRORS;
    如果(ES>顶== ES-GT&;底部)
        ES-GT&;底部=(ES>底部+ 1)%ERR_NUM_ERRORS;
    ES-GT&; err_flags [第ES>顶] = 0;
    ES-GT&; err_buffer [第ES>顶] = ERR_PACK(LIB,FUNC,原因);
    ES-GT&; err_file [第ES>顶] =文件;
    ES-GT&; err_line [第ES>顶] =线;
    err_clear_data(ES,ES-GT&;顶部); < - 假设被释放
    }

...

  ERR_STATE * ERR_get_state(无效)
    {
    静态ERR_STATE备用;
    ERR_STATE * RET,TMP,* TMPP = NULL;
    INT I;
    CRYPTO_THREADID TID;    err_fns_check();
    CRYPTO_THREADID_current(安培; TID);
    CRYPTO_THREADID_cpy(安培; tmp.tid,&安培; TID);
    RET = ERRFN(thread_get_item)(安培; TMP);    / * RET ==错误状态,如果为null,使一个新的* /
    如果(RET == NULL)
        {
        RET =(ERR_STATE *)OPENSSL_malloc(的sizeof(ERR_STATE)); < - 内存分配得到
        如果(RET == NULL)回报率(安培;回退);
        CRYPTO_THREADID_cpy(安培; ret-> TID,&安培; TID);
        ret->顶= 0;
        ret->底部= 0;
        对于(i = 0; I< ERR_NUM_ERRORS;我++)
            {
            ret-> err_data [我] = NULL;
            ret-> err_data_flags [I] = 0;
            }
        TMPP = ERRFN(thread_set_item)(RET);
        / *要检查是否插入失败,做一个GET。 * /
        如果(ERRFN(thread_get_item)(RET)!= RET)
            {
            ERR_STATE_free(RET); / *无法将其插入* /
            返回(安培;回退);
            }
        / *如果比赛在这个函数中发生,我们来到第二,磷酸川芎嗪
         *是我们刚刚更换了第一个。 * /
        如果(TMPP)
            ERR_STATE_free(TMPP);
        }
    返回RET;
    }

...

 的#define err_clear_data(P,I)\\
    做{\\
    如果(((对) - > err_data [I] = NULL)及&放大器; \\!
        (对) - > err_data_flags [1] - 放大器; ERR_TXT_MALLOCED)\\< - 与ERR_TXT_MALLOCED标志weired逻辑
        {\\
        OPENSSL_free((对) - > err_data [I]); \\
        (对) - > err_data [I] = NULL; \\
        } \\
    (对) - > err_data_flags [I] = 0; \\
    }而(0)


解决方案

呼叫退出程序前这些功能,你应该罚款:

  CRYPTO_cleanup_all_ex_data();
ERR_free_strings();
ERR_remove_state(0);
EVP_cleanup();

I implemented encryption procedure based on the tutorial:

http://www.openssl.org/docs/crypto/EVP_EncryptInit.html#

When I run it trough valgring and got the following report:

==2371== 176 bytes in 1 blocks are still reachable in loss record 3 of 6
==2371==    at 0x4C2B6CD: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64- linux.so)
==2371==    by 0x56CA133: CRYPTO_malloc (in /lib/x86_64-linux-gnu/libcrypto.so.1.0.0)
==2371==    by 0x575280F: lh_new (in /lib/x86_64-linux-gnu/libcrypto.so.1.0.0)
==2371==    by 0x5754D4F: ??? (in /lib/x86_64-linux-gnu/libcrypto.so.1.0.0)
==2371==    by 0x575503E: ??? (in /lib/x86_64-linux-gnu/libcrypto.so.1.0.0)
==2371==    by 0x5755A1D: ERR_get_state (in /lib/x86_64-linux-gnu/libcrypto.so.1.0.0)
==2371==    by 0x5755E5E: ERR_put_error (in /lib/x86_64-linux-gnu/libcrypto.so.1.0.0)
==2371==    by 0x5757E38: EVP_DecryptFinal_ex (in /lib/x86_64-linux-gnu/libcrypto.so.1.0.0)
==2371==    by 0x46DA2A: unmangleUrl(std::string const&, std::string const&, std::string const&) (mangle_url.cpp:84)
==2371==    by 0x46621C: main (main.cpp:348)

I downloaded source code of OpenSSL. Inside ERR_put_error I see memory allocation in ERR_get_state, and releasing memory inside err_clear_data, but accruing the logic inside err_clear_data. It got release only if flag ERR_TXT_MALLOCED, and I do not see who is raising the flag.

What I am doing wrong?

I am using Ubuntu 12.04

$ cat /etc/issue
Ubuntu 12.04.2 LTS \n \l

And OpenSSL version is

$ openssl version
OpenSSL 1.0.1 14 Mar 2012

My code snippet mangle_url.cpp:

std::string unmangleUrl(const std::string &key, const std::string &iv, const std::string &url)
{
    std::string binUrl = hexToBin(url);
    std::string res;
    res.resize(binUrl.size() * 2);
    EVP_CIPHER_CTX ctx;
    EVP_CIPHER_CTX_init(&ctx);
    EVP_DecryptInit_ex(&ctx, EVP_bf_cbc(), NULL, (const unsigned char *)&key[0], (const unsigned char *)&iv[0]);
    int len;
    if(!EVP_DecryptUpdate(&ctx, (unsigned char *)&res[0], &len, (const unsigned char *)&binUrl[0], binUrl.size()))
    {
        EVP_CIPHER_CTX_cleanup(&ctx);
        throw std::runtime_error("cannot decrypt URL");
    }
    int tmpLen;
    if(!EVP_DecryptFinal_ex(&ctx, (unsigned char *)&res[len], &tmpLen))
    {
        EVP_CIPHER_CTX_cleanup(&ctx);
        throw std::runtime_error("cannot decrypt URL");
    }
    len += tmpLen;
    EVP_CIPHER_CTX_cleanup(&ctx);
    res.resize(len);

    return res;
}

Code from OpenSSL err.c:

...

void ERR_put_error(int lib, int func, int reason, const char *file,
         int line)
    {
    ERR_STATE *es;

#ifdef _OSD_POSIX
    /* In the BS2000-OSD POSIX subsystem, the compiler generates
     * path names in the form "*POSIX(/etc/passwd)".
     * This dirty hack strips them to something sensible.
     * @@@ We shouldn't modify a const string, though.
     */
    if (strncmp(file,"*POSIX(", sizeof("*POSIX(")-1) == 0) {
        char *end;

        /* Skip the "*POSIX(" prefix */
        file += sizeof("*POSIX(")-1;
        end = &file[strlen(file)-1];
        if (*end == ')')
            *end = '\0';
        /* Optional: use the basename of the path only. */
        if ((end = strrchr(file, '/')) != NULL)
            file = &end[1];
    }
#endif
    es=ERR_get_state(); <-- memory got allocated

    es->top=(es->top+1)%ERR_NUM_ERRORS;
    if (es->top == es->bottom)
        es->bottom=(es->bottom+1)%ERR_NUM_ERRORS;
    es->err_flags[es->top]=0;
    es->err_buffer[es->top]=ERR_PACK(lib,func,reason);
    es->err_file[es->top]=file;
    es->err_line[es->top]=line;
    err_clear_data(es,es->top); <-- suppose to be released 
    }

...

ERR_STATE *ERR_get_state(void)
    {
    static ERR_STATE fallback;
    ERR_STATE *ret,tmp,*tmpp=NULL;
    int i;
    CRYPTO_THREADID tid;

    err_fns_check();
    CRYPTO_THREADID_current(&tid);
    CRYPTO_THREADID_cpy(&tmp.tid, &tid);
    ret=ERRFN(thread_get_item)(&tmp);

    /* ret == the error state, if NULL, make a new one */
    if (ret == NULL)
        {
        ret=(ERR_STATE *)OPENSSL_malloc(sizeof(ERR_STATE)); <-- memory got allocated
        if (ret == NULL) return(&fallback);
        CRYPTO_THREADID_cpy(&ret->tid, &tid);
        ret->top=0;
        ret->bottom=0;
        for (i=0; i<ERR_NUM_ERRORS; i++)
            {
            ret->err_data[i]=NULL;
            ret->err_data_flags[i]=0;
            }
        tmpp = ERRFN(thread_set_item)(ret);
        /* To check if insertion failed, do a get. */
        if (ERRFN(thread_get_item)(ret) != ret)
            {
            ERR_STATE_free(ret); /* could not insert it */
            return(&fallback);
            }
        /* If a race occured in this function and we came second, tmpp
         * is the first one that we just replaced. */
        if (tmpp)
            ERR_STATE_free(tmpp);
        }
    return ret;
    }

...

#define err_clear_data(p,i) \
    do { \
    if (((p)->err_data[i] != NULL) && \
        (p)->err_data_flags[i] & ERR_TXT_MALLOCED) \ <-- weired logic with ERR_TXT_MALLOCED flag
        {  \
        OPENSSL_free((p)->err_data[i]); \
        (p)->err_data[i]=NULL; \
        } \
    (p)->err_data_flags[i]=0; \
    } while(0)

解决方案

Call these functions before exiting your program, you should be fine:

CRYPTO_cleanup_all_ex_data();
ERR_free_strings();
ERR_remove_state(0);
EVP_cleanup();

这篇关于在OpenSSL的功能EVP_EncryptFinal_ex内存泄漏的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆