1 /*
2 *
3 * Copyright (c) International Business Machines Corp., 2001
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
13 * the GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20 /*
21 * NAME
22 * fcntl17.c
23 *
24 * DESCRIPTION
25 * Check deadlock detection for file locking
26 *
27 * ALGORITHM
28 * The parent forks off 3 children. The parent controls the children
29 * with messages via pipes to create a delayed deadlock between the
30 * second and third child.
31 *
32 * USAGE
33 * fcntl17
34 *
35 * HISTORY
36 * 07/2001 Ported by Wayne Boyer
37 * 04/2002 Minor fixes by William Jay Huie (testcase name
38 fcntl05 => fcntl17, check signal return for SIG_ERR)
39 *
40 * RESTRICTIONS
41 * None
42 */
43
44 #ifndef _GNU_SOURCE
45 #define _GNU_SOURCE
46 #endif
47
48 #include <fcntl.h>
49 #include <errno.h>
50 #include <signal.h>
51 #include <sys/stat.h>
52 #include <sys/types.h>
53 #include <sys/wait.h>
54 #include <inttypes.h>
55
56 #include "test.h"
57
58 char *TCID = "fcntl17";
59 int TST_TOTAL = 1;
60
61 #define STRINGSIZE 27
62 #define STRING "abcdefghijklmnopqrstuvwxyz\n"
63 #define STOP 0xFFF0
64 #define TIME_OUT 10
65
66 /* global variables */
67 int parent_pipe[2];
68 int child_pipe1[2];
69 int child_pipe2[2];
70 int child_pipe3[2];
71 int file_fd;
72 pid_t parent_pid, child_pid1, child_pid2, child_pid3;
73 int child_stat;
74 struct flock lock1 = { (short)F_WRLCK, (short)0, 2, 5, (short)0 };
75 struct flock lock2 = { (short)F_WRLCK, (short)0, 9, 5, (short)0 };
76 struct flock lock3 = { (short)F_WRLCK, (short)0, 17, 5, (short)0 };
77 struct flock lock4 = { (short)F_WRLCK, (short)0, 17, 5, (short)0 };
78 struct flock lock5 = { (short)F_WRLCK, (short)0, 2, 14, (short)0 };
79 struct flock unlock = { (short)F_UNLCK, (short)0, 0, 0, (short)0 };
80
81 /* prototype declarations */
82 int setup();
83 void cleanup();
84 int parent_wait();
85 void parent_free();
86 void child_wait();
87 void child_free();
88 void do_child1();
89 void do_child2();
90 void do_child3();
91 int do_test(struct flock *, pid_t);
92 void stop_children();
93 void catch_child();
94 void catch_alarm();
95 char *str_type();
96
setup(void)97 int setup(void)
98 {
99 char *buf = STRING;
100 char template[PATH_MAX];
101 struct sigaction act;
102
103 tst_sig(FORK, DEF_HANDLER, NULL);
104 umask(0);
105 TEST_PAUSE;
106 tst_tmpdir(); /* make temp dir and cd to it */
107
108 if (pipe(parent_pipe) < 0) {
109 tst_resm(TFAIL, "Couldn't create parent_pipe! errno = %d",
110 errno);
111 return 1;
112 }
113 if (pipe(child_pipe1) < 0) {
114 tst_resm(TFAIL, "Couldn't create child_pipe1! errno = %d",
115 errno);
116 return 1;
117 }
118 if (pipe(child_pipe2) < 0) {
119 tst_resm(TFAIL, "Couldn't create child_pipe2! errno = %d",
120 errno);
121 return 1;
122 }
123 if (pipe(child_pipe3) < 0) {
124 tst_resm(TFAIL, "Couldn't create child_pipe3! errno = %d",
125 errno);
126 return 1;
127 }
128 parent_pid = getpid();
129 snprintf(template, PATH_MAX, "fcntl17XXXXXX");
130
131 if ((file_fd = mkstemp(template)) < 0) {
132 tst_resm(TFAIL, "Couldn't open temp file! errno = %d", errno);
133 }
134
135 if (write(file_fd, buf, STRINGSIZE) < 0) {
136 tst_resm(TFAIL, "Couldn't write to temp file! errno = %d",
137 errno);
138 }
139
140 memset(&act, 0, sizeof(act));
141 act.sa_handler = catch_alarm;
142 sigemptyset(&act.sa_mask);
143 sigaddset(&act.sa_mask, SIGALRM);
144 if (sigaction(SIGALRM, &act, NULL) < 0) {
145 tst_resm(TFAIL, "SIGALRM signal setup failed, errno: %d",
146 errno);
147 return 1;
148 }
149
150 memset(&act, 0, sizeof(act));
151 act.sa_handler = catch_child;
152 sigemptyset(&act.sa_mask);
153 sigaddset(&act.sa_mask, SIGCHLD);
154 if (sigaction(SIGCHLD, &act, NULL) < 0) {
155 tst_resm(TFAIL, "SIGCHLD signal setup failed, errno: %d", errno);
156 return 1;
157 }
158 return 0;
159 }
160
cleanup(void)161 void cleanup(void)
162 {
163 if (child_pid1 > 0)
164 kill(child_pid1, 9);
165
166 if (child_pid2 > 0)
167 kill(child_pid2, 9);
168
169 if (child_pid3 > 0)
170 kill(child_pid3, 9);
171
172 close(file_fd);
173 tst_rmdir();
174
175 }
176
do_child1(void)177 void do_child1(void)
178 {
179 int err;
180
181 close(parent_pipe[0]);
182 close(child_pipe1[1]);
183 close(child_pipe2[0]);
184 close(child_pipe2[1]);
185 close(child_pipe3[0]);
186 close(child_pipe3[1]);
187
188 child_wait(child_pipe1[0]);
189 tst_resm(TINFO, "child 1 starting");
190 if (fcntl(file_fd, F_SETLK, &lock1) < 0) {
191 err = errno;
192 tst_resm(TINFO, "child 1 lock err %d", err);
193 parent_free(err);
194 } else {
195 tst_resm(TINFO, "child 1 pid %d locked", getpid());
196 parent_free(0);
197 }
198
199 child_wait(child_pipe1[0]);
200 tst_resm(TINFO, "child 1 resuming");
201 fcntl(file_fd, F_SETLK, &unlock);
202 tst_resm(TINFO, "child 1 unlocked");
203
204 child_wait(child_pipe1[0]);
205 tst_resm(TINFO, "child 1 exiting");
206 exit(1);
207 }
208
do_child2(void)209 void do_child2(void)
210 {
211 int err;
212
213 close(parent_pipe[0]);
214 close(child_pipe1[0]);
215 close(child_pipe1[1]);
216 close(child_pipe2[1]);
217 close(child_pipe3[0]);
218 close(child_pipe3[1]);
219
220 child_wait(child_pipe2[0]);
221 tst_resm(TINFO, "child 2 starting");
222 if (fcntl(file_fd, F_SETLK, &lock2) < 0) {
223 err = errno;
224 tst_resm(TINFO, "child 2 lock err %d", err);
225 parent_free(err);
226 } else {
227 tst_resm(TINFO, "child 2 pid %d locked", getpid());
228 parent_free(0);
229 }
230
231 child_wait(child_pipe2[0]);
232 tst_resm(TINFO, "child 2 resuming");
233 if (fcntl(file_fd, F_SETLKW, &lock4) < 0) {
234 err = errno;
235 tst_resm(TINFO, "child 2 lockw err %d", err);
236 parent_free(err);
237 } else {
238 tst_resm(TINFO, "child 2 lockw locked");
239 parent_free(0);
240 }
241
242 child_wait(child_pipe2[0]);
243 tst_resm(TINFO, "child 2 exiting");
244 exit(1);
245 }
246
do_child3(void)247 void do_child3(void)
248 {
249 int err;
250
251 close(parent_pipe[0]);
252 close(child_pipe1[0]);
253 close(child_pipe1[1]);
254 close(child_pipe2[0]);
255 close(child_pipe2[1]);
256 close(child_pipe3[1]);
257
258 child_wait(child_pipe3[0]);
259 tst_resm(TINFO, "child 3 starting");
260 if (fcntl(file_fd, F_SETLK, &lock3) < 0) {
261 err = errno;
262 tst_resm(TINFO, "child 3 lock err %d", err);
263 parent_free(err);
264 } else {
265 tst_resm(TINFO, "child 3 pid %d locked", getpid());
266 parent_free(0);
267 }
268
269 child_wait(child_pipe3[0]);
270 tst_resm(TINFO, "child 3 resuming");
271 if (fcntl(file_fd, F_SETLKW, &lock5) < 0) {
272 err = errno;
273 tst_resm(TINFO, "child 3 lockw err %d", err);
274 parent_free(err);
275 } else {
276 tst_resm(TINFO, "child 3 lockw locked");
277 parent_free(0);
278 }
279
280 child_wait(child_pipe3[0]);
281 tst_resm(TINFO, "child 3 exiting");
282 exit(1);
283 }
284
do_test(struct flock * lock,pid_t pid)285 int do_test(struct flock *lock, pid_t pid)
286 {
287 struct flock fl;
288
289 fl.l_type = /* lock->l_type */ F_RDLCK;
290 fl.l_whence = lock->l_whence;
291 fl.l_start = lock->l_start;
292 fl.l_len = lock->l_len;
293 fl.l_pid = (short)0;
294 if (fcntl(file_fd, F_GETLK, &fl) < 0) {
295 tst_resm(TFAIL, "fcntl on file failed, errno =%d", errno);
296 return 1;
297 }
298
299 if (fl.l_type != lock->l_type) {
300 tst_resm(TFAIL, "lock type is wrong should be %s is %s",
301 str_type(lock->l_type), str_type(fl.l_type));
302 return 1;
303 }
304
305 if (fl.l_whence != lock->l_whence) {
306 tst_resm(TFAIL, "lock whence is wrong should be %d is %d",
307 lock->l_whence, fl.l_whence);
308 return 1;
309 }
310
311 if (fl.l_start != lock->l_start) {
312 tst_resm(TFAIL, "region starts in wrong place, "
313 "should be %" PRId64 " is %" PRId64,
314 (int64_t) lock->l_start, (int64_t) fl.l_start);
315 return 1;
316 }
317
318 if (fl.l_len != lock->l_len) {
319 tst_resm(TFAIL,
320 "region length is wrong, should be %" PRId64 " is %"
321 PRId64, (int64_t) lock->l_len, (int64_t) fl.l_len);
322 return 1;
323 }
324
325 if (fl.l_pid != pid) {
326 tst_resm(TFAIL, "locking pid is wrong, should be %d is %d",
327 pid, fl.l_pid);
328 return 1;
329 }
330 return 0;
331 }
332
str_type(int type)333 char *str_type(int type)
334 {
335 static char buf[20];
336
337 switch (type) {
338 case F_RDLCK:
339 return ("F_RDLCK");
340 case F_WRLCK:
341 return ("F_WRLCK");
342 case F_UNLCK:
343 return ("F_UNLCK");
344 default:
345 sprintf(buf, "BAD VALUE: %d", type);
346 return (buf);
347 }
348 }
349
parent_free(int arg)350 void parent_free(int arg)
351 {
352 if (write(parent_pipe[1], &arg, sizeof(arg)) != sizeof(arg)) {
353 tst_resm(TFAIL, "couldn't send message to parent");
354 exit(1);
355 }
356 }
357
parent_wait(void)358 int parent_wait(void)
359 {
360 int arg;
361
362 if (read(parent_pipe[0], &arg, sizeof(arg)) != sizeof(arg)) {
363 tst_resm(TFAIL, "parent_wait() failed");
364 return (errno);
365 }
366 return (arg);
367 }
368
child_free(int fd,int arg)369 void child_free(int fd, int arg)
370 {
371 if (write(fd, &arg, sizeof(arg)) != sizeof(arg)) {
372 tst_resm(TFAIL, "couldn't send message to child");
373 exit(1);
374 }
375 }
376
child_wait(int fd)377 void child_wait(int fd)
378 {
379 int arg;
380
381 if (read(fd, &arg, sizeof(arg)) != sizeof(arg)) {
382 tst_resm(TFAIL, "couldn't get message from parent");
383 exit(1);
384 } else if (arg == (short)STOP) {
385 exit(0);
386 }
387 }
388
stop_children(void)389 void stop_children(void)
390 {
391 int arg;
392
393 signal(SIGCHLD, SIG_DFL);
394 arg = STOP;
395 child_free(child_pipe1[1], arg);
396 child_free(child_pipe2[1], arg);
397 child_free(child_pipe3[1], arg);
398 waitpid(child_pid1, &child_stat, 0);
399 child_pid1 = 0;
400 waitpid(child_pid2, &child_stat, 0);
401 child_pid2 = 0;
402 waitpid(child_pid3, &child_stat, 0);
403 child_pid3 = 0;
404 }
405
catch_child(void)406 void catch_child(void)
407 {
408 tst_resm(TFAIL, "Unexpected death of child process");
409 cleanup();
410 }
411
catch_alarm(void)412 void catch_alarm(void)
413 {
414 sighold(SIGCHLD);
415 /*
416 * Timer has runout and the children have not detected the deadlock.
417 * Need to kill the kids and exit
418 */
419 if (child_pid1 != 0 && (kill(child_pid1, SIGKILL)) < 0) {
420 tst_resm(TFAIL, "Attempt to signal child 1 failed.");
421 }
422
423 if (child_pid2 != 0 && (kill(child_pid2, SIGKILL)) < 0) {
424 tst_resm(TFAIL, "Attempt to signal child 2 failed.");
425 }
426 if (child_pid3 != 0 && (kill(child_pid3, SIGKILL)) < 0) {
427 tst_resm(TFAIL, "Attempt to signal child 2 failed.");
428 }
429 tst_resm(TFAIL, "Alarm expired, deadlock not detected");
430 tst_resm(TWARN, "You may need to kill child processes by hand");
431 cleanup();
432 }
433
main(int ac,char ** av)434 int main(int ac, char **av)
435 {
436 int ans;
437 int lc;
438 int fail = 0;
439
440 tst_parse_opts(ac, av, NULL, NULL);
441
442 if (setup()) { /* global testup */
443 tst_resm(TINFO, "setup failed");
444 cleanup();
445 }
446
447 /* check for looping state if -i option is given */
448 for (lc = 0; TEST_LOOPING(lc); lc++) {
449 /* reset tst_count in case we are looping */
450 tst_count = 0;
451
452 tst_resm(TINFO, "Enter preparation phase");
453 if ((child_pid1 = tst_fork()) == 0) /* first child */
454 do_child1();
455 else if (child_pid1 < 0)
456 tst_brkm(TBROK|TERRNO, cleanup, "Fork failed: child 1");
457
458 /* parent */
459
460 if ((child_pid2 = fork()) == 0) /* second child */
461 do_child2();
462 else if (child_pid2 < 0)
463 tst_brkm(TBROK|TERRNO, cleanup, "Fork failed: child 2");
464
465 /* parent */
466
467 if ((child_pid3 = fork()) == 0) { /* third child */
468 do_child3();
469 do_child3();
470 } else if (child_pid3 < 0) {
471 tst_brkm(TBROK|TERRNO, cleanup, "Fork failed: child 3");
472 }
473 /* parent */
474
475 close(parent_pipe[1]);
476 close(child_pipe1[0]);
477 close(child_pipe2[0]);
478 close(child_pipe3[0]);
479 tst_resm(TINFO, "Exit preparation phase");
480
481 /* //block1: */
482 tst_resm(TINFO, "Enter block 1");
483 fail = 0;
484 /*
485 * child 1 puts first lock (bytes 2-7)
486 */
487 child_free(child_pipe1[1], 0);
488 if (parent_wait()) {
489 tst_resm(TFAIL, "didn't set first child's lock, "
490 "errno: %d", errno);
491 }
492 if (do_test(&lock1, child_pid1)) {
493 tst_resm(TINFO, "do_test failed child 1");
494 fail = 1;
495 }
496
497 /*
498 * child 2 puts second lock (bytes 9-14)
499 */
500 child_free(child_pipe2[1], 0);
501 if (parent_wait()) {
502 tst_resm(TINFO, "didn't set second child's lock, "
503 "errno: %d", errno);
504 fail = 1;
505 }
506 if (do_test(&lock2, child_pid2)) {
507 tst_resm(TINFO, "do_test failed child 2");
508 fail = 1;
509 }
510
511 /*
512 * child 3 puts third lock (bytes 17-22)
513 */
514 child_free(child_pipe3[1], 0);
515 if (parent_wait()) {
516 tst_resm(TFAIL, "didn't set third child's lock, "
517 "errno: %d", errno);
518 fail = 1;
519 }
520 if (do_test(&lock3, child_pid3)) {
521 tst_resm(TINFO, "do_test failed child 3");
522 fail = 1;
523 }
524
525 /*
526 * child 2 tries to lock same range as
527 * child 3's first lock.
528 */
529 child_free(child_pipe2[1], 0);
530
531 /*
532 * child 3 tries to lock same range as
533 * child 1 and child 2's first locks.
534 */
535 child_free(child_pipe3[1], 0);
536
537 /*
538 * Tell child 1 to release its lock. This should cause a
539 * delayed deadlock between child 2 and child 3.
540 */
541 child_free(child_pipe1[1], 0);
542
543 /*
544 * Setup an alarm to go off in case the deadlock is not
545 * detected
546 */
547 alarm(TIME_OUT);
548
549 /*
550 * should get a message from child 3 telling that its
551 * second lock EDEADLOCK
552 */
553 if ((ans = parent_wait()) != EDEADLK) {
554 tst_resm(TFAIL, "child 2 didn't deadlock, "
555 "returned: %d", ans);
556 fail = 1;
557 }
558
559 /*
560 * Double check that lock 2 and lock 3 are still right
561 */
562 do_test(&lock2, child_pid2);
563 do_test(&lock3, child_pid3);
564
565 stop_children();
566
567 if (fail) {
568 tst_resm(TFAIL, "Block 1 FAILED");
569 } else {
570 tst_resm(TPASS, "Block 1 PASSED");
571 }
572 tst_resm(TINFO, "Exit block 1");
573 }
574 cleanup();
575 tst_exit();
576 }
577