165492d95SNico Weber //===-- sanitizer_stacktrace.cpp ------------------------------------------===//
265492d95SNico Weber //
365492d95SNico Weber // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
465492d95SNico Weber // See https://llvm.org/LICENSE.txt for license information.
565492d95SNico Weber // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
665492d95SNico Weber //
765492d95SNico Weber //===----------------------------------------------------------------------===//
865492d95SNico Weber //
965492d95SNico Weber // This file is shared between AddressSanitizer and ThreadSanitizer
1065492d95SNico Weber // run-time libraries.
1165492d95SNico Weber //===----------------------------------------------------------------------===//
1265492d95SNico Weber
137ce4dfb4SAlexey Baturo #include "sanitizer_stacktrace.h"
147ce4dfb4SAlexey Baturo
1565492d95SNico Weber #include "sanitizer_common.h"
1665492d95SNico Weber #include "sanitizer_flags.h"
177ce4dfb4SAlexey Baturo #include "sanitizer_platform.h"
184220531cSDaniel Kiss #include "sanitizer_ptrauth.h"
1965492d95SNico Weber
2065492d95SNico Weber namespace __sanitizer {
2165492d95SNico Weber
GetNextInstructionPc(uptr pc)22557855e0SNico Weber uptr StackTrace::GetNextInstructionPc(uptr pc) {
233de5322bSFangrui Song #if defined(__aarch64__)
24b3980b5bSJulian Lettner return STRIP_PAC_PC((void *)pc) + 4;
253de5322bSFangrui Song #elif defined(__sparc__) || defined(__mips__)
263de5322bSFangrui Song return pc + 8;
27557855e0SNico Weber #elif SANITIZER_RISCV64
28557855e0SNico Weber // Current check order is 4 -> 2 -> 6 -> 8
29557855e0SNico Weber u8 InsnByte = *(u8 *)(pc);
30557855e0SNico Weber if (((InsnByte & 0x3) == 0x3) && ((InsnByte & 0x1c) != 0x1c)) {
31557855e0SNico Weber // xxxxxxxxxxxbbb11 | 32 bit | bbb != 111
32557855e0SNico Weber return pc + 4;
33557855e0SNico Weber }
34557855e0SNico Weber if ((InsnByte & 0x3) != 0x3) {
35557855e0SNico Weber // xxxxxxxxxxxxxxaa | 16 bit | aa != 11
36557855e0SNico Weber return pc + 2;
37557855e0SNico Weber }
38557855e0SNico Weber // RISC-V encoding allows instructions to be up to 8 bytes long
39557855e0SNico Weber if ((InsnByte & 0x3f) == 0x1f) {
40557855e0SNico Weber // xxxxxxxxxx011111 | 48 bit |
41557855e0SNico Weber return pc + 6;
42557855e0SNico Weber }
43557855e0SNico Weber if ((InsnByte & 0x7f) == 0x3f) {
44557855e0SNico Weber // xxxxxxxxx0111111 | 64 bit |
45557855e0SNico Weber return pc + 8;
46557855e0SNico Weber }
47557855e0SNico Weber // bail-out if could not figure out the instruction size
48557855e0SNico Weber return 0;
49632ea692SFangrui Song #elif SANITIZER_S390 || SANITIZER_I386 || SANITIZER_X32 || SANITIZER_X64
50557855e0SNico Weber return pc + 1;
513de5322bSFangrui Song #else
523de5322bSFangrui Song return pc + 4;
53557855e0SNico Weber #endif
54557855e0SNico Weber }
55557855e0SNico Weber
GetCurrentPc()5665492d95SNico Weber uptr StackTrace::GetCurrentPc() {
5765492d95SNico Weber return GET_CALLER_PC();
5865492d95SNico Weber }
5965492d95SNico Weber
Init(const uptr * pcs,uptr cnt,uptr extra_top_pc)6065492d95SNico Weber void BufferedStackTrace::Init(const uptr *pcs, uptr cnt, uptr extra_top_pc) {
6165492d95SNico Weber size = cnt + !!extra_top_pc;
6265492d95SNico Weber CHECK_LE(size, kStackTraceMax);
6365492d95SNico Weber internal_memcpy(trace_buffer, pcs, cnt * sizeof(trace_buffer[0]));
6465492d95SNico Weber if (extra_top_pc)
6565492d95SNico Weber trace_buffer[cnt] = extra_top_pc;
6665492d95SNico Weber top_frame_bp = 0;
6765492d95SNico Weber }
6865492d95SNico Weber
69a1e7e401SKazuaki Ishizaki // Sparc implementation is in its own file.
7065492d95SNico Weber #if !defined(__sparc__)
7165492d95SNico Weber
7265492d95SNico Weber // In GCC on ARM bp points to saved lr, not fp, so we should check the next
7365492d95SNico Weber // cell in stack to be a saved frame pointer. GetCanonicFrame returns the
7465492d95SNico Weber // pointer to saved frame pointer in any case.
GetCanonicFrame(uptr bp,uptr stack_top,uptr stack_bottom)7565492d95SNico Weber static inline uhwptr *GetCanonicFrame(uptr bp,
7665492d95SNico Weber uptr stack_top,
7765492d95SNico Weber uptr stack_bottom) {
7865492d95SNico Weber CHECK_GT(stack_top, stack_bottom);
7965492d95SNico Weber #ifdef __arm__
8065492d95SNico Weber if (!IsValidFrame(bp, stack_top, stack_bottom)) return 0;
8165492d95SNico Weber uhwptr *bp_prev = (uhwptr *)bp;
8265492d95SNico Weber if (IsValidFrame((uptr)bp_prev[0], stack_top, stack_bottom)) return bp_prev;
8365492d95SNico Weber // The next frame pointer does not look right. This could be a GCC frame, step
8465492d95SNico Weber // back by 1 word and try again.
8565492d95SNico Weber if (IsValidFrame((uptr)bp_prev[-1], stack_top, stack_bottom))
8665492d95SNico Weber return bp_prev - 1;
8765492d95SNico Weber // Nope, this does not look right either. This means the frame after next does
8865492d95SNico Weber // not have a valid frame pointer, but we can still extract the caller PC.
8965492d95SNico Weber // Unfortunately, there is no way to decide between GCC and LLVM frame
9065492d95SNico Weber // layouts. Assume LLVM.
9165492d95SNico Weber return bp_prev;
9265492d95SNico Weber #else
9365492d95SNico Weber return (uhwptr*)bp;
9465492d95SNico Weber #endif
9565492d95SNico Weber }
9665492d95SNico Weber
UnwindFast(uptr pc,uptr bp,uptr stack_top,uptr stack_bottom,u32 max_depth)9765492d95SNico Weber void BufferedStackTrace::UnwindFast(uptr pc, uptr bp, uptr stack_top,
9865492d95SNico Weber uptr stack_bottom, u32 max_depth) {
9965492d95SNico Weber // TODO(yln): add arg sanity check for stack_top/stack_bottom
10065492d95SNico Weber CHECK_GE(max_depth, 2);
10165492d95SNico Weber const uptr kPageSize = GetPageSizeCached();
10265492d95SNico Weber trace_buffer[0] = pc;
10365492d95SNico Weber size = 1;
10465492d95SNico Weber if (stack_top < 4096) return; // Sanity check for stack top.
10565492d95SNico Weber uhwptr *frame = GetCanonicFrame(bp, stack_top, stack_bottom);
10665492d95SNico Weber // Lowest possible address that makes sense as the next frame pointer.
10765492d95SNico Weber // Goes up as we walk the stack.
10865492d95SNico Weber uptr bottom = stack_bottom;
10965492d95SNico Weber // Avoid infinite loop when frame == frame[0] by using frame > prev_frame.
11065492d95SNico Weber while (IsValidFrame((uptr)frame, stack_top, bottom) &&
11165492d95SNico Weber IsAligned((uptr)frame, sizeof(*frame)) &&
11265492d95SNico Weber size < max_depth) {
11365492d95SNico Weber #ifdef __powerpc__
11465492d95SNico Weber // PowerPC ABIs specify that the return address is saved at offset
11565492d95SNico Weber // 16 of the *caller's* stack frame. Thus we must dereference the
11665492d95SNico Weber // back chain to find the caller frame before extracting it.
11765492d95SNico Weber uhwptr *caller_frame = (uhwptr*)frame[0];
11865492d95SNico Weber if (!IsValidFrame((uptr)caller_frame, stack_top, bottom) ||
11965492d95SNico Weber !IsAligned((uptr)caller_frame, sizeof(uhwptr)))
12065492d95SNico Weber break;
12165492d95SNico Weber uhwptr pc1 = caller_frame[2];
12265492d95SNico Weber #elif defined(__s390__)
12365492d95SNico Weber uhwptr pc1 = frame[14];
124*bba1f26fSXi Ruoyao #elif defined(__loongarch__) || defined(__riscv)
12551beb0c8SAlexey Baturo // frame[-1] contains the return address
12651beb0c8SAlexey Baturo uhwptr pc1 = frame[-1];
12765492d95SNico Weber #else
1284220531cSDaniel Kiss uhwptr pc1 = STRIP_PAC_PC((void *)frame[1]);
12965492d95SNico Weber #endif
13065492d95SNico Weber // Let's assume that any pointer in the 0th page (i.e. <0x1000 on i386 and
13165492d95SNico Weber // x86_64) is invalid and stop unwinding here. If we're adding support for
13265492d95SNico Weber // a platform where this isn't true, we need to reconsider this check.
13365492d95SNico Weber if (pc1 < kPageSize)
13465492d95SNico Weber break;
13565492d95SNico Weber if (pc1 != pc) {
13665492d95SNico Weber trace_buffer[size++] = (uptr) pc1;
13765492d95SNico Weber }
13865492d95SNico Weber bottom = (uptr)frame;
139*bba1f26fSXi Ruoyao #if defined(__loongarch__) || defined(__riscv)
14051beb0c8SAlexey Baturo // frame[-2] contain fp of the previous frame
14151beb0c8SAlexey Baturo uptr new_bp = (uptr)frame[-2];
14251beb0c8SAlexey Baturo #else
14351beb0c8SAlexey Baturo uptr new_bp = (uptr)frame[0];
14451beb0c8SAlexey Baturo #endif
14551beb0c8SAlexey Baturo frame = GetCanonicFrame(new_bp, stack_top, bottom);
14665492d95SNico Weber }
14765492d95SNico Weber }
14865492d95SNico Weber
14965492d95SNico Weber #endif // !defined(__sparc__)
15065492d95SNico Weber
PopStackFrames(uptr count)15165492d95SNico Weber void BufferedStackTrace::PopStackFrames(uptr count) {
15265492d95SNico Weber CHECK_LT(count, size);
15365492d95SNico Weber size -= count;
15465492d95SNico Weber for (uptr i = 0; i < size; ++i) {
15565492d95SNico Weber trace_buffer[i] = trace_buffer[i + count];
15665492d95SNico Weber }
15765492d95SNico Weber }
15865492d95SNico Weber
Distance(uptr a,uptr b)15965492d95SNico Weber static uptr Distance(uptr a, uptr b) { return a < b ? b - a : a - b; }
16065492d95SNico Weber
LocatePcInTrace(uptr pc)16165492d95SNico Weber uptr BufferedStackTrace::LocatePcInTrace(uptr pc) {
16265492d95SNico Weber uptr best = 0;
16365492d95SNico Weber for (uptr i = 1; i < size; ++i) {
16465492d95SNico Weber if (Distance(trace[i], pc) < Distance(trace[best], pc)) best = i;
16565492d95SNico Weber }
16665492d95SNico Weber return best;
16765492d95SNico Weber }
16865492d95SNico Weber
16965492d95SNico Weber } // namespace __sanitizer
170