unix: set close-on-exec flag on received fds

Set the close-on-exec flag on file descriptors that we've received with
recvmsg() so we don't leak them when calling fork() afterwards.

On Linux, we use the MSG_CMSG_CLOEXEC flag when supported (2.6.23 and
up.)

On older Linux versions and other platforms, we walk the received file
descriptors and set the close-on-exec flag for each fd manually.  That
won't entirely avoid race conditions when other threads call fork() or
clone() but at least we're less likely to leak file descriptors now.
This commit is contained in:
Ben Noordhuis 2013-11-15 23:00:27 +01:00
parent 86b5c1ef1c
commit e9f75fb146
3 changed files with 40 additions and 1 deletions

View File

@ -542,6 +542,44 @@ int uv__dup(int fd) {
}
ssize_t uv__recvmsg(int fd, struct msghdr* msg, int flags) {
struct cmsghdr* cmsg;
ssize_t rc;
int* pfd;
int* end;
#if defined(__linux__)
static int no_msg_cmsg_cloexec;
if (no_msg_cmsg_cloexec == 0) {
rc = recvmsg(fd, msg, flags | 0x40000000); /* MSG_CMSG_CLOEXEC */
if (rc != -1)
return rc;
if (errno != EINVAL)
return -errno;
rc = recvmsg(fd, msg, flags);
if (rc == -1)
return -errno;
no_msg_cmsg_cloexec = 1;
} else {
rc = recvmsg(fd, msg, flags);
}
#else
rc = recvmsg(fd, msg, flags);
#endif
if (rc == -1)
return -errno;
if (msg->msg_controllen == 0)
return rc;
for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; cmsg = CMSG_NXTHDR(msg, cmsg))
if (cmsg->cmsg_type == SCM_RIGHTS)
for (pfd = (int*) CMSG_DATA(cmsg),
end = (int*) ((char*) cmsg + cmsg->cmsg_len);
pfd < end;
pfd += 1)
uv__cloexec(*pfd, 1);
return rc;
}
int uv_cwd(char* buffer, size_t size) {
if (buffer == NULL)
return -EINVAL;

View File

@ -139,6 +139,7 @@ int uv__close(int fd);
int uv__cloexec(int fd, int set);
int uv__socket(int domain, int type, int protocol);
int uv__dup(int fd);
ssize_t uv__recvmsg(int fd, struct msghdr *msg, int flags);
void uv__make_close_pending(uv_handle_t* handle);
void uv__io_init(uv__io_t* w, uv__io_cb cb, int fd);

View File

@ -1007,7 +1007,7 @@ static void uv__read(uv_stream_t* stream) {
msg.msg_control = (void*) cmsg_space;
do {
nread = recvmsg(uv__stream_fd(stream), &msg, 0);
nread = uv__recvmsg(uv__stream_fd(stream), &msg, 0);
}
while (nread < 0 && errno == EINTR);
}