Skip to content

Commit ed0dfff

Browse files
edumazetdavem330
authored andcommitted
udp: fix potential infinite loop in SO_REUSEPORT logic
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: e32ea7e ("soreuseport: fast reuseport UDP socket selection") Reported-by: Dmitry Vyukov <dvyukov@google.com> Signed-off-by: Eric Dumazet <edumazet@google.com> Cc: Craig Gallek <kraigatgoog@gmail.com> Acked-by: Craig Gallek <kraig@google.com> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent a200dcb commit ed0dfff

File tree

2 files changed

+42
-22
lines changed

2 files changed

+42
-22
lines changed

net/ipv4/udp.c

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -499,6 +499,7 @@ static struct sock *udp4_lib_lookup2(struct net *net,
499499
struct sock *sk, *result;
500500
struct hlist_nulls_node *node;
501501
int score, badness, matches = 0, reuseport = 0;
502+
bool select_ok = true;
502503
u32 hash = 0;
503504

504505
begin:
@@ -512,14 +513,18 @@ static struct sock *udp4_lib_lookup2(struct net *net,
512513
badness = score;
513514
reuseport = sk->sk_reuseport;
514515
if (reuseport) {
515-
struct sock *sk2;
516516
hash = udp_ehashfn(net, daddr, hnum,
517517
saddr, sport);
518-
sk2 = reuseport_select_sock(sk, hash, skb,
519-
sizeof(struct udphdr));
520-
if (sk2) {
521-
result = sk2;
522-
goto found;
518+
if (select_ok) {
519+
struct sock *sk2;
520+
521+
sk2 = reuseport_select_sock(sk, hash, skb,
522+
sizeof(struct udphdr));
523+
if (sk2) {
524+
result = sk2;
525+
select_ok = false;
526+
goto found;
527+
}
523528
}
524529
matches = 1;
525530
}
@@ -563,6 +568,7 @@ struct sock *__udp4_lib_lookup(struct net *net, __be32 saddr,
563568
unsigned int hash2, slot2, slot = udp_hashfn(net, hnum, udptable->mask);
564569
struct udp_hslot *hslot2, *hslot = &udptable->hash[slot];
565570
int score, badness, matches = 0, reuseport = 0;
571+
bool select_ok = true;
566572
u32 hash = 0;
567573

568574
rcu_read_lock();
@@ -601,14 +607,18 @@ struct sock *__udp4_lib_lookup(struct net *net, __be32 saddr,
601607
badness = score;
602608
reuseport = sk->sk_reuseport;
603609
if (reuseport) {
604-
struct sock *sk2;
605610
hash = udp_ehashfn(net, daddr, hnum,
606611
saddr, sport);
607-
sk2 = reuseport_select_sock(sk, hash, skb,
612+
if (select_ok) {
613+
struct sock *sk2;
614+
615+
sk2 = reuseport_select_sock(sk, hash, skb,
608616
sizeof(struct udphdr));
609-
if (sk2) {
610-
result = sk2;
611-
goto found;
617+
if (sk2) {
618+
result = sk2;
619+
select_ok = false;
620+
goto found;
621+
}
612622
}
613623
matches = 1;
614624
}

net/ipv6/udp.c

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,7 @@ static struct sock *udp6_lib_lookup2(struct net *net,
257257
struct sock *sk, *result;
258258
struct hlist_nulls_node *node;
259259
int score, badness, matches = 0, reuseport = 0;
260+
bool select_ok = true;
260261
u32 hash = 0;
261262

262263
begin:
@@ -270,14 +271,18 @@ static struct sock *udp6_lib_lookup2(struct net *net,
270271
badness = score;
271272
reuseport = sk->sk_reuseport;
272273
if (reuseport) {
273-
struct sock *sk2;
274274
hash = udp6_ehashfn(net, daddr, hnum,
275275
saddr, sport);
276-
sk2 = reuseport_select_sock(sk, hash, skb,
277-
sizeof(struct udphdr));
278-
if (sk2) {
279-
result = sk2;
280-
goto found;
276+
if (select_ok) {
277+
struct sock *sk2;
278+
279+
sk2 = reuseport_select_sock(sk, hash, skb,
280+
sizeof(struct udphdr));
281+
if (sk2) {
282+
result = sk2;
283+
select_ok = false;
284+
goto found;
285+
}
281286
}
282287
matches = 1;
283288
}
@@ -321,6 +326,7 @@ struct sock *__udp6_lib_lookup(struct net *net,
321326
unsigned int hash2, slot2, slot = udp_hashfn(net, hnum, udptable->mask);
322327
struct udp_hslot *hslot2, *hslot = &udptable->hash[slot];
323328
int score, badness, matches = 0, reuseport = 0;
329+
bool select_ok = true;
324330
u32 hash = 0;
325331

326332
rcu_read_lock();
@@ -358,14 +364,18 @@ struct sock *__udp6_lib_lookup(struct net *net,
358364
badness = score;
359365
reuseport = sk->sk_reuseport;
360366
if (reuseport) {
361-
struct sock *sk2;
362367
hash = udp6_ehashfn(net, daddr, hnum,
363368
saddr, sport);
364-
sk2 = reuseport_select_sock(sk, hash, skb,
369+
if (select_ok) {
370+
struct sock *sk2;
371+
372+
sk2 = reuseport_select_sock(sk, hash, skb,
365373
sizeof(struct udphdr));
366-
if (sk2) {
367-
result = sk2;
368-
goto found;
374+
if (sk2) {
375+
result = sk2;
376+
select_ok = false;
377+
goto found;
378+
}
369379
}
370380
matches = 1;
371381
}

0 commit comments

Comments
 (0)