Index | Thread | Search

From:
Kirill A. Korinsky <kirill@korins.ky>
Subject:
libcrypto: BIO_read_ex and BIO_write_ex
To:
OpenBSD tech <tech@openbsd.org>
Date:
Wed, 18 Mar 2026 01:48:25 +0100

Download raw body.

Thread
  • Kirill A. Korinsky:

    libcrypto: BIO_read_ex and BIO_write_ex

tech@,

Here a bit naive implementation of BIO_read_ex and BIO_write_ex for
libcrypto.

Why naive? Because I've implemented only wrappers and haven't touched
callbacks which limits the length as INT_MAX.

I need it for ffmpeg-8.1 upgrade, and it was briefly tested with it.

Thoughts? OKs?


Index: Symbols.list
===================================================================
RCS file: /home/cvs/src/lib/libcrypto/Symbols.list,v
diff -u -p -r1.224 Symbols.list
--- Symbols.list	24 Oct 2025 11:33:38 -0000	1.224
+++ Symbols.list	18 Mar 2026 00:21:07 -0000
@@ -305,6 +305,7 @@ BIO_ptr_ctrl
 BIO_push
 BIO_puts
 BIO_read
+BIO_read_ex
 BIO_s_accept
 BIO_s_bio
 BIO_s_connect
@@ -337,6 +338,7 @@ BIO_test_flags
 BIO_up_ref
 BIO_vfree
 BIO_write
+BIO_write_ex
 BN_CTX_end
 BN_CTX_free
 BN_CTX_get
Index: bio/bio.h
===================================================================
RCS file: /home/cvs/src/lib/libcrypto/bio/bio.h,v
diff -u -p -r1.65 bio.h
--- bio/bio.h	16 Jul 2025 18:12:54 -0000	1.65
+++ bio/bio.h	18 Mar 2026 00:21:07 -0000
@@ -541,9 +541,13 @@ void	BIO_set_shutdown(BIO *a, int shut);
 void	BIO_vfree(BIO *a);
 int	BIO_read(BIO *b, void *data, int len)
 		__attribute__((__bounded__(__buffer__,2,3)));
+int	BIO_read_ex(BIO *b, void *data, size_t len, size_t *readbytes)
+		__attribute__((__bounded__(__buffer__,2,3)));
 int	BIO_gets(BIO *bp, char *buf, int size)
 		__attribute__((__bounded__ (__string__,2,3)));
 int	BIO_write(BIO *b, const void *data, int len)
+		__attribute__((__bounded__(__buffer__,2,3)));
+int	BIO_write_ex(BIO *b, const void *data, size_t len, size_t *written)
 		__attribute__((__bounded__(__buffer__,2,3)));
 int	BIO_puts(BIO *bp, const char *buf);
 int	BIO_indent(BIO *b, int indent, int max);
Index: bio/bio_lib.c
===================================================================
RCS file: /home/cvs/src/lib/libcrypto/bio/bio_lib.c,v
diff -u -p -r1.55 bio_lib.c
--- bio/bio_lib.c	10 May 2025 05:54:38 -0000	1.55
+++ bio/bio_lib.c	18 Mar 2026 00:21:07 -0000
@@ -380,6 +380,69 @@ BIO_read(BIO *b, void *out, int outl)
 LCRYPTO_ALIAS(BIO_read);
 
 int
+BIO_read_ex(BIO *b, void *out, size_t outl, size_t *readbytes)
+{
+	size_t callback_len, processed = 0;
+	int len, ret;
+
+	if (readbytes == NULL) {
+		BIOerror(ERR_R_PASSED_NULL_PARAMETER);
+		return (0);
+	}
+	*readbytes = 0;
+
+	if (b == NULL) {
+		BIOerror(ERR_R_PASSED_NULL_PARAMETER);
+		return (0);
+	}
+
+	if (outl == 0)
+		return (0);
+
+	if (out == NULL) {
+		BIOerror(ERR_R_PASSED_NULL_PARAMETER);
+		return (0);
+	}
+
+	if (b->method == NULL || b->method->bread == NULL) {
+		BIOerror(BIO_R_UNSUPPORTED_METHOD);
+		return (0);
+	}
+
+	callback_len = outl;
+	if (b->callback_ex == NULL && callback_len > INT_MAX)
+		callback_len = INT_MAX;
+
+	if (b->callback != NULL || b->callback_ex != NULL) {
+		if ((ret = (int)bio_call_callback(b, BIO_CB_READ, out,
+		    callback_len, 0, 0L, 1L, NULL)) <= 0)
+			return (0);
+	}
+
+	if (!b->init) {
+		BIOerror(BIO_R_UNINITIALIZED);
+		return (0);
+	}
+
+	len = (outl > INT_MAX) ? INT_MAX : (int)outl;
+
+	if ((ret = b->method->bread(b, out, len)) > 0)
+		*readbytes = (size_t)ret;
+
+	b->num_read += *readbytes;
+
+	if (b->callback != NULL || b->callback_ex != NULL) {
+		processed = *readbytes;
+		ret = (int)bio_call_callback(b, BIO_CB_READ | BIO_CB_RETURN,
+		    out, callback_len, 0, 0L, (ret > 0) ? 1 : ret, &processed);
+		*readbytes = processed;
+	}
+
+	return (ret > 0);
+}
+LCRYPTO_ALIAS(BIO_read_ex);
+
+int
 BIO_write(BIO *b, const void *in, int inl)
 {
 	size_t writebytes = 0;
@@ -435,6 +498,64 @@ BIO_write(BIO *b, const void *in, int in
 	return (ret);
 }
 LCRYPTO_ALIAS(BIO_write);
+
+int
+BIO_write_ex(BIO *b, const void *in, size_t inl, size_t *written)
+{
+	size_t callback_len, processed = 0, *writebytes;
+	int len, ret;
+
+	writebytes = (written == NULL) ? &processed : written;
+	*writebytes = 0;
+
+	if (inl == 0)
+		return (1);
+
+	if (b == NULL)
+		return (0);
+
+	if (in == NULL) {
+		BIOerror(ERR_R_PASSED_NULL_PARAMETER);
+		return (0);
+	}
+
+	if (b->method == NULL || b->method->bwrite == NULL) {
+		BIOerror(BIO_R_UNSUPPORTED_METHOD);
+		return (0);
+	}
+
+	callback_len = inl;
+	if (b->callback_ex == NULL && callback_len > INT_MAX)
+		callback_len = INT_MAX;
+
+	if (b->callback != NULL || b->callback_ex != NULL) {
+		if ((ret = (int)bio_call_callback(b, BIO_CB_WRITE, in,
+		    callback_len, 0, 0L, 1L, NULL)) <= 0)
+			return (0);
+	}
+
+	if (!b->init) {
+		BIOerror(BIO_R_UNINITIALIZED);
+		return (0);
+	}
+
+	len = (inl > INT_MAX) ? INT_MAX : (int)inl;
+
+	if ((ret = b->method->bwrite(b, in, len)) > 0)
+		*writebytes = (size_t)ret;
+
+	b->num_write += *writebytes;
+
+	if (b->callback != NULL || b->callback_ex != NULL) {
+		processed = *writebytes;
+		ret = (int)bio_call_callback(b, BIO_CB_WRITE | BIO_CB_RETURN,
+		    in, callback_len, 0, 0L, (ret > 0) ? 1 : ret, &processed);
+		*writebytes = processed;
+	}
+
+	return (ret > 0);
+}
+LCRYPTO_ALIAS(BIO_write_ex);
 
 int
 BIO_puts(BIO *b, const char *in)
Index: hidden/openssl/bio.h
===================================================================
RCS file: /home/cvs/src/lib/libcrypto/hidden/openssl/bio.h,v
diff -u -p -r1.9 bio.h
--- hidden/openssl/bio.h	16 Jul 2025 15:59:26 -0000	1.9
+++ hidden/openssl/bio.h	18 Mar 2026 00:26:48 -0000
@@ -78,8 +78,10 @@ LCRYPTO_USED(BIO_get_shutdown);
 LCRYPTO_USED(BIO_set_shutdown);
 LCRYPTO_USED(BIO_vfree);
 LCRYPTO_USED(BIO_read);
+LCRYPTO_USED(BIO_read_ex);
 LCRYPTO_USED(BIO_gets);
 LCRYPTO_USED(BIO_write);
+LCRYPTO_USED(BIO_write_ex);
 LCRYPTO_USED(BIO_puts);
 LCRYPTO_USED(BIO_indent);
 LCRYPTO_USED(BIO_ctrl);
Index: man/BIO_read.3
===================================================================
RCS file: /home/cvs/src/lib/libcrypto/man/BIO_read.3,v
diff -u -p -r1.12 BIO_read.3
--- man/BIO_read.3	8 Jun 2025 22:40:29 -0000	1.12
+++ man/BIO_read.3	18 Mar 2026 00:22:31 -0000
@@ -69,6 +69,8 @@
 .Dt BIO_READ 3
 .Os
 .Sh NAME
+.Nm BIO_read_ex ,
+.Nm BIO_write_ex ,
 .Nm BIO_read ,
 .Nm BIO_number_read ,
 .Nm BIO_gets ,
@@ -81,6 +83,20 @@
 .Lb libcrypto
 .In openssl/bio.h
 .Ft int
+.Fo BIO_read_ex
+.Fa "BIO *b"
+.Fa "void *buf"
+.Fa "size_t len"
+.Fa "size_t *readbytes"
+.Fc
+.Ft int
+.Fo BIO_write_ex
+.Fa "BIO *b"
+.Fa "const void *buf"
+.Fa "size_t len"
+.Fa "size_t *written"
+.Fc
+.Ft int
 .Fo BIO_read
 .Fa "BIO *b"
 .Fa "void *buf"
@@ -118,6 +134,45 @@
 .Fa "BIO *b"
 .Fc
 .Sh DESCRIPTION
+.Fn BIO_read_ex
+attempts to read up to
+.Fa len
+bytes from
+.Fa b
+and places the data in
+.Fa buf .
+If successful,
+the number of bytes read is stored in
+.Fa *readbytes .
+.Pp
+.Fn BIO_write_ex
+attempts to write up to
+.Fa len
+bytes from
+.Fa buf
+to
+.Fa b .
+If successful and
+.Fa written
+is not
+.Dv NULL ,
+the number of bytes written is stored in
+.Fa *written .
+.Pp
+Because the underlying BIO method interfaces take
+.Vt int
+length arguments,
+a single call to
+.Fn BIO_read_ex
+or
+.Fn BIO_write_ex
+processes at most
+.Dv INT_MAX
+bytes,
+even if
+.Fa len
+is larger.
+.Pp
 .Fn BIO_read
 attempts to read
 .Fa len
@@ -131,6 +186,8 @@ returns the grand total of bytes read fr
 .Fa b
 using
 .Fn BIO_read
+and
+.Fn BIO_read_ex
 so far.
 Bytes read with
 .Fn BIO_gets
@@ -188,6 +245,7 @@ returns the grand total of bytes written
 .Fa b
 using
 .Fn BIO_write ,
+.Fn BIO_write_ex ,
 .Fn BIO_puts ,
 and
 .Fn BIO_indent
@@ -238,6 +296,13 @@ to the chain.
 returns 1 if successful, even if nothing was written,
 or 0 if writing fails.
 .Pp
+.Fn BIO_read_ex
+returns 1 if data was successfully read and 0 otherwise.
+.Pp
+.Fn BIO_write_ex
+returns 1 if no error was encountered writing data and 0 otherwise.
+Requesting to write 0 bytes is not considered an error.
+.Pp
 .Fn BIO_number_read
 and
 .Fn BIO_number_written
@@ -264,6 +329,12 @@ the application should retry the operati
 .Xr BIO_new 3 ,
 .Xr BIO_should_retry 3
 .Sh HISTORY
+.Fn BIO_read_ex
+and
+.Fn BIO_write_ex
+first appeared in
+.Ox 7.9 .
+.Pp
 .Fn BIO_read ,
 .Fn BIO_gets ,
 .Fn BIO_write ,



-- 
wbr, Kirill