From 0f9ea7141f365b4f27226898e62220fb98ef8dc6 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 17 Mar 2026 09:10:13 -0700 Subject: [PATCH] net: shaper: protect late read accesses to the hierarchy We look up a netdev during prep of Netlink ops (pre- callbacks) and take a ref to it. Then later in the body of the callback we take its lock or RCU which are the actual protections. This is not proper, a conversion from a ref to a locked netdev must include a liveness check (a check if the netdev hasn't been unregistered already). Fix the read cases (those under RCU). Writes needs a separate change to protect from creating the hierarchy after flush has already run. Fixes: 4b623f9f0f59 ("net-shapers: implement NL get operation") Reported-by: Paul Moses Link: https://lore.kernel.org/20260309173450.538026-1-p@1g4.org Signed-off-by: Jakub Kicinski Link: https://patch.msgid.link/20260317161014.779569-1-kuba@kernel.org Signed-off-by: Paolo Abeni --- net/shaper/shaper.c | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/net/shaper/shaper.c b/net/shaper/shaper.c index 3fd6629cb999..6b4c87e12f1f 100644 --- a/net/shaper/shaper.c +++ b/net/shaper/shaper.c @@ -65,6 +65,21 @@ net_shaper_hierarchy(struct net_shaper_binding *binding) return NULL; } +static struct net_shaper_hierarchy * +net_shaper_hierarchy_rcu(struct net_shaper_binding *binding) +{ + /* Readers look up the device and take a ref, then take RCU lock + * later at which point netdev may have been unregistered and flushed. + * READ_ONCE() pairs with WRITE_ONCE() in net_shaper_hierarchy_setup. + */ + if (binding->type == NET_SHAPER_BINDING_TYPE_NETDEV && + READ_ONCE(binding->netdev->reg_state) <= NETREG_REGISTERED) + return READ_ONCE(binding->netdev->net_shaper_hierarchy); + + /* No other type supported yet. */ + return NULL; +} + static const struct net_shaper_ops * net_shaper_ops(struct net_shaper_binding *binding) { @@ -251,9 +266,10 @@ static struct net_shaper * net_shaper_lookup(struct net_shaper_binding *binding, const struct net_shaper_handle *handle) { - struct net_shaper_hierarchy *hierarchy = net_shaper_hierarchy(binding); u32 index = net_shaper_handle_to_index(handle); + struct net_shaper_hierarchy *hierarchy; + hierarchy = net_shaper_hierarchy_rcu(binding); if (!hierarchy || xa_get_mark(&hierarchy->shapers, index, NET_SHAPER_NOT_VALID)) return NULL; @@ -778,17 +794,19 @@ int net_shaper_nl_get_dumpit(struct sk_buff *skb, /* Don't error out dumps performed before any set operation. */ binding = net_shaper_binding_from_ctx(ctx); - hierarchy = net_shaper_hierarchy(binding); - if (!hierarchy) - return 0; rcu_read_lock(); + hierarchy = net_shaper_hierarchy_rcu(binding); + if (!hierarchy) + goto out_unlock; + for (; (shaper = xa_find(&hierarchy->shapers, &ctx->start_index, U32_MAX, XA_PRESENT)); ctx->start_index++) { ret = net_shaper_fill_one(skb, binding, shaper, info); if (ret) break; } +out_unlock: rcu_read_unlock(); return ret;