]> www.infradead.org Git - users/hch/dma-mapping.git/commitdiff
selftest: af_unix: Add msg_oob.c.
authorKuniyuki Iwashima <kuniyu@amazon.com>
Tue, 25 Jun 2024 01:36:36 +0000 (18:36 -0700)
committerPaolo Abeni <pabeni@redhat.com>
Thu, 27 Jun 2024 10:05:00 +0000 (12:05 +0200)
AF_UNIX's MSG_OOB functionality lacked thorough testing, and we found
some bizarre behaviour.

The new selftest validates every MSG_OOB operation against TCP as a
reference implementation.

This patch adds only a few tests with basic send() and recv() that
do not fail.

The following patches will add more test cases for SO_OOBINLINE, SIGURG,
EPOLLPRI, and SIOCATMARK.

Signed-off-by: Kuniyuki Iwashima <kuniyu@amazon.com>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
tools/testing/selftests/net/af_unix/Makefile
tools/testing/selftests/net/af_unix/msg_oob.c [new file with mode: 0644]

index a25845251eed7d7dac9620f9996031c839110de5..50584479540b0998b08dc5c1ba963554f78a2a8c 100644 (file)
@@ -1,4 +1,4 @@
 CFLAGS += $(KHDR_INCLUDES)
-TEST_GEN_PROGS := diag_uid scm_pidfd scm_rights unix_connect
+TEST_GEN_PROGS := diag_uid msg_oob scm_pidfd scm_rights unix_connect
 
 include ../../lib.mk
diff --git a/tools/testing/selftests/net/af_unix/msg_oob.c b/tools/testing/selftests/net/af_unix/msg_oob.c
new file mode 100644 (file)
index 0000000..d427d39
--- /dev/null
@@ -0,0 +1,220 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright Amazon.com Inc. or its affiliates. */
+
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <netinet/in.h>
+#include <sys/socket.h>
+
+#include "../../kselftest_harness.h"
+
+#define BUF_SZ 32
+
+FIXTURE(msg_oob)
+{
+       int fd[4];              /* 0: AF_UNIX sender
+                                * 1: AF_UNIX receiver
+                                * 2: TCP sender
+                                * 3: TCP receiver
+                                */
+};
+
+static void create_unix_socketpair(struct __test_metadata *_metadata,
+                                  FIXTURE_DATA(msg_oob) *self)
+{
+       int ret;
+
+       ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0, self->fd);
+       ASSERT_EQ(ret, 0);
+}
+
+static void create_tcp_socketpair(struct __test_metadata *_metadata,
+                                 FIXTURE_DATA(msg_oob) *self)
+{
+       struct sockaddr_in addr;
+       socklen_t addrlen;
+       int listen_fd;
+       int ret;
+
+       listen_fd = socket(AF_INET, SOCK_STREAM, 0);
+       ASSERT_GE(listen_fd, 0);
+
+       ret = listen(listen_fd, -1);
+       ASSERT_EQ(ret, 0);
+
+       addrlen = sizeof(addr);
+       ret = getsockname(listen_fd, (struct sockaddr *)&addr, &addrlen);
+       ASSERT_EQ(ret, 0);
+
+       self->fd[2] = socket(AF_INET, SOCK_STREAM, 0);
+       ASSERT_GE(self->fd[2], 0);
+
+       ret = connect(self->fd[2], (struct sockaddr *)&addr, addrlen);
+       ASSERT_EQ(ret, 0);
+
+       self->fd[3] = accept(listen_fd, (struct sockaddr *)&addr, &addrlen);
+       ASSERT_GE(self->fd[3], 0);
+
+       ret = fcntl(self->fd[3], F_SETFL, O_NONBLOCK);
+       ASSERT_EQ(ret, 0);
+}
+
+static void close_sockets(FIXTURE_DATA(msg_oob) *self)
+{
+       int i;
+
+       for (i = 0; i < 4; i++)
+               close(self->fd[i]);
+}
+
+FIXTURE_SETUP(msg_oob)
+{
+       create_unix_socketpair(_metadata, self);
+       create_tcp_socketpair(_metadata, self);
+}
+
+FIXTURE_TEARDOWN(msg_oob)
+{
+       close_sockets(self);
+}
+
+static void __sendpair(struct __test_metadata *_metadata,
+                      FIXTURE_DATA(msg_oob) *self,
+                      const void *buf, size_t len, int flags)
+{
+       int i, ret[2];
+
+       for (i = 0; i < 2; i++)
+               ret[i] = send(self->fd[i * 2], buf, len, flags);
+
+       ASSERT_EQ(ret[0], len);
+       ASSERT_EQ(ret[0], ret[1]);
+}
+
+static void __recvpair(struct __test_metadata *_metadata,
+                      FIXTURE_DATA(msg_oob) *self,
+                      const void *expected_buf, int expected_len,
+                      int buf_len, int flags)
+{
+       int i, ret[2], recv_errno[2], expected_errno = 0;
+       char recv_buf[2][BUF_SZ] = {};
+
+       ASSERT_GE(BUF_SZ, buf_len);
+
+       errno = 0;
+
+       for (i = 0; i < 2; i++) {
+               ret[i] = recv(self->fd[i * 2 + 1], recv_buf[i], buf_len, flags);
+               recv_errno[i] = errno;
+       }
+
+       if (expected_len < 0) {
+               expected_errno = -expected_len;
+               expected_len = -1;
+       }
+
+       if (ret[0] != expected_len || recv_errno[0] != expected_errno) {
+               TH_LOG("AF_UNIX :%s", ret[0] < 0 ? strerror(recv_errno[0]) : recv_buf[0]);
+               TH_LOG("Expected:%s", expected_errno ? strerror(expected_errno) : expected_buf);
+
+               ASSERT_EQ(ret[0], expected_len);
+               ASSERT_EQ(recv_errno[0], expected_errno);
+       }
+
+       if (ret[0] != ret[1] || recv_errno[0] != recv_errno[1]) {
+               TH_LOG("AF_UNIX :%s", ret[0] < 0 ? strerror(recv_errno[0]) : recv_buf[0]);
+               TH_LOG("TCP     :%s", ret[1] < 0 ? strerror(recv_errno[1]) : recv_buf[1]);
+
+               ASSERT_EQ(ret[0], ret[1]);
+               ASSERT_EQ(recv_errno[0], recv_errno[1]);
+       }
+
+       if (expected_len >= 0) {
+               int cmp;
+
+               cmp = strncmp(expected_buf, recv_buf[0], expected_len);
+               if (cmp) {
+                       TH_LOG("AF_UNIX :%s", ret[0] < 0 ? strerror(recv_errno[0]) : recv_buf[0]);
+                       TH_LOG("Expected:%s", expected_errno ? strerror(expected_errno) : expected_buf);
+
+                       ASSERT_EQ(cmp, 0);
+               }
+
+               cmp = strncmp(recv_buf[0], recv_buf[1], expected_len);
+               if (cmp) {
+                       TH_LOG("AF_UNIX :%s", ret[0] < 0 ? strerror(recv_errno[0]) : recv_buf[0]);
+                       TH_LOG("TCP     :%s", ret[1] < 0 ? strerror(recv_errno[1]) : recv_buf[1]);
+
+                       ASSERT_EQ(cmp, 0);
+               }
+       }
+}
+
+#define sendpair(buf, len, flags)                                      \
+       __sendpair(_metadata, self, buf, len, flags)
+
+#define recvpair(expected_buf, expected_len, buf_len, flags)           \
+       __recvpair(_metadata, self,                                     \
+                  expected_buf, expected_len, buf_len, flags)
+
+TEST_F(msg_oob, non_oob)
+{
+       sendpair("x", 1, 0);
+
+       recvpair("", -EINVAL, 1, MSG_OOB);
+}
+
+TEST_F(msg_oob, oob)
+{
+       sendpair("x", 1, MSG_OOB);
+
+       recvpair("x", 1, 1, MSG_OOB);
+}
+
+TEST_F(msg_oob, oob_drop)
+{
+       sendpair("x", 1, MSG_OOB);
+
+       recvpair("", -EAGAIN, 1, 0);            /* Drop OOB. */
+       recvpair("", -EINVAL, 1, MSG_OOB);
+}
+
+TEST_F(msg_oob, oob_ahead)
+{
+       sendpair("hello", 5, MSG_OOB);
+
+       recvpair("o", 1, 1, MSG_OOB);
+       recvpair("hell", 4, 4, 0);
+}
+
+TEST_F(msg_oob, oob_break)
+{
+       sendpair("hello", 5, MSG_OOB);
+
+       recvpair("hell", 4, 5, 0);              /* Break at OOB even with enough buffer. */
+       recvpair("o", 1, 1, MSG_OOB);
+}
+
+TEST_F(msg_oob, oob_ahead_break)
+{
+       sendpair("hello", 5, MSG_OOB);
+       sendpair("world", 5, 0);
+
+       recvpair("o", 1, 1, MSG_OOB);
+       recvpair("hell", 4, 9, 0);              /* Break at OOB even after it's recv()ed. */
+       recvpair("world", 5, 5, 0);
+}
+
+TEST_F(msg_oob, oob_break_drop)
+{
+       sendpair("hello", 5, MSG_OOB);
+       sendpair("world", 5, 0);
+
+       recvpair("hell", 4, 10, 0);             /* Break at OOB even with enough buffer. */
+       recvpair("world", 5, 10, 0);            /* Drop OOB and recv() the next skb. */
+       recvpair("", -EINVAL, 1, MSG_OOB);
+}
+
+TEST_HARNESS_MAIN