From 1de15815a8b85f02ba73d9f7c30322530b471b5f Mon Sep 17 00:00:00 2001 From: yoshoku Date: Thu, 4 Dec 2025 09:04:11 +0900 Subject: [PATCH 1/5] [ruby/rubygems] Fix native extension loading in newgem template for RHEL-based systems Add fallback to `require` when `require_relative` fails to load native extensions. This addresses an issue on RHEL-based Linux distributions where Ruby scripts and built native extension shared libraries are installed in separate directories. https://github.com/ruby/rubygems/commit/68599bd107 --- lib/bundler/templates/newgem/lib/newgem.rb.tt | 2 +- spec/bundler/commands/newgem_spec.rb | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/bundler/templates/newgem/lib/newgem.rb.tt b/lib/bundler/templates/newgem/lib/newgem.rb.tt index caf6e32f4abf30..3aedee0d25e92b 100644 --- a/lib/bundler/templates/newgem/lib/newgem.rb.tt +++ b/lib/bundler/templates/newgem/lib/newgem.rb.tt @@ -2,7 +2,7 @@ require_relative "<%= File.basename(config[:namespaced_path]) %>/version" <%- if config[:ext] -%> -require_relative "<%= File.basename(config[:namespaced_path]) %>/<%= config[:underscored_name] %>" +require "<%= File.basename(config[:namespaced_path]) %>/<%= config[:underscored_name] %>" <%- end -%> <%- config[:constant_array].each_with_index do |c, i| -%> diff --git a/spec/bundler/commands/newgem_spec.rb b/spec/bundler/commands/newgem_spec.rb index 1d158726bed6a0..06c226f9e56a17 100644 --- a/spec/bundler/commands/newgem_spec.rb +++ b/spec/bundler/commands/newgem_spec.rb @@ -1681,6 +1681,13 @@ def create_temporary_dir(dir) expect(bundled_app("#{gem_name}/ext/#{gem_name}/#{gem_name}.c")).to exist end + it "generates native extension loading code" do + expect(bundled_app("#{gem_name}/lib/#{gem_name}.rb").read).to include(<<~RUBY) + require_relative "test_gem/version" + require "#{gem_name}/#{gem_name}" + RUBY + end + it "includes rake-compiler, but no Rust related changes" do expect(bundled_app("#{gem_name}/Gemfile").read).to include('gem "rake-compiler"') From ced333677fb97f2fc720dfb86213ea416b3ebcf4 Mon Sep 17 00:00:00 2001 From: Koichi Sasada Date: Mon, 8 Dec 2025 17:47:07 +0900 Subject: [PATCH 2/5] fix SEGV on clang-16/18 Maybe because of TLS/coroutine problem, CI fails on clang-16/18 ``` 1) Failure: TestTimeout#test_ractor [/tmp/ruby/src/trunk_clang_18/test/test_timeout.rb:288]: pid 307341 killed by SIGSEGV (signal 11) (core dumped) | /tmp/ruby/src/trunk_clang_18/lib/timeout.rb:98: [BUG] Segmentation fault at 0x0000000000000030 | ruby 4.0.0dev (2025-12-07T16:51:02Z master 4f900c35bc) +PRISM [x86_64-linux] | | -- Control frame information ----------------------------------------------- | c:0006 p:---- s:0026 e:000025 l:y b:---- CFUNC :sleep | c:0005 p:---- s:0023 e:000022 l:y b:---- CFUNC :wait | c:0004 p:0020 s:0017 e:000016 l:n b:---- BLOCK /tmp/ruby/src/trunk_clang_18/lib/timeout.rb:98 [FINISH] | c:0003 p:---- s:0014 e:000013 l:y b:---- CFUNC :synchronize | c:0002 p:0072 s:0010 e:000009 l:n b:---- BLOCK /tmp/ruby/src/trunk_clang_18/lib/timeout.rb:96 [FINISH] | c:0001 p:---- s:0003 e:000002 l:y b:---- DUMMY [FINISH] | | -- Ruby level backtrace information ---------------------------------------- | /tmp/ruby/src/trunk_clang_18/lib/timeout.rb:96:in 'block in create_timeout_thread' | /tmp/ruby/src/trunk_clang_18/lib/timeout.rb:96:in 'synchronize' | /tmp/ruby/src/trunk_clang_18/lib/timeout.rb:98:in 'block (2 levels) in create_timeout_thread' | /tmp/ruby/src/trunk_clang_18/lib/timeout.rb:98:in 'wait' | /tmp/ruby/src/trunk_clang_18/lib/timeout.rb:98:in 'sleep' | | -- Threading information --------------------------------------------------- | Total ractor count: 3 | Ruby thread count for this ractor: 2 | | -- Machine register context ------------------------------------------------ | RIP: 0x0000602b1e08a5b5 RBP: 0x000071c65facd130 RSP: 0x000071c6258842e0 | RAX: 0x0000000000000000 RBX: 0x000000006935f7c4 RCX: 0x0000000000000000 | RDX: 0x0000602b1e520c20 RDI: 0x000071c620012480 RSI: 0x000071c620012480 | R8: 0x0000000000000000 R9: 0x0000000000000000 R10: 0x0000000000000000 | R11: 0x0000000000000000 R12: 0x000071c65fa2f640 R13: 0x000071c65fb66e48 | R14: 0x0000000000000000 R15: 0xfdccaa3270000002 EFL: 0x0000000000010202 | | -- C level backtrace information ------------------------------------------- | /tmp/ruby/build/trunk_clang_18/ruby(rb_print_backtrace+0x14) [0x602b1e31a6ea] /tmp/ruby/src/trunk_clang_18/vm_dump.c:1105 | /tmp/ruby/build/trunk_clang_18/ruby(rb_vm_bugreport) /tmp/ruby/src/trunk_clang_18/vm_dump.c:1450 | /tmp/ruby/build/trunk_clang_18/ruby(rb_bug_for_fatal_signal+0x15c) [0x602b1e2d960c] /tmp/ruby/src/trunk_clang_18/error.c:1131 | /tmp/ruby/build/trunk_clang_18/ruby(sigsegv+0x5a) [0x602b1e05528a] /tmp/ruby/src/trunk_clang_18/signal.c:948 | /lib/x86_64-linux-gnu/libc.so.6(0x71c65fd46320) [0x71c65fd46320] | /tmp/ruby/build/trunk_clang_18/ruby(vm_check_ints_blocking+0x0) [0x602b1e08a5b5] /tmp/ruby/src/trunk_clang_18/vm_core.h:2097 | /tmp/ruby/build/trunk_clang_18/ruby(rb_current_execution_context) /tmp/ruby/src/trunk_clang_18/thread_sync.c:617 | /tmp/ruby/build/trunk_clang_18/ruby(rb_mutex_sleep) /tmp/ruby/src/trunk_clang_18/thread_sync.c:617 ``` This patch introduces workaround by acquiring EC before swithcing coroutine. --- thread_sync.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/thread_sync.c b/thread_sync.c index 6cc23f7d87b32d..8967e24e341bad 100644 --- a/thread_sync.c +++ b/thread_sync.c @@ -599,6 +599,8 @@ mutex_sleep_begin(VALUE _arguments) VALUE rb_mutex_sleep(VALUE self, VALUE timeout) { + rb_execution_context_t *ec = GET_EC(); + if (!NIL_P(timeout)) { // Validate the argument: rb_time_interval(timeout); @@ -614,7 +616,7 @@ rb_mutex_sleep(VALUE self, VALUE timeout) VALUE woken = rb_ensure(mutex_sleep_begin, (VALUE)&arguments, mutex_lock_uninterruptible, self); - RUBY_VM_CHECK_INTS_BLOCKING(GET_EC()); + RUBY_VM_CHECK_INTS_BLOCKING(ec); if (!woken) return Qnil; time_t end = time(0) - beg; return TIMET2NUM(end); From 159430e8b6a2c6b5a5a9c926676082fcaef7535e Mon Sep 17 00:00:00 2001 From: Koichi Sasada Date: Mon, 8 Dec 2025 17:59:57 +0900 Subject: [PATCH 3/5] ignore Thread creation error on resource limited environment. ``` stderr output is not empty bootstraptest.test_ractor.rb_2446_1412.rb:23:in 'Ractor.new': can't create Thread: Cannot allocate memory (ThreadError) ``` --- bootstraptest/test_ractor.rb | 56 +++++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 23 deletions(-) diff --git a/bootstraptest/test_ractor.rb b/bootstraptest/test_ractor.rb index af739ba4bc7fd7..9042f945f7f4f4 100644 --- a/bootstraptest/test_ractor.rb +++ b/bootstraptest/test_ractor.rb @@ -2444,35 +2444,45 @@ def call_test(obj) # When creating bmethods in Ractors, they should only be usable from their # defining ractor, even if it is GC'd assert_equal 'ok', <<~'RUBY' -CLASSES = 1000.times.map { Class.new }.freeze -# This would be better to run in parallel, but there's a bug with lambda -# creation and YJIT causing crashes in dev mode -ractors = CLASSES.map do |klass| - Ractor.new(klass) do |klass| - Ractor.receive - klass.define_method(:foo) {} +begin + CLASSES = 1000.times.map { Class.new }.freeze + + # This would be better to run in parallel, but there's a bug with lambda + # creation and YJIT causing crashes in dev mode + ractors = CLASSES.map do |klass| + Ractor.new(klass) do |klass| + Ractor.receive + klass.define_method(:foo) {} + end end -end -ractors.each do |ractor| - ractor << nil - ractor.join -end + ractors.each do |ractor| + ractor << nil + ractor.join + end -ractors.clear -GC.start + ractors.clear + GC.start -any = 1000.times.map do - Ractor.new do - CLASSES.any? do |klass| - begin - klass.new.foo - true - rescue RuntimeError - false + any = 1000.times.map do + Ractor.new do + CLASSES.any? do |klass| + begin + klass.new.foo + true + rescue RuntimeError + false + end end end + end.map(&:value).none? && :ok +rescue ThreadError => e + # ignore limited memory machine + if /can\'t create Thread/ =~ e.message + :ok + else + raise end -end.map(&:value).none? && :ok +end RUBY From 27d60e29845cce3159856ef25d8be533ef402e3c Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Mon, 8 Dec 2025 17:59:21 +0900 Subject: [PATCH 4/5] [ruby/resolv] Fix warnings on cygwin https://github.com/ruby/resolv/commit/075e76f997 --- ext/win32/resolv/resolv.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/ext/win32/resolv/resolv.c b/ext/win32/resolv/resolv.c index 066856dd984df0..4c56c61d7de579 100644 --- a/ext/win32/resolv/resolv.c +++ b/ext/win32/resolv/resolv.c @@ -21,7 +21,7 @@ w32error_make_error(DWORD e) FORMAT_MESSAGE_IGNORE_INSERTS, &source, e, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), buffer, sizeof(buffer), NULL)) { - snprintf(buffer, sizeof(buffer), "Unknown Error %u", (unsigned long)e); + snprintf(buffer, sizeof(buffer), "Unknown Error %lu", (unsigned long)e); } p = buffer; while ((p = strpbrk(p, "\r\n")) != NULL) { @@ -149,7 +149,6 @@ reg_each_key(VALUE self) { WCHAR wname[256]; HKEY hkey = DATA_PTR(self); - rb_encoding *utf8 = rb_utf8_encoding(); VALUE k = TypedData_Wrap_Struct(CLASS_OF(self), &hkey_type, NULL); DWORD i, e, n; for (i = 0; n = numberof(wname), (e = RegEnumKeyExW(hkey, i, wname, &n, NULL, NULL, NULL, NULL)) == ERROR_SUCCESS; i++) { @@ -187,7 +186,7 @@ reg_value(VALUE self, VALUE name) case REG_DWORD: case REG_DWORD_BIG_ENDIAN: { DWORD d; - if (size != sizeof(d)) rb_raise(rb_eRuntimeError, "invalid size returned: %lu", size); + if (size != sizeof(d)) rb_raise(rb_eRuntimeError, "invalid size returned: %lu", (unsigned long)size); w32error_check(RegGetValueW(hkey, NULL, wname, RRF_RT_REG_DWORD, &type, &d, &size)); if (type == REG_DWORD_BIG_ENDIAN) d = swap_dw(d); return ULONG2NUM(d); @@ -195,12 +194,12 @@ reg_value(VALUE self, VALUE name) case REG_QWORD: { QWORD q; - if (size != sizeof(q)) rb_raise(rb_eRuntimeError, "invalid size returned: %lu", size); + if (size != sizeof(q)) rb_raise(rb_eRuntimeError, "invalid size returned: %lu", (unsigned long)size); w32error_check(RegGetValueW(hkey, NULL, wname, RRF_RT_REG_QWORD, &type, &q, &size)); return ULL2NUM(q); } case REG_SZ: case REG_MULTI_SZ: case REG_EXPAND_SZ: - if (size % sizeof(WCHAR)) rb_raise(rb_eRuntimeError, "invalid size returned: %lu", size); + if (size % sizeof(WCHAR)) rb_raise(rb_eRuntimeError, "invalid size returned: %lu", (unsigned long)size); buffer = ALLOCV_N(char, value_buffer, size); break; default: @@ -211,7 +210,6 @@ reg_value(VALUE self, VALUE name) switch (type) { case REG_MULTI_SZ: { const WCHAR *w = (WCHAR *)buffer; - rb_encoding *utf8 = rb_utf8_encoding(); result = rb_ary_new(); size /= sizeof(WCHAR); size -= 1; From 66b2cc3dabbedca1331507f8b4f2b274d0a47570 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Mon, 8 Dec 2025 18:29:06 +0900 Subject: [PATCH 5/5] [ruby/resolv] Check the second RegGetValue type https://github.com/ruby/resolv/commit/3678de9e30 --- ext/win32/resolv/resolv.c | 49 +++++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/ext/win32/resolv/resolv.c b/ext/win32/resolv/resolv.c index 4c56c61d7de579..b2d377df9fffc6 100644 --- a/ext/win32/resolv/resolv.c +++ b/ext/win32/resolv/resolv.c @@ -182,6 +182,15 @@ reg_value(VALUE self, VALUE name) e = RegGetValueW(hkey, NULL, wname, RRF_RT_ANY, &type, NULL, &size); if (e == ERROR_FILE_NOT_FOUND) return Qnil; w32error_check(e); +# define get_value_2nd(data, dsize) do { \ + DWORD type2 = type; \ + w32error_check(RegGetValueW(hkey, NULL, wname, RRF_RT_ANY, &type2, data, dsize)); \ + if (type != type2) { \ + rb_raise(rb_eRuntimeError, "registry value type changed %lu -> %lu", \ + (unsigned long)type, (unsigned long)type2); \ + } \ + } while (0) + switch (type) { case REG_DWORD: case REG_DWORD_BIG_ENDIAN: { @@ -201,30 +210,30 @@ reg_value(VALUE self, VALUE name) case REG_SZ: case REG_MULTI_SZ: case REG_EXPAND_SZ: if (size % sizeof(WCHAR)) rb_raise(rb_eRuntimeError, "invalid size returned: %lu", (unsigned long)size); buffer = ALLOCV_N(char, value_buffer, size); + get_value_2nd(buffer, &size); + if (type == REG_MULTI_SZ) { + const WCHAR *w = (WCHAR *)buffer; + result = rb_ary_new(); + size /= sizeof(WCHAR); + size -= 1; + for (size_t i = 0; i < size; ++i) { + int n = lstrlenW(w+i); + rb_ary_push(result, wchar_to_utf8(w+i, n)); + i += n; + } + } + else { + result = wchar_to_utf8((WCHAR *)buffer, lstrlenW((WCHAR *)buffer)); + } + ALLOCV_END(value_buffer); break; default: result = rb_str_new(0, size); - buffer = RSTRING_PTR(result); - } - w32error_check(RegGetValueW(hkey, NULL, wname, RRF_RT_ANY, &type, buffer, &size)); - switch (type) { - case REG_MULTI_SZ: { - const WCHAR *w = (WCHAR *)buffer; - result = rb_ary_new(); - size /= sizeof(WCHAR); - size -= 1; - for (size_t i = 0; i < size; ++i) { - int n = lstrlenW(w+i); - rb_ary_push(result, wchar_to_utf8(w+i, n)); - i += n; - } - return result; - } - case REG_SZ: case REG_EXPAND_SZ: - return wchar_to_utf8((WCHAR *)buffer, lstrlenW((WCHAR *)buffer)); - default: - return result; + get_value_2nd(RSTRING_PTR(result), &size); + rb_str_set_len(result, size); + break; } + return result; } void