From 1f575f1b1df1d4effa5a97cf97850097d69aee8c Mon Sep 17 00:00:00 2001 From: Richard Levitte Date: Fri, 12 Nov 1999 02:51:24 +0000 Subject: [PATCH] Two changes have been made: 1. Added code to the memory leak detecting code to give the user the possibility to add information, thereby forming a traceback. 2. Make the memory leak detecting code multithread-safe. The idea is that we're actually dealing with two separate critical sections, one containing the hash tables with the information, the other containing the current memory checking mode. Those should not be handled with the same lock, especially since their handling overlap. Hence, the added second lock. --- crypto/cryptlib.c | 3 +- crypto/crypto.h | 6 +- crypto/mem.c | 273 +++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 264 insertions(+), 18 deletions(-) diff --git a/crypto/cryptlib.c b/crypto/cryptlib.c index b9586ce1bb..5142b6b6bf 100644 --- a/crypto/cryptlib.c +++ b/crypto/cryptlib.c @@ -93,7 +93,8 @@ static const char* lock_names[CRYPTO_NUM_LOCKS] = "readdir", "RSA_blinding", "dh", -#if CRYPTO_NUM_LOCKS != 25 + "debug_malloc2", +#if CRYPTO_NUM_LOCKS != 26 # error "Inconsistency between crypto.h and cryptlib.c" #endif }; diff --git a/crypto/crypto.h b/crypto/crypto.h index f7f52dbdd4..3bc9b1a9af 100644 --- a/crypto/crypto.h +++ b/crypto/crypto.h @@ -112,7 +112,8 @@ extern "C" { #define CRYPTO_LOCK_READDIR 22 #define CRYPTO_LOCK_RSA_BLINDING 23 #define CRYPTO_LOCK_DH 24 -#define CRYPTO_NUM_LOCKS 25 +#define CRYPTO_LOCK_MALLOC2 25 +#define CRYPTO_NUM_LOCKS 26 #define CRYPTO_LOCK 1 #define CRYPTO_UNLOCK 2 @@ -302,6 +303,9 @@ void CRYPTO_free(void *); void *CRYPTO_realloc(void *addr,int num); void *CRYPTO_remalloc(void *addr,int num); +int CRYPTO_add_info(const char *file, int line, const char *info); +int CRYPTO_remove_info(); + void *CRYPTO_dbg_malloc(int num,const char *file,int line); void *CRYPTO_dbg_realloc(void *addr,int num,const char *file,int line); void CRYPTO_dbg_free(void *); diff --git a/crypto/mem.c b/crypto/mem.c index 61fc1e184e..5e728e3852 100644 --- a/crypto/mem.c +++ b/crypto/mem.c @@ -71,6 +71,7 @@ /* static int mh_mode=CRYPTO_MEM_CHECK_ON; */ /* #else */ static int mh_mode=CRYPTO_MEM_CHECK_OFF; +static unsigned long disabling_thread = 0; /* #endif */ /* State CRYPTO_MEM_CHECK_ON exists only temporarily when the library * thinks that certain allocations should not be checked (e.g. the data @@ -84,6 +85,18 @@ static int mh_mode=CRYPTO_MEM_CHECK_OFF; static unsigned long order=0; +static LHASH *amih=NULL; + +typedef struct app_mem_info_st + { + unsigned long thread; + const char *file; + int line; + const char *info; + struct app_mem_info_st *next; + int references; + } APP_INFO; + static LHASH *mh=NULL; typedef struct mem_st @@ -99,6 +112,7 @@ typedef struct mem_st #ifdef CRYPTO_MDEBUG_TIME time_t time; #endif + APP_INFO *app_info; } MEM; int CRYPTO_mem_ctrl(int mode) @@ -111,18 +125,35 @@ int CRYPTO_mem_ctrl(int mode) /* for applications: */ case CRYPTO_MEM_CHECK_ON: /* aka MemCheck_start() */ mh_mode = CRYPTO_MEM_CHECK_ON|CRYPTO_MEM_CHECK_ENABLE; + disabling_thread = 0; break; case CRYPTO_MEM_CHECK_OFF: /* aka MemCheck_stop() */ mh_mode = 0; + disabling_thread = 0; break; /* switch off temporarily (for library-internal use): */ case CRYPTO_MEM_CHECK_DISABLE: /* aka MemCheck_off() */ - mh_mode&= ~CRYPTO_MEM_CHECK_ENABLE; + if (mh_mode & CRYPTO_MEM_CHECK_ON) + { + mh_mode&= ~CRYPTO_MEM_CHECK_ENABLE; + if (disabling_thread != CRYPTO_thread_id()) + { + CRYPTO_w_lock(CRYPTO_LOCK_MALLOC2); + disabling_thread=CRYPTO_thread_id(); + } + } break; case CRYPTO_MEM_CHECK_ENABLE: /* aka MemCheck_on() */ - if (mh_mode&CRYPTO_MEM_CHECK_ON) + if (mh_mode & CRYPTO_MEM_CHECK_ON) + { mh_mode|=CRYPTO_MEM_CHECK_ENABLE; + if (disabling_thread != 0) + { + disabling_thread=0; + CRYPTO_w_unlock(CRYPTO_LOCK_MALLOC2); + } + } break; default: @@ -132,6 +163,22 @@ int CRYPTO_mem_ctrl(int mode) return(ret); } +static int is_MemCheck_On() + { + int ret = 0; + + if (mh_mode & CRYPTO_MEM_CHECK_ON) + { + CRYPTO_w_lock(CRYPTO_LOCK_MALLOC); + + ret = (mh_mode & CRYPTO_MEM_CHECK_ENABLE) + && disabling_thread != CRYPTO_thread_id(); + + CRYPTO_w_unlock(CRYPTO_LOCK_MALLOC); + } + return(ret); + } + static int mem_cmp(MEM *a, MEM *b) { return(a->addr - b->addr); @@ -147,6 +194,136 @@ static unsigned long mem_hash(MEM *a) return(ret); } +static int app_info_cmp(APP_INFO *a, APP_INFO *b) + { + return(a->thread - b->thread); + } + +static unsigned long app_info_hash(APP_INFO *a) + { + unsigned long ret; + + ret=(unsigned long)a->thread; + + ret=ret*17851+(ret>>14)*7+(ret>>4)*251; + return(ret); + } + +static APP_INFO *free_info(APP_INFO *app_info) + { + APP_INFO *next; + + if (app_info == NULL) + return NULL; + + if (--(app_info->references) > 0) + return app_info; + + app_info->references = 0; + + next = app_info->next; + app_info->next = NULL; /* Just to make sure */ + + Free(app_info); + if (next != app_info) + return free_info(next); + return NULL; + } + +static APP_INFO *remove_info() + { + APP_INFO tmp; + APP_INFO *ret = NULL; + + if (amih != NULL) + { + tmp.thread=CRYPTO_thread_id(); + if ((ret=(APP_INFO *)lh_delete(amih,(char *)&tmp)) != NULL) + { + APP_INFO *next=ret->next; +#ifdef LEVITTE_DEBUG + if (ret->thread != tmp.thread) + { + fprintf(stderr, "remove_info(): deleted info has other thread ID (%d) than the current thread (%d)!!!!\n", + ret->thread, tmp.thread); + abort(); + } +#endif + if (next != NULL) + { + lh_insert(amih,(char *)next); + } + free_info(ret); + } + } + return(ret); + } + +int CRYPTO_add_info(const char *file, int line, const char *info) + { + APP_INFO *ami, *amim; + int ret=0; + + if (is_MemCheck_On()) + { + MemCheck_off(); + + if ((ami = (APP_INFO *)Malloc(sizeof(APP_INFO))) == NULL) + { + ret=0; + goto err; + } + if (amih == NULL) + { + if ((amih=lh_new(app_info_hash,app_info_cmp)) == NULL) + { + Free(ami); + ret=0; + goto err; + } + } + + ami->thread=CRYPTO_thread_id(); + ami->file=file; + ami->line=line; + ami->info=info; + ami->references=1; + ami->next=NULL; + + if ((amim=(APP_INFO *)lh_insert(amih,(char *)ami)) != NULL) + { +#ifdef LEVITTE_DEBUG + if (ami->thread != amim->thread) + { + fprintf(stderr, "CRYPTO_add_info(): previous info has other thread ID (%d) than the current thread (%d)!!!!\n", + amim->thread, ami->thread); + abort(); + } +#endif + ami->next=amim; + } + err: + MemCheck_on(); + } + + return(ret); + } + +int CRYPTO_remove_info() + { + int ret=0; + + if (is_MemCheck_On()) + { + MemCheck_off(); + + ret=(remove_info() != NULL); + + MemCheck_on(); + } + return(ret); + } + static char *(*malloc_locked_func)()=(char *(*)())malloc; static void (*free_locked_func)()=(void (*)())free; static char *(*malloc_func)()= (char *(*)())malloc; @@ -213,11 +390,12 @@ void *CRYPTO_dbg_malloc(int num, const char *file, int line) { char *ret; MEM *m,*mm; + APP_INFO tmp,*amim; if ((ret=malloc_func(num)) == NULL) return(NULL); - if (mh_mode & CRYPTO_MEM_CHECK_ENABLE) + if (is_MemCheck_On()) { MemCheck_off(); if ((m=(MEM *)Malloc(sizeof(MEM))) == NULL) @@ -226,7 +404,6 @@ void *CRYPTO_dbg_malloc(int num, const char *file, int line) MemCheck_on(); return(NULL); } - CRYPTO_w_lock(CRYPTO_LOCK_MALLOC); if (mh == NULL) { if ((mh=lh_new(mem_hash,mem_cmp)) == NULL) @@ -254,13 +431,26 @@ void *CRYPTO_dbg_malloc(int num, const char *file, int line) #ifdef CRYPTO_MDEBUG_TIME m->time=time(NULL); #endif + + tmp.thread=CRYPTO_thread_id(); + m->app_info=NULL; + if (amih != NULL + && (amim=(APP_INFO *)lh_retrieve(amih,(char *)&tmp)) != NULL) + { + m->app_info = amim; + amim->references++; + } + if ((mm=(MEM *)lh_insert(mh,(char *)m)) != NULL) { /* Not good, but don't sweat it */ + if (mm->app_info != NULL) + { + mm->app_info->references--; + } Free(mm); } err: - CRYPTO_w_unlock(CRYPTO_LOCK_MALLOC); MemCheck_on(); } return(ret); @@ -270,15 +460,21 @@ void CRYPTO_dbg_free(void *addr) { MEM m,*mp; - if ((mh_mode & CRYPTO_MEM_CHECK_ENABLE) && (mh != NULL)) + if (is_MemCheck_On() && (mh != NULL)) { MemCheck_off(); - CRYPTO_w_lock(CRYPTO_LOCK_MALLOC); + m.addr=addr; mp=(MEM *)lh_delete(mh,(char *)&m); if (mp != NULL) + { + if (mp->app_info != NULL) + { + mp->app_info->references--; + } Free(mp); - CRYPTO_w_unlock(CRYPTO_LOCK_MALLOC); + } + MemCheck_on(); } free_func(addr); @@ -292,19 +488,20 @@ void *CRYPTO_dbg_realloc(void *addr, int num, const char *file, int line) ret=realloc_func(addr,num); if (ret == addr) return(ret); - if (mh_mode & CRYPTO_MEM_CHECK_ENABLE) + if (is_MemCheck_On()) { - MemCheck_off(); if (ret == NULL) return(NULL); + + MemCheck_off(); + m.addr=addr; - CRYPTO_w_lock(CRYPTO_LOCK_MALLOC); mp=(MEM *)lh_delete(mh,(char *)&m); if (mp != NULL) { mp->addr=ret; lh_insert(mh,(char *)mp); } - CRYPTO_w_unlock(CRYPTO_LOCK_MALLOC); + MemCheck_on(); } return(ret); @@ -335,9 +532,12 @@ typedef struct mem_leak_st static void print_leak(MEM *m, MEM_LEAK *l) { char buf[128]; + APP_INFO *amip; + int ami_cnt; #ifdef CRYPTO_MDEBUG_TIME struct tm *lcl; #endif + unsigned long ti; if(m->addr == (char *)l->bio) return; @@ -365,8 +565,49 @@ static void print_leak(MEM *m, MEM_LEAK *l) m->num,(unsigned long)m->addr); BIO_puts(l->bio,buf); + l->chunks++; l->bytes+=m->num; + + amip=m->app_info; + ami_cnt=0; + if (amip) + ti=amip->thread; + while(amip && amip->thread == ti) + { + int buf_len; + int info_len; + + ami_cnt++; + memset(buf,'>',ami_cnt); + sprintf(buf + ami_cnt, + "thread=%d, file=%s, line=%d, info=\"", + amip->thread, amip->file, amip->line); + buf_len=strlen(buf); + info_len=strlen(amip->info); + if (128 - buf_len - 3 < info_len) + { + memcpy(buf + buf_len, amip->info, 128 - buf_len - 3); + buf_len = 128 - 3; + } + else + { + strcpy(buf + buf_len, amip->info); + buf_len = strlen(buf); + } + sprintf(buf + buf_len, "\"\n"); + + BIO_puts(l->bio,buf); + + amip = amip->next; + } +#ifdef LEVITTE_DEBUG + if (amip) + { + fprintf(stderr, "Thread switch detected i backtrace!!!!\n"); + abort(); + } +#endif } void CRYPTO_mem_leaks(BIO *b) @@ -378,9 +619,9 @@ void CRYPTO_mem_leaks(BIO *b) ml.bio=b; ml.bytes=0; ml.chunks=0; - CRYPTO_w_lock(CRYPTO_LOCK_MALLOC); + CRYPTO_w_lock(CRYPTO_LOCK_MALLOC2); lh_doall_arg(mh,(void (*)())print_leak,(char *)&ml); - CRYPTO_w_unlock(CRYPTO_LOCK_MALLOC); + CRYPTO_w_unlock(CRYPTO_LOCK_MALLOC2); if (ml.chunks != 0) { sprintf(buf,"%ld bytes leaked in %d chunks\n", @@ -406,11 +647,11 @@ static void cb_leak(MEM *m, char *cb) void CRYPTO_mem_leaks_cb(void (*cb)()) { if (mh == NULL) return; - CRYPTO_w_lock(CRYPTO_LOCK_MALLOC); + CRYPTO_w_lock(CRYPTO_LOCK_MALLOC2); mem_cb=cb; lh_doall_arg(mh,(void (*)())cb_leak,(char *)mem_cb); mem_cb=NULL; - CRYPTO_w_unlock(CRYPTO_LOCK_MALLOC); + CRYPTO_w_unlock(CRYPTO_LOCK_MALLOC2); } #ifndef NO_FP_API -- 2.25.1