You need to sign in or sign up before continuing.
提交 a4ea2941 编写于 作者: T Tang Yizhou 提交者: Yang Yingliang

share_pool: Fix spa memleak in dvpp channel destroy procedure

ascend inclusion
category: feature
bugzilla: NA
CVE: NA

-------------------------------------------------

DVPP process channel destroy procedure:
do_exit() -> exit_mm() (mm no longer in spg) -> exit_task_work()
-> task_work_run() -> __fput() -> ... -> vdec_close() ->
sp_unshare(uva, ..., SPG_ID_DEFAULT).

So when it calls sp_unshare(), current->mm is NULL, just like
buff_module_guard_work kthread.

To solve this problem, we skip corresponding sanity checks.
Signed-off-by: NTang Yizhou <tangyizhou@huawei.com>
Reviewed-by: NDing Tianhong <dingtianhong@huawei.com>
Signed-off-by: NYang Yingliang <yangyingliang@huawei.com>
上级 9665ceaf
...@@ -530,7 +530,8 @@ int sp_group_add_task(int pid, int spg_id) ...@@ -530,7 +530,8 @@ int sp_group_add_task(int pid, int spg_id)
} }
} }
mm = get_task_mm(tsk); /* current thread may be exiting in a multithread process */
mm = get_task_mm(tsk->group_leader);
if (!mm) { if (!mm) {
ret = -ESRCH; ret = -ESRCH;
goto out_drop_group; goto out_drop_group;
...@@ -583,6 +584,8 @@ int sp_group_add_task(int pid, int spg_id) ...@@ -583,6 +584,8 @@ int sp_group_add_task(int pid, int spg_id)
pr_warn("share pool: task add group failed when mm populate " pr_warn("share pool: task add group failed when mm populate "
"failed (potential no enough memory): %d\n", ret); "failed (potential no enough memory): %d\n", ret);
sp_munmap_task_areas(mm, spa->link.next); sp_munmap_task_areas(mm, spa->link.next);
spin_lock(&sp_area_lock);
break;
} }
} }
...@@ -859,6 +862,7 @@ static struct sp_area *sp_alloc_area(unsigned long size, unsigned long flags, ...@@ -859,6 +862,7 @@ static struct sp_area *sp_alloc_area(unsigned long size, unsigned long flags,
spa->spg = spg; spa->spg = spg;
atomic_set(&spa->use_count, 1); atomic_set(&spa->use_count, 1);
spa->type = type; spa->type = type;
spa->mm = NULL;
if (spa_inc_usage(type, size)) { if (spa_inc_usage(type, size)) {
err = ERR_PTR(-EINVAL); err = ERR_PTR(-EINVAL);
...@@ -1292,7 +1296,6 @@ void *sp_alloc(unsigned long size, unsigned long sp_flags, int spg_id) ...@@ -1292,7 +1296,6 @@ void *sp_alloc(unsigned long size, unsigned long sp_flags, int spg_id)
pr_warn("share pool: allocation failed due to mm populate failed" pr_warn("share pool: allocation failed due to mm populate failed"
"(potential no enough memory when -12): %d\n", ret); "(potential no enough memory when -12): %d\n", ret);
p = ERR_PTR(ret); p = ERR_PTR(ret);
__sp_area_drop(spa);
mode = FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE; mode = FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE;
offset = sp_addr - MMAP_SHARE_POOL_START; offset = sp_addr - MMAP_SHARE_POOL_START;
...@@ -1840,11 +1843,29 @@ void *sp_make_share_u2k(unsigned long uva, unsigned long size, int pid) ...@@ -1840,11 +1843,29 @@ void *sp_make_share_u2k(unsigned long uva, unsigned long size, int pid)
} }
EXPORT_SYMBOL_GPL(sp_make_share_u2k); EXPORT_SYMBOL_GPL(sp_make_share_u2k);
/*
* Input parameters uva and spg_id are now useless. spg_id will be useful when
* supporting a process in multiple sp groups.
* Always use process pid. Using thread pid is hard to check sanity.
*
* Procedure of unshare uva must be compatible with:
*
* 1. DVPP channel destroy procedure:
* do_exit() -> exit_mm() (mm no longer in spg and current->mm == NULL) ->
* exit_task_work() -> task_work_run() -> __fput() -> ... -> vdec_close() ->
* sp_unshare(uva, SPG_ID_DEFAULT)
*
* 2. Process A once was the target of k2u(to group), then it exits.
* Guard worker kthread tries to free this uva and it must succeed, otherwise
* spa of this uva leaks.
*
* This also means we must trust DVPP channel destroy and guard worker code.
*/
static int sp_unshare_uva(unsigned long uva, unsigned long size, int pid, int spg_id) static int sp_unshare_uva(unsigned long uva, unsigned long size, int pid, int spg_id)
{ {
int ret = 0; int ret = 0;
struct task_struct *tsk; struct task_struct *tsk;
struct sp_group *spg; struct mm_struct *mm;
struct sp_area *spa; struct sp_area *spa;
unsigned long uva_aligned; unsigned long uva_aligned;
unsigned long size_aligned; unsigned long size_aligned;
...@@ -1861,7 +1882,8 @@ static int sp_unshare_uva(unsigned long uva, unsigned long size, int pid, int sp ...@@ -1861,7 +1882,8 @@ static int sp_unshare_uva(unsigned long uva, unsigned long size, int pid, int sp
if (!spa) { if (!spa) {
ret = -EINVAL; ret = -EINVAL;
if (printk_ratelimit()) if (printk_ratelimit())
pr_err("share pool: invalid input uva %pK in unshare uva\n", (void *)uva); pr_err("share pool: invalid input uva %pK in unshare uva\n",
(void *)uva);
goto out_unlock; goto out_unlock;
} }
} }
...@@ -1904,7 +1926,6 @@ static int sp_unshare_uva(unsigned long uva, unsigned long size, int pid, int sp ...@@ -1904,7 +1926,6 @@ static int sp_unshare_uva(unsigned long uva, unsigned long size, int pid, int sp
if (printk_ratelimit()) if (printk_ratelimit())
pr_info("share pool: no need to unshare uva(to task), " pr_info("share pool: no need to unshare uva(to task), "
"target process not found or do_exit\n"); "target process not found or do_exit\n");
ret = -EINVAL;
rcu_read_unlock(); rcu_read_unlock();
sp_dump_stack(); sp_dump_stack();
goto out_drop_area; goto out_drop_area;
...@@ -1912,35 +1933,51 @@ static int sp_unshare_uva(unsigned long uva, unsigned long size, int pid, int sp ...@@ -1912,35 +1933,51 @@ static int sp_unshare_uva(unsigned long uva, unsigned long size, int pid, int sp
get_task_struct(tsk); get_task_struct(tsk);
rcu_read_unlock(); rcu_read_unlock();
if (!spa->mm || if (!spa->mm) {
(current->mm && (current->mm != tsk->mm || tsk->mm != spa->mm))) {
if (printk_ratelimit()) if (printk_ratelimit())
pr_err("share pool: unshare uva(to task) failed, " pr_err("share pool: unshare uva(to task) failed, "
"wrong pid or invalid spa\n"); "none spa owner\n");
ret = -EINVAL; ret = -EINVAL;
put_task_struct(tsk);
goto out_drop_area; goto out_drop_area;
} }
if (spa->mm != tsk->mm) { /* current thread may be exiting in a multithread process */
mm = get_task_mm(tsk->group_leader);
if (!mm) {
if (printk_ratelimit())
pr_info("share pool: no need to unshare uva(to task), "
"target process mm is exiting\n");
put_task_struct(tsk);
goto out_drop_area;
}
if (spa->mm != mm) {
if (printk_ratelimit()) if (printk_ratelimit())
pr_err("share pool: unshare uva(to task) failed, " pr_err("share pool: unshare uva(to task) failed, "
"spa not belong to the task\n"); "spa not belong to the task\n");
ret = -EINVAL; ret = -EINVAL;
mmput(mm);
put_task_struct(tsk);
goto out_drop_area; goto out_drop_area;
} }
if (!mmget_not_zero(tsk->mm)) { /* alway allow kthread and dvpp channel destroy procedure */
put_task_struct(tsk); if (current->mm && current->mm != mm) {
if (printk_ratelimit()) if (printk_ratelimit())
pr_info("share pool: no need to unshare uva(to task), " pr_err("share pool: unshare uva(to task failed, caller "
"target process mm is not existing\n"); "process %d not match target process %d\n)",
sp_dump_stack(); current->pid, pid);
ret = -EINVAL;
mmput(mm);
put_task_struct(tsk);
goto out_drop_area; goto out_drop_area;
} }
down_write(&tsk->mm->mmap_sem);
down_write(&mm->mmap_sem);
ret = do_munmap(tsk->mm, uva_aligned, size_aligned, NULL); ret = do_munmap(tsk->mm, uva_aligned, size_aligned, NULL);
up_write(&tsk->mm->mmap_sem); up_write(&mm->mmap_sem);
mmput(tsk->mm); mmput(mm);
if (ret) { if (ret) {
/* we are not supposed to fail */ /* we are not supposed to fail */
pr_err("share pool: failed to unmap VA %pK when munmap in unshare uva\n", pr_err("share pool: failed to unmap VA %pK when munmap in unshare uva\n",
...@@ -1948,7 +1985,7 @@ static int sp_unshare_uva(unsigned long uva, unsigned long size, int pid, int sp ...@@ -1948,7 +1985,7 @@ static int sp_unshare_uva(unsigned long uva, unsigned long size, int pid, int sp
} }
put_task_struct(tsk); put_task_struct(tsk);
} else if (spa->type == SPA_TYPE_K2SPG) { } else if (spa->type == SPA_TYPE_K2SPG) {
if (!spa->spg || spg_id == SPG_ID_NONE) { if (spg_id < 0) {
if (printk_ratelimit()) if (printk_ratelimit())
pr_err("share pool: unshare uva(to group) failed, " pr_err("share pool: unshare uva(to group) failed, "
"invalid spg id %d\n", spg_id); "invalid spg id %d\n", spg_id);
...@@ -1956,24 +1993,15 @@ static int sp_unshare_uva(unsigned long uva, unsigned long size, int pid, int sp ...@@ -1956,24 +1993,15 @@ static int sp_unshare_uva(unsigned long uva, unsigned long size, int pid, int sp
goto out_drop_area; goto out_drop_area;
} }
spg = __sp_find_spg(pid, SPG_ID_DEFAULT); if (!spg_valid(spa->spg)) {
if (!spg_valid(spg)) {
if (printk_ratelimit()) if (printk_ratelimit())
pr_err("share pool: unshare uva(to group) invalid pid, " pr_info("share pool: no need to unshare uva(to group), "
"process not in sp group or group is dead\n"); "spa doesn't belong to a sp group or group is dead\n");
ret = -EINVAL;
goto out_drop_area;
}
if (spa->spg != spg) {
if (printk_ratelimit())
pr_err("share pool: unshare uva(to group) failed, "
"spa not belong to the group\n");
ret = -EINVAL;
goto out_drop_area; goto out_drop_area;
} }
if (current->mm && current->mm->sp_group != spg) { /* alway allow kthread and dvpp channel destroy procedure */
if (current->mm && current->mm->sp_group != spa->spg) {
if (printk_ratelimit()) if (printk_ratelimit())
pr_err("share pool: unshare uva(to group) failed, " pr_err("share pool: unshare uva(to group) failed, "
"caller process doesn't belong to target group\n"); "caller process doesn't belong to target group\n");
...@@ -1981,7 +2009,7 @@ static int sp_unshare_uva(unsigned long uva, unsigned long size, int pid, int sp ...@@ -1981,7 +2009,7 @@ static int sp_unshare_uva(unsigned long uva, unsigned long size, int pid, int sp
goto out_drop_area; goto out_drop_area;
} }
__sp_free(spg, uva_aligned, size_aligned, NULL); __sp_free(spa->spg, uva_aligned, size_aligned, NULL);
} }
if (!ret) { if (!ret) {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册