Commit 1d3364ac9d2bcd2d194f0afa0d301456d0d4e276

Carlos Martín Nieto 2014-06-11T20:52:15

netops: init OpenSSL once under lock The OpenSSL init functions are not reentrant, which means that running multiple fetches in parallel can cause us to crash. Use a mutex to init OpenSSL, and since we're adding this extra checks, init it only once.

diff --git a/src/global.c b/src/global.c
index 7da3185..fe41058 100644
--- a/src/global.c
+++ b/src/global.c
@@ -16,6 +16,9 @@ git_mutex git__mwindow_mutex;
 
 #define MAX_SHUTDOWN_CB 8
 
+git_mutex git__ssl_mutex;
+git_atomic git__ssl_init;
+
 static git_global_shutdown_fn git__shutdown_callbacks[MAX_SHUTDOWN_CB];
 static git_atomic git__n_shutdown_callbacks;
 static git_atomic git__n_inits;
diff --git a/src/global.h b/src/global.h
index 7782503..245f811 100644
--- a/src/global.h
+++ b/src/global.h
@@ -18,6 +18,8 @@ typedef struct {
 git_global_st *git__global_state(void);
 
 extern git_mutex git__mwindow_mutex;
+extern git_mutex git__ssl_mutex;
+extern git_atomic git__ssl_init;
 
 #define GIT_GLOBAL (git__global_state())
 
diff --git a/src/netops.c b/src/netops.c
index a0193a0..7bce159 100644
--- a/src/netops.c
+++ b/src/netops.c
@@ -33,6 +33,7 @@
 #include "posix.h"
 #include "buffer.h"
 #include "http_parser.h"
+#include "global.h"
 
 #ifdef GIT_WIN32
 static void net_set_error(const char *str)
@@ -386,12 +387,41 @@ cert_fail_name:
 	return -1;
 }
 
-static int ssl_setup(gitno_socket *socket, const char *host, int flags)
+/**
+ * The OpenSSL init functions are not reentrant so we need to init
+ * them under lock.
+ */
+static int init_ssl(void)
 {
-	int ret;
+	if (git__ssl_init.val)
+		return 0;
+
+	if (git_mutex_lock(&git__ssl_mutex) < 0) {
+		giterr_set(GITERR_OS, "failed to acquire ssl init lock");
+		return -1;
+	}
+
+	/* if we had to wait for the lock, someone else did it, we can return */
+	if (git__ssl_init.val)
+		return 0;
+
 
 	SSL_library_init();
 	SSL_load_error_strings();
+
+	git_atomic_set(&git__ssl_init, 1);
+	git_mutex_unlock(&git__ssl_mutex);
+
+	return 0;
+}
+
+static int ssl_setup(gitno_socket *socket, const char *host, int flags)
+{
+	int ret;
+
+	if (init_ssl() < 0)
+		return -1;
+
 	socket->ssl.ctx = SSL_CTX_new(SSLv23_method());
 	if (socket->ssl.ctx == NULL)
 		return ssl_set_error(&socket->ssl, 0);