Skip to content

Commit 831d36f

Browse files
committed
[clang] Function type attribute to prevent CFI instrumentation
This introduces the attribute discussed in https://discourse.llvm.org/t/rfc-function-type-attribute-to-prevent-cfi-instrumentation/85458. The proposed name has been changed from `no_cfi` to `cfi_unchecked_callee` to help differentiate from `no_sanitize("cfi")` more easily. The proposed attribute has the following semantics: 1. Indirect calls to a function type with this attribute will not be instrumented with CFI. That is, the indirect call will not be checked. Note that this only changes the behavior for indirect calls on pointers to function types having this attribute. It does not prevent all indirect function calls for a given type from being checked. 2. All direct references to a function whose type has this attribute will always reference the true function definition rather than an entry in the CFI jump table. 3. When a pointer to a function with this attribute is implicitly cast to a pointer to a function without this attribute, the compiler will give a warning saying this attribute is discarded. This warning can be silenced with an explicit C-style cast or C++ static_cast.
1 parent a6385a8 commit 831d36f

24 files changed

+768
-22
lines changed

clang/include/clang/AST/Type.h

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1988,7 +1988,7 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
19881988
/// Extra information which affects how the function is called, like
19891989
/// regparm and the calling convention.
19901990
LLVM_PREFERRED_TYPE(CallingConv)
1991-
unsigned ExtInfo : 14;
1991+
unsigned ExtInfo : 15;
19921992

19931993
/// The number of parameters this function has, not counting '...'.
19941994
/// According to [implimits] 8 bits should be enough here but this is
@@ -2563,6 +2563,8 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
25632563
bool isSignableType() const;
25642564
bool isAnyPointerType() const; // Any C pointer or ObjC object pointer
25652565
bool isCountAttributedType() const;
2566+
bool isCFIUncheckedCalleeFunctionType() const;
2567+
bool isPointerToCFIUncheckedCalleeFunctionType() const;
25662568
bool isBlockPointerType() const;
25672569
bool isVoidPointerType() const;
25682570
bool isReferenceType() const;
@@ -4493,8 +4495,8 @@ class FunctionType : public Type {
44934495
// adjust the Bits field below, and if you add bits, you'll need to adjust
44944496
// Type::FunctionTypeBitfields::ExtInfo as well.
44954497

4496-
// | CC |noreturn|produces|nocallersavedregs|regparm|nocfcheck|cmsenscall|
4497-
// |0 .. 5| 6 | 7 | 8 |9 .. 11| 12 | 13 |
4498+
// | CC |noreturn|produces|nocallersavedregs|regparm|nocfcheck|cmsenscall|cfiuncheckedcallee|
4499+
// |0 .. 5| 6 | 7 | 8 |9 .. 11| 12 | 13 | 14 |
44984500
//
44994501
// regparm is either 0 (no regparm attribute) or the regparm value+1.
45004502
enum { CallConvMask = 0x3F };
@@ -4504,6 +4506,7 @@ class FunctionType : public Type {
45044506
enum { RegParmMask = 0xe00, RegParmOffset = 9 };
45054507
enum { NoCfCheckMask = 0x1000 };
45064508
enum { CmseNSCallMask = 0x2000 };
4509+
enum { CFIUncheckedCalleeMask = 0x4000 };
45074510
uint16_t Bits = CC_C;
45084511

45094512
ExtInfo(unsigned Bits) : Bits(static_cast<uint16_t>(Bits)) {}
@@ -4513,14 +4516,15 @@ class FunctionType : public Type {
45134516
// have all the elements (when reading an AST file for example).
45144517
ExtInfo(bool noReturn, bool hasRegParm, unsigned regParm, CallingConv cc,
45154518
bool producesResult, bool noCallerSavedRegs, bool NoCfCheck,
4516-
bool cmseNSCall) {
4519+
bool cmseNSCall, bool cfiUncheckedCallee) {
45174520
assert((!hasRegParm || regParm < 7) && "Invalid regparm value");
45184521
Bits = ((unsigned)cc) | (noReturn ? NoReturnMask : 0) |
45194522
(producesResult ? ProducesResultMask : 0) |
45204523
(noCallerSavedRegs ? NoCallerSavedRegsMask : 0) |
45214524
(hasRegParm ? ((regParm + 1) << RegParmOffset) : 0) |
45224525
(NoCfCheck ? NoCfCheckMask : 0) |
4523-
(cmseNSCall ? CmseNSCallMask : 0);
4526+
(cmseNSCall ? CmseNSCallMask : 0) |
4527+
(cfiUncheckedCallee ? CFIUncheckedCalleeMask : 0);
45244528
}
45254529

45264530
// Constructor with all defaults. Use when for example creating a
@@ -4537,6 +4541,7 @@ class FunctionType : public Type {
45374541
bool getNoCallerSavedRegs() const { return Bits & NoCallerSavedRegsMask; }
45384542
bool getNoCfCheck() const { return Bits & NoCfCheckMask; }
45394543
bool getHasRegParm() const { return ((Bits & RegParmMask) >> RegParmOffset) != 0; }
4544+
bool getCFIUncheckedCallee() const { return Bits & CFIUncheckedCalleeMask; }
45404545

45414546
unsigned getRegParm() const {
45424547
unsigned RegParm = (Bits & RegParmMask) >> RegParmOffset;
@@ -4557,6 +4562,13 @@ class FunctionType : public Type {
45574562
// Note that we don't have setters. That is by design, use
45584563
// the following with methods instead of mutating these objects.
45594564

4565+
ExtInfo withCFIUncheckedCallee(bool cfiUncheckedCallee) const {
4566+
if (cfiUncheckedCallee)
4567+
return ExtInfo(Bits | CFIUncheckedCalleeMask);
4568+
else
4569+
return ExtInfo(Bits & ~CFIUncheckedCalleeMask);
4570+
}
4571+
45604572
ExtInfo withNoReturn(bool noReturn) const {
45614573
if (noReturn)
45624574
return ExtInfo(Bits | NoReturnMask);
@@ -4709,6 +4721,10 @@ class FunctionType : public Type {
47094721
/// type.
47104722
bool getNoReturnAttr() const { return getExtInfo().getNoReturn(); }
47114723

4724+
bool getCFIUncheckedCalleeAttr() const {
4725+
return getExtInfo().getCFIUncheckedCallee();
4726+
}
4727+
47124728
bool getCmseNSCallAttr() const { return getExtInfo().getCmseNSCall(); }
47134729
CallingConv getCallConv() const { return getExtInfo().getCC(); }
47144730
ExtInfo getExtInfo() const { return ExtInfo(FunctionTypeBits.ExtInfo); }
@@ -8244,6 +8260,19 @@ inline bool Type::isObjectPointerType() const {
82448260
return false;
82458261
}
82468262

8263+
inline bool Type::isCFIUncheckedCalleeFunctionType() const {
8264+
if (const auto *Fn = getAs<FunctionType>())
8265+
return Fn->getCFIUncheckedCalleeAttr();
8266+
return false;
8267+
}
8268+
8269+
inline bool Type::isPointerToCFIUncheckedCalleeFunctionType() const {
8270+
QualType Pointee = getPointeeType();
8271+
if (Pointee.isNull())
8272+
return false;
8273+
return Pointee->isCFIUncheckedCalleeFunctionType();
8274+
}
8275+
82478276
inline bool Type::isFunctionPointerType() const {
82488277
if (const auto *T = getAs<PointerType>())
82498278
return T->getPointeeType()->isFunctionType();

clang/include/clang/AST/TypeProperties.td

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -298,14 +298,17 @@ let Class = FunctionType in {
298298
def : Property<"cmseNSCall", Bool> {
299299
let Read = [{ node->getExtInfo().getCmseNSCall() }];
300300
}
301+
def : Property<"cfiUncheckedCallee", Bool> {
302+
let Read = [{ node->getExtInfo().getCFIUncheckedCallee() }];
303+
}
301304
}
302305

303306
let Class = FunctionNoProtoType in {
304307
def : Creator<[{
305308
auto extInfo = FunctionType::ExtInfo(noReturn, hasRegParm, regParm,
306309
callingConvention, producesResult,
307310
noCallerSavedRegs, noCfCheck,
308-
cmseNSCall);
311+
cmseNSCall, cfiUncheckedCallee);
309312
return ctx.getFunctionNoProtoType(returnType, extInfo);
310313
}]>;
311314
}
@@ -348,7 +351,7 @@ let Class = FunctionProtoType in {
348351
auto extInfo = FunctionType::ExtInfo(noReturn, hasRegParm, regParm,
349352
callingConvention, producesResult,
350353
noCallerSavedRegs, noCfCheck,
351-
cmseNSCall);
354+
cmseNSCall, cfiUncheckedCallee);
352355
FunctionProtoType::ExtProtoInfo epi;
353356
epi.ExtInfo = extInfo;
354357
epi.Variadic = variadic;

clang/include/clang/Basic/Attr.td

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3098,6 +3098,11 @@ def NoDeref : TypeAttr {
30983098
let Documentation = [NoDerefDocs];
30993099
}
31003100

3101+
def CFIUncheckedCallee : TypeAttr {
3102+
let Spellings = [Clang<"cfi_unchecked_callee">];
3103+
let Documentation = [CFIUncheckedCalleeDocs];
3104+
}
3105+
31013106
def ReqdWorkGroupSize : InheritableAttr {
31023107
// Does not have a [[]] spelling because it is an OpenCL-related attribute.
31033108
let Spellings = [GNU<"reqd_work_group_size">];

clang/include/clang/Basic/AttrDocs.td

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6869,6 +6869,54 @@ for references or Objective-C object pointers.
68696869
}];
68706870
}
68716871

6872+
def CFIUncheckedCalleeDocs : Documentation {
6873+
let Category = DocCatType;
6874+
let Content = [{
6875+
``cfi_unchecked_callee`` is a function type attribute which prevents the compiler from instrumenting
6876+
`Control Flow Integrity <https://clang.llvm.org/docs/ControlFlowIntegrity.html>`_ checks on indirect
6877+
function calls. Specifically, the attribute has the following semantics:
6878+
6879+
1. Indirect calls to a function type with this attribute will not be instrumented with CFI. That is,
6880+
the indirect call will not be checked. Note that this only changes the behavior for indirect calls
6881+
on pointers to function types having this attribute. It does not prevent all indirect function calls
6882+
for a given type from being checked.
6883+
2. All direct references to a function whose type has this attribute will always reference the
6884+
function definition rather than an entry in the CFI jump table.
6885+
3. When a pointer to a function with this attribute is implicitly cast to a pointer to a function
6886+
without this attribute, the compiler will give a warning saying this attribute is discarded. This
6887+
warning can be silenced with an explicit cast. Note an explicit cast just disables the warning, so
6888+
direct references to a function with a ``cfi_unchecked_callee`` attribute will still reference the
6889+
function definition rather than the CFI jump table.
6890+
6891+
.. code-block:: c
6892+
6893+
#define CFI_UNCHECKED_CALLEE __attribute__((cfi_unchecked_callee))
6894+
6895+
void no_cfi() CFI_UNCHECKED_CALLEE {}
6896+
6897+
void (*with_cfi)() = no_cfi; // warning: implicit conversion discards `cfi_unchecked_callee` attribute.
6898+
// `with_cfi` also points to the actual definition of `no_cfi` rather than
6899+
// its jump table entry.
6900+
6901+
void invoke(void (CFI_UNCHECKED_CALLEE *func)()) {
6902+
func(); // CFI will not instrument this indirect call.
6903+
6904+
void (*func2)() = func; // warning: implicit conversion discards `cfi_unchecked_callee` attribute.
6905+
6906+
func2(); // CFI will instrument this indirect call. Users should be careful however because if this
6907+
// references a function with type `cfi_unchecked_callee`, then the CFI check may incorrectly
6908+
// fail because the reference will be to the function definition rather than the CFI jump
6909+
// table entry.
6910+
}
6911+
6912+
This attribute can only be applied on functions or member functions. This attribute can be a good
6913+
alternative to ``no_sanitize("cfi")`` if you only want to disable innstrumentation for specific indirect
6914+
calls rather than applying ``no_sanitize("cfi")`` on the whole function containing indirect call. Note
6915+
that ``cfi_unchecked_attribute`` is a type attribute doesn't disable CFI instrumentation on a function
6916+
body.
6917+
}];
6918+
}
6919+
68726920
def ReinitializesDocs : Documentation {
68736921
let Category = DocCatFunction;
68746922
let Content = [{

clang/include/clang/Basic/DiagnosticGroups.td

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1577,6 +1577,8 @@ def FunctionMultiVersioning
15771577

15781578
def NoDeref : DiagGroup<"noderef">;
15791579

1580+
def CFIUncheckedCallee : DiagGroup<"cfi-unchecked-callee">;
1581+
15801582
// -fbounds-safety and bounds annotation related warnings
15811583
def BoundsSafetyCountedByEltTyUnknownSize :
15821584
DiagGroup<"bounds-safety-counted-by-elt-type-unknown-size">;

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12632,6 +12632,15 @@ def warn_noderef_on_non_pointer_or_array : Warning<
1263212632
def warn_noderef_to_dereferenceable_pointer : Warning<
1263312633
"casting to dereferenceable pointer removes 'noderef' attribute">, InGroup<NoDeref>;
1263412634

12635+
def warn_cfi_unchecked_callee_on_non_function
12636+
: Warning<"use of `cfi_unchecked_callee` on %0; can only be used on "
12637+
"function types">,
12638+
InGroup<CFIUncheckedCallee>;
12639+
def warn_cast_discards_cfi_unchecked_callee
12640+
: Warning<"implicit conversion from %0 to %1 discards "
12641+
"`cfi_unchecked_callee` attribute">,
12642+
InGroup<CFIUncheckedCallee>;
12643+
1263512644
def err_builtin_launder_invalid_arg : Error<
1263612645
"%select{non-pointer|function pointer|void pointer}0 argument to "
1263712646
"'__builtin_launder' is not allowed">;

clang/include/clang/CodeGen/CGFunctionInfo.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -602,6 +602,10 @@ class CGFunctionInfo final
602602
LLVM_PREFERRED_TYPE(bool)
603603
unsigned CmseNSCall : 1;
604604

605+
/// Whether this function is a CFI unchecked callee
606+
LLVM_PREFERRED_TYPE(bool)
607+
unsigned CFIUncheckedCallee : 1;
608+
605609
/// Whether this function is noreturn.
606610
LLVM_PREFERRED_TYPE(bool)
607611
unsigned NoReturn : 1;
@@ -704,6 +708,8 @@ class CGFunctionInfo final
704708

705709
bool isNoReturn() const { return NoReturn; }
706710

711+
bool isCFIUncheckedCallee() const { return CFIUncheckedCallee; }
712+
707713
/// In ARC, whether this function retains its return value. This
708714
/// is not always reliable for call sites.
709715
bool isReturnsRetained() const { return ReturnsRetained; }
@@ -740,7 +746,7 @@ class CGFunctionInfo final
740746
return FunctionType::ExtInfo(isNoReturn(), getHasRegParm(), getRegParm(),
741747
getASTCallingConvention(), isReturnsRetained(),
742748
isNoCallerSavedRegs(), isNoCfCheck(),
743-
isCmseNSCall());
749+
isCmseNSCall(), isCFIUncheckedCallee());
744750
}
745751

746752
CanQualType getReturnType() const { return getArgsBuffer()[0].type; }
@@ -794,6 +800,7 @@ class CGFunctionInfo final
794800
ID.AddInteger(RegParm);
795801
ID.AddBoolean(NoCfCheck);
796802
ID.AddBoolean(CmseNSCall);
803+
ID.AddBoolean(CFIUncheckedCallee);
797804
ID.AddInteger(Required.getOpaqueData());
798805
ID.AddBoolean(HasExtParameterInfos);
799806
if (HasExtParameterInfos) {
@@ -821,6 +828,7 @@ class CGFunctionInfo final
821828
ID.AddInteger(info.getRegParm());
822829
ID.AddBoolean(info.getNoCfCheck());
823830
ID.AddBoolean(info.getCmseNSCall());
831+
ID.AddBoolean(info.getCFIUncheckedCallee());
824832
ID.AddInteger(required.getOpaqueData());
825833
ID.AddBoolean(!paramInfos.empty());
826834
if (!paramInfos.empty()) {

clang/include/clang/Sema/Sema.h

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2645,6 +2645,16 @@ class Sema final : public SemaBase {
26452645
/// void*).
26462646
void DiscardMisalignedMemberAddress(const Type *T, Expr *E);
26472647

2648+
/// Returns true if `From` is a function or pointer to a function with the
2649+
/// `cfi_unchecked_callee` attribute but `To` is a function or pointer to
2650+
/// function without this attribute.
2651+
bool DiscardingCFIUncheckedCallee(QualType From, QualType To) const;
2652+
2653+
/// Returns true if `From` is a function or pointer to a function without the
2654+
/// `cfi_unchecked_callee` attribute but `To` is a function or pointer to
2655+
/// function with this attribute.
2656+
bool AddingCFIUncheckedCallee(QualType From, QualType To) const;
2657+
26482658
/// This function calls Action when it determines that E designates a
26492659
/// misaligned member due to the packed attribute. This is used to emit
26502660
/// local diagnostics like in reference binding.
@@ -10238,9 +10248,15 @@ class Sema final : public SemaBase {
1023810248
bool CStyle, bool &ObjCLifetimeConversion);
1023910249

1024010250
/// Determine whether the conversion from FromType to ToType is a valid
10241-
/// conversion that strips "noexcept" or "noreturn" off the nested function
10242-
/// type.
10243-
bool IsFunctionConversion(QualType FromType, QualType ToType) const;
10251+
/// conversion that strips "noexcept" or "noreturn" or "cfi_unchecked_callee"
10252+
/// off the nested function type. This also checks if "cfi_unchecked_callee"
10253+
/// was added to the function type. If "cfi_unchecked_callee" is added and
10254+
/// `AddingCFIUncheckedCallee` is provided, it will be set to true. The same
10255+
/// thing applies for `DiscardingCFIUncheckedCallee` if the attribute is
10256+
/// discarded.
10257+
bool IsFunctionConversion(QualType FromType, QualType ToType,
10258+
bool *DiscardingCFIUncheckedCallee = nullptr,
10259+
bool *AddingCFIUncheckedCallee = nullptr) const;
1024410260

1024510261
/// Same as `IsFunctionConversion`, but if this would return true, it sets
1024610262
/// `ResultTy` to `ToType`.

clang/lib/AST/TypePrinter.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1158,6 +1158,8 @@ void TypePrinter::printFunctionAfter(const FunctionType::ExtInfo &Info,
11581158

11591159
if (Info.getNoReturn())
11601160
OS << " __attribute__((noreturn))";
1161+
if (Info.getCFIUncheckedCallee())
1162+
OS << " __attribute__((cfi_unchecked_callee))";
11611163
if (Info.getCmseNSCall())
11621164
OS << " __attribute__((cmse_nonsecure_call))";
11631165
if (Info.getProducesResult())
@@ -2089,6 +2091,9 @@ void TypePrinter::printAttributedAfter(const AttributedType *T,
20892091
case attr::NoDeref:
20902092
OS << "noderef";
20912093
break;
2094+
case attr::CFIUncheckedCallee:
2095+
OS << "cfi_unchecked_callee";
2096+
break;
20922097
case attr::AcquireHandle:
20932098
OS << "acquire_handle";
20942099
break;

clang/lib/CodeGen/CGExpr.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3006,6 +3006,10 @@ static LValue EmitFunctionDeclLValue(CodeGenFunction &CGF, const Expr *E,
30063006
GlobalDecl GD) {
30073007
const FunctionDecl *FD = cast<FunctionDecl>(GD.getDecl());
30083008
llvm::Constant *V = CGF.CGM.getFunctionPointer(GD);
3009+
if (E->getType()->isCFIUncheckedCalleeFunctionType()) {
3010+
if (auto *GV = dyn_cast<llvm::GlobalValue>(V))
3011+
V = llvm::NoCFIValue::get(GV);
3012+
}
30093013
CharUnits Alignment = CGF.getContext().getDeclAlign(FD);
30103014
return CGF.MakeAddrLValue(V, E->getType(), Alignment,
30113015
AlignmentSource::Decl);
@@ -6240,10 +6244,12 @@ RValue CodeGenFunction::EmitCall(QualType CalleeType,
62406244
FD && FD->hasAttr<OpenCLKernelAttr>())
62416245
CGM.getTargetCodeGenInfo().setOCLKernelStubCallingConvention(FnType);
62426246

6247+
bool CFIUnchecked = CalleeType->isPointerToCFIUncheckedCalleeFunctionType();
6248+
62436249
// If we are checking indirect calls and this call is indirect, check that the
62446250
// function pointer is a member of the bit set for the function type.
62456251
if (SanOpts.has(SanitizerKind::CFIICall) &&
6246-
(!TargetDecl || !isa<FunctionDecl>(TargetDecl))) {
6252+
(!TargetDecl || !isa<FunctionDecl>(TargetDecl)) && !CFIUnchecked) {
62476253
SanitizerScope SanScope(this);
62486254
EmitSanitizerStatReport(llvm::SanStat_CFI_ICall);
62496255

clang/lib/CodeGen/CGExprConstant.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2236,8 +2236,12 @@ ConstantLValueEmitter::tryEmitBase(const APValue::LValueBase &base) {
22362236
return ConstantLValue(C);
22372237
};
22382238

2239-
if (const auto *FD = dyn_cast<FunctionDecl>(D))
2240-
return PtrAuthSign(CGM.getRawFunctionPointer(FD));
2239+
if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
2240+
llvm::Constant *C = CGM.getRawFunctionPointer(FD);
2241+
if (FD->getType()->isCFIUncheckedCalleeFunctionType())
2242+
C = llvm::NoCFIValue::get(cast<llvm::GlobalValue>(C));
2243+
return PtrAuthSign(C);
2244+
}
22412245

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

clang/lib/CodeGen/CGPointerAuth.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,11 @@ llvm::Constant *CodeGenModule::getMemberFunctionPointer(llvm::Constant *Pointer,
517517
Pointer, PointerAuth.getKey(), nullptr,
518518
cast_or_null<llvm::ConstantInt>(PointerAuth.getDiscriminator()));
519519

520+
if (const auto *MFT = dyn_cast<MemberPointerType>(FT.getTypePtr())) {
521+
if (MFT->isPointerToCFIUncheckedCalleeFunctionType())
522+
Pointer = llvm::NoCFIValue::get(cast<llvm::GlobalValue>(Pointer));
523+
}
524+
520525
return Pointer;
521526
}
522527

clang/lib/CodeGen/ItaniumCXXABI.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -693,6 +693,18 @@ CGCallee ItaniumCXXABI::EmitLoadOfMemberFunctionPointer(
693693
llvm::Constant *CheckTypeDesc;
694694
bool ShouldEmitCFICheck = CGF.SanOpts.has(SanitizerKind::CFIMFCall) &&
695695
CGM.HasHiddenLTOVisibility(RD);
696+
697+
if (ShouldEmitCFICheck) {
698+
if (const auto *BinOp = dyn_cast<BinaryOperator>(E)) {
699+
if (BinOp->isPtrMemOp()) {
700+
if (BinOp->getRHS()
701+
->getType()
702+
->isPointerToCFIUncheckedCalleeFunctionType())
703+
ShouldEmitCFICheck = false;
704+
}
705+
}
706+
}
707+
696708
bool ShouldEmitVFEInfo = CGM.getCodeGenOpts().VirtualFunctionElimination &&
697709
CGM.HasHiddenLTOVisibility(RD);
698710
bool ShouldEmitWPDInfo =

0 commit comments

Comments
 (0)