lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20220404080519.pi6izyuop3mmdg2g@wittgenstein>
Date:   Mon, 4 Apr 2022 10:05:19 +0200
From:   Christian Brauner <brauner@...nel.org>
To:     "Alejandro Colomar (man-pages)" <alx.manpages@...il.com>
Cc:     bugzilla-daemon@...nel.org,
        "linux-kernel@...r.kernel.org" <linux-kernel@...r.kernel.org>,
        Коренберг Марк 
        <socketpair@...il.com>, Andrei Vagin <avagin@...nvz.org>,
        Dmitry Safonov <dima@...sta.com>,
        Thomas Gleixner <tglx@...utronix.de>,
        Arnd Bergmann <arnd@...db.de>, Serge Hallyn <serge@...lyn.com>
Subject: Re: vfork(2) fails after unshare(CLONE_NEWTIME) (was: [Bug 215769]
 man 2 vfork() does not document corner case when PID == 1)

On Sat, Apr 02, 2022 at 11:15:52PM +0200, Alejandro Colomar (man-pages) wrote:
> [Added some kernel CCs that may know what's going on]
> 
> Hi,
> 
> On 3/31/22 09:53, bugzilla-daemon@...nel.org wrote:
> > https://bugzilla.kernel.org/show_bug.cgi?id=215769
> > 
> > --- Comment #3 from Коренберг Марк (socketpair@...il.com) ---
> > Hi,
> > I appreciate depth of information validation. Actually, you are right. vfork()
> > DOES work with pid=1 processes. I figured out the cause in my case. In order to
> > reproduce -- add unshare(CLONE_NEWTIME) just before vfork(). Now, I don't know
> > if it's a bug in vfork() or in fork(). Yes, both are clone() actually.
> > 
> > In any case, they should either both give EINVAL or both don't fail. But it's
> > definitely bug in the kernel around CLONE_NEWTIME.
> > 
> 
> On 3/31/22 10:12, bugzilla-daemon@...nel.org wrote:
> > https://bugzilla.kernel.org/show_bug.cgi?id=215769
> >
> > --- Comment #4 from Коренберг Марк (socketpair@...il.com) ---
> > #define _GNU_SOURCE 1
> > #include <stdio.h>
> > #include <sched.h>
> > #include <stdlib.h>
> > #include <unistd.h>
> > #include <sys/types.h>
> > #include <sys/wait.h>
> > #include <err.h>
> >
> > #ifndef CLONE_NEWTIME
> > #define CLONE_NEWTIME   0x00000080
> > #endif
> >
> > int main (void)
> > {
> >   if (unshare (CLONE_NEWTIME))  err (EXIT_FAILURE, "UNSHARE_NEWTIME");
> >
> >   pid_t pid;
> >   switch (pid=vfork ())
> >   {
> >   case 0:
> >     _exit(0);
> >   case -1:
> >     err(EXIT_FAILURE, "vfork BUG");
> >   default:
> >     waitpid(pid, NULL, 0);
> >   }
> >   return 0;
> > }
> >
> 
> I could reproduce it with the following code.  I tried
> syscall(SYS_vfork) to make sure it's not a problem in the libc wrapper,
> and to make sure I do call vfork(2).  If I replace vfork(2) with
> fork(2), I don't get the error.
> 
> 
> $ cat vfork.c
> #define _GNU_SOURCE
> #include <err.h>
> #include <linux/sched.h>
> #include <sched.h>
> #include <signal.h>
> #include <stdlib.h>
> #include <sys/syscall.h>
> #include <unistd.h>
> 
> int main(void)
> {
> 	pid_t pid;
> 
> 	if (unshare(CLONE_NEWTIME) == -1)
> 		err(EXIT_FAILURE, "unshare(2)");
> 	if (signal(SIGCHLD, SIG_IGN) == SIG_ERR)
> 		err(EXIT_FAILURE, "sigaction(2)");
> 	pid = syscall(SYS_vfork);
> 	switch (pid) {
> 	case 0:
> 		errx(EXIT_SUCCESS, "Grandchild exiting normally.");
> 	case -1:
> 		/* If we got here, the report is confirmed. */
> 		err(EXIT_FAILURE, "vfork(2)");
> 	default:
> 		errx(EXIT_SUCCESS, "Child exiting normally.");
> 	}
> }
> 
> $ cc -Wall -Wextra -Werror vfork.c
> $ sudo ./a.out
> a.out: vfork(2): Invalid argument
> 
> 
> 
> $ grep_syscall_def vfork
> kernel/fork.c:2711:
> SYSCALL_DEFINE0(vfork)
> {
> 	struct kernel_clone_args args = {
> 		.flags		= CLONE_VFORK | CLONE_VM,
> 		.exit_signal	= SIGCHLD,
> 	};
> 
> 	return kernel_clone(&args);
> }
> 
> 
> Maybe someone in the kernel can send some patch for the clone(2) and/or
> vfork(2) manual pages that explains the reason (if it's intended).

Hey Alejandro,

I won't be able to send a patch very soon but I can at least explain why
you see EINVAL. :)

This is intended. 

vfork() suspends the parent process and the child process will share the
same vm as the parent process. If the child process is in a new time
namespace different from its parent process it is not allowed to be in
the same threadgroup or share virtual memory with the parent process.
That's why you see EINVAL.

Note, the unshare(CLONE_NEWTIME) call will _not_ cause the calling
process to be moved into a different time namespace. Only the newly
created child process will be after a subsequent
fork()/vfork()/clone()/clone3()...

The semantics are equivalent to that of CLONE_NEWPID in this regard. You
can see this via /proc/<pid>/ns/ where you see two entries for pid
namespaces and also two entries for time namespaces:

* CLONE_NEWTIME
  * /proc/<pid>/ns/time			// current time namespace
  * /proc/<pid>/ns/time_for_children	// time namespace for the new child process

If during fork:

parent_process->time != parent_process->time_for_children

and either CLONE_VM or CLONE_THREAD is set you see EINVAL.

You can thus replicate the same error via:

unshare(CLONE_NEWTIME)

and a

clone() or clone3() call with CLONE_VM or CLONE_THREAD.

Christian

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ