base: fix child destruction while close requested

This patch fixes a corner case where a child is destructed while a
asynchronous close request to a sibling server is still pending.

The child immediately discarded the session ID as the end of the
close-session processing, assuming that this ID is never to be needed
again. The session-state continues to exist to handle asynchrous close
protocol with the server.

However, if the child is destructed at this point (before the server
responded to the session request), the destruction of the child would
not cover the discharging of the session state because the session state
was no longer be part of the client's ID space. So once the asynchronous
close response from the server came in, the session state contained
stale information, in particular a stale closed_callback pointer.

The patch fixes the problem by deferring the discarding of the client ID
to the point where the session state is actually destructed. So the
session of a pending close response is covered by the child destructor.

Thanks to Pirmin Duss for reporting this issue along with a test
scenario for reproducing it!

Fixes #4039
This commit is contained in:
Norman Feske
2021-03-08 17:14:09 +01:00
parent 755aed7cb2
commit 935bb36fe4
3 changed files with 131 additions and 16 deletions

View File

@@ -467,7 +467,6 @@ Child::Close_result Child::_close(Session_state &session)
_policy.session_state_changed();
session.discard_id_at_client();
session.service().wakeup();
return CLOSE_PENDING;
@@ -926,7 +925,11 @@ void Child::close_all_sessions()
auto close_fn = [&] (Session_state &session) {
session.closed_callback = nullptr;
session.ready_callback = nullptr;
(void)_close(session);
Close_result const close_result = _close(session);
if (close_result == CLOSE_PENDING)
session.discard_id_at_client();
};
while (_id_space.apply_any<Session_state>(close_fn));