From nobody Mon Feb 9 17:56:30 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 150818538293220.257292144670487; Mon, 16 Oct 2017 13:23:02 -0700 (PDT) Received: from localhost ([::1]:35053 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1e4BuW-0000ro-JE for importer@patchew.org; Mon, 16 Oct 2017 16:22:52 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:44410) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1e4Boz-0004cF-Tt for qemu-devel@nongnu.org; Mon, 16 Oct 2017 16:17:11 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1e4Boy-0000E9-M6 for qemu-devel@nongnu.org; Mon, 16 Oct 2017 16:17:09 -0400 Received: from mx1.redhat.com ([209.132.183.28]:39944) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1e4Boy-0000Dh-Dx for qemu-devel@nongnu.org; Mon, 16 Oct 2017 16:17:08 -0400 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.phx2.redhat.com [10.5.11.15]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 8A2934E4E6; Mon, 16 Oct 2017 20:17:07 +0000 (UTC) Received: from localhost.localdomain.com (unknown [10.33.36.3]) by smtp.corp.redhat.com (Postfix) with ESMTP id 63F085D6B5; Mon, 16 Oct 2017 20:17:06 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com 8A2934E4E6 Authentication-Results: ext-mx09.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com Authentication-Results: ext-mx09.extmail.prod.ext.phx2.redhat.com; spf=fail smtp.mailfrom=berrange@redhat.com From: "Daniel P. Berrange" To: qemu-devel@nongnu.org Date: Mon, 16 Oct 2017 21:16:42 +0100 Message-Id: <20171016201650.18399-4-berrange@redhat.com> In-Reply-To: <20171016201650.18399-1-berrange@redhat.com> References: <20171016201650.18399-1-berrange@redhat.com> X-Scanned-By: MIMEDefang 2.79 on 10.5.11.15 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.38]); Mon, 16 Oct 2017 20:17:07 +0000 (UTC) X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PULL v1 03/11] sockets: Handle race condition between binds to the same port X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Peter Maydell , Knut Omang Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" From: Knut Omang If an offset of ports is specified to the inet_listen_saddr function(), and two or more processes tries to bind from these ports at the same time, occasionally more than one process may be able to bind to the same port. The condition is detected by listen() but too late to avoid a failure. This function is called by socket_listen() and used by all socket listening code in QEMU, so all cases where any form of dynamic port selection is used should be subject to this issue. Add code to close and re-establish the socket when this condition is observed, hiding the race condition from the user. Also clean up some issues with error handling to allow more accurate reporting of the cause of an error. This has been developed and tested by means of the test-listen unit test in the previous commit. Enable the test for make check now that it passes. Reviewed-by: Bhavesh Davda Reviewed-by: Yuval Shaia Reviewed-by: Girish Moodalbail Signed-off-by: Knut Omang Reviewed-by: Daniel P. Berrange Signed-off-by: Daniel P. Berrange --- util/qemu-sockets.c | 58 +++++++++++++++++++++++++++++++++++++------------= ---- 1 file changed, 41 insertions(+), 17 deletions(-) diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c index 65f6dcd5ef..b47fb45885 100644 --- a/util/qemu-sockets.c +++ b/util/qemu-sockets.c @@ -206,7 +206,10 @@ static int inet_listen_saddr(InetSocketAddress *saddr, char port[33]; char uaddr[INET6_ADDRSTRLEN+1]; char uport[33]; - int slisten, rc, port_min, port_max, p; + int rc, port_min, port_max, p; + int slisten =3D 0; + int saved_errno =3D 0; + bool socket_created =3D false; Error *err =3D NULL; =20 memset(&ai,0, sizeof(ai)); @@ -258,7 +261,7 @@ static int inet_listen_saddr(InetSocketAddress *saddr, return -1; } =20 - /* create socket + bind */ + /* create socket + bind/listen */ for (e =3D res; e !=3D NULL; e =3D e->ai_next) { getnameinfo((struct sockaddr*)e->ai_addr,e->ai_addrlen, uaddr,INET6_ADDRSTRLEN,uport,32, @@ -266,37 +269,58 @@ static int inet_listen_saddr(InetSocketAddress *saddr, =20 slisten =3D create_fast_reuse_socket(e); if (slisten < 0) { - if (!e->ai_next) { - error_setg_errno(errp, errno, "Failed to create socket"); - } continue; } =20 + socket_created =3D true; port_min =3D inet_getport(e); port_max =3D saddr->has_to ? saddr->to + port_offset : port_min; for (p =3D port_min; p <=3D port_max; p++) { inet_setport(e, p); - if (try_bind(slisten, saddr, e) >=3D 0) { - goto listen; - } - if (p =3D=3D port_max) { - if (!e->ai_next) { + rc =3D try_bind(slisten, saddr, e); + if (rc) { + if (errno =3D=3D EADDRINUSE) { + continue; + } else { error_setg_errno(errp, errno, "Failed to bind socket"); + goto listen_failed; } } + if (!listen(slisten, 1)) { + goto listen_ok; + } + if (errno !=3D 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 + * recover from this situation, so we need to recreate the + * socket to allow bind attempts for subsequent ports: + */ + closesocket(slisten); + slisten =3D create_fast_reuse_socket(e); + if (slisten < 0) { + error_setg_errno(errp, errno, + "Failed to recreate failed listening sock= et"); + goto listen_failed; + } } + } + error_setg_errno(errp, errno, + socket_created ? + "Failed to find an available port" : + "Failed to create a socket"); +listen_failed: + saved_errno =3D errno; + if (slisten >=3D 0) { closesocket(slisten); } freeaddrinfo(res); + errno =3D saved_errno; return -1; =20 -listen: - if (listen(slisten,1) !=3D 0) { - error_setg_errno(errp, errno, "Failed to listen on socket"); - closesocket(slisten); - freeaddrinfo(res); - return -1; - } +listen_ok: if (update_addr) { g_free(saddr->host); saddr->host =3D g_strdup(uaddr); --=20 2.13.5