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