1 /* Test that dup_safer leaves standard fds alone.
2    Copyright (C) 2009-2020 Free Software Foundation, Inc.
3 
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 3 of the License, or
7    (at your option) any later version.
8 
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
16 
17 /* Written by Eric Blake <ebb9@byu.net>, 2009.  */
18 
19 #include <config.h>
20 
21 #include "unistd--.h"
22 
23 #include <fcntl.h>
24 #include <errno.h>
25 #include <stdbool.h>
26 #include <stdio.h>
27 #include <unistd.h>
28 
29 #include "binary-io.h"
30 #include "cloexec.h"
31 
32 #if defined _WIN32 && ! defined __CYGWIN__
33 /* Get declarations of the native Windows API functions.  */
34 # define WIN32_LEAN_AND_MEAN
35 # include <windows.h>
36 /* Get _get_osfhandle.  */
37 # if GNULIB_MSVC_NOTHROW
38 #  include "msvc-nothrow.h"
39 # else
40 #  include <io.h>
41 # endif
42 #endif
43 
44 #if !O_BINARY
45 # define setmode(f,m) zero ()
zero(void)46 static int zero (void) { return 0; }
47 #endif
48 
49 /* This test intentionally closes stderr.  So, we arrange to have fd 10
50    (outside the range of interesting fd's during the test) set up to
51    duplicate the original stderr.  */
52 
53 #define BACKUP_STDERR_FILENO 10
54 #define ASSERT_STREAM myerr
55 #include "macros.h"
56 
57 static FILE *myerr;
58 
59 /* Return true if FD is open.  */
60 static bool
is_open(int fd)61 is_open (int fd)
62 {
63 #if defined _WIN32 && ! defined __CYGWIN__
64   /* On native Windows, the initial state of unassigned standard file
65      descriptors is that they are open but point to an
66      INVALID_HANDLE_VALUE, and there is no fcntl.  */
67   return (HANDLE) _get_osfhandle (fd) != INVALID_HANDLE_VALUE;
68 #else
69 # ifndef F_GETFL
70 #  error Please port fcntl to your platform
71 # endif
72   return 0 <= fcntl (fd, F_GETFL);
73 #endif
74 }
75 
76 /* Return true if FD is open and inheritable across exec/spawn.  */
77 static bool
is_inheritable(int fd)78 is_inheritable (int fd)
79 {
80 #if defined _WIN32 && ! defined __CYGWIN__
81   /* On native Windows, the initial state of unassigned standard file
82      descriptors is that they are open but point to an
83      INVALID_HANDLE_VALUE, and there is no fcntl.  */
84   HANDLE h = (HANDLE) _get_osfhandle (fd);
85   DWORD flags;
86   if (h == INVALID_HANDLE_VALUE || GetHandleInformation (h, &flags) == 0)
87     return 0;
88   return (flags & HANDLE_FLAG_INHERIT) != 0;
89 #else
90 # ifndef F_GETFD
91 #  error Please port fcntl to your platform
92 # endif
93   int i = fcntl (fd, F_GETFD);
94   return 0 <= i && (i & FD_CLOEXEC) == 0;
95 #endif
96 }
97 
98 /* Return true if FD is open in the given MODE, which is either
99    O_TEXT or O_BINARY.  */
100 static bool
is_mode(int fd,int mode)101 is_mode (int fd, int mode)
102 {
103   int value = setmode (fd, O_BINARY);
104   setmode (fd, value);
105   return mode == value;
106 }
107 
108 #define witness "test-dup-safer.txt"
109 
110 int
main(void)111 main (void)
112 {
113   int i;
114   int fd;
115   int bad_fd = getdtablesize ();
116 
117   /* We close fd 2 later, so save it in fd 10.  */
118   if (dup2 (STDERR_FILENO, BACKUP_STDERR_FILENO) != BACKUP_STDERR_FILENO
119       || (myerr = fdopen (BACKUP_STDERR_FILENO, "w")) == NULL)
120     return 2;
121 
122   /* Create file for later checks.  */
123   fd = creat (witness, 0600);
124   ASSERT (STDERR_FILENO < fd);
125 
126   /* Four iterations, with progressively more standard descriptors
127      closed.  */
128   for (i = -1; i <= STDERR_FILENO; i++)
129     {
130       if (0 <= i)
131         ASSERT (close (i) == 0);
132 
133       /* Detect errors.  */
134       errno = 0;
135       ASSERT (dup (-1) == -1);
136       ASSERT (errno == EBADF);
137       errno = 0;
138       ASSERT (dup (bad_fd) == -1);
139       ASSERT (errno == EBADF);
140       close (fd + 1);
141       errno = 0;
142       ASSERT (dup (fd + 1) == -1);
143       ASSERT (errno == EBADF);
144 
145       /* Preserve text vs. binary.  */
146       setmode (fd, O_BINARY);
147       ASSERT (dup (fd) == fd + 1);
148       ASSERT (is_open (fd + 1));
149       ASSERT (is_inheritable (fd + 1));
150       ASSERT (is_mode (fd + 1, O_BINARY));
151 
152       ASSERT (close (fd + 1) == 0);
153       setmode (fd, O_TEXT);
154       ASSERT (dup (fd) == fd + 1);
155       ASSERT (is_open (fd + 1));
156       ASSERT (is_inheritable (fd + 1));
157       ASSERT (is_mode (fd + 1, O_TEXT));
158 
159       /* Create cloexec copy.  */
160       ASSERT (close (fd + 1) == 0);
161       ASSERT (fd_safer_flag (dup_cloexec (fd), O_CLOEXEC) == fd + 1);
162       ASSERT (set_cloexec_flag (fd + 1, true) == 0);
163       ASSERT (is_open (fd + 1));
164       ASSERT (!is_inheritable (fd + 1));
165       ASSERT (close (fd) == 0);
166 
167       /* dup always creates inheritable copies.  Also, check that
168          earliest slot past std fds is used.  */
169       ASSERT (dup (fd + 1) == fd);
170       ASSERT (is_open (fd));
171       ASSERT (is_inheritable (fd));
172       ASSERT (close (fd + 1) == 0);
173     }
174 
175   /* Cleanup.  */
176   ASSERT (close (fd) == 0);
177   ASSERT (unlink (witness) == 0);
178 
179   return 0;
180 }
181