Skip to content

Instantly share code, notes, and snippets.

@yyamano
Created January 10, 2018 09:39
Show Gist options
  • Save yyamano/603b6934cafb68d63278f2739bc21c5e to your computer and use it in GitHub Desktop.
Save yyamano/603b6934cafb68d63278f2739bc21c5e to your computer and use it in GitHub Desktop.
ngx_mruby SIGSEGV with the latest mruby
Program received signal SIGSEGV, Segmentation fault.
0x0000000000584ed5 in mrb_vm_exec (mrb=mrb@entry=0xbf3dd0, proc=<optimized out>, proc@entry=0xcb8680, pc=<optimized out>)
at /home/vagrant/ngx_mruby/mruby/src/vm.c:1854
1854 JUMP;
Missing separate debuginfos, use: debuginfo-install glibc-2.17-106.el7_2.6.x86_64 nss-softokn-freebl-3.16.2.3-13.el7_1.x86_64 openssl-libs-1.0.1e-51.el7_2.5.x86_64 pcre-8.32-15.el7_2.1.x86_64 zlib-1.2.7-15.el7.x86_64
(gdb) bt
#0 0x0000000000584ed5 in mrb_vm_exec (mrb=mrb@entry=0xbf3dd0, proc=<optimized out>, proc@entry=0xcb8680, pc=<optimized out>)
at /home/vagrant/ngx_mruby/mruby/src/vm.c:1854
#1 0x000000000058af46 in mrb_vm_run (mrb=0xbf3dd0, proc=0xcb8680, self=..., stack_keep=<optimized out>)
at /home/vagrant/ngx_mruby/mruby/src/vm.c:935
#2 0x000000000058ceee in mrb_run (mrb=<optimized out>, proc=<optimized out>, self=...)
at /home/vagrant/ngx_mruby/mruby/src/vm.c:2985
#3 0x000000000054ec78 in ngx_mrb_run (r=0xde7720, state=0xbed168, code=0xcd9258, cached=1, result=0x0)
at /home/vagrant/ngx_mruby/src/http/ngx_http_mruby_module.c:836
#4 0x00000000005512be in ngx_http_mruby_rewrite_inline_handler (r=0xde7720)
at /home/vagrant/ngx_mruby/src/http/ngx_http_mruby_module.c:1575
#5 0x00000000004ae82e in ngx_http_core_rewrite_phase (r=0xde7720, ph=0xd63350) at src/http/ngx_http_core_module.c:905
#6 0x00000000004ae68b in ngx_http_core_run_phases (r=0xde7720) at src/http/ngx_http_core_module.c:851
#7 0x00000000004ae5f9 in ngx_http_handler (r=0xde7720) at src/http/ngx_http_core_module.c:834
#8 0x00000000004bd64a in ngx_http_process_request (r=0xde7720) at src/http/ngx_http_request.c:1948
#9 0x00000000004bbfd3 in ngx_http_process_request_headers (rev=0xbe0c10) at src/http/ngx_http_request.c:1375
#10 0x00000000004bb399 in ngx_http_process_request_line (rev=0xbe0c10) at src/http/ngx_http_request.c:1048
#11 0x00000000004b9f59 in ngx_http_wait_request_handler (rev=0xbe0c10) at src/http/ngx_http_request.c:506
#12 0x000000000049d5c7 in ngx_epoll_process_events (cycle=0xbdc530, timer=60000, flags=1)
at src/event/modules/ngx_epoll_module.c:902
#13 0x000000000048c351 in ngx_process_events_and_timers (cycle=0xbdc530) at src/event/ngx_event.c:242
#14 0x0000000000499a89 in ngx_single_process_cycle (cycle=0xbdc530) at src/os/unix/ngx_process_cycle.c:309
#15 0x0000000000458040 in main (argc=1, argv=0x7fffffffe408) at src/core/nginx.c:378
(gdb) p pc
$1 = <optimized out>
(gdb) p *mrb->c
$2 = {prev = 0x0, stack = 0xc095c0, stbase = 0xc095c0, stend = 0xc09dc0, ci = 0xc09d80, cibase = 0xc09dd0, ciend = 0xc0a7d0,
rescue = 0x0, rsize = 0, ensure = 0x0, esize = 0, eidx = 0, status = MRB_FIBER_CREATED, vmexec = 0 '\000', fib = 0x0}
(gdb) p *mrb->c->ci
$3 = {mid = 0, proc = 0x0, stackent = 0x0, nregs = 0, ridx = 0, epos = 0, env = 0x0, pc = 0x0, err = 0x0, argc = 0, acc = 0,
target_class = 0xa11}
Fiberがらみだとすると、 https://github.com/mruby/mruby/issues/3789 ?
@yyamano
Copy link
Author

yyamano commented Jan 31, 2018

Fiber関係ない。多分、ngx_mrubyの問題。以下、推測も含んでいるので、検証する必要あり。

問題のスクリプトは

mruby_rewrite_handler_code '
    Nginx.redirect "/vars"
';
irep 0xcec1c0 nregs=4 nlocals=1 pools=1 syms=2 reps=0
file: INLINE CODE
    2 000 OP_GETCONST	R1	:Nginx
    2 001 OP_STRING	R2	L(0)	; "/vars"
    2 002 OP_SEND	R1	:redirect	1
    2 003 OP_STOP

Nginx.redirectを呼ぶと内部では、ngx_http_internal_redirect()が呼ばれて nginxに制御が移る。
その間に別のリクエストを受け取ると、別のスクリプトが実行される。
つまり、mrb_run()の実行中に別のコンテキストでmrb_run()が実行される。
最初の mrb_run()に戻ってくると、mrb->c->callinfoが変ってるので pcがNULLってことのような気がする。

https://github.com/mruby/mruby/blob/master/src/vm.c#L1388-L1506

  L_SEND:
    CASE(OP_SEND) {
      /* A B C  R(A) := call(R(A),Syms(B),R(A+1),...,R(A+C)) */
      int a = GETARG_A(i);
      int n = GETARG_C(i);
      int argc = (n == CALL_MAXARGS) ? -1 : n;
      int bidx = (argc < 0) ? a+2 : a+n+1;
      mrb_method_t m;
      struct RClass *c;
      mrb_callinfo *ci = mrb->c->ci;
      mrb_value recv, blk;
      mrb_sym mid = syms[GETARG_B(i)];

      mrb_assert(bidx < ci->nregs);

      recv = regs[a];
      if (GET_OPCODE(i) != OP_SENDB) {
        SET_NIL_VALUE(regs[bidx]);
        blk = regs[bidx];
      }
      else {
        blk = regs[bidx];
        if (!mrb_nil_p(blk) && mrb_type(blk) != MRB_TT_PROC) {
          blk = mrb_convert_type(mrb, blk, MRB_TT_PROC, "Proc", "to_proc");
          /* The stack might have been reallocated during mrb_convert_type(),
             see #3622 */
          regs[bidx] = blk;
        }
      }
      c = mrb_class(mrb, recv);
      m = mrb_method_search_vm(mrb, &c, mid);
      if (MRB_METHOD_UNDEF_P(m)) {
        mrb_sym missing = mrb_intern_lit(mrb, "method_missing");
        m = mrb_method_search_vm(mrb, &c, missing);
        if (MRB_METHOD_UNDEF_P(m) || (missing == mrb->c->ci->mid && mrb_obj_eq(mrb, regs[0], recv))) {
          mrb_value args = (argc < 0) ? regs[a+1] : mrb_ary_new_from_values(mrb, n, regs+a+1);
          ERR_PC_SET(mrb, pc);
          mrb_method_missing(mrb, mid, recv, args);
        }
        if (argc >= 0) {
          if (a+2 >= irep->nregs) {
            stack_extend(mrb, a+3);
          }
          regs[a+1] = mrb_ary_new_from_values(mrb, n, regs+a+1);
          regs[a+2] = blk;
          argc = -1;
        }
        mrb_ary_unshift(mrb, regs[a+1], mrb_symbol_value(mid));
        mid = missing;
      }

     // ここでcallinfoをpush。戻ってくるために pcを設定
      /* push callinfo */
      ci = cipush(mrb);
      ci->mid = mid;
      ci->stackent = mrb->c->stack;
      ci->target_class = c;
      ci->argc = argc;

      ci->pc = pc + 1;
      ci->acc = a;

      /* prepare stack */
      mrb->c->stack += a;

      if (MRB_METHOD_CFUNC_P(m)) {
        ci->nregs = (argc < 0) ? 3 : n+2;
        // ここでNginx.redirectのC関数を呼んで、nginxに制御が移る。
        if (MRB_METHOD_PROC_P(m)) {
          struct RProc *p = MRB_METHOD_PROC(m);

          ci->proc = p;
          recv = p->body.func(mrb, recv);
        }
        else {
          recv = MRB_METHOD_FUNC(m)(mrb, recv);
        }
        mrb_gc_arena_restore(mrb, ai);
        mrb_gc_arena_shrink(mrb, ai);
        if (mrb->exc) goto L_RAISE;
        // 戻ってきたので  callinfoを取得。でも、別のmrb_run()が実行されているので書き換わっている。
        ci = mrb->c->ci;
        if (GET_OPCODE(i) == OP_SENDB) {
          if (mrb_type(blk) == MRB_TT_PROC) {
            struct RProc *p = mrb_proc_ptr(blk);
            if (p && !MRB_PROC_STRICT_P(p) && MRB_PROC_ENV(p) == ci[-1].env) {
              p->flags |= MRB_PROC_ORPHAN;
            }
          }
        }
        if (!ci->target_class) { /* return from context modifying method (resume/yield) */
          if (ci->acc == CI_ACC_RESUMED) {
            mrb->jmp = prev_jmp;
            return recv;
          }
          else {
            mrb_assert(!MRB_PROC_CFUNC_P(ci[-1].proc));
            proc = ci[-1].proc;
            irep = proc->body.irep;
            pool = irep->pool;
            syms = irep->syms;
          }
        }
        mrb->c->stack[0] = recv;
        /* pop stackpos */
        mrb->c->stack = ci->stackent;
        pc = ci->pc;
        cipop(mrb);
        JUMP;
      }
      else {
        /* setup environment for calling method */
        proc = ci->proc = MRB_METHOD_PROC(m);
        irep = proc->body.irep;
        pool = irep->pool;
        syms = irep->syms;
        ci->nregs = irep->nregs;
        stack_extend(mrb, (argc < 0 && ci->nregs < 3) ? 3 : ci->nregs);
        pc = irep->iseq;
        JUMP;
      }
    }

@yyamano
Copy link
Author

yyamano commented Jan 31, 2018

その間に別のリクエストを受け取ると、別のスクリプトが実行される。

リダイレクト先の別のmrubyのハンドラーが動いているのかも。割り込んで実行されているのは以下のスクリプト。

irep 0xcea5b0 nregs=6 nlocals=1 pools=2 syms=7 reps=0
file: INLINE CODE
    1 000 OP_STRING	R1	L(0)	; "global_ngx_mruby"
    1 001 OP_GETCONST	R2	:Nginx
    1 002 OP_GETMCNST	R2	R2::Request
    1 003 OP_SEND	R2	:new	0
    1 004 OP_SEND	R2	:headers_out	0
    1 005 OP_STRING	R3	L(1)	; "Server"
    1 006 OP_MOVE	R4	R1	
    1 007 OP_SEND	R2	:[]=	2
    1 008 OP_GETCONST	R1	:Nginx
    1 009 OP_GETCONST	R2	:Nginx
    1 010 OP_GETMCNST	R2	R2::DECLINED
    1 011 OP_SEND	R1	:return	1
    1 012 OP_STOP
irep 0xcc3f80 nregs=6 nlocals=2 pools=5 syms=8 reps=0
file: INLINE CODE
    2 000 OP_GETCONST	R2	:Nginx
    2 001 OP_GETMCNST	R2	R2::Request	; 
    2 002 OP_SEND	R2	:new	0
    2 003 OP_MOVE	R1	R2		; R1:r
    3 004 OP_STRING	R2	L(0)	; "text/html"
    3 005 OP_MOVE	R3	R1		; R1:r
    3 006 OP_MOVE	R4	R2	
    3 007 OP_SEND	R3	:content_type=	1
    4 008 OP_GETCONST	R2	:Nginx
    4 009 OP_STRING	R3	L(1)	; "host => "
    4 010 OP_MOVE	R4	R1		; R1:r
    4 011 OP_SEND	R4	:var	0
    4 012 OP_SEND	R4	:host	0
    4 013 OP_STRCAT	R3	R4	
    4 014 OP_STRING	R4	L(2)	; " "
    4 015 OP_STRCAT	R3	R4	
    4 016 OP_SEND	R2	:rputs	1
    5 017 OP_GETCONST	R2	:Nginx
    5 018 OP_STRING	R3	L(3)	; "foo => "
    5 019 OP_MOVE	R4	R1		; R1:r
    5 020 OP_SEND	R4	:var	0
    5 021 OP_SEND	R4	:foo	0
    5 022 OP_STRCAT	R3	R4	
    5 023 OP_SEND	R2	:rputs	1
    5 024 OP_STOP

@yyamano
Copy link
Author

yyamano commented Feb 2, 2018

mruby/mruby@55431b4 で直ったっぽい。

@yyamano
Copy link
Author

yyamano commented Feb 2, 2018

mrb_codedump_all(mrb_state*, struct RProc*) 使うと irep のダンプが出来る。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment