Download raw body.
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
libcrypto: BIO_read_ex and BIO_write_ex