Skip to content

Commit 5446d33

Browse files
authored
Merge pull request swiftlang#28126 from danliew-apple/rdar_56346688
[Sanitizers] Add Driver/Frontend option to enable sanitizer instrumentation that supports error recovery
2 parents 0a3a130 + 63e7290 commit 5446d33

File tree

13 files changed

+223
-2
lines changed

13 files changed

+223
-2
lines changed

include/swift/AST/DiagnosticsFrontend.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,10 @@ ERROR(error_option_requires_sanitizer, none,
7171
"option '%0' requires a sanitizer to be enabled. Use -sanitize= to "
7272
"enable a sanitizer", (StringRef))
7373

74+
WARNING(warning_option_requires_specific_sanitizer, none,
75+
"option '%0' has no effect when '%1' sanitizer is disabled. Use -sanitize=%1 to "
76+
"enable the sanitizer", (StringRef, StringRef))
77+
7478
ERROR(error_option_missing_required_argument, none,
7579
"option '%0' is missing a required argument (%1)", (StringRef, StringRef))
7680

include/swift/AST/IRGenOptions.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,9 @@ class IRGenOptions {
106106
/// Which sanitizer is turned on.
107107
OptionSet<SanitizerKind> Sanitizers;
108108

109+
/// Which sanitizer(s) have recovery instrumentation enabled.
110+
OptionSet<SanitizerKind> SanitizersWithRecoveryInstrumentation;
111+
109112
/// Path prefixes that should be rewritten in debug info.
110113
PathRemapper DebugPrefixMap;
111114

@@ -239,6 +242,7 @@ class IRGenOptions {
239242
: DWARFVersion(2), OutputKind(IRGenOutputKind::LLVMAssembly),
240243
Verify(true), OptMode(OptimizationMode::NotSet),
241244
Sanitizers(OptionSet<SanitizerKind>()),
245+
SanitizersWithRecoveryInstrumentation(OptionSet<SanitizerKind>()),
242246
DebugInfoLevel(IRGenDebugInfoLevel::None),
243247
DebugInfoFormat(IRGenDebugInfoFormat::None),
244248
DisableClangModuleSkeletonCUs(false), UseJIT(false),

include/swift/Option/Options.td

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -889,6 +889,15 @@ def sanitize_EQ : CommaJoined<["-"], "sanitize=">,
889889
Flags<[FrontendOption, NoInteractiveOption]>, MetaVarName<"<check>">,
890890
HelpText<"Turn on runtime checks for erroneous behavior.">;
891891

892+
def sanitize_recover_EQ
893+
: CommaJoined<["-"], "sanitize-recover=">,
894+
Flags<[FrontendOption, NoInteractiveOption]>,
895+
MetaVarName<"<check>">,
896+
HelpText<"Specify which sanitizer runtime checks (see -sanitize=) will "
897+
"generate instrumentation that allows error recovery. Listed "
898+
"checks should be comma separated. Default behavior is to not "
899+
"allow error recovery.">;
900+
892901
def sanitize_coverage_EQ : CommaJoined<["-"], "sanitize-coverage=">,
893902
Flags<[FrontendOption, NoInteractiveOption]>,
894903
MetaVarName<"<type>">,

include/swift/Option/SanitizerOptions.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ OptionSet<SanitizerKind> parseSanitizerArgValues(
3636
const llvm::Triple &Triple, DiagnosticEngine &Diag,
3737
llvm::function_ref<bool(llvm::StringRef, bool)> sanitizerRuntimeLibExists);
3838

39+
OptionSet<SanitizerKind> parseSanitizerRecoverArgValues(
40+
const llvm::opt::Arg *A, const OptionSet<SanitizerKind> &enabledSanitizers,
41+
DiagnosticEngine &Diags, bool emitWarnings);
42+
3943
/// Parses a -sanitize-coverage= argument's value.
4044
llvm::SanitizerCoverageOptions parseSanitizerCoverageArgValue(
4145
const llvm::opt::Arg *A,

lib/Driver/Driver.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1636,6 +1636,14 @@ void Driver::buildOutputInfo(const ToolChain &TC, const DerivedArgList &Args,
16361636
return TC.sanitizerRuntimeLibExists(Args, sanitizerName, shared);
16371637
});
16381638

1639+
if (const Arg *A = Args.getLastArg(options::OPT_sanitize_recover_EQ)) {
1640+
// Just validate the args. The frontend will parse these again and actually
1641+
// use them. To avoid emitting warnings multiple times we surpress warnings
1642+
// here but not in the frontend.
1643+
(void)parseSanitizerRecoverArgValues(A, OI.SelectedSanitizers, Diags,
1644+
/*emitWarnings=*/false);
1645+
}
1646+
16391647
if (const Arg *A = Args.getLastArg(options::OPT_sanitize_coverage_EQ)) {
16401648

16411649
// Check that the sanitizer coverage flags are supported if supplied.

lib/Driver/ToolChains.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,7 @@ static void addCommonFrontendArgs(const ToolChain &TC, const OutputInfo &OI,
214214
inputArgs.AddLastArg(arguments, options::OPT_profile_coverage_mapping);
215215
inputArgs.AddLastArg(arguments, options::OPT_warnings_as_errors);
216216
inputArgs.AddLastArg(arguments, options::OPT_sanitize_EQ);
217+
inputArgs.AddLastArg(arguments, options::OPT_sanitize_recover_EQ);
217218
inputArgs.AddLastArg(arguments, options::OPT_sanitize_coverage_EQ);
218219
inputArgs.AddLastArg(arguments, options::OPT_static);
219220
inputArgs.AddLastArg(arguments, options::OPT_swift_version);

lib/Frontend/CompilerInvocation.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -908,6 +908,12 @@ static bool ParseSILArgs(SILOptions &Opts, ArgList &Args,
908908
IRGenOpts.Sanitizers = Opts.Sanitizers;
909909
}
910910

911+
if (const Arg *A = Args.getLastArg(options::OPT_sanitize_recover_EQ)) {
912+
IRGenOpts.SanitizersWithRecoveryInstrumentation =
913+
parseSanitizerRecoverArgValues(A, Opts.Sanitizers, Diags,
914+
/*emitWarnings=*/true);
915+
}
916+
911917
if (auto A = Args.getLastArg(OPT_enable_verify_exclusivity,
912918
OPT_disable_verify_exclusivity)) {
913919
Opts.VerifyExclusivity

lib/IRGen/IRGen.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,8 +126,14 @@ static void addSwiftMergeFunctionsPass(const PassManagerBuilder &Builder,
126126

127127
static void addAddressSanitizerPasses(const PassManagerBuilder &Builder,
128128
legacy::PassManagerBase &PM) {
129-
PM.add(createAddressSanitizerFunctionPass());
130-
PM.add(createModuleAddressSanitizerLegacyPassPass());
129+
auto &BuilderWrapper =
130+
static_cast<const PassManagerBuilderWrapper &>(Builder);
131+
auto recover =
132+
bool(BuilderWrapper.IRGOpts.SanitizersWithRecoveryInstrumentation &
133+
SanitizerKind::Address);
134+
PM.add(createAddressSanitizerFunctionPass(/*CompileKernel=*/false, recover));
135+
PM.add(createModuleAddressSanitizerLegacyPassPass(/*CompileKernel=*/false,
136+
recover));
131137
}
132138

133139
static void addThreadSanitizerPass(const PassManagerBuilder &Builder,

lib/Option/SanitizerOptions.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,49 @@ OptionSet<SanitizerKind> swift::parseSanitizerArgValues(
185185
return sanitizerSet;
186186
}
187187

188+
OptionSet<SanitizerKind> swift::parseSanitizerRecoverArgValues(
189+
const llvm::opt::Arg *A, const OptionSet<SanitizerKind> &enabledSanitizers,
190+
DiagnosticEngine &Diags, bool emitWarnings) {
191+
OptionSet<SanitizerKind> sanitizerRecoverSet;
192+
193+
// Find the sanitizer kind.
194+
for (const char *arg : A->getValues()) {
195+
Optional<SanitizerKind> optKind = parse(arg);
196+
197+
// Unrecognized sanitizer option
198+
if (!optKind.hasValue()) {
199+
Diags.diagnose(SourceLoc(), diag::error_unsupported_option_argument,
200+
A->getOption().getPrefixedName(), arg);
201+
continue;
202+
}
203+
SanitizerKind kind = optKind.getValue();
204+
205+
// Only support ASan for now.
206+
if (kind != SanitizerKind::Address) {
207+
Diags.diagnose(SourceLoc(), diag::error_unsupported_option_argument,
208+
A->getOption().getPrefixedName(), arg);
209+
continue;
210+
}
211+
212+
// Check that the sanitizer is enabled.
213+
if (!(enabledSanitizers & kind)) {
214+
SmallString<128> b;
215+
if (emitWarnings) {
216+
Diags.diagnose(SourceLoc(),
217+
diag::warning_option_requires_specific_sanitizer,
218+
(A->getOption().getPrefixedName() + toStringRef(kind))
219+
.toStringRef(b),
220+
toStringRef(kind));
221+
}
222+
continue;
223+
}
224+
225+
sanitizerRecoverSet |= kind;
226+
}
227+
228+
return sanitizerRecoverSet;
229+
}
230+
188231
std::string swift::getSanitizerList(const OptionSet<SanitizerKind> &Set) {
189232
std::string list;
190233
#define SANITIZER(_, kind, name, file) \

test/Driver/sanitize_recover.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// RUN: not %swiftc_driver -driver-print-jobs -sanitize=address -sanitize-recover=foo %s 2>&1 | %FileCheck -check-prefix=SAN_RECOVER_INVALID_ARG %s
2+
// RUN: not %swiftc_driver -driver-print-jobs -sanitize=address -sanitize-recover=thread %s 2>&1 | %FileCheck -check-prefix=SAN_RECOVER_UNSUPPORTED_ARG %s
3+
// RUN: %swiftc_driver -v -sanitize-recover=address %s -o %t 2>&1 | %FileCheck -check-prefix=SAN_RECOVER_MISSING_INSTRUMENTATION_OPTION %s
4+
// RUN: %swiftc_driver -driver-print-jobs -sanitize=address -sanitize-recover=address %s 2>&1 | %FileCheck -check-prefix=ASAN_WITH_RECOVER %s
5+
// RUN: %swiftc_driver -driver-print-jobs -sanitize=address %s 2>&1 | %FileCheck -check-prefix=ASAN_WITHOUT_RECOVER --implicit-check-not='-sanitize-recover=address' %s
6+
7+
// SAN_RECOVER_INVALID_ARG: unsupported argument 'foo' to option '-sanitize-recover='
8+
// SAN_RECOVER_UNSUPPORTED_ARG: unsupported argument 'thread' to option '-sanitize-recover='
9+
10+
// SAN_RECOVER_MISSING_INSTRUMENTATION_OPTION: warning: option '-sanitize-recover=address' has no effect when 'address' sanitizer is disabled. Use -sanitize=address to enable the sanitizer
11+
// SAN_RECOVER_MISSING_INSTRUMENTATION_OPTION-NOT: warning: option '-sanitize-recover=address' has no effect when 'address' sanitizer is disabled. Use -sanitize=address to enable the sanitizer
12+
13+
// ASAN_WITH_RECOVER: swift
14+
// ASAN_WITH_RECOVER-DAG: -sanitize=address
15+
// ASAN_WITH_RECOVER-DAG: -sanitize-recover=address
16+
17+
// ASAN_WITHOUT_RECOVER: swift
18+
// ASAN_WITHOUT_RECOVER: -sanitize=address

0 commit comments

Comments
 (0)