diff --git a/driver/cl_options_sanitizers.cpp b/driver/cl_options_sanitizers.cpp index 67ebe34f949..7808afc0067 100644 --- a/driver/cl_options_sanitizers.cpp +++ b/driver/cl_options_sanitizers.cpp @@ -50,6 +50,12 @@ cl::list fSanitizeCoverage( llvm::SanitizerCoverageOptions sanitizerCoverageOptions; +cl::list + fSanitizeRecover("fsanitize-recover", cl::CommaSeparated, + cl::desc("Enable recovery for specified sanitizers ('all' " + "= enable for all supported sanitizers)."), + cl::value_desc("name")); + SanitizerBits parseFSanitizeCmdlineParameter() { SanitizerBits retval = 0; for (const auto &name : fSanitize) { @@ -61,6 +67,24 @@ SanitizerBits parseFSanitizeCmdlineParameter() { return retval; } +SanitizerBits parseFSanitizeRecoverCmdlineParameter() { + SanitizerBits retval = 0; + for (const auto &name : fSanitizeRecover) { + if (name == "all") { + retval |= ~SanitizerBits(0); + continue; + } + SanitizerCheck check = parseSanitizerName(name, [&name] { + error(Loc(), "fsanitize-recover: Unrecognized sanitizer name '%s'.", name.c_str()); + }); + if (check & supportedSanitizerRecoveries) + retval |= SanitizerBits(check); + else + error(Loc(), "fsanitize-recover: Recovery not supported for '%s'.", name.c_str()); + } + return retval; +} + void parseFSanitizeCoverageParameter(llvm::StringRef name, llvm::SanitizerCoverageOptions &opts) { if (name == "func") { @@ -148,6 +172,8 @@ cl::opt fSanitizeAddressUseAfterReturn( "but not as much as never). Requires druntime support."))); SanitizerBits enabledSanitizers = 0; +SanitizerBits enabledSanitizerRecoveries = 0; +const SanitizerBits supportedSanitizerRecoveries = AddressSanitizer | MemorySanitizer; // Parse sanitizer name passed on commandline and return the corresponding // sanitizer bits. @@ -171,6 +197,7 @@ SanitizerCheck parseSanitizerName(llvm::StringRef name, void initializeSanitizerOptionsFromCmdline() { enabledSanitizers |= parseFSanitizeCmdlineParameter(); + enabledSanitizerRecoveries |= parseFSanitizeRecoverCmdlineParameter(); auto &sancovOpts = sanitizerCoverageOptions; diff --git a/driver/cl_options_sanitizers.h b/driver/cl_options_sanitizers.h index c7a1acf9d98..16b38f70c8f 100644 --- a/driver/cl_options_sanitizers.h +++ b/driver/cl_options_sanitizers.h @@ -37,6 +37,8 @@ enum SanitizerCheck : SanitizerBits { LeakSanitizer = 1 << 5, }; extern SanitizerBits enabledSanitizers; +extern SanitizerBits enabledSanitizerRecoveries; +extern const SanitizerBits supportedSanitizerRecoveries; extern cl::opt fSanitizeAddressUseAfterReturn; @@ -45,6 +47,10 @@ inline bool isSanitizerEnabled(SanitizerBits san) { return enabledSanitizers & san; } +inline bool isSanitizerRecoveryEnabled(SanitizerBits san) { + return enabledSanitizerRecoveries & san; +} + SanitizerCheck parseSanitizerName(llvm::StringRef name, std::function actionUponError); diff --git a/driver/linker-gcc.cpp b/driver/linker-gcc.cpp index a0aad16a972..800e134b56c 100644 --- a/driver/linker-gcc.cpp +++ b/driver/linker-gcc.cpp @@ -508,6 +508,13 @@ void ArgsBuilder::addSanitizers(const llvm::Triple &triple) { if (opts::isSanitizerEnabled(opts::ThreadSanitizer)) { addSanitizerLinkFlags(triple, "tsan", "-fsanitize=thread"); } + + if (opts::isSanitizerRecoveryEnabled(opts::AddressSanitizer)) { + args.push_back("-fsanitize-recover=address"); + } + if (opts::isSanitizerRecoveryEnabled(opts::MemorySanitizer)) { + args.push_back("-fsanitize-recover=memory"); + } } ////////////////////////////////////////////////////////////////////////////// diff --git a/gen/optimizer.cpp b/gen/optimizer.cpp index e8936d0f7ef..1e15789156e 100644 --- a/gen/optimizer.cpp +++ b/gen/optimizer.cpp @@ -184,7 +184,7 @@ static void addAddressSanitizerPasses(ModulePassManager &mpm, OptimizationLevel level ) { AddressSanitizerOptions aso; aso.CompileKernel = false; - aso.Recover = false; + aso.Recover = opts::isSanitizerRecoveryEnabled(opts::AddressSanitizer); aso.UseAfterScope = true; aso.UseAfterReturn = opts::fSanitizeAddressUseAfterReturn; @@ -199,7 +199,7 @@ static void addMemorySanitizerPass(ModulePassManager &mpm, FunctionPassManager &fpm, OptimizationLevel level ) { int trackOrigins = fSanitizeMemoryTrackOrigins; - bool recover = false; + bool recover = opts::isSanitizerRecoveryEnabled(opts::MemorySanitizer); bool kernel = false; #if LDC_LLVM_VER >= 1600 mpm.addPass(MemorySanitizerPass( diff --git a/tests/sanitizers/asan_recover_stackoverflow.d b/tests/sanitizers/asan_recover_stackoverflow.d new file mode 100644 index 00000000000..c77040b1a0a --- /dev/null +++ b/tests/sanitizers/asan_recover_stackoverflow.d @@ -0,0 +1,32 @@ +// Test recovery with AddressSanitizer + +// REQUIRES: ASan + +// RUN: %ldc -g -fsanitize=address -fsanitize-recover=address %s -of=%t%exe +// RUN: not %t%exe 2>&1 | FileCheck %s +// RUN: %env_asan_opts=halt_on_error=true not %t%exe 2>&1 | FileCheck %s +// RUN: %env_asan_opts=halt_on_error=false %t%exe 2>&1 | FileCheck %s + +// RUN: %ldc -g -fsanitize=address -fsanitize-recover=all %s -of=%t%exe +// RUN: not %t%exe 2>&1 | FileCheck %s +// RUN: %env_asan_opts=halt_on_error=true not %t%exe 2>&1 | FileCheck %s +// RUN: %env_asan_opts=halt_on_error=false %t%exe 2>&1 | FileCheck %s + +void foo(int* arr) +{ + // CHECK: stack-buffer-overflow + // CHECK: WRITE of size 4 + // CHECK-NEXT: #0 {{.*}} in {{.*foo.*}} {{.*}}asan_recover_stackoverflow.d:[[@LINE+1]] + arr[10] = 1; +} + +// CHECK: Address {{.*}} is located in stack of +// CHECK-NEXT: #0 {{.*}} in {{.*main.*}} {{.*}}asan_recover_stackoverflow.d:[[@LINE+1]] +void main() +{ + // Test for the name of the variable that is overflown. + // CHECK: 'aiaiaiaiaiaiai'{{.*}} <== {{.*}} overflows this variable + int[10] aiaiaiaiaiaiai; + int b; + foo(&aiaiaiaiaiaiai[0]); +}