Skip to content

Commit c378bb1

Browse files
authored
CodeGen: Optionally emit PAuth relocations as IRELATIVE relocations.
This supports the following use cases: - ConstantPtrAuth expressions that are unrepresentable using standard PAuth relocations such as expressions involving an integer operand or deactivation symbols. - libc implementations that do not support PAuth relocations. For more information see the RFC: https://discourse.llvm.org/t/rfc-structure-protection-a-family-of-uaf-mitigation-techniques/85555 Reviewers: MaskRay, fmayer, smithp35, kovdan01 Reviewed By: fmayer Pull Request: llvm#133533
1 parent 3a25a4a commit c378bb1

File tree

3 files changed

+282
-16
lines changed

3 files changed

+282
-16
lines changed

llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp

Lines changed: 203 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,14 @@
4949
#include "llvm/IR/Module.h"
5050
#include "llvm/MC/MCAsmInfo.h"
5151
#include "llvm/MC/MCContext.h"
52+
#include "llvm/MC/MCExpr.h"
5253
#include "llvm/MC/MCInst.h"
5354
#include "llvm/MC/MCInstBuilder.h"
5455
#include "llvm/MC/MCSectionELF.h"
5556
#include "llvm/MC/MCSectionMachO.h"
5657
#include "llvm/MC/MCStreamer.h"
5758
#include "llvm/MC/MCSymbol.h"
59+
#include "llvm/MC/MCValue.h"
5860
#include "llvm/MC/TargetRegistry.h"
5961
#include "llvm/Support/Casting.h"
6062
#include "llvm/Support/CommandLine.h"
@@ -95,6 +97,7 @@ class AArch64AsmPrinter : public AsmPrinter {
9597
bool EnableImportCallOptimization = false;
9698
DenseMap<MCSection *, std::vector<std::pair<MCSymbol *, MCSymbol *>>>
9799
SectionToImportedFunctionCalls;
100+
unsigned PAuthIFuncNextUniqueID = 1;
98101

99102
public:
100103
static char ID;
@@ -211,6 +214,12 @@ class AArch64AsmPrinter : public AsmPrinter {
211214
// authenticating)
212215
void LowerLOADgotAUTH(const MachineInstr &MI);
213216

217+
const MCExpr *emitPAuthRelocationAsIRelative(const MCExpr *Target,
218+
uint16_t Disc,
219+
AArch64PACKey::ID KeyID,
220+
bool HasAddressDiversity,
221+
bool IsDSOLocal);
222+
214223
/// tblgen'erated driver function for lowering simple MI->MC
215224
/// pseudo instructions.
216225
bool lowerPseudoInstExpansion(const MachineInstr *MI, MCInst &Inst);
@@ -2299,6 +2308,182 @@ void AArch64AsmPrinter::emitPtrauthBranch(const MachineInstr *MI) {
22992308
EmitToStreamer(*OutStreamer, BRInst);
23002309
}
23012310

2311+
static void emitAddress(MCStreamer &Streamer, MCRegister Reg,
2312+
const MCExpr *Expr, bool DSOLocal,
2313+
const MCSubtargetInfo &STI) {
2314+
MCValue Val;
2315+
if (!Expr->evaluateAsRelocatable(Val, nullptr))
2316+
report_fatal_error("emitAddress could not evaluate");
2317+
if (DSOLocal) {
2318+
Streamer.emitInstruction(
2319+
MCInstBuilder(AArch64::ADRP)
2320+
.addReg(Reg)
2321+
.addExpr(MCSpecifierExpr::create(Expr, AArch64::S_ABS_PAGE,
2322+
Streamer.getContext())),
2323+
STI);
2324+
Streamer.emitInstruction(
2325+
MCInstBuilder(AArch64::ADDXri)
2326+
.addReg(Reg)
2327+
.addReg(Reg)
2328+
.addExpr(MCSpecifierExpr::create(Expr, AArch64::S_LO12,
2329+
Streamer.getContext()))
2330+
.addImm(0),
2331+
STI);
2332+
} else {
2333+
auto *SymRef =
2334+
MCSymbolRefExpr::create(Val.getAddSym(), Streamer.getContext());
2335+
Streamer.emitInstruction(
2336+
MCInstBuilder(AArch64::ADRP)
2337+
.addReg(Reg)
2338+
.addExpr(MCSpecifierExpr::create(SymRef, AArch64::S_GOT_PAGE,
2339+
Streamer.getContext())),
2340+
STI);
2341+
Streamer.emitInstruction(
2342+
MCInstBuilder(AArch64::LDRXui)
2343+
.addReg(Reg)
2344+
.addReg(Reg)
2345+
.addExpr(MCSpecifierExpr::create(SymRef, AArch64::S_GOT_LO12,
2346+
Streamer.getContext())),
2347+
STI);
2348+
if (Val.getConstant())
2349+
Streamer.emitInstruction(MCInstBuilder(AArch64::ADDXri)
2350+
.addReg(Reg)
2351+
.addReg(Reg)
2352+
.addImm(Val.getConstant())
2353+
.addImm(0),
2354+
STI);
2355+
}
2356+
}
2357+
2358+
static bool targetSupportsPAuthRelocation(const Triple &TT,
2359+
const MCExpr *Target) {
2360+
// No released version of glibc supports PAuth relocations.
2361+
if (TT.isOSGlibc())
2362+
return false;
2363+
2364+
// We emit PAuth constants as IRELATIVE relocations in cases where the
2365+
// constant cannot be represented as a PAuth relocation:
2366+
// 1) The signed value is not a symbol.
2367+
return !isa<MCConstantExpr>(Target);
2368+
}
2369+
2370+
static bool targetSupportsIRelativeRelocation(const Triple &TT) {
2371+
// IFUNCs are ELF-only.
2372+
if (!TT.isOSBinFormatELF())
2373+
return false;
2374+
2375+
// musl doesn't support IFUNCs.
2376+
if (TT.isMusl())
2377+
return false;
2378+
2379+
return true;
2380+
}
2381+
2382+
// Emit an ifunc resolver that returns a signed pointer to the specified target,
2383+
// and return a FUNCINIT reference to the resolver. In the linked binary, this
2384+
// function becomes the target of an IRELATIVE relocation. This resolver is used
2385+
// to relocate signed pointers in global variable initializers in special cases
2386+
// where the standard R_AARCH64_AUTH_ABS64 relocation would not work.
2387+
//
2388+
// Example (signed null pointer, not address discriminated):
2389+
//
2390+
// .8byte .Lpauth_ifunc0
2391+
// .pushsection .text.startup,"ax",@progbits
2392+
// .Lpauth_ifunc0:
2393+
// mov x0, #0
2394+
// mov x1, #12345
2395+
// b __emupac_pacda
2396+
//
2397+
// Example (signed null pointer, address discriminated):
2398+
//
2399+
// .Ltmp:
2400+
// .8byte .Lpauth_ifunc0
2401+
// .pushsection .text.startup,"ax",@progbits
2402+
// .Lpauth_ifunc0:
2403+
// mov x0, #0
2404+
// adrp x1, .Ltmp
2405+
// add x1, x1, :lo12:.Ltmp
2406+
// b __emupac_pacda
2407+
// .popsection
2408+
//
2409+
// Example (signed pointer to symbol, not address discriminated):
2410+
//
2411+
// .Ltmp:
2412+
// .8byte .Lpauth_ifunc0
2413+
// .pushsection .text.startup,"ax",@progbits
2414+
// .Lpauth_ifunc0:
2415+
// adrp x0, symbol
2416+
// add x0, x0, :lo12:symbol
2417+
// mov x1, #12345
2418+
// b __emupac_pacda
2419+
// .popsection
2420+
const MCExpr *AArch64AsmPrinter::emitPAuthRelocationAsIRelative(
2421+
const MCExpr *Target, uint16_t Disc, AArch64PACKey::ID KeyID,
2422+
bool HasAddressDiversity, bool IsDSOLocal) {
2423+
const Triple &TT = TM.getTargetTriple();
2424+
2425+
// We only emit an IRELATIVE relocation if the target supports IRELATIVE and
2426+
// does not support the kind of PAuth relocation that we are trying to emit.
2427+
if (targetSupportsPAuthRelocation(TT, Target) ||
2428+
!targetSupportsIRelativeRelocation(TT))
2429+
return nullptr;
2430+
2431+
// For now, only the DA key is supported.
2432+
if (KeyID != AArch64PACKey::DA)
2433+
return nullptr;
2434+
2435+
std::unique_ptr<MCSubtargetInfo> STI(
2436+
TM.getTarget().createMCSubtargetInfo(TT, "", ""));
2437+
assert(STI && "Unable to create subtarget info");
2438+
2439+
MCSymbol *Place = OutStreamer->getContext().createTempSymbol();
2440+
OutStreamer->emitLabel(Place);
2441+
OutStreamer->pushSection();
2442+
2443+
OutStreamer->switchSection(OutStreamer->getContext().getELFSection(
2444+
".text.startup", ELF::SHT_PROGBITS, ELF::SHF_ALLOC | ELF::SHF_EXECINSTR,
2445+
0, "", true, PAuthIFuncNextUniqueID++, nullptr));
2446+
2447+
MCSymbol *IRelativeSym =
2448+
OutStreamer->getContext().createLinkerPrivateSymbol("pauth_ifunc");
2449+
OutStreamer->emitLabel(IRelativeSym);
2450+
if (isa<MCConstantExpr>(Target)) {
2451+
OutStreamer->emitInstruction(MCInstBuilder(AArch64::MOVZXi)
2452+
.addReg(AArch64::X0)
2453+
.addExpr(Target)
2454+
.addImm(0),
2455+
*STI);
2456+
} else {
2457+
emitAddress(*OutStreamer, AArch64::X0, Target, IsDSOLocal, *STI);
2458+
}
2459+
if (HasAddressDiversity) {
2460+
auto *PlacePlusDisc = MCBinaryExpr::createAdd(
2461+
MCSymbolRefExpr::create(Place, OutStreamer->getContext()),
2462+
MCConstantExpr::create(static_cast<int16_t>(Disc),
2463+
OutStreamer->getContext()),
2464+
OutStreamer->getContext());
2465+
emitAddress(*OutStreamer, AArch64::X1, PlacePlusDisc, /*IsDSOLocal=*/true,
2466+
*STI);
2467+
} else {
2468+
emitMOVZ(AArch64::X1, Disc, 0);
2469+
}
2470+
2471+
// We don't know the subtarget because this is being emitted for a global
2472+
// initializer. Because the performance of IFUNC resolvers is unimportant, we
2473+
// always call the EmuPAC runtime, which will end up using the PAC instruction
2474+
// if the target supports PAC.
2475+
MCSymbol *EmuPAC =
2476+
OutStreamer->getContext().getOrCreateSymbol("__emupac_pacda");
2477+
const MCSymbolRefExpr *EmuPACRef =
2478+
MCSymbolRefExpr::create(EmuPAC, OutStreamer->getContext());
2479+
OutStreamer->emitInstruction(MCInstBuilder(AArch64::B).addExpr(EmuPACRef),
2480+
*STI);
2481+
OutStreamer->popSection();
2482+
2483+
return MCSymbolRefExpr::create(IRelativeSym, AArch64::S_FUNCINIT,
2484+
OutStreamer->getContext());
2485+
}
2486+
23022487
const MCExpr *
23032488
AArch64AsmPrinter::lowerConstantPtrAuth(const ConstantPtrAuth &CPA) {
23042489
MCContext &Ctx = OutContext;
@@ -2310,23 +2495,20 @@ AArch64AsmPrinter::lowerConstantPtrAuth(const ConstantPtrAuth &CPA) {
23102495

23112496
auto *BaseGVB = dyn_cast<GlobalValue>(BaseGV);
23122497

2313-
// If we can't understand the referenced ConstantExpr, there's nothing
2314-
// else we can do: emit an error.
2315-
if (!BaseGVB) {
2316-
BaseGV->getContext().emitError(
2317-
"cannot resolve target base/addend of ptrauth constant");
2318-
return nullptr;
2498+
const MCExpr *Sym;
2499+
if (BaseGVB) {
2500+
// If there is an addend, turn that into the appropriate MCExpr.
2501+
Sym = MCSymbolRefExpr::create(getSymbol(BaseGVB), Ctx);
2502+
if (Offset.sgt(0))
2503+
Sym = MCBinaryExpr::createAdd(
2504+
Sym, MCConstantExpr::create(Offset.getSExtValue(), Ctx), Ctx);
2505+
else if (Offset.slt(0))
2506+
Sym = MCBinaryExpr::createSub(
2507+
Sym, MCConstantExpr::create((-Offset).getSExtValue(), Ctx), Ctx);
2508+
} else {
2509+
Sym = MCConstantExpr::create(Offset.getSExtValue(), Ctx);
23192510
}
23202511

2321-
// If there is an addend, turn that into the appropriate MCExpr.
2322-
const MCExpr *Sym = MCSymbolRefExpr::create(getSymbol(BaseGVB), Ctx);
2323-
if (Offset.sgt(0))
2324-
Sym = MCBinaryExpr::createAdd(
2325-
Sym, MCConstantExpr::create(Offset.getSExtValue(), Ctx), Ctx);
2326-
else if (Offset.slt(0))
2327-
Sym = MCBinaryExpr::createSub(
2328-
Sym, MCConstantExpr::create((-Offset).getSExtValue(), Ctx), Ctx);
2329-
23302512
uint64_t KeyID = CPA.getKey()->getZExtValue();
23312513
// We later rely on valid KeyID value in AArch64PACKeyIDToString call from
23322514
// AArch64AuthMCExpr::printImpl, so fail fast.
@@ -2344,6 +2526,12 @@ AArch64AsmPrinter::lowerConstantPtrAuth(const ConstantPtrAuth &CPA) {
23442526
Disc = 0;
23452527
}
23462528

2529+
// Check if we need to represent this with an IRELATIVE and emit it if so.
2530+
if (auto *IFuncSym = emitPAuthRelocationAsIRelative(
2531+
Sym, Disc, AArch64PACKey::ID(KeyID), CPA.hasAddressDiscriminator(),
2532+
BaseGVB && BaseGVB->isDSOLocal()))
2533+
return IFuncSym;
2534+
23472535
// Finally build the complete @AUTH expr.
23482536
return AArch64AuthMCExpr::create(Sym, Disc, AArch64PACKey::ID(KeyID),
23492537
CPA.hasAddressDiscriminator(), Ctx);
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
; RUN: llc -mtriple aarch64-linux-gnu -mattr=+pauth -filetype=asm -o - %s | FileCheck %s
2+
3+
; CHECK: nullref:
4+
; CHECK-NEXT: [[PLACE:.*]]:
5+
; CHECK-NEXT: .section .text.startup
6+
; CHECK-NEXT: [[FUNC:.*]]:
7+
; CHECK-NEXT: movz x0, #0
8+
; CHECK-NEXT: mov x1, #1
9+
; CHECK-NEXT: b __emupac_pacda
10+
; CHECK-NEXT: .section .rodata
11+
; CHECK-NEXT: .xword [[FUNC]]@FUNCINIT
12+
@nullref = constant ptr ptrauth (ptr null, i32 2, i64 1, ptr null), align 8
13+
14+
@dsolocal = external dso_local global i8
15+
16+
; CHECK: dsolocalref:
17+
; CHECK-NEXT: [[PLACE:.*]]:
18+
; CHECK-NEXT: .section .text.startup
19+
; CHECK-NEXT: [[FUNC:.*]]:
20+
; CHECK-NEXT: adrp x0, dsolocal
21+
; CHECK-NEXT: add x0, x0, :lo12:dsolocal
22+
; CHECK-NEXT: mov x1, #2
23+
; CHECK-NEXT: b __emupac_pacda
24+
; CHECK-NEXT: .section .rodata
25+
; CHECK-NEXT: .xword [[FUNC]]@FUNCINIT
26+
@dsolocalref = constant ptr ptrauth (ptr @dsolocal, i32 2, i64 2, ptr null), align 8
27+
28+
; CHECK: dsolocalref8:
29+
; CHECK-NEXT: [[PLACE:.*]]:
30+
; CHECK-NEXT: .section .text.startup
31+
; CHECK-NEXT: [[FUNC:.*]]:
32+
; CHECK-NEXT: adrp x0, dsolocal+8
33+
; CHECK-NEXT: add x0, x0, :lo12:dsolocal+8
34+
; CHECK-NEXT: mov x1, #3
35+
; CHECK-NEXT: b __emupac_pacda
36+
; CHECK-NEXT: .section .rodata
37+
; CHECK-NEXT: .xword [[FUNC]]@FUNCINIT
38+
@dsolocalref8 = constant ptr ptrauth (ptr getelementptr (i8, ptr @dsolocal, i64 8), i32 2, i64 3, ptr null), align 8
39+
40+
; CHECK: disc:
41+
; CHECK-NEXT: [[PLACE:.*]]:
42+
; CHECK-NEXT: .section .text.startup
43+
; CHECK-NEXT: [[FUNC:.*]]:
44+
; CHECK-NEXT: adrp x0, dsolocal
45+
; CHECK-NEXT: add x0, x0, :lo12:dsolocal
46+
; CHECK-NEXT: adrp x1, [[PLACE]]
47+
; CHECK-NEXT: add x1, x1, :lo12:[[PLACE]]
48+
; CHECK-NEXT: b __emupac_pacda
49+
; CHECK-NEXT: .section .rodata
50+
; CHECK-NEXT: .xword [[FUNC]]@FUNCINIT
51+
@disc = constant ptr ptrauth (ptr @dsolocal, i32 2, i64 0, ptr @disc), align 8
52+
53+
@global = external global i8
54+
55+
; CHECK: globalref:
56+
; CHECK-NEXT: [[PLACE:.*]]:
57+
; CHECK-NEXT: .section .text.startup
58+
; CHECK-NEXT: [[FUNC:.*]]:
59+
; CHECK-NEXT: adrp x0, :got:global
60+
; CHECK-NEXT: ldr x0, [x0, :got_lo12:global]
61+
; CHECK-NEXT: mov x1, #4
62+
; CHECK-NEXT: b __emupac_pacda
63+
; CHECK-NEXT: .section .rodata
64+
; CHECK-NEXT: .xword [[FUNC]]@FUNCINIT
65+
@globalref = constant ptr ptrauth (ptr @global, i32 2, i64 4, ptr null), align 8
66+
67+
; CHECK: globalref8:
68+
; CHECK-NEXT: [[PLACE:.*]]:
69+
; CHECK-NEXT: .section .text.startup
70+
; CHECK-NEXT: [[FUNC:.*]]:
71+
; CHECK-NEXT: adrp x0, :got:global
72+
; CHECK-NEXT: ldr x0, [x0, :got_lo12:global]
73+
; CHECK-NEXT: add x0, x0, #8
74+
; CHECK-NEXT: mov x1, #5
75+
; CHECK-NEXT: b __emupac_pacda
76+
; CHECK-NEXT: .section .rodata
77+
; CHECK-NEXT: .xword [[FUNC]]@FUNCINIT
78+
@globalref8 = constant ptr ptrauth (ptr getelementptr (i8, ptr @global, i64 8), i32 2, i64 5, ptr null), align 8

llvm/test/CodeGen/AArch64/ptrauth-type-info-vptr-discr.ll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
; RUN: llc -mtriple aarch64-linux-gnu -mattr=+pauth -filetype=asm -o - %s | FileCheck --check-prefix=ELF %s
1+
; RUN: llc -mtriple aarch64-linux-musl -mattr=+pauth -filetype=asm -o - %s | FileCheck --check-prefix=ELF %s
22
; RUN: llc -mtriple aarch64-apple-darwin -mattr=+pauth -filetype=asm -o - %s | FileCheck --check-prefix=MACHO %s
33

44
; ELF-LABEL: _ZTI10Disc:

0 commit comments

Comments
 (0)