Skip to content

Commit 9ef536a

Browse files
committed
[UBSan] Disable the function and kcfi sanitizers on an execute-only target.
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: #64931. Reviewed By: MaskRay, probinson, simon_tatham Differential Revision: https://reviews.llvm.org/D158614
1 parent aada8f2 commit 9ef536a

File tree

6 files changed

+62
-0
lines changed

6 files changed

+62
-0
lines changed

clang/include/clang/Basic/Sanitizers.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,11 @@
2323

2424
namespace llvm {
2525
class hash_code;
26+
class Triple;
27+
namespace opt {
28+
class ArgList;
2629
}
30+
} // namespace llvm
2731

2832
namespace clang {
2933

@@ -205,6 +209,11 @@ StringRef AsanDetectStackUseAfterReturnModeToString(
205209
llvm::AsanDetectStackUseAfterReturnMode
206210
AsanDetectStackUseAfterReturnModeFromString(StringRef modeStr);
207211

212+
/// Return true if an execute-only target disallows data access to code
213+
/// sections.
214+
bool isExecuteOnlyTarget(const llvm::Triple &Triple,
215+
const llvm::opt::ArgList &Args);
216+
208217
} // namespace clang
209218

210219
#endif // LLVM_CLANG_BASIC_SANITIZERS_H

clang/lib/Basic/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
set(LLVM_LINK_COMPONENTS
2+
Option
23
Support
34
TargetParser
45
)

clang/lib/Basic/Sanitizers.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,13 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
#include "clang/Basic/Sanitizers.h"
14+
#include "clang/Driver/Options.h"
1415
#include "llvm/ADT/Hashing.h"
1516
#include "llvm/ADT/SmallVector.h"
1617
#include "llvm/ADT/StringSwitch.h"
18+
#include "llvm/Option/ArgList.h"
1719
#include "llvm/Support/MathExtras.h"
20+
#include "llvm/TargetParser/Triple.h"
1821

1922
using namespace clang;
2023

@@ -112,4 +115,14 @@ AsanDetectStackUseAfterReturnModeFromString(StringRef modeStr) {
112115
.Default(llvm::AsanDetectStackUseAfterReturnMode::Invalid);
113116
}
114117

118+
bool isExecuteOnlyTarget(const llvm::Triple &Triple,
119+
const llvm::opt::ArgList &Args) {
120+
if (Triple.isPS5())
121+
return true;
122+
123+
// On Arm, the clang `-mexecute-only` option is used to generate the
124+
// execute-only output (no data access to code sections).
125+
return Args.hasFlag(clang::driver::options::OPT_mexecute_only,
126+
clang::driver::options::OPT_mno_execute_only, false);
127+
}
115128
} // namespace clang

clang/lib/Driver/SanitizerArgs.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ static const SanitizerMask NeedsUbsanCxxRt =
3737
SanitizerKind::Vptr | SanitizerKind::CFI;
3838
static const SanitizerMask NotAllowedWithTrap = SanitizerKind::Vptr;
3939
static const SanitizerMask NotAllowedWithMinimalRuntime = SanitizerKind::Vptr;
40+
static const SanitizerMask NotAllowedWithExecuteOnly =
41+
SanitizerKind::Function | SanitizerKind::KCFI;
4042
static const SanitizerMask RequiresPIE =
4143
SanitizerKind::DataFlow | SanitizerKind::Scudo;
4244
static const SanitizerMask NeedsUnwindTables =
@@ -395,6 +397,22 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
395397
DiagnosedKinds |= SanitizerKind::Function;
396398
}
397399
}
400+
// -fsanitize=function and -fsanitize=kcfi instrument indirect function
401+
// calls to load a type hash before the function label. Therefore, an
402+
// execute-only target doesn't support the function and kcfi sanitizers.
403+
const llvm::Triple &Triple = TC.getTriple();
404+
if (isExecuteOnlyTarget(Triple, Args)) {
405+
if (SanitizerMask KindsToDiagnose =
406+
Add & NotAllowedWithExecuteOnly & ~DiagnosedKinds) {
407+
if (DiagnoseErrors) {
408+
std::string Desc = describeSanitizeArg(Arg, KindsToDiagnose);
409+
D.Diag(diag::err_drv_argument_not_allowed_with)
410+
<< Desc << Triple.str();
411+
}
412+
DiagnosedKinds |= KindsToDiagnose;
413+
}
414+
Add &= ~NotAllowedWithExecuteOnly;
415+
}
398416

399417
// FIXME: Make CFI on member function calls compatible with cross-DSO CFI.
400418
// There are currently two problems:
@@ -457,6 +475,10 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
457475
if (MinimalRuntime) {
458476
Add &= ~NotAllowedWithMinimalRuntime;
459477
}
478+
// NotAllowedWithExecuteOnly is silently discarded on an execute-only
479+
// target if implicitly enabled through group expansion.
480+
if (isExecuteOnlyTarget(Triple, Args))
481+
Add &= ~NotAllowedWithExecuteOnly;
460482
if (CfiCrossDso)
461483
Add &= ~SanitizerKind::CFIMFCall;
462484
Add &= Supported;

clang/test/CodeGenObjCXX/crash-function-type.mm

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
// Mark test as unsupported on PS5 due to PS5 doesn't support function sanitizer.
2+
// UNSUPPORTED: target=x86_64-sie-ps5
3+
14
// RUN: %clang_cc1 -fblocks -fsanitize=function -emit-llvm %s -o %t
25

36
void g(void (^)());

clang/test/Driver/fsanitize.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -966,3 +966,17 @@
966966

967967
// 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
968968
// CHECK-UBSAN-FUNCTION-CODE-MODEL: error: invalid argument '-fsanitize=function' only allowed with '-mcmodel=small'
969+
970+
// RUN: not %clang --target=x86_64-sie-ps5 -fsanitize=function %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UBSAN-FUNCTION
971+
// RUN: not %clang --target=x86_64-sie-ps5 -fsanitize=undefined -fsanitize=function %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UBSAN-FUNCTION
972+
// RUN: not %clang --target=x86_64-sie-ps5 -fsanitize=kcfi %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UBSAN-KCFI
973+
// 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
974+
// RUN: %clang --target=x86_64-sie-ps5 -fsanitize=undefined %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UBSAN-UNDEFINED
975+
976+
// RUN: not %clang --target=armv6t2-eabi -mexecute-only -fsanitize=function %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UBSAN-FUNCTION
977+
// RUN: not %clang --target=armv6t2-eabi -mexecute-only -fsanitize=kcfi %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UBSAN-KCFI
978+
// RUN: %clang --target=armv6t2-eabi -mexecute-only -fsanitize=undefined %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UBSAN-UNDEFINED
979+
980+
// CHECK-UBSAN-KCFI-DAG: error: invalid argument '-fsanitize=kcfi' not allowed with {{('x86_64-sie-ps5'|'armv6t2-unknown-unknown-eabi')}}
981+
// CHECK-UBSAN-FUNCTION-DAG: error: invalid argument '-fsanitize=function' not allowed with {{('x86_64-sie-ps5'|'armv6t2-unknown-unknown-eabi')}}
982+
// 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 commit comments

Comments
 (0)