forked from mirrors/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: 5f18426928 ("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>
			
			
This commit is contained in:
		
							parent
							
								
									0e67899abf
								
							
						
					
					
						commit
						0d60d8df6f
					
				
					 5 changed files with 16 additions and 12 deletions
				
			
		|  | @ -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 { | ||||||
|  * @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 { | ||||||
| 	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 @@ | ||||||
| #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); | ||||||
| 
 | 
 | ||||||
| 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 { | ||||||
| 	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); | ||||||
| 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 | ||||||
| { | { | ||||||
| #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 | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Eric Dumazet
						Eric Dumazet