From 60ba210a6bd1780e6fb30ee78b8b560fd04cba35 Mon Sep 17 00:00:00 2001 From: Stefan Kalkowski Date: Tue, 1 Dec 2015 14:50:14 +0100 Subject: [PATCH] hw: reference count capabilities in UTCBs When capabilities are delegated to components, they are added to the UTCB of the target thread. Before the thread is able to take out the capability id out of the UTCB and adapt the user-level capability reference counter, it might happen that another thread of the same component deletes the same capability because its user-level reference counter reached zero. If the kernel then destroys the capability, before the same capability id is taken out of all UTCBs, an inconsitent view in the component is the result. To keep an consistent view in the multi-threading scenario, the kernel now counts how often it puts a capability into a UTCB. The threads on the other hand hint the kernel when they took capabilities out of the UTCB, so the kernel can decrement the counter again. Only when the counter is zero, capabilities can get destructed. Fix #1623 --- repos/base-hw/include/base/native_types.h | 5 ++++- repos/base-hw/include/kernel/interface.h | 13 ++++++++++++- repos/base-hw/src/core/include/kernel/object.h | 5 +++++ repos/base-hw/src/core/include/kernel/thread.h | 1 + repos/base-hw/src/core/kernel/ipc_node.cc | 9 ++------- repos/base-hw/src/core/kernel/object.cc | 2 +- repos/base-hw/src/core/kernel/thread.cc | 14 +++++++++++++- 7 files changed, 38 insertions(+), 11 deletions(-) diff --git a/repos/base-hw/include/base/native_types.h b/repos/base-hw/include/base/native_types.h index 6fd0445fc2..30fff4e9ca 100644 --- a/repos/base-hw/include/base/native_types.h +++ b/repos/base-hw/include/base/native_types.h @@ -161,7 +161,10 @@ class Genode::Native_utcb void copy_to(Msgbuf_base &o) { o._snd_cap_cnt = _cap_cnt; - for (unsigned i = 0; i < _cap_cnt; i++) o._caps[i] = _caps[i]; + for (unsigned i = 0; i < _cap_cnt; i++) { + o._caps[i] = _caps[i]; + if (o._caps[i].valid()) Kernel::ack_cap(o._caps[i].dst()); + } memcpy(o.buf, _buf, min(_size, o._size)); } diff --git a/repos/base-hw/include/kernel/interface.h b/repos/base-hw/include/kernel/interface.h index b74f5a08d1..aee31bf025 100644 --- a/repos/base-hw/include/kernel/interface.h +++ b/repos/base-hw/include/kernel/interface.h @@ -41,7 +41,8 @@ namespace Kernel constexpr Call_arg call_id_print_char() { return 10; } constexpr Call_arg call_id_update_data_region() { return 11; } constexpr Call_arg call_id_update_instr_region() { return 12; } - constexpr Call_arg call_id_delete_cap() { return 13; } + constexpr Call_arg call_id_ack_cap() { return 13; } + constexpr Call_arg call_id_delete_cap() { return 14; } /***************************************************************** @@ -266,6 +267,16 @@ namespace Kernel return call(call_id_kill_signal_context(), context); } + /** + * Acknowledge reception of a capability + * + * \param cap capability id to acknowledge + */ + inline void ack_cap(capid_t const cap) + { + call(call_id_ack_cap(), cap); + } + /** * Delete a capability id * diff --git a/repos/base-hw/src/core/include/kernel/object.h b/repos/base-hw/src/core/include/kernel/object.h index e043f13ec5..6adfd5acf7 100644 --- a/repos/base-hw/src/core/include/kernel/object.h +++ b/repos/base-hw/src/core/include/kernel/object.h @@ -106,6 +106,7 @@ class Kernel::Object_identity_reference capid_t _capid; Object_identity *_identity; Pd &_pd; + unsigned short _in_utcbs; public: @@ -125,6 +126,10 @@ class Kernel::Object_identity_reference Pd & pd() { return _pd; } capid_t capid() { return _capid; } + void add_to_utcb() { _in_utcbs++; } + void remove_from_utcb() { _in_utcbs--; } + bool in_utcb() { return _in_utcbs > 0; } + void invalidate(); diff --git a/repos/base-hw/src/core/include/kernel/thread.h b/repos/base-hw/src/core/include/kernel/thread.h index 5c180a93f1..719b88fd3c 100644 --- a/repos/base-hw/src/core/include/kernel/thread.h +++ b/repos/base-hw/src/core/include/kernel/thread.h @@ -244,6 +244,7 @@ class Kernel::Thread void _call_ack_irq(); void _call_new_obj(); void _call_delete_obj(); + void _call_ack_cap(); void _call_delete_cap(); template diff --git a/repos/base-hw/src/core/kernel/ipc_node.cc b/repos/base-hw/src/core/kernel/ipc_node.cc index 1f2d803944..493bcc70ae 100644 --- a/repos/base-hw/src/core/kernel/ipc_node.cc +++ b/repos/base-hw/src/core/kernel/ipc_node.cc @@ -47,13 +47,6 @@ void Ipc_node::copy_msg(Ipc_node * const sender) continue; } - /* within the same pd, we can simply copy the id */ - if (pd() == sender->pd()) { - _utcb->cap_add(id); - pd()->platform_pd()->capability_slab().free(_obj_id_ref_ptr[i]); - continue; - } - /* lookup the capability id within the caller's cap space */ Reference *oir = (id == cap_id_invalid()) ? nullptr : sender->pd()->cap_tree().find(id); @@ -76,6 +69,8 @@ void Ipc_node::copy_msg(Ipc_node * const sender) } else /* otherwise free the pre-allocation */ pd()->platform_pd()->capability_slab().free(_obj_id_ref_ptr[i]); + if (dst_oir) dst_oir->add_to_utcb(); + /* add the translated capability id to the target buffer */ _utcb->cap_add(dst_oir ? dst_oir->capid() : cap_id_invalid()); } diff --git a/repos/base-hw/src/core/kernel/object.cc b/repos/base-hw/src/core/kernel/object.cc index 83bdefb697..fdd4006138 100644 --- a/repos/base-hw/src/core/kernel/object.cc +++ b/repos/base-hw/src/core/kernel/object.cc @@ -82,7 +82,7 @@ void Object_identity_reference::invalidate() { Object_identity_reference::Object_identity_reference(Object_identity *oi, Pd &pd) -: _capid(pd.capid_alloc().alloc()), _identity(oi), _pd(pd) +: _capid(pd.capid_alloc().alloc()), _identity(oi), _pd(pd), _in_utcbs(0) { if (_identity) _identity->insert(this); _pd.cap_tree().insert(this); diff --git a/repos/base-hw/src/core/kernel/thread.cc b/repos/base-hw/src/core/kernel/thread.cc index fa328e5a55..5bb5e303ca 100644 --- a/repos/base-hw/src/core/kernel/thread.cc +++ b/repos/base-hw/src/core/kernel/thread.cc @@ -585,10 +585,21 @@ void Thread::_call_delete_obj() } +void Thread::_call_ack_cap() +{ + Object_identity_reference * oir = pd()->cap_tree().find(user_arg_1()); + if (oir) oir->remove_from_utcb(); +} + + void Thread::_call_delete_cap() { Object_identity_reference * oir = pd()->cap_tree().find(user_arg_1()); - if (oir) destroy(pd()->platform_pd()->capability_slab(), oir); + if (!oir) return; + + if (oir->in_utcb()) return; + + destroy(pd()->platform_pd()->capability_slab(), oir); } @@ -612,6 +623,7 @@ void Thread::_call() case call_id_await_signal(): _call_await_signal(); return; case call_id_ack_signal(): _call_ack_signal(); return; case call_id_print_char(): _call_print_char(); return; + case call_id_ack_cap(): _call_ack_cap(); return; case call_id_delete_cap(): _call_delete_cap(); return; default: /* check wether this is a core thread */