@@ -24,6 +24,8 @@ struct fsverity_hash_alg fsverity_hash_algs[] = {
24
24
},
25
25
};
26
26
27
+ static DEFINE_MUTEX (fsverity_hash_alg_init_mutex );
28
+
27
29
/**
28
30
* fsverity_get_hash_alg() - validate and prepare a hash algorithm
29
31
* @inode: optional inode for logging purposes
@@ -36,8 +38,8 @@ struct fsverity_hash_alg fsverity_hash_algs[] = {
36
38
*
37
39
* Return: pointer to the hash alg on success, else an ERR_PTR()
38
40
*/
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 )
41
43
{
42
44
struct fsverity_hash_alg * alg ;
43
45
struct crypto_ahash * tfm ;
@@ -50,10 +52,15 @@ const struct fsverity_hash_alg *fsverity_get_hash_alg(const struct inode *inode,
50
52
}
51
53
alg = & fsverity_hash_algs [num ];
52
54
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 ))
56
57
return alg ;
58
+
59
+ mutex_lock (& fsverity_hash_alg_init_mutex );
60
+
61
+ if (alg -> tfm != NULL )
62
+ goto out_unlock ;
63
+
57
64
/*
58
65
* Using the shash API would make things a bit simpler, but the ahash
59
66
* 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,
64
71
fsverity_warn (inode ,
65
72
"Missing crypto API support for hash algorithm \"%s\"" ,
66
73
alg -> name );
67
- return ERR_PTR (- ENOPKG );
74
+ alg = ERR_PTR (- ENOPKG );
75
+ goto out_unlock ;
68
76
}
69
77
fsverity_err (inode ,
70
78
"Error allocating hash algorithm \"%s\": %ld" ,
71
79
alg -> name , PTR_ERR (tfm ));
72
- return ERR_CAST (tfm );
80
+ alg = ERR_CAST (tfm );
81
+ goto out_unlock ;
73
82
}
74
83
75
84
err = - EINVAL ;
@@ -78,18 +87,61 @@ const struct fsverity_hash_alg *fsverity_get_hash_alg(const struct inode *inode,
78
87
if (WARN_ON (alg -> block_size != crypto_ahash_blocksize (tfm )))
79
88
goto err_free_tfm ;
80
89
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
+
81
96
pr_info ("%s using implementation \"%s\"\n" ,
82
97
alg -> name , crypto_ahash_driver_name (tfm ));
83
98
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 ;
89
102
90
103
err_free_tfm :
91
104
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
+ }
93
145
}
94
146
95
147
/**
@@ -101,7 +153,7 @@ const struct fsverity_hash_alg *fsverity_get_hash_alg(const struct inode *inode,
101
153
* Return: NULL if the salt is empty, otherwise the kmalloc()'ed precomputed
102
154
* initial hash state on success or an ERR_PTR() on failure.
103
155
*/
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 ,
105
157
const u8 * salt , size_t salt_size )
106
158
{
107
159
u8 * hashstate = NULL ;
@@ -119,11 +171,8 @@ const u8 *fsverity_prepare_hash_state(const struct fsverity_hash_alg *alg,
119
171
if (!hashstate )
120
172
return ERR_PTR (- ENOMEM );
121
173
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 );
127
176
128
177
/*
129
178
* 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,
158
207
if (err )
159
208
goto err_free ;
160
209
out :
161
- ahash_request_free ( req );
210
+ fsverity_free_hash_request ( alg , req );
162
211
kfree (padded_salt );
163
212
return hashstate ;
164
213
@@ -229,17 +278,16 @@ int fsverity_hash_page(const struct merkle_tree_params *params,
229
278
*
230
279
* Return: 0 on success, -errno on failure
231
280
*/
232
- int fsverity_hash_buffer (const struct fsverity_hash_alg * alg ,
281
+ int fsverity_hash_buffer (struct fsverity_hash_alg * alg ,
233
282
const void * data , size_t size , u8 * out )
234
283
{
235
284
struct ahash_request * req ;
236
285
struct scatterlist sg ;
237
286
DECLARE_CRYPTO_WAIT (wait );
238
287
int err ;
239
288
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 );
243
291
244
292
sg_init_one (& sg , data , size );
245
293
ahash_request_set_callback (req , CRYPTO_TFM_REQ_MAY_SLEEP |
@@ -249,7 +297,7 @@ int fsverity_hash_buffer(const struct fsverity_hash_alg *alg,
249
297
250
298
err = crypto_wait_req (crypto_ahash_digest (req ), & wait );
251
299
252
- ahash_request_free ( req );
300
+ fsverity_free_hash_request ( alg , req );
253
301
return err ;
254
302
}
255
303
0 commit comments