|  | /* 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); | 
|  | } |