nbd: partition nbd_read_stat() into nbd_read_reply() and nbd_handle_reply()
authorYu Kuai <yukuai3@huawei.com>
Thu, 16 Sep 2021 09:33:49 +0000 (17:33 +0800)
committerJens Axboe <axboe@kernel.dk>
Mon, 18 Oct 2021 20:50:37 +0000 (14:50 -0600)
Prepare to fix uaf in nbd_read_stat(), no functional changes.

Signed-off-by: Yu Kuai <yukuai3@huawei.com>
Reviewed-by: Ming Lei <ming.lei@redhat.com>
Reviewed-by: Josef Bacik <josef@toxicpanda.com>
Link: https://lore.kernel.org/r/20210916093350.1410403-7-yukuai3@huawei.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
drivers/block/nbd.c

index 1d34560..06e292f 100644 (file)
@@ -700,38 +700,45 @@ out:
        return 0;
 }
 
-/* NULL returned = something went wrong, inform userspace */
-static struct nbd_cmd *nbd_read_stat(struct nbd_device *nbd, int index)
+static int nbd_read_reply(struct nbd_device *nbd, int index,
+                         struct nbd_reply *reply)
 {
-       struct nbd_config *config = nbd->config;
-       int result;
-       struct nbd_reply reply;
-       struct nbd_cmd *cmd;
-       struct request *req = NULL;
-       u64 handle;
-       u16 hwq;
-       u32 tag;
-       struct kvec iov = {.iov_base = &reply, .iov_len = sizeof(reply)};
+       struct kvec iov = {.iov_base = reply, .iov_len = sizeof(*reply)};
        struct iov_iter to;
-       int ret = 0;
+       int result;
 
-       reply.magic = 0;
-       iov_iter_kvec(&to, READ, &iov, 1, sizeof(reply));
+       reply->magic = 0;
+       iov_iter_kvec(&to, READ, &iov, 1, sizeof(*reply));
        result = sock_xmit(nbd, index, 0, &to, MSG_WAITALL, NULL);
        if (result < 0) {
-               if (!nbd_disconnected(config))
+               if (!nbd_disconnected(nbd->config))
                        dev_err(disk_to_dev(nbd->disk),
                                "Receive control failed (result %d)\n", result);
-               return ERR_PTR(result);
+               return result;
        }
 
-       if (ntohl(reply.magic) != NBD_REPLY_MAGIC) {
+       if (ntohl(reply->magic) != NBD_REPLY_MAGIC) {
                dev_err(disk_to_dev(nbd->disk), "Wrong magic (0x%lx)\n",
-                               (unsigned long)ntohl(reply.magic));
-               return ERR_PTR(-EPROTO);
+                               (unsigned long)ntohl(reply->magic));
+               return -EPROTO;
        }
 
-       memcpy(&handle, reply.handle, sizeof(handle));
+       return 0;
+}
+
+/* NULL returned = something went wrong, inform userspace */
+static struct nbd_cmd *nbd_handle_reply(struct nbd_device *nbd, int index,
+                                       struct nbd_reply *reply)
+{
+       int result;
+       struct nbd_cmd *cmd;
+       struct request *req = NULL;
+       u64 handle;
+       u16 hwq;
+       u32 tag;
+       int ret = 0;
+
+       memcpy(&handle, reply->handle, sizeof(handle));
        tag = nbd_handle_to_tag(handle);
        hwq = blk_mq_unique_tag_to_hwq(tag);
        if (hwq < nbd->tag_set.nr_hw_queues)
@@ -774,9 +781,9 @@ static struct nbd_cmd *nbd_read_stat(struct nbd_device *nbd, int index)
                ret = -ENOENT;
                goto out;
        }
-       if (ntohl(reply.error)) {
+       if (ntohl(reply->error)) {
                dev_err(disk_to_dev(nbd->disk), "Other side returned error (%d)\n",
-                       ntohl(reply.error));
+                       ntohl(reply->error));
                cmd->status = BLK_STS_IOERR;
                goto out;
        }
@@ -785,6 +792,7 @@ static struct nbd_cmd *nbd_read_stat(struct nbd_device *nbd, int index)
        if (rq_data_dir(req) != WRITE) {
                struct req_iterator iter;
                struct bio_vec bvec;
+               struct iov_iter to;
 
                rq_for_each_segment(bvec, req, iter) {
                        iov_iter_bvec(&to, READ, &bvec, 1, bvec.bv_len);
@@ -798,7 +806,7 @@ static struct nbd_cmd *nbd_read_stat(struct nbd_device *nbd, int index)
                                 * and let the timeout stuff handle resubmitting
                                 * this request onto another connection.
                                 */
-                               if (nbd_disconnected(config)) {
+                               if (nbd_disconnected(nbd->config)) {
                                        cmd->status = BLK_STS_IOERR;
                                        goto out;
                                }
@@ -822,24 +830,30 @@ static void recv_work(struct work_struct *work)
                                                     work);
        struct nbd_device *nbd = args->nbd;
        struct nbd_config *config = nbd->config;
+       struct nbd_sock *nsock;
        struct nbd_cmd *cmd;
        struct request *rq;
 
        while (1) {
-               cmd = nbd_read_stat(nbd, args->index);
-               if (IS_ERR(cmd)) {
-                       struct nbd_sock *nsock = config->socks[args->index];
+               struct nbd_reply reply;
 
-                       mutex_lock(&nsock->tx_lock);
-                       nbd_mark_nsock_dead(nbd, nsock, 1);
-                       mutex_unlock(&nsock->tx_lock);
+               if (nbd_read_reply(nbd, args->index, &reply))
+                       break;
+
+               cmd = nbd_handle_reply(nbd, args->index, &reply);
+               if (IS_ERR(cmd))
                        break;
-               }
 
                rq = blk_mq_rq_from_pdu(cmd);
                if (likely(!blk_should_fake_timeout(rq->q)))
                        blk_mq_complete_request(rq);
        }
+
+       nsock = config->socks[args->index];
+       mutex_lock(&nsock->tx_lock);
+       nbd_mark_nsock_dead(nbd, nsock, 1);
+       mutex_unlock(&nsock->tx_lock);
+
        nbd_config_put(nbd);
        atomic_dec(&config->recv_threads);
        wake_up(&config->recv_wq);