Skip to content

Commit 0edc97f

Browse files
[IR][AArch64][PAC] Add "ptrauth(...)" Constant to represent signed pointers. (#85738)
This defines a new kind of IR Constant that represents a ptrauth signed pointer, as used in AArch64 PAuth. It allows representing most kinds of signed pointer constants used thus far in the llvm ptrauth implementations, notably those used in the Darwin and ELF ABIs being implemented for c/c++. These signed pointer constants are then lowered to ELF/MachO relocations. These can be simply thought of as a constant `llvm.ptrauth.sign`, with the interesting addition of discriminator computation: the `ptrauth` constant can also represent a combined blend, when both address and integer discriminator operands are used. Both operands are otherwise optional, with default values 0/null.
1 parent 60bce6e commit 0edc97f

25 files changed

+488
-1
lines changed

llvm/docs/LangRef.rst

+34
Original file line numberDiff line numberDiff line change
@@ -4754,6 +4754,40 @@ reference to the CFI jump table in the ``LowerTypeTests`` pass. These constants
47544754
may be useful in low-level programs, such as operating system kernels, which
47554755
need to refer to the actual function body.
47564756

4757+
.. _ptrauth_constant:
4758+
4759+
Pointer Authentication Constants
4760+
--------------------------------
4761+
4762+
``ptrauth (ptr CST, i32 KEY[, i64 DISC[, ptr ADDRDISC]?]?)``
4763+
4764+
A '``ptrauth``' constant represents a pointer with a cryptographic
4765+
authentication signature embedded into some bits, as described in the
4766+
`Pointer Authentication <PointerAuth.html>`__ document.
4767+
4768+
A '``ptrauth``' constant is simply a constant equivalent to the
4769+
``llvm.ptrauth.sign`` intrinsic, potentially fed by a discriminator
4770+
``llvm.ptrauth.blend`` if needed.
4771+
4772+
Its type is the same as the first argument. An integer constant discriminator
4773+
and an address discriminator may be optionally specified. Otherwise, they have
4774+
values ``i64 0`` and ``ptr null``.
4775+
4776+
If the address discriminator is ``null`` then the expression is equivalent to
4777+
4778+
.. code-block:: llvm
4779+
4780+
%tmp = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr CST to i64), i32 KEY, i64 DISC)
4781+
%val = inttoptr i64 %tmp to ptr
4782+
4783+
Otherwise, the expression is equivalent to:
4784+
4785+
.. code-block:: llvm
4786+
4787+
%tmp1 = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr ADDRDISC to i64), i64 DISC)
4788+
%tmp2 = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr CST to i64), i32 KEY, i64 %tmp1)
4789+
%val = inttoptr i64 %tmp2 to ptr
4790+
47574791
.. _constantexprs:
47584792

47594793
Constant Expressions

llvm/docs/PointerAuth.md

+22
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ For more details, see the clang documentation page for
1616
At the IR level, it is represented using:
1717

1818
* a [set of intrinsics](#intrinsics) (to sign/authenticate pointers)
19+
* a [signed pointer constant](#constant) (to sign globals)
1920
* a [call operand bundle](#operand-bundle) (to authenticate called pointers)
2021

2122
The current implementation leverages the
@@ -225,6 +226,27 @@ with a pointer address discriminator, in a way that is specified by the target
225226
implementation.
226227

227228

229+
### Constant
230+
231+
[Intrinsics](#intrinsics) can be used to produce signed pointers dynamically,
232+
in code, but not for signed pointers referenced by constants, in, e.g., global
233+
initializers.
234+
235+
The latter are represented using a
236+
[``ptrauth`` constant](https://llvm.org/docs/LangRef.html#ptrauth-constant),
237+
which describes an authenticated relocation producing a signed pointer.
238+
239+
```llvm
240+
ptrauth (ptr CST, i32 KEY, i64 DISC, ptr ADDRDISC)
241+
```
242+
243+
is equivalent to:
244+
245+
```llvm
246+
%disc = call i64 @llvm.ptrauth.blend(i64 ptrtoint(ptr ADDRDISC to i64), i64 DISC)
247+
%signedval = call i64 @llvm.ptrauth.sign(ptr CST, i32 KEY, i64 %disc)
248+
```
249+
228250
### Operand Bundle
229251

230252
Function pointers used as indirect call targets can be signed when materialized,

llvm/include/llvm/AsmParser/LLToken.h

+1
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,7 @@ enum Kind {
346346
kw_blockaddress,
347347
kw_dso_local_equivalent,
348348
kw_no_cfi,
349+
kw_ptrauth,
349350

350351
kw_freeze,
351352

llvm/include/llvm/Bitcode/LLVMBitCodes.h

+1
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,7 @@ enum ConstantsCodes {
413413
// asmstr,conststr]
414414
CST_CODE_CE_GEP_WITH_INRANGE = 31, // [opty, flags, range, n x operands]
415415
CST_CODE_CE_GEP = 32, // [opty, flags, n x operands]
416+
CST_CODE_PTRAUTH = 33, // [ptr, key, disc, addrdisc]
416417
};
417418

418419
/// CastOpcodes - These are values used in the bitcode files to encode which

llvm/include/llvm/IR/Constants.h

+66
Original file line numberDiff line numberDiff line change
@@ -1008,6 +1008,72 @@ struct OperandTraits<NoCFIValue> : public FixedNumOperandTraits<NoCFIValue, 1> {
10081008

10091009
DEFINE_TRANSPARENT_OPERAND_ACCESSORS(NoCFIValue, Value)
10101010

1011+
/// A signed pointer, in the ptrauth sense.
1012+
class ConstantPtrAuth final : public Constant {
1013+
friend struct ConstantPtrAuthKeyType;
1014+
friend class Constant;
1015+
1016+
ConstantPtrAuth(Constant *Ptr, ConstantInt *Key, ConstantInt *Disc,
1017+
Constant *AddrDisc);
1018+
1019+
void *operator new(size_t s) { return User::operator new(s, 4); }
1020+
1021+
void destroyConstantImpl();
1022+
Value *handleOperandChangeImpl(Value *From, Value *To);
1023+
1024+
public:
1025+
/// Return a pointer signed with the specified parameters.
1026+
static ConstantPtrAuth *get(Constant *Ptr, ConstantInt *Key,
1027+
ConstantInt *Disc, Constant *AddrDisc);
1028+
1029+
/// Produce a new ptrauth expression signing the given value using
1030+
/// the same schema as is stored in one.
1031+
ConstantPtrAuth *getWithSameSchema(Constant *Pointer) const;
1032+
1033+
/// Transparently provide more efficient getOperand methods.
1034+
DECLARE_TRANSPARENT_OPERAND_ACCESSORS(Constant);
1035+
1036+
/// The pointer that is signed in this ptrauth signed pointer.
1037+
Constant *getPointer() const { return cast<Constant>(Op<0>().get()); }
1038+
1039+
/// The Key ID, an i32 constant.
1040+
ConstantInt *getKey() const { return cast<ConstantInt>(Op<1>().get()); }
1041+
1042+
/// The integer discriminator, an i64 constant, or 0.
1043+
ConstantInt *getDiscriminator() const {
1044+
return cast<ConstantInt>(Op<2>().get());
1045+
}
1046+
1047+
/// The address discriminator if any, or the null constant.
1048+
/// If present, this must be a value equivalent to the storage location of
1049+
/// the only global-initializer user of the ptrauth signed pointer.
1050+
Constant *getAddrDiscriminator() const {
1051+
return cast<Constant>(Op<3>().get());
1052+
}
1053+
1054+
/// Whether there is any non-null address discriminator.
1055+
bool hasAddressDiscriminator() const {
1056+
return !getAddrDiscriminator()->isNullValue();
1057+
}
1058+
1059+
/// Check whether an authentication operation with key \p Key and (possibly
1060+
/// blended) discriminator \p Discriminator is known to be compatible with
1061+
/// this ptrauth signed pointer.
1062+
bool isKnownCompatibleWith(const Value *Key, const Value *Discriminator,
1063+
const DataLayout &DL) const;
1064+
1065+
/// Methods for support type inquiry through isa, cast, and dyn_cast:
1066+
static bool classof(const Value *V) {
1067+
return V->getValueID() == ConstantPtrAuthVal;
1068+
}
1069+
};
1070+
1071+
template <>
1072+
struct OperandTraits<ConstantPtrAuth>
1073+
: public FixedNumOperandTraits<ConstantPtrAuth, 4> {};
1074+
1075+
DEFINE_TRANSPARENT_OPERAND_ACCESSORS(ConstantPtrAuth, Constant)
1076+
10111077
//===----------------------------------------------------------------------===//
10121078
/// A constant value that is initialized with an expression using
10131079
/// other constant values.

llvm/include/llvm/IR/Value.def

+1
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ HANDLE_CONSTANT(BlockAddress)
8181
HANDLE_CONSTANT(ConstantExpr)
8282
HANDLE_CONSTANT_EXCLUDE_LLVM_C_API(DSOLocalEquivalent)
8383
HANDLE_CONSTANT_EXCLUDE_LLVM_C_API(NoCFIValue)
84+
HANDLE_CONSTANT_EXCLUDE_LLVM_C_API(ConstantPtrAuth)
8485

8586
// ConstantAggregate.
8687
HANDLE_CONSTANT(ConstantArray)

llvm/lib/Analysis/ValueTracking.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -3140,6 +3140,10 @@ bool isKnownNonZero(const Value *V, const APInt &DemandedElts,
31403140
return true;
31413141
}
31423142

3143+
// Constant ptrauth can be null, iff the base pointer can be.
3144+
if (auto *CPA = dyn_cast<ConstantPtrAuth>(V))
3145+
return isKnownNonZero(CPA->getPointer(), DemandedElts, Q, Depth);
3146+
31433147
// A global variable in address space 0 is non null unless extern weak
31443148
// or an absolute symbol reference. Other address spaces may have null as a
31453149
// valid address for a global, so we can't assume anything.

llvm/lib/AsmParser/LLLexer.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -710,6 +710,7 @@ lltok::Kind LLLexer::LexIdentifier() {
710710
KEYWORD(blockaddress);
711711
KEYWORD(dso_local_equivalent);
712712
KEYWORD(no_cfi);
713+
KEYWORD(ptrauth);
713714

714715
// Metadata types.
715716
KEYWORD(distinct);

llvm/lib/AsmParser/LLParser.cpp

+54
Original file line numberDiff line numberDiff line change
@@ -4046,6 +4046,60 @@ bool LLParser::parseValID(ValID &ID, PerFunctionState *PFS, Type *ExpectedTy) {
40464046
ID.NoCFI = true;
40474047
return false;
40484048
}
4049+
case lltok::kw_ptrauth: {
4050+
// ValID ::= 'ptrauth' '(' ptr @foo ',' i32 <key>
4051+
// (',' i64 <disc> (',' ptr addrdisc)? )? ')'
4052+
Lex.Lex();
4053+
4054+
Constant *Ptr, *Key;
4055+
Constant *Disc = nullptr, *AddrDisc = nullptr;
4056+
4057+
if (parseToken(lltok::lparen,
4058+
"expected '(' in constant ptrauth expression") ||
4059+
parseGlobalTypeAndValue(Ptr) ||
4060+
parseToken(lltok::comma,
4061+
"expected comma in constant ptrauth expression") ||
4062+
parseGlobalTypeAndValue(Key))
4063+
return true;
4064+
// If present, parse the optional disc/addrdisc.
4065+
if (EatIfPresent(lltok::comma))
4066+
if (parseGlobalTypeAndValue(Disc) ||
4067+
(EatIfPresent(lltok::comma) && parseGlobalTypeAndValue(AddrDisc)))
4068+
return true;
4069+
if (parseToken(lltok::rparen,
4070+
"expected ')' in constant ptrauth expression"))
4071+
return true;
4072+
4073+
if (!Ptr->getType()->isPointerTy())
4074+
return error(ID.Loc, "constant ptrauth base pointer must be a pointer");
4075+
4076+
auto *KeyC = dyn_cast<ConstantInt>(Key);
4077+
if (!KeyC || KeyC->getBitWidth() != 32)
4078+
return error(ID.Loc, "constant ptrauth key must be i32 constant");
4079+
4080+
ConstantInt *DiscC = nullptr;
4081+
if (Disc) {
4082+
DiscC = dyn_cast<ConstantInt>(Disc);
4083+
if (!DiscC || DiscC->getBitWidth() != 64)
4084+
return error(
4085+
ID.Loc,
4086+
"constant ptrauth integer discriminator must be i64 constant");
4087+
} else {
4088+
DiscC = ConstantInt::get(Type::getInt64Ty(Context), 0);
4089+
}
4090+
4091+
if (AddrDisc) {
4092+
if (!AddrDisc->getType()->isPointerTy())
4093+
return error(
4094+
ID.Loc, "constant ptrauth address discriminator must be a pointer");
4095+
} else {
4096+
AddrDisc = ConstantPointerNull::get(PointerType::get(Context, 0));
4097+
}
4098+
4099+
ID.ConstantVal = ConstantPtrAuth::get(Ptr, KeyC, DiscC, AddrDisc);
4100+
ID.Kind = ValID::t_Constant;
4101+
return false;
4102+
}
40494103

40504104
case lltok::kw_trunc:
40514105
case lltok::kw_bitcast:

llvm/lib/Bitcode/Reader/BitcodeAnalyzer.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,7 @@ GetCodeName(unsigned CodeID, unsigned BlockID,
222222
STRINGIFY_CODE(CST_CODE, CE_UNOP)
223223
STRINGIFY_CODE(CST_CODE, DSO_LOCAL_EQUIVALENT)
224224
STRINGIFY_CODE(CST_CODE, NO_CFI_VALUE)
225+
STRINGIFY_CODE(CST_CODE, PTRAUTH)
225226
case bitc::CST_CODE_BLOCKADDRESS:
226227
return "CST_CODE_BLOCKADDRESS";
227228
STRINGIFY_CODE(CST_CODE, DATA)

llvm/lib/Bitcode/Reader/BitcodeReader.cpp

+24-1
Original file line numberDiff line numberDiff line change
@@ -517,7 +517,8 @@ class BitcodeConstant final : public Value,
517517
static constexpr uint8_t NoCFIOpcode = 252;
518518
static constexpr uint8_t DSOLocalEquivalentOpcode = 251;
519519
static constexpr uint8_t BlockAddressOpcode = 250;
520-
static constexpr uint8_t FirstSpecialOpcode = BlockAddressOpcode;
520+
static constexpr uint8_t ConstantPtrAuthOpcode = 249;
521+
static constexpr uint8_t FirstSpecialOpcode = ConstantPtrAuthOpcode;
521522

522523
// Separate struct to make passing different number of parameters to
523524
// BitcodeConstant::create() more convenient.
@@ -1562,6 +1563,18 @@ Expected<Value *> BitcodeReader::materializeValue(unsigned StartValID,
15621563
C = ConstantExpr::get(BC->Opcode, ConstOps[0], ConstOps[1], BC->Flags);
15631564
} else {
15641565
switch (BC->Opcode) {
1566+
case BitcodeConstant::ConstantPtrAuthOpcode: {
1567+
auto *Key = dyn_cast<ConstantInt>(ConstOps[1]);
1568+
if (!Key)
1569+
return error("ptrauth key operand must be ConstantInt");
1570+
1571+
auto *Disc = dyn_cast<ConstantInt>(ConstOps[2]);
1572+
if (!Disc)
1573+
return error("ptrauth disc operand must be ConstantInt");
1574+
1575+
C = ConstantPtrAuth::get(ConstOps[0], Key, Disc, ConstOps[3]);
1576+
break;
1577+
}
15651578
case BitcodeConstant::NoCFIOpcode: {
15661579
auto *GV = dyn_cast<GlobalValue>(ConstOps[0]);
15671580
if (!GV)
@@ -3644,6 +3657,16 @@ Error BitcodeReader::parseConstants() {
36443657
Record[1]);
36453658
break;
36463659
}
3660+
case bitc::CST_CODE_PTRAUTH: {
3661+
if (Record.size() < 4)
3662+
return error("Invalid ptrauth record");
3663+
// Ptr, Key, Disc, AddrDisc
3664+
V = BitcodeConstant::create(Alloc, CurTy,
3665+
BitcodeConstant::ConstantPtrAuthOpcode,
3666+
{(unsigned)Record[0], (unsigned)Record[1],
3667+
(unsigned)Record[2], (unsigned)Record[3]});
3668+
break;
3669+
}
36473670
}
36483671

36493672
assert(V->getType() == getTypeByID(CurTyID) && "Incorrect result type ID");

llvm/lib/Bitcode/Writer/BitcodeWriter.cpp

+6
Original file line numberDiff line numberDiff line change
@@ -2848,6 +2848,12 @@ void ModuleBitcodeWriter::writeConstants(unsigned FirstVal, unsigned LastVal,
28482848
Code = bitc::CST_CODE_NO_CFI_VALUE;
28492849
Record.push_back(VE.getTypeID(NC->getGlobalValue()->getType()));
28502850
Record.push_back(VE.getValueID(NC->getGlobalValue()));
2851+
} else if (const auto *CPA = dyn_cast<ConstantPtrAuth>(C)) {
2852+
Code = bitc::CST_CODE_PTRAUTH;
2853+
Record.push_back(VE.getValueID(CPA->getPointer()));
2854+
Record.push_back(VE.getValueID(CPA->getKey()));
2855+
Record.push_back(VE.getValueID(CPA->getDiscriminator()));
2856+
Record.push_back(VE.getValueID(CPA->getAddrDiscriminator()));
28512857
} else {
28522858
#ifndef NDEBUG
28532859
C->dump();

llvm/lib/IR/AsmWriter.cpp

+21
Original file line numberDiff line numberDiff line change
@@ -1594,6 +1594,27 @@ static void WriteConstantInternal(raw_ostream &Out, const Constant *CV,
15941594
return;
15951595
}
15961596

1597+
if (const ConstantPtrAuth *CPA = dyn_cast<ConstantPtrAuth>(CV)) {
1598+
Out << "ptrauth (";
1599+
1600+
// ptrauth (ptr CST, i32 KEY[, i64 DISC[, ptr ADDRDISC]?]?)
1601+
unsigned NumOpsToWrite = 2;
1602+
if (!CPA->getOperand(2)->isNullValue())
1603+
NumOpsToWrite = 3;
1604+
if (!CPA->getOperand(3)->isNullValue())
1605+
NumOpsToWrite = 4;
1606+
1607+
ListSeparator LS;
1608+
for (unsigned i = 0, e = NumOpsToWrite; i != e; ++i) {
1609+
Out << LS;
1610+
WriterCtx.TypePrinter->print(CPA->getOperand(i)->getType(), Out);
1611+
Out << ' ';
1612+
WriteAsOperandInternal(Out, CPA->getOperand(i), WriterCtx);
1613+
}
1614+
Out << ')';
1615+
return;
1616+
}
1617+
15971618
if (const ConstantArray *CA = dyn_cast<ConstantArray>(CV)) {
15981619
Type *ETy = CA->getType()->getElementType();
15991620
Out << '[';

0 commit comments

Comments
 (0)