From: Tang Yizhou tangyizhou@huawei.com
ascend inclusion category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/I4EUVI CVE: NA
------------------------------------------------- We encounter a problem of accessing NULL pointer of mm as below:
[25853.542104] Unable to handle kernel NULL pointer dereference at virtual address 000000000000004c [25853.542105] Mem abort info: [25853.542106] ESR = 0x96000006 [25853.542108] Exception class = DABT (current EL), IL = 32 bits [25853.542109] SET = 0, FnV = 0 [25853.542110] EA = 0, S1PTW = 0 [25853.542110] Data abort info: [25853.542111] ISV = 0, ISS = 0x00000006 [25853.542112] CM = 0, WnR = 0 [25853.542115] user pgtable: 4k pages, 48-bit VAs, pgdp = 0000000090ec4b55 [25853.542116] [000000000000004c] pgd=0000000d49b38003, pud=0000000d49be6003, pmd=0000000000000000 [25853.542121] Internal error: Oops: 96000006 [#1] SMP [25853.542123] Process dds_common_app (pid: 29004, stack limit = 0x000000001fcac39f) [25853.542127] CPU: 8 PID: 29004 Comm: dds_common_app Tainted: G O 4.19.95-1.h1.AOS2.0.aarch64 #1 [25853.542128] Hardware name: asic (DT) [25853.542129] pstate: 60400009 (nZCv daif +PAN -UAO) [25853.542136] pc : mmput+0x20/0x170 [25853.542141] lr : sp_make_share_u2k+0x1c0/0x2d8 [25853.542141] sp : ffff00000d263b80 [25853.542142] pmr_save: 000000e0 [25853.542143] x29: ffff00000d263b80 x28: 0000000000000007 [25853.542145] x27: 0000000000000007 x26: ffff00000d263cb0 [25853.542147] x25: ffff800d3d32d000 x24: ffff00000107f000 [25853.542148] x23: 0000000000007143 x22: 0000e80006000000 [25853.542150] x21: ffff00009b140000 x20: ffff0000095fa000 [25853.542151] x19: 0000000000000000 x18: ffff00000961b588 [25853.542153] x17: 0000000000000000 x16: 000000000000000e [25853.542154] x15: 0000000000000000 x14: ffff800d44d66418 [25853.542155] x13: 0000000000000000 x12: 0140000000000000 [25853.542157] x11: ffff0000db140000 x10: ffff0000096468f8 [25853.542158] x9 : ffff80089a517e80 x8 : 0000000000000008 [25853.542160] x7 : 00000000ffffffff x6 : 000000c4c5d56692 [25853.542161] x5 : 0000000000000000 x4 : 0000000000000020 [25853.542163] x3 : 0000000000000010 x2 : 000000000000004c [25853.542164] x1 : 0000000000000000 x0 : ffff00000831c718 [25853.542166] Call trace: [25853.542168] mmput+0x20/0x170 [25853.542169] sp_make_share_u2k+0x1c0/0x2d8 [25853.542180] mz_create+0x7c/0x328 [drv_buff_module] [25853.542185] buff_req_ioctl_mz_create+0xf8/0x338 [drv_buff_module] [25853.542189] buff_ioctl+0xc8/0x3f8 [drv_buff_module] [25853.542193] do_vfs_ioctl+0xc4/0x8c0 [25853.542194] ksys_ioctl+0x8c/0xa0 [25853.542196] __arm64_sys_ioctl+0x28/0x38 [25853.542200] el0_svc_common+0x8c/0x218 [25853.542202] el0_svc_handler+0x38/0x88 [25853.542205] el0_svc+0x14/0x40
The offset of mm_user in mm_struct is 0x4c so the NULL pointer is mm.
The concurrency scene can be described as follows: At first, mmget_not_zero() increases the refcount of mm successfully, but then target process is killed. In exit_mm(), the exiting process set
current->mm = NULL;
And when mmput() is called in u2k, tsk->mm is already NULL.
To fix the problem, we use get_task_mm() to keep mm pointer in a local variable.
Signed-off-by: Tang Yizhou tangyizhou@huawei.com Reviewed-by: Ding Tianhong dingtianhong@huawei.com Signed-off-by: Yang Yingliang yangyingliang@huawei.com Reviewed-by: Weilong Chen chenweilong@huawei.com Signed-off-by: Yang Yingliang yangyingliang@huawei.com --- mm/share_pool.c | 39 ++++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 17 deletions(-)
diff --git a/mm/share_pool.c b/mm/share_pool.c index f98d2a0f5dcc8..fb492679473c0 100644 --- a/mm/share_pool.c +++ b/mm/share_pool.c @@ -1746,7 +1746,7 @@ static int sp_hugetlb_entry(pte_t *ptep, unsigned long hmask, * ALIGN(uva+size) - uva_aligned */ static int __sp_walk_page_range(unsigned long uva, unsigned long size, - struct task_struct *tsk, struct sp_walk_data *sp_walk_data) + struct mm_struct *mm, struct sp_walk_data *sp_walk_data) { int ret = 0; struct vm_area_struct *vma; @@ -1767,7 +1767,7 @@ static int __sp_walk_page_range(unsigned long uva, unsigned long size, * In this situation, the correctness of the parameters is mainly * guaranteed by the caller. */ - vma = find_vma(tsk->mm, uva); + vma = find_vma(mm, uva); if (!vma) { if (printk_ratelimit()) pr_err("share pool: u2k input uva %pK is invalid\n", (void *)uva); @@ -1811,7 +1811,7 @@ static int __sp_walk_page_range(unsigned long uva, unsigned long size, } sp_walk_data->pages = pages;
- sp_walk.mm = tsk->mm; + sp_walk.mm = mm; sp_walk.private = sp_walk_data;
ret = walk_page_range(uva_aligned, uva_aligned + size_aligned, @@ -1835,6 +1835,7 @@ void *sp_make_share_u2k(unsigned long uva, unsigned long size, int pid) { int ret = 0; struct task_struct *tsk; + struct mm_struct *mm; void *p = ERR_PTR(-ENODEV); struct sp_walk_data sp_walk_data = { .page_count = 0, @@ -1853,14 +1854,15 @@ void *sp_make_share_u2k(unsigned long uva, unsigned long size, int pid) goto out; }
- if (!mmget_not_zero(tsk->mm)) + mm = get_task_mm(tsk); + if (mm == NULL) goto out_put_task; - down_write(&tsk->mm->mmap_sem); - ret = __sp_walk_page_range(uva, size, tsk, &sp_walk_data); + down_write(&mm->mmap_sem); + ret = __sp_walk_page_range(uva, size, mm, &sp_walk_data); if (ret) { pr_err("share pool: walk page range failed, ret %d\n", ret); - up_write(&tsk->mm->mmap_sem); - mmput(tsk->mm); + up_write(&mm->mmap_sem); + mmput(mm); p = ERR_PTR(ret); goto out_put_task; } @@ -1871,8 +1873,8 @@ void *sp_make_share_u2k(unsigned long uva, unsigned long size, int pid) else p = vmap(sp_walk_data.pages, sp_walk_data.page_count, VM_MAP, PAGE_KERNEL); - up_write(&tsk->mm->mmap_sem); - mmput(tsk->mm); + up_write(&mm->mmap_sem); + mmput(mm);
if (!p) { if (printk_ratelimit()) @@ -2145,6 +2147,7 @@ EXPORT_SYMBOL_GPL(sp_unshare); int sp_walk_page_range(unsigned long uva, unsigned long size, struct task_struct *tsk, struct sp_walk_data *sp_walk_data) { + struct mm_struct *mm; int ret = 0;
if (unlikely(!sp_walk_data)) { @@ -2155,17 +2158,19 @@ int sp_walk_page_range(unsigned long uva, unsigned long size, if (!tsk || (tsk->flags & PF_EXITING)) return -ESRCH;
- sp_walk_data->page_count = 0; - get_task_struct(tsk); - if (!mmget_not_zero(tsk->mm)) { + mm = get_task_mm(tsk); + if (!mm) { put_task_struct(tsk); return -ESRCH; } - down_write(&tsk->mm->mmap_sem); - ret = __sp_walk_page_range(uva, size, tsk, sp_walk_data); - up_write(&tsk->mm->mmap_sem); - mmput(tsk->mm); + + sp_walk_data->page_count = 0; + down_write(&mm->mmap_sem); + ret = __sp_walk_page_range(uva, size, mm, sp_walk_data); + up_write(&mm->mmap_sem); + + mmput(mm); put_task_struct(tsk);
return ret;