Skip to content

Commit ef29e99

Browse files
WillemKaufStephanDollberg
authored andcommitted
thread: track context switches in the ucontext variant too
The previous commit sets `g_context_switch_in_progress` around the `setjmp`/`longjmp` based switches only. Sanitized builds take the `SEASTAR_ASAN_ENABLED` branch and switch stacks via `swapcontext()`, which has the same problem: glibc's aarch64 swapcontext carries an empty (nop-only) CFI program while its third instruction already clobbers the link register, so a backtrace taken from a signal handler that lands mid-switch walks garbage frames and can crash in libgcc's unwinder. Move begin/end_context_switch() to shared scope and apply them to the ucontext-based switch functions, mirroring the setjmp variant: the flag set before a switch is cleared at the resume point control transfers to, or in initial_switch_in_completed() for a thread's first entry.
1 parent 0387c2f commit ef29e99

1 file changed

Lines changed: 18 additions & 12 deletions

File tree

src/core/thread.cc

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,16 @@ thread_local jmp_buf_link* g_current_context;
5050

5151
namespace {
5252
thread_local std::atomic_flag g_context_switch_in_progress{};
53+
54+
inline void begin_context_switch() noexcept {
55+
g_context_switch_in_progress.test_and_set(std::memory_order_relaxed);
56+
std::atomic_signal_fence(std::memory_order_seq_cst);
57+
}
58+
59+
inline void end_context_switch() noexcept {
60+
std::atomic_signal_fence(std::memory_order_seq_cst);
61+
g_context_switch_in_progress.clear(std::memory_order_relaxed);
62+
}
5363
}
5464

5565
#ifdef SEASTAR_ASAN_ENABLED
@@ -80,46 +90,54 @@ thread_local jmp_buf_link* g_previous_context;
8090

8191
void jmp_buf_link::initial_switch_in(ucontext_t* initial_context, const void* stack_bottom, size_t stack_size)
8292
{
93+
begin_context_switch();
8394
auto prev = std::exchange(g_current_context, this);
8495
link = prev;
8596
g_previous_context = prev;
8697
__sanitizer_start_switch_fiber(&prev->fake_stack, stack_bottom, stack_size);
8798
swapcontext(&prev->context, initial_context);
8899
__sanitizer_finish_switch_fiber(g_current_context->fake_stack, &g_previous_context->stack_bottom,
89100
&g_previous_context->stack_size);
101+
end_context_switch();
90102
}
91103

92104
void jmp_buf_link::switch_in()
93105
{
106+
begin_context_switch();
94107
auto prev = std::exchange(g_current_context, this);
95108
link = prev;
96109
g_previous_context = prev;
97110
__sanitizer_start_switch_fiber(&prev->fake_stack, stack_bottom, stack_size);
98111
swapcontext(&prev->context, &context);
99112
__sanitizer_finish_switch_fiber(g_current_context->fake_stack, &g_previous_context->stack_bottom,
100113
&g_previous_context->stack_size);
114+
end_context_switch();
101115
}
102116

103117
void jmp_buf_link::switch_out()
104118
{
119+
begin_context_switch();
105120
g_current_context = link;
106121
g_previous_context = this;
107122
__sanitizer_start_switch_fiber(&fake_stack, g_current_context->stack_bottom,
108123
g_current_context->stack_size);
109124
swapcontext(&context, &g_current_context->context);
110125
__sanitizer_finish_switch_fiber(g_current_context->fake_stack, &g_previous_context->stack_bottom,
111126
&g_previous_context->stack_size);
127+
end_context_switch();
112128
}
113129

114130
void jmp_buf_link::initial_switch_in_completed()
115131
{
116132
// This is a new thread and it doesn't have the fake stack yet. ASan will
117133
// create it lazily, for now just pass nullptr.
118134
__sanitizer_finish_switch_fiber(nullptr, &g_previous_context->stack_bottom, &g_previous_context->stack_size);
135+
end_context_switch();
119136
}
120137

121138
void jmp_buf_link::final_switch_out()
122139
{
140+
begin_context_switch();
123141
g_current_context = link;
124142
g_previous_context = this;
125143
// Since the thread is about to die we pass nullptr as fake_stack_save argument
@@ -130,18 +148,6 @@ void jmp_buf_link::final_switch_out()
130148

131149
#else
132150

133-
namespace {
134-
inline void begin_context_switch() noexcept {
135-
g_context_switch_in_progress.test_and_set(std::memory_order_relaxed);
136-
std::atomic_signal_fence(std::memory_order_seq_cst);
137-
}
138-
139-
inline void end_context_switch() noexcept {
140-
std::atomic_signal_fence(std::memory_order_seq_cst);
141-
g_context_switch_in_progress.clear(std::memory_order_relaxed);
142-
}
143-
}
144-
145151
inline void jmp_buf_link::initial_switch_in(ucontext_t* initial_context, const void*, size_t)
146152
{
147153
begin_context_switch();

0 commit comments

Comments
 (0)