diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 4a2425967e4fa..b2a0f57df33aa 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -1236,6 +1236,9 @@ fn add_sanitizer_libraries( if sanitizer.contains(SanitizerSet::SAFESTACK) { link_sanitizer_runtime(sess, flavor, linker, "safestack"); } + if sanitizer.contains(SanitizerSet::REALTIME) { + link_sanitizer_runtime(sess, flavor, linker, "rtsan"); + } } fn link_sanitizer_runtime( diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index ecd82c0cc01ab..a4d5dcf71f58b 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -740,7 +740,7 @@ mod desc { pub(crate) const parse_opt_panic_strategy: &str = parse_panic_strategy; pub(crate) const parse_oom_strategy: &str = "either `panic` or `abort`"; pub(crate) const parse_relro_level: &str = "one of: `full`, `partial`, or `off`"; - pub(crate) const parse_sanitizers: &str = "comma separated list of sanitizers: `address`, `cfi`, `dataflow`, `hwaddress`, `kcfi`, `kernel-address`, `leak`, `memory`, `memtag`, `safestack`, `shadow-call-stack`, or `thread`"; + pub(crate) const parse_sanitizers: &str = "comma separated list of sanitizers: `address`, `cfi`, `dataflow`, `hwaddress`, `kcfi`, `kernel-address`, `leak`, `memory`, `memtag`, `realtime`, `safestack`, `shadow-call-stack` or `thread`"; pub(crate) const parse_sanitizer_memory_track_origins: &str = "0, 1, or 2"; pub(crate) const parse_cfguard: &str = "either a boolean (`yes`, `no`, `on`, `off`, etc), `checks`, or `nochecks`"; @@ -1185,6 +1185,7 @@ pub mod parse { "thread" => SanitizerSet::THREAD, "hwaddress" => SanitizerSet::HWADDRESS, "safestack" => SanitizerSet::SAFESTACK, + "realtime" => SanitizerSet::REALTIME, _ => return false, } } diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 7a49f0040722c..fc0e297019a60 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1460,6 +1460,7 @@ bitflags::bitflags! { const KERNELADDRESS = 1 << 9; const SAFESTACK = 1 << 10; const DATAFLOW = 1 << 11; + const REALTIME = 1 << 12; } } rustc_data_structures::external_bitflags_debug! { SanitizerSet } @@ -1510,6 +1511,7 @@ impl SanitizerSet { SanitizerSet::SHADOWCALLSTACK => "shadow-call-stack", SanitizerSet::THREAD => "thread", SanitizerSet::HWADDRESS => "hwaddress", + SanitizerSet::REALTIME => "realtime", _ => return None, }) } diff --git a/compiler/rustc_target/src/spec/targets/aarch64_apple_darwin.rs b/compiler/rustc_target/src/spec/targets/aarch64_apple_darwin.rs index 4dd39877715a7..132e79e036727 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_apple_darwin.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_apple_darwin.rs @@ -20,7 +20,10 @@ pub(crate) fn target() -> Target { cpu: "apple-m1".into(), max_atomic_width: Some(128), // FIXME: The leak sanitizer currently fails the tests, see #88132. - supported_sanitizers: SanitizerSet::ADDRESS | SanitizerSet::CFI | SanitizerSet::THREAD, + supported_sanitizers: SanitizerSet::ADDRESS + | SanitizerSet::CFI + | SanitizerSet::THREAD + | SanitizerSet::REALTIME, supports_xray: true, ..opts }, diff --git a/compiler/rustc_target/src/spec/targets/aarch64_apple_ios.rs b/compiler/rustc_target/src/spec/targets/aarch64_apple_ios.rs index 769a7b6c3919e..d49840bb1f107 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_apple_ios.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_apple_ios.rs @@ -18,7 +18,9 @@ pub(crate) fn target() -> Target { options: TargetOptions { features: "+neon,+fp-armv8,+apple-a7".into(), max_atomic_width: Some(128), - supported_sanitizers: SanitizerSet::ADDRESS | SanitizerSet::THREAD, + supported_sanitizers: SanitizerSet::ADDRESS + | SanitizerSet::THREAD + | SanitizerSet::REALTIME, ..opts }, } diff --git a/compiler/rustc_target/src/spec/targets/aarch64_apple_ios_sim.rs b/compiler/rustc_target/src/spec/targets/aarch64_apple_ios_sim.rs index 7d04034e759b4..06ab17d212352 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_apple_ios_sim.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_apple_ios_sim.rs @@ -18,7 +18,9 @@ pub(crate) fn target() -> Target { options: TargetOptions { features: "+neon,+fp-armv8,+apple-a7".into(), max_atomic_width: Some(128), - supported_sanitizers: SanitizerSet::ADDRESS | SanitizerSet::THREAD, + supported_sanitizers: SanitizerSet::ADDRESS + | SanitizerSet::THREAD + | SanitizerSet::REALTIME, ..opts }, } diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_gnu.rs index 4220d74dfc890..5f123ed9e37fc 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_gnu.rs @@ -30,7 +30,8 @@ pub(crate) fn target() -> Target { | SanitizerSet::MEMORY | SanitizerSet::MEMTAG | SanitizerSet::THREAD - | SanitizerSet::HWADDRESS, + | SanitizerSet::HWADDRESS + | SanitizerSet::REALTIME, supports_xray: true, ..base::linux_gnu::opts() }, diff --git a/compiler/rustc_target/src/spec/targets/x86_64_apple_darwin.rs b/compiler/rustc_target/src/spec/targets/x86_64_apple_darwin.rs index eba595ba7dd24..bd55c5cee7d18 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_apple_darwin.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_apple_darwin.rs @@ -21,7 +21,8 @@ pub(crate) fn target() -> Target { supported_sanitizers: SanitizerSet::ADDRESS | SanitizerSet::CFI | SanitizerSet::LEAK - | SanitizerSet::THREAD, + | SanitizerSet::THREAD + | SanitizerSet::REALTIME, supports_xray: true, ..opts }, diff --git a/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_gnu.rs index 0c8353fad182f..3f62d47f5c338 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_gnu.rs @@ -17,7 +17,8 @@ pub(crate) fn target() -> Target { | SanitizerSet::LEAK | SanitizerSet::MEMORY | SanitizerSet::SAFESTACK - | SanitizerSet::THREAD; + | SanitizerSet::THREAD + | SanitizerSet::REALTIME; base.supports_xray = true; // When we're asked to use the `rust-lld` linker by default, set the appropriate lld-using diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index de67a5f77e643..4defb5a782d43 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -1257,13 +1257,13 @@ fn supported_sanitizers( }; match &*target.triple { - "aarch64-apple-darwin" => darwin_libs("osx", &["asan", "lsan", "tsan"]), - "aarch64-apple-ios" => darwin_libs("ios", &["asan", "tsan"]), - "aarch64-apple-ios-sim" => darwin_libs("iossim", &["asan", "tsan"]), + "aarch64-apple-darwin" => darwin_libs("osx", &["asan", "lsan", "tsan", "rtsan"]), + "aarch64-apple-ios" => darwin_libs("ios", &["asan", "tsan", "rtsan"]), + "aarch64-apple-ios-sim" => darwin_libs("iossim", &["asan", "tsan", "rtsan"]), "aarch64-apple-ios-macabi" => darwin_libs("osx", &["asan", "lsan", "tsan"]), "aarch64-unknown-fuchsia" => common_libs("fuchsia", "aarch64", &["asan"]), "aarch64-unknown-linux-gnu" => { - common_libs("linux", "aarch64", &["asan", "lsan", "msan", "tsan", "hwasan"]) + common_libs("linux", "aarch64", &["asan", "lsan", "msan", "tsan", "hwasan", "rtsan"]) } "aarch64-unknown-linux-ohos" => { common_libs("linux", "aarch64", &["asan", "lsan", "msan", "tsan", "hwasan"]) @@ -1271,7 +1271,7 @@ fn supported_sanitizers( "loongarch64-unknown-linux-gnu" | "loongarch64-unknown-linux-musl" => { common_libs("linux", "loongarch64", &["asan", "lsan", "msan", "tsan"]) } - "x86_64-apple-darwin" => darwin_libs("osx", &["asan", "lsan", "tsan"]), + "x86_64-apple-darwin" => darwin_libs("osx", &["asan", "lsan", "tsan", "rtsan"]), "x86_64-unknown-fuchsia" => common_libs("fuchsia", "x86_64", &["asan"]), "x86_64-apple-ios" => darwin_libs("iossim", &["asan", "tsan"]), "x86_64-apple-ios-macabi" => darwin_libs("osx", &["asan", "lsan", "tsan"]), @@ -1281,9 +1281,11 @@ fn supported_sanitizers( } "x86_64-unknown-illumos" => common_libs("illumos", "x86_64", &["asan"]), "x86_64-pc-solaris" => common_libs("solaris", "x86_64", &["asan"]), - "x86_64-unknown-linux-gnu" => { - common_libs("linux", "x86_64", &["asan", "dfsan", "lsan", "msan", "safestack", "tsan"]) - } + "x86_64-unknown-linux-gnu" => common_libs( + "linux", + "x86_64", + &["asan", "dfsan", "lsan", "msan", "safestack", "tsan", "rtsan"], + ), "x86_64-unknown-linux-musl" => { common_libs("linux", "x86_64", &["asan", "lsan", "msan", "tsan"]) } diff --git a/src/doc/unstable-book/src/compiler-flags/sanitizer.md b/src/doc/unstable-book/src/compiler-flags/sanitizer.md index 2f9d4d22e5a85..d2e2180bdd6fd 100644 --- a/src/doc/unstable-book/src/compiler-flags/sanitizer.md +++ b/src/doc/unstable-book/src/compiler-flags/sanitizer.md @@ -24,6 +24,8 @@ This feature allows for use of one of following sanitizers: AddressSanitizer, but based on partial hardware assistance. * [LeakSanitizer](#leaksanitizer) a run-time memory leak detector. * [MemorySanitizer](#memorysanitizer) a detector of uninitialized reads. + * [RealtimeSanitizer](#realtimesanitizer) a detector of functions with + non-deterministic execution time in realtime contexts. * [ThreadSanitizer](#threadsanitizer) a fast data race detector. * Those that apart from testing, may be used in production: @@ -43,11 +45,11 @@ This feature allows for use of one of following sanitizers: To enable a sanitizer compile with `-Zsanitizer=address`, `-Zsanitizer=cfi`, `-Zsanitizer=dataflow`,`-Zsanitizer=hwaddress`, `-Zsanitizer=leak`, -`-Zsanitizer=memory`, `-Zsanitizer=memtag`, `-Zsanitizer=shadow-call-stack`, or -`-Zsanitizer=thread`. You might also need the `--target` and `build-std` flags. -If you're working with other languages that are also instrumented with sanitizers, -you might need the `external-clangrt` flag. See the section on -[working with other languages](#working-with-other-languages). +`-Zsanitizer=memory`, `-Zsanitizer=memtag`, `-Zsanitizer=realtime`, +`-Zsanitizer=shadow-call-stack` or `-Zsanitizer=thread`. You might also need the +`--target` and `build-std` flags. If you're working with other languages that are also +instrumented with sanitizers, you might need the `external-clangrt` flag. See +the section on [working with other languages](#working-with-other-languages). Example: ```shell @@ -865,6 +867,81 @@ WARNING: ThreadSanitizer: data race (pid=10574) Location is global 'example::A::h43ac149ddf992709' of size 8 at 0x5632dfe3d030 (example+0x000000bd9030) ``` +# RealtimeSanitizer +RealtimeSanitizer detects non-deterministic execution time calls in realtime contexts. +Usafe of RTSan is on a function-by-function basis. The sanitizer only inspects code that +is marked as being real-time sensitive. Currently this is done with the functions +`__rtsan_realtime_enter()` and `__rtsan_realtime_exit()`. Any function that is subject to +real-time contraints must call `__rtsan_realtime_enter()` at its entry point and +`__rtsan_realtime_exit()` at every exit point. To not miss any exit points, it is +recommended to use a guard object that exits the real-time context on drop + +Controlling this sanitizer using attributes isn't yet supported in Rust. The +sanitizer exports the following functions that allow using it. These functions +are not stable. When the attributes are added to rust, using them over these +functions is recommended. + +```rust +/// Each call to `__rtsan_realtime_enter()` must be matched with exactly one call to `__rtsan_realtime_exit()`. +/// The same goes for `__rtsan_disable()` with `__rtsan_enable()` +extern "C" { + /// Enters a realtime context. Can be nested. Must be matched with one later call to `__rtsan_realtime_exit()`. + pub fn __rtsan_realtime_enter(); + /// Exits a realtime context. Must be matched to a preceding call to `__rtsan_realtime_enter()`. + /// After calling this the program could still be in a realtime context, as they can be nested. + pub fn __rtsan_realtime_exit(); + /// Disables the sanitizer. Must be matched to a later call to `__rtsan_enable()`. + /// Useful for situations where you know the systemcall you do has a deterministic execution time. + pub fn __rtsan_disable(); + /// Enables the sanitizer again. Must be matched to a preceding call to `__rtsan_disable()`. + /// Useful when you know that a systemcall you do has a deterministic execution time in that situation. + pub fn __rtsan_enable(); + /// Must be called before any other sanitizer calls are made. You should place this at the entry point of + /// your program. + pub fn __rtsan_ensure_initialized(); + /// To mark your function as one with a non-deterministic execution time, place this call as the first line + /// in the function. If rtsan hits this call while in a real-time context it will about in the same way that + /// calling an illegal system call does. + pub fn __rtsan_notify_blocking_call(blocking_function_name: *const core::ffi::c_char); +} +``` + +See the [Clang RealtimeSanitizer documentation][clang-rtsan] for more details. + +## Example + +```rust +fn main() { + unsafe { + __rtsan_ensure_initialized(); + __rtsan_realtime_enter(); + } + let vec = vec![0, 1, 2]; + unsafe { + __rtsan_realtime_exit(); + } + println!("alloc not detected") +} +``` + +```shell +==8670==ERROR: RealtimeSanitizer: unsafe-library-call +Intercepted call to real-time unsafe function `malloc` in real-time context! + #0 0x00010107b0d8 in malloc rtsan_interceptors_posix.cpp:792 + #1 0x000100d94e70 in alloc::alloc::Global::alloc_impl::h9e1fc3206c868eea+0xa0 (realtime_vec:arm64+0x100000e70) + #2 0x000100d94d90 in alloc::alloc::exchange_malloc::hd45b5788339eb5c8+0x48 (realtime_vec:arm64+0x100000d90) + #3 0x000100d95020 in realtime_vec::main::hea6bd69b03eb9ca1+0x24 (realtime_vec:arm64+0x100001020) + #4 0x000100d94a28 in core::ops::function::FnOnce::call_once::h493b6cb9dd87d87c+0xc (realtime_vec:arm64+0x100000a28) + #5 0x000100d949b8 in std::sys::backtrace::__rust_begin_short_backtrace::hfcddb06c73c19eea+0x8 (realtime_vec:arm64+0x1000009b8) + #6 0x000100d9499c in std::rt::lang_start::_$u7b$$u7b$closure$u7d$$u7d$::h202288c05a2064f0+0xc (realtime_vec:arm64+0x10000099c) + #7 0x000100d9fa34 in std::rt::lang_start_internal::h6c763158a05ac05f+0x6c (realtime_vec:arm64+0x10000ba34) + #8 0x000100d94980 in std::rt::lang_start::h1c29cc56df0598b4+0x38 (realtime_vec:arm64+0x100000980) + #9 0x000100d95118 in main+0x20 (realtime_vec:arm64+0x100001118) + #10 0x000183a46b94 in start+0x17b8 (dyld:arm64+0xfffffffffff3ab94) + +SUMMARY: RealtimeSanitizer: unsafe-library-call rtsan_interceptors_posix.cpp:792 in malloc +``` + # Instrumentation of external dependencies and std The sanitizers to varying degrees work correctly with partially instrumented @@ -918,6 +995,7 @@ Sanitizers produce symbolized stacktraces when llvm-symbolizer binary is in `PAT * [MemorySanitizer in Clang][clang-msan] * [MemTagSanitizer in LLVM][llvm-memtag] * [ThreadSanitizer in Clang][clang-tsan] +* [RealtimeSanitizer in Clang][clang-rtsan] [clang-asan]: https://clang.llvm.org/docs/AddressSanitizer.html [clang-cfi]: https://clang.llvm.org/docs/ControlFlowIntegrity.html @@ -926,6 +1004,7 @@ Sanitizers produce symbolized stacktraces when llvm-symbolizer binary is in `PAT [clang-kcfi]: https://clang.llvm.org/docs/ControlFlowIntegrity.html#fsanitize-kcfi [clang-lsan]: https://clang.llvm.org/docs/LeakSanitizer.html [clang-msan]: https://clang.llvm.org/docs/MemorySanitizer.html +[clan-rtsan]: https://clang.llvm.org/docs/RealtimeSanitizer.html [clang-safestack]: https://clang.llvm.org/docs/SafeStack.html [clang-scs]: https://clang.llvm.org/docs/ShadowCallStack.html [clang-tsan]: https://clang.llvm.org/docs/ThreadSanitizer.html diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index 9b9d94bbead09..77368e4b1da6a 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -169,6 +169,7 @@ pub enum Sanitizer { ShadowCallStack, Thread, Hwaddress, + Realtime, } /// Configuration for compiletest diff --git a/src/tools/compiletest/src/directive-list.rs b/src/tools/compiletest/src/directive-list.rs index 2ecb4fc865213..30f8e14260f32 100644 --- a/src/tools/compiletest/src/directive-list.rs +++ b/src/tools/compiletest/src/directive-list.rs @@ -162,6 +162,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "needs-sanitizer-shadow-call-stack", "needs-sanitizer-support", "needs-sanitizer-thread", + "needs-sanitizer-realtime", "needs-std-debug-assertions", "needs-subprocess", "needs-symlink", diff --git a/src/tools/compiletest/src/header/needs.rs b/src/tools/compiletest/src/header/needs.rs index b1165f4bb184f..11ba4a1526a15 100644 --- a/src/tools/compiletest/src/header/needs.rs +++ b/src/tools/compiletest/src/header/needs.rs @@ -79,6 +79,11 @@ pub(super) fn handle_needs( condition: cache.sanitizer_safestack, ignore_reason: "ignored on targets without SafeStack support", }, + Need { + name: "needs-sanitizer-realtime", + condition: cache.sanitizer_realtime, + ignore_reason: "ignored on targets without RealtimeSanitizer support", + }, Need { name: "needs-enzyme", condition: config.has_enzyme, @@ -327,6 +332,7 @@ pub(super) struct CachedNeedsConditions { sanitizer_memtag: bool, sanitizer_shadow_call_stack: bool, sanitizer_safestack: bool, + sanitizer_realtime: bool, xray: bool, rust_lld: bool, dlltool: bool, @@ -353,6 +359,7 @@ impl CachedNeedsConditions { sanitizer_memtag: sanitizers.contains(&Sanitizer::Memtag), sanitizer_shadow_call_stack: sanitizers.contains(&Sanitizer::ShadowCallStack), sanitizer_safestack: sanitizers.contains(&Sanitizer::Safestack), + sanitizer_realtime: sanitizers.contains(&Sanitizer::Realtime), xray: config.target_cfg().xray, // For tests using the `needs-rust-lld` directive (e.g. for `-Clink-self-contained=+linker`), diff --git a/tests/ui/sanitizer/realtime_vec.rs b/tests/ui/sanitizer/realtime_vec.rs new file mode 100644 index 0000000000000..ef7515ab0a2eb --- /dev/null +++ b/tests/ui/sanitizer/realtime_vec.rs @@ -0,0 +1,28 @@ +//@ needs-sanitizer-support +//@ needs-sanitizer-realtime +// +//@ compile-flags: -Z sanitizer=realtime +// +//@ run-fail +//@ error-pattern: Intercepted call to real-time unsafe function `malloc` in real-time context! + +extern "C" { + pub fn __rtsan_realtime_enter(); + pub fn __rtsan_realtime_exit(); + pub fn __rtsan_disable(); + pub fn __rtsan_enable(); + pub fn __rtsan_ensure_initialized(); + pub fn __rtsan_notify_blocking_call(blocking_function_name: *const core::ffi::c_char); +} + +fn main() { + unsafe { + __rtsan_ensure_initialized(); + __rtsan_realtime_enter(); + } + let vec = vec![0, 1, 2]; + unsafe { + __rtsan_realtime_exit(); + } + println!("alloc not detected") +}