Skip to content

Commit f77e8f7

Browse files
authored
[clang][rtsan] Reland realtime sanitizer codegen and driver (#102622)
This reverts commit a1e9b7e This relands commit d010ec6 No modifications from the original patch. It was determined that the ubsan build failure was happening even after the revert, some examples: https://lab.llvm.org/buildbot/#/builders/159/builds/4477 https://lab.llvm.org/buildbot/#/builders/159/builds/4478 https://lab.llvm.org/buildbot/#/builders/159/builds/4479
1 parent 6a8f738 commit f77e8f7

16 files changed

+208
-5
lines changed

clang/docs/RealtimeSanitizer.rst

+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
=================
2+
RealtimeSanitizer
3+
=================
4+
5+
.. contents::
6+
:local:
7+
8+
Introduction
9+
============
10+
RealtimeSanitizer (a.k.a. RTSan) is a real-time safety testing tool for C and C++
11+
projects. RTSan can be used to detect real-time violations, i.e. calls to methods
12+
that are not safe for use in functions with deterministic runtime requirements.
13+
RTSan considers any function marked with the ``[[clang::nonblocking]]`` attribute
14+
to be a real-time function. If RTSan detects a call to ``malloc``, ``free``,
15+
``pthread_mutex_lock``, or anything else that could have a non-deterministic
16+
execution time in a function marked ``[[clang::nonblocking]]``
17+
RTSan raises an error.
18+
19+
The runtime slowdown introduced by RealtimeSanitizer is negligible.
20+
21+
How to build
22+
============
23+
24+
Build LLVM/Clang with `CMake <https://llvm.org/docs/CMake.html>` and enable the
25+
``compiler-rt`` runtime. An example CMake configuration that will allow for the
26+
use/testing of RealtimeSanitizer:
27+
28+
.. code-block:: console
29+
30+
$ cmake -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS="clang" -DLLVM_ENABLE_RUNTIMES="compiler-rt" <path to source>/llvm
31+
32+
Usage
33+
=====
34+
35+
There are two requirements:
36+
37+
1. The code must be compiled with the ``-fsanitize=realtime`` flag.
38+
2. Functions that are subject to real-time constraints must be marked
39+
with the ``[[clang::nonblocking]]`` attribute.
40+
41+
Typically, these attributes should be added onto the functions that are entry
42+
points for threads with real-time priority. These threads are subject to a fixed
43+
callback time, such as audio callback threads or rendering loops in video game
44+
code.
45+
46+
.. code-block:: console
47+
48+
% cat example_realtime_violation.cpp
49+
#include <vector>
50+
51+
void violation() [[clang::nonblocking]]{
52+
std::vector<float> v;
53+
v.resize(100);
54+
}
55+
56+
int main() {
57+
violation();
58+
return 0;
59+
}
60+
# Compile and link
61+
% clang++ -fsanitize=realtime -g example_realtime_violation.cpp
62+
63+
If a real-time safety violation is detected in a ``[[clang::nonblocking]]``
64+
context, or any function invoked by that function, the program will exit with a
65+
non-zero exit code.
66+
67+
.. code-block:: console
68+
69+
% clang++ -fsanitize=realtime -g example_realtime_violation.cpp
70+
% ./a.out
71+
Real-time violation: intercepted call to real-time unsafe function `malloc` in real-time context! Stack trace:
72+
#0 0x000102893034 in __rtsan::PrintStackTrace() rtsan_stack.cpp:45
73+
#1 0x000102892e64 in __rtsan::Context::ExpectNotRealtime(char const*) rtsan_context.cpp:78
74+
#2 0x00010289397c in malloc rtsan_interceptors.cpp:286
75+
#3 0x000195bd7bd0 in operator new(unsigned long)+0x1c (libc++abi.dylib:arm64+0x16bd0)
76+
#4 0x5c7f00010230f07c (<unknown module>)
77+
#5 0x00010230f058 in std::__1::__libcpp_allocate[abi:ue170006](unsigned long, unsigned long) new:324
78+
#6 0x00010230effc in std::__1::allocator<float>::allocate[abi:ue170006](unsigned long) allocator.h:114
79+
... snip ...
80+
#10 0x00010230e4bc in std::__1::vector<float, std::__1::allocator<float>>::__append(unsigned long) vector:1162
81+
#11 0x00010230dcdc in std::__1::vector<float, std::__1::allocator<float>>::resize(unsigned long) vector:1981
82+
#12 0x00010230dc28 in violation() main.cpp:5
83+
#13 0x00010230dd64 in main main.cpp:9
84+
#14 0x0001958960dc (<unknown module>)
85+
#15 0x2f557ffffffffffc (<unknown module>)

clang/docs/ReleaseNotes.rst

+5
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,11 @@ Moved checkers
444444

445445
Sanitizers
446446
----------
447+
- Introduced Realtime Sanitizer, activated by using the -fsanitize=realtime
448+
flag. This sanitizer detects unsafe system library calls, such as memory
449+
allocations and mutex locks. If any such function is called during invocation
450+
of a function marked with the ``[[clang::nonblocking]]`` attribute, an error
451+
is printed to the console and the process exits non-zero.
447452

448453
- Added the ``-fsanitize-undefined-ignore-overflow-pattern`` flag which can be
449454
used to disable specific overflow-dependent code patterns. The supported

clang/docs/UsersManual.rst

+2
Original file line numberDiff line numberDiff line change
@@ -2068,6 +2068,8 @@ are listed below.
20682068
integrity.
20692069
- ``-fsanitize=safe-stack``: :doc:`safe stack <SafeStack>`
20702070
protection against stack-based memory corruption errors.
2071+
- ``-fsanitize=realtime``: :doc:`RealtimeSanitizer`,
2072+
a real-time safety checker.
20712073

20722074
There are more fine-grained checks available: see
20732075
the :ref:`list <ubsan-checks>` of specific kinds of

clang/docs/index.rst

+1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ Using Clang as a Compiler
3232
UndefinedBehaviorSanitizer
3333
DataFlowSanitizer
3434
LeakSanitizer
35+
RealtimeSanitizer
3536
SanitizerCoverage
3637
SanitizerStats
3738
SanitizerSpecialCaseList

clang/include/clang/Basic/Sanitizers.def

+3
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@ SANITIZER("thread", Thread)
7979
// Numerical stability sanitizer.
8080
SANITIZER("numerical", NumericalStability)
8181

82+
// RealtimeSanitizer
83+
SANITIZER("realtime", Realtime)
84+
8285
// LeakSanitizer
8386
SANITIZER("leak", Leak)
8487

clang/include/clang/Driver/SanitizerArgs.h

+1
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ class SanitizerArgs {
107107
bool needsNsanRt() const {
108108
return Sanitizers.has(SanitizerKind::NumericalStability);
109109
}
110+
bool needsRtsanRt() const { return Sanitizers.has(SanitizerKind::Realtime); }
110111

111112
bool hasMemTag() const {
112113
return hasMemtagHeap() || hasMemtagStack() || hasMemtagGlobals();

clang/lib/CodeGen/BackendUtil.cpp

+8
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@
7878
#include "llvm/Transforms/Instrumentation/MemorySanitizer.h"
7979
#include "llvm/Transforms/Instrumentation/NumericalStabilitySanitizer.h"
8080
#include "llvm/Transforms/Instrumentation/PGOInstrumentation.h"
81+
#include "llvm/Transforms/Instrumentation/RealtimeSanitizer.h"
8182
#include "llvm/Transforms/Instrumentation/SanitizerBinaryMetadata.h"
8283
#include "llvm/Transforms/Instrumentation/SanitizerCoverage.h"
8384
#include "llvm/Transforms/Instrumentation/ThreadSanitizer.h"
@@ -990,6 +991,13 @@ void EmitAssemblyHelper::RunOptimizationPipeline(
990991
FPM.addPass(BoundsCheckingPass());
991992
});
992993

994+
if (LangOpts.Sanitize.has(SanitizerKind::Realtime))
995+
PB.registerScalarOptimizerLateEPCallback(
996+
[](FunctionPassManager &FPM, OptimizationLevel Level) {
997+
RealtimeSanitizerOptions Opts;
998+
FPM.addPass(RealtimeSanitizerPass(Opts));
999+
});
1000+
9931001
// Don't add sanitizers if we are here from ThinLTO PostLink. That already
9941002
// done on PreLink stage.
9951003
if (!IsThinLTOPostLink) {

clang/lib/CodeGen/CodeGenFunction.cpp

+7
Original file line numberDiff line numberDiff line change
@@ -845,6 +845,13 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy,
845845
if (SanOpts.has(SanitizerKind::ShadowCallStack))
846846
Fn->addFnAttr(llvm::Attribute::ShadowCallStack);
847847

848+
if (SanOpts.has(SanitizerKind::Realtime))
849+
if (FD && FD->getASTContext().hasAnyFunctionEffects())
850+
for (const FunctionEffectWithCondition &Fe : FD->getFunctionEffects()) {
851+
if (Fe.Effect.kind() == FunctionEffect::Kind::NonBlocking)
852+
Fn->addFnAttr(llvm::Attribute::SanitizeRealtime);
853+
}
854+
848855
// Apply fuzzing attribute to the function.
849856
if (SanOpts.hasOneOf(SanitizerKind::Fuzzer | SanitizerKind::FuzzerNoLink))
850857
Fn->addFnAttr(llvm::Attribute::OptForFuzzing);

clang/lib/Driver/SanitizerArgs.cpp

+9-5
Original file line numberDiff line numberDiff line change
@@ -558,11 +558,15 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
558558
SanitizerKind::Leak | SanitizerKind::Thread |
559559
SanitizerKind::Memory | SanitizerKind::KernelAddress |
560560
SanitizerKind::Scudo | SanitizerKind::SafeStack),
561-
std::make_pair(SanitizerKind::MemTag,
562-
SanitizerKind::Address | SanitizerKind::KernelAddress |
563-
SanitizerKind::HWAddress |
564-
SanitizerKind::KernelHWAddress),
565-
std::make_pair(SanitizerKind::KCFI, SanitizerKind::Function)};
561+
std::make_pair(SanitizerKind::MemTag, SanitizerKind::Address |
562+
SanitizerKind::KernelAddress |
563+
SanitizerKind::HWAddress |
564+
SanitizerKind::KernelHWAddress),
565+
std::make_pair(SanitizerKind::KCFI, SanitizerKind::Function),
566+
std::make_pair(SanitizerKind::Realtime,
567+
SanitizerKind::Address | SanitizerKind::Thread |
568+
SanitizerKind::Undefined | SanitizerKind::Memory)};
569+
566570
// Enable toolchain specific default sanitizers if not explicitly disabled.
567571
SanitizerMask Default = TC.getDefaultSanitizers() & ~AllRemove;
568572

clang/lib/Driver/ToolChains/CommonArgs.cpp

+6
Original file line numberDiff line numberDiff line change
@@ -1456,6 +1456,8 @@ collectSanitizerRuntimes(const ToolChain &TC, const ArgList &Args,
14561456
if (!Args.hasArg(options::OPT_shared))
14571457
HelperStaticRuntimes.push_back("hwasan-preinit");
14581458
}
1459+
if (SanArgs.needsRtsanRt() && SanArgs.linkRuntimes())
1460+
SharedRuntimes.push_back("rtsan");
14591461
}
14601462

14611463
// The stats_client library is also statically linked into DSOs.
@@ -1481,6 +1483,10 @@ collectSanitizerRuntimes(const ToolChain &TC, const ArgList &Args,
14811483
StaticRuntimes.push_back("asan_cxx");
14821484
}
14831485

1486+
if (!SanArgs.needsSharedRt() && SanArgs.needsRtsanRt() &&
1487+
SanArgs.linkRuntimes())
1488+
StaticRuntimes.push_back("rtsan");
1489+
14841490
if (!SanArgs.needsSharedRt() && SanArgs.needsMemProfRt()) {
14851491
StaticRuntimes.push_back("memprof");
14861492
if (SanArgs.linkCXXRuntimes())

clang/lib/Driver/ToolChains/Darwin.cpp

+8
Original file line numberDiff line numberDiff line change
@@ -1519,6 +1519,8 @@ void DarwinClang::AddLinkRuntimeLibArgs(const ArgList &Args,
15191519
const char *sanitizer = nullptr;
15201520
if (Sanitize.needsUbsanRt()) {
15211521
sanitizer = "UndefinedBehaviorSanitizer";
1522+
} else if (Sanitize.needsRtsanRt()) {
1523+
sanitizer = "RealtimeSanitizer";
15221524
} else if (Sanitize.needsAsanRt()) {
15231525
sanitizer = "AddressSanitizer";
15241526
} else if (Sanitize.needsTsanRt()) {
@@ -1541,6 +1543,11 @@ void DarwinClang::AddLinkRuntimeLibArgs(const ArgList &Args,
15411543
AddLinkSanitizerLibArgs(Args, CmdArgs, "asan");
15421544
}
15431545
}
1546+
if (Sanitize.needsRtsanRt()) {
1547+
assert(Sanitize.needsSharedRt() &&
1548+
"Static sanitizer runtimes not supported");
1549+
AddLinkSanitizerLibArgs(Args, CmdArgs, "rtsan");
1550+
}
15441551
if (Sanitize.needsLsanRt())
15451552
AddLinkSanitizerLibArgs(Args, CmdArgs, "lsan");
15461553
if (Sanitize.needsUbsanRt()) {
@@ -3539,6 +3546,7 @@ SanitizerMask Darwin::getSupportedSanitizers() const {
35393546
Res |= SanitizerKind::Address;
35403547
Res |= SanitizerKind::PointerCompare;
35413548
Res |= SanitizerKind::PointerSubtract;
3549+
Res |= SanitizerKind::Realtime;
35423550
Res |= SanitizerKind::Leak;
35433551
Res |= SanitizerKind::Fuzzer;
35443552
Res |= SanitizerKind::FuzzerNoLink;

clang/lib/Driver/ToolChains/Linux.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -800,6 +800,7 @@ SanitizerMask Linux::getSupportedSanitizers() const {
800800
Res |= SanitizerKind::Address;
801801
Res |= SanitizerKind::PointerCompare;
802802
Res |= SanitizerKind::PointerSubtract;
803+
Res |= SanitizerKind::Realtime;
803804
Res |= SanitizerKind::Fuzzer;
804805
Res |= SanitizerKind::FuzzerNoLink;
805806
Res |= SanitizerKind::KernelAddress;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=realtime %s -emit-llvm -o - %s | FileCheck %s
2+
3+
float process(float *a) [[clang::nonblocking]] { return *a; }
4+
5+
// CHECK-LABEL: @process{{.*}}#0 {
6+
// CHECK: attributes #0 = {
7+
// CHECK-SAME: {{.*sanitize_realtime.*}}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// RUN: %clang_cc1 -triple aarch64-unknown-linux-gnu -fsanitize=realtime -emit-llvm -o - %s | FileCheck %s
2+
3+
int foo(int *a) [[clang::nonblocking]] { return *a; }
4+
5+
// The first instruction after the function is entred should be a call to
6+
// enable the realtime sanitizer stack.
7+
// CHECK-LABEL: define{{.*}}@foo
8+
// CHECK-NEXT: entry:
9+
// CHECK-NEXT: call{{.*}}__rtsan_realtime_enter
10+
11+
// __rtsan_realtime_exit should be inserted at all function returns.
12+
// CHECK-LABEL: call{{.*}}__rtsan_realtime_exit
13+
// CHECK-NEXT: ret
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// RUN: %clang_cc1 -triple aarch64-unknown-linux-gnu -emit-llvm -o - %s | FileCheck %s
2+
3+
float process(float *a) [[clang::nonblocking]] { return *a; }
4+
5+
// Without the -fsanitize=realtime flag, we shouldn't attach the attribute.
6+
// CHECK-NOT: {{.*sanitize_realtime.*}}

clang/test/Driver/fsanitize.c

+46
Original file line numberDiff line numberDiff line change
@@ -1040,3 +1040,49 @@
10401040
// RUN: not %clang --target=aarch64-none-elf -fsanitize=dataflow %s -### 2>&1 | FileCheck %s -check-prefix=UNSUPPORTED-BAREMETAL
10411041
// RUN: not %clang --target=arm-arm-none-eabi -fsanitize=shadow-call-stack %s -### 2>&1 | FileCheck %s -check-prefix=UNSUPPORTED-BAREMETAL
10421042
// UNSUPPORTED-BAREMETAL: unsupported option '-fsanitize={{.*}}' for target
1043+
1044+
// RUN: %clang --target=x86_64-apple-darwin -fsanitize=realtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-RTSAN-X86-64-DARWIN
1045+
// CHECK-RTSAN-X86-64-DARWIN-NOT: unsupported option
1046+
1047+
// RUN: %clang --target=x86_64-apple-darwin -fsanitize=realtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-RTSAN-X86-64-DARWIN
1048+
// CHECK-RTSAN-X86-64-DARWIN-NOT: unsupported option
1049+
// RUN: %clang --target=x86_64-apple-macos -fsanitize=realtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-RTSAN-X86-64-MACOS
1050+
// CHECK-RTSAN-X86-64-MACOS-NOT: unsupported option
1051+
// RUN: %clang --target=arm64-apple-macos -fsanitize=realtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-RTSAN-ARM64-MACOS
1052+
// CHECK-RTSAN-ARM64-MACOS-NOT: unsupported option
1053+
1054+
// RUN: %clang --target=arm64-apple-ios-simulator -fsanitize=realtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-RTSAN-ARM64-IOSSIMULATOR
1055+
// CHECK-RTSAN-ARM64-IOSSIMULATOR-NOT: unsupported option
1056+
1057+
// RUN: %clang --target=arm64-apple-watchos-simulator -fsanitize=realtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-RTSAN-ARM64-WATCHOSSIMULATOR
1058+
// CHECK-RTSAN-ARM64-WATCHOSSIMULATOR-NOT: unsupported option
1059+
1060+
// RUN: %clang --target=arm64-apple-tvos-simulator -fsanitize=realtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-RTSAN-ARM64-TVOSSIMULATOR
1061+
// CHECK-RTSAN-ARM64-TVOSSIMULATOR-NOT: unsupported option
1062+
1063+
// RUN: %clang --target=x86_64-apple-ios-simulator -fsanitize=realtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-RTSAN-X86-64-IOSSIMULATOR
1064+
// CHECK-RTSAN-X86-64-IOSSIMULATOR-NOT: unsupported option
1065+
1066+
// RUN: %clang --target=x86_64-apple-watchos-simulator -fsanitize=realtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-RTSAN-X86-64-WATCHOSSIMULATOR
1067+
// CHECK-RTSAN-X86-64-WATCHOSSIMULATOR-NOT: unsupported option
1068+
1069+
// RUN: %clang --target=x86_64-apple-tvos-simulator -fsanitize=realtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-RTSAN-X86-64-TVOSSIMULATOR
1070+
// CHECK-RTSAN-X86-64-TVOSSIMULATOR-NOT: unsupported option
1071+
1072+
// RUN: %clang --target=x86_64-linux-gnu -fsanitize=realtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-RTSAN-X86-64-LINUX
1073+
// CHECK-RTSAN-X86-64-LINUX-NOT: unsupported option
1074+
1075+
// RUN: not %clang --target=i386-pc-openbsd -fsanitize=realtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-RTSAN-OPENBSD
1076+
// CHECK-RTSAN-OPENBSD: unsupported option '-fsanitize=realtime' for target 'i386-pc-openbsd'
1077+
1078+
// RUN: not %clang --target=x86_64-linux-gnu -fsanitize=realtime,thread %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-REALTIME-TSAN
1079+
// CHECK-REALTIME-TSAN: error: invalid argument '-fsanitize=realtime' not allowed with '-fsanitize=thread'
1080+
1081+
// RUN: not %clang --target=x86_64-linux-gnu -fsanitize=realtime,address %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-REALTIME-ASAN
1082+
// CHECK-REALTIME-ASAN: error: invalid argument '-fsanitize=realtime' not allowed with '-fsanitize=address'
1083+
1084+
// RUN: not %clang --target=x86_64-linux-gnu -fsanitize=realtime,memory %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-REALTIME-MSAN
1085+
// CHECK-REALTIME-MSAN: error: invalid argument '-fsanitize=realtime' not allowed with '-fsanitize=memory'
1086+
1087+
// RUN: not %clang --target=x86_64-linux-gnu -fsanitize=realtime,undefined %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-REALTIME-UBSAN
1088+
// CHECK-REALTIME-UBSAN: error: invalid argument '-fsanitize=realtime' not allowed with '-fsanitize=undefined'

0 commit comments

Comments
 (0)