diff --git a/CHANGES b/CHANGES index 7890b3f858..5135511a8e 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,5 @@ +2504. [bug] Address race condition in the socket code. [RT #18899] + 2503. [port] linux: improve compatibility with Linux Standard Base. [RT #18793] diff --git a/lib/isc/unix/socket.c b/lib/isc/unix/socket.c index de2f9ce11e..9475c7eeb4 100644 --- a/lib/isc/unix/socket.c +++ b/lib/isc/unix/socket.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: socket.c,v 1.309 2008/12/01 00:15:37 marka Exp $ */ +/* $Id: socket.c,v 1.310 2008/12/03 02:03:47 marka Exp $ */ /*! \file */ @@ -2360,18 +2360,15 @@ isc_socket_detach(isc_socket_t **socketp) { isc_result_t isc_socket_close(isc_socket_t *sock) { int fd; + isc_socketmgr_t *manager; + isc_sockettype_t type; REQUIRE(VALID_SOCKET(sock)); LOCK(&sock->lock); + REQUIRE(sock->references == 1); REQUIRE(sock->type != isc_sockettype_fdwatch); - UNLOCK(&sock->lock); - /* - * We don't need to retain the lock hereafter, since no one else has - * this socket. - */ - REQUIRE(sock->fd >= 0 && sock->fd < (int)sock->manager->maxsocks); INSIST(!sock->connecting); @@ -2383,6 +2380,8 @@ isc_socket_close(isc_socket_t *sock) { INSIST(ISC_LIST_EMPTY(sock->accept_list)); INSIST(sock->connect_ev == NULL); + manager = sock->manager; + type = sock->type; fd = sock->fd; sock->fd = -1; sock->listener = 0; @@ -2390,8 +2389,9 @@ isc_socket_close(isc_socket_t *sock) { sock->connecting = 0; sock->bound = 0; isc_sockaddr_any(&sock->peer_address); + UNLOCK(&sock->lock); - closesocket(sock->manager, sock->type, fd); + closesocket(manager, type, fd); return (ISC_R_SUCCESS); } @@ -3028,6 +3028,7 @@ process_fd(isc_socketmgr_t *manager, int fd, isc_boolean_t readable, { isc_socket_t *sock; isc_boolean_t unlock_sock; + isc_boolean_t unwatch_read = ISC_FALSE, unwatch_write = ISC_FALSE; int lockid = FDLOCK_ID(fd); /* @@ -3043,28 +3044,27 @@ process_fd(isc_socketmgr_t *manager, int fd, isc_boolean_t readable, } sock = manager->fds[fd]; - UNLOCK(&manager->fdlock[lockid]); unlock_sock = ISC_FALSE; if (readable) { if (sock == NULL) { - (void)unwatch_fd(manager, fd, SELECT_POKE_READ); + unwatch_read = ISC_TRUE; goto check_write; } unlock_sock = ISC_TRUE; - LOCK(&sock->lock); + LOCK(&sock->lock); /* XXXMPA */ if (!SOCK_DEAD(sock)) { if (sock->listener) dispatch_accept(sock); else dispatch_recv(sock); } - (void)unwatch_fd(manager, fd, SELECT_POKE_READ); + unwatch_read = ISC_TRUE; } check_write: if (writeable) { if (sock == NULL) { - (void)unwatch_fd(manager, fd, SELECT_POKE_WRITE); - return; + unwatch_write = ISC_TRUE; + goto unlock_fd; } if (!unlock_sock) { unlock_sock = ISC_TRUE; @@ -3076,10 +3076,18 @@ check_write: else dispatch_send(sock); } - (void)unwatch_fd(manager, fd, SELECT_POKE_WRITE); + unwatch_write = ISC_TRUE; } if (unlock_sock) UNLOCK(&sock->lock); + + unlock_fd: + UNLOCK(&manager->fdlock[lockid]); + if (unwatch_read) + (void)unwatch_fd(manager, fd, SELECT_POKE_READ); + if (unwatch_write) + (void)unwatch_fd(manager, fd, SELECT_POKE_WRITE); + } #ifdef USE_KQUEUE