Index: clang/include/clang/Basic/DiagnosticCommonKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticCommonKinds.td +++ clang/include/clang/Basic/DiagnosticCommonKinds.td @@ -323,6 +323,8 @@ def err_target_unsupported_abi_for_triple : Error< "ABI '%0' is not supported for '%1'">; def err_unsupported_abi_for_opt : Error<"'%0' can only be used with the '%1' ABI">; +def err_unsupported_opt_for_execute_only_target + : Error<"unsupported option '%0' for the execute only target '%1'">; def err_mips_fp64_req : Error< "'%0' can only be used if the target supports the mfhc1 and mthc1 instructions">; def err_target_unknown_fpmath : Error<"unknown FP unit '%0'">; Index: clang/include/clang/Basic/Sanitizers.h =================================================================== --- clang/include/clang/Basic/Sanitizers.h +++ clang/include/clang/Basic/Sanitizers.h @@ -23,7 +23,11 @@ namespace llvm { class hash_code; +class Triple; +namespace opt { +class ArgList; } +} // namespace llvm namespace clang { @@ -205,6 +209,11 @@ llvm::AsanDetectStackUseAfterReturnMode AsanDetectStackUseAfterReturnModeFromString(StringRef modeStr); +/// Return true if an execute-only target disallows data access to code +/// sections. +bool isExecuteOnlyTarget(const llvm::Triple &Triple, + const llvm::opt::ArgList &Args); + } // namespace clang #endif // LLVM_CLANG_BASIC_SANITIZERS_H Index: clang/lib/Basic/CMakeLists.txt =================================================================== --- clang/lib/Basic/CMakeLists.txt +++ clang/lib/Basic/CMakeLists.txt @@ -1,4 +1,5 @@ set(LLVM_LINK_COMPONENTS + Option Support TargetParser ) Index: clang/lib/Basic/Sanitizers.cpp =================================================================== --- clang/lib/Basic/Sanitizers.cpp +++ clang/lib/Basic/Sanitizers.cpp @@ -11,10 +11,15 @@ //===----------------------------------------------------------------------===// #include "clang/Basic/Sanitizers.h" +#include "clang/Driver/Options.h" #include "llvm/ADT/Hashing.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringSwitch.h" +#include "llvm/Option/ArgList.h" #include "llvm/Support/MathExtras.h" +#include "llvm/TargetParser/ARMTargetParser.h" +#include "llvm/TargetParser/Triple.h" +#include using namespace clang; @@ -112,4 +117,34 @@ .Default(llvm::AsanDetectStackUseAfterReturnMode::Invalid); } +bool isExecuteOnlyTarget(const llvm::Triple &Triple, + const llvm::opt::ArgList &Args) { + if (Triple.isPS5()) + return true; + // On Arm, the clang `-mexecute-only` option is used to generate the + // execute-only output (no data access to code sections). + const llvm::opt::Arg *A = + Args.getLastArg(clang::driver::options::OPT_mexecute_only, + clang::driver::options::OPT_mno_execute_only); + // On Arm, `-target-feature +execute-only` is used to generate the + // execute-only output from the `clang_cc1` command. + const std::vector Features = + Args.getAllArgValues(clang::driver::options::OPT_target_feature); + + if ((A && + A->getOption().matches(clang::driver::options::OPT_mexecute_only)) || + (std::find(Features.begin(), Features.end(), "+execute-only") != + Features.end())) { + // The execute-only output is supported only on ARMv6T2 and ARMv7 and above. + if (llvm::ARM::parseArchVersion(Triple.getArchName()) > 7 || + llvm::ARM::parseArch(Triple.getArchName()) == + llvm::ARM::ArchKind::ARMV6T2 || + llvm::ARM::parseArch(Triple.getArchName()) == + llvm::ARM::ArchKind::ARMV6M) + return true; + } + + return false; +} + } // namespace clang Index: clang/lib/Driver/SanitizerArgs.cpp =================================================================== --- clang/lib/Driver/SanitizerArgs.cpp +++ clang/lib/Driver/SanitizerArgs.cpp @@ -37,6 +37,7 @@ SanitizerKind::Vptr | SanitizerKind::CFI; static const SanitizerMask NotAllowedWithTrap = SanitizerKind::Vptr; static const SanitizerMask NotAllowedWithMinimalRuntime = SanitizerKind::Vptr; +static const SanitizerMask NotAllowedWithExecuteOnly = SanitizerKind::Function; static const SanitizerMask RequiresPIE = SanitizerKind::DataFlow | SanitizerKind::Scudo; static const SanitizerMask NeedsUnwindTables = @@ -395,6 +396,21 @@ DiagnosedKinds |= SanitizerKind::Function; } } + // When enabling the function sanitizer (-fsanitize=function), UBSan + // function signatures and type hashes are emitted within the function's + // prologue data to check the function type. Therefore, an execute-only + // target doesn't support the function sanitizer. + const llvm::Triple &Triple = TC.getTriple(); + if (isExecuteOnlyTarget(Triple, Args)) { + if (SanitizerMask KindsToDiagnose = + Add & NotAllowedWithExecuteOnly & ~DiagnosedKinds) { + if (DiagnoseErrors) + D.Diag(diag::err_unsupported_opt_for_execute_only_target) + << "-fsanitize=function" << Triple.str(); + DiagnosedKinds |= KindsToDiagnose; + } + Add &= ~NotAllowedWithExecuteOnly; + } // FIXME: Make CFI on member function calls compatible with cross-DSO CFI. // There are currently two problems: @@ -457,6 +473,11 @@ if (MinimalRuntime) { Add &= ~NotAllowedWithMinimalRuntime; } + // `-fsanitize=function` is silently discarded on an execute-only target + // if implicitly enabled through group expansion. + if (isExecuteOnlyTarget(Triple, Args)) { + Add &= ~NotAllowedWithExecuteOnly; + } if (CfiCrossDso) Add &= ~SanitizerKind::CFIMFCall; Add &= Supported; Index: clang/lib/Frontend/CompilerInvocation.cpp =================================================================== --- clang/lib/Frontend/CompilerInvocation.cpp +++ clang/lib/Frontend/CompilerInvocation.cpp @@ -4398,6 +4398,16 @@ ParseLangArgs(LangOpts, Args, DashX, T, Res.getPreprocessorOpts().Includes, Diags); + + // An execute-only target doesn't support the function sanitizer. Since `clang + // -cc1` doesn't allow group check (e.g. 'undefined') in '-fsanitize=', the + // value of '-fsanitize=' must be `function` if function sanitizer is enabled. + if (isExecuteOnlyTarget(T, Args) && + LangOpts.Sanitize.has(SanitizerKind::Function)) { + Diags.Report(diag::err_unsupported_opt_for_execute_only_target) + << "-fsanitize=function" << T.getTriple(); + } + if (Res.getFrontendOpts().ProgramAction == frontend::RewriteObjC) LangOpts.ObjCExceptions = 1; Index: clang/test/CodeGen/ubsan-function.c =================================================================== --- clang/test/CodeGen/ubsan-function.c +++ clang/test/CodeGen/ubsan-function.c @@ -1,4 +1,6 @@ // RUN: %clang_cc1 -emit-llvm -triple x86_64 -std=c17 -fsanitize=function %s -o - | FileCheck %s +// RUN: not %clang_cc1 -emit-llvm -triple x86_64-sie-ps5 -fsanitize=function %s -o 2>&1 | FileCheck %s --check-prefix=UBSAN-FUNCTION-ERR +// RUN: not %clang_cc1 -emit-llvm -triple armv6t2-unknown-unknown-eabi -target-feature +execute-only -fsanitize=function %s -o 2>&1 | FileCheck %s --check-prefix=UBSAN-FUNCTION-ERR // CHECK-LABEL: define{{.*}} @call_no_prototype( // CHECK-NOT: __ubsan_handle_function_type_mismatch @@ -7,3 +9,5 @@ // CHECK-LABEL: define{{.*}} @call_prototype( // CHECK: __ubsan_handle_function_type_mismatch void call_prototype(void (*f)(void)) { f(); } + +// UBSAN-FUNCTION-ERR: error: unsupported option '-fsanitize=function' for the execute only target {{('x86_64-sie-ps5'|'armv6t2-unknown-unknown-eabi')}} Index: clang/test/CodeGenObjCXX/crash-function-type.mm =================================================================== --- clang/test/CodeGenObjCXX/crash-function-type.mm +++ clang/test/CodeGenObjCXX/crash-function-type.mm @@ -1,3 +1,6 @@ +// Mark test as unsupported on PS5 due to PS5 doesn't support function sanitizer. +// UNSUPPORTED: target=x86_64-sie-ps5 + // RUN: %clang_cc1 -fblocks -fsanitize=function -emit-llvm %s -o %t void g(void (^)()); Index: clang/test/Driver/fsanitize.c =================================================================== --- clang/test/Driver/fsanitize.c +++ clang/test/Driver/fsanitize.c @@ -971,3 +971,14 @@ // RUN: not %clang --target=x86_64-linux-gnu -fsanitize=undefined,function -mcmodel=large %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UBSAN-FUNCTION-CODE-MODEL // CHECK-UBSAN-FUNCTION-CODE-MODEL: error: invalid argument '-fsanitize=function' only allowed with '-mcmodel=small' + +// RUN: not %clang --target=x86_64-sie-ps5 -fsanitize=function %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UBSAN-FUNCTION +// RUN: not %clang --target=x86_64-sie-ps5 -fsanitize=undefined,function %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UBSAN-FUNCTION +// RUN: %clang --target=x86_64-sie-ps5 -fsanitize=undefined %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UBSAN-UNDEFINED + +// RUN: not %clang --target=armv6t2-eabi -mexecute-only -fsanitize=function %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UBSAN-FUNCTION +// RUN: not %clang --target=armv6t2-eabi -mexecute-only -fsanitize=undefined,function %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UBSAN-FUNCTION +// RUN: %clang --target=armv6t2-eabi -mexecute-only -fsanitize=undefined %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UBSAN-UNDEFINED + +// CHECK-UBSAN-FUNCTION: error: unsupported option '-fsanitize=function' for the execute only target {{('x86_64-sie-ps5'|'armv6t2-unknown-unknown-eabi')}} +// CHECK-UBSAN-UNDEFINED-NOT: error: unsupported option '-fsanitize=function' for the execute only target {{('x86_64-sie-ps5'|'armv6t2-unknown-unknown-eabi')}}