// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) International Business Machines Corp., 2001 * 03/2001 - Written by Wayne Boyer * * Copyright (c) 2008 Renaud Lottiaux (Renaud.Lottiaux@kerlabs.com) */ /*\ * [Description] * * Test for ENOENT, EEXIST, EINVAL, EACCES, EPERM errors. * * - ENOENT - No segment exists for the given key and IPC_CREAT was not specified. * - EEXIST - the segment exists and IPC_CREAT | IPC_EXCL is given. * - EINVAL - A new segment was to be created and size is less than SHMMIN or * greater than SHMMAX. Or a segment for the given key exists, but size is * gran eater than the size of that segment. * - EACCES - The user does not have permission to access the shared memory segment. * - EPERM - The SHM_HUGETLB flag was specified, but the caller was not * privileged (did not have the CAP_IPC_LOCK capability) and is not a member * of the sysctl_hugetlb_shm_group group. * - ENOMEM - The SHM_HUGETLB flag was specified, the caller was privileged but * not have enough hugepage memory space. */ #include #include #include #include #include #include #include #include "tst_safe_sysv_ipc.h" #include "tst_kconfig.h" #include "tst_test.h" #include "libnewipc.h" #include "lapi/shm.h" static int shm_id = -1; static key_t shmkey, shmkey1; static struct passwd *pw; static struct tcase { int *shmkey; size_t size; int flags; /*1: nobody expected 0: root expected */ int exp_user; /*1: nobody expected 0: root expected */ int exp_group; int exp_err; } tcases[] = { {&shmkey1, SHM_SIZE, IPC_EXCL, 0, 0, ENOENT}, {&shmkey, SHM_SIZE, IPC_CREAT | IPC_EXCL, 0, 0, EEXIST}, {&shmkey1, SHMMIN - 1, IPC_CREAT | IPC_EXCL, 0, 0, EINVAL}, {&shmkey1, 8192 + 1, IPC_CREAT | IPC_EXCL, 0, 0, EINVAL}, {&shmkey, SHM_SIZE * 2, IPC_EXCL, 0, 0, EINVAL}, {&shmkey, SHM_SIZE, SHM_RD, 1, 0, EACCES}, {&shmkey1, SHM_SIZE, IPC_CREAT | SHM_HUGETLB, 0, 1, EPERM}, {&shmkey1, SHM_SIZE, IPC_CREAT | SHM_HUGETLB, 0, 0, ENOMEM} }; static int get_hugetlb_exp_error(void) { long tmp; struct tst_kconfig_var kconfig = TST_KCONFIG_INIT("CONFIG_HUGETLBFS"); tst_kconfig_read(&kconfig, 1); if (kconfig.choice != 'y') { tst_res(TINFO, "SHM_HUGETLB not supported by kernel"); return EINVAL; } if (FILE_LINES_SCANF("/proc/meminfo", "Hugepagesize: %ld", &tmp)) { tst_res(TINFO, "Huge pages not supported by hardware"); return ENOENT; } return 0; } static void do_test(unsigned int n) { struct tcase *tc = &tcases[n]; pid_t pid; if (tc->exp_user == 0 && tc->exp_group == 0) { TST_EXP_FAIL2(shmget(*tc->shmkey, tc->size, tc->flags), tc->exp_err, "shmget(%i, %lu, %i)", *tc->shmkey, tc->size, tc->flags); return; } pid = SAFE_FORK(); if (pid == 0) { if (tc->exp_group) { SAFE_SETGROUPS(0, NULL); SAFE_SETGID(pw->pw_gid); } SAFE_SETUID(pw->pw_uid); TST_EXP_FAIL2(shmget(*tc->shmkey, tc->size, tc->flags), tc->exp_err, "shmget(%i, %lu, %i)", *tc->shmkey, tc->size, tc->flags); exit(0); } tst_reap_children(); } static void setup(void) { struct rlimit rl = { 0, 0 }; int hugetlb_errno; unsigned int i; shmkey = GETIPCKEY(); shmkey1 = GETIPCKEY(); SAFE_SETRLIMIT(RLIMIT_MEMLOCK, &rl); shm_id = SAFE_SHMGET(shmkey, SHM_SIZE, IPC_CREAT | IPC_EXCL); pw = SAFE_GETPWNAM("nobody"); hugetlb_errno = get_hugetlb_exp_error(); if (!hugetlb_errno) return; for (i = 0; i < ARRAY_SIZE(tcases); i++) { if (tcases[i].flags & SHM_HUGETLB) tcases[i].exp_err = hugetlb_errno; } } static void cleanup(void) { if (shm_id >= 0) SAFE_SHMCTL(shm_id, IPC_RMID, NULL); } static struct tst_test test = { .needs_tmpdir = 1, .needs_root = 1, .forks_child = 1, .setup = setup, .cleanup = cleanup, .test = do_test, .tcnt = ARRAY_SIZE(tcases), .hugepages = {TST_NO_HUGEPAGES}, .save_restore = (const struct tst_path_val[]) { {"/proc/sys/kernel/shmmax", "8192", TST_SR_TCONF_MISSING | TST_SR_TBROK_RO}, {} }, };