Skip to content

Commit cbb6fad

Browse files
ahmedbougacharjmccall
authored andcommitted
[clang] Implement function pointer signing and authenticated function calls (llvm#93906)
The functions are currently always signed/authenticated with zero discriminator. Co-Authored-By: John McCall <[email protected]>
1 parent a428ac0 commit cbb6fad

22 files changed

+596
-15
lines changed

clang/include/clang/Basic/CodeGenOptions.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#ifndef LLVM_CLANG_BASIC_CODEGENOPTIONS_H
1414
#define LLVM_CLANG_BASIC_CODEGENOPTIONS_H
1515

16+
#include "clang/Basic/PointerAuthOptions.h"
1617
#include "clang/Basic/Sanitizers.h"
1718
#include "clang/Basic/XRayInstr.h"
1819
#include "llvm/ADT/FloatingPointMode.h"
@@ -391,6 +392,9 @@ class CodeGenOptions : public CodeGenOptionsBase {
391392

392393
std::vector<std::string> Reciprocals;
393394

395+
/// Configuration for pointer-signing.
396+
PointerAuthOptions PointerAuth;
397+
394398
/// The preferred width for auto-vectorization transforms. This is intended to
395399
/// override default transforms based on the width of the architected vector
396400
/// registers.

clang/include/clang/Basic/PointerAuthOptions.h

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,144 @@
1414
#ifndef LLVM_CLANG_BASIC_POINTERAUTHOPTIONS_H
1515
#define LLVM_CLANG_BASIC_POINTERAUTHOPTIONS_H
1616

17+
#include "clang/Basic/LLVM.h"
18+
#include "clang/Basic/LangOptions.h"
19+
#include "llvm/ADT/STLForwardCompat.h"
20+
#include "llvm/Support/ErrorHandling.h"
21+
#include "llvm/Target/TargetOptions.h"
22+
#include <optional>
23+
1724
namespace clang {
1825

1926
constexpr unsigned PointerAuthKeyNone = -1;
2027

28+
class PointerAuthSchema {
29+
public:
30+
enum class Kind : unsigned {
31+
None,
32+
ARM8_3,
33+
};
34+
35+
/// Hardware pointer-signing keys in ARM8.3.
36+
///
37+
/// These values are the same used in ptrauth.h.
38+
enum class ARM8_3Key : unsigned {
39+
ASIA = 0,
40+
ASIB = 1,
41+
ASDA = 2,
42+
ASDB = 3
43+
};
44+
45+
/// Forms of extra discrimination.
46+
enum class Discrimination : unsigned {
47+
/// No additional discrimination.
48+
None,
49+
50+
/// Discriminate using a constant value.
51+
Constant,
52+
};
53+
54+
private:
55+
Kind TheKind : 2;
56+
unsigned IsAddressDiscriminated : 1;
57+
unsigned IsIsaPointer : 1;
58+
unsigned AuthenticatesNullValues : 1;
59+
PointerAuthenticationMode SelectedAuthenticationMode : 2;
60+
Discrimination DiscriminationKind : 2;
61+
unsigned Key : 2;
62+
unsigned ConstantDiscriminator : 16;
63+
64+
public:
65+
PointerAuthSchema() : TheKind(Kind::None) {}
66+
67+
PointerAuthSchema(
68+
ARM8_3Key Key, bool IsAddressDiscriminated,
69+
PointerAuthenticationMode AuthenticationMode,
70+
Discrimination OtherDiscrimination,
71+
std::optional<uint16_t> ConstantDiscriminatorOrNone = std::nullopt,
72+
bool IsIsaPointer = false, bool AuthenticatesNullValues = false)
73+
: TheKind(Kind::ARM8_3), IsAddressDiscriminated(IsAddressDiscriminated),
74+
IsIsaPointer(IsIsaPointer),
75+
AuthenticatesNullValues(AuthenticatesNullValues),
76+
SelectedAuthenticationMode(AuthenticationMode),
77+
DiscriminationKind(OtherDiscrimination), Key(llvm::to_underlying(Key)) {
78+
assert((getOtherDiscrimination() != Discrimination::Constant ||
79+
ConstantDiscriminatorOrNone) &&
80+
"constant discrimination requires a constant!");
81+
if (ConstantDiscriminatorOrNone)
82+
ConstantDiscriminator = *ConstantDiscriminatorOrNone;
83+
}
84+
85+
PointerAuthSchema(
86+
ARM8_3Key Key, bool IsAddressDiscriminated,
87+
Discrimination OtherDiscrimination,
88+
std::optional<uint16_t> ConstantDiscriminatorOrNone = std::nullopt,
89+
bool IsIsaPointer = false, bool AuthenticatesNullValues = false)
90+
: PointerAuthSchema(Key, IsAddressDiscriminated,
91+
PointerAuthenticationMode::SignAndAuth,
92+
OtherDiscrimination, ConstantDiscriminatorOrNone,
93+
IsIsaPointer, AuthenticatesNullValues) {}
94+
95+
Kind getKind() const { return TheKind; }
96+
97+
explicit operator bool() const { return isEnabled(); }
98+
99+
bool isEnabled() const { return getKind() != Kind::None; }
100+
101+
bool isAddressDiscriminated() const {
102+
assert(getKind() != Kind::None);
103+
return IsAddressDiscriminated;
104+
}
105+
106+
bool isIsaPointer() const {
107+
assert(getKind() != Kind::None);
108+
return IsIsaPointer;
109+
}
110+
111+
bool authenticatesNullValues() const {
112+
assert(getKind() != Kind::None);
113+
return AuthenticatesNullValues;
114+
}
115+
116+
bool hasOtherDiscrimination() const {
117+
return getOtherDiscrimination() != Discrimination::None;
118+
}
119+
120+
Discrimination getOtherDiscrimination() const {
121+
assert(getKind() != Kind::None);
122+
return DiscriminationKind;
123+
}
124+
125+
uint16_t getConstantDiscrimination() const {
126+
assert(getOtherDiscrimination() == Discrimination::Constant);
127+
return ConstantDiscriminator;
128+
}
129+
130+
unsigned getKey() const {
131+
switch (getKind()) {
132+
case Kind::None:
133+
llvm_unreachable("calling getKey() on disabled schema");
134+
case Kind::ARM8_3:
135+
return llvm::to_underlying(getARM8_3Key());
136+
}
137+
llvm_unreachable("bad key kind");
138+
}
139+
140+
PointerAuthenticationMode getAuthenticationMode() const {
141+
return SelectedAuthenticationMode;
142+
}
143+
144+
ARM8_3Key getARM8_3Key() const {
145+
assert(getKind() == Kind::ARM8_3);
146+
return ARM8_3Key(Key);
147+
}
148+
};
149+
150+
struct PointerAuthOptions {
151+
/// The ABI for C function pointers.
152+
PointerAuthSchema FunctionPointers;
153+
};
154+
21155
} // end namespace clang
22156

23157
#endif

clang/include/clang/Frontend/CompilerInvocation.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,15 @@ class CompilerInvocation : public CompilerInvocationBase {
305305
/// executable), for finding the builtin compiler path.
306306
static std::string GetResourcesPath(const char *Argv0, void *MainAddr);
307307

308+
/// Populate \p Opts with the default set of pointer authentication-related
309+
/// options given \p LangOpts and \p Triple.
310+
///
311+
/// Note: This is intended to be used by tools which must be aware of
312+
/// pointer authentication-related code generation, e.g. lldb.
313+
static void setDefaultPointerAuthOptions(PointerAuthOptions &Opts,
314+
const LangOptions &LangOpts,
315+
const llvm::Triple &Triple);
316+
308317
/// Retrieve a module hash string that is suitable for uniquely
309318
/// identifying the conditions under which the module was built.
310319
std::string getModuleHash() const;

clang/lib/CodeGen/CGBuiltin.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6015,8 +6015,7 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
60156015
// If this is a predefined lib function (e.g. malloc), emit the call
60166016
// using exactly the normal call path.
60176017
if (getContext().BuiltinInfo.isPredefinedLibFunction(BuiltinID))
6018-
return emitLibraryCall(
6019-
*this, FD, E, cast<llvm::Constant>(EmitScalarExpr(E->getCallee())));
6018+
return emitLibraryCall(*this, FD, E, CGM.getRawFunctionPointer(FD));
60206019

60216020
// Check that a call to a target specific builtin has the correct target
60226021
// features.

clang/lib/CodeGen/CGCall.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5678,6 +5678,9 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
56785678
!isa_and_nonnull<FunctionDecl>(TargetDecl))
56795679
EmitKCFIOperandBundle(ConcreteCallee, BundleList);
56805680

5681+
// Add the pointer-authentication bundle.
5682+
EmitPointerAuthOperandBundle(ConcreteCallee.getPointerAuthInfo(), BundleList);
5683+
56815684
if (const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(CurFuncDecl))
56825685
if (FD->hasAttr<StrictFPAttr>())
56835686
// All calls within a strictfp function are marked strictfp

clang/lib/CodeGen/CGCall.h

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#ifndef LLVM_CLANG_LIB_CODEGEN_CGCALL_H
1515
#define LLVM_CLANG_LIB_CODEGEN_CGCALL_H
1616

17+
#include "CGPointerAuthInfo.h"
1718
#include "CGValue.h"
1819
#include "EHScopeStack.h"
1920
#include "clang/AST/ASTFwd.h"
@@ -69,6 +70,10 @@ class CGCallee {
6970
Last = Virtual
7071
};
7172

73+
struct OrdinaryInfoStorage {
74+
CGCalleeInfo AbstractInfo;
75+
CGPointerAuthInfo PointerAuthInfo;
76+
};
7277
struct BuiltinInfoStorage {
7378
const FunctionDecl *Decl;
7479
unsigned ID;
@@ -85,7 +90,7 @@ class CGCallee {
8590

8691
SpecialKind KindOrFunctionPointer;
8792
union {
88-
CGCalleeInfo AbstractInfo;
93+
OrdinaryInfoStorage OrdinaryInfo;
8994
BuiltinInfoStorage BuiltinInfo;
9095
PseudoDestructorInfoStorage PseudoDestructorInfo;
9196
VirtualInfoStorage VirtualInfo;
@@ -104,10 +109,13 @@ class CGCallee {
104109

105110
/// Construct a callee. Call this constructor directly when this
106111
/// isn't a direct call.
107-
CGCallee(const CGCalleeInfo &abstractInfo, llvm::Value *functionPtr)
112+
CGCallee(const CGCalleeInfo &abstractInfo, llvm::Value *functionPtr,
113+
/* FIXME: make parameter pointerAuthInfo mandatory */
114+
const CGPointerAuthInfo &pointerAuthInfo = CGPointerAuthInfo())
108115
: KindOrFunctionPointer(
109116
SpecialKind(reinterpret_cast<uintptr_t>(functionPtr))) {
110-
AbstractInfo = abstractInfo;
117+
OrdinaryInfo.AbstractInfo = abstractInfo;
118+
OrdinaryInfo.PointerAuthInfo = pointerAuthInfo;
111119
assert(functionPtr && "configuring callee without function pointer");
112120
assert(functionPtr->getType()->isPointerTy());
113121
}
@@ -173,7 +181,11 @@ class CGCallee {
173181
if (isVirtual())
174182
return VirtualInfo.MD;
175183
assert(isOrdinary());
176-
return AbstractInfo;
184+
return OrdinaryInfo.AbstractInfo;
185+
}
186+
const CGPointerAuthInfo &getPointerAuthInfo() const {
187+
assert(isOrdinary());
188+
return OrdinaryInfo.PointerAuthInfo;
177189
}
178190
llvm::Value *getFunctionPointer() const {
179191
assert(isOrdinary());
@@ -184,6 +196,10 @@ class CGCallee {
184196
KindOrFunctionPointer =
185197
SpecialKind(reinterpret_cast<uintptr_t>(functionPtr));
186198
}
199+
void setPointerAuthInfo(CGPointerAuthInfo PointerAuth) {
200+
assert(isOrdinary());
201+
OrdinaryInfo.PointerAuthInfo = PointerAuth;
202+
}
187203

188204
bool isVirtual() const {
189205
return KindOrFunctionPointer == SpecialKind::Virtual;

clang/lib/CodeGen/CGExpr.cpp

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2856,22 +2856,22 @@ static LValue EmitGlobalVarDeclLValue(CodeGenFunction &CGF,
28562856
return LV;
28572857
}
28582858

2859-
static llvm::Constant *EmitFunctionDeclPointer(CodeGenModule &CGM,
2860-
GlobalDecl GD) {
2859+
llvm::Constant *CodeGenModule::getRawFunctionPointer(GlobalDecl GD,
2860+
llvm::Type *Ty) {
28612861
const FunctionDecl *FD = cast<FunctionDecl>(GD.getDecl());
28622862
if (FD->hasAttr<WeakRefAttr>()) {
2863-
ConstantAddress aliasee = CGM.GetWeakRefReference(FD);
2863+
ConstantAddress aliasee = GetWeakRefReference(FD);
28642864
return aliasee.getPointer();
28652865
}
28662866

2867-
llvm::Constant *V = CGM.GetAddrOfFunction(GD);
2867+
llvm::Constant *V = GetAddrOfFunction(GD, Ty);
28682868
return V;
28692869
}
28702870

28712871
static LValue EmitFunctionDeclLValue(CodeGenFunction &CGF, const Expr *E,
28722872
GlobalDecl GD) {
28732873
const FunctionDecl *FD = cast<FunctionDecl>(GD.getDecl());
2874-
llvm::Value *V = EmitFunctionDeclPointer(CGF.CGM, GD);
2874+
llvm::Constant *V = CGF.CGM.getFunctionPointer(GD);
28752875
CharUnits Alignment = CGF.getContext().getDeclAlign(FD);
28762876
return CGF.MakeAddrLValue(V, E->getType(), Alignment,
28772877
AlignmentSource::Decl);
@@ -5506,7 +5506,7 @@ static CGCallee EmitDirectCallee(CodeGenFunction &CGF, GlobalDecl GD) {
55065506
// name to make it clear it's not the actual builtin.
55075507
if (CGF.CurFn->getName() != FDInlineName &&
55085508
OnlyHasInlineBuiltinDeclaration(FD)) {
5509-
llvm::Constant *CalleePtr = EmitFunctionDeclPointer(CGF.CGM, GD);
5509+
llvm::Constant *CalleePtr = CGF.CGM.getRawFunctionPointer(GD);
55105510
llvm::Function *Fn = llvm::cast<llvm::Function>(CalleePtr);
55115511
llvm::Module *M = Fn->getParent();
55125512
llvm::Function *Clone = M->getFunction(FDInlineName);
@@ -5529,7 +5529,7 @@ static CGCallee EmitDirectCallee(CodeGenFunction &CGF, GlobalDecl GD) {
55295529
return CGCallee::forBuiltin(builtinID, FD);
55305530
}
55315531

5532-
llvm::Constant *CalleePtr = EmitFunctionDeclPointer(CGF.CGM, GD);
5532+
llvm::Constant *CalleePtr = CGF.CGM.getRawFunctionPointer(GD);
55335533
if (CGF.CGM.getLangOpts().CUDA && !CGF.CGM.getLangOpts().CUDAIsDevice &&
55345534
FD->hasAttr<CUDAGlobalAttr>())
55355535
CalleePtr = CGF.CGM.getCUDARuntime().getKernelStub(
@@ -5586,7 +5586,8 @@ CGCallee CodeGenFunction::EmitCallee(const Expr *E) {
55865586
GD = GlobalDecl(VD);
55875587

55885588
CGCalleeInfo calleeInfo(functionType->getAs<FunctionProtoType>(), GD);
5589-
CGCallee callee(calleeInfo, calleePtr);
5589+
CGPointerAuthInfo pointerAuth = CGM.getFunctionPointerAuthInfo(functionType);
5590+
CGCallee callee(calleeInfo, calleePtr, pointerAuth);
55905591
return callee;
55915592
}
55925593

clang/lib/CodeGen/CGExprConstant.cpp

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2024,8 +2024,25 @@ ConstantLValueEmitter::tryEmitBase(const APValue::LValueBase &base) {
20242024
if (D->hasAttr<WeakRefAttr>())
20252025
return CGM.GetWeakRefReference(D).getPointer();
20262026

2027+
auto PtrAuthSign = [&](llvm::Constant *C) {
2028+
CGPointerAuthInfo AuthInfo = CGM.getFunctionPointerAuthInfo(DestType);
2029+
2030+
if (AuthInfo) {
2031+
if (hasNonZeroOffset())
2032+
return ConstantLValue(nullptr);
2033+
2034+
C = applyOffset(C);
2035+
C = CGM.getConstantSignedPointer(
2036+
C, AuthInfo.getKey(), nullptr,
2037+
cast_or_null<llvm::ConstantInt>(AuthInfo.getDiscriminator()));
2038+
return ConstantLValue(C, /*applied offset*/ true);
2039+
}
2040+
2041+
return ConstantLValue(C);
2042+
};
2043+
20272044
if (auto FD = dyn_cast<FunctionDecl>(D))
2028-
return CGM.GetAddrOfFunction(FD);
2045+
return PtrAuthSign(CGM.getRawFunctionPointer(FD));
20292046

20302047
if (auto VD = dyn_cast<VarDecl>(D)) {
20312048
// We can never refer to a variable with local storage.

clang/lib/CodeGen/CGPointerAuth.cpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,24 @@
1717
using namespace clang;
1818
using namespace CodeGen;
1919

20+
/// Return the abstract pointer authentication schema for a pointer to the given
21+
/// function type.
22+
CGPointerAuthInfo CodeGenModule::getFunctionPointerAuthInfo(QualType T) {
23+
const auto &Schema = getCodeGenOpts().PointerAuth.FunctionPointers;
24+
if (!Schema)
25+
return CGPointerAuthInfo();
26+
27+
assert(!Schema.isAddressDiscriminated() &&
28+
"function pointers cannot use address-specific discrimination");
29+
30+
assert(!Schema.hasOtherDiscrimination() &&
31+
"function pointers don't support any discrimination yet");
32+
33+
return CGPointerAuthInfo(Schema.getKey(), Schema.getAuthenticationMode(),
34+
/*IsaPointer=*/false, /*AuthenticatesNull=*/false,
35+
/*Discriminator=*/nullptr);
36+
}
37+
2038
llvm::Constant *
2139
CodeGenModule::getConstantSignedPointer(llvm::Constant *Pointer, unsigned Key,
2240
llvm::Constant *StorageAddress,
@@ -41,3 +59,24 @@ CodeGenModule::getConstantSignedPointer(llvm::Constant *Pointer, unsigned Key,
4159
llvm::ConstantInt::get(Int32Ty, Key),
4260
IntegerDiscriminator, AddressDiscriminator);
4361
}
62+
63+
llvm::Constant *CodeGenModule::getFunctionPointer(llvm::Constant *Pointer,
64+
QualType FunctionType) {
65+
assert(FunctionType->isFunctionType() ||
66+
FunctionType->isFunctionReferenceType() ||
67+
FunctionType->isFunctionPointerType());
68+
69+
if (auto PointerAuth = getFunctionPointerAuthInfo(FunctionType))
70+
return getConstantSignedPointer(
71+
Pointer, PointerAuth.getKey(), /*StorageAddress=*/nullptr,
72+
cast_or_null<llvm::ConstantInt>(PointerAuth.getDiscriminator()));
73+
74+
return Pointer;
75+
}
76+
77+
llvm::Constant *CodeGenModule::getFunctionPointer(GlobalDecl GD,
78+
llvm::Type *Ty) {
79+
const auto *FD = cast<FunctionDecl>(GD.getDecl());
80+
QualType FuncType = FD->getType();
81+
return getFunctionPointer(getRawFunctionPointer(GD, Ty), FuncType);
82+
}

0 commit comments

Comments
 (0)