From: Cheng Xu <chengyou@linux.alibaba.com> mainline inclusion from mainline-v7.3-rc1 commit a52eeff32024f190b3bdc99088c7becccd4fa60b category: bugfix bugzilla: https://atomgit.com/src-openeuler/kernel/issues/19471 CVE: CVE-2026-90308 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=... -------------------------------- AE QP fatal events and iWARP CM paths load QPs from dev->qp_xa and then use or reference them outside the xarray lock. erdma_destroy_qp() can drop the destroy-path reference and free QP resources while such a lookup is in flight. Add erdma_qp_get_by_qpn() to acquire a kref under the xarray lock with kref_get_unless_zero(). Remove the QP from the xarray before dropping the destroy-path reference so no new lookup can acquire it while destruction waits for existing users. Fixes: 155055771704 ("RDMA/erdma: Add verbs implementation") Signed-off-by: Cheng Xu <chengyou@linux.alibaba.com> Link: https://patch.msgid.link/20260730124357.12976-2-chengyou@linux.alibaba.com Signed-off-by: Leon Romanovsky <leon@kernel.org> Conflicts: drivers/infiniband/hw/erdma/erdma_verbs.c [In erdma_destroy_qp() the downstream tree keeps its own local declaration order (struct erdma_qp_attrs qp_attrs plus a later struct erdma_cmdq_destroy_qp_req req) and its own internal modify-qp helper, so the only adaptation is adding the new unsigned long flags declaration; the xa_erase change is applied unchanged.] Co-authored-by: BackportAgent@deepseek-v4.1-flash Signed-off-by: Hulk Robot <hulkrobot@huawei.com> Signed-off-by: Tang Hui <tanghui20@huawei.com> --- drivers/infiniband/hw/erdma/erdma_cm.c | 6 ++---- drivers/infiniband/hw/erdma/erdma_eq.c | 3 ++- drivers/infiniband/hw/erdma/erdma_verbs.c | 6 +++++- drivers/infiniband/hw/erdma/erdma_verbs.h | 15 +++++++++++++++ 4 files changed, 24 insertions(+), 6 deletions(-) diff --git a/drivers/infiniband/hw/erdma/erdma_cm.c b/drivers/infiniband/hw/erdma/erdma_cm.c index e349e8d2fb50a..c747f77c1bdbd 100644 --- a/drivers/infiniband/hw/erdma/erdma_cm.c +++ b/drivers/infiniband/hw/erdma/erdma_cm.c @@ -1017,10 +1017,9 @@ int erdma_connect(struct iw_cm_id *id, struct iw_cm_conn_param *params) if (laddr->sa_family != AF_INET || raddr->sa_family != AF_INET) return -EAFNOSUPPORT; - qp = find_qp_by_qpn(dev, params->qpn); + qp = erdma_qp_get_by_qpn(dev, params->qpn); if (!qp) return -ENOENT; - erdma_qp_get(qp); ret = sock_create(AF_INET, SOCK_STREAM, IPPROTO_TCP, &s); if (ret < 0) @@ -1149,10 +1148,9 @@ int erdma_accept(struct iw_cm_id *id, struct iw_cm_conn_param *params) return -ECONNRESET; } - qp = find_qp_by_qpn(dev, params->qpn); + qp = erdma_qp_get_by_qpn(dev, params->qpn); if (!qp) return -ENOENT; - erdma_qp_get(qp); down_write(&qp->state_lock); if (qp->attrs.state > ERDMA_QP_STATE_RTR) { diff --git a/drivers/infiniband/hw/erdma/erdma_eq.c b/drivers/infiniband/hw/erdma/erdma_eq.c index d59e2de4e4e10..eb695f10ea395 100644 --- a/drivers/infiniband/hw/erdma/erdma_eq.c +++ b/drivers/infiniband/hw/erdma/erdma_eq.c @@ -65,7 +65,7 @@ void erdma_aeq_event_handler(struct erdma_dev *dev) erdma_cq_put(cq); } else { qpn = le32_to_cpu(aeqe->event_data0); - qp = find_qp_by_qpn(dev, qpn); + qp = erdma_qp_get_by_qpn(dev, qpn); if (!qp) continue; @@ -75,6 +75,7 @@ void erdma_aeq_event_handler(struct erdma_dev *dev) if (qp->ibqp.event_handler) qp->ibqp.event_handler(&event, qp->ibqp.qp_context); + erdma_qp_put(qp); } } diff --git a/drivers/infiniband/hw/erdma/erdma_verbs.c b/drivers/infiniband/hw/erdma/erdma_verbs.c index 0c66d2bed569b..8aa830aee88bd 100644 --- a/drivers/infiniband/hw/erdma/erdma_verbs.c +++ b/drivers/infiniband/hw/erdma/erdma_verbs.c @@ -1266,6 +1266,7 @@ int erdma_destroy_qp(struct ib_qp *ibqp, struct ib_udata *udata) struct erdma_ucontext *ctx = rdma_udata_to_drv_context( udata, struct erdma_ucontext, ibucontext); struct erdma_qp_attrs qp_attrs; + unsigned long flags; int err; struct erdma_cmdq_destroy_qp_req req; @@ -1284,6 +1285,10 @@ int erdma_destroy_qp(struct ib_qp *ibqp, struct ib_udata *udata) if (err) return err; + xa_lock_irqsave(&dev->qp_xa, flags); + __xa_erase(&dev->qp_xa, QP_ID(qp)); + xa_unlock_irqrestore(&dev->qp_xa, flags); + erdma_qp_put(qp); wait_for_completion(&qp->safe_free); @@ -1306,7 +1311,6 @@ int erdma_destroy_qp(struct ib_qp *ibqp, struct ib_udata *udata) if (qp->cep) erdma_cep_put(qp->cep); - xa_erase(&dev->qp_xa, QP_ID(qp)); return 0; } diff --git a/drivers/infiniband/hw/erdma/erdma_verbs.h b/drivers/infiniband/hw/erdma/erdma_verbs.h index 10b9b8a5f6a8b..687017531b958 100644 --- a/drivers/infiniband/hw/erdma/erdma_verbs.h +++ b/drivers/infiniband/hw/erdma/erdma_verbs.h @@ -280,6 +280,21 @@ static inline struct erdma_qp *find_qp_by_qpn(struct erdma_dev *dev, int id) return (struct erdma_qp *)xa_load(&dev->qp_xa, id); } +static inline struct erdma_qp *erdma_qp_get_by_qpn(struct erdma_dev *dev, + int id) +{ + struct erdma_qp *qp; + unsigned long flags; + + xa_lock_irqsave(&dev->qp_xa, flags); + qp = xa_load(&dev->qp_xa, id); + if (qp && !kref_get_unless_zero(&qp->ref)) + qp = NULL; + xa_unlock_irqrestore(&dev->qp_xa, flags); + + return qp; +} + static inline struct erdma_cq *erdma_cq_get_by_cqn(struct erdma_dev *dev, int id) { -- 2.34.1