| /* Copyright (C) 2000-2016 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 | 
 |    <http://www.gnu.org/licenses/>.  */ | 
 |  | 
 | #include <errno.h> | 
 | #include <fcntl.h> | 
 | #include <paths.h> | 
 | #include <unistd.h> | 
 | #include <sys/stat.h> | 
 | #include <sys/sysmacros.h> | 
 |  | 
 | /* Try to get a machine dependent instruction which will make the | 
 |    program crash.  This is used in case everything else fails.  */ | 
 | #include <abort-instr.h> | 
 | #ifndef ABORT_INSTRUCTION | 
 | /* No such instruction is available.  */ | 
 | # define ABORT_INSTRUCTION | 
 | #endif | 
 |  | 
 | #include <device-nrs.h> | 
 | #include <not-cancel.h> | 
 |  | 
 |  | 
 | /* Should other OSes (e.g., Hurd) have different versions which can | 
 |    be written in a better way?  */ | 
 | static void | 
 | check_one_fd (int fd, int mode) | 
 | { | 
 |   /* Note that fcntl() with this parameter is not a cancellation point.  */ | 
 |   if (__builtin_expect (__libc_fcntl (fd, F_GETFD), 0) == -1 | 
 |       && errno == EBADF) | 
 |     { | 
 |       const char *name; | 
 |       dev_t dev; | 
 |  | 
 |       /* For writable descriptors we use /dev/full.  */ | 
 |       if ((mode & O_ACCMODE) == O_WRONLY) | 
 | 	{ | 
 | 	  name = _PATH_DEV "full"; | 
 | 	  dev = makedev (DEV_FULL_MAJOR, DEV_FULL_MINOR); | 
 | 	} | 
 |       else | 
 | 	{ | 
 | 	  name = _PATH_DEVNULL; | 
 | 	  dev = makedev (DEV_NULL_MAJOR, DEV_NULL_MINOR); | 
 | 	} | 
 |  | 
 |       /* Something is wrong with this descriptor, it's probably not | 
 | 	 opened.  Open /dev/null so that the SUID program we are | 
 | 	 about to start does not accidentally use this descriptor.  */ | 
 |       int nullfd = open_not_cancel (name, mode, 0); | 
 |  | 
 |       /* We are very paranoid here.  With all means we try to ensure | 
 | 	 that we are actually opening the /dev/null device and nothing | 
 | 	 else. | 
 |  | 
 | 	 Note that the following code assumes that STDIN_FILENO, | 
 | 	 STDOUT_FILENO, STDERR_FILENO are the three lowest file | 
 | 	 decsriptor numbers, in this order.  */ | 
 |       struct stat64 st; | 
 |       if (__builtin_expect (nullfd != fd, 0) | 
 | 	  || __builtin_expect (__fxstat64 (_STAT_VER, fd, &st), 0) != 0 | 
 | 	  || __builtin_expect (S_ISCHR (st.st_mode), 1) == 0 | 
 | 	  || st.st_rdev != dev) | 
 | 	/* We cannot even give an error message here since it would | 
 | 	   run into the same problems.  */ | 
 | 	while (1) | 
 | 	  /* Try for ever and ever.  */ | 
 | 	  ABORT_INSTRUCTION; | 
 |     } | 
 | } | 
 |  | 
 |  | 
 | void | 
 | __libc_check_standard_fds (void) | 
 | { | 
 |   /* This is really paranoid but some people actually are.  If /dev/null | 
 |      should happen to be a symlink to somewhere else and not the device | 
 |      commonly known as "/dev/null" we bail out.  We can detect this with | 
 |      the O_NOFOLLOW flag for open() but only on some system.  */ | 
 | #ifndef O_NOFOLLOW | 
 | # define O_NOFOLLOW	0 | 
 | #endif | 
 |   /* Check all three standard file descriptors.  */ | 
 |   check_one_fd (STDIN_FILENO, O_WRONLY | O_NOFOLLOW); | 
 |   check_one_fd (STDOUT_FILENO, O_RDONLY | O_NOFOLLOW); | 
 |   check_one_fd (STDERR_FILENO, O_RDONLY | O_NOFOLLOW); | 
 | } |