Commit 60829145 authored by M Chetan Kumar's avatar M Chetan Kumar Committed by David S. Miller

net: wwan: iosm: fix NULL pointer dereference when removing device

In suspend and resume cycle, the removal and rescan of device ends
up in NULL pointer dereference.

During driver initialization, if the ipc_imem_wwan_channel_init()
fails to get the valid device capabilities it returns an error and
further no resource (wwan struct) will be allocated. Now in this
situation if driver removal procedure is initiated it would result
in NULL pointer exception since unallocated wwan struct is dereferenced
inside ipc_wwan_deinit().

ipc_imem_run_state_worker() to handle the called functions return value
and to release the resource in failure case. It also reports the link
down event in failure cases. The user space application can handle this
event to do a device reset for restoring the device communication.

Fixes: 3670970d ("net: iosm: shared memory IPC interface")
Reported-by: default avatarSamuel Wein PhD <sam@samwein.com>
Closes: https://lore.kernel.org/netdev/20230427140819.1310f4bd@kernel.org/T/Signed-off-by: default avatarM Chetan Kumar <m.chetan.kumar@linux.intel.com>
Reviewed-by: default avatarSimon Horman <simon.horman@corigine.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent dacab578
...@@ -565,23 +565,31 @@ static void ipc_imem_run_state_worker(struct work_struct *instance) ...@@ -565,23 +565,31 @@ static void ipc_imem_run_state_worker(struct work_struct *instance)
struct ipc_mux_config mux_cfg; struct ipc_mux_config mux_cfg;
struct iosm_imem *ipc_imem; struct iosm_imem *ipc_imem;
u8 ctrl_chl_idx = 0; u8 ctrl_chl_idx = 0;
int ret;
ipc_imem = container_of(instance, struct iosm_imem, run_state_worker); ipc_imem = container_of(instance, struct iosm_imem, run_state_worker);
if (ipc_imem->phase != IPC_P_RUN) { if (ipc_imem->phase != IPC_P_RUN) {
dev_err(ipc_imem->dev, dev_err(ipc_imem->dev,
"Modem link down. Exit run state worker."); "Modem link down. Exit run state worker.");
return; goto err_out;
} }
if (test_and_clear_bit(IOSM_DEVLINK_INIT, &ipc_imem->flag)) if (test_and_clear_bit(IOSM_DEVLINK_INIT, &ipc_imem->flag))
ipc_devlink_deinit(ipc_imem->ipc_devlink); ipc_devlink_deinit(ipc_imem->ipc_devlink);
if (!ipc_imem_setup_cp_mux_cap_init(ipc_imem, &mux_cfg)) ret = ipc_imem_setup_cp_mux_cap_init(ipc_imem, &mux_cfg);
if (ret < 0)
goto err_out;
ipc_imem->mux = ipc_mux_init(&mux_cfg, ipc_imem); ipc_imem->mux = ipc_mux_init(&mux_cfg, ipc_imem);
if (!ipc_imem->mux)
goto err_out;
ret = ipc_imem_wwan_channel_init(ipc_imem, mux_cfg.protocol);
if (ret < 0)
goto err_ipc_mux_deinit;
ipc_imem_wwan_channel_init(ipc_imem, mux_cfg.protocol);
if (ipc_imem->mux)
ipc_imem->mux->wwan = ipc_imem->wwan; ipc_imem->mux->wwan = ipc_imem->wwan;
while (ctrl_chl_idx < IPC_MEM_MAX_CHANNELS) { while (ctrl_chl_idx < IPC_MEM_MAX_CHANNELS) {
...@@ -622,6 +630,13 @@ static void ipc_imem_run_state_worker(struct work_struct *instance) ...@@ -622,6 +630,13 @@ static void ipc_imem_run_state_worker(struct work_struct *instance)
/* Complete all memory stores after setting bit */ /* Complete all memory stores after setting bit */
smp_mb__after_atomic(); smp_mb__after_atomic();
return;
err_ipc_mux_deinit:
ipc_mux_deinit(ipc_imem->mux);
err_out:
ipc_uevent_send(ipc_imem->dev, UEVENT_CD_READY_LINK_DOWN);
} }
static void ipc_imem_handle_irq(struct iosm_imem *ipc_imem, int irq) static void ipc_imem_handle_irq(struct iosm_imem *ipc_imem, int irq)
......
...@@ -77,7 +77,7 @@ int ipc_imem_sys_wwan_transmit(struct iosm_imem *ipc_imem, ...@@ -77,7 +77,7 @@ int ipc_imem_sys_wwan_transmit(struct iosm_imem *ipc_imem,
} }
/* Initialize wwan channel */ /* Initialize wwan channel */
void ipc_imem_wwan_channel_init(struct iosm_imem *ipc_imem, int ipc_imem_wwan_channel_init(struct iosm_imem *ipc_imem,
enum ipc_mux_protocol mux_type) enum ipc_mux_protocol mux_type)
{ {
struct ipc_chnl_cfg chnl_cfg = { 0 }; struct ipc_chnl_cfg chnl_cfg = { 0 };
...@@ -87,7 +87,7 @@ void ipc_imem_wwan_channel_init(struct iosm_imem *ipc_imem, ...@@ -87,7 +87,7 @@ void ipc_imem_wwan_channel_init(struct iosm_imem *ipc_imem,
/* If modem version is invalid (0xffffffff), do not initialize WWAN. */ /* If modem version is invalid (0xffffffff), do not initialize WWAN. */
if (ipc_imem->cp_version == -1) { if (ipc_imem->cp_version == -1) {
dev_err(ipc_imem->dev, "invalid CP version"); dev_err(ipc_imem->dev, "invalid CP version");
return; return -EIO;
} }
ipc_chnl_cfg_get(&chnl_cfg, ipc_imem->nr_of_channels); ipc_chnl_cfg_get(&chnl_cfg, ipc_imem->nr_of_channels);
...@@ -104,9 +104,13 @@ void ipc_imem_wwan_channel_init(struct iosm_imem *ipc_imem, ...@@ -104,9 +104,13 @@ void ipc_imem_wwan_channel_init(struct iosm_imem *ipc_imem,
/* WWAN registration. */ /* WWAN registration. */
ipc_imem->wwan = ipc_wwan_init(ipc_imem, ipc_imem->dev); ipc_imem->wwan = ipc_wwan_init(ipc_imem, ipc_imem->dev);
if (!ipc_imem->wwan) if (!ipc_imem->wwan) {
dev_err(ipc_imem->dev, dev_err(ipc_imem->dev,
"failed to register the ipc_wwan interfaces"); "failed to register the ipc_wwan interfaces");
return -ENOMEM;
}
return 0;
} }
/* Map SKB to DMA for transfer */ /* Map SKB to DMA for transfer */
......
...@@ -91,8 +91,10 @@ int ipc_imem_sys_wwan_transmit(struct iosm_imem *ipc_imem, int if_id, ...@@ -91,8 +91,10 @@ int ipc_imem_sys_wwan_transmit(struct iosm_imem *ipc_imem, int if_id,
* MUX. * MUX.
* @ipc_imem: Pointer to iosm_imem struct. * @ipc_imem: Pointer to iosm_imem struct.
* @mux_type: Type of mux protocol. * @mux_type: Type of mux protocol.
*
* Return: 0 on success and failure value on error
*/ */
void ipc_imem_wwan_channel_init(struct iosm_imem *ipc_imem, int ipc_imem_wwan_channel_init(struct iosm_imem *ipc_imem,
enum ipc_mux_protocol mux_type); enum ipc_mux_protocol mux_type);
/** /**
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment