From 79abec90386e5477949ab04e2f80b9970c88b41b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Marczykowski-G=C3=B3recki?= Date: Tue, 1 Jul 2014 03:24:46 +0200 Subject: [PATCH] qrexec: fix deadlock in qrexec-client When VM-VM qrexec service is called, two qrexec-clients are connected in dom0. If both VMs are sending data simultaneously it can happen that both qrexec-client processes will call write(2) and none of them will be reading -> deadlock. Solve it by handling I/O in two separate threads (one for reading from VM, another for writing), at any time qrexec-client is ready to accept data from either direction. --- qrexec/Makefile | 2 +- qrexec/qrexec-client.c | 38 +++++++++++++++++--------------------- 2 files changed, 18 insertions(+), 22 deletions(-) diff --git a/qrexec/Makefile b/qrexec/Makefile index 890f2c5..537b4ed 100644 --- a/qrexec/Makefile +++ b/qrexec/Makefile @@ -7,6 +7,6 @@ all: qrexec-daemon qrexec-client qrexec-daemon: qrexec-daemon.o $(CC) -pie -g -o qrexec-daemon qrexec-daemon.o $(LIBS) qrexec-client: qrexec-client.o - $(CC) -pie -g -o qrexec-client qrexec-client.o $(LIBS) + $(CC) -pie -g -o qrexec-client qrexec-client.o $(LIBS) -lpthread clean: rm -f *.o *~ qrexec-daemon qrexec-client diff --git a/qrexec/qrexec-client.c b/qrexec/qrexec-client.c index b863a2d..fda292d 100644 --- a/qrexec/qrexec-client.c +++ b/qrexec/qrexec-client.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "qrexec.h" #include "libqrexec-utils.h" @@ -225,30 +226,25 @@ void handle_daemon_only_until_writable(int s) } while (!FD_ISSET(s, &wrset)); } +void *input_process_loop(void *arg) { + int s = *(int*)arg; + while (local_stdout_fd != -1) + handle_input(s); + return NULL; +} + + void select_loop(int s) { - fd_set select_set; - int max; - for (;;) { - handle_daemon_only_until_writable(s); - FD_ZERO(&select_set); - FD_SET(s, &select_set); - max = s; - if (local_stdout_fd != -1) { - FD_SET(local_stdout_fd, &select_set); - if (s < local_stdout_fd) - max = local_stdout_fd; - } - if (select(max + 1, &select_set, NULL, NULL, NULL) < 0) { - perror("select"); - do_exit(1); - } - if (FD_ISSET(s, &select_set)) - handle_daemon_data(s); - if (local_stdout_fd != -1 - && FD_ISSET(local_stdout_fd, &select_set)) - handle_input(s); + pthread_t input_thread; + if (pthread_create(&input_thread, NULL, input_process_loop, &s) != 0) { + perror("pthread_create"); + do_exit(1); } + for (;;) { + handle_daemon_data(s); + } + pthread_join(input_thread, NULL); } void usage(const char *name)