1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Copyright (c) International Business Machines Corp., 2001 4 * 03/2001 - Written by Wayne Boyer 5 * 6 * Copyright (c) 2008 Renaud Lottiaux (Renaud.Lottiaux@kerlabs.com) 7 */ 8 9/*\ 10 * [Description] 11 * 12 * Test for ENOENT, EEXIST, EINVAL, EACCES, EPERM errors. 13 * 14 * - ENOENT - No segment exists for the given key and IPC_CREAT was not specified. 15 * - EEXIST - the segment exists and IPC_CREAT | IPC_EXCL is given. 16 * - EINVAL - A new segment was to be created and size is less than SHMMIN or 17 * greater than SHMMAX. Or a segment for the given key exists, but size is 18 * gran eater than the size of that segment. 19 * - EACCES - The user does not have permission to access the shared memory segment. 20 * - EPERM - The SHM_HUGETLB flag was specified, but the caller was not 21 * privileged (did not have the CAP_IPC_LOCK capability) and is not a member 22 * of the sysctl_hugetlb_shm_group group. 23 * - ENOMEM - The SHM_HUGETLB flag was specified, the caller was privileged but 24 * not have enough hugepage memory space. 25 */ 26 27#include <errno.h> 28#include <sys/types.h> 29#include <sys/ipc.h> 30#include <stdlib.h> 31#include <pwd.h> 32#include <sys/shm.h> 33#include <grp.h> 34#include "tst_safe_sysv_ipc.h" 35#include "tst_kconfig.h" 36#include "tst_test.h" 37#include "libnewipc.h" 38#include "lapi/shm.h" 39 40#define CONFIG_HUGETLBFS "CONFIG_HUGETLBFS" 41 42static int shm_id = -1; 43static key_t shmkey, shmkey1; 44static struct passwd *pw; 45 46static struct tcase { 47 int *shmkey; 48 size_t size; 49 int flags; 50 /*1: nobody expected 0: root expected */ 51 int exp_user; 52 /*1: nobody expected 0: root expected */ 53 int exp_group; 54 int exp_err; 55} tcases[] = { 56 {&shmkey1, SHM_SIZE, IPC_EXCL, 0, 0, ENOENT}, 57 {&shmkey, SHM_SIZE, IPC_CREAT | IPC_EXCL, 0, 0, EEXIST}, 58 {&shmkey1, SHMMIN - 1, IPC_CREAT | IPC_EXCL, 0, 0, EINVAL}, 59 {&shmkey1, 8192 + 1, IPC_CREAT | IPC_EXCL, 0, 0, EINVAL}, 60 {&shmkey, SHM_SIZE * 2, IPC_EXCL, 0, 0, EINVAL}, 61 {&shmkey, SHM_SIZE, SHM_RD, 1, 0, EACCES}, 62 {&shmkey1, SHM_SIZE, IPC_CREAT | SHM_HUGETLB, 0, 1, EPERM}, 63 {&shmkey1, SHM_SIZE, IPC_CREAT | SHM_HUGETLB, 0, 0, ENOMEM} 64}; 65 66static int get_hugetlb_exp_error(void) 67{ 68 long tmp; 69 struct tst_kconfig_var kconfig = { 70 .id = CONFIG_HUGETLBFS, 71 .id_len = sizeof(CONFIG_HUGETLBFS)-1, 72 }; 73 74 tst_kconfig_read(&kconfig, 1); 75 76 if (kconfig.choice != 'y') { 77 tst_res(TINFO, "SHM_HUGETLB not supported by kernel"); 78 return EINVAL; 79 } 80 81 if (FILE_LINES_SCANF("/proc/meminfo", "Hugepagesize: %ld", &tmp)) { 82 tst_res(TINFO, "Huge pages not supported by hardware"); 83 return ENOENT; 84 } 85 86 return 0; 87} 88 89static void do_test(unsigned int n) 90{ 91 struct tcase *tc = &tcases[n]; 92 pid_t pid; 93 94 if (tc->exp_user == 0 && tc->exp_group == 0) { 95 TST_EXP_FAIL2(shmget(*tc->shmkey, tc->size, tc->flags), tc->exp_err, 96 "shmget(%i, %lu, %i)", *tc->shmkey, tc->size, tc->flags); 97 return; 98 } 99 100 pid = SAFE_FORK(); 101 if (pid == 0) { 102 if (tc->exp_group) { 103 SAFE_SETGROUPS(0, NULL); 104 SAFE_SETGID(pw->pw_gid); 105 } 106 SAFE_SETUID(pw->pw_uid); 107 TST_EXP_FAIL2(shmget(*tc->shmkey, tc->size, tc->flags), tc->exp_err, 108 "shmget(%i, %lu, %i)", *tc->shmkey, tc->size, tc->flags); 109 exit(0); 110 } 111 tst_reap_children(); 112} 113 114static void setup(void) 115{ 116 struct rlimit rl = { 0, 0 }; 117 int hugetlb_errno; 118 unsigned int i; 119 120 shmkey = GETIPCKEY(); 121 shmkey1 = GETIPCKEY(); 122 123 SAFE_SETRLIMIT(RLIMIT_MEMLOCK, &rl); 124 shm_id = SAFE_SHMGET(shmkey, SHM_SIZE, IPC_CREAT | IPC_EXCL); 125 pw = SAFE_GETPWNAM("nobody"); 126 hugetlb_errno = get_hugetlb_exp_error(); 127 128 if (!hugetlb_errno) 129 return; 130 131 for (i = 0; i < ARRAY_SIZE(tcases); i++) { 132 if (tcases[i].flags & SHM_HUGETLB) 133 tcases[i].exp_err = hugetlb_errno; 134 } 135} 136 137static void cleanup(void) 138{ 139 if (shm_id >= 0) 140 SAFE_SHMCTL(shm_id, IPC_RMID, NULL); 141} 142 143static struct tst_test test = { 144 .needs_tmpdir = 1, 145 .needs_root = 1, 146 .forks_child = 1, 147 .setup = setup, 148 .cleanup = cleanup, 149 .test = do_test, 150 .tcnt = ARRAY_SIZE(tcases), 151 .hugepages = {TST_NO_HUGEPAGES}, 152 .save_restore = (const struct tst_path_val[]) { 153 {"/proc/sys/kernel/shmmax", "8192", TST_SR_TCONF_MISSING | TST_SR_TBROK_RO}, 154 {} 155 }, 156}; 157