at86rf2xx and PHR filtering

Hi,

I recentely talked with another 6lowpan linux developer about an ugly behaviour of at86rf2xx transceivers and I told him I could break many of nodes which use them because nobody really care about that while programming.

The issue is that the len byte inside the PHR will not filtered by the at86rf2xx transceivers, so the length could be above 127. Also remember this can happen when the CRC is still correct. So I can mostly overwrite some stack space, when the buffer is allocated on stack at first.

Most datasheets doesn't say anything what they filter on PHR or not.

In conclusion we introduce inside the Linux kernel [0] and all drivers will check the length field when receiving at first.

In case of at86rf230 driver we check the len field at first, if invalid then we read out the full frame buffer (interesting for monitor interfaces and mac802154/etc should filter them correctly anyway if it's invalid), just avoid copying above 127 because array boundaries. See [1].

btw: We read also the full framebuffer always because the RX_SAFE_MODE functionality from at86rf2xx transceivers. But then we check on a valid length field.

The developer told me to tell that RIOT, so I just want to leave a note here and I don't know if RIOT does filtering on that or not.

- Alex

[0] http://lxr.free-electrons.com/source/include/linux/ieee802154.h#L263 [1] http://lxr.free-electrons.com/source/drivers/net/ieee802154/at86rf230.c#L704

Most datasheets doesn’t say anything what they filter on PHR or not.

The MSB in the PHR field is reserved, see at86rf230 DS:

At and after RF231 this Bit could be sent by the transceiver and can then be seen at the receiver site as well.

Also the standard tells that this bit is reserved, so proper implementations needs to mask this out in order

to get the correct frame length at the receiving site.

Best Regards, Axel

From: devel [mailto:devel-bounces@riot-os.org] On Behalf Of Alexander Aring

Sent: Freitag, 1. April 2016 10:31

To: devel@riot-os.org

Subject: [riot-devel] at86rf2xx and PHR filtering

Hi,

I recentely talked with another 6lowpan linux developer about an ugly

behaviour of at86rf2xx transceivers and I told him I could break many of nodes

which use them because nobody really care about that while programming.

The issue is that the len byte inside the PHR will not filtered by the at86rf2xx

transceivers, so the length could be above 127. Also remember this can happen

when the CRC is still correct. So I can mostly overwrite some stack space, when

the buffer is allocated on stack at first.

Most datasheets doesn’t say anything what they filter on PHR or not.

In conclusion we introduce inside the Linux kernel [0] and all drivers will check

the length field when receiving at first.

In case of at86rf230 driver we check the len field at first, if invalid then we

read out the full frame buffer (interesting for monitor interfaces and

mac802154/etc should filter them correctly anyway if it’s invalid), just avoid

copying above 127 because array boundaries. See [1].

btw: We read also the full framebuffer always because the RX_SAFE_MODE

functionality from at86rf2xx transceivers. But then we check on a valid length

field.

The developer told me to tell that RIOT, so I just want to leave a note here and I

don’t know if RIOT does filtering on that or not.

  • Alex

[0] http://lxr.free-electrons.com/source/include/linux/ieee802154.h#L263

[1] http://lxr.free-

> electrons.com/source/drivers/net/ieee802154/at86rf230.c#L704

Okay, then all transceivers need to do that and mostly at driver layer because at the tail are mostly LQI/RSSI information.

But it's not just the MSB bit, there are also some other "len" values which are reserved:

256 * 0-4 Reserved 257 * 5 MPDU (Acknowledgment) 258 * 6-8 Reserved 259 * 9-127 MPDU

If you received one which is reserved then you know:

"It's an invalid 802.15.4 frame".

Question is how to react on that, already drop then on driver layer -> because it's part of phy layer and not mac layer. Or simple do what we do with deliver the full 127 byte frame to mac layer.

We assume then something weird happend on the air. :slight_smile:

- Alex

Hi Alex,

weird things on air result mostly in a damaged CRC. However malicious frames can always generated by any SDR. My vote would be, because RIOT targets to 15.4 to drop any invalid length frame early on PHY level and don't bother the upper layers to handle the junk. (part of the junk handling is already done in RF23x HW, e.g. when RX_AACK drops invalid CRC frames, or if the framefilter rejects frames with none matching addresses.)

... just my two cents on this ...

But anyway good that you found this potential vulnerability!

Best Regards, Axel.

Hi,

Hi Alex,

weird things on air result mostly in a damaged CRC. However malicious frames can always generated by any SDR. My vote would be, because RIOT targets to 15.4 to drop any invalid length frame early on PHY level and don't bother the upper layers to handle the junk. (part of the junk handling is already done in RF23x HW, e.g. when RX_AACK drops invalid CRC frames, or if the framefilter rejects frames with none matching addresses.)

I think the PHR will not be part of CRC, also you cannot be sure that CRC guarantees that the frame is correct received when the CRC is correct.

I agree when the CRC is invalid then the PHR is maybe also broken, that's why this issue is likely when going into promiscuous mode (then crc in RX_AACK_ON will be ignored).

Let me imagine the following device which is build by a very bad guy/girl:

Looking for preamble, then let length field in some reserved area and let the mac frame looking okay, correct CRC, etc.

Then walk with such device on the next embedded world and have fun. :wink: Reminds me about the TV-B-Gone remote control.

But I think when the len is above 127, then I don't know which field will be used by at86rf2xx for the CRC (?maybe the last two ones?). I would need to check that at first. :slight_smile:

btw: when I talking right now with an atmel expert. Aother issue would be the state-change assertion which is recommended by:

"AVR2022: AT86RF231 - Software Programming Model"

or some other Software Programming Model of at86rf2xx on site:

"Transitions to State RX_AACK_ON"

all state changes should be have an assertion to be sure if you are inside the state which you wanted to change in before.

This doesn't work for RX_AACK_ON, because it could be that the assertion will be done in the time between "change into RX_AACK_ON" and "received SFD", then the transceiver will go into BUSY state and the assertion will fail.

It's not a failure, I am happy that I am so fast in my implementation that this race can occur. :slight_smile: But everybody should ignore the state RX_BUSY while assertion when going into RX_AACK_ON. That's what we do inside the Linux driver, see [0].

- Alex

[0] http://lxr.free-electrons.com/source/drivers/net/ieee802154/at86rf230.c#L413

What I did mean, there are various reasons, why a frame can be invalid. The reserved PHR bit is one thing, invalid CRC another. The CRC calculation inside the transceivers is done without the reserved PHR bit, the PHR value is not part of the CRC itself.

For RX_AACK: I would suggest not trying to be a software nanny for the hardware, e.g. not trying to track the *_BUSY states. (this is just an temporary/transient information and when you receive this information in SW, it might already be outdated)

The state change commands in RF23x are queued commands (except FORCE_TRX_OFF, FORCE_PLL_ON) That means any ongoing transaction is completed before the next command is effective (transaction = receiving or tranmistting a frame).

E.g. if you are in BUSY_RX and you write CMD_PLL_ON, at first the entire frame received and after it, the TRX switches to PLL_ON.

In all basic RX and TX states, including TX_ARET_ON, a transaction ends with a TRX_END IRQ.

This is not true for RX_AACK, reasons for this are: either a CRC error or address match error (see address filtering in DS). This means that in case of a max length frame the TRX stays in BUSY_RX_AACK for 4.xms before it switches back to RX_AACK_ON.

To end RX_AACK use either CMD_TRX_OFF or CMD_PLL_ON and wait until the desired state is reached or one of the FORCE_commands if you need the transceiver immediately to stop the ongoing transaction (e.g. when a Beacon needs to be sent)

Best Regards, Axel

What I did mean, there are various reasons, why a frame can be invalid. The reserved PHR bit is one thing, invalid CRC another. The CRC calculation inside the transceivers is done without the reserved PHR bit, the PHR value is not part of the CRC itself.

ok.

For RX_AACK: I would suggest not trying to be a software nanny for the hardware, e.g. not trying to track the *_BUSY states. (this is just an temporary/transient information and when you receive this information in SW, it might already be outdated)

The state change commands in RF23x are queued commands (except FORCE_TRX_OFF, FORCE_PLL_ON) That means any ongoing transaction is completed before the next command is effective (transaction = receiving or tranmistting a frame).

E.g. if you are in BUSY_RX and you write CMD_PLL_ON, at first the entire frame received and after it, the TRX switches to PLL_ON.

In all basic RX and TX states, including TX_ARET_ON, a transaction ends with a TRX_END IRQ.

This is not true for RX_AACK, reasons for this are: either a CRC error or address match error (see address filtering in DS). This means that in case of a max length frame the TRX stays in BUSY_RX_AACK for 4.xms before it switches back to RX_AACK_ON.

To end RX_AACK use either CMD_TRX_OFF or CMD_PLL_ON and wait until the desired state is reached or one of the FORCE_commands if you need the transceiver immediately to stop the ongoing transaction (e.g. when a Beacon needs to be sent)

I looked now in RIOT code [0].

So they will call at86rf2xx_set_state(at86rf2xx_t *dev, uint8_t state) with state as RX_AACK_ON, but at the end they call:

_set_state(at86rf2xx_t *dev, uint8_t state)

with RX_AACK_ON, so it could be that RIOT has this behaviour:

MCU -> at86rf2xx_set_state(foo, RX_AACK_ON) at86rf2xx -> going into RX_AACK_ON at86rf2xx -> detected SFD, going into RX_AACK_BUSY   MCU -> _set_state(foo, RX_AACK_ON)     MCU -> while (at86rf2xx_get_status(dev) != state) -> stucks inside this loop at86rf2xx -> receiving done, going into RX_AACK_ON     MCU -> while (at86rf2xx_get_status(dev) != state) -> loop ends

The stucking inside the loop for assertion is here a possible case which is bad. But okay it's unlikely I also get such issues only when I make really big traffic.

- Alex

[0] RIOT/drivers/at86rf2xx/at86rf2xx_getset.c at master · RIOT-OS/RIOT · GitHub

MCU -> at86rf2xx_set_state(foo, RX_AACK_ON) at86rf2xx -> going into RX_AACK_ON at86rf2xx -> detected SFD, going into RX_AACK_BUSY   MCU -> _set_state(foo, RX_AACK_ON)     MCU -> while (at86rf2xx_get_status(dev) != state) -> stucks inside this loop at86rf2xx -> receiving done, going into RX_AACK_ON     MCU -> while (at86rf2xx_get_status(dev) != state) -> loop ends

The stucking inside the loop for assertion is here a possible case which is bad. But okay it's unlikely I also get such issues only when I make really big traffic.

[aw] Yes I agree, waiting in RX_AACK_ON state handling is not a good idea.

BR; Axel.

Hi Alex,

thanks for pointing out and letting us know about a possible vulnerability related to the length field. It rose attention to the fact that we checked it in past versions of the driver but missed it at some point in transition. With [1] we should respect this again.

Regarding your example on state transition below, for the driver getting stuck in the assertion loop the transceiver would already have to be in RX_AACK_ON state before to change into RX_AACK_BUSY. The MCU's call of at86rf2xx_set_state(foo, RX_AACK_ON) would then already return in the fourth line of this function [2]. As far as I can see the situation pointed out by you should be avoided with this for most situations.

Looking at the code now we might should exchange the order of this check with the loop waiting for busy states though to catch the case the transceiver is busy receiving and a call of at86rf2xx_set_state(foo, RX_AACK_ON).

Best, Thomas

[1] https://github.com/RIOT-OS/RIOT/pull/5234 [2] https://github.com/RIOT-OS/RIOT/blob/master/drivers/at86rf2xx/at86rf2xx_getset.c#L429