Group<f_clang_Group>;
def fno_sanitize_undefined_trap_on_error : Flag<["-"], "fno-sanitize-undefined-trap-on-error">,
Group<f_clang_Group>;
+def fsanitize_minimal_runtime : Flag<["-"], "fsanitize-minimal-runtime">,
+ Group<f_clang_Group>;
+def fno_sanitize_minimal_runtime : Flag<["-"], "fno-sanitize-minimal-runtime">,
+ Group<f_clang_Group>;
def fsanitize_link_cxx_runtime : Flag<["-"], "fsanitize-link-c++-runtime">,
Group<f_clang_Group>;
def fsanitize_cfi_cross_dso : Flag<["-"], "fsanitize-cfi-cross-dso">,
bool TsanMemoryAccess = true;
bool TsanFuncEntryExit = true;
bool TsanAtomics = true;
+ bool MinimalRuntime = false;
public:
/// Parses the sanitizer arguments from an argument list.
!Sanitizers.has(SanitizerKind::Address);
}
bool needsUbsanRt() const;
+ bool requiresMinimalRuntime() const { return MinimalRuntime; }
bool needsDfsanRt() const { return Sanitizers.has(SanitizerKind::DataFlow); }
bool needsSafeStackRt() const { return SafeStackRuntime; }
bool needsCfiRt() const;
CODEGENOPT(SanitizeMemoryUseAfterDtor, 1, 0) ///< Enable use-after-delete detection
///< in MemorySanitizer
CODEGENOPT(SanitizeCfiCrossDso, 1, 0) ///< Enable cross-dso support in CFI.
+CODEGENOPT(SanitizeMinimalRuntime, 1, 0) ///< Use "_minimal" sanitizer runtime for
+ ///< diagnostics.
CODEGENOPT(SanitizeCoverageType, 2, 0) ///< Type of sanitizer coverage
///< instrumentation.
CODEGENOPT(SanitizeCoverageIndirectCalls, 1, 0) ///< Enable sanitizer coverage
assert(IsFatal || RecoverKind != CheckRecoverableKind::Unrecoverable);
bool NeedsAbortSuffix =
IsFatal && RecoverKind != CheckRecoverableKind::Unrecoverable;
+ bool MinimalRuntime = CGF.CGM.getCodeGenOpts().SanitizeMinimalRuntime;
const SanitizerHandlerInfo &CheckInfo = SanitizerHandlers[CheckHandler];
const StringRef CheckName = CheckInfo.Name;
- std::string FnName =
- ("__ubsan_handle_" + CheckName +
- (CheckInfo.Version ? "_v" + llvm::utostr(CheckInfo.Version) : "") +
- (NeedsAbortSuffix ? "_abort" : ""))
- .str();
+ std::string FnName = "__ubsan_handle_" + CheckName.str();
+ if (CheckInfo.Version && !MinimalRuntime)
+ FnName += "_v" + llvm::utostr(CheckInfo.Version);
+ if (MinimalRuntime)
+ FnName += "_minimal";
+ if (NeedsAbortSuffix)
+ FnName += "_abort";
bool MayReturn =
!IsFatal || RecoverKind == CheckRecoverableKind::AlwaysRecoverable;
// representing operand values.
SmallVector<llvm::Value *, 4> Args;
SmallVector<llvm::Type *, 4> ArgTypes;
- Args.reserve(DynamicArgs.size() + 1);
- ArgTypes.reserve(DynamicArgs.size() + 1);
-
- // Emit handler arguments and create handler function type.
- if (!StaticArgs.empty()) {
- llvm::Constant *Info = llvm::ConstantStruct::getAnon(StaticArgs);
- auto *InfoPtr =
- new llvm::GlobalVariable(CGM.getModule(), Info->getType(), false,
- llvm::GlobalVariable::PrivateLinkage, Info);
- InfoPtr->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
- CGM.getSanitizerMetadata()->disableSanitizerForGlobal(InfoPtr);
- Args.push_back(Builder.CreateBitCast(InfoPtr, Int8PtrTy));
- ArgTypes.push_back(Int8PtrTy);
- }
+ if (!CGM.getCodeGenOpts().SanitizeMinimalRuntime) {
+ Args.reserve(DynamicArgs.size() + 1);
+ ArgTypes.reserve(DynamicArgs.size() + 1);
+
+ // Emit handler arguments and create handler function type.
+ if (!StaticArgs.empty()) {
+ llvm::Constant *Info = llvm::ConstantStruct::getAnon(StaticArgs);
+ auto *InfoPtr =
+ new llvm::GlobalVariable(CGM.getModule(), Info->getType(), false,
+ llvm::GlobalVariable::PrivateLinkage, Info);
+ InfoPtr->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
+ CGM.getSanitizerMetadata()->disableSanitizerForGlobal(InfoPtr);
+ Args.push_back(Builder.CreateBitCast(InfoPtr, Int8PtrTy));
+ ArgTypes.push_back(Int8PtrTy);
+ }
- for (size_t i = 0, n = DynamicArgs.size(); i != n; ++i) {
- Args.push_back(EmitCheckValue(DynamicArgs[i]));
- ArgTypes.push_back(IntPtrTy);
+ for (size_t i = 0, n = DynamicArgs.size(); i != n; ++i) {
+ Args.push_back(EmitCheckValue(DynamicArgs[i]));
+ ArgTypes.push_back(IntPtrTy);
+ }
}
llvm::FunctionType *FnType =
NeedsUbsanRt = Undefined | Integer | Nullability | CFI,
NeedsUbsanCxxRt = Vptr | CFI,
NotAllowedWithTrap = Vptr,
+ NotAllowedWithMinimalRuntime = Vptr,
RequiresPIE = DataFlow,
NeedsUnwindTables = Address | Thread | Memory | DataFlow,
SupportsCoverage = Address | KernelAddress | Memory | Leak | Undefined |
Nullability | LocalBounds | CFI,
TrappingDefault = CFI,
CFIClasses = CFIVCall | CFINVCall | CFIDerivedCast | CFIUnrelatedCast,
+ CompatibleWithMinimalRuntime = TrappingSupported,
};
enum CoverageFeature {
SanitizerMask TrappingKinds = parseSanitizeTrapArgs(D, Args);
SanitizerMask InvalidTrappingKinds = TrappingKinds & NotAllowedWithTrap;
+ MinimalRuntime =
+ Args.hasFlag(options::OPT_fsanitize_minimal_runtime,
+ options::OPT_fno_sanitize_minimal_runtime, MinimalRuntime);
+
// The object size sanitizer should not be enabled at -O0.
Arg *OptLevel = Args.getLastArg(options::OPT_O_Group);
bool RemoveObjectSizeAtO0 =
DiagnosedKinds |= KindsToDiagnose;
}
Add &= ~InvalidTrappingKinds;
+
+ if (MinimalRuntime) {
+ if (SanitizerMask KindsToDiagnose =
+ Add & NotAllowedWithMinimalRuntime & ~DiagnosedKinds) {
+ std::string Desc = describeSanitizeArg(*I, KindsToDiagnose);
+ D.Diag(diag::err_drv_argument_not_allowed_with)
+ << Desc << "-fsanitize-minimal-runtime";
+ DiagnosedKinds |= KindsToDiagnose;
+ }
+ Add &= ~NotAllowedWithMinimalRuntime;
+ }
+
if (SanitizerMask KindsToDiagnose = Add & ~Supported & ~DiagnosedKinds) {
std::string Desc = describeSanitizeArg(*I, KindsToDiagnose);
D.Diag(diag::err_drv_unsupported_opt_for_target)
// Silently discard any unsupported sanitizers implicitly enabled through
// group expansion.
Add &= ~InvalidTrappingKinds;
+ if (MinimalRuntime) {
+ Add &= ~NotAllowedWithMinimalRuntime;
+ }
Add &= Supported;
if (Add & Fuzzer)
Stats = Args.hasFlag(options::OPT_fsanitize_stats,
options::OPT_fno_sanitize_stats, false);
+ if (MinimalRuntime) {
+ SanitizerMask IncompatibleMask =
+ Kinds & ~setGroupBits(CompatibleWithMinimalRuntime);
+ if (IncompatibleMask)
+ D.Diag(clang::diag::err_drv_argument_not_allowed_with)
+ << "-fsanitize-minimal-runtime"
+ << lastArgumentForMask(D, Args, IncompatibleMask);
+
+ SanitizerMask NonTrappingCfi = Kinds & CFI & ~TrappingKinds;
+ if (NonTrappingCfi)
+ D.Diag(clang::diag::err_drv_argument_only_allowed_with)
+ << "fsanitize-minimal-runtime"
+ << "fsanitize-trap=cfi";
+ }
+
// Parse -f(no-)?sanitize-coverage flags if coverage is supported by the
// enabled sanitizers.
for (const auto *Arg : Args) {
if (Stats)
CmdArgs.push_back("-fsanitize-stats");
+ if (MinimalRuntime)
+ CmdArgs.push_back("-fsanitize-minimal-runtime");
+
if (AsanFieldPadding)
CmdArgs.push_back(Args.MakeArgString("-fsanitize-address-field-padding=" +
llvm::utostr(AsanFieldPadding)));
if (SanArgs.needsAsanRt() && SanArgs.needsSharedAsanRt()) {
SharedRuntimes.push_back("asan");
}
+
// The stats_client library is also statically linked into DSOs.
if (SanArgs.needsStatsRt())
StaticRuntimes.push_back("stats_client");
StaticRuntimes.push_back("tsan_cxx");
}
if (SanArgs.needsUbsanRt()) {
- StaticRuntimes.push_back("ubsan_standalone");
- if (SanArgs.linkCXXRuntimes())
- StaticRuntimes.push_back("ubsan_standalone_cxx");
+ if (SanArgs.requiresMinimalRuntime()) {
+ StaticRuntimes.push_back("ubsan_minimal");
+ } else {
+ StaticRuntimes.push_back("ubsan_standalone");
+ if (SanArgs.linkCXXRuntimes())
+ StaticRuntimes.push_back("ubsan_standalone_cxx");
+ }
}
if (SanArgs.needsSafeStackRt()) {
NonWholeStaticRuntimes.push_back("safestack");
getLastArgIntValue(Args, OPT_fsanitize_memory_track_origins_EQ, 0, Diags);
Opts.SanitizeMemoryUseAfterDtor =
Args.hasArg(OPT_fsanitize_memory_use_after_dtor);
+ Opts.SanitizeMinimalRuntime = Args.hasArg(OPT_fsanitize_minimal_runtime);
Opts.SanitizeCfiCrossDso = Args.hasArg(OPT_fsanitize_cfi_cross_dso);
Opts.SanitizeStats = Args.hasArg(OPT_fsanitize_stats);
if (Arg *A = Args.getLastArg(OPT_fsanitize_address_use_after_scope,
--- /dev/null
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsanitize=unsigned-integer-overflow -fsanitize-minimal-runtime %s -emit-llvm -o - | FileCheck %s
+
+unsigned long li, lj, lk;
+
+// CHECK-LABEL: define void @testlongadd()
+void testlongadd() {
+ // CHECK: call void @__ubsan_handle_add_overflow_minimal_abort()
+ li = lj + lk;
+}
+
+// CHECK-LABEL: define void @testlongsub()
+void testlongsub() {
+ // CHECK: call void @__ubsan_handle_sub_overflow_minimal_abort()
+ li = lj - lk;
+}
+
+// CHECK-LABEL: define void @testlongmul()
+void testlongmul() {
+ // CHECK: call void @__ubsan_handle_mul_overflow_minimal_abort()
+ li = lj * lk;
+}
// Make sure there are no *.{o,bc} or -l passed before the ASan library.
// CHECK-ASAN-PS4-NOT: {{(\.(o|bc)"? |-l).*-lSceDbgAddressSanitizer_stub_weak}}
// CHECK-ASAN-PS4: -lSceDbgAddressSanitizer_stub_weak
+
+// RUN: %clang -target x86_64-linux-gnu -fsanitize=address -fsanitize-minimal-runtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-ASAN-MINIMAL
+// CHECK-ASAN-MINIMAL: error: invalid argument '-fsanitize-minimal-runtime' not allowed with '-fsanitize=address'
+
+// RUN: %clang -target x86_64-linux-gnu -fsanitize=thread -fsanitize-minimal-runtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-TSAN-MINIMAL
+// CHECK-TSAN-MINIMAL: error: invalid argument '-fsanitize-minimal-runtime' not allowed with '-fsanitize=thread'
+
+// RUN: %clang -target x86_64-linux-gnu -fsanitize=undefined -fsanitize-minimal-runtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UBSAN-MINIMAL
+// CHECK-UBSAN-MINIMAL: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|float-divide-by-zero|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute|function),?){19}"}}
+// CHECK-UBSAN-MINIMAL: "-fsanitize-minimal-runtime"
+
+// RUN: %clang -target x86_64-linux-gnu -fsanitize=undefined -fsanitize=vptr -fsanitize-minimal-runtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UBSAN-VPTR-MINIMAL
+// CHECK-UBSAN-VPTR-MINIMAL: error: invalid argument '-fsanitize=vptr' not allowed with '-fsanitize-minimal-runtime'
+
+// RUN: %clang -target x86_64-linux-gnu -fsanitize=address -fsanitize-minimal-runtime -fsanitize=undefined %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-ASAN-UBSAN-MINIMAL
+// CHECK-ASAN-UBSAN-MINIMAL: error: invalid argument '-fsanitize-minimal-runtime' not allowed with '-fsanitize=address'
+
+// RUN: %clang -target x86_64-linux-gnu -fsanitize=cfi -flto -fvisibility=hidden -fsanitize-minimal-runtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-CFI-MINIMAL
+// CHECK-CFI-MINIMAL: "-fsanitize=cfi-derived-cast,cfi-icall,cfi-unrelated-cast,cfi-nvcall,cfi-vcall"
+// CHECK-CFI-MINIMAL: "-fsanitize-trap=cfi-derived-cast,cfi-icall,cfi-unrelated-cast,cfi-nvcall,cfi-vcall"
+// CHECK-CFI-MINIMAL: "-fsanitize-minimal-runtime"
+
+// RUN: %clang -target x86_64-linux-gnu -fsanitize=cfi -fno-sanitize-trap=cfi-icall -flto -fvisibility=hidden -fsanitize-minimal-runtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-CFI-NOTRAP-MINIMAL
+// CHECK-CFI-NOTRAP-MINIMAL: error: invalid argument 'fsanitize-minimal-runtime' only allowed with 'fsanitize-trap=cfi'
+
+// RUN: %clang -target x86_64-linux-gnu -fsanitize=cfi -fno-sanitize-trap=cfi-icall -fno-sanitize=cfi-icall -flto -fvisibility=hidden -fsanitize-minimal-runtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-CFI-NOICALL-MINIMAL
+// CHECK-CFI-NOICALL-MINIMAL: "-fsanitize=cfi-derived-cast,cfi-unrelated-cast,cfi-nvcall,cfi-vcall"
+// CHECK-CFI-NOICALL-MINIMAL: "-fsanitize-trap=cfi-derived-cast,cfi-unrelated-cast,cfi-nvcall,cfi-vcall"
+// CHECK-CFI-NOICALL-MINIMAL: "-fsanitize-minimal-runtime"
// CHECK-UBSAN-LINUX-CXX-NOT: libclang_rt.asan
// CHECK-UBSAN-LINUX-CXX: "-lpthread"
+// RUN: %clang -fsanitize=undefined -fsanitize-minimal-runtime %s -### -o %t.o 2>&1 \
+// RUN: -target i386-unknown-linux -fuse-ld=ld \
+// RUN: --sysroot=%S/Inputs/basic_linux_tree \
+// RUN: | FileCheck --check-prefix=CHECK-UBSAN-MINIMAL-LINUX %s
+// CHECK-UBSAN-MINIMAL-LINUX: "{{.*}}ld{{(.exe)?}}"
+// CHECK-UBSAN-MINIMAL-LINUX: "-whole-archive" "{{.*}}libclang_rt.ubsan_minimal-i386.a" "-no-whole-archive"
+// CHECK-UBSAN-MINIMAL-LINUX: "-lpthread"
+
// RUN: %clang -fsanitize=address,undefined %s -### -o %t.o 2>&1 \
// RUN: -target i386-unknown-linux -fuse-ld=ld \
// RUN: --sysroot=%S/Inputs/basic_linux_tree \