From ccd9717ba5d501b45fda957f0ea41c4660ef414c Mon Sep 17 00:00:00 2001 From: Jo-Philipp Wich Date: Wed, 24 Jan 2018 21:02:46 +0100 Subject: [PATCH] proc: avoid stdio deadlocks When a request handler accepting post data is too slow in consuming stdin, uhttpd might deadlock with the master process stuck in a blocking write() to the child and the child stuck with a blocking write() to the master. Avoid this issue by putting the master side write end of the child pipe into nonblocking mode right away and by raising the data_blocked flag when attempts to write to the child yield EAGAIN. Setting the flag ensures that client_poll_post_data() does not immediately trigger a write attempt again, which effectively yields the master write cycle so that the relay ustream has a chance to consume output of the client process, thus solving the deadlock. Signed-off-by: Jo-Philipp Wich --- proc.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/proc.c b/proc.c index e360897..edfcc8f 100644 --- a/proc.c +++ b/proc.c @@ -265,6 +265,7 @@ static void proc_write_cb(struct uloop_fd *fd, unsigned int events) struct client *cl = container_of(fd, struct client, dispatch.proc.wrfd); client_poll_post_data(cl); + cl->dispatch.data_blocked = false; } static void proc_relay_write_cb(struct client *cl) @@ -291,8 +292,10 @@ static int proc_data_send(struct client *cl, const char *data, int len) if (errno == EINTR) continue; - if (errno == EAGAIN || errno == EWOULDBLOCK) + if (errno == EAGAIN || errno == EWOULDBLOCK) { + cl->dispatch.data_blocked = true; break; + } /* consume all data */ ret = len; @@ -366,6 +369,7 @@ bool uh_create_process(struct client *cl, struct path_info *pi, char *url, proc->wrfd.fd = wfd[1]; uh_relay_open(cl, &proc->r, rfd[0], pid); + uloop_fd_add(&proc->wrfd, ULOOP_WRITE); d->free = proc_free; d->close_fds = proc_close_fds; -- 2.11.0