Skip to content

Commit cc976d1

Browse files
committed
signals: switch to app TLS if available
This patch changes logic around executing user signal handler in kill() to make new signal handler thread use app TLS of an application thread if available. Normally, application threads share thread local storage area (TLS) with kernel or use one created by kernel. But when running statically linked executables or dynamically ones with Linux dynamic linker, the application threads create TLS on its own and user signal handler may reference thread local variables which do not exist in the TLS of the new signal handler thread. As a result the app would crash like so: ``` 9 0x000000004030bd54 in page_fault (ef=0x40007fe83088) at arch/x64/mmu.cc:42 10 <signal handler called> 11 __GI___pthread_cleanup_upto (target=target@entry=0x200000205080 <sigend_jmp_buf>, targetframe=targetframe@entry=0x40007fe93fc8 "\005\"D\177") at ./nptl/pthread_cleanup_upto.c:32 12 0x000020007f44232c in _longjmp_unwind (env=env@entry=0x200000205080 <sigend_jmp_buf>, val=val@entry=1) at ../sysdeps/nptl/jmp-unwind.c:27 13 0x000020007f442205 in __libc_siglongjmp (env=0x200000205080 <sigend_jmp_buf>, val=1) at ../setjmp/longjmp.c:30 14 0x0000200000202543 in sigend_handler (sig=2) at main.c:121 15 0x000000004037f0ee in sched::thread::main (this=0x40007fe7e040) at core/sched.cc:1415 16 sched::thread_main_c (t=0x40007fe7e040) at arch/x64/arch-switch.hh:377 17 0x000000004030bc52 in thread_main () at arch/x64/entry.S:161 ``` This patch applies a bit of trickery (potentially dangerous if user handler tries to write to TLS) and select an application TLS of a current thread or one of the other application threads and makes it available to the new signal handler thread. The signal handler thread in such case would switch from kernel TLS to app TLS before executing handler routine. This patch makes following tests pass when running with Linux dynamic linker: - tst-kill - tst-sigwait - tst-sigaction Refers cloudius-systems#1278 Signed-off-by: Waldemar Kozaczuk <[email protected]>
1 parent ad355ab commit cc976d1

File tree

4 files changed

+63
-0
lines changed

4 files changed

+63
-0
lines changed

arch/x64/tls-switch.hh

+22
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,28 @@ public:
4848
}
4949
}
5050
};
51+
//
52+
//Simple RAII utility classes that implement the logic to switch
53+
//fsbase to the specified app address and back to the kernel one
54+
class user_tls_switch {
55+
thread_control_block *_kernel_tcb;
56+
public:
57+
user_tls_switch() {
58+
asm volatile ( "movq %%gs:16, %0\n\t" : "=r"(_kernel_tcb));
59+
60+
//Switch to app tcb if app tcb present
61+
if (_kernel_tcb->app_tcb) {
62+
set_fsbase(reinterpret_cast<u64>(_kernel_tcb->app_tcb));
63+
}
64+
}
65+
66+
~user_tls_switch() {
67+
//Switch to kernel tcb if app tcb present
68+
if (_kernel_tcb->app_tcb) {
69+
set_fsbase(reinterpret_cast<u64>(_kernel_tcb->self));
70+
}
71+
}
72+
};
5173

5274
}
5375

core/sched.cc

+10
Original file line numberDiff line numberDiff line change
@@ -2088,6 +2088,16 @@ void with_thread_by_id(unsigned id, std::function<void(thread *)> f) {
20882088
}
20892089
}
20902090

2091+
thread *find_first_app_thread(std::function<bool(thread &)> f) {
2092+
WITH_LOCK(thread_map_mutex) {
2093+
for (auto th : thread_map) {
2094+
if(th.second->is_app() && f(*th.second)) {
2095+
return th.second;
2096+
}
2097+
}
2098+
}
2099+
return nullptr;
2100+
}
20912101

20922102
}
20932103

include/osv/sched.hh

+2
Original file line numberDiff line numberDiff line change
@@ -1552,6 +1552,8 @@ void with_all_threads(std::function<void(sched::thread &)>);
15521552
// should return quickly.
15531553
void with_thread_by_id(unsigned id, std::function<void(sched::thread *)>);
15541554

1555+
thread *find_first_app_thread(std::function<bool(thread &)> f);
1556+
15551557
}
15561558

15571559
#endif /* SCHED_HH_ */

libc/signal.cc

+29
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@
2121
#include <osv/pid.h>
2222
#include <osv/export.h>
2323

24+
#ifdef __x86_64__
25+
#include "tls-switch.hh"
26+
#endif
27+
2428
using namespace osv::clock::literals;
2529

2630
namespace osv {
@@ -393,6 +397,11 @@ int kill(pid_t pid, int sig)
393397
signal_actions[sigidx].sa_flags = 0;
394398
signal_actions[sigidx].sa_handler = SIG_DFL;
395399
}
400+
#ifdef __x86_64__
401+
//In case this signal handler thread has specified app thread local storage
402+
//let us switch to it before invoking the user handler routine
403+
arch::user_tls_switch tls_switch;
404+
#endif
396405
if (sa.sa_flags & SA_SIGINFO) {
397406
// FIXME: proper second (siginfo) and third (context) arguments (See example in call_signal_handler)
398407
sa.sa_sigaction(sig, nullptr, nullptr);
@@ -401,6 +410,26 @@ int kill(pid_t pid, int sig)
401410
}
402411
}, sched::thread::attr().detached().stack(65536).name("signal_handler"),
403412
false, true);
413+
//If we are running statically linked executable or a dynamic one with Linux
414+
//dynamic linker, its threads very likely use app thread local storage and signal
415+
//routine may rely on it presence. For that reason we use app TLS of the current
416+
//thread if it has one. Otherwise we find 1st app thread with non-null app TLS
417+
//and assign the signal handler thread app TLS to it so it is switched to (see above).
418+
//TODO: Ideally we should only run the logic below if the current app is statically
419+
//linked executable or a dynamic one ran with Linux dynamic linker
420+
//(what if we are handling "Ctrl-C"?)
421+
u64 app_tcb = sched::thread::current()->get_app_tcb();
422+
if (!app_tcb) {
423+
auto first_app_thread = sched::find_first_app_thread([&](sched::thread &t) {
424+
return t.get_app_tcb();
425+
});
426+
if (first_app_thread) {
427+
app_tcb = first_app_thread->get_app_tcb();
428+
}
429+
}
430+
if (app_tcb) {
431+
t->set_app_tcb(app_tcb);
432+
}
404433
t->start();
405434
}
406435
return 0;

0 commit comments

Comments
 (0)