Commit 7b0ddc13 authored by Mike Christie's avatar Mike Christie Committed by Martin K. Petersen

scsi: be2iscsi: Fix use-after-free during IP updates

This fixes a bug found by Lv Yunlong where, because beiscsi_exec_nemb_cmd()
frees memory for the be_dma_mem cmd(), we can access freed memory when
beiscsi_if_clr_ip()/beiscsi_if_set_ip()'s call to beiscsi_exec_nemb_cmd()
fails and we access the freed req. This fixes the issue by having the
caller free the cmd's memory.

Link: https://lore.kernel.org/r/20210701190840.175120-1-michael.christie@oracle.comReported-by: default avatarLv Yunlong <lyl2019@mail.ustc.edu.cn>
Signed-off-by: default avatarMike Christie <michael.christie@oracle.com>
Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent 1c0810e7
...@@ -235,8 +235,7 @@ static int beiscsi_exec_nemb_cmd(struct beiscsi_hba *phba, ...@@ -235,8 +235,7 @@ static int beiscsi_exec_nemb_cmd(struct beiscsi_hba *phba,
wrb = alloc_mcc_wrb(phba, &tag); wrb = alloc_mcc_wrb(phba, &tag);
if (!wrb) { if (!wrb) {
mutex_unlock(&ctrl->mbox_lock); mutex_unlock(&ctrl->mbox_lock);
rc = -ENOMEM; return -ENOMEM;
goto free_cmd;
} }
sge = nonembedded_sgl(wrb); sge = nonembedded_sgl(wrb);
...@@ -269,24 +268,6 @@ static int beiscsi_exec_nemb_cmd(struct beiscsi_hba *phba, ...@@ -269,24 +268,6 @@ static int beiscsi_exec_nemb_cmd(struct beiscsi_hba *phba,
/* copy the response, if any */ /* copy the response, if any */
if (resp_buf) if (resp_buf)
memcpy(resp_buf, nonemb_cmd->va, resp_buf_len); memcpy(resp_buf, nonemb_cmd->va, resp_buf_len);
/**
* This is special case of NTWK_GET_IF_INFO where the size of
* response is not known. beiscsi_if_get_info checks the return
* value to free DMA buffer.
*/
if (rc == -EAGAIN)
return rc;
/**
* If FW is busy that is driver timed out, DMA buffer is saved with
* the tag, only when the cmd completes this buffer is freed.
*/
if (rc == -EBUSY)
return rc;
free_cmd:
dma_free_coherent(&ctrl->pdev->dev, nonemb_cmd->size,
nonemb_cmd->va, nonemb_cmd->dma);
return rc; return rc;
} }
...@@ -309,6 +290,19 @@ static int beiscsi_prep_nemb_cmd(struct beiscsi_hba *phba, ...@@ -309,6 +290,19 @@ static int beiscsi_prep_nemb_cmd(struct beiscsi_hba *phba,
return 0; return 0;
} }
static void beiscsi_free_nemb_cmd(struct beiscsi_hba *phba,
struct be_dma_mem *cmd, int rc)
{
/*
* If FW is busy the DMA buffer is saved with the tag. When the cmd
* completes this buffer is freed.
*/
if (rc == -EBUSY)
return;
dma_free_coherent(&phba->ctrl.pdev->dev, cmd->size, cmd->va, cmd->dma);
}
static void __beiscsi_eq_delay_compl(struct beiscsi_hba *phba, unsigned int tag) static void __beiscsi_eq_delay_compl(struct beiscsi_hba *phba, unsigned int tag)
{ {
struct be_dma_mem *tag_mem; struct be_dma_mem *tag_mem;
...@@ -344,8 +338,16 @@ int beiscsi_modify_eq_delay(struct beiscsi_hba *phba, ...@@ -344,8 +338,16 @@ int beiscsi_modify_eq_delay(struct beiscsi_hba *phba,
cpu_to_le32(set_eqd[i].delay_multiplier); cpu_to_le32(set_eqd[i].delay_multiplier);
} }
return beiscsi_exec_nemb_cmd(phba, &nonemb_cmd, rc = beiscsi_exec_nemb_cmd(phba, &nonemb_cmd, __beiscsi_eq_delay_compl,
__beiscsi_eq_delay_compl, NULL, 0); NULL, 0);
if (rc) {
/*
* Only free on failure. Async cmds are handled like -EBUSY
* where it's handled for us.
*/
beiscsi_free_nemb_cmd(phba, &nonemb_cmd, rc);
}
return rc;
} }
/** /**
...@@ -372,6 +374,7 @@ int beiscsi_get_initiator_name(struct beiscsi_hba *phba, char *name, bool cfg) ...@@ -372,6 +374,7 @@ int beiscsi_get_initiator_name(struct beiscsi_hba *phba, char *name, bool cfg)
req->hdr.version = 1; req->hdr.version = 1;
rc = beiscsi_exec_nemb_cmd(phba, &nonemb_cmd, NULL, rc = beiscsi_exec_nemb_cmd(phba, &nonemb_cmd, NULL,
&resp, sizeof(resp)); &resp, sizeof(resp));
beiscsi_free_nemb_cmd(phba, &nonemb_cmd, rc);
if (rc) { if (rc) {
beiscsi_log(phba, KERN_ERR, beiscsi_log(phba, KERN_ERR,
BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX, BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX,
...@@ -449,7 +452,9 @@ static int beiscsi_if_mod_gw(struct beiscsi_hba *phba, ...@@ -449,7 +452,9 @@ static int beiscsi_if_mod_gw(struct beiscsi_hba *phba,
req->ip_addr.ip_type = ip_type; req->ip_addr.ip_type = ip_type;
memcpy(req->ip_addr.addr, gw, memcpy(req->ip_addr.addr, gw,
(ip_type < BEISCSI_IP_TYPE_V6) ? IP_V4_LEN : IP_V6_LEN); (ip_type < BEISCSI_IP_TYPE_V6) ? IP_V4_LEN : IP_V6_LEN);
return beiscsi_exec_nemb_cmd(phba, &nonemb_cmd, NULL, NULL, 0); rt_val = beiscsi_exec_nemb_cmd(phba, &nonemb_cmd, NULL, NULL, 0);
beiscsi_free_nemb_cmd(phba, &nonemb_cmd, rt_val);
return rt_val;
} }
int beiscsi_if_set_gw(struct beiscsi_hba *phba, u32 ip_type, u8 *gw) int beiscsi_if_set_gw(struct beiscsi_hba *phba, u32 ip_type, u8 *gw)
...@@ -499,8 +504,10 @@ int beiscsi_if_get_gw(struct beiscsi_hba *phba, u32 ip_type, ...@@ -499,8 +504,10 @@ int beiscsi_if_get_gw(struct beiscsi_hba *phba, u32 ip_type,
req = nonemb_cmd.va; req = nonemb_cmd.va;
req->ip_type = ip_type; req->ip_type = ip_type;
return beiscsi_exec_nemb_cmd(phba, &nonemb_cmd, NULL, rc = beiscsi_exec_nemb_cmd(phba, &nonemb_cmd, NULL, resp,
resp, sizeof(*resp)); sizeof(*resp));
beiscsi_free_nemb_cmd(phba, &nonemb_cmd, rc);
return rc;
} }
static int static int
...@@ -537,6 +544,7 @@ beiscsi_if_clr_ip(struct beiscsi_hba *phba, ...@@ -537,6 +544,7 @@ beiscsi_if_clr_ip(struct beiscsi_hba *phba,
"BG_%d : failed to clear IP: rc %d status %d\n", "BG_%d : failed to clear IP: rc %d status %d\n",
rc, req->ip_params.ip_record.status); rc, req->ip_params.ip_record.status);
} }
beiscsi_free_nemb_cmd(phba, &nonemb_cmd, rc);
return rc; return rc;
} }
...@@ -581,6 +589,7 @@ beiscsi_if_set_ip(struct beiscsi_hba *phba, u8 *ip, ...@@ -581,6 +589,7 @@ beiscsi_if_set_ip(struct beiscsi_hba *phba, u8 *ip,
if (req->ip_params.ip_record.status) if (req->ip_params.ip_record.status)
rc = -EINVAL; rc = -EINVAL;
} }
beiscsi_free_nemb_cmd(phba, &nonemb_cmd, rc);
return rc; return rc;
} }
...@@ -608,6 +617,7 @@ int beiscsi_if_en_static(struct beiscsi_hba *phba, u32 ip_type, ...@@ -608,6 +617,7 @@ int beiscsi_if_en_static(struct beiscsi_hba *phba, u32 ip_type,
reldhcp->interface_hndl = phba->interface_handle; reldhcp->interface_hndl = phba->interface_handle;
reldhcp->ip_type = ip_type; reldhcp->ip_type = ip_type;
rc = beiscsi_exec_nemb_cmd(phba, &nonemb_cmd, NULL, NULL, 0); rc = beiscsi_exec_nemb_cmd(phba, &nonemb_cmd, NULL, NULL, 0);
beiscsi_free_nemb_cmd(phba, &nonemb_cmd, rc);
if (rc < 0) { if (rc < 0) {
beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_CONFIG, beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_CONFIG,
"BG_%d : failed to release existing DHCP: %d\n", "BG_%d : failed to release existing DHCP: %d\n",
...@@ -689,7 +699,7 @@ int beiscsi_if_en_dhcp(struct beiscsi_hba *phba, u32 ip_type) ...@@ -689,7 +699,7 @@ int beiscsi_if_en_dhcp(struct beiscsi_hba *phba, u32 ip_type)
dhcpreq->interface_hndl = phba->interface_handle; dhcpreq->interface_hndl = phba->interface_handle;
dhcpreq->ip_type = ip_type; dhcpreq->ip_type = ip_type;
rc = beiscsi_exec_nemb_cmd(phba, &nonemb_cmd, NULL, NULL, 0); rc = beiscsi_exec_nemb_cmd(phba, &nonemb_cmd, NULL, NULL, 0);
beiscsi_free_nemb_cmd(phba, &nonemb_cmd, rc);
exit: exit:
kfree(if_info); kfree(if_info);
return rc; return rc;
...@@ -762,11 +772,8 @@ int beiscsi_if_get_info(struct beiscsi_hba *phba, int ip_type, ...@@ -762,11 +772,8 @@ int beiscsi_if_get_info(struct beiscsi_hba *phba, int ip_type,
BEISCSI_LOG_INIT | BEISCSI_LOG_CONFIG, BEISCSI_LOG_INIT | BEISCSI_LOG_CONFIG,
"BG_%d : Memory Allocation Failure\n"); "BG_%d : Memory Allocation Failure\n");
/* Free the DMA memory for the IOCTL issuing */ beiscsi_free_nemb_cmd(phba, &nonemb_cmd,
dma_free_coherent(&phba->ctrl.pdev->dev, -ENOMEM);
nonemb_cmd.size,
nonemb_cmd.va,
nonemb_cmd.dma);
return -ENOMEM; return -ENOMEM;
} }
...@@ -781,15 +788,13 @@ int beiscsi_if_get_info(struct beiscsi_hba *phba, int ip_type, ...@@ -781,15 +788,13 @@ int beiscsi_if_get_info(struct beiscsi_hba *phba, int ip_type,
nonemb_cmd.va)->actual_resp_len; nonemb_cmd.va)->actual_resp_len;
ioctl_size += sizeof(struct be_cmd_req_hdr); ioctl_size += sizeof(struct be_cmd_req_hdr);
/* Free the previous allocated DMA memory */ beiscsi_free_nemb_cmd(phba, &nonemb_cmd, rc);
dma_free_coherent(&phba->ctrl.pdev->dev, nonemb_cmd.size,
nonemb_cmd.va,
nonemb_cmd.dma);
/* Free the virtual memory */ /* Free the virtual memory */
kfree(*if_info); kfree(*if_info);
} else } else {
beiscsi_free_nemb_cmd(phba, &nonemb_cmd, rc);
break; break;
}
} while (true); } while (true);
return rc; return rc;
} }
...@@ -806,8 +811,9 @@ int mgmt_get_nic_conf(struct beiscsi_hba *phba, ...@@ -806,8 +811,9 @@ int mgmt_get_nic_conf(struct beiscsi_hba *phba,
if (rc) if (rc)
return rc; return rc;
return beiscsi_exec_nemb_cmd(phba, &nonemb_cmd, NULL, rc = beiscsi_exec_nemb_cmd(phba, &nonemb_cmd, NULL, nic, sizeof(*nic));
nic, sizeof(*nic)); beiscsi_free_nemb_cmd(phba, &nonemb_cmd, rc);
return rc;
} }
static void beiscsi_boot_process_compl(struct beiscsi_hba *phba, static void beiscsi_boot_process_compl(struct beiscsi_hba *phba,
......
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