Skip to content

Commit 9177f3c

Browse files
Mikulas PatockaMike Snitzer
authored andcommitted
dm-verity: recheck the hash after a failure
If a userspace process reads (with O_DIRECT) multiple blocks into the same buffer, dm-verity reports an error [1]. This commit fixes dm-verity, so that if hash verification fails, the data is read again into a kernel buffer (where userspace can't modify it) and the hash is rechecked. If the recheck succeeds, the content of the kernel buffer is copied into the user buffer; if the recheck fails, an error is reported. [1] https://people.redhat.com/~mpatocka/testcases/blk-auth-modify/read2.c Signed-off-by: Mikulas Patocka <mpatocka@redhat.com> Cc: stable@vger.kernel.org Signed-off-by: Mike Snitzer <snitzer@kernel.org>
1 parent c88f5e5 commit 9177f3c

File tree

2 files changed

+86
-6
lines changed

2 files changed

+86
-6
lines changed

drivers/md/dm-verity-target.c

Lines changed: 80 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,63 @@ int verity_for_bv_block(struct dm_verity *v, struct dm_verity_io *io,
482482
return 0;
483483
}
484484

485+
static int verity_recheck_copy(struct dm_verity *v, struct dm_verity_io *io,
486+
u8 *data, size_t len)
487+
{
488+
memcpy(data, io->recheck_buffer, len);
489+
io->recheck_buffer += len;
490+
491+
return 0;
492+
}
493+
494+
static int verity_recheck(struct dm_verity *v, struct dm_verity_io *io,
495+
struct bvec_iter start, sector_t cur_block)
496+
{
497+
struct page *page;
498+
void *buffer;
499+
int r;
500+
struct dm_io_request io_req;
501+
struct dm_io_region io_loc;
502+
503+
page = mempool_alloc(&v->recheck_pool, GFP_NOIO);
504+
buffer = page_to_virt(page);
505+
506+
io_req.bi_opf = REQ_OP_READ;
507+
io_req.mem.type = DM_IO_KMEM;
508+
io_req.mem.ptr.addr = buffer;
509+
io_req.notify.fn = NULL;
510+
io_req.client = v->io;
511+
io_loc.bdev = v->data_dev->bdev;
512+
io_loc.sector = cur_block << (v->data_dev_block_bits - SECTOR_SHIFT);
513+
io_loc.count = 1 << (v->data_dev_block_bits - SECTOR_SHIFT);
514+
r = dm_io(&io_req, 1, &io_loc, NULL);
515+
if (unlikely(r))
516+
goto free_ret;
517+
518+
r = verity_hash(v, verity_io_hash_req(v, io), buffer,
519+
1 << v->data_dev_block_bits,
520+
verity_io_real_digest(v, io), true);
521+
if (unlikely(r))
522+
goto free_ret;
523+
524+
if (memcmp(verity_io_real_digest(v, io),
525+
verity_io_want_digest(v, io), v->digest_size)) {
526+
r = -EIO;
527+
goto free_ret;
528+
}
529+
530+
io->recheck_buffer = buffer;
531+
r = verity_for_bv_block(v, io, &start, verity_recheck_copy);
532+
if (unlikely(r))
533+
goto free_ret;
534+
535+
r = 0;
536+
free_ret:
537+
mempool_free(page, &v->recheck_pool);
538+
539+
return r;
540+
}
541+
485542
static int verity_bv_zero(struct dm_verity *v, struct dm_verity_io *io,
486543
u8 *data, size_t len)
487544
{
@@ -508,9 +565,7 @@ static int verity_verify_io(struct dm_verity_io *io)
508565
{
509566
bool is_zero;
510567
struct dm_verity *v = io->v;
511-
#if defined(CONFIG_DM_VERITY_FEC)
512568
struct bvec_iter start;
513-
#endif
514569
struct bvec_iter iter_copy;
515570
struct bvec_iter *iter;
516571
struct crypto_wait wait;
@@ -561,10 +616,7 @@ static int verity_verify_io(struct dm_verity_io *io)
561616
if (unlikely(r < 0))
562617
return r;
563618

564-
#if defined(CONFIG_DM_VERITY_FEC)
565-
if (verity_fec_is_enabled(v))
566-
start = *iter;
567-
#endif
619+
start = *iter;
568620
r = verity_for_io_block(v, io, iter, &wait);
569621
if (unlikely(r < 0))
570622
return r;
@@ -586,6 +638,10 @@ static int verity_verify_io(struct dm_verity_io *io)
586638
* tasklet since it may sleep, so fallback to work-queue.
587639
*/
588640
return -EAGAIN;
641+
} else if (verity_recheck(v, io, start, cur_block) == 0) {
642+
if (v->validated_blocks)
643+
set_bit(cur_block, v->validated_blocks);
644+
continue;
589645
#if defined(CONFIG_DM_VERITY_FEC)
590646
} else if (verity_fec_decode(v, io, DM_VERITY_BLOCK_TYPE_DATA,
591647
cur_block, NULL, &start) == 0) {
@@ -941,6 +997,10 @@ static void verity_dtr(struct dm_target *ti)
941997
if (v->verify_wq)
942998
destroy_workqueue(v->verify_wq);
943999

1000+
mempool_exit(&v->recheck_pool);
1001+
if (v->io)
1002+
dm_io_client_destroy(v->io);
1003+
9441004
if (v->bufio)
9451005
dm_bufio_client_destroy(v->bufio);
9461006

@@ -1379,6 +1439,20 @@ static int verity_ctr(struct dm_target *ti, unsigned int argc, char **argv)
13791439
}
13801440
v->hash_blocks = hash_position;
13811441

1442+
r = mempool_init_page_pool(&v->recheck_pool, 1, 0);
1443+
if (unlikely(r)) {
1444+
ti->error = "Cannot allocate mempool";
1445+
goto bad;
1446+
}
1447+
1448+
v->io = dm_io_client_create();
1449+
if (IS_ERR(v->io)) {
1450+
r = PTR_ERR(v->io);
1451+
v->io = NULL;
1452+
ti->error = "Cannot allocate dm io";
1453+
goto bad;
1454+
}
1455+
13821456
v->bufio = dm_bufio_client_create(v->hash_dev->bdev,
13831457
1 << v->hash_dev_block_bits, 1, sizeof(struct buffer_aux),
13841458
dm_bufio_alloc_callback, NULL,

drivers/md/dm-verity.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#ifndef DM_VERITY_H
1212
#define DM_VERITY_H
1313

14+
#include <linux/dm-io.h>
1415
#include <linux/dm-bufio.h>
1516
#include <linux/device-mapper.h>
1617
#include <linux/interrupt.h>
@@ -68,6 +69,9 @@ struct dm_verity {
6869
unsigned long *validated_blocks; /* bitset blocks validated */
6970

7071
char *signature_key_desc; /* signature keyring reference */
72+
73+
struct dm_io_client *io;
74+
mempool_t recheck_pool;
7175
};
7276

7377
struct dm_verity_io {
@@ -84,6 +88,8 @@ struct dm_verity_io {
8488

8589
struct work_struct work;
8690

91+
char *recheck_buffer;
92+
8793
/*
8894
* Three variably-size fields follow this struct:
8995
*

0 commit comments

Comments
 (0)