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