Skip to content

Commit 199877d

Browse files
committed
YJIT: Abandon block when gen_outlined_exit() fails
When CodeBlock::set_page fails (part of next_page(), see their docs for exact conditions), it can cause gen_outlined_exit() to fail while there is still plenty of memory available. Previously, this can have YJIT running incomplete code due to taking the early return in end_block_with_jump() that manifested as crashes with SIGILL. Add and use a wrapper with error handling.
1 parent 2df2e86 commit 199877d

File tree

1 file changed

+18
-3
lines changed

1 file changed

+18
-3
lines changed

yjit/src/codegen.rs

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,17 @@ impl<'a> JITState<'a> {
223223
}
224224
}
225225

226+
/// Wrapper for [self::gen_outlined_exit] with error handling.
227+
fn gen_outlined_exit(&mut self, exit_pc: *mut VALUE, ctx: &Context) -> Option<CodePtr> {
228+
let result = gen_outlined_exit(exit_pc, self.num_locals(), ctx, self.get_ocb());
229+
if result.is_none() {
230+
// When we can't have the exits, the code is incomplete and we have to bail.
231+
self.block_abandoned = true;
232+
}
233+
234+
result
235+
}
236+
226237
/// Return true if the current ISEQ could escape an environment.
227238
///
228239
/// As of vm_push_frame(), EP is always equal to BP. However, after pushing
@@ -879,6 +890,10 @@ fn gen_exit(exit_pc: *mut VALUE, asm: &mut Assembler) {
879890
/// moment, so there is one unique side exit for each context. Note that
880891
/// it's incorrect to jump to the side exit after any ctx stack push operations
881892
/// since they change the logic required for reconstructing interpreter state.
893+
///
894+
/// If you're in [the codegen module][self], use [JITState::gen_outlined_exit]
895+
/// instead of calling this directly.
896+
#[must_use]
882897
pub fn gen_outlined_exit(exit_pc: *mut VALUE, num_locals: u32, ctx: &Context, ocb: &mut OutlinedCb) -> Option<CodePtr> {
883898
let mut cb = ocb.unwrap();
884899
let mut asm = Assembler::new(num_locals);
@@ -943,7 +958,7 @@ pub fn jit_ensure_block_entry_exit(jit: &mut JITState, asm: &mut Assembler) -> O
943958
jit.block_entry_exit = Some(entry_exit?);
944959
} else {
945960
let block_entry_pc = unsafe { rb_iseq_pc_at_idx(jit.iseq, jit.starting_insn_idx.into()) };
946-
jit.block_entry_exit = Some(gen_outlined_exit(block_entry_pc, jit.num_locals(), block_starting_context, jit.get_ocb())?);
961+
jit.block_entry_exit = Some(jit.gen_outlined_exit(block_entry_pc, block_starting_context)?);
947962
}
948963

949964
Some(())
@@ -1201,7 +1216,7 @@ fn end_block_with_jump(
12011216
if jit.record_boundary_patch_point {
12021217
jit.record_boundary_patch_point = false;
12031218
let exit_pc = unsafe { rb_iseq_pc_at_idx(jit.iseq, continuation_insn_idx.into())};
1204-
let exit_pos = gen_outlined_exit(exit_pc, jit.num_locals(), &reset_depth, jit.get_ocb());
1219+
let exit_pos = jit.gen_outlined_exit(exit_pc, &reset_depth);
12051220
record_global_inval_patch(asm, exit_pos?);
12061221
}
12071222

@@ -1310,7 +1325,7 @@ pub fn gen_single_block(
13101325
// If previous instruction requested to record the boundary
13111326
if jit.record_boundary_patch_point {
13121327
// Generate an exit to this instruction and record it
1313-
let exit_pos = gen_outlined_exit(jit.pc, jit.num_locals(), &asm.ctx, jit.get_ocb()).ok_or(())?;
1328+
let exit_pos = jit.gen_outlined_exit(jit.pc, &asm.ctx).ok_or(())?;
13141329
record_global_inval_patch(&mut asm, exit_pos);
13151330
jit.record_boundary_patch_point = false;
13161331
}

0 commit comments

Comments
 (0)