Skip to content

Commit c48d45e

Browse files
authored
[sanitizer] Refactor -f(no-)?sanitize-recover parsing (llvm#119819)
This moves the -f(no-)?sanitize-recover parsing into a generic parseSanitizerArgs function, and then applies it to parse -f(no-)?sanitize-recover and -f(no-)?sanitize-trap. N.B. parseSanitizeTrapArgs does *not* remove non-TrappingSupported arguments. This maintains the legacy behavior of '-fsanitize=undefined -fsanitize-trap=undefined' (clang/test/Driver/fsanitize.c), which is that vptr is not enabled at all (not even in recover mode) in order to avoid the need for a ubsan runtime.
1 parent 9d11aa1 commit c48d45e

File tree

1 file changed

+62
-65
lines changed

1 file changed

+62
-65
lines changed

clang/lib/Driver/SanitizerArgs.cpp

Lines changed: 62 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -248,48 +248,76 @@ static SanitizerMask setGroupBits(SanitizerMask Kinds) {
248248
return Kinds;
249249
}
250250

251-
// Computes the sanitizer mask based on the default plus opt-in (if supported)
252-
// minus opt-out.
251+
// Computes the sanitizer mask as:
252+
// Default + Arguments (in or out)
253+
// with arguments parsed from left to right.
254+
//
255+
// Error messages are printed if the AlwaysIn or AlwaysOut invariants are
256+
// violated, but the caller must enforce these invariants themselves.
253257
static SanitizerMask
254258
parseSanitizeArgs(const Driver &D, const llvm::opt::ArgList &Args,
255-
bool DiagnoseErrors, SanitizerMask Supported,
256-
SanitizerMask Default, int OptInID, int OptOutID) {
257-
SanitizerMask Remove; // During the loop below, the accumulated set of
258-
// sanitizers disabled by the current sanitizer
259-
// argument or any argument after it.
260-
SanitizerMask Kinds;
261-
SanitizerMask SupportedWithGroups = setGroupBits(Supported);
262-
263-
for (const llvm::opt::Arg *Arg : llvm::reverse(Args)) {
259+
bool DiagnoseErrors, SanitizerMask Default,
260+
SanitizerMask AlwaysIn, SanitizerMask AlwaysOut, int OptInID,
261+
int OptOutID) {
262+
assert(!(AlwaysIn & AlwaysOut) &&
263+
"parseSanitizeArgs called with contradictory in/out requirements");
264+
265+
SanitizerMask Output = Default;
266+
// Keep track of which violations we have already reported, to avoid
267+
// duplicate error messages.
268+
SanitizerMask DiagnosedAlwaysInViolations;
269+
SanitizerMask DiagnosedAlwaysOutViolations;
270+
for (const auto *Arg : Args) {
264271
if (Arg->getOption().matches(OptInID)) {
265-
Arg->claim();
266-
SanitizerMask Add = parseArgValues(D, Arg, true);
267-
Add &= ~Remove;
268-
SanitizerMask InvalidValues = Add & ~SupportedWithGroups;
269-
if (InvalidValues && DiagnoseErrors) {
270-
SanitizerSet S;
271-
S.Mask = InvalidValues;
272-
D.Diag(diag::err_drv_unsupported_option_argument)
273-
<< Arg->getSpelling() << toString(S);
272+
SanitizerMask Add = parseArgValues(D, Arg, DiagnoseErrors);
273+
// Report error if user explicitly tries to opt-in to an always-out
274+
// sanitizer.
275+
if (SanitizerMask KindsToDiagnose =
276+
Add & AlwaysOut & ~DiagnosedAlwaysOutViolations) {
277+
if (DiagnoseErrors) {
278+
SanitizerSet SetToDiagnose;
279+
SetToDiagnose.Mask |= KindsToDiagnose;
280+
D.Diag(diag::err_drv_unsupported_option_argument)
281+
<< Arg->getSpelling() << toString(SetToDiagnose);
282+
DiagnosedAlwaysOutViolations |= KindsToDiagnose;
283+
}
274284
}
275-
Kinds |= expandSanitizerGroups(Add) & ~Remove;
285+
Output |= expandSanitizerGroups(Add);
286+
Arg->claim();
276287
} else if (Arg->getOption().matches(OptOutID)) {
288+
SanitizerMask Remove = parseArgValues(D, Arg, DiagnoseErrors);
289+
// Report error if user explicitly tries to opt-out of an always-in
290+
// sanitizer.
291+
if (SanitizerMask KindsToDiagnose =
292+
Remove & AlwaysIn & ~DiagnosedAlwaysInViolations) {
293+
if (DiagnoseErrors) {
294+
SanitizerSet SetToDiagnose;
295+
SetToDiagnose.Mask |= KindsToDiagnose;
296+
D.Diag(diag::err_drv_unsupported_option_argument)
297+
<< Arg->getSpelling() << toString(SetToDiagnose);
298+
DiagnosedAlwaysInViolations |= KindsToDiagnose;
299+
}
300+
}
301+
Output &= ~expandSanitizerGroups(Remove);
277302
Arg->claim();
278-
Remove |= expandSanitizerGroups(parseArgValues(D, Arg, DiagnoseErrors));
279303
}
280304
}
281305

282-
// Apply default behavior.
283-
Kinds |= Default & ~Remove;
284-
285-
return Kinds;
306+
return Output;
286307
}
287308

288309
static SanitizerMask parseSanitizeTrapArgs(const Driver &D,
289310
const llvm::opt::ArgList &Args,
290311
bool DiagnoseErrors) {
291-
return parseSanitizeArgs(D, Args, DiagnoseErrors, TrappingSupported,
292-
TrappingDefault, options::OPT_fsanitize_trap_EQ,
312+
SanitizerMask AlwaysTrap; // Empty
313+
SanitizerMask NeverTrap = ~(setGroupBits(TrappingSupported));
314+
315+
// N.B. We do *not* enforce NeverTrap. This maintains the behavior of
316+
// '-fsanitize=undefined -fsanitize-trap=undefined'
317+
// (clang/test/Driver/fsanitize.c ), which is that vptr is not enabled at all
318+
// (not even in recover mode) in order to avoid the need for a ubsan runtime.
319+
return parseSanitizeArgs(D, Args, DiagnoseErrors, TrappingDefault, AlwaysTrap,
320+
NeverTrap, options::OPT_fsanitize_trap_EQ,
293321
options::OPT_fno_sanitize_trap_EQ);
294322
}
295323

@@ -657,44 +685,13 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
657685
// default in ASan?
658686

659687
// Parse -f(no-)?sanitize-recover flags.
660-
SanitizerMask RecoverableKinds = RecoverableByDefault | AlwaysRecoverable;
661-
SanitizerMask DiagnosedUnrecoverableKinds;
662-
SanitizerMask DiagnosedAlwaysRecoverableKinds;
663-
for (const auto *Arg : Args) {
664-
if (Arg->getOption().matches(options::OPT_fsanitize_recover_EQ)) {
665-
SanitizerMask Add = parseArgValues(D, Arg, DiagnoseErrors);
666-
// Report error if user explicitly tries to recover from unrecoverable
667-
// sanitizer.
668-
if (SanitizerMask KindsToDiagnose =
669-
Add & Unrecoverable & ~DiagnosedUnrecoverableKinds) {
670-
SanitizerSet SetToDiagnose;
671-
SetToDiagnose.Mask |= KindsToDiagnose;
672-
if (DiagnoseErrors)
673-
D.Diag(diag::err_drv_unsupported_option_argument)
674-
<< Arg->getSpelling() << toString(SetToDiagnose);
675-
DiagnosedUnrecoverableKinds |= KindsToDiagnose;
676-
}
677-
RecoverableKinds |= expandSanitizerGroups(Add);
678-
Arg->claim();
679-
} else if (Arg->getOption().matches(options::OPT_fno_sanitize_recover_EQ)) {
680-
SanitizerMask Remove = parseArgValues(D, Arg, DiagnoseErrors);
681-
// Report error if user explicitly tries to disable recovery from
682-
// always recoverable sanitizer.
683-
if (SanitizerMask KindsToDiagnose =
684-
Remove & AlwaysRecoverable & ~DiagnosedAlwaysRecoverableKinds) {
685-
SanitizerSet SetToDiagnose;
686-
SetToDiagnose.Mask |= KindsToDiagnose;
687-
if (DiagnoseErrors)
688-
D.Diag(diag::err_drv_unsupported_option_argument)
689-
<< Arg->getSpelling() << toString(SetToDiagnose);
690-
DiagnosedAlwaysRecoverableKinds |= KindsToDiagnose;
691-
}
692-
RecoverableKinds &= ~expandSanitizerGroups(Remove);
693-
Arg->claim();
694-
}
695-
}
696-
RecoverableKinds &= Kinds;
688+
SanitizerMask RecoverableKinds = parseSanitizeArgs(
689+
D, Args, DiagnoseErrors, RecoverableByDefault, AlwaysRecoverable,
690+
Unrecoverable, options::OPT_fsanitize_recover_EQ,
691+
options::OPT_fno_sanitize_recover_EQ);
692+
RecoverableKinds |= AlwaysRecoverable;
697693
RecoverableKinds &= ~Unrecoverable;
694+
RecoverableKinds &= Kinds;
698695

699696
TrappingKinds &= Kinds;
700697
RecoverableKinds &= ~TrappingKinds;

0 commit comments

Comments
 (0)