Skip to content

Commit b8721fa

Browse files
[AArch64][PAC] Sign block addresses used in indirectbr. (llvm#97647)
Enabled in clang using: -fptrauth-indirect-gotos and at the IR level using function attribute: "ptrauth-indirect-gotos" Signing uses IA and a per-function integer discriminator. The discriminator isn't ABI-visible, and is currently: ptrauth_string_discriminator("<function_name> blockaddress") A sufficiently sophisticated frontend could benefit from per-indirectbr discrimination, which would need additional machinery, such as allowing "ptrauth" bundles on indirectbr. For our purposes, the simple scheme above is sufficient. This approach doesn't support subtracting label addresses and using the result as offsets, because each label address is signed. Pointer arithmetic on signed pointers corrupts the signature bits, and because label address expressions aren't typed beyond void*, we can't do anything reliably intelligent on the arithmetic exprs. Not signing addresses when used to form offsets would allow easily hijacking control flow by overwriting the offset. This diagnoses the basic cases (`&&lbl2 - &&lbl1`) in the frontend, while we evaluate either alternative implementations (e.g., lowering blockaddress to a bb number, and indirectbr to a checked jump-table), or better diagnostics (both at the frontend level and on unencodable IR constants).
1 parent 34ab855 commit b8721fa

23 files changed

+498
-11
lines changed

clang/include/clang/Basic/DiagnosticSemaKinds.td

+3
Original file line numberDiff line numberDiff line change
@@ -949,6 +949,9 @@ def note_ptrauth_virtual_function_pointer_incomplete_arg_ret :
949949
def note_ptrauth_virtual_function_incomplete_arg_ret_type :
950950
Note<"%0 is incomplete">;
951951

952+
def err_ptrauth_indirect_goto_addrlabel_arithmetic : Error<
953+
"%select{subtraction|addition}0 of address-of-label expressions is not "
954+
"supported with ptrauth indirect gotos">;
952955

953956
/// main()
954957
// static main() is not an error in C, just in C++.

clang/include/clang/Basic/Features.def

+1
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ FEATURE(ptrauth_type_info_vtable_pointer_discrimination, LangOpts.PointerAuthTyp
112112
FEATURE(ptrauth_member_function_pointer_type_discrimination, LangOpts.PointerAuthCalls)
113113
FEATURE(ptrauth_init_fini, LangOpts.PointerAuthInitFini)
114114
FEATURE(ptrauth_function_pointer_type_discrimination, LangOpts.PointerAuthFunctionTypeDiscrimination)
115+
FEATURE(ptrauth_indirect_gotos, LangOpts.PointerAuthIndirectGotos)
115116
EXTENSION(swiftcc,
116117
PP.getTargetInfo().checkCallingConvention(CC_Swift) ==
117118
clang::TargetInfo::CCCR_OK)

clang/include/clang/Basic/LangOptions.def

+1
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ LANGOPT(ExperimentalLibrary, 1, 0, "enable unstable and experimental library fea
165165
LANGOPT(PointerAuthIntrinsics, 1, 0, "pointer authentication intrinsics")
166166
LANGOPT(PointerAuthCalls , 1, 0, "function pointer authentication")
167167
LANGOPT(PointerAuthReturns, 1, 0, "return pointer authentication")
168+
LANGOPT(PointerAuthIndirectGotos, 1, 0, "indirect gotos pointer authentication")
168169
LANGOPT(PointerAuthAuthTraps, 1, 0, "pointer authentication failure traps")
169170
LANGOPT(PointerAuthVTPtrAddressDiscrimination, 1, 0, "incorporate address discrimination in authenticated vtable pointers")
170171
LANGOPT(PointerAuthVTPtrTypeDiscrimination, 1, 0, "incorporate type discrimination in authenticated vtable pointers")

clang/include/clang/Basic/PointerAuthOptions.h

+3
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,9 @@ class PointerAuthSchema {
159159
};
160160

161161
struct PointerAuthOptions {
162+
/// Do indirect goto label addresses need to be authenticated?
163+
bool IndirectGotos = false;
164+
162165
/// The ABI for C function pointers.
163166
PointerAuthSchema FunctionPointers;
164167

clang/include/clang/Driver/Options.td

+2
Original file line numberDiff line numberDiff line change
@@ -4254,6 +4254,8 @@ defm ptrauth_type_info_vtable_pointer_discrimination :
42544254
defm ptrauth_init_fini : OptInCC1FFlag<"ptrauth-init-fini", "Enable signing of function pointers in init/fini arrays">;
42554255
defm ptrauth_function_pointer_type_discrimination : OptInCC1FFlag<"ptrauth-function-pointer-type-discrimination",
42564256
"Enable type discrimination on C function pointers">;
4257+
defm ptrauth_indirect_gotos : OptInCC1FFlag<"ptrauth-indirect-gotos",
4258+
"Enable signing and authentication of indirect goto targets">;
42574259
}
42584260

42594261
def fenable_matrix : Flag<["-"], "fenable-matrix">, Group<f_Group>,

clang/lib/CodeGen/CodeGenFunction.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -882,6 +882,8 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy,
882882
const CodeGenOptions &CodeGenOpts = CGM.getCodeGenOpts();
883883
if (CodeGenOpts.PointerAuth.FunctionPointers)
884884
Fn->addFnAttr("ptrauth-calls");
885+
if (CodeGenOpts.PointerAuth.IndirectGotos)
886+
Fn->addFnAttr("ptrauth-indirect-gotos");
885887

886888
// Apply xray attributes to the function (as a string, for now)
887889
bool AlwaysXRayAttr = false;

clang/lib/Driver/ToolChains/Clang.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -1848,6 +1848,9 @@ void Clang::AddAArch64TargetArgs(const ArgList &Args,
18481848
Args.addOptInFlag(
18491849
CmdArgs, options::OPT_fptrauth_function_pointer_type_discrimination,
18501850
options::OPT_fno_ptrauth_function_pointer_type_discrimination);
1851+
1852+
Args.addOptInFlag(CmdArgs, options::OPT_fptrauth_indirect_gotos,
1853+
options::OPT_fno_ptrauth_indirect_gotos);
18511854
}
18521855

18531856
void Clang::AddLoongArchTargetArgs(const ArgList &Args,

clang/lib/Frontend/CompilerInvocation.cpp

+5-1
Original file line numberDiff line numberDiff line change
@@ -1504,13 +1504,14 @@ void CompilerInvocation::setDefaultPointerAuthOptions(
15041504
Opts.CXXMemberFunctionPointers =
15051505
PointerAuthSchema(Key::ASIA, false, Discrimination::Type);
15061506
}
1507+
Opts.IndirectGotos = LangOpts.PointerAuthIndirectGotos;
15071508
}
15081509

15091510
static void parsePointerAuthOptions(PointerAuthOptions &Opts,
15101511
const LangOptions &LangOpts,
15111512
const llvm::Triple &Triple,
15121513
DiagnosticsEngine &Diags) {
1513-
if (!LangOpts.PointerAuthCalls)
1514+
if (!LangOpts.PointerAuthCalls && !LangOpts.PointerAuthIndirectGotos)
15141515
return;
15151516

15161517
CompilerInvocation::setDefaultPointerAuthOptions(Opts, LangOpts, Triple);
@@ -3414,6 +3415,8 @@ static void GeneratePointerAuthArgs(const LangOptions &Opts,
34143415
GenerateArg(Consumer, OPT_fptrauth_calls);
34153416
if (Opts.PointerAuthReturns)
34163417
GenerateArg(Consumer, OPT_fptrauth_returns);
3418+
if (Opts.PointerAuthIndirectGotos)
3419+
GenerateArg(Consumer, OPT_fptrauth_indirect_gotos);
34173420
if (Opts.PointerAuthAuthTraps)
34183421
GenerateArg(Consumer, OPT_fptrauth_auth_traps);
34193422
if (Opts.PointerAuthVTPtrAddressDiscrimination)
@@ -3434,6 +3437,7 @@ static void ParsePointerAuthArgs(LangOptions &Opts, ArgList &Args,
34343437
Opts.PointerAuthIntrinsics = Args.hasArg(OPT_fptrauth_intrinsics);
34353438
Opts.PointerAuthCalls = Args.hasArg(OPT_fptrauth_calls);
34363439
Opts.PointerAuthReturns = Args.hasArg(OPT_fptrauth_returns);
3440+
Opts.PointerAuthIndirectGotos = Args.hasArg(OPT_fptrauth_indirect_gotos);
34373441
Opts.PointerAuthAuthTraps = Args.hasArg(OPT_fptrauth_auth_traps);
34383442
Opts.PointerAuthVTPtrAddressDiscrimination =
34393443
Args.hasArg(OPT_fptrauth_vtable_pointer_address_discrimination);

clang/lib/Sema/SemaExpr.cpp

+17
Original file line numberDiff line numberDiff line change
@@ -10909,6 +10909,14 @@ QualType Sema::CheckAdditionOperands(ExprResult &LHS, ExprResult &RHS,
1090910909
if (isObjCPointer && checkArithmeticOnObjCPointer(*this, Loc, PExp))
1091010910
return QualType();
1091110911

10912+
// Arithmetic on label addresses is normally allowed, except when we add
10913+
// a ptrauth signature to the addresses.
10914+
if (isa<AddrLabelExpr>(PExp) && getLangOpts().PointerAuthIndirectGotos) {
10915+
Diag(Loc, diag::err_ptrauth_indirect_goto_addrlabel_arithmetic)
10916+
<< /*addition*/ 1;
10917+
return QualType();
10918+
}
10919+
1091210920
// Check array bounds for pointer arithemtic
1091310921
CheckArrayAccess(PExp, IExp);
1091410922

@@ -10983,6 +10991,15 @@ QualType Sema::CheckSubtractionOperands(ExprResult &LHS, ExprResult &RHS,
1098310991
checkArithmeticOnObjCPointer(*this, Loc, LHS.get()))
1098410992
return QualType();
1098510993

10994+
// Arithmetic on label addresses is normally allowed, except when we add
10995+
// a ptrauth signature to the addresses.
10996+
if (isa<AddrLabelExpr>(LHS.get()) &&
10997+
getLangOpts().PointerAuthIndirectGotos) {
10998+
Diag(Loc, diag::err_ptrauth_indirect_goto_addrlabel_arithmetic)
10999+
<< /*subtraction*/ 0;
11000+
return QualType();
11001+
}
11002+
1098611003
// The result type of a pointer-int computation is the pointer type.
1098711004
if (RHS.get()->getType()->isIntegerType()) {
1098811005
// Subtracting from a null pointer should produce a warning.

clang/test/CodeGen/ptrauth-function-attributes.c

+5
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,15 @@
44
// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -emit-llvm %s -o - | FileCheck %s --check-prefixes=ALL,CALLS
55
// RUN: %clang_cc1 -triple aarch64-linux-gnu -fptrauth-calls -emit-llvm %s -o - | FileCheck %s --check-prefixes=ALL,CALLS
66

7+
// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-indirect-gotos -emit-llvm %s -o - | FileCheck %s --check-prefixes=ALL,GOTOS
8+
// RUN: %clang_cc1 -triple arm64e-apple-ios -fptrauth-indirect-gotos -emit-llvm %s -o - | FileCheck %s --check-prefixes=ALL,GOTOS
9+
710
// ALL: define {{(dso_local )?}}void @test() #0
811
void test() {
912
}
1013

1114
// CALLS: attributes #0 = {{{.*}} "ptrauth-calls" {{.*}}}
1215

16+
// GOTOS: attributes #0 = {{{.*}} "ptrauth-indirect-gotos" {{.*}}}
17+
1318
// OFF-NOT: attributes {{.*}} "ptrauth-
+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// RUN: %clang_cc1 -triple arm64e-apple-darwin -fsyntax-only -verify %s -fptrauth-indirect-gotos
2+
3+
int f() {
4+
static void *addrs[] = { &&l1, &&l2 };
5+
6+
static int diffs[] = {
7+
&&l1 - &&l1, // expected-error{{subtraction of address-of-label expressions is not supported with ptrauth indirect gotos}}
8+
&&l1 - &&l2 // expected-error{{subtraction of address-of-label expressions is not supported with ptrauth indirect gotos}}
9+
};
10+
11+
int diff_32 = &&l1 - &&l2; // expected-error{{subtraction of address-of-label expressions is not supported with ptrauth indirect gotos}}
12+
goto *(&&l1 + diff_32); // expected-error{{addition of address-of-label expressions is not supported with ptrauth indirect gotos}}
13+
14+
l1:
15+
return 0;
16+
l2:
17+
return 1;
18+
}

llvm/docs/PointerAuth.md

+24
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ At the IR level, it is represented using:
1818
* a [set of intrinsics](#intrinsics) (to sign/authenticate pointers)
1919
* a [signed pointer constant](#constant) (to sign globals)
2020
* a [call operand bundle](#operand-bundle) (to authenticate called pointers)
21+
* a [set of function attributes](#function-attributes) (to describe what
22+
pointers are signed and how, to control implicit codegen in the backend, as
23+
well as preserve invariants in the mid-level optimizer)
2124

2225
The current implementation leverages the
2326
[Armv8.3-A PAuth/Pointer Authentication Code](#armv8-3-a-pauth-pointer-authentication-code)
@@ -287,6 +290,27 @@ but with the added guarantee that `%fp_i`, `%fp_auth`, and `%fp_auth_p`
287290
are not stored to (and reloaded from) memory.
288291

289292

293+
### Function Attributes
294+
295+
Some function attributes are used to describe other pointer authentication
296+
operations that are not otherwise explicitly expressed in IR.
297+
298+
#### ``ptrauth-indirect-gotos``
299+
300+
``ptrauth-indirect-gotos`` specifies that indirect gotos in this function
301+
should authenticate their target. At the IR level, no other change is needed.
302+
When lowering [``blockaddress`` constants](https://llvm.org/docs/LangRef.html#blockaddress),
303+
and [``indirectbr`` instructions](https://llvm.org/docs/LangRef.html#i-indirectbr),
304+
this tells the backend to respectively sign and authenticate the pointers.
305+
306+
The specific scheme isn't ABI-visible. Currently, the AArch64 backend
307+
signs blockaddresses using the `ASIA` key, with an integer discriminator
308+
derived from the parent function's name, using the SipHash stable discriminator:
309+
```
310+
ptrauth_string_discriminator("<function_name> blockaddress")
311+
```
312+
313+
290314
## AArch64 Support
291315

292316
AArch64 is currently the only architecture with full support of the pointer

llvm/include/llvm/CodeGen/AsmPrinter.h

+3
Original file line numberDiff line numberDiff line change
@@ -577,6 +577,9 @@ class AsmPrinter : public MachineFunctionPass {
577577
report_fatal_error("ptrauth constant lowering not implemented");
578578
}
579579

580+
/// Lower the specified BlockAddress to an MCExpr.
581+
virtual const MCExpr *lowerBlockAddressConstant(const BlockAddress &BA);
582+
580583
/// Return true if the basic block has exactly one predecessor and the control
581584
/// transfer mechanism between the predecessor and this block is a
582585
/// fall-through.

llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp

+5-1
Original file line numberDiff line numberDiff line change
@@ -3144,7 +3144,7 @@ const MCExpr *AsmPrinter::lowerConstant(const Constant *CV) {
31443144
return MCSymbolRefExpr::create(getSymbol(GV), Ctx);
31453145

31463146
if (const BlockAddress *BA = dyn_cast<BlockAddress>(CV))
3147-
return MCSymbolRefExpr::create(GetBlockAddressSymbol(BA), Ctx);
3147+
return lowerBlockAddressConstant(*BA);
31483148

31493149
if (const auto *Equiv = dyn_cast<DSOLocalEquivalent>(CV))
31503150
return getObjFileLowering().lowerDSOLocalEquivalent(Equiv, TM);
@@ -3826,6 +3826,10 @@ MCSymbol *AsmPrinter::GetBlockAddressSymbol(const BasicBlock *BB) const {
38263826
return const_cast<AsmPrinter *>(this)->getAddrLabelSymbol(BB);
38273827
}
38283828

3829+
const MCExpr *AsmPrinter::lowerBlockAddressConstant(const BlockAddress &BA) {
3830+
return MCSymbolRefExpr::create(GetBlockAddressSymbol(&BA), OutContext);
3831+
}
3832+
38293833
/// GetCPISymbol - Return the symbol for the specified constant pool entry.
38303834
MCSymbol *AsmPrinter::GetCPISymbol(unsigned CPID) const {
38313835
if (getSubtargetInfo().getTargetTriple().isWindowsMSVCEnvironment()) {

llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp

+29-5
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ class AArch64AsmPrinter : public AsmPrinter {
9393

9494
const MCExpr *lowerConstantPtrAuth(const ConstantPtrAuth &CPA) override;
9595

96+
const MCExpr *lowerBlockAddressConstant(const BlockAddress &BA) override;
97+
9698
void emitStartOfAsmFile(Module &M) override;
9799
void emitJumpTableInfo() override;
98100
std::tuple<const MCSymbol *, uint64_t, const MCSymbol *,
@@ -130,7 +132,7 @@ class AArch64AsmPrinter : public AsmPrinter {
130132

131133
void emitSled(const MachineInstr &MI, SledKind Kind);
132134

133-
// Emit the sequence for BLRA (authenticate + branch).
135+
// Emit the sequence for BRA/BLRA (authenticate + branch/call).
134136
void emitPtrauthBranch(const MachineInstr *MI);
135137
// Emit the sequence to compute a discriminator into x17, or reuse AddrDisc.
136138
unsigned emitPtrauthDiscriminator(uint16_t Disc, unsigned AddrDisc,
@@ -1760,6 +1762,7 @@ unsigned AArch64AsmPrinter::emitPtrauthDiscriminator(uint16_t Disc,
17601762

17611763
void AArch64AsmPrinter::emitPtrauthBranch(const MachineInstr *MI) {
17621764
unsigned InstsEmitted = 0;
1765+
bool IsCall = MI->getOpcode() == AArch64::BLRA;
17631766
unsigned BrTarget = MI->getOperand(0).getReg();
17641767

17651768
auto Key = (AArch64PACKey::ID)MI->getOperand(1).getImm();
@@ -1776,10 +1779,17 @@ void AArch64AsmPrinter::emitPtrauthBranch(const MachineInstr *MI) {
17761779
bool IsZeroDisc = DiscReg == AArch64::XZR;
17771780

17781781
unsigned Opc;
1779-
if (Key == AArch64PACKey::IA)
1780-
Opc = IsZeroDisc ? AArch64::BLRAAZ : AArch64::BLRAA;
1781-
else
1782-
Opc = IsZeroDisc ? AArch64::BLRABZ : AArch64::BLRAB;
1782+
if (IsCall) {
1783+
if (Key == AArch64PACKey::IA)
1784+
Opc = IsZeroDisc ? AArch64::BLRAAZ : AArch64::BLRAA;
1785+
else
1786+
Opc = IsZeroDisc ? AArch64::BLRABZ : AArch64::BLRAB;
1787+
} else {
1788+
if (Key == AArch64PACKey::IA)
1789+
Opc = IsZeroDisc ? AArch64::BRAAZ : AArch64::BRAA;
1790+
else
1791+
Opc = IsZeroDisc ? AArch64::BRABZ : AArch64::BRAB;
1792+
}
17831793

17841794
MCInst BRInst;
17851795
BRInst.setOpcode(Opc);
@@ -2056,6 +2066,19 @@ void AArch64AsmPrinter::LowerMOVaddrPAC(const MachineInstr &MI) {
20562066
assert(STI->getInstrInfo()->getInstSizeInBytes(MI) >= InstsEmitted * 4);
20572067
}
20582068

2069+
const MCExpr *
2070+
AArch64AsmPrinter::lowerBlockAddressConstant(const BlockAddress &BA) {
2071+
const MCExpr *BAE = AsmPrinter::lowerBlockAddressConstant(BA);
2072+
const Function &Fn = *BA.getFunction();
2073+
2074+
if (std::optional<uint16_t> BADisc =
2075+
STI->getPtrAuthBlockAddressDiscriminatorIfEnabled(Fn))
2076+
return AArch64AuthMCExpr::create(BAE, *BADisc, AArch64PACKey::IA,
2077+
/*HasAddressDiversity=*/false, OutContext);
2078+
2079+
return BAE;
2080+
}
2081+
20592082
// Simple pseudo-instructions have their lowering (with expansion to real
20602083
// instructions) auto-generated.
20612084
#include "AArch64GenMCPseudoLowering.inc"
@@ -2200,6 +2223,7 @@ void AArch64AsmPrinter::emitInstruction(const MachineInstr *MI) {
22002223
LowerMOVaddrPAC(*MI);
22012224
return;
22022225

2226+
case AArch64::BRA:
22032227
case AArch64::BLRA:
22042228
emitPtrauthBranch(MI);
22052229
return;

llvm/lib/Target/AArch64/AArch64FastISel.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -2516,6 +2516,10 @@ bool AArch64FastISel::selectIndirectBr(const Instruction *I) {
25162516
if (AddrReg == 0)
25172517
return false;
25182518

2519+
// Authenticated indirectbr is not implemented yet.
2520+
if (FuncInfo.MF->getFunction().hasFnAttribute("ptrauth-indirect-gotos"))
2521+
return false;
2522+
25192523
// Emit the indirect branch.
25202524
const MCInstrDesc &II = TII.get(AArch64::BR);
25212525
AddrReg = constrainOperandRegClass(II, AddrReg, II.getNumDefs());

0 commit comments

Comments
 (0)