diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -2440,6 +2440,13 @@ return __builtin_addressof(value); } +``__builtin_addressof_nocfi`` +----------------------------- + +``__builtin_addressof_nocfi`` is similar to ``__builtin_addressof``, but +with ``-fsanitize=cfi``, when passed a function, it returns the address of +the function body instead of a pointer to the CFI jump table. + ``__builtin_operator_new`` and ``__builtin_operator_delete`` ------------------------------------------------------------ diff --git a/clang/include/clang/AST/APValue.h b/clang/include/clang/AST/APValue.h --- a/clang/include/clang/AST/APValue.h +++ b/clang/include/clang/AST/APValue.h @@ -176,6 +176,8 @@ unsigned getVersion() const; QualType getTypeInfoType() const; QualType getDynamicAllocType() const; + bool isNoCFIValue() const; + void setNoCFIValue(bool NoCFI); QualType getType() const; @@ -190,6 +192,7 @@ PtrTy Ptr; struct LocalState { unsigned CallIndex, Version; + bool NoCFIValue : 1; }; union { LocalState Local; diff --git a/clang/include/clang/Basic/Builtins.def b/clang/include/clang/Basic/Builtins.def --- a/clang/include/clang/Basic/Builtins.def +++ b/clang/include/clang/Basic/Builtins.def @@ -1556,6 +1556,7 @@ // Clang builtins (not available in GCC). BUILTIN(__builtin_addressof, "v*v&", "nct") +BUILTIN(__builtin_addressof_nocfi, "v*v&", "nct") BUILTIN(__builtin_operator_new, "v*z", "tc") BUILTIN(__builtin_operator_delete, "vv*", "tn") BUILTIN(__builtin_char_memchr, "c*cC*iz", "n") diff --git a/clang/lib/AST/APValue.cpp b/clang/lib/AST/APValue.cpp --- a/clang/lib/AST/APValue.cpp +++ b/clang/lib/AST/APValue.cpp @@ -40,9 +40,10 @@ "Type is insufficiently aligned"); APValue::LValueBase::LValueBase(const ValueDecl *P, unsigned I, unsigned V) - : Ptr(P ? cast(P->getCanonicalDecl()) : nullptr), Local{I, V} {} + : Ptr(P ? cast(P->getCanonicalDecl()) : nullptr), Local{I, V, + false} {} APValue::LValueBase::LValueBase(const Expr *P, unsigned I, unsigned V) - : Ptr(P), Local{I, V} {} + : Ptr(P), Local{I, V, false} {} APValue::LValueBase APValue::LValueBase::getDynamicAlloc(DynamicAllocLValue LV, QualType Type) { @@ -124,6 +125,16 @@ return QualType::getFromOpaquePtr(DynamicAllocType); } +bool APValue::LValueBase::isNoCFIValue() const { + return (is() ? Local.NoCFIValue : false); +} + +void APValue::LValueBase::setNoCFIValue(bool NoCFI) { + if (is()) { + Local.NoCFIValue = NoCFI; + } +} + void APValue::LValueBase::Profile(llvm::FoldingSetNodeID &ID) const { ID.AddPointer(Ptr.getOpaqueValue()); if (is() || is()) diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -8980,6 +8980,11 @@ switch (BuiltinOp) { case Builtin::BI__builtin_addressof: return evaluateLValue(E->getArg(0), Result); + case Builtin::BI__builtin_addressof_nocfi: + if (!evaluateLValue(E->getArg(0), Result)) + return false; + Result.Base.setNoCFIValue(true); + return true; case Builtin::BI__builtin_assume_aligned: { // We need to be very careful here because: if the pointer does not have the // asserted alignment, then the behavior is undefined, and undefined diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -4375,6 +4375,9 @@ } case Builtin::BI__builtin_addressof: return RValue::get(EmitLValue(E->getArg(0)).getPointer(*this)); + case Builtin::BI__builtin_addressof_nocfi: + return RValue::get(llvm::NoCFIValue::get( + cast(EmitLValue(E->getArg(0)).getPointer(*this)))); case Builtin::BI__builtin_operator_new: return EmitBuiltinNewDeleteCall( E->getCallee()->getType()->castAs(), E, false); diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp --- a/clang/lib/CodeGen/CGExprConstant.cpp +++ b/clang/lib/CodeGen/CGExprConstant.cpp @@ -1887,8 +1887,12 @@ if (D->hasAttr()) return CGM.GetWeakRefReference(D).getPointer(); - if (auto FD = dyn_cast(D)) - return CGM.GetAddrOfFunction(FD); + if (const auto *FD = dyn_cast(D)) { + llvm::Constant *C = CGM.GetAddrOfFunction(FD); + if (base.isNoCFIValue()) + return llvm::NoCFIValue::get(cast(C)); + return C; + } if (auto VD = dyn_cast(D)) { // We can never refer to a variable with local storage. diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -1749,6 +1749,7 @@ return ExprError(); break; case Builtin::BI__builtin_addressof: + case Builtin::BI__builtin_addressof_nocfi: if (SemaBuiltinAddressof(*this, TheCall)) return ExprError(); break; diff --git a/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp --- a/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp @@ -66,7 +66,8 @@ case Builtin::BI__builtin_expect: case Builtin::BI__builtin_expect_with_probability: case Builtin::BI__builtin_assume_aligned: - case Builtin::BI__builtin_addressof: { + case Builtin::BI__builtin_addressof: + case Builtin::BI__builtin_addressof_nocfi: { // For __builtin_unpredictable, __builtin_expect, // __builtin_expect_with_probability and __builtin_assume_aligned, // just return the value of the subexpression. diff --git a/clang/test/CodeGen/builtin-addressof-nocfi.c b/clang/test/CodeGen/builtin-addressof-nocfi.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/builtin-addressof-nocfi.c @@ -0,0 +1,22 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -fsanitize=cfi-icall -o - %s | FileCheck %s + +void a(void) {} +void b(void) {} +void c(void (*p)(void)) { + p(); +} + +// CHECK: @e = constant void ()* no_cfi @a +void (*const e)(void) = __builtin_addressof_nocfi(a); +// CHECK: @f = global [2 x void ()*] [void ()* @b, void ()* no_cfi @b] +void (*f[])(void) = {b, __builtin_addressof_nocfi(b)}; + +void d(void) { + // CHECK: store void ()* no_cfi @b, void ()** %g + void (*g)(void) = __builtin_addressof_nocfi(b); + // CHECK: call void @c(void ()* no_cfi @a) + c(__builtin_addressof_nocfi(a)); + e(); + f[1](); + g(); +} diff --git a/clang/test/SemaCXX/builtins.cpp b/clang/test/SemaCXX/builtins.cpp --- a/clang/test/SemaCXX/builtins.cpp +++ b/clang/test/SemaCXX/builtins.cpp @@ -39,6 +39,14 @@ S *ptmp = __builtin_addressof(S{}); // expected-error {{taking the address of a temporary}} } +namespace addressof_nocfi { + void a(void) {} + static_assert(__builtin_addressof_nocfi(a) == a, ""); + + struct S {} s; + static_assert(__builtin_addressof_nocfi(s) == &s, ""); +} + void no_ms_builtins() { __assume(1); // expected-error {{use of undeclared}} __noop(1); // expected-error {{use of undeclared}}