diff --git a/CHANGELOG.md b/CHANGELOG.md index 398efdd6..fa6f2b8e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] + +### Fixed +- Handle potentially unaligned frame pointer (#217) + ## [0.12.0] - 2023-07-03 ### Added diff --git a/src/backtrace/frame_pointer.rs b/src/backtrace/frame_pointer.rs index e8c57c4a..e68ab9ec 100644 --- a/src/backtrace/frame_pointer.rs +++ b/src/backtrace/frame_pointer.rs @@ -36,6 +36,20 @@ impl super::Frame for Frame { } } +/// helper to read a pointer from a potentially unaligned address +/// +/// # Safety +/// +/// Same as [`std::ptr::read_unaligned`] +#[inline] +unsafe fn read_ptr(ptr: *const T) -> T { + if ptr.align_offset(std::mem::align_of::()) == 0 { + std::ptr::read(ptr) + } else { + std::ptr::read_unaligned(ptr) + } +} + pub struct Trace {} impl super::Trace for Trace { type Frame = Frame; @@ -104,13 +118,13 @@ impl super::Trace for Trace { // iterate to the next frame let frame = Frame { - ip: unsafe { (*frame_pointer).ret }, + ip: unsafe { read_ptr(frame_pointer).ret }, }; if !cb(&frame) { break; } - frame_pointer = unsafe { (*frame_pointer).frame_pointer }; + frame_pointer = unsafe { read_ptr(frame_pointer).frame_pointer }; } } } @@ -120,3 +134,33 @@ struct FramePointerLayout { frame_pointer: *mut FramePointerLayout, ret: usize, } + +#[cfg(test)] +mod tests { + use super::*; + + #[repr(C, align(64))] + struct AlignToSixtyFour([u8; 64]); + + impl Default for AlignToSixtyFour { + fn default() -> Self { + AlignToSixtyFour([0; 64]) + } + } + + #[test] + fn test_read_ptr_aligned() { + let x = AlignToSixtyFour::default(); + let ptr = &x.0[0] as *const u8; + assert_eq!(unsafe { read_ptr(ptr) }, x.0[0]); + } + + #[test] + fn test_read_ptr_unaligned() { + let mut x = AlignToSixtyFour::default(); + let expected = usize::MAX / 2; + x.0[1..9].copy_from_slice(&expected.to_ne_bytes()[..]); + let ptr: *const usize = unsafe { std::mem::transmute(&x.0[1] as *const u8) }; + assert_eq!(unsafe { read_ptr(ptr) }, expected); + } +}