Skip to content

Teach the Swift front-end to generate code with sanitizer coverage #2527

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 28, 2016

Conversation

delcypher
Copy link
Contributor

What's in this pull request?

Teach the Swift front-end to generate code with "Sanitizer Coverage" with a new command line flag
-sanitize-coverage=. This flag is analogous to Clang's -fsanitize-coverage=.

This instrumentation currently requires ASan or TSan to be enabled
because the module pass created by createSanitizerCoverageModulePass()
inserts calls into functions found in compiler-rt's "sanitizer_common".
"sanitizer_common" is not shipped as an individual library but instead
exists in several of the sanitizer runtime libraries so we have to
link with one of them to avoid linking errors.

The rationale between adding this feature is to allow experimentation
with libFuzzer which currently relies on "Sanitizer Coverage"
instrumentation.

CC: @AnnaZaks @devincoughlin


Before merging this pull request to apple/swift repository:

  • Test pull request on Swift continuous integration.

Triggering Swift CI

The swift-ci is triggered by writing a comment on this PR addressed to the GitHub user @swift-ci. Different tests will run depending on the specific comment that you use. The currently available comments are:

Smoke Testing

Platform Comment
All supported platforms @swift-ci Please smoke test
OS X platform @swift-ci Please smoke test OS X platform
Linux platform @swift-ci Please smoke test Linux platform

Validation Testing

Platform Comment
All supported platforms @swift-ci Please test
OS X platform @swift-ci Please test OS X platform
Linux platform @swift-ci Please test Linux platform

Note: Only members of the Apple organization can trigger swift-ci.

@delcypher
Copy link
Contributor Author

@jrose-apple I suspect you will have opinions on this PR. Please take a look when you have time.

legacy::PassManagerBase &PM) {
SanitizerCoverageOptions opts;
opts.CoverageType = sanitizerCoverageType;
assert(opts.CoverageType != SanitizerCoverageOptions::SCK_None);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should be able to make this a static_assert, since it's a template parameter.

@jrose-apple
Copy link
Contributor

This looks really good, Dan. I peppered it with some minor comments but I don't see any real issues.

@delcypher
Copy link
Contributor Author

@jrose-apple Thank you for the review. It turns out I need to extend the -sanitize-coverage= flag to support a few more options (namely trace-pc, `trace-cmpandindirect-calls``) to match what clang supports. So I think I'll try to fix your issues first and then adds the extra flags I need. These will be all separate commits. Once you're happy with them I can squash everything into a single commit to be merged.

@jrose-apple
Copy link
Contributor

Sounds good, thanks!

@delcypher delcypher force-pushed the sanitizer_coverage branch from 2833a04 to 1f4d5dc Compare May 21, 2016 05:51
@delcypher
Copy link
Contributor Author

@jrose-apple You can probably take another look now.

Something I'm not very happy about is the introduction of the swift::SanitizerCoverageOpts struct which is basically the same as llvm::SanitizerCoverageOptions. It only exists to avoid doing #include "llvm/Transforms/Instrumentation.h" in IRGenOptions.h. In that header file it looks like there aren't any LLVM includes other than what's in the Support library and I don't want to introduce new includes.

@delcypher delcypher force-pushed the sanitizer_coverage branch from 1f4d5dc to a66ffc9 Compare May 21, 2016 05:59
@AnnaZaks
Copy link
Contributor

Something I'm not very happy about is the introduction of the swift::SanitizerCoverageOpts struct

I am leaning toward including the definitions from LLVM rather than copying them over, especially, if what's defined in the LLVM header is identical to what you use. You could also consider splitting the LLVM header into 2. For example, one of them could contain only the definitions of the options.

@delcypher
Copy link
Contributor Author

delcypher commented May 22, 2016

@AnnaZaks

I am leaning toward including the definitions from LLVM rather than copying them over, especially, if what's defined in the LLVM header is identical to what you use.

I should probably go for that then unless @jrose-apple has objections.

You could also consider splitting the LLVM header into 2. For example, one of them could contain only the definitions of the options.

Unfortunately I don't think that is practical. The change would need to be made upstream (I don't want this to be a swift only change to LLVM) which might get some push back and take a while to get accepted upstream which would delay getting this PR merged.


ERROR(error_option_missing_required_argument, none,
"option '%0' is missing a required argument. The required "
"argument %1", (StringRef, StringRef))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:-/ I'm still unhappy with one of the diagnostic arguments being a whole phrase. How about "option '%0' is missing a required argument (%1)"? That way there's less of a grammatical restriction, and it's also more concise.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SGTM. I will make this change.

@jrose-apple
Copy link
Contributor

I'm torn about the LLVM header. That's not (transitively) a small header, but Anna's right that the duplication isn't worth it. Let's include it for now, but file a bug to split it up upstream as suggested.

@delcypher
Copy link
Contributor Author

Okay. I've addressed @jrose-apple comments (apart from the LLVM header issue. I'm waiting on @rjmccall to give me the okay on that). Looks like this PR won't merge cleanly anymore so I'm going to rebase now and squash everything into a single commit.

@AnnaZaks
Copy link
Contributor

For sanitizer tests that crash, please use the following option. This will ensure they do not crash, which is bad because it will generate a crash report.
env ASAN_OPTIONS=abort_on_error=0

@delcypher delcypher force-pushed the sanitizer_coverage branch 2 times, most recently from 43e4439 to 8647614 Compare May 23, 2016 22:20
@delcypher
Copy link
Contributor Author

@AnnaZaks

For sanitizer tests that crash, please use the following option. This will ensure they do not crash, which is bad because it will generate a crash report.
env ASAN_OPTIONS=abort_on_error=0

Is that necessary here? The single test that use the sanitizer shouldn't be doing anything bad with addresses. The test/Sanitizers/sanitizer_coverage.swift is only using ASan because we need bits of the ASan compiler-rt linked in for the Sanitizer coverage to be linked in.

@delcypher delcypher force-pushed the sanitizer_coverage branch from 8647614 to 601c31c Compare May 23, 2016 22:24
@delcypher
Copy link
Contributor Author

@jrose-apple @AnnaZaks This PR has been rebased, modified to use the LLVM header and squashed into a single commit. When you have time please take a look.

@AnnaZaks
Copy link
Contributor

You don't have to add that if the test is not reporting any issues. Though, we might want to have it for consistency.

@@ -91,6 +94,9 @@ class IRGenOptions {
/// Which sanitizer is turned on.
SanitizerKind Sanitize : 2;

/// Which sanitizer coverage is turned on.
llvm::SanitizerCoverageOptions SanitizeCoverage;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, nitpick: please move this out of the bitfields, so it doesn't stop them from getting merged.

@delcypher delcypher force-pushed the sanitizer_coverage branch 3 times, most recently from 35b1613 to bce30a0 Compare May 24, 2016 05:45
@delcypher
Copy link
Contributor Author

@AnnaZaks

You don't have to add that if the test is not reporting any issues. Though, we might want to have it for consistency.

Oh looks like you've added a test/Sanitizers directory since I last looked. I'm glad we picked the same name :). I've set the abort_on_error=0 ASAN_OPTIONS option although I didn't use env. Is that necessary. It doesn't seem to be on my local machine.

@delcypher
Copy link
Contributor Author

@jrose-apple Sorry for the issues. I've tried to fix them in the latest version of the commit.

@AnnaZaks
Copy link
Contributor

Oh looks like you've added a test/Sanitizers directory since I last looked. I'm glad we picked the same name :).

I've checked which directory you use in this patch before adding mine:)

I've set the abort_on_error=0 ASAN_OPTIONS option although I didn't use env. Is that necessary. It doesn't seem to be on my local machine.

I call %target-run %t_tsan-binary, which does not work without using 'env'.

@jrose-apple
Copy link
Contributor

Looks good on my end. Thanks for bearing with my feedback!

@delcypher
Copy link
Contributor Author

@jrose-apple Great. Is there anything left for me to do (e.g. triggering test runs)?

@AnnaZaks
Copy link
Contributor

@swift-ci test

@delcypher
Copy link
Contributor Author

@AnnaZaks I don't think the Linux build failure is to do with me. It looks like its in lldb.

@AnnaZaks
Copy link
Contributor

@swift-ci please test and merge

1 similar comment
@AnnaZaks
Copy link
Contributor

@swift-ci please test and merge

@jrose-apple
Copy link
Contributor

<unknown>:0: error: unsupported option '-sanitize=address' for target 'x86_64-unknown-linux-gnu'

Failing Tests (2):
    Swift :: Driver/sanitize_coverage.swift
    Swift :: IRGen/sanitize_coverage.swift

That looks like a real issue. Are these tests supposed to have REQUIRES guards?

@jrose-apple
Copy link
Contributor

jrose-apple commented May 27, 2016

REQUIRES: asan_runtime, right? Although I wouldn't expect the Driver test to need it…

// RUN: %target-swift-frontend -emit-ir -sanitize=address -sanitize-coverage=edge,indirect-calls %s | FileCheck %s -check-prefix=SANCOV -check-prefix=SANCOV_INDIRECT_CALLS
// RUN: %target-swift-frontend -emit-ir -sanitize=address -sanitize-coverage=edge,8bit-counters %s | FileCheck %s -check-prefix=SANCOV -check-prefix=SANCOV_8BIT_COUNTERS

import Foundation
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that I think of it, please import Darwin instead of Foundation, to make it easier to port this test to Linux later.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay. I'm not sure how that makes porting easier but I'm not familiar enough with these modules to know any better.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It just means we won't accidentally start introducing dependencies on ObjC and/or Foundation.

@delcypher
Copy link
Contributor Author

@jrose-apple

That looks like a real issue. Are these tests supposed to have REQUIRES guards?

Yes they should. I completely forgot about this. I can probably use REQUIRES: asan_runtime although for the driver test this a little misleading because it isn't really that the asan_runtime is required it is asan support in the frontend. Currently the driver will error out if the host is Linux and -sanitize=(address|thread). Should I just go ahead and use REQUIRES: asan_runtime or should I use XFAIL: linux?

@AnnaZaks
Copy link
Contributor

You need XFAIL: linux. I suspect you do not need REQUIRES: asan_runtime for the driver tests. The failure above will be addressed by the XFAIL. The REQUIRES will make sure that the engineers that do not check out compiler RT will not run into test failures.

"Sanitizer Coverage" with a new flag ``-sanitize-coverage=``. This
flag is analogous to Clang's ``-fsanitize-coverage=``.

This instrumentation currently requires ASan or TSan to be enabled
because the module pass created by ``createSanitizerCoverageModulePass()``
inserts calls into functions found in compiler-rt's "sanitizer_common".
"sanitizer_common" is not shipped as an individual library but instead
exists in several of the sanitizer runtime libraries so we have to
link with one of them to avoid linking errors.

The rationale between adding this feature is to allow experimentation
with libFuzzer which currently relies on "Sanitizer Coverage"
instrumentation.
@delcypher delcypher force-pushed the sanitizer_coverage branch from bce30a0 to 827f573 Compare May 27, 2016 20:35
@delcypher
Copy link
Contributor Author

@AnnaZaks Okay I've made the changes. Could you re-run the tests?

@AnnaZaks
Copy link
Contributor

@swift-ci please test and merge

@delcypher
Copy link
Contributor Author

@jrose-apple @AnnaZaks
I don't think this build failure is related to this PR.

@jrose-apple
Copy link
Contributor

Yeah, we're seeing this on master too. I think it's okay to merge.

@delcypher
Copy link
Contributor Author

@jrose-apple Okay I'm merging then. Fingers crossed...

@delcypher delcypher merged commit 83da10b into swiftlang:master May 28, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants