Skip to content

Commit 73d7b06

Browse files
author
Mike Snitzer
committed
dm zone: fix NULL pointer dereference in dm_zone_map_bio
Commit 0fbb4d9 ("dm: add dm_submit_bio_remap interface") changed the alloc_io() function to delay the initialization of struct dm_io's orig_bio member, leaving it NULL until after the dm_io and associated user submitted bio is processed by __split_and_process_bio(). This change causes a NULL pointer dereference in dm_zone_map_bio() when the original user bio is inspected to detect the need for zone append command emulation. Fix this NULL pointer by updating dm_zone_map_bio() to not access ->orig_bio when the same info can be accessed from the clone of the ->orig_bio _before_ any ->map processing. Save off the bio_op() and bio_sectors() for the clone and then use the saved orig_bio_details as needed. Fixes: 0fbb4d9 ("dm: add dm_submit_bio_remap interface") Reported-by: Damien Le Moal <[email protected]> Tested-by: Damien Le Moal <[email protected]> Signed-off-by: Mike Snitzer <[email protected]>
1 parent ce40426 commit 73d7b06

File tree

1 file changed

+28
-21
lines changed

1 file changed

+28
-21
lines changed

drivers/md/dm-zone.c

Lines changed: 28 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -360,16 +360,20 @@ static int dm_update_zone_wp_offset(struct mapped_device *md, unsigned int zno,
360360
return 0;
361361
}
362362

363+
struct orig_bio_details {
364+
unsigned int op;
365+
unsigned int nr_sectors;
366+
};
367+
363368
/*
364369
* First phase of BIO mapping for targets with zone append emulation:
365370
* check all BIO that change a zone writer pointer and change zone
366371
* append operations into regular write operations.
367372
*/
368373
static bool dm_zone_map_bio_begin(struct mapped_device *md,
369-
struct bio *orig_bio, struct bio *clone)
374+
unsigned int zno, struct bio *clone)
370375
{
371376
sector_t zsectors = blk_queue_zone_sectors(md->queue);
372-
unsigned int zno = bio_zone_no(orig_bio);
373377
unsigned int zwp_offset = READ_ONCE(md->zwp_offset[zno]);
374378

375379
/*
@@ -384,7 +388,7 @@ static bool dm_zone_map_bio_begin(struct mapped_device *md,
384388
WRITE_ONCE(md->zwp_offset[zno], zwp_offset);
385389
}
386390

387-
switch (bio_op(orig_bio)) {
391+
switch (bio_op(clone)) {
388392
case REQ_OP_ZONE_RESET:
389393
case REQ_OP_ZONE_FINISH:
390394
return true;
@@ -401,9 +405,8 @@ static bool dm_zone_map_bio_begin(struct mapped_device *md,
401405
* target zone.
402406
*/
403407
clone->bi_opf = REQ_OP_WRITE | REQ_NOMERGE |
404-
(orig_bio->bi_opf & (~REQ_OP_MASK));
405-
clone->bi_iter.bi_sector =
406-
orig_bio->bi_iter.bi_sector + zwp_offset;
408+
(clone->bi_opf & (~REQ_OP_MASK));
409+
clone->bi_iter.bi_sector += zwp_offset;
407410
break;
408411
default:
409412
DMWARN_LIMIT("Invalid BIO operation");
@@ -423,19 +426,18 @@ static bool dm_zone_map_bio_begin(struct mapped_device *md,
423426
* data written to a zone. Note that at this point, the remapped clone BIO
424427
* may already have completed, so we do not touch it.
425428
*/
426-
static blk_status_t dm_zone_map_bio_end(struct mapped_device *md,
427-
struct bio *orig_bio,
429+
static blk_status_t dm_zone_map_bio_end(struct mapped_device *md, unsigned int zno,
430+
struct orig_bio_details *orig_bio_details,
428431
unsigned int nr_sectors)
429432
{
430-
unsigned int zno = bio_zone_no(orig_bio);
431433
unsigned int zwp_offset = READ_ONCE(md->zwp_offset[zno]);
432434

433435
/* The clone BIO may already have been completed and failed */
434436
if (zwp_offset == DM_ZONE_INVALID_WP_OFST)
435437
return BLK_STS_IOERR;
436438

437439
/* Update the zone wp offset */
438-
switch (bio_op(orig_bio)) {
440+
switch (orig_bio_details->op) {
439441
case REQ_OP_ZONE_RESET:
440442
WRITE_ONCE(md->zwp_offset[zno], 0);
441443
return BLK_STS_OK;
@@ -452,7 +454,7 @@ static blk_status_t dm_zone_map_bio_end(struct mapped_device *md,
452454
* Check that the target did not truncate the write operation
453455
* emulating a zone append.
454456
*/
455-
if (nr_sectors != bio_sectors(orig_bio)) {
457+
if (nr_sectors != orig_bio_details->nr_sectors) {
456458
DMWARN_LIMIT("Truncated write for zone append");
457459
return BLK_STS_IOERR;
458460
}
@@ -488,23 +490,23 @@ static inline void dm_zone_unlock(struct request_queue *q,
488490
bio_clear_flag(clone, BIO_ZONE_WRITE_LOCKED);
489491
}
490492

491-
static bool dm_need_zone_wp_tracking(struct bio *orig_bio)
493+
static bool dm_need_zone_wp_tracking(struct bio *bio)
492494
{
493495
/*
494496
* Special processing is not needed for operations that do not need the
495497
* zone write lock, that is, all operations that target conventional
496498
* zones and all operations that do not modify directly a sequential
497499
* zone write pointer.
498500
*/
499-
if (op_is_flush(orig_bio->bi_opf) && !bio_sectors(orig_bio))
501+
if (op_is_flush(bio->bi_opf) && !bio_sectors(bio))
500502
return false;
501-
switch (bio_op(orig_bio)) {
503+
switch (bio_op(bio)) {
502504
case REQ_OP_WRITE_ZEROES:
503505
case REQ_OP_WRITE:
504506
case REQ_OP_ZONE_RESET:
505507
case REQ_OP_ZONE_FINISH:
506508
case REQ_OP_ZONE_APPEND:
507-
return bio_zone_is_seq(orig_bio);
509+
return bio_zone_is_seq(bio);
508510
default:
509511
return false;
510512
}
@@ -519,8 +521,8 @@ int dm_zone_map_bio(struct dm_target_io *tio)
519521
struct dm_target *ti = tio->ti;
520522
struct mapped_device *md = io->md;
521523
struct request_queue *q = md->queue;
522-
struct bio *orig_bio = io->orig_bio;
523524
struct bio *clone = &tio->clone;
525+
struct orig_bio_details orig_bio_details;
524526
unsigned int zno;
525527
blk_status_t sts;
526528
int r;
@@ -529,18 +531,21 @@ int dm_zone_map_bio(struct dm_target_io *tio)
529531
* IOs that do not change a zone write pointer do not need
530532
* any additional special processing.
531533
*/
532-
if (!dm_need_zone_wp_tracking(orig_bio))
534+
if (!dm_need_zone_wp_tracking(clone))
533535
return ti->type->map(ti, clone);
534536

535537
/* Lock the target zone */
536-
zno = bio_zone_no(orig_bio);
538+
zno = bio_zone_no(clone);
537539
dm_zone_lock(q, zno, clone);
538540

541+
orig_bio_details.nr_sectors = bio_sectors(clone);
542+
orig_bio_details.op = bio_op(clone);
543+
539544
/*
540545
* Check that the bio and the target zone write pointer offset are
541546
* both valid, and if the bio is a zone append, remap it to a write.
542547
*/
543-
if (!dm_zone_map_bio_begin(md, orig_bio, clone)) {
548+
if (!dm_zone_map_bio_begin(md, zno, clone)) {
544549
dm_zone_unlock(q, zno, clone);
545550
return DM_MAPIO_KILL;
546551
}
@@ -560,15 +565,17 @@ int dm_zone_map_bio(struct dm_target_io *tio)
560565
* The target submitted the clone BIO. The target zone will
561566
* be unlocked on completion of the clone.
562567
*/
563-
sts = dm_zone_map_bio_end(md, orig_bio, *tio->len_ptr);
568+
sts = dm_zone_map_bio_end(md, zno, &orig_bio_details,
569+
*tio->len_ptr);
564570
break;
565571
case DM_MAPIO_REMAPPED:
566572
/*
567573
* The target only remapped the clone BIO. In case of error,
568574
* unlock the target zone here as the clone will not be
569575
* submitted.
570576
*/
571-
sts = dm_zone_map_bio_end(md, orig_bio, *tio->len_ptr);
577+
sts = dm_zone_map_bio_end(md, zno, &orig_bio_details,
578+
*tio->len_ptr);
572579
if (sts != BLK_STS_OK)
573580
dm_zone_unlock(q, zno, clone);
574581
break;

0 commit comments

Comments
 (0)