Skip to content

[Bug #21567] Fix crash when $LOADED_FEATURES is modified during require#14487

Merged
peterzhu2118 merged 1 commit intoruby:masterfrom
peterzhu2118:require-loaded-features-modify-crash
Sep 10, 2025
Merged

[Bug #21567] Fix crash when $LOADED_FEATURES is modified during require#14487
peterzhu2118 merged 1 commit intoruby:masterfrom
peterzhu2118:require-loaded-features-modify-crash

Conversation

@peterzhu2118
Copy link
Member

@peterzhu2118 peterzhu2118 commented Sep 9, 2025

When we require an object that is not a string, it will attempt to convert it to a string by calling to_str on it. If we modify the $LOADED_FEATURES array while it calls to_str, Ruby can crash because it can end up inserting the string in the wrong index in the array.

For example, the following script crashes:

require "tempfile"

class MyString
  def initialize(path)
    @path = path
  end

  def to_str
    $LOADED_FEATURES.clear
    @path
  end

  def to_path = @path
end

def create_ruby_file = Tempfile.create(["test", ".rb"]).path

require MyString.new(create_ruby_file)
$LOADED_FEATURES.unshift(create_ruby_file)
$LOADED_FEATURES << MyString.new(create_ruby_file)
require create_ruby_file

Crash log:

test.rb:21: [BUG] Segmentation fault at 0x0000000000000004
ruby 3.5.0dev (2025-09-09T09:29:35Z master ce94add7fb) +PRISM [arm64-darwin24]

-- Crash Report log information --------------------------------------------
  See Crash Report log file in one of the following locations:
    * ~/Library/Logs/DiagnosticReports
    * /Library/Logs/DiagnosticReports
  for more details.
Don't forget to include the above Crash Report log file in bug reports.

-- Control frame information -----------------------------------------------
c:0003 p:---- s:0011 e:000010 CFUNC  :require
c:0002 p:0076 s:0006 e:000005 EVAL   test.rb:21 [FINISH]
c:0001 p:0000 s:0003 E:0001b0 DUMMY  [FINISH]

-- Ruby level backtrace information ----------------------------------------
test.rb:21:in '<main>'
test.rb:21:in 'require'

-- Threading information ---------------------------------------------------
Total ractor count: 1
Ruby thread count for this ractor: 1

-- Machine register context ------------------------------------------------
  x0: 0x0000000000000004  x1: 0x000000000000c800  x2: 0x0000000000000000
  x3: 0x0000000000000000  x4: 0x0000000000000205  x5: 0x0000000000000000
  x6: 0x0000000000000000  x7: 0x0000000000000001 x18: 0x0000000000000000
x19: 0x0000000209dfc0b0 x20: 0x0000000209dfc018 x21: 0x000000016ee8ab58
x22: 0x0fffffff0009d71d x23: 0x0000000209dfc018 x24: 0x0000000209dfc150
x25: 0x000000016ee8acc0 x26: 0x0000000000000000 x27: 0x0000000000000000
x28: 0x0000000000000000  lr: 0x0000000101244140  fp: 0x000000016ee887f0
  sp: 0x000000016ee887d0

-- C level backtrace information -------------------------------------------
miniruby(rb_print_backtrace+0x24) [0x101317b08] vm_dump.c:843
miniruby(rb_print_backtrace) (null):0
miniruby(rb_vm_bugreport+0x26c) [0x101317d94] vm_dump.c:1175
miniruby(rb_bug_for_fatal_signal+0xa4) [0x10105ddac] error.c:1130
miniruby(sig_do_nothing+0x0) [0x1012278c0] signal.c:948
miniruby(sigsegv) (null):0
/usr/lib/system/libsystem_platform.dylib(_sigtramp+0x38) [0x19c1216a4]
miniruby(rb_str_new_frozen+0x1c) [0x101244140] string.c:1495
miniruby(rb_check_realpath_internal+0x68) [0x101077804] file.c:4679
miniruby(rb_check_realpath+0x2c) [0x101077aa4] file.c:4765
miniruby(get_loaded_features_index+0x37c) [0x1010f9c94] load.c:467
miniruby(rb_feature_p+0xd0) [0x1010f8174] load.c:582
miniruby(search_required+0xac) [0x1010f6ad4] load.c:1193
miniruby(require_internal+0x274) [0x1010f7518] load.c:1424
miniruby(rb_require_string_internal+0x94) [0x1010f6830] load.c:1571
miniruby(rb_require_string+0x58) [0x1010f66e8] load.c:1557
miniruby(rb_f_require+0x1c) [0x1010f6684] load.c:1150
miniruby(ractor_safe_call_cfunc_1+0x38) [0x101306c28] vm_insnhelper.c:3696
miniruby(vm_call_cfunc_with_frame_+0x250) [0x1012f857c] vm_insnhelper.c:3873
miniruby(vm_call_cfunc_with_frame+0x6c) [0x1012f8834] vm_insnhelper.c:3919
miniruby(vm_sendish+0x1a8) [0x1012c990c] vm_insnhelper.c:6087
miniruby(vm_exec_core+0x4050) [0x1012cfb48] insns.def:900
miniruby(vm_exec_loop+0x80) [0x1012e5448] vm.c:2666
miniruby(rb_vm_exec+0x134) [0x1012c9b40] vm.c:2645
miniruby(rb_iseq_eval_main+0x34) [0x1012e5628] vm.c:2919
miniruby(rb_ec_exec_node+0xe4) [0x10106d094] eval.c:282
miniruby(ruby_run_node+0x94) [0x10106cf64] eval.c:320
miniruby(rb_main+0x40) [0x100f7499c] main.c:42
miniruby(main+0x60) [0x100f74928] main.c:62

[Bug #21567]

When we require an object that is not a string, it will attempt to convert
it to a string by calling to_str on it. If we modify the $LOADED_FEATURES
array while it calls to_str, Ruby can crash because it can end up inserting
the string in the wrong index in the array.

For example, the following script crashes:

    require "tempfile"

    class MyString
      def initialize(path)
        @path = path
      end

      def to_str
        $LOADED_FEATURES.clear
        @path
      end

      def to_path = @path
    end

    def create_ruby_file = Tempfile.create(["test", ".rb"]).path

    require MyString.new(create_ruby_file)
    $LOADED_FEATURES.unshift(create_ruby_file)
    $LOADED_FEATURES << MyString.new(create_ruby_file)
    require create_ruby_file

Crash log:

    test.rb:21: [BUG] Segmentation fault at 0x0000000000000004
    ruby 3.5.0dev (2025-09-09T09:29:35Z master ce94add) +PRISM [arm64-darwin24]

    -- Crash Report log information --------------------------------------------
      See Crash Report log file in one of the following locations:
        * ~/Library/Logs/DiagnosticReports
        * /Library/Logs/DiagnosticReports
      for more details.
    Don't forget to include the above Crash Report log file in bug reports.

    -- Control frame information -----------------------------------------------
    c:0003 p:---- s:0011 e:000010 CFUNC  :require
    c:0002 p:0076 s:0006 e:000005 EVAL   test.rb:21 [FINISH]
    c:0001 p:0000 s:0003 E:0001b0 DUMMY  [FINISH]

    -- Ruby level backtrace information ----------------------------------------
    test.rb:21:in '<main>'
    test.rb:21:in 'require'

    -- Threading information ---------------------------------------------------
    Total ractor count: 1
    Ruby thread count for this ractor: 1

    -- Machine register context ------------------------------------------------
      x0: 0x0000000000000004  x1: 0x000000000000c800  x2: 0x0000000000000000
      x3: 0x0000000000000000  x4: 0x0000000000000205  x5: 0x0000000000000000
      x6: 0x0000000000000000  x7: 0x0000000000000001 x18: 0x0000000000000000
    x19: 0x0000000209dfc0b0 x20: 0x0000000209dfc018 x21: 0x000000016ee8ab58
    x22: 0x0fffffff0009d71d x23: 0x0000000209dfc018 x24: 0x0000000209dfc150
    x25: 0x000000016ee8acc0 x26: 0x0000000000000000 x27: 0x0000000000000000
    x28: 0x0000000000000000  lr: 0x0000000101244140  fp: 0x000000016ee887f0
      sp: 0x000000016ee887d0

    -- C level backtrace information -------------------------------------------
    miniruby(rb_print_backtrace+0x24) [0x101317b08] vm_dump.c:843
    miniruby(rb_print_backtrace) (null):0
    miniruby(rb_vm_bugreport+0x26c) [0x101317d94] vm_dump.c:1175
    miniruby(rb_bug_for_fatal_signal+0xa4) [0x10105ddac] error.c:1130
    miniruby(sig_do_nothing+0x0) [0x1012278c0] signal.c:948
    miniruby(sigsegv) (null):0
    /usr/lib/system/libsystem_platform.dylib(_sigtramp+0x38) [0x19c1216a4]
    miniruby(rb_str_new_frozen+0x1c) [0x101244140] string.c:1495
    miniruby(rb_check_realpath_internal+0x68) [0x101077804] file.c:4679
    miniruby(rb_check_realpath+0x2c) [0x101077aa4] file.c:4765
    miniruby(get_loaded_features_index+0x37c) [0x1010f9c94] load.c:467
    miniruby(rb_feature_p+0xd0) [0x1010f8174] load.c:582
    miniruby(search_required+0xac) [0x1010f6ad4] load.c:1193
    miniruby(require_internal+0x274) [0x1010f7518] load.c:1424
    miniruby(rb_require_string_internal+0x94) [0x1010f6830] load.c:1571
    miniruby(rb_require_string+0x58) [0x1010f66e8] load.c:1557
    miniruby(rb_f_require+0x1c) [0x1010f6684] load.c:1150
    miniruby(ractor_safe_call_cfunc_1+0x38) [0x101306c28] vm_insnhelper.c:3696
    miniruby(vm_call_cfunc_with_frame_+0x250) [0x1012f857c] vm_insnhelper.c:3873
    miniruby(vm_call_cfunc_with_frame+0x6c) [0x1012f8834] vm_insnhelper.c:3919
    miniruby(vm_sendish+0x1a8) [0x1012c990c] vm_insnhelper.c:6087
    miniruby(vm_exec_core+0x4050) [0x1012cfb48] insns.def:900
    miniruby(vm_exec_loop+0x80) [0x1012e5448] vm.c:2666
    miniruby(rb_vm_exec+0x134) [0x1012c9b40] vm.c:2645
    miniruby(rb_iseq_eval_main+0x34) [0x1012e5628] vm.c:2919
    miniruby(rb_ec_exec_node+0xe4) [0x10106d094] eval.c:282
    miniruby(ruby_run_node+0x94) [0x10106cf64] eval.c:320
    miniruby(rb_main+0x40) [0x100f7499c] main.c:42
    miniruby(main+0x60) [0x100f74928] main.c:62
@peterzhu2118 peterzhu2118 changed the title Fix crash when $LOADED_FEATURES is modified during require [Bug #21567] Fix crash when $LOADED_FEATURES is modified during require Sep 9, 2025
@peterzhu2118 peterzhu2118 merged commit 928fea3 into ruby:master Sep 10, 2025
84 checks passed
@peterzhu2118 peterzhu2118 deleted the require-loaded-features-modify-crash branch September 10, 2025 09:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant