// Copyright 2017 The Fuchsia Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include #include #include #include #include #include #include #include #include #include #include #include #include #include bool socketpair_test(void) { BEGIN_TEST; int fds[2]; int status = socketpair(AF_UNIX, SOCK_STREAM, 0, fds); ASSERT_EQ(status, 0, "socketpair(AF_UNIX, SOCK_STREAM, 0, fds) failed"); // write() and read() should work. char buf[4] = "abc\0"; status = write(fds[0], buf, 4); if (status < 0) printf("write failed %s\n", strerror(errno)); EXPECT_EQ(status, 4, "write failed"); char recvbuf[4]; status = read(fds[1], recvbuf, 4); if (status < 0) printf("read failed %s\n", strerror(errno)); EXPECT_EQ(status, 4, "read failed"); EXPECT_EQ(memcmp(buf, recvbuf, 4), 0, "data did not make it after write+read"); // send() and recv() should also work. memcpy(buf, "def", 4); status = send(fds[1], buf, 4, 0); if (status < 0) printf("send failed %s\n", strerror(errno)); EXPECT_EQ(status, 4, "send failed"); status = recv(fds[0], recvbuf, 4, 0); if (status < 0) printf("recv failed %s\n", strerror(errno)); EXPECT_EQ(memcmp(buf, recvbuf, 4), 0, "data did not make it after send+recv"); EXPECT_EQ(close(fds[0]), 0, "close(fds[0]) failed"); EXPECT_EQ(close(fds[1]), 0, "close(fds[1]) failed"); END_TEST; } static_assert(EAGAIN == EWOULDBLOCK, "Assuming EAGAIN and EWOULDBLOCK have same value"); bool socketpair_shutdown_setup(int fds[2]) { int status = socketpair(AF_UNIX, SOCK_STREAM, 0, fds); ASSERT_EQ(status, 0, "socketpair(AF_UNIX, SOCK_STREAM, 0, fds) failed"); // Set both ends to non-blocking to make testing for readability/writability easier. ASSERT_EQ(fcntl(fds[0], F_SETFL, O_NONBLOCK), 0, ""); ASSERT_EQ(fcntl(fds[1], F_SETFL, O_NONBLOCK), 0, ""); char buf[1] = {}; // Both sides should be readable. errno = 0; status = read(fds[0], buf, sizeof(buf)); EXPECT_EQ(status, -1, "fds[0] should initially be readable"); EXPECT_EQ(errno, EAGAIN, ""); errno = 0; status = read(fds[1], buf, sizeof(buf)); EXPECT_EQ(status, -1, "fds[1] should initially be readable"); EXPECT_EQ(errno, EAGAIN, ""); // Both sides should be writable. EXPECT_EQ(write(fds[0], buf, sizeof(buf)), 1, "fds[0] should be initially writable"); EXPECT_EQ(write(fds[1], buf, sizeof(buf)), 1, "fds[1] should be initially writable"); EXPECT_EQ(read(fds[0], buf, sizeof(buf)), 1, ""); EXPECT_EQ(read(fds[1], buf, sizeof(buf)), 1, ""); return true; } #if defined(__Fuchsia__) #define SEND_FLAGS 0 #else #define SEND_FLAGS MSG_NOSIGNAL #endif bool socketpair_shutdown_rd_test(void) { BEGIN_TEST; int fds[2]; socketpair_shutdown_setup(fds); // Write a byte into fds[1] to test for readability later. char buf[1] = {}; EXPECT_EQ(write(fds[1], buf, sizeof(buf)), 1, ""); // Close one side down for reading. int status = shutdown(fds[0], SHUT_RD); EXPECT_EQ(status, 0, "shutdown(fds[0], SHUT_RD)"); if (status != 0) printf("\nerrno %d\n", errno); // Can read the byte already written into the pipe. EXPECT_EQ(read(fds[0], buf, sizeof(buf)), 1, "fds[0] should not be readable after SHUT_RD"); // But not send any further bytes EXPECT_EQ(send(fds[1], buf, sizeof(buf), SEND_FLAGS), -1, ""); EXPECT_EQ(errno, EPIPE, "send should return EPIPE after shutdown(SHUT_RD) on other side"); // Or read any more EXPECT_EQ(read(fds[0], buf, sizeof(buf)), 0, ""); EXPECT_EQ(close(fds[0]), 0, ""); EXPECT_EQ(close(fds[1]), 0, ""); END_TEST; } bool socketpair_shutdown_wr_test(void) { BEGIN_TEST; int fds[2]; socketpair_shutdown_setup(fds); // Close one side down for writing. int status = shutdown(fds[0], SHUT_WR); EXPECT_EQ(status, 0, "shutdown(fds[0], SHUT_WR)"); if (status != 0) printf("\nerrno %d\n", errno); char buf[1] = {}; // Should still be readable. EXPECT_EQ(read(fds[0], buf, sizeof(buf)), -1, ""); EXPECT_EQ(errno, EAGAIN, "errno after read after SHUT_WR"); // But not writable EXPECT_EQ(send(fds[0], buf, sizeof(buf), SEND_FLAGS), -1, "write after SHUT_WR"); EXPECT_EQ(errno, EPIPE, "errno after write after SHUT_WR"); // Should still be able to write + read a message in the other direction. EXPECT_EQ(write(fds[1], buf, sizeof(buf)), 1, ""); EXPECT_EQ(read(fds[0], buf, sizeof(buf)), 1, ""); EXPECT_EQ(close(fds[0]), 0, ""); EXPECT_EQ(close(fds[1]), 0, ""); END_TEST; } bool socketpair_shutdown_rdwr_test(void) { BEGIN_TEST; int fds[2]; socketpair_shutdown_setup(fds); // Close one side for reading and writing. int status = shutdown(fds[0], SHUT_RDWR); EXPECT_EQ(status, 0, "shutdown(fds[0], SHUT_RDWR"); if (status != 0) printf("\nerrno %d\n", errno); char buf[1] = {}; // Writing should fail. EXPECT_EQ(send(fds[0], buf, sizeof(buf), SEND_FLAGS), -1, ""); EXPECT_EQ(errno, EPIPE, "errno after write after SHUT_RDWR"); // Reading should return no data. EXPECT_EQ(read(fds[0], buf, sizeof(buf)), 0, ""); END_TEST; } typedef struct poll_for_read_args { int fd; int poll_result; zx_time_t poll_time; } poll_for_read_args_t; int poll_for_read_with_timeout(void* arg) { poll_for_read_args_t* poll_args = (poll_for_read_args_t*) arg; struct pollfd pollfd; pollfd.fd = poll_args->fd; pollfd.events = POLLIN; pollfd.revents = 0; int timeout_ms = 100; zx_time_t time_before = zx_clock_get(CLOCK_MONOTONIC); poll_args->poll_result = poll(&pollfd, 1, timeout_ms); zx_time_t time_after = zx_clock_get(CLOCK_MONOTONIC); poll_args->poll_time = time_after - time_before; int num_readable = 0; EXPECT_EQ(ioctl(poll_args->fd, FIONREAD, &num_readable), 0, "ioctl(FIONREAD)"); EXPECT_EQ(num_readable, 0, ""); return 0; } bool socketpair_shutdown_self_wr_poll_test(void) { BEGIN_TEST; int fds[2]; socketpair_shutdown_setup(fds); poll_for_read_args_t poll_args = {}; poll_args.fd = fds[0]; thrd_t poll_thread; int thrd_create_result = thrd_create(&poll_thread, poll_for_read_with_timeout, &poll_args); ASSERT_EQ(thrd_create_result, thrd_success, "create blocking read thread"); shutdown(fds[0], SHUT_RDWR); ASSERT_EQ(thrd_join(poll_thread, NULL), thrd_success, "join blocking read thread"); EXPECT_EQ(poll_args.poll_result, 1, "poll should have one entry"); EXPECT_LT(poll_args.poll_time, 100u * 1000 * 1000, "poll should not have timed out"); END_TEST; } bool socketpair_shutdown_peer_wr_poll_test(void) { BEGIN_TEST; int fds[2]; socketpair_shutdown_setup(fds); poll_for_read_args_t poll_args = {}; poll_args.fd = fds[0]; thrd_t poll_thread; int thrd_create_result = thrd_create(&poll_thread, poll_for_read_with_timeout, &poll_args); ASSERT_EQ(thrd_create_result, thrd_success, "create blocking read thread"); shutdown(fds[1], SHUT_RDWR); ASSERT_EQ(thrd_join(poll_thread, NULL), thrd_success, "join blocking read thread"); EXPECT_EQ(poll_args.poll_result, 1, "poll should have one entry"); EXPECT_LT(poll_args.poll_time, 100u * 1000 * 1000, "poll should not have timed out"); END_TEST; } #define BUF_SIZE 256 typedef struct recv_args { int fd; int recv_result; int recv_errno; char buf[BUF_SIZE]; } recv_args_t; int recv_thread(void* arg) { recv_args_t* recv_args = (recv_args_t*) arg; recv_args->recv_result = recv(recv_args->fd, recv_args->buf, BUF_SIZE, 0u); if (recv_args->recv_result < 0) recv_args->recv_errno = errno; return 0; } bool socketpair_shutdown_self_rd_during_recv_test(void) { BEGIN_TEST; int fds[2]; int status = socketpair(AF_UNIX, SOCK_STREAM, 0, fds); ASSERT_EQ(status, 0, "socketpair(AF_UNIX, SOCK_STREAM, 0, fds) failed"); recv_args_t recv_args = {}; recv_args.fd = fds[0]; thrd_t t; int thrd_create_result = thrd_create(&t, recv_thread, &recv_args); ASSERT_EQ(thrd_create_result, thrd_success, "create blocking read thread"); shutdown(fds[0], SHUT_RD); ASSERT_EQ(thrd_join(t, NULL), thrd_success, "join blocking read thread"); EXPECT_EQ(recv_args.recv_result, 0, "recv should have returned 0"); EXPECT_EQ(recv_args.recv_errno, 0, "recv should have left errno alone"); END_TEST; } bool socketpair_shutdown_peer_wr_during_recv_test(void) { BEGIN_TEST; int fds[2]; int status = socketpair(AF_UNIX, SOCK_STREAM, 0, fds); ASSERT_EQ(status, 0, "socketpair(AF_UNIX, SOCK_STREAM, 0, fds) failed"); recv_args_t recv_args = {}; recv_args.fd = fds[0]; thrd_t t; int thrd_create_result = thrd_create(&t, recv_thread, &recv_args); ASSERT_EQ(thrd_create_result, thrd_success, "create blocking read thread"); shutdown(fds[1], SHUT_WR); ASSERT_EQ(thrd_join(t, NULL), thrd_success, "join blocking read thread"); EXPECT_EQ(recv_args.recv_result, 0, "recv should have returned 0"); EXPECT_EQ(recv_args.recv_errno, 0, "recv should have left errno alone"); END_TEST; } typedef struct send_args { int fd; int send_result; int send_errno; char buf[BUF_SIZE]; } send_args_t; int send_thread(void* arg) { send_args_t* send_args = (send_args_t*) arg; send_args->send_result = send(send_args->fd, send_args->buf, BUF_SIZE, 0u); if (send_args->send_result < 0) send_args->send_errno = errno; return 0; } bool socketpair_shutdown_self_wr_during_send_test(void) { BEGIN_TEST; int fds[2]; int status = socketpair(AF_UNIX, SOCK_STREAM, 0, fds); ASSERT_EQ(status, 0, "socketpair(AF_UNIX, SOCK_STREAM, 0, fds) failed"); // First, fill up the socket so the next send() will block. char buf[BUF_SIZE] = {}; while (true) { status = send(fds[0], buf, sizeof(buf), MSG_DONTWAIT); if (status < 0) { ASSERT_EQ(errno, EAGAIN, "send should eventually return EAGAIN when full"); break; } } send_args_t send_args = {}; send_args.fd = fds[0]; thrd_t t; // Then start a thread blocking on a send(). int thrd_create_result = thrd_create(&t, send_thread, &send_args); ASSERT_EQ(thrd_create_result, thrd_success, "create blocking send thread"); shutdown(fds[0], SHUT_WR); ASSERT_EQ(thrd_join(t, NULL), thrd_success, "join blocking send thread"); EXPECT_EQ(send_args.send_result, -1, "send should have returned -1"); EXPECT_EQ(send_args.send_errno, EPIPE, "send should have set errno to EPIPE"); END_TEST; } bool socketpair_shutdown_peer_rd_during_send_test(void) { BEGIN_TEST; int fds[2]; int status = socketpair(AF_UNIX, SOCK_STREAM, 0, fds); ASSERT_EQ(status, 0, "socketpair(AF_UNIX, SOCK_STREAM, 0, fds) failed"); // First, fill up the socket so the next send() will block. char buf[BUF_SIZE] = {}; while (true) { status = send(fds[0], buf, sizeof(buf), MSG_DONTWAIT); if (status < 0) { ASSERT_EQ(errno, EAGAIN, "send should eventually return EAGAIN when full"); break; } } send_args_t send_args = {}; send_args.fd = fds[0]; thrd_t t; int thrd_create_result = thrd_create(&t, send_thread, &send_args); ASSERT_EQ(thrd_create_result, thrd_success, "create blocking send thread"); shutdown(fds[1], SHUT_RD); ASSERT_EQ(thrd_join(t, NULL), thrd_success, "join blocking send thread"); EXPECT_EQ(send_args.send_result, -1, "send should have returned -1"); EXPECT_EQ(send_args.send_errno, EPIPE, "send should have set errno to EPIPE"); END_TEST; } bool socketpair_clone_or_unwrap_and_wrap_test(void) { BEGIN_TEST; int fds[2]; int status = socketpair(AF_UNIX, SOCK_STREAM, 0, fds); ASSERT_EQ(status, 0, "socketpair(AF_UNIX, SOCK_STREAM, 0, fds) failed"); zx_handle_t handles[FDIO_MAX_HANDLES]; uint32_t types[FDIO_MAX_HANDLES]; zx_status_t handle_count = fdio_clone_fd(fds[0], fds[0], handles, types); ASSERT_GT(handle_count, 0, "fdio_clone_fd() failed"); EXPECT_EQ(PA_HND_TYPE(types[0]), PA_FDIO_SOCKET, "Wrong cloned fd type"); int cloned_fd = -1; status = fdio_create_fd(handles, types, handle_count, &cloned_fd); EXPECT_EQ(status, 0, "fdio_create_fd(..., &cloned_fd) failed"); handle_count = fdio_transfer_fd(fds[0], fds[0], handles, types); ASSERT_GT(handle_count, 0, "fdio_transfer_fd() failed"); EXPECT_EQ(PA_HND_TYPE(types[0]), PA_FDIO_SOCKET, "Wrong transferred fd type"); int transferred_fd = -1; status = fdio_create_fd(handles, types, handle_count, &transferred_fd); EXPECT_EQ(status, 0, "fdio_create_fd(..., &transferred_fd) failed"); // Verify that an operation specific to socketpairs works on these fds. ASSERT_EQ(shutdown(cloned_fd, SHUT_RD), 0, "shutdown(cloned_fd, SHUT_RD) failed"); ASSERT_EQ(shutdown(transferred_fd, SHUT_WR), 0, "shutdown(transferred_fd, SHUT_WR) failed"); if (cloned_fd != -1) ASSERT_EQ(close(cloned_fd), 0, "Failed to close cloned_fd"); if (transferred_fd != -1) ASSERT_EQ(close(transferred_fd), 0, "Failed to close transferred_fd"); END_TEST; } BEGIN_TEST_CASE(fdio_socketpair_test) RUN_TEST(socketpair_test); RUN_TEST(socketpair_shutdown_rd_test); RUN_TEST(socketpair_shutdown_wr_test); RUN_TEST(socketpair_shutdown_rdwr_test); RUN_TEST(socketpair_shutdown_self_wr_poll_test); RUN_TEST(socketpair_shutdown_peer_wr_poll_test); RUN_TEST(socketpair_shutdown_self_rd_during_recv_test); RUN_TEST(socketpair_shutdown_peer_wr_during_recv_test); RUN_TEST(socketpair_shutdown_self_wr_during_send_test); RUN_TEST(socketpair_shutdown_peer_rd_during_send_test); RUN_TEST(socketpair_clone_or_unwrap_and_wrap_test); END_TEST_CASE(fdio_socketpair_test)