driver inclusion category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/IDBQ74 ---------------------------------------------------------------------- While walking VMA pages, the starting address of a page is validated while ending address is not. If user requests cache maintenance over a memory region, that its start address is not aligned to page size, and the modular of memory region size to page size is smaller than the starting address page offset, cache maintenance may be out of desired region, for example: If user requests cache maintenance to address 0x1002 with size 0x1001, the desired maintain range should be 0x1002-0x2003. But when walking the pages to find the physical address of 0x1001, the driver will only check if 0x1000 is a valid PTE, and if so, maintain of 0x1002-0x2003 will be performed even if 0x2000 does not exists. Fix this by adding a check when walking the page. Some refactor is also applied by changing the interfaces to let walk page callback get the total maintain range. Fixes: e6ecc3b028b8 ("soc cache: Add framework driver for HiSilicon SoC cache") Signed-off-by: Yushan Wang <wangyushan12@huawei.com> Signed-off-by: Hongye Lin <linhongye@h-partners.com> --- .../soc/hisilicon/hisi_soc_cache_framework.c | 33 ++++++++++--------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/drivers/soc/hisilicon/hisi_soc_cache_framework.c b/drivers/soc/hisilicon/hisi_soc_cache_framework.c index a8903e535e0b..b37c142bdf08 100644 --- a/drivers/soc/hisilicon/hisi_soc_cache_framework.c +++ b/drivers/soc/hisilicon/hisi_soc_cache_framework.c @@ -154,21 +154,24 @@ static int hisi_soc_cache_maint_pte_entry(pte_t *pte, unsigned long addr, unsigned long next, struct mm_walk *walk) { #ifdef HISI_SOC_CACHE_LLT - enum hisi_soc_cache_maint_type mnt_type = - *((enum hisi_soc_cache_maint_type *)walk->priv); + struct hisi_soc_cache_ioctl_param *param = + (struct hisi_soc_cache_ioctl_param *)walk->priv; #else - enum hisi_soc_cache_maint_type mnt_type = - *((enum hisi_soc_cache_maint_type *)walk->private); + struct hisi_soc_cache_ioctl_param *param = + (struct hisi_soc_cache_ioctl_param *)walk->private; #endif size_t size = next - addr; phys_addr_t paddr; + if (next > param->addr + param->size) + return -EINVAL; + if (!pte_present(ptep_get(pte))) return -EINVAL; paddr = PFN_PHYS(pte_pfn(*pte)) + offset_in_page(addr); - return hisi_soc_cache_maintain(paddr, size, mnt_type); + return hisi_soc_cache_maintain(paddr, size, param->op_type); } static const struct mm_walk_ops hisi_soc_cache_maint_walk = { @@ -459,25 +462,24 @@ static int hisi_soc_cache_mmap(struct file *file, struct vm_area_struct *vma) return ret; } -static int __hisi_soc_cache_maintain(unsigned long __user vaddr, size_t size, - enum hisi_soc_cache_maint_type mnt_type) +static int __hisi_soc_cache_maintain(struct hisi_soc_cache_ioctl_param *param) { - unsigned long start = untagged_addr(vaddr); + unsigned long start = untagged_addr(param->addr); struct vm_area_struct *vma; int ret = 0; /* MakeInvalid is not allowed for calls from userspace. */ - if (mnt_type >= HISI_CACHE_MAINT_MAKEINVALID) + if (param->op_type >= HISI_CACHE_MAINT_MAKEINVALID) return -EINVAL; /* Prevent overflow of vaddr + size. */ - if (!size || vaddr + size < vaddr) + if (!param->size || start + param->size < start) return -EINVAL; mmap_read_lock_killable(current->mm); - vma = vma_lookup(current->mm, vaddr); - if (!range_in_vma(vma, start, vaddr + size)) { + vma = vma_lookup(current->mm, param->addr); + if (!range_in_vma(vma, start, start + param->size)) { ret = -EINVAL; goto out; } @@ -488,8 +490,8 @@ static int __hisi_soc_cache_maintain(unsigned long __user vaddr, size_t size, goto out; } - ret = walk_page_range(current->mm, start, start + size, - &hisi_soc_cache_maint_walk, &mnt_type); + ret = walk_page_range(current->mm, start, start + param->size, + &hisi_soc_cache_maint_walk, param); out: mmap_read_unlock(current->mm); @@ -506,8 +508,7 @@ static long hisi_soc_cache_mgmt_ioctl(struct file *file, u32 cmd, unsigned long switch (cmd) { case HISI_CACHE_MAINTAIN: - ret = __hisi_soc_cache_maintain(param.addr, param.size, - param.op_type); + ret = __hisi_soc_cache_maintain(¶m); break; default: ret = -EINVAL; -- 2.33.0