Skip to content

Commit 1b60193

Browse files
authored
[rtsan][compiler-rt] Introduce __rtsan_notify_blocking_call (#109529)
# Why? In llvm, we need to add a call to `__rtsan_notify_blocking_call()` when a function is marked `[[clang::blocking]]`. This will produce a different error message than a call to an unsafe malloc etc
1 parent 56124fe commit 1b60193

File tree

8 files changed

+141
-25
lines changed

8 files changed

+141
-25
lines changed

compiler-rt/lib/rtsan/rtsan.cpp

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "sanitizer_common/sanitizer_atomic.h"
1717
#include "sanitizer_common/sanitizer_common.h"
1818
#include "sanitizer_common/sanitizer_mutex.h"
19+
#include "sanitizer_common/sanitizer_stacktrace.h"
1920

2021
using namespace __rtsan;
2122
using namespace __sanitizer;
@@ -75,6 +76,20 @@ SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_enable() {
7576
SANITIZER_INTERFACE_ATTRIBUTE void
7677
__rtsan_notify_intercepted_call(const char *intercepted_function_name) {
7778
__rtsan_ensure_initialized();
78-
ExpectNotRealtime(GetContextForThisThread(), intercepted_function_name);
79+
80+
GET_CALLER_PC_BP;
81+
DiagnosticsInfo info = {InterceptedCallInfo{intercepted_function_name}, pc,
82+
bp};
83+
ExpectNotRealtime(GetContextForThisThread(), info);
7984
}
85+
86+
SANITIZER_INTERFACE_ATTRIBUTE void
87+
__rtsan_notify_blocking_call(const char *blocking_function_name) {
88+
__rtsan_ensure_initialized();
89+
90+
GET_CALLER_PC_BP;
91+
DiagnosticsInfo info = {BlockingCallInfo{blocking_function_name}, pc, bp};
92+
ExpectNotRealtime(GetContextForThisThread(), info);
93+
}
94+
8095
} // extern "C"

compiler-rt/lib/rtsan/rtsan.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,6 @@ SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_enable();
4545
SANITIZER_INTERFACE_ATTRIBUTE void
4646
__rtsan_notify_intercepted_call(const char *intercepted_function_name);
4747

48+
SANITIZER_INTERFACE_ATTRIBUTE void
49+
__rtsan_notify_blocking_call(const char *blocking_function_name);
4850
} // extern "C"

compiler-rt/lib/rtsan/rtsan_assertions.cpp

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,14 @@
1414
#include "rtsan/rtsan.h"
1515
#include "rtsan/rtsan_diagnostics.h"
1616

17-
#include "sanitizer_common/sanitizer_stacktrace.h"
18-
1917
using namespace __sanitizer;
2018

21-
void __rtsan::ExpectNotRealtime(Context &context,
22-
const char *intercepted_function_name) {
19+
void __rtsan::ExpectNotRealtime(Context &context, const DiagnosticsInfo &info) {
2320
CHECK(__rtsan_is_initialized());
2421
if (context.InRealtimeContext() && !context.IsBypassed()) {
2522
context.BypassPush();
2623

27-
GET_CALLER_PC_BP;
28-
PrintDiagnostics(intercepted_function_name, pc, bp);
24+
PrintDiagnostics(info);
2925
Die();
3026
context.BypassPop();
3127
}

compiler-rt/lib/rtsan/rtsan_assertions.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@
1313
#pragma once
1414

1515
#include "rtsan/rtsan_context.h"
16+
#include "rtsan/rtsan_diagnostics.h"
1617

1718
namespace __rtsan {
18-
void ExpectNotRealtime(Context &context, const char *intercepted_function_name);
19+
20+
void ExpectNotRealtime(Context &context, const DiagnosticsInfo &info);
1921
} // namespace __rtsan

compiler-rt/lib/rtsan/rtsan_diagnostics.cpp

Lines changed: 46 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,15 @@ namespace {
3434
class Decorator : public __sanitizer::SanitizerCommonDecorator {
3535
public:
3636
Decorator() : SanitizerCommonDecorator() {}
37-
const char *FunctionName() { return Green(); }
38-
const char *Reason() { return Blue(); }
37+
const char *FunctionName() const { return Green(); }
38+
const char *Reason() const { return Blue(); }
3939
};
40+
41+
template <class... Ts> struct Overloaded : Ts... {
42+
using Ts::operator()...;
43+
};
44+
// TODO: Remove below when c++20
45+
template <class... Ts> Overloaded(Ts...) -> Overloaded<Ts...>;
4046
} // namespace
4147

4248
static void PrintStackTrace(uptr pc, uptr bp) {
@@ -46,18 +52,46 @@ static void PrintStackTrace(uptr pc, uptr bp) {
4652
stack.Print();
4753
}
4854

49-
void __rtsan::PrintDiagnostics(const char *intercepted_function_name, uptr pc,
50-
uptr bp) {
55+
static void PrintError(const Decorator &decorator,
56+
const DiagnosticsCallerInfo &info) {
57+
const char *violation_type = std::visit(
58+
Overloaded{
59+
[](const InterceptedCallInfo &) { return "unsafe-library-call"; },
60+
[](const BlockingCallInfo &) { return "blocking-call"; }},
61+
info);
62+
63+
Printf("%s", decorator.Error());
64+
Report("ERROR: RealtimeSanitizer: %s\n", violation_type);
65+
}
66+
67+
static void PrintReason(const Decorator &decorator,
68+
const DiagnosticsCallerInfo &info) {
69+
Printf("%s", decorator.Reason());
70+
71+
std::visit(
72+
Overloaded{[decorator](const InterceptedCallInfo &call) {
73+
Printf("Intercepted call to real-time unsafe function "
74+
"`%s%s%s` in real-time context!",
75+
decorator.FunctionName(),
76+
call.intercepted_function_name_, decorator.Reason());
77+
},
78+
[decorator](const BlockingCallInfo &arg) {
79+
Printf("Call to blocking function "
80+
"`%s%s%s` in real-time context!",
81+
decorator.FunctionName(), arg.blocking_function_name_,
82+
decorator.Reason());
83+
}},
84+
info);
85+
86+
Printf("\n");
87+
}
88+
89+
void __rtsan::PrintDiagnostics(const DiagnosticsInfo &info) {
5190
ScopedErrorReportLock l;
5291

5392
Decorator d;
54-
Printf("%s", d.Error());
55-
Report("ERROR: RealtimeSanitizer: unsafe-library-call\n");
56-
Printf("%s", d.Reason());
57-
Printf("Intercepted call to real-time unsafe function "
58-
"`%s%s%s` in real-time context!\n",
59-
d.FunctionName(), intercepted_function_name, d.Reason());
60-
93+
PrintError(d, info.call_info);
94+
PrintReason(d, info.call_info);
6195
Printf("%s", d.Default());
62-
PrintStackTrace(pc, bp);
96+
PrintStackTrace(info.pc, info.bp);
6397
}

compiler-rt/lib/rtsan/rtsan_diagnostics.h

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,31 @@
1212

1313
#pragma once
1414

15+
#include "sanitizer_common/sanitizer_common.h"
1516
#include "sanitizer_common/sanitizer_internal_defs.h"
1617

18+
#include <variant>
19+
1720
namespace __rtsan {
18-
void PrintDiagnostics(const char *intercepted_function_name,
19-
__sanitizer::uptr pc, __sanitizer::uptr bp);
21+
22+
struct InterceptedCallInfo {
23+
const char *intercepted_function_name_;
24+
};
25+
26+
struct BlockingCallInfo {
27+
public:
28+
const char *blocking_function_name_;
29+
};
30+
31+
using DiagnosticsCallerInfo =
32+
std::variant<InterceptedCallInfo, BlockingCallInfo>;
33+
34+
struct DiagnosticsInfo {
35+
DiagnosticsCallerInfo call_info;
36+
37+
__sanitizer::uptr pc;
38+
__sanitizer::uptr bp;
39+
};
40+
41+
void PrintDiagnostics(const DiagnosticsInfo &info);
2042
} // namespace __rtsan

compiler-rt/lib/rtsan/tests/rtsan_test_assertions.cpp

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,30 +13,41 @@
1313
#include "rtsan_test_utilities.h"
1414

1515
#include "rtsan/rtsan_assertions.h"
16+
#include "rtsan/rtsan_diagnostics.h"
1617

1718
#include <gtest/gtest.h>
1819

20+
using namespace __rtsan;
21+
1922
class TestRtsanAssertions : public ::testing::Test {
2023
protected:
2124
void SetUp() override { __rtsan_ensure_initialized(); }
2225
};
2326

27+
DiagnosticsInfo FakeDiagnosticsInfo() {
28+
DiagnosticsInfo info;
29+
info.pc = 0;
30+
info.bp = 0;
31+
info.call_info = InterceptedCallInfo{"fake_function_name"};
32+
return info;
33+
}
34+
2435
TEST_F(TestRtsanAssertions, ExpectNotRealtimeDoesNotDieIfNotInRealtimeContext) {
2536
__rtsan::Context context{};
2637
ASSERT_FALSE(context.InRealtimeContext());
27-
ExpectNotRealtime(context, "fake_function_name");
38+
ExpectNotRealtime(context, FakeDiagnosticsInfo());
2839
}
2940

3041
TEST_F(TestRtsanAssertions, ExpectNotRealtimeDiesIfInRealtimeContext) {
3142
__rtsan::Context context{};
3243
context.RealtimePush();
3344
ASSERT_TRUE(context.InRealtimeContext());
34-
EXPECT_DEATH(ExpectNotRealtime(context, "fake_function_name"), "");
45+
EXPECT_DEATH(ExpectNotRealtime(context, FakeDiagnosticsInfo()), "");
3546
}
3647

3748
TEST_F(TestRtsanAssertions, ExpectNotRealtimeDoesNotDieIfRealtimeButBypassed) {
3849
__rtsan::Context context{};
3950
context.RealtimePush();
4051
context.BypassPush();
41-
ExpectNotRealtime(context, "fake_function_name");
52+
ExpectNotRealtime(context, FakeDiagnosticsInfo());
4253
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// RUN: %clangxx -fsanitize=realtime %s -o %t
2+
// RUN: not %run %t 2>&1 | FileCheck %s
3+
// UNSUPPORTED: ios
4+
5+
// Intent: Check that a function marked with [[clang::nonblocking]] cannot call a function that is blocking.
6+
7+
#include <stdio.h>
8+
#include <stdlib.h>
9+
10+
// TODO: Remove when [[blocking]] is implemented.
11+
extern "C" void __rtsan_notify_blocking_call(const char *function_name);
12+
13+
void custom_blocking_function() {
14+
// TODO: When [[blocking]] is implemented, don't call this directly.
15+
__rtsan_notify_blocking_call(__func__);
16+
}
17+
18+
void safe_call() {
19+
// TODO: When [[blocking]] is implemented, don't call this directly.
20+
__rtsan_notify_blocking_call(__func__);
21+
}
22+
23+
void process() [[clang::nonblocking]] { custom_blocking_function(); }
24+
25+
int main() {
26+
safe_call(); // This shouldn't die, because it isn't in nonblocking context.
27+
process();
28+
return 0;
29+
// CHECK-NOT: {{.*safe_call*}}
30+
// CHECK: ==ERROR: RealtimeSanitizer: blocking-call
31+
// CHECK-NEXT: Call to blocking function `custom_blocking_function` in real-time context!
32+
// CHECK-NEXT: {{.*custom_blocking_function*}}
33+
// CHECK-NEXT: {{.*process*}}
34+
}

0 commit comments

Comments
 (0)