[v7,8/8] linux: Add pidfd_getpid

Message ID 20230803163558.991832-9-adhemerval.zanella@linaro.org
State Superseded
Headers
Series Add pidfd and cgroupv2 support for process creation |

Checks

Context Check Description
redhat-pt-bot/TryBot-apply_patch success Patch applied to master at the time it was sent
redhat-pt-bot/TryBot-32bit success Build for i686
linaro-tcwg-bot/tcwg_glibc_build--master-aarch64 success Testing passed
linaro-tcwg-bot/tcwg_glibc_check--master-arm fail Testing failed
linaro-tcwg-bot/tcwg_glibc_check--master-aarch64 fail Testing failed
linaro-tcwg-bot/tcwg_glibc_build--master-arm success Testing passed

Commit Message

Adhemerval Zanella Netto Aug. 3, 2023, 4:35 p.m. UTC
  This interface allows to obtain the associated process ID from the
process file descriptor.  It is done by parsing the procps fdinfo
information.  Its prototype is:

   pid_t pidfd_getpid (int fd)

It returns the associated pid or -1 in case of an error and sets the
errno accordingly.  The possible errno values are those from open,
read, and close (used on procps parsing), along with:

   - EBADF if the FD is negative, does not have a PID associatedi, or
     if the fdinfo fields contains a value larger than pid_t.

   - EREMOTE if the PID is in a separate namespace.

   - ESRCH if the process is already terminated.

Checked on x86_64-linux-gnu on Linux 4.15 (no CLONE_PID or waitid
support), Linux 5.15 (only clone support), and Linux 5.19 (full
support including clone3).
---
 NEWS                                          |   4 +
 manual/process.texi                           |  31 +++
 sysdeps/unix/sysv/linux/Makefile              |   3 +
 sysdeps/unix/sysv/linux/Versions              |   1 +
 sysdeps/unix/sysv/linux/aarch64/libc.abilist  |   1 +
 sysdeps/unix/sysv/linux/alpha/libc.abilist    |   1 +
 sysdeps/unix/sysv/linux/arc/libc.abilist      |   1 +
 sysdeps/unix/sysv/linux/arm/be/libc.abilist   |   1 +
 sysdeps/unix/sysv/linux/arm/le/libc.abilist   |   1 +
 sysdeps/unix/sysv/linux/csky/libc.abilist     |   1 +
 sysdeps/unix/sysv/linux/hppa/libc.abilist     |   1 +
 sysdeps/unix/sysv/linux/i386/libc.abilist     |   1 +
 sysdeps/unix/sysv/linux/ia64/libc.abilist     |   1 +
 .../sysv/linux/loongarch/lp64/libc.abilist    |   1 +
 .../sysv/linux/m68k/coldfire/libc.abilist     |   1 +
 .../unix/sysv/linux/m68k/m680x0/libc.abilist  |   1 +
 .../sysv/linux/microblaze/be/libc.abilist     |   1 +
 .../sysv/linux/microblaze/le/libc.abilist     |   1 +
 .../sysv/linux/mips/mips32/fpu/libc.abilist   |   1 +
 .../sysv/linux/mips/mips32/nofpu/libc.abilist |   1 +
 .../sysv/linux/mips/mips64/n32/libc.abilist   |   1 +
 .../sysv/linux/mips/mips64/n64/libc.abilist   |   1 +
 sysdeps/unix/sysv/linux/nios2/libc.abilist    |   1 +
 sysdeps/unix/sysv/linux/or1k/libc.abilist     |   1 +
 sysdeps/unix/sysv/linux/pidfd_getpid.c        | 122 ++++++++++++
 .../linux/powerpc/powerpc32/fpu/libc.abilist  |   1 +
 .../powerpc/powerpc32/nofpu/libc.abilist      |   1 +
 .../linux/powerpc/powerpc64/be/libc.abilist   |   1 +
 .../linux/powerpc/powerpc64/le/libc.abilist   |   1 +
 sysdeps/unix/sysv/linux/procutils.c           | 104 ++++++++++
 sysdeps/unix/sysv/linux/procutils.h           |  35 ++++
 .../unix/sysv/linux/riscv/rv32/libc.abilist   |   1 +
 .../unix/sysv/linux/riscv/rv64/libc.abilist   |   1 +
 .../unix/sysv/linux/s390/s390-32/libc.abilist |   1 +
 .../unix/sysv/linux/s390/s390-64/libc.abilist |   1 +
 sysdeps/unix/sysv/linux/sh/be/libc.abilist    |   1 +
 sysdeps/unix/sysv/linux/sh/le/libc.abilist    |   1 +
 .../sysv/linux/sparc/sparc32/libc.abilist     |   1 +
 .../sysv/linux/sparc/sparc64/libc.abilist     |   1 +
 sysdeps/unix/sysv/linux/sys/pidfd.h           |   4 +
 sysdeps/unix/sysv/linux/tst-pidfd.c           |  47 +++++
 sysdeps/unix/sysv/linux/tst-pidfd_getpid.c    | 187 ++++++++++++++++++
 .../unix/sysv/linux/x86_64/64/libc.abilist    |   1 +
 .../unix/sysv/linux/x86_64/x32/libc.abilist   |   1 +
 44 files changed, 572 insertions(+)
 create mode 100644 sysdeps/unix/sysv/linux/pidfd_getpid.c
 create mode 100644 sysdeps/unix/sysv/linux/procutils.c
 create mode 100644 sysdeps/unix/sysv/linux/procutils.h
 create mode 100644 sysdeps/unix/sysv/linux/tst-pidfd_getpid.c
  

Comments

Florian Weimer Aug. 11, 2023, 2:36 p.m. UTC | #1
* Adhemerval Zanella via Libc-alpha:

> +If the operation fails, @code{pidfd_getpid} return @code{-1} and the following
> +@code{errno} error conditionas are defined:
> +
> +@table @code
> +@item EBADF
> +The input file descriptor is invalid, does not have a pidfd associated, or an
> +error has occurred parsing the kernel data.
> +@item EREMOTE
> +There is no process ID to denote the process in the current namespace.
> +@item ESRCH
> +The process for which the file descriptor refers to is terminated.
> +@end table

Maybe document ENOENT (/proc not mounted), ENFILE, EMFILE, ENOMEM as
well?

There are missing spaces in a few places:

+  while (*l == ' ' || (unsigned int)(*l) -'\t' < 5)
+      if ((unsigned int)(*l) - '0' >= 10)
+typedef int (*procutils_closure_t)(const char *line, void *arg);
+      char buf[CMSG_SPACE(sizeof(int))];

> diff --git a/sysdeps/unix/sysv/linux/pidfd_getpid.c b/sysdeps/unix/sysv/linux/pidfd_getpid.c
> new file mode 100644
> index 0000000000..46848a5983
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/pidfd_getpid.c

> +  bool neg = false;
> +  switch (*l)
> +    {
> +    case '-': neg = true;
> +    case '+': l++;

'+' should probably return -1?

> +    }
> +
> +  if (*l == '\0')
> +    return 0;
> +
> +  int n = 0;
> +  while (*l != '\0')
> +    {
> +      /* Check if '*l' is a digit.  */
> +      if ((unsigned int)(*l) - '0' >= 10)

It's a strange way to write this condition. '0' <= *l && l <= '9' should
work equally well.  I know is supposed to optimize this into one
condition, but it's not immediately obvious why this works with an early
cast of unsigned int instead of unsigned char.

> +      /* Ignore invalid large values.  */
> +      if (INT_MULTIPLY_WRAPV (10, n, &n)
> +          || INT_ADD_WRAPV (n, *l++ - '0', &n))
> +        return 0;

Shouldn't this return -1?

I think these error returns should set errno (EINVAL perhaps, since
that's unlikely to come from read or open), so that we have a chance to
identify parser problems.


> diff --git a/sysdeps/unix/sysv/linux/procutils.c b/sysdeps/unix/sysv/linux/procutils.c
> new file mode 100644
> index 0000000000..83b327cb9a
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/procutils.c
> @@ -0,0 +1,104 @@
> +/* Utilities functions to read/parse Linux procfs and sysfs.
> +   Copyright (C) 2023 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <assert.h>
> +#include <not-cancel.h>
> +#include <procutils.h>
> +#include <string.h>
> +
> +static int
> +next_line (char **r, int fd, char *const buffer, char **cp, char **re,
> +           char *const buffer_end)
> +{
> +  char *res = *cp;
> +  char *nl = memchr (*cp, '\n', *re - *cp);
> +  if (nl == NULL)
> +    {
> +      if (*cp != buffer)
> +        {
> +          if (*re == buffer_end)
> +            {
> +              memmove (buffer, *cp, *re - *cp);
> +              *re = buffer + (*re - *cp);
> +              *cp = buffer;
> +
> +              ssize_t n = __read_nocancel (fd, *re, buffer_end - *re);

Missing TEMP_FAILURE_RETRY, I would (also below, and further below for
__open64_nocancel).

> +              if (n < 0)
> +                return -1;
> +
> +              *re += n;
> +
> +              nl = memchr (*cp, '\n', *re - *cp);
> +              while (nl == NULL && *re == buffer_end)
> +                {
> +                  /* Truncate too long lines.  */
> +                  *re = buffer + 3 * (buffer_end - buffer) / 4;
> +                  n = __read_nocancel (fd, *re, buffer_end - *re);
> +                  if (n < 0)
> +                    return -1;
> +
> +                  nl = memchr (*re, '\n', n);
> +                  **re = '\0';
> +                  *re += n;
> +                }

Should we just skip long lines?  The 3/4 business is a bit strange.

This results in an endless loop if the file does not end with '\n', I
think.

> diff --git a/sysdeps/unix/sysv/linux/procutils.h b/sysdeps/unix/sysv/linux/procutils.h
> new file mode 100644
> index 0000000000..64e1080920
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/procutils.h
> @@ -0,0 +1,35 @@

> +typedef int (*procutils_closure_t)(const char *line, void *arg);
> +
> +/* Open and read the path FILENAME, line per line, and call CLOSURE with
> +   argument ARG on each line.  The read is done with a static buffer,
> +   with non-cancellable calls, and the line is null terminated.
> +
> +   The CLOSURE should return true if the read should continue, or false
> +   if the function should stop.
> +
> +   It returns 0 in case of success, or -1 otherwise.  */
> +int procutils_read_file (const char *filename, procutils_closure_t closure,
> +			 void *arg) attribute_hidden;
> +

A comment should say whether '\n' is included in line argument, and what
happens to overlong lines.

The return value for the closure should be an actual bool, or otherwise
the int return value should be passed through to the caller of
procutils_read_file.

> diff --git a/sysdeps/unix/sysv/linux/tst-pidfd.c b/sysdeps/unix/sysv/linux/tst-pidfd.c
> index 64d8a2ef40..53d223f702 100644
> --- a/sysdeps/unix/sysv/linux/tst-pidfd.c
> +++ b/sysdeps/unix/sysv/linux/tst-pidfd.c
> @@ -18,6 +18,7 @@

> +  /* Check if pidfd_getpid returns ESRCH for exited subprocess.  */
> +  {
> +    int pidfd;
> +    pid_t pidfork = pidfd_fork (&pidfd, -1, 0);
> +    if (pidfork == 0)
> +      _exit (EXIT_SUCCESS);
> +
> +    /* The process might be still running or already in zombie state, in any
> +       case the PID is still allocated to the process.  */
> +    pid_t pid = pidfd_getpid (pidfd);
> +    if (pid > 0)
> +      support_process_state_wait (pid, support_process_state_zombie);

The condition does not match the comment.  I don't know which one is
correct.  Please verify that pid > 0 (if the PID remains available), or
change the comment to “in [either] case”.

> diff --git a/sysdeps/unix/sysv/linux/tst-pidfd_getpid.c b/sysdeps/unix/sysv/linux/tst-pidfd_getpid.c
> new file mode 100644
> index 0000000000..41d03a04ad
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/tst-pidfd_getpid.c

> +  TEST_VERIFY_EXIT (socketpair (AF_UNIX, SOCK_STREAM, 0, sockfd) == 0);
> +
> +  /* Check if pidfd_getpid returns EREMOTE for process not in current
> +     namespace.  */
> +  {
> +    int pidfd;
> +    pid_t pid = pidfd_fork (&pidfd, -1, 0);

I think you can avoid the file descriptor passing if you call pidfd_fork
to create an unrelated descriptor, and then do the namespace thing below
after another fork.  This way, the descriptor will just be inherited via
fork.

> +    send_fd (sockfd[1], pidfd);
> +
> +    siginfo_t info;
> +    TEST_COMPARE (waitid (P_PIDFD, pidfd, &info, WEXITED), 0);
> +    if (info.si_status == EXIT_UNSUPPORTED)
> +      FAIL_UNSUPPORTED ("unable to unshare user/fs/pid");
> +    TEST_COMPARE (info.si_status, 0);
> +    TEST_COMPARE (info.si_code, CLD_EXITED);

I think this could have a few tests, like the pidfd_getpid value
matching what comes back subsequently in si_pid.

> diff --git a/sysdeps/unix/sysv/linux/sys/pidfd.h b/sysdeps/unix/sysv/linux/sys/pidfd.h
> index 87095212a7..8cf4df6b81 100644
> --- a/sysdeps/unix/sysv/linux/sys/pidfd.h
> +++ b/sysdeps/unix/sysv/linux/sys/pidfd.h
> @@ -67,4 +67,8 @@ extern int pidfd_send_signal (int __pidfd, int __sig, siginfo_t *__info,
>  extern pid_t pidfd_fork (int *__pidfd, int __cgroup, unsigned int __flags)
>    __THROW;
>  
> +/* Query the process ID (PID) from process descriptor __FD.  Return the PID
> +   or -1 in case of an error.  */
> +extern pid_t pidfd_getpid (int __fd) __THROW;
> +

__FD should be FD.


Thanks,
Florian
  
Adhemerval Zanella Netto Aug. 11, 2023, 5:29 p.m. UTC | #2
On 11/08/23 11:36, Florian Weimer wrote:
> * Adhemerval Zanella via Libc-alpha:
> 
>> +If the operation fails, @code{pidfd_getpid} return @code{-1} and the following
>> +@code{errno} error conditionas are defined:
>> +
>> +@table @code
>> +@item EBADF
>> +The input file descriptor is invalid, does not have a pidfd associated, or an
>> +error has occurred parsing the kernel data.
>> +@item EREMOTE
>> +There is no process ID to denote the process in the current namespace.
>> +@item ESRCH
>> +The process for which the file descriptor refers to is terminated.
>> +@end table
> 
> Maybe document ENOENT (/proc not mounted), ENFILE, EMFILE, ENOMEM as
> well?

Ack.

> 
> There are missing spaces in a few places:
> 
> +  while (*l == ' ' || (unsigned int)(*l) -'\t' < 5)
> +      if ((unsigned int)(*l) - '0' >= 10)
> +typedef int (*procutils_closure_t)(const char *line, void *arg);
> +      char buf[CMSG_SPACE(sizeof(int))];
Ack.

> 
>> diff --git a/sysdeps/unix/sysv/linux/pidfd_getpid.c b/sysdeps/unix/sysv/linux/pidfd_getpid.c
>> new file mode 100644
>> index 0000000000..46848a5983
>> --- /dev/null
>> +++ b/sysdeps/unix/sysv/linux/pidfd_getpid.c
> 
>> +  bool neg = false;
>> +  switch (*l)
>> +    {
>> +    case '-': neg = true;
>> +    case '+': l++;
> 
> '+' should probably return -1?

I think it makes sense here indeed.

> 
>> +    }
>> +
>> +  if (*l == '\0')
>> +    return 0;
>> +
>> +  int n = 0;
>> +  while (*l != '\0')
>> +    {
>> +      /* Check if '*l' is a digit.  */
>> +      if ((unsigned int)(*l) - '0' >= 10)
> 
> It's a strange way to write this condition. '0' <= *l && l <= '9' should
> work equally well.  I know is supposed to optimize this into one
> condition, but it's not immediately obvious why this works with an early
> cast of unsigned int instead of unsigned char.

'0' > *l || *l > '9' works for me as well.

> 
>> +      /* Ignore invalid large values.  */
>> +      if (INT_MULTIPLY_WRAPV (10, n, &n)
>> +          || INT_ADD_WRAPV (n, *l++ - '0', &n))
>> +        return 0;
> 
> Shouldn't this return -1?

I think there is no point in returning 0 for any parsing failure when a
'Pid:' line is found (since multiple 'Pid:' entries also do not make
sense).

> 
> I think these error returns should set errno (EINVAL perhaps, since
> that's unlikely to come from read or open), so that we have a chance to
> identify parser problems.

The procutils_read_file already returns -1 for the case the closure
returns before reading whole file, so pidfd_getpid can return EBADF
in the case of an invalid 'Pid:'.  And it also return the same error
for the case 'Pid:' is not existent (procutils_read_file return 0,
but parse_fdinfo_t is { false, ... }).

We can change the later to EINVAL, but I am not sure if really fits
here.  An inexistent 'Pid: ' associated with the input file descriptor
argument means that its not a pidfd one; which imho does make to 
EBADF.

> 
> 
>> diff --git a/sysdeps/unix/sysv/linux/procutils.c b/sysdeps/unix/sysv/linux/procutils.c
>> new file mode 100644
>> index 0000000000..83b327cb9a
>> --- /dev/null
>> +++ b/sysdeps/unix/sysv/linux/procutils.c
>> @@ -0,0 +1,104 @@
>> +/* Utilities functions to read/parse Linux procfs and sysfs.
>> +   Copyright (C) 2023 Free Software Foundation, Inc.
>> +   This file is part of the GNU C Library.
>> +
>> +   The GNU C Library is free software; you can redistribute it and/or
>> +   modify it under the terms of the GNU Lesser General Public
>> +   License as published by the Free Software Foundation; either
>> +   version 2.1 of the License, or (at your option) any later version.
>> +
>> +   The GNU C Library is distributed in the hope that it will be useful,
>> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
>> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> +   Lesser General Public License for more details.
>> +
>> +   You should have received a copy of the GNU Lesser General Public
>> +   License along with the GNU C Library; if not, see
>> +   <https://www.gnu.org/licenses/>.  */
>> +
>> +#include <assert.h>
>> +#include <not-cancel.h>
>> +#include <procutils.h>
>> +#include <string.h>
>> +
>> +static int
>> +next_line (char **r, int fd, char *const buffer, char **cp, char **re,
>> +           char *const buffer_end)
>> +{
>> +  char *res = *cp;
>> +  char *nl = memchr (*cp, '\n', *re - *cp);
>> +  if (nl == NULL)
>> +    {
>> +      if (*cp != buffer)
>> +        {
>> +          if (*re == buffer_end)
>> +            {
>> +              memmove (buffer, *cp, *re - *cp);
>> +              *re = buffer + (*re - *cp);
>> +              *cp = buffer;
>> +
>> +              ssize_t n = __read_nocancel (fd, *re, buffer_end - *re);
> 
> Missing TEMP_FAILURE_RETRY, I would (also below, and further below for
> __open64_nocancel).

Ack.

> 
>> +              if (n < 0)
>> +                return -1;
>> +
>> +              *re += n;
>> +
>> +              nl = memchr (*cp, '\n', *re - *cp);
>> +              while (nl == NULL && *re == buffer_end)
>> +                {
>> +                  /* Truncate too long lines.  */
>> +                  *re = buffer + 3 * (buffer_end - buffer) / 4;
>> +                  n = __read_nocancel (fd, *re, buffer_end - *re);
>> +                  if (n < 0)
>> +                    return -1;
>> +
>> +                  nl = memchr (*re, '\n', n);
>> +                  **re = '\0';
>> +                  *re += n;
>> +                }
> 
> Should we just skip long lines?  The 3/4 business is a bit strange.

So this is essentially the same code from sysdeps/unix/sysv/linux/getsysstats.c
(which I plan to consolidate later), and I agree with you that 3/4 is not really
clear.

> 
> This results in an endless loop if the file does not end with '\n', I
> think.

I think it is meant to be a generic interface to read procfs/sysfs, I
would be better to bail out for long lines. I will change it.

> 
>> diff --git a/sysdeps/unix/sysv/linux/procutils.h b/sysdeps/unix/sysv/linux/procutils.h
>> new file mode 100644
>> index 0000000000..64e1080920
>> --- /dev/null
>> +++ b/sysdeps/unix/sysv/linux/procutils.h
>> @@ -0,0 +1,35 @@
> 
>> +typedef int (*procutils_closure_t)(const char *line, void *arg);
>> +
>> +/* Open and read the path FILENAME, line per line, and call CLOSURE with
>> +   argument ARG on each line.  The read is done with a static buffer,
>> +   with non-cancellable calls, and the line is null terminated.
>> +
>> +   The CLOSURE should return true if the read should continue, or false
>> +   if the function should stop.
>> +
>> +   It returns 0 in case of success, or -1 otherwise.  */
>> +int procutils_read_file (const char *filename, procutils_closure_t closure,
>> +			 void *arg) attribute_hidden;
>> +
> 
> A comment should say whether '\n' is included in line argument, and what
> happens to overlong lines.
> 
> The return value for the closure should be an actual bool, or otherwise
> the int return value should be passed through to the caller of
> procutils_read_file.

Ack, I changed to bool.

> 
>> diff --git a/sysdeps/unix/sysv/linux/tst-pidfd.c b/sysdeps/unix/sysv/linux/tst-pidfd.c
>> index 64d8a2ef40..53d223f702 100644
>> --- a/sysdeps/unix/sysv/linux/tst-pidfd.c
>> +++ b/sysdeps/unix/sysv/linux/tst-pidfd.c
>> @@ -18,6 +18,7 @@
> 
>> +  /* Check if pidfd_getpid returns ESRCH for exited subprocess.  */
>> +  {
>> +    int pidfd;
>> +    pid_t pidfork = pidfd_fork (&pidfd, -1, 0);
>> +    if (pidfork == 0)
>> +      _exit (EXIT_SUCCESS);
>> +
>> +    /* The process might be still running or already in zombie state, in any
>> +       case the PID is still allocated to the process.  */
>> +    pid_t pid = pidfd_getpid (pidfd);
>> +    if (pid > 0)
>> +      support_process_state_wait (pid, support_process_state_zombie);
> 
> The condition does not match the comment.  I don't know which one is
> correct.  Please verify that pid > 0 (if the PID remains available), or
> change the comment to “in [either] case”.

I changed the comment, what this snippet is trying to test if the process
it still executing pid will be positive one and thus we need to wait it
become a zombie. Otherwise we can issue the pidfd_getpid directly.

> 
>> diff --git a/sysdeps/unix/sysv/linux/tst-pidfd_getpid.c b/sysdeps/unix/sysv/linux/tst-pidfd_getpid.c
>> new file mode 100644
>> index 0000000000..41d03a04ad
>> --- /dev/null
>> +++ b/sysdeps/unix/sysv/linux/tst-pidfd_getpid.c
> 
>> +  TEST_VERIFY_EXIT (socketpair (AF_UNIX, SOCK_STREAM, 0, sockfd) == 0);
>> +
>> +  /* Check if pidfd_getpid returns EREMOTE for process not in current
>> +     namespace.  */
>> +  {
>> +    int pidfd;
>> +    pid_t pid = pidfd_fork (&pidfd, -1, 0);
> 
> I think you can avoid the file descriptor passing if you call pidfd_fork
> to create an unrelated descriptor, and then do the namespace thing below
> after another fork.  This way, the descriptor will just be inherited via
> fork.

Ok, I will check if this simplifies things.

> 
>> +    send_fd (sockfd[1], pidfd);
>> +
>> +    siginfo_t info;
>> +    TEST_COMPARE (waitid (P_PIDFD, pidfd, &info, WEXITED), 0);
>> +    if (info.si_status == EXIT_UNSUPPORTED)
>> +      FAIL_UNSUPPORTED ("unable to unshare user/fs/pid");
>> +    TEST_COMPARE (info.si_status, 0);
>> +    TEST_COMPARE (info.si_code, CLD_EXITED);
> 
> I think this could have a few tests, like the pidfd_getpid value
> matching what comes back subsequently in si_pid.

Ack.

> 
>> diff --git a/sysdeps/unix/sysv/linux/sys/pidfd.h b/sysdeps/unix/sysv/linux/sys/pidfd.h
>> index 87095212a7..8cf4df6b81 100644
>> --- a/sysdeps/unix/sysv/linux/sys/pidfd.h
>> +++ b/sysdeps/unix/sysv/linux/sys/pidfd.h
>> @@ -67,4 +67,8 @@ extern int pidfd_send_signal (int __pidfd, int __sig, siginfo_t *__info,
>>  extern pid_t pidfd_fork (int *__pidfd, int __cgroup, unsigned int __flags)
>>    __THROW;
>>  
>> +/* Query the process ID (PID) from process descriptor __FD.  Return the PID
>> +   or -1 in case of an error.  */
>> +extern pid_t pidfd_getpid (int __fd) __THROW;
>> +
> 
> __FD should be FD.

Ack.
  

Patch

diff --git a/NEWS b/NEWS
index 3d753a0f39..3a8cb00554 100644
--- a/NEWS
+++ b/NEWS
@@ -27,6 +27,10 @@  Major new features:
   of return a process ID, it returns a file descriptor that can be used
   along other pidfd functions.
 
+* On Linux, the pidfd_getpid function has been added.  It allows to retrieve
+  the process ID associated with process file descriptor created with
+  pid_spawn, pidfd_fork, or pidfd_open.
+
 Deprecated and removed features, and other changes affecting compatibility:
 
   [Add deprecations, removals and changes affecting compatibility here]
diff --git a/manual/process.texi b/manual/process.texi
index c60701aeb8..a74f316ddc 100644
--- a/manual/process.texi
+++ b/manual/process.texi
@@ -33,6 +33,7 @@  primitive functions to do each step individually instead.
 * Process Creation Concepts::   An overview of the hard way to do it.
 * Process Identification::      How to get the process ID of a process.
 * Creating a Process::          How to fork a child process.
+* Querying a Process::          How to query a child process.
 * Executing a File::            How to make a process execute another program.
 * Process Completion::          How to tell when a child process has completed.
 * Process Completion Status::   How to interpret the status value
@@ -401,6 +402,36 @@  Do not send a @code{SIGCHLD} termination signal when child terminates.
 
 This function is specific to Linux.
 
+@node Querying a Process
+@section Querying a Process
+
+The file descriptor returned by the @code{pidfd_fork} function can be used to
+query process extra information.
+
+@deftypefun pid_t pidfd_getpid (int @var{fd})
+@standards{GNU, sys/pidfd.h}
+@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
+
+The @code{pidfd_getpid} function retrieves the process ID associated with process
+file descriptor created with @code{pid_spawn}, @code{pidfd_fork}, or
+@code{pidfd_open}.
+
+If the operation fails, @code{pidfd_getpid} return @code{-1} and the following
+@code{errno} error conditionas are defined:
+
+@table @code
+@item EBADF
+The input file descriptor is invalid, does not have a pidfd associated, or an
+error has occurred parsing the kernel data.
+@item EREMOTE
+There is no process ID to denote the process in the current namespace.
+@item ESRCH
+The process for which the file descriptor refers to is terminated.
+@end table
+
+This function is specific to Linux.
+@end deftypefun
+
 @node Executing a File
 @section Executing a File
 @cindex executing a file
diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile
index 58dc23a2fb..ee1f40883c 100644
--- a/sysdeps/unix/sysv/linux/Makefile
+++ b/sysdeps/unix/sysv/linux/Makefile
@@ -213,6 +213,7 @@  tests += \
   tst-ofdlocks \
   tst-personality \
   tst-pidfd \
+  tst-pidfd_getpid \
   tst-pkey \
   tst-ppoll \
   tst-prctl \
@@ -494,8 +495,10 @@  sysdep_routines += \
   getcpu \
   oldglob \
   pidfd_fork \
+  pidfd_getpid \
   pidfd_spawn \
   pidfd_spawnp \
+  procutils \
   sched_getcpu \
   spawnattr_getcgroup_np \
   spawnattr_setcgroup_np \
diff --git a/sysdeps/unix/sysv/linux/Versions b/sysdeps/unix/sysv/linux/Versions
index f50531ce8a..1310c009e8 100644
--- a/sysdeps/unix/sysv/linux/Versions
+++ b/sysdeps/unix/sysv/linux/Versions
@@ -325,6 +325,7 @@  libc {
     posix_spawnattr_getcgroup_np;
     posix_spawnattr_setcgroup_np;
     pidfd_fork;
+    pidfd_getpid;
     pidfd_spawn;
     pidfd_spawnp;
   }
diff --git a/sysdeps/unix/sysv/linux/aarch64/libc.abilist b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
index 0d252d841b..c230d2f9bf 100644
--- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
@@ -2674,6 +2674,7 @@  GLIBC_2.38 strlcpy F
 GLIBC_2.38 wcslcat F
 GLIBC_2.38 wcslcpy F
 GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
 GLIBC_2.39 pidfd_spawn F
 GLIBC_2.39 pidfd_spawnp F
 GLIBC_2.39 posix_spawnattr_getcgroup_np F
diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist
index 347c7e2de5..735b230f3a 100644
--- a/sysdeps/unix/sysv/linux/alpha/libc.abilist
+++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist
@@ -2783,6 +2783,7 @@  GLIBC_2.38 strlcpy F
 GLIBC_2.38 wcslcat F
 GLIBC_2.38 wcslcpy F
 GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
 GLIBC_2.39 pidfd_spawn F
 GLIBC_2.39 pidfd_spawnp F
 GLIBC_2.39 posix_spawnattr_getcgroup_np F
diff --git a/sysdeps/unix/sysv/linux/arc/libc.abilist b/sysdeps/unix/sysv/linux/arc/libc.abilist
index 78da9c8434..c959044259 100644
--- a/sysdeps/unix/sysv/linux/arc/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arc/libc.abilist
@@ -2435,6 +2435,7 @@  GLIBC_2.38 strlcpy F
 GLIBC_2.38 wcslcat F
 GLIBC_2.38 wcslcpy F
 GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
 GLIBC_2.39 pidfd_spawn F
 GLIBC_2.39 pidfd_spawnp F
 GLIBC_2.39 posix_spawnattr_getcgroup_np F
diff --git a/sysdeps/unix/sysv/linux/arm/be/libc.abilist b/sysdeps/unix/sysv/linux/arm/be/libc.abilist
index c99cc53158..c88bc44bac 100644
--- a/sysdeps/unix/sysv/linux/arm/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/be/libc.abilist
@@ -555,6 +555,7 @@  GLIBC_2.38 strlcpy F
 GLIBC_2.38 wcslcat F
 GLIBC_2.38 wcslcpy F
 GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
 GLIBC_2.39 pidfd_spawn F
 GLIBC_2.39 pidfd_spawnp F
 GLIBC_2.39 posix_spawnattr_getcgroup_np F
diff --git a/sysdeps/unix/sysv/linux/arm/le/libc.abilist b/sysdeps/unix/sysv/linux/arm/le/libc.abilist
index 1d3412a4ec..e67da18c51 100644
--- a/sysdeps/unix/sysv/linux/arm/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/le/libc.abilist
@@ -552,6 +552,7 @@  GLIBC_2.38 strlcpy F
 GLIBC_2.38 wcslcat F
 GLIBC_2.38 wcslcpy F
 GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
 GLIBC_2.39 pidfd_spawn F
 GLIBC_2.39 pidfd_spawnp F
 GLIBC_2.39 posix_spawnattr_getcgroup_np F
diff --git a/sysdeps/unix/sysv/linux/csky/libc.abilist b/sysdeps/unix/sysv/linux/csky/libc.abilist
index 993eb6d8b7..24a7326b48 100644
--- a/sysdeps/unix/sysv/linux/csky/libc.abilist
+++ b/sysdeps/unix/sysv/linux/csky/libc.abilist
@@ -2711,6 +2711,7 @@  GLIBC_2.38 strlcpy F
 GLIBC_2.38 wcslcat F
 GLIBC_2.38 wcslcpy F
 GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
 GLIBC_2.39 pidfd_spawn F
 GLIBC_2.39 pidfd_spawnp F
 GLIBC_2.39 posix_spawnattr_getcgroup_np F
diff --git a/sysdeps/unix/sysv/linux/hppa/libc.abilist b/sysdeps/unix/sysv/linux/hppa/libc.abilist
index a5825a7e27..1ba625305d 100644
--- a/sysdeps/unix/sysv/linux/hppa/libc.abilist
+++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist
@@ -2660,6 +2660,7 @@  GLIBC_2.38 strlcpy F
 GLIBC_2.38 wcslcat F
 GLIBC_2.38 wcslcpy F
 GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
 GLIBC_2.39 pidfd_spawn F
 GLIBC_2.39 pidfd_spawnp F
 GLIBC_2.39 posix_spawnattr_getcgroup_np F
diff --git a/sysdeps/unix/sysv/linux/i386/libc.abilist b/sysdeps/unix/sysv/linux/i386/libc.abilist
index 696ef98aea..324bf961d4 100644
--- a/sysdeps/unix/sysv/linux/i386/libc.abilist
+++ b/sysdeps/unix/sysv/linux/i386/libc.abilist
@@ -2844,6 +2844,7 @@  GLIBC_2.38 strlcpy F
 GLIBC_2.38 wcslcat F
 GLIBC_2.38 wcslcpy F
 GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
 GLIBC_2.39 pidfd_spawn F
 GLIBC_2.39 pidfd_spawnp F
 GLIBC_2.39 posix_spawnattr_getcgroup_np F
diff --git a/sysdeps/unix/sysv/linux/ia64/libc.abilist b/sysdeps/unix/sysv/linux/ia64/libc.abilist
index 0cd4b92159..564476beac 100644
--- a/sysdeps/unix/sysv/linux/ia64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/ia64/libc.abilist
@@ -2609,6 +2609,7 @@  GLIBC_2.38 strlcpy F
 GLIBC_2.38 wcslcat F
 GLIBC_2.38 wcslcpy F
 GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
 GLIBC_2.39 pidfd_spawn F
 GLIBC_2.39 pidfd_spawnp F
 GLIBC_2.39 posix_spawnattr_getcgroup_np F
diff --git a/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist b/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist
index cfb358dc15..17364cb102 100644
--- a/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist
@@ -2195,6 +2195,7 @@  GLIBC_2.38 strlcpy F
 GLIBC_2.38 wcslcat F
 GLIBC_2.38 wcslcpy F
 GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
 GLIBC_2.39 pidfd_spawn F
 GLIBC_2.39 pidfd_spawnp F
 GLIBC_2.39 posix_spawnattr_getcgroup_np F
diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
index c7de4154f4..da63ffbd55 100644
--- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
@@ -556,6 +556,7 @@  GLIBC_2.38 strlcpy F
 GLIBC_2.38 wcslcat F
 GLIBC_2.38 wcslcpy F
 GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
 GLIBC_2.39 pidfd_spawn F
 GLIBC_2.39 pidfd_spawnp F
 GLIBC_2.39 posix_spawnattr_getcgroup_np F
diff --git a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
index e99ce0daa7..a6715fb165 100644
--- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
@@ -2787,6 +2787,7 @@  GLIBC_2.38 strlcpy F
 GLIBC_2.38 wcslcat F
 GLIBC_2.38 wcslcpy F
 GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
 GLIBC_2.39 pidfd_spawn F
 GLIBC_2.39 pidfd_spawnp F
 GLIBC_2.39 posix_spawnattr_getcgroup_np F
diff --git a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
index ffb3b1fa20..9ef3686f7b 100644
--- a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
@@ -2760,6 +2760,7 @@  GLIBC_2.38 strlcpy F
 GLIBC_2.38 wcslcat F
 GLIBC_2.38 wcslcpy F
 GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
 GLIBC_2.39 pidfd_spawn F
 GLIBC_2.39 pidfd_spawnp F
 GLIBC_2.39 posix_spawnattr_getcgroup_np F
diff --git a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
index 120b0707ec..6442415958 100644
--- a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
@@ -2757,6 +2757,7 @@  GLIBC_2.38 strlcpy F
 GLIBC_2.38 wcslcat F
 GLIBC_2.38 wcslcpy F
 GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
 GLIBC_2.39 pidfd_spawn F
 GLIBC_2.39 pidfd_spawnp F
 GLIBC_2.39 posix_spawnattr_getcgroup_np F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
index d0f2dce89c..8f184efc2f 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
@@ -2752,6 +2752,7 @@  GLIBC_2.38 strlcpy F
 GLIBC_2.38 wcslcat F
 GLIBC_2.38 wcslcpy F
 GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
 GLIBC_2.39 pidfd_spawn F
 GLIBC_2.39 pidfd_spawnp F
 GLIBC_2.39 posix_spawnattr_getcgroup_np F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
index 3b33299274..89667debc6 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
@@ -2750,6 +2750,7 @@  GLIBC_2.38 strlcpy F
 GLIBC_2.38 wcslcat F
 GLIBC_2.38 wcslcpy F
 GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
 GLIBC_2.39 pidfd_spawn F
 GLIBC_2.39 pidfd_spawnp F
 GLIBC_2.39 posix_spawnattr_getcgroup_np F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
index 0253aeb4e4..ec7b79a4b1 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
@@ -2758,6 +2758,7 @@  GLIBC_2.38 strlcpy F
 GLIBC_2.38 wcslcat F
 GLIBC_2.38 wcslcpy F
 GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
 GLIBC_2.39 pidfd_spawn F
 GLIBC_2.39 pidfd_spawnp F
 GLIBC_2.39 posix_spawnattr_getcgroup_np F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
index 1613b18958..afc87d46ac 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
@@ -2660,6 +2660,7 @@  GLIBC_2.38 strlcpy F
 GLIBC_2.38 wcslcat F
 GLIBC_2.38 wcslcpy F
 GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
 GLIBC_2.39 pidfd_spawn F
 GLIBC_2.39 pidfd_spawnp F
 GLIBC_2.39 posix_spawnattr_getcgroup_np F
diff --git a/sysdeps/unix/sysv/linux/nios2/libc.abilist b/sysdeps/unix/sysv/linux/nios2/libc.abilist
index b2e2723a97..f836f84740 100644
--- a/sysdeps/unix/sysv/linux/nios2/libc.abilist
+++ b/sysdeps/unix/sysv/linux/nios2/libc.abilist
@@ -2799,6 +2799,7 @@  GLIBC_2.38 strlcpy F
 GLIBC_2.38 wcslcat F
 GLIBC_2.38 wcslcpy F
 GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
 GLIBC_2.39 pidfd_spawn F
 GLIBC_2.39 pidfd_spawnp F
 GLIBC_2.39 posix_spawnattr_getcgroup_np F
diff --git a/sysdeps/unix/sysv/linux/or1k/libc.abilist b/sysdeps/unix/sysv/linux/or1k/libc.abilist
index 800aec0661..d089993ca3 100644
--- a/sysdeps/unix/sysv/linux/or1k/libc.abilist
+++ b/sysdeps/unix/sysv/linux/or1k/libc.abilist
@@ -2181,6 +2181,7 @@  GLIBC_2.38 strlcpy F
 GLIBC_2.38 wcslcat F
 GLIBC_2.38 wcslcpy F
 GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
 GLIBC_2.39 pidfd_spawn F
 GLIBC_2.39 pidfd_spawnp F
 GLIBC_2.39 posix_spawnattr_getcgroup_np F
diff --git a/sysdeps/unix/sysv/linux/pidfd_getpid.c b/sysdeps/unix/sysv/linux/pidfd_getpid.c
new file mode 100644
index 0000000000..46848a5983
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/pidfd_getpid.c
@@ -0,0 +1,122 @@ 
+/* pidfd_getpid - Get the associated pid from the pid file descriptor.
+   Copyright (C) 2023 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <_itoa.h>
+#include <errno.h>
+#include <intprops.h>
+#include <procutils.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysdep.h>
+#include <unistd.h>
+
+#define FDINFO_TO_FILENAME_PREFIX "/proc/self/fdinfo/"
+
+#define FDINFO_FILENAME_LEN \
+  (sizeof (FDINFO_TO_FILENAME_PREFIX) + INT_STRLEN_BOUND (int))
+
+struct parse_fdinfo_t
+{
+  bool found;
+  pid_t pid;
+};
+
+/* Parse the PID field in the fdinfo entry, if existent.  Avoid strtol or
+   similar to not be locale dependent.  */
+static int
+parse_fdinfo (const char *l, void *arg)
+{
+  enum { fieldlen = sizeof ("Pid:") - 1 };
+  if (strncmp (l, "Pid:", fieldlen) != 0)
+    return 0;
+
+  l += fieldlen;
+
+  /* Skip leading spaces.  */
+  while (*l == ' ' || (unsigned int)(*l) -'\t' < 5)
+    l++;
+
+  bool neg = false;
+  switch (*l)
+    {
+    case '-': neg = true;
+    case '+': l++;
+    }
+
+  if (*l == '\0')
+    return 0;
+
+  int n = 0;
+  while (*l != '\0')
+    {
+      /* Check if '*l' is a digit.  */
+      if ((unsigned int)(*l) - '0' >= 10)
+        return 0;
+
+      /* Ignore invalid large values.  */
+      if (INT_MULTIPLY_WRAPV (10, n, &n)
+          || INT_ADD_WRAPV (n, *l++ - '0', &n))
+        return 0;
+    }
+
+  /* -1 indicates that the process is terminated.  */
+  if (neg && n != 1)
+    return 0;
+
+  struct parse_fdinfo_t *fdinfo = arg;
+  fdinfo->pid = neg ? -n : n;
+  fdinfo->found = true;
+
+  return 1;
+}
+
+pid_t
+pidfd_getpid (int fd)
+{
+  if (__glibc_unlikely (fd < 0))
+    {
+      __set_errno (EBADF);
+      return -1;
+    }
+
+  char fdinfoname[FDINFO_FILENAME_LEN];
+
+  char *p = mempcpy (fdinfoname, FDINFO_TO_FILENAME_PREFIX,
+		     strlen (FDINFO_TO_FILENAME_PREFIX));
+  *_fitoa_word (fd, p, 10, 0) = '\0';
+
+  struct parse_fdinfo_t fdinfo = { .found = false, .pid = -1 };
+  if (procutils_read_file (fdinfoname, parse_fdinfo, &fdinfo) == -1)
+    /* The fdinfo contains an invalid 'Pid:' value.  */
+    return INLINE_SYSCALL_ERROR_RETURN_VALUE (EBADF);
+
+  /* The FD does not have a 'Pid:' entry associated.  */
+  if (!fdinfo.found)
+    return INLINE_SYSCALL_ERROR_RETURN_VALUE (EBADF);
+
+  /* The pidfd cannot be resolved because it is in a separate pid
+     namespace.  */
+  if (fdinfo.pid == 0)
+    return INLINE_SYSCALL_ERROR_RETURN_VALUE (EREMOTE);
+
+  /* A negative value means the process is terminated.  */
+  if (fdinfo.pid < 0)
+    return INLINE_SYSCALL_ERROR_RETURN_VALUE (ESRCH);
+
+  return fdinfo.pid;
+}
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
index 6ce440b9a0..73f551946c 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
@@ -2826,6 +2826,7 @@  GLIBC_2.38 strlcpy F
 GLIBC_2.38 wcslcat F
 GLIBC_2.38 wcslcpy F
 GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
 GLIBC_2.39 pidfd_spawn F
 GLIBC_2.39 pidfd_spawnp F
 GLIBC_2.39 posix_spawnattr_getcgroup_np F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
index d4cdb3f50b..06777226b9 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
@@ -2859,6 +2859,7 @@  GLIBC_2.38 strlcpy F
 GLIBC_2.38 wcslcat F
 GLIBC_2.38 wcslcpy F
 GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
 GLIBC_2.39 pidfd_spawn F
 GLIBC_2.39 pidfd_spawnp F
 GLIBC_2.39 posix_spawnattr_getcgroup_np F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
index e2bba8152b..5b49433840 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
@@ -2580,6 +2580,7 @@  GLIBC_2.38 strlcpy F
 GLIBC_2.38 wcslcat F
 GLIBC_2.38 wcslcpy F
 GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
 GLIBC_2.39 pidfd_spawn F
 GLIBC_2.39 pidfd_spawnp F
 GLIBC_2.39 posix_spawnattr_getcgroup_np F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
index 8976c6b37b..e82d5ef81d 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
@@ -2894,6 +2894,7 @@  GLIBC_2.38 strlcpy F
 GLIBC_2.38 wcslcat F
 GLIBC_2.38 wcslcpy F
 GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
 GLIBC_2.39 pidfd_spawn F
 GLIBC_2.39 pidfd_spawnp F
 GLIBC_2.39 posix_spawnattr_getcgroup_np F
diff --git a/sysdeps/unix/sysv/linux/procutils.c b/sysdeps/unix/sysv/linux/procutils.c
new file mode 100644
index 0000000000..83b327cb9a
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/procutils.c
@@ -0,0 +1,104 @@ 
+/* Utilities functions to read/parse Linux procfs and sysfs.
+   Copyright (C) 2023 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <assert.h>
+#include <not-cancel.h>
+#include <procutils.h>
+#include <string.h>
+
+static int
+next_line (char **r, int fd, char *const buffer, char **cp, char **re,
+           char *const buffer_end)
+{
+  char *res = *cp;
+  char *nl = memchr (*cp, '\n', *re - *cp);
+  if (nl == NULL)
+    {
+      if (*cp != buffer)
+        {
+          if (*re == buffer_end)
+            {
+              memmove (buffer, *cp, *re - *cp);
+              *re = buffer + (*re - *cp);
+              *cp = buffer;
+
+              ssize_t n = __read_nocancel (fd, *re, buffer_end - *re);
+              if (n < 0)
+                return -1;
+
+              *re += n;
+
+              nl = memchr (*cp, '\n', *re - *cp);
+              while (nl == NULL && *re == buffer_end)
+                {
+                  /* Truncate too long lines.  */
+                  *re = buffer + 3 * (buffer_end - buffer) / 4;
+                  n = __read_nocancel (fd, *re, buffer_end - *re);
+                  if (n < 0)
+                    return -1;
+
+                  nl = memchr (*re, '\n', n);
+                  **re = '\0';
+                  *re += n;
+                }
+            }
+          else
+            nl = memchr (*cp, '\n', *re - *cp);
+
+          res = *cp;
+        }
+
+      if (nl == NULL)
+        nl = *re - 1;
+    }
+
+  *nl = '\0';
+  *cp = nl + 1;
+  assert (*cp <= *re);
+
+  if (res == *re)
+    return 0;
+
+  *r = res;
+  return 1;
+}
+
+int
+procutils_read_file (const char *filename, procutils_closure_t closure,
+		     void *arg)
+{
+  enum { buffer_size = 1024 };
+  char buffer[buffer_size];
+  char *buffer_end = buffer + buffer_size;
+  char *cp = buffer_end;
+  char *re = buffer_end;
+
+  int fd = __open64_nocancel (filename, O_RDONLY | O_CLOEXEC);
+  if (fd == -1)
+    return -1;
+
+  char *l;
+  int r;
+  while ((r = next_line (&l, fd, buffer, &cp, &re, buffer_end)) > 0)
+    if (closure (l, arg) != 0)
+      break;
+
+  __close_nocancel_nostatus (fd);
+
+  return r;
+}
diff --git a/sysdeps/unix/sysv/linux/procutils.h b/sysdeps/unix/sysv/linux/procutils.h
new file mode 100644
index 0000000000..64e1080920
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/procutils.h
@@ -0,0 +1,35 @@ 
+/* Utilities functions to read/parse Linux procfs and sysfs.
+   Copyright (C) 2023 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#ifndef _PROCUTILS_H
+#define _PROCUTILS_H
+
+typedef int (*procutils_closure_t)(const char *line, void *arg);
+
+/* Open and read the path FILENAME, line per line, and call CLOSURE with
+   argument ARG on each line.  The read is done with a static buffer,
+   with non-cancellable calls, and the line is null terminated.
+
+   The CLOSURE should return true if the read should continue, or false
+   if the function should stop.
+
+   It returns 0 in case of success, or -1 otherwise.  */
+int procutils_read_file (const char *filename, procutils_closure_t closure,
+			 void *arg) attribute_hidden;
+
+#endif
diff --git a/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
index 9dc6a3df1e..ceb537ed1f 100644
--- a/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
@@ -2437,6 +2437,7 @@  GLIBC_2.38 strlcpy F
 GLIBC_2.38 wcslcat F
 GLIBC_2.38 wcslcpy F
 GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
 GLIBC_2.39 pidfd_spawn F
 GLIBC_2.39 pidfd_spawnp F
 GLIBC_2.39 posix_spawnattr_getcgroup_np F
diff --git a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
index 4bafa24dba..93b4237957 100644
--- a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
@@ -2637,6 +2637,7 @@  GLIBC_2.38 strlcpy F
 GLIBC_2.38 wcslcat F
 GLIBC_2.38 wcslcpy F
 GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
 GLIBC_2.39 pidfd_spawn F
 GLIBC_2.39 pidfd_spawnp F
 GLIBC_2.39 posix_spawnattr_getcgroup_np F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
index abb21da1ce..36d6d8b389 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
@@ -2824,6 +2824,7 @@  GLIBC_2.38 strlcpy F
 GLIBC_2.38 wcslcat F
 GLIBC_2.38 wcslcpy F
 GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
 GLIBC_2.39 pidfd_spawn F
 GLIBC_2.39 pidfd_spawnp F
 GLIBC_2.39 posix_spawnattr_getcgroup_np F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
index 246a180900..4e49bdb79d 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
@@ -2617,6 +2617,7 @@  GLIBC_2.38 strlcpy F
 GLIBC_2.38 wcslcat F
 GLIBC_2.38 wcslcpy F
 GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
 GLIBC_2.39 pidfd_spawn F
 GLIBC_2.39 pidfd_spawnp F
 GLIBC_2.39 posix_spawnattr_getcgroup_np F
diff --git a/sysdeps/unix/sysv/linux/sh/be/libc.abilist b/sysdeps/unix/sysv/linux/sh/be/libc.abilist
index 493b9a53f9..d6edb37baf 100644
--- a/sysdeps/unix/sysv/linux/sh/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sh/be/libc.abilist
@@ -2667,6 +2667,7 @@  GLIBC_2.38 strlcpy F
 GLIBC_2.38 wcslcat F
 GLIBC_2.38 wcslcpy F
 GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
 GLIBC_2.39 pidfd_spawn F
 GLIBC_2.39 pidfd_spawnp F
 GLIBC_2.39 posix_spawnattr_getcgroup_np F
diff --git a/sysdeps/unix/sysv/linux/sh/le/libc.abilist b/sysdeps/unix/sysv/linux/sh/le/libc.abilist
index 0a23cc2a0d..c63992b93e 100644
--- a/sysdeps/unix/sysv/linux/sh/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sh/le/libc.abilist
@@ -2664,6 +2664,7 @@  GLIBC_2.38 strlcpy F
 GLIBC_2.38 wcslcat F
 GLIBC_2.38 wcslcpy F
 GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
 GLIBC_2.39 pidfd_spawn F
 GLIBC_2.39 pidfd_spawnp F
 GLIBC_2.39 posix_spawnattr_getcgroup_np F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
index 40c8747a75..489002b9a5 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
@@ -2819,6 +2819,7 @@  GLIBC_2.38 strlcpy F
 GLIBC_2.38 wcslcat F
 GLIBC_2.38 wcslcpy F
 GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
 GLIBC_2.39 pidfd_spawn F
 GLIBC_2.39 pidfd_spawnp F
 GLIBC_2.39 posix_spawnattr_getcgroup_np F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
index 873949ce6c..65a8d30c5a 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
@@ -2632,6 +2632,7 @@  GLIBC_2.38 strlcpy F
 GLIBC_2.38 wcslcat F
 GLIBC_2.38 wcslcpy F
 GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
 GLIBC_2.39 pidfd_spawn F
 GLIBC_2.39 pidfd_spawnp F
 GLIBC_2.39 posix_spawnattr_getcgroup_np F
diff --git a/sysdeps/unix/sysv/linux/sys/pidfd.h b/sysdeps/unix/sysv/linux/sys/pidfd.h
index 87095212a7..8cf4df6b81 100644
--- a/sysdeps/unix/sysv/linux/sys/pidfd.h
+++ b/sysdeps/unix/sysv/linux/sys/pidfd.h
@@ -67,4 +67,8 @@  extern int pidfd_send_signal (int __pidfd, int __sig, siginfo_t *__info,
 extern pid_t pidfd_fork (int *__pidfd, int __cgroup, unsigned int __flags)
   __THROW;
 
+/* Query the process ID (PID) from process descriptor __FD.  Return the PID
+   or -1 in case of an error.  */
+extern pid_t pidfd_getpid (int __fd) __THROW;
+
 #endif /* _PIDFD_H  */
diff --git a/sysdeps/unix/sysv/linux/tst-pidfd.c b/sysdeps/unix/sysv/linux/tst-pidfd.c
index 64d8a2ef40..53d223f702 100644
--- a/sysdeps/unix/sysv/linux/tst-pidfd.c
+++ b/sysdeps/unix/sysv/linux/tst-pidfd.c
@@ -18,6 +18,7 @@ 
 
 #include <errno.h>
 #include <fcntl.h>
+#include <limits.h>
 #include <support/capture_subprocess.h>
 #include <support/check.h>
 #include <support/process_state.h>
@@ -27,6 +28,9 @@ 
 #include <support/xsocket.h>
 #include <sys/pidfd.h>
 #include <sys/wait.h>
+#include <stdlib.h>
+
+#include <string.h>
 
 #define REMOTE_PATH "/dev/null"
 
@@ -102,6 +106,43 @@  do_test (void)
   ppid = getpid ();
   puid = getuid ();
 
+  /* Sanity check for invalid inputs.  */
+  TEST_COMPARE (pidfd_getpid (-1), -1);
+  TEST_COMPARE (errno, EBADF);
+
+  {
+    pid_t pid = pidfd_getpid (STDOUT_FILENO);
+    TEST_COMPARE (pid, -1);
+    TEST_COMPARE (errno, EBADF);
+  }
+
+  /* Check if pidfd_getpid returns ESRCH for exited subprocess.  */
+  {
+    int pidfd;
+    pid_t pidfork = pidfd_fork (&pidfd, -1, 0);
+    if (pidfork == 0)
+      _exit (EXIT_SUCCESS);
+
+    /* The process might be still running or already in zombie state, in any
+       case the PID is still allocated to the process.  */
+    pid_t pid = pidfd_getpid (pidfd);
+    if (pid > 0)
+      support_process_state_wait (pid, support_process_state_zombie);
+
+    siginfo_t info;
+    TEST_COMPARE (waitid (P_PIDFD, pidfd, &info, WEXITED), 0);
+    TEST_COMPARE (info.si_pid, pidfork);
+    TEST_COMPARE (info.si_status, 0);
+    TEST_COMPARE (info.si_code, CLD_EXITED);
+
+    /* Once the process is reaped the associated PID is not available.  */
+    pid = pidfd_getpid (pidfd);
+    TEST_COMPARE (pid, -1);
+    TEST_COMPARE (errno, ESRCH);
+
+    xclose (pidfd);
+  }
+
   TEST_COMPARE (socketpair (AF_UNIX, SOCK_STREAM, 0, sockets), 0);
 
   pid_t pid = xfork ();
@@ -118,6 +159,12 @@  do_test (void)
   int pidfd = pidfd_open (pid, 0);
   TEST_VERIFY (pidfd != -1);
 
+  TEST_COMPARE (pidfd_getpid (INT_MAX), -1);
+  {
+    pid_t querypid = pidfd_getpid (pidfd);
+    TEST_COMPARE (querypid, pid);
+  }
+
   /* Wait for first sigtimedwait.  */
   support_process_state_wait (pid, support_process_state_sleeping);
   TEST_COMPARE (pidfd_send_signal (pidfd, SIGUSR1, NULL, 0), 0);
diff --git a/sysdeps/unix/sysv/linux/tst-pidfd_getpid.c b/sysdeps/unix/sysv/linux/tst-pidfd_getpid.c
new file mode 100644
index 0000000000..41d03a04ad
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/tst-pidfd_getpid.c
@@ -0,0 +1,187 @@ 
+/* Specific tests for Linux pidfd_getpid.
+   Copyright (C) 2023 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <errno.h>
+#include <sched.h>
+#include <stdlib.h>
+#include <support/check.h>
+#include <support/xsocket.h>
+#include <support/xunistd.h>
+#include <support/test-driver.h>
+#include <sys/pidfd.h>
+#include <sys/wait.h>
+#include <sys/mount.h>
+#include <string.h>
+
+#include <stdio.h>
+
+static int sockfd[2];
+
+static void
+send_fd (const int sock, const int fd)
+{
+  union
+    {
+      struct cmsghdr hdr;
+      char buf[CMSG_SPACE (sizeof (int))];
+    } cmsgbuf = {0};
+  struct cmsghdr *cmsg;
+  char ch = 'A';
+  struct iovec vec =
+    {
+      .iov_base = &ch,
+      .iov_len = sizeof ch
+    };
+  struct msghdr msg =
+    {
+      .msg_control = &cmsgbuf.buf,
+      .msg_controllen = sizeof (cmsgbuf.buf),
+      .msg_iov = &vec,
+      .msg_iovlen = 1,
+    };
+
+  cmsg = CMSG_FIRSTHDR (&msg);
+  cmsg->cmsg_len = CMSG_LEN (sizeof (int));
+  cmsg->cmsg_level = SOL_SOCKET;
+  cmsg->cmsg_type = SCM_RIGHTS;
+  memcpy (CMSG_DATA (cmsg), &fd, sizeof (fd));
+
+  ssize_t n;
+  while ((n = sendmsg (sock, &msg, 0)) == -1 && errno == EINTR);
+
+  TEST_VERIFY_EXIT (n == 1);
+}
+
+static int
+recv_fd (const int sock)
+{
+  union
+    {
+      struct cmsghdr hdr;
+      char buf[CMSG_SPACE(sizeof(int))];
+    } cmsgbuf = {0};
+  struct cmsghdr *cmsg;
+  char ch = '\0';
+  struct iovec vec =
+    {
+      .iov_base = &ch,
+      .iov_len = sizeof ch
+    };
+  struct msghdr msg =
+    {
+      .msg_control = &cmsgbuf.buf,
+      .msg_controllen = sizeof (cmsgbuf.buf),
+      .msg_iov = &vec,
+      .msg_iovlen = 1,
+    };
+
+  ssize_t n;
+  while ((n = recvmsg (sock, &msg, 0)) == -1 && errno == EINTR);
+  if (n != 1 || ch != 'A')
+    return -1;
+
+  cmsg = CMSG_FIRSTHDR (&msg);
+  if (cmsg == NULL)
+    return -1;
+  if (cmsg->cmsg_type != SCM_RIGHTS)
+    return -1;
+
+  int fd = -1;
+  memcpy (&fd, CMSG_DATA (cmsg), sizeof (fd));
+  if (fd < 0)
+    return -1;
+  return fd;
+}
+
+static int
+do_test (void)
+{
+  {
+    /* The pidfd_getfd syscall was the last in the set of pidfd related
+       syscalls added to the kernel.  Use pidfd_getfd to decide if this
+       kernel has pidfd support that we can test.  */
+    int r = pidfd_getfd (0, 0, 1);
+    TEST_VERIFY_EXIT (r == -1);
+    if (errno == ENOSYS)
+      FAIL_UNSUPPORTED ("kernel does not support pidfd_getfd, skipping test");
+  }
+
+  TEST_VERIFY_EXIT (socketpair (AF_UNIX, SOCK_STREAM, 0, sockfd) == 0);
+
+  /* Check if pidfd_getpid returns EREMOTE for process not in current
+     namespace.  */
+  {
+    int pidfd;
+    pid_t pid = pidfd_fork (&pidfd, -1, 0);
+    TEST_VERIFY_EXIT (pid >= 0);
+    if (pid == 0)
+      {
+        if (unshare (CLONE_NEWNS | CLONE_NEWUSER | CLONE_NEWPID) < 0)
+	  {
+	    /* Older kernels may not support all the options, or security
+	       policy may block this call.  */
+	    if (errno == EINVAL || errno == EPERM || errno == ENOSPC)
+	      exit (EXIT_UNSUPPORTED);
+	    FAIL_EXIT1 ("unshare user/fs/pid failed: %m");
+	  }
+
+	TEST_VERIFY_EXIT (mount (NULL, "/", NULL, MS_REC | MS_PRIVATE, 0)
+			  == 0);
+
+	pid_t child = xfork ();
+	if (child > 0)
+	  {
+	    int status;
+	    xwaitpid (child, &status, 0);
+	    TEST_VERIFY (WIFEXITED (status));
+	    exit (WEXITSTATUS (status));
+	  }
+
+	/* Now that we're pid 1 (effectively "root") we can mount /proc  */
+	if (mount ("proc", "/proc", "proc", 0, NULL) != 0)
+	  /* This happens if we're trying to create a nested container,
+	     like if the build is running under podman, and we lack
+	     priviledges.  */
+	  {
+	    if (errno == EPERM)
+	      _exit (EXIT_UNSUPPORTED);
+	    else
+	      _exit (EXIT_FAILURE);
+	  }
+
+	int ppidfd = recv_fd (sockfd[0]);
+	TEST_COMPARE (pidfd_getpid (ppidfd), -1);
+	TEST_COMPARE (errno, EREMOTE);
+
+	_exit (EXIT_SUCCESS);
+      }
+
+    send_fd (sockfd[1], pidfd);
+
+    siginfo_t info;
+    TEST_COMPARE (waitid (P_PIDFD, pidfd, &info, WEXITED), 0);
+    if (info.si_status == EXIT_UNSUPPORTED)
+      FAIL_UNSUPPORTED ("unable to unshare user/fs/pid");
+    TEST_COMPARE (info.si_status, 0);
+    TEST_COMPARE (info.si_code, CLD_EXITED);
+  }
+
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
index 3aac9a0fb9..072b92e51d 100644
--- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
@@ -2583,6 +2583,7 @@  GLIBC_2.38 strlcpy F
 GLIBC_2.38 wcslcat F
 GLIBC_2.38 wcslcpy F
 GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
 GLIBC_2.39 pidfd_spawn F
 GLIBC_2.39 pidfd_spawnp F
 GLIBC_2.39 posix_spawnattr_getcgroup_np F
diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
index 9a53054b37..0bbb88176e 100644
--- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
@@ -2689,6 +2689,7 @@  GLIBC_2.38 strlcpy F
 GLIBC_2.38 wcslcat F
 GLIBC_2.38 wcslcpy F
 GLIBC_2.39 pidfd_fork F
+GLIBC_2.39 pidfd_getpid F
 GLIBC_2.39 pidfd_spawn F
 GLIBC_2.39 pidfd_spawnp F
 GLIBC_2.39 posix_spawnattr_getcgroup_np F