Skip to content

Commit 0e0f8ec

Browse files
committed
Fix non-blocking OCSP pendingMsg buffer free on fragmented handshake
When WOLFSSL_NONBLOCK_OCSP is enabled without WOLFSSL_ASYNC_CRYPT and a handshake message (e.g. Certificate) is fragmented due to low MFL, the pendingMsg buffer was freed after OCSP_WANT_READ, causing BUFFER_ERROR on re-entry. Extend the preprocessor guard and condition check in both DoTls13HandShakeMsg (tls13.c) and DoHandShakeMsg (internal.c) to preserve pendingMsg on OCSP_WANT_READ, matching the existing pattern used elsewhere in the codebase. Add regression tests for TLS 1.3 and TLS 1.2 code paths. Ref: wolfSSL#9957 Signed-off-by: Sameeh Jubran <sameeh@wolfssl.com>
1 parent 0792c67 commit 0e0f8ec

File tree

3 files changed

+201
-4
lines changed

3 files changed

+201
-4
lines changed

src/internal.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18781,8 +18781,9 @@ static int DoHandShakeMsg(WOLFSSL* ssl, byte* input, word32* inOutIdx,
1878118781
&idx, ssl->arrays->pendingMsgType,
1878218782
ssl->arrays->pendingMsgSz - idx,
1878318783
ssl->arrays->pendingMsgSz);
18784-
#ifdef WOLFSSL_ASYNC_CRYPT
18785-
if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) {
18784+
#if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLFSSL_NONBLOCK_OCSP)
18785+
if (ret == WC_NO_ERR_TRACE(WC_PENDING_E) ||
18786+
ret == WC_NO_ERR_TRACE(OCSP_WANT_READ)) {
1878618787
/* setup to process fragment again */
1878718788
ssl->arrays->pendingMsgOffset -= inputLength;
1878818789
*inOutIdx -= inputLength;

src/tls13.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13449,8 +13449,9 @@ int DoTls13HandShakeMsg(WOLFSSL* ssl, byte* input, word32* inOutIdx,
1344913449
&idx, ssl->arrays->pendingMsgType,
1345013450
ssl->arrays->pendingMsgSz - HANDSHAKE_HEADER_SZ,
1345113451
ssl->arrays->pendingMsgSz);
13452-
#ifdef WOLFSSL_ASYNC_CRYPT
13453-
if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) {
13452+
#if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLFSSL_NONBLOCK_OCSP)
13453+
if (ret == WC_NO_ERR_TRACE(WC_PENDING_E) ||
13454+
ret == WC_NO_ERR_TRACE(OCSP_WANT_READ)) {
1345413455
/* setup to process fragment again */
1345513456
ssl->arrays->pendingMsgOffset -= inputLength;
1345613457
*inOutIdx -= inputLength + ssl->keys.padSz;

tests/api.c

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33418,6 +33418,197 @@ static int test_ocsp_callback_fails(void)
3341833418
defined(HAVE_OCSP) && \
3341933419
defined(HAVE_CERTIFICATE_STATUS_REQUEST) */
3342033420

33421+
/* ---------------------------------------------------------------------------
33422+
* Tests for non-blocking OCSP with fragmented handshake messages.
33423+
*
33424+
* Reproducer for: https://github.com/wolfSSL/wolfssl/pull/9957
33425+
*
33426+
* When WOLFSSL_NONBLOCK_OCSP is enabled without WOLFSSL_ASYNC_CRYPT and the
33427+
* Certificate handshake message is fragmented (due to a low MFL), the
33428+
* pendingMsg buffer was incorrectly freed after DoTls13HandShakeMsgType
33429+
* returned OCSP_WANT_READ. On re-entry the assembled certificate data is
33430+
* gone, causing BUFFER_ERROR.
33431+
*
33432+
* The same pattern applies to the TLS 1.2 code path in DoHandShakeMsg
33433+
* (internal.c).
33434+
*
33435+
* Strategy: use an OCSP IO callback that always returns WANT_READ. Step
33436+
* through the handshake and verify we keep getting OCSP_WANT_READ (the
33437+
* buffer is preserved) rather than BUFFER_ERROR (the buffer was freed).
33438+
* --------------------------------------------------------------------------- */
33439+
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
33440+
defined(HAVE_OCSP) && defined(WOLFSSL_NONBLOCK_OCSP) && \
33441+
defined(HAVE_MAX_FRAGMENT)
33442+
33443+
static int test_ocsp_nonblock_frag_cb(void* ctx, const char* url, int urlSz,
33444+
unsigned char* request, int requestSz, unsigned char** response)
33445+
{
33446+
(void)ctx;
33447+
(void)url;
33448+
(void)urlSz;
33449+
(void)request;
33450+
(void)requestSz;
33451+
(void)response;
33452+
return WOLFSSL_CBIO_ERR_WANT_READ;
33453+
}
33454+
33455+
static void test_ocsp_nonblock_frag_resp_free(void* ctx,
33456+
unsigned char* response)
33457+
{
33458+
(void)ctx;
33459+
(void)response;
33460+
}
33461+
33462+
static int test_ocsp_nonblock_frag_do_handshake(WOLFSSL* ssl_c,
33463+
WOLFSSL* ssl_s, int max_rounds)
33464+
{
33465+
byte hs_c = 0, hs_s = 0;
33466+
int ret, err;
33467+
33468+
while (!(hs_c && hs_s) && max_rounds-- > 0) {
33469+
if (!hs_c) {
33470+
ret = wolfSSL_connect(ssl_c);
33471+
if (ret == WOLFSSL_SUCCESS) {
33472+
hs_c = 1;
33473+
}
33474+
else {
33475+
err = wolfSSL_get_error(ssl_c, ret);
33476+
if (err == WC_NO_ERR_TRACE(BUFFER_ERROR))
33477+
return BUFFER_ERROR;
33478+
if (err != WOLFSSL_ERROR_WANT_READ &&
33479+
err != WOLFSSL_ERROR_WANT_WRITE &&
33480+
err != WC_NO_ERR_TRACE(OCSP_WANT_READ)) {
33481+
return err;
33482+
}
33483+
}
33484+
}
33485+
if (!hs_s) {
33486+
ret = wolfSSL_accept(ssl_s);
33487+
if (ret == WOLFSSL_SUCCESS) {
33488+
hs_s = 1;
33489+
}
33490+
else {
33491+
err = wolfSSL_get_error(ssl_s, ret);
33492+
if (err == WC_NO_ERR_TRACE(BUFFER_ERROR))
33493+
return BUFFER_ERROR;
33494+
if (err != WOLFSSL_ERROR_WANT_READ &&
33495+
err != WOLFSSL_ERROR_WANT_WRITE &&
33496+
err != WC_NO_ERR_TRACE(OCSP_WANT_READ)) {
33497+
return err;
33498+
}
33499+
}
33500+
}
33501+
}
33502+
if (hs_c && hs_s)
33503+
return 0;
33504+
return WOLFSSL_ERROR_WANT_READ;
33505+
}
33506+
33507+
#if defined(WOLFSSL_TLS13)
33508+
static int test_tls13_nonblock_ocsp_fragment(void)
33509+
{
33510+
EXPECT_DECLS;
33511+
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
33512+
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
33513+
struct test_memio_ctx test_ctx;
33514+
int ret;
33515+
33516+
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
33517+
33518+
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
33519+
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
33520+
33521+
ExpectIntEQ(wolfSSL_CTX_EnableOCSP(ctx_c,
33522+
WOLFSSL_OCSP_CHECKALL | WOLFSSL_OCSP_URL_OVERRIDE), WOLFSSL_SUCCESS);
33523+
ExpectIntEQ(wolfSSL_CTX_SetOCSP_OverrideURL(ctx_c, "http://127.0.0.1:0"),
33524+
WOLFSSL_SUCCESS);
33525+
ExpectIntEQ(wolfSSL_CTX_SetOCSP_Cb(ctx_c,
33526+
test_ocsp_nonblock_frag_cb, test_ocsp_nonblock_frag_resp_free, NULL),
33527+
WOLFSSL_SUCCESS);
33528+
33529+
/* MFL 1024 — small enough to fragment a typical Certificate message */
33530+
ExpectIntEQ(wolfSSL_UseMaxFragment(ssl_c, WOLFSSL_MFL_2_10),
33531+
WOLFSSL_SUCCESS);
33532+
33533+
if (EXPECT_SUCCESS()) {
33534+
ret = test_ocsp_nonblock_frag_do_handshake(ssl_c, ssl_s, 20);
33535+
/* Handshake won't complete (OCSP never finishes), but the critical
33536+
* assertion is that we never got BUFFER_ERROR. */
33537+
ExpectIntNE(ret, BUFFER_ERROR);
33538+
}
33539+
33540+
wolfSSL_free(ssl_c);
33541+
wolfSSL_free(ssl_s);
33542+
wolfSSL_CTX_free(ctx_c);
33543+
wolfSSL_CTX_free(ctx_s);
33544+
33545+
return EXPECT_RESULT();
33546+
}
33547+
#else
33548+
static int test_tls13_nonblock_ocsp_fragment(void)
33549+
{
33550+
return TEST_SKIPPED;
33551+
}
33552+
#endif /* WOLFSSL_TLS13 */
33553+
33554+
#if !defined(WOLFSSL_NO_TLS12)
33555+
static int test_tls12_nonblock_ocsp_fragment(void)
33556+
{
33557+
EXPECT_DECLS;
33558+
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
33559+
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
33560+
struct test_memio_ctx test_ctx;
33561+
int ret;
33562+
33563+
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
33564+
33565+
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
33566+
wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0);
33567+
33568+
ExpectIntEQ(wolfSSL_CTX_EnableOCSP(ctx_c,
33569+
WOLFSSL_OCSP_CHECKALL | WOLFSSL_OCSP_URL_OVERRIDE), WOLFSSL_SUCCESS);
33570+
ExpectIntEQ(wolfSSL_CTX_SetOCSP_OverrideURL(ctx_c, "http://127.0.0.1:0"),
33571+
WOLFSSL_SUCCESS);
33572+
ExpectIntEQ(wolfSSL_CTX_SetOCSP_Cb(ctx_c,
33573+
test_ocsp_nonblock_frag_cb, test_ocsp_nonblock_frag_resp_free, NULL),
33574+
WOLFSSL_SUCCESS);
33575+
33576+
/* MFL 1024 — small enough to fragment a typical Certificate message */
33577+
ExpectIntEQ(wolfSSL_UseMaxFragment(ssl_c, WOLFSSL_MFL_2_10),
33578+
WOLFSSL_SUCCESS);
33579+
33580+
if (EXPECT_SUCCESS()) {
33581+
ret = test_ocsp_nonblock_frag_do_handshake(ssl_c, ssl_s, 20);
33582+
ExpectIntNE(ret, BUFFER_ERROR);
33583+
}
33584+
33585+
wolfSSL_free(ssl_c);
33586+
wolfSSL_free(ssl_s);
33587+
wolfSSL_CTX_free(ctx_c);
33588+
wolfSSL_CTX_free(ctx_s);
33589+
33590+
return EXPECT_RESULT();
33591+
}
33592+
#else
33593+
static int test_tls12_nonblock_ocsp_fragment(void)
33594+
{
33595+
return TEST_SKIPPED;
33596+
}
33597+
#endif /* !WOLFSSL_NO_TLS12 */
33598+
33599+
#else /* !HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES || !HAVE_OCSP ||
33600+
* !WOLFSSL_NONBLOCK_OCSP || !HAVE_MAX_FRAGMENT */
33601+
static int test_tls13_nonblock_ocsp_fragment(void)
33602+
{
33603+
return TEST_SKIPPED;
33604+
}
33605+
static int test_tls12_nonblock_ocsp_fragment(void)
33606+
{
33607+
return TEST_SKIPPED;
33608+
}
33609+
#endif /* HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES && HAVE_OCSP &&
33610+
* WOLFSSL_NONBLOCK_OCSP && HAVE_MAX_FRAGMENT */
33611+
3342133612
#ifdef HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES
3342233613
static int test_wolfSSL_SSLDisableRead_recv(WOLFSSL *ssl, char *buf, int sz,
3342333614
void *ctx)
@@ -34411,6 +34602,10 @@ TEST_CASE testCases[] = {
3441134602
TEST_DECL(test_self_signed_stapling),
3441234603
TEST_DECL(test_ocsp_callback_fails),
3441334604

34605+
/* Non-blocking OCSP with fragmented handshake (PR #9957) */
34606+
TEST_DECL(test_tls13_nonblock_ocsp_fragment),
34607+
TEST_DECL(test_tls12_nonblock_ocsp_fragment),
34608+
3441434609
/* Multicast */
3441534610
TEST_DECL(test_wolfSSL_mcast),
3441634611

0 commit comments

Comments
 (0)