Commit 0d60d8df authored by Eric Dumazet's avatar Eric Dumazet Committed by Jakub Kicinski

dpll: rely on rcu for netdev_dpll_pin()

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: 5f184269 ("netdev: expose DPLL pin handle for netdevice")
Signed-off-by: default avatarEric Dumazet <edumazet@google.com>
Cc: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
Cc: Vadim Fedorenko <vadim.fedorenko@linux.dev>
Reviewed-by: default avatarJiri Pirko <jiri@nvidia.com>
Link: https://lore.kernel.org/r/20240223123208.3543319-1-edumazet@google.comSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent 0e67899a
...@@ -564,7 +564,7 @@ void dpll_pin_put(struct dpll_pin *pin) ...@@ -564,7 +564,7 @@ void dpll_pin_put(struct dpll_pin *pin)
xa_destroy(&pin->parent_refs); xa_destroy(&pin->parent_refs);
xa_erase(&dpll_pin_xa, pin->id); xa_erase(&dpll_pin_xa, pin->id);
dpll_pin_prop_free(&pin->prop); dpll_pin_prop_free(&pin->prop);
kfree(pin); kfree_rcu(pin, rcu);
} }
mutex_unlock(&dpll_lock); mutex_unlock(&dpll_lock);
} }
......
...@@ -47,6 +47,7 @@ struct dpll_device { ...@@ -47,6 +47,7 @@ struct dpll_device {
* @prop: pin properties copied from the registerer * @prop: pin properties copied from the registerer
* @rclk_dev_name: holds name of device when pin can recover clock from it * @rclk_dev_name: holds name of device when pin can recover clock from it
* @refcount: refcount * @refcount: refcount
* @rcu: rcu_head for kfree_rcu()
**/ **/
struct dpll_pin { struct dpll_pin {
u32 id; u32 id;
...@@ -57,6 +58,7 @@ struct dpll_pin { ...@@ -57,6 +58,7 @@ struct dpll_pin {
struct xarray parent_refs; struct xarray parent_refs;
struct dpll_pin_properties prop; struct dpll_pin_properties prop;
refcount_t refcount; refcount_t refcount;
struct rcu_head rcu;
}; };
/** /**
......
...@@ -10,6 +10,8 @@ ...@@ -10,6 +10,8 @@
#include <uapi/linux/dpll.h> #include <uapi/linux/dpll.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/netlink.h> #include <linux/netlink.h>
#include <linux/netdevice.h>
#include <linux/rtnetlink.h>
struct dpll_device; struct dpll_device;
struct dpll_pin; struct dpll_pin;
...@@ -167,4 +169,13 @@ int dpll_device_change_ntf(struct dpll_device *dpll); ...@@ -167,4 +169,13 @@ int dpll_device_change_ntf(struct dpll_device *dpll);
int dpll_pin_change_ntf(struct dpll_pin *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 #endif
...@@ -2469,7 +2469,7 @@ struct net_device { ...@@ -2469,7 +2469,7 @@ struct net_device {
struct devlink_port *devlink_port; struct devlink_port *devlink_port;
#if IS_ENABLED(CONFIG_DPLL) #if IS_ENABLED(CONFIG_DPLL)
struct dpll_pin *dpll_pin; struct dpll_pin __rcu *dpll_pin;
#endif #endif
#if IS_ENABLED(CONFIG_PAGE_POOL) #if IS_ENABLED(CONFIG_PAGE_POOL)
/** @page_pools: page pools created for this netdevice */ /** @page_pools: page pools created for this netdevice */
...@@ -4035,15 +4035,6 @@ bool netdev_port_same_parent_id(struct net_device *a, struct net_device *b); ...@@ -4035,15 +4035,6 @@ bool netdev_port_same_parent_id(struct net_device *a, struct net_device *b);
void netdev_dpll_pin_set(struct net_device *dev, struct dpll_pin *dpll_pin); void netdev_dpll_pin_set(struct net_device *dev, struct dpll_pin *dpll_pin);
void netdev_dpll_pin_clear(struct net_device *dev); 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 *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 sk_buff *dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev,
struct netdev_queue *txq, int *ret); struct netdev_queue *txq, int *ret);
......
...@@ -9078,7 +9078,7 @@ static void netdev_dpll_pin_assign(struct net_device *dev, struct dpll_pin *dpll ...@@ -9078,7 +9078,7 @@ static void netdev_dpll_pin_assign(struct net_device *dev, struct dpll_pin *dpll
{ {
#if IS_ENABLED(CONFIG_DPLL) #if IS_ENABLED(CONFIG_DPLL)
rtnl_lock(); rtnl_lock();
dev->dpll_pin = dpll_pin; rcu_assign_pointer(dev->dpll_pin, dpll_pin);
rtnl_unlock(); rtnl_unlock();
#endif #endif
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment