use crate::sym::SymbolTable;
const PANIC_SYMBOL: &str = "rust_panic$";
const UNWIND_SYMBOL: &str = "rust_begin_unwind$";
const ABORT_SYMBOL: &str = "std::process::abort";
const PANIC_SYMBOLS: &[&str] = &[PANIC_SYMBOL, UNWIND_SYMBOL];
const BINARY_PANIC_PREFIXES: &[&str] = &["core::panicking::"];
pub fn find_entry_points(symbols: &SymbolTable) -> Vec<(String, String, u64)> {
let mut entry_points = Vec::new();
let mut seen_addrs = std::collections::HashSet::new();
for pattern in PANIC_SYMBOLS {
if let Ok(Some((sym, dem))) = symbols.find_symbol_containing(pattern)
&& let Some(addr) = symbols.find_symbol_address(&sym)
&& seen_addrs.insert(addr)
{
entry_points.push((sym, dem, addr));
}
}
if let Ok(panic_symbols) = symbols.find_all_symbols_with_addresses(BINARY_PANIC_PREFIXES) {
for (sym, dem, addr) in panic_symbols {
if seen_addrs.insert(addr) {
entry_points.push((sym, dem, addr));
}
}
}
if let Ok(abort_symbols) = symbols.find_all_symbols_with_addresses(&[ABORT_SYMBOL]) {
for (sym, dem, addr) in abort_symbols {
if seen_addrs.insert(addr) {
entry_points.push((sym, dem, addr));
}
}
}
entry_points
}
const LIBRARY_PANIC_MODULE_PREFIXES: &[&str] = &["core::panicking::", "std::panicking::"];
const LIBRARY_PANIC_METHOD_SUFFIXES: &[&str] = &[
">::unwrap",
">::expect",
">::unwrap_err",
">::expect_err",
"::unwrap_failed",
"::expect_failed",
];
pub fn is_library_panic_symbol(name: &str) -> bool {
if LIBRARY_PANIC_MODULE_PREFIXES
.iter()
.any(|p| name.contains(p))
{
return !name.contains("set_hook") && !name.contains("take_hook");
}
LIBRARY_PANIC_METHOD_SUFFIXES
.iter()
.any(|s| name.ends_with(s))
}
pub fn is_panic_triggering_function(func_name: &str) -> bool {
detect_panic_cause(func_name).is_some() || func_name.contains("panic_fmt")
}
use crate::panic_cause::PanicCause;
#[derive(Clone, Copy)]
enum Match {
Contains,
EndsWith,
ContainsBoth(&'static str),
}
const PANIC_CAUSE_RULES: &[(&str, Match, PanicCause)] = &[
(
"async_fn_resumed",
Match::Contains,
PanicCause::AsyncFnResumed,
),
("panic_in_cleanup", Match::Contains, PanicCause::PanicInDrop),
(
"panic_cannot_unwind",
Match::Contains,
PanicCause::CannotUnwind,
),
("panic_nounwind", Match::Contains, PanicCause::CannotUnwind),
(
"panic_bounds_check",
Match::Contains,
PanicCause::BoundsCheck,
),
("slice_index_fail", Match::Contains, PanicCause::BoundsCheck),
(
"panic_const_add_overflow",
Match::Contains,
PanicCause::ArithmeticOverflow("addition"),
),
(
"panic_const_sub_overflow",
Match::Contains,
PanicCause::ArithmeticOverflow("subtraction"),
),
(
"panic_const_mul_overflow",
Match::Contains,
PanicCause::ArithmeticOverflow("multiplication"),
),
(
"panic_const_div_overflow",
Match::Contains,
PanicCause::ArithmeticOverflow("division"),
),
(
"panic_const_rem_overflow",
Match::Contains,
PanicCause::ArithmeticOverflow("remainder"),
),
(
"panic_const_neg_overflow",
Match::Contains,
PanicCause::ArithmeticOverflow("negation"),
),
(
"panic_const_shl_overflow",
Match::Contains,
PanicCause::ShiftOverflow("left"),
),
(
"panic_const_shr_overflow",
Match::Contains,
PanicCause::ShiftOverflow("right"),
),
(
"panic_const_div_by_zero",
Match::Contains,
PanicCause::DivisionByZero,
),
(
"panic_const_rem_by_zero",
Match::Contains,
PanicCause::DivisionByZero,
),
("unwrap_failed", Match::Contains, PanicCause::Unwrap),
(">::unwrap", Match::EndsWith, PanicCause::Unwrap),
(">::unwrap_err", Match::EndsWith, PanicCause::Unwrap),
("expect_failed", Match::Contains, PanicCause::Expect),
(">::expect", Match::EndsWith, PanicCause::Expect),
(">::expect_err", Match::EndsWith, PanicCause::Expect),
("assert_failed", Match::Contains, PanicCause::AssertFailed),
("panic_display", Match::Contains, PanicCause::ExplicitPanic),
(
"panic_misaligned_pointer_dereference",
Match::Contains,
PanicCause::MisalignedPointer,
),
(
"panic_invalid_enum_construction",
Match::Contains,
PanicCause::InvalidEnum,
),
("core::fmt::", Match::Contains, PanicCause::FormattingError),
("alloc::fmt::", Match::Contains, PanicCause::FormattingError),
("format_inner", Match::Contains, PanicCause::FormattingError),
("write_fmt", Match::Contains, PanicCause::FormattingError),
(
"capacity_overflow",
Match::Contains,
PanicCause::CapacityOverflow,
),
(
"handle_alloc_error",
Match::Contains,
PanicCause::OutOfMemory,
),
(
"alloc_error_handler",
Match::Contains,
PanicCause::OutOfMemory,
),
("alloc_error_hook", Match::Contains, PanicCause::OutOfMemory),
(
"slice_error_fail",
Match::Contains,
PanicCause::StringSliceError,
),
(
"str_index_overflow_fail",
Match::Contains,
PanicCause::StringSliceError,
),
(
"slice_start_index_overflow",
Match::Contains,
PanicCause::StringSliceError,
),
(
"slice_end_index_overflow",
Match::Contains,
PanicCause::StringSliceError,
),
(
"unreachable",
Match::ContainsBoth("panic"),
PanicCause::Unreachable,
),
(
"::fmt",
Match::ContainsBoth("Display"),
PanicCause::FormattingError,
),
(
"::fmt",
Match::ContainsBoth("Debug"),
PanicCause::FormattingError,
),
(
"raw_vec",
Match::ContainsBoth("grow"),
PanicCause::CapacityOverflow,
),
(
">::index",
Match::ContainsBoth("HashMap"),
PanicCause::KeyNotFound,
),
(
">::index",
Match::ContainsBoth("BTreeMap"),
PanicCause::KeyNotFound,
),
(
">::index",
Match::ContainsBoth("hash::map"),
PanicCause::KeyNotFound,
),
(
">::index",
Match::ContainsBoth("btree::map"),
PanicCause::KeyNotFound,
),
(
">::index",
Match::ContainsBoth("str::"),
PanicCause::StringSliceError,
),
(
"index<",
Match::ContainsBoth("str::"),
PanicCause::StringSliceError,
),
(">::index", Match::Contains, PanicCause::BoundsCheck),
("Index::index", Match::Contains, PanicCause::BoundsCheck),
("index<", Match::Contains, PanicCause::BoundsCheck),
(
"hashbrown::raw::",
Match::Contains,
PanicCause::CapacityOverflow,
),
(
"std::collections::hash::",
Match::Contains,
PanicCause::CapacityOverflow,
),
];
pub fn detect_panic_cause(func_name: &str) -> Option<PanicCause> {
for (pattern, kind, cause) in PANIC_CAUSE_RULES {
let matched = match kind {
Match::Contains => func_name.contains(pattern),
Match::EndsWith => func_name.ends_with(pattern),
Match::ContainsBoth(second) => {
func_name.contains(pattern) && func_name.contains(second)
}
};
if matched {
return Some(cause.clone());
}
}
None
}
#[cfg(test)]
mod tests {
use super::*;
use crate::panic_cause::PanicCause;
#[test]
fn test_panic_symbol_patterns_cover_key_entry_points() {
assert!(PANIC_SYMBOL.contains("panic"));
assert!(ABORT_SYMBOL.contains("abort"));
}
#[test]
fn test_is_library_panic_symbol() {
assert!(is_library_panic_symbol("core::panicking::panic_fmt"));
assert!(is_library_panic_symbol(
"core::panicking::panic_const::panic_const_add_overflow"
));
assert!(is_library_panic_symbol("std::panicking::begin_panic"));
assert!(!is_library_panic_symbol("std::panicking::set_hook"));
assert!(!is_library_panic_symbol("std::panicking::take_hook"));
assert!(!is_library_panic_symbol("my_crate::process_data"));
assert!(is_library_panic_symbol("core::option::Option<T>::unwrap"));
assert!(is_library_panic_symbol("core::option::Option<T>::expect"));
assert!(is_library_panic_symbol("core::result::Result<T,E>::unwrap"));
assert!(is_library_panic_symbol(
"core::result::Result<T,E>::unwrap_err"
));
assert!(is_library_panic_symbol(
"core::result::Result<T,E>::expect_err"
));
assert!(is_library_panic_symbol("core::option::unwrap_failed"));
assert!(is_library_panic_symbol("core::option::expect_failed"));
assert!(is_library_panic_symbol("core::result::unwrap_failed"));
assert!(!is_library_panic_symbol(
"core::option::Option<T>::unwrap_or"
));
assert!(!is_library_panic_symbol(
"core::option::Option<T>::unwrap_or_default"
));
assert!(!is_library_panic_symbol(
"core::option::Option<T>::unwrap_or_else"
));
assert!(!is_library_panic_symbol(
"core::result::Result<T,E>::unwrap_or"
));
assert!(!is_library_panic_symbol(
"core::result::Result<T,E>::unwrap_or_default"
));
assert!(!is_library_panic_symbol(
"core::result::Result<T,E>::unwrap_or_else"
));
}
#[test]
fn test_is_library_panic_symbol_unwrap_err_expect_err() {
assert!(is_library_panic_symbol("Result<T,E>::unwrap_err"));
assert!(is_library_panic_symbol("Result<T,E>::expect_err"));
}
#[test]
fn test_is_library_panic_symbol_expect_failed() {
assert!(is_library_panic_symbol("core::option::expect_failed"));
}
#[test]
fn test_detect_panic_cause_bounds_check() {
assert_eq!(
detect_panic_cause("panic_bounds_check"),
Some(PanicCause::BoundsCheck)
);
}
#[test]
fn test_detect_panic_cause_arithmetic_overflow() {
assert_eq!(
detect_panic_cause("panic_const_add_overflow"),
Some(PanicCause::ArithmeticOverflow("addition"))
);
assert_eq!(
detect_panic_cause("panic_const_sub_overflow"),
Some(PanicCause::ArithmeticOverflow("subtraction"))
);
assert_eq!(
detect_panic_cause("panic_const_mul_overflow"),
Some(PanicCause::ArithmeticOverflow("multiplication"))
);
}
#[test]
fn test_detect_panic_cause_shift_overflow() {
assert_eq!(
detect_panic_cause("panic_const_shl_overflow"),
Some(PanicCause::ShiftOverflow("left"))
);
assert_eq!(
detect_panic_cause("panic_const_shr_overflow"),
Some(PanicCause::ShiftOverflow("right"))
);
}
#[test]
fn test_detect_panic_cause_division_by_zero() {
assert_eq!(
detect_panic_cause("panic_const_div_by_zero"),
Some(PanicCause::DivisionByZero)
);
assert_eq!(
detect_panic_cause("panic_const_rem_by_zero"),
Some(PanicCause::DivisionByZero)
);
}
#[test]
fn test_detect_panic_cause_unwrap_failed() {
assert_eq!(
detect_panic_cause("unwrap_failed"),
Some(PanicCause::Unwrap)
);
assert_eq!(
detect_panic_cause("core::option::unwrap_failed"),
Some(PanicCause::Unwrap)
);
assert_eq!(
detect_panic_cause("core::result::unwrap_failed"),
Some(PanicCause::Unwrap)
);
}
#[test]
fn test_detect_panic_cause_unwrap_method() {
assert_eq!(
detect_panic_cause("core::option::Option<T>::unwrap"),
Some(PanicCause::Unwrap)
);
assert_eq!(
detect_panic_cause("core::result::Result<T,E>::unwrap"),
Some(PanicCause::Unwrap)
);
assert_eq!(
detect_panic_cause("core::result::Result<T,E>::unwrap_err"),
Some(PanicCause::Unwrap)
);
assert_ne!(
detect_panic_cause("core::option::Option<T>::unwrap_or"),
Some(PanicCause::Unwrap)
);
assert_ne!(
detect_panic_cause("core::option::Option<T>::unwrap_or_default"),
Some(PanicCause::Unwrap)
);
}
#[test]
fn test_detect_panic_cause_expect_failed() {
assert_eq!(
detect_panic_cause("expect_failed"),
Some(PanicCause::Expect)
);
}
#[test]
fn test_detect_panic_cause_expect_method() {
assert_eq!(
detect_panic_cause("core::result::Result<T,E>::expect"),
Some(PanicCause::Expect)
);
assert_eq!(
detect_panic_cause("core::result::Result<T,E>::expect_err"),
Some(PanicCause::Expect)
);
assert_eq!(
detect_panic_cause("core::option::Option<T>::expect"),
Some(PanicCause::Expect)
);
}
#[test]
fn test_detect_panic_cause_assert_failed() {
assert_eq!(
detect_panic_cause("assert_failed"),
Some(PanicCause::AssertFailed)
);
}
#[test]
fn test_detect_panic_cause_explicit_panic() {
assert_eq!(
detect_panic_cause("panic_display"),
Some(PanicCause::ExplicitPanic)
);
assert_eq!(detect_panic_cause("panic_fmt"), None);
}
#[test]
fn test_detect_panic_cause_safe_variants_return_none() {
assert_eq!(detect_panic_cause("unwrap_or_default"), None);
assert_eq!(
detect_panic_cause("core::option::Option<T>::unwrap_or"),
None
);
assert_eq!(
detect_panic_cause("core::option::Option<T>::unwrap_or_else"),
None
);
assert_eq!(detect_panic_cause("my_function"), None);
assert_eq!(detect_panic_cause("process_data"), None);
}
#[test]
fn test_detect_panic_cause_panic_in_cleanup() {
assert_eq!(
detect_panic_cause("panic_in_cleanup"),
Some(PanicCause::PanicInDrop)
);
}
#[test]
fn test_detect_panic_cause_panic_cannot_unwind() {
assert_eq!(
detect_panic_cause("panic_cannot_unwind"),
Some(PanicCause::CannotUnwind)
);
assert_eq!(
detect_panic_cause("panic_nounwind"),
Some(PanicCause::CannotUnwind)
);
}
#[test]
fn test_detect_panic_cause_formatting() {
assert_eq!(
detect_panic_cause("core::fmt::write"),
Some(PanicCause::FormattingError)
);
assert_eq!(
detect_panic_cause("write_fmt"),
Some(PanicCause::FormattingError)
);
}
#[test]
fn test_detect_panic_cause_capacity_overflow() {
assert_eq!(
detect_panic_cause("capacity_overflow"),
Some(PanicCause::CapacityOverflow)
);
}
#[test]
fn test_detect_panic_cause_out_of_memory() {
assert_eq!(
detect_panic_cause("handle_alloc_error"),
Some(PanicCause::OutOfMemory)
);
}
#[test]
fn test_detect_panic_cause_string_slice_error() {
assert_eq!(
detect_panic_cause("slice_error_fail"),
Some(PanicCause::StringSliceError)
);
assert_eq!(
detect_panic_cause("str_index_overflow_fail"),
Some(PanicCause::StringSliceError)
);
}
#[test]
fn test_detect_panic_cause_index_bounds() {
assert_eq!(
detect_panic_cause("index<T, usize>"),
Some(PanicCause::BoundsCheck)
);
assert_eq!(
detect_panic_cause("Index::index"),
Some(PanicCause::BoundsCheck)
);
}
#[test]
fn test_detect_panic_cause_index_string() {
assert_eq!(
detect_panic_cause("core::str::traits::<impl Index<I> for str>::index"),
Some(PanicCause::StringSliceError)
);
assert_eq!(
detect_panic_cause("str::index<Range>"),
Some(PanicCause::StringSliceError)
);
}
#[test]
fn test_detect_panic_cause_invalid_enum() {
assert_eq!(
detect_panic_cause("panic_invalid_enum_construction"),
Some(PanicCause::InvalidEnum)
);
}
#[test]
fn test_detect_panic_cause_misaligned_pointer() {
assert_eq!(
detect_panic_cause("panic_misaligned_pointer_dereference"),
Some(PanicCause::MisalignedPointer)
);
}
#[test]
fn test_detect_panic_cause_hashbrown_raw() {
assert_eq!(
detect_panic_cause("hashbrown::raw::TableLayout::calculate_layout_for"),
Some(PanicCause::CapacityOverflow)
);
assert_eq!(
detect_panic_cause("hashbrown::raw::RawTableInner::fallible_with_capacity"),
Some(PanicCause::CapacityOverflow)
);
assert_eq!(
detect_panic_cause("hashbrown::raw::RawTableInner::new_uninitialized"),
Some(PanicCause::CapacityOverflow)
);
}
#[test]
fn test_detect_panic_cause_std_collections_hash() {
assert_eq!(
detect_panic_cause("std::collections::hash::map::HashMap<K,V>::new"),
Some(PanicCause::CapacityOverflow)
);
assert_eq!(
detect_panic_cause("std::collections::hash::set::HashSet<T>::with_capacity"),
Some(PanicCause::CapacityOverflow)
);
}
#[test]
fn test_detect_hashbrown_capacity_overflow() {
assert_eq!(
detect_panic_cause("hashbrown::raw::Fallibility::capacity_overflow"),
Some(PanicCause::CapacityOverflow)
);
assert_eq!(
detect_panic_cause("hashbrown::raw::Fallibility::alloc_err"),
Some(PanicCause::CapacityOverflow)
);
}
#[test]
fn test_detect_panic_cause_unknown() {
assert_eq!(detect_panic_cause("some_random_function"), None);
}
#[test]
fn test_detect_panic_cause_unreachable() {
assert_eq!(
detect_panic_cause("unreachable_panic_handler"),
Some(PanicCause::Unreachable)
);
}
#[test]
fn test_detect_panic_cause_raw_vec_grow() {
assert_eq!(
detect_panic_cause("raw_vec::grow"),
Some(PanicCause::CapacityOverflow)
);
}
#[test]
fn test_detect_panic_cause_display_fmt() {
assert_eq!(
detect_panic_cause("Display::fmt"),
Some(PanicCause::FormattingError)
);
assert_eq!(
detect_panic_cause("Debug::fmt"),
Some(PanicCause::FormattingError)
);
}
#[test]
fn test_detect_panic_cause_async_fn_resumed() {
assert_eq!(
detect_panic_cause("panic_const_async_fn_resumed"),
Some(PanicCause::AsyncFnResumed)
);
assert_eq!(
detect_panic_cause("core::panicking::panic_const::panic_const_async_fn_resumed"),
Some(PanicCause::AsyncFnResumed)
);
assert_eq!(
detect_panic_cause("panic_const_async_fn_resumed_panic"),
Some(PanicCause::AsyncFnResumed)
);
}
#[test]
fn test_async_fn_resumed_is_panic_triggering() {
assert!(is_panic_triggering_function("panic_const_async_fn_resumed"));
assert!(is_panic_triggering_function(
"core::panicking::panic_const::panic_const_async_fn_resumed"
));
}
}