linux-man.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* For review: pidfd_send_signal(2) manual page
@ 2019-09-23  9:12 Michael Kerrisk (man-pages)
  2019-09-23 11:26 ` Florian Weimer
                   ` (3 more replies)
  0 siblings, 4 replies; 23+ messages in thread
From: Michael Kerrisk (man-pages) @ 2019-09-23  9:12 UTC (permalink / raw)
  To: Oleg Nesterov, Christian Brauner, Jann Horn, Eric W. Biederman,
	Daniel Colascione, Joel Fernandes
  Cc: mtk.manpages, linux-man, Linux API, lkml

Hello Christian and all,

Below, I have the rendered version of the current draft of
the pidfd_send_signal(2) manual page that I have written.
The page source can be found in a Git branch at:
https://git.kernel.org/pub/scm/docs/man-pages/man-pages.git/log/?h=draft_pidfd

I would be pleased to receive corrections and notes on any
details that should be added. (For example, are there error
cases that I have missed?)

Would you be able to review please?

Thanks,

Michael


NAME
       pidfd_send_signal - send a signal to a process specified by a file
       descriptor

SYNOPSIS
       int pidfd_send_signal(int pidfd, int sig, siginfo_t info,
                             unsigned int flags);

DESCRIPTION
       The pidfd_send_signal() system call sends the signal  sig  to  the
       target  process  referred  to by pidfd, a PID file descriptor that
       refers to a process.

       If the info argument points to a  siginfo_t  buffer,  that  buffer
       should be populated as described in rt_sigqueueinfo(2).

       If  the  info  argument  is  a NULL pointer, this is equivalent to
       specifying a pointer to a siginfo_t buffer whose fields match  the
       values  that  are  implicitly supplied when a signal is sent using
       kill(2):

       *  si_signo is set to the signal number;
       *  si_errno is set to 0;
       *  si_code is set to SI_USER;
       *  si_pid is set to the caller's PID; and
       *  si_uid is set to the caller's real user ID.

       The calling process must either be in the same  PID  namespace  as
       the  process  referred  to  by pidfd, or be in an ancestor of that
       namespace.

       The flags argument is reserved for  future  use;  currently,  this
       argument must be specified as 0.

RETURN VALUE
       On  success,  pidfd_send_signal()  returns  0.   On success, -1 is
       returned and errno is set to indicate the cause of the error.

ERRORS
       EBADF  pidfd is not a valid PID file descriptor.

       EINVAL sig is not a valid signal.

       EINVAL The calling process is not in a PID namespace from which it
              can send a signal to the target process.

       EINVAL flags is not 0.

       EPERM  The  calling  process  does not have permission to send the
              signal to the target process.

       EPERM  pidfd  doesn't  refer   to   the   calling   process,   and
              info.si_code is invalid (see rt_sigqueueinfo(2)).

       ESRCH  The target process does not exist.

VERSIONS
       pidfd_send_signal() first appeared in Linux 5.1.

CONFORMING TO
       pidfd_send_signal() is Linux specific.

NOTES
       Currently, there is no glibc wrapper for this system call; call it
       using syscall(2).

   PID file descriptors
       The pidfd argument is a PID file  descriptor,  a  file  descriptor
       that  refers  to  process.  Such a file descriptor can be obtained
       in any of the following ways:

       *  by opening a /proc/[pid] directory;

       *  using pidfd_open(2); or

       *  via the PID file descriptor that  is  returned  by  a  call  to
          clone(2) or clone3(2) that specifies the CLONE_PIDFD flag.

       The  pidfd_send_signal()  system call allows the avoidance of race
       conditions that occur when using traditional interfaces  (such  as
       kill(2)) to signal a process.  The problem is that the traditional
       interfaces specify the target process via a process ID (PID), with
       the  result  that the sender may accidentally send a signal to the
       wrong process if the originally intended target process has termi‐
       nated  and its PID has been recycled for another process.  By con‐
       trast, a PID file descriptor is a stable reference to  a  specific
       process;  if  that  process  terminates,  then the file descriptor
       ceases to be  valid  and  the  caller  of  pidfd_send_signal()  is
       informed of this fact via an ESRCH error.

EXAMPLE
       #define _GNU_SOURCE
       #include <limits.h>
       #include <signal.h>
       #include <fcntl.h>
       #include <stdio.h>
       #include <string.h>
       #include <stdlib.h>
       #include <unistd.h>
       #include <sys/syscall.h>

       #ifndef __NR_pidfd_send_signal
       #define __NR_pidfd_send_signal 424
       #endif

       static
       int pidfd_send_signal(int pidfd, int sig, siginfo_t *info,
               unsigned int flags)
       {
           return syscall(__NR_pidfd_send_signal, pidfd, sig, info, flags);
       }

       int
       main(int argc, char *argv[])
       {
           siginfo_t info;
           char path[PATH_MAX];
           int pidfd, sig;

           if (argc != 3) {
               fprintf(stderr, "Usage: %s <pid> <signal>\n", argv[0]);
               exit(EXIT_FAILURE);
           }

           sig = atoi(argv[2]);

           /* Obtain a PID file descriptor by opening the /proc/PID directory
              of the target process */

           snprintf(path, sizeof(path), "/proc/%s", argv[1]);

           pidfd = open(path, O_RDONLY);
           if (pidfd == -1) {
               perror("open");
               exit(EXIT_FAILURE);
           }

           /* Populate a 'siginfo_t' structure for use with
              pidfd_send_signal() */

           memset(&info, 0, sizeof(info));
           info.si_code = SI_QUEUE;
           info.si_signo = sig;
           info.si_errno = 0;
           info.si_uid = getuid();
           info.si_pid = getpid();
           info.si_value.sival_int = 1234;

           /* Send the signal */

           if (pidfd_send_signal(pidfd, sig, &info, 0) == -1) {
               perror("pidfd_send_signal");
               exit(EXIT_FAILURE);
           }

           exit(EXIT_SUCCESS);
       }

SEE ALSO
       clone(2),   kill(2),   pidfd_open(2),  rt_sigqueueinfo(2),  sigac‐
       tion(2), pid_namespaces(7), signal(7)


^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: For review: pidfd_send_signal(2) manual page
  2019-09-23  9:12 For review: pidfd_send_signal(2) manual page Michael Kerrisk (man-pages)
@ 2019-09-23 11:26 ` Florian Weimer
  2019-09-23 14:23   ` Christian Brauner
                     ` (2 more replies)
  2019-09-23 11:31 ` Daniel Colascione
                   ` (2 subsequent siblings)
  3 siblings, 3 replies; 23+ messages in thread
From: Florian Weimer @ 2019-09-23 11:26 UTC (permalink / raw)
  To: Michael Kerrisk (man-pages)
  Cc: Oleg Nesterov, Christian Brauner, Jann Horn, Eric W. Biederman,
	Daniel Colascione, Joel Fernandes, linux-man, Linux API, lkml

* Michael Kerrisk:

> SYNOPSIS
>        int pidfd_send_signal(int pidfd, int sig, siginfo_t info,
>                              unsigned int flags);

This probably should reference a header for siginfo_t.

>        ESRCH  The target process does not exist.

If the descriptor is valid, does this mean the process has been waited
for?  Maybe this can be made more explicit.

>        The  pidfd_send_signal()  system call allows the avoidance of race
>        conditions that occur when using traditional interfaces  (such  as
>        kill(2)) to signal a process.  The problem is that the traditional
>        interfaces specify the target process via a process ID (PID), with
>        the  result  that the sender may accidentally send a signal to the
>        wrong process if the originally intended target process has termi‐
>        nated  and its PID has been recycled for another process.  By con‐
>        trast, a PID file descriptor is a stable reference to  a  specific
>        process;  if  that  process  terminates,  then the file descriptor
>        ceases to be  valid  and  the  caller  of  pidfd_send_signal()  is
>        informed of this fact via an ESRCH error.

It would be nice to explain somewhere how you can avoid the race using
a PID descriptor.  Is there anything else besides CLONE_PIDFD?

>        static
>        int pidfd_send_signal(int pidfd, int sig, siginfo_t *info,
>                unsigned int flags)
>        {
>            return syscall(__NR_pidfd_send_signal, pidfd, sig, info, flags);
>        }

Please use a different function name.  Thanks.

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: For review: pidfd_send_signal(2) manual page
  2019-09-23  9:12 For review: pidfd_send_signal(2) manual page Michael Kerrisk (man-pages)
  2019-09-23 11:26 ` Florian Weimer
@ 2019-09-23 11:31 ` Daniel Colascione
  2019-09-24 19:42   ` Michael Kerrisk (man-pages)
  2019-09-23 14:29 ` Christian Brauner
  2019-09-23 21:27 ` Eric W. Biederman
  3 siblings, 1 reply; 23+ messages in thread
From: Daniel Colascione @ 2019-09-23 11:31 UTC (permalink / raw)
  To: Michael Kerrisk (man-pages)
  Cc: Oleg Nesterov, Christian Brauner, Jann Horn, Eric W. Biederman,
	Joel Fernandes, linux-man, Linux API, lkml

On Mon, Sep 23, 2019 at 2:12 AM Michael Kerrisk (man-pages)
<mtk.manpages@gmail.com> wrote:
>        The  pidfd_send_signal()  system call allows the avoidance of race
>        conditions that occur when using traditional interfaces  (such  as
>        kill(2)) to signal a process.  The problem is that the traditional
>        interfaces specify the target process via a process ID (PID), with
>        the  result  that the sender may accidentally send a signal to the
>        wrong process if the originally intended target process has termi‐
>        nated  and its PID has been recycled for another process.  By con‐
>        trast, a PID file descriptor is a stable reference to  a  specific
>        process;  if  that  process  terminates,  then the file descriptor
>        ceases to be  valid

The file *descriptor* remains valid even after the process to which it
refers exits. You can close(2) the file descriptor without getting
EBADF. I'd say, instead, that "a PID file descriptor is a stable
reference to a specific process; process-related operations on a PID
file descriptor fail after that process exits".

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: For review: pidfd_send_signal(2) manual page
  2019-09-23 11:26 ` Florian Weimer
@ 2019-09-23 14:23   ` Christian Brauner
  2019-09-24 19:44     ` Michael Kerrisk (man-pages)
  2019-09-24 19:43   ` Michael Kerrisk (man-pages)
  2019-09-25  1:48   ` Jann Horn
  2 siblings, 1 reply; 23+ messages in thread
From: Christian Brauner @ 2019-09-23 14:23 UTC (permalink / raw)
  To: Florian Weimer
  Cc: Michael Kerrisk (man-pages),
	Oleg Nesterov, Christian Brauner, Jann Horn, Eric W. Biederman,
	Daniel Colascione, Joel Fernandes, linux-man, Linux API, lkml

On Mon, Sep 23, 2019 at 01:26:34PM +0200, Florian Weimer wrote:
> * Michael Kerrisk:
> 
> > SYNOPSIS
> >        int pidfd_send_signal(int pidfd, int sig, siginfo_t info,
> >                              unsigned int flags);
> 
> This probably should reference a header for siginfo_t.

Agreed.

> 
> >        ESRCH  The target process does not exist.
> 
> If the descriptor is valid, does this mean the process has been waited
> for?  Maybe this can be made more explicit.

If by valid you mean "refers to a process/thread-group leader" aka is a
pidfd then yes: Getting ESRCH means that the process has exited and has
already been waited upon.
If it had only exited but not waited upon aka is a zombie, then sending
a signal will just work because that's currently how sending signals to
zombies works, i.e. if you only send a signal and don't do any
additional checks you won't notice a difference between a process being
alive and a process being a zombie. The userspace visible behavior in
terms of signaling them is identical.

> 
> >        The  pidfd_send_signal()  system call allows the avoidance of race
> >        conditions that occur when using traditional interfaces  (such  as
> >        kill(2)) to signal a process.  The problem is that the traditional
> >        interfaces specify the target process via a process ID (PID), with
> >        the  result  that the sender may accidentally send a signal to the
> >        wrong process if the originally intended target process has termi‐
> >        nated  and its PID has been recycled for another process.  By con‐
> >        trast, a PID file descriptor is a stable reference to  a  specific
> >        process;  if  that  process  terminates,  then the file descriptor
> >        ceases to be  valid  and  the  caller  of  pidfd_send_signal()  is
> >        informed of this fact via an ESRCH error.
> 
> It would be nice to explain somewhere how you can avoid the race using
> a PID descriptor.  Is there anything else besides CLONE_PIDFD?

If you're the parent of the process you can do this without CLONE_PIDFD:
pid = fork();
pidfd = pidfd_open();
ret = pidfd_send_signal(pidfd, 0, NULL, 0);
if (ret < 0 && errno == ESRCH)
	/* pidfd refers to another, recycled process */

> 
> >        static
> >        int pidfd_send_signal(int pidfd, int sig, siginfo_t *info,
> >                unsigned int flags)
> >        {
> >            return syscall(__NR_pidfd_send_signal, pidfd, sig, info, flags);
> >        }
> 
> Please use a different function name.  Thanks.

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: For review: pidfd_send_signal(2) manual page
  2019-09-23  9:12 For review: pidfd_send_signal(2) manual page Michael Kerrisk (man-pages)
  2019-09-23 11:26 ` Florian Weimer
  2019-09-23 11:31 ` Daniel Colascione
@ 2019-09-23 14:29 ` Christian Brauner
  2019-09-23 20:27   ` Michael Kerrisk (man-pages)
  2019-09-23 21:27 ` Eric W. Biederman
  3 siblings, 1 reply; 23+ messages in thread
From: Christian Brauner @ 2019-09-23 14:29 UTC (permalink / raw)
  To: Michael Kerrisk (man-pages)
  Cc: Oleg Nesterov, Christian Brauner, Jann Horn, Eric W. Biederman,
	Daniel Colascione, Joel Fernandes, linux-man, Linux API, lkml

On Mon, Sep 23, 2019 at 11:12:00AM +0200, Michael Kerrisk (man-pages) wrote:
> Hello Christian and all,
> 
> Below, I have the rendered version of the current draft of
> the pidfd_send_signal(2) manual page that I have written.
> The page source can be found in a Git branch at:
> https://git.kernel.org/pub/scm/docs/man-pages/man-pages.git/log/?h=draft_pidfd
> 
> I would be pleased to receive corrections and notes on any
> details that should be added. (For example, are there error
> cases that I have missed?)
> 
> Would you be able to review please?

Michael,

A big big thank you for doing this! Really appreciated.
I'm happy to review this!

> 
> Thanks,
> 
> Michael
> 
> 
> NAME
>        pidfd_send_signal - send a signal to a process specified by a file
>        descriptor
> 
> SYNOPSIS
>        int pidfd_send_signal(int pidfd, int sig, siginfo_t info,
>                              unsigned int flags);
> 
> DESCRIPTION
>        The pidfd_send_signal() system call sends the signal  sig  to  the
>        target  process  referred  to by pidfd, a PID file descriptor that
>        refers to a process.
> 
>        If the info argument points to a  siginfo_t  buffer,  that  buffer
>        should be populated as described in rt_sigqueueinfo(2).
> 
>        If  the  info  argument  is  a NULL pointer, this is equivalent to
>        specifying a pointer to a siginfo_t buffer whose fields match  the
>        values  that  are  implicitly supplied when a signal is sent using
>        kill(2):
> 
>        *  si_signo is set to the signal number;
>        *  si_errno is set to 0;
>        *  si_code is set to SI_USER;
>        *  si_pid is set to the caller's PID; and
>        *  si_uid is set to the caller's real user ID.
> 
>        The calling process must either be in the same  PID  namespace  as
>        the  process  referred  to  by pidfd, or be in an ancestor of that
>        namespace.
> 
>        The flags argument is reserved for  future  use;  currently,  this
>        argument must be specified as 0.
> 
> RETURN VALUE
>        On  success,  pidfd_send_signal()  returns  0.   On success, -1 is

This should probably be "On error, -1 is [...]".

>        returned and errno is set to indicate the cause of the error.
> 
> ERRORS
>        EBADF  pidfd is not a valid PID file descriptor.
> 
>        EINVAL sig is not a valid signal.
> 
>        EINVAL The calling process is not in a PID namespace from which it
>               can send a signal to the target process.
> 
>        EINVAL flags is not 0.
> 
>        EPERM  The  calling  process  does not have permission to send the
>               signal to the target process.
> 
>        EPERM  pidfd  doesn't  refer   to   the   calling   process,   and
>               info.si_code is invalid (see rt_sigqueueinfo(2)).
> 
>        ESRCH  The target process does not exist.
> 
> VERSIONS
>        pidfd_send_signal() first appeared in Linux 5.1.
> 
> CONFORMING TO
>        pidfd_send_signal() is Linux specific.
> 
> NOTES
>        Currently, there is no glibc wrapper for this system call; call it
>        using syscall(2).
> 
>    PID file descriptors
>        The pidfd argument is a PID file  descriptor,  a  file  descriptor
>        that  refers  to  process.  Such a file descriptor can be obtained
>        in any of the following ways:
> 
>        *  by opening a /proc/[pid] directory;
> 
>        *  using pidfd_open(2); or
> 
>        *  via the PID file descriptor that  is  returned  by  a  call  to
>           clone(2) or clone3(2) that specifies the CLONE_PIDFD flag.
> 
>        The  pidfd_send_signal()  system call allows the avoidance of race
>        conditions that occur when using traditional interfaces  (such  as
>        kill(2)) to signal a process.  The problem is that the traditional
>        interfaces specify the target process via a process ID (PID), with
>        the  result  that the sender may accidentally send a signal to the
>        wrong process if the originally intended target process has termi‐
>        nated  and its PID has been recycled for another process.  By con‐
>        trast, a PID file descriptor is a stable reference to  a  specific
>        process;  if  that  process  terminates,  then the file descriptor
>        ceases to be  valid  and  the  caller  of  pidfd_send_signal()  is
>        informed of this fact via an ESRCH error.
> 
> EXAMPLE
>        #define _GNU_SOURCE
>        #include <limits.h>
>        #include <signal.h>
>        #include <fcntl.h>
>        #include <stdio.h>
>        #include <string.h>
>        #include <stdlib.h>
>        #include <unistd.h>
>        #include <sys/syscall.h>
> 
>        #ifndef __NR_pidfd_send_signal
>        #define __NR_pidfd_send_signal 424
>        #endif
> 
>        static
>        int pidfd_send_signal(int pidfd, int sig, siginfo_t *info,
>                unsigned int flags)
>        {
>            return syscall(__NR_pidfd_send_signal, pidfd, sig, info, flags);
>        }
> 
>        int
>        main(int argc, char *argv[])
>        {
>            siginfo_t info;
>            char path[PATH_MAX];
>            int pidfd, sig;
> 
>            if (argc != 3) {
>                fprintf(stderr, "Usage: %s <pid> <signal>\n", argv[0]);
>                exit(EXIT_FAILURE);
>            }
> 
>            sig = atoi(argv[2]);
> 
>            /* Obtain a PID file descriptor by opening the /proc/PID directory
>               of the target process */
> 
>            snprintf(path, sizeof(path), "/proc/%s", argv[1]);
> 
>            pidfd = open(path, O_RDONLY);
>            if (pidfd == -1) {
>                perror("open");
>                exit(EXIT_FAILURE);
>            }
> 
>            /* Populate a 'siginfo_t' structure for use with
>               pidfd_send_signal() */
> 
>            memset(&info, 0, sizeof(info));
>            info.si_code = SI_QUEUE;
>            info.si_signo = sig;
>            info.si_errno = 0;
>            info.si_uid = getuid();
>            info.si_pid = getpid();
>            info.si_value.sival_int = 1234;
> 
>            /* Send the signal */
> 
>            if (pidfd_send_signal(pidfd, sig, &info, 0) == -1) {
>                perror("pidfd_send_signal");
>                exit(EXIT_FAILURE);
>            }
> 
>            exit(EXIT_SUCCESS);
>        }
> 
> SEE ALSO
>        clone(2),   kill(2),   pidfd_open(2),  rt_sigqueueinfo(2),  sigac‐
>        tion(2), pid_namespaces(7), signal(7)
> 

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: For review: pidfd_send_signal(2) manual page
  2019-09-23 14:29 ` Christian Brauner
@ 2019-09-23 20:27   ` Michael Kerrisk (man-pages)
  0 siblings, 0 replies; 23+ messages in thread
From: Michael Kerrisk (man-pages) @ 2019-09-23 20:27 UTC (permalink / raw)
  To: Christian Brauner
  Cc: mtk.manpages, Oleg Nesterov, Christian Brauner, Jann Horn,
	Eric W. Biederman, Daniel Colascione, Joel Fernandes, linux-man,
	Linux API, lkml

Hello Christian,

On 9/23/19 4:29 PM, Christian Brauner wrote:
> On Mon, Sep 23, 2019 at 11:12:00AM +0200, Michael Kerrisk (man-pages) wrote:
>> Hello Christian and all,
>>
>> Below, I have the rendered version of the current draft of
>> the pidfd_send_signal(2) manual page that I have written.
>> The page source can be found in a Git branch at:
>> https://git.kernel.org/pub/scm/docs/man-pages/man-pages.git/log/?h=draft_pidfd
>>
>> I would be pleased to receive corrections and notes on any
>> details that should be added. (For example, are there error
>> cases that I have missed?)
>>
>> Would you be able to review please?
> 
> Michael,
> 
> A big big thank you for doing this! Really appreciated.
> I'm happy to review this!
> 
>>
>> Thanks,
>>
>> Michael
>>
>>
>> NAME
>>        pidfd_send_signal - send a signal to a process specified by a file
>>        descriptor
>>
>> SYNOPSIS
>>        int pidfd_send_signal(int pidfd, int sig, siginfo_t info,
>>                              unsigned int flags);
>>
>> DESCRIPTION
>>        The pidfd_send_signal() system call sends the signal  sig  to  the
>>        target  process  referred  to by pidfd, a PID file descriptor that
>>        refers to a process.
>>
>>        If the info argument points to a  siginfo_t  buffer,  that  buffer
>>        should be populated as described in rt_sigqueueinfo(2).
>>
>>        If  the  info  argument  is  a NULL pointer, this is equivalent to
>>        specifying a pointer to a siginfo_t buffer whose fields match  the
>>        values  that  are  implicitly supplied when a signal is sent using
>>        kill(2):
>>
>>        *  si_signo is set to the signal number;
>>        *  si_errno is set to 0;
>>        *  si_code is set to SI_USER;
>>        *  si_pid is set to the caller's PID; and
>>        *  si_uid is set to the caller's real user ID.
>>
>>        The calling process must either be in the same  PID  namespace  as
>>        the  process  referred  to  by pidfd, or be in an ancestor of that
>>        namespace.
>>
>>        The flags argument is reserved for  future  use;  currently,  this
>>        argument must be specified as 0.
>>
>> RETURN VALUE
>>        On  success,  pidfd_send_signal()  returns  0.   On success, -1 is
> 
> This should probably be "On error, -1 is [...]".

Thanks. Fixed.


Cheers,

Michael


-- 
Michael Kerrisk
Linux man-pages maintainer; http://www.kernel.org/doc/man-pages/
Linux/UNIX System Programming Training: http://man7.org/training/

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: For review: pidfd_send_signal(2) manual page
  2019-09-23  9:12 For review: pidfd_send_signal(2) manual page Michael Kerrisk (man-pages)
                   ` (2 preceding siblings ...)
  2019-09-23 14:29 ` Christian Brauner
@ 2019-09-23 21:27 ` Eric W. Biederman
  2019-09-24 19:10   ` Michael Kerrisk (man-pages)
  3 siblings, 1 reply; 23+ messages in thread
From: Eric W. Biederman @ 2019-09-23 21:27 UTC (permalink / raw)
  To: Michael Kerrisk (man-pages)
  Cc: Oleg Nesterov, Christian Brauner, Jann Horn, Daniel Colascione,
	Joel Fernandes, linux-man, Linux API, lkml

"Michael Kerrisk (man-pages)" <mtk.manpages@gmail.com> writes:

> Hello Christian and all,
>
> Below, I have the rendered version of the current draft of
> the pidfd_send_signal(2) manual page that I have written.
> The page source can be found in a Git branch at:
> https://git.kernel.org/pub/scm/docs/man-pages/man-pages.git/log/?h=draft_pidfd
>
> I would be pleased to receive corrections and notes on any
> details that should be added. (For example, are there error
> cases that I have missed?)
>
> Would you be able to review please?
>
> Thanks,
>
> Michael
>
>
> NAME
>        pidfd_send_signal - send a signal to a process specified by a file
>        descriptor
>
> SYNOPSIS
>        int pidfd_send_signal(int pidfd, int sig, siginfo_t info,

 This needs to be "siginfo_t *info," -----------------------^

>                              unsigned int flags);
>

Eric

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: For review: pidfd_send_signal(2) manual page
  2019-09-23 21:27 ` Eric W. Biederman
@ 2019-09-24 19:10   ` Michael Kerrisk (man-pages)
  0 siblings, 0 replies; 23+ messages in thread
From: Michael Kerrisk (man-pages) @ 2019-09-24 19:10 UTC (permalink / raw)
  To: Eric W. Biederman
  Cc: mtk.manpages, Oleg Nesterov, Christian Brauner, Jann Horn,
	Daniel Colascione, Joel Fernandes, linux-man, Linux API, lkml

On 9/23/19 11:27 PM, Eric W. Biederman wrote:
> "Michael Kerrisk (man-pages)" <mtk.manpages@gmail.com> writes:
> 
>> Hello Christian and all,
>>
>> Below, I have the rendered version of the current draft of
>> the pidfd_send_signal(2) manual page that I have written.
>> The page source can be found in a Git branch at:
>> https://git.kernel.org/pub/scm/docs/man-pages/man-pages.git/log/?h=draft_pidfd
>>
>> I would be pleased to receive corrections and notes on any
>> details that should be added. (For example, are there error
>> cases that I have missed?)
>>
>> Would you be able to review please?
>>
>> Thanks,
>>
>> Michael
>>
>>
>> NAME
>>        pidfd_send_signal - send a signal to a process specified by a file
>>        descriptor
>>
>> SYNOPSIS
>>        int pidfd_send_signal(int pidfd, int sig, siginfo_t info,
> 
>  This needs to be "siginfo_t *info," -----------------------^

Thanks, Eric. Fixed.

Cheers,

Michael

-- 
Michael Kerrisk
Linux man-pages maintainer; http://www.kernel.org/doc/man-pages/
Linux/UNIX System Programming Training: http://man7.org/training/

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: For review: pidfd_send_signal(2) manual page
  2019-09-23 11:31 ` Daniel Colascione
@ 2019-09-24 19:42   ` Michael Kerrisk (man-pages)
  0 siblings, 0 replies; 23+ messages in thread
From: Michael Kerrisk (man-pages) @ 2019-09-24 19:42 UTC (permalink / raw)
  To: Daniel Colascione
  Cc: mtk.manpages, Oleg Nesterov, Christian Brauner, Jann Horn,
	Eric W. Biederman, Joel Fernandes, linux-man, Linux API, lkml

On 9/23/19 1:31 PM, Daniel Colascione wrote:
> On Mon, Sep 23, 2019 at 2:12 AM Michael Kerrisk (man-pages)
> <mtk.manpages@gmail.com> wrote:
>>        The  pidfd_send_signal()  system call allows the avoidance of race
>>        conditions that occur when using traditional interfaces  (such  as
>>        kill(2)) to signal a process.  The problem is that the traditional
>>        interfaces specify the target process via a process ID (PID), with
>>        the  result  that the sender may accidentally send a signal to the
>>        wrong process if the originally intended target process has termi‐
>>        nated  and its PID has been recycled for another process.  By con‐
>>        trast, a PID file descriptor is a stable reference to  a  specific
>>        process;  if  that  process  terminates,  then the file descriptor
>>        ceases to be  valid
> 
> The file *descriptor* remains valid even after the process to which it
> refers exits. You can close(2) the file descriptor without getting
> EBADF. I'd say, instead, that "a PID file descriptor is a stable
> reference to a specific process; process-related operations on a PID
> file descriptor fail after that process exits".

Thanks, Daniel. I like that rephrasing, but, since pidfd_send_signal()
is (so far as I know) currently the only relevant process-related
operation (and because this is the manual page describing that
syscall), I made it:

[[
By contrast, a PID file descriptor is a stable reference to a
specific process; if that process terminates, pidfd_send_signal()
fails with the error ESRCH.
]]

Thanks,

Michael


-- 
Michael Kerrisk
Linux man-pages maintainer; http://www.kernel.org/doc/man-pages/
Linux/UNIX System Programming Training: http://man7.org/training/

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: For review: pidfd_send_signal(2) manual page
  2019-09-23 11:26 ` Florian Weimer
  2019-09-23 14:23   ` Christian Brauner
@ 2019-09-24 19:43   ` Michael Kerrisk (man-pages)
  2019-09-25  1:48   ` Jann Horn
  2 siblings, 0 replies; 23+ messages in thread
From: Michael Kerrisk (man-pages) @ 2019-09-24 19:43 UTC (permalink / raw)
  To: Florian Weimer
  Cc: mtk.manpages, Oleg Nesterov, Christian Brauner, Jann Horn,
	Eric W. Biederman, Daniel Colascione, Joel Fernandes, linux-man,
	Linux API, lkml

Hello Florian,

On 9/23/19 1:26 PM, Florian Weimer wrote:
> * Michael Kerrisk:
> 
>> SYNOPSIS
>>        int pidfd_send_signal(int pidfd, int sig, siginfo_t info,
>>                              unsigned int flags);
> 
> This probably should reference a header for siginfo_t.

Thanks. I added: #include <signal.h>

>>        ESRCH  The target process does not exist.
> 
> If the descriptor is valid, does this mean the process has been waited
> for?  Maybe this can be made more explicit.

Yes. I added "(i.e., it has terminated and been waited on)".

>>        The  pidfd_send_signal()  system call allows the avoidance of race
>>        conditions that occur when using traditional interfaces  (such  as
>>        kill(2)) to signal a process.  The problem is that the traditional
>>        interfaces specify the target process via a process ID (PID), with
>>        the  result  that the sender may accidentally send a signal to the
>>        wrong process if the originally intended target process has termi‐
>>        nated  and its PID has been recycled for another process.  By con‐
>>        trast, a PID file descriptor is a stable reference to  a  specific
>>        process;  if  that  process  terminates,  then the file descriptor
>>        ceases to be  valid  and  the  caller  of  pidfd_send_signal()  is
>>        informed of this fact via an ESRCH error.
> 
> It would be nice to explain somewhere how you can avoid the race using
> a PID descriptor.  Is there anything else besides CLONE_PIDFD?

Please see my comment in reply to Christian (which will be sent just
after this).

>>        static
>>        int pidfd_send_signal(int pidfd, int sig, siginfo_t *info,
>>                unsigned int flags)
>>        {
>>            return syscall(__NR_pidfd_send_signal, pidfd, sig, info, flags);
>>        }
> 
> Please use a different function name.  Thanks.

Please see my open question in the thread on pidfd_open().

Thanks for the review, Florian.

Cheers,

Michael

Michael

-- 
Michael Kerrisk
Linux man-pages maintainer; http://www.kernel.org/doc/man-pages/
Linux/UNIX System Programming Training: http://man7.org/training/

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: For review: pidfd_send_signal(2) manual page
  2019-09-23 14:23   ` Christian Brauner
@ 2019-09-24 19:44     ` Michael Kerrisk (man-pages)
  2019-09-24 19:57       ` Christian Brauner
  0 siblings, 1 reply; 23+ messages in thread
From: Michael Kerrisk (man-pages) @ 2019-09-24 19:44 UTC (permalink / raw)
  To: Christian Brauner, Florian Weimer
  Cc: mtk.manpages, Oleg Nesterov, Christian Brauner, Jann Horn,
	Eric W. Biederman, Daniel Colascione, Joel Fernandes, linux-man,
	Linux API, lkml

Hello Christian,

On 9/23/19 4:23 PM, Christian Brauner wrote:
> On Mon, Sep 23, 2019 at 01:26:34PM +0200, Florian Weimer wrote:
>> * Michael Kerrisk:
>>
>>> SYNOPSIS
>>>        int pidfd_send_signal(int pidfd, int sig, siginfo_t info,
>>>                              unsigned int flags);
>>
>> This probably should reference a header for siginfo_t.
> 
> Agreed.
> 
>>
>>>        ESRCH  The target process does not exist.
>>
>> If the descriptor is valid, does this mean the process has been waited
>> for?  Maybe this can be made more explicit.
> 
> If by valid you mean "refers to a process/thread-group leader" aka is a
> pidfd then yes: Getting ESRCH means that the process has exited and has
> already been waited upon.
> If it had only exited but not waited upon aka is a zombie, then sending
> a signal will just work because that's currently how sending signals to
> zombies works, i.e. if you only send a signal and don't do any
> additional checks you won't notice a difference between a process being
> alive and a process being a zombie. The userspace visible behavior in
> terms of signaling them is identical.

(Thanks for the clarification. I added the text "(i.e., it has 
terminated and been waited on)" to the ESRCH error.)

>>>        The  pidfd_send_signal()  system call allows the avoidance of race
>>>        conditions that occur when using traditional interfaces  (such  as
>>>        kill(2)) to signal a process.  The problem is that the traditional
>>>        interfaces specify the target process via a process ID (PID), with
>>>        the  result  that the sender may accidentally send a signal to the
>>>        wrong process if the originally intended target process has termi‐
>>>        nated  and its PID has been recycled for another process.  By con‐
>>>        trast, a PID file descriptor is a stable reference to  a  specific
>>>        process;  if  that  process  terminates,  then the file descriptor
>>>        ceases to be  valid  and  the  caller  of  pidfd_send_signal()  is
>>>        informed of this fact via an ESRCH error.
>>
>> It would be nice to explain somewhere how you can avoid the race using
>> a PID descriptor.  Is there anything else besides CLONE_PIDFD?
> 
> If you're the parent of the process you can do this without CLONE_PIDFD:
> pid = fork();
> pidfd = pidfd_open();
> ret = pidfd_send_signal(pidfd, 0, NULL, 0);
> if (ret < 0 && errno == ESRCH)
> 	/* pidfd refers to another, recycled process */

Although there is still the race between the fork() and the
pidfd_open(), right?

>>>        static
>>>        int pidfd_send_signal(int pidfd, int sig, siginfo_t *info,
>>>                unsigned int flags)
>>>        {
>>>            return syscall(__NR_pidfd_send_signal, pidfd, sig, info, flags);
>>>        }
>>
>> Please use a different function name.  Thanks.

Covered in another thread. I await some further feedback from Florian.

Thanks,

Michael



-- 
Michael Kerrisk
Linux man-pages maintainer; http://www.kernel.org/doc/man-pages/
Linux/UNIX System Programming Training: http://man7.org/training/

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: For review: pidfd_send_signal(2) manual page
  2019-09-24 19:44     ` Michael Kerrisk (man-pages)
@ 2019-09-24 19:57       ` Christian Brauner
  2019-09-24 20:07         ` Christian Brauner
  2019-09-24 21:00         ` Michael Kerrisk (man-pages)
  0 siblings, 2 replies; 23+ messages in thread
From: Christian Brauner @ 2019-09-24 19:57 UTC (permalink / raw)
  To: Michael Kerrisk (man-pages)
  Cc: Florian Weimer, Oleg Nesterov, Jann Horn, Eric W. Biederman,
	Daniel Colascione, Joel Fernandes, linux-man, Linux API, lkml

On Tue, Sep 24, 2019 at 09:44:49PM +0200, Michael Kerrisk (man-pages) wrote:
> Hello Christian,
> 
> On 9/23/19 4:23 PM, Christian Brauner wrote:
> > On Mon, Sep 23, 2019 at 01:26:34PM +0200, Florian Weimer wrote:
> >> * Michael Kerrisk:
> >>
> >>> SYNOPSIS
> >>>        int pidfd_send_signal(int pidfd, int sig, siginfo_t info,
> >>>                              unsigned int flags);
> >>
> >> This probably should reference a header for siginfo_t.
> > 
> > Agreed.
> > 
> >>
> >>>        ESRCH  The target process does not exist.
> >>
> >> If the descriptor is valid, does this mean the process has been waited
> >> for?  Maybe this can be made more explicit.
> > 
> > If by valid you mean "refers to a process/thread-group leader" aka is a
> > pidfd then yes: Getting ESRCH means that the process has exited and has
> > already been waited upon.
> > If it had only exited but not waited upon aka is a zombie, then sending
> > a signal will just work because that's currently how sending signals to
> > zombies works, i.e. if you only send a signal and don't do any
> > additional checks you won't notice a difference between a process being
> > alive and a process being a zombie. The userspace visible behavior in
> > terms of signaling them is identical.
> 
> (Thanks for the clarification. I added the text "(i.e., it has 
> terminated and been waited on)" to the ESRCH error.)
> 
> >>>        The  pidfd_send_signal()  system call allows the avoidance of race
> >>>        conditions that occur when using traditional interfaces  (such  as
> >>>        kill(2)) to signal a process.  The problem is that the traditional
> >>>        interfaces specify the target process via a process ID (PID), with
> >>>        the  result  that the sender may accidentally send a signal to the
> >>>        wrong process if the originally intended target process has termi‐
> >>>        nated  and its PID has been recycled for another process.  By con‐
> >>>        trast, a PID file descriptor is a stable reference to  a  specific
> >>>        process;  if  that  process  terminates,  then the file descriptor
> >>>        ceases to be  valid  and  the  caller  of  pidfd_send_signal()  is
> >>>        informed of this fact via an ESRCH error.
> >>
> >> It would be nice to explain somewhere how you can avoid the race using
> >> a PID descriptor.  Is there anything else besides CLONE_PIDFD?
> > 
> > If you're the parent of the process you can do this without CLONE_PIDFD:
> > pid = fork();
> > pidfd = pidfd_open();
> > ret = pidfd_send_signal(pidfd, 0, NULL, 0);
> > if (ret < 0 && errno == ESRCH)
> > 	/* pidfd refers to another, recycled process */
> 
> Although there is still the race between the fork() and the
> pidfd_open(), right?

Actually no and my code is even too complex.
If you are the parent, and this is really a sequence that obeys the
ordering pidfd_open() before waiting:

pid = fork();
if (pid == 0)
	exit(EXIT_SUCCESS);
pidfd = pidfd_open(pid, 0);
waitid(pid, ...);

Then you are guaranteed that pidfd will refer to pid. No recycling can
happen since the process has not been waited upon yet (That is,
excluding special cases such as where you have a mainloop where a
callback reacts to a SIGCHLD event and waits on the child behind your
back and your next callback in the mainloop calls pidfd_open() while the
pid has been recycled etc.).
A race could only appear in sequences where waiting happens before
pidfd_open():

pid = fork();
if (pid == 0)
	exit(EXIT_SUCCESS);
waitid(pid, ...);
pidfd = pidfd_open(pid, 0);

which honestly simply doesn't make any sense. So if you're the parent
and you combine fork() + pidfd_open() correctly things should be fine
without even having to verify via pidfd_send_signal() (I missed that in
my first mail.).
(Now, it gets more hairy when one considers clone(CLONE_PARENT) but that
would be wildly esoteric because at that point you're using clone()
already and then you should simply pass clone(CLONE_PARENT | CLONE_PIDFD).)

If you're _not_ the parent then CLONE_PIDFD and sending around the pidfd
are your only option to avoiding the race imho.

> 
> >>>        static
> >>>        int pidfd_send_signal(int pidfd, int sig, siginfo_t *info,
> >>>                unsigned int flags)
> >>>        {
> >>>            return syscall(__NR_pidfd_send_signal, pidfd, sig, info, flags);
> >>>        }
> >>
> >> Please use a different function name.  Thanks.
> 
> Covered in another thread. I await some further feedback from Florian.

Right, that wasn't my suggestion anyway. :)

Thanks!
Christian

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: For review: pidfd_send_signal(2) manual page
  2019-09-24 19:57       ` Christian Brauner
@ 2019-09-24 20:07         ` Christian Brauner
  2019-09-24 21:00         ` Michael Kerrisk (man-pages)
  1 sibling, 0 replies; 23+ messages in thread
From: Christian Brauner @ 2019-09-24 20:07 UTC (permalink / raw)
  To: Michael Kerrisk (man-pages)
  Cc: Florian Weimer, Oleg Nesterov, Jann Horn, Eric W. Biederman,
	Daniel Colascione, Joel Fernandes, linux-man, Linux API, lkml

On Tue, Sep 24, 2019 at 09:57:04PM +0200, Christian Brauner wrote:
> On Tue, Sep 24, 2019 at 09:44:49PM +0200, Michael Kerrisk (man-pages) wrote:
> > Hello Christian,
> > 
> > On 9/23/19 4:23 PM, Christian Brauner wrote:
> > > On Mon, Sep 23, 2019 at 01:26:34PM +0200, Florian Weimer wrote:
> > >> * Michael Kerrisk:
> > >>
> > >>> SYNOPSIS
> > >>>        int pidfd_send_signal(int pidfd, int sig, siginfo_t info,
> > >>>                              unsigned int flags);
> > >>
> > >> This probably should reference a header for siginfo_t.
> > > 
> > > Agreed.
> > > 
> > >>
> > >>>        ESRCH  The target process does not exist.
> > >>
> > >> If the descriptor is valid, does this mean the process has been waited
> > >> for?  Maybe this can be made more explicit.
> > > 
> > > If by valid you mean "refers to a process/thread-group leader" aka is a
> > > pidfd then yes: Getting ESRCH means that the process has exited and has
> > > already been waited upon.
> > > If it had only exited but not waited upon aka is a zombie, then sending
> > > a signal will just work because that's currently how sending signals to
> > > zombies works, i.e. if you only send a signal and don't do any
> > > additional checks you won't notice a difference between a process being
> > > alive and a process being a zombie. The userspace visible behavior in
> > > terms of signaling them is identical.
> > 
> > (Thanks for the clarification. I added the text "(i.e., it has 
> > terminated and been waited on)" to the ESRCH error.)
> > 
> > >>>        The  pidfd_send_signal()  system call allows the avoidance of race
> > >>>        conditions that occur when using traditional interfaces  (such  as
> > >>>        kill(2)) to signal a process.  The problem is that the traditional
> > >>>        interfaces specify the target process via a process ID (PID), with
> > >>>        the  result  that the sender may accidentally send a signal to the
> > >>>        wrong process if the originally intended target process has termi‐
> > >>>        nated  and its PID has been recycled for another process.  By con‐
> > >>>        trast, a PID file descriptor is a stable reference to  a  specific
> > >>>        process;  if  that  process  terminates,  then the file descriptor
> > >>>        ceases to be  valid  and  the  caller  of  pidfd_send_signal()  is
> > >>>        informed of this fact via an ESRCH error.
> > >>
> > >> It would be nice to explain somewhere how you can avoid the race using
> > >> a PID descriptor.  Is there anything else besides CLONE_PIDFD?
> > > 
> > > If you're the parent of the process you can do this without CLONE_PIDFD:
> > > pid = fork();
> > > pidfd = pidfd_open();
> > > ret = pidfd_send_signal(pidfd, 0, NULL, 0);
> > > if (ret < 0 && errno == ESRCH)
> > > 	/* pidfd refers to another, recycled process */
> > 
> > Although there is still the race between the fork() and the
> > pidfd_open(), right?
> 
> Actually no and my code is even too complex.
> If you are the parent, and this is really a sequence that obeys the
> ordering pidfd_open() before waiting:
> 
> pid = fork();
> if (pid == 0)
> 	exit(EXIT_SUCCESS);
> pidfd = pidfd_open(pid, 0);
> waitid(pid, ...);
> 
> Then you are guaranteed that pidfd will refer to pid. No recycling can
> happen since the process has not been waited upon yet (That is,
> excluding special cases such as where you have a mainloop where a
> callback reacts to a SIGCHLD event and waits on the child behind your
> back and your next callback in the mainloop calls pidfd_open() while the
> pid has been recycled etc.).

If we wanted to be super nitpicky one could also get in that situation
where you do:

signal(SIGCHLD,SIG_IGN);

// or

struct sigaction sa;
sa.sa_handler = SIG_IGN;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sigaction(SIGCHLD, &sa, 0)

pid = fork();
if (pid == 0)
	exit(EXIT_SUCCESS);
pidfd = pidfd_open();

because then the process gets autoreaped and can be recycled. But again,
that's just bad form and in that scenario one should again use
clone(CLONE_PIDFD) instead of fork().

Christian

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: For review: pidfd_send_signal(2) manual page
  2019-09-24 19:57       ` Christian Brauner
  2019-09-24 20:07         ` Christian Brauner
@ 2019-09-24 21:00         ` Michael Kerrisk (man-pages)
  2019-09-24 21:08           ` Daniel Colascione
  2019-09-24 21:53           ` Christian Brauner
  1 sibling, 2 replies; 23+ messages in thread
From: Michael Kerrisk (man-pages) @ 2019-09-24 21:00 UTC (permalink / raw)
  To: Christian Brauner
  Cc: mtk.manpages, Florian Weimer, Oleg Nesterov, Jann Horn,
	Eric W. Biederman, Daniel Colascione, Joel Fernandes, linux-man,
	Linux API, lkml

Hello Christian,

>>> If you're the parent of the process you can do this without CLONE_PIDFD:
>>> pid = fork();
>>> pidfd = pidfd_open();
>>> ret = pidfd_send_signal(pidfd, 0, NULL, 0);
>>> if (ret < 0 && errno == ESRCH)
>>> 	/* pidfd refers to another, recycled process */
>>
>> Although there is still the race between the fork() and the
>> pidfd_open(), right?
> 
> Actually no and my code is even too complex.
> If you are the parent, and this is really a sequence that obeys the
> ordering pidfd_open() before waiting:
> 
> pid = fork();
> if (pid == 0)
> 	exit(EXIT_SUCCESS);
> pidfd = pidfd_open(pid, 0);
> waitid(pid, ...);
> 
> Then you are guaranteed that pidfd will refer to pid. No recycling can
> happen since the process has not been waited upon yet (That is,

D'oh! Yes, of course. 

> excluding special cases such as where you have a mainloop where a
> callback reacts to a SIGCHLD event and waits on the child behind your
> back and your next callback in the mainloop calls pidfd_open() while the
> pid has been recycled etc.).
> A race could only appear in sequences where waiting happens before
> pidfd_open():
> 
> pid = fork();
> if (pid == 0)
> 	exit(EXIT_SUCCESS);
> waitid(pid, ...);
> pidfd = pidfd_open(pid, 0);
> 
> which honestly simply doesn't make any sense. So if you're the parent
> and you combine fork() + pidfd_open() correctly things should be fine
> without even having to verify via pidfd_send_signal() (I missed that in
> my first mail.).

Thanks for the additional detail.

I added the following to the pidfd_open() page, to
prevent people making the same thinko as me:

       The following code sequence can be used to obtain a file  descrip‐
       tor for the child of fork(2):

           pid = fork();
           if (pid > 0) {     /* If parent */
               pidfd = pidfd_open(pid, 0);
               ...
           }

       Even  if  the  child process has already terminated by the time of
       the pidfd_open() call, the returned file descriptor is  guaranteed
       to refer to the child because the parent has not yet waited on the
       child (and therefore, the child's ID has not been recycled).

Thanks,

Michael

-- 
Michael Kerrisk
Linux man-pages maintainer; http://www.kernel.org/doc/man-pages/
Linux/UNIX System Programming Training: http://man7.org/training/

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: For review: pidfd_send_signal(2) manual page
  2019-09-24 21:00         ` Michael Kerrisk (man-pages)
@ 2019-09-24 21:08           ` Daniel Colascione
  2019-09-25 13:46             ` Michael Kerrisk (man-pages)
  2019-09-24 21:53           ` Christian Brauner
  1 sibling, 1 reply; 23+ messages in thread
From: Daniel Colascione @ 2019-09-24 21:08 UTC (permalink / raw)
  To: Michael Kerrisk (man-pages)
  Cc: Christian Brauner, Florian Weimer, Oleg Nesterov, Jann Horn,
	Eric W. Biederman, Joel Fernandes, linux-man, Linux API, lkml

On Tue, Sep 24, 2019 at 2:00 PM Michael Kerrisk (man-pages)
<mtk.manpages@gmail.com> wrote:
>
> Hello Christian,
>
> >>> If you're the parent of the process you can do this without CLONE_PIDFD:
> >>> pid = fork();
> >>> pidfd = pidfd_open();
> >>> ret = pidfd_send_signal(pidfd, 0, NULL, 0);
> >>> if (ret < 0 && errno == ESRCH)
> >>>     /* pidfd refers to another, recycled process */
> >>
> >> Although there is still the race between the fork() and the
> >> pidfd_open(), right?
> >
> > Actually no and my code is even too complex.
> > If you are the parent, and this is really a sequence that obeys the
> > ordering pidfd_open() before waiting:
> >
> > pid = fork();
> > if (pid == 0)
> >       exit(EXIT_SUCCESS);
> > pidfd = pidfd_open(pid, 0);
> > waitid(pid, ...);
> >
> > Then you are guaranteed that pidfd will refer to pid. No recycling can
> > happen since the process has not been waited upon yet (That is,
>
> D'oh! Yes, of course.

You still have a race if you're the parent and you have SIGCHLD set to
SIG_IGN though.

> > excluding special cases such as where you have a mainloop where a
> > callback reacts to a SIGCHLD event and waits on the child behind your
> > back and your next callback in the mainloop calls pidfd_open() while the
> > pid has been recycled etc.).

That's a pretty common case though, especially if you're a library.

> > A race could only appear in sequences where waiting happens before
> > pidfd_open():
> >
> > pid = fork();
> > if (pid == 0)
> >       exit(EXIT_SUCCESS);
> > waitid(pid, ...);
> > pidfd = pidfd_open(pid, 0);
> >
> > which honestly simply doesn't make any sense. So if you're the parent
> > and you combine fork() + pidfd_open() correctly things should be fine
> > without even having to verify via pidfd_send_signal() (I missed that in
> > my first mail.).
>
> Thanks for the additional detail.
>
> I added the following to the pidfd_open() page, to
> prevent people making the same thinko as me:
>
>        The following code sequence can be used to obtain a file  descrip‐
>        tor for the child of fork(2):
>
>            pid = fork();
>            if (pid > 0) {     /* If parent */
>                pidfd = pidfd_open(pid, 0);
>                ...
>            }
>
>        Even  if  the  child process has already terminated by the time of
>        the pidfd_open() call, the returned file descriptor is  guaranteed
>        to refer to the child because the parent has not yet waited on the
>        child (and therefore, the child's ID has not been recycled).

I'd prefer that sample code be robust in all cases.

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: For review: pidfd_send_signal(2) manual page
  2019-09-24 21:00         ` Michael Kerrisk (man-pages)
  2019-09-24 21:08           ` Daniel Colascione
@ 2019-09-24 21:53           ` Christian Brauner
  2019-09-25 13:46             ` Michael Kerrisk (man-pages)
  1 sibling, 1 reply; 23+ messages in thread
From: Christian Brauner @ 2019-09-24 21:53 UTC (permalink / raw)
  To: Michael Kerrisk (man-pages)
  Cc: Florian Weimer, Oleg Nesterov, Jann Horn, Eric W. Biederman,
	Daniel Colascione, Joel Fernandes, linux-man, Linux API, lkml

On Tue, Sep 24, 2019 at 11:00:03PM +0200, Michael Kerrisk (man-pages) wrote:
> Hello Christian,
> 
> >>> If you're the parent of the process you can do this without CLONE_PIDFD:
> >>> pid = fork();
> >>> pidfd = pidfd_open();
> >>> ret = pidfd_send_signal(pidfd, 0, NULL, 0);
> >>> if (ret < 0 && errno == ESRCH)
> >>> 	/* pidfd refers to another, recycled process */
> >>
> >> Although there is still the race between the fork() and the
> >> pidfd_open(), right?
> > 
> > Actually no and my code is even too complex.
> > If you are the parent, and this is really a sequence that obeys the
> > ordering pidfd_open() before waiting:
> > 
> > pid = fork();
> > if (pid == 0)
> > 	exit(EXIT_SUCCESS);
> > pidfd = pidfd_open(pid, 0);
> > waitid(pid, ...);
> > 
> > Then you are guaranteed that pidfd will refer to pid. No recycling can
> > happen since the process has not been waited upon yet (That is,
> 
> D'oh! Yes, of course. 
> 
> > excluding special cases such as where you have a mainloop where a
> > callback reacts to a SIGCHLD event and waits on the child behind your
> > back and your next callback in the mainloop calls pidfd_open() while the
> > pid has been recycled etc.).
> > A race could only appear in sequences where waiting happens before
> > pidfd_open():
> > 
> > pid = fork();
> > if (pid == 0)
> > 	exit(EXIT_SUCCESS);
> > waitid(pid, ...);
> > pidfd = pidfd_open(pid, 0);
> > 
> > which honestly simply doesn't make any sense. So if you're the parent
> > and you combine fork() + pidfd_open() correctly things should be fine
> > without even having to verify via pidfd_send_signal() (I missed that in
> > my first mail.).
> 
> Thanks for the additional detail.

You're very welcome.

> 
> I added the following to the pidfd_open() page, to
> prevent people making the same thinko as me:
> 
>        The following code sequence can be used to obtain a file  descrip‐
>        tor for the child of fork(2):
> 
>            pid = fork();
>            if (pid > 0) {     /* If parent */
>                pidfd = pidfd_open(pid, 0);
>                ...
>            }
> 
>        Even  if  the  child process has already terminated by the time of
>        the pidfd_open() call, the returned file descriptor is  guaranteed
>        to refer to the child because the parent has not yet waited on the
>        child (and therefore, the child's ID has not been recycled).

Thanks! I'm fine with the example. The code illustrates the basics. If
you want to go overboard, you can mention my callback example and put my
SIG_IGN code snippet from my earlier mails (cf. [1] and [2]) in there.
But imho, that'll complicate the manpage and I'm not sure it's worth it.

Thanks!
Christian

/* References */
[1]: https://lore.kernel.org/r/20190924195701.7pw2olbviieqsg5q@wittgenstein
[2]: https://lore.kernel.org/r/20190924200735.2dvqhan7ynnmfc7s@wittgenstein

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: For review: pidfd_send_signal(2) manual page
  2019-09-23 11:26 ` Florian Weimer
  2019-09-23 14:23   ` Christian Brauner
  2019-09-24 19:43   ` Michael Kerrisk (man-pages)
@ 2019-09-25  1:48   ` Jann Horn
  2 siblings, 0 replies; 23+ messages in thread
From: Jann Horn @ 2019-09-25  1:48 UTC (permalink / raw)
  To: Florian Weimer
  Cc: Michael Kerrisk (man-pages),
	Oleg Nesterov, Christian Brauner, Eric W. Biederman,
	Daniel Colascione, Joel Fernandes, linux-man, Linux API, lkml

On Mon, Sep 23, 2019 at 1:26 PM Florian Weimer <fw@deneb.enyo.de> wrote:
> * Michael Kerrisk:
> >        The  pidfd_send_signal()  system call allows the avoidance of race
> >        conditions that occur when using traditional interfaces  (such  as
> >        kill(2)) to signal a process.  The problem is that the traditional
> >        interfaces specify the target process via a process ID (PID), with
> >        the  result  that the sender may accidentally send a signal to the
> >        wrong process if the originally intended target process has termi‐
> >        nated  and its PID has been recycled for another process.  By con‐
> >        trast, a PID file descriptor is a stable reference to  a  specific
> >        process;  if  that  process  terminates,  then the file descriptor
> >        ceases to be  valid  and  the  caller  of  pidfd_send_signal()  is
> >        informed of this fact via an ESRCH error.
>
> It would be nice to explain somewhere how you can avoid the race using
> a PID descriptor.  Is there anything else besides CLONE_PIDFD?

My favorite example here is that you could implement "killall" without
PID reuse races. With /proc/$pid file descriptors, you could do it
like this (rough pseudocode with missing error handling and resource
leaks and such):

for each pid {
  procfs_pid_fd = open("/proc/"+pid);
  if (procfs_pid_fd == -1) continue;
  comm_fd = openat(procfs_pid_fd, "comm");
  if (comm_fd == -1) continue;
  char buf[1000];
  int n = read(comm_fd, buf, sizeof(buf)-1);
  buf[n] = 0;
  if (strcmp(buf, expected_comm) == 0) {
    pidfd_send_signal(procfs_pid_fd, SIGKILL, NULL, 0);
  }
}

If you want to avoid using a procfs fd for this, I think you can still
do it, the dance just gets more complicated:

for each pid {
  procfs_pid_fd = open("/proc/"+pid);
  if (procfs_pid_fd == -1) continue;
  pid_fd = pidfd_open(pid, 0);
  if (pid_fd == -1) continue;
  /* at this point procfs_pid_fd and pid_fd may refer to different processes */
  comm_fd = openat(procfs_pid_fd, "comm");
  if (comm_fd == -1) continue;
  /* at this point we know that procfs_pid_fd and pid_fd refer to the
same struct pid, because otherwise the procfs_pid_fd must point to a
directory that throws -ESRCH for everything */
  char buf[1000];
  int n = read(comm_fd, buf, sizeof(buf)-1);
  buf[n] = 0;
  if (strcmp(buf, expected_comm) == 0) {
    pidfd_send_signal(pid_fd, SIGKILL, NULL, 0);
  }
}

But I don't think anyone is actually interested in using pidfds for
this kind of usecase right now.

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: For review: pidfd_send_signal(2) manual page
  2019-09-24 21:08           ` Daniel Colascione
@ 2019-09-25 13:46             ` Michael Kerrisk (man-pages)
  0 siblings, 0 replies; 23+ messages in thread
From: Michael Kerrisk (man-pages) @ 2019-09-25 13:46 UTC (permalink / raw)
  To: Daniel Colascione
  Cc: mtk.manpages, Christian Brauner, Florian Weimer, Oleg Nesterov,
	Jann Horn, Eric W. Biederman, Joel Fernandes, linux-man,
	Linux API, lkml

Hello Daniel,

On 9/24/19 11:08 PM, Daniel Colascione wrote:
> On Tue, Sep 24, 2019 at 2:00 PM Michael Kerrisk (man-pages)
> <mtk.manpages@gmail.com> wrote:
>>
>> Hello Christian,
>>
>>>>> If you're the parent of the process you can do this without CLONE_PIDFD:
>>>>> pid = fork();
>>>>> pidfd = pidfd_open();
>>>>> ret = pidfd_send_signal(pidfd, 0, NULL, 0);
>>>>> if (ret < 0 && errno == ESRCH)
>>>>>     /* pidfd refers to another, recycled process */
>>>>
>>>> Although there is still the race between the fork() and the
>>>> pidfd_open(), right?
>>>
>>> Actually no and my code is even too complex.
>>> If you are the parent, and this is really a sequence that obeys the
>>> ordering pidfd_open() before waiting:
>>>
>>> pid = fork();
>>> if (pid == 0)
>>>       exit(EXIT_SUCCESS);
>>> pidfd = pidfd_open(pid, 0);
>>> waitid(pid, ...);
>>>
>>> Then you are guaranteed that pidfd will refer to pid. No recycling can
>>> happen since the process has not been waited upon yet (That is,
>>
>> D'oh! Yes, of course.
> 
> You still have a race if you're the parent and you have SIGCHLD set to
> SIG_IGN though.

Yes, thanks for reminding me of that (as did Christian also).

>>> excluding special cases such as where you have a mainloop where a
>>> callback reacts to a SIGCHLD event and waits on the child behind your
>>> back and your next callback in the mainloop calls pidfd_open() while the
>>> pid has been recycled etc.).
> 
> That's a pretty common case though, especially if you're a library.

I presume that conventionally the only real resolution of this kind of
problem is that the mainloop SIGCHLD call back has a waitpid() loop
that (in a nonblocking fashion) loops checking each of the PIDs created
by the mainloop, right?

>>> A race could only appear in sequences where waiting happens before
>>> pidfd_open():
>>>
>>> pid = fork();
>>> if (pid == 0)
>>>       exit(EXIT_SUCCESS);
>>> waitid(pid, ...);
>>> pidfd = pidfd_open(pid, 0);
>>>
>>> which honestly simply doesn't make any sense. So if you're the parent
>>> and you combine fork() + pidfd_open() correctly things should be fine
>>> without even having to verify via pidfd_send_signal() (I missed that in
>>> my first mail.).
>>
>> Thanks for the additional detail.
>>
>> I added the following to the pidfd_open() page, to
>> prevent people making the same thinko as me:
>>
>>        The following code sequence can be used to obtain a file  descrip‐
>>        tor for the child of fork(2):
>>
>>            pid = fork();
>>            if (pid > 0) {     /* If parent */
>>                pidfd = pidfd_open(pid, 0);
>>                ...
>>            }
>>
>>        Even  if  the  child process has already terminated by the time of
>>        the pidfd_open() call, the returned file descriptor is  guaranteed
>>        to refer to the child because the parent has not yet waited on the
>>        child (and therefore, the child's ID has not been recycled).
> 
> I'd prefer that sample code be robust in all cases.

I'm not clear what you think is missing. Or do you mean that the code
can't be robust in the face of (1) waitpid(-1) in another thread or an
asynchronous SIGCHLD handler and (2) SIGCHLD disposition set to SIG_IGN?

Thanks,

Michael

-- 
Michael Kerrisk
Linux man-pages maintainer; http://www.kernel.org/doc/man-pages/
Linux/UNIX System Programming Training: http://man7.org/training/

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: For review: pidfd_send_signal(2) manual page
  2019-09-24 21:53           ` Christian Brauner
@ 2019-09-25 13:46             ` Michael Kerrisk (man-pages)
  2019-09-25 13:51               ` Florian Weimer
  2019-09-25 13:53               ` Christian Brauner
  0 siblings, 2 replies; 23+ messages in thread
From: Michael Kerrisk (man-pages) @ 2019-09-25 13:46 UTC (permalink / raw)
  To: Christian Brauner
  Cc: mtk.manpages, Florian Weimer, Oleg Nesterov, Jann Horn,
	Eric W. Biederman, Daniel Colascione, Joel Fernandes, linux-man,
	Linux API, lkml

On 9/24/19 11:53 PM, Christian Brauner wrote:
> On Tue, Sep 24, 2019 at 11:00:03PM +0200, Michael Kerrisk (man-pages) wrote:
>> Hello Christian,
>>
>>>>> If you're the parent of the process you can do this without CLONE_PIDFD:
>>>>> pid = fork();
>>>>> pidfd = pidfd_open();
>>>>> ret = pidfd_send_signal(pidfd, 0, NULL, 0);
>>>>> if (ret < 0 && errno == ESRCH)
>>>>> 	/* pidfd refers to another, recycled process */
>>>>
>>>> Although there is still the race between the fork() and the
>>>> pidfd_open(), right?
>>>
>>> Actually no and my code is even too complex.
>>> If you are the parent, and this is really a sequence that obeys the
>>> ordering pidfd_open() before waiting:
>>>
>>> pid = fork();
>>> if (pid == 0)
>>> 	exit(EXIT_SUCCESS);
>>> pidfd = pidfd_open(pid, 0);
>>> waitid(pid, ...);
>>>
>>> Then you are guaranteed that pidfd will refer to pid. No recycling can
>>> happen since the process has not been waited upon yet (That is,
>>
>> D'oh! Yes, of course. 
>>
>>> excluding special cases such as where you have a mainloop where a
>>> callback reacts to a SIGCHLD event and waits on the child behind your
>>> back and your next callback in the mainloop calls pidfd_open() while the
>>> pid has been recycled etc.).
>>> A race could only appear in sequences where waiting happens before
>>> pidfd_open():
>>>
>>> pid = fork();
>>> if (pid == 0)
>>> 	exit(EXIT_SUCCESS);
>>> waitid(pid, ...);
>>> pidfd = pidfd_open(pid, 0);
>>>
>>> which honestly simply doesn't make any sense. So if you're the parent
>>> and you combine fork() + pidfd_open() correctly things should be fine
>>> without even having to verify via pidfd_send_signal() (I missed that in
>>> my first mail.).
>>
>> Thanks for the additional detail.
> 
> You're very welcome.
> 
>>
>> I added the following to the pidfd_open() page, to
>> prevent people making the same thinko as me:
>>
>>        The following code sequence can be used to obtain a file  descrip‐
>>        tor for the child of fork(2):
>>
>>            pid = fork();
>>            if (pid > 0) {     /* If parent */
>>                pidfd = pidfd_open(pid, 0);
>>                ...
>>            }
>>
>>        Even  if  the  child process has already terminated by the time of
>>        the pidfd_open() call, the returned file descriptor is  guaranteed
>>        to refer to the child because the parent has not yet waited on the
>>        child (and therefore, the child's ID has not been recycled).
> 
> Thanks! I'm fine with the example. The code illustrates the basics. If
> you want to go overboard, you can mention my callback example and put my
> SIG_IGN code snippet from my earlier mails (cf. [1] and [2]) in there.
> But imho, that'll complicate the manpage and I'm not sure it's worth it.

I agree that we should not complicate this discussion with more code,
but how about we refine the text as follows:

       The following code sequence can be used to obtain a file  descrip‐
       tor for the child of fork(2):

           pid = fork();
           if (pid > 0) {     /* If parent */
               pidfd = pidfd_open(pid, 0);
               ...
           }

       Even  if  the  child  has  already  terminated  by the time of the
       pidfd_open() call, its PID will not have  been  recycled  and  the
       returned  file  descriptor  will  refer  to  the  resulting zombie
       process.  Note, however, that this is guaranteed only if the  fol‐
       lowing conditions hold true:

       *  the  disposition  of  SIGCHLD  has  not  been explicitly set to
          SIG_IGN (see sigaction(2)); and

       *  the zombie process was not  reaped  elsewhere  in  the  program
          (e.g.,  either  by an asynchronously executed signal handler or
          by wait(2) or similar in another thread).

       If these conditions don't hold true, then the child process should
       instead be created using clone(2) with the CLONE_PID flag.

Thanks,

Michael

-- 
Michael Kerrisk
Linux man-pages maintainer; http://www.kernel.org/doc/man-pages/
Linux/UNIX System Programming Training: http://man7.org/training/

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: For review: pidfd_send_signal(2) manual page
  2019-09-25 13:46             ` Michael Kerrisk (man-pages)
@ 2019-09-25 13:51               ` Florian Weimer
  2019-09-25 14:02                 ` Michael Kerrisk (man-pages)
  2019-09-25 13:53               ` Christian Brauner
  1 sibling, 1 reply; 23+ messages in thread
From: Florian Weimer @ 2019-09-25 13:51 UTC (permalink / raw)
  To: Michael Kerrisk (man-pages)
  Cc: Christian Brauner, Oleg Nesterov, Jann Horn, Eric W. Biederman,
	Daniel Colascione, Joel Fernandes, linux-man, Linux API, lkml

* Michael Kerrisk:

>        If these conditions don't hold true, then the child process should
>        instead be created using clone(2) with the CLONE_PID flag.

I think this should be CLONE_PIDFD.

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: For review: pidfd_send_signal(2) manual page
  2019-09-25 13:46             ` Michael Kerrisk (man-pages)
  2019-09-25 13:51               ` Florian Weimer
@ 2019-09-25 13:53               ` Christian Brauner
  2019-09-25 14:29                 ` Michael Kerrisk (man-pages)
  1 sibling, 1 reply; 23+ messages in thread
From: Christian Brauner @ 2019-09-25 13:53 UTC (permalink / raw)
  To: Michael Kerrisk (man-pages)
  Cc: Florian Weimer, Oleg Nesterov, Jann Horn, Eric W. Biederman,
	Daniel Colascione, Joel Fernandes, linux-man, Linux API, lkml

On Wed, Sep 25, 2019 at 03:46:26PM +0200, Michael Kerrisk (man-pages) wrote:
> On 9/24/19 11:53 PM, Christian Brauner wrote:
> > On Tue, Sep 24, 2019 at 11:00:03PM +0200, Michael Kerrisk (man-pages) wrote:
> >> Hello Christian,
> >>
> >>>>> If you're the parent of the process you can do this without CLONE_PIDFD:
> >>>>> pid = fork();
> >>>>> pidfd = pidfd_open();
> >>>>> ret = pidfd_send_signal(pidfd, 0, NULL, 0);
> >>>>> if (ret < 0 && errno == ESRCH)
> >>>>> 	/* pidfd refers to another, recycled process */
> >>>>
> >>>> Although there is still the race between the fork() and the
> >>>> pidfd_open(), right?
> >>>
> >>> Actually no and my code is even too complex.
> >>> If you are the parent, and this is really a sequence that obeys the
> >>> ordering pidfd_open() before waiting:
> >>>
> >>> pid = fork();
> >>> if (pid == 0)
> >>> 	exit(EXIT_SUCCESS);
> >>> pidfd = pidfd_open(pid, 0);
> >>> waitid(pid, ...);
> >>>
> >>> Then you are guaranteed that pidfd will refer to pid. No recycling can
> >>> happen since the process has not been waited upon yet (That is,
> >>
> >> D'oh! Yes, of course. 
> >>
> >>> excluding special cases such as where you have a mainloop where a
> >>> callback reacts to a SIGCHLD event and waits on the child behind your
> >>> back and your next callback in the mainloop calls pidfd_open() while the
> >>> pid has been recycled etc.).
> >>> A race could only appear in sequences where waiting happens before
> >>> pidfd_open():
> >>>
> >>> pid = fork();
> >>> if (pid == 0)
> >>> 	exit(EXIT_SUCCESS);
> >>> waitid(pid, ...);
> >>> pidfd = pidfd_open(pid, 0);
> >>>
> >>> which honestly simply doesn't make any sense. So if you're the parent
> >>> and you combine fork() + pidfd_open() correctly things should be fine
> >>> without even having to verify via pidfd_send_signal() (I missed that in
> >>> my first mail.).
> >>
> >> Thanks for the additional detail.
> > 
> > You're very welcome.
> > 
> >>
> >> I added the following to the pidfd_open() page, to
> >> prevent people making the same thinko as me:
> >>
> >>        The following code sequence can be used to obtain a file  descrip‐
> >>        tor for the child of fork(2):
> >>
> >>            pid = fork();
> >>            if (pid > 0) {     /* If parent */
> >>                pidfd = pidfd_open(pid, 0);
> >>                ...
> >>            }
> >>
> >>        Even  if  the  child process has already terminated by the time of
> >>        the pidfd_open() call, the returned file descriptor is  guaranteed
> >>        to refer to the child because the parent has not yet waited on the
> >>        child (and therefore, the child's ID has not been recycled).
> > 
> > Thanks! I'm fine with the example. The code illustrates the basics. If
> > you want to go overboard, you can mention my callback example and put my
> > SIG_IGN code snippet from my earlier mails (cf. [1] and [2]) in there.
> > But imho, that'll complicate the manpage and I'm not sure it's worth it.
> 
> I agree that we should not complicate this discussion with more code,
> but how about we refine the text as follows:
> 
>        The following code sequence can be used to obtain a file  descrip‐
>        tor for the child of fork(2):
> 
>            pid = fork();
>            if (pid > 0) {     /* If parent */
>                pidfd = pidfd_open(pid, 0);
>                ...
>            }
> 
>        Even  if  the  child  has  already  terminated  by the time of the
>        pidfd_open() call, its PID will not have  been  recycled  and  the
>        returned  file  descriptor  will  refer  to  the  resulting zombie
>        process.  Note, however, that this is guaranteed only if the  fol‐
>        lowing conditions hold true:
> 
>        *  the  disposition  of  SIGCHLD  has  not  been explicitly set to
>           SIG_IGN (see sigaction(2)); and

Ugh, I forgot a third one. There's also SA_NOCLDWAIT. When set and
the SIGCHLD handler is set to SIG_DFL then no zombie processes are
created and no SIGCHLD signal is sent. When an explicit handler for
SIGCHLD is set then a SIGCHLD signal is generated but the process will
still not be turned into a zombie...

> 
>        *  the zombie process was not  reaped  elsewhere  in  the  program
>           (e.g.,  either  by an asynchronously executed signal handler or
>           by wait(2) or similar in another thread).
> 
>        If these conditions don't hold true, then the child process should

"If any of these conditions does not hold, the child process..."

That might be clearer. But I leave the call on that to you. :)

Christian

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: For review: pidfd_send_signal(2) manual page
  2019-09-25 13:51               ` Florian Weimer
@ 2019-09-25 14:02                 ` Michael Kerrisk (man-pages)
  0 siblings, 0 replies; 23+ messages in thread
From: Michael Kerrisk (man-pages) @ 2019-09-25 14:02 UTC (permalink / raw)
  To: Florian Weimer
  Cc: mtk.manpages, Christian Brauner, Oleg Nesterov, Jann Horn,
	Eric W. Biederman, Daniel Colascione, Joel Fernandes, linux-man,
	Linux API, lkml

On 9/25/19 3:51 PM, Florian Weimer wrote:
> * Michael Kerrisk:
> 
>>        If these conditions don't hold true, then the child process should
>>        instead be created using clone(2) with the CLONE_PID flag.
> 
> I think this should be CLONE_PIDFD.

Thanks Florian. Fixed.

Cheers,

Michael


-- 
Michael Kerrisk
Linux man-pages maintainer; http://www.kernel.org/doc/man-pages/
Linux/UNIX System Programming Training: http://man7.org/training/

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: For review: pidfd_send_signal(2) manual page
  2019-09-25 13:53               ` Christian Brauner
@ 2019-09-25 14:29                 ` Michael Kerrisk (man-pages)
  0 siblings, 0 replies; 23+ messages in thread
From: Michael Kerrisk (man-pages) @ 2019-09-25 14:29 UTC (permalink / raw)
  To: Christian Brauner
  Cc: mtk.manpages, Florian Weimer, Oleg Nesterov, Jann Horn,
	Eric W. Biederman, Daniel Colascione, Joel Fernandes, linux-man,
	Linux API, lkml

Hello Christian,

On 9/25/19 3:53 PM, Christian Brauner wrote:
> On Wed, Sep 25, 2019 at 03:46:26PM +0200, Michael Kerrisk (man-pages) wrote:
>> On 9/24/19 11:53 PM, Christian Brauner wrote:
>>> On Tue, Sep 24, 2019 at 11:00:03PM +0200, Michael Kerrisk (man-pages) wrote:
>>>> Hello Christian,
>>>>
>>>>>>> If you're the parent of the process you can do this without CLONE_PIDFD:
>>>>>>> pid = fork();
>>>>>>> pidfd = pidfd_open();
>>>>>>> ret = pidfd_send_signal(pidfd, 0, NULL, 0);
>>>>>>> if (ret < 0 && errno == ESRCH)
>>>>>>> 	/* pidfd refers to another, recycled process */
>>>>>>
>>>>>> Although there is still the race between the fork() and the
>>>>>> pidfd_open(), right?
>>>>>
>>>>> Actually no and my code is even too complex.
>>>>> If you are the parent, and this is really a sequence that obeys the
>>>>> ordering pidfd_open() before waiting:
>>>>>
>>>>> pid = fork();
>>>>> if (pid == 0)
>>>>> 	exit(EXIT_SUCCESS);
>>>>> pidfd = pidfd_open(pid, 0);
>>>>> waitid(pid, ...);
>>>>>
>>>>> Then you are guaranteed that pidfd will refer to pid. No recycling can
>>>>> happen since the process has not been waited upon yet (That is,
>>>>
>>>> D'oh! Yes, of course. 
>>>>
>>>>> excluding special cases such as where you have a mainloop where a
>>>>> callback reacts to a SIGCHLD event and waits on the child behind your
>>>>> back and your next callback in the mainloop calls pidfd_open() while the
>>>>> pid has been recycled etc.).
>>>>> A race could only appear in sequences where waiting happens before
>>>>> pidfd_open():
>>>>>
>>>>> pid = fork();
>>>>> if (pid == 0)
>>>>> 	exit(EXIT_SUCCESS);
>>>>> waitid(pid, ...);
>>>>> pidfd = pidfd_open(pid, 0);
>>>>>
>>>>> which honestly simply doesn't make any sense. So if you're the parent
>>>>> and you combine fork() + pidfd_open() correctly things should be fine
>>>>> without even having to verify via pidfd_send_signal() (I missed that in
>>>>> my first mail.).
>>>>
>>>> Thanks for the additional detail.
>>>
>>> You're very welcome.
>>>
>>>>
>>>> I added the following to the pidfd_open() page, to
>>>> prevent people making the same thinko as me:
>>>>
>>>>        The following code sequence can be used to obtain a file  descrip‐
>>>>        tor for the child of fork(2):
>>>>
>>>>            pid = fork();
>>>>            if (pid > 0) {     /* If parent */
>>>>                pidfd = pidfd_open(pid, 0);
>>>>                ...
>>>>            }
>>>>
>>>>        Even  if  the  child process has already terminated by the time of
>>>>        the pidfd_open() call, the returned file descriptor is  guaranteed
>>>>        to refer to the child because the parent has not yet waited on the
>>>>        child (and therefore, the child's ID has not been recycled).
>>>
>>> Thanks! I'm fine with the example. The code illustrates the basics. If
>>> you want to go overboard, you can mention my callback example and put my
>>> SIG_IGN code snippet from my earlier mails (cf. [1] and [2]) in there.
>>> But imho, that'll complicate the manpage and I'm not sure it's worth it.
>>
>> I agree that we should not complicate this discussion with more code,
>> but how about we refine the text as follows:
>>
>>        The following code sequence can be used to obtain a file  descrip‐
>>        tor for the child of fork(2):
>>
>>            pid = fork();
>>            if (pid > 0) {     /* If parent */
>>                pidfd = pidfd_open(pid, 0);
>>                ...
>>            }
>>
>>        Even  if  the  child  has  already  terminated  by the time of the
>>        pidfd_open() call, its PID will not have  been  recycled  and  the
>>        returned  file  descriptor  will  refer  to  the  resulting zombie
>>        process.  Note, however, that this is guaranteed only if the  fol‐
>>        lowing conditions hold true:
>>
>>        *  the  disposition  of  SIGCHLD  has  not  been explicitly set to
>>           SIG_IGN (see sigaction(2)); and
> 
> Ugh, I forgot a third one. There's also SA_NOCLDWAIT. When set and
> the SIGCHLD handler is set to SIG_DFL then no zombie processes are
> created and no SIGCHLD signal is sent. When an explicit handler for
> SIGCHLD is set then a SIGCHLD signal is generated but the process will
> still not be turned into a zombie...

Oh, yes. I added:

       *  the SA_NOCLDSTOP flag was not specified  while  establishing  a
          handler  for  SIGCHLD  or while setting the disposition of that
          signal to SIG_DFL (see sigaction(2));

>>        *  the zombie process was not  reaped  elsewhere  in  the  program
>>           (e.g.,  either  by an asynchronously executed signal handler or
>>           by wait(2) or similar in another thread).
>>
>>        If these conditions don't hold true, then the child process should
> 
> "If any of these conditions does not hold, the child process..."
> 
> That might be clearer. But I leave the call on that to you. :)

Yep, your wording is better. Fixed.

Thanks,

Michael

-- 
Michael Kerrisk
Linux man-pages maintainer; http://www.kernel.org/doc/man-pages/
Linux/UNIX System Programming Training: http://man7.org/training/

^ permalink raw reply	[flat|nested] 23+ messages in thread

end of thread, other threads:[~2019-09-25 14:30 UTC | newest]

Thread overview: 23+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-09-23  9:12 For review: pidfd_send_signal(2) manual page Michael Kerrisk (man-pages)
2019-09-23 11:26 ` Florian Weimer
2019-09-23 14:23   ` Christian Brauner
2019-09-24 19:44     ` Michael Kerrisk (man-pages)
2019-09-24 19:57       ` Christian Brauner
2019-09-24 20:07         ` Christian Brauner
2019-09-24 21:00         ` Michael Kerrisk (man-pages)
2019-09-24 21:08           ` Daniel Colascione
2019-09-25 13:46             ` Michael Kerrisk (man-pages)
2019-09-24 21:53           ` Christian Brauner
2019-09-25 13:46             ` Michael Kerrisk (man-pages)
2019-09-25 13:51               ` Florian Weimer
2019-09-25 14:02                 ` Michael Kerrisk (man-pages)
2019-09-25 13:53               ` Christian Brauner
2019-09-25 14:29                 ` Michael Kerrisk (man-pages)
2019-09-24 19:43   ` Michael Kerrisk (man-pages)
2019-09-25  1:48   ` Jann Horn
2019-09-23 11:31 ` Daniel Colascione
2019-09-24 19:42   ` Michael Kerrisk (man-pages)
2019-09-23 14:29 ` Christian Brauner
2019-09-23 20:27   ` Michael Kerrisk (man-pages)
2019-09-23 21:27 ` Eric W. Biederman
2019-09-24 19:10   ` Michael Kerrisk (man-pages)

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).