2 tridge@linuxcare.com January 2000
4 This is a sample program that demonstrates a deadlock in sshd when
5 handling large amounts of bi-directional data. This deadlock has
6 been the cause of lots of problems with rsync over ssh. Although the
7 problem only maniifests itself occasionally it makes large rsync
8 over ssh transfers unreliable.
10 Luckily the fix is very easy - tell sshd to use socketpair() instead
11 of pipe(). Just remove the line:
13 from near the bottom of includes.h in the ssh sources.
15 Note that fixing the problem by playing with the write sizes or by
16 using non-blocking writes due to the following bugs in various OSes.
18 - in Linux 2.2 a write to a non-blocking pipe will either return
19 EAGAIN if no space is avaiilable or will block if not. This is fixed
22 - Under IRIX and OSF1 a select on a blocking pipe may return before
23 PIPE_BUF can be written. When a subsequent write of more than 1 byte
24 is done the write blocks.
26 - under some versions of OpenBSD a select on a non-blocking pipe
27 will always return immediately, even when no space is available.
29 The sum of all these factors means it is not possible to write
30 portable code that uses pipes in a way that won't allow deadlocks,
31 unless writes are restricted to a single byte after a select on a
32 blocking pipe. The performance of that solution would be terrible.
37 2) nettest "ssh localhost nettest -s"
38 3) if it blocks then you have hit the deadlock.
47 #include <sys/types.h>
49 #include <sys/socket.h>
52 #define TOTAL_SIZE (10*1024*1024)
55 static int fd_pair(int fd[2])
57 return socketpair(AF_UNIX, SOCK_STREAM, 0, fd);
61 static int write_loop(int fd, char *buf, int size)
66 int n = write(fd, buf, size);
75 static int piped_child(char *command,int *f_in,int *f_out)
79 int from_child_pipe[2];
81 if (fd_pair(to_child_pipe) < 0 ||
82 fd_pair(from_child_pipe) < 0) {
83 fprintf(stderr,"fd_pair: %s\n",strerror(errno));
90 fprintf(stderr,"fork: %s\n",strerror(errno));
95 if (dup2(to_child_pipe[0], STDIN_FILENO) < 0 ||
96 close(to_child_pipe[1]) < 0 ||
97 close(from_child_pipe[0]) < 0 ||
98 dup2(from_child_pipe[1], STDOUT_FILENO) < 0) {
99 fprintf(stderr,"Failed to dup/close : %s\n",strerror(errno));
102 if (to_child_pipe[0] != STDIN_FILENO) close(to_child_pipe[0]);
103 if (from_child_pipe[1] != STDOUT_FILENO) close(from_child_pipe[1]);
108 if (close(from_child_pipe[1]) < 0 ||
109 close(to_child_pipe[0]) < 0) {
110 fprintf(stderr,"Failed to close : %s\n",strerror(errno));
114 *f_in = from_child_pipe[0];
115 *f_out = to_child_pipe[1];
120 static void sender(int fin, int fout)
126 while (total < TOTAL_SIZE) {
127 n = read(fin, buf, sizeof(buf));
129 fprintf(stderr,"write error in sender at %d\n", total);
132 write_loop(fout, buf, n);
134 fprintf(stderr, "-");
136 fprintf(stderr, "sender done\n");
139 static void generator(int fd)
145 while (total < TOTAL_SIZE) {
146 n = 1 + random() % (sizeof(buf)-1);
147 n = write_loop(fd, buf, n);
149 fprintf(stderr,"write error in generator at %d\n", total);
153 fprintf(stderr, "*");
155 fprintf(stderr, "generator done\n");
158 static void receiver(int fd)
164 while (total < TOTAL_SIZE) {
165 n = read(fd, buf, sizeof(buf));
167 fprintf(stderr,"read error in receiver\n");
171 fprintf(stderr, "+");
173 fprintf(stderr, "receiver done\n");
176 int main(int argc, char *argv[])
181 while ((c = getopt(argc, argv, "s")) != -1) {
192 char *command = argv[1];
193 printf("running %s\n", command);
194 piped_child(command, &f_in, &f_out);