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 "test.h"
36 #include "safe_macros.h"
37 #include "libclone.h"
38 #include "ipcns_helper.h"
39 #include "lapi/semun.h"
40
41 #define TESTKEY 124426L
42 char *TCID = "sem_comm";
43 int TST_TOTAL = 1;
44
cleanup(void)45 static void cleanup(void)
46 {
47 tst_rmdir();
48 }
49
setup(void)50 static void setup(void)
51 {
52 tst_require_root();
53 check_newipc();
54 tst_tmpdir();
55 TST_CHECKPOINT_INIT(tst_rmdir);
56 }
57
chld1_sem(void * arg)58 int chld1_sem(void *arg)
59 {
60 int id;
61 union semun su;
62 struct sembuf sm;
63
64 id = semget(TESTKEY, 1, IPC_CREAT);
65 if (id == -1) {
66 perror("semget");
67 return 2;
68 }
69
70 su.val = 1;
71 if (semctl(id, 0, SETVAL, su) == -1) {
72 perror("semctl");
73 semctl(id, 0, IPC_RMID);
74 return 2;
75 }
76
77 /* tell child2 to continue and wait for it to create the semaphore */
78 TST_SAFE_CHECKPOINT_WAKE_AND_WAIT(NULL, 0);
79
80 sm.sem_num = 0;
81 sm.sem_op = -1;
82 sm.sem_flg = IPC_NOWAIT;
83 if (semop(id, &sm, 1) == -1) {
84 perror("semop");
85 semctl(id, 0, IPC_RMID);
86 return 2;
87 }
88
89 /* tell child2 to continue and wait for it to lock the semaphore */
90 TST_SAFE_CHECKPOINT_WAKE_AND_WAIT(NULL, 0);
91
92 sm.sem_op = 1;
93 semop(id, &sm, 1);
94
95 semctl(id, 0, IPC_RMID);
96 return 0;
97 }
98
chld2_sem(void * arg)99 int chld2_sem(void *arg)
100 {
101 int id, rval = 0;
102 struct sembuf sm;
103 union semun su;
104
105 /* wait for child1 to create the semaphore */
106 TST_SAFE_CHECKPOINT_WAIT(NULL, 0);
107
108 id = semget(TESTKEY, 1, IPC_CREAT);
109 if (id == -1) {
110 perror("semget");
111 return 2;
112 }
113
114 su.val = 1;
115 if (semctl(id, 0, SETVAL, su) == -1) {
116 perror("semctl");
117 semctl(id, 0, IPC_RMID);
118 return 2;
119 }
120
121 /* tell child1 to continue and wait for it to lock the semaphore */
122 TST_SAFE_CHECKPOINT_WAKE_AND_WAIT(NULL, 0);
123
124 sm.sem_num = 0;
125 sm.sem_op = -1;
126 sm.sem_flg = IPC_NOWAIT;
127 if (semop(id, &sm, 1) == -1) {
128 if (errno == EAGAIN) {
129 rval = 1;
130 } else {
131 perror("semop");
132 semctl(id, 0, IPC_RMID);
133 return 2;
134 }
135 }
136
137 /* tell child1 to continue */
138 TST_SAFE_CHECKPOINT_WAKE(NULL, 0);
139
140 sm.sem_op = 1;
141 semop(id, &sm, 1);
142
143 semctl(id, 0, IPC_RMID);
144 return rval;
145 }
146
test(void)147 static void test(void)
148 {
149 int status, ret = 0;
150
151 ret = do_clone_unshare_test(T_CLONE, CLONE_NEWIPC, chld1_sem, NULL);
152 if (ret == -1)
153 tst_brkm(TBROK | TERRNO, cleanup, "clone failed");
154
155 ret = do_clone_unshare_test(T_CLONE, CLONE_NEWIPC, chld2_sem, NULL);
156 if (ret == -1)
157 tst_brkm(TBROK | TERRNO, cleanup, "clone failed");
158
159
160 while (wait(&status) > 0) {
161 if (WIFEXITED(status) && WEXITSTATUS(status) == 1)
162 ret = 1;
163 if (WIFEXITED(status) && WEXITSTATUS(status) == 2)
164 tst_brkm(TBROK | TERRNO, cleanup, "error in child");
165 if (WIFSIGNALED(status)) {
166 tst_resm(TFAIL, "child was killed with signal %s",
167 tst_strsig(WTERMSIG(status)));
168 return;
169 }
170 }
171
172 if (ret)
173 tst_resm(TFAIL, "SysV sem: communication with identical keys"
174 " between namespaces");
175 else
176 tst_resm(TPASS, "SysV sem: communication with identical keys"
177 " between namespaces");
178 }
179
main(int argc,char * argv[])180 int main(int argc, char *argv[])
181 {
182 int lc;
183
184 tst_parse_opts(argc, argv, NULL, NULL);
185
186 setup();
187
188 for (lc = 0; TEST_LOOPING(lc); lc++)
189 test();
190
191 cleanup();
192 tst_exit();
193 }
194