0a9bab391e
Tasklets have an inherent problem with memory corruption. The function tasklet_action_common calls tasklet_trylock, then it calls the tasklet callback and then it calls tasklet_unlock. If the tasklet callback frees the structure that contains the tasklet or if it calls some code that may free it, tasklet_unlock will write into free memory. The commits8e14f61015
andd9a02e016a
try to fix it for dm-crypt, but it is not a sufficient fix and the data corruption can still happen [1]. There is no fix for dm-verity and dm-verity will write into free memory with every tasklet-processed bio. There will be atomic workqueues implemented in the kernel 6.9 [2]. They will have better interface and they will not suffer from the memory corruption problem. But we need something that stops the memory corruption now and that can be backported to the stable kernels. So, I'm proposing this commit that disables tasklets in both dm-crypt and dm-verity. This commit doesn't remove the tasklet support, because the tasklet code will be reused when atomic workqueues will be implemented. [1] https://lore.kernel.org/all/d390d7ee-f142-44d3-822a-87949e14608b@suse.de/T/ [2] https://lore.kernel.org/lkml/20240130091300.2968534-1-tj@kernel.org/ Signed-off-by: Mikulas Patocka <mpatocka@redhat.com> Cc: stable@vger.kernel.org Fixes:39d42fa96b
("dm crypt: add flags to optionally bypass kcryptd workqueues") Fixes:5721d4e5a9
("dm verity: Add optional "try_verify_in_tasklet" feature") Signed-off-by: Mike Snitzer <snitzer@kernel.org>
135 lines
3.9 KiB
C
135 lines
3.9 KiB
C
/* SPDX-License-Identifier: GPL-2.0-only */
|
|
/*
|
|
* Copyright (C) 2012 Red Hat, Inc.
|
|
* Copyright (C) 2015 Google, Inc.
|
|
*
|
|
* Author: Mikulas Patocka <mpatocka@redhat.com>
|
|
*
|
|
* Based on Chromium dm-verity driver (C) 2011 The Chromium OS Authors
|
|
*/
|
|
|
|
#ifndef DM_VERITY_H
|
|
#define DM_VERITY_H
|
|
|
|
#include <linux/dm-bufio.h>
|
|
#include <linux/device-mapper.h>
|
|
#include <linux/interrupt.h>
|
|
#include <crypto/hash.h>
|
|
|
|
#define DM_VERITY_MAX_LEVELS 63
|
|
|
|
enum verity_mode {
|
|
DM_VERITY_MODE_EIO,
|
|
DM_VERITY_MODE_LOGGING,
|
|
DM_VERITY_MODE_RESTART,
|
|
DM_VERITY_MODE_PANIC
|
|
};
|
|
|
|
enum verity_block_type {
|
|
DM_VERITY_BLOCK_TYPE_DATA,
|
|
DM_VERITY_BLOCK_TYPE_METADATA
|
|
};
|
|
|
|
struct dm_verity_fec;
|
|
|
|
struct dm_verity {
|
|
struct dm_dev *data_dev;
|
|
struct dm_dev *hash_dev;
|
|
struct dm_target *ti;
|
|
struct dm_bufio_client *bufio;
|
|
char *alg_name;
|
|
struct crypto_ahash *tfm;
|
|
u8 *root_digest; /* digest of the root block */
|
|
u8 *salt; /* salt: its size is salt_size */
|
|
u8 *zero_digest; /* digest for a zero block */
|
|
unsigned int salt_size;
|
|
sector_t data_start; /* data offset in 512-byte sectors */
|
|
sector_t hash_start; /* hash start in blocks */
|
|
sector_t data_blocks; /* the number of data blocks */
|
|
sector_t hash_blocks; /* the number of hash blocks */
|
|
unsigned char data_dev_block_bits; /* log2(data blocksize) */
|
|
unsigned char hash_dev_block_bits; /* log2(hash blocksize) */
|
|
unsigned char hash_per_block_bits; /* log2(hashes in hash block) */
|
|
unsigned char levels; /* the number of tree levels */
|
|
unsigned char version;
|
|
bool hash_failed:1; /* set if hash of any block failed */
|
|
bool use_tasklet:1; /* try to verify in tasklet before work-queue */
|
|
unsigned int digest_size; /* digest size for the current hash algorithm */
|
|
unsigned int ahash_reqsize;/* the size of temporary space for crypto */
|
|
enum verity_mode mode; /* mode for handling verification errors */
|
|
unsigned int corrupted_errs;/* Number of errors for corrupted blocks */
|
|
|
|
struct workqueue_struct *verify_wq;
|
|
|
|
/* starting blocks for each tree level. 0 is the lowest level. */
|
|
sector_t hash_level_block[DM_VERITY_MAX_LEVELS];
|
|
|
|
struct dm_verity_fec *fec; /* forward error correction */
|
|
unsigned long *validated_blocks; /* bitset blocks validated */
|
|
|
|
char *signature_key_desc; /* signature keyring reference */
|
|
};
|
|
|
|
struct dm_verity_io {
|
|
struct dm_verity *v;
|
|
|
|
/* original value of bio->bi_end_io */
|
|
bio_end_io_t *orig_bi_end_io;
|
|
|
|
sector_t block;
|
|
unsigned int n_blocks;
|
|
bool in_tasklet;
|
|
|
|
struct bvec_iter iter;
|
|
|
|
struct work_struct work;
|
|
|
|
/*
|
|
* Three variably-size fields follow this struct:
|
|
*
|
|
* u8 hash_req[v->ahash_reqsize];
|
|
* u8 real_digest[v->digest_size];
|
|
* u8 want_digest[v->digest_size];
|
|
*
|
|
* To access them use: verity_io_hash_req(), verity_io_real_digest()
|
|
* and verity_io_want_digest().
|
|
*/
|
|
};
|
|
|
|
static inline struct ahash_request *verity_io_hash_req(struct dm_verity *v,
|
|
struct dm_verity_io *io)
|
|
{
|
|
return (struct ahash_request *)(io + 1);
|
|
}
|
|
|
|
static inline u8 *verity_io_real_digest(struct dm_verity *v,
|
|
struct dm_verity_io *io)
|
|
{
|
|
return (u8 *)(io + 1) + v->ahash_reqsize;
|
|
}
|
|
|
|
static inline u8 *verity_io_want_digest(struct dm_verity *v,
|
|
struct dm_verity_io *io)
|
|
{
|
|
return (u8 *)(io + 1) + v->ahash_reqsize + v->digest_size;
|
|
}
|
|
|
|
extern int verity_for_bv_block(struct dm_verity *v, struct dm_verity_io *io,
|
|
struct bvec_iter *iter,
|
|
int (*process)(struct dm_verity *v,
|
|
struct dm_verity_io *io,
|
|
u8 *data, size_t len));
|
|
|
|
extern int verity_hash(struct dm_verity *v, struct ahash_request *req,
|
|
const u8 *data, size_t len, u8 *digest, bool may_sleep);
|
|
|
|
extern int verity_hash_for_block(struct dm_verity *v, struct dm_verity_io *io,
|
|
sector_t block, u8 *digest, bool *is_zero);
|
|
|
|
extern bool dm_is_verity_target(struct dm_target *ti);
|
|
extern int dm_verity_get_mode(struct dm_target *ti);
|
|
extern int dm_verity_get_root_digest(struct dm_target *ti, u8 **root_digest,
|
|
unsigned int *digest_size);
|
|
|
|
#endif /* DM_VERITY_H */
|