Use-after-free on freeing FSM

Functions associated with a state transition are called with the FSM
mutex unlocked and this gave dds_security_fsm_free a chance of freeing
the memory while the FSM thread was still accessing the state.

This marks the FSM as "busy" while the mutex is unlocked for the
invoking of this functions and waits with freeing the memory until it is
no longer busy.

Signed-off-by: Erik Boasson <eb@ilities.com>
This commit is contained in:
Erik Boasson 2020-07-26 16:56:20 +02:00 committed by eboasson
parent 79773729d6
commit 39d55550ce

View file

@ -50,6 +50,7 @@ struct dds_security_fsm
struct dds_security_fsm *next_fsm;
struct dds_security_fsm *prev_fsm;
bool deleting;
bool busy;
struct dds_security_fsm_control *control;
const dds_security_fsm_transition *transitions;
uint32_t size;
@ -245,6 +246,7 @@ static void fsm_state_change (struct dds_security_fsm_control *control, struct f
fsm->current = fsm->transitions[i].end;
set_state_timer (fsm);
fsm->busy = true;
ddsrt_mutex_unlock (&control->lock);
if (fsm->transitions[i].func)
@ -253,6 +255,8 @@ static void fsm_state_change (struct dds_security_fsm_control *control, struct f
fsm->current->func (fsm, fsm->arg);
ddsrt_mutex_lock (&control->lock);
fsm->busy = false;
if (!fsm->deleting)
fsm_check_auto_state_change (fsm);
else
@ -447,6 +451,7 @@ struct dds_security_fsm * dds_security_fsm_create (struct dds_security_fsm_contr
fsm->overall_timeout_event.endtime = DDS_NEVER;
fsm->overall_timeout_event.fsm = fsm;
fsm->deleting = false;
fsm->busy = false;
fsm->next_fsm = NULL;
fsm->prev_fsm = NULL;
fsm->control = control;
@ -490,6 +495,8 @@ static void fsm_delete (struct dds_security_fsm_control *control, struct dds_sec
{
remove_fsm_from_list (control, fsm);
fsm_deactivate (control, fsm);
while (fsm->busy)
ddsrt_cond_wait (&control->cond, &control->lock);
ddsrt_free(fsm);
}