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 #ifdef UCLINUX
442 maybe_run_child(&do_child1, "nddddddddd", 1, &file_fd,
443 &parent_pipe[0], &parent_pipe[1],
444 &child_pipe1[0], &child_pipe1[1],
445 &child_pipe2[0], &child_pipe2[1],
446 &child_pipe3[0], &child_pipe3[1]);
447 maybe_run_child(&do_child2, "nddddddddd", 2, &file_fd,
448 &parent_pipe[0], &parent_pipe[1],
449 &child_pipe1[0], &child_pipe1[1],
450 &child_pipe2[0], &child_pipe2[1],
451 &child_pipe3[0], &child_pipe3[1]);
452 maybe_run_child(&do_child3, "nddddddddd", 3, &file_fd,
453 &parent_pipe[0], &parent_pipe[1],
454 &child_pipe1[0], &child_pipe1[1],
455 &child_pipe2[0], &child_pipe2[1],
456 &child_pipe3[0], &child_pipe3[1]);
457 #endif
458
459 if (setup()) { /* global testup */
460 tst_resm(TINFO, "setup failed");
461 cleanup();
462 }
463
464 /* check for looping state if -i option is given */
465 for (lc = 0; TEST_LOOPING(lc); lc++) {
466 /* reset tst_count in case we are looping */
467 tst_count = 0;
468
469 tst_resm(TINFO, "Enter preparation phase");
470 if ((child_pid1 = FORK_OR_VFORK()) == 0) { /* first child */
471 #ifdef UCLINUX
472 if (self_exec(av[0], "nddddddddd", 1, file_fd,
473 parent_pipe[0], parent_pipe[1],
474 child_pipe1[0], child_pipe1[1],
475 child_pipe2[0], child_pipe2[1],
476 child_pipe3[0], child_pipe3[1]) < 0) {
477 perror("self_exec failed, child 1");
478 cleanup();
479 }
480 #else
481 do_child1();
482 #endif
483 } else if (child_pid1 < 0)
484 tst_brkm(TBROK|TERRNO, cleanup, "Fork failed: child 1");
485
486 /* parent */
487
488 if ((child_pid2 = fork()) == 0) { /* second child */
489 #ifdef UCLINUX
490 if (self_exec(av[0], "nddddddddd", 2, file_fd,
491 parent_pipe[0], parent_pipe[1],
492 child_pipe1[0], child_pipe1[1],
493 child_pipe2[0], child_pipe2[1],
494 child_pipe3[0], child_pipe3[1]) < 0) {
495 perror("self_exec failed, child 2");
496 cleanup();
497 }
498 #else
499 do_child2();
500 #endif
501 } else if (child_pid2 < 0) {
502 tst_brkm(TBROK|TERRNO, cleanup, "Fork failed: child 2");
503 }
504
505 /* parent */
506
507 if ((child_pid3 = fork()) == 0) { /* third child */
508 #ifdef UCLINUX
509 if (self_exec(av[0], "nddddddddd", 3, file_fd,
510 parent_pipe[0], parent_pipe[1],
511 child_pipe1[0], child_pipe1[1],
512 child_pipe2[0], child_pipe2[1],
513 child_pipe3[0], child_pipe3[1]) < 0) {
514 perror("self_exec failed, child 3");
515 cleanup();
516 }
517 #else
518 do_child3();
519 #endif
520 do_child3();
521 } else if (child_pid3 < 0) {
522 tst_brkm(TBROK|TERRNO, cleanup, "Fork failed: child 3");
523 }
524 /* parent */
525
526 close(parent_pipe[1]);
527 close(child_pipe1[0]);
528 close(child_pipe2[0]);
529 close(child_pipe3[0]);
530 tst_resm(TINFO, "Exit preparation phase");
531
532 /* //block1: */
533 tst_resm(TINFO, "Enter block 1");
534 fail = 0;
535 /*
536 * child 1 puts first lock (bytes 2-7)
537 */
538 child_free(child_pipe1[1], 0);
539 if (parent_wait()) {
540 tst_resm(TFAIL, "didn't set first child's lock, "
541 "errno: %d", errno);
542 }
543 if (do_test(&lock1, child_pid1)) {
544 tst_resm(TINFO, "do_test failed child 1");
545 fail = 1;
546 }
547
548 /*
549 * child 2 puts second lock (bytes 9-14)
550 */
551 child_free(child_pipe2[1], 0);
552 if (parent_wait()) {
553 tst_resm(TINFO, "didn't set second child's lock, "
554 "errno: %d", errno);
555 fail = 1;
556 }
557 if (do_test(&lock2, child_pid2)) {
558 tst_resm(TINFO, "do_test failed child 2");
559 fail = 1;
560 }
561
562 /*
563 * child 3 puts third lock (bytes 17-22)
564 */
565 child_free(child_pipe3[1], 0);
566 if (parent_wait()) {
567 tst_resm(TFAIL, "didn't set third child's lock, "
568 "errno: %d", errno);
569 fail = 1;
570 }
571 if (do_test(&lock3, child_pid3)) {
572 tst_resm(TINFO, "do_test failed child 3");
573 fail = 1;
574 }
575
576 /*
577 * child 2 tries to lock same range as
578 * child 3's first lock.
579 */
580 child_free(child_pipe2[1], 0);
581
582 /*
583 * child 3 tries to lock same range as
584 * child 1 and child 2's first locks.
585 */
586 child_free(child_pipe3[1], 0);
587
588 /*
589 * Tell child 1 to release its lock. This should cause a
590 * delayed deadlock between child 2 and child 3.
591 */
592 child_free(child_pipe1[1], 0);
593
594 /*
595 * Setup an alarm to go off in case the deadlock is not
596 * detected
597 */
598 alarm(TIME_OUT);
599
600 /*
601 * should get a message from child 3 telling that its
602 * second lock EDEADLOCK
603 */
604 if ((ans = parent_wait()) != EDEADLK) {
605 tst_resm(TFAIL, "child 2 didn't deadlock, "
606 "returned: %d", ans);
607 fail = 1;
608 }
609
610 /*
611 * Double check that lock 2 and lock 3 are still right
612 */
613 do_test(&lock2, child_pid2);
614 do_test(&lock3, child_pid3);
615
616 stop_children();
617
618 if (fail) {
619 tst_resm(TFAIL, "Block 1 FAILED");
620 } else {
621 tst_resm(TPASS, "Block 1 PASSED");
622 }
623 tst_resm(TINFO, "Exit block 1");
624 }
625 cleanup();
626 tst_exit();
627 }
628