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 privileged
21 * (did not have the CAP_IPC_LOCK capability) and is not a member of the
22 * sysctl_hugetlb_shm_group group.
23 * ENOMEM - The SHM_HUGETLB flag was specified, the caller was privileged but not
24 * 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
42 static int shm_id = -1;
43 static key_t shmkey, shmkey1;
44 static struct passwd *pw;
45
46 static 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, SHMMAX + 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
get_hugetlb_exp_error(void)66 static 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
do_test(unsigned int n)89 static 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
setup(void)114 static 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
cleanup(void)137 static void cleanup(void)
138 {
139 if (shm_id >= 0)
140 SAFE_SHMCTL(shm_id, IPC_RMID, NULL);
141 }
142
143 static 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 .request_hugepages = TST_NO_HUGEPAGES,
152 };
153