aboutsummaryrefslogtreecommitdiff
path: root/testcases/kernel/syscalls/ipc/shmget/shmget02.c
blob: 8168803a55a1596bc7eb8204a9f3d98280146924 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Copyright (c) International Business Machines  Corp., 2001
 *  03/2001 - Written by Wayne Boyer
 *
 * Copyright (c) 2008 Renaud Lottiaux (Renaud.Lottiaux@kerlabs.com)
 */

/*\
 * [Description]
 *
 * Test for ENOENT, EEXIST, EINVAL, EACCES, EPERM errors.
 *
 * - ENOENT - No segment exists for the given key and IPC_CREAT was not specified.
 * - EEXIST - the segment exists and IPC_CREAT | IPC_EXCL is given.
 * - EINVAL - A new segment was to be created and size is less than SHMMIN or
 *   greater than SHMMAX. Or a segment for the given key exists, but size is
 *   gran eater than the size of that segment.
 * - EACCES - The user does not have permission to access the shared memory segment.
 * - EPERM - The SHM_HUGETLB flag was specified, but the caller was not
 *   privileged (did not have the CAP_IPC_LOCK capability) and is not a member
 *   of the sysctl_hugetlb_shm_group group.
 * - ENOMEM - The SHM_HUGETLB flag was specified, the caller was privileged but
 *   not have enough hugepage memory space.
 */

#include <errno.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <stdlib.h>
#include <pwd.h>
#include <sys/shm.h>
#include <grp.h>
#include "tst_safe_sysv_ipc.h"
#include "tst_kconfig.h"
#include "tst_test.h"
#include "libnewipc.h"
#include "lapi/shm.h"

#define CONFIG_HUGETLBFS "CONFIG_HUGETLBFS"

static int shm_id = -1;
static key_t shmkey, shmkey1;
static struct passwd *pw;

static struct tcase {
	int *shmkey;
	size_t size;
	int flags;
	/*1: nobody expected  0: root expected */
	int exp_user;
	/*1: nobody expected  0: root expected */
	int exp_group;
	int exp_err;
} tcases[] = {
	{&shmkey1, SHM_SIZE, IPC_EXCL, 0, 0, ENOENT},
	{&shmkey, SHM_SIZE, IPC_CREAT | IPC_EXCL, 0, 0, EEXIST},
	{&shmkey1, SHMMIN - 1, IPC_CREAT | IPC_EXCL, 0, 0, EINVAL},
	{&shmkey1, 8192 + 1, IPC_CREAT | IPC_EXCL, 0, 0, EINVAL},
	{&shmkey, SHM_SIZE * 2, IPC_EXCL, 0, 0, EINVAL},
	{&shmkey, SHM_SIZE, SHM_RD, 1, 0, EACCES},
	{&shmkey1, SHM_SIZE, IPC_CREAT | SHM_HUGETLB, 0, 1, EPERM},
	{&shmkey1, SHM_SIZE, IPC_CREAT | SHM_HUGETLB, 0, 0, ENOMEM}
};

static int get_hugetlb_exp_error(void)
{
	long tmp;
	struct tst_kconfig_var kconfig = {
		.id = CONFIG_HUGETLBFS,
		.id_len = sizeof(CONFIG_HUGETLBFS)-1,
	};

	tst_kconfig_read(&kconfig, 1);

	if (kconfig.choice != 'y') {
		tst_res(TINFO, "SHM_HUGETLB not supported by kernel");
		return EINVAL;
	}

	if (FILE_LINES_SCANF("/proc/meminfo", "Hugepagesize: %ld", &tmp)) {
		tst_res(TINFO, "Huge pages not supported by hardware");
		return ENOENT;
	}

	return 0;
}

static void do_test(unsigned int n)
{
	struct tcase *tc = &tcases[n];
	pid_t pid;

	if (tc->exp_user == 0 && tc->exp_group == 0) {
		TST_EXP_FAIL2(shmget(*tc->shmkey, tc->size, tc->flags), tc->exp_err,
			"shmget(%i, %lu, %i)", *tc->shmkey, tc->size, tc->flags);
		return;
	}

	pid = SAFE_FORK();
	if (pid == 0) {
		if (tc->exp_group) {
			SAFE_SETGROUPS(0, NULL);
			SAFE_SETGID(pw->pw_gid);
		}
		SAFE_SETUID(pw->pw_uid);
		TST_EXP_FAIL2(shmget(*tc->shmkey, tc->size, tc->flags), tc->exp_err,
			"shmget(%i, %lu, %i)", *tc->shmkey, tc->size, tc->flags);
		exit(0);
	}
	tst_reap_children();
}

static void setup(void)
{
	struct rlimit rl = { 0, 0 };
	int hugetlb_errno;
	unsigned int i;

	shmkey = GETIPCKEY();
	shmkey1 = GETIPCKEY();

	SAFE_SETRLIMIT(RLIMIT_MEMLOCK, &rl);
	shm_id = SAFE_SHMGET(shmkey, SHM_SIZE, IPC_CREAT | IPC_EXCL);
	pw = SAFE_GETPWNAM("nobody");
	hugetlb_errno = get_hugetlb_exp_error();

	if (!hugetlb_errno)
		return;

	for (i = 0; i < ARRAY_SIZE(tcases); i++) {
		if (tcases[i].flags & SHM_HUGETLB)
			tcases[i].exp_err = hugetlb_errno;
	}
}

static void cleanup(void)
{
	if (shm_id >= 0)
		SAFE_SHMCTL(shm_id, IPC_RMID, NULL);
}

static struct tst_test test = {
	.needs_tmpdir = 1,
	.needs_root = 1,
	.forks_child = 1,
	.setup = setup,
	.cleanup = cleanup,
	.test = do_test,
	.tcnt = ARRAY_SIZE(tcases),
	.hugepages = {TST_NO_HUGEPAGES},
	.save_restore = (const struct tst_path_val[]) {
		{"/proc/sys/kernel/shmmax", "8192", TST_SR_TCONF_MISSING | TST_SR_TBROK_RO},
		{}
	},
};