symbolic_common/
heuristics.rs

1//! Heuristics for correcting instruction pointers based on the CPU architecture.
2
3use crate::types::{Arch, CpuFamily};
4
5const SIGILL: u32 = 4;
6const SIGBUS: u32 = 10;
7const SIGSEGV: u32 = 11;
8
9/// Helper to work with instruction addresses.
10///
11/// Directly symbolicated stack traces may show the wrong calling symbols, as the stack frame's
12/// return addresses point a few bytes past the original call site, which may place the address
13/// within a different symbol entirely.
14///
15/// The most useful function is [`caller_address`], which applies some heuristics to determine the
16/// call site of a function call based on the return address.
17///
18/// # Examples
19///
20/// ```
21/// use symbolic_common::{Arch, InstructionInfo};
22///
23/// const SIGSEGV: u32 = 11;
24///
25/// let caller_address = InstructionInfo::new(Arch::Arm64, 0x1337)
26///     .is_crashing_frame(false)
27///     .signal(Some(SIGSEGV))
28///     .ip_register_value(Some(0x4242))
29///     .caller_address();
30///
31/// assert_eq!(caller_address, 0x1330);
32/// ```
33///
34/// # Background
35///
36/// When *calling* a function, it is necessary for the *called* function to know where it should
37/// return to upon completion. To support this, a *return address* is supplied as part of the
38/// standard function call semantics. This return address specifies the instruction that the called
39/// function should jump to upon completion of its execution.
40///
41/// When a crash reporter generates a backtrace, it first collects the thread state of all active
42/// threads, including the **actual** current execution address. The reporter then iterates over
43/// those threads, walking backwards to find calling frames – what it's actually finding during this
44/// process are the **return addresses**. The actual address of the call instruction is not recorded
45/// anywhere. The only address available is the address at which execution should resume after
46/// function return.
47///
48/// To make things more complicated, there is no guarantee that a return address be set to exactly
49/// one instruction after the call. It's entirely proper for a function to remove itself from the
50/// call stack by setting a different return address entirely. This is why you never see
51/// `objc_msgSend` in your backtrace unless you actually crash inside of `objc_msgSend`. When
52/// `objc_msgSend` jumps to a method's implementation, it leaves its caller's return address in
53/// place, and `objc_msgSend` itself disappears from the stack trace. In the case of `objc_msgSend`,
54/// the loss of that information is of no great importance, but it's hardly the only function that
55/// elides its own code from the return address.
56///
57/// # Heuristics
58///
59/// To resolve this particular issue, it is necessary for the symbolication implementor to apply a
60/// per-architecture heuristics to the return addresses, and thus derive the **likely** address of
61/// the actual calling instruction. There is a high probability of correctness, but absolutely no
62/// guarantee.
63///
64/// This derived address **should** be used as the symbolication address, but **should not** replace
65/// the return address in the crash report. This derived address is a best guess, and if you replace
66/// the return address in the report, the end-user will have lost access to the original canonical
67/// data from which they could have made their own assessment.
68///
69/// These heuristics must not be applied to frame #0 on any thread. The first frame of all threads
70/// contains the actual register state of that thread at the time that it crashed (if it's the
71/// crashing thread), or at the time it was suspended (if it is a non-crashing thread). These
72/// heuristics should only be applied to frames *after* frame #0 – that is, starting with frame #1.
73///
74/// Additionally, these heuristics assume that your symbolication implementation correctly handles
75/// addresses that occur within an instruction, rather than directly at the start of a valid
76/// instruction. This should be the case for any reasonable implementation, but is something to be
77/// aware of when deploying these changes.
78///
79/// ## x86 and x86-64
80///
81/// x86 uses variable-width instruction encodings; subtract one byte from the return address to
82/// derive an address that should be within the calling instruction. This will provide an address
83/// within a calling instruction found directly prior to the return address.
84///
85/// ## ARMv6 and ARMv7
86///
87/// - **Step 1:** Strip the low order thumb bit from the return address. ARM uses the low bit to
88///   inform the processor that it should enter thumb mode when jumping to the return address. Since
89///   all instructions are at least 2 byte aligned, an actual instruction address will never have
90///   the low bit set.
91///
92/// - **Step 2:** Subtract 2 Bytes. 32-bit ARM instructions are either 2 or 4 bytes long, depending
93///   on the use of thumb. This will place the symbolication address within the likely calling
94///   instruction. All ARM64 instructions are 4 bytes long; subtract 4 bytes from the return address
95///   to derive the likely address of the calling instruction.
96///
97/// # More Information
98///
99/// The above information was taken and slightly updated from the now-gone *PLCrashReporter Wiki*.
100/// An old copy can still be found in the [internet archive].
101///
102/// [internet archive]: https://web.archive.org/web/20161012225323/https://opensource.plausible.coop/wiki/display/PLCR/Automated+Crash+Report+Analysis
103/// [`caller_address`]: struct.InstructionInfo.html#method.caller_address
104#[derive(Clone, Debug)]
105pub struct InstructionInfo {
106    addr: u64,
107    arch: Arch,
108    crashing_frame: bool,
109    signal: Option<u32>,
110    ip_reg: Option<u64>,
111}
112
113impl InstructionInfo {
114    /// Creates a new instruction info instance.
115    ///
116    /// By default, the frame is not marked as *crashing frame*. The signal and instruction pointer
117    /// register value are empty.
118    ///
119    /// # Examples
120    ///
121    /// ```
122    /// use symbolic_common::{Arch, InstructionInfo};
123    ///
124    /// let caller_address = InstructionInfo::new(Arch::X86, 0x1337)
125    ///     .caller_address();
126    /// ```
127    pub fn new(arch: Arch, instruction_address: u64) -> Self {
128        Self {
129            arch,
130            addr: instruction_address,
131            crashing_frame: false,
132            signal: None,
133            ip_reg: None,
134        }
135    }
136
137    /// Marks this as the crashing frame.
138    ///
139    /// The crashing frame is the first frame yielded by the stack walker. In such a frame, the
140    /// instruction address is the location of the direct crash. This is used by
141    /// [`should_adjust_caller`] to determine which frames need caller address adjustment.
142    ///
143    /// Defaults to `false`.
144    ///
145    /// [`should_adjust_caller`]: struct.InstructionInfo.html#method.should_adjust_caller
146    pub fn is_crashing_frame(&mut self, flag: bool) -> &mut Self {
147        self.crashing_frame = flag;
148        self
149    }
150
151    /// Sets a POSIX signal number.
152    ///
153    /// The signal number is used by [`should_adjust_caller`] to determine which frames need caller
154    /// address adjustment.
155    ///
156    /// [`should_adjust_caller`]: struct.InstructionInfo.html#method.should_adjust_caller
157    pub fn signal(&mut self, signal: Option<u32>) -> &mut Self {
158        self.signal = signal;
159        self
160    }
161
162    /// Sets the value of the instruction pointer register.
163    ///
164    /// This should be the original register value at the time of the crash, and not a restored
165    /// register value. This is used by [`should_adjust_caller`] to determine which frames need
166    /// caller address adjustment.
167    ///
168    /// [`should_adjust_caller`]: struct.InstructionInfo.html#method.should_adjust_caller
169    pub fn ip_register_value(&mut self, value: Option<u64>) -> &mut Self {
170        self.ip_reg = value;
171        self
172    }
173
174    /// Tries to resolve the start address of the current instruction.
175    ///
176    /// For architectures without fixed alignment (such as Intel with variable instruction lengths),
177    /// this will return the same address. Otherwise, the address is aligned to the architecture's
178    /// instruction alignment.
179    ///
180    /// # Examples
181    ///
182    /// For example, on 64-bit ARM, addresses are aligned at 4 byte boundaries. This applies to all
183    /// 64-bit ARM variants, even unknown ones:
184    ///
185    /// ```
186    /// use symbolic_common::{Arch, InstructionInfo};
187    ///
188    /// let info = InstructionInfo::new(Arch::Arm64, 0x1337);
189    /// assert_eq!(info.aligned_address(), 0x1334);
190    /// ```
191    pub fn aligned_address(&self) -> u64 {
192        if let Some(alignment) = self.arch.cpu_family().instruction_alignment() {
193            self.addr - (self.addr % alignment)
194        } else {
195            self.addr
196        }
197    }
198
199    /// Returns the instruction preceding the current one.
200    ///
201    /// For known architectures, this will return the start address of the instruction immediately
202    /// before the current one in the machine code. This is likely the instruction that was just
203    /// executed or that called a function returning at the current address.
204    ///
205    /// For unknown architectures or those using variable instruction size, the exact start address
206    /// cannot be determined. Instead, an address *within* the preceding instruction will be
207    /// returned. For this reason, the return value of this function should be considered an upper
208    /// bound.
209    ///
210    /// # Examples
211    ///
212    /// On 64-bit ARM, instructions have 4 bytes in size. The previous address is therefore 4 bytes
213    /// before the start of the current instruction (returned by [`aligned_address`]):
214    ///
215    /// ```
216    /// use symbolic_common::{Arch, InstructionInfo};
217    ///
218    /// let info = InstructionInfo::new(Arch::Arm64, 0x1337);
219    /// assert_eq!(info.previous_address(), 0x1330);
220    /// ```
221    ///
222    /// On the contrary, Intel uses variable-length instruction encoding. In such a case, the best
223    /// effort is to subtract 1 byte and hope that it points into the previous instruction:
224    ///
225    /// ```
226    /// use symbolic_common::{Arch, InstructionInfo};
227    ///
228    /// let info = InstructionInfo::new(Arch::X86, 0x1337);
229    /// assert_eq!(info.previous_address(), 0x1336);
230    /// ```
231    ///
232    /// [`aligned_address`]: struct.InstructionInfo.html#method.aligned_address
233    pub fn previous_address(&self) -> u64 {
234        let instruction_size = self.arch.cpu_family().instruction_alignment().unwrap_or(1);
235
236        // In MIPS, the return address apparently often points two instructions after the the
237        // previous program counter. On other architectures, just subtract one instruction.
238        let pc_offset = match self.arch.cpu_family() {
239            CpuFamily::Mips32 | CpuFamily::Mips64 => 2 * instruction_size,
240            _ => instruction_size,
241        };
242
243        self.aligned_address() - pc_offset
244    }
245
246    /// Returns whether the application attempted to jump to an invalid, privileged or misaligned
247    /// address.
248    ///
249    /// This indicates that certain adjustments should be made on the caller instruction address.
250    ///
251    /// # Example
252    ///
253    /// ```
254    /// use symbolic_common::{Arch, InstructionInfo};
255    ///
256    /// const SIGSEGV: u32 = 11;
257    ///
258    /// let is_crash = InstructionInfo::new(Arch::X86, 0x1337)
259    ///     .signal(Some(SIGSEGV))
260    ///     .is_crash_signal();
261    ///
262    /// assert!(is_crash);
263    /// ```
264    pub fn is_crash_signal(&self) -> bool {
265        matches!(self.signal, Some(SIGILL) | Some(SIGBUS) | Some(SIGSEGV))
266    }
267
268    /// Determines whether the given address should be adjusted to resolve the call site of a stack
269    /// frame.
270    ///
271    /// This generally applies to all frames except the crashing / suspended frame. However, if the
272    /// process crashed with an illegal instruction, even the top-most frame needs to be adjusted to
273    /// account for the signal handler.
274    ///
275    /// # Examples
276    ///
277    /// By default, all frames need to be adjusted. There are only few exceptions to this rule: The
278    /// crashing frame is the first frame yielded in the stack trace and specifies the actual
279    /// instruction pointer address. Therefore, it does not need to be adjusted:
280    ///
281    /// ```
282    /// use symbolic_common::{Arch, InstructionInfo};
283    ///
284    /// let should_adjust = InstructionInfo::new(Arch::X86, 0x1337)
285    ///     .is_crashing_frame(true)
286    ///     .should_adjust_caller();
287    ///
288    /// assert!(!should_adjust);
289    /// ```
290    pub fn should_adjust_caller(&self) -> bool {
291        // All frames other than the crashing frame (or suspended frame for
292        // other threads) report the return address. This address (generally)
293        // points to the instruction after the function call. Therefore, we
294        // need to adjust the caller address for these frames.
295        if !self.crashing_frame {
296            return true;
297        }
298
299        // KSCrash applies a heuristic to remove the signal handler frame from
300        // the top of the stack trace, if the crash was caused by certain
301        // signals. However, that means that the top-most frame contains a
302        // return address just like any other and needs to be adjusted.
303        if let Some(ip) = self.ip_reg {
304            if ip != self.addr && self.is_crash_signal() {
305                return true;
306            }
307        }
308
309        // The crashing frame usually contains the actual register contents,
310        // which points to the exact instruction that crashed and must not be
311        // adjusted.
312        false
313    }
314
315    /// Determines the address of the call site based on a return address.
316    ///
317    /// In the top most frame (often referred to as context frame), this is the value of the
318    /// instruction pointer register. In all other frames, the return address is generally one
319    /// instruction after the jump / call.
320    ///
321    /// This function actually resolves an address _within_ the call instruction rather than its
322    /// beginning. Also, in some cases the top most frame has been modified by certain signal
323    /// handlers or return optimizations. A set of heuristics tries to recover this for well-known
324    /// cases.
325    ///
326    /// # Examples
327    ///
328    /// Returns the aligned address for crashing frames:
329    ///
330    /// ```
331    /// use symbolic_common::{Arch, InstructionInfo};
332    ///
333    /// let caller_address = InstructionInfo::new(Arch::Arm64, 0x1337)
334    ///     .is_crashing_frame(true)
335    ///     .caller_address();
336    ///
337    /// assert_eq!(caller_address, 0x1334);
338    /// ```
339    ///
340    /// For all other frames, it returns the previous address:
341    ///
342    /// ```
343    /// use symbolic_common::{Arch, InstructionInfo};
344    ///
345    /// let caller_address = InstructionInfo::new(Arch::Arm64, 0x1337)
346    ///     .is_crashing_frame(false)
347    ///     .caller_address();
348    ///
349    /// assert_eq!(caller_address, 0x1330);
350    /// ```
351    pub fn caller_address(&self) -> u64 {
352        if self.should_adjust_caller() {
353            self.previous_address()
354        } else {
355            self.aligned_address()
356        }
357
358        // NOTE: Currently, we only provide stack traces from KSCrash and
359        // Breakpad. Both already apply a set of heuristics while stackwalking
360        // in order to fix return addresses. It seems that no further heuristics
361        // are necessary at the moment.
362    }
363}