Skip to content
  • NARUSE, Yui's avatar
    0090cb82
    merge revision(s) df6b72b8: [Backport #19348] · 0090cb82
    NARUSE, Yui authored
    	Avoid checking interrupt when loading iseq
    	MIME-Version: 1.0
    	Content-Type: text/plain; charset=UTF-8
    	Content-Transfer-Encoding: 8bit
    
    	The interrupt check will unintentionally release the VM lock when loading an iseq.
    	And this will cause issues with the `debug` gem's
    	[`ObjectSpace.each_iseq` method](https://github.com/ruby/debug/blob/0fcfc28acae33ec1c08068fb7c33703cfa681fa7/ext/debug/iseq_collector.c#L61-L67),
    	which wraps iseqs with a wrapper and exposes their internal states when they're actually not ready to be used.
    
    	And when that happens, errors like this would occur and kill the `debug` gem's thread:
    
    	```
    	 DEBUGGER: ReaderThreadError: uninitialized InstructionSequence
    	┃ DEBUGGER: Disconnected.
    	┃ ["/opt/rubies/ruby-3.2.0/lib/ruby/gems/3.2.0/gems/debug-1.7.1/lib/debug/breakpoint.rb:247:in `absolute_path'",
    	┃  "/opt/rubies/ruby-3.2.0/lib/ruby/gems/3.2.0/gems/debug-1.7.1/lib/debug/breakpoint.rb:247:in `block in iterate_iseq'",
    	┃  "/opt/rubies/ruby-3.2.0/lib/ruby/gems/3.2.0/gems/debug-1.7.1/lib/debug/breakpoint.rb:246:in `each_iseq'",
    	...
    	```
    
    	A way to reproduce the issue is to satisfy these conditions at the same time:
    
    	1. `debug` gem calling `ObjectSpace.each_iseq` (e.g. [activating a `LineBreakpoint`](https://github.com/ruby/debug/blob/0fcfc28acae33ec1c08068fb7c33703cfa681fa7/lib/debug/breakpoint.rb#L246)
    
    ).
    	2. A large amount of iseq being loaded from another thread (possibly through the `bootsnap` gem).
    	3. 1 and 2 iterating through the same iseq(s) at the same time.
    
    	Because this issue requires external dependencies and a rather complicated timing setup to reproduce, I wasn't able to write a test case for it.
    	But here's some pseudo code to help reproduce it:
    
    	```rb
    	require "debug/session"
    
    	Thread.new do
    	  100.times do
    	    ObjectSpace.each_iseq do |iseq|
    	      iseq.absolute_path
    	    end
    	  end
    	end
    
    	sleep 0.1
    
    	load_a_bunch_of_iseq
    	possibly_through_bootsnap
    	```
    
    	[Bug #19348]
    
    Co-authored-by: default avatarPeter Zhu <peter@peterzhu.ca>
    	---
    	 compile.c       |  2 +-
    	 vm_core.h       |  1 +
    	 vm_insnhelper.c | 11 +++++++++++
    	 3 files changed, 13 insertions(+), 1 deletion(-)
    0090cb82
    merge revision(s) df6b72b8: [Backport #19348]
    NARUSE, Yui authored
    	Avoid checking interrupt when loading iseq
    	MIME-Version: 1.0
    	Content-Type: text/plain; charset=UTF-8
    	Content-Transfer-Encoding: 8bit
    
    	The interrupt check will unintentionally release the VM lock when loading an iseq.
    	And this will cause issues with the `debug` gem's
    	[`ObjectSpace.each_iseq` method](https://github.com/ruby/debug/blob/0fcfc28acae33ec1c08068fb7c33703cfa681fa7/ext/debug/iseq_collector.c#L61-L67),
    	which wraps iseqs with a wrapper and exposes their internal states when they're actually not ready to be used.
    
    	And when that happens, errors like this would occur and kill the `debug` gem's thread:
    
    	```
    	 DEBUGGER: ReaderThreadError: uninitialized InstructionSequence
    	┃ DEBUGGER: Disconnected.
    	┃ ["/opt/rubies/ruby-3.2.0/lib/ruby/gems/3.2.0/gems/debug-1.7.1/lib/debug/breakpoint.rb:247:in `absolute_path'",
    	┃  "/opt/rubies/ruby-3.2.0/lib/ruby/gems/3.2.0/gems/debug-1.7.1/lib/debug/breakpoint.rb:247:in `block in iterate_iseq'",
    	┃  "/opt/rubies/ruby-3.2.0/lib/ruby/gems/3.2.0/gems/debug-1.7.1/lib/debug/breakpoint.rb:246:in `each_iseq'",
    	...
    	```
    
    	A way to reproduce the issue is to satisfy these conditions at the same time:
    
    	1. `debug` gem calling `ObjectSpace.each_iseq` (e.g. [activating a `LineBreakpoint`](https://github.com/ruby/debug/blob/0fcfc28acae33ec1c08068fb7c33703cfa681fa7/lib/debug/breakpoint.rb#L246)
    
    ).
    	2. A large amount of iseq being loaded from another thread (possibly through the `bootsnap` gem).
    	3. 1 and 2 iterating through the same iseq(s) at the same time.
    
    	Because this issue requires external dependencies and a rather complicated timing setup to reproduce, I wasn't able to write a test case for it.
    	But here's some pseudo code to help reproduce it:
    
    	```rb
    	require "debug/session"
    
    	Thread.new do
    	  100.times do
    	    ObjectSpace.each_iseq do |iseq|
    	      iseq.absolute_path
    	    end
    	  end
    	end
    
    	sleep 0.1
    
    	load_a_bunch_of_iseq
    	possibly_through_bootsnap
    	```
    
    	[Bug #19348]
    
    Co-authored-by: default avatarPeter Zhu <peter@peterzhu.ca>
    	---
    	 compile.c       |  2 +-
    	 vm_core.h       |  1 +
    	 vm_insnhelper.c | 11 +++++++++++
    	 3 files changed, 13 insertions(+), 1 deletion(-)
Loading