Add locking to QUIC front-end

Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/20348)
This commit is contained in:
Hugo Landau 2023-02-21 10:18:58 +00:00
parent 4847599b54
commit a8489257e6
2 changed files with 189 additions and 63 deletions

View File

@ -61,8 +61,29 @@
* mutex which then serves as the channel mutex; see QUIC_CHANNEL_ARGS.
*/
/*
* The function does not acquire the channel mutex and assumes it is already
* held by the calling thread.
*
* Any function tagged with this has the following precondition:
*
* Precondition: must hold channel mutex (unchecked)
*/
# define QUIC_NEEDS_LOCK
/*
* The function acquires the channel mutex and releases it before returning in
* all circumstances.
*
* Any function tagged with this has the following precondition and
* postcondition:
*
* Precondition: must not hold channel mutex (unchecked)
* Postcondition: channel mutex is not held (by calling thread)
*
*/
# define QUIC_TAKES_LOCK
# define QUIC_TODO_LOCK
# define QUIC_CHANNEL_STATE_IDLE 0

View File

@ -175,7 +175,7 @@ err:
}
/* SSL_free */
QUIC_TODO_LOCK
QUIC_TAKES_LOCK
void ossl_quic_free(SSL *s)
{
QUIC_CONNECTION *qc = QUIC_CONNECTION_FROM_SSL(s);
@ -184,6 +184,7 @@ void ossl_quic_free(SSL *s)
if (!expect_quic_conn(qc))
return;
quic_lock(qc); /* best effort */
ossl_quic_channel_free(qc->ch);
BIO_free(qc->net_rbio);
@ -431,13 +432,19 @@ static int blocking_mode(const QUIC_CONNECTION *qc)
}
/* SSL_tick; ticks the reactor. */
QUIC_TODO_LOCK
QUIC_TAKES_LOCK
int ossl_quic_tick(QUIC_CONNECTION *qc)
{
if (qc->ch == NULL)
if (!quic_lock(qc))
return 0;
if (qc->ch == NULL) {
quic_unlock(qc);
return 1;
}
ossl_quic_reactor_tick(ossl_quic_channel_get_reactor(qc->ch));
quic_unlock(qc);
return 1;
}
@ -447,11 +454,14 @@ int ossl_quic_tick(QUIC_CONNECTION *qc)
* the object should be ticked immediately and tv->tv_sec is set to -1 if no
* timeout is currently active.
*/
QUIC_TODO_LOCK
QUIC_TAKES_LOCK
int ossl_quic_get_tick_timeout(QUIC_CONNECTION *qc, struct timeval *tv)
{
OSSL_TIME deadline = ossl_time_infinite();
if (!quic_lock(qc))
return 0;
if (qc->ch != NULL)
deadline
= ossl_quic_reactor_get_tick_deadline(ossl_quic_channel_get_reactor(qc->ch));
@ -459,10 +469,12 @@ int ossl_quic_get_tick_timeout(QUIC_CONNECTION *qc, struct timeval *tv)
if (ossl_time_is_infinite(deadline)) {
tv->tv_sec = -1;
tv->tv_usec = 0;
quic_unlock(qc);
return 1;
}
*tv = ossl_time_to_timeval(ossl_time_subtract(deadline, ossl_time_now()));
quic_unlock(qc);
return 1;
}
@ -485,23 +497,37 @@ int ossl_quic_get_wpoll_descriptor(QUIC_CONNECTION *qc, BIO_POLL_DESCRIPTOR *des
}
/* SSL_net_read_desired */
QUIC_TODO_LOCK
QUIC_TAKES_LOCK
int ossl_quic_get_net_read_desired(QUIC_CONNECTION *qc)
{
int ret;
if (!quic_lock(qc))
return 0;
if (qc->ch == NULL)
return 0;
return ossl_quic_reactor_net_read_desired(ossl_quic_channel_get_reactor(qc->ch));
ret = ossl_quic_reactor_net_read_desired(ossl_quic_channel_get_reactor(qc->ch));
quic_unlock(qc);
return ret;
}
/* SSL_net_write_desired */
QUIC_TODO_LOCK
QUIC_TAKES_LOCK
int ossl_quic_get_net_write_desired(QUIC_CONNECTION *qc)
{
int ret;
if (!quic_lock(qc))
return 0;
if (qc->ch == NULL)
return 0;
return ossl_quic_reactor_net_write_desired(ossl_quic_channel_get_reactor(qc->ch));
ret = ossl_quic_reactor_net_write_desired(ossl_quic_channel_get_reactor(qc->ch));
quic_unlock(qc);
return ret;
}
/*
@ -526,28 +552,39 @@ static int quic_shutdown_wait(void *arg)
return qc->ch == NULL || ossl_quic_channel_is_terminated(qc->ch);
}
QUIC_TODO_LOCK
QUIC_TAKES_LOCK
int ossl_quic_conn_shutdown(QUIC_CONNECTION *qc, uint64_t flags,
const SSL_SHUTDOWN_EX_ARGS *args,
size_t args_len)
{
if (!ensure_channel(qc))
int ret;
if (!quic_lock(qc))
return -1;
if (!ensure_channel(qc)) {
quic_unlock(qc);
return -1;
}
ossl_quic_channel_local_close(qc->ch,
args != NULL ? args->quic_error_code : 0);
/* TODO(QUIC): !SSL_SHUTDOWN_FLAG_NO_STREAM_FLUSH */
if (ossl_quic_channel_is_terminated(qc->ch))
if (ossl_quic_channel_is_terminated(qc->ch)) {
quic_unlock(qc);
return 1;
}
if (blocking_mode(qc) && (flags & SSL_SHUTDOWN_FLAG_RAPID) == 0)
block_until_pred(qc, quic_shutdown_wait, qc, 0);
else
ossl_quic_reactor_tick(ossl_quic_channel_get_reactor(qc->ch));
return ossl_quic_channel_is_terminated(qc->ch);
ret = ossl_quic_channel_is_terminated(qc->ch);
quic_unlock(qc);
return ret;
}
/* SSL_ctrl */
@ -674,34 +711,44 @@ static int ensure_channel_and_start(QUIC_CONNECTION *qc)
return 1;
}
QUIC_TODO_LOCK
QUIC_TAKES_LOCK
int ossl_quic_do_handshake(QUIC_CONNECTION *qc)
{
int ret;
if (qc->ch != NULL && ossl_quic_channel_is_handshake_complete(qc->ch))
/* Handshake already completed. */
return 1;
if (!quic_lock(qc))
return -1;
if (qc->ch != NULL && ossl_quic_channel_is_term_any(qc->ch))
return QUIC_RAISE_NON_NORMAL_ERROR(qc, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL);
if (qc->ch != NULL && ossl_quic_channel_is_handshake_complete(qc->ch)) {
/* Handshake already completed. */
ret = 1;
goto out;
}
if (qc->ch != NULL && ossl_quic_channel_is_term_any(qc->ch)) {
ret = QUIC_RAISE_NON_NORMAL_ERROR(qc, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL);
goto out;
}
if (BIO_ADDR_family(&qc->init_peer_addr) == AF_UNSPEC) {
/* Peer address must have been set. */
QUIC_RAISE_NON_NORMAL_ERROR(qc, SSL_R_REMOTE_PEER_ADDRESS_NOT_SET, NULL);
return -1; /* Non-protocol error */
ret = -1; /* Non-protocol error */
goto out;
}
if (qc->as_server) {
/* TODO(QUIC): Server mode not currently supported */
QUIC_RAISE_NON_NORMAL_ERROR(qc, ERR_R_PASSED_INVALID_ARGUMENT, NULL);
return -1; /* Non-protocol error */
ret = -1;
goto out; /* Non-protocol error */
}
if (qc->net_rbio == NULL || qc->net_wbio == NULL) {
/* Need read and write BIOs. */
QUIC_RAISE_NON_NORMAL_ERROR(qc, SSL_R_BIO_NOT_SET, NULL);
return -1; /* Non-protocol error */
ret = -1;
goto out; /* Non-protocol error */
}
/*
@ -710,12 +757,15 @@ int ossl_quic_do_handshake(QUIC_CONNECTION *qc)
*/
if (!ensure_channel_and_start(qc)) {
QUIC_RAISE_NON_NORMAL_ERROR(qc, ERR_R_INTERNAL_ERROR, NULL);
return -1; /* Non-protocol error */
ret = -1;
goto out; /* Non-protocol error */
}
if (ossl_quic_channel_is_handshake_complete(qc->ch))
if (ossl_quic_channel_is_handshake_complete(qc->ch)) {
/* The handshake is now done. */
return 1;
ret = 1;
goto out;
}
if (blocking_mode(qc)) {
/* In blocking mode, wait for the handshake to complete. */
@ -726,26 +776,36 @@ int ossl_quic_do_handshake(QUIC_CONNECTION *qc)
ret = block_until_pred(qc, quic_handshake_wait, &args, 0);
if (!ossl_quic_channel_is_active(qc->ch)) {
QUIC_RAISE_NON_NORMAL_ERROR(qc, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL);
return 0; /* Shutdown before completion */
ret = 0;
goto out; /* Shutdown before completion */
} else if (ret <= 0) {
QUIC_RAISE_NON_NORMAL_ERROR(qc, ERR_R_INTERNAL_ERROR, NULL);
return -1; /* Non-protocol error */
ret = -1;
goto out; /* Non-protocol error */
}
assert(ossl_quic_channel_is_handshake_complete(qc->ch));
return 1;
ret = 1;
goto out;
} else {
/* Try to advance the reactor. */
ossl_quic_reactor_tick(ossl_quic_channel_get_reactor(qc->ch));
if (ossl_quic_channel_is_handshake_complete(qc->ch))
if (ossl_quic_channel_is_handshake_complete(qc->ch)) {
/* The handshake is now done. */
return 1;
ret = 1;
goto out;
}
/* Otherwise, indicate that the handshake isn't done yet. */
QUIC_RAISE_NORMAL_ERROR(qc, SSL_ERROR_WANT_READ);
return -1; /* Non-protocol error */
ret = -1;
goto out; /* Non-protocol error */
}
out:
quic_unlock(qc);
return ret;
}
/* SSL_connect */
@ -1044,9 +1104,10 @@ static int quic_write_nonblocking_epw(QUIC_CONNECTION *qc, const void *buf, size
return 1;
}
QUIC_TODO_LOCK
QUIC_TAKES_LOCK
int ossl_quic_write(SSL *s, const void *buf, size_t len, size_t *written)
{
int ret;
QUIC_CONNECTION *qc = QUIC_CONNECTION_FROM_SSL(s);
int partial_write = ((qc->ssl_mode & SSL_MODE_ENABLE_PARTIAL_WRITE) != 0);
@ -1055,25 +1116,38 @@ int ossl_quic_write(SSL *s, const void *buf, size_t len, size_t *written)
if (!expect_quic_conn(qc))
return 0;
if (qc->ch != NULL && ossl_quic_channel_is_term_any(qc->ch))
return QUIC_RAISE_NON_NORMAL_ERROR(qc, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL);
if (!quic_lock(qc))
return 0;
if (qc->ch != NULL && ossl_quic_channel_is_term_any(qc->ch)) {
ret = QUIC_RAISE_NON_NORMAL_ERROR(qc, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL);
goto out;
}
/*
* If we haven't finished the handshake, try to advance it.
* We don't accept writes until the handshake is completed.
*/
if (ossl_quic_do_handshake(qc) < 1)
return 0;
if (ossl_quic_do_handshake(qc) < 1) {
ret = 0;
goto out;
}
if (qc->stream0 == NULL || qc->stream0->sstream == NULL)
return QUIC_RAISE_NON_NORMAL_ERROR(qc, ERR_R_INTERNAL_ERROR, NULL);
if (qc->stream0 == NULL || qc->stream0->sstream == NULL) {
ret = QUIC_RAISE_NON_NORMAL_ERROR(qc, ERR_R_INTERNAL_ERROR, NULL);
goto out;
}
if (blocking_mode(qc))
return quic_write_blocking(qc, buf, len, written);
ret = quic_write_blocking(qc, buf, len, written);
else if (partial_write)
return quic_write_nonblocking_epw(qc, buf, len, written);
ret = quic_write_nonblocking_epw(qc, buf, len, written);
else
return quic_write_nonblocking_aon(qc, buf, len, written);
ret = quic_write_nonblocking_aon(qc, buf, len, written);
out:
quic_unlock(qc);
return ret;
}
/*
@ -1167,10 +1241,10 @@ static int quic_read_again(void *arg)
return 0; /* did not read anything, keep trying */
}
QUIC_TODO_LOCK
QUIC_TAKES_LOCK
static int quic_read(SSL *s, void *buf, size_t len, size_t *bytes_read, int peek)
{
int res;
int ret, res;
QUIC_CONNECTION *qc = QUIC_CONNECTION_FROM_SSL(s);
struct quic_read_again_args args;
@ -1179,18 +1253,29 @@ static int quic_read(SSL *s, void *buf, size_t len, size_t *bytes_read, int peek
if (!expect_quic_conn(qc))
return 0;
if (qc->ch != NULL && ossl_quic_channel_is_term_any(qc->ch))
return QUIC_RAISE_NON_NORMAL_ERROR(qc, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL);
if (!quic_lock(qc))
return 0;
if (qc->ch != NULL && ossl_quic_channel_is_term_any(qc->ch)) {
ret = QUIC_RAISE_NON_NORMAL_ERROR(qc, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL);
goto out;
}
/* If we haven't finished the handshake, try to advance it. */
if (ossl_quic_do_handshake(qc) < 1)
return 0; /* ossl_quic_do_handshake raised error here */
if (ossl_quic_do_handshake(qc) < 1) {
ret = 0; /* ossl_quic_do_handshake raised error here */
goto out;
}
if (qc->stream0 == NULL)
return QUIC_RAISE_NON_NORMAL_ERROR(qc, ERR_R_INTERNAL_ERROR, NULL);
if (qc->stream0 == NULL) {
ret = QUIC_RAISE_NON_NORMAL_ERROR(qc, ERR_R_INTERNAL_ERROR, NULL);
goto out;
}
if (!quic_read_actual(qc, qc->stream0, buf, len, bytes_read, peek))
return 0; /* quic_read_actual raised error here */
if (!quic_read_actual(qc, qc->stream0, buf, len, bytes_read, peek)) {
ret = 0; /* quic_read_actual raised error here */
goto out;
}
if (*bytes_read > 0) {
/*
@ -1198,7 +1283,7 @@ static int quic_read(SSL *s, void *buf, size_t len, size_t *bytes_read, int peek
* handling other aspects of the QUIC connection.
*/
ossl_quic_reactor_tick(ossl_quic_channel_get_reactor(qc->ch));
return 1;
ret = 1;
} else if (blocking_mode(qc)) {
/*
* We were not able to read anything immediately, so our stream
@ -1213,16 +1298,23 @@ static int quic_read(SSL *s, void *buf, size_t len, size_t *bytes_read, int peek
args.peek = peek;
res = block_until_pred(qc, quic_read_again, &args, 0);
if (res == 0)
return QUIC_RAISE_NON_NORMAL_ERROR(qc, ERR_R_INTERNAL_ERROR, NULL);
else if (res < 0)
return 0; /* quic_read_again raised error here */
if (res == 0) {
ret = QUIC_RAISE_NON_NORMAL_ERROR(qc, ERR_R_INTERNAL_ERROR, NULL);
goto out;
} else if (res < 0) {
ret = 0; /* quic_read_again raised error here */
goto out;
}
return 1;
ret = 1;
} else {
/* We did not get any bytes and are not in blocking mode. */
return QUIC_RAISE_NORMAL_ERROR(qc, SSL_ERROR_WANT_READ);
ret = QUIC_RAISE_NORMAL_ERROR(qc, SSL_ERROR_WANT_READ);
}
out:
quic_unlock(qc);
return ret;
}
int ossl_quic_read(SSL *s, void *buf, size_t len, size_t *bytes_read)
@ -1239,7 +1331,7 @@ int ossl_quic_peek(SSL *s, void *buf, size_t len, size_t *bytes_read)
* SSL_pending
* -----------
*/
QUIC_TODO_LOCK
QUIC_TAKES_LOCK
static size_t ossl_quic_pending_int(const QUIC_CONNECTION *qc)
{
size_t avail = 0;
@ -1248,13 +1340,18 @@ static size_t ossl_quic_pending_int(const QUIC_CONNECTION *qc)
if (!expect_quic_conn(qc))
return 0;
if (!quic_lock((QUIC_CONNECTION *)qc))
return 0;
if (qc->stream0 == NULL || qc->stream0->rstream == NULL)
/* Cannot raise errors here because we are const, just fail. */
return 0;
goto out;
if (!ossl_quic_rstream_available(qc->stream0->rstream, &avail, &fin))
return 0;
avail = 0;
out:
quic_unlock((QUIC_CONNECTION *)qc);
return avail;
}
@ -1274,20 +1371,28 @@ int ossl_quic_has_pending(const QUIC_CONNECTION *qc)
* SSL_stream_conclude
* -------------------
*/
QUIC_TODO_LOCK
QUIC_TAKES_LOCK
int ossl_quic_conn_stream_conclude(QUIC_CONNECTION *qc)
{
QUIC_STREAM *qs = qc->stream0;
if (qs == NULL || qs->sstream == NULL)
if (!quic_lock(qc))
return 0;
if (qs == NULL || qs->sstream == NULL) {
quic_unlock(qc);
return 0;
}
if (!ossl_quic_channel_is_active(qc->ch)
|| ossl_quic_sstream_get_final_size(qs->sstream, NULL))
|| ossl_quic_sstream_get_final_size(qs->sstream, NULL)) {
quic_unlock(qc);
return 1;
}
ossl_quic_sstream_fin(qs->sstream);
quic_post_write(qc, 1, 1);
quic_unlock(qc);
return 1;
}