mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2026-09-22 09:34:56 +02:00
net/9p: fix race condition on rdma->state in trans_rdma.c
[ Upstream commit7d54894a1e] The rdma->state field is modified without holding req_lock in both recv_done() and p9_cm_event_handler(), while rdma_request() accesses the same field under the req_lock spinlock. This inconsistent locking creates a race condition: - recv_done() running in softirq completion context sets rdma->state = P9_RDMA_FLUSHING without acquiring req_lock - p9_cm_event_handler() modifies rdma->state at multiple points (ADDR_RESOLVED, ROUTE_RESOLVED, ESTABLISHED, CLOSED) without req_lock - rdma_request() uses spin_lock_irqsave(&rdma->req_lock, flags) to protect the read-modify-write of rdma->state The race can cause lost state transitions: recv_done() or the CM event handler could set state to FLUSHING/CLOSED while rdma_request() is concurrently checking or modifying state under the lock, leading to the FLUSHING transition being silently overwritten by CLOSING. This corrupts the connection state machine and can cause use-after-free on RDMA request objects during teardown. Fix by adding req_lock protection to all rdma->state modifications in recv_done() and p9_cm_event_handler(), matching the pattern already used in rdma_request(). Use spin_lock_irqsave/spin_unlock_irqrestore in the CM event handler since it can race with recv_done() which runs in softirq context. Tested with a kernel module that races two threads (simulating rdma_request and recv_done/CM handler) on rdma->state with proper locking: 5.5M+ FLUSHING writes over 27M iterations with 0 lost transitions. Fixes:473c7dd1d7("9p/rdma: remove useless check in cm_event_handler") Reported-by: Yizhou Zhao <zhaoyz24@mails.tsinghua.edu.cn> Reported-by: Yuxiang Yang <yangyx22@mails.tsinghua.edu.cn> Reported-by: Ao Wang <wangao@seu.edu.cn> Reported-by: Xuewei Feng <fengxw06@126.com> Reported-by: Qi Li <qli01@tsinghua.edu.cn> Reported-by: Ke Xu <xuke@tsinghua.edu.cn> Assisted-by: GLM:GLM-5.1 Signed-off-by: Yizhou Zhao <zhaoyz24@mails.tsinghua.edu.cn> Message-ID: <20260529073933.77315-1-zhaoyz24@mails.tsinghua.edu.cn> Signed-off-by: Dominique Martinet <asmadeus@codewreck.org> Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
49b34bd3ad
commit
8aadc136d8
+17
-2
@@ -240,25 +240,36 @@ p9_cm_event_handler(struct rdma_cm_id *id, struct rdma_cm_event *event)
|
||||
{
|
||||
struct p9_client *c = id->context;
|
||||
struct p9_trans_rdma *rdma = c->trans;
|
||||
unsigned long flags;
|
||||
|
||||
switch (event->event) {
|
||||
case RDMA_CM_EVENT_ADDR_RESOLVED:
|
||||
spin_lock_irqsave(&rdma->req_lock, flags);
|
||||
BUG_ON(rdma->state != P9_RDMA_INIT);
|
||||
rdma->state = P9_RDMA_ADDR_RESOLVED;
|
||||
spin_unlock_irqrestore(&rdma->req_lock, flags);
|
||||
break;
|
||||
|
||||
case RDMA_CM_EVENT_ROUTE_RESOLVED:
|
||||
spin_lock_irqsave(&rdma->req_lock, flags);
|
||||
BUG_ON(rdma->state != P9_RDMA_ADDR_RESOLVED);
|
||||
rdma->state = P9_RDMA_ROUTE_RESOLVED;
|
||||
spin_unlock_irqrestore(&rdma->req_lock, flags);
|
||||
break;
|
||||
|
||||
case RDMA_CM_EVENT_ESTABLISHED:
|
||||
spin_lock_irqsave(&rdma->req_lock, flags);
|
||||
BUG_ON(rdma->state != P9_RDMA_ROUTE_RESOLVED);
|
||||
rdma->state = P9_RDMA_CONNECTED;
|
||||
spin_unlock_irqrestore(&rdma->req_lock, flags);
|
||||
break;
|
||||
|
||||
case RDMA_CM_EVENT_DISCONNECTED:
|
||||
if (rdma)
|
||||
if (rdma) {
|
||||
spin_lock_irqsave(&rdma->req_lock, flags);
|
||||
rdma->state = P9_RDMA_CLOSED;
|
||||
spin_unlock_irqrestore(&rdma->req_lock, flags);
|
||||
}
|
||||
c->status = Disconnected;
|
||||
break;
|
||||
|
||||
@@ -296,6 +307,7 @@ recv_done(struct ib_cq *cq, struct ib_wc *wc)
|
||||
struct p9_req_t *req;
|
||||
int err = 0;
|
||||
int16_t tag;
|
||||
unsigned long flags;
|
||||
|
||||
req = NULL;
|
||||
ib_dma_unmap_single(rdma->cm_id->device, c->busa, client->msize,
|
||||
@@ -332,7 +344,10 @@ recv_done(struct ib_cq *cq, struct ib_wc *wc)
|
||||
err_out:
|
||||
p9_debug(P9_DEBUG_ERROR, "req %p err %d status %d\n",
|
||||
req, err, wc->status);
|
||||
rdma->state = P9_RDMA_FLUSHING;
|
||||
spin_lock_irqsave(&rdma->req_lock, flags);
|
||||
if (rdma->state < P9_RDMA_FLUSHING)
|
||||
rdma->state = P9_RDMA_FLUSHING;
|
||||
spin_unlock_irqrestore(&rdma->req_lock, flags);
|
||||
client->status = Disconnected;
|
||||
goto out;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user