mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-03-21 23:16:50 +08:00
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:
committed by
Paolo Abeni
parent
8a63baadf0
commit
0f9ea7141f
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user