From fbe250db76b409a99457b47486a09b57677d5108 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 11 Sep 2013 14:59:21 +0200 Subject: httpd: treat errors from stdin correctly. Fron bug report: If a CGI or proxied connection is rudely aborted (SIG_{KILL,BUS,SEGV}) then httpd will spin madly the poll loop in: networking/httpd.c:1080 cgi_io_loop_and_exit() Upon investigation I found that pfd[0].revents == 0x0018 (POLLHUP|POLLERR), which leads to empty read, but the pfd[0].fd (STDIN_FILENO) is left open, and in the FD list given to poll() which immediately returns to once again inform the loop of (POLLHUP|POLLERR) condition of pfd[0].fd. This continues until pfd[FROM_CGI].revents != 0 Signed-off-by: Denys Vlasenko --- networking/httpd.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/networking/httpd.c b/networking/httpd.c index cef9b8baf..143331389 100644 --- a/networking/httpd.c +++ b/networking/httpd.c @@ -1105,16 +1105,21 @@ static NOINLINE void cgi_io_loop_and_exit(int fromCgi_rd, int toCgi_wr, int post /* NB: breaking out of this loop jumps to log_and_exit() */ out_cnt = 0; while (1) { - memset(pfd, 0, sizeof(pfd)); + /* Note: even pfd[0].events == 0 won't prevent + * revents == POLLHUP|POLLERR reports from closed stdin. + * This works: */ + pfd[0].fd = -1; pfd[FROM_CGI].fd = fromCgi_rd; pfd[FROM_CGI].events = POLLIN; - if (toCgi_wr) { - pfd[TO_CGI].fd = toCgi_wr; - if (hdr_cnt > 0) { - pfd[TO_CGI].events = POLLOUT; - } else if (post_len > 0) { + pfd[TO_CGI].fd = toCgi_wr; + pfd[TO_CGI].events = POLLOUT; + + if (toCgi_wr && hdr_cnt <= 0) { + if (post_len > 0) { + /* Expect more POST data from network */ + pfd[0].fd = 0; pfd[0].events = POLLIN; } else { /* post_len <= 0 && hdr_cnt <= 0: @@ -1127,7 +1132,7 @@ static NOINLINE void cgi_io_loop_and_exit(int fromCgi_rd, int toCgi_wr, int post } /* Now wait on the set of sockets */ - count = safe_poll(pfd, toCgi_wr ? TO_CGI+1 : FROM_CGI+1, -1); + count = safe_poll(pfd, hdr_cnt > 0 ? TO_CGI+1 : FROM_CGI+1, -1); if (count <= 0) { #if 0 if (safe_waitpid(pid, &status, WNOHANG) <= 0) { @@ -1144,7 +1149,7 @@ static NOINLINE void cgi_io_loop_and_exit(int fromCgi_rd, int toCgi_wr, int post } if (pfd[TO_CGI].revents) { - /* hdr_cnt > 0 here due to the way pfd[TO_CGI].events set */ + /* hdr_cnt > 0 here due to the way poll() called */ /* Have data from peer and can write to CGI */ count = safe_write(toCgi_wr, hdr_ptr, hdr_cnt); /* Doesn't happen, we dont use nonblocking IO here -- cgit v1.2.3