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