Skip to content

Commit

Permalink
[UBSan] Disable the function and kcfi sanitizers on an execute-only t…
Browse files Browse the repository at this point in the history
…arget.

An execute-only target disallows data access to code sections.
-fsanitize=function and -fsanitize=kcfi instrument indirect function
calls to load a type hash before the function label. 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 or -fsanitize=kcfi on an
execute-only target, an error will be emitted.

Fixes: llvm/llvm-project#64931.

Reviewed By: MaskRay, probinson, simon_tatham

Differential Revision: https://reviews.llvm.org/D158614

(cherry picked from commit 9ef536a12ea65a2b9e2511936327c7b621af38fb)
  • Loading branch information
MaggieYingYi authored and llvmbot committed Aug 31, 2023
1 parent 45c677d commit fba14e4
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 0 deletions.
9 changes: 9 additions & 0 deletions clang/include/clang/Basic/Sanitizers.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,11 @@

namespace llvm {
class hash_code;
class Triple;
namespace opt {
class ArgList;
}
} // namespace llvm

namespace clang {

Expand Down Expand Up @@ -205,6 +209,11 @@ StringRef AsanDetectStackUseAfterReturnModeToString(
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
1 change: 1 addition & 0 deletions clang/lib/Basic/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
set(LLVM_LINK_COMPONENTS
Option
Support
TargetParser
)
Expand Down
13 changes: 13 additions & 0 deletions clang/lib/Basic/Sanitizers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,13 @@
//===----------------------------------------------------------------------===//

#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/Triple.h"

using namespace clang;

Expand Down Expand Up @@ -112,4 +115,14 @@ AsanDetectStackUseAfterReturnModeFromString(StringRef modeStr) {
.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).
return Args.hasFlag(clang::driver::options::OPT_mexecute_only,
clang::driver::options::OPT_mno_execute_only, false);
}
} // namespace clang
22 changes: 22 additions & 0 deletions clang/lib/Driver/SanitizerArgs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ static const SanitizerMask NeedsUbsanCxxRt =
SanitizerKind::Vptr | SanitizerKind::CFI;
static const SanitizerMask NotAllowedWithTrap = SanitizerKind::Vptr;
static const SanitizerMask NotAllowedWithMinimalRuntime = SanitizerKind::Vptr;
static const SanitizerMask NotAllowedWithExecuteOnly =
SanitizerKind::Function | SanitizerKind::KCFI;
static const SanitizerMask RequiresPIE =
SanitizerKind::DataFlow | SanitizerKind::Scudo;
static const SanitizerMask NeedsUnwindTables =
Expand Down Expand Up @@ -395,6 +397,22 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
DiagnosedKinds |= SanitizerKind::Function;
}
}
// -fsanitize=function and -fsanitize=kcfi instrument indirect function
// calls to load a type hash before the function label. Therefore, an
// execute-only target doesn't support the function and kcfi sanitizers.
const llvm::Triple &Triple = TC.getTriple();
if (isExecuteOnlyTarget(Triple, Args)) {
if (SanitizerMask KindsToDiagnose =
Add & NotAllowedWithExecuteOnly & ~DiagnosedKinds) {
if (DiagnoseErrors) {
std::string Desc = describeSanitizeArg(Arg, KindsToDiagnose);
D.Diag(diag::err_drv_argument_not_allowed_with)
<< Desc << Triple.str();
}
DiagnosedKinds |= KindsToDiagnose;
}
Add &= ~NotAllowedWithExecuteOnly;
}

// FIXME: Make CFI on member function calls compatible with cross-DSO CFI.
// There are currently two problems:
Expand Down Expand Up @@ -457,6 +475,10 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
if (MinimalRuntime) {
Add &= ~NotAllowedWithMinimalRuntime;
}
// NotAllowedWithExecuteOnly 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;
Expand Down
3 changes: 3 additions & 0 deletions clang/test/CodeGenObjCXX/crash-function-type.mm
Original file line number Diff line number Diff line change
@@ -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 (^)());
Expand Down
14 changes: 14 additions & 0 deletions clang/test/Driver/fsanitize.c
Original file line number Diff line number Diff line change
Expand Up @@ -971,3 +971,17 @@

// RUN: %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 -fsanitize=function %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UBSAN-FUNCTION
// RUN: not %clang --target=x86_64-sie-ps5 -fsanitize=kcfi %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UBSAN-KCFI
// RUN: not %clang --target=x86_64-sie-ps5 -fsanitize=function -fsanitize=kcfi %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UBSAN-KCFI --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=kcfi %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UBSAN-KCFI
// RUN: %clang --target=armv6t2-eabi -mexecute-only -fsanitize=undefined %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UBSAN-UNDEFINED

// CHECK-UBSAN-KCFI-DAG: error: invalid argument '-fsanitize=kcfi' not allowed with {{('x86_64-sie-ps5'|'armv6t2-unknown-unknown-eabi')}}
// CHECK-UBSAN-FUNCTION-DAG: error: invalid argument '-fsanitize=function' not allowed with {{('x86_64-sie-ps5'|'armv6t2-unknown-unknown-eabi')}}
// CHECK-UBSAN-UNDEFINED: "-fsanitize={{((alignment|array-bounds|bool|builtin|enum|float-cast-overflow|integer-divide-by-zero|nonnull-attribute|null|pointer-overflow|return|returns-nonnull-attribute|shift-base|shift-exponent|signed-integer-overflow|unreachable|vla-bound),?){17}"}}

0 comments on commit fba14e4

Please sign in to comment.