Skip to content

Commit 439bea1

Browse files
committed
fs-verity: use mempool for hash requests
When initializing an fs-verity hash algorithm, also initialize a mempool that contains a single preallocated hash request object. Then replace the direct calls to ahash_request_alloc() and ahash_request_free() with allocating and freeing from this mempool. This eliminates the possibility of the allocation failing, which is desirable for the I/O path. This doesn't cause deadlocks because there's no case where multiple hash requests are needed at a time to make forward progress. Link: https://lore.kernel.org/r/[email protected] Reviewed-by: Theodore Ts'o <[email protected]> Signed-off-by: Eric Biggers <[email protected]>
1 parent fd39073 commit 439bea1

File tree

5 files changed

+97
-46
lines changed

5 files changed

+97
-46
lines changed

fs/verity/enable.c

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -165,9 +165,11 @@ static int build_merkle_tree(struct file *filp,
165165
return 0;
166166
}
167167

168+
/* This allocation never fails, since it's mempool-backed. */
169+
req = fsverity_alloc_hash_request(params->hash_alg, GFP_KERNEL);
170+
168171
pending_hashes = kmalloc(params->block_size, GFP_KERNEL);
169-
req = ahash_request_alloc(params->hash_alg->tfm, GFP_KERNEL);
170-
if (!pending_hashes || !req)
172+
if (!pending_hashes)
171173
goto out;
172174

173175
/*
@@ -189,7 +191,7 @@ static int build_merkle_tree(struct file *filp,
189191
err = 0;
190192
out:
191193
kfree(pending_hashes);
192-
ahash_request_free(req);
194+
fsverity_free_hash_request(params->hash_alg, req);
193195
return err;
194196
}
195197

fs/verity/fsverity_private.h

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
#include <crypto/sha.h>
1818
#include <linux/fsverity.h>
19+
#include <linux/mempool.h>
1920

2021
struct ahash_request;
2122

@@ -37,11 +38,12 @@ struct fsverity_hash_alg {
3738
const char *name; /* crypto API name, e.g. sha256 */
3839
unsigned int digest_size; /* digest size in bytes, e.g. 32 for SHA-256 */
3940
unsigned int block_size; /* block size in bytes, e.g. 64 for SHA-256 */
41+
mempool_t req_pool; /* mempool with a preallocated hash request */
4042
};
4143

4244
/* Merkle tree parameters: hash algorithm, initial hash state, and topology */
4345
struct merkle_tree_params {
44-
const struct fsverity_hash_alg *hash_alg; /* the hash algorithm */
46+
struct fsverity_hash_alg *hash_alg; /* the hash algorithm */
4547
const u8 *hashstate; /* initial hash state or NULL */
4648
unsigned int digest_size; /* same as hash_alg->digest_size */
4749
unsigned int block_size; /* size of data and tree blocks */
@@ -115,14 +117,18 @@ struct fsverity_signed_digest {
115117

116118
extern struct fsverity_hash_alg fsverity_hash_algs[];
117119

118-
const struct fsverity_hash_alg *fsverity_get_hash_alg(const struct inode *inode,
119-
unsigned int num);
120-
const u8 *fsverity_prepare_hash_state(const struct fsverity_hash_alg *alg,
120+
struct fsverity_hash_alg *fsverity_get_hash_alg(const struct inode *inode,
121+
unsigned int num);
122+
struct ahash_request *fsverity_alloc_hash_request(struct fsverity_hash_alg *alg,
123+
gfp_t gfp_flags);
124+
void fsverity_free_hash_request(struct fsverity_hash_alg *alg,
125+
struct ahash_request *req);
126+
const u8 *fsverity_prepare_hash_state(struct fsverity_hash_alg *alg,
121127
const u8 *salt, size_t salt_size);
122128
int fsverity_hash_page(const struct merkle_tree_params *params,
123129
const struct inode *inode,
124130
struct ahash_request *req, struct page *page, u8 *out);
125-
int fsverity_hash_buffer(const struct fsverity_hash_alg *alg,
131+
int fsverity_hash_buffer(struct fsverity_hash_alg *alg,
126132
const void *data, size_t size, u8 *out);
127133
void __init fsverity_check_hash_algs(void);
128134

fs/verity/hash_algs.c

Lines changed: 73 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ struct fsverity_hash_alg fsverity_hash_algs[] = {
2424
},
2525
};
2626

27+
static DEFINE_MUTEX(fsverity_hash_alg_init_mutex);
28+
2729
/**
2830
* fsverity_get_hash_alg() - validate and prepare a hash algorithm
2931
* @inode: optional inode for logging purposes
@@ -36,8 +38,8 @@ struct fsverity_hash_alg fsverity_hash_algs[] = {
3638
*
3739
* Return: pointer to the hash alg on success, else an ERR_PTR()
3840
*/
39-
const struct fsverity_hash_alg *fsverity_get_hash_alg(const struct inode *inode,
40-
unsigned int num)
41+
struct fsverity_hash_alg *fsverity_get_hash_alg(const struct inode *inode,
42+
unsigned int num)
4143
{
4244
struct fsverity_hash_alg *alg;
4345
struct crypto_ahash *tfm;
@@ -50,10 +52,15 @@ const struct fsverity_hash_alg *fsverity_get_hash_alg(const struct inode *inode,
5052
}
5153
alg = &fsverity_hash_algs[num];
5254

53-
/* pairs with cmpxchg() below */
54-
tfm = READ_ONCE(alg->tfm);
55-
if (likely(tfm != NULL))
55+
/* pairs with smp_store_release() below */
56+
if (likely(smp_load_acquire(&alg->tfm) != NULL))
5657
return alg;
58+
59+
mutex_lock(&fsverity_hash_alg_init_mutex);
60+
61+
if (alg->tfm != NULL)
62+
goto out_unlock;
63+
5764
/*
5865
* Using the shash API would make things a bit simpler, but the ahash
5966
* API is preferable as it allows the use of crypto accelerators.
@@ -64,12 +71,14 @@ const struct fsverity_hash_alg *fsverity_get_hash_alg(const struct inode *inode,
6471
fsverity_warn(inode,
6572
"Missing crypto API support for hash algorithm \"%s\"",
6673
alg->name);
67-
return ERR_PTR(-ENOPKG);
74+
alg = ERR_PTR(-ENOPKG);
75+
goto out_unlock;
6876
}
6977
fsverity_err(inode,
7078
"Error allocating hash algorithm \"%s\": %ld",
7179
alg->name, PTR_ERR(tfm));
72-
return ERR_CAST(tfm);
80+
alg = ERR_CAST(tfm);
81+
goto out_unlock;
7382
}
7483

7584
err = -EINVAL;
@@ -78,18 +87,61 @@ const struct fsverity_hash_alg *fsverity_get_hash_alg(const struct inode *inode,
7887
if (WARN_ON(alg->block_size != crypto_ahash_blocksize(tfm)))
7988
goto err_free_tfm;
8089

90+
err = mempool_init_kmalloc_pool(&alg->req_pool, 1,
91+
sizeof(struct ahash_request) +
92+
crypto_ahash_reqsize(tfm));
93+
if (err)
94+
goto err_free_tfm;
95+
8196
pr_info("%s using implementation \"%s\"\n",
8297
alg->name, crypto_ahash_driver_name(tfm));
8398

84-
/* pairs with READ_ONCE() above */
85-
if (cmpxchg(&alg->tfm, NULL, tfm) != NULL)
86-
crypto_free_ahash(tfm);
87-
88-
return alg;
99+
/* pairs with smp_load_acquire() above */
100+
smp_store_release(&alg->tfm, tfm);
101+
goto out_unlock;
89102

90103
err_free_tfm:
91104
crypto_free_ahash(tfm);
92-
return ERR_PTR(err);
105+
alg = ERR_PTR(err);
106+
out_unlock:
107+
mutex_unlock(&fsverity_hash_alg_init_mutex);
108+
return alg;
109+
}
110+
111+
/**
112+
* fsverity_alloc_hash_request() - allocate a hash request object
113+
* @alg: the hash algorithm for which to allocate the request
114+
* @gfp_flags: memory allocation flags
115+
*
116+
* This is mempool-backed, so this never fails if __GFP_DIRECT_RECLAIM is set in
117+
* @gfp_flags. However, in that case this might need to wait for all
118+
* previously-allocated requests to be freed. So to avoid deadlocks, callers
119+
* must never need multiple requests at a time to make forward progress.
120+
*
121+
* Return: the request object on success; NULL on failure (but see above)
122+
*/
123+
struct ahash_request *fsverity_alloc_hash_request(struct fsverity_hash_alg *alg,
124+
gfp_t gfp_flags)
125+
{
126+
struct ahash_request *req = mempool_alloc(&alg->req_pool, gfp_flags);
127+
128+
if (req)
129+
ahash_request_set_tfm(req, alg->tfm);
130+
return req;
131+
}
132+
133+
/**
134+
* fsverity_free_hash_request() - free a hash request object
135+
* @alg: the hash algorithm
136+
* @req: the hash request object to free
137+
*/
138+
void fsverity_free_hash_request(struct fsverity_hash_alg *alg,
139+
struct ahash_request *req)
140+
{
141+
if (req) {
142+
ahash_request_zero(req);
143+
mempool_free(req, &alg->req_pool);
144+
}
93145
}
94146

95147
/**
@@ -101,7 +153,7 @@ const struct fsverity_hash_alg *fsverity_get_hash_alg(const struct inode *inode,
101153
* Return: NULL if the salt is empty, otherwise the kmalloc()'ed precomputed
102154
* initial hash state on success or an ERR_PTR() on failure.
103155
*/
104-
const u8 *fsverity_prepare_hash_state(const struct fsverity_hash_alg *alg,
156+
const u8 *fsverity_prepare_hash_state(struct fsverity_hash_alg *alg,
105157
const u8 *salt, size_t salt_size)
106158
{
107159
u8 *hashstate = NULL;
@@ -119,11 +171,8 @@ const u8 *fsverity_prepare_hash_state(const struct fsverity_hash_alg *alg,
119171
if (!hashstate)
120172
return ERR_PTR(-ENOMEM);
121173

122-
req = ahash_request_alloc(alg->tfm, GFP_KERNEL);
123-
if (!req) {
124-
err = -ENOMEM;
125-
goto err_free;
126-
}
174+
/* This allocation never fails, since it's mempool-backed. */
175+
req = fsverity_alloc_hash_request(alg, GFP_KERNEL);
127176

128177
/*
129178
* Zero-pad the salt to the next multiple of the input size of the hash
@@ -158,7 +207,7 @@ const u8 *fsverity_prepare_hash_state(const struct fsverity_hash_alg *alg,
158207
if (err)
159208
goto err_free;
160209
out:
161-
ahash_request_free(req);
210+
fsverity_free_hash_request(alg, req);
162211
kfree(padded_salt);
163212
return hashstate;
164213

@@ -229,17 +278,16 @@ int fsverity_hash_page(const struct merkle_tree_params *params,
229278
*
230279
* Return: 0 on success, -errno on failure
231280
*/
232-
int fsverity_hash_buffer(const struct fsverity_hash_alg *alg,
281+
int fsverity_hash_buffer(struct fsverity_hash_alg *alg,
233282
const void *data, size_t size, u8 *out)
234283
{
235284
struct ahash_request *req;
236285
struct scatterlist sg;
237286
DECLARE_CRYPTO_WAIT(wait);
238287
int err;
239288

240-
req = ahash_request_alloc(alg->tfm, GFP_KERNEL);
241-
if (!req)
242-
return -ENOMEM;
289+
/* This allocation never fails, since it's mempool-backed. */
290+
req = fsverity_alloc_hash_request(alg, GFP_KERNEL);
243291

244292
sg_init_one(&sg, data, size);
245293
ahash_request_set_callback(req, CRYPTO_TFM_REQ_MAY_SLEEP |
@@ -249,7 +297,7 @@ int fsverity_hash_buffer(const struct fsverity_hash_alg *alg,
249297

250298
err = crypto_wait_req(crypto_ahash_digest(req), &wait);
251299

252-
ahash_request_free(req);
300+
fsverity_free_hash_request(alg, req);
253301
return err;
254302
}
255303

fs/verity/open.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ int fsverity_init_merkle_tree_params(struct merkle_tree_params *params,
3131
unsigned int log_blocksize,
3232
const u8 *salt, size_t salt_size)
3333
{
34-
const struct fsverity_hash_alg *hash_alg;
34+
struct fsverity_hash_alg *hash_alg;
3535
int err;
3636
u64 blocks;
3737
u64 offset;
@@ -127,7 +127,7 @@ int fsverity_init_merkle_tree_params(struct merkle_tree_params *params,
127127
* Compute the file measurement by hashing the fsverity_descriptor excluding the
128128
* signature and with the sig_size field set to 0.
129129
*/
130-
static int compute_file_measurement(const struct fsverity_hash_alg *hash_alg,
130+
static int compute_file_measurement(struct fsverity_hash_alg *hash_alg,
131131
struct fsverity_descriptor *desc,
132132
u8 *measurement)
133133
{

fs/verity/verify.c

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -192,13 +192,12 @@ bool fsverity_verify_page(struct page *page)
192192
struct ahash_request *req;
193193
bool valid;
194194

195-
req = ahash_request_alloc(vi->tree_params.hash_alg->tfm, GFP_NOFS);
196-
if (unlikely(!req))
197-
return false;
195+
/* This allocation never fails, since it's mempool-backed. */
196+
req = fsverity_alloc_hash_request(vi->tree_params.hash_alg, GFP_NOFS);
198197

199198
valid = verify_page(inode, vi, req, page, 0);
200199

201-
ahash_request_free(req);
200+
fsverity_free_hash_request(vi->tree_params.hash_alg, req);
202201

203202
return valid;
204203
}
@@ -229,12 +228,8 @@ void fsverity_verify_bio(struct bio *bio)
229228
struct bvec_iter_all iter_all;
230229
unsigned long max_ra_pages = 0;
231230

232-
req = ahash_request_alloc(params->hash_alg->tfm, GFP_NOFS);
233-
if (unlikely(!req)) {
234-
bio_for_each_segment_all(bv, bio, iter_all)
235-
SetPageError(bv->bv_page);
236-
return;
237-
}
231+
/* This allocation never fails, since it's mempool-backed. */
232+
req = fsverity_alloc_hash_request(params->hash_alg, GFP_NOFS);
238233

239234
if (bio->bi_opf & REQ_RAHEAD) {
240235
/*
@@ -262,7 +257,7 @@ void fsverity_verify_bio(struct bio *bio)
262257
SetPageError(page);
263258
}
264259

265-
ahash_request_free(req);
260+
fsverity_free_hash_request(params->hash_alg, req);
266261
}
267262
EXPORT_SYMBOL_GPL(fsverity_verify_bio);
268263
#endif /* CONFIG_BLOCK */

0 commit comments

Comments
 (0)