firmware: arm_scmi: Free mailbox channels if probe fails
PATCH v2 1/1 firmware: arm_scmi: Free mailbox channels if probe fails
Mailbox channels for the base protocol are setup during probe. There can be a scenario where probe fails to acquire the base protocol due to a timeout leading to cleaning up of all device managed memory including the scmi_mailbox structure setup during mailbox_chan_setup function.
[ 12.735104]arm-scmi soc:qcom,scmi: timed out in resp(caller: version_get+0x84/0x140) [ 12.735224]arm-scmi soc:qcom,scmi: unable to communicate with SCMI [ 12.735947]arm-scmi: probe of soc:qcom,scmi failed with error -110
Now when a message arrives at cpu slightly after the timeout, the mailbox controller will try to call the rx_callback of the client and might end up accessing freed memory.
[ 12.758363][ C0] Call trace: [ 12.758367][ C0] rx_callback+0x24/0x160 [ 12.758372][ C0] mbox_chan_received_data+0x44/0x94 [ 12.758386][ C0] __handle_irq_event_percpu+0xd4/0x240
This patch frees the mailbox channels setup during probe and adds some more error handling in case the probe fails.
--- drivers/firmware/arm_scmi/driver.c | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-)
drivers/firmware/arm_scmi/driver.c +24 -11
@@ -1390,6 +1390,21 @@ void scmi_protocol_device_unrequest(const struct scmi_device_id *id_table)mutex_unlock (&scmi_requested_devices_mtx); } +static int cleanup_txrx_channels (struct scmi_info *info) +{ +int ret; +struct idr *idr = &info->tx_idr; + + ret =idr_for_each (idr, info->desc->ops->chan_free, idr); +idr_destroy (&info->tx_idr); + + idr = &info->rx_idr; + ret =idr_for_each (idr, info->desc->ops->chan_free, idr); +idr_destroy (&info->rx_idr); + +return ret; +} +static int scmi_probe (struct platform_device *pdev) {int ret;
@@ -1430,7 +1445,7 @@ static int scmi_probe(struct platform_device *pdev) ret =scmi_xfer_info_init (info);if (ret) -return ret; + goto clear_txrx_setup;if (scmi_notification_init (handle))dev_err (dev,"SCMI Notifications NOT available.\n" );
@@ -1443,7 +1458,7 @@ static int scmi_probe(struct platform_device *pdev) ret =scmi_protocol_acquire (handle,SCMI_PROTOCOL_BASE );if (ret) {dev_err (dev,"unable to communicate with SCMI\n" ); -return ret; + goto notification_exit; }mutex_lock (&scmi_list_mutex);
@@ -1482,6 +1497,12 @@ static int scmi_probe(struct platform_device *pdev) }return 0 ; + +notification_exit: +scmi_notification_exit (&info->handle); +clear_txrx_setup: +cleanup_txrx_channels (info); +return ret; }void scmi_free_channel (struct scmi_chan_info *cinfo,struct idr *idr,int id)
@@ -1493,7 +1514,6 @@ static int scmi_remove(struct platform_device *pdev) {int ret =0 , id;struct scmi_info *info =platform_get_drvdata (pdev); -struct idr *idr = &info->tx_idr;struct device_node *child;mutex_lock (&scmi_list_mutex);
@@ -1517,14 +1537,7 @@ static int scmi_remove(struct platform_device *pdev)idr_destroy (&info->active_protocols);/* Safe to free channels since no more users */ - ret =idr_for_each (idr, info->desc->ops->chan_free, idr); -idr_destroy (&info->tx_idr); - - idr = &info->rx_idr; - ret =idr_for_each (idr, info->desc->ops->chan_free, idr); -idr_destroy (&info->rx_idr); - -return ret; +return cleanup_txrx_channels (info); } static ssize_t protocol_version_show(struct device *dev,
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project
Quoted 16 lines · Rishabh Bhatnagar
Mailbox channels for the base protocol are setup during probe. There can be a scenario where probe fails to acquire the base protocol due to a timeout leading to cleaning up of all device managed memory including the
scmi_mailboxstructure setup duringmailbox_chan_setupfunction.[ 12.735104]arm-scmi soc:qcom,scmi: timed out in resp(caller: version_get+0x84/0x140) [ 12.735224]arm-scmi soc:qcom,scmi: unable to communicate with SCMI [ 12.735947]arm-scmi: probe of soc:qcom,scmi failed with error -110Now when a message arrives at cpu slightly after the timeout, the mailbox controller will try to call the
rx_callbackof the client and might end up accessing freed memory.[ 12.758363][ C0] Call trace: [ 12.758367][ C0] rx_callback+0x24/0x160 [ 12.758372][ C0] mbox_chan_received_data+0x44/0x94 [ 12.758386][ C0] __handle_irq_event_percpu+0xd4/0x240
Quoted 5 lines
This patch frees the mailbox channels setup during probe and adds some more error handling in case the probe fails.
Signed-off-by Rishabh Bhatnagar <[email protected]>---
Hi,
Quoted 16 lines
drivers/firmware/arm_scmi/driver.c | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-)drivers/firmware/arm_scmi/driver.c +5
-0@@ -1390,6 +1390,21 @@ void scmi_protocol_device_unrequest(const struct scmi_device_id *id_table)mutex_unlock (&scmi_requested_devices_mtx); } +static int cleanup_txrx_channels (struct scmi_info *info) +{ +int ret; +struct idr *idr = &info->tx_idr; +
drivers/firmware/arm_scmi/driver.c void scmi_protocol_device_unrequest(const struct scmi_device_id *id_table) · line 1407
Quoted 10 lines
drivers/firmware/arm_scmi/driver.c +10
-0+ ret =idr_for_each (idr, info->desc->ops->chan_free, idr); +idr_destroy (&info->tx_idr); + + idr = &info->rx_idr; + ret =idr_for_each (idr, info->desc->ops->chan_free, idr); +idr_destroy (&info->rx_idr); + +return ret; +} +
Sorry for being pedantic but the preferred naming for the function should be scmi_cleanup_txrx_channels()
Quoted 47 lines
drivers/firmware/arm_scmi/driver.c +8
-4static int scmi_probe (struct platform_device *pdev) {int ret;@@ -1430,7 +1445,7 @@ static int scmi_probe(struct platform_device *pdev) ret =scmi_xfer_info_init (info);if (ret) -return ret; + goto clear_txrx_setup;if (scmi_notification_init (handle))dev_err (dev,"SCMI Notifications NOT available.\n" );@@ -1443,7 +1458,7 @@ static int scmi_probe(struct platform_device *pdev) ret =scmi_protocol_acquire (handle,SCMI_PROTOCOL_BASE );if (ret) {dev_err (dev,"unable to communicate with SCMI\n" ); -return ret; + goto notification_exit; }mutex_lock (&scmi_list_mutex);@@ -1482,6 +1497,12 @@ static int scmi_probe(struct platform_device *pdev) }return 0 ; + +notification_exit: +scmi_notification_exit (&info->handle); +clear_txrx_setup: +cleanup_txrx_channels (info); +return ret; }void scmi_free_channel (struct scmi_chan_info *cinfo,struct idr *idr,int id)@@ -1493,7 +1514,6 @@ static int scmi_remove(struct platform_device *pdev) {int ret =0 , id;struct scmi_info *info =platform_get_drvdata (pdev); -struct idr *idr = &info->tx_idr;struct device_node *child;mutex_lock (&scmi_list_mutex);@@ -1517,14 +1537,7 @@ static int scmi_remove(struct platform_device *pdev)idr_destroy (&info->active_protocols);/* Safe to free channels since no more users */ - ret =idr_for_each (idr, info->desc->ops->chan_free, idr);
drivers/firmware/arm_scmi/driver.c static int scmi_remove(struct platform_device *pdev) · line 1542
Quoted 10 lines
drivers/firmware/arm_scmi/driver.c +1
-7-idr_destroy (&info->tx_idr); - - idr = &info->rx_idr; - ret =idr_for_each (idr, info->desc->ops->chan_free, idr); -idr_destroy (&info->rx_idr); - -return ret; +return cleanup_txrx_channels (info); }
Maybe also a Fixes: tag could be appropriate, but I'm not so sure you could easily identify one common commit to use given that notifications have been added later and the code around scmi_protocol_acquire() has been changed recently too. (in such a case just ignore the Fixes tag suggestion)
Other than the above remarks,
Thanks,
Cristian