Skip to content

Commit 17877eb

Browse files
authored
Backport WASI setjmp handler memory leak fixes to Ruby 3.4 (ruby#14788)
[Bug #21626]
1 parent 18e1766 commit 17877eb

File tree

6 files changed

+57
-0
lines changed

6 files changed

+57
-0
lines changed

cont.c

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1509,6 +1509,51 @@ cont_restore_thread(rb_context_t *cont)
15091509
rb_raise(rb_eRuntimeError, "can't call across trace_func");
15101510
}
15111511

1512+
#if defined(__wasm__) && !defined(__EMSCRIPTEN__)
1513+
if (th->ec->tag != sec->tag) {
1514+
/* find the lowest common ancestor tag of the current EC and the saved EC */
1515+
1516+
struct rb_vm_tag *lowest_common_ancestor = NULL;
1517+
size_t num_tags = 0;
1518+
size_t num_saved_tags = 0;
1519+
for (struct rb_vm_tag *tag = th->ec->tag; tag != NULL; tag = tag->prev) {
1520+
++num_tags;
1521+
}
1522+
for (struct rb_vm_tag *tag = sec->tag; tag != NULL; tag = tag->prev) {
1523+
++num_saved_tags;
1524+
}
1525+
1526+
size_t min_tags = num_tags <= num_saved_tags ? num_tags : num_saved_tags;
1527+
1528+
struct rb_vm_tag *tag = th->ec->tag;
1529+
while (num_tags > min_tags) {
1530+
tag = tag->prev;
1531+
--num_tags;
1532+
}
1533+
1534+
struct rb_vm_tag *saved_tag = sec->tag;
1535+
while (num_saved_tags > min_tags) {
1536+
saved_tag = saved_tag->prev;
1537+
--num_saved_tags;
1538+
}
1539+
1540+
while (min_tags > 0) {
1541+
if (tag == saved_tag) {
1542+
lowest_common_ancestor = tag;
1543+
break;
1544+
}
1545+
tag = tag->prev;
1546+
saved_tag = saved_tag->prev;
1547+
--min_tags;
1548+
}
1549+
1550+
/* free all the jump buffers between the current EC's tag and the lowest common ancestor tag */
1551+
for (struct rb_vm_tag *tag = th->ec->tag; tag != lowest_common_ancestor; tag = tag->prev) {
1552+
rb_vm_tag_jmpbuf_deinit(&tag->buf);
1553+
}
1554+
}
1555+
#endif
1556+
15121557
/* copy vm stack */
15131558
#ifdef CAPTURE_JUST_VALID_VM_STACK
15141559
MEMCPY(th->ec->vm_stack,

signal.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -854,6 +854,7 @@ check_stack_overflow(int sig, const uintptr_t addr, const ucontext_t *ctx)
854854
* otherwise it can cause stack overflow again at the same
855855
* place. */
856856
if ((crit = (!ec->tag->prev || !--uplevel)) != FALSE) break;
857+
rb_vm_tag_jmpbuf_deinit(&ec->tag->buf);
857858
ec->tag = ec->tag->prev;
858859
}
859860
reset_sigmask(sig);

vm.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2829,6 +2829,7 @@ vm_exec_handle_exception(rb_execution_context_t *ec, enum ruby_tag_type state, V
28292829
if (VM_FRAME_FINISHED_P(ec->cfp)) {
28302830
rb_vm_pop_frame(ec);
28312831
ec->errinfo = (VALUE)err;
2832+
rb_vm_tag_jmpbuf_deinit(&ec->tag->buf);
28322833
ec->tag = ec->tag->prev;
28332834
EC_JUMP_TAG(ec, state);
28342835
}

vm_trace.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,7 @@ rb_exec_event_hooks(rb_trace_arg_t *trace_arg, rb_hook_list_t *hooks, int pop_p)
455455
if (state) {
456456
if (pop_p) {
457457
if (VM_FRAME_FINISHED_P(ec->cfp)) {
458+
rb_vm_tag_jmpbuf_deinit(&ec->tag->buf);
458459
ec->tag = ec->tag->prev;
459460
}
460461
rb_vm_pop_frame(ec);

wasm/setjmp.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,9 +143,11 @@ rb_wasm_try_catch_init(struct rb_wasm_try_catch *try_catch,
143143
try_catch->try_f = try_f;
144144
try_catch->catch_f = catch_f;
145145
try_catch->context = context;
146+
try_catch->stack_pointer = NULL;
146147
}
147148

148149
// NOTE: This function is not processed by Asyncify due to a call of asyncify_stop_rewind
150+
__attribute__((noinline))
149151
void
150152
rb_wasm_try_catch_loop_run(struct rb_wasm_try_catch *try_catch, rb_wasm_jmp_buf *target)
151153
{
@@ -154,6 +156,10 @@ rb_wasm_try_catch_loop_run(struct rb_wasm_try_catch *try_catch, rb_wasm_jmp_buf
154156

155157
target->state = JMP_BUF_STATE_CAPTURED;
156158

159+
if (try_catch->stack_pointer == NULL) {
160+
try_catch->stack_pointer = rb_wasm_get_stack_pointer();
161+
}
162+
157163
switch ((enum try_catch_phase)try_catch->state) {
158164
case TRY_CATCH_PHASE_MAIN:
159165
// may unwind
@@ -175,6 +181,8 @@ rb_wasm_try_catch_loop_run(struct rb_wasm_try_catch *try_catch, rb_wasm_jmp_buf
175181
// stop unwinding
176182
// (but call stop_rewind to update the asyncify state to "normal" from "unwind")
177183
asyncify_stop_rewind();
184+
// reset the stack pointer to what it was before the most recent call to try_f or catch_f
185+
rb_wasm_set_stack_pointer(try_catch->stack_pointer);
178186
// clear the active jmpbuf because it's already stopped
179187
_rb_wasm_active_jmpbuf = NULL;
180188
// reset jmpbuf state to be able to unwind again

wasm/setjmp.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ struct rb_wasm_try_catch {
6565
rb_wasm_try_catch_func_t try_f;
6666
rb_wasm_try_catch_func_t catch_f;
6767
void *context;
68+
void *stack_pointer;
6869
int state;
6970
};
7071

0 commit comments

Comments
 (0)