Skip to content

[Sanitizers] Add Driver/Frontend option to enable sanitizer instrumentation that supports error recovery #28126

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
Nov 14, 2019

Conversation

danliew-apple
Copy link
Contributor

The new option -sanitize-recover= takes a list of sanitizers that
recovery instrumentation should be enabled for. Currently we only
support it for Address Sanitizer.

If the option is not specified then the generated instrumentation does
not allow error recovery.

This option mirrors the -fsanitize-recover= option of Clang.

We don't enable recoverable instrumentation by default because it may
lead to code size blow up (control flow has to be resumable).

rdar://problem/56346688

Copy link
Contributor

@yln yln left a comment

Choose a reason for hiding this comment

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

This comes with tests for: driver, IRGen, and full integration-style execution tests. Good work!

LGTM. Let's also get approval from the Swift folks.

HelpText<"Specify which sanitizer runtime checks (see -sanitize=) will "
"generate instrumentation that allows error recovery. Listed "
"checks should be comma separated. Default behavior is to not "
"allow error recovery.">;
Copy link
Contributor

Choose a reason for hiding this comment

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

How should I understand this flag compared to e.g. tsan's halt_on_error=0? If I set sanitize-recover=address does it unconditionally recover or can I say ASAN_OPTIONS= halt_on_error=1?

You mentioned it only supports asan, but isn't this the default behaviour for tsan? Or is this somehow different from what tsan is doing?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good questions.

The halt_on_error flag is actually not TSan specific. The other sanitizers have it too.

If I set sanitize-recover=address does it unconditionally recover or can I say ASAN_OPTIONS= halt_on_error=1?

No, it does not unconditionally recover. What it does is cause ASan's instrumentation to allow for error recovery. The runtime halt_on_error option is used to decide what to do when ASan detects an issue.

Today, setting ASAN_OPTIONS=halt_on_error=0 doesn't always work. If you compile without the -sanitize-recover=address option (equivalent to the current behavior of the swift compiler) then the generated instrumentation doesn't allow for error recovery. What this means is that if you set ASAN_OPTIONS=halt_on_error=0 at runtime and if an ASan issue is caught via instrumentation then the process will always halt regardless of how halt_on_error is set. However, if ASan catches an issue via one of its interceptors (e.g. memcpy) then halt_on_error runtime option is respected.

With -sanitize-recover=address the generated instrumentation allows for error recovery which means that the halt_on_error runtime option always does what you would expect.

ASan's default for halt_on_error is true which means this issue only effects people who choose to not use the default behavior.

You mentioned it only supports asan, but isn't this the default behaviour for tsan? Or is this somehow different from what tsan is doing?

halt_on_error defaults to false for TSan. The difference here is that TSan's instrumentation pass always allows for error recovery but ASan's instrumentation pass does not.

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for the explanation! Any reason not to accept -sanitize-recover=thread then for consistency?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Well, the flag wouldn't do anything. The ThreadSanitizer pass doesn't have an option to change the instrumentation so there's nothing we can change in the compilation process. It seems odd to me to have a driver/frontend option that doesn't effect the compilation process.

Given that we're adding a new option I'd prefer to limit the ways in which it can be used.

@danliew-apple
Copy link
Contributor Author

@swift-ci Please smoke test

@danliew-apple
Copy link
Contributor Author

Interesting. The test doesn't seem to work on Linux

12:38:45 : 'RUN: at line 21';   '/usr/bin/python' '/home/buildnode/jenkins/workspace/swift-PR-Linux-smoke-test@2/branch-master/swift/utils/PathSanitizingFileCheck' --sanitize BUILD_DIR='/home/buildnode/jenkins/workspace/swift-PR-Linux-smoke-test@2/branch-master/buildbot_linux/swift-linux-x86_64' --sanitize SOURCE_DIR='/home/buildnode/jenkins/workspace/swift-PR-Linux-smoke-test@2/branch-master/swift' --use-filecheck '/home/buildnode/jenkins/workspace/swift-PR-Linux-smoke-test@2/branch-master/buildbot_linux/llvm-linux-x86_64/bin/FileCheck'  --check-prefixes=CHECK-COMMON-STDOUT,CHECK-NO-RECOVER-STDOUT -input-file=/home/buildnode/jenkins/workspace/swift-PR-Linux-smoke-test@2/branch-master/buildbot_linux/swift-linux-x86_64/test-linux-x86_64/Sanitizers/Output/asan_recover.swift.tmp_asan_no_recover.stdout /home/buildnode/jenkins/workspace/swift-PR-Linux-smoke-test@2/branch-master/swift/test/Sanitizers/asan_recover.swift
12:38:45 --
12:38:45 Exit Code: 1
12:38:45 
12:38:45 Command Output (stdout):
12:38:45 --
12:38:45 /home/buildnode/jenkins/workspace/swift-PR-Linux-smoke-test@2/branch-master/buildbot_linux/swift-linux-x86_64/test-linux-x86_64/Sanitizers/Output/asan_recover.swift.tmp_asan_recover
12:38:45 
12:38:45 --
12:38:45 Command Output (stderr):
12:38:45 --
12:38:45 /usr/bin/ld.gold: warning: Cannot export local symbol '__asan_extra_spill_area'
12:38:45 
/home/buildnode/jenkins/workspace/swift-PR-Linux-smoke-test@2/branch-master/swift/test/Sanitizers/asan_recover.swift:72:25: error: CHECK-COMMON-STDERR: expected string not found in input
12:38:45 // CHECK-COMMON-STDERR: #0 0x{{.+}} in main asan_recover.swift:[[@LINE+1]]
12:38:45                         ^
12:38:45 /home/buildnode/jenkins/workspace/swift-PR-Linux-smoke-test@2/branch-master/buildbot_linux/swift-linux-x86_64/test-linux-x86_64/Sanitizers/Output/asan_recover.swift.tmp_asan_recover.stderr:2:52: note: scanning from here
12:38:45 ==26270==ERROR: AddressSanitizer: use-after-poison on address 0x60d000000040 at pc 0x55bef441207e bp 0x7fff4935c230 sp 0x7fff4935c228
12:38:45                                                    ^
12:38:45 /home/buildnode/jenkins/workspace/swift-PR-Linux-smoke-test@2/branch-master/buildbot_linux/swift-linux-x86_64/test-linux-x86_64/Sanitizers/Output/asan_recover.swift.tmp_asan_recover.stderr:2:52: note: with "@LINE+1" equal to "73"
12:38:45 ==26270==ERROR: AddressSanitizer: use-after-poison on address 0x60d000000040 at pc 0x55bef441207e bp 0x7fff4935c230 sp 0x7fff4935c228
12:38:45                                                    ^
12:38:45 /home/buildnode/jenkins/workspace/swift-PR-Linux-smoke-test@2/branch-master/buildbot_linux/swift-linux-x86_64/test-linux-x86_64/Sanitizers/Output/asan_recover.swift.tmp_asan_recover.stderr:4:107: note: possible intended match here
12:38:45  #0 0x55bef441207d in main /home/buildnode/jenkins/workspace/swift-PR-Linux-smoke-test@2/branch-master/swift/test/Sanitizers/asan_recover.swift:73:47

Apart from the Linker complaining (@kubamracek @yln should I worry about this?) it looks like on Linux the symbolized source location is the full path rather than just the filename. That should be easy to fix.

@danliew-apple
Copy link
Contributor Author

@swift-ci Please smoke test

@yln
Copy link
Contributor

yln commented Nov 7, 2019

Linker: I don't understand what this means, but I think we can ignore it for this PR.
Full path names on Linux: we should handle this the same way as other tests do. Do they use{{.*}}?

@danliew-apple
Copy link
Contributor Author

Linker: I don't understand what this means, but I think we can ignore it for this PR.
Full path names on Linux: we should handle this the same way as other tests do. Do they use{{.*}}?

Well something similar:

@danliew-apple danliew-apple removed the request for review from compnerd November 7, 2019 23:36
Copy link
Contributor

@beccadax beccadax left a comment

Choose a reason for hiding this comment

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

I have some questions and ideas, but if none of them are valid, I think this is fine as is.

@@ -71,6 +71,10 @@ ERROR(error_option_requires_sanitizer, none,
"option '%0' requires a sanitizer to be enabled. Use -sanitize= to "
"enable a sanitizer", (StringRef))

ERROR(error_option_requires_specific_sanitizer, none,
"option '%0' requires '%1' sanitizer to be enabled. Use -sanitize=%1 to "
"enable the sanitizer", (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.

Why not automatically enable the matching sanitizer?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Two reasons

  1. Using -sanitize-recover=address without -sanitize=address is a mistake in the build system and we should catch that.
  2. Clang doesn't automatically enable a sanitizer when provided -fsanitize-recover=address either. It doesn't produce an error though, but I think producing an error here is better.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@brentdax I've actually changed my mind here. After thinking about this some more I realised there's a legitimate use case where passing -sanitize-recover=address without -sanitize=address should not result in an error. Consider a scenario where -sanitize-recover=address is globally passed to the build of a project but -sanitize-recover=address is not enabled everywhere in the project (e.g. project is being incrementally ported to support ASan). The current version of this PR would cause a compiler error which is unhelpful. Instead I'm going to make this a warning.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've made this a warning now.

…tation that supports error recovery.

The new option `-sanitize-recover=` takes a list of sanitizers that
recovery instrumentation should be enabled for. Currently we only
support it for Address Sanitizer.

If the option is not specified then the generated instrumentation does
not allow error recovery.

This option mirrors the `-fsanitize-recover=` option of Clang.

We don't enable recoverable instrumentation by default because it may
lead to code size blow up (control flow has to be resumable).

The motivation behind this change is that today, setting
`ASAN_OPTIONS=halt_on_error=0` at runtime doesn't always work. If you
compile without the `-sanitize-recover=address` option (equivalent to
the current behavior of the swift compiler) then the generated
instrumentation doesn't allow for error recovery. What this means is
that if you set `ASAN_OPTIONS=halt_on_error=0` at runtime and if an ASan
issue is caught via instrumentation then the process will always halt
regardless of how `halt_on_error` is set. However, if ASan catches an
issue via one of its interceptors (e.g. memcpy) then `the halt_on_error`
runtime option is respected.

With `-sanitize-recover=address` the generated instrumentation allows
for error recovery which means that the `halt_on_error` runtime option
is also respected when the ASan issue is caught by instrumentation.

ASan's default for `halt_on_error` is true which means this issue only
effects people who choose to not use the default behavior.

rdar://problem/56346688
@danliew-apple
Copy link
Contributor Author

@swift-ci Please smoke test

// We need to test reads via instrumentation not via runtime so try to check
// for calls to unwanted interceptor/runtime functions.
// CHECK-IR-NOT: call {{.+}} @__asan_memcpy
// CHECK-IR-NOT: call {{.+}} @memcpy
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@brentdax Here's the check I've added.

@danliew-apple
Copy link
Contributor Author

@brentdax @benlangmuir I've done another pass other the PR and the smoke tests pass. Could I get an approval on this PR or some feedback if there are still problems that need addressing?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants