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