From nobody Fri Nov 22 04:55:24 2024 X-Original-To: dev-commits-src-all@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 4XvjTS5gQPz5dM94; Fri, 22 Nov 2024 04:55:24 +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 "R10" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4XvjTS56dcz3x9b; Fri, 22 Nov 2024 04:55:24 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1732251324; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=jAzNpD4mEMtkuvzRuk0xGTKbcDigfNwxjGldXCBZ7H8=; b=l/16QeSjXLtWfBPYKZGnPRJQKP9GPTpjkRSdz70eho0m6722GcF+nOPG6v1Z6Hxxh9MT/R /9Y0aXvsUbpl7e69U6CUPjIYOF0E65hsr/gwESRsUb3VLVOneMhyi6jASg/feUvJpzwUvq kz99Cdt81oDBeWzpsMRoPRlQxl6twwmaLzMwsTcX609GgBAxzQJEX8L1u1XLE+9uBnmWRh CHUxiBYM1KdKRhZ1rZDucJvQorJuLhkHTuTtWIwjaOcBDHl3i8pLYezwRFIUvaIc3t+6Es QcvS4eNsEp7lbuot80hsIgywwavbt8FLG6BiEaMz0qyTeH8HPjsL7+/kK37nKA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1732251324; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=jAzNpD4mEMtkuvzRuk0xGTKbcDigfNwxjGldXCBZ7H8=; b=FptWShGwOgb+o8L55gb7e8yKZPgoM2uzrDf1OrHIJUgxc8XqmaFKFU9f2I7a1LHJxkNcvv yiOMET0gnbHLwtMGWofOflDzjjdVdnnA+Dv9CMMXfxHkv0DED4G7wNhEaoGXKMHa6en7Zy e5KS9tj0dBYq6/7fG3+F1CxWmPHYkVO2JsH73M3OdUb/BsOpCb+EDOElAFQ61SGn+kDpbB UNwpXYL75UO0OJitXj8gEXn/5SCHgTGRnK2N9dyu0FczfXVL67Qsn+civIPm16noDfj1m7 1MEOXe3batE1GIUUVFAdXwzp99ETg6MCjn9M73dOE4aZYtkwbAOd0IN5NqoVpA== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1732251324; a=rsa-sha256; cv=none; b=yAxhLpcOgH284/nOKVxLeMbQy7gJ5aIcc4hro3uRDokQVJpKdxVv6R7GN7Guv9EdB7/2YW 59wgy8nCOPts+ktt/PWVXt0oq3GM4g07lQ9mbgWxCQfMtX3ZhDyDVliD1+FOQkcteYkM7C s1OMBNpgSWvdV+0J8KgSDzqSjuuGjRFHY4qcsQtuDdREkpZR4kdP0+Pkz0h6lOzWWBNWc7 bP5bt78OAzL9+btHnpO8kLrCvU7s/PiSR+HJV70RSPB9uzP0SSmtxVb1yphGQsztfxDgcs oiplQVBAHDLtELUzfskj/G9CbcTcQe+Zp7RaHBPI6FcGhFqn0a1214H+blk1Nw== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (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 did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 4XvjTS42Y6z19Cr; Fri, 22 Nov 2024 04:55:24 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.18.1/8.18.1) with ESMTP id 4AM4tOl9035771; Fri, 22 Nov 2024 04:55:24 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.18.1/8.18.1/Submit) id 4AM4tOHP035768; Fri, 22 Nov 2024 04:55:24 GMT (envelope-from git) Date: Fri, 22 Nov 2024 04:55:24 GMT Message-Id: <202411220455.4AM4tOHP035768@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org From: Kyle Evans Subject: git: 654292c0d683 - stable/14 - libthr: add some tests for pthread_atfork() handling List-Id: Commit messages for all branches of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-all@freebsd.org Sender: owner-dev-commits-src-all@FreeBSD.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: kevans X-Git-Repository: src X-Git-Refname: refs/heads/stable/14 X-Git-Reftype: branch X-Git-Commit: 654292c0d683fb7805df7f876693e125250024bb Auto-Submitted: auto-generated The branch stable/14 has been updated by kevans: URL: https://cgit.FreeBSD.org/src/commit/?id=654292c0d683fb7805df7f876693e125250024bb commit 654292c0d683fb7805df7f876693e125250024bb Author: Kyle Evans AuthorDate: 2024-11-14 01:33:36 +0000 Commit: Kyle Evans CommitDate: 2024-11-22 04:54:52 +0000 libthr: add some tests for pthread_atfork() handling Test that it generally functions, and also that registering multiple times calls each handler in the order that it's documented to call them in. Reviewed by: kib, markj (cherry picked from commit 7e6ac503ffeb81733272d54af367db58e45e57ca) --- lib/libthr/tests/Makefile | 1 + lib/libthr/tests/atfork_test.c | 227 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 228 insertions(+) diff --git a/lib/libthr/tests/Makefile b/lib/libthr/tests/Makefile index d882ccfd877c..34e48c63d197 100644 --- a/lib/libthr/tests/Makefile +++ b/lib/libthr/tests/Makefile @@ -34,6 +34,7 @@ NETBSD_ATF_TESTS_SH+= cancel_test NETBSD_ATF_TESTS_SH+= exit_test NETBSD_ATF_TESTS_SH+= resolv_test +ATF_TESTS_C+= atfork_test ATF_TESTS_C+= umtx_op_test ATF_TESTS_C+= pthread_sigqueue_test diff --git a/lib/libthr/tests/atfork_test.c b/lib/libthr/tests/atfork_test.c new file mode 100644 index 000000000000..5133330b1247 --- /dev/null +++ b/lib/libthr/tests/atfork_test.c @@ -0,0 +1,227 @@ +/*- + * + * Copyright (C) 2024 Kyle Evans + * + * SPDX-License-Identifier: BSD-2-Clause + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#define EXIT_NOPREPARE 1 +#define EXIT_CALLEDPARENT 2 +#define EXIT_NOCHILD 3 +#define EXIT_BADORDER 4 + +static int child; +static int forked; +static int parent; + +static void +basic_prepare(void) +{ + ATF_REQUIRE(parent == 0); + forked++; +} + +static void +basic_parent(void) +{ + ATF_REQUIRE(forked != 0); + parent++; +} + +static void +basic_child(void) +{ + if (!forked) + _exit(EXIT_NOPREPARE); + if (parent != 0) + _exit(EXIT_CALLEDPARENT); + child++; +} + +/* + * In the basic test, we'll register just once and set some globals to confirm + * that the prepare/parent callbacks were executed as expected. The child will + * use its exit status to communicate to us if the callback was not executed + * properly since we cannot assert there. This is a subset of the + * multi-callback test, but separated out so that it's more obvious from running + * the atfork_test if pthread_atfork() is completely broken or just + * out-of-order. + */ +ATF_TC(basic_atfork); +ATF_TC_HEAD(basic_atfork, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Checks invocation of all three atfork callbacks"); +} +ATF_TC_BODY(basic_atfork, tc) +{ + pid_t p, wpid; + int status; + + pthread_atfork(basic_prepare, basic_parent, basic_child); + + p = fork(); + + ATF_REQUIRE(p >= 0); + if (p == 0) + _exit(child != 0 ? 0 : EXIT_NOCHILD); + + /* + * The child can't use any of our standard atf-c(3) macros, so we have + * to rely on the exit status to convey any shenanigans. + */ + while ((wpid = waitpid(p, &status, 0)) != p) { + ATF_REQUIRE_ERRNO(EINTR, wpid == -1); + if (wpid == -1) + continue; + } + + ATF_REQUIRE_MSG(WIFEXITED(status), + "child did not exit cleanly, status %x", status); + + status = WEXITSTATUS(status); + ATF_REQUIRE_MSG(status == 0, "atfork in child %s", + status == EXIT_NOPREPARE ? "did not see `prepare` execute" : + (status == EXIT_CALLEDPARENT ? "observed `parent` executing" : + (status == EXIT_NOCHILD ? "did not see `child` execute" : + "mystery"))); + + ATF_REQUIRE(forked != 0); + ATF_REQUIRE(parent != 0); + ATF_REQUIRE(child == 0); +} + +static void +multi_assert(bool cond, bool can_assert) +{ + if (can_assert) + ATF_REQUIRE((cond)); + else if (!(cond)) + _exit(EXIT_BADORDER); +} + +static void +multi_bump(int *var, int bit, bool can_assert) +{ + int mask, val; + + mask = (1 << (bit - 1)); + val = *var; + + /* + * Every bit below this one must be set, and none of the upper bits + * should be set. + */ + multi_assert((val & mask) == 0, can_assert); + if (bit == 1) + multi_assert(val == 0, can_assert); + else + multi_assert((val & ~mask) == (mask - 1), can_assert); + + *var |= mask; +} + +static void +multi_prepare1(void) +{ + /* + * The bits are flipped for prepare because it's supposed to be called + * in the reverse order of registration. + */ + multi_bump(&forked, 2, true); +} +static void +multi_prepare2(void) +{ + multi_bump(&forked, 1, true); +} + +static void +multi_parent1(void) +{ + multi_bump(&parent, 1, true); +} +static void +multi_parent2(void) +{ + multi_bump(&parent, 2, true); +} + +static void +multi_child1(void) +{ + multi_bump(&child, 1, false); +} +static void +multi_child2(void) +{ + multi_bump(&child, 2, false); +} + +/* + * The multi-atfork test works much like the basic one, but it registers + * multiple times and enforces an order. The child still does just as strict + * of tests as the parent and continues to communicate the results of those + * tests back via its exit status. + */ +ATF_TC(multi_atfork); +ATF_TC_HEAD(multi_atfork, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Checks that multiple callbacks are called in the documented order"); +} +ATF_TC_BODY(multi_atfork, tc) +{ + pid_t p, wpid; + int status; + + pthread_atfork(multi_prepare1, multi_parent1, multi_child1); + pthread_atfork(multi_prepare2, multi_parent2, multi_child2); + + p = fork(); + + ATF_REQUIRE(p >= 0); + if (p == 0) + _exit(child != 0 ? 0 : EXIT_NOCHILD); + + /* + * The child can't use any of our standard atf-c(3) macros, so we have + * to rely on the exit status to convey any shenanigans. + */ + while ((wpid = waitpid(p, &status, 0)) != p) { + ATF_REQUIRE_ERRNO(EINTR, wpid == -1); + if (wpid == -1) + continue; + } + + ATF_REQUIRE_MSG(WIFEXITED(status), + "child did not exit cleanly, status %x", status); + + status = WEXITSTATUS(status); + ATF_REQUIRE_MSG(status == 0, "atfork in child %s", + status == EXIT_BADORDER ? "called in wrong order" : + (status == EXIT_NOCHILD ? "did not see `child` execute" : + "mystery")); + + ATF_REQUIRE(forked != 0); + ATF_REQUIRE(parent != 0); + ATF_REQUIRE(child == 0); +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, basic_atfork); + ATF_TP_ADD_TC(tp, multi_atfork); + return (atf_no_error()); +}