1*a10bc81dSMariusz Zaborski /*-
2*a10bc81dSMariusz Zaborski * SPDX-License-Identifier: BSD-2-Clause
3*a10bc81dSMariusz Zaborski *
4*a10bc81dSMariusz Zaborski * Copyright (c) 2026 Mariusz Zaborski <oshogbo@FreeBSD.org>
5*a10bc81dSMariusz Zaborski *
6*a10bc81dSMariusz Zaborski * Redistribution and use in source and binary forms, with or without
7*a10bc81dSMariusz Zaborski * modification, are permitted provided that the following conditions
8*a10bc81dSMariusz Zaborski * are met:
9*a10bc81dSMariusz Zaborski * 1. Redistributions of source code must retain the above copyright
10*a10bc81dSMariusz Zaborski * notice, this list of conditions and the following disclaimer.
11*a10bc81dSMariusz Zaborski * 2. Redistributions in binary form must reproduce the above copyright
12*a10bc81dSMariusz Zaborski * notice, this list of conditions and the following disclaimer in the
13*a10bc81dSMariusz Zaborski * documentation and/or other materials provided with the distribution.
14*a10bc81dSMariusz Zaborski *
15*a10bc81dSMariusz Zaborski * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
16*a10bc81dSMariusz Zaborski * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17*a10bc81dSMariusz Zaborski * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18*a10bc81dSMariusz Zaborski * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
19*a10bc81dSMariusz Zaborski * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20*a10bc81dSMariusz Zaborski * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21*a10bc81dSMariusz Zaborski * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22*a10bc81dSMariusz Zaborski * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23*a10bc81dSMariusz Zaborski * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24*a10bc81dSMariusz Zaborski * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25*a10bc81dSMariusz Zaborski * SUCH DAMAGE.
26*a10bc81dSMariusz Zaborski */
27*a10bc81dSMariusz Zaborski
28*a10bc81dSMariusz Zaborski #include <sys/resource.h>
29*a10bc81dSMariusz Zaborski #include <sys/select.h>
30*a10bc81dSMariusz Zaborski
31*a10bc81dSMariusz Zaborski #include <errno.h>
32*a10bc81dSMariusz Zaborski #include <inttypes.h>
33*a10bc81dSMariusz Zaborski #include <stdlib.h>
34*a10bc81dSMariusz Zaborski #include <string.h>
35*a10bc81dSMariusz Zaborski
36*a10bc81dSMariusz Zaborski #include <libcasper.h>
37*a10bc81dSMariusz Zaborski
38*a10bc81dSMariusz Zaborski #include <atf-c.h>
39*a10bc81dSMariusz Zaborski
40*a10bc81dSMariusz Zaborski #define NCONNECTIONS (FD_SETSIZE + 64)
41*a10bc81dSMariusz Zaborski #define FD_HEADROOM 64
42*a10bc81dSMariusz Zaborski
43*a10bc81dSMariusz Zaborski /* Test that file descriptors past FD_SETSIZE (1024) work. */
44*a10bc81dSMariusz Zaborski ATF_TC_WITHOUT_HEAD(many_connections);
ATF_TC_BODY(many_connections,tc)45*a10bc81dSMariusz Zaborski ATF_TC_BODY(many_connections, tc)
46*a10bc81dSMariusz Zaborski {
47*a10bc81dSMariusz Zaborski struct rlimit rl;
48*a10bc81dSMariusz Zaborski cap_channel_t *chan;
49*a10bc81dSMariusz Zaborski cap_channel_t **clones;
50*a10bc81dSMariusz Zaborski size_t i;
51*a10bc81dSMariusz Zaborski
52*a10bc81dSMariusz Zaborski if (getrlimit(RLIMIT_NOFILE, &rl) != 0)
53*a10bc81dSMariusz Zaborski atf_tc_skip("getrlimit: %s", strerror(errno));
54*a10bc81dSMariusz Zaborski if (rl.rlim_max < NCONNECTIONS + FD_HEADROOM)
55*a10bc81dSMariusz Zaborski atf_tc_skip("RLIMIT_NOFILE hard cap %ju below required %d",
56*a10bc81dSMariusz Zaborski (uintmax_t)rl.rlim_max, NCONNECTIONS + FD_HEADROOM);
57*a10bc81dSMariusz Zaborski rl.rlim_cur = rl.rlim_max;
58*a10bc81dSMariusz Zaborski ATF_REQUIRE_MSG(setrlimit(RLIMIT_NOFILE, &rl) == 0,
59*a10bc81dSMariusz Zaborski "setrlimit: %s", strerror(errno));
60*a10bc81dSMariusz Zaborski
61*a10bc81dSMariusz Zaborski chan = cap_init();
62*a10bc81dSMariusz Zaborski ATF_REQUIRE_MSG(chan != NULL, "cap_init failed: %s", strerror(errno));
63*a10bc81dSMariusz Zaborski
64*a10bc81dSMariusz Zaborski clones = calloc(NCONNECTIONS, sizeof(*clones));
65*a10bc81dSMariusz Zaborski ATF_REQUIRE(clones != NULL);
66*a10bc81dSMariusz Zaborski
67*a10bc81dSMariusz Zaborski /*
68*a10bc81dSMariusz Zaborski * Every cap_clone(3) adds one more connection to the helper.
69*a10bc81dSMariusz Zaborski * After this loop the helper is watching more fds than an
70*a10bc81dSMariusz Zaborski * fd_set can hold.
71*a10bc81dSMariusz Zaborski */
72*a10bc81dSMariusz Zaborski for (i = 0; i < NCONNECTIONS; i++) {
73*a10bc81dSMariusz Zaborski clones[i] = cap_clone(chan);
74*a10bc81dSMariusz Zaborski ATF_REQUIRE_MSG(clones[i] != NULL,
75*a10bc81dSMariusz Zaborski "cap_clone failed at %zu/%d: %s",
76*a10bc81dSMariusz Zaborski i, NCONNECTIONS, strerror(errno));
77*a10bc81dSMariusz Zaborski }
78*a10bc81dSMariusz Zaborski
79*a10bc81dSMariusz Zaborski for (i = 0; i < NCONNECTIONS; i++)
80*a10bc81dSMariusz Zaborski cap_close(clones[i]);
81*a10bc81dSMariusz Zaborski free(clones);
82*a10bc81dSMariusz Zaborski cap_close(chan);
83*a10bc81dSMariusz Zaborski }
84*a10bc81dSMariusz Zaborski
85*a10bc81dSMariusz Zaborski #define CHURN_CONNECTIONS 50
86*a10bc81dSMariusz Zaborski #define CHURN_CLOSE_STEP 5
87*a10bc81dSMariusz Zaborski
88*a10bc81dSMariusz Zaborski /* Test that gaps in the file descriptor list do not break casper. */
89*a10bc81dSMariusz Zaborski ATF_TC_WITHOUT_HEAD(connection_churn);
ATF_TC_BODY(connection_churn,tc)90*a10bc81dSMariusz Zaborski ATF_TC_BODY(connection_churn, tc)
91*a10bc81dSMariusz Zaborski {
92*a10bc81dSMariusz Zaborski cap_channel_t *chan, *survivor, *extra;
93*a10bc81dSMariusz Zaborski cap_channel_t *clones[CHURN_CONNECTIONS];
94*a10bc81dSMariusz Zaborski size_t i, survivor_idx;
95*a10bc81dSMariusz Zaborski
96*a10bc81dSMariusz Zaborski chan = cap_init();
97*a10bc81dSMariusz Zaborski ATF_REQUIRE_MSG(chan != NULL, "cap_init failed: %s", strerror(errno));
98*a10bc81dSMariusz Zaborski
99*a10bc81dSMariusz Zaborski for (i = 0; i < CHURN_CONNECTIONS; i++) {
100*a10bc81dSMariusz Zaborski clones[i] = cap_clone(chan);
101*a10bc81dSMariusz Zaborski ATF_REQUIRE_MSG(clones[i] != NULL,
102*a10bc81dSMariusz Zaborski "cap_clone failed at %zu: %s", i, strerror(errno));
103*a10bc81dSMariusz Zaborski }
104*a10bc81dSMariusz Zaborski
105*a10bc81dSMariusz Zaborski /*
106*a10bc81dSMariusz Zaborski * Close every Nth clone.
107*a10bc81dSMariusz Zaborski */
108*a10bc81dSMariusz Zaborski for (i = 0; i < CHURN_CONNECTIONS; i += CHURN_CLOSE_STEP) {
109*a10bc81dSMariusz Zaborski cap_close(clones[i]);
110*a10bc81dSMariusz Zaborski clones[i] = NULL;
111*a10bc81dSMariusz Zaborski }
112*a10bc81dSMariusz Zaborski
113*a10bc81dSMariusz Zaborski /*
114*a10bc81dSMariusz Zaborski * Force a poll() cycle: the helper handles POLLIN on chan and
115*a10bc81dSMariusz Zaborski * POLLHUP on the closed clones in the same walk.
116*a10bc81dSMariusz Zaborski */
117*a10bc81dSMariusz Zaborski extra = cap_clone(chan);
118*a10bc81dSMariusz Zaborski ATF_REQUIRE_MSG(extra != NULL, "cap_clone after churn failed: %s",
119*a10bc81dSMariusz Zaborski strerror(errno));
120*a10bc81dSMariusz Zaborski
121*a10bc81dSMariusz Zaborski /* A surviving clone must still round-trip. */
122*a10bc81dSMariusz Zaborski survivor_idx = 1;
123*a10bc81dSMariusz Zaborski survivor = cap_clone(clones[survivor_idx]);
124*a10bc81dSMariusz Zaborski ATF_REQUIRE_MSG(survivor != NULL,
125*a10bc81dSMariusz Zaborski "cap_clone on survivor failed: %s", strerror(errno));
126*a10bc81dSMariusz Zaborski
127*a10bc81dSMariusz Zaborski cap_close(survivor);
128*a10bc81dSMariusz Zaborski cap_close(extra);
129*a10bc81dSMariusz Zaborski for (i = 0; i < CHURN_CONNECTIONS; i++) {
130*a10bc81dSMariusz Zaborski if (clones[i] != NULL)
131*a10bc81dSMariusz Zaborski cap_close(clones[i]);
132*a10bc81dSMariusz Zaborski }
133*a10bc81dSMariusz Zaborski cap_close(chan);
134*a10bc81dSMariusz Zaborski }
135*a10bc81dSMariusz Zaborski
ATF_TP_ADD_TCS(tp)136*a10bc81dSMariusz Zaborski ATF_TP_ADD_TCS(tp)
137*a10bc81dSMariusz Zaborski {
138*a10bc81dSMariusz Zaborski
139*a10bc81dSMariusz Zaborski ATF_TP_ADD_TC(tp, many_connections);
140*a10bc81dSMariusz Zaborski ATF_TP_ADD_TC(tp, connection_churn);
141*a10bc81dSMariusz Zaborski return (atf_no_error());
142*a10bc81dSMariusz Zaborski }
143