1 /* $OpenBSD: t_write.c,v 1.2 2019/11/22 15:59:53 bluhm Exp $ */ 2 /* $NetBSD: t_write.c,v 1.7 2019/07/16 17:29:18 martin Exp $ */ 3 4 /*- 5 * Copyright (c) 2001, 2008 The NetBSD Foundation, Inc. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 21 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 * POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30 #include "macros.h" 31 32 #include <sys/cdefs.h> 33 __COPYRIGHT("@(#) Copyright (c) 2008\ 34 The NetBSD Foundation, inc. All rights reserved."); 35 __RCSID("$NetBSD: t_write.c,v 1.7 2019/07/16 17:29:18 martin Exp $"); 36 37 #include <sys/uio.h> 38 #include <sys/mman.h> 39 40 #include "atf-c.h" 41 #include <errno.h> 42 #include <fcntl.h> 43 #include <signal.h> 44 #include <limits.h> 45 #include <stdio.h> 46 #include <stdint.h> 47 #include <string.h> 48 #include <unistd.h> 49 #include <paths.h> 50 51 static void sighandler(int); 52 53 static bool fail = false; 54 static const char *path = "write"; 55 56 static void 57 sighandler(int signo __unused) 58 { 59 fail = false; 60 } 61 62 ATF_TC_WITH_CLEANUP(write_err); 63 ATF_TC_HEAD(write_err, tc) 64 { 65 atf_tc_set_md_var(tc, "descr", "Checks errors from write(2)"); 66 } 67 68 ATF_TC_BODY(write_err, tc) 69 { 70 char rbuf[3] = { 'a', 'b', 'c' }; 71 char wbuf[3] = { 'x', 'y', 'z' }; 72 int fd; 73 74 errno = 0; 75 ATF_REQUIRE_ERRNO(EBADF, write(-1, wbuf, sizeof(wbuf)) == -1); 76 77 fd = open(path, O_RDWR | O_CREAT); 78 79 if (fd >= 0) { 80 81 errno = 0; 82 ATF_REQUIRE_ERRNO(0, write(fd, wbuf, 3) == 3); 83 84 errno = 0; 85 ATF_REQUIRE_ERRNO(EINVAL, write(fd, wbuf, SIZE_MAX) == -1); 86 87 errno = 0; 88 ATF_REQUIRE_ERRNO(EFAULT, write(fd, (void *)-1, 1) == -1); 89 90 /* 91 * Check that the above bogus write(2) 92 * calls did not corrupt the file. 93 */ 94 ATF_REQUIRE(lseek(fd, 0, SEEK_SET) == 0); 95 ATF_REQUIRE(read(fd, rbuf, 3) == 3); 96 ATF_REQUIRE(memcmp(rbuf, wbuf, 3) == 0); 97 98 (void)close(fd); 99 (void)unlink(path); 100 } 101 } 102 103 ATF_TC_CLEANUP(write_err, tc) 104 { 105 (void)unlink(path); 106 } 107 108 ATF_TC(write_pipe); 109 ATF_TC_HEAD(write_pipe, tc) 110 { 111 atf_tc_set_md_var(tc, "descr", "Checks for EPIPE from write(2)"); 112 } 113 114 ATF_TC_BODY(write_pipe, tc) 115 { 116 int fds[2]; 117 118 ATF_REQUIRE(pipe(fds) == 0); 119 ATF_REQUIRE(signal(SIGPIPE, sighandler) == 0); 120 121 ATF_REQUIRE(write(fds[1], "x", 1) != -1); 122 ATF_REQUIRE(close(fds[0]) == 0); 123 124 errno = 0; 125 fail = true; 126 127 if (write(fds[1], "x", 1) != -1 || errno != EPIPE) 128 atf_tc_fail_nonfatal("expected EPIPE but write(2) succeeded"); 129 130 ATF_REQUIRE(close(fds[1]) == 0); 131 132 if (fail != false) 133 atf_tc_fail_nonfatal("SIGPIPE was not raised"); 134 } 135 136 ATF_TC_WITH_CLEANUP(write_pos); 137 ATF_TC_HEAD(write_pos, tc) 138 { 139 atf_tc_set_md_var(tc, "descr", "Checks that write(2) " 140 "updates the file position"); 141 } 142 143 ATF_TC_BODY(write_pos, tc) 144 { 145 const size_t n = 123; 146 size_t i; 147 int fd; 148 149 fd = open(path, O_RDWR | O_CREAT); 150 ATF_REQUIRE(fd >= 0); 151 152 for (i = 0; i < n; i++) { 153 ATF_REQUIRE(write(fd, "x", 1) == 1); 154 ATF_REQUIRE(lseek(fd, 0, SEEK_CUR) == (off_t)(i + 1)); 155 } 156 157 ATF_REQUIRE(close(fd) == 0); 158 ATF_REQUIRE(unlink(path) == 0); 159 } 160 161 ATF_TC_CLEANUP(write_pos, tc) 162 { 163 (void)unlink(path); 164 } 165 166 ATF_TC_WITH_CLEANUP(write_ret); 167 ATF_TC_HEAD(write_ret, tc) 168 { 169 atf_tc_set_md_var(tc, "descr", "Checks return values from write(2)"); 170 } 171 172 ATF_TC_BODY(write_ret, tc) 173 { 174 const size_t n = 99; 175 char buf[123]; 176 size_t i, j; 177 int fd; 178 179 fd = open(path, O_WRONLY | O_CREAT, 0600); 180 ATF_REQUIRE(fd >= 0); 181 182 (void)memset(buf, 'x', sizeof(buf)); 183 184 for (i = j = 0; i < n; i++) 185 j += write(fd, buf, sizeof(buf)); 186 187 if (j != n * 123) 188 atf_tc_fail("inconsistent return values from write(2)"); 189 190 (void)close(fd); 191 (void)unlink(path); 192 } 193 194 ATF_TC_CLEANUP(write_ret, tc) 195 { 196 (void)unlink(path); 197 } 198 199 ATF_TC(writev_iovmax); 200 ATF_TC_HEAD(writev_iovmax, tc) 201 { 202 atf_tc_set_md_var(tc, "timeout", "10"); 203 atf_tc_set_md_var(tc, "descr", 204 "Checks that file descriptor is properly FILE_UNUSE()d " 205 "when iovcnt is greater than IOV_MAX"); 206 } 207 208 ATF_TC_BODY(writev_iovmax, tc) 209 { 210 ssize_t retval; 211 212 (void)printf("Calling writev(2, NULL, IOV_MAX + 1)...\n"); 213 214 errno = 0; 215 retval = writev(2, NULL, IOV_MAX + 1); 216 217 ATF_REQUIRE_EQ_MSG(retval, -1, "got: %zd", retval); 218 ATF_REQUIRE_EQ_MSG(errno, EINVAL, "got: %s", strerror(errno)); 219 } 220 221 ATF_TC(write_fault); 222 223 ATF_TC_HEAD(write_fault, tc) 224 { 225 atf_tc_set_md_var(tc, "descr", 226 "Check that writing to non-permitted space returns EFAULT"); 227 } 228 229 #define SIZE 8192 230 231 ATF_TC_BODY(write_fault, tc) 232 { 233 int fd[2]; 234 ATF_REQUIRE(pipe(fd) != -1); 235 // Can't use /dev/null cause it doesn't access the buffer. 236 237 void *map = mmap(NULL, SIZE, PROT_NONE, MAP_ANON | MAP_PRIVATE, -1, 0); 238 ATF_REQUIRE(map != MAP_FAILED); 239 240 ssize_t retval = write(fd[1], map, SIZE); 241 242 ATF_REQUIRE_EQ_MSG(retval, -1, "got: %zd", retval); 243 ATF_REQUIRE_EQ_MSG(errno, EFAULT, "got: %s", strerror(errno)); 244 245 munmap(map, SIZE); 246 close(fd[0]); 247 close(fd[1]); 248 } 249 250 ATF_TC(read_fault); 251 252 ATF_TC_HEAD(read_fault, tc) 253 { 254 atf_tc_set_md_var(tc, "descr", 255 "Check that reading from non-permitted space returns EFAULT"); 256 } 257 258 ATF_TC_BODY(read_fault, tc) 259 { 260 int fd = open(_PATH_DEVZERO, O_RDONLY); 261 ATF_REQUIRE(fd != -1); 262 263 void *map = mmap(NULL, SIZE, PROT_NONE, MAP_ANON | MAP_PRIVATE, -1, 0); 264 ATF_REQUIRE(map != MAP_FAILED); 265 266 ssize_t retval = read(fd, map, SIZE); 267 268 ATF_REQUIRE_EQ_MSG(retval, -1, "got: %zd", retval); 269 ATF_REQUIRE_EQ_MSG(errno, EFAULT, "got: %s", strerror(errno)); 270 271 munmap(map, SIZE); 272 close(fd); 273 } 274 275 ATF_TP_ADD_TCS(tp) 276 { 277 278 ATF_TP_ADD_TC(tp, write_err); 279 ATF_TP_ADD_TC(tp, write_pipe); 280 ATF_TP_ADD_TC(tp, write_pos); 281 ATF_TP_ADD_TC(tp, write_ret); 282 ATF_TP_ADD_TC(tp, writev_iovmax); 283 ATF_TP_ADD_TC(tp, write_fault); 284 ATF_TP_ADD_TC(tp, read_fault); 285 286 return atf_no_error(); 287 } 288