sockets: avoid leak of listen file descriptor
PATCH v2 1/1 sockets: avoid leak of listen file descriptor
If we iterate over the full port range without successfully binding+listening on the socket, we'll try the next address, whereupon we overwrite the slisten file descriptor variable without closing it.
Rather than having two places where we open + close socket FDs on different iterations of nested for loops, re-arrange the code to always open+close within the same loop iteration.
--- util/qemu-sockets.c | 52 +++++++++++++++++++++++++++++----------------------- 1 file changed, 29 insertions(+), 23 deletions(-)
util/qemu-sockets.c +29 -23
@@ -207,7 +207,7 @@ static int inet_listen_saddr(InetSocketAddress *saddr,char uaddr[INET6_ADDRSTRLEN +1 ];char uport[33 ];int rc, port_min, port_max, p; -int slisten =0 ; +int slisten =-1 ;int saved_errno =0 ;bool socket_created = false;Error *err =NULL ;
@@ -267,31 +267,42 @@ static int inet_listen_saddr(InetSocketAddress *saddr, uaddr,INET6_ADDRSTRLEN ,uport,32 , NI_NUMERICHOST | NI_NUMERICSERV); - slisten =create_fast_reuse_socket (e); -if (slisten <0 ) { -continue ; - } - - socket_created = true; port_min =inet_getport (e); port_max = saddr->has_to ? saddr->to + port_offset : port_min;for (p = port_min; p <= port_max; p++) {inet_setport (e, p); - rc =try_bind (slisten, saddr, e); -if (rc) { -if (errno ==EADDRINUSE ) { + + slisten =create_fast_reuse_socket (e); +if (slisten <0 ) { +/* First time we expect we might fail to create the socket +* eg if 'e' has AF_INET6 but ipv6 kmod is not loaded. +* Later iterations should always succeeed if first iteration +* worked though, so treat that as fatal. +*/ +if (p == port_min) {continue ; }else { -error_setg_errno (errp, errno,"Failed to bind socket" ); +error_setg_errno (errp, errno, +"Failed to recreate failed listening socket" ); goto listen_failed; } } -if (!listen (slisten,1 )) { - goto listen_ok; - } -if (errno !=EADDRINUSE ) { -error_setg_errno (errp, errno,"Failed to listen on socket" ); - goto listen_failed; + socket_created = true; + + rc =try_bind (slisten, saddr, e); +if (rc <0 ) { +if (errno !=EADDRINUSE ) { +error_setg_errno (errp, errno,"Failed to bind socket" ); + goto listen_failed; + } + }else { +if (!listen (slisten,1 )) { + goto listen_ok; + } +if (errno !=EADDRINUSE ) { +error_setg_errno (errp, errno,"Failed to listen on socket" ); + goto listen_failed; + } }/* Someone else managed to bind to the same port and beat us * to listen on it! Socket semantics does not allow us to
@@ -299,12 +310,7 @@ static int inet_listen_saddr(InetSocketAddress *saddr,* socket to allow bind attempts for subsequent ports: */ closesocket (slisten); - slisten =create_fast_reuse_socket (e); -if (slisten <0 ) { -error_setg_errno (errp, errno, -"Failed to recreate failed listening socket" ); - goto listen_failed; - } + slisten =-1 ; } } error_setg_errno(errp, errno,
Signature · 1 line
2.13.6
Quoted 14 lines · Daniel P. Berrange
If we iterate over the full port range without successfully binding+listening on the socket, we'll try the next address, whereupon we overwrite the slisten file descriptor variable without closing it.
Rather than having two places where we open + close socket FDs on different iterations of nested for loops, re-arrange the code to always open+close within the same loop iteration.
Signed-off-by Daniel P. Berrange <[email protected]>--- util/qemu-sockets.c | 52 +++++++++++++++++++++++++++++----------------------- 1 file changed, 29 insertions(+), 23 deletions(-)
util/qemu-sockets.c static int inet_listen_saddr(InetSocketAddress *saddr, · line 279
Quoted 5 lines
util/qemu-sockets.c +5
-0+ slisten =create_fast_reuse_socket (e); +if (slisten <0 ) { +/* First time we expect we might fail to create the socket +* eg if 'e' has AF_INET6 but ipv6 kmod is not loaded. +* Later iterations should always succeeed if first iteration
s/succeeed/succeed/
Signature · 3 lines
Eric Blake, Principal Software Engineer Red Hat, Inc. +1-919-301-3266 Virtualization: qemu.org | libvirt.org