Skip to content
Open
6 changes: 3 additions & 3 deletions clang/lib/CodeGen/CGPointerAuth.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -440,9 +440,9 @@ CodeGenModule::getConstantSignedPointer(llvm::Constant *Pointer, unsigned Key,
IntegerDiscriminator = llvm::ConstantInt::get(Int64Ty, 0);
}

return llvm::ConstantPtrAuth::get(Pointer,
llvm::ConstantInt::get(Int32Ty, Key),
IntegerDiscriminator, AddressDiscriminator);
return llvm::ConstantPtrAuth::get(
Pointer, llvm::ConstantInt::get(Int32Ty, Key), IntegerDiscriminator,
AddressDiscriminator, llvm::Constant::getNullValue(UnqualPtrTy));
}

/// Does a given PointerAuthScheme require us to sign a value
Expand Down
9 changes: 8 additions & 1 deletion llvm/docs/LangRef.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3092,6 +3092,8 @@ A "convergencectrl" operand bundle is only valid on a ``convergent`` operation.
When present, the operand bundle must contain exactly one value of token type.
See the :doc:`ConvergentOperations` document for details.

.. _deactivationsymbol:

Deactivation Symbol Operand Bundles
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Expand Down Expand Up @@ -5149,7 +5151,7 @@ need to refer to the actual function body.
Pointer Authentication Constants
--------------------------------

``ptrauth (ptr CST, i32 KEY[, i64 DISC[, ptr ADDRDISC]?]?)``
``ptrauth (ptr CST, i32 KEY[, i64 DISC[, ptr ADDRDISC[, ptr DS]?]?]?)``

A '``ptrauth``' constant represents a pointer with a cryptographic
authentication signature embedded into some bits, as described in the
Expand Down Expand Up @@ -5178,6 +5180,11 @@ Otherwise, the expression is equivalent to:
%tmp2 = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr CST to i64), i32 KEY, i64 %tmp1)
%val = inttoptr i64 %tmp2 to ptr

If the deactivation symbol operand ``DS`` has a non-null value,
the semantics are as if a :ref:`deactivation-symbol operand bundle
<deactivationsymbol>` were added to the ``llvm.ptrauth.sign`` intrinsic
calls above, with ``DS`` as the only operand.

.. _constantexprs:

Constant Expressions
Expand Down
2 changes: 2 additions & 0 deletions llvm/include/llvm/Bitcode/LLVMBitCodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,8 @@ enum ConstantsCodes {
CST_CODE_CE_GEP_WITH_INRANGE = 31, // [opty, flags, range, n x operands]
CST_CODE_CE_GEP = 32, // [opty, flags, n x operands]
CST_CODE_PTRAUTH = 33, // [ptr, key, disc, addrdisc]
CST_CODE_PTRAUTH2 = 34, // [ptr, key, disc, addrdisc,
// deactivation_symbol]
};

/// CastOpcodes - These are values used in the bitcode files to encode which
Expand Down
13 changes: 9 additions & 4 deletions llvm/include/llvm/IR/Constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -1033,10 +1033,10 @@ class ConstantPtrAuth final : public Constant {
friend struct ConstantPtrAuthKeyType;
friend class Constant;

constexpr static IntrusiveOperandsAllocMarker AllocMarker{4};
constexpr static IntrusiveOperandsAllocMarker AllocMarker{5};

ConstantPtrAuth(Constant *Ptr, ConstantInt *Key, ConstantInt *Disc,
Constant *AddrDisc);
Constant *AddrDisc, Constant *DeactivationSymbol);

void *operator new(size_t s) { return User::operator new(s, AllocMarker); }

Expand All @@ -1046,7 +1046,8 @@ class ConstantPtrAuth final : public Constant {
public:
/// Return a pointer signed with the specified parameters.
LLVM_ABI static ConstantPtrAuth *get(Constant *Ptr, ConstantInt *Key,
ConstantInt *Disc, Constant *AddrDisc);
ConstantInt *Disc, Constant *AddrDisc,
Constant *DeactivationSymbol);
Copy link
Member

Choose a reason for hiding this comment

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

You don't have to do this here, but we probably should make the optional operands (in textual IR) optional here as well, and implicitly make them null? Now that I think about it, I'm not sure how idiomatic that would be

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I considered it, but since there are only a few places that need this, it seemed slightly better to be explicit about all the operands since that's consistent with what we have elsewhere.

What might be nice is if we initialized this using fields of a passed-in struct so that call sites are more readable, but that's a separate change.


/// Produce a new ptrauth expression signing the given value using
/// the same schema as is stored in one.
Expand Down Expand Up @@ -1078,6 +1079,10 @@ class ConstantPtrAuth final : public Constant {
return !getAddrDiscriminator()->isNullValue();
}

Constant *getDeactivationSymbol() const {
return cast<Constant>(Op<4>().get());
}

/// A constant value for the address discriminator which has special
/// significance to ctors/dtors lowering. Regular address discrimination can't
/// be applied for them since uses of llvm.global_{c|d}tors are disallowed
Expand Down Expand Up @@ -1106,7 +1111,7 @@ class ConstantPtrAuth final : public Constant {

template <>
struct OperandTraits<ConstantPtrAuth>
: public FixedNumOperandTraits<ConstantPtrAuth, 4> {};
: public FixedNumOperandTraits<ConstantPtrAuth, 5> {};

DEFINE_TRANSPARENT_OPERAND_ACCESSORS(ConstantPtrAuth, Constant)

Expand Down
5 changes: 4 additions & 1 deletion llvm/include/llvm/SandboxIR/Constant.h
Original file line number Diff line number Diff line change
Expand Up @@ -1363,7 +1363,8 @@ class ConstantPtrAuth final : public Constant {
public:
/// Return a pointer signed with the specified parameters.
LLVM_ABI static ConstantPtrAuth *get(Constant *Ptr, ConstantInt *Key,
ConstantInt *Disc, Constant *AddrDisc);
ConstantInt *Disc, Constant *AddrDisc,
Constant *DeactivationSymbol);
/// The pointer that is signed in this ptrauth signed pointer.
LLVM_ABI Constant *getPointer() const;

Expand All @@ -1378,6 +1379,8 @@ class ConstantPtrAuth final : public Constant {
/// the only global-initializer user of the ptrauth signed pointer.
LLVM_ABI Constant *getAddrDiscriminator() const;

Constant *getDeactivationSymbol() const;

/// Whether there is any non-null address discriminator.
bool hasAddressDiscriminator() const {
return cast<llvm::ConstantPtrAuth>(Val)->hasAddressDiscriminator();
Expand Down
29 changes: 21 additions & 8 deletions llvm/lib/AsmParser/LLParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4216,11 +4216,12 @@ bool LLParser::parseValID(ValID &ID, PerFunctionState *PFS, Type *ExpectedTy) {
}
case lltok::kw_ptrauth: {
// ValID ::= 'ptrauth' '(' ptr @foo ',' i32 <key>
// (',' i64 <disc> (',' ptr addrdisc)? )? ')'
// (',' i64 <disc> (',' ptr addrdisc (',' ptr ds)? )? )? ')'
Lex.Lex();

Constant *Ptr, *Key;
Constant *Disc = nullptr, *AddrDisc = nullptr;
Constant *Disc = nullptr, *AddrDisc = nullptr,
*DeactivationSymbol = nullptr;

if (parseToken(lltok::lparen,
"expected '(' in constant ptrauth expression") ||
Expand All @@ -4229,11 +4230,14 @@ bool LLParser::parseValID(ValID &ID, PerFunctionState *PFS, Type *ExpectedTy) {
"expected comma in constant ptrauth expression") ||
parseGlobalTypeAndValue(Key))
return true;
// If present, parse the optional disc/addrdisc.
if (EatIfPresent(lltok::comma))
if (parseGlobalTypeAndValue(Disc) ||
(EatIfPresent(lltok::comma) && parseGlobalTypeAndValue(AddrDisc)))
return true;
// If present, parse the optional disc/addrdisc/ds.
if (EatIfPresent(lltok::comma) && parseGlobalTypeAndValue(Disc))
return true;
if (EatIfPresent(lltok::comma) && parseGlobalTypeAndValue(AddrDisc))
return true;
if (EatIfPresent(lltok::comma) &&
parseGlobalTypeAndValue(DeactivationSymbol))
return true;
if (parseToken(lltok::rparen,
"expected ')' in constant ptrauth expression"))
return true;
Expand Down Expand Up @@ -4264,7 +4268,16 @@ bool LLParser::parseValID(ValID &ID, PerFunctionState *PFS, Type *ExpectedTy) {
AddrDisc = ConstantPointerNull::get(PointerType::get(Context, 0));
}

ID.ConstantVal = ConstantPtrAuth::get(Ptr, KeyC, DiscC, AddrDisc);
if (DeactivationSymbol) {
if (!DeactivationSymbol->getType()->isPointerTy())
return error(
ID.Loc, "constant ptrauth deactivation symbol must be a pointer");
} else {
DeactivationSymbol = ConstantPointerNull::get(PointerType::get(Context, 0));
}

ID.ConstantVal =
ConstantPtrAuth::get(Ptr, KeyC, DiscC, AddrDisc, DeactivationSymbol);
ID.Kind = ValID::t_Constant;
return false;
}
Expand Down
21 changes: 20 additions & 1 deletion llvm/lib/Bitcode/Reader/BitcodeReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1609,7 +1609,16 @@ Expected<Value *> BitcodeReader::materializeValue(unsigned StartValID,
if (!Disc)
return error("ptrauth disc operand must be ConstantInt");

C = ConstantPtrAuth::get(ConstOps[0], Key, Disc, ConstOps[3]);
Constant *DeactivationSymbol =
ConstOps.size() > 4 ? ConstOps[4]
: ConstantPointerNull::get(cast<PointerType>(
ConstOps[3]->getType()));
if (!DeactivationSymbol->getType()->isPointerTy())
return error(
"ptrauth deactivation symbol operand must be a pointer");

C = ConstantPtrAuth::get(ConstOps[0], Key, Disc, ConstOps[3],
DeactivationSymbol);
break;
}
case BitcodeConstant::NoCFIOpcode: {
Expand Down Expand Up @@ -3809,6 +3818,16 @@ Error BitcodeReader::parseConstants() {
(unsigned)Record[2], (unsigned)Record[3]});
break;
}
case bitc::CST_CODE_PTRAUTH2: {
if (Record.size() < 4)
Copy link
Member

Choose a reason for hiding this comment

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

Should this be 5?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Indeed, fixed

return error("Invalid ptrauth record");
// Ptr, Key, Disc, AddrDisc, DeactivationSymbol
V = BitcodeConstant::create(
Alloc, CurTy, BitcodeConstant::ConstantPtrAuthOpcode,
{(unsigned)Record[0], (unsigned)Record[1], (unsigned)Record[2],
(unsigned)Record[3], (unsigned)Record[4]});
break;
}
}

assert(V->getType() == getTypeByID(CurTyID) && "Incorrect result type ID");
Expand Down
3 changes: 2 additions & 1 deletion llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3007,11 +3007,12 @@ void ModuleBitcodeWriter::writeConstants(unsigned FirstVal, unsigned LastVal,
Record.push_back(VE.getTypeID(NC->getGlobalValue()->getType()));
Record.push_back(VE.getValueID(NC->getGlobalValue()));
} else if (const auto *CPA = dyn_cast<ConstantPtrAuth>(C)) {
Code = bitc::CST_CODE_PTRAUTH;
Code = bitc::CST_CODE_PTRAUTH2;
Copy link
Contributor

@ojhunt ojhunt Sep 11, 2025

Choose a reason for hiding this comment

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

I think this should be

Constant *DeactivationSymbol = CPA->getDeactivationSymbol();
Code =  DeactivationSymbol->isNullValue() ? CST_CODE_PTRAUTH : CST_CODE_PTRAUTH2;
...
if (!DeactivationSymbol->isNullValue())
   Record.push_back(VE.getValueID(DeactivationSymbol));

Copy link
Contributor

Choose a reason for hiding this comment

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

updated to correct the of the Code

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Bitcode doesn't have forwards compatibility, only backwards compatibility. So I think it's fine to use PTRAUTH2 unconditionally here.

Record.push_back(VE.getValueID(CPA->getPointer()));
Record.push_back(VE.getValueID(CPA->getKey()));
Record.push_back(VE.getValueID(CPA->getDiscriminator()));
Record.push_back(VE.getValueID(CPA->getAddrDiscriminator()));
Record.push_back(VE.getValueID(CPA->getDeactivationSymbol()));
} else {
#ifndef NDEBUG
C->dump();
Expand Down
4 changes: 3 additions & 1 deletion llvm/lib/IR/AsmWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1667,12 +1667,14 @@ static void WriteConstantInternal(raw_ostream &Out, const Constant *CV,
if (const ConstantPtrAuth *CPA = dyn_cast<ConstantPtrAuth>(CV)) {
Out << "ptrauth (";

// ptrauth (ptr CST, i32 KEY[, i64 DISC[, ptr ADDRDISC]?]?)
// ptrauth (ptr CST, i32 KEY[, i64 DISC[, ptr ADDRDISC[, ptr DS]?]?]?)
unsigned NumOpsToWrite = 2;
if (!CPA->getOperand(2)->isNullValue())
NumOpsToWrite = 3;
if (!CPA->getOperand(3)->isNullValue())
NumOpsToWrite = 4;
if (!CPA->getOperand(4)->isNullValue())
NumOpsToWrite = 5;

ListSeparator LS;
for (unsigned i = 0, e = NumOpsToWrite; i != e; ++i) {
Expand Down
18 changes: 14 additions & 4 deletions llvm/lib/IR/Constants.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2061,28 +2061,33 @@ Value *NoCFIValue::handleOperandChangeImpl(Value *From, Value *To) {
//

ConstantPtrAuth *ConstantPtrAuth::get(Constant *Ptr, ConstantInt *Key,
ConstantInt *Disc, Constant *AddrDisc) {
Constant *ArgVec[] = {Ptr, Key, Disc, AddrDisc};
ConstantInt *Disc, Constant *AddrDisc,
Constant *DeactivationSymbol) {
Constant *ArgVec[] = {Ptr, Key, Disc, AddrDisc, DeactivationSymbol};
ConstantPtrAuthKeyType MapKey(ArgVec);
LLVMContextImpl *pImpl = Ptr->getContext().pImpl;
return pImpl->ConstantPtrAuths.getOrCreate(Ptr->getType(), MapKey);
}

ConstantPtrAuth *ConstantPtrAuth::getWithSameSchema(Constant *Pointer) const {
return get(Pointer, getKey(), getDiscriminator(), getAddrDiscriminator());
return get(Pointer, getKey(), getDiscriminator(), getAddrDiscriminator(),
getDeactivationSymbol());
}

ConstantPtrAuth::ConstantPtrAuth(Constant *Ptr, ConstantInt *Key,
ConstantInt *Disc, Constant *AddrDisc)
ConstantInt *Disc, Constant *AddrDisc,
Constant *DeactivationSymbol)
: Constant(Ptr->getType(), Value::ConstantPtrAuthVal, AllocMarker) {
assert(Ptr->getType()->isPointerTy());
assert(Key->getBitWidth() == 32);
assert(Disc->getBitWidth() == 64);
assert(AddrDisc->getType()->isPointerTy());
assert(DeactivationSymbol->getType()->isPointerTy());
setOperand(0, Ptr);
setOperand(1, Key);
setOperand(2, Disc);
setOperand(3, AddrDisc);
setOperand(4, DeactivationSymbol);
}

/// Remove the constant from the constant table.
Expand Down Expand Up @@ -2130,6 +2135,11 @@ bool ConstantPtrAuth::hasSpecialAddressDiscriminator(uint64_t Value) const {
bool ConstantPtrAuth::isKnownCompatibleWith(const Value *Key,
const Value *Discriminator,
const DataLayout &DL) const {
// This function may only be validly called to analyze a ptrauth operation with
// no deactivation symbol, so if we have one it isn't compatible.
if (!getDeactivationSymbol()->isNullValue())
return false;

// If the keys are different, there's no chance for this to be compatible.
if (getKey() != Key)
return false;
Expand Down
3 changes: 2 additions & 1 deletion llvm/lib/IR/ConstantsContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -539,7 +539,8 @@ struct ConstantPtrAuthKeyType {

ConstantPtrAuth *create(TypeClass *Ty) const {
return new ConstantPtrAuth(Operands[0], cast<ConstantInt>(Operands[1]),
cast<ConstantInt>(Operands[2]), Operands[3]);
cast<ConstantInt>(Operands[2]), Operands[3],
Operands[4]);
}
};

Expand Down
4 changes: 3 additions & 1 deletion llvm/lib/IR/Core.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1699,7 +1699,9 @@ LLVMValueRef LLVMConstantPtrAuth(LLVMValueRef Ptr, LLVMValueRef Key,
LLVMValueRef Disc, LLVMValueRef AddrDisc) {
return wrap(ConstantPtrAuth::get(
unwrap<Constant>(Ptr), unwrap<ConstantInt>(Key),
unwrap<ConstantInt>(Disc), unwrap<Constant>(AddrDisc)));
unwrap<ConstantInt>(Disc), unwrap<Constant>(AddrDisc),
ConstantPointerNull::get(
cast<PointerType>(unwrap<Constant>(AddrDisc)->getType()))));
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do we want to extend the C API to give access to this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I reckon that could be done in a followup if anyone needs it.

}

/*-- Opcode mapping */
Expand Down
8 changes: 8 additions & 0 deletions llvm/lib/IR/Verifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2631,6 +2631,14 @@ void Verifier::visitConstantPtrAuth(const ConstantPtrAuth *CPA) {

Check(CPA->getDiscriminator()->getBitWidth() == 64,
"signed ptrauth constant discriminator must be i64 constant integer");

Check(CPA->getDeactivationSymbol()->getType()->isPointerTy(),
"signed ptrauth constant deactivation symbol must be a pointer");

Check(isa<GlobalValue>(CPA->getDeactivationSymbol()) ||
CPA->getDeactivationSymbol()->isNullValue(),
Copy link
Collaborator

Choose a reason for hiding this comment

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

Maybe also check CPA->getDeactivationSymbol()->getType()->isPointerTy()?

Copy link
Contributor Author

@pcc pcc Sep 9, 2025

Choose a reason for hiding this comment

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

I think it's probably best to check that in the ConstantPtrAuth constructor; I've added a check for that there.

I had already added a check for this operand being a pointer in the .ll parser; I noticed that there was no test coverage for that so I added it.

Copy link
Collaborator

Choose a reason for hiding this comment

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

For things we can easily check in the verifier, I prefer to check them even if there's also an assertion, to catch issues in assert-disabled builds.

Also, missing check in the bitcode parser.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

For things we can easily check in the verifier, I prefer to check them even if there's also an assertion, to catch issues in assert-disabled builds.

Isn't the verifier disabled by default in no-asserts builds? So I guess it wouldn't make much of a difference.

CmdArgs.push_back("-disable-llvm-verifier");

(Looks like the comments were meant to read "no-asserts", I sent #157769 for that.)

That being said I don't feel strongly so I added it.

Also, missing check in the bitcode parser.

Added.

"signed ptrauth constant deactivation symbol must be a global value "
"or null");
}

bool Verifier::verifyAttributeCount(AttributeList Attrs, unsigned Params) {
Expand Down
11 changes: 9 additions & 2 deletions llvm/lib/SandboxIR/Constant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -412,10 +412,12 @@ PointerType *NoCFIValue::getType() const {
}

ConstantPtrAuth *ConstantPtrAuth::get(Constant *Ptr, ConstantInt *Key,
ConstantInt *Disc, Constant *AddrDisc) {
ConstantInt *Disc, Constant *AddrDisc,
Constant *DeactivationSymbol) {
auto *LLVMC = llvm::ConstantPtrAuth::get(
cast<llvm::Constant>(Ptr->Val), cast<llvm::ConstantInt>(Key->Val),
cast<llvm::ConstantInt>(Disc->Val), cast<llvm::Constant>(AddrDisc->Val));
cast<llvm::ConstantInt>(Disc->Val), cast<llvm::Constant>(AddrDisc->Val),
cast<llvm::Constant>(DeactivationSymbol->Val));
return cast<ConstantPtrAuth>(Ptr->getContext().getOrCreateConstant(LLVMC));
}

Expand All @@ -439,6 +441,11 @@ Constant *ConstantPtrAuth::getAddrDiscriminator() const {
cast<llvm::ConstantPtrAuth>(Val)->getAddrDiscriminator());
}

Constant *ConstantPtrAuth::getDeactivationSymbol() const {
return Ctx.getOrCreateConstant(
cast<llvm::ConstantPtrAuth>(Val)->getDeactivationSymbol());
}

ConstantPtrAuth *ConstantPtrAuth::getWithSameSchema(Constant *Pointer) const {
auto *LLVMC = cast<llvm::ConstantPtrAuth>(Val)->getWithSameSchema(
cast<llvm::Constant>(Pointer->Val));
Expand Down
Loading
Loading