Merge remote-tracking branch 'origin/issue-1043' into integration_2026_w05 (!3860)

NGAP: improve error handling in RRCReconfiguration from Handover Command decoding

This MR refactors doRRCReconfiguration_from_HandoverCommand for consistency and
safety, and enables NGAP Handover Cancel encoding/decoding.
- Refactored doRRCReconfiguration_from_HandoverCommand function:
  - Changed signature to return byte_array_t instead of taking a buffer parameter
  - Prevents buffer overflow by auto-sizing memory with uper_encode_to_new_buffer
  - Input validation
  - HandoverCommand structure validation
  - Error handling and cleanup for encode/decode failures
  - ASN.1 structure cleanup on all error paths
  - Updated caller in rrc_gNB_NGAP.c: use new byte_array_t return signature,
    added buffer cleanup with free_byte_array(), sends Handover Cancel to AMF on
    decoding failure (source gNB)
  - Improved error logging
- NGAP Handover Cancel encoding/decoding support
  - Added missing Handover Cancel ack case handler for
    NGAP_ProcedureCode_id_HandoverCancel in ngap_gNB_decode_initiating_message()
    to decode and log incoming Handover Cancel Ack message
  - Added NGAP_ProcedureCode_id_HandoverCancel to the list of supported
    initiating message procedure codes in ngap_gNB_encode_initiating() allowing
    the gNB to encode and send Handover Cancel messages

Note: The Handover Cancel procedure is already implemented elsewhere in the NGAP
stack; these changes complete support by ensuring the procedure code is
recognized in both encoder and decoder.

Closes #1043
This commit is contained in:
Jaroslava Fiedlerova
2026-01-27 20:30:51 +01:00
5 changed files with 62 additions and 16 deletions
+42 -10
View File
@@ -1350,20 +1350,33 @@ void fill_removal_lists_from_source_measConfig(NR_MeasConfig_t *currentMC, byte_
ASN_STRUCT_FREE(asn_DEF_NR_RRCReconfiguration, rrcReconf);
}
int doRRCReconfiguration_from_HandoverCommand(byte_array_t *ba, const byte_array_t handoverCommand)
byte_array_t doRRCReconfiguration_from_HandoverCommand(const byte_array_t handoverCommand)
{
DevAssert(handoverCommand.buf);
DevAssert(handoverCommand.len > 0);
byte_array_t msg = {.buf = NULL, .len = 0};
// Decode Handover Command
NR_HandoverCommand_t *hoCommand = NULL;
asn_dec_rval_t dec_rval = uper_decode_complete(NULL, &asn_DEF_NR_HandoverCommand, (void **)&hoCommand, handoverCommand.buf, handoverCommand.len);
if (dec_rval.code != RC_OK && dec_rval.consumed == 0) {
LOG_E(NR_RRC, "Can not decode Handover Command!\n");
return -1;
if (dec_rval.code != RC_OK || !hoCommand) {
LOG_E(NR_RRC, "Failed to decode Handover Command (dec_rval.code=%d)!\n", dec_rval.code);
return msg;
}
// Validate HandoverCommand structure
if (hoCommand->criticalExtensions.present != NR_HandoverCommand__criticalExtensions_PR_c1
|| !hoCommand->criticalExtensions.choice.c1
|| hoCommand->criticalExtensions.choice.c1->present != NR_HandoverCommand__criticalExtensions__c1_PR_handoverCommand
|| !hoCommand->criticalExtensions.choice.c1->choice.handoverCommand) {
LOG_E(NR_RRC, "Invalid HandoverCommand in criticalExtensions.choice.c1->choice.handoverCommand\n");
ASN_STRUCT_FREE(asn_DEF_NR_HandoverCommand, hoCommand);
return msg;
}
// Prepare DL DCCH message for RRCReconfiguration
NR_DL_DCCH_Message_t dl_dcch_msg = {0};
asn_enc_rval_t enc_rval;
dl_dcch_msg.message.present = NR_DL_DCCH_MessageType_PR_c1;
asn1cCalloc(dl_dcch_msg.message.choice.c1, c1);
c1->present = NR_DL_DCCH_MessageType__c1_PR_rrcReconfiguration;
@@ -1371,18 +1384,37 @@ int doRRCReconfiguration_from_HandoverCommand(byte_array_t *ba, const byte_array
// Decode RRCReconfiguration from handoverCommandMessage
OCTET_STRING_t *ho = &hoCommand->criticalExtensions.choice.c1->choice.handoverCommand->handoverCommandMessage;
uper_decode_complete(NULL, &asn_DEF_NR_RRCReconfiguration, (void **)&rrcReconf, ho->buf, ho->size);
if (!ho->buf || ho->size == 0) {
LOG_E(NR_RRC, "Invalid handoverCommandMessage OCTET_STRING (buf=%p, size=%zu)!\n", ho->buf, ho->size);
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NR_DL_DCCH_Message, &dl_dcch_msg);
ASN_STRUCT_FREE(asn_DEF_NR_HandoverCommand, hoCommand);
return msg;
}
asn_dec_rval_t rrc_dec_rval = uper_decode_complete(NULL, &asn_DEF_NR_RRCReconfiguration, (void **)&rrcReconf, ho->buf, ho->size);
if (rrc_dec_rval.code != RC_OK || !rrcReconf) {
LOG_E(NR_RRC, "Failed to decode RRCReconfiguration from Handover Command!\n");
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NR_DL_DCCH_Message, &dl_dcch_msg);
ASN_STRUCT_FREE(asn_DEF_NR_HandoverCommand, hoCommand);
return msg;
}
if (LOG_DEBUGFLAG(DEBUG_ASN1))
xer_fprint(stdout, &asn_DEF_NR_DL_DCCH_Message, (void *)&dl_dcch_msg);
// Encode DL DCCH message in buffer
enc_rval = uper_encode_to_buffer(&asn_DEF_NR_DL_DCCH_Message, NULL, &dl_dcch_msg, ba->buf, ba->len);
AssertFatal(enc_rval.encoded > 0, "ASN1 message encoding failed (%s, %lu)!\n", enc_rval.failed_type->name, enc_rval.encoded);
// Encode DL DCCH message to new buffer
int val = uper_encode_to_new_buffer(&asn_DEF_NR_DL_DCCH_Message, NULL, &dl_dcch_msg, (void **)&msg.buf);
if (val <= 0) {
LOG_E(NR_RRC, "Failed to encode RRCReconfiguration from Handover Command!\n");
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NR_DL_DCCH_Message, &dl_dcch_msg);
ASN_STRUCT_FREE(asn_DEF_NR_HandoverCommand, hoCommand);
return msg;
}
msg.len = val;
LOG_D(NR_RRC, "RRCReconfiguration from HandoverCommand: Encoded (%ld bytes)\n", msg.len);
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NR_DL_DCCH_Message, &dl_dcch_msg);
ASN_STRUCT_FREE(asn_DEF_NR_HandoverCommand, hoCommand);
return ((enc_rval.encoded + 7) / 8);
return msg;
}
byte_array_t get_HandoverCommandMessage(nr_rrc_reconfig_param_t *params)
+1 -1
View File
@@ -175,7 +175,7 @@ int do_NR_Paging(uint8_t Mod_id, uint8_t *buffer, uint32_t tmsi);
byte_array_t get_HandoverPreparationInformation(nr_rrc_reconfig_param_t *params, int scell_pci);
byte_array_t get_HandoverCommandMessage(nr_rrc_reconfig_param_t *params);
void fill_removal_lists_from_source_measConfig(NR_MeasConfig_t *measConfig, byte_array_t prep_info);
int doRRCReconfiguration_from_HandoverCommand(byte_array_t *ba, byte_array_t handoverCommand);
byte_array_t doRRCReconfiguration_from_HandoverCommand(byte_array_t handoverCommand);
struct NR_UE_NR_Capability *get_ue_nr_capability(int rnti, uint8_t *buf, uint32_t len);
NR_UE_NR_Capability_t *decode_nr_ue_capability(int rnti, const NR_UE_CapabilityRAT_ContainerList_t *clist);
+12 -5
View File
@@ -1276,13 +1276,20 @@ void rrc_gNB_process_HandoverCommand(gNB_RRC_INST *rrc, const ngap_handover_comm
}
gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
uint8_t buffer[NR_RRC_BUF_SIZE];
byte_array_t ba = {.buf = buffer, .len = sizeof(buffer)};
int enc = doRRCReconfiguration_from_HandoverCommand(&ba, msg->handoverCommand);
DevAssert(enc > 0);
byte_array_t buffer = doRRCReconfiguration_from_HandoverCommand(msg->handoverCommand);
if (!buffer.buf || buffer.len == 0) {
LOG_E(NR_RRC, "Failed to decode/encode RRCReconfiguration from HandoverCommand\n");
// Notify AMF that handover was cancelled
DevAssert(UE->ho_context);
DevAssert(UE->ho_context->source);
DevAssert(UE->ho_context->source->ho_cancel);
UE->ho_context->source->ho_cancel(rrc, UE);
return;
}
rrc_gNB_trigger_reconfiguration_for_handover(rrc, UE, buffer, enc);
rrc_gNB_trigger_reconfiguration_for_handover(rrc, UE, buffer.buf, buffer.len);
LOG_A(NR_RRC, "Send reconfiguration (HO Command) to UE %u/RNTI %04x\n", UE->rrc_ue_id, UE->rnti);
free_byte_array(buffer);
}
void rrc_gNB_free_Handover_Command(ngap_handover_command_t *msg)
+6
View File
@@ -76,6 +76,12 @@ static int ngap_gNB_decode_initiating_message(NGAP_NGAP_PDU_t *pdu) {
NGAP_INFO("Handover Preparation initiating message\n");
break;
case NGAP_ProcedureCode_id_HandoverCancel:
res = asn_encode_to_new_buffer(NULL, ATS_CANONICAL_XER, &asn_DEF_NGAP_NGAP_PDU, pdu);
free(res.buffer);
NGAP_INFO("Handover Cancel initiating message\n");
break;
case NGAP_ProcedureCode_id_PDUSessionResourceModify:
res = asn_encode_to_new_buffer(NULL, ATS_CANONICAL_XER, &asn_DEF_NGAP_NGAP_PDU, pdu);
free(res.buffer);
+1
View File
@@ -55,6 +55,7 @@ static inline int ngap_gNB_encode_initiating(NGAP_NGAP_PDU_t *pdu, uint8_t **buf
NGAP_ProcedureCode_id_PDUSessionResourceModifyIndication,
NGAP_ProcedureCode_id_UplinkRANStatusTransfer,
NGAP_ProcedureCode_id_HandoverPreparation,
NGAP_ProcedureCode_id_HandoverCancel,
NGAP_ProcedureCode_id_HandoverNotification};
int i;
for (i = 0; i < sizeofArray(tmp); i++)