@@ -27,7 +27,7 @@
#include <abort-instr.h>
#ifndef ABORT_INSTRUCTION
/* No such instruction is available. */
-# define ABORT_INSTRUCTION
+# define ABORT_INSTRUCTION _exit (127)
#endif
#include <device-nrs.h>
@@ -37,61 +37,65 @@
/* 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)
+check_one_fd (int fd)
{
- /* 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;
+ if (__glibc_likely (__fcntl_nocancel (fd, F_GETFD) >= 0) || errno != EBADF)
+ return;
- /* For writable descriptors we use /dev/full. */
- if ((mode & O_ACCMODE) == O_WRONLY)
- {
- name = _PATH_DEV "full";
- dev = __gnu_dev_makedev (DEV_FULL_MAJOR, DEV_FULL_MINOR);
- }
- else
- {
- name = _PATH_DEVNULL;
- dev = __gnu_dev_makedev (DEV_NULL_MAJOR, DEV_NULL_MINOR);
- }
+ /* One of the standard file descriptors is not open. Open a
+ harmless file, so that the SUID program we are about to start
+ does not accidentally use this descriptor for something else. */
+ const char *name;
+ dev_t dev;
+ int mode;
- /* 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_nocancel (name, mode, 0);
+ /* For stdin, simply open /dev/null for reading; this is most likely
+ to behave as programs expect (no input available). For stdout
+ and stderr, open /dev/full for writing, which will cause all
+ writes to fail with ENOSPC. */
+ if (fd == STDIN_FILENO)
+ {
+ mode = O_RDONLY;
+ name = _PATH_DEVNULL;
+ dev = __gnu_dev_makedev (DEV_NULL_MAJOR, DEV_NULL_MINOR);
+ }
+ else
+ {
+ mode = O_WRONLY;
+ name = _PATH_DEV "full";
+ dev = __gnu_dev_makedev (DEV_FULL_MAJOR, DEV_FULL_MINOR);
+ }
- /* We are very paranoid here. With all means we try to ensure
- that we are actually opening the /dev/null device and nothing
- else.
+ /* Because STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO are the three
+ lowest file descriptor numbers, in that order, and they are being
+ checked in that order (see below), this should return the
+ descriptor that we just found was not open. For extra paranoia,
+ use O_NOFOLLOW so that we will not open /dev/null or /dev/full if
+ it has been replaced with a symlink to somewhere else. */
+ int nfd = __open64_nocancel (name, mode | O_NOFOLLOW, 0);
- 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;
- }
+ /* Verify that we got the correct descriptor number, and that what
+ we just opened is really the device we wanted to open. */
+ struct stat64 st;
+ if (__glibc_likely (nfd == fd
+ && __fxstat64 (_STAT_VER, fd, &st) == 0
+ && S_ISCHR (st.st_mode)
+ && st.st_rdev == dev))
+ return;
+
+ /* There is no safe way to report an error when stderr might be
+ closed. Crash the program. */
+ while (1)
+ /* Try for ever and ever. */
+ ABORT_INSTRUCTION;
}
void
__libc_check_standard_fds (void)
{
- /* Check all three standard file descriptors. The O_NOFOLLOW flag
- 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. */
- 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);
+ /* Check all three standard file descriptors. */
+ check_one_fd (STDIN_FILENO);
+ check_one_fd (STDOUT_FILENO);
+ check_one_fd (STDERR_FILENO);
}