From cebb4ae5857da0424434fdcf09c98fb995ed66df Mon Sep 17 00:00:00 2001 From: David Smith Date: Wed, 2 Apr 2014 14:22:55 -0500 Subject: [PATCH] PR16716 partial fix: Better types in 'syscall.{sendmsg,sendmmsg}'. * tapset/linux/syscalls2.stp: Fixed types and nesting in 'syscall.{sendmsg,sendmmsg}. * tapset/linux/nd_syscalls2.stp: Ditto. * tapset/linux/syscalls.stpm: Added __compat_syscall() macro. * tapset/linux/aux_syscalls.stp: Includes compat_net.h. * runtime/linux/compat_net.h: New file. * runtime/linux/compat_unistd.h: Added defines. * testsuite/systemtap.syscall/sendmmsg.c: New test. * testsuite/systemtap.syscall/sendmsg.c: Ditto. --- runtime/linux/compat_net.h | 22 +++ runtime/linux/compat_unistd.h | 8 + tapset/linux/aux_syscalls.stp | 1 + tapset/linux/nd_syscalls2.stp | 112 ++++++++++++-- tapset/linux/syscalls.stpm | 10 ++ tapset/linux/syscalls2.stp | 122 +++++++++++++--- testsuite/systemtap.syscall/sendmmsg.c | 193 +++++++++++++++++++++++++ testsuite/systemtap.syscall/sendmsg.c | 172 ++++++++++++++++++++++ 8 files changed, 608 insertions(+), 32 deletions(-) create mode 100644 runtime/linux/compat_net.h create mode 100644 testsuite/systemtap.syscall/sendmmsg.c create mode 100644 testsuite/systemtap.syscall/sendmsg.c diff --git a/runtime/linux/compat_net.h b/runtime/linux/compat_net.h new file mode 100644 index 000000000..ca8050ce1 --- /dev/null +++ b/runtime/linux/compat_net.h @@ -0,0 +1,22 @@ +/* -*- linux-c -*- + * Networking compatibility defines. + * Copyright (C) 2014 Red Hat Inc. + * + * This file is part of systemtap, and is free software. You can + * redistribute it and/or modify it under the terms of the GNU General + * Public License (GPL); either version 2, or (at your option) any + * later version. + */ + +#ifndef _COMPAT_NET_H_ +#define _COMPAT_NET_H_ + +#include + +/* Older kernels don't have this defined and don't support + * sys_sendmmsg(2). We just need the define to get things to compile. */ +#ifndef SYS_SENDMMSG +#define SYS_SENDMMSG 20 +#endif + +#endif /* _COMPAT_NET_H_ */ diff --git a/runtime/linux/compat_unistd.h b/runtime/linux/compat_unistd.h index f0f24417a..fced70730 100644 --- a/runtime/linux/compat_unistd.h +++ b/runtime/linux/compat_unistd.h @@ -60,6 +60,9 @@ #ifndef __NR_ia32_rt_sigprocmask #define __NR_ia32_rt_sigprocmask 175 #endif +#ifndef __NR_ia32_sendmmsg +#define __NR_ia32_sendmmsg 345 +#endif #ifndef __NR_ia32_symlinkat #define __NR_ia32_symlinkat 304 #endif @@ -82,6 +85,7 @@ #define __NR_compat_readlinkat __NR_ia32_readlinkat #define __NR_compat_renameat __NR_ia32_renameat #define __NR_compat_rt_sigprocmask __NR_ia32_rt_sigprocmask +#define __NR_compat_sendmmsg __NR_ia32_sendmmsg #define __NR_compat_symlinkat __NR_ia32_symlinkat #define __NR_compat_umount2 __NR_ia32_umount2 @@ -107,6 +111,7 @@ #define __NR_compat_readlinkat __NR_readlinkat #define __NR_compat_renameat __NR_renameat #define __NR_compat_rt_sigprocmask __NR_rt_sigprocmask +#define __NR_compat_sendmmsg __NR_sendmmsg #define __NR_compat_symlinkat __NR_symlinkat #define __NR_compat_umount2 __NR_umount2 @@ -140,6 +145,9 @@ #ifndef __NR_recvfrom #define __NR_recvfrom (__NR_syscall_max + 1) #endif +#ifndef __NR_sendmmsg +#define __NR_sendmmsg (__NR_syscall_max + 1) +#endif #ifndef __NR_sendto #define __NR_sendto (__NR_syscall_max + 1) #endif diff --git a/tapset/linux/aux_syscalls.stp b/tapset/linux/aux_syscalls.stp index 6f5fdaab8..dcc9aca19 100644 --- a/tapset/linux/aux_syscalls.stp +++ b/tapset/linux/aux_syscalls.stp @@ -6,6 +6,7 @@ // Be sure we have the SYS_* defines. #include +#include "linux/compat_net.h" /* * Simple lookup functions for mapping values to names diff --git a/tapset/linux/nd_syscalls2.stp b/tapset/linux/nd_syscalls2.stp index 4f0bf3d05..8f2bd70b8 100644 --- a/tapset/linux/nd_syscalls2.stp +++ b/tapset/linux/nd_syscalls2.stp @@ -2025,6 +2025,14 @@ probe __nd_syscall.sendfile.return = kprobe.function("sys_sendfile").return ?, # probe nd_syscall.sendmsg = kprobe.function("sys_sendmsg") ? { +%( CONFIG_COMPAT == "y" %? + # Avoid probe hits from compat_sys_socketcall() calling + # compat_sys_sendmsg(), which sometimes calls + # sys_sendmsg(). We could call __syscall_gate2() here with + # NR_sendmsg and NR_socketcall, but all we really need to + # check is that we're not in a compat task. + if (%{ _stp_is_compat_task() %}) next +%) name = "sendmsg" asmlinkage() s = int_arg(1) @@ -2035,6 +2043,14 @@ probe nd_syscall.sendmsg = kprobe.function("sys_sendmsg") ? } probe nd_syscall.sendmsg.return = kprobe.function("sys_sendmsg").return ? { +%( CONFIG_COMPAT == "y" %? + # Avoid probe hits from compat_sys_socketcall() calling + # compat_sys_sendmsg(), which sometimes calls + # sys_sendmsg(). We could call __syscall_gate2() here with + # NR_sendmsg and NR_socketcall, but all we really need to + # check is that we're not in a compat task. + if (%{ _stp_is_compat_task() %}) next +%) name = "sendmsg" retstr = returnstr(1) } @@ -2043,19 +2059,34 @@ probe nd_syscall.sendmsg.return = kprobe.function("sys_sendmsg").return ? # # long compat_sys_sendmsg(int fd, struct compat_msghdr __user *msg, unsigned flags) # -probe nd_syscall.compat_sys_sendmsg = kprobe.function("compat_sys_sendmsg") ? +# On all tested kernels/architectures, the compat sendmsg() syscall +# goes through compat_sys_socketcall(). compat_sys_socketcall() then +# calls an inlined version of compat_sys_sendmsg() on some +# architectures (like x86_64 and ppc64). So, the only reliable thing +# to do here is just probe compat_sys_socketcall(). +# +# Note that this probe should have been either called +# 'nd_syscall.compat_sendmsg' or just merged with +# 'nd_syscall.sendmsg'. +# +probe nd_syscall.compat_sys_sendmsg = + kprobe.function("compat_sys_socketcall").call ? { - name = "compat_sys_sendmsg" asmlinkage() - s = int_arg(1) - msg_uaddr = pointer_arg(2) - flags = uint_arg(3) + if (int_arg(1) != %{ SYS_SENDMSG %}) next; + name = "sendmsg" + __args = pointer_arg(2) + s = user_int(&@cast(__args, "unsigned int")[0]) + msg_uaddr = user_uint32(&@cast(__args, "unsigned int")[1]) + flags = user_uint32(&@cast(__args, "unsigned int")[2]) flags_str = _msg_flags_str(flags) argstr = sprintf("%d, %p, %s", s, msg_uaddr, _msg_flags_str(flags)) } -probe nd_syscall.compat_sys_sendmsg.return = kprobe.function("compat_sys_sendmsg").return ? +probe nd_syscall.compat_sys_sendmsg.return = + kprobe.function("compat_sys_socketcall").return ? { - name = "compat_sys_sendmsg" + if (@entry(__asmlinkage_int_arg(1)) != %{ SYS_SENDMSG %}) next; + name = "sendmsg" retstr = returnstr(1) } @@ -2064,23 +2095,80 @@ probe nd_syscall.compat_sys_sendmsg.return = kprobe.function("compat_sys_sendmsg # int sys_sendmmsg(int fd, struct mmsghdr __user *mmsg, unsigned int vlen, # unsigned int flags) # -probe nd_syscall.sendmmsg = kprobe.function("sys_sendmmsg").call ? +probe nd_syscall.sendmmsg = __nd_syscall.sendmmsg ?, + __nd_syscall.compat_socketcall.sendmmsg ?, + __nd_syscall.compat_sendmmsg ? { name = "sendmmsg" + flags_str = _msg_flags_str(flags) + argstr = sprintf("%d, %p, %d, %s", s, mmsg_uaddr, vlen, + _msg_flags_str(flags)) +} +probe __nd_syscall.sendmmsg = kprobe.function("sys_sendmmsg").call ? +{ +%( CONFIG_COMPAT == "y" %? + # Avoid probe hits from compat_sys_socketcall() calling + # compat_sys_sendmmsg(), which sometimes calls + # sys_sendmmsg(). We could call __syscall_gate2() here with + # NR_sendmmsg and NR_socketcall, but all we really need to + # check is that we're not in a compat task. + if (%{ _stp_is_compat_task() %}) next +%) + asmlinkage() + s = int_arg(1) + mmsg_uaddr = pointer_arg(2) + vlen = uint_arg(3) + flags = uint_arg(4) +} +probe __nd_syscall.compat_socketcall.sendmmsg = + kprobe.function("compat_sys_socketcall").call ? +{ + asmlinkage() + if (int_arg(1) != %{ SYS_SENDMMSG %}) next; + __args = pointer_arg(2) + s = user_int(&@cast(__args, "unsigned int")[0]) + mmsg_uaddr = user_uint32(&@cast(__args, "unsigned int")[1]) + vlen = user_uint32(&@cast(__args, "unsigned int")[2]) + flags = user_uint32(&@cast(__args, "unsigned int")[3]) +} +probe __nd_syscall.compat_sendmmsg = + kprobe.function("compat_sys_sendmmsg").call ? +{ + @__compat_syscall_gate(%{ __NR_compat_sendmmsg %}) asmlinkage() s = int_arg(1) mmsg_uaddr = pointer_arg(2) vlen = uint_arg(3) flags = uint_arg(4) - flags_str = _msg_flags_str(flags) - argstr = sprintf("%d, %p, %d, %s", s, mmsg_uaddr, vlen, - _msg_flags_str(flags)) } -probe nd_syscall.sendmmsg.return = kprobe.function("sys_sendmmsg").return ? +probe nd_syscall.sendmmsg.return = __nd_syscall.sendmmsg.return ?, + __nd_syscall.compat_socketcall.sendmmsg.return ?, + __nd_syscall.compat_sendmmsg.return ? { name = "sendmmsg" retstr = returnstr(1) } +probe __nd_syscall.sendmmsg.return = kprobe.function("sys_sendmmsg").return ? +{ +%( CONFIG_COMPAT == "y" %? + # Avoid probe hits from compat_sys_socketcall() calling + # compat_sys_sendmmsg(), which sometimes calls + # sys_sendmmsg(). We could call __syscall_gate2() here with + # NR_sendmmsg and NR_socketcall, but all we really need to + # check is that we're not in a compat task. + if (%{ _stp_is_compat_task() %}) next +%) +} +probe __nd_syscall.compat_socketcall.sendmmsg.return = + kprobe.function("compat_sys_socketcall").return ? +{ + if (@entry(__asmlinkage_int_arg(1)) != %{ SYS_SENDMMSG %}) next; +} +probe __nd_syscall.compat_sendmmsg.return = + kprobe.function("compat_sys_sendmmsg").return ? +{ + @__compat_syscall_gate(%{ __NR_compat_sendmmsg %}) +} # sendto _____________________________________________________ # diff --git a/tapset/linux/syscalls.stpm b/tapset/linux/syscalls.stpm index a590218a7..0d796691c 100644 --- a/tapset/linux/syscalls.stpm +++ b/tapset/linux/syscalls.stpm @@ -49,6 +49,16 @@ %) %) +@define __compat_syscall_gate(compat_syscall_nr) +%( + %( CONFIG_COMPAT == "y" %? + try { __nr = _stp_syscall_nr() } catch { next } + if (%{ _stp_is_compat_task() %}) { + if (__nr != @compat_syscall_nr) next + } + %) +%) + @define __pointer(val) %( %( CONFIG_COMPAT == "y" %? diff --git a/tapset/linux/syscalls2.stp b/tapset/linux/syscalls2.stp index b5c86ee80..fe8ce26a1 100644 --- a/tapset/linux/syscalls2.stp +++ b/tapset/linux/syscalls2.stp @@ -2019,15 +2019,31 @@ probe __syscall.sendfile.return = kernel.function("sys_sendfile").return ?, # probe syscall.sendmsg = kernel.function("sys_sendmsg").call ? { +%( CONFIG_COMPAT == "y" %? + # Avoid probe hits from compat_sys_socketcall() calling + # compat_sys_sendmsg(), which sometimes calls + # sys_sendmsg(). We could call __syscall_gate2() here with + # NR_sendmsg and NR_socketcall, but all we really need to + # check is that we're not in a compat task. + if (%{ _stp_is_compat_task() %}) next +%) name = "sendmsg" - s = $fd + s = __int32($fd) msg_uaddr = $msg - flags = $flags + flags = __uint32($flags) flags_str = _msg_flags_str($flags) - argstr = sprintf("%d, %p, %s", $fd, $msg, _msg_flags_str($flags)) + argstr = sprintf("%d, %p, %s", s, $msg, _msg_flags_str(flags)) } probe syscall.sendmsg.return = kernel.function("sys_sendmsg").return ? { +%( CONFIG_COMPAT == "y" %? + # Avoid probe hits from compat_sys_socketcall() calling + # compat_sys_sendmsg(), which sometimes calls + # sys_sendmsg(). We could call __syscall_gate2() here with + # NR_sendmsg and NR_socketcall, but all we really need to + # check is that we're not in a compat task. + if (%{ _stp_is_compat_task() %}) next +%) name = "sendmsg" retstr = return_str(1, $return) } @@ -2035,18 +2051,31 @@ probe syscall.sendmsg.return = kernel.function("sys_sendmsg").return ? # # long compat_sys_sendmsg(int fd, struct compat_msghdr __user *msg, unsigned flags) # -probe syscall.compat_sys_sendmsg = kernel.function("compat_sys_sendmsg").call ? +# On all tested kernels/architectures, the compat sendmsg() syscall +# goes through compat_sys_socketcall(). compat_sys_socketcall() then +# calls an inlined version of compat_sys_sendmsg() on some +# architectures (like x86_64 and ppc64). So, the only reliable thing +# to do here is just probe compat_sys_socketcall(). +# +# Note that this probe should have been either called +# 'syscall.compat_sendmsg' or just merged with 'syscall.sendmsg'. +# +probe syscall.compat_sys_sendmsg = + kernel.function("compat_sys_socketcall").call ? { - name = "compat_sys_sendmsg" - s = $fd - msg_uaddr = $msg - flags = $flags - flags_str = _msg_flags_str($flags) - argstr = sprintf("%d, %p, %s", $fd, $msg, _msg_flags_str($flags)) + if ($call != %{ SYS_SENDMSG %}) next; + name = "sendmsg" + s = user_int(&@cast($args, "unsigned int")[0]) + msg_uaddr = user_uint32(&@cast($args, "unsigned int")[1]) + flags = user_uint32(&@cast($args, "unsigned int")[2]) + flags_str = _msg_flags_str(flags) + argstr = sprintf("%d, %p, %s", s, msg_uaddr, _msg_flags_str(flags)) } -probe syscall.compat_sys_sendmsg.return = kernel.function("compat_sys_sendmsg").return ? +probe syscall.compat_sys_sendmsg.return = + kernel.function("compat_sys_socketcall").return ? { - name = "compat_sys_sendmsg" + if ($call != %{ SYS_SENDMSG %}) next; + name = "sendmsg" retstr = return_str(1, $return) } @@ -2055,22 +2084,75 @@ probe syscall.compat_sys_sendmsg.return = kernel.function("compat_sys_sendmsg"). # int sys_sendmmsg(int fd, struct mmsghdr __user *mmsg, unsigned int vlen, # unsigned int flags) # -probe syscall.sendmmsg = kernel.function("sys_sendmmsg").call ? +probe syscall.sendmmsg = __syscall.sendmmsg ?, + __syscall.compat_socketcall.sendmmsg ?, __syscall.compat_sendmmsg ? { name = "sendmmsg" - s = $fd + flags_str = _msg_flags_str(flags) + argstr = sprintf("%d, %p, %d, %s", s, mmsg_uaddr, vlen, + _msg_flags_str(flags)) +} +probe __syscall.sendmmsg = kernel.function("sys_sendmmsg").call ? +{ +%( CONFIG_COMPAT == "y" %? + # Avoid probe hits from compat_sys_socketcall() calling + # compat_sys_sendmmsg(), which sometimes calls + # sys_sendmmsg(). We could call __syscall_gate2() here with + # NR_sendmmsg and NR_socketcall, but all we really need to + # check is that we're not in a compat task. + if (%{ _stp_is_compat_task() %}) next +%) + s = __int32($fd) mmsg_uaddr = $mmsg - vlen = $vlen - flags = $flags - flags_str = _msg_flags_str($flags) - argstr = sprintf("%d, %p, %d, %s", $fd, $mmsg, $vlen, - _msg_flags_str($flags)) + vlen = __uint32($vlen) + flags = __uint32($flags) } -probe syscall.sendmmsg.return = kernel.function("sys_sendmmsg").return ? +probe __syscall.compat_socketcall.sendmmsg = + kernel.function("compat_sys_socketcall").call ? +{ + if ($call != %{ SYS_SENDMMSG %}) next + s = user_int(&@cast($args, "unsigned int")[0]) + mmsg_uaddr = user_uint32(&@cast($args, "unsigned int")[1]) + vlen = user_uint32(&@cast($args, "unsigned int")[2]) + flags = user_uint32(&@cast($args, "unsigned int")[3]) +} +probe __syscall.compat_sendmmsg = + kernel.function("compat_sys_sendmmsg").call ? +{ + @__compat_syscall_gate(%{ __NR_compat_sendmmsg %}) + s = __int32($fd) + mmsg_uaddr = $mmsg + vlen = __uint32($vlen) + flags = __uint32($flags) +} +probe syscall.sendmmsg.return = __syscall.sendmmsg.return ?, + __syscall.compat_socketcall.sendmmsg.return ?, + __syscall.compat_sendmmsg.return ? { name = "sendmmsg" retstr = return_str(1, $return) } +probe __syscall.sendmmsg.return = kernel.function("sys_sendmmsg").return ? +{ +%( CONFIG_COMPAT == "y" %? + # Avoid probe hits from compat_sys_socketcall() calling + # compat_sys_sendmmsg(), which sometimes calls + # sys_sendmmsg(). We could call __syscall_gate2() here with + # NR_sendmmsg and NR_socketcall, but all we really need to + # check is that we're not in a compat task. + if (%{ _stp_is_compat_task() %}) next +%) +} +probe __syscall.compat_socketcall.sendmmsg.return = + kernel.function("compat_sys_socketcall").return ? +{ + if ($call != %{ SYS_SENDMMSG %}) next +} +probe __syscall.compat_sendmmsg.return = + kernel.function("compat_sys_sendmmsg").return ? +{ + @__compat_syscall_gate(%{ __NR_compat_sendmmsg %}) +} # sendto _____________________________________________________ # diff --git a/testsuite/systemtap.syscall/sendmmsg.c b/testsuite/systemtap.syscall/sendmmsg.c new file mode 100644 index 000000000..e455b141f --- /dev/null +++ b/testsuite/systemtap.syscall/sendmmsg.c @@ -0,0 +1,193 @@ +/* COVERAGE: sendmmsg */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int sfd; /* shared between start_server and do_child */ + +void do_child() +{ + struct sockaddr_in fsin; + fd_set afds, rfds; + int nfds, cc, fd; + char buf[1024]; + + FD_ZERO(&afds); + FD_SET(sfd, &afds); + + nfds = FD_SETSIZE; + + /* accept connections until killed */ + while (1) { + socklen_t fromlen; + + memcpy(&rfds, &afds, sizeof(rfds)); + + if (select(nfds, &rfds, (fd_set *) 0, (fd_set *) 0, + (struct timeval *)0) < 0) { + if (errno != EINTR) + exit(1); + } + if (FD_ISSET(sfd, &rfds)) { + int newfd; + + fromlen = sizeof(fsin); + newfd = accept(sfd, (struct sockaddr *)&fsin, &fromlen); + if (newfd >= 0) + FD_SET(newfd, &afds); + } + for (fd = 0; fd < nfds; ++fd) { + if (fd != sfd && FD_ISSET(fd, &rfds)) { + cc = read(fd, buf, sizeof(buf)); + if (cc == 0 || (cc < 0 && errno != EINTR)) { + (void)close(fd); + FD_CLR(fd, &afds); + } + } + } + } +} + +pid_t +start_server(struct sockaddr_in *sin0) +{ + struct sockaddr_in sin1 = *sin0; + pid_t pid; + + sfd = socket(PF_INET, SOCK_STREAM, 0); + if (sfd < 0) + return -1; + if (bind(sfd, (struct sockaddr *)&sin1, sizeof(sin1)) < 0) + return -1; + if (listen(sfd, 10) < 0) + return -1; + + switch (pid = fork()) { + case 0: /* child */ + do_child(); + break; + case -1: /* fall through */ + default: /* parent */ + (void)close(sfd); + return pid; + } + + return -1; +} + +int main() +{ + /* Only try this test if we have 'SYS_sendmmsg'. Unfortunately, + * on RHEL6 we have SYS_sendmmsg(), but no glibc support for + * sendmmsg(). */ +#ifdef SYS_sendmmsg + int s, fd_null; + struct sockaddr_in sin1, from; + pid_t pid = 0; + char buf[2][1024]; + fd_set rdfds; + struct mmsghdr msgs[2]; + struct iovec iov[2]; + socklen_t fromlen; + + /* initialize sockaddr's */ + sin1.sin_family = AF_INET; + sin1.sin_port = htons((getpid() % 32768) + 11000); + sin1.sin_addr.s_addr = INADDR_ANY; + + pid = start_server(&sin1); + + fromlen = sizeof(from); + + memset(msgs, 0, sizeof(msgs)); + iov[0].iov_base = buf[0]; + iov[0].iov_len = sizeof(buf[0]); + msgs[0].msg_hdr.msg_iov = &iov[0]; + msgs[0].msg_hdr.msg_iovlen = 1; + iov[1].iov_base = buf[1]; + iov[1].iov_len = sizeof(buf[1]); + msgs[1].msg_hdr.msg_iov = &iov[1]; + msgs[1].msg_hdr.msg_iovlen = 1; + + fd_null = open("/dev/null", O_WRONLY); + + sendmmsg(-1, msgs, 2, 0); + //staptest// sendmmsg (-1, XXXX, 2, 0x0) = -NNNN (EBADF) + + sendmmsg(fd_null, msgs, 2, MSG_DONTWAIT); + //staptest// sendmmsg (NNNN, XXXX, 2, MSG_DONTWAIT) = -NNNN (ENOTSOCK) + + s = socket(PF_INET, SOCK_DGRAM, 0); + //staptest// socket (PF_INET, SOCK_DGRAM, IPPROTO_IP) = NNNN + + sendmmsg(s, msgs, 2, 0); + //staptest// sendmmsg (NNNN, XXXX, 2, 0x0) = -NNNN (EDESTADDRREQ) + + sendmmsg(s, (struct mmsghdr *)-1, 2, 0); + //staptest// sendmmsg (NNNN, 0x[f]+, 2, 0x0) = -NNNN (EFAULT) + + close(s); + //staptest// close (NNNN) = 0 + + s = socket(PF_INET, SOCK_STREAM, 0); + //staptest// socket (PF_INET, SOCK_STREAM, IPPROTO_IP) = NNNN + + connect(s, (struct sockaddr *)&sin1, sizeof(sin1)); + //staptest// connect (NNNN, {AF_INET, 0.0.0.0, NNNN}, 16) = 0 + + // Note that the exact failure return value can differ here, so + // we'll just ignore it. + sendmmsg(s, msgs, 2, -1); + //staptest// sendmmsg (NNNN, XXXX, 2, MSG_[^ ]+|XXXX) = -NNNN + + close(s); + //staptest// close (NNNN) = 0 + + s = socket(PF_INET, SOCK_STREAM, 0); + //staptest// socket (PF_INET, SOCK_STREAM, IPPROTO_IP) = NNNN + + connect(s, (struct sockaddr *)&sin1, sizeof(sin1)); + //staptest// connect (NNNN, {AF_INET, 0.0.0.0, NNNN}, 16) = 0 + + // We don't want to wait for -1 vectors, since we'd be waiting for + // a long time... + sendmmsg(s, msgs, -1, MSG_DONTWAIT); + //staptest// sendmmsg (NNNN, XXXX, 4294967295, MSG_DONTWAIT) + + close(s); + //staptest// close (NNNN) = 0 + + s = socket(PF_INET, SOCK_STREAM, 0); + //staptest// socket (PF_INET, SOCK_STREAM, IPPROTO_IP) = NNNN + + connect(s, (struct sockaddr *)&sin1, sizeof(sin1)); + //staptest// connect (NNNN, {AF_INET, 0.0.0.0, NNNN}, 16) = 0 + + sendmmsg(s, msgs, 2, MSG_DONTWAIT); + //staptest// sendmmsg (NNNN, XXXX, 2, MSG_DONTWAIT) = NNNN + + close(s); + //staptest// close (NNNN) = 0 + + close(fd_null); + //staptest// close (NNNN) = 0 + + if (pid > 0) + (void)kill(pid, SIGKILL); /* kill server */ + //staptest// kill (NNNN, SIGKILL) = 0 +#endif + + return 0; +} diff --git a/testsuite/systemtap.syscall/sendmsg.c b/testsuite/systemtap.syscall/sendmsg.c new file mode 100644 index 000000000..7d569a38f --- /dev/null +++ b/testsuite/systemtap.syscall/sendmsg.c @@ -0,0 +1,172 @@ +/* COVERAGE: sendmsg */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int sfd; /* shared between start_server and do_child */ + +void do_child() +{ + struct sockaddr_in fsin; + fd_set afds, rfds; + int nfds, cc, fd; + char buf[1024]; + + FD_ZERO(&afds); + FD_SET(sfd, &afds); + + nfds = FD_SETSIZE; + + /* accept connections until killed */ + while (1) { + socklen_t fromlen; + + memcpy(&rfds, &afds, sizeof(rfds)); + + if (select(nfds, &rfds, (fd_set *) 0, (fd_set *) 0, + (struct timeval *)0) < 0) { + if (errno != EINTR) + exit(1); + } + if (FD_ISSET(sfd, &rfds)) { + int newfd; + + fromlen = sizeof(fsin); + newfd = accept(sfd, (struct sockaddr *)&fsin, &fromlen); + if (newfd >= 0) + FD_SET(newfd, &afds); + } + for (fd = 0; fd < nfds; ++fd) { + if (fd != sfd && FD_ISSET(fd, &rfds)) { + cc = read(fd, buf, sizeof(buf)); + if (cc == 0 || (cc < 0 && errno != EINTR)) { + (void)close(fd); + FD_CLR(fd, &afds); + } + } + } + } +} + +pid_t +start_server(struct sockaddr_in *sin0) +{ + struct sockaddr_in sin1 = *sin0; + pid_t pid; + + sfd = socket(PF_INET, SOCK_STREAM, 0); + if (sfd < 0) + return -1; + if (bind(sfd, (struct sockaddr *)&sin1, sizeof(sin1)) < 0) + return -1; + if (listen(sfd, 10) < 0) + return -1; + + switch (pid = fork()) { + case 0: /* child */ + do_child(); + break; + case -1: /* fall through */ + default: /* parent */ + (void)close(sfd); + return pid; + } + + return -1; +} + +int main() +{ + int s, fd_null; + struct sockaddr_in sin1, from; + pid_t pid = 0; + char buf[1024]; + fd_set rdfds; + struct msghdr msgdat; + struct iovec iov; + socklen_t fromlen; + + /* initialize sockaddr's */ + sin1.sin_family = AF_INET; + sin1.sin_port = htons((getpid() % 32768) + 11000); + sin1.sin_addr.s_addr = INADDR_ANY; + + pid = start_server(&sin1); + + fromlen = sizeof(from); + iov.iov_base = buf; + iov.iov_len = sizeof(buf); + msgdat.msg_name = &from; + msgdat.msg_namelen = fromlen; + msgdat.msg_iov = &iov; + msgdat.msg_iovlen = 1; + msgdat.msg_control = NULL; + msgdat.msg_controllen = 0; + msgdat.msg_flags = 0; + + fd_null = open("/dev/null", O_WRONLY); + + sendmsg(-1, &msgdat, 0); + //staptest// sendmsg (-1, XXXX, 0x0) = -NNNN (EBADF) + + sendmsg(fd_null, &msgdat, MSG_DONTWAIT); + //staptest// sendmsg (NNNN, XXXX, MSG_DONTWAIT) = -NNNN (ENOTSOCK) + + s = socket(PF_INET, SOCK_DGRAM, 0); + //staptest// socket (PF_INET, SOCK_DGRAM, IPPROTO_IP) = NNNN + + sendmsg(s, &msgdat, MSG_OOB); + //staptest// sendmsg (NNNN, XXXX, MSG_OOB) = -NNNN (EOPNOTSUPP) + + sendmsg(s, (struct msghdr *)-1, 0); + //staptest// sendmsg (NNNN, 0x[f]+, 0x0) = -NNNN (EFAULT) + + close(s); + //staptest// close (NNNN) = 0 + + s = socket(PF_INET, SOCK_STREAM, 0); + //staptest// socket (PF_INET, SOCK_STREAM, IPPROTO_IP) = NNNN + + connect(s, (struct sockaddr *)&sin1, sizeof(sin1)); + //staptest// connect (NNNN, {AF_INET, 0.0.0.0, NNNN}, 16) = 0 + + // Note that the exact return value can differ here, so we'll just + // ignore it. + sendmsg(s, &msgdat, -1); + //staptest// sendmsg (NNNN, XXXX, MSG_[^ ]+|XXXX) + + close(s); + //staptest// close (NNNN) = 0 + + s = socket(PF_INET, SOCK_STREAM, 0); + //staptest// socket (PF_INET, SOCK_STREAM, IPPROTO_IP) = NNNN + + connect(s, (struct sockaddr *)&sin1, sizeof(sin1)); + //staptest// connect (NNNN, {AF_INET, 0.0.0.0, NNNN}, 16) = 0 + + sendmsg(s, &msgdat, MSG_DONTWAIT); + //staptest// sendmsg (NNNN, XXXX, MSG_DONTWAIT) = 1024 + + close(s); + //staptest// close (NNNN) = 0 + + close(fd_null); + //staptest// close (NNNN) = 0 + + if (pid > 0) + (void)kill(pid, SIGKILL); /* kill server */ + //staptest// kill (NNNN, SIGKILL) = 0 + + return 0; +} -- 2.43.5