21742 Commits
Author SHA1 Message Date
Kazuki Yamaguchi 9bcca300da test/ruby/test_process.rb: simplify testing spawn with long strings
test_aspawn_too_long_path just wants to verify that passing a very long
string to spawn does not cause a crash. Use an input string that is long
enough for this purpose and does not ends up with spawning too many
processes.

This change makes this test run much faster on systems where /bin/sh is
bash, which retries fork() several times with long delays before giving
up.

(cherry picked from commit fa8df43bd1)
2026-10-22 20:55:35 -07:00
Kazuki Yamaguchi 844a167595 test/ruby/test_io.rb: do not set RLIMIT_NPROC
On my computer, setting RLIMIT_NPROC to the low value of 2048 prevents
the forked process from creating any new native threads and it causes
make test-all to hang forever.

Remove it, as it does not seem to serve any purpose here.

(cherry picked from commit 7d1be0888a)
2026-10-22 20:55:35 -07:00
Hiroya Fujinami c3c8863acd Reject overflowed repeat/null-check IDs in huge regexps (#16801)
Add explicit errors for excessive range-repeat and null-check IDs;
map new errors in regerror; add regression tests for oversized generated
patterns.
2026-10-22 13:07:20 -07:00
Kevin Newton 0cd151f4b6 [ruby/prism] Reverse-sync from upstream
https://github.com/ruby/prism/commit/e8da28b4cb
2026-10-22 06:10:36 +00:00
Erik Berlin 4ba4954dd7 Fix Range#include? of an empty range of single characters
A range of single character Strings told that it includes its end
even when it is empty, since the end was not compared with the begin.

    ("z".."a").to_a          #=> []
    ("z".."a").include?("a") #=> true

(cherry picked from commit 1bf9130943)
2026-10-21 11:56:12 -07:00
John Hawthorn c092fd8552 Flush the current Ractor's stdout/stderr before spawning
prefork() flushed rb_stdout and rb_stderr, which are the main Ractor's IOs.
From another Ractor that takes the write_lock of an IO the Ractor does not
own. When a main Ractor thread holds it (any thread logging to $stdout will
do), the spawning Ractor's only thread sleeps on the mutex and
rb_check_deadlock, which only looks at the Ractor's own threads, raises
"No live threads left. Deadlock?" (fatal) from Kernel#spawn/system/popen.

Use rb_ractor_stdout()/rb_ractor_stderr() so each Ractor flushes its own.

[Bug #22434]

(cherry picked from commit 28f2eae4cf)
2026-10-21 11:46:04 -07:00
Randy Stauner ac121993f5 [Backport #22415] Restore ec->errinfo after handling interrupts
Interrupts are checked at arbitrary points, including while a jump
(break/next/redo/return/raise/throw) is being propagated, and the payload
of that jump lives in ec->errinfo.

Handling an interrupt can run Ruby code: rb_signal_exec() runs a trap
handler and threadptr_interrupt_exec_exec() runs interrupt_exec tasks.
Neither preserves ec->errinfo, and any rescue or ensure clause that code
enters sets it to nil.

Thread.handle_interrupt checks for interrupts after its block returns but
before re-raising the jump that left the block.  If a signal handler runs
in that window the throw data is replaced with nil and the VM dereferences
nil as a vm_throw_data:

    THROW_DATA_CATCH_FRAME (obj=0x4) at vm_insnhelper.h:217
    vm_exec_handle_exception (ec=..., errinfo=4) at vm.c:2880
    vm_exec_loop / rb_vm_exec at vm.c:2829 / vm.c:2812
    invoke_iseq_block_from_c / rb_yield / rb_ary_each

rb_postponed_job_flush() and rb_exec_event_hooks() already save and restore
ec->errinfo around the Ruby code they run; do the same for the rest of
rb_threadptr_execute_interrupts().  The paths that set errinfo on purpose
(rb_exc_raise, rb_threadptr_to_kill) jump out of the function and so do not
reach the restore.
2026-10-16 09:23:40 -07:00
Douglas Eichelberger 27204205e6 Reset the fork lock in the child without unlocking it
A thread that is waiting for the fork lock when another thread forks
exists only in the parent, but on macOS the child's copy of the lock
still counts it. After the child unlocks its copy,
pthread_rwlock_destroy() and pthread_rwlock_init() fail with EBUSY, so
the child aborts with
"[BUG] pthread_rwlock_destroy: Resource busy (EBUSY)". Lookup threads
wait for the lock whenever a fork holds it, so a process that resolves
names in background threads while it forks loses a child now and then.

Reset the child's copy by assigning PTHREAD_RWLOCK_INITIALIZER, as
rb_internal_thread_event_hooks_rw_lock_atfork() does for its lock, and
do not unlock it first. With a waiter counted, macOS's unlock enters the
kernel to wake it, and the state that leaves behind for the lock's
address survives the assignment, so a later waiter in the child can
sleep forever although no thread holds the lock. FreeBSD already skipped
the unlock in the child (dc8c382266), and on glibc the child's unlock
does not release the write lock anyway.

[Bug #22445]
2026-10-15 12:37:17 -07:00
Douglas Eichelberger 59db3a3a81 Join getaddrinfo and getnameinfo threads before fork
A getaddrinfo thread calls getaddrinfo(3) inside rb_thread_prevent_fork,
but on glibc its thread exit takes the resolver configuration lock again
after the thread has released the fork lock. A fork that lands then
leaves the child with that lock held, and every later name lookup in the
child blocks forever. getnameinfo(3) attaches the same resolver state,
even for a name in /etc/hosts, and its thread ran outside
rb_thread_prevent_fork altogether.

Make the lookup threads joinable and have fork join any that may still
be exiting. As a lookup thread finishes, still inside
rb_thread_prevent_fork, it swaps itself into a single slot with an
atomic exchange and joins the thread it displaced, which has usually
exited by then, and rb_thread_acquire_fork_lock joins whatever is left
in the slot. Once lookups settle, at most one finished thread is left
unjoined. The slot holds pthread_t as a pointer, with NULL for empty,
which assumes pthread_t is scalar and never 0.

getnameinfo threads now run within rb_thread_prevent_fork and register
as they exit, as getaddrinfo threads do, and where getnameinfo runs
without a helper thread it is called within rb_thread_prevent_fork too.
So fork also waits for a reverse lookup in progress, as it already did
for a forward one. raddrinfo_pthread_create now always creates joinable
threads, which drops its pthread_detach fallback for platforms without
pthread_attr_setdetachstate.

The slot is kept across fork, so every fork path that keeps running
Ruby code has to take the fork lock. On the BSDs, Process.daemon forks
with daemon(3) and did not take it, so the daemon would inherit the
registered thread, and its next fork would abort in pthread_join with
ESRCH. Take and release the fork lock around daemon(3) as rb_fork_ruby
does. daemon(3) can fail in the child as well as in the parent, so
compare pids to tell which process returned.

[Bug #22406]

(cherry picked from commit 44f7c6b00d)
2026-10-14 17:15:16 -07:00
Randy Stauner df0d7a87b6 [Backport #22426] ZJIT: Snapshot FrameState with reordered args before direct send
You can see the reordered args in the new Snapshot right before the
DirectSend insn:

       v14:Any = Snapshot FrameState { pc: 0x00, stack: [v6, v11, v13], locals: [] }
       PatchPoint MethodRedefined(Object@0x00, a@0x00, cme:0x00)
       PatchPoint NoSingletonClass(Object@0x00)
       v22:HeapObject[class_exact*:Object@VALUE(0x00)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x00)]
    -  v23:BasicObject = SendWithoutBlockDirect v22, :a (0x00), v13, v11
    -  v16:Any = Snapshot FrameState { pc: 0x00, stack: [v23], locals: [] }
    +  v23:Any = Snapshot FrameState { pc: 0x00, stack: [v6, v13, v11], locals: [] }
    +  v24:BasicObject = SendWithoutBlockDirect v22, :a (0x00), v13, v11
    +  v16:Any = Snapshot FrameState { pc: 0x00, stack: [v24], locals: [] }

4.0 Backport of d81a11d4e6
2026-10-13 13:59:15 -07:00
Nobuyoshi Nakada 15212f6c1c [Bug #22416] Respect timezone abbreviation encodings in strftime
Timezone objects can return abbreviations in encodings beyond the
locale.  Respect their encoding when formatting `%Z` to avoid
misinterpreting their bytes.
2026-10-09 16:07:27 -07:00
Peter Zhu 738b4971c6 Fix buffer overflow in Array#values_at
Array#values_at can execute arbitrary code during execution which may
modify the source array. This can cause a buffer overflow if the new length
of the array has decreased. The following script crashes:

    a = (0..100_000).to_a
    obj = Object.new
    obj.define_singleton_method(:begin) do
      a.clear
      0
    end
    obj.define_singleton_method(:end) { 10_000 }
    obj.define_singleton_method(:exclude_end?) { false }
    puts a.values_at(obj).length
2026-10-09 16:07:24 -07:00
Hiroshi SHIBATA 44da562d59 [Bug #22284] Test curry with an arity that responds to to_int
It also counts the to_int calls, so it fails on LP64 platforms too, where the old code converted the arity again on every call.
2026-10-09 16:07:20 -07:00
Nobuyoshi Nakada ccccb04486 [Bug #22388] Fix use-after-free in Set#& 2026-10-09 16:07:16 -07:00
Nobuyoshi Nakada 566e6dc8c0 [Bug #22388] Fix use-after-free in Enumerator::Lazy#flat_map 2026-10-09 16:07:16 -07:00
Nobuyoshi Nakada bd154f0435 [Bug #22388] Fix use-after-free in Enumerable#sum 2026-10-09 16:07:16 -07:00
Nobuyoshi Nakada 9568b5dfce [Bug #22388] Fix use-after-free in some Enumerable methods 2026-10-09 16:07:16 -07:00
Sethu f426035478 [Bug #22384] Fix memory leak on compile error in a later branch of an alternation 2026-10-09 16:07:12 -07:00
Tavian Barnes 133bc46e19 io: preserve errno across rb_io_wait() call. (#19121)
[Bug #22391]
2026-10-09 16:07:08 -07:00
Kazuki Yamaguchi 4982f5bd18 Fix IO#gets ignoring the limit argument
IO#gets first searches for separator candidates by looking for the last
byte of the record separator, and then checks whether it is an actual
match.

If a candidate is found at limit-th byte of the stream but is rejected
because it does not start at a character boundary, the limit check is
incorrectly skipped.

[Bug #22342]
2026-10-09 16:07:03 -07:00
Hiroshi SHIBATAandClaude Opus 5.5 eb4d16c775 Use Kernel without test/unit in the classext GC stress test
The Enumerable version did not crash an unfixed build on Linux.  Kernel
does on both Linux and macOS, but only while nothing in the main box has
written to it yet, and loading test/unit already has.  So run the code
through assert_in_out_err instead of assert_separately.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 16:06:59 -07:00
Hiroshi SHIBATAandClaude Opus 5 d05180d0f6 Attach a box classext to its class before filling it in
rb_class_duplicate_classext builds the whole copy first and the caller only
then puts it in the class's table, so a GC in the middle collects the tables
it has made and marking the class afterwards walks into a freed object.  Put
the classext in the table first instead.  Marking it while it is still half
built is fine, since ZALLOC leaves every field zero.

    $ RUBY_BOX=1 ruby --disable-gems -e 'GC.stress = true; module Enumerable; def m; end; end'
    -e:1: [BUG] try to mark T_NONE object (obj: out-of-heap:0x..., parent: ... T_MODULE/Module Enumerable)

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-09 16:06:59 -07:00
Hiroshi SHIBATAandClaude Opus 5 0f17de3708 [Bug #22339] Skip classes already freed while freeing a box
`RUBY_FREE_AT_EXIT=1` frees every object in an arbitrary order, so a
class a box refers to can go before the box itself, and freeing a class
already frees its classext for that box.  The box then reached rb_bug
with a T_NONE object, and the report crashed and looped in the signal
handler instead of aborting, so the process spun at exit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-09 16:06:56 -07:00
Hiroshi SHIBATAandClaude Fable 5 ae7c5a31f8 Fix prepend ancestry order in user boxes
ensure_origin read the m_tbl before the first write created the box-local classext, so the origin ICLASS captured the prime m_tbl while the subclass ICLASSes referred the box-local copy made by the CoW. rb_prepend_module then missed the shared m_tbl check and inserted the prepended module after the target module instead of before it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-10-09 16:06:56 -07:00
Nobuyoshi Nakada 831f43b97e [Bug #22335] Use identity hashes for keyword argument tracking
With more than 31 keyword arguments, tracking unevaluated defaults
uses an integer-keyed Hash whose collisions can invoke methods when
`Integer#==` is refined or redefined, corrupting argument locals above
`cfp->sp`.  Use identity hashes to prevent these calls during argument
setup and `checkkeyword` execution.

Making `rb_any_cmp` compare special constants directly would also
bypass redefined `eql?` methods in ordinary hashes.  Limit identity
comparison to the internal keyword tracking hashes to preserve that
behavior for now.
2026-10-09 16:06:52 -07:00
Nobuyoshi Nakada 2374bd218c [Bug #22340] Fix stat on symbolic-link loops on Windows
Preserve `ELOOP` instead of hiding symbolic-link resolution errors
with the file-enumeration fallback, so existence checks return false.
2026-10-09 16:06:48 -07:00
Peter Zhu 117b6497e7 Fix use-after-free in String#encode when string modified
[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)
2026-10-09 16:06:44 -07:00
Hiroshi SHIBATAandClaude Opus 4.8 8fe2e0da67 [Bug] win32: create directory symlinks for relative directory targets
File.symlink decided whether to create a directory symlink by calling
GetFileAttributesW on the target as given, which resolves a relative
target against the current directory instead of the link's directory.
When the two differ, a relative target pointing at a directory became a
file symlink, so Dir operations on it failed. Resolve relative targets
against the link's directory before testing for a directory.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-09 16:06:40 -07:00
Peter Zhu 8b1a10840a [Bug #22336] Fix use-after-free in IO::Buffer#get_string. (#18919) 2026-10-09 16:06:37 -07:00
Peter Zhu fc7d08aeca Fix crash when returning splat
[Bug #22334]

If we return a with a keyword splat, then each element does not necessarily
push two values onto the stack, so we cannot compile this as a simple sequence
of key-value pairs combined with newhash. The following script reproduces
the issue and crashes:

    def r
      return **{foo: 1}
    end
    puts r()
2026-10-09 16:06:33 -07:00
Nobuyoshi Nakada db1082f4dc [Bug #22322] Take transcoder names from declared transcoders
Fix use-after-free of dangling pointers.
2026-10-09 16:06:29 -07:00
Peter Zhu 4e9102bf35 Fix use-after-free when clearing array during zip
[Bug #22319]

Array#zip triggers an use-after-free it does not account for when the call
to rb_check_array_type modifies the source array. The following script crashes:

    a = (1..100_000).to_a
    evil = Object.new; $a = a
    def evil.to_ary; $a.clear; [1,2,3]; end
    a.zip(evil)
2026-10-09 16:06:25 -07:00
Peter Zhu f8cb02fbb7 Fix use-after-free when clearing array during flatten
[Bug #22318]

Array#flatten triggers an use-after-free it does not account for when the
call to rb_check_array_type modifies the source array. The following script
crashes:

    a = (1..100_000).to_a
    $a = a
    bad = Object.new
    def bad.to_ary; $a.clear; [1,2,3]; end
    a << bad
    a.flatten
2026-10-09 16:06:21 -07:00
Peter Zhu b58cb49058 Fix crash when source string modified in String#unpack
[Bug #22315]

The following script crashes because there is a use-after-free on the
source string during String#unpack:

    s = "C" * 4000000
    s.unpack("L*") { s.clear }

String#unpack will now instead raise RuntimeError with message "string modified".
2026-10-09 16:06:18 -07:00
Peter Zhu 555611e109 Fix Method#inspect when klass is iclass
[Bug #22321]

In the following script, the klass of the Method object is an iclass, which
will raise an error "NoMethodError: undefined method 'inspect' for an instance of M":

    module M
      def foo = 1
    end

    class C
      include M
    end

    obj = C.new
    meth = obj.method(:foo).unbind.bind(obj)
    puts meth.inspect
2026-10-09 16:06:13 -07:00
Hiroshi SHIBATAandClaude Opus 5.5 6e4eb1e1d0 Omit the fiber pool exhaustion test with a large max_map_count
The test creates fibers with GC disabled until mmap reaches max_map_count, so its memory use grows with that limit. GitHub Actions runners set it to 262144, where a RUBY_DEBUG build needs about 16GB, and the parse.y check has been losing its runner while this test runs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 14:47:03 -07:00
Nobuyoshi Nakada 801356f317 Relax the flaky test threshold on RHEL 10.0 x86_64
(cherry picked from commit acd0c68a07)
2026-10-02 14:43:28 -07:00
John Hawthorn f980c9475b Fix reuse of MatchData assigned to $~
$~ = m = /a/.match("a").dup
    /b/ =~ "b"
    p m

[Bug #22381]

(cherry picked from commit ea3cd7efe1)
2026-10-01 12:02:09 -07:00
XrXr 9554389753 ZJIT: Don't panic when calling RubyVM::ZJIT.reset_stats! with ZJIT off
[Backport #22327] (The rust harness isn't setup for running code without
ZJIT, so the test is in ruby.)
2026-09-29 17:05:34 -07:00
Randy Stauner 67cd1d3bde [Bug #22387] Fix termlen in str_shared_replace (#19104)
[Bug #22387] Fix termlen in str_shared_replace

The buffer is copied over based on the new string's termlen
so we can just set the new encoding raw
instead of recalculating based on the old encoding's termlen.
2026-09-28 15:53:16 -07:00
Randy Stauner 4ac2a34ced [Bug #22383] Fix inverted STR_SHARED check in rb_str_tmp_frozen_release
45a2c95d0f mistakenly rewrote

    FL_TEST_RAW(orig, STR_SHARED) && !FL_TEST_RAW(orig, STR_TMPLOCK|RUBY_FL_FREEZE)

as

    FL_TEST_RAW(orig, STR_SHARED | STR_TMPLOCK) == STR_TMPLOCK

requiring orig to not be shared, the opposite of the original
condition.  orig always shares the buffer with tmp here, so the buffer
was never given back and the string stayed shared until its next
modification copied it.  The code then read aux.shared from strings
that are not shared, where the union holds aux.capa.

This should be backported to 4.0
2026-09-25 08:48:32 -07:00
Hiroshi SHIBATAandClaude Opus 5.5 53da7e1660 Avoid overflow in the quantifier expansion size check
Nested bounded repeats let the int product tlen * qn->lower overflow, so
the check that keeps the compiler from unrolling a large repeat could pass
for an enormous one, hanging the compile or emitting wrapped jump offsets.
Compare against the limit by division instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 08:45:55 -07:00
Gil DesmaraisandJeremy Evans 1533c10971 Preserve compare_by_identity in some Set operations
Backport of f069efc3f6

Fixes [Bug #22174]

Co-authored-by: Jeremy Evans <code@jeremyevans.net>
2026-09-24 11:19:06 -07:00
Takashi Kokubun 1e7d45210e merge revision(s) 5831948a66: [Backport #22303]
[PATCH] Fix the `intern` VM instruction to be `leaf=false` (also in YJIT, ZJIT)

	rb_str_intern can raise EncodingError and call its initializer.
	This call can push a Ruby frame.

	Fix in the VM, YJIT, and ZJIT.
2026-09-14 12:48:48 -07:00
Takashi Kokubun 310533c5a2 merge revision(s) e5132b1627: [Backport #22264]
[PATCH] [Bug #22264] Warn for duplicate keys after leading keyword splat

	Leading keyword splats must not suppress duplicate literal key
	warnings.
2026-09-14 12:47:14 -07:00
Takashi Kokubun f903ac6707 merge revision(s) 46c287e2ca: [Backport #22259]
[PATCH] Fix splicing an array that shares its buffer with a longer array

	Arrays sharing one buffer have the same data pointer, so rb_ary_splice()
	took `a` in `b = a.dup; b.pop; b.concat(a)` for `b` itself and rebased
	the source onto `b`'s shorter storage, copying uninitialized memory.
	Have the callers tell whether the replacement really is `ary`.

	[Bug #22259]

	Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-14 12:47:06 -07:00
Takashi Kokubun 137e40a9f7 merge revision(s) 239a472609, d29afc8ab8: [Backport #20958]
[PATCH] fix ENV.keys encoding on windows

	Fix the encoding of ENV.keys elements to UTF-8 on windows.
	They contain UTF-8 byte sequences with locale encoding.

	[PATCH] [Bug #20958] Fix test_keys_encoding on hosts without en_US.UTF-8

	`EnvUtil.invoke_ruby` forces the C locale on the child process, so the
	test set `LC_ALL=en_US.UTF-8` to get a UTF-8 locale back.  That locale
	is not generated on the debian11 chkbuild host, where `setlocale` falls
	back to C and the ENV key comes out as ASCII-8BIT instead of the
	default internal encoding.  Inherit the locale of the parent process
	instead, and omit the test when that locale is not UTF-8.

	https://rubyci.s3.amazonaws.com/debian11/ruby-master/log/20260725T093003Z.fail.html.gz
2026-09-14 12:43:45 -07:00
Hiroshi SHIBATAandClaude Opus 5 8c6c731547 Merge RubyGems/Bundler 4.0.20
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 09:25:43 -07:00
Hiroshi SHIBATAandClaude Opus 5 8aad2b9b57 Merge RubyGems/Bundler 4.0.19
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 09:25:43 -07:00
Hiroshi SHIBATAandClaude Opus 5 b667732024 Merge RubyGems/Bundler 4.0.18
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 09:25:43 -07:00