Address review: probe the DTLS socket type where the fd is set

Under WOLFSSL_RW_THREADED the read and write threads could both perform
the lazy isDGramSock() first-time cache write concurrently; the cached
bit-fields share a storage unit with other dtlsCtx flags, making this a
data race.

Instead of caching from inside the I/O callbacks, run the
getsockopt(SO_TYPE) probe where dtlsCtx.rfd/wfd is assigned and store
the result per descriptor (rfd and wfd may be different sockets of
different types). fd assignment happens during single-threaded setup,
so no thread-specific handling is needed, and the I/O callbacks reduce
to reading a struct member, so isDGramSock() is dropped in favor of
reading the flags directly. The stateless-hash test no longer needs to
mask the fields: the I/O callbacks no longer write to the WOLFSSL
object.
This commit is contained in:
Juliusz Sosinowicz
2026-06-10 02:44:41 +00:00
parent d2c9f53ef6
commit 85e85c1fb4
6 changed files with 28 additions and 25 deletions
+2 -1
View File
@@ -7945,7 +7945,8 @@ int InitSSL(WOLFSSL* ssl, WOLFSSL_CTX* ctx, int writeDup)
ssl->buffers.dtlsCtx.rfd = -1;
ssl->buffers.dtlsCtx.wfd = -1;
ssl->buffers.dtlsCtx.isDGramCached = 0;
ssl->buffers.dtlsCtx.rfdIsDGram = 0;
ssl->buffers.dtlsCtx.wfdIsDGram = 0;
#ifdef WOLFSSL_RW_THREADED
if (wc_InitRwLock(&ssl->buffers.dtlsCtx.peerLock) != 0)
+12 -3
View File
@@ -1131,7 +1131,10 @@ int wolfSSL_set_read_fd(WOLFSSL* ssl, int fd)
if (ssl->options.dtls) {
ssl->IOCB_ReadCtx = &ssl->buffers.dtlsCtx;
ssl->buffers.dtlsCtx.rfd = fd;
ssl->buffers.dtlsCtx.isDGramCached = 0;
#ifdef USE_WOLFSSL_IO
ssl->buffers.dtlsCtx.rfdIsDGram =
(byte)(wolfIO_SockIsDGram(fd) != 0);
#endif
}
#endif
@@ -1156,7 +1159,10 @@ int wolfSSL_set_write_fd(WOLFSSL* ssl, int fd)
if (ssl->options.dtls) {
ssl->IOCB_WriteCtx = &ssl->buffers.dtlsCtx;
ssl->buffers.dtlsCtx.wfd = fd;
ssl->buffers.dtlsCtx.isDGramCached = 0;
#ifdef USE_WOLFSSL_IO
ssl->buffers.dtlsCtx.wfdIsDGram =
(byte)(wolfIO_SockIsDGram(fd) != 0);
#endif
}
#endif
@@ -6821,7 +6827,10 @@ int wolfSSL_set_compression(WOLFSSL* ssl)
if (ssl->options.dtls) {
ssl->IOCB_ReadCtx = &ssl->buffers.dtlsCtx;
ssl->buffers.dtlsCtx.rfd = rfd;
ssl->buffers.dtlsCtx.isDGramCached = 0;
#ifdef USE_WOLFSSL_IO
ssl->buffers.dtlsCtx.rfdIsDGram =
(byte)(wolfIO_SockIsDGram(rfd) != 0);
#endif
}
#endif
+8 -13
View File
@@ -643,27 +643,22 @@ static int PeerIsIpv6(const SOCKADDR_S *peer, XSOCKLENT len)
#endif /* !WOLFSSL_IPV6 */
/* Return non-zero iff sfd is a SOCK_DGRAM socket. A descriptor's type is
* fixed for its lifetime, so the getsockopt(SO_TYPE) probe runs once per
* connection and is cached in dtlsCtx; the cache is invalidated wherever
* rfd/wfd is reassigned. */
static int isDGramSock(WOLFSSL_DTLS_CTX* dtlsCtx, int sfd)
* fixed for its lifetime, so the probe runs where dtlsCtx.rfd/wfd is
* assigned and the result is stored alongside the descriptor, keeping the
* getsockopt() syscall out of the I/O callbacks. */
int wolfIO_SockIsDGram(int sfd)
{
int type = 0;
/* optvalue 'type' is of size int */
XSOCKLENT length = (XSOCKLENT)sizeof(type);
if (dtlsCtx->isDGramCached)
return dtlsCtx->isDGram;
if (getsockopt(sfd, SOL_SOCKET, SO_TYPE, (XSOCKOPT_TYPE_OPTVAL_TYPE)&type,
&length) == 0 && type != SOCK_DGRAM) {
dtlsCtx->isDGram = 0;
return 0;
}
else {
dtlsCtx->isDGram = 1;
return 1;
}
dtlsCtx->isDGramCached = 1;
return dtlsCtx->isDGram;
}
void wolfSSL_SetRecvFrom(WOLFSSL* ssl, WolfSSLRecvFrom recvFrom)
@@ -853,7 +848,7 @@ int EmbedReceiveFrom(WOLFSSL *ssl, char *buf, int sz, void *ctx)
return recvd;
}
else if (recvd == 0) {
if (!isDGramSock(dtlsCtx, sd)) {
if (!dtlsCtx->rfdIsDGram) {
/* Closed TCP connection */
recvd = WOLFSSL_CBIO_ERR_CONN_CLOSE;
}
@@ -951,7 +946,7 @@ int EmbedSendTo(WOLFSSL* ssl, char *buf, int sz, void *ctx)
if (sz < 0)
return WOLFSSL_CBIO_ERR_GENERAL;
if (!isDGramSock(dtlsCtx, sd)) {
if (!dtlsCtx->wfdIsDGram) {
/* Probably a TCP socket. peer and peerSz MUST be NULL and 0 */
}
else if (!dtlsCtx->connected) {
-4
View File
@@ -3613,10 +3613,6 @@ static word32 test_wolfSSL_dtls_stateless_HashWOLFSSL(const WOLFSSL* ssl)
sslCopy.dtls13FastTimeout = 0;
#endif
sslCopy.keys.dtls_peer_handshake_number = 0;
/* isDGram* is a local-socket-type memoization the IO callbacks may
* populate lazily; it is not peer state and not relevant here. */
sslCopy.buffers.dtlsCtx.isDGramCached = 0;
sslCopy.buffers.dtlsCtx.isDGram = 0;
XMEMSET(&sslCopy.alert_history, 0, sizeof(sslCopy.alert_history));
sslCopy.hsHashes = NULL;
#if !defined(WOLFSSL_NO_CLIENT_AUTH) && \
+5 -4
View File
@@ -2791,10 +2791,11 @@ typedef struct WOLFSSL_DTLS_CTX {
* connected (connect() and bind() both called).
* This means that sendto and recvfrom do not need to
* specify and store the peer address. */
byte isDGramCached:1; /* whether isDGram below is valid; reset whenever
* rfd/wfd is (re)assigned so the SO_TYPE probe
* re-runs once for the new descriptor. */
byte isDGram:1; /* cached isDGramSock() for the rfd/wfd socket */
byte rfdIsDGram:1; /* whether rfd is a SOCK_DGRAM socket; probed with
* getsockopt(SO_TYPE) where rfd is assigned to keep
* the syscall out of the I/O callbacks. */
byte wfdIsDGram:1; /* as rfdIsDGram, for wfd; rfd and wfd may be
* different sockets of different types. */
#ifdef WOLFSSL_DTLS_CID
byte processingPendingRecord:1;
#endif
+1
View File
@@ -703,6 +703,7 @@ WOLFSSL_LOCAL int SslBioReceive(WOLFSSL* ssl, char* buf, int sz, void* ctx);
WOLFSSL_API int EmbedSendTo(WOLFSSL* ssl, char *buf, int sz, void *ctx);
WOLFSSL_API int EmbedGenerateCookie(WOLFSSL* ssl, byte *buf, int sz,
void *ctx);
WOLFSSL_LOCAL int wolfIO_SockIsDGram(int sfd);
#ifdef WOLFSSL_MULTICAST
WOLFSSL_API int EmbedReceiveFromMcast(WOLFSSL *ssl, char *buf,
int sz, void *ctx);