--- a/sys/dev/disk/dm/crypt/dm_target_crypt.c +++ b/sys/dev/disk/dm/crypt/dm_target_crypt.c @@ -96,8 +96,19 @@ struct malloc_pipe read_mpipe; struct malloc_pipe write_mpipe; + /* + * In-flight bios that still reference this config (see + * dm_target_crypt_destroy). + */ + volatile int nactive; + } dm_target_crypt_config_t; +#define DMTC_BIO_DONE(priv) do { \ + if (atomic_fetchadd_int(&(priv)->nactive, -1) == 1) \ + wakeup(priv); \ +} while (0) + struct dmtc_dump_helper { dm_target_crypt_config_t *priv; void *data; @@ -488,6 +499,9 @@ priv = kmalloc(sizeof(dm_target_crypt_config_t), M_DMCRYPT, M_WAITOK); + /* No in-flight bios reference this config yet */ + priv->nactive = 0; + /* Insert dmp to global pdev list */ if ((priv->pdev = dm_pdev_insert(dev)) == NULL) { kprintf("dm_target_crypt: dm_pdev_insert failed\n"); @@ -617,6 +631,16 @@ return 0; dm_pdev_decr(priv->pdev); + /* + * Wait until every in-flight bio referencing this config has + * completed. dmstrategy drops its table reference as soon as + * strategy() returns, but crypt completions run asynchronously + * afterwards holding nothing but bio_caller_info1.ptr; freeing + * the config (and the mpipes) underneath them is a use-after-free. + */ + while (priv->nactive != 0) + tsleep(priv, 0, "dmtcdn", 1); + dmtc_destroy_mpipe(priv); /* @@ -692,6 +716,7 @@ switch (bp->b_cmd) { case BUF_CMD_READ: + atomic_add_int(&priv->nactive, 1); bio = push_bio(&bp->b_bio1); bio->bio_offset = bp->b_bio1.bio_offset + priv->block_offset * DEV_BSIZE; @@ -700,6 +725,7 @@ vn_strategy(priv->pdev->pdev_vnode, bio); break; case BUF_CMD_WRITE: + atomic_add_int(&priv->nactive, 1); bio = push_bio(&bp->b_bio1); bio->bio_offset = bp->b_bio1.bio_offset + priv->block_offset * DEV_BSIZE; @@ -729,6 +755,7 @@ dmtc_bio_read_done(struct bio *bio) { struct bio *obio; + dm_target_crypt_config_t *priv = bio->bio_caller_info1.ptr; /* * If a read error occurs we shortcut the operation, otherwise @@ -737,6 +764,7 @@ if (bio->bio_buf->b_flags & B_ERROR) { obio = pop_bio(bio); biodone(obio); + DMTC_BIO_DONE(priv); } else { dmtc_bio_read_decrypt_start(bio); } @@ -829,6 +857,8 @@ if (mpipe) mpipe_free(mpipe, data_buf); + + DMTC_BIO_DONE(priv); } /************************************************************************ @@ -906,6 +936,8 @@ bio->bio_buf->b_flags |= B_ERROR; struct bio *obio = pop_bio(bio); biodone(obio); + + DMTC_BIO_DONE(priv); } else { bio->bio_caller_info2.ptr = bio->bio_buf->b_data; /* orig_buf */ bio->bio_buf->b_data = data_buf; @@ -946,6 +978,8 @@ struct bio *obio = pop_bio(bio); biodone(obio); + + DMTC_BIO_DONE(priv); } /************************************************************************