Commit 637399bf authored by Maxime Chevallier's avatar Maxime Chevallier Committed by Jakub Kicinski
Browse files

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: c15e065b ("net: ethtool: Allow passing a phy index for some commands")
Signed-off-by: default avatarMaxime Chevallier <maxime.chevallier@bootlin.com>
Reviewed-by: default avatarKory Maincent <kory.maincent@bootlin.com>
Reported-by: default avatarParthiban Veerasooran <parthiban.veerasooran@microchip.com>
Link: https://patch.msgid.link/20250301141114.97204-1-maxime.chevallier@bootlin.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent 4c2d14c4
Loading
Loading
Loading
Loading
+4 −4
Original line number Diff line number Diff line
@@ -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;
+1 −1
Original line number Diff line number Diff line
@@ -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);
+3 −3
Original line number Diff line number Diff line
@@ -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);
	}
+3 −2
Original line number Diff line number Diff line
@@ -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);

/**
+1 −1
Original line number Diff line number Diff line
@@ -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;
Loading