1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright 2019 Google LLC
4 */
5
6 /*
7 * Regression test for kernel commit 21d4120ec6f5 ("crypto: user - prevent
8 * operating on larval algorithms"). See the commit message for a detailed
9 * explanation of the problem. Basically, this test tries to cause a NULL
10 * pointer dereference in the kernel by abusing the CRYPTO_MSG_DELALG message in
11 * the NETLINK_CRYPTO interface to try to delete a "larval" algorithm, which is
12 * a kernel-internal marker for an algorithm which has been registered but isn't
13 * ready yet (e.g., hasn't completed the in-kernel crypto self-tests yet).
14 *
15 * CRYPTO_MSG_NEWALG will create such a larval algorithm. However, it waits
16 * (killably) for the larval to mature before returning, and it holds a lock
17 * that prevents CRYPTO_MSG_DELALG from running. To get around this, this test
18 * sends a fatal signal to the process executing CRYPTO_MSG_NEWALG.
19 */
20
21 #include <errno.h>
22 #include <stdlib.h>
23 #include <sys/wait.h>
24
25 #include "tst_test.h"
26 #include "tst_crypto.h"
27 #include "tst_timer.h"
28
29 static struct tst_crypto_session ses = TST_CRYPTO_SESSION_INIT;
30
setup(void)31 static void setup(void)
32 {
33 tst_crypto_open(&ses);
34 }
35
run(void)36 static void run(void)
37 {
38 struct crypto_user_alg alg = {
39 /*
40 * Any algorithm instantiated from a template can do here, but
41 * choose something that's commonly available.
42 */
43 .cru_driver_name = "hmac(sha256-generic)",
44 };
45 pid_t pid;
46 int status;
47
48 /* Check whether the algorithm is supported before continuing. */
49 TEST(tst_crypto_add_alg(&ses, &alg));
50 if (TST_RET != 0 && TST_RET != -EEXIST) {
51 if (TST_RET == -ENOENT)
52 tst_brk(TCONF, "%s not supported", alg.cru_driver_name);
53
54 tst_brk(TBROK | TRERRNO,
55 "unexpected error checking for algorithm support");
56 }
57
58 tst_res(TINFO,
59 "Starting crypto_user larval deletion test. May crash buggy kernels.");
60
61 tst_timer_start(CLOCK_MONOTONIC);
62
63 while (!tst_timer_expired_ms(1000)) {
64 pid = SAFE_FORK();
65
66 if (pid == 0) {
67 /* Child process: execute CRYPTO_MSG_NEWALG. */
68 tst_crypto_open(&ses);
69 for (;;) {
70 TEST(tst_crypto_add_alg(&ses, &alg));
71 if (TST_RET && TST_RET != -EEXIST)
72 tst_brk(TBROK | TRERRNO,
73 "unexpected error from tst_crypto_add_alg()");
74 }
75 }
76
77 /*
78 * Parent process: kill the child process (hopefully while it's
79 * executing CRYPTO_MSG_NEWALG) and execute CRYPTO_MSG_DELALG.
80 * Buggy kernels sometimes dereferenced a NULL pointer during
81 * CRYPTO_MSG_DELALG here.
82 */
83 usleep(rand() % 5000);
84 kill(pid, SIGKILL);
85 SAFE_WAIT(&status);
86 if (!WIFSIGNALED(status) || WTERMSIG(status) != SIGKILL)
87 tst_brk(TBROK, "child %s", tst_strstatus(status));
88 TEST(tst_crypto_del_alg(&ses, &alg));
89 if (TST_RET && TST_RET != -ENOENT)
90 tst_brk(TBROK | TRERRNO,
91 "unexpected error from tst_crypto_del_alg()");
92 }
93
94 tst_res(TPASS, "didn't crash");
95 }
96
cleanup(void)97 static void cleanup(void)
98 {
99 tst_crypto_close(&ses);
100 }
101
102 static struct tst_test test = {
103 .setup = setup,
104 .test_all = run,
105 .cleanup = cleanup,
106 .needs_root = 1,
107 .forks_child = 1,
108 .tags = (const struct tst_tag[]) {
109 {"linux-git", "21d4120ec6f5"},
110 {}
111 }
112 };
113