This is an archive of the discontinued LLVM Phabricator instance.

[Driver][sanitizers] Warn about ignored -fsanitize-trap or -fsanitize-recover
Changes PlannedPublic

Authored by jkorous on Apr 28 2021, 6:10 PM.

Details

Summary

We have a number of internal reports from UBSan users that use -fsanitize-trap=foo or -fsanitize-recover=bar expecting the respective -fsanitize= is automatically implied which isn't the case and this option is silently ignored.

I feel we should try harder to prevent our users from making this mistake.

Diff Detail

Event Timeline

jkorous requested review of this revision.Apr 28 2021, 6:10 PM
jkorous created this revision.
yln accepted this revision.Apr 28 2021, 6:59 PM

This sounds like a good improvement, thanks!

LGTM, with nits. We should also get one additional approval from an external/open source contributor.

clang/include/clang/Basic/DiagnosticDriverKinds.td
548–551

Can foo and bar here be individual UBSan checks or only "full" sanitizers?

If it's easy (i.e., there already exists a SanitizerKind->string mapping), can we include which foo or bar causes the warning. Might be helpful when (a long list of) individual UBSan checks are used.

Message wording nitpicking: active form, more aligned with other warnings.

Ignoring -fsanitize-trap=foo without corresponding -fsanitize=foo
clang/lib/Driver/SanitizerArgs.cpp
208–215

Are those good names?

My understanding is that we have a trap default behavior which can be overridden for specific checks. Previously we would directly return the computed set of "default + overrides", but now we want to print these new warnings which requires us to know individual overrides.

How about the following:

struct SanitizeTrapOverrides {
  SanitizerMask TrapsAdded;
  SanitizerMask TrapsRemoved;
};

Maybe even warrants renaming the TrappingKinds local var below.

This revision is now accepted and ready to land.Apr 28 2021, 6:59 PM
vsk added a subscriber: vsk.Apr 29 2021, 10:14 AM

Since this is a fairly impactful driver change, I'd suggest sending a heads-up about it to llvm-dev and adding a release note for extra visibility.

jkorous added inline comments.Apr 29 2021, 11:02 AM
clang/include/clang/Basic/DiagnosticDriverKinds.td
548–551

Yes, it responds to presence of any -fsanitize-trap/recover command line argument no matter its value. I changed the test so it includes a specific UBSan check.

I'm not sure we need to spell out the command line argument value here unless we start to be more aggressive with the warning.
Currently the warning is emitted only if there's ANY explicit -fsanitize-trap that wasn't disabled with -fno-sanitize-trap AND NO -fsanitize (explicit or implicit == no default sanitizer for the toolchain). Which means that all the -fsanitize-trap arguments are equally important to warn about and adding any -fsanitize argument will fix the warning.

Thanks to your question I just realized I should also add a test for -fsanitize-trap=foo -fno-sanitize-trap=foo case :)

I don't think we can start warning about a missing -fsanitize per sanitizer/check group/check found in -fsanitize-trap/recover as for UBSan it's not unreasonable to use for example:
-fsanitize=array-bounds,integer -fsanitize-trap=undefined
It simply means we want to trap for all UBSan checks but we don't want to repeat the definition of which checks to use.

I'm open to be persuaded we don't need to be this conservative with the warnings in which case spelling out the value of suspicious -fsanitize-trap/recover argument might be necessary.

Edit: I just realized there's a problem with UBSan groups of checks - for example shift = {shift-base, shift-exponent}. That means that for this command line the problematic check isn't even spelled out: -fsanitize-trap=shift -fno-sanitize-trap=shift-base (as this pretty much evaluates to -fsanitize-trap=shift-exponent).
I'm not sure if this is argument against your idea (potentially complex implementation) or for (current behavior might be confusing for users).

clang/lib/Driver/SanitizerArgs.cpp
208–215

TLDR: I don't feel strongly about these names. I'm willing to change them - just explaining how did I arrive at the current names. Let me know if you still consider these better.

Yes, you are pretty much correct - except we don't care about the individual overrides, just about the existence of any of these.
Also the fno-sanitize-trap "un-overrides" have a funny property of affecting only the state that have been processed before themselves (with default being processed at the start). In other words - the order on the command line matters and the semantics here isn't simple set operations.

> bin/clang -fsanitize=array-bounds -fno-sanitize-trap=array-bounds -fsanitize-trap=array-bounds ~/tmp/overflow.c -###

... "-fsanitize=array-bounds" "-fsanitize-trap=array-bounds" ...

> bin/clang -fsanitize=array-bounds -fsanitize-trap=array-bounds -fno-sanitize-trap=array-bounds ~/tmp/overflow.c -##
... "-fsanitize=array-bounds" "-fsanitize-recover=array-bounds" ...

I kept the names "low level" - with semantics close to command line option spelling to avoid interpreting them. If we were to rename these in the spirit of your suggestion it should probably be more like this:

struct SanitizeTrapOverrides {
  SanitizerMask TrapsAddedButNotRemoved;
  SanitizerMask TrapsToBeRemovedFromDefault;
};

The local var name seems sensible to me - it is the end result of processing the command line which says which checks will trap (if used; modulo some sanity checks of this set later).

jkorous updated this revision to Diff 341582.Apr 29 2021, 11:03 AM

improved tests

jkorous planned changes to this revision.Apr 29 2021, 12:48 PM

I just realized that for -fsanitize-recover the logic is inverted - it's actually -fno-sanitize-recover that's an explicit change from the default.
I will probably need to do the same thing I do for traps.

I will send an email to llvm-dev@ and add a release note once I finish that.