1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (c) 2017 Google, Inc.
4 * Copyright (c) Linux Test Project, 2018-2024
5 */
6
7 /*\
8 * [Description]
9 *
10 * Regression test for commit 37863c43b2c6 ("KEYS: prevent KEYCTL_READ on
11 * negative key"). This is CVE-2017-12192.
12 */
13
14 #include <errno.h>
15 #include <stdlib.h>
16 #include <sys/wait.h>
17
18 #include "tst_test.h"
19 #include "lapi/keyctl.h"
20
try_to_read_negative_key(void)21 static void try_to_read_negative_key(void)
22 {
23 key_serial_t key_id;
24 char buffer[128];
25
26 /*
27 * Create a negatively instantiated key of the "user" key type. This
28 * key type is chosen because it has a ->read() method (which makes the
29 * bug reachable) and is available whenever CONFIG_KEYS is enabled.
30 *
31 * request_key() will result in the creation of a negative key provided
32 * that /sbin/request-key isn't configured to positively instantiate the
33 * key, based on the provided type, description, and callout_info. If
34 * /sbin/request-key doesn't exist, errno will be ENOENT; while if it
35 * does exist and we specify some random unprefixed description, errno
36 * should be ENOKEY (since /sbin/request-key should not be configured to
37 * instantiate random user keys). In either case a negative key should
38 * be created and we can continue on with the test. Negative keys last
39 * for 60 seconds so there should be plenty of time for the test.
40 */
41 TEST(request_key("user", "description", "callout_info",
42 KEY_SPEC_PROCESS_KEYRING));
43 if (TST_RET != -1)
44 tst_brk(TBROK, "request_key() unexpectedly succeeded");
45
46 if (TST_ERR != ENOKEY && TST_ERR != ENOENT) {
47 tst_brk(TBROK | TTERRNO,
48 "request_key() failed with unexpected error");
49 }
50
51 /* Get the ID of the negative key by reading the keyring */
52 TEST(keyctl(KEYCTL_READ, KEY_SPEC_PROCESS_KEYRING,
53 &key_id, sizeof(key_id)));
54 if (TST_RET < 0)
55 tst_brk(TBROK | TTERRNO, "KEYCTL_READ unexpectedly failed");
56 if (TST_RET != sizeof(key_id)) {
57 tst_brk(TBROK, "KEYCTL_READ returned %ld but expected %zu",
58 TST_RET, sizeof(key_id));
59 }
60
61 /*
62 * Now try to read the negative key. Unpatched kernels will oops trying
63 * to read from memory address 0x00000000ffffff92.
64 */
65 tst_res(TINFO, "trying to read from the negative key...");
66 TEST(keyctl(KEYCTL_READ, key_id, buffer, sizeof(buffer)));
67 if (TST_RET != -1) {
68 tst_brk(TFAIL,
69 "KEYCTL_READ on negative key unexpectedly succeeded");
70 }
71 if (TST_ERR != ENOKEY) {
72 tst_brk(TFAIL | TTERRNO,
73 "KEYCTL_READ on negative key failed with unexpected error");
74 }
75 tst_res(TPASS,
76 "KEYCTL_READ on negative key expectedly failed with ENOKEY");
77 }
78
do_test(void)79 static void do_test(void)
80 {
81 int status;
82
83 if (SAFE_FORK() == 0) {
84 try_to_read_negative_key();
85 return;
86 }
87
88 SAFE_WAIT(&status);
89
90 if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
91 tst_res(TPASS, "didn't crash while reading from negative key");
92 return;
93 }
94
95 if (WIFSIGNALED(status) && WTERMSIG(status) == SIGKILL) {
96 tst_res(TFAIL, "reading from negative key caused kernel oops");
97 return;
98 }
99
100 if (WIFEXITED(status) && WEXITSTATUS(status) == TCONF)
101 tst_brk(TCONF, "syscall not implemented");
102
103 tst_brk(TBROK, "Child %s", tst_strstatus(status));
104 }
105
106 static struct tst_test test = {
107 .test_all = do_test,
108 .forks_child = 1,
109 .tags = (const struct tst_tag[]) {
110 {"CVE", "2017-12192"},
111 {"linux-git", "37863c43b2c6"},
112 {}
113 }
114 };
115