<commit_before>/*
 * Copyright (C) 2015 <NAME> <<EMAIL>>
 *
 * 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.
 */

/**
 * @{
 *
 * @file
 */

#include <errno.h>
#include <string.h>
#include <limits.h>

#include "assert.h"
#include "log.h"
#include "utlist.h"
#include "net/gnrc/netreg.h"
#include "net/gnrc/nettype.h"
#include "net/gnrc/pkt.h"
#include "net/gnrc/icmpv6.h"
#include "net/gnrc/ipv6.h"
#include "net/gnrc/udp.h"
#ifdef MODULE_GNRC_TCP
#include "net/gnrc/tcp.h"
#endif

#define _INVALID_TYPE(type) (((type) < GNRC_NETTYPE_UNDEF) || ((type) >= GNRC_NETTYPE_NUMOF))

/* The registry as lookup table by gnrc_nettype_t */
static gnrc_netreg_entry_t *netreg[GNRC_NETTYPE_NUMOF];

/** Held while accessing _lock_counter, and also while the exclusive lock is held */
static mutex_t _lock_for_counter = MUTEX_INIT;
/** Number of shared locks on netreg. Saturating arithmetic is used; if this
 * reaches UINT_MAX, the lock will never be freed again. This is likely
 * accurate, given that it will only happen when the lock was leaked, so it
 * can't be freed any more anyway.
 *
 * This can be accessed only when _lock_for_counter is held; an alternative
 * implementation with atomics would likely be possible, but for the case of "I
 * can't do that right now" it would still need a mutex. But that's
 * optimization that could be done without changing the public API.
 * */
static unsigned int _lock_counter = 0;
/** Held while the netreg lists are being read. This is what the exclusive
 * users block on if they can't grab the exclusive lock right away.
 *
 * This is largely used as a boolean flag (is locked / is unlocked) that is
 * modified while the _lock_for_counter is held, and will not block because it
 * is synchronized to _lock_counter being 0. It is only ever accessed outside
 * _lock_for_counter when waiting for the counter to reach 0, and even then is
 * released immediately to avoid deadlocks. (Instead, the exclusive acquisition
 * tries to acquire the _lock_for_counter, and if that fails it queues up again
 * after the reader that just snatched it).
 * */
static mutex_t _lock_wait_exclusive = MUTEX_INIT;

void gnrc_netreg_init(void)
{
    /* set all pointers in registry to NULL */
    memset(netreg, 0, GNRC_NETTYPE_NUMOF * sizeof(gnrc_netreg_entry_t *));
}

void gnrc_netreg_acquire_shared(void) {
    mutex_lock(&_lock_for_counter);
    if (_lock_counter == 0) {
        /* At most, this blocks for the very short time until
         * _gnrc_netreg_acquire_exclusive returns it immediately */
        mutex_lock(&_lock_wait_exclusive);
    }
    if (_lock_counter != UINT_MAX) {
        _lock_counter += 1;
    }
    mutex_unlock(&_lock_for_counter);
}

void gnrc_netreg_release_shared(void) {
    mutex_lock(&_lock_for_counter);

    assert(_lock_counter != 0); /* Release without acquire */

    if (_lock_counter != UINT_MAX) {
        _lock_counter -= 1;
    }
    if (_lock_counter == 0) {
        mutex_unlock(&_lock_wait_exclusive);
    }
    mutex_unlock(&_lock_for_counter);
}

/** Assert that there is a shared lock on gnrc_netreg -- this should help weed
 * out callers to @ref gnrc_netreg_lookup that don't properly lock. */
static void _gnrc_netreg_assert_shared(void) {
#if DEVELHELP
    /* Even if we just peek: It's not an atomic, so it needs synchronization */
    mutex_lock(&_lock_for_counter);
    assert(_lock_counter != 0);
    mutex_unlock(&_lock_for_counter);
#endif
}

static void _gnrc_netreg_acquire_exclusive(void) {
    while (true) {
        mutex_lock(&_lock_for_counter);
        if (_lock_counter == 0) {
            /* At most, this blocks for the very short time until
             * another caller of _gnrc_netreg_acquire_exclusive returns it
             * immediately */
            mutex_lock(&_lock_wait_exclusive);
            /* Leaving both locked */
            return;
        }
        mutex_unlock(&_lock_for_counter);

        mutex_lock(&_lock_wait_exclusive);
        /* ... but maybe someone just started grabbing _lock_for_counter, so we
         * give them a chance to finish rather than deadlocking them */
        mutex_unlock(&_lock_wait_exclusive);
    }
}

static void _gnrc_netreg_release_exclusive(void) {
     mutex_unlock(&_lock_wait_exclusive);
     mutex_unlock(&_lock_for_counter);
}

int gnrc_netreg_register(gnrc_nettype_t type, gnrc_netreg_entry_t *entry)
{
#if DEVELHELP
# if defined(MODULE_GNRC_NETAPI_MBOX) || defined(MODULE_GNRC_NETAPI_CALLBACKS)
    bool has_msg_q = (entry->type != GNRC_NETREG_TYPE_DEFAULT) ||
                     thread_has_msg_queue(thread_get(entry->target.pid));
# else
    bool has_msg_q = thread_has_msg_queue(thread_get(entry->target.pid));
# endif

    /* only threads with a message queue are allowed to register at gnrc */
    if (!has_msg_q) {
        LOG_ERROR("\n!!!! gnrc_netreg: initialize message queue of thread %u "
                  "using msg_init_queue() !!!!\n\n", entry->target.pid);
    }
    assert(has_msg_q);
#endif /* DEVELHELP */

    if (_INVALID_TYPE(type)) {
        return -EINVAL;
    }

    _gnrc_netreg_acquire_exclusive();
    LL_PREPEND(netreg[type], entry);
    _gnrc_netreg_release_exclusive();

    return 0;
}

void gnrc_netreg_unregister(gnrc_nettype_t type, gnrc_netreg_entry_t *entry)
{
    if (_INVALID_TYPE(type)) {
        return;
    }

    _gnrc_netreg_acquire_exclusive();
    LL_DELETE(netreg[type], entry);
    /* We can release now already: No new references to this entry can be made
     * any more, and the caller is only allowed to reuse the entry and the mbox
     * target referenced by it after *this* function returned, not when the
     * lock becomes available again. */
    _gnrc_netreg_release_exclusive();

#if defined(MODULE_GNRC_NETAPI_MBOX)
    /* drain packets still in the mbox */
    if (entry->type == GNRC_NETREG_TYPE_MBOX) {
        msg_t msg;
        while (mbox_try_get(entry->target.mbox, &msg)) {
            gnrc_pktbuf_release_error(msg.content.ptr, EBADF);
        }<commit_msg>sys/net/grnc/netreg: avoid freeing wild pointers<commit_after>/*
 * Copyright (C) 2015 <NAME> <<EMAIL>>
 *
 * 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.
 */

/**
 * @{
 *
 * @file
 */

#include <errno.h>
#include <string.h>
#include <limits.h>

#include "assert.h"
#include "log.h"
#include "utlist.h"
#include "net/gnrc/netreg.h"
#include "net/gnrc/nettype.h"
#include "net/gnrc/pkt.h"
#include "net/gnrc/icmpv6.h"
#include "net/gnrc/ipv6.h"
#include "net/gnrc/udp.h"
#ifdef MODULE_GNRC_TCP
#include "net/gnrc/tcp.h"
#endif

#define _INVALID_TYPE(type) (((type) < GNRC_NETTYPE_UNDEF) || ((type) >= GNRC_NETTYPE_NUMOF))

/* The registry as lookup table by gnrc_nettype_t */
static gnrc_netreg_entry_t *netreg[GNRC_NETTYPE_NUMOF];

/** Held while accessing _lock_counter, and also while the exclusive lock is held */
static mutex_t _lock_for_counter = MUTEX_INIT;
/** Number of shared locks on netreg. Saturating arithmetic is used; if this
 * reaches UINT_MAX, the lock will never be freed again. This is likely
 * accurate, given that it will only happen when the lock was leaked, so it
 * can't be freed any more anyway.
 *
 * This can be accessed only when _lock_for_counter is held; an alternative
 * implementation with atomics would likely be possible, but for the case of "I
 * can't do that right now" it would still need a mutex. But that's
 * optimization that could be done without changing the public API.
 * */
static unsigned int _lock_counter = 0;
/** Held while the netreg lists are being read. This is what the exclusive
 * users block on if they can't grab the exclusive lock right away.
 *
 * This is largely used as a boolean flag (is locked / is unlocked) that is
 * modified while the _lock_for_counter is held, and will not block because it
 * is synchronized to _lock_counter being 0. It is only ever accessed outside
 * _lock_for_counter when waiting for the counter to reach 0, and even then is
 * released immediately to avoid deadlocks. (Instead, the exclusive acquisition
 * tries to acquire the _lock_for_counter, and if that fails it queues up again
 * after the reader that just snatched it).
 * */
static mutex_t _lock_wait_exclusive = MUTEX_INIT;

void gnrc_netreg_init(void)
{
    /* set all pointers in registry to NULL */
    memset(netreg, 0, GNRC_NETTYPE_NUMOF * sizeof(gnrc_netreg_entry_t *));
}

void gnrc_netreg_acquire_shared(void) {
    mutex_lock(&_lock_for_counter);
    if (_lock_counter == 0) {
        /* At most, this blocks for the very short time until
         * _gnrc_netreg_acquire_exclusive returns it immediately */
        mutex_lock(&_lock_wait_exclusive);
    }
    if (_lock_counter != UINT_MAX) {
        _lock_counter += 1;
    }
    mutex_unlock(&_lock_for_counter);
}

void gnrc_netreg_release_shared(void) {
    mutex_lock(&_lock_for_counter);

    assert(_lock_counter != 0); /* Release without acquire */

    if (_lock_counter != UINT_MAX) {
        _lock_counter -= 1;
    }
    if (_lock_counter == 0) {
        mutex_unlock(&_lock_wait_exclusive);
    }
    mutex_unlock(&_lock_for_counter);
}

/** Assert that there is a shared lock on gnrc_netreg -- this should help weed
 * out callers to @ref gnrc_netreg_lookup that don't properly lock. */
static void _gnrc_netreg_assert_shared(void) {
#if DEVELHELP
    /* Even if we just peek: It's not an atomic, so it needs synchronization */
    mutex_lock(&_lock_for_counter);
    assert(_lock_counter != 0);
    mutex_unlock(&_lock_for_counter);
#endif
}

static void _gnrc_netreg_acquire_exclusive(void) {
    while (true) {
        mutex_lock(&_lock_for_counter);
        if (_lock_counter == 0) {
            /* At most, this blocks for the very short time until
             * another caller of _gnrc_netreg_acquire_exclusive returns it
             * immediately */
            mutex_lock(&_lock_wait_exclusive);
            /* Leaving both locked */
            return;
        }
        mutex_unlock(&_lock_for_counter);

        mutex_lock(&_lock_wait_exclusive);
        /* ... but maybe someone just started grabbing _lock_for_counter, so we
         * give them a chance to finish rather than deadlocking them */
        mutex_unlock(&_lock_wait_exclusive);
    }
}

static void _gnrc_netreg_release_exclusive(void) {
     mutex_unlock(&_lock_wait_exclusive);
     mutex_unlock(&_lock_for_counter);
}

int gnrc_netreg_register(gnrc_nettype_t type, gnrc_netreg_entry_t *entry)
{
#if DEVELHELP
# if defined(MODULE_GNRC_NETAPI_MBOX) || defined(MODULE_GNRC_NETAPI_CALLBACKS)
    bool has_msg_q = (entry->type != GNRC_NETREG_TYPE_DEFAULT) ||
                     thread_has_msg_queue(thread_get(entry->target.pid));
# else
    bool has_msg_q = thread_has_msg_queue(thread_get(entry->target.pid));
# endif

    /* only threads with a message queue are allowed to register at gnrc */
    if (!has_msg_q) {
        LOG_ERROR("\n!!!! gnrc_netreg: initialize message queue of thread %u "
                  "using msg_init_queue() !!!!\n\n", entry->target.pid);
    }
    assert(has_msg_q);
#endif /* DEVELHELP */

    if (_INVALID_TYPE(type)) {
        return -EINVAL;
    }

    _gnrc_netreg_acquire_exclusive();
    LL_PREPEND(netreg[type], entry);
    _gnrc_netreg_release_exclusive();

    return 0;
}

void gnrc_netreg_unregister(gnrc_nettype_t type, gnrc_netreg_entry_t *entry)
{
    if (_INVALID_TYPE(type)) {
        return;
    }

    _gnrc_netreg_acquire_exclusive();
    LL_DELETE(netreg[type], entry);
    /* We can release now already: No new references to this entry can be made
     * any more, and the caller is only allowed to reuse the entry and the mbox
     * target referenced by it after *this* function returned, not when the
     * lock becomes available again. */
    _gnrc_netreg_release_exclusive();

#if defined(MODULE_GNRC_NETAPI_MBOX)
    /* drain packets still in the mbox */
    if (entry->type == GNRC_NETREG_TYPE_MBOX) {
        msg_t msg;
        while (mbox_try_get(entry->target.mbox, &msg)) {
            if ((msg.type == GNRC_NETAPI_MSG_TYPE_RCV) ||
                (msg.type == GNRC_NETAPI_MSG_TYPE_SND)) {
                gnrc_pktbuf_release_error(msg.content.ptr, EBADF);
            }
        }
{'fraction_non_alphanumeric': 0.09408537077382394, 'fraction_numerical': 0.0032276284999596546, 'mean_word_length': 3.445480631276901, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 29, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6418366', 'n_tokens_mistral': 4132, 'n_tokens_neox': 3989, 'n_words': 1201}