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 ([email protected])
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 static int shm_id = -1;
41 static key_t shmkey, shmkey1;
42 static struct passwd *pw;
43
44 static struct tcase {
45 int *shmkey;
46 size_t size;
47 int flags;
48 /*1: nobody expected 0: root expected */
49 int exp_user;
50 /*1: nobody expected 0: root expected */
51 int exp_group;
52 int exp_err;
53 } tcases[] = {
54 {&shmkey1, SHM_SIZE, IPC_EXCL, 0, 0, ENOENT},
55 {&shmkey, SHM_SIZE, IPC_CREAT | IPC_EXCL, 0, 0, EEXIST},
56 {&shmkey1, SHMMIN - 1, IPC_CREAT | IPC_EXCL, 0, 0, EINVAL},
57 {&shmkey1, 8192 + 1, IPC_CREAT | IPC_EXCL, 0, 0, EINVAL},
58 {&shmkey, SHM_SIZE * 2, IPC_EXCL, 0, 0, EINVAL},
59 {&shmkey, SHM_SIZE, SHM_RD, 1, 0, EACCES},
60 {&shmkey1, SHM_SIZE, IPC_CREAT | SHM_HUGETLB, 0, 1, EPERM},
61 {&shmkey1, SHM_SIZE, IPC_CREAT | SHM_HUGETLB, 0, 0, ENOMEM}
62 };
63
get_hugetlb_exp_error(void)64 static int get_hugetlb_exp_error(void)
65 {
66 long tmp;
67 struct tst_kconfig_var kconfig = TST_KCONFIG_INIT("CONFIG_HUGETLBFS");
68
69 tst_kconfig_read(&kconfig, 1);
70
71 if (kconfig.choice != 'y') {
72 tst_res(TINFO, "SHM_HUGETLB not supported by kernel");
73 return EINVAL;
74 }
75
76 if (FILE_LINES_SCANF("/proc/meminfo", "Hugepagesize: %ld", &tmp)) {
77 tst_res(TINFO, "Huge pages not supported by hardware");
78 return ENOENT;
79 }
80
81 return 0;
82 }
83
do_test(unsigned int n)84 static void do_test(unsigned int n)
85 {
86 struct tcase *tc = &tcases[n];
87 pid_t pid;
88
89 if (tc->exp_user == 0 && tc->exp_group == 0) {
90 TST_EXP_FAIL2(shmget(*tc->shmkey, tc->size, tc->flags), tc->exp_err,
91 "shmget(%i, %lu, %i)", *tc->shmkey, tc->size, tc->flags);
92 return;
93 }
94
95 pid = SAFE_FORK();
96 if (pid == 0) {
97 if (tc->exp_group) {
98 SAFE_SETGROUPS(0, NULL);
99 SAFE_SETGID(pw->pw_gid);
100 }
101 SAFE_SETUID(pw->pw_uid);
102 TST_EXP_FAIL2(shmget(*tc->shmkey, tc->size, tc->flags), tc->exp_err,
103 "shmget(%i, %lu, %i)", *tc->shmkey, tc->size, tc->flags);
104 exit(0);
105 }
106 tst_reap_children();
107 }
108
setup(void)109 static void setup(void)
110 {
111 struct rlimit rl = { 0, 0 };
112 int hugetlb_errno;
113 unsigned int i;
114
115 shmkey = GETIPCKEY();
116 shmkey1 = GETIPCKEY();
117
118 SAFE_SETRLIMIT(RLIMIT_MEMLOCK, &rl);
119 shm_id = SAFE_SHMGET(shmkey, SHM_SIZE, IPC_CREAT | IPC_EXCL);
120 pw = SAFE_GETPWNAM("nobody");
121 hugetlb_errno = get_hugetlb_exp_error();
122
123 if (!hugetlb_errno)
124 return;
125
126 for (i = 0; i < ARRAY_SIZE(tcases); i++) {
127 if (tcases[i].flags & SHM_HUGETLB)
128 tcases[i].exp_err = hugetlb_errno;
129 }
130 }
131
cleanup(void)132 static void cleanup(void)
133 {
134 if (shm_id >= 0)
135 SAFE_SHMCTL(shm_id, IPC_RMID, NULL);
136 }
137
138 static struct tst_test test = {
139 .needs_tmpdir = 1,
140 .needs_root = 1,
141 .forks_child = 1,
142 .setup = setup,
143 .cleanup = cleanup,
144 .test = do_test,
145 .tcnt = ARRAY_SIZE(tcases),
146 .hugepages = {TST_NO_HUGEPAGES},
147 .save_restore = (const struct tst_path_val[]) {
148 {"/proc/sys/kernel/shmmax", "8192", TST_SR_TCONF_MISSING | TST_SR_TBROK_RO},
149 {}
150 },
151 };
152