FILE * fopen_safer (char const *file, char const *mode) { FILE *fp = fopen (file, mode); if (fp) { int fd = fileno (fp); if (0 <= fd && fd <= STDERR_FILENO) { int f = dup_safer (fd); if (f < 0) { int e = errno; fclose (fp); errno = e; return NULL; } if (fclose (fp) != 0 || ! (fp = fdopen (f, mode))) { int e = errno; close (f); errno = e; return NULL; } } } return fp; }
int main () { int i; DIR *dp; /* The dirent-safer module works without the use of fdopendir (which would also pull in fchdir and openat); but if those modules were also used, we ensure that they are safe. In particular, the gnulib version of fdopendir is unable to guarantee that dirfd(fdopendir(fd))==fd, but we can at least guarantee that if they are not equal, the fd returned by dirfd is safe. */ #if HAVE_FDOPENDIR || GNULIB_FDOPENDIR int dfd; #endif /* We close fd 2 later, so save it in fd 10. */ if (dup2 (STDERR_FILENO, BACKUP_STDERR_FILENO) != BACKUP_STDERR_FILENO || (myerr = fdopen (BACKUP_STDERR_FILENO, "w")) == NULL) return 2; #if HAVE_FDOPENDIR || GNULIB_FDOPENDIR dfd = open (".", O_RDONLY); ASSERT (STDERR_FILENO < dfd); #endif /* Four iterations, with progressively more standard descriptors closed. */ for (i = -1; i <= STDERR_FILENO; i++) { if (0 <= i) ASSERT (close (i) == 0); dp = opendir ("."); ASSERT (dp); ASSERT (dirfd (dp) == -1 || STDERR_FILENO < dirfd (dp)); ASSERT (closedir (dp) == 0); #if HAVE_FDOPENDIR || GNULIB_FDOPENDIR { int fd = dup_safer (dfd); ASSERT (STDERR_FILENO < fd); dp = fdopendir (fd); ASSERT (dp); ASSERT (dirfd (dp) == -1 || STDERR_FILENO < dirfd (dp)); ASSERT (closedir (dp) == 0); errno = 0; ASSERT (close (fd) == -1); ASSERT (errno == EBADF); } #endif } #if HAVE_FDOPENDIR || GNULIB_FDOPENDIR ASSERT (close (dfd) == 0); #endif return 0; }
int fd_safer (int fd) { if (STDIN_FILENO <= fd && fd <= STDERR_FILENO) { int f = dup_safer (fd); int e = errno; close (fd); errno = e; fd = f; } return fd; }
DIR * opendir_safer (char const *name) { DIR *dp = opendir (name); if (dp) { int fd = dirfd (dp); if (0 <= fd && fd <= STDERR_FILENO) { /* If fdopendir is native (as on Linux), then it is safe to assume dirfd(fdopendir(n))==n. If we are using the gnulib module fdopendir, then this guarantee is not met, but fdopendir recursively calls opendir_safer up to 3 times to at least get a safe fd. If fdopendir is not present but dirfd is accurate (as on cygwin 1.5.x), then we recurse up to 3 times ourselves. Finally, if dirfd always fails (as on mingw), then we are already safe. */ DIR *newdp; int e; #if HAVE_FDOPENDIR || GNULIB_FDOPENDIR int f = dup_safer (fd); if (f < 0) { e = errno; newdp = NULL; } else { newdp = fdopendir (f); e = errno; if (! newdp) close (f); } #else /* !FDOPENDIR */ newdp = opendir_safer (name); e = errno; #endif closedir (dp); errno = e; dp = newdp; } } return dp; }
FILE * tmpfile_safer (void) { FILE *fp = tmpfile (); if (fp) { int fd = fileno (fp); if (0 <= fd && fd <= STDERR_FILENO) { int f = dup_safer (fd); if (f < 0) { int e = errno; fclose (fp); errno = e; return NULL; } /* Keep the temporary file in binary mode, on platforms where that matters. */ if (fclose (fp) != 0 || ! (fp = fdopen (f, O_BINARY ? "wb+" : "w+"))) { int e = errno; close (f); errno = e; return NULL; } } } return fp; }