RRC: improve error handling in doRRCReconfiguration_from_HandoverCommand

- Refactor function to return byte_array_t instead of taking buffer
  parameter for consistency with other encoding functions
  (do_RRCReconfiguration, do_HO_RRCReconfiguration) and to prevent buffer
  overflow issues since the memory size is determined automatically

- Add comprehensive error checking:
  * Input validation
  * Check return of each decode (HandoverCommand
    and RRCReconfiguration)
  * Validate HandoverCommand structure before access
  * Validate OCTET_STRING before decoding RRCReconfiguration
  * Error handling and cleanup for enc/dec failures

- Update caller in rrc_gNB_NGAP.c
  * use new signature
  * free buffer
  * send HO Cancel on decoding failure (source gNB)

Closes #1043
This commit is contained in:
Guido Casati
2026-01-14 17:05:35 +01:00
parent f6d23422aa
commit d613cf7b0b
3 changed files with 55 additions and 16 deletions
+42 -10
View File
@@ -1318,20 +1318,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;
@@ -1339,18 +1352,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
@@ -171,7 +171,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
@@ -1275,13 +1275,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)