/* * Copyright (C) 2013-2020 Canonical, Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * This code is a complete clean re-write of the stress tool by * Colin Ian King and attempts to be * backwardly compatible with the stress tool by Amos Waterland * but has more stress tests and more * functionality. * */ #include "stress-ng.h" static const stress_help_t help[] = { { NULL, "key N", "start N workers exercising key operations" }, { NULL, "key-ops N", "stop after N key bogo operations" }, { NULL, NULL, NULL } }; #if defined(HAVE_KEYUTILS_H) && \ defined(HAVE_ADD_KEY) && \ defined(HAVE_KEYCTL) #define MAX_KEYS (256) #define KEYCTL_TIMEOUT (2) static long sys_keyctl(int cmd, ...) { va_list args; long int arg0, arg1, arg2, ret; va_start(args, cmd); arg0 = va_arg(args, long int); arg1 = va_arg(args, long int); arg2 = va_arg(args, long int); ret = syscall(__NR_keyctl, cmd, arg0, arg1, arg2); va_end(args); return ret; } static key_serial_t sys_add_key( const char *type, const char *description, const void *payload, size_t plen, key_serial_t keyring) { return (key_serial_t)syscall(__NR_add_key, type, description, payload, plen, keyring); } #if defined(HAVE_REQUEST_KEY) static key_serial_t sys_request_key( const char *type, const char *description, const char *callout_info, key_serial_t keyring) { return (key_serial_t)syscall(__NR_request_key, type, description, callout_info, keyring); } #endif /* * stress_key * stress key operations */ static int stress_key(const stress_args_t *args) { key_serial_t keys[MAX_KEYS]; pid_t ppid = getppid(); int rc = EXIT_SUCCESS; bool timeout_supported = true; bool no_error = true; do { size_t i = 0, n = 0; char description[64]; char payload[64]; /* Add as many keys as we are allowed */ for (n = 0; n < MAX_KEYS; n++) { (void)snprintf(description, sizeof(description), "stress-ng-key-%u-%" PRIu32 "-%zu", ppid, args->instance, n); (void)snprintf(payload, sizeof(payload), "somedata-%zu", n); keys[n] = sys_add_key("user", description, payload, strlen(payload), KEY_SPEC_PROCESS_KEYRING); if (keys[n] < 0) { if (errno == ENOSYS) { pr_inf("%s: skipping stressor, add_key not implemented\n", args->name); no_error = false; rc = EXIT_NOT_IMPLEMENTED; goto tidy; } if ((errno == ENOMEM) || (errno == EDQUOT)) break; pr_fail("%s: add_key failed, errno=%d (%s)\n", args->name, errno, strerror(errno)); goto tidy; } #if defined(KEYCTL_SET_TIMEOUT) if (timeout_supported) { if (sys_keyctl(KEYCTL_SET_TIMEOUT, keys[n], KEYCTL_TIMEOUT) < 0) { /* Some platforms don't support this */ if (errno == ENOSYS) { timeout_supported = false; } else { pr_fail("%s: keyctl KEYCTL_SET_TIMEOUT failed, errno=%d (%s)\n", args->name, errno, strerror(errno)); } } } if (!keep_stressing_flag()) goto tidy; #endif } /* And manipulate the keys */ for (i = 0; i < n; i++) { (void)snprintf(description, sizeof(description), "stress-ng-key-%u-%" PRIu32 "-%zu", ppid, args->instance, i); #if defined(KEYCTL_DESCRIBE) if (sys_keyctl(KEYCTL_DESCRIBE, keys[i], description) < 0) if ((errno != ENOMEM) && #if defined(EKEYEXPIRED) (errno != EKEYEXPIRED) && #endif #if defined(ENOKEY) (errno != ENOKEY) && #endif (errno != EDQUOT)) { pr_fail("%s: keyctl KEYCTL_DESCRIBE failed, errno=%d (%s)\n", args->name, errno, strerror(errno)); } if (!keep_stressing_flag()) goto tidy; #endif (void)snprintf(payload, sizeof(payload), "somedata-%zu", n); #if defined(KEYCTL_UPDATE) if (sys_keyctl(KEYCTL_UPDATE, keys[i], payload, strlen(payload)) < 0) { if ((errno != ENOMEM) && #if defined(EKEYEXPIRED) (errno != EKEYEXPIRED) && #endif #if defined(ENOKEY) (errno != ENOKEY) && #endif (errno != EDQUOT)) { pr_fail("%s: keyctl KEYCTL_UPDATE failed, errno=%d (%s)\n", args->name, errno, strerror(errno)); } } if (!keep_stressing_flag()) goto tidy; #endif #if defined(KEYCTL_READ) (void)memset(payload, 0, sizeof(payload)); if (sys_keyctl(KEYCTL_READ, keys[i], payload, sizeof(payload)) < 0) { if ((errno != ENOMEM) && #if defined(EKEYEXPIRED) (errno != EKEYEXPIRED) && #endif #if defined(ENOKEY) (errno != ENOKEY) && #endif (errno != EDQUOT)) { pr_fail("%s: keyctl KEYCTL_READ failed, errno=%d (%s)\n", args->name, errno, strerror(errno)); } } if (!keep_stressing_flag()) goto tidy; #endif #if defined(HAVE_REQUEST_KEY) (void)snprintf(description, sizeof(description), "stress-ng-key-%u-%" PRIu32 "-%zu", ppid, args->instance, i); if (sys_request_key("user", description, NULL, KEY_SPEC_PROCESS_KEYRING) < 0) { if ((errno != ENOMEM) && #if defined(EKEYEXPIRED) (errno != EKEYEXPIRED) && #endif #if defined(ENOKEY) (errno != ENOKEY) && #endif (errno != EDQUOT)) { pr_fail("%s: request_key failed, errno=%d (%s)\n", args->name, errno, strerror(errno)); } } if (!keep_stressing_flag()) goto tidy; #endif #if defined(KEYCTL_CHOWN) (void)sys_keyctl(KEYCTL_CHOWN, keys[i], getuid(), -1); (void)sys_keyctl(KEYCTL_CHOWN, keys[i], -1, getgid()); #endif #if defined(KEYCTL_SETPERM) (void)sys_keyctl(KEYCTL_SETPERM, keys[i], KEY_USR_ALL); #endif #if defined(KEYCTL_REVOKE) if (stress_mwc1()) (void)sys_keyctl(KEYCTL_REVOKE, keys[i]); #endif #if defined(KEYCTL_INVALIDATE) (void)sys_keyctl(KEYCTL_INVALIDATE, keys[i]); #endif } tidy: inc_counter(args); /* If we hit too many errors and bailed out early, clean up */ for (i = 0; i < n; i++) { if (keys[i] >= 0) { #if defined(KEYCTL_INVALIDATE) (void)sys_keyctl(KEYCTL_INVALIDATE, keys[i]); #endif } } } while (no_error && keep_stressing()); return rc; } stressor_info_t stress_key_info = { .stressor = stress_key, .class = CLASS_OS, .help = help }; #else stressor_info_t stress_key_info = { .stressor = stress_not_implemented, .class = CLASS_OS, .help = help }; #endif