Subject: For review: pidfd_send_signal(2) manual page

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)


2019-09-25 11:59:11

by Daniel Colascione

[permalink] [raw]
Subject: Re: For review: pidfd_send_signal(2) manual page

On Mon, Sep 23, 2019 at 2:12 AM Michael Kerrisk (man-pages)
<[email protected]> 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".

2019-09-25 17:41:52

by Christian Brauner

[permalink] [raw]
Subject: Re: For review: pidfd_send_signal(2) manual page

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)
>

Subject: Re: For review: pidfd_send_signal(2) manual page

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/

2019-09-26 06:06:29

by Eric W. Biederman

[permalink] [raw]
Subject: Re: For review: pidfd_send_signal(2) manual page

"Michael Kerrisk (man-pages)" <[email protected]> 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

Subject: Re: For review: pidfd_send_signal(2) manual page

On 9/23/19 11:27 PM, Eric W. Biederman wrote:
> "Michael Kerrisk (man-pages)" <[email protected]> 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/

Subject: Re: For review: pidfd_send_signal(2) manual page

On 9/23/19 1:31 PM, Daniel Colascione wrote:
> On Mon, Sep 23, 2019 at 2:12 AM Michael Kerrisk (man-pages)
> <[email protected]> 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/

2019-09-26 09:34:02

by Christian Brauner

[permalink] [raw]
Subject: Re: For review: pidfd_send_signal(2) manual page

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

2019-09-26 09:34:10

by Florian Weimer

[permalink] [raw]
Subject: Re: For review: pidfd_send_signal(2) manual page

* 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.

Subject: Re: For review: pidfd_send_signal(2) manual page

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/

Subject: Re: For review: pidfd_send_signal(2) manual page

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/