• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (C) 2020 Cyril Hrubis <chrubis@suse.cz>
4  */
5 
6 /*\
7  * [Description]
8  *
9  * Call shmctl() with SHM_INFO flag and check that:
10  *
11  * * The returned index points to a valid SHM by calling SHM_STAT_ANY
12  * * Also count that valid indexes < returned max index sums up to used_ids
13  * * And the data are consistent with /proc/sysvipc/shm
14  *
15  * There is a possible race between the call to the shmctl() and read from the
16  * proc file so this test cannot be run in parallel with any IPC testcases that
17  * adds or removes SHM segments.
18  *
19  * Note what we create a SHM segment in the test setup to make sure that there
20  * is at least one during the testrun.
21  */
22 
23 #define _GNU_SOURCE
24 #include <stdio.h>
25 #include <pwd.h>
26 #include "tst_test.h"
27 #include "tst_safe_sysv_ipc.h"
28 #include "libnewipc.h"
29 #include "lapi/shm.h"
30 
31 #define SHM_SIZE 2048
32 
33 static int shm_id = -1;
34 static uid_t nobody_uid, root_uid;
35 
36 static struct tcases {
37 	uid_t *uid;
38 	char *desc;
39 } tests[] = {
40 	{&nobody_uid, "with nobody user"},
41 	{&root_uid, "with root user"}
42 };
43 
parse_proc_sysvipc(struct shm_info * info)44 static void parse_proc_sysvipc(struct shm_info *info)
45 {
46 	int page_size = getpagesize();
47 	FILE *f = fopen("/proc/sysvipc/shm", "r");
48 	int used_ids = 0;
49 	int shmid_max = 0;
50 	unsigned long shm_rss = 0;
51 	unsigned long shm_swp = 0;
52 	unsigned long shm_tot = 0;
53 
54 	/* Eat header */
55 	for (;;) {
56 		int c = fgetc(f);
57 
58 		if (c == '\n' || c == EOF)
59 			break;
60 	}
61 
62 	int shmid, size, rss, swap;
63 
64 	/*
65 	 * Sum rss, swap and size for all elements listed, which should equal
66 	 * the data returned in the shm_info structure.
67 	 *
68 	 * Note that the size has to be rounded up to nearest multiple of page
69 	 * size.
70 	 */
71 	while (fscanf(f, "%*i %i %*i %i %*i %*i %*i %*i %*i %*i %*i %*i %*i %*i %i %i",
72 			&shmid, &size, &rss, &swap) > 0) {
73 		used_ids++;
74 		shm_rss += rss/page_size;
75 		shm_swp += swap/page_size;
76 		shm_tot += (size + page_size - 1) / page_size;
77 		if (shmid > shmid_max)
78 			shmid_max = shmid;
79 	}
80 
81 	if (info->used_ids != used_ids) {
82 		tst_res(TFAIL, "used_ids = %i, expected %i",
83 			info->used_ids, used_ids);
84 	} else {
85 		tst_res(TPASS, "used_ids = %i", used_ids);
86 	}
87 
88 	if (info->shm_rss != shm_rss) {
89 		tst_res(TFAIL, "shm_rss = %li, expected %li",
90 			info->shm_rss, shm_rss);
91 	} else {
92 		tst_res(TPASS, "shm_rss = %li", shm_rss);
93 	}
94 
95 	if (info->shm_swp != shm_swp) {
96 		tst_res(TFAIL, "shm_swp = %li, expected %li",
97 			info->shm_swp, shm_swp);
98 	} else {
99 		tst_res(TPASS, "shm_swp = %li", shm_swp);
100 	}
101 
102 	if (info->shm_tot != shm_tot) {
103 		tst_res(TFAIL, "shm_tot = %li, expected %li",
104 			info->shm_tot, shm_tot);
105 	} else {
106 		tst_res(TPASS, "shm_tot = %li", shm_tot);
107 	}
108 
109 	fclose(f);
110 }
111 
verify_shminfo(unsigned int n)112 static void verify_shminfo(unsigned int n)
113 {
114 	struct tcases *tc = &tests[n];
115 	struct shm_info info;
116 	struct shmid_ds ds;
117 	int i, shmid, cnt = 0;
118 
119 	tst_res(TINFO, "Test SHM_STAT_ANY %s", tc->desc);
120 
121 	SAFE_SETEUID(*tc->uid);
122 
123 	TEST(shmctl(0, SHM_INFO, (struct shmid_ds *)&info));
124 
125 	if (TST_RET == -1) {
126 		tst_res(TFAIL | TTERRNO, "shmctl(0, SHM_INFO, ...)");
127 		return;
128 	}
129 
130 	shmid = shmctl(TST_RET, SHM_STAT_ANY, &ds);
131 
132 	if (shmid == -1) {
133 		tst_res(TFAIL | TERRNO, "SHM_INFO haven't returned a valid index");
134 	} else {
135 		tst_res(TPASS,
136 			"SHM_INFO returned valid index %li maps to shmid %i",
137 			TST_RET, shmid);
138 	}
139 
140 	for (i = 0; i <= TST_RET; i++) {
141 		if (shmctl(i, SHM_STAT_ANY, &ds) != -1)
142 			cnt++;
143 	}
144 
145 	if (cnt == info.used_ids) {
146 		tst_res(TPASS, "Counted used = %i", cnt);
147 	} else {
148 		tst_res(TFAIL, "Counted used = %i, used_ids = %i",
149 			cnt, info.used_ids);
150 	}
151 
152 	parse_proc_sysvipc(&info);
153 }
154 
setup(void)155 static void setup(void)
156 {
157 	struct passwd *ltpuser = SAFE_GETPWNAM("nobody");
158 	struct shmid_ds temp_ds;
159 
160 	nobody_uid = ltpuser->pw_uid;
161 	root_uid = 0;
162 
163 	shm_id = SAFE_SHMGET(IPC_PRIVATE, SHM_SIZE, IPC_CREAT | SHM_RW);
164 
165 	TEST(shmctl(shm_id, SHM_STAT_ANY, &temp_ds));
166 	if (TST_RET == -1) {
167 		if (TST_ERR == EINVAL)
168 			tst_brk(TCONF, "kernel doesn't support SHM_STAT_ANY");
169 		else
170 			tst_brk(TBROK | TTERRNO,
171 				"Current environment doesn't permit SHM_STAT_ANY");
172 	}
173 }
174 
cleanup(void)175 static void cleanup(void)
176 {
177 	if (shm_id >= 0)
178 		SAFE_SHMCTL(shm_id, IPC_RMID, NULL);
179 }
180 
181 static struct tst_test test = {
182 	.setup = setup,
183 	.cleanup = cleanup,
184 	.test = verify_shminfo,
185 	.tcnt = ARRAY_SIZE(tests),
186 	.needs_root = 1,
187 };
188