Two changes have been made:
authorRichard Levitte <levitte@openssl.org>
Fri, 12 Nov 1999 02:51:24 +0000 (02:51 +0000)
committerRichard Levitte <levitte@openssl.org>
Fri, 12 Nov 1999 02:51:24 +0000 (02:51 +0000)
  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
crypto/crypto.h
crypto/mem.c

index b9586ce1bbc3f4011d0788ae52ad5f000b726065..5142b6b6bf76f641398fac7d7698b91f562b2004 100644 (file)
@@ -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
        };
index f7f52dbdd4f60bdd38817d077fcfa879767cff00..3bc9b1a9afa9ec60c695530e3ceb7e0ba32a1707 100644 (file)
@@ -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 *);
index 61fc1e184ed6b4e87765f2a22989e17e1c2f5faf..5e728e38529c92ff89b770a088739217e1820c46 100644 (file)
@@ -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