All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH net v7] net: sched: cls_u32: Fix u32's systematic failure to free IDR entries for hnodes.
@ 2024-11-10 17:28 Alexandre Ferrieux
  2024-11-10 18:14 ` Victor Nogueira
  2024-11-13  5:00 ` patchwork-bot+netdevbpf
  0 siblings, 2 replies; 14+ messages in thread
From: Alexandre Ferrieux @ 2024-11-10 17:28 UTC (permalink / raw)
  To: edumazet; +Cc: jhs, xiyou.wangcong, jiri, horms, alexandre.ferrieux, netdev

To generate hnode handles (in gen_new_htid()), u32 uses IDR and
encodes the returned small integer into a structured 32-bit
word. Unfortunately, at disposal time, the needed decoding
is not done. As a result, idr_remove() fails, and the IDR
fills up. Since its size is 2048, the following script ends up
with "Filter already exists":

  tc filter add dev myve $FILTER1
  tc filter add dev myve $FILTER2
  for i in {1..2048}
  do
    echo $i
    tc filter del dev myve $FILTER2
    tc filter add dev myve $FILTER2
  done

This patch adds the missing decoding logic for handles that
deserve it.

Fixes: e7614370d6f0 ("net_sched: use idr to allocate u32 filter handles")
Reviewed-by: Eric Dumazet <edumazet@google.com>
Acked-by: Jamal Hadi Salim <jhs@mojatatu.com>
Signed-off-by: Alexandre Ferrieux <alexandre.ferrieux@orange.com>
---
v7: static inline -> static
v6: big speedup of the tdc test with batch tc
v5: fix title - again
v4: add tdc test
v3: prepend title with subsystem ident
v2: use u32 type in handle encoder/decoder

 net/sched/cls_u32.c                           | 18 ++++++++++----
 .../tc-testing/tc-tests/filters/u32.json      | 24 +++++++++++++++++++
 2 files changed, 38 insertions(+), 4 deletions(-)

diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c
index 9412d88a99bc..d3a03c57545b 100644
--- a/net/sched/cls_u32.c
+++ b/net/sched/cls_u32.c
@@ -92,6 +92,16 @@ struct tc_u_common {
 	long			knodes;
 };
 
+static u32 handle2id(u32 h)
+{
+	return ((h & 0x80000000) ? ((h >> 20) & 0x7FF) : h);
+}
+
+static u32 id2handle(u32 id)
+{
+	return (id | 0x800U) << 20;
+}
+
 static inline unsigned int u32_hash_fold(__be32 key,
 					 const struct tc_u32_sel *sel,
 					 u8 fshift)
@@ -310,7 +320,7 @@ static u32 gen_new_htid(struct tc_u_common *tp_c, struct tc_u_hnode *ptr)
 	int id = idr_alloc_cyclic(&tp_c->handle_idr, ptr, 1, 0x7FF, GFP_KERNEL);
 	if (id < 0)
 		return 0;
-	return (id | 0x800U) << 20;
+	return id2handle(id);
 }
 
 static struct hlist_head *tc_u_common_hash;
@@ -360,7 +370,7 @@ static int u32_init(struct tcf_proto *tp)
 		return -ENOBUFS;
 
 	refcount_set(&root_ht->refcnt, 1);
-	root_ht->handle = tp_c ? gen_new_htid(tp_c, root_ht) : 0x80000000;
+	root_ht->handle = tp_c ? gen_new_htid(tp_c, root_ht) : id2handle(0);
 	root_ht->prio = tp->prio;
 	root_ht->is_root = true;
 	idr_init(&root_ht->handle_idr);
@@ -612,7 +622,7 @@ static int u32_destroy_hnode(struct tcf_proto *tp, struct tc_u_hnode *ht,
 		if (phn == ht) {
 			u32_clear_hw_hnode(tp, ht, extack);
 			idr_destroy(&ht->handle_idr);
-			idr_remove(&tp_c->handle_idr, ht->handle);
+			idr_remove(&tp_c->handle_idr, handle2id(ht->handle));
 			RCU_INIT_POINTER(*hn, ht->next);
 			kfree_rcu(ht, rcu);
 			return 0;
@@ -989,7 +999,7 @@ static int u32_change(struct net *net, struct sk_buff *in_skb,
 
 		err = u32_replace_hw_hnode(tp, ht, userflags, extack);
 		if (err) {
-			idr_remove(&tp_c->handle_idr, handle);
+			idr_remove(&tp_c->handle_idr, handle2id(handle));
 			kfree(ht);
 			return err;
 		}
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 14+ messages in thread
* RFC: chasing all idr_remove() misses
@ 2024-11-16 17:45 Alexandre Ferrieux
  2024-11-16 19:43 ` Dan Carpenter
  0 siblings, 1 reply; 14+ messages in thread
From: Alexandre Ferrieux @ 2024-11-16 17:45 UTC (permalink / raw)
  To: kernel-janitors

Hi,

In the recent fix of u32's IDR leaks:

  73af53d82076 net: sched: cls_u32: Fix u32's systematic failure to free IDR
               entries for hnodes.

... one side remark is that the problem went unnoticed for 7 years due to the
NULL result from idr_remove() being ignored at this call site.

Now, a cursory grep over the whole Linux tree shows 306 out of 386 call sites
(excluding those hidden in macros, if any) don't bother to extract the value
returned by idr_remove().

Indeed, a failed IDR removal is "mostly harmless" since IDs are not pointers so
the mismatch is detectable (and is detected, returning NULL). However, in racy
situations you may end up killing an innocent fresh entry, which may really
break things a bit later. And in all cases, a true bug is the root cause.

So, unless we have reasons to think cls_u32 was the only place where two ID
encodings might lend themselves to confusion, I'm wondering if it wouldn't make
sense to chase the issue more systematically:

 - either with WARN_ON[_ONCE](idr_remove()==NULL) on each call site individually
(a year-long endeavor implying tens of maintainers)

 - or with WARN_ON[_ONCE] just before returning NULL within idr_remove() itself,
or even radix_tree_delete_item() (quicker but possibly disruptive)

 - a variant of the latter being to do it only for harsh bug-hunting builds (the
ones typically used by patrolling bots)

Opinions ?

^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2024-11-22 21:32 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-11-10 17:28 [PATCH net v7] net: sched: cls_u32: Fix u32's systematic failure to free IDR entries for hnodes Alexandre Ferrieux
2024-11-10 18:14 ` Victor Nogueira
2024-11-13  5:00 ` patchwork-bot+netdevbpf
2024-11-14 18:24   ` RFC: chasing all idr_remove() misses Alexandre Ferrieux
2024-11-19  3:51     ` Cong Wang
2024-11-19  3:57       ` Cong Wang
2024-11-19  6:46       ` Alexandre Ferrieux
2024-11-22 21:32         ` Cong Wang
  -- strict thread matches above, loose matches on Subject: below --
2024-11-16 17:45 Alexandre Ferrieux
2024-11-16 19:43 ` Dan Carpenter
2024-11-17 15:07   ` Alexandre Ferrieux
2024-11-18  2:47     ` Matthew Wilcox
2024-11-18 11:33       ` Alexandre Ferrieux
2024-11-20 14:43         ` Alexandre Ferrieux

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.