nbd: fix use-after-free of rq/bio in the xmit path
[pandora-kernel.git] / drivers / block / nbd.c
index 788581e..3d3f54a 100644 (file)
@@ -235,6 +235,7 @@ static int nbd_send_req(struct nbd_device *lo, struct request *req)
        int result, flags;
        struct nbd_request request;
        unsigned long size = blk_rq_bytes(req);
+       struct bio *bio;
 
        request.magic = htonl(NBD_REQUEST_MAGIC);
        request.type = htonl(nbd_cmd(req));
@@ -255,16 +256,20 @@ static int nbd_send_req(struct nbd_device *lo, struct request *req)
                goto error_out;
        }
 
-       if (nbd_cmd(req) == NBD_CMD_WRITE) {
-               struct req_iterator iter;
+       if (nbd_cmd(req) != NBD_CMD_WRITE)
+               return 0;
+
+       flags = 0;
+       bio = req->bio;
+       while (bio) {
+               struct bio *next = bio->bi_next;
+               int i;
                struct bio_vec *bvec;
-               /*
-                * we are really probing at internals to determine
-                * whether to set MSG_MORE or not...
-                */
-               rq_for_each_segment(bvec, req, iter) {
-                       flags = 0;
-                       if (!rq_iter_last(req, iter))
+
+               bio_for_each_segment(bvec, bio, i) {
+                       bool is_last = !next && i == bio->bi_vcnt - 1;
+
+                       if (is_last)
                                flags = MSG_MORE;
                        dprintk(DBG_TX, "%s: request %p: sending %d bytes data\n",
                                        lo->disk->disk_name, req, bvec->bv_len);
@@ -275,7 +280,16 @@ static int nbd_send_req(struct nbd_device *lo, struct request *req)
                                        result);
                                goto error_out;
                        }
+                       /*
+                        * The completion might already have come in,
+                        * so break for the last one instead of letting
+                        * the iterator do it. This prevents use-after-free
+                        * of the bio.
+                        */
+                       if (is_last)
+                               break;
                }
+               bio = next;
        }
        return 0;