USB HOST library is not thread safe
USB HOST library is not thread safe (RTOS mode). Documentation UM1720 does not warn about it. It even gives false belief it is safe.
5.3 Using the host library in RTOS mode
[...]
When the RTOS mode is used the host core background process runs as a separate RTOS task. The communication between the application task and the host core task uses the RTOS message queue mechanism. The CMSIS RTOS APIs are used to support the RTOS mode.
[...]
MSC class does not provide protection against NULL pointer dereference and modification of deallocated memory (free).
I did not check other classess but I assume the bug is also there as the defect is generic.
The problem is in:
MSC_HandleTypeDef *MSC_Handle = (MSC_HandleTypeDef *) phost->pActiveClass->pData;
pActiveClass and pData are controlled by USBH_Process. It assignes address tp pActiveClass and malloc/free pData.
RTOS can switch from thread reading a file (application) to the USBH_Process thread, execute HOST_DEV_DISCONNECTED case and go back to file reading thread.
Is ST aware of this bug and when it may be repaired?
A good solution would be that file reading thread would not read directly but pass a request to the USBH_Process via message queue mechanism.
Then USBH_Process would do the job. This solution is safe because there would be only one thread which uses and modifies the data.
Generally CLASS code and CORE code inter-operationality in buggy.
USBH_StatusTypeDef USBH_Process(USBH_HandleTypeDef *phost)
{
__IO USBH_StatusTypeDef status = USBH_FAIL;
uint8_t idx = 0U;
/* check for Host pending port disconnect event */
if (phost->device.is_disconnected == 1U)
{
phost->gState = HOST_DEV_DISCONNECTED;
}
[...]
case HOST_DEV_DISCONNECTED :
phost->device.is_disconnected = 0U;
DeInitStateMachine(phost);
/* Re-Initilaize Host for new Enumeration */
if (phost->pActiveClass != NULL)
{
phost->pActiveClass->DeInit(phost); // COMMENT: It deallocates phost->pActiveClass->pData (i.e. MSC_HandleTypeDef).
phost->pActiveClass = NULL;
// COMMENT: Now RTOS can switch back to file reading thread and we have a big problem.
}
[...]
}USBH_StatusTypeDef USBH_MSC_Read(USBH_HandleTypeDef *phost,
uint8_t lun,
uint32_t address,
uint8_t *pbuf,
uint32_t length)
{
uint32_t timeout;
MSC_HandleTypeDef *MSC_Handle = (MSC_HandleTypeDef *) phost->pActiveClass->pData;
// COMMENT: BUG: Possible NULL pointer dereference
if ((phost->device.is_connected == 0U) ||
(phost->gState != HOST_CLASS) ||
(MSC_Handle->unit[lun].state != MSC_IDLE))
{
return USBH_FAIL;
}
MSC_Handle->state = MSC_READ;
MSC_Handle->unit[lun].state = MSC_READ;
MSC_Handle->rw_lun = lun;
USBH_MSC_SCSI_Read(phost, lun, address, pbuf, length);
timeout = phost->Timer;
// COMMENT: USBH_MSC_RdWrProcess dereferences pActiveClass and uses pData without any check. It is done in a loop, so very often.
while (USBH_MSC_RdWrProcess(phost, lun) == USBH_BUSY)
{
if (((phost->Timer - timeout) > (10000U * length)) || (phost->device.is_connected == 0U))
{
MSC_Handle->state = MSC_IDLE;
return USBH_FAIL;
}
}
MSC_Handle->state = MSC_IDLE;
return USBH_OK;
}What is also bad in this library is that USBH_MSC_RdWrProcess is not releasing the CPU - no semaphore, no message queue. It causes a drop in performance, especially when thread has high priority.
