MaggieYi created this revision.
MaggieYi added reviewers: MaskRay, peter.smith, vitalybuka, probinson, 
pgousseau, glandium, uabelho.
Herald added a project: All.
MaggieYi requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

PR for https://github.com/llvm/llvm-project/issues/64931.

An execute-only target disallows data access to code sections. When enabling 
the function sanitizer (-fsanitize=function), UBSan function signatures and 
type hashes are emitted within the function's prologue data to enable checking 
of the function type. This results in a non-execute access to the code section 
and a runtime error.

To solve the issue, -fsanitize=function should not be included in any check 
group (e.g. undefined) on an execute-only target. If a user passes 
-fsanitize=undefined, there is no error and no warning. However, if the user 
explicitly passes -fsanitize=function on an execute-only target, an error will 
be emitted.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D158614

Files:
  clang/include/clang/Basic/DiagnosticCommonKinds.td
  clang/include/clang/Basic/Sanitizers.h
  clang/lib/Basic/CMakeLists.txt
  clang/lib/Basic/Sanitizers.cpp
  clang/lib/Driver/SanitizerArgs.cpp
  clang/lib/Frontend/CompilerInvocation.cpp
  clang/test/CodeGen/ubsan-function.c
  clang/test/CodeGenObjCXX/crash-function-type.mm
  clang/test/Driver/fsanitize.c

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')}}
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/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/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/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/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 <algorithm>
 
 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<std::string> 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/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/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/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'">;
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to