1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (c) 2019 Google LLC
4 * Copyright (c) 2021 Joerg Vehlow <joerg.vehlow@aox-tech.de>
5 */
6
7 /*
8 * Regression test for kernel commit 21d4120ec6f5 ("crypto: user - prevent
9 * operating on larval algorithms"). See the commit message for a detailed
10 * explanation of the problem. Basically, this test tries to cause a NULL
11 * pointer dereference in the kernel by abusing the CRYPTO_MSG_DELALG message in
12 * the NETLINK_CRYPTO interface to try to delete a "larval" algorithm, which is
13 * a kernel-internal marker for an algorithm which has been registered but isn't
14 * ready yet (e.g., hasn't completed the in-kernel crypto self-tests yet).
15 *
16 * CRYPTO_MSG_NEWALG will create such a larval algorithm. However, it waits
17 * (killably) for the larval to mature before returning, and it holds a lock
18 * that prevents CRYPTO_MSG_DELALG from running. To get around this, this test
19 * sends a fatal signal to the process executing CRYPTO_MSG_NEWALG.
20 */
21
22 #include <errno.h>
23 #include <stdlib.h>
24 #include <sys/wait.h>
25
26 #include "tst_test.h"
27 #include "tst_crypto.h"
28 #include "tst_timer.h"
29
30 /*
31 * List of possible algorithms to use try (not exhaustive).
32 * The algorithm has to be valid (i.e. the drivers must exists
33 * and be a valid combination) and it has to be deleteable.
34 * To be deletable it cannot be used by someone else.
35 * The first algorithm, that fullfils the criteria is used for the test.
36 */
37 static const char * const ALGORITHM_CANDIDATES[] = {
38 "hmac(sha1-generic)",
39 "hmac(sha224-generic)",
40 "hmac(sha256-generic)",
41 "hmac(sha384-generic)",
42 "hmac(md5-generic)",
43 "hmac(sm3-generic)",
44 "hmac(sha512-generic)",
45 "hmac(rmd160-generic)",
46 "hmac(sha3-224-generic)",
47 "hmac(sha3-256-generic)",
48 "hmac(sha3-384-generic)",
49 "hmac(sha3-512-generic)",
50 "hmac(streebog256-generic)",
51 "hmac(streebog512-generic)"
52 };
53
54 static const char* algorithm = NULL;
55 static struct tst_crypto_session ses = TST_CRYPTO_SESSION_INIT;
56
57
setup(void)58 static void setup(void)
59 {
60 int rc;
61 unsigned i;
62 struct crypto_user_alg alg;
63 tst_crypto_open(&ses);
64
65 /* find an algorithm, that is not in use */
66 for (i = 0; i < ARRAY_SIZE(ALGORITHM_CANDIDATES); ++i) {
67 memset(&alg, 0, sizeof(alg));
68 strcpy(alg.cru_driver_name, ALGORITHM_CANDIDATES[i]);
69
70 /* try to add it, to see if it is valid */
71 rc = tst_crypto_add_alg(&ses, &alg);
72 if (rc != 0)
73 continue;
74
75 /* it also has to be deletable */
76 rc = tst_crypto_del_alg(&ses, &alg);
77 if (rc == 0) {
78 algorithm = ALGORITHM_CANDIDATES[i];
79 break;
80 }
81 }
82
83 if (!algorithm)
84 tst_brk(TCONF, "No viable algorithm found");
85 }
86
run(void)87 static void run(void)
88 {
89 struct crypto_user_alg alg = {};
90 pid_t pid;
91 int status;
92
93 strcpy(alg.cru_driver_name, algorithm);
94
95 tst_res(TINFO,
96 "Starting crypto_user larval deletion test using algorithm %s. May crash buggy kernels.",
97 algorithm);
98
99 tst_timer_start(CLOCK_MONOTONIC);
100
101 while (!tst_timer_expired_ms(1000)) {
102 pid = SAFE_FORK();
103
104 if (pid == 0) {
105 /* Child process: execute CRYPTO_MSG_NEWALG. */
106 tst_crypto_open(&ses);
107 for (;;) {
108 TEST(tst_crypto_add_alg(&ses, &alg));
109 if (TST_RET && TST_RET != -EEXIST)
110 tst_brk(TBROK | TRERRNO,
111 "unexpected error from tst_crypto_add_alg()");
112 }
113 }
114
115 /*
116 * Parent process: kill the child process (hopefully while it's
117 * executing CRYPTO_MSG_NEWALG) and execute CRYPTO_MSG_DELALG.
118 * Buggy kernels sometimes dereferenced a NULL pointer during
119 * CRYPTO_MSG_DELALG here.
120 */
121 usleep(rand() % 5000);
122 kill(pid, SIGKILL);
123 SAFE_WAIT(&status);
124 if (!WIFSIGNALED(status) || WTERMSIG(status) != SIGKILL)
125 tst_brk(TBROK, "child %s", tst_strstatus(status));
126 TEST(tst_crypto_del_alg(&ses, &alg));
127 if (TST_RET && TST_RET != -ENOENT)
128 tst_brk(TBROK | TRERRNO,
129 "unexpected error from tst_crypto_del_alg()");
130 }
131
132 tst_res(TPASS, "didn't crash");
133 }
134
cleanup(void)135 static void cleanup(void)
136 {
137 tst_crypto_close(&ses);
138 }
139
140 static struct tst_test test = {
141 .setup = setup,
142 .test_all = run,
143 .cleanup = cleanup,
144 .needs_root = 1,
145 .forks_child = 1,
146 .tags = (const struct tst_tag[]) {
147 {"linux-git", "21d4120ec6f5"},
148 {}
149 }
150 };
151