summaryrefslogtreecommitdiffstats
path: root/tools/regression
diff options
context:
space:
mode:
Diffstat (limited to 'tools/regression')
-rw-r--r--tools/regression/bin/sh/builtins/eval6.05
-rw-r--r--tools/regression/bin/sh/builtins/read6.05
-rw-r--r--tools/regression/bin/sh/builtins/trap12.010
-rw-r--r--tools/regression/bin/sh/builtins/wait4.012
-rw-r--r--tools/regression/bin/sh/builtins/wait5.012
-rw-r--r--tools/regression/bin/sh/builtins/wait6.03
-rw-r--r--tools/regression/bin/sh/builtins/wait7.04
-rw-r--r--tools/regression/bin/sh/execution/not1.04
-rw-r--r--tools/regression/bin/sh/execution/not2.06
-rw-r--r--tools/regression/bin/test/regress.sh65
-rw-r--r--tools/regression/capsicum/syscalls/Makefile28
-rw-r--r--tools/regression/capsicum/syscalls/cap_fcntls_limit.c540
-rw-r--r--tools/regression/capsicum/syscalls/cap_getmode.c167
-rw-r--r--tools/regression/capsicum/syscalls/cap_ioctls_limit.c462
-rw-r--r--tools/regression/capsicum/syscalls/misc.c128
-rw-r--r--tools/regression/capsicum/syscalls/misc.h62
-rw-r--r--tools/regression/lib/libc/gen/test-wordexp.c19
-rw-r--r--tools/regression/lib/libc/stdio/Makefile4
-rw-r--r--tools/regression/lib/libc/stdio/test-open_memstream.c203
-rw-r--r--tools/regression/lib/libc/stdio/test-open_memstream.t10
-rw-r--r--tools/regression/lib/libc/stdio/test-open_wmemstream.c203
-rw-r--r--tools/regression/lib/libc/stdio/test-open_wmemstream.t10
-rw-r--r--tools/regression/pjdfstest/Makefile2
-rw-r--r--tools/regression/pjdfstest/pjdfstest.c65
-rw-r--r--tools/regression/pjdfstest/tests/misc.sh4
-rw-r--r--tools/regression/security/cap_test/cap_test_capabilities.c478
-rw-r--r--tools/regression/security/cap_test/cap_test_relative.c3
-rw-r--r--tools/regression/sockets/unix_cmsg/README211
-rw-r--r--tools/regression/sockets/unix_cmsg/unix_cmsg.c2599
-rw-r--r--tools/regression/sockets/unix_cmsg/unix_cmsg.t91
30 files changed, 4059 insertions, 1356 deletions
diff --git a/tools/regression/bin/sh/builtins/eval6.0 b/tools/regression/bin/sh/builtins/eval6.0
new file mode 100644
index 0000000..6752bb6
--- /dev/null
+++ b/tools/regression/bin/sh/builtins/eval6.0
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+# eval should preserve $? from command substitutions when starting
+# the parsed command.
+[ $(false; eval 'echo $?' $(:)) = 0 ]
diff --git a/tools/regression/bin/sh/builtins/read6.0 b/tools/regression/bin/sh/builtins/read6.0
new file mode 100644
index 0000000..2168e10
--- /dev/null
+++ b/tools/regression/bin/sh/builtins/read6.0
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+: | read x
+r=$?
+[ "$r" = 1 ]
diff --git a/tools/regression/bin/sh/builtins/trap12.0 b/tools/regression/bin/sh/builtins/trap12.0
new file mode 100644
index 0000000..8c62ffd
--- /dev/null
+++ b/tools/regression/bin/sh/builtins/trap12.0
@@ -0,0 +1,10 @@
+# $FreeBSD$
+
+f() {
+ trap 'return 42' USR1
+ kill -USR1 $$
+ return 3
+}
+f
+r=$?
+[ "$r" = 42 ]
diff --git a/tools/regression/bin/sh/builtins/wait4.0 b/tools/regression/bin/sh/builtins/wait4.0
new file mode 100644
index 0000000..7935131
--- /dev/null
+++ b/tools/regression/bin/sh/builtins/wait4.0
@@ -0,0 +1,12 @@
+# $FreeBSD$
+
+T=`mktemp -d ${TMPDIR:-/tmp}/sh-test.XXXXXX`
+trap 'rm -rf $T' 0
+cd $T || exit 3
+mkfifo fifo1
+trapped=
+trap trapped=1 QUIT
+{ kill -QUIT $$; sleep 1; exit 4; } >fifo1 &
+wait $! <fifo1
+r=$?
+[ "$r" -gt 128 ] && [ -n "$trapped" ]
diff --git a/tools/regression/bin/sh/builtins/wait5.0 b/tools/regression/bin/sh/builtins/wait5.0
new file mode 100644
index 0000000..6874bf6
--- /dev/null
+++ b/tools/regression/bin/sh/builtins/wait5.0
@@ -0,0 +1,12 @@
+# $FreeBSD$
+
+T=`mktemp -d ${TMPDIR:-/tmp}/sh-test.XXXXXX`
+trap 'rm -rf $T' 0
+cd $T || exit 3
+mkfifo fifo1
+trapped=
+trap trapped=1 QUIT
+{ kill -QUIT $$; sleep 1; exit 4; } >fifo1 &
+wait <fifo1
+r=$?
+[ "$r" -gt 128 ] && [ -n "$trapped" ]
diff --git a/tools/regression/bin/sh/builtins/wait6.0 b/tools/regression/bin/sh/builtins/wait6.0
new file mode 100644
index 0000000..20e3c68
--- /dev/null
+++ b/tools/regression/bin/sh/builtins/wait6.0
@@ -0,0 +1,3 @@
+# $FreeBSD$
+
+wait --
diff --git a/tools/regression/bin/sh/builtins/wait7.0 b/tools/regression/bin/sh/builtins/wait7.0
new file mode 100644
index 0000000..0fb092f
--- /dev/null
+++ b/tools/regression/bin/sh/builtins/wait7.0
@@ -0,0 +1,4 @@
+# $FreeBSD$
+
+: &
+wait -- $!
diff --git a/tools/regression/bin/sh/execution/not1.0 b/tools/regression/bin/sh/execution/not1.0
new file mode 100644
index 0000000..12c6265
--- /dev/null
+++ b/tools/regression/bin/sh/execution/not1.0
@@ -0,0 +1,4 @@
+# $FreeBSD$
+
+f() { ! return $1; }
+f 0 && ! f 1
diff --git a/tools/regression/bin/sh/execution/not2.0 b/tools/regression/bin/sh/execution/not2.0
new file mode 100644
index 0000000..1b128d0
--- /dev/null
+++ b/tools/regression/bin/sh/execution/not2.0
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+while :; do
+ ! break
+ exit 3
+done
diff --git a/tools/regression/bin/test/regress.sh b/tools/regression/bin/test/regress.sh
index 9229551..117a7e2 100644
--- a/tools/regression/bin/test/regress.sh
+++ b/tools/regression/bin/test/regress.sh
@@ -52,7 +52,7 @@ t ()
}
count=0
-echo "1..130"
+echo "1..266"
t 0 'b = b'
t 0 'b == b'
@@ -194,3 +194,66 @@ t 1 '\( ! -a \)'
t 0 '\( -n -o \)'
t 1 '\( -z -o \)'
t 1 '\( ! -o \)'
+
+# Test all file timestamp comparison operators
+s() {
+ t ${1} "${35} -nt ${36}"
+ t ${2} "${35} -ntaa ${36}"
+ t ${3} "${35} -ntab ${36}"
+ t ${4} "${35} -ntac ${36}"
+ t ${5} "${35} -ntam ${36}"
+ t ${6} "${35} -ntba ${36}"
+ t ${7} "${35} -ntbb ${36}"
+ t ${8} "${35} -ntbc ${36}"
+ t ${9} "${35} -ntbm ${36}"
+ t ${10} "${35} -ntca ${36}"
+ t ${11} "${35} -ntcb ${36}"
+ t ${12} "${35} -ntcc ${36}"
+ t ${13} "${35} -ntcm ${36}"
+ t ${14} "${35} -ntma ${36}"
+ t ${15} "${35} -ntmb ${36}"
+ t ${16} "${35} -ntmc ${36}"
+ t ${17} "${35} -ntmm ${36}"
+ t ${18} "${35} -ot ${36}"
+ t ${19} "${35} -otaa ${36}"
+ t ${20} "${35} -otab ${36}"
+ t ${21} "${35} -otac ${36}"
+ t ${22} "${35} -otam ${36}"
+ t ${23} "${35} -otba ${36}"
+ t ${24} "${35} -otbb ${36}"
+ t ${25} "${35} -otbc ${36}"
+ t ${26} "${35} -otbm ${36}"
+ t ${27} "${35} -otca ${36}"
+ t ${28} "${35} -otcb ${36}"
+ t ${29} "${35} -otcc ${36}"
+ t ${30} "${35} -otcm ${36}"
+ t ${31} "${35} -otma ${36}"
+ t ${32} "${35} -otmb ${36}"
+ t ${33} "${35} -otmc ${36}"
+ t ${34} "${35} -otmm ${36}"
+}
+
+a=/tmp/test$$.1
+b=/tmp/test$$.2
+trap "rm -f $a $b" EXIT
+
+# Tests 131-164
+s 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 $a $b
+
+touch $a
+# Tests 165-198
+s 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 $a $b
+
+sleep 2 # Ensure $b is newer than $a
+touch $b
+# Tests 199-232
+s 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 $a $b
+
+sleep 2
+echo >$b # Updates mtime & ctime
+sleep 2
+touch -A 01 -a $b
+
+# $b now has ctime > mtime > atime > btime
+# Tests 233-266
+s 1 1 0 1 1 1 1 1 1 0 0 1 0 0 0 1 1 1 1 1 0 0 0 1 0 0 1 1 1 1 1 1 0 1 $b $b
diff --git a/tools/regression/capsicum/syscalls/Makefile b/tools/regression/capsicum/syscalls/Makefile
new file mode 100644
index 0000000..5d34226
--- /dev/null
+++ b/tools/regression/capsicum/syscalls/Makefile
@@ -0,0 +1,28 @@
+# $FreeBSD$
+
+SYSCALLS= cap_fcntls_limit cap_getmode cap_ioctls_limit
+
+CFLAGS= -O2 -pipe -std=gnu99 -fstack-protector
+CFLAGS+= -Wsystem-headers -Werror -Wall -Wno-format-y2k -W -Wno-unused-parameter
+CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes -Wpointer-arith -Wreturn-type
+CFLAGS+= -Wcast-qual -Wwrite-strings -Wswitch -Wshadow -Wunused-parameter
+CFLAGS+= -Wcast-align -Wchar-subscripts -Winline -Wnested-externs -Wredundant-decls
+CFLAGS+= -Wold-style-definition -Wno-pointer-sign
+
+all: ${SYSCALLS} ${SYSCALLS:=.t}
+
+.for SYSCALL in ${SYSCALLS}
+
+${SYSCALL}: ${SYSCALL}.c misc.c
+ ${CC} ${CFLAGS} ${@}.c misc.c -o $@
+
+${SYSCALL}.t: ${SYSCALL}
+ @printf "#!/bin/sh\n\n%s/%s\n" ${.CURDIR} ${@:.t=} > $@
+
+.endfor
+
+test: all
+ @prove -r ${.CURDIR}
+
+clean:
+ rm -f ${SYSCALLS} ${SYSCALLS:=.t}
diff --git a/tools/regression/capsicum/syscalls/cap_fcntls_limit.c b/tools/regression/capsicum/syscalls/cap_fcntls_limit.c
new file mode 100644
index 0000000..c97203d
--- /dev/null
+++ b/tools/regression/capsicum/syscalls/cap_fcntls_limit.c
@@ -0,0 +1,540 @@
+/*-
+ * Copyright (c) 2012 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Pawel Jakub Dawidek under sponsorship from
+ * the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/capability.h>
+#include <sys/procdesc.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "misc.h"
+
+static void
+fcntl_tests_0(int fd)
+{
+ uint32_t fcntlrights;
+
+ fcntlrights = 0;
+ CHECK(cap_fcntls_get(fd, &fcntlrights) == 0);
+ CHECK(fcntlrights == CAP_FCNTL_ALL);
+
+ CHECK(fcntl(fd, F_GETFD) == 0);
+ CHECK(fcntl(fd, F_SETFD, FD_CLOEXEC) == 0);
+ CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC);
+ CHECK(fcntl(fd, F_SETFD, 0) == 0);
+ CHECK(fcntl(fd, F_GETFD) == 0);
+
+ CHECK(fcntl(fd, F_GETFL) == O_RDWR);
+ CHECK(fcntl(fd, F_SETFL, O_NONBLOCK) == 0);
+ CHECK(fcntl(fd, F_GETFL) == (O_RDWR | O_NONBLOCK));
+ CHECK(fcntl(fd, F_SETFL, 0) == 0);
+ CHECK(fcntl(fd, F_GETFL) == O_RDWR);
+
+ errno = 0;
+ CHECK(cap_fcntls_limit(fd, ~CAP_FCNTL_ALL) == -1);
+ CHECK(errno == EINVAL);
+ CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL | CAP_FCNTL_SETFL) == 0);
+ fcntlrights = 0;
+ CHECK(cap_fcntls_get(fd, &fcntlrights) == 0);
+ CHECK(fcntlrights == (CAP_FCNTL_GETFL | CAP_FCNTL_SETFL));
+ CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL | CAP_FCNTL_SETFL) == 0);
+ fcntlrights = 0;
+ CHECK(cap_fcntls_get(fd, &fcntlrights) == 0);
+ CHECK(fcntlrights == (CAP_FCNTL_GETFL | CAP_FCNTL_SETFL));
+
+ CHECK(fcntl(fd, F_GETFD) == 0);
+ CHECK(fcntl(fd, F_SETFD, FD_CLOEXEC) == 0);
+ CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC);
+ CHECK(fcntl(fd, F_SETFD, 0) == 0);
+ CHECK(fcntl(fd, F_GETFD) == 0);
+
+ CHECK(fcntl(fd, F_GETFL) == O_RDWR);
+ CHECK(fcntl(fd, F_SETFL, O_NONBLOCK) == 0);
+ CHECK(fcntl(fd, F_GETFL) == (O_RDWR | O_NONBLOCK));
+ CHECK(fcntl(fd, F_SETFL, 0) == 0);
+ CHECK(fcntl(fd, F_GETFL) == O_RDWR);
+
+ CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL) == 0);
+ fcntlrights = 0;
+ CHECK(cap_fcntls_get(fd, &fcntlrights) == 0);
+ CHECK(fcntlrights == CAP_FCNTL_GETFL);
+ errno = 0;
+ CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL | CAP_FCNTL_SETFL) == -1);
+ CHECK(errno == ENOTCAPABLE);
+ fcntlrights = 0;
+ CHECK(cap_fcntls_get(fd, &fcntlrights) == 0);
+ CHECK(fcntlrights == CAP_FCNTL_GETFL);
+
+ CHECK(fcntl(fd, F_GETFD) == 0);
+ CHECK(fcntl(fd, F_SETFD, FD_CLOEXEC) == 0);
+ CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC);
+ CHECK(fcntl(fd, F_SETFD, 0) == 0);
+ CHECK(fcntl(fd, F_GETFD) == 0);
+
+ CHECK(fcntl(fd, F_GETFL) == O_RDWR);
+ errno = 0;
+ CHECK(fcntl(fd, F_SETFL, O_NONBLOCK) == -1);
+ CHECK(errno == ENOTCAPABLE);
+ CHECK(fcntl(fd, F_GETFL) == O_RDWR);
+ errno = 0;
+ CHECK(fcntl(fd, F_SETFL, 0) == -1);
+ CHECK(errno == ENOTCAPABLE);
+ CHECK(fcntl(fd, F_GETFL) == O_RDWR);
+
+ CHECK(cap_fcntls_limit(fd, 0) == 0);
+ fcntlrights = CAP_FCNTL_ALL;
+ CHECK(cap_fcntls_get(fd, &fcntlrights) == 0);
+ CHECK(fcntlrights == 0);
+ errno = 0;
+ CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL | CAP_FCNTL_SETFL) == -1);
+ CHECK(errno == ENOTCAPABLE);
+ fcntlrights = CAP_FCNTL_ALL;
+ CHECK(cap_fcntls_get(fd, &fcntlrights) == 0);
+ CHECK(fcntlrights == 0);
+ errno = 0;
+ CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL) == -1);
+ CHECK(errno == ENOTCAPABLE);
+ fcntlrights = CAP_FCNTL_ALL;
+ CHECK(cap_fcntls_get(fd, &fcntlrights) == 0);
+ CHECK(fcntlrights == 0);
+
+ CHECK(fcntl(fd, F_GETFD) == 0);
+ CHECK(fcntl(fd, F_SETFD, FD_CLOEXEC) == 0);
+ CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC);
+ CHECK(fcntl(fd, F_SETFD, 0) == 0);
+ CHECK(fcntl(fd, F_GETFD) == 0);
+
+ errno = 0;
+ CHECK(fcntl(fd, F_GETFL) == -1);
+ CHECK(errno == ENOTCAPABLE);
+ errno = 0;
+ CHECK(fcntl(fd, F_SETFL, O_NONBLOCK) == -1);
+ CHECK(errno == ENOTCAPABLE);
+ errno = 0;
+ CHECK(fcntl(fd, F_SETFL, 0) == -1);
+ CHECK(errno == ENOTCAPABLE);
+ errno = 0;
+ CHECK(fcntl(fd, F_GETFL) == -1);
+ CHECK(errno == ENOTCAPABLE);
+}
+
+static void
+fcntl_tests_1(int fd)
+{
+ uint32_t fcntlrights;
+
+ CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL) == 0);
+ fcntlrights = 0;
+ CHECK(cap_fcntls_get(fd, &fcntlrights) == 0);
+ CHECK(fcntlrights == CAP_FCNTL_GETFL);
+
+ CHECK(cap_rights_limit(fd, CAP_ALL & ~CAP_FCNTL) == 0);
+
+ fcntlrights = CAP_FCNTL_ALL;
+ CHECK(cap_fcntls_get(fd, &fcntlrights) == 0);
+ CHECK(fcntlrights == 0);
+
+ errno = 0;
+ CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL | CAP_FCNTL_SETFL) == -1);
+ CHECK(errno == ENOTCAPABLE);
+ fcntlrights = CAP_FCNTL_ALL;
+ CHECK(cap_fcntls_get(fd, &fcntlrights) == 0);
+ CHECK(fcntlrights == 0);
+ errno = 0;
+ CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL) == -1);
+ CHECK(errno == ENOTCAPABLE);
+ fcntlrights = CAP_FCNTL_ALL;
+ CHECK(cap_fcntls_get(fd, &fcntlrights) == 0);
+ CHECK(fcntlrights == 0);
+
+ CHECK(fcntl(fd, F_GETFD) == 0);
+ CHECK(fcntl(fd, F_SETFD, FD_CLOEXEC) == 0);
+ CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC);
+ CHECK(fcntl(fd, F_SETFD, 0) == 0);
+ CHECK(fcntl(fd, F_GETFD) == 0);
+
+ errno = 0;
+ CHECK(fcntl(fd, F_GETFL) == -1);
+ CHECK(errno == ENOTCAPABLE);
+ errno = 0;
+ CHECK(fcntl(fd, F_SETFL, O_NONBLOCK) == -1);
+ CHECK(errno == ENOTCAPABLE);
+ errno = 0;
+ CHECK(fcntl(fd, F_SETFL, 0) == -1);
+ CHECK(errno == ENOTCAPABLE);
+ errno = 0;
+ CHECK(fcntl(fd, F_GETFL) == -1);
+ CHECK(errno == ENOTCAPABLE);
+}
+
+static void
+fcntl_tests_2(int fd)
+{
+ uint32_t fcntlrights;
+
+ CHECK(cap_rights_limit(fd, CAP_ALL & ~CAP_FCNTL) == 0);
+
+ fcntlrights = CAP_FCNTL_ALL;
+ CHECK(cap_fcntls_get(fd, &fcntlrights) == 0);
+ CHECK(fcntlrights == 0);
+
+ errno = 0;
+ CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL | CAP_FCNTL_SETFL) == -1);
+ CHECK(errno == ENOTCAPABLE);
+ fcntlrights = CAP_FCNTL_ALL;
+ CHECK(cap_fcntls_get(fd, &fcntlrights) == 0);
+ CHECK(fcntlrights == 0);
+ errno = 0;
+ CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL) == -1);
+ CHECK(errno == ENOTCAPABLE);
+ fcntlrights = CAP_FCNTL_ALL;
+ CHECK(cap_fcntls_get(fd, &fcntlrights) == 0);
+ CHECK(fcntlrights == 0);
+
+ CHECK(fcntl(fd, F_GETFD) == 0);
+ CHECK(fcntl(fd, F_SETFD, FD_CLOEXEC) == 0);
+ CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC);
+ CHECK(fcntl(fd, F_SETFD, 0) == 0);
+ CHECK(fcntl(fd, F_GETFD) == 0);
+
+ errno = 0;
+ CHECK(fcntl(fd, F_GETFL) == -1);
+ CHECK(errno == ENOTCAPABLE);
+ errno = 0;
+ CHECK(fcntl(fd, F_SETFL, O_NONBLOCK) == -1);
+ CHECK(errno == ENOTCAPABLE);
+ errno = 0;
+ CHECK(fcntl(fd, F_SETFL, 0) == -1);
+ CHECK(errno == ENOTCAPABLE);
+ errno = 0;
+ CHECK(fcntl(fd, F_GETFL) == -1);
+ CHECK(errno == ENOTCAPABLE);
+}
+
+static void
+fcntl_tests_send_0(int sock)
+{
+ int fd;
+
+ CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0);
+ CHECK(descriptor_send(sock, fd) == 0);
+ CHECK(close(fd) == 0);
+
+ CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0);
+ CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL | CAP_FCNTL_SETFL) == 0);
+ CHECK(descriptor_send(sock, fd) == 0);
+ CHECK(close(fd) == 0);
+
+ CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0);
+ CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL) == 0);
+ CHECK(descriptor_send(sock, fd) == 0);
+ CHECK(close(fd) == 0);
+
+ CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0);
+ CHECK(cap_fcntls_limit(fd, 0) == 0);
+ CHECK(descriptor_send(sock, fd) == 0);
+ CHECK(close(fd) == 0);
+}
+
+static void
+fcntl_tests_recv_0(int sock)
+{
+ uint32_t fcntlrights;
+ int fd;
+
+ CHECK(descriptor_recv(sock, &fd) == 0);
+
+ fcntlrights = 0;
+ CHECK(cap_fcntls_get(fd, &fcntlrights) == 0);
+ CHECK(fcntlrights == CAP_FCNTL_ALL);
+
+ CHECK(fcntl(fd, F_GETFD) == 0);
+ CHECK(fcntl(fd, F_SETFD, FD_CLOEXEC) == 0);
+ CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC);
+ CHECK(fcntl(fd, F_SETFD, 0) == 0);
+ CHECK(fcntl(fd, F_GETFD) == 0);
+
+ CHECK(fcntl(fd, F_GETFL) == O_RDWR);
+ CHECK(fcntl(fd, F_SETFL, O_NONBLOCK) == 0);
+ CHECK(fcntl(fd, F_GETFL) == (O_RDWR | O_NONBLOCK));
+ CHECK(fcntl(fd, F_SETFL, 0) == 0);
+ CHECK(fcntl(fd, F_GETFL) == O_RDWR);
+
+ CHECK(close(fd) == 0);
+
+ CHECK(descriptor_recv(sock, &fd) == 0);
+
+ fcntlrights = 0;
+ CHECK(cap_fcntls_get(fd, &fcntlrights) == 0);
+ CHECK(fcntlrights == (CAP_FCNTL_GETFL | CAP_FCNTL_SETFL));
+ CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL | CAP_FCNTL_SETFL) == 0);
+ fcntlrights = 0;
+ CHECK(cap_fcntls_get(fd, &fcntlrights) == 0);
+ CHECK(fcntlrights == (CAP_FCNTL_GETFL | CAP_FCNTL_SETFL));
+
+ CHECK(fcntl(fd, F_GETFD) == 0);
+ CHECK(fcntl(fd, F_SETFD, FD_CLOEXEC) == 0);
+ CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC);
+ CHECK(fcntl(fd, F_SETFD, 0) == 0);
+ CHECK(fcntl(fd, F_GETFD) == 0);
+
+ CHECK(fcntl(fd, F_GETFL) == O_RDWR);
+ CHECK(fcntl(fd, F_SETFL, O_NONBLOCK) == 0);
+ CHECK(fcntl(fd, F_GETFL) == (O_RDWR | O_NONBLOCK));
+ CHECK(fcntl(fd, F_SETFL, 0) == 0);
+ CHECK(fcntl(fd, F_GETFL) == O_RDWR);
+
+ CHECK(close(fd) == 0);
+
+ CHECK(descriptor_recv(sock, &fd) == 0);
+
+ fcntlrights = 0;
+ CHECK(cap_fcntls_get(fd, &fcntlrights) == 0);
+ CHECK(fcntlrights == CAP_FCNTL_GETFL);
+ errno = 0;
+ CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL | CAP_FCNTL_SETFL) == -1);
+ CHECK(errno == ENOTCAPABLE);
+ fcntlrights = 0;
+ CHECK(cap_fcntls_get(fd, &fcntlrights) == 0);
+ CHECK(fcntlrights == CAP_FCNTL_GETFL);
+ CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL) == 0);
+ fcntlrights = 0;
+ CHECK(cap_fcntls_get(fd, &fcntlrights) == 0);
+ CHECK(fcntlrights == CAP_FCNTL_GETFL);
+
+ CHECK(fcntl(fd, F_GETFD) == 0);
+ CHECK(fcntl(fd, F_SETFD, FD_CLOEXEC) == 0);
+ CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC);
+ CHECK(fcntl(fd, F_SETFD, 0) == 0);
+ CHECK(fcntl(fd, F_GETFD) == 0);
+
+ CHECK(fcntl(fd, F_GETFL) == O_RDWR);
+ errno = 0;
+ CHECK(fcntl(fd, F_SETFL, O_NONBLOCK) == -1);
+ CHECK(errno == ENOTCAPABLE);
+ CHECK(fcntl(fd, F_GETFL) == O_RDWR);
+ errno = 0;
+ CHECK(fcntl(fd, F_SETFL, 0) == -1);
+ CHECK(errno == ENOTCAPABLE);
+ CHECK(fcntl(fd, F_GETFL) == O_RDWR);
+
+ CHECK(close(fd) == 0);
+
+ CHECK(descriptor_recv(sock, &fd) == 0);
+
+ fcntlrights = 0;
+ CHECK(cap_fcntls_get(fd, &fcntlrights) == 0);
+ CHECK(fcntlrights == 0);
+ errno = 0;
+ CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL | CAP_FCNTL_SETFL) == -1);
+ CHECK(errno == ENOTCAPABLE);
+ fcntlrights = 0;
+ CHECK(cap_fcntls_get(fd, &fcntlrights) == 0);
+ CHECK(fcntlrights == 0);
+ errno = 0;
+ CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL) == -1);
+ CHECK(errno == ENOTCAPABLE);
+ fcntlrights = 0;
+ CHECK(cap_fcntls_get(fd, &fcntlrights) == 0);
+ CHECK(fcntlrights == 0);
+ errno = 0;
+ CHECK(cap_fcntls_limit(fd, CAP_FCNTL_SETFL) == -1);
+ CHECK(errno == ENOTCAPABLE);
+ fcntlrights = 0;
+ CHECK(cap_fcntls_get(fd, &fcntlrights) == 0);
+ CHECK(fcntlrights == 0);
+
+ CHECK(fcntl(fd, F_GETFD) == 0);
+ CHECK(fcntl(fd, F_SETFD, FD_CLOEXEC) == 0);
+ CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC);
+ CHECK(fcntl(fd, F_SETFD, 0) == 0);
+ CHECK(fcntl(fd, F_GETFD) == 0);
+
+ errno = 0;
+ CHECK(fcntl(fd, F_GETFL) == -1);
+ CHECK(errno == ENOTCAPABLE);
+ errno = 0;
+ CHECK(fcntl(fd, F_SETFL, O_NONBLOCK) == -1);
+ CHECK(errno == ENOTCAPABLE);
+ errno = 0;
+ CHECK(fcntl(fd, F_SETFL, 0) == -1);
+ CHECK(errno == ENOTCAPABLE);
+ errno = 0;
+ CHECK(fcntl(fd, F_GETFL) == -1);
+ CHECK(errno == ENOTCAPABLE);
+
+ CHECK(close(fd) == 0);
+}
+
+int
+main(void)
+{
+ int fd, pfd, sp[2];
+ pid_t pid;
+
+ printf("1..870\n");
+
+ CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0);
+ fcntl_tests_0(fd);
+ CHECK(close(fd) == 0);
+
+ CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0);
+ fcntl_tests_1(fd);
+ CHECK(close(fd) == 0);
+
+ CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0);
+ fcntl_tests_2(fd);
+ CHECK(close(fd) == 0);
+
+ /* Child inherits descriptor and operates on it first. */
+ CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0);
+ CHECK((pid = fork()) >= 0);
+ if (pid == 0) {
+ fcntl_tests_0(fd);
+ CHECK(close(fd) == 0);
+ exit(0);
+ } else {
+ CHECK(waitpid(pid, NULL, 0) == pid);
+ fcntl_tests_0(fd);
+ }
+ CHECK(close(fd) == 0);
+
+ /* Child inherits descriptor, but operates on it after parent. */
+ CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0);
+ CHECK((pid = fork()) >= 0);
+ if (pid == 0) {
+ sleep(1);
+ fcntl_tests_0(fd);
+ CHECK(close(fd) == 0);
+ exit(0);
+ } else {
+ fcntl_tests_0(fd);
+ CHECK(waitpid(pid, NULL, 0) == pid);
+ }
+ CHECK(close(fd) == 0);
+
+ /* Child inherits descriptor and operates on it first. */
+ CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0);
+ CHECK((pid = pdfork(&pfd, 0)) >= 0);
+ if (pid == 0) {
+ fcntl_tests_1(fd);
+ exit(0);
+ } else {
+ CHECK(pdwait(pfd) == 0);
+/*
+ It fails with EBADF, which I believe is a bug.
+ CHECK(close(pfd) == 0);
+*/
+ fcntl_tests_1(fd);
+ }
+ CHECK(close(fd) == 0);
+
+ /* Child inherits descriptor, but operates on it after parent. */
+ CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0);
+ CHECK((pid = pdfork(&pfd, 0)) >= 0);
+ if (pid == 0) {
+ sleep(1);
+ fcntl_tests_1(fd);
+ exit(0);
+ } else {
+ fcntl_tests_1(fd);
+ CHECK(pdwait(pfd) == 0);
+/*
+ It fails with EBADF, which I believe is a bug.
+ CHECK(close(pfd) == 0);
+*/
+ }
+ CHECK(close(fd) == 0);
+
+ /* Child inherits descriptor and operates on it first. */
+ CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0);
+ CHECK((pid = fork()) >= 0);
+ if (pid == 0) {
+ fcntl_tests_2(fd);
+ exit(0);
+ } else {
+ CHECK(waitpid(pid, NULL, 0) == pid);
+ fcntl_tests_2(fd);
+ }
+ CHECK(close(fd) == 0);
+
+ /* Child inherits descriptor, but operates on it after parent. */
+ CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0);
+ CHECK((pid = fork()) >= 0);
+ if (pid == 0) {
+ sleep(1);
+ fcntl_tests_2(fd);
+ exit(0);
+ } else {
+ fcntl_tests_2(fd);
+ CHECK(waitpid(pid, NULL, 0) == pid);
+ }
+ CHECK(close(fd) == 0);
+
+ /* Send descriptors from parent to child. */
+ CHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, sp) == 0);
+ CHECK((pid = fork()) >= 0);
+ if (pid == 0) {
+ CHECK(close(sp[0]) == 0);
+ fcntl_tests_recv_0(sp[1]);
+ CHECK(close(sp[1]) == 0);
+ exit(0);
+ } else {
+ CHECK(close(sp[1]) == 0);
+ fcntl_tests_send_0(sp[0]);
+ CHECK(waitpid(pid, NULL, 0) == pid);
+ CHECK(close(sp[0]) == 0);
+ }
+
+ /* Send descriptors from child to parent. */
+ CHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, sp) == 0);
+ CHECK((pid = fork()) >= 0);
+ if (pid == 0) {
+ CHECK(close(sp[0]) == 0);
+ fcntl_tests_send_0(sp[1]);
+ CHECK(close(sp[1]) == 0);
+ exit(0);
+ } else {
+ CHECK(close(sp[1]) == 0);
+ fcntl_tests_recv_0(sp[0]);
+ CHECK(waitpid(pid, NULL, 0) == pid);
+ CHECK(close(sp[0]) == 0);
+ }
+
+ exit(0);
+}
diff --git a/tools/regression/capsicum/syscalls/cap_getmode.c b/tools/regression/capsicum/syscalls/cap_getmode.c
new file mode 100644
index 0000000..1a38202
--- /dev/null
+++ b/tools/regression/capsicum/syscalls/cap_getmode.c
@@ -0,0 +1,167 @@
+/*-
+ * Copyright (c) 2012 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Pawel Jakub Dawidek under sponsorship from
+ * the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/capability.h>
+#include <sys/procdesc.h>
+#include <sys/wait.h>
+
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "misc.h"
+
+int
+main(void)
+{
+ unsigned int mode;
+ pid_t pid;
+ int pfd;
+
+ printf("1..27\n");
+
+ mode = 666;
+ CHECK(cap_getmode(&mode) == 0);
+ /* If cap_getmode() succeeded mode should be modified. */
+ CHECK(mode != 666);
+ /* We are not in capability mode. */
+ CHECK(mode == 0);
+
+ /* Expect EFAULT. */
+ errno = 0;
+ CHECK(cap_getmode(NULL) == -1);
+ CHECK(errno == EFAULT);
+ errno = 0;
+ CHECK(cap_getmode((void *)(uintptr_t)0xdeadc0de) == -1);
+ CHECK(errno == EFAULT);
+
+ /* If parent is not in capability mode, child after fork() also won't be. */
+ pid = fork();
+ switch (pid) {
+ case -1:
+ err(1, "fork() failed");
+ case 0:
+ mode = 666;
+ CHECK(cap_getmode(&mode) == 0);
+ /* If cap_getmode() succeeded mode should be modified. */
+ CHECK(mode != 666);
+ /* We are not in capability mode. */
+ CHECK(mode == 0);
+ exit(0);
+ default:
+ if (waitpid(pid, NULL, 0) == -1)
+ err(1, "waitpid() failed");
+ }
+
+ /* If parent is not in capability mode, child after pdfork() also won't be. */
+ pid = pdfork(&pfd, 0);
+ switch (pid) {
+ case -1:
+ err(1, "pdfork() failed");
+ case 0:
+ mode = 666;
+ CHECK(cap_getmode(&mode) == 0);
+ /* If cap_getmode() succeeded mode should be modified. */
+ CHECK(mode != 666);
+ /* We are not in capability mode. */
+ CHECK(mode == 0);
+ exit(0);
+ default:
+ if (pdwait(pfd) == -1)
+ err(1, "pdwait() failed");
+ close(pfd);
+ }
+
+ /* In capability mode... */
+
+ CHECK(cap_enter() == 0);
+
+ mode = 666;
+ CHECK(cap_getmode(&mode) == 0);
+ /* If cap_getmode() succeeded mode should be modified. */
+ CHECK(mode != 666);
+ /* We are in capability mode. */
+ CHECK(mode == 1);
+
+ /* Expect EFAULT. */
+ errno = 0;
+ CHECK(cap_getmode(NULL) == -1);
+ CHECK(errno == EFAULT);
+ errno = 0;
+ CHECK(cap_getmode((void *)(uintptr_t)0xdeadc0de) == -1);
+ CHECK(errno == EFAULT);
+
+ /* If parent is in capability mode, child after fork() also will be. */
+ pid = fork();
+ switch (pid) {
+ case -1:
+ err(1, "fork() failed");
+ case 0:
+ mode = 666;
+ CHECK(cap_getmode(&mode) == 0);
+ /* If cap_getmode() succeeded mode should be modified. */
+ CHECK(mode != 666);
+ /* We are in capability mode. */
+ CHECK(mode == 1);
+ exit(0);
+ default:
+ /*
+ * wait(2) and friends are not permitted in the capability mode,
+ * so we can only just wait for a while.
+ */
+ sleep(1);
+ }
+
+ /* If parent is in capability mode, child after pdfork() also will be. */
+ pid = pdfork(&pfd, 0);
+ switch (pid) {
+ case -1:
+ err(1, "pdfork() failed");
+ case 0:
+ mode = 666;
+ CHECK(cap_getmode(&mode) == 0);
+ /* If cap_getmode() succeeded mode should be modified. */
+ CHECK(mode != 666);
+ /* We are in capability mode. */
+ CHECK(mode == 1);
+ exit(0);
+ default:
+ if (pdwait(pfd) == -1)
+ err(1, "pdwait() failed");
+ close(pfd);
+ }
+
+ exit(0);
+}
diff --git a/tools/regression/capsicum/syscalls/cap_ioctls_limit.c b/tools/regression/capsicum/syscalls/cap_ioctls_limit.c
new file mode 100644
index 0000000..753aa71
--- /dev/null
+++ b/tools/regression/capsicum/syscalls/cap_ioctls_limit.c
@@ -0,0 +1,462 @@
+/*-
+ * Copyright (c) 2012 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Pawel Jakub Dawidek under sponsorship from
+ * the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/capability.h>
+#include <sys/ioctl.h>
+#include <sys/procdesc.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "misc.h"
+
+static void
+ioctl_tests_0(int fd)
+{
+ unsigned long cmds[2];
+
+ CHECK(cap_ioctls_get(fd, NULL, 0) == CAP_IOCTLS_ALL);
+
+ CHECK(fcntl(fd, F_GETFD) == 0);
+ CHECK(ioctl(fd, FIOCLEX) == 0);
+ CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC);
+ CHECK(ioctl(fd, FIONCLEX) == 0);
+ CHECK(fcntl(fd, F_GETFD) == 0);
+
+ cmds[0] = FIOCLEX;
+ cmds[1] = FIONCLEX;
+ CHECK(cap_ioctls_limit(fd, cmds, nitems(cmds)) == 0);
+ cmds[0] = cmds[1] = 0;
+ CHECK(cap_ioctls_get(fd, cmds, nitems(cmds)) == nitems(cmds));
+ CHECK((cmds[0] == FIOCLEX && cmds[1] == FIONCLEX) ||
+ (cmds[0] == FIONCLEX && cmds[1] == FIOCLEX));
+ cmds[0] = FIOCLEX;
+ cmds[1] = FIONCLEX;
+ CHECK(cap_ioctls_limit(fd, cmds, nitems(cmds)) == 0);
+ cmds[0] = cmds[1] = 0;
+ CHECK(cap_ioctls_get(fd, cmds, 1) == nitems(cmds));
+ CHECK(cmds[0] == FIOCLEX || cmds[0] == FIONCLEX);
+ CHECK(cmds[1] == 0);
+
+ CHECK(fcntl(fd, F_GETFD) == 0);
+ CHECK(ioctl(fd, FIOCLEX) == 0);
+ CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC);
+ CHECK(ioctl(fd, FIONCLEX) == 0);
+ CHECK(fcntl(fd, F_GETFD) == 0);
+
+ cmds[0] = FIOCLEX;
+ CHECK(cap_ioctls_limit(fd, cmds, 1) == 0);
+ cmds[0] = cmds[1] = 0;
+ CHECK(cap_ioctls_get(fd, cmds, nitems(cmds)) == 1);
+ CHECK(cmds[0] == FIOCLEX);
+ cmds[0] = FIOCLEX;
+ cmds[1] = FIONCLEX;
+ errno = 0;
+ CHECK(cap_ioctls_limit(fd, cmds, nitems(cmds)) == -1);
+ CHECK(errno == ENOTCAPABLE);
+ cmds[0] = cmds[1] = 0;
+ CHECK(cap_ioctls_get(fd, cmds, nitems(cmds)) == 1);
+ CHECK(cmds[0] == FIOCLEX);
+
+ CHECK(fcntl(fd, F_GETFD) == 0);
+ CHECK(ioctl(fd, FIOCLEX) == 0);
+ CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC);
+ errno = 0;
+ CHECK(ioctl(fd, FIONCLEX) == -1);
+ CHECK(errno == ENOTCAPABLE);
+ CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC);
+ CHECK(fcntl(fd, F_SETFD, 0) == 0);
+ CHECK(fcntl(fd, F_GETFD) == 0);
+
+ CHECK(cap_ioctls_limit(fd, NULL, 0) == 0);
+ CHECK(cap_ioctls_get(fd, cmds, nitems(cmds)) == 0);
+ cmds[0] = FIOCLEX;
+ errno = 0;
+ CHECK(cap_ioctls_limit(fd, cmds, 1) == -1);
+ CHECK(errno == ENOTCAPABLE);
+ CHECK(cap_ioctls_get(fd, cmds, nitems(cmds)) == 0);
+
+ CHECK(fcntl(fd, F_GETFD) == 0);
+ errno = 0;
+ CHECK(ioctl(fd, FIOCLEX) == -1);
+ CHECK(errno == ENOTCAPABLE);
+ CHECK(fcntl(fd, F_GETFD) == 0);
+ CHECK(fcntl(fd, F_SETFD, FD_CLOEXEC) == 0);
+ CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC);
+ errno = 0;
+ CHECK(ioctl(fd, FIONCLEX) == -1);
+ CHECK(errno == ENOTCAPABLE);
+ CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC);
+ CHECK(fcntl(fd, F_SETFD, 0) == 0);
+ CHECK(fcntl(fd, F_GETFD) == 0);
+}
+
+static void
+ioctl_tests_1(int fd)
+{
+ unsigned long cmds[2];
+
+ cmds[0] = FIOCLEX;
+ CHECK(cap_ioctls_limit(fd, cmds, 1) == 0);
+ cmds[0] = cmds[1] = 0;
+ CHECK(cap_ioctls_get(fd, cmds, nitems(cmds)) == 1);
+ CHECK(cmds[0] == FIOCLEX);
+ CHECK(cmds[1] == 0);
+
+ CHECK(cap_rights_limit(fd, CAP_ALL & ~CAP_IOCTL) == 0);
+ CHECK(cap_ioctls_get(fd, cmds, nitems(cmds)) == 0);
+
+ cmds[0] = FIOCLEX;
+ cmds[1] = FIONCLEX;
+ errno = 0;
+ CHECK(cap_ioctls_limit(fd, cmds, nitems(cmds)) == -1);
+ CHECK(errno == ENOTCAPABLE);
+ CHECK(cap_ioctls_get(fd, cmds, nitems(cmds)) == 0);
+ cmds[0] = FIOCLEX;
+ errno = 0;
+ CHECK(cap_ioctls_limit(fd, cmds, 1) == -1);
+ CHECK(errno == ENOTCAPABLE);
+ CHECK(cap_ioctls_get(fd, cmds, nitems(cmds)) == 0);
+
+ CHECK(fcntl(fd, F_GETFD) == 0);
+ errno = 0;
+ CHECK(ioctl(fd, FIOCLEX) == -1);
+ CHECK(errno == ENOTCAPABLE);
+ CHECK(fcntl(fd, F_GETFD) == 0);
+ CHECK(fcntl(fd, F_SETFD, FD_CLOEXEC) == 0);
+ CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC);
+ errno = 0;
+ CHECK(ioctl(fd, FIONCLEX) == -1);
+ CHECK(errno == ENOTCAPABLE);
+ CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC);
+ CHECK(fcntl(fd, F_SETFD, 0) == 0);
+ CHECK(fcntl(fd, F_GETFD) == 0);
+}
+
+static void
+ioctl_tests_2(int fd)
+{
+ unsigned long cmds[2];
+
+ CHECK(cap_rights_limit(fd, CAP_ALL & ~CAP_IOCTL) == 0);
+ CHECK(cap_ioctls_get(fd, cmds, nitems(cmds)) == 0);
+
+ cmds[0] = FIOCLEX;
+ cmds[1] = FIONCLEX;
+ errno = 0;
+ CHECK(cap_ioctls_limit(fd, cmds, nitems(cmds)) == -1);
+ CHECK(errno == ENOTCAPABLE);
+ CHECK(cap_ioctls_get(fd, cmds, nitems(cmds)) == 0);
+ cmds[0] = FIOCLEX;
+ errno = 0;
+ CHECK(cap_ioctls_limit(fd, cmds, 1) == -1);
+ CHECK(errno == ENOTCAPABLE);
+ CHECK(cap_ioctls_get(fd, cmds, nitems(cmds)) == 0);
+
+ CHECK(fcntl(fd, F_GETFD) == 0);
+ errno = 0;
+ CHECK(ioctl(fd, FIOCLEX) == -1);
+ CHECK(errno == ENOTCAPABLE);
+ CHECK(fcntl(fd, F_GETFD) == 0);
+ CHECK(fcntl(fd, F_SETFD, FD_CLOEXEC) == 0);
+ CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC);
+ errno = 0;
+ CHECK(ioctl(fd, FIONCLEX) == -1);
+ CHECK(errno == ENOTCAPABLE);
+ CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC);
+ CHECK(fcntl(fd, F_SETFD, 0) == 0);
+ CHECK(fcntl(fd, F_GETFD) == 0);
+}
+
+static void
+ioctl_tests_send_0(int sock)
+{
+ unsigned long cmds[2];
+ int fd;
+
+ CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0);
+ CHECK(descriptor_send(sock, fd) == 0);
+ CHECK(close(fd) == 0);
+
+ CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0);
+ cmds[0] = FIOCLEX;
+ cmds[1] = FIONCLEX;
+ CHECK(cap_ioctls_limit(fd, cmds, nitems(cmds)) == 0);
+ CHECK(descriptor_send(sock, fd) == 0);
+ CHECK(close(fd) == 0);
+
+ CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0);
+ cmds[0] = FIOCLEX;
+ CHECK(cap_ioctls_limit(fd, cmds, 1) == 0);
+ CHECK(descriptor_send(sock, fd) == 0);
+ CHECK(close(fd) == 0);
+
+ CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0);
+ CHECK(cap_ioctls_limit(fd, NULL, 0) == 0);
+ CHECK(descriptor_send(sock, fd) == 0);
+ CHECK(close(fd) == 0);
+}
+
+static void
+ioctl_tests_recv_0(int sock)
+{
+ unsigned long cmds[2];
+ int fd;
+
+ CHECK(descriptor_recv(sock, &fd) == 0);
+
+ CHECK(cap_ioctls_get(fd, NULL, 0) == CAP_IOCTLS_ALL);
+
+ CHECK(fcntl(fd, F_GETFD) == 0);
+ CHECK(ioctl(fd, FIOCLEX) == 0);
+ CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC);
+ CHECK(ioctl(fd, FIONCLEX) == 0);
+ CHECK(fcntl(fd, F_GETFD) == 0);
+
+ CHECK(close(fd) == 0);
+
+ CHECK(descriptor_recv(sock, &fd) == 0);
+
+ cmds[0] = cmds[1] = 0;
+ CHECK(cap_ioctls_get(fd, cmds, nitems(cmds)) == nitems(cmds));
+ CHECK((cmds[0] == FIOCLEX && cmds[1] == FIONCLEX) ||
+ (cmds[0] == FIONCLEX && cmds[1] == FIOCLEX));
+
+ CHECK(fcntl(fd, F_GETFD) == 0);
+ CHECK(ioctl(fd, FIOCLEX) == 0);
+ CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC);
+ CHECK(ioctl(fd, FIONCLEX) == 0);
+ CHECK(fcntl(fd, F_GETFD) == 0);
+
+ CHECK(close(fd) == 0);
+
+ CHECK(descriptor_recv(sock, &fd) == 0);
+
+ cmds[0] = cmds[1] = 0;
+ CHECK(cap_ioctls_get(fd, cmds, nitems(cmds)) == 1);
+ CHECK(cmds[0] == FIOCLEX);
+
+ CHECK(fcntl(fd, F_GETFD) == 0);
+ CHECK(ioctl(fd, FIOCLEX) == 0);
+ CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC);
+ errno = 0;
+ CHECK(ioctl(fd, FIONCLEX) == -1);
+ CHECK(errno == ENOTCAPABLE);
+ CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC);
+ CHECK(fcntl(fd, F_SETFD, 0) == 0);
+ CHECK(fcntl(fd, F_GETFD) == 0);
+
+ CHECK(close(fd) == 0);
+
+ CHECK(descriptor_recv(sock, &fd) == 0);
+
+ CHECK(cap_ioctls_get(fd, cmds, nitems(cmds)) == 0);
+
+ CHECK(fcntl(fd, F_GETFD) == 0);
+ errno = 0;
+ CHECK(ioctl(fd, FIOCLEX) == -1);
+ CHECK(errno == ENOTCAPABLE);
+ CHECK(fcntl(fd, F_GETFD) == 0);
+ CHECK(fcntl(fd, F_SETFD, FD_CLOEXEC) == 0);
+ CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC);
+ errno = 0;
+ CHECK(ioctl(fd, FIONCLEX) == -1);
+ CHECK(errno == ENOTCAPABLE);
+ CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC);
+ CHECK(fcntl(fd, F_SETFD, 0) == 0);
+ CHECK(fcntl(fd, F_GETFD) == 0);
+
+ CHECK(close(fd) == 0);
+}
+
+int
+main(void)
+{
+ int fd, pfd, sp[2];
+ pid_t pid;
+
+ printf("1..607\n");
+
+ CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0);
+ ioctl_tests_0(fd);
+ CHECK(close(fd) == 0);
+
+ CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0);
+ ioctl_tests_1(fd);
+ CHECK(close(fd) == 0);
+
+ CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0);
+ ioctl_tests_2(fd);
+ CHECK(close(fd) == 0);
+
+ /* Child inherits descriptor and operates on it first. */
+ CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0);
+ pid = fork();
+ switch (pid) {
+ case -1:
+ err(1, "fork() failed");
+ case 0:
+ ioctl_tests_0(fd);
+ CHECK(close(fd) == 0);
+ exit(0);
+ default:
+ if (waitpid(pid, NULL, 0) == -1)
+ err(1, "waitpid() failed");
+ ioctl_tests_0(fd);
+ }
+ CHECK(close(fd) == 0);
+
+ /* Child inherits descriptor, but operates on it after parent. */
+ CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0);
+ pid = fork();
+ switch (pid) {
+ case -1:
+ err(1, "fork() failed");
+ case 0:
+ sleep(1);
+ ioctl_tests_0(fd);
+ CHECK(close(fd) == 0);
+ exit(0);
+ default:
+ ioctl_tests_0(fd);
+ if (waitpid(pid, NULL, 0) == -1)
+ err(1, "waitpid() failed");
+ }
+ CHECK(close(fd) == 0);
+
+ /* Child inherits descriptor and operates on it first. */
+ CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0);
+ pid = pdfork(&pfd, 0);
+ switch (pid) {
+ case -1:
+ err(1, "pdfork() failed");
+ case 0:
+ ioctl_tests_1(fd);
+ exit(0);
+ default:
+ if (pdwait(pfd) == -1)
+ err(1, "pdwait() failed");
+ close(pfd);
+ ioctl_tests_1(fd);
+ }
+ CHECK(close(fd) == 0);
+
+ /* Child inherits descriptor, but operates on it after parent. */
+ CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0);
+ pid = pdfork(&pfd, 0);
+ switch (pid) {
+ case -1:
+ err(1, "pdfork() failed");
+ case 0:
+ sleep(1);
+ ioctl_tests_1(fd);
+ exit(0);
+ default:
+ ioctl_tests_1(fd);
+ if (pdwait(pfd) == -1)
+ err(1, "pdwait() failed");
+ close(pfd);
+ }
+ CHECK(close(fd) == 0);
+
+ /* Child inherits descriptor and operates on it first. */
+ CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0);
+ pid = fork();
+ switch (pid) {
+ case -1:
+ err(1, "fork() failed");
+ case 0:
+ ioctl_tests_2(fd);
+ exit(0);
+ default:
+ if (waitpid(pid, NULL, 0) == -1)
+ err(1, "waitpid() failed");
+ ioctl_tests_2(fd);
+ }
+ CHECK(close(fd) == 0);
+
+ /* Child inherits descriptor, but operates on it after parent. */
+ CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0);
+ pid = fork();
+ switch (pid) {
+ case -1:
+ err(1, "fork() failed");
+ case 0:
+ sleep(1);
+ ioctl_tests_2(fd);
+ exit(0);
+ default:
+ ioctl_tests_2(fd);
+ if (waitpid(pid, NULL, 0) == -1)
+ err(1, "waitpid() failed");
+ }
+ CHECK(close(fd) == 0);
+
+ /* Send descriptors from parent to child. */
+ CHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, sp) == 0);
+ CHECK((pid = fork()) >= 0);
+ if (pid == 0) {
+ CHECK(close(sp[0]) == 0);
+ ioctl_tests_recv_0(sp[1]);
+ CHECK(close(sp[1]) == 0);
+ exit(0);
+ } else {
+ CHECK(close(sp[1]) == 0);
+ ioctl_tests_send_0(sp[0]);
+ CHECK(waitpid(pid, NULL, 0) == pid);
+ CHECK(close(sp[0]) == 0);
+ }
+
+ /* Send descriptors from child to parent. */
+ CHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, sp) == 0);
+ CHECK((pid = fork()) >= 0);
+ if (pid == 0) {
+ CHECK(close(sp[0]) == 0);
+ ioctl_tests_send_0(sp[1]);
+ CHECK(close(sp[1]) == 0);
+ exit(0);
+ } else {
+ CHECK(close(sp[1]) == 0);
+ ioctl_tests_recv_0(sp[0]);
+ CHECK(waitpid(pid, NULL, 0) == pid);
+ CHECK(close(sp[0]) == 0);
+ }
+
+ exit(0);
+}
diff --git a/tools/regression/capsicum/syscalls/misc.c b/tools/regression/capsicum/syscalls/misc.c
new file mode 100644
index 0000000..303a911
--- /dev/null
+++ b/tools/regression/capsicum/syscalls/misc.c
@@ -0,0 +1,128 @@
+/*-
+ * Copyright (c) 2012 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Pawel Jakub Dawidek under sponsorship from
+ * the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/select.h>
+#include <sys/socket.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <strings.h>
+
+#include "misc.h"
+
+int
+pdwait(int pfd)
+{
+ fd_set fdset;
+
+ FD_ZERO(&fdset);
+ FD_SET(pfd, &fdset);
+
+ return (select(pfd + 1, NULL, &fdset, NULL, NULL) == -1 ? -1 : 0);
+}
+
+int
+descriptor_send(int sock, int fd)
+{
+ unsigned char ctrl[CMSG_SPACE(sizeof(fd))];
+ struct msghdr msg;
+ struct cmsghdr *cmsg;
+
+ assert(sock >= 0);
+ assert(fd >= 0);
+
+ bzero(&msg, sizeof(msg));
+ bzero(&ctrl, sizeof(ctrl));
+
+ msg.msg_iov = NULL;
+ msg.msg_iovlen = 0;
+ msg.msg_control = ctrl;
+ msg.msg_controllen = sizeof(ctrl);
+
+ cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
+ bcopy(&fd, CMSG_DATA(cmsg), sizeof(fd));
+
+ if (sendmsg(sock, &msg, 0) == -1)
+ return (errno);
+
+ return (0);
+}
+
+int
+descriptor_recv(int sock, int *fdp)
+{
+ unsigned char ctrl[CMSG_SPACE(sizeof(*fdp))];
+ struct msghdr msg;
+ struct cmsghdr *cmsg;
+ struct iovec iov;
+ int val;
+
+ assert(sock >= 0);
+ assert(fdp != NULL);
+
+ bzero(&msg, sizeof(msg));
+ bzero(&ctrl, sizeof(ctrl));
+
+#if 1
+ /*
+ * This doesn't really make sense, as we don't plan to receive any
+ * data, but if no buffer is provided and recv(2) returns 0 without
+ * control message. Must be kernel bug.
+ */
+ iov.iov_base = &val;
+ iov.iov_len = sizeof(val);
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+#else
+ msg.msg_iov = NULL;
+ msg.msg_iovlen = 0;
+#endif
+ msg.msg_control = ctrl;
+ msg.msg_controllen = sizeof(ctrl);
+
+ if (recvmsg(sock, &msg, 0) == -1)
+ return (errno);
+
+ cmsg = CMSG_FIRSTHDR(&msg);
+ if (cmsg == NULL || cmsg->cmsg_level != SOL_SOCKET ||
+ cmsg->cmsg_type != SCM_RIGHTS) {
+ return (EINVAL);
+ }
+ bcopy(CMSG_DATA(cmsg), fdp, sizeof(*fdp));
+
+ return (0);
+}
diff --git a/tools/regression/capsicum/syscalls/misc.h b/tools/regression/capsicum/syscalls/misc.h
new file mode 100644
index 0000000..4968c3e
--- /dev/null
+++ b/tools/regression/capsicum/syscalls/misc.h
@@ -0,0 +1,62 @@
+/*-
+ * Copyright (c) 2012 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Pawel Jakub Dawidek under sponsorship from
+ * the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MISC_H_
+#define _MISC_H_
+
+#define OK() do { \
+ int _serrno = errno; \
+ printf("ok # line %u\n", __LINE__); \
+ fflush(stdout); \
+ errno = _serrno; \
+} while (0)
+#define NOK() do { \
+ int _serrno = errno; \
+ printf("not ok # line %u\n", __LINE__); \
+ fflush(stdout); \
+ errno = _serrno; \
+} while (0)
+#define CHECK(cond) do { \
+ if ((cond)) \
+ OK(); \
+ else \
+ NOK(); \
+} while (0)
+
+/*
+ * This can be removed once pdwait4(2) is implemented.
+ */
+int pdwait(int pfd);
+
+int descriptor_send(int sock, int fd);
+int descriptor_recv(int sock, int *fdp);
+
+#endif /* !_MISC_H_ */
diff --git a/tools/regression/lib/libc/gen/test-wordexp.c b/tools/regression/lib/libc/gen/test-wordexp.c
index df8f885..d94f870 100644
--- a/tools/regression/lib/libc/gen/test-wordexp.c
+++ b/tools/regression/lib/libc/gen/test-wordexp.c
@@ -208,6 +208,25 @@ main(int argc, char *argv[])
assert(strcmp(we.we_wordv[1], "world") == 0);
assert(we.we_wordv[2] == NULL);
wordfree(&we);
+ sa.sa_handler = SIG_DFL;
+ r = sigaction(SIGCHLD, &sa, NULL);
+ assert(r == 0);
+
+ /*
+ * With IFS set to a non-default value (without depending on whether
+ * IFS is inherited or not).
+ */
+ r = setenv("IFS", ":", 1);
+ assert(r == 0);
+ r = wordexp("hello world", &we, 0);
+ assert(r == 0);
+ assert(we.we_wordc == 2);
+ assert(strcmp(we.we_wordv[0], "hello") == 0);
+ assert(strcmp(we.we_wordv[1], "world") == 0);
+ assert(we.we_wordv[2] == NULL);
+ wordfree(&we);
+ r = unsetenv("IFS");
+ assert(r == 0);
printf("PASS wordexp()\n");
printf("PASS wordfree()\n");
diff --git a/tools/regression/lib/libc/stdio/Makefile b/tools/regression/lib/libc/stdio/Makefile
index d62ac84..f496f73 100644
--- a/tools/regression/lib/libc/stdio/Makefile
+++ b/tools/regression/lib/libc/stdio/Makefile
@@ -1,6 +1,8 @@
# $FreeBSD$
-TESTS= test-getdelim test-perror test-print-positional test-printbasic test-printfloat test-scanfloat
+TESTS= test-fmemopen test-getdelim test-open_memstream test-open_wmemstream \
+ test-perror test-print-positional test-printbasic test-printfloat \
+ test-scanfloat
CFLAGS+= -lm
.PHONY: tests
diff --git a/tools/regression/lib/libc/stdio/test-open_memstream.c b/tools/regression/lib/libc/stdio/test-open_memstream.c
new file mode 100644
index 0000000..1a168c6
--- /dev/null
+++ b/tools/regression/lib/libc/stdio/test-open_memstream.c
@@ -0,0 +1,203 @@
+/*-
+ * Copyright (c) 2013 Advanced Computing Technologies LLC
+ * Written by: John H. Baldwin <jhb@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+
+static char *buf;
+static size_t len;
+
+static void
+assert_stream(const char *contents)
+{
+ if (strlen(contents) != len)
+ printf("bad length %zd for \"%s\"\n", len, contents);
+ else if (strncmp(buf, contents, strlen(contents)) != 0)
+ printf("bad buffer \"%s\" for \"%s\"\n", buf, contents);
+}
+
+static void
+open_group_test(void)
+{
+ FILE *fp;
+ off_t eob;
+
+ fp = open_memstream(&buf, &len);
+ if (fp == NULL)
+ err(1, "failed to open stream");
+
+ fprintf(fp, "hello my world");
+ fflush(fp);
+ assert_stream("hello my world");
+ eob = ftello(fp);
+ rewind(fp);
+ fprintf(fp, "good-bye");
+ fseeko(fp, eob, SEEK_SET);
+ fclose(fp);
+ assert_stream("good-bye world");
+ free(buf);
+}
+
+static void
+simple_tests(void)
+{
+ static const char zerobuf[] =
+ { 'f', 'o', 'o', 0, 0, 0, 0, 'b', 'a', 'r', 0 };
+ char c;
+ FILE *fp;
+
+ fp = open_memstream(&buf, NULL);
+ if (fp != NULL)
+ errx(1, "did not fail to open stream");
+ else if (errno != EINVAL)
+ err(1, "incorrect error for bad length pointer");
+ fp = open_memstream(NULL, &len);
+ if (fp != NULL)
+ errx(1, "did not fail to open stream");
+ else if (errno != EINVAL)
+ err(1, "incorrect error for bad buffer pointer");
+ fp = open_memstream(&buf, &len);
+ if (fp == NULL)
+ err(1, "failed to open stream");
+ fflush(fp);
+ assert_stream("");
+ if (fwide(fp, 0) >= 0)
+ printf("stream is not byte-oriented\n");
+
+ fprintf(fp, "fo");
+ fflush(fp);
+ assert_stream("fo");
+ fputc('o', fp);
+ fflush(fp);
+ assert_stream("foo");
+ rewind(fp);
+ fflush(fp);
+ assert_stream("");
+ fseek(fp, 0, SEEK_END);
+ fflush(fp);
+ assert_stream("foo");
+
+ /*
+ * Test seeking out past the current end. Should zero-fill the
+ * intermediate area.
+ */
+ fseek(fp, 4, SEEK_END);
+ fprintf(fp, "bar");
+ fflush(fp);
+
+ /*
+ * Can't use assert_stream() here since this should contain
+ * embedded null characters.
+ */
+ if (len != 10)
+ printf("bad length %zd for zero-fill test\n", len);
+ else if (memcmp(buf, zerobuf, sizeof(zerobuf)) != 0)
+ printf("bad buffer for zero-fill test\n");
+
+ fseek(fp, 3, SEEK_SET);
+ fprintf(fp, " in ");
+ fflush(fp);
+ assert_stream("foo in ");
+ fseek(fp, 0, SEEK_END);
+ fflush(fp);
+ assert_stream("foo in bar");
+
+ rewind(fp);
+ if (fread(&c, sizeof(c), 1, fp) != 0)
+ printf("fread did not fail\n");
+ else if (!ferror(fp))
+ printf("error indicator not set after fread\n");
+ else
+ clearerr(fp);
+
+ fseek(fp, 4, SEEK_SET);
+ fprintf(fp, "bar baz");
+ fclose(fp);
+ assert_stream("foo bar baz");
+ free(buf);
+}
+
+static void
+seek_tests(void)
+{
+ FILE *fp;
+
+ fp = open_memstream(&buf, &len);
+ if (fp == NULL)
+ err(1, "failed to open stream");
+#define SEEK_FAIL(offset, whence, error) do { \
+ errno = 0; \
+ if (fseeko(fp, (offset), (whence)) == 0) \
+ printf("fseeko(%s, %s) did not fail, set pos to %jd\n", \
+ __STRING(offset), __STRING(whence), \
+ (intmax_t)ftello(fp)); \
+ else if (errno != (error)) \
+ printf("fseeko(%s, %s) failed with %d rather than %s\n",\
+ __STRING(offset), __STRING(whence), errno, \
+ __STRING(error)); \
+} while (0)
+
+#define SEEK_OK(offset, whence, result) do { \
+ if (fseeko(fp, (offset), (whence)) != 0) \
+ printf("fseeko(%s, %s) failed: %s\n", \
+ __STRING(offset), __STRING(whence), strerror(errno)); \
+ else if (ftello(fp) != (result)) \
+ printf("fseeko(%s, %s) seeked to %jd rather than %s\n", \
+ __STRING(offset), __STRING(whence), \
+ (intmax_t)ftello(fp), __STRING(result)); \
+} while (0)
+
+ SEEK_FAIL(-1, SEEK_SET, EINVAL);
+ SEEK_FAIL(-1, SEEK_CUR, EINVAL);
+ SEEK_FAIL(-1, SEEK_END, EINVAL);
+ fprintf(fp, "foo");
+ SEEK_OK(-1, SEEK_CUR, 2);
+ SEEK_OK(0, SEEK_SET, 0);
+ SEEK_OK(-1, SEEK_END, 2);
+ SEEK_OK(OFF_MAX - 1, SEEK_SET, OFF_MAX - 1);
+ SEEK_FAIL(2, SEEK_CUR, EOVERFLOW);
+ fclose(fp);
+}
+
+int
+main(int ac, char **av)
+{
+
+ open_group_test();
+ simple_tests();
+ seek_tests();
+ return (0);
+}
diff --git a/tools/regression/lib/libc/stdio/test-open_memstream.t b/tools/regression/lib/libc/stdio/test-open_memstream.t
new file mode 100644
index 0000000..8bdfd03
--- /dev/null
+++ b/tools/regression/lib/libc/stdio/test-open_memstream.t
@@ -0,0 +1,10 @@
+#!/bin/sh
+# $FreeBSD$
+
+cd `dirname $0`
+
+executable=`basename $0 .t`
+
+make $executable 2>&1 > /dev/null
+
+exec ./$executable
diff --git a/tools/regression/lib/libc/stdio/test-open_wmemstream.c b/tools/regression/lib/libc/stdio/test-open_wmemstream.c
new file mode 100644
index 0000000..4cd0ab9
--- /dev/null
+++ b/tools/regression/lib/libc/stdio/test-open_wmemstream.c
@@ -0,0 +1,203 @@
+/*-
+ * Copyright (c) 2013 Advanced Computing Technologies LLC
+ * Written by: John H. Baldwin <jhb@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+
+static wchar_t *buf;
+static size_t len;
+
+static void
+assert_stream(const wchar_t *contents)
+{
+ if (wcslen(contents) != len)
+ printf("bad length %zd for \"%ls\"\n", len, contents);
+ else if (wcsncmp(buf, contents, wcslen(contents)) != 0)
+ printf("bad buffer \"%ls\" for \"%ls\"\n", buf, contents);
+}
+
+static void
+open_group_test(void)
+{
+ FILE *fp;
+ off_t eob;
+
+ fp = open_wmemstream(&buf, &len);
+ if (fp == NULL)
+ err(1, "failed to open stream");
+
+ fwprintf(fp, L"hello my world");
+ fflush(fp);
+ assert_stream(L"hello my world");
+ eob = ftello(fp);
+ rewind(fp);
+ fwprintf(fp, L"good-bye");
+ fseeko(fp, eob, SEEK_SET);
+ fclose(fp);
+ assert_stream(L"good-bye world");
+ free(buf);
+}
+
+static void
+simple_tests(void)
+{
+ static const wchar_t zerobuf[] =
+ { L'f', L'o', L'o', 0, 0, 0, 0, L'b', L'a', L'r', 0 };
+ wchar_t c;
+ FILE *fp;
+
+ fp = open_wmemstream(&buf, NULL);
+ if (fp != NULL)
+ errx(1, "did not fail to open stream");
+ else if (errno != EINVAL)
+ err(1, "incorrect error for bad length pointer");
+ fp = open_wmemstream(NULL, &len);
+ if (fp != NULL)
+ errx(1, "did not fail to open stream");
+ else if (errno != EINVAL)
+ err(1, "incorrect error for bad buffer pointer");
+ fp = open_wmemstream(&buf, &len);
+ if (fp == NULL)
+ err(1, "failed to open stream");
+ fflush(fp);
+ assert_stream(L"");
+ if (fwide(fp, 0) <= 0)
+ printf("stream is not wide-oriented\n");
+
+ fwprintf(fp, L"fo");
+ fflush(fp);
+ assert_stream(L"fo");
+ fputwc(L'o', fp);
+ fflush(fp);
+ assert_stream(L"foo");
+ rewind(fp);
+ fflush(fp);
+ assert_stream(L"");
+ fseek(fp, 0, SEEK_END);
+ fflush(fp);
+ assert_stream(L"foo");
+
+ /*
+ * Test seeking out past the current end. Should zero-fill the
+ * intermediate area.
+ */
+ fseek(fp, 4, SEEK_END);
+ fwprintf(fp, L"bar");
+ fflush(fp);
+
+ /*
+ * Can't use assert_stream() here since this should contain
+ * embedded null characters.
+ */
+ if (len != 10)
+ printf("bad length %zd for zero-fill test\n", len);
+ else if (memcmp(buf, zerobuf, sizeof(zerobuf)) != 0)
+ printf("bad buffer for zero-fill test\n");
+
+ fseek(fp, 3, SEEK_SET);
+ fwprintf(fp, L" in ");
+ fflush(fp);
+ assert_stream(L"foo in ");
+ fseek(fp, 0, SEEK_END);
+ fflush(fp);
+ assert_stream(L"foo in bar");
+
+ rewind(fp);
+ if (fread(&c, sizeof(c), 1, fp) != 0)
+ printf("fread did not fail\n");
+ else if (!ferror(fp))
+ printf("error indicator not set after fread\n");
+ else
+ clearerr(fp);
+
+ fseek(fp, 4, SEEK_SET);
+ fwprintf(fp, L"bar baz");
+ fclose(fp);
+ assert_stream(L"foo bar baz");
+ free(buf);
+}
+
+static void
+seek_tests(void)
+{
+ FILE *fp;
+
+ fp = open_wmemstream(&buf, &len);
+ if (fp == NULL)
+ err(1, "failed to open stream");
+#define SEEK_FAIL(offset, whence, error) do { \
+ errno = 0; \
+ if (fseeko(fp, (offset), (whence)) == 0) \
+ printf("fseeko(%s, %s) did not fail, set pos to %jd\n", \
+ __STRING(offset), __STRING(whence), \
+ (intmax_t)ftello(fp)); \
+ else if (errno != (error)) \
+ printf("fseeko(%s, %s) failed with %d rather than %s\n",\
+ __STRING(offset), __STRING(whence), errno, \
+ __STRING(error)); \
+} while (0)
+
+#define SEEK_OK(offset, whence, result) do { \
+ if (fseeko(fp, (offset), (whence)) != 0) \
+ printf("fseeko(%s, %s) failed: %s\n", \
+ __STRING(offset), __STRING(whence), strerror(errno)); \
+ else if (ftello(fp) != (result)) \
+ printf("fseeko(%s, %s) seeked to %jd rather than %s\n", \
+ __STRING(offset), __STRING(whence), \
+ (intmax_t)ftello(fp), __STRING(result)); \
+} while (0)
+
+ SEEK_FAIL(-1, SEEK_SET, EINVAL);
+ SEEK_FAIL(-1, SEEK_CUR, EINVAL);
+ SEEK_FAIL(-1, SEEK_END, EINVAL);
+ fwprintf(fp, L"foo");
+ SEEK_OK(-1, SEEK_CUR, 2);
+ SEEK_OK(0, SEEK_SET, 0);
+ SEEK_OK(-1, SEEK_END, 2);
+ SEEK_OK(OFF_MAX - 1, SEEK_SET, OFF_MAX - 1);
+ SEEK_FAIL(2, SEEK_CUR, EOVERFLOW);
+ fclose(fp);
+}
+
+int
+main(int ac, char **av)
+{
+
+ open_group_test();
+ simple_tests();
+ seek_tests();
+ return (0);
+}
diff --git a/tools/regression/lib/libc/stdio/test-open_wmemstream.t b/tools/regression/lib/libc/stdio/test-open_wmemstream.t
new file mode 100644
index 0000000..8bdfd03
--- /dev/null
+++ b/tools/regression/lib/libc/stdio/test-open_wmemstream.t
@@ -0,0 +1,10 @@
+#!/bin/sh
+# $FreeBSD$
+
+cd `dirname $0`
+
+executable=`basename $0 .t`
+
+make $executable 2>&1 > /dev/null
+
+exec ./$executable
diff --git a/tools/regression/pjdfstest/Makefile b/tools/regression/pjdfstest/Makefile
index ca789fe..eca598f 100644
--- a/tools/regression/pjdfstest/Makefile
+++ b/tools/regression/pjdfstest/Makefile
@@ -6,7 +6,7 @@ ${PROG}: ${PROG}.c
@OSTYPE=`uname`; \
CFLAGS=-D__OS_$${OSTYPE}__; \
if [ $$OSTYPE = "FreeBSD" ]; then \
- CFLAGS="$$CFLAGS -DHAS_LCHMOD -DHAS_CHFLAGS -DHAS_FCHFLAGS -DHAS_LCHFLAGS -DHAS_FREEBSD_ACL"; \
+ CFLAGS="$$CFLAGS -DHAS_LCHMOD -DHAS_CHFLAGS -DHAS_FCHFLAGS -DHAS_CHFLAGSAT -DHAS_LCHFLAGS -DHAS_FREEBSD_ACL -DHAS_BINDAT -DHAS_CONNECTAT"; \
elif [ $$OSTYPE = "SunOS" ]; then \
CFLAGS="$$CFLAGS -DHAS_TRUNCATE64 -DHAS_STAT64"; \
CFLAGS="$$CFLAGS -lsocket"; \
diff --git a/tools/regression/pjdfstest/pjdfstest.c b/tools/regression/pjdfstest/pjdfstest.c
index c08fafd..fd19084 100644
--- a/tools/regression/pjdfstest/pjdfstest.c
+++ b/tools/regression/pjdfstest/pjdfstest.c
@@ -82,7 +82,13 @@ enum action {
ACTION_MKNOD,
ACTION_MKNODAT,
ACTION_BIND,
+#ifdef HAS_BINDAT
+ ACTION_BINDAT,
+#endif
ACTION_CONNECT,
+#ifdef HAS_CONNECTAT
+ ACTION_CONNECTAT,
+#endif
ACTION_CHMOD,
ACTION_FCHMOD,
#ifdef HAS_LCHMOD
@@ -99,6 +105,9 @@ enum action {
#ifdef HAS_FCHFLAGS
ACTION_FCHFLAGS,
#endif
+#ifdef HAS_CHFLAGSAT
+ ACTION_CHFLAGSAT,
+#endif
#ifdef HAS_LCHFLAGS
ACTION_LCHFLAGS,
#endif
@@ -154,7 +163,13 @@ static struct syscall_desc syscalls[] = {
{ "mknod", ACTION_MKNOD, { TYPE_STRING, TYPE_STRING, TYPE_NUMBER, TYPE_NUMBER, TYPE_NUMBER, TYPE_NONE} },
{ "mknodat", ACTION_MKNODAT, { TYPE_DESCRIPTOR, TYPE_STRING, TYPE_STRING, TYPE_NUMBER, TYPE_NUMBER, TYPE_NUMBER, TYPE_NONE} },
{ "bind", ACTION_BIND, { TYPE_STRING, TYPE_NONE } },
+#ifdef HAS_BINDAT
+ { "bindat", ACTION_BINDAT, { TYPE_DESCRIPTOR, TYPE_STRING, TYPE_NONE } },
+#endif
{ "connect", ACTION_CONNECT, { TYPE_STRING, TYPE_NONE } },
+#ifdef HAS_CONNECTAT
+ { "connectat", ACTION_CONNECTAT, { TYPE_DESCRIPTOR, TYPE_STRING, TYPE_NONE } },
+#endif
{ "chmod", ACTION_CHMOD, { TYPE_STRING, TYPE_NUMBER, TYPE_NONE } },
{ "fchmod", ACTION_FCHMOD, { TYPE_DESCRIPTOR, TYPE_NUMBER, TYPE_NONE } },
#ifdef HAS_LCHMOD
@@ -171,6 +186,9 @@ static struct syscall_desc syscalls[] = {
#ifdef HAS_FCHFLAGS
{ "fchflags", ACTION_FCHFLAGS, { TYPE_DESCRIPTOR, TYPE_STRING, TYPE_NONE } },
#endif
+#ifdef HAS_CHFLAGSAT
+ { "chflagsat", ACTION_CHFLAGSAT, { TYPE_DESCRIPTOR, TYPE_STRING, TYPE_STRING, TYPE_STRING, TYPE_NONE } },
+#endif
#ifdef HAS_LCHFLAGS
{ "lchflags", ACTION_LCHFLAGS, { TYPE_STRING, TYPE_STRING, TYPE_NONE } },
#endif
@@ -294,6 +312,11 @@ static struct flag linkat_flags[] = {
{ 0, NULL }
};
+static struct flag chflagsat_flags[] = {
+ { AT_SYMLINK_NOFOLLOW, "AT_SYMLINK_NOFOLLOW" },
+ { 0, NULL }
+};
+
static struct flag fchmodat_flags[] = {
{ AT_SYMLINK_NOFOLLOW, "AT_SYMLINK_NOFOLLOW" },
{ 0, NULL }
@@ -732,6 +755,22 @@ call_syscall(struct syscall_desc *scall, char *argv[])
rval = bind(rval, (struct sockaddr *)&sunx, sizeof(sunx));
break;
}
+#ifdef HAS_BINDAT
+ case ACTION_BINDAT:
+ {
+ struct sockaddr_un sunx;
+
+ sunx.sun_family = AF_UNIX;
+ strncpy(sunx.sun_path, STR(1), sizeof(sunx.sun_path) - 1);
+ sunx.sun_path[sizeof(sunx.sun_path) - 1] = '\0';
+ rval = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (rval < 0)
+ break;
+ rval = bindat(NUM(0), rval, (struct sockaddr *)&sunx,
+ sizeof(sunx));
+ break;
+ }
+#endif
case ACTION_CONNECT:
{
struct sockaddr_un sunx;
@@ -745,6 +784,22 @@ call_syscall(struct syscall_desc *scall, char *argv[])
rval = connect(rval, (struct sockaddr *)&sunx, sizeof(sunx));
break;
}
+#ifdef HAS_CONNECTAT
+ case ACTION_CONNECTAT:
+ {
+ struct sockaddr_un sunx;
+
+ sunx.sun_family = AF_UNIX;
+ strncpy(sunx.sun_path, STR(1), sizeof(sunx.sun_path) - 1);
+ sunx.sun_path[sizeof(sunx.sun_path) - 1] = '\0';
+ rval = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (rval < 0)
+ break;
+ rval = connectat(NUM(0), rval, (struct sockaddr *)&sunx,
+ sizeof(sunx));
+ break;
+ }
+#endif
case ACTION_CHMOD:
rval = chmod(STR(0), (mode_t)NUM(1));
break;
@@ -785,9 +840,17 @@ call_syscall(struct syscall_desc *scall, char *argv[])
(unsigned long)str2flags(chflags_flags, STR(1)));
break;
#endif
+#ifdef HAS_CHFLAGSAT
+ case ACTION_CHFLAGSAT:
+ rval = chflagsat(NUM(0), STR(1),
+ (unsigned long)str2flags(chflags_flags, STR(2)),
+ (int)str2flags(chflagsat_flags, STR(3)));
+ break;
+#endif
#ifdef HAS_LCHFLAGS
case ACTION_LCHFLAGS:
- rval = lchflags(STR(0), (int)str2flags(chflags_flags, STR(1)));
+ rval = lchflags(STR(0),
+ (unsigned long)str2flags(chflags_flags, STR(1)));
break;
#endif
case ACTION_TRUNCATE:
diff --git a/tools/regression/pjdfstest/tests/misc.sh b/tools/regression/pjdfstest/tests/misc.sh
index 0d3ce35..8978b5f 100644
--- a/tools/regression/pjdfstest/tests/misc.sh
+++ b/tools/regression/pjdfstest/tests/misc.sh
@@ -89,7 +89,7 @@ todo()
namegen()
{
- echo "pjdfstest_`dd if=/dev/urandom bs=1k count=1 2>/dev/null | openssl md5`"
+ echo "pjdfstest_`dd if=/dev/urandom bs=1k count=1 2>/dev/null | openssl md5 | awk '{print $NF}'`"
}
namegen_len()
@@ -98,7 +98,7 @@ namegen_len()
name=""
while :; do
- namepart="`dd if=/dev/urandom bs=64 count=1 2>/dev/null | openssl md5`"
+ namepart="`dd if=/dev/urandom bs=64 count=1 2>/dev/null | openssl md5 | awk '{print $NF}'`"
name="${name}${namepart}"
curlen=`printf "%s" "${name}" | wc -c`
[ ${curlen} -lt ${len} ] || break
diff --git a/tools/regression/security/cap_test/cap_test_capabilities.c b/tools/regression/security/cap_test/cap_test_capabilities.c
index 450ad88..d4347ee 100644
--- a/tools/regression/security/cap_test/cap_test_capabilities.c
+++ b/tools/regression/security/cap_test/cap_test_capabilities.c
@@ -1,8 +1,12 @@
/*-
* Copyright (c) 2009-2011 Robert N. M. Watson
* Copyright (c) 2011 Jonathan Anderson
+ * Copyright (c) 2012 FreeBSD Foundation
* All rights reserved.
*
+ * Portions of this software were developed by Pawel Jakub Dawidek under
+ * sponsorship from the FreeBSD Foundation.
+ *
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@@ -43,6 +47,7 @@ __FBSDID("$FreeBSD$");
#include <err.h>
#include <fcntl.h>
#include <poll.h>
+#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -60,14 +65,20 @@ __FBSDID("$FreeBSD$");
*/
#define CHECK_RESULT(syscall, rights_needed, succeeded) do { \
if ((rights & (rights_needed)) == (rights_needed)) { \
- if (!(succeeded)) \
+ if (succeeded) { \
+ if (success == -1) \
+ success = PASSED; \
+ } else { \
SYSCALL_FAIL(syscall, "failed"); \
+ } \
} else { \
- if (succeeded) \
+ if (succeeded) { \
FAILX("%s:\tsucceeded when it shouldn't have" \
- " (rights 0x%jx)", #syscall, rights); \
- else if (errno != ENOTCAPABLE) \
+ " (rights 0x%jx)", #syscall, \
+ (uintmax_t)rights); \
+ } else if (errno != ENOTCAPABLE) { \
SYSCALL_FAIL(syscall, "errno != ENOTCAPABLE"); \
+ } \
} \
errno = 0; \
} while (0)
@@ -79,8 +90,11 @@ __FBSDID("$FreeBSD$");
if ((rights & (rights_needed)) == (rights_needed)) { \
if (p == MAP_FAILED) \
SYSCALL_FAIL(mmap, "failed"); \
- else \
+ else { \
(void)munmap(p, getpagesize()); \
+ if (success == -1) \
+ success = PASSED; \
+ } \
} else { \
if (p != MAP_FAILED) { \
FAILX("%s:\tsucceeded when it shouldn't have" \
@@ -97,96 +111,231 @@ __FBSDID("$FreeBSD$");
* make sure only those rights work.
*/
static int
-try_file_ops(int fd, cap_rights_t rights)
+try_file_ops(int filefd, int dirfd, cap_rights_t rights)
{
struct stat sb;
struct statfs sf;
- int fd_cap, fd_capcap;
+ cap_rights_t erights;
+ int fd_cap, fd_capcap, dfd_cap;
ssize_t ssize, ssize2;
off_t off;
void *p;
char ch;
int ret, is_nfs;
struct pollfd pollfd;
- int success = PASSED;
+ int success = -1;
- REQUIRE(fstatfs(fd, &sf));
- is_nfs = (strncmp("nfs", sf.f_fstypename, sizeof(sf.f_fstypename))
- == 0);
+ REQUIRE(fstatfs(filefd, &sf));
+ is_nfs = (strcmp("nfs", sf.f_fstypename) == 0);
- REQUIRE(fd_cap = cap_new(fd, rights));
+ REQUIRE(fd_cap = cap_new(filefd, rights));
+ CHECK(cap_getrights(fd_cap, &erights) == 0);
+ CHECK(rights == erights);
REQUIRE(fd_capcap = cap_new(fd_cap, rights));
+ CHECK(cap_getrights(fd_capcap, &erights) == 0);
+ CHECK(rights == erights);
CHECK(fd_capcap != fd_cap);
-
- pollfd.fd = fd_cap;
- pollfd.events = POLLIN | POLLERR | POLLHUP;
- pollfd.revents = 0;
+ REQUIRE(dfd_cap = cap_new(dirfd, rights));
+ CHECK(cap_getrights(dfd_cap, &erights) == 0);
+ CHECK(rights == erights);
ssize = read(fd_cap, &ch, sizeof(ch));
- CHECK_RESULT(read, CAP_READ | CAP_SEEK, ssize >= 0);
-
- ssize = pread(fd_cap, &ch, sizeof(ch), 0);
- ssize2 = pread(fd_cap, &ch, sizeof(ch), 0);
- CHECK_RESULT(pread, CAP_READ, ssize >= 0);
- CHECK(ssize == ssize2);
+ CHECK_RESULT(read, CAP_READ, ssize >= 0);
ssize = write(fd_cap, &ch, sizeof(ch));
- CHECK_RESULT(write, CAP_WRITE | CAP_SEEK, ssize >= 0);
-
- ssize = pwrite(fd_cap, &ch, sizeof(ch), 0);
- CHECK_RESULT(pwrite, CAP_WRITE, ssize >= 0);
+ CHECK_RESULT(write, CAP_WRITE, ssize >= 0);
off = lseek(fd_cap, 0, SEEK_SET);
CHECK_RESULT(lseek, CAP_SEEK, off >= 0);
- /*
- * Note: this is not expected to work over NFS.
- */
- ret = fchflags(fd_cap, UF_NODUMP);
- CHECK_RESULT(fchflags, CAP_FCHFLAGS,
- (ret == 0) || (is_nfs && (errno == EOPNOTSUPP)));
+ ssize = pread(fd_cap, &ch, sizeof(ch), 0);
+ ssize2 = pread(fd_cap, &ch, sizeof(ch), 0);
+ CHECK_RESULT(pread, CAP_PREAD, ssize >= 0);
+ CHECK(ssize == ssize2);
- ret = fstat(fd_cap, &sb);
- CHECK_RESULT(fstat, CAP_FSTAT, ret == 0);
+ ssize = pwrite(fd_cap, &ch, sizeof(ch), 0);
+ CHECK_RESULT(pwrite, CAP_PWRITE, ssize >= 0);
+
+ p = mmap(NULL, getpagesize(), PROT_NONE, MAP_SHARED, fd_cap, 0);
+ CHECK_MMAP_RESULT(CAP_MMAP);
p = mmap(NULL, getpagesize(), PROT_READ, MAP_SHARED, fd_cap, 0);
- CHECK_MMAP_RESULT(CAP_MMAP | CAP_READ);
+ CHECK_MMAP_RESULT(CAP_MMAP_R);
p = mmap(NULL, getpagesize(), PROT_WRITE, MAP_SHARED, fd_cap, 0);
- CHECK_MMAP_RESULT(CAP_MMAP | CAP_WRITE);
+ CHECK_MMAP_RESULT(CAP_MMAP_W);
p = mmap(NULL, getpagesize(), PROT_EXEC, MAP_SHARED, fd_cap, 0);
- CHECK_MMAP_RESULT(CAP_MMAP | CAP_MAPEXEC);
+ CHECK_MMAP_RESULT(CAP_MMAP_X);
p = mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE, MAP_SHARED,
fd_cap, 0);
- CHECK_MMAP_RESULT(CAP_MMAP | CAP_READ | CAP_WRITE);
+ CHECK_MMAP_RESULT(CAP_MMAP_RW);
p = mmap(NULL, getpagesize(), PROT_READ | PROT_EXEC, MAP_SHARED,
fd_cap, 0);
- CHECK_MMAP_RESULT(CAP_MMAP | CAP_READ | CAP_MAPEXEC);
+ CHECK_MMAP_RESULT(CAP_MMAP_RX);
p = mmap(NULL, getpagesize(), PROT_EXEC | PROT_WRITE, MAP_SHARED,
fd_cap, 0);
- CHECK_MMAP_RESULT(CAP_MMAP | CAP_MAPEXEC | CAP_WRITE);
+ CHECK_MMAP_RESULT(CAP_MMAP_WX);
p = mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_SHARED, fd_cap, 0);
- CHECK_MMAP_RESULT(CAP_MMAP | CAP_READ | CAP_WRITE | CAP_MAPEXEC);
+ CHECK_MMAP_RESULT(CAP_MMAP_RWX);
+
+ ret = openat(dfd_cap, "cap_create", O_CREAT | O_RDONLY, 0600);
+ CHECK_RESULT(openat(O_CREATE | O_RDONLY),
+ CAP_CREATE | CAP_READ | CAP_LOOKUP, ret >= 0);
+ CHECK(ret == -1 || close(ret) == 0);
+ CHECK(ret == -1 || unlinkat(dirfd, "cap_create", 0) == 0);
+ ret = openat(dfd_cap, "cap_create", O_CREAT | O_WRONLY | O_APPEND,
+ 0600);
+ CHECK_RESULT(openat(O_CREATE | O_WRONLY | O_APPEND),
+ CAP_CREATE | CAP_WRITE | CAP_LOOKUP, ret >= 0);
+ CHECK(ret == -1 || close(ret) == 0);
+ CHECK(ret == -1 || unlinkat(dirfd, "cap_create", 0) == 0);
+ ret = openat(dfd_cap, "cap_create", O_CREAT | O_RDWR | O_APPEND, 0600);
+ CHECK_RESULT(openat(O_CREATE | O_RDWR | O_APPEND),
+ CAP_CREATE | CAP_READ | CAP_WRITE | CAP_LOOKUP, ret >= 0);
+ CHECK(ret == -1 || close(ret) == 0);
+ CHECK(ret == -1 || unlinkat(dirfd, "cap_create", 0) == 0);
ret = fsync(fd_cap);
CHECK_RESULT(fsync, CAP_FSYNC, ret == 0);
+ ret = openat(dirfd, "cap_fsync", O_CREAT, 0600);
+ CHECK(ret >= 0);
+ CHECK(close(ret) == 0);
+ ret = openat(dfd_cap, "cap_fsync", O_FSYNC | O_RDONLY);
+ CHECK_RESULT(openat(O_FSYNC | O_RDONLY),
+ CAP_FSYNC | CAP_READ | CAP_LOOKUP, ret >= 0);
+ CHECK(ret == -1 || close(ret) == 0);
+ ret = openat(dfd_cap, "cap_fsync", O_FSYNC | O_WRONLY | O_APPEND);
+ CHECK_RESULT(openat(O_FSYNC | O_WRONLY | O_APPEND),
+ CAP_FSYNC | CAP_WRITE | CAP_LOOKUP, ret >= 0);
+ CHECK(ret == -1 || close(ret) == 0);
+ ret = openat(dfd_cap, "cap_fsync", O_FSYNC | O_RDWR | O_APPEND);
+ CHECK_RESULT(openat(O_FSYNC | O_RDWR | O_APPEND),
+ CAP_FSYNC | CAP_READ | CAP_WRITE | CAP_LOOKUP, ret >= 0);
+ CHECK(ret == -1 || close(ret) == 0);
+ ret = openat(dfd_cap, "cap_fsync", O_SYNC | O_RDONLY);
+ CHECK_RESULT(openat(O_SYNC | O_RDONLY),
+ CAP_FSYNC | CAP_READ | CAP_LOOKUP, ret >= 0);
+ CHECK(ret == -1 || close(ret) == 0);
+ ret = openat(dfd_cap, "cap_fsync", O_SYNC | O_WRONLY | O_APPEND);
+ CHECK_RESULT(openat(O_SYNC | O_WRONLY | O_APPEND),
+ CAP_FSYNC | CAP_WRITE | CAP_LOOKUP, ret >= 0);
+ CHECK(ret == -1 || close(ret) == 0);
+ ret = openat(dfd_cap, "cap_fsync", O_SYNC | O_RDWR | O_APPEND);
+ CHECK_RESULT(openat(O_SYNC | O_RDWR | O_APPEND),
+ CAP_FSYNC | CAP_READ | CAP_WRITE | CAP_LOOKUP, ret >= 0);
+ CHECK(ret == -1 || close(ret) == 0);
+ CHECK(unlinkat(dirfd, "cap_fsync", 0) == 0);
+
+ ret = ftruncate(fd_cap, 0);
+ CHECK_RESULT(ftruncate, CAP_FTRUNCATE, ret == 0);
+
+ ret = openat(dirfd, "cap_ftruncate", O_CREAT, 0600);
+ CHECK(ret >= 0);
+ CHECK(close(ret) == 0);
+ ret = openat(dfd_cap, "cap_ftruncate", O_TRUNC | O_RDONLY);
+ CHECK_RESULT(openat(O_TRUNC | O_RDONLY),
+ CAP_FTRUNCATE | CAP_READ | CAP_LOOKUP, ret >= 0);
+ CHECK(ret == -1 || close(ret) == 0);
+ ret = openat(dfd_cap, "cap_ftruncate", O_TRUNC | O_WRONLY);
+ CHECK_RESULT(openat(O_TRUNC | O_WRONLY),
+ CAP_FTRUNCATE | CAP_WRITE | CAP_LOOKUP, ret >= 0);
+ CHECK(ret == -1 || close(ret) == 0);
+ ret = openat(dfd_cap, "cap_ftruncate", O_TRUNC | O_RDWR);
+ CHECK_RESULT(openat(O_TRUNC | O_RDWR),
+ CAP_FTRUNCATE | CAP_READ | CAP_WRITE | CAP_LOOKUP, ret >= 0);
+ CHECK(ret == -1 || close(ret) == 0);
+ CHECK(unlinkat(dirfd, "cap_ftruncate", 0) == 0);
+
+ ret = openat(dfd_cap, "cap_create", O_CREAT | O_WRONLY, 0600);
+ CHECK_RESULT(openat(O_CREATE | O_WRONLY),
+ CAP_CREATE | CAP_WRITE | CAP_SEEK | CAP_LOOKUP, ret >= 0);
+ CHECK(ret == -1 || close(ret) == 0);
+ CHECK(ret == -1 || unlinkat(dirfd, "cap_create", 0) == 0);
+ ret = openat(dfd_cap, "cap_create", O_CREAT | O_RDWR, 0600);
+ CHECK_RESULT(openat(O_CREATE | O_RDWR),
+ CAP_CREATE | CAP_READ | CAP_WRITE | CAP_SEEK | CAP_LOOKUP,
+ ret >= 0);
+ CHECK(ret == -1 || close(ret) == 0);
+ CHECK(ret == -1 || unlinkat(dirfd, "cap_create", 0) == 0);
+
+ ret = openat(dirfd, "cap_fsync", O_CREAT, 0600);
+ CHECK(ret >= 0);
+ CHECK(close(ret) == 0);
+ ret = openat(dfd_cap, "cap_fsync", O_FSYNC | O_WRONLY);
+ CHECK_RESULT(openat(O_FSYNC | O_WRONLY),
+ CAP_FSYNC | CAP_WRITE | CAP_SEEK | CAP_LOOKUP, ret >= 0);
+ CHECK(ret == -1 || close(ret) == 0);
+ ret = openat(dfd_cap, "cap_fsync", O_FSYNC | O_RDWR);
+ CHECK_RESULT(openat(O_FSYNC | O_RDWR),
+ CAP_FSYNC | CAP_READ | CAP_WRITE | CAP_SEEK | CAP_LOOKUP, ret >= 0);
+ CHECK(ret == -1 || close(ret) == 0);
+ ret = openat(dfd_cap, "cap_fsync", O_SYNC | O_WRONLY);
+ CHECK_RESULT(openat(O_SYNC | O_WRONLY),
+ CAP_FSYNC | CAP_WRITE | CAP_SEEK | CAP_LOOKUP, ret >= 0);
+ CHECK(ret == -1 || close(ret) == 0);
+ ret = openat(dfd_cap, "cap_fsync", O_SYNC | O_RDWR);
+ CHECK_RESULT(openat(O_SYNC | O_RDWR),
+ CAP_FSYNC | CAP_READ | CAP_WRITE | CAP_SEEK | CAP_LOOKUP, ret >= 0);
+ CHECK(ret == -1 || close(ret) == 0);
+ CHECK(unlinkat(dirfd, "cap_fsync", 0) == 0);
+
+ /*
+ * Note: this is not expected to work over NFS.
+ */
+ ret = fchflags(fd_cap, UF_NODUMP);
+ CHECK_RESULT(fchflags, CAP_FCHFLAGS,
+ ret == 0 || (is_nfs && errno == EOPNOTSUPP));
+
+ ret = openat(dirfd, "cap_chflagsat", O_CREAT, 0600);
+ CHECK(ret >= 0);
+ CHECK(close(ret) == 0);
+ ret = chflagsat(dfd_cap, "cap_chflagsat", UF_NODUMP, 0);
+ CHECK_RESULT(chflagsat, CAP_CHFLAGSAT | CAP_LOOKUP, ret == 0);
+ CHECK(unlinkat(dirfd, "cap_chflagsat", 0) == 0);
+
ret = fchown(fd_cap, -1, -1);
CHECK_RESULT(fchown, CAP_FCHOWN, ret == 0);
+ ret = openat(dirfd, "cap_fchownat", O_CREAT, 0600);
+ CHECK(ret >= 0);
+ CHECK(close(ret) == 0);
+ ret = fchownat(dfd_cap, "cap_fchownat", -1, -1, 0);
+ CHECK_RESULT(fchownat, CAP_FCHOWN | CAP_LOOKUP, ret == 0);
+ CHECK(unlinkat(dirfd, "cap_fchownat", 0) == 0);
+
ret = fchmod(fd_cap, 0644);
CHECK_RESULT(fchmod, CAP_FCHMOD, ret == 0);
+ ret = openat(dirfd, "cap_fchmodat", O_CREAT, 0600);
+ CHECK(ret >= 0);
+ CHECK(close(ret) == 0);
+ ret = fchmodat(dfd_cap, "cap_fchmodat", 0600, 0);
+ CHECK_RESULT(fchmodat, CAP_FCHMOD | CAP_LOOKUP, ret == 0);
+ CHECK(unlinkat(dirfd, "cap_fchmodat", 0) == 0);
+
+ ret = fcntl(fd_cap, F_GETFL);
+ CHECK_RESULT(fcntl(F_GETFL), CAP_FCNTL, ret >= 0);
+ ret = fcntl(fd_cap, F_SETFL, ret);
+ CHECK_RESULT(fcntl(F_SETFL), CAP_FCNTL, ret == 0);
+
/* XXX flock */
- ret = ftruncate(fd_cap, 0);
- CHECK_RESULT(ftruncate, CAP_FTRUNCATE, ret == 0);
+ ret = fstat(fd_cap, &sb);
+ CHECK_RESULT(fstat, CAP_FSTAT, ret == 0);
+
+ ret = openat(dirfd, "cap_fstatat", O_CREAT, 0600);
+ CHECK(ret >= 0);
+ CHECK(close(ret) == 0);
+ ret = fstatat(dfd_cap, "cap_fstatat", &sb, 0);
+ CHECK_RESULT(fstatat, CAP_FSTAT | CAP_LOOKUP, ret == 0);
+ CHECK(unlinkat(dirfd, "cap_fstatat", 0) == 0);
ret = fstatfs(fd_cap, &sf);
CHECK_RESULT(fstatfs, CAP_FSTATFS, ret == 0);
@@ -197,6 +346,55 @@ try_file_ops(int fd, cap_rights_t rights)
ret = futimes(fd_cap, NULL);
CHECK_RESULT(futimes, CAP_FUTIMES, ret == 0);
+ ret = openat(dirfd, "cap_futimesat", O_CREAT, 0600);
+ CHECK(ret >= 0);
+ CHECK(close(ret) == 0);
+ ret = futimesat(dfd_cap, "cap_futimesat", NULL);
+ CHECK_RESULT(futimesat, CAP_FUTIMES | CAP_LOOKUP, ret == 0);
+ CHECK(unlinkat(dirfd, "cap_futimesat", 0) == 0);
+
+ ret = openat(dirfd, "cap_linkat_src", O_CREAT, 0600);
+ CHECK(ret >= 0);
+ CHECK(close(ret) == 0);
+ ret = linkat(dirfd, "cap_linkat_src", dfd_cap, "cap_linkat_dst", 0);
+ CHECK_RESULT(linkat, CAP_LINKAT | CAP_LOOKUP, ret == 0);
+ CHECK(unlinkat(dirfd, "cap_linkat_src", 0) == 0);
+ CHECK(ret == -1 || unlinkat(dirfd, "cap_linkat_dst", 0) == 0);
+
+ ret = mkdirat(dfd_cap, "cap_mkdirat", 0700);
+ CHECK_RESULT(mkdirat, CAP_MKDIRAT | CAP_LOOKUP, ret == 0);
+ CHECK(ret == -1 || unlinkat(dirfd, "cap_mkdirat", AT_REMOVEDIR) == 0);
+
+ ret = mkfifoat(dfd_cap, "cap_mkfifoat", 0600);
+ CHECK_RESULT(mkfifoat, CAP_MKFIFOAT | CAP_LOOKUP, ret == 0);
+ CHECK(ret == -1 || unlinkat(dirfd, "cap_mkfifoat", 0) == 0);
+
+ ret = mknodat(dfd_cap, "cap_mknodat", S_IFCHR | 0600, 0);
+ CHECK_RESULT(mknodat, CAP_MKNODAT | CAP_LOOKUP, ret == 0);
+ CHECK(ret == -1 || unlinkat(dirfd, "cap_mknodat", 0) == 0);
+
+ /* TODO: renameat(2) */
+
+ ret = symlinkat("test", dfd_cap, "cap_symlinkat");
+ CHECK_RESULT(symlinkat, CAP_SYMLINKAT | CAP_LOOKUP, ret == 0);
+ CHECK(ret == -1 || unlinkat(dirfd, "cap_symlinkat", 0) == 0);
+
+ ret = openat(dirfd, "cap_unlinkat", O_CREAT, 0600);
+ CHECK(ret >= 0);
+ CHECK(close(ret) == 0);
+ ret = unlinkat(dfd_cap, "cap_unlinkat", 0);
+ CHECK_RESULT(unlinkat, CAP_UNLINKAT | CAP_LOOKUP, ret == 0);
+ CHECK(ret == 0 || unlinkat(dirfd, "cap_unlinkat", 0) == 0);
+ ret = mkdirat(dirfd, "cap_unlinkat", 0700);
+ CHECK(ret == 0);
+ ret = unlinkat(dfd_cap, "cap_unlinkat", AT_REMOVEDIR);
+ CHECK_RESULT(unlinkat, CAP_UNLINKAT | CAP_LOOKUP, ret == 0);
+ CHECK(ret == 0 || unlinkat(dirfd, "cap_unlinkat", AT_REMOVEDIR) == 0);
+
+ pollfd.fd = fd_cap;
+ pollfd.events = POLLIN | POLLERR | POLLHUP;
+ pollfd.revents = 0;
+
ret = poll(&pollfd, 1, 0);
if (rights & CAP_POLL_EVENT)
CHECK((pollfd.revents & POLLNVAL) == 0);
@@ -205,79 +403,159 @@ try_file_ops(int fd, cap_rights_t rights)
/* XXX: select, kqueue */
- close (fd_cap);
+ close(fd_cap);
+ close(fd_capcap);
+
+ if (success == -1) {
+ fprintf(stderr, "No tests for rights 0x%jx.\n",
+ (uintmax_t)rights);
+ success = FAILED;
+ }
return (success);
}
-#define TRY(fd, rights) \
+#define TRY(rights) \
do { \
if (success == PASSED) \
- success = try_file_ops(fd, rights); \
+ success = try_file_ops(filefd, dirfd, (rights)); \
else \
/* We've already failed, but try the test anyway. */ \
- try_file_ops(fd, rights); \
+ try_file_ops(filefd, dirfd, (rights)); \
} while (0)
+#define KEEP_ERRNO(...) do { \
+ int _saved_errno = errno; \
+ __VA_ARGS__; \
+ errno = _saved_errno; \
+} while (0);
+
int
test_capabilities(void)
{
- int fd;
+ int filefd, dirfd, tmpfd;
int success = PASSED;
-
- fd = open("/tmp/cap_test_capabilities", O_RDWR | O_CREAT, 0644);
- if (fd < 0)
+ char file[] = "/tmp/cap_test.XXXXXXXXXX";
+ char dir[] = "/tmp/cap_test.XXXXXXXXXX";
+
+ filefd = mkstemp(file);
+ if (filefd < 0)
+ err(-1, "mkstemp");
+ if (mkdtemp(dir) == NULL) {
+ KEEP_ERRNO(unlink(file));
+ err(-1, "mkdtemp");
+ }
+ dirfd = open(dir, O_RDONLY | O_DIRECTORY);
+ if (dirfd == -1) {
+ KEEP_ERRNO(unlink(file));
+ KEEP_ERRNO(rmdir(dir));
err(-1, "open");
+ }
+ tmpfd = open("/tmp", O_RDONLY | O_DIRECTORY);
+ if (tmpfd == -1) {
+ KEEP_ERRNO(unlink(file));
+ KEEP_ERRNO(rmdir(dir));
+ err(-1, "open");
+ }
- if (cap_enter() < 0)
+ if (cap_enter() == -1) {
+ KEEP_ERRNO(unlink(file));
+ KEEP_ERRNO(rmdir(dir));
err(-1, "cap_enter");
-
- /* XXX: Really want to try all combinations. */
- TRY(fd, CAP_READ);
- TRY(fd, CAP_READ | CAP_SEEK);
- TRY(fd, CAP_WRITE);
- TRY(fd, CAP_WRITE | CAP_SEEK);
- TRY(fd, CAP_READ | CAP_WRITE);
- TRY(fd, CAP_READ | CAP_WRITE | CAP_SEEK);
- TRY(fd, CAP_SEEK);
- TRY(fd, CAP_FCHFLAGS);
- TRY(fd, CAP_IOCTL);
- TRY(fd, CAP_FSTAT);
- TRY(fd, CAP_MMAP);
- TRY(fd, CAP_MMAP | CAP_READ);
- TRY(fd, CAP_MMAP | CAP_WRITE);
- TRY(fd, CAP_MMAP | CAP_MAPEXEC);
- TRY(fd, CAP_MMAP | CAP_READ | CAP_WRITE);
- TRY(fd, CAP_MMAP | CAP_READ | CAP_MAPEXEC);
- TRY(fd, CAP_MMAP | CAP_MAPEXEC | CAP_WRITE);
- TRY(fd, CAP_MMAP | CAP_READ | CAP_WRITE | CAP_MAPEXEC);
- TRY(fd, CAP_FCNTL);
- TRY(fd, CAP_POST_EVENT);
- TRY(fd, CAP_POLL_EVENT);
- TRY(fd, CAP_FSYNC);
- TRY(fd, CAP_FCHOWN);
- TRY(fd, CAP_FCHMOD);
- TRY(fd, CAP_FTRUNCATE);
- TRY(fd, CAP_FLOCK);
- TRY(fd, CAP_FSTATFS);
- TRY(fd, CAP_FPATHCONF);
- TRY(fd, CAP_FUTIMES);
- TRY(fd, CAP_ACL_GET);
- TRY(fd, CAP_ACL_SET);
- TRY(fd, CAP_ACL_DELETE);
- TRY(fd, CAP_ACL_CHECK);
- TRY(fd, CAP_EXTATTR_GET);
- TRY(fd, CAP_EXTATTR_SET);
- TRY(fd, CAP_EXTATTR_DELETE);
- TRY(fd, CAP_EXTATTR_LIST);
- TRY(fd, CAP_MAC_GET);
- TRY(fd, CAP_MAC_SET);
-
- /*
- * Socket-specific.
- */
- TRY(fd, CAP_GETPEERNAME);
- TRY(fd, CAP_GETSOCKNAME);
- TRY(fd, CAP_ACCEPT);
+ }
+
+ TRY(CAP_READ);
+ TRY(CAP_WRITE);
+ TRY(CAP_SEEK);
+ TRY(CAP_PREAD);
+ TRY(CAP_PWRITE);
+ TRY(CAP_READ | CAP_WRITE);
+ TRY(CAP_PREAD | CAP_PWRITE);
+ TRY(CAP_MMAP);
+ TRY(CAP_MMAP_R);
+ TRY(CAP_MMAP_W);
+ TRY(CAP_MMAP_X);
+ TRY(CAP_MMAP_RW);
+ TRY(CAP_MMAP_RX);
+ TRY(CAP_MMAP_WX);
+ TRY(CAP_MMAP_RWX);
+ TRY(CAP_CREATE | CAP_READ | CAP_LOOKUP);
+ TRY(CAP_CREATE | CAP_WRITE | CAP_LOOKUP);
+ TRY(CAP_CREATE | CAP_READ | CAP_WRITE | CAP_LOOKUP);
+#ifdef TODO
+ TRY(CAP_FEXECVE);
+#endif
+ TRY(CAP_FSYNC);
+ TRY(CAP_FSYNC | CAP_READ | CAP_LOOKUP);
+ TRY(CAP_FSYNC | CAP_WRITE | CAP_LOOKUP);
+ TRY(CAP_FSYNC | CAP_READ | CAP_WRITE | CAP_LOOKUP);
+ TRY(CAP_FTRUNCATE);
+ TRY(CAP_FTRUNCATE | CAP_READ | CAP_LOOKUP);
+ TRY(CAP_FTRUNCATE | CAP_WRITE | CAP_LOOKUP);
+ TRY(CAP_FTRUNCATE | CAP_READ | CAP_WRITE | CAP_LOOKUP);
+#ifdef TODO
+ TRY(CAP_FCHDIR);
+#endif
+ TRY(CAP_FCHFLAGS);
+ TRY(CAP_FCHOWN);
+ TRY(CAP_FCHOWN | CAP_LOOKUP);
+ TRY(CAP_FCHMOD | CAP_LOOKUP);
+ TRY(CAP_FCNTL);
+#ifdef TODO
+ TRY(CAP_FLOCK);
+#endif
+ TRY(CAP_FPATHCONF);
+#ifdef TODO
+ TRY(CAP_FSCK);
+#endif
+ TRY(CAP_FSTAT | CAP_LOOKUP);
+ TRY(CAP_FSTATFS);
+ TRY(CAP_FUTIMES | CAP_LOOKUP);
+ TRY(CAP_LINKAT | CAP_LOOKUP);
+ TRY(CAP_MKDIRAT | CAP_LOOKUP);
+ TRY(CAP_MKFIFOAT | CAP_LOOKUP);
+ TRY(CAP_MKNODAT | CAP_LOOKUP);
+ TRY(CAP_SYMLINKAT | CAP_LOOKUP);
+ TRY(CAP_UNLINKAT | CAP_LOOKUP);
+ /* Rename needs CAP_RENAMEAT on source directory and CAP_LINKAT on destination directory. */
+ TRY(CAP_RENAMEAT | CAP_UNLINKAT | CAP_LOOKUP);
+#ifdef TODO
+ TRY(CAP_LOOKUP);
+ TRY(CAP_EXTATTR_DELETE);
+ TRY(CAP_EXTATTR_GET);
+ TRY(CAP_EXTATTR_LIST);
+ TRY(CAP_EXTATTR_SET);
+ TRY(CAP_ACL_CHECK);
+ TRY(CAP_ACL_DELETE);
+ TRY(CAP_ACL_GET);
+ TRY(CAP_ACL_SET);
+ TRY(CAP_ACCEPT);
+ TRY(CAP_BIND);
+ TRY(CAP_CONNECT);
+ TRY(CAP_GETPEERNAME);
+ TRY(CAP_GETSOCKNAME);
+ TRY(CAP_GETSOCKOPT);
+ TRY(CAP_LISTEN);
+ TRY(CAP_PEELOFF);
+ TRY(CAP_RECV);
+ TRY(CAP_SEND);
+ TRY(CAP_SETSOCKOPT);
+ TRY(CAP_SHUTDOWN);
+ TRY(CAP_MAC_GET);
+ TRY(CAP_MAC_SET);
+ TRY(CAP_SEM_GETVALUE);
+ TRY(CAP_SEM_POST);
+ TRY(CAP_SEM_WAIT);
+ TRY(CAP_POST_EVENT);
+ TRY(CAP_POLL_EVENT);
+ TRY(CAP_IOCTL);
+ TRY(CAP_TTYHOOK);
+ TRY(CAP_PDGETPID);
+ TRY(CAP_PDWAIT);
+ TRY(CAP_PDKILL);
+#endif
+
+ (void)unlinkat(tmpfd, file + strlen("/tmp/"), 0);
+ (void)unlinkat(tmpfd, dir + strlen("/tmp/"), AT_REMOVEDIR);
return (success);
}
diff --git a/tools/regression/security/cap_test/cap_test_relative.c b/tools/regression/security/cap_test/cap_test_relative.c
index 1f62e84..27410eb 100644
--- a/tools/regression/security/cap_test/cap_test_relative.c
+++ b/tools/regression/security/cap_test/cap_test_relative.c
@@ -61,7 +61,8 @@ test_relative(void)
cap_rights_t rights;
REQUIRE(etc = open("/etc/", O_RDONLY));
- CHECK_SYSCALL_FAILS(EINVAL, cap_getrights, etc, &rights);
+ CHECK_SYSCALL_SUCCEEDS(cap_getrights, etc, &rights);
+ CHECK_RIGHTS(rights, CAP_ALL);
MAKE_CAPABILITY(etc_cap, etc, CAP_READ);
MAKE_CAPABILITY(etc_cap_ro, etc, CAP_READ | CAP_LOOKUP);
diff --git a/tools/regression/sockets/unix_cmsg/README b/tools/regression/sockets/unix_cmsg/README
index 359da43..df51723 100644
--- a/tools/regression/sockets/unix_cmsg/README
+++ b/tools/regression/sockets/unix_cmsg/README
@@ -1,127 +1,160 @@
$FreeBSD$
About unix_cmsg
-================
+===============
-This program is a collection of regression tests for ancillary (control)
-data for PF_LOCAL sockets (local domain or Unix domain sockets). There
-are tests for stream and datagram sockets.
+This program is a collection of regression tests for ancillary data
+(control information) for PF_LOCAL sockets (local domain or Unix domain
+sockets). There are tests for stream and datagram sockets.
-Usually each test does following steps: create Server, fork Client,
-Client sends something to Server, Server verifies if everything
-is correct in received message. Sometimes Client sends several
-messages to Server.
+Usually each test does following steps: creates Server, forks Client,
+Client sends something to Server, Server verifies whether everything is
+correct in received message(s).
It is better to change the owner of unix_cmsg to some safe user
-(eg. nobody:nogroup) and set SUID and SGID bits, else some tests
-can give correct results for wrong implementation.
+(eg. nobody:nogroup) and set SUID and SGID bits, else some tests that
+check credentials can give correct results for wrong implementation.
+
+It is better to run this program by a user that belongs to more
+than 16 groups.
Available options
=================
--d Output debugging information, values of different fields of
- received messages, etc. Will produce many lines of information.
-
--h Output help message and exit.
+usage: unix_cmsg [-dh] [-n num] [-s size] [-t type] [-z value] [testno]
+
+ Options are:
+ -d Output debugging information
+ -h Output the help message and exit
+ -n num Number of messages to send
+ -s size Specify size of data for IPC
+ -t type Specify socket type (stream, dgram) for tests
+ -z value Do not send data in a message (bit 0x1), do not send
+ data array associated with a cmsghdr structure (bit 0x2)
+ testno Run one test by its number (require the -t option)
+
+Description
+===========
+
+If Client sends something to Server, then it sends 5 messages by default.
+Number of messages can be changed in the -n command line option. Number
+of messages will be given as N in the following descriptions.
+
+If Client sends something to Server, then it sends some data (few bytes)
+in each message by default. The size of this data can be changed by the -s
+command line option. The "-s 0" command line option means, that Client will
+send zero bytes represented by { NULL, 0 } value of struct iovec{}, referenced
+by the msg_iov field from struct msghdr{}. The "-z 1" or "-z 3" command line
+option means, that Client will send zero bytes represented by the NULL value
+in the msg_iov field from struct msghdr{}.
+
+If Client sends some ancillary data object, then this ancillary data object
+always has associated data array by default. The "-z 2" or "-z 3" option
+means, that Client will not send associated data array if possible.
--t <socktype>
- Run tests only for the given socket type: "stream" or "dgram".
- With this option it is possible to run only particular test,
- not all of them.
+For SOCK_STREAM sockets:
+-----------------------
--z Do not send real control data if possible. Struct cmsghdr{}
- should be followed by real control data. It is not clear if
- a sender should give control data in all cases (this is not
- documented and an arbitrary application can choose anything).
+ 1: Sending, receiving cmsgcred
- At least for PF_LOCAL sockets' control messages with types
- SCM_CREDS and SCM_TIMESTAMP the kernel does not need any
- control data. This option allow to not send real control data
- for SCM_CREDS and SCM_TIMESTAMP control messages.
+ Client connects to Server and sends N messages with SCM_CREDS ancillary
+ data object. Server should receive N messages, each message should
+ have SCM_CREDS ancillary data object followed by struct cmsgcred{}.
-Description of tests
-====================
+ 2: Receiving sockcred (listening socket)
-For SOCK_STREAM sockets:
------------------------
+ Server creates a listening stream socket and sets the LOCAL_CREDS
+ socket option for it. Client connects to Server two times, each time
+ it sends N messages. Server accepts two connections and receives N
+ messages from each connection. The first message from each connection
+ should have SCM_CREDS ancillary data object followed by struct sockcred{},
+ next messages from the same connection should not have ancillary data.
- 1: Sending, receiving cmsgcred
+ 3: Receiving sockcred (accepted socket)
- Client connects to Server and sends two messages with data and
- control message with SCM_CREDS type to Server. Server should
- receive two messages, in both messages there should be data and
- control message with SCM_CREDS type followed by struct cmsgcred{}
- and this structure should contain correct information.
-
- 2: Receiving sockcred (listening socket has LOCAL_CREDS)
-
- Server creates listen socket and set socket option LOCAL_CREDS
- for it. Client connects to Server and sends two messages with data
- to Server. Server should receive two messages, in first message
- there should be data and control message with SCM_CREDS type followed
- by struct sockcred{} and this structure should contain correct
- information, in second message there should be data and no control
- message.
-
- 3: Receiving sockcred (accepted socket has LOCAL_CREDS)
-
- Client connects to Server and sends two messages with data. Server
- accepts connection and set socket option LOCAL_CREDS for just accepted
- socket (here synchronization is used, to allow Client to see just set
- flag on Server's socket before sending messages to Server). Server
- should receive two messages, in first message there should be data and
- control message with SOCK_CRED type followed by struct sockcred{} and
- this structure should contain correct information, in second message
- there should be data and no control message.
+ Client connects to Server. Server accepts connection and sets the
+ LOCAL_CREDS socket option for just accepted socket. Client sends N
+ messages to Server. Server should receive N messages, the first
+ message should have SCM_CREDS ancillary data object followed by
+ struct sockcred{}, next messages should not have ancillary data.
4: Sending cmsgcred, receiving sockcred
- Server creates listen socket and set socket option LOCAL_CREDS
- for it. Client connects to Server and sends one message with data
- and control message with SCM_CREDS type to Server. Server should
- receive one message with data and control message with SCM_CREDS type
- followed by struct sockcred{} and this structure should contain
- correct information.
+ Server creates a listening stream socket and sets the LOCAL_CREDS
+ socket option for it. Client connects to Server and sends N messages
+ with SCM_CREDS ancillary data object. Server should receive N messages,
+ the first message should have SCM_CREDS ancillary data object followed
+ by struct sockcred{}, each of next messages should have SCM_CREDS
+ ancillary data object followed by struct cmsgcred{}.
+
+ 5: Sending, receiving timeval
+
+ Client connects to Server and sends message with SCM_TIMESTAMP ancillary
+ data object. Server should receive one message with SCM_TIMESTAMP
+ ancillary data object followed by struct timeval{}.
- 5: Sending, receiving timestamp
+ 6: Sending, receiving bintime
- Client connects to Server and sends message with data and control
- message with SCM_TIMESTAMP type to Server. Server should receive
- message with data and control message with SCM_TIMESTAMP type
- followed by struct timeval{}.
+ Client connects to Server and sends message with SCM_BINTIME ancillary
+ data object. Server should receive one message with SCM_BINTIME
+ ancillary data object followed by struct bintime{}.
+
+ 7: Checking cmsghdr.cmsg_len
+
+ Client connects to Server and tries to send several messages with
+ SCM_CREDS ancillary data object that has wrong cmsg_len field in its
+ struct cmsghdr{}. All these attempts should fail, since cmsg_len
+ in all requests is less than CMSG_LEN(0).
+
+ 8: Check LOCAL_PEERCRED socket option
+
+ This test does not use ancillary data, but can be implemented here.
+ Client connects to Server. Both Client and Server verify that
+ credentials of the peer are correct using LOCAL_PEERCRED socket option.
For SOCK_DGRAM sockets:
----------------------
1: Sending, receiving cmsgcred
- Client sends to Server two messages with data and control message
- with SCM_CREDS type to Server. Server should receive two messages,
- in both messages there should be data and control message with
- SCM_CREDS type followed by struct cmsgcred{} and this structure
- should contain correct information.
+ Client connects to Server and sends N messages with SCM_CREDS ancillary
+ data object. Server should receive N messages, each message should
+ have SCM_CREDS ancillary data object followed by struct cmsgcred{}.
2: Receiving sockcred
- Server creates datagram socket and set socket option LOCAL_CREDS
- for it. Client sends two messages with data to Server. Server should
- receive two messages, in both messages there should be data and control
- message with SCM_CREDS type followed by struct sockcred{} and this
- structure should contain correct information.
+ Server creates datagram socket and sets the LOCAL_CREDS socket option
+ for it. Client sends N messages to Server. Server should receive N
+ messages, each message should have SCM_CREDS ancillary data object
+ followed by struct sockcred{}.
3: Sending cmsgcred, receiving sockcred
-
- Server creates datagram socket and set socket option LOCAL_CREDS
- for it. Client sends one message with data and control message with
- SOCK_CREDS type to Server. Server should receive one message with
- data and control message with SCM_CREDS type followed by struct
- sockcred{} and this structure should contain correct information.
- 4: Sending, receiving timestamp
+ Server creates datagram socket and sets the LOCAL_CREDS socket option
+ for it. Client sends N messages with SCM_CREDS ancillary data object
+ to Server. Server should receive N messages, the first message should
+ have SCM_CREDS ancillary data object followed by struct sockcred{},
+ each of next messages should have SCM_CREDS ancillary data object
+ followed by struct cmsgcred{}.
+
+ 4: Sending, receiving timeval
+
+ Client sends one message with SCM_TIMESTAMP ancillary data object
+ to Server. Server should receive one message with SCM_TIMESTAMP
+ ancillary data object followed by struct timeval{}.
+
+ 5: Sending, receiving bintime
+
+ Client sends one message with SCM_BINTIME ancillary data object
+ to Server. Server should receive one message with SCM_BINTIME
+ ancillary data object followed by struct bintime{}.
+
+ 6: Checking cmsghdr.cmsg_len
- Client sends message with data and control message with SCM_TIMESTAMP
- type to Server. Server should receive message with data and control
- message with SCM_TIMESTAMP type followed by struct timeval{}.
+ Client tries to send Server several messages with SCM_CREDS ancillary
+ data object that has wrong cmsg_len field in its struct cmsghdr{}.
+ All these attempts should fail, since cmsg_len in all requests is less
+ than CMSG_LEN(0).
- Andrey Simonenko
-simon@comsys.ntu-kpi.kiev.ua
+andreysimonenko@users.sourceforge.net
diff --git a/tools/regression/sockets/unix_cmsg/unix_cmsg.c b/tools/regression/sockets/unix_cmsg/unix_cmsg.c
index 06728d7..d91cef4 100644
--- a/tools/regression/sockets/unix_cmsg/unix_cmsg.c
+++ b/tools/regression/sockets/unix_cmsg/unix_cmsg.c
@@ -27,48 +27,46 @@
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
-#include <sys/types.h>
+#include <sys/param.h>
#include <sys/resource.h>
#include <sys/time.h>
+#include <sys/select.h>
#include <sys/socket.h>
+#include <sys/ucred.h>
#include <sys/un.h>
#include <sys/wait.h>
-#include <assert.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
+#include <fcntl.h>
#include <inttypes.h>
#include <limits.h>
-#include <setjmp.h>
+#include <paths.h>
#include <signal.h>
#include <stdarg.h>
+#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <sysexits.h>
#include <unistd.h>
/*
* There are tables with tests descriptions and pointers to test
* functions. Each t_*() function returns 0 if its test passed,
- * -1 if its test failed (something wrong was found in local domain
- * control messages), -2 if some system error occurred. If test
- * function returns -2, then a program exits.
+ * -1 if its test failed, -2 if some system error occurred.
+ * If a test function returns -2, then a program exits.
*
- * Each test function completely control what to do (eg. fork or
- * do not fork a client process). If a test function forks a client
- * process, then it waits for its termination. If a return code of a
- * client process is not equal to zero, or if a client process was
- * terminated by a signal, then test function returns -2.
+ * If a test function forks a client process, then it waits for its
+ * termination. If a return code of a client process is not equal
+ * to zero, or if a client process was terminated by a signal, then
+ * a test function returns -1 or -2 depending on exit status of
+ * a client process.
*
- * Each test function and complete program are not optimized
- * a lot to allow easy to modify tests.
- *
- * Each function which can block, is run under TIMEOUT, if timeout
- * occurs, then test function returns -2 or a client process exits
- * with nonzero return code.
+ * Each function which can block, is run under TIMEOUT. If timeout
+ * occurs, then a test function returns -2 or a client process exits
+ * with a non-zero return code.
*/
#ifndef LISTENQ
@@ -76,207 +74,290 @@ __FBSDID("$FreeBSD$");
#endif
#ifndef TIMEOUT
-# define TIMEOUT 60
+# define TIMEOUT 2
#endif
-#define EXTRA_CMSG_SPACE 512 /* Memory for not expected control data. */
-
-static int t_cmsgcred(void), t_sockcred_stream1(void);
-static int t_sockcred_stream2(void), t_cmsgcred_sockcred(void);
-static int t_sockcred_dgram(void), t_timestamp(void);
+static int t_cmsgcred(void);
+static int t_sockcred_1(void);
+static int t_sockcred_2(void);
+static int t_cmsgcred_sockcred(void);
+static int t_timeval(void);
+static int t_bintime(void);
+static int t_cmsg_len(void);
+static int t_peercred(void);
struct test_func {
- int (*func)(void); /* Pointer to function. */
- const char *desc; /* Test description. */
+ int (*func)(void);
+ const char *desc;
};
-static struct test_func test_stream_tbl[] = {
- { NULL, " 0: All tests" },
- { t_cmsgcred, " 1: Sending, receiving cmsgcred" },
- { t_sockcred_stream1, " 2: Receiving sockcred (listening socket has LOCAL_CREDS)" },
- { t_sockcred_stream2, " 3: Receiving sockcred (accepted socket has LOCAL_CREDS)" },
- { t_cmsgcred_sockcred, " 4: Sending cmsgcred, receiving sockcred" },
- { t_timestamp, " 5: Sending, receiving timestamp" },
- { NULL, NULL }
+static const struct test_func test_stream_tbl[] = {
+ {
+ .func = NULL,
+ .desc = "All tests"
+ },
+ {
+ .func = t_cmsgcred,
+ .desc = "Sending, receiving cmsgcred"
+ },
+ {
+ .func = t_sockcred_1,
+ .desc = "Receiving sockcred (listening socket)"
+ },
+ {
+ .func = t_sockcred_2,
+ .desc = "Receiving sockcred (accepted socket)"
+ },
+ {
+ .func = t_cmsgcred_sockcred,
+ .desc = "Sending cmsgcred, receiving sockcred"
+ },
+ {
+ .func = t_timeval,
+ .desc = "Sending, receiving timeval"
+ },
+ {
+ .func = t_bintime,
+ .desc = "Sending, receiving bintime"
+ },
+ {
+ .func = t_cmsg_len,
+ .desc = "Check cmsghdr.cmsg_len"
+ },
+ {
+ .func = t_peercred,
+ .desc = "Check LOCAL_PEERCRED socket option"
+ }
};
-static struct test_func test_dgram_tbl[] = {
- { NULL, " 0: All tests" },
- { t_cmsgcred, " 1: Sending, receiving cmsgcred" },
- { t_sockcred_dgram, " 2: Receiving sockcred" },
- { t_cmsgcred_sockcred, " 3: Sending cmsgcred, receiving sockcred" },
- { t_timestamp, " 4: Sending, receiving timestamp" },
- { NULL, NULL }
+#define TEST_STREAM_TBL_SIZE \
+ (sizeof(test_stream_tbl) / sizeof(test_stream_tbl[0]))
+
+static const struct test_func test_dgram_tbl[] = {
+ {
+ .func = NULL,
+ .desc = "All tests"
+ },
+ {
+ .func = t_cmsgcred,
+ .desc = "Sending, receiving cmsgcred"
+ },
+ {
+ .func = t_sockcred_2,
+ .desc = "Receiving sockcred"
+ },
+ {
+ .func = t_cmsgcred_sockcred,
+ .desc = "Sending cmsgcred, receiving sockcred"
+ },
+ {
+ .func = t_timeval,
+ .desc = "Sending, receiving timeval"
+ },
+ {
+ .func = t_bintime,
+ .desc = "Sending, receiving bintime"
+ },
+ {
+ .func = t_cmsg_len,
+ .desc = "Check cmsghdr.cmsg_len"
+ }
};
-#define TEST_STREAM_NO_MAX (sizeof(test_stream_tbl) / sizeof(struct test_func) - 2)
-#define TEST_DGRAM_NO_MAX (sizeof(test_dgram_tbl) / sizeof(struct test_func) - 2)
+#define TEST_DGRAM_TBL_SIZE \
+ (sizeof(test_dgram_tbl) / sizeof(test_dgram_tbl[0]))
-static const char *myname = "SERVER"; /* "SERVER" or "CLIENT" */
+static bool debug = false;
+static bool server_flag = true;
+static bool send_data_flag = true;
+static bool send_array_flag = true;
+static bool failed_flag = false;
-static int debug = 0; /* 1, if -d. */
-static int no_control_data = 0; /* 1, if -z. */
+static int sock_type;
+static const char *sock_type_str;
-static u_int nfailed = 0; /* Number of failed tests. */
+static const char *proc_name;
-static int sock_type; /* SOCK_STREAM or SOCK_DGRAM */
-static const char *sock_type_str; /* "SOCK_STREAM" or "SOCK_DGRAN" */
+static char work_dir[] = _PATH_TMP "unix_cmsg.XXXXXXX";
+static int serv_sock_fd;
+static struct sockaddr_un serv_addr_sun;
-static char tempdir[] = "/tmp/unix_cmsg.XXXXXXX";
-static char serv_sock_path[PATH_MAX];
+static struct {
+ char *buf_send;
+ char *buf_recv;
+ size_t buf_size;
+ u_int msg_num;
+} ipc_msg;
-static char ipc_message[] = "hello";
+#define IPC_MSG_NUM_DEF 5
+#define IPC_MSG_NUM_MAX 10
+#define IPC_MSG_SIZE_DEF 7
+#define IPC_MSG_SIZE_MAX 128
-#define IPC_MESSAGE_SIZE (sizeof(ipc_message))
+static struct {
+ uid_t uid;
+ uid_t euid;
+ gid_t gid;
+ gid_t egid;
+ gid_t *gid_arr;
+ int gid_num;
+} proc_cred;
-static struct sockaddr_un servaddr; /* Server address. */
+static pid_t client_pid;
-static sigjmp_buf env_alrm;
+#define SYNC_SERVER 0
+#define SYNC_CLIENT 1
+#define SYNC_RECV 0
+#define SYNC_SEND 1
-static uid_t my_uid;
-static uid_t my_euid;
-static gid_t my_gid;
-static gid_t my_egid;
+static int sync_fd[2][2];
-/*
- * my_gids[0] is EGID, next items are supplementary GIDs,
- * my_ngids determines valid items in my_gids array.
- */
-static gid_t my_gids[NGROUPS_MAX];
-static int my_ngids;
-
-static pid_t client_pid; /* PID of forked client. */
-
-#define dbgmsg(x) do { \
- if (debug) \
- logmsgx x ; \
-} while (/* CONSTCOND */0)
+#define LOGMSG_SIZE 128
static void logmsg(const char *, ...) __printflike(1, 2);
static void logmsgx(const char *, ...) __printflike(1, 2);
+static void dbgmsg(const char *, ...) __printflike(1, 2);
static void output(const char *, ...) __printflike(1, 2);
-extern char *__progname; /* The name of program. */
-
-/*
- * Output the help message (-h switch).
- */
static void
-usage(int quick)
+usage(bool verbose)
{
- const struct test_func *test_func;
+ u_int i;
- fprintf(stderr, "Usage: %s [-dhz] [-t <socktype>] [testno]\n",
- __progname);
- if (quick)
+ printf("usage: %s [-dh] [-n num] [-s size] [-t type] "
+ "[-z value] [testno]\n", getprogname());
+ if (!verbose)
return;
- fprintf(stderr, "\n Options are:\n\
- -d\t\t\tOutput debugging information\n\
- -h\t\t\tOutput this help message and exit\n\
- -t <socktype>\t\tRun test only for the given socket type:\n\
-\t\t\tstream or dgram\n\
- -z\t\t\tDo not send real control data if possible\n\n");
- fprintf(stderr, " Available tests for stream sockets:\n");
- for (test_func = test_stream_tbl; test_func->desc != NULL; ++test_func)
- fprintf(stderr, " %s\n", test_func->desc);
- fprintf(stderr, "\n Available tests for datagram sockets:\n");
- for (test_func = test_dgram_tbl; test_func->desc != NULL; ++test_func)
- fprintf(stderr, " %s\n", test_func->desc);
+ printf("\n Options are:\n\
+ -d Output debugging information\n\
+ -h Output the help message and exit\n\
+ -n num Number of messages to send\n\
+ -s size Specify size of data for IPC\n\
+ -t type Specify socket type (stream, dgram) for tests\n\
+ -z value Do not send data in a message (bit 0x1), do not send\n\
+ data array associated with a cmsghdr structure (bit 0x2)\n\
+ testno Run one test by its number (require the -t option)\n\n");
+ printf(" Available tests for stream sockets:\n");
+ for (i = 0; i < TEST_STREAM_TBL_SIZE; ++i)
+ printf(" %u: %s\n", i, test_stream_tbl[i].desc);
+ printf("\n Available tests for datagram sockets:\n");
+ for (i = 0; i < TEST_DGRAM_TBL_SIZE; ++i)
+ printf(" %u: %s\n", i, test_dgram_tbl[i].desc);
}
-/*
- * printf-like function for outputting to STDOUT_FILENO.
- */
static void
output(const char *format, ...)
{
- char buf[128];
+ char buf[LOGMSG_SIZE];
va_list ap;
va_start(ap, format);
if (vsnprintf(buf, sizeof(buf), format, ap) < 0)
- err(EX_SOFTWARE, "output: vsnprintf failed");
+ err(EXIT_FAILURE, "output: vsnprintf failed");
write(STDOUT_FILENO, buf, strlen(buf));
va_end(ap);
}
-/*
- * printf-like function for logging, also outputs message for errno.
- */
static void
logmsg(const char *format, ...)
{
- char buf[128];
+ char buf[LOGMSG_SIZE];
va_list ap;
int errno_save;
- errno_save = errno; /* Save errno. */
-
+ errno_save = errno;
va_start(ap, format);
if (vsnprintf(buf, sizeof(buf), format, ap) < 0)
- err(EX_SOFTWARE, "logmsg: vsnprintf failed");
+ err(EXIT_FAILURE, "logmsg: vsnprintf failed");
if (errno_save == 0)
- output("%s: %s\n", myname, buf);
+ output("%s: %s\n", proc_name, buf);
else
- output("%s: %s: %s\n", myname, buf, strerror(errno_save));
+ output("%s: %s: %s\n", proc_name, buf, strerror(errno_save));
va_end(ap);
+ errno = errno_save;
+}
+
+static void
+vlogmsgx(const char *format, va_list ap)
+{
+ char buf[LOGMSG_SIZE];
+
+ if (vsnprintf(buf, sizeof(buf), format, ap) < 0)
+ err(EXIT_FAILURE, "logmsgx: vsnprintf failed");
+ output("%s: %s\n", proc_name, buf);
- errno = errno_save; /* Restore errno. */
}
-/*
- * printf-like function for logging, do not output message for errno.
- */
static void
logmsgx(const char *format, ...)
{
- char buf[128];
va_list ap;
va_start(ap, format);
- if (vsnprintf(buf, sizeof(buf), format, ap) < 0)
- err(EX_SOFTWARE, "logmsgx: vsnprintf failed");
- output("%s: %s\n", myname, buf);
+ vlogmsgx(format, ap);
va_end(ap);
}
-/*
- * Run tests from testno1 to testno2.
- */
+static void
+dbgmsg(const char *format, ...)
+{
+ va_list ap;
+
+ if (debug) {
+ va_start(ap, format);
+ vlogmsgx(format, ap);
+ va_end(ap);
+ }
+}
+
static int
-run_tests(u_int testno1, u_int testno2)
+run_tests(int type, u_int testno1)
{
- const struct test_func *test_func;
- u_int i, nfailed1;
+ const struct test_func *tf;
+ u_int i, testno2, failed_num;
- output("Running tests for %s sockets:\n", sock_type_str);
- test_func = (sock_type == SOCK_STREAM ?
- test_stream_tbl : test_dgram_tbl) + testno1;
+ sock_type = type;
+ if (type == SOCK_STREAM) {
+ sock_type_str = "SOCK_STREAM";
+ tf = test_stream_tbl;
+ i = TEST_STREAM_TBL_SIZE - 1;
+ } else {
+ sock_type_str = "SOCK_DGRAM";
+ tf = test_dgram_tbl;
+ i = TEST_DGRAM_TBL_SIZE - 1;
+ }
+ if (testno1 == 0) {
+ testno1 = 1;
+ testno2 = i;
+ } else
+ testno2 = testno1;
- nfailed1 = 0;
- for (i = testno1; i <= testno2; ++test_func, ++i) {
- output(" %s\n", test_func->desc);
- switch (test_func->func()) {
+ output("Running tests for %s sockets:\n", sock_type_str);
+ failed_num = 0;
+ for (i = testno1, tf += testno1; i <= testno2; ++tf, ++i) {
+ output(" %u: %s\n", i, tf->desc);
+ switch (tf->func()) {
case -1:
- ++nfailed1;
+ ++failed_num;
break;
case -2:
- logmsgx("some system error occurred, exiting");
+ logmsgx("some system error or timeout occurred");
return (-1);
}
}
- nfailed += nfailed1;
+ if (failed_num != 0)
+ failed_flag = true;
if (testno1 != testno2) {
- if (nfailed1 == 0)
- output("-- all tests were passed!\n");
+ if (failed_num == 0)
+ output("-- all tests passed!\n");
else
- output("-- %u test%s failed!\n", nfailed1,
- nfailed1 == 1 ? "" : "s");
+ output("-- %u test%s failed!\n",
+ failed_num, failed_num == 1 ? "" : "s");
} else {
- if (nfailed == 0)
- output("-- test was passed!\n");
+ if (failed_num == 0)
+ output("-- test passed!\n");
else
output("-- test failed!\n");
}
@@ -284,183 +365,322 @@ run_tests(u_int testno1, u_int testno2)
return (0);
}
-/* ARGSUSED */
-static void
-sig_alrm(int signo __unused)
+static int
+init(void)
{
- siglongjmp(env_alrm, 1);
+ struct sigaction sigact;
+ size_t idx;
+ int rv;
+
+ proc_name = "SERVER";
+
+ sigact.sa_handler = SIG_IGN;
+ sigact.sa_flags = 0;
+ sigemptyset(&sigact.sa_mask);
+ if (sigaction(SIGPIPE, &sigact, (struct sigaction *)NULL) < 0) {
+ logmsg("init: sigaction");
+ return (-1);
+ }
+
+ if (ipc_msg.buf_size == 0)
+ ipc_msg.buf_send = ipc_msg.buf_recv = NULL;
+ else {
+ ipc_msg.buf_send = malloc(ipc_msg.buf_size);
+ ipc_msg.buf_recv = malloc(ipc_msg.buf_size);
+ if (ipc_msg.buf_send == NULL || ipc_msg.buf_recv == NULL) {
+ logmsg("init: malloc");
+ return (-1);
+ }
+ for (idx = 0; idx < ipc_msg.buf_size; ++idx)
+ ipc_msg.buf_send[idx] = (char)idx;
+ }
+
+ proc_cred.uid = getuid();
+ proc_cred.euid = geteuid();
+ proc_cred.gid = getgid();
+ proc_cred.egid = getegid();
+ proc_cred.gid_num = getgroups(0, (gid_t *)NULL);
+ if (proc_cred.gid_num < 0) {
+ logmsg("init: getgroups");
+ return (-1);
+ }
+ proc_cred.gid_arr = malloc(proc_cred.gid_num *
+ sizeof(*proc_cred.gid_arr));
+ if (proc_cred.gid_arr == NULL) {
+ logmsg("init: malloc");
+ return (-1);
+ }
+ if (getgroups(proc_cred.gid_num, proc_cred.gid_arr) < 0) {
+ logmsg("init: getgroups");
+ return (-1);
+ }
+
+ memset(&serv_addr_sun, 0, sizeof(serv_addr_sun));
+ rv = snprintf(serv_addr_sun.sun_path, sizeof(serv_addr_sun.sun_path),
+ "%s/%s", work_dir, proc_name);
+ if (rv < 0) {
+ logmsg("init: snprintf");
+ return (-1);
+ }
+ if ((size_t)rv >= sizeof(serv_addr_sun.sun_path)) {
+ logmsgx("init: not enough space for socket pathname");
+ return (-1);
+ }
+ serv_addr_sun.sun_family = PF_LOCAL;
+ serv_addr_sun.sun_len = SUN_LEN(&serv_addr_sun);
+
+ return (0);
+}
+
+static int
+client_fork(void)
+{
+ int fd1, fd2;
+
+ if (pipe(sync_fd[SYNC_SERVER]) < 0 ||
+ pipe(sync_fd[SYNC_CLIENT]) < 0) {
+ logmsg("client_fork: pipe");
+ return (-1);
+ }
+ client_pid = fork();
+ if (client_pid == (pid_t)-1) {
+ logmsg("client_fork: fork");
+ return (-1);
+ }
+ if (client_pid == 0) {
+ proc_name = "CLIENT";
+ server_flag = false;
+ fd1 = sync_fd[SYNC_SERVER][SYNC_RECV];
+ fd2 = sync_fd[SYNC_CLIENT][SYNC_SEND];
+ } else {
+ fd1 = sync_fd[SYNC_SERVER][SYNC_SEND];
+ fd2 = sync_fd[SYNC_CLIENT][SYNC_RECV];
+ }
+ if (close(fd1) < 0 || close(fd2) < 0) {
+ logmsg("client_fork: close");
+ return (-1);
+ }
+ return (client_pid != 0);
}
-/*
- * Initialize signals handlers.
- */
static void
-sig_init(void)
+client_exit(int rv)
+{
+ if (close(sync_fd[SYNC_SERVER][SYNC_SEND]) < 0 ||
+ close(sync_fd[SYNC_CLIENT][SYNC_RECV]) < 0) {
+ logmsg("client_exit: close");
+ rv = -1;
+ }
+ rv = rv == 0 ? EXIT_SUCCESS : -rv;
+ dbgmsg("exit: code %d", rv);
+ _exit(rv);
+}
+
+static int
+client_wait(void)
{
- struct sigaction sa;
+ int status;
+ pid_t pid;
+
+ dbgmsg("waiting for client");
+
+ if (close(sync_fd[SYNC_SERVER][SYNC_RECV]) < 0 ||
+ close(sync_fd[SYNC_CLIENT][SYNC_SEND]) < 0) {
+ logmsg("client_wait: close");
+ return (-1);
+ }
+
+ pid = waitpid(client_pid, &status, 0);
+ if (pid == (pid_t)-1) {
+ logmsg("client_wait: waitpid");
+ return (-1);
+ }
- sa.sa_handler = SIG_IGN;
- sigemptyset(&sa.sa_mask);
- sa.sa_flags = 0;
- if (sigaction(SIGPIPE, &sa, (struct sigaction *)NULL) < 0)
- err(EX_OSERR, "sigaction(SIGPIPE)");
+ if (WIFEXITED(status)) {
+ if (WEXITSTATUS(status) != EXIT_SUCCESS) {
+ logmsgx("client exit status is %d",
+ WEXITSTATUS(status));
+ return (-WEXITSTATUS(status));
+ }
+ } else {
+ if (WIFSIGNALED(status))
+ logmsgx("abnormal termination of client, signal %d%s",
+ WTERMSIG(status), WCOREDUMP(status) ?
+ " (core file generated)" : "");
+ else
+ logmsgx("termination of client, unknown status");
+ return (-1);
+ }
- sa.sa_handler = sig_alrm;
- if (sigaction(SIGALRM, &sa, (struct sigaction *)NULL) < 0)
- err(EX_OSERR, "sigaction(SIGALRM)");
+ return (0);
}
int
main(int argc, char *argv[])
{
const char *errstr;
- int opt, dgramflag, streamflag;
- u_int testno1, testno2;
-
- dgramflag = streamflag = 0;
- while ((opt = getopt(argc, argv, "dht:z")) != -1)
+ u_int testno, zvalue;
+ int opt, rv;
+ bool dgram_flag, stream_flag;
+
+ ipc_msg.buf_size = IPC_MSG_SIZE_DEF;
+ ipc_msg.msg_num = IPC_MSG_NUM_DEF;
+ dgram_flag = stream_flag = false;
+ while ((opt = getopt(argc, argv, "dhn:s:t:z:")) != -1)
switch (opt) {
case 'd':
- debug = 1;
+ debug = true;
break;
case 'h':
- usage(0);
- return (EX_OK);
+ usage(true);
+ return (EXIT_SUCCESS);
+ case 'n':
+ ipc_msg.msg_num = strtonum(optarg, 1,
+ IPC_MSG_NUM_MAX, &errstr);
+ if (errstr != NULL)
+ errx(EXIT_FAILURE, "option -n: number is %s",
+ errstr);
+ break;
+ case 's':
+ ipc_msg.buf_size = strtonum(optarg, 0,
+ IPC_MSG_SIZE_MAX, &errstr);
+ if (errstr != NULL)
+ errx(EXIT_FAILURE, "option -s: number is %s",
+ errstr);
+ break;
case 't':
if (strcmp(optarg, "stream") == 0)
- streamflag = 1;
+ stream_flag = true;
else if (strcmp(optarg, "dgram") == 0)
- dgramflag = 1;
+ dgram_flag = true;
else
- errx(EX_USAGE, "wrong socket type in -t option");
+ errx(EXIT_FAILURE, "option -t: "
+ "wrong socket type");
break;
case 'z':
- no_control_data = 1;
+ zvalue = strtonum(optarg, 0, 3, &errstr);
+ if (errstr != NULL)
+ errx(EXIT_FAILURE, "option -z: number is %s",
+ errstr);
+ if (zvalue & 0x1)
+ send_data_flag = false;
+ if (zvalue & 0x2)
+ send_array_flag = false;
break;
- case '?':
default:
- usage(1);
- return (EX_USAGE);
+ usage(false);
+ return (EXIT_FAILURE);
}
if (optind < argc) {
if (optind + 1 != argc)
- errx(EX_USAGE, "too many arguments");
- testno1 = strtonum(argv[optind], 0, UINT_MAX, &errstr);
+ errx(EXIT_FAILURE, "too many arguments");
+ testno = strtonum(argv[optind], 0, UINT_MAX, &errstr);
if (errstr != NULL)
- errx(EX_USAGE, "wrong test number: %s", errstr);
+ errx(EXIT_FAILURE, "test number is %s", errstr);
+ if (stream_flag && testno >= TEST_STREAM_TBL_SIZE)
+ errx(EXIT_FAILURE, "given test %u for stream "
+ "sockets does not exist", testno);
+ if (dgram_flag && testno >= TEST_DGRAM_TBL_SIZE)
+ errx(EXIT_FAILURE, "given test %u for datagram "
+ "sockets does not exist", testno);
} else
- testno1 = 0;
+ testno = 0;
- if (dgramflag == 0 && streamflag == 0)
- dgramflag = streamflag = 1;
-
- if (dgramflag && streamflag && testno1 != 0)
- errx(EX_USAGE, "you can use particular test, only with datagram or stream sockets");
-
- if (streamflag) {
- if (testno1 > TEST_STREAM_NO_MAX)
- errx(EX_USAGE, "given test %u for stream sockets does not exist",
- testno1);
- } else {
- if (testno1 > TEST_DGRAM_NO_MAX)
- errx(EX_USAGE, "given test %u for datagram sockets does not exist",
- testno1);
- }
-
- my_uid = getuid();
- my_euid = geteuid();
- my_gid = getgid();
- my_egid = getegid();
- switch (my_ngids = getgroups(sizeof(my_gids) / sizeof(my_gids[0]), my_gids)) {
- case -1:
- err(EX_SOFTWARE, "getgroups");
- /* NOTREACHED */
- case 0:
- errx(EX_OSERR, "getgroups returned 0 groups");
+ if (!dgram_flag && !stream_flag) {
+ if (testno != 0)
+ errx(EXIT_FAILURE, "particular test number "
+ "can be used with the -t option only");
+ dgram_flag = stream_flag = true;
}
- sig_init();
-
- if (mkdtemp(tempdir) == NULL)
- err(EX_OSERR, "mkdtemp");
+ if (mkdtemp(work_dir) == NULL)
+ err(EXIT_FAILURE, "mkdtemp(%s)", work_dir);
- if (streamflag) {
- sock_type = SOCK_STREAM;
- sock_type_str = "SOCK_STREAM";
- if (testno1 == 0) {
- testno1 = 1;
- testno2 = TEST_STREAM_NO_MAX;
- } else
- testno2 = testno1;
- if (run_tests(testno1, testno2) < 0)
- goto failed;
- testno1 = 0;
- }
+ rv = EXIT_FAILURE;
+ if (init() < 0)
+ goto done;
- if (dgramflag) {
- sock_type = SOCK_DGRAM;
- sock_type_str = "SOCK_DGRAM";
- if (testno1 == 0) {
- testno1 = 1;
- testno2 = TEST_DGRAM_NO_MAX;
- } else
- testno2 = testno1;
- if (run_tests(testno1, testno2) < 0)
- goto failed;
- }
+ if (stream_flag)
+ if (run_tests(SOCK_STREAM, testno) < 0)
+ goto done;
+ if (dgram_flag)
+ if (run_tests(SOCK_DGRAM, testno) < 0)
+ goto done;
- if (rmdir(tempdir) < 0) {
- logmsg("rmdir(%s)", tempdir);
- return (EX_OSERR);
+ rv = EXIT_SUCCESS;
+done:
+ if (rmdir(work_dir) < 0) {
+ logmsg("rmdir(%s)", work_dir);
+ rv = EXIT_FAILURE;
}
+ return (failed_flag ? EXIT_FAILURE : rv);
+}
- return (nfailed ? EX_OSERR : EX_OK);
+static int
+socket_close(int fd)
+{
+ int rv;
-failed:
- if (rmdir(tempdir) < 0)
- logmsg("rmdir(%s)", tempdir);
- return (EX_OSERR);
+ rv = 0;
+ if (close(fd) < 0) {
+ logmsg("socket_close: close");
+ rv = -1;
+ }
+ if (server_flag && fd == serv_sock_fd)
+ if (unlink(serv_addr_sun.sun_path) < 0) {
+ logmsg("socket_close: unlink(%s)",
+ serv_addr_sun.sun_path);
+ rv = -1;
+ }
+ return (rv);
}
-/*
- * Create PF_LOCAL socket, if sock_path is not equal to NULL, then
- * bind() it. Return socket address in addr. Return file descriptor
- * or -1 if some error occurred.
- */
static int
-create_socket(char *sock_path, size_t sock_path_len, struct sockaddr_un *addr)
+socket_create(void)
{
- int rv, fd;
+ struct timeval tv;
+ int fd;
- if ((fd = socket(PF_LOCAL, sock_type, 0)) < 0) {
- logmsg("create_socket: socket(PF_LOCAL, %s, 0)", sock_type_str);
+ fd = socket(PF_LOCAL, sock_type, 0);
+ if (fd < 0) {
+ logmsg("socket_create: socket(PF_LOCAL, %s, 0)", sock_type_str);
return (-1);
}
+ if (server_flag)
+ serv_sock_fd = fd;
+
+ tv.tv_sec = TIMEOUT;
+ tv.tv_usec = 0;
+ if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0 ||
+ setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0) {
+ logmsg("socket_create: setsockopt(SO_RCVTIMEO/SO_SNDTIMEO)");
+ goto failed;
+ }
- if (sock_path != NULL) {
- if ((rv = snprintf(sock_path, sock_path_len, "%s/%s",
- tempdir, myname)) < 0) {
- logmsg("create_socket: snprintf failed");
- goto failed;
- }
- if ((size_t)rv >= sock_path_len) {
- logmsgx("create_socket: too long path name for given buffer");
- goto failed;
- }
-
- memset(addr, 0, sizeof(*addr));
- addr->sun_family = AF_LOCAL;
- if (strlen(sock_path) >= sizeof(addr->sun_path)) {
- logmsgx("create_socket: too long path name (>= %lu) for local domain socket",
- (u_long)sizeof(addr->sun_path));
+ if (server_flag) {
+ if (bind(fd, (struct sockaddr *)&serv_addr_sun,
+ serv_addr_sun.sun_len) < 0) {
+ logmsg("socket_create: bind(%s)",
+ serv_addr_sun.sun_path);
goto failed;
}
- strcpy(addr->sun_path, sock_path);
+ if (sock_type == SOCK_STREAM) {
+ int val;
- if (bind(fd, (struct sockaddr *)addr, SUN_LEN(addr)) < 0) {
- logmsg("create_socket: bind(%s)", sock_path);
- goto failed;
+ if (listen(fd, LISTENQ) < 0) {
+ logmsg("socket_create: listen");
+ goto failed;
+ }
+ val = fcntl(fd, F_GETFL, 0);
+ if (val < 0) {
+ logmsg("socket_create: fcntl(F_GETFL)");
+ goto failed;
+ }
+ if (fcntl(fd, F_SETFL, val | O_NONBLOCK) < 0) {
+ logmsg("socket_create: fcntl(F_SETFL)");
+ goto failed;
+ }
}
}
@@ -468,1163 +688,1282 @@ create_socket(char *sock_path, size_t sock_path_len, struct sockaddr_un *addr)
failed:
if (close(fd) < 0)
- logmsg("create_socket: close");
+ logmsg("socket_create: close");
+ if (server_flag)
+ if (unlink(serv_addr_sun.sun_path) < 0)
+ logmsg("socket_close: unlink(%s)",
+ serv_addr_sun.sun_path);
return (-1);
}
-/*
- * Call create_socket() for server listening socket.
- * Return socket descriptor or -1 if some error occurred.
- */
static int
-create_server_socket(void)
+socket_connect(int fd)
{
- return (create_socket(serv_sock_path, sizeof(serv_sock_path), &servaddr));
+ dbgmsg("connect");
+
+ if (connect(fd, (struct sockaddr *)&serv_addr_sun,
+ serv_addr_sun.sun_len) < 0) {
+ logmsg("socket_connect: connect(%s)", serv_addr_sun.sun_path);
+ return (-1);
+ }
+ return (0);
}
-/*
- * Create unbound socket.
- */
static int
-create_unbound_socket(void)
+sync_recv(void)
{
- return (create_socket((char *)NULL, 0, (struct sockaddr_un *)NULL));
+ ssize_t ssize;
+ int fd;
+ char buf;
+
+ dbgmsg("sync: wait");
+
+ fd = sync_fd[server_flag ? SYNC_SERVER : SYNC_CLIENT][SYNC_RECV];
+
+ ssize = read(fd, &buf, 1);
+ if (ssize < 0) {
+ logmsg("sync_recv: read");
+ return (-1);
+ }
+ if (ssize < 1) {
+ logmsgx("sync_recv: read %zd of 1 byte", ssize);
+ return (-1);
+ }
+
+ dbgmsg("sync: received");
+
+ return (0);
}
-/*
- * Close socket descriptor, if sock_path is not equal to NULL,
- * then unlink the given path.
- */
static int
-close_socket(const char *sock_path, int fd)
+sync_send(void)
{
- int error = 0;
+ ssize_t ssize;
+ int fd;
- if (close(fd) < 0) {
- logmsg("close_socket: close");
- error = -1;
+ dbgmsg("sync: send");
+
+ fd = sync_fd[server_flag ? SYNC_CLIENT : SYNC_SERVER][SYNC_SEND];
+
+ ssize = write(fd, "", 1);
+ if (ssize < 0) {
+ logmsg("sync_send: write");
+ return (-1);
}
- if (sock_path != NULL)
- if (unlink(sock_path) < 0) {
- logmsg("close_socket: unlink(%s)", sock_path);
- error = -1;
- }
- return (error);
+ if (ssize < 1) {
+ logmsgx("sync_send: sent %zd of 1 byte", ssize);
+ return (-1);
+ }
+
+ return (0);
}
-/*
- * Connect to server (socket address in servaddr).
- */
static int
-connect_server(int fd)
+message_send(int fd, const struct msghdr *msghdr)
{
- dbgmsg(("connecting to %s", serv_sock_path));
-
- /*
- * If PF_LOCAL listening socket's queue is full, then connect()
- * returns ECONNREFUSED immediately, do not need timeout.
- */
- if (connect(fd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {
- logmsg("connect_server: connect(%s)", serv_sock_path);
+ const struct cmsghdr *cmsghdr;
+ size_t size;
+ ssize_t ssize;
+
+ size = msghdr->msg_iov != 0 ? msghdr->msg_iov->iov_len : 0;
+ dbgmsg("send: data size %zu", size);
+ dbgmsg("send: msghdr.msg_controllen %u",
+ (u_int)msghdr->msg_controllen);
+ cmsghdr = CMSG_FIRSTHDR(msghdr);
+ if (cmsghdr != NULL)
+ dbgmsg("send: cmsghdr.cmsg_len %u",
+ (u_int)cmsghdr->cmsg_len);
+
+ ssize = sendmsg(fd, msghdr, 0);
+ if (ssize < 0) {
+ logmsg("message_send: sendmsg");
+ return (-1);
+ }
+ if ((size_t)ssize != size) {
+ logmsgx("message_send: sendmsg: sent %zd of %zu bytes",
+ ssize, size);
return (-1);
}
+ if (!send_data_flag)
+ if (sync_send() < 0)
+ return (-1);
+
return (0);
}
-/*
- * sendmsg() with timeout.
- */
static int
-sendmsg_timeout(int fd, struct msghdr *msg, size_t n)
+message_sendn(int fd, struct msghdr *msghdr)
{
- ssize_t nsent;
-
- dbgmsg(("sending %lu bytes", (u_long)n));
+ u_int i;
- if (sigsetjmp(env_alrm, 1) != 0) {
- logmsgx("sendmsg_timeout: cannot send message to %s (timeout)", serv_sock_path);
- return (-1);
+ for (i = 1; i <= ipc_msg.msg_num; ++i) {
+ dbgmsg("message #%u", i);
+ if (message_send(fd, msghdr) < 0)
+ return (-1);
}
+ return (0);
+}
- (void)alarm(TIMEOUT);
-
- nsent = sendmsg(fd, msg, 0);
+static int
+message_recv(int fd, struct msghdr *msghdr)
+{
+ const struct cmsghdr *cmsghdr;
+ size_t size;
+ ssize_t ssize;
- (void)alarm(0);
+ if (!send_data_flag)
+ if (sync_recv() < 0)
+ return (-1);
- if (nsent < 0) {
- logmsg("sendmsg_timeout: sendmsg");
+ size = msghdr->msg_iov != NULL ? msghdr->msg_iov->iov_len : 0;
+ ssize = recvmsg(fd, msghdr, MSG_WAITALL);
+ if (ssize < 0) {
+ logmsg("message_recv: recvmsg");
+ return (-1);
+ }
+ if ((size_t)ssize != size) {
+ logmsgx("message_recv: recvmsg: received %zd of %zu bytes",
+ ssize, size);
return (-1);
}
- if ((size_t)nsent != n) {
- logmsgx("sendmsg_timeout: sendmsg: short send: %ld of %lu bytes",
- (long)nsent, (u_long)n);
+ dbgmsg("recv: data size %zd", ssize);
+ dbgmsg("recv: msghdr.msg_controllen %u",
+ (u_int)msghdr->msg_controllen);
+ cmsghdr = CMSG_FIRSTHDR(msghdr);
+ if (cmsghdr != NULL)
+ dbgmsg("recv: cmsghdr.cmsg_len %u",
+ (u_int)cmsghdr->cmsg_len);
+
+ if (memcmp(ipc_msg.buf_recv, ipc_msg.buf_send, size) != 0) {
+ logmsgx("message_recv: received message has wrong content");
return (-1);
}
return (0);
}
-/*
- * accept() with timeout.
- */
static int
-accept_timeout(int listenfd)
+socket_accept(int listenfd)
{
- int fd;
-
- dbgmsg(("accepting connection"));
-
- if (sigsetjmp(env_alrm, 1) != 0) {
- logmsgx("accept_timeout: cannot accept connection (timeout)");
+ fd_set rset;
+ struct timeval tv;
+ int fd, rv, val;
+
+ dbgmsg("accept");
+
+ FD_ZERO(&rset);
+ FD_SET(listenfd, &rset);
+ tv.tv_sec = TIMEOUT;
+ tv.tv_usec = 0;
+ rv = select(listenfd + 1, &rset, (fd_set *)NULL, (fd_set *)NULL, &tv);
+ if (rv < 0) {
+ logmsg("socket_accept: select");
+ return (-1);
+ }
+ if (rv == 0) {
+ logmsgx("socket_accept: select timeout");
return (-1);
}
-
- (void)alarm(TIMEOUT);
fd = accept(listenfd, (struct sockaddr *)NULL, (socklen_t *)NULL);
-
- (void)alarm(0);
-
if (fd < 0) {
- logmsg("accept_timeout: accept");
+ logmsg("socket_accept: accept");
return (-1);
}
+ val = fcntl(fd, F_GETFL, 0);
+ if (val < 0) {
+ logmsg("socket_accept: fcntl(F_GETFL)");
+ goto failed;
+ }
+ if (fcntl(fd, F_SETFL, val & ~O_NONBLOCK) < 0) {
+ logmsg("socket_accept: fcntl(F_SETFL)");
+ goto failed;
+ }
+
return (fd);
+
+failed:
+ if (close(fd) < 0)
+ logmsg("socket_accept: close");
+ return (-1);
}
-/*
- * recvmsg() with timeout.
- */
static int
-recvmsg_timeout(int fd, struct msghdr *msg, size_t n)
+check_msghdr(const struct msghdr *msghdr, size_t size)
{
- ssize_t nread;
-
- dbgmsg(("receiving %lu bytes", (u_long)n));
-
- if (sigsetjmp(env_alrm, 1) != 0) {
- logmsgx("recvmsg_timeout: cannot receive message (timeout)");
+ if (msghdr->msg_flags & MSG_TRUNC) {
+ logmsgx("msghdr.msg_flags has MSG_TRUNC");
return (-1);
}
-
- (void)alarm(TIMEOUT);
-
- nread = recvmsg(fd, msg, MSG_WAITALL);
-
- (void)alarm(0);
-
- if (nread < 0) {
- logmsg("recvmsg_timeout: recvmsg");
+ if (msghdr->msg_flags & MSG_CTRUNC) {
+ logmsgx("msghdr.msg_flags has MSG_CTRUNC");
return (-1);
}
-
- if ((size_t)nread != n) {
- logmsgx("recvmsg_timeout: recvmsg: short read: %ld of %lu bytes",
- (long)nread, (u_long)n);
+ if (msghdr->msg_controllen < size) {
+ logmsgx("msghdr.msg_controllen %u < %zu",
+ (u_int)msghdr->msg_controllen, size);
+ return (-1);
+ }
+ if (msghdr->msg_controllen > 0 && size == 0) {
+ logmsgx("msghdr.msg_controllen %u > 0",
+ (u_int)msghdr->msg_controllen);
return (-1);
}
+ return (0);
+}
+static int
+check_cmsghdr(const struct cmsghdr *cmsghdr, int type, size_t size)
+{
+ if (cmsghdr == NULL) {
+ logmsgx("cmsghdr is NULL");
+ return (-1);
+ }
+ if (cmsghdr->cmsg_level != SOL_SOCKET) {
+ logmsgx("cmsghdr.cmsg_level %d != SOL_SOCKET",
+ cmsghdr->cmsg_level);
+ return (-1);
+ }
+ if (cmsghdr->cmsg_type != type) {
+ logmsgx("cmsghdr.cmsg_type %d != %d",
+ cmsghdr->cmsg_type, type);
+ return (-1);
+ }
+ if (cmsghdr->cmsg_len != CMSG_LEN(size)) {
+ logmsgx("cmsghdr.cmsg_len %u != %zu",
+ (u_int)cmsghdr->cmsg_len, CMSG_LEN(size));
+ return (-1);
+ }
return (0);
}
-/*
- * Wait for synchronization message (1 byte) with timeout.
- */
static int
-sync_recv(int fd)
+check_groups(const char *gid_arr_str, const gid_t *gid_arr,
+ const char *gid_num_str, int gid_num, bool all_gids)
{
- ssize_t nread;
- char buf;
+ int i;
- dbgmsg(("waiting for sync message"));
+ for (i = 0; i < gid_num; ++i)
+ dbgmsg("%s[%d] %lu", gid_arr_str, i, (u_long)gid_arr[i]);
- if (sigsetjmp(env_alrm, 1) != 0) {
- logmsgx("sync_recv: cannot receive sync message (timeout)");
+ if (all_gids) {
+ if (gid_num != proc_cred.gid_num) {
+ logmsgx("%s %d != %d", gid_num_str, gid_num,
+ proc_cred.gid_num);
+ return (-1);
+ }
+ } else {
+ if (gid_num > proc_cred.gid_num) {
+ logmsgx("%s %d > %d", gid_num_str, gid_num,
+ proc_cred.gid_num);
+ return (-1);
+ }
+ }
+ if (memcmp(gid_arr, proc_cred.gid_arr,
+ gid_num * sizeof(*gid_arr)) != 0) {
+ logmsgx("%s content is wrong", gid_arr_str);
+ for (i = 0; i < gid_num; ++i)
+ if (gid_arr[i] != proc_cred.gid_arr[i]) {
+ logmsgx("%s[%d] %lu != %lu",
+ gid_arr_str, i, (u_long)gid_arr[i],
+ (u_long)proc_cred.gid_arr[i]);
+ break;
+ }
return (-1);
}
+ return (0);
+}
- (void)alarm(TIMEOUT);
-
- nread = read(fd, &buf, 1);
+static int
+check_xucred(const struct xucred *xucred, socklen_t len)
+{
+ if (len != sizeof(*xucred)) {
+ logmsgx("option value size %zu != %zu",
+ (size_t)len, sizeof(*xucred));
+ return (-1);
+ }
- (void)alarm(0);
+ dbgmsg("xucred.cr_version %u", xucred->cr_version);
+ dbgmsg("xucred.cr_uid %lu", (u_long)xucred->cr_uid);
+ dbgmsg("xucred.cr_ngroups %d", xucred->cr_ngroups);
- if (nread < 0) {
- logmsg("sync_recv: read");
+ if (xucred->cr_version != XUCRED_VERSION) {
+ logmsgx("xucred.cr_version %u != %d",
+ xucred->cr_version, XUCRED_VERSION);
return (-1);
}
-
- if (nread != 1) {
- logmsgx("sync_recv: read: short read: %ld of 1 byte",
- (long)nread);
+ if (xucred->cr_uid != proc_cred.euid) {
+ logmsgx("xucred.cr_uid %lu != %lu (EUID)",
+ (u_long)xucred->cr_uid, (u_long)proc_cred.euid);
return (-1);
}
-
+ if (xucred->cr_ngroups == 0) {
+ logmsgx("xucred.cr_ngroups == 0");
+ return (-1);
+ }
+ if (xucred->cr_ngroups < 0) {
+ logmsgx("xucred.cr_ngroups < 0");
+ return (-1);
+ }
+ if (xucred->cr_ngroups > XU_NGROUPS) {
+ logmsgx("xucred.cr_ngroups %hu > %u (max)",
+ xucred->cr_ngroups, XU_NGROUPS);
+ return (-1);
+ }
+ if (xucred->cr_groups[0] != proc_cred.egid) {
+ logmsgx("xucred.cr_groups[0] %lu != %lu (EGID)",
+ (u_long)xucred->cr_groups[0], (u_long)proc_cred.egid);
+ return (-1);
+ }
+ if (check_groups("xucred.cr_groups", xucred->cr_groups,
+ "xucred.cr_ngroups", xucred->cr_ngroups, false) < 0)
+ return (-1);
return (0);
}
-/*
- * Send synchronization message (1 byte) with timeout.
- */
static int
-sync_send(int fd)
+check_scm_creds_cmsgcred(struct cmsghdr *cmsghdr)
{
- ssize_t nsent;
-
- dbgmsg(("sending sync message"));
+ const struct cmsgcred *cmsgcred;
- if (sigsetjmp(env_alrm, 1) != 0) {
- logmsgx("sync_send: cannot send sync message (timeout)");
+ if (check_cmsghdr(cmsghdr, SCM_CREDS, sizeof(*cmsgcred)) < 0)
return (-1);
- }
-
- (void)alarm(TIMEOUT);
- nsent = write(fd, "", 1);
+ cmsgcred = (struct cmsgcred *)CMSG_DATA(cmsghdr);
- (void)alarm(0);
+ dbgmsg("cmsgcred.cmcred_pid %ld", (long)cmsgcred->cmcred_pid);
+ dbgmsg("cmsgcred.cmcred_uid %lu", (u_long)cmsgcred->cmcred_uid);
+ dbgmsg("cmsgcred.cmcred_euid %lu", (u_long)cmsgcred->cmcred_euid);
+ dbgmsg("cmsgcred.cmcred_gid %lu", (u_long)cmsgcred->cmcred_gid);
+ dbgmsg("cmsgcred.cmcred_ngroups %d", cmsgcred->cmcred_ngroups);
- if (nsent < 0) {
- logmsg("sync_send: write");
+ if (cmsgcred->cmcred_pid != client_pid) {
+ logmsgx("cmsgcred.cmcred_pid %ld != %ld",
+ (long)cmsgcred->cmcred_pid, (long)client_pid);
return (-1);
}
-
- if (nsent != 1) {
- logmsgx("sync_send: write: short write: %ld of 1 byte",
- (long)nsent);
+ if (cmsgcred->cmcred_uid != proc_cred.uid) {
+ logmsgx("cmsgcred.cmcred_uid %lu != %lu",
+ (u_long)cmsgcred->cmcred_uid, (u_long)proc_cred.uid);
return (-1);
}
-
+ if (cmsgcred->cmcred_euid != proc_cred.euid) {
+ logmsgx("cmsgcred.cmcred_euid %lu != %lu",
+ (u_long)cmsgcred->cmcred_euid, (u_long)proc_cred.euid);
+ return (-1);
+ }
+ if (cmsgcred->cmcred_gid != proc_cred.gid) {
+ logmsgx("cmsgcred.cmcred_gid %lu != %lu",
+ (u_long)cmsgcred->cmcred_gid, (u_long)proc_cred.gid);
+ return (-1);
+ }
+ if (cmsgcred->cmcred_ngroups == 0) {
+ logmsgx("cmsgcred.cmcred_ngroups == 0");
+ return (-1);
+ }
+ if (cmsgcred->cmcred_ngroups < 0) {
+ logmsgx("cmsgcred.cmcred_ngroups %d < 0",
+ cmsgcred->cmcred_ngroups);
+ return (-1);
+ }
+ if (cmsgcred->cmcred_ngroups > CMGROUP_MAX) {
+ logmsgx("cmsgcred.cmcred_ngroups %d > %d",
+ cmsgcred->cmcred_ngroups, CMGROUP_MAX);
+ return (-1);
+ }
+ if (cmsgcred->cmcred_groups[0] != proc_cred.egid) {
+ logmsgx("cmsgcred.cmcred_groups[0] %lu != %lu (EGID)",
+ (u_long)cmsgcred->cmcred_groups[0], (u_long)proc_cred.egid);
+ return (-1);
+ }
+ if (check_groups("cmsgcred.cmcred_groups", cmsgcred->cmcred_groups,
+ "cmsgcred.cmcred_ngroups", cmsgcred->cmcred_ngroups, false) < 0)
+ return (-1);
return (0);
}
-/*
- * waitpid() for client with timeout.
- */
static int
-wait_client(void)
+check_scm_creds_sockcred(struct cmsghdr *cmsghdr)
{
- int status;
- pid_t pid;
+ const struct sockcred *sockcred;
- if (sigsetjmp(env_alrm, 1) != 0) {
- logmsgx("wait_client: cannot get exit status of client PID %ld (timeout)",
- (long)client_pid);
+ if (check_cmsghdr(cmsghdr, SCM_CREDS,
+ SOCKCREDSIZE(proc_cred.gid_num)) < 0)
return (-1);
- }
- (void)alarm(TIMEOUT);
-
- pid = waitpid(client_pid, &status, 0);
+ sockcred = (struct sockcred *)CMSG_DATA(cmsghdr);
- (void)alarm(0);
+ dbgmsg("sockcred.sc_uid %lu", (u_long)sockcred->sc_uid);
+ dbgmsg("sockcred.sc_euid %lu", (u_long)sockcred->sc_euid);
+ dbgmsg("sockcred.sc_gid %lu", (u_long)sockcred->sc_gid);
+ dbgmsg("sockcred.sc_egid %lu", (u_long)sockcred->sc_egid);
+ dbgmsg("sockcred.sc_ngroups %d", sockcred->sc_ngroups);
- if (pid == (pid_t)-1) {
- logmsg("wait_client: waitpid");
+ if (sockcred->sc_uid != proc_cred.uid) {
+ logmsgx("sockcred.sc_uid %lu != %lu",
+ (u_long)sockcred->sc_uid, (u_long)proc_cred.uid);
return (-1);
}
-
- if (WIFEXITED(status)) {
- if (WEXITSTATUS(status) != 0) {
- logmsgx("wait_client: exit status of client PID %ld is %d",
- (long)client_pid, WEXITSTATUS(status));
- return (-1);
- }
- } else {
- if (WIFSIGNALED(status))
- logmsgx("wait_client: abnormal termination of client PID %ld, signal %d%s",
- (long)client_pid, WTERMSIG(status), WCOREDUMP(status) ? " (core file generated)" : "");
- else
- logmsgx("wait_client: termination of client PID %ld, unknown status",
- (long)client_pid);
+ if (sockcred->sc_euid != proc_cred.euid) {
+ logmsgx("sockcred.sc_euid %lu != %lu",
+ (u_long)sockcred->sc_euid, (u_long)proc_cred.euid);
return (-1);
}
-
+ if (sockcred->sc_gid != proc_cred.gid) {
+ logmsgx("sockcred.sc_gid %lu != %lu",
+ (u_long)sockcred->sc_gid, (u_long)proc_cred.gid);
+ return (-1);
+ }
+ if (sockcred->sc_egid != proc_cred.egid) {
+ logmsgx("sockcred.sc_egid %lu != %lu",
+ (u_long)sockcred->sc_egid, (u_long)proc_cred.egid);
+ return (-1);
+ }
+ if (sockcred->sc_ngroups == 0) {
+ logmsgx("sockcred.sc_ngroups == 0");
+ return (-1);
+ }
+ if (sockcred->sc_ngroups < 0) {
+ logmsgx("sockcred.sc_ngroups %d < 0",
+ sockcred->sc_ngroups);
+ return (-1);
+ }
+ if (sockcred->sc_ngroups != proc_cred.gid_num) {
+ logmsgx("sockcred.sc_ngroups %d != %u",
+ sockcred->sc_ngroups, proc_cred.gid_num);
+ return (-1);
+ }
+ if (check_groups("sockcred.sc_groups", sockcred->sc_groups,
+ "sockcred.sc_ngroups", sockcred->sc_ngroups, true) < 0)
+ return (-1);
return (0);
}
-/*
- * Check if n supplementary GIDs in gids are correct. (my_gids + 1)
- * has (my_ngids - 1) supplementary GIDs of current process.
- */
static int
-check_groups(const gid_t *gids, int n)
+check_scm_timestamp(struct cmsghdr *cmsghdr)
{
- char match[NGROUPS_MAX] = { 0 };
- int error, i, j;
+ const struct timeval *timeval;
- if (n != my_ngids - 1) {
- logmsgx("wrong number of groups %d != %d (returned from getgroups() - 1)",
- n, my_ngids - 1);
- error = -1;
- } else
- error = 0;
- for (i = 0; i < n; ++i) {
- for (j = 1; j < my_ngids; ++j) {
- if (gids[i] == my_gids[j]) {
- if (match[j]) {
- logmsgx("duplicated GID %lu",
- (u_long)gids[i]);
- error = -1;
- } else
- match[j] = 1;
- break;
- }
- }
- if (j == my_ngids) {
- logmsgx("unexpected GID %lu", (u_long)gids[i]);
- error = -1;
- }
- }
- for (j = 1; j < my_ngids; ++j)
- if (match[j] == 0) {
- logmsgx("did not receive supplementary GID %u", my_gids[j]);
- error = -1;
- }
- return (error);
-}
+ if (check_cmsghdr(cmsghdr, SCM_TIMESTAMP, sizeof(struct timeval)) < 0)
+ return (-1);
-/*
- * Send n messages with data and control message with SCM_CREDS type
- * to server and exit.
- */
-static void
-t_cmsgcred_client(u_int n)
-{
- union {
- struct cmsghdr cm;
- char control[CMSG_SPACE(sizeof(struct cmsgcred))];
- } control_un;
- struct msghdr msg;
- struct iovec iov[1];
- struct cmsghdr *cmptr;
- int fd;
- u_int i;
+ timeval = (struct timeval *)CMSG_DATA(cmsghdr);
- assert(n == 1 || n == 2);
+ dbgmsg("timeval.tv_sec %"PRIdMAX", timeval.tv_usec %"PRIdMAX,
+ (intmax_t)timeval->tv_sec, (intmax_t)timeval->tv_usec);
- if ((fd = create_unbound_socket()) < 0)
- goto failed;
+ return (0);
+}
- if (connect_server(fd) < 0)
- goto failed_close;
+static int
+check_scm_bintime(struct cmsghdr *cmsghdr)
+{
+ const struct bintime *bintime;
+
+ if (check_cmsghdr(cmsghdr, SCM_BINTIME, sizeof(struct bintime)) < 0)
+ return (-1);
- iov[0].iov_base = ipc_message;
- iov[0].iov_len = IPC_MESSAGE_SIZE;
+ bintime = (struct bintime *)CMSG_DATA(cmsghdr);
- msg.msg_name = NULL;
- msg.msg_namelen = 0;
- msg.msg_iov = iov;
- msg.msg_iovlen = 1;
- msg.msg_control = control_un.control;
- msg.msg_controllen = no_control_data ?
- sizeof(struct cmsghdr) : sizeof(control_un.control);
- msg.msg_flags = 0;
+ dbgmsg("bintime.sec %"PRIdMAX", bintime.frac %"PRIu64,
+ (intmax_t)bintime->sec, bintime->frac);
- cmptr = CMSG_FIRSTHDR(&msg);
- cmptr->cmsg_len = CMSG_LEN(no_control_data ?
- 0 : sizeof(struct cmsgcred));
- cmptr->cmsg_level = SOL_SOCKET;
- cmptr->cmsg_type = SCM_CREDS;
+ return (0);
+}
- for (i = 0; i < n; ++i) {
- dbgmsg(("#%u msg_controllen = %u, cmsg_len = %u", i,
- (u_int)msg.msg_controllen, (u_int)cmptr->cmsg_len));
- if (sendmsg_timeout(fd, &msg, IPC_MESSAGE_SIZE) < 0)
- goto failed_close;
+static void
+msghdr_init_generic(struct msghdr *msghdr, struct iovec *iov, void *cmsg_data)
+{
+ msghdr->msg_name = NULL;
+ msghdr->msg_namelen = 0;
+ if (send_data_flag) {
+ iov->iov_base = server_flag ?
+ ipc_msg.buf_recv : ipc_msg.buf_send;
+ iov->iov_len = ipc_msg.buf_size;
+ msghdr->msg_iov = iov;
+ msghdr->msg_iovlen = 1;
+ } else {
+ msghdr->msg_iov = NULL;
+ msghdr->msg_iovlen = 0;
}
+ msghdr->msg_control = cmsg_data;
+ msghdr->msg_flags = 0;
+}
- if (close_socket((const char *)NULL, fd) < 0)
- goto failed;
+static void
+msghdr_init_server(struct msghdr *msghdr, struct iovec *iov,
+ void *cmsg_data, size_t cmsg_size)
+{
+ msghdr_init_generic(msghdr, iov, cmsg_data);
+ msghdr->msg_controllen = cmsg_size;
+ dbgmsg("init: data size %zu", msghdr->msg_iov != NULL ?
+ msghdr->msg_iov->iov_len : (size_t)0);
+ dbgmsg("init: msghdr.msg_controllen %u",
+ (u_int)msghdr->msg_controllen);
+}
- _exit(0);
+static void
+msghdr_init_client(struct msghdr *msghdr, struct iovec *iov,
+ void *cmsg_data, size_t cmsg_size, int type, size_t arr_size)
+{
+ struct cmsghdr *cmsghdr;
+
+ msghdr_init_generic(msghdr, iov, cmsg_data);
+ if (cmsg_data != NULL) {
+ msghdr->msg_controllen = send_array_flag ?
+ cmsg_size : CMSG_SPACE(0);
+ cmsghdr = CMSG_FIRSTHDR(msghdr);
+ cmsghdr->cmsg_level = SOL_SOCKET;
+ cmsghdr->cmsg_type = type;
+ cmsghdr->cmsg_len = CMSG_LEN(send_array_flag ? arr_size : 0);
+ } else
+ msghdr->msg_controllen = 0;
+}
-failed_close:
- (void)close_socket((const char *)NULL, fd);
+static int
+t_generic(int (*client_func)(int), int (*server_func)(int))
+{
+ int fd, rv, rv_client;
-failed:
- _exit(1);
+ switch (client_fork()) {
+ case 0:
+ fd = socket_create();
+ if (fd < 0)
+ rv = -2;
+ else {
+ rv = client_func(fd);
+ if (socket_close(fd) < 0)
+ rv = -2;
+ }
+ client_exit(rv);
+ break;
+ case 1:
+ fd = socket_create();
+ if (fd < 0)
+ rv = -2;
+ else {
+ rv = server_func(fd);
+ rv_client = client_wait();
+ if (rv == 0 || (rv == -2 && rv_client != 0))
+ rv = rv_client;
+ if (socket_close(fd) < 0)
+ rv = -2;
+ }
+ break;
+ default:
+ rv = -2;
+ }
+ return (rv);
}
-/*
- * Receive two messages with data and control message with SCM_CREDS
- * type followed by struct cmsgcred{} from client. fd1 is a listen
- * socket for stream sockets or simply socket for datagram sockets.
- */
static int
-t_cmsgcred_server(int fd1)
+t_cmsgcred_client(int fd)
{
- char buf[IPC_MESSAGE_SIZE];
- union {
- struct cmsghdr cm;
- char control[CMSG_SPACE(sizeof(struct cmsgcred)) + EXTRA_CMSG_SPACE];
- } control_un;
- struct msghdr msg;
+ struct msghdr msghdr;
struct iovec iov[1];
- struct cmsghdr *cmptr;
- const struct cmsgcred *cmcredptr;
- socklen_t controllen;
- int error, error2, fd2;
- u_int i;
-
- if (sock_type == SOCK_STREAM) {
- if ((fd2 = accept_timeout(fd1)) < 0)
- return (-2);
- } else
- fd2 = fd1;
-
- error = 0;
+ void *cmsg_data;
+ size_t cmsg_size;
+ int rv;
- controllen = sizeof(control_un.control);
-
- for (i = 0; i < 2; ++i) {
- iov[0].iov_base = buf;
- iov[0].iov_len = sizeof(buf);
+ if (sync_recv() < 0)
+ return (-2);
- msg.msg_name = NULL;
- msg.msg_namelen = 0;
- msg.msg_iov = iov;
- msg.msg_iovlen = 1;
- msg.msg_control = control_un.control;
- msg.msg_controllen = controllen;
- msg.msg_flags = 0;
+ rv = -2;
- controllen = CMSG_SPACE(sizeof(struct cmsgcred));
+ cmsg_size = CMSG_SPACE(sizeof(struct cmsgcred));
+ cmsg_data = malloc(cmsg_size);
+ if (cmsg_data == NULL) {
+ logmsg("malloc");
+ goto done;
+ }
+ msghdr_init_client(&msghdr, iov, cmsg_data, cmsg_size,
+ SCM_CREDS, sizeof(struct cmsgcred));
- if (recvmsg_timeout(fd2, &msg, sizeof(buf)) < 0)
- goto failed;
+ if (socket_connect(fd) < 0)
+ goto done;
- if (msg.msg_flags & MSG_CTRUNC) {
- logmsgx("#%u control data was truncated, MSG_CTRUNC flag is on",
- i);
- goto next_error;
- }
+ if (message_sendn(fd, &msghdr) < 0)
+ goto done;
- if (msg.msg_controllen < sizeof(struct cmsghdr)) {
- logmsgx("#%u msg_controllen %u < %lu (sizeof(struct cmsghdr))",
- i, (u_int)msg.msg_controllen, (u_long)sizeof(struct cmsghdr));
- goto next_error;
- }
+ rv = 0;
+done:
+ free(cmsg_data);
+ return (rv);
+}
- if ((cmptr = CMSG_FIRSTHDR(&msg)) == NULL) {
- logmsgx("CMSG_FIRSTHDR is NULL");
- goto next_error;
- }
+static int
+t_cmsgcred_server(int fd1)
+{
+ struct msghdr msghdr;
+ struct iovec iov[1];
+ struct cmsghdr *cmsghdr;
+ void *cmsg_data;
+ size_t cmsg_size;
+ u_int i;
+ int fd2, rv;
- dbgmsg(("#%u msg_controllen = %u, cmsg_len = %u", i,
- (u_int)msg.msg_controllen, (u_int)cmptr->cmsg_len));
+ if (sync_send() < 0)
+ return (-2);
- if (cmptr->cmsg_level != SOL_SOCKET) {
- logmsgx("#%u cmsg_level %d != SOL_SOCKET", i,
- cmptr->cmsg_level);
- goto next_error;
- }
+ fd2 = -1;
+ rv = -2;
- if (cmptr->cmsg_type != SCM_CREDS) {
- logmsgx("#%u cmsg_type %d != SCM_CREDS", i,
- cmptr->cmsg_type);
- goto next_error;
- }
+ cmsg_size = CMSG_SPACE(sizeof(struct cmsgcred));
+ cmsg_data = malloc(cmsg_size);
+ if (cmsg_data == NULL) {
+ logmsg("malloc");
+ goto done;
+ }
- if (cmptr->cmsg_len != CMSG_LEN(sizeof(struct cmsgcred))) {
- logmsgx("#%u cmsg_len %u != %lu (CMSG_LEN(sizeof(struct cmsgcred))",
- i, (u_int)cmptr->cmsg_len, (u_long)CMSG_LEN(sizeof(struct cmsgcred)));
- goto next_error;
- }
+ if (sock_type == SOCK_STREAM) {
+ fd2 = socket_accept(fd1);
+ if (fd2 < 0)
+ goto done;
+ } else
+ fd2 = fd1;
- cmcredptr = (const struct cmsgcred *)CMSG_DATA(cmptr);
+ rv = -1;
+ for (i = 1; i <= ipc_msg.msg_num; ++i) {
+ dbgmsg("message #%u", i);
- error2 = 0;
- if (cmcredptr->cmcred_pid != client_pid) {
- logmsgx("#%u cmcred_pid %ld != %ld (PID of client)",
- i, (long)cmcredptr->cmcred_pid, (long)client_pid);
- error2 = 1;
- }
- if (cmcredptr->cmcred_uid != my_uid) {
- logmsgx("#%u cmcred_uid %lu != %lu (UID of current process)",
- i, (u_long)cmcredptr->cmcred_uid, (u_long)my_uid);
- error2 = 1;
- }
- if (cmcredptr->cmcred_euid != my_euid) {
- logmsgx("#%u cmcred_euid %lu != %lu (EUID of current process)",
- i, (u_long)cmcredptr->cmcred_euid, (u_long)my_euid);
- error2 = 1;
- }
- if (cmcredptr->cmcred_gid != my_gid) {
- logmsgx("#%u cmcred_gid %lu != %lu (GID of current process)",
- i, (u_long)cmcredptr->cmcred_gid, (u_long)my_gid);
- error2 = 1;
- }
- if (cmcredptr->cmcred_ngroups == 0) {
- logmsgx("#%u cmcred_ngroups = 0, this is wrong", i);
- error2 = 1;
- } else {
- if (cmcredptr->cmcred_ngroups > NGROUPS_MAX) {
- logmsgx("#%u cmcred_ngroups %d > %u (NGROUPS_MAX)",
- i, cmcredptr->cmcred_ngroups, NGROUPS_MAX);
- error2 = 1;
- } else if (cmcredptr->cmcred_ngroups < 0) {
- logmsgx("#%u cmcred_ngroups %d < 0",
- i, cmcredptr->cmcred_ngroups);
- error2 = 1;
- } else {
- dbgmsg(("#%u cmcred_ngroups = %d", i,
- cmcredptr->cmcred_ngroups));
- if (cmcredptr->cmcred_groups[0] != my_egid) {
- logmsgx("#%u cmcred_groups[0] %lu != %lu (EGID of current process)",
- i, (u_long)cmcredptr->cmcred_groups[0], (u_long)my_egid);
- error2 = 1;
- }
- if (check_groups(cmcredptr->cmcred_groups + 1, cmcredptr->cmcred_ngroups - 1) < 0) {
- logmsgx("#%u cmcred_groups has wrong GIDs", i);
- error2 = 1;
- }
- }
+ msghdr_init_server(&msghdr, iov, cmsg_data, cmsg_size);
+ if (message_recv(fd2, &msghdr) < 0) {
+ rv = -2;
+ break;
}
- if (error2)
- goto next_error;
-
- if ((cmptr = CMSG_NXTHDR(&msg, cmptr)) != NULL) {
- logmsgx("#%u control data has extra header", i);
- goto next_error;
- }
+ if (check_msghdr(&msghdr, sizeof(*cmsghdr)) < 0)
+ break;
- continue;
-next_error:
- error = -1;
+ cmsghdr = CMSG_FIRSTHDR(&msghdr);
+ if (check_scm_creds_cmsgcred(cmsghdr) < 0)
+ break;
}
-
- if (sock_type == SOCK_STREAM)
- if (close(fd2) < 0) {
- logmsg("close");
- return (-2);
- }
- return (error);
-
-failed:
- if (sock_type == SOCK_STREAM)
- if (close(fd2) < 0)
- logmsg("close");
- return (-2);
+ if (i > ipc_msg.msg_num)
+ rv = 0;
+done:
+ free(cmsg_data);
+ if (sock_type == SOCK_STREAM && fd2 >= 0)
+ if (socket_close(fd2) < 0)
+ rv = -2;
+ return (rv);
}
static int
t_cmsgcred(void)
{
- int error, fd;
+ return (t_generic(t_cmsgcred_client, t_cmsgcred_server));
+}
- if ((fd = create_server_socket()) < 0)
- return (-2);
+static int
+t_sockcred_client(int type, int fd)
+{
+ struct msghdr msghdr;
+ struct iovec iov[1];
+ int rv;
- if (sock_type == SOCK_STREAM)
- if (listen(fd, LISTENQ) < 0) {
- logmsg("listen");
- goto failed;
- }
+ if (sync_recv() < 0)
+ return (-2);
- if ((client_pid = fork()) == (pid_t)-1) {
- logmsg("fork");
- goto failed;
- }
+ rv = -2;
- if (client_pid == 0) {
- myname = "CLIENT";
- if (close_socket((const char *)NULL, fd) < 0)
- _exit(1);
- t_cmsgcred_client(2);
- }
+ msghdr_init_client(&msghdr, iov, NULL, 0, 0, 0);
- if ((error = t_cmsgcred_server(fd)) == -2) {
- (void)wait_client();
- goto failed;
- }
+ if (socket_connect(fd) < 0)
+ goto done;
- if (wait_client() < 0)
- goto failed;
+ if (type == 2)
+ if (sync_recv() < 0)
+ goto done;
- if (close_socket(serv_sock_path, fd) < 0) {
- logmsgx("close_socket failed");
- return (-2);
- }
- return (error);
+ if (message_sendn(fd, &msghdr) < 0)
+ goto done;
-failed:
- if (close_socket(serv_sock_path, fd) < 0)
- logmsgx("close_socket failed");
- return (-2);
+ rv = 0;
+done:
+ return (rv);
}
-/*
- * Send two messages with data to server and exit.
- */
-static void
-t_sockcred_client(int type)
+static int
+t_sockcred_server(int type, int fd1)
{
- struct msghdr msg;
+ struct msghdr msghdr;
struct iovec iov[1];
- int fd;
+ struct cmsghdr *cmsghdr;
+ void *cmsg_data;
+ size_t cmsg_size;
u_int i;
+ int fd2, rv, val;
- assert(type == 0 || type == 1);
+ fd2 = -1;
+ rv = -2;
- if ((fd = create_unbound_socket()) < 0)
- goto failed;
-
- if (connect_server(fd) < 0)
- goto failed_close;
+ cmsg_size = CMSG_SPACE(SOCKCREDSIZE(proc_cred.gid_num));
+ cmsg_data = malloc(cmsg_size);
+ if (cmsg_data == NULL) {
+ logmsg("malloc");
+ goto done;
+ }
- if (type == 1)
- if (sync_recv(fd) < 0)
- goto failed_close;
+ if (type == 1) {
+ dbgmsg("setting LOCAL_CREDS");
+ val = 1;
+ if (setsockopt(fd1, 0, LOCAL_CREDS, &val, sizeof(val)) < 0) {
+ logmsg("setsockopt(LOCAL_CREDS)");
+ goto done;
+ }
+ }
- iov[0].iov_base = ipc_message;
- iov[0].iov_len = IPC_MESSAGE_SIZE;
+ if (sync_send() < 0)
+ goto done;
- msg.msg_name = NULL;
- msg.msg_namelen = 0;
- msg.msg_iov = iov;
- msg.msg_iovlen = 1;
- msg.msg_control = NULL;
- msg.msg_controllen = 0;
- msg.msg_flags = 0;
+ if (sock_type == SOCK_STREAM) {
+ fd2 = socket_accept(fd1);
+ if (fd2 < 0)
+ goto done;
+ } else
+ fd2 = fd1;
- for (i = 0; i < 2; ++i)
- if (sendmsg_timeout(fd, &msg, IPC_MESSAGE_SIZE) < 0)
- goto failed_close;
+ if (type == 2) {
+ dbgmsg("setting LOCAL_CREDS");
+ val = 1;
+ if (setsockopt(fd2, 0, LOCAL_CREDS, &val, sizeof(val)) < 0) {
+ logmsg("setsockopt(LOCAL_CREDS)");
+ goto done;
+ }
+ if (sync_send() < 0)
+ goto done;
+ }
- if (close_socket((const char *)NULL, fd) < 0)
- goto failed;
+ rv = -1;
+ for (i = 1; i <= ipc_msg.msg_num; ++i) {
+ dbgmsg("message #%u", i);
- _exit(0);
+ msghdr_init_server(&msghdr, iov, cmsg_data, cmsg_size);
+ if (message_recv(fd2, &msghdr) < 0) {
+ rv = -2;
+ break;
+ }
-failed_close:
- (void)close_socket((const char *)NULL, fd);
+ if (i > 1 && sock_type == SOCK_STREAM) {
+ if (check_msghdr(&msghdr, 0) < 0)
+ break;
+ } else {
+ if (check_msghdr(&msghdr, sizeof(*cmsghdr)) < 0)
+ break;
-failed:
- _exit(1);
+ cmsghdr = CMSG_FIRSTHDR(&msghdr);
+ if (check_scm_creds_sockcred(cmsghdr) < 0)
+ break;
+ }
+ }
+ if (i > ipc_msg.msg_num)
+ rv = 0;
+done:
+ free(cmsg_data);
+ if (sock_type == SOCK_STREAM && fd2 >= 0)
+ if (socket_close(fd2) < 0)
+ rv = -2;
+ return (rv);
}
-/*
- * Receive one message with data and control message with SCM_CREDS
- * type followed by struct sockcred{} and if n is not equal 1, then
- * receive another one message with data. fd1 is a listen socket for
- * stream sockets or simply socket for datagram sockets. If type is
- * 1, then set LOCAL_CREDS option for accepted stream socket.
- */
static int
-t_sockcred_server(int type, int fd1, u_int n)
+t_sockcred_1(void)
{
- char buf[IPC_MESSAGE_SIZE];
- union {
- struct cmsghdr cm;
- char control[CMSG_SPACE(SOCKCREDSIZE(NGROUPS_MAX)) + EXTRA_CMSG_SPACE];
- } control_un;
- struct msghdr msg;
- struct iovec iov[1];
- struct cmsghdr *cmptr;
- const struct sockcred *sockcred;
- int error, error2, fd2, optval;
u_int i;
+ int fd, rv, rv_client;
- assert(n == 1 || n == 2);
- assert(type == 0 || type == 1);
-
- if (sock_type == SOCK_STREAM) {
- if ((fd2 = accept_timeout(fd1)) < 0)
- return (-2);
- if (type == 1) {
- optval = 1;
- if (setsockopt(fd2, 0, LOCAL_CREDS, &optval, sizeof optval) < 0) {
- logmsg("setsockopt(LOCAL_CREDS) for accepted socket");
- if (errno == ENOPROTOOPT) {
- error = -1;
- goto done_close;
- }
- goto failed;
+ switch (client_fork()) {
+ case 0:
+ for (i = 1; i <= 2; ++i) {
+ dbgmsg("client #%u", i);
+ fd = socket_create();
+ if (fd < 0)
+ rv = -2;
+ else {
+ rv = t_sockcred_client(1, fd);
+ if (socket_close(fd) < 0)
+ rv = -2;
}
- if (sync_send(fd2) < 0)
- goto failed;
+ if (rv != 0)
+ break;
}
- } else
- fd2 = fd1;
+ client_exit(rv);
+ break;
+ case 1:
+ fd = socket_create();
+ if (fd < 0)
+ rv = -2;
+ else {
+ rv = t_sockcred_server(1, fd);
+ if (rv == 0)
+ rv = t_sockcred_server(3, fd);
+ rv_client = client_wait();
+ if (rv == 0 || (rv == -2 && rv_client != 0))
+ rv = rv_client;
+ if (socket_close(fd) < 0)
+ rv = -2;
+ }
+ break;
+ default:
+ rv = -2;
+ }
- error = 0;
+ return (rv);
+}
- for (i = 0; i < n; ++i) {
- iov[0].iov_base = buf;
- iov[0].iov_len = sizeof buf;
+static int
+t_sockcred_2_client(int fd)
+{
+ return (t_sockcred_client(2, fd));
+}
- msg.msg_name = NULL;
- msg.msg_namelen = 0;
- msg.msg_iov = iov;
- msg.msg_iovlen = 1;
- msg.msg_control = control_un.control;
- msg.msg_controllen = sizeof control_un.control;
- msg.msg_flags = 0;
+static int
+t_sockcred_2_server(int fd)
+{
+ return (t_sockcred_server(2, fd));
+}
- if (recvmsg_timeout(fd2, &msg, sizeof buf) < 0)
- goto failed;
+static int
+t_sockcred_2(void)
+{
+ return (t_generic(t_sockcred_2_client, t_sockcred_2_server));
+}
- if (msg.msg_flags & MSG_CTRUNC) {
- logmsgx("control data was truncated, MSG_CTRUNC flag is on");
- goto next_error;
- }
+static int
+t_cmsgcred_sockcred_server(int fd1)
+{
+ struct msghdr msghdr;
+ struct iovec iov[1];
+ struct cmsghdr *cmsghdr;
+ void *cmsg_data, *cmsg1_data, *cmsg2_data;
+ size_t cmsg_size, cmsg1_size, cmsg2_size;
+ u_int i;
+ int fd2, rv, val;
- if (i != 0 && sock_type == SOCK_STREAM) {
- if (msg.msg_controllen != 0) {
- logmsgx("second message has control data, this is wrong for stream sockets");
- goto next_error;
- }
- dbgmsg(("#%u msg_controllen = %u", i,
- (u_int)msg.msg_controllen));
- continue;
- }
+ fd2 = -1;
+ rv = -2;
- if (msg.msg_controllen < sizeof(struct cmsghdr)) {
- logmsgx("#%u msg_controllen %u < %lu (sizeof(struct cmsghdr))",
- i, (u_int)msg.msg_controllen, (u_long)sizeof(struct cmsghdr));
- goto next_error;
- }
+ cmsg1_size = CMSG_SPACE(SOCKCREDSIZE(proc_cred.gid_num));
+ cmsg2_size = CMSG_SPACE(sizeof(struct cmsgcred));
+ cmsg1_data = malloc(cmsg1_size);
+ cmsg2_data = malloc(cmsg2_size);
+ if (cmsg1_data == NULL || cmsg2_data == NULL) {
+ logmsg("malloc");
+ goto done;
+ }
- if ((cmptr = CMSG_FIRSTHDR(&msg)) == NULL) {
- logmsgx("CMSG_FIRSTHDR is NULL");
- goto next_error;
- }
+ dbgmsg("setting LOCAL_CREDS");
+ val = 1;
+ if (setsockopt(fd1, 0, LOCAL_CREDS, &val, sizeof(val)) < 0) {
+ logmsg("setsockopt(LOCAL_CREDS)");
+ goto done;
+ }
- dbgmsg(("#%u msg_controllen = %u, cmsg_len = %u", i,
- (u_int)msg.msg_controllen, (u_int)cmptr->cmsg_len));
+ if (sync_send() < 0)
+ goto done;
- if (cmptr->cmsg_level != SOL_SOCKET) {
- logmsgx("#%u cmsg_level %d != SOL_SOCKET", i,
- cmptr->cmsg_level);
- goto next_error;
- }
+ if (sock_type == SOCK_STREAM) {
+ fd2 = socket_accept(fd1);
+ if (fd2 < 0)
+ goto done;
+ } else
+ fd2 = fd1;
- if (cmptr->cmsg_type != SCM_CREDS) {
- logmsgx("#%u cmsg_type %d != SCM_CREDS", i,
- cmptr->cmsg_type);
- goto next_error;
- }
+ cmsg_data = cmsg1_data;
+ cmsg_size = cmsg1_size;
+ rv = -1;
+ for (i = 1; i <= ipc_msg.msg_num; ++i) {
+ dbgmsg("message #%u", i);
- if (cmptr->cmsg_len < CMSG_LEN(SOCKCREDSIZE(1))) {
- logmsgx("#%u cmsg_len %u != %lu (CMSG_LEN(SOCKCREDSIZE(1)))",
- i, (u_int)cmptr->cmsg_len, (u_long)CMSG_LEN(SOCKCREDSIZE(1)));
- goto next_error;
+ msghdr_init_server(&msghdr, iov, cmsg_data, cmsg_size);
+ if (message_recv(fd2, &msghdr) < 0) {
+ rv = -2;
+ break;
}
- sockcred = (const struct sockcred *)CMSG_DATA(cmptr);
+ if (check_msghdr(&msghdr, sizeof(*cmsghdr)) < 0)
+ break;
- error2 = 0;
- if (sockcred->sc_uid != my_uid) {
- logmsgx("#%u sc_uid %lu != %lu (UID of current process)",
- i, (u_long)sockcred->sc_uid, (u_long)my_uid);
- error2 = 1;
- }
- if (sockcred->sc_euid != my_euid) {
- logmsgx("#%u sc_euid %lu != %lu (EUID of current process)",
- i, (u_long)sockcred->sc_euid, (u_long)my_euid);
- error2 = 1;
- }
- if (sockcred->sc_gid != my_gid) {
- logmsgx("#%u sc_gid %lu != %lu (GID of current process)",
- i, (u_long)sockcred->sc_gid, (u_long)my_gid);
- error2 = 1;
- }
- if (sockcred->sc_egid != my_egid) {
- logmsgx("#%u sc_egid %lu != %lu (EGID of current process)",
- i, (u_long)sockcred->sc_gid, (u_long)my_egid);
- error2 = 1;
- }
- if (sockcred->sc_ngroups > NGROUPS_MAX) {
- logmsgx("#%u sc_ngroups %d > %u (NGROUPS_MAX)",
- i, sockcred->sc_ngroups, NGROUPS_MAX);
- error2 = 1;
- } else if (sockcred->sc_ngroups < 0) {
- logmsgx("#%u sc_ngroups %d < 0",
- i, sockcred->sc_ngroups);
- error2 = 1;
+ cmsghdr = CMSG_FIRSTHDR(&msghdr);
+ if (i == 1 || sock_type == SOCK_DGRAM) {
+ if (check_scm_creds_sockcred(cmsghdr) < 0)
+ break;
} else {
- dbgmsg(("#%u sc_ngroups = %d", i, sockcred->sc_ngroups));
- if (check_groups(sockcred->sc_groups, sockcred->sc_ngroups) < 0) {
- logmsgx("#%u sc_groups has wrong GIDs", i);
- error2 = 1;
- }
- }
-
- if (error2)
- goto next_error;
-
- if ((cmptr = CMSG_NXTHDR(&msg, cmptr)) != NULL) {
- logmsgx("#%u control data has extra header, this is wrong",
- i);
- goto next_error;
+ if (check_scm_creds_cmsgcred(cmsghdr) < 0)
+ break;
}
- continue;
-next_error:
- error = -1;
+ cmsg_data = cmsg2_data;
+ cmsg_size = cmsg2_size;
}
-
-done_close:
- if (sock_type == SOCK_STREAM)
- if (close(fd2) < 0) {
- logmsg("close");
- return (-2);
- }
- return (error);
-
-failed:
- if (sock_type == SOCK_STREAM)
- if (close(fd2) < 0)
- logmsg("close");
- return (-2);
+ if (i > ipc_msg.msg_num)
+ rv = 0;
+done:
+ free(cmsg1_data);
+ free(cmsg2_data);
+ if (sock_type == SOCK_STREAM && fd2 >= 0)
+ if (socket_close(fd2) < 0)
+ rv = -2;
+ return (rv);
}
static int
-t_sockcred(int type)
+t_cmsgcred_sockcred(void)
{
- int error, fd, optval;
+ return (t_generic(t_cmsgcred_client, t_cmsgcred_sockcred_server));
+}
- assert(type == 0 || type == 1);
+static int
+t_timeval_client(int fd)
+{
+ struct msghdr msghdr;
+ struct iovec iov[1];
+ void *cmsg_data;
+ size_t cmsg_size;
+ int rv;
- if ((fd = create_server_socket()) < 0)
+ if (sync_recv() < 0)
return (-2);
- if (sock_type == SOCK_STREAM)
- if (listen(fd, LISTENQ) < 0) {
- logmsg("listen");
- goto failed;
- }
+ rv = -2;
- if (type == 0) {
- optval = 1;
- if (setsockopt(fd, 0, LOCAL_CREDS, &optval, sizeof optval) < 0) {
- logmsg("setsockopt(LOCAL_CREDS) for %s socket",
- sock_type == SOCK_STREAM ? "stream listening" : "datagram");
- if (errno == ENOPROTOOPT) {
- error = -1;
- goto done_close;
- }
- goto failed;
- }
+ cmsg_size = CMSG_SPACE(sizeof(struct timeval));
+ cmsg_data = malloc(cmsg_size);
+ if (cmsg_data == NULL) {
+ logmsg("malloc");
+ goto done;
}
+ msghdr_init_client(&msghdr, iov, cmsg_data, cmsg_size,
+ SCM_TIMESTAMP, sizeof(struct timeval));
- if ((client_pid = fork()) == (pid_t)-1) {
- logmsg("fork");
- goto failed;
- }
+ if (socket_connect(fd) < 0)
+ goto done;
- if (client_pid == 0) {
- myname = "CLIENT";
- if (close_socket((const char *)NULL, fd) < 0)
- _exit(1);
- t_sockcred_client(type);
- }
+ if (message_sendn(fd, &msghdr) < 0)
+ goto done;
- if ((error = t_sockcred_server(type, fd, 2)) == -2) {
- (void)wait_client();
- goto failed;
- }
+ rv = 0;
+done:
+ free(cmsg_data);
+ return (rv);
+}
- if (wait_client() < 0)
- goto failed;
+static int
+t_timeval_server(int fd1)
+{
+ struct msghdr msghdr;
+ struct iovec iov[1];
+ struct cmsghdr *cmsghdr;
+ void *cmsg_data;
+ size_t cmsg_size;
+ u_int i;
+ int fd2, rv;
-done_close:
- if (close_socket(serv_sock_path, fd) < 0) {
- logmsgx("close_socket failed");
+ if (sync_send() < 0)
return (-2);
+
+ fd2 = -1;
+ rv = -2;
+
+ cmsg_size = CMSG_SPACE(sizeof(struct timeval));
+ cmsg_data = malloc(cmsg_size);
+ if (cmsg_data == NULL) {
+ logmsg("malloc");
+ goto done;
}
- return (error);
-failed:
- if (close_socket(serv_sock_path, fd) < 0)
- logmsgx("close_socket failed");
- return (-2);
-}
+ if (sock_type == SOCK_STREAM) {
+ fd2 = socket_accept(fd1);
+ if (fd2 < 0)
+ goto done;
+ } else
+ fd2 = fd1;
-static int
-t_sockcred_stream1(void)
-{
- return (t_sockcred(0));
-}
+ rv = -1;
+ for (i = 1; i <= ipc_msg.msg_num; ++i) {
+ dbgmsg("message #%u", i);
-static int
-t_sockcred_stream2(void)
-{
- return (t_sockcred(1));
+ msghdr_init_server(&msghdr, iov, cmsg_data, cmsg_size);
+ if (message_recv(fd2, &msghdr) < 0) {
+ rv = -2;
+ break;
+ }
+
+ if (check_msghdr(&msghdr, sizeof(*cmsghdr)) < 0)
+ break;
+
+ cmsghdr = CMSG_FIRSTHDR(&msghdr);
+ if (check_scm_timestamp(cmsghdr) < 0)
+ break;
+ }
+ if (i > ipc_msg.msg_num)
+ rv = 0;
+done:
+ free(cmsg_data);
+ if (sock_type == SOCK_STREAM && fd2 >= 0)
+ if (socket_close(fd2) < 0)
+ rv = -2;
+ return (rv);
}
static int
-t_sockcred_dgram(void)
+t_timeval(void)
{
- return (t_sockcred(0));
+ return (t_generic(t_timeval_client, t_timeval_server));
}
static int
-t_cmsgcred_sockcred(void)
+t_bintime_client(int fd)
{
- int error, fd, optval;
+ struct msghdr msghdr;
+ struct iovec iov[1];
+ void *cmsg_data;
+ size_t cmsg_size;
+ int rv;
- if ((fd = create_server_socket()) < 0)
+ if (sync_recv() < 0)
return (-2);
- if (sock_type == SOCK_STREAM)
- if (listen(fd, LISTENQ) < 0) {
- logmsg("listen");
- goto failed;
- }
+ rv = -2;
- optval = 1;
- if (setsockopt(fd, 0, LOCAL_CREDS, &optval, sizeof optval) < 0) {
- logmsg("setsockopt(LOCAL_CREDS) for %s socket",
- sock_type == SOCK_STREAM ? "stream listening" : "datagram");
- if (errno == ENOPROTOOPT) {
- error = -1;
- goto done_close;
- }
- goto failed;
- }
-
- if ((client_pid = fork()) == (pid_t)-1) {
- logmsg("fork");
- goto failed;
- }
-
- if (client_pid == 0) {
- myname = "CLIENT";
- if (close_socket((const char *)NULL, fd) < 0)
- _exit(1);
- t_cmsgcred_client(1);
- }
-
- if ((error = t_sockcred_server(0, fd, 1)) == -2) {
- (void)wait_client();
- goto failed;
+ cmsg_size = CMSG_SPACE(sizeof(struct bintime));
+ cmsg_data = malloc(cmsg_size);
+ if (cmsg_data == NULL) {
+ logmsg("malloc");
+ goto done;
}
+ msghdr_init_client(&msghdr, iov, cmsg_data, cmsg_size,
+ SCM_BINTIME, sizeof(struct bintime));
- if (wait_client() < 0)
- goto failed;
+ if (socket_connect(fd) < 0)
+ goto done;
-done_close:
- if (close_socket(serv_sock_path, fd) < 0) {
- logmsgx("close_socket failed");
- return (-2);
- }
- return (error);
+ if (message_sendn(fd, &msghdr) < 0)
+ goto done;
-failed:
- if (close_socket(serv_sock_path, fd) < 0)
- logmsgx("close_socket failed");
- return (-2);
+ rv = 0;
+done:
+ free(cmsg_data);
+ return (rv);
}
-/*
- * Send one message with data and control message with SCM_TIMESTAMP
- * type to server and exit.
- */
-static void
-t_timestamp_client(void)
+static int
+t_bintime_server(int fd1)
{
- union {
- struct cmsghdr cm;
- char control[CMSG_SPACE(sizeof(struct timeval))];
- } control_un;
- struct msghdr msg;
+ struct msghdr msghdr;
struct iovec iov[1];
- struct cmsghdr *cmptr;
- int fd;
-
- if ((fd = create_unbound_socket()) < 0)
- goto failed;
-
- if (connect_server(fd) < 0)
- goto failed_close;
+ struct cmsghdr *cmsghdr;
+ void *cmsg_data;
+ size_t cmsg_size;
+ u_int i;
+ int fd2, rv;
- iov[0].iov_base = ipc_message;
- iov[0].iov_len = IPC_MESSAGE_SIZE;
+ if (sync_send() < 0)
+ return (-2);
- msg.msg_name = NULL;
- msg.msg_namelen = 0;
- msg.msg_iov = iov;
- msg.msg_iovlen = 1;
- msg.msg_control = control_un.control;
- msg.msg_controllen = no_control_data ?
- sizeof(struct cmsghdr) :sizeof control_un.control;
- msg.msg_flags = 0;
+ fd2 = -1;
+ rv = -2;
- cmptr = CMSG_FIRSTHDR(&msg);
- cmptr->cmsg_len = CMSG_LEN(no_control_data ?
- 0 : sizeof(struct timeval));
- cmptr->cmsg_level = SOL_SOCKET;
- cmptr->cmsg_type = SCM_TIMESTAMP;
+ cmsg_size = CMSG_SPACE(sizeof(struct bintime));
+ cmsg_data = malloc(cmsg_size);
+ if (cmsg_data == NULL) {
+ logmsg("malloc");
+ goto done;
+ }
- dbgmsg(("msg_controllen = %u, cmsg_len = %u",
- (u_int)msg.msg_controllen, (u_int)cmptr->cmsg_len));
+ if (sock_type == SOCK_STREAM) {
+ fd2 = socket_accept(fd1);
+ if (fd2 < 0)
+ goto done;
+ } else
+ fd2 = fd1;
- if (sendmsg_timeout(fd, &msg, IPC_MESSAGE_SIZE) < 0)
- goto failed_close;
+ rv = -1;
+ for (i = 1; i <= ipc_msg.msg_num; ++i) {
+ dbgmsg("message #%u", i);
- if (close_socket((const char *)NULL, fd) < 0)
- goto failed;
+ msghdr_init_server(&msghdr, iov, cmsg_data, cmsg_size);
+ if (message_recv(fd2, &msghdr) < 0) {
+ rv = -2;
+ break;
+ }
- _exit(0);
+ if (check_msghdr(&msghdr, sizeof(*cmsghdr)) < 0)
+ break;
-failed_close:
- (void)close_socket((const char *)NULL, fd);
+ cmsghdr = CMSG_FIRSTHDR(&msghdr);
+ if (check_scm_bintime(cmsghdr) < 0)
+ break;
+ }
+ if (i > ipc_msg.msg_num)
+ rv = 0;
+done:
+ free(cmsg_data);
+ if (sock_type == SOCK_STREAM && fd2 >= 0)
+ if (socket_close(fd2) < 0)
+ rv = -2;
+ return (rv);
+}
-failed:
- _exit(1);
+static int
+t_bintime(void)
+{
+ return (t_generic(t_bintime_client, t_bintime_server));
}
-/*
- * Receive one message with data and control message with SCM_TIMESTAMP
- * type followed by struct timeval{} from client.
- */
static int
-t_timestamp_server(int fd1)
+t_cmsg_len_client(int fd)
{
- union {
- struct cmsghdr cm;
- char control[CMSG_SPACE(sizeof(struct timeval)) + EXTRA_CMSG_SPACE];
- } control_un;
- char buf[IPC_MESSAGE_SIZE];
- int error, fd2;
- struct msghdr msg;
+ struct msghdr msghdr;
struct iovec iov[1];
- struct cmsghdr *cmptr;
- const struct timeval *timeval;
+ struct cmsghdr *cmsghdr;
+ void *cmsg_data;
+ size_t size, cmsg_size;
+ socklen_t socklen;
+ int rv;
- if (sock_type == SOCK_STREAM) {
- if ((fd2 = accept_timeout(fd1)) < 0)
- return (-2);
- } else
- fd2 = fd1;
-
- iov[0].iov_base = buf;
- iov[0].iov_len = sizeof buf;
-
- msg.msg_name = NULL;
- msg.msg_namelen = 0;
- msg.msg_iov = iov;
- msg.msg_iovlen = 1;
- msg.msg_control = control_un.control;
- msg.msg_controllen = sizeof control_un.control;
- msg.msg_flags = 0;
-
- if (recvmsg_timeout(fd2, &msg, sizeof buf) < 0)
- goto failed;
+ if (sync_recv() < 0)
+ return (-2);
- error = -1;
+ rv = -2;
- if (msg.msg_flags & MSG_CTRUNC) {
- logmsgx("control data was truncated, MSG_CTRUNC flag is on");
+ cmsg_size = CMSG_SPACE(sizeof(struct cmsgcred));
+ cmsg_data = malloc(cmsg_size);
+ if (cmsg_data == NULL) {
+ logmsg("malloc");
goto done;
}
+ msghdr_init_client(&msghdr, iov, cmsg_data, cmsg_size,
+ SCM_CREDS, sizeof(struct cmsgcred));
+ cmsghdr = CMSG_FIRSTHDR(&msghdr);
- if (msg.msg_controllen < sizeof(struct cmsghdr)) {
- logmsgx("msg_controllen %u < %lu (sizeof(struct cmsghdr))",
- (u_int)msg.msg_controllen, (u_long)sizeof(struct cmsghdr));
+ if (socket_connect(fd) < 0)
goto done;
- }
- if ((cmptr = CMSG_FIRSTHDR(&msg)) == NULL) {
- logmsgx("CMSG_FIRSTHDR is NULL");
- goto done;
+ size = msghdr.msg_iov != NULL ? msghdr.msg_iov->iov_len : 0;
+ rv = -1;
+ for (socklen = 0; socklen < CMSG_LEN(0); ++socklen) {
+ cmsghdr->cmsg_len = socklen;
+ dbgmsg("send: data size %zu", size);
+ dbgmsg("send: msghdr.msg_controllen %u",
+ (u_int)msghdr.msg_controllen);
+ dbgmsg("send: cmsghdr.cmsg_len %u",
+ (u_int)cmsghdr->cmsg_len);
+ if (sendmsg(fd, &msghdr, 0) < 0)
+ continue;
+ logmsgx("sent message with cmsghdr.cmsg_len %u < %u",
+ (u_int)cmsghdr->cmsg_len, (u_int)CMSG_LEN(0));
+ break;
}
+ if (socklen == CMSG_LEN(0))
+ rv = 0;
- dbgmsg(("msg_controllen = %u, cmsg_len = %u",
- (u_int)msg.msg_controllen, (u_int)cmptr->cmsg_len));
-
- if (cmptr->cmsg_level != SOL_SOCKET) {
- logmsgx("cmsg_level %d != SOL_SOCKET", cmptr->cmsg_level);
+ if (sync_send() < 0) {
+ rv = -2;
goto done;
}
+done:
+ free(cmsg_data);
+ return (rv);
+}
- if (cmptr->cmsg_type != SCM_TIMESTAMP) {
- logmsgx("cmsg_type %d != SCM_TIMESTAMP", cmptr->cmsg_type);
- goto done;
- }
+static int
+t_cmsg_len_server(int fd1)
+{
+ int fd2, rv;
- if (cmptr->cmsg_len != CMSG_LEN(sizeof(struct timeval))) {
- logmsgx("cmsg_len %u != %lu (CMSG_LEN(sizeof(struct timeval))",
- (u_int)cmptr->cmsg_len, (u_long)CMSG_LEN(sizeof(struct timeval)));
- goto done;
- }
+ if (sync_send() < 0)
+ return (-2);
- timeval = (const struct timeval *)CMSG_DATA(cmptr);
+ rv = -2;
- dbgmsg(("timeval tv_sec %jd, tv_usec %jd",
- (intmax_t)timeval->tv_sec, (intmax_t)timeval->tv_usec));
+ if (sock_type == SOCK_STREAM) {
+ fd2 = socket_accept(fd1);
+ if (fd2 < 0)
+ goto done;
+ } else
+ fd2 = fd1;
- if ((cmptr = CMSG_NXTHDR(&msg, cmptr)) != NULL) {
- logmsgx("control data has extra header");
+ if (sync_recv() < 0)
goto done;
- }
-
- error = 0;
+ rv = 0;
done:
- if (sock_type == SOCK_STREAM)
- if (close(fd2) < 0) {
- logmsg("close");
- return (-2);
- }
- return (error);
+ if (sock_type == SOCK_STREAM && fd2 >= 0)
+ if (socket_close(fd2) < 0)
+ rv = -2;
+ return (rv);
+}
-failed:
- if (sock_type == SOCK_STREAM)
- if (close(fd2) < 0)
- logmsg("close");
- return (-2);
+static int
+t_cmsg_len(void)
+{
+ return (t_generic(t_cmsg_len_client, t_cmsg_len_server));
}
static int
-t_timestamp(void)
+t_peercred_client(int fd)
{
- int error, fd;
+ struct xucred xucred;
+ socklen_t len;
- if ((fd = create_server_socket()) < 0)
- return (-2);
+ if (sync_recv() < 0)
+ return (-1);
- if (sock_type == SOCK_STREAM)
- if (listen(fd, LISTENQ) < 0) {
- logmsg("listen");
- goto failed;
- }
+ if (socket_connect(fd) < 0)
+ return (-1);
- if ((client_pid = fork()) == (pid_t)-1) {
- logmsg("fork");
- goto failed;
+ len = sizeof(xucred);
+ if (getsockopt(fd, 0, LOCAL_PEERCRED, &xucred, &len) < 0) {
+ logmsg("getsockopt(LOCAL_PEERCRED)");
+ return (-1);
}
- if (client_pid == 0) {
- myname = "CLIENT";
- if (close_socket((const char *)NULL, fd) < 0)
- _exit(1);
- t_timestamp_client();
- }
+ if (check_xucred(&xucred, len) < 0)
+ return (-1);
- if ((error = t_timestamp_server(fd)) == -2) {
- (void)wait_client();
- goto failed;
- }
+ return (0);
+}
- if (wait_client() < 0)
- goto failed;
+static int
+t_peercred_server(int fd1)
+{
+ struct xucred xucred;
+ socklen_t len;
+ int fd2, rv;
- if (close_socket(serv_sock_path, fd) < 0) {
- logmsgx("close_socket failed");
+ if (sync_send() < 0)
return (-2);
+
+ fd2 = socket_accept(fd1);
+ if (fd2 < 0)
+ return (-2);
+
+ len = sizeof(xucred);
+ if (getsockopt(fd2, 0, LOCAL_PEERCRED, &xucred, &len) < 0) {
+ logmsg("getsockopt(LOCAL_PEERCRED)");
+ rv = -2;
+ goto done;
}
- return (error);
-failed:
- if (close_socket(serv_sock_path, fd) < 0)
- logmsgx("close_socket failed");
- return (-2);
+ if (check_xucred(&xucred, len) < 0) {
+ rv = -1;
+ goto done;
+ }
+
+ rv = 0;
+done:
+ if (socket_close(fd2) < 0)
+ rv = -2;
+ return (rv);
+}
+
+static int
+t_peercred(void)
+{
+ return (t_generic(t_peercred_client, t_peercred_server));
}
diff --git a/tools/regression/sockets/unix_cmsg/unix_cmsg.t b/tools/regression/sockets/unix_cmsg/unix_cmsg.t
index c8dea15..70d9277 100644
--- a/tools/regression/sockets/unix_cmsg/unix_cmsg.t
+++ b/tools/regression/sockets/unix_cmsg/unix_cmsg.t
@@ -11,47 +11,78 @@ n=0
run()
{
- result=`${cmd} -t $2 $3 $4 2>&1`
- if [ $? -eq 0 ]; then
- echo -n "ok $1"
- else
- echo -n "not ok $1"
+ result=`${cmd} -t $2 $3 ${5%% *} 2>&1`
+ if [ $? -ne 0 ]; then
+ echo -n "not "
fi
- echo " -" $5
+ echo "ok $1 - $4 ${5#* }"
echo ${result} | grep -E "SERVER|CLIENT" | while read line; do
echo "# ${line}"
done
}
-echo "1..15"
+echo "1..47"
-for desc in \
- "Sending, receiving cmsgcred" \
- "Receiving sockcred (listening socket has LOCAL_CREDS) # TODO" \
- "Receiving sockcred (accepted socket has LOCAL_CREDS) # TODO" \
- "Sending cmsgcred, receiving sockcred # TODO" \
- "Sending, receiving timestamp"
+for t1 in \
+ "1 Sending, receiving cmsgcred" \
+ "4 Sending cmsgcred, receiving sockcred" \
+ "5 Sending, receiving timeval" \
+ "6 Sending, receiving bintime" \
+ "7 Check cmsghdr.cmsg_len"
do
- n=`expr ${n} + 1`
- run ${n} stream "" ${n} "STREAM ${desc}"
+ for t2 in \
+ "0 " \
+ "1 (no data)" \
+ "2 (no array)" \
+ "3 (no data, array)"
+ do
+ n=$((n + 1))
+ run ${n} stream "-z ${t2%% *}" STREAM "${t1} ${t2#* }"
+ done
done
-i=0
-for desc in \
- "Sending, receiving cmsgcred" \
- "Receiving sockcred # TODO" \
- "Sending cmsgcred, receiving sockcred # TODO" \
- "Sending, receiving timestamp"
+for t1 in \
+ "2 Receiving sockcred (listening socket)" \
+ "3 Receiving sockcred (accepted socket)"
do
- i=`expr ${i} + 1`
- n=`expr ${n} + 1`
- run ${n} dgram "" ${i} "DGRAM ${desc}"
+ for t2 in \
+ "0 " \
+ "1 (no data)"
+ do
+ n=$((n + 1))
+ run ${n} stream "-z ${t2%% *}" STREAM "${t1} ${t2#* }"
+ done
done
-run 10 stream -z 1 "STREAM Sending, receiving cmsgcred (no control data)"
-run 11 stream -z 4 "STREAM Sending cmsgcred, receiving sockcred (no control data) # TODO"
-run 12 stream -z 5 "STREAM Sending, receiving timestamp (no control data)"
+n=$((n + 1))
+run ${n} stream "-z 0" STREAM "8 Check LOCAL_PEERCRED socket option"
-run 13 dgram -z 1 "DGRAM Sending, receiving cmsgcred (no control data)"
-run 14 dgram -z 3 "DGRAM Sending cmsgcred, receiving sockcred (no control data) # TODO"
-run 15 dgram -z 4 "DGRAM Sending, receiving timestamp (no control data)"
+for t1 in \
+ "1 Sending, receiving cmsgcred" \
+ "3 Sending cmsgcred, receiving sockcred" \
+ "4 Sending, receiving timeval" \
+ "5 Sending, receiving bintime" \
+ "6 Check cmsghdr.cmsg_len"
+do
+ for t2 in \
+ "0 " \
+ "1 (no data)" \
+ "2 (no array)" \
+ "3 (no data, array)"
+ do
+ n=$((n + 1))
+ run ${n} dgram "-z ${t2%% *}" DGRAM "${t1} ${t2#* }"
+ done
+done
+
+for t1 in \
+ "2 Receiving sockcred"
+do
+ for t2 in \
+ "0 " \
+ "1 (no data)"
+ do
+ n=$((n + 1))
+ run ${n} dgram "-z ${t2%% *}" DGRAM "${t1} ${t2#* }"
+ done
+done
OpenPOWER on IntegriCloud