xref: /openbsd/regress/sys/kern/dup2_self/dup2_self.c (revision d415bd75)
1 /*	$OpenBSD: dup2_self.c,v 1.3 2003/07/31 21:48:08 deraadt Exp $	*/
2 /*
3  *	Written by Artur Grabowski <art@openbsd.org> 2002 Public Domain.
4  */
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <unistd.h>
8 #include <err.h>
9 #include <fcntl.h>
10 
11 /*
12  * We're testing a small tweak in dup2 semantics. Normally dup and dup2
13  * will clear the close-on-exec flag on the new fd (which appears to be
14  * an implementation mistake from start and not some planned behavior).
15  * In todays implementations of dup and dup2 we have to make an effort
16  * to really clear that flag. But all tested implementations of dup2 have
17  * another tweak. If we dup2(old, new) when old == new, the syscall
18  * short-circuits and returns early (because there is no need to do all
19  * the work (and there is a risk for serious mistakes)). So although the
20  * docs say that dup2 should "take 'old', close 'new' perform a dup(2) of
21  * 'old' into 'new'" the docs are not really followed because close-on-exec
22  * is not cleared on 'new'.
23  *
24  * Since everyone has this bug, we pretend that this is the way it is
25  * supposed to be and test here that it really works that way.
26  *
27  * This is a fine example on where two separate implementation fuckups
28  * take out each other and make the end-result the way it was meant to be.
29  */
30 
31 int
32 main(int argc, char *argv[])
33 {
34 	int orgfd, fd1, fd2;
35 	char temp[] = "/tmp/dup2XXXXXXXXX";
36 
37 	if ((orgfd = mkstemp(temp)) < 0)
38 		err(1, "mkstemp");
39 	remove(temp);
40 
41 	if (ftruncate(orgfd, 1024) != 0)
42 		err(1, "ftruncate");
43 
44 	if ((fd1 = dup(orgfd)) < 0)
45 		err(1, "dup");
46 
47 	/* Set close-on-exec */
48 	if (fcntl(fd1, F_SETFD, 1) != 0)
49 		err(1, "fcntl(F_SETFD)");
50 
51 	if ((fd2 = dup2(fd1, fd1)) < 0)
52 		err(1, "dup2");
53 
54 	/* Test 1: Do we get the right fd? */
55 	if (fd2 != fd1)
56 		errx(1, "dup2 didn't give us the right fd");
57 
58 	/* Test 2: Was close-on-exec cleared? */
59 	if (fcntl(fd2, F_GETFD) == 0)
60 		errx(1, "dup2 cleared close-on-exec");
61 
62 	return 0;
63 }
64