From 6556c8869427b4bfa325135fa91a97e369c9e654 Mon Sep 17 00:00:00 2001 From: Cedric Roux Date: Thu, 2 Apr 2026 21:41:18 +0200 Subject: [PATCH] nr rlc: tolerate bogus data Some unfriendly entity may play with the RLC module and send bogus data to trigger funky behaviors here and there. A report sent by Dhanish, India, indentifies one such issue. From analyzing this report, a possible scenario is the following. Unfriendly entity sends an RLC PDU with: is_first=0 is_last=0 so=15 lenght of data, whatever, let's say 1. This PDU is put in the RX list. Then later it sends another PDU (for the same SN, obviously), with: is_first=1 is_last=1 so=0 (well, since is_first=1, necessarily so=0; it is not transmitted, see 38.322 6.2.2.3 for UM and 38.322 6.2.2.4 for AM) length=10, let's say. This PDU is also put in the RX list, before the previous one. Then the function sdu_full() returns 1, so reassemble_and_deliver() is called and the 'while (pdu)' loop is executed for both PDUs. When the second (bogus one) is processed, so==10 (after processing the first PDU) and the line: int len = pdu->size - (so - pdu->so) is: int len = 1 - (10 - 15) which is not good. So we detect the case 'pdu->so > so' and reject the SDU. We could label the other entity as bogus, since no standard RLC implementation will produce such a case, but let's remain friendly, even with unfriendly entities. (To be changed later if needed.) The problem was reported for RLC AM but is also present in RLC UM. (Not in RLC TM, where there is no segmentation.) Note: according to the report, this bug was found using a fuzzer described as 'AI-assisted custom 5G NR protocol fuzzer'. It had to be said. --- openair2/LAYER2/nr_rlc/nr_rlc_entity_am.c | 6 ++++++ openair2/LAYER2/nr_rlc/nr_rlc_entity_um.c | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/openair2/LAYER2/nr_rlc/nr_rlc_entity_am.c b/openair2/LAYER2/nr_rlc/nr_rlc_entity_am.c index f7ede089a5..62c1fa90c6 100644 --- a/openair2/LAYER2/nr_rlc/nr_rlc_entity_am.c +++ b/openair2/LAYER2/nr_rlc/nr_rlc_entity_am.c @@ -185,6 +185,12 @@ static void reassemble_and_deliver(nr_rlc_entity_am_t *entity, int sn) /* reassemble - free 'data' of each segment after processing */ while (pdu != NULL) { + if (pdu->so > so && !bad_sdu) { + /* pdu->so > so is possible when the other end sends bogus data */ + LOG_E(RLC, "%s:%d:%s: inconsistent SDU, discarding\n", + __FILE__, __LINE__, __FUNCTION__); + bad_sdu = 1; + } int len = pdu->size - (so - pdu->so); if (so + len > NR_SDU_MAX && !bad_sdu) { LOG_E(RLC, "%s:%d:%s: bad SDU, too big, discarding\n", diff --git a/openair2/LAYER2/nr_rlc/nr_rlc_entity_um.c b/openair2/LAYER2/nr_rlc/nr_rlc_entity_um.c index b0ada2e514..a12c991f82 100644 --- a/openair2/LAYER2/nr_rlc/nr_rlc_entity_um.c +++ b/openair2/LAYER2/nr_rlc/nr_rlc_entity_um.c @@ -139,6 +139,12 @@ static void reassemble_and_deliver(nr_rlc_entity_um_t *entity, int sn) /* reassemble - free 'data' of each segment after processing */ while (pdu != NULL && pdu->sn == sn) { + if (pdu->so > so && !bad_sdu) { + /* pdu->so > so is possible when the other end sends bogus data */ + LOG_E(RLC, "%s:%d:%s: inconsistent SDU, discarding\n", + __FILE__, __LINE__, __FUNCTION__); + bad_sdu = 1; + } int len = pdu->size - (so - pdu->so); if (so + len > NR_SDU_MAX && !bad_sdu) { LOG_E(RLC, "%s:%d:%s: bad SDU, too big, discarding\n",