@@ -43,24 +43,31 @@ static _Noreturn void handle_vchan_error(const char *op)
43
43
/*
44
44
* Closing the file descriptors:
45
45
*
46
- * - If this is a single socket used for both stdin and stdout, call shutdown()
47
- * to indicate that no more data will be sent or received.
48
- * - Otherwise, restore the blocking status of the FD.
46
+ * - If this file descriptor was inherited _and_ is not used for both stdin
47
+ * and stdout, restore the blocking status of the FD and then close it.
48
+ * - Otherwise, call shutdown() to indicate that no more data will be sent or
49
+ * received in the given direction.
49
50
*/
50
- static void close_stdio (int fd , int other_fd , int direction ) {
51
+ static bool close_stdio (int fd , int direction , bool single_socket ) {
51
52
if (fd == -1 )
52
- return ;
53
- if (fd == other_fd ) {
54
- /* Do not close the FD, as it is still in use. */
55
- /* ENOTCONN can happen with TCP and is harmless */
56
- if (shutdown (fd , direction ) == -1 && errno != ENOTSOCK && errno != ENOTCONN )
57
- PERROR ("shutdown" );
58
- return ;
59
- }
60
-
61
- if (fd < 3 )
53
+ return false;
54
+ if (single_socket || fd > 2 ) {
55
+ if (shutdown (fd , direction ) == -1 ) {
56
+ if (errno == ENOTSOCK && !single_socket ) {
57
+ close (fd );
58
+ return true;
59
+ } else if (errno != ENOTCONN ) {
60
+ // ENOTCONN can happen with TCP and is harmless
61
+ PERROR ("shutdown" );
62
+ }
63
+ }
64
+ return false;
65
+ } else {
66
+ /* Close the file descriptor */
62
67
set_block (fd );
63
- close (fd );
68
+ close (fd );
69
+ return true;
70
+ }
64
71
}
65
72
66
73
static void close_stderr (int fd ) {
@@ -105,6 +112,10 @@ int qrexec_process_io(const struct process_io_request *req,
105
112
106
113
pid_t local_status = -1 ;
107
114
pid_t remote_status = -1 ;
115
+ /* Saved version of stdin_fd. If we get SIGHUP,
116
+ * this replaces stdout_fd. Set to -1 only if stdin_fd
117
+ * is closed, not merely shut down. */
118
+ int saved_stdin_fd = stdin_fd ;
108
119
int stdout_msg_type = is_service ? MSG_DATA_STDOUT : MSG_DATA_STDIN ;
109
120
int ret ;
110
121
struct pollfd fds [FD_NUM ];
@@ -125,8 +136,17 @@ int qrexec_process_io(const struct process_io_request *req,
125
136
sigprocmask (SIG_BLOCK , & pollmask , NULL );
126
137
sigemptyset (& pollmask );
127
138
139
+ /* Invariants:
140
+ *
141
+ * - if stdin_fd == stdout_fd, then use_stdio_socket is true.
142
+ * - if use_stdio_socket is true, then either:
143
+ * - stdin_fd == stdout_fd right now.
144
+ * - stdin_fd == stdout_fd at some point in the past,
145
+ * and one of them is currently -1.
146
+ */
147
+ bool use_stdio_socket = stdin_fd == stdout_fd ;
128
148
set_nonblock (stdin_fd );
129
- if (stdout_fd != stdin_fd )
149
+ if (! use_stdio_socket )
130
150
set_nonblock (stdout_fd );
131
151
if (is_service && local_pid == 0 ) {
132
152
assert (stdin_fd == stdout_fd );
@@ -142,27 +162,30 @@ int qrexec_process_io(const struct process_io_request *req,
142
162
}
143
163
144
164
/* Convenience macros that eliminate a ton of error-prone boilerplate */
145
- #define close_stdin () do { \
146
- if (exit_on_stdin_eof) { \
147
- /* Set stdin_fd and stdout_fd to -1. \
148
- * No need to close them as the process \
149
- * will soon exit. */ \
150
- stdin_fd = stdout_fd = -1; \
151
- } else { \
152
- close_stdio(stdin_fd, stdout_fd, SHUT_WR); \
153
- stdin_fd = -1; \
154
- } \
165
+ #define close_stdin () do { \
166
+ if (exit_on_stdin_eof) { \
167
+ /* Set stdin_fd and stdout_fd to -1. \
168
+ * No need to close them as the process \
169
+ * will soon exit. */ \
170
+ stdin_fd = stdout_fd = -1; \
171
+ } else { \
172
+ /* if stdin_fd was actually closed, set saved_stdin_fd \
173
+ * to -1 to avoid use-after-close */ \
174
+ if (close_stdio(stdin_fd, SHUT_WR, use_stdio_socket)) \
175
+ saved_stdin_fd = -1; \
176
+ stdin_fd = -1; \
177
+ } \
155
178
} while (0)
156
- #define close_stdout () do { \
157
- if (exit_on_stdout_eof) { \
158
- /* Set stdin_fd and stdout_fd to -1. \
159
- * No need to close them as the process \
160
- * will soon exit. */ \
161
- stdin_fd = stdout_fd = -1; \
162
- } else { \
163
- close_stdio(stdout_fd, stdin_fd, SHUT_RD); \
164
- stdout_fd = -1; \
165
- } \
179
+ #define close_stdout () do { \
180
+ if (exit_on_stdout_eof) { \
181
+ /* Set stdin_fd and stdout_fd to -1. \
182
+ * No need to close them as the process \
183
+ * will soon exit. */ \
184
+ stdin_fd = stdout_fd = -1; \
185
+ } else { \
186
+ close_stdio(stdout_fd, SHUT_RD, use_stdio_socket); \
187
+ stdout_fd = -1; \
188
+ } \
166
189
} while (0)
167
190
#pragma GCC poison close_stdio
168
191
@@ -218,9 +241,10 @@ int qrexec_process_io(const struct process_io_request *req,
218
241
}
219
242
220
243
/* child signaled desire to use single socket for both stdin and stdout */
221
- if (sigusr1 && * sigusr1 && stdin_fd != stdout_fd ) {
244
+ if (sigusr1 && * sigusr1 && ! use_stdio_socket ) {
222
245
close_stdout ();
223
- stdout_fd = stdin_fd ;
246
+ stdout_fd = saved_stdin_fd ;
247
+ use_stdio_socket = true;
224
248
* sigusr1 = 0 ;
225
249
}
226
250
0 commit comments