[ruby-core:70097] rb_ensure and setjmp buffers AV on Win32

From: Alex Budovski <abudovski@...>
Date: 2015-07-23 08:22:55 UTC
List: ruby-core #70097
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?

In This Thread

Prev Next