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