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: 4b623f9f0f ("net-shapers: implement NL get operation")
Reported-by: Paul Moses <p@1g4.org>
Link: https://lore.kernel.org/20260309173450.538026-1-p@1g4.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Link: https://patch.msgid.link/20260317161014.779569-1-kuba@kernel.org
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
Jakub Kicinski
2026-03-17 09:10:13 -07:00
committed by Paolo Abeni
parent 8a63baadf0
commit 0f9ea7141f

View File

@@ -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;