From: Hugo Landau Date: Thu, 9 Nov 2023 10:27:14 +0000 (+0000) Subject: QUIC PORT: Allow errors to be tracked at port level X-Git-Tag: openssl-3.3.0-alpha1~408 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4df4add22d8acf09cdd8bd58614a9dc69284a1bb;p=thirdparty%2Fopenssl.git QUIC PORT: Allow errors to be tracked at port level Reviewed-by: Tomas Mraz Reviewed-by: Matt Caswell (Merged from https://github.com/openssl/openssl/pull/22674) --- diff --git a/include/internal/quic_port.h b/include/internal/quic_port.h index ea97b933921..be08e213ee4 100644 --- a/include/internal/quic_port.h +++ b/include/internal/quic_port.h @@ -133,6 +133,12 @@ void ossl_quic_port_set_inhibit_tick(QUIC_PORT *port, int inhibit); /* Returns 1 if the port is running/healthy, 0 if it has failed. */ int ossl_quic_port_is_running(const QUIC_PORT *port); +/* + * Restores port-level error to the error stack. To be called only if + * the port is no longer running. + */ +void ossl_quic_port_restore_err_state(const QUIC_PORT *port); + /* * Events * ====== @@ -140,9 +146,11 @@ int ossl_quic_port_is_running(const QUIC_PORT *port); /* * Called if a permanent network error occurs. Terminates all channels - * immediately. + * immediately. triggering_ch is an optional argument designating + * a channel which encountered the network error. */ -void ossl_quic_port_raise_net_error(QUIC_PORT *port); +void ossl_quic_port_raise_net_error(QUIC_PORT *port, + QUIC_CHANNEL *triggering_ch); # endif diff --git a/ssl/quic/quic_channel.c b/ssl/quic/quic_channel.c index aed911ad9fd..5bcd66c9e45 100644 --- a/ssl/quic/quic_channel.c +++ b/ssl/quic/quic_channel.c @@ -2310,7 +2310,7 @@ static int ch_tx(QUIC_CHANNEL *ch) case QTX_FLUSH_NET_RES_PERMANENT_FAIL: default: /* Permanent underlying network BIO, start terminating. */ - ossl_quic_port_raise_net_error(ch->port); + ossl_quic_port_raise_net_error(ch->port, ch); break; } @@ -2927,13 +2927,14 @@ void ossl_quic_channel_raise_net_error(QUIC_CHANNEL *ch) { QUIC_TERMINATE_CAUSE tcause = {0}; - ch->net_error = 1; + if (ch->net_error) + return; - ERR_raise_data(ERR_LIB_SSL, SSL_R_QUIC_NETWORK_ERROR, - "connection terminated due to network error"); - ch_save_err_state(ch); + ch->net_error = 1; tcause.error_code = QUIC_ERR_INTERNAL_ERROR; + tcause.reason = "network BIO I/O error"; + tcause.reason_len = strlen(tcause.reason); /* * Skip Terminating state and go directly to Terminated, no point trying to @@ -2952,7 +2953,10 @@ void ossl_quic_channel_restore_err_state(QUIC_CHANNEL *ch) if (ch == NULL) return; - OSSL_ERR_STATE_restore(ch->err_state); + if (!ossl_quic_port_is_running(ch->port)) + ossl_quic_port_restore_err_state(ch->port); + else + OSSL_ERR_STATE_restore(ch->err_state); } void ossl_quic_channel_raise_protocol_error_loc(QUIC_CHANNEL *ch, diff --git a/ssl/quic/quic_port.c b/ssl/quic/quic_port.c index db1a352adff..e6dba46bf4a 100644 --- a/ssl/quic/quic_port.c +++ b/ssl/quic/quic_port.c @@ -70,6 +70,9 @@ static int port_init(QUIC_PORT *port) if (port->channel_ctx == NULL) goto err; + if ((port->err_state = OSSL_ERR_STATE_new()) == NULL) + goto err; + if ((port->demux = ossl_quic_demux_new(/*BIO=*/NULL, /*Short CID Len=*/rx_short_dcid_len, get_time, port)) == NULL) @@ -108,6 +111,9 @@ static void port_cleanup(QUIC_PORT *port) ossl_quic_lcidm_free(port->lcidm); port->lcidm = NULL; + + OSSL_ERR_STATE_free(port->err_state); + port->err_state = NULL; } static void port_transition_failed(QUIC_PORT *port) @@ -341,7 +347,8 @@ static void port_tick(QUIC_TICK_RESULT *res, void *arg, uint32_t flags) if (!port->inhibit_tick) { /* Handle any incoming data from network. */ - port_rx_pre(port); + if (ossl_quic_port_is_running(port)) + port_rx_pre(port); /* Iterate through all channels and service them. */ LIST_FOREACH(ch, ch, &port->channel_list) { @@ -370,7 +377,7 @@ static void port_rx_pre(QUIC_PORT *port) * Terminated state as there is no point trying to send CONNECTION_CLOSE * frames if the network BIO is not operating correctly. */ - ossl_quic_port_raise_net_error(port); + ossl_quic_port_raise_net_error(port, NULL); } /* @@ -538,12 +545,35 @@ void ossl_quic_port_set_inhibit_tick(QUIC_PORT *port, int inhibit) port->inhibit_tick = (inhibit != 0); } -void ossl_quic_port_raise_net_error(QUIC_PORT *port) +void ossl_quic_port_raise_net_error(QUIC_PORT *port, + QUIC_CHANNEL *triggering_ch) { QUIC_CHANNEL *ch; + if (!ossl_quic_port_is_running(port)) + return; + + /* + * Immediately capture any triggering error on the error stack, with a + * cover error. + */ + ERR_raise_data(ERR_LIB_SSL, SSL_R_QUIC_NETWORK_ERROR, + "port failed due to network BIO I/O error"); + OSSL_ERR_STATE_save(port->err_state); + port_transition_failed(port); + /* Give the triggering channel (if any) the first notification. */ + if (triggering_ch != NULL) + ossl_quic_channel_raise_net_error(triggering_ch); + LIST_FOREACH(ch, ch, &port->channel_list) - ossl_quic_channel_raise_net_error(ch); + if (ch != triggering_ch) + ossl_quic_channel_raise_net_error(ch); +} + +void ossl_quic_port_restore_err_state(const QUIC_PORT *port) +{ + ERR_clear_error(); + OSSL_ERR_STATE_restore(port->err_state); } diff --git a/ssl/quic/quic_port_local.h b/ssl/quic/quic_port_local.h index a2937beeaf9..38bb0193d8a 100644 --- a/ssl/quic/quic_port_local.h +++ b/ssl/quic/quic_port_local.h @@ -71,6 +71,9 @@ struct quic_port_st { /* SRTM used for incoming packet routing by SRT. */ QUIC_SRTM *srtm; + /* Port-level permanent errors (causing failure state) are stored here. */ + ERR_STATE *err_state; + /* DCID length used for incoming short header packets. */ unsigned char rx_short_dcid_len; /* For clients, CID length used for outgoing Initial packets. */