Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

crypto: api - Fix built-in testing dependency failures

When complex algorithms that depend on other algorithms are built
into the kernel, the order of registration must be done such that
the underlying algorithms are ready before the ones on top are
registered. As otherwise they would fail during the self-test
which is required during registration.

In the past we have used subsystem initialisation ordering to
guarantee this. The number of such precedence levels are limited
and they may cause ripple effects in other subsystems.

This patch solves this problem by delaying all self-tests during
boot-up for built-in algorithms. They will be tested either when
something else in the kernel requests for them, or when we have
finished registering all built-in algorithms, whichever comes
earlier.

Reported-by: Vladis Dronov <vdronov@redhat.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>

+108 -27
+51 -22
crypto/algapi.c
··· 389 389 } 390 390 EXPORT_SYMBOL_GPL(crypto_remove_final); 391 391 392 - static void crypto_wait_for_test(struct crypto_larval *larval) 393 - { 394 - int err; 395 - 396 - err = crypto_probing_notify(CRYPTO_MSG_ALG_REGISTER, larval->adult); 397 - if (err != NOTIFY_STOP) { 398 - if (WARN_ON(err != NOTIFY_DONE)) 399 - goto out; 400 - crypto_alg_tested(larval->alg.cra_driver_name, 0); 401 - } 402 - 403 - err = wait_for_completion_killable(&larval->completion); 404 - WARN_ON(err); 405 - if (!err) 406 - crypto_notify(CRYPTO_MSG_ALG_LOADED, larval); 407 - 408 - out: 409 - crypto_larval_kill(&larval->alg); 410 - } 411 - 412 392 int crypto_register_alg(struct crypto_alg *alg) 413 393 { 414 394 struct crypto_larval *larval; 395 + bool test_started; 415 396 int err; 416 397 417 398 alg->cra_flags &= ~CRYPTO_ALG_DEAD; ··· 402 421 403 422 down_write(&crypto_alg_sem); 404 423 larval = __crypto_register_alg(alg); 424 + test_started = static_key_enabled(&crypto_boot_test_finished); 425 + larval->test_started = test_started; 405 426 up_write(&crypto_alg_sem); 406 427 407 428 if (IS_ERR(larval)) 408 429 return PTR_ERR(larval); 409 430 410 - crypto_wait_for_test(larval); 431 + if (test_started) 432 + crypto_wait_for_test(larval); 411 433 return 0; 412 434 } 413 435 EXPORT_SYMBOL_GPL(crypto_register_alg); ··· 616 632 larval = __crypto_register_alg(&inst->alg); 617 633 if (IS_ERR(larval)) 618 634 goto unlock; 635 + 636 + larval->test_started = true; 619 637 620 638 hlist_add_head(&inst->list, &tmpl->instances); 621 639 inst->tmpl = tmpl; ··· 1247 1261 EXPORT_SYMBOL_GPL(crypto_stats_skcipher_decrypt); 1248 1262 #endif 1249 1263 1264 + static void __init crypto_start_tests(void) 1265 + { 1266 + for (;;) { 1267 + struct crypto_larval *larval = NULL; 1268 + struct crypto_alg *q; 1269 + 1270 + down_write(&crypto_alg_sem); 1271 + 1272 + list_for_each_entry(q, &crypto_alg_list, cra_list) { 1273 + struct crypto_larval *l; 1274 + 1275 + if (!crypto_is_larval(q)) 1276 + continue; 1277 + 1278 + l = (void *)q; 1279 + 1280 + if (!crypto_is_test_larval(l)) 1281 + continue; 1282 + 1283 + if (l->test_started) 1284 + continue; 1285 + 1286 + l->test_started = true; 1287 + larval = l; 1288 + break; 1289 + } 1290 + 1291 + up_write(&crypto_alg_sem); 1292 + 1293 + if (!larval) 1294 + break; 1295 + 1296 + crypto_wait_for_test(larval); 1297 + } 1298 + 1299 + static_branch_enable(&crypto_boot_test_finished); 1300 + } 1301 + 1250 1302 static int __init crypto_algapi_init(void) 1251 1303 { 1252 1304 crypto_init_proc(); 1305 + crypto_start_tests(); 1253 1306 return 0; 1254 1307 } 1255 1308 ··· 1297 1272 crypto_exit_proc(); 1298 1273 } 1299 1274 1300 - module_init(crypto_algapi_init); 1275 + /* 1276 + * We run this at late_initcall so that all the built-in algorithms 1277 + * have had a chance to register themselves first. 1278 + */ 1279 + late_initcall(crypto_algapi_init); 1301 1280 module_exit(crypto_algapi_exit); 1302 1281 1303 1282 MODULE_LICENSE("GPL");
+47 -5
crypto/api.c
··· 12 12 13 13 #include <linux/err.h> 14 14 #include <linux/errno.h> 15 + #include <linux/jump_label.h> 15 16 #include <linux/kernel.h> 16 17 #include <linux/kmod.h> 17 18 #include <linux/module.h> ··· 31 30 BLOCKING_NOTIFIER_HEAD(crypto_chain); 32 31 EXPORT_SYMBOL_GPL(crypto_chain); 33 32 33 + DEFINE_STATIC_KEY_FALSE(crypto_boot_test_finished); 34 + 34 35 static struct crypto_alg *crypto_larval_wait(struct crypto_alg *alg); 35 36 36 37 struct crypto_alg *crypto_mod_get(struct crypto_alg *alg) ··· 49 46 module_put(module); 50 47 } 51 48 EXPORT_SYMBOL_GPL(crypto_mod_put); 52 - 53 - static inline int crypto_is_test_larval(struct crypto_larval *larval) 54 - { 55 - return larval->alg.cra_driver_name[0]; 56 - } 57 49 58 50 static struct crypto_alg *__crypto_alg_lookup(const char *name, u32 type, 59 51 u32 mask) ··· 161 163 } 162 164 EXPORT_SYMBOL_GPL(crypto_larval_kill); 163 165 166 + void crypto_wait_for_test(struct crypto_larval *larval) 167 + { 168 + int err; 169 + 170 + err = crypto_probing_notify(CRYPTO_MSG_ALG_REGISTER, larval->adult); 171 + if (err != NOTIFY_STOP) { 172 + if (WARN_ON(err != NOTIFY_DONE)) 173 + goto out; 174 + crypto_alg_tested(larval->alg.cra_driver_name, 0); 175 + } 176 + 177 + err = wait_for_completion_killable(&larval->completion); 178 + WARN_ON(err); 179 + if (!err) 180 + crypto_notify(CRYPTO_MSG_ALG_LOADED, larval); 181 + 182 + out: 183 + crypto_larval_kill(&larval->alg); 184 + } 185 + EXPORT_SYMBOL_GPL(crypto_wait_for_test); 186 + 187 + static void crypto_start_test(struct crypto_larval *larval) 188 + { 189 + if (!crypto_is_test_larval(larval)) 190 + return; 191 + 192 + if (larval->test_started) 193 + return; 194 + 195 + down_write(&crypto_alg_sem); 196 + if (larval->test_started) { 197 + up_write(&crypto_alg_sem); 198 + return; 199 + } 200 + 201 + larval->test_started = true; 202 + up_write(&crypto_alg_sem); 203 + 204 + crypto_wait_for_test(larval); 205 + } 206 + 164 207 static struct crypto_alg *crypto_larval_wait(struct crypto_alg *alg) 165 208 { 166 209 struct crypto_larval *larval = (void *)alg; 167 210 long timeout; 211 + 212 + if (!static_branch_likely(&crypto_boot_test_finished)) 213 + crypto_start_test(larval); 168 214 169 215 timeout = wait_for_completion_killable_timeout( 170 216 &larval->completion, 60 * HZ);
+10
crypto/internal.h
··· 10 10 11 11 #include <crypto/algapi.h> 12 12 #include <linux/completion.h> 13 + #include <linux/jump_label.h> 13 14 #include <linux/list.h> 14 15 #include <linux/module.h> 15 16 #include <linux/notifier.h> ··· 28 27 struct crypto_alg *adult; 29 28 struct completion completion; 30 29 u32 mask; 30 + bool test_started; 31 31 }; 32 32 33 33 enum { ··· 46 44 extern struct list_head crypto_alg_list; 47 45 extern struct rw_semaphore crypto_alg_sem; 48 46 extern struct blocking_notifier_head crypto_chain; 47 + 48 + DECLARE_STATIC_KEY_FALSE(crypto_boot_test_finished); 49 49 50 50 #ifdef CONFIG_PROC_FS 51 51 void __init crypto_init_proc(void); ··· 74 70 75 71 struct crypto_larval *crypto_larval_alloc(const char *name, u32 type, u32 mask); 76 72 void crypto_larval_kill(struct crypto_alg *alg); 73 + void crypto_wait_for_test(struct crypto_larval *larval); 77 74 void crypto_alg_tested(const char *name, int err); 78 75 79 76 void crypto_remove_spawns(struct crypto_alg *alg, struct list_head *list, ··· 159 154 { 160 155 if (flags & CRYPTO_TFM_REQ_MAY_SLEEP) 161 156 cond_resched(); 157 + } 158 + 159 + static inline int crypto_is_test_larval(struct crypto_larval *larval) 160 + { 161 + return larval->alg.cra_driver_name[0]; 162 162 } 163 163 164 164 #endif /* _CRYPTO_INTERNAL_H */