[LTP] [PATCH v3] syscalls/sendmmsg: add new test
Cyril Hrubis
chrubis@suse.cz
Wed Jul 24 15:42:25 CEST 2019
Hi!
> Test basic functionality of sendmmsg and recvmmsg.
>
> Signed-off-by: Steve Muckle <smuckle@google.com>
> ---
>
> Changes since v2:
> - fix race between sender and receiver
> - use TST_GET_UNUSED_PORT
> - fix checkpatch error (it still complains about the // on the SPDX
> identifier in sendmmsg_var.h, but other headers in this project
> use that style)
> - add SPDX identifier in sendmmsg01.c
>
> Changes since v1:
> - rebased on recent autotools cleanup for libc func checks
> - used test_variant to test direct syscalls and libc syscalls
> - use tst_syscall instead of ltp_syscall
> - check that both messages have been sent, retry sending messages if
> necessary
> - use tst_brk(TBROK... if sendmmsg fails so recv thread is not kept
> waiting
>
> configure.ac | 3 +
> include/lapi/socket.h | 8 +
> runtest/syscalls | 2 +
> testcases/kernel/syscalls/sendmmsg/.gitignore | 1 +
> testcases/kernel/syscalls/sendmmsg/Makefile | 9 +
> .../kernel/syscalls/sendmmsg/sendmmsg01.c | 162 ++++++++++++++++++
> .../kernel/syscalls/sendmmsg/sendmmsg_var.h | 60 +++++++
> 7 files changed, 245 insertions(+)
> create mode 100644 testcases/kernel/syscalls/sendmmsg/.gitignore
> create mode 100644 testcases/kernel/syscalls/sendmmsg/Makefile
> create mode 100644 testcases/kernel/syscalls/sendmmsg/sendmmsg01.c
> create mode 100644 testcases/kernel/syscalls/sendmmsg/sendmmsg_var.h
>
> diff --git a/configure.ac b/configure.ac
> index 3dcf282e8..5e4e7f1f9 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -82,9 +82,11 @@ AC_CHECK_FUNCS([ \
> pwritev \
> pwritev2 \
> readlinkat \
> + recvmmsg \
> renameat \
> renameat2 \
> sched_getcpu \
> + sendmmsg \
> sigpending \
> splice \
> stime \
> @@ -253,6 +255,7 @@ LTP_CHECK_TIME
> LTP_CHECK_TIMERFD
> test "x$with_tirpc" = xyes && LTP_CHECK_TIRPC
> LTP_CHECK_TPACKET_V3
> +LTP_CHECK_MMSGHDR
This seems to be already there under the # custom functions comment.
Also there is mmsghdr definition in the cve-2016-7117.c we should
include the lapi/socket.h there later on...
> LTP_CHECK_UNAME_DOMAINNAME
> LTP_CHECK_XFS_QUOTACTL
> LTP_CHECK_X_TABLES
> diff --git a/include/lapi/socket.h b/include/lapi/socket.h
> index 2605443e8..6d9e9fe30 100644
> --- a/include/lapi/socket.h
> +++ b/include/lapi/socket.h
> @@ -19,6 +19,7 @@
> #ifndef __LAPI_SOCKET_H__
> #define __LAPI_SOCKET_H__
>
> +#include "config.h"
> #include <sys/socket.h>
>
> #ifndef MSG_ZEROCOPY
> @@ -69,4 +70,11 @@
> # define SOL_ALG 279
> #endif
>
> +#ifndef HAVE_STRUCT_MMSGHDR
> +struct mmsghdr {
> + struct msghdr msg_hdr;
> + unsigned int msg_len;
> +};
> +#endif
> +
> #endif /* __LAPI_SOCKET_H__ */
> diff --git a/runtest/syscalls b/runtest/syscalls
> index 67dfed661..1e79e41c3 100644
> --- a/runtest/syscalls
> +++ b/runtest/syscalls
> @@ -1117,6 +1117,8 @@ sendfile09_64 sendfile09_64
> sendmsg01 sendmsg01
> sendmsg02 sendmsg02
>
> +sendmmsg01 sendmmsg01
> +
> sendto01 sendto01
> sendto02 sendto02
>
> diff --git a/testcases/kernel/syscalls/sendmmsg/.gitignore b/testcases/kernel/syscalls/sendmmsg/.gitignore
> new file mode 100644
> index 000000000..b703ececd
> --- /dev/null
> +++ b/testcases/kernel/syscalls/sendmmsg/.gitignore
> @@ -0,0 +1 @@
> +sendmmsg01
> diff --git a/testcases/kernel/syscalls/sendmmsg/Makefile b/testcases/kernel/syscalls/sendmmsg/Makefile
> new file mode 100644
> index 000000000..47e063722
> --- /dev/null
> +++ b/testcases/kernel/syscalls/sendmmsg/Makefile
> @@ -0,0 +1,9 @@
> +# SPDX-License-Identifier: GPL-2.0-or-later
> +
> +top_srcdir ?= ../../../..
> +
> +sendmmsg01: CFLAGS+=-pthread
> +
> +include $(top_srcdir)/include/mk/testcases.mk
> +
> +include $(top_srcdir)/include/mk/generic_leaf_target.mk
> diff --git a/testcases/kernel/syscalls/sendmmsg/sendmmsg01.c b/testcases/kernel/syscalls/sendmmsg/sendmmsg01.c
> new file mode 100644
> index 000000000..0786200a3
> --- /dev/null
> +++ b/testcases/kernel/syscalls/sendmmsg/sendmmsg01.c
> @@ -0,0 +1,162 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * This test is based on source contained in the man pages for sendmmsg and
> + * recvmmsg in release 4.15 of the Linux man-pages project.
> + */
> +
> +#define _GNU_SOURCE
> +#include <netinet/ip.h>
> +#include <semaphore.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <sys/types.h>
> +
> +#include "tst_test.h"
> +#include "lapi/socket.h"
> +#include "tst_safe_macros.h"
> +#include "tst_safe_pthread.h"
> +
> +#include "sendmmsg_var.h"
> +
> +#define BUFSIZE 16
> +#define VLEN 2
> +
> +static sem_t send_sem;
> +static unsigned int port;
> +
> +static void *sender_thread(LTP_ATTRIBUTE_UNUSED void *arg)
> +{
> + struct sockaddr_in addr;
> + struct mmsghdr msg[VLEN];
> + struct iovec msg1[2], msg2;
> + int send_sockfd;
> + int msgs = VLEN;
> + int retval;
> +
> + send_sockfd = SAFE_SOCKET(AF_INET, SOCK_DGRAM, 0);
> +
> + addr.sin_family = AF_INET;
> + addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
> + addr.sin_port = htons(port);
The port returned by TST_GET_UNUSED_PORT() is already in network byte
order, we found a bug recently where test was failing randomly since if
we attempt to convert the value we may end up with priviledged port
number if we are unlucky.
> + SAFE_CONNECT(send_sockfd, (struct sockaddr *) &addr, sizeof(addr));
> +
> + memset(msg1, 0, sizeof(msg1));
> + msg1[0].iov_base = "one";
> + msg1[0].iov_len = 3;
> + msg1[1].iov_base = "two";
> + msg1[1].iov_len = 3;
> +
> + memset(&msg2, 0, sizeof(msg2));
> + msg2.iov_base = "three";
> + msg2.iov_len = 5;
> +
> + memset(msg, 0, sizeof(msg));
> + msg[0].msg_hdr.msg_iov = msg1;
> + msg[0].msg_hdr.msg_iovlen = 2;
> +
> + msg[1].msg_hdr.msg_iov = &msg2;
> + msg[1].msg_hdr.msg_iovlen = 1;
> +
> + sem_wait(&send_sem);
> +
> + while (msgs) {
> + retval = do_sendmmsg(send_sockfd, msg, msgs, 0);
> + if (retval < 0) {
> + /*
> + * tst_brk is used here so reader is not left waiting
> + * for data - note timeout for recvmmsg does not work
> + * as one would expect (see man page)
> + */
> + tst_brk(TBROK|TTERRNO, "sendmmsg failed");
> + goto out;
> + }
> + msgs -= retval;
Wouldn't this resend the start of the message again if we got
interrupted in the middle?
I guess that the correct retry would loop over the messages and
decrement the iov_len and shift the buffers based on the msg_len.
Something as:
retry:
retval = do_sendmmsg(send_sockfd, msg, msgs, 0);
for (i = 0; i < retval; i++) {
int transmitted = msg[i].msg_len;
msg[i].msg_len = 0;
for (j = 0; j < msg[i].msg_iovlen; j++) {
int len = msg[i].msg_iov[j].msg_iovlen;
/* whole buffer send */
if (transmitted >= len) {
transmitted -= len;
msg[i].msg_iov[j].msg_iovlen = 0;
continue;
}
msg[i].msg_iov[j].msg_iovlen -= transmitted;
msg[i].msg_iov[j].msg_iov += transmitted;
goto retry;
}
}
Also I'm pretty sure that we will actually happen to write the whole
buffer unless we fill up the kernel buffers, which we hardly will with
a few bytes. So maybe we should just send the message here and check
that the msg_len are filled correctly in this case.
> + }
> +
> +out:
> + SAFE_CLOSE(send_sockfd);
> + return NULL;
> +}
> +
> +
> +static void *receiver_thread(LTP_ATTRIBUTE_UNUSED void *arg)
> +{
> + int receive_sockfd;
> + struct sockaddr_in addr;
> + struct mmsghdr msgs[VLEN];
> + struct iovec iovecs[VLEN];
> + char bufs[VLEN][BUFSIZE+1];
> + struct timespec timeout;
> + int i, retval;
> +
> + receive_sockfd = SAFE_SOCKET(AF_INET, SOCK_DGRAM, 0);
> + addr.sin_family = AF_INET;
> + addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
> + addr.sin_port = htons(port);
Here as well, the htons() should be dropped.
> + SAFE_BIND(receive_sockfd, (struct sockaddr *)&addr, sizeof(addr));
> +
> + sem_post(&send_sem);
> +
> + memset(msgs, 0, sizeof(msgs));
> + for (i = 0; i < VLEN; i++) {
> + iovecs[i].iov_base = bufs[i];
> + iovecs[i].iov_len = BUFSIZE;
> + msgs[i].msg_hdr.msg_iov = &iovecs[i];
> + msgs[i].msg_hdr.msg_iovlen = 1;
> + }
> +
> + timeout.tv_sec = 1;
> + timeout.tv_nsec = 0;
> +
> + retval = do_recvmmsg(receive_sockfd, msgs, VLEN, 0, &timeout);
> + SAFE_CLOSE(receive_sockfd);
> +
> + if (retval == -1) {
> + tst_res(TFAIL | TTERRNO, "recvmmsg failed");
> + return NULL;
> + }
> + if (retval != 2) {
> + tst_res(TFAIL, "Received unexpected number of messages (%d)",
> + retval);
> + return NULL;
> + }
> +
> + bufs[0][msgs[0].msg_len] = 0;
> + if (strcmp(bufs[0], "onetwo"))
> + tst_res(TFAIL, "Error in first received message.");
> + else
> + tst_res(TPASS, "First message received successfully.");
> +
> + bufs[1][msgs[1].msg_len] = 0;
> + if (strcmp(bufs[1], "three"))
> + tst_res(TFAIL, "Error in second received message.");
> + else
> + tst_res(TPASS, "Second message received successfully.");
> +
> + return NULL;
> +}
> +
> +static void run(void)
> +{
> + pthread_t sender;
> + pthread_t receiver;
> +
> + SAFE_PTHREAD_CREATE(&sender, NULL, sender_thread, NULL);
> + SAFE_PTHREAD_CREATE(&receiver, NULL, receiver_thread, NULL);
> + SAFE_PTHREAD_JOIN(sender, NULL);
> + SAFE_PTHREAD_JOIN(receiver, NULL);
> +}
> +
> +static void setup(void)
> +{
> + sem_init(&send_sem, 0, 0);
> + port = TST_GET_UNUSED_PORT(AF_INET, SOCK_DGRAM);
> + test_info();
> +}
> +
> +static struct tst_test test = {
> + .test_all = run,
> + .setup = setup,
> + .test_variants = TEST_VARIANTS,
> +};
> diff --git a/testcases/kernel/syscalls/sendmmsg/sendmmsg_var.h b/testcases/kernel/syscalls/sendmmsg/sendmmsg_var.h
> new file mode 100644
> index 000000000..f00cf056a
> --- /dev/null
> +++ b/testcases/kernel/syscalls/sendmmsg/sendmmsg_var.h
> @@ -0,0 +1,60 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Copyright (c) 2018 Google, Inc.
> + */
> +
> +#ifndef SENDMMSG_VAR__
> +#define SENDMMSG_VAR__
> +
> +#include "lapi/syscalls.h"
> +
> +static int do_sendmmsg(int sockfd, struct mmsghdr *msgvec, unsigned int vlen,
> + int flags)
> +{
> + switch (tst_variant) {
> + case 0:
> + return tst_syscall(__NR_sendmmsg, sockfd, msgvec, vlen, flags);
> + case 1:
> +#ifdef HAVE_SENDMMSG
> + return sendmmsg(sockfd, msgvec, vlen, flags);
> +#else
> + tst_brk(TCONF, "libc sendmmsg not present");
> +#endif
> + }
> +
> + return -1;
> +}
> +
> +static int do_recvmmsg(int sockfd, struct mmsghdr *msgvec, unsigned int vlen,
> + int flags, struct timespec *timeout)
> +{
> + switch (tst_variant) {
> + case 0:
> + return tst_syscall(__NR_recvmmsg, sockfd, msgvec, vlen, flags,
> + timeout);
> + case 1:
> +#ifdef HAVE_RECVMMSG
> + return recvmmsg(sockfd, msgvec, vlen, flags, timeout);
> +#else
> + tst_brk(TCONF, "libc recvmmsg not present");
> +#endif
> + }
> +
> + return -1;
> +}
> +
> +static void test_info(void)
> +{
> + switch (tst_variant) {
> + case 0:
> + tst_res(TINFO, "Testing direct sendmmsg and recvmmsg syscalls");
> + break;
> + case 1:
> + tst_res(TINFO, "Testing libc sendmmsg and recvmmsg syscalls");
> + break;
> + }
> +}
> +
> +#define TEST_VARIANTS 2
> +
> +#endif /* SENDMMSG_VAR__ */
> --
> 2.22.0.657.g960e92d24f-goog
>
>
> --
> Mailing list info: https://lists.linux.it/listinfo/ltp
--
Cyril Hrubis
chrubis@suse.cz
More information about the ltp
mailing list