fix(ngap): add nil guards for missing mandatory NGAP-ID IEs in handlers#731
Conversation
There was a problem hiding this comment.
Pull request overview
This PR hardens several NGAP handlers against malformed/non-conformant peers by preventing nil-pointer dereferences when mandatory IEs are absent from ProtocolIEs, avoiding AMF panics/crashes during message handling.
Changes:
HandleInitialUEMessage: return immediately after sendingErrorIndicationfor reject/missing IEs; add explicit guard for missingRANUENGAPIDbefore UE lookup/creation.HandleInitialContextSetupResponse/HandleInitialContextSetupFailure: add explicit guard for missingRANUENGAPIDbefore UE lookup.HandleUEContextReleaseRequest: make AMF/RAN UE ID lookup nil-safe and remove a nil-deref from the “no context” error log path.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
0d4080d to
6ddd2f0
Compare
|
Good catch. Fixed in c689af4. Added a post-loop nASPDU == nil guard in HandleUplinkNasTransport, HandleInitialUEMessage, and HandleNasNonDeliveryIndication. I also swept all the handlers touched in this PR for any other post-loop IE dereference and found none remaining. |
c689af4 to
343ba49
Compare
gab-arrobo
left a comment
There was a problem hiding this comment.
@donivtech, thanks for your contribution. Please see a few comments below but those comments are applicable across the proposed changes. Let me know what you think. Thanks!
|
Switched these to the helpers as suggested (44fe599): findRanUeByRanNgapID / findRanUeByAmfNgapID for the lookups in UplinkNasTransport, UEContextReleaseComplete, PDUSessionResourceReleaseResponse, InitialContextSetupResponse/Failure, HandoverCancel, NasNonDeliveryIndication, and UERadioCapabilityInfoIndication. In each of these the No UE Context[…: %d] log right after the lookup dereferences the same ID, and with the helper that line is now reached when the ID is nil — so I wrapped it in if id != nil to stay panic-safe (the helper already logs the nil-ID case). Happy to instead expand the helpers to take a caller label (your "from X" idea) if you'd prefer that over the generic nil-ID log - just say |
Hi @donivtech, if rANUENGAPID == nil {
ran.Log.Errorln("RANUENGAPID IE missing from UplinkNasTransport")
return
}I think a better approach would be to keep the following check(s) and remove the if statements from inside if rANUENGAPID == nil {
ran.Log.Errorln("RANUENGAPID IE missing from UplinkNasTransport")
return
}That is, something like this in all places where diff --git a/ngap/handler.go b/ngap/handler.go
index ed83f62..4b7599e 100644
--- a/ngap/handler.go
+++ b/ngap/handler.go
@@ -789,6 +789,11 @@ func HandleUplinkNasTransport(ctx ctxt.Context, ran *context.AmfRan, message *ng
}
}
+ if rANUENGAPID == nil {
+ ran.Log.Errorln("RANUENGAPID IE missing from UplinkNasTransport")
+ return
+ }
+
if aMFUENGAPID == nil {
ran.Log.Errorln("AMFUENGAPID IE missing from UplinkNasTransport")
return
@@ -800,9 +805,7 @@ func HandleUplinkNasTransport(ctx ctxt.Context, ran *context.AmfRan, message *ng
ranUe := findRanUeByRanNgapID(ran, rANUENGAPID)
if ranUe == nil {
- if rANUENGAPID != nil {
- ran.Log.Errorf("No UE Context[RanUeNgapID: %d]", rANUENGAPID.Value)
- }
+ ran.Log.Errorf("No UE Context[RanUeNgapID: %d]", rANUENGAPID.Value)
return
}
Thanks! |
44fe599 to
194d1af
Compare
|
Yes, that is better— applied your suggestion in the latest push. Kept the explicit if id == nil { … return } guard before each lookup and removed the if id != nil wrapper from inside if ranUe == nil, while keeping the findRanUeBy*NgapID helper for the lookup itself. So it reads guard → helper → clean No UE Context[…: %d] log (the guard guarantees the ID is non-nil by that point). Also merged latest main in. Applied across all eight: UplinkNasTransport, UEContextReleaseComplete, PDUSessionResourceReleaseResponse, InitialContextSetupResponse/Failure, HandoverCancel, NasNonDeliveryIndication, UERadioCapabilityInfoIndication. |
194d1af to
ce4f60b
Compare
|
Good point , done. HandleUEContextReleaseRequest now uses findRanUeByAmfNgapID(ran, aMFUENGAPID) for the primary lookup (and findRanUeByRanNgapID(ran, rANUENGAPID) for the RAN-ID fallback), dropping the != nil wrappers since the helpers nil-check internally. |
Several NGAP handlers dereference a mandatory NGAP-ID IE's .Value immediately after the IE-decoding loop. The in-loop checks only catch "IE present but value nil"; an IE entirely absent from the ProtocolIEs list leaves the pointer nil, so the post-loop dereference (and the "No UE Context[... %d]" error log that follows it) panics. Add a post-loop nil guard before the first dereference in each affected handler. Note the findRanUeBy*NgapID helper alone is not sufficient here: the following "No UE Context" log re-derefs the same ID and is reached exactly when it is nil, so the guard must return before the lookup. Handlers guarded: - HandleInitialUEMessage (also return after the criticality-diagnostics ErrorIndication) - HandleInitialContextSetupResponse - HandleInitialContextSetupFailure - HandleUEContextReleaseRequest (nil-safe AMFUENGAPID/RANUENGAPID lookup + drop the nil-deref from its "no context" log) - HandleUplinkNasTransport - HandleUEContextReleaseComplete - HandlePDUSessionResourceReleaseResponse - HandleHandoverCancel - HandleNasNonDeliveryIndication - HandleUERadioCapabilityInfoIndication Signed-off-by: Vinod Patmanathan <vinod.patmanathan@forsway.com>
Address review feedback on the handler nil-guards: - HandleUplinkNasTransport and HandleHandoverCancel also dereference aMFUENGAPID.Value later (in a "No UE Context" log and an AmfUeNgapId conflict check respectively), which the RANUENGAPID guard alone did not cover. Add a symmetric AMFUENGAPID nil guard so an absent AMFUENGAPID IE can't trigger a nil-pointer panic. - HandleUEContextReleaseRequest: instead of a generic message, log whichever of AMFUENGAPID / RANUENGAPID is present (the dual lookup may have either), keeping the identifiers available for debugging without dereferencing a nil pointer. Signed-off-by: Vinod Patmanathan <vinod.patmanathan@forsway.com>
Address review feedback: HandleUplinkNasTransport, HandleInitialUEMessage and HandleNasNonDeliveryIndication pass nASPDU.Value to nas.HandleNAS() after the IE loop. The in-loop check only catches a present-but-nil NASPDU; an entirely-absent NASPDU IE leaves nASPDU nil and panics there. Add a post-loop NASPDU nil guard in each (NASPDU is a mandatory IE in these messages per TS 38.413). Signed-off-by: Vinod Patmanathan <vinod.patmanathan@forsway.com>
Per review: keep the explicit `if id == nil { return }` guard before the
lookup (so the ID is guaranteed non-nil for the following "No UE Context"
log) and use the findRanUeByRanNgapID / findRanUeByAmfNgapID helpers for
the lookup itself. This removes the `if id != nil` wrappers from inside
`if ranUe == nil` used in the previous revision while staying panic-safe.
Handlers: HandleUplinkNasTransport, HandleUEContextReleaseComplete,
HandlePDUSessionResourceReleaseResponse, HandleInitialContextSetupResponse,
HandleInitialContextSetupFailure, HandleHandoverCancel,
HandleNasNonDeliveryIndication, HandleUERadioCapabilityInfoIndication.
Signed-off-by: Vinod Patmanathan <vinod.patmanathan@forsway.com>
Per review: route the AMF/RAN UE lookup in HandleUEContextReleaseRequest through findRanUeByAmfNgapID / findRanUeByRanNgapID instead of the raw context.AMF_Self().RanUeFindByAmfUeNgapID / ran.RanUeFindByRanUeNgapID calls with explicit `!= nil` wrappers. The helpers already nil-check the IDs, and the `if ranUe == nil` conditional log below remains nil-safe. Signed-off-by: Vinod Patmanathan <vinod.patmanathan@forsway.com>
fc76f7b to
7f127f3
Compare
Description
Several NGAP handlers dereference a mandatory NGAP-ID IE's
.Valueright after the IE-decoding loop. The in-loop checks only catch the "IE present but value is nil" case — an IE that is entirely absent from theProtocolIEslist leaves the pointernil, so a malformed/non-conformant peer can panic the AMF by omitting it.The panic occurs in two places per handler: the lookup
RanUeFind…(id.Value), and theNo UE Context[…: %d]error log immediately after it (which re-derefs the same ID and is reached precisely when the lookup returns nil).This adds a post-loop
if <id> == nil { return }guard before the first dereference in each affected handler.Handlers guarded (10)
HandleInitialUEMessage(alsoreturnafter the criticality-diagnosticsErrorIndication)HandleInitialContextSetupResponseHandleInitialContextSetupFailureHandleUEContextReleaseRequest(nil-safe dual AMFUENGAPID/RANUENGAPID lookup + drop nil-deref from its log)HandleUplinkNasTransportHandleUEContextReleaseCompleteHandlePDUSessionResourceReleaseResponseHandleHandoverCancelHandleNasNonDeliveryIndicationHandleUERadioCapabilityInfoIndicationThe remaining handlers already guard their mandatory IDs (post-loop
== nilguard, inlineif id != nilwrapper, or the criticality-diagnosticsif len > 0 { return }pattern), so they're left unchanged.Testing
go build ./...,go vet ./...,go test ./...all passgolangci-lint run --new-from-rev=main→0 issues.gofmtclean