return 0;
}
+/*
+ * ch_cleanup() is idempotent: every owned pointer is NULL'd after its free,
+ * and every "have_*" flag is reset after its destructor runs. Calling this
+ * twice on the same channel is safe.
+ */
static void ch_cleanup(QUIC_CHANNEL *ch)
{
uint32_t pn_space;
ossl_quic_srtm_cull(ch->srtm, ch);
ossl_quic_tx_packetiser_free(ch->txp);
+ ch->txp = NULL;
ossl_quic_txpim_free(ch->txpim);
+ ch->txpim = NULL;
ossl_quic_cfq_free(ch->cfq);
+ ch->cfq = NULL;
ossl_qtx_free(ch->qtx);
- if (ch->cc_data != NULL)
+ ch->qtx = NULL;
+ if (ch->cc_data != NULL) {
ch->cc_method->free(ch->cc_data);
- if (ch->have_statm)
+ ch->cc_data = NULL;
+ }
+ if (ch->have_statm) {
ossl_statm_destroy(&ch->statm);
+ ch->have_statm = 0;
+ }
ossl_ackm_free(ch->ackm);
+ ch->ackm = NULL;
- if (ch->have_qsm)
+ if (ch->have_qsm) {
ossl_quic_stream_map_cleanup(&ch->qsm);
+ ch->have_qsm = 0;
+ }
for (pn_space = QUIC_PN_SPACE_INITIAL; pn_space < QUIC_PN_SPACE_NUM; ++pn_space) {
ossl_quic_sstream_free(ch->crypto_send[pn_space]);
+ ch->crypto_send[pn_space] = NULL;
ossl_quic_rstream_free(ch->crypto_recv[pn_space]);
+ ch->crypto_recv[pn_space] = NULL;
}
ossl_qrx_pkt_release(ch->qrx_pkt);
ch->qrx_pkt = NULL;
ossl_quic_tls_free(ch->qtls);
+ ch->qtls = NULL;
ossl_qrx_free(ch->qrx);
+ ch->qrx = NULL;
OPENSSL_free(ch->local_transport_params);
+ ch->local_transport_params = NULL;
OPENSSL_free((char *)ch->terminate_cause.reason);
+ ch->terminate_cause.reason = NULL;
OSSL_ERR_STATE_free(ch->err_state);
+ ch->err_state = NULL;
OPENSSL_free(ch->ack_range_scratch);
+ ch->ack_range_scratch = NULL;
OPENSSL_free(ch->pending_new_token);
+ ch->pending_new_token = NULL;
if (ch->on_port_list) {
ossl_list_ch_remove(&ch->port->channel_list, ch);
ossl_qlog_flush(ch->qlog); /* best effort */
OPENSSL_free(ch->qlog_title);
+ ch->qlog_title = NULL;
ossl_qlog_free(ch->qlog);
+ ch->qlog = NULL;
#endif
}
QUIC_CHANNEL_ARGS args = { 0 };
QUIC_CHANNEL *ch;
SSL *user_ssl = NULL;
- int ch_cleaned = 0;
args.port = port;
args.is_server = is_server;
/*
* And finally init the channel struct
*/
- if (!ossl_quic_channel_init(ch)) {
- ch_cleaned = 1;
+ if (!ossl_quic_channel_init(ch))
goto err;
- }
ossl_qtx_set_bio(ch->qtx, port->net_wbio);
return ch;
if (user_ssl != NULL)
((QUIC_CONNECTION *)user_ssl)->ch = NULL;
- if (ch_cleaned)
- OPENSSL_free(ch);
- else
- ossl_quic_channel_free(ch);
-
+ ossl_quic_channel_free(ch);
SSL_free(user_ssl);
return NULL;
#include "internal/quic_ssl.h"
#include "internal/ssl_unwrap.h"
#include "../ssl/quic/quic_local.h"
+#include "../ssl/quic/quic_port_local.h"
#include "testutil.h"
return ret;
}
+static int test_ch_cleanup_idempotent(void)
+{
+ SSL_CTX *ctx = NULL;
+ SSL *listener = NULL;
+ QUIC_LISTENER *ql;
+ QUIC_CHANNEL_ARGS args = { 0 };
+ QUIC_CHANNEL *ch = NULL;
+ int alloc_failed = 0;
+ int ret = 0;
+ OSSL_LIB_CTX *lctx = NULL;
+
+ if (!TEST_ptr(lctx = OSSL_LIB_CTX_new()))
+ goto err;
+ ctx = SSL_CTX_new_ex(lctx, NULL, OSSL_QUIC_server_method());
+ if (!TEST_ptr(ctx))
+ goto err;
+
+ listener = SSL_new_listener(ctx, SSL_LISTENER_FLAG_NO_VALIDATE);
+ if (!TEST_ptr(listener))
+ goto err;
+ ql = QUIC_LISTENER_FROM_SSL(listener);
+
+ args.port = ql->port;
+ args.lcidm = ql->port->lcidm;
+ args.srtm = ql->port->srtm;
+ args.is_server = 1;
+ args.is_tserver_ch = 1;
+ args.use_qlog = 1;
+ args.qlog_title = "qlog";
+
+ MFAIL_start();
+ ch = ossl_quic_channel_alloc(&args);
+ if (ch == NULL) {
+ alloc_failed = 1;
+ } else {
+ if (!ossl_quic_channel_init(ch))
+ alloc_failed = 1;
+
+ /*
+ * Whether init succeeded or failed, ossl_quic_channel_free() runs
+ * ch_cleanup(). On the failure path that's the second ch_cleanup()
+ * for this channel and must not crash or double-free.
+ */
+ ossl_quic_channel_free(ch);
+ ch = NULL;
+ }
+ MFAIL_end();
+
+ ret = alloc_failed ? 0 : 1;
+
+err:
+ SSL_free(listener);
+ SSL_CTX_free(ctx);
+ OSSL_LIB_CTX_free(lctx);
+ return ret;
+}
+
int setup_tests(void)
{
ADD_MFAIL_TEST(test_ossl_quic_port_create_incoming);
+ ADD_MFAIL_TEST(test_ch_cleanup_idempotent);
return 1;
}