1*49a6e16fSderaadt /* $OpenBSD: t_dup.c,v 1.3 2021/12/13 16:56:48 deraadt Exp $ */
2a545a52cSbluhm /* $NetBSD: t_dup.c,v 1.9 2017/01/13 20:31:53 christos Exp $ */
3a545a52cSbluhm
4a545a52cSbluhm /*-
5a545a52cSbluhm * Copyright (c) 2011 The NetBSD Foundation, Inc.
6a545a52cSbluhm * All rights reserved.
7a545a52cSbluhm *
8a545a52cSbluhm * This code is derived from software contributed to The NetBSD Foundation
9a545a52cSbluhm * by Jukka Ruohonen.
10a545a52cSbluhm *
11a545a52cSbluhm * Redistribution and use in source and binary forms, with or without
12a545a52cSbluhm * modification, are permitted provided that the following conditions
13a545a52cSbluhm * are met:
14a545a52cSbluhm * 1. Redistributions of source code must retain the above copyright
15a545a52cSbluhm * notice, this list of conditions and the following disclaimer.
16a545a52cSbluhm * 2. Redistributions in binary form must reproduce the above copyright
17a545a52cSbluhm * notice, this list of conditions and the following disclaimer in the
18a545a52cSbluhm * documentation and/or other materials provided with the distribution.
19a545a52cSbluhm *
20a545a52cSbluhm * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21a545a52cSbluhm * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22a545a52cSbluhm * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23a545a52cSbluhm * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24a545a52cSbluhm * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25a545a52cSbluhm * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26a545a52cSbluhm * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27a545a52cSbluhm * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28a545a52cSbluhm * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29a545a52cSbluhm * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30a545a52cSbluhm * POSSIBILITY OF SUCH DAMAGE.
31a545a52cSbluhm */
32a545a52cSbluhm
33a545a52cSbluhm #include "macros.h"
34a545a52cSbluhm
35a545a52cSbluhm #include <sys/resource.h>
36a545a52cSbluhm #include <sys/stat.h>
37a545a52cSbluhm #include <sys/wait.h>
38a545a52cSbluhm
39a545a52cSbluhm #include "atf-c.h"
40a545a52cSbluhm #include <errno.h>
41a545a52cSbluhm #include <fcntl.h>
42a545a52cSbluhm #include <limits.h>
43a545a52cSbluhm #include <stdbool.h>
44a545a52cSbluhm #include <stdio.h>
45a545a52cSbluhm #include <stdlib.h>
46a545a52cSbluhm #include <string.h>
47a545a52cSbluhm #include <unistd.h>
48a545a52cSbluhm #include <sysexits.h>
49a545a52cSbluhm
50a545a52cSbluhm static char path[] = "dup";
51a545a52cSbluhm static void check_mode(bool, bool, bool);
52a545a52cSbluhm
53a545a52cSbluhm static void
check_mode(bool _dup,bool _dup2,bool _dup3)54a545a52cSbluhm check_mode(bool _dup, bool _dup2, bool _dup3)
55a545a52cSbluhm {
56a545a52cSbluhm int mode[3] = { O_RDONLY, O_WRONLY, O_RDWR };
57a545a52cSbluhm int perm[5] = { 0700, 0400, 0600, 0444, 0666 };
58a545a52cSbluhm struct stat st, st1;
59a545a52cSbluhm int fd, fd1, fd2;
60a545a52cSbluhm size_t i, j;
61a545a52cSbluhm
62a545a52cSbluhm /*
63a545a52cSbluhm * Check that a duplicated descriptor
64a545a52cSbluhm * retains the mode of the original file.
65a545a52cSbluhm */
66a545a52cSbluhm for (i = 0; i < __arraycount(mode); i++) {
67a545a52cSbluhm
68a545a52cSbluhm for (j = 0; j < __arraycount(perm); j++) {
69a545a52cSbluhm
70a545a52cSbluhm fd1 = open(path, mode[i] | O_CREAT, perm[j]);
71a545a52cSbluhm fd2 = open("/etc/passwd", O_RDONLY);
72a545a52cSbluhm
73a545a52cSbluhm ATF_REQUIRE(fd1 >= 0);
74a545a52cSbluhm ATF_REQUIRE(fd2 >= 0);
75a545a52cSbluhm
76a545a52cSbluhm if (_dup != false)
77a545a52cSbluhm fd = dup(fd1);
78a545a52cSbluhm else if (_dup2 != false)
79a545a52cSbluhm fd = dup2(fd1, fd2);
80a545a52cSbluhm else if (_dup3 != false)
81a545a52cSbluhm fd = dup3(fd1, fd2, O_CLOEXEC);
82a545a52cSbluhm else {
83a545a52cSbluhm fd = -1;
84a545a52cSbluhm }
85a545a52cSbluhm
86a545a52cSbluhm ATF_REQUIRE(fd >= 0);
87a545a52cSbluhm
88a545a52cSbluhm (void)memset(&st, 0, sizeof(struct stat));
89a545a52cSbluhm (void)memset(&st1, 0, sizeof(struct stat));
90a545a52cSbluhm
91a545a52cSbluhm ATF_REQUIRE(fstat(fd, &st) == 0);
92a545a52cSbluhm ATF_REQUIRE(fstat(fd1, &st1) == 0);
93a545a52cSbluhm
94a545a52cSbluhm if (st.st_mode != st1.st_mode)
95a545a52cSbluhm atf_tc_fail("invalid mode");
96a545a52cSbluhm
97a545a52cSbluhm (void)close(fd);
98a545a52cSbluhm (void)close(fd1);
99a545a52cSbluhm (void)close(fd2);
100a545a52cSbluhm (void)unlink(path);
101a545a52cSbluhm }
102a545a52cSbluhm }
103a545a52cSbluhm }
104a545a52cSbluhm
105a545a52cSbluhm ATF_TC(dup2_basic);
ATF_TC_HEAD(dup2_basic,tc)106a545a52cSbluhm ATF_TC_HEAD(dup2_basic, tc)
107a545a52cSbluhm {
108a545a52cSbluhm atf_tc_set_md_var(tc, "descr", "A basic test of dup2(2)");
109a545a52cSbluhm }
110a545a52cSbluhm
ATF_TC_BODY(dup2_basic,tc)111a545a52cSbluhm ATF_TC_BODY(dup2_basic, tc)
112a545a52cSbluhm {
113a545a52cSbluhm int fd, fd1, fd2;
114a545a52cSbluhm
115a545a52cSbluhm fd1 = open("/etc/passwd", O_RDONLY);
116a545a52cSbluhm fd2 = open("/etc/passwd", O_RDONLY);
117a545a52cSbluhm
118a545a52cSbluhm ATF_REQUIRE(fd1 >= 0);
119a545a52cSbluhm ATF_REQUIRE(fd2 >= 0);
120a545a52cSbluhm
121a545a52cSbluhm fd = dup2(fd1, fd2);
122a545a52cSbluhm ATF_REQUIRE(fd >= 0);
123a545a52cSbluhm
124a545a52cSbluhm if (fd != fd2)
125a545a52cSbluhm atf_tc_fail("invalid descriptor");
126a545a52cSbluhm
127a545a52cSbluhm (void)close(fd);
128a545a52cSbluhm (void)close(fd1);
129a545a52cSbluhm
130a545a52cSbluhm ATF_REQUIRE(close(fd2) != 0);
131a545a52cSbluhm }
132a545a52cSbluhm
133a545a52cSbluhm ATF_TC(dup2_err);
ATF_TC_HEAD(dup2_err,tc)134a545a52cSbluhm ATF_TC_HEAD(dup2_err, tc)
135a545a52cSbluhm {
136a545a52cSbluhm atf_tc_set_md_var(tc, "descr", "Test error conditions of dup2(2)");
137a545a52cSbluhm }
138a545a52cSbluhm
ATF_TC_BODY(dup2_err,tc)139a545a52cSbluhm ATF_TC_BODY(dup2_err, tc)
140a545a52cSbluhm {
141a545a52cSbluhm int fd;
142a545a52cSbluhm
143a545a52cSbluhm fd = open("/etc/passwd", O_RDONLY);
144a545a52cSbluhm ATF_REQUIRE(fd >= 0);
145a545a52cSbluhm
146a545a52cSbluhm errno = 0;
147a545a52cSbluhm ATF_REQUIRE_ERRNO(EBADF, dup2(-1, -1) == -1);
148a545a52cSbluhm
149a545a52cSbluhm errno = 0;
150a545a52cSbluhm ATF_REQUIRE_ERRNO(EBADF, dup2(fd, -1) == -1);
151a545a52cSbluhm
152a545a52cSbluhm errno = 0;
153a545a52cSbluhm ATF_REQUIRE_ERRNO(EBADF, dup2(-1, fd) == -1);
154a545a52cSbluhm
155a545a52cSbluhm /*
156a545a52cSbluhm * Note that this should not fail with EINVAL.
157a545a52cSbluhm */
158a545a52cSbluhm ATF_REQUIRE(dup2(fd, fd) != -1);
159a545a52cSbluhm
160a545a52cSbluhm (void)close(fd);
161a545a52cSbluhm }
162a545a52cSbluhm
163a545a52cSbluhm ATF_TC(dup2_max);
ATF_TC_HEAD(dup2_max,tc)164a545a52cSbluhm ATF_TC_HEAD(dup2_max, tc)
165a545a52cSbluhm {
166a545a52cSbluhm atf_tc_set_md_var(tc, "descr", "Test dup2(2) against limits");
167a545a52cSbluhm }
168a545a52cSbluhm
ATF_TC_BODY(dup2_max,tc)169a545a52cSbluhm ATF_TC_BODY(dup2_max, tc)
170a545a52cSbluhm {
171a545a52cSbluhm struct rlimit res;
172a545a52cSbluhm
173a545a52cSbluhm (void)memset(&res, 0, sizeof(struct rlimit));
174a545a52cSbluhm (void)getrlimit(RLIMIT_NOFILE, &res);
175a545a52cSbluhm
176a545a52cSbluhm errno = 0;
177a545a52cSbluhm ATF_REQUIRE_ERRNO(EBADF, dup2(STDERR_FILENO, res.rlim_cur + 1) == -1);
178a545a52cSbluhm }
179a545a52cSbluhm
180a545a52cSbluhm ATF_TC_WITH_CLEANUP(dup2_mode);
ATF_TC_HEAD(dup2_mode,tc)181a545a52cSbluhm ATF_TC_HEAD(dup2_mode, tc)
182a545a52cSbluhm {
183a545a52cSbluhm atf_tc_set_md_var(tc, "descr", "A basic test of dup2(2)");
184a545a52cSbluhm }
185a545a52cSbluhm
ATF_TC_BODY(dup2_mode,tc)186a545a52cSbluhm ATF_TC_BODY(dup2_mode, tc)
187a545a52cSbluhm {
188a545a52cSbluhm check_mode(false, true, false);
189a545a52cSbluhm }
190a545a52cSbluhm
ATF_TC_CLEANUP(dup2_mode,tc)191a545a52cSbluhm ATF_TC_CLEANUP(dup2_mode, tc)
192a545a52cSbluhm {
193a545a52cSbluhm (void)unlink(path);
194a545a52cSbluhm }
195a545a52cSbluhm
196a545a52cSbluhm
197a545a52cSbluhm ATF_TC(dup3_err);
ATF_TC_HEAD(dup3_err,tc)198a545a52cSbluhm ATF_TC_HEAD(dup3_err, tc)
199a545a52cSbluhm {
200a545a52cSbluhm atf_tc_set_md_var(tc, "descr",
201a545a52cSbluhm "Test error conditions of dup3(2) (PR lib/45148)");
202a545a52cSbluhm }
203a545a52cSbluhm
ATF_TC_BODY(dup3_err,tc)204a545a52cSbluhm ATF_TC_BODY(dup3_err, tc)
205a545a52cSbluhm {
206a545a52cSbluhm int fd;
207a545a52cSbluhm
208a545a52cSbluhm fd = open("/etc/passwd", O_RDONLY);
209a545a52cSbluhm ATF_REQUIRE(fd >= 0);
210a545a52cSbluhm
211a545a52cSbluhm errno = 0;
2127496d4e5Sbluhm #ifdef __OpenBSD__
213a545a52cSbluhm ATF_REQUIRE(dup3(fd, fd, O_CLOEXEC) == -1);
2147496d4e5Sbluhm #else
2157496d4e5Sbluhm ATF_REQUIRE(dup3(fd, fd, O_CLOEXEC) != -1);
2167496d4e5Sbluhm #endif
217a545a52cSbluhm
218a545a52cSbluhm errno = 0;
2197496d4e5Sbluhm #ifdef __OpenBSD__
220a545a52cSbluhm ATF_REQUIRE_ERRNO(EINVAL, dup3(-1, -1, O_CLOEXEC) == -1);
2217496d4e5Sbluhm #else
2227496d4e5Sbluhm ATF_REQUIRE_ERRNO(EBADF, dup3(-1, -1, O_CLOEXEC) == -1);
2237496d4e5Sbluhm #endif
224a545a52cSbluhm
225a545a52cSbluhm errno = 0;
226a545a52cSbluhm ATF_REQUIRE_ERRNO(EBADF, dup3(fd, -1, O_CLOEXEC) == -1);
227a545a52cSbluhm
228a545a52cSbluhm errno = 0;
229a545a52cSbluhm ATF_REQUIRE_ERRNO(EBADF, dup3(-1, fd, O_CLOEXEC) == -1);
230a545a52cSbluhm
231a545a52cSbluhm errno = 0;
232a545a52cSbluhm ATF_REQUIRE_ERRNO(EINVAL, dup3(fd, 1, O_NOFOLLOW) == -1);
233a545a52cSbluhm
234a545a52cSbluhm (void)close(fd);
235a545a52cSbluhm }
236a545a52cSbluhm
237a545a52cSbluhm ATF_TC(dup3_max);
ATF_TC_HEAD(dup3_max,tc)238a545a52cSbluhm ATF_TC_HEAD(dup3_max, tc)
239a545a52cSbluhm {
240a545a52cSbluhm atf_tc_set_md_var(tc, "descr", "Test dup3(2) against limits");
241a545a52cSbluhm }
242a545a52cSbluhm
ATF_TC_BODY(dup3_max,tc)243a545a52cSbluhm ATF_TC_BODY(dup3_max, tc)
244a545a52cSbluhm {
245a545a52cSbluhm struct rlimit res;
246a545a52cSbluhm
247a545a52cSbluhm (void)memset(&res, 0, sizeof(struct rlimit));
248a545a52cSbluhm (void)getrlimit(RLIMIT_NOFILE, &res);
249a545a52cSbluhm
250a545a52cSbluhm errno = 0;
251a545a52cSbluhm ATF_REQUIRE_ERRNO(EBADF, dup3(STDERR_FILENO,
252a545a52cSbluhm res.rlim_cur + 1, O_CLOEXEC) == -1);
253a545a52cSbluhm }
254a545a52cSbluhm
255a545a52cSbluhm ATF_TC_WITH_CLEANUP(dup3_mode);
ATF_TC_HEAD(dup3_mode,tc)256a545a52cSbluhm ATF_TC_HEAD(dup3_mode, tc)
257a545a52cSbluhm {
258a545a52cSbluhm atf_tc_set_md_var(tc, "descr", "A basic test of dup3(2)");
259a545a52cSbluhm }
260a545a52cSbluhm
ATF_TC_BODY(dup3_mode,tc)261a545a52cSbluhm ATF_TC_BODY(dup3_mode, tc)
262a545a52cSbluhm {
263a545a52cSbluhm check_mode(false, false, true);
264a545a52cSbluhm }
265a545a52cSbluhm
ATF_TC_CLEANUP(dup3_mode,tc)266a545a52cSbluhm ATF_TC_CLEANUP(dup3_mode, tc)
267a545a52cSbluhm {
268a545a52cSbluhm (void)unlink(path);
269a545a52cSbluhm }
270a545a52cSbluhm
271a545a52cSbluhm ATF_TC(dup_err);
ATF_TC_HEAD(dup_err,tc)272a545a52cSbluhm ATF_TC_HEAD(dup_err, tc)
273a545a52cSbluhm {
274a545a52cSbluhm atf_tc_set_md_var(tc, "descr", "Test error conditions of dup(2)");
275a545a52cSbluhm }
276a545a52cSbluhm
ATF_TC_BODY(dup_err,tc)277a545a52cSbluhm ATF_TC_BODY(dup_err, tc)
278a545a52cSbluhm {
279a545a52cSbluhm
280a545a52cSbluhm errno = 0;
281a545a52cSbluhm ATF_REQUIRE_ERRNO(EBADF, dup(-1) == -1);
282a545a52cSbluhm }
283a545a52cSbluhm
284a545a52cSbluhm ATF_TC_WITH_CLEANUP(dup_max);
ATF_TC_HEAD(dup_max,tc)285a545a52cSbluhm ATF_TC_HEAD(dup_max, tc)
286a545a52cSbluhm {
287a545a52cSbluhm atf_tc_set_md_var(tc, "descr", "Test dup(2) against limits");
288a545a52cSbluhm }
289a545a52cSbluhm
ATF_TC_BODY(dup_max,tc)290a545a52cSbluhm ATF_TC_BODY(dup_max, tc)
291a545a52cSbluhm {
292a545a52cSbluhm struct rlimit res;
293a545a52cSbluhm int *buf, fd, sta;
294a545a52cSbluhm size_t i, n;
295a545a52cSbluhm pid_t pid;
296a545a52cSbluhm
297a545a52cSbluhm pid = fork();
298a545a52cSbluhm ATF_REQUIRE(pid >= 0);
299a545a52cSbluhm
300a545a52cSbluhm if (pid == 0) {
301a545a52cSbluhm
302a545a52cSbluhm /*
303a545a52cSbluhm * Open a temporary file until the
304a545a52cSbluhm * maximum number of open files is
305a545a52cSbluhm * reached. Ater that dup(2) family
306a545a52cSbluhm * should fail with EMFILE.
307a545a52cSbluhm */
3087496d4e5Sbluhm #ifdef __OpenBSD__
309a545a52cSbluhm (void)closefrom(STDERR_FILENO + 1);
3107496d4e5Sbluhm #else
3117496d4e5Sbluhm (void)closefrom(0);
3127496d4e5Sbluhm #endif
313a545a52cSbluhm (void)memset(&res, 0, sizeof(struct rlimit));
314a545a52cSbluhm
315a545a52cSbluhm n = 10;
316a545a52cSbluhm res.rlim_cur = res.rlim_max = n;
317a545a52cSbluhm if (setrlimit(RLIMIT_NOFILE, &res) != 0)
318a545a52cSbluhm _exit(EX_OSERR);
319a545a52cSbluhm
320a545a52cSbluhm buf = calloc(n, sizeof(int));
321a545a52cSbluhm
322a545a52cSbluhm if (buf == NULL)
323a545a52cSbluhm _exit(EX_OSERR);
324a545a52cSbluhm
3257496d4e5Sbluhm #ifdef __OpenBSD__
326a545a52cSbluhm buf[0] = open(path, O_CREAT|O_EXCL|O_RDWR, S_IRUSR|S_IWUSR);
3277496d4e5Sbluhm #else
3287496d4e5Sbluhm buf[0] = mkstemp(path);
3297496d4e5Sbluhm #endif
330a545a52cSbluhm
331a545a52cSbluhm if (buf[0] < 0)
332a545a52cSbluhm _exit(EX_OSERR);
333a545a52cSbluhm
3347496d4e5Sbluhm #ifdef __OpenBSD__
335a545a52cSbluhm for (i = 1; i < n - (STDERR_FILENO + 1); i++) {
3367496d4e5Sbluhm #else
3377496d4e5Sbluhm for (i = 1; i < n; i++) {
3387496d4e5Sbluhm #endif
339a545a52cSbluhm
340a545a52cSbluhm buf[i] = open(path, O_RDONLY);
341a545a52cSbluhm
342a545a52cSbluhm if (buf[i] < 0)
343a545a52cSbluhm _exit(EX_OSERR);
344a545a52cSbluhm }
345a545a52cSbluhm
346a545a52cSbluhm errno = 0;
347a545a52cSbluhm fd = dup(buf[0]);
348a545a52cSbluhm
349a545a52cSbluhm if (fd != -1 || errno != EMFILE)
350a545a52cSbluhm _exit(EX_DATAERR);
351a545a52cSbluhm
352a545a52cSbluhm _exit(EXIT_SUCCESS);
353a545a52cSbluhm }
354a545a52cSbluhm
355a545a52cSbluhm (void)wait(&sta);
356a545a52cSbluhm
357a545a52cSbluhm if (WIFEXITED(sta) == 0 || WEXITSTATUS(sta) != EXIT_SUCCESS) {
358a545a52cSbluhm
359a545a52cSbluhm if (WEXITSTATUS(sta) == EX_OSERR)
360a545a52cSbluhm atf_tc_fail("system call error");
361a545a52cSbluhm
362a545a52cSbluhm if (WEXITSTATUS(sta) == EX_DATAERR)
363a545a52cSbluhm atf_tc_fail("dup(2) dupped more than RLIMIT_NOFILE");
364a545a52cSbluhm
365a545a52cSbluhm atf_tc_fail("unknown error");
366a545a52cSbluhm }
367a545a52cSbluhm
368a545a52cSbluhm (void)unlink(path);
369a545a52cSbluhm }
370a545a52cSbluhm
371a545a52cSbluhm ATF_TC_CLEANUP(dup_max, tc)
372a545a52cSbluhm {
373a545a52cSbluhm (void)unlink(path);
374a545a52cSbluhm }
375a545a52cSbluhm
376a545a52cSbluhm ATF_TC_WITH_CLEANUP(dup_mode);
377a545a52cSbluhm ATF_TC_HEAD(dup_mode, tc)
378a545a52cSbluhm {
379a545a52cSbluhm atf_tc_set_md_var(tc, "descr", "A basic test of dup(2)");
380a545a52cSbluhm }
381a545a52cSbluhm
382a545a52cSbluhm ATF_TC_BODY(dup_mode, tc)
383a545a52cSbluhm {
384a545a52cSbluhm check_mode(true, false, false);
385a545a52cSbluhm }
386a545a52cSbluhm
387a545a52cSbluhm ATF_TC_CLEANUP(dup_mode, tc)
388a545a52cSbluhm {
389a545a52cSbluhm (void)unlink(path);
390a545a52cSbluhm }
391a545a52cSbluhm
392a545a52cSbluhm ATF_TP_ADD_TCS(tp)
393a545a52cSbluhm {
394a545a52cSbluhm
395a545a52cSbluhm ATF_TP_ADD_TC(tp, dup2_basic);
396a545a52cSbluhm ATF_TP_ADD_TC(tp, dup2_err);
397a545a52cSbluhm ATF_TP_ADD_TC(tp, dup2_max);
398a545a52cSbluhm ATF_TP_ADD_TC(tp, dup2_mode);
399a545a52cSbluhm ATF_TP_ADD_TC(tp, dup3_err);
400a545a52cSbluhm ATF_TP_ADD_TC(tp, dup3_max);
401a545a52cSbluhm ATF_TP_ADD_TC(tp, dup3_mode);
402a545a52cSbluhm ATF_TP_ADD_TC(tp, dup_err);
403a545a52cSbluhm ATF_TP_ADD_TC(tp, dup_max);
404a545a52cSbluhm ATF_TP_ADD_TC(tp, dup_mode);
405a545a52cSbluhm
406a545a52cSbluhm return atf_no_error();
407a545a52cSbluhm }
408