]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
9p/client: fix data race on req->status
authorDominique Martinet <asmadeus@codewreck.org>
Mon, 5 Dec 2022 12:39:01 +0000 (21:39 +0900)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 12 Jan 2023 11:02:36 +0000 (12:02 +0100)
[ Upstream commit 1a4f69ef15ec29b213e2b086b2502644e8ef76ee ]

KCSAN reported a race between writing req->status in p9_client_cb and
accessing it in p9_client_rpc's wait_event.

Accesses to req itself is protected by the data barrier (writing req
fields, write barrier, writing status // reading status, read barrier,
reading other req fields), but status accesses themselves apparently
also must be annotated properly with WRITE_ONCE/READ_ONCE when we
access it without locks.

Follows:
 - error paths writing status in various threads all can notify
p9_client_rpc, so these all also need WRITE_ONCE
 - there's a similar read loop in trans_virtio for zc case that also
needs READ_ONCE
 - other reads in trans_fd should be protected by the trans_fd lock and
lists state machine, as corresponding writers all are within trans_fd
and should be under the same lock. If KCSAN complains on them we likely
will have something else to fix as well, so it's better to leave them
unmarked and look again if required.

Link: https://lkml.kernel.org/r/20221205124756.426350-1-asmadeus@codewreck.org
Reported-by: Naresh Kamboju <naresh.kamboju@linaro.org>
Suggested-by: Marco Elver <elver@google.com>
Acked-by: Marco Elver <elver@google.com>
Reviewed-by: Christian Schoenebeck <linux_oss@crudebyte.com>
Signed-off-by: Dominique Martinet <asmadeus@codewreck.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
net/9p/client.c
net/9p/trans_fd.c
net/9p/trans_rdma.c
net/9p/trans_virtio.c
net/9p/trans_xen.c

index b554f8357f96730356dd24cb88f982638abb4207..b5aa25f82b78dca4277e124281f8e4ce3afffd55 100644 (file)
@@ -443,7 +443,7 @@ void p9_client_cb(struct p9_client *c, struct p9_req_t *req, int status)
         * the status change is visible to another thread
         */
        smp_wmb();
-       req->status = status;
+       WRITE_ONCE(req->status, status);
 
        wake_up(&req->wq);
        p9_debug(P9_DEBUG_MUX, "wakeup: %d\n", req->tc.tag);
@@ -605,7 +605,7 @@ static int p9_client_flush(struct p9_client *c, struct p9_req_t *oldreq)
        /* if we haven't received a response for oldreq,
         * remove it from the list
         */
-       if (oldreq->status == REQ_STATUS_SENT) {
+       if (READ_ONCE(oldreq->status) == REQ_STATUS_SENT) {
                if (c->trans_mod->cancelled)
                        c->trans_mod->cancelled(c, oldreq);
        }
@@ -702,7 +702,8 @@ p9_client_rpc(struct p9_client *c, int8_t type, const char *fmt, ...)
        }
 again:
        /* Wait for the response */
-       err = wait_event_killable(req->wq, req->status >= REQ_STATUS_RCVD);
+       err = wait_event_killable(req->wq,
+                                 READ_ONCE(req->status) >= REQ_STATUS_RCVD);
 
        /* Make sure our req is coherent with regard to updates in other
         * threads - echoes to wmb() in the callback
@@ -716,7 +717,7 @@ again:
                goto again;
        }
 
-       if (req->status == REQ_STATUS_ERROR) {
+       if (READ_ONCE(req->status) == REQ_STATUS_ERROR) {
                p9_debug(P9_DEBUG_ERROR, "req_status error %d\n", req->t_err);
                err = req->t_err;
        }
@@ -729,7 +730,7 @@ again:
                        p9_client_flush(c, req);
 
                /* if we received the response anyway, don't signal error */
-               if (req->status == REQ_STATUS_RCVD)
+               if (READ_ONCE(req->status) == REQ_STATUS_RCVD)
                        err = 0;
        }
 recalc_sigpending:
@@ -798,7 +799,7 @@ static struct p9_req_t *p9_client_zc_rpc(struct p9_client *c, int8_t type,
                if (err != -ERESTARTSYS)
                        goto recalc_sigpending;
        }
-       if (req->status == REQ_STATUS_ERROR) {
+       if (READ_ONCE(req->status) == REQ_STATUS_ERROR) {
                p9_debug(P9_DEBUG_ERROR, "req_status error %d\n", req->t_err);
                err = req->t_err;
        }
@@ -811,7 +812,7 @@ static struct p9_req_t *p9_client_zc_rpc(struct p9_client *c, int8_t type,
                        p9_client_flush(c, req);
 
                /* if we received the response anyway, don't signal error */
-               if (req->status == REQ_STATUS_RCVD)
+               if (READ_ONCE(req->status) == REQ_STATUS_RCVD)
                        err = 0;
        }
 recalc_sigpending:
index 07db2f436d44b03cb706a042df91825b37580f05..5a1aecf7fe4874e784f9cc0bc22315974f7c4d0f 100644 (file)
@@ -202,11 +202,11 @@ static void p9_conn_cancel(struct p9_conn *m, int err)
 
        list_for_each_entry_safe(req, rtmp, &m->req_list, req_list) {
                list_move(&req->req_list, &cancel_list);
-               req->status = REQ_STATUS_ERROR;
+               WRITE_ONCE(req->status, REQ_STATUS_ERROR);
        }
        list_for_each_entry_safe(req, rtmp, &m->unsent_req_list, req_list) {
                list_move(&req->req_list, &cancel_list);
-               req->status = REQ_STATUS_ERROR;
+               WRITE_ONCE(req->status, REQ_STATUS_ERROR);
        }
 
        spin_unlock(&m->req_lock);
@@ -467,7 +467,7 @@ static void p9_write_work(struct work_struct *work)
 
                req = list_entry(m->unsent_req_list.next, struct p9_req_t,
                               req_list);
-               req->status = REQ_STATUS_SENT;
+               WRITE_ONCE(req->status, REQ_STATUS_SENT);
                p9_debug(P9_DEBUG_TRANS, "move req %p\n", req);
                list_move_tail(&req->req_list, &m->req_list);
 
@@ -676,7 +676,7 @@ static int p9_fd_request(struct p9_client *client, struct p9_req_t *req)
                return m->err;
 
        spin_lock(&m->req_lock);
-       req->status = REQ_STATUS_UNSENT;
+       WRITE_ONCE(req->status, REQ_STATUS_UNSENT);
        list_add_tail(&req->req_list, &m->unsent_req_list);
        spin_unlock(&m->req_lock);
 
@@ -703,7 +703,7 @@ static int p9_fd_cancel(struct p9_client *client, struct p9_req_t *req)
 
        if (req->status == REQ_STATUS_UNSENT) {
                list_del(&req->req_list);
-               req->status = REQ_STATUS_FLSHD;
+               WRITE_ONCE(req->status, REQ_STATUS_FLSHD);
                p9_req_put(client, req);
                ret = 0;
        }
@@ -732,7 +732,7 @@ static int p9_fd_cancelled(struct p9_client *client, struct p9_req_t *req)
         * remove it from the list.
         */
        list_del(&req->req_list);
-       req->status = REQ_STATUS_FLSHD;
+       WRITE_ONCE(req->status, REQ_STATUS_FLSHD);
        spin_unlock(&m->req_lock);
 
        p9_req_put(client, req);
index 6ff706760676e02995514199f5a575840c85831a..e9a830c69058c42d383a8cac977be8e0b29c25cd 100644 (file)
@@ -507,7 +507,7 @@ dont_need_post_recv:
         * because doing if after could erase the REQ_STATUS_RCVD
         * status in case of a very fast reply.
         */
-       req->status = REQ_STATUS_SENT;
+       WRITE_ONCE(req->status, REQ_STATUS_SENT);
        err = ib_post_send(rdma->qp, &wr, NULL);
        if (err)
                goto send_error;
@@ -517,7 +517,7 @@ dont_need_post_recv:
 
  /* Handle errors that happened during or while preparing the send: */
  send_error:
-       req->status = REQ_STATUS_ERROR;
+       WRITE_ONCE(req->status, REQ_STATUS_ERROR);
        kfree(c);
        p9_debug(P9_DEBUG_ERROR, "Error %d in rdma_request()\n", err);
 
index e757f0601304361d31d9e0b00166278fb2c0e3ae..3f3eb03cda7d6c01badf532b0172fbaf54c16da6 100644 (file)
@@ -263,7 +263,7 @@ p9_virtio_request(struct p9_client *client, struct p9_req_t *req)
 
        p9_debug(P9_DEBUG_TRANS, "9p debug: virtio request\n");
 
-       req->status = REQ_STATUS_SENT;
+       WRITE_ONCE(req->status, REQ_STATUS_SENT);
 req_retry:
        spin_lock_irqsave(&chan->lock, flags);
 
@@ -469,7 +469,7 @@ p9_virtio_zc_request(struct p9_client *client, struct p9_req_t *req,
                        inlen = n;
                }
        }
-       req->status = REQ_STATUS_SENT;
+       WRITE_ONCE(req->status, REQ_STATUS_SENT);
 req_retry_pinned:
        spin_lock_irqsave(&chan->lock, flags);
 
@@ -532,9 +532,10 @@ req_retry_pinned:
        spin_unlock_irqrestore(&chan->lock, flags);
        kicked = 1;
        p9_debug(P9_DEBUG_TRANS, "virtio request kicked\n");
-       err = wait_event_killable(req->wq, req->status >= REQ_STATUS_RCVD);
+       err = wait_event_killable(req->wq,
+                                 READ_ONCE(req->status) >= REQ_STATUS_RCVD);
        // RERROR needs reply (== error string) in static data
-       if (req->status == REQ_STATUS_RCVD &&
+       if (READ_ONCE(req->status) == REQ_STATUS_RCVD &&
            unlikely(req->rc.sdata[4] == P9_RERROR))
                handle_rerror(req, in_hdr_len, offs, in_pages);
 
index aaa5fd364691b0bd32957f56a36a80d4989ed7d1..cf1b89ba522b4b3bab28bd6e2fa8202234a18816 100644 (file)
@@ -157,7 +157,7 @@ again:
                              &masked_prod, masked_cons,
                              XEN_9PFS_RING_SIZE(ring));
 
-       p9_req->status = REQ_STATUS_SENT;
+       WRITE_ONCE(p9_req->status, REQ_STATUS_SENT);
        virt_wmb();                     /* write ring before updating pointer */
        prod += size;
        ring->intf->out_prod = prod;
@@ -212,7 +212,7 @@ static void p9_xen_response(struct work_struct *work)
                        dev_warn(&priv->dev->dev,
                                 "requested packet size too big: %d for tag %d with capacity %zd\n",
                                 h.size, h.tag, req->rc.capacity);
-                       req->status = REQ_STATUS_ERROR;
+                       WRITE_ONCE(req->status, REQ_STATUS_ERROR);
                        goto recv_error;
                }