mirror of
https://github.com/intel/llvm.git
synced 2026-02-02 18:18:09 +08:00
[clang][rtsan] Introduce realtime sanitizer codegen and driver (#102622)
Introduce the `-fsanitize=realtime` flag in clang driver Plug in the RealtimeSanitizer PassManager pass in Codegen, and attribute a function based on if it has the `[[clang::nonblocking]]` function effect.
This commit is contained in:
85
clang/docs/RealtimeSanitizer.rst
Normal file
85
clang/docs/RealtimeSanitizer.rst
Normal file
@@ -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, 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 runtime slowdown introduced by RealtimeSanitizer is negligible.
|
||||
|
||||
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
|
||||
#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
|
||||
|
||||
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 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 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>)
|
||||
@@ -452,6 +452,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.
|
||||
|
||||
- Added the ``-fsanitize-undefined-ignore-overflow-pattern`` flag which can be
|
||||
used to disable specific overflow-dependent code patterns. The supported
|
||||
|
||||
@@ -2068,6 +2068,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
|
||||
|
||||
@@ -32,6 +32,7 @@ Using Clang as a Compiler
|
||||
UndefinedBehaviorSanitizer
|
||||
DataFlowSanitizer
|
||||
LeakSanitizer
|
||||
RealtimeSanitizer
|
||||
SanitizerCoverage
|
||||
SanitizerStats
|
||||
SanitizerSpecialCaseList
|
||||
|
||||
@@ -79,6 +79,9 @@ SANITIZER("thread", Thread)
|
||||
// Numerical stability sanitizer.
|
||||
SANITIZER("numerical", NumericalStability)
|
||||
|
||||
// RealtimeSanitizer
|
||||
SANITIZER("realtime", Realtime)
|
||||
|
||||
// LeakSanitizer
|
||||
SANITIZER("leak", Leak)
|
||||
|
||||
|
||||
@@ -107,6 +107,7 @@ public:
|
||||
bool needsNsanRt() const {
|
||||
return Sanitizers.has(SanitizerKind::NumericalStability);
|
||||
}
|
||||
bool needsRtsanRt() const { return Sanitizers.has(SanitizerKind::Realtime); }
|
||||
|
||||
bool hasMemTag() const {
|
||||
return hasMemtagHeap() || hasMemtagStack() || hasMemtagGlobals();
|
||||
|
||||
@@ -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"
|
||||
@@ -990,6 +991,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) {
|
||||
|
||||
@@ -845,6 +845,13 @@ 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())
|
||||
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);
|
||||
|
||||
@@ -558,11 +558,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;
|
||||
|
||||
|
||||
@@ -1456,6 +1456,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.
|
||||
@@ -1481,6 +1483,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())
|
||||
|
||||
@@ -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()) {
|
||||
@@ -3539,6 +3546,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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
7
clang/test/CodeGen/rtsan_attribute_inserted.c
Normal file
7
clang/test/CodeGen/rtsan_attribute_inserted.c
Normal file
@@ -0,0 +1,7 @@
|
||||
// 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; }
|
||||
|
||||
// CHECK-LABEL: @process{{.*}}#0 {
|
||||
// CHECK: attributes #0 = {
|
||||
// CHECK-SAME: {{.*sanitize_realtime.*}}
|
||||
13
clang/test/CodeGen/rtsan_entry_exit_insertion.c
Normal file
13
clang/test/CodeGen/rtsan_entry_exit_insertion.c
Normal file
@@ -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
|
||||
@@ -0,0 +1,6 @@
|
||||
// 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.
|
||||
// CHECK-NOT: {{.*sanitize_realtime.*}}
|
||||
@@ -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'
|
||||
|
||||
Reference in New Issue
Block a user