summaryrefslogtreecommitdiff
path: root/ext/openssl/ossl.c
diff options
context:
space:
mode:
Diffstat (limited to 'ext/openssl/ossl.c')
-rw-r--r--ext/openssl/ossl.c175
1 files changed, 140 insertions, 35 deletions
diff --git a/ext/openssl/ossl.c b/ext/openssl/ossl.c
index c22966df5a..88bb8f2101 100644
--- a/ext/openssl/ossl.c
+++ b/ext/openssl/ossl.c
@@ -414,44 +414,123 @@ ossl_fips_mode_set(VALUE self, VALUE enabled)
#endif
}
+#if defined(OSSL_DEBUG)
+#if !defined(LIBRESSL_VERSION_NUMBER) && \
+ (OPENSSL_VERSION_NUMBER >= 0x10100000 && !defined(OPENSSL_NO_CRYPTO_MDEBUG) || \
+ defined(CRYPTO_malloc_debug_init))
+/*
+ * call-seq:
+ * OpenSSL.mem_check_start -> nil
+ *
+ * Calls CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON). Starts tracking memory
+ * allocations. See also OpenSSL.print_mem_leaks.
+ *
+ * This is available only when built with a capable OpenSSL and --enable-debug
+ * configure option.
+ */
+static VALUE
+mem_check_start(VALUE self)
+{
+ CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON);
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * OpenSSL.print_mem_leaks -> true | false
+ *
+ * For debugging the Ruby/OpenSSL library. Calls CRYPTO_mem_leaks_fp(stderr).
+ * Prints detected memory leaks to standard error. This cleans the global state
+ * up thus you cannot use any methods of the library after calling this.
+ *
+ * Returns true if leaks detected, false otherwise.
+ *
+ * This is available only when built with a capable OpenSSL and --enable-debug
+ * configure option.
+ *
+ * === Example
+ * OpenSSL.mem_check_start
+ * NOT_GCED = OpenSSL::PKey::RSA.new(256)
+ *
+ * END {
+ * GC.start
+ * OpenSSL.print_mem_leaks # will print the leakage
+ * }
+ */
+static VALUE
+print_mem_leaks(VALUE self)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x10100000
+ int ret;
+#endif
+
+ BN_CTX_free(ossl_bn_ctx);
+ ossl_bn_ctx = NULL;
+
+#if OPENSSL_VERSION_NUMBER >= 0x10100000
+ ret = CRYPTO_mem_leaks_fp(stderr);
+ if (ret < 0)
+ ossl_raise(eOSSLError, "CRYPTO_mem_leaks_fp");
+ return ret ? Qfalse : Qtrue;
+#else
+ CRYPTO_mem_leaks_fp(stderr);
+ return Qnil;
+#endif
+}
+#endif
+#endif
+
#if !defined(HAVE_OPENSSL_110_THREADING_API)
/**
* Stores locks needed for OpenSSL thread safety
*/
-static rb_nativethread_lock_t *ossl_locks;
+struct CRYPTO_dynlock_value {
+ rb_nativethread_lock_t lock;
+ rb_nativethread_id_t owner;
+ size_t count;
+};
static void
-ossl_lock_unlock(int mode, rb_nativethread_lock_t *lock)
+ossl_lock_init(struct CRYPTO_dynlock_value *l)
{
- if (mode & CRYPTO_LOCK) {
- rb_nativethread_lock_lock(lock);
- } else {
- rb_nativethread_lock_unlock(lock);
- }
+ rb_nativethread_lock_initialize(&l->lock);
+ l->count = 0;
}
static void
-ossl_lock_callback(int mode, int type, const char *file, int line)
+ossl_lock_unlock(int mode, struct CRYPTO_dynlock_value *l)
{
- ossl_lock_unlock(mode, &ossl_locks[type]);
+ if (mode & CRYPTO_LOCK) {
+ /* TODO: rb_nativethread_id_t is not necessarily compared with ==. */
+ rb_nativethread_id_t tid = rb_nativethread_self();
+ if (l->count && l->owner == tid) {
+ l->count++;
+ return;
+ }
+ rb_nativethread_lock_lock(&l->lock);
+ l->owner = tid;
+ l->count = 1;
+ } else {
+ if (!--l->count)
+ rb_nativethread_lock_unlock(&l->lock);
+ }
}
-struct CRYPTO_dynlock_value {
- rb_nativethread_lock_t lock;
-};
-
static struct CRYPTO_dynlock_value *
ossl_dyn_create_callback(const char *file, int line)
{
- struct CRYPTO_dynlock_value *dynlock = (struct CRYPTO_dynlock_value *)OPENSSL_malloc((int)sizeof(struct CRYPTO_dynlock_value));
- rb_nativethread_lock_initialize(&dynlock->lock);
+ /* Do not use xmalloc() here, since it may raise NoMemoryError */
+ struct CRYPTO_dynlock_value *dynlock =
+ OPENSSL_malloc(sizeof(struct CRYPTO_dynlock_value));
+ if (dynlock)
+ ossl_lock_init(dynlock);
return dynlock;
}
static void
ossl_dyn_lock_callback(int mode, struct CRYPTO_dynlock_value *l, const char *file, int line)
{
- ossl_lock_unlock(mode, &l->lock);
+ ossl_lock_unlock(mode, l);
}
static void
@@ -475,21 +554,22 @@ static unsigned long ossl_thread_id(void)
}
#endif
+static struct CRYPTO_dynlock_value *ossl_locks;
+
+static void
+ossl_lock_callback(int mode, int type, const char *file, int line)
+{
+ ossl_lock_unlock(mode, &ossl_locks[type]);
+}
+
static void Init_ossl_locks(void)
{
int i;
int num_locks = CRYPTO_num_locks();
- if ((unsigned)num_locks >= INT_MAX / (int)sizeof(VALUE)) {
- rb_raise(rb_eRuntimeError, "CRYPTO_num_locks() is too big: %d", num_locks);
- }
- ossl_locks = (rb_nativethread_lock_t *) OPENSSL_malloc(num_locks * (int)sizeof(rb_nativethread_lock_t));
- if (!ossl_locks) {
- rb_raise(rb_eNoMemError, "CRYPTO_num_locks() is too big: %d", num_locks);
- }
- for (i = 0; i < num_locks; i++) {
- rb_nativethread_lock_initialize(&ossl_locks[i]);
- }
+ ossl_locks = ALLOC_N(struct CRYPTO_dynlock_value, num_locks);
+ for (i = 0; i < num_locks; i++)
+ ossl_lock_init(&ossl_locks[i]);
#ifdef HAVE_CRYPTO_THREADID_PTR
CRYPTO_THREADID_set_callback(ossl_threadid_func);
@@ -1114,15 +1194,40 @@ Init_openssl(void)
Init_ossl_ocsp();
Init_ossl_engine();
Init_ossl_asn1();
-}
#if defined(OSSL_DEBUG)
-/*
- * Check if all symbols are OK with 'make LDSHARED=gcc all'
- */
-int
-main(int argc, char *argv[])
-{
- return 0;
+ /*
+ * For debugging Ruby/OpenSSL. Enable only when built with --enable-debug
+ */
+#if !defined(LIBRESSL_VERSION_NUMBER) && \
+ (OPENSSL_VERSION_NUMBER >= 0x10100000 && !defined(OPENSSL_NO_CRYPTO_MDEBUG) || \
+ defined(CRYPTO_malloc_debug_init))
+ rb_define_module_function(mOSSL, "mem_check_start", mem_check_start, 0);
+ rb_define_module_function(mOSSL, "print_mem_leaks", print_mem_leaks, 0);
+
+#if defined(CRYPTO_malloc_debug_init) /* <= 1.0.2 */
+ CRYPTO_malloc_debug_init();
+#endif
+
+#if defined(V_CRYPTO_MDEBUG_ALL) /* <= 1.0.2 */
+ CRYPTO_set_mem_debug_options(V_CRYPTO_MDEBUG_ALL);
+#endif
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000 /* <= 1.0.2 */
+ {
+ int i;
+ /*
+ * See crypto/ex_data.c; call def_get_class() immediately to avoid
+ * allocations. 15 is the maximum number that is used as the class index
+ * in OpenSSL 1.0.2.
+ */
+ for (i = 0; i <= 15; i++) {
+ if (CRYPTO_get_ex_new_index(i, 0, (void *)"ossl-mdebug-dummy", 0, 0, 0) < 0)
+ rb_raise(rb_eRuntimeError, "CRYPTO_get_ex_new_index for "
+ "class index %d failed", i);
+ }
+ }
+#endif
+#endif
+#endif
}
-#endif /* OSSL_DEBUG */