[clang] [clang][rtsan] Introduce realtime sanitizer codegen and driver (PR #102622)
Chris Apple via cfe-commits
cfe-commits at lists.llvm.org
Mon Aug 19 11:28:00 PDT 2024
https://github.com/cjappl updated https://github.com/llvm/llvm-project/pull/102622
>From 9d3d49fa755c28b21c3b4771faae65cf418dec5a Mon Sep 17 00:00:00 2001
From: Chris Apple <cja-private at pm.me>
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 <cja-private at pm.me>
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 <cja-private at pm.me>
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 <https://llvm.org/docs/CMake.html>` 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" <path to source>/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 (<unknown module>)
+ #5 0x0001958960dc (<unknown module>)
+ #6 0x45737ffffffffffc (<unknown module>)
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 <SafeStack>`
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 <ubsan-checks>` 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 <cja-private at pm.me>
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 <vector>
+
+ void violation() [[clang::nonblocking]]{
+ std::vector<float> 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 (<unknown module>)
- #5 0x0001958960dc (<unknown module>)
- #6 0x45737ffffffffffc (<unknown module>)
+ #4 0x5c7f00010230f07c (<unknown module>)
+ #5 0x00010230f058 in std::__1::__libcpp_allocate[abi:ue170006](unsigned long, unsigned long) new:324
+ #6 0x00010230effc in std::__1::allocator<float>::allocate[abi:ue170006](unsigned long) allocator.h:114
+ ... snip ...
+ #10 0x00010230e4bc in std::__1::vector<float, std::__1::allocator<float>>::__append(unsigned long) vector:1162
+ #11 0x00010230dcdc in std::__1::vector<float, std::__1::allocator<float>>::resize(unsigned long) vector:1981
+ #12 0x00010230dc28 in violation() main.cpp:5
+ #13 0x00010230dd64 in main main.cpp:9
+ #14 0x0001958960dc (<unknown module>)
+ #15 0x2f557ffffffffffc (<unknown module>)
>From 4a978ed08b3731e1c82cb507477dd81ba6eeab94 Mon Sep 17 00:00:00 2001
From: Chris Apple <cja-private at pm.me>
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 <cja-private at pm.me>
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
More information about the cfe-commits
mailing list