bnxt_en: Return error if FW returns more data than dump length
authorVasundhara Volam <vasundhara-v.volam@broadcom.com>
Tue, 10 Dec 2019 07:49:09 +0000 (02:49 -0500)
committerDavid S. Miller <davem@davemloft.net>
Wed, 11 Dec 2019 01:37:14 +0000 (17:37 -0800)
If any change happened in the configuration of VF in VM while
collecting live dump, there could be a race and firmware can return
more data than allocated dump length. Fix it by keeping track of
the accumulated core dump length copied so far and abort the copy
with error code if the next chunk of core dump will exceed the
original dump length.

Fixes: 6c5657d085ae ("bnxt_en: Add support for ethtool get dump.")
Signed-off-by: Vasundhara Volam <vasundhara-v.volam@broadcom.com>
Signed-off-by: Michael Chan <michael.chan@broadcom.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c
drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.h

index 2ccf79cdcb1ef91e176fdfcc8e1f237c92de478d..08d56ec7b68a503003c6cab305d56630803687ff 100644 (file)
@@ -3071,8 +3071,15 @@ static int bnxt_hwrm_dbg_dma_data(struct bnxt *bp, void *msg, int msg_len,
                        }
                }
 
-               if (info->dest_buf)
-                       memcpy(info->dest_buf + off, dma_buf, len);
+               if (info->dest_buf) {
+                       if ((info->seg_start + off + len) <=
+                           BNXT_COREDUMP_BUF_LEN(info->buf_len)) {
+                               memcpy(info->dest_buf + off, dma_buf, len);
+                       } else {
+                               rc = -ENOBUFS;
+                               break;
+                       }
+               }
 
                if (cmn_req->req_type ==
                                cpu_to_le16(HWRM_DBG_COREDUMP_RETRIEVE))
@@ -3126,7 +3133,7 @@ static int bnxt_hwrm_dbg_coredump_initiate(struct bnxt *bp, u16 component_id,
 
 static int bnxt_hwrm_dbg_coredump_retrieve(struct bnxt *bp, u16 component_id,
                                           u16 segment_id, u32 *seg_len,
-                                          void *buf, u32 offset)
+                                          void *buf, u32 buf_len, u32 offset)
 {
        struct hwrm_dbg_coredump_retrieve_input req = {0};
        struct bnxt_hwrm_dbg_dma_info info = {NULL};
@@ -3141,8 +3148,11 @@ static int bnxt_hwrm_dbg_coredump_retrieve(struct bnxt *bp, u16 component_id,
                                seq_no);
        info.data_len_off = offsetof(struct hwrm_dbg_coredump_retrieve_output,
                                     data_len);
-       if (buf)
+       if (buf) {
                info.dest_buf = buf + offset;
+               info.buf_len = buf_len;
+               info.seg_start = offset;
+       }
 
        rc = bnxt_hwrm_dbg_dma_data(bp, &req, sizeof(req), &info);
        if (!rc)
@@ -3232,14 +3242,17 @@ bnxt_fill_coredump_record(struct bnxt *bp, struct bnxt_coredump_record *record,
 static int bnxt_get_coredump(struct bnxt *bp, void *buf, u32 *dump_len)
 {
        u32 ver_get_resp_len = sizeof(struct hwrm_ver_get_output);
+       u32 offset = 0, seg_hdr_len, seg_record_len, buf_len = 0;
        struct coredump_segment_record *seg_record = NULL;
-       u32 offset = 0, seg_hdr_len, seg_record_len;
        struct bnxt_coredump_segment_hdr seg_hdr;
        struct bnxt_coredump coredump = {NULL};
        time64_t start_time;
        u16 start_utc;
        int rc = 0, i;
 
+       if (buf)
+               buf_len = *dump_len;
+
        start_time = ktime_get_real_seconds();
        start_utc = sys_tz.tz_minuteswest * 60;
        seg_hdr_len = sizeof(seg_hdr);
@@ -3272,6 +3285,12 @@ static int bnxt_get_coredump(struct bnxt *bp, void *buf, u32 *dump_len)
                u32 duration = 0, seg_len = 0;
                unsigned long start, end;
 
+               if (buf && ((offset + seg_hdr_len) >
+                           BNXT_COREDUMP_BUF_LEN(buf_len))) {
+                       rc = -ENOBUFS;
+                       goto err;
+               }
+
                start = jiffies;
 
                rc = bnxt_hwrm_dbg_coredump_initiate(bp, comp_id, seg_id);
@@ -3284,9 +3303,11 @@ static int bnxt_get_coredump(struct bnxt *bp, void *buf, u32 *dump_len)
 
                /* Write segment data into the buffer */
                rc = bnxt_hwrm_dbg_coredump_retrieve(bp, comp_id, seg_id,
-                                                    &seg_len, buf,
+                                                    &seg_len, buf, buf_len,
                                                     offset + seg_hdr_len);
-               if (rc)
+               if (rc && rc == -ENOBUFS)
+                       goto err;
+               else if (rc)
                        netdev_err(bp->dev,
                                   "Failed to retrieve coredump for seg = %d\n",
                                   seg_record->segment_id);
@@ -3316,7 +3337,8 @@ err:
                                          rc);
        kfree(coredump.data);
        *dump_len += sizeof(struct bnxt_coredump_record);
-
+       if (rc == -ENOBUFS)
+               netdev_err(bp->dev, "Firmware returned large coredump buffer");
        return rc;
 }
 
index 4428d0abcbc1a61b45037149e944595b8ff14f0c..3576d951727b6ff6986f88d300bfb6a8294aee3b 100644 (file)
@@ -31,6 +31,8 @@ struct bnxt_coredump {
        u16             total_segs;
 };
 
+#define BNXT_COREDUMP_BUF_LEN(len) ((len) - sizeof(struct bnxt_coredump_record))
+
 struct bnxt_hwrm_dbg_dma_info {
        void *dest_buf;
        int dest_buf_size;
@@ -38,6 +40,8 @@ struct bnxt_hwrm_dbg_dma_info {
        u16 seq_off;
        u16 data_len_off;
        u16 segs;
+       u32 seg_start;
+       u32 buf_len;
 };
 
 struct hwrm_dbg_cmn_input {