Vulnerability: A flaw possibility of race condition and incorrect initialization of the process id was found in the Linux kernel child/parent process identification handling while filtering signal handlers. A local attacker is able to abuse this flaw to bypass checks to send any signal to a privileged process.
Vulnerability ID: CVE-2020-35508
CWE: CWE-362
Severity score: 6.4
Project: https://github.com/torvalds/linux
Published: 2021-03-26
File: kernel/fork.c
Commit message: fork: fix copy_process(CLONE_PARENT) race with the exiting ->real_parent

current->group_leader->exit_signal may change during copy_process() if
current->real_parent exits.

Move the assignment inside tasklist_lock to avoid the race.

Signed-off-by: Eddy Wu <eddy_wu@trendmicro.com>
Acked-by: Oleg Nesterov <oleg@redhat.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Commit author: Eddy Wu

Vulnerable version (before fix):
 static __latent_entropy struct task_struct *copy_process( 	/* ok, now we should be set up.. */ 	p->pid = pid_nr(pid); 	if (clone_flags & CLONE_THREAD) { 		p->exit_signal = -1; 		p->group_leader = current->group_leader; 		p->tgid = current->tgid; 	} else { 		if (clone_flags & CLONE_PARENT) 			p->exit_signal = current->group_leader->exit_signal; 		else 			p->exit_signal = args->exit_signal; 		p->group_leader = p; 		p->tgid = p->pid; 	} static __latent_entropy struct task_struct *copy_process( 	if (clone_flags & (CLONE_PARENT|CLONE_THREAD)) { 		p->real_parent = current->real_parent; 		p->parent_exec_id = current->parent_exec_id; 	} else { 		p->real_parent = current; 		p->parent_exec_id = current->self_exec_id; 	}  	klp_copy_process(p);

Fixed version (after fix):
 static __latent_entropy struct task_struct *copy_process( 	/* ok, now we should be set up.. */ 	p->pid = pid_nr(pid); 	if (clone_flags & CLONE_THREAD) { 		p->group_leader = current->group_leader; 		p->tgid = current->tgid; 	} else { 		p->group_leader = p; 		p->tgid = p->pid; 	} static __latent_entropy struct task_struct *copy_process( 	if (clone_flags & (CLONE_PARENT|CLONE_THREAD)) { 		p->real_parent = current->real_parent; 		p->parent_exec_id = current->parent_exec_id; 		if (clone_flags & CLONE_THREAD) 			p->exit_signal = -1; 		else 			p->exit_signal = current->group_leader->exit_signal; 	} else { 		p->real_parent = current; 		p->parent_exec_id = current->self_exec_id; 		p->exit_signal = args->exit_signal; 	}  	klp_copy_process(p);

Patch:
patch: @@ -2167,14 +2167,9 @@ static __latent_entropy struct task_struct *copy_process(
 	/* ok, now we should be set up.. */
 	p->pid = pid_nr(pid);
 	if (clone_flags & CLONE_THREAD) {
-		p->exit_signal = -1;
 		p->group_leader = current->group_leader;
 		p->tgid = current->tgid;
 	} else {
-		if (clone_flags & CLONE_PARENT)
-			p->exit_signal = current->group_leader->exit_signal;
-		else
-			p->exit_signal = args->exit_signal;
 		p->group_leader = p;
 		p->tgid = p->pid;
 	}
@@ -2218,9 +2213,14 @@ static __latent_entropy struct task_struct *copy_process(
 	if (clone_flags & (CLONE_PARENT|CLONE_THREAD)) {
 		p->real_parent = current->real_parent;
 		p->parent_exec_id = current->parent_exec_id;
+		if (clone_flags & CLONE_THREAD)
+			p->exit_signal = -1;
+		else
+			p->exit_signal = current->group_leader->exit_signal;
 	} else {
 		p->real_parent = current;
 		p->parent_exec_id = current->self_exec_id;
+		p->exit_signal = args->exit_signal;
 	}
 
 	klp_copy_process(p);
path: kernel/fork.c