ipv6_flowlabel_get() lets a socket reacquire an existing flowlabel and link another ipv6_fl_socklist entry through the recheck path. mem_check() only counts socket leases after fl_size falls below FL_MAX_SIZE - FL_MAX_PER_SOCK. Reusing an existing flowlabel does not increase fl_size, so duplicate GET requests can keep taking the recheck path and grow one socket's lease list without ever hitting the FL_MAX_PER_SOCK limit. Check the current socket lease count before linking a reused flowlabel. This keeps the duplicate-acquisition path consistent with the long-standing per-socket cap without changing the global budget logic for new flowlabels. Fixes: 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Reported-by: Vega Signed-off-by: Zhiling Zou --- net/ipv6/ip6_flowlabel.c | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/net/ipv6/ip6_flowlabel.c b/net/ipv6/ip6_flowlabel.c index 1ab5ad0dcf24f..e295f85bcc604 100644 --- a/net/ipv6/ip6_flowlabel.c +++ b/net/ipv6/ip6_flowlabel.c @@ -493,6 +493,24 @@ static int mem_check(struct sock *sk) return 0; } +static int mem_check_dup(struct sock *sk) +{ + struct ipv6_fl_socklist *sfl; + int count = 0; + + lockdep_assert_held(&ip6_fl_lock); + + rcu_read_lock(); + for_each_sk_fl_rcu(sk, sfl) + count++; + rcu_read_unlock(); + + if (count >= FL_MAX_PER_SOCK && !capable(CAP_NET_ADMIN)) + return -ENOBUFS; + + return 0; +} + static inline void fl_link(struct sock *sk, struct ipv6_fl_socklist *sfl, struct ip6_flowlabel *fl) { @@ -679,10 +697,17 @@ static int ipv6_flowlabel_get(struct sock *sk, struct in6_flowlabel_req *freq, err = -ENOMEM; if (!sfl1) goto release; - if (fl->linger > fl1->linger) - fl1->linger = fl->linger; - if ((long)(fl->expires - fl1->expires) > 0) - fl1->expires = fl->expires; + spin_lock_bh(&ip6_fl_lock); + err = mem_check_dup(sk); + if (err == 0) { + if (fl->linger > fl1->linger) + fl1->linger = fl->linger; + if ((long)(fl->expires - fl1->expires) > 0) + fl1->expires = fl->expires; + } + spin_unlock_bh(&ip6_fl_lock); + if (err != 0) + goto release; fl_link(sk, sfl1, fl1); fl_free(fl); return 0; -- 2.43.0