Author: eugenis Date: Tue Aug 29 13:03:51 2017 New Revision: 312029 URL: http://llvm.org/viewvc/llvm-project?rev=312029&view=rev Log: 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 Added: cfe/trunk/test/CodeGen/unsigned-overflow-minimal.c Modified: cfe/trunk/include/clang/Driver/Options.td cfe/trunk/include/clang/Driver/SanitizerArgs.h cfe/trunk/include/clang/Frontend/CodeGenOptions.def cfe/trunk/lib/CodeGen/CGExpr.cpp cfe/trunk/lib/Driver/SanitizerArgs.cpp cfe/trunk/lib/Driver/ToolChains/CommonArgs.cpp cfe/trunk/lib/Frontend/CompilerInvocation.cpp cfe/trunk/test/Driver/fsanitize.c cfe/trunk/test/Driver/sanitizer-ld.c Modified: cfe/trunk/include/clang/Driver/Options.td URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Driver/Options.td?rev=312029&r1=312028&r2=312029&view=diff ============================================================================== --- cfe/trunk/include/clang/Driver/Options.td (original) +++ cfe/trunk/include/clang/Driver/Options.td Tue Aug 29 13:03:51 2017 @@ -885,6 +885,10 @@ def fsanitize_undefined_trap_on_error : 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">, Modified: cfe/trunk/include/clang/Driver/SanitizerArgs.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Driver/SanitizerArgs.h?rev=312029&r1=312028&r2=312029&view=diff ============================================================================== --- cfe/trunk/include/clang/Driver/SanitizerArgs.h (original) +++ cfe/trunk/include/clang/Driver/SanitizerArgs.h Tue Aug 29 13:03:51 2017 @@ -43,6 +43,7 @@ class SanitizerArgs { bool TsanMemoryAccess = true; bool TsanFuncEntryExit = true; bool TsanAtomics = true; + bool MinimalRuntime = false; public: /// Parses the sanitizer arguments from an argument list. @@ -58,6 +59,7 @@ class SanitizerArgs { !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; Modified: cfe/trunk/include/clang/Frontend/CodeGenOptions.def URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Frontend/CodeGenOptions.def?rev=312029&r1=312028&r2=312029&view=diff ============================================================================== --- cfe/trunk/include/clang/Frontend/CodeGenOptions.def (original) +++ cfe/trunk/include/clang/Frontend/CodeGenOptions.def Tue Aug 29 13:03:51 2017 @@ -152,6 +152,8 @@ CODEGENOPT(SanitizeMemoryTrackOrigins, 2 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 Modified: cfe/trunk/lib/CodeGen/CGExpr.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGExpr.cpp?rev=312029&r1=312028&r2=312029&view=diff ============================================================================== --- cfe/trunk/lib/CodeGen/CGExpr.cpp (original) +++ cfe/trunk/lib/CodeGen/CGExpr.cpp Tue Aug 29 13:03:51 2017 @@ -2724,13 +2724,16 @@ static void emitCheckHandlerCall(CodeGen 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; @@ -2817,24 +2820,26 @@ void CodeGenFunction::EmitCheck( // 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); - } - - for (size_t i = 0, n = DynamicArgs.size(); i != n; ++i) { - Args.push_back(EmitCheckValue(DynamicArgs[i])); - ArgTypes.push_back(IntPtrTy); + 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); + } } llvm::FunctionType *FnType = Modified: cfe/trunk/lib/Driver/SanitizerArgs.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Driver/SanitizerArgs.cpp?rev=312029&r1=312028&r2=312029&view=diff ============================================================================== --- cfe/trunk/lib/Driver/SanitizerArgs.cpp (original) +++ cfe/trunk/lib/Driver/SanitizerArgs.cpp Tue Aug 29 13:03:51 2017 @@ -29,6 +29,7 @@ enum : SanitizerMask { 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 | @@ -41,6 +42,7 @@ enum : SanitizerMask { Nullability | LocalBounds | CFI, TrappingDefault = CFI, CFIClasses = CFIVCall | CFINVCall | CFIDerivedCast | CFIUnrelatedCast, + CompatibleWithMinimalRuntime = TrappingSupported, }; enum CoverageFeature { @@ -212,6 +214,10 @@ SanitizerArgs::SanitizerArgs(const ToolC 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 = @@ -249,6 +255,18 @@ SanitizerArgs::SanitizerArgs(const ToolC 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) @@ -285,6 +303,9 @@ SanitizerArgs::SanitizerArgs(const ToolC // Silently discard any unsupported sanitizers implicitly enabled through // group expansion. Add &= ~InvalidTrappingKinds; + if (MinimalRuntime) { + Add &= ~NotAllowedWithMinimalRuntime; + } Add &= Supported; if (Add & Fuzzer) @@ -496,6 +517,21 @@ SanitizerArgs::SanitizerArgs(const ToolC 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) { @@ -762,6 +798,9 @@ void SanitizerArgs::addArgs(const ToolCh 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))); Modified: cfe/trunk/lib/Driver/ToolChains/CommonArgs.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Driver/ToolChains/CommonArgs.cpp?rev=312029&r1=312028&r2=312029&view=diff ============================================================================== --- cfe/trunk/lib/Driver/ToolChains/CommonArgs.cpp (original) +++ cfe/trunk/lib/Driver/ToolChains/CommonArgs.cpp Tue Aug 29 13:03:51 2017 @@ -555,6 +555,7 @@ collectSanitizerRuntimes(const ToolChain 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"); @@ -588,9 +589,13 @@ collectSanitizerRuntimes(const ToolChain 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"); Modified: cfe/trunk/lib/Frontend/CompilerInvocation.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Frontend/CompilerInvocation.cpp?rev=312029&r1=312028&r2=312029&view=diff ============================================================================== --- cfe/trunk/lib/Frontend/CompilerInvocation.cpp (original) +++ cfe/trunk/lib/Frontend/CompilerInvocation.cpp Tue Aug 29 13:03:51 2017 @@ -830,6 +830,7 @@ static bool ParseCodeGenArgs(CodeGenOpti 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, Added: cfe/trunk/test/CodeGen/unsigned-overflow-minimal.c URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGen/unsigned-overflow-minimal.c?rev=312029&view=auto ============================================================================== --- cfe/trunk/test/CodeGen/unsigned-overflow-minimal.c (added) +++ cfe/trunk/test/CodeGen/unsigned-overflow-minimal.c Tue Aug 29 13:03:51 2017 @@ -0,0 +1,21 @@ +// 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; +} Modified: cfe/trunk/test/Driver/fsanitize.c URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Driver/fsanitize.c?rev=312029&r1=312028&r2=312029&view=diff ============================================================================== --- cfe/trunk/test/Driver/fsanitize.c (original) +++ cfe/trunk/test/Driver/fsanitize.c Tue Aug 29 13:03:51 2017 @@ -558,3 +558,32 @@ // 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" Modified: cfe/trunk/test/Driver/sanitizer-ld.c URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Driver/sanitizer-ld.c?rev=312029&r1=312028&r2=312029&view=diff ============================================================================== --- cfe/trunk/test/Driver/sanitizer-ld.c (original) +++ cfe/trunk/test/Driver/sanitizer-ld.c Tue Aug 29 13:03:51 2017 @@ -234,6 +234,14 @@ // 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 \ _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits