1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (c) 2019 Google LLC
4 * Copyright (c) 2021 Joerg Vehlow <[email protected]>
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_netlink_context *ctx;
56
57
setup(void)58 static void setup(void)
59 {
60 int rc;
61 unsigned i;
62 struct crypto_user_alg alg;
63
64 ctx = NETLINK_CREATE_CONTEXT(NETLINK_CRYPTO);
65
66 /* find an algorithm, that is not in use */
67 for (i = 0; i < ARRAY_SIZE(ALGORITHM_CANDIDATES); ++i) {
68 memset(&alg, 0, sizeof(alg));
69 strcpy(alg.cru_driver_name, ALGORITHM_CANDIDATES[i]);
70
71 /* try to add it, to see if it is valid */
72 rc = tst_crypto_add_alg(ctx, &alg);
73 if (rc != 0)
74 continue;
75
76 /* it also has to be deletable */
77 rc = tst_crypto_del_alg(ctx, &alg, 1000);
78 if (rc == 0) {
79 algorithm = ALGORITHM_CANDIDATES[i];
80 break;
81 }
82 }
83
84 if (!algorithm)
85 tst_brk(TCONF, "No viable algorithm found");
86 }
87
run(void)88 static void run(void)
89 {
90 struct crypto_user_alg alg = {};
91 pid_t pid;
92 int status;
93
94 strcpy(alg.cru_driver_name, algorithm);
95
96 tst_res(TINFO,
97 "Starting crypto_user larval deletion test using algorithm %s. May crash buggy kernels.",
98 algorithm);
99
100 tst_timer_start(CLOCK_MONOTONIC);
101
102 while (!tst_timer_expired_ms(1000)) {
103 pid = SAFE_FORK();
104
105 if (pid == 0) {
106 /* Child process: execute CRYPTO_MSG_NEWALG. */
107 ctx = NETLINK_CREATE_CONTEXT(NETLINK_CRYPTO);
108 for (;;) {
109 TEST(tst_crypto_add_alg(ctx, &alg));
110 if (TST_RET && TST_RET != -EEXIST)
111 tst_brk(TBROK | TRERRNO,
112 "unexpected error from tst_crypto_add_alg()");
113 }
114 }
115
116 /*
117 * Parent process: kill the child process (hopefully while it's
118 * executing CRYPTO_MSG_NEWALG) and execute CRYPTO_MSG_DELALG.
119 * Buggy kernels sometimes dereferenced a NULL pointer during
120 * CRYPTO_MSG_DELALG here.
121 */
122 usleep(rand() % 5000);
123 kill(pid, SIGKILL);
124 SAFE_WAIT(&status);
125 if (!WIFSIGNALED(status) || WTERMSIG(status) != SIGKILL)
126 tst_brk(TBROK, "child %s", tst_strstatus(status));
127 TEST(tst_crypto_del_alg(ctx, &alg, 1000));
128 if (TST_RET && TST_RET != -ENOENT)
129 tst_brk(TBROK | TRERRNO,
130 "unexpected error from tst_crypto_del_alg()");
131 }
132
133 tst_res(TPASS, "didn't crash");
134 }
135
cleanup(void)136 static void cleanup(void)
137 {
138 NETLINK_DESTROY_CONTEXT(ctx);
139 }
140
141 static struct tst_test test = {
142 .setup = setup,
143 .test_all = run,
144 .cleanup = cleanup,
145 .needs_root = 1,
146 .forks_child = 1,
147 .tags = (const struct tst_tag[]) {
148 {"linux-git", "21d4120ec6f5"},
149 {}
150 }
151 };
152