diff --git a/clang/docs/ControlFlowIntegrity.rst b/clang/docs/ControlFlowIntegrity.rst --- a/clang/docs/ControlFlowIntegrity.rst +++ b/clang/docs/ControlFlowIntegrity.rst @@ -314,10 +314,8 @@ is a security hardening mechanism designed to be deployed in release builds. ``-fsanitize=function`` has a higher space and time overhead due to a more -complex type check at indirect call sites, as well as a need for run-time -type information (RTTI), which may make it unsuitable for deployment. Because -of the need for RTTI, ``-fsanitize=function`` can only be used with C++ -programs, whereas ``-fsanitize=cfi-icall`` can protect both C and C++ programs. +complex type check at indirect call sites, which may make it unsuitable for +deployment. On the other hand, ``-fsanitize=function`` conforms more closely with the C++ standard and user expectations around interaction with shared libraries; diff --git a/clang/docs/UndefinedBehaviorSanitizer.rst b/clang/docs/UndefinedBehaviorSanitizer.rst --- a/clang/docs/UndefinedBehaviorSanitizer.rst +++ b/clang/docs/UndefinedBehaviorSanitizer.rst @@ -100,7 +100,7 @@ by Clang (and by ISO/IEC/IEEE 60559 / IEEE 754) as producing either an infinity or NaN value, so is not included in ``-fsanitize=undefined``. - ``-fsanitize=function``: Indirect call of a function through a - function pointer of the wrong type (C++ only). + function pointer of the wrong type. - ``-fsanitize=implicit-unsigned-integer-truncation``, ``-fsanitize=implicit-signed-integer-truncation``: Implicit conversion from integer of larger bit width to smaller bit width, if that results in data diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -5349,8 +5349,9 @@ CGCallee Callee = OrigCallee; - if (getLangOpts().CPlusPlus && SanOpts.has(SanitizerKind::Function) && - (!TargetDecl || !isa(TargetDecl))) { + if (SanOpts.has(SanitizerKind::Function) && + (!TargetDecl || !isa(TargetDecl)) && + !isa(PointeeType)) { if (llvm::Constant *PrefixSig = CGM.getTargetCodeGenInfo().getUBSanFunctionSignature(CGM)) { SanitizerScope SanScope(this); diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -572,10 +572,11 @@ CodeGenFunction::getUBSanFunctionTypeHash(QualType Ty) const { // Remove any (C++17) exception specifications, to allow calling e.g. a // noexcept function through a non-noexcept pointer. - auto ProtoTy = getContext().getFunctionTypeWithExceptionSpec(Ty, EST_None); + if (!isa(Ty)) + Ty = getContext().getFunctionTypeWithExceptionSpec(Ty, EST_None); std::string Mangled; llvm::raw_string_ostream Out(Mangled); - CGM.getCXXABI().getMangleContext().mangleTypeName(ProtoTy, Out, false); + CGM.getCXXABI().getMangleContext().mangleTypeName(Ty, Out, false); return llvm::ConstantInt::get(CGM.Int32Ty, static_cast(llvm::xxHash64(Mangled))); } @@ -945,7 +946,7 @@ // If we are checking function types, emit a function type signature as // prologue data. - if (FD && getLangOpts().CPlusPlus && SanOpts.has(SanitizerKind::Function)) { + if (FD && SanOpts.has(SanitizerKind::Function)) { if (llvm::Constant *PrologueSig = getPrologueSignature(CGM, FD)) { llvm::LLVMContext &Ctx = Fn->getContext(); llvm::MDBuilder MDB(Ctx); diff --git a/clang/test/CodeGen/ubsan-function.c b/clang/test/CodeGen/ubsan-function.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/ubsan-function.c @@ -0,0 +1,9 @@ +// RUN: %clang_cc1 -emit-llvm -triple x86_64 -std=c17 -fsanitize=function %s -o - | FileCheck %s + +// CHECK-LABEL: define{{.*}} @call_no_prototype( +// CHECK-NOT: __ubsan_handle_function_type_mismatch +void call_no_prototype(void (*f)()) { f(); } + +// CHECK-LABEL: define{{.*}} @call_prototype( +// CHECK: __ubsan_handle_function_type_mismatch +void call_prototype(void (*f)(void)) { f(); } diff --git a/compiler-rt/test/ubsan/TestCases/TypeCheck/Function/c.c b/compiler-rt/test/ubsan/TestCases/TypeCheck/Function/c.c new file mode 100644 --- /dev/null +++ b/compiler-rt/test/ubsan/TestCases/TypeCheck/Function/c.c @@ -0,0 +1,14 @@ +// RUN: %clang -g -fsanitize=function %s -o %t +// RUN: %run %t 2>&1 | FileCheck %s --check-prefix=CHECK --implicit-check-not='runtime error:' + +void f(void (*fp)(int (*)[])) { fp(0); } + +void callee0(int (*a)[]) {} +void callee1(int (*a)[1]) {} + +int main() { + int a[1]; + f(callee0); + // CHECK: runtime error: call to function callee1 through pointer to incorrect function type 'void (*)(int (*)[])' + f(callee1); // compatible type in C, but flagged +} diff --git a/compiler-rt/test/ubsan/TestCases/TypeCheck/Function/function.cpp b/compiler-rt/test/ubsan/TestCases/TypeCheck/Function/function.cpp --- a/compiler-rt/test/ubsan/TestCases/TypeCheck/Function/function.cpp +++ b/compiler-rt/test/ubsan/TestCases/TypeCheck/Function/function.cpp @@ -1,6 +1,3 @@ -// Work around "Cannot represent a difference across sections" -// UNSUPPORTED: target=powerpc64-{{.*}} - // RUN: %clangxx -DDETERMINE_UNIQUE %s -o %t-unique // RUN: %clangxx -std=c++17 -fsanitize=function %s -O3 -g -DSHARED_LIB -fPIC -shared -o %t-so.so // RUN: %clangxx -std=c++17 -fsanitize=function %s -O3 -g -o %t %t-so.so diff --git a/compiler-rt/test/ubsan/TestCases/TypeCheck/Function/lit.local.cfg.py b/compiler-rt/test/ubsan/TestCases/TypeCheck/Function/lit.local.cfg.py --- a/compiler-rt/test/ubsan/TestCases/TypeCheck/Function/lit.local.cfg.py +++ b/compiler-rt/test/ubsan/TestCases/TypeCheck/Function/lit.local.cfg.py @@ -1,5 +1,8 @@ if config.host_os not in ['Darwin', 'FreeBSD', 'Linux', 'NetBSD']: config.unsupported = True +# Work around "Cannot represent a difference across sections" +if config.target_arch == 'powerpc64': + config.unsupported = True # Work around "library ... not found: needed by main executable" in qemu. if config.android and config.target_arch not in ['x86', 'x86_64']: config.unsupported = True