From: Wang Wensheng wangwensheng4@huawei.com
Offering: HULK hulk inclusion category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/I650K6
--------------------------------
There is a double delete list problem in sp_group_exit Unable to handle kernel paging request at virtual address dead000000000108 Call trace: sp_group_exit+0x104/0x238 do_exit+0x188/0xb88 __arm64_sys_exit+0x24/0x28
Calls to sp_group_exit depends on the value of group_dead, which is controlled by CLONE_THREAD. If process A clone B with CLONE_VM and *NO* CLONE_THREAD. A and B will have group_dead = 1 and have the same mm_struct on exit. So sp_group_exit processes an mm_struct more than once.
To sovle the problem, we check the tgid in sp_group_exit and allow only the parent process to continue.
Similar check should be added in mg_sp_group_add/del_task.
Signed-off-by: Wang Wensheng wangwensheng4@huawei.com --- mm/share_pool.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+)
diff --git a/mm/share_pool.c b/mm/share_pool.c index 1dd699371879..2ba05eb7d1ca 100644 --- a/mm/share_pool.c +++ b/mm/share_pool.c @@ -224,6 +224,7 @@ struct sp_group {
/* a per-process(per mm) struct which manages a sp_group_node list */ struct sp_group_master { + pid_t tgid; /* * number of sp groups the process belongs to, * a.k.a the number of sp_node in node_list @@ -584,6 +585,7 @@ static int sp_init_group_master_locked(struct task_struct *tsk, struct mm_struct INIT_LIST_HEAD(&master->node_list); master->count = 0; master->mm = mm; + master->tgid = tsk->tgid; sp_init_group_master_stat(tsk->tgid, mm, &master->instat); mm->sp_group_master = master; sp_add_group_master(master); @@ -1465,6 +1467,15 @@ int mg_sp_group_add_task(int tgid, unsigned long prot, int spg_id) goto out_put_task; }
+ if (mm->sp_group_master && mm->sp_group_master->tgid != tgid) { + up_write(&sp_group_sem); + pr_err("add: task(%d) is a vfork child of the original task(%d)\n", + tgid, mm->sp_group_master->tgid); + ret = -EINVAL; + free_new_spg_id(id_newly_generated, spg_id); + goto out_put_mm; + } + spg = find_or_alloc_sp_group(spg_id, flag); if (IS_ERR(spg)) { up_write(&sp_group_sem); @@ -1669,6 +1680,14 @@ int mg_sp_group_del_task(int tgid, int spg_id) goto out_put_mm; }
+ if (mm->sp_group_master->tgid != tgid) { + up_write(&sp_group_sem); + pr_err("del: task(%d) is a vfork child of the original task(%d)\n", + tgid, mm->sp_group_master->tgid); + ret = -EINVAL; + goto out_put_mm; + } + spg_node = find_spg_node_by_spg(mm, spg); if (!spg_node) { up_write(&sp_group_sem); @@ -4200,6 +4219,11 @@ int sp_group_exit(void) return 0; }
+ if (master->tgid != current->tgid) { + up_write(&sp_group_sem); + return 0; + } + list_for_each_entry_safe(spg_node, tmp, &master->node_list, group_node) { spg = spg_node->spg;