diff options
| author | Mark Johnston <markj@FreeBSD.org> | 2026-05-08 13:03:49 +0000 |
|---|---|---|
| committer | Mark Johnston <markj@FreeBSD.org> | 2026-05-20 19:34:50 +0000 |
| commit | 4b6a23eb8a7e4b137d9e1b527d1fa84c950484eb (patch) | |
| tree | 825e52c7dcfa1056d3f9b14568e58dbec6e07a47 /tests | |
| parent | c028080749c09e68c555155df0e9f681ba63c6ae (diff) | |
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/sys/kern/Makefile | 1 | ||||
| -rw-r--r-- | tests/sys/kern/procdesc.c | 86 |
2 files changed, 86 insertions, 1 deletions
diff --git a/tests/sys/kern/Makefile b/tests/sys/kern/Makefile index a06e8702f16d..fb75720114ac 100644 --- a/tests/sys/kern/Makefile +++ b/tests/sys/kern/Makefile @@ -104,6 +104,7 @@ LIBADD.kcov+= pthread CFLAGS.ktls_test+= -DOPENSSL_API_COMPAT=0x10100000L LIBADD.ktls_test+= crypto util LIBADD.listener_wakeup+= pthread +LIBADD.procdesc+= pthread LIBADD.shutdown_dgram+= pthread LIBADD.socket_msg_waitall+= pthread LIBADD.socket_splice+= pthread diff --git a/tests/sys/kern/procdesc.c b/tests/sys/kern/procdesc.c index 3334ee404518..2d710ef057f2 100644 --- a/tests/sys/kern/procdesc.c +++ b/tests/sys/kern/procdesc.c @@ -2,6 +2,7 @@ * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2026 ConnectWise + * Copyright (c) 2026 Mark Johnston <markj@FreeBSD.org> * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -32,8 +33,12 @@ #include <sys/sysctl.h> #include <sys/wait.h> -#include <atf-c.h> +#include <poll.h> +#include <pthread.h> #include <stdio.h> +#include <unistd.h> + +#include <atf-c.h> /* Tests for procdesc(4) that aren't specific to any one syscall */ @@ -90,9 +95,88 @@ ATF_TC_BODY(pid_recycle, tc) close(pd); } +static void * +poll_procdesc(void *arg) +{ + struct pollfd pfd; + + pfd.fd = *(int *)arg; + pfd.events = POLLHUP; + (void)poll(&pfd, 1, 5000); + return ((void *)(uintptr_t)pfd.revents); +} + +/* + * Regression test to exercise the case where a procdesc is closed while a + * thread is poll()ing it. + */ +ATF_TC_WITHOUT_HEAD(poll_close_race); +ATF_TC_BODY(poll_close_race, tc) +{ + pthread_t thr; + pid_t pid; + uintptr_t revents; + int error, pd; + + pid = pdfork(&pd, PD_DAEMON); + ATF_REQUIRE_MSG(pid >= 0, "pdfork: %s", strerror(errno)); + if (pid == 0) { + pause(); + _exit(0); + } + + error = pthread_create(&thr, NULL, poll_procdesc, &pd); + ATF_REQUIRE_MSG(error == 0, "pthread_create: %s", strerror(error)); + + /* Wait for the thread to block in poll(2). */ + usleep(250000); + + ATF_REQUIRE_MSG(close(pd) == 0, "close: %s", strerror(errno)); + + error = pthread_join(thr, (void *)&revents); + ATF_REQUIRE_MSG(error == 0, "pthread_join: %s", strerror(error)); + ATF_REQUIRE_EQ(revents, POLLNVAL); +} + +/* + * Verify that poll(2) of a procdesc returns POLLHUP when the process exits. + */ +ATF_TC_WITHOUT_HEAD(poll_exit_wakeup); +ATF_TC_BODY(poll_exit_wakeup, tc) +{ + pthread_t thr; + uintptr_t revents; + pid_t pid; + int error, pd; + + pid = pdfork(&pd, PD_DAEMON); + ATF_REQUIRE_MSG(pid >= 0, "pdfork: %s", strerror(errno)); + if (pid == 0) { + pause(); + _exit(0); + } + + error = pthread_create(&thr, NULL, poll_procdesc, &pd); + ATF_REQUIRE_MSG(error == 0, "pthread_create: %s", strerror(error)); + + /* Wait for the thread to block in poll(2). */ + usleep(250000); + + ATF_REQUIRE_MSG(pdkill(pd, SIGKILL) == 0, + "pdkill: %s", strerror(errno)); + + error = pthread_join(thr, (void *)&revents); + ATF_REQUIRE_MSG(error == 0, "pthread_join: %s", strerror(error)); + ATF_REQUIRE_EQ(revents, POLLHUP); + + ATF_REQUIRE_MSG(close(pd) == 0, "close: %s", strerror(errno)); +} + ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, pid_recycle); + ATF_TP_ADD_TC(tp, poll_close_race); + ATF_TP_ADD_TC(tp, poll_exit_wakeup); return (atf_no_error()); } |
