Showing results for 
Search instead for 
Did you mean: 

Bugs in Mailbox communication code of STM32WB

Dominik Lorych
Associate III


I have found a few bugs in the Mailbox communication code of the STM32WB (which is responsible for letting CPU1 and CPU2 communicate). But other Dual-Core MCUs potentially could have the same bugs.

The bugs are not security-critical, but they cause that the code does not adhere to the specification given in AN5185. The bugs cancel each other out as long as both CPUs have the same bugs. But, if one wants to write custom communication code for the application CPU, the communication just does not work because the other side does not behave as specified.

A short introduction into the Mailbox system: Communication between both CPUs gets handled via the Mailbox and the IPCC peripheral. For this, commands and responses get written into a shared RAM in a defined table structure (the Mailbox). Usually, CPU1 (the application core) writes commands into the Mailbox, and CPU2 (the secure co-processor) reacts with responses. To notify the other CPU that data has been written into the Mailbox, notifications get sent via the IPCC.

Sections given in the following will refer to AN5185 - "ST firmware upgrade services for the STM32WB Series". Note that I did not check if the same bugs also happen in the wireless stack of CPU2, but i cannot think of a reason why not as the communication code of CPU1 seems to have many similarities between FUS communication and wireless stack communication code.


For commands and responses, 2 bytes are used as opcode (section 6.3). Of those 2 bytes, the first 6 bits need to be ones (b'111111XX XXXXXXXX). However, the bytes are actually swapped in memory, so b'XXXXXXXX 111111XX is actually expected. For example, to send the opcode 0xFC52, one actually needs to write 0x52FC into the RAM. The reason here is because the STM code writes and reads a uint16_t into memory, and a uint16-t is byte-swapped.


The spec says (section 6.1.2, address of system command / response buffer) that commands and responses are written to the same memory address and overwrite each other. A pointer points to the buffer to write into. Each buffer contains one command or response packet, defined in 6.3 (vendor specific HCI command packet & HCI command complete event packet).

But actually, the command buffer is offset by 8 bytes, so one needs to start writing the command buffer 8 bytes after the address given by the pointer. The response buffer does not have this problem.

0693W00000JOwmEQAT.jpgThe reason here is that the STM code directly writes the struct TL_CmdPacket_t (from interface/patterns/ble_thread/tl/ in the Middleware STM32_WPAN) into RAM, which contains both the TL_PacketHeader_t (which contains two pointers, aka 8 byte) and TL_CmdSerial_t, the command buffer itself. The proper way would be to just write the TL_CmdSerial_t into RAM.

Note that the structure being bigger than intended does not have any consequences with data overflowing into the next structure, because all structures are defined as C structs placed in a linker section defining the Mailbox, so all following structs automatically get placed behind the command buffer by the compiler.


For the last point I am not sure because the spec is a bit ambiguous there, regarding the IPCC notification system. As far as I understand it, the CPU1 should notify CPU2 via the TX channel when a command has been written into the Mailbox. Then CPU2 processes the command and removes the notification from the TX channel. When the response is ready, CPU2 writes the response buffer into the Mailbox and notifies CPU1 via the RX channel. CPU1 processes the response, then removes the notification.

However, in reality only the TX channel gets used for this command-response communication. So CPU1 writes the command buffer into the Mailbox, notifies CPU2 via the TX channel. Then CPU2 processes it, writes the response into the Mailbox and notifies CPU1 by removing the notification from the TX channel.

The spec does not specify how exactly the IPCC gets used, but it is a bit unintuitive that the RX channel does not get used to notify CPU1 of responses.

I guess the easiest path for STM here would be to change the specification as fixing the bugs would mean that e.g. newer, fixed versions of the FUS could no longer talk to older, buggy application code running on CPU1.