1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (c) 2017 Google, Inc.
4 */
5
6 /*
7 * Regression test for commit ea6789980fda ("assoc_array: Fix a buggy
8 * node-splitting case"), or CVE-2017-12193.
9 *
10 * Reproducing this bug requires adding keys to a keyring in a certain way that
11 * triggers a corner case in the kernel's "associative array" implementation,
12 * which is the data structure used to hold keys in a keyring, indexed by type
13 * and description.
14 *
15 * Specifically, the root node of a keyring's associative array must be
16 * completely filled with keys that all cluster together within the same slot.
17 * Then a key must be added which goes in a different slot. On broken kernels,
18 * this caused a NULL pointer dereference in assoc_array_apply_edit().
19 *
20 * This can be done by carefully crafting key descriptions. However, an easier
21 * way is to just add 16 keyrings and then a non-keyring, since keyrings all go
22 * into their own top-level slot. This test takes the easier approach.
23 */
24
25 #include <errno.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <sys/wait.h>
29
30 #include "tst_test.h"
31 #include "lapi/keyctl.h"
32
33 #define ASSOC_ARRAY_FAN_OUT 16
34
35 #define PAYLOAD "payload"
36
37 static char *payload;
38
do_test(void)39 static void do_test(void)
40 {
41 int status;
42
43 TEST(keyctl(KEYCTL_JOIN_SESSION_KEYRING, NULL));
44 if (TST_RET < 0)
45 tst_brk(TBROK | TTERRNO, "failed to join new session keyring");
46
47 if (SAFE_FORK() == 0) {
48 char description[32];
49 int i;
50
51 for (i = 0; i < ASSOC_ARRAY_FAN_OUT; i++) {
52 sprintf(description, "keyring%d", i);
53 TEST(add_key("keyring", description, NULL, 0,
54 KEY_SPEC_SESSION_KEYRING));
55 if (TST_RET < 0) {
56 tst_brk(TBROK | TTERRNO,
57 "unable to create keyring %d", i);
58 }
59 }
60
61 TEST(add_key("user", "userkey", payload, sizeof(PAYLOAD),
62 KEY_SPEC_SESSION_KEYRING));
63 if (TST_RET < 0)
64 tst_brk(TBROK | TTERRNO, "unable to create user key");
65
66 exit(0);
67 }
68
69 SAFE_WAIT(&status);
70 if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
71 tst_res(TPASS, "didn't crash while filling keyring");
72 else if (WIFSIGNALED(status) && WTERMSIG(status) == SIGKILL)
73 tst_res(TFAIL, "kernel oops while filling keyring");
74 else
75 tst_brk(TBROK, "Child %s", tst_strstatus(status));
76 }
77
setup(void)78 static void setup(void)
79 {
80 payload = tst_strdup(PAYLOAD);
81 }
82
83 static struct tst_test test = {
84 .setup = setup,
85 .test_all = do_test,
86 .forks_child = 1,
87 .tags = (const struct tst_tag[]) {
88 {"CVE", "2017-12193"},
89 {"linux-git", "ea6789980fda"},
90 {}
91 }
92 };
93