From 52805d233b59503cdfe58ee21e894ed6bb3b1da9 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 6 Jun 2017 11:34:10 -0700 Subject: [PATCH] std: Avoid panics in rust_eh_personality This commit removes a few calls to panic and/or assert in `rust_eh_personality`. This function definitely can't itself panic (that'd probably segfault or do something else weird) and I was also noticing that a `pub extern fn foo() {}` cdylib was abnormally large. Turns out all that size was the panicking machinery brought in by the personality function! The change here is to return a `Result` internally so we can bubble up the fatal error, eventually translating to the appropriate error code for the libunwind ABI. --- src/libpanic_unwind/dwarf/eh.rs | 55 +++++++++++++++++++------------- src/libpanic_unwind/gcc.rs | 14 ++++++-- src/libpanic_unwind/seh64_gnu.rs | 9 +++--- 3 files changed, 48 insertions(+), 30 deletions(-) diff --git a/src/libpanic_unwind/dwarf/eh.rs b/src/libpanic_unwind/dwarf/eh.rs index e7994f4e0ef0a..0c326ce371843 100644 --- a/src/libpanic_unwind/dwarf/eh.rs +++ b/src/libpanic_unwind/dwarf/eh.rs @@ -61,9 +61,11 @@ pub enum EHAction { pub const USING_SJLJ_EXCEPTIONS: bool = cfg!(all(target_os = "ios", target_arch = "arm")); -pub unsafe fn find_eh_action(lsda: *const u8, context: &EHContext) -> EHAction { +pub unsafe fn find_eh_action(lsda: *const u8, context: &EHContext) + -> Result +{ if lsda.is_null() { - return EHAction::None; + return Ok(EHAction::None) } let func_start = context.func_start; @@ -72,7 +74,7 @@ pub unsafe fn find_eh_action(lsda: *const u8, context: &EHContext) -> EHAction { let start_encoding = reader.read::(); // base address for landing pad offsets let lpad_base = if start_encoding != DW_EH_PE_omit { - read_encoded_pointer(&mut reader, context, start_encoding) + read_encoded_pointer(&mut reader, context, start_encoding)? } else { func_start }; @@ -90,9 +92,9 @@ pub unsafe fn find_eh_action(lsda: *const u8, context: &EHContext) -> EHAction { if !USING_SJLJ_EXCEPTIONS { while reader.ptr < action_table { - let cs_start = read_encoded_pointer(&mut reader, context, call_site_encoding); - let cs_len = read_encoded_pointer(&mut reader, context, call_site_encoding); - let cs_lpad = read_encoded_pointer(&mut reader, context, call_site_encoding); + let cs_start = read_encoded_pointer(&mut reader, context, call_site_encoding)?; + let cs_len = read_encoded_pointer(&mut reader, context, call_site_encoding)?; + let cs_lpad = read_encoded_pointer(&mut reader, context, call_site_encoding)?; let cs_action = reader.read_uleb128(); // Callsite table is sorted by cs_start, so if we've passed the ip, we // may stop searching. @@ -101,23 +103,23 @@ pub unsafe fn find_eh_action(lsda: *const u8, context: &EHContext) -> EHAction { } if ip < func_start + cs_start + cs_len { if cs_lpad == 0 { - return EHAction::None; + return Ok(EHAction::None) } else { let lpad = lpad_base + cs_lpad; - return interpret_cs_action(cs_action, lpad); + return Ok(interpret_cs_action(cs_action, lpad)) } } } // Ip is not present in the table. This should not happen... but it does: issue #35011. // So rather than returning EHAction::Terminate, we do this. - EHAction::None + Ok(EHAction::None) } else { // SjLj version: // The "IP" is an index into the call-site table, with two exceptions: // -1 means 'no-action', and 0 means 'terminate'. match ip as isize { - -1 => return EHAction::None, - 0 => return EHAction::Terminate, + -1 => return Ok(EHAction::None), + 0 => return Ok(EHAction::Terminate), _ => (), } let mut idx = ip; @@ -129,7 +131,7 @@ pub unsafe fn find_eh_action(lsda: *const u8, context: &EHContext) -> EHAction { // Can never have null landing pad for sjlj -- that would have // been indicated by a -1 call site index. let lpad = (cs_lpad + 1) as usize; - return interpret_cs_action(cs_action, lpad); + return Ok(interpret_cs_action(cs_action, lpad)) } } } @@ -144,21 +146,26 @@ fn interpret_cs_action(cs_action: u64, lpad: usize) -> EHAction { } #[inline] -fn round_up(unrounded: usize, align: usize) -> usize { - assert!(align.is_power_of_two()); - (unrounded + align - 1) & !(align - 1) +fn round_up(unrounded: usize, align: usize) -> Result { + if align.is_power_of_two() { + Ok((unrounded + align - 1) & !(align - 1)) + } else { + Err(()) + } } unsafe fn read_encoded_pointer(reader: &mut DwarfReader, context: &EHContext, encoding: u8) - -> usize { - assert!(encoding != DW_EH_PE_omit); + -> Result { + if encoding == DW_EH_PE_omit { + return Err(()) + } // DW_EH_PE_aligned implies it's an absolute pointer value if encoding == DW_EH_PE_aligned { - reader.ptr = round_up(reader.ptr as usize, mem::size_of::()) as *const u8; - return reader.read::(); + reader.ptr = round_up(reader.ptr as usize, mem::size_of::())? as *const u8; + return Ok(reader.read::()) } let mut result = match encoding & 0x0F { @@ -171,7 +178,7 @@ unsafe fn read_encoded_pointer(reader: &mut DwarfReader, DW_EH_PE_sdata2 => reader.read::() as usize, DW_EH_PE_sdata4 => reader.read::() as usize, DW_EH_PE_sdata8 => reader.read::() as usize, - _ => panic!(), + _ => return Err(()), }; result += match encoding & 0x70 { @@ -179,17 +186,19 @@ unsafe fn read_encoded_pointer(reader: &mut DwarfReader, // relative to address of the encoded value, despite the name DW_EH_PE_pcrel => reader.ptr as usize, DW_EH_PE_funcrel => { - assert!(context.func_start != 0); + if context.func_start == 0 { + return Err(()) + } context.func_start } DW_EH_PE_textrel => (*context.get_text_start)(), DW_EH_PE_datarel => (*context.get_data_start)(), - _ => panic!(), + _ => return Err(()), }; if encoding & DW_EH_PE_indirect != 0 { result = *(result as *const usize); } - result + Ok(result) } diff --git a/src/libpanic_unwind/gcc.rs b/src/libpanic_unwind/gcc.rs index 84abc6bc4a513..aadbeb96b2d23 100644 --- a/src/libpanic_unwind/gcc.rs +++ b/src/libpanic_unwind/gcc.rs @@ -156,7 +156,10 @@ unsafe extern "C" fn rust_eh_personality(version: c_int, if version != 1 { return uw::_URC_FATAL_PHASE1_ERROR; } - let eh_action = find_eh_action(context); + let eh_action = match find_eh_action(context) { + Ok(action) => action, + Err(_) => return uw::_URC_FATAL_PHASE1_ERROR, + }; if actions as i32 & uw::_UA_SEARCH_PHASE as i32 != 0 { match eh_action { EHAction::None | @@ -219,7 +222,10 @@ unsafe extern "C" fn rust_eh_personality(state: uw::_Unwind_State, // _Unwind_Context in our libunwind bindings and fetch the required data from there directly, // bypassing DWARF compatibility functions. - let eh_action = find_eh_action(context); + let eh_action = match find_eh_action(context) { + Ok(action) => action, + Err(_) => return uw::_URC_FAILURE, + }; if search_phase { match eh_action { EHAction::None | @@ -260,7 +266,9 @@ unsafe extern "C" fn rust_eh_personality(state: uw::_Unwind_State, } } -unsafe fn find_eh_action(context: *mut uw::_Unwind_Context) -> EHAction { +unsafe fn find_eh_action(context: *mut uw::_Unwind_Context) + -> Result +{ let lsda = uw::_Unwind_GetLanguageSpecificData(context) as *const u8; let mut ip_before_instr: c_int = 0; let ip = uw::_Unwind_GetIPInfo(context, &mut ip_before_instr); diff --git a/src/libpanic_unwind/seh64_gnu.rs b/src/libpanic_unwind/seh64_gnu.rs index d4906b556b31a..0a9fa7d9a80b4 100644 --- a/src/libpanic_unwind/seh64_gnu.rs +++ b/src/libpanic_unwind/seh64_gnu.rs @@ -128,9 +128,10 @@ unsafe fn find_landing_pad(dc: &c::DISPATCHER_CONTEXT) -> Option { get_data_start: &|| unimplemented!(), }; match find_eh_action(dc.HandlerData, &eh_ctx) { - EHAction::None => None, - EHAction::Cleanup(lpad) | - EHAction::Catch(lpad) => Some(lpad), - EHAction::Terminate => intrinsics::abort(), + Err(_) | + Ok(EHAction::None) => None, + Ok(EHAction::Cleanup(lpad)) | + Ok(EHAction::Catch(lpad)) => Some(lpad), + Ok(EHAction::Terminate) => intrinsics::abort(), } }