1314817Sngie/* $NetBSD: t_dup.c,v 1.9 2017/01/13 20:31:53 christos Exp $ */ 2272343Sngie 3272343Sngie/*- 4272343Sngie * Copyright (c) 2011 The NetBSD Foundation, Inc. 5272343Sngie * All rights reserved. 6272343Sngie * 7272343Sngie * This code is derived from software contributed to The NetBSD Foundation 8272343Sngie * by Jukka Ruohonen. 9272343Sngie * 10272343Sngie * Redistribution and use in source and binary forms, with or without 11272343Sngie * modification, are permitted provided that the following conditions 12272343Sngie * are met: 13272343Sngie * 1. Redistributions of source code must retain the above copyright 14272343Sngie * notice, this list of conditions and the following disclaimer. 15272343Sngie * 2. Redistributions in binary form must reproduce the above copyright 16272343Sngie * notice, this list of conditions and the following disclaimer in the 17272343Sngie * documentation and/or other materials provided with the distribution. 18272343Sngie * 19272343Sngie * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20272343Sngie * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21272343Sngie * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22272343Sngie * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23272343Sngie * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24272343Sngie * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25272343Sngie * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26272343Sngie * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27272343Sngie * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28272343Sngie * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29272343Sngie * POSSIBILITY OF SUCH DAMAGE. 30272343Sngie */ 31272343Sngie#include <sys/cdefs.h> 32314817Sngie__RCSID("$NetBSD: t_dup.c,v 1.9 2017/01/13 20:31:53 christos Exp $"); 33272343Sngie 34272343Sngie#include <sys/resource.h> 35272343Sngie#include <sys/stat.h> 36272343Sngie#include <sys/wait.h> 37272343Sngie 38272343Sngie#include <atf-c.h> 39272343Sngie#include <errno.h> 40272343Sngie#include <fcntl.h> 41272343Sngie#include <limits.h> 42314817Sngie#include <stdbool.h> 43272343Sngie#include <stdio.h> 44272343Sngie#include <stdlib.h> 45272343Sngie#include <string.h> 46272343Sngie#include <unistd.h> 47272343Sngie#include <sysexits.h> 48272343Sngie 49272343Sngiestatic char path[] = "dup"; 50276478Sngie#ifdef __NetBSD__ 51272343Sngiestatic void check_mode(bool, bool, bool); 52276478Sngie#endif 53272343Sngie 54272343Sngiestatic void 55272343Sngiecheck_mode(bool _dup, bool _dup2, bool _dup3) 56272343Sngie{ 57272343Sngie int mode[3] = { O_RDONLY, O_WRONLY, O_RDWR }; 58272343Sngie int perm[5] = { 0700, 0400, 0600, 0444, 0666 }; 59272343Sngie struct stat st, st1; 60272343Sngie int fd, fd1, fd2; 61272343Sngie size_t i, j; 62272343Sngie 63272343Sngie /* 64272343Sngie * Check that a duplicated descriptor 65272343Sngie * retains the mode of the original file. 66272343Sngie */ 67272343Sngie for (i = 0; i < __arraycount(mode); i++) { 68272343Sngie 69272343Sngie for (j = 0; j < __arraycount(perm); j++) { 70272343Sngie 71272343Sngie fd1 = open(path, mode[i] | O_CREAT, perm[j]); 72272343Sngie fd2 = open("/etc/passwd", O_RDONLY); 73272343Sngie 74272343Sngie ATF_REQUIRE(fd1 >= 0); 75272343Sngie ATF_REQUIRE(fd2 >= 0); 76272343Sngie 77272343Sngie if (_dup != false) 78272343Sngie fd = dup(fd1); 79272343Sngie else if (_dup2 != false) 80272343Sngie fd = dup2(fd1, fd2); 81272343Sngie else if (_dup3 != false) 82272343Sngie fd = dup3(fd1, fd2, O_CLOEXEC); 83272343Sngie else { 84272343Sngie fd = -1; 85272343Sngie } 86272343Sngie 87272343Sngie ATF_REQUIRE(fd >= 0); 88272343Sngie 89272343Sngie (void)memset(&st, 0, sizeof(struct stat)); 90272343Sngie (void)memset(&st1, 0, sizeof(struct stat)); 91272343Sngie 92272343Sngie ATF_REQUIRE(fstat(fd, &st) == 0); 93272343Sngie ATF_REQUIRE(fstat(fd1, &st1) == 0); 94272343Sngie 95272343Sngie if (st.st_mode != st1.st_mode) 96272343Sngie atf_tc_fail("invalid mode"); 97272343Sngie 98272343Sngie (void)close(fd); 99272343Sngie (void)close(fd1); 100272343Sngie (void)close(fd2); 101272343Sngie (void)unlink(path); 102272343Sngie } 103272343Sngie } 104272343Sngie} 105272343Sngie 106272343SngieATF_TC(dup2_basic); 107272343SngieATF_TC_HEAD(dup2_basic, tc) 108272343Sngie{ 109272343Sngie atf_tc_set_md_var(tc, "descr", "A basic test of dup2(2)"); 110272343Sngie} 111272343Sngie 112272343SngieATF_TC_BODY(dup2_basic, tc) 113272343Sngie{ 114272343Sngie int fd, fd1, fd2; 115272343Sngie 116272343Sngie fd1 = open("/etc/passwd", O_RDONLY); 117272343Sngie fd2 = open("/etc/passwd", O_RDONLY); 118272343Sngie 119272343Sngie ATF_REQUIRE(fd1 >= 0); 120272343Sngie ATF_REQUIRE(fd2 >= 0); 121272343Sngie 122272343Sngie fd = dup2(fd1, fd2); 123272343Sngie ATF_REQUIRE(fd >= 0); 124272343Sngie 125272343Sngie if (fd != fd2) 126272343Sngie atf_tc_fail("invalid descriptor"); 127272343Sngie 128272343Sngie (void)close(fd); 129272343Sngie (void)close(fd1); 130272343Sngie 131272343Sngie ATF_REQUIRE(close(fd2) != 0); 132272343Sngie} 133272343Sngie 134272343SngieATF_TC(dup2_err); 135272343SngieATF_TC_HEAD(dup2_err, tc) 136272343Sngie{ 137272343Sngie atf_tc_set_md_var(tc, "descr", "Test error conditions of dup2(2)"); 138272343Sngie} 139272343Sngie 140272343SngieATF_TC_BODY(dup2_err, tc) 141272343Sngie{ 142272343Sngie int fd; 143272343Sngie 144272343Sngie fd = open("/etc/passwd", O_RDONLY); 145272343Sngie ATF_REQUIRE(fd >= 0); 146272343Sngie 147272343Sngie errno = 0; 148272343Sngie ATF_REQUIRE_ERRNO(EBADF, dup2(-1, -1) == -1); 149272343Sngie 150272343Sngie errno = 0; 151272343Sngie ATF_REQUIRE_ERRNO(EBADF, dup2(fd, -1) == -1); 152272343Sngie 153272343Sngie errno = 0; 154272343Sngie ATF_REQUIRE_ERRNO(EBADF, dup2(-1, fd) == -1); 155272343Sngie 156272343Sngie /* 157272343Sngie * Note that this should not fail with EINVAL. 158272343Sngie */ 159272343Sngie ATF_REQUIRE(dup2(fd, fd) != -1); 160272343Sngie 161272343Sngie (void)close(fd); 162272343Sngie} 163272343Sngie 164272343SngieATF_TC(dup2_max); 165272343SngieATF_TC_HEAD(dup2_max, tc) 166272343Sngie{ 167272343Sngie atf_tc_set_md_var(tc, "descr", "Test dup2(2) against limits"); 168272343Sngie} 169272343Sngie 170272343SngieATF_TC_BODY(dup2_max, tc) 171272343Sngie{ 172272343Sngie struct rlimit res; 173272343Sngie 174272343Sngie (void)memset(&res, 0, sizeof(struct rlimit)); 175272343Sngie (void)getrlimit(RLIMIT_NOFILE, &res); 176272343Sngie 177272343Sngie errno = 0; 178272343Sngie ATF_REQUIRE_ERRNO(EBADF, dup2(STDERR_FILENO, res.rlim_cur + 1) == -1); 179272343Sngie} 180272343Sngie 181272343SngieATF_TC_WITH_CLEANUP(dup2_mode); 182272343SngieATF_TC_HEAD(dup2_mode, tc) 183272343Sngie{ 184272343Sngie atf_tc_set_md_var(tc, "descr", "A basic test of dup2(2)"); 185272343Sngie} 186272343Sngie 187272343SngieATF_TC_BODY(dup2_mode, tc) 188272343Sngie{ 189272343Sngie check_mode(false, true, false); 190272343Sngie} 191272343Sngie 192272343SngieATF_TC_CLEANUP(dup2_mode, tc) 193272343Sngie{ 194272343Sngie (void)unlink(path); 195272343Sngie} 196272343Sngie 197272343Sngie 198272343SngieATF_TC(dup3_err); 199272343SngieATF_TC_HEAD(dup3_err, tc) 200272343Sngie{ 201272343Sngie atf_tc_set_md_var(tc, "descr", 202272343Sngie "Test error conditions of dup3(2) (PR lib/45148)"); 203272343Sngie} 204272343Sngie 205272343SngieATF_TC_BODY(dup3_err, tc) 206272343Sngie{ 207272343Sngie int fd; 208272343Sngie 209272343Sngie fd = open("/etc/passwd", O_RDONLY); 210272343Sngie ATF_REQUIRE(fd >= 0); 211272343Sngie 212272343Sngie errno = 0; 213276478Sngie#if defined(__FreeBSD__) || defined(__linux__) 214276478Sngie /* 215276478Sngie * FreeBSD and linux return EINVAL, because... 216276478Sngie * 217276478Sngie * [EINVAL] The oldd argument is equal to the newd argument. 218276478Sngie */ 219276478Sngie ATF_REQUIRE(dup3(fd, fd, O_CLOEXEC) == -1); 220276478Sngie#else 221272343Sngie ATF_REQUIRE(dup3(fd, fd, O_CLOEXEC) != -1); 222276478Sngie#endif 223272343Sngie 224272343Sngie errno = 0; 225276478Sngie#if defined(__FreeBSD__) || defined(__linux__) 226276478Sngie ATF_REQUIRE_ERRNO(EINVAL, dup3(-1, -1, O_CLOEXEC) == -1); 227276478Sngie ATF_REQUIRE_ERRNO(EBADF, dup3(fd, -1, O_CLOEXEC) == -1); 228276478Sngie#else 229272343Sngie ATF_REQUIRE_ERRNO(EBADF, dup3(-1, -1, O_CLOEXEC) == -1); 230276478Sngie#endif 231272343Sngie 232272343Sngie errno = 0; 233272343Sngie ATF_REQUIRE_ERRNO(EBADF, dup3(fd, -1, O_CLOEXEC) == -1); 234272343Sngie 235272343Sngie errno = 0; 236272343Sngie ATF_REQUIRE_ERRNO(EBADF, dup3(-1, fd, O_CLOEXEC) == -1); 237272343Sngie 238272343Sngie errno = 0; 239272343Sngie ATF_REQUIRE_ERRNO(EINVAL, dup3(fd, 1, O_NOFOLLOW) == -1); 240272343Sngie 241272343Sngie (void)close(fd); 242272343Sngie} 243272343Sngie 244272343SngieATF_TC(dup3_max); 245272343SngieATF_TC_HEAD(dup3_max, tc) 246272343Sngie{ 247272343Sngie atf_tc_set_md_var(tc, "descr", "Test dup3(2) against limits"); 248272343Sngie} 249272343Sngie 250272343SngieATF_TC_BODY(dup3_max, tc) 251272343Sngie{ 252272343Sngie struct rlimit res; 253272343Sngie 254272343Sngie (void)memset(&res, 0, sizeof(struct rlimit)); 255272343Sngie (void)getrlimit(RLIMIT_NOFILE, &res); 256272343Sngie 257272343Sngie errno = 0; 258272343Sngie ATF_REQUIRE_ERRNO(EBADF, dup3(STDERR_FILENO, 259272343Sngie res.rlim_cur + 1, O_CLOEXEC) == -1); 260272343Sngie} 261272343Sngie 262272343SngieATF_TC_WITH_CLEANUP(dup3_mode); 263272343SngieATF_TC_HEAD(dup3_mode, tc) 264272343Sngie{ 265272343Sngie atf_tc_set_md_var(tc, "descr", "A basic test of dup3(2)"); 266272343Sngie} 267272343Sngie 268272343SngieATF_TC_BODY(dup3_mode, tc) 269272343Sngie{ 270272343Sngie check_mode(false, false, true); 271272343Sngie} 272272343Sngie 273272343SngieATF_TC_CLEANUP(dup3_mode, tc) 274272343Sngie{ 275272343Sngie (void)unlink(path); 276272343Sngie} 277272343Sngie 278272343SngieATF_TC(dup_err); 279272343SngieATF_TC_HEAD(dup_err, tc) 280272343Sngie{ 281272343Sngie atf_tc_set_md_var(tc, "descr", "Test error conditions of dup(2)"); 282272343Sngie} 283272343Sngie 284272343SngieATF_TC_BODY(dup_err, tc) 285272343Sngie{ 286272343Sngie 287272343Sngie errno = 0; 288272343Sngie ATF_REQUIRE_ERRNO(EBADF, dup(-1) == -1); 289272343Sngie} 290272343Sngie 291272343SngieATF_TC_WITH_CLEANUP(dup_max); 292272343SngieATF_TC_HEAD(dup_max, tc) 293272343Sngie{ 294272343Sngie atf_tc_set_md_var(tc, "descr", "Test dup(2) against limits"); 295272343Sngie} 296272343Sngie 297272343SngieATF_TC_BODY(dup_max, tc) 298272343Sngie{ 299272343Sngie struct rlimit res; 300272343Sngie int *buf, fd, sta; 301272343Sngie size_t i, n; 302272343Sngie pid_t pid; 303272343Sngie 304272343Sngie pid = fork(); 305272343Sngie ATF_REQUIRE(pid >= 0); 306272343Sngie 307272343Sngie if (pid == 0) { 308272343Sngie 309272343Sngie /* 310272343Sngie * Open a temporary file until the 311272343Sngie * maximum number of open files is 312272343Sngie * reached. Ater that dup(2) family 313272343Sngie * should fail with EMFILE. 314272343Sngie */ 315272343Sngie (void)closefrom(0); 316272343Sngie (void)memset(&res, 0, sizeof(struct rlimit)); 317272343Sngie 318272343Sngie n = 10; 319272343Sngie res.rlim_cur = res.rlim_max = n; 320272343Sngie if (setrlimit(RLIMIT_NOFILE, &res) != 0) 321272343Sngie _exit(EX_OSERR); 322272343Sngie 323272343Sngie buf = calloc(n, sizeof(int)); 324272343Sngie 325272343Sngie if (buf == NULL) 326272343Sngie _exit(EX_OSERR); 327272343Sngie 328272343Sngie buf[0] = mkstemp(path); 329272343Sngie 330272343Sngie if (buf[0] < 0) 331272343Sngie _exit(EX_OSERR); 332272343Sngie 333272343Sngie for (i = 1; i < n; i++) { 334272343Sngie 335272343Sngie buf[i] = open(path, O_RDONLY); 336272343Sngie 337272343Sngie if (buf[i] < 0) 338272343Sngie _exit(EX_OSERR); 339272343Sngie } 340272343Sngie 341272343Sngie errno = 0; 342272343Sngie fd = dup(buf[0]); 343272343Sngie 344272343Sngie if (fd != -1 || errno != EMFILE) 345272343Sngie _exit(EX_DATAERR); 346272343Sngie 347272343Sngie _exit(EXIT_SUCCESS); 348272343Sngie } 349272343Sngie 350272343Sngie (void)wait(&sta); 351272343Sngie 352272343Sngie if (WIFEXITED(sta) == 0 || WEXITSTATUS(sta) != EXIT_SUCCESS) { 353272343Sngie 354272343Sngie if (WEXITSTATUS(sta) == EX_OSERR) 355272343Sngie atf_tc_fail("system call error"); 356272343Sngie 357272343Sngie if (WEXITSTATUS(sta) == EX_DATAERR) 358272343Sngie atf_tc_fail("dup(2) dupped more than RLIMIT_NOFILE"); 359272343Sngie 360272343Sngie atf_tc_fail("unknown error"); 361272343Sngie } 362272343Sngie 363272343Sngie (void)unlink(path); 364272343Sngie} 365272343Sngie 366272343SngieATF_TC_CLEANUP(dup_max, tc) 367272343Sngie{ 368272343Sngie (void)unlink(path); 369272343Sngie} 370272343Sngie 371272343SngieATF_TC_WITH_CLEANUP(dup_mode); 372272343SngieATF_TC_HEAD(dup_mode, tc) 373272343Sngie{ 374272343Sngie atf_tc_set_md_var(tc, "descr", "A basic test of dup(2)"); 375272343Sngie} 376272343Sngie 377272343SngieATF_TC_BODY(dup_mode, tc) 378272343Sngie{ 379272343Sngie check_mode(true, false, false); 380272343Sngie} 381272343Sngie 382272343SngieATF_TC_CLEANUP(dup_mode, tc) 383272343Sngie{ 384272343Sngie (void)unlink(path); 385272343Sngie} 386272343Sngie 387272343SngieATF_TP_ADD_TCS(tp) 388272343Sngie{ 389272343Sngie 390272343Sngie ATF_TP_ADD_TC(tp, dup2_basic); 391272343Sngie ATF_TP_ADD_TC(tp, dup2_err); 392272343Sngie ATF_TP_ADD_TC(tp, dup2_max); 393272343Sngie ATF_TP_ADD_TC(tp, dup2_mode); 394272343Sngie ATF_TP_ADD_TC(tp, dup3_err); 395272343Sngie ATF_TP_ADD_TC(tp, dup3_max); 396272343Sngie ATF_TP_ADD_TC(tp, dup3_mode); 397272343Sngie ATF_TP_ADD_TC(tp, dup_err); 398272343Sngie ATF_TP_ADD_TC(tp, dup_max); 399272343Sngie ATF_TP_ADD_TC(tp, dup_mode); 400272343Sngie 401272343Sngie return atf_no_error(); 402272343Sngie} 403