Bug #4992
closedfinalizer中のThread.newでSEGV
Added by sorah (Sorah Fukumori) over 14 years ago. Updated almost 3 years ago.
Description
以下のようなコードを実行するとSEGVします.
$ ruby -e'ObjectSpace.define_finalizer(""){Thread.new{}}'
$ ruby -e'ObjectSpace.define_finalizer(""){Thread.new{}}'
SEGV received in SEGV handler
$ ruby -e'ObjectSpace.define_finalizer(""){2.times{Thread.new{}}}'
SEGV received in SEGV handler
$ ruby -e'ObjectSpace.define_finalizer(""){2.times{Thread.new{}}}'
SEGV received in SEGV handler
2.timesをつけるとほぼ確実,つけないと2回に1回くらいの割合で再現します.
        
           Updated by sorah (Sorah Fukumori) over 14 years ago
          
          
        
        
          
            Actions
          
          #1
            [ruby-core:37859]
          Updated by sorah (Sorah Fukumori) over 14 years ago
          
          
        
        
          
            Actions
          
          #1
            [ruby-core:37859]
        
      
      Oops, I sent this issue into ruby-core.
I wrote this issue again in English:
-- Thread.new in finalizer raises SEGV
The following code raises SEGV sometimes:
$ ruby -e'ObjectSpace.define_finalizer(""){Thread.new{}}'
$ ruby -e'ObjectSpace.define_finalizer(""){Thread.new{}}'
SEGV received in SEGV handler
But this code raises SEGV authenticity:
$ ruby -e'ObjectSpace.define_finalizer(""){2.times{Thread.new{}}}'
SEGV received in SEGV handler
$ ruby -e'ObjectSpace.define_finalizer(""){2.times{Thread.new{}}}'
SEGV received in SEGV handler
        
           Updated by kosaki (Motohiro KOSAKI) over 14 years ago
          
          
        
        
          
            Actions
          
          #2
            [ruby-core:37860]
          Updated by kosaki (Motohiro KOSAKI) over 14 years ago
          
          
        
        
          
            Actions
          
          #2
            [ruby-core:37860]
        
      
      - ruby -v changed from ruby 1.9.3dev (2011-07-05 trunk 32413) [x86_64-darwin10.8.0] to -
-- Thread.new in finalizer raises SEGV
The following code raises SEGV sometimes:
$ ruby -e'ObjectSpace.define_finalizer(""){Thread.new{}}'
$ ruby -e'ObjectSpace.define_finalizer(""){Thread.new{}}'
SEGV received in SEGV handler
I think Thread.new in finalizer should raise argument error.
IOW, we shouldn't allow to create new thread in finalizer.
It's disaster. just my 2 cent.
        
           Updated by ko1 (Koichi Sasada) over 14 years ago
          
          
        
        
          
            Actions
          
          #3
            [ruby-core:37861]
          Updated by ko1 (Koichi Sasada) over 14 years ago
          
          
        
        
          
            Actions
          
          #3
            [ruby-core:37861]
        
      
      (2011/07/08 10:24), KOSAKI Motohiro wrote:
IOW, we shouldn't allow to create new thread in finalizer.
It's disaster. just my 2 cent.
Why that?
--
// SASADA Koichi at atdot dot net
        
           Updated by kosaki (Motohiro KOSAKI) over 14 years ago
          
          
        
        
          
            Actions
          
          #4
            [ruby-core:37863]
          Updated by kosaki (Motohiro KOSAKI) over 14 years ago
          
          
        
        
          
            Actions
          
          #4
            [ruby-core:37863]
        
      
      2011/7/8 SASADA Koichi ko1@atdot.net:
(2011/07/08 10:24), KOSAKI Motohiro wrote:
IOW, we shouldn't allow to create new thread in finalizer.
It's disaster. just my 2 cent.Why that?
After ruby_finalize_1(), main thread start to destruct various core data
structure (eg GVL), therefore subthread have no guarantee to don't get
SEGV.
        
           Updated by kosaki (Motohiro KOSAKI) over 14 years ago
          
          
        
        
          
            Actions
          
          #5
            [ruby-core:37879]
          Updated by kosaki (Motohiro KOSAKI) over 14 years ago
          
          
        
        
          
            Actions
          
          #5
            [ruby-core:37879]
        
      
      - Status changed from Open to Assigned
- Assignee set to kosaki (Motohiro KOSAKI)
- Target version set to 1.9.3
        
           Updated by kosaki (Motohiro KOSAKI) over 14 years ago
          
          
        
        
          
            Actions
          
          #6
            [ruby-core:37892]
          Updated by kosaki (Motohiro KOSAKI) over 14 years ago
          
          
        
        
          
            Actions
          
          #6
            [ruby-core:37892]
        
      
      - Assignee changed from kosaki (Motohiro KOSAKI) to ko1 (Koichi Sasada)
How's this?
Index: vm_core.h
===================================================================
--- vm_core.h	(revision 32446)
+++ vm_core.h	(working copy)
@@ -285,6 +285,7 @@
     VALUE thgroup_default;
 
     int running;
+    int inhibit_thread_creation;
     int thread_abort_on_exception;
     unsigned long trace_flag;
     volatile int sleeper;
Index: thread.c
===================================================================
--- thread.c	(revision 32447)
+++ thread.c	(working copy)
@@ -367,6 +367,7 @@
 
     thread_debug("rb_thread_terminate_all (main thread: %p)\n", (void *)th);
     st_foreach(vm->living_threads, terminate_i, (st_data_t)th);
+    vm->inhibit_thread_creation = 1;
 
     while (!rb_thread_alone()) {
 	PUSH_TAG();
@@ -583,6 +584,10 @@
 {
     rb_thread_t *th;
     VALUE thread = rb_thread_alloc(klass);
+
+    if (GET_VM()->inhibit_thread_creation)
+	rb_raise(rb_eThreadError, "can't alloc thread");
+
     rb_obj_call_init(thread, argc, argv);
     GetThreadPtr(thread, th);
     if (!th->first_args) {
        
           Updated by ko1 (Koichi Sasada) over 14 years ago
          
          
        
        
          
            Actions
          
          #7
            [ruby-core:37951]
          Updated by ko1 (Koichi Sasada) over 14 years ago
          
          
        
        
          
            Actions
          
          #7
            [ruby-core:37951]
        
      
      Motohiro KOSAKI wrote:
How's this?
Thank you. On 1.9.3, it is Okay. Could you commit it?
        
           Updated by kosaki (Motohiro KOSAKI) over 14 years ago
          
          
        
        
          
            Actions
          
          #8
          Updated by kosaki (Motohiro KOSAKI) over 14 years ago
          
          
        
        
          
            Actions
          
          #8
        
      
      - Status changed from Assigned to Closed
- % Done changed from 0 to 100
This issue was solved with changeset r32492.
Shota, thank you for reporting this issue.
Your contribution to Ruby is greatly appreciated.
May Ruby be with you.
- 
vm_core.h (typedef struct rb_vm_struct): create a new 
 'inhibit_thread_createion' field.
- 
thread.c (rb_thread_terminate_all): set inhibit_thread_creation. 
- 
thread.c (thread_s_new): don't permit to create new thread 
 if the VM is under destruction. Otherwise evil finalizer code
 can make SEGV. [Bug #4992][ruby-core:37858]
- 
bootstraptest/test_objectspace.rb: new test for this fix. 
        
           Updated by nobu (Nobuyoshi Nakada) almost 3 years ago
          
          
        
        
          
            Actions
          
          #9
            [ruby-core:110827]
          Updated by nobu (Nobuyoshi Nakada) almost 3 years ago
          
          
        
        
          
            Actions
          
          #9
            [ruby-core:110827]
        
      
      Thread.new is prohibited but not Thread.start.
$ ruby -v --disable=gems -d -e 'class E<StandardError;def detailed_message(...) Thread.start{"<#{super}>"}.value; end; end' -e 'raise E'
ruby 3.2.0dev (2022-11-13T14:54:43Z master 2f7d2662dd) [x86_64-darwin21]
Exception `E' at -e:2 - E
-e:2:in `<main>': <E (E)>
bash: exit 1