MEDIUM: ssl: add a rwlock for SSL server session cache

When adding the server side support for certificate update over the CLI
we encountered a design problem with the SSL session cache which was not
locked.

Indeed, once a certificate is updated we need to flush the cache, but we
also need to ensure that the cache is not used during the update.
To prevent the use of the cache during an update, this patch introduce a
rwlock for the SSL server session cache.

In the SSL session part this patch only lock in read, even if it writes.
The reason behind this, is that in the session part, there is one cache
storage per thread so it is not a problem to write in the cache from
several threads. The problem is only when trying to write in the cache
from the CLI (which could be on any thread) when a session is trying to
access the cache. So there is a write lock in the CLI part to prevent
simultaneous access by a session and the CLI.

This patch also remove the thread_isolate attempt which is eating too
much CPU time and was not protecting from the use of a free ptr in the
session.
diff --git a/src/ssl_sock.c b/src/ssl_sock.c
index 099e7f9..db5b019 100644
--- a/src/ssl_sock.c
+++ b/src/ssl_sock.c
@@ -3984,11 +3984,16 @@
 
 	s = __objt_server(conn->target);
 
+	/* RWLOCK: only read lock the SSL cache even when writing in it because there is
+	 * one cache per thread, it only prevents to flush it from the CLI in
+	 * another thread */
+
 	if (!(s->ssl_ctx.options & SRV_SSL_O_NO_REUSE)) {
 		int len;
 		unsigned char *ptr;
 
 		len = i2d_SSL_SESSION(sess, NULL);
+		HA_RWLOCK_RDLOCK(SSL_SERVER_LOCK, &s->ssl_ctx.lock);
 		if (s->ssl_ctx.reused_sess[tid].ptr && s->ssl_ctx.reused_sess[tid].allocated_size >= len) {
 			ptr = s->ssl_ctx.reused_sess[tid].ptr;
 		} else {
@@ -4000,9 +4005,12 @@
 			s->ssl_ctx.reused_sess[tid].size = i2d_SSL_SESSION(sess,
 			    &ptr);
 		}
+		HA_RWLOCK_RDUNLOCK(SSL_SERVER_LOCK, &s->ssl_ctx.lock);
 	} else {
+		HA_RWLOCK_RDLOCK(SSL_SERVER_LOCK, &s->ssl_ctx.lock);
 		free(s->ssl_ctx.reused_sess[tid].ptr);
 		s->ssl_ctx.reused_sess[tid].ptr = NULL;
+		HA_RWLOCK_RDUNLOCK(SSL_SERVER_LOCK, &s->ssl_ctx.lock);
 	}
 
 	return 0;
@@ -5265,6 +5273,7 @@
 			goto err;
 
 		SSL_set_connect_state(ctx->ssl);
+		HA_RWLOCK_RDLOCK(SSL_SERVER_LOCK, &(__objt_server(conn->target)->ssl_ctx.lock));
 		if (__objt_server(conn->target)->ssl_ctx.reused_sess[tid].ptr) {
 			const unsigned char *ptr = __objt_server(conn->target)->ssl_ctx.reused_sess[tid].ptr;
 			SSL_SESSION *sess = d2i_SSL_SESSION(NULL, &ptr, __objt_server(conn->target)->ssl_ctx.reused_sess[tid].size);
@@ -5276,6 +5285,7 @@
 				SSL_SESSION_free(sess);
 			}
 		}
+		HA_RWLOCK_RDUNLOCK(SSL_SERVER_LOCK, &(__objt_server(conn->target)->ssl_ctx.lock));
 
 		/* leave init state and start handshake */
 		conn->flags |= CO_FL_SSL_WAIT_HS | CO_FL_WAIT_L6_CONN;
@@ -5658,9 +5668,18 @@
 	ERR_clear_error();
 
 	/* free resumed session if exists */
-	if (objt_server(conn->target) && __objt_server(conn->target)->ssl_ctx.reused_sess[tid].ptr) {
-		free(__objt_server(conn->target)->ssl_ctx.reused_sess[tid].ptr);
-		__objt_server(conn->target)->ssl_ctx.reused_sess[tid].ptr = NULL;
+	if (objt_server(conn->target)) {
+		struct server *s = __objt_server(conn->target);
+		/* RWLOCK: only rdlock the SSL cache even when writing in it because there is
+		 * one cache per thread, it only prevents to flush it from the CLI in
+		 * another thread */
+
+		HA_RWLOCK_RDLOCK(SSL_SERVER_LOCK, &s->ssl_ctx.lock);
+		if (s->ssl_ctx.reused_sess[tid].ptr) {
+			free(s->ssl_ctx.reused_sess[tid].ptr);
+			s->ssl_ctx.reused_sess[tid].ptr = NULL;
+		}
+		HA_RWLOCK_RDUNLOCK(SSL_SERVER_LOCK, &s->ssl_ctx.lock);
 	}
 
 	if (counters) {