1 /*- 2 * Copyright (c) 2018 Enji Cooper. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27 #include <sys/cdefs.h> 28 __FBSDID("$FreeBSD$"); 29 30 #include <sys/param.h> 31 #include <sys/mman.h> 32 #include <sys/socket.h> 33 #include <sys/stat.h> 34 #include <sys/sysctl.h> 35 #include <sys/uio.h> 36 #include <err.h> 37 #include <errno.h> 38 #include <fcntl.h> 39 #include <netdb.h> 40 #include <paths.h> 41 #include <stdio.h> 42 #include <stdlib.h> 43 #include <string.h> 44 #include <unistd.h> 45 46 #include <atf-c.h> 47 48 const char DETERMINISTIC_PATTERN[] = 49 "The past is already gone, the future is not yet here. There's only one moment for you to live.\n"; 50 51 #define SOURCE_FILE "source" 52 #define DESTINATION_FILE "dest" 53 54 #define PORTRANGE_FIRST "net.inet.ip.portrange.first" 55 #define PORTRANGE_LAST "net.inet.ip.portrange.last" 56 57 static int portrange_first, portrange_last; 58 59 static int 60 get_int_via_sysctlbyname(const char *oidname) 61 { 62 size_t oldlen; 63 int int_value; 64 65 ATF_REQUIRE_EQ_MSG(sysctlbyname(oidname, &int_value, &oldlen, NULL, 0), 66 0, "sysctlbyname(%s, ...) failed: %s", oidname, strerror(errno)); 67 ATF_REQUIRE_EQ_MSG(sizeof(int_value), oldlen, "sanity check failed"); 68 69 return (int_value); 70 } 71 72 static int 73 generate_random_port(int seed) 74 { 75 int random_port; 76 77 printf("Generating a random port with seed=%d\n", seed); 78 if (portrange_first == 0) { 79 portrange_first = get_int_via_sysctlbyname(PORTRANGE_FIRST); 80 printf("Port range lower bound: %d\n", portrange_first); 81 } 82 83 if (portrange_last == 0) { 84 portrange_last = get_int_via_sysctlbyname(PORTRANGE_LAST); 85 printf("Port range upper bound: %d\n", portrange_last); 86 } 87 88 srand((unsigned)seed); 89 90 random_port = rand() % (portrange_last - portrange_first) + 91 portrange_first; 92 93 printf("Random port generated: %d\n", random_port); 94 return (random_port); 95 } 96 97 static void 98 resolve_localhost(struct addrinfo **res, int domain, int type, int port) 99 { 100 char *serv; 101 struct addrinfo hints; 102 int error; 103 104 ATF_REQUIRE_MSG(domain == AF_INET || domain == AF_INET6, 105 "unhandled domain: %d", domain); 106 107 ATF_REQUIRE_MSG(asprintf(&serv, "%d", port) >= 0, 108 "asprintf failed: %s", strerror(errno)); 109 110 memset(&hints, 0, sizeof(hints)); 111 hints.ai_family = domain; 112 hints.ai_flags = AI_ADDRCONFIG|AI_NUMERICSERV; 113 hints.ai_socktype = type; 114 115 error = getaddrinfo("localhost", serv, &hints, res); 116 ATF_REQUIRE_EQ_MSG(error, 0, 117 "getaddrinfo failed: %s", gai_strerror(error)); 118 free(serv); 119 } 120 121 static int 122 make_socket(int domain, int type, int protocol) 123 { 124 int sock; 125 126 sock = socket(domain, type, protocol); 127 ATF_REQUIRE_MSG(sock != -1, "socket(%d, %d, 0) failed: %s", 128 domain, type, strerror(errno)); 129 130 return (sock); 131 } 132 133 static int 134 setup_client(int domain, int type, int port) 135 { 136 struct addrinfo *res; 137 char host[NI_MAXHOST+1]; 138 int error, sock; 139 140 resolve_localhost(&res, domain, type, port); 141 error = getnameinfo( 142 (const struct sockaddr*)res->ai_addr, res->ai_addrlen, 143 host, nitems(host) - 1, NULL, 0, NI_NUMERICHOST); 144 ATF_REQUIRE_EQ_MSG(error, 0, 145 "getnameinfo failed: %s", gai_strerror(error)); 146 printf( 147 "Will try to connect to host='%s', address_family=%d, " 148 "socket_type=%d\n", 149 host, res->ai_family, res->ai_socktype); 150 sock = make_socket(res->ai_family, res->ai_socktype, res->ai_protocol); 151 error = connect(sock, (struct sockaddr*)res->ai_addr, res->ai_addrlen); 152 freeaddrinfo(res); 153 ATF_REQUIRE_EQ_MSG(error, 0, "connect failed: %s", strerror(errno)); 154 return (sock); 155 } 156 157 /* 158 * XXX: use linear probing to find a free port and eliminate `port` argument as 159 * a [const] int (it will need to be a pointer so it can be passed back out of 160 * the function and can influence which port `setup_client(..)` connects on. 161 */ 162 static int 163 setup_server(int domain, int type, int port) 164 { 165 struct addrinfo *res; 166 char host[NI_MAXHOST+1]; 167 int error, sock; 168 169 resolve_localhost(&res, domain, type, port); 170 sock = make_socket(res->ai_family, res->ai_socktype, res->ai_protocol); 171 172 error = getnameinfo( 173 (const struct sockaddr*)res->ai_addr, res->ai_addrlen, 174 host, nitems(host) - 1, NULL, 0, NI_NUMERICHOST); 175 ATF_REQUIRE_EQ_MSG(error, 0, 176 "getnameinfo failed: %s", gai_strerror(error)); 177 printf( 178 "Will try to bind socket to host='%s', address_family=%d, " 179 "socket_type=%d\n", 180 host, res->ai_family, res->ai_socktype); 181 error = bind(sock, res->ai_addr, res->ai_addrlen); 182 freeaddrinfo(res); 183 ATF_REQUIRE_EQ_MSG(error, 0, "bind failed: %s", strerror(errno)); 184 error = listen(sock, 1); 185 ATF_REQUIRE_EQ_MSG(error, 0, "listen failed: %s", strerror(errno)); 186 187 return (sock); 188 } 189 190 /* 191 * This function is a helper routine for taking data being sent by `sendfile` via 192 * `server_sock`, and pushing the received stream out to a file, denoted by 193 * `dest_filename`. 194 */ 195 static void 196 server_cat(const char *dest_filename, int server_sock, size_t len) 197 { 198 char *buffer; 199 int recv_sock; 200 ssize_t received_bytes; 201 202 buffer = calloc(len + 1, sizeof(char)); 203 if (buffer == NULL) 204 err(1, "malloc failed"); 205 206 recv_sock = accept(server_sock, NULL, 0); 207 if (recv_sock == -1) 208 err(1, "accept failed"); 209 210 /* 211 * XXX: this assumes the simplest case where all data is received in a 212 * single recv(2) call. 213 */ 214 if (recv(recv_sock, buffer, len, 0) == -1) 215 err(1, "recv failed"); 216 217 atf_utils_create_file(dest_filename, "%s", buffer); 218 219 /* 220 * This recv(2) call helps ensure the amount of sent data is exactly 221 * what was specified by `len`. 222 */ 223 received_bytes = recv(recv_sock, buffer, len, 0); 224 switch (received_bytes) { 225 case -1: 226 err(1, "recv failed"); 227 case 0: 228 break; 229 default: 230 errx(1, "received unexpected data: %s", buffer); 231 } 232 233 (void)close(recv_sock); 234 (void)close(server_sock); 235 free(buffer); 236 } 237 238 static int 239 setup_tcp_server(int domain, int port) 240 { 241 242 return (setup_server(domain, SOCK_STREAM, port)); 243 } 244 245 static int 246 setup_tcp_client(int domain, int port) 247 { 248 249 return (setup_client(domain, SOCK_STREAM, port)); 250 } 251 252 static off_t 253 file_size_from_fd(int fd) 254 { 255 struct stat st; 256 257 ATF_REQUIRE_EQ_MSG(0, fstat(fd, &st), 258 "fstat failed: %s", strerror(errno)); 259 260 return (st.st_size); 261 } 262 263 /* 264 * NB: `nbytes` == 0 has special connotations given the sendfile(2) API 265 * contract. In short, "send the whole file" (paraphrased). 266 */ 267 static void 268 verify_source_and_dest(const char* dest_filename, int src_fd, off_t offset, 269 size_t nbytes) 270 { 271 char *dest_pointer, *src_pointer; 272 off_t dest_file_size, src_file_size; 273 size_t length; 274 int dest_fd; 275 276 atf_utils_cat_file(dest_filename, "dest_file: "); 277 278 dest_fd = open(dest_filename, O_RDONLY); 279 ATF_REQUIRE_MSG(dest_fd != -1, "open failed"); 280 281 dest_file_size = file_size_from_fd(dest_fd); 282 src_file_size = file_size_from_fd(src_fd); 283 284 /* 285 * Per sendfile(2), "send the whole file" (paraphrased). This means 286 * that we need to grab the file size, as passing in length = 0 with 287 * mmap(2) will result in a failure with EINVAL (length = 0 is invalid). 288 */ 289 length = (nbytes == 0) ? (size_t)(src_file_size - offset) : nbytes; 290 291 ATF_REQUIRE_EQ_MSG(dest_file_size, length, 292 "number of bytes written out to %s (%ju) doesn't match the " 293 "expected number of bytes (%zu)", dest_filename, dest_file_size, 294 length); 295 296 ATF_REQUIRE_EQ_MSG(0, lseek(src_fd, offset, SEEK_SET), 297 "lseek failed: %s", strerror(errno)); 298 299 dest_pointer = mmap(NULL, length, PROT_READ, MAP_PRIVATE, dest_fd, 0); 300 ATF_REQUIRE_MSG(dest_pointer != MAP_FAILED, "mmap failed: %s", 301 strerror(errno)); 302 303 printf("Will mmap in the source file from offset=%jd to length=%zu\n", 304 offset, length); 305 306 src_pointer = mmap(NULL, length, PROT_READ, MAP_PRIVATE, src_fd, offset); 307 ATF_REQUIRE_MSG(src_pointer != MAP_FAILED, "mmap failed: %s", 308 strerror(errno)); 309 310 ATF_REQUIRE_EQ_MSG(0, memcmp(src_pointer, dest_pointer, length), 311 "Contents of source and destination do not match. '%s' != '%s'", 312 src_pointer, dest_pointer); 313 314 (void)munmap(src_pointer, length); 315 (void)munmap(dest_pointer, length); 316 (void)close(dest_fd); 317 } 318 319 static void 320 fd_positive_file_test(int domain) 321 { 322 off_t offset; 323 size_t nbytes, pattern_size; 324 int client_sock, error, fd, port, server_sock; 325 pid_t server_pid; 326 327 pattern_size = strlen(DETERMINISTIC_PATTERN); 328 329 atf_utils_create_file(SOURCE_FILE, "%s", DETERMINISTIC_PATTERN); 330 fd = open(SOURCE_FILE, O_RDONLY); 331 ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno)); 332 333 port = generate_random_port(__LINE__ + domain); 334 server_sock = setup_tcp_server(domain, port); 335 client_sock = setup_tcp_client(domain, port); 336 337 server_pid = atf_utils_fork(); 338 if (server_pid == 0) { 339 (void)close(client_sock); 340 server_cat(DESTINATION_FILE, server_sock, pattern_size); 341 _exit(0); 342 } else 343 (void)close(server_sock); 344 345 nbytes = 0; 346 offset = 0; 347 error = sendfile(fd, client_sock, offset, nbytes, NULL, NULL, 348 SF_FLAGS(0, 0)); 349 ATF_REQUIRE_EQ_MSG(0, error, "sendfile failed: %s", strerror(errno)); 350 (void)close(client_sock); 351 352 atf_utils_wait(server_pid, 0, "", ""); 353 verify_source_and_dest(DESTINATION_FILE, fd, offset, nbytes); 354 355 (void)close(fd); 356 } 357 358 ATF_TC(fd_positive_file_v4); 359 ATF_TC_HEAD(fd_positive_file_v4, tc) 360 { 361 362 atf_tc_set_md_var(tc, "descr", 363 "Verify regular file as file descriptor support (IPv4)"); 364 } 365 ATF_TC_BODY(fd_positive_file_v4, tc) 366 { 367 368 fd_positive_file_test(AF_INET); 369 } 370 371 ATF_TC(fd_positive_file_v6); 372 ATF_TC_HEAD(fd_positive_file_v6, tc) 373 { 374 375 atf_tc_set_md_var(tc, "descr", 376 "Verify regular file as file descriptor support (IPv6)"); 377 } 378 ATF_TC_BODY(fd_positive_file_v6, tc) 379 { 380 381 fd_positive_file_test(AF_INET6); 382 } 383 384 static void 385 fd_positive_shm_test(int domain) 386 { 387 char *shm_pointer; 388 off_t offset; 389 size_t nbytes, pattern_size; 390 pid_t server_pid; 391 int client_sock, error, fd, port, server_sock; 392 393 pattern_size = strlen(DETERMINISTIC_PATTERN); 394 395 printf("pattern size: %zu\n", pattern_size); 396 397 fd = shm_open(SHM_ANON, O_RDWR|O_CREAT, 0600); 398 ATF_REQUIRE_MSG(fd != -1, "shm_open failed: %s", strerror(errno)); 399 ATF_REQUIRE_EQ_MSG(0, ftruncate(fd, pattern_size), 400 "ftruncate failed: %s", strerror(errno)); 401 shm_pointer = mmap(NULL, pattern_size, PROT_READ|PROT_WRITE, 402 MAP_SHARED, fd, 0); 403 ATF_REQUIRE_MSG(shm_pointer != MAP_FAILED, 404 "mmap failed: %s", strerror(errno)); 405 memcpy(shm_pointer, DETERMINISTIC_PATTERN, pattern_size); 406 ATF_REQUIRE_EQ_MSG(0, 407 memcmp(shm_pointer, DETERMINISTIC_PATTERN, pattern_size), 408 "memcmp showed data mismatch: '%s' != '%s'", 409 DETERMINISTIC_PATTERN, shm_pointer); 410 411 port = generate_random_port(__LINE__ + domain); 412 server_sock = setup_tcp_server(domain, port); 413 client_sock = setup_tcp_client(domain, port); 414 415 server_pid = atf_utils_fork(); 416 if (server_pid == 0) { 417 (void)close(client_sock); 418 server_cat(DESTINATION_FILE, server_sock, pattern_size); 419 _exit(0); 420 } else 421 (void)close(server_sock); 422 423 nbytes = 0; 424 offset = 0; 425 error = sendfile(fd, client_sock, offset, nbytes, NULL, NULL, 426 SF_FLAGS(0, 0)); 427 ATF_REQUIRE_EQ_MSG(0, error, "sendfile failed: %s", strerror(errno)); 428 (void)close(client_sock); 429 430 atf_utils_wait(server_pid, 0, "", ""); 431 verify_source_and_dest(DESTINATION_FILE, fd, offset, nbytes); 432 433 (void)munmap(shm_pointer, sizeof(DETERMINISTIC_PATTERN)); 434 (void)close(fd); 435 } 436 437 ATF_TC(fd_positive_shm_v4); 438 ATF_TC_HEAD(fd_positive_shm_v4, tc) 439 { 440 441 atf_tc_set_md_var(tc, "descr", 442 "Verify shared memory as file descriptor support (IPv4)"); 443 } 444 ATF_TC_BODY(fd_positive_shm_v4, tc) 445 { 446 447 fd_positive_shm_test(AF_INET); 448 } 449 450 ATF_TC(fd_positive_shm_v6); 451 ATF_TC_HEAD(fd_positive_shm_v6, tc) 452 { 453 454 atf_tc_set_md_var(tc, "descr", 455 "Verify shared memory as file descriptor support (IPv6))"); 456 } 457 ATF_TC_BODY(fd_positive_shm_v6, tc) 458 { 459 460 fd_positive_shm_test(AF_INET6); 461 } 462 463 static void 464 fd_negative_bad_fd_test(int domain) 465 { 466 int client_sock, error, fd, port, server_sock; 467 468 port = generate_random_port(__LINE__ + domain); 469 server_sock = setup_tcp_server(domain, port); 470 client_sock = setup_tcp_client(domain, port); 471 472 fd = -1; 473 474 error = sendfile(fd, client_sock, 0, 0, NULL, NULL, SF_FLAGS(0, 0)); 475 ATF_REQUIRE_ERRNO(EBADF, error == -1); 476 477 (void)close(client_sock); 478 (void)close(server_sock); 479 } 480 481 ATF_TC(fd_negative_bad_fd_v4); 482 ATF_TC_HEAD(fd_negative_bad_fd_v4, tc) 483 { 484 485 atf_tc_set_md_var(tc, "descr", 486 "Verify bad file descriptor returns EBADF (IPv4)"); 487 } 488 ATF_TC_BODY(fd_negative_bad_fd_v4, tc) 489 { 490 491 fd_negative_bad_fd_test(AF_INET); 492 } 493 494 ATF_TC(fd_negative_bad_fd_v6); 495 ATF_TC_HEAD(fd_negative_bad_fd_v6, tc) 496 { 497 498 atf_tc_set_md_var(tc, "descr", 499 "Verify bad file descriptor returns EBADF (IPv6)"); 500 } 501 ATF_TC_BODY(fd_negative_bad_fd_v6, tc) 502 { 503 504 fd_negative_bad_fd_test(AF_INET6); 505 } 506 507 static void 508 flags_test(int domain) 509 { 510 off_t offset; 511 size_t nbytes, pattern_size; 512 int client_sock, error, fd, i, port, server_sock; 513 pid_t server_pid; 514 int16_t number_pages = 10; 515 516 pattern_size = strlen(DETERMINISTIC_PATTERN); 517 518 struct testcase { 519 int16_t readahead_pages, flags; 520 } testcases[] = { 521 /* This is covered in `:fd_positive_file` */ 522 #if 0 523 { 524 .readahead_pages = 0, 525 .flags = 0 526 }, 527 #endif 528 { 529 .readahead_pages = 0, 530 .flags = SF_NOCACHE 531 }, 532 #ifdef SF_USER_READAHEAD 533 { 534 .readahead_pages = 0, 535 .flags = SF_NOCACHE|SF_USER_READAHEAD 536 }, 537 { 538 .readahead_pages = 0, 539 .flags = SF_USER_READAHEAD 540 }, 541 #endif 542 { 543 .readahead_pages = number_pages, 544 .flags = 0 545 }, 546 { 547 .readahead_pages = number_pages, 548 .flags = SF_NOCACHE 549 }, 550 #ifdef SF_USER_READAHEAD 551 { 552 .readahead_pages = number_pages, 553 .flags = SF_NOCACHE|SF_USER_READAHEAD 554 }, 555 #endif 556 { 557 .readahead_pages = number_pages, 558 .flags = SF_NOCACHE 559 }, 560 { 561 .readahead_pages = number_pages, 562 .flags = SF_NODISKIO 563 } 564 }; 565 566 atf_utils_create_file(SOURCE_FILE, "%s", DETERMINISTIC_PATTERN); 567 for (i = 0; i < nitems(testcases); i++) { 568 fd = open(SOURCE_FILE, O_RDONLY); 569 ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno)); 570 571 port = generate_random_port(i * __LINE__ + domain); 572 server_sock = setup_tcp_server(domain, port); 573 client_sock = setup_tcp_client(domain, port); 574 575 server_pid = atf_utils_fork(); 576 if (server_pid == 0) { 577 (void)close(client_sock); 578 server_cat(DESTINATION_FILE, server_sock, pattern_size); 579 _exit(0); 580 } else 581 (void)close(server_sock); 582 583 nbytes = 0; 584 offset = 0; 585 error = sendfile(fd, client_sock, offset, nbytes, NULL, NULL, 586 SF_FLAGS(testcases[i].readahead_pages, testcases[i].flags)); 587 ATF_CHECK_EQ_MSG(error, 0, "sendfile testcase #%d failed: %s", 588 i, strerror(errno)); 589 (void)close(client_sock); 590 591 atf_utils_wait(server_pid, 0, "", ""); 592 verify_source_and_dest(DESTINATION_FILE, fd, offset, nbytes); 593 594 (void)close(fd); 595 } 596 } 597 598 ATF_TC(flags_v4); 599 ATF_TC_HEAD(flags_v4, tc) 600 { 601 602 atf_tc_set_md_var(tc, "descr", "Verify flags functionality (IPv4)"); 603 } 604 ATF_TC_BODY(flags_v4, tc) 605 { 606 607 flags_test(AF_INET); 608 } 609 610 ATF_TC(flags_v6); 611 ATF_TC_HEAD(flags_v6, tc) 612 { 613 614 atf_tc_set_md_var(tc, "descr", "Verify flags functionality (IPv6)"); 615 } 616 ATF_TC_BODY(flags_v6, tc) 617 { 618 619 flags_test(AF_INET6); 620 } 621 622 static void 623 hdtr_positive_test(int domain) 624 { 625 struct iovec headers[1], trailers[1]; 626 struct testcase { 627 bool include_headers, include_trailers; 628 } testcases[] = { 629 /* This is covered in `:fd_positive_file` */ 630 #if 0 631 { 632 .include_headers = false, 633 .include_trailers = false 634 }, 635 #endif 636 { 637 .include_headers = true, 638 .include_trailers = false 639 }, 640 { 641 .include_headers = false, 642 .include_trailers = true 643 }, 644 { 645 .include_headers = true, 646 .include_trailers = true 647 } 648 }; 649 off_t offset; 650 size_t nbytes; 651 int client_sock, error, fd, fd2, i, port, rc, server_sock; 652 pid_t server_pid; 653 654 headers[0].iov_base = "This is a header"; 655 headers[0].iov_len = strlen(headers[0].iov_base); 656 trailers[0].iov_base = "This is a trailer"; 657 trailers[0].iov_len = strlen(trailers[0].iov_base); 658 offset = 0; 659 nbytes = 0; 660 661 atf_tc_expect_fail( 662 "The header/trailer testcases fail today with a data mismatch; " 663 "bug # 234809"); 664 665 for (i = 0; i < nitems(testcases); i++) { 666 struct sf_hdtr hdtr; 667 char *pattern; 668 669 if (testcases[i].include_headers) { 670 hdtr.headers = headers; 671 hdtr.hdr_cnt = nitems(headers); 672 } else { 673 hdtr.headers = NULL; 674 hdtr.hdr_cnt = 0; 675 } 676 677 if (testcases[i].include_trailers) { 678 hdtr.trailers = trailers; 679 hdtr.trl_cnt = nitems(trailers); 680 } else { 681 hdtr.trailers = NULL; 682 hdtr.trl_cnt = 0; 683 } 684 685 port = generate_random_port(i * __LINE__ + domain); 686 server_sock = setup_tcp_server(domain, port); 687 client_sock = setup_tcp_client(domain, port); 688 689 rc = asprintf(&pattern, "%s%s%s", 690 testcases[i].include_headers ? (char *)headers[0].iov_base : "", 691 DETERMINISTIC_PATTERN, 692 testcases[i].include_trailers ? (char *)trailers[0].iov_base : ""); 693 ATF_REQUIRE_MSG(rc != -1, "asprintf failed: %s", strerror(errno)); 694 695 atf_utils_create_file(SOURCE_FILE ".full", "%s", pattern); 696 atf_utils_create_file(SOURCE_FILE, "%s", DETERMINISTIC_PATTERN); 697 698 fd = open(SOURCE_FILE, O_RDONLY); 699 ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno)); 700 701 fd2 = open(SOURCE_FILE ".full", O_RDONLY); 702 ATF_REQUIRE_MSG(fd2 != -1, "open failed: %s", strerror(errno)); 703 704 server_pid = atf_utils_fork(); 705 if (server_pid == 0) { 706 (void)close(client_sock); 707 server_cat(DESTINATION_FILE, server_sock, 708 strlen(pattern)); 709 _exit(0); 710 } else 711 (void)close(server_sock); 712 713 error = sendfile(fd, client_sock, offset, nbytes, &hdtr, 714 NULL, SF_FLAGS(0, 0)); 715 ATF_CHECK_EQ_MSG(error, 0, "sendfile testcase #%d failed: %s", 716 i, strerror(errno)); 717 (void)close(client_sock); 718 719 atf_utils_wait(server_pid, 0, "", ""); 720 verify_source_and_dest(DESTINATION_FILE, fd2, offset, nbytes); 721 722 (void)close(fd); 723 (void)close(fd2); 724 free(pattern); 725 pattern = NULL; 726 } 727 } 728 729 ATF_TC(hdtr_positive_v4); 730 ATF_TC_HEAD(hdtr_positive_v4, tc) 731 { 732 733 atf_tc_set_md_var(tc, "descr", 734 "Verify positive hdtr functionality (IPv4)"); 735 } 736 ATF_TC_BODY(hdtr_positive_v4, tc) 737 { 738 739 hdtr_positive_test(AF_INET); 740 } 741 742 ATF_TC(hdtr_positive_v6); 743 ATF_TC_HEAD(hdtr_positive_v6, tc) 744 { 745 746 atf_tc_set_md_var(tc, "descr", 747 "Verify positive hdtr functionality (IPv6)"); 748 } 749 ATF_TC_BODY(hdtr_positive_v6, tc) 750 { 751 752 hdtr_positive_test(AF_INET); 753 } 754 755 static void 756 hdtr_negative_bad_pointers_test(int domain) 757 { 758 int client_sock, error, fd, port, server_sock; 759 struct sf_hdtr *hdtr1, hdtr2, hdtr3; 760 761 port = generate_random_port(__LINE__ + domain); 762 763 hdtr1 = (struct sf_hdtr*)-1; 764 765 memset(&hdtr2, 0, sizeof(hdtr2)); 766 hdtr2.hdr_cnt = 1; 767 hdtr2.headers = (struct iovec*)-1; 768 769 memset(&hdtr3, 0, sizeof(hdtr3)); 770 hdtr3.trl_cnt = 1; 771 hdtr3.trailers = (struct iovec*)-1; 772 773 fd = open(SOURCE_FILE, O_CREAT|O_RDWR); 774 ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno)); 775 776 server_sock = setup_tcp_server(domain, port); 777 client_sock = setup_tcp_client(domain, port); 778 779 error = sendfile(fd, client_sock, 0, 0, hdtr1, NULL, SF_FLAGS(0, 0)); 780 ATF_CHECK_ERRNO(EFAULT, error == -1); 781 782 error = sendfile(fd, client_sock, 0, 0, &hdtr2, NULL, SF_FLAGS(0, 0)); 783 ATF_CHECK_ERRNO(EFAULT, error == -1); 784 785 error = sendfile(fd, client_sock, 0, 0, &hdtr3, NULL, SF_FLAGS(0, 0)); 786 ATF_CHECK_ERRNO(EFAULT, error == -1); 787 788 (void)close(fd); 789 (void)close(client_sock); 790 (void)close(server_sock); 791 } 792 793 ATF_TC(hdtr_negative_bad_pointers_v4); 794 ATF_TC_HEAD(hdtr_negative_bad_pointers_v4, tc) 795 { 796 797 atf_tc_set_md_var(tc, "descr", 798 "Verify that bad pointers for hdtr storage result in EFAULT (IPv4)"); 799 } 800 ATF_TC_BODY(hdtr_negative_bad_pointers_v4, tc) 801 { 802 803 hdtr_negative_bad_pointers_test(AF_INET); 804 } 805 806 ATF_TC(hdtr_negative_bad_pointers_v6); 807 ATF_TC_HEAD(hdtr_negative_bad_pointers_v6, tc) 808 { 809 810 atf_tc_set_md_var(tc, "descr", 811 "Verify that bad pointers for hdtr storage result in EFAULT (IPv6)"); 812 } 813 ATF_TC_BODY(hdtr_negative_bad_pointers_v6, tc) 814 { 815 816 hdtr_negative_bad_pointers_test(AF_INET6); 817 } 818 819 static void 820 offset_negative_value_less_than_zero_test(int domain) 821 { 822 int client_sock, error, fd, port, server_sock; 823 824 port = generate_random_port(__LINE__ + domain); 825 server_sock = setup_tcp_server(domain, port); 826 client_sock = setup_tcp_client(domain, port); 827 828 fd = open(SOURCE_FILE, O_CREAT|O_RDWR); 829 ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno)); 830 831 error = sendfile(fd, client_sock, -1, 0, NULL, NULL, SF_FLAGS(0, 0)); 832 ATF_REQUIRE_ERRNO(EINVAL, error == -1); 833 834 (void)close(fd); 835 (void)close(client_sock); 836 (void)close(server_sock); 837 } 838 839 ATF_TC(offset_negative_value_less_than_zero_v4); 840 ATF_TC_HEAD(offset_negative_value_less_than_zero_v4, tc) 841 { 842 843 atf_tc_set_md_var(tc, "descr", 844 "Verify that a negative offset results in EINVAL (IPv4)"); 845 } 846 ATF_TC_BODY(offset_negative_value_less_than_zero_v4, tc) 847 { 848 849 offset_negative_value_less_than_zero_test(AF_INET); 850 } 851 852 ATF_TC(offset_negative_value_less_than_zero_v6); 853 ATF_TC_HEAD(offset_negative_value_less_than_zero_v6, tc) 854 { 855 856 atf_tc_set_md_var(tc, "descr", 857 "Verify that a negative offset results in EINVAL (IPv6)"); 858 } 859 ATF_TC_BODY(offset_negative_value_less_than_zero_v6, tc) 860 { 861 862 offset_negative_value_less_than_zero_test(AF_INET6); 863 } 864 865 static void 866 sbytes_positive_test(int domain) 867 { 868 size_t pattern_size = strlen(DETERMINISTIC_PATTERN); 869 off_t sbytes; 870 int client_sock, error, fd, port, server_sock; 871 872 port = generate_random_port(__LINE__ + domain); 873 server_sock = setup_tcp_server(domain, port); 874 client_sock = setup_tcp_client(domain, port); 875 876 atf_utils_create_file(SOURCE_FILE, "%s", DETERMINISTIC_PATTERN); 877 fd = open(SOURCE_FILE, O_RDONLY); 878 ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno)); 879 880 error = sendfile(fd, client_sock, 0, 0, NULL, &sbytes, SF_FLAGS(0, 0)); 881 ATF_CHECK_EQ_MSG(error, 0, "sendfile failed: %s", strerror(errno)); 882 883 (void)close(fd); 884 (void)close(client_sock); 885 (void)close(server_sock); 886 887 ATF_CHECK_EQ_MSG(pattern_size, sbytes, 888 "the value returned by sbytes does not match the expected pattern " 889 "size"); 890 } 891 892 ATF_TC(sbytes_positive_v4); 893 ATF_TC_HEAD(sbytes_positive_v4, tc) 894 { 895 896 atf_tc_set_md_var(tc, "descr", 897 "Verify positive `sbytes` functionality (IPv4)"); 898 } 899 ATF_TC_BODY(sbytes_positive_v4, tc) 900 { 901 902 sbytes_positive_test(AF_INET); 903 } 904 905 ATF_TC(sbytes_positive_v6); 906 ATF_TC_HEAD(sbytes_positive_v6, tc) 907 { 908 909 atf_tc_set_md_var(tc, "descr", 910 "Verify positive `sbytes` functionality (IPv6)"); 911 } 912 ATF_TC_BODY(sbytes_positive_v6, tc) 913 { 914 915 sbytes_positive_test(AF_INET6); 916 } 917 918 static void 919 sbytes_negative_test(int domain) 920 { 921 off_t *sbytes_p = (off_t*)-1; 922 int client_sock, error, fd, port, server_sock; 923 924 port = generate_random_port(__LINE__ + domain); 925 server_sock = setup_tcp_server(domain, port); 926 client_sock = setup_tcp_client(domain, port); 927 928 atf_utils_create_file(SOURCE_FILE, "%s", DETERMINISTIC_PATTERN); 929 fd = open(SOURCE_FILE, O_RDONLY); 930 ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno)); 931 932 atf_tc_expect_fail( 933 "bug 232210: EFAULT assert fails because copyout(9) call is not checked"); 934 935 error = sendfile(fd, client_sock, 0, 0, NULL, sbytes_p, SF_FLAGS(0, 0)); 936 ATF_REQUIRE_ERRNO(EFAULT, error == -1); 937 938 (void)close(fd); 939 (void)close(client_sock); 940 (void)close(server_sock); 941 } 942 943 ATF_TC(sbytes_negative_v4); 944 ATF_TC_HEAD(sbytes_negative_v4, tc) 945 { 946 947 atf_tc_set_md_var(tc, "descr", 948 "Verify negative `sbytes` functionality (IPv4)"); 949 } 950 ATF_TC_BODY(sbytes_negative_v4, tc) 951 { 952 953 sbytes_negative_test(AF_INET); 954 } 955 956 ATF_TC(sbytes_negative_v6); 957 ATF_TC_HEAD(sbytes_negative_v6, tc) 958 { 959 960 atf_tc_set_md_var(tc, "descr", 961 "Verify negative `sbytes` functionality (IPv6)"); 962 } 963 ATF_TC_BODY(sbytes_negative_v6, tc) 964 { 965 966 sbytes_negative_test(AF_INET6); 967 } 968 969 static void 970 s_negative_not_connected_socket_test(int domain) 971 { 972 int client_sock, error, fd, port; 973 974 port = generate_random_port(__LINE__ + domain); 975 client_sock = setup_tcp_server(domain, port); 976 977 fd = open(SOURCE_FILE, O_CREAT|O_RDWR); 978 ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno)); 979 980 error = sendfile(fd, client_sock, 0, 0, NULL, NULL, SF_FLAGS(0, 0)); 981 ATF_REQUIRE_ERRNO(ENOTCONN, error == -1); 982 983 (void)close(fd); 984 (void)close(client_sock); 985 } 986 987 ATF_TC(s_negative_not_connected_socket_v4); 988 ATF_TC_HEAD(s_negative_not_connected_socket_v4, tc) 989 { 990 991 atf_tc_set_md_var(tc, "descr", 992 "Verify that a non-connected SOCK_STREAM socket results in ENOTCONN (IPv4)"); 993 } 994 995 ATF_TC_BODY(s_negative_not_connected_socket_v4, tc) 996 { 997 998 s_negative_not_connected_socket_test(AF_INET); 999 } 1000 1001 ATF_TC(s_negative_not_connected_socket_v6); 1002 ATF_TC_HEAD(s_negative_not_connected_socket_v6, tc) 1003 { 1004 1005 atf_tc_set_md_var(tc, "descr", 1006 "Verify that a non-connected SOCK_STREAM socket results in ENOTCONN (IPv6)"); 1007 } 1008 1009 ATF_TC_BODY(s_negative_not_connected_socket_v6, tc) 1010 { 1011 1012 s_negative_not_connected_socket_test(AF_INET6); 1013 } 1014 1015 ATF_TC(s_negative_not_descriptor); 1016 ATF_TC_HEAD(s_negative_not_descriptor, tc) 1017 { 1018 1019 atf_tc_set_md_var(tc, "descr", 1020 "Verify that an invalid file descriptor, e.g., -1, fails with EBADF"); 1021 } 1022 1023 ATF_TC_BODY(s_negative_not_descriptor, tc) 1024 { 1025 int client_sock, error, fd; 1026 1027 client_sock = -1; 1028 1029 fd = open(SOURCE_FILE, O_CREAT|O_RDWR); 1030 ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno)); 1031 1032 error = sendfile(fd, client_sock, 0, 0, NULL, NULL, SF_FLAGS(0, 0)); 1033 ATF_REQUIRE_ERRNO(EBADF, error == -1); 1034 1035 (void)close(fd); 1036 } 1037 1038 ATF_TC(s_negative_not_socket_file_descriptor); 1039 ATF_TC_HEAD(s_negative_not_socket_file_descriptor, tc) 1040 { 1041 1042 atf_tc_set_md_var(tc, "descr", 1043 "Verify that a non-socket file descriptor fails with ENOTSOCK"); 1044 } 1045 1046 ATF_TC_BODY(s_negative_not_socket_file_descriptor, tc) 1047 { 1048 int client_sock, error, fd; 1049 1050 fd = open(SOURCE_FILE, O_CREAT|O_RDWR); 1051 ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno)); 1052 1053 client_sock = open(_PATH_DEVNULL, O_WRONLY); 1054 ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno)); 1055 1056 error = sendfile(fd, client_sock, 0, 0, NULL, NULL, SF_FLAGS(0, 0)); 1057 ATF_REQUIRE_ERRNO(ENOTSOCK, error == -1); 1058 1059 (void)close(fd); 1060 (void)close(client_sock); 1061 } 1062 1063 static void 1064 s_negative_udp_socket_test(int domain) 1065 { 1066 int client_sock, error, fd, port; 1067 1068 port = generate_random_port(__LINE__ + domain); 1069 client_sock = setup_client(domain, SOCK_DGRAM, port); 1070 1071 fd = open(SOURCE_FILE, O_CREAT|O_RDWR); 1072 ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno)); 1073 1074 error = sendfile(fd, client_sock, 0, 0, NULL, NULL, SF_FLAGS(0, 0)); 1075 ATF_REQUIRE_ERRNO(EINVAL, error == -1); 1076 1077 (void)close(fd); 1078 (void)close(client_sock); 1079 } 1080 1081 ATF_TC(s_negative_udp_socket_v4); 1082 ATF_TC_HEAD(s_negative_udp_socket_v4, tc) 1083 { 1084 1085 atf_tc_set_md_var(tc, "descr", 1086 "Verify that a non-SOCK_STREAM type socket results in EINVAL (IPv4)"); 1087 } 1088 ATF_TC_BODY(s_negative_udp_socket_v4, tc) 1089 { 1090 1091 s_negative_udp_socket_test(AF_INET); 1092 } 1093 1094 ATF_TC(s_negative_udp_socket_v6); 1095 ATF_TC_HEAD(s_negative_udp_socket_v6, tc) 1096 { 1097 1098 atf_tc_set_md_var(tc, "descr", 1099 "Verify that a non-SOCK_STREAM type socket results in EINVAL (IPv6)"); 1100 } 1101 ATF_TC_BODY(s_negative_udp_socket_v6, tc) 1102 { 1103 1104 s_negative_udp_socket_test(AF_INET6); 1105 } 1106 1107 ATF_TP_ADD_TCS(tp) 1108 { 1109 1110 ATF_TP_ADD_TC(tp, fd_positive_file_v4); 1111 ATF_TP_ADD_TC(tp, fd_positive_file_v6); 1112 ATF_TP_ADD_TC(tp, fd_positive_shm_v4); 1113 ATF_TP_ADD_TC(tp, fd_positive_shm_v6); 1114 ATF_TP_ADD_TC(tp, fd_negative_bad_fd_v4); 1115 ATF_TP_ADD_TC(tp, fd_negative_bad_fd_v6); 1116 ATF_TP_ADD_TC(tp, flags_v4); 1117 ATF_TP_ADD_TC(tp, flags_v6); 1118 /* 1119 * TODO: the negative case for SF_NODISKIO (returns EBUSY if file in 1120 * use) is not covered yet. 1121 * 1122 * Need to lock a file in a subprocess in write mode, then try and 1123 * send the data in read mode with sendfile. 1124 * 1125 * This should work with FFS/UFS, but there are no guarantees about 1126 * other filesystem implementations of sendfile(2), e.g., ZFS. 1127 */ 1128 ATF_TP_ADD_TC(tp, hdtr_positive_v4); 1129 ATF_TP_ADD_TC(tp, hdtr_positive_v6); 1130 ATF_TP_ADD_TC(tp, hdtr_negative_bad_pointers_v4); 1131 ATF_TP_ADD_TC(tp, hdtr_negative_bad_pointers_v6); 1132 ATF_TP_ADD_TC(tp, offset_negative_value_less_than_zero_v4); 1133 ATF_TP_ADD_TC(tp, offset_negative_value_less_than_zero_v6); 1134 ATF_TP_ADD_TC(tp, sbytes_positive_v4); 1135 ATF_TP_ADD_TC(tp, sbytes_positive_v6); 1136 ATF_TP_ADD_TC(tp, sbytes_negative_v4); 1137 ATF_TP_ADD_TC(tp, sbytes_negative_v6); 1138 ATF_TP_ADD_TC(tp, s_negative_not_connected_socket_v4); 1139 ATF_TP_ADD_TC(tp, s_negative_not_connected_socket_v6); 1140 ATF_TP_ADD_TC(tp, s_negative_not_descriptor); 1141 ATF_TP_ADD_TC(tp, s_negative_not_socket_file_descriptor); 1142 ATF_TP_ADD_TC(tp, s_negative_udp_socket_v4); 1143 ATF_TP_ADD_TC(tp, s_negative_udp_socket_v6); 1144 1145 return (atf_no_error()); 1146 } 1147