Skip to content

IMEI odd/even indication bit uninitialized in nas_5g::send_identity_response() (Identity Response) #1538

Description

@gasparllamazares

Bug: odd/even indication bit of the IMEI is left uninitialized in nas_5g::send_identity_response(), causing intermittent NAS Identity Response rejection by strict 5GC implementations

Summary

When srsUE answers a 5GS Identity Request for IMEI with an Identity Response, the odd/even indication bit of the 5GS mobile identity IE is never explicitly set for the IMEI case. Since imei_s::odd_even_indicator has no default member initializer, the transmitted bit ends up holding whatever value was already in that memory location — non-deterministically 0 ("even") or 1 ("odd").

Since a real IMEI always has 15 digits (an odd count), the only spec-compliant value is 1 ("odd"). When the bit happens to come out as 0, the resulting IE is inconsistent with 3GPP TS 24.501 §9.11.3.4 (an "even" indication implies the last semi-octet must be filler 1111, but here it holds a real digit). Some 5GC implementations (e.g. Open5GS) don't cross-validate this and accept the message anyway; other, stricter NAS decoders reject it outright, and the registration fails with no indication at the NGAP layer that anything is wrong (NGAP just carries the NAS-PDU as an opaque blob, so both the accepted and rejected captures look identical at that layer).

The exact same uninitialized-read pattern exists for imeisv_s::odd_even_indicator in the IMEISV case, though there it "accidentally" tends to work more often because the correct value for IMEISV (16 digits, even) happens to coincide with a zero-initialized bool.

How this was found

We run a lab 5G SA testbed (OCUDU gNB + srsUE, ZMQ loopback RF) registering against a proprietary, strictly-conformant 5GC. Registration was failing intermittently: the core kept resending the NAS Identity Request (PEI/IMEI) roughly every 5s (timer T3570), gave up after 5 attempts, and released the UE context — even though authentication and security setup had already completed successfully.

To isolate the problem, we captured the N2 (NGAP/SCTP) interface on the AMF side and compared two traces against the same core: our emulated UE (srsUE via OCUDU) and, as a reference, a real commercial handset registering through a separate 5G femtocell (its own independent CU/DU stack, unrelated to OCUDU/srsRAN). Both traces proceed identically through Authentication Request/Response and Security Mode Command/Complete — confirming USIM credentials, PLMN and security algorithms are all correct. The divergence appears immediately after: the real handset's Identity Response is accepted and the registration reaches Registration Accept + PDU session establishment, while srsUE's Identity Response is rejected every time, triggering the repeated Identity Request/T3570/UEContextReleaseCommand sequence described above.

Step srsUE (OCUDU) Real handset (femtocell)
RRC Connected
Authentication Request/Response
Security Mode Command/Complete
Identity Request (IMEI) 5 attempts, never accepted not requested by the core in this run
Registration Accept
Outcome UEContextReleaseCommand Registration complete

Diffing the Identity Response NAS-PDU byte-for-byte between a rejected srsUE attempt and an accepted real-handset attempt showed every byte identical except one nibble — which is what led us to the odd/even indication bit and from there to the missing initialization in send_identity_response().

Why this isn't an OCUDU (gNB) issue: the gNB never touches NAS-PDU content — it's integrity-protected/ciphered end-to-end between UE and AMF and is forwarded by the RAN as an opaque blob over NGAP. The buggy code lives entirely in nas_5g.cc, which is compiled into the srsue binary, not into the gNB. The real-handset reference trace above also went through a completely different RAN (a commercial femtocell with its own CU/DU, no OCUDU/srsRAN involved at all) and had no issue — confirming the divergence tracks the UE's NAS stack, not the radio/gNB side.

Affected code

srsue/src/stack/upper/nas_5g.cc, function nas_5g::send_identity_response():

    case (identity_type_5gs_t::identity_types_::imei): {
      srsran::nas_5g::mobile_identity_5gs_t::imei_s& imei = identity_response.mobile_identity.set_imei();
      usim->get_imei_vec(imei.imei.data(), 15);
      // <-- imei.odd_even_indicator is never set here
    } break;
    case (identity_type_5gs_t::identity_types_::imeisv): {
      srsran::nas_5g::mobile_identity_5gs_t::imeisv_s& imeisv = identity_response.mobile_identity.set_imeisv();
      usim->get_imei_vec(imeisv.imeisv.data(), 15);
      imeisv.imeisv[14] = ue_svn_oct1;
      imeisv.imeisv[15] = ue_svn_oct2;
      // <-- imeisv.odd_even_indicator is never set here either
    } break;

lib/include/srsran/asn1/nas_5g_ies.h:

  class imei_s
  {
  public:
    bool                    odd_even_indicator;   // no default initializer
    std::array<uint8_t, 15> imei;
    ...
  };

Expected behavior

The Identity Response for IMEI should always encode odd/even indication = 1 (odd), deterministically, on every attempt (a real IMEI always has 15 digits).

Actual behavior

The bit is effectively random across runs, because it's read from uninitialized memory. Roughly 1 in N attempts encodes it as 0 (even), which strict 5GC NAS decoders reject.

Evidence

Two UplinkNASTransport / Identity Response NGAP dissections, same lab, same IMEI family, decoded with a null-ciphering NAS security context (EEA0) so the embedded plain NAS message is directly visible:

Rejected attempt (srsUE/OCUDU emulated UE):

NAS-PDU: 7e0281ac0f88017e005c00083305034574570043
  Security header type: Integrity protected and ciphered (2)
  Message type: Identity response (0x5c)
  5GS mobile identity
    Length: 8
    Odd/even indication: Even number of identity digits   <-- bit = 0
    Type of identity: IMEI (3)
    IMEI: 350305447750034

Accepted attempt (real commercial handset via femtocell):

NAS-PDU: 7e024aef41b7017e005c00083b05034574570003
  Security header type: Integrity protected and ciphered (2)
  Message type: Identity response (0x5c)
  5GS mobile identity
    Length: 8
    Odd/even indication: Odd number of identity digits    <-- bit = 1
    Type of identity: IMEI (3)
    IMEI: 350305447750030

Only bit 4 of the type-of-identity octet differs (0x33 vs 0x3b); everything else in the IE, including all 15 IMEI digit nibbles, follows the same encoding.

Suggested fix

    case (identity_type_5gs_t::identity_types_::imei): {
      srsran::nas_5g::mobile_identity_5gs_t::imei_s& imei = identity_response.mobile_identity.set_imei();
      usim->get_imei_vec(imei.imei.data(), 15);
      imei.odd_even_indicator = true; // IMEI is always 15 digits -> odd
    } break;
    case (identity_type_5gs_t::identity_types_::imeisv): {
      srsran::nas_5g::mobile_identity_5gs_t::imeisv_s& imeisv = identity_response.mobile_identity.set_imeisv();
      usim->get_imei_vec(imeisv.imeisv.data(), 15);
      imeisv.imeisv[14] = ue_svn_oct1;
      imeisv.imeisv[15] = ue_svn_oct2;
      imeisv.odd_even_indicator = false; // IMEISV is always 16 digits -> even
    } break;

We have this fix applied and rebuilt locally, and confirmed it makes the registration succeed deterministically against the same strict 5GC. Happy to open a PR with it if useful — flagging as an issue first in case there's a preferred process or additional context we're missing.

Environment

  • srsRAN_4G commit: 6bcbd9e5b (branch agpl_next, merge of next)
  • UE: srsUE (ZMQ loopback RF) against an OCUDU gNB (ZMQ)
  • 5GC: proprietary/commercial 5GC with strict NAS IE conformance checking (Open5GS does not reproduce this, as it doesn't cross-validate the odd/even bit against IE content)
  • PLMN 001/01, identity type requested: IMEI

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions