Skip to content

fix(producer): guard against nil EventChannel in N1N2 handlers#663

Merged
gab-arrobo merged 2 commits into
omec-project:mainfrom
donivtech:fix/n1n2-nil-eventchannel-cm-idle
Apr 22, 2026
Merged

fix(producer): guard against nil EventChannel in N1N2 handlers#663
gab-arrobo merged 2 commits into
omec-project:mainfrom
donivtech:fix/n1n2-nil-eventchannel-cm-idle

Conversation

@donivtech

Copy link
Copy Markdown
Contributor

Issue

AMF panics on nil-pointer dereference when the SMF sends an N1N2MessageTransfer request for a UE whose context was restored from MongoDB. Reachable from any deployment where the receiving AMF is not the same process that served the UE's initial NAS attach (HA, multi-replica, pod restart, crash recovery).

Panic site: producer/n1n2message.go:71

ue.EventChannel.UpdateSbiHandler(ProducerHandler)  // ue.EventChannel == nil

Downstream consequence: the SMF gets HTTP 500, the buffered downlink packet that triggered the paging is dropped, and paging never happens for that UE.

RCA

AmfUe.EventChannel is the per-UE goroutine dispatch mechanism used by the three N1N2 HTTP handlers. It is created on demand by the NGAP dispatcher when a live NGAP message arrives (SetEventChannel).

Two factors combine to produce the bug:

  1. The field is tagged json:"-" — excluded from MongoDB serialisation. When an AmfUe is restored from DB, the field is zero-valued (nil).
  2. The three N1N2 handlers dereference it unconditionally, with no nil guard and no lazy initialisation.

When an AmfUe is restored from MongoDB and an N1N2MessageTransfer arrives before any NGAP activity on that context, EventChannel is nil → panic.

The paging logic inside N1N2MessageTransferProcedure itself is correct — it handles CM-IDLE via CmConnect() check + BuildPaging / SendPaging. The bug is purely in the HTTP wrapper that panics before the procedure can run.

Why this is rarely seen in single-pod lab setups

On a single long-running AMF pod, the EventChannel stays alive in memory even after the UE goes CM-IDLE (it is only torn down via ue.Remove()). So the bug is invisible until the AMF is recreated — which is exactly when any real deployment (k8s, HA) is exposed.

Fix

Add a nil-guard at the top of each N1N2 handler. When EventChannel is nil, bypass the event-channel dispatch and call the underlying procedure directly. The procedures are already written to handle CM-IDLE, so no new paging logic is introduced. Each fallback logs a WARN for operator visibility.

Three handlers patched in producer/n1n2message.go:

Handler Fallback call
HandleN1N2MessageTransferRequest N1N2MessageTransferProcedure
HandleN1N2MessageTransferStatusRequest N1N2MessageTransferStatusProcedure
HandleN1N2MessageSubscirbeRequest N1N2MessageSubscribeProcedure

48 lines added, 0 removed. Happy path (EventChannel non-nil) is unchanged.

Scope / risk

  • No behavioural change on the happy path — existing code runs unchanged.
  • On the fallback path: same procedure, same arguments as the event-channel path would have invoked.
  • New observable: WARN log lines when the fallback fires. Expected after any AMF restart with persisted UE contexts; not a new problem indicator.
  • Known limitation (out of scope): this does not lazily re-initialise EventChannel. A deeper refactor to reconstruct it on deserialisation, or to remove the channel from the HTTP critical path, would be a separate PR.

@donivtech
donivtech requested a review from a team April 20, 2026 09:50
@gab-arrobo
gab-arrobo force-pushed the fix/n1n2-nil-eventchannel-cm-idle branch from 1e12ced to 97801ab Compare April 20, 2026 15:41
@gab-arrobo
gab-arrobo requested a review from Copilot April 20, 2026 15:41

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR prevents AMF panics when N1N2 SBI requests arrive for a UE whose context was restored from DB and therefore has a nil AmfUe.EventChannel, by adding a direct-procedure fallback in the N1N2 HTTP handlers.

Changes:

  • Add ue.EventChannel == nil guards in the three N1N2 handlers.
  • When nil, bypass event-channel dispatch and invoke the underlying procedure directly, with WARN logging.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread producer/n1n2message.go
Comment thread producer/n1n2message.go
@gab-arrobo
gab-arrobo force-pushed the fix/n1n2-nil-eventchannel-cm-idle branch from 97801ab to 9b95b74 Compare April 20, 2026 16:10
donivtech pushed a commit to donivtech/amf that referenced this pull request Apr 22, 2026
The event-channel path in HandleN1N2MessageSubscirbeRequest declared
and type-asserted msg.RespData to *UeN1N2InfoSubscriptionCreateData
(the request type) while N1N2MessageSubscribeProcedure actually
returns *UeN1N2InfoSubscriptionCreatedData (the response type).

Per TS 29.518, a successful subscribe returns
UeN1N2InfoSubscriptionCreatedData with the allocated subscription ID.
The type assertion would panic on any successful subscribe; this has
not been observed because the endpoint is not exercised in current
deployments.

Bundled into this PR at reviewer request (see PR omec-project#663 review
discussion).

Signed-off-by: Vinod Patmanathan <vinod.patmanathan@forsway.com>
Vinod Patmanathan added 2 commits April 22, 2026 09:28
Adds nil-guards in three N1N2 message handlers so AMF doesn't panic
when an AmfUe is reconstructed from MongoDB (EventChannel is tagged
json:"-" and is only set by the NGAP dispatcher). When EventChannel
is nil, bypass the event-channel dispatch and call the underlying
procedure directly - the procedures already handle CM-IDLE/paging.

Affected handlers in producer/n1n2message.go:
- HandleN1N2MessageTransferRequest
- HandleN1N2MessageTransferStatusRequest
- HandleN1N2MessageSubscirbeRequest

Signed-off-by: Vinod Patmanathan <vinod.patmanathan@forsway.com>
The event-channel path in HandleN1N2MessageSubscirbeRequest declared
and type-asserted msg.RespData to *UeN1N2InfoSubscriptionCreateData
(the request type) while N1N2MessageSubscribeProcedure actually
returns *UeN1N2InfoSubscriptionCreatedData (the response type).

Per TS 29.518, a successful subscribe returns
UeN1N2InfoSubscriptionCreatedData with the allocated subscription ID.
The type assertion would panic on any successful subscribe; this has
not been observed because the endpoint is not exercised in current
deployments.

Bundled into this PR at reviewer request (see PR omec-project#663 review
discussion).

Signed-off-by: Vinod Patmanathan <vinod.patmanathan@forsway.com>
@donivtech
donivtech force-pushed the fix/n1n2-nil-eventchannel-cm-idle branch from f447e44 to f9dace1 Compare April 22, 2026 07:28
@gab-arrobo
gab-arrobo merged commit 0c4fb43 into omec-project:main Apr 22, 2026
12 checks passed
@donivtech
donivtech deleted the fix/n1n2-nil-eventchannel-cm-idle branch June 25, 2026 13:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants