diff --git a/compiler-rt/lib/rtsan/rtsan.cpp b/compiler-rt/lib/rtsan/rtsan.cpp index fe7247ec8e7bb..936f0b5b8cee3 100644 --- a/compiler-rt/lib/rtsan/rtsan.cpp +++ b/compiler-rt/lib/rtsan/rtsan.cpp @@ -16,6 +16,7 @@ #include "sanitizer_common/sanitizer_atomic.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_mutex.h" +#include "sanitizer_common/sanitizer_stacktrace.h" using namespace __rtsan; using namespace __sanitizer; @@ -75,6 +76,20 @@ SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_enable() { SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_notify_intercepted_call(const char *intercepted_function_name) { __rtsan_ensure_initialized(); - ExpectNotRealtime(GetContextForThisThread(), intercepted_function_name); + + GET_CALLER_PC_BP; + DiagnosticsInfo info = {InterceptedCallInfo{intercepted_function_name}, pc, + bp}; + ExpectNotRealtime(GetContextForThisThread(), info); } + +SANITIZER_INTERFACE_ATTRIBUTE void +__rtsan_notify_blocking_call(const char *blocking_function_name) { + __rtsan_ensure_initialized(); + + GET_CALLER_PC_BP; + DiagnosticsInfo info = {BlockingCallInfo{blocking_function_name}, pc, bp}; + ExpectNotRealtime(GetContextForThisThread(), info); +} + } // extern "C" diff --git a/compiler-rt/lib/rtsan/rtsan.h b/compiler-rt/lib/rtsan/rtsan.h index b690f734e1032..37628ae2731f6 100644 --- a/compiler-rt/lib/rtsan/rtsan.h +++ b/compiler-rt/lib/rtsan/rtsan.h @@ -45,4 +45,6 @@ SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_enable(); SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_notify_intercepted_call(const char *intercepted_function_name); +SANITIZER_INTERFACE_ATTRIBUTE void +__rtsan_notify_blocking_call(const char *blocking_function_name); } // extern "C" diff --git a/compiler-rt/lib/rtsan/rtsan_assertions.cpp b/compiler-rt/lib/rtsan/rtsan_assertions.cpp index ef996c92dc1e8..4aae85de5c52f 100644 --- a/compiler-rt/lib/rtsan/rtsan_assertions.cpp +++ b/compiler-rt/lib/rtsan/rtsan_assertions.cpp @@ -14,18 +14,14 @@ #include "rtsan/rtsan.h" #include "rtsan/rtsan_diagnostics.h" -#include "sanitizer_common/sanitizer_stacktrace.h" - using namespace __sanitizer; -void __rtsan::ExpectNotRealtime(Context &context, - const char *intercepted_function_name) { +void __rtsan::ExpectNotRealtime(Context &context, const DiagnosticsInfo &info) { CHECK(__rtsan_is_initialized()); if (context.InRealtimeContext() && !context.IsBypassed()) { context.BypassPush(); - GET_CALLER_PC_BP; - PrintDiagnostics(intercepted_function_name, pc, bp); + PrintDiagnostics(info); Die(); context.BypassPop(); } diff --git a/compiler-rt/lib/rtsan/rtsan_assertions.h b/compiler-rt/lib/rtsan/rtsan_assertions.h index bc38a0f116eec..bc1235363669d 100644 --- a/compiler-rt/lib/rtsan/rtsan_assertions.h +++ b/compiler-rt/lib/rtsan/rtsan_assertions.h @@ -13,7 +13,9 @@ #pragma once #include "rtsan/rtsan_context.h" +#include "rtsan/rtsan_diagnostics.h" namespace __rtsan { -void ExpectNotRealtime(Context &context, const char *intercepted_function_name); + +void ExpectNotRealtime(Context &context, const DiagnosticsInfo &info); } // namespace __rtsan diff --git a/compiler-rt/lib/rtsan/rtsan_diagnostics.cpp b/compiler-rt/lib/rtsan/rtsan_diagnostics.cpp index d4c656606f365..ac13b0743be06 100644 --- a/compiler-rt/lib/rtsan/rtsan_diagnostics.cpp +++ b/compiler-rt/lib/rtsan/rtsan_diagnostics.cpp @@ -34,9 +34,15 @@ namespace { class Decorator : public __sanitizer::SanitizerCommonDecorator { public: Decorator() : SanitizerCommonDecorator() {} - const char *FunctionName() { return Green(); } - const char *Reason() { return Blue(); } + const char *FunctionName() const { return Green(); } + const char *Reason() const { return Blue(); } }; + +template struct Overloaded : Ts... { + using Ts::operator()...; +}; +// TODO: Remove below when c++20 +template Overloaded(Ts...) -> Overloaded; } // namespace static void PrintStackTrace(uptr pc, uptr bp) { @@ -46,18 +52,46 @@ static void PrintStackTrace(uptr pc, uptr bp) { stack.Print(); } -void __rtsan::PrintDiagnostics(const char *intercepted_function_name, uptr pc, - uptr bp) { +static void PrintError(const Decorator &decorator, + const DiagnosticsCallerInfo &info) { + const char *violation_type = std::visit( + Overloaded{ + [](const InterceptedCallInfo &) { return "unsafe-library-call"; }, + [](const BlockingCallInfo &) { return "blocking-call"; }}, + info); + + Printf("%s", decorator.Error()); + Report("ERROR: RealtimeSanitizer: %s\n", violation_type); +} + +static void PrintReason(const Decorator &decorator, + const DiagnosticsCallerInfo &info) { + Printf("%s", decorator.Reason()); + + std::visit( + Overloaded{[decorator](const InterceptedCallInfo &call) { + Printf("Intercepted call to real-time unsafe function " + "`%s%s%s` in real-time context!", + decorator.FunctionName(), + call.intercepted_function_name_, decorator.Reason()); + }, + [decorator](const BlockingCallInfo &arg) { + Printf("Call to blocking function " + "`%s%s%s` in real-time context!", + decorator.FunctionName(), arg.blocking_function_name_, + decorator.Reason()); + }}, + info); + + Printf("\n"); +} + +void __rtsan::PrintDiagnostics(const DiagnosticsInfo &info) { ScopedErrorReportLock l; Decorator d; - Printf("%s", d.Error()); - Report("ERROR: RealtimeSanitizer: unsafe-library-call\n"); - Printf("%s", d.Reason()); - Printf("Intercepted call to real-time unsafe function " - "`%s%s%s` in real-time context!\n", - d.FunctionName(), intercepted_function_name, d.Reason()); - + PrintError(d, info.call_info); + PrintReason(d, info.call_info); Printf("%s", d.Default()); - PrintStackTrace(pc, bp); + PrintStackTrace(info.pc, info.bp); } diff --git a/compiler-rt/lib/rtsan/rtsan_diagnostics.h b/compiler-rt/lib/rtsan/rtsan_diagnostics.h index 1d6c3ccb7bc7e..8aec512584b30 100644 --- a/compiler-rt/lib/rtsan/rtsan_diagnostics.h +++ b/compiler-rt/lib/rtsan/rtsan_diagnostics.h @@ -12,9 +12,31 @@ #pragma once +#include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_internal_defs.h" +#include + namespace __rtsan { -void PrintDiagnostics(const char *intercepted_function_name, - __sanitizer::uptr pc, __sanitizer::uptr bp); + +struct InterceptedCallInfo { + const char *intercepted_function_name_; +}; + +struct BlockingCallInfo { +public: + const char *blocking_function_name_; +}; + +using DiagnosticsCallerInfo = + std::variant; + +struct DiagnosticsInfo { + DiagnosticsCallerInfo call_info; + + __sanitizer::uptr pc; + __sanitizer::uptr bp; +}; + +void PrintDiagnostics(const DiagnosticsInfo &info); } // namespace __rtsan diff --git a/compiler-rt/lib/rtsan/tests/rtsan_test_assertions.cpp b/compiler-rt/lib/rtsan/tests/rtsan_test_assertions.cpp index cdf2ac3217004..b6999eeb7746a 100644 --- a/compiler-rt/lib/rtsan/tests/rtsan_test_assertions.cpp +++ b/compiler-rt/lib/rtsan/tests/rtsan_test_assertions.cpp @@ -13,30 +13,41 @@ #include "rtsan_test_utilities.h" #include "rtsan/rtsan_assertions.h" +#include "rtsan/rtsan_diagnostics.h" #include +using namespace __rtsan; + class TestRtsanAssertions : public ::testing::Test { protected: void SetUp() override { __rtsan_ensure_initialized(); } }; +DiagnosticsInfo FakeDiagnosticsInfo() { + DiagnosticsInfo info; + info.pc = 0; + info.bp = 0; + info.call_info = InterceptedCallInfo{"fake_function_name"}; + return info; +} + TEST_F(TestRtsanAssertions, ExpectNotRealtimeDoesNotDieIfNotInRealtimeContext) { __rtsan::Context context{}; ASSERT_FALSE(context.InRealtimeContext()); - ExpectNotRealtime(context, "fake_function_name"); + ExpectNotRealtime(context, FakeDiagnosticsInfo()); } TEST_F(TestRtsanAssertions, ExpectNotRealtimeDiesIfInRealtimeContext) { __rtsan::Context context{}; context.RealtimePush(); ASSERT_TRUE(context.InRealtimeContext()); - EXPECT_DEATH(ExpectNotRealtime(context, "fake_function_name"), ""); + EXPECT_DEATH(ExpectNotRealtime(context, FakeDiagnosticsInfo()), ""); } TEST_F(TestRtsanAssertions, ExpectNotRealtimeDoesNotDieIfRealtimeButBypassed) { __rtsan::Context context{}; context.RealtimePush(); context.BypassPush(); - ExpectNotRealtime(context, "fake_function_name"); + ExpectNotRealtime(context, FakeDiagnosticsInfo()); } diff --git a/compiler-rt/test/rtsan/blocking_call.cpp b/compiler-rt/test/rtsan/blocking_call.cpp new file mode 100644 index 0000000000000..47ce3d5544cbd --- /dev/null +++ b/compiler-rt/test/rtsan/blocking_call.cpp @@ -0,0 +1,34 @@ +// RUN: %clangxx -fsanitize=realtime %s -o %t +// RUN: not %run %t 2>&1 | FileCheck %s +// UNSUPPORTED: ios + +// Intent: Check that a function marked with [[clang::nonblocking]] cannot call a function that is blocking. + +#include +#include + +// TODO: Remove when [[blocking]] is implemented. +extern "C" void __rtsan_notify_blocking_call(const char *function_name); + +void custom_blocking_function() { + // TODO: When [[blocking]] is implemented, don't call this directly. + __rtsan_notify_blocking_call(__func__); +} + +void safe_call() { + // TODO: When [[blocking]] is implemented, don't call this directly. + __rtsan_notify_blocking_call(__func__); +} + +void process() [[clang::nonblocking]] { custom_blocking_function(); } + +int main() { + safe_call(); // This shouldn't die, because it isn't in nonblocking context. + process(); + return 0; + // CHECK-NOT: {{.*safe_call*}} + // CHECK: ==ERROR: RealtimeSanitizer: blocking-call + // CHECK-NEXT: Call to blocking function `custom_blocking_function` in real-time context! + // CHECK-NEXT: {{.*custom_blocking_function*}} + // CHECK-NEXT: {{.*process*}} +}