• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (c) International Business Machines  Corp., 2001
4  * Copyright (C) 2020 Cyril Hrubis <chrubis@suse.cz>
5  */
6 
7 /*\
8  * [Description]
9  *
10  * Verify that shmctl() IPC_STAT and SHM_STAT reports correct data.
11  *
12  * The shm_nattach is excercised by:
13  *
14  * - forking() children that attach and detach SHM
15  * - attaching the SHM before fork and letting the children detach it
16  *
17  * We check that the number shm_nattach is correct after each step we do.
18  */
19 
20 #define _GNU_SOURCE
21 #include <stdlib.h>
22 #include "tst_test.h"
23 #include "tst_safe_sysv_ipc.h"
24 #include "tst_clocks.h"
25 #include "libnewipc.h"
26 
27 #define NCHILD 20
28 
29 static pid_t children[NCHILD];
30 
31 static int shm_id;
32 static int shm_idx;
33 static time_t ctime_min, ctime_max;
34 
35 static void *addr;
36 
attach_child(void)37 static void attach_child(void)
38 {
39 	pause();
40 
41 	addr = SAFE_SHMAT(shm_id, NULL, 0);
42 
43 	pause();
44 
45 	SAFE_SHMDT(addr);
46 
47 	pause();
48 
49 	exit(0);
50 }
51 
detach_child(void)52 static void detach_child(void)
53 {
54 	pause();
55 
56 	SAFE_SHMDT(addr);
57 
58 	pause();
59 
60 	exit(0);
61 }
62 
fork_children(void (* child_func)(void))63 static void fork_children(void (*child_func)(void))
64 {
65 	unsigned int i;
66 
67 	for (i = 0; i < NCHILD; i++) {
68 		pid_t pid = SAFE_FORK();
69 
70 		if (!pid)
71 			child_func();
72 
73 		children[i] = pid;
74 	}
75 }
76 
wait_for_children(void)77 static void wait_for_children(void)
78 {
79 	unsigned int i;
80 
81 	for (i = 0; i < NCHILD; i++)
82 		TST_PROCESS_STATE_WAIT(children[i], 'S', 0);
83 }
84 
signal_children(void)85 static void signal_children(void)
86 {
87 	unsigned int i;
88 
89 	for (i = 0; i < NCHILD; i++)
90 		SAFE_KILL(children[i], SIGUSR1);
91 }
92 
reap_children(void)93 static void reap_children(void)
94 {
95 	unsigned int i;
96 
97 	for (i = 0; i < NCHILD; i++)
98 		SAFE_WAITPID(children[i], NULL, 0);
99 }
100 
check_nattch(int exp_nattch,const char * msg)101 static void check_nattch(int exp_nattch, const char *msg)
102 {
103 	struct shmid_ds ds1;
104 	struct shmid_ds ds2;
105 
106 	SAFE_SHMCTL(shm_id, IPC_STAT, &ds1);
107 	SAFE_SHMCTL(shm_idx, SHM_STAT, &ds2);
108 
109 	if (ds1.shm_nattch != ds2.shm_nattch) {
110 		tst_res(TFAIL, "IPC_STAT nattch=%li SHM_STAT nattch=%li",
111 			(long)ds1.shm_nattch, (long)ds2.shm_nattch);
112 		return;
113 	}
114 
115 	if ((int)ds1.shm_nattch == exp_nattch) {
116 		tst_res(TPASS, "%s shm_nattch=%i", msg, exp_nattch);
117 		return;
118 	}
119 
120 	tst_res(TFAIL, "%s shm_nattcg=%li expected %i",
121 		msg, (long)ds1.shm_nattch, exp_nattch);
122 }
123 
verify_shmstat_attach(void)124 static void verify_shmstat_attach(void)
125 {
126 	fork_children(attach_child);
127 	wait_for_children();
128 
129 	check_nattch(0, "before child shmat()");
130 
131 	signal_children();
132 	wait_for_children();
133 
134 	check_nattch(NCHILD, "after child shmat()");
135 
136 	signal_children();
137 	wait_for_children();
138 
139 	check_nattch(0, "after child shmdt()");
140 
141 	signal_children();
142 	reap_children();
143 }
144 
verify_shmstat_inherit(void)145 static void verify_shmstat_inherit(void)
146 {
147 	addr = SAFE_SHMAT(shm_id, NULL, 0);
148 
149 	fork_children(detach_child);
150 	wait_for_children();
151 
152 	check_nattch(NCHILD+1, "inherited after fork()");
153 
154 	signal_children();
155 	wait_for_children();
156 
157 	check_nattch(1, "after child shmdt()");
158 
159 	SAFE_SHMDT(addr);
160 
161 	check_nattch(0, "after parent shmdt()");
162 
163 	signal_children();
164 	reap_children();
165 }
166 
check_ds(struct shmid_ds * ds,const char * desc)167 static void check_ds(struct shmid_ds *ds, const char *desc)
168 {
169 	pid_t pid = getpid();
170 
171 	if (ds->shm_segsz != SHM_SIZE) {
172 		tst_res(TFAIL, "%s: shm_segsz=%zu, expected %i",
173 			desc, ds->shm_segsz, SHM_SIZE);
174 	} else {
175 		tst_res(TPASS, "%s: shm_segsz=%i", desc, SHM_SIZE);
176 	}
177 
178 	if (ds->shm_cpid != pid) {
179 		tst_res(TFAIL, "%s: shm_cpid=%i, expected %i",
180 			desc, ds->shm_cpid, pid);
181 	} else {
182 		tst_res(TPASS, "%s: shm_cpid=%i", desc, pid);
183 	}
184 
185 	if (ds->shm_ctime < ctime_min || ds->shm_ctime > ctime_max) {
186 		tst_res(TFAIL, "%s: shm_ctime=%li, expected <%li,%li>",
187 			desc, ds->shm_ctime, ctime_min, ctime_max);
188 	} else {
189 		tst_res(TPASS, "%s: shm_ctime=%li in range <%li,%li>",
190 			desc, ds->shm_ctime, ctime_min, ctime_max);
191 	}
192 }
193 
shmstat_basic_check(void)194 static void shmstat_basic_check(void)
195 {
196 	struct shmid_ds ds;
197 
198 	memset(&ds, 0, sizeof(ds));
199 	SAFE_SHMCTL(shm_id, IPC_STAT, &ds);
200 
201 	check_ds(&ds, "IPC_STAT");
202 
203 	memset(&ds, 0, sizeof(ds));
204 	SAFE_SHMCTL(shm_idx, SHM_STAT, &ds);
205 
206 	check_ds(&ds, "SHM_STAT");
207 }
208 
209 static struct tcase {
210 	void (*func)(void);
211 	const char *desc;
212 } tcases[] = {
213 	{shmstat_basic_check, "Basic checks"},
214 	{verify_shmstat_attach, "Children attach SHM"},
215 	{verify_shmstat_inherit, "Chidlren inherit SHM"},
216 };
217 
verify_shmstat(unsigned int n)218 static void verify_shmstat(unsigned int n)
219 {
220 	tst_res(TINFO, "%s", tcases[n].desc);
221 	tcases[n].func();
222 }
223 
dummy_sighandler(int sig)224 static void dummy_sighandler(int sig)
225 {
226 	(void)sig;
227 }
228 
get_shm_idx_from_id(int shm_id)229 static int get_shm_idx_from_id(int shm_id)
230 {
231 	struct shm_info dummy;
232 	struct shmid_ds dummy_ds;
233 	int max_idx, i;
234 
235 	max_idx = SAFE_SHMCTL(shm_id, SHM_INFO, (void *)&dummy);
236 
237 	for (i = 0; i <= max_idx; i++) {
238 		if (shmctl(i, SHM_STAT, &dummy_ds) == shm_id)
239 			return i;
240 	}
241 
242 	return -1;
243 }
244 
setup(void)245 static void setup(void)
246 {
247 	ctime_min = tst_get_fs_timestamp();
248 	shm_id = SAFE_SHMGET(IPC_PRIVATE, SHM_SIZE, IPC_CREAT | SHM_RW);
249 	ctime_max = tst_get_fs_timestamp();
250 
251 	shm_idx = get_shm_idx_from_id(shm_id);
252 
253 	if (shm_idx < 0)
254 		tst_brk(TBROK, "Failed to get shm_id to idx mapping");
255 
256 	tst_res(TINFO, "shm_id=%i maps to kernel index=%i", shm_id, shm_idx);
257 
258 	SAFE_SIGNAL(SIGUSR1, dummy_sighandler);
259 }
260 
cleanup(void)261 static void cleanup(void)
262 {
263 	if (shm_id >= 0)
264 		SAFE_SHMCTL(shm_id, IPC_RMID, NULL);
265 }
266 
267 static struct tst_test test = {
268 	.setup = setup,
269 	.cleanup = cleanup,
270 	.forks_child = 1,
271 	.test = verify_shmstat,
272 	.tcnt = ARRAY_SIZE(tcases),
273 };
274