static void SSL_SESSION_list_remove(SSL_CTX *ctx, SSL_SESSION *s);
static void SSL_SESSION_list_add(SSL_CTX *ctx, SSL_SESSION *s);
-static int remove_session_lock(SSL_CTX *ctx, SSL_SESSION *c, int lck);
+static SSL_SESSION *remove_session_locked(SSL_CTX *ctx, SSL_SESSION *c);
DEFINE_STACK_OF(SSL_SESSION)
ssl_session_calculate_timeout(c);
}
+ /*
+ * evicted_head is a singly-linked list (via the next pointer, which
+ * SSL_SESSION_list_remove zeroes out) of sessions evicted from the cache
+ * that need their remove_session_cb called and their reference dropped
+ * once the lock is released.
+ */
+ SSL_SESSION *evicted_head = NULL;
+
if (s == NULL) {
/*
* new cache entry -- remove old ones if cache has become too large
if (SSL_CTX_sess_get_cache_size(ctx) > 0) {
while (SSL_CTX_sess_number(ctx) >= SSL_CTX_sess_get_cache_size(ctx)) {
- if (!remove_session_lock(ctx, ctx->session_cache_tail, 0))
+ SSL_SESSION *r = remove_session_locked(ctx, ctx->session_cache_tail);
+
+ if (r == NULL)
break;
- else
- ssl_tsan_counter(ctx, &ctx->stats.sess_cache_full);
+ ssl_tsan_counter(ctx, &ctx->stats.sess_cache_full);
+ r->next = evicted_head;
+ evicted_head = r;
}
}
ret = 0;
}
CRYPTO_THREAD_unlock(ctx->lock);
+
+ while (evicted_head != NULL) {
+ SSL_SESSION *next = evicted_head->next;
+
+ evicted_head->next = NULL;
+ if (ctx->remove_session_cb != NULL)
+ ctx->remove_session_cb(ctx, evicted_head);
+ SSL_SESSION_free(evicted_head);
+ evicted_head = next;
+ }
+
return ret;
}
int SSL_CTX_remove_session(SSL_CTX *ctx, SSL_SESSION *c)
{
- return remove_session_lock(ctx, c, 1);
+ SSL_SESSION *r;
+
+ if (c == NULL || c->session_id_length == 0)
+ return 0;
+ if (!CRYPTO_THREAD_write_lock(ctx->lock))
+ return 0;
+ r = remove_session_locked(ctx, c);
+ CRYPTO_THREAD_unlock(ctx->lock);
+
+ /*
+ * The callback is invoked even when the session is not in the internal
+ * cache so that external caches can be notified.
+ */
+ if (ctx->remove_session_cb != NULL)
+ ctx->remove_session_cb(ctx, c);
+ SSL_SESSION_free(r);
+ return r != NULL;
}
-static int remove_session_lock(SSL_CTX *ctx, SSL_SESSION *c, int lck)
+/*
+ * Removes c from the session cache. Caller must hold ctx->lock.
+ * Returns the removed session (caller must invoke remove_session_cb and
+ * SSL_SESSION_free), or NULL if not found.
+ */
+static SSL_SESSION *remove_session_locked(SSL_CTX *ctx, SSL_SESSION *c)
{
- SSL_SESSION *r;
- int ret = 0;
+ SSL_SESSION *r = NULL;
- if ((c != NULL) && (c->session_id_length != 0)) {
- if (lck) {
- if (!CRYPTO_THREAD_write_lock(ctx->lock))
- return 0;
- }
- if ((r = lh_SSL_SESSION_retrieve(ctx->sessions, c)) != NULL) {
- ret = 1;
+ if (c != NULL && c->session_id_length != 0) {
+ r = lh_SSL_SESSION_retrieve(ctx->sessions, c);
+ if (r != NULL) {
r = lh_SSL_SESSION_delete(ctx->sessions, r);
SSL_SESSION_list_remove(ctx, r);
}
c->not_resumable = 1;
-
- if (lck)
- CRYPTO_THREAD_unlock(ctx->lock);
-
- if (ctx->remove_session_cb != NULL)
- ctx->remove_session_cb(ctx, c);
-
- if (ret)
- SSL_SESSION_free(r);
}
- return ret;
+ return r;
}
void SSL_SESSION_free(SSL_SESSION *ss)
/*
* Iterate over the list from the back (oldest), and stop
* when a session can no longer be removed.
- * Add the session to a temporary list to be freed outside
- * the SSL_CTX lock.
- * But still do the remove_session_cb() within the lock.
+ * Collect removed sessions on a stack to be processed outside the lock,
+ * so that remove_session_cb is never invoked while holding ctx->lock.
+ * If the stack failed to create, or a push fails, free the session
+ * immediately (without invoking the callback).
*/
while (s->session_cache_tail != NULL) {
current = s->session_cache_tail;
lh_SSL_SESSION_delete(s->sessions, current);
SSL_SESSION_list_remove(s, current);
current->not_resumable = 1;
- if (s->remove_session_cb != NULL)
- s->remove_session_cb(s, current);
- /*
- * Throw the session on a stack, it's entirely plausible
- * that while freeing outside the critical section, the
- * session could be re-added, so avoid using the next/prev
- * pointers. If the stack failed to create, or the session
- * couldn't be put on the stack, just free it here
- */
if (sk == NULL || !sk_SSL_SESSION_push(sk, current))
SSL_SESSION_free(current);
} else {
lh_SSL_SESSION_set_down_load(s->sessions, i);
CRYPTO_THREAD_unlock(s->lock);
- sk_SSL_SESSION_pop_free(sk, SSL_SESSION_free);
+ while (sk_SSL_SESSION_num(sk) > 0) {
+ current = sk_SSL_SESSION_pop(sk);
+ if (s->remove_session_cb != NULL)
+ s->remove_session_cb(s, current);
+ SSL_SESSION_free(current);
+ }
+ sk_SSL_SESSION_free(sk);
}
int ssl_clear_bad_session(SSL_CONNECTION *s)