Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 10 Mar 2019 16:14:43 +0000 (UTC)
From:      Enji Cooper <ngie@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-12@freebsd.org
Subject:   svn commit: r344978 - stable/12/lib/libc/tests/sys
Message-ID:  <201903101614.x2AGEhZ7002151@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: ngie
Date: Sun Mar 10 16:14:42 2019
New Revision: 344978
URL: https://svnweb.freebsd.org/changeset/base/344978

Log:
  MFC r343362,r343365,r343367,r343368,r343461,r343751,r344310:
  
  r343362:
  
  Add [initial] functional tests for sendfile(2) as lib/libc/sys/sendfile
  
  These testcases exercise a number of functional requirements for sendfile(2).
  
  The testcases use IPv4 and IPv6 domain sockets with TCP, and were confirmed
  functional on UFS and ZFS. UDP address family sockets cannot be used per the
  sendfile(2) contract, thus using UDP sockets is outside the scope of
  testing the syscall in positive cases. As seen in
  `:s_negative_udp_socket_test`, UDP is used to test the sendfile(2) contract
  to ensure that EINVAL is returned by sendfile(2).
  
  The testcases added explicitly avoid testing out `SF_SYNC` due to the
  complexity of verifying that support. However, this is a good next logical
  item to verify.
  
  The `hdtr_positive*` testcases work to a certain degree (the header
  testcases pass), but the trailer testcases do not work (it is an expected
  failure). In particular, the value received by the mock server doesn't match
  the expected value, and instead looks something like the following (using
  python array notation):
  
  `trailer[:]message[1:]`
  
  instead of:
  
  `message[:]trailer[:]`
  
  This makes me think there's a buffer overrun issue or problem with the
  offset somewhere in the sendfile(2) system call, but I need to do some
  other testing first to verify that the code is indeed sane, and my
  assumptions/code isn't buggy.
  
  The `sbytes_negative` testcases that check `sbytes` being set to an
  invalid value resulting in `EFAULT` fails today as the other change
  (which checks `copyout(9)`) has not been committed [1]. Thus, it
  should remain an expected failure (see bug 232210 for more details
  on this item).
  
  Next steps for testing sendfile(2):
  1. Fix the header/trailer testcases so that they pass.
  2. Setup if_tap interface and test with it, instead of using "localhost", per
     @asomers's suggestion.
  3. Handle short recv(2)'s in `server_cat(..)`.
  4. Add `SF_SYNC` support.
  5. Add some more negative tests outside the scope of the functional contract.
  
  PR: 		232210
  
  r343365:
  
  Unbreak the gcc build with sendfile_test after r343362
  
  gcc 8.x is more pedantic than clang 7.x with format strings and the tests
  passed `void*` variables while supplying `%s` (which is technically
  incorrect).
  
  Make the affected `void*` variables use `char*` storage instead to address
  this issue, as the compiler will upcast the values to `char*`.
  
  MFC with:	r343362
  
  r343367:
  
  Unbreak the build on architectures where size_t isn't synonymous with uintmax_t
  
  I should have used `%zu` instead of `%ju` with `size_t` types.
  
  MFC with:	r343362, r343365
  Pointyhat to:	ngie
  
  r343368:
  
  Fix up r343367
  
  I should have only changed the format qualifier with the `size_t` value,
  `length`, not the other [`off_t`] value, `dest_file_size`.
  
  MFC with:	r343362, r343365, r343367
  
  r343461:
  
  Fix reporting errors with `gai_strerror(..)`
  
  The return value (`err`) should be checked; not the `errno` value.
  
  PR:		235200
  MFC with:	r343362, r343365, r343367-r343368
  
  r343751:
  
  Avoid the DNS lookup for "localhost"
  
  ci.FreeBSD.org does not have access to a DNS resolver/network (unlike my test
  VM), so in order for the test to pass on the host, it needs to avoid the DNS
  lookup by using the numeric host address representation.
  
  PR:		235200
  MFC with:	r343362, r343365, r343367-r343368, r343461
  
  r344310:
  
  Make `server_cat(..)` handle short receives
  
  In short, the prior code was far too simplistic when it came to calling recv(2)
  and failed intermittently (or in the case of Jenkins, deterministically).
  
  Handle short recv(2)s by checking the return code and incrementing the window
  into the buffer by the number of received bytes. If the number of received
  bytes <= 0, then bail out of the loop, and test the total number of received
  bytes vs the expected number of bytes sent for equality, and base whether or
  not the test passes/fails on that fact.
  
  Remove the expected failure, now that the hdtr testcases deterministically pass
  on my host after this change [1].
  
  PR:		234809 [1], 235200
  
  Approved by:	emaste (mentor, implicit: MFC to ^/stable/11 in r344977)
  Differential Revision: https://reviews.freebsd.org/D19523

Added:
  stable/12/lib/libc/tests/sys/sendfile_test.c
     - copied, changed from r343362, head/lib/libc/tests/sys/sendfile_test.c
Modified:
  stable/12/lib/libc/tests/sys/Makefile
Directory Properties:
  stable/12/   (props changed)

Modified: stable/12/lib/libc/tests/sys/Makefile
==============================================================================
--- stable/12/lib/libc/tests/sys/Makefile	Sun Mar 10 16:13:00 2019	(r344977)
+++ stable/12/lib/libc/tests/sys/Makefile	Sun Mar 10 16:14:42 2019	(r344978)
@@ -8,6 +8,7 @@ PACKAGE=			tests
 ATF_TESTS_C+=			brk_test
 .endif
 ATF_TESTS_C+=			queue_test
+ATF_TESTS_C+=			sendfile_test
 
 # TODO: clone, lwp_create, lwp_ctl, posix_fadvise, recvmmsg,
 # swapcontext

Copied and modified: stable/12/lib/libc/tests/sys/sendfile_test.c (from r343362, head/lib/libc/tests/sys/sendfile_test.c)
==============================================================================
--- head/lib/libc/tests/sys/sendfile_test.c	Wed Jan 23 22:00:17 2019	(r343362, copy source)
+++ stable/12/lib/libc/tests/sys/sendfile_test.c	Sun Mar 10 16:14:42 2019	(r344978)
@@ -97,24 +97,33 @@ generate_random_port(int seed)
 static void
 resolve_localhost(struct addrinfo **res, int domain, int type, int port)
 {
+	const char *host;
 	char *serv;
 	struct addrinfo hints;
 	int error;
 
-	ATF_REQUIRE_MSG(domain == AF_INET || domain == AF_INET6,
-	    "unhandled domain: %d", domain);
+	switch (domain) {
+	case AF_INET:
+		host = "127.0.0.1";
+		break;
+	case AF_INET6:
+		host = "::1";
+		break;
+	default:
+		atf_tc_fail("unhandled domain: %d", domain);
+	}
 
 	ATF_REQUIRE_MSG(asprintf(&serv, "%d", port) >= 0,
 	    "asprintf failed: %s", strerror(errno));
 
 	memset(&hints, 0, sizeof(hints));
 	hints.ai_family = domain;
-	hints.ai_flags = AI_ADDRCONFIG|AI_NUMERICSERV;
+	hints.ai_flags = AI_ADDRCONFIG|AI_NUMERICSERV|AI_NUMERICHOST;
 	hints.ai_socktype = type;
 
-	error = getaddrinfo("localhost", serv, &hints, res);
+	error = getaddrinfo(host, serv, &hints, res);
 	ATF_REQUIRE_EQ_MSG(error, 0,
-	    "getaddrinfo failed: %s", gai_strerror(errno));
+	    "getaddrinfo failed: %s", gai_strerror(error));
 	free(serv);
 }
 
@@ -147,6 +156,8 @@ setup_client(int domain, int type, int port)
 	    "Will try to connect to host='%s', address_family=%d, "
 	    "socket_type=%d\n",
 	    host, res->ai_family, res->ai_socktype);
+	/* Avoid a double print when forked by flushing. */
+	fflush(stdout);
 	sock = make_socket(res->ai_family, res->ai_socktype, res->ai_protocol);
 	error = connect(sock, (struct sockaddr*)res->ai_addr, res->ai_addrlen);
 	freeaddrinfo(res);
@@ -178,6 +189,8 @@ setup_server(int domain, int type, int port)
 	    "Will try to bind socket to host='%s', address_family=%d, "
 	    "socket_type=%d\n",
 	    host, res->ai_family, res->ai_socktype);
+	/* Avoid a double print when forked by flushing. */
+	fflush(stdout);
 	error = bind(sock, res->ai_addr, res->ai_addrlen);
 	freeaddrinfo(res);
 	ATF_REQUIRE_EQ_MSG(error, 0, "bind failed: %s", strerror(errno));
@@ -195,11 +208,17 @@ setup_server(int domain, int type, int port)
 static void
 server_cat(const char *dest_filename, int server_sock, size_t len)
 {
-	void *buffer;
+	char *buffer, *buf_window_ptr;
 	int recv_sock;
-	ssize_t received_bytes;
+	size_t buffer_size;
+	ssize_t received_bytes, recv_ret;
 
-	buffer = calloc(len + 1, sizeof(char));
+	/*
+	 * Ensure that there isn't excess data sent across the wire by
+	 * capturing 10 extra bytes (plus 1 for nul).
+	 */
+	buffer_size = len + 10 + 1;
+	buffer = calloc(buffer_size, sizeof(char));
 	if (buffer == NULL)
 		err(1, "malloc failed");
 
@@ -207,32 +226,26 @@ server_cat(const char *dest_filename, int server_sock,
 	if (recv_sock == -1)
 		err(1, "accept failed");
 
-	/*
-	 * XXX: this assumes the simplest case where all data is received in a
-	 * single recv(2) call.
-	 */
-	if (recv(recv_sock, buffer, len, 0) == -1)
-		err(1, "recv failed");
+	buf_window_ptr = buffer;
+	received_bytes = 0;
+	do {
+		recv_ret = recv(recv_sock, buf_window_ptr,
+		    buffer_size - received_bytes, 0);
+		if (recv_ret <= 0)
+			break;
+		buf_window_ptr += recv_ret;
+		received_bytes += recv_ret;
+	} while (received_bytes < buffer_size);
 
 	atf_utils_create_file(dest_filename, "%s", buffer);
 
-	/*
-	 * This recv(2) call helps ensure the amount of sent data is exactly
-	 * what was specified by `len`.
-	 */
-	received_bytes = recv(recv_sock, buffer, len, 0);
-	switch (received_bytes) {
-	case -1:
-		err(1, "recv failed");
-	case 0:
-		break;
-	default:
-		errx(1, "received unexpected data: %s", buffer);
-	}
-
 	(void)close(recv_sock);
 	(void)close(server_sock);
 	free(buffer);
+
+	if (received_bytes != len)
+		errx(1, "received unexpected data: %zd != %zd", received_bytes,
+		    len);
 }
 
 static int
@@ -268,7 +281,7 @@ static void
 verify_source_and_dest(const char* dest_filename, int src_fd, off_t offset,
     size_t nbytes)
 {
-	void *dest_pointer, *src_pointer;
+	char *dest_pointer, *src_pointer;
 	off_t dest_file_size, src_file_size;
 	size_t length;
 	int dest_fd;
@@ -290,7 +303,7 @@ verify_source_and_dest(const char* dest_filename, int 
 
 	ATF_REQUIRE_EQ_MSG(dest_file_size, length,
 	    "number of bytes written out to %s (%ju) doesn't match the "
-	    "expected number of bytes (%ju)", dest_filename, dest_file_size,
+	    "expected number of bytes (%zu)", dest_filename, dest_file_size,
 	    length);
 
 	ATF_REQUIRE_EQ_MSG(0, lseek(src_fd, offset, SEEK_SET),
@@ -384,7 +397,7 @@ ATF_TC_BODY(fd_positive_file_v6, tc)
 static void
 fd_positive_shm_test(int domain)
 {
-	void *shm_pointer;
+	char *shm_pointer;
 	off_t offset;
 	size_t nbytes, pattern_size;
 	pid_t server_pid;
@@ -658,10 +671,6 @@ hdtr_positive_test(int domain)
 	offset = 0;
 	nbytes = 0;
 
-	atf_tc_expect_fail(
-	    "The header/trailer testcases fail today with a data mismatch; "
-	    "bug # 234809");
-
 	for (i = 0; i < nitems(testcases); i++) {
 		struct sf_hdtr hdtr;
 		char *pattern;
@@ -687,9 +696,9 @@ hdtr_positive_test(int domain)
 		client_sock = setup_tcp_client(domain, port);
 
 		rc = asprintf(&pattern, "%s%s%s",
-		    testcases[i].include_headers ? headers[0].iov_base : "",
+		    testcases[i].include_headers ? (char *)headers[0].iov_base : "",
 		    DETERMINISTIC_PATTERN,
-		    testcases[i].include_trailers ? trailers[0].iov_base : "");
+		    testcases[i].include_trailers ? (char *)trailers[0].iov_base : "");
 		ATF_REQUIRE_MSG(rc != -1, "asprintf failed: %s", strerror(errno));
 
 		atf_utils_create_file(SOURCE_FILE ".full", "%s", pattern);



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201903101614.x2AGEhZ7002151>