1 /* Copyright (c) 2014 Red Hat, Inc.
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of version 2 the GNU General Public License as
5 * published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 ***********************************************************************
15 * File: sem_comm.c
16 *
17 * Description:
18 * 1. Clones two child processes with CLONE_NEWIPC flag, each child
19 * creates System V semaphore (sem) with the _identical_ key.
20 * 2. Child1 locks the semaphore.
21 * 3. Child2 locks the semaphore.
22 * 4. Locking the semaphore with the identical key but from two different
23 * IPC namespaces should not interfere with each other, so if child2
24 * is able to lock the semaphore (after child1 locked it), test passes,
25 * otherwise test fails.
26 */
27
28 #define _GNU_SOURCE
29 #include <sys/ipc.h>
30 #include <sys/sem.h>
31 #include <sys/types.h>
32 #include <sys/wait.h>
33 #include <stdio.h>
34 #include <errno.h>
35 #include "ipcns_helper.h"
36 #include "test.h"
37 #include "safe_macros.h"
38 #include "lapi/semun.h"
39
40 #define TESTKEY 124426L
41 char *TCID = "sem_comm";
42 int TST_TOTAL = 1;
43
cleanup(void)44 static void cleanup(void)
45 {
46 tst_rmdir();
47 }
48
setup(void)49 static void setup(void)
50 {
51 tst_require_root();
52 check_newipc();
53 tst_tmpdir();
54 TST_CHECKPOINT_INIT(tst_rmdir);
55 }
56
chld1_sem(void * arg)57 int chld1_sem(void *arg)
58 {
59 int id;
60 union semun su;
61 struct sembuf sm;
62
63 id = semget(TESTKEY, 1, IPC_CREAT);
64 if (id == -1) {
65 perror("semget");
66 return 2;
67 }
68
69 su.val = 1;
70 if (semctl(id, 0, SETVAL, su) == -1) {
71 perror("semctl");
72 semctl(id, 0, IPC_RMID);
73 return 2;
74 }
75
76 /* tell child2 to continue and wait for it to create the semaphore */
77 TST_SAFE_CHECKPOINT_WAKE_AND_WAIT(NULL, 0);
78
79 sm.sem_num = 0;
80 sm.sem_op = -1;
81 sm.sem_flg = IPC_NOWAIT;
82 if (semop(id, &sm, 1) == -1) {
83 perror("semop");
84 semctl(id, 0, IPC_RMID);
85 return 2;
86 }
87
88 /* tell child2 to continue and wait for it to lock the semaphore */
89 TST_SAFE_CHECKPOINT_WAKE_AND_WAIT(NULL, 0);
90
91 sm.sem_op = 1;
92 semop(id, &sm, 1);
93
94 semctl(id, 0, IPC_RMID);
95 return 0;
96 }
97
chld2_sem(void * arg)98 int chld2_sem(void *arg)
99 {
100 int id, rval = 0;
101 struct sembuf sm;
102 union semun su;
103
104 /* wait for child1 to create the semaphore */
105 TST_SAFE_CHECKPOINT_WAIT(NULL, 0);
106
107 id = semget(TESTKEY, 1, IPC_CREAT);
108 if (id == -1) {
109 perror("semget");
110 return 2;
111 }
112
113 su.val = 1;
114 if (semctl(id, 0, SETVAL, su) == -1) {
115 perror("semctl");
116 semctl(id, 0, IPC_RMID);
117 return 2;
118 }
119
120 /* tell child1 to continue and wait for it to lock the semaphore */
121 TST_SAFE_CHECKPOINT_WAKE_AND_WAIT(NULL, 0);
122
123 sm.sem_num = 0;
124 sm.sem_op = -1;
125 sm.sem_flg = IPC_NOWAIT;
126 if (semop(id, &sm, 1) == -1) {
127 if (errno == EAGAIN) {
128 rval = 1;
129 } else {
130 perror("semop");
131 semctl(id, 0, IPC_RMID);
132 return 2;
133 }
134 }
135
136 /* tell child1 to continue */
137 TST_SAFE_CHECKPOINT_WAKE(NULL, 0);
138
139 sm.sem_op = 1;
140 semop(id, &sm, 1);
141
142 semctl(id, 0, IPC_RMID);
143 return rval;
144 }
145
test(void)146 static void test(void)
147 {
148 int status, ret = 0;
149
150 ret = do_clone_unshare_test(T_CLONE, CLONE_NEWIPC, chld1_sem, NULL);
151 if (ret == -1)
152 tst_brkm(TBROK | TERRNO, cleanup, "clone failed");
153
154 ret = do_clone_unshare_test(T_CLONE, CLONE_NEWIPC, chld2_sem, NULL);
155 if (ret == -1)
156 tst_brkm(TBROK | TERRNO, cleanup, "clone failed");
157
158
159 while (wait(&status) > 0) {
160 if (WIFEXITED(status) && WEXITSTATUS(status) == 1)
161 ret = 1;
162 if (WIFEXITED(status) && WEXITSTATUS(status) == 2)
163 tst_brkm(TBROK | TERRNO, cleanup, "error in child");
164 if (WIFSIGNALED(status)) {
165 tst_resm(TFAIL, "child was killed with signal %s",
166 tst_strsig(WTERMSIG(status)));
167 return;
168 }
169 }
170
171 if (ret)
172 tst_resm(TFAIL, "SysV sem: communication with identical keys"
173 " between namespaces");
174 else
175 tst_resm(TPASS, "SysV sem: communication with identical keys"
176 " between namespaces");
177 }
178
main(int argc,char * argv[])179 int main(int argc, char *argv[])
180 {
181 int lc;
182
183 tst_parse_opts(argc, argv, NULL, NULL);
184
185 setup();
186
187 for (lc = 0; TEST_LOOPING(lc); lc++)
188 test();
189
190 cleanup();
191 tst_exit();
192 }
193