mirror of
https://github.com/genodelabs/genode.git
synced 2025-04-08 20:05:54 +00:00
nova: fix pager cleanup handling
The cleanup call must be performed already during the _dissolve function shortly after the object at the cap_session is freed up. Otherwise there is the chance that an in-flight IPC will find the to be dissolved function again. Bomb test triggered the case, that a already dissolved rpc_object was found by a in-flight IPC. If the rpc_object was already freed up by alloc->destroy the thread using this stale rpc_object pointer cause page-faults in core. Fixes partly #549
This commit is contained in:
parent
28f7cf5dc2
commit
edd30b56a2
@ -186,6 +186,15 @@ namespace Genode {
|
||||
*/
|
||||
Thread_capability thread_cap() { return _thread_cap; } const
|
||||
void thread_cap(Thread_capability cap) { _thread_cap = cap; }
|
||||
|
||||
/**
|
||||
* Make sure nobody is in the handler anymore by doing an IPC to a
|
||||
* local cap pointing to same serving thread (if not running in the
|
||||
* context of the serving thread). When the call returns
|
||||
* we know that nobody is handled by this object anymore, because
|
||||
* all remotely available portals had been revoked beforehand.
|
||||
*/
|
||||
void cleanup_call();
|
||||
};
|
||||
|
||||
|
||||
|
@ -79,8 +79,8 @@ void Pager_object::_exception_handler(addr_t portal_id)
|
||||
Utcb *utcb = _check_handler(myself, obj);
|
||||
addr_t fault_ip = utcb->ip;
|
||||
|
||||
if (obj->submit_exception_signal())
|
||||
/* Somebody takes care don't die - just recall and block */
|
||||
if (obj->submit_exception_signal())
|
||||
/* somebody takes care don't die - just recall and block */
|
||||
obj->client_recall();
|
||||
else {
|
||||
PWRN("unresolvable exception at ip 0x%lx, exception portal 0x%lx",
|
||||
@ -126,13 +126,13 @@ void Pager_object::_recall_handler()
|
||||
utcb->flags = obj->_state.thread.eflags | 0x100UL;
|
||||
utcb->mtd = Nova::Mtd(Mtd::EFL).value();
|
||||
} else
|
||||
if (!obj->_state.singlestep && singlestep_state) {
|
||||
utcb->flags = obj->_state.thread.eflags & ~0x100UL;
|
||||
utcb->mtd = Nova::Mtd(Mtd::EFL).value();
|
||||
} else
|
||||
utcb->mtd = 0;
|
||||
if (!obj->_state.singlestep && singlestep_state) {
|
||||
utcb->flags = obj->_state.thread.eflags & ~0x100UL;
|
||||
utcb->mtd = Nova::Mtd(Mtd::EFL).value();
|
||||
} else
|
||||
utcb->mtd = 0;
|
||||
utcb->set_msg_word(0);
|
||||
|
||||
|
||||
reply(myself->stack_top());
|
||||
}
|
||||
|
||||
@ -200,6 +200,27 @@ uint8_t Pager_object::client_recall()
|
||||
}
|
||||
|
||||
|
||||
void Pager_object::cleanup_call()
|
||||
{
|
||||
/*
|
||||
* Revoke all portals of Pager_object from others.
|
||||
* The portals will be finally revoked during thread destruction.
|
||||
*/
|
||||
revoke(Obj_crd(exc_pt_sel(), NUM_INITIAL_PT_LOG2), false);
|
||||
|
||||
Utcb *utcb = (Utcb *)Thread_base::myself()->utcb();
|
||||
if (reinterpret_cast<Utcb *>(this->utcb()) == utcb) return;
|
||||
|
||||
/* if pager is blocked wake him up */
|
||||
wake_up();
|
||||
|
||||
utcb->set_msg_word(0);
|
||||
if (uint8_t res = call(_pt_cleanup))
|
||||
PERR("%8p - cleanup call to pager (%8p) failed res=%d",
|
||||
utcb, this->utcb(), res);
|
||||
}
|
||||
|
||||
|
||||
Pager_object::Pager_object(unsigned long badge)
|
||||
: Thread_base("pager", PF_HANDLER_STACK_SIZE), _badge(badge)
|
||||
{
|
||||
@ -290,40 +311,24 @@ Pager_object::Pager_object(unsigned long badge)
|
||||
|
||||
Pager_object::~Pager_object()
|
||||
{
|
||||
/*
|
||||
* Revoke all portals of Pager_object from others.
|
||||
* The portals will be finally revoked during thread destruction.
|
||||
*/
|
||||
revoke(Obj_crd(exc_pt_sel(), NUM_INITIAL_PT_LOG2), false);
|
||||
|
||||
/* revoke semaphore cap to signal valid state after recall */
|
||||
addr_t sm_cap = _sm_state_notify;
|
||||
addr_t sm_cap = _sm_state_notify;
|
||||
_sm_state_notify = Native_thread::INVALID_INDEX;
|
||||
/* wake up client blocked in a thread::pause call */
|
||||
|
||||
/* if pager is blocked wake him up */
|
||||
sm_ctrl(sm_cap, SEMAPHORE_UP);
|
||||
revoke(Obj_crd(sm_cap, 0));
|
||||
|
||||
/* if pager is blocked wake him up */
|
||||
wake_up();
|
||||
|
||||
/*
|
||||
* Make sure nobody is in the handler anymore by doing an IPC to a
|
||||
* local cap pointing to same serving thread (if not running in the
|
||||
* context of the serving thread). When the call returns
|
||||
* we know that nobody is handled by this object anymore, because
|
||||
* all remotely available portals had been revoked beforehand.
|
||||
*/
|
||||
Utcb *utcb = (Utcb *)Thread_base::myself()->utcb();
|
||||
if (reinterpret_cast<Utcb *>(&_context->utcb) != utcb) {
|
||||
utcb->set_msg_word(0);
|
||||
if (uint8_t res = call(_pt_cleanup))
|
||||
PERR("failure - cleanup call failed res=%d", res);
|
||||
}
|
||||
/* take care nobody is handled anymore by this object */
|
||||
cleanup_call();
|
||||
|
||||
/* revoke portal used for the cleanup call */
|
||||
revoke(Obj_crd(_pt_cleanup, 0));
|
||||
|
||||
Native_capability pager_obj = ::Object_pool<Pager_object>::Entry::cap();
|
||||
cap_selector_allocator()->free(_pt_cleanup, 0);
|
||||
cap_selector_allocator()->free(sm_cap, 0);
|
||||
cap_selector_allocator()->free(pager_obj.local_name(), 0);
|
||||
}
|
||||
|
||||
|
||||
@ -346,17 +351,15 @@ Pager_capability Pager_entrypoint::manage(Pager_object *obj)
|
||||
|
||||
void Pager_entrypoint::dissolve(Pager_object *obj)
|
||||
{
|
||||
Native_capability pager_obj = obj->Object_pool<Pager_object>::Entry::cap();
|
||||
|
||||
/* cleanup at cap session */
|
||||
_cap_session->free(obj->Object_pool<Pager_object>::Entry::cap());
|
||||
_cap_session->free(pager_obj);
|
||||
|
||||
/* cleanup locally */
|
||||
Native_capability pager_pt =
|
||||
obj->Object_pool<Pager_object>::Entry::cap();
|
||||
|
||||
revoke(pager_pt.dst(), true);
|
||||
|
||||
cap_selector_allocator()->free(pager_pt.local_name(), 0);
|
||||
revoke(pager_obj.dst(), true);
|
||||
|
||||
remove_locked(obj);
|
||||
obj->cleanup_call();
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user