[PATCH net v2 1/1] ipv6: flowlabel: cap duplicate leases per socket

Zhiling Zou <[email protected]>
Newsgroups org.kernel.vger.netdev
Message-ID <528bc30d301bcf32aca37cda1933b617d2d295f4.1786447968.git.zhilinz@nebusec.ai>
ipv6_flowlabel_get() allocates an ipv6_fl_socklist entry for every
successful GET. The recheck path for a compatible existing flowlabel
links another lease without applying any lease admission check. Repeated
GET requests for one shareable label can therefore grow a socket's lease
list without bound.

Count matching leases during the existing socket-list lookup and reject
a new unprivileged lease once that count reaches FL_MAX_PER_SOCK. The
IPv6 sockopt lock serializes the count with fl_link() and PUT, so no
global ip6_fl_lock is needed. New-label admission remains under the
existing mem_check() policy. CAP_NET_ADMIN callers remain unrestricted
and stop after the first matching lease.

Do the check before updating linger and expires. A GET that cannot add
its lease returns an error without refreshing the shared label, matching
the existing socket-list allocation failure path.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: [email protected]
Reported-by: Vega <[email protected]>
Signed-off-by: Zhiling Zou <[email protected]>
---
changes in v2:
- Count only leases of the flowlabel being reused.
- Fold the count into the existing socket-list lookup.
- Keep new-label admission under the existing mem_check() policy.
- Stop after the first matching lease for CAP_NET_ADMIN callers.
- Explain why a rejected GET does not refresh linger or expires.
- Correct the reuse-path explanation and trim the Fixes hash.
- v1 Link: https://lore.kernel.org/all/cf4fdc79ae4dc46bd4eb7eeb57e5de2091c13cd3.1785746178.git.zhilinz@nebusec.ai/

 net/ipv6/ip6_flowlabel.c | 18 ++++++++++++++----
 1 file changed, 14 insertions(+), 4 deletions(-)

diff --git a/net/ipv6/ip6_flowlabel.c b/net/ipv6/ip6_flowlabel.c
index 1ab5ad0dcf24f..55cc5ec485c59 100644
--- a/net/ipv6/ip6_flowlabel.c
+++ b/net/ipv6/ip6_flowlabel.c
@@ -617,6 +617,8 @@ static int ipv6_flowlabel_get(struct sock *sk, struct in6_flowlabel_req *freq,
 	struct ipv6_fl_socklist *sfl, *sfl1 = NULL;
 	struct ip6_flowlabel *fl, *fl1 = NULL;
 	struct net *net = sock_net(sk);
+	bool cap_net_admin = false;
+	int dup_count = 0;
 	int err;
 
 	if (freq->flr_flags & IPV6_FL_F_REFLECT) {
@@ -652,10 +654,14 @@ static int ipv6_flowlabel_get(struct sock *sk, struct in6_flowlabel_req *freq,
 					rcu_read_unlock();
 					goto done;
 				}
-				fl1 = sfl->fl;
-				if (!atomic_inc_not_zero(&fl1->users))
-					fl1 = NULL;
-				break;
+				if (!fl1) {
+					fl1 = sfl->fl;
+					if (!atomic_inc_not_zero(&fl1->users))
+						fl1 = NULL;
+					cap_net_admin = capable(CAP_NET_ADMIN);
+				}
+				if (++dup_count >= FL_MAX_PER_SOCK || cap_net_admin)
+					break;
 			}
 		}
 		rcu_read_unlock();
@@ -679,6 +685,10 @@ static int ipv6_flowlabel_get(struct sock *sk, struct in6_flowlabel_req *freq,
 			err = -ENOMEM;
 			if (!sfl1)
 				goto release;
+			/* sockopt_lock_sock() serializes the count and fl_link(). */
+			err = -ENOBUFS;
+			if (dup_count >= FL_MAX_PER_SOCK && !cap_net_admin)
+				goto release;
 			if (fl->linger > fl1->linger)
 				fl1->linger = fl->linger;
 			if ((long)(fl->expires - fl1->expires) > 0)
-- 
2.43.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.