Skip to content

[AArch64][PAC] Sign block addresses used in indirectbr. #97647

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
3 changes: 3 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -949,6 +949,9 @@ def note_ptrauth_virtual_function_pointer_incomplete_arg_ret :
def note_ptrauth_virtual_function_incomplete_arg_ret_type :
Note<"%0 is incomplete">;

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

/// main()
// static main() is not an error in C, just in C++.
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Basic/Features.def
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ FEATURE(ptrauth_type_info_vtable_pointer_discrimination, LangOpts.PointerAuthTyp
FEATURE(ptrauth_member_function_pointer_type_discrimination, LangOpts.PointerAuthCalls)
FEATURE(ptrauth_init_fini, LangOpts.PointerAuthInitFini)
FEATURE(ptrauth_function_pointer_type_discrimination, LangOpts.PointerAuthFunctionTypeDiscrimination)
FEATURE(ptrauth_indirect_gotos, LangOpts.PointerAuthIndirectGotos)
EXTENSION(swiftcc,
PP.getTargetInfo().checkCallingConvention(CC_Swift) ==
clang::TargetInfo::CCCR_OK)
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Basic/LangOptions.def
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ LANGOPT(ExperimentalLibrary, 1, 0, "enable unstable and experimental library fea
LANGOPT(PointerAuthIntrinsics, 1, 0, "pointer authentication intrinsics")
LANGOPT(PointerAuthCalls , 1, 0, "function pointer authentication")
LANGOPT(PointerAuthReturns, 1, 0, "return pointer authentication")
LANGOPT(PointerAuthIndirectGotos, 1, 0, "indirect gotos pointer authentication")
LANGOPT(PointerAuthAuthTraps, 1, 0, "pointer authentication failure traps")
LANGOPT(PointerAuthVTPtrAddressDiscrimination, 1, 0, "incorporate address discrimination in authenticated vtable pointers")
LANGOPT(PointerAuthVTPtrTypeDiscrimination, 1, 0, "incorporate type discrimination in authenticated vtable pointers")
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Basic/PointerAuthOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,9 @@ class PointerAuthSchema {
};

struct PointerAuthOptions {
/// Do indirect goto label addresses need to be authenticated?
bool IndirectGotos = false;

/// The ABI for C function pointers.
PointerAuthSchema FunctionPointers;

Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Driver/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -4254,6 +4254,8 @@ defm ptrauth_type_info_vtable_pointer_discrimination :
defm ptrauth_init_fini : OptInCC1FFlag<"ptrauth-init-fini", "Enable signing of function pointers in init/fini arrays">;
defm ptrauth_function_pointer_type_discrimination : OptInCC1FFlag<"ptrauth-function-pointer-type-discrimination",
"Enable type discrimination on C function pointers">;
defm ptrauth_indirect_gotos : OptInCC1FFlag<"ptrauth-indirect-gotos",
"Enable signing and authentication of indirect goto targets">;
}

def fenable_matrix : Flag<["-"], "fenable-matrix">, Group<f_Group>,
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/CodeGen/CodeGenFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -882,6 +882,8 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy,
const CodeGenOptions &CodeGenOpts = CGM.getCodeGenOpts();
if (CodeGenOpts.PointerAuth.FunctionPointers)
Fn->addFnAttr("ptrauth-calls");
if (CodeGenOpts.PointerAuth.IndirectGotos)
Fn->addFnAttr("ptrauth-indirect-gotos");

// Apply xray attributes to the function (as a string, for now)
bool AlwaysXRayAttr = false;
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/Driver/ToolChains/Clang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1848,6 +1848,9 @@ void Clang::AddAArch64TargetArgs(const ArgList &Args,
Args.addOptInFlag(
CmdArgs, options::OPT_fptrauth_function_pointer_type_discrimination,
options::OPT_fno_ptrauth_function_pointer_type_discrimination);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: there is no empty line between previous Args.addOptInFlag(...) invocations, so probably this new line should also be deleted.

Feel free to ignore

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The whitespaces help differentiate the different classes of fptrauth features (here frontendy vs backendy); TBH we should probably have more, not less

Args.addOptInFlag(CmdArgs, options::OPT_fptrauth_indirect_gotos,
options::OPT_fno_ptrauth_indirect_gotos);
}

void Clang::AddLoongArchTargetArgs(const ArgList &Args,
Expand Down
6 changes: 5 additions & 1 deletion clang/lib/Frontend/CompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1504,13 +1504,14 @@ void CompilerInvocation::setDefaultPointerAuthOptions(
Opts.CXXMemberFunctionPointers =
PointerAuthSchema(Key::ASIA, false, Discrimination::Type);
}
Opts.IndirectGotos = LangOpts.PointerAuthIndirectGotos;
}

static void parsePointerAuthOptions(PointerAuthOptions &Opts,
const LangOptions &LangOpts,
const llvm::Triple &Triple,
DiagnosticsEngine &Diags) {
if (!LangOpts.PointerAuthCalls)
if (!LangOpts.PointerAuthCalls && !LangOpts.PointerAuthIndirectGotos)
return;

CompilerInvocation::setDefaultPointerAuthOptions(Opts, LangOpts, Triple);
Expand Down Expand Up @@ -3414,6 +3415,8 @@ static void GeneratePointerAuthArgs(const LangOptions &Opts,
GenerateArg(Consumer, OPT_fptrauth_calls);
if (Opts.PointerAuthReturns)
GenerateArg(Consumer, OPT_fptrauth_returns);
if (Opts.PointerAuthIndirectGotos)
GenerateArg(Consumer, OPT_fptrauth_indirect_gotos);
if (Opts.PointerAuthAuthTraps)
GenerateArg(Consumer, OPT_fptrauth_auth_traps);
if (Opts.PointerAuthVTPtrAddressDiscrimination)
Expand All @@ -3434,6 +3437,7 @@ static void ParsePointerAuthArgs(LangOptions &Opts, ArgList &Args,
Opts.PointerAuthIntrinsics = Args.hasArg(OPT_fptrauth_intrinsics);
Opts.PointerAuthCalls = Args.hasArg(OPT_fptrauth_calls);
Opts.PointerAuthReturns = Args.hasArg(OPT_fptrauth_returns);
Opts.PointerAuthIndirectGotos = Args.hasArg(OPT_fptrauth_indirect_gotos);
Opts.PointerAuthAuthTraps = Args.hasArg(OPT_fptrauth_auth_traps);
Opts.PointerAuthVTPtrAddressDiscrimination =
Args.hasArg(OPT_fptrauth_vtable_pointer_address_discrimination);
Expand Down
17 changes: 17 additions & 0 deletions clang/lib/Sema/SemaExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10909,6 +10909,14 @@ QualType Sema::CheckAdditionOperands(ExprResult &LHS, ExprResult &RHS,
if (isObjCPointer && checkArithmeticOnObjCPointer(*this, Loc, PExp))
return QualType();

// Arithmetic on label addresses is normally allowed, except when we add
// a ptrauth signature to the addresses.
if (isa<AddrLabelExpr>(PExp) && getLangOpts().PointerAuthIndirectGotos) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this catches all the relevant cases: there are various ways you could "hide" an address-of-label from this check. Parentheses, a conditional operator, a constexpr variable, etc.

Maybe the check should be in IntExprEvaluator::VisitBinaryOperator.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this only catches the obvious cases to start. I did try having this in IntExprEvaluator, but only got it to behave as I expected (without dupes) with what I'm pretty sure is a gross misuse of noteFailure ;) Let me look into it some more separately

Diag(Loc, diag::err_ptrauth_indirect_goto_addrlabel_arithmetic)
<< /*addition*/ 1;
return QualType();
}

// Check array bounds for pointer arithemtic
CheckArrayAccess(PExp, IExp);

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

// Arithmetic on label addresses is normally allowed, except when we add
// a ptrauth signature to the addresses.
if (isa<AddrLabelExpr>(LHS.get()) &&
getLangOpts().PointerAuthIndirectGotos) {
Diag(Loc, diag::err_ptrauth_indirect_goto_addrlabel_arithmetic)
<< /*subtraction*/ 0;
return QualType();
}

// The result type of a pointer-int computation is the pointer type.
if (RHS.get()->getType()->isIntegerType()) {
// Subtracting from a null pointer should produce a warning.
Expand Down
5 changes: 5 additions & 0 deletions clang/test/CodeGen/ptrauth-function-attributes.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,15 @@
// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -emit-llvm %s -o - | FileCheck %s --check-prefixes=ALL,CALLS
// RUN: %clang_cc1 -triple aarch64-linux-gnu -fptrauth-calls -emit-llvm %s -o - | FileCheck %s --check-prefixes=ALL,CALLS

// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-indirect-gotos -emit-llvm %s -o - | FileCheck %s --check-prefixes=ALL,GOTOS
// RUN: %clang_cc1 -triple arm64e-apple-ios -fptrauth-indirect-gotos -emit-llvm %s -o - | FileCheck %s --check-prefixes=ALL,GOTOS

// ALL: define {{(dso_local )?}}void @test() #0
void test() {
}

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

// GOTOS: attributes #0 = {{{.*}} "ptrauth-indirect-gotos" {{.*}}}

// OFF-NOT: attributes {{.*}} "ptrauth-
18 changes: 18 additions & 0 deletions clang/test/Sema/ptrauth-indirect-goto.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// RUN: %clang_cc1 -triple arm64e-apple-darwin -fsyntax-only -verify %s -fptrauth-indirect-gotos

int f() {
static void *addrs[] = { &&l1, &&l2 };

static int diffs[] = {
&&l1 - &&l1, // expected-error{{subtraction of address-of-label expressions is not supported with ptrauth indirect gotos}}
&&l1 - &&l2 // expected-error{{subtraction of address-of-label expressions is not supported with ptrauth indirect gotos}}
};

int diff_32 = &&l1 - &&l2; // expected-error{{subtraction of address-of-label expressions is not supported with ptrauth indirect gotos}}
goto *(&&l1 + diff_32); // expected-error{{addition of address-of-label expressions is not supported with ptrauth indirect gotos}}

l1:
return 0;
l2:
return 1;
}
24 changes: 24 additions & 0 deletions llvm/docs/PointerAuth.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ At the IR level, it is represented using:
* a [set of intrinsics](#intrinsics) (to sign/authenticate pointers)
* a [signed pointer constant](#constant) (to sign globals)
* a [call operand bundle](#operand-bundle) (to authenticate called pointers)
* a [set of function attributes](#function-attributes) (to describe what
pointers are signed and how, to control implicit codegen in the backend, as
well as preserve invariants in the mid-level optimizer)

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


### Function Attributes

Some function attributes are used to describe other pointer authentication
operations that are not otherwise explicitly expressed in IR.

#### ``ptrauth-indirect-gotos``

``ptrauth-indirect-gotos`` specifies that indirect gotos in this function
should authenticate their target. At the IR level, no other change is needed.
When lowering [``blockaddress`` constants](https://llvm.org/docs/LangRef.html#blockaddress),
and [``indirectbr`` instructions](https://llvm.org/docs/LangRef.html#i-indirectbr),
this tells the backend to respectively sign and authenticate the pointers.

The specific scheme isn't ABI-visible. Currently, the AArch64 backend
signs blockaddresses using the `ASIA` key, with an integer discriminator
derived from the parent function's name, using the SipHash stable discriminator:
```
ptrauth_string_discriminator("<function_name> blockaddress")
```


## AArch64 Support

AArch64 is currently the only architecture with full support of the pointer
Expand Down
3 changes: 3 additions & 0 deletions llvm/include/llvm/CodeGen/AsmPrinter.h
Original file line number Diff line number Diff line change
Expand Up @@ -577,6 +577,9 @@ class AsmPrinter : public MachineFunctionPass {
report_fatal_error("ptrauth constant lowering not implemented");
}

/// Lower the specified BlockAddress to an MCExpr.
virtual const MCExpr *lowerBlockAddressConstant(const BlockAddress &BA);

/// Return true if the basic block has exactly one predecessor and the control
/// transfer mechanism between the predecessor and this block is a
/// fall-through.
Expand Down
6 changes: 5 additions & 1 deletion llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3144,7 +3144,7 @@ const MCExpr *AsmPrinter::lowerConstant(const Constant *CV) {
return MCSymbolRefExpr::create(getSymbol(GV), Ctx);

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

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

const MCExpr *AsmPrinter::lowerBlockAddressConstant(const BlockAddress &BA) {
return MCSymbolRefExpr::create(GetBlockAddressSymbol(&BA), OutContext);
}

/// GetCPISymbol - Return the symbol for the specified constant pool entry.
MCSymbol *AsmPrinter::GetCPISymbol(unsigned CPID) const {
if (getSubtargetInfo().getTargetTriple().isWindowsMSVCEnvironment()) {
Expand Down
34 changes: 29 additions & 5 deletions llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ class AArch64AsmPrinter : public AsmPrinter {

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

const MCExpr *lowerBlockAddressConstant(const BlockAddress &BA) override;

void emitStartOfAsmFile(Module &M) override;
void emitJumpTableInfo() override;
std::tuple<const MCSymbol *, uint64_t, const MCSymbol *,
Expand Down Expand Up @@ -130,7 +132,7 @@ class AArch64AsmPrinter : public AsmPrinter {

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

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

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

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

unsigned Opc;
if (Key == AArch64PACKey::IA)
Opc = IsZeroDisc ? AArch64::BLRAAZ : AArch64::BLRAA;
else
Opc = IsZeroDisc ? AArch64::BLRABZ : AArch64::BLRAB;
if (IsCall) {
if (Key == AArch64PACKey::IA)
Opc = IsZeroDisc ? AArch64::BLRAAZ : AArch64::BLRAA;
else
Opc = IsZeroDisc ? AArch64::BLRABZ : AArch64::BLRAB;
} else {
if (Key == AArch64PACKey::IA)
Opc = IsZeroDisc ? AArch64::BRAAZ : AArch64::BRAA;
else
Opc = IsZeroDisc ? AArch64::BRABZ : AArch64::BRAB;
}

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

const MCExpr *
AArch64AsmPrinter::lowerBlockAddressConstant(const BlockAddress &BA) {
const MCExpr *BAE = AsmPrinter::lowerBlockAddressConstant(BA);
const Function &Fn = *BA.getFunction();

if (std::optional<uint16_t> BADisc =
STI->getPtrAuthBlockAddressDiscriminatorIfEnabled(Fn))
return AArch64AuthMCExpr::create(BAE, *BADisc, AArch64PACKey::IA,
/*HasAddressDiversity=*/false, OutContext);

return BAE;
}

// Simple pseudo-instructions have their lowering (with expansion to real
// instructions) auto-generated.
#include "AArch64GenMCPseudoLowering.inc"
Expand Down Expand Up @@ -2200,6 +2223,7 @@ void AArch64AsmPrinter::emitInstruction(const MachineInstr *MI) {
LowerMOVaddrPAC(*MI);
return;

case AArch64::BRA:
case AArch64::BLRA:
emitPtrauthBranch(MI);
return;
Expand Down
4 changes: 4 additions & 0 deletions llvm/lib/Target/AArch64/AArch64FastISel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2516,6 +2516,10 @@ bool AArch64FastISel::selectIndirectBr(const Instruction *I) {
if (AddrReg == 0)
return false;

// Authenticated indirectbr is not implemented yet.
if (FuncInfo.MF->getFunction().hasFnAttribute("ptrauth-indirect-gotos"))
return false;

// Emit the indirect branch.
const MCInstrDesc &II = TII.get(AArch64::BR);
AddrReg = constrainOperandRegClass(II, AddrReg, II.getNumDefs());
Expand Down
Loading
Loading