From patchwork Tue Jan 19 16:36:43 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eric Dumazet X-Patchwork-Id: 570066 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 4F5F914030F for ; Wed, 20 Jan 2016 03:36:52 +1100 (AEDT) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b=DvSAqzqq; dkim-atps=neutral Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1755652AbcASQgs (ORCPT ); Tue, 19 Jan 2016 11:36:48 -0500 Received: from mail-pf0-f173.google.com ([209.85.192.173]:35647 "EHLO mail-pf0-f173.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754881AbcASQgp (ORCPT ); Tue, 19 Jan 2016 11:36:45 -0500 Received: by mail-pf0-f173.google.com with SMTP id 65so177902619pff.2; Tue, 19 Jan 2016 08:36:44 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=message-id:subject:from:to:cc:date:in-reply-to:references :content-type:mime-version:content-transfer-encoding; bh=wfoxCDujKxWYanowXH1AIyF1fG8t1VjAW+1Vtp8wOe0=; b=DvSAqzqq2In0SQG9SM7BmMR8AZQlFXu8vbr1CLLtb6sKS5/3cJbtIyWkE+O63qYAvP xYkJvjdbMc4L2YC7wbH2yRuGONAiRzlQZHNp0lBWtKfwJL777j/isgJD58fS5H/G9jdS Se1aixv8KEBDc2GSkb2dpDmTrRskPDIEGTpJrMp2XNi9gyXHazXDWnmxVkHthgPvgybH zHRHzKgVzoKDoeIQW5zS8dy6yEgakaRe9iAiCtaPidfAhmhriYYD5iKhhrjyaQsoFyNz FpGdZiETxp37pvwd7atYZXCmYWYwaohOmfGZ+yaKfZMpctQP9gLlDOLRx45krEuARgCu d78Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:message-id:subject:from:to:cc:date:in-reply-to :references:content-type:mime-version:content-transfer-encoding; bh=wfoxCDujKxWYanowXH1AIyF1fG8t1VjAW+1Vtp8wOe0=; b=gX7PcP7ZiFS628qFHBKcz9ELk4OP6hKPU77yZqtCRz+sP72KhIdXxZtr0ykV0hFRfl SaYPRq3bmF+igibiKs1P9v/GsudyKNaIxSG66sONI52jG+LNZ285gVUatCFMPCNmQ1om 1uEVZMUIRaRUjXr0Z4gfEIjic8r3qub7tFEW/0ZQJ+w8nhuec+wRBV/MrC5WH4BJ00Wy ExzjTIjIAg5XurvN4dA2+ChVDlxowdScMZF1opPoLXo2yPllNbocHee4ei6v9t1x5rAm 1YRxQT6ecWVd3GIOqrC+WS7tgwI4gtp9XAQnLgWFyr/uM/fioDMqKhbfASE1Uv79FcDG DunA== X-Gm-Message-State: ALoCoQk2blnHd0dKbSTc68lbQF8ADXwfqLg5VqAWKifd9BqGGrmehMsDTkOxPz/+eXFsiIT7Z8G/KBKGnZOdyQu0Gn8J2iKi6Q== X-Received: by 10.98.72.136 with SMTP id q8mr45779055pfi.86.1453221404642; Tue, 19 Jan 2016 08:36:44 -0800 (PST) Received: from [172.19.246.212] ([172.19.246.212]) by smtp.gmail.com with ESMTPSA id n27sm42640217pfb.53.2016.01.19.08.36.43 (version=TLSv1/SSLv3 cipher=OTHER); Tue, 19 Jan 2016 08:36:43 -0800 (PST) Message-ID: <1453221403.1223.266.camel@edumazet-glaptop2.roam.corp.google.com> Subject: [PATCH net] udp: fix potential infinite loop in SO_REUSEPORT logic From: Eric Dumazet To: Craig Gallek Cc: Dmitry Vyukov , "David S. Miller" , netdev , LKML Date: Tue, 19 Jan 2016 08:36:43 -0800 In-Reply-To: References: <1453170024.1223.251.camel@edumazet-glaptop2.roam.corp.google.com> <1453171769.1223.255.camel@edumazet-glaptop2.roam.corp.google.com> X-Mailer: Evolution 3.10.4-0ubuntu2 Mime-Version: 1.0 Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org From: Eric Dumazet Using a combination of connected and un-connected sockets, Dmitry was able to trigger soft lockups with his fuzzer. The problem is that sockets in the SO_REUSEPORT array might have different scores. Right after sk2=socket(), setsockopt(sk2,...,SO_REUSEPORT, on) and bind(sk2, ...), but _before_ the connect(sk2) is done, sk2 is added into the soreuseport array, with a score which is smaller than the score of first socket sk1 found in hash table (I am speaking of the regular UDP hash table), if sk1 had the connect() done, giving a +8 to its score. hash bucket [X] -> sk1 -> sk2 -> NULL sk1 score = 14 (because it did a connect()) sk2 score = 6 SO_REUSEPORT fast selection is an optimization. If it turns out the score of the selected socket does not match score of first socket, just fallback to old SO_REUSEPORT logic instead of trying to be too smart. Normal SO_REUSEPORT users do not mix different kind of sockets, as this mechanism is used for load balance traffic. Fixes: e32ea7e74727 ("soreuseport: fast reuseport UDP socket selection") Reported-by: Dmitry Vyukov Signed-off-by: Eric Dumazet Cc: Craig Gallek Acked-by: Craig Gallek --- net/ipv4/udp.c | 32 +++++++++++++++++++++----------- net/ipv6/udp.c | 32 +++++++++++++++++++++----------- 2 files changed, 42 insertions(+), 22 deletions(-) diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index dc45b538e237..be0b21852b13 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -499,6 +499,7 @@ static struct sock *udp4_lib_lookup2(struct net *net, struct sock *sk, *result; struct hlist_nulls_node *node; int score, badness, matches = 0, reuseport = 0; + bool select_ok = true; u32 hash = 0; begin: @@ -512,14 +513,18 @@ begin: badness = score; reuseport = sk->sk_reuseport; if (reuseport) { - struct sock *sk2; hash = udp_ehashfn(net, daddr, hnum, saddr, sport); - sk2 = reuseport_select_sock(sk, hash, skb, - sizeof(struct udphdr)); - if (sk2) { - result = sk2; - goto found; + if (select_ok) { + struct sock *sk2; + + sk2 = reuseport_select_sock(sk, hash, skb, + sizeof(struct udphdr)); + if (sk2) { + result = sk2; + select_ok = false; + goto found; + } } matches = 1; } @@ -563,6 +568,7 @@ struct sock *__udp4_lib_lookup(struct net *net, __be32 saddr, unsigned int hash2, slot2, slot = udp_hashfn(net, hnum, udptable->mask); struct udp_hslot *hslot2, *hslot = &udptable->hash[slot]; int score, badness, matches = 0, reuseport = 0; + bool select_ok = true; u32 hash = 0; rcu_read_lock(); @@ -601,14 +607,18 @@ begin: badness = score; reuseport = sk->sk_reuseport; if (reuseport) { - struct sock *sk2; hash = udp_ehashfn(net, daddr, hnum, saddr, sport); - sk2 = reuseport_select_sock(sk, hash, skb, + if (select_ok) { + struct sock *sk2; + + sk2 = reuseport_select_sock(sk, hash, skb, sizeof(struct udphdr)); - if (sk2) { - result = sk2; - goto found; + if (sk2) { + result = sk2; + select_ok = false; + goto found; + } } matches = 1; } diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 5d2c2afffe7b..22e28a44e3c8 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -257,6 +257,7 @@ static struct sock *udp6_lib_lookup2(struct net *net, struct sock *sk, *result; struct hlist_nulls_node *node; int score, badness, matches = 0, reuseport = 0; + bool select_ok = true; u32 hash = 0; begin: @@ -270,14 +271,18 @@ begin: badness = score; reuseport = sk->sk_reuseport; if (reuseport) { - struct sock *sk2; hash = udp6_ehashfn(net, daddr, hnum, saddr, sport); - sk2 = reuseport_select_sock(sk, hash, skb, - sizeof(struct udphdr)); - if (sk2) { - result = sk2; - goto found; + if (select_ok) { + struct sock *sk2; + + sk2 = reuseport_select_sock(sk, hash, skb, + sizeof(struct udphdr)); + if (sk2) { + result = sk2; + select_ok = false; + goto found; + } } matches = 1; } @@ -321,6 +326,7 @@ struct sock *__udp6_lib_lookup(struct net *net, unsigned int hash2, slot2, slot = udp_hashfn(net, hnum, udptable->mask); struct udp_hslot *hslot2, *hslot = &udptable->hash[slot]; int score, badness, matches = 0, reuseport = 0; + bool select_ok = true; u32 hash = 0; rcu_read_lock(); @@ -358,14 +364,18 @@ begin: badness = score; reuseport = sk->sk_reuseport; if (reuseport) { - struct sock *sk2; hash = udp6_ehashfn(net, daddr, hnum, saddr, sport); - sk2 = reuseport_select_sock(sk, hash, skb, + if (select_ok) { + struct sock *sk2; + + sk2 = reuseport_select_sock(sk, hash, skb, sizeof(struct udphdr)); - if (sk2) { - result = sk2; - goto found; + if (sk2) { + result = sk2; + select_ok = false; + goto found; + } } matches = 1; }