Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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: 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>

authored by

Eric Dumazet and committed by
Jakub Kicinski
0d60d8df 0e67899a

+16 -12
+1 -1
drivers/dpll/dpll_core.c
··· 564 564 xa_destroy(&pin->parent_refs); 565 565 xa_erase(&dpll_pin_xa, pin->id); 566 566 dpll_pin_prop_free(&pin->prop); 567 - kfree(pin); 567 + kfree_rcu(pin, rcu); 568 568 } 569 569 mutex_unlock(&dpll_lock); 570 570 }
+2
drivers/dpll/dpll_core.h
··· 47 47 * @prop: pin properties copied from the registerer 48 48 * @rclk_dev_name: holds name of device when pin can recover clock from it 49 49 * @refcount: refcount 50 + * @rcu: rcu_head for kfree_rcu() 50 51 **/ 51 52 struct dpll_pin { 52 53 u32 id; ··· 58 57 struct xarray parent_refs; 59 58 struct dpll_pin_properties prop; 60 59 refcount_t refcount; 60 + struct rcu_head rcu; 61 61 }; 62 62 63 63 /**
+11
include/linux/dpll.h
··· 10 10 #include <uapi/linux/dpll.h> 11 11 #include <linux/device.h> 12 12 #include <linux/netlink.h> 13 + #include <linux/netdevice.h> 14 + #include <linux/rtnetlink.h> 13 15 14 16 struct dpll_device; 15 17 struct dpll_pin; ··· 168 166 int dpll_device_change_ntf(struct dpll_device *dpll); 169 167 170 168 int dpll_pin_change_ntf(struct dpll_pin *pin); 169 + 170 + static inline struct dpll_pin *netdev_dpll_pin(const struct net_device *dev) 171 + { 172 + #if IS_ENABLED(CONFIG_DPLL) 173 + return rcu_dereference_rtnl(dev->dpll_pin); 174 + #else 175 + return NULL; 176 + #endif 177 + } 171 178 172 179 #endif
+1 -10
include/linux/netdevice.h
··· 2469 2469 struct devlink_port *devlink_port; 2470 2470 2471 2471 #if IS_ENABLED(CONFIG_DPLL) 2472 - struct dpll_pin *dpll_pin; 2472 + struct dpll_pin __rcu *dpll_pin; 2473 2473 #endif 2474 2474 #if IS_ENABLED(CONFIG_PAGE_POOL) 2475 2475 /** @page_pools: page pools created for this netdevice */ ··· 4034 4034 bool netdev_port_same_parent_id(struct net_device *a, struct net_device *b); 4035 4035 void netdev_dpll_pin_set(struct net_device *dev, struct dpll_pin *dpll_pin); 4036 4036 void netdev_dpll_pin_clear(struct net_device *dev); 4037 - 4038 - static inline struct dpll_pin *netdev_dpll_pin(const struct net_device *dev) 4039 - { 4040 - #if IS_ENABLED(CONFIG_DPLL) 4041 - return dev->dpll_pin; 4042 - #else 4043 - return NULL; 4044 - #endif 4045 - } 4046 4037 4047 4038 struct sk_buff *validate_xmit_skb_list(struct sk_buff *skb, struct net_device *dev, bool *again); 4048 4039 struct sk_buff *dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev,
+1 -1
net/core/dev.c
··· 9078 9078 { 9079 9079 #if IS_ENABLED(CONFIG_DPLL) 9080 9080 rtnl_lock(); 9081 - dev->dpll_pin = dpll_pin; 9081 + rcu_assign_pointer(dev->dpll_pin, dpll_pin); 9082 9082 rtnl_unlock(); 9083 9083 #endif 9084 9084 }