diff --git a/clang/docs/UndefinedBehaviorSanitizer.rst b/clang/docs/UndefinedBehaviorSanitizer.rst --- a/clang/docs/UndefinedBehaviorSanitizer.rst +++ b/clang/docs/UndefinedBehaviorSanitizer.rst @@ -214,6 +214,28 @@ ``-fsanitize=undefined``, you could enable the minimal runtime with ``-fsanitize=undefined -fsanitize-minimal-runtime``. +␇ Runtime +========= + +There is a ␇ UBSan runtime available. It is named in honor of Bell Labs (who +gave us the C programming language and Undefined Behavior), the ASCII "bell" +character (value `07`), and famed violinist Joshua Bell. It is not related to +the city of Bell in California. This runtime will emit sound, most traditionally +the terminal's bell sound, when undefined behavior occurs. + +To use the minimal runtime, add ``-fsanitize-bel-runtime`` to the clang command +line options. For example, if you're used to compiling with +``-fsanitize=undefined``, you could enable the minimal runtime with +``-fsanitize=undefined -fsanitize-bel-runtime``. + +When combined with ``-fsanitize-recover=undefined``, the ␇ runtime will simply +chime on Undefined Behavior without killing the program for each chime. To avoid +Pavlovian effects, the ␇ runtime uses Advanced Compiler Techniques called +"heuristics" to avoid chiming too often at the same location. + +On macOS, the ␇ runtime will helpfully announce what specific undefined behavior +you've encountered, and then taunt you. + Stack traces and report symbolization ===================================== If you want UBSan to print symbolized stack trace for each error report, you diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def --- a/clang/include/clang/Basic/CodeGenOptions.def +++ b/clang/include/clang/Basic/CodeGenOptions.def @@ -209,6 +209,7 @@ CODEGENOPT(SanitizeCfiCrossDso, 1, 0) ///< Enable cross-dso support in CFI. CODEGENOPT(SanitizeMinimalRuntime, 1, 0) ///< Use "_minimal" sanitizer runtime for ///< diagnostics. +CODEGENOPT(SanitizeBelRuntime, 1, 0) ///< Use "_bel" sanitizer runtime for diagnostics. CODEGENOPT(SanitizeCfiICallGeneralizePointers, 1, 0) ///< Generalize pointer types in ///< CFI icall function signatures CODEGENOPT(SanitizeCfiCanonicalJumpTables, 1, 0) ///< Make jump table symbols canonical diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -1096,6 +1096,8 @@ Group; def fno_sanitize_minimal_runtime : Flag<["-"], "fno-sanitize-minimal-runtime">, Group; +def fsanitize_bel_runtime : Flag<["-"], "fsanitize-bel-runtime">, Group; +def fno_sanitize_bel_runtime : Flag<["-"], "fno-sanitize-bel-runtime">, Group; def fsanitize_link_runtime : Flag<["-"], "fsanitize-link-runtime">, Group; def fno_sanitize_link_runtime : Flag<["-"], "fno-sanitize-link-runtime">, diff --git a/clang/include/clang/Driver/SanitizerArgs.h b/clang/include/clang/Driver/SanitizerArgs.h --- a/clang/include/clang/Driver/SanitizerArgs.h +++ b/clang/include/clang/Driver/SanitizerArgs.h @@ -51,6 +51,7 @@ bool TsanFuncEntryExit = true; bool TsanAtomics = true; bool MinimalRuntime = false; + bool BelRuntime = false; // True if cross-dso CFI support if provided by the system (i.e. Android). bool ImplicitCfiRuntime = false; @@ -74,6 +75,7 @@ } bool needsUbsanRt() const; bool requiresMinimalRuntime() const { return MinimalRuntime; } + bool requiresBelRuntime() const { return BelRuntime; } bool needsDfsanRt() const { return Sanitizers.has(SanitizerKind::DataFlow); } bool needsSafeStackRt() const { return SafeStackRuntime; } bool needsCfiRt() const; diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -3014,13 +3014,16 @@ bool NeedsAbortSuffix = IsFatal && RecoverKind != CheckRecoverableKind::Unrecoverable; bool MinimalRuntime = CGF.CGM.getCodeGenOpts().SanitizeMinimalRuntime; + bool BelRuntime = CGF.CGM.getCodeGenOpts().SanitizeBelRuntime; const SanitizerHandlerInfo &CheckInfo = SanitizerHandlers[CheckHandler]; const StringRef CheckName = CheckInfo.Name; std::string FnName = "__ubsan_handle_" + CheckName.str(); - if (CheckInfo.Version && !MinimalRuntime) + if (CheckInfo.Version && !MinimalRuntime && !BelRuntime) FnName += "_v" + llvm::utostr(CheckInfo.Version); if (MinimalRuntime) FnName += "_minimal"; + if (BelRuntime) + FnName += "_bel"; if (NeedsAbortSuffix) FnName += "_abort"; bool MayReturn = @@ -3109,7 +3112,7 @@ // representing operand values. SmallVector Args; SmallVector ArgTypes; - if (!CGM.getCodeGenOpts().SanitizeMinimalRuntime) { + if (!(CGM.getCodeGenOpts().SanitizeMinimalRuntime || CGM.getCodeGenOpts().SanitizeBelRuntime)) { Args.reserve(DynamicArgs.size() + 1); ArgTypes.reserve(DynamicArgs.size() + 1); diff --git a/clang/lib/Driver/SanitizerArgs.cpp b/clang/lib/Driver/SanitizerArgs.cpp --- a/clang/lib/Driver/SanitizerArgs.cpp +++ b/clang/lib/Driver/SanitizerArgs.cpp @@ -33,6 +33,8 @@ static const SanitizerMask NotAllowedWithTrap = SanitizerKind::Vptr; static const SanitizerMask NotAllowedWithMinimalRuntime = SanitizerKind::Function | SanitizerKind::Vptr; +static const SanitizerMask NotAllowedWithBelRuntime = + SanitizerKind::Function | SanitizerKind::Vptr; static const SanitizerMask RequiresPIE = SanitizerKind::DataFlow | SanitizerKind::HWAddress | SanitizerKind::Scudo; static const SanitizerMask NeedsUnwindTables = @@ -71,6 +73,8 @@ SanitizerKind::CFIUnrelatedCast; static const SanitizerMask CompatibleWithMinimalRuntime = TrappingSupported | SanitizerKind::Scudo | SanitizerKind::ShadowCallStack; +static const SanitizerMask CompatibleWithBelRuntime = + TrappingSupported | SanitizerKind::Scudo | SanitizerKind::ShadowCallStack; enum CoverageFeature { CoverageFunc = 1 << 0, @@ -209,7 +213,7 @@ // All of these include ubsan. if (needsAsanRt() || needsMsanRt() || needsHwasanRt() || needsTsanRt() || needsDfsanRt() || needsLsanRt() || needsCfiDiagRt() || - (needsScudoRt() && !requiresMinimalRuntime())) + (needsScudoRt() && !requiresMinimalRuntime() && !requiresBelRuntime())) return false; return (Sanitizers.Mask & NeedsUbsanRt & ~TrapSanitizers.Mask) || @@ -266,6 +270,10 @@ Args.hasFlag(options::OPT_fsanitize_minimal_runtime, options::OPT_fno_sanitize_minimal_runtime, MinimalRuntime); + BelRuntime = + Args.hasFlag(options::OPT_fsanitize_bel_runtime, + options::OPT_fno_sanitize_bel_runtime, BelRuntime); + // The object size sanitizer should not be enabled at -O0. Arg *OptLevel = Args.getLastArg(options::OPT_O_Group); bool RemoveObjectSizeAtO0 = @@ -315,6 +323,17 @@ Add &= ~NotAllowedWithMinimalRuntime; } + if (BelRuntime) { + if (SanitizerMask KindsToDiagnose = + Add & NotAllowedWithBelRuntime & ~DiagnosedKinds) { + std::string Desc = describeSanitizeArg(*I, KindsToDiagnose); + D.Diag(diag::err_drv_argument_not_allowed_with) + << Desc << "-fsanitize-bel-runtime"; + DiagnosedKinds |= KindsToDiagnose; + } + Add &= ~NotAllowedWithBelRuntime; + } + // FIXME: Make CFI on member function calls compatible with cross-DSO CFI. // There are currently two problems: // - Virtual function call checks need to pass a pointer to the function @@ -371,6 +390,9 @@ if (MinimalRuntime) { Add &= ~NotAllowedWithMinimalRuntime; } + if (BelRuntime) { + Add &= ~NotAllowedWithBelRuntime; + } if (CfiCrossDso) Add &= ~SanitizerKind::CFIMFCall; Add &= Supported; @@ -668,6 +690,21 @@ << "fsanitize-trap=cfi"; } + if (BelRuntime) { + SanitizerMask IncompatibleMask = + Kinds & ~setGroupBits(CompatibleWithBelRuntime); + if (IncompatibleMask) + D.Diag(clang::diag::err_drv_argument_not_allowed_with) + << "-fsanitize-bel-runtime" + << lastArgumentForMask(D, Args, IncompatibleMask); + + SanitizerMask NonTrappingCfi = Kinds & SanitizerKind::CFI & ~TrappingKinds; + if (NonTrappingCfi) + D.Diag(clang::diag::err_drv_argument_only_allowed_with) + << "fsanitize-bel-runtime" + << "fsanitize-trap=cfi"; + } + // Parse -f(no-)?sanitize-coverage flags if coverage is supported by the // enabled sanitizers. for (const auto *Arg : Args) { @@ -1010,6 +1047,9 @@ if (MinimalRuntime) CmdArgs.push_back("-fsanitize-minimal-runtime"); + if (BelRuntime) + CmdArgs.push_back("-fsanitize-bel-runtime"); + if (AsanFieldPadding) CmdArgs.push_back(Args.MakeArgString("-fsanitize-address-field-padding=" + Twine(AsanFieldPadding))); diff --git a/clang/lib/Driver/ToolChains/CommonArgs.cpp b/clang/lib/Driver/ToolChains/CommonArgs.cpp --- a/clang/lib/Driver/ToolChains/CommonArgs.cpp +++ b/clang/lib/Driver/ToolChains/CommonArgs.cpp @@ -635,12 +635,16 @@ if (SanArgs.needsUbsanRt() && SanArgs.linkRuntimes()) { if (SanArgs.requiresMinimalRuntime()) SharedRuntimes.push_back("ubsan_minimal"); + else if (SanArgs.requiresBelRuntime()) + SharedRuntimes.push_back("ubsan_bel"); else SharedRuntimes.push_back("ubsan_standalone"); } if (SanArgs.needsScudoRt() && SanArgs.linkRuntimes()) { if (SanArgs.requiresMinimalRuntime()) SharedRuntimes.push_back("scudo_minimal"); + else if (SanArgs.requiresBelRuntime()) + SharedRuntimes.push_back("scudo_bel"); else SharedRuntimes.push_back("scudo"); } @@ -689,6 +693,8 @@ if (!SanArgs.needsSharedRt() && SanArgs.needsUbsanRt() && SanArgs.linkRuntimes()) { if (SanArgs.requiresMinimalRuntime()) { StaticRuntimes.push_back("ubsan_minimal"); + } else if (SanArgs.requiresBelRuntime()) { + StaticRuntimes.push_back("ubsan_bel"); } else { StaticRuntimes.push_back("ubsan_standalone"); if (SanArgs.linkCXXRuntimes()) @@ -717,6 +723,10 @@ StaticRuntimes.push_back("scudo_minimal"); if (SanArgs.linkCXXRuntimes()) StaticRuntimes.push_back("scudo_cxx_minimal"); + } else if (SanArgs.requiresBelRuntime()) { + StaticRuntimes.push_back("scudo_bel"); + if (SanArgs.linkCXXRuntimes()) + StaticRuntimes.push_back("scudo_cxx_bel"); } else { StaticRuntimes.push_back("scudo"); if (SanArgs.linkCXXRuntimes()) diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -1184,6 +1184,7 @@ OPT_fno_sanitize_memory_use_after_dtor, false); Opts.SanitizeMinimalRuntime = Args.hasArg(OPT_fsanitize_minimal_runtime); + Opts.SanitizeBelRuntime = Args.hasArg(OPT_fsanitize_bel_runtime); Opts.SanitizeCfiCrossDso = Args.hasArg(OPT_fsanitize_cfi_cross_dso); Opts.SanitizeCfiICallGeneralizePointers = Args.hasArg(OPT_fsanitize_cfi_icall_generalize_pointers); diff --git a/clang/test/CodeGen/unsigned-overflow-bel.c b/clang/test/CodeGen/unsigned-overflow-bel.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/unsigned-overflow-bel.c @@ -0,0 +1,21 @@ +// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsanitize=unsigned-integer-overflow -fsanitize-bel-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_bel_abort() + li = lj + lk; +} + +// CHECK-LABEL: define void @testlongsub() +void testlongsub() { + // CHECK: call void @__ubsan_handle_sub_overflow_bel_abort() + li = lj - lk; +} + +// CHECK-LABEL: define void @testlongmul() +void testlongmul() { + // CHECK: call void @__ubsan_handle_mul_overflow_bel_abort() + li = lj * lk; +} diff --git a/clang/test/Driver/fsanitize.c b/clang/test/Driver/fsanitize.c --- a/clang/test/Driver/fsanitize.c +++ b/clang/test/Driver/fsanitize.c @@ -809,6 +809,44 @@ // CHECK-SCS-MINIMAL: "-fsanitize=shadow-call-stack" // CHECK-SCS-MINIMAL: "-fsanitize-minimal-runtime" +// RUN: %clang -target x86_64-linux-gnu -fsanitize=address -fsanitize-bel-runtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-ASAN-BEL +// CHECK-ASAN-BEL: error: invalid argument '-fsanitize-bel-runtime' not allowed with '-fsanitize=address' + +// RUN: %clang -target x86_64-linux-gnu -fsanitize=thread -fsanitize-bel-runtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-TSAN-BEL +// CHECK-TSAN-BEL: error: invalid argument '-fsanitize-bel-runtime' not allowed with '-fsanitize=thread' + +// RUN: %clang -target x86_64-linux-gnu -fsanitize=undefined -fsanitize-bel-runtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UBSAN-BEL +// CHECK-UBSAN-BEL: "-fsanitize={{((signed-integer-overflow|integer-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),?){17}"}} +// CHECK-UBSAN-BEL: "-fsanitize-bel-runtime" + +// RUN: %clang -target x86_64-linux-gnu -fsanitize=undefined -fsanitize=function -fsanitize-bel-runtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UBSAN-FUNCTION-BEL +// CHECK-UBSAN-FUNCTION-BEL: error: invalid argument '-fsanitize=function' not allowed with '-fsanitize-bel-runtime' + +// RUN: %clang -target x86_64-linux-gnu -fsanitize=undefined -fsanitize=vptr -fsanitize-bel-runtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UBSAN-VPTR-BEL +// CHECK-UBSAN-VPTR-BEL: error: invalid argument '-fsanitize=vptr' not allowed with '-fsanitize-bel-runtime' + +// RUN: %clang -target x86_64-linux-gnu -fsanitize=address -fsanitize-bel-runtime -fsanitize=undefined %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-ASAN-UBSAN-BEL +// CHECK-ASAN-UBSAN-BEL: error: invalid argument '-fsanitize-bel-runtime' not allowed with '-fsanitize=address' +// RUN: %clang -target x86_64-linux-gnu -fsanitize=hwaddress -fsanitize-bel-runtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-HWASAN-BEL +// CHECK-HWASAN-BEL: error: invalid argument '-fsanitize-bel-runtime' not allowed with '-fsanitize=hwaddress' + +// RUN: %clang -target x86_64-linux-gnu -fsanitize=cfi -flto -fvisibility=hidden -fsanitize-bel-runtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-CFI-BEL +// CHECK-CFI-BEL: "-fsanitize=cfi-derived-cast,cfi-icall,cfi-mfcall,cfi-unrelated-cast,cfi-nvcall,cfi-vcall" +// CHECK-CFI-BEL: "-fsanitize-trap=cfi-derived-cast,cfi-icall,cfi-mfcall,cfi-unrelated-cast,cfi-nvcall,cfi-vcall" +// CHECK-CFI-BEL: "-fsanitize-bel-runtime" + +// RUN: %clang -target x86_64-linux-gnu -fsanitize=cfi -fno-sanitize-trap=cfi-icall -flto -fvisibility=hidden -fsanitize-bel-runtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-CFI-NOTRAP-BEL +// CHECK-CFI-NOTRAP-BEL: error: invalid argument 'fsanitize-bel-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-bel-runtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-CFI-NOICALL-BEL +// CHECK-CFI-NOICALL-BEL: "-fsanitize=cfi-derived-cast,cfi-mfcall,cfi-unrelated-cast,cfi-nvcall,cfi-vcall" +// CHECK-CFI-NOICALL-BEL: "-fsanitize-trap=cfi-derived-cast,cfi-mfcall,cfi-unrelated-cast,cfi-nvcall,cfi-vcall" +// CHECK-CFI-NOICALL-BEL: "-fsanitize-bel-runtime" + +// RUN: %clang -target x86_64-linux-gnu -fsanitize=shadow-call-stack -fsanitize-bel-runtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SCS-BEL +// CHECK-SCS-BEL: "-fsanitize=shadow-call-stack" +// CHECK-SCS-BEL: "-fsanitize-bel-runtime" + // RUN: %clang -target aarch64-linux-gnu -fsanitize=scudo %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SCUDO // RUN: %clang -target arm-linux-androideabi -fsanitize=scudo %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SCUDO // RUN: %clang -target x86_64-linux-gnu -fsanitize=scudo %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SCUDO @@ -837,6 +875,14 @@ // CHECK-SCUDO-UBSAN-MINIMAL: "-fsanitize={{.*}}scudo" // CHECK-SCUDO-UBSAN-MINIMAL: "-fsanitize-minimal-runtime" +// RUN: %clang -target x86_64-linux-gnu -fsanitize=scudo -fsanitize-bel-runtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SCUDO-BEL +// CHECK-SCUDO-BEL: "-fsanitize=scudo" +// CHECK-SCUDO-BEL: "-fsanitize-bel-runtime" + +// RUN: %clang -target x86_64-linux-gnu -fsanitize=undefined,scudo -fsanitize-bel-runtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SCUDO-UBSAN-BEL +// CHECK-SCUDO-UBSAN-BEL: "-fsanitize={{.*}}scudo" +// CHECK-SCUDO-UBSAN-BEL: "-fsanitize-bel-runtime" + // RUN: %clang -target powerpc-unknown-linux -fsanitize=scudo %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-NO-SCUDO // CHECK-NO-SCUDO: unsupported option @@ -886,5 +932,8 @@ // RUN: %clang -fsanitize=float-divide-by-zero -fsanitize-minimal-runtime %s -### 2>&1 | FileCheck %s --check-prefixes=CHECK-DIVBYZERO,CHECK-DIVBYZERO-MINIMAL // CHECK-DIVBYZERO-MINIMAL: "-fsanitize-minimal-runtime" +// RUN: %clang -fsanitize=float-divide-by-zero -fsanitize-bel-runtime %s -### 2>&1 | FileCheck %s --check-prefixes=CHECK-DIVBYZERO,CHECK-DIVBYZERO-BEL +// CHECK-DIVBYZERO-BEL: "-fsanitize-bel-runtime" + // RUN: %clang -fsanitize=undefined,float-divide-by-zero %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-DIVBYZERO-UBSAN // CHECK-DIVBYZERO-UBSAN: "-fsanitize={{.*}},float-divide-by-zero,{{.*}}" diff --git a/clang/test/Driver/sanitizer-ld.c b/clang/test/Driver/sanitizer-ld.c --- a/clang/test/Driver/sanitizer-ld.c +++ b/clang/test/Driver/sanitizer-ld.c @@ -374,6 +374,21 @@ // CHECK-UBSAN-MINIMAL-DARWIN: "{{.*}}ld{{(.exe)?}}" // CHECK-UBSAN-MINIMAL-DARWIN: "{{.*}}libclang_rt.ubsan_minimal_osx_dynamic.dylib" +// RUN: %clang -fsanitize=undefined -fsanitize-bel-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-BEL-LINUX %s +// CHECK-UBSAN-BEL-LINUX: "{{.*}}ld{{(.exe)?}}" +// CHECK-UBSAN-BEL-LINUX: "--whole-archive" "{{.*}}libclang_rt.ubsan_bel-i386.a" "--no-whole-archive" +// CHECK-UBSAN-BEL-LINUX: "-lpthread" + +// RUN: %clang -fsanitize=undefined -fsanitize-bel-runtime %s -### -o %t.o 2>&1 \ +// RUN: -target x86_64-apple-darwin -fuse-ld=ld \ +// RUN: --sysroot=%S/Inputs/basic_linux_tree \ +// RUN: | FileCheck --check-prefix=CHECK-UBSAN-BEL-DARWIN %s +// CHECK-UBSAN-BEL-DARWIN: "{{.*}}ld{{(.exe)?}}" +// CHECK-UBSAN-BEL-DARWIN: "{{.*}}libclang_rt.ubsan_bel_osx_dynamic.dylib" + // RUN: %clang -fsanitize=undefined %s -### -o %t.o 2>&1 \ // RUN: -target x86_64-apple-darwin -fuse-ld=ld -static-libsan \ // RUN: --sysroot=%S/Inputs/basic_linux_tree \ @@ -745,6 +760,15 @@ // CHECK-SCUDO-MINIMAL-LINUX: "--whole-archive" "{{.*}}libclang_rt.scudo_minimal-i386.a" "--no-whole-archive" // CHECK-SCUDO-MINIMAL-LINUX: "-lpthread" +// RUN: %clang -fsanitize=scudo -fsanitize-bel-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-SCUDO-BEL-LINUX %s +// CHECK-SCUDO-BEL-LINUX: "{{.*}}ld{{(.exe)?}}" +// CHECK-SCUDO-BEL-LINUX: "-pie" +// CHECK-SCUDO-BEL-LINUX: "--whole-archive" "{{.*}}libclang_rt.scudo_bel-i386.a" "--no-whole-archive" +// CHECK-SCUDO-BEL-LINUX: "-lpthread" + // RUN: %clang -no-canonical-prefixes %s -### -o %t.so -shared 2>&1 \ // RUN: -target i386-unknown-linux -fuse-ld=ld -fsanitize=scudo -shared-libsan \ // RUN: -resource-dir=%S/Inputs/resource_dir \ diff --git a/compiler-rt/cmake/config-ix.cmake b/compiler-rt/cmake/config-ix.cmake --- a/compiler-rt/cmake/config-ix.cmake +++ b/compiler-rt/cmake/config-ix.cmake @@ -615,7 +615,7 @@ endif() message(STATUS "Compiler-RT supported architectures: ${COMPILER_RT_SUPPORTED_ARCH}") -set(ALL_SANITIZERS asan;dfsan;msan;hwasan;tsan;safestack;cfi;scudo;ubsan_minimal;gwp_asan) +set(ALL_SANITIZERS asan;dfsan;msan;hwasan;tsan;safestack;cfi;scudo;ubsan_minimal;ubsan_bel;gwp_asan) set(COMPILER_RT_SANITIZERS_TO_BUILD all CACHE STRING "sanitizers to build if supported on the target (all;${ALL_SANITIZERS})") list_replace(COMPILER_RT_SANITIZERS_TO_BUILD all "${ALL_SANITIZERS}") @@ -708,8 +708,10 @@ if (COMPILER_RT_HAS_SANITIZER_COMMON AND UBSAN_SUPPORTED_ARCH AND OS_NAME MATCHES "Linux|FreeBSD|NetBSD|OpenBSD|Android|Darwin") set(COMPILER_RT_HAS_UBSAN_MINIMAL TRUE) + set(COMPILER_RT_HAS_UBSAN_BEL TRUE) else() set(COMPILER_RT_HAS_UBSAN_MINIMAL FALSE) + set(COMPILER_RT_HAS_UBSAN_BEL FALSE) endif() if (COMPILER_RT_HAS_SANITIZER_COMMON AND SAFESTACK_SUPPORTED_ARCH AND diff --git a/compiler-rt/lib/ubsan_bel/CMakeLists.txt b/compiler-rt/lib/ubsan_bel/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/compiler-rt/lib/ubsan_bel/CMakeLists.txt @@ -0,0 +1,53 @@ +# Build for the bel undefined behavior sanitizer runtime support library. + +set(UBSAN_BEL_SOURCES + ubsan_bel_handlers.cpp + ) + +include_directories(..) + +set(UBSAN_CFLAGS ${SANITIZER_COMMON_CFLAGS}) +append_rtti_flag(OFF UBSAN_CFLAGS) + +set(UBSAN_LINK_FLAGS ${SANITIZER_COMMON_LINK_FLAGS}) + +set(UBSAN_DYNAMIC_LIBS ${SANITIZER_COMMON_LINK_LIBS}) + +add_compiler_rt_component(ubsan-bel) + +# Common parts of bel UBSan runtime. +add_compiler_rt_object_libraries(RTUbsan_bel + OS ${SANITIZER_COMMON_SUPPORTED_OS} + ARCHS ${UBSAN_COMMON_SUPPORTED_ARCH} + SOURCES ${UBSAN_BEL_SOURCES} CFLAGS ${UBSAN_CFLAGS}) + + +if(COMPILER_RT_HAS_UBSAN_BEL) + # Standalone bel UBSan runtimes. + add_compiler_rt_runtime(clang_rt.ubsan_bel + STATIC + OS ${SANITIZER_COMMON_SUPPORTED_OS} + ARCHS ${UBSAN_SUPPORTED_ARCH} + OBJECT_LIBS RTUbsan_bel + CFLAGS ${UBSAN_CFLAGS} + PARENT_TARGET ubsan-bel) + + add_compiler_rt_runtime(clang_rt.ubsan_bel + SHARED + OS ${SANITIZER_COMMON_SUPPORTED_OS} + ARCHS ${UBSAN_SUPPORTED_ARCH} + OBJECT_LIBS RTUbsan_bel + CFLAGS ${UBSAN_CFLAGS} + LINK_FLAGS ${UBSAN_LINK_FLAGS} + LINK_LIBS ${UBSAN_DYNAMIC_LIBS} + PARENT_TARGET ubsan-bel) + + if (SANITIZER_USE_SYMBOLS AND NOT APPLE) + set(ARCHS_FOR_SYMBOLS ${UBSAN_SUPPORTED_ARCH}) + list(REMOVE_ITEM ARCHS_FOR_SYMBOLS i386 i686) + add_sanitizer_rt_symbols(clang_rt.ubsan_bel + ARCHS ${ARCHS_FOR_SYMBOLS} + PARENT_TARGET ubsan-bel + EXTRA ubsan.syms.extra) + endif() +endif() diff --git a/compiler-rt/lib/ubsan_bel/ubsan.syms.extra b/compiler-rt/lib/ubsan_bel/ubsan.syms.extra new file mode 100644 --- /dev/null +++ b/compiler-rt/lib/ubsan_bel/ubsan.syms.extra @@ -0,0 +1 @@ +__ubsan_* diff --git a/compiler-rt/lib/ubsan_bel/ubsan_bel_handlers.cpp b/compiler-rt/lib/ubsan_bel/ubsan_bel_handlers.cpp new file mode 100644 --- /dev/null +++ b/compiler-rt/lib/ubsan_bel/ubsan_bel_handlers.cpp @@ -0,0 +1,158 @@ +//===-- ubsan_bel_handlers.cpp --------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Entry points for the UBSan ␇ runtime. +// +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_atomic.h" +#include "sanitizer_common/sanitizer_platform.h" + +#include +#include +#include +#include +#include + +static void message(const char *msg, void *address = nullptr) { +#if SANITIZER_MAC + auto quip = []() { + static const char *quips[] = { + "I'm so sorry", + "Maybe running the program again will avoid the problem next time", + "That sounds pretty bad", + "This must have hurt", + "Please stop", + "Did you have to?", + "I bet you didn't know this particular thing was undefined!", + "Why would you even do this?", + "Wow, that was a sweet jump!", + "This Is Just To Say: I have optimized out the statements that were in the program and which you were probably saving for correctness. Forgive me, they were delicious. So undefined, and so fast", + "Now go away or I shall taunt you a second time", + "Explain to me again how sheep's bladders may be employed to prevent earthquakes", + "You sons of silly persons", + }; + static std::random_device r; + static std::default_random_engine e(r()); + static std::uniform_int_distribution d(0, sizeof(quips) / sizeof(quips[0]) - 1); + return quips[d(e)]; + }; + char buf[2048] = { '\0' }; + size_t pos = 0; + const size_t end = sizeof(buf) / sizeof(buf[0]) - 1; + auto cp = [&] (const char *what) { + while (*what && pos < end) + buf[pos++] = *what++; + }; + cp("say \"Undefined Behavior "); + cp(msg); + if (address && (pos + 28 < end)) { + cp(" at address "); + pos += sprintf(&buf[pos], "%16llx", (uint64_t)address); + } + cp("... "); + cp(quip()); + cp("\""); + system(buf); +#else + // Plain Old Bell (POB). + (void)msg; + write(2, "\a", strlen("\a")); +#endif +} + +static const int kMaxCallerPcs = 20; +static __sanitizer::atomic_uintptr_t caller_pcs[kMaxCallerPcs]; +static __sanitizer::atomic_uint32_t caller_pcs_sz; + +__attribute__((noinline)) static bool report_this_error(void *caller_p) { + uintptr_t caller = reinterpret_cast(caller_p); + if (caller == 0) return false; + while (true) { + unsigned sz = __sanitizer::atomic_load_relaxed(&caller_pcs_sz); + if (sz > kMaxCallerPcs) return false; + if (sz > 0 && sz < kMaxCallerPcs) { + uintptr_t p; + for (unsigned i = 0; i < sz; ++i) { + p = __sanitizer::atomic_load_relaxed(&caller_pcs[i]); + if (p == 0) break; // Concurrent update. + if (p == caller) return false; + } + if (p == 0) continue; + } + + if (!__sanitizer::atomic_compare_exchange_strong( + &caller_pcs_sz, &sz, sz + 1, __sanitizer::memory_order_seq_cst)) + continue; + + if (sz == kMaxCallerPcs) { + message("is over 9000, there's no way that could be right"); +#if SANITIZER_MAC + system("open https://www.youtube.com/watch?v=SiMHTK15Pik"); +#endif + return false; + } + __sanitizer::atomic_store_relaxed(&caller_pcs[sz], caller); + return true; + } +} + +#if SANITIZER_DEBUG +namespace __sanitizer { +// The DCHECK macro needs this symbol to be defined. +void NORETURN CheckFailed(const char *file, int, const char *cond, u64, u64) { + message("Sanitizer CHECK failed: "); + message(file); + message(":?? : "); // FIXME: Show line number. + message(cond); + abort(); +} +} // namespace __sanitizer +#endif + +#define INTERFACE extern "C" __attribute__((visibility("default"))) + +#define HANDLER_RECOVER(name, msg) \ + INTERFACE void __ubsan_handle_##name##_bel() { \ + if (!report_this_error(__builtin_return_address(0))) return; \ + message(msg, __builtin_return_address(0)); \ + } + +#define HANDLER_NORECOVER(name, msg) \ + INTERFACE void __ubsan_handle_##name##_bel_abort() { \ + message(msg, __builtin_return_address(0)); \ + abort(); \ + } + +#define HANDLER(name, msg) \ + HANDLER_RECOVER(name, msg) \ + HANDLER_NORECOVER(name, msg) + +HANDLER(type_mismatch, "type mismatch") +HANDLER(alignment_assumption, "alignment assumption") +HANDLER(add_overflow, "add overflow") +HANDLER(sub_overflow, "subtraction overflow") +HANDLER(mul_overflow, "multiplication overflow") +HANDLER(negate_overflow, "negate overflow") +HANDLER(divrem_overflow, "divrem overflow") +HANDLER(shift_out_of_bounds, "shift out of bounds") +HANDLER(out_of_bounds, "out of bounds") +HANDLER_RECOVER(builtin_unreachable, "builtin unreachable") +HANDLER_RECOVER(missing_return, "missing return") +HANDLER(vla_bound_not_positive, "variable length array bound not positive") +HANDLER(float_cast_overflow, "float cast overflow") +HANDLER(load_invalid_value, "load invalid value") +HANDLER(invalid_builtin, "invalid builtin") +HANDLER(function_type_mismatch, "function type mismatch") +HANDLER(implicit_conversion, "implicit conversion") +HANDLER(nonnull_arg, "non-null argument") +HANDLER(nonnull_return, "non-null return") +HANDLER(nullability_arg, "nullability argument") +HANDLER(nullability_return, "nullability return") +HANDLER(pointer_overflow, "pointer overflow") +HANDLER(cfi_check_fail, "control flow integrity check fail") diff --git a/compiler-rt/test/ubsan_bel/CMakeLists.txt b/compiler-rt/test/ubsan_bel/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/compiler-rt/test/ubsan_bel/CMakeLists.txt @@ -0,0 +1,26 @@ +set(UBSAN_LIT_TESTS_DIR ${CMAKE_CURRENT_SOURCE_DIR}) + +set(UBSAN_TEST_ARCH ${UBSAN_SUPPORTED_ARCH}) +if(APPLE) + darwin_filter_host_archs(UBSAN_SUPPORTED_ARCH UBSAN_TEST_ARCH) +endif() + +set(UBSAN_TESTSUITES) +set(UBSAN_TEST_DEPS ${SANITIZER_COMMON_LIT_TEST_DEPS}) +if(NOT COMPILER_RT_STANDALONE_BUILD) + list(APPEND UBSAN_TEST_DEPS ubsan-bel) +endif() + +foreach(arch ${UBSAN_TEST_ARCH}) + get_test_cc_for_arch(${arch} UBSAN_TEST_TARGET_CC UBSAN_TEST_TARGET_CFLAGS) + set(CONFIG_NAME ${arch}) + configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.py.in + ${CMAKE_CURRENT_BINARY_DIR}/${CONFIG_NAME}/lit.site.cfg.py) + list(APPEND UBSAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/${CONFIG_NAME}) +endforeach() + +add_lit_testsuite(check-ubsan-bel "Running UndefinedBehaviorSanitizerBel tests" + ${UBSAN_TESTSUITES} + DEPENDS ${UBSAN_TEST_DEPS}) +set_target_properties(check-ubsan-bel PROPERTIES FOLDER "Compiler-RT Misc") diff --git a/compiler-rt/test/ubsan_bel/TestCases/alignment-assumption.c b/compiler-rt/test/ubsan_bel/TestCases/alignment-assumption.c new file mode 100644 --- /dev/null +++ b/compiler-rt/test/ubsan_bel/TestCases/alignment-assumption.c @@ -0,0 +1,17 @@ +// RUN: %clang -fsanitize=alignment %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefixes=CHECK + +#include + +int main(int argc, char* argv[]) { +// CHECK-NOT: alignment-assumption + +char *ptr = (char *)malloc(2); + +__builtin_assume_aligned(ptr + 1, 0x8000); +// CHECK: alignment-assumption +// CHECK-NOT: alignment-assumption + +free(ptr); + +return 0; +} diff --git a/compiler-rt/test/ubsan_bel/TestCases/implicit-integer-sign-change.c b/compiler-rt/test/ubsan_bel/TestCases/implicit-integer-sign-change.c new file mode 100644 --- /dev/null +++ b/compiler-rt/test/ubsan_bel/TestCases/implicit-integer-sign-change.c @@ -0,0 +1,17 @@ +// RUN: %clang -fsanitize=implicit-integer-sign-change %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefixes=CHECK + +#include + +int main() { +// CHECK-NOT: implicit-conversion + + // Explicitly casting hides it, + int32_t n0 = (int32_t)(~((uint32_t)0)); + + // Positive tests. + int32_t t0 = (~((uint32_t)0)); +// CHECK: implicit-conversion +// CHECK-NOT: implicit-conversion + + return 0; +} diff --git a/compiler-rt/test/ubsan_bel/TestCases/implicit-signed-integer-truncation-or-sign-change.c b/compiler-rt/test/ubsan_bel/TestCases/implicit-signed-integer-truncation-or-sign-change.c new file mode 100644 --- /dev/null +++ b/compiler-rt/test/ubsan_bel/TestCases/implicit-signed-integer-truncation-or-sign-change.c @@ -0,0 +1,17 @@ +// RUN: %clang -fsanitize=implicit-signed-integer-truncation,implicit-integer-sign-change %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefixes=CHECK + +#include + +int main() { +// CHECK-NOT: implicit-conversion + + // Explicitly casting hides it, + int8_t n0 = (int8_t)((uint32_t)-1); + + // Positive tests. + int8_t t0 = (uint32_t)-1; +// CHECK: implicit-conversion +// CHECK-NOT: implicit-conversion + + return 0; +} diff --git a/compiler-rt/test/ubsan_bel/TestCases/implicit-signed-integer-truncation.c b/compiler-rt/test/ubsan_bel/TestCases/implicit-signed-integer-truncation.c new file mode 100644 --- /dev/null +++ b/compiler-rt/test/ubsan_bel/TestCases/implicit-signed-integer-truncation.c @@ -0,0 +1,25 @@ +// RUN: %clang -fsanitize=implicit-signed-integer-truncation %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefixes=CHECK + +#include + +int main() { +// CHECK-NOT: implicit-conversion + + // Negative tests. Even if they produce unexpected results, this sanitizer does not care. + int8_t n0 = (~((uint32_t)(0))); // ~0 -> -1, but do not warn. + uint8_t n2 = 128; + uint8_t n3 = 255; + // Bools do not count + _Bool b0 = (~((uint32_t)(0))); + _Bool b1 = 255; + + // Explicit and-ing of bits will silence it. + uint8_t nc0 = ((int32_t)(-1)) & 255; + + // Positive tests. + uint8_t t0 = (int32_t)(-1); +// CHECK: implicit-conversion +// CHECK-NOT: implicit-conversion + + return 0; +} diff --git a/compiler-rt/test/ubsan_bel/TestCases/implicit-unsigned-integer-truncation.c b/compiler-rt/test/ubsan_bel/TestCases/implicit-unsigned-integer-truncation.c new file mode 100644 --- /dev/null +++ b/compiler-rt/test/ubsan_bel/TestCases/implicit-unsigned-integer-truncation.c @@ -0,0 +1,25 @@ +// RUN: %clang -fsanitize=implicit-unsigned-integer-truncation %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefixes=CHECK + +#include + +int main() { +// CHECK-NOT: implicit-conversion + + // Negative tests. Even if they produce unexpected results, this sanitizer does not care. + int8_t n0 = (~((uint32_t)(0))); // ~0 -> -1, but do not warn. + uint8_t n2 = 128; + uint8_t n3 = 255; + // Bools do not count + _Bool b0 = (~((uint32_t)(0))); + _Bool b1 = 255; + + // Explicit and-ing of bits will silence it. + uint8_t nc0 = ((~((uint32_t)(0))) & 255); + + // Positive tests. + uint8_t t0 = (~((uint32_t)(0))); +// CHECK: implicit-conversion +// CHECK-NOT: implicit-conversion + + return 0; +} diff --git a/compiler-rt/test/ubsan_bel/TestCases/nullptr-and-nonzero-offset.c b/compiler-rt/test/ubsan_bel/TestCases/nullptr-and-nonzero-offset.c new file mode 100644 --- /dev/null +++ b/compiler-rt/test/ubsan_bel/TestCases/nullptr-and-nonzero-offset.c @@ -0,0 +1,22 @@ +// RUN: %clang -fsanitize=pointer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefixes=CHECK,CHECK-C --implicit-check-not="pointer-overflow" +// RUN: %clangxx -fsanitize=pointer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefixes=CHECK,CHECK-CPP --implicit-check-not="pointer-overflow" + +#include + +int main(int argc, char *argv[]) { + char *base, *result; + + base = (char *)0; + result = base + 0; + // CHECK-C: pointer-overflow + + base = (char *)0; + result = base + 1; + // CHECK: pointer-overflow + + base = (char *)1; + result = base - 1; + // CHECK: pointer-overflow + + return 0; +} diff --git a/compiler-rt/test/ubsan_bel/TestCases/recover-dedup-limit.cpp b/compiler-rt/test/ubsan_bel/TestCases/recover-dedup-limit.cpp new file mode 100644 --- /dev/null +++ b/compiler-rt/test/ubsan_bel/TestCases/recover-dedup-limit.cpp @@ -0,0 +1,41 @@ +// RUN: %clangxx -fsanitize=signed-integer-overflow -fsanitize-recover=all %s -o %t && %run %t 2>&1 | FileCheck %s + +#include + +#define OVERFLOW \ + x = 0x7FFFFFFE; \ + x += __LINE__ + +int main() { + int32_t x; + OVERFLOW; // CHECK: add-overflow + OVERFLOW; // CHECK: add-overflow + OVERFLOW; // CHECK: add-overflow + OVERFLOW; // CHECK: add-overflow + OVERFLOW; // CHECK: add-overflow + + OVERFLOW; // CHECK: add-overflow + OVERFLOW; // CHECK: add-overflow + OVERFLOW; // CHECK: add-overflow + OVERFLOW; // CHECK: add-overflow + OVERFLOW; // CHECK: add-overflow + + OVERFLOW; // CHECK: add-overflow + OVERFLOW; // CHECK: add-overflow + OVERFLOW; // CHECK: add-overflow + OVERFLOW; // CHECK: add-overflow + OVERFLOW; // CHECK: add-overflow + + OVERFLOW; // CHECK: add-overflow + OVERFLOW; // CHECK: add-overflow + OVERFLOW; // CHECK: add-overflow + OVERFLOW; // CHECK: add-overflow + OVERFLOW; // CHECK: add-overflow + + // CHECK-NOT: add-overflow + OVERFLOW; // CHECK: too many errors + // CHECK-NOT: add-overflow + OVERFLOW; + OVERFLOW; + OVERFLOW; +} diff --git a/compiler-rt/test/ubsan_bel/TestCases/recover-dedup.cpp b/compiler-rt/test/ubsan_bel/TestCases/recover-dedup.cpp new file mode 100644 --- /dev/null +++ b/compiler-rt/test/ubsan_bel/TestCases/recover-dedup.cpp @@ -0,0 +1,39 @@ +// RUN: %clangxx -w -fsanitize=signed-integer-overflow,nullability-return,returns-nonnull-attribute -fsanitize-recover=all %s -o %t && %run %t 2>&1 | FileCheck %s + +#include +#include + +int *_Nonnull h() { + // CHECK: nullability-return + return NULL; +} + +__attribute__((returns_nonnull)) +int *i() { + // CHECK: nonnull-return + return NULL; +} + +__attribute__((noinline)) +int f(int x, int y) { + // CHECK: mul-overflow + return x * y; +} + +__attribute__((noinline)) +int g(int x, int y) { + // CHECK: mul-overflow + return x * (y + 1); +} + +int main() { + h(); + i(); + int x = 2; + for (int i = 0; i < 10; ++i) + x = f(x, x); + x = 2; + for (int i = 0; i < 10; ++i) + x = g(x, x); + // CHECK-NOT: mul-overflow +} diff --git a/compiler-rt/test/ubsan_bel/TestCases/test-darwin-interface.c b/compiler-rt/test/ubsan_bel/TestCases/test-darwin-interface.c new file mode 100644 --- /dev/null +++ b/compiler-rt/test/ubsan_bel/TestCases/test-darwin-interface.c @@ -0,0 +1,16 @@ +// Check that the ubsan and ubsan-minimal runtimes have the same symbols, +// making exceptions as necessary. +// +// REQUIRES: x86_64-darwin + +// RUN: nm -jgU `%clangxx -fsanitize-minimal-runtime -fsanitize=undefined %s -o %t '-###' 2>&1 | grep "libclang_rt.ubsan_minimal_osx_dynamic.dylib" | sed -e 's/.*"\(.*libclang_rt.ubsan_minimal_osx_dynamic.dylib\)".*/\1/'` | grep "^___ubsan_handle" \ +// RUN: | sed 's/_minimal//g' \ +// RUN: > %t.minimal.symlist +// +// RUN: nm -jgU `%clangxx -fno-sanitize-minimal-runtime -fsanitize=undefined %s -o %t '-###' 2>&1 | grep "libclang_rt.ubsan_osx_dynamic.dylib" | sed -e 's/.*"\(.*libclang_rt.ubsan_osx_dynamic.dylib\)".*/\1/'` | grep "^___ubsan_handle" \ +// RUN: | grep -vE "^___ubsan_handle_dynamic_type_cache_miss" \ +// RUN: | grep -vE "^___ubsan_handle_cfi_bad_type" \ +// RUN: | sed 's/_v1//g' \ +// RUN: > %t.full.symlist +// +// RUN: diff %t.minimal.symlist %t.full.symlist diff --git a/compiler-rt/test/ubsan_bel/TestCases/uadd-overflow.cpp b/compiler-rt/test/ubsan_bel/TestCases/uadd-overflow.cpp new file mode 100644 --- /dev/null +++ b/compiler-rt/test/ubsan_bel/TestCases/uadd-overflow.cpp @@ -0,0 +1,10 @@ +// RUN: %clangxx -fsanitize=unsigned-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s +// RUN: %clangxx -fsanitize=unsigned-integer-overflow -fno-sanitize-recover=all %s -o %t && not --crash %run %t 2>&1 | FileCheck %s + +#include + +int main() { + uint32_t k = 0x87654321; + k += 0xedcba987; + // CHECK: add-overflow +} diff --git a/compiler-rt/test/ubsan_bel/lit.common.cfg.py b/compiler-rt/test/ubsan_bel/lit.common.cfg.py new file mode 100644 --- /dev/null +++ b/compiler-rt/test/ubsan_bel/lit.common.cfg.py @@ -0,0 +1,40 @@ +# -*- Python -*- + +import os + +def get_required_attr(config, attr_name): + attr_value = getattr(config, attr_name, None) + if attr_value == None: + lit_config.fatal( + "No attribute %r in test configuration! You may need to run " + "tests from your build directory or add this attribute " + "to lit.site.cfg.py " % attr_name) + return attr_value + +# Setup source root. +config.test_source_root = os.path.dirname(__file__) +config.name = 'UBSan-Bel-' + config.target_arch + +def build_invocation(compile_flags): + return " " + " ".join([config.clang] + compile_flags) + " " + +target_cflags = [get_required_attr(config, "target_cflags")] +clang_ubsan_cflags = ["-fsanitize-bel-runtime"] + target_cflags +clang_ubsan_cxxflags = config.cxx_mode_flags + clang_ubsan_cflags + +# Define %clang and %clangxx substitutions to use in test RUN lines. +config.substitutions.append( ("%clang ", build_invocation(clang_ubsan_cflags)) ) +config.substitutions.append( ("%clangxx ", build_invocation(clang_ubsan_cxxflags)) ) + +# Default test suffixes. +config.suffixes = ['.c', '.cpp'] + +# Check that the host supports UndefinedBehaviorSanitizerBel tests +if config.host_os not in ['Linux', 'FreeBSD', 'NetBSD', 'Darwin', 'OpenBSD']: # TODO: Windows + config.unsupported = True + +# Don't target x86_64h if the test machine can't execute x86_64h binaries. +if '-arch x86_64h' in target_cflags and 'x86_64h' not in config.available_features: + config.unsupported = True + +config.available_features.add('arch=' + config.target_arch) diff --git a/compiler-rt/test/ubsan_bel/lit.site.cfg.py.in b/compiler-rt/test/ubsan_bel/lit.site.cfg.py.in new file mode 100644 --- /dev/null +++ b/compiler-rt/test/ubsan_bel/lit.site.cfg.py.in @@ -0,0 +1,11 @@ +@LIT_SITE_CFG_IN_HEADER@ + +# Tool-specific config options. +config.target_cflags = "@UBSAN_TEST_TARGET_CFLAGS@" +config.target_arch = "@UBSAN_TEST_TARGET_ARCH@" + +# Load common config for all compiler-rt lit tests. +lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/test/lit.common.configured") + +# Load tool-specific config that would do the real work. +lit_config.load_config(config, "@UBSAN_LIT_TESTS_DIR@/lit.common.cfg.py")