1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/sys/net/gnrc/pktbuf_malloc/gnrc_pktbuf_malloc.c
2024-11-15 16:41:42 +01:00

301 lines
7.6 KiB
C

/*
* Copyright (C) 2017 Martine Lenders <mlenders@inf.fu-berlin.de>
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup net_gnrc_pktbuf
* @{
*
* @file
*
* @author Martine Lenders <mlenders@inf.fu-berlin.de>
*/
#include <assert.h>
#include <errno.h>
#include <inttypes.h>
#include <stdbool.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/uio.h>
#include "log.h"
#include "mutex.h"
#include "od.h"
#include "utlist.h"
#include "net/gnrc/pktbuf.h"
#include "net/gnrc/nettype.h"
#include "net/gnrc/pkt.h"
#include "pktbuf_internal.h"
#define ENABLE_DEBUG 0
#include "debug.h"
#ifdef MODULE_FUZZING
extern gnrc_pktsnip_t *gnrc_pktbuf_fuzzptr;
#endif
#if defined(TEST_SUITES) || defined(MODULE_FUZZING)
static unsigned mallocs;
static inline void *_malloc(size_t size)
{
mallocs++;
return malloc(size);
}
static inline void _free(void *ptr)
{
if (ptr != NULL) {
/* The fuzzing module is only enabled when building a fuzzing
* application from the fuzzing/ subdirectory. If _free is
* called on the crafted fuzzing packet, the setup assumes that
* input processing has completed and the application terminates. */
#if defined(MODULE_FUZZING) && !defined(MODULE_GNRC_SOCK)
if (ptr == gnrc_pktbuf_fuzzptr) {
exit(EXIT_SUCCESS);
}
#endif
mallocs--;
free(ptr);
}
}
#else
#define _malloc(size) malloc(size)
#define _free(ptr) free(ptr)
#endif
/* internal gnrc_pktbuf functions */
static gnrc_pktsnip_t *_create_snip(gnrc_pktsnip_t *next, const void *data, size_t size,
gnrc_nettype_t type);
static inline void _set_pktsnip(gnrc_pktsnip_t *pkt, gnrc_pktsnip_t *next,
void *data, size_t size, gnrc_nettype_t type)
{
pkt->next = next;
pkt->data = data;
pkt->size = size;
pkt->type = type;
pkt->users = 1;
#ifdef MODULE_GNRC_NETERR
pkt->err_sub = KERNEL_PID_UNDEF;
#endif
}
void gnrc_pktbuf_init(void)
{
#ifdef TEST_SUITES
mallocs = 0;
#endif
}
gnrc_pktsnip_t *gnrc_pktbuf_add(gnrc_pktsnip_t *next, const void *data, size_t size,
gnrc_nettype_t type)
{
gnrc_pktsnip_t *pkt;
if (size > CONFIG_GNRC_PKTBUF_SIZE) {
DEBUG("pktbuf: size (%u) > CONFIG_GNRC_PKTBUF_SIZE (%u)\n",
(unsigned)size, CONFIG_GNRC_PKTBUF_SIZE);
return NULL;
}
mutex_lock(&gnrc_pktbuf_mutex);
pkt = _create_snip(next, data, size, type);
mutex_unlock(&gnrc_pktbuf_mutex);
return pkt;
}
static gnrc_pktsnip_t *_mark(gnrc_pktsnip_t *pkt, size_t size, gnrc_nettype_t type)
{
gnrc_pktsnip_t *header;
void *header_data, *payload;
if ((size == 0) || (pkt == NULL) || (size > pkt->size) || (pkt->data == NULL)) {
DEBUG("pktbuf: size == 0 (was %" PRIuSIZE ") or pkt == NULL (was %p) or "
"size > pkt->size (was %" PRIuSIZE ") or pkt->data == NULL (was %p)\n",
size, (void *)pkt, (pkt ? pkt->size : 0),
(pkt ? pkt->data : NULL));
return NULL;
}
/* create new snip descriptor for marked data */
header = _malloc(sizeof(gnrc_pktsnip_t));
if (header == NULL) {
DEBUG("pktbuf: could not reallocate marked section.\n");
return NULL;
}
if (pkt->size == size) {
_set_pktsnip(header, pkt->next, pkt->data, size, type);
_set_pktsnip(pkt, header, NULL, 0, pkt->type);
return header;
}
/* we can not just "snip off" something from the end of a malloc'd section
* so we need to realloc for marked snip */
payload = _malloc(pkt->size - size);
if (payload == NULL) {
DEBUG("pktbuf: could not reallocate marked section.\n");
_free(header);
return NULL;
}
memcpy(payload, ((uint8_t *)pkt->data) + size, pkt->size - size);
header_data = realloc(pkt->data, size);
if (header_data == NULL) {
DEBUG("pktbuf: could not reallocate marked section.\n");
_free(payload);
_free(header);
return NULL;
}
pkt->data = payload;
pkt->size -= size;
_set_pktsnip(header, pkt->next, header_data, size, type);
pkt->next = header;
return header;
}
gnrc_pktsnip_t *gnrc_pktbuf_mark(gnrc_pktsnip_t *pkt, size_t size, gnrc_nettype_t type)
{
gnrc_pktsnip_t *new;
mutex_lock(&gnrc_pktbuf_mutex);
new = _mark(pkt, size, type);
mutex_unlock(&gnrc_pktbuf_mutex);
return new;
}
static int _realloc_data(gnrc_pktsnip_t *pkt, size_t size)
{
assert(pkt != NULL);
assert(((pkt->size == 0) && (pkt->data == NULL)) ||
((pkt->size > 0) && (pkt->data != NULL)));
/* new size and old size are equal */
if (size == pkt->size) {
/* nothing to do */
return 0;
}
/* new size is 0 and data pointer isn't already NULL */
if ((size == 0) && (pkt->data != NULL)) {
/* set data pointer to NULL */
_free(pkt->data);
pkt->data = NULL;
}
else {
void *data = (pkt->data) ? realloc(pkt->data, size) : _malloc(size);
if (data == NULL) {
DEBUG("pktbuf: error allocating new data section\n");
return ENOMEM;
}
pkt->data = data;
}
pkt->size = size;
return 0;
}
int gnrc_pktbuf_realloc_data(gnrc_pktsnip_t *pkt, size_t size)
{
int res;
mutex_lock(&gnrc_pktbuf_mutex);
res = _realloc_data(pkt, size);
mutex_unlock(&gnrc_pktbuf_mutex);
return res;
}
void gnrc_pktbuf_hold(gnrc_pktsnip_t *pkt, unsigned int num)
{
mutex_lock(&gnrc_pktbuf_mutex);
while (pkt) {
assert(pkt->users + num <= 0xff);
pkt->users += num;
pkt = pkt->next;
}
mutex_unlock(&gnrc_pktbuf_mutex);
}
gnrc_pktsnip_t *gnrc_pktbuf_start_write(gnrc_pktsnip_t *pkt)
{
mutex_lock(&gnrc_pktbuf_mutex);
if (pkt == NULL) {
mutex_unlock(&gnrc_pktbuf_mutex);
return NULL;
}
if (pkt->users > 1) {
gnrc_pktsnip_t *new;
new = _create_snip(pkt->next, pkt->data, pkt->size, pkt->type);
if (new != NULL) {
pkt->users--;
}
mutex_unlock(&gnrc_pktbuf_mutex);
return new;
}
mutex_unlock(&gnrc_pktbuf_mutex);
return pkt;
}
#ifdef DEVELHELP
void gnrc_pktbuf_stats(void)
{
LOG_INFO("pktbuf: no stat output for gnrc_pktbuf_malloc, use tools like valgrind\n");
}
#endif
#ifdef TEST_SUITES
bool gnrc_pktbuf_is_empty(void)
{
/* assert always true, use valgrind to check */
return (mallocs == 0);
}
bool gnrc_pktbuf_is_sane(void)
{
/* assert always true, use valgrind to check */
return true;
}
#endif
static gnrc_pktsnip_t *_create_snip(gnrc_pktsnip_t *next, const void *data, size_t size,
gnrc_nettype_t type)
{
gnrc_pktsnip_t *pkt = _malloc(sizeof(gnrc_pktsnip_t));
void *_data = NULL;
if (pkt == NULL) {
DEBUG("pktbuf: error allocating new packet snip\n");
return NULL;
}
if (size > 0) {
_data = _malloc(size);
if (_data == NULL) {
DEBUG("pktbuf: error allocating data for new packet snip\n");
_free(pkt);
return NULL;
}
}
_set_pktsnip(pkt, next, _data, size, type);
if (data != NULL) {
memcpy(_data, data, size);
}
return pkt;
}
void gnrc_pktbuf_free_internal(void *data, size_t size)
{
(void)size;
_free(data);
}
bool gnrc_pktbuf_contains(void *ptr)
{
(void)ptr;
/* tracking the memory areas malloced is to expensive, so this function
* only is useful with gnrc_pktbuf_static */
return true;
}
/** @} */