From nobody Tue Nov 26 20:37:18 2024 X-Original-To: dev-commits-src-main@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 4XyZBQ4FmYz5dylR; Tue, 26 Nov 2024 20:37:18 +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 4XyZBQ3R6xz45fH; Tue, 26 Nov 2024 20:37:18 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1732653438; 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=FMFkqrsf7TuhJIBhqjCzba5jsW2dlkGNIBshzr8vigU=; b=JgprldIpS07P5IqpEeOts2+dVrUivy6RMC/mChb0QrqfXOAXFkX7WKT1kzHFA5S2OVoeg+ hiAHJfZGcyfwLCpokHwiqn+JiUYevNyi3w2uUxyjmP/nGBCmX7pYhjdLGO7zlsoZqH+spO Qx66ruW0+TZwXoITcwi7AZZcVxMfOtPBSHe87o+vix1gi/ZVdNx3TddYQs8UDxv+se5iAi oYo5doWskKKaZU/RvHwcR0enQL+vLXjPjMvDiqJ444FqGkBPXLOAfag5JLAN7dHlXuYJYt zHINQdbnr4aq1L/kTuxrrI2N3vOTcBg+LlGbAjb/KtV7yzgN8XokYTBpb5j5Ow== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1732653438; 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=FMFkqrsf7TuhJIBhqjCzba5jsW2dlkGNIBshzr8vigU=; b=YSMFXv145tSzxkX/mivzj9qA1nFeH6gwhFZsSkLZyZPjqY6daZD2FFvF9cqCtcrniCFC0L 202XfHVYFZBB5/Qy1WGV+EXCdUEICpYs/0w9r/Yz/qKOIOb/QRpM3XdYj0jr6h8IMppqmJ ZRe7SzmAF18fl+UvkQv//LWYqeRIe5UkjjrMkH1CiUrEUNaFqMdpKzcT63hkEUHS/DaYPs BXX6aPzfeVtwvmal1xn2MP7Ddxic7XyGkZDRoqroWXWJKmfN76hoiFNnRhQN+0QMSx9iou dxstwDCC009ObpzXdsWlpSvYPvjaVP0TS/SqJb2a9OB8v8E0vjcNxdVvWa30/Q== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1732653438; a=rsa-sha256; cv=none; b=EarZc2NQ1CXYubx4DNLt2oiTmDrlw0S21oZnwIOYIHewi8SGWAsVVK1b2TXZBj0Ab30Y1b jzqRYQ4aLB7gnGZaqoXj/OSpgSMluz2a8ecTmL+7g1Wgzfldarv8ZaQwarKT00calj/Aju wS4qpBgqNx069eU+yfvFL765occIUuZTtUIY2V6dCuH7BOPmpj1paf/yM8zNeK6/1PY2h8 eeoo/xIizCe9knMr9gTJ6uRZrGnKejTw5iT6T0iksiP7yiLQESC/EZFqc92YFzsgbhnPP4 Kphzw18svb8tGZVCh2QAlkLmRzw0Wpi4cCYEm8WFRo44uZEr0R1fcuzrMgWupQ== 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 4XyZBQ32K9zgfd; Tue, 26 Nov 2024 20:37:18 +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 4AQKbIFJ036158; Tue, 26 Nov 2024 20:37:18 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.18.1/8.18.1/Submit) id 4AQKbIGw036155; Tue, 26 Nov 2024 20:37:18 GMT (envelope-from git) Date: Tue, 26 Nov 2024 20:37:18 GMT Message-Id: <202411262037.4AQKbIGw036155@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Dag-Erling =?utf-8?Q?Sm=C3=B8rgrav?= Subject: git: 6748d4e0eb8a - main - tests: Add regression test for ppoll() / pselect() race. List-Id: Commit messages for the main branch of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-main List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-main@freebsd.org Sender: owner-dev-commits-src-main@FreeBSD.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: des X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: 6748d4e0eb8ab918d55f69ae59a60ecbac8df85f Auto-Submitted: auto-generated The branch main has been updated by des: URL: https://cgit.FreeBSD.org/src/commit/?id=6748d4e0eb8ab918d55f69ae59a60ecbac8df85f commit 6748d4e0eb8ab918d55f69ae59a60ecbac8df85f Author: Dag-Erling Smørgrav AuthorDate: 2024-11-26 20:36:09 +0000 Commit: Dag-Erling Smørgrav CommitDate: 2024-11-26 20:36:09 +0000 tests: Add regression test for ppoll() / pselect() race. These tests demonstrate the bug that was fixed in ccb973da1f1b. Sponsored by: Klara, Inc. Sponsored by: NetApp, Inc. Reviewed by: markj Differential Revision: https://reviews.freebsd.org/D47738 --- tests/sys/kern/Makefile | 1 + tests/sys/kern/prace.c | 144 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 145 insertions(+) diff --git a/tests/sys/kern/Makefile b/tests/sys/kern/Makefile index b2d133a0457f..8785caf4e293 100644 --- a/tests/sys/kern/Makefile +++ b/tests/sys/kern/Makefile @@ -23,6 +23,7 @@ ATF_TESTS_C+= ktls_test ATF_TESTS_C+= ktrace_test ATF_TESTS_C+= listener_wakeup ATF_TESTS_C+= module_test +ATF_TESTS_C+= prace ATF_TESTS_C+= ptrace_test TEST_METADATA.ptrace_test+= timeout="15" ATF_TESTS_C+= reaper diff --git a/tests/sys/kern/prace.c b/tests/sys/kern/prace.c new file mode 100644 index 000000000000..e6aa09ec2180 --- /dev/null +++ b/tests/sys/kern/prace.c @@ -0,0 +1,144 @@ +/*- + * Copyright (c) 2024 Klara, Inc. + * + * SPDX-License-Identifier: BSD-2-Clause + * + * These tests demonstrate a bug in ppoll() and pselect() where a blocked + * signal can fire after the timer runs out but before the signal mask is + * restored. To do this, we fork a child process which installs a SIGINT + * handler and repeatedly calls either ppoll() or pselect() with a 1 ms + * timeout, while the parent repeatedly sends SIGINT to the child at + * intervals that start out at 1100 us and gradually decrease to 900 us. + * Each SIGINT resynchronizes parent and child, and sooner or later the + * parent hits the sweet spot and the SIGINT arrives at just the right + * time to demonstrate the bug. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +static volatile sig_atomic_t caught[NSIG]; + +static void +handler(int signo) +{ + caught[signo]++; +} + +static void +child(int rd, bool poll) +{ + struct timespec timeout = { .tv_nsec = 1000000 }; + sigset_t set0, set1; + int ret; + + /* empty mask for ppoll() / pselect() */ + sigemptyset(&set0); + + /* block SIGINT, then install a handler for it */ + sigemptyset(&set1); + sigaddset(&set1, SIGINT); + sigprocmask(SIG_BLOCK, &set1, NULL); + signal(SIGINT, handler); + + /* signal parent that we are ready */ + close(rd); + for (;;) { + /* sleep for 1 ms with signals unblocked */ + ret = poll ? ppoll(NULL, 0, &timeout, &set0) : + pselect(0, NULL, NULL, NULL, &timeout, &set0); + /* + * At this point, either ret == 0 (timer ran out) errno == + * EINTR (a signal was received). Any other outcome is + * abnormal. + */ + if (ret != 0 && errno != EINTR) + err(1, "p%s()", poll ? "poll" : "select"); + /* if ret == 0, we should not have caught any signals */ + if (ret == 0 && caught[SIGINT]) { + /* + * We successfully demonstrated the race. Restore + * the default action and re-raise SIGINT. + */ + signal(SIGINT, SIG_DFL); + raise(SIGINT); + /* Not reached */ + } + /* reset for next attempt */ + caught[SIGINT] = 0; + } + /* Not reached */ +} + +static void +prace(bool poll) +{ + int pd[2], status; + pid_t pid; + + /* fork child process */ + if (pipe(pd) != 0) + err(1, "pipe()"); + if ((pid = fork()) < 0) + err(1, "fork()"); + if (pid == 0) { + close(pd[0]); + child(pd[1], poll); + /* Not reached */ + } + close(pd[1]); + + /* wait for child to signal readiness */ + (void)read(pd[0], &pd[0], sizeof(pd[0])); + close(pd[0]); + + /* repeatedly attempt to signal at just the right moment */ + for (useconds_t timeout = 1100; timeout > 900; timeout--) { + usleep(timeout); + if (kill(pid, SIGINT) != 0) { + if (errno != ENOENT) + err(1, "kill()"); + /* ENOENT means the child has terminated */ + break; + } + } + + /* we're done, kill the child for sure */ + (void)kill(pid, SIGKILL); + if (waitpid(pid, &status, 0) < 0) + err(1, "waitpid()"); + + /* assert that the child died of SIGKILL */ + ATF_REQUIRE(WIFSIGNALED(status)); + ATF_REQUIRE_MSG(WTERMSIG(status) == SIGKILL, + "child caught SIG%s", sys_signame[WTERMSIG(status)]); +} + +ATF_TC_WITHOUT_HEAD(ppoll_race); +ATF_TC_BODY(ppoll_race, tc) +{ + prace(true); +} + +ATF_TC_WITHOUT_HEAD(pselect_race); +ATF_TC_BODY(pselect_race, tc) +{ + prace(false); +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, ppoll_race); + ATF_TP_ADD_TC(tp, pselect_race); + return (atf_no_error()); +}