Two changes have been made:
[openssl.git] / crypto / mem.c
index 4274cc938afbc706918ca91a528d1f973f91092b..5e728e38529c92ff89b770a088739217e1820c46 100644 (file)
@@ -1,5 +1,5 @@
 /* crypto/mem.c */
-/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com)
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
  * All rights reserved.
  *
  * This package is an SSL implementation written
 
 #include <stdio.h>
 #include <stdlib.h>
-#include "buffer.h"
-#include "bio.h"
-#include "lhash.h"
+#include <openssl/crypto.h>
+#ifdef CRYPTO_MDEBUG_TIME
+# include <time.h>     
+#endif
+#include <openssl/buffer.h>
+#include <openssl/bio.h>
+#include <openssl/lhash.h>
 #include "cryptlib.h"
 
+/* #ifdef CRYPTO_MDEBUG */
+/* 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
+ * structures used for memory checking).  It is not suitable as an initial
+ * state: the library will unexpectedly enable memory checking when it
+ * executes one of those sections that want to disable checking
+ * temporarily.
+ *
+ * State CRYPTO_MEM_CHECK_ENABLE without ..._ON makes no sense whatsoever.
+ */
+
 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
        {
        char *addr;
        int num;
-       char *file;
+       const char *file;
        int line;
+#ifdef CRYPTO_MDEBUG_THREAD
+       unsigned long thread;
+#endif
        unsigned long order;
+#ifdef CRYPTO_MDEBUG_TIME
+       time_t time;
+#endif
+       APP_INFO *app_info;
        } MEM;
 
-int CRYPTO_mem_ctrl(mode)
-int mode;
+int CRYPTO_mem_ctrl(int mode)
        {
        int ret=mh_mode;
 
        CRYPTO_w_lock(CRYPTO_LOCK_MALLOC);
        switch (mode)
                {
-       case CRYPTO_MEM_CHECK_ON:
-               mh_mode|=CRYPTO_MEM_CHECK_ON;
+       /* 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;
-       case CRYPTO_MEM_CHECK_OFF:
-               mh_mode&= ~CRYPTO_MEM_CHECK_ON;
+
+       /* switch off temporarily (for library-internal use): */
+       case CRYPTO_MEM_CHECK_DISABLE: /* aka MemCheck_off() */
+               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)
+                       {
+                       mh_mode|=CRYPTO_MEM_CHECK_ENABLE;
+                       if (disabling_thread != 0)
+                               {
+                               disabling_thread=0;
+                               CRYPTO_w_unlock(CRYPTO_LOCK_MALLOC2);
+                               }
+                       }
                break;
+
        default:
                break;
                }
@@ -98,14 +163,28 @@ int mode;
        return(ret);
        }
 
-static int mem_cmp(a,b)
-MEM *a,*b;
+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);
        }
 
-static unsigned long mem_hash(a)
-MEM *a;
+static unsigned long mem_hash(MEM *a)
        {
        unsigned long ret;
 
@@ -115,76 +194,224 @@ 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;
 static char *(*realloc_func)()=        (char *(*)())realloc;
 static void (*free_func)()=    (void (*)())free;
 
-void CRYPTO_set_mem_functions(m,r,f)
-char *(*m)();
-char *(*r)();
-void (*f)();
+void CRYPTO_set_mem_functions(char *(*m)(), char *(*r)(), void (*f)())
        {
        if ((m == NULL) || (r == NULL) || (f == NULL)) return;
        malloc_func=m;
        realloc_func=r;
        free_func=f;
+       malloc_locked_func=m;
+       free_locked_func=f;
        }
 
-void CRYPTO_get_mem_functions(m,r,f)
-char *(**m)();
-char *(**r)();
-void (**f)();
+void CRYPTO_set_locked_mem_functions(char *(*m)(), void (*f)())
+       {
+       if ((m == NULL) || (f == NULL)) return;
+       malloc_locked_func=m;
+       free_locked_func=f;
+       }
+
+void CRYPTO_get_mem_functions(char *(**m)(), char *(**r)(), void (**f)())
        {
        if (m != NULL) *m=malloc_func;
        if (r != NULL) *r=realloc_func;
        if (f != NULL) *f=free_func;
        }
 
-char *CRYPTO_malloc(num)
-int num;
+void CRYPTO_get_locked_mem_functions(char *(**m)(), void (**f)())
+       {
+       if (m != NULL) *m=malloc_locked_func;
+       if (f != NULL) *f=free_locked_func;
+       }
+
+void *CRYPTO_malloc_locked(int num)
+       {
+       return(malloc_locked_func(num));
+       }
+
+void CRYPTO_free_locked(void *str)
+       {
+       free_locked_func(str);
+       }
+
+void *CRYPTO_malloc(int num)
        {
        return(malloc_func(num));
        }
 
-char *CRYPTO_realloc(str,num)
-char *str;
-int num;
+void *CRYPTO_realloc(void *str, int num)
        {
        return(realloc_func(str,num));
        }
 
-void CRYPTO_free(str)
-char *str;
+void CRYPTO_free(void *str)
        {
        free_func(str);
        }
 
-char *CRYPTO_dbg_malloc(num,file,line)
-int num;
-char *file;
-int line;
+static unsigned long break_order_num=0;
+void *CRYPTO_dbg_malloc(int num, const char *file, int line)
        {
        char *ret;
-       MEM *m;
+       MEM *m,*mm;
+       APP_INFO tmp,*amim;
 
        if ((ret=malloc_func(num)) == NULL)
                return(NULL);
 
-       if (mh_mode & CRYPTO_MEM_CHECK_ON)
+       if (is_MemCheck_On())
                {
-               if ((m=(MEM *)malloc(sizeof(MEM))) == NULL)
+               MemCheck_off();
+               if ((m=(MEM *)Malloc(sizeof(MEM))) == NULL)
                        {
-                       free(ret);
+                       Free(ret);
+                       MemCheck_on();
                        return(NULL);
                        }
-               CRYPTO_w_lock(CRYPTO_LOCK_MALLOC);
                if (mh == NULL)
                        {
                        if ((mh=lh_new(mem_hash,mem_cmp)) == NULL)
                                {
-                               free(ret);
-                               free(m);
-                               return(NULL);
+                               Free(ret);
+                               Free(m);
+                               ret=NULL;
+                               goto err;
                                }
                        }
 
@@ -192,41 +419,68 @@ int line;
                m->file=file;
                m->line=line;
                m->num=num;
+#ifdef CRYPTO_MDEBUG_THREAD
+               m->thread=CRYPTO_thread_id();
+#endif
+               if (order == break_order_num)
+                       {
+                       /* BREAK HERE */
+                       m->order=order;
+                       }
                m->order=order++;
-               if (lh_insert(mh,(char *)m) != NULL)
+#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)
                        {
-                       free(m);
-                       free(ret);
-                       /* abort(); */
-                       ret=NULL;
+                       m->app_info = amim;
+                       amim->references++;
                        }
-               CRYPTO_w_unlock(CRYPTO_LOCK_MALLOC);
+
+               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:
+               MemCheck_on();
                }
        return(ret);
        }
 
-void CRYPTO_dbg_free(addr)
-char *addr;
+void CRYPTO_dbg_free(void *addr)
        {
        MEM m,*mp;
 
-       if ((mh_mode & CRYPTO_MEM_CHECK_ON) && (mh != NULL))
+       if (is_MemCheck_On() && (mh != NULL))
                {
-               CRYPTO_w_lock(CRYPTO_LOCK_MALLOC);
+               MemCheck_off();
+
                m.addr=addr;
                mp=(MEM *)lh_delete(mh,(char *)&m);
                if (mp != NULL)
-                       free(mp);
-               CRYPTO_w_unlock(CRYPTO_LOCK_MALLOC);
+                       {
+                       if (mp->app_info != NULL)
+                               {
+                               mp->app_info->references--;
+                               }
+                       Free(mp);
+                       }
+
+               MemCheck_on();
                }
        free_func(addr);
        }
 
-char *CRYPTO_dbg_realloc(addr,num,file,line)
-char *addr;
-int num;
-char *file;
-int line;
+void *CRYPTO_dbg_realloc(void *addr, int num, const char *file, int line)
        {
        char *ret;
        MEM m,*mp;
@@ -234,36 +488,33 @@ int line;
        ret=realloc_func(addr,num);
        if (ret == addr) return(ret);
 
-       if (mh_mode & CRYPTO_MEM_CHECK_ON)
+       if (is_MemCheck_On())
                {
                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);
        }
 
-char *CRYPTO_remalloc(a,n)
-char *a;
-int n;
+void *CRYPTO_remalloc(void *a, int n)
        {
        if (a != NULL) Free(a);
        a=(char *)Malloc(n);
        return(a);
        }
 
-char *CRYPTO_dbg_remalloc(a,n,file,line)
-char *a;
-int n;
-char *file;
-int line;
+void *CRYPTO_dbg_remalloc(void *a, int n, const char *file, int line)
        {
        if (a != NULL) CRYPTO_dbg_free(a);
        a=(char *)CRYPTO_dbg_malloc(n,file,line);
@@ -278,21 +529,88 @@ typedef struct mem_leak_st
        long bytes;
        } MEM_LEAK;
 
-static void print_leak(m,l)
-MEM *m;
-MEM_LEAK *l;
+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;
+
+#ifdef CRYPTO_MDEBUG_TIME
+       lcl = localtime(&m->time);
+#endif
+
+       sprintf(buf,
+#ifdef CRYPTO_MDEBUG_TIME
+               "[%02d:%02d:%02d] "
+#endif
+               "%5lu file=%s, line=%d, "
+#ifdef CRYPTO_MDEBUG_THREAD
+               "thread=%lu, "
+#endif
+               "number=%d, address=%08lX\n",
+#ifdef CRYPTO_MDEBUG_TIME
+               lcl->tm_hour,lcl->tm_min,lcl->tm_sec,
+#endif
+               m->order,m->file,m->line,
+#ifdef CRYPTO_MDEBUG_THREAD
+               m->thread,
+#endif
+               m->num,(unsigned long)m->addr);
 
-       sprintf(buf,"%5ld file=%s, line=%d, number=%d, address=%08lX\n",
-               m->order,m->file,m->line,m->num,(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(b)
-BIO *b;
+void CRYPTO_mem_leaks(BIO *b)
        {
        MEM_LEAK ml;
        char buf[80];
@@ -301,46 +619,43 @@ 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",
                        ml.bytes,ml.chunks);
                BIO_puts(b,buf);
                }
-       /*
+
+#if 0
        lh_stats_bio(mh,b);
-        lh_node_stats_bio(mh,b);
-        lh_node_usage_stats_bio(mh,b);
-       */
+       lh_node_stats_bio(mh,b);
+       lh_node_usage_stats_bio(mh,b);
+#endif
        }
 
 static void (*mem_cb)()=NULL;
 
-static void cb_leak(m,cb)
-MEM *m;
-char *cb;
+static void cb_leak(MEM *m, char *cb)
        {
        void (*mem_callback)()=(void (*)())cb;
        mem_callback(m->order,m->file,m->line,m->num,m->addr);
        }
 
-void CRYPTO_mem_leaks_cb(cb)
-void (*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 WIN16
-void CRYPTO_mem_leaks_fp(fp)
-FILE *fp;
+#ifndef NO_FP_API
+void CRYPTO_mem_leaks_fp(FILE *fp)
        {
        BIO *b;