mhi: core: fix potential buffer overflow
Fix a potential buffer overflow that could occur during firmware download by storing buffer address locally instead of inside MHI context. CRs-Fixed: 1109397 Change-Id: If27ba602cdafdc8d25a94fdc0d74e8970bf0b0f4 Signed-off-by: Sujeev Dias <sdias@codeaurora.org>
This commit is contained in:
parent
60be71604a
commit
632e3c71d3
2 changed files with 36 additions and 55 deletions
|
@ -82,12 +82,6 @@ struct bhie_vec_table {
|
|||
|
||||
struct bhi_ctxt_t {
|
||||
void __iomem *bhi_base;
|
||||
void *unaligned_image_loc;
|
||||
dma_addr_t dma_handle;
|
||||
size_t alloc_size;
|
||||
void *image_loc;
|
||||
dma_addr_t phy_image_loc;
|
||||
size_t image_size;
|
||||
dev_t bhi_dev;
|
||||
struct cdev cdev;
|
||||
struct device *dev;
|
||||
|
|
|
@ -109,30 +109,29 @@ alloc_bhi_mem_info_error:
|
|||
}
|
||||
|
||||
static int bhi_alloc_pbl_xfer(struct mhi_device_ctxt *mhi_dev_ctxt,
|
||||
struct bhie_mem_info *const mem_info,
|
||||
size_t size)
|
||||
{
|
||||
struct bhi_ctxt_t *bhi_ctxt = &mhi_dev_ctxt->bhi_ctxt;
|
||||
const phys_addr_t align_len = bhi_ctxt->alignment;
|
||||
size_t alloc_size = size + (align_len - 1);
|
||||
struct device *dev = &mhi_dev_ctxt->plat_dev->dev;
|
||||
|
||||
bhi_ctxt->unaligned_image_loc =
|
||||
dma_alloc_coherent(dev, alloc_size, &bhi_ctxt->dma_handle,
|
||||
GFP_KERNEL);
|
||||
if (bhi_ctxt->unaligned_image_loc == NULL)
|
||||
mem_info->size = size;
|
||||
mem_info->alloc_size = size + (align_len - 1);
|
||||
mem_info->pre_aligned =
|
||||
dma_alloc_coherent(dev, mem_info->alloc_size,
|
||||
&mem_info->dma_handle, GFP_KERNEL);
|
||||
if (mem_info->pre_aligned == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
bhi_ctxt->alloc_size = alloc_size;
|
||||
bhi_ctxt->phy_image_loc = (bhi_ctxt->dma_handle + (align_len - 1)) &
|
||||
mem_info->phys_addr = (mem_info->dma_handle + (align_len - 1)) &
|
||||
~(align_len - 1);
|
||||
bhi_ctxt->image_loc = bhi_ctxt->unaligned_image_loc +
|
||||
(bhi_ctxt->phy_image_loc - bhi_ctxt->dma_handle);
|
||||
bhi_ctxt->image_size = size;
|
||||
|
||||
mem_info->aligned = mem_info->pre_aligned + (mem_info->phys_addr -
|
||||
mem_info->dma_handle);
|
||||
mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
|
||||
"alloc_size:%lu image_size:%lu unal_addr:0x%llx0x al_addr:0x%llx\n",
|
||||
bhi_ctxt->alloc_size, bhi_ctxt->image_size,
|
||||
bhi_ctxt->dma_handle, bhi_ctxt->phy_image_loc);
|
||||
mem_info->alloc_size, mem_info->size,
|
||||
mem_info->dma_handle, mem_info->phys_addr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -264,7 +263,8 @@ int bhi_rddm(struct mhi_device_ctxt *mhi_dev_ctxt, bool in_panic)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int bhi_load_firmware(struct mhi_device_ctxt *mhi_dev_ctxt)
|
||||
static int bhi_load_firmware(struct mhi_device_ctxt *mhi_dev_ctxt,
|
||||
const struct bhie_mem_info *const mem_info)
|
||||
{
|
||||
struct bhi_ctxt_t *bhi_ctxt = &mhi_dev_ctxt->bhi_ctxt;
|
||||
u32 pcie_word_val = 0;
|
||||
|
@ -278,28 +278,21 @@ static int bhi_load_firmware(struct mhi_device_ctxt *mhi_dev_ctxt)
|
|||
read_unlock_bh(pm_xfer_lock);
|
||||
return -EIO;
|
||||
}
|
||||
pcie_word_val = HIGH_WORD(bhi_ctxt->phy_image_loc);
|
||||
mhi_reg_write_field(mhi_dev_ctxt, bhi_ctxt->bhi_base,
|
||||
BHI_IMGADDR_HIGH,
|
||||
0xFFFFFFFF,
|
||||
0,
|
||||
pcie_word_val);
|
||||
pcie_word_val = HIGH_WORD(mem_info->phys_addr);
|
||||
mhi_reg_write_field(mhi_dev_ctxt, bhi_ctxt->bhi_base, BHI_IMGADDR_HIGH,
|
||||
0xFFFFFFFF, 0, pcie_word_val);
|
||||
|
||||
pcie_word_val = LOW_WORD(bhi_ctxt->phy_image_loc);
|
||||
pcie_word_val = LOW_WORD(mem_info->phys_addr);
|
||||
mhi_reg_write_field(mhi_dev_ctxt, bhi_ctxt->bhi_base, BHI_IMGADDR_LOW,
|
||||
0xFFFFFFFF, 0, pcie_word_val);
|
||||
|
||||
mhi_reg_write_field(mhi_dev_ctxt, bhi_ctxt->bhi_base,
|
||||
BHI_IMGADDR_LOW,
|
||||
0xFFFFFFFF,
|
||||
0,
|
||||
pcie_word_val);
|
||||
|
||||
pcie_word_val = bhi_ctxt->image_size;
|
||||
pcie_word_val = mem_info->size;
|
||||
mhi_reg_write_field(mhi_dev_ctxt, bhi_ctxt->bhi_base, BHI_IMGSIZE,
|
||||
0xFFFFFFFF, 0, pcie_word_val);
|
||||
0xFFFFFFFF, 0, pcie_word_val);
|
||||
|
||||
pcie_word_val = mhi_reg_read(bhi_ctxt->bhi_base, BHI_IMGTXDB);
|
||||
mhi_reg_write_field(mhi_dev_ctxt, bhi_ctxt->bhi_base,
|
||||
BHI_IMGTXDB, 0xFFFFFFFF, 0, ++pcie_word_val);
|
||||
BHI_IMGTXDB, 0xFFFFFFFF, 0, ++pcie_word_val);
|
||||
read_unlock_bh(pm_xfer_lock);
|
||||
timeout = jiffies + msecs_to_jiffies(bhi_ctxt->poll_timeout);
|
||||
while (time_before(jiffies, timeout)) {
|
||||
|
@ -342,6 +335,7 @@ static ssize_t bhi_write(struct file *file,
|
|||
int ret_val = 0;
|
||||
struct mhi_device_ctxt *mhi_dev_ctxt = file->private_data;
|
||||
struct bhi_ctxt_t *bhi_ctxt = &mhi_dev_ctxt->bhi_ctxt;
|
||||
struct bhie_mem_info mem_info;
|
||||
long timeout;
|
||||
|
||||
if (buf == NULL || 0 == count)
|
||||
|
@ -350,11 +344,11 @@ static ssize_t bhi_write(struct file *file,
|
|||
if (count > BHI_MAX_IMAGE_SIZE)
|
||||
return -ENOMEM;
|
||||
|
||||
ret_val = bhi_alloc_pbl_xfer(mhi_dev_ctxt, count);
|
||||
ret_val = bhi_alloc_pbl_xfer(mhi_dev_ctxt, &mem_info, count);
|
||||
if (ret_val)
|
||||
return -ENOMEM;
|
||||
|
||||
if (copy_from_user(bhi_ctxt->image_loc, buf, count)) {
|
||||
if (copy_from_user(mem_info.aligned, buf, count)) {
|
||||
ret_val = -ENOMEM;
|
||||
goto bhi_copy_error;
|
||||
}
|
||||
|
@ -370,15 +364,13 @@ static ssize_t bhi_write(struct file *file,
|
|||
goto bhi_copy_error;
|
||||
}
|
||||
|
||||
ret_val = bhi_load_firmware(mhi_dev_ctxt);
|
||||
ret_val = bhi_load_firmware(mhi_dev_ctxt, &mem_info);
|
||||
if (ret_val) {
|
||||
mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
|
||||
"Failed to load bhi image\n");
|
||||
}
|
||||
dma_free_coherent(&mhi_dev_ctxt->plat_dev->dev,
|
||||
bhi_ctxt->alloc_size,
|
||||
bhi_ctxt->unaligned_image_loc,
|
||||
bhi_ctxt->dma_handle);
|
||||
dma_free_coherent(&mhi_dev_ctxt->plat_dev->dev, mem_info.alloc_size,
|
||||
mem_info.pre_aligned, mem_info.dma_handle);
|
||||
|
||||
/* Regardless of failure set to RESET state */
|
||||
ret_val = mhi_init_state_transition(mhi_dev_ctxt,
|
||||
|
@ -390,10 +382,8 @@ static ssize_t bhi_write(struct file *file,
|
|||
return count;
|
||||
|
||||
bhi_copy_error:
|
||||
dma_free_coherent(&mhi_dev_ctxt->plat_dev->dev,
|
||||
bhi_ctxt->alloc_size,
|
||||
bhi_ctxt->unaligned_image_loc,
|
||||
bhi_ctxt->dma_handle);
|
||||
dma_free_coherent(&mhi_dev_ctxt->plat_dev->dev, mem_info.alloc_size,
|
||||
mem_info.pre_aligned, mem_info.dma_handle);
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
|
@ -447,6 +437,7 @@ void bhi_firmware_download(struct work_struct *work)
|
|||
{
|
||||
struct mhi_device_ctxt *mhi_dev_ctxt;
|
||||
struct bhi_ctxt_t *bhi_ctxt;
|
||||
struct bhie_mem_info mem_info;
|
||||
int ret;
|
||||
long timeout;
|
||||
|
||||
|
@ -459,7 +450,10 @@ void bhi_firmware_download(struct work_struct *work)
|
|||
wait_event_interruptible(*mhi_dev_ctxt->mhi_ev_wq.bhi_event,
|
||||
mhi_dev_ctxt->mhi_state == MHI_STATE_BHI);
|
||||
|
||||
ret = bhi_load_firmware(mhi_dev_ctxt);
|
||||
/* PBL image is the first segment in firmware vector table */
|
||||
mem_info = *bhi_ctxt->fw_table.bhie_mem_info;
|
||||
mem_info.size = bhi_ctxt->firmware_info.max_sbl_len;
|
||||
ret = bhi_load_firmware(mhi_dev_ctxt, &mem_info);
|
||||
if (ret) {
|
||||
mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
|
||||
"Failed to Load sbl firmware\n");
|
||||
|
@ -547,13 +541,6 @@ int bhi_probe(struct mhi_device_ctxt *mhi_dev_ctxt)
|
|||
image += to_copy;
|
||||
}
|
||||
|
||||
/*
|
||||
* Re-use BHI/E pointer for BHI since we guranteed BHI/E segment
|
||||
* is >= to SBL image.
|
||||
*/
|
||||
bhi_ctxt->phy_image_loc = sg_dma_address(&fw_table->sg_list[1]);
|
||||
bhi_ctxt->image_size = fw_info->max_sbl_len;
|
||||
|
||||
fw_table->sequence++;
|
||||
release_firmware(firmware);
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue