From 056ed9e52e1eddb2b35c82bacb855b0061f68be7 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Wed, 18 Mar 2026 18:26:43 +0100 Subject: [PATCH 1/3] Add custom BIO callback dispatching - Route BIO_ctrl_pending, BIO_reset, and BIO_get_mem_data through the custom method's ctrlCb when set, enabling fully custom BIO types to handle these operations. - Add test_wolfSSL_BIO_custom_method that exercises a custom BIO with all callbacks (create, destroy, read, write, puts, gets, ctrl) and verifies each callback is invoked via bitfield tracking. --- src/bio.c | 15 ++- tests/api/test_ossl_bio.c | 209 ++++++++++++++++++++++++++++++++++++++ tests/api/test_ossl_bio.h | 4 +- 3 files changed, 226 insertions(+), 2 deletions(-) diff --git a/src/bio.c b/src/bio.c index 02431dd38ae..a4debfb51c6 100644 --- a/src/bio.c +++ b/src/bio.c @@ -1303,6 +1303,11 @@ size_t wolfSSL_BIO_ctrl_pending(WOLFSSL_BIO *bio) return 0; } + if (bio->method != NULL && bio->method->ctrlCb != NULL) { + WOLFSSL_MSG("Calling custom BIO ctrl pending callback"); + return (size_t)bio->method->ctrlCb(bio, WOLFSSL_BIO_CTRL_PENDING, 0, NULL); + } + if (bio->type == WOLFSSL_BIO_MD || bio->type == WOLFSSL_BIO_BASE64) { /* these are wrappers only, get next bio */ @@ -1713,6 +1718,11 @@ int wolfSSL_BIO_reset(WOLFSSL_BIO *bio) return WOLFSSL_BIO_ERROR; } + if (bio->method != NULL && bio->method->ctrlCb != NULL) { + WOLFSSL_MSG("Calling custom BIO reset callback"); + return (int)bio->method->ctrlCb(bio, WOLFSSL_BIO_CTRL_RESET, 0, NULL); + } + switch (bio->type) { #ifndef NO_FILESYSTEM case WOLFSSL_BIO_FILE: @@ -2184,7 +2194,10 @@ int wolfSSL_BIO_get_mem_data(WOLFSSL_BIO* bio, void* p) if (bio == NULL) return WOLFSSL_FATAL_ERROR; - + if (bio->method != NULL && bio->method->ctrlCb != NULL) { + WOLFSSL_MSG("Calling custom BIO get mem data callback"); + return (int)bio->method->ctrlCb(bio, WOLFSSL_BIO_CTRL_INFO, 0, p); + } mem_bio = bio; /* Return pointer from last memory BIO in chain */ while (bio->next) { diff --git a/tests/api/test_ossl_bio.c b/tests/api/test_ossl_bio.c index 9c928b620ff..6ecb6f4ef47 100644 --- a/tests/api/test_ossl_bio.c +++ b/tests/api/test_ossl_bio.c @@ -1466,5 +1466,214 @@ int test_wolfSSL_BIO_BIO_ring_read(void) return EXPECT_RESULT(); } +/* Custom BIO backing store for test_wolfSSL_BIO_custom_method */ +#if defined(OPENSSL_EXTRA) + +struct custom_bio_data { + char buf[256]; + int len; + byte createCalled:1; + byte destroyCalled:1; + byte writeCalled:1; + byte readCalled:1; + byte putsCalled:1; + byte getsCalled:1; + byte ctrlCalled:1; +}; + +static int custom_bio_createCb(WOLFSSL_BIO* bio) +{ + struct custom_bio_data* data; + data = (struct custom_bio_data*)XMALLOC(sizeof(*data), NULL, + DYNAMIC_TYPE_TMP_BUFFER); + if (data == NULL) + return 0; + XMEMSET(data, 0, sizeof(*data)); + data->createCalled = 1; + BIO_set_data(bio, data); + BIO_set_init(bio, 1); + return 1; +} + +static int custom_bio_destroyCb(WOLFSSL_BIO* bio) +{ + struct custom_bio_data* data = (struct custom_bio_data*)BIO_get_data(bio); + if (data != NULL) { + data->destroyCalled = 1; + XFREE(data, NULL, DYNAMIC_TYPE_TMP_BUFFER); + } + BIO_set_data(bio, NULL); + return 1; +} + +static int custom_bio_writeCb(WOLFSSL_BIO* bio, const char* in, int len) +{ + struct custom_bio_data* data = (struct custom_bio_data*)BIO_get_data(bio); + int avail; + if (data == NULL || in == NULL || len <= 0) + return -1; + data->writeCalled = 1; + avail = (int)sizeof(data->buf) - data->len; + if (len > avail) + len = avail; + XMEMCPY(data->buf + data->len, in, (size_t)len); + data->len += len; + return len; +} + +static int custom_bio_readCb(WOLFSSL_BIO* bio, char* out, int len) +{ + struct custom_bio_data* data = (struct custom_bio_data*)BIO_get_data(bio); + if (data == NULL || out == NULL || len <= 0) + return -1; + data->readCalled = 1; + if (data->len == 0) + return 0; + if (len > data->len) + len = data->len; + XMEMCPY(out, data->buf, (size_t)len); + /* shift remaining data */ + data->len -= len; + if (data->len > 0) + XMEMMOVE(data->buf, data->buf + len, (size_t)data->len); + return len; +} + +static int custom_bio_putsCb(WOLFSSL_BIO* bio, const char* str) +{ + struct custom_bio_data* data = (struct custom_bio_data*)BIO_get_data(bio); + if (str == NULL || data == NULL) + return -1; + data->putsCalled = 1; + return custom_bio_writeCb(bio, str, (int)XSTRLEN(str)); +} + +static int custom_bio_getsCb(WOLFSSL_BIO* bio, char* buf, int sz) +{ + struct custom_bio_data* data = (struct custom_bio_data*)BIO_get_data(bio); + int i; + int readLen; + if (data == NULL || buf == NULL || sz <= 1) + return -1; + data->getsCalled = 1; + if (data->len == 0) + return 0; + /* read up to newline or sz-1 bytes */ + readLen = (sz - 1 < data->len) ? sz - 1 : data->len; + for (i = 0; i < readLen; i++) { + buf[i] = data->buf[i]; + if (data->buf[i] == '\n') { + i++; + break; + } + } + buf[i] = '\0'; + /* shift remaining data */ + data->len -= i; + if (data->len > 0) + XMEMMOVE(data->buf, data->buf + i, (size_t)data->len); + return i; +} + +static long custom_bio_ctrlCb(WOLFSSL_BIO* bio, int cmd, long larg, void* parg) +{ + struct custom_bio_data* data = (struct custom_bio_data*)BIO_get_data(bio); + (void)larg; + if (data == NULL) + return -1; + data->ctrlCalled = 1; + switch (cmd) { + case BIO_CTRL_PENDING: + return (long)data->len; + case BIO_CTRL_RESET: + data->len = 0; + return WOLFSSL_SUCCESS; + case BIO_CTRL_FLUSH: + return 1; + case BIO_CTRL_INFO: + if (parg != NULL) + *(char**)parg = data->buf; + return (long)data->len; + default: + return 0; + } +} + +#endif /* OPENSSL_EXTRA */ + +int test_wolfSSL_BIO_custom_method(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) + BIO_METHOD* method = NULL; + BIO* bio = NULL; + struct custom_bio_data* data = NULL; + char buf[256]; + char* memPtr = NULL; + + /* Create custom method and set all callbacks */ + ExpectNotNull(method = BIO_meth_new(WOLFSSL_BIO_UNDEF, "custom_test")); + ExpectIntEQ(BIO_meth_set_create(method, custom_bio_createCb), + WOLFSSL_SUCCESS); + ExpectIntEQ(BIO_meth_set_destroy(method, custom_bio_destroyCb), + WOLFSSL_SUCCESS); + ExpectIntEQ(BIO_meth_set_write(method, custom_bio_writeCb), + WOLFSSL_SUCCESS); + ExpectIntEQ(BIO_meth_set_read(method, custom_bio_readCb), + WOLFSSL_SUCCESS); + ExpectIntEQ(BIO_meth_set_puts(method, custom_bio_putsCb), + WOLFSSL_SUCCESS); + ExpectIntEQ(BIO_meth_set_gets(method, custom_bio_getsCb), + WOLFSSL_SUCCESS); + ExpectIntEQ(BIO_meth_set_ctrl(method, custom_bio_ctrlCb), + WOLFSSL_SUCCESS); + + /* Create BIO - should invoke createCb */ + ExpectNotNull(bio = BIO_new(method)); + ExpectNotNull(data = (struct custom_bio_data*)BIO_get_data(bio)); + ExpectTrue(data->createCalled); + + /* write */ + ExpectIntEQ(BIO_write(bio, "hello", 5), 5); + ExpectTrue(data->writeCalled); + + /* ctrl_pending via ctrlCb */ + ExpectIntEQ((int)BIO_ctrl_pending(bio), 5); + ExpectTrue(data->ctrlCalled); + + /* read */ + XMEMSET(buf, 0, sizeof(buf)); + ExpectIntEQ(BIO_read(bio, buf, 5), 5); + ExpectBufEQ(buf, "hello", 5); + ExpectTrue(data->readCalled); + ExpectIntEQ((int)BIO_ctrl_pending(bio), 0); + + /* puts */ + ExpectIntEQ(BIO_puts(bio, "world\nfoo"), 9); + ExpectTrue(data->putsCalled); + ExpectIntEQ((int)BIO_ctrl_pending(bio), 9); + + /* gets - should read up to and including first newline */ + XMEMSET(buf, 0, sizeof(buf)); + ExpectIntEQ(BIO_gets(bio, buf, (int)sizeof(buf)), 6); + ExpectStrEQ(buf, "world\n"); + ExpectTrue(data->getsCalled); + ExpectIntEQ((int)BIO_ctrl_pending(bio), 3); + + /* get_mem_data via BIO_CTRL_INFO */ + ExpectIntEQ(BIO_get_mem_data(bio, &memPtr), 3); + ExpectNotNull(memPtr); + + /* reset via ctrlCb */ + ExpectIntEQ(BIO_reset(bio), WOLFSSL_SUCCESS); + ExpectIntEQ((int)BIO_ctrl_pending(bio), 0); + + /* free - should invoke destroyCb */ + BIO_free(bio); + BIO_meth_free(method); +#endif + return EXPECT_RESULT(); +} + #endif /* !NO_BIO */ diff --git a/tests/api/test_ossl_bio.h b/tests/api/test_ossl_bio.h index 7b04242a284..edcce4bbdfc 100644 --- a/tests/api/test_ossl_bio.h +++ b/tests/api/test_ossl_bio.h @@ -42,6 +42,7 @@ int test_wolfSSL_BIO_reset(void); int test_wolfSSL_BIO_get_len(void); int test_wolfSSL_BIO(void); int test_wolfSSL_BIO_BIO_ring_read(void); +int test_wolfSSL_BIO_custom_method(void); #define TEST_OSSL_BIO_DECLS \ TEST_DECL_GROUP("ossl_bio", test_wolfSSL_BIO_gets), \ @@ -56,7 +57,8 @@ int test_wolfSSL_BIO_BIO_ring_read(void); TEST_DECL_GROUP("ossl_bio", test_wolfSSL_BIO_reset), \ TEST_DECL_GROUP("ossl_bio", test_wolfSSL_BIO_get_len), \ TEST_DECL_GROUP("ossl_bio", test_wolfSSL_BIO), \ - TEST_DECL_GROUP("ossl_bio", test_wolfSSL_BIO_BIO_ring_read) + TEST_DECL_GROUP("ossl_bio", test_wolfSSL_BIO_BIO_ring_read), \ + TEST_DECL_GROUP("ossl_bio", test_wolfSSL_BIO_custom_method) #define TEST_OSSL_BIO_TLS_DECLS \ TEST_DECL_GROUP("ossl_bio_tls", test_wolfSSL_BIO_connect), \ From 84da6d22c83066a5d5307231586566dde8912f43 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Fri, 20 Mar 2026 15:42:50 +0100 Subject: [PATCH 2/3] Address code review --- src/bio.c | 4 +++- tests/api/test_ossl_bio.c | 7 +++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/bio.c b/src/bio.c index a4debfb51c6..328a01ddf69 100644 --- a/src/bio.c +++ b/src/bio.c @@ -1304,8 +1304,10 @@ size_t wolfSSL_BIO_ctrl_pending(WOLFSSL_BIO *bio) } if (bio->method != NULL && bio->method->ctrlCb != NULL) { + long ret; WOLFSSL_MSG("Calling custom BIO ctrl pending callback"); - return (size_t)bio->method->ctrlCb(bio, WOLFSSL_BIO_CTRL_PENDING, 0, NULL); + ret = bio->method->ctrlCb(bio, WOLFSSL_BIO_CTRL_PENDING, 0, NULL); + return (ret < 0) ? 0 : (size_t)ret; } if (bio->type == WOLFSSL_BIO_MD || diff --git a/tests/api/test_ossl_bio.c b/tests/api/test_ossl_bio.c index 6ecb6f4ef47..05d04a40157 100644 --- a/tests/api/test_ossl_bio.c +++ b/tests/api/test_ossl_bio.c @@ -1469,11 +1469,12 @@ int test_wolfSSL_BIO_BIO_ring_read(void) /* Custom BIO backing store for test_wolfSSL_BIO_custom_method */ #if defined(OPENSSL_EXTRA) +static int custom_bio_destroyCalled = 0; + struct custom_bio_data { char buf[256]; int len; byte createCalled:1; - byte destroyCalled:1; byte writeCalled:1; byte readCalled:1; byte putsCalled:1; @@ -1499,7 +1500,7 @@ static int custom_bio_destroyCb(WOLFSSL_BIO* bio) { struct custom_bio_data* data = (struct custom_bio_data*)BIO_get_data(bio); if (data != NULL) { - data->destroyCalled = 1; + custom_bio_destroyCalled = 1; XFREE(data, NULL, DYNAMIC_TYPE_TMP_BUFFER); } BIO_set_data(bio, NULL); @@ -1669,7 +1670,9 @@ int test_wolfSSL_BIO_custom_method(void) ExpectIntEQ((int)BIO_ctrl_pending(bio), 0); /* free - should invoke destroyCb */ + custom_bio_destroyCalled = 0; BIO_free(bio); + ExpectTrue(custom_bio_destroyCalled); BIO_meth_free(method); #endif return EXPECT_RESULT(); From 431ee9e2d89bd74315c64d73ad4b0fbc5bc6d192 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Fri, 20 Mar 2026 17:03:26 +0100 Subject: [PATCH 3/3] Implement wolfSSL_BIO_set_init --- src/bio.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/bio.c b/src/bio.c index 328a01ddf69..03acfb42fdc 100644 --- a/src/bio.c +++ b/src/bio.c @@ -2028,6 +2028,13 @@ void* wolfSSL_BIO_get_data(WOLFSSL_BIO* bio) return NULL; } +void wolfSSL_BIO_set_init(WOLFSSL_BIO* bio, int init) +{ + WOLFSSL_ENTER("wolfSSL_BIO_set_init"); + if (bio != NULL) + bio->init = (byte)(init != 0); +} + /* If flag is 0 then blocking is set, if 1 then non blocking. * Always returns WOLFSSL_SUCCESS. */ @@ -3635,15 +3642,6 @@ int wolfSSL_BIO_new_bio_pair(WOLFSSL_BIO **bio1_p, size_t writebuf1, #ifdef OPENSSL_ALL -#ifndef NO_WOLFSSL_STUB -void wolfSSL_BIO_set_init(WOLFSSL_BIO* bio, int init) -{ - WOLFSSL_STUB("wolfSSL_BIO_set_init"); - (void)bio; - (void)init; -} -#endif /* NO_WOLFSSL_STUB */ - void wolfSSL_BIO_set_shutdown(WOLFSSL_BIO* bio, int shut) { WOLFSSL_ENTER("wolfSSL_BIO_set_shutdown");