Files
openairinterface5g/openair2/LAYER2/rlc_v2/rlc_entity_um.c
T
Cedric Roux 21a2a8c6e1 rlc v2: some fixes for 'bugs' found by coverity scan
Minor fixes, doesn't change anything. Not sure these are 'bugs' either,
but let's be polite with coverity scan...

One thing was not changed.
Coverity scan says:
*** CID 357991:  Memory - illegal accesses  (USE_AFTER_FREE)
/home/carabe/raphael/openairinterface5g/openair2/LAYER2/rlc_v2/rlc_entity_am.c: 507 in tx_list_remove_sn()
501         } else {
502           prev = cur;
503           cur = cur->next;
504         }
505       }
506
>>>     CID 357991:  Memory - illegal accesses  (USE_AFTER_FREE)
>>>     Using freed pointer "head.next".
507       return head.next;
508     }
509
510     void cleanup_sdu_list(rlc_entity_am_t *entity)
511     {
512       rlc_sdu_t head;

But as far as I understand, there is no problem. We don't access
head.next at all if it has been freed. Or is there some aliasing
going on there (pointer aliasing)? I doubt it. False positive?
2020-05-07 18:05:05 +02:00

709 lines
20 KiB
C

/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#include "rlc_entity_um.h"
#include "rlc_pdu.h"
#include <stdlib.h>
#include <string.h>
#include "LOG/log.h"
/*************************************************************************/
/* PDU RX functions */
/*************************************************************************/
static int modulus_rx(rlc_entity_um_t *entity, int a)
{
/* as per 36.322 7.1, modulus base is vr(uh)-window_size and modulus is
* 2^sn_field_length (which is 'sn_modulus' in rlc_entity_um_t)
*/
int r = a - (entity->vr_uh - entity->window_size);
if (r < 0) r += entity->sn_modulus;
return r % entity->sn_modulus;
}
static int sn_compare_rx(void *_entity, int a, int b)
{
rlc_entity_um_t *entity = _entity;
return modulus_rx(entity, a) - modulus_rx(entity, b);
}
static int sn_in_recv_window(void *_entity, int sn)
{
rlc_entity_um_t *entity = _entity;
int mod_sn = modulus_rx(entity, sn);
/* we simplify (VR(UH) - UM_Window_Size) <= SN < VR(UH), base is
* (VR(UH) - UM_Window_Size) and VR(UH) = base + window_size
*/
return mod_sn < entity->window_size;
}
/* return 1 if a PDU with SN == 'sn' is in the rx list, 0 otherwise */
static int rlc_um_pdu_received(rlc_entity_um_t *entity, int sn)
{
rlc_rx_pdu_segment_t *cur = entity->rx_list;
while (cur != NULL) {
if (cur->sn == sn)
return 1;
cur = cur->next;
}
return 0;
}
static int less_than_vr_ur(rlc_entity_um_t *entity, int sn)
{
return sn_compare_rx(entity, sn, entity->vr_ur) < 0;
}
static int outside_of_reordering_window(rlc_entity_um_t *entity, int sn)
{
return !sn_in_recv_window(entity, sn);
}
static int less_than_vr_uh(rlc_entity_um_t *entity, int sn)
{
return sn_compare_rx(entity, sn, entity->vr_uh) < 0;
}
static void rlc_um_reassemble_pdu(rlc_entity_um_t *entity,
rlc_rx_pdu_segment_t *pdu)
{
rlc_um_reassemble_t *r = &entity->reassemble;
int fi;
int e;
int sn;
int data_pos;
int sdu_len;
int sdu_offset;
sdu_offset = pdu->data_offset;
rlc_pdu_decoder_init(&r->dec, pdu->data, pdu->size);
if (entity->sn_field_length == 10)
rlc_pdu_decoder_get_bits(&r->dec, 3);
fi = rlc_pdu_decoder_get_bits(&r->dec, 2);
e = rlc_pdu_decoder_get_bits(&r->dec, 1);
sn = rlc_pdu_decoder_get_bits(&r->dec, entity->sn_field_length);
if (e) {
e = rlc_pdu_decoder_get_bits(&r->dec, 1);
sdu_len = rlc_pdu_decoder_get_bits(&r->dec, 11);
} else
sdu_len = pdu->size - sdu_offset;
/* discard current SDU being reassembled if bad SN or bad FI */
if (sn != (r->sn + 1) % entity->sn_modulus ||
!(fi & 0x02)) {
if (r->sdu_pos)
LOG_D(RLC, "%s:%d:%s: warning: discard partially reassembled SDU\n",
__FILE__, __LINE__, __FUNCTION__);
r->sdu_pos = 0;
}
/* if the head of the SDU is missing, still process the PDU
* but remember to discard the reassembled SDU later on (the
* head has not been received).
* The head is missing if sdu_pos == 0 and fi says the PDU does not
* start an SDU.
*/
if (r->sdu_pos == 0 && (fi & 0x02))
r->sdu_head_missing = 1;
r->sn = sn;
data_pos = pdu->data_offset;
while (1) {
if (r->sdu_pos >= SDU_MAX) {
/* TODO: proper error handling (discard PDUs with current sn from
* reassembly queue? something else?)
*/
LOG_E(RLC, "%s:%d:%s: bad RLC PDU\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
r->sdu[r->sdu_pos] = pdu->data[data_pos];
r->sdu_pos++;
data_pos++;
if (data_pos == sdu_offset + sdu_len) {
/* all bytes of SDU are consumed, check if SDU is fully there.
* It is if the data pointer is not at the end of the PDU segment
* or if 'fi' & 1 == 0
*/
if (data_pos != pdu->size || (fi & 1) == 0) {
/* time to discard the SDU if we didn't receive the head */
if (r->sdu_head_missing) {
LOG_D(RLC, "%s:%d:%s: warning: discard SDU, head not received\n",
__FILE__, __LINE__, __FUNCTION__);
r->sdu_head_missing = 0;
} else {
/* SDU is full - deliver to higher layer */
entity->common.deliver_sdu(entity->common.deliver_sdu_data,
(rlc_entity_t *)entity,
r->sdu, r->sdu_pos);
}
r->sdu_pos = 0;
}
/* done with PDU? */
if (data_pos == pdu->size)
break;
/* not at the end of PDU, process next SDU */
sdu_offset += sdu_len;
if (e) {
e = rlc_pdu_decoder_get_bits(&r->dec, 1);
sdu_len = rlc_pdu_decoder_get_bits(&r->dec, 11);
} else
sdu_len = pdu->size - sdu_offset;
}
}
}
static void rlc_um_reassemble(rlc_entity_um_t *entity,
int (*check_sn)(rlc_entity_um_t *entity, int sn))
{
rlc_rx_pdu_segment_t *cur;
/* process all PDUs from head of rx list until all is processed or
* the SN is not valid anymore with respect to 'check_sn'
*/
while (entity->rx_list != NULL && check_sn(entity, entity->rx_list->sn)) {
cur = entity->rx_list;
rlc_um_reassemble_pdu(entity, cur);
entity->rx_size -= cur->size;
entity->rx_list = cur->next;
rlc_rx_free_pdu_segment(cur);
}
}
static void rlc_um_reception_actions(rlc_entity_um_t *entity,
rlc_rx_pdu_segment_t *pdu_segment)
{
if (!sn_in_recv_window(entity, pdu_segment->sn)) {
entity->vr_uh = (pdu_segment->sn + 1) % entity->sn_modulus;
rlc_um_reassemble(entity, outside_of_reordering_window);
if (!sn_in_recv_window(entity, entity->vr_ur))
entity->vr_ur = (entity->vr_uh - entity->window_size
+ entity->sn_modulus) % entity->sn_modulus;
}
if (rlc_um_pdu_received(entity, entity->vr_ur)) {
do {
entity->vr_ur = (entity->vr_ur + 1) % entity->sn_modulus;
} while (rlc_um_pdu_received(entity, entity->vr_ur));
rlc_um_reassemble(entity, less_than_vr_ur);
}
if (entity->t_reordering_start) {
if (sn_compare_rx(entity, entity->vr_ux, entity->vr_ur) <= 0 ||
(!sn_in_recv_window(entity, entity->vr_ux) &&
entity->vr_ux != entity->vr_uh))
entity->t_reordering_start = 0;
}
if (entity->t_reordering_start == 0) {
if (sn_compare_rx(entity, entity->vr_uh, entity->vr_ur) > 0) {
entity->t_reordering_start = entity->t_current;
entity->vr_ux = entity->vr_uh;
}
}
}
void rlc_entity_um_recv_pdu(rlc_entity_t *_entity, char *buffer, int size)
{
#define R(d) do { if (rlc_pdu_decoder_in_error(&d)) goto err; } while (0)
rlc_entity_um_t *entity = (rlc_entity_um_t *)_entity;
rlc_pdu_decoder_t decoder;
rlc_pdu_decoder_t data_decoder;
int e;
int sn;
int data_e;
int data_li;
int packet_count;
int data_size;
int data_start;
int indicated_data_size;
rlc_rx_pdu_segment_t *pdu_segment;
rlc_pdu_decoder_init(&decoder, buffer, size);
if (entity->sn_field_length == 10) {
rlc_pdu_decoder_get_bits(&decoder, 3); R(decoder); /* R1 */
}
rlc_pdu_decoder_get_bits(&decoder, 2); R(decoder); /* FI */
e = rlc_pdu_decoder_get_bits(&decoder, 1); R(decoder);
sn = rlc_pdu_decoder_get_bits(&decoder, entity->sn_field_length); R(decoder);
/* dicard PDU if rx buffer is full */
if (entity->rx_size + size > entity->rx_maxsize) {
LOG_D(RLC, "%s:%d:%s: warning: discard PDU, RX buffer full\n",
__FILE__, __LINE__, __FUNCTION__);
return;
}
/* discard according to 36.322 5.1.2.2.2 */
if ((sn_compare_rx(entity, entity->vr_ur, sn) < 0 &&
sn_compare_rx(entity, sn, entity->vr_uh) < 0 &&
rlc_um_pdu_received(entity, sn)) ||
(sn_compare_rx(entity, entity->vr_uh - entity->window_size, sn) <= 0 &&
sn_compare_rx(entity, sn, entity->vr_ur) < 0)) {
LOG_D(RLC, "%s:%d:%s: warning: discard PDU (sn %d vr(ur) %d vr(uh) %d)\n",
__FILE__, __LINE__, __FUNCTION__,
sn, entity->vr_ur, entity->vr_uh);
return;
}
packet_count = 1;
/* go to start of data */
indicated_data_size = 0;
data_decoder = decoder;
data_e = e;
while (data_e) {
data_e = rlc_pdu_decoder_get_bits(&data_decoder, 1); R(data_decoder);
data_li = rlc_pdu_decoder_get_bits(&data_decoder, 11); R(data_decoder);
if (data_li == 0) {
LOG_D(RLC, "%s:%d:%s: warning: discard PDU, li == 0\n",
__FILE__, __LINE__, __FUNCTION__);
return;
}
indicated_data_size += data_li;
packet_count++;
}
rlc_pdu_decoder_align(&data_decoder);
data_start = data_decoder.byte;
data_size = size - data_start;
if (data_size <= 0) {
LOG_D(RLC, "%s:%d:%s: warning: discard PDU, wrong data size (sum of LI %d data size %d)\n",
__FILE__, __LINE__, __FUNCTION__,
indicated_data_size, data_size);
return;
}
if (indicated_data_size >= data_size) {
LOG_D(RLC, "%s:%d:%s: warning: discard PDU, bad LIs (sum of LI %d data size %d)\n",
__FILE__, __LINE__, __FUNCTION__,
indicated_data_size, data_size);
return;
}
/* put in pdu reception list */
entity->rx_size += size;
pdu_segment = rlc_rx_new_pdu_segment(sn, 0, size, 1, buffer, data_start);
entity->rx_list = rlc_rx_pdu_segment_list_add(sn_compare_rx, entity,
entity->rx_list, pdu_segment);
/* do reception actions (36.322 5.1.2.2.3) */
rlc_um_reception_actions(entity, pdu_segment);
return;
err:
LOG_D(RLC, "%s:%d:%s: error decoding PDU, discarding\n", __FILE__, __LINE__, __FUNCTION__);
#undef R
}
/*************************************************************************/
/* TX functions */
/*************************************************************************/
typedef struct {
int sdu_count;
int data_size;
int header_size;
int last_sdu_is_full;
int first_sdu_length;
} tx_pdu_size_t;
static int header_size(int sn_field_length, int sdu_count)
{
int bits = 8 + 8 * (sn_field_length == 10) + 12 * (sdu_count - 1);
/* padding if we have to */
return (bits + 7) / 8;
}
static tx_pdu_size_t tx_pdu_size(rlc_entity_um_t *entity, int maxsize)
{
tx_pdu_size_t ret;
int sdu_count;
int sdu_size;
int pdu_data_size;
rlc_sdu_t *sdu;
ret.sdu_count = 0;
ret.data_size = 0;
ret.header_size = 0;
ret.last_sdu_is_full = 1;
ret.first_sdu_length = 0;
/* TX PDU - let's make the biggest PDU we can with the SDUs we have */
sdu_count = 0;
pdu_data_size = 0;
sdu = entity->tx_list;
while (sdu != NULL) {
int new_header_size = header_size(entity->sn_field_length, sdu_count+1);
/* if we cannot put new header + at least 1 byte of data then over */
if (new_header_size + pdu_data_size >= maxsize)
break;
sdu_count++;
/* only include the bytes of this SDU not included in PDUs already */
sdu_size = sdu->size - sdu->next_byte;
/* don't feed more than 'maxsize' bytes */
if (new_header_size + pdu_data_size + sdu_size > maxsize) {
sdu_size = maxsize - new_header_size - pdu_data_size;
ret.last_sdu_is_full = 0;
}
if (sdu_count == 1)
ret.first_sdu_length = sdu_size;
pdu_data_size += sdu_size;
/* if we put more than 2^11-1 bytes then the LI field cannot be used,
* so this is the last SDU we can put
*/
if (sdu_size > 2047)
break;
sdu = sdu->next;
}
if (sdu_count) {
ret.sdu_count = sdu_count;
ret.data_size = pdu_data_size;
ret.header_size = header_size(entity->sn_field_length, sdu_count);
}
return ret;
}
rlc_entity_buffer_status_t rlc_entity_um_buffer_status(
rlc_entity_t *_entity, int maxsize)
{
rlc_entity_um_t *entity = (rlc_entity_um_t *)_entity;
rlc_entity_buffer_status_t ret;
tx_pdu_size_t tx_size;
ret.status_size = 0;
/* todo: if an SDU has size >2047 in the tx list then processing
* stops and computed size will not be accurate. Change the computation
* to be more accurate (if needed).
*/
tx_size = tx_pdu_size(entity, maxsize);
ret.tx_size = tx_size.data_size + tx_size.header_size;
ret.retx_size = 0;
return ret;
}
int rlc_entity_um_generate_pdu(rlc_entity_t *_entity, char *buffer, int size)
{
rlc_entity_um_t *entity = (rlc_entity_um_t *)_entity;
tx_pdu_size_t pdu_size;
rlc_sdu_t *sdu;
int i;
int cursize;
int first_sdu_full;
int last_sdu_full;
rlc_pdu_encoder_t encoder;
int fi;
int e;
int li;
char *out;
int outpos;
int first_sdu_start_byte;
int sdu_start_byte;
pdu_size = tx_pdu_size(entity, size);
if (pdu_size.sdu_count == 0)
return 0;
sdu = entity->tx_list;
first_sdu_start_byte = sdu->next_byte;
/* reserve SDU bytes */
cursize = 0;
for (i = 0; i < pdu_size.sdu_count; i++, sdu = sdu->next) {
int sdu_size = sdu->size - sdu->next_byte;
if (cursize + sdu_size > pdu_size.data_size)
sdu_size = pdu_size.data_size - cursize;
sdu->next_byte += sdu_size;
cursize += sdu_size;
}
first_sdu_full = first_sdu_start_byte == 0;
last_sdu_full = pdu_size.last_sdu_is_full;
/* generate header */
rlc_pdu_encoder_init(&encoder, buffer, size);
if (entity->sn_field_length == 10)
rlc_pdu_encoder_put_bits(&encoder, 0, 3); /* R1 */
fi = 0;
if (!first_sdu_full)
fi |= 0x02;
if (!last_sdu_full)
fi |= 0x01;
rlc_pdu_encoder_put_bits(&encoder, fi, 2); /* FI */
/* see the AM code to understand the logic for Es and LIs */
if (pdu_size.sdu_count >= 2)
e = 1;
else
e = 0;
rlc_pdu_encoder_put_bits(&encoder, e, 1); /* E */
if (entity->sn_field_length == 10)
rlc_pdu_encoder_put_bits(&encoder, entity->vt_us, 10); /* SN */
else
rlc_pdu_encoder_put_bits(&encoder, entity->vt_us, 5); /* SN */
/* put LIs */
sdu = entity->tx_list;
/* first SDU */
li = pdu_size.first_sdu_length;
/* put E+LI only if at least 2 SDUs */
if (pdu_size.sdu_count >= 2) {
/* E is 1 if at least 3 SDUs */
if (pdu_size.sdu_count >= 3)
e = 1;
else
e = 0;
rlc_pdu_encoder_put_bits(&encoder, e, 1); /* E */
rlc_pdu_encoder_put_bits(&encoder, li, 11); /* LI */
}
/* next SDUs, but not the last (no LI for the last) */
sdu = sdu->next;
for (i = 2; i < pdu_size.sdu_count; i++, sdu = sdu->next) {
if (i != pdu_size.sdu_count - 1)
e = 1;
else
e = 0;
li = sdu->size;
rlc_pdu_encoder_put_bits(&encoder, e, 1); /* E */
rlc_pdu_encoder_put_bits(&encoder, li, 11); /* LI */
}
rlc_pdu_encoder_align(&encoder);
/* generate data */
out = buffer + pdu_size.header_size;
sdu = entity->tx_list;
sdu_start_byte = first_sdu_start_byte;
outpos = 0;
for (i = 0; i < pdu_size.sdu_count; i++, sdu = sdu->next) {
li = sdu->size - sdu_start_byte;
if (outpos + li >= pdu_size.data_size)
li = pdu_size.data_size - outpos;
memcpy(out+outpos, sdu->data + sdu_start_byte, li);
outpos += li;
sdu_start_byte = 0;
}
/* cleanup sdu list */
while (entity->tx_list != NULL &&
entity->tx_list->size == entity->tx_list->next_byte) {
rlc_sdu_t *c = entity->tx_list;
/* release SDU bytes */
entity->tx_size -= c->size;
entity->tx_list = c->next;
rlc_free_sdu(c);
}
if (entity->tx_list == NULL)
entity->tx_end = NULL;
/* update VT(US) */
entity->vt_us = (entity->vt_us + 1) % entity->sn_modulus;
return pdu_size.header_size + pdu_size.data_size;
}
/*************************************************************************/
/* SDU RX functions */
/*************************************************************************/
void rlc_entity_um_recv_sdu(rlc_entity_t *_entity, char *buffer, int size,
int sdu_id)
{
rlc_entity_um_t *entity = (rlc_entity_um_t *)_entity;
rlc_sdu_t *sdu;
if (size > SDU_MAX) {
LOG_E(RLC, "%s:%d:%s: fatal: SDU size too big (%d bytes)\n",
__FILE__, __LINE__, __FUNCTION__, size);
exit(1);
}
if (entity->tx_size + size > entity->tx_maxsize) {
LOG_D(RLC, "%s:%d:%s: warning: SDU rejected, SDU buffer full\n",
__FILE__, __LINE__, __FUNCTION__);
return;
}
entity->tx_size += size;
sdu = rlc_new_sdu(buffer, size, sdu_id);
rlc_sdu_list_add(&entity->tx_list, &entity->tx_end, sdu);
}
/*************************************************************************/
/* time/timers */
/*************************************************************************/
static void check_t_reordering(rlc_entity_um_t *entity)
{
int sn;
/* is t_reordering running and if yes has it expired? */
if (entity->t_reordering_start == 0 ||
entity->t_current <= entity->t_reordering_start + entity->t_reordering)
return;
/* stop timer */
entity->t_reordering_start = 0;
LOG_D(RLC, "%s:%d:%s: t_reordering expired\n", __FILE__, __LINE__, __FUNCTION__);
/* update VR(UR) to first SN >= VR(UX) of PDU not received
*/
sn = entity->vr_ux;
while (rlc_um_pdu_received(entity, sn))
sn = (sn + 1) % entity->sn_modulus;
entity->vr_ur = sn;
rlc_um_reassemble(entity, less_than_vr_ur);
if (sn_compare_rx(entity, entity->vr_uh, entity->vr_ur) > 0) {
entity->t_reordering_start = entity->t_current;
entity->vr_ux = entity->vr_uh;
}
}
void rlc_entity_um_set_time(rlc_entity_t *_entity, uint64_t now)
{
rlc_entity_um_t *entity = (rlc_entity_um_t *)_entity;
entity->t_current = now;
check_t_reordering(entity);
}
/*************************************************************************/
/* discard/re-establishment/delete */
/*************************************************************************/
void rlc_entity_um_discard_sdu(rlc_entity_t *_entity, int sdu_id)
{
/* implements 36.322 5.3 */
rlc_entity_um_t *entity = (rlc_entity_um_t *)_entity;
rlc_sdu_t head;
rlc_sdu_t *cur;
rlc_sdu_t *prev;
head.next = entity->tx_list;
cur = entity->tx_list;
prev = &head;
while (cur != NULL && cur->upper_layer_id != sdu_id) {
prev = cur;
cur = cur->next;
}
/* if sdu_id not found or some bytes have already been 'PDU-ized'
* then do nothing
*/
if (cur == NULL || cur->next_byte != 0)
return;
/* remove SDU from tx_list */
prev->next = cur->next;
entity->tx_list = head.next;
if (entity->tx_end == cur) {
if (prev != &head)
entity->tx_end = prev;
else
entity->tx_end = NULL;
}
rlc_free_sdu(cur);
}
static void clear_entity(rlc_entity_um_t *entity)
{
rlc_rx_pdu_segment_t *cur_rx;
rlc_sdu_t *cur_tx;
entity->vr_ur = 0;
entity->vr_ux = 0;
entity->vr_uh = 0;
entity->vt_us = 0;
entity->t_current = 0;
entity->t_reordering_start = 0;
cur_rx = entity->rx_list;
while (cur_rx != NULL) {
rlc_rx_pdu_segment_t *p = cur_rx;
cur_rx = cur_rx->next;
rlc_rx_free_pdu_segment(p);
}
entity->rx_list = NULL;
entity->rx_size = 0;
memset(&entity->reassemble, 0, sizeof(rlc_um_reassemble_t));
cur_tx = entity->tx_list;
while (cur_tx != NULL) {
rlc_sdu_t *p = cur_tx;
cur_tx = cur_tx->next;
rlc_free_sdu(p);
}
entity->tx_list = NULL;
entity->tx_end = NULL;
entity->tx_size = 0;
}
void rlc_entity_um_reestablishment(rlc_entity_t *_entity)
{
rlc_entity_um_t *entity = (rlc_entity_um_t *)_entity;
rlc_um_reassemble(entity, less_than_vr_uh);
clear_entity(entity);
}
void rlc_entity_um_delete(rlc_entity_t *_entity)
{
rlc_entity_um_t *entity = (rlc_entity_um_t *)_entity;
clear_entity(entity);
free(entity);
}