diff --git a/auto/unix b/auto/unix index 10835f6c..b5b33bb3 100644 --- a/auto/unix +++ b/auto/unix @@ -990,3 +990,27 @@ ngx_feature_test='struct addrinfo *res; if (getaddrinfo("localhost", NULL, NULL, &res) != 0) return 1; freeaddrinfo(res)' . auto/feature + +ngx_feature="SOCK_CLOEXEC support" +ngx_feature_name="NGX_HAVE_SOCKET_CLOEXEC" +ngx_feature_run=no +ngx_feature_incs="#include <sys/types.h> + #include <sys/socket.h>" +ngx_feature_path= +ngx_feature_libs= +ngx_feature_test="int fd; + fd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);" +. auto/feature + +ngx_feature="FD_CLOEXEC support" +ngx_feature_name="NGX_HAVE_FD_CLOEXEC" +ngx_feature_run=no +ngx_feature_incs="#include <sys/types.h> + #include <sys/socket.h> + #include <fcntl.h>" +ngx_feature_path= +ngx_feature_libs= +ngx_feature_test="int fd; + fd = socket(AF_INET, SOCK_STREAM, 0); + fcntl(fd, F_SETFD, FD_CLOEXEC);" +. auto/feature diff --git a/src/core/ngx_resolver.c b/src/core/ngx_resolver.c index cd55520c..438e0806 100644 --- a/src/core/ngx_resolver.c +++ b/src/core/ngx_resolver.c @@ -4466,8 +4466,14 @@ ngx_tcp_connect(ngx_resolver_connection_t *rec) ngx_event_t *rev, *wev; ngx_connection_t *c; +#if (NGX_HAVE_SOCKET_CLOEXEC) + s = ngx_socket(rec->sockaddr->sa_family, SOCK_STREAM | SOCK_CLOEXEC, 0); + +#else s = ngx_socket(rec->sockaddr->sa_family, SOCK_STREAM, 0); +#endif + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, &rec->log, 0, "TCP socket %d", s); if (s == (ngx_socket_t) -1) { @@ -4494,6 +4500,15 @@ ngx_tcp_connect(ngx_resolver_connection_t *rec) goto failed; } +#if (NGX_HAVE_FD_CLOEXEC) + if (ngx_cloexec(s) == -1) { + ngx_log_error(NGX_LOG_ALERT, &rec->log, ngx_socket_errno, + ngx_cloexec_n " failed"); + + goto failed; + } +#endif + rev = c->read; wev = c->write; diff --git a/src/event/ngx_event.h b/src/event/ngx_event.h index 19fec68..8c2f01a 100644 --- a/src/event/ngx_event.h +++ b/src/event/ngx_event.h @@ -73,6 +73,9 @@ struct ngx_event_s { /* to test on worker exit */ unsigned channel:1; unsigned resolver:1; +#if (HAVE_SOCKET_CLOEXEC_PATCH) + unsigned skip_socket_leak_check:1; +#endif unsigned cancelable:1; diff --git a/src/event/ngx_event_accept.c b/src/event/ngx_event_accept.c index 77563709..5827b9d0 100644 --- a/src/event/ngx_event_accept.c +++ b/src/event/ngx_event_accept.c @@ -62,7 +62,9 @@ ngx_event_accept(ngx_event_t *ev) #if (NGX_HAVE_ACCEPT4) if (use_accept4) { - s = accept4(lc->fd, &sa.sockaddr, &socklen, SOCK_NONBLOCK); + s = accept4(lc->fd, &sa.sockaddr, &socklen, + SOCK_NONBLOCK | SOCK_CLOEXEC); + } else { s = accept(lc->fd, &sa.sockaddr, &socklen); } @@ -202,6 +204,16 @@ ngx_event_accept(ngx_event_t *ev) ngx_close_accepted_connection(c); return; } + +#if (NGX_HAVE_FD_CLOEXEC) + if (ngx_cloexec(s) == -1) { + ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_socket_errno, + ngx_cloexec_n " failed"); + ngx_close_accepted_connection(c); + return; + } +#endif + } } diff --git a/src/event/ngx_event_connect.c b/src/event/ngx_event_connect.c index c5bb8068..cf33b1d2 100644 --- a/src/event/ngx_event_connect.c +++ b/src/event/ngx_event_connect.c @@ -38,8 +38,15 @@ ngx_event_connect_peer(ngx_peer_connection_t *pc) type = (pc->type ? pc->type : SOCK_STREAM); +#if (NGX_HAVE_SOCKET_CLOEXEC) + s = ngx_socket(pc->sockaddr->sa_family, type | SOCK_CLOEXEC, 0); + +#else s = ngx_socket(pc->sockaddr->sa_family, type, 0); +#endif + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, pc->log, 0, "%s socket %d", (type == SOCK_STREAM) ? "stream" : "dgram", s); @@ -80,6 +87,15 @@ ngx_event_connect_peer(ngx_peer_connection_t *pc) goto failed; } +#if (NGX_HAVE_FD_CLOEXEC) + if (ngx_cloexec(s) == -1) { + ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno, + ngx_cloexec_n " failed"); + + goto failed; + } +#endif + if (pc->local) { #if (NGX_HAVE_TRANSPARENT_PROXY) diff --git a/src/os/unix/ngx_process_cycle.c b/src/os/unix/ngx_process_cycle.c index c4376a5..48e8fa8 100644 --- a/src/os/unix/ngx_process_cycle.c +++ b/src/os/unix/ngx_process_cycle.c @@ -1032,6 +1032,9 @@ ngx_worker_process_exit(ngx_cycle_t *cycle) for (i = 0; i < cycle->connection_n; i++) { if (c[i].fd != -1 && c[i].read +#if (HAVE_SOCKET_CLOEXEC_PATCH) + && !c[i].read->skip_socket_leak_check +#endif && !c[i].read->accept && !c[i].read->channel && !c[i].read->resolver) diff --git a/src/os/unix/ngx_socket.h b/src/os/unix/ngx_socket.h index fcc51533..d1eebf47 100644 --- a/src/os/unix/ngx_socket.h +++ b/src/os/unix/ngx_socket.h @@ -38,6 +38,17 @@ int ngx_blocking(ngx_socket_t s); #endif +#if (NGX_HAVE_FD_CLOEXEC) + +#define ngx_cloexec(s) fcntl(s, F_SETFD, FD_CLOEXEC) +#define ngx_cloexec_n "fcntl(FD_CLOEXEC)" + +/* at least FD_CLOEXEC is required to ensure connection fd is closed + * after execve */ +#define HAVE_SOCKET_CLOEXEC_PATCH 1 + +#endif + int ngx_tcp_nopush(ngx_socket_t s); int ngx_tcp_push(ngx_socket_t s);