diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2013-09-11 14:59:21 +0200 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2013-09-11 14:59:21 +0200 |
commit | fbe250db76b409a99457b47486a09b57677d5108 (patch) | |
tree | 1b222f195c0947502c46d375e35f549eb4cbe9b0 | |
parent | b5352078a7eb16c33404a8b079aaa9c2b994546c (diff) | |
download | busybox-fbe250db76b409a99457b47486a09b57677d5108.tar.gz |
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 <vda.linux@googlemail.com>
-rw-r--r-- | networking/httpd.c | 21 |
1 files 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 |