Skip to content
This repository was archived by the owner on Nov 1, 2021. It is now read-only.

Commit b4377ba

Browse files
committed
Minimal runtime for UBSan.
Summary: An implementation of ubsan runtime library suitable for use in production. Minimal attack surface. * No stack traces. * Definitely no C++ demangling. * No UBSAN_OPTIONS=log_file=/path (very suid-unfriendly). And no UBSAN_OPTIONS in general. * as simple as possible Minimal CPU and RAM overhead. * Source locations unnecessary in the presence of (split) debug info. * Values and types (as in A+B overflows T) can be reconstructed from register/stack dumps, once you know what type of error you are looking at. * above two items save 3% binary size. When UBSan is used with -ftrap-function=abort, sometimes it is hard to reason about failures. This library replaces abort with a slightly more informative message without much extra overhead. Since ubsan interface in not stable, this code must reside in compiler-rt. Reviewers: pcc, kcc Subscribers: srhines, mgorny, aprantl, krytarowski, llvm-commits Differential Revision: https://reviews.llvm.org/D36810 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@312029 91177308-0d34-0410-b5e6-96231b3b80d8
1 parent 5214ac8 commit b4377ba

File tree

10 files changed

+141
-25
lines changed

10 files changed

+141
-25
lines changed

include/clang/Driver/Options.td

+4
Original file line numberDiff line numberDiff line change
@@ -885,6 +885,10 @@ def fsanitize_undefined_trap_on_error : Flag<["-"], "fsanitize-undefined-trap-on
885885
Group<f_clang_Group>;
886886
def fno_sanitize_undefined_trap_on_error : Flag<["-"], "fno-sanitize-undefined-trap-on-error">,
887887
Group<f_clang_Group>;
888+
def fsanitize_minimal_runtime : Flag<["-"], "fsanitize-minimal-runtime">,
889+
Group<f_clang_Group>;
890+
def fno_sanitize_minimal_runtime : Flag<["-"], "fno-sanitize-minimal-runtime">,
891+
Group<f_clang_Group>;
888892
def fsanitize_link_cxx_runtime : Flag<["-"], "fsanitize-link-c++-runtime">,
889893
Group<f_clang_Group>;
890894
def fsanitize_cfi_cross_dso : Flag<["-"], "fsanitize-cfi-cross-dso">,

include/clang/Driver/SanitizerArgs.h

+2
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ class SanitizerArgs {
4343
bool TsanMemoryAccess = true;
4444
bool TsanFuncEntryExit = true;
4545
bool TsanAtomics = true;
46+
bool MinimalRuntime = false;
4647

4748
public:
4849
/// Parses the sanitizer arguments from an argument list.
@@ -58,6 +59,7 @@ class SanitizerArgs {
5859
!Sanitizers.has(SanitizerKind::Address);
5960
}
6061
bool needsUbsanRt() const;
62+
bool requiresMinimalRuntime() const { return MinimalRuntime; }
6163
bool needsDfsanRt() const { return Sanitizers.has(SanitizerKind::DataFlow); }
6264
bool needsSafeStackRt() const { return SafeStackRuntime; }
6365
bool needsCfiRt() const;

include/clang/Frontend/CodeGenOptions.def

+2
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,8 @@ CODEGENOPT(SanitizeMemoryTrackOrigins, 2, 0) ///< Enable tracking origins in
152152
CODEGENOPT(SanitizeMemoryUseAfterDtor, 1, 0) ///< Enable use-after-delete detection
153153
///< in MemorySanitizer
154154
CODEGENOPT(SanitizeCfiCrossDso, 1, 0) ///< Enable cross-dso support in CFI.
155+
CODEGENOPT(SanitizeMinimalRuntime, 1, 0) ///< Use "_minimal" sanitizer runtime for
156+
///< diagnostics.
155157
CODEGENOPT(SanitizeCoverageType, 2, 0) ///< Type of sanitizer coverage
156158
///< instrumentation.
157159
CODEGENOPT(SanitizeCoverageIndirectCalls, 1, 0) ///< Enable sanitizer coverage

lib/CodeGen/CGExpr.cpp

+27-22
Original file line numberDiff line numberDiff line change
@@ -2724,13 +2724,16 @@ static void emitCheckHandlerCall(CodeGenFunction &CGF,
27242724
assert(IsFatal || RecoverKind != CheckRecoverableKind::Unrecoverable);
27252725
bool NeedsAbortSuffix =
27262726
IsFatal && RecoverKind != CheckRecoverableKind::Unrecoverable;
2727+
bool MinimalRuntime = CGF.CGM.getCodeGenOpts().SanitizeMinimalRuntime;
27272728
const SanitizerHandlerInfo &CheckInfo = SanitizerHandlers[CheckHandler];
27282729
const StringRef CheckName = CheckInfo.Name;
2729-
std::string FnName =
2730-
("__ubsan_handle_" + CheckName +
2731-
(CheckInfo.Version ? "_v" + llvm::utostr(CheckInfo.Version) : "") +
2732-
(NeedsAbortSuffix ? "_abort" : ""))
2733-
.str();
2730+
std::string FnName = "__ubsan_handle_" + CheckName.str();
2731+
if (CheckInfo.Version && !MinimalRuntime)
2732+
FnName += "_v" + llvm::utostr(CheckInfo.Version);
2733+
if (MinimalRuntime)
2734+
FnName += "_minimal";
2735+
if (NeedsAbortSuffix)
2736+
FnName += "_abort";
27342737
bool MayReturn =
27352738
!IsFatal || RecoverKind == CheckRecoverableKind::AlwaysRecoverable;
27362739

@@ -2817,24 +2820,26 @@ void CodeGenFunction::EmitCheck(
28172820
// representing operand values.
28182821
SmallVector<llvm::Value *, 4> Args;
28192822
SmallVector<llvm::Type *, 4> ArgTypes;
2820-
Args.reserve(DynamicArgs.size() + 1);
2821-
ArgTypes.reserve(DynamicArgs.size() + 1);
2822-
2823-
// Emit handler arguments and create handler function type.
2824-
if (!StaticArgs.empty()) {
2825-
llvm::Constant *Info = llvm::ConstantStruct::getAnon(StaticArgs);
2826-
auto *InfoPtr =
2827-
new llvm::GlobalVariable(CGM.getModule(), Info->getType(), false,
2828-
llvm::GlobalVariable::PrivateLinkage, Info);
2829-
InfoPtr->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
2830-
CGM.getSanitizerMetadata()->disableSanitizerForGlobal(InfoPtr);
2831-
Args.push_back(Builder.CreateBitCast(InfoPtr, Int8PtrTy));
2832-
ArgTypes.push_back(Int8PtrTy);
2833-
}
2823+
if (!CGM.getCodeGenOpts().SanitizeMinimalRuntime) {
2824+
Args.reserve(DynamicArgs.size() + 1);
2825+
ArgTypes.reserve(DynamicArgs.size() + 1);
2826+
2827+
// Emit handler arguments and create handler function type.
2828+
if (!StaticArgs.empty()) {
2829+
llvm::Constant *Info = llvm::ConstantStruct::getAnon(StaticArgs);
2830+
auto *InfoPtr =
2831+
new llvm::GlobalVariable(CGM.getModule(), Info->getType(), false,
2832+
llvm::GlobalVariable::PrivateLinkage, Info);
2833+
InfoPtr->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
2834+
CGM.getSanitizerMetadata()->disableSanitizerForGlobal(InfoPtr);
2835+
Args.push_back(Builder.CreateBitCast(InfoPtr, Int8PtrTy));
2836+
ArgTypes.push_back(Int8PtrTy);
2837+
}
28342838

2835-
for (size_t i = 0, n = DynamicArgs.size(); i != n; ++i) {
2836-
Args.push_back(EmitCheckValue(DynamicArgs[i]));
2837-
ArgTypes.push_back(IntPtrTy);
2839+
for (size_t i = 0, n = DynamicArgs.size(); i != n; ++i) {
2840+
Args.push_back(EmitCheckValue(DynamicArgs[i]));
2841+
ArgTypes.push_back(IntPtrTy);
2842+
}
28382843
}
28392844

28402845
llvm::FunctionType *FnType =

lib/Driver/SanitizerArgs.cpp

+39
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ enum : SanitizerMask {
2929
NeedsUbsanRt = Undefined | Integer | Nullability | CFI,
3030
NeedsUbsanCxxRt = Vptr | CFI,
3131
NotAllowedWithTrap = Vptr,
32+
NotAllowedWithMinimalRuntime = Vptr,
3233
RequiresPIE = DataFlow,
3334
NeedsUnwindTables = Address | Thread | Memory | DataFlow,
3435
SupportsCoverage = Address | KernelAddress | Memory | Leak | Undefined |
@@ -41,6 +42,7 @@ enum : SanitizerMask {
4142
Nullability | LocalBounds | CFI,
4243
TrappingDefault = CFI,
4344
CFIClasses = CFIVCall | CFINVCall | CFIDerivedCast | CFIUnrelatedCast,
45+
CompatibleWithMinimalRuntime = TrappingSupported,
4446
};
4547

4648
enum CoverageFeature {
@@ -212,6 +214,10 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
212214
SanitizerMask TrappingKinds = parseSanitizeTrapArgs(D, Args);
213215
SanitizerMask InvalidTrappingKinds = TrappingKinds & NotAllowedWithTrap;
214216

217+
MinimalRuntime =
218+
Args.hasFlag(options::OPT_fsanitize_minimal_runtime,
219+
options::OPT_fno_sanitize_minimal_runtime, MinimalRuntime);
220+
215221
// The object size sanitizer should not be enabled at -O0.
216222
Arg *OptLevel = Args.getLastArg(options::OPT_O_Group);
217223
bool RemoveObjectSizeAtO0 =
@@ -249,6 +255,18 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
249255
DiagnosedKinds |= KindsToDiagnose;
250256
}
251257
Add &= ~InvalidTrappingKinds;
258+
259+
if (MinimalRuntime) {
260+
if (SanitizerMask KindsToDiagnose =
261+
Add & NotAllowedWithMinimalRuntime & ~DiagnosedKinds) {
262+
std::string Desc = describeSanitizeArg(*I, KindsToDiagnose);
263+
D.Diag(diag::err_drv_argument_not_allowed_with)
264+
<< Desc << "-fsanitize-minimal-runtime";
265+
DiagnosedKinds |= KindsToDiagnose;
266+
}
267+
Add &= ~NotAllowedWithMinimalRuntime;
268+
}
269+
252270
if (SanitizerMask KindsToDiagnose = Add & ~Supported & ~DiagnosedKinds) {
253271
std::string Desc = describeSanitizeArg(*I, KindsToDiagnose);
254272
D.Diag(diag::err_drv_unsupported_opt_for_target)
@@ -285,6 +303,9 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
285303
// Silently discard any unsupported sanitizers implicitly enabled through
286304
// group expansion.
287305
Add &= ~InvalidTrappingKinds;
306+
if (MinimalRuntime) {
307+
Add &= ~NotAllowedWithMinimalRuntime;
308+
}
288309
Add &= Supported;
289310

290311
if (Add & Fuzzer)
@@ -496,6 +517,21 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
496517
Stats = Args.hasFlag(options::OPT_fsanitize_stats,
497518
options::OPT_fno_sanitize_stats, false);
498519

520+
if (MinimalRuntime) {
521+
SanitizerMask IncompatibleMask =
522+
Kinds & ~setGroupBits(CompatibleWithMinimalRuntime);
523+
if (IncompatibleMask)
524+
D.Diag(clang::diag::err_drv_argument_not_allowed_with)
525+
<< "-fsanitize-minimal-runtime"
526+
<< lastArgumentForMask(D, Args, IncompatibleMask);
527+
528+
SanitizerMask NonTrappingCfi = Kinds & CFI & ~TrappingKinds;
529+
if (NonTrappingCfi)
530+
D.Diag(clang::diag::err_drv_argument_only_allowed_with)
531+
<< "fsanitize-minimal-runtime"
532+
<< "fsanitize-trap=cfi";
533+
}
534+
499535
// Parse -f(no-)?sanitize-coverage flags if coverage is supported by the
500536
// enabled sanitizers.
501537
for (const auto *Arg : Args) {
@@ -762,6 +798,9 @@ void SanitizerArgs::addArgs(const ToolChain &TC, const llvm::opt::ArgList &Args,
762798
if (Stats)
763799
CmdArgs.push_back("-fsanitize-stats");
764800

801+
if (MinimalRuntime)
802+
CmdArgs.push_back("-fsanitize-minimal-runtime");
803+
765804
if (AsanFieldPadding)
766805
CmdArgs.push_back(Args.MakeArgString("-fsanitize-address-field-padding=" +
767806
llvm::utostr(AsanFieldPadding)));

lib/Driver/ToolChains/CommonArgs.cpp

+8-3
Original file line numberDiff line numberDiff line change
@@ -555,6 +555,7 @@ collectSanitizerRuntimes(const ToolChain &TC, const ArgList &Args,
555555
if (SanArgs.needsAsanRt() && SanArgs.needsSharedAsanRt()) {
556556
SharedRuntimes.push_back("asan");
557557
}
558+
558559
// The stats_client library is also statically linked into DSOs.
559560
if (SanArgs.needsStatsRt())
560561
StaticRuntimes.push_back("stats_client");
@@ -588,9 +589,13 @@ collectSanitizerRuntimes(const ToolChain &TC, const ArgList &Args,
588589
StaticRuntimes.push_back("tsan_cxx");
589590
}
590591
if (SanArgs.needsUbsanRt()) {
591-
StaticRuntimes.push_back("ubsan_standalone");
592-
if (SanArgs.linkCXXRuntimes())
593-
StaticRuntimes.push_back("ubsan_standalone_cxx");
592+
if (SanArgs.requiresMinimalRuntime()) {
593+
StaticRuntimes.push_back("ubsan_minimal");
594+
} else {
595+
StaticRuntimes.push_back("ubsan_standalone");
596+
if (SanArgs.linkCXXRuntimes())
597+
StaticRuntimes.push_back("ubsan_standalone_cxx");
598+
}
594599
}
595600
if (SanArgs.needsSafeStackRt()) {
596601
NonWholeStaticRuntimes.push_back("safestack");

lib/Frontend/CompilerInvocation.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -830,6 +830,7 @@ static bool ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK,
830830
getLastArgIntValue(Args, OPT_fsanitize_memory_track_origins_EQ, 0, Diags);
831831
Opts.SanitizeMemoryUseAfterDtor =
832832
Args.hasArg(OPT_fsanitize_memory_use_after_dtor);
833+
Opts.SanitizeMinimalRuntime = Args.hasArg(OPT_fsanitize_minimal_runtime);
833834
Opts.SanitizeCfiCrossDso = Args.hasArg(OPT_fsanitize_cfi_cross_dso);
834835
Opts.SanitizeStats = Args.hasArg(OPT_fsanitize_stats);
835836
if (Arg *A = Args.getLastArg(OPT_fsanitize_address_use_after_scope,
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsanitize=unsigned-integer-overflow -fsanitize-minimal-runtime %s -emit-llvm -o - | FileCheck %s
2+
3+
unsigned long li, lj, lk;
4+
5+
// CHECK-LABEL: define void @testlongadd()
6+
void testlongadd() {
7+
// CHECK: call void @__ubsan_handle_add_overflow_minimal_abort()
8+
li = lj + lk;
9+
}
10+
11+
// CHECK-LABEL: define void @testlongsub()
12+
void testlongsub() {
13+
// CHECK: call void @__ubsan_handle_sub_overflow_minimal_abort()
14+
li = lj - lk;
15+
}
16+
17+
// CHECK-LABEL: define void @testlongmul()
18+
void testlongmul() {
19+
// CHECK: call void @__ubsan_handle_mul_overflow_minimal_abort()
20+
li = lj * lk;
21+
}

test/Driver/fsanitize.c

+29
Original file line numberDiff line numberDiff line change
@@ -558,3 +558,32 @@
558558
// Make sure there are no *.{o,bc} or -l passed before the ASan library.
559559
// CHECK-ASAN-PS4-NOT: {{(\.(o|bc)"? |-l).*-lSceDbgAddressSanitizer_stub_weak}}
560560
// CHECK-ASAN-PS4: -lSceDbgAddressSanitizer_stub_weak
561+
562+
// RUN: %clang -target x86_64-linux-gnu -fsanitize=address -fsanitize-minimal-runtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-ASAN-MINIMAL
563+
// CHECK-ASAN-MINIMAL: error: invalid argument '-fsanitize-minimal-runtime' not allowed with '-fsanitize=address'
564+
565+
// RUN: %clang -target x86_64-linux-gnu -fsanitize=thread -fsanitize-minimal-runtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-TSAN-MINIMAL
566+
// CHECK-TSAN-MINIMAL: error: invalid argument '-fsanitize-minimal-runtime' not allowed with '-fsanitize=thread'
567+
568+
// RUN: %clang -target x86_64-linux-gnu -fsanitize=undefined -fsanitize-minimal-runtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UBSAN-MINIMAL
569+
// 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}"}}
570+
// CHECK-UBSAN-MINIMAL: "-fsanitize-minimal-runtime"
571+
572+
// 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
573+
// CHECK-UBSAN-VPTR-MINIMAL: error: invalid argument '-fsanitize=vptr' not allowed with '-fsanitize-minimal-runtime'
574+
575+
// 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
576+
// CHECK-ASAN-UBSAN-MINIMAL: error: invalid argument '-fsanitize-minimal-runtime' not allowed with '-fsanitize=address'
577+
578+
// 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
579+
// CHECK-CFI-MINIMAL: "-fsanitize=cfi-derived-cast,cfi-icall,cfi-unrelated-cast,cfi-nvcall,cfi-vcall"
580+
// CHECK-CFI-MINIMAL: "-fsanitize-trap=cfi-derived-cast,cfi-icall,cfi-unrelated-cast,cfi-nvcall,cfi-vcall"
581+
// CHECK-CFI-MINIMAL: "-fsanitize-minimal-runtime"
582+
583+
// 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
584+
// CHECK-CFI-NOTRAP-MINIMAL: error: invalid argument 'fsanitize-minimal-runtime' only allowed with 'fsanitize-trap=cfi'
585+
586+
// 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
587+
// CHECK-CFI-NOICALL-MINIMAL: "-fsanitize=cfi-derived-cast,cfi-unrelated-cast,cfi-nvcall,cfi-vcall"
588+
// CHECK-CFI-NOICALL-MINIMAL: "-fsanitize-trap=cfi-derived-cast,cfi-unrelated-cast,cfi-nvcall,cfi-vcall"
589+
// CHECK-CFI-NOICALL-MINIMAL: "-fsanitize-minimal-runtime"

test/Driver/sanitizer-ld.c

+8
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,14 @@
234234
// CHECK-UBSAN-LINUX-CXX-NOT: libclang_rt.asan
235235
// CHECK-UBSAN-LINUX-CXX: "-lpthread"
236236

237+
// RUN: %clang -fsanitize=undefined -fsanitize-minimal-runtime %s -### -o %t.o 2>&1 \
238+
// RUN: -target i386-unknown-linux -fuse-ld=ld \
239+
// RUN: --sysroot=%S/Inputs/basic_linux_tree \
240+
// RUN: | FileCheck --check-prefix=CHECK-UBSAN-MINIMAL-LINUX %s
241+
// CHECK-UBSAN-MINIMAL-LINUX: "{{.*}}ld{{(.exe)?}}"
242+
// CHECK-UBSAN-MINIMAL-LINUX: "-whole-archive" "{{.*}}libclang_rt.ubsan_minimal-i386.a" "-no-whole-archive"
243+
// CHECK-UBSAN-MINIMAL-LINUX: "-lpthread"
244+
237245
// RUN: %clang -fsanitize=address,undefined %s -### -o %t.o 2>&1 \
238246
// RUN: -target i386-unknown-linux -fuse-ld=ld \
239247
// RUN: --sysroot=%S/Inputs/basic_linux_tree \

0 commit comments

Comments
 (0)