1 // Copyright 2018 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 #include <assert.h>
5 #include <fcntl.h>
6 #include <sys/socket.h>
7 #include <unistd.h>
8 
9 #include <lib/fdio/util.h>
10 #include <unittest/unittest.h>
11 #include <zircon/processargs.h>
12 #include <zircon/syscalls.h>
13 #include <zircon/types.h>
14 
create_socket_fdio_pair(zx_handle_t * socket_out,int * fd_out)15 static bool create_socket_fdio_pair(zx_handle_t* socket_out, int* fd_out) {
16     // Create new socket pair.
17     zx_handle_t s1, s2;
18     ASSERT_EQ(ZX_OK, zx_socket_create(ZX_SOCKET_STREAM | ZX_SOCKET_HAS_CONTROL, &s1, &s2), "Socket create failed");
19 
20     // Convert one socket to FDIO
21     uint32_t type = PA_FDIO_SOCKET;
22     int fd;
23     ASSERT_EQ(ZX_OK, fdio_create_fd(&s2, &type, 1, &fd), "Socket from handle failed");
24 
25     *fd_out = fd;
26     *socket_out = s1;
27 
28     return true;
29 }
30 
set_nonblocking_io(int fd)31 static bool set_nonblocking_io(int fd) {
32     int flags = fcntl(fd, F_GETFL);
33     EXPECT_NE(-1, flags, "fcntl failed");
34     EXPECT_NE(-1, fcntl(fd, F_SETFL, flags | O_NONBLOCK), "Set NONBLOCK failed");
35     return true;
36 }
37 
38 // Verify scenario, where multi-segment recvmsg is requested, but the socket has
39 // just enough data to *completely* fill one segment.
40 // In this scenario, an attempt to read data for the next segment immediately
41 // fails with ZX_ERR_SHOULD_WAIT, and this may lead to bogus EAGAIN even if some
42 // data has actually been read.
socket_recvmsg_nonblock_boundary_test(void)43 bool socket_recvmsg_nonblock_boundary_test(void) {
44     BEGIN_TEST;
45 
46     zx_handle_t s;
47     int fd;
48 
49     if (!create_socket_fdio_pair(&s, &fd) || !set_nonblocking_io(fd)) {
50         return false;
51     }
52 
53     // Write 4 bytes of data to socket.
54     size_t actual;
55     const uint32_t data_out = 0x12345678;
56     EXPECT_EQ(ZX_OK, zx_socket_write(s, 0, &data_out, sizeof(data_out), &actual), "Socket write failed");
57     EXPECT_EQ(sizeof(data_out), actual, "Socket write length mismatch");
58 
59     uint32_t data_in1, data_in2;
60     // Fail at compilation stage if anyone changes types.
61     // This is mandatory here: we need the first chunk to be exactly the same
62     // length as total size of data we just wrote.
63     assert(sizeof(data_in1) == sizeof(data_out));
64 
65     struct iovec iov[2];
66     iov[0].iov_base = &data_in1;
67     iov[0].iov_len = sizeof(data_in1);
68     iov[1].iov_base = &data_in2;
69     iov[1].iov_len = sizeof(data_in2);
70 
71     struct msghdr msg;
72     msg.msg_name = NULL;
73     msg.msg_namelen = 0;
74     msg.msg_iov = iov;
75     msg.msg_iovlen = sizeof(iov) / sizeof(*iov);
76     msg.msg_control = NULL;
77     msg.msg_controllen = 0;
78     msg.msg_flags = 0;
79 
80     actual = recvmsg(fd, &msg, 0);
81     EXPECT_EQ(4u, actual, "Socket read failed");
82 
83     zx_handle_close(s);
84     close(fd);
85     END_TEST;
86 }
87 
88 // Verify scenario, where multi-segment sendmsg is requested, but the socket has
89 // just enough spare buffer to *completely* read one segment.
90 // In this scenario, an attempt to send second segment should immediately fail
91 // with ZX_ERR_SHOULD_WAIT, but the sendmsg should report first segment length
92 // rather than failing with EAGAIN.
socket_sendmsg_nonblock_boundary_test(void)93 bool socket_sendmsg_nonblock_boundary_test(void) {
94     BEGIN_TEST;
95 
96     const size_t memlength = 65536;
97     void* memchunk = malloc(memlength);
98 
99     struct iovec iov[2];
100     iov[0].iov_base = memchunk;
101     iov[0].iov_len = memlength;
102     iov[1].iov_base = memchunk;
103     iov[1].iov_len = memlength;
104 
105     zx_handle_t s;
106     int fd;
107 
108     if (!create_socket_fdio_pair(&s, &fd) || !set_nonblocking_io(fd)) {
109         return false;
110     }
111 
112     struct msghdr msg;
113     msg.msg_name = NULL;
114     msg.msg_namelen = 0;
115     msg.msg_iov = iov;
116     msg.msg_iovlen = sizeof(iov) / sizeof(*iov);
117     msg.msg_control = NULL;
118     msg.msg_controllen = 0;
119     msg.msg_flags = 0;
120 
121     // 1. Keep sending data until socket can take no more.
122     while (sendmsg(fd, &msg, 0) > 0)
123         ;
124 
125     // 2. Consume one segment of the data
126     size_t actual = 0;
127     zx_socket_read(s, 0, memchunk, memlength, &actual);
128     EXPECT_EQ(memlength, actual, "Failed to read from a full socket");
129 
130     // 3. Push again 2 packets of <memlength> bytes, observe only one sent.
131     EXPECT_EQ((ssize_t)memlength, sendmsg(fd, &msg, 0),
132               "Partial sendmsg failed; is the socket buffer varying?");
133 
134     zx_handle_close(s);
135     close(fd);
136     free(memchunk);
137     END_TEST;
138 }
139 
140 BEGIN_TEST_CASE(newsocket_tests)
141 RUN_TEST(socket_recvmsg_nonblock_boundary_test)
142 RUN_TEST(socket_sendmsg_nonblock_boundary_test)
143 END_TEST_CASE(newsocket_tests)
144