Skip to content

Commit a0eb9ab

Browse files
esyr-rhChristian Brauner
authored andcommitted
fork: block invalid exit signals with clone3()
Previously, higher 32 bits of exit_signal fields were lost when copied to the kernel args structure (that uses int as a type for the respective field). Moreover, as Oleg has noted, exit_signal is used unchecked, so it has to be checked for sanity before use; for the legacy syscalls, applying CSIGNAL mask guarantees that it is at least non-negative; however, there's no such thing is done in clone3() code path, and that can break at least thread_group_leader. This commit adds a check to copy_clone_args_from_user() to verify that the exit signal is limited by CSIGNAL as with legacy clone() and that the signal is valid. With this we don't get the legacy clone behavior were an invalid signal could be handed down and would only be detected and ignored in do_notify_parent(). Users of clone3() will now get a proper error when they pass an invalid exit signal. Note, that this is not user-visible behavior since no kernel with clone3() has been released yet. The following program will cause a splat on a non-fixed clone3() version and will fail correctly on a fixed version: #define _GNU_SOURCE #include <linux/sched.h> #include <linux/types.h> #include <sched.h> #include <stdio.h> #include <stdlib.h> #include <sys/syscall.h> #include <sys/wait.h> #include <unistd.h> int main(int argc, char *argv[]) { pid_t pid = -1; struct clone_args args = {0}; args.exit_signal = -1; pid = syscall(__NR_clone3, &args, sizeof(struct clone_args)); if (pid < 0) exit(EXIT_FAILURE); if (pid == 0) exit(EXIT_SUCCESS); wait(NULL); exit(EXIT_SUCCESS); } Fixes: 7f192e3 ("fork: add clone3") Reported-by: Oleg Nesterov <[email protected]> Suggested-by: Oleg Nesterov <[email protected]> Suggested-by: Dmitry V. Levin <[email protected]> Signed-off-by: Eugene Syromiatnikov <[email protected]> Link: https://lore.kernel.org/r/4b38fa4ce420b119a4c6345f42fe3cec2de9b0b5.1568223594.git.esyr@redhat.com [[email protected]: simplify check and rework commit message] Signed-off-by: Christian Brauner <[email protected]>
1 parent f74c2bb commit a0eb9ab

File tree

1 file changed

+10
-0
lines changed

1 file changed

+10
-0
lines changed

kernel/fork.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2338,6 +2338,8 @@ struct mm_struct *copy_init_mm(void)
23382338
*
23392339
* It copies the process, and if successful kick-starts
23402340
* it and waits for it to finish using the VM if required.
2341+
*
2342+
* args->exit_signal is expected to be checked for sanity by the caller.
23412343
*/
23422344
long _do_fork(struct kernel_clone_args *args)
23432345
{
@@ -2562,6 +2564,14 @@ noinline static int copy_clone_args_from_user(struct kernel_clone_args *kargs,
25622564
if (copy_from_user(&args, uargs, size))
25632565
return -EFAULT;
25642566

2567+
/*
2568+
* Verify that higher 32bits of exit_signal are unset and that
2569+
* it is a valid signal
2570+
*/
2571+
if (unlikely((args.exit_signal & ~((u64)CSIGNAL)) ||
2572+
!valid_signal(args.exit_signal)))
2573+
return -EINVAL;
2574+
25652575
*kargs = (struct kernel_clone_args){
25662576
.flags = args.flags,
25672577
.pidfd = u64_to_user_ptr(args.pidfd),

0 commit comments

Comments
 (0)