CVE-2023-33974: Concurrent Execution using Shared Resource with Improper Synchronization ('Race Condition') (CWE-362)
value: RIOT-OS, an operating system for Internet of Things (IoT) devices, contains a network stack with the ability to process 6LoWPAN frames. In versions 2023.01 and prior, an attacker can send multiple crafted frames to the device to trigger a race condition. The race condition invalidates assumptions about the program state and leads to an invalid memory access resulting in denial of service. This issue is patched in pull request 19679. There are no known workarounds.
CWE description: The product contains a code sequence that can run concurrently with other code, and the code sequence requires temporary, exclusive access to a shared resource, but a timing window exists in which the shared resource can be modified by another code sequence that is operating concurrently.
CVSS v3 base score: 5.9
Published: 2023-05-30T17:15Z
Repository: https://github.com/RIOT-OS/RIOT
Language: C
Files changed: sys/net/gnrc/network_layer/sixlowpan/frag/sfr/gnrc_sixlowpan_frag_sfr.c
Commit date: 2023-05-30 16:42:35 +0200
Commit message: gnrc_sixlowpan_frag_sfr: fix ARQ scheduler race-condition

Diff: diff --git a/sys/net/gnrc/network_layer/sixlowpan/frag/sfr/gnrc_sixlowpan_frag_sfr.c b/sys/net/gnrc/network_layer/sixlowpan/frag/sfr/gnrc_sixlowpan_frag_sfr.c
index c013ef9ff1f..adc2054fe60 100644
--- a/sys/net/gnrc/network_layer/sixlowpan/frag/sfr/gnrc_sixlowpan_frag_sfr.c
+++ b/sys/net/gnrc/network_layer/sixlowpan/frag/sfr/gnrc_sixlowpan_frag_sfr.c
@@ -504,11 +504,10 @@ void gnrc_sixlowpan_frag_sfr_arq_timeout(gnrc_sixlowpan_frag_fb_t *fbuf)
     uint32_t next_arq_offset = fbuf->sfr.arq_timeout;
     bool reschedule_arq_timeout = false;
     int error_no = ETIMEDOUT;   /* assume time out for fbuf->pkt */
 
     DEBUG("6lo sfr: ARQ timeout for datagram %u\n", fbuf->tag);
-    fbuf->sfr.arq_timeout_event.msg.content.ptr = NULL;
     if (IS_ACTIVE(CONFIG_GNRC_SIXLOWPAN_SFR_MOCK_ARQ_TIMER)) {
         /* mock-up to emulate time having passed beyond (1us) the ARQ timeout */
         now -= (fbuf->sfr.arq_timeout * US_PER_MS) + 1;
     }
     if (IS_USED(MODULE_GNRC_SIXLOWPAN_FRAG_SFR_CONGURE) && frag_desc) {
@@ -679,11 +678,10 @@ static void _clean_slate_datagram(gnrc_sixlowpan_frag_fb_t *fbuf)
 
     fbuf->sfr.arq_timeout_event.msg.content.ptr = NULL;
     /* remove potentially scheduled timers for this datagram */
     evtimer_del((evtimer_t *)(&_arq_timer),
                 &fbuf->sfr.arq_timeout_event.event);
-    fbuf->sfr.arq_timeout_event.event.next = NULL;
     if (gnrc_sixlowpan_frag_sfr_congure_snd_has_inter_frame_gap()) {
         for (clist_node_t *node = clist_lpop(&_frame_queue);
              node != NULL; node = clist_lpop(&_frame_queue)) {
             _frame_queue_t *entry = (_frame_queue_t *)node;
             /* remove frames of this datagram from frame queue */
@@ -1706,17 +1704,30 @@ static void _sched_next_frame(gnrc_sixlowpan_frag_fb_t *fbuf)
          * can not be the case if we came from there (except for misconfigured if_gap). */
         gnrc_sixlowpan_frag_sfr_inter_frame_gap(fbuf);
     }
 }
 
+static inline bool _arq_scheduled(gnrc_sixlowpan_frag_fb_t *fbuf)
+{
+    evtimer_event_t *ptr = _arq_timer.events;
+    evtimer_event_t *event = &fbuf->sfr.arq_timeout_event.event;
+    while (ptr) {
+        if (ptr == event) {
+            return true;
+        }
+        ptr = ptr->next;
+    }
+    return false;
+}
+
 static void _sched_arq_timeout(gnrc_sixlowpan_frag_fb_t *fbuf, uint32_t offset)
 {
     if (IS_ACTIVE(CONFIG_GNRC_SIXLOWPAN_SFR_MOCK_ARQ_TIMER)) {
         /* mock does not need to be scheduled */
         return;
     }
-    if (fbuf->sfr.arq_timeout_event.msg.content.ptr != NULL) {
+    if (_arq_scheduled(fbuf)) {
         DEBUG("6lo sfr: ARQ timeout for datagram %u already scheduled\n",
               (uint8_t)fbuf->tag);
         return;
     }
     DEBUG("6lo sfr: arming ACK timeout in %lums for datagram %u\n",
@@ -1802,11 +1813,10 @@ static void _handle_ack(gnrc_netif_hdr_t *netif_hdr, gnrc_pktsnip_t *pkt,
         if ((fbuf = gnrc_sixlowpan_frag_fb_get_by_tag(hdr->base.tag)) != NULL) {
             /* ACK for pending ACK timeout received. removing ACK timeout */
             DEBUG("6lo sfr: cancelling ARQ timeout\n");
             evtimer_del((evtimer_t *)(&_arq_timer),
                         &fbuf->sfr.arq_timeout_event.event);
-            fbuf->sfr.arq_timeout_event.msg.content.ptr = NULL;
             if ((unaligned_get_u32(hdr->bitmap) == _null_bitmap.u32)) {
                 /* ACK indicates the reassembling endpoint canceled reassembly
                  */
                 DEBUG("6lo sfr: fragmentation canceled\n");
                 /* Retry to send whole datagram if configured, otherwise

Vulnerable code:     fbuf->sfr.arq_timeout_event.msg.content.ptr = NULL;
    fbuf->sfr.arq_timeout_event.event.next = NULL;
    if (fbuf->sfr.arq_timeout_event.msg.content.ptr != NULL) {
            fbuf->sfr.arq_timeout_event.msg.content.ptr = NULL;
Fixed code: static inline bool _arq_scheduled(gnrc_sixlowpan_frag_fb_t *fbuf)
{
    evtimer_event_t *ptr = _arq_timer.events;
    evtimer_event_t *event = &fbuf->sfr.arq_timeout_event.event;
    while (ptr) {
        if (ptr == event) {
            return true;
        }
        ptr = ptr->next;
    }
    return false;
}

    if (_arq_scheduled(fbuf)) {