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:
Sujeev Dias 2017-05-01 23:15:10 -07:00 committed by Gerrit - the friendly Code Review server
parent 60be71604a
commit 632e3c71d3
2 changed files with 36 additions and 55 deletions

View file

@ -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;

View file

@ -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);