Skip to content

Commit 3534fad

Browse files
authored
Merge pull request #9295 from rizlik/shutdown_nonblocking_fix
wolfSSL_shutdown: handle non-blocking I/O
2 parents 77dcbb5 + 4280b52 commit 3534fad

File tree

5 files changed

+125
-60
lines changed

5 files changed

+125
-60
lines changed

src/internal.c

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -26433,20 +26433,6 @@ static int SendAlert_ex(WOLFSSL* ssl, int severity, int type)
2643326433
}
2643426434
#endif
2643526435

26436-
/*
26437-
* We check if we are trying to send a
26438-
* CLOSE_NOTIFY alert.
26439-
* */
26440-
if (type == close_notify) {
26441-
if (!ssl->options.sentNotify) {
26442-
ssl->options.sentNotify = 1;
26443-
}
26444-
else {
26445-
/* CLOSE_NOTIFY already sent */
26446-
return 0;
26447-
}
26448-
}
26449-
2645026436
ssl->buffers.outputBuffer.length += (word32)sendSz;
2645126437

2645226438
ret = SendBuffered(ssl);

src/ssl.c

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4471,15 +4471,53 @@ int wolfSSL_shutdown(WOLFSSL* ssl)
44714471
ret = WOLFSSL_SUCCESS;
44724472
}
44734473
else {
4474+
4475+
/* Try to flush the buffer first, it might contain the alert */
4476+
if (ssl->error == WC_NO_ERR_TRACE(WANT_WRITE) &&
4477+
ssl->buffers.outputBuffer.length > 0) {
4478+
ret = SendBuffered(ssl);
4479+
if (ret != 0) {
4480+
ssl->error = ret;
4481+
/* for error tracing */
4482+
if (ret != WC_NO_ERR_TRACE(WANT_WRITE))
4483+
WOLFSSL_ERROR(ret);
4484+
ret = WOLFSSL_FATAL_ERROR;
4485+
WOLFSSL_LEAVE("wolfSSL_shutdown", ret);
4486+
return ret;
4487+
}
4488+
4489+
ssl->error = WOLFSSL_ERROR_NONE;
4490+
/* we succeeded in sending the alert now */
4491+
if (ssl->options.sentNotify) {
4492+
/* just after we send the alert, if we didn't receive the alert
4493+
* from the other peer yet, return WOLFSSL_STHUDOWN_NOT_DONE */
4494+
if (!ssl->options.closeNotify) {
4495+
ret = WOLFSSL_SHUTDOWN_NOT_DONE;
4496+
WOLFSSL_LEAVE("wolfSSL_shutdown", ret);
4497+
return ret;
4498+
}
4499+
else {
4500+
ssl->options.shutdownDone = 1;
4501+
ret = WOLFSSL_SUCCESS;
4502+
}
4503+
}
4504+
}
4505+
44744506
/* try to send close notify, not an error if can't */
44754507
if (!ssl->options.isClosed && !ssl->options.connReset &&
44764508
!ssl->options.sentNotify) {
44774509
ssl->error = SendAlert(ssl, alert_warning, close_notify);
4510+
4511+
/* the alert is now sent or sitting in the buffer,
4512+
* where will be sent eventually */
4513+
if (ssl->error == 0 || ssl->error == WC_NO_ERR_TRACE(WANT_WRITE))
4514+
ssl->options.sentNotify = 1;
4515+
44784516
if (ssl->error < 0) {
44794517
WOLFSSL_ERROR(ssl->error);
44804518
return WOLFSSL_FATAL_ERROR;
44814519
}
4482-
ssl->options.sentNotify = 1; /* don't send close_notify twice */
4520+
44834521
if (ssl->options.closeNotify) {
44844522
ret = WOLFSSL_SUCCESS;
44854523
ssl->options.shutdownDone = 1;
@@ -4499,7 +4537,7 @@ int wolfSSL_shutdown(WOLFSSL* ssl)
44994537
}
45004538
#endif
45014539

4502-
/* call wolfSSL_shutdown again for bidirectional shutdown */
4540+
/* wolfSSL_shutdown called again for bidirectional shutdown */
45034541
if (ssl->options.sentNotify && !ssl->options.closeNotify) {
45044542
ret = ProcessReply(ssl);
45054543
if ((ret == WC_NO_ERR_TRACE(ZERO_RETURN)) ||
@@ -4509,11 +4547,18 @@ int wolfSSL_shutdown(WOLFSSL* ssl)
45094547
/* Clear error */
45104548
ssl->error = WOLFSSL_ERROR_NONE;
45114549
ret = WOLFSSL_SUCCESS;
4512-
} else if (ret == WC_NO_ERR_TRACE(MEMORY_E)) {
4550+
}
4551+
else if (ret == WC_NO_ERR_TRACE(MEMORY_E)) {
45134552
ret = WOLFSSL_FATAL_ERROR;
4514-
} else if (ssl->error == WOLFSSL_ERROR_NONE) {
4553+
}
4554+
else if (ret == WC_NO_ERR_TRACE(WANT_READ)) {
4555+
ssl->error = ret;
4556+
ret = WOLFSSL_FATAL_ERROR;
4557+
}
4558+
else if (ssl->error == WOLFSSL_ERROR_NONE) {
45154559
ret = WOLFSSL_SHUTDOWN_NOT_DONE;
4516-
} else {
4560+
}
4561+
else {
45174562
WOLFSSL_ERROR(ssl->error);
45184563
ret = WOLFSSL_FATAL_ERROR;
45194564
}

tests/api.c

Lines changed: 50 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -46751,25 +46751,12 @@ static int test_extra_alerts_bad_psk(void)
4675146751
}
4675246752
#endif
4675346753

46754-
#if defined(OPENSSL_EXTRA) && defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && !defined(WOLFSSL_NO_TLS12)
46755-
/*
46756-
* Emulates wolfSSL_shutdown that goes on EAGAIN,
46757-
* by returning on output WOLFSSL_ERROR_WANT_WRITE.*/
46758-
static int custom_wolfSSL_shutdown(WOLFSSL *ssl, char *buf,
46759-
int sz, void *ctx)
46760-
{
46761-
(void)ssl;
46762-
(void)buf;
46763-
(void)ctx;
46764-
(void)sz;
46765-
46766-
return WOLFSSL_CBIO_ERR_WANT_WRITE;
46767-
}
46768-
46769-
static int test_multiple_alerts_EAGAIN(void)
46754+
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && !defined(WOLFSSL_NO_TLS12)
46755+
static int test_multiple_shutdown_nonblocking(void)
4677046756
{
4677146757
EXPECT_DECLS;
4677246758
size_t size_of_last_packet = 0;
46759+
int dummy_recv_buffer;
4677346760

4677446761
/* declare wolfSSL objects */
4677546762
struct test_memio_ctx test_ctx;
@@ -46779,46 +46766,68 @@ static int test_multiple_alerts_EAGAIN(void)
4677946766
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
4678046767

4678146768
/* Create and initialize WOLFSSL_CTX and WOLFSSL objects */
46782-
#ifdef USE_TLSV13
46783-
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
46784-
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
46785-
#else
4678646769
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
4678746770
wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0);
46788-
#endif
46771+
4678946772
ExpectNotNull(ctx_c);
4679046773
ExpectNotNull(ssl_c);
4679146774
ExpectNotNull(ctx_s);
4679246775
ExpectNotNull(ssl_s);
4679346776

46794-
/* Load client certificates into WOLFSSL_CTX */
46795-
ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_c, "./certs/ca-cert.pem", NULL), WOLFSSL_SUCCESS);
46796-
4679746777
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
4679846778

46799-
/*
46800-
* We set the custom callback for the IO to emulate multiple EAGAINs
46801-
* on shutdown, so we can check that we don't send multiple packets.
46802-
* */
46803-
wolfSSL_SSLSetIOSend(ssl_c, custom_wolfSSL_shutdown);
46779+
/* buffers should be empty now */
46780+
ExpectIntEQ(test_ctx.c_len, 0);
46781+
ExpectIntEQ(test_ctx.s_len, 0);
46782+
ExpectIntEQ(ssl_c->buffers.outputBuffer.length, 0);
46783+
46784+
test_memio_simulate_want_write(&test_ctx, 0, 1);
4680446785

4680546786
/*
46806-
* We call wolfSSL_shutdown multiple times to reproduce the behaviour,
46807-
* to check that it doesn't add the CLOSE_NOTIFY packet multiple times
46808-
* on the output buffer.
46787+
* We call wolfSSL_shutdown multiple times to to check that it doesn't add
46788+
* the CLOSE_NOTIFY packet multiple times on the output buffer.
4680946789
* */
46810-
wolfSSL_shutdown(ssl_c);
46811-
wolfSSL_shutdown(ssl_c);
46790+
ExpectIntEQ(wolfSSL_shutdown(ssl_c), -1);
46791+
ExpectIntEQ(wolfSSL_get_error(ssl_c, 0), WOLFSSL_ERROR_WANT_WRITE);
4681246792

46793+
/* store the size of the packet */
4681346794
if (ssl_c != NULL) {
4681446795
size_of_last_packet = ssl_c->buffers.outputBuffer.length;
4681546796
}
46816-
wolfSSL_shutdown(ssl_c);
4681746797

46818-
/*
46819-
* Finally we check the length of the output buffer.
46820-
* */
46821-
ExpectIntEQ((ssl_c->buffers.outputBuffer.length - size_of_last_packet), 0);
46798+
/* invoke it multiple times shouldn't change the wolfssl internal output buffer size */
46799+
ExpectIntEQ(wolfSSL_shutdown(ssl_c), -1);
46800+
ExpectIntEQ(wolfSSL_get_error(ssl_c, 0), WOLFSSL_ERROR_WANT_WRITE);
46801+
ExpectIntEQ(wolfSSL_shutdown(ssl_c), -1);
46802+
ExpectIntEQ(wolfSSL_get_error(ssl_c, 0), WOLFSSL_ERROR_WANT_WRITE);
46803+
46804+
ExpectIntEQ(ssl_c->buffers.outputBuffer.length, size_of_last_packet);
46805+
46806+
/* now send the CLOSE_NOTIFY to the server for real, expecting shutdown not done */
46807+
test_memio_simulate_want_write(&test_ctx, 0, 0);
46808+
ExpectIntEQ(wolfSSL_shutdown(ssl_c), WOLFSSL_SHUTDOWN_NOT_DONE);
46809+
46810+
/* output buffer should be empty and socket buffer should contain the message */
46811+
ExpectIntEQ(ssl_c->buffers.outputBuffer.length, 0);
46812+
ExpectIntEQ(test_ctx.s_len, size_of_last_packet);
46813+
46814+
46815+
/* this should try to read from the socket */
46816+
ExpectIntEQ(wolfSSL_shutdown(ssl_c), -1);
46817+
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
46818+
46819+
/* complete the bidirectional shutdown */
46820+
46821+
/* check that server received the shutdown alert */
46822+
ExpectIntEQ(wolfSSL_recv(ssl_s, &dummy_recv_buffer, 0, 0), 0);
46823+
ExpectIntEQ(wolfSSL_get_error(ssl_s, 0), WOLFSSL_ERROR_ZERO_RETURN);
46824+
46825+
/* send the shutdown from the server side */
46826+
ExpectIntEQ(wolfSSL_shutdown(ssl_s), WOLFSSL_SUCCESS);
46827+
46828+
/* This should return success and zero return */
46829+
ExpectIntEQ(wolfSSL_shutdown(ssl_c), WOLFSSL_SUCCESS);
46830+
ExpectIntEQ(wolfSSL_get_error(ssl_c, 0), WOLFSSL_ERROR_ZERO_RETURN);
4682246831

4682346832
/* Cleanup and return */
4682446833
wolfSSL_CTX_free(ctx_c);
@@ -46829,7 +46838,7 @@ static int test_multiple_alerts_EAGAIN(void)
4682946838
return EXPECT_RESULT();
4683046839
}
4683146840
#else
46832-
static int test_multiple_alerts_EAGAIN(void)
46841+
static int test_multiple_shutdown_nonblocking(void)
4683346842
{
4683446843
return TEST_SKIPPED;
4683546844
}
@@ -51365,7 +51374,7 @@ TEST_CASE testCases[] = {
5136551374
TEST_DECL(test_extra_alerts_wrong_cs),
5136651375
TEST_DECL(test_extra_alerts_skip_hs),
5136751376
TEST_DECL(test_extra_alerts_bad_psk),
51368-
TEST_DECL(test_multiple_alerts_EAGAIN),
51377+
TEST_DECL(test_multiple_shutdown_nonblocking),
5136951378
/* Can't memory test as client/server Asserts. */
5137051379
TEST_DECL(test_harden_no_secure_renegotiation),
5137151380
TEST_DECL(test_override_alt_cert_chain),

tests/utils.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ int test_memio_write_cb(WOLFSSL *ssl, char *data, int sz, void *ctx)
4848
int *len;
4949
int *msg_sizes;
5050
int *msg_count;
51+
int *forceWantWrite;
5152

5253
test_ctx = (struct test_memio_ctx*)ctx;
5354

@@ -56,14 +57,19 @@ int test_memio_write_cb(WOLFSSL *ssl, char *data, int sz, void *ctx)
5657
len = &test_ctx->c_len;
5758
msg_sizes = test_ctx->c_msg_sizes;
5859
msg_count = &test_ctx->c_msg_count;
60+
forceWantWrite = &test_ctx->c_force_want_write;
5961
}
6062
else {
6163
buf = test_ctx->s_buff;
6264
len = &test_ctx->s_len;
6365
msg_sizes = test_ctx->s_msg_sizes;
6466
msg_count = &test_ctx->s_msg_count;
67+
forceWantWrite = &test_ctx->s_force_want_write;
6568
}
6669

70+
if (*forceWantWrite)
71+
return WOLFSSL_CBIO_ERR_WANT_WRITE;
72+
6773
if ((unsigned)(*len + sz) > TEST_MEMIO_BUF_SZ)
6874
return WOLFSSL_CBIO_ERR_WANT_WRITE;
6975

@@ -362,16 +368,30 @@ int test_memio_setup_ex(struct test_memio_ctx *ctx,
362368
return 0;
363369
}
364370

371+
void test_memio_simulate_want_write(struct test_memio_ctx *ctx, int is_client,
372+
int enable)
373+
{
374+
if (ctx == NULL)
375+
return;
376+
377+
if (is_client)
378+
ctx->c_force_want_write = (enable != 0);
379+
else
380+
ctx->s_force_want_write = (enable != 0);
381+
}
382+
365383
void test_memio_clear_buffer(struct test_memio_ctx *ctx, int is_client)
366384
{
367385
if (is_client) {
368386
ctx->c_len = 0;
369387
ctx->c_msg_pos = 0;
370388
ctx->c_msg_count = 0;
389+
ctx->c_force_want_write = 0;
371390
} else {
372391
ctx->s_len = 0;
373392
ctx->s_msg_pos = 0;
374393
ctx->s_msg_count = 0;
394+
ctx->s_force_want_write = 0;
375395
}
376396
}
377397

tests/utils.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ struct test_memio_ctx
4444
int s_len;
4545
const char* s_ciphers;
4646

47+
int c_force_want_write;
48+
int s_force_want_write;
49+
4750
int c_msg_sizes[TEST_MEMIO_MAX_MSGS];
4851
int c_msg_count;
4952
int c_msg_pos;
@@ -64,6 +67,8 @@ int test_memio_setup_ex(struct test_memio_ctx *ctx,
6467
method_provider method_c, method_provider method_s,
6568
byte *caCert, int caCertSz, byte *serverCert, int serverCertSz,
6669
byte *serverKey, int serverKeySz);
70+
void test_memio_simulate_want_write(struct test_memio_ctx *ctx, int is_client,
71+
int enable);
6772
void test_memio_clear_buffer(struct test_memio_ctx *ctx, int is_client);
6873
int test_memio_inject_message(struct test_memio_ctx *ctx, int client, const char *data, int sz);
6974
int test_memio_copy_message(const struct test_memio_ctx *ctx, int client,

0 commit comments

Comments
 (0)