Skip to content

[Sanitizers] Add Scudo support #28538

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
Jan 27, 2020
Merged

Conversation

Lukasa
Copy link
Contributor

@Lukasa Lukasa commented Dec 3, 2019

LLVM ships a hardened memory allocator called Scudo:
https://llvm.org/docs/ScudoHardenedAllocator.html. This allocator
provides additional mitigations against heap-based vulnerabilities, but
retains sufficient performance to be safely run in production
applications.

While ideal Swift applications are obviously written in pure Swift, in
practice most applications contain some amount of code written in
less-safe languages. Additionally, plenty of Swift programs themselves
contain unsafe code, particularly when attempting to implement
high-performance data structures. These sources of unsafety introduce
the risk of memory issues, and having the option to use the Scudo
allocator is a useful defense-in-depth tool.

This patch enables -sanitize=scudo as an extra swiftc flag. This
sanitizer is only supported on Linux, so no further work is required to
enable it on Windows or Apple platforms. As this "sanitizer" is only a
runtime component, we do not require any wider changes to instrument
code.

The result of this patch matches the API of clang, which supports
-fsanitize=scudo.


I do have one question: currently this patch contains no regression tests. I was fundamentally unsure of where to place these regression tests, but I think we should have them, so please do as part of your review suggest a location and form for the regression tests.

@Lukasa
Copy link
Contributor Author

Lukasa commented Dec 3, 2019

@swift-ci please smoke test

@Lukasa
Copy link
Contributor Author

Lukasa commented Dec 3, 2019

I should note that, when we update the version of llvm we're using, enabling Scudo will also enable GWP-Asan, which provides not just mitigation but active detection of problems.

Lukasa added a commit to Lukasa/swift-package-manager that referenced this pull request Dec 4, 2019
The Swift PR swiftlang/swift#28538 adds support for the Scudo hardened
allocator to swiftc. This patch adds support for it to Swift Package
Manager, allowing the Package Manager to correctly plumb Scudo through
the entire build pipeline.

See the aforementioned PR for more discussion on the value of supporting
Scudo.
@danliew-apple
Copy link
Contributor

@Lukasa Thanks for the patch. There are something things that need to be addressed before this can land.

  1. Given that this only works on Linux you need to add code in the Swift driver to refuse to use -sanitize=scudo on non Linux platforms. I can't see anything obvious in the code you added that forbids this.
  2. This patch doesn't change anything about the compilation process. AFAIK Scudo is a replacement allocator library so I would expect that the linker would need to be told to link against the scudo runtime.
  3. This is missing tests. You need:
  • A test for successful driver invocation on Linux. You should probably check that the linker flags look right (i.e. links against Scudo runtime).
  • A test for driver failure on non-Linux platforms.
  • Tests for the invalid santizer+scudo combination.
  • At least one test case where we compile and run a program that uses Scudo.

Copy link
Contributor

@danliew-apple danliew-apple left a comment

Choose a reason for hiding this comment

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

See my initial comments.

@Lukasa
Copy link
Contributor Author

Lukasa commented Dec 6, 2019

Thanks @danliew-apple!

Given that this only works on Linux you need to add code in the Swift driver to refuse to use -sanitize=scudo on non Linux platforms. I can't see anything obvious in the code you added that forbids this.

I don't believe this is true. There are separate Sanitizer whitelists for Darwin and Windows which explicitly whitelist the sanitisers they support. I have observed that with this toolchain built on Darwin the Scudo flag is rejected: I believe this is true for Windows as well, though @compnerd could further clarify.

This patch doesn't change anything about the compilation process. AFAIK Scudo is a replacement allocator library so I would expect that the linker would need to be told to link against the scudo runtime.

It turns out it already is. On Linux the driver will simply pass the sanitizer option through to clang, which drives the actual linking process. This results in the sanitising being applied. Again, I've verified this in live code.

This is missing tests.

Fantastic. Can you point me to places in the code that do similar testing? I wasn't able to find them, and that made it very hard to write more.

@danliew-apple
Copy link
Contributor

@Lukasa Here are some examples:

Note that llvm-lit has a notion of features and you can use this to make a test run on only certain platforms. My suggestion is that

  • Tests for driver options should probably execute on all platforms. In these tests you'd explicit specify the target so that we can verify that the driver allows Scudo on Linux but rejects it for Windows on macOS.
  • Tests that are only going to work on Linux should be annotated with REQUIRES: OS=linux-gnu.

@Lukasa
Copy link
Contributor Author

Lukasa commented Dec 6, 2019

Thanks @danliew-apple, I’ll write some tests to cover this and ping for re-review when I’ve done so.

@danliew-apple
Copy link
Contributor

danliew-apple commented Dec 6, 2019

I don't believe this is true. There are separate Sanitizer whitelists for Darwin and Windows which explicitly whitelist the sanitisers they support. I have observed that with this toolchain built on Darwin the Scudo flag is rejected: I believe this is true for Windows as well, though @compnerd could further clarify.

The code you link to is related to linking. The driver should fail compilation much earlier than that. If someone passes -sanitizer=scudo option for any mode of the driver and the platform doesn't support it then it should fail.

This patch doesn't change anything about the compilation process. AFAIK Scudo is a replacement allocator library so I would expect that the linker would need to be told to link against the scudo runtime.

It turns out it already is. On Linux the driver will simply pass the sanitizer option through to clang, which drives the actual linking process. This results in the sanitising being applied. Again, I've verified this in live code.

Interesting. I would not have expected that given that the Swift compiler shouldn't know what to do with this new enum member you've added. You'll need to show me working tests to convince me.

@danliew-apple
Copy link
Contributor

@Lukasa Just to be clear I'm only giving you a review the code as I see it. I don't work on the swift compiler very frequently so I think you will need approval from others to land this. Given that this is very untested I suspect we might want to label it as experimental.

@Lukasa
Copy link
Contributor Author

Lukasa commented Dec 6, 2019

@danliew-apple Totally understood, and I’m very appreciative of your time. I’m confident the patch-as-written works in the straightforward cases because I tested it live, but I also totally agree that without the tests it is hard for anyone else to validate my confidence! So I absolutely appreciate the feedback. I’ll work on adding those tests, and we can worry about wider approval once I’ve dealt with this feedback.

@danliew-apple
Copy link
Contributor

@airspeedswift @mikeash Does adopting the hardened Scudo on Linux seem a reasonable thing to support?

@danliew-apple
Copy link
Contributor

@Lukasa I just noticed that the Scudo documentation says.. "Compiling with Scudo will also enforce PIE for the output binary". That's the behaviour for Clang. Do you know why they do this? If there's a good reason for this we might need to do the same thing for Swift. I don't know if Swift has an equivalent PIE mode.

@Lukasa
Copy link
Contributor Author

Lukasa commented Dec 9, 2019

@swift-ci please smoke test

@Lukasa
Copy link
Contributor Author

Lukasa commented Dec 9, 2019

@swift-ci please smoke test

2 similar comments
@Lukasa
Copy link
Contributor Author

Lukasa commented Dec 9, 2019

@swift-ci please smoke test

@Lukasa
Copy link
Contributor Author

Lukasa commented Dec 9, 2019

@swift-ci please smoke test

@mikeash
Copy link
Contributor

mikeash commented Dec 9, 2019

@danliew-apple I'm not too familiar with it, but reading through the link you provided, this seems like a great thing to support. Especially since the hard work is in LLVM and we apparently just need to support the option and pass it along. @weissi Any additional thoughts?

@weissi
Copy link
Contributor

weissi commented Dec 9, 2019

@mikeash / @Lukasa this looks good to me, IMHO definitely something we want :)

@Lukasa
Copy link
Contributor Author

Lukasa commented Dec 9, 2019

@swift-ci please smoke test

3 similar comments
@Lukasa
Copy link
Contributor Author

Lukasa commented Dec 9, 2019

@swift-ci please smoke test

@Lukasa
Copy link
Contributor Author

Lukasa commented Dec 9, 2019

@swift-ci please smoke test

@Lukasa
Copy link
Contributor Author

Lukasa commented Dec 9, 2019

@swift-ci please smoke test

@Lukasa
Copy link
Contributor Author

Lukasa commented Jan 17, 2020

@swift-ci please smoke test

@Lukasa
Copy link
Contributor Author

Lukasa commented Jan 17, 2020

@swift-ci please test

@swift-ci
Copy link
Contributor

Build failed
Swift Test Linux Platform
Git Sha - 828dbfc3a77f4dd87848bb9cb00cba0cb5b493e4

@swift-ci
Copy link
Contributor

Build failed
Swift Test OS X Platform
Git Sha - 828dbfc3a77f4dd87848bb9cb00cba0cb5b493e4

Copy link
Contributor

@danliew-apple danliew-apple left a comment

Choose a reason for hiding this comment

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

There's still an issue with the test that needs to be resolved.

I also have a very minor nit with the commit message.

The result of this patch matches the API of clang, which supports
-fsanitize=scudo.

Technically -fsanitize=scudo isn't really an API and even then its not an exact match because the command line flag name is different.

Maybe when you say

This patch enables -sanitize=scudo as an extra swiftc flag.

You could say as the next sentence.

This is similar to Clang's -fsanitize=scudo flag.

/*
* Make sure we don't accidentally add the sanitizer library path when building libraries or modules
*/
// RUN: %swiftc_driver -resource-dir %S/Inputs/fake-resource-dir/lib/swift/ -driver-print-jobs -emit-library -sanitize=scudo -target x86_64-unknown-linux-gnu %s 2>&1 | %FileCheck --implicit-check-not=SCUDO_LINUX %s
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this is right: when building only libraries, we don't want to add the sanitiser because it should only be added when building the main binary. This is the same as the requirement on all the other sanitisers with runtime libraries: only the main binary actually invokes the linker to link the sanitiser runtime.

I wasn't questioning that aspect. I was questioning the FileCheck invocation which I'm fairly certain is wrong because you're not using --implicit-check-not as intended (see the FileCheck documentation). You're supposed to give it a pattern and not a check prefix which is what the current patch does.

Sure the test currently passes but that's because the Swift compiler isn't producing the text SWIFT_LINUX in its output. So the test is passing by accident and you're not testing what you actually testing what you want to test.

LLVM ships a hardened memory allocator called Scudo:
https://llvm.org/docs/ScudoHardenedAllocator.html. This allocator
provides additional mitigations against heap-based vulnerabilities, but
retains sufficient performance to be safely run in production
applications.

While ideal Swift applications are obviously written in pure Swift, in
practice most applications contain some amount of code written in
less-safe languages. Additionally, plenty of Swift programs themselves
contain unsafe code, particularly when attempting to implement
high-performance data structures. These sources of unsafety introduce
the risk of memory issues, and having the option to use the Scudo
allocator is a useful defense-in-depth tool.

This patch enables `-sanitize=scudo` as an extra `swiftc` flag. This
sanitizer is only supported on Linux, so no further work is required to
enable it on Windows or Apple platforms. As this "sanitizer" is only a
runtime component, we do not require any wider changes to instrument
code. This is similar to clang's `-fsanitize=scudo` flag.

The Swift driver rejects platforms that don't support Scudo using an
existing mechanism in the Driver that is not part of this patch. This
mechanism is in swift::parseSanitizerArgValues(...)
(lib/Option/SanitizerOptions.cpp). The mechanism determines if a
sanitizer is supported by checking for the existence of the
corresponding sanitizer runtime library in the compiler's resource
directory. The Scudo runtime library currently only exists in the
Linux compiler resource directory. This results in the driver only
allowing Scudo when targeting Linux.
@Lukasa
Copy link
Contributor Author

Lukasa commented Jan 20, 2020

@swift-ci please smoke test

@Lukasa
Copy link
Contributor Author

Lukasa commented Jan 20, 2020

Thanks for that clarification @danliew-apple, that helped a lot! I have rewritten that test and also updated the commit message, please take another look.

@Lukasa
Copy link
Contributor Author

Lukasa commented Jan 20, 2020

@swift-ci please test

@swift-ci
Copy link
Contributor

Build failed
Swift Test OS X Platform
Git Sha - 32f18ce0b0aa39b3b4c6a7bb1a93c37a283b9f6b

@swift-ci
Copy link
Contributor

Build failed
Swift Test Linux Platform
Git Sha - 32f18ce0b0aa39b3b4c6a7bb1a93c37a283b9f6b

@danliew-apple
Copy link
Contributor

@swift-ci please test

@danliew-apple
Copy link
Contributor

@Lukasa Sorry for being slow. I've approved the changes. Thanks for being patient.

@Lukasa
Copy link
Contributor Author

Lukasa commented Jan 24, 2020

No need to apologise, I understand completely that we’re all busy! Thanks so much for your time on this.

@swift-ci
Copy link
Contributor

Build failed
Swift Test Linux Platform
Git Sha - 4fb1920

@danliew-apple
Copy link
Contributor

@swift-ci please test

@danliew-apple danliew-apple merged commit 5652986 into swiftlang:master Jan 27, 2020
@compnerd
Copy link
Member

I believe that this has caused a regression on Windows: https://ci-external.swift.org/job/oss-swift-windows-x86_64/2607/

compnerd added a commit to compnerd/apple-swift that referenced this pull request Jan 27, 2020
@Lukasa
Copy link
Contributor Author

Lukasa commented Jan 27, 2020

Good catch @compnerd, that fix looks right. Sorry about that!

Lukasa added a commit to Lukasa/swift-package-manager that referenced this pull request Apr 27, 2020
The Swift PR swiftlang/swift#28538 adds support for the Scudo hardened
allocator to swiftc. This patch adds support for it to Swift Package
Manager, allowing the Package Manager to correctly plumb Scudo through
the entire build pipeline.

See the aforementioned PR for more discussion on the value of supporting
Scudo.
Lukasa added a commit to Lukasa/swift-package-manager that referenced this pull request Apr 27, 2020
The Swift PR swiftlang/swift#28538 adds support for the Scudo hardened
allocator to swiftc. This patch adds support for it to Swift Package
Manager, allowing the Package Manager to correctly plumb Scudo through
the entire build pipeline.

See the aforementioned PR for more discussion on the value of supporting
Scudo.
Lukasa added a commit to Lukasa/swift-package-manager that referenced this pull request Apr 29, 2020
The Swift PR swiftlang/swift#28538 adds support for the Scudo hardened
allocator to swiftc. This patch adds support for it to Swift Package
Manager, allowing the Package Manager to correctly plumb Scudo through
the entire build pipeline.

See the aforementioned PR for more discussion on the value of supporting
Scudo.

(cherry picked from commit 594ccaa)
Lukasa added a commit to Lukasa/swift-package-manager that referenced this pull request May 12, 2020
The Swift PR swiftlang/swift#28538 adds support for the Scudo hardened
allocator to swiftc. This patch adds support for it to Swift Package
Manager, allowing the Package Manager to correctly plumb Scudo through
the entire build pipeline. It also brings in the testing strategy from

See the aforementioned PR for more discussion on the value of supporting
Scudo.

(cherry picked from commit 594ccaa)
Lukasa added a commit to Lukasa/swift-package-manager that referenced this pull request May 15, 2020
The Swift PR swiftlang/swift#28538 adds support for the Scudo hardened
allocator to swiftc. This patch adds support for it to Swift Package
Manager, allowing the Package Manager to correctly plumb Scudo through
the entire build pipeline.

See the aforementioned PR for more discussion on the value of supporting
Scudo.

(cherry picked from commit 594ccaa)
rballard pushed a commit to swiftlang/swift-package-manager that referenced this pull request May 15, 2020
* Add support for the Scudo sanitizer. + Test sanitizers with build plans

The Swift PR swiftlang/swift#28538 adds support for the Scudo hardened
allocator to swiftc. This patch adds support for it to Swift Package
Manager, allowing the Package Manager to correctly plumb Scudo through
the entire build pipeline.

See the aforementioned PR for more discussion on the value of supporting
Scudo.

(cherry picked from commit 594ccaa)

* Test sanitizers with build plans. (#2720)

The current tests for sanitizers in SwiftPM are essentially "live": they
build a package that will do something to trigger a sanitizer, and then
run it and confirm it fails. This is a pretty fragile way to test, and
it exposes SwiftPM to a number of possible test failures that don't
have anything to do with the core mission (exposing the sanitizer).

This patch replaces those tests with build plan tests that validate that
SwiftPM correctly invokes swiftc and clang. These tests are acceptable
because for now SwiftPM doesn't have any smarts with the sanitizers: it
just passes them all through to swiftc and clang.

This patch also deletes the current live tests, as they are no longer
necessary.

(cherry picked from commit 984618b)
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.

8 participants