From: Alex Budovski Date: 2015-07-23T09:35:23-07:00 Subject: [ruby-core:70099] Re: rb_ensure and setjmp buffers AV on Win32 In fact, here's an utterly trivial repro: #include #include int main() { ruby_setup(); rb_load_file("abc"); // AV here ruby_cleanup(0); } rbtest1.exe!rb_vm_bugreport(const void * ctx) Line 1024 C rbtest1.exe!rb_bug_context(const void * ctx, const char * fmt, ...) Line 422 C rbtest1.exe!sigsegv(int sig) Line 887 C [External Code] rbtest1.exe!rb_threadptr_tag_jump(rb_thread_struct * th, int st) Line 163 C > rbtest1.exe!rb_ensure(unsigned __int64 (...) * b_proc, unsigned __int64 data1, unsigned __int64 (...) * e_proc, unsigned __int64 data2) Line 915 C rbtest1.exe!load_file(unsigned __int64 parser, unsigned __int64 fname, int script, cmdline_options * opt) Line 1779 C rbtest1.exe!rb_load_file_str(unsigned __int64 fname_v) Line 1794 C rbtest1.exe!rb_load_file(const char * fname) Line 1786 C rbtest1.exe!main() Line 7 C++ Tried using both stable 2.2.2 and git master f965866f4f0a00c8179a1097e89fb4e61f71a92a Win Server 2012 R2, VS 2013 Update 4. x64. On Thu, Jul 23, 2015 at 1:22 AM, Alex Budovski wrote: > Hi all, > > I'm trying a trivial example calling > > void* node = rb_load_file(file); > > where 'file' intentionally doesn't exist. (Just to test graceful error handling) > > I get an AV (segfault) consistently. > > The AV was due to the following sequence of events, all revolving > around rb_ensure. > > 1. PUSH_TAG(); creates a local _tag on the stack, and sets th->tag to > its address. > 2. EXEC_TAG(); calls setjmp on this _tag object > 3. result = (*b_proc) (data1); fails with LoadError (calls > load_file_internal with a nonexistent file, intentionally), setting > state to 6. > 4. POP_TAG(); resets th->tag to NULL. > 5. if (state) > JUMP_TAG(state); > executes, looks up the current thread, and tries to jump to > > ruby_longjmp(th->tag->buf, 1); > > but th->tag is NULL, due to (4) above! So we AV when trying to get th->tag->buf. > > (68e4.20a4): Access violation - code c0000005 (first chance) > First chance exceptions are reported before any exception handling. > This exception may be expected and handled. > *** WARNING: Unable to verify checksum for > E:\dev\dbgscript\build\x64\Debug\x64-msvcr120-ruby230.dll > MSVCR120!longjmp+0x12: > 00007fff`c7c470c2 4c3911 cmp qword ptr [rcx],r10 > ds:00000000`00000010=???????????????? > 0:004> k > # Child-SP RetAddr Call Site > 00 0000008f`0d8ac890 00007fff`9e6a8dc7 MSVCR120!longjmp+0x12 > [f:\dd\vctools\crt\crtw32\misc\amd64\longjmp.asm @ 82] > 01 0000008f`0d8acdd0 00007fff`9e6a782e > x64_msvcr120_ruby230!rb_threadptr_tag_jump+0x37 > [e:\dev\ruby\eval_intern.h @ 163] > 02 0000008f`0d8ace00 00007fff`9e6a18b5 > x64_msvcr120_ruby230!rb_ensure+0x17e [e:\dev\ruby\eval.c @ 915] > 03 0000008f`0d8acfb0 00007fff`9e6a124d > x64_msvcr120_ruby230!load_file+0x65 [e:\dev\ruby\ruby.c @ 1779] > 04 0000008f`0d8ad010 00007fff`9e6a11f2 > x64_msvcr120_ruby230!rb_load_file_str+0x4d [e:\dev\ruby\ruby.c @ 1794] > 05 0000008f`0d8ad0e0 00007fff`cd15122f > x64_msvcr120_ruby230!rb_load_file+0x22 [e:\dev\ruby\ruby.c @ 1786] > 06 0000008f`0d8ad120 00007fff`d14fc65f dbgscript!runscript+0x9f > [e:\dev\dbgscript\src\dllmain.cpp @ 118] > // other frames omitted > > VALUE > rb_ensure(VALUE (*b_proc)(ANYARGS), VALUE data1, VALUE > (*e_proc)(ANYARGS), VALUE data2) > { > int state; > volatile VALUE result = Qnil; > volatile VALUE errinfo; > rb_thread_t *const th = GET_THREAD(); > rb_ensure_list_t ensure_list; > ensure_list.entry.marker = 0; > ensure_list.entry.e_proc = e_proc; > ensure_list.entry.data2 = data2; > ensure_list.next = th->ensure_list; > th->ensure_list = &ensure_list; > PUSH_TAG(); > if ((state = EXEC_TAG()) == 0) { > result = (*b_proc) (data1); > } > POP_TAG(); > errinfo = th->errinfo; > th->ensure_list=ensure_list.next; > (*ensure_list.entry.e_proc)(ensure_list.entry.data2); > th->errinfo = errinfo; > if (state) > JUMP_TAG(state); > return result; > } > > Can someone explain how this is supposed to work?