96675 Commits

Author SHA1 Message Date
Stan Lo
53bed35cba JIT: Reject --{y,z}jit-exec-mem-size values that overflow the u32 reservation size (#19720)
(cherry picked from commit 9bfa7c99a6b4b115318e63d92438f1a92cfbb9ed)
2026-11-05 18:09:18 -08:00
Hiroshi SHIBATA
35bd011334 YJIT: A64: Load the absolute address for far jumps in emit_jmp_ptr
Since a1c61f0ae5 made CodePtr an offset into the code region, the fallback for targets out of the range of b has loaded that offset for br instead of the address. The fallback is taken once the code region exceeds 128MiB, with --yjit-exec-mem-size or with --yjit-mem-size when that is not given.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 17:41:17 -08:00
Hiroshi SHIBATA
1636a3cb0c Fix callcc availability checks in tests
`callcc` is a private global function, so `respond_to?(:callcc)` is always false and these tests have been skipped since 5621d794a2. Builds that disable `callcc` still skip them, because `respond_to?` returns false for not-implemented methods even with `include_all`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 3a3a921ac6c02b4269a2c6d4c6cb30d06fa6892e)
2026-11-04 07:36:23 -08:00
Hiroshi SHIBATA
c671724a4b Remove Hash tests for re-entering iteration with callcc
ac5ac48a36 removed the ensure rollback these tests exercise. They were left behind because `respond_to?(:callcc)` always skipped them, and they fail once that check is fixed.

[Bug #20655]

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 1d490a00b21a3efac5b65b26ef7c1057fbc776f2)
2026-11-04 07:36:23 -08:00
Jean Boussier
342caec27b Upgrade json gem to 1.8.2
Merge of the following upstream commits:

commit 6d3266dc0edbbdb1dae513f4f3be16198c432af3
Author: Jean Boussier <byroot@ruby-lang.org>
Date:   Mon Nov 2 15:51:58 2026 +0100

    Release 2.18.2

commit dcfbbcd36f240c18c3b89e5399105715f70162e3
Author: Jean Boussier <jean.boussier@gmail.com>
Date:   Wed Mar 25 11:45:17 2026 +0100

    Fix handling of unescaped control characters preceeded by a backslash

    GHSA-cjpr-jw2g-fw99

commit 6ec6e7baff137e67831a23090061e84bf028dbfc
Author: Jean Boussier <jean.boussier@gmail.com>
Date:   Tue Feb 3 08:35:11 2026 +0100

    Release 2.18.1

commit fff25c9f4b9c590a59ed4a2ec3c545121db844d3
Author: Jean Boussier <jean.boussier@gmail.com>
Date:   Mon Feb 2 19:44:02 2026 +0100

    fbuffer_append_str: assume string

commit 79b6e168ba02f6ac7a0dac378968b1c918af91db
Author: Jean Boussier <jean.boussier@gmail.com>
Date:   Mon Feb 2 22:06:44 2026 +0100

    Ensure `Generator::State` is kept on the stack

    Fix: https://github.com/ruby/json/issues/929

    When calling `cState_partial_generate` from `mHash_to_json` or other
    `to_json` funcs, `VState` becomes unreachable very quickly, hence the
    compiler may optimize it out of the stack, and make it invisible to
    the GC stack scanning. This is particularly liekly given how aggressively
    we inline.

    Repro:

    ```ruby
    require 'json'

    test_data = {
      "flag" => true,
      "data" => 10000.times.map { [1.0] },
      :flag => false,
    }

    10.times do
      test_data.to_json
    end
    ```

    But in practice the cause was just that the issued warning calls
    Hash#inspect on a big hash, which triggers GC.

    So it can be triggered even more reliably with:

    ```ruby
    require 'json'

    module JSON
      module Common
        def self.on_mixed_keys_hash(...)
          GC.start
        end
      end
    end

    test_data = {
      "flag" => true,
      "data" => 10000.times.map { [1.0] },
      :flag => false,
    }

    test_data.to_json
    ```

commit 062fcdd22818d226f0e4b49647eb2aabbfaf002b
Author: BurdetteLamar <burdettelamar@yahoo.com>
Date:   Thu Jan 22 22:08:15 2026 +0000

    Improve class JSON intro

commit 1b276c8623da0ff9bec62396d9a7172365bc1784
Author: Jean Boussier <jean.boussier@gmail.com>
Date:   Fri Jan 16 18:43:08 2026 +0100

    Extract json_fast_memcpy16 for readability

commit 643ee11fed6898b7e6f75f266daa6e4b2a07bb07
Author: Scott Myron <samyron@gmail.com>
Date:   Thu Jan 15 19:12:41 2026 -0600

    Use __builtin_memcpy, if available, to copy overlapping byte ranges in copy_remaining_bytes to avoid a branch to MEMCPY. Additionally use a space as padding byte instead of an 'X' so it can be represented diretly on AArch64 with a single instruction.

commit 15eb40dbdfb299813bbbb72f70e12d1614b26200
Author: Nobuyoshi Nakada <nobu@ruby-lang.org>
Date:   Wed Jan 14 21:28:37 2026 +0900

    Remove trailing spaces [ci skip]

commit 73818b80c8fe4394153002a60ecd285e8688b4cd
Author: Scott Myron <samyron@gmail.com>
Date:   Tue Jan 13 22:17:16 2026 -0600

    initialize search.chunk_end to silence a warning about it being potentially uninitialized

commit 86a5cce405102eca15d639e2dae2f539fb3e7c26
Author: Scott Myron <samyron@gmail.com>
Date:   Mon Jan 12 18:57:09 2026 -0600

    use a conditional to select SIMD implementation rather than pointer

commit a51317c94900ec5ea44ef0fd9327cca4d601bb5c
Author: Scott Myron <samyron@gmail.com>
Date:   Fri Jan 2 20:47:59 2026 -0600

    Directly write to the output buffer when converting UTF32 to UTF8.

commit 3e23e57568d29aa96f73d5f0c27b8c3676af4ee5
Merge: 976ba366 8ad744c5
Author: Nobuyoshi Nakada <nobu@ruby-lang.org>
Date:   Wed Dec 31 21:43:48 2025 +0900

    Merge pull request #921 from nobu/nonportable-code

    Fix non-portable code

commit 8ad744c53269330cc827bf4ae213991ae11712ce
Author: Nobuyoshi Nakada <nobu@ruby-lang.org>
Date:   Wed Dec 31 21:40:16 2025 +0900

    Fix non-portable code

    A plain `char` may be `signed` or `unsigned` depending on the
    implementation.  Also, bitwise ORing of `signed` values ​​is not
    guaranteed to be `signed`.  To ensure portability, should logical-OR
    each comparison, but casting to `signed char` is usually sufficient.

commit 976ba36629b2d20fa4de3da12ed405ab178deeba
Author: Scott Myron <samyron@gmail.com>
Date:   Tue Dec 30 21:32:03 2025 -0600

    Simplify unescape_unicode

    Co-Authored-By: Jean Boussier <jean.boussier@gmail.com>

commit d21d9362fa36bd1b34c53e28b04ba6e91115a3be
Author: Scott Myron <samyron@gmail.com>
Date:   Tue Dec 30 20:48:18 2025 -0600

    Keep track of the the number of additional backslashes to avoid an extra memchr searching the remaining characters when no more backslashes exist.

commit a5c160f37285cab4406017990a6a02e82e283055
Author: Jean Boussier <jean.boussier@gmail.com>
Date:   Mon Dec 22 09:16:09 2025 +0100

    Add missing documentation for `allow_control_characters` parsing option

commit 4808fee9affb801bc9bc63ea2d69d1cb28deda2a
Author: 久我山菜々 <nna@nna774.net>
Date:   Fri Dec 19 01:30:37 2025 +0900

    Update `fpconv_dtoa` definition to use `dest[32]`

commit 5793694ee6632245f345e3b923af5ebfd7db68a9
Author: Étienne Barrié <etienne.barrie@gmail.com>
Date:   Thu Dec 11 16:12:35 2025 +0100

    Revert "Skip test failing with JRuby in CI"

    This reverts commit b7e1734d9ca090e95b1923b05536f5fb6f437060.
2026-11-02 10:05:05 -08:00
Jean Boussier
c34c4a3cfb Speedup repeated calls to rb_gccct_clear_table
Profiling of `ruby --disable-all -e 1` shows between 5 and 10%
of the time is spent in `_platform_memset`, called from `rb_gccct_clear_table()`,
itself called from `clear_method_cache_by_id_in_class()`.

Which makes sense, during boot we define numerous methods, and after
each we have to invalidate the `gccct`, which is done by zeroing
1023 pointers, so roughtly 8kiB of memory.

By keeping track of whether the table was used since the last clear,
we can save a lot of useless memory writes.

This may also be beneficial when loading user defined code, as it's unlikely
the `gccct` would be dirtied between two `def`.

NB: profiling such a short lived process gives a lot of variance, but
with a very high sampling rate and multiple attempts, `clear_method_cache_by_id_in_class`
is always in the top 2 hotspots.

(cherry picked from commit 14eb0831527b8e1c5bbfef21ecaed14b21103315)
2026-10-30 16:46:28 -07:00
Thomas Klemm
9bebcb455f YJIT: Fall back for explicit keywords in IFUNC block calls
The IFUNC fast path passes raw keyword values to rb_vm_yield_with_cfunc
without packing them into a Hash. For example, an Enumerable iterator
that yields [], key: i loses the keyword name when called by each_slice.

Use VM dispatch for VM_CALL_KWARG, as for keyword splats, so argument
setup preserves the keywords.

(cherry picked from commit 54d098816777fd29e716746c2adcb3bbe4f88710)
2026-10-30 09:11:25 -07:00
Takashi Kokubun
95a0b20120 Add a missing dependency
https://github.com/ruby/ruby/pull/19622
2026-10-30 09:07:31 -07:00
Benoit Daloze
6ffc6fd196 Restore the previous SIGPROF action when stopping the sampler
The test sampler left its SIGPROF handler installed after stopping, as a
SIGPROF sent just before stopping might still be pending and would then
be delivered to the previous action, which terminates the process for
SIG_DFL.

Instead, make the sampler thread wait for each signal to be handled
before sending the next one, so that no SIGPROF is pending once it is
joined. Then save the previous action when starting and restore it when
stopping.

[Bug #22476]

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit ca1ec04e0ad69764185a5d7e65c73a5924ea9b48)
2026-10-30 09:06:44 -07:00
Benoit Daloze
bbbb0d0b67 Set the flags of the heap ep before cfp->ep points to it
The heap ep is meant to be fully initialized before cfp->ep and the
child ep point to it, as rb_profile_frames() might read it from a signal
handler. VM_ENV_FLAG_ESCAPED and VM_ENV_FLAG_WB_REQUIRED were however
set after the store to cfp->ep. This was harmless for rb_profile_frames()
since it does not check these flags, but move setting them before the
signal fence so cfp->ep never points to a heap ep which is not marked as
escaped.

[Bug #22476]

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 08a10375fe449867079d01d324ead1deb095b38b)
2026-10-30 09:06:44 -07:00
Benoit Daloze
085e192c4c Add signal fences to vm_make_env_each() for rb_profile_frames()
rb_profile_frames() can be called from a signal handler on the same
thread, so the order of the stores in vm_make_env_each() matters even
though no other thread observes them. The compiler could otherwise
reorder them: env_body is malloc'ed memory which is known not to alias,
and with strict aliasing the store to cfp->ep (a pointer) may be
reordered with the overwrite of the on-stack ep flags (a VALUE).

Like in vm_push_frame(), use atomic_signal_fence() to ensure the heap
ep is initialized before cfp->ep and the child ep point to it, and that
those are updated before the on-stack ep flags are overwritten.

With Apple clang 21 on arm64 and clang 20 on x86_64, the machine code of
vm.o is identical with and without these fences. With GCC 16 on arm64
and GCC 13 on x86_64, they prevent hoisting the update of the heap ep
flags above the store to cfp->ep. The stores to child_ep and the
on-stack ep stay in source order without the fences with all of these
compilers.

[Bug #22476]

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit db6aebc5c1bf304034e74145266bf47555be8fe7)
2026-10-30 09:06:44 -07:00
Benoit Daloze
5eb182603b Keep env chains walkable by rb_profile_frames() in vm_make_env_each()
vm_make_env_each() first escapes the parent frame, which overwrites the
flags of the parent's on-stack ep with the env object, and only then
updated the child frame to point to the parent's heap ep. If a signal
handler called rb_profile_frames() in between, as sampling profilers do,
rb_vm_frame_method_entry_unchecked() followed the child frame to the
overwritten on-stack ep, treated it as non-local and, if the parent had
no method entry (e.g. a class body), dereferenced VM_BLOCK_HANDLER_NONE
as the next ep and segfaulted.

Now the child frame is updated to point to the heap ep before the
on-stack ep of the parent is overwritten.

On x86_64 Linux with GCC 13, the new test segfaults in about half of
the runs without this change, and passed 30 out of 30 runs with it.
On macOS arm64 with Apple clang 21, it segfaulted in 4 out of 4 runs
without this change, and passed 5 out of 5 runs with it.

[Bug #22476]

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 9c217d461608f440a7c16fb00ce4f80fd472dab2)
2026-10-30 09:06:44 -07:00
John Hawthorn
ccb92217d3 Don't write str2's code range back in rb_str_buf_append
Since rb_enc_str_coderange(str2) is called first, str2_cr is never
UNKNOWN and rb_enc_cr_str_buf_cat returns it unchanged, so writing it
back to str2 is redundant. It is also an unsynchronized write to str2,
which may be a frozen string shared with other Ractors.

When str and str2 are the same string, the write-back also overwrote
the result's code range with the pre-append one, so `s << s` could
leave a valid string marked BROKEN.

[Bug #22451]

(cherry picked from commit b740f7d553e6ee4abdff8847760394b4e6ea389c)
2026-10-29 18:44:31 -07:00
Benoit Daloze
9ba7257f44 Add a signal fence in rb_ec_clear_vm_stack()
Since the fix for Bug #21441, ec->cfp is set to NULL before clearing
the VM stack so that thread_profile_frames() called from a signal
handler returns early instead of walking a stack that is no longer set
on the ec. But these are plain stores to distinct fields of the same
struct, so the compiler is free to reorder them.

With Apple clang 21 and GCC 16 on macOS arm64, and with clang 18 and
GCC 13 on Linux aarch64, both stores of rb_ec_set_vm_stack() were
combined into a single stp emitted before the store to ec->cfp
(vm_stack is at offset 0, vm_stack_size at 0x8 and cfp at 0x10):

    stp xzr, xzr, [x0]        ; ec->vm_stack = NULL, vm_stack_size = 0
    str xzr, [x0, #0x10]      ; ec->cfp = NULL
    ret

So a signal in between would see a non-NULL ec->cfp with a NULL
ec->vm_stack. Stopping there in lldb when a thread terminates and
calling rb_profile_frames() segfaults in thread_profile_frames(), as
end_cfp is computed from the NULL ec->vm_stack and the loop walks past
the control frames. With this change, ec->cfp is already NULL at that
point and rb_profile_frames() returns 0.

Like in vm_push_frame(), use atomic_signal_fence() to keep the store to
ec->cfp first. With it, all these compilers emit the store to ec->cfp no
later than the stores clearing the stack. The rest of vm.o is unchanged
with Apple clang 21, GCC 16 and GCC 13 on aarch64. Clang 21 and 18:

    stp xzr, xzr, [x0, #0x8]  ; ec->vm_stack_size = 0, cfp = NULL
    str xzr, [x0]             ; ec->vm_stack = NULL
    ret

GCC 16 and 13:

    str xzr, [x0, #0x10]      ; ec->cfp = NULL
    stp xzr, xzr, [x0]        ; ec->vm_stack = NULL, vm_stack_size = 0
    ret

With GCC 13 on x86_64, the store to ec->cfp is already emitted first
without the fence, and vm.o is identical with and without it:

    endbr64
    movq $0x0, 0x10(%rdi)     ; ec->cfp = NULL
    movq $0x0, (%rdi)         ; ec->vm_stack = NULL
    movq $0x0, 0x8(%rdi)      ; ec->vm_stack_size = 0
    ret

[Bug #22477]

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 12:35:42 -07:00
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 fa8df43bd1ba83f41d37d9aa3facc8eb2858fc23)
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 7d1be0888a3b83be8508f437d0628601f21c1275)
2026-10-22 20:55:35 -07:00
Takashi Kokubun
3e93b23afc v4.0.9 v4.0.9 2026-10-22 19:36:04 -07:00
Hiroshi SHIBATA
21a7f5894b Keep the program size error from OP_END with subexp calls
With a subexp call, the result of unset_addr_list_fix overwrote the error, so a pattern over the limit compiled without raising RegexpError.

Follow-up to cf35145fc46.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 19:34:29 -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
Takashi Kokubun
60c3d71bfb v4.0.8 v4.0.8 2026-10-21 23:22:37 -07:00
Takashi Kokubun
d8cd2c2e89 [ruby/prism] Bump the remaining version references to v1.8.2
(https://github.com/ruby/prism/pull/4301)

https://github.com/ruby/prism/commit/6870939137
2026-10-22 06:10:37 +00:00
Takashi Kokubun
db71375c5d [ruby/prism] Bump to v1.8.2
(https://github.com/ruby/prism/pull/4300)

https://github.com/ruby/prism/commit/d819a226af
2026-10-22 06:10:37 +00:00
Kevin Newton
62752fb9e3 [ruby/prism] Fix assume warning
That attribute is C++23, not C23

https://github.com/ruby/prism/commit/656e8f72a4
2026-10-22 06:10:37 +00:00
Kevin Newton
ec4c25ab9d [ruby/prism] Assume for the GCC bounds checks
https://github.com/ruby/prism/commit/fe4054ddf2
2026-10-22 06:10:36 +00: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
Takashi Kokubun
5761520495 Sync prism from its ruby-4.0 branch
ruby/prism's ruby-4.0 branch triggers sync_default_gems.yml on ruby_4_0
since ruby/prism#4005, but the sync only fetched prism's main branch.
The ruby-4.0 commits were never fetched, so every sync found no commits
to pick and finished successfully without syncing anything.
2026-10-21 23:03:43 -07:00
Earlopain
479bab3e03 [ruby/prism] Remove a warning from discarded-qualifiers
I've been seeing this for a while now:
```
../../../../ext/prism/../../src/memchr.c: In function ‘pm_memchr’:
../../../../ext/prism/../../src/memchr.c:35:16: warning: return discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
   35 |         return memchr(memory, character, number);
      |                ^~~~~~
```

https://github.com/ruby/prism/commit/a3b1f10dbc
(cherry picked from commit ce398d7545f04bd8b81a908f5ae905ba290c13b9)
2026-10-21 22:08:54 -07: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 1bf91309438d071359aa521328074cc6ec293299)
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 28f2eae4cf7681be36c3011a423154e45d21721c)
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
Étienne Barrié
6776cc5a38 Remove discard qualifiers warning with C23 strchr
C23 has qualifier-preserving standard library functions, so calling
strchr with a `const char *` will return a `const char *`. We can change
the type of the local variables because we don't mutate the strings.

(cherry picked from commit 42b3cdc51a30b4b155f80fb7808e2b0e634dddc1)
2026-10-15 12:37:44 -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 44f7c6b00d3263c9c81af58e77eeb79eaec7bd0e)
2026-10-14 17:15:16 -07:00
Hiroshi SHIBATA
f4f2dfb3c7 Defer unloading box-local extension DLLs on Windows
box_entry_free could run before other T_DATA objects are finalized in
rb_objspace_call_finalizer, and FreeLibrary there unmapped the
rb_data_type_t and dfree functions that later finalization still
dereferences, crashing any boxed process that leaves extension objects
alive at exit. Keep the copied DLLs loaded until ruby_vm_destruct, then
unload and delete them.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-10-13 16:13:54 -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 d81a11d4e61f67b6fb0aaa44aaa7ead4022148dd
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
Hiroshi SHIBATA
18e7b33cf4 [Bug #22284] Let NUM2INT handle Fixnum arity of curry
NUM2INT already takes the Fixnum path by itself.
2026-10-09 16:07:20 -07:00
YO4
1328590e45 [Bug #22284] Fix curry with a non-Fixnum arity on LLP64
FIX2INT does not call to_int where int and long have the same size, so `method(:puts).curry(1r)` waited for a garbage number of arguments on Windows. Convert the arity once and keep the result as a Fixnum, which also limits the to_int call to one.
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 SHIBATA
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 SHIBATA
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 SHIBATA
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