be2net: Fix buffer overflow in be_get_module_eeprom
authorHristo Venev <hristo@venev.name>
Sat, 16 Jul 2022 08:51:34 +0000 (11:51 +0300)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 29 Jul 2022 15:25:21 +0000 (17:25 +0200)
[ Upstream commit d7241f679a59cfe27f92cb5c6272cb429fb1f7ec ]

be_cmd_read_port_transceiver_data assumes that it is given a buffer that
is at least PAGE_DATA_LEN long, or twice that if the module supports SFF
8472. However, this is not always the case.

Fix this by passing the desired offset and length to
be_cmd_read_port_transceiver_data so that we only copy the bytes once.

Fixes: e36edd9d26cf ("be2net: add ethtool "-m" option support")
Signed-off-by: Hristo Venev <hristo@venev.name>
Link: https://lore.kernel.org/r/20220716085134.6095-1-hristo@venev.name
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/net/ethernet/emulex/benet/be_cmds.c
drivers/net/ethernet/emulex/benet/be_cmds.h
drivers/net/ethernet/emulex/benet/be_ethtool.c

index 649c5c4..1288b5e 100644 (file)
@@ -2287,7 +2287,7 @@ err:
 
 /* Uses sync mcc */
 int be_cmd_read_port_transceiver_data(struct be_adapter *adapter,
-                                     u8 page_num, u8 *data)
+                                     u8 page_num, u32 off, u32 len, u8 *data)
 {
        struct be_dma_mem cmd;
        struct be_mcc_wrb *wrb;
@@ -2321,10 +2321,10 @@ int be_cmd_read_port_transceiver_data(struct be_adapter *adapter,
        req->port = cpu_to_le32(adapter->hba_port_num);
        req->page_num = cpu_to_le32(page_num);
        status = be_mcc_notify_wait(adapter);
-       if (!status) {
+       if (!status && len > 0) {
                struct be_cmd_resp_port_type *resp = cmd.va;
 
-               memcpy(data, resp->page_data, PAGE_DATA_LEN);
+               memcpy(data, resp->page_data + off, len);
        }
 err:
        mutex_unlock(&adapter->mcc_lock);
@@ -2415,7 +2415,7 @@ int be_cmd_query_cable_type(struct be_adapter *adapter)
        int status;
 
        status = be_cmd_read_port_transceiver_data(adapter, TR_PAGE_A0,
-                                                  page_data);
+                                                  0, PAGE_DATA_LEN, page_data);
        if (!status) {
                switch (adapter->phy.interface_type) {
                case PHY_TYPE_QSFP:
@@ -2440,7 +2440,7 @@ int be_cmd_query_sfp_info(struct be_adapter *adapter)
        int status;
 
        status = be_cmd_read_port_transceiver_data(adapter, TR_PAGE_A0,
-                                                  page_data);
+                                                  0, PAGE_DATA_LEN, page_data);
        if (!status) {
                strlcpy(adapter->phy.vendor_name, page_data +
                        SFP_VENDOR_NAME_OFFSET, SFP_VENDOR_NAME_LEN - 1);
index c30d6d6..9e17d6a 100644 (file)
@@ -2427,7 +2427,7 @@ int be_cmd_set_beacon_state(struct be_adapter *adapter, u8 port_num, u8 beacon,
 int be_cmd_get_beacon_state(struct be_adapter *adapter, u8 port_num,
                            u32 *state);
 int be_cmd_read_port_transceiver_data(struct be_adapter *adapter,
-                                     u8 page_num, u8 *data);
+                                     u8 page_num, u32 off, u32 len, u8 *data);
 int be_cmd_query_cable_type(struct be_adapter *adapter);
 int be_cmd_query_sfp_info(struct be_adapter *adapter);
 int lancer_cmd_read_object(struct be_adapter *adapter, struct be_dma_mem *cmd,
index f995530..010a002 100644 (file)
@@ -1342,7 +1342,7 @@ static int be_get_module_info(struct net_device *netdev,
                return -EOPNOTSUPP;
 
        status = be_cmd_read_port_transceiver_data(adapter, TR_PAGE_A0,
-                                                  page_data);
+                                                  0, PAGE_DATA_LEN, page_data);
        if (!status) {
                if (!page_data[SFP_PLUS_SFF_8472_COMP]) {
                        modinfo->type = ETH_MODULE_SFF_8079;
@@ -1360,25 +1360,32 @@ static int be_get_module_eeprom(struct net_device *netdev,
 {
        struct be_adapter *adapter = netdev_priv(netdev);
        int status;
+       u32 begin, end;
 
        if (!check_privilege(adapter, MAX_PRIVILEGES))
                return -EOPNOTSUPP;
 
-       status = be_cmd_read_port_transceiver_data(adapter, TR_PAGE_A0,
-                                                  data);
-       if (status)
-               goto err;
+       begin = eeprom->offset;
+       end = eeprom->offset + eeprom->len;
+
+       if (begin < PAGE_DATA_LEN) {
+               status = be_cmd_read_port_transceiver_data(adapter, TR_PAGE_A0, begin,
+                                                          min_t(u32, end, PAGE_DATA_LEN) - begin,
+                                                          data);
+               if (status)
+                       goto err;
+
+               data += PAGE_DATA_LEN - begin;
+               begin = PAGE_DATA_LEN;
+       }
 
-       if (eeprom->offset + eeprom->len > PAGE_DATA_LEN) {
-               status = be_cmd_read_port_transceiver_data(adapter,
-                                                          TR_PAGE_A2,
-                                                          data +
-                                                          PAGE_DATA_LEN);
+       if (end > PAGE_DATA_LEN) {
+               status = be_cmd_read_port_transceiver_data(adapter, TR_PAGE_A2,
+                                                          begin - PAGE_DATA_LEN,
+                                                          end - begin, data);
                if (status)
                        goto err;
        }
-       if (eeprom->offset)
-               memcpy(data, data + eeprom->offset, eeprom->len);
 err:
        return be_cmd_status(status);
 }