Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Fix memory leaks in WASI setjmp handler #12995

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Mar 31, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions eval_intern.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,11 +102,11 @@ extern int select_large_fdset(int, fd_set *, fd_set *, fd_set *, struct timeval
_tag.tag = Qundef; \
_tag.prev = _ec->tag; \
_tag.lock_rec = rb_ec_vm_lock_rec(_ec); \
rb_vm_tag_jmpbuf_init(&_tag.buf); \
rb_vm_tag_jmpbuf_init(&_tag);

#define EC_POP_TAG() \
_ec->tag = _tag.prev; \
rb_vm_tag_jmpbuf_deinit(&_tag.buf); \
rb_vm_tag_jmpbuf_deinit(&_tag); \
} while (0)

#define EC_TMPPOP_TAG() \
Expand Down
77 changes: 52 additions & 25 deletions vm_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -946,52 +946,79 @@ typedef void *rb_jmpbuf_t[5];
Therefore, we allocates the buffer on the heap on such
environments.
*/
typedef rb_jmpbuf_t *rb_vm_tag_jmpbuf_t;
typedef struct _rb_vm_tag_jmpbuf {
struct _rb_vm_tag_jmpbuf *next;
rb_jmpbuf_t buf;
} *rb_vm_tag_jmpbuf_t;

#define RB_VM_TAG_JMPBUF_GET(buf) (*buf)
#define RB_VM_TAG_JMPBUF_GET(jmpbuf) ((jmpbuf)->buf)
#else
typedef rb_jmpbuf_t rb_vm_tag_jmpbuf_t;

#define RB_VM_TAG_JMPBUF_GET(jmpbuf) (jmpbuf)
#endif

/*
the members which are written in EC_PUSH_TAG() should be placed at
the beginning and the end, so that entire region is accessible.
*/
struct rb_vm_tag {
VALUE tag;
VALUE retval;
rb_vm_tag_jmpbuf_t buf;
struct rb_vm_tag *prev;
enum ruby_tag_type state;
unsigned int lock_rec;
};

#if defined(__wasm__) && !defined(__EMSCRIPTEN__)
static inline void
_rb_vm_tag_jmpbuf_deinit_internal(rb_vm_tag_jmpbuf_t jmpbuf)
{
rb_vm_tag_jmpbuf_t buf = jmpbuf;
while (buf != NULL) {
rb_vm_tag_jmpbuf_t next = buf->next;
ruby_xfree(buf);
buf = next;
}
}

static inline void
rb_vm_tag_jmpbuf_init(rb_vm_tag_jmpbuf_t *jmpbuf)
rb_vm_tag_jmpbuf_init(struct rb_vm_tag *tag)
{
*jmpbuf = ruby_xmalloc(sizeof(rb_jmpbuf_t));
if (tag->prev != NULL && tag->prev->buf->next != NULL) {
_rb_vm_tag_jmpbuf_deinit_internal(tag->prev->buf->next);
tag->prev->buf->next = NULL;
}
tag->buf = ruby_xmalloc(sizeof *tag->buf);
tag->buf->next = NULL;
if (tag->prev != NULL) {
tag->prev->buf->next = tag->buf;
}
}

static inline void
rb_vm_tag_jmpbuf_deinit(const rb_vm_tag_jmpbuf_t *jmpbuf)
rb_vm_tag_jmpbuf_deinit(struct rb_vm_tag *tag)
{
ruby_xfree(*jmpbuf);
if (tag->prev != NULL) {
tag->prev->buf->next = NULL;
}
_rb_vm_tag_jmpbuf_deinit_internal(tag->buf);
}
#else
typedef rb_jmpbuf_t rb_vm_tag_jmpbuf_t;

#define RB_VM_TAG_JMPBUF_GET(buf) (buf)

static inline void
rb_vm_tag_jmpbuf_init(rb_vm_tag_jmpbuf_t *jmpbuf)
rb_vm_tag_jmpbuf_init(struct rb_vm_tag *tag)
{
// no-op
}

static inline void
rb_vm_tag_jmpbuf_deinit(const rb_vm_tag_jmpbuf_t *jmpbuf)
rb_vm_tag_jmpbuf_deinit(struct rb_vm_tag *tag)
{
// no-op
}
#endif

/*
the members which are written in EC_PUSH_TAG() should be placed at
the beginning and the end, so that entire region is accessible.
*/
struct rb_vm_tag {
VALUE tag;
VALUE retval;
rb_vm_tag_jmpbuf_t buf;
struct rb_vm_tag *prev;
enum ruby_tag_type state;
unsigned int lock_rec;
};

STATIC_ASSERT(rb_vm_tag_buf_offset, offsetof(struct rb_vm_tag, buf) > 0);
STATIC_ASSERT(rb_vm_tag_buf_end,
offsetof(struct rb_vm_tag, buf) + sizeof(rb_vm_tag_jmpbuf_t) <
Expand Down
8 changes: 8 additions & 0 deletions wasm/setjmp.c
Original file line number Diff line number Diff line change
Expand Up @@ -143,9 +143,11 @@ rb_wasm_try_catch_init(struct rb_wasm_try_catch *try_catch,
try_catch->try_f = try_f;
try_catch->catch_f = catch_f;
try_catch->context = context;
try_catch->stack_pointer = NULL;
}

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

target->state = JMP_BUF_STATE_CAPTURED;

if (try_catch->stack_pointer == NULL) {
try_catch->stack_pointer = rb_wasm_get_stack_pointer();
}

switch ((enum try_catch_phase)try_catch->state) {
case TRY_CATCH_PHASE_MAIN:
// may unwind
Expand All @@ -175,6 +181,8 @@ rb_wasm_try_catch_loop_run(struct rb_wasm_try_catch *try_catch, rb_wasm_jmp_buf
// stop unwinding
// (but call stop_rewind to update the asyncify state to "normal" from "unwind")
asyncify_stop_rewind();
// reset the stack pointer to what it was before the most recent call to try_f or catch_f
rb_wasm_set_stack_pointer(try_catch->stack_pointer);
// clear the active jmpbuf because it's already stopped
_rb_wasm_active_jmpbuf = NULL;
// reset jmpbuf state to be able to unwind again
Expand Down
1 change: 1 addition & 0 deletions wasm/setjmp.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ struct rb_wasm_try_catch {
rb_wasm_try_catch_func_t try_f;
rb_wasm_try_catch_func_t catch_f;
void *context;
void *stack_pointer;
int state;
};

Expand Down
Loading