[PATCH] D51432: [AArch64] Unwinding support for return address signing
This revision was automatically updated to reflect the committed changes. Closed by commit rL342895: [AArch64] Unwinding support for return address signing (authored by LukeCheeseman, committed by ). Herald added subscribers: llvm-commits, christof. Changed prior to commit: https://reviews.llvm.org/D51432?vs=165775=166700#toc Repository: rL LLVM https://reviews.llvm.org/D51432 Files: libunwind/trunk/include/libunwind.h libunwind/trunk/src/DwarfInstructions.hpp libunwind/trunk/src/DwarfParser.hpp libunwind/trunk/src/Registers.hpp libunwind/trunk/src/dwarf2.h Index: libunwind/trunk/src/DwarfInstructions.hpp === --- libunwind/trunk/src/DwarfInstructions.hpp +++ libunwind/trunk/src/DwarfInstructions.hpp @@ -198,6 +198,24 @@ // restoring SP means setting it to CFA. newRegisters.setSP(cfa); +#if defined(_LIBUNWIND_TARGET_AARCH64) + // If the target is aarch64 then the return address may have been signed + // using the v8.3 pointer authentication extensions. The original + // return address needs to be authenticated before the return address is + // restored. autia1716 is used instead of autia as autia1716 assembles + // to a NOP on pre-v8.3a architectures. + if (prolog.savedRegisters[UNW_ARM64_RA_SIGN_STATE].value) { +#if !defined(_LIBUNWIND_IS_NATIVE_ONLY) +return UNW_ECROSSRASIGNING; +#else +register unsigned long long x17 __asm("x17") = returnAddress; +register unsigned long long x16 __asm("x16") = cfa; +asm("autia1716": "+r"(x17): "r"(x16)); +returnAddress = x17; +#endif + } +#endif + // Return address is address after call site instruction, so setting IP to // that does simualates a return. newRegisters.setIP(returnAddress); Index: libunwind/trunk/src/dwarf2.h === --- libunwind/trunk/src/dwarf2.h +++ libunwind/trunk/src/dwarf2.h @@ -49,7 +49,10 @@ // GNU extensions DW_CFA_GNU_window_save = 0x2D, DW_CFA_GNU_args_size= 0x2E, - DW_CFA_GNU_negative_offset_extended = 0x2F + DW_CFA_GNU_negative_offset_extended = 0x2F, + + // AARCH64 extensions + DW_CFA_AARCH64_negate_ra_state = 0x2D }; Index: libunwind/trunk/src/DwarfParser.hpp === --- libunwind/trunk/src/DwarfParser.hpp +++ libunwind/trunk/src/DwarfParser.hpp @@ -666,6 +666,14 @@ _LIBUNWIND_TRACE_DWARF( "DW_CFA_GNU_negative_offset_extended(%" PRId64 ")\n", offset); break; + +#if defined(_LIBUNWIND_TARGET_AARCH64) +case DW_CFA_AARCH64_negate_ra_state: + results->savedRegisters[UNW_ARM64_RA_SIGN_STATE].value ^= 0x1; + _LIBUNWIND_TRACE_DWARF("DW_CFA_AARCH64_negate_ra_state\n"); + break; +#endif + default: operand = opcode & 0x3F; switch (opcode & 0xC0) { Index: libunwind/trunk/src/Registers.hpp === --- libunwind/trunk/src/Registers.hpp +++ libunwind/trunk/src/Registers.hpp @@ -1786,7 +1786,7 @@ uint64_t __lr;// Link register x30 uint64_t __sp;// Stack pointer x31 uint64_t __pc;// Program counter -uint64_t padding; // 16-byte align +uint64_t __ra_sign_state; // RA sign state register }; GPRs_registers; @@ -1822,6 +1822,8 @@ return false; if (regNum > 95) return false; + if (regNum == UNW_ARM64_RA_SIGN_STATE) +return true; if ((regNum > 31) && (regNum < 64)) return false; return true; @@ -1832,16 +1834,21 @@ return _registers.__pc; if (regNum == UNW_REG_SP) return _registers.__sp; + if (regNum == UNW_ARM64_RA_SIGN_STATE) +return _registers.__ra_sign_state; if ((regNum >= 0) && (regNum < 32)) return _registers.__x[regNum]; + _LIBUNWIND_ABORT("unsupported arm64 register"); } inline void Registers_arm64::setRegister(int regNum, uint64_t value) { if (regNum == UNW_REG_IP) _registers.__pc = value; else if (regNum == UNW_REG_SP) _registers.__sp = value; + else if (regNum == UNW_ARM64_RA_SIGN_STATE) +_registers.__ra_sign_state = value; else if ((regNum >= 0) && (regNum < 32)) _registers.__x[regNum] = value; else Index: libunwind/trunk/include/libunwind.h === --- libunwind/trunk/include/libunwind.h +++ libunwind/trunk/include/libunwind.h @@ -57,6 +57,9 @@ UNW_EINVAL= -6547, /* unsupported operation or bad value */ UNW_EBADVERSION = -6548, /* unwind info has unsupported version */ UNW_ENOINFO = -6549 /* no unwind info found */ +#if defined(_LIBUNWIND_TARGET_AARCH64) && !defined(_LIBUNWIND_IS_NATIVE_ONLY) + , UNW_ECROSSRASIGNING = -6550 /* cross unwind with return address signing */ +#endif }; struct unw_context_t { @@ -547,6 +550,8 @@
[PATCH] D51432: [AArch64] Unwinding support for return address signing
olista01 accepted this revision. olista01 added a comment. This revision is now accepted and ready to land. LGTM, thanks! https://reviews.llvm.org/D51432 ___ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[PATCH] D51432: [AArch64] Unwinding support for return address signing
LukeCheeseman updated this revision to Diff 165775. LukeCheeseman added a comment. return an error code when trying to sign return addresses and cross unwinding https://reviews.llvm.org/D51432 Files: include/libunwind.h src/DwarfInstructions.hpp src/DwarfParser.hpp src/Registers.hpp src/dwarf2.h Index: src/dwarf2.h === --- src/dwarf2.h +++ src/dwarf2.h @@ -49,7 +49,10 @@ // GNU extensions DW_CFA_GNU_window_save = 0x2D, DW_CFA_GNU_args_size= 0x2E, - DW_CFA_GNU_negative_offset_extended = 0x2F + DW_CFA_GNU_negative_offset_extended = 0x2F, + + // AARCH64 extensions + DW_CFA_AARCH64_negate_ra_state = 0x2D }; Index: src/Registers.hpp === --- src/Registers.hpp +++ src/Registers.hpp @@ -1783,7 +1783,7 @@ uint64_t __lr;// Link register x30 uint64_t __sp;// Stack pointer x31 uint64_t __pc;// Program counter -uint64_t padding; // 16-byte align +uint64_t __ra_sign_state; // RA sign state register }; GPRs_registers; @@ -1819,6 +1819,8 @@ return false; if (regNum > 95) return false; + if (regNum == UNW_ARM64_RA_SIGN_STATE) +return true; if ((regNum > 31) && (regNum < 64)) return false; return true; @@ -1829,16 +1831,21 @@ return _registers.__pc; if (regNum == UNW_REG_SP) return _registers.__sp; + if (regNum == UNW_ARM64_RA_SIGN_STATE) +return _registers.__ra_sign_state; if ((regNum >= 0) && (regNum < 32)) return _registers.__x[regNum]; + _LIBUNWIND_ABORT("unsupported arm64 register"); } inline void Registers_arm64::setRegister(int regNum, uint64_t value) { if (regNum == UNW_REG_IP) _registers.__pc = value; else if (regNum == UNW_REG_SP) _registers.__sp = value; + else if (regNum == UNW_ARM64_RA_SIGN_STATE) +_registers.__ra_sign_state = value; else if ((regNum >= 0) && (regNum < 32)) _registers.__x[regNum] = value; else Index: src/DwarfParser.hpp === --- src/DwarfParser.hpp +++ src/DwarfParser.hpp @@ -666,6 +666,14 @@ _LIBUNWIND_TRACE_DWARF( "DW_CFA_GNU_negative_offset_extended(%" PRId64 ")\n", offset); break; + +#if defined(_LIBUNWIND_TARGET_AARCH64) +case DW_CFA_AARCH64_negate_ra_state: + results->savedRegisters[UNW_ARM64_RA_SIGN_STATE].value ^= 0x1; + _LIBUNWIND_TRACE_DWARF("DW_CFA_AARCH64_negate_ra_state\n"); + break; +#endif + default: operand = opcode & 0x3F; switch (opcode & 0xC0) { Index: src/DwarfInstructions.hpp === --- src/DwarfInstructions.hpp +++ src/DwarfInstructions.hpp @@ -198,6 +198,24 @@ // restoring SP means setting it to CFA. newRegisters.setSP(cfa); +#if defined(_LIBUNWIND_TARGET_AARCH64) + // If the target is aarch64 then the return address may have been signed + // using the v8.3 pointer authentication extensions. The original + // return address needs to be authenticated before the return address is + // restored. autia1716 is used instead of autia as autia1716 assembles + // to a NOP on pre-v8.3a architectures. + if (prolog.savedRegisters[UNW_ARM64_RA_SIGN_STATE].value) { +#if !defined(_LIBUNWIND_IS_NATIVE_ONLY) +return UNW_ECROSSRASIGNING; +#else +register unsigned long long x17 __asm("x17") = returnAddress; +register unsigned long long x16 __asm("x16") = cfa; +asm("autia1716": "+r"(x17): "r"(x16)); +returnAddress = x17; +#endif + } +#endif + // Return address is address after call site instruction, so setting IP to // that does simualates a return. newRegisters.setIP(returnAddress); Index: include/libunwind.h === --- include/libunwind.h +++ include/libunwind.h @@ -57,6 +57,9 @@ UNW_EINVAL= -6547, /* unsupported operation or bad value */ UNW_EBADVERSION = -6548, /* unwind info has unsupported version */ UNW_ENOINFO = -6549 /* no unwind info found */ +#if defined(_LIBUNWIND_TARGET_AARCH64) && !defined(_LIBUNWIND_IS_NATIVE_ONLY) + , UNW_ECROSSRASIGNING = -6550 /* cross unwind with return address signing */ +#endif }; struct unw_context_t { @@ -547,6 +550,8 @@ UNW_ARM64_X31 = 31, UNW_ARM64_SP = 31, // reserved block + UNW_ARM64_RA_SIGN_STATE = 34, + // reserved block UNW_ARM64_D0 = 64, UNW_ARM64_D1 = 65, UNW_ARM64_D2 = 66, ___ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[PATCH] D51432: [AArch64] Unwinding support for return address signing
LukeCheeseman added inline comments. Comment at: src/Registers.hpp:1835 + if (((regNum >= 0) && (regNum < 32)) || regNum == UNW_ARM64_RA_SIGN_STATE) return _registers.__x[regNum]; + olista01 wrote: > When regNum == UNW_ARM64_RA_SIGN_STATE, the index into __x is out of range. > We'll need to add new storage to hold this value, I'd suggest replacing the > current padding value in the GPRs struct, as that will avoid changing the > layout expected by the context save/restore functions. Good catch. Thanks, I didn't check the struct definition. Repository: rUNW libunwind https://reviews.llvm.org/D51432 ___ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[PATCH] D51432: [AArch64] Unwinding support for return address signing
olista01 added inline comments. Comment at: src/DwarfInstructions.hpp:210 +register unsigned long long x16 __asm("x16") = cfa; +asm("autia1716": "+r"(x17): "r"(x16)); +returnAddress = x17; I don't think this will work for cross-unwinding builds: for them, _LIBUNWIND_TARGET_AARCH64 is defined even when the compilation target is not AArch64, so this instruction won't exist. Fully supporting cross-unwinding looks non-trivial: we'd need to either provide some way to ask the client to authenticate a pointer on the target, or strip the high bits of the pointer (which requires knowing the virtual address size of the target). For now, I think it's OK to not support cross-unwinding. Comment at: src/Registers.hpp:1835 + if (((regNum >= 0) && (regNum < 32)) || regNum == UNW_ARM64_RA_SIGN_STATE) return _registers.__x[regNum]; + When regNum == UNW_ARM64_RA_SIGN_STATE, the index into __x is out of range. We'll need to add new storage to hold this value, I'd suggest replacing the current padding value in the GPRs struct, as that will avoid changing the layout expected by the context save/restore functions. Comment at: src/Registers.hpp:1845 _registers.__sp = value; - else if ((regNum >= 0) && (regNum < 32)) + else if ((regNum >= 0) && (regNum < 32) || regNum == UNW_ARM64_RA_SIGN_STATE) _registers.__x[regNum] = value; Ditto. Repository: rUNW libunwind https://reviews.llvm.org/D51432 ___ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[PATCH] D51432: [AArch64] Unwinding support for return address signing
LukeCheeseman created this revision. LukeCheeseman added reviewers: pcc, kcc, eugenis, vlad.tsyrklevich. Herald added a reviewer: javed.absar. Herald added subscribers: cfe-commits, chrib, JDevlieghere, kristof.beyls. - When return address signing is enabled, the LR may be signed on function entry - When an exception is thrown the return address is inspected used to unwind the call stack - Before this happens, the return address must be correctly authenticated to avoid causing an abort by dereferencing the signed pointer Repository: rUNW libunwind https://reviews.llvm.org/D51432 Files: include/libunwind.h src/DwarfInstructions.hpp src/DwarfParser.hpp src/Registers.hpp src/dwarf2.h Index: src/dwarf2.h === --- src/dwarf2.h +++ src/dwarf2.h @@ -49,7 +49,10 @@ // GNU extensions DW_CFA_GNU_window_save = 0x2D, DW_CFA_GNU_args_size= 0x2E, - DW_CFA_GNU_negative_offset_extended = 0x2F + DW_CFA_GNU_negative_offset_extended = 0x2F, + + // AARCH64 extensions + DW_CFA_AARCH64_negate_ra_state = 0x2D }; Index: src/Registers.hpp === --- src/Registers.hpp +++ src/Registers.hpp @@ -1819,6 +1819,8 @@ return false; if (regNum > 95) return false; + if (regNum == UNW_ARM64_RA_SIGN_STATE) +return true; if ((regNum > 31) && (regNum < 64)) return false; return true; @@ -1829,17 +1831,18 @@ return _registers.__pc; if (regNum == UNW_REG_SP) return _registers.__sp; - if ((regNum >= 0) && (regNum < 32)) + if (((regNum >= 0) && (regNum < 32)) || regNum == UNW_ARM64_RA_SIGN_STATE) return _registers.__x[regNum]; + _LIBUNWIND_ABORT("unsupported arm64 register"); } inline void Registers_arm64::setRegister(int regNum, uint64_t value) { if (regNum == UNW_REG_IP) _registers.__pc = value; else if (regNum == UNW_REG_SP) _registers.__sp = value; - else if ((regNum >= 0) && (regNum < 32)) + else if ((regNum >= 0) && (regNum < 32) || regNum == UNW_ARM64_RA_SIGN_STATE) _registers.__x[regNum] = value; else _LIBUNWIND_ABORT("unsupported arm64 register"); Index: src/DwarfParser.hpp === --- src/DwarfParser.hpp +++ src/DwarfParser.hpp @@ -666,6 +666,14 @@ _LIBUNWIND_TRACE_DWARF( "DW_CFA_GNU_negative_offset_extended(%" PRId64 ")\n", offset); break; + +#if defined(_LIBUNWIND_TARGET_AARCH64) +case DW_CFA_AARCH64_negate_ra_state: + results->savedRegisters[UNW_ARM64_RA_SIGN_STATE].value ^= 0x1; + _LIBUNWIND_TRACE_DWARF("DW_CFA_AARCH64_negate_ra_state\n"); + break; +#endif + default: operand = opcode & 0x3F; switch (opcode & 0xC0) { Index: src/DwarfInstructions.hpp === --- src/DwarfInstructions.hpp +++ src/DwarfInstructions.hpp @@ -198,6 +198,20 @@ // restoring SP means setting it to CFA. newRegisters.setSP(cfa); +#if defined(_LIBUNWIND_TARGET_AARCH64) + // If the target is aarch64 then the return address may have been signed + // using the v8.3 pointer authentication extensions. The original + // return address needs to be authenticated before the return address is + // restored. autia1716 is used instead of autia as autia1716 assembles + // to a NOP on pre-v8.3a architectures. + if (prolog.savedRegisters[UNW_ARM64_RA_SIGN_STATE].value) { +register unsigned long long x17 __asm("x17") = returnAddress; +register unsigned long long x16 __asm("x16") = cfa; +asm("autia1716": "+r"(x17): "r"(x16)); +returnAddress = x17; + } +#endif + // Return address is address after call site instruction, so setting IP to // that does simualates a return. newRegisters.setIP(returnAddress); Index: include/libunwind.h === --- include/libunwind.h +++ include/libunwind.h @@ -547,6 +547,8 @@ UNW_ARM64_X31 = 31, UNW_ARM64_SP = 31, // reserved block + UNW_ARM64_RA_SIGN_STATE = 34, + // reserved block UNW_ARM64_D0 = 64, UNW_ARM64_D1 = 65, UNW_ARM64_D2 = 66, ___ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits