This fixes a possible UAF in if_nlmsg_size(),
which can run without RTNL.
Add rcu protection to "struct dpll_pin"
Move netdev_dpll_pin() from netdevice.h to dpll.h to
decrease name pollution.
Note: This looks possible to no longer acquire RTNL in
netdev_dpll_pin_assign() later in net-next.
v2: do not force rcu_read_lock() in rtnl_dpll_pin_size() (Jiri Pirko)
Fixes: 5f1842692880 ("netdev: expose DPLL pin handle for netdevice")
Signed-off-by: Eric Dumazet <edumazet@google.com>
Cc: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
Cc: Vadim Fedorenko <vadim.fedorenko@linux.dev>
Reviewed-by: Jiri Pirko <jiri@nvidia.com>
Link: https://lore.kernel.org/r/20240223123208.3543319-1-edumazet@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
                xa_destroy(&pin->parent_refs);
                xa_erase(&dpll_pin_xa, pin->id);
                dpll_pin_prop_free(&pin->prop);
-               kfree(pin);
+               kfree_rcu(pin, rcu);
        }
        mutex_unlock(&dpll_lock);
 }
 
  * @prop:              pin properties copied from the registerer
  * @rclk_dev_name:     holds name of device when pin can recover clock from it
  * @refcount:          refcount
+ * @rcu:               rcu_head for kfree_rcu()
  **/
 struct dpll_pin {
        u32 id;
        struct xarray parent_refs;
        struct dpll_pin_properties prop;
        refcount_t refcount;
+       struct rcu_head rcu;
 };
 
 /**
 
 #include <uapi/linux/dpll.h>
 #include <linux/device.h>
 #include <linux/netlink.h>
+#include <linux/netdevice.h>
+#include <linux/rtnetlink.h>
 
 struct dpll_device;
 struct dpll_pin;
 
 int dpll_pin_change_ntf(struct dpll_pin *pin);
 
+static inline struct dpll_pin *netdev_dpll_pin(const struct net_device *dev)
+{
+#if IS_ENABLED(CONFIG_DPLL)
+       return rcu_dereference_rtnl(dev->dpll_pin);
+#else
+       return NULL;
+#endif
+}
+
 #endif
 
        struct devlink_port     *devlink_port;
 
 #if IS_ENABLED(CONFIG_DPLL)
-       struct dpll_pin         *dpll_pin;
+       struct dpll_pin __rcu   *dpll_pin;
 #endif
 #if IS_ENABLED(CONFIG_PAGE_POOL)
        /** @page_pools: page pools created for this netdevice */
 void netdev_dpll_pin_set(struct net_device *dev, struct dpll_pin *dpll_pin);
 void netdev_dpll_pin_clear(struct net_device *dev);
 
-static inline struct dpll_pin *netdev_dpll_pin(const struct net_device *dev)
-{
-#if IS_ENABLED(CONFIG_DPLL)
-       return dev->dpll_pin;
-#else
-       return NULL;
-#endif
-}
-
 struct sk_buff *validate_xmit_skb_list(struct sk_buff *skb, struct net_device *dev, bool *again);
 struct sk_buff *dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev,
                                    struct netdev_queue *txq, int *ret);
 
 {
 #if IS_ENABLED(CONFIG_DPLL)
        rtnl_lock();
-       dev->dpll_pin = dpll_pin;
+       rcu_assign_pointer(dev->dpll_pin, dpll_pin);
        rtnl_unlock();
 #endif
 }