(root)/
glibc-2.38/
csu/
check_fds.c
       1  /* Copyright (C) 2000-2023 Free Software Foundation, Inc.
       2     This file is part of the GNU C Library.
       3  
       4     The GNU C Library is free software; you can redistribute it and/or
       5     modify it under the terms of the GNU Lesser General Public
       6     License as published by the Free Software Foundation; either
       7     version 2.1 of the License, or (at your option) any later version.
       8  
       9     The GNU C Library is distributed in the hope that it will be useful,
      10     but WITHOUT ANY WARRANTY; without even the implied warranty of
      11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      12     Lesser General Public License for more details.
      13  
      14     You should have received a copy of the GNU Lesser General Public
      15     License along with the GNU C Library; if not, see
      16     <https://www.gnu.org/licenses/>.  */
      17  
      18  #include <errno.h>
      19  #include <fcntl.h>
      20  #include <paths.h>
      21  #include <unistd.h>
      22  #include <sys/stat.h>
      23  #include <sys/sysmacros.h>
      24  
      25  /* Try to get a machine dependent instruction which will make the
      26     program crash.  This is used in case everything else fails.  */
      27  #include <abort-instr.h>
      28  #ifndef ABORT_INSTRUCTION
      29  /* No such instruction is available.  */
      30  # define ABORT_INSTRUCTION
      31  #endif
      32  
      33  #include <device-nrs.h>
      34  #include <not-cancel.h>
      35  
      36  
      37  /* Should other OSes (e.g., Hurd) have different versions which can
      38     be written in a better way?  */
      39  static void
      40  check_one_fd (int fd, int mode)
      41  {
      42    if (__builtin_expect (__fcntl64_nocancel (fd, F_GETFD), 0) == -1
      43        && errno == EBADF)
      44      {
      45        const char *name;
      46        dev_t dev;
      47  
      48        /* For writable descriptors we use /dev/full.  */
      49        if ((mode & O_ACCMODE) == O_WRONLY)
      50  	{
      51  	  name = _PATH_DEV "full";
      52  	  dev = __gnu_dev_makedev (DEV_FULL_MAJOR, DEV_FULL_MINOR);
      53  	}
      54        else
      55  	{
      56  	  name = _PATH_DEVNULL;
      57  	  dev = __gnu_dev_makedev (DEV_NULL_MAJOR, DEV_NULL_MINOR);
      58  	}
      59  
      60        /* Something is wrong with this descriptor, it's probably not
      61  	 opened.  Open /dev/null so that the SUID program we are
      62  	 about to start does not accidentally use this descriptor.  */
      63        int nullfd = __open_nocancel (name, mode, 0);
      64  
      65        /* We are very paranoid here.  With all means we try to ensure
      66  	 that we are actually opening the /dev/null device and nothing
      67  	 else.
      68  
      69  	 Note that the following code assumes that STDIN_FILENO,
      70  	 STDOUT_FILENO, STDERR_FILENO are the three lowest file
      71  	 descriptor numbers, in this order.  */
      72        struct __stat64_t64 st;
      73        if (__glibc_unlikely (nullfd != fd)
      74  	  || __glibc_likely (__fstat64_time64 (fd, &st) != 0)
      75  	  || __glibc_unlikely (S_ISCHR (st.st_mode) == 0)
      76  	  || st.st_rdev != dev)
      77  	/* We cannot even give an error message here since it would
      78  	   run into the same problems.  */
      79  	while (1)
      80  	  /* Try for ever and ever.  */
      81  	  ABORT_INSTRUCTION;
      82      }
      83  }
      84  
      85  
      86  void
      87  __libc_check_standard_fds (void)
      88  {
      89    /* Check all three standard file descriptors.  The O_NOFOLLOW flag
      90       is really paranoid but some people actually are.  If /dev/null
      91       should happen to be a symlink to somewhere else and not the
      92       device commonly known as "/dev/null" we bail out.  */
      93    check_one_fd (STDIN_FILENO, O_WRONLY | O_NOFOLLOW);
      94    check_one_fd (STDOUT_FILENO, O_RDONLY | O_NOFOLLOW);
      95    check_one_fd (STDERR_FILENO, O_RDONLY | O_NOFOLLOW);
      96  }