]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
net: shaper: protect late read accesses to the hierarchy
authorJakub Kicinski <kuba@kernel.org>
Tue, 17 Mar 2026 16:10:13 +0000 (09:10 -0700)
committerPaolo Abeni <pabeni@redhat.com>
Thu, 19 Mar 2026 12:47:15 +0000 (13:47 +0100)
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 <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>
net/shaper/shaper.c

index 3fd6629cb99923ec83955bebe9db86410d33e616..6b4c87e12f1f4acd649921ebe0c93a0ebcea7b8a 100644 (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;