Skip to content

Commit dccd271

Browse files
[ubsan] Connect -fsanitize-skip-hot-cutoff to LowerAllowCheckPass<cutoffs> (#124857)
This adds the plumbing between -fsanitize-skip-hot-cutoff (introduced in #121619) and LowerAllowCheckPass<cutoffs> (introduced in #124211). The net effect is that -fsanitize-skip-hot-cutoff now combines the functionality of -ubsan-guard-checks and -lower-allow-check-percentile-cutoff (though this patch does not remove those yet), and generalizes the latter to allow per-sanitizer cutoffs. Note: this patch replaces Intrinsic::allow_ubsan_check's SanitizerHandler parameter with SanitizerOrdinal; this is necessary because the hot cutoffs are specified in terms of SanitizerOrdinal (e.g., null, alignment), not SanitizerHandler (e.g., TypeMismatch). Likewise, CodeGenFunction::EmitCheck is changed to emit allow_ubsan_check() for each individual check. --------- Co-authored-by: Vitaly Buka <[email protected]> Co-authored-by: Vitaly Buka <[email protected]>
1 parent 4b2d615 commit dccd271

File tree

6 files changed

+141
-71
lines changed

6 files changed

+141
-71
lines changed

clang/include/clang/Basic/Sanitizers.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,10 @@ class SanitizerMaskCutoffs {
162162

163163
void set(SanitizerMask K, double V);
164164
void clear(SanitizerMask K = SanitizerKind::All);
165+
166+
// Returns nullopt if all the values are zero.
167+
// Otherwise, return value contains a vector of all the scaled values.
168+
std::optional<std::vector<unsigned>> getAllScaled(unsigned ScalingFactor) const;
165169
};
166170

167171
struct SanitizerSet {

clang/lib/Basic/Sanitizers.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "llvm/Support/MathExtras.h"
1919
#include "llvm/Support/raw_ostream.h"
2020
#include <algorithm>
21+
#include <cmath>
2122
#include <optional>
2223

2324
using namespace clang;
@@ -43,6 +44,27 @@ std::optional<double> SanitizerMaskCutoffs::operator[](unsigned Kind) const {
4344

4445
void SanitizerMaskCutoffs::clear(SanitizerMask K) { set(K, 0); }
4546

47+
std::optional<std::vector<unsigned>>
48+
SanitizerMaskCutoffs::getAllScaled(unsigned ScalingFactor) const {
49+
std::vector<unsigned> ScaledCutoffs;
50+
51+
bool AnyCutoff = false;
52+
for (unsigned int i = 0; i < SanitizerKind::SO_Count; ++i) {
53+
auto C = (*this)[i];
54+
if (C.has_value()) {
55+
ScaledCutoffs.push_back(lround(std::clamp(*C, 0.0, 1.0) * ScalingFactor));
56+
AnyCutoff = true;
57+
} else {
58+
ScaledCutoffs.push_back(0);
59+
}
60+
}
61+
62+
if (AnyCutoff)
63+
return ScaledCutoffs;
64+
65+
return std::nullopt;
66+
}
67+
4668
// Once LLVM switches to C++17, the constexpr variables can be inline and we
4769
// won't need this.
4870
#define SANITIZER(NAME, ID) constexpr SanitizerMask SanitizerKind::ID;

clang/lib/CodeGen/BackendUtil.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -795,12 +795,20 @@ static void addSanitizers(const Triple &TargetTriple,
795795
PB.registerOptimizerLastEPCallback(SanitizersCallback);
796796
}
797797

798-
if (LowerAllowCheckPass::IsRequested()) {
798+
// SanitizeSkipHotCutoffs: doubles with range [0, 1]
799+
// Opts.cutoffs: unsigned ints with range [0, 1000000]
800+
auto ScaledCutoffs = CodeGenOpts.SanitizeSkipHotCutoffs.getAllScaled(1000000);
801+
802+
// TODO: remove IsRequested()
803+
if (LowerAllowCheckPass::IsRequested() || ScaledCutoffs.has_value()) {
799804
// We want to call it after inline, which is about OptimizerEarlyEPCallback.
800805
PB.registerOptimizerEarlyEPCallback([&](ModulePassManager &MPM,
801806
OptimizationLevel Level,
802807
ThinOrFullLTOPhase Phase) {
803808
LowerAllowCheckPass::Options Opts;
809+
// TODO: after removing IsRequested(), make this unconditional
810+
if (ScaledCutoffs.has_value())
811+
Opts.cutoffs = ScaledCutoffs.value();
804812
MPM.addPass(createModuleToFunctionPassAdaptor(LowerAllowCheckPass(Opts)));
805813
});
806814
}

clang/lib/CodeGen/CGExpr.cpp

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3614,29 +3614,33 @@ void CodeGenFunction::EmitCheck(
36143614
llvm::Value *RecoverableCond = nullptr;
36153615
llvm::Value *TrapCond = nullptr;
36163616
bool NoMerge = false;
3617+
// Expand checks into:
3618+
// (Check1 || !allow_ubsan_check) && (Check2 || !allow_ubsan_check) ...
3619+
// We need separate allow_ubsan_check intrinsics because they have separately
3620+
// specified cutoffs.
3621+
// This expression looks expensive but will be simplified after
3622+
// LowerAllowCheckPass.
36173623
for (auto &[Check, Ord] : Checked) {
3624+
llvm::Value *GuardedCheck = Check;
3625+
if (ClSanitizeGuardChecks ||
3626+
(CGM.getCodeGenOpts().SanitizeSkipHotCutoffs[Ord] > 0)) {
3627+
llvm::Value *Allow = Builder.CreateCall(
3628+
CGM.getIntrinsic(llvm::Intrinsic::allow_ubsan_check),
3629+
llvm::ConstantInt::get(CGM.Int8Ty, Ord));
3630+
GuardedCheck = Builder.CreateOr(Check, Builder.CreateNot(Allow));
3631+
}
3632+
36183633
// -fsanitize-trap= overrides -fsanitize-recover=.
36193634
llvm::Value *&Cond = CGM.getCodeGenOpts().SanitizeTrap.has(Ord) ? TrapCond
36203635
: CGM.getCodeGenOpts().SanitizeRecover.has(Ord)
36213636
? RecoverableCond
36223637
: FatalCond;
3623-
Cond = Cond ? Builder.CreateAnd(Cond, Check) : Check;
3638+
Cond = Cond ? Builder.CreateAnd(Cond, GuardedCheck) : GuardedCheck;
36243639

36253640
if (!CGM.getCodeGenOpts().SanitizeMergeHandlers.has(Ord))
36263641
NoMerge = true;
36273642
}
36283643

3629-
if (ClSanitizeGuardChecks) {
3630-
llvm::Value *Allow =
3631-
Builder.CreateCall(CGM.getIntrinsic(llvm::Intrinsic::allow_ubsan_check),
3632-
llvm::ConstantInt::get(CGM.Int8Ty, CheckHandler));
3633-
3634-
for (llvm::Value **Cond : {&FatalCond, &RecoverableCond, &TrapCond}) {
3635-
if (*Cond)
3636-
*Cond = Builder.CreateOr(*Cond, Builder.CreateNot(Allow));
3637-
}
3638-
}
3639-
36403644
if (TrapCond)
36413645
EmitTrapCheck(TrapCond, CheckHandler, NoMerge);
36423646
if (!FatalCond && !RecoverableCond)

clang/test/CodeGen/allow-ubsan-check-inline.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -emit-llvm -o - %s -fsanitize=signed-integer-overflow -fsanitize-skip-hot-cutoff=signed-integer-overflow=0.000001 -O3 -mllvm -lower-allow-check-random-rate=1 -Rpass=lower-allow-check -Rpass-missed=lower-allow-check -fno-inline 2>&1 | FileCheck %s --check-prefixes=NOINL --implicit-check-not="remark:"
2+
// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -emit-llvm -o - %s -fsanitize=signed-integer-overflow -fsanitize-skip-hot-cutoff=signed-integer-overflow=0.000001 -O3 -mllvm -lower-allow-check-random-rate=1 -Rpass=lower-allow-check -Rpass-missed=lower-allow-check 2>&1 | FileCheck %s --check-prefixes=INLINE --implicit-check-not="remark:"
3+
//
4+
// -ubsan-guard-checks is deprecated and will be removed in the future;
5+
// use -fsanitize-skip-hot-cutoff, as shown above.
16
// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -emit-llvm -o - %s -fsanitize=signed-integer-overflow -mllvm -ubsan-guard-checks -O3 -mllvm -lower-allow-check-random-rate=1 -Rpass=lower-allow-check -Rpass-missed=lower-allow-check -fno-inline 2>&1 | FileCheck %s --check-prefixes=NOINL --implicit-check-not="remark:"
27
// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -emit-llvm -o - %s -fsanitize=signed-integer-overflow -mllvm -ubsan-guard-checks -O3 -mllvm -lower-allow-check-random-rate=1 -Rpass=lower-allow-check -Rpass-missed=lower-allow-check 2>&1 | FileCheck %s --check-prefixes=INLINE --implicit-check-not="remark:"
38

0 commit comments

Comments
 (0)