diff --git a/libunwind/src/DwarfInstructions.hpp b/libunwind/src/DwarfInstructions.hpp --- a/libunwind/src/DwarfInstructions.hpp +++ b/libunwind/src/DwarfInstructions.hpp @@ -34,7 +34,7 @@ typedef typename A::sint_t sint_t; static int stepWithDwarf(A &addressSpace, pint_t pc, pint_t fdeStart, - R ®isters, bool &isSignalFrame); + R ®isters, bool &isSignalFrame, bool clean); private: @@ -189,7 +189,7 @@ template int DwarfInstructions::stepWithDwarf(A &addressSpace, pint_t pc, pint_t fdeStart, R ®isters, - bool &isSignalFrame) { + bool &isSignalFrame, bool clean) { FDE_Info fdeInfo; CIE_Info cieInfo; if (CFI_Parser::decodeFDE(addressSpace, fdeStart, &fdeInfo, @@ -200,7 +200,32 @@ // get pointer to cfa (architecture specific) pint_t cfa = getCFA(addressSpace, prolog, registers); - // restore registers that DWARF says were saved + (void)clean; +#if defined(_LIBUNWIND_TARGET_AARCH64) + if (clean && cieInfo.mteTaggedFrame) { + pint_t sp = registers.getSP(); + pint_t p = sp; + // AArch64 doesn't require the value of SP to be 16-byte aligned at + // all times, only at memory accesses and public interfaces [1]. Thus, + // a signal could arrive at a point where SP is not aligned properly. + // In that case, the kernel fixes up [2] the signal frame, but we + // still have a misaligned SP in the previous frame. If that signal + // handler caused stack unwinding, we would have an unaligned SP. + // We do not need to fix up the CFA, as that is the SP at a "public + // interface". + // [1]: + // https://github.com/ARM-software/abi-aa/blob/main/aapcs64/aapcs64.rst#622the-stack + // [2]: + // https://github.com/torvalds/linux/blob/1930a6e739c4b4a654a69164dbe39e554d228915/arch/arm64/kernel/signal.c#L718 + p &= ~15ULL; + for (; p < cfa; p += 16) { + __asm__ __volatile__( + ".arch_extension memtag\nstg %[Ptr], [%[Ptr]]\n" ::[Ptr] "r"(p) + : "memory"); + } + } +#endif + // restore registers that DWARF says were saved R newRegisters = registers; // Typically, the CFA is the stack pointer at the call site in diff --git a/libunwind/src/DwarfParser.hpp b/libunwind/src/DwarfParser.hpp --- a/libunwind/src/DwarfParser.hpp +++ b/libunwind/src/DwarfParser.hpp @@ -51,6 +51,7 @@ uint8_t returnAddressRegister; #if defined(_LIBUNWIND_TARGET_AARCH64) bool addressesSignedWithBKey; + bool mteTaggedFrame; #endif }; @@ -325,6 +326,7 @@ cieInfo->fdesHaveAugmentationData = false; #if defined(_LIBUNWIND_TARGET_AARCH64) cieInfo->addressesSignedWithBKey = false; + cieInfo->mteTaggedFrame = false; #endif cieInfo->cieStart = cie; pint_t p = cie; @@ -394,6 +396,9 @@ case 'B': cieInfo->addressesSignedWithBKey = true; break; + case 'G': + cieInfo->mteTaggedFrame = true; + break; #endif default: // ignore unknown letters diff --git a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp --- a/libunwind/src/UnwindCursor.hpp +++ b/libunwind/src/UnwindCursor.hpp @@ -443,7 +443,7 @@ virtual void setFloatReg(int, unw_fpreg_t) { _LIBUNWIND_ABORT("setFloatReg not implemented"); } - virtual int step() { _LIBUNWIND_ABORT("step not implemented"); } + virtual int step(bool = false) { _LIBUNWIND_ABORT("step not implemented"); } virtual void getInfo(unw_proc_info_t *) { _LIBUNWIND_ABORT("getInfo not implemented"); } @@ -495,7 +495,7 @@ virtual bool validFloatReg(int); virtual unw_fpreg_t getFloatReg(int); virtual void setFloatReg(int, unw_fpreg_t); - virtual int step(); + virtual int step(bool = false); virtual void getInfo(unw_proc_info_t *); virtual void jumpto(); virtual bool isSignalFrame(); @@ -926,7 +926,7 @@ virtual bool validFloatReg(int); virtual unw_fpreg_t getFloatReg(int); virtual void setFloatReg(int, unw_fpreg_t); - virtual int step(); + virtual int step(bool clean = false); virtual void getInfo(unw_proc_info_t *); virtual void jumpto(); virtual bool isSignalFrame(); @@ -1000,22 +1000,21 @@ pint_t pc, uintptr_t dso_base); bool getInfoFromDwarfSection(pint_t pc, const UnwindInfoSections §s, uint32_t fdeSectionOffsetHint=0); - int stepWithDwarfFDE() { - return DwarfInstructions::stepWithDwarf(_addressSpace, - (pint_t)this->getReg(UNW_REG_IP), - (pint_t)_info.unwind_info, - _registers, _isSignalFrame); + int stepWithDwarfFDE(bool clean) { + return DwarfInstructions::stepWithDwarf( + _addressSpace, (pint_t)this->getReg(UNW_REG_IP), + (pint_t)_info.unwind_info, _registers, _isSignalFrame, clean); } #endif #if defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND) bool getInfoFromCompactEncodingSection(pint_t pc, const UnwindInfoSections §s); - int stepWithCompactEncoding() { - #if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) + int stepWithCompactEncoding(bool clean = false) { +#if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) if ( compactSaysUseDwarf() ) - return stepWithDwarfFDE(); - #endif + return stepWithDwarfFDE(clean); +#endif R dummy; return stepWithCompactEncoding(dummy); } @@ -2791,8 +2790,8 @@ #endif // defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN) && // defined(_LIBUNWIND_TARGET_S390X) -template -int UnwindCursor::step() { +template int UnwindCursor::step(bool clean) { + (void)clean; // Bottom of stack is defined is when unwind info cannot be found. if (_unwindInfoMissing) return UNW_STEP_END; @@ -2806,13 +2805,13 @@ #endif { #if defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND) - result = this->stepWithCompactEncoding(); + result = this->stepWithCompactEncoding(clean); #elif defined(_LIBUNWIND_SUPPORT_SEH_UNWIND) result = this->stepWithSEHData(); #elif defined(_LIBUNWIND_SUPPORT_TBTAB_UNWIND) result = this->stepWithTBTableData(); #elif defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) - result = this->stepWithDwarfFDE(); + result = this->stepWithDwarfFDE(clean); #elif defined(_LIBUNWIND_ARM_EHABI) result = this->stepWithEHABI(); #else diff --git a/libunwind/src/UnwindLevel1.c b/libunwind/src/UnwindLevel1.c --- a/libunwind/src/UnwindLevel1.c +++ b/libunwind/src/UnwindLevel1.c @@ -182,16 +182,16 @@ // Ask libunwind to get next frame (skip over first which is // _Unwind_RaiseException). - int stepResult = __unw_step(cursor); + int stepResult = __unw_clean_step(cursor); if (stepResult == 0) { _LIBUNWIND_TRACE_UNWINDING( - "unwind_phase2(ex_ojb=%p): __unw_step() reached " + "unwind_phase2(ex_ojb=%p): __unw_clean_step() reached " "bottom => _URC_END_OF_STACK", (void *)exception_object); return _URC_END_OF_STACK; } else if (stepResult < 0) { _LIBUNWIND_TRACE_UNWINDING( - "unwind_phase2(ex_ojb=%p): __unw_step failed => " + "unwind_phase2(ex_ojb=%p): __unw_clean_step failed => " "_URC_FATAL_PHASE1_ERROR", (void *)exception_object); return _URC_FATAL_PHASE2_ERROR; @@ -296,14 +296,15 @@ // frame walked is unwind_phase2_forced. unsigned framesWalked = 1; // Walk each frame until we reach where search phase said to stop - while (__unw_step(cursor) > 0) { + while (__unw_clean_step(cursor) > 0) { // Update info about this frame. unw_proc_info_t frameInfo; if (__unw_get_proc_info(cursor, &frameInfo) != UNW_ESUCCESS) { - _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): __unw_step " - "failed => _URC_END_OF_STACK", - (void *)exception_object); + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase2_forced(ex_ojb=%p): __unw_clean_step " + "failed => _URC_END_OF_STACK", + (void *)exception_object); return _URC_FATAL_PHASE2_ERROR; } diff --git a/libunwind/src/libunwind.cpp b/libunwind/src/libunwind.cpp --- a/libunwind/src/libunwind.cpp +++ b/libunwind/src/libunwind.cpp @@ -181,6 +181,15 @@ } _LIBUNWIND_WEAK_ALIAS(__unw_step, unw_step) +/// Move cursor to next frame and clean up the current stack frame. In +// particular, this resets MTE tags of tagged frames to zero. +_LIBUNWIND_HIDDEN int __unw_clean_step(unw_cursor_t *cursor) { + _LIBUNWIND_TRACE_API("__unw_clean_step(cursor=%p)", + static_cast(cursor)); + AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; + return co->step(true); +} + /// Get unwind info at cursor position in stack frame. _LIBUNWIND_HIDDEN int __unw_get_proc_info(unw_cursor_t *cursor, unw_proc_info_t *info) { diff --git a/libunwind/src/libunwind_ext.h b/libunwind/src/libunwind_ext.h --- a/libunwind/src/libunwind_ext.h +++ b/libunwind/src/libunwind_ext.h @@ -26,6 +26,7 @@ extern int __unw_getcontext(unw_context_t *); extern int __unw_init_local(unw_cursor_t *, unw_context_t *); extern int __unw_step(unw_cursor_t *); +extern int __unw_clean_step(unw_cursor_t *); extern int __unw_get_reg(unw_cursor_t *, unw_regnum_t, unw_word_t *); extern int __unw_get_fpreg(unw_cursor_t *, unw_regnum_t, unw_fpreg_t *); extern int __unw_set_reg(unw_cursor_t *, unw_regnum_t, unw_word_t);