Skip to content

Commit

Permalink
Implement -fsanitize-recover for ASan and MSan. (#4783)
Browse files Browse the repository at this point in the history
Resolves #4770
  • Loading branch information
JohanEngelen authored Nov 29, 2024
1 parent e241b93 commit c421e4c
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 2 deletions.
27 changes: 27 additions & 0 deletions driver/cl_options_sanitizers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ cl::list<std::string> fSanitizeCoverage(

llvm::SanitizerCoverageOptions sanitizerCoverageOptions;

cl::list<std::string>
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) {
Expand All @@ -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") {
Expand Down Expand Up @@ -148,6 +172,8 @@ cl::opt<llvm::AsanDetectStackUseAfterReturnMode> 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.
Expand All @@ -171,6 +197,7 @@ SanitizerCheck parseSanitizerName(llvm::StringRef name,
void initializeSanitizerOptionsFromCmdline()
{
enabledSanitizers |= parseFSanitizeCmdlineParameter();
enabledSanitizerRecoveries |= parseFSanitizeRecoverCmdlineParameter();

auto &sancovOpts = sanitizerCoverageOptions;

Expand Down
6 changes: 6 additions & 0 deletions driver/cl_options_sanitizers.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ enum SanitizerCheck : SanitizerBits {
LeakSanitizer = 1 << 5,
};
extern SanitizerBits enabledSanitizers;
extern SanitizerBits enabledSanitizerRecoveries;
extern const SanitizerBits supportedSanitizerRecoveries;

extern cl::opt<llvm::AsanDetectStackUseAfterReturnMode> fSanitizeAddressUseAfterReturn;

Expand All @@ -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<void()> actionUponError);

Expand Down
7 changes: 7 additions & 0 deletions driver/linker-gcc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
}

//////////////////////////////////////////////////////////////////////////////
Expand Down
4 changes: 2 additions & 2 deletions gen/optimizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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(
Expand Down
32 changes: 32 additions & 0 deletions tests/sanitizers/asan_recover_stackoverflow.d
Original file line number Diff line number Diff line change
@@ -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]);
}

0 comments on commit c421e4c

Please sign in to comment.