Push a real iseq in rb_vm_push_frame_fname()

Previously, vm_make_env_each() (used during proc
creation and for the debug inspector C API) picked up the
non-GC-allocated iseq that rb_vm_push_frame_fname() creates,
which led to a SEGV when the GC tried to mark the non GC object.

Put a real iseq imemo instead. Speed should be about the same since
the old code also did a imemo allocation and a malloc allocation.

Real iseq allows ironing out the special-casing of dummy frames in
rb_execution_context_mark() and rb_execution_context_update(). A check
is added to RubyVM::ISeq#eval, though, to stop attempts to run dummy
iseqs.

[Bug #21180]

Co-authored-by: Aaron Patterson <tenderlove@ruby-lang.org>
This commit is contained in:
Alan Wu 2025-03-10 22:37:44 -04:00
parent 9b9661883b
commit 08b3a45bc9
Notes: git 2025-03-12 19:00:45 +00:00
4 changed files with 57 additions and 38 deletions

19
iseq.c
View file

@ -531,6 +531,19 @@ rb_iseq_pathobj_set(const rb_iseq_t *iseq, VALUE path, VALUE realpath)
rb_iseq_pathobj_new(path, realpath)); rb_iseq_pathobj_new(path, realpath));
} }
// Make a dummy iseq for a dummy frame that exposes a path for profilers to inspect
rb_iseq_t *
rb_iseq_alloc_with_dummy_path(VALUE fname)
{
rb_iseq_t *dummy_iseq = iseq_alloc();
ISEQ_BODY(dummy_iseq)->type = ISEQ_TYPE_TOP;
RB_OBJ_WRITE(dummy_iseq, &ISEQ_BODY(dummy_iseq)->location.pathobj, fname);
RB_OBJ_WRITE(dummy_iseq, &ISEQ_BODY(dummy_iseq)->location.label, fname);
return dummy_iseq;
}
static rb_iseq_location_t * static rb_iseq_location_t *
iseq_location_setup(rb_iseq_t *iseq, VALUE name, VALUE path, VALUE realpath, int first_lineno, const rb_code_location_t *code_location, const int node_id) iseq_location_setup(rb_iseq_t *iseq, VALUE name, VALUE path, VALUE realpath, int first_lineno, const rb_code_location_t *code_location, const int node_id)
{ {
@ -1909,7 +1922,11 @@ rb_iseqw_to_iseq(VALUE iseqw)
static VALUE static VALUE
iseqw_eval(VALUE self) iseqw_eval(VALUE self)
{ {
return rb_iseq_eval(iseqw_check(self)); const rb_iseq_t *iseq = iseqw_check(self);
if (0 == ISEQ_BODY(iseq)->iseq_size) {
rb_raise(rb_eTypeError, "attempt to evaluate dummy InstructionSequence");
}
return rb_iseq_eval(iseq);
} }
/* /*

View file

@ -139,6 +139,19 @@ class TestFiberScheduler < Test::Unit::TestCase
end end
end end
def test_iseq_compile_under_gc_stress_bug_21180
Thread.new do
scheduler = Scheduler.new
Fiber.set_scheduler scheduler
Fiber.schedule do
EnvUtil.under_gc_stress do
RubyVM::InstructionSequence.compile_file(File::NULL)
end
end
end.join
end
def test_deadlock def test_deadlock
mutex = Thread::Mutex.new mutex = Thread::Mutex.new
condition = Thread::ConditionVariable.new condition = Thread::ConditionVariable.new

50
vm.c
View file

@ -3362,22 +3362,20 @@ rb_execution_context_update(rb_execution_context_t *ec)
} }
while (cfp != limit_cfp) { while (cfp != limit_cfp) {
if (VM_FRAME_TYPE(cfp) != VM_FRAME_MAGIC_DUMMY) { const VALUE *ep = cfp->ep;
const VALUE *ep = cfp->ep; cfp->self = rb_gc_location(cfp->self);
cfp->self = rb_gc_location(cfp->self); cfp->iseq = (rb_iseq_t *)rb_gc_location((VALUE)cfp->iseq);
cfp->iseq = (rb_iseq_t *)rb_gc_location((VALUE)cfp->iseq); cfp->block_code = (void *)rb_gc_location((VALUE)cfp->block_code);
cfp->block_code = (void *)rb_gc_location((VALUE)cfp->block_code);
if (!VM_ENV_LOCAL_P(ep)) { if (!VM_ENV_LOCAL_P(ep)) {
const VALUE *prev_ep = VM_ENV_PREV_EP(ep); const VALUE *prev_ep = VM_ENV_PREV_EP(ep);
if (VM_ENV_FLAGS(prev_ep, VM_ENV_FLAG_ESCAPED)) { if (VM_ENV_FLAGS(prev_ep, VM_ENV_FLAG_ESCAPED)) {
VM_FORCE_WRITE(&prev_ep[VM_ENV_DATA_INDEX_ENV], rb_gc_location(prev_ep[VM_ENV_DATA_INDEX_ENV])); VM_FORCE_WRITE(&prev_ep[VM_ENV_DATA_INDEX_ENV], rb_gc_location(prev_ep[VM_ENV_DATA_INDEX_ENV]));
} }
if (VM_ENV_FLAGS(ep, VM_ENV_FLAG_ESCAPED)) { if (VM_ENV_FLAGS(ep, VM_ENV_FLAG_ESCAPED)) {
VM_FORCE_WRITE(&ep[VM_ENV_DATA_INDEX_ENV], rb_gc_location(ep[VM_ENV_DATA_INDEX_ENV])); VM_FORCE_WRITE(&ep[VM_ENV_DATA_INDEX_ENV], rb_gc_location(ep[VM_ENV_DATA_INDEX_ENV]));
VM_FORCE_WRITE(&ep[VM_ENV_DATA_INDEX_ME_CREF], rb_gc_location(ep[VM_ENV_DATA_INDEX_ME_CREF])); VM_FORCE_WRITE(&ep[VM_ENV_DATA_INDEX_ME_CREF], rb_gc_location(ep[VM_ENV_DATA_INDEX_ME_CREF]));
}
} }
} }
@ -3413,21 +3411,19 @@ rb_execution_context_mark(const rb_execution_context_t *ec)
const VALUE *ep = cfp->ep; const VALUE *ep = cfp->ep;
VM_ASSERT(!!VM_ENV_FLAGS(ep, VM_ENV_FLAG_ESCAPED) == vm_ep_in_heap_p_(ec, ep)); VM_ASSERT(!!VM_ENV_FLAGS(ep, VM_ENV_FLAG_ESCAPED) == vm_ep_in_heap_p_(ec, ep));
if (VM_FRAME_TYPE(cfp) != VM_FRAME_MAGIC_DUMMY) { rb_gc_mark_movable(cfp->self);
rb_gc_mark_movable(cfp->self); rb_gc_mark_movable((VALUE)cfp->iseq);
rb_gc_mark_movable((VALUE)cfp->iseq); rb_gc_mark_movable((VALUE)cfp->block_code);
rb_gc_mark_movable((VALUE)cfp->block_code);
if (!VM_ENV_LOCAL_P(ep)) { if (!VM_ENV_LOCAL_P(ep)) {
const VALUE *prev_ep = VM_ENV_PREV_EP(ep); const VALUE *prev_ep = VM_ENV_PREV_EP(ep);
if (VM_ENV_FLAGS(prev_ep, VM_ENV_FLAG_ESCAPED)) { if (VM_ENV_FLAGS(prev_ep, VM_ENV_FLAG_ESCAPED)) {
rb_gc_mark_movable(prev_ep[VM_ENV_DATA_INDEX_ENV]); rb_gc_mark_movable(prev_ep[VM_ENV_DATA_INDEX_ENV]);
} }
if (VM_ENV_FLAGS(ep, VM_ENV_FLAG_ESCAPED)) { if (VM_ENV_FLAGS(ep, VM_ENV_FLAG_ESCAPED)) {
rb_gc_mark_movable(ep[VM_ENV_DATA_INDEX_ENV]); rb_gc_mark_movable(ep[VM_ENV_DATA_INDEX_ENV]);
rb_gc_mark(ep[VM_ENV_DATA_INDEX_ME_CREF]); rb_gc_mark(ep[VM_ENV_DATA_INDEX_ME_CREF]);
}
} }
} }

View file

@ -469,15 +469,8 @@ rb_vm_pop_frame(rb_execution_context_t *ec)
VALUE VALUE
rb_vm_push_frame_fname(rb_execution_context_t *ec, VALUE fname) rb_vm_push_frame_fname(rb_execution_context_t *ec, VALUE fname)
{ {
VALUE tmpbuf = rb_imemo_tmpbuf_auto_free_pointer(); rb_iseq_t *rb_iseq_alloc_with_dummy_path(VALUE fname);
void *ptr = ruby_xcalloc(sizeof(struct rb_iseq_constant_body) + sizeof(struct rb_iseq_struct), 1); rb_iseq_t *dmy_iseq = rb_iseq_alloc_with_dummy_path(fname);
rb_imemo_tmpbuf_set_ptr(tmpbuf, ptr);
struct rb_iseq_struct *dmy_iseq = (struct rb_iseq_struct *)ptr;
struct rb_iseq_constant_body *dmy_body = (struct rb_iseq_constant_body *)&dmy_iseq[1];
dmy_iseq->body = dmy_body;
dmy_body->type = ISEQ_TYPE_TOP;
dmy_body->location.pathobj = fname;
vm_push_frame(ec, vm_push_frame(ec,
dmy_iseq, //const rb_iseq_t *iseq, dmy_iseq, //const rb_iseq_t *iseq,
@ -490,7 +483,7 @@ rb_vm_push_frame_fname(rb_execution_context_t *ec, VALUE fname)
0, // int local_size, 0, // int local_size,
0); // int stack_max 0); // int stack_max
return tmpbuf; return (VALUE)dmy_iseq;
} }
/* method dispatch */ /* method dispatch */