Add CRYPTO_get_alloc_counts.
authorRich Salz <rsalz@openssl.org>
Thu, 5 Oct 2017 01:17:58 +0000 (21:17 -0400)
committerRich Salz <rsalz@openssl.org>
Fri, 13 Oct 2017 02:04:12 +0000 (22:04 -0400)
Use atomic operations for the counters
Rename malloc_lock to memdbg_lock
Also fix some style errors in mem_dbg.c

Reviewed-by: Paul Dale <paul.dale@oracle.com>
(Merged from https://github.com/openssl/openssl/pull/4359)

crypto/mem.c
crypto/mem_dbg.c
doc/man3/OPENSSL_malloc.pod
include/internal/cryptlib.h
include/openssl/crypto.h
util/libcrypto.num

index c171ae4..c77584c 100644 (file)
@@ -31,6 +31,14 @@ static void (*free_impl)(void *, const char *, int)
     = CRYPTO_free;
 
 #ifndef OPENSSL_NO_CRYPTO_MDEBUG
+static int malloc_count;
+static int realloc_count;
+static int free_count;
+static int dummy;
+
+# define INCREMENT(x) CRYPTO_atomic_add(&x, 1, &dummy, memdbg_lock)
+# define GET(ret, val) CRYPTO_atomic_read(&val, ret, memdbg_lock)
+
 static char *md_failstring;
 static long md_count;
 static int md_fail_percent = 0;
@@ -45,6 +53,7 @@ static int shouldfail(void);
 #else
 static int call_malloc_debug = 0;
 
+# define INCREMENT(x) /* empty */
 # define FAILTEST() /* empty */
 #endif
 
@@ -86,6 +95,16 @@ void CRYPTO_get_mem_functions(
 }
 
 #ifndef OPENSSL_NO_CRYPTO_MDEBUG
+void CRYPTO_get_alloc_counts(int *mcount, int *rcount, int *fcount)
+{
+    if (mcount != NULL)
+        GET(mcount, malloc_count);
+    if (rcount != NULL)
+        GET(rcount, realloc_count);
+    if (fcount != NULL)
+        GET(fcount, free_count);
+}
+
 /*
  * Parse a "malloc failure spec" string.  This likes like a set of fields
  * separated by semicolons.  Each field has a count and an optional failure
@@ -171,6 +190,7 @@ void *CRYPTO_malloc(size_t num, const char *file, int line)
 {
     void *ret = NULL;
 
+    INCREMENT(malloc_count);
     if (malloc_impl != NULL && malloc_impl != CRYPTO_malloc)
         return malloc_impl(num, file, line);
 
@@ -207,6 +227,7 @@ void *CRYPTO_zalloc(size_t num, const char *file, int line)
 
 void *CRYPTO_realloc(void *str, size_t num, const char *file, int line)
 {
+    INCREMENT(realloc_count);
     if (realloc_impl != NULL && realloc_impl != &CRYPTO_realloc)
         return realloc_impl(str, num, file, line);
 
@@ -264,6 +285,7 @@ void *CRYPTO_clear_realloc(void *str, size_t old_len, size_t num,
 
 void CRYPTO_free(void *str, const char *file, int line)
 {
+    INCREMENT(free_count);
     if (free_impl != NULL && free_impl != &CRYPTO_free) {
         free_impl(str, file, line);
         return;
index 9228dce..b394de8 100644 (file)
@@ -56,8 +56,8 @@ struct app_mem_info_st {
 };
 
 static CRYPTO_ONCE memdbg_init = CRYPTO_ONCE_STATIC_INIT;
-static CRYPTO_RWLOCK *malloc_lock = NULL;
-static CRYPTO_RWLOCK *long_malloc_lock = NULL;
+CRYPTO_RWLOCK *memdbg_lock;
+static CRYPTO_RWLOCK *long_memdbg_lock;
 static CRYPTO_THREAD_LOCAL appinfokey;
 
 /* memory-block description */
@@ -76,28 +76,32 @@ struct mem_st {
 #endif
 };
 
-static LHASH_OF(MEM) *mh = NULL; /* hash-table of memory requests (address as
-                                  * key); access requires MALLOC2 lock */
+/*
+ * hash-table of memory requests (address as * key); access requires
+ * long_memdbg_lock lock
+ */
+static LHASH_OF(MEM) *mh = NULL;
 
 /* num_disable > 0 iff mh_mode == CRYPTO_MEM_CHECK_ON (w/o ..._ENABLE) */
 static unsigned int num_disable = 0;
 
 /*
- * Valid iff num_disable > 0.  long_malloc_lock is locked exactly in this
+ * Valid iff num_disable > 0.  long_memdbg_lock is locked exactly in this
  * case (by the thread named in disabling_thread).
  */
 static CRYPTO_THREAD_ID disabling_threadid;
 
 DEFINE_RUN_ONCE_STATIC(do_memdbg_init)
 {
-    malloc_lock = CRYPTO_THREAD_glock_new("malloc");
-    long_malloc_lock = CRYPTO_THREAD_glock_new("long_malloc");
-    if (malloc_lock == NULL || long_malloc_lock == NULL
-        || !CRYPTO_THREAD_init_local(&appinfokey, NULL)) {
-        CRYPTO_THREAD_lock_free(malloc_lock);
-        malloc_lock = NULL;
-        CRYPTO_THREAD_lock_free(long_malloc_lock);
-        long_malloc_lock = NULL;
+    memdbg_lock = CRYPTO_THREAD_glock_new("malloc");
+    long_memdbg_lock = CRYPTO_THREAD_glock_new("long_malloc");
+    if (memdbg_lock == NULL
+            || long_memdbg_lock == NULL
+            || !CRYPTO_THREAD_init_local(&appinfokey, NULL)) {
+        CRYPTO_THREAD_lock_free(memdbg_lock);
+        memdbg_lock = NULL;
+        CRYPTO_THREAD_lock_free(long_memdbg_lock);
+        long_memdbg_lock = NULL;
         return 0;
     }
     return 1;
@@ -105,7 +109,7 @@ DEFINE_RUN_ONCE_STATIC(do_memdbg_init)
 
 static void app_info_free(APP_INFO *inf)
 {
-    if (!inf)
+    if (inf == NULL)
         return;
     if (--(inf->references) <= 0) {
         app_info_free(inf->next);
@@ -124,7 +128,7 @@ int CRYPTO_mem_ctrl(int mode)
     if (!RUN_ONCE(&memdbg_init, do_memdbg_init))
         return -1;
 
-    CRYPTO_THREAD_write_lock(malloc_lock);
+    CRYPTO_THREAD_write_lock(memdbg_lock);
     switch (mode) {
     default:
         break;
@@ -143,26 +147,26 @@ int CRYPTO_mem_ctrl(int mode)
     case CRYPTO_MEM_CHECK_DISABLE:
         if (mh_mode & CRYPTO_MEM_CHECK_ON) {
             CRYPTO_THREAD_ID cur = CRYPTO_THREAD_get_current_id();
-            /* see if we don't have long_malloc_lock already */
+            /* see if we don't have long_memdbg_lock already */
             if (!num_disable
                 || !CRYPTO_THREAD_compare_id(disabling_threadid, cur)) {
                 /*
-                 * Long-time lock long_malloc_lock must not be claimed
-                 * while we're holding malloc_lock, or we'll deadlock
-                 * if somebody else holds long_malloc_lock (and cannot
+                 * Long-time lock long_memdbg_lock must not be claimed
+                 * while we're holding memdbg_lock, or we'll deadlock
+                 * if somebody else holds long_memdbg_lock (and cannot
                  * release it because we block entry to this function). Give
                  * them a chance, first, and then claim the locks in
                  * appropriate order (long-time lock first).
                  */
-                CRYPTO_THREAD_unlock(malloc_lock);
+                CRYPTO_THREAD_unlock(memdbg_lock);
                 /*
-                 * Note that after we have waited for long_malloc_lock and
-                 * malloc_lock, we'll still be in the right "case" and
+                 * Note that after we have waited for long_memdbg_lock and
+                 * memdbg_lock, we'll still be in the right "case" and
                  * "if" branch because MemCheck_start and MemCheck_stop may
                  * never be used while there are multiple OpenSSL threads.
                  */
-                CRYPTO_THREAD_write_lock(long_malloc_lock);
-                CRYPTO_THREAD_write_lock(malloc_lock);
+                CRYPTO_THREAD_write_lock(long_memdbg_lock);
+                CRYPTO_THREAD_write_lock(memdbg_lock);
                 mh_mode &= ~CRYPTO_MEM_CHECK_ENABLE;
                 disabling_threadid = cur;
             }
@@ -176,13 +180,13 @@ int CRYPTO_mem_ctrl(int mode)
                 num_disable--;
                 if (num_disable == 0) {
                     mh_mode |= CRYPTO_MEM_CHECK_ENABLE;
-                    CRYPTO_THREAD_unlock(long_malloc_lock);
+                    CRYPTO_THREAD_unlock(long_memdbg_lock);
                 }
             }
         }
         break;
     }
-    CRYPTO_THREAD_unlock(malloc_lock);
+    CRYPTO_THREAD_unlock(memdbg_lock);
     return ret;
 #endif
 }
@@ -199,12 +203,12 @@ static int mem_check_on(void)
             return 0;
 
         cur = CRYPTO_THREAD_get_current_id();
-        CRYPTO_THREAD_read_lock(malloc_lock);
+        CRYPTO_THREAD_read_lock(memdbg_lock);
 
         ret = (mh_mode & CRYPTO_MEM_CHECK_ENABLE)
             || !CRYPTO_THREAD_compare_id(disabling_threadid, cur);
 
-        CRYPTO_THREAD_unlock(malloc_lock);
+        CRYPTO_THREAD_unlock(memdbg_lock);
     }
     return ret;
 }
@@ -598,7 +602,7 @@ int CRYPTO_mem_leaks_cb(int (*cb) (const char *str, size_t len, void *u),
          */
         int old_mh_mode;
 
-        CRYPTO_THREAD_write_lock(malloc_lock);
+        CRYPTO_THREAD_write_lock(memdbg_lock);
 
         /*
          * avoid deadlock when lh_free() uses CRYPTO_mem_debug_free(), which uses
@@ -611,16 +615,16 @@ int CRYPTO_mem_leaks_cb(int (*cb) (const char *str, size_t len, void *u),
         mh = NULL;
 
         mh_mode = old_mh_mode;
-        CRYPTO_THREAD_unlock(malloc_lock);
+        CRYPTO_THREAD_unlock(memdbg_lock);
     }
     CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_OFF);
 
     /* Clean up locks etc */
     CRYPTO_THREAD_cleanup_local(&appinfokey);
-    CRYPTO_THREAD_lock_free(malloc_lock);
-    CRYPTO_THREAD_lock_free(long_malloc_lock);
-    malloc_lock = NULL;
-    long_malloc_lock = NULL;
+    CRYPTO_THREAD_lock_free(memdbg_lock);
+    CRYPTO_THREAD_lock_free(long_memdbg_lock);
+    memdbg_lock = NULL;
+    long_memdbg_lock = NULL;
 
     return ml.chunks == 0 ? 1 : 0;
 }
index 39f9047..2d48ae2 100644 (file)
@@ -14,6 +14,7 @@ OPENSSL_mem_debug_push, OPENSSL_mem_debug_pop,
 CRYPTO_mem_debug_push, CRYPTO_mem_debug_pop,
 CRYPTO_clear_realloc, CRYPTO_clear_free,
 CRYPTO_get_mem_functions, CRYPTO_set_mem_functions,
+CRYPTO_get_alloc_counts,
 CRYPTO_set_mem_debug, CRYPTO_mem_ctrl,
 CRYPTO_mem_leaks, CRYPTO_mem_leaks_fp, CRYPTO_mem_leaks_cb,
 OPENSSL_MALLOC_FAILURES,
@@ -62,6 +63,8 @@ OPENSSL_MALLOC_FD
          void *(*r)(void *, size_t, const char *, int),
          void (*f)(void *, const char *, int))
 
+ void CRYPTO_get_alloc_counts(int *m, int *r, int *f)
+
  int CRYPTO_set_mem_debug(int onoff)
 
  env OPENSSL_MALLOC_FAILURES=... <application>
@@ -148,31 +151,6 @@ CRYPTO_set_mem_debug() turns this tracking on and off.  In order to have
 any effect, is must be called before any of the allocation functions
 (e.g., CRYPTO_malloc()) are called, and is therefore normally one of the
 first lines of main() in an application.
-
-If the library is built with the C<crypto-mdebug> option, then two additional
-environment variables can be used for testing failure handling.  The variable
-B<OPENSSL_MALLOC_FAILURES> controls how often allocations should fail.
-It is a set of fields separated by semicolons, which each field is a count
-(defaulting to zero) and an optional atsign and percentage (defaulting
-to 100).  If the count is zero, then it lasts forever.  For example,
-C<100;@25> or C<100@0;0@25> means the first 100 allocations pass, then all
-other allocations (until the program exits or crashes) have a 25% chance of
-failing.
-
-If the variable B<OPENSSL_MALLOC_FD> is parsed as a positive integer, then
-it is taken as an open file descriptor, and a record of all allocations is
-written to that descriptor.  If an allocation will fail, and the platform
-supports it, then a backtrace will be written to the descriptor.  This can
-be useful because a malloc may fail but not be checked, and problems will
-only occur later.  The following example in classic shell syntax shows how
-to use this (will not work on all platforms):
-
-  OPENSSL_MALLOC_FAILURES='200;@10'
-  export OPENSSL_MALLOC_FAILURES
-  OPENSSL_MALLOC_FD=3
-  export OPENSSL_MALLOC_FD
-  ...app invocation... 3>/tmp/log$$
-
 CRYPTO_mem_ctrl() provides fine-grained control of memory leak tracking.
 To enable tracking call CRYPTO_mem_ctrl() with a B<mode> argument of
 the B<CRYPTO_MEM_CHECK_ON>.
@@ -198,6 +176,40 @@ of writing to a given BIO, the callback function is called for each
 output string with the string, length, and userdata B<u> as the callback
 parameters.
 
+If the library is built with the C<crypto-mdebug> option, then one
+function, CRYPTO_get_alloc_counts(), and two additional environment
+variables, B<OPENSSL_MALLOC_FAILURES> and B<OPENSSL_MALLOC_FD>,
+are available.
+
+The function CRYPTO_get_alloc_counts() fills in the number of times
+each of CRYPTO_malloc(), CRYPTO_realloc(), and CRYPTO_free() have been
+called, into the values pointed to by B<mcount>, B<rcount>, and B<fcount>,
+respectively.  If a pointer is NULL, then the corresponding count is not stored.
+
+The variable
+B<OPENSSL_MALLOC_FAILURES> controls how often allocations should fail.
+It is a set of fields separated by semicolons, which each field is a count
+(defaulting to zero) and an optional atsign and percentage (defaulting
+to 100).  If the count is zero, then it lasts forever.  For example,
+C<100;@25> or C<100@0;0@25> means the first 100 allocations pass, then all
+other allocations (until the program exits or crashes) have a 25% chance of
+failing.
+
+If the variable B<OPENSSL_MALLOC_FD> is parsed as a positive integer, then
+it is taken as an open file descriptor, and a record of all allocations is
+written to that descriptor.  If an allocation will fail, and the platform
+supports it, then a backtrace will be written to the descriptor.  This can
+be useful because a malloc may fail but not be checked, and problems will
+only occur later.  The following example in classic shell syntax shows how
+to use this (will not work on all platforms):
+
+  OPENSSL_MALLOC_FAILURES='200;@10'
+  export OPENSSL_MALLOC_FAILURES
+  OPENSSL_MALLOC_FD=3
+  export OPENSSL_MALLOC_FD
+  ...app invocation... 3>/tmp/log$$
+
+
 =head1 RETURN VALUES
 
 OPENSSL_malloc_init(), OPENSSL_free(), OPENSSL_clear_free()
index 5f2cb44..4280185 100644 (file)
@@ -86,6 +86,7 @@ extern int OPENSSL_NONPIC_relocated;
 void crypto_cleanup_all_ex_data_int(void);
 int openssl_init_fork_handlers(void);
 
+extern CRYPTO_RWLOCK *memdbg_lock;
 int openssl_strerror_r(int errnum, char *buf, size_t buflen);
 # if !defined(OPENSSL_NO_STDIO)
 FILE *openssl_fopen(const char *filename, const char *mode);
index 8df7f3c..5e9517d 100644 (file)
@@ -303,6 +303,7 @@ void OPENSSL_cleanse(void *ptr, size_t len);
         CRYPTO_mem_debug_pop()
 int CRYPTO_mem_debug_push(const char *info, const char *file, int line);
 int CRYPTO_mem_debug_pop(void);
+void CRYPTO_get_alloc_counts(int *mcount, int *rcount, int *fcount);
 
 /*-
  * Debugging functions (enabled by CRYPTO_set_mem_debug(1))
index 04f35e3..0a29e8c 100644 (file)
@@ -4403,3 +4403,4 @@ CRYPTO_atomic_write                     4346      1_1_1   EXIST::FUNCTION:
 EVP_PKEY_set1_engine                    4347   1_1_0g  EXIST::FUNCTION:ENGINE
 DH_new_by_nid                           4348   1_1_1   EXIST::FUNCTION:DH
 DH_get_nid                              4349   1_1_1   EXIST::FUNCTION:DH
+CRYPTO_get_alloc_counts                 4350   1_1_1   EXIST::FUNCTION:CRYPTO_MDEBUG