From 40404303b3a74f6be1cf2a373487d1e85b156960 Mon Sep 17 00:00:00 2001 From: Sharon Rosner Date: Fri, 23 Jan 2026 08:26:24 +0100 Subject: [PATCH 1/8] Add support for custom BIO method This adds support for using a custom BIO method for performing SSL/TLS I/O through a Ruby IO instance (normally the underlying socket). Alternatively, a pair of read and write procs may be used for performing the I/O. --- ext/openssl/extconf.rb | 2 + ext/openssl/ossl_ssl.c | 95 +++++++++++++++- ext/openssl/ossl_ssl_custom_bio.c | 144 +++++++++++++++++++++++++ ext/openssl/ossl_ssl_custom_bio.h | 17 +++ test/openssl/test_ssl.rb | 174 ++++++++++++++++++++++++++++++ test/openssl/utils.rb | 6 ++ 6 files changed, 437 insertions(+), 1 deletion(-) create mode 100644 ext/openssl/ossl_ssl_custom_bio.c create mode 100644 ext/openssl/ossl_ssl_custom_bio.h diff --git a/ext/openssl/extconf.rb b/ext/openssl/extconf.rb index a897c86b6..b030a2beb 100644 --- a/ext/openssl/extconf.rb +++ b/ext/openssl/extconf.rb @@ -146,6 +146,8 @@ def find_openssl_library # added in 1.1.0, currently not in LibreSSL have_func("EVP_PBE_scrypt(\"\", 0, (unsigned char *)\"\", 0, 0, 0, 0, 0, NULL, 0)", evp_h) +have_func("BIO_meth_new"); +have_func("SSL_set0_rbio"); # added in OpenSSL 1.1.1 and LibreSSL 3.5.0, then removed in LibreSSL 4.0.0 have_func("EVP_PKEY_check(NULL)", evp_h) diff --git a/ext/openssl/ossl_ssl.c b/ext/openssl/ossl_ssl.c index 630d46e43..6faff8935 100644 --- a/ext/openssl/ossl_ssl.c +++ b/ext/openssl/ossl_ssl.c @@ -10,6 +10,7 @@ * (See the file 'COPYING'.) */ #include "ossl.h" +#include "ossl_ssl_custom_bio.h" #ifndef OPENSSL_NO_SOCK #define numberof(ary) (int)(sizeof(ary)/sizeof((ary)[0])) @@ -29,7 +30,7 @@ } while (0) VALUE mSSL; -static VALUE eSSLError; +VALUE eSSLError; static VALUE cSSLContext; VALUE cSSLSocket; @@ -48,6 +49,7 @@ static ID id_i_cert_store, id_i_ca_file, id_i_ca_path, id_i_verify_mode, id_i_alpn_select_cb, id_i_alpn_protocols, id_i_servername_cb, id_i_verify_hostname, id_i_keylog_cb, id_i_tmp_dh_callback; static ID id_i_io, id_i_context, id_i_hostname; +static ID id_i_bio_method; static int ossl_ssl_ex_ptr_idx; static int ossl_sslctx_ex_ptr_idx; @@ -2700,6 +2702,92 @@ ossl_ssl_get_group(VALUE self) #endif /* !defined(OPENSSL_NO_SOCK) */ +#ifdef HAVE_BIO_METH_NEW +/* + * call-seq: + * ssl.bio_method => method or nil + * + * Returns the BIO method for the socket, or nil if not set. See also + * SSLSocket#bio_method=. + */ +static VALUE +ossl_ssl_get_bio_method(VALUE self) +{ + return rb_ivar_get(self, id_i_bio_method); +} + +/* + * call-seq: + * ssl.bio_method = nil + * ssl.bio_method = io + * ssl.bio_method = [->(buf, maxlen) { ... }, ->(buf, len) { ... }] + * + * Sets the BIO method for the SSL socket. By default, the SSL connection uses a + * socket BIO for performing I/O, which means that OpenSSL will bypass the + * I/O implementation in the standard library, only using it for checking for + * I/O readiness. + * + * When the BIO method is set to an IO instance (normally the underlying socket + * instance), OpenSSL will read and write to the connection by calling the + * `#read` and `#write` methods on the given IO instance. This also allows + * better integration with a fiber scheduler for applications that use + * fiber-based concurrency. + * + * Alternatively, the BIO method may be customized by setting it to an array + * containing a read proc and a write proc. The read proc takes as parameters an + * IO::Buffer and the maximum number of bytes to read. The proc should return + * the number of bytes read. The write proc takes as parameters an IO::Buffer + * and the number of bytes to write. It should return the number of bytes + * written. Example usage: + * + * io = ssl.to_io + * ssl.bio_method = [ + * ->(buf, maxlen) { + * str = io.read(maxlen) + * len = str.bytesize + * buf.set_string(str) + * len + * }, + * ->(buf, len) { + * str = buf.get_string(0, len) + * io.write(str) + * } + * ] + */ +static VALUE +ossl_ssl_set_bio_method(VALUE self, VALUE method) +{ + SSL *ssl; + GetSSL(self, ssl); + + switch(TYPE(method)) { + case T_FILE: + case T_OBJECT: + case T_STRUCT: + break; + case T_ARRAY: + if (RARRAY_LEN(method) != 2) + rb_raise(eSSLError, "Invalid BIO method"); + break; + default: + rb_raise(eSSLError, "Invalid BIO method"); + } + + rb_ivar_set(self, id_i_bio_method, method); + + if (NIL_P(method)) { + VALUE io = rb_ivar_get(self, id_i_io); + if (!SSL_set_fd(ssl, TO_SOCKET(rb_io_descriptor(io)))) + ossl_raise(eSSLError, "SSL_set_fd"); + } + else { + ossl_ssl_set_custom_bio(ssl, method); + } + + return self; +} +#endif + void Init_ossl_ssl(void) { @@ -3133,6 +3221,10 @@ Init_ossl_ssl(void) rb_define_method(cSSLSocket, "tmp_key", ossl_ssl_tmp_key, 0); rb_define_method(cSSLSocket, "alpn_protocol", ossl_ssl_alpn_protocol, 0); rb_define_method(cSSLSocket, "export_keying_material", ossl_ssl_export_keying_material, -1); +#ifdef HAVE_BIO_METH_NEW + rb_define_method(cSSLSocket, "bio_method", ossl_ssl_get_bio_method, 0); + rb_define_method(cSSLSocket, "bio_method=", ossl_ssl_set_bio_method, 1); +#endif # ifdef OSSL_USE_NEXTPROTONEG rb_define_method(cSSLSocket, "npn_protocol", ossl_ssl_npn_protocol, 0); # endif @@ -3300,5 +3392,6 @@ Init_ossl_ssl(void) DefIVarID(io); DefIVarID(context); DefIVarID(hostname); + DefIVarID(bio_method); #endif /* !defined(OPENSSL_NO_SOCK) */ } diff --git a/ext/openssl/ossl_ssl_custom_bio.c b/ext/openssl/ossl_ssl_custom_bio.c new file mode 100644 index 000000000..00517f40f --- /dev/null +++ b/ext/openssl/ossl_ssl_custom_bio.c @@ -0,0 +1,144 @@ +/* + * 'OpenSSL for Ruby' project + * Copyright (C) 2026 Sharon Rosner + * All rights reserved. + */ +/* + * This program is licensed under the same licence as Ruby. + * (See the file 'COPYING'.) +*/ +#include "ossl.h" + +#ifdef HAVE_BIO_METH_NEW + +#include "ossl_ssl_custom_bio.h" +#include "ruby/io/buffer.h" + +extern VALUE eSSLError; +static ID id_read, id_write, id_call, id_eof_p; + +static int +ossl_ssl_custom_bio_in_read(BIO *bio, char *buf, int blen) +{ + VALUE target = (VALUE)BIO_get_data(bio); + + switch(TYPE(target)) { + case T_FILE: + case T_OBJECT: + case T_STRUCT: { + VALUE str = rb_funcall(target, id_read, 1, INT2NUM(blen)); + int slen = RSTRING_LEN(str); + memcpy(buf, RSTRING_PTR(str), slen); + RB_GC_GUARD(str); + return slen; + } + case T_ARRAY: { + VALUE read_proc = rb_ary_entry(target, 0); + VALUE buffer = rb_io_buffer_new(buf, blen, RB_IO_BUFFER_LOCKED); + VALUE len = rb_funcall(read_proc, id_call, 2, buffer, INT2NUM(blen)); + rb_io_buffer_free_locked(buffer); + return NUM2INT(len); + } + default: + rb_raise(eSSLError, "Invalid BIO target"); + } +} + +static int +ossl_ssl_custom_bio_out_write(BIO *bio, const char *buf, int blen) +{ + VALUE target = (VALUE)BIO_get_data(bio); + switch(TYPE(target)) { + case T_FILE: + case T_OBJECT: + case T_STRUCT: { + VALUE str = rb_str_new(buf, blen); + VALUE res = rb_funcall(target, id_write, 1, str); + RB_GC_GUARD(str); + return NUM2SIZET(res); + } + case T_ARRAY: { + VALUE write_proc = rb_ary_entry(target, 1); + VALUE buffer = rb_io_buffer_new((char *)buf, blen, RB_IO_BUFFER_LOCKED | RB_IO_BUFFER_READONLY); + VALUE len = rb_funcall(write_proc, id_call, 2, buffer, INT2NUM(blen)); + RB_GC_GUARD(buffer); + rb_io_buffer_free_locked(buffer); + return NUM2INT(len); + } + default: + rb_raise(eSSLError, "Invalid BIO target"); + } +} + +static long +ossl_ssl_custom_bio_ctrl(BIO *bio, int cmd, long num, void *ptr) +{ + VALUE target = (VALUE)BIO_get_data(bio); + + switch(cmd) { + case BIO_CTRL_GET_CLOSE: + return (long)BIO_get_shutdown(bio); + case BIO_CTRL_SET_CLOSE: + BIO_set_shutdown(bio, (int)num); + return 1; + case BIO_CTRL_FLUSH: + // we don't buffer writes, so noop + return 1; + case BIO_CTRL_EOF: { + switch(TYPE(target)) { + case T_FILE: + case T_OBJECT: + case T_STRUCT: { + VALUE eof = rb_funcall(target, id_eof_p, 0); + return RTEST(eof); + } + default: + return 0; + } + } + default: + return 0; + } +} + +BIO_METHOD * +ossl_ssl_create_custom_bio_method() +{ + BIO_METHOD *m = BIO_meth_new(BIO_TYPE_MEM, "OpenSSL Ruby BIO"); + if(m) { + BIO_meth_set_write(m, &ossl_ssl_custom_bio_out_write); + BIO_meth_set_read(m, &ossl_ssl_custom_bio_in_read); + BIO_meth_set_ctrl(m, &ossl_ssl_custom_bio_ctrl); + } + return m; +} + + +static BIO_METHOD *custom_bio_method = NULL; + +void +ossl_ssl_set_custom_bio(SSL *ssl, VALUE target) +{ + if (!custom_bio_method) { + custom_bio_method = ossl_ssl_create_custom_bio_method(); + id_read = rb_intern_const("read"); + id_write = rb_intern_const("write"); + id_call = rb_intern_const("call"); + id_eof_p = rb_intern_const("eof?"); + } + + BIO *bio = BIO_new(custom_bio_method); + if(!bio) + rb_raise(eSSLError, "Failed to create custom BIO"); + + BIO_set_data(bio, (void *)target); +#ifdef HAVE_SSL_SET0_RBIO + BIO_up_ref(bio); + SSL_set0_rbio(ssl, bio); + SSL_set0_wbio(ssl, bio); +#else + SSL_set_bio(ssl, bio, bio); +#endif +} + +#endif diff --git a/ext/openssl/ossl_ssl_custom_bio.h b/ext/openssl/ossl_ssl_custom_bio.h new file mode 100644 index 000000000..3f24fd6b7 --- /dev/null +++ b/ext/openssl/ossl_ssl_custom_bio.h @@ -0,0 +1,17 @@ +/* + * 'OpenSSL for Ruby' project + * Copyright (C) 2026 Sharon Rosner + * All rights reserved. + */ +/* + * This program is licensed under the same licence as Ruby. + * (See the file 'COPYING'.) +*/ +#if !defined(_OSSL_SSL_CUSTOM_BIO_H_) +#define _OSSL_SSL_CUSTOM_BIO_H_ + +#include "ossl.h" + +void ossl_ssl_set_custom_bio(SSL *ssl, VALUE target); + +#endif /* _OSSL_SSL_CUSTOM_BIO_H_ */ diff --git a/test/openssl/test_ssl.rb b/test/openssl/test_ssl.rb index 5d20ccd1f..db2ddf5ae 100644 --- a/test/openssl/test_ssl.rb +++ b/test/openssl/test_ssl.rb @@ -2313,6 +2313,180 @@ def test_fileno sock2.close end + def test_bio_method_io + omit_on_no_bio_method + + start_server { |port| + begin + ssl = OpenSSL::SSL::SSLSocket.open("127.0.0.1", port) + ssl.sync_close = true + ssl.bio_method = ssl.to_io + assert_equal ssl.to_io, ssl.bio_method + ssl.connect + + ssl.puts "abc"; assert_equal "abc\n", ssl.gets + ensure + ssl&.close + end + } + end + + def test_bio_method_io_like + omit_on_no_bio_method + + custom_class = Struct.new(:io, :ops) do + def read(len) + (self.ops ||= []) << :read + self.io.read(len) + end + + def write(buf) + (self.ops ||= []) << :write + self.io.write(buf) + end + end + + start_server { |port| + begin + ssl = OpenSSL::SSL::SSLSocket.open("127.0.0.1", port) + ssl.sync_close = true + custom = custom_class.new(io: ssl.to_io) + ssl.bio_method = custom + assert_equal custom, ssl.bio_method + ssl.connect + + ssl.puts "abc"; assert_equal "abc\n", ssl.gets + refute_empty custom.ops + ensure + ssl&.close + end + } + end + + class MyIOError < StandardError; end + + def test_bio_method_io_like_exception + omit_on_no_bio_method + + custom_class = Struct.new(:io, :ops) do + def read(len) + (self.ops ||= []) << :read + self.io.read(len) + end + + def write(buf) + (self.ops ||= []) << :write + raise MyIOError + end + end + + start_server(ignore_listener_error: true) { |port| + begin + ssl = OpenSSL::SSL::SSLSocket.open("127.0.0.1", port) + ssl.sync_close = true + custom = custom_class.new(io: ssl.to_io) + ssl.bio_method = custom + assert_equal custom, ssl.bio_method + + assert_raise(MyIOError) { ssl.connect } + assert_equal [:write], custom.ops + ensure + ssl&.close rescue nil + end + } + end + + def test_bio_method_custom + omit_on_no_bio_method + + start_server { |port| + begin + ops = [] + ssl = OpenSSL::SSL::SSLSocket.open("127.0.0.1", port) + ssl.sync_close = true + + io = ssl.to_io + read_proc = ->(buf, maxlen) { + ops << :read + str = io.read(maxlen) + len = str.bytesize + buf.set_string(str) + len + } + write_proc = ->(buf, len) { + ops << :write + str = buf.get_string(0, len) + len = io.write(str) + len + } + + ssl.bio_method = [read_proc, write_proc] + ssl.connect + + ssl.puts "abc"; assert_equal "abc\n", ssl.gets + refute_empty ops + ensure + ssl&.close + end + } + end + + def test_bio_method_custom_exception + omit_on_no_bio_method + + start_server(ignore_listener_error: true) { |port| + begin + ops = [] + ssl = OpenSSL::SSL::SSLSocket.open("127.0.0.1", port) + ssl.sync_close = true + + io = ssl.to_io + read_proc = ->(buf, maxlen) { + ops << :read + str = io.read(maxlen) + len = str.bytesize + buf.set_string(str) + len + } + write_proc = ->(buf, len) { + ops << :write + raise MyIOError + } + + ssl.bio_method = [read_proc, write_proc] + + assert_raise(MyIOError) { ssl.connect } + assert_equal [:write], ops + ensure + ssl&.close rescue nil + end + } + end + def test_bio_method_invalid + omit_on_no_bio_method + + start_server { |port| + begin + ssl = OpenSSL::SSL::SSLSocket.open("127.0.0.1", port) + ssl.sync_close = true + + assert_raise(OpenSSL::SSL::SSLError) { ssl.bio_method = 42 } + assert_raise(OpenSSL::SSL::SSLError) { ssl.bio_method = "foo" } + assert_raise(OpenSSL::SSL::SSLError) { ssl.bio_method = :foo } + assert_raise(OpenSSL::SSL::SSLError) { ssl.bio_method = [] } + assert_raise(OpenSSL::SSL::SSLError) { ssl.bio_method = [:foo] } + + assert_nil ssl.bio_method + + ssl.connect + + ssl.puts "abc"; assert_equal "abc\n", ssl.gets + ensure + ssl&.close rescue nil + end + } + end + def test_export_keying_material start_server do |port| cli_ctx = OpenSSL::SSL::SSLContext.new diff --git a/test/openssl/utils.rb b/test/openssl/utils.rb index 7e6fe8b16..a88e7ac63 100644 --- a/test/openssl/utils.rb +++ b/test/openssl/utils.rb @@ -163,6 +163,12 @@ def omit_on_non_fips omit "Only for OpenSSL FIPS" end + + def omit_on_no_bio_method + return if OpenSSL::SSL::SSLSocket.instance_methods.include?(:bio_method) + + omit "No support for setting BIO method" + end end class OpenSSL::SSLTestCase < OpenSSL::TestCase From 993afe0c31a4e2361ed27796115ba80133a0cbbb Mon Sep 17 00:00:00 2001 From: Sharon Rosner Date: Fri, 23 Jan 2026 10:38:05 +0100 Subject: [PATCH 2/8] Check if rb_io_buffer_new is available --- ext/openssl/extconf.rb | 2 ++ ext/openssl/ossl.h | 4 ++++ ext/openssl/ossl_ssl.c | 4 ++-- ext/openssl/ossl_ssl_custom_bio.c | 2 +- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/ext/openssl/extconf.rb b/ext/openssl/extconf.rb index b030a2beb..f5ae62e0d 100644 --- a/ext/openssl/extconf.rb +++ b/ext/openssl/extconf.rb @@ -171,6 +171,8 @@ def find_openssl_library # added in 3.5.0 have_func("SSL_get0_peer_signature_name(NULL, NULL)", ssl_h) +have_func("rb_io_buffer_new") + Logging::message "=== Checking done. ===\n" # Append flags from environment variables. diff --git a/ext/openssl/ossl.h b/ext/openssl/ossl.h index 0b479a720..ad2b6913d 100644 --- a/ext/openssl/ossl.h +++ b/ext/openssl/ossl.h @@ -78,6 +78,10 @@ # define OSSL_HAVE_IMMUTABLE_PKEY #endif +#if HAVE_BIO_METH_NEW && HAVE_RB_IO_BUFFER_NEW +# define OSSL_CUSTOM_BIO +#endif + /* * Common Module */ diff --git a/ext/openssl/ossl_ssl.c b/ext/openssl/ossl_ssl.c index 6faff8935..5bd7e1582 100644 --- a/ext/openssl/ossl_ssl.c +++ b/ext/openssl/ossl_ssl.c @@ -2702,7 +2702,7 @@ ossl_ssl_get_group(VALUE self) #endif /* !defined(OPENSSL_NO_SOCK) */ -#ifdef HAVE_BIO_METH_NEW +#ifdef OSSL_CUSTOM_BIO /* * call-seq: * ssl.bio_method => method or nil @@ -3221,7 +3221,7 @@ Init_ossl_ssl(void) rb_define_method(cSSLSocket, "tmp_key", ossl_ssl_tmp_key, 0); rb_define_method(cSSLSocket, "alpn_protocol", ossl_ssl_alpn_protocol, 0); rb_define_method(cSSLSocket, "export_keying_material", ossl_ssl_export_keying_material, -1); -#ifdef HAVE_BIO_METH_NEW +#ifdef OSSL_CUSTOM_BIO rb_define_method(cSSLSocket, "bio_method", ossl_ssl_get_bio_method, 0); rb_define_method(cSSLSocket, "bio_method=", ossl_ssl_set_bio_method, 1); #endif diff --git a/ext/openssl/ossl_ssl_custom_bio.c b/ext/openssl/ossl_ssl_custom_bio.c index 00517f40f..2469373b1 100644 --- a/ext/openssl/ossl_ssl_custom_bio.c +++ b/ext/openssl/ossl_ssl_custom_bio.c @@ -9,7 +9,7 @@ */ #include "ossl.h" -#ifdef HAVE_BIO_METH_NEW +#ifdef OSSL_CUSTOM_BIO #include "ossl_ssl_custom_bio.h" #include "ruby/io/buffer.h" From a248321ad8c401a29232a464751e1b8c9e54edf4 Mon Sep 17 00:00:00 2001 From: Sharon Rosner Date: Fri, 23 Jan 2026 11:03:00 +0100 Subject: [PATCH 3/8] Fix old-style function definition compile error --- ext/openssl/ossl_ssl_custom_bio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/openssl/ossl_ssl_custom_bio.c b/ext/openssl/ossl_ssl_custom_bio.c index 2469373b1..e4a9548b6 100644 --- a/ext/openssl/ossl_ssl_custom_bio.c +++ b/ext/openssl/ossl_ssl_custom_bio.c @@ -102,7 +102,7 @@ ossl_ssl_custom_bio_ctrl(BIO *bio, int cmd, long num, void *ptr) } BIO_METHOD * -ossl_ssl_create_custom_bio_method() +ossl_ssl_create_custom_bio_method(void) { BIO_METHOD *m = BIO_meth_new(BIO_TYPE_MEM, "OpenSSL Ruby BIO"); if(m) { From de02944fac5d08607c6470e0aab6b329ae107ce6 Mon Sep 17 00:00:00 2001 From: Sharon Rosner Date: Fri, 23 Jan 2026 11:08:29 +0100 Subject: [PATCH 4/8] Check also for availability of rb_io_buffer_free_locked --- ext/openssl/extconf.rb | 1 + ext/openssl/ossl.h | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/ext/openssl/extconf.rb b/ext/openssl/extconf.rb index f5ae62e0d..134169a6f 100644 --- a/ext/openssl/extconf.rb +++ b/ext/openssl/extconf.rb @@ -172,6 +172,7 @@ def find_openssl_library have_func("SSL_get0_peer_signature_name(NULL, NULL)", ssl_h) have_func("rb_io_buffer_new") +have_func("rb_io_buffer_free_locked") Logging::message "=== Checking done. ===\n" diff --git a/ext/openssl/ossl.h b/ext/openssl/ossl.h index ad2b6913d..cc185d545 100644 --- a/ext/openssl/ossl.h +++ b/ext/openssl/ossl.h @@ -78,7 +78,7 @@ # define OSSL_HAVE_IMMUTABLE_PKEY #endif -#if HAVE_BIO_METH_NEW && HAVE_RB_IO_BUFFER_NEW +#if HAVE_BIO_METH_NEW && HAVE_RB_IO_BUFFER_NEW && HAVE_RB_IO_BUFFER_FREE_LOCKED # define OSSL_CUSTOM_BIO #endif From 38a2f38353d046bfffd57d30878911dadb2a30c4 Mon Sep 17 00:00:00 2001 From: Sharon Rosner Date: Fri, 23 Jan 2026 11:10:40 +0100 Subject: [PATCH 5/8] Fix define check --- ext/openssl/ossl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/openssl/ossl.h b/ext/openssl/ossl.h index cc185d545..61845750e 100644 --- a/ext/openssl/ossl.h +++ b/ext/openssl/ossl.h @@ -78,7 +78,7 @@ # define OSSL_HAVE_IMMUTABLE_PKEY #endif -#if HAVE_BIO_METH_NEW && HAVE_RB_IO_BUFFER_NEW && HAVE_RB_IO_BUFFER_FREE_LOCKED +#if defined(HAVE_BIO_METH_NEW) && defined(HAVE_RB_IO_BUFFER_NEW) && defined(HAVE_RB_IO_BUFFER_FREE_LOCKED) # define OSSL_CUSTOM_BIO #endif From 6d4c32cf165afa142b2eea4e58efea899eded930 Mon Sep 17 00:00:00 2001 From: Sharon Rosner Date: Fri, 23 Jan 2026 11:12:10 +0100 Subject: [PATCH 6/8] Use long for RSTRING_LEN --- ext/openssl/ossl_ssl_custom_bio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/openssl/ossl_ssl_custom_bio.c b/ext/openssl/ossl_ssl_custom_bio.c index e4a9548b6..58e6bf386 100644 --- a/ext/openssl/ossl_ssl_custom_bio.c +++ b/ext/openssl/ossl_ssl_custom_bio.c @@ -27,7 +27,7 @@ ossl_ssl_custom_bio_in_read(BIO *bio, char *buf, int blen) case T_OBJECT: case T_STRUCT: { VALUE str = rb_funcall(target, id_read, 1, INT2NUM(blen)); - int slen = RSTRING_LEN(str); + long slen = RSTRING_LEN(str); memcpy(buf, RSTRING_PTR(str), slen); RB_GC_GUARD(str); return slen; From adb3571afa34267294e5719877af6333efed42fc Mon Sep 17 00:00:00 2001 From: Sharon Rosner Date: Fri, 23 Jan 2026 11:13:55 +0100 Subject: [PATCH 7/8] Fix slen return --- ext/openssl/ossl_ssl_custom_bio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/openssl/ossl_ssl_custom_bio.c b/ext/openssl/ossl_ssl_custom_bio.c index 58e6bf386..bb5e14e9a 100644 --- a/ext/openssl/ossl_ssl_custom_bio.c +++ b/ext/openssl/ossl_ssl_custom_bio.c @@ -30,7 +30,7 @@ ossl_ssl_custom_bio_in_read(BIO *bio, char *buf, int blen) long slen = RSTRING_LEN(str); memcpy(buf, RSTRING_PTR(str), slen); RB_GC_GUARD(str); - return slen; + return (int)slen; } case T_ARRAY: { VALUE read_proc = rb_ary_entry(target, 0); From ecdeff8db6f7d7ece188680bdff0615573dafb24 Mon Sep 17 00:00:00 2001 From: Sharon Rosner Date: Fri, 23 Jan 2026 11:21:19 +0100 Subject: [PATCH 8/8] Fix return values in read/write hooks --- ext/openssl/ossl_ssl_custom_bio.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ext/openssl/ossl_ssl_custom_bio.c b/ext/openssl/ossl_ssl_custom_bio.c index bb5e14e9a..9e3dd9e52 100644 --- a/ext/openssl/ossl_ssl_custom_bio.c +++ b/ext/openssl/ossl_ssl_custom_bio.c @@ -35,9 +35,9 @@ ossl_ssl_custom_bio_in_read(BIO *bio, char *buf, int blen) case T_ARRAY: { VALUE read_proc = rb_ary_entry(target, 0); VALUE buffer = rb_io_buffer_new(buf, blen, RB_IO_BUFFER_LOCKED); - VALUE len = rb_funcall(read_proc, id_call, 2, buffer, INT2NUM(blen)); + VALUE res = rb_funcall(read_proc, id_call, 2, buffer, INT2NUM(blen)); rb_io_buffer_free_locked(buffer); - return NUM2INT(len); + return NUM2INT(res); } default: rb_raise(eSSLError, "Invalid BIO target"); @@ -55,15 +55,15 @@ ossl_ssl_custom_bio_out_write(BIO *bio, const char *buf, int blen) VALUE str = rb_str_new(buf, blen); VALUE res = rb_funcall(target, id_write, 1, str); RB_GC_GUARD(str); - return NUM2SIZET(res); + return NUM2INT(res); } case T_ARRAY: { VALUE write_proc = rb_ary_entry(target, 1); VALUE buffer = rb_io_buffer_new((char *)buf, blen, RB_IO_BUFFER_LOCKED | RB_IO_BUFFER_READONLY); - VALUE len = rb_funcall(write_proc, id_call, 2, buffer, INT2NUM(blen)); + VALUE res = rb_funcall(write_proc, id_call, 2, buffer, INT2NUM(blen)); RB_GC_GUARD(buffer); rb_io_buffer_free_locked(buffer); - return NUM2INT(len); + return NUM2INT(res); } default: rb_raise(eSSLError, "Invalid BIO target");