From 78df081a4bfc249a25e1256463649278b877f23b Mon Sep 17 00:00:00 2001 From: Chris Apple Date: Fri, 23 Aug 2024 08:16:52 -0700 Subject: [PATCH] [clang][rtsan] Reland realtime sanitizer codegen and driver (#102622) This reverts commit a1e9b7e646b76bf844e8a9a101ebd27de11992ff This relands commit d010ec6af8162a8ae4e42d2cac5282f83db0ce07 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 --- clang/docs/RealtimeSanitizer.rst | 85 +++++++++++++++++++ clang/docs/ReleaseNotes.rst | 5 ++ clang/docs/UsersManual.rst | 2 + clang/docs/index.rst | 1 + 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 | 7 ++ 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 ++ .../test/CodeGen/rtsan_entry_exit_insertion.c | 13 +++ .../rtsan_no_attribute_sanitizer_disabled.c | 6 ++ clang/test/Driver/fsanitize.c | 46 ++++++++++ 16 files changed, 208 insertions(+), 5 deletions(-) create mode 100644 clang/docs/RealtimeSanitizer.rst create mode 100644 clang/test/CodeGen/rtsan_attribute_inserted.c create mode 100644 clang/test/CodeGen/rtsan_entry_exit_insertion.c create mode 100644 clang/test/CodeGen/rtsan_no_attribute_sanitizer_disabled.c diff --git a/clang/docs/RealtimeSanitizer.rst b/clang/docs/RealtimeSanitizer.rst new file mode 100644 index 000000000000000..799cd43509c6e62 --- /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, 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 ` 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 + #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 + +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 () + #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 () diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 70ff5dedab217f4..fb3c2f699964c3f 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -444,6 +444,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 diff --git a/clang/docs/UsersManual.rst b/clang/docs/UsersManual.rst index d19b77ae40b0d70..069ecba875cd59b 100644 --- a/clang/docs/UsersManual.rst +++ b/clang/docs/UsersManual.rst @@ -2068,6 +2068,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 9bae0bd83243bda..4a497f4d9bcc3c3 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 diff --git a/clang/include/clang/Basic/Sanitizers.def b/clang/include/clang/Basic/Sanitizers.def index bee35e9dca7c39c..9223f62b3639a76 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 e64ec463ca89070..0c6f3869549ef74 100644 --- a/clang/include/clang/Driver/SanitizerArgs.h +++ b/clang/include/clang/Driver/SanitizerArgs.h @@ -107,6 +107,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 fdd89edd72e1099..026f16484c09498 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" @@ -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) { diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp index eff8c9f56940849..c89eaa0f4e3bfcf 100644 --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -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); diff --git a/clang/lib/Driver/SanitizerArgs.cpp b/clang/lib/Driver/SanitizerArgs.cpp index 9d9ad79d51d7f88..09262f40b5b50cb 100644 --- a/clang/lib/Driver/SanitizerArgs.cpp +++ b/clang/lib/Driver/SanitizerArgs.cpp @@ -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; diff --git a/clang/lib/Driver/ToolChains/CommonArgs.cpp b/clang/lib/Driver/ToolChains/CommonArgs.cpp index 0738ed18f540782..0601016c3b14b8c 100644 --- a/clang/lib/Driver/ToolChains/CommonArgs.cpp +++ b/clang/lib/Driver/ToolChains/CommonArgs.cpp @@ -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()) diff --git a/clang/lib/Driver/ToolChains/Darwin.cpp b/clang/lib/Driver/ToolChains/Darwin.cpp index 2550541a4384818..5e7f9290e2009d8 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()) { @@ -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; diff --git a/clang/lib/Driver/ToolChains/Linux.cpp b/clang/lib/Driver/ToolChains/Linux.cpp index 2265138edbffbeb..96680b3412a2db7 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 000000000000000..05a1d9a8c2047a0 --- /dev/null +++ b/clang/test/CodeGen/rtsan_attribute_inserted.c @@ -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.*}} 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 000000000000000..9ba0103ca1e3537 --- /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_no_attribute_sanitizer_disabled.c b/clang/test/CodeGen/rtsan_no_attribute_sanitizer_disabled.c new file mode 100644 index 000000000000000..43ad6ed1a429ee7 --- /dev/null +++ b/clang/test/CodeGen/rtsan_no_attribute_sanitizer_disabled.c @@ -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.*}} diff --git a/clang/test/Driver/fsanitize.c b/clang/test/Driver/fsanitize.c index 678fa432fb0a0a6..f86c978f221cd49 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'