• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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