| From 1b9a8b3e28cedf2b6b75fd2c0f668b29838ecace Mon Sep 17 00:00:00 2001 |
| From: Vasundhara Volam <vasundhara-v.volam@broadcom.com> |
| Date: Tue, 10 Dec 2019 02:49:09 -0500 |
| Subject: [PATCH] bnxt_en: Return error if FW returns more data than dump |
| length |
| |
| commit c74751f4c39232c31214ec6a3bc1c7e62f5c728b upstream. |
| |
| 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> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c |
| index b761a2e28a10..e6705f7cae40 100644 |
| --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c |
| +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c |
| @@ -3018,8 +3018,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)) |
| @@ -3073,7 +3080,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}; |
| @@ -3088,8 +3095,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) |
| @@ -3179,14 +3189,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); |
| @@ -3219,6 +3232,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); |
| @@ -3231,9 +3250,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); |
| @@ -3263,7 +3284,8 @@ static int bnxt_get_coredump(struct bnxt *bp, void *buf, u32 *dump_len) |
| 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; |
| } |
| |
| diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.h |
| index b5b65b3f8534..3998f6e809a9 100644 |
| --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.h |
| +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.h |
| @@ -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 { |
| -- |
| 2.7.4 |
| |