2013-08-29 2 views
4

Я реализовал процедуру шифрования на основе учебника:утечка памяти в функции OpenSSL EVP_EncryptFinal_ex

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

Когда я запускаю его корыта valgring и получил следующее сообщение:

==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) 

я скачал исходный код OpenSSL. Внутри ERR_put_error я вижу распределение памяти в ERR_get_state и освобождение памяти внутри err_clear_data, но нарастает логика внутри err_clear_data. Он получил освобождение только в том случае, если флаг ERR_TXT_MALLOCED, и я не вижу, кто поднимает флаг.

Что я делаю неправильно?

Я использую Ubuntu 12.04

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

И OpenSSL версии

$ openssl version 
OpenSSL 1.0.1 14 Mar 2012 

Мой фрагмент кода 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; 
} 

Код с 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) 

ответ

7

вызова эти функции перед выходом вашей программы, вы должны быть хорошо:

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

Спасибо , это помогло. Теперь Valgrind счастлив :) – antonte

+1

Просто для будущих поколений 'ERR_remove_state()' устарел в текущей версии OpenSSL (1.0.2g), и вместо этого следует использовать 'ERR_remove_thread_state()'. –

Смежные вопросы