Add testing/docs for blocking write

- Fix case where message grouping can make CheckAvailableSize return a WANT_WRITE
- CheckAvailableSize in tls13.c will not return a WANT_WRITE since it only does so for DTLS <=1.2
This commit is contained in:
Juliusz Sosinowicz 2022-05-27 21:26:08 +02:00
parent 4e8c362152
commit 50c0b3d2a2
10 changed files with 160 additions and 32 deletions

View File

@ -1055,7 +1055,8 @@ static int ClientRead(WOLFSSL* ssl, char* reply, int replyLen, int mustRead,
}
else
#endif
if (err != WOLFSSL_ERROR_WANT_READ && err != APP_DATA_READY) {
if (err != WOLFSSL_ERROR_WANT_READ &&
err != WOLFSSL_ERROR_WANT_WRITE && err != APP_DATA_READY) {
fprintf(stderr, "SSL_read reply error %d, %s\n", err,
wolfSSL_ERR_error_string(err, buffer));
if (!exitWithRet) {
@ -1076,6 +1077,7 @@ static int ClientRead(WOLFSSL* ssl, char* reply, int replyLen, int mustRead,
}
}
} while ((mustRead && err == WOLFSSL_ERROR_WANT_READ)
|| err == WOLFSSL_ERROR_WANT_WRITE
#ifdef WOLFSSL_ASYNC_CRYPT
|| err == WC_PENDING_E
#endif

View File

@ -533,6 +533,8 @@ static void ServerRead(WOLFSSL* ssl, char* input, int inputLen)
else
#endif
if (err != WOLFSSL_ERROR_WANT_READ
&& err != WOLFSSL_ERROR_WANT_WRITE /* Can happen during
* handshake */
#ifdef HAVE_SECURE_RENEGOTIATION
&& err != APP_DATA_READY
#endif

View File

@ -8864,7 +8864,7 @@ static int SendHandshakeMsg(WOLFSSL* ssl, byte* input, word32 inputSz,
WOLFSSL_MSG("Can't use output buffer for input in SendHandshakeMsg");
return BAD_FUNC_ARG;
}
if (ssl->fragOffset == 0) {
if (!ssl->options.buildingMsg) {
/* Hash it before the loop as we modify the input with
* encryption on */
ret = HashOutput(ssl, input, headerSz + (int)inputSz, 0);
@ -8882,6 +8882,9 @@ static int SendHandshakeMsg(WOLFSSL* ssl, byte* input, word32 inputSz,
int outputSz;
byte* data = input + ssl->fragOffset + headerSz;
word32 fragSz = (word32)maxFrag;
ssl->options.buildingMsg = 1;
if (inputSz - ssl->fragOffset < fragSz)
fragSz = inputSz - ssl->fragOffset;
@ -8967,6 +8970,7 @@ static int SendHandshakeMsg(WOLFSSL* ssl, byte* input, word32 inputSz,
ssl->keys.dtls_handshake_number++;
#endif
ssl->fragOffset = 0;
ssl->options.buildingMsg = 0;
return ret;
}
#endif /* !NO_WOLFSSL_SERVER || (!NO_WOLFSSL_CLIENT && !NO_CERTS &&
@ -18188,6 +18192,10 @@ int SendChangeCipher(WOLFSSL* ssl)
sendSz += MAX_MSG_EXTRA;
}
/* Set this in case CheckAvailableSize returns a WANT_WRITE so that state
* is not advanced yet */
ssl->options.buildingMsg = 1;
/* check for available size */
if ((ret = CheckAvailableSize(ssl, sendSz)) != 0)
return ret;
@ -18247,6 +18255,8 @@ int SendChangeCipher(WOLFSSL* ssl)
#endif
}
ssl->options.buildingMsg = 0;
if (ssl->options.groupMessages)
return 0;
#if defined(WOLFSSL_DTLS) && !defined(WOLFSSL_DEBUG_DTLS)
@ -19043,6 +19053,11 @@ int SendFinished(WOLFSSL* ssl)
/* check for available size */
outputSz = sizeof(input) + MAX_MSG_EXTRA;
/* Set this in case CheckAvailableSize returns a WANT_WRITE so that state
* is not advanced yet */
ssl->options.buildingMsg = 1;
if ((ret = CheckAvailableSize(ssl, outputSz)) != 0)
return ret;
@ -19145,6 +19160,8 @@ int SendFinished(WOLFSSL* ssl)
ret = SendBuffered(ssl);
ssl->options.buildingMsg = 0;
#ifdef WOLFSSL_DTLS
if ((!ssl->options.resuming &&
ssl->options.side == WOLFSSL_SERVER_END) ||
@ -19394,6 +19411,8 @@ int SendCertificate(WOLFSSL* ssl)
word32 i = RECORD_HEADER_SZ;
int sendSz = RECORD_HEADER_SZ;
ssl->options.buildingMsg = 1;
if (!ssl->options.dtls) {
if (ssl->fragOffset == 0) {
if (headerSz + certSz + certChainSz <=
@ -19434,6 +19453,9 @@ int SendCertificate(WOLFSSL* ssl)
output = ssl->buffers.outputBuffer.buffer +
ssl->buffers.outputBuffer.length;
/* Safe to use ssl->fragOffset since it will be incremented immediately
* after this block. This block needs to be entered only once to not
* hash the cert msg twice. */
if (ssl->fragOffset == 0) {
if (!ssl->options.dtls) {
AddFragHeaders(output, fragSz, 0, payloadSz, certificate, ssl);
@ -19583,6 +19605,7 @@ int SendCertificate(WOLFSSL* ssl)
if (ret != WANT_WRITE) {
/* Clean up the fragment offset. */
ssl->options.buildingMsg = 0;
ssl->fragOffset = 0;
#ifdef WOLFSSL_DTLS
if (ssl->options.dtls)
@ -19657,6 +19680,10 @@ int SendCertificateRequest(WOLFSSL* ssl)
if (IsEncryptionOn(ssl, 1))
sendSz += cipherExtraData(ssl);
/* Set this in case CheckAvailableSize returns a WANT_WRITE so that state
* is not advanced yet */
ssl->options.buildingMsg = 1;
/* check for available size */
if ((ret = CheckAvailableSize(ssl, sendSz)) != 0)
return ret;
@ -19772,6 +19799,8 @@ int SendCertificateRequest(WOLFSSL* ssl)
else
ret = SendBuffered(ssl);
ssl->options.buildingMsg = 0;
WOLFSSL_LEAVE("SendCertificateRequest", ret);
WOLFSSL_END(WC_FUNC_CERTIFICATE_REQUEST_SEND);
@ -19812,6 +19841,10 @@ static int BuildCertificateStatus(WOLFSSL* ssl, byte type, buffer* status,
if (ssl->keys.encryptionOn)
sendSz += MAX_MSG_EXTRA;
/* Set this in case CheckAvailableSize returns a WANT_WRITE so that state
* is not advanced yet */
ssl->options.buildingMsg = 1;
if ((ret = CheckAvailableSize(ssl, sendSz)) == 0) {
output = ssl->buffers.outputBuffer.buffer +
ssl->buffers.outputBuffer.length;
@ -19876,6 +19909,7 @@ static int BuildCertificateStatus(WOLFSSL* ssl, byte type, buffer* status,
#endif
if (ret == 0) {
ssl->options.buildingMsg = 0;
ssl->buffers.outputBuffer.length += sendSz;
if (!ssl->options.groupMessages)
ret = SendBuffered(ssl);
@ -20574,8 +20608,16 @@ int SendAlert(WOLFSSL* ssl, int severity, int type)
/* check for available size */
outputSz = ALERT_SIZE + MAX_MSG_EXTRA + dtlsExtra;
if ((ret = CheckAvailableSize(ssl, outputSz)) != 0)
if ((ret = CheckAvailableSize(ssl, outputSz)) != 0) {
/* If CheckAvailableSize returned WANT_WRITE due to a blocking write
* then discard pending output and just send the alert. */
if (ret != WANT_WRITE || severity != alert_fatal)
return ret;
ShrinkOutputBuffer(ssl);
if ((ret = CheckAvailableSize(ssl, outputSz)) != 0) {
return ret;
}
}
/* Check output buffer */
if (ssl->buffers.outputBuffer.buffer == NULL)
@ -23310,6 +23352,10 @@ exit_dpk:
if (IsEncryptionOn(ssl, 1))
sendSz += MAX_MSG_EXTRA;
/* Set this in case CheckAvailableSize returns a WANT_WRITE so that state
* is not advanced yet */
ssl->options.buildingMsg = 1;
/* check for available size */
if ((ret = CheckAvailableSize(ssl, sendSz)) != 0)
return ret;
@ -23470,6 +23516,8 @@ exit_dpk:
WRITE_PROTO, ssl->heap);
#endif
ssl->options.buildingMsg = 0;
ssl->buffers.outputBuffer.length += sendSz;
ret = SendBuffered(ssl);
@ -25631,7 +25679,7 @@ static void FreeSckeArgs(WOLFSSL* ssl, void* pArgs)
int SendClientKeyExchange(WOLFSSL* ssl)
{
int ret = 0;
#ifdef WOLFSSL_ASYNC_CRYPT
#ifdef WOLFSSL_ASYNC_IO
SckeArgs* args = NULL;
WOLFSSL_ASSERT_SIZEOF_GE(ssl->async->args, *args);
#else
@ -25648,7 +25696,7 @@ int SendClientKeyExchange(WOLFSSL* ssl)
ssl->CBIS(ssl, SSL_CB_CONNECT_LOOP, SSL_SUCCESS);
#endif
#ifdef WOLFSSL_ASYNC_CRYPT
#ifdef WOLFSSL_ASYNC_IO
if (ssl->async == NULL) {
ssl->async = (struct WOLFSSL_ASYNC*)
XMALLOC(sizeof(struct WOLFSSL_ASYNC), ssl->heap,
@ -25658,6 +25706,7 @@ int SendClientKeyExchange(WOLFSSL* ssl)
}
args = (SckeArgs*)ssl->async->args;
#ifdef WOLFSSL_ASYNC_CRYPT
ret = wolfSSL_AsyncPop(ssl, &ssl->options.asyncState);
if (ret != WC_NOT_PENDING_E) {
/* Check for error */
@ -25665,13 +25714,21 @@ int SendClientKeyExchange(WOLFSSL* ssl)
goto exit_scke;
}
else
#endif
if (ssl->options.buildingMsg) {
/* Continue building the message */
}
else
#endif
{
/* Reset state */
ret = 0;
ssl->options.asyncState = TLS_ASYNC_BEGIN;
XMEMSET(args, 0, sizeof(SckeArgs));
#ifdef WOLFSSL_ASYNC_CRYPT
/* Set this in case CheckAvailableSize returns a WANT_WRITE so that state
* is not advanced yet */
ssl->options.buildingMsg = 1;
#ifdef WOLFSSL_ASYNC_IO
ssl->async->freeArgs = FreeSckeArgs;
#endif
}
@ -26680,9 +26737,8 @@ int SendClientKeyExchange(WOLFSSL* ssl)
}
/* check for available size */
if ((ret = CheckAvailableSize(ssl, args->sendSz)) != 0) {
if ((ret = CheckAvailableSize(ssl, args->sendSz)) != 0)
goto exit_scke;
}
/* get output buffer */
args->output = ssl->buffers.outputBuffer.buffer +
@ -26773,6 +26829,7 @@ int SendClientKeyExchange(WOLFSSL* ssl)
ret = tmpRet; /* save WANT_WRITE unless more serious */
}
ssl->options.clientState = CLIENT_KEYEXCHANGE_COMPLETE;
ssl->options.buildingMsg = 0;
}
#if defined(OPENSSL_EXTRA) && defined(HAVE_SECRET_CALLBACK)
if (ssl->keyLogCb != NULL) {
@ -26794,10 +26851,14 @@ exit_scke:
WOLFSSL_LEAVE("SendClientKeyExchange", ret);
WOLFSSL_END(WC_FUNC_CLIENT_KEY_EXCHANGE_SEND);
#ifdef WOLFSSL_ASYNC_CRYPT
#ifdef WOLFSSL_ASYNC_IO
/* Handle async operation */
if (ret == WC_PENDING_E)
if (ret == WC_PENDING_E || ret == WANT_WRITE) {
if (ssl->options.buildingMsg)
return ret;
/* If we have completed all states then we will not enter this function
* again. We need to do clean up now. */
}
#endif
/* No further need for PMS */
@ -26807,7 +26868,7 @@ exit_scke:
ssl->arrays->preMasterSz = 0;
/* Final cleanup */
#ifdef WOLFSSL_ASYNC_CRYPT
#ifdef WOLFSSL_ASYNC_IO
/* Cleanup async */
FreeAsyncCtx(ssl, 0);
#else
@ -26894,7 +26955,7 @@ int SendCertificateVerify(WOLFSSL* ssl)
}
else
#endif
if (ssl->fragOffset != 0) {
if (ssl->options.buildingMsg) {
/* We should be in the sending state. */
if (ssl->options.asyncState != TLS_ASYNC_END) {
ret = BAD_STATE_E;
@ -27225,12 +27286,6 @@ int SendCertificateVerify(WOLFSSL* ssl)
if (args->output == NULL) {
ERROR_OUT(BUFFER_ERROR, exit_scv);
}
#ifdef WOLFSSL_DTLS
/* We have re-entered this funtion after a WANT_WRITE. Make sure
* the handshake number stays the same. */
if (ssl->options.dtls && ssl->fragOffset != 0)
ssl->keys.dtls_handshake_number--;
#endif
AddHeaders(args->output, (word32)args->length + args->extraSz +
VERIFY_HEADER, certificate_verify, ssl);
@ -27615,6 +27670,10 @@ static int DoSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
if (IsEncryptionOn(ssl, 1))
sendSz += MAX_MSG_EXTRA;
/* Set this in case CheckAvailableSize returns a WANT_WRITE so that state
* is not advanced yet */
ssl->options.buildingMsg = 1;
/* check for available size */
if ((ret = CheckAvailableSize(ssl, sendSz)) != 0)
return ret;
@ -27769,6 +27828,7 @@ static int DoSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
#endif
ssl->options.serverState = SERVER_HELLO_COMPLETE;
ssl->options.buildingMsg = 0;
ssl->buffers.outputBuffer.length += sendSz;
if (ssl->options.groupMessages)
@ -27885,7 +27945,7 @@ static int DoSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
}
else
#endif
if (ssl->fragOffset != 0) {
if (ssl->options.buildingMsg) {
/* We should be in the sending state. */
if (ssl->options.asyncState != TLS_ASYNC_END) {
ret = BAD_STATE_E;
@ -29344,12 +29404,6 @@ static int DoSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
case TLS_ASYNC_FINALIZE:
{
#ifdef WOLFSSL_DTLS
/* We have re-entered this funtion after a WANT_WRITE. Make sure
* the handshake number stays the same. */
if (ssl->options.dtls && ssl->fragOffset != 0)
ssl->keys.dtls_handshake_number--;
#endif
#if defined(HAVE_ECC) || defined(HAVE_CURVE25519) || \
defined(HAVE_CURVE448)
if (ssl->specs.kea == ecdhe_psk_kea ||
@ -31060,6 +31114,10 @@ static int DoSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
if (IsEncryptionOn(ssl, 1))
sendSz += MAX_MSG_EXTRA;
/* Set this in case CheckAvailableSize returns a WANT_WRITE so that state
* is not advanced yet */
ssl->options.buildingMsg = 1;
/* check for available size */
if ((ret = CheckAvailableSize(ssl, sendSz)) != 0)
return ret;
@ -31120,6 +31178,7 @@ static int DoSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
WRITE_PROTO, ssl->heap);
#endif
ssl->options.serverState = SERVER_HELLODONE_COMPLETE;
ssl->options.buildingMsg = 0;
ssl->buffers.outputBuffer.length += sendSz;
@ -31544,6 +31603,10 @@ static int DoSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
if (IsEncryptionOn(ssl, 1) && ssl->options.handShakeDone)
sendSz += cipherExtraData(ssl);
/* Set this in case CheckAvailableSize returns a WANT_WRITE so that state
* is not advanced yet */
ssl->options.buildingMsg = 1;
/* check for available size */
if ((ret = CheckAvailableSize(ssl, sendSz)) != 0)
return ret;
@ -31601,6 +31664,7 @@ static int DoSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
}
ssl->buffers.outputBuffer.length += sendSz;
ssl->options.buildingMsg = 0;
if (!ssl->options.groupMessages)
ret = SendBuffered(ssl);
@ -32065,6 +32129,10 @@ static int DefTicketEncCb(WOLFSSL* ssl, byte key_name[WOLFSSL_TICKET_NAME_SZ],
if (ssl->options.dtls)
sendSz += DTLS_RECORD_EXTRA + DTLS_HANDSHAKE_EXTRA;
/* Set this in case CheckAvailableSize returns a WANT_WRITE so that state
* is not advanced yet */
ssl->options.buildingMsg = 1;
/* check for available size */
if ((ret = CheckAvailableSize(ssl, sendSz)) != 0)
return ret;
@ -32106,6 +32174,7 @@ static int DefTicketEncCb(WOLFSSL* ssl, byte key_name[WOLFSSL_TICKET_NAME_SZ],
}
ssl->buffers.outputBuffer.length += sendSz;
ssl->options.buildingMsg = 0;
ret = SendBuffered(ssl);

View File

@ -11850,7 +11850,7 @@ int wolfSSL_DTLS_SetCookieSecret(WOLFSSL* ssl,
/* fragOffset is non-zero when sending fragments. On the last
* fragment, fragOffset is zero again, and the state can be
* advanced. */
if (ssl->fragOffset == 0) {
if (ssl->fragOffset == 0 && !ssl->options.buildingMsg) {
if (ssl->options.connectState == CONNECT_BEGIN ||
ssl->options.connectState == HELLO_AGAIN ||
(ssl->options.connectState >= FIRST_REPLY_DONE &&
@ -12330,7 +12330,7 @@ int wolfSSL_DTLS_SetCookieSecret(WOLFSSL* ssl,
/* fragOffset is non-zero when sending fragments. On the last
* fragment, fragOffset is zero again, and the state can be
* advanced. */
if (ssl->fragOffset == 0) {
if (ssl->fragOffset == 0 && !ssl->options.buildingMsg) {
if (ssl->options.acceptState == ACCEPT_FIRST_REPLY_DONE ||
ssl->options.acceptState == SERVER_HELLO_SENT ||
ssl->options.acceptState == CERT_SENT ||

View File

@ -5932,6 +5932,8 @@ static int SendTls13Certificate(WOLFSSL* ssl)
word32 i = RECORD_HEADER_SZ;
int sendSz = RECORD_HEADER_SZ;
ssl->options.buildingMsg = 1;
if (ssl->fragOffset == 0) {
if (headerSz + certSz + extSz + certChainSz <=
maxFragment - HANDSHAKE_HEADER_SZ) {
@ -6053,6 +6055,7 @@ static int SendTls13Certificate(WOLFSSL* ssl)
if (ret != WANT_WRITE) {
/* Clean up the fragment offset. */
ssl->options.buildingMsg = 0;
ssl->fragOffset = 0;
if (ssl->options.side == WOLFSSL_SERVER_END)
ssl->options.serverState = SERVER_CERT_COMPLETE;
@ -8720,7 +8723,7 @@ int wolfSSL_connect_TLSv13(WOLFSSL* ssl)
/* fragOffset is non-zero when sending fragments. On the last
* fragment, fragOffset is zero again, and the state can be
* advanced. */
if (ssl->fragOffset == 0) {
if (ssl->fragOffset == 0 && !ssl->options.buildingMsg) {
/* Only increment from states in which we send data */
if (ssl->options.connectState == CONNECT_BEGIN ||
ssl->options.connectState == HELLO_AGAIN ||
@ -9688,7 +9691,7 @@ int wolfSSL_accept_TLSv13(WOLFSSL* ssl)
/* fragOffset is non-zero when sending fragments. On the last
* fragment, fragOffset is zero again, and the state can be
* advanced. */
if (ssl->fragOffset == 0) {
if (ssl->fragOffset == 0 && !ssl->options.buildingMsg) {
/* Only increment from states in which we send data */
if (ssl->options.acceptState == TLS13_ACCEPT_CLIENT_HELLO_DONE ||
ssl->options.acceptState == TLS13_ACCEPT_HELLO_RETRY_REQUEST_DONE ||

View File

@ -3521,3 +3521,17 @@
-f
-v 3
-l AES256-SHA256
# server DTLSv1.2 TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 with write blocking
-u 512
-6
-f
-v 3
-l TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256
# client DTLSv1.2 TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 with write blocking
-u 512
-6
-f
-v 3
-l TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256

View File

@ -1061,3 +1061,19 @@
-a
-v 2
-l ADH-AES128-SHA
# server DTLSv1.2 TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 with non-blocking write
-m
-u 512
-v 3
-6
-l TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256
-f
# client DTLSv1.2 TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 with non-blocking write
-R
-u 512
-v 3
-6
-l TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256
-f

View File

@ -177,3 +177,14 @@
-v 3
-l DHE-RSA-AES256-GCM-SHA384
-F 6
# server TLSv1.2 DHE-RSA-AES256-GCM-SHA384
-v 3
-l DHE-RSA-AES256-GCM-SHA384
-6
# client TLSv1.2 DHE-RSA-AES256-GCM-SHA384
-v 3
-l DHE-RSA-AES256-GCM-SHA384
-F 1
-6

View File

@ -2070,3 +2070,13 @@
-v 3
-l ECDHE-RSA-AES128-SHA256
# server test with a blocking write socket
-v 3
-6
-l TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256
# client test with a blocking write socket
-v 3
-6
-l TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256

View File

@ -3505,7 +3505,6 @@ enum AcceptState {
ACCEPT_HELLO_RETRY_REQUEST_DONE,
ACCEPT_FIRST_REPLY_DONE,
SERVER_HELLO_SENT,
SERVER_EXTENSIONS_SENT,
CERT_SENT,
CERT_VERIFY_SENT,
CERT_STATUS_SENT,
@ -3773,6 +3772,8 @@ typedef struct Options {
word16 buildArgsSet:1; /* buildArgs are set and need to
* be free'd */
#endif
word16 buildingMsg:1; /* If set then we need to re-enter the
* handshake logic. */
/* need full byte values for this section */
byte processReply; /* nonblocking resume */