• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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