mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	net: ethtool: netlink: Allow NULL nlattrs when getting a phy_device
ethnl_req_get_phydev() is used to lookup a phy_device, in the case an
ethtool netlink command targets a specific phydev within a netdev's
topology.
It takes as a parameter a const struct nlattr *header that's used for
error handling :
       if (!phydev) {
               NL_SET_ERR_MSG_ATTR(extack, header,
                                   "no phy matching phyindex");
               return ERR_PTR(-ENODEV);
       }
In the notify path after a ->set operation however, there's no request
attributes available.
The typical callsite for the above function looks like:
	phydev = ethnl_req_get_phydev(req_base, tb[ETHTOOL_A_XXX_HEADER],
				      info->extack);
So, when tb is NULL (such as in the ethnl notify path), we have a nice
crash.
It turns out that there's only the PLCA command that is in that case, as
the other phydev-specific commands don't have a notification.
This commit fixes the crash by passing the cmd index and the nlattr
array separately, allowing NULL-checking it directly inside the helper.
Fixes: c15e065b46 ("net: ethtool: Allow passing a phy index for some commands")
Signed-off-by: Maxime Chevallier <maxime.chevallier@bootlin.com>
Reviewed-by: Kory Maincent <kory.maincent@bootlin.com>
Reported-by: Parthiban Veerasooran <parthiban.veerasooran@microchip.com>
Link: https://patch.msgid.link/20250301141114.97204-1-maxime.chevallier@bootlin.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
			
			
This commit is contained in:
		
							parent
							
								
									4c2d14c40a
								
							
						
					
					
						commit
						637399bf7e
					
				
					 9 changed files with 19 additions and 18 deletions
				
			
		| 
						 | 
				
			
			@ -72,8 +72,8 @@ int ethnl_act_cable_test(struct sk_buff *skb, struct genl_info *info)
 | 
			
		|||
	dev = req_info.dev;
 | 
			
		||||
 | 
			
		||||
	rtnl_lock();
 | 
			
		||||
	phydev = ethnl_req_get_phydev(&req_info,
 | 
			
		||||
				      tb[ETHTOOL_A_CABLE_TEST_HEADER],
 | 
			
		||||
	phydev = ethnl_req_get_phydev(&req_info, tb,
 | 
			
		||||
				      ETHTOOL_A_CABLE_TEST_HEADER,
 | 
			
		||||
				      info->extack);
 | 
			
		||||
	if (IS_ERR_OR_NULL(phydev)) {
 | 
			
		||||
		ret = -EOPNOTSUPP;
 | 
			
		||||
| 
						 | 
				
			
			@ -339,8 +339,8 @@ int ethnl_act_cable_test_tdr(struct sk_buff *skb, struct genl_info *info)
 | 
			
		|||
		goto out_dev_put;
 | 
			
		||||
 | 
			
		||||
	rtnl_lock();
 | 
			
		||||
	phydev = ethnl_req_get_phydev(&req_info,
 | 
			
		||||
				      tb[ETHTOOL_A_CABLE_TEST_TDR_HEADER],
 | 
			
		||||
	phydev = ethnl_req_get_phydev(&req_info, tb,
 | 
			
		||||
				      ETHTOOL_A_CABLE_TEST_TDR_HEADER,
 | 
			
		||||
				      info->extack);
 | 
			
		||||
	if (IS_ERR_OR_NULL(phydev)) {
 | 
			
		||||
		ret = -EOPNOTSUPP;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -103,7 +103,7 @@ static int linkstate_prepare_data(const struct ethnl_req_info *req_base,
 | 
			
		|||
	struct phy_device *phydev;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	phydev = ethnl_req_get_phydev(req_base, tb[ETHTOOL_A_LINKSTATE_HEADER],
 | 
			
		||||
	phydev = ethnl_req_get_phydev(req_base, tb, ETHTOOL_A_LINKSTATE_HEADER,
 | 
			
		||||
				      info->extack);
 | 
			
		||||
	if (IS_ERR(phydev)) {
 | 
			
		||||
		ret = PTR_ERR(phydev);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -211,7 +211,7 @@ int ethnl_parse_header_dev_get(struct ethnl_req_info *req_info,
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
struct phy_device *ethnl_req_get_phydev(const struct ethnl_req_info *req_info,
 | 
			
		||||
					const struct nlattr *header,
 | 
			
		||||
					struct nlattr **tb, unsigned int header,
 | 
			
		||||
					struct netlink_ext_ack *extack)
 | 
			
		||||
{
 | 
			
		||||
	struct phy_device *phydev;
 | 
			
		||||
| 
						 | 
				
			
			@ -225,8 +225,8 @@ struct phy_device *ethnl_req_get_phydev(const struct ethnl_req_info *req_info,
 | 
			
		|||
		return req_info->dev->phydev;
 | 
			
		||||
 | 
			
		||||
	phydev = phy_link_topo_get_phy(req_info->dev, req_info->phy_index);
 | 
			
		||||
	if (!phydev) {
 | 
			
		||||
		NL_SET_ERR_MSG_ATTR(extack, header,
 | 
			
		||||
	if (!phydev && tb) {
 | 
			
		||||
		NL_SET_ERR_MSG_ATTR(extack, tb[header],
 | 
			
		||||
				    "no phy matching phyindex");
 | 
			
		||||
		return ERR_PTR(-ENODEV);
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -275,7 +275,8 @@ static inline void ethnl_parse_header_dev_put(struct ethnl_req_info *req_info)
 | 
			
		|||
 * ethnl_req_get_phydev() - Gets the phy_device targeted by this request,
 | 
			
		||||
 *			    if any. Must be called under rntl_lock().
 | 
			
		||||
 * @req_info:	The ethnl request to get the phy from.
 | 
			
		||||
 * @header:	The netlink header, used for error reporting.
 | 
			
		||||
 * @tb:		The netlink attributes array, for error reporting.
 | 
			
		||||
 * @header:	The netlink header index, used for error reporting.
 | 
			
		||||
 * @extack:	The netlink extended ACK, for error reporting.
 | 
			
		||||
 *
 | 
			
		||||
 * The caller must hold RTNL, until it's done interacting with the returned
 | 
			
		||||
| 
						 | 
				
			
			@ -289,7 +290,7 @@ static inline void ethnl_parse_header_dev_put(struct ethnl_req_info *req_info)
 | 
			
		|||
 *	   is returned.
 | 
			
		||||
 */
 | 
			
		||||
struct phy_device *ethnl_req_get_phydev(const struct ethnl_req_info *req_info,
 | 
			
		||||
					const struct nlattr *header,
 | 
			
		||||
					struct nlattr **tb, unsigned int header,
 | 
			
		||||
					struct netlink_ext_ack *extack);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -125,7 +125,7 @@ static int ethnl_phy_parse_request(struct ethnl_req_info *req_base,
 | 
			
		|||
	struct phy_req_info *req_info = PHY_REQINFO(req_base);
 | 
			
		||||
	struct phy_device *phydev;
 | 
			
		||||
 | 
			
		||||
	phydev = ethnl_req_get_phydev(req_base, tb[ETHTOOL_A_PHY_HEADER],
 | 
			
		||||
	phydev = ethnl_req_get_phydev(req_base, tb, ETHTOOL_A_PHY_HEADER,
 | 
			
		||||
				      extack);
 | 
			
		||||
	if (!phydev)
 | 
			
		||||
		return 0;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -62,7 +62,7 @@ static int plca_get_cfg_prepare_data(const struct ethnl_req_info *req_base,
 | 
			
		|||
	struct phy_device *phydev;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	phydev = ethnl_req_get_phydev(req_base, tb[ETHTOOL_A_PLCA_HEADER],
 | 
			
		||||
	phydev = ethnl_req_get_phydev(req_base, tb, ETHTOOL_A_PLCA_HEADER,
 | 
			
		||||
				      info->extack);
 | 
			
		||||
	// check that the PHY device is available and connected
 | 
			
		||||
	if (IS_ERR_OR_NULL(phydev)) {
 | 
			
		||||
| 
						 | 
				
			
			@ -152,7 +152,7 @@ ethnl_set_plca(struct ethnl_req_info *req_info, struct genl_info *info)
 | 
			
		|||
	bool mod = false;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	phydev = ethnl_req_get_phydev(req_info, tb[ETHTOOL_A_PLCA_HEADER],
 | 
			
		||||
	phydev = ethnl_req_get_phydev(req_info, tb, ETHTOOL_A_PLCA_HEADER,
 | 
			
		||||
				      info->extack);
 | 
			
		||||
	// check that the PHY device is available and connected
 | 
			
		||||
	if (IS_ERR_OR_NULL(phydev))
 | 
			
		||||
| 
						 | 
				
			
			@ -211,7 +211,7 @@ static int plca_get_status_prepare_data(const struct ethnl_req_info *req_base,
 | 
			
		|||
	struct phy_device *phydev;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	phydev = ethnl_req_get_phydev(req_base, tb[ETHTOOL_A_PLCA_HEADER],
 | 
			
		||||
	phydev = ethnl_req_get_phydev(req_base, tb, ETHTOOL_A_PLCA_HEADER,
 | 
			
		||||
				      info->extack);
 | 
			
		||||
	// check that the PHY device is available and connected
 | 
			
		||||
	if (IS_ERR_OR_NULL(phydev)) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -64,7 +64,7 @@ static int pse_prepare_data(const struct ethnl_req_info *req_base,
 | 
			
		|||
	if (ret < 0)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	phydev = ethnl_req_get_phydev(req_base, tb[ETHTOOL_A_PSE_HEADER],
 | 
			
		||||
	phydev = ethnl_req_get_phydev(req_base, tb, ETHTOOL_A_PSE_HEADER,
 | 
			
		||||
				      info->extack);
 | 
			
		||||
	if (IS_ERR(phydev))
 | 
			
		||||
		return -ENODEV;
 | 
			
		||||
| 
						 | 
				
			
			@ -261,7 +261,7 @@ ethnl_set_pse(struct ethnl_req_info *req_info, struct genl_info *info)
 | 
			
		|||
	struct phy_device *phydev;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	phydev = ethnl_req_get_phydev(req_info, tb[ETHTOOL_A_PSE_HEADER],
 | 
			
		||||
	phydev = ethnl_req_get_phydev(req_info, tb, ETHTOOL_A_PSE_HEADER,
 | 
			
		||||
				      info->extack);
 | 
			
		||||
	ret = ethnl_set_pse_validate(phydev, info);
 | 
			
		||||
	if (ret)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -138,7 +138,7 @@ static int stats_prepare_data(const struct ethnl_req_info *req_base,
 | 
			
		|||
	struct phy_device *phydev;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	phydev = ethnl_req_get_phydev(req_base, tb[ETHTOOL_A_STATS_HEADER],
 | 
			
		||||
	phydev = ethnl_req_get_phydev(req_base, tb, ETHTOOL_A_STATS_HEADER,
 | 
			
		||||
				      info->extack);
 | 
			
		||||
	if (IS_ERR(phydev))
 | 
			
		||||
		return PTR_ERR(phydev);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -309,7 +309,7 @@ static int strset_prepare_data(const struct ethnl_req_info *req_base,
 | 
			
		|||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	phydev = ethnl_req_get_phydev(req_base, tb[ETHTOOL_A_HEADER_FLAGS],
 | 
			
		||||
	phydev = ethnl_req_get_phydev(req_base, tb, ETHTOOL_A_HEADER_FLAGS,
 | 
			
		||||
				      info->extack);
 | 
			
		||||
 | 
			
		||||
	/* phydev can be NULL, check for errors only */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue