From 9d3d49fa755c28b21c3b4771faae65cf418dec5a Mon Sep 17 00:00:00 2001 From: Chris Apple Date: Wed, 24 Jul 2024 14:25:44 -0700 Subject: [PATCH 1/6] [clang][rtsan] Introduce realtime sanitizer codegen and driver --- clang/include/clang/Basic/Sanitizers.def | 3 ++ clang/include/clang/Driver/SanitizerArgs.h | 1 + clang/lib/CodeGen/BackendUtil.cpp | 8 ++++ clang/lib/CodeGen/CodeGenFunction.cpp | 6 +++ clang/lib/Driver/SanitizerArgs.cpp | 14 ++++-- clang/lib/Driver/ToolChains/CommonArgs.cpp | 6 +++ clang/lib/Driver/ToolChains/Darwin.cpp | 8 ++++ clang/lib/Driver/ToolChains/Linux.cpp | 1 + clang/test/CodeGen/rtsan_attribute_inserted.c | 7 +++ clang/test/CodeGen/rtsan_insert_at_entry.c | 9 ++++ clang/test/CodeGen/rtsan_insert_at_exit.c | 9 ++++ .../rtsan_no_attribute_sanitizer_disabled.c | 8 ++++ clang/test/Driver/fsanitize.c | 46 +++++++++++++++++++ clang/test/Driver/rtsan.c | 14 ++++++ 14 files changed, 135 insertions(+), 5 deletions(-) create mode 100644 clang/test/CodeGen/rtsan_attribute_inserted.c create mode 100644 clang/test/CodeGen/rtsan_insert_at_entry.c create mode 100644 clang/test/CodeGen/rtsan_insert_at_exit.c create mode 100644 clang/test/CodeGen/rtsan_no_attribute_sanitizer_disabled.c create mode 100644 clang/test/Driver/rtsan.c diff --git a/clang/include/clang/Basic/Sanitizers.def b/clang/include/clang/Basic/Sanitizers.def index bee35e9dca7c39..9223f62b3639a7 100644 --- a/clang/include/clang/Basic/Sanitizers.def +++ b/clang/include/clang/Basic/Sanitizers.def @@ -79,6 +79,9 @@ SANITIZER("thread", Thread) // Numerical stability sanitizer. SANITIZER("numerical", NumericalStability) +// RealtimeSanitizer +SANITIZER("realtime", Realtime) + // LeakSanitizer SANITIZER("leak", Leak) diff --git a/clang/include/clang/Driver/SanitizerArgs.h b/clang/include/clang/Driver/SanitizerArgs.h index 47ef175302679f..c13a640268f0c7 100644 --- a/clang/include/clang/Driver/SanitizerArgs.h +++ b/clang/include/clang/Driver/SanitizerArgs.h @@ -106,6 +106,7 @@ class SanitizerArgs { bool needsNsanRt() const { return Sanitizers.has(SanitizerKind::NumericalStability); } + bool needsRtsanRt() const { return Sanitizers.has(SanitizerKind::Realtime); } bool hasMemTag() const { return hasMemtagHeap() || hasMemtagStack() || hasMemtagGlobals(); diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp index 81e6702d5de666..95aa328c0245de 100644 --- a/clang/lib/CodeGen/BackendUtil.cpp +++ b/clang/lib/CodeGen/BackendUtil.cpp @@ -78,6 +78,7 @@ #include "llvm/Transforms/Instrumentation/MemorySanitizer.h" #include "llvm/Transforms/Instrumentation/NumericalStabilitySanitizer.h" #include "llvm/Transforms/Instrumentation/PGOInstrumentation.h" +#include "llvm/Transforms/Instrumentation/RealtimeSanitizer.h" #include "llvm/Transforms/Instrumentation/SanitizerBinaryMetadata.h" #include "llvm/Transforms/Instrumentation/SanitizerCoverage.h" #include "llvm/Transforms/Instrumentation/ThreadSanitizer.h" @@ -989,6 +990,13 @@ void EmitAssemblyHelper::RunOptimizationPipeline( FPM.addPass(BoundsCheckingPass()); }); + if (LangOpts.Sanitize.has(SanitizerKind::Realtime)) + PB.registerScalarOptimizerLateEPCallback( + [](FunctionPassManager &FPM, OptimizationLevel Level) { + RealtimeSanitizerOptions Opts; + FPM.addPass(RealtimeSanitizerPass(Opts)); + }); + // Don't add sanitizers if we are here from ThinLTO PostLink. That already // done on PreLink stage. if (!IsThinLTOPostLink) { diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp index 2b2e23f1e5d7fb..fc388f6f879a1e 100644 --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -845,6 +845,12 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy, if (SanOpts.has(SanitizerKind::ShadowCallStack)) Fn->addFnAttr(llvm::Attribute::ShadowCallStack); + if (SanOpts.has(SanitizerKind::Realtime)) { + for (const FunctionEffectWithCondition &Fe : FD->getFunctionEffects()) + if (Fe.Effect.kind() == FunctionEffect::Kind::NonBlocking) + Fn->addFnAttr(llvm::Attribute::SanitizeRealtime); + } + // Apply fuzzing attribute to the function. if (SanOpts.hasOneOf(SanitizerKind::Fuzzer | SanitizerKind::FuzzerNoLink)) Fn->addFnAttr(llvm::Attribute::OptForFuzzing); diff --git a/clang/lib/Driver/SanitizerArgs.cpp b/clang/lib/Driver/SanitizerArgs.cpp index 1fd870b72286e5..7b38f20fc8d059 100644 --- a/clang/lib/Driver/SanitizerArgs.cpp +++ b/clang/lib/Driver/SanitizerArgs.cpp @@ -552,11 +552,15 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC, SanitizerKind::Leak | SanitizerKind::Thread | SanitizerKind::Memory | SanitizerKind::KernelAddress | SanitizerKind::Scudo | SanitizerKind::SafeStack), - std::make_pair(SanitizerKind::MemTag, - SanitizerKind::Address | SanitizerKind::KernelAddress | - SanitizerKind::HWAddress | - SanitizerKind::KernelHWAddress), - std::make_pair(SanitizerKind::KCFI, SanitizerKind::Function)}; + std::make_pair(SanitizerKind::MemTag, SanitizerKind::Address | + SanitizerKind::KernelAddress | + SanitizerKind::HWAddress | + SanitizerKind::KernelHWAddress), + std::make_pair(SanitizerKind::KCFI, SanitizerKind::Function), + std::make_pair(SanitizerKind::Realtime, + SanitizerKind::Address | SanitizerKind::Thread | + SanitizerKind::Undefined | SanitizerKind::Memory)}; + // Enable toolchain specific default sanitizers if not explicitly disabled. SanitizerMask Default = TC.getDefaultSanitizers() & ~AllRemove; diff --git a/clang/lib/Driver/ToolChains/CommonArgs.cpp b/clang/lib/Driver/ToolChains/CommonArgs.cpp index 6e9744607d9ebc..61ec730aa4a5b5 100644 --- a/clang/lib/Driver/ToolChains/CommonArgs.cpp +++ b/clang/lib/Driver/ToolChains/CommonArgs.cpp @@ -1431,6 +1431,8 @@ collectSanitizerRuntimes(const ToolChain &TC, const ArgList &Args, if (!Args.hasArg(options::OPT_shared)) HelperStaticRuntimes.push_back("hwasan-preinit"); } + if (SanArgs.needsRtsanRt() && SanArgs.linkRuntimes()) + SharedRuntimes.push_back("rtsan"); } // The stats_client library is also statically linked into DSOs. @@ -1456,6 +1458,10 @@ collectSanitizerRuntimes(const ToolChain &TC, const ArgList &Args, StaticRuntimes.push_back("asan_cxx"); } + if (!SanArgs.needsSharedRt() && SanArgs.needsRtsanRt() && + SanArgs.linkRuntimes()) + StaticRuntimes.push_back("rtsan"); + if (!SanArgs.needsSharedRt() && SanArgs.needsMemProfRt()) { StaticRuntimes.push_back("memprof"); if (SanArgs.linkCXXRuntimes()) diff --git a/clang/lib/Driver/ToolChains/Darwin.cpp b/clang/lib/Driver/ToolChains/Darwin.cpp index ee6890d5b98f0e..9dfb803e12bbee 100644 --- a/clang/lib/Driver/ToolChains/Darwin.cpp +++ b/clang/lib/Driver/ToolChains/Darwin.cpp @@ -1519,6 +1519,8 @@ void DarwinClang::AddLinkRuntimeLibArgs(const ArgList &Args, const char *sanitizer = nullptr; if (Sanitize.needsUbsanRt()) { sanitizer = "UndefinedBehaviorSanitizer"; + } else if (Sanitize.needsRtsanRt()) { + sanitizer = "RealtimeSanitizer"; } else if (Sanitize.needsAsanRt()) { sanitizer = "AddressSanitizer"; } else if (Sanitize.needsTsanRt()) { @@ -1541,6 +1543,11 @@ void DarwinClang::AddLinkRuntimeLibArgs(const ArgList &Args, AddLinkSanitizerLibArgs(Args, CmdArgs, "asan"); } } + if (Sanitize.needsRtsanRt()) { + assert(Sanitize.needsSharedRt() && + "Static sanitizer runtimes not supported"); + AddLinkSanitizerLibArgs(Args, CmdArgs, "rtsan"); + } if (Sanitize.needsLsanRt()) AddLinkSanitizerLibArgs(Args, CmdArgs, "lsan"); if (Sanitize.needsUbsanRt()) { @@ -3531,6 +3538,7 @@ SanitizerMask Darwin::getSupportedSanitizers() const { Res |= SanitizerKind::Address; Res |= SanitizerKind::PointerCompare; Res |= SanitizerKind::PointerSubtract; + Res |= SanitizerKind::Realtime; Res |= SanitizerKind::Leak; Res |= SanitizerKind::Fuzzer; Res |= SanitizerKind::FuzzerNoLink; diff --git a/clang/lib/Driver/ToolChains/Linux.cpp b/clang/lib/Driver/ToolChains/Linux.cpp index 2265138edbffbe..96680b3412a2db 100644 --- a/clang/lib/Driver/ToolChains/Linux.cpp +++ b/clang/lib/Driver/ToolChains/Linux.cpp @@ -800,6 +800,7 @@ SanitizerMask Linux::getSupportedSanitizers() const { Res |= SanitizerKind::Address; Res |= SanitizerKind::PointerCompare; Res |= SanitizerKind::PointerSubtract; + Res |= SanitizerKind::Realtime; Res |= SanitizerKind::Fuzzer; Res |= SanitizerKind::FuzzerNoLink; Res |= SanitizerKind::KernelAddress; diff --git a/clang/test/CodeGen/rtsan_attribute_inserted.c b/clang/test/CodeGen/rtsan_attribute_inserted.c new file mode 100644 index 00000000000000..ecba06703fee2c --- /dev/null +++ b/clang/test/CodeGen/rtsan_attribute_inserted.c @@ -0,0 +1,7 @@ +// RUN: %clang -target x86_64-unknown-linux -fsanitize=realtime %s -S -emit-llvm -o - | FileCheck %s + +float process(float *a) [[clang::nonblocking]] { return *a; } + +// CHECK-LABEL: @process{{.*}}#0 { +// CHECK: attributes #0 = { +// CHECK-SAME: {{.*sanitize_realtime.*}} diff --git a/clang/test/CodeGen/rtsan_insert_at_entry.c b/clang/test/CodeGen/rtsan_insert_at_entry.c new file mode 100644 index 00000000000000..22d568425cbe74 --- /dev/null +++ b/clang/test/CodeGen/rtsan_insert_at_entry.c @@ -0,0 +1,9 @@ +// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -fsanitize=realtime -emit-llvm -o - %s | FileCheck %s + +// The first instruction after the function is entred should be a call to +// enable the realtime sanitizer stack + +int foo(int *a) [[clang::nonblocking]] { return *a; } +// CHECK-LABEL: define{{.*}}@foo +// CHECK-NEXT: entry: +// CHECK-NEXT: call{{.*}}__rtsan_realtime_enter diff --git a/clang/test/CodeGen/rtsan_insert_at_exit.c b/clang/test/CodeGen/rtsan_insert_at_exit.c new file mode 100644 index 00000000000000..32b381f163f595 --- /dev/null +++ b/clang/test/CodeGen/rtsan_insert_at_exit.c @@ -0,0 +1,9 @@ +// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -fsanitize=realtime -emit-llvm -o - %s | FileCheck %s + +// __rtsan_realtime_exit should be inserted at all function returns + +int bar(int* x) [[clang::nonblocking]] { + return *x; +} +// CHECK-LABEL: call{{.*}}__rtsan_realtime_exit +// CHECK-NEXT: ret diff --git a/clang/test/CodeGen/rtsan_no_attribute_sanitizer_disabled.c b/clang/test/CodeGen/rtsan_no_attribute_sanitizer_disabled.c new file mode 100644 index 00000000000000..85742d8b63d9ec --- /dev/null +++ b/clang/test/CodeGen/rtsan_no_attribute_sanitizer_disabled.c @@ -0,0 +1,8 @@ +// RUN: %clang -target x86_64-unknown-linux %s -S -emit-llvm -o - | FileCheck %s + + +float process(float *a) [[clang::nonblocking]] { return *a; } + +// Without the -fsanitize=realtime flag, we shouldn't attach +// the attribute +// CHECK-NOT: {{.*sanitize_realtime.*}} diff --git a/clang/test/Driver/fsanitize.c b/clang/test/Driver/fsanitize.c index 678fa432fb0a0a..f86c978f221cd4 100644 --- a/clang/test/Driver/fsanitize.c +++ b/clang/test/Driver/fsanitize.c @@ -1040,3 +1040,49 @@ // RUN: not %clang --target=aarch64-none-elf -fsanitize=dataflow %s -### 2>&1 | FileCheck %s -check-prefix=UNSUPPORTED-BAREMETAL // RUN: not %clang --target=arm-arm-none-eabi -fsanitize=shadow-call-stack %s -### 2>&1 | FileCheck %s -check-prefix=UNSUPPORTED-BAREMETAL // UNSUPPORTED-BAREMETAL: unsupported option '-fsanitize={{.*}}' for target + +// RUN: %clang --target=x86_64-apple-darwin -fsanitize=realtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-RTSAN-X86-64-DARWIN +// CHECK-RTSAN-X86-64-DARWIN-NOT: unsupported option + +// RUN: %clang --target=x86_64-apple-darwin -fsanitize=realtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-RTSAN-X86-64-DARWIN +// CHECK-RTSAN-X86-64-DARWIN-NOT: unsupported option +// RUN: %clang --target=x86_64-apple-macos -fsanitize=realtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-RTSAN-X86-64-MACOS +// CHECK-RTSAN-X86-64-MACOS-NOT: unsupported option +// RUN: %clang --target=arm64-apple-macos -fsanitize=realtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-RTSAN-ARM64-MACOS +// CHECK-RTSAN-ARM64-MACOS-NOT: unsupported option + +// RUN: %clang --target=arm64-apple-ios-simulator -fsanitize=realtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-RTSAN-ARM64-IOSSIMULATOR +// CHECK-RTSAN-ARM64-IOSSIMULATOR-NOT: unsupported option + +// RUN: %clang --target=arm64-apple-watchos-simulator -fsanitize=realtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-RTSAN-ARM64-WATCHOSSIMULATOR +// CHECK-RTSAN-ARM64-WATCHOSSIMULATOR-NOT: unsupported option + +// RUN: %clang --target=arm64-apple-tvos-simulator -fsanitize=realtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-RTSAN-ARM64-TVOSSIMULATOR +// CHECK-RTSAN-ARM64-TVOSSIMULATOR-NOT: unsupported option + +// RUN: %clang --target=x86_64-apple-ios-simulator -fsanitize=realtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-RTSAN-X86-64-IOSSIMULATOR +// CHECK-RTSAN-X86-64-IOSSIMULATOR-NOT: unsupported option + +// RUN: %clang --target=x86_64-apple-watchos-simulator -fsanitize=realtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-RTSAN-X86-64-WATCHOSSIMULATOR +// CHECK-RTSAN-X86-64-WATCHOSSIMULATOR-NOT: unsupported option + +// RUN: %clang --target=x86_64-apple-tvos-simulator -fsanitize=realtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-RTSAN-X86-64-TVOSSIMULATOR +// CHECK-RTSAN-X86-64-TVOSSIMULATOR-NOT: unsupported option + +// RUN: %clang --target=x86_64-linux-gnu -fsanitize=realtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-RTSAN-X86-64-LINUX +// CHECK-RTSAN-X86-64-LINUX-NOT: unsupported option + +// RUN: not %clang --target=i386-pc-openbsd -fsanitize=realtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-RTSAN-OPENBSD +// CHECK-RTSAN-OPENBSD: unsupported option '-fsanitize=realtime' for target 'i386-pc-openbsd' + +// RUN: not %clang --target=x86_64-linux-gnu -fsanitize=realtime,thread %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-REALTIME-TSAN +// CHECK-REALTIME-TSAN: error: invalid argument '-fsanitize=realtime' not allowed with '-fsanitize=thread' + +// RUN: not %clang --target=x86_64-linux-gnu -fsanitize=realtime,address %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-REALTIME-ASAN +// CHECK-REALTIME-ASAN: error: invalid argument '-fsanitize=realtime' not allowed with '-fsanitize=address' + +// RUN: not %clang --target=x86_64-linux-gnu -fsanitize=realtime,memory %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-REALTIME-MSAN +// CHECK-REALTIME-MSAN: error: invalid argument '-fsanitize=realtime' not allowed with '-fsanitize=memory' + +// RUN: not %clang --target=x86_64-linux-gnu -fsanitize=realtime,undefined %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-REALTIME-UBSAN +// CHECK-REALTIME-UBSAN: error: invalid argument '-fsanitize=realtime' not allowed with '-fsanitize=undefined' diff --git a/clang/test/Driver/rtsan.c b/clang/test/Driver/rtsan.c new file mode 100644 index 00000000000000..999e88d066427c --- /dev/null +++ b/clang/test/Driver/rtsan.c @@ -0,0 +1,14 @@ +// RUN: %clang -target x86_64-unknown-linux -fsanitize=realtime %s -S -emit-llvm -o - | FileCheck %s +// RUN: %clang -O1 -target x86_64-unknown-linux -fsanitize=realtime %s -S -emit-llvm -o - | FileCheck %s +// RUN: %clang -O2 -target x86_64-unknown-linux -fsanitize=realtime %s -S -emit-llvm -o - | FileCheck %s +// RUN: %clang -O3 -target x86_64-unknown-linux -fsanitize=realtime %s -S -emit-llvm -o - | FileCheck %s +// RUN: %clang -target x86_64-unknown-linux -fsanitize=realtime %s -S -emit-llvm -flto=thin -o - | FileCheck %s +// RUN: %clang -O2 -target x86_64-unknown-linux -fsanitize=realtime %s -S -emit-llvm -flto=thin -o - | FileCheck %s +// RUN: %clang -target x86_64-unknown-linux -fsanitize=realtime %s -S -emit-llvm -flto -o - | FileCheck %s +// RUN: %clang -O2 -target x86_64-unknown-linux -fsanitize=realtime %s -S -emit-llvm -flto -o - | FileCheck %s + +// Ensure the rtsan_realtime calls are never optimized away + +int foo(int *a) [[clang::nonblocking]] { return *a; } +// CHECK: __rtsan_realtime_enter +// CHECK: __rtsan_realtime_exit From 57de56187f721fa257ba84923e9f24bd910074b5 Mon Sep 17 00:00:00 2001 From: Chris Apple Date: Fri, 9 Aug 2024 08:42:22 -0700 Subject: [PATCH 2/6] [PR] Test hasAnyFunctionEffects before iterating --- clang/lib/CodeGen/CodeGenFunction.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp index fc388f6f879a1e..364333e780b6d7 100644 --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -846,9 +846,12 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy, Fn->addFnAttr(llvm::Attribute::ShadowCallStack); if (SanOpts.has(SanitizerKind::Realtime)) { - for (const FunctionEffectWithCondition &Fe : FD->getFunctionEffects()) - if (Fe.Effect.kind() == FunctionEffect::Kind::NonBlocking) - Fn->addFnAttr(llvm::Attribute::SanitizeRealtime); + if (FD && FD->getASTContext().hasAnyFunctionEffects()) { + for (const FunctionEffectWithCondition &Fe : FD->getFunctionEffects()) { + if (Fe.Effect.kind() == FunctionEffect::Kind::NonBlocking) + Fn->addFnAttr(llvm::Attribute::SanitizeRealtime); + } + } } // Apply fuzzing attribute to the function. From 88c1efc660d6bf9102ccda61d8466902c24a252f Mon Sep 17 00:00:00 2001 From: Chris Apple Date: Sat, 10 Aug 2024 08:41:31 -0700 Subject: [PATCH 3/6] Documentation draft 1 --- clang/docs/RealtimeSanitizer.rst | 85 ++++++++++++++++++++++++++++++++ clang/docs/ReleaseNotes.rst | 5 ++ clang/docs/UsersManual.rst | 2 + clang/docs/index.rst | 1 + 4 files changed, 93 insertions(+) create mode 100644 clang/docs/RealtimeSanitizer.rst diff --git a/clang/docs/RealtimeSanitizer.rst b/clang/docs/RealtimeSanitizer.rst new file mode 100644 index 00000000000000..abc34dde2c4129 --- /dev/null +++ b/clang/docs/RealtimeSanitizer.rst @@ -0,0 +1,85 @@ +================= +RealtimeSanitizer +================= + +.. contents:: + :local: + +Introduction +============ +RealtimeSanitizer (a.k.a. RTSan) is a real-time safety testing tool for C and +C++ projects. RTSan can be used to detect real-time violations,such as calls to +methods that are not safe for use in functions with deterministic runtime +requirements. + +The tool can detect the following types of real-time violations: + +* System calls +* Allocations +* Exceptions + +These checks are put in place when compiling with the +``-fsanitize=realtime`` flag, for functions marked with +``[[clang::nonblocking]]``. + +.. code-block:: c + + void process_audio(float* buffer) [[clang::nonblocking]] { + ... + } + +The runtime slowdown introduced by RealtimeSanitizer is trivial. Code in +real-time contexts without real-time safety violations have no slowdown. + +How to build +============ + +Build LLVM/Clang with `CMake ` and enable the +``compiler-rt`` runtime. An example CMake configuration that will allow for the +use/testing of RealtimeSanitizer: + +.. code-block:: console + + $ cmake -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS="clang" -DLLVM_ENABLE_RUNTIMES="compiler-rt" /llvm + +Usage +===== + +There are two requirements: + +1. The code must be compiled with the ``-fsanitize=realtime`` flag. +2. Functions that are subject to real-time constraints must be marked + with the ``[[clang::nonblocking]]`` attribute. + +Typically, these attributes should be added onto the functions that are entry +points for threads with real-time priority. These threads are subject to a fixed +callback time, such as audio callback threads or rendering loops in video game +code. + +.. code-block:: console + + % cat example_realtime_violation.cpp + int main() [[clang::nonblocking]] { + int* p = new int; + return 0; + } + + # Compile and link + % clang -fsanitize=realtime -g example_realtime_violation.cpp + +If a real-time safety violation is detected in a ``[[clang::nonblocking]]`` +context, or any function invoked by that function, the program will exit with a +non-zero exit code. + +.. code-block:: console + + % clang -fsanitize=realtime -g example_realtime_violation.cpp + % ./a.out + Real-time violation: intercepted call to real-time unsafe function `malloc` in real-time context! Stack trace: + #0 0x00010065ad9c in __rtsan::PrintStackTrace() rtsan_stack.cpp:45 + #1 0x00010065abcc in __rtsan::Context::ExpectNotRealtime(char const*) rtsan_context.cpp:78 + #2 0x00010065b8d0 in malloc rtsan_interceptors.cpp:289 + #3 0x000195bd7bd0 in operator new(unsigned long)+0x1c (libc++abi.dylib:arm64+0x16bd0) + #4 0xb338001000dbf68 () + #5 0x0001958960dc () + #6 0x45737ffffffffffc () diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 6796a619ba97f8..79f721169fdbb2 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -349,6 +349,11 @@ Moved checkers Sanitizers ---------- +- Introduced Realtime Sanitizer, activated by using the -fsanitize=realtime + flag. This sanitizer detects unsafe system library calls, such as memory + allocations and mutex locks. If any such function is called during invocation + of a function marked with the ``[[clang::nonblocking]]`` attribute, an error + is printed to the console and the process exits non-zero. Python Binding Changes ---------------------- diff --git a/clang/docs/UsersManual.rst b/clang/docs/UsersManual.rst index 64e991451bf703..bdb0c4038e2b01 100644 --- a/clang/docs/UsersManual.rst +++ b/clang/docs/UsersManual.rst @@ -2063,6 +2063,8 @@ are listed below. integrity. - ``-fsanitize=safe-stack``: :doc:`safe stack ` protection against stack-based memory corruption errors. + - ``-fsanitize=realtime``: :doc:`RealtimeSanitizer`, + a real-time safety checker. There are more fine-grained checks available: see the :ref:`list ` of specific kinds of diff --git a/clang/docs/index.rst b/clang/docs/index.rst index 9bae0bd83243bd..4a497f4d9bcc3c 100644 --- a/clang/docs/index.rst +++ b/clang/docs/index.rst @@ -32,6 +32,7 @@ Using Clang as a Compiler UndefinedBehaviorSanitizer DataFlowSanitizer LeakSanitizer + RealtimeSanitizer SanitizerCoverage SanitizerStats SanitizerSpecialCaseList From f03d8b6c2f471133a71a9c539bfae957ef43a3b9 Mon Sep 17 00:00:00 2001 From: Chris Apple Date: Tue, 13 Aug 2024 05:39:38 -0700 Subject: [PATCH 4/6] [PR] Documentation feedback from David and MaskRay --- clang/docs/RealtimeSanitizer.rst | 66 ++++++++++++++++---------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/clang/docs/RealtimeSanitizer.rst b/clang/docs/RealtimeSanitizer.rst index abc34dde2c4129..799cd43509c6e6 100644 --- a/clang/docs/RealtimeSanitizer.rst +++ b/clang/docs/RealtimeSanitizer.rst @@ -7,29 +7,16 @@ RealtimeSanitizer Introduction ============ -RealtimeSanitizer (a.k.a. RTSan) is a real-time safety testing tool for C and -C++ projects. RTSan can be used to detect real-time violations,such as calls to -methods that are not safe for use in functions with deterministic runtime -requirements. +RealtimeSanitizer (a.k.a. RTSan) is a real-time safety testing tool for C and C++ +projects. RTSan can be used to detect real-time violations, i.e. calls to methods +that are not safe for use in functions with deterministic runtime requirements. +RTSan considers any function marked with the ``[[clang::nonblocking]]`` attribute +to be a real-time function. If RTSan detects a call to ``malloc``, ``free``, +``pthread_mutex_lock``, or anything else that could have a non-deterministic +execution time in a function marked ``[[clang::nonblocking]]`` +RTSan raises an error. -The tool can detect the following types of real-time violations: - -* System calls -* Allocations -* Exceptions - -These checks are put in place when compiling with the -``-fsanitize=realtime`` flag, for functions marked with -``[[clang::nonblocking]]``. - -.. code-block:: c - - void process_audio(float* buffer) [[clang::nonblocking]] { - ... - } - -The runtime slowdown introduced by RealtimeSanitizer is trivial. Code in -real-time contexts without real-time safety violations have no slowdown. +The runtime slowdown introduced by RealtimeSanitizer is negligible. How to build ============ @@ -59,13 +46,19 @@ code. .. code-block:: console % cat example_realtime_violation.cpp - int main() [[clang::nonblocking]] { - int* p = new int; - return 0; + #include + + void violation() [[clang::nonblocking]]{ + std::vector v; + v.resize(100); } + int main() { + violation(); + return 0; + } # Compile and link - % clang -fsanitize=realtime -g example_realtime_violation.cpp + % clang++ -fsanitize=realtime -g example_realtime_violation.cpp If a real-time safety violation is detected in a ``[[clang::nonblocking]]`` context, or any function invoked by that function, the program will exit with a @@ -73,13 +66,20 @@ non-zero exit code. .. code-block:: console - % clang -fsanitize=realtime -g example_realtime_violation.cpp + % clang++ -fsanitize=realtime -g example_realtime_violation.cpp % ./a.out Real-time violation: intercepted call to real-time unsafe function `malloc` in real-time context! Stack trace: - #0 0x00010065ad9c in __rtsan::PrintStackTrace() rtsan_stack.cpp:45 - #1 0x00010065abcc in __rtsan::Context::ExpectNotRealtime(char const*) rtsan_context.cpp:78 - #2 0x00010065b8d0 in malloc rtsan_interceptors.cpp:289 + #0 0x000102893034 in __rtsan::PrintStackTrace() rtsan_stack.cpp:45 + #1 0x000102892e64 in __rtsan::Context::ExpectNotRealtime(char const*) rtsan_context.cpp:78 + #2 0x00010289397c in malloc rtsan_interceptors.cpp:286 #3 0x000195bd7bd0 in operator new(unsigned long)+0x1c (libc++abi.dylib:arm64+0x16bd0) - #4 0xb338001000dbf68 () - #5 0x0001958960dc () - #6 0x45737ffffffffffc () + #4 0x5c7f00010230f07c () + #5 0x00010230f058 in std::__1::__libcpp_allocate[abi:ue170006](unsigned long, unsigned long) new:324 + #6 0x00010230effc in std::__1::allocator::allocate[abi:ue170006](unsigned long) allocator.h:114 + ... snip ... + #10 0x00010230e4bc in std::__1::vector>::__append(unsigned long) vector:1162 + #11 0x00010230dcdc in std::__1::vector>::resize(unsigned long) vector:1981 + #12 0x00010230dc28 in violation() main.cpp:5 + #13 0x00010230dd64 in main main.cpp:9 + #14 0x0001958960dc () + #15 0x2f557ffffffffffc () From 4a978ed08b3731e1c82cb507477dd81ba6eeab94 Mon Sep 17 00:00:00 2001 From: Chris Apple Date: Sun, 18 Aug 2024 20:11:07 -0700 Subject: [PATCH 5/6] [PR] Maskray - fix braces, change codegen tests to use clang_cc1 --- clang/lib/CodeGen/CodeGenFunction.cpp | 6 ++---- clang/test/CodeGen/rtsan_attribute_inserted.c | 2 +- clang/test/CodeGen/rtsan_no_attribute_sanitizer_disabled.c | 3 +-- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp index 364333e780b6d7..f5f9eaf6277f40 100644 --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -845,14 +845,12 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy, if (SanOpts.has(SanitizerKind::ShadowCallStack)) Fn->addFnAttr(llvm::Attribute::ShadowCallStack); - if (SanOpts.has(SanitizerKind::Realtime)) { - if (FD && FD->getASTContext().hasAnyFunctionEffects()) { + if (SanOpts.has(SanitizerKind::Realtime)) + if (FD && FD->getASTContext().hasAnyFunctionEffects()) for (const FunctionEffectWithCondition &Fe : FD->getFunctionEffects()) { if (Fe.Effect.kind() == FunctionEffect::Kind::NonBlocking) Fn->addFnAttr(llvm::Attribute::SanitizeRealtime); } - } - } // Apply fuzzing attribute to the function. if (SanOpts.hasOneOf(SanitizerKind::Fuzzer | SanitizerKind::FuzzerNoLink)) diff --git a/clang/test/CodeGen/rtsan_attribute_inserted.c b/clang/test/CodeGen/rtsan_attribute_inserted.c index ecba06703fee2c..05a1d9a8c2047a 100644 --- a/clang/test/CodeGen/rtsan_attribute_inserted.c +++ b/clang/test/CodeGen/rtsan_attribute_inserted.c @@ -1,4 +1,4 @@ -// RUN: %clang -target x86_64-unknown-linux -fsanitize=realtime %s -S -emit-llvm -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=realtime %s -emit-llvm -o - %s | FileCheck %s float process(float *a) [[clang::nonblocking]] { return *a; } diff --git a/clang/test/CodeGen/rtsan_no_attribute_sanitizer_disabled.c b/clang/test/CodeGen/rtsan_no_attribute_sanitizer_disabled.c index 85742d8b63d9ec..191cd853e4da98 100644 --- a/clang/test/CodeGen/rtsan_no_attribute_sanitizer_disabled.c +++ b/clang/test/CodeGen/rtsan_no_attribute_sanitizer_disabled.c @@ -1,5 +1,4 @@ -// RUN: %clang -target x86_64-unknown-linux %s -S -emit-llvm -o - | FileCheck %s - +// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -emit-llvm -o - %s | FileCheck %s float process(float *a) [[clang::nonblocking]] { return *a; } From e85d100dcd4fae646ba6b6b11c0a63b7e71fcafa Mon Sep 17 00:00:00 2001 From: Chris Apple Date: Mon, 19 Aug 2024 11:26:34 -0700 Subject: [PATCH 6/6] [PR] Maskray - fix grammar, merge entry and exit test, change to aarch-unknown, ditch unnecessary driver test --- clang/test/CodeGen/rtsan_entry_exit_insertion.c | 13 +++++++++++++ clang/test/CodeGen/rtsan_insert_at_entry.c | 9 --------- clang/test/CodeGen/rtsan_insert_at_exit.c | 9 --------- .../rtsan_no_attribute_sanitizer_disabled.c | 5 ++--- clang/test/Driver/rtsan.c | 14 -------------- 5 files changed, 15 insertions(+), 35 deletions(-) create mode 100644 clang/test/CodeGen/rtsan_entry_exit_insertion.c delete mode 100644 clang/test/CodeGen/rtsan_insert_at_entry.c delete mode 100644 clang/test/CodeGen/rtsan_insert_at_exit.c delete mode 100644 clang/test/Driver/rtsan.c diff --git a/clang/test/CodeGen/rtsan_entry_exit_insertion.c b/clang/test/CodeGen/rtsan_entry_exit_insertion.c new file mode 100644 index 00000000000000..9ba0103ca1e353 --- /dev/null +++ b/clang/test/CodeGen/rtsan_entry_exit_insertion.c @@ -0,0 +1,13 @@ +// RUN: %clang_cc1 -triple aarch64-unknown-linux-gnu -fsanitize=realtime -emit-llvm -o - %s | FileCheck %s + +int foo(int *a) [[clang::nonblocking]] { return *a; } + +// The first instruction after the function is entred should be a call to +// enable the realtime sanitizer stack. +// CHECK-LABEL: define{{.*}}@foo +// CHECK-NEXT: entry: +// CHECK-NEXT: call{{.*}}__rtsan_realtime_enter + +// __rtsan_realtime_exit should be inserted at all function returns. +// CHECK-LABEL: call{{.*}}__rtsan_realtime_exit +// CHECK-NEXT: ret diff --git a/clang/test/CodeGen/rtsan_insert_at_entry.c b/clang/test/CodeGen/rtsan_insert_at_entry.c deleted file mode 100644 index 22d568425cbe74..00000000000000 --- a/clang/test/CodeGen/rtsan_insert_at_entry.c +++ /dev/null @@ -1,9 +0,0 @@ -// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -fsanitize=realtime -emit-llvm -o - %s | FileCheck %s - -// The first instruction after the function is entred should be a call to -// enable the realtime sanitizer stack - -int foo(int *a) [[clang::nonblocking]] { return *a; } -// CHECK-LABEL: define{{.*}}@foo -// CHECK-NEXT: entry: -// CHECK-NEXT: call{{.*}}__rtsan_realtime_enter diff --git a/clang/test/CodeGen/rtsan_insert_at_exit.c b/clang/test/CodeGen/rtsan_insert_at_exit.c deleted file mode 100644 index 32b381f163f595..00000000000000 --- a/clang/test/CodeGen/rtsan_insert_at_exit.c +++ /dev/null @@ -1,9 +0,0 @@ -// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -fsanitize=realtime -emit-llvm -o - %s | FileCheck %s - -// __rtsan_realtime_exit should be inserted at all function returns - -int bar(int* x) [[clang::nonblocking]] { - return *x; -} -// CHECK-LABEL: call{{.*}}__rtsan_realtime_exit -// CHECK-NEXT: ret diff --git a/clang/test/CodeGen/rtsan_no_attribute_sanitizer_disabled.c b/clang/test/CodeGen/rtsan_no_attribute_sanitizer_disabled.c index 191cd853e4da98..43ad6ed1a429ee 100644 --- a/clang/test/CodeGen/rtsan_no_attribute_sanitizer_disabled.c +++ b/clang/test/CodeGen/rtsan_no_attribute_sanitizer_disabled.c @@ -1,7 +1,6 @@ -// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple aarch64-unknown-linux-gnu -emit-llvm -o - %s | FileCheck %s float process(float *a) [[clang::nonblocking]] { return *a; } -// Without the -fsanitize=realtime flag, we shouldn't attach -// the attribute +// Without the -fsanitize=realtime flag, we shouldn't attach the attribute. // CHECK-NOT: {{.*sanitize_realtime.*}} diff --git a/clang/test/Driver/rtsan.c b/clang/test/Driver/rtsan.c deleted file mode 100644 index 999e88d066427c..00000000000000 --- a/clang/test/Driver/rtsan.c +++ /dev/null @@ -1,14 +0,0 @@ -// RUN: %clang -target x86_64-unknown-linux -fsanitize=realtime %s -S -emit-llvm -o - | FileCheck %s -// RUN: %clang -O1 -target x86_64-unknown-linux -fsanitize=realtime %s -S -emit-llvm -o - | FileCheck %s -// RUN: %clang -O2 -target x86_64-unknown-linux -fsanitize=realtime %s -S -emit-llvm -o - | FileCheck %s -// RUN: %clang -O3 -target x86_64-unknown-linux -fsanitize=realtime %s -S -emit-llvm -o - | FileCheck %s -// RUN: %clang -target x86_64-unknown-linux -fsanitize=realtime %s -S -emit-llvm -flto=thin -o - | FileCheck %s -// RUN: %clang -O2 -target x86_64-unknown-linux -fsanitize=realtime %s -S -emit-llvm -flto=thin -o - | FileCheck %s -// RUN: %clang -target x86_64-unknown-linux -fsanitize=realtime %s -S -emit-llvm -flto -o - | FileCheck %s -// RUN: %clang -O2 -target x86_64-unknown-linux -fsanitize=realtime %s -S -emit-llvm -flto -o - | FileCheck %s - -// Ensure the rtsan_realtime calls are never optimized away - -int foo(int *a) [[clang::nonblocking]] { return *a; } -// CHECK: __rtsan_realtime_enter -// CHECK: __rtsan_realtime_exit