]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
net: ethtool: netlink: Allow NULL nlattrs when getting a phy_device
authorMaxime Chevallier <maxime.chevallier@bootlin.com>
Sat, 1 Mar 2025 14:11:13 +0000 (15:11 +0100)
committerJakub Kicinski <kuba@kernel.org>
Wed, 5 Mar 2025 01:12:01 +0000 (17:12 -0800)
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: c15e065b46dc ("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>
net/ethtool/cabletest.c
net/ethtool/linkstate.c
net/ethtool/netlink.c
net/ethtool/netlink.h
net/ethtool/phy.c
net/ethtool/plca.c
net/ethtool/pse-pd.c
net/ethtool/stats.c
net/ethtool/strset.c

index f22051f33868ac1155252a8605a4ff8a858e5dbe..84096f6b0236e8675a5278a79f3c9e2d5602ab88 100644 (file)
@@ -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;
index af19e1bed303f1484c51f7e085b848280f1b2324..05a5f72c99fab14a1ae096b2dd76a10363bc29b6 100644 (file)
@@ -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);
index b4c45207fa32efa40b11717fc8e3c5982ea747ed..734849a573691d8240365f1c7cafbf905c1a5692 100644 (file)
@@ -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);
        }
index ff69ca0715dea61c4ac3513d37d9b7e1f12136f3..ec6ab5443a6f2eb3e0ec3c3f4c78a432828d277b 100644 (file)
@@ -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);
 
 /**
index ed8f690f6bac81d43d02da2d2f58e30f2e255967..e067cc234419dc5babcf0aee8576def1db6727c6 100644 (file)
@@ -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;
index d95d92f173a6d2a6ab6a4c89b3b0e9ba599d014f..e1f7820a6158f489c8ad55b6c1a3e77dca600ed4 100644 (file)
@@ -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)) {
index 2819e2ba6be2d8a47c69725f039bc4b630f23acd..4f6b99eab2a6c346af6d01166bf6721ab66e9cb3 100644 (file)
@@ -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)
index 038a2558f0520458702bf73d9818f6e3e0e07539..3ca8eb2a3b314ae675beb37106a45a8c97cf4309 100644 (file)
@@ -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);
index 6b76c05caba4d8f0fb23adb3414a92b4e636aed8..f6a67109beda1bcb7e77ad23443981c3f6ed0f0b 100644 (file)
@@ -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 */