1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Copyright (c) 2017 Fujitsu Ltd. 4 * Ported: Guangwen Feng <fenggw-fnst@cn.fujitsu.com> 5 */ 6 7/* 8 * This is a regression test for the race between keyctl_read() and 9 * keyctl_revoke(), if the revoke happens between keyctl_read() 10 * checking the validity of a key and the key's semaphore being taken, 11 * then the key type read method will see a revoked key. 12 * 13 * This causes a problem for the user-defined key type because it 14 * assumes in its read method that there will always be a payload 15 * in a non-revoked key and doesn't check for a NULL pointer. 16 * 17 * This test can crash the buggy kernel, and the bug was fixed in: 18 * 19 * commit b4a1b4f5047e4f54e194681125c74c0aa64d637d 20 * Author: David Howells <dhowells@redhat.com> 21 * Date: Fri Dec 18 01:34:26 2015 +0000 22 * 23 * KEYS: Fix race between read and revoke 24 */ 25 26#include <errno.h> 27#include <pthread.h> 28#include <sys/types.h> 29 30#include "tst_safe_pthread.h" 31#include "tst_test.h" 32#include "lapi/keyctl.h" 33 34#define LOOPS 20000 35#define MAX_WAIT_FOR_GC_MS 5000 36#define PATH_KEY_COUNT_QUOTA "/proc/sys/kernel/keys/root_maxkeys" 37 38static int orig_maxkeys; 39 40static void *do_read(void *arg) 41{ 42 key_serial_t key = (unsigned long)arg; 43 char buffer[4] = { 0 }; 44 45 keyctl(KEYCTL_READ, key, buffer, 4); 46 47 return NULL; 48} 49 50static void *do_revoke(void *arg) 51{ 52 key_serial_t key = (unsigned long)arg; 53 54 keyctl(KEYCTL_REVOKE, key); 55 56 return NULL; 57} 58 59static void do_test(void) 60{ 61 int i, ret; 62 key_serial_t key, key_inv; 63 pthread_t pth[4]; 64 65 for (i = 0; i < LOOPS; i++) { 66 key = add_key("user", "ltptestkey", "foo", 3, 67 KEY_SPEC_PROCESS_KEYRING); 68 if (key == -1) 69 tst_brk(TBROK | TERRNO, "Failed to add key"); 70 71 SAFE_PTHREAD_CREATE(&pth[0], NULL, do_read, 72 (void *)(unsigned long)key); 73 SAFE_PTHREAD_CREATE(&pth[1], NULL, do_revoke, 74 (void *)(unsigned long)key); 75 SAFE_PTHREAD_CREATE(&pth[2], NULL, do_read, 76 (void *)(unsigned long)key); 77 SAFE_PTHREAD_CREATE(&pth[3], NULL, do_revoke, 78 (void *)(unsigned long)key); 79 80 SAFE_PTHREAD_JOIN(pth[0], NULL); 81 SAFE_PTHREAD_JOIN(pth[1], NULL); 82 SAFE_PTHREAD_JOIN(pth[2], NULL); 83 SAFE_PTHREAD_JOIN(pth[3], NULL); 84 85 if (!tst_remaining_runtime()) { 86 tst_res(TINFO, "Runtime exhausted, exiting after %d loops", i); 87 break; 88 } 89 } 90 91 /* 92 * Kernel should start garbage collect when last reference to key 93 * is removed (see key_put()). Since we are adding keys with identical 94 * description and type, each replacement should schedule a gc run, 95 * see comment at __key_link(). 96 * 97 * We create extra key here, to remove reference to last revoked key. 98 */ 99 key_inv = add_key("user", "ltptestkey", "foo", 3, 100 KEY_SPEC_PROCESS_KEYRING); 101 if (key_inv == -1) 102 tst_brk(TBROK | TERRNO, "Failed to add key"); 103 104 /* 105 * If we have invalidate, we can drop extra key immediately as well, 106 * which also schedules gc. 107 */ 108 if (keyctl(KEYCTL_INVALIDATE, key_inv) == -1 && errno != EOPNOTSUPP) 109 tst_brk(TBROK | TERRNO, "Failed to invalidate key"); 110 111 /* 112 * At this point we are quite confident that gc has been scheduled, 113 * so we wait and periodically check for last test key to be removed. 114 */ 115 for (i = 0; i < MAX_WAIT_FOR_GC_MS; i += 100) { 116 ret = keyctl(KEYCTL_REVOKE, key); 117 if (ret == -1 && errno == ENOKEY) 118 break; 119 usleep(100*1000); 120 } 121 122 if (i) 123 tst_res(TINFO, "waiting for key gc took: %d ms", i); 124 tst_res(TPASS, "Bug not reproduced"); 125} 126 127static void setup(void) 128{ 129 SAFE_FILE_SCANF(PATH_KEY_COUNT_QUOTA, "%d", &orig_maxkeys); 130 SAFE_FILE_PRINTF(PATH_KEY_COUNT_QUOTA, "%d", orig_maxkeys + LOOPS + 1); 131} 132 133static void cleanup(void) 134{ 135 if (orig_maxkeys > 0) 136 SAFE_FILE_PRINTF(PATH_KEY_COUNT_QUOTA, "%d", orig_maxkeys); 137} 138 139static struct tst_test test = { 140 .needs_root = 1, 141 .setup = setup, 142 .cleanup = cleanup, 143 .max_runtime = 60, 144 .test_all = do_test, 145 .tags = (const struct tst_tag[]) { 146 {"linux-git", "b4a1b4f5047e"}, 147 {"CVE", "2015-7550"}, 148 {} 149 } 150}; 151