2016-05-17 19:38:09 +00:00
|
|
|
/*
|
2018-02-13 12:51:29 +00:00
|
|
|
* Copyright 1995-2018 The OpenSSL Project Authors. All Rights Reserved.
|
2006-06-23 15:21:36 +00:00
|
|
|
*
|
2018-12-06 13:03:01 +00:00
|
|
|
* Licensed under the Apache License 2.0 (the "License"). You may not use
|
2016-05-17 19:38:09 +00:00
|
|
|
* this file except in compliance with the License. You can obtain a copy
|
|
|
|
* in the file LICENSE in the source distribution or at
|
|
|
|
* https://www.openssl.org/source/license.html
|
2006-06-23 15:21:36 +00:00
|
|
|
*/
|
1999-12-17 12:56:24 +00:00
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
2015-01-22 03:40:55 +00:00
|
|
|
#include <time.h>
|
2015-05-14 14:56:48 +00:00
|
|
|
#include "internal/cryptlib.h"
|
2016-07-20 01:57:23 +00:00
|
|
|
#include "internal/thread_once.h"
|
1999-12-17 12:56:24 +00:00
|
|
|
#include <openssl/crypto.h>
|
|
|
|
#include <openssl/buffer.h>
|
2016-04-14 20:28:54 +00:00
|
|
|
#include "internal/bio.h"
|
1999-12-17 12:56:24 +00:00
|
|
|
#include <openssl/lhash.h>
|
2016-02-14 11:16:52 +00:00
|
|
|
|
|
|
|
#ifndef OPENSSL_NO_CRYPTO_MDEBUG_BACKTRACE
|
2015-12-02 12:19:45 +00:00
|
|
|
# include <execinfo.h>
|
|
|
|
#endif
|
1999-12-17 12:56:24 +00:00
|
|
|
|
2015-01-22 03:40:55 +00:00
|
|
|
/*
|
|
|
|
* The state changes to CRYPTO_MEM_CHECK_ON | CRYPTO_MEM_CHECK_ENABLE when
|
|
|
|
* the application asks for it (usually after library initialisation for
|
|
|
|
* which no book-keeping is desired). 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.
|
1999-12-17 12:56:24 +00:00
|
|
|
*/
|
2016-01-10 19:42:10 +00:00
|
|
|
#ifndef OPENSSL_NO_CRYPTO_MDEBUG
|
2016-01-07 20:06:38 +00:00
|
|
|
static int mh_mode = CRYPTO_MEM_CHECK_OFF;
|
2016-01-10 19:42:10 +00:00
|
|
|
#endif
|
1999-12-17 12:56:24 +00:00
|
|
|
|
2016-01-08 02:40:52 +00:00
|
|
|
#ifndef OPENSSL_NO_CRYPTO_MDEBUG
|
1999-12-18 05:22:50 +00:00
|
|
|
static unsigned long order = 0; /* number of memory requests */
|
2008-05-26 11:24:29 +00:00
|
|
|
|
2015-01-16 09:21:50 +00:00
|
|
|
/*-
|
|
|
|
* For application-defined information (static C-string `info')
|
1999-12-18 05:22:50 +00:00
|
|
|
* to be displayed in memory leak list.
|
|
|
|
* Each thread has its own stack. For applications, there is
|
2015-12-17 04:02:47 +00:00
|
|
|
* OPENSSL_mem_debug_push("...") to push an entry,
|
|
|
|
* OPENSSL_mem_debug_pop() to pop an entry,
|
1999-12-18 05:22:50 +00:00
|
|
|
*/
|
2016-01-11 14:11:13 +00:00
|
|
|
struct app_mem_info_st {
|
2016-03-08 15:44:05 +00:00
|
|
|
CRYPTO_THREAD_ID threadid;
|
2015-01-22 03:40:55 +00:00
|
|
|
const char *file;
|
|
|
|
int line;
|
|
|
|
const char *info;
|
|
|
|
struct app_mem_info_st *next; /* tail of thread's stack */
|
|
|
|
int references;
|
2016-01-11 14:11:13 +00:00
|
|
|
};
|
1999-12-17 12:56:24 +00:00
|
|
|
|
2016-03-08 15:44:05 +00:00
|
|
|
static CRYPTO_ONCE memdbg_init = CRYPTO_ONCE_STATIC_INIT;
|
2017-10-05 01:17:58 +00:00
|
|
|
CRYPTO_RWLOCK *memdbg_lock;
|
|
|
|
static CRYPTO_RWLOCK *long_memdbg_lock;
|
2016-03-08 15:44:05 +00:00
|
|
|
static CRYPTO_THREAD_LOCAL appinfokey;
|
1999-12-17 12:56:24 +00:00
|
|
|
|
1999-12-18 05:22:50 +00:00
|
|
|
/* memory-block description */
|
2016-01-11 14:11:13 +00:00
|
|
|
struct mem_st {
|
2015-01-22 03:40:55 +00:00
|
|
|
void *addr;
|
|
|
|
int num;
|
|
|
|
const char *file;
|
|
|
|
int line;
|
2016-03-08 15:44:05 +00:00
|
|
|
CRYPTO_THREAD_ID threadid;
|
2015-01-22 03:40:55 +00:00
|
|
|
unsigned long order;
|
|
|
|
time_t time;
|
|
|
|
APP_INFO *app_info;
|
2016-02-14 11:16:52 +00:00
|
|
|
#ifndef OPENSSL_NO_CRYPTO_MDEBUG_BACKTRACE
|
2015-12-02 12:19:45 +00:00
|
|
|
void *array[30];
|
|
|
|
size_t array_siz;
|
|
|
|
#endif
|
2016-01-11 14:11:13 +00:00
|
|
|
};
|
|
|
|
|
2017-10-05 01:17:58 +00:00
|
|
|
/*
|
|
|
|
* hash-table of memory requests (address as * key); access requires
|
|
|
|
* long_memdbg_lock lock
|
|
|
|
*/
|
|
|
|
static LHASH_OF(MEM) *mh = NULL;
|
2015-01-22 03:40:55 +00:00
|
|
|
|
2016-01-07 20:06:38 +00:00
|
|
|
/* num_disable > 0 iff mh_mode == CRYPTO_MEM_CHECK_ON (w/o ..._ENABLE) */
|
|
|
|
static unsigned int num_disable = 0;
|
2006-06-23 15:21:36 +00:00
|
|
|
|
2015-01-22 03:40:55 +00:00
|
|
|
/*
|
2017-10-05 01:17:58 +00:00
|
|
|
* Valid iff num_disable > 0. long_memdbg_lock is locked exactly in this
|
2008-08-06 15:54:15 +00:00
|
|
|
* case (by the thread named in disabling_thread).
|
2008-07-03 19:59:25 +00:00
|
|
|
*/
|
2016-03-08 15:44:05 +00:00
|
|
|
static CRYPTO_THREAD_ID disabling_threadid;
|
|
|
|
|
2016-07-20 01:57:23 +00:00
|
|
|
DEFINE_RUN_ONCE_STATIC(do_memdbg_init)
|
2016-03-08 15:44:05 +00:00
|
|
|
{
|
Revert the crypto "global lock" implementation
Conceptually, this is a squashed version of:
Revert "Address feedback"
This reverts commit 75551e07bd2339dfea06ef1d31d69929e13a4495.
and
Revert "Add CRYPTO_thread_glock_new"
This reverts commit ed6b2c7938ec6f07b15745d4183afc276e74c6dd.
But there were some intervening commits that made neither revert apply
cleanly, so instead do it all as one shot.
The crypto global locks were an attempt to cope with the awkward
POSIX semantics for pthread_atfork(); its documentation (the "RATIONALE"
section) indicates that the expected usage is to have the prefork handler
lock all "global" locks, and the parent and child handlers release those
locks, to ensure that forking happens with a consistent (lock) state.
However, the set of functions available in the child process is limited
to async-signal-safe functions, and pthread_mutex_unlock() is not on
the list of async-signal-safe functions! The only synchronization
primitives that are async-signal-safe are the semaphore primitives,
which are not really appropriate for general-purpose usage.
However, the state consistency problem that the global locks were
attempting to solve is not actually a serious problem, particularly for
OpenSSL. That is, we can consider four cases of forking application
that might use OpenSSL:
(1) Single-threaded, does not call into OpenSSL in the child (e.g.,
the child calls exec() immediately)
For this class of process, no locking is needed at all, since there is
only ever a single thread of execution and the only reentrancy is due to
signal handlers (which are themselves limited to async-signal-safe
operation and should not be doing much work at all).
(2) Single-threaded, calls into OpenSSL after fork()
The application must ensure that it does not fork() with an unexpected
lock held (that is, one that would get unlocked in the parent but
accidentally remain locked in the child and cause deadlock). Since
OpenSSL does not expose any of its internal locks to the application
and the application is single-threaded, the OpenSSL internal locks
will be unlocked for the fork(), and the state will be consistent.
(OpenSSL will need to reseed its PRNG in the child, but that is
an orthogonal issue.) If the application makes use of locks from
libcrypto, proper handling for those locks is the responsibility of
the application, as for any other locking primitive that is available
for application programming.
(3) Multi-threaded, does not call into OpenSSL after fork()
As for (1), the OpenSSL state is only relevant in the parent, so
no particular fork()-related handling is needed. The internal locks
are relevant, but there is no interaction with the child to consider.
(4) Multi-threaded, calls into OpenSSL after fork()
This is the case where the pthread_atfork() hooks to ensure that all
global locks are in a known state across fork() would come into play,
per the above discussion. However, these "calls into OpenSSL after
fork()" are still subject to the restriction to async-signal-safe
functions. Since OpenSSL uses all sorts of locking and libc functions
that are not on the list of safe functions (e.g., malloc()), this
case is not currently usable and is unlikely to ever be usable,
independently of the locking situation. So, there is no need to
go through contortions to attempt to support this case in the one small
area of locking interaction with fork().
In light of the above analysis (thanks @davidben and @achernya), go
back to the simpler implementation that does not need to distinguish
"library-global" locks or to have complicated atfork handling for locks.
Reviewed-by: Kurt Roeckx <kurt@roeckx.be>
Reviewed-by: Matthias St. Pierre <Matthias.St.Pierre@ncp-e.com>
(Merged from https://github.com/openssl/openssl/pull/5089)
2018-01-16 15:49:54 +00:00
|
|
|
memdbg_lock = CRYPTO_THREAD_lock_new();
|
|
|
|
long_memdbg_lock = CRYPTO_THREAD_lock_new();
|
|
|
|
if (memdbg_lock == NULL || long_memdbg_lock == NULL
|
|
|
|
|| !CRYPTO_THREAD_init_local(&appinfokey, NULL)) {
|
2017-10-05 01:17:58 +00:00
|
|
|
CRYPTO_THREAD_lock_free(memdbg_lock);
|
|
|
|
memdbg_lock = NULL;
|
|
|
|
CRYPTO_THREAD_lock_free(long_memdbg_lock);
|
|
|
|
long_memdbg_lock = NULL;
|
2016-07-20 01:57:23 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 1;
|
2016-03-08 15:44:05 +00:00
|
|
|
}
|
1999-12-17 12:56:24 +00:00
|
|
|
|
2002-11-18 14:00:42 +00:00
|
|
|
static void app_info_free(APP_INFO *inf)
|
2015-01-22 03:40:55 +00:00
|
|
|
{
|
2017-10-05 01:17:58 +00:00
|
|
|
if (inf == NULL)
|
2015-05-01 18:37:16 +00:00
|
|
|
return;
|
2015-01-22 03:40:55 +00:00
|
|
|
if (--(inf->references) <= 0) {
|
2015-05-01 18:37:16 +00:00
|
|
|
app_info_free(inf->next);
|
2015-01-22 03:40:55 +00:00
|
|
|
OPENSSL_free(inf);
|
|
|
|
}
|
|
|
|
}
|
2016-01-07 20:06:38 +00:00
|
|
|
#endif
|
2002-11-18 14:00:42 +00:00
|
|
|
|
1999-12-17 12:56:24 +00:00
|
|
|
int CRYPTO_mem_ctrl(int mode)
|
2015-01-22 03:40:55 +00:00
|
|
|
{
|
2016-01-10 19:42:10 +00:00
|
|
|
#ifdef OPENSSL_NO_CRYPTO_MDEBUG
|
2016-01-07 20:06:38 +00:00
|
|
|
return mode - mode;
|
|
|
|
#else
|
2015-01-22 03:40:55 +00:00
|
|
|
int ret = mh_mode;
|
|
|
|
|
2016-07-19 17:42:11 +00:00
|
|
|
if (!RUN_ONCE(&memdbg_init, do_memdbg_init))
|
|
|
|
return -1;
|
2016-03-08 15:44:05 +00:00
|
|
|
|
2017-10-05 01:17:58 +00:00
|
|
|
CRYPTO_THREAD_write_lock(memdbg_lock);
|
2015-01-22 03:40:55 +00:00
|
|
|
switch (mode) {
|
2016-01-07 20:06:38 +00:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CRYPTO_MEM_CHECK_ON:
|
2015-01-22 03:40:55 +00:00
|
|
|
mh_mode = CRYPTO_MEM_CHECK_ON | CRYPTO_MEM_CHECK_ENABLE;
|
|
|
|
num_disable = 0;
|
|
|
|
break;
|
2016-01-07 20:06:38 +00:00
|
|
|
|
|
|
|
case CRYPTO_MEM_CHECK_OFF:
|
2015-01-22 03:40:55 +00:00
|
|
|
mh_mode = 0;
|
2016-01-07 20:06:38 +00:00
|
|
|
num_disable = 0;
|
2015-01-22 03:40:55 +00:00
|
|
|
break;
|
|
|
|
|
2016-01-07 20:06:38 +00:00
|
|
|
/* switch off temporarily (for library-internal use): */
|
|
|
|
case CRYPTO_MEM_CHECK_DISABLE:
|
2015-01-22 03:40:55 +00:00
|
|
|
if (mh_mode & CRYPTO_MEM_CHECK_ON) {
|
2016-03-08 15:44:05 +00:00
|
|
|
CRYPTO_THREAD_ID cur = CRYPTO_THREAD_get_current_id();
|
2017-10-05 01:17:58 +00:00
|
|
|
/* see if we don't have long_memdbg_lock already */
|
2015-01-22 03:40:55 +00:00
|
|
|
if (!num_disable
|
2016-03-08 15:44:05 +00:00
|
|
|
|| !CRYPTO_THREAD_compare_id(disabling_threadid, cur)) {
|
2015-01-22 03:40:55 +00:00
|
|
|
/*
|
2017-10-05 01:17:58 +00:00
|
|
|
* 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
|
2015-01-22 03:40:55 +00:00
|
|
|
* 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).
|
|
|
|
*/
|
2017-10-05 01:17:58 +00:00
|
|
|
CRYPTO_THREAD_unlock(memdbg_lock);
|
2015-01-22 03:40:55 +00:00
|
|
|
/*
|
2017-10-05 01:17:58 +00:00
|
|
|
* Note that after we have waited for long_memdbg_lock and
|
|
|
|
* memdbg_lock, we'll still be in the right "case" and
|
2015-01-22 03:40:55 +00:00
|
|
|
* "if" branch because MemCheck_start and MemCheck_stop may
|
|
|
|
* never be used while there are multiple OpenSSL threads.
|
|
|
|
*/
|
2017-10-05 01:17:58 +00:00
|
|
|
CRYPTO_THREAD_write_lock(long_memdbg_lock);
|
|
|
|
CRYPTO_THREAD_write_lock(memdbg_lock);
|
2015-01-22 03:40:55 +00:00
|
|
|
mh_mode &= ~CRYPTO_MEM_CHECK_ENABLE;
|
2016-03-08 15:44:05 +00:00
|
|
|
disabling_threadid = cur;
|
2015-01-22 03:40:55 +00:00
|
|
|
}
|
|
|
|
num_disable++;
|
|
|
|
}
|
|
|
|
break;
|
2016-01-07 20:06:38 +00:00
|
|
|
|
|
|
|
case CRYPTO_MEM_CHECK_ENABLE:
|
2015-01-22 03:40:55 +00:00
|
|
|
if (mh_mode & CRYPTO_MEM_CHECK_ON) {
|
|
|
|
if (num_disable) { /* always true, or something is going wrong */
|
|
|
|
num_disable--;
|
|
|
|
if (num_disable == 0) {
|
|
|
|
mh_mode |= CRYPTO_MEM_CHECK_ENABLE;
|
2017-10-05 01:17:58 +00:00
|
|
|
CRYPTO_THREAD_unlock(long_memdbg_lock);
|
2015-01-22 03:40:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2017-10-05 01:17:58 +00:00
|
|
|
CRYPTO_THREAD_unlock(memdbg_lock);
|
2017-07-06 21:29:55 +00:00
|
|
|
return ret;
|
2016-01-07 20:06:38 +00:00
|
|
|
#endif
|
2015-01-22 03:40:55 +00:00
|
|
|
}
|
1999-12-17 12:56:24 +00:00
|
|
|
|
2016-01-08 02:40:52 +00:00
|
|
|
#ifndef OPENSSL_NO_CRYPTO_MDEBUG
|
2016-01-07 20:06:38 +00:00
|
|
|
|
|
|
|
static int mem_check_on(void)
|
2015-01-22 03:40:55 +00:00
|
|
|
{
|
|
|
|
int ret = 0;
|
2016-03-08 21:58:17 +00:00
|
|
|
CRYPTO_THREAD_ID cur;
|
1999-12-17 12:56:24 +00:00
|
|
|
|
2015-01-22 03:40:55 +00:00
|
|
|
if (mh_mode & CRYPTO_MEM_CHECK_ON) {
|
2016-07-19 17:42:11 +00:00
|
|
|
if (!RUN_ONCE(&memdbg_init, do_memdbg_init))
|
|
|
|
return 0;
|
2016-03-08 15:44:05 +00:00
|
|
|
|
2016-03-08 21:58:17 +00:00
|
|
|
cur = CRYPTO_THREAD_get_current_id();
|
2017-10-05 01:17:58 +00:00
|
|
|
CRYPTO_THREAD_read_lock(memdbg_lock);
|
1999-12-17 12:56:24 +00:00
|
|
|
|
2015-01-22 03:40:55 +00:00
|
|
|
ret = (mh_mode & CRYPTO_MEM_CHECK_ENABLE)
|
2016-03-08 15:44:05 +00:00
|
|
|
|| !CRYPTO_THREAD_compare_id(disabling_threadid, cur);
|
1999-12-17 12:56:24 +00:00
|
|
|
|
2017-10-05 01:17:58 +00:00
|
|
|
CRYPTO_THREAD_unlock(memdbg_lock);
|
2015-01-22 03:40:55 +00:00
|
|
|
}
|
2017-07-06 21:29:55 +00:00
|
|
|
return ret;
|
2015-01-22 03:40:55 +00:00
|
|
|
}
|
1999-12-17 12:56:24 +00:00
|
|
|
|
2008-05-26 11:24:29 +00:00
|
|
|
static int mem_cmp(const MEM *a, const MEM *b)
|
2015-01-22 03:40:55 +00:00
|
|
|
{
|
2005-07-05 11:44:45 +00:00
|
|
|
#ifdef _WIN64
|
2015-01-22 03:40:55 +00:00
|
|
|
const char *ap = (const char *)a->addr, *bp = (const char *)b->addr;
|
|
|
|
if (ap == bp)
|
|
|
|
return 0;
|
|
|
|
else if (ap > bp)
|
|
|
|
return 1;
|
|
|
|
else
|
|
|
|
return -1;
|
2005-07-05 11:44:45 +00:00
|
|
|
#else
|
2015-01-22 03:40:55 +00:00
|
|
|
return (const char *)a->addr - (const char *)b->addr;
|
2005-07-05 11:44:45 +00:00
|
|
|
#endif
|
2015-01-22 03:40:55 +00:00
|
|
|
}
|
|
|
|
|
2008-05-26 11:24:29 +00:00
|
|
|
static unsigned long mem_hash(const MEM *a)
|
2015-01-22 03:40:55 +00:00
|
|
|
{
|
2015-09-30 08:36:21 +00:00
|
|
|
size_t ret;
|
1999-12-17 12:56:24 +00:00
|
|
|
|
2015-09-30 08:36:21 +00:00
|
|
|
ret = (size_t)a->addr;
|
2015-01-22 03:40:55 +00:00
|
|
|
|
|
|
|
ret = ret * 17851 + (ret >> 14) * 7 + (ret >> 4) * 251;
|
2017-07-06 21:29:55 +00:00
|
|
|
return ret;
|
2015-01-22 03:40:55 +00:00
|
|
|
}
|
1999-12-17 12:56:24 +00:00
|
|
|
|
2016-01-10 12:43:37 +00:00
|
|
|
/* returns 1 if there was an info to pop, 0 if the stack was empty. */
|
|
|
|
static int pop_info(void)
|
2015-01-22 03:40:55 +00:00
|
|
|
{
|
2016-01-10 12:43:37 +00:00
|
|
|
APP_INFO *current = NULL;
|
2015-01-22 03:40:55 +00:00
|
|
|
|
2016-07-19 17:42:11 +00:00
|
|
|
if (!RUN_ONCE(&memdbg_init, do_memdbg_init))
|
|
|
|
return 0;
|
|
|
|
|
2016-03-08 15:44:05 +00:00
|
|
|
current = (APP_INFO *)CRYPTO_THREAD_get_local(&appinfokey);
|
|
|
|
if (current != NULL) {
|
|
|
|
APP_INFO *next = current->next;
|
2015-01-22 03:40:55 +00:00
|
|
|
|
2016-03-08 15:44:05 +00:00
|
|
|
if (next != NULL) {
|
|
|
|
next->references++;
|
|
|
|
CRYPTO_THREAD_set_local(&appinfokey, next);
|
|
|
|
} else {
|
|
|
|
CRYPTO_THREAD_set_local(&appinfokey, NULL);
|
|
|
|
}
|
|
|
|
if (--(current->references) <= 0) {
|
|
|
|
current->next = NULL;
|
|
|
|
if (next != NULL)
|
|
|
|
next->references--;
|
|
|
|
OPENSSL_free(current);
|
2015-01-22 03:40:55 +00:00
|
|
|
}
|
2016-03-08 15:44:05 +00:00
|
|
|
return 1;
|
2015-01-22 03:40:55 +00:00
|
|
|
}
|
2016-01-10 12:43:37 +00:00
|
|
|
return 0;
|
2015-01-22 03:40:55 +00:00
|
|
|
}
|
1999-12-17 12:56:24 +00:00
|
|
|
|
2015-12-17 04:02:47 +00:00
|
|
|
int CRYPTO_mem_debug_push(const char *info, const char *file, int line)
|
2015-01-22 03:40:55 +00:00
|
|
|
{
|
|
|
|
APP_INFO *ami, *amim;
|
|
|
|
int ret = 0;
|
|
|
|
|
2016-01-07 20:06:38 +00:00
|
|
|
if (mem_check_on()) {
|
|
|
|
CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_DISABLE);
|
2015-01-22 03:40:55 +00:00
|
|
|
|
2016-07-19 17:42:11 +00:00
|
|
|
if (!RUN_ONCE(&memdbg_init, do_memdbg_init)
|
|
|
|
|| (ami = OPENSSL_malloc(sizeof(*ami))) == NULL)
|
2015-01-22 03:40:55 +00:00
|
|
|
goto err;
|
|
|
|
|
2016-03-08 15:44:05 +00:00
|
|
|
ami->threadid = CRYPTO_THREAD_get_current_id();
|
2015-01-22 03:40:55 +00:00
|
|
|
ami->file = file;
|
|
|
|
ami->line = line;
|
|
|
|
ami->info = info;
|
|
|
|
ami->references = 1;
|
|
|
|
ami->next = NULL;
|
|
|
|
|
2016-03-08 15:44:05 +00:00
|
|
|
amim = (APP_INFO *)CRYPTO_THREAD_get_local(&appinfokey);
|
|
|
|
CRYPTO_THREAD_set_local(&appinfokey, ami);
|
|
|
|
|
|
|
|
if (amim != NULL)
|
2015-01-22 03:40:55 +00:00
|
|
|
ami->next = amim;
|
2016-01-07 20:06:38 +00:00
|
|
|
ret = 1;
|
1999-12-17 12:56:24 +00:00
|
|
|
err:
|
2016-01-07 20:06:38 +00:00
|
|
|
CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ENABLE);
|
2015-01-22 03:40:55 +00:00
|
|
|
}
|
1999-12-17 12:56:24 +00:00
|
|
|
|
2017-07-06 21:29:55 +00:00
|
|
|
return ret;
|
2015-01-22 03:40:55 +00:00
|
|
|
}
|
1999-12-17 12:56:24 +00:00
|
|
|
|
2015-12-17 04:02:47 +00:00
|
|
|
int CRYPTO_mem_debug_pop(void)
|
2015-01-22 03:40:55 +00:00
|
|
|
{
|
|
|
|
int ret = 0;
|
1999-12-17 12:56:24 +00:00
|
|
|
|
2016-01-07 20:06:38 +00:00
|
|
|
if (mem_check_on()) {
|
|
|
|
CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_DISABLE);
|
2016-01-10 12:43:37 +00:00
|
|
|
ret = pop_info();
|
2016-01-07 20:06:38 +00:00
|
|
|
CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ENABLE);
|
2015-01-22 03:40:55 +00:00
|
|
|
}
|
2017-07-06 21:29:55 +00:00
|
|
|
return ret;
|
2015-01-22 03:40:55 +00:00
|
|
|
}
|
1999-12-17 12:56:24 +00:00
|
|
|
|
2015-01-22 03:40:55 +00:00
|
|
|
static unsigned long break_order_num = 0;
|
2016-01-07 20:06:38 +00:00
|
|
|
|
|
|
|
void CRYPTO_mem_debug_malloc(void *addr, size_t num, int before_p,
|
|
|
|
const char *file, int line)
|
2015-01-22 03:40:55 +00:00
|
|
|
{
|
|
|
|
MEM *m, *mm;
|
2016-03-08 15:44:05 +00:00
|
|
|
APP_INFO *amim;
|
2015-01-22 03:40:55 +00:00
|
|
|
|
|
|
|
switch (before_p & 127) {
|
|
|
|
case 0:
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
if (addr == NULL)
|
|
|
|
break;
|
|
|
|
|
2016-01-07 20:06:38 +00:00
|
|
|
if (mem_check_on()) {
|
|
|
|
CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_DISABLE);
|
2016-03-08 15:44:05 +00:00
|
|
|
|
2016-07-19 17:42:11 +00:00
|
|
|
if (!RUN_ONCE(&memdbg_init, do_memdbg_init)
|
|
|
|
|| (m = OPENSSL_malloc(sizeof(*m))) == NULL) {
|
2015-01-22 03:40:55 +00:00
|
|
|
OPENSSL_free(addr);
|
2016-01-07 20:06:38 +00:00
|
|
|
CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ENABLE);
|
2015-01-22 03:40:55 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (mh == NULL) {
|
2015-12-24 15:51:23 +00:00
|
|
|
if ((mh = lh_MEM_new(mem_hash, mem_cmp)) == NULL) {
|
2015-01-22 03:40:55 +00:00
|
|
|
OPENSSL_free(addr);
|
|
|
|
OPENSSL_free(m);
|
|
|
|
addr = NULL;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
m->addr = addr;
|
|
|
|
m->file = file;
|
|
|
|
m->line = line;
|
|
|
|
m->num = num;
|
2016-03-08 15:44:05 +00:00
|
|
|
m->threadid = CRYPTO_THREAD_get_current_id();
|
2015-01-22 03:40:55 +00:00
|
|
|
|
|
|
|
if (order == break_order_num) {
|
|
|
|
/* BREAK HERE */
|
|
|
|
m->order = order;
|
|
|
|
}
|
|
|
|
m->order = order++;
|
2016-02-14 11:16:52 +00:00
|
|
|
# ifndef OPENSSL_NO_CRYPTO_MDEBUG_BACKTRACE
|
2015-12-02 12:19:45 +00:00
|
|
|
m->array_siz = backtrace(m->array, OSSL_NELEM(m->array));
|
2016-01-07 20:06:38 +00:00
|
|
|
# endif
|
|
|
|
m->time = time(NULL);
|
2015-01-22 03:40:55 +00:00
|
|
|
|
2016-03-08 15:44:05 +00:00
|
|
|
amim = (APP_INFO *)CRYPTO_THREAD_get_local(&appinfokey);
|
|
|
|
m->app_info = amim;
|
|
|
|
if (amim != NULL)
|
2015-01-22 03:40:55 +00:00
|
|
|
amim->references++;
|
|
|
|
|
|
|
|
if ((mm = lh_MEM_insert(mh, m)) != NULL) {
|
|
|
|
/* Not good, but don't sweat it */
|
|
|
|
if (mm->app_info != NULL) {
|
|
|
|
mm->app_info->references--;
|
|
|
|
}
|
|
|
|
OPENSSL_free(mm);
|
|
|
|
}
|
|
|
|
err:
|
2016-01-07 20:06:38 +00:00
|
|
|
CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ENABLE);
|
2015-01-22 03:40:55 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
1999-12-17 12:56:24 +00:00
|
|
|
|
2016-02-17 01:24:25 +00:00
|
|
|
void CRYPTO_mem_debug_free(void *addr, int before_p,
|
|
|
|
const char *file, int line)
|
2015-01-22 03:40:55 +00:00
|
|
|
{
|
|
|
|
MEM m, *mp;
|
|
|
|
|
|
|
|
switch (before_p) {
|
|
|
|
case 0:
|
|
|
|
if (addr == NULL)
|
|
|
|
break;
|
|
|
|
|
2016-01-07 20:06:38 +00:00
|
|
|
if (mem_check_on() && (mh != NULL)) {
|
|
|
|
CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_DISABLE);
|
2015-01-22 03:40:55 +00:00
|
|
|
|
|
|
|
m.addr = addr;
|
|
|
|
mp = lh_MEM_delete(mh, &m);
|
|
|
|
if (mp != NULL) {
|
2015-05-01 18:37:16 +00:00
|
|
|
app_info_free(mp->app_info);
|
2015-01-22 03:40:55 +00:00
|
|
|
OPENSSL_free(mp);
|
|
|
|
}
|
|
|
|
|
2016-01-07 20:06:38 +00:00
|
|
|
CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ENABLE);
|
2015-01-22 03:40:55 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
1999-12-17 12:56:24 +00:00
|
|
|
|
2016-01-07 20:06:38 +00:00
|
|
|
void CRYPTO_mem_debug_realloc(void *addr1, void *addr2, size_t num,
|
|
|
|
int before_p, const char *file, int line)
|
2015-01-22 03:40:55 +00:00
|
|
|
{
|
|
|
|
MEM m, *mp;
|
1999-12-17 12:56:24 +00:00
|
|
|
|
2015-01-22 03:40:55 +00:00
|
|
|
switch (before_p) {
|
|
|
|
case 0:
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
if (addr2 == NULL)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (addr1 == NULL) {
|
2016-01-07 20:06:38 +00:00
|
|
|
CRYPTO_mem_debug_malloc(addr2, num, 128 | before_p, file, line);
|
2015-01-22 03:40:55 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2016-01-07 20:06:38 +00:00
|
|
|
if (mem_check_on()) {
|
|
|
|
CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_DISABLE);
|
2015-01-22 03:40:55 +00:00
|
|
|
|
|
|
|
m.addr = addr1;
|
|
|
|
mp = lh_MEM_delete(mh, &m);
|
|
|
|
if (mp != NULL) {
|
|
|
|
mp->addr = addr2;
|
|
|
|
mp->num = num;
|
2016-02-14 11:16:52 +00:00
|
|
|
#ifndef OPENSSL_NO_CRYPTO_MDEBUG_BACKTRACE
|
2015-12-02 12:19:45 +00:00
|
|
|
mp->array_siz = backtrace(mp->array, OSSL_NELEM(mp->array));
|
|
|
|
#endif
|
2015-01-22 03:40:55 +00:00
|
|
|
(void)lh_MEM_insert(mh, mp);
|
|
|
|
}
|
|
|
|
|
2016-01-07 20:06:38 +00:00
|
|
|
CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ENABLE);
|
2015-01-22 03:40:55 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef struct mem_leak_st {
|
2017-04-19 10:51:06 +00:00
|
|
|
int (*print_cb) (const char *str, size_t len, void *u);
|
|
|
|
void *print_cb_arg;
|
2015-01-22 03:40:55 +00:00
|
|
|
int chunks;
|
|
|
|
long bytes;
|
|
|
|
} MEM_LEAK;
|
1999-12-17 12:56:24 +00:00
|
|
|
|
2015-12-24 16:20:54 +00:00
|
|
|
static void print_leak(const MEM *m, MEM_LEAK *l)
|
2015-01-22 03:40:55 +00:00
|
|
|
{
|
|
|
|
char buf[1024];
|
|
|
|
char *bufp = buf;
|
2017-07-07 00:17:59 +00:00
|
|
|
size_t len = sizeof(buf), ami_cnt;
|
2015-01-22 03:40:55 +00:00
|
|
|
APP_INFO *amip;
|
2017-07-07 00:17:59 +00:00
|
|
|
int n;
|
2015-01-22 03:40:55 +00:00
|
|
|
struct tm *lcl = NULL;
|
2016-03-08 15:44:05 +00:00
|
|
|
/*
|
|
|
|
* Convert between CRYPTO_THREAD_ID (which could be anything at all) and
|
|
|
|
* a long. This may not be meaningful depending on what CRYPTO_THREAD_ID is
|
|
|
|
* but hopefully should give something sensible on most platforms
|
|
|
|
*/
|
|
|
|
union {
|
|
|
|
CRYPTO_THREAD_ID tid;
|
|
|
|
unsigned long ltid;
|
|
|
|
} tid;
|
|
|
|
CRYPTO_THREAD_ID ti;
|
1999-12-17 12:56:24 +00:00
|
|
|
|
2016-01-07 20:06:38 +00:00
|
|
|
lcl = localtime(&m->time);
|
2017-07-07 00:17:59 +00:00
|
|
|
n = BIO_snprintf(bufp, len, "[%02d:%02d:%02d] ",
|
|
|
|
lcl->tm_hour, lcl->tm_min, lcl->tm_sec);
|
|
|
|
if (n <= 0) {
|
|
|
|
bufp[0] = '\0';
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
bufp += n;
|
|
|
|
len -= n;
|
2015-01-22 03:40:55 +00:00
|
|
|
|
2017-07-07 00:17:59 +00:00
|
|
|
n = BIO_snprintf(bufp, len, "%5lu file=%s, line=%d, ",
|
|
|
|
m->order, m->file, m->line);
|
|
|
|
if (n <= 0)
|
|
|
|
return;
|
|
|
|
bufp += n;
|
|
|
|
len -= n;
|
2015-01-22 03:40:55 +00:00
|
|
|
|
2016-03-08 15:44:05 +00:00
|
|
|
tid.ltid = 0;
|
|
|
|
tid.tid = m->threadid;
|
2017-07-07 00:17:59 +00:00
|
|
|
n = BIO_snprintf(bufp, len, "thread=%lu, ", tid.ltid);
|
|
|
|
if (n <= 0)
|
|
|
|
return;
|
|
|
|
bufp += n;
|
|
|
|
len -= n;
|
2015-01-22 03:40:55 +00:00
|
|
|
|
2017-07-07 00:17:59 +00:00
|
|
|
n = BIO_snprintf(bufp, len, "number=%d, address=%p\n", m->num, m->addr);
|
|
|
|
if (n <= 0)
|
|
|
|
return;
|
|
|
|
bufp += n;
|
|
|
|
len -= n;
|
2015-01-22 03:40:55 +00:00
|
|
|
|
2017-07-07 00:17:59 +00:00
|
|
|
l->print_cb(buf, (size_t)(bufp - buf), l->print_cb_arg);
|
2015-01-22 03:40:55 +00:00
|
|
|
|
|
|
|
l->chunks++;
|
|
|
|
l->bytes += m->num;
|
|
|
|
|
|
|
|
amip = m->app_info;
|
|
|
|
ami_cnt = 0;
|
2016-01-07 20:06:38 +00:00
|
|
|
|
2015-12-02 12:19:45 +00:00
|
|
|
if (amip) {
|
2016-03-08 15:44:05 +00:00
|
|
|
ti = amip->threadid;
|
2015-12-02 12:19:45 +00:00
|
|
|
|
|
|
|
do {
|
|
|
|
int buf_len;
|
|
|
|
int info_len;
|
|
|
|
|
|
|
|
ami_cnt++;
|
2017-07-07 00:17:59 +00:00
|
|
|
if (ami_cnt >= sizeof(buf) - 1)
|
|
|
|
break;
|
2015-12-02 12:19:45 +00:00
|
|
|
memset(buf, '>', ami_cnt);
|
2017-07-07 00:17:59 +00:00
|
|
|
buf[ami_cnt] = '\0';
|
2016-03-08 15:44:05 +00:00
|
|
|
tid.ltid = 0;
|
|
|
|
tid.tid = amip->threadid;
|
2017-07-07 00:17:59 +00:00
|
|
|
n = BIO_snprintf(buf + ami_cnt, sizeof(buf) - ami_cnt,
|
|
|
|
" thread=%lu, file=%s, line=%d, info=\"",
|
|
|
|
tid.ltid, amip->file, amip->line);
|
|
|
|
if (n <= 0)
|
|
|
|
break;
|
|
|
|
buf_len = ami_cnt + n;
|
2015-12-02 12:19:45 +00:00
|
|
|
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 {
|
2017-07-07 00:17:59 +00:00
|
|
|
n = BIO_snprintf(buf + buf_len, sizeof(buf) - buf_len, "%s",
|
|
|
|
amip->info);
|
|
|
|
if (n < 0)
|
|
|
|
break;
|
|
|
|
buf_len += n;
|
2015-12-02 12:19:45 +00:00
|
|
|
}
|
2017-07-07 00:17:59 +00:00
|
|
|
n = BIO_snprintf(buf + buf_len, sizeof(buf) - buf_len, "\"\n");
|
|
|
|
if (n <= 0)
|
|
|
|
break;
|
2015-01-22 03:40:55 +00:00
|
|
|
|
2017-07-07 00:17:59 +00:00
|
|
|
l->print_cb(buf, buf_len + n, l->print_cb_arg);
|
2015-01-22 03:40:55 +00:00
|
|
|
|
2015-12-02 12:19:45 +00:00
|
|
|
amip = amip->next;
|
|
|
|
}
|
2016-03-08 15:44:05 +00:00
|
|
|
while (amip && CRYPTO_THREAD_compare_id(amip->threadid, ti));
|
2015-01-22 03:40:55 +00:00
|
|
|
}
|
2008-03-28 02:49:43 +00:00
|
|
|
|
2016-02-14 11:16:52 +00:00
|
|
|
#ifndef OPENSSL_NO_CRYPTO_MDEBUG_BACKTRACE
|
2015-12-02 12:19:45 +00:00
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
char **strings = backtrace_symbols(m->array, m->array_siz);
|
2016-01-07 20:06:38 +00:00
|
|
|
|
2015-12-02 12:19:45 +00:00
|
|
|
for (i = 0; i < m->array_siz; i++)
|
|
|
|
fprintf(stderr, "##> %s\n", strings[i]);
|
|
|
|
free(strings);
|
|
|
|
}
|
|
|
|
#endif
|
2015-01-22 03:40:55 +00:00
|
|
|
}
|
1999-12-17 12:56:24 +00:00
|
|
|
|
2015-12-24 16:20:54 +00:00
|
|
|
IMPLEMENT_LHASH_DOALL_ARG_CONST(MEM, MEM_LEAK);
|
2001-01-09 00:13:25 +00:00
|
|
|
|
2017-04-19 10:51:06 +00:00
|
|
|
int CRYPTO_mem_leaks_cb(int (*cb) (const char *str, size_t len, void *u),
|
|
|
|
void *u)
|
2015-01-22 03:40:55 +00:00
|
|
|
{
|
|
|
|
MEM_LEAK ml;
|
|
|
|
|
2016-02-09 22:09:56 +00:00
|
|
|
/* Ensure all resources are released */
|
2016-02-09 16:52:40 +00:00
|
|
|
OPENSSL_cleanup();
|
2016-02-09 22:09:56 +00:00
|
|
|
|
2016-07-19 17:42:11 +00:00
|
|
|
if (!RUN_ONCE(&memdbg_init, do_memdbg_init))
|
|
|
|
return -1;
|
2016-03-08 15:44:05 +00:00
|
|
|
|
2016-01-07 20:06:38 +00:00
|
|
|
CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_DISABLE);
|
2015-01-22 03:40:55 +00:00
|
|
|
|
2017-04-19 10:51:06 +00:00
|
|
|
ml.print_cb = cb;
|
|
|
|
ml.print_cb_arg = u;
|
2015-01-22 03:40:55 +00:00
|
|
|
ml.bytes = 0;
|
|
|
|
ml.chunks = 0;
|
|
|
|
if (mh != NULL)
|
2015-12-24 16:20:54 +00:00
|
|
|
lh_MEM_doall_MEM_LEAK(mh, print_leak, &ml);
|
2016-03-30 17:12:59 +00:00
|
|
|
|
2015-01-22 03:40:55 +00:00
|
|
|
if (ml.chunks != 0) {
|
2017-04-19 10:51:06 +00:00
|
|
|
char buf[256];
|
|
|
|
|
|
|
|
BIO_snprintf(buf, sizeof(buf), "%ld bytes leaked in %d chunks\n",
|
|
|
|
ml.bytes, ml.chunks);
|
|
|
|
cb(buf, strlen(buf), u);
|
2015-01-22 03:40:55 +00:00
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* Make sure that, if we found no leaks, memory-leak debugging itself
|
|
|
|
* does not introduce memory leaks (which might irritate external
|
|
|
|
* debugging tools). (When someone enables leak checking, but does not
|
2016-01-07 20:06:38 +00:00
|
|
|
* call this function, we declare it to be their fault.)
|
2015-01-22 03:40:55 +00:00
|
|
|
*/
|
|
|
|
int old_mh_mode;
|
|
|
|
|
2017-10-05 01:17:58 +00:00
|
|
|
CRYPTO_THREAD_write_lock(memdbg_lock);
|
2015-01-22 03:40:55 +00:00
|
|
|
|
|
|
|
/*
|
2016-01-07 20:06:38 +00:00
|
|
|
* avoid deadlock when lh_free() uses CRYPTO_mem_debug_free(), which uses
|
|
|
|
* mem_check_on
|
2015-01-22 03:40:55 +00:00
|
|
|
*/
|
|
|
|
old_mh_mode = mh_mode;
|
|
|
|
mh_mode = CRYPTO_MEM_CHECK_OFF;
|
|
|
|
|
2015-05-01 18:37:16 +00:00
|
|
|
lh_MEM_free(mh);
|
|
|
|
mh = NULL;
|
2015-01-22 03:40:55 +00:00
|
|
|
|
|
|
|
mh_mode = old_mh_mode;
|
2017-10-05 01:17:58 +00:00
|
|
|
CRYPTO_THREAD_unlock(memdbg_lock);
|
2015-01-22 03:40:55 +00:00
|
|
|
}
|
2016-03-08 15:44:05 +00:00
|
|
|
CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_OFF);
|
|
|
|
|
|
|
|
/* Clean up locks etc */
|
|
|
|
CRYPTO_THREAD_cleanup_local(&appinfokey);
|
2017-10-05 01:17:58 +00:00
|
|
|
CRYPTO_THREAD_lock_free(memdbg_lock);
|
|
|
|
CRYPTO_THREAD_lock_free(long_memdbg_lock);
|
|
|
|
memdbg_lock = NULL;
|
|
|
|
long_memdbg_lock = NULL;
|
2016-03-08 15:44:05 +00:00
|
|
|
|
2016-01-10 23:25:07 +00:00
|
|
|
return ml.chunks == 0 ? 1 : 0;
|
2015-01-22 03:40:55 +00:00
|
|
|
}
|
1999-12-17 12:56:24 +00:00
|
|
|
|
2017-04-19 10:51:06 +00:00
|
|
|
static int print_bio(const char *str, size_t len, void *b)
|
|
|
|
{
|
|
|
|
return BIO_write((BIO *)b, str, len);
|
|
|
|
}
|
|
|
|
|
|
|
|
int CRYPTO_mem_leaks(BIO *b)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* OPENSSL_cleanup() will free the ex_data locks so we can't have any
|
|
|
|
* ex_data hanging around
|
|
|
|
*/
|
|
|
|
bio_free_ex_data(b);
|
|
|
|
|
|
|
|
return CRYPTO_mem_leaks_cb(print_bio, b);
|
|
|
|
}
|
|
|
|
|
2016-01-07 20:06:38 +00:00
|
|
|
# ifndef OPENSSL_NO_STDIO
|
2016-01-10 23:25:07 +00:00
|
|
|
int CRYPTO_mem_leaks_fp(FILE *fp)
|
2015-01-22 03:40:55 +00:00
|
|
|
{
|
|
|
|
BIO *b;
|
2016-01-10 23:25:07 +00:00
|
|
|
int ret;
|
2015-01-22 03:40:55 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Need to turn off memory checking when allocated BIOs ... especially as
|
|
|
|
* we're creating them at a time when we're trying to check we've not
|
|
|
|
* left anything un-free()'d!!
|
|
|
|
*/
|
2016-01-07 20:06:38 +00:00
|
|
|
CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_DISABLE);
|
2015-01-22 03:40:55 +00:00
|
|
|
b = BIO_new(BIO_s_file());
|
2016-01-07 20:06:38 +00:00
|
|
|
CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ENABLE);
|
2015-10-30 11:12:26 +00:00
|
|
|
if (b == NULL)
|
2016-01-10 23:25:07 +00:00
|
|
|
return -1;
|
2015-01-22 03:40:55 +00:00
|
|
|
BIO_set_fp(b, fp, BIO_NOCLOSE);
|
2017-04-19 10:51:06 +00:00
|
|
|
ret = CRYPTO_mem_leaks_cb(print_bio, b);
|
2015-01-22 03:40:55 +00:00
|
|
|
BIO_free(b);
|
2016-01-10 23:25:07 +00:00
|
|
|
return ret;
|
2015-01-22 03:40:55 +00:00
|
|
|
}
|
2016-01-07 20:06:38 +00:00
|
|
|
# endif
|
2000-05-02 13:36:50 +00:00
|
|
|
|
2016-01-07 20:06:38 +00:00
|
|
|
#endif
|