Skip to content

[pull] master from ruby:master - #1422

Merged
pull[bot] merged 22 commits into
turkdevops:masterfrom
ruby:master
Sep 18, 2026
Merged

pull[bot] merged 22 commits into
turkdevops:masterfrom
ruby:master

Conversation

@pull

@pull pull Bot commented Sep 18, 2026

Copy link
Copy Markdown

See Commits and Changes for more details.


Created by pull[bot] (v2.0.0-alpha.4)

Can you help keep this open source service alive? 💖 Please sponsor : )

jeremyevans and others added 22 commits September 17, 2026 18:29
Ruby will be emitting a deprecation warning for this shortly. Switch
to using super instead of an alias.
Aliasing a method in a prepended module can result in a super
call going into a descendant instead of an ancestor. Removal plan:

4.1: Deprecation warning
4.2: Warning even in non-verbose mode
4.3: Removal (target method lookup starts at origin class)

Fixes [Bug #22273]
When running multiple Ractors, we can't call dfree of a non-thread-safe
T_DATA while other Ractors are running or doing GC work. If there are multiple
Ractors, we save the T_DATA object's `dfree` and `data` pointer in an entry. Once
the entry buffer is full per-Ractor, we publish it to the global list atomically.
Once the threshold for these deferred T_DATAs is reached (right now ~65K), the next
Ractor to run interrupts calls the `dfree` functions for these objects under the VM
barrier. It does not do any other GC work during this postponed job.

If a global GC runs, it also calls the `dfree` functions for all these objects (it drains
the deferred-free queue). When only 1 Ractor is running (`rb_gc_single_objspace_p()`),
these objects are freed normally and aren't added to any queue.

NOTE
----
Embedded T_DATA objects need to be special-cased because their `data` points to inside the
object's slot itself, which we reuse right away now. For non-thread-safe T_DATA that is
embeddable, we need to create it as unembedded - even without Ractors.
Gem::Specification#to_ruby interpolates the version into the `# stub:` comment without escaping, and #ensure_loadable_spec evals that output, so a line break in the version ends the comment and the rest runs as Ruby before the version itself is rejected. A version loaded from gem metadata with Psych goes through Gem::Version#yaml_initialize and skips the checks in #initialize. Require the pattern Gem::Version accepts, without the surrounding whitespace it strips.

ruby/rubygems@78aa8f57b1

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
GemVersionPromoter sorts every candidate newest first under --major, and --strict does not cap it at any major, so "next major version" never matched what it does.

ruby/rubygems#8090

ruby/rubygems@8db0a06d37

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…an 3.2

Symbol#name (3.0), Exception#detailed_message (3.2), Module#ruby2_keywords (2.7) and URI::Generic#hostname (1.9.3) exist on every supported Ruby, so the fallbacks were dead code.

ruby/rubygems@ee203a9319

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Ruby 3.2 ships openssl 3.1, so PKey#public_to_der and SSLContext#min_version= are always available. get_public_key is documented to take a PKey, so the test that passed it a certificate now reads the key from the certificate directly.

ruby/rubygems@c76d1bab81

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Psych::Visitors::YAMLTree.create has existed since psych 2.0, so the shim was never defined on any supported Ruby.

ruby/rubygems@242ca5848b

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Bumps the github-actions group with 2 updates in the / directory: [ruby/setup-ruby](https://github.com/ruby/setup-ruby) and [taiki-e/install-action](https://github.com/taiki-e/install-action).


Updates `ruby/setup-ruby` from 1.322.0 to 1.323.0
- [Release notes](https://github.com/ruby/setup-ruby/releases)
- [Changelog](https://github.com/ruby/setup-ruby/blob/master/release.rb)
- [Commits](ruby/setup-ruby@bec3f19...984c0c8)

Updates `taiki-e/install-action` from 2.87.12 to 2.87.13
- [Release notes](https://github.com/taiki-e/install-action/releases)
- [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md)
- [Commits](taiki-e/install-action@3f74d7c...26e9283)

---
updated-dependencies:
- dependency-name: ruby/setup-ruby
  dependency-version: 1.323.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: github-actions
- dependency-name: taiki-e/install-action
  dependency-version: 2.87.13
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
`https://TOKEN:@host/` parses with an empty-string password, so `token?`
did not recognize it and `redacted` kept the token in clear text as
`https://TOKEN:REDACTED@host/`. `redact_credentials_from` also matched
the empty string and inserted `REDACTED` at the start of the message.

ruby/rubygems@860d7b0390

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…roxy?

`net_http_args` calls `no_proxy?` whether or not a proxy is configured,
so a typo such as `https:/host` that URI parses with a nil host died with
`NoMethodError` instead of reaching the connection error that reports
the URI.

ruby/rubygems@7e1a37a6c3

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…pact index fetcher

A relative Location died with `NoMethodError` because it was parsed on
its own instead of against the request URI, 308 fell through to "Bad
response", the non-https rejection printed the Location's credentials in
clear text, and an absolute Location on the same host lost the userinfo
that a relative one keeps. Gem::CompactIndexClient::HTTPFetcher already
handles all four.

ruby/rubygems@1714f94872

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
[Bug #22330]

The following script demonstrates a use-afer-free where we see corruption:

    s = "あ" * 10_000
    s.encode("US-ASCII", fallback: proc { |c| s.clear; "?" })

Raises may different errors such as:

    "\xCB" followed by "\xF5" on UTF-8 (Encoding::InvalidByteSequenceError)
    "\xF4" followed by "r" on UTF-8 (Encoding::InvalidByteSequenceError)
    "\x8B" on UTF-8 (Encoding::InvalidByteSequenceError)
Each objspace has its own finalizer list since RLGC landed so there aren't
concurrency issues here. This used to be a global table that all Ractors
shared. This affects only the default GC.
…8900)

When a socket has buffered output (`sync == false`) and `#close_write` is
called, the write side was shut down via `shutdown(SHUT_WR)` without first
flushing the buffer. This had two bad consequences:

- the buffered bytes were silently dropped (never delivered to the peer);
- a subsequent `#close` tried to flush the still-populated write buffer
  into the now `shutdown(SHUT_WR)` socket and raised `Errno::EPIPE`.

Flush the buffer before shutting down the write side, matching the
write-only branch (which flushes via `rb_io_close`) and the non-socket
`IO#close_write` path. `IO#close_write` for a plain `IO` wrapping a socket
fd is fixed in the same way.

Assisted-By: devx/2c064143-8655-4631-a7cd-6395bb241bdc
Fixes the following crash:

    str = "hello" * 100
    obj = Object.new
    obj.define_singleton_method(:to_str) do
      str.replace("")
      "x"
    end
    str[/h.l/] = obj
#18574 makes $stdout and $stderr reassignment reach Kernel#puts and Kernel#warn inside a box. The box lanes run on a ruby-core master build, which has the fix.

https://bugs.ruby-lang.org/issues/21867

ruby/rubygems@69e92e9fdb

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…icFileWriter

`defined?(Gem::SecureRandom)` is already true while another thread is still loading the file and has not yet extended it with `Random::Formatter`, so a parallel installer worker could skip the require and fail with `NoMethodError` on `Gem::SecureRandom.hex`. Requiring it unconditionally makes that worker wait until the load finishes.

ruby/rubygems@bf5ac6dd11

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`IO#write` with many arguments in sync mode was not observably atomic:
when the argument count exceeded `IOV_MAX`, the internal writev path
coalesced the trailing data into the write buffer and returned without
flushing, so nothing reached the peer until the next flush or `#close`.

`io_binwritev` is only reached in sync/TTY mode (see `io_writev`), but its
"append to buffer when it fits" branch returned without flushing. Flush
the coalesced buffer before returning so that a multi-argument write is
immediately written out, matching the single-argument path.

Before, a pipe in the default `sync == true` mode:

    w.write(*(["a"] * 1024))
    r.read_nonblock(1024) # => nothing available until w.close

Assisted-By: devx/2c064143-8655-4631-a7cd-6395bb241bdc
The following script causes a buffer overflow and returns corrupted strings
because it's reading past the end of the string buffer:

    s = "x" + "l" * 3999
    obj = Object.new
    obj.define_singleton_method(:to_int) do
      s.clear
      0
    end
    p s.slice!(/l+$/, obj)

Outputs corrupted string that looks like:

    "\xEA\x94\xEA\xCE`\u0000\u0000`\x88n\xEFC
@pull pull Bot locked and limited conversation to collaborators Sep 18, 2026
@pull pull Bot added the ⤵️ pull label Sep 18, 2026
@pull
pull Bot merged commit ab5d140 into turkdevops:master Sep 18, 2026
1 of 3 checks passed
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants