From 21974061cfb3c4b0b1a83447fb5e7cdcd06e56dc Mon Sep 17 00:00:00 2001 From: Mike Krinkin Date: Sun, 19 Jul 2015 09:53:17 +0300 Subject: null_blk: fix use-after-free problem end_cmd finishes request associated with nullb_cmd struct, so we should save pointer to request_queue in a local variable before calling end_cmd. The problem was causes general protection fault with slab poisoning enabled. Fixes: 8b70f45e2eb2 ("null_blk: restart request processing on completion handler") Tested-by: Akinobu Mita Signed-off-by: Mike Krinkin Signed-off-by: Jens Axboe --- drivers/block/null_blk.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/block/null_blk.c b/drivers/block/null_blk.c index 69de41a87b74..3177b245d2bd 100644 --- a/drivers/block/null_blk.c +++ b/drivers/block/null_blk.c @@ -240,19 +240,19 @@ static enum hrtimer_restart null_cmd_timer_expired(struct hrtimer *timer) while ((entry = llist_del_all(&cq->list)) != NULL) { entry = llist_reverse_order(entry); do { + struct request_queue *q = NULL; + cmd = container_of(entry, struct nullb_cmd, ll_list); entry = entry->next; + if (cmd->rq) + q = cmd->rq->q; end_cmd(cmd); - if (cmd->rq) { - struct request_queue *q = cmd->rq->q; - - if (!q->mq_ops && blk_queue_stopped(q)) { - spin_lock(q->queue_lock); - if (blk_queue_stopped(q)) - blk_start_queue(q); - spin_unlock(q->queue_lock); - } + if (q && !q->mq_ops && blk_queue_stopped(q)) { + spin_lock(q->queue_lock); + if (blk_queue_stopped(q)) + blk_start_queue(q); + spin_unlock(q->queue_lock); } } while (entry); } -- cgit v1.2.3 From 5f6c2d2b7dbb541c1e922538c49fa04c494ae3d7 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 22 Jul 2015 18:05:53 -0400 Subject: blkcg: fix gendisk reference leak in blkg_conf_prep() When a blkcg configuration is targeted to a partition rather than a whole device, blkg_conf_prep fails with -EINVAL; unfortunately, it forgets to put the gendisk ref in that case. Fix it. Signed-off-by: Tejun Heo Cc: stable@vger.kernel.org Signed-off-by: Jens Axboe --- block/blk-cgroup.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c index 9da02c021ebe..d6283b3f5db5 100644 --- a/block/blk-cgroup.c +++ b/block/blk-cgroup.c @@ -718,8 +718,12 @@ int blkg_conf_prep(struct blkcg *blkcg, const struct blkcg_policy *pol, return -EINVAL; disk = get_gendisk(MKDEV(major, minor), &part); - if (!disk || part) + if (!disk) return -EINVAL; + if (part) { + put_disk(disk); + return -EINVAL; + } rcu_read_lock(); spin_lock_irq(disk->queue->queue_lock); -- cgit v1.2.3 From 5aa2a96b34073fb775917fbc7d5578a04164b9f7 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 23 Jul 2015 14:27:09 -0400 Subject: block: export bio_associate_*() and wbc_account_io() bio_associate_blkcg(), bio_associate_current() and wbc_account_io() are used to implement cgroup writeback support for filesystems and thus need to be exported. Export them. Signed-off-by: Tejun Heo Reported-by: Stephen Rothwell Signed-off-by: Jens Axboe --- block/bio.c | 2 ++ fs/fs-writeback.c | 1 + 2 files changed, 3 insertions(+) diff --git a/block/bio.c b/block/bio.c index 2a00d349cd68..b671a0ff731c 100644 --- a/block/bio.c +++ b/block/bio.c @@ -2009,6 +2009,7 @@ int bio_associate_blkcg(struct bio *bio, struct cgroup_subsys_state *blkcg_css) bio->bi_css = blkcg_css; return 0; } +EXPORT_SYMBOL_GPL(bio_associate_blkcg); /** * bio_associate_current - associate a bio with %current @@ -2039,6 +2040,7 @@ int bio_associate_current(struct bio *bio) bio->bi_css = task_get_css(current, blkio_cgrp_id); return 0; } +EXPORT_SYMBOL_GPL(bio_associate_current); /** * bio_disassociate_task - undo bio_associate_current() diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index f0520bcf2094..518c6294bf6c 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -702,6 +702,7 @@ void wbc_account_io(struct writeback_control *wbc, struct page *page, else wbc->wb_tcand_bytes -= min(bytes, wbc->wb_tcand_bytes); } +EXPORT_SYMBOL_GPL(wbc_account_io); /** * inode_congested - test whether an inode is congested -- cgit v1.2.3 From f3f5da624e0a891c34d8cd513c57f1d9b0c7dadc Mon Sep 17 00:00:00 2001 From: "Martin K. Petersen" Date: Wed, 22 Jul 2015 07:57:12 -0400 Subject: block: Do a full clone when splitting discard bios This fixes a data corruption bug when using discard on top of MD linear, raid0 and raid10 personalities. Commit 20d0189b1012 "block: Introduce new bio_split()" permits sharing the bio_vec between the two resulting bios. That is fine for read/write requests where the bio_vec is immutable. For discards, however, we need to be able to attach a payload and update the bio_vec so the page can get mapped to a scatterlist entry. Therefore the bio_vec can not be shared when splitting discards and we must do a full clone. Signed-off-by: Martin K. Petersen Reported-by: Seunguk Shin Tested-by: Seunguk Shin Cc: Seunguk Shin Cc: Jens Axboe Cc: Kent Overstreet Cc: # v3.14+ Reviewed-by: Christoph Hellwig Signed-off-by: Jens Axboe --- block/bio.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/block/bio.c b/block/bio.c index b671a0ff731c..d6e5ba3399f0 100644 --- a/block/bio.c +++ b/block/bio.c @@ -1831,8 +1831,9 @@ EXPORT_SYMBOL(bio_endio); * Allocates and returns a new bio which represents @sectors from the start of * @bio, and updates @bio to represent the remaining sectors. * - * The newly allocated bio will point to @bio's bi_io_vec; it is the caller's - * responsibility to ensure that @bio is not freed before the split. + * Unless this is a discard request the newly allocated bio will point + * to @bio's bi_io_vec; it is the caller's responsibility to ensure that + * @bio is not freed before the split. */ struct bio *bio_split(struct bio *bio, int sectors, gfp_t gfp, struct bio_set *bs) @@ -1842,7 +1843,15 @@ struct bio *bio_split(struct bio *bio, int sectors, BUG_ON(sectors <= 0); BUG_ON(sectors >= bio_sectors(bio)); - split = bio_clone_fast(bio, gfp, bs); + /* + * Discards need a mutable bio_vec to accommodate the payload + * required by the DSM TRIM and UNMAP commands. + */ + if (bio->bi_rw & REQ_DISCARD) + split = bio_clone_bioset(bio, gfp, bs); + else + split = bio_clone_fast(bio, gfp, bs); + if (!split) return NULL; -- cgit v1.2.3