Unexpected Pointer Aliasing in IEEE 802154 Fragment Reassembly

Description

  1. 4. Unexpected Pointer Aliasing in IEEE 802154 Fragment Reassembly

  • Bug Description: Fragment reassembly cache handling crashes in case a fragment is typed for fragmentation, but itself contains the full payload.

  • Bug Result: A NULL pointer is de-referenced as an assumed-to-be-valid pointer to a network packet structure.

  • Bug Impact: Denial of Service (DOS) of the target by an attacker sending a single malformed IEEE 802154 fragment.

  1.  

    1. Bug Details

  • Affected code: Fragment reassembly logic in subsys/net/l2/ieee802154/ieee802154_fragment.c#fragment_add_to_cache

High-Level reasoning for bug occurrence:
1. A cache structure holds a list of previously sent ieee802154 which are gathered for later re-assembly
2. Reassembly is only necessary in case multiple (small radio-layer) fragments are required to represent the whole payload.
3. Reassembly logic implicitly assumes that multiple fragments are present before reassembly is triggered
4. This, it is unaware of the situation where the start of the cache list may actually be the newly added fragment itself
5. This leads to an unexpected alias between two pointers, and cleanup logic corrupts the packet structure

  1.  

    1. Vulnerable code path:
      ieee802154_manage_recv_packet->ieee802154_reassemble->fragment_add_to_cache accepts an incoming fragment which is marked for fragmentation

1. If the fragment is the first of its set (indicated by the tag number), a new reassembly cache is requested

BUG: The issue arises where the currently added packet also is the start of the cache list, which arises if the first packet which is sent also contains the full payload. In the following code snippet, this leads to the condition `pkt == cache->pkt`. Thus, the assignment `cache->pkt->buffer = NULL;` sets `pkt->buffer=NULL`.

```
fragment_append(cache->pkt, frag);

if (fragment_cached_pkt_len(cache->pkt) == cache->size) {
/* Assign buffer back to input packet. */
pkt->buffer = cache->pkt->buffer;
cache->pkt->buffer = NULL;

fragment_reconstruct_packet(pkt);

...

// Assume a valid pkt, and thus pkt->buffer to be properly initialized
}
```

In the following, `net_6lo_uncompress` is called, in which `pkt->buffer` is used and a crash occurs:
```
bool net_6lo_uncompress(struct net_pkt *pkt)
{
NET_ASSERT(pkt && pkt->frags);

if ((pkt->frags->data[0] & NET_6LO_DISPATCH_IPHC_MASK) ==
NET_6LO_DISPATCH_IPHC) {
// BUG CRASH TRIGGER: pkt->frags->data[0] crashes, as
// pkt->frags==NULL (pkt->frags is an alternative name for pkt->buffer)
...
}
```

  1.  

    1. Proposed Fix

  • Add check to reassembly logic for the packet which is added also being the first one

  • Note that single-frame fragmentation may not be adhering to a specification, so dropping the packet may be an option as well


if (fragment_cached_pkt_len(cache->pkt) == cache->size) {

  • /* Assign buffer back to input packet. */

  • pkt->buffer = cache->pkt->buffer;

  • cache->pkt->buffer = NULL;
    + if (!first_frag) {
    + /* Assign buffer back to input packet. */
    + pkt->buffer = cache->pkt->buffer;
    + cache->pkt->buffer = NULL;
    + }


fragment_reconstruct_packet(pkt);
```

Environment

None

Attachments

1

Activity

Show:

Tobias Scharnowski June 2, 2021 at 10:30 PM

This looks like it to me. I was also looking back for these issues as we are getting close to our academic publication and having the official record would be great

Flavio Ceolin February 9, 2021 at 7:05 PM

was this fixed in ?

Tomasz Bursztyka February 2, 2021 at 11:27 AM

Unfortunately the guy who did that code originaly is no longer working on Zephyr. I have no idea myself if, by specification, the very first fragment can hold already the full data. Logically it would not make sense, but a bad device (either by intention or not) could send such fragment.

David Brown January 14, 2021 at 10:36 PM
Edited

And another update regarding the same issue 04. The pointer aliasing resulting from single-fragment reassembly breaks more assumptions on the cache down that code path. So a fix needs to make sure the cache cleanup logic does not release the local pkt variable due to the aliasing pkt == cache->pkt.

With that said, my current local patch looks like the following:

I just wanted to avoid leaving a known invalid fix standing. You may find a better solution here.

Best regards,
Tobias

David Brown January 14, 2021 at 10:36 PM
Edited

And another update regarding the same issue 04. The pointer aliasing resulting from single-fragment reassembly breaks more assumptions on the cache down that code path. So a fix needs to make sure the cache cleanup logic does not release the local pkt variable due to the aliasing pkt == cache->pkt.

With that said, my current local patch looks like the following:

I just wanted to avoid leaving a known invalid fix standing. You may find a better solution here.

Best regards,
Tobias

Details

Assignee

Reporter

Authorized viewers

Tomasz Bursztyka

CVE

Embargo Lift

Components

Affects versions

Priority

Created January 14, 2021 at 10:35 PM
Updated October 12, 2021 at 9:18 PM