Skip to content

Commit 056e7f5

Browse files
committed
YJIT: Prevent making a branch from a dead block to a live block
I'm seeing some memory corruption in the wild on blocks in `IseqPayload::dead_blocks`. While I unfortunately can't recreate the issue, (For all I know, it could be some external code corrupting YJIT's memory.) establishing a link between dead blocks and live blocks seems fishy enough that we ought to prevent it. When it did happen, it might've had bad interacts with Code GC and the optimization to immediately free empty blocks.
1 parent deba4d3 commit 056e7f5

1 file changed

Lines changed: 15 additions & 7 deletions

File tree

yjit/src/core.rs

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3591,6 +3591,13 @@ fn branch_stub_hit_body(branch_ptr: *const c_void, target_idx: u32, ec: EcPtr) -
35913591
return CodegenGlobals::get_stub_exit_code().raw_ptr(cb);
35923592
}
35933593

3594+
// Bail if this branch is housed in an invalidated (dead) block.
3595+
// This only happens in rare invalidation scenarios and we need
3596+
// to avoid linking a dead block to a live block with a branch.
3597+
if branch.block.get().as_ref().iseq.get().is_null() {
3598+
return CodegenGlobals::get_stub_exit_code().raw_ptr(cb);
3599+
}
3600+
35943601
(cfp, original_interp_sp)
35953602
};
35963603

@@ -4297,25 +4304,26 @@ pub fn invalidate_block_version(blockref: &BlockRef) {
42974304
incr_counter!(invalidation_count);
42984305
}
42994306

4300-
// We cannot deallocate blocks immediately after invalidation since there
4301-
// could be stubs waiting to access branch pointers. Return stubs can do
4302-
// this since patching the code for setting up return addresses does not
4303-
// affect old return addresses that are already set up to use potentially
4304-
// invalidated branch pointers. Example:
4307+
// We cannot deallocate blocks immediately after invalidation since patching the code for setting
4308+
// up return addresses does not affect outstanding return addresses that are on stack and will use
4309+
// invalidated branch pointers when hit. Example:
43054310
// def foo(n)
43064311
// if n == 2
43074312
// # 1.times.each to create a cfunc frame to preserve the JIT frame
43084313
// # which will return to a stub housed in an invalidated block
43094314
// return 1.times.each { Object.define_method(:foo) {} }
43104315
// end
43114316
//
4312-
// foo(n + 1)
4317+
// foo(n + 1) # The block for this call houses the return branch stub
43134318
// end
43144319
// p foo(1)
43154320
pub fn delayed_deallocation(blockref: BlockRef) {
43164321
block_assumptions_free(blockref);
43174322

4318-
let payload = get_iseq_payload(unsafe { blockref.as_ref() }.iseq.get()).unwrap();
4323+
let block = unsafe { blockref.as_ref() };
4324+
// Set null ISEQ on the block to signal that it's dead.
4325+
let iseq = block.iseq.replace(ptr::null());
4326+
let payload = get_iseq_payload(iseq).unwrap();
43194327
payload.dead_blocks.push(blockref);
43204328
}
43214329

0 commit comments

Comments
 (0)