mirror of
				https://gitlab.gnome.org/GNOME/glib.git
				synced 2025-11-04 10:08:56 +01:00 
			
		
		
		
	glib-unix: Add g_closefrom(), g_fdwalk_set_cloexec()
These are the same as Linux `close_range (lowfd, ~0U, 0)` and `close_range (lowfd, ~0U, CLOSE_RANGE_CLOEXEC)`, but portable. Unlike some implementations of BSD closefrom(3), they are async-signal-safe. The implementations were moved from the GSpawn code, which already needs all of this functionality anyway, with the exception of set_cloexec() which was copied (leading to some minor duplication, but it's very simple). Resolves: https://gitlab.gnome.org/GNOME/glib/-/issues/3247 Signed-off-by: Simon McVittie <smcv@collabora.com>
This commit is contained in:
		
							
								
								
									
										421
									
								
								glib/glib-unix.c
									
									
									
									
									
								
							
							
						
						
									
										421
									
								
								glib/glib-unix.c
									
									
									
									
									
								
							@@ -1,6 +1,20 @@
 | 
			
		||||
/* GLIB - Library of useful routines for C programming
 | 
			
		||||
 * Copyright (C) 2011 Red Hat, Inc.
 | 
			
		||||
 * Copyright 2023 Collabora Ltd.
 | 
			
		||||
 * Copyright 2000-2022 Red Hat, Inc.
 | 
			
		||||
 * Copyright 2006-2007 Matthias Clasen
 | 
			
		||||
 * Copyright 2006 Padraig O'Briain
 | 
			
		||||
 * Copyright 2007 Lennart Poettering
 | 
			
		||||
 * Copyright 2018-2022 Endless OS Foundation, LLC
 | 
			
		||||
 * Copyright 2018 Peter Wu
 | 
			
		||||
 * Copyright 2019 Ting-Wei Lan
 | 
			
		||||
 * Copyright 2019 Sebastian Schwarz
 | 
			
		||||
 * Copyright 2020 Matt Rose
 | 
			
		||||
 * Copyright 2021 Casper Dik
 | 
			
		||||
 * Copyright 2022 Alexander Richardson
 | 
			
		||||
 * Copyright 2022 Ray Strode
 | 
			
		||||
 * Copyright 2022 Thomas Haller
 | 
			
		||||
 * Copyright 2023-2024 Collabora Ltd.
 | 
			
		||||
 * Copyright 2023 Sebastian Wilhelmi
 | 
			
		||||
 * Copyright 2023 CaiJingLong
 | 
			
		||||
 *
 | 
			
		||||
 * glib-unix.c: UNIX specific API wrappers and convenience functions
 | 
			
		||||
 *
 | 
			
		||||
@@ -28,9 +42,27 @@
 | 
			
		||||
#include "glib-unixprivate.h"
 | 
			
		||||
#include "gmain-internal.h"
 | 
			
		||||
 | 
			
		||||
#include <dirent.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
#include <stdlib.h>   /* for fdwalk */
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <pwd.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
#if defined(__linux__) || defined(__DragonFly__)
 | 
			
		||||
#include <sys/syscall.h>  /* for syscall and SYS_getdents64 */
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef HAVE_SYS_RESOURCE_H
 | 
			
		||||
#include <sys/resource.h>
 | 
			
		||||
#endif /* HAVE_SYS_RESOURCE_H */
 | 
			
		||||
 | 
			
		||||
#if defined(__APPLE__) && defined(HAVE_LIBPROC_H)
 | 
			
		||||
#include <libproc.h>
 | 
			
		||||
#include <sys/proc_info.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
G_STATIC_ASSERT (sizeof (ssize_t) == GLIB_SIZEOF_SSIZE_T);
 | 
			
		||||
G_STATIC_ASSERT (G_ALIGNOF (gssize) == G_ALIGNOF (ssize_t));
 | 
			
		||||
@@ -507,3 +539,388 @@ g_unix_get_passwd_entry (const gchar  *user_name,
 | 
			
		||||
 | 
			
		||||
  return (struct passwd *) g_steal_pointer (&buffer);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* This function is called between fork() and exec() and hence must be
 | 
			
		||||
 * async-signal-safe (see signal-safety(7)). */
 | 
			
		||||
static int
 | 
			
		||||
set_cloexec (void *data, gint fd)
 | 
			
		||||
{
 | 
			
		||||
  if (fd >= GPOINTER_TO_INT (data))
 | 
			
		||||
    fcntl (fd, F_SETFD, FD_CLOEXEC);
 | 
			
		||||
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* fdwalk()-compatible callback to close a fd for non-compliant
 | 
			
		||||
 * implementations of fdwalk() that potentially pass already
 | 
			
		||||
 * closed fds.
 | 
			
		||||
 *
 | 
			
		||||
 * It is not an error to pass an invalid fd to this function.
 | 
			
		||||
 *
 | 
			
		||||
 * This function is called between fork() and exec() and hence must be
 | 
			
		||||
 * async-signal-safe (see signal-safety(7)).
 | 
			
		||||
 */
 | 
			
		||||
G_GNUC_UNUSED static int
 | 
			
		||||
close_func_with_invalid_fds (void *data, int fd)
 | 
			
		||||
{
 | 
			
		||||
  /* We use close and not g_close here because on some platforms, we
 | 
			
		||||
   * don't know how to close only valid, open file descriptors, so we
 | 
			
		||||
   * have to pass bad fds to close too. g_close warns if given a bad
 | 
			
		||||
   * fd.
 | 
			
		||||
   *
 | 
			
		||||
   * This function returns no error, because there is nothing that the caller
 | 
			
		||||
   * could do with that information. That is even the case for EINTR. See
 | 
			
		||||
   * g_close() about the specialty of EINTR and why that is correct.
 | 
			
		||||
   * If g_close() ever gets extended to handle EINTR specially, then this place
 | 
			
		||||
   * should get updated to do the same handling.
 | 
			
		||||
   */
 | 
			
		||||
  if (fd >= GPOINTER_TO_INT (data))
 | 
			
		||||
    close (fd);
 | 
			
		||||
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef __linux__
 | 
			
		||||
struct linux_dirent64
 | 
			
		||||
{
 | 
			
		||||
  guint64        d_ino;    /* 64-bit inode number */
 | 
			
		||||
  guint64        d_off;    /* 64-bit offset to next structure */
 | 
			
		||||
  unsigned short d_reclen; /* Size of this dirent */
 | 
			
		||||
  unsigned char  d_type;   /* File type */
 | 
			
		||||
  char           d_name[]; /* Filename (null-terminated) */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* This function is called between fork() and exec() and hence must be
 | 
			
		||||
 * async-signal-safe (see signal-safety(7)). */
 | 
			
		||||
static gint
 | 
			
		||||
filename_to_fd (const char *p)
 | 
			
		||||
{
 | 
			
		||||
  char c;
 | 
			
		||||
  int fd = 0;
 | 
			
		||||
  const int cutoff = G_MAXINT / 10;
 | 
			
		||||
  const int cutlim = G_MAXINT % 10;
 | 
			
		||||
 | 
			
		||||
  if (*p == '\0')
 | 
			
		||||
    return -1;
 | 
			
		||||
 | 
			
		||||
  while ((c = *p++) != '\0')
 | 
			
		||||
    {
 | 
			
		||||
      if (c < '0' || c > '9')
 | 
			
		||||
        return -1;
 | 
			
		||||
      c -= '0';
 | 
			
		||||
 | 
			
		||||
      /* Check for overflow. */
 | 
			
		||||
      if (fd > cutoff || (fd == cutoff && c > cutlim))
 | 
			
		||||
        return -1;
 | 
			
		||||
 | 
			
		||||
      fd = fd * 10 + c;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  return fd;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static int safe_fdwalk_with_invalid_fds (int (*cb)(void *data, int fd), void *data);
 | 
			
		||||
 | 
			
		||||
/* This function is called between fork() and exec() and hence must be
 | 
			
		||||
 * async-signal-safe (see signal-safety(7)). */
 | 
			
		||||
static int
 | 
			
		||||
safe_fdwalk (int (*cb)(void *data, int fd), void *data)
 | 
			
		||||
{
 | 
			
		||||
#if 0
 | 
			
		||||
  /* Use fdwalk function provided by the system if it is known to be
 | 
			
		||||
   * async-signal safe.
 | 
			
		||||
   *
 | 
			
		||||
   * Currently there are no operating systems known to provide a safe
 | 
			
		||||
   * implementation, so this section is not used for now.
 | 
			
		||||
   */
 | 
			
		||||
  return fdwalk (cb, data);
 | 
			
		||||
#else
 | 
			
		||||
  /* Fallback implementation of fdwalk. It should be async-signal safe, but it
 | 
			
		||||
   * may fail on non-Linux operating systems. See safe_fdwalk_with_invalid_fds
 | 
			
		||||
   * for a slower alternative.
 | 
			
		||||
   */
 | 
			
		||||
 | 
			
		||||
#ifdef __linux__
 | 
			
		||||
  gint fd;
 | 
			
		||||
  gint res = 0;
 | 
			
		||||
 | 
			
		||||
  /* Avoid use of opendir/closedir since these are not async-signal-safe. */
 | 
			
		||||
  int dir_fd = open ("/proc/self/fd", O_RDONLY | O_DIRECTORY);
 | 
			
		||||
  if (dir_fd >= 0)
 | 
			
		||||
    {
 | 
			
		||||
      /* buf needs to be aligned correctly to receive linux_dirent64.
 | 
			
		||||
       * C11 has _Alignof for this purpose, but for now a
 | 
			
		||||
       * union serves the same purpose. */
 | 
			
		||||
      union
 | 
			
		||||
      {
 | 
			
		||||
        char buf[4096];
 | 
			
		||||
        struct linux_dirent64 alignment;
 | 
			
		||||
      } u;
 | 
			
		||||
      int pos, nread;
 | 
			
		||||
      struct linux_dirent64 *de;
 | 
			
		||||
 | 
			
		||||
      while ((nread = syscall (SYS_getdents64, dir_fd, u.buf, sizeof (u.buf))) > 0)
 | 
			
		||||
        {
 | 
			
		||||
          for (pos = 0; pos < nread; pos += de->d_reclen)
 | 
			
		||||
            {
 | 
			
		||||
              de = (struct linux_dirent64 *) (u.buf + pos);
 | 
			
		||||
 | 
			
		||||
              fd = filename_to_fd (de->d_name);
 | 
			
		||||
              if (fd < 0 || fd == dir_fd)
 | 
			
		||||
                  continue;
 | 
			
		||||
 | 
			
		||||
              if ((res = cb (data, fd)) != 0)
 | 
			
		||||
                  break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
      g_close (dir_fd, NULL);
 | 
			
		||||
      return res;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  /* If /proc is not mounted or not accessible we fail here and rely on
 | 
			
		||||
   * safe_fdwalk_with_invalid_fds to fall back to the old
 | 
			
		||||
   * rlimit trick. */
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if defined(__sun__) && defined(F_PREVFD) && defined(F_NEXTFD)
 | 
			
		||||
/*
 | 
			
		||||
 * Solaris 11.4 has a signal-safe way which allows
 | 
			
		||||
 * us to find all file descriptors in a process.
 | 
			
		||||
 *
 | 
			
		||||
 * fcntl(fd, F_NEXTFD, maxfd)
 | 
			
		||||
 * - returns the first allocated file descriptor <= maxfd  > fd.
 | 
			
		||||
 *
 | 
			
		||||
 * fcntl(fd, F_PREVFD)
 | 
			
		||||
 * - return highest allocated file descriptor < fd.
 | 
			
		||||
 */
 | 
			
		||||
  gint fd;
 | 
			
		||||
  gint res = 0;
 | 
			
		||||
 | 
			
		||||
  open_max = fcntl (INT_MAX, F_PREVFD); /* find the maximum fd */
 | 
			
		||||
  if (open_max < 0) /* No open files */
 | 
			
		||||
    return 0;
 | 
			
		||||
 | 
			
		||||
  for (fd = -1; (fd = fcntl (fd, F_NEXTFD, open_max)) != -1; )
 | 
			
		||||
    if ((res = cb (data, fd)) != 0 || fd == open_max)
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
  return res;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  return safe_fdwalk_with_invalid_fds (cb, data);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* This function is called between fork() and exec() and hence must be
 | 
			
		||||
 * async-signal-safe (see signal-safety(7)). */
 | 
			
		||||
static int
 | 
			
		||||
safe_fdwalk_with_invalid_fds (int (*cb)(void *data, int fd), void *data)
 | 
			
		||||
{
 | 
			
		||||
  /* Fallback implementation of fdwalk. It should be async-signal safe, but it
 | 
			
		||||
   * may be slow, especially on systems allowing very high number of open file
 | 
			
		||||
   * descriptors.
 | 
			
		||||
   */
 | 
			
		||||
  gint open_max = -1;
 | 
			
		||||
  gint fd;
 | 
			
		||||
  gint res = 0;
 | 
			
		||||
 | 
			
		||||
#if 0 && defined(HAVE_SYS_RESOURCE_H)
 | 
			
		||||
  struct rlimit rl;
 | 
			
		||||
 | 
			
		||||
  /* Use getrlimit() function provided by the system if it is known to be
 | 
			
		||||
   * async-signal safe.
 | 
			
		||||
   *
 | 
			
		||||
   * Currently there are no operating systems known to provide a safe
 | 
			
		||||
   * implementation, so this section is not used for now.
 | 
			
		||||
   */
 | 
			
		||||
  if (getrlimit (RLIMIT_NOFILE, &rl) == 0 && rl.rlim_max != RLIM_INFINITY)
 | 
			
		||||
    open_max = rl.rlim_max;
 | 
			
		||||
#endif
 | 
			
		||||
#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
 | 
			
		||||
  /* Use sysconf() function provided by the system if it is known to be
 | 
			
		||||
   * async-signal safe.
 | 
			
		||||
   *
 | 
			
		||||
   * FreeBSD: sysconf() is included in the list of async-signal safe functions
 | 
			
		||||
   * found in https://man.freebsd.org/sigaction(2).
 | 
			
		||||
   *
 | 
			
		||||
   * OpenBSD: sysconf() is included in the list of async-signal safe functions
 | 
			
		||||
   * found in https://man.openbsd.org/sigaction.2.
 | 
			
		||||
   *
 | 
			
		||||
   * Apple: sysconf() is included in the list of async-signal safe functions
 | 
			
		||||
   * found in https://opensource.apple.com/source/xnu/xnu-517.12.7/bsd/man/man2/sigaction.2
 | 
			
		||||
   */
 | 
			
		||||
  if (open_max < 0)
 | 
			
		||||
    open_max = sysconf (_SC_OPEN_MAX);
 | 
			
		||||
#endif
 | 
			
		||||
  /* Hardcoded fallback: the default process hard limit in Linux as of 2020 */
 | 
			
		||||
  if (open_max < 0)
 | 
			
		||||
    open_max = 4096;
 | 
			
		||||
 | 
			
		||||
#if defined(__APPLE__) && defined(HAVE_LIBPROC_H)
 | 
			
		||||
  /* proc_pidinfo isn't documented as async-signal-safe but looking at the implementation
 | 
			
		||||
   * in the darwin tree here:
 | 
			
		||||
   *
 | 
			
		||||
   * https://opensource.apple.com/source/Libc/Libc-498/darwin/libproc.c.auto.html
 | 
			
		||||
   *
 | 
			
		||||
   * It's just a thin wrapper around a syscall, so it's probably okay.
 | 
			
		||||
   */
 | 
			
		||||
  {
 | 
			
		||||
    char buffer[4096 * PROC_PIDLISTFD_SIZE];
 | 
			
		||||
    ssize_t buffer_size;
 | 
			
		||||
 | 
			
		||||
    buffer_size = proc_pidinfo (getpid (), PROC_PIDLISTFDS, 0, buffer, sizeof (buffer));
 | 
			
		||||
 | 
			
		||||
    if (buffer_size > 0 &&
 | 
			
		||||
        sizeof (buffer) >= (size_t) buffer_size &&
 | 
			
		||||
        (buffer_size % PROC_PIDLISTFD_SIZE) == 0)
 | 
			
		||||
      {
 | 
			
		||||
        const struct proc_fdinfo *fd_info = (const struct proc_fdinfo *) buffer;
 | 
			
		||||
        size_t number_of_fds = (size_t) buffer_size / PROC_PIDLISTFD_SIZE;
 | 
			
		||||
 | 
			
		||||
        for (size_t i = 0; i < number_of_fds; i++)
 | 
			
		||||
          if ((res = cb (data, fd_info[i].proc_fd)) != 0)
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        return res;
 | 
			
		||||
      }
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  for (fd = 0; fd < open_max; fd++)
 | 
			
		||||
      if ((res = cb (data, fd)) != 0)
 | 
			
		||||
          break;
 | 
			
		||||
 | 
			
		||||
  return res;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * g_fdwalk_set_cloexec:
 | 
			
		||||
 * @lowfd: Minimum fd to act on, which must be non-negative
 | 
			
		||||
 *
 | 
			
		||||
 * Mark every file descriptor equal to or greater than @lowfd to be closed
 | 
			
		||||
 * at the next `execve()` or similar, as if via the `FD_CLOEXEC` flag.
 | 
			
		||||
 *
 | 
			
		||||
 * Typically @lowfd will be 3, to leave standard input, standard output
 | 
			
		||||
 * and standard error open after exec.
 | 
			
		||||
 *
 | 
			
		||||
 * This is the same as Linux `close_range (lowfd, ~0U, CLOSE_RANGE_CLOEXEC)`,
 | 
			
		||||
 * but portable to other OSs and to older versions of Linux.
 | 
			
		||||
 *
 | 
			
		||||
 * This function is async-signal safe, making it safe to call from a
 | 
			
		||||
 * signal handler or a [callback@GLib.SpawnChildSetupFunc], as long as @lowfd is
 | 
			
		||||
 * non-negative.
 | 
			
		||||
 * See [`signal(7)`](man:signal(7)) and
 | 
			
		||||
 * [`signal-safety(7)`](man:signal-safety(7)) for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns: 0 on success, -1 with errno set on error
 | 
			
		||||
 * Since: 2.80
 | 
			
		||||
 */
 | 
			
		||||
int
 | 
			
		||||
g_fdwalk_set_cloexec (int lowfd)
 | 
			
		||||
{
 | 
			
		||||
  int ret;
 | 
			
		||||
 | 
			
		||||
  g_return_val_if_fail (lowfd >= 0, (errno = EINVAL, -1));
 | 
			
		||||
 | 
			
		||||
#if defined(HAVE_CLOSE_RANGE) && defined(CLOSE_RANGE_CLOEXEC)
 | 
			
		||||
  /* close_range() is available in Linux since kernel 5.9, and on FreeBSD at
 | 
			
		||||
   * around the same time. It was designed for use in async-signal-safe
 | 
			
		||||
   * situations: https://bugs.python.org/issue38061
 | 
			
		||||
   *
 | 
			
		||||
   * The `CLOSE_RANGE_CLOEXEC` flag was added in Linux 5.11, and is not yet
 | 
			
		||||
   * present in FreeBSD.
 | 
			
		||||
   *
 | 
			
		||||
   * Handle ENOSYS in case it’s supported in libc but not the kernel; if so,
 | 
			
		||||
   * fall back to safe_fdwalk(). Handle EINVAL in case `CLOSE_RANGE_CLOEXEC`
 | 
			
		||||
   * is not supported. */
 | 
			
		||||
  ret = close_range (lowfd, G_MAXUINT, CLOSE_RANGE_CLOEXEC);
 | 
			
		||||
  if (ret == 0 || !(errno == ENOSYS || errno == EINVAL))
 | 
			
		||||
    return ret;
 | 
			
		||||
#endif  /* HAVE_CLOSE_RANGE */
 | 
			
		||||
 | 
			
		||||
  ret = safe_fdwalk (set_cloexec, GINT_TO_POINTER (lowfd));
 | 
			
		||||
 | 
			
		||||
  return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * g_closefrom:
 | 
			
		||||
 * @lowfd: Minimum fd to close, which must be non-negative
 | 
			
		||||
 *
 | 
			
		||||
 * Close every file descriptor equal to or greater than @lowfd.
 | 
			
		||||
 *
 | 
			
		||||
 * Typically @lowfd will be 3, to leave standard input, standard output
 | 
			
		||||
 * and standard error open.
 | 
			
		||||
 *
 | 
			
		||||
 * This is the same as Linux `close_range (lowfd, ~0U, 0)`,
 | 
			
		||||
 * but portable to other OSs and to older versions of Linux.
 | 
			
		||||
 * Equivalently, it is the same as BSD `closefrom (lowfd)`, but portable,
 | 
			
		||||
 * and async-signal-safe on all OSs.
 | 
			
		||||
 *
 | 
			
		||||
 * This function is async-signal safe, making it safe to call from a
 | 
			
		||||
 * signal handler or a [callback@GLib.SpawnChildSetupFunc], as long as @lowfd is
 | 
			
		||||
 * non-negative.
 | 
			
		||||
 * See [`signal(7)`](man:signal(7)) and
 | 
			
		||||
 * [`signal-safety(7)`](man:signal-safety(7)) for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns: 0 on success, -1 with errno set on error
 | 
			
		||||
 * Since: 2.80
 | 
			
		||||
 */
 | 
			
		||||
int
 | 
			
		||||
g_closefrom (int lowfd)
 | 
			
		||||
{
 | 
			
		||||
  int ret;
 | 
			
		||||
 | 
			
		||||
  g_return_val_if_fail (lowfd >= 0, (errno = EINVAL, -1));
 | 
			
		||||
 | 
			
		||||
#if defined(HAVE_CLOSE_RANGE)
 | 
			
		||||
  /* close_range() is available in Linux since kernel 5.9, and on FreeBSD at
 | 
			
		||||
   * around the same time. It was designed for use in async-signal-safe
 | 
			
		||||
   * situations: https://bugs.python.org/issue38061
 | 
			
		||||
   *
 | 
			
		||||
   * Handle ENOSYS in case it’s supported in libc but not the kernel; if so,
 | 
			
		||||
   * fall back to safe_fdwalk(). */
 | 
			
		||||
  ret = close_range (lowfd, G_MAXUINT, 0);
 | 
			
		||||
  if (ret == 0 || errno != ENOSYS)
 | 
			
		||||
    return ret;
 | 
			
		||||
#endif  /* HAVE_CLOSE_RANGE */
 | 
			
		||||
 | 
			
		||||
#if defined(__FreeBSD__) || defined(__OpenBSD__) || \
 | 
			
		||||
  (defined(__sun__) && defined(F_CLOSEFROM))
 | 
			
		||||
  /* Use closefrom function provided by the system if it is known to be
 | 
			
		||||
   * async-signal safe.
 | 
			
		||||
   *
 | 
			
		||||
   * FreeBSD: closefrom is included in the list of async-signal safe functions
 | 
			
		||||
   * found in https://man.freebsd.org/sigaction(2).
 | 
			
		||||
   *
 | 
			
		||||
   * OpenBSD: closefrom is not included in the list, but a direct system call
 | 
			
		||||
   * should be safe to use.
 | 
			
		||||
   *
 | 
			
		||||
   * In Solaris as of 11.3 SRU 31, closefrom() is also a direct system call.
 | 
			
		||||
   * On such systems, F_CLOSEFROM is defined.
 | 
			
		||||
   */
 | 
			
		||||
  (void) closefrom (lowfd);
 | 
			
		||||
  return 0;
 | 
			
		||||
#elif defined(__DragonFly__)
 | 
			
		||||
  /* It is unclear whether closefrom function included in DragonFlyBSD libc_r
 | 
			
		||||
   * is safe to use because it calls a lot of library functions. It is also
 | 
			
		||||
   * unclear whether libc_r itself is still being used. Therefore, we do a
 | 
			
		||||
   * direct system call here ourselves to avoid possible issues.
 | 
			
		||||
   */
 | 
			
		||||
  (void) syscall (SYS_closefrom, lowfd);
 | 
			
		||||
  return 0;
 | 
			
		||||
#elif defined(F_CLOSEM)
 | 
			
		||||
  /* NetBSD and AIX have a special fcntl command which does the same thing as
 | 
			
		||||
   * closefrom. NetBSD also includes closefrom function, which seems to be a
 | 
			
		||||
   * simple wrapper of the fcntl command.
 | 
			
		||||
   */
 | 
			
		||||
  return fcntl (lowfd, F_CLOSEM);
 | 
			
		||||
#else
 | 
			
		||||
  ret = safe_fdwalk (close_func_with_invalid_fds, GINT_TO_POINTER (lowfd));
 | 
			
		||||
 | 
			
		||||
  return ret;
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -326,6 +326,12 @@ g_unix_pipe_clear (GUnixPipe *self)
 | 
			
		||||
 | 
			
		||||
G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC (GUnixPipe, g_unix_pipe_clear)
 | 
			
		||||
 | 
			
		||||
GLIB_AVAILABLE_IN_2_80
 | 
			
		||||
int g_closefrom (int lowfd);
 | 
			
		||||
 | 
			
		||||
GLIB_AVAILABLE_IN_2_80
 | 
			
		||||
int g_fdwalk_set_cloexec (int lowfd);
 | 
			
		||||
 | 
			
		||||
G_GNUC_END_IGNORE_DEPRECATIONS
 | 
			
		||||
 | 
			
		||||
G_END_DECLS
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										335
									
								
								glib/gspawn.c
									
									
									
									
									
								
							
							
						
						
									
										335
									
								
								glib/gspawn.c
									
									
									
									
									
								
							@@ -1265,337 +1265,6 @@ dupfd_cloexec (int old_fd, int new_fd_min)
 | 
			
		||||
  return fd;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* fdwalk()-compatible callback to close a fd for non-compliant
 | 
			
		||||
 * implementations of fdwalk() that potentially pass already
 | 
			
		||||
 * closed fds.
 | 
			
		||||
 *
 | 
			
		||||
 * It is not an error to pass an invalid fd to this function.
 | 
			
		||||
 *
 | 
			
		||||
 * This function is called between fork() and exec() and hence must be
 | 
			
		||||
 * async-signal-safe (see signal-safety(7)).
 | 
			
		||||
 */
 | 
			
		||||
G_GNUC_UNUSED static int
 | 
			
		||||
close_func_with_invalid_fds (void *data, int fd)
 | 
			
		||||
{
 | 
			
		||||
  /* We use close and not g_close here because on some platforms, we
 | 
			
		||||
   * don't know how to close only valid, open file descriptors, so we
 | 
			
		||||
   * have to pass bad fds to close too. g_close warns if given a bad
 | 
			
		||||
   * fd.
 | 
			
		||||
   *
 | 
			
		||||
   * This function returns no error, because there is nothing that the caller
 | 
			
		||||
   * could do with that information. That is even the case for EINTR. See
 | 
			
		||||
   * g_close() about the specialty of EINTR and why that is correct.
 | 
			
		||||
   * If g_close() ever gets extended to handle EINTR specially, then this place
 | 
			
		||||
   * should get updated to do the same handling.
 | 
			
		||||
   */
 | 
			
		||||
  if (fd >= GPOINTER_TO_INT (data))
 | 
			
		||||
    close (fd);
 | 
			
		||||
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef __linux__
 | 
			
		||||
struct linux_dirent64
 | 
			
		||||
{
 | 
			
		||||
  guint64        d_ino;    /* 64-bit inode number */
 | 
			
		||||
  guint64        d_off;    /* 64-bit offset to next structure */
 | 
			
		||||
  unsigned short d_reclen; /* Size of this dirent */
 | 
			
		||||
  unsigned char  d_type;   /* File type */
 | 
			
		||||
  char           d_name[]; /* Filename (null-terminated) */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* This function is called between fork() and exec() and hence must be
 | 
			
		||||
 * async-signal-safe (see signal-safety(7)). */
 | 
			
		||||
static gint
 | 
			
		||||
filename_to_fd (const char *p)
 | 
			
		||||
{
 | 
			
		||||
  char c;
 | 
			
		||||
  int fd = 0;
 | 
			
		||||
  const int cutoff = G_MAXINT / 10;
 | 
			
		||||
  const int cutlim = G_MAXINT % 10;
 | 
			
		||||
 | 
			
		||||
  if (*p == '\0')
 | 
			
		||||
    return -1;
 | 
			
		||||
 | 
			
		||||
  while ((c = *p++) != '\0')
 | 
			
		||||
    {
 | 
			
		||||
      if (c < '0' || c > '9')
 | 
			
		||||
        return -1;
 | 
			
		||||
      c -= '0';
 | 
			
		||||
 | 
			
		||||
      /* Check for overflow. */
 | 
			
		||||
      if (fd > cutoff || (fd == cutoff && c > cutlim))
 | 
			
		||||
        return -1;
 | 
			
		||||
 | 
			
		||||
      fd = fd * 10 + c;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  return fd;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static int safe_fdwalk_with_invalid_fds (int (*cb)(void *data, int fd), void *data);
 | 
			
		||||
 | 
			
		||||
/* This function is called between fork() and exec() and hence must be
 | 
			
		||||
 * async-signal-safe (see signal-safety(7)). */
 | 
			
		||||
static int
 | 
			
		||||
safe_fdwalk (int (*cb)(void *data, int fd), void *data)
 | 
			
		||||
{
 | 
			
		||||
#if 0
 | 
			
		||||
  /* Use fdwalk function provided by the system if it is known to be
 | 
			
		||||
   * async-signal safe.
 | 
			
		||||
   *
 | 
			
		||||
   * Currently there are no operating systems known to provide a safe
 | 
			
		||||
   * implementation, so this section is not used for now.
 | 
			
		||||
   */
 | 
			
		||||
  return fdwalk (cb, data);
 | 
			
		||||
#else
 | 
			
		||||
  /* Fallback implementation of fdwalk. It should be async-signal safe, but it
 | 
			
		||||
   * may fail on non-Linux operating systems. See safe_fdwalk_with_invalid_fds
 | 
			
		||||
   * for a slower alternative.
 | 
			
		||||
   */
 | 
			
		||||
 | 
			
		||||
#ifdef __linux__
 | 
			
		||||
  gint fd;
 | 
			
		||||
  gint res = 0;
 | 
			
		||||
 | 
			
		||||
  /* Avoid use of opendir/closedir since these are not async-signal-safe. */
 | 
			
		||||
  int dir_fd = open ("/proc/self/fd", O_RDONLY | O_DIRECTORY);
 | 
			
		||||
  if (dir_fd >= 0)
 | 
			
		||||
    {
 | 
			
		||||
      /* buf needs to be aligned correctly to receive linux_dirent64.
 | 
			
		||||
       * C11 has _Alignof for this purpose, but for now a
 | 
			
		||||
       * union serves the same purpose. */
 | 
			
		||||
      union
 | 
			
		||||
      {
 | 
			
		||||
        char buf[4096];
 | 
			
		||||
        struct linux_dirent64 alignment;
 | 
			
		||||
      } u;
 | 
			
		||||
      int pos, nread;
 | 
			
		||||
      struct linux_dirent64 *de;
 | 
			
		||||
 | 
			
		||||
      while ((nread = syscall (SYS_getdents64, dir_fd, u.buf, sizeof (u.buf))) > 0)
 | 
			
		||||
        {
 | 
			
		||||
          for (pos = 0; pos < nread; pos += de->d_reclen)
 | 
			
		||||
            {
 | 
			
		||||
              de = (struct linux_dirent64 *) (u.buf + pos);
 | 
			
		||||
 | 
			
		||||
              fd = filename_to_fd (de->d_name);
 | 
			
		||||
              if (fd < 0 || fd == dir_fd)
 | 
			
		||||
                  continue;
 | 
			
		||||
 | 
			
		||||
              if ((res = cb (data, fd)) != 0)
 | 
			
		||||
                  break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
      g_close (dir_fd, NULL);
 | 
			
		||||
      return res;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  /* If /proc is not mounted or not accessible we fail here and rely on
 | 
			
		||||
   * safe_fdwalk_with_invalid_fds to fall back to the old
 | 
			
		||||
   * rlimit trick. */
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if defined(__sun__) && defined(F_PREVFD) && defined(F_NEXTFD)
 | 
			
		||||
/*
 | 
			
		||||
 * Solaris 11.4 has a signal-safe way which allows
 | 
			
		||||
 * us to find all file descriptors in a process.
 | 
			
		||||
 *
 | 
			
		||||
 * fcntl(fd, F_NEXTFD, maxfd)
 | 
			
		||||
 * - returns the first allocated file descriptor <= maxfd  > fd.
 | 
			
		||||
 *
 | 
			
		||||
 * fcntl(fd, F_PREVFD)
 | 
			
		||||
 * - return highest allocated file descriptor < fd.
 | 
			
		||||
 */
 | 
			
		||||
  gint fd;
 | 
			
		||||
  gint res = 0;
 | 
			
		||||
 | 
			
		||||
  open_max = fcntl (INT_MAX, F_PREVFD); /* find the maximum fd */
 | 
			
		||||
  if (open_max < 0) /* No open files */
 | 
			
		||||
    return 0;
 | 
			
		||||
 | 
			
		||||
  for (fd = -1; (fd = fcntl (fd, F_NEXTFD, open_max)) != -1; )
 | 
			
		||||
    if ((res = cb (data, fd)) != 0 || fd == open_max)
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
  return res;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  return safe_fdwalk_with_invalid_fds (cb, data);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* This function is called between fork() and exec() and hence must be
 | 
			
		||||
 * async-signal-safe (see signal-safety(7)). */
 | 
			
		||||
static int
 | 
			
		||||
safe_fdwalk_with_invalid_fds (int (*cb)(void *data, int fd), void *data)
 | 
			
		||||
{
 | 
			
		||||
  /* Fallback implementation of fdwalk. It should be async-signal safe, but it
 | 
			
		||||
   * may be slow, especially on systems allowing very high number of open file
 | 
			
		||||
   * descriptors.
 | 
			
		||||
   */
 | 
			
		||||
  gint open_max = -1;
 | 
			
		||||
  gint fd;
 | 
			
		||||
  gint res = 0;
 | 
			
		||||
 | 
			
		||||
#if 0 && defined(HAVE_SYS_RESOURCE_H)
 | 
			
		||||
  struct rlimit rl;
 | 
			
		||||
 | 
			
		||||
  /* Use getrlimit() function provided by the system if it is known to be
 | 
			
		||||
   * async-signal safe.
 | 
			
		||||
   *
 | 
			
		||||
   * Currently there are no operating systems known to provide a safe
 | 
			
		||||
   * implementation, so this section is not used for now.
 | 
			
		||||
   */
 | 
			
		||||
  if (getrlimit (RLIMIT_NOFILE, &rl) == 0 && rl.rlim_max != RLIM_INFINITY)
 | 
			
		||||
    open_max = rl.rlim_max;
 | 
			
		||||
#endif
 | 
			
		||||
#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
 | 
			
		||||
  /* Use sysconf() function provided by the system if it is known to be
 | 
			
		||||
   * async-signal safe.
 | 
			
		||||
   *
 | 
			
		||||
   * FreeBSD: sysconf() is included in the list of async-signal safe functions
 | 
			
		||||
   * found in https://man.freebsd.org/sigaction(2).
 | 
			
		||||
   *
 | 
			
		||||
   * OpenBSD: sysconf() is included in the list of async-signal safe functions
 | 
			
		||||
   * found in https://man.openbsd.org/sigaction.2.
 | 
			
		||||
   * 
 | 
			
		||||
   * Apple: sysconf() is included in the list of async-signal safe functions
 | 
			
		||||
   * found in https://opensource.apple.com/source/xnu/xnu-517.12.7/bsd/man/man2/sigaction.2
 | 
			
		||||
   */
 | 
			
		||||
  if (open_max < 0)
 | 
			
		||||
    open_max = sysconf (_SC_OPEN_MAX);
 | 
			
		||||
#endif
 | 
			
		||||
  /* Hardcoded fallback: the default process hard limit in Linux as of 2020 */
 | 
			
		||||
  if (open_max < 0)
 | 
			
		||||
    open_max = 4096;
 | 
			
		||||
 | 
			
		||||
#if defined(__APPLE__) && defined(HAVE_LIBPROC_H)
 | 
			
		||||
  /* proc_pidinfo isn't documented as async-signal-safe but looking at the implementation
 | 
			
		||||
   * in the darwin tree here:
 | 
			
		||||
   *
 | 
			
		||||
   * https://opensource.apple.com/source/Libc/Libc-498/darwin/libproc.c.auto.html
 | 
			
		||||
   *
 | 
			
		||||
   * It's just a thin wrapper around a syscall, so it's probably okay.
 | 
			
		||||
   */
 | 
			
		||||
  {
 | 
			
		||||
    char buffer[4096 * PROC_PIDLISTFD_SIZE];
 | 
			
		||||
    ssize_t buffer_size;
 | 
			
		||||
 | 
			
		||||
    buffer_size = proc_pidinfo (getpid (), PROC_PIDLISTFDS, 0, buffer, sizeof (buffer));
 | 
			
		||||
 | 
			
		||||
    if (buffer_size > 0 &&
 | 
			
		||||
        sizeof (buffer) >= (size_t) buffer_size &&
 | 
			
		||||
        (buffer_size % PROC_PIDLISTFD_SIZE) == 0)
 | 
			
		||||
      {
 | 
			
		||||
        const struct proc_fdinfo *fd_info = (const struct proc_fdinfo *) buffer;
 | 
			
		||||
        size_t number_of_fds = (size_t) buffer_size / PROC_PIDLISTFD_SIZE;
 | 
			
		||||
 | 
			
		||||
        for (size_t i = 0; i < number_of_fds; i++)
 | 
			
		||||
          if ((res = cb (data, fd_info[i].proc_fd)) != 0)
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        return res;
 | 
			
		||||
      }
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  for (fd = 0; fd < open_max; fd++)
 | 
			
		||||
      if ((res = cb (data, fd)) != 0)
 | 
			
		||||
          break;
 | 
			
		||||
 | 
			
		||||
  return res;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* This function is called between fork() and exec() and hence must be
 | 
			
		||||
 * async-signal-safe (see signal-safety(7)). */
 | 
			
		||||
static int
 | 
			
		||||
safe_fdwalk_set_cloexec (int lowfd)
 | 
			
		||||
{
 | 
			
		||||
  int ret;
 | 
			
		||||
 | 
			
		||||
#if defined(HAVE_CLOSE_RANGE) && defined(CLOSE_RANGE_CLOEXEC)
 | 
			
		||||
  /* close_range() is available in Linux since kernel 5.9, and on FreeBSD at
 | 
			
		||||
   * around the same time. It was designed for use in async-signal-safe
 | 
			
		||||
   * situations: https://bugs.python.org/issue38061
 | 
			
		||||
   *
 | 
			
		||||
   * The `CLOSE_RANGE_CLOEXEC` flag was added in Linux 5.11, and is not yet
 | 
			
		||||
   * present in FreeBSD.
 | 
			
		||||
   *
 | 
			
		||||
   * Handle ENOSYS in case it’s supported in libc but not the kernel; if so,
 | 
			
		||||
   * fall back to safe_fdwalk(). Handle EINVAL in case `CLOSE_RANGE_CLOEXEC`
 | 
			
		||||
   * is not supported. */
 | 
			
		||||
  ret = close_range (lowfd, G_MAXUINT, CLOSE_RANGE_CLOEXEC);
 | 
			
		||||
  if (ret == 0 || !(errno == ENOSYS || errno == EINVAL))
 | 
			
		||||
    return ret;
 | 
			
		||||
#endif  /* HAVE_CLOSE_RANGE */
 | 
			
		||||
 | 
			
		||||
  ret = safe_fdwalk (set_cloexec, GINT_TO_POINTER (lowfd));
 | 
			
		||||
 | 
			
		||||
  return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* This function is called between fork() and exec() and hence must be
 | 
			
		||||
 * async-signal-safe (see signal-safety(7)).
 | 
			
		||||
 *
 | 
			
		||||
 * On failure, `-1` will be returned and errno will be set. */
 | 
			
		||||
static int
 | 
			
		||||
safe_closefrom (int lowfd)
 | 
			
		||||
{
 | 
			
		||||
  int ret;
 | 
			
		||||
 | 
			
		||||
#if defined(HAVE_CLOSE_RANGE)
 | 
			
		||||
  /* close_range() is available in Linux since kernel 5.9, and on FreeBSD at
 | 
			
		||||
   * around the same time. It was designed for use in async-signal-safe
 | 
			
		||||
   * situations: https://bugs.python.org/issue38061
 | 
			
		||||
   *
 | 
			
		||||
   * Handle ENOSYS in case it’s supported in libc but not the kernel; if so,
 | 
			
		||||
   * fall back to safe_fdwalk(). */
 | 
			
		||||
  ret = close_range (lowfd, G_MAXUINT, 0);
 | 
			
		||||
  if (ret == 0 || errno != ENOSYS)
 | 
			
		||||
    return ret;
 | 
			
		||||
#endif  /* HAVE_CLOSE_RANGE */
 | 
			
		||||
 | 
			
		||||
#if defined(__FreeBSD__) || defined(__OpenBSD__) || \
 | 
			
		||||
  (defined(__sun__) && defined(F_CLOSEFROM))
 | 
			
		||||
  /* Use closefrom function provided by the system if it is known to be
 | 
			
		||||
   * async-signal safe.
 | 
			
		||||
   *
 | 
			
		||||
   * FreeBSD: closefrom is included in the list of async-signal safe functions
 | 
			
		||||
   * found in https://man.freebsd.org/sigaction(2).
 | 
			
		||||
   *
 | 
			
		||||
   * OpenBSD: closefrom is not included in the list, but a direct system call
 | 
			
		||||
   * should be safe to use.
 | 
			
		||||
   *
 | 
			
		||||
   * In Solaris as of 11.3 SRU 31, closefrom() is also a direct system call.
 | 
			
		||||
   * On such systems, F_CLOSEFROM is defined.
 | 
			
		||||
   */
 | 
			
		||||
  (void) closefrom (lowfd);
 | 
			
		||||
  return 0;
 | 
			
		||||
#elif defined(__DragonFly__)
 | 
			
		||||
  /* It is unclear whether closefrom function included in DragonFlyBSD libc_r
 | 
			
		||||
   * is safe to use because it calls a lot of library functions. It is also
 | 
			
		||||
   * unclear whether libc_r itself is still being used. Therefore, we do a
 | 
			
		||||
   * direct system call here ourselves to avoid possible issues.
 | 
			
		||||
   */
 | 
			
		||||
  (void) syscall (SYS_closefrom, lowfd);
 | 
			
		||||
  return 0;
 | 
			
		||||
#elif defined(F_CLOSEM)
 | 
			
		||||
  /* NetBSD and AIX have a special fcntl command which does the same thing as
 | 
			
		||||
   * closefrom. NetBSD also includes closefrom function, which seems to be a
 | 
			
		||||
   * simple wrapper of the fcntl command.
 | 
			
		||||
   */
 | 
			
		||||
  return fcntl (lowfd, F_CLOSEM);
 | 
			
		||||
#else
 | 
			
		||||
  ret = safe_fdwalk (close_func_with_invalid_fds, GINT_TO_POINTER (lowfd));
 | 
			
		||||
 | 
			
		||||
  return ret;
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* This function is called between fork() and exec() and hence must be
 | 
			
		||||
 * async-signal-safe (see signal-safety(7)). */
 | 
			
		||||
static gint
 | 
			
		||||
@@ -1823,13 +1492,13 @@ do_exec (gint                  child_err_report_fd,
 | 
			
		||||
          if (safe_dup2 (child_err_report_fd, 3) < 0)
 | 
			
		||||
            write_err_and_exit (child_err_report_fd, CHILD_DUPFD_FAILED);
 | 
			
		||||
          set_cloexec (GINT_TO_POINTER (0), 3);
 | 
			
		||||
          if (safe_closefrom (4) < 0)
 | 
			
		||||
          if (g_closefrom (4) < 0)
 | 
			
		||||
            write_err_and_exit (child_err_report_fd, CHILD_CLOSE_FAILED);
 | 
			
		||||
          child_err_report_fd = 3;
 | 
			
		||||
        }
 | 
			
		||||
      else
 | 
			
		||||
        {
 | 
			
		||||
          if (safe_fdwalk_set_cloexec (3) < 0)
 | 
			
		||||
          if (g_fdwalk_set_cloexec (3) < 0)
 | 
			
		||||
            write_err_and_exit (child_err_report_fd, CHILD_CLOSE_FAILED);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user