Merge pull request #10164 from rizlik/bio

BIO improvements and fixes
This commit is contained in:
David Garske
2026-04-13 12:40:02 -07:00
committed by GitHub
5 changed files with 219 additions and 107 deletions
+84 -103
View File
@@ -164,7 +164,7 @@ static int wolfSSL_BIO_MEMORY_read(WOLFSSL_BIO* bio, void* buf, int len)
/* Resize the memory so we are not taking up more than necessary.
* memmove reverts internally to memcpy if areas don't overlap */
XMEMMOVE(bio->mem_buf->data, bio->mem_buf->data + bio->rdIdx,
(long unsigned int)bio->wrSz - (size_t)bio->rdIdx);
(size_t)bio->wrSz - (size_t)bio->rdIdx);
bio->wrSz -= bio->rdIdx;
bio->rdIdx = 0;
/* Resize down to WOLFSSL_BIO_RESIZE_THRESHOLD for fewer
@@ -592,34 +592,40 @@ static int wolfSSL_BIO_BIO_write(WOLFSSL_BIO* bio, const void* data,
* data buffer holding the data to be written
* len length of data buffer
*
* returns the amount of data written on success and WOLFSSL_FAILURE or
* WOLFSSL_BIO_ERROR for failure cases.
* returns the amount of data written on success or WOLFSSL_BIO_ERROR
* for failure cases.
*/
static int wolfSSL_BIO_MEMORY_write(WOLFSSL_BIO* bio, const void* data,
int len)
{
WOLFSSL_ENTER("wolfSSL_BIO_MEMORY_write");
if (bio == NULL || bio->mem_buf == NULL || data == NULL) {
WOLFSSL_MSG("one of input parameters is null");
return WOLFSSL_FAILURE;
}
if (bio->flags & WOLFSSL_BIO_FLAG_MEM_RDONLY) {
return WOLFSSL_FAILURE;
/* ossl returns 0 on bio == NULL */
if (bio == NULL) {
return 0;
}
if (len == 0)
return WOLFSSL_SUCCESS; /* Return early to make logic simpler */
if (bio->mem_buf == NULL || data == NULL) {
WOLFSSL_MSG("one of input parameters is null");
return WOLFSSL_BIO_ERROR;
}
if (bio->flags & WOLFSSL_BIO_FLAG_MEM_RDONLY) {
return WOLFSSL_BIO_ERROR;
}
if (len <= 0)
return 0; /* Nothing to write */
if (wolfSSL_BUF_MEM_grow_ex(bio->mem_buf, ((size_t)bio->wrSz) +
((size_t)len), 0) == 0) {
WOLFSSL_MSG("Error growing memory area");
return WOLFSSL_FAILURE;
return WOLFSSL_BIO_ERROR;
}
if (bio->mem_buf->data == NULL) {
WOLFSSL_MSG("Buffer data is NULL");
return WOLFSSL_FAILURE;
return WOLFSSL_BIO_ERROR;
}
XMEMCPY(bio->mem_buf->data + bio->wrSz, data, (size_t)len);
@@ -868,7 +874,11 @@ long wolfSSL_BIO_ctrl(WOLFSSL_BIO *bio, int cmd, long larg, void *parg)
WOLFSSL_ENTER("wolfSSL_BIO_ctrl");
if (bio && bio->method && bio->method->ctrlCb) {
if (bio == NULL) {
return WOLFSSL_FAILURE;
}
if (bio->method && bio->method->ctrlCb) {
return bio->method->ctrlCb(bio, cmd, larg, parg);
}
@@ -952,6 +962,7 @@ int wolfSSL_BIO_up_ref(WOLFSSL_BIO* bio)
#ifdef WOLFSSL_REFCNT_ERROR_RETURN
if (ret != 0) {
WOLFSSL_MSG("Failed to lock BIO mutex");
return WOLFSSL_FAILURE;
}
#else
(void)ret;
@@ -984,6 +995,8 @@ void wolfSSL_BIO_ADDR_clear(WOLFSSL_BIO_ADDR *addr) {
}
socklen_t wolfSSL_BIO_ADDR_size(const WOLFSSL_BIO_ADDR *addr) {
if (addr == NULL)
return 0;
switch (addr->sa.sa_family) {
#ifndef WOLFSSL_NO_BIO_ADDR_IN
case AF_INET:
@@ -1101,20 +1114,17 @@ int wolfSSL_BIO_gets(WOLFSSL_BIO* bio, char* buf, int sz)
}
cSz = wolfSSL_getLineLength((char*)c, cSz);
/* check case where line was bigger then buffer and buffer
* needs end terminator */
if (cSz >= sz) {
cSz = sz - 1;
buf[cSz] = '\0';
}
else {
/* not minus 1 here because placing terminator after
msg and have checked that sz is large enough */
buf[cSz] = '\0';
}
ret = wolfSSL_BIO_MEMORY_read(bio, (void*)buf, cSz);
/* ret is read after the switch statement */
if (ret > 0) {
buf[ret] = '\0';
}
else {
buf[0] = '\0';
}
break;
}
case WOLFSSL_BIO_BIO:
@@ -1129,21 +1139,17 @@ int wolfSSL_BIO_gets(WOLFSSL_BIO* bio, char* buf, int sz)
}
cSz = wolfSSL_getLineLength(c, cSz);
/* check case where line was bigger then buffer and buffer
* needs end terminator */
if (cSz >= sz) {
cSz = sz - 1;
buf[cSz] = '\0';
}
else {
/* not minus 1 here because placing terminator after
msg and have checked that sz is large enough */
buf[cSz] = '\0';
}
ret = wolfSSL_BIO_nread(bio, &c, cSz);
if (ret > 0 && ret < sz) {
if (ret > 0) {
XMEMCPY(buf, c, (size_t)ret);
buf[ret] = '\0';
}
else {
buf[0] = '\0';
}
break;
}
@@ -1299,51 +1305,42 @@ size_t wolfSSL_BIO_ctrl_pending(WOLFSSL_BIO *bio)
WOLFSSL_ENTER("wolfSSL_BIO_ctrl_pending");
#endif
if (bio == NULL) {
return 0;
}
if (bio->method != NULL && bio->method->ctrlCb != NULL) {
long ret;
WOLFSSL_MSG("Calling custom BIO ctrl pending callback");
ret = bio->method->ctrlCb(bio, WOLFSSL_BIO_CTRL_PENDING, 0, NULL);
return (ret < 0) ? 0 : (size_t)ret;
}
if (bio->type == WOLFSSL_BIO_MD ||
bio->type == WOLFSSL_BIO_BASE64) {
/* these are wrappers only, get next bio */
while (bio->next != NULL) {
bio = bio->next;
if (bio->type == WOLFSSL_BIO_MD ||
bio->type == WOLFSSL_BIO_BASE64) {
break;
}
for (; bio != NULL; bio = bio->next) {
if (bio->method != NULL && bio->method->ctrlCb != NULL) {
long ret;
WOLFSSL_MSG("Calling custom BIO ctrl pending callback");
ret = bio->method->ctrlCb(bio, WOLFSSL_BIO_CTRL_PENDING, 0, NULL);
return (ret < 0) ? 0 : (size_t)ret;
}
}
switch (bio->type) {
case WOLFSSL_BIO_MD:
case WOLFSSL_BIO_BASE64:
/* wrappers only, skip to next bio in chain */
continue;
#ifndef WOLFCRYPT_ONLY
if (bio->type == WOLFSSL_BIO_SSL && bio->ptr.ssl != NULL) {
return (size_t)wolfSSL_pending(bio->ptr.ssl);
}
case WOLFSSL_BIO_SSL:
if (bio->ptr.ssl != NULL)
return (size_t)wolfSSL_pending(bio->ptr.ssl);
return 0;
#endif
if (bio->type == WOLFSSL_BIO_MEMORY) {
return (size_t)(bio->wrSz - bio->rdIdx);
}
/* type BIO_BIO then check paired buffer */
if (bio->type == WOLFSSL_BIO_BIO && bio->pair != NULL) {
WOLFSSL_BIO* pair = bio->pair;
if (pair->wrIdx > 0 && pair->wrIdx <= pair->rdIdx) {
/* in wrap around state where beginning of buffer is being
* overwritten */
return ((size_t)pair->wrSz) - ((size_t)pair->rdIdx) +
((size_t)pair->wrIdx);
}
else {
/* simple case where has not wrapped around */
return (size_t)(pair->wrIdx - pair->rdIdx);
case WOLFSSL_BIO_MEMORY:
return (size_t)(bio->wrSz - bio->rdIdx);
case WOLFSSL_BIO_BIO:
if (bio->pair != NULL) {
WOLFSSL_BIO* pair = bio->pair;
if (pair->wrIdx > 0 && pair->wrIdx <= pair->rdIdx) {
/* wrap around state */
return ((size_t)pair->wrSz) - ((size_t)pair->rdIdx) +
((size_t)pair->wrIdx);
}
else {
return (size_t)(pair->wrIdx - pair->rdIdx);
}
}
return 0;
default:
return 0;
}
}
return 0;
@@ -1827,7 +1824,7 @@ long wolfSSL_BIO_get_fp(WOLFSSL_BIO *bio, XFILE* fp)
{
WOLFSSL_ENTER("wolfSSL_BIO_get_fp");
if (bio == NULL || fp == XBADFILE) {
if (bio == NULL || fp == NULL) {
return WOLFSSL_FAILURE;
}
@@ -2094,7 +2091,7 @@ WOLFSSL_BIO_METHOD *wolfSSL_BIO_meth_new(int type, const char *name)
return NULL;
}
XMEMSET(meth, 0, sizeof(WOLFSSL_BIO_METHOD));
meth->type = (byte)type;
meth->type = type;
XSTRNCPY(meth->name, name, MAX_BIO_METHOD_NAME - 1);
return meth;
@@ -2830,39 +2827,17 @@ int wolfSSL_BIO_flush(WOLFSSL_BIO* bio)
}
newLen = XSTRLEN(name);
if (b->ip != NULL && XSTRLEN(b->ip) != newLen) {
XFREE(b->ip, b->heap, DYNAMIC_TYPE_OPENSSL);
b->ip = NULL;
}
if (b->ip == NULL) {
/* +1 for null char */
b->ip = (char*)XMALLOC(newLen + 1, b->heap, DYNAMIC_TYPE_OPENSSL);
if (b->ip == NULL) {
WOLFSSL_MSG("Hostname malloc failed.");
return WOLFSSL_FAILURE;
}
}
else {
size_t currLen = XSTRLEN(b->ip);
#ifdef WOLFSSL_NO_REALLOC
char* tmp = NULL;
#endif
if (currLen != newLen) {
#ifdef WOLFSSL_NO_REALLOC
tmp = b->ip;
b->ip = (char*)XMALLOC(newLen+1, b->heap, DYNAMIC_TYPE_OPENSSL);
if (b->ip != NULL && tmp != NULL) {
XMEMCPY(b->ip, tmp, newLen);
XFREE(tmp, b->heap, DYNAMIC_TYPE_OPENSSL);
tmp = NULL;
}
#else
b->ip = (char*)XREALLOC(b->ip, newLen + 1, b->heap,
DYNAMIC_TYPE_OPENSSL);
#endif
if (b->ip == NULL) {
WOLFSSL_MSG("Hostname realloc failed.");
return WOLFSSL_FAILURE;
}
}
}
XMEMCPY(b->ip, name, newLen);
b->ip[newLen] = '\0';
@@ -2913,7 +2888,7 @@ int wolfSSL_BIO_flush(WOLFSSL_BIO* bio)
DYNAMIC_TYPE_OPENSSL);
if (bio) {
XMEMSET(bio, 0, sizeof(WOLFSSL_BIO));
bio->type = (byte)method->type;
bio->type = method->type;
#if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10100000L
bio->method = (WOLFSSL_BIO_METHOD*)method;
#else
@@ -3183,6 +3158,8 @@ int wolfSSL_BIO_flush(WOLFSSL_BIO* bio)
*/
WOLFSSL_BIO* wolfSSL_BIO_pop(WOLFSSL_BIO* bio)
{
WOLFSSL_BIO* next;
if (bio == NULL) {
WOLFSSL_MSG("Bad argument passed in");
return NULL;
@@ -3196,7 +3173,11 @@ WOLFSSL_BIO* wolfSSL_BIO_pop(WOLFSSL_BIO* bio)
bio->next->prev = bio->prev;
}
return bio->next;
next = bio->next;
bio->prev = NULL;
bio->next = NULL;
return next;
}
+126 -1
View File
@@ -1150,7 +1150,7 @@ int test_wolfSSL_BIO_reset(void)
ExpectIntEQ(BIO_read(bio, buf, 16), 10);
ExpectIntEQ(XMEMCMP(buf, " your data", 10), 0);
/* You cannot write to MEM BIO with read-only mode. */
ExpectIntEQ(BIO_write(bio, "WriteToReadonly", 15), 0);
ExpectIntEQ(BIO_write(bio, "WriteToReadonly", 15), WOLFSSL_BIO_ERROR);
ExpectIntEQ(BIO_read(bio, buf, 16), -1);
XMEMSET(buf, 0, 16);
ExpectIntEQ(BIO_reset(bio), 1);
@@ -1678,5 +1678,130 @@ int test_wolfSSL_BIO_custom_method(void)
return EXPECT_RESULT();
}
int test_wolfSSL_BIO_set_conn_hostname(void)
{
EXPECT_DECLS;
#if defined(OPENSSL_EXTRA)
BIO* bio = NULL;
/* NULL bio should fail */
ExpectIntEQ(BIO_set_conn_hostname(NULL, (char*)"localhost"),
WOLFSSL_FAILURE);
/* Create a bare socket BIO (ip starts as NULL) */
ExpectNotNull(bio = BIO_new(BIO_s_socket()));
/* NULL hostname should fail */
ExpectIntEQ(BIO_set_conn_hostname(bio, NULL), WOLFSSL_FAILURE);
/* Set initial hostname (ip == NULL, triggers malloc) */
ExpectIntEQ(BIO_set_conn_hostname(bio, (char*)"127.0.0.1"),
WOLFSSL_SUCCESS);
/* Set same-length hostname (reuses existing buffer) */
ExpectIntEQ(BIO_set_conn_hostname(bio, (char*)"192.168.0"),
WOLFSSL_SUCCESS);
/* Set shorter hostname (triggers free + malloc) */
ExpectIntEQ(BIO_set_conn_hostname(bio, (char*)"short"), WOLFSSL_SUCCESS);
/* Set longer hostname (triggers free + malloc) */
ExpectIntEQ(BIO_set_conn_hostname(bio, (char*)"a]longer.hostname.example"),
WOLFSSL_SUCCESS);
BIO_free(bio);
#endif
return EXPECT_RESULT();
}
#if defined(OPENSSL_EXTRA)
static long pending_ctrlCb(WOLFSSL_BIO* bio, int cmd, long larg, void* parg)
{
(void)bio;
(void)larg;
(void)parg;
if (cmd == BIO_CTRL_PENDING)
return 42;
return 0;
}
#endif
int test_wolfSSL_BIO_ctrl_pending_chain(void)
{
EXPECT_DECLS;
#if defined(OPENSSL_EXTRA)
BIO* md = NULL;
BIO* b64 = NULL;
BIO* mem = NULL;
char msg[] = "hello";
/* Build chain: md -> b64 -> mem */
ExpectNotNull(md = BIO_new(BIO_f_md()));
ExpectNotNull(b64 = BIO_new(BIO_f_base64()));
ExpectNotNull(mem = BIO_new(BIO_s_mem()));
ExpectNotNull(BIO_push(md, b64));
ExpectNotNull(BIO_push(b64, mem));
/* Write directly to mem so wrSz is set */
ExpectIntEQ(BIO_write(mem, msg, sizeof(msg)), (int)sizeof(msg));
/* ctrl_pending on mem should work */
ExpectIntEQ((int)BIO_ctrl_pending(mem), (int)sizeof(msg));
/* ctrl_pending on the head of the chain (md) should skip both wrappers
* and return mem's pending count */
ExpectIntEQ((int)BIO_ctrl_pending(md), (int)sizeof(msg));
BIO_free(md);
BIO_free(b64);
BIO_free(mem);
/* Test that ctrl_pending reaches a custom ctrlCb through a wrapper.
* Chain: md -> custom(with ctrlCb) -> mem. The custom BIO's ctrlCb
* returns 42 for BIO_CTRL_PENDING, so ctrl_pending on md should
* return 42 (stopping at custom), not fall through to mem. */
{
BIO* md2 = NULL;
BIO* custom = NULL;
BIO* mem2 = NULL;
BIO_METHOD* meth = NULL;
ExpectNotNull(meth = BIO_meth_new(0, "pending_test"));
ExpectIntEQ(BIO_meth_set_ctrl(meth, pending_ctrlCb), WOLFSSL_SUCCESS);
ExpectNotNull(md2 = BIO_new(BIO_f_md()));
ExpectNotNull(custom = BIO_new(meth));
ExpectNotNull(mem2 = BIO_new(BIO_s_mem()));
ExpectNotNull(BIO_push(md2, custom));
ExpectNotNull(BIO_push(custom, mem2));
ExpectIntEQ((int)BIO_ctrl_pending(md2), 42);
BIO_free(md2);
BIO_free(custom);
BIO_free(mem2);
BIO_meth_free(meth);
}
#endif
return EXPECT_RESULT();
}
int test_wolfSSL_BIO_meth_type_large(void)
{
EXPECT_DECLS;
#if defined(OPENSSL_EXTRA)
BIO_METHOD* method = NULL;
BIO* bio = NULL;
/* Type value exceeding 255, as used by applications like HAProxy (0x666) */
ExpectNotNull(method = BIO_meth_new(0x666, "large_type_test"));
ExpectNotNull(bio = BIO_new(method));
ExpectIntEQ(BIO_method_type(bio), 0x666);
BIO_free(bio);
BIO_meth_free(method);
#endif
return EXPECT_RESULT();
}
#endif /* !NO_BIO */
+7 -1
View File
@@ -43,6 +43,9 @@ 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);
int test_wolfSSL_BIO_set_conn_hostname(void);
int test_wolfSSL_BIO_ctrl_pending_chain(void);
int test_wolfSSL_BIO_meth_type_large(void);
#define TEST_OSSL_BIO_DECLS \
TEST_DECL_GROUP("ossl_bio", test_wolfSSL_BIO_gets), \
@@ -58,7 +61,10 @@ int test_wolfSSL_BIO_custom_method(void);
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_custom_method)
TEST_DECL_GROUP("ossl_bio", test_wolfSSL_BIO_custom_method), \
TEST_DECL_GROUP("ossl_bio", test_wolfSSL_BIO_set_conn_hostname), \
TEST_DECL_GROUP("ossl_bio", test_wolfSSL_BIO_ctrl_pending_chain), \
TEST_DECL_GROUP("ossl_bio", test_wolfSSL_BIO_meth_type_large)
#define TEST_OSSL_BIO_TLS_DECLS \
TEST_DECL_GROUP("ossl_bio_tls", test_wolfSSL_BIO_connect), \
+1 -1
View File
@@ -2819,7 +2819,7 @@ struct WOLFSSL_BIO {
} num;
int eof; /* eof flag */
int flags;
byte type; /* method type */
int type; /* method type */
byte init:1; /* bio has been initialized */
byte shutdown:1; /* close flag */
byte connected:1; /* connected state, for datagram BIOs -- as for
+1 -1
View File
@@ -783,7 +783,7 @@ typedef long (*wolfssl_BIO_meth_ctrl_info_cb)(WOLFSSL_BIO*, int, wolfSSL_BIO_inf
#define MAX_BIO_METHOD_NAME 256
#endif
struct WOLFSSL_BIO_METHOD {
byte type; /* method type */
int type; /* method type */
char name[MAX_BIO_METHOD_NAME];
wolfSSL_BIO_meth_write_cb writeCb;
wolfSSL_BIO_meth_read_cb readCb;