openssl/test/helpers/quictestlib.c

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

1374 lines
40 KiB
C
Raw Normal View History

/*
* Copyright 2022-2025 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
#include <assert.h>
#include <openssl/configuration.h>
#include <openssl/bio.h>
#include "internal/e_os.h" /* For struct timeval */
#include "quictestlib.h"
#include "ssltestlib.h"
#include "../testutil.h"
#if defined(OPENSSL_THREADS) && !defined(CRYPTO_TDEBUG)
# include "../threadstest.h"
#endif
#include "internal/quic_ssl.h"
#include "internal/quic_wire_pkt.h"
#include "internal/quic_record_tx.h"
#include "internal/quic_error.h"
#include "internal/packet.h"
#include "internal/tsan_assist.h"
#define GROWTH_ALLOWANCE 1024
struct noise_args_data_st {
BIO *cbio;
BIO *sbio;
BIO *tracebio;
int flags;
};
struct qtest_fault {
QUIC_TSERVER *qtserv;
/* Plain packet mutations */
/* Header for the plaintext packet */
QUIC_PKT_HDR pplainhdr;
/* iovec for the plaintext packet data buffer */
OSSL_QTX_IOVEC pplainio;
/* Allocated size of the plaintext packet data buffer */
size_t pplainbuf_alloc;
qtest_fault_on_packet_plain_cb pplaincb;
void *pplaincbarg;
/* Handshake message mutations */
/* Handshake message buffer */
unsigned char *handbuf;
/* Allocated size of the handshake message buffer */
size_t handbufalloc;
/* Actual length of the handshake message */
size_t handbuflen;
qtest_fault_on_handshake_cb handshakecb;
void *handshakecbarg;
qtest_fault_on_enc_ext_cb encextcb;
void *encextcbarg;
/* Cipher packet mutations */
qtest_fault_on_packet_cipher_cb pciphercb;
void *pciphercbarg;
/* Datagram mutations */
qtest_fault_on_datagram_cb datagramcb;
void *datagramcbarg;
/* The currently processed message */
BIO_MSG msg;
/* Allocated size of msg data buffer */
size_t msgalloc;
struct noise_args_data_st noiseargs;
};
Attempt to fix occasional failure of quicapi test in ci https://github.com/openssl/openssl/actions/runs/15214054228/job/42795224720 the theory I have for the cause of this failure is: 1. qtest_create_quic_connection_ex is called for the client 2. The client is in blocking mode, so we fall into the conditional on line 512 3. We create the server thread on line 519, which is non-blocking 4. The scheduler in the failing case, lets the server run ahead of the client 5. Server thread enters qtest_create_quic_connection_ex and iterates steps 6-9 in the do_while loop starting on line 530 6. Server calls qtest_add_time 7. Server calls ossl_quic_tserver_tick 8. Server calls ossl_quic_tserver_is_term_any, received NULL return 9. Server calls qtest_wait_for_timeout 10. Eventually qtest_wait_for_timeout returns zero, adn the server jumps to the error label, returning zero to globservret, and the thread exits 11. Client thread regains the cpu, and attempts to call SSL_connect, which fails, as the server is no longer listening 12. We fall into the error case on line 556, and SSL_get_error returns SSL_ERROR_SSL, which causes clienterr to get set to 1 13. We exit the do{} while loop on line 581, and do the TEST_true check on line 593. The server having exited wait_for_thread returns true, but globserverret is still zero from step 10 above, and so the test fails I can't prove this is the case, as the test only appears to fail in CI, and we can't dump verbose logging there, lest we affect the timing of the tests, so this is just a theory, but it seems to fit the observations we have. Attempting to fix this, by creating a thread interlock with a condition variable that blocks the server from ticking the quic reactor until such time as the client is about to call SSL_connect to prevent the race condition Reviewed-by: Saša Nedvědický <sashan@openssl.org> Reviewed-by: Tomas Mraz <tomas@openssl.org> (Merged from https://github.com/openssl/openssl/pull/27704)
2025-05-27 21:36:35 +08:00
#if defined(OPENSSL_THREADS) && !defined(CRYPTO_TDEBUG)
static int client_ready = 0;
static CRYPTO_CONDVAR *client_ready_cond = NULL;
static CRYPTO_MUTEX *client_ready_mutex = NULL;
#endif
static void packet_plain_finish(void *arg);
static void handshake_finish(void *arg);
add locking around fake_now fake_now in the quictestlib is read/written by potentially many threads, and as such should have a surrounding lock to prevent WAR/RAW errors as caught by tsan: 2023-11-03T16:27:23.7184999Z ================== 2023-11-03T16:27:23.7185290Z WARNING: ThreadSanitizer: data race (pid=18754) 2023-11-03T16:27:23.7185720Z Read of size 8 at 0x558f6f9fe970 by main thread: 2023-11-03T16:27:23.7186726Z #0 qtest_create_quic_connection_ex <null> (quicapitest+0x14aead) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7187665Z #1 qtest_create_quic_connection <null> (quicapitest+0x14b220) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7188567Z #2 test_quic_write_read quicapitest.c (quicapitest+0x150ee2) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7189561Z #3 run_tests <null> (quicapitest+0x2237ab) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7190294Z #4 main <null> (quicapitest+0x223d2b) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7190720Z 2023-11-03T16:27:23.7190902Z Previous write of size 8 at 0x558f6f9fe970 by thread T1: 2023-11-03T16:27:23.7191607Z #0 qtest_create_quic_connection_ex <null> (quicapitest+0x14aecf) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7192505Z #1 run_server_thread quictestlib.c (quicapitest+0x14b1d6) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7193361Z #2 thread_run quictestlib.c (quicapitest+0x14cadf) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7193848Z 2023-11-03T16:27:23.7194220Z Location is global 'fake_now.0' of size 8 at 0x558f6f9fe970 (quicapitest+0x1af4970) 2023-11-03T16:27:23.7194636Z 2023-11-03T16:27:23.7194816Z Thread T1 (tid=18760, running) created by main thread at: 2023-11-03T16:27:23.7195465Z #0 pthread_create <null> (quicapitest+0xca12d) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7196317Z #1 qtest_create_quic_connection_ex <null> (quicapitest+0x14adcb) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7197214Z #2 qtest_create_quic_connection <null> (quicapitest+0x14b220) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7198111Z #3 test_quic_write_read quicapitest.c (quicapitest+0x150ee2) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7198940Z #4 run_tests <null> (quicapitest+0x2237ab) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7199661Z #5 main <null> (quicapitest+0x223d2b) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7200083Z 2023-11-03T16:27:23.7200862Z SUMMARY: ThreadSanitizer: data race (/home/runner/work/openssl/openssl/test/quicapitest+0x14aead) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) in qtest_create_quic_connection_ex Reviewed-by: Hugo Landau <hlandau@openssl.org> Reviewed-by: Paul Dale <pauli@openssl.org> Reviewed-by: Matt Caswell <matt@openssl.org> Reviewed-by: Tomas Mraz <tomas@openssl.org> (Merged from https://github.com/openssl/openssl/pull/22616)
2023-11-04 00:56:40 +08:00
static OSSL_TIME qtest_get_time(void);
static void qtest_reset_time(void);
static int using_fake_time = 0;
static OSSL_TIME fake_now;
add locking around fake_now fake_now in the quictestlib is read/written by potentially many threads, and as such should have a surrounding lock to prevent WAR/RAW errors as caught by tsan: 2023-11-03T16:27:23.7184999Z ================== 2023-11-03T16:27:23.7185290Z WARNING: ThreadSanitizer: data race (pid=18754) 2023-11-03T16:27:23.7185720Z Read of size 8 at 0x558f6f9fe970 by main thread: 2023-11-03T16:27:23.7186726Z #0 qtest_create_quic_connection_ex <null> (quicapitest+0x14aead) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7187665Z #1 qtest_create_quic_connection <null> (quicapitest+0x14b220) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7188567Z #2 test_quic_write_read quicapitest.c (quicapitest+0x150ee2) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7189561Z #3 run_tests <null> (quicapitest+0x2237ab) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7190294Z #4 main <null> (quicapitest+0x223d2b) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7190720Z 2023-11-03T16:27:23.7190902Z Previous write of size 8 at 0x558f6f9fe970 by thread T1: 2023-11-03T16:27:23.7191607Z #0 qtest_create_quic_connection_ex <null> (quicapitest+0x14aecf) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7192505Z #1 run_server_thread quictestlib.c (quicapitest+0x14b1d6) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7193361Z #2 thread_run quictestlib.c (quicapitest+0x14cadf) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7193848Z 2023-11-03T16:27:23.7194220Z Location is global 'fake_now.0' of size 8 at 0x558f6f9fe970 (quicapitest+0x1af4970) 2023-11-03T16:27:23.7194636Z 2023-11-03T16:27:23.7194816Z Thread T1 (tid=18760, running) created by main thread at: 2023-11-03T16:27:23.7195465Z #0 pthread_create <null> (quicapitest+0xca12d) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7196317Z #1 qtest_create_quic_connection_ex <null> (quicapitest+0x14adcb) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7197214Z #2 qtest_create_quic_connection <null> (quicapitest+0x14b220) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7198111Z #3 test_quic_write_read quicapitest.c (quicapitest+0x150ee2) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7198940Z #4 run_tests <null> (quicapitest+0x2237ab) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7199661Z #5 main <null> (quicapitest+0x223d2b) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7200083Z 2023-11-03T16:27:23.7200862Z SUMMARY: ThreadSanitizer: data race (/home/runner/work/openssl/openssl/test/quicapitest+0x14aead) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) in qtest_create_quic_connection_ex Reviewed-by: Hugo Landau <hlandau@openssl.org> Reviewed-by: Paul Dale <pauli@openssl.org> Reviewed-by: Matt Caswell <matt@openssl.org> Reviewed-by: Tomas Mraz <tomas@openssl.org> (Merged from https://github.com/openssl/openssl/pull/22616)
2023-11-04 00:56:40 +08:00
static CRYPTO_RWLOCK *fake_now_lock = NULL;
static OSSL_TIME start_time;
static OSSL_TIME fake_now_cb(void *arg)
{
add locking around fake_now fake_now in the quictestlib is read/written by potentially many threads, and as such should have a surrounding lock to prevent WAR/RAW errors as caught by tsan: 2023-11-03T16:27:23.7184999Z ================== 2023-11-03T16:27:23.7185290Z WARNING: ThreadSanitizer: data race (pid=18754) 2023-11-03T16:27:23.7185720Z Read of size 8 at 0x558f6f9fe970 by main thread: 2023-11-03T16:27:23.7186726Z #0 qtest_create_quic_connection_ex <null> (quicapitest+0x14aead) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7187665Z #1 qtest_create_quic_connection <null> (quicapitest+0x14b220) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7188567Z #2 test_quic_write_read quicapitest.c (quicapitest+0x150ee2) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7189561Z #3 run_tests <null> (quicapitest+0x2237ab) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7190294Z #4 main <null> (quicapitest+0x223d2b) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7190720Z 2023-11-03T16:27:23.7190902Z Previous write of size 8 at 0x558f6f9fe970 by thread T1: 2023-11-03T16:27:23.7191607Z #0 qtest_create_quic_connection_ex <null> (quicapitest+0x14aecf) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7192505Z #1 run_server_thread quictestlib.c (quicapitest+0x14b1d6) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7193361Z #2 thread_run quictestlib.c (quicapitest+0x14cadf) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7193848Z 2023-11-03T16:27:23.7194220Z Location is global 'fake_now.0' of size 8 at 0x558f6f9fe970 (quicapitest+0x1af4970) 2023-11-03T16:27:23.7194636Z 2023-11-03T16:27:23.7194816Z Thread T1 (tid=18760, running) created by main thread at: 2023-11-03T16:27:23.7195465Z #0 pthread_create <null> (quicapitest+0xca12d) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7196317Z #1 qtest_create_quic_connection_ex <null> (quicapitest+0x14adcb) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7197214Z #2 qtest_create_quic_connection <null> (quicapitest+0x14b220) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7198111Z #3 test_quic_write_read quicapitest.c (quicapitest+0x150ee2) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7198940Z #4 run_tests <null> (quicapitest+0x2237ab) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7199661Z #5 main <null> (quicapitest+0x223d2b) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7200083Z 2023-11-03T16:27:23.7200862Z SUMMARY: ThreadSanitizer: data race (/home/runner/work/openssl/openssl/test/quicapitest+0x14aead) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) in qtest_create_quic_connection_ex Reviewed-by: Hugo Landau <hlandau@openssl.org> Reviewed-by: Paul Dale <pauli@openssl.org> Reviewed-by: Matt Caswell <matt@openssl.org> Reviewed-by: Tomas Mraz <tomas@openssl.org> (Merged from https://github.com/openssl/openssl/pull/22616)
2023-11-04 00:56:40 +08:00
return qtest_get_time();
}
static void noise_msg_callback(int write_p, int version, int content_type,
const void *buf, size_t len, SSL *ssl,
void *arg)
{
struct noise_args_data_st *noiseargs = (struct noise_args_data_st *)arg;
if (content_type == SSL3_RT_QUIC_FRAME_FULL) {
PACKET pkt;
uint64_t frame_type;
if (!PACKET_buf_init(&pkt, buf, len))
return;
if (!ossl_quic_wire_peek_frame_header(&pkt, &frame_type, NULL))
return;
if (frame_type == OSSL_QUIC_FRAME_TYPE_PING) {
/*
* If either endpoint issues a ping frame then we are in danger
* of our noise being too much such that the connection itself
* fails. We back off on the noise for a bit to avoid that.
*/
Extend backoff period in noisydgram BIO users Initially tests that were written which make use of the noisy dgram BIO, were done under the assumption that, despite any packet mangling done by the noisy dgram bio, the connection would still be established. This was initiall guaranteed by configuring the BIO to avoid corrupting/dropping/duplicating/re-injecting the first packet received, thus ensuring that the client and server hello frames would make it to the peer successfully. This implicitly made the assumption that the client and server hellos were contained within a single datagram, which until recently was true. However, with the introduction of ML-KEM keyshares, the above assumption no longer holds. Large ML-KEM keyshares generally expand these TLS messages accross multiple datagrams, and so it is now possible that those initial records can become corrupted/lost etc, leading to unexpected connection failures. Lets fix it by restoring the guarantee that these tests were written under by making the backoff time configurable to a number of frames, and configuring the quic connection objects used in the test to not drop the first two initial frames, once again guaranteeing that the client and server hello arrive at the peer uncorrupted, so that we get a good connection established. Fixes #27103 Reviewed-by: Matt Caswell <matt@openssl.org> Reviewed-by: Saša Nedvědický <sashan@openssl.org> Reviewed-by: Tomas Mraz <tomas@openssl.org> (Merged from https://github.com/openssl/openssl/pull/27169)
2025-03-26 23:17:31 +08:00
(void)BIO_ctrl(noiseargs->cbio, BIO_CTRL_NOISE_BACK_OFF, 1, NULL);
(void)BIO_ctrl(noiseargs->sbio, BIO_CTRL_NOISE_BACK_OFF, 1, NULL);
}
}
#ifndef OPENSSL_NO_SSL_TRACE
if ((noiseargs->flags & QTEST_FLAG_CLIENT_TRACE) != 0
&& !SSL_is_server(ssl))
SSL_trace(write_p, version, content_type, buf, len, ssl,
noiseargs->tracebio);
#endif
}
int qtest_create_quic_objects(OSSL_LIB_CTX *libctx, SSL_CTX *clientctx,
SSL_CTX *serverctx, char *certfile, char *keyfile,
int flags, QUIC_TSERVER **qtserv, SSL **cssl,
QTEST_FAULT **fault, BIO **tracebio)
{
/* ALPN value as recognised by QUIC_TSERVER */
unsigned char alpn[] = { 8, 'o', 's', 's', 'l', 't', 'e', 's', 't' };
QUIC_TSERVER_ARGS tserver_args = {0};
BIO *cbio = NULL, *sbio = NULL, *fisbio = NULL;
BIO_ADDR *peeraddr = NULL;
struct in_addr ina = {0};
BIO *tmpbio = NULL;
QTEST_DATA *bdata = NULL;
Attempt to fix occasional failure of quicapi test in ci https://github.com/openssl/openssl/actions/runs/15214054228/job/42795224720 the theory I have for the cause of this failure is: 1. qtest_create_quic_connection_ex is called for the client 2. The client is in blocking mode, so we fall into the conditional on line 512 3. We create the server thread on line 519, which is non-blocking 4. The scheduler in the failing case, lets the server run ahead of the client 5. Server thread enters qtest_create_quic_connection_ex and iterates steps 6-9 in the do_while loop starting on line 530 6. Server calls qtest_add_time 7. Server calls ossl_quic_tserver_tick 8. Server calls ossl_quic_tserver_is_term_any, received NULL return 9. Server calls qtest_wait_for_timeout 10. Eventually qtest_wait_for_timeout returns zero, adn the server jumps to the error label, returning zero to globservret, and the thread exits 11. Client thread regains the cpu, and attempts to call SSL_connect, which fails, as the server is no longer listening 12. We fall into the error case on line 556, and SSL_get_error returns SSL_ERROR_SSL, which causes clienterr to get set to 1 13. We exit the do{} while loop on line 581, and do the TEST_true check on line 593. The server having exited wait_for_thread returns true, but globserverret is still zero from step 10 above, and so the test fails I can't prove this is the case, as the test only appears to fail in CI, and we can't dump verbose logging there, lest we affect the timing of the tests, so this is just a theory, but it seems to fit the observations we have. Attempting to fix this, by creating a thread interlock with a condition variable that blocks the server from ticking the quic reactor until such time as the client is about to call SSL_connect to prevent the race condition Reviewed-by: Saša Nedvědický <sashan@openssl.org> Reviewed-by: Tomas Mraz <tomas@openssl.org> (Merged from https://github.com/openssl/openssl/pull/27704)
2025-05-27 21:36:35 +08:00
#if defined(OPENSSL_THREADS) && !defined(CRYPTO_TDEBUG)
if (client_ready_cond == NULL) {
client_ready_cond = ossl_crypto_condvar_new();
if (client_ready_cond == NULL)
return 0;
}
if (client_ready_mutex == NULL) {
client_ready_mutex = ossl_crypto_mutex_new();
if (client_ready_mutex == NULL) {
ossl_crypto_condvar_free(&client_ready_cond);
client_ready_cond = NULL;
return 0;
}
}
#endif
bdata = OPENSSL_zalloc(sizeof(QTEST_DATA));
if (bdata == NULL)
return 0;
*qtserv = NULL;
if (*cssl == NULL) {
*cssl = SSL_new(clientctx);
if (!TEST_ptr(*cssl))
return 0;
}
if (fault != NULL) {
*fault = OPENSSL_zalloc(sizeof(**fault));
if (*fault == NULL)
goto err;
bdata->fault = *fault;
}
#ifndef OPENSSL_NO_SSL_TRACE
if ((flags & QTEST_FLAG_CLIENT_TRACE) != 0) {
tmpbio = BIO_new_fp(stdout, BIO_NOCLOSE);
if (!TEST_ptr(tmpbio))
goto err;
SSL_set_msg_callback(*cssl, SSL_trace);
SSL_set_msg_callback_arg(*cssl, tmpbio);
}
#endif
if (tracebio != NULL)
*tracebio = tmpbio;
/* SSL_set_alpn_protos returns 0 for success! */
if (!TEST_false(SSL_set_alpn_protos(*cssl, alpn, sizeof(alpn))))
goto err;
if (!TEST_ptr(peeraddr = BIO_ADDR_new()))
goto err;
if ((flags & QTEST_FLAG_BLOCK) != 0) {
#if !defined(OPENSSL_NO_POSIX_IO)
int cfd, sfd;
/*
* For blocking mode we need to create actual sockets rather than doing
* everything in memory
*/
if (!TEST_true(create_test_sockets(&cfd, &sfd, SOCK_DGRAM, peeraddr)))
goto err;
cbio = BIO_new_dgram(cfd, 1);
if (!TEST_ptr(cbio)) {
close(cfd);
close(sfd);
goto err;
}
sbio = BIO_new_dgram(sfd, 1);
if (!TEST_ptr(sbio)) {
close(sfd);
goto err;
}
#else
goto err;
#endif
} else {
BIO_ADDR *localaddr = NULL;
if (!TEST_true(BIO_new_bio_dgram_pair(&cbio, 0, &sbio, 0)))
goto err;
if (!TEST_true(BIO_dgram_set_caps(cbio, BIO_DGRAM_CAP_HANDLES_DST_ADDR))
|| !TEST_true(BIO_dgram_set_caps(sbio, BIO_DGRAM_CAP_HANDLES_DST_ADDR)))
goto err;
if (!TEST_ptr(localaddr = BIO_ADDR_new()))
goto err;
/* Dummy client local addresses */
if (!TEST_true(BIO_ADDR_rawmake(localaddr, AF_INET, &ina, sizeof(ina),
htons(0)))) {
BIO_ADDR_free(localaddr);
goto err;
}
if (!TEST_int_eq(BIO_dgram_set0_local_addr(cbio, localaddr), 1)) {
BIO_ADDR_free(localaddr);
goto err;
}
/* Dummy server address */
if (!TEST_true(BIO_ADDR_rawmake(peeraddr, AF_INET, &ina, sizeof(ina),
htons(0))))
goto err;
}
if ((flags & QTEST_FLAG_PACKET_SPLIT) != 0) {
BIO *pktsplitbio = BIO_new(bio_f_pkt_split_dgram_filter());
if (!TEST_ptr(pktsplitbio))
goto err;
cbio = BIO_push(pktsplitbio, cbio);
BIO_set_data(pktsplitbio, bdata);
pktsplitbio = BIO_new(bio_f_pkt_split_dgram_filter());
if (!TEST_ptr(pktsplitbio))
goto err;
sbio = BIO_push(pktsplitbio, sbio);
BIO_set_data(pktsplitbio, bdata);
}
if ((flags & QTEST_FLAG_NOISE) != 0) {
BIO *noisebio;
struct bio_noise_now_cb_st now_cb = { fake_now_cb, NULL };
/*
* It is an error to not have a QTEST_FAULT object when introducing noise
*/
if (!TEST_ptr(fault))
goto err;
noisebio = BIO_new(bio_f_noisy_dgram_filter());
if (!TEST_ptr(noisebio))
goto err;
cbio = BIO_push(noisebio, cbio);
if ((flags & QTEST_FLAG_FAKE_TIME) != 0) {
if (!TEST_int_eq(BIO_ctrl(cbio, BIO_CTRL_NOISE_SET_NOW_CB,
0, &now_cb), 1))
goto err;
}
noisebio = BIO_new(bio_f_noisy_dgram_filter());
if (!TEST_ptr(noisebio))
goto err;
sbio = BIO_push(noisebio, sbio);
if ((flags & QTEST_FLAG_FAKE_TIME) != 0) {
if (!TEST_int_eq(BIO_ctrl(sbio, BIO_CTRL_NOISE_SET_NOW_CB,
0, &now_cb), 1))
goto err;
}
Extend backoff period in noisydgram BIO users Initially tests that were written which make use of the noisy dgram BIO, were done under the assumption that, despite any packet mangling done by the noisy dgram bio, the connection would still be established. This was initiall guaranteed by configuring the BIO to avoid corrupting/dropping/duplicating/re-injecting the first packet received, thus ensuring that the client and server hello frames would make it to the peer successfully. This implicitly made the assumption that the client and server hellos were contained within a single datagram, which until recently was true. However, with the introduction of ML-KEM keyshares, the above assumption no longer holds. Large ML-KEM keyshares generally expand these TLS messages accross multiple datagrams, and so it is now possible that those initial records can become corrupted/lost etc, leading to unexpected connection failures. Lets fix it by restoring the guarantee that these tests were written under by making the backoff time configurable to a number of frames, and configuring the quic connection objects used in the test to not drop the first two initial frames, once again guaranteeing that the client and server hello arrive at the peer uncorrupted, so that we get a good connection established. Fixes #27103 Reviewed-by: Matt Caswell <matt@openssl.org> Reviewed-by: Saša Nedvědický <sashan@openssl.org> Reviewed-by: Tomas Mraz <tomas@openssl.org> (Merged from https://github.com/openssl/openssl/pull/27169)
2025-03-26 23:17:31 +08:00
(void)BIO_ctrl(sbio, BIO_CTRL_NOISE_BACK_OFF, 2, NULL);
(*fault)->noiseargs.cbio = cbio;
(*fault)->noiseargs.sbio = sbio;
(*fault)->noiseargs.tracebio = tmpbio;
(*fault)->noiseargs.flags = flags;
SSL_set_msg_callback(*cssl, noise_msg_callback);
SSL_set_msg_callback_arg(*cssl, &(*fault)->noiseargs);
}
SSL_set_bio(*cssl, cbio, cbio);
if (!TEST_true(SSL_set_blocking_mode(*cssl,
(flags & QTEST_FLAG_BLOCK) != 0 ? 1 : 0)))
goto err;
if (!TEST_true(SSL_set1_initial_peer_addr(*cssl, peeraddr)))
goto err;
fisbio = BIO_new(qtest_get_bio_method());
if (!TEST_ptr(fisbio))
goto err;
BIO_set_data(fisbio, bdata);
if (!BIO_up_ref(sbio))
goto err;
if (!TEST_ptr(BIO_push(fisbio, sbio))) {
BIO_free(sbio);
goto err;
}
tserver_args.libctx = libctx;
tserver_args.net_rbio = sbio;
tserver_args.net_wbio = fisbio;
tserver_args.alpn = NULL;
if (serverctx != NULL && !TEST_true(SSL_CTX_up_ref(serverctx)))
goto err;
tserver_args.ctx = serverctx;
add locking around fake_now fake_now in the quictestlib is read/written by potentially many threads, and as such should have a surrounding lock to prevent WAR/RAW errors as caught by tsan: 2023-11-03T16:27:23.7184999Z ================== 2023-11-03T16:27:23.7185290Z WARNING: ThreadSanitizer: data race (pid=18754) 2023-11-03T16:27:23.7185720Z Read of size 8 at 0x558f6f9fe970 by main thread: 2023-11-03T16:27:23.7186726Z #0 qtest_create_quic_connection_ex <null> (quicapitest+0x14aead) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7187665Z #1 qtest_create_quic_connection <null> (quicapitest+0x14b220) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7188567Z #2 test_quic_write_read quicapitest.c (quicapitest+0x150ee2) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7189561Z #3 run_tests <null> (quicapitest+0x2237ab) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7190294Z #4 main <null> (quicapitest+0x223d2b) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7190720Z 2023-11-03T16:27:23.7190902Z Previous write of size 8 at 0x558f6f9fe970 by thread T1: 2023-11-03T16:27:23.7191607Z #0 qtest_create_quic_connection_ex <null> (quicapitest+0x14aecf) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7192505Z #1 run_server_thread quictestlib.c (quicapitest+0x14b1d6) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7193361Z #2 thread_run quictestlib.c (quicapitest+0x14cadf) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7193848Z 2023-11-03T16:27:23.7194220Z Location is global 'fake_now.0' of size 8 at 0x558f6f9fe970 (quicapitest+0x1af4970) 2023-11-03T16:27:23.7194636Z 2023-11-03T16:27:23.7194816Z Thread T1 (tid=18760, running) created by main thread at: 2023-11-03T16:27:23.7195465Z #0 pthread_create <null> (quicapitest+0xca12d) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7196317Z #1 qtest_create_quic_connection_ex <null> (quicapitest+0x14adcb) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7197214Z #2 qtest_create_quic_connection <null> (quicapitest+0x14b220) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7198111Z #3 test_quic_write_read quicapitest.c (quicapitest+0x150ee2) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7198940Z #4 run_tests <null> (quicapitest+0x2237ab) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7199661Z #5 main <null> (quicapitest+0x223d2b) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7200083Z 2023-11-03T16:27:23.7200862Z SUMMARY: ThreadSanitizer: data race (/home/runner/work/openssl/openssl/test/quicapitest+0x14aead) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) in qtest_create_quic_connection_ex Reviewed-by: Hugo Landau <hlandau@openssl.org> Reviewed-by: Paul Dale <pauli@openssl.org> Reviewed-by: Matt Caswell <matt@openssl.org> Reviewed-by: Tomas Mraz <tomas@openssl.org> (Merged from https://github.com/openssl/openssl/pull/22616)
2023-11-04 00:56:40 +08:00
if (fake_now_lock == NULL) {
fake_now_lock = CRYPTO_THREAD_lock_new();
if (fake_now_lock == NULL)
goto err;
}
if ((flags & QTEST_FLAG_FAKE_TIME) != 0) {
using_fake_time = 1;
add locking around fake_now fake_now in the quictestlib is read/written by potentially many threads, and as such should have a surrounding lock to prevent WAR/RAW errors as caught by tsan: 2023-11-03T16:27:23.7184999Z ================== 2023-11-03T16:27:23.7185290Z WARNING: ThreadSanitizer: data race (pid=18754) 2023-11-03T16:27:23.7185720Z Read of size 8 at 0x558f6f9fe970 by main thread: 2023-11-03T16:27:23.7186726Z #0 qtest_create_quic_connection_ex <null> (quicapitest+0x14aead) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7187665Z #1 qtest_create_quic_connection <null> (quicapitest+0x14b220) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7188567Z #2 test_quic_write_read quicapitest.c (quicapitest+0x150ee2) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7189561Z #3 run_tests <null> (quicapitest+0x2237ab) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7190294Z #4 main <null> (quicapitest+0x223d2b) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7190720Z 2023-11-03T16:27:23.7190902Z Previous write of size 8 at 0x558f6f9fe970 by thread T1: 2023-11-03T16:27:23.7191607Z #0 qtest_create_quic_connection_ex <null> (quicapitest+0x14aecf) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7192505Z #1 run_server_thread quictestlib.c (quicapitest+0x14b1d6) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7193361Z #2 thread_run quictestlib.c (quicapitest+0x14cadf) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7193848Z 2023-11-03T16:27:23.7194220Z Location is global 'fake_now.0' of size 8 at 0x558f6f9fe970 (quicapitest+0x1af4970) 2023-11-03T16:27:23.7194636Z 2023-11-03T16:27:23.7194816Z Thread T1 (tid=18760, running) created by main thread at: 2023-11-03T16:27:23.7195465Z #0 pthread_create <null> (quicapitest+0xca12d) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7196317Z #1 qtest_create_quic_connection_ex <null> (quicapitest+0x14adcb) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7197214Z #2 qtest_create_quic_connection <null> (quicapitest+0x14b220) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7198111Z #3 test_quic_write_read quicapitest.c (quicapitest+0x150ee2) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7198940Z #4 run_tests <null> (quicapitest+0x2237ab) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7199661Z #5 main <null> (quicapitest+0x223d2b) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7200083Z 2023-11-03T16:27:23.7200862Z SUMMARY: ThreadSanitizer: data race (/home/runner/work/openssl/openssl/test/quicapitest+0x14aead) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) in qtest_create_quic_connection_ex Reviewed-by: Hugo Landau <hlandau@openssl.org> Reviewed-by: Paul Dale <pauli@openssl.org> Reviewed-by: Matt Caswell <matt@openssl.org> Reviewed-by: Tomas Mraz <tomas@openssl.org> (Merged from https://github.com/openssl/openssl/pull/22616)
2023-11-04 00:56:40 +08:00
qtest_reset_time();
tserver_args.now_cb = fake_now_cb;
(void)ossl_quic_set_override_now_cb(*cssl, fake_now_cb, NULL);
} else {
using_fake_time = 0;
}
if (!TEST_ptr(*qtserv = ossl_quic_tserver_new(&tserver_args, certfile,
keyfile)))
goto err;
bdata->short_conn_id_len = ossl_quic_tserver_get_short_header_conn_id_len(*qtserv);
/* Ownership of fisbio and sbio is now held by *qtserv */
sbio = NULL;
fisbio = NULL;
if ((flags & QTEST_FLAG_NOISE) != 0)
ossl_quic_tserver_set_msg_callback(*qtserv, noise_msg_callback,
&(*fault)->noiseargs);
if (fault != NULL)
(*fault)->qtserv = *qtserv;
BIO_ADDR_free(peeraddr);
return 1;
err:
SSL_CTX_free(tserver_args.ctx);
BIO_ADDR_free(peeraddr);
BIO_free_all(cbio);
BIO_free_all(fisbio);
BIO_free_all(sbio);
SSL_free(*cssl);
*cssl = NULL;
ossl_quic_tserver_free(*qtserv);
if (fault != NULL)
OPENSSL_free(*fault);
OPENSSL_free(bdata);
BIO_free(tmpbio);
if (tracebio != NULL)
*tracebio = NULL;
return 0;
}
void qtest_add_time(uint64_t millis)
{
add locking around fake_now fake_now in the quictestlib is read/written by potentially many threads, and as such should have a surrounding lock to prevent WAR/RAW errors as caught by tsan: 2023-11-03T16:27:23.7184999Z ================== 2023-11-03T16:27:23.7185290Z WARNING: ThreadSanitizer: data race (pid=18754) 2023-11-03T16:27:23.7185720Z Read of size 8 at 0x558f6f9fe970 by main thread: 2023-11-03T16:27:23.7186726Z #0 qtest_create_quic_connection_ex <null> (quicapitest+0x14aead) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7187665Z #1 qtest_create_quic_connection <null> (quicapitest+0x14b220) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7188567Z #2 test_quic_write_read quicapitest.c (quicapitest+0x150ee2) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7189561Z #3 run_tests <null> (quicapitest+0x2237ab) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7190294Z #4 main <null> (quicapitest+0x223d2b) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7190720Z 2023-11-03T16:27:23.7190902Z Previous write of size 8 at 0x558f6f9fe970 by thread T1: 2023-11-03T16:27:23.7191607Z #0 qtest_create_quic_connection_ex <null> (quicapitest+0x14aecf) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7192505Z #1 run_server_thread quictestlib.c (quicapitest+0x14b1d6) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7193361Z #2 thread_run quictestlib.c (quicapitest+0x14cadf) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7193848Z 2023-11-03T16:27:23.7194220Z Location is global 'fake_now.0' of size 8 at 0x558f6f9fe970 (quicapitest+0x1af4970) 2023-11-03T16:27:23.7194636Z 2023-11-03T16:27:23.7194816Z Thread T1 (tid=18760, running) created by main thread at: 2023-11-03T16:27:23.7195465Z #0 pthread_create <null> (quicapitest+0xca12d) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7196317Z #1 qtest_create_quic_connection_ex <null> (quicapitest+0x14adcb) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7197214Z #2 qtest_create_quic_connection <null> (quicapitest+0x14b220) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7198111Z #3 test_quic_write_read quicapitest.c (quicapitest+0x150ee2) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7198940Z #4 run_tests <null> (quicapitest+0x2237ab) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7199661Z #5 main <null> (quicapitest+0x223d2b) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7200083Z 2023-11-03T16:27:23.7200862Z SUMMARY: ThreadSanitizer: data race (/home/runner/work/openssl/openssl/test/quicapitest+0x14aead) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) in qtest_create_quic_connection_ex Reviewed-by: Hugo Landau <hlandau@openssl.org> Reviewed-by: Paul Dale <pauli@openssl.org> Reviewed-by: Matt Caswell <matt@openssl.org> Reviewed-by: Tomas Mraz <tomas@openssl.org> (Merged from https://github.com/openssl/openssl/pull/22616)
2023-11-04 00:56:40 +08:00
if (!CRYPTO_THREAD_write_lock(fake_now_lock))
return;
fake_now = ossl_time_add(fake_now, ossl_ms2time(millis));
add locking around fake_now fake_now in the quictestlib is read/written by potentially many threads, and as such should have a surrounding lock to prevent WAR/RAW errors as caught by tsan: 2023-11-03T16:27:23.7184999Z ================== 2023-11-03T16:27:23.7185290Z WARNING: ThreadSanitizer: data race (pid=18754) 2023-11-03T16:27:23.7185720Z Read of size 8 at 0x558f6f9fe970 by main thread: 2023-11-03T16:27:23.7186726Z #0 qtest_create_quic_connection_ex <null> (quicapitest+0x14aead) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7187665Z #1 qtest_create_quic_connection <null> (quicapitest+0x14b220) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7188567Z #2 test_quic_write_read quicapitest.c (quicapitest+0x150ee2) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7189561Z #3 run_tests <null> (quicapitest+0x2237ab) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7190294Z #4 main <null> (quicapitest+0x223d2b) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7190720Z 2023-11-03T16:27:23.7190902Z Previous write of size 8 at 0x558f6f9fe970 by thread T1: 2023-11-03T16:27:23.7191607Z #0 qtest_create_quic_connection_ex <null> (quicapitest+0x14aecf) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7192505Z #1 run_server_thread quictestlib.c (quicapitest+0x14b1d6) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7193361Z #2 thread_run quictestlib.c (quicapitest+0x14cadf) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7193848Z 2023-11-03T16:27:23.7194220Z Location is global 'fake_now.0' of size 8 at 0x558f6f9fe970 (quicapitest+0x1af4970) 2023-11-03T16:27:23.7194636Z 2023-11-03T16:27:23.7194816Z Thread T1 (tid=18760, running) created by main thread at: 2023-11-03T16:27:23.7195465Z #0 pthread_create <null> (quicapitest+0xca12d) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7196317Z #1 qtest_create_quic_connection_ex <null> (quicapitest+0x14adcb) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7197214Z #2 qtest_create_quic_connection <null> (quicapitest+0x14b220) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7198111Z #3 test_quic_write_read quicapitest.c (quicapitest+0x150ee2) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7198940Z #4 run_tests <null> (quicapitest+0x2237ab) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7199661Z #5 main <null> (quicapitest+0x223d2b) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7200083Z 2023-11-03T16:27:23.7200862Z SUMMARY: ThreadSanitizer: data race (/home/runner/work/openssl/openssl/test/quicapitest+0x14aead) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) in qtest_create_quic_connection_ex Reviewed-by: Hugo Landau <hlandau@openssl.org> Reviewed-by: Paul Dale <pauli@openssl.org> Reviewed-by: Matt Caswell <matt@openssl.org> Reviewed-by: Tomas Mraz <tomas@openssl.org> (Merged from https://github.com/openssl/openssl/pull/22616)
2023-11-04 00:56:40 +08:00
CRYPTO_THREAD_unlock(fake_now_lock);
}
static OSSL_TIME qtest_get_time(void)
{
OSSL_TIME ret;
if (!CRYPTO_THREAD_read_lock(fake_now_lock))
return ossl_time_zero();
ret = fake_now;
CRYPTO_THREAD_unlock(fake_now_lock);
return ret;
}
static void qtest_reset_time(void)
{
if (!CRYPTO_THREAD_write_lock(fake_now_lock))
return;
fake_now = ossl_time_zero();
CRYPTO_THREAD_unlock(fake_now_lock);
/* zero time can have a special meaning, bump it */
qtest_add_time(1);
}
void qtest_start_stopwatch(void)
{
start_time = qtest_get_time();
}
uint64_t qtest_get_stopwatch_time(void)
{
return ossl_time2ms(ossl_time_subtract(qtest_get_time(), start_time));
}
QTEST_FAULT *qtest_create_injector(QUIC_TSERVER *ts)
{
QTEST_FAULT *f;
f = OPENSSL_zalloc(sizeof(*f));
if (f == NULL)
return NULL;
f->qtserv = ts;
return f;
}
int qtest_supports_blocking(void)
{
#if !defined(OPENSSL_NO_POSIX_IO) && defined(OPENSSL_THREADS) && !defined(CRYPTO_TDEBUG)
return 1;
#else
return 0;
#endif
}
#define MAXLOOPS 1000
#if defined(OPENSSL_THREADS) && !defined(CRYPTO_TDEBUG)
static int globserverret = 0;
static TSAN_QUALIFIER int abortserverthread = 0;
static QUIC_TSERVER *globtserv;
static const thread_t thread_zero;
static void run_server_thread(void)
{
/*
* This will operate in a busy loop because the server does not block,
* but should be acceptable because it is local and we expect this to be
* fast
*/
globserverret = qtest_create_quic_connection(globtserv, NULL);
}
#endif
int qtest_wait_for_timeout(SSL *s, QUIC_TSERVER *qtserv)
{
struct timeval tv;
OSSL_TIME ctimeout, stimeout, mintimeout, now;
int cinf;
/* We don't need to wait in blocking mode */
if (s == NULL || SSL_get_blocking_mode(s))
return 1;
/* Don't wait if either BIO has data waiting */
if (BIO_pending(SSL_get_rbio(s)) > 0
|| BIO_pending(ossl_quic_tserver_get0_rbio(qtserv)) > 0)
return 1;
/*
* Neither endpoint has data waiting to be read. We assume data transmission
* is instantaneous due to using mem based BIOs, so there is no data "in
* flight" and no more data will be sent by either endpoint until some time
* based event has occurred. Therefore, wait for a timeout to occur. This
* might happen if we are using the noisy BIO and datagrams have been lost.
*/
if (!SSL_get_event_timeout(s, &tv, &cinf))
return 0;
add locking around fake_now fake_now in the quictestlib is read/written by potentially many threads, and as such should have a surrounding lock to prevent WAR/RAW errors as caught by tsan: 2023-11-03T16:27:23.7184999Z ================== 2023-11-03T16:27:23.7185290Z WARNING: ThreadSanitizer: data race (pid=18754) 2023-11-03T16:27:23.7185720Z Read of size 8 at 0x558f6f9fe970 by main thread: 2023-11-03T16:27:23.7186726Z #0 qtest_create_quic_connection_ex <null> (quicapitest+0x14aead) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7187665Z #1 qtest_create_quic_connection <null> (quicapitest+0x14b220) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7188567Z #2 test_quic_write_read quicapitest.c (quicapitest+0x150ee2) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7189561Z #3 run_tests <null> (quicapitest+0x2237ab) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7190294Z #4 main <null> (quicapitest+0x223d2b) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7190720Z 2023-11-03T16:27:23.7190902Z Previous write of size 8 at 0x558f6f9fe970 by thread T1: 2023-11-03T16:27:23.7191607Z #0 qtest_create_quic_connection_ex <null> (quicapitest+0x14aecf) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7192505Z #1 run_server_thread quictestlib.c (quicapitest+0x14b1d6) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7193361Z #2 thread_run quictestlib.c (quicapitest+0x14cadf) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7193848Z 2023-11-03T16:27:23.7194220Z Location is global 'fake_now.0' of size 8 at 0x558f6f9fe970 (quicapitest+0x1af4970) 2023-11-03T16:27:23.7194636Z 2023-11-03T16:27:23.7194816Z Thread T1 (tid=18760, running) created by main thread at: 2023-11-03T16:27:23.7195465Z #0 pthread_create <null> (quicapitest+0xca12d) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7196317Z #1 qtest_create_quic_connection_ex <null> (quicapitest+0x14adcb) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7197214Z #2 qtest_create_quic_connection <null> (quicapitest+0x14b220) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7198111Z #3 test_quic_write_read quicapitest.c (quicapitest+0x150ee2) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7198940Z #4 run_tests <null> (quicapitest+0x2237ab) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7199661Z #5 main <null> (quicapitest+0x223d2b) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7200083Z 2023-11-03T16:27:23.7200862Z SUMMARY: ThreadSanitizer: data race (/home/runner/work/openssl/openssl/test/quicapitest+0x14aead) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) in qtest_create_quic_connection_ex Reviewed-by: Hugo Landau <hlandau@openssl.org> Reviewed-by: Paul Dale <pauli@openssl.org> Reviewed-by: Matt Caswell <matt@openssl.org> Reviewed-by: Tomas Mraz <tomas@openssl.org> (Merged from https://github.com/openssl/openssl/pull/22616)
2023-11-04 00:56:40 +08:00
if (using_fake_time)
add locking around fake_now fake_now in the quictestlib is read/written by potentially many threads, and as such should have a surrounding lock to prevent WAR/RAW errors as caught by tsan: 2023-11-03T16:27:23.7184999Z ================== 2023-11-03T16:27:23.7185290Z WARNING: ThreadSanitizer: data race (pid=18754) 2023-11-03T16:27:23.7185720Z Read of size 8 at 0x558f6f9fe970 by main thread: 2023-11-03T16:27:23.7186726Z #0 qtest_create_quic_connection_ex <null> (quicapitest+0x14aead) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7187665Z #1 qtest_create_quic_connection <null> (quicapitest+0x14b220) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7188567Z #2 test_quic_write_read quicapitest.c (quicapitest+0x150ee2) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7189561Z #3 run_tests <null> (quicapitest+0x2237ab) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7190294Z #4 main <null> (quicapitest+0x223d2b) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7190720Z 2023-11-03T16:27:23.7190902Z Previous write of size 8 at 0x558f6f9fe970 by thread T1: 2023-11-03T16:27:23.7191607Z #0 qtest_create_quic_connection_ex <null> (quicapitest+0x14aecf) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7192505Z #1 run_server_thread quictestlib.c (quicapitest+0x14b1d6) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7193361Z #2 thread_run quictestlib.c (quicapitest+0x14cadf) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7193848Z 2023-11-03T16:27:23.7194220Z Location is global 'fake_now.0' of size 8 at 0x558f6f9fe970 (quicapitest+0x1af4970) 2023-11-03T16:27:23.7194636Z 2023-11-03T16:27:23.7194816Z Thread T1 (tid=18760, running) created by main thread at: 2023-11-03T16:27:23.7195465Z #0 pthread_create <null> (quicapitest+0xca12d) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7196317Z #1 qtest_create_quic_connection_ex <null> (quicapitest+0x14adcb) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7197214Z #2 qtest_create_quic_connection <null> (quicapitest+0x14b220) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7198111Z #3 test_quic_write_read quicapitest.c (quicapitest+0x150ee2) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7198940Z #4 run_tests <null> (quicapitest+0x2237ab) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7199661Z #5 main <null> (quicapitest+0x223d2b) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7200083Z 2023-11-03T16:27:23.7200862Z SUMMARY: ThreadSanitizer: data race (/home/runner/work/openssl/openssl/test/quicapitest+0x14aead) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) in qtest_create_quic_connection_ex Reviewed-by: Hugo Landau <hlandau@openssl.org> Reviewed-by: Paul Dale <pauli@openssl.org> Reviewed-by: Matt Caswell <matt@openssl.org> Reviewed-by: Tomas Mraz <tomas@openssl.org> (Merged from https://github.com/openssl/openssl/pull/22616)
2023-11-04 00:56:40 +08:00
now = qtest_get_time();
else
now = ossl_time_now();
add locking around fake_now fake_now in the quictestlib is read/written by potentially many threads, and as such should have a surrounding lock to prevent WAR/RAW errors as caught by tsan: 2023-11-03T16:27:23.7184999Z ================== 2023-11-03T16:27:23.7185290Z WARNING: ThreadSanitizer: data race (pid=18754) 2023-11-03T16:27:23.7185720Z Read of size 8 at 0x558f6f9fe970 by main thread: 2023-11-03T16:27:23.7186726Z #0 qtest_create_quic_connection_ex <null> (quicapitest+0x14aead) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7187665Z #1 qtest_create_quic_connection <null> (quicapitest+0x14b220) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7188567Z #2 test_quic_write_read quicapitest.c (quicapitest+0x150ee2) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7189561Z #3 run_tests <null> (quicapitest+0x2237ab) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7190294Z #4 main <null> (quicapitest+0x223d2b) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7190720Z 2023-11-03T16:27:23.7190902Z Previous write of size 8 at 0x558f6f9fe970 by thread T1: 2023-11-03T16:27:23.7191607Z #0 qtest_create_quic_connection_ex <null> (quicapitest+0x14aecf) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7192505Z #1 run_server_thread quictestlib.c (quicapitest+0x14b1d6) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7193361Z #2 thread_run quictestlib.c (quicapitest+0x14cadf) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7193848Z 2023-11-03T16:27:23.7194220Z Location is global 'fake_now.0' of size 8 at 0x558f6f9fe970 (quicapitest+0x1af4970) 2023-11-03T16:27:23.7194636Z 2023-11-03T16:27:23.7194816Z Thread T1 (tid=18760, running) created by main thread at: 2023-11-03T16:27:23.7195465Z #0 pthread_create <null> (quicapitest+0xca12d) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7196317Z #1 qtest_create_quic_connection_ex <null> (quicapitest+0x14adcb) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7197214Z #2 qtest_create_quic_connection <null> (quicapitest+0x14b220) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7198111Z #3 test_quic_write_read quicapitest.c (quicapitest+0x150ee2) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7198940Z #4 run_tests <null> (quicapitest+0x2237ab) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7199661Z #5 main <null> (quicapitest+0x223d2b) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7200083Z 2023-11-03T16:27:23.7200862Z SUMMARY: ThreadSanitizer: data race (/home/runner/work/openssl/openssl/test/quicapitest+0x14aead) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) in qtest_create_quic_connection_ex Reviewed-by: Hugo Landau <hlandau@openssl.org> Reviewed-by: Paul Dale <pauli@openssl.org> Reviewed-by: Matt Caswell <matt@openssl.org> Reviewed-by: Tomas Mraz <tomas@openssl.org> (Merged from https://github.com/openssl/openssl/pull/22616)
2023-11-04 00:56:40 +08:00
ctimeout = cinf ? ossl_time_infinite() : ossl_time_from_timeval(tv);
stimeout = ossl_time_subtract(ossl_quic_tserver_get_deadline(qtserv), now);
mintimeout = ossl_time_min(ctimeout, stimeout);
if (ossl_time_is_infinite(mintimeout))
return 0;
add locking around fake_now fake_now in the quictestlib is read/written by potentially many threads, and as such should have a surrounding lock to prevent WAR/RAW errors as caught by tsan: 2023-11-03T16:27:23.7184999Z ================== 2023-11-03T16:27:23.7185290Z WARNING: ThreadSanitizer: data race (pid=18754) 2023-11-03T16:27:23.7185720Z Read of size 8 at 0x558f6f9fe970 by main thread: 2023-11-03T16:27:23.7186726Z #0 qtest_create_quic_connection_ex <null> (quicapitest+0x14aead) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7187665Z #1 qtest_create_quic_connection <null> (quicapitest+0x14b220) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7188567Z #2 test_quic_write_read quicapitest.c (quicapitest+0x150ee2) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7189561Z #3 run_tests <null> (quicapitest+0x2237ab) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7190294Z #4 main <null> (quicapitest+0x223d2b) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7190720Z 2023-11-03T16:27:23.7190902Z Previous write of size 8 at 0x558f6f9fe970 by thread T1: 2023-11-03T16:27:23.7191607Z #0 qtest_create_quic_connection_ex <null> (quicapitest+0x14aecf) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7192505Z #1 run_server_thread quictestlib.c (quicapitest+0x14b1d6) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7193361Z #2 thread_run quictestlib.c (quicapitest+0x14cadf) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7193848Z 2023-11-03T16:27:23.7194220Z Location is global 'fake_now.0' of size 8 at 0x558f6f9fe970 (quicapitest+0x1af4970) 2023-11-03T16:27:23.7194636Z 2023-11-03T16:27:23.7194816Z Thread T1 (tid=18760, running) created by main thread at: 2023-11-03T16:27:23.7195465Z #0 pthread_create <null> (quicapitest+0xca12d) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7196317Z #1 qtest_create_quic_connection_ex <null> (quicapitest+0x14adcb) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7197214Z #2 qtest_create_quic_connection <null> (quicapitest+0x14b220) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7198111Z #3 test_quic_write_read quicapitest.c (quicapitest+0x150ee2) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7198940Z #4 run_tests <null> (quicapitest+0x2237ab) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7199661Z #5 main <null> (quicapitest+0x223d2b) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7200083Z 2023-11-03T16:27:23.7200862Z SUMMARY: ThreadSanitizer: data race (/home/runner/work/openssl/openssl/test/quicapitest+0x14aead) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) in qtest_create_quic_connection_ex Reviewed-by: Hugo Landau <hlandau@openssl.org> Reviewed-by: Paul Dale <pauli@openssl.org> Reviewed-by: Matt Caswell <matt@openssl.org> Reviewed-by: Tomas Mraz <tomas@openssl.org> (Merged from https://github.com/openssl/openssl/pull/22616)
2023-11-04 00:56:40 +08:00
if (using_fake_time)
add locking around fake_now fake_now in the quictestlib is read/written by potentially many threads, and as such should have a surrounding lock to prevent WAR/RAW errors as caught by tsan: 2023-11-03T16:27:23.7184999Z ================== 2023-11-03T16:27:23.7185290Z WARNING: ThreadSanitizer: data race (pid=18754) 2023-11-03T16:27:23.7185720Z Read of size 8 at 0x558f6f9fe970 by main thread: 2023-11-03T16:27:23.7186726Z #0 qtest_create_quic_connection_ex <null> (quicapitest+0x14aead) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7187665Z #1 qtest_create_quic_connection <null> (quicapitest+0x14b220) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7188567Z #2 test_quic_write_read quicapitest.c (quicapitest+0x150ee2) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7189561Z #3 run_tests <null> (quicapitest+0x2237ab) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7190294Z #4 main <null> (quicapitest+0x223d2b) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7190720Z 2023-11-03T16:27:23.7190902Z Previous write of size 8 at 0x558f6f9fe970 by thread T1: 2023-11-03T16:27:23.7191607Z #0 qtest_create_quic_connection_ex <null> (quicapitest+0x14aecf) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7192505Z #1 run_server_thread quictestlib.c (quicapitest+0x14b1d6) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7193361Z #2 thread_run quictestlib.c (quicapitest+0x14cadf) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7193848Z 2023-11-03T16:27:23.7194220Z Location is global 'fake_now.0' of size 8 at 0x558f6f9fe970 (quicapitest+0x1af4970) 2023-11-03T16:27:23.7194636Z 2023-11-03T16:27:23.7194816Z Thread T1 (tid=18760, running) created by main thread at: 2023-11-03T16:27:23.7195465Z #0 pthread_create <null> (quicapitest+0xca12d) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7196317Z #1 qtest_create_quic_connection_ex <null> (quicapitest+0x14adcb) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7197214Z #2 qtest_create_quic_connection <null> (quicapitest+0x14b220) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7198111Z #3 test_quic_write_read quicapitest.c (quicapitest+0x150ee2) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7198940Z #4 run_tests <null> (quicapitest+0x2237ab) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7199661Z #5 main <null> (quicapitest+0x223d2b) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) 2023-11-03T16:27:23.7200083Z 2023-11-03T16:27:23.7200862Z SUMMARY: ThreadSanitizer: data race (/home/runner/work/openssl/openssl/test/quicapitest+0x14aead) (BuildId: d06f7b04830b55de9c8482b398a1781472d1c7d5) in qtest_create_quic_connection_ex Reviewed-by: Hugo Landau <hlandau@openssl.org> Reviewed-by: Paul Dale <pauli@openssl.org> Reviewed-by: Matt Caswell <matt@openssl.org> Reviewed-by: Tomas Mraz <tomas@openssl.org> (Merged from https://github.com/openssl/openssl/pull/22616)
2023-11-04 00:56:40 +08:00
qtest_add_time(ossl_time2ms(mintimeout));
else
OSSL_sleep(ossl_time2ms(mintimeout));
return 1;
}
int qtest_create_quic_connection_ex(QUIC_TSERVER *qtserv, SSL *clientssl,
int wanterr)
{
int retc = -1, rets = 0, abortctr = 0, ret = 0;
int clienterr = 0, servererr = 0;
#if defined(OPENSSL_THREADS) && !defined(CRYPTO_TDEBUG)
Attempt to fix occasional failure of quicapi test in ci https://github.com/openssl/openssl/actions/runs/15214054228/job/42795224720 the theory I have for the cause of this failure is: 1. qtest_create_quic_connection_ex is called for the client 2. The client is in blocking mode, so we fall into the conditional on line 512 3. We create the server thread on line 519, which is non-blocking 4. The scheduler in the failing case, lets the server run ahead of the client 5. Server thread enters qtest_create_quic_connection_ex and iterates steps 6-9 in the do_while loop starting on line 530 6. Server calls qtest_add_time 7. Server calls ossl_quic_tserver_tick 8. Server calls ossl_quic_tserver_is_term_any, received NULL return 9. Server calls qtest_wait_for_timeout 10. Eventually qtest_wait_for_timeout returns zero, adn the server jumps to the error label, returning zero to globservret, and the thread exits 11. Client thread regains the cpu, and attempts to call SSL_connect, which fails, as the server is no longer listening 12. We fall into the error case on line 556, and SSL_get_error returns SSL_ERROR_SSL, which causes clienterr to get set to 1 13. We exit the do{} while loop on line 581, and do the TEST_true check on line 593. The server having exited wait_for_thread returns true, but globserverret is still zero from step 10 above, and so the test fails I can't prove this is the case, as the test only appears to fail in CI, and we can't dump verbose logging there, lest we affect the timing of the tests, so this is just a theory, but it seems to fit the observations we have. Attempting to fix this, by creating a thread interlock with a condition variable that blocks the server from ticking the quic reactor until such time as the client is about to call SSL_connect to prevent the race condition Reviewed-by: Saša Nedvědický <sashan@openssl.org> Reviewed-by: Tomas Mraz <tomas@openssl.org> (Merged from https://github.com/openssl/openssl/pull/27704)
2025-05-27 21:36:35 +08:00
/*
* Pointless initialisation to avoid bogus compiler warnings about using
* t uninitialised
*/
thread_t t = thread_zero;
if (clientssl != NULL)
abortserverthread = 0;
Attempt to fix occasional failure of quicapi test in ci https://github.com/openssl/openssl/actions/runs/15214054228/job/42795224720 the theory I have for the cause of this failure is: 1. qtest_create_quic_connection_ex is called for the client 2. The client is in blocking mode, so we fall into the conditional on line 512 3. We create the server thread on line 519, which is non-blocking 4. The scheduler in the failing case, lets the server run ahead of the client 5. Server thread enters qtest_create_quic_connection_ex and iterates steps 6-9 in the do_while loop starting on line 530 6. Server calls qtest_add_time 7. Server calls ossl_quic_tserver_tick 8. Server calls ossl_quic_tserver_is_term_any, received NULL return 9. Server calls qtest_wait_for_timeout 10. Eventually qtest_wait_for_timeout returns zero, adn the server jumps to the error label, returning zero to globservret, and the thread exits 11. Client thread regains the cpu, and attempts to call SSL_connect, which fails, as the server is no longer listening 12. We fall into the error case on line 556, and SSL_get_error returns SSL_ERROR_SSL, which causes clienterr to get set to 1 13. We exit the do{} while loop on line 581, and do the TEST_true check on line 593. The server having exited wait_for_thread returns true, but globserverret is still zero from step 10 above, and so the test fails I can't prove this is the case, as the test only appears to fail in CI, and we can't dump verbose logging there, lest we affect the timing of the tests, so this is just a theory, but it seems to fit the observations we have. Attempting to fix this, by creating a thread interlock with a condition variable that blocks the server from ticking the quic reactor until such time as the client is about to call SSL_connect to prevent the race condition Reviewed-by: Saša Nedvědický <sashan@openssl.org> Reviewed-by: Tomas Mraz <tomas@openssl.org> (Merged from https://github.com/openssl/openssl/pull/27704)
2025-05-27 21:36:35 +08:00
/*
* Only set the client_ready flag to zero if we are the client
*/
if (clientssl != NULL) {
ossl_crypto_mutex_lock(client_ready_mutex);
client_ready = 0;
ossl_crypto_mutex_unlock(client_ready_mutex);
}
#endif
if (!TEST_ptr(qtserv)) {
goto err;
} else if (clientssl == NULL) {
retc = 1;
} else if (SSL_get_blocking_mode(clientssl) > 0) {
#if defined(OPENSSL_THREADS) && !defined(CRYPTO_TDEBUG)
/*
* clientssl is blocking. We will need a thread to complete the
* connection
*/
globtserv = qtserv;
if (!TEST_true(run_thread(&t, run_server_thread)))
goto err;
qtserv = NULL;
rets = 1;
#else
TEST_error("No thread support in this build");
goto err;
#endif
}
do {
if (!clienterr && retc <= 0) {
int err;
Attempt to fix occasional failure of quicapi test in ci https://github.com/openssl/openssl/actions/runs/15214054228/job/42795224720 the theory I have for the cause of this failure is: 1. qtest_create_quic_connection_ex is called for the client 2. The client is in blocking mode, so we fall into the conditional on line 512 3. We create the server thread on line 519, which is non-blocking 4. The scheduler in the failing case, lets the server run ahead of the client 5. Server thread enters qtest_create_quic_connection_ex and iterates steps 6-9 in the do_while loop starting on line 530 6. Server calls qtest_add_time 7. Server calls ossl_quic_tserver_tick 8. Server calls ossl_quic_tserver_is_term_any, received NULL return 9. Server calls qtest_wait_for_timeout 10. Eventually qtest_wait_for_timeout returns zero, adn the server jumps to the error label, returning zero to globservret, and the thread exits 11. Client thread regains the cpu, and attempts to call SSL_connect, which fails, as the server is no longer listening 12. We fall into the error case on line 556, and SSL_get_error returns SSL_ERROR_SSL, which causes clienterr to get set to 1 13. We exit the do{} while loop on line 581, and do the TEST_true check on line 593. The server having exited wait_for_thread returns true, but globserverret is still zero from step 10 above, and so the test fails I can't prove this is the case, as the test only appears to fail in CI, and we can't dump verbose logging there, lest we affect the timing of the tests, so this is just a theory, but it seems to fit the observations we have. Attempting to fix this, by creating a thread interlock with a condition variable that blocks the server from ticking the quic reactor until such time as the client is about to call SSL_connect to prevent the race condition Reviewed-by: Saša Nedvědický <sashan@openssl.org> Reviewed-by: Tomas Mraz <tomas@openssl.org> (Merged from https://github.com/openssl/openssl/pull/27704)
2025-05-27 21:36:35 +08:00
#if defined(OPENSSL_THREADS) && !defined(CRYPTO_TDEBUG)
ossl_crypto_mutex_lock(client_ready_mutex);
client_ready = 1;
ossl_crypto_condvar_broadcast(client_ready_cond);
ossl_crypto_mutex_unlock(client_ready_mutex);
#endif
retc = SSL_connect(clientssl);
if (retc <= 0) {
err = SSL_get_error(clientssl, retc);
if (err == wanterr) {
retc = 1;
#if defined(OPENSSL_THREADS) && !defined(CRYPTO_TDEBUG)
if (qtserv == NULL && rets > 0)
tsan_store(&abortserverthread, 1);
else
#endif
rets = 1;
} else {
if (err != SSL_ERROR_WANT_READ
&& err != SSL_ERROR_WANT_WRITE) {
TEST_info("SSL_connect() failed %d, %d", retc, err);
TEST_openssl_errors();
clienterr = 1;
}
}
}
}
qtest_add_time(1);
if (clientssl != NULL)
SSL_handle_events(clientssl);
Attempt to fix occasional failure of quicapi test in ci https://github.com/openssl/openssl/actions/runs/15214054228/job/42795224720 the theory I have for the cause of this failure is: 1. qtest_create_quic_connection_ex is called for the client 2. The client is in blocking mode, so we fall into the conditional on line 512 3. We create the server thread on line 519, which is non-blocking 4. The scheduler in the failing case, lets the server run ahead of the client 5. Server thread enters qtest_create_quic_connection_ex and iterates steps 6-9 in the do_while loop starting on line 530 6. Server calls qtest_add_time 7. Server calls ossl_quic_tserver_tick 8. Server calls ossl_quic_tserver_is_term_any, received NULL return 9. Server calls qtest_wait_for_timeout 10. Eventually qtest_wait_for_timeout returns zero, adn the server jumps to the error label, returning zero to globservret, and the thread exits 11. Client thread regains the cpu, and attempts to call SSL_connect, which fails, as the server is no longer listening 12. We fall into the error case on line 556, and SSL_get_error returns SSL_ERROR_SSL, which causes clienterr to get set to 1 13. We exit the do{} while loop on line 581, and do the TEST_true check on line 593. The server having exited wait_for_thread returns true, but globserverret is still zero from step 10 above, and so the test fails I can't prove this is the case, as the test only appears to fail in CI, and we can't dump verbose logging there, lest we affect the timing of the tests, so this is just a theory, but it seems to fit the observations we have. Attempting to fix this, by creating a thread interlock with a condition variable that blocks the server from ticking the quic reactor until such time as the client is about to call SSL_connect to prevent the race condition Reviewed-by: Saša Nedvědický <sashan@openssl.org> Reviewed-by: Tomas Mraz <tomas@openssl.org> (Merged from https://github.com/openssl/openssl/pull/27704)
2025-05-27 21:36:35 +08:00
if (qtserv != NULL) {
#if defined(OPENSSL_THREADS) && !defined(CRYPTO_TDEBUG)
ossl_crypto_mutex_lock(client_ready_mutex);
for (;;) {
if (client_ready == 1)
break;
ossl_crypto_condvar_wait(client_ready_cond, client_ready_mutex);
}
ossl_crypto_mutex_unlock(client_ready_mutex);
#endif
ossl_quic_tserver_tick(qtserv);
Attempt to fix occasional failure of quicapi test in ci https://github.com/openssl/openssl/actions/runs/15214054228/job/42795224720 the theory I have for the cause of this failure is: 1. qtest_create_quic_connection_ex is called for the client 2. The client is in blocking mode, so we fall into the conditional on line 512 3. We create the server thread on line 519, which is non-blocking 4. The scheduler in the failing case, lets the server run ahead of the client 5. Server thread enters qtest_create_quic_connection_ex and iterates steps 6-9 in the do_while loop starting on line 530 6. Server calls qtest_add_time 7. Server calls ossl_quic_tserver_tick 8. Server calls ossl_quic_tserver_is_term_any, received NULL return 9. Server calls qtest_wait_for_timeout 10. Eventually qtest_wait_for_timeout returns zero, adn the server jumps to the error label, returning zero to globservret, and the thread exits 11. Client thread regains the cpu, and attempts to call SSL_connect, which fails, as the server is no longer listening 12. We fall into the error case on line 556, and SSL_get_error returns SSL_ERROR_SSL, which causes clienterr to get set to 1 13. We exit the do{} while loop on line 581, and do the TEST_true check on line 593. The server having exited wait_for_thread returns true, but globserverret is still zero from step 10 above, and so the test fails I can't prove this is the case, as the test only appears to fail in CI, and we can't dump verbose logging there, lest we affect the timing of the tests, so this is just a theory, but it seems to fit the observations we have. Attempting to fix this, by creating a thread interlock with a condition variable that blocks the server from ticking the quic reactor until such time as the client is about to call SSL_connect to prevent the race condition Reviewed-by: Saša Nedvědický <sashan@openssl.org> Reviewed-by: Tomas Mraz <tomas@openssl.org> (Merged from https://github.com/openssl/openssl/pull/27704)
2025-05-27 21:36:35 +08:00
}
if (!servererr && rets <= 0) {
servererr = ossl_quic_tserver_is_term_any(qtserv);
if (!servererr)
rets = ossl_quic_tserver_is_handshake_confirmed(qtserv);
}
if (clienterr && servererr)
goto err;
if (clientssl != NULL && ++abortctr == MAXLOOPS) {
TEST_info("No progress made");
goto err;
}
if ((retc <= 0 && !clienterr) || (rets <= 0 && !servererr)) {
if (!qtest_wait_for_timeout(clientssl, qtserv))
goto err;
}
} while ((retc <= 0 && !clienterr)
|| (rets <= 0 && !servererr
#if defined(OPENSSL_THREADS) && !defined(CRYPTO_TDEBUG)
&& !tsan_load(&abortserverthread)
#endif
));
if (qtserv == NULL && rets > 0) {
#if defined(OPENSSL_THREADS) && !defined(CRYPTO_TDEBUG)
Attempt to fix occasional failure of quicapi test in ci https://github.com/openssl/openssl/actions/runs/15214054228/job/42795224720 the theory I have for the cause of this failure is: 1. qtest_create_quic_connection_ex is called for the client 2. The client is in blocking mode, so we fall into the conditional on line 512 3. We create the server thread on line 519, which is non-blocking 4. The scheduler in the failing case, lets the server run ahead of the client 5. Server thread enters qtest_create_quic_connection_ex and iterates steps 6-9 in the do_while loop starting on line 530 6. Server calls qtest_add_time 7. Server calls ossl_quic_tserver_tick 8. Server calls ossl_quic_tserver_is_term_any, received NULL return 9. Server calls qtest_wait_for_timeout 10. Eventually qtest_wait_for_timeout returns zero, adn the server jumps to the error label, returning zero to globservret, and the thread exits 11. Client thread regains the cpu, and attempts to call SSL_connect, which fails, as the server is no longer listening 12. We fall into the error case on line 556, and SSL_get_error returns SSL_ERROR_SSL, which causes clienterr to get set to 1 13. We exit the do{} while loop on line 581, and do the TEST_true check on line 593. The server having exited wait_for_thread returns true, but globserverret is still zero from step 10 above, and so the test fails I can't prove this is the case, as the test only appears to fail in CI, and we can't dump verbose logging there, lest we affect the timing of the tests, so this is just a theory, but it seems to fit the observations we have. Attempting to fix this, by creating a thread interlock with a condition variable that blocks the server from ticking the quic reactor until such time as the client is about to call SSL_connect to prevent the race condition Reviewed-by: Saša Nedvědický <sashan@openssl.org> Reviewed-by: Tomas Mraz <tomas@openssl.org> (Merged from https://github.com/openssl/openssl/pull/27704)
2025-05-27 21:36:35 +08:00
/*
* Make sure we unblock the server before we wait on completion here
* in case it didn't happen in the connect loop above
*/
ossl_crypto_mutex_lock(client_ready_mutex);
client_ready = 1;
ossl_crypto_condvar_broadcast(client_ready_cond);
ossl_crypto_mutex_unlock(client_ready_mutex);
if (!TEST_true(wait_for_thread(t)) || !TEST_true(globserverret))
goto err;
#else
TEST_error("Should not happen");
goto err;
#endif
}
if (!clienterr && !servererr)
ret = 1;
err:
return ret;
}
int qtest_create_quic_connection(QUIC_TSERVER *qtserv, SSL *clientssl)
{
return qtest_create_quic_connection_ex(qtserv, clientssl, SSL_ERROR_NONE);
}
#if defined(OPENSSL_THREADS) && !defined(CRYPTO_TDEBUG)
static TSAN_QUALIFIER int shutdowndone;
static void run_server_shutdown_thread(void)
{
/*
* This will operate in a busy loop because the server does not block,
* but should be acceptable because it is local and we expect this to be
* fast
*/
do {
ossl_quic_tserver_tick(globtserv);
} while(!tsan_load(&shutdowndone));
}
#endif
int qtest_shutdown(QUIC_TSERVER *qtserv, SSL *clientssl)
{
int tickserver = 1;
int ret = 0;
#if defined(OPENSSL_THREADS) && !defined(CRYPTO_TDEBUG)
/*
* Pointless initialisation to avoid bogus compiler warnings about using
* t uninitialised
*/
thread_t t = thread_zero;
Attempt to fix occasional failure of quicapi test in ci https://github.com/openssl/openssl/actions/runs/15214054228/job/42795224720 the theory I have for the cause of this failure is: 1. qtest_create_quic_connection_ex is called for the client 2. The client is in blocking mode, so we fall into the conditional on line 512 3. We create the server thread on line 519, which is non-blocking 4. The scheduler in the failing case, lets the server run ahead of the client 5. Server thread enters qtest_create_quic_connection_ex and iterates steps 6-9 in the do_while loop starting on line 530 6. Server calls qtest_add_time 7. Server calls ossl_quic_tserver_tick 8. Server calls ossl_quic_tserver_is_term_any, received NULL return 9. Server calls qtest_wait_for_timeout 10. Eventually qtest_wait_for_timeout returns zero, adn the server jumps to the error label, returning zero to globservret, and the thread exits 11. Client thread regains the cpu, and attempts to call SSL_connect, which fails, as the server is no longer listening 12. We fall into the error case on line 556, and SSL_get_error returns SSL_ERROR_SSL, which causes clienterr to get set to 1 13. We exit the do{} while loop on line 581, and do the TEST_true check on line 593. The server having exited wait_for_thread returns true, but globserverret is still zero from step 10 above, and so the test fails I can't prove this is the case, as the test only appears to fail in CI, and we can't dump verbose logging there, lest we affect the timing of the tests, so this is just a theory, but it seems to fit the observations we have. Attempting to fix this, by creating a thread interlock with a condition variable that blocks the server from ticking the quic reactor until such time as the client is about to call SSL_connect to prevent the race condition Reviewed-by: Saša Nedvědický <sashan@openssl.org> Reviewed-by: Tomas Mraz <tomas@openssl.org> (Merged from https://github.com/openssl/openssl/pull/27704)
2025-05-27 21:36:35 +08:00
ossl_crypto_condvar_free(&client_ready_cond);
client_ready_cond = NULL;
ossl_crypto_mutex_free(&client_ready_mutex);
client_ready_mutex = NULL;
#endif
if (SSL_get_blocking_mode(clientssl) > 0) {
#if defined(OPENSSL_THREADS) && !defined(CRYPTO_TDEBUG)
/*
* clientssl is blocking. We will need a thread to complete the
* connection
*/
globtserv = qtserv;
shutdowndone = 0;
if (!TEST_true(run_thread(&t, run_server_shutdown_thread)))
return 0;
tickserver = 0;
#else
TEST_error("No thread support in this build");
return 0;
#endif
}
/* Busy loop in non-blocking mode. It should be quick because its local */
for (;;) {
int rc = SSL_shutdown(clientssl);
if (rc == 1) {
ret = 1;
break;
}
if (rc < 0)
break;
if (tickserver)
ossl_quic_tserver_tick(qtserv);
}
#if defined(OPENSSL_THREADS) && !defined(CRYPTO_TDEBUG)
tsan_store(&shutdowndone, 1);
if (!tickserver) {
if (!TEST_true(wait_for_thread(t)))
ret = 0;
}
#endif
return ret;
}
int qtest_check_server_transport_err(QUIC_TSERVER *qtserv, uint64_t code)
{
const QUIC_TERMINATE_CAUSE *cause;
ossl_quic_tserver_tick(qtserv);
/*
* Check that the server has closed with the specified code from the client
*/
if (!TEST_true(ossl_quic_tserver_is_term_any(qtserv)))
return 0;
cause = ossl_quic_tserver_get_terminate_cause(qtserv);
if (!TEST_ptr(cause)
|| !TEST_true(cause->remote)
|| !TEST_false(cause->app)
|| !TEST_uint64_t_eq(cause->error_code, code))
return 0;
return 1;
}
int qtest_check_server_protocol_err(QUIC_TSERVER *qtserv)
{
return qtest_check_server_transport_err(qtserv, OSSL_QUIC_ERR_PROTOCOL_VIOLATION);
}
int qtest_check_server_frame_encoding_err(QUIC_TSERVER *qtserv)
{
return qtest_check_server_transport_err(qtserv, OSSL_QUIC_ERR_FRAME_ENCODING_ERROR);
}
void qtest_fault_free(QTEST_FAULT *fault)
{
if (fault == NULL)
return;
packet_plain_finish(fault);
handshake_finish(fault);
OPENSSL_free(fault);
}
static int packet_plain_mutate(const QUIC_PKT_HDR *hdrin,
const OSSL_QTX_IOVEC *iovecin, size_t numin,
QUIC_PKT_HDR **hdrout,
const OSSL_QTX_IOVEC **iovecout,
size_t *numout,
void *arg)
{
QTEST_FAULT *fault = arg;
size_t i, bufsz = 0;
unsigned char *cur;
int grow_allowance;
/* Coalesce our data into a single buffer */
/* First calculate required buffer size */
for (i = 0; i < numin; i++)
bufsz += iovecin[i].buf_len;
fault->pplainio.buf_len = bufsz;
/*
* 1200 is QUIC payload length we use
* bufsz is what we got from txp
* 16 is the length of tag added by encryption
* 14 long header (we assume token length is 0,
* which is fine for server not so fine for client)
*/
grow_allowance = 1200 - bufsz - 16 - 14;
grow_allowance -= hdrin->dst_conn_id.id_len;
grow_allowance -= hdrin->src_conn_id.id_len;
assert(grow_allowance >= 0);
bufsz += grow_allowance;
fault->pplainio.buf = cur = OPENSSL_malloc(bufsz);
if (cur == NULL) {
fault->pplainio.buf_len = 0;
return 0;
}
fault->pplainbuf_alloc = bufsz;
/* Copy in the data from the input buffers */
for (i = 0; i < numin; i++) {
memcpy(cur, iovecin[i].buf, iovecin[i].buf_len);
cur += iovecin[i].buf_len;
}
fault->pplainhdr = *hdrin;
/*
* Cast below is safe because we allocated the buffer
* mutation is best effort. we can inject frame if
* there is enough space. If there is not enough space
* we must give up.
*/
if (fault->pplaincb != NULL)
fault->pplaincb(fault, &fault->pplainhdr,
(unsigned char *)fault->pplainio.buf,
fault->pplainio.buf_len, fault->pplaincbarg);
*hdrout = &fault->pplainhdr;
*iovecout = &fault->pplainio;
*numout = 1;
return 1;
}
static void packet_plain_finish(void *arg)
{
QTEST_FAULT *fault = arg;
/* Cast below is safe because we allocated the buffer */
OPENSSL_free((unsigned char *)fault->pplainio.buf);
fault->pplainio.buf_len = 0;
fault->pplainbuf_alloc = 0;
fault->pplainio.buf = NULL;
}
int qtest_fault_set_packet_plain_listener(QTEST_FAULT *fault,
qtest_fault_on_packet_plain_cb pplaincb,
void *pplaincbarg)
{
fault->pplaincb = pplaincb;
fault->pplaincbarg = pplaincbarg;
return ossl_quic_tserver_set_plain_packet_mutator(fault->qtserv,
packet_plain_mutate,
packet_plain_finish,
fault);
}
/* To be called from a packet_plain_listener callback */
int qtest_fault_resize_plain_packet(QTEST_FAULT *fault, size_t newlen)
{
unsigned char *buf;
size_t oldlen = fault->pplainio.buf_len;
/*
* Alloc'd size should always be non-zero, so if this fails we've been
* incorrectly called
*/
if (fault->pplainbuf_alloc == 0)
return 0;
if (newlen > fault->pplainbuf_alloc) {
/* This exceeds our growth allowance. Fail */
return 0;
}
/* Cast below is safe because we allocated the buffer */
buf = (unsigned char *)fault->pplainio.buf;
if (newlen > oldlen) {
/* Extend packet with 0 bytes */
memset(buf + oldlen, 0, newlen - oldlen);
} /* else we're truncating or staying the same */
fault->pplainio.buf_len = newlen;
fault->pplainhdr.len = newlen;
return 1;
}
/*
* Prepend frame data into a packet. To be called from a packet_plain_listener
* callback
*/
int qtest_fault_prepend_frame(QTEST_FAULT *fault, const unsigned char *frame,
size_t frame_len)
{
unsigned char *buf;
size_t old_len;
/*
* Alloc'd size should always be non-zero, so if this fails we've been
* incorrectly called
*/
if (fault->pplainbuf_alloc == 0)
return 0;
/* Cast below is safe because we allocated the buffer */
buf = (unsigned char *)fault->pplainio.buf;
old_len = fault->pplainio.buf_len;
/* Extend the size of the packet by the size of the new frame */
if (!TEST_true(qtest_fault_resize_plain_packet(fault,
old_len + frame_len)))
return 0;
memmove(buf + frame_len, buf, old_len);
memcpy(buf, frame, frame_len);
return 1;
}
static int handshake_mutate(const unsigned char *msgin, size_t msginlen,
unsigned char **msgout, size_t *msgoutlen,
void *arg)
{
QTEST_FAULT *fault = arg;
unsigned char *buf;
unsigned long payloadlen;
unsigned int msgtype;
PACKET pkt;
buf = OPENSSL_malloc(msginlen + GROWTH_ALLOWANCE);
if (buf == NULL)
return 0;
fault->handbuf = buf;
fault->handbuflen = msginlen;
fault->handbufalloc = msginlen + GROWTH_ALLOWANCE;
memcpy(buf, msgin, msginlen);
if (!PACKET_buf_init(&pkt, buf, msginlen)
|| !PACKET_get_1(&pkt, &msgtype)
|| !PACKET_get_net_3(&pkt, &payloadlen)
|| PACKET_remaining(&pkt) != payloadlen)
return 0;
/* Parse specific message types */
switch (msgtype) {
case SSL3_MT_ENCRYPTED_EXTENSIONS:
{
QTEST_ENCRYPTED_EXTENSIONS ee;
if (fault->encextcb == NULL)
break;
/*
* The EncryptedExtensions message is very simple. It just has an
* extensions block in it and nothing else.
*/
ee.extensions = (unsigned char *)PACKET_data(&pkt);
ee.extensionslen = payloadlen;
if (!fault->encextcb(fault, &ee, payloadlen, fault->encextcbarg))
return 0;
}
default:
/* No specific handlers for these message types yet */
break;
}
if (fault->handshakecb != NULL
&& !fault->handshakecb(fault, buf, fault->handbuflen,
fault->handshakecbarg))
return 0;
*msgout = buf;
*msgoutlen = fault->handbuflen;
return 1;
}
static void handshake_finish(void *arg)
{
QTEST_FAULT *fault = arg;
OPENSSL_free(fault->handbuf);
fault->handbuf = NULL;
}
int qtest_fault_set_handshake_listener(QTEST_FAULT *fault,
qtest_fault_on_handshake_cb handshakecb,
void *handshakecbarg)
{
fault->handshakecb = handshakecb;
fault->handshakecbarg = handshakecbarg;
return ossl_quic_tserver_set_handshake_mutator(fault->qtserv,
handshake_mutate,
handshake_finish,
fault);
}
int qtest_fault_set_hand_enc_ext_listener(QTEST_FAULT *fault,
qtest_fault_on_enc_ext_cb encextcb,
void *encextcbarg)
{
fault->encextcb = encextcb;
fault->encextcbarg = encextcbarg;
return ossl_quic_tserver_set_handshake_mutator(fault->qtserv,
handshake_mutate,
handshake_finish,
fault);
}
/* To be called from a handshake_listener callback */
int qtest_fault_resize_handshake(QTEST_FAULT *fault, size_t newlen)
{
unsigned char *buf;
size_t oldlen = fault->handbuflen;
/*
* Alloc'd size should always be non-zero, so if this fails we've been
* incorrectly called
*/
if (fault->handbufalloc == 0)
return 0;
if (newlen > fault->handbufalloc) {
/* This exceeds our growth allowance. Fail */
return 0;
}
buf = (unsigned char *)fault->handbuf;
if (newlen > oldlen) {
/* Extend packet with 0 bytes */
memset(buf + oldlen, 0, newlen - oldlen);
} /* else we're truncating or staying the same */
fault->handbuflen = newlen;
return 1;
}
/* To be called from message specific listener callbacks */
int qtest_fault_resize_message(QTEST_FAULT *fault, size_t newlen)
{
/* First resize the underlying message */
if (!qtest_fault_resize_handshake(fault, newlen + SSL3_HM_HEADER_LENGTH))
return 0;
/* Fixup the handshake message header */
fault->handbuf[1] = (unsigned char)((newlen >> 16) & 0xff);
fault->handbuf[2] = (unsigned char)((newlen >> 8) & 0xff);
fault->handbuf[3] = (unsigned char)((newlen ) & 0xff);
return 1;
}
int qtest_fault_delete_extension(QTEST_FAULT *fault,
unsigned int exttype, unsigned char *ext,
size_t *extlen,
BUF_MEM *old_ext)
{
PACKET pkt, sub, subext;
WPACKET old_ext_wpkt;
unsigned int type;
const unsigned char *start, *end;
size_t newlen, w;
size_t msglen = fault->handbuflen;
if (!PACKET_buf_init(&pkt, ext, *extlen))
return 0;
/* Extension block starts with 2 bytes for extension block length */
if (!PACKET_as_length_prefixed_2(&pkt, &sub))
return 0;
do {
start = PACKET_data(&sub);
if (!PACKET_get_net_2(&sub, &type)
|| !PACKET_get_length_prefixed_2(&sub, &subext))
return 0;
} while (type != exttype);
/* Found it */
end = PACKET_data(&sub);
if (old_ext != NULL) {
if (!WPACKET_init(&old_ext_wpkt, old_ext))
return 0;
if (!WPACKET_memcpy(&old_ext_wpkt, PACKET_data(&subext),
PACKET_remaining(&subext))
|| !WPACKET_get_total_written(&old_ext_wpkt, &w)) {
WPACKET_cleanup(&old_ext_wpkt);
return 0;
}
WPACKET_finish(&old_ext_wpkt);
old_ext->length = w;
}
/*
* If we're not the last extension we need to move the rest earlier. The
* cast below is safe because we own the underlying buffer and we're no
* longer making PACKET calls.
*/
if (end < ext + *extlen)
memmove((unsigned char *)start, end, end - start);
/*
* Calculate new extensions payload length =
* Original length
* - 2 extension block length bytes
* - length of removed extension
*/
newlen = *extlen - 2 - (end - start);
/* Fixup the length bytes for the extension block */
ext[0] = (unsigned char)((newlen >> 8) & 0xff);
ext[1] = (unsigned char)((newlen ) & 0xff);
/*
* Length of the whole extension block is the new payload length plus the
* 2 bytes for the length
*/
*extlen = newlen + 2;
/* We can now resize the message */
if ((size_t)(end - start) + SSL3_HM_HEADER_LENGTH > msglen)
return 0; /* Should not happen */
msglen -= (end - start) + SSL3_HM_HEADER_LENGTH;
if (!qtest_fault_resize_message(fault, msglen))
return 0;
return 1;
}
#define BIO_TYPE_CIPHER_PACKET_FILTER (0x80 | BIO_TYPE_FILTER)
static BIO_METHOD *pcipherbiometh = NULL;
# define BIO_MSG_N(array, stride, n) (*(BIO_MSG *)((char *)(array) + (n)*(stride)))
static int pcipher_sendmmsg(BIO *b, BIO_MSG *msg, size_t stride,
size_t num_msg, uint64_t flags,
size_t *num_processed)
{
BIO *next = BIO_next(b);
ossl_ssize_t ret = 0;
size_t i = 0, tmpnump;
QUIC_PKT_HDR hdr;
PACKET pkt;
unsigned char *tmpdata;
QTEST_DATA *bdata = NULL;
if (next == NULL)
return 0;
bdata = BIO_get_data(b);
if (bdata == NULL || bdata->fault == NULL
|| (bdata->fault->pciphercb == NULL && bdata->fault->datagramcb == NULL))
return BIO_sendmmsg(next, msg, stride, num_msg, flags, num_processed);
if (num_msg == 0) {
*num_processed = 0;
return 1;
}
for (i = 0; i < num_msg; ++i) {
bdata->fault->msg = BIO_MSG_N(msg, stride, i);
/* Take a copy of the data so that callbacks can modify it */
tmpdata = OPENSSL_malloc(bdata->fault->msg.data_len + GROWTH_ALLOWANCE);
if (tmpdata == NULL)
return 0;
memcpy(tmpdata, bdata->fault->msg.data, bdata->fault->msg.data_len);
bdata->fault->msg.data = tmpdata;
bdata->fault->msgalloc = bdata->fault->msg.data_len + GROWTH_ALLOWANCE;
if (bdata->fault->pciphercb != NULL) {
if (!PACKET_buf_init(&pkt, bdata->fault->msg.data, bdata->fault->msg.data_len))
return 0;
do {
if (!ossl_quic_wire_decode_pkt_hdr(&pkt,
bdata->short_conn_id_len,
1, 0, &hdr, NULL, NULL))
goto out;
/*
* hdr.data is const - but its our buffer so casting away the
* const is safe
*/
if (!bdata->fault->pciphercb(bdata->fault, &hdr,
(unsigned char *)hdr.data, hdr.len,
bdata->fault->pciphercbarg))
goto out;
/*
* At the moment modifications to hdr by the callback
* are ignored. We might need to rewrite the QUIC header to
* enable tests to change this. We also don't yet have a
* mechanism for the callback to change the encrypted data
* length. It's not clear if that's needed or not.
*/
} while (PACKET_remaining(&pkt) > 0);
}
if (bdata->fault->datagramcb != NULL
&& !bdata->fault->datagramcb(bdata->fault, &bdata->fault->msg, stride,
bdata->fault->datagramcbarg))
goto out;
if (!BIO_sendmmsg(next, &bdata->fault->msg, stride, 1, flags, &tmpnump)) {
*num_processed = i;
goto out;
}
OPENSSL_free(bdata->fault->msg.data);
bdata->fault->msg.data = NULL;
bdata->fault->msgalloc = 0;
}
*num_processed = i;
out:
ret = i > 0;
OPENSSL_free(bdata->fault->msg.data);
bdata->fault->msg.data = NULL;
return ret;
}
static long pcipher_ctrl(BIO *b, int cmd, long larg, void *parg)
{
BIO *next = BIO_next(b);
if (next == NULL)
return -1;
return BIO_ctrl(next, cmd, larg, parg);
}
static int pcipher_destroy(BIO *b)
{
OPENSSL_free(BIO_get_data(b));
return 1;
}
BIO_METHOD *qtest_get_bio_method(void)
{
BIO_METHOD *tmp;
if (pcipherbiometh != NULL)
return pcipherbiometh;
tmp = BIO_meth_new(BIO_TYPE_CIPHER_PACKET_FILTER, "Cipher Packet Filter");
if (!TEST_ptr(tmp))
return NULL;
if (!TEST_true(BIO_meth_set_sendmmsg(tmp, pcipher_sendmmsg))
|| !TEST_true(BIO_meth_set_ctrl(tmp, pcipher_ctrl))
|| !TEST_true(BIO_meth_set_destroy(tmp, pcipher_destroy)))
goto err;
pcipherbiometh = tmp;
tmp = NULL;
err:
BIO_meth_free(tmp);
return pcipherbiometh;
}
int qtest_fault_set_packet_cipher_listener(QTEST_FAULT *fault,
qtest_fault_on_packet_cipher_cb pciphercb,
void *pciphercbarg)
{
fault->pciphercb = pciphercb;
fault->pciphercbarg = pciphercbarg;
return 1;
}
int qtest_fault_set_datagram_listener(QTEST_FAULT *fault,
qtest_fault_on_datagram_cb datagramcb,
void *datagramcbarg)
{
fault->datagramcb = datagramcb;
fault->datagramcbarg = datagramcbarg;
return 1;
}
/* To be called from a datagram_listener callback */
int qtest_fault_resize_datagram(QTEST_FAULT *fault, size_t newlen)
{
if (newlen > fault->msgalloc)
return 0;
if (newlen > fault->msg.data_len)
memset((unsigned char *)fault->msg.data + fault->msg.data_len, 0,
newlen - fault->msg.data_len);
fault->msg.data_len = newlen;
return 1;
}
int qtest_fault_set_bw_limit(QTEST_FAULT *fault,
size_t ctos_bw, size_t stoc_bw,
int noise_rate)
{
BIO *sbio = fault->noiseargs.sbio;
BIO *cbio = fault->noiseargs.cbio;
if (!TEST_ptr(sbio) || !TEST_ptr(cbio))
return 0;
if (!TEST_int_eq(BIO_ctrl(sbio, BIO_CTRL_NOISE_RATE, noise_rate, NULL), 1))
return 0;
if (!TEST_int_eq(BIO_ctrl(cbio, BIO_CTRL_NOISE_RATE, noise_rate, NULL), 1))
return 0;
/* We set the bandwidth limit on the sending side */
if (!TEST_int_eq(BIO_ctrl(cbio, BIO_CTRL_NOISE_SEND_BANDWIDTH,
(long)ctos_bw, NULL), 1))
return 0;
if (!TEST_int_eq(BIO_ctrl(sbio, BIO_CTRL_NOISE_SEND_BANDWIDTH,
(long)stoc_bw, NULL), 1))
return 0;
return 1;
}
int bio_msg_copy(BIO_MSG *dst, BIO_MSG *src)
{
/*
* Note it is assumed that the originally allocated data sizes for dst and
* src are the same
*/
memcpy(dst->data, src->data, src->data_len);
dst->data_len = src->data_len;
dst->flags = src->flags;
if (dst->local != NULL) {
if (src->local != NULL) {
if (!TEST_true(BIO_ADDR_copy(dst->local, src->local)))
return 0;
} else {
BIO_ADDR_clear(dst->local);
}
}
if (!TEST_true(BIO_ADDR_copy(dst->peer, src->peer)))
return 0;
return 1;
}