From nobody Sat Feb 21 09:49:49 2026 X-Original-To: dev-commits-src-branches@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4fJ2Qj6n1sz6SKX8 for ; Sat, 21 Feb 2026 09:49:49 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R12" (not verified)) by mx1.freebsd.org (Postfix) with ESMTPS id 4fJ2Qj5g0Tz41H9 for ; Sat, 21 Feb 2026 09:49:49 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1771667389; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=KaOWqh3TDA9ypG0LBMwnCDPmi6wP2j4kX3c+AmafcS0=; b=S4ptNZcbNBN47azjBDbPcL6vDN4n4dbs+/nXhgutNJDeahyhvIpjdwWFfSZNKMV+Vidwjz eW36ihIZTCDhsNC5qiKLX1tgw9gdWSSo+cInrl1jTxXsj7u/nQil7zZnyG+jKmaRCfAzIA 4Z6EIyFOSr5JN/JUwtqAbtAZJIydMLaxn7BTix2JkCYa9HxSBnWGUsgeMe9EVQGlwPxczi VB4qXuFbCm5BNh8r0Yf2KoWOm0i1BqQh08iG2t05U6UDHNBfk1zWFHHBmU5Afhpa2zGOZv Owdy8zJN+BpdiOQOBUkUgSQhzkjBWI03KtEJTvcAVvnEJSSHp7PpCh8EAgeavw== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1771667389; a=rsa-sha256; cv=none; b=Nw+J9RmF83trPlRUJfTDdUo9uTwxefNCP0lQB5s6SJocko9owkpbHJpdGktKr3KvNCswND Idkpy+outDa4g73gvUkhwkSJ9z3gyIuU5quIbF2Wcr4I69APRylzUtqOdqYylT5iktY8jX ofLb6XvPLAZhsnjL1Hg7TjCyOdpc5DrZ+VA7gEX+J7Bj+sIs/M/kxMhq3SuTKfmH/84KiK P7YFsoOGW9ktldLPqSEymbjgINkU0SQxwL4I+6mamdFvDaicUxyjjBP6fqfW96vNoOgx9y yAixVr3UCTWKw0KbRk4vEeIqdvaNgfz2szPT/q6a1ZALJSwyaQt1Ko1vFUxfvQ== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1771667389; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=KaOWqh3TDA9ypG0LBMwnCDPmi6wP2j4kX3c+AmafcS0=; b=pZAr1qqfPUPxJLchEJN7ymqDnpSv4c/H9FcCH2zK0sIJTyTcQEs1CWyqng0Rf7zdor+4h1 JFAw2sjw+7+N1uCEeplZXpGx5wMO+NuvuRJ0G1U+SsFmdq2z0F4RDfQdsA8rDboMFUVOVz eOB1Pm0qVENvzVWkeFGyvPShuLSnIT33zCof386mkGQoyMvZlSBTP4BnGz+uHXPcoe+oyo m5k4m23FhnBtwNXW9Y6dpbIFM7Mc8RF0KiaMT9kOl+7RUeu1AqTGeveTBUhTU8pRB0TsCz 7we5ddcCP0AVsIMfE3DQ9rqnPOuGT1Mxq46gIaqBFsQsTq14EhD0ZgjV3lJvtA== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4fJ2Qj4xmqz17Ql for ; Sat, 21 Feb 2026 09:49:49 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 1f5b8 by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Sat, 21 Feb 2026 09:49:49 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org Cc: Alan Somers From: Konstantin Belousov Subject: git: 558b76135452 - stable/15 - Add tests for pdrfork List-Id: Commits to the stable branches of the FreeBSD src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-branches List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-branches@freebsd.org Sender: owner-dev-commits-src-branches@FreeBSD.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: kib X-Git-Repository: src X-Git-Refname: refs/heads/stable/15 X-Git-Reftype: branch X-Git-Commit: 558b761354523d5224928d2c350e1145f9c6b7f7 Auto-Submitted: auto-generated Date: Sat, 21 Feb 2026 09:49:49 +0000 Message-Id: <69997fbd.1f5b8.5075c159@gitrepo.freebsd.org> The branch stable/15 has been updated by kib: URL: https://cgit.FreeBSD.org/src/commit/?id=558b761354523d5224928d2c350e1145f9c6b7f7 commit 558b761354523d5224928d2c350e1145f9c6b7f7 Author: Alan Somers AuthorDate: 2026-01-25 16:13:43 +0000 Commit: Konstantin Belousov CommitDate: 2026-02-21 09:48:58 +0000 Add tests for pdrfork (cherry picked from commit d1e6057fa47e842c63461b64b29ac5d3e9fdad76) --- tests/sys/kern/Makefile | 1 + tests/sys/kern/pdrfork.c | 187 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 188 insertions(+) diff --git a/tests/sys/kern/Makefile b/tests/sys/kern/Makefile index 9044b1e7e4f2..946a1c2b9b8d 100644 --- a/tests/sys/kern/Makefile +++ b/tests/sys/kern/Makefile @@ -30,6 +30,7 @@ ATF_TESTS_C+= ktls_test ATF_TESTS_C+= ktrace_test ATF_TESTS_C+= listener_wakeup ATF_TESTS_C+= module_test +ATF_TESTS_C+= pdrfork ATF_TESTS_C+= prace ATF_TESTS_C+= ptrace_test TEST_METADATA.ptrace_test+= timeout="15" diff --git a/tests/sys/kern/pdrfork.c b/tests/sys/kern/pdrfork.c new file mode 100644 index 000000000000..fa644b6658d7 --- /dev/null +++ b/tests/sys/kern/pdrfork.c @@ -0,0 +1,187 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2026 ConnectWise + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +static void basic_usage(int rfflags) { + int pd = -1; + pid_t pid, pd_pid, waited_pid; + int r, status; + + pid = pdrfork(&pd, 0, rfflags); + ATF_REQUIRE_MSG(pid >= 0, "rfork failed with %s", strerror(errno)); + if (pid == 0) { + /* In child */ + _exit(0); + } + ATF_REQUIRE_MSG(pd >= 0, "rfork did not return a process descriptor"); + r = pdgetpid(pd, &pd_pid); + ATF_CHECK_EQ_MSG(r, 0, "pdgetpid failed: %s", strerror(errno)); + + /* We should be able to collect the child's status */ + waited_pid = waitpid(pid, &status, WEXITED | WNOWAIT); + ATF_CHECK_EQ(waited_pid, pid); + + /* But after closing the process descriptor, we won't */ + close(pd); + waited_pid = waitpid(pid, &status, WEXITED | WNOHANG); + ATF_CHECK_EQ(-1, waited_pid); + ATF_CHECK_EQ(ECHILD, errno); +} + +/* pdrfork does not return a process descriptor to the child */ +ATF_TC_WITHOUT_HEAD(child_gets_no_pidfd); +ATF_TC_BODY(child_gets_no_pidfd, tc) +{ + int pd = -1; + pid_t pid, pd_pid, waited_pid; + int r, status; + + pid = pdrfork(&pd, 0, RFPROC | RFPROCDESC); + ATF_REQUIRE_MSG(pid >= 0, "rfork failed with %s", strerror(errno)); + if (pid == 0) { + /* + * In child. We can't do very much here before we exec, so + * just use our exit status to report success. + */ + _exit(pd == -1); + } + ATF_REQUIRE_MSG(pd >= 0, "rfork did not return a process descriptor"); + r = pdgetpid(pd, &pd_pid); + ATF_CHECK_EQ_MSG(r, 0, "pdgetpid failed: %s", strerror(errno)); + + waited_pid = waitpid(pid, &status, WEXITED | WNOWAIT); + ATF_CHECK_EQ(waited_pid, pid); + ATF_REQUIRE(WIFEXITED(status) && (WEXITSTATUS(status) == true)); + + close(pd); +} + +/* If the pidfd argument is invalid, the error should be handled gracefully */ +ATF_TC_WITHOUT_HEAD(efault); +ATF_TC_BODY(efault, tc) +{ + ATF_REQUIRE_ERRNO(EFAULT, pdrfork((int*)-1, 0, RFPROC | RFPROCDESC) < 0); +} + +/* Invalid combinations of flags should return EINVAL */ +ATF_TC_WITHOUT_HEAD(einval); +ATF_TC_BODY(einval, tc) +{ + int pd = -1; + + ATF_CHECK_ERRNO(EINVAL, pdrfork(&pd, -1, RFSPAWN) < 0); + ATF_CHECK_ERRNO(EINVAL, pdrfork(&pd, 0, -1) < 0); + ATF_CHECK_ERRNO(EINVAL, pdrfork(&pd, 0, RFSPAWN | RFNOWAIT) < 0); + ATF_CHECK_ERRNO(EINVAL, pdrfork(&pd, 0, RFPROC | RFFDG| RFCFDG) < 0); + ATF_CHECK_ERRNO(EINVAL, pdrfork(&pd, 0, RFPROCDESC) < 0); +} + +/* + * Without RFSPAWN, RFPROC, or RFPROCDESC, an existing process may be modified + */ +ATF_TC_WITHOUT_HEAD(modify_child); +ATF_TC_BODY(modify_child, tc) +{ + int fdp = -1; + pid_t pid1, pid2; + + pid1 = pdfork(&fdp, 0); + if (pid1 == 0) + _exit(0); + ATF_REQUIRE_MSG(pid1 >= 0, "pdfork failed: %s", strerror(errno)); + ATF_REQUIRE_MSG(fdp >= 0, "pdfork didn't return a process descriptor"); + + pid2 = pdrfork(&fdp, 0, RFNOWAIT); + ATF_REQUIRE_MSG(pid2 >= 0, "pdrfork failed: %s", strerror(errno)); + ATF_CHECK_EQ_MSG(pid2, 0, + "pdrfork created a process even though we told it not to"); + + close(fdp); +} + +/* + * Basic usage with RFPROC. No process descriptor will be created. + * I'm not sure why you would use pdrfork in this case instead of plain rfork + */ +ATF_TC_WITHOUT_HEAD(rfproc); +ATF_TC_BODY(rfproc, tc) +{ + int pd = -1; + pid_t pid; + + pid = pdrfork(&pd, 0, RFPROC); + ATF_REQUIRE_MSG(pid > 0, "rfork failed with %s", strerror(errno)); + if (pid == 0) + _exit(0); + + ATF_REQUIRE_EQ_MSG(pd, -1, + "rfork(RFPROC) returned a process descriptor"); +} + +/* basic usage with RFPROCDESC */ +ATF_TC_WITHOUT_HEAD(rfprocdesc); +ATF_TC_BODY(rfprocdesc, tc) +{ + basic_usage(RFPROC | RFPROCDESC); +} + +/* basic usage with RFSPAWN */ +/* + * Skip on i386 and x86_64 because RFSPAWN cannot be used from C code on those + * architectures. See lib/libc/gen/posix_spawn.c for details. + */ +#if !(defined(__i386__)) && !(defined(__amd64__)) +ATF_TC_WITHOUT_HEAD(rfspawn); +ATF_TC_BODY(rfspawn, tc) +{ + basic_usage(RFSPAWN); +} +#endif + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, child_gets_no_pidfd); + ATF_TP_ADD_TC(tp, efault); + ATF_TP_ADD_TC(tp, einval); + ATF_TP_ADD_TC(tp, modify_child); + ATF_TP_ADD_TC(tp, rfproc); + ATF_TP_ADD_TC(tp, rfprocdesc); +#if !(defined(__i386__)) && !(defined(__amd64__)) + ATF_TP_ADD_TC(tp, rfspawn); +#endif + + return (atf_no_error()); +}