1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Copyright (c) 2017 Google, Inc. 4 */ 5 6/* 7 * Regression test for two related bugs: 8 * 9 * (1) CVE-2017-15299, fixed by commit 60ff5b2f547a ("KEYS: don't let add_key() 10 * update an uninstantiated key") 11 * (2) CVE-2017-15951, fixed by commit 363b02dab09b ("KEYS: Fix race between 12 * updating and finding a negative key") 13 * 14 * We test for the bugs together because the reproduction steps are essentially 15 * the same: repeatedly try to add/update a key with add_key() while requesting 16 * it with request_key() in another task. This reproduces both bugs: 17 * 18 * For CVE-2017-15299, add_key() has to run while the key being created by 19 * request_key() is still in the "uninstantiated" state. For the "encrypted" or 20 * "trusted" key types (not guaranteed to be available) this caused a NULL 21 * pointer dereference in encrypted_update() or in trusted_update(), 22 * respectively. For the "user" key type, this caused the WARN_ON() in 23 * construct_key() to be hit. 24 * 25 * For CVE-2017-15951, request_key() has to run while the key is "negatively 26 * instantiated" (from a prior request_key()) and is being concurrently changed 27 * to "positively instantiated" via add_key() updating it. This race, which is 28 * a bit more difficult to reproduce, caused the task executing request_key() to 29 * dereference an invalid pointer in __key_link_begin(). 30 */ 31 32#include <errno.h> 33#include <stdbool.h> 34#include <stdlib.h> 35#include <sys/wait.h> 36 37#include "tst_test.h" 38#include "lapi/keyctl.h" 39 40static struct test_case { 41 const char *type; 42 const char *payload; 43 int effort; 44} testcase_list[] = { 45 /* 46 * Briefly test the "encrypted" and/or "trusted" key types when 47 * availaible, mainly to reproduce CVE-2017-15299. 48 */ 49 {"encrypted", "update user:foo 32", 2}, 50 {"trusted", "update", 2}, 51 52 /* 53 * Test the "user" key type for longer, mainly in order to reproduce 54 * CVE-2017-15951. However, without the fix for CVE-2017-15299 as well, 55 * WARNs may show up in the kernel log. 56 * 57 * Note: the precise iteration count is arbitrary; it's just intended to 58 * be enough to give a decent chance of reproducing the bug, without 59 * wasting too much time. 60 */ 61 {"user", "payload", 20}, 62}; 63 64static char *opt_bug; 65 66static void run_child_add(const char *type, const char *payload, int effort) 67{ 68 int i; 69 70 /* 71 * Depending on the state of the key, add_key() should either succeed or 72 * fail with one of several errors: 73 * 74 * (1) key didn't exist at all: either add_key() should succeed (if the 75 * payload is valid), or it should fail with EINVAL (if the payload 76 * is invalid; this is needed for the "encrypted" and "trusted" key 77 * types because they have a quirk where the payload syntax differs 78 * for creating new keys vs. updating existing keys) 79 * 80 * (2) key was negative: add_key() should succeed 81 * 82 * (3) key was uninstantiated: add_key() should wait for the key to be 83 * negated, then fail with ENOKEY 84 * 85 * For now we also accept EDQUOT because the kernel frees up the keys 86 * quota asynchronously after keys are unlinked. So it may be hit. 87 */ 88 for (i = 0; i < 100 * effort; i++) { 89 usleep(rand() % 1024); 90 TEST(add_key(type, "desc", payload, strlen(payload), 91 KEY_SPEC_SESSION_KEYRING)); 92 if (TST_RET < 0 && TST_ERR != EINVAL && TST_ERR != ENOKEY && 93 TST_ERR != EDQUOT) { 94 tst_brk(TBROK | TTERRNO, 95 "unexpected error adding key of type '%s'", 96 type); 97 } 98 99 TEST(keyctl(KEYCTL_CLEAR, KEY_SPEC_SESSION_KEYRING)); 100 101 if (TST_RET < 0) 102 tst_brk(TBROK | TTERRNO, "unable to clear keyring"); 103 104 if (!tst_remaining_runtime()) { 105 tst_res(TINFO, "add_key() process runtime exceeded"); 106 break; 107 } 108 } 109} 110 111static void run_child_request(const char *type, int effort) 112{ 113 int i; 114 115 for (i = 0; i < 5000 * effort; i++) { 116 TEST(request_key(type, "desc", "callout_info", 117 KEY_SPEC_SESSION_KEYRING)); 118 if (TST_RET < 0 && TST_ERR != ENOKEY && TST_ERR != ENOENT && 119 TST_ERR != EDQUOT) { 120 tst_brk(TBROK | TTERRNO, 121 "unexpected error requesting key of type '%s'", 122 type); 123 } 124 125 if (!tst_remaining_runtime()) { 126 tst_res(TINFO, 127 "request_key() process runtime exceeded"); 128 break; 129 } 130 } 131} 132 133static void do_test(unsigned int n) 134{ 135 int status; 136 pid_t add_key_pid; 137 pid_t request_key_pid; 138 bool info_only; 139 struct test_case *tc = testcase_list + n; 140 141 TEST(keyctl(KEYCTL_JOIN_SESSION_KEYRING, NULL)); 142 if (TST_RET < 0) 143 tst_brk(TBROK | TTERRNO, "failed to join new session keyring"); 144 145 TEST(add_key(tc->type, "desc", tc->payload, strlen(tc->payload), 146 KEY_SPEC_SESSION_KEYRING)); 147 if (TST_RET < 0 && TST_ERR != EINVAL) { 148 if (TST_ERR == ENODEV) { 149 tst_res(TCONF, "kernel doesn't support key type '%s'", 150 tc->type); 151 return; 152 } 153 tst_brk(TBROK | TTERRNO, 154 "unexpected error checking whether key type '%s' is supported", 155 tc->type); 156 } 157 158 /* 159 * Fork a subprocess which repeatedly tries to "add" a key of the given 160 * type. This actually will try to update the key if it already exists. 161 */ 162 add_key_pid = SAFE_FORK(); 163 if (add_key_pid == 0) { 164 run_child_add(tc->type, tc->payload, tc->effort); 165 exit(0); 166 } 167 168 request_key_pid = SAFE_FORK(); 169 if (request_key_pid == 0) { 170 run_child_request(tc->type, tc->effort); 171 exit(0); 172 } 173 174 /* 175 * Verify that neither the add_key() nor the request_key() process 176 * crashed. If the add_key() process crashed it is likely due to 177 * CVE-2017-15299, while if the request_key() process crashed it is 178 * likely due to CVE-2017-15951. If testing for one of the bugs 179 * specifically, only pay attention to the corresponding process. 180 */ 181 182 SAFE_WAITPID(add_key_pid, &status, 0); 183 info_only = (opt_bug && strcmp(opt_bug, "cve-2017-15299") != 0); 184 if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { 185 tst_res(info_only ? TINFO : TPASS, 186 "didn't crash while updating key of type '%s'", 187 tc->type); 188 } else if (WIFSIGNALED(status) && WTERMSIG(status) == SIGKILL) { 189 tst_res(info_only ? TINFO : TFAIL, 190 "kernel oops while updating key of type '%s'", 191 tc->type); 192 } else { 193 tst_brk(TBROK, "add_key child %s", tst_strstatus(status)); 194 } 195 196 SAFE_WAITPID(request_key_pid, &status, 0); 197 info_only = (opt_bug && strcmp(opt_bug, "cve-2017-15951") != 0); 198 if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { 199 tst_res(info_only ? TINFO : TPASS, 200 "didn't crash while requesting key of type '%s'", 201 tc->type); 202 } else if (WIFSIGNALED(status) && WTERMSIG(status) == SIGKILL) { 203 tst_res(info_only ? TINFO : TFAIL, 204 "kernel oops while requesting key of type '%s'", 205 tc->type); 206 } else { 207 tst_brk(TBROK, "request_key child %s", tst_strstatus(status)); 208 } 209} 210 211static struct tst_test test = { 212 .test = do_test, 213 .tcnt = ARRAY_SIZE(testcase_list), 214 .forks_child = 1, 215 .max_runtime = 20, 216 .options = (struct tst_option[]) { 217 {"b:", &opt_bug, "Bug to test for (cve-2017-15299 or cve-2017-15951; default is both)"}, 218 {} 219 }, 220 .tags = (const struct tst_tag[]) { 221 {"CVE", "2017-15299"}, 222 {"linux-git", "60ff5b2f547a"}, 223 {"CVE", "2017-15951"}, 224 {"linux-git", "363b02dab09b"}, 225 {}, 226 } 227}; 228