base-hw: rm C++ exception from syscall dispatcher

This patch removes the only residual C++ exception from the kernel part
of core, eliminating the risk of the kernel thread trying to enter the
kernel itself via the C++ exception-handling path. When throwing an
exception, __cxa_allocate_exception invokes the cxx_heap, which
synchronizes accesses via a Genode::Mutex. In the contention case,
the blocking of the mutex issues a syscall to pause the caller.
The patch fixes the problem by replacing the exception with a return
value.

Fixes #5382
Issue #5245
This commit is contained in:
Norman Feske 2024-11-14 14:33:01 +01:00 committed by Christian Helmuth
parent 591aadea54
commit 052dd903a4
2 changed files with 59 additions and 33 deletions

View File

@ -33,45 +33,42 @@ extern "C" void _core_start(void);
using namespace Kernel; using namespace Kernel;
void Thread::_ipc_alloc_recv_caps(unsigned cap_count) Thread::Ipc_alloc_result Thread::_ipc_alloc_recv_caps(unsigned cap_count)
{ {
using Allocator = Genode::Allocator; using Allocator = Genode::Allocator;
using Result = Ipc_alloc_result;
Allocator &slab = pd().platform_pd().capability_slab(); Allocator &slab = pd().platform_pd().capability_slab();
for (unsigned i = 0; i < cap_count; i++) { for (unsigned i = 0; i < cap_count; i++) {
if (_obj_id_ref_ptr[i] != nullptr) if (_obj_id_ref_ptr[i] != nullptr)
continue; continue;
slab.try_alloc(sizeof(Object_identity_reference)).with_result( Result const result =
slab.try_alloc(sizeof(Object_identity_reference)).convert<Result>(
[&] (void *ptr) { [&] (void *ptr) {
_obj_id_ref_ptr[i] = ptr; }, _obj_id_ref_ptr[i] = ptr;
return Result::OK; },
[&] (Allocator::Alloc_error e) { [&] (Allocator::Alloc_error e) {
switch (e) {
case Allocator::Alloc_error::DENIED:
/* /*
* Slab is exhausted, reflect condition to the client. * Conditions other than DENIED cannot happen because the slab
*/ * does not try to grow automatically. It is explicitely
throw Genode::Out_of_ram(); * expanded by the client as response to the EXHAUSTED return
* value.
case Allocator::Alloc_error::OUT_OF_CAPS:
case Allocator::Alloc_error::OUT_OF_RAM:
/*
* These conditions cannot happen because the slab
* does not try to grow automatically. It is
* explicitely expanded by the client as response to
* the 'Out_of_ram' condition above.
*/ */
if (e != Allocator::Alloc_error::DENIED)
Genode::raw("unexpected recv_caps allocation failure"); Genode::raw("unexpected recv_caps allocation failure");
}
return Result::EXHAUSTED;
} }
); );
if (result == Result::EXHAUSTED)
return result;
} }
_ipc_rcv_caps = cap_count; _ipc_rcv_caps = cap_count;
return Result::OK;
} }
@ -87,11 +84,20 @@ void Thread::_ipc_free_recv_caps()
} }
void Thread::_ipc_init(Genode::Native_utcb &utcb, Thread &starter) Thread::Ipc_alloc_result Thread::_ipc_init(Genode::Native_utcb &utcb, Thread &starter)
{ {
_utcb = &utcb; _utcb = &utcb;
_ipc_alloc_recv_caps((unsigned)(starter._utcb->cap_cnt()));
switch (_ipc_alloc_recv_caps((unsigned)(starter._utcb->cap_cnt()))) {
case Ipc_alloc_result::OK:
ipc_copy_msg(starter); ipc_copy_msg(starter);
break;
case Ipc_alloc_result::EXHAUSTED:
return Ipc_alloc_result::EXHAUSTED;
}
return Ipc_alloc_result::OK;
} }
@ -330,7 +336,13 @@ void Thread::_call_start_thread()
/* join protection domain */ /* join protection domain */
thread._pd = (Pd *) user_arg_3(); thread._pd = (Pd *) user_arg_3();
thread._ipc_init(*(Native_utcb *)user_arg_4(), *this); switch (thread._ipc_init(*(Native_utcb *)user_arg_4(), *this)) {
case Ipc_alloc_result::OK:
break;
case Ipc_alloc_result::EXHAUSTED:
user_arg_0(-2);
return;
}
/* /*
* Sanity check core threads! * Sanity check core threads!
@ -482,7 +494,14 @@ void Thread::_call_delete_pd()
void Thread::_call_await_request_msg() void Thread::_call_await_request_msg()
{ {
if (_ipc_node.ready_to_wait()) { if (_ipc_node.ready_to_wait()) {
_ipc_alloc_recv_caps((unsigned)user_arg_1());
switch (_ipc_alloc_recv_caps((unsigned)user_arg_1())) {
case Ipc_alloc_result::OK:
break;
case Ipc_alloc_result::EXHAUSTED:
user_arg_0(-2);
return;
}
_ipc_node.wait(); _ipc_node.wait();
if (_ipc_node.waiting()) { if (_ipc_node.waiting()) {
_become_inactive(AWAITS_IPC); _become_inactive(AWAITS_IPC);
@ -545,7 +564,13 @@ void Thread::_call_send_request_msg()
if (!_ipc_node.ready_to_send()) { if (!_ipc_node.ready_to_send()) {
Genode::raw("IPC send request: bad state"); Genode::raw("IPC send request: bad state");
} else { } else {
_ipc_alloc_recv_caps((unsigned)user_arg_2()); switch (_ipc_alloc_recv_caps((unsigned)user_arg_2())) {
case Ipc_alloc_result::OK:
break;
case Ipc_alloc_result::EXHAUSTED:
user_arg_0(-2);
return;
}
_ipc_capid = oir ? oir->capid() : cap_id_invalid(); _ipc_capid = oir ? oir->capid() : cap_id_invalid();
_ipc_node.send(dst->_ipc_node, help); _ipc_node.send(dst->_ipc_node, help);
} }
@ -822,8 +847,6 @@ void Thread::_call_single_step() {
void Thread::_call() void Thread::_call()
{ {
try {
/* switch over unrestricted kernel calls */ /* switch over unrestricted kernel calls */
unsigned const call_id = (unsigned)user_arg_0(); unsigned const call_id = (unsigned)user_arg_0();
switch (call_id) { switch (call_id) {
@ -907,7 +930,6 @@ void Thread::_call()
_die(); _die();
return; return;
} }
} catch (Genode::Allocator::Out_of_memory &e) { user_arg_0(-2); }
} }

View File

@ -322,9 +322,13 @@ class Kernel::Thread : private Kernel::Object, public Cpu_job, private Timeout
kobj.destruct(); kobj.destruct();
} }
void _ipc_alloc_recv_caps(unsigned rcv_cap_count); enum Ipc_alloc_result { OK, EXHAUSTED };
[[nodiscard]] Ipc_alloc_result _ipc_alloc_recv_caps(unsigned rcv_cap_count);
void _ipc_free_recv_caps(); void _ipc_free_recv_caps();
void _ipc_init(Genode::Native_utcb &utcb, Thread &callee);
[[nodiscard]] Ipc_alloc_result _ipc_init(Genode::Native_utcb &utcb, Thread &callee);
public: public: