1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2026 ConnectWise 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 #include <sys/types.h> 29 #include <sys/user.h> 30 #include <sys/procdesc.h> 31 #include <sys/wait.h> 32 33 #include <atf-c.h> 34 #include <stdio.h> 35 #include <string.h> 36 #include <unistd.h> 37 38 static void basic_usage(int rfflags) { 39 int pd = -1; 40 pid_t pid, pd_pid, waited_pid; 41 int r, status; 42 43 pid = pdrfork(&pd, 0, rfflags); 44 ATF_REQUIRE_MSG(pid >= 0, "rfork failed with %s", strerror(errno)); 45 if (pid == 0) { 46 /* In child */ 47 _exit(0); 48 } 49 ATF_REQUIRE_MSG(pd >= 0, "rfork did not return a process descriptor"); 50 r = pdgetpid(pd, &pd_pid); 51 ATF_CHECK_EQ_MSG(r, 0, "pdgetpid failed: %s", strerror(errno)); 52 53 /* We should be able to collect the child's status */ 54 waited_pid = waitpid(pid, &status, WEXITED | WNOWAIT); 55 ATF_CHECK_EQ(waited_pid, pid); 56 57 /* But after closing the process descriptor, we won't */ 58 close(pd); 59 waited_pid = waitpid(pid, &status, WEXITED | WNOHANG); 60 ATF_CHECK_EQ(-1, waited_pid); 61 ATF_CHECK_EQ(ECHILD, errno); 62 } 63 64 /* pdrfork does not return a process descriptor to the child */ 65 ATF_TC_WITHOUT_HEAD(child_gets_no_pidfd); 66 ATF_TC_BODY(child_gets_no_pidfd, tc) 67 { 68 int pd = -1; 69 pid_t pid, pd_pid, waited_pid; 70 int r, status; 71 72 pid = pdrfork(&pd, 0, RFPROC | RFPROCDESC); 73 ATF_REQUIRE_MSG(pid >= 0, "rfork failed with %s", strerror(errno)); 74 if (pid == 0) { 75 /* 76 * In child. We can't do very much here before we exec, so 77 * just use our exit status to report success. 78 */ 79 _exit(pd == -1); 80 } 81 ATF_REQUIRE_MSG(pd >= 0, "rfork did not return a process descriptor"); 82 r = pdgetpid(pd, &pd_pid); 83 ATF_CHECK_EQ_MSG(r, 0, "pdgetpid failed: %s", strerror(errno)); 84 85 waited_pid = waitpid(pid, &status, WEXITED | WNOWAIT); 86 ATF_CHECK_EQ(waited_pid, pid); 87 ATF_REQUIRE(WIFEXITED(status) && (WEXITSTATUS(status) == true)); 88 89 close(pd); 90 } 91 92 /* If the pidfd argument is invalid, the error should be handled gracefully */ 93 ATF_TC_WITHOUT_HEAD(efault); 94 ATF_TC_BODY(efault, tc) 95 { 96 ATF_REQUIRE_ERRNO(EFAULT, pdrfork((int*)-1, 0, RFPROC | RFPROCDESC) < 0); 97 } 98 99 /* Invalid combinations of flags should return EINVAL */ 100 ATF_TC_WITHOUT_HEAD(einval); 101 ATF_TC_BODY(einval, tc) 102 { 103 int pd = -1; 104 105 ATF_CHECK_ERRNO(EINVAL, pdrfork(&pd, -1, RFSPAWN) < 0); 106 ATF_CHECK_ERRNO(EINVAL, pdrfork(&pd, 0, -1) < 0); 107 ATF_CHECK_ERRNO(EINVAL, pdrfork(&pd, 0, RFSPAWN | RFNOWAIT) < 0); 108 ATF_CHECK_ERRNO(EINVAL, pdrfork(&pd, 0, RFPROC | RFFDG| RFCFDG) < 0); 109 ATF_CHECK_ERRNO(EINVAL, pdrfork(&pd, 0, RFPROCDESC) < 0); 110 } 111 112 /* 113 * Without RFSPAWN, RFPROC, or RFPROCDESC, an existing process may be modified 114 */ 115 ATF_TC_WITHOUT_HEAD(modify_child); 116 ATF_TC_BODY(modify_child, tc) 117 { 118 int fdp = -1; 119 pid_t pid1, pid2; 120 121 pid1 = pdfork(&fdp, 0); 122 if (pid1 == 0) 123 _exit(0); 124 ATF_REQUIRE_MSG(pid1 >= 0, "pdfork failed: %s", strerror(errno)); 125 ATF_REQUIRE_MSG(fdp >= 0, "pdfork didn't return a process descriptor"); 126 127 pid2 = pdrfork(&fdp, 0, RFNOWAIT); 128 ATF_REQUIRE_MSG(pid2 >= 0, "pdrfork failed: %s", strerror(errno)); 129 ATF_CHECK_EQ_MSG(pid2, 0, 130 "pdrfork created a process even though we told it not to"); 131 132 close(fdp); 133 } 134 135 /* 136 * Basic usage with RFPROC. No process descriptor will be created. 137 * I'm not sure why you would use pdrfork in this case instead of plain rfork 138 */ 139 ATF_TC_WITHOUT_HEAD(rfproc); 140 ATF_TC_BODY(rfproc, tc) 141 { 142 int pd = -1; 143 pid_t pid; 144 145 pid = pdrfork(&pd, 0, RFPROC); 146 ATF_REQUIRE_MSG(pid > 0, "rfork failed with %s", strerror(errno)); 147 if (pid == 0) 148 _exit(0); 149 150 ATF_REQUIRE_EQ_MSG(pd, -1, 151 "rfork(RFPROC) returned a process descriptor"); 152 } 153 154 /* basic usage with RFPROCDESC */ 155 ATF_TC_WITHOUT_HEAD(rfprocdesc); 156 ATF_TC_BODY(rfprocdesc, tc) 157 { 158 basic_usage(RFPROC | RFPROCDESC); 159 } 160 161 /* basic usage with RFSPAWN */ 162 /* 163 * Skip on i386 and x86_64 because RFSPAWN cannot be used from C code on those 164 * architectures. See lib/libc/gen/posix_spawn.c for details. 165 */ 166 #if !(defined(__i386__)) && !(defined(__amd64__)) 167 ATF_TC_WITHOUT_HEAD(rfspawn); 168 ATF_TC_BODY(rfspawn, tc) 169 { 170 basic_usage(RFSPAWN); 171 } 172 #endif 173 174 ATF_TP_ADD_TCS(tp) 175 { 176 ATF_TP_ADD_TC(tp, child_gets_no_pidfd); 177 ATF_TP_ADD_TC(tp, efault); 178 ATF_TP_ADD_TC(tp, einval); 179 ATF_TP_ADD_TC(tp, modify_child); 180 ATF_TP_ADD_TC(tp, rfproc); 181 ATF_TP_ADD_TC(tp, rfprocdesc); 182 #if !(defined(__i386__)) && !(defined(__amd64__)) 183 ATF_TP_ADD_TC(tp, rfspawn); 184 #endif 185 186 return (atf_no_error()); 187 } 188