Skip to content

Commit aea9884

Browse files
[IR][AArch64] Add "ptrauth(...)" Constant to represent signed pointers.
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. Co-Authored-by: Tim Northover <[email protected]>
1 parent 0c423af commit aea9884

26 files changed

+438
-1
lines changed

llvm/docs/LangRef.rst

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4748,6 +4748,33 @@ reference to the CFI jump table in the ``LowerTypeTests`` pass. These constants
47484748
may be useful in low-level programs, such as operating system kernels, which
47494749
need to refer to the actual function body.
47504750

4751+
.. _ptrauth
4752+
4753+
Authenticated Pointers
4754+
----------------------
4755+
4756+
``ptrauth (ptr CST, i32 KEY, ptr ADDRDISC, i16 DISC)
4757+
4758+
A '``ptrauth``' constant represents a pointer with a cryptographic
4759+
authentication signature embedded into some bits. Its type is the same as the
4760+
first argument.
4761+
4762+
4763+
If the address disciminator is ``null`` then the expression is equivalent to
4764+
4765+
.. code-block:llvm
4766+
%tmp = call i64 @llvm.ptrauth.sign.i64(i64 ptrtoint (ptr CST to i64), i32 KEY, i64 DISC)
4767+
%val = inttoptr i64 %tmp to ptr
4768+
4769+
If the address discriminator is present, then it is
4770+
4771+
.. code-block:llvm
4772+
%tmp1 = call i64 @llvm.ptrauth.blend.i64(i64 ptrtoint (ptr ADDRDISC to i64), i64 DISC)
4773+
%tmp2 = call i64 @llvm.ptrauth.sign.i64(i64 ptrtoint (ptr CST to i64), i64 %tmp1)
4774+
%val = inttoptr i64 %tmp2 to ptr
4775+
4776+
%tmp = call i64 @llvm.ptrauth.blend.i64
4777+
47514778
.. _constantexprs:
47524779

47534780
Constant Expressions

llvm/include/llvm-c/Core.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,7 @@ typedef enum {
286286
LLVMInstructionValueKind,
287287
LLVMPoisonValueValueKind,
288288
LLVMConstantTargetNoneValueKind,
289+
LLVMConstantPtrAuthValueKind,
289290
} LLVMValueKind;
290291

291292
typedef enum {

llvm/include/llvm/AsmParser/LLToken.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,7 @@ enum Kind {
343343
kw_insertvalue,
344344
kw_blockaddress,
345345
kw_dso_local_equivalent,
346+
kw_ptrauth,
346347
kw_no_cfi,
347348

348349
kw_freeze,

llvm/include/llvm/Bitcode/LLVMBitCodes.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,8 @@ enum ConstantsCodes {
411411
// sideeffect|alignstack|
412412
// asmdialect|unwind,
413413
// asmstr,conststr]
414+
CST_CODE_SIGNED_PTR = 31, // CE_SIGNED_PTR: [ptrty, ptr, key,
415+
// addrdiscty, addrdisc, disc]
414416
};
415417

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

llvm/include/llvm/IR/Constants.h

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1006,6 +1006,69 @@ struct OperandTraits<NoCFIValue> : public FixedNumOperandTraits<NoCFIValue, 1> {
10061006

10071007
DEFINE_TRANSPARENT_OPERAND_ACCESSORS(NoCFIValue, Value)
10081008

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

llvm/include/llvm/IR/Value.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ HANDLE_GLOBAL_VALUE(GlobalAlias)
7878
HANDLE_GLOBAL_VALUE(GlobalIFunc)
7979
HANDLE_GLOBAL_VALUE(GlobalVariable)
8080
HANDLE_CONSTANT(BlockAddress)
81+
HANDLE_CONSTANT(ConstantPtrAuth)
8182
HANDLE_CONSTANT(ConstantExpr)
8283
HANDLE_CONSTANT_EXCLUDE_LLVM_C_API(DSOLocalEquivalent)
8384
HANDLE_CONSTANT_EXCLUDE_LLVM_C_API(NoCFIValue)

llvm/lib/Analysis/ValueTracking.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2900,6 +2900,10 @@ bool isKnownNonZero(const Value *V, const APInt &DemandedElts, unsigned Depth,
29002900
return true;
29012901
}
29022902

2903+
// Constant ptrauth can be null, iff the base pointer can be.
2904+
if (auto *CPA = dyn_cast<ConstantPtrAuth>(V))
2905+
return isKnownNonZero(CPA->getPointer(), DemandedElts, Depth, Q);
2906+
29032907
// A global variable in address space 0 is non null unless extern weak
29042908
// or an absolute symbol reference. Other address spaces may have null as a
29052909
// valid address for a global, so we can't assume anything.
@@ -6993,6 +6997,9 @@ static bool isGuaranteedNotToBeUndefOrPoison(
69936997
isa<ConstantPointerNull>(C) || isa<Function>(C))
69946998
return true;
69956999

7000+
if (isa<ConstantPtrAuth>(C))
7001+
return true;
7002+
69967003
if (C->getType()->isVectorTy() && !isa<ConstantExpr>(C))
69977004
return (!includesUndef(Kind) ? !C->containsPoisonElement()
69987005
: !C->containsUndefOrPoisonElement()) &&

llvm/lib/AsmParser/LLLexer.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -708,6 +708,7 @@ lltok::Kind LLLexer::LexIdentifier() {
708708
KEYWORD(blockaddress);
709709
KEYWORD(dso_local_equivalent);
710710
KEYWORD(no_cfi);
711+
KEYWORD(ptrauth);
711712

712713
// Metadata types.
713714
KEYWORD(distinct);

llvm/lib/AsmParser/LLParser.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3998,6 +3998,48 @@ bool LLParser::parseValID(ValID &ID, PerFunctionState *PFS, Type *ExpectedTy) {
39983998
ID.NoCFI = true;
39993999
return false;
40004000
}
4001+
case lltok::kw_ptrauth: {
4002+
// ValID ::= 'ptrauth' '(' ptr @foo ',' i32 <key> ','
4003+
// ptr addrdisc ',' i64 <disc> ')'
4004+
Lex.Lex();
4005+
4006+
Constant *Ptr, *Key, *AddrDisc, *Disc;
4007+
4008+
if (parseToken(lltok::lparen,
4009+
"expected '(' in signed pointer expression") ||
4010+
parseGlobalTypeAndValue(Ptr) ||
4011+
parseToken(lltok::comma,
4012+
"expected comma in signed pointer expression") ||
4013+
parseGlobalTypeAndValue(Key) ||
4014+
parseToken(lltok::comma,
4015+
"expected comma in signed pointer expression") ||
4016+
parseGlobalTypeAndValue(AddrDisc) ||
4017+
parseToken(lltok::comma,
4018+
"expected comma in signed pointer expression") ||
4019+
parseGlobalTypeAndValue(Disc) ||
4020+
parseToken(lltok::rparen, "expected ')' in signed pointer expression"))
4021+
return true;
4022+
4023+
if (!Ptr->getType()->isPointerTy())
4024+
return error(ID.Loc, "signed pointer must be a pointer");
4025+
4026+
auto KeyC = dyn_cast<ConstantInt>(Key);
4027+
if (!KeyC || KeyC->getBitWidth() != 32)
4028+
return error(ID.Loc, "signed pointer key must be i32 constant integer");
4029+
4030+
if (!AddrDisc->getType()->isPointerTy())
4031+
return error(ID.Loc,
4032+
"signed pointer address discriminator must be a pointer");
4033+
4034+
auto DiscC = dyn_cast<ConstantInt>(Disc);
4035+
if (!DiscC || DiscC->getBitWidth() != 64)
4036+
return error(ID.Loc,
4037+
"signed pointer discriminator must be i64 constant integer");
4038+
4039+
ID.ConstantVal = ConstantPtrAuth::get(Ptr, KeyC, AddrDisc, DiscC);
4040+
ID.Kind = ValID::t_Constant;
4041+
return false;
4042+
}
40014043

40024044
case lltok::kw_trunc:
40034045
case lltok::kw_bitcast:

llvm/lib/Bitcode/Reader/BitcodeReader.cpp

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -504,7 +504,8 @@ class BitcodeConstant final : public Value,
504504
static constexpr uint8_t NoCFIOpcode = 252;
505505
static constexpr uint8_t DSOLocalEquivalentOpcode = 251;
506506
static constexpr uint8_t BlockAddressOpcode = 250;
507-
static constexpr uint8_t FirstSpecialOpcode = BlockAddressOpcode;
507+
static constexpr uint8_t ConstantPtrAuthOpcode = 249;
508+
static constexpr uint8_t FirstSpecialOpcode = ConstantPtrAuthOpcode;
508509

509510
// Separate struct to make passing different number of parameters to
510511
// BitcodeConstant::create() more convenient.
@@ -1528,6 +1529,18 @@ Expected<Value *> BitcodeReader::materializeValue(unsigned StartValID,
15281529
C = ConstantExpr::get(BC->Opcode, ConstOps[0], ConstOps[1], BC->Flags);
15291530
} else {
15301531
switch (BC->Opcode) {
1532+
case BitcodeConstant::ConstantPtrAuthOpcode: {
1533+
auto *Key = dyn_cast<ConstantInt>(ConstOps[1]);
1534+
if (!Key)
1535+
return error("ptrauth key operand must be ConstantInt");
1536+
1537+
auto *Disc = dyn_cast<ConstantInt>(ConstOps[3]);
1538+
if (!Disc)
1539+
return error("ptrauth disc operand must be ConstantInt");
1540+
1541+
C = ConstantPtrAuth::get(ConstOps[0], Key, ConstOps[2], Disc);
1542+
break;
1543+
}
15311544
case BitcodeConstant::NoCFIOpcode: {
15321545
auto *GV = dyn_cast<GlobalValue>(ConstOps[0]);
15331546
if (!GV)
@@ -3596,6 +3609,21 @@ Error BitcodeReader::parseConstants() {
35963609
Record[1]);
35973610
break;
35983611
}
3612+
case bitc::CST_CODE_SIGNED_PTR: {
3613+
if (Record.size() < 6)
3614+
return error("Invalid record");
3615+
Type *PtrTy = getTypeByID(Record[0]);
3616+
if (!PtrTy)
3617+
return error("Invalid record");
3618+
3619+
// PtrTy, Ptr, Key, AddrDiscTy, AddrDisc, Disc
3620+
V = BitcodeConstant::create(
3621+
Alloc, CurTy, BitcodeConstant::ConstantPtrAuthOpcode,
3622+
{(unsigned)Record[1], (unsigned)Record[2], (unsigned)Record[4],
3623+
(unsigned)Record[5]});
3624+
3625+
break;
3626+
}
35993627
}
36003628

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

llvm/lib/Bitcode/Writer/BitcodeWriter.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2800,6 +2800,14 @@ void ModuleBitcodeWriter::writeConstants(unsigned FirstVal, unsigned LastVal,
28002800
Record.push_back(VE.getTypeID(BA->getFunction()->getType()));
28012801
Record.push_back(VE.getValueID(BA->getFunction()));
28022802
Record.push_back(VE.getGlobalBasicBlockID(BA->getBasicBlock()));
2803+
} else if (const ConstantPtrAuth *SP = dyn_cast<ConstantPtrAuth>(C)) {
2804+
Code = bitc::CST_CODE_SIGNED_PTR;
2805+
Record.push_back(VE.getTypeID(SP->getPointer()->getType()));
2806+
Record.push_back(VE.getValueID(SP->getPointer()));
2807+
Record.push_back(VE.getValueID(SP->getKey()));
2808+
Record.push_back(VE.getTypeID(SP->getAddrDiscriminator()->getType()));
2809+
Record.push_back(VE.getValueID(SP->getAddrDiscriminator()));
2810+
Record.push_back(VE.getValueID(SP->getDiscriminator()));
28032811
} else if (const auto *Equiv = dyn_cast<DSOLocalEquivalent>(C)) {
28042812
Code = bitc::CST_CODE_DSO_LOCAL_EQUIVALENT;
28052813
Record.push_back(VE.getTypeID(Equiv->getGlobalValue()->getType()));

llvm/lib/IR/AsmWriter.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1578,6 +1578,21 @@ static void WriteConstantInternal(raw_ostream &Out, const Constant *CV,
15781578
return;
15791579
}
15801580

1581+
if (const ConstantPtrAuth *SP = dyn_cast<ConstantPtrAuth>(CV)) {
1582+
Out << "ptrauth (";
1583+
1584+
for (unsigned i = 0; i < SP->getNumOperands(); ++i) {
1585+
WriterCtx.TypePrinter->print(SP->getOperand(i)->getType(), Out);
1586+
Out << ' ';
1587+
WriteAsOperandInternal(Out, SP->getOperand(i), WriterCtx);
1588+
if (i != SP->getNumOperands() - 1)
1589+
Out << ", ";
1590+
}
1591+
1592+
Out << ')';
1593+
return;
1594+
}
1595+
15811596
if (const ConstantArray *CA = dyn_cast<ConstantArray>(CV)) {
15821597
Type *ETy = CA->getType()->getElementType();
15831598
Out << '[';

llvm/lib/IR/ConstantFold.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1154,6 +1154,9 @@ static ICmpInst::Predicate evaluateICmpRelation(Constant *V1, Constant *V2) {
11541154
GV->getType()->getAddressSpace()))
11551155
return ICmpInst::ICMP_UGT;
11561156
}
1157+
} else if (const ConstantPtrAuth *SP = dyn_cast<ConstantPtrAuth>(V1)) {
1158+
// FIXME: ahmedbougacha: implement ptrauth cst comparison
1159+
return ICmpInst::BAD_ICMP_PREDICATE;
11571160
} else {
11581161
// Ok, the LHS is known to be a constantexpr. The RHS can be any of a
11591162
// constantexpr, a global, block address, or a simple constant.

0 commit comments

Comments
 (0)