[NFM] Prevent dispatch of SBI events to NF instance FSMs finalized by SIGTERM (#3938)

In state_operational, guard against dispatching to NF instance FSMs whose
state has been reset to zero by ogs_fsm_fini() in event_termination(). Drop any
incoming SBI events for those instances and log an error, preventing assertion
failures when late HTTP callbacks arrive after an asynchronous SIGTERM shutdown.
This commit is contained in:
Sukchan Lee 2025-06-27 17:12:49 +09:00
parent db0196cba7
commit 53e9e059ed
10 changed files with 259 additions and 31 deletions

View file

@ -332,10 +332,33 @@ void amf_state_operational(ogs_fsm_t *s, amf_event_t *e)
CASE(OGS_SBI_RESOURCE_NAME_NF_INSTANCES)
nf_instance = e->h.sbi.data;
ogs_assert(nf_instance);
ogs_assert(OGS_FSM_STATE(&nf_instance->sm));
e->h.sbi.message = &sbi_message;
ogs_fsm_dispatch(&nf_instance->sm, e);
/*
* Guard against dispatching to an FSM that may have been finalized
* by an asynchronous shutdown triggered by SIGTERM.
*
* In init.cs event_termination(), which can be invoked asynchronously
* when the process receives SIGTERM, we iterate over all NF instances:
* ogs_list_for_each(&ogs_sbi_self()->nf_instance_list, nf_instance)
* ogs_sbi_nf_fsm_fini(nf_instance);
* and call ogs_fsm_fini() on each instances FSM. That finalizes the FSM
* and its state is reset to zero.
*
* After event_termination(), any incoming SBI responsesuch as an NRF
* client callback arriving after deregistrationwould otherwise be
* dispatched into a dead FSM and trigger an assertion failure.
*
* To avoid this, we check OGS_FSM_STATE(&nf_instance->sm):
* - If non-zero, the FSM is still active and can safely handle the event.
* - If zero, the FSM has already been finalized by event_termination(),
* so we log and drop the event to allow graceful shutdown.
*/
if (OGS_FSM_STATE(&nf_instance->sm)) {
e->h.sbi.message = &sbi_message;
ogs_fsm_dispatch(&nf_instance->sm, e);
} else
ogs_error("NF instance FSM has been finalized");
break;
CASE(OGS_SBI_RESOURCE_NAME_SUBSCRIPTIONS)

View file

@ -215,10 +215,33 @@ void ausf_state_operational(ogs_fsm_t *s, ausf_event_t *e)
CASE(OGS_SBI_RESOURCE_NAME_NF_INSTANCES)
nf_instance = e->h.sbi.data;
ogs_assert(nf_instance);
ogs_assert(OGS_FSM_STATE(&nf_instance->sm));
e->h.sbi.message = &message;
ogs_fsm_dispatch(&nf_instance->sm, e);
/*
* Guard against dispatching to an FSM that may have been finalized
* by an asynchronous shutdown triggered by SIGTERM.
*
* In init.cs event_termination(), which can be invoked asynchronously
* when the process receives SIGTERM, we iterate over all NF instances:
* ogs_list_for_each(&ogs_sbi_self()->nf_instance_list, nf_instance)
* ogs_sbi_nf_fsm_fini(nf_instance);
* and call ogs_fsm_fini() on each instances FSM. That finalizes the FSM
* and its state is reset to zero.
*
* After event_termination(), any incoming SBI responsesuch as an NRF
* client callback arriving after deregistrationwould otherwise be
* dispatched into a dead FSM and trigger an assertion failure.
*
* To avoid this, we check OGS_FSM_STATE(&nf_instance->sm):
* - If non-zero, the FSM is still active and can safely handle the event.
* - If zero, the FSM has already been finalized by event_termination(),
* so we log and drop the event to allow graceful shutdown.
*/
if (OGS_FSM_STATE(&nf_instance->sm)) {
e->h.sbi.message = &message;
ogs_fsm_dispatch(&nf_instance->sm, e);
} else
ogs_error("NF instance FSM has been finalized");
break;
CASE(OGS_SBI_RESOURCE_NAME_SUBSCRIPTIONS)

View file

@ -234,10 +234,33 @@ void bsf_state_operational(ogs_fsm_t *s, bsf_event_t *e)
CASE(OGS_SBI_RESOURCE_NAME_NF_INSTANCES)
nf_instance = e->h.sbi.data;
ogs_assert(nf_instance);
ogs_assert(OGS_FSM_STATE(&nf_instance->sm));
e->h.sbi.message = &message;
ogs_fsm_dispatch(&nf_instance->sm, e);
/*
* Guard against dispatching to an FSM that may have been finalized
* by an asynchronous shutdown triggered by SIGTERM.
*
* In init.cs event_termination(), which can be invoked asynchronously
* when the process receives SIGTERM, we iterate over all NF instances:
* ogs_list_for_each(&ogs_sbi_self()->nf_instance_list, nf_instance)
* ogs_sbi_nf_fsm_fini(nf_instance);
* and call ogs_fsm_fini() on each instances FSM. That finalizes the FSM
* and its state is reset to zero.
*
* After event_termination(), any incoming SBI responsesuch as an NRF
* client callback arriving after deregistrationwould otherwise be
* dispatched into a dead FSM and trigger an assertion failure.
*
* To avoid this, we check OGS_FSM_STATE(&nf_instance->sm):
* - If non-zero, the FSM is still active and can safely handle the event.
* - If zero, the FSM has already been finalized by event_termination(),
* so we log and drop the event to allow graceful shutdown.
*/
if (OGS_FSM_STATE(&nf_instance->sm)) {
e->h.sbi.message = &message;
ogs_fsm_dispatch(&nf_instance->sm, e);
} else
ogs_error("NF instance FSM has been finalized");
break;
CASE(OGS_SBI_RESOURCE_NAME_SUBSCRIPTIONS)

View file

@ -205,10 +205,33 @@ void nssf_state_operational(ogs_fsm_t *s, nssf_event_t *e)
CASE(OGS_SBI_RESOURCE_NAME_NF_INSTANCES)
nf_instance = e->h.sbi.data;
ogs_assert(nf_instance);
ogs_assert(OGS_FSM_STATE(&nf_instance->sm));
e->h.sbi.message = &message;
ogs_fsm_dispatch(&nf_instance->sm, e);
/*
* Guard against dispatching to an FSM that may have been finalized
* by an asynchronous shutdown triggered by SIGTERM.
*
* In init.cs event_termination(), which can be invoked asynchronously
* when the process receives SIGTERM, we iterate over all NF instances:
* ogs_list_for_each(&ogs_sbi_self()->nf_instance_list, nf_instance)
* ogs_sbi_nf_fsm_fini(nf_instance);
* and call ogs_fsm_fini() on each instances FSM. That finalizes the FSM
* and its state is reset to zero.
*
* After event_termination(), any incoming SBI responsesuch as an NRF
* client callback arriving after deregistrationwould otherwise be
* dispatched into a dead FSM and trigger an assertion failure.
*
* To avoid this, we check OGS_FSM_STATE(&nf_instance->sm):
* - If non-zero, the FSM is still active and can safely handle the event.
* - If zero, the FSM has already been finalized by event_termination(),
* so we log and drop the event to allow graceful shutdown.
*/
if (OGS_FSM_STATE(&nf_instance->sm)) {
e->h.sbi.message = &message;
ogs_fsm_dispatch(&nf_instance->sm, e);
} else
ogs_error("NF instance FSM has been finalized");
break;
CASE(OGS_SBI_RESOURCE_NAME_SUBSCRIPTIONS)

View file

@ -381,10 +381,33 @@ void pcf_state_operational(ogs_fsm_t *s, pcf_event_t *e)
CASE(OGS_SBI_RESOURCE_NAME_NF_INSTANCES)
nf_instance = e->h.sbi.data;
ogs_assert(nf_instance);
ogs_assert(OGS_FSM_STATE(&nf_instance->sm));
e->h.sbi.message = &message;
ogs_fsm_dispatch(&nf_instance->sm, e);
/*
* Guard against dispatching to an FSM that may have been finalized
* by an asynchronous shutdown triggered by SIGTERM.
*
* In init.cs event_termination(), which can be invoked asynchronously
* when the process receives SIGTERM, we iterate over all NF instances:
* ogs_list_for_each(&ogs_sbi_self()->nf_instance_list, nf_instance)
* ogs_sbi_nf_fsm_fini(nf_instance);
* and call ogs_fsm_fini() on each instances FSM. That finalizes the FSM
* and its state is reset to zero.
*
* After event_termination(), any incoming SBI responsesuch as an NRF
* client callback arriving after deregistrationwould otherwise be
* dispatched into a dead FSM and trigger an assertion failure.
*
* To avoid this, we check OGS_FSM_STATE(&nf_instance->sm):
* - If non-zero, the FSM is still active and can safely handle the event.
* - If zero, the FSM has already been finalized by event_termination(),
* so we log and drop the event to allow graceful shutdown.
*/
if (OGS_FSM_STATE(&nf_instance->sm)) {
e->h.sbi.message = &message;
ogs_fsm_dispatch(&nf_instance->sm, e);
} else
ogs_error("NF instance FSM has been finalized");
break;
CASE(OGS_SBI_RESOURCE_NAME_SUBSCRIPTIONS)

View file

@ -167,10 +167,33 @@ void scp_state_operational(ogs_fsm_t *s, scp_event_t *e)
CASE(OGS_SBI_RESOURCE_NAME_NF_INSTANCES)
nf_instance = e->h.sbi.data;
ogs_assert(nf_instance);
ogs_assert(OGS_FSM_STATE(&nf_instance->sm));
e->h.sbi.message = &message;
ogs_fsm_dispatch(&nf_instance->sm, e);
/*
* Guard against dispatching to an FSM that may have been finalized
* by an asynchronous shutdown triggered by SIGTERM.
*
* In init.cs event_termination(), which can be invoked asynchronously
* when the process receives SIGTERM, we iterate over all NF instances:
* ogs_list_for_each(&ogs_sbi_self()->nf_instance_list, nf_instance)
* ogs_sbi_nf_fsm_fini(nf_instance);
* and call ogs_fsm_fini() on each instances FSM. That finalizes the FSM
* and its state is reset to zero.
*
* After event_termination(), any incoming SBI responsesuch as an NRF
* client callback arriving after deregistrationwould otherwise be
* dispatched into a dead FSM and trigger an assertion failure.
*
* To avoid this, we check OGS_FSM_STATE(&nf_instance->sm):
* - If non-zero, the FSM is still active and can safely handle the event.
* - If zero, the FSM has already been finalized by event_termination(),
* so we log and drop the event to allow graceful shutdown.
*/
if (OGS_FSM_STATE(&nf_instance->sm)) {
e->h.sbi.message = &message;
ogs_fsm_dispatch(&nf_instance->sm, e);
} else
ogs_error("NF instance FSM has been finalized");
break;
CASE(OGS_SBI_RESOURCE_NAME_SUBSCRIPTIONS)

View file

@ -241,11 +241,32 @@ void sepp_state_operational(ogs_fsm_t *s, sepp_event_t *e)
nf_instance = e->h.sbi.data;
ogs_assert(nf_instance);
/*
* Guard against dispatching to an FSM that may have been finalized
* by an asynchronous shutdown triggered by SIGTERM.
*
* In init.cs event_termination(), which can be invoked asynchronously
* when the process receives SIGTERM, we iterate over all NF instances:
* ogs_list_for_each(&ogs_sbi_self()->nf_instance_list, nf_instance)
* ogs_sbi_nf_fsm_fini(nf_instance);
* and call ogs_fsm_fini() on each instances FSM. That finalizes the FSM
* and its state is reset to zero.
*
* After event_termination(), any incoming SBI responsesuch as an NRF
* client callback arriving after deregistrationwould otherwise be
* dispatched into a dead FSM and trigger an assertion failure.
*
* To avoid this, we check OGS_FSM_STATE(&nf_instance->sm):
* - If non-zero, the FSM is still active and can safely handle the event.
* - If zero, the FSM has already been finalized by event_termination(),
* so we log and drop the event to allow graceful shutdown.
*/
old_state = OGS_FSM_STATE(&nf_instance->sm);
ogs_assert(old_state);
e->h.sbi.message = &message;
ogs_fsm_dispatch(&nf_instance->sm, e);
if (old_state) {
e->h.sbi.message = &message;
ogs_fsm_dispatch(&nf_instance->sm, e);
} else
ogs_error("NF instance FSM has been finalized");
/*
* The SEPP on the H-PLMN should send a n32c-handshake message

View file

@ -711,10 +711,33 @@ void smf_state_operational(ogs_fsm_t *s, smf_event_t *e)
CASE(OGS_SBI_RESOURCE_NAME_NF_INSTANCES)
nf_instance = e->h.sbi.data;
ogs_assert(nf_instance);
ogs_assert(OGS_FSM_STATE(&nf_instance->sm));
e->h.sbi.message = &sbi_message;
ogs_fsm_dispatch(&nf_instance->sm, e);
/*
* Guard against dispatching to an FSM that may have been finalized
* by an asynchronous shutdown triggered by SIGTERM.
*
* In init.cs event_termination(), which can be invoked asynchronously
* when the process receives SIGTERM, we iterate over all NF instances:
* ogs_list_for_each(&ogs_sbi_self()->nf_instance_list, nf_instance)
* ogs_sbi_nf_fsm_fini(nf_instance);
* and call ogs_fsm_fini() on each instances FSM. That finalizes the FSM
* and its state is reset to zero.
*
* After event_termination(), any incoming SBI responsesuch as an NRF
* client callback arriving after deregistrationwould otherwise be
* dispatched into a dead FSM and trigger an assertion failure.
*
* To avoid this, we check OGS_FSM_STATE(&nf_instance->sm):
* - If non-zero, the FSM is still active and can safely handle the event.
* - If zero, the FSM has already been finalized by event_termination(),
* so we log and drop the event to allow graceful shutdown.
*/
if (OGS_FSM_STATE(&nf_instance->sm)) {
e->h.sbi.message = &sbi_message;
ogs_fsm_dispatch(&nf_instance->sm, e);
} else
ogs_error("NF instance FSM has been finalized");
break;
CASE(OGS_SBI_RESOURCE_NAME_SUBSCRIPTIONS)

View file

@ -288,10 +288,33 @@ void udm_state_operational(ogs_fsm_t *s, udm_event_t *e)
CASE(OGS_SBI_RESOURCE_NAME_NF_INSTANCES)
nf_instance = e->h.sbi.data;
ogs_assert(nf_instance);
ogs_assert(OGS_FSM_STATE(&nf_instance->sm));
e->h.sbi.message = &message;
ogs_fsm_dispatch(&nf_instance->sm, e);
/*
* Guard against dispatching to an FSM that may have been finalized
* by an asynchronous shutdown triggered by SIGTERM.
*
* In init.cs event_termination(), which can be invoked asynchronously
* when the process receives SIGTERM, we iterate over all NF instances:
* ogs_list_for_each(&ogs_sbi_self()->nf_instance_list, nf_instance)
* ogs_sbi_nf_fsm_fini(nf_instance);
* and call ogs_fsm_fini() on each instances FSM. That finalizes the FSM
* and its state is reset to zero.
*
* After event_termination(), any incoming SBI responsesuch as an NRF
* client callback arriving after deregistrationwould otherwise be
* dispatched into a dead FSM and trigger an assertion failure.
*
* To avoid this, we check OGS_FSM_STATE(&nf_instance->sm):
* - If non-zero, the FSM is still active and can safely handle the event.
* - If zero, the FSM has already been finalized by event_termination(),
* so we log and drop the event to allow graceful shutdown.
*/
if (OGS_FSM_STATE(&nf_instance->sm)) {
e->h.sbi.message = &message;
ogs_fsm_dispatch(&nf_instance->sm, e);
} else
ogs_error("NF instance FSM has been finalized");
break;
CASE(OGS_SBI_RESOURCE_NAME_SUBSCRIPTIONS)

View file

@ -225,10 +225,33 @@ void udr_state_operational(ogs_fsm_t *s, udr_event_t *e)
CASE(OGS_SBI_RESOURCE_NAME_NF_INSTANCES)
nf_instance = e->h.sbi.data;
ogs_assert(nf_instance);
ogs_assert(OGS_FSM_STATE(&nf_instance->sm));
e->h.sbi.message = &message;
ogs_fsm_dispatch(&nf_instance->sm, e);
/*
* Guard against dispatching to an FSM that may have been finalized
* by an asynchronous shutdown triggered by SIGTERM.
*
* In init.cs event_termination(), which can be invoked asynchronously
* when the process receives SIGTERM, we iterate over all NF instances:
* ogs_list_for_each(&ogs_sbi_self()->nf_instance_list, nf_instance)
* ogs_sbi_nf_fsm_fini(nf_instance);
* and call ogs_fsm_fini() on each instances FSM. That finalizes the FSM
* and its state is reset to zero.
*
* After event_termination(), any incoming SBI responsesuch as an NRF
* client callback arriving after deregistrationwould otherwise be
* dispatched into a dead FSM and trigger an assertion failure.
*
* To avoid this, we check OGS_FSM_STATE(&nf_instance->sm):
* - If non-zero, the FSM is still active and can safely handle the event.
* - If zero, the FSM has already been finalized by event_termination(),
* so we log and drop the event to allow graceful shutdown.
*/
if (OGS_FSM_STATE(&nf_instance->sm)) {
e->h.sbi.message = &message;
ogs_fsm_dispatch(&nf_instance->sm, e);
} else
ogs_error("NF instance FSM has been finalized");
break;
CASE(OGS_SBI_RESOURCE_NAME_SUBSCRIPTIONS)