* [PATCH net 1/1] net/sched: cls_route: fix fastmap use-after-free on filter
@ 2026-07-23 10:52 Jamal Hadi Salim
2026-07-28 10:34 ` Paolo Abeni
0 siblings, 1 reply; 5+ messages in thread
From: Jamal Hadi Salim @ 2026-07-23 10:52 UTC (permalink / raw)
To: netdev
Cc: pabeni, kuba, davem, edumazet, horms, john.fastabend, jiri,
zdi-disclosures, sh.kalluri129, victor, security, stable,
Jamal Hadi Salim, Santosh Kalluri
A case of partially rcu'ed structs.
The route4 classifier maintains a 16-slot fastmap cache that stores raw
struct route4_filter pointers indexed by (id, iif). The reader
(route4_classify) populates this cache via route4_set_fastmap() for
every classified packet that hits a filter. The writer
(route4_delete, route4_change) clears the cache via
route4_reset_fastmap() before RCU-deferred kfree of the filter.
This creates a UAF race:
1. Reader walks the RCU-protected bucket chain, finds filter f
2. Writer unlinks f, calls route4_reset_fastmap(), then tcf_queue_work()
3. Reader calls route4_set_fastmap() and writes f into the cache
*after* the writer's reset, caching a pointer about to be freed
4. After the RCU grace period, kfree(f) executes
5. Next classified packet on the same (id, iif) tuple hits the stale
fastmap entry and reads f->res from freed memory
Reproduced with an mdelay(100) accelerator in route4_set_fastmap() and a
concurrent add/delete stress test (provided by both zdi and Santosh) deletes
the last filter on a tp. Both triggered KASAN slab-use-after-free reports
in the route4 fastmap paths.
Fix:
Move the fastmap flush to the writer side under RTNL, ordered after
synchronize_rcu(). This ensures stale fastmap entries written by in-flight
readers are visible (readers have finished) and no new readers can find the
unlinked filter, so the subsequent route4_reset_fastmap(head) flushes them
safely while head is valid under RTNL.
Note: This fix will slow down deletes and filter add/replace, since
synchronize_rcu() is called under RTNL in route4_delete() and
route4_change(). An alternative fix would have been to introduce refcnt
to the head struct but that is a much bigger patch. We justify this as
a reasonable fix.
Fixes: 1109c00547fc ("net: sched: RCU cls_route")
Reported-by: zdi-disclosures@trendmicro.com
Reported-by: Santosh Kalluri <santosh.kalluri129@gmail.com>
Suggested-by: Santosh Kalluri <santosh.kalluri129@gmail.com>
Tested-by: Victor Nogueira <victor@mojatatu.com>
Tested-by: Santosh Kalluri <santosh.kalluri129@gmail.com>
Signed-off-by: Jamal Hadi Salim <jhs@mojatatu.com>
---
net/sched/cls_route.c | 42 +++++++++++++++++++++++++++++++++++-------
1 file changed, 35 insertions(+), 7 deletions(-)
diff --git a/net/sched/cls_route.c b/net/sched/cls_route.c
index bd6f945bd388..700f878fc3cd 100644
--- a/net/sched/cls_route.c
+++ b/net/sched/cls_route.c
@@ -297,16 +297,29 @@ static void route4_destroy(struct tcf_proto *tp, bool rtnl_held,
next = rtnl_dereference(f->next);
RCU_INIT_POINTER(b->ht[h2], next);
tcf_unbind_filter(tp, &f->res);
- if (tcf_exts_get_net(&f->exts))
- route4_queue_work(f);
- else
- __route4_delete_filter(f);
+ /* Always defer the free. The direct
+ * __route4_delete_filter() path has no
+ * grace period and races with readers
+ * that cached f in the fastmap.
+ */
+ tcf_exts_get_net(&f->exts);
+ route4_queue_work(f);
}
}
RCU_INIT_POINTER(head->table[h1], NULL);
kfree_rcu(b, rcu);
}
}
+
+ /* All filters are unlinked; no new reader can find them on the
+ * chain. Wait for in-flight readers that may still hold a filter
+ * pointer and have published it into the fastmap after we unlinked.
+ * Then flush the stale entries while head is still valid under
+ * RTNL, matching the route4_delete() pattern.
+ */
+ synchronize_rcu();
+ route4_reset_fastmap(head);
+
kfree_rcu(head, rcu);
}
@@ -334,10 +347,16 @@ static int route4_delete(struct tcf_proto *tp, void *arg, bool *last,
/* unlink it */
RCU_INIT_POINTER(*fp, rtnl_dereference(f->next));
- /* Remove any fastmap lookups that might ref filter
- * notice we unlink'd the filter so we can't get it
- * back in the fastmap.
+ /* This code path assumes we have the RTNL lock.
+ * We wait for in-flight readers that may still hold
+ * the filter pointer and publish it into the fastmap
+ * after we unlinked it.
+ * Once done, no new reader can find the filter on the
+ * chain, so the only stale fastmap entries are the
+ * ones those readers just wrote. Flush them now
+ * while head is still valid.
*/
+ synchronize_rcu();
route4_reset_fastmap(head);
/* Delete it */
@@ -558,6 +577,15 @@ static int route4_change(struct net *net, struct sk_buff *in_skb,
}
}
+ /* Assuming RTNL lock. Wait for in-flight readers that may still
+ * hold a filter pointer and publish it into the fastmap after we
+ * inserted the new filter (or unlinked fold when replacing).
+ * Once they are done, flush the stale entries while head is still
+ * valid. Needed on both the creation and replace paths: on
+ * creation, readers may have cached a ROUTE4_FAILURE entry for the
+ * (id, iif) tuple that the new filter now matches.
+ */
+ synchronize_rcu();
route4_reset_fastmap(head);
*arg = f;
if (fold) {
--
2.34.1
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH net 1/1] net/sched: cls_route: fix fastmap use-after-free on filter
2026-07-23 10:52 [PATCH net 1/1] net/sched: cls_route: fix fastmap use-after-free on filter Jamal Hadi Salim
@ 2026-07-28 10:34 ` Paolo Abeni
2026-07-28 12:37 ` Jamal Hadi Salim
0 siblings, 1 reply; 5+ messages in thread
From: Paolo Abeni @ 2026-07-28 10:34 UTC (permalink / raw)
To: Jamal Hadi Salim, netdev
Cc: kuba, davem, edumazet, horms, john.fastabend, jiri,
zdi-disclosures, sh.kalluri129, victor, security, stable,
Santosh Kalluri
On 7/23/26 12:52 PM, Jamal Hadi Salim wrote:
> diff --git a/net/sched/cls_route.c b/net/sched/cls_route.c
> index bd6f945bd388..700f878fc3cd 100644
> --- a/net/sched/cls_route.c
> +++ b/net/sched/cls_route.c
> @@ -297,16 +297,29 @@ static void route4_destroy(struct tcf_proto *tp, bool rtnl_held,
> next = rtnl_dereference(f->next);
> RCU_INIT_POINTER(b->ht[h2], next);
> tcf_unbind_filter(tp, &f->res);
> - if (tcf_exts_get_net(&f->exts))
> - route4_queue_work(f);
> - else
> - __route4_delete_filter(f);
> + /* Always defer the free. The direct
> + * __route4_delete_filter() path has no
> + * grace period and races with readers
> + * that cached f in the fastmap.
> + */
> + tcf_exts_get_net(&f->exts);
> + route4_queue_work(f);
Sashiko fears the above would be race prone:
https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260723105210.817079-1-jhs%40mojatatu.com
> }
> }
> RCU_INIT_POINTER(head->table[h1], NULL);
> kfree_rcu(b, rcu);
> }
> }
> +
> + /* All filters are unlinked; no new reader can find them on the
> + * chain. Wait for in-flight readers that may still hold a filter
> + * pointer and have published it into the fastmap after we unlinked.
> + * Then flush the stale entries while head is still valid under
> + * RTNL, matching the route4_delete() pattern.
> + */
> + synchronize_rcu();
> + route4_reset_fastmap(head);
This really looks like quite a big hammer. Since there is already a
synchronization point for both reader and write while acquiring the
fastmap_lock, I'm wondering if something alike the following could work ?!?
(completely untested!!!):
---
diff --git a/net/sched/cls_route.c b/net/sched/cls_route.c
index bd6f945bd388..8cecc945dcde 100644
--- a/net/sched/cls_route.c
+++ b/net/sched/cls_route.c
@@ -52,6 +52,7 @@ struct route4_filter {
struct tcf_result res;
struct tcf_exts exts;
u32 handle;
+ bool dying;
struct route4_bucket *bkt;
struct tcf_proto *tp;
struct rcu_work rwork;
@@ -66,9 +67,11 @@ static inline int route4_fastmap_hash(u32 id, int iif)
static DEFINE_SPINLOCK(fastmap_lock);
static void
-route4_reset_fastmap(struct route4_head *head)
+route4_reset_fastmap(struct route4_head *head, struct route4_filter *f)
{
spin_lock_bh(&fastmap_lock);
+ if (f)
+ f->dying = true;
memset(head->fastmap, 0, sizeof(head->fastmap));
spin_unlock_bh(&fastmap_lock);
}
@@ -81,9 +84,11 @@ route4_set_fastmap(struct route4_head *head, u32 id, int iif,
/* fastmap updates must look atomic to aling id, iff, filter */
spin_lock_bh(&fastmap_lock);
- head->fastmap[h].id = id;
- head->fastmap[h].iif = iif;
- head->fastmap[h].filter = f;
+ if (!f->dying) {
+ head->fastmap[h].id = id;
+ head->fastmap[h].iif = iif;
+ head->fastmap[h].filter = f;
+ }
spin_unlock_bh(&fastmap_lock);
}
@@ -338,7 +343,7 @@ static int route4_delete(struct tcf_proto *tp, void *arg, bool *last,
* notice we unlink'd the filter so we can't get it
* back in the fastmap.
*/
- route4_reset_fastmap(head);
+ route4_reset_fastmap(head, f);
/* Delete it */
tcf_unbind_filter(tp, &f->res);
@@ -558,7 +563,7 @@ static int route4_change(struct net *net, struct sk_buff *in_skb,
}
}
- route4_reset_fastmap(head);
+ route4_reset_fastmap(head, fold);
*arg = f;
if (fold) {
tcf_unbind_filter(tp, &fold->res);
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH net 1/1] net/sched: cls_route: fix fastmap use-after-free on filter
2026-07-28 10:34 ` Paolo Abeni
@ 2026-07-28 12:37 ` Jamal Hadi Salim
2026-07-28 13:05 ` Paolo Abeni
0 siblings, 1 reply; 5+ messages in thread
From: Jamal Hadi Salim @ 2026-07-28 12:37 UTC (permalink / raw)
To: Paolo Abeni
Cc: netdev, kuba, davem, edumazet, horms, john.fastabend, jiri,
zdi-disclosures, sh.kalluri129, victor, security, stable,
Santosh Kalluri
On Tue, Jul 28, 2026 at 6:34 AM Paolo Abeni <pabeni@redhat.com> wrote:
>
> On 7/23/26 12:52 PM, Jamal Hadi Salim wrote:
> > diff --git a/net/sched/cls_route.c b/net/sched/cls_route.c
> > index bd6f945bd388..700f878fc3cd 100644
> > --- a/net/sched/cls_route.c
> > +++ b/net/sched/cls_route.c
> > @@ -297,16 +297,29 @@ static void route4_destroy(struct tcf_proto *tp, bool rtnl_held,
> > next = rtnl_dereference(f->next);
> > RCU_INIT_POINTER(b->ht[h2], next);
> > tcf_unbind_filter(tp, &f->res);
> > - if (tcf_exts_get_net(&f->exts))
> > - route4_queue_work(f);
> > - else
> > - __route4_delete_filter(f);
> > + /* Always defer the free. The direct
> > + * __route4_delete_filter() path has no
> > + * grace period and races with readers
> > + * that cached f in the fastmap.
> > + */
> > + tcf_exts_get_net(&f->exts);
> > + route4_queue_work(f);
>
> Sashiko fears the above would be race prone:
>
> https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260723105210.817079-1-jhs%40mojatatu.com
>
That was fixed in v2. Is there something i can do next time (message
patchwork?) to not waste your time looking at an older version?
>
>
> > }
> > }
> > RCU_INIT_POINTER(head->table[h1], NULL);
> > kfree_rcu(b, rcu);
> > }
> > }
> > +
> > + /* All filters are unlinked; no new reader can find them on the
> > + * chain. Wait for in-flight readers that may still hold a filter
> > + * pointer and have published it into the fastmap after we unlinked.
> > + * Then flush the stale entries while head is still valid under
> > + * RTNL, matching the route4_delete() pattern.
> > + */
> > + synchronize_rcu();
> > + route4_reset_fastmap(head);
>
> This really looks like quite a big hammer.
It is.
I viewed it as bug fix to original intent of 1109c00547fc - that
commit even though had the intent of protecting the fast map as as
well (based on the wording of comment i saw).
If performance is a big concern: another approach that maybe worth
considering is to totally remove the fastpath. It maybe more
performant than imposing the rcus.
>Since there is already a
> synchronization point for both reader and write while acquiring the
> fastmap_lock, I'm wondering if something alike the following could work ?!?
> (completely untested!!!):
>
Your approach has some holes, example misses the most dangerous part -
destroy(), etc
I will mull over it and see if it can be fixed to handle all the
issues. I think it's possible i just need to put time to
validate/test.
cheers,
jamal
> ---
> diff --git a/net/sched/cls_route.c b/net/sched/cls_route.c
> index bd6f945bd388..8cecc945dcde 100644
> --- a/net/sched/cls_route.c
> +++ b/net/sched/cls_route.c
> @@ -52,6 +52,7 @@ struct route4_filter {
> struct tcf_result res;
> struct tcf_exts exts;
> u32 handle;
> + bool dying;
> struct route4_bucket *bkt;
> struct tcf_proto *tp;
> struct rcu_work rwork;
> @@ -66,9 +67,11 @@ static inline int route4_fastmap_hash(u32 id, int iif)
>
> static DEFINE_SPINLOCK(fastmap_lock);
> static void
> -route4_reset_fastmap(struct route4_head *head)
> +route4_reset_fastmap(struct route4_head *head, struct route4_filter *f)
> {
> spin_lock_bh(&fastmap_lock);
> + if (f)
> + f->dying = true;
> memset(head->fastmap, 0, sizeof(head->fastmap));
> spin_unlock_bh(&fastmap_lock);
> }
> @@ -81,9 +84,11 @@ route4_set_fastmap(struct route4_head *head, u32 id, int iif,
>
> /* fastmap updates must look atomic to aling id, iff, filter */
> spin_lock_bh(&fastmap_lock);
> - head->fastmap[h].id = id;
> - head->fastmap[h].iif = iif;
> - head->fastmap[h].filter = f;
> + if (!f->dying) {
> + head->fastmap[h].id = id;
> + head->fastmap[h].iif = iif;
> + head->fastmap[h].filter = f;
> + }
> spin_unlock_bh(&fastmap_lock);
> }
>
> @@ -338,7 +343,7 @@ static int route4_delete(struct tcf_proto *tp, void *arg, bool *last,
> * notice we unlink'd the filter so we can't get it
> * back in the fastmap.
> */
> - route4_reset_fastmap(head);
> + route4_reset_fastmap(head, f);
>
> /* Delete it */
> tcf_unbind_filter(tp, &f->res);
> @@ -558,7 +563,7 @@ static int route4_change(struct net *net, struct sk_buff *in_skb,
> }
> }
>
> - route4_reset_fastmap(head);
> + route4_reset_fastmap(head, fold);
> *arg = f;
> if (fold) {
> tcf_unbind_filter(tp, &fold->res);
>
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH net 1/1] net/sched: cls_route: fix fastmap use-after-free on filter
2026-07-28 12:37 ` Jamal Hadi Salim
@ 2026-07-28 13:05 ` Paolo Abeni
2026-07-28 13:12 ` Jamal Hadi Salim
0 siblings, 1 reply; 5+ messages in thread
From: Paolo Abeni @ 2026-07-28 13:05 UTC (permalink / raw)
To: Jamal Hadi Salim
Cc: netdev, kuba, davem, edumazet, horms, john.fastabend, jiri,
zdi-disclosures, sh.kalluri129, victor, security, stable,
Santosh Kalluri
On 7/28/26 2:37 PM, Jamal Hadi Salim wrote:
> On Tue, Jul 28, 2026 at 6:34 AM Paolo Abeni <pabeni@redhat.com> wrote:
>>
>> On 7/23/26 12:52 PM, Jamal Hadi Salim wrote:
>>> diff --git a/net/sched/cls_route.c b/net/sched/cls_route.c
>>> index bd6f945bd388..700f878fc3cd 100644
>>> --- a/net/sched/cls_route.c
>>> +++ b/net/sched/cls_route.c
>>> @@ -297,16 +297,29 @@ static void route4_destroy(struct tcf_proto *tp, bool rtnl_held,
>>> next = rtnl_dereference(f->next);
>>> RCU_INIT_POINTER(b->ht[h2], next);
>>> tcf_unbind_filter(tp, &f->res);
>>> - if (tcf_exts_get_net(&f->exts))
>>> - route4_queue_work(f);
>>> - else
>>> - __route4_delete_filter(f);
>>> + /* Always defer the free. The direct
>>> + * __route4_delete_filter() path has no
>>> + * grace period and races with readers
>>> + * that cached f in the fastmap.
>>> + */
>>> + tcf_exts_get_net(&f->exts);
>>> + route4_queue_work(f);
>>
>> Sashiko fears the above would be race prone:
>>
>> https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260723105210.817079-1-jhs%40mojatatu.com
>>
>
> That was fixed in v2. Is there something i can do next time (message
> patchwork?) to not waste your time looking at an older version?
Usually the tool properly updates the old revision status, but sometimes
it fails do to that.
Double checking that the old revision is marked as superseded in PW
after posting the new one could help.
>>> }
>>> }
>>> RCU_INIT_POINTER(head->table[h1], NULL);
>>> kfree_rcu(b, rcu);
>>> }
>>> }
>>> +
>>> + /* All filters are unlinked; no new reader can find them on the
>>> + * chain. Wait for in-flight readers that may still hold a filter
>>> + * pointer and have published it into the fastmap after we unlinked.
>>> + * Then flush the stale entries while head is still valid under
>>> + * RTNL, matching the route4_delete() pattern.
>>> + */
>>> + synchronize_rcu();
>>> + route4_reset_fastmap(head);
>>
>> This really looks like quite a big hammer.
>
> It is.
> I viewed it as bug fix to original intent of 1109c00547fc - that
> commit even though had the intent of protecting the fast map as as
> well (based on the wording of comment i saw).
> If performance is a big concern: another approach that maybe worth
> considering is to totally remove the fastpath. It maybe more
> performant than imposing the rcus.
Given the amount of effort involved in rtnl lock contention reduction, I
think it would be great if we could avoid the synchronize_rcu(), if it
does not block the fix for an unreasonable amount of time.
>> Since there is already a
>> synchronization point for both reader and write while acquiring the
>> fastmap_lock, I'm wondering if something alike the following could work ?!?
>> (completely untested!!!):
>>
>
> Your approach has some holes, example misses the most dangerous part -
> destroy(), etc
> I will mull over it and see if it can be fixed to handle all the
> issues. I think it's possible i just need to put time to
> validate/test.
I would appreciate that, thanks!
Paolo
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH net 1/1] net/sched: cls_route: fix fastmap use-after-free on filter
2026-07-28 13:05 ` Paolo Abeni
@ 2026-07-28 13:12 ` Jamal Hadi Salim
0 siblings, 0 replies; 5+ messages in thread
From: Jamal Hadi Salim @ 2026-07-28 13:12 UTC (permalink / raw)
To: Paolo Abeni
Cc: netdev, kuba, davem, edumazet, horms, john.fastabend, jiri,
zdi-disclosures, sh.kalluri129, victor, security, stable,
Santosh Kalluri
[-- Attachment #1: Type: text/plain, Size: 4150 bytes --]
On Tue, Jul 28, 2026 at 9:05 AM Paolo Abeni <pabeni@redhat.com> wrote:
>
> On 7/28/26 2:37 PM, Jamal Hadi Salim wrote:
> > On Tue, Jul 28, 2026 at 6:34 AM Paolo Abeni <pabeni@redhat.com> wrote:
> >>
> >> On 7/23/26 12:52 PM, Jamal Hadi Salim wrote:
> >>> diff --git a/net/sched/cls_route.c b/net/sched/cls_route.c
> >>> index bd6f945bd388..700f878fc3cd 100644
> >>> --- a/net/sched/cls_route.c
> >>> +++ b/net/sched/cls_route.c
> >>> @@ -297,16 +297,29 @@ static void route4_destroy(struct tcf_proto *tp, bool rtnl_held,
> >>> next = rtnl_dereference(f->next);
> >>> RCU_INIT_POINTER(b->ht[h2], next);
> >>> tcf_unbind_filter(tp, &f->res);
> >>> - if (tcf_exts_get_net(&f->exts))
> >>> - route4_queue_work(f);
> >>> - else
> >>> - __route4_delete_filter(f);
> >>> + /* Always defer the free. The direct
> >>> + * __route4_delete_filter() path has no
> >>> + * grace period and races with readers
> >>> + * that cached f in the fastmap.
> >>> + */
> >>> + tcf_exts_get_net(&f->exts);
> >>> + route4_queue_work(f);
> >>
> >> Sashiko fears the above would be race prone:
> >>
> >> https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260723105210.817079-1-jhs%40mojatatu.com
> >>
> >
> > That was fixed in v2. Is there something i can do next time (message
> > patchwork?) to not waste your time looking at an older version?
>
> Usually the tool properly updates the old revision status, but sometimes
> it fails do to that.
>
> Double checking that the old revision is marked as superseded in PW
> after posting the new one could help.
>
> >>> }
> >>> }
> >>> RCU_INIT_POINTER(head->table[h1], NULL);
> >>> kfree_rcu(b, rcu);
> >>> }
> >>> }
> >>> +
> >>> + /* All filters are unlinked; no new reader can find them on the
> >>> + * chain. Wait for in-flight readers that may still hold a filter
> >>> + * pointer and have published it into the fastmap after we unlinked.
> >>> + * Then flush the stale entries while head is still valid under
> >>> + * RTNL, matching the route4_delete() pattern.
> >>> + */
> >>> + synchronize_rcu();
> >>> + route4_reset_fastmap(head);
> >>
> >> This really looks like quite a big hammer.
> >
> > It is.
> > I viewed it as bug fix to original intent of 1109c00547fc - that
> > commit even though had the intent of protecting the fast map as as
> > well (based on the wording of comment i saw).
> > If performance is a big concern: another approach that maybe worth
> > considering is to totally remove the fastpath. It maybe more
> > performant than imposing the rcus.
>
> Given the amount of effort involved in rtnl lock contention reduction, I
> think it would be great if we could avoid the synchronize_rcu(), if it
> does not block the fix for an unreasonable amount of time.
>
> >> Since there is already a
> >> synchronization point for both reader and write while acquiring the
> >> fastmap_lock, I'm wondering if something alike the following could work ?!?
> >> (completely untested!!!):
> >>
> >
> > Your approach has some holes, example misses the most dangerous part -
> > destroy(), etc
> > I will mull over it and see if it can be fixed to handle all the
> > issues. I think it's possible i just need to put time to
> > validate/test.
> I would appreciate that, thanks!
See attached. Untested (compiles) - addresses my destroy() concern
eaerlier _i think_ (unless something pops up in testing)
cheers,
jamal
> Paolo
>
[-- Attachment #2: patch-paolo-mod --]
[-- Type: application/octet-stream, Size: 2597 bytes --]
diff --git a/net/sched/cls_route.c b/net/sched/cls_route.c
index bd6f945bd388..c68ac98f2047 100644
--- a/net/sched/cls_route.c
+++ b/net/sched/cls_route.c
@@ -52,6 +52,7 @@ struct route4_filter {
struct tcf_result res;
struct tcf_exts exts;
u32 handle;
+ bool dying;
struct route4_bucket *bkt;
struct tcf_proto *tp;
struct rcu_work rwork;
@@ -66,9 +67,11 @@ static inline int route4_fastmap_hash(u32 id, int iif)
static DEFINE_SPINLOCK(fastmap_lock);
static void
-route4_reset_fastmap(struct route4_head *head)
+route4_reset_fastmap(struct route4_head *head, struct route4_filter *f)
{
spin_lock_bh(&fastmap_lock);
+ if (f)
+ f->dying = true;
memset(head->fastmap, 0, sizeof(head->fastmap));
spin_unlock_bh(&fastmap_lock);
}
@@ -81,9 +84,11 @@ route4_set_fastmap(struct route4_head *head, u32 id, int iif,
/* fastmap updates must look atomic to aling id, iff, filter */
spin_lock_bh(&fastmap_lock);
- head->fastmap[h].id = id;
- head->fastmap[h].iif = iif;
- head->fastmap[h].filter = f;
+ if (f == ROUTE4_FAILURE || !f->dying) {
+ head->fastmap[h].id = id;
+ head->fastmap[h].iif = iif;
+ head->fastmap[h].filter = f;
+ }
spin_unlock_bh(&fastmap_lock);
}
@@ -297,6 +302,13 @@ static void route4_destroy(struct tcf_proto *tp, bool rtnl_held,
next = rtnl_dereference(f->next);
RCU_INIT_POINTER(b->ht[h2], next);
tcf_unbind_filter(tp, &f->res);
+ /* Mark the filter dying under fastmap_lock so
+ * any in-flight reader that still holds it
+ * will skip the republish in route4_set_fastmap().
+ */
+ spin_lock_bh(&fastmap_lock);
+ f->dying = true;
+ spin_unlock_bh(&fastmap_lock);
if (tcf_exts_get_net(&f->exts))
route4_queue_work(f);
else
@@ -307,6 +319,16 @@ static void route4_destroy(struct tcf_proto *tp, bool rtnl_held,
kfree_rcu(b, rcu);
}
}
+
+ /* All filters are unlinked and marked dying, so no in-flight
+ * reader can republish a stale entry after this reset.
+ */
+ route4_reset_fastmap(head, NULL);
kfree_rcu(head, rcu);
}
@@ -338,7 +360,7 @@ static int route4_delete(struct tcf_proto *tp, void *arg, bool *last,
* notice we unlink'd the filter so we can't get it
* back in the fastmap.
*/
- route4_reset_fastmap(head);
+ route4_reset_fastmap(head, f);
/* Delete it */
tcf_unbind_filter(tp, &f->res);
@@ -558,7 +580,7 @@ static int route4_change(struct net *net, struct sk_buff *in_skb,
}
}
- route4_reset_fastmap(head);
+ route4_reset_fastmap(head, fold);
*arg = f;
if (fold) {
tcf_unbind_filter(tp, &fold->res);
^ permalink raw reply related [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-07-28 13:12 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-23 10:52 [PATCH net 1/1] net/sched: cls_route: fix fastmap use-after-free on filter Jamal Hadi Salim
2026-07-28 10:34 ` Paolo Abeni
2026-07-28 12:37 ` Jamal Hadi Salim
2026-07-28 13:05 ` Paolo Abeni
2026-07-28 13:12 ` Jamal Hadi Salim
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox