Project

General

Profile

Actions

Bug #2127

closed

Fiber#resume - segfault inside C extension

Added by sunaku (Suraj Kurapati) over 14 years ago. Updated almost 3 years ago.

Status:
Rejected
Assignee:
-
Target version:
-
ruby -v:
ruby 1.9.2dev (2009-09-14 trunk 24923) [i686-linux]
Backport:
[ruby-core:25681]

Description

Hi,

I am using ruby 1.9.2dev (2009-09-14 trunk 24923) [i686-linux].

Both Fiber#resume and rb_fiber_resume() are causing a segfault
when called from inside a very simple C extension (see below).

Thanks for your consideration.

 ###############################################################
 # ls
 ###############################################################
 
 extconf.rb  main.c
 
 ###############################################################
 # cat extconf.rb 
 ###############################################################
 
 require 'mkmf'
 
 have_library('ruby-static', 'ruby_init') ||
 have_library('ruby', 'ruby_init')
 
 create_makefile('main')
 
 ###############################################################
 # cat main.c
 ###############################################################
 
 #include <stdio.h>
 #include <ruby.h>
 
 VALUE body(VALUE arg)
 {
     printf("Fiber: Inside fiber body"); 
     fflush(stdout);
     return Qnil;
 }
 
 RUBY_GLOBAL_SETUP
 
 int main(int argc, char** argv)
 {
     ruby_sysinit(&argc, &argv);
     RUBY_INIT_STACK;
     ruby_init();
 
     printf("Main: Creating Ruby fiber..."); 
     fflush(stdout);
     VALUE fib = rb_fiber_new(body, Qnil);
 
     VALUE dump = rb_inspect(fib);
     rb_io_puts(1, &dump, rb_stdout);
 
     printf("Main: Going to resume fiber...\n"); 
     fflush(stdout);
 
     rb_funcall(fib, rb_intern("resume"), 0); /* <== SEGFAULT */
 
     printf("Main: Fiber resumed successfully.\n");
     fflush(stdout);
 
     return ruby_cleanup(0);
 }
 
 ###############################################################
 # ruby -v extconf.rb 
 ###############################################################
 
 ruby 1.9.2dev (2009-09-14 trunk 24923) [i686-linux]
 checking for ruby_init() in -lruby-static... yes
 creating Makefile
 
 ###############################################################
 # sed -i 's,-shared,,g' Makefile ; make
 ###############################################################
 
 gcc -I. -I/home/sun/app/ruby-1.9-git/include/ruby-1.9.1/i686-linux -I/home/sun/app/ruby-1.9-git/include/ruby-1.9.1/ruby/backward -I/home/sun/app/ruby-1.9-git/include/ruby-1.9.1 -I. -D_FILE_OFFSET_BITS=64  -fPIC -g  -o main.o -c main.c
 gcc  -o main.so main.o -L. -L/home/sun/app/ruby-1.9-git/lib -Wl,-R/home/sun/app/ruby-1.9-git/lib -L.  -rdynamic -Wl,-export-dynamic    -lruby-static  -lpthread -lrt -ldl -lcrypt -lm   -lc
 
 ###############################################################
 # ./main.so
 ###############################################################
 
 Main: Creating Ruby fiber...#<Fiber:0x8a5938c>
 Main: Going to resume fiber...
 [BUG] Segmentation fault
 ruby 1.9.2dev (2009-09-14 trunk 24923) [i686-linux]
 
 -- control frame ----------
 c:0001 p:---- s:0001 b:-001 l:000000 d:000000 ------
 ---------------------------
 
 fish: Job 1, “./main.so” terminated by signal SIGSEGV (Address boundary error)
 
 ####################################################
 # gdb main.so core 
 ####################################################
 
 GNU gdb 6.8
 [...]
 Core was generated by `./main.so'.
 Program terminated with signal 11, Segmentation fault.
 [New process 8635]
 [New process 8636]
 #0  0xb7c539be in uw_frame_state_for () from /usr/lib/libgcc_s.so.1
 
 (gdb) bt full
 #0  0xb7c539be in uw_frame_state_for () from /usr/lib/libgcc_s.so.1
 No symbol table info available.
 #1  0xb7c54011 in _Unwind_Backtrace () from /usr/lib/libgcc_s.so.1
 No symbol table info available.
 #2  0xb7ddf405 in backtrace () from /lib/libc.so.6
 No symbol table info available.
 #3  0x08141c33 in rb_vm_bugreport () at vm_dump.c:598
 	trace = {0x8141c33, 0x816fb60, 0x816fbba, 0x80e386e, 0xb7ee740c, 0x0 <repeats 1019 times>}
 	n = -1209027184
 	syms = (char **) 0xb7d4630f
 	i = -1209789088
 #4  0x0816fb60 in report_bug (file=0x0, line=0, fmt=0x819a2e3 "Segmentation fault", 
     args=0x8aa97d4 "\236é\031k") at error.c:215
 	buf = '\0' <repeats 8176 times>, "\230\227ª\bcè\023\b\2102£\b\230~¬\b"
 	out = (FILE *) 0xb7e41560
 	len = 0
 #5  0x0816fbba in rb_bug (fmt=0x819a2e3 "Segmentation fault") at error.c:232
 	args = 0x8aa97d4 "\236é\031k"
 #6  0x080e386e in sigsegv (sig=11, info=0x8aa980c, ctx=0x8aa988c) at signal.c:616
 	th = (rb_thread_t *) 0x8a33288
 #7  <signal handler called>
 No symbol table info available.
 #8  0x6b19e99e in ?? ()
 No symbol table info available.
Actions #1

Updated by nobu (Nobuyoshi Nakada) over 14 years ago

  • Status changed from Open to Closed
  • % Done changed from 0 to 100

=begin
Applied in changeset r25014.
=end

Actions #2

Updated by nobu (Nobuyoshi Nakada) over 14 years ago

=begin
Hi,

At Mon, 21 Sep 2009 09:12:52 +0900,
Suraj Kurapati wrote in [ruby-core:25681]:

Both Fiber#resume and rb_fiber_resume() are causing a segfault
when called from inside a very simple C extension (see below).

Don't create a fiber inside a not-running thread.

 $ cat bug-2127.c
 #include <stdio.h>
 #include <ruby.h>
 
 VALUE
 body(VALUE arg)
 {
     printf("Fiber: Inside fiber body\n");
     fflush(stdout);
     return Qnil;
 }
 
 VALUE
 run_body(VALUE arg)
 {
     VALUE fib = rb_fiber_new(body, arg), dump;
 
     printf("Main: Creating Ruby fiber..."); 
     fflush(stdout);
     dump = rb_inspect(fib);
     rb_io_puts(1, &dump, rb_stdout);
 
     printf("Main: Going to resume fiber...\n"); 
     fflush(stdout);
 
     rb_fiber_resume(fib, 0, 0); /* <== SEGFAULT */
 
     printf("Main: Fiber resumed successfully.\n");
     fflush(stdout);
     return Qnil;
 }
 
 RUBY_GLOBAL_SETUP
 
 int
 main(int argc, char** argv)
 {
     ruby_sysinit(&argc, &argv);
 
     {
 	RUBY_INIT_STACK;
 	ruby_init();
 
 	rb_protect(run_body, Qnil, 0);
     }
 
     return ruby_cleanup(0);
 }
 $ make MAINOBJ='$(PROGRAM).o' PROGRAM=bug-2127 program LIBRUBYARG='$(LIBRUBY_A)' 
 $ ./bug-2127
 Main: Creating Ruby fiber...#<Fiber:0x2c365c>
 Main: Going to resume fiber...
 Fiber: Inside fiber body
 Main: Fiber resumed successfully.

--
Nobu Nakada

=end

Actions #3

Updated by nobu (Nobuyoshi Nakada) over 14 years ago

  • Status changed from Closed to Open

=begin

=end

Actions #4

Updated by nobu (Nobuyoshi Nakada) over 14 years ago

  • Status changed from Open to Rejected

=begin

=end

Actions #5

Updated by sunaku (Suraj Kurapati) over 14 years ago

=begin
Wow, thanks for the quick response! The corrections
you made to my C code are very helpful and instructive.

Thanks for your help! Cheers. :-)
=end

Actions #6

Updated by sunaku (Suraj Kurapati) over 14 years ago

=begin
I have one small question about the example code:

I want to pause the execution of run_body() and return
control to main(). Later, main() will resume run_body().
In other words, I want to make run_body() like a Fiber.

Is this possible?

Thanks for your consideration.
=end

Actions #7

Updated by sunaku (Suraj Kurapati) over 14 years ago

=begin
I found an answer to my question:

The trick is to always work with the Fiber inside the environment
(a running thread?) provided by rb_protect(). If you try to do
anything to the Fiber (other than #inspect) outside of rb_protect(),
a segfault occurs (see the NOTE comment in the example below).

Below is an updated example that shows the answer to my question.

Cheers.

###############################################################

cat main.c

###############################################################

#include <stdio.h>
#include <ruby.h>

VALUE
my_fiber_body(VALUE arg)
{
printf("Fiber: Entering fiber body\n");

 int i;
 for (i = 0; i < 5; i++)
 {
     printf("Fiber: going to yield=%d\n", i);

     VALUE count = INT2FIX(i);
     rb_fiber_yield(1, &count);
 }

 printf("Fiber: Exiting fiber body\n");

 return Qtrue;

}

VALUE
create_my_fiber(VALUE arg)
{
VALUE fib = rb_fiber_new(my_fiber_body, arg), dump;

 printf("create_my_fiber: Created my fiber=");
 fflush(stdout);
 dump = rb_inspect(fib);
 rb_io_puts(1, &dump, rb_stdout);

 return fib;

}

VALUE
resume_my_fiber(VALUE fib)
{
VALUE dump;

 printf("resume_my_fiber: Going to resume fiber=");
 fflush(stdout);
 dump = rb_inspect(fib);
 rb_io_puts(1, &dump, rb_stdout);

 if (RTEST(rb_fiber_alive_p(fib)))
 {
     VALUE result = rb_fiber_resume(fib, 0, 0);

     printf("resume_my_fiber: Fiber yielded value=");
     fflush(stdout);
     dump = rb_inspect(result);
     rb_io_puts(1, &dump, rb_stdout);

     return result;
 }
 else
 {
     printf("resume_my_fiber: Fiber is dead! cannot resume\n");
     return Qfalse;
 }

}

RUBY_GLOBAL_SETUP

int
main(int argc, char** argv)
{
ruby_sysinit(&argc, &argv);
{
RUBY_INIT_STACK;
ruby_init();

     VALUE fib = rb_protect(create_my_fiber, Qnil, 0);

     printf("Main: Outside rb_protect()\n");
     /*
         NOTE: I resume the fiber here (outside of rb_protect())
               then a segfault occurs.  Is this because the
               rb_protect() function provides a running thread?
     */

     printf("Main: Going to resume fiber many times...\n");

     VALUE count;
     do
     {
         count = rb_protect(resume_my_fiber, fib, 0);
     }
     while (RTEST(count));

     printf("Main: Goodbye!\n");
 }

 return ruby_cleanup(0);

}

###############################################################

./main.so

###############################################################

create_my_fiber: Created my fiber=#Fiber:0x92d7414
Main: Outside rb_protect()
Main: Going to resume fiber many times...
resume_my_fiber: Going to resume fiber=#Fiber:0x92d7414
Fiber: Entering fiber body
Fiber: going to yield=0
resume_my_fiber: Fiber yielded value=0
resume_my_fiber: Going to resume fiber=#Fiber:0x92d7414
Fiber: going to yield=1
resume_my_fiber: Fiber yielded value=1
resume_my_fiber: Going to resume fiber=#Fiber:0x92d7414
Fiber: going to yield=2
resume_my_fiber: Fiber yielded value=2
resume_my_fiber: Going to resume fiber=#Fiber:0x92d7414
Fiber: going to yield=3
resume_my_fiber: Fiber yielded value=3
resume_my_fiber: Going to resume fiber=#Fiber:0x92d7414
Fiber: going to yield=4
resume_my_fiber: Fiber yielded value=4
resume_my_fiber: Going to resume fiber=#Fiber:0x92d7414
Fiber: Exiting fiber body
resume_my_fiber: Fiber yielded value=true
resume_my_fiber: Going to resume fiber=#Fiber:0x92d7414
resume_my_fiber: Fiber is dead! cannot resume
Main: Goodbye!

=end

Actions #8

Updated by nobu (Nobuyoshi Nakada) over 14 years ago

=begin
Hi,

At Mon, 21 Sep 2009 13:34:31 +0900,
Suraj Kurapati wrote in [ruby-core:25689]:

The trick is to always work with the Fiber inside the environment
(a running thread?) provided by rb_protect(). If you try to do
anything to the Fiber (other than #inspect) outside of rb_protect(),
a segfault occurs (see the NOTE comment in the example below).

Regardless of fiber, uncaught exception causes a segfault. And
I don't think it's guaranteed to resume a fiber in different
rb_protect scope.

--
Nobu Nakada

=end

Actions #9

Updated by sunaku (Suraj Kurapati) over 14 years ago

=begin
Hi,

Nobu Nakada wrote:

Regardless of fiber, uncaught exception causes a segfault.

Thanks, this is good to know.

I don't think it's guaranteed to resume a fiber in different
rb_protect scope.

Why is that? Does each rb_protect scope have a different running thread (with a different stack + heap)? Is there danger of a cross-thread violation?

Should I try to store the original rb_protect scope into a variable and reuse it when resuming my Fiber? What do you suggest?

Thanks for your consideration.

(Sorry for asking so many questions!)
=end

Actions #10

Updated by nobu (Nobuyoshi Nakada) over 14 years ago

=begin
Hi,

At Mon, 21 Sep 2009 16:26:17 +0900,
Suraj Kurapati wrote in [ruby-core:25691]:

I don't think it's guaranteed to resume a fiber in different
rb_protect scope.

Why is that? Does each rb_protect scope have a different
running thread (with a different stack + heap)? Is there
danger of a cross-thread violation?

Maybe safe, maybe dangerous, noone has confirmed, nor
considered it yet. There is no cross-thread violation like
checks right now, but it might be figured out that the check is
necessary.

Should I try to store the original rb_protect scope into a
variable and reuse it when resuming my Fiber? What do you
suggest?

Create a new thread and use fibers within one rb_protect.

--
Nobu Nakada

=end

Actions #11

Updated by sunaku (Suraj Kurapati) over 14 years ago

=begin
Hi,

Nobu Nakada wrote:

Maybe safe, maybe dangerous, noone has confirmed, nor
considered it yet. There is no cross-thread violation like
checks right now, but it might be figured out that the check is
necessary.

You are correct. I got a #<FiberError: fiber called across trap>
error when I used a more complex Ruby script as the body of my Fiber.

Create a new thread and use fibers within one rb_protect.

Thanks, I will try this suggestion. Cheers!
=end

Updated by ioquatix (Samuel Williams) almost 3 years ago

  • Description updated (diff)

Make description more readable.

Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0