Merge branch 'af_unix-fix-bunch-of-msg_oob-bugs-and-add-new-tests'

Kuniyuki Iwashima says:

====================
af_unix: Fix bunch of MSG_OOB bugs and add new tests.

This series rewrites the selftest for AF_UNIX MSG_OOB and fixes
bunch of bugs that AF_UNIX behaves differently compared to TCP.

Note that the test discovered few more bugs in TCP side, which
will be fixed in another series.
====================

Link: https://lore.kernel.org/r/20240625013645.45034-1-kuniyu@amazon.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
Paolo Abeni 2024-06-27 12:05:04 +02:00
commit 3f4d9e4f82
5 changed files with 766 additions and 444 deletions

View File

@ -2613,10 +2613,24 @@ static struct sk_buff *manage_oob(struct sk_buff *skb, struct sock *sk,
{
struct unix_sock *u = unix_sk(sk);
if (!unix_skb_len(skb) && !(flags & MSG_PEEK)) {
skb_unlink(skb, &sk->sk_receive_queue);
consume_skb(skb);
skb = NULL;
if (!unix_skb_len(skb)) {
struct sk_buff *unlinked_skb = NULL;
spin_lock(&sk->sk_receive_queue.lock);
if (copied && (!u->oob_skb || skb == u->oob_skb)) {
skb = NULL;
} else if (flags & MSG_PEEK) {
skb = skb_peek_next(skb, &sk->sk_receive_queue);
} else {
unlinked_skb = skb;
skb = skb_peek_next(skb, &sk->sk_receive_queue);
__skb_unlink(unlinked_skb, &sk->sk_receive_queue);
}
spin_unlock(&sk->sk_receive_queue.lock);
consume_skb(unlinked_skb);
} else {
struct sk_buff *unlinked_skb = NULL;
@ -3093,12 +3107,23 @@ static int unix_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
#if IS_ENABLED(CONFIG_AF_UNIX_OOB)
case SIOCATMARK:
{
struct unix_sock *u = unix_sk(sk);
struct sk_buff *skb;
int answ = 0;
mutex_lock(&u->iolock);
skb = skb_peek(&sk->sk_receive_queue);
if (skb && skb == READ_ONCE(unix_sk(sk)->oob_skb))
answ = 1;
if (skb) {
struct sk_buff *oob_skb = READ_ONCE(u->oob_skb);
if (skb == oob_skb ||
(!oob_skb && !unix_skb_len(skb)))
answ = 1;
}
mutex_unlock(&u->iolock);
err = put_user(answ, (int __user *)arg);
}
break;

View File

@ -43,7 +43,6 @@ tap
tcp_fastopen_backup_key
tcp_inq
tcp_mmap
test_unix_oob
timestamping
tls
toeplitz

View File

@ -1,4 +1,4 @@
CFLAGS += $(KHDR_INCLUDES)
TEST_GEN_PROGS := diag_uid test_unix_oob unix_connect scm_pidfd scm_rights
TEST_GEN_PROGS := diag_uid msg_oob scm_pidfd scm_rights unix_connect
include ../../lib.mk

View File

@ -0,0 +1,734 @@
// 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/epoll.h>
#include <sys/ioctl.h>
#include <sys/signalfd.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
*/
int signal_fd;
int epoll_fd[2]; /* 0: AF_UNIX receiver
* 1: TCP receiver
*/
bool tcp_compliant;
};
FIXTURE_VARIANT(msg_oob)
{
bool peek;
};
FIXTURE_VARIANT_ADD(msg_oob, no_peek)
{
.peek = false,
};
FIXTURE_VARIANT_ADD(msg_oob, peek)
{
.peek = true
};
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 setup_sigurg(struct __test_metadata *_metadata,
FIXTURE_DATA(msg_oob) *self)
{
struct signalfd_siginfo siginfo;
int pid = getpid();
sigset_t mask;
int i, ret;
for (i = 0; i < 2; i++) {
ret = ioctl(self->fd[i * 2 + 1], FIOSETOWN, &pid);
ASSERT_EQ(ret, 0);
}
ret = sigemptyset(&mask);
ASSERT_EQ(ret, 0);
ret = sigaddset(&mask, SIGURG);
ASSERT_EQ(ret, 0);
ret = sigprocmask(SIG_BLOCK, &mask, NULL);
ASSERT_EQ(ret, 0);
self->signal_fd = signalfd(-1, &mask, SFD_NONBLOCK);
ASSERT_GE(self->signal_fd, 0);
ret = read(self->signal_fd, &siginfo, sizeof(siginfo));
ASSERT_EQ(ret, -1);
}
static void setup_epollpri(struct __test_metadata *_metadata,
FIXTURE_DATA(msg_oob) *self)
{
struct epoll_event event = {
.events = EPOLLPRI,
};
int i;
for (i = 0; i < 2; i++) {
int ret;
self->epoll_fd[i] = epoll_create1(0);
ASSERT_GE(self->epoll_fd[i], 0);
ret = epoll_ctl(self->epoll_fd[i], EPOLL_CTL_ADD, self->fd[i * 2 + 1], &event);
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);
setup_sigurg(_metadata, self);
setup_epollpri(_metadata, self);
self->tcp_compliant = true;
}
FIXTURE_TEARDOWN(msg_oob)
{
close_sockets(self);
}
static void __epollpair(struct __test_metadata *_metadata,
FIXTURE_DATA(msg_oob) *self,
bool oob_remaining)
{
struct epoll_event event[2] = {};
int i, ret[2];
for (i = 0; i < 2; i++)
ret[i] = epoll_wait(self->epoll_fd[i], &event[i], 1, 0);
ASSERT_EQ(ret[0], oob_remaining);
if (self->tcp_compliant)
ASSERT_EQ(ret[0], ret[1]);
if (oob_remaining) {
ASSERT_EQ(event[0].events, EPOLLPRI);
if (self->tcp_compliant)
ASSERT_EQ(event[0].events, event[1].events);
}
}
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++) {
struct signalfd_siginfo siginfo = {};
int bytes;
ret[i] = send(self->fd[i * 2], buf, len, flags);
bytes = read(self->signal_fd, &siginfo, sizeof(siginfo));
if (flags & MSG_OOB) {
ASSERT_EQ(bytes, sizeof(siginfo));
ASSERT_EQ(siginfo.ssi_signo, SIGURG);
bytes = read(self->signal_fd, &siginfo, sizeof(siginfo));
}
ASSERT_EQ(bytes, -1);
}
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] = {};
bool printed = false;
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]);
printed = true;
if (self->tcp_compliant) {
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) {
if (!printed) {
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]);
}
if (self->tcp_compliant)
ASSERT_EQ(cmp, 0);
}
}
}
static void __setinlinepair(struct __test_metadata *_metadata,
FIXTURE_DATA(msg_oob) *self)
{
int i, oob_inline = 1;
for (i = 0; i < 2; i++) {
int ret;
ret = setsockopt(self->fd[i * 2 + 1], SOL_SOCKET, SO_OOBINLINE,
&oob_inline, sizeof(oob_inline));
ASSERT_EQ(ret, 0);
}
}
static void __siocatmarkpair(struct __test_metadata *_metadata,
FIXTURE_DATA(msg_oob) *self,
bool oob_head)
{
int answ[2] = {};
int i;
for (i = 0; i < 2; i++) {
int ret;
ret = ioctl(self->fd[i * 2 + 1], SIOCATMARK, &answ[i]);
ASSERT_EQ(ret, 0);
}
ASSERT_EQ(answ[0], oob_head);
if (self->tcp_compliant)
ASSERT_EQ(answ[0], answ[1]);
}
#define sendpair(buf, len, flags) \
__sendpair(_metadata, self, buf, len, flags)
#define recvpair(expected_buf, expected_len, buf_len, flags) \
do { \
if (variant->peek) \
__recvpair(_metadata, self, \
expected_buf, expected_len, \
buf_len, (flags) | MSG_PEEK); \
__recvpair(_metadata, self, \
expected_buf, expected_len, buf_len, flags); \
} while (0)
#define epollpair(oob_remaining) \
__epollpair(_metadata, self, oob_remaining)
#define siocatmarkpair(oob_head) \
__siocatmarkpair(_metadata, self, oob_head)
#define setinlinepair() \
__setinlinepair(_metadata, self)
#define tcp_incompliant \
for (self->tcp_compliant = false; \
self->tcp_compliant == false; \
self->tcp_compliant = true)
TEST_F(msg_oob, non_oob)
{
sendpair("x", 1, 0);
epollpair(false);
siocatmarkpair(false);
recvpair("", -EINVAL, 1, MSG_OOB);
epollpair(false);
siocatmarkpair(false);
}
TEST_F(msg_oob, oob)
{
sendpair("x", 1, MSG_OOB);
epollpair(true);
siocatmarkpair(true);
recvpair("x", 1, 1, MSG_OOB);
epollpair(false);
siocatmarkpair(true);
}
TEST_F(msg_oob, oob_drop)
{
sendpair("x", 1, MSG_OOB);
epollpair(true);
siocatmarkpair(true);
recvpair("", -EAGAIN, 1, 0); /* Drop OOB. */
epollpair(false);
siocatmarkpair(false);
recvpair("", -EINVAL, 1, MSG_OOB);
epollpair(false);
siocatmarkpair(false);
}
TEST_F(msg_oob, oob_ahead)
{
sendpair("hello", 5, MSG_OOB);
epollpair(true);
siocatmarkpair(false);
recvpair("o", 1, 1, MSG_OOB);
epollpair(false);
siocatmarkpair(false);
recvpair("hell", 4, 4, 0);
epollpair(false);
siocatmarkpair(true);
}
TEST_F(msg_oob, oob_break)
{
sendpair("hello", 5, MSG_OOB);
epollpair(true);
siocatmarkpair(false);
recvpair("hell", 4, 5, 0); /* Break at OOB even with enough buffer. */
epollpair(true);
siocatmarkpair(true);
recvpair("o", 1, 1, MSG_OOB);
epollpair(false);
siocatmarkpair(true);
recvpair("", -EAGAIN, 1, 0);
siocatmarkpair(false);
}
TEST_F(msg_oob, oob_ahead_break)
{
sendpair("hello", 5, MSG_OOB);
epollpair(true);
siocatmarkpair(false);
sendpair("world", 5, 0);
epollpair(true);
siocatmarkpair(false);
recvpair("o", 1, 1, MSG_OOB);
epollpair(false);
siocatmarkpair(false);
recvpair("hell", 4, 9, 0); /* Break at OOB even after it's recv()ed. */
epollpair(false);
siocatmarkpair(true);
recvpair("world", 5, 5, 0);
epollpair(false);
siocatmarkpair(false);
}
TEST_F(msg_oob, oob_break_drop)
{
sendpair("hello", 5, MSG_OOB);
epollpair(true);
siocatmarkpair(false);
sendpair("world", 5, 0);
epollpair(true);
siocatmarkpair(false);
recvpair("hell", 4, 10, 0); /* Break at OOB even with enough buffer. */
epollpair(true);
siocatmarkpair(true);
recvpair("world", 5, 10, 0); /* Drop OOB and recv() the next skb. */
epollpair(false);
siocatmarkpair(false);
recvpair("", -EINVAL, 1, MSG_OOB);
epollpair(false);
siocatmarkpair(false);
}
TEST_F(msg_oob, ex_oob_break)
{
sendpair("hello", 5, MSG_OOB);
epollpair(true);
siocatmarkpair(false);
sendpair("wor", 3, MSG_OOB);
epollpair(true);
siocatmarkpair(false);
sendpair("ld", 2, 0);
epollpair(true);
siocatmarkpair(false);
recvpair("hellowo", 7, 10, 0); /* Break at OOB but not at ex-OOB. */
epollpair(true);
siocatmarkpair(true);
recvpair("r", 1, 1, MSG_OOB);
epollpair(false);
siocatmarkpair(true);
recvpair("ld", 2, 2, 0);
epollpair(false);
siocatmarkpair(false);
}
TEST_F(msg_oob, ex_oob_drop)
{
sendpair("x", 1, MSG_OOB);
epollpair(true);
siocatmarkpair(true);
sendpair("y", 1, MSG_OOB); /* TCP drops "x" at this moment. */
epollpair(true);
tcp_incompliant {
siocatmarkpair(false);
recvpair("x", 1, 1, 0); /* TCP drops "y" by passing through it. */
epollpair(true);
siocatmarkpair(true);
recvpair("y", 1, 1, MSG_OOB); /* TCP returns -EINVAL. */
epollpair(false);
siocatmarkpair(true);
}
}
TEST_F(msg_oob, ex_oob_drop_2)
{
sendpair("x", 1, MSG_OOB);
epollpair(true);
siocatmarkpair(true);
sendpair("y", 1, MSG_OOB); /* TCP drops "x" at this moment. */
epollpair(true);
tcp_incompliant {
siocatmarkpair(false);
}
recvpair("y", 1, 1, MSG_OOB);
epollpair(false);
tcp_incompliant {
siocatmarkpair(false);
recvpair("x", 1, 1, 0); /* TCP returns -EAGAIN. */
epollpair(false);
siocatmarkpair(true);
}
}
TEST_F(msg_oob, ex_oob_ahead_break)
{
sendpair("hello", 5, MSG_OOB);
epollpair(true);
siocatmarkpair(false);
sendpair("wor", 3, MSG_OOB);
epollpair(true);
siocatmarkpair(false);
recvpair("r", 1, 1, MSG_OOB);
epollpair(false);
siocatmarkpair(false);
sendpair("ld", 2, MSG_OOB);
epollpair(true);
siocatmarkpair(false);
tcp_incompliant {
recvpair("hellowol", 8, 10, 0); /* TCP recv()s "helloworl", why "r" ?? */
}
epollpair(true);
siocatmarkpair(true);
recvpair("d", 1, 1, MSG_OOB);
epollpair(false);
siocatmarkpair(true);
}
TEST_F(msg_oob, ex_oob_siocatmark)
{
sendpair("hello", 5, MSG_OOB);
epollpair(true);
siocatmarkpair(false);
recvpair("o", 1, 1, MSG_OOB);
epollpair(false);
siocatmarkpair(false);
sendpair("world", 5, MSG_OOB);
epollpair(true);
siocatmarkpair(false);
recvpair("hell", 4, 4, 0); /* Intentionally stop at ex-OOB. */
epollpair(true);
siocatmarkpair(false);
}
TEST_F(msg_oob, inline_oob)
{
setinlinepair();
sendpair("x", 1, MSG_OOB);
epollpair(true);
siocatmarkpair(true);
recvpair("", -EINVAL, 1, MSG_OOB);
epollpair(true);
siocatmarkpair(true);
recvpair("x", 1, 1, 0);
epollpair(false);
siocatmarkpair(false);
}
TEST_F(msg_oob, inline_oob_break)
{
setinlinepair();
sendpair("hello", 5, MSG_OOB);
epollpair(true);
siocatmarkpair(false);
recvpair("", -EINVAL, 1, MSG_OOB);
epollpair(true);
siocatmarkpair(false);
recvpair("hell", 4, 5, 0); /* Break at OOB but not at ex-OOB. */
epollpair(true);
siocatmarkpair(true);
recvpair("o", 1, 1, 0);
epollpair(false);
siocatmarkpair(false);
}
TEST_F(msg_oob, inline_oob_ahead_break)
{
sendpair("hello", 5, MSG_OOB);
epollpair(true);
siocatmarkpair(false);
sendpair("world", 5, 0);
epollpair(true);
siocatmarkpair(false);
recvpair("o", 1, 1, MSG_OOB);
epollpair(false);
siocatmarkpair(false);
setinlinepair();
recvpair("hell", 4, 9, 0); /* Break at OOB even with enough buffer. */
epollpair(false);
siocatmarkpair(true);
tcp_incompliant {
recvpair("world", 5, 6, 0); /* TCP recv()s "oworld", ... "o" ??? */
}
epollpair(false);
siocatmarkpair(false);
}
TEST_F(msg_oob, inline_ex_oob_break)
{
sendpair("hello", 5, MSG_OOB);
epollpair(true);
siocatmarkpair(false);
sendpair("wor", 3, MSG_OOB);
epollpair(true);
siocatmarkpair(false);
sendpair("ld", 2, 0);
epollpair(true);
siocatmarkpair(false);
setinlinepair();
recvpair("hellowo", 7, 10, 0); /* Break at OOB but not at ex-OOB. */
epollpair(true);
siocatmarkpair(true);
recvpair("rld", 3, 3, 0);
epollpair(false);
siocatmarkpair(false);
}
TEST_F(msg_oob, inline_ex_oob_no_drop)
{
sendpair("x", 1, MSG_OOB);
epollpair(true);
siocatmarkpair(true);
setinlinepair();
sendpair("y", 1, MSG_OOB); /* TCP does NOT drops "x" at this moment. */
epollpair(true);
siocatmarkpair(false);
recvpair("x", 1, 1, 0);
epollpair(true);
siocatmarkpair(true);
recvpair("y", 1, 1, 0);
epollpair(false);
siocatmarkpair(false);
}
TEST_F(msg_oob, inline_ex_oob_drop)
{
sendpair("x", 1, MSG_OOB);
epollpair(true);
siocatmarkpair(true);
sendpair("y", 1, MSG_OOB); /* TCP drops "x" at this moment. */
epollpair(true);
setinlinepair();
tcp_incompliant {
siocatmarkpair(false);
recvpair("x", 1, 1, 0); /* TCP recv()s "y". */
epollpair(true);
siocatmarkpair(true);
recvpair("y", 1, 1, 0); /* TCP returns -EAGAIN. */
epollpair(false);
siocatmarkpair(false);
}
}
TEST_F(msg_oob, inline_ex_oob_siocatmark)
{
sendpair("hello", 5, MSG_OOB);
epollpair(true);
siocatmarkpair(false);
recvpair("o", 1, 1, MSG_OOB);
epollpair(false);
siocatmarkpair(false);
setinlinepair();
sendpair("world", 5, MSG_OOB);
epollpair(true);
siocatmarkpair(false);
recvpair("hell", 4, 4, 0); /* Intentionally stop at ex-OOB. */
epollpair(true);
siocatmarkpair(false);
}
TEST_HARNESS_MAIN

View File

@ -1,436 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <netinet/tcp.h>
#include <sys/un.h>
#include <sys/signal.h>
#include <sys/poll.h>
static int pipefd[2];
static int signal_recvd;
static pid_t producer_id;
static char sock_name[32];
static void sig_hand(int sn, siginfo_t *si, void *p)
{
signal_recvd = sn;
}
static int set_sig_handler(int signal)
{
struct sigaction sa;
sa.sa_sigaction = sig_hand;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_SIGINFO | SA_RESTART;
return sigaction(signal, &sa, NULL);
}
static void set_filemode(int fd, int set)
{
int flags = fcntl(fd, F_GETFL, 0);
if (set)
flags &= ~O_NONBLOCK;
else
flags |= O_NONBLOCK;
fcntl(fd, F_SETFL, flags);
}
static void signal_producer(int fd)
{
char cmd;
cmd = 'S';
write(fd, &cmd, sizeof(cmd));
}
static void wait_for_signal(int fd)
{
char buf[5];
read(fd, buf, 5);
}
static void die(int status)
{
fflush(NULL);
unlink(sock_name);
kill(producer_id, SIGTERM);
exit(status);
}
int is_sioctatmark(int fd)
{
int ans = -1;
if (ioctl(fd, SIOCATMARK, &ans, sizeof(ans)) < 0) {
#ifdef DEBUG
perror("SIOCATMARK Failed");
#endif
}
return ans;
}
void read_oob(int fd, char *c)
{
*c = ' ';
if (recv(fd, c, sizeof(*c), MSG_OOB) < 0) {
#ifdef DEBUG
perror("Reading MSG_OOB Failed");
#endif
}
}
int read_data(int pfd, char *buf, int size)
{
int len = 0;
memset(buf, size, '0');
len = read(pfd, buf, size);
#ifdef DEBUG
if (len < 0)
perror("read failed");
#endif
return len;
}
static void wait_for_data(int pfd, int event)
{
struct pollfd pfds[1];
pfds[0].fd = pfd;
pfds[0].events = event;
poll(pfds, 1, -1);
}
void producer(struct sockaddr_un *consumer_addr)
{
int cfd;
char buf[64];
int i;
memset(buf, 'x', sizeof(buf));
cfd = socket(AF_UNIX, SOCK_STREAM, 0);
wait_for_signal(pipefd[0]);
if (connect(cfd, (struct sockaddr *)consumer_addr,
sizeof(*consumer_addr)) != 0) {
perror("Connect failed");
kill(0, SIGTERM);
exit(1);
}
for (i = 0; i < 2; i++) {
/* Test 1: Test for SIGURG and OOB */
wait_for_signal(pipefd[0]);
memset(buf, 'x', sizeof(buf));
buf[63] = '@';
send(cfd, buf, sizeof(buf), MSG_OOB);
wait_for_signal(pipefd[0]);
/* Test 2: Test for OOB being overwitten */
memset(buf, 'x', sizeof(buf));
buf[63] = '%';
send(cfd, buf, sizeof(buf), MSG_OOB);
memset(buf, 'x', sizeof(buf));
buf[63] = '#';
send(cfd, buf, sizeof(buf), MSG_OOB);
wait_for_signal(pipefd[0]);
/* Test 3: Test for SIOCATMARK */
memset(buf, 'x', sizeof(buf));
buf[63] = '@';
send(cfd, buf, sizeof(buf), MSG_OOB);
memset(buf, 'x', sizeof(buf));
buf[63] = '%';
send(cfd, buf, sizeof(buf), MSG_OOB);
memset(buf, 'x', sizeof(buf));
send(cfd, buf, sizeof(buf), 0);
wait_for_signal(pipefd[0]);
/* Test 4: Test for 1byte OOB msg */
memset(buf, 'x', sizeof(buf));
buf[0] = '@';
send(cfd, buf, 1, MSG_OOB);
}
}
int
main(int argc, char **argv)
{
int lfd, pfd;
struct sockaddr_un consumer_addr, paddr;
socklen_t len = sizeof(consumer_addr);
char buf[1024];
int on = 0;
char oob;
int atmark;
lfd = socket(AF_UNIX, SOCK_STREAM, 0);
memset(&consumer_addr, 0, sizeof(consumer_addr));
consumer_addr.sun_family = AF_UNIX;
sprintf(sock_name, "unix_oob_%d", getpid());
unlink(sock_name);
strcpy(consumer_addr.sun_path, sock_name);
if ((bind(lfd, (struct sockaddr *)&consumer_addr,
sizeof(consumer_addr))) != 0) {
perror("socket bind failed");
exit(1);
}
pipe(pipefd);
listen(lfd, 1);
producer_id = fork();
if (producer_id == 0) {
producer(&consumer_addr);
exit(0);
}
set_sig_handler(SIGURG);
signal_producer(pipefd[1]);
pfd = accept(lfd, (struct sockaddr *) &paddr, &len);
fcntl(pfd, F_SETOWN, getpid());
signal_recvd = 0;
signal_producer(pipefd[1]);
/* Test 1:
* veriyf that SIGURG is
* delivered, 63 bytes are
* read, oob is '@', and POLLPRI works.
*/
wait_for_data(pfd, POLLPRI);
read_oob(pfd, &oob);
len = read_data(pfd, buf, 1024);
if (!signal_recvd || len != 63 || oob != '@') {
fprintf(stderr, "Test 1 failed sigurg %d len %d %c\n",
signal_recvd, len, oob);
die(1);
}
signal_recvd = 0;
signal_producer(pipefd[1]);
/* Test 2:
* Verify that the first OOB is over written by
* the 2nd one and the first OOB is returned as
* part of the read, and sigurg is received.
*/
wait_for_data(pfd, POLLIN | POLLPRI);
len = 0;
while (len < 70)
len = recv(pfd, buf, 1024, MSG_PEEK);
len = read_data(pfd, buf, 1024);
read_oob(pfd, &oob);
if (!signal_recvd || len != 127 || oob != '#') {
fprintf(stderr, "Test 2 failed, sigurg %d len %d OOB %c\n",
signal_recvd, len, oob);
die(1);
}
signal_recvd = 0;
signal_producer(pipefd[1]);
/* Test 3:
* verify that 2nd oob over writes
* the first one and read breaks at
* oob boundary returning 127 bytes
* and sigurg is received and atmark
* is set.
* oob is '%' and second read returns
* 64 bytes.
*/
len = 0;
wait_for_data(pfd, POLLIN | POLLPRI);
while (len < 150)
len = recv(pfd, buf, 1024, MSG_PEEK);
len = read_data(pfd, buf, 1024);
atmark = is_sioctatmark(pfd);
read_oob(pfd, &oob);
if (!signal_recvd || len != 127 || oob != '%' || atmark != 1) {
fprintf(stderr,
"Test 3 failed, sigurg %d len %d OOB %c atmark %d\n",
signal_recvd, len, oob, atmark);
die(1);
}
signal_recvd = 0;
len = read_data(pfd, buf, 1024);
if (len != 64) {
fprintf(stderr, "Test 3.1 failed, sigurg %d len %d OOB %c\n",
signal_recvd, len, oob);
die(1);
}
signal_recvd = 0;
signal_producer(pipefd[1]);
/* Test 4:
* verify that a single byte
* oob message is delivered.
* set non blocking mode and
* check proper error is
* returned and sigurg is
* received and correct
* oob is read.
*/
set_filemode(pfd, 0);
wait_for_data(pfd, POLLIN | POLLPRI);
len = read_data(pfd, buf, 1024);
if ((len == -1) && (errno == 11))
len = 0;
read_oob(pfd, &oob);
if (!signal_recvd || len != 0 || oob != '@') {
fprintf(stderr, "Test 4 failed, sigurg %d len %d OOB %c\n",
signal_recvd, len, oob);
die(1);
}
set_filemode(pfd, 1);
/* Inline Testing */
on = 1;
if (setsockopt(pfd, SOL_SOCKET, SO_OOBINLINE, &on, sizeof(on))) {
perror("SO_OOBINLINE");
die(1);
}
signal_recvd = 0;
signal_producer(pipefd[1]);
/* Test 1 -- Inline:
* Check that SIGURG is
* delivered and 63 bytes are
* read and oob is '@'
*/
wait_for_data(pfd, POLLIN | POLLPRI);
len = read_data(pfd, buf, 1024);
if (!signal_recvd || len != 63) {
fprintf(stderr, "Test 1 Inline failed, sigurg %d len %d\n",
signal_recvd, len);
die(1);
}
len = read_data(pfd, buf, 1024);
if (len != 1) {
fprintf(stderr,
"Test 1.1 Inline failed, sigurg %d len %d oob %c\n",
signal_recvd, len, oob);
die(1);
}
signal_recvd = 0;
signal_producer(pipefd[1]);
/* Test 2 -- Inline:
* Verify that the first OOB is over written by
* the 2nd one and read breaks correctly on
* 2nd OOB boundary with the first OOB returned as
* part of the read, and sigurg is delivered and
* siocatmark returns true.
* next read returns one byte, the oob byte
* and siocatmark returns false.
*/
len = 0;
wait_for_data(pfd, POLLIN | POLLPRI);
while (len < 70)
len = recv(pfd, buf, 1024, MSG_PEEK);
len = read_data(pfd, buf, 1024);
atmark = is_sioctatmark(pfd);
if (len != 127 || atmark != 1 || !signal_recvd) {
fprintf(stderr, "Test 2 Inline failed, len %d atmark %d\n",
len, atmark);
die(1);
}
len = read_data(pfd, buf, 1024);
atmark = is_sioctatmark(pfd);
if (len != 1 || buf[0] != '#' || atmark == 1) {
fprintf(stderr, "Test 2.1 Inline failed, len %d data %c atmark %d\n",
len, buf[0], atmark);
die(1);
}
signal_recvd = 0;
signal_producer(pipefd[1]);
/* Test 3 -- Inline:
* verify that 2nd oob over writes
* the first one and read breaks at
* oob boundary returning 127 bytes
* and sigurg is received and siocatmark
* is true after the read.
* subsequent read returns 65 bytes
* because of oob which should be '%'.
*/
len = 0;
wait_for_data(pfd, POLLIN | POLLPRI);
while (len < 126)
len = recv(pfd, buf, 1024, MSG_PEEK);
len = read_data(pfd, buf, 1024);
atmark = is_sioctatmark(pfd);
if (!signal_recvd || len != 127 || !atmark) {
fprintf(stderr,
"Test 3 Inline failed, sigurg %d len %d data %c\n",
signal_recvd, len, buf[0]);
die(1);
}
len = read_data(pfd, buf, 1024);
atmark = is_sioctatmark(pfd);
if (len != 65 || buf[0] != '%' || atmark != 0) {
fprintf(stderr,
"Test 3.1 Inline failed, len %d oob %c atmark %d\n",
len, buf[0], atmark);
die(1);
}
signal_recvd = 0;
signal_producer(pipefd[1]);
/* Test 4 -- Inline:
* verify that a single
* byte oob message is delivered
* and read returns one byte, the oob
* byte and sigurg is received
*/
wait_for_data(pfd, POLLIN | POLLPRI);
len = read_data(pfd, buf, 1024);
if (!signal_recvd || len != 1 || buf[0] != '@') {
fprintf(stderr,
"Test 4 Inline failed, signal %d len %d data %c\n",
signal_recvd, len, buf[0]);
die(1);
}
die(0);
}