From 3082e00b6188110428c2292c16fac05073faa304 Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Wed, 24 Jun 2026 20:03:57 +0900 Subject: [PATCH 1/5] [ruby/openssl] ssl: let SSLServer accept frozen SSLContext Do not raise FrozenError in SSLServer.new when SSLContext#session_id_context cannot be updated. session_id_context is only necessary for session resumption, so its absence is not critical. Fixes https://github.com/ruby/openssl/pull/742 https://github.com/ruby/openssl/commit/51de9c303e --- ext/openssl/lib/openssl/ssl.rb | 2 +- test/openssl/test_ssl_server.rb | 67 +++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 test/openssl/test_ssl_server.rb diff --git a/ext/openssl/lib/openssl/ssl.rb b/ext/openssl/lib/openssl/ssl.rb index 3268c126b9efad..dccc11a5567f04 100644 --- a/ext/openssl/lib/openssl/ssl.rb +++ b/ext/openssl/lib/openssl/ssl.rb @@ -486,7 +486,7 @@ class SSLServer def initialize(svr, ctx) @svr = svr @ctx = ctx - unless ctx.session_id_context + if !ctx.frozen? && !ctx.session_id_context # see #6137 - session id may not exceed 32 bytes prng = ::Random.new($0.hash) session_id = prng.bytes(16).unpack1('H*') diff --git a/test/openssl/test_ssl_server.rb b/test/openssl/test_ssl_server.rb new file mode 100644 index 00000000000000..076b589658cb85 --- /dev/null +++ b/test/openssl/test_ssl_server.rb @@ -0,0 +1,67 @@ +# frozen_string_literal: true +require_relative "utils" + +return unless defined?(OpenSSL::SSL) + +class OpenSSL::TestSSLServer < OpenSSL::SSLTestCase + def test_tcpserver + tcps = TCPServer.new("127.0.0.1", 0) + sctx = OpenSSL::SSL::SSLContext.new + sctx.add_certificate(@svr_cert, @svr_key) + server = OpenSSL::SSL::SSLServer.new(tcps, sctx) + assert_same(tcps, server.to_io) + assert_kind_of(String, sctx.session_id_context) + th = Thread.start do + sssl = server.accept + sssl.puts(sssl.gets) + ensure + sssl&.close + end + server_connect(tcps.local_address.ip_port) do |ssl| + assert_equal(@svr_cert.to_der, ssl.peer_cert.to_der) + ssl.puts("abc") + assert_equal("abc\n", ssl.gets) + end + th.join + server.close + assert_predicate(tcps, :closed?) + end + + def test_ctx_frozen + tcps = TCPServer.new("127.0.0.1", 0) + sctx = OpenSSL::SSL::SSLContext.new + sctx.add_certificate(@svr_cert, @svr_key) + sctx.setup + server = OpenSSL::SSL::SSLServer.new(tcps, sctx) + assert_nil(sctx.session_id_context) + th = Thread.start do + sssl = server.accept + sssl.puts(sssl.gets) + ensure + sssl&.close + end + server_connect(tcps.local_address.ip_port) do |ssl| + assert_equal(@svr_cert.to_der, ssl.peer_cert.to_der) + ssl.puts("abc") + assert_equal("abc\n", ssl.gets) + end + th.join + server.close + end + + private + + def server_connect(port, ctx = nil) + sock = TCPSocket.new("127.0.0.1", port) + ssl = ctx ? OpenSSL::SSL::SSLSocket.new(sock, ctx) : OpenSSL::SSL::SSLSocket.new(sock) + ssl.sync_close = true + ssl.connect + yield ssl if block_given? + ensure + if ssl + ssl.close + elsif sock + sock.close + end + end +end From b914f8305443456f367641aca5172beb7e2a3678 Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Sun, 21 Jun 2026 18:56:02 +0900 Subject: [PATCH 2/5] [ruby/openssl] x509store: add X509::Store{,Context}#flags and #clear_flags OpenSSL::X509::Store{,Context}#flags= does not clear existing flags. Instead, it ORs new flags to the current set. This is contrary to normal convention and likely unintentional, but changing the behavior would not be acceptable for compatibility reasons. Add #flags to get the current flags to allow users to explicitly express the intention with "store.flags |= OpenSSL::X509::V_...". Also, add #clear_flags to remove existing flags. The underlying OpenSSL C APIs appear to have been added in a patch release of OpenSSL 0.9.8. Warn in #flags= if the argument is not a superset of the existing flags. https://github.com/ruby/openssl/commit/a245620e2e --- ext/openssl/ossl_x509store.c | 129 +++++++++++++++++++++++++++++++-- test/openssl/test_x509store.rb | 18 +++++ 2 files changed, 139 insertions(+), 8 deletions(-) diff --git a/ext/openssl/ossl_x509store.c b/ext/openssl/ossl_x509store.c index 9e43336c4406f2..bed124dc3ed454 100644 --- a/ext/openssl/ossl_x509store.c +++ b/ext/openssl/ossl_x509store.c @@ -224,6 +224,41 @@ ossl_x509store_initialize(int argc, VALUE *argv, VALUE self) return self; } +/* + * call-seq: + * store.flags -> Integer + * + * Gets the verification flags for the Store. + * + * See also the man page X509_VERIFY_PARAM_get_flags(3). + */ +static VALUE +ossl_x509store_get_flags(VALUE self) +{ + X509_STORE *store; + X509_VERIFY_PARAM *vpm; + + GetX509Store(self, store); + vpm = X509_STORE_get0_param(store); + return ULONG2NUM(X509_VERIFY_PARAM_get_flags(vpm)); +} + +static void +x509vpm_set_flags_i(X509_VERIFY_PARAM *vpm, VALUE flags) +{ + unsigned long curr = X509_VERIFY_PARAM_get_flags(vpm); + unsigned long f = NUM2ULONG(flags); + + if ((curr | f) != f) { + rb_warn("`obj.flags = new_flags` does not clear existing flags; " \ + "use `obj.clear_flags` first if you want to replace them, " \ + "or `obj.flags |= new_flags` to indicate that " \ + "appending flags is intentional"); + } + if (!X509_VERIFY_PARAM_set_flags(vpm, f)) + ossl_raise(eX509StoreError, "X509_VERIFY_PARAM_set_flags"); +} + /* * call-seq: * store.flags = flags @@ -231,6 +266,10 @@ ossl_x509store_initialize(int argc, VALUE *argv, VALUE self) * Sets the default flags used by certificate chain verification performed with * the Store. * + * *NOTE*: Despite the name, this method appends the specified flags to the + * existing flag set rather than replacing it. To clear existing flags, use + * #clear_flags before calling this method. + * * _flags_ consists of zero or more of the constants defined in OpenSSL::X509 * with name V_FLAG_* or'ed together. * @@ -243,14 +282,42 @@ static VALUE ossl_x509store_set_flags(VALUE self, VALUE flags) { X509_STORE *store; - long f = NUM2LONG(flags); GetX509Store(self, store); - X509_STORE_set_flags(store, f); - + x509vpm_set_flags_i(X509_STORE_get0_param(store), flags); return flags; } +static void +x509vpm_clear_flags_i(X509_VERIFY_PARAM *vpm, VALUE flags) +{ + unsigned long f = NIL_P(flags) ? ~0UL : NUM2ULONG(flags); + + if (!X509_VERIFY_PARAM_clear_flags(vpm, f)) + ossl_raise(eX509StoreError, "X509_VERIFY_PARAM_clear_flags"); +} + +/* + * call-seq: + * store.clear_flags(flags = nil) + * + * Clears verification flags _flags_ for the Store. If _flags_ is omitted, + * clears all existing flags. + * + * See also the man page X509_VERIFY_PARAM_clear_flags(3). + */ +static VALUE +ossl_x509store_clear_flags(int argc, VALUE *argv, VALUE self) +{ + X509_STORE *store; + VALUE flags; + + rb_scan_args(argc, argv, "01", &flags); + GetX509Store(self, store); + x509vpm_clear_flags_i(X509_STORE_get0_param(store), flags); + return Qnil; +} + /* * call-seq: * store.purpose = purpose @@ -768,6 +835,25 @@ ossl_x509stctx_get_curr_crl(VALUE self) return ossl_x509crl_new(crl); } +/* + * call-seq: + * stctx.flags -> Integer + * + * Gets the verification flags for the context. + * + * See also the man page X509_VERIFY_PARAM_get_flags(3). + */ +static VALUE +ossl_x509stctx_get_flags(VALUE self) +{ + X509_STORE_CTX *ctx; + X509_VERIFY_PARAM *vpm; + + GetX509StCtx(self, ctx); + vpm = X509_STORE_CTX_get0_param(ctx); + return ULONG2NUM(X509_VERIFY_PARAM_get_flags(vpm)); +} + /* * call-seq: * stctx.flags = flags @@ -775,20 +861,43 @@ ossl_x509stctx_get_curr_crl(VALUE self) * Sets the verification flags to the context. This overrides the default value * set by Store#flags=. * + * *NOTE*: Despite the name, this method appends the specified flags to the + * existing flag set rather than replacing it. To clear existing flags, use + * #clear_flags before calling this method. + * * See also the man page X509_VERIFY_PARAM_set_flags(3). */ static VALUE ossl_x509stctx_set_flags(VALUE self, VALUE flags) { - X509_STORE_CTX *store; - long f = NUM2LONG(flags); - - GetX509StCtx(self, store); - X509_STORE_CTX_set_flags(store, f); + X509_STORE_CTX *ctx; + GetX509StCtx(self, ctx); + x509vpm_set_flags_i(X509_STORE_CTX_get0_param(ctx), flags); return flags; } +/* + * call-seq: + * stctx.clear_flags(flags = nil) + * + * Clears verification flags _flags_ for the Store. If _flags_ is omitted, + * clears all existing flags. + * + * See also the man page X509_VERIFY_PARAM_clear_flags(3). + */ +static VALUE +ossl_x509stctx_clear_flags(int argc, VALUE *argv, VALUE self) +{ + X509_STORE_CTX *ctx; + VALUE flags; + + rb_scan_args(argc, argv, "01", &flags); + GetX509StCtx(self, ctx); + x509vpm_clear_flags_i(X509_STORE_CTX_get0_param(ctx), flags); + return Qnil; +} + /* * call-seq: * stctx.purpose = purpose @@ -944,7 +1053,9 @@ Init_ossl_x509store(void) rb_define_method(cX509Store, "initialize", ossl_x509store_initialize, -1); rb_undef_method(cX509Store, "initialize_copy"); rb_define_method(cX509Store, "verify_callback=", ossl_x509store_set_vfy_cb, 1); + rb_define_method(cX509Store, "flags", ossl_x509store_get_flags, 0); rb_define_method(cX509Store, "flags=", ossl_x509store_set_flags, 1); + rb_define_method(cX509Store, "clear_flags", ossl_x509store_clear_flags, -1); rb_define_method(cX509Store, "purpose=", ossl_x509store_set_purpose, 1); rb_define_method(cX509Store, "trust=", ossl_x509store_set_trust, 1); rb_define_method(cX509Store, "time=", ossl_x509store_set_time, 1); @@ -973,7 +1084,9 @@ Init_ossl_x509store(void) rb_define_method(cX509StoreContext, "error_depth", ossl_x509stctx_get_err_depth, 0); rb_define_method(cX509StoreContext, "current_cert", ossl_x509stctx_get_curr_cert, 0); rb_define_method(cX509StoreContext, "current_crl", ossl_x509stctx_get_curr_crl, 0); + rb_define_method(cX509StoreContext, "flags", ossl_x509stctx_get_flags, 0); rb_define_method(cX509StoreContext, "flags=", ossl_x509stctx_set_flags, 1); + rb_define_method(cX509StoreContext, "clear_flags", ossl_x509stctx_clear_flags, -1); rb_define_method(cX509StoreContext, "purpose=", ossl_x509stctx_set_purpose, 1); rb_define_method(cX509StoreContext, "trust=", ossl_x509stctx_set_trust, 1); rb_define_method(cX509StoreContext, "time=", ossl_x509stctx_set_time, 1); diff --git a/test/openssl/test_x509store.rb b/test/openssl/test_x509store.rb index c13beae364ff1b..6385a46d95bdb0 100644 --- a/test/openssl/test_x509store.rb +++ b/test/openssl/test_x509store.rb @@ -370,6 +370,24 @@ def test_dup assert_raise(NoMethodError) { ctx.dup } end + def test_flags_clear + store = OpenSSL::X509::Store.new + assert_equal(0, store.flags) + store.flags = OpenSSL::X509::V_FLAG_CRL_CHECK + assert_equal(OpenSSL::X509::V_FLAG_CRL_CHECK, store.flags) + assert_warning(/clear_flags/) { + store.flags = OpenSSL::X509::V_FLAG_CRL_CHECK_ALL + } + assert_equal( + OpenSSL::X509::V_FLAG_CRL_CHECK|OpenSSL::X509::V_FLAG_CRL_CHECK_ALL, + store.flags + ) + store.clear_flags(OpenSSL::X509::V_FLAG_CRL_CHECK_ALL) + assert_equal(OpenSSL::X509::V_FLAG_CRL_CHECK, store.flags) + store.clear_flags + assert_equal(0, store.flags) + end + def test_ctx_cleanup # Deprecated in Ruby 1.9.3 cert = OpenSSL::X509::Certificate.new From 9404598418bc3ace24887927633fd4303484a09b Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Wed, 24 Jun 2026 23:18:05 +0900 Subject: [PATCH 3/5] [DOC] Fix rdoc-ref replacement Replace attributes and decode escaped punctuations. ``` Net/HTTP.html: `rdoc-ref:OpenSSL::SSL::SSLContext#max_version-3D` can't be resolved for `` Net/HTTP.html: `rdoc-ref:OpenSSL::SSL::SSLContext#min_version-3D` can't be resolved for `` Net/HTTP.html: `rdoc-ref:OpenSSL::SSL::SSLContext#ssl_version-3D` can't be resolved for `` Net/HTTP.html: `rdoc-ref:OpenSSL::SSL::SSLContext#attribute-i-verify_hostname` can't be resolved for `` ``` --- lib/net/http.rb | 10 +++++----- tool/sync_default_gems.rb | 3 ++- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/net/http.rb b/lib/net/http.rb index 7765a11588d6c3..0e8d8bf3fa869f 100644 --- a/lib/net/http.rb +++ b/lib/net/http.rb @@ -1550,7 +1550,7 @@ def use_ssl=(flag) attr_accessor :cert_store # Sets or returns the available SSL ciphers. - # See {OpenSSL::SSL::SSLContext#ciphers=}[rdoc-ref:OpenSSL::SSL::SSLContext#ciphers-3D]. + # See {OpenSSL::SSL::SSLContext#ciphers=}[rdoc-ref:OpenSSL::SSL::SSLContext#ciphers=]. attr_accessor :ciphers # Sets or returns the extra X509 certificates to be added to the certificate chain. @@ -1564,15 +1564,15 @@ def use_ssl=(flag) attr_accessor :ssl_timeout # Sets or returns the SSL version. - # See {OpenSSL::SSL::SSLContext#ssl_version=}[rdoc-ref:OpenSSL::SSL::SSLContext#ssl_version-3D]. + # See {OpenSSL::SSL::SSLContext#ssl_version=}[rdoc-ref:OpenSSL::SSL::SSLContext#ssl_version=]. attr_accessor :ssl_version # Sets or returns the minimum SSL version. - # See {OpenSSL::SSL::SSLContext#min_version=}[rdoc-ref:OpenSSL::SSL::SSLContext#min_version-3D]. + # See {OpenSSL::SSL::SSLContext#min_version=}[rdoc-ref:OpenSSL::SSL::SSLContext#min_version=]. attr_accessor :min_version # Sets or returns the maximum SSL version. - # See {OpenSSL::SSL::SSLContext#max_version=}[rdoc-ref:OpenSSL::SSL::SSLContext#max_version-3D]. + # See {OpenSSL::SSL::SSLContext#max_version=}[rdoc-ref:OpenSSL::SSL::SSLContext#max_version=]. attr_accessor :max_version # Sets or returns the callback for the server certification verification. @@ -1588,7 +1588,7 @@ def use_ssl=(flag) # Sets or returns whether to verify that the server certificate is valid # for the hostname. - # See {OpenSSL::SSL::SSLContext#verify_hostname=}[rdoc-ref:OpenSSL::SSL::SSLContext#attribute-i-verify_hostname]. + # See {OpenSSL::SSL::SSLContext#verify_hostname=}[rdoc-ref:OpenSSL::SSL::SSLContext#verify_hostname]. attr_accessor :verify_hostname # Returns the X509 certificate chain (an array of strings) diff --git a/tool/sync_default_gems.rb b/tool/sync_default_gems.rb index 3242dee5d2b541..9ab602ac801f9e 100755 --- a/tool/sync_default_gems.rb +++ b/tool/sync_default_gems.rb @@ -344,9 +344,10 @@ def replace_rdoc_ref(file) changed |= src.gsub!(%r[\[\Khttps://docs\.ruby-lang\.org/en/master(?:/doc)?/(([A-Z]\w+(?:/[A-Z]\w+)*)|\w+_rdoc)\.html(\#\S+)?(?=\])]) do name, mod, label = $1, $2, $3 mod &&= mod.gsub('/', '::') - if label && (m = label.match(/\A\#(?:method-([ci])|(?:(?:class|module)-#{mod}-)?label)-([-+\w]+)\z/)) + if label && (m = label.match(/\A\#(?:(?:method|attribute)-([ci])|(?:(?:class|module)-#{mod}-)?label)-(.+)\z/)) scope, label = m[1], m[2] scope = scope ? scope.tr('ci', '.#') : '@' + label.gsub!(/-(\h\h)/) {$1.to_i(16).chr(Encoding::ASCII_8BIT)} end "rdoc-ref:#{mod || name.chomp("_rdoc") + ".rdoc"}#{scope}#{label}" end From 491d6151b2aa8c1e7b46eccaa652826678266df9 Mon Sep 17 00:00:00 2001 From: Stan Lo Date: Wed, 24 Jun 2026 16:58:38 +0100 Subject: [PATCH 4/5] Bump RDoc bundled gem revision (#17448) * Bump RDoc bundled gem revision * Skip RBS RDoc plugin tests for RDoc bump --- gems/bundled_gems | 2 +- tool/rbs_skip_tests | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/gems/bundled_gems b/gems/bundled_gems index c89365a8526530..d67684d6e5d672 100644 --- a/gems/bundled_gems +++ b/gems/bundled_gems @@ -37,7 +37,7 @@ ostruct 0.6.3 https://github.com/ruby/ostruct pstore 0.2.1 https://github.com/ruby/pstore benchmark 0.5.0 https://github.com/ruby/benchmark logger 1.7.0 https://github.com/ruby/logger -rdoc 7.2.0 https://github.com/ruby/rdoc 1f93543615928b6d45357432f16ec6006e2d8b1e +rdoc 7.2.0 https://github.com/ruby/rdoc a8df5c5d03b63cf05425bf676644688ac673a329 win32ole 1.9.3 https://github.com/ruby/win32ole irb 1.18.0 https://github.com/ruby/irb reline 0.6.3 https://github.com/ruby/reline diff --git a/tool/rbs_skip_tests b/tool/rbs_skip_tests index 39ac16cb8f1551..54259cfdf424f8 100644 --- a/tool/rbs_skip_tests +++ b/tool/rbs_skip_tests @@ -50,3 +50,13 @@ test_new(RegexpSingletonTest) # Errno::ENOENT: No such file or directory - bundle test_collection_install__pathname_set(RBS::CliTest) test_collection_install__set_pathname__manifest(RBS::CliTest) + +# RBS 4.0.3's RDoc plugin is incompatible with the RDoc 7.2.0 development revision +test_attr_decl_1(RDocPluginParserTest) +test_attr_decl_2(RDocPluginParserTest) +test_instance_method_1(RDocPluginParserTest) +test_instance_method_comment_and_tokens(RDocPluginParserTest) +test_instance_method_generic(RDocPluginParserTest) +test_instance_method_with_block(RDocPluginParserTest) +test_method_alias_decl_1(RDocPluginParserTest) +test_method_alias_decl_2(RDocPluginParserTest) From fd2f18c3a41af0d84a3ce9ed41e0d2344041d456 Mon Sep 17 00:00:00 2001 From: Max Bernstein Date: Wed, 24 Jun 2026 14:47:08 -0400 Subject: [PATCH 5/5] ZJIT: Print string name of constant in rb_zjit_constant_state_changed (#17470) It's not so useful to see `ID(abc)` when debugging with `--zjit-debug` or in Perfetto traces. Also provide the string representation of the ID, for example: `ID(abc): String`. --- zjit/src/invariants.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zjit/src/invariants.rs b/zjit/src/invariants.rs index 1433b8b9b3ce56..233ffc21a7903d 100644 --- a/zjit/src/invariants.rs +++ b/zjit/src/invariants.rs @@ -374,10 +374,10 @@ pub extern "C" fn rb_zjit_constant_state_changed(id: ID) { let invariants = ZJITState::get_invariants(); if let Some(patch_points) = invariants.constant_state_patch_points.remove(&id) { let cb = ZJITState::get_code_block(); - debug!("Constant state changed: {:?}", id); + debug!("Constant state changed: {id:?}: {}", id.contents_lossy()); // Invalidate all patch points for this constant ID - compile_patch_points!(cb, patch_points, Const, "Constant state changed: {:?}", id); + compile_patch_points!(cb, patch_points, Const, "Constant state changed: {id:?}: {}", id.contents_lossy()); cb.mark_all_executable(); }