Skip to content

Commit 4c24dfe

Browse files
committed
Merge remote-tracking branch 'origin/pr/151'
* origin/pr/151: Ensure that EOF is propagated to stdout Document the file descriptrs for struct process_io_request Prefer close() to shutdown() do_fork_exec(): Drop status pipe Use close_range() instead of close loop fix_fds(): check that input FDs are okay
2 parents 2844865 + c2f197c commit 4c24dfe

File tree

8 files changed

+175
-136
lines changed

8 files changed

+175
-136
lines changed

agent/qrexec-client-vm.c

+26-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
1919
*
2020
*/
21+
#include <assert.h>
2122
#include <sys/socket.h>
2223
#include <sys/wait.h>
2324
#include <sys/un.h>
@@ -94,6 +95,7 @@ static void convert_target_name_keyword(char *target)
9495
enum {
9596
opt_no_filter_stdout = 't'+128,
9697
opt_no_filter_stderr = 'T'+128,
98+
opt_use_stdin_socket = 'u'+128,
9799
};
98100

99101
static struct option longopts[] = {
@@ -104,6 +106,7 @@ static struct option longopts[] = {
104106
{ "no-filter-escape-chars-stderr", no_argument, 0, opt_no_filter_stderr},
105107
{ "agent-socket", required_argument, 0, 'a'},
106108
{ "prefix-data", required_argument, 0, 'p' },
109+
{ "use-stdin-socket", no_argument, 0, opt_use_stdin_socket },
107110
{ "help", no_argument, 0, 'h' },
108111
{ NULL, 0, 0, 0},
109112
};
@@ -141,6 +144,7 @@ int main(int argc, char **argv)
141144
int inpipe[2], outpipe[2];
142145
int buffer_size = 0;
143146
int opt;
147+
int stdout_fd = 1;
144148
const char *agent_trigger_path = QREXEC_AGENT_TRIGGER_PATH, *prefix_data = NULL;
145149

146150
setup_logging("qrexec-client-vm");
@@ -198,6 +202,23 @@ int main(int argc, char **argv)
198202
exit(1);
199203
}
200204
break;
205+
case opt_use_stdin_socket:
206+
{
207+
int type;
208+
if (stdout_fd == 0)
209+
errx(2, "--use-stdin-socket passed twice");
210+
socklen_t len = sizeof(type);
211+
if (getsockopt(0, SOL_SOCKET, SO_TYPE, &type, &len)) {
212+
if (errno == ENOTSOCK)
213+
errx(2, "--use-stdin-socket passed, but stdin not a socket");
214+
err(2, "getsockopt(0, SOL_SOCKET, SO_TYPE)");
215+
}
216+
assert(len == sizeof(type));
217+
if (type != SOCK_STREAM)
218+
errx(2, "stdin was a socket of type %d, not SOCK_STREAM (%d)", type, SOCK_STREAM);
219+
stdout_fd = 0;
220+
}
221+
break;
201222
case '?':
202223
usage(argv[0], 2);
203224
}
@@ -209,6 +230,10 @@ int main(int argc, char **argv)
209230
if (argc - optind > 2) {
210231
start_local_process = 1;
211232
}
233+
if (start_local_process && stdout_fd != 1) {
234+
fprintf(stderr, "cannot spawn a local process with --use-stdin-socket\n");
235+
usage(argv[0], 2);
236+
}
212237

213238
if (!start_local_process) {
214239
if (replace_chars_stdout == -1 && isatty(1))
@@ -303,7 +328,7 @@ int main(int argc, char **argv)
303328
} else {
304329
ret = handle_data_client(MSG_SERVICE_CONNECT,
305330
exec_params.connect_domain, exec_params.connect_port,
306-
1, 0, -1, buffer_size, 0, prefix_data);
331+
stdout_fd, 0, -1, buffer_size, 0, prefix_data);
307332
}
308333

309334
close(trigger_fd);

daemon/qrexec-client.c

+31-3
Original file line numberDiff line numberDiff line change
@@ -72,11 +72,16 @@ static _Noreturn void do_exec(const char *prog, const char *username __attribute
7272
PERROR("exec bash");
7373
exit(1);
7474
}
75+
enum {
76+
opt_socket_dir = 'd'+128,
77+
opt_use_stdin_socket = 'u'+128,
78+
};
7579

7680
static struct option longopts[] = {
7781
{ "help", no_argument, 0, 'h' },
78-
{ "socket-dir", required_argument, 0, 'd'+128 },
82+
{ "socket-dir", required_argument, 0, opt_socket_dir },
7983
{ "no-exit-code", no_argument, 0, 'E' },
84+
{ "use-stdin-socket", no_argument, 0, opt_use_stdin_socket },
8085
{ NULL, 0, 0, 0 },
8186
};
8287

@@ -96,7 +101,8 @@ _Noreturn static void usage(const char *const name)
96101
" -c - connect to existing process (response to trigger service call)\n"
97102
" -w timeout - override default connection timeout of 5s (set 0 for no timeout)\n"
98103
" -k - kill the domain right before exiting\n"
99-
" --socket-dir=PATH - directory for qrexec socket, default: %s\n",
104+
" --socket-dir=PATH - directory for qrexec socket, default: %s\n"
105+
" --use-stdin-socket - use fd 0 (which must be socket) for both stdin and stdout\n",
100106
name ? name : "qrexec-client", QREXEC_DAEMON_SOCKET_DIR);
101107
exit(1);
102108
}
@@ -207,9 +213,26 @@ int main(int argc, char **argv)
207213
case 'W':
208214
wait_connection_end = true;
209215
break;
210-
case 'd' + 128:
216+
case opt_socket_dir:
211217
socket_dir = strdup(optarg);
212218
break;
219+
case opt_use_stdin_socket:
220+
{
221+
int type;
222+
if (local_stdin_fd != 1)
223+
errx(2, "--use-stdin-socket passed twice");
224+
socklen_t len = sizeof(type);
225+
if (getsockopt(0, SOL_SOCKET, SO_TYPE, &type, &len)) {
226+
if (errno == ENOTSOCK)
227+
errx(2, "--use-stdin-socket passed, but stdin not a socket");
228+
err(2, "getsockopt(0, SOL_SOCKET, SO_TYPE)");
229+
}
230+
assert(len == sizeof(type) && "wrong socket option length?");
231+
if (type != SOCK_STREAM)
232+
errx(2, "stdin was a socket of type %d, not SOCK_STREAM (%d)", type, SOCK_STREAM);
233+
local_stdin_fd = 0;
234+
}
235+
break;
213236
case 'k':
214237
kill = true;
215238
break;
@@ -231,6 +254,11 @@ int main(int argc, char **argv)
231254
usage(argv[0]);
232255
}
233256

257+
if ((local_cmdline != NULL) && (local_stdin_fd != 1)) {
258+
fprintf(stderr, "ERROR: at most one of -l and --use-stdin-socket can be specified\n");
259+
usage(argv[0]);
260+
}
261+
234262
if (strcmp(domname, "dom0") == 0 || strcmp(domname, "@adminvm") == 0) {
235263
if (request_id == NULL) {
236264
fprintf(stderr, "ERROR: when target domain is 'dom0', -c must be specified\n");

daemon/qrexec-daemon-common.c

+2-1
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,8 @@ bool send_service_connect(int s, const char *conn_ident,
255255

256256
#define QREXEC_DATA_MIN_VERSION QREXEC_PROTOCOL_V2
257257

258-
static int local_stdin_fd = 1, local_stdout_fd = 0;
258+
static int local_stdout_fd = 0;
259+
int local_stdin_fd = 1;
259260
static pid_t local_pid = 0;
260261

261262
static volatile sig_atomic_t sigchld = 0;

daemon/qrexec-daemon-common.h

+2
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,5 @@ __attribute__((warn_unused_result))
4242
bool qrexec_execute_vm(const char *target, bool autostart, int remote_domain_id,
4343
const char *cmd, size_t service_length, const char *request_id,
4444
bool just_exec, bool wait_connection_end);
45+
/* FD for stdout of remote process */
46+
extern int local_stdin_fd;

libqrexec/exec.c

+24-36
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include <sys/types.h>
3131
#include <sys/socket.h>
3232
#include <sys/stat.h>
33+
#include <sys/syscall.h>
3334
#include <sys/un.h>
3435
#include <sys/wait.h>
3536
#include <netdb.h>
@@ -88,20 +89,27 @@ void exec_qubes_rpc_if_requested(const char *prog, char *const envp[]) {
8889

8990
void fix_fds(int fdin, int fdout, int fderr)
9091
{
91-
int i;
92-
for (i = 3; i < 256; i++)
93-
if (i != fdin && i != fdout && i != fderr)
94-
close(i);
92+
if (fdin < 0 || fdout < 1 || fderr < 2) {
93+
LOG(ERROR, "fix_fds: bad FD numbers: fdin %d, fdout %d, fderr %d",
94+
fdin, fdout, fderr);
95+
_exit(125);
96+
}
9597
if (dup2(fdin, 0) < 0 || dup2(fdout, 1) < 0 || dup2(fderr, 2) < 0) {
9698
PERROR("dup2");
9799
abort();
98100
}
99-
100-
if (close(fdin) || (fdin != fdout && close(fdout)) ||
101-
(fdin != fderr && fdout != fderr && fderr != 2 && close(fderr))) {
102-
PERROR("close");
101+
#ifdef SYS_close_range
102+
int close_range_res = syscall(SYS_close_range, 3, ~0U, 0);
103+
if (close_range_res == 0)
104+
return;
105+
assert(close_range_res == -1);
106+
if (errno != ENOSYS) {
107+
PERROR("close_range");
103108
abort();
104109
}
110+
#endif
111+
for (int i = 3; i < 256; ++i)
112+
close(i);
105113
}
106114

107115
static int do_fork_exec(const char *user,
@@ -111,14 +119,13 @@ static int do_fork_exec(const char *user,
111119
int *stdout_fd,
112120
int *stderr_fd)
113121
{
114-
int inpipe[2], outpipe[2], errpipe[2], statuspipe[2], retval;
122+
int inpipe[2], outpipe[2], errpipe[2], retval;
115123
#ifndef SOCK_CLOEXEC
116124
#define SOCK_CLOEXEC 0
117125
#endif
118-
if (socketpair(AF_UNIX, SOCK_STREAM, 0, inpipe) ||
119-
socketpair(AF_UNIX, SOCK_STREAM, 0, outpipe) ||
120-
(stderr_fd && socketpair(AF_UNIX, SOCK_STREAM, 0, errpipe)) ||
121-
socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, statuspipe)) {
126+
if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, inpipe) ||
127+
socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, outpipe) ||
128+
(stderr_fd && socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, errpipe))) {
122129
PERROR("socketpair");
123130
/* FD leaks do not matter, we exit soon anyway */
124131
return -2;
@@ -128,38 +135,19 @@ static int do_fork_exec(const char *user,
128135
PERROR("fork");
129136
/* ditto */
130137
return -2;
131-
case 0: {
132-
int status;
138+
case 0:
133139
if (signal(SIGPIPE, SIG_DFL) == SIG_ERR)
134140
abort();
135141
if (stderr_fd) {
136142
fix_fds(inpipe[0], outpipe[1], errpipe[1]);
137143
} else
138144
fix_fds(inpipe[0], outpipe[1], 2);
139145

140-
close(statuspipe[0]);
141-
if (SOCK_CLOEXEC == (0)) {
142-
status = fcntl(statuspipe[1], F_GETFD);
143-
fcntl(statuspipe[1], F_SETFD, status | FD_CLOEXEC);
144-
}
145146
if (exec_func != NULL)
146147
exec_func(cmdline, user);
147-
else
148-
abort();
149-
status = -1;
150-
while (write(statuspipe[1], &status, sizeof status) <= 0) {}
151-
_exit(-1);
152-
}
153-
default: {
154-
close(statuspipe[1]);
155-
if (read(statuspipe[0], &retval, sizeof retval) == sizeof retval) {
156-
siginfo_t siginfo;
157-
memset(&siginfo, 0, sizeof siginfo);
158-
waitid(P_PID, *pid, &siginfo, WEXITED); // discard result
159-
} else {
160-
retval = 0;
161-
}
162-
}
148+
abort();
149+
default:
150+
retval = 0;
163151
}
164152
close(inpipe[0]);
165153
close(outpipe[1]);

libqrexec/libqrexec-utils.h

+5
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,9 @@ struct process_io_request {
267267
libvchan_t *vchan;
268268
struct buffer *stdin_buf;
269269

270+
/* Note that stdin_fd, stdout_fd, and stderr_fd are named assuming a
271+
* *local* process. For a *remote* process, stdin_fd is the standard
272+
* *output*, stdout_fd is the standard *input*, and stderr_fd must be -1. */
270273
// stderr_fd can be -1
271274
int stdin_fd, stdout_fd, stderr_fd;
272275
// 0 if no child process
@@ -275,12 +278,14 @@ struct process_io_request {
275278
/*
276279
is_service true (this is a service):
277280
- send local data as MSG_DATA_STDOUT
281+
- send local stderr as MSG_DATA_STDERR, unless in dom0
278282
- send exit code
279283
- wait just for local end
280284
- return local exit code
281285
282286
is_service false (this is a client):
283287
- send local data as MSG_DATA_STDIN
288+
- stderr_fd is always -1
284289
- don't send exit code
285290
- wait for local and remote end
286291
- return remote exit code

0 commit comments

Comments
 (0)