1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2022 Dell Inc.
5  * Author: Eric van Gyzen
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #if 0
30 
31 1. Run some "bad" tests that prevent kyua from removing the work directory.
32    We use "chflags uunlink".  Mounting a file system from an md(4) device
33    is another common use case.
34 2. Fork a lot, nearly wrapping the PID number space, so step 3 will re-use
35    a PID from step 1.  Running the entire FreeBSD test suite is a more
36    realistic scenario for this step.
37 3. Run some more tests.  If the stars align, the bug is not fixed yet, and
38    kyua is built with debugging, kyua will abort with the following messages.
39    Without debugging, the tests in step 3 will reuse the context from step 1,
40    including stdout, stderr, and working directory, which are still populated
41    with stuff from step 1.  When I found this bug, step 3 was
42    __test_cases_list__, which expects a certain format in stdout and failed
43    when it found something completely unrelated.
44 4. You can clean up with: chflags -R nouunlink /tmp/kyua.*; rm -rf /tmp/kyua.*
45 
46 $ cc -o pid_wrap -latf-c pid_wrap.c
47 $ kyua test
48 pid_wrap:leak_0  ->  passed  [0.001s]
49 pid_wrap:leak_1  ->  passed  [0.001s]
50 pid_wrap:leak_2  ->  passed  [0.001s]
51 pid_wrap:leak_3  ->  passed  [0.001s]
52 pid_wrap:leak_4  ->  passed  [0.001s]
53 pid_wrap:leak_5  ->  passed  [0.001s]
54 pid_wrap:leak_6  ->  passed  [0.001s]
55 pid_wrap:leak_7  ->  passed  [0.001s]
56 pid_wrap:leak_8  ->  passed  [0.001s]
57 pid_wrap:leak_9  ->  passed  [0.001s]
58 pid_wrap:pid_wrap  ->  passed  [1.113s]
59 pid_wrap:pid_wrap_0  ->  passed  [0.001s]
60 pid_wrap:pid_wrap_1  ->  passed  [0.001s]
61 pid_wrap:pid_wrap_2  ->  passed  [0.001s]
62 pid_wrap:pid_wrap_3  ->  *** /usr/src/main/contrib/kyua/utils/process/executor.cpp:779: Invariant check failed: PID 60876 already in all_exec_handles; not properly cleaned up or reused too fast
63 *** Fatal signal 6 received
64 *** Log file is /home/vangyzen/.kyua/logs/kyua.20221006-193544.log
65 *** Please report this problem to kyua-discuss@googlegroups.com detailing what you were doing before the crash happened; if possible, include the log file mentioned above
66 Abort trap (core dumped)
67 
68 #endif
69 
70 #include <sys/stat.h>
71 
72 #include <atf-c++.hpp>
73 
74 #include <fcntl.h>
75 #include <signal.h>
76 #include <unistd.h>
77 
78 #include <cerrno>
79 #include <cstring>
80 
81 void
leak_work_dir()82 leak_work_dir()
83 {
84 	int fd;
85 
86 	ATF_REQUIRE((fd = open("unforgettable", O_CREAT|O_EXCL|O_WRONLY, 0600))
87 	    >= 0);
88 	ATF_REQUIRE_EQ(0, fchflags(fd, UF_NOUNLINK));
89 	ATF_REQUIRE_EQ(0, close(fd));
90 }
91 
92 void
wrap_pids()93 wrap_pids()
94 {
95 	pid_t begin, current, target;
96 	bool wrapped;
97 
98 	begin = getpid();
99 	target = begin - 15;
100 	if (target <= 1) {
101 		target += 99999;    // PID_MAX
102 		wrapped = true;
103 	} else {
104 		wrapped = false;
105 	}
106 
107 	ATF_REQUIRE(signal(SIGCHLD, SIG_IGN) != SIG_ERR);
108 
109 	do {
110 		current = vfork();
111 		if (current == 0) {
112 			_exit(0);
113 		}
114 		ATF_REQUIRE(current != -1);
115 		if (current < begin) {
116 			wrapped = true;
117 		}
118 	} while (!wrapped || current < target);
119 }
120 
121 void
test_work_dir_reuse()122 test_work_dir_reuse()
123 {
124 	// If kyua is built with debugging, it would abort here before the fix.
125 }
126 
127 void
clean_up()128 clean_up()
129 {
130 	(void)system("chflags -R nouunlink ../..");
131 }
132 
133 ATF_TEST_CASE_WITHOUT_HEAD(leak_0);
ATF_TEST_CASE_BODY(leak_0)134 ATF_TEST_CASE_BODY(leak_0) { leak_work_dir(); }
135 ATF_TEST_CASE_WITHOUT_HEAD(leak_1);
ATF_TEST_CASE_BODY(leak_1)136 ATF_TEST_CASE_BODY(leak_1) { leak_work_dir(); }
137 ATF_TEST_CASE_WITHOUT_HEAD(leak_2);
ATF_TEST_CASE_BODY(leak_2)138 ATF_TEST_CASE_BODY(leak_2) { leak_work_dir(); }
139 ATF_TEST_CASE_WITHOUT_HEAD(leak_3);
ATF_TEST_CASE_BODY(leak_3)140 ATF_TEST_CASE_BODY(leak_3) { leak_work_dir(); }
141 ATF_TEST_CASE_WITHOUT_HEAD(leak_4);
ATF_TEST_CASE_BODY(leak_4)142 ATF_TEST_CASE_BODY(leak_4) { leak_work_dir(); }
143 ATF_TEST_CASE_WITHOUT_HEAD(leak_5);
ATF_TEST_CASE_BODY(leak_5)144 ATF_TEST_CASE_BODY(leak_5) { leak_work_dir(); }
145 ATF_TEST_CASE_WITHOUT_HEAD(leak_6);
ATF_TEST_CASE_BODY(leak_6)146 ATF_TEST_CASE_BODY(leak_6) { leak_work_dir(); }
147 ATF_TEST_CASE_WITHOUT_HEAD(leak_7);
ATF_TEST_CASE_BODY(leak_7)148 ATF_TEST_CASE_BODY(leak_7) { leak_work_dir(); }
149 ATF_TEST_CASE_WITHOUT_HEAD(leak_8);
ATF_TEST_CASE_BODY(leak_8)150 ATF_TEST_CASE_BODY(leak_8) { leak_work_dir(); }
151 ATF_TEST_CASE_WITHOUT_HEAD(leak_9);
ATF_TEST_CASE_BODY(leak_9)152 ATF_TEST_CASE_BODY(leak_9) { leak_work_dir(); }
153 
154 ATF_TEST_CASE_WITHOUT_HEAD(pid_wrap);
ATF_TEST_CASE_BODY(pid_wrap)155 ATF_TEST_CASE_BODY(pid_wrap) { wrap_pids(); }
156 
157 ATF_TEST_CASE_WITHOUT_HEAD(pid_wrap_0);
ATF_TEST_CASE_BODY(pid_wrap_0)158 ATF_TEST_CASE_BODY(pid_wrap_0) { test_work_dir_reuse(); }
159 ATF_TEST_CASE_WITHOUT_HEAD(pid_wrap_1);
ATF_TEST_CASE_BODY(pid_wrap_1)160 ATF_TEST_CASE_BODY(pid_wrap_1) { test_work_dir_reuse(); }
161 ATF_TEST_CASE_WITHOUT_HEAD(pid_wrap_2);
ATF_TEST_CASE_BODY(pid_wrap_2)162 ATF_TEST_CASE_BODY(pid_wrap_2) { test_work_dir_reuse(); }
163 ATF_TEST_CASE_WITHOUT_HEAD(pid_wrap_3);
ATF_TEST_CASE_BODY(pid_wrap_3)164 ATF_TEST_CASE_BODY(pid_wrap_3) { test_work_dir_reuse(); }
165 ATF_TEST_CASE_WITHOUT_HEAD(pid_wrap_4);
ATF_TEST_CASE_BODY(pid_wrap_4)166 ATF_TEST_CASE_BODY(pid_wrap_4) { test_work_dir_reuse(); }
167 ATF_TEST_CASE_WITHOUT_HEAD(pid_wrap_5);
ATF_TEST_CASE_BODY(pid_wrap_5)168 ATF_TEST_CASE_BODY(pid_wrap_5) { test_work_dir_reuse(); }
169 ATF_TEST_CASE_WITHOUT_HEAD(pid_wrap_6);
ATF_TEST_CASE_BODY(pid_wrap_6)170 ATF_TEST_CASE_BODY(pid_wrap_6) { test_work_dir_reuse(); }
171 ATF_TEST_CASE_WITHOUT_HEAD(pid_wrap_7);
ATF_TEST_CASE_BODY(pid_wrap_7)172 ATF_TEST_CASE_BODY(pid_wrap_7) { test_work_dir_reuse(); }
173 ATF_TEST_CASE_WITHOUT_HEAD(pid_wrap_8);
ATF_TEST_CASE_BODY(pid_wrap_8)174 ATF_TEST_CASE_BODY(pid_wrap_8) { test_work_dir_reuse(); }
175 ATF_TEST_CASE_WITHOUT_HEAD(pid_wrap_9);
ATF_TEST_CASE_BODY(pid_wrap_9)176 ATF_TEST_CASE_BODY(pid_wrap_9) { test_work_dir_reuse(); }
177 
178 ATF_TEST_CASE_WITHOUT_HEAD(really_clean_up);
ATF_TEST_CASE_BODY(really_clean_up)179 ATF_TEST_CASE_BODY(really_clean_up) { clean_up(); }
180 
ATF_INIT_TEST_CASES(tcs)181 ATF_INIT_TEST_CASES(tcs)
182 {
183 	ATF_ADD_TEST_CASE(tcs, leak_0);
184 	ATF_ADD_TEST_CASE(tcs, leak_1);
185 	ATF_ADD_TEST_CASE(tcs, leak_2);
186 	ATF_ADD_TEST_CASE(tcs, leak_3);
187 	ATF_ADD_TEST_CASE(tcs, leak_4);
188 	ATF_ADD_TEST_CASE(tcs, leak_5);
189 	ATF_ADD_TEST_CASE(tcs, leak_6);
190 	ATF_ADD_TEST_CASE(tcs, leak_7);
191 	ATF_ADD_TEST_CASE(tcs, leak_8);
192 	ATF_ADD_TEST_CASE(tcs, leak_9);
193 
194 	ATF_ADD_TEST_CASE(tcs, pid_wrap);
195 
196 	ATF_ADD_TEST_CASE(tcs, pid_wrap_0);
197 	ATF_ADD_TEST_CASE(tcs, pid_wrap_1);
198 	ATF_ADD_TEST_CASE(tcs, pid_wrap_2);
199 	ATF_ADD_TEST_CASE(tcs, pid_wrap_3);
200 	ATF_ADD_TEST_CASE(tcs, pid_wrap_4);
201 	ATF_ADD_TEST_CASE(tcs, pid_wrap_5);
202 	ATF_ADD_TEST_CASE(tcs, pid_wrap_6);
203 	ATF_ADD_TEST_CASE(tcs, pid_wrap_7);
204 	ATF_ADD_TEST_CASE(tcs, pid_wrap_8);
205 	ATF_ADD_TEST_CASE(tcs, pid_wrap_9);
206 
207 	ATF_ADD_TEST_CASE(tcs, really_clean_up);
208 }
209