From 203675526b14d9556eeb4212536ebcfc81691c1b Mon Sep 17 00:00:00 2001 From: Wenpeng Liang Date: Mon, 17 Jan 2022 20:43:38 +0800 Subject: libhns: Fix out-of-bounds write when filling inline data into extended sge space If the buf to store inline data is in the last page of the extended sge space, filling the entire inline data into the extended sge space at one time may result in out-of-bounds writing. When the remaining space at the end of the extended sge is not enough to accommodate the entire inline data, the inline data needs to be filled into the extended sge space in two steps: (1) The front part of the inline data is filled into the remaining space at the end of the extended sge. (2) The remaining inline data is filled into the header space of the extended sge. Fixes: b7814b7b9715("libhns: Support inline data in extented sge space for RC") Signed-off-by: Wenpeng Liang --- providers/hns/hns_roce_u_hw_v2.c | 40 ++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/providers/hns/hns_roce_u_hw_v2.c b/providers/hns/hns_roce_u_hw_v2.c index e39ee7f..20745dc 100644 --- a/providers/hns/hns_roce_u_hw_v2.c +++ b/providers/hns/hns_roce_u_hw_v2.c @@ -772,21 +772,43 @@ static int fill_ext_sge_inl_data(struct hns_roce_qp *qp, struct hns_roce_sge_info *sge_info) { unsigned int sge_sz = sizeof(struct hns_roce_v2_wqe_data_seg); - void *dseg; + unsigned int sge_mask = qp->ex_sge.sge_cnt - 1; + void *dst_addr, *src_addr, *tail_bound_addr; + uint32_t src_len, tail_len; int i; + if (sge_info->total_len > qp->sq.max_gs * sge_sz) return EINVAL; - dseg = get_send_sge_ex(qp, sge_info->start_idx); + dst_addr = get_send_sge_ex(qp, sge_info->start_idx & sge_mask); + tail_bound_addr = get_send_sge_ex(qp, qp->ex_sge.sge_cnt & sge_mask); for (i = 0; i < wr->num_sge; i++) { - memcpy(dseg, (void *)(uintptr_t)wr->sg_list[i].addr, - wr->sg_list[i].length); - dseg += wr->sg_list[i].length; + tail_len = (uintptr_t)tail_bound_addr - (uintptr_t)dst_addr; + + src_addr = (void *)(uintptr_t)wr->sg_list[i].addr; + src_len = wr->sg_list[i].length; + + if (src_len < tail_len) { + memcpy(dst_addr, src_addr, src_len); + dst_addr += src_len; + } else if (src_len == tail_len) { + memcpy(dst_addr, src_addr, src_len); + dst_addr = get_send_sge_ex(qp, 0); + } else { + memcpy(dst_addr, src_addr, tail_len); + dst_addr = get_send_sge_ex(qp, 0); + src_addr += tail_len; + src_len -= tail_len; + + memcpy(dst_addr, src_addr, src_len); + dst_addr += src_len; + } } - sge_info->start_idx += DIV_ROUND_UP(sge_info->total_len, sge_sz); + sge_info->valid_num = DIV_ROUND_UP(sge_info->total_len, sge_sz); + sge_info->start_idx += sge_info->valid_num; return 0; } @@ -828,7 +850,6 @@ static int set_ud_inl(struct hns_roce_qp *qp, const struct ibv_send_wr *wr, struct hns_roce_ud_sq_wqe *ud_sq_wqe, struct hns_roce_sge_info *sge_info) { - unsigned int sge_idx = sge_info->start_idx; int ret; if (!check_inl_data_len(qp, sge_info->total_len)) @@ -845,8 +866,6 @@ static int set_ud_inl(struct hns_roce_qp *qp, const struct ibv_send_wr *wr, if (ret) return ret; - sge_info->valid_num = sge_info->start_idx - sge_idx; - hr_reg_write(ud_sq_wqe, UDWQE_SGE_NUM, sge_info->valid_num); } @@ -969,7 +988,6 @@ static int set_rc_inl(struct hns_roce_qp *qp, const struct ibv_send_wr *wr, struct hns_roce_rc_sq_wqe *rc_sq_wqe, struct hns_roce_sge_info *sge_info) { - unsigned int sge_idx = sge_info->start_idx; void *dseg = rc_sq_wqe; int ret; int i; @@ -997,8 +1015,6 @@ static int set_rc_inl(struct hns_roce_qp *qp, const struct ibv_send_wr *wr, if (ret) return ret; - sge_info->valid_num = sge_info->start_idx - sge_idx; - hr_reg_write(rc_sq_wqe, RCWQE_SGE_NUM, sge_info->valid_num); } -- 2.27.0