[dm-devel] [bug report] dm flakey: clone pages on write bio before corrupting them
Mikulas Patocka
mpatocka at redhat.com
Wed Jun 14 15:12:03 UTC 2023
Hi
I don't get it what the problem is. This seems like a false positive.
On Wed, 14 Jun 2023, Dan Carpenter wrote:
> Hello Mikulas Patocka,
>
> The patch 90ed93c305a0: "dm flakey: clone pages on write bio before
> corrupting them" from May 1, 2023, leads to the following Smatch
> static checker warning:
>
> drivers/md/dm.c:1157 clone_endio()
> warn: passing freed memory 'ti'
>
> drivers/md/dm.c
> 1105 static void clone_endio(struct bio *bio)
> 1106 {
> 1107 blk_status_t error = bio->bi_status;
> 1108 struct dm_target_io *tio = clone_to_tio(bio);
> 1109 struct dm_target *ti = tio->ti;
> 1110 dm_endio_fn endio = ti->type->end_io;
> 1111 struct dm_io *io = tio->io;
> 1112 struct mapped_device *md = io->md;
> 1113
> 1114 if (unlikely(error == BLK_STS_TARGET)) {
> 1115 if (bio_op(bio) == REQ_OP_DISCARD &&
> 1116 !bdev_max_discard_sectors(bio->bi_bdev))
> 1117 disable_discard(md);
> 1118 else if (bio_op(bio) == REQ_OP_WRITE_ZEROES &&
> 1119 !bdev_write_zeroes_sectors(bio->bi_bdev))
> 1120 disable_write_zeroes(md);
> 1121 }
> 1122
> 1123 if (static_branch_unlikely(&zoned_enabled) &&
> 1124 unlikely(bdev_is_zoned(bio->bi_bdev)))
> 1125 dm_zone_endio(io, bio);
> 1126
> 1127 if (endio) {
> 1128 int r = endio(ti, bio, &error);
>
> clone_endio() from drivers/md/dm-flakey.c frees "ti". (Yes, I get
> that "flakey" in the filename means it is supposed to crash but
> presumably not in this way).
Here we call "endio", not "clone_endio". "endio" points to "flakey_end_io"
and it doesn't free the "ti" structure. It possibly corrupts the read data
and always return DM_ENDIO_DONE.
clone_endio() in dm-flakey.c calls bio_endio(), bio_endio() calls
bi_endio, bi_endio points to clone_endio() in dm.c and clone_endio() in
dm.c decrements the reference count and when the count goes to 0, "ti" may
be freed.
There are two clone_endio functions, one in dm.c and one in dm-flakey.c.
Perhaps the static analysis tool mixed them up.
> 1129
> 1130 switch (r) {
> 1131 case DM_ENDIO_REQUEUE:
> 1132 if (static_branch_unlikely(&zoned_enabled)) {
> 1133 /*
> 1134 * Requeuing writes to a sequential zone of a zoned
> 1135 * target will break the sequential write pattern:
> 1136 * fail such IO.
> 1137 */
> 1138 if (WARN_ON_ONCE(dm_is_zone_write(md, bio)))
> 1139 error = BLK_STS_IOERR;
> 1140 else
> 1141 error = BLK_STS_DM_REQUEUE;
> 1142 } else
> 1143 error = BLK_STS_DM_REQUEUE;
> 1144 fallthrough;
> 1145 case DM_ENDIO_DONE:
> 1146 break;
> 1147 case DM_ENDIO_INCOMPLETE:
> 1148 /* The target will handle the io */
> 1149 return;
> 1150 default:
> 1151 DMCRIT("unimplemented target endio return value: %d", r);
> 1152 BUG();
> 1153 }
> 1154 }
> 1155
> 1156 if (static_branch_unlikely(&swap_bios_enabled) &&
> --> 1157 unlikely(swap_bios_limit(ti, bio)))
>
> Use after free.
There is a reference on the "io" structure held at this point, so "ti"
cannot be freed here. We will drop the reference later with
dm_io_dec_pending().
> 1158 up(&md->swap_bios_semaphore);
> 1159
> 1160 free_tio(bio);
> 1161 dm_io_dec_pending(io, error);
When we drop the last reference with "dm_io_dec_pending", "ti" may be
freed. But "dm_io_dec_pending" is the last statement in this function, so
this should be safe.
dm_io_dec_pending calls __dm_io_dec_pending, there it goes to
dm_io_complete and __dm_io_complete, there it decrements the in-use
counter with this_cpu_dec - at this point, "ti" may be freed. But the code
at this point doesn't access the "ti" variable at all.
Mikulas
> 1162 }
>
> regards,
> dan carpenter
>
More information about the dm-devel
mailing list