1 /* Invoke freopen, but avoid some glitches. 2 3 Copyright (C) 2009-2018 Free Software Foundation, Inc. 4 5 This program is free software: you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published by 7 the Free Software Foundation; either version 3 of the License, or 8 (at your option) any later version. 9 10 This program is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with this program. If not, see <https://www.gnu.org/licenses/>. */ 17 18 /* Written by Eric Blake. */ 19 20 #include <config.h> 21 22 #include "stdio-safer.h" 23 24 #include <errno.h> 25 #include <fcntl.h> 26 #include <stdbool.h> 27 #include <unistd.h> 28 29 #ifndef FALLTHROUGH 30 # if __GNUC__ < 7 31 # define FALLTHROUGH ((void) 0) 32 # else 33 # define FALLTHROUGH __attribute__ ((__fallthrough__)) 34 # endif 35 #endif 36 37 /* Guarantee that FD is open; all smaller FDs must already be open. 38 Return true if successful. */ 39 static bool 40 protect_fd (int fd) 41 { 42 int value = open ("/dev/null", O_RDONLY); 43 if (value != fd) 44 { 45 if (0 <= value) 46 { 47 close (value); 48 errno = EBADF; /* Unexpected; this is as good as anything else. */ 49 } 50 return false; 51 } 52 return true; 53 } 54 55 /* Like freopen, but guarantee that reopening stdin, stdout, or stderr 56 preserves the invariant that STDxxx_FILENO==fileno(stdxxx), and 57 that no other stream will interfere with the standard streams. 58 This is necessary because most freopen implementations will change 59 the associated fd of a stream to the lowest available slot. */ 60 61 FILE * 62 freopen_safer (char const *name, char const *mode, FILE *f) 63 { 64 /* Unfortunately, we cannot use the fopen_safer approach of using 65 fdopen (dup_safer (fileno (freopen (cmd, mode, f)))), because we 66 need to return f itself. The implementation of freopen(NULL,m,f) 67 is system-dependent, so the best we can do is guarantee that all 68 lower-valued standard fds are open prior to the freopen call, 69 even though this puts more pressure on open fds. */ 70 bool protect_in = false; 71 bool protect_out = false; 72 bool protect_err = false; 73 int saved_errno; 74 75 switch (fileno (f)) 76 { 77 default: /* -1 or not a standard stream. */ 78 if (dup2 (STDERR_FILENO, STDERR_FILENO) != STDERR_FILENO) 79 protect_err = true; 80 FALLTHROUGH; 81 case STDERR_FILENO: 82 if (dup2 (STDOUT_FILENO, STDOUT_FILENO) != STDOUT_FILENO) 83 protect_out = true; 84 FALLTHROUGH; 85 case STDOUT_FILENO: 86 if (dup2 (STDIN_FILENO, STDIN_FILENO) != STDIN_FILENO) 87 protect_in = true; 88 FALLTHROUGH; 89 case STDIN_FILENO: 90 /* Nothing left to protect. */ 91 break; 92 } 93 if (protect_in && !protect_fd (STDIN_FILENO)) 94 f = NULL; 95 else if (protect_out && !protect_fd (STDOUT_FILENO)) 96 f = NULL; 97 else if (protect_err && !protect_fd (STDERR_FILENO)) 98 f = NULL; 99 else 100 f = freopen (name, mode, f); 101 saved_errno = errno; 102 if (protect_err) 103 close (STDERR_FILENO); 104 if (protect_out) 105 close (STDOUT_FILENO); 106 if (protect_in) 107 close (STDIN_FILENO); 108 if (!f) 109 errno = saved_errno; 110 return f; 111 } 112