802.15.4 stack unusable due to immediate crashes stemming from unsound swi usage in driver code

In theory all driver calls are suspect, but for example I see crashes in nrf_802154_buffer_free_raw, at this assert:

    result = nrf_802154_request_buffer_free(p_data);
    NRF_802154_ASSERT(result);

This is easily reproducible by running 802154_phy_test example on nrf52840. It crashes less an a second after startup. I also tried openthread coap_server example and it always crashes after a few seconds as well.

So the investigation:

nrf_802154_request_buffer_free is really a swi call to nrf_802154_core_notify_buffer_free. And nrf_802154_core_notify_buffer_free always returns true, so the return value is not getting back correctly. The way return values are passed back from swi calls is a pointer where the value will be written is pushed to the queue along with the params. Now let's have a look at the assembly to understand what went wrong:

/home/andrew/ncs/v3.2.4/nrfxlib/nrf_802154/driver/src/nrf_802154_request_swi.c:274
   44cb8:       480a            ldr     r0, [pc, #40]   @ (44ce4 <nrf_802154_request_buffer_free+0x7c>)
   44cba:       f7ff fd6d       bl      44798 <nrf_802154_queue_push_commit>
nrf_egu_task_trigger():
/home/andrew/ncs/v3.2.4/modules/hal/nordic/nrfx/hal/nrf_egu.h:333
   44cbe:       4b0a            ldr     r3, [pc, #40]   @ (44ce8 <nrf_802154_request_buffer_free+0x80>)
   44cc0:       2201            movs    r2, #1
   44cc2:       609a            str     r2, [r3, #8]
req_exit():
/home/andrew/ncs/v3.2.4/nrfxlib/nrf_802154/driver/src/nrf_802154_request_swi.c:278
   44cc4:       682b            ldr     r3, [r5, #0]
__set_PRIMASK():
/home/andrew/ncs/v3.2.4/modules/hal/cmsis_6/CMSIS/Core/Include/m-profile/cmsis_gcc_m.h:396
   44cc6:       f383 8810       msr     PRIMASK, r3
nrf_802154_request_buffer_free():
/home/andrew/ncs/v3.2.4/nrfxlib/nrf_802154/driver/src/nrf_802154_request_swi.c:767
   44cca:       f89d 0007       ldrb.w  r0, [sp, #7]
   44cce:       b003            add     sp, #12
   44cd0:       bd30            pop     {r4, r5, pc}
req_enter():
/home/andrew/ncs/v3.2.4/nrfxlib/nrf_802154/driver/src/nrf_802154_request_swi.c:261
   44cd2:       f7fc fd03       bl      416dc <nrf_802154_assert_handler>
   44cd6:       e7e5            b.n     44ca4 <nrf_802154_request_buffer_free+0x3c>
...
   44ce8:       40014000        .word   0x40014000

This instruction writes to the EGU register requesting an interrupt `44cc2:  str     r2, [r3, #8]`

Two instructions later, this instruction unmasks interrupts: `44cc6:  msr     PRIMASK, r3`

The very next instruction the return value is read from the memory location where it's expected to be written: `44cca:  ldrb.w  r0, [sp, #7]`

I hope it's obvious by now what can go wrong ... what if the interrupt gets delivered a few cycles later? The we read the return value before it is written! Running in a debugger we can see that the interrupt sometimes actually gets delivered when executing the pop instructions, two instruction later.

To confirm this is the issue, I tried adding a few nops after unmasking interrupts, and lo and behold, no more crashes.

static void req_exit(void)
{
    nrf_802154_queue_push_commit(&m_requests_queue);

    nrf_egu_task_trigger(NRF_802154_EGU_INSTANCE, REQ_TASK);

    nrf_802154_mcu_critical_exit(m_mcu_cs);

    for (int i = 0; i < 4; i++)
        __asm__ volatile("nop");
}

I don't think this is an appropriate long term fix, I think you need to have a flag that's set after the request is actually executed, e.g. in irq_handler_req_event, and spin on that flag, of course now risking that it spins forever :( Or even better, stop doing all this crap and use SVC.

P.S. seems that other people have also hit this issue, e.g. devzone.nordicsemi.com/.../558728

Parents Reply Children
  • Hi again, and sorry about the wait. I've now heard from the relevant team.

    What HW are you using?

    The sample application is set to RX, so it listens to all transmissions. The error log rx failed error 2! means the radio received on-air data with an incorrect CRC (caused by noise - the preamble matches IEEE 802.25.4, but the data is rejected due to a checksum error). You may also see rx failed error 3!, caused by communication between two other devices - the data is not addressed to your DK (incorrect address).

    This is expected and does not cause a crash.

    Regards,

    Elfving

Related