Skip to content

[rtsan][compiler-rt] Introduce __rtsan_notify_blocking_call #109529

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Sep 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion compiler-rt/lib/rtsan/rtsan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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"
2 changes: 2 additions & 0 deletions compiler-rt/lib/rtsan/rtsan.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
8 changes: 2 additions & 6 deletions compiler-rt/lib/rtsan/rtsan_assertions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
Expand Down
4 changes: 3 additions & 1 deletion compiler-rt/lib/rtsan/rtsan_assertions.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
58 changes: 46 additions & 12 deletions compiler-rt/lib/rtsan/rtsan_diagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 <class... Ts> struct Overloaded : Ts... {
using Ts::operator()...;
};
// TODO: Remove below when c++20
template <class... Ts> Overloaded(Ts...) -> Overloaded<Ts...>;
} // namespace

static void PrintStackTrace(uptr pc, uptr bp) {
Expand All @@ -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);
}
26 changes: 24 additions & 2 deletions compiler-rt/lib/rtsan/rtsan_diagnostics.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,31 @@

#pragma once

#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_internal_defs.h"

#include <variant>

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<InterceptedCallInfo, BlockingCallInfo>;

struct DiagnosticsInfo {
DiagnosticsCallerInfo call_info;

__sanitizer::uptr pc;
__sanitizer::uptr bp;
};

void PrintDiagnostics(const DiagnosticsInfo &info);
} // namespace __rtsan
17 changes: 14 additions & 3 deletions compiler-rt/lib/rtsan/tests/rtsan_test_assertions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,30 +13,41 @@
#include "rtsan_test_utilities.h"

#include "rtsan/rtsan_assertions.h"
#include "rtsan/rtsan_diagnostics.h"

#include <gtest/gtest.h>

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());
}
34 changes: 34 additions & 0 deletions compiler-rt/test/rtsan/blocking_call.cpp
Original file line number Diff line number Diff line change
@@ -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 <stdio.h>
#include <stdlib.h>

// TODO: Remove when [[blocking]] is implemented.
extern "C" void __rtsan_notify_blocking_call(const char *function_name);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test exists right now to nail down the output format, and make sure this works as intended. Shortly we will have the proper [[blocking]] attribute and this test will be simplified somewhat


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*}}
}
Loading