Skip to content

Commit 77f4db4

Browse files
committed
arm64e/ptrauth unified patch: libunwind
1 parent 74d0a01 commit 77f4db4

File tree

11 files changed

+293
-33
lines changed

11 files changed

+293
-33
lines changed

libunwind/src/AddressSpace.hpp

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,8 @@ class _LIBUNWIND_HIDDEN LocalAddressSpace {
196196
static int64_t getSLEB128(pint_t &addr, pint_t end);
197197

198198
pint_t getEncodedP(pint_t &addr, pint_t end, uint8_t encoding,
199-
pint_t datarelBase = 0);
199+
pint_t datarelBase = 0,
200+
pint_t *resultAddr = nullptr);
200201
bool findFunctionName(pint_t addr, char *buf, size_t bufLen,
201202
unw_word_t *offset);
202203
bool findUnwindSections(pint_t targetAddr, UnwindInfoSections &info);
@@ -269,7 +270,7 @@ inline int64_t LocalAddressSpace::getSLEB128(pint_t &addr, pint_t end) {
269270

270271
inline LocalAddressSpace::pint_t
271272
LocalAddressSpace::getEncodedP(pint_t &addr, pint_t end, uint8_t encoding,
272-
pint_t datarelBase) {
273+
pint_t datarelBase, pint_t *resultAddr) {
273274
pint_t startAddr = addr;
274275
const uint8_t *p = (uint8_t *)addr;
275276
pint_t result;
@@ -353,8 +354,14 @@ LocalAddressSpace::getEncodedP(pint_t &addr, pint_t end, uint8_t encoding,
353354
break;
354355
}
355356

356-
if (encoding & DW_EH_PE_indirect)
357+
if (encoding & DW_EH_PE_indirect) {
358+
if (resultAddr)
359+
*resultAddr = result;
357360
result = getP(result);
361+
} else {
362+
if (resultAddr)
363+
*resultAddr = startAddr;
364+
}
358365

359366
return result;
360367
}

libunwind/src/CompactUnwinder.hpp

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -499,38 +499,39 @@ class CompactUnwinder_arm64 {
499499

500500
static int stepWithCompactEncoding(compact_unwind_encoding_t compactEncoding,
501501
uint64_t functionStart, A &addressSpace,
502-
Registers_arm64 &registers);
502+
unw_word_t procInfoFlags, Registers_arm64 &registers);
503503

504504
private:
505505
typename A::pint_t pint_t;
506506

507507
static int
508508
stepWithCompactEncodingFrame(compact_unwind_encoding_t compactEncoding,
509509
uint64_t functionStart, A &addressSpace,
510-
Registers_arm64 &registers);
510+
unw_word_t procInfoFlags, Registers_arm64 &registers);
511511
static int stepWithCompactEncodingFrameless(
512512
compact_unwind_encoding_t compactEncoding, uint64_t functionStart,
513-
A &addressSpace, Registers_arm64 &registers);
513+
A &addressSpace, unw_word_t procInfoFlags, Registers_arm64 &registers);
514514
};
515515

516516
template <typename A>
517517
int CompactUnwinder_arm64<A>::stepWithCompactEncoding(
518518
compact_unwind_encoding_t compactEncoding, uint64_t functionStart,
519-
A &addressSpace, Registers_arm64 &registers) {
519+
A &addressSpace, unw_word_t procInfoFlags, Registers_arm64 &registers) {
520520
switch (compactEncoding & UNWIND_ARM64_MODE_MASK) {
521521
case UNWIND_ARM64_MODE_FRAME:
522522
return stepWithCompactEncodingFrame(compactEncoding, functionStart,
523-
addressSpace, registers);
523+
addressSpace, procInfoFlags, registers);
524524
case UNWIND_ARM64_MODE_FRAMELESS:
525525
return stepWithCompactEncodingFrameless(compactEncoding, functionStart,
526-
addressSpace, registers);
526+
addressSpace, procInfoFlags, registers);
527527
}
528528
_LIBUNWIND_ABORT("invalid compact unwind encoding");
529529
}
530530

531531
template <typename A>
532532
int CompactUnwinder_arm64<A>::stepWithCompactEncodingFrameless(
533533
compact_unwind_encoding_t encoding, uint64_t, A &addressSpace,
534+
unw_word_t procInfoFlags,
534535
Registers_arm64 &registers) {
535536
uint32_t stackSize =
536537
16 * EXTRACT_BITS(encoding, UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK);
@@ -601,19 +602,23 @@ int CompactUnwinder_arm64<A>::stepWithCompactEncodingFrameless(
601602
savedRegisterLoc -= 8;
602603
}
603604

605+
Registers_arm64::reg_t linkRegister = registers.getRegister(UNW_AARCH64_LR);
606+
604607
// subtract stack size off of sp
605608
registers.setSP(savedRegisterLoc);
606609

610+
registers.normalizeNewLinkRegister(linkRegister, procInfoFlags);
611+
607612
// set pc to be value in lr
608-
registers.setIP(registers.getRegister(UNW_AARCH64_LR));
613+
registers.setIP(linkRegister);
609614

610615
return UNW_STEP_SUCCESS;
611616
}
612617

613618
template <typename A>
614619
int CompactUnwinder_arm64<A>::stepWithCompactEncodingFrame(
615620
compact_unwind_encoding_t encoding, uint64_t, A &addressSpace,
616-
Registers_arm64 &registers) {
621+
unw_word_t procInfoFlags, Registers_arm64 &registers) {
617622
uint64_t savedRegisterLoc = registers.getFP() - 8;
618623

619624
if (encoding & UNWIND_ARM64_FRAME_X19_X20_PAIR) {
@@ -685,8 +690,13 @@ int CompactUnwinder_arm64<A>::stepWithCompactEncodingFrame(
685690
registers.setFP(addressSpace.get64(fp));
686691
// old sp is fp less saved fp and lr
687692
registers.setSP(fp + 16);
693+
688694
// pop return address into pc
689-
registers.setIP(addressSpace.get64(fp + 8));
695+
Registers_arm64::reg_t linkRegister = addressSpace.get64(fp + 8);
696+
697+
registers.normalizeNewLinkRegister(linkRegister, procInfoFlags);
698+
699+
registers.setIP(linkRegister);
690700

691701
return UNW_STEP_SUCCESS;
692702
}

libunwind/src/DwarfInstructions.hpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222
#include "dwarf2.h"
2323
#include "libunwind_ext.h"
2424

25+
#if __has_feature(ptrauth_calls)
26+
#include <ptrauth.h>
27+
#endif
2528

2629
namespace libunwind {
2730

@@ -35,6 +38,7 @@ class DwarfInstructions {
3538
typedef typename A::sint_t sint_t;
3639

3740
static int stepWithDwarf(A &addressSpace, pint_t pc, pint_t fdeStart,
41+
unw_word_t procInfoFlags,
3842
R &registers, bool &isSignalFrame, bool stage2);
3943

4044
private:
@@ -189,7 +193,9 @@ bool DwarfInstructions<A, R>::getRA_SIGN_STATE(A &addressSpace, R registers,
189193

190194
template <typename A, typename R>
191195
int DwarfInstructions<A, R>::stepWithDwarf(A &addressSpace, pint_t pc,
192-
pint_t fdeStart, R &registers,
196+
pint_t fdeStart,
197+
unw_word_t procInfoFlags,
198+
R &registers,
193199
bool &isSignalFrame, bool stage2) {
194200
FDE_Info fdeInfo;
195201
CIE_Info cieInfo;
@@ -245,7 +251,7 @@ int DwarfInstructions<A, R>::stepWithDwarf(A &addressSpace, pint_t pc,
245251
// by a CFI directive later on.
246252
newRegisters.setSP(cfa);
247253

248-
pint_t returnAddress = 0;
254+
typename R::reg_t returnAddress = 0;
249255
constexpr int lastReg = R::lastDwarfRegNum();
250256
static_assert(static_cast<int>(CFI_Parser<A>::kMaxRegisterNumber) >=
251257
lastReg,
@@ -363,6 +369,8 @@ int DwarfInstructions<A, R>::stepWithDwarf(A &addressSpace, pint_t pc,
363369
}
364370
#endif
365371

372+
newRegisters.normalizeNewLinkRegister(returnAddress, procInfoFlags);
373+
366374
// Return address is address after call site instruction, so setting IP to
367375
// that does simulates a return.
368376
newRegisters.setIP(returnAddress);

libunwind/src/DwarfParser.hpp

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@
2323

2424
#include "config.h"
2525

26+
#if __has_feature(ptrauth_calls)
27+
#include <ptrauth.h>
28+
#endif
29+
2630
namespace libunwind {
2731

2832
/// CFI_Parser does basic parsing of a CFI (Call Frame Information) records.
@@ -366,6 +370,7 @@ const char *CFI_Parser<A>::parseCIE(A &addressSpace, pint_t cie,
366370
cieInfo->returnAddressRegister = (uint8_t)raReg;
367371
// parse augmentation data based on augmentation string
368372
const char *result = NULL;
373+
pint_t resultAddr = 0;
369374
if (addressSpace.get8(strStart) == 'z') {
370375
// parse augmentation data length
371376
addressSpace.getULEB128(p, cieContentEnd);
@@ -379,7 +384,20 @@ const char *CFI_Parser<A>::parseCIE(A &addressSpace, pint_t cie,
379384
++p;
380385
cieInfo->personalityOffsetInCIE = (uint8_t)(p - cie);
381386
cieInfo->personality = addressSpace
382-
.getEncodedP(p, cieContentEnd, cieInfo->personalityEncoding);
387+
.getEncodedP(p, cieContentEnd, cieInfo->personalityEncoding,
388+
/*datarelBase=*/0, &resultAddr);
389+
#if __has_feature(ptrauth_calls)
390+
// The GOT for the personality function was signed address authenticated.
391+
// Resign is as a regular function pointer.
392+
if (cieInfo->personality) {
393+
void* signedPtr = ptrauth_auth_and_resign((void*)cieInfo->personality,
394+
ptrauth_key_function_pointer,
395+
resultAddr,
396+
ptrauth_key_function_pointer,
397+
0);
398+
cieInfo->personality = (__typeof(cieInfo->personality))signedPtr;
399+
}
400+
#endif
383401
break;
384402
case 'L':
385403
cieInfo->lsdaEncoding = addressSpace.get8(p);

libunwind/src/Registers.hpp

Lines changed: 98 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@
1818
#include "cet_unwind.h"
1919
#include "config.h"
2020
#include "libunwind.h"
21+
#include "libunwind_ext.h"
22+
23+
#if __has_feature(ptrauth_calls)
24+
#include <ptrauth.h>
25+
#endif
2126

2227
namespace libunwind {
2328

@@ -93,6 +98,10 @@ class _LIBUNWIND_HIDDEN Registers_x86 {
9398
uint32_t getEDI() const { return _registers.__edi; }
9499
void setEDI(uint32_t value) { _registers.__edi = value; }
95100

101+
typedef uint32_t reg_t;
102+
void normalizeNewLinkRegister(reg_t&, unw_word_t) { }
103+
void normalizeExistingLinkRegister(reg_t&) { }
104+
96105
private:
97106
struct GPRs {
98107
unsigned int __eax;
@@ -311,6 +320,10 @@ class _LIBUNWIND_HIDDEN Registers_x86_64 {
311320
uint64_t getR15() const { return _registers.__r15; }
312321
void setR15(uint64_t value) { _registers.__r15 = value; }
313322

323+
typedef uint64_t reg_t;
324+
void normalizeNewLinkRegister(reg_t&, unw_word_t) { }
325+
void normalizeExistingLinkRegister(reg_t&) { }
326+
314327
private:
315328
struct GPRs {
316329
uint64_t __rax;
@@ -620,6 +633,10 @@ class _LIBUNWIND_HIDDEN Registers_ppc {
620633
uint64_t getCR() const { return _registers.__cr; }
621634
void setCR(uint32_t value) { _registers.__cr = value; }
622635

636+
typedef uint32_t reg_t;
637+
void normalizeNewLinkRegister(reg_t&, unw_word_t) { }
638+
void normalizeExistingLinkRegister(reg_t&) { }
639+
623640
private:
624641
struct ppc_thread_state_t {
625642
unsigned int __srr0; /* Instruction address register (PC) */
@@ -1815,6 +1832,8 @@ class _LIBUNWIND_HIDDEN Registers_arm64 {
18151832
public:
18161833
Registers_arm64();
18171834
Registers_arm64(const void *registers);
1835+
Registers_arm64(const Registers_arm64&);
1836+
Registers_arm64& operator=(const Registers_arm64&);
18181837

18191838
bool validRegister(int num) const;
18201839
uint64_t getRegister(int num) const;
@@ -1834,11 +1853,63 @@ class _LIBUNWIND_HIDDEN Registers_arm64 {
18341853

18351854
uint64_t getSP() const { return _registers.__sp; }
18361855
void setSP(uint64_t value) { _registers.__sp = value; }
1837-
uint64_t getIP() const { return _registers.__pc; }
1838-
void setIP(uint64_t value) { _registers.__pc = value; }
1856+
uint64_t getIP() const {
1857+
uint64_t value = _registers.__pc;
1858+
#if __has_feature(ptrauth_calls)
1859+
// Note the value of the PC was signed to its address in the register state
1860+
// but everyone else expects it to be sign by the SP, so convert on return.
1861+
value = (uint64_t)ptrauth_auth_and_resign((void*)_registers.__pc,
1862+
ptrauth_key_return_address,
1863+
&_registers.__pc,
1864+
ptrauth_key_return_address,
1865+
getSP());
1866+
#endif
1867+
return value;
1868+
}
1869+
void setIP(uint64_t value) {
1870+
#if __has_feature(ptrauth_calls)
1871+
// Note the value which was set should have been signed with the SP.
1872+
// We then resign with the slot we are being stored in to so that both SP and LR
1873+
// can't be spoofed at the same time.
1874+
value = (uint64_t)ptrauth_auth_and_resign((void*)value,
1875+
ptrauth_key_return_address,
1876+
getSP(),
1877+
ptrauth_key_return_address,
1878+
&_registers.__pc);
1879+
#endif
1880+
_registers.__pc = value;
1881+
}
18391882
uint64_t getFP() const { return _registers.__fp; }
18401883
void setFP(uint64_t value) { _registers.__fp = value; }
18411884

1885+
typedef uint64_t reg_t;
1886+
void normalizeNewLinkRegister(reg_t& linkRegister, unw_word_t procInfoFlags) {
1887+
(void)linkRegister;
1888+
(void)procInfoFlags;
1889+
#if __has_feature(ptrauth_calls)
1890+
if (procInfoFlags == ProcInfoFlags_IsARM64Image) {
1891+
// If the current frame is arm64e then the LR should have been signed by
1892+
// the SP. In this case, we'll just leave it as is. For other frames,
1893+
// we'll now sign the LR so that setIP below can assume all inputs are signed.
1894+
linkRegister = (uint64_t)ptrauth_sign_unauthenticated((void*)linkRegister,
1895+
ptrauth_key_return_address,
1896+
_registers.__sp);
1897+
}
1898+
#endif
1899+
}
1900+
1901+
void normalizeExistingLinkRegister(reg_t& linkRegister) {
1902+
(void)linkRegister;
1903+
#if __has_feature(ptrauth_calls)
1904+
// If we are in an arm64/arm64e frame, then the PC should have been signed with the SP
1905+
linkRegister = (uint64_t)ptrauth_auth_data((void*)linkRegister, ptrauth_key_return_address, _registers.__sp);
1906+
#endif
1907+
}
1908+
1909+
// arm64_32 and i386 simulator hack
1910+
void normalizeNewLinkRegister(uint32_t&, unw_word_t) { }
1911+
void normalizeExistingLinkRegister(uint32_t&) { }
1912+
18421913
private:
18431914
struct GPRs {
18441915
uint64_t __x[29]; // x0-x28
@@ -1866,6 +1937,25 @@ inline Registers_arm64::Registers_arm64(const void *registers) {
18661937
memcpy(_vectorHalfRegisters,
18671938
static_cast<const uint8_t *>(registers) + sizeof(GPRs),
18681939
sizeof(_vectorHalfRegisters));
1940+
#if __has_feature(ptrauth_calls)
1941+
uint64_t pcRegister = 0;
1942+
memcpy(&pcRegister, ((uint8_t*)&_registers) + offsetof(GPRs, __pc), sizeof(pcRegister));
1943+
setIP(pcRegister);
1944+
#endif
1945+
}
1946+
1947+
inline Registers_arm64::Registers_arm64(const Registers_arm64& other) {
1948+
*this = other;
1949+
}
1950+
1951+
inline Registers_arm64& Registers_arm64::operator=(const Registers_arm64& other) {
1952+
memcpy(&_registers, &other._registers, sizeof(_registers));
1953+
memcpy(_vectorHalfRegisters, &other._vectorHalfRegisters,
1954+
sizeof(_vectorHalfRegisters));
1955+
#if __has_feature(ptrauth_calls)
1956+
setIP(other.getIP());
1957+
#endif
1958+
return *this;
18691959
}
18701960

18711961
inline Registers_arm64::Registers_arm64() {
@@ -1891,7 +1981,7 @@ inline bool Registers_arm64::validRegister(int regNum) const {
18911981

18921982
inline uint64_t Registers_arm64::getRegister(int regNum) const {
18931983
if (regNum == UNW_REG_IP || regNum == UNW_AARCH64_PC)
1894-
return _registers.__pc;
1984+
return getIP();
18951985
if (regNum == UNW_REG_SP || regNum == UNW_AARCH64_SP)
18961986
return _registers.__sp;
18971987
if (regNum == UNW_AARCH64_RA_SIGN_STATE)
@@ -1907,7 +1997,7 @@ inline uint64_t Registers_arm64::getRegister(int regNum) const {
19071997

19081998
inline void Registers_arm64::setRegister(int regNum, uint64_t value) {
19091999
if (regNum == UNW_REG_IP || regNum == UNW_AARCH64_PC)
1910-
_registers.__pc = value;
2000+
setIP(value);
19112001
else if (regNum == UNW_REG_SP || regNum == UNW_AARCH64_SP)
19122002
_registers.__sp = value;
19132003
else if (regNum == UNW_AARCH64_RA_SIGN_STATE)
@@ -2129,6 +2219,10 @@ class _LIBUNWIND_HIDDEN Registers_arm {
21292219
uint32_t getIP() const { return _registers.__pc; }
21302220
void setIP(uint32_t value) { _registers.__pc = value; }
21312221

2222+
typedef uint32_t reg_t;
2223+
void normalizeNewLinkRegister(reg_t&, unw_word_t) { }
2224+
void normalizeExistingLinkRegister(reg_t&) { }
2225+
21322226
void saveVFPAsX() {
21332227
assert(_use_X_for_vfp_save || !_saved_vfp_d0_d15);
21342228
_use_X_for_vfp_save = true;

0 commit comments

Comments
 (0)