diff --git a/Makefile b/Makefile --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.45 2015/06/22 00:05:23 matt Exp $ +# $NetBSD: Makefile,v 1.46 2017/05/21 15:28:42 riastradh Exp $ .include @@ -17,9 +17,7 @@ . endif . endif -. if ${MKCRYPTO} != "no" TESTS_SUBDIRS+= crypto -. endif . if ${MKIPFILTER} != "no" TESTS_SUBDIRS+= ipf diff --git a/Makefile.inc b/Makefile.inc --- a/Makefile.inc +++ b/Makefile.inc @@ -1,3 +1,14 @@ -# $NetBSD: Makefile.inc,v 1.2 2011/09/16 16:30:18 joerg Exp $ +# $NetBSD: Makefile.inc,v 1.10 2021/07/07 11:51:45 martin Exp $ WARNS ?= 4 CWARNFLAGS+= -Wno-missing-noreturn +TOOLSSRCDIR:= ${.PARSEDIR} +CPPFLAGS+= -I${TOOLSSRCDIR} +.if ${RUMPFIFO:Uno} == "yes" +LIBRUMPFIFO_PRE = -lrumpvfs_fifofs -lrumpnet_local -lrumpnet_net -lrumpnet +.endif +LIBRUMPFIFO = -lrumpvfs_nofifofs +LIBRUMPBASE = ${LIBRUMPFIFO_PRE} -lrumpvfs -lrump -lrumpvfs -lrumpvfs_nofifofs -lrumpuser -lrump -lpthread + +.if ${RUMP_SANITIZE:Uno} != "no" +LIBRUMPBASE+= -fsanitize=${RUMP_SANITIZE} +.endif diff --git a/bin/Makefile b/bin/Makefile --- a/bin/Makefile +++ b/bin/Makefile @@ -1,9 +1,9 @@ -# $NetBSD: Makefile,v 1.3 2012/03/30 15:49:24 njoly Exp $ +# $NetBSD: Makefile,v 1.4 2020/07/03 03:59:18 jruoho Exp $ .include TESTSDIR= ${TESTSBASE}/bin -TESTS_SUBDIRS= cat cp dd df expr pax ps sh sleep tar +TESTS_SUBDIRS= cat cp date dd df expr pax ps sh sleep tar .include diff --git a/bin/cat/t_cat.sh b/bin/cat/t_cat.sh old mode 100755 new mode 100644 diff --git a/bin/cp/t_cp.sh b/bin/cp/t_cp.sh old mode 100755 new mode 100644 diff --git a/bin/date/Makefile b/bin/date/Makefile new file mode 100644 --- /dev/null +++ b/bin/date/Makefile @@ -0,0 +1,8 @@ +# $NetBSD: Makefile,v 1.1 2020/07/03 03:59:18 jruoho Exp $ + +.include + +TESTSDIR= ${TESTSBASE}/bin/date +TESTS_SH= t_date + +.include diff --git a/bin/date/t_date.sh b/bin/date/t_date.sh new file mode 100644 --- /dev/null +++ b/bin/date/t_date.sh @@ -0,0 +1,56 @@ +# $NetBSD: t_date.sh,v 1.2 2020/10/30 22:03:35 kre Exp $ +# +# Copyright (c) 2020 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Jukka Ruohonen. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# +atf_test_case overflow +overflow_head() { + atf_set "descr" "Check that date(1) does not overflow (PR lib/46542)" +} + +overflow_body() { + + ## atf_expect_fail "PR lib/46542" # no longer true + + years="1 10 100 1000 10000 100000 1000000 10000000 \ + 100000000 1000000000 10000000000 100000000000" + + for year in $years; do + + y=$(date +%Y) + yy=$((y + year)) + yyy=$(date -d "$year years" +%Y) || continue + + if [ "$yy" -ne "$yyy" ]; then + atf_fail "$yy vs. $yyy" + fi + done +} + +atf_init_test_cases() { + atf_add_test_case overflow +} diff --git a/bin/dd/t_dd.sh b/bin/dd/t_dd.sh old mode 100755 new mode 100644 diff --git a/bin/df/Makefile b/bin/df/Makefile --- a/bin/df/Makefile +++ b/bin/df/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.1 2012/03/17 16:33:11 jruoho Exp $ +# $NetBSD: Makefile,v 1.2 2019/08/15 08:24:11 kamil Exp $ NOMAN= # defined @@ -24,4 +24,6 @@ SRCS+= humanize_number.c .endif +SANITIZER_RENAME_SYMBOL+= __getmntinfo13 + .include diff --git a/bin/df/t_df.sh b/bin/df/t_df.sh old mode 100755 new mode 100644 --- a/bin/df/t_df.sh +++ b/bin/df/t_df.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_df.sh,v 1.1 2012/03/17 16:33:11 jruoho Exp $ +# $NetBSD: t_df.sh,v 1.2 2020/08/23 15:51:30 ryo Exp $ # # Copyright (c) 2007, 2008 The NetBSD Foundation, Inc. # All rights reserved. @@ -33,51 +33,51 @@ } normal_body() { cat >expout <expout < @@ -8,17 +8,21 @@ TESTS_SUBDIRS += dotcmd TESTS_SH+= t_arith +TESTS_SH+= t_builtins TESTS_SH+= t_cmdsub TESTS_SH+= t_evaltested TESTS_SH+= t_exit TESTS_SH+= t_expand TESTS_SH+= t_fsplit TESTS_SH+= t_here +TESTS_SH+= t_input TESTS_SH+= t_option +TESTS_SH+= t_patterns TESTS_SH+= t_redir TESTS_SH+= t_redircloexec TESTS_SH+= t_set_e TESTS_SH+= t_shift +TESTS_SH+= t_syntax TESTS_SH+= t_ulimit TESTS_SH+= t_varquote TESTS_SH+= t_varval diff --git a/bin/sh/dotcmd/scoped_command b/bin/sh/dotcmd/scoped_command --- a/bin/sh/dotcmd/scoped_command +++ b/bin/sh/dotcmd/scoped_command @@ -1,6 +1,6 @@ #!/bin/sh # -# $NetBSD: scoped_command,v 1.2 2016/03/27 14:57:50 christos Exp $ +# $NetBSD: scoped_command,v 1.3 2018/12/04 09:47:25 kre Exp $ # # Copyright (c) 2014 The NetBSD Foundation, Inc. # All rights reserved. @@ -71,7 +71,7 @@ # don't rely on command lists (';') cmd="echo 'before ${3}' ${2} -echo 'after ${3}, return value:' ${?}" +echo 'after ${3}, return value:' \${?}" echo "#!${TEST_SH}" diff --git a/bin/sh/dotcmd/t_dotcmd.sh b/bin/sh/dotcmd/t_dotcmd.sh old mode 100755 new mode 100644 diff --git a/bin/sh/t_arith.sh b/bin/sh/t_arith.sh old mode 100755 new mode 100644 --- a/bin/sh/t_arith.sh +++ b/bin/sh/t_arith.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_arith.sh,v 1.5 2016/05/12 14:25:11 kre Exp $ +# $NetBSD: t_arith.sh,v 1.8 2017/07/15 18:50:42 kre Exp $ # # Copyright (c) 2016 The NetBSD Foundation, Inc. # All rights reserved. @@ -818,6 +818,30 @@ 'echo $(( 0x33 || 0xF0F0 ))' } +atf_test_case nested_arith +nested_arith_head() +{ + atf_set "descr" 'Test nested arithmetic $(( $(( )) ))' +} +nested_arith_body() +{ + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( $(( 0 )) ))' + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 1 + $(( 2 - 2 )) ))' + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( $(( 3 / 3 )) + $((1*1*1)) - $(( 7 % 6 ))))' + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(($(($(($(($((1))))))))))' + + atf_check -s exit:0 -o inline:'246\n' -e empty ${TEST_SH} -c \ + 'echo $(( 2$((2 * 2))6 ))' + atf_check -s exit:0 -o inline:'291117\n' -e empty ${TEST_SH} -c \ + 'echo $(( $((1 + 1))$((3 * 3))$(( 99-88 ))$(( 17))))' + atf_check -s exit:0 -o inline:'123456789\n' -e empty ${TEST_SH} -c \ + 'echo $(( 1$((2$((1+2))4$((2 + 2 + 1))6))7$((4 * 2))$(($((81/9))))))' +} + atf_test_case make_selection make_selection_head() { @@ -913,6 +937,46 @@ atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ 'echo $(( 0xfD & 0xF == 0xF ))' + + ${TEST_SH} -c ': $(( 1 , 2 , 3 ))' 2>/dev/null && { + atf_check -s exit:0 -o inline:'2\n' -e empty ${TEST_SH} -c \ + 'echo $(( 3 * 7 , 2 << 8 , 9 - 7 ))' + atf_check -s exit:0 -o inline:'4\n' -e empty ${TEST_SH} -c \ + 'echo $(( 1 ? 2 : 3 , 0 ? 1 : 4 ))' + } + + return 0 +} + +atf_test_case optional_comma +optional_comma_head() +{ + atf_set "descr" "Test the optional comma operator" +} +optional_comma_body() +{ + # First, see if it is supported or not. + ${TEST_SH} -c ': $(( 1 , 2 , 3 ))' 2>/dev/null || atf_skip \ + "${TEST_SH} does not implement the ',' operator in"' $(( ))' + + + # Note ',' should be set off from numbers by spaces, as in some + # locales it is a valid chacacter in a number, and we want to + # avoid any possibility of confusing the parser. + + atf_check -s exit:0 -o inline:'2\n' -e empty ${TEST_SH} -c \ + 'echo $(( 1 , 2 ))' + atf_check -s exit:0 -o inline:'3\n' -e empty ${TEST_SH} -c \ + 'echo $(( 1 , 2 , 3 ))' + atf_check -s exit:0 -o inline:'4\n' -e empty ${TEST_SH} -c \ + 'echo $(( 1 , 2 , 3 , 4 ))' + + atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \ + 'echo $(( , 2 ))' + atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \ + 'echo $(( 2 , ))' + atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \ + 'echo $(( 1 , , 2 ))' } parentheses_head() @@ -975,9 +1039,240 @@ 'echo $(( 2 << ((3 >= 3) << 2) ))' # sh inherits C's crazy operator precedence... - atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ 'echo $(( (0xfD & 0xF) == 0xF ))' + + ${TEST_SH} -c ': $(( 1 , 2 , 3 ))' 2>/dev/null && { + atf_check -s exit:0 -o inline:'24\n' -e empty ${TEST_SH} -c \ + 'echo $(( 3 * (7 , 2) << (8 , 9 - 7) ))' + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0 ? 2 : ( ( 0 , 3 ) ? 1 : 4) ))' + } + return 0 +} + +atf_test_case var_assign +var_assign_head() +{ + atf_set "descr" "Test assignment operators in arithmetic expressions" +} +var_assign_body() +{ + atf_check -s exit:0 -o inline:'3\n3\n' -e empty ${TEST_SH} -c \ + 'unset x; echo $(( x = 3 )); echo $x' + atf_check -s exit:0 -o inline:'3\n3\n' -e empty ${TEST_SH} -c \ + 'unset x; echo $((x=3)); echo $x' + atf_check -s exit:0 -o inline:'3\n3\n' -e empty ${TEST_SH} -c \ + 'x=5; echo $((x=3)); echo $x' + + atf_check -s exit:0 -o inline:'3\n3\n' -e empty ${TEST_SH} -c \ + 'set +u;unset x; echo $((x+=3)); echo $x' + atf_check -s exit:0 -o inline:'3\n3\n' -e empty ${TEST_SH} -c \ + 'x=2; echo $((x+=1)); echo $x' + atf_check -s exit:0 -o inline:'3\n3\n' -e empty ${TEST_SH} -c \ + 'x=4; echo $((x-=1)); echo $x' + atf_check -s exit:0 -o inline:'3\n3\n' -e empty ${TEST_SH} -c \ + 'x=3; echo $((x*=1)); echo $x' + atf_check -s exit:0 -o inline:'3\n3\n' -e empty ${TEST_SH} -c \ + 'x=3; echo $((x/=1)); echo $x' + atf_check -s exit:0 -o inline:'3\n3\n' -e empty ${TEST_SH} -c \ + 'x=28; echo $((x%=5)); echo $x' + atf_check -s exit:0 -o inline:'3\n3\n' -e empty ${TEST_SH} -c \ + 'x=7; echo $((x&=3)); echo $x' + atf_check -s exit:0 -o inline:'3\n3\n' -e empty ${TEST_SH} -c \ + 'x=2; echo $((x|=1)); echo $x' + atf_check -s exit:0 -o inline:'3\n3\n' -e empty ${TEST_SH} -c \ + 'x=6; echo $((x^=5)); echo $x' + atf_check -s exit:0 -o inline:'3\n3\n' -e empty ${TEST_SH} -c \ + 'x=7; echo $((x>>=1)); echo $x' + atf_check -s exit:0 -o inline:'2\n2\n' -e empty ${TEST_SH} -c \ + 'x=1; echo $((x<<=1)); echo $x' + + atf_check -s exit:0 -o inline:'2\n3\n' -e empty ${TEST_SH} -c \ + 'x=2; echo $(( (x+=1)-1 )); echo $x' + atf_check -s exit:0 -o inline:'4\n3\n' -e empty ${TEST_SH} -c \ + 'x=4; echo $(( (x-=1)+1 )); echo $x' + + atf_check -s exit:0 -o inline:'36\n5 7\n' -e empty ${TEST_SH} -c \ + 'unset x y; echo $(( (x=5) * (y=7) + 1 )); echo $x $y' + atf_check -s exit:0 -o inline:'36\n5 7\n' -e empty ${TEST_SH} -c \ + 'x=99; y=17; echo $(( (x=5) * (y=7) + 1 )); echo $x $y' + atf_check -s exit:0 -o inline:'36\n5 7\n' -e empty ${TEST_SH} -c \ + 'x=4; y=9; echo $(( (x+=1) * (y-=2) + 1 )); echo $x $y' + + atf_check -s exit:0 -o inline:'3\n3\n' -e empty ${TEST_SH} -c \ + 'set -u; unset x; echo $(( x = 3 )); echo $x' + atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \ + 'set -u; unset x; echo $(( x + 3 )); echo $x' + atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \ + 'set -u; unset x; echo $(( x+=3 )); echo $x' + + ${TEST_SH} -c ': $(( 1 , 2 , 3 ))' 2>/dev/null && { + atf_check -s exit:0 -o inline:'3\n3\n' -e empty ${TEST_SH} -c \ + 'echo $((x=2 , x|=1)); echo $x' + atf_check -s exit:0 -o inline:'3\n3\n' -e empty ${TEST_SH} -c \ + 'set -u; echo $((x = 2 ,x |= 1)); echo $x' + atf_check -s exit:0 -o inline:'6\n1:2:3:6\n' -e empty \ + ${TEST_SH} -c \ + 'echo $((a=1 , b=2 , c = 3 , x=a+b + c)); echo $a:$b:$c:$x' + atf_check -s exit:0 -o inline:'6\n1:2:3:6\n' -e empty \ + ${TEST_SH} -c \ + 'set -u;echo $((a=1 ,b=2 ,c=3 ,x=a+b+c)); echo $a:$b:$c:$x' + } + return 0 +} + +atf_test_case var_postinc +var_postinc_head() +{ + atf_set "descr" "Test suffix ++ operator" +} +var_postinc_body() +{ + ${TEST_SH} -c 'X=1; : $(( X++ ))' 2>/dev/null || + atf_skip "${TEST_SH} does not support the suffix ++ operator" + + unset X ; # just in case ... + + atf_check -s exit:0 -o inline:'1\n2\n' -e empty ${TEST_SH} -c \ + 'X=1; echo $(( X++ )); echo $X' + atf_check -s exit:0 -o inline:'0\n1\n' -e empty ${TEST_SH} -c \ + 'echo $(( X++ )); echo $X' + + atf_check -s exit:0 -o inline:'0\n1:0\n' -e empty ${TEST_SH} -c \ + 'unset Y; echo $(( Y = X++ )); echo $X:$Y' + atf_check -s exit:0 -o inline:'12\n4:5\n' -e empty ${TEST_SH} -c \ + 'X=3 Y=4; echo $(( Y++*X++ )); echo $X:$Y' + + atf_check -s exit:0 -o inline:'1\n2\n' -e empty ${TEST_SH} -c \ + 'set -u; X=1; echo $(( X++ )); echo $X' + atf_check -s exit:0 -o inline:'0\n1:0\n' -e empty ${TEST_SH} -c \ + 'set -u; X=0; unset Y; echo $(( Y = X++ )); echo $X:$Y' + atf_check -s exit:0 -o inline:'12\n4:5\n' -e empty ${TEST_SH} -c \ + 'set -u; X=3 Y=4; echo $(( Y++*X++ )); echo $X:$Y' + + atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \ + 'set -u; echo $(( X++ ))' + atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \ + 'set -u; unset Y; echo $(( X = Y++ ))' + + atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \ + 'X=3; readonly X; echo $(( X++ ))' + +} +atf_test_case var_postdec +var_postdec_head() +{ + atf_set "descr" "Test suffix -- operator" +} +var_postdec_body() +{ + ${TEST_SH} -c 'X=1; : $(( X-- ))' 2>/dev/null || + atf_skip "${TEST_SH} does not support the suffix -- operator" + + unset X ; # just in case ... + + atf_check -s exit:0 -o inline:'1\n0\n' -e empty ${TEST_SH} -c \ + 'X=1; echo $(( X-- )); echo $X' + atf_check -s exit:0 -o inline:'0\n-1\n' -e empty ${TEST_SH} -c \ + 'echo $(( X-- )); echo $X' + + atf_check -s exit:0 -o inline:'0\n-1:0\n' -e empty ${TEST_SH} -c \ + 'unset Y; echo $(( Y = X-- )); echo $X:$Y' + atf_check -s exit:0 -o inline:'12\n2:3\n' -e empty ${TEST_SH} -c \ + 'X=3 Y=4; echo $(( Y--*X-- )); echo $X:$Y' + + atf_check -s exit:0 -o inline:'1\n0\n' -e empty ${TEST_SH} -c \ + 'set -u; X=1; echo $(( X-- )); echo $X' + atf_check -s exit:0 -o inline:'0\n-1:0\n' -e empty ${TEST_SH} -c \ + 'set -u; X=0; unset Y; echo $(( Y = X-- )); echo $X:$Y' + atf_check -s exit:0 -o inline:'12\n2:3\n' -e empty ${TEST_SH} -c \ + 'set -u; X=3 Y=4; echo $(( Y--*X-- )); echo $X:$Y' + + atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \ + 'set -u; echo $(( X-- ))' + atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \ + 'set -u; unset Y; echo $(( X = Y-- ))' + + atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \ + 'X=3; readonly X; echo $(( X-- ))' + +} +atf_test_case var_preinc +var_preinc_head() +{ + atf_set "descr" "Test prefix ++ operator" +} +var_preinc_body() +{ + ${TEST_SH} -c 'X=1; : $(( ++X ))' 2>/dev/null || + atf_skip "${TEST_SH} does not support the prefix ++ operator" + + unset X ; # just in case ... + + atf_check -s exit:0 -o inline:'2\n2\n' -e empty ${TEST_SH} -c \ + 'X=1; echo $(( ++X )); echo $X' + atf_check -s exit:0 -o inline:'1\n1\n' -e empty ${TEST_SH} -c \ + 'echo $(( ++X )); echo $X' + + atf_check -s exit:0 -o inline:'1\n1:1\n' -e empty ${TEST_SH} -c \ + 'unset Y; echo $(( Y = ++X )); echo $X:$Y' + atf_check -s exit:0 -o inline:'20\n4:5\n' -e empty ${TEST_SH} -c \ + 'X=3 Y=4; echo $(( ++Y*++X )); echo $X:$Y' + + atf_check -s exit:0 -o inline:'2\n2\n' -e empty ${TEST_SH} -c \ + 'set -u; X=1; echo $(( ++X )); echo $X' + atf_check -s exit:0 -o inline:'1\n1:1\n' -e empty ${TEST_SH} -c \ + 'set -u; X=0; unset Y; echo $(( Y = ++X )); echo $X:$Y' + atf_check -s exit:0 -o inline:'20\n4:5\n' -e empty ${TEST_SH} -c \ + 'set -u; X=3 Y=4; echo $(( ++Y*++X )); echo $X:$Y' + + atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \ + 'set -u; echo $(( ++X ))' + atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \ + 'set -u; unset Y; echo $(( X = ++Y ))' + + atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \ + 'X=3; readonly X; echo $(( ++X ))' + +} +atf_test_case var_predec +var_predec_head() +{ + atf_set "descr" "Test prefix -- operator" +} +var_predec_body() +{ + ${TEST_SH} -c 'X=1; : $(( --X ))' 2>/dev/null || + atf_skip "${TEST_SH} does not support the prefix -- operator" + + unset X ; # just in case ... + + atf_check -s exit:0 -o inline:'0\n0\n' -e empty ${TEST_SH} -c \ + 'X=1; echo $(( --X )); echo $X' + atf_check -s exit:0 -o inline:'-1\n-1\n' -e empty ${TEST_SH} -c \ + 'echo $(( --X )); echo $X' + + atf_check -s exit:0 -o inline:'-1\n-1:-1\n' -e empty ${TEST_SH} -c \ + 'unset Y; echo $(( Y = --X )); echo $X:$Y' + atf_check -s exit:0 -o inline:'6\n2:3\n' -e empty ${TEST_SH} -c \ + 'X=3 Y=4; echo $(( --Y*--X )); echo $X:$Y' + + atf_check -s exit:0 -o inline:'0\n0\n' -e empty ${TEST_SH} -c \ + 'set -u; X=1; echo $(( --X )); echo $X' + atf_check -s exit:0 -o inline:'-1\n-1:-1\n' -e empty ${TEST_SH} -c \ + 'set -u; X=0; unset Y; echo $(( Y = --X )); echo $X:$Y' + atf_check -s exit:0 -o inline:'6\n2:3\n' -e empty ${TEST_SH} -c \ + 'set -u; X=3 Y=4; echo $(( --Y*--X )); echo $X:$Y' + + atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \ + 'set -u; echo $(( --X ))' + atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \ + 'set -u; unset Y; echo $(( X = --Y ))' + + atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \ + 'X=3; readonly X; echo $(( --X ))' + } atf_test_case arithmetic_fails @@ -1025,11 +1320,17 @@ atf_add_test_case logical_and atf_add_test_case logical_or atf_add_test_case make_selection + atf_add_test_case nested_arith atf_add_test_case operator_precedence + atf_add_test_case optional_comma atf_add_test_case parentheses # atf_add_test_case progressive # build up big expr # atf_add_test_case test_errors # erroneous input # atf_add_test_case torture # hard stuff (if there is any) - # atf_add_test_case var_assign # assignment ops + atf_add_test_case var_assign # assignment ops + atf_add_test_case var_postinc # var++ + atf_add_test_case var_postdec # var-- + atf_add_test_case var_preinc # ++var + atf_add_test_case var_predec # --var # atf_add_test_case vulgarity # truly evil inputs (syntax in vars...) } diff --git a/bin/sh/t_builtins.sh b/bin/sh/t_builtins.sh new file mode 100644 --- /dev/null +++ b/bin/sh/t_builtins.sh @@ -0,0 +1,1009 @@ +# $NetBSD: t_builtins.sh,v 1.6 2021/05/18 21:37:56 kre Exp $ +# +# Copyright (c) 2018 The NetBSD Foundation, Inc. +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# +# the implementation of "sh" to test +: ${TEST_SH:="/bin/sh"} + +# +# This file tests the various sh builtin utilities. +# +# Those utilities that are really external programs, which are builtin in +# for (mostly) performance (printf, kill, test, ...), are tested elsewhere. +# We do test the builtin "echo" here as (in NetBSD) it is different than +# the external one. +# +# The (mostly special) builtins which appear to be more syntax than command +# are tested in other test programs, rather than here (break, continue...) +# +# And finally, those which are fundamental to the operation of the shell, +# like wait, set, shift, ... are also tested in other test programs where +# all their operations can be more thoroughly verified. +# +# This leaves those which need to be built in (cd, umask, ...) but whose +# purpose is mostly to alter the environment in which the shell operates +# of that of the commands it runs. These tests act in co-operation with +# other tests exist here (where thy do) by not duplicating tests run +# elsewhere (ulimit is one example) but just adding to those. +# One day these might be unified. +# +# We do test both standard use of the builtins (where they are standard) +# and NetBSD sh extensions (when run on a shell with no support, such tests +# should be skipped.) +# + +# Utility function able to test whether most of the builtins exist in +# the shell being tested. +have_builtin() +{ + ${TEST_SH} -c "( $3 $1 $4 ) >/dev/null 2>&1" && + LC_ALL=C ${TEST_SH} -c \ + 'case "$( (type '"$1"') 2>&1)" in + (*built*) exit 0 ;; + (*reserved*) exit 0 ;; # zsh!! (reserved words are builtin) + esac + exit 1' || + { + test -z "$2" && atf_skip "${TEST_SH} has no '$1$5' built-in" + return 1; + } + + return 0 +} + +# And another to test if the shell being tested is the NetBSD shell, +# as we use these tests both to test standards conformance (correctness) +# which should be possible for all shells, and to test NetBSD +# extensions (which we mostly do by testing if the extension exists) +# and NetBSD sh behaviour for what is unspecified by the standard +# (so we will be notified via test failure should that unspecified +# behaviour alter) for which we have to discover if that shell is the +# one being tested. + +is_netbsd_sh() +{ + unset NETBSD_SHELL 2>/dev/null + test -n "$( ${TEST_SH} -c 'printf %s "${NETBSD_SHELL}"')" +} + +### Helper functions + +nl=' +' +reset() +{ + TEST_NUM=0 + TEST_FAILURES='' + TEST_FAIL_COUNT=0 + TEST_ID="$1" + + # These are used in check() + atf_require_prog tr + atf_require_prog printf + atf_require_prog mktemp +} + +# Test run & validate. +# +# $1 is the command to run (via sh -c) +# $2 is the expected output +# $3 is the expected exit status from sh +# $4 is optional extra data for the error msg (if there is one) +# +# Stderr is exxpected to be empty, unless the expected exit code ($3) is != 0 +# in which case some message there is expected (and nothing is a failure). +# When non-zero exit is expected, we note a different (non-zero) value +# observed, but do not fail the test because of that. + +check() +{ + fail=false + TEMP_FILE=$( mktemp OUT.XXXXXX ) + TEST_NUM=$(( $TEST_NUM + 1 )) + MSG= + + # our local shell (ATF_SHELL) better do quoting correctly... + # some of the tests expect us to expand $nl internally... + CMD="$1" + + # determine what the test generates, preserving trailing \n's + result="$( ${TEST_SH} -c "${CMD}" 2>"${TEMP_FILE}" && printf X )" + STATUS=$? + result="${result%X}" + + + if [ "${STATUS}" -ne "$3" ]; then + MSG="${MSG}${MSG:+${nl}}[$TEST_NUM]" + MSG="${MSG} expected exit code $3, got ${STATUS}" + + # don't actually fail just because of wrong exit code + # unless we either expected, or received "good" + # or something else is detected as incorrect as well. + case "$3/${STATUS}" in + (*/0|0/*) fail=true;; + esac + fi + + if [ "$3" -eq 0 ]; then + if [ -s "${TEMP_FILE}" ]; then + MSG="${MSG}${MSG:+${nl}}[$TEST_NUM]" + MSG="${MSG} Messages produced on stderr unexpected..." + MSG="${MSG}${nl}$( cat "${TEMP_FILE}" )" + fail=true + fi + else + if ! [ -s "${TEMP_FILE}" ]; then + MSG="${MSG}${MSG:+${nl}}[$TEST_NUM]" + MSG="${MSG} Expected messages on stderr," + MSG="${MSG} nothing produced" + fail=true + fi + fi + rm -f "${TEMP_FILE}" + + if [ "$2" != "${result}" ] + then + MSG="${MSG}${MSG:+${nl}}[$TEST_NUM]" + MSG="${MSG} Expected: <<$2>>, received: <<$result>>" + fail=true + fi + + if $fail + then + if [ -n "$4" ]; then + MSG="${MSG}${MSG:+${nl}}[$TEST_NUM] Note: ${4}" + fi + MSG="${MSG}${MSG:+${nl}}[$TEST_NUM]" + MSG="${MSG} Full command: <<${CMD}>>" + fi + + $fail && test -n "$TEST_ID" && { + TEST_FAILURES="${TEST_FAILURES}${TEST_FAILURES:+${nl}}" + TEST_FAILURES="${TEST_FAILURES}${TEST_ID}[$TEST_NUM]:" + TEST_FAILURES="${TEST_FAILURES} Test of <<$1>> failed."; + TEST_FAILURES="${TEST_FAILURES}${nl}${MSG}" + TEST_FAIL_COUNT=$(( $TEST_FAIL_COUNT + 1 )) + return 0 + } + $fail && atf_fail "Test[$TEST_NUM] failed: $( + # ATF does not like newlines in messages, so change them... + printf '%s' "${MSG}" | tr '\n' ';' + )" + return 0 +} + +results() +{ + test -n "$1" && atf_expect_fail "$1" + + test -z "${TEST_ID}" && return 0 + test -z "${TEST_FAILURES}" && return 0 + + echo >&2 "==========================================" + echo >&2 "While testing '${TEST_ID}'" + echo >&2 " - - - - - - - - - - - - - - - - -" + echo >&2 "${TEST_FAILURES}" + + atf_fail \ + "Test ${TEST_ID}: $TEST_FAIL_COUNT (of $TEST_NUM) subtests failed - see stderr" +} + +####### End helpers + +atf_test_case colon +colon_head() { + atf_set "descr" "Tests the shell special builtin ':' command" +} +colon_body() { + have_builtin : || return 0 + + atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c ":" + + # ':' is a special builtin, so we should exit on redirect error + # and variable assignments should persist (stupid, but it is the rule) + + atf_check -s not-exit:0 -e not-empty -o empty ${TEST_SH} -c \ + ": >/foo/bar; printf %s No-exit-BUG" + atf_check -s exit:0 -e empty -o inline:OK ${TEST_SH} -c \ + 'X=BUG; X=OK : ; printf %s "${X}"' +} + +atf_test_case echo +echo_head() { + atf_set "descr" "Tests the shell builtin version of echo" +} +echo_body() { + have_builtin echo || return 0 + + if ! is_netbsd_sh; then + atf_skip \ + "${TEST_SH%% *} is not the NetBSD shell, this test is for it alone" + return 0 + fi + + reset echo + + check 'echo "hello world"' "hello world${nl}" 0 + check 'echo hello world' "hello world${nl}" 0 + check 'echo -n "hello world"' "hello world" 0 + check 'IFS=:; echo hello world' "hello world${nl}" 0 + check 'IFS=; echo hello world' "hello world${nl}" 0 + + check 'echo -e "hello world"' "hello world${nl}" 0 + check 'echo -e hello world' "hello world${nl}" 0 + check 'IFS=:; echo -e hello world' "hello world${nl}" 0 + + # only one of the options is used + check 'echo -e -n "hello world"' "-n hello world${nl}" 0 + check 'echo -n -e "hello world"' "-e hello world" 0 + # and only when it is alone + check 'echo -en "hello world"' "-en hello world${nl}" 0 + check 'echo -ne "hello world"' "-ne hello world${nl}" 0 + + # echo is specifically required to *not* support -- + check 'echo -- "hello world"' "-- hello world${nl}" 0 + + # similarly any other unknown option is simply part of the output + for OPT in a b c v E N Q V 0 1 2 @ , \? \[ \] \( \; . \* -help -version + do + check "echo '-${OPT}' foo" "-${OPT} foo${nl}" 0 + done + + # Now test the \\ expansions, with and without -e + + # We rely upon printf %b (tested elsewhere, not only a sh test) + # to verify the output when the \\ is supposed to be expanded. + + for E in '' -e + do + for B in a b c e f n r t v \\ 04 010 012 0177 + do + S="test string with \\${B} in it" + if [ -z "${E}" ]; then + R="${S}${nl}" + else + R="$(printf '%b\nX' "${S}")" + R=${R%X} + fi + check "echo $E '${S}'" "${R}" 0 + done + done + + check 'echo foo >&-' "" 1 + check 'echo foo >&- 2>&-; echo $?; echo $?' "1${nl}0${nl}" 0 + + results +} + +atf_test_case eval +eval_head() { + atf_set "descr" "Tests the shell special builtin 'eval'" +} +eval_body() { + have_builtin eval || return 0 + + atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c 'eval "exit 0"' + atf_check -s exit:1 -e empty -o empty ${TEST_SH} -c 'eval "exit 1"' + atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c 'eval exit 0' + atf_check -s exit:1 -e empty -o empty ${TEST_SH} -c 'eval exit 1' + + atf_check -s exit:0 -e empty -o inline:0 ${TEST_SH} -c \ + 'eval true; printf %d $?' + atf_check -s exit:0 -e empty -o inline:1 ${TEST_SH} -c \ + 'eval false; printf %d $?' + + atf_check -s exit:0 -e empty -o inline:abc ${TEST_SH} -c \ + 'X=a Y=b Z=c; for V in X Y Z; do eval "printf %s \$$V"; done' + atf_check -s exit:0 -e empty -o inline:abc ${TEST_SH} -c \ + 'X=a Y=b Z=c; for V in X Y Z; do eval printf %s \$$V; done' + atf_check -s exit:0 -e empty -o inline:XYZ ${TEST_SH} -c \ + 'for V in X Y Z; do eval "${V}=${V}"; done; printf %s "$X$Y$Z"' + + # make sure eval'd strings affect the shell environment + + atf_check -s exit:0 -e empty -o inline:/b/ ${TEST_SH} -c \ + 'X=a; eval "X=b"; printf /%s/ "${X-unset}"' + atf_check -s exit:0 -e empty -o inline:/b/ ${TEST_SH} -c \ + 'X=a; Y=X; Z=b; eval "$Y=$Z"; printf /%s/ "${X-unset}"' + atf_check -s exit:0 -e empty -o inline:/unset/ ${TEST_SH} -c \ + 'X=a; eval "unset X"; printf /%s/ "${X-unset}"' + atf_check -s exit:0 -e empty -o inline:// ${TEST_SH} -c \ + 'unset X; eval "X="; printf /%s/ "${X-unset}"' + atf_check -s exit:0 -e empty -o inline:'2 y Z ' ${TEST_SH} -c \ + 'set -- X y Z; eval shift; printf "%s " "$#" "$@"' + + # ensure an error in an eval'd string causes the shell to exit + # unless 'eval' is preceded by 'command' (in which case the + # string is not eval'd but execution continues) + + atf_check -s not-exit:0 -e not-empty -o empty ${TEST_SH} -c \ + 'eval "if done"; printf %s status=$?' + + atf_check -s exit:0 -e not-empty -o 'match:status=[1-9]' \ + ${TEST_SH} -c \ + 'command eval "if done"; printf %s status=$?' + + atf_check -s not-exit:0 -e not-empty \ + -o 'match:status=[1-9]' -o 'not-match:[XY]' ${TEST_SH} -c \ + 'command eval "printf X; if done; printf Y" + S=$?; printf %s status=$S; exit $S' + + # whether 'X' is output here is (or should be) unspecified. + atf_check -s not-exit:0 -e not-empty \ + -o 'match:status=[1-9]' -o 'not-match:Y' ${TEST_SH} -c \ + 'command eval "printf X + if done + printf Y" + S=$?; printf %s status=$S; exit $S' + + if is_netbsd_sh + then + # but on NetBSD we expect that X to appear... + atf_check -s not-exit:0 -e not-empty -o 'match:X' \ + -o 'match:status=[1-9]' -o 'not-match:Y' ${TEST_SH} -c \ + 'command eval "printf X + if done + printf Y" + S=$?; printf %s status=$S; exit $S' + fi +} + +atf_test_case exec +exec_head() { + atf_set "descr" "Tests the shell special builtin 'exec'" +} +exec_body() { + have_builtin exec || return 0 + + atf_check -s exit:0 -e empty -o inline:OK ${TEST_SH} -c \ + 'exec printf OK; printf BROKEN; exit 3' + atf_check -s exit:3 -e empty -o inline:OKOK ${TEST_SH} -c \ + '(exec printf OK); printf OK; exit 3' +} + +atf_test_case export +export_head() { + atf_set "descr" "Tests the shell builtin 'export'" +} +export_body() { + have_builtin export || return 0 + + atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c 'export VAR' + atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c 'export VAR=abc' + atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c 'export V A R' + atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c \ + 'export V A=1 R=2' + + atf_require_prog printenv + + atf_check -s exit:1 -e empty -o empty ${TEST_SH} -c \ + 'unset VAR || exit 7; export VAR; printenv VAR' + atf_check -s exit:0 -e empty -o inline:\\n ${TEST_SH} -c \ + 'unset VAR || exit 7; export VAR=; printenv VAR' + atf_check -s exit:0 -e empty -o inline:\\n ${TEST_SH} -c \ + 'unset VAR || exit 7; VAR=; export VAR; printenv VAR' + atf_check -s exit:0 -e empty -o inline:\\n ${TEST_SH} -c \ + 'unset VAR || exit 7; export VAR; VAR=; printenv VAR' + atf_check -s exit:0 -e empty -o inline:XYZ\\n ${TEST_SH} -c \ + 'unset VAR || exit 7; export VAR=XYZ; printenv VAR' + atf_check -s exit:0 -e empty -o inline:ABC\\n ${TEST_SH} -c \ + 'VAR=ABC; export VAR; printenv VAR' + atf_check -s exit:0 -e empty -o inline:ABC\\n ${TEST_SH} -c \ + 'unset VAR || exit 7; export VAR; VAR=ABC; printenv VAR' + atf_check -s exit:0 -e empty -o inline:ABC\\nXYZ\\n ${TEST_SH} -c \ + 'VAR=ABC; export VAR; printenv VAR; VAR=XYZ; printenv VAR' + atf_check -s exit:0 -e empty -o inline:ABC\\nXYZ\\n ${TEST_SH} -c \ + 'unset VAR || exit 7; export VAR; + VAR=ABC; printenv VAR; VAR=XYZ; printenv VAR' + + # don't check VAR=value, some shells provide meaningless quoting... + atf_check -s exit:0 -e empty -o match:VAR= -o match:foobar \ + ${TEST_SH} -c \ + 'VAR=foobar ; export VAR ; export -p' + atf_check -s exit:0 -e empty -o match:VAR= -o match:foobar \ + ${TEST_SH} -c \ + 'export VAR=foobar ; export -p' + atf_check -s exit:0 -e empty -o match:VAR\$ ${TEST_SH} -c \ + 'unset VAR ; export VAR ; export -p' + atf_check -s exit:0 -e empty -o not-match:VAR ${TEST_SH} -c \ + 'export VAR ; unset VAR ; export -p' + atf_check -s exit:0 -e empty -o not-match:VAR -o not-match:foobar \ + ${TEST_SH} -c \ + 'VAR=foobar; export VAR ; unset VAR ; export -p' + + atf_check -s exit:0 -e empty -o match:VAR= -o match:FOUND=foobar \ + ${TEST_SH} -c \ + 'export VAR=foobar; V=$(export -p); + unset VAR; eval "$V"; export -p; + printf %s\\n FOUND=${VAR-unset}' + atf_check -s exit:0 -e empty -o match:VAR -o match:FOUND=unset \ + ${TEST_SH} -c \ + 'export VAR; V=$(export -p); + unset VAR; eval "$V"; export -p; + printf %s\\n FOUND=${VAR-unset}' + + atf_check -s exit:1 -e empty -o inline:ABC\\nXYZ\\n ${TEST_SH} -c \ + 'VAR=ABC; export VAR; printenv VAR; VAR=XYZ; printenv VAR; + unset VAR; printenv VAR; VAR=PQR; printenv VAR' + atf_check -s exit:0 -e empty -o inline:ABC\\nXYZ\\nVAR=unset\\nMNO\\n \ + ${TEST_SH} -c \ + 'VAR=ABC; export VAR; printenv VAR; VAR=XYZ; printenv VAR; + unset VAR; printf %s\\n "VAR=${VAR-unset}"; printenv VAR; + VAR=PQR; printenv VAR; VAR=MNO; export VAR; printenv VAR' +} + +atf_test_case export_nbsd +export_nbsd_head() { + atf_set "descr" "Tests NetBSD extensions to the shell builtin 'export'" +} +export_nbsd_body() { + have_builtin "export" "" "" "-n foo" ' -n' || return 0 + + atf_require_prog printenv + + atf_check -s exit:1 -e empty -o inline:ABC\\nXYZ\\n ${TEST_SH} -c \ + 'VAR=ABC; export VAR; printenv VAR; VAR=XYZ; printenv VAR; + export -n VAR; printenv VAR; VAR=PQR; printenv VAR' + + atf_check -s exit:0 -e empty -o inline:ABC\\nXYZ\\nVAR=XYZ\\nMNO\\n \ + ${TEST_SH} -c \ + 'VAR=ABC; export VAR; printenv VAR; VAR=XYZ; printenv VAR; + export -n VAR; printf %s\\n "VAR=${VAR-unset}"; printenv VAR; + VAR=PQR; printenv VAR; VAR=MNO; export VAR; printenv VAR' + + have_builtin "export" "" "" -x ' -x' || return 0 + + atf_check -s exit:1 -e empty -o empty ${TEST_SH} -c \ + 'export VAR=exported; export -x VAR; printenv VAR' + atf_check -s exit:1 -e empty -o empty ${TEST_SH} -c \ + 'export VAR=exported; export -x VAR; VAR=local; printenv VAR' + atf_check -s exit:0 -e empty -o inline:once\\nx\\n ${TEST_SH} -c \ + 'export VAR=exported + export -x VAR + VAR=once printenv VAR + printenv VAR || printf %s\\n x' + + atf_check -s not-exit:0 -e not-empty -o empty ${TEST_SH} -c \ + 'export VAR=exported; export -x VAR; export VAR=FOO' + + have_builtin export '' 'export VAR;' '-q VAR' ' -q' || return 0 + + atf_check -s exit:1 -o empty -e empty ${TEST_SH} -c \ + 'unset VAR; VAR=set; export -q VAR' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'export VAR=set; export -q VAR' + atf_check -s exit:1 -o empty -e empty ${TEST_SH} -c \ + 'VAR=set; RW=set; export -q VAR RW' + atf_check -s exit:1 -o empty -e empty ${TEST_SH} -c \ + 'VAR=set; export RO=set; export -q VAR RO' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'export VAR=set RO=set; export -q VAR RO' + + atf_check -s exit:1 -o empty -e empty ${TEST_SH} -c \ + 'unset VAR; export -q VAR' + # next one is the same as the have_builtin test, so "cannot" fail... + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'unset VAR; export VAR; export -q VAR' + + # if we have -q we should also have -p var... + # What's more, we are testing NetBSD sh, so we know output format. + + atf_check -s exit:0 -e empty -o match:VAR=foobar \ + ${TEST_SH} -c \ + 'VAR=foobar ; export VAR ; export -p VAR' + atf_check -s exit:0 -e empty -o inline:1 \ + ${TEST_SH} -c \ + 'VAR=foobar ; export VAR ; + printf %d $(export -p VAR | wc -l)' + atf_check -s exit:0 -e empty \ + -o inline:'export VAR=foobar\nexport OTHER\n' \ + ${TEST_SH} -c \ + 'VAR=foobar; export VAR OTHER; export -p VAR OTHER' + atf_check -s exit:0 -e empty \ + -o inline:'export A=aaa\nexport B\nexport D='"''"'\n' \ + ${TEST_SH} -c \ + 'A=aaa D= C=foo; unset B; export A B D; + export -p A B C D' +} + +atf_test_case getopts +getopts_head() { + atf_set "descr" "Tests the shell builtin 'getopts'" +} +getopts_body() { + have_builtin getopts "" "f() {" "a x; }; f -a" || return 0 +} + +atf_test_case jobs +jobs_head() { + atf_set "descr" "Tests the shell builting 'jobs' command" +} +jobs_body() { + have_builtin jobs || return 0 + + atf_require_prog sleep + + # note that POSIX requires that we reference $! otherwise + # the shell is not required to remember the process... + + atf_check -s exit:0 -e empty -o match:sleep -o match:Running \ + ${TEST_SH} -c 'sleep 1 & P=$!; jobs; wait' + atf_check -s exit:0 -e empty -o match:sleep -o match:Done \ + ${TEST_SH} -c 'sleep 1 & P=$!; sleep 2; jobs; wait' +} + +atf_test_case read +read_head() { + atf_set "descr" "Tests the shell builtin read command" +} +read_body() { + have_builtin read "" "echo x|" "var" || return 0 +} + +atf_test_case readonly +readonly_head() { + atf_set "descr" "Tests the shell builtin 'readonly'" +} +readonly_body() { + have_builtin readonly || return 0 + + atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c 'readonly VAR' + atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c 'readonly VAR=abc' + atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c 'readonly V A R' + atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c 'readonly V A=1 R=2' + + atf_check -s exit:0 -e empty -o inline:unset ${TEST_SH} -c \ + 'unset VAR; readonly VAR; printf %s ${VAR-unset}' + atf_check -s exit:0 -e empty -o inline:set ${TEST_SH} -c \ + 'unset VAR; readonly VAR=set; printf %s ${VAR-unset}' + atf_check -s exit:0 -e empty -o inline:set ${TEST_SH} -c \ + 'VAR=initial; readonly VAR=set; printf %s ${VAR-unset}' + atf_check -s not-exit:0 -e not-empty -o empty ${TEST_SH} -c \ + 'readonly VAR=initial; VAR=new; printf %s "${VAR}"' + + # don't check VAR=value, some shells provide meaningless quoting... + atf_check -s exit:0 -e empty -o match:VAR= -o match:foobar \ + ${TEST_SH} -c \ + 'VAR=foobar ; readonly VAR ; readonly -p' + atf_check -s exit:0 -e empty -o match:VAR= -o match:foobar \ + ${TEST_SH} -c \ + 'readonly VAR=foobar ; readonly -p' + atf_check -s exit:0 -e empty -o match:VAR= -o match:foobar \ + -o not-match:badvalue ${TEST_SH} -c \ + 'VAR=badvalue; readonly VAR=foobar ; readonly -p' + atf_check -s exit:0 -e empty -o match:VAR\$ ${TEST_SH} -c \ + 'unset VAR ; readonly VAR ; readonly -p' + + # checking that readonly -p works (to reset stuff) is a pain... + # particularly since not all shells say "readonly..." by default + atf_check -s exit:0 -e empty -o match:MYVAR= -o match:FOUND=foobar \ + ${TEST_SH} -c \ + 'V=$(readonly MYVAR=foobar; readonly -p | grep " MYVAR") + unset MYVAR; eval "$V"; readonly -p; + printf %s\\n FOUND=${MYVAR-unset}' + atf_check -s exit:0 -e empty -o match:MYVAR\$ -o match:FOUND=unset \ + ${TEST_SH} -c \ + 'V=$(readonly MYVAR; readonly -p | grep " MYVAR") + unset MYVAR; eval "$V"; readonly -p; + printf %s\\n "FOUND=${MYVAR-unset}"' + atf_check -s exit:0 -e empty -o match:MYVAR= -o match:FOUND=empty \ + ${TEST_SH} -c \ + 'V=$(readonly MYVAR=; readonly -p | grep " MYVAR") + unset VAR; eval "$V"; readonly -p; + printf %s\\n "FOUND=${MYVAR-unset&}${MYVAR:-empty}"' + + # don't test stderr, some shells inist on generating a message for an + # unset of a readonly var (rather than simply having unset make $?=1) + + atf_check -s not-exit:0 -e empty -o empty ${TEST_SH} -c \ + 'unset VAR; readonly VAR=set; + unset VAR 2>/dev/null && printf %s ${VAR:-XX}' + atf_check -s not-exit:0 -e ignore -o empty ${TEST_SH} -c \ + 'unset VAR; readonly VAR=set; unset VAR && printf %s ${VAR:-XX}' + atf_check -s exit:0 -e ignore -o inline:set ${TEST_SH} -c \ + 'unset VAR; readonly VAR=set; unset VAR; printf %s ${VAR-unset}' +} + +atf_test_case readonly_nbsd +readonly_nbsd_head() { + atf_set "descr" "Tests NetBSD extensions to 'readonly'" +} +readonly_nbsd_body() { + have_builtin readonly '' 'readonly VAR;' '-q VAR' ' -q' || return 0 + + atf_check -s exit:1 -o empty -e empty ${TEST_SH} -c \ + 'VAR=set; readonly -q VAR' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'readonly VAR=set; readonly -q VAR' + atf_check -s exit:1 -o empty -e empty ${TEST_SH} -c \ + 'VAR=set; RW=set; readonly -q VAR RW' + atf_check -s exit:1 -o empty -e empty ${TEST_SH} -c \ + 'VAR=set; readonly RO=set; readonly -q VAR RO' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'readonly VAR=set RO=set; readonly -q VAR RO' + + atf_check -s exit:1 -o empty -e empty ${TEST_SH} -c \ + 'unset VAR; readonly -q VAR' + # next one is the same as the have_builtin test, so "cannot" fail... + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'unset VAR; readonly VAR; readonly -q VAR' + + # if we have -q we should also have -p var... + # What's more, we are testing NetBSD sh, so we know output format. + + atf_check -s exit:0 -e empty -o match:VAR=foobar \ + ${TEST_SH} -c \ + 'VAR=foobar ; readonly VAR ; readonly -p VAR' + atf_check -s exit:0 -e empty -o inline:1 \ + ${TEST_SH} -c \ + 'VAR=foobar ; readonly VAR ; + printf %d $(readonly -p VAR | wc -l)' + atf_check -s exit:0 -e empty \ + -o inline:'readonly VAR=foobar\nreadonly OTHER\n' \ + ${TEST_SH} -c \ + 'VAR=foobar; readonly VAR OTHER; readonly -p VAR OTHER' + atf_check -s exit:0 -e empty \ + -o inline:'readonly A=aaa\nreadonly B\nreadonly D='"''"'\n' \ + ${TEST_SH} -c \ + 'A=aaa D= C=foo; unset B; readonly A B D; + readonly -p A B C D' +} + +atf_test_case cd_pwd +cd_pwd_head() { + atf_set "descr" "Tests the shell builtins 'cd' & 'pwd'" +} +cd_pwd_body() { + have_builtin cd "" "HOME=/;" || return 0 + have_builtin pwd || return 0 +} + +atf_test_case true_false +true_false_head() { + atf_set "descr" "Tests the 'true' and 'false' shell builtin commands" +} +true_false_body() { + have_builtin true || return 0 + + atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c true + + # true is not a special builtin, so errors do not cause exit + # but we should still get an error from the broken redirect + # and the exit status of true should be false... + + atf_check -s exit:0 -e not-empty -o inline:OK ${TEST_SH} -c \ + "true >/foo/bar && printf %s NOT-; printf %s OK" + + # and var-assigns should not affect the current sh env + + atf_check -s exit:0 -e empty -o inline:IS-OK ${TEST_SH} -c \ + 'X=OK; X=BROKEN true && printf %s IS-; printf %s "${X}"' + + have_builtin false "" ! || return 0 + + atf_check -s exit:1 -e empty -o empty ${TEST_SH} -c false +} + +atf_test_case type +type_head() { + atf_set "descr" "Tests the sh builtin 'type' command" +} +type_body() { + have_builtin type "" "" type || return 0 +} + +# This currently has its own t_ulimit - either merge that here, +# or delete this one and keep that... ulimit -n is also tested in +# the t_redir tests, as that affects the shell's use of file descriptors +atf_test_case ulimit +ulimit_head() { + atf_set "descr" "Tests the sh builtin 'ulimit'" +} +ulimit_body() { + have_builtin ulimit || return 0 +} + +atf_test_case umask +umask_head() { + atf_set "descr" "Tests the sh builtin 'umask'" +} +umask_body() { + have_builtin umask || return 0 + + atf_require_prog touch + atf_require_prog stat + atf_require_prog rm + atf_require_prog chmod + + reset umask + + # 8 octal digits + for M in 0 1 2 3 4 5 6 7 + do + # Test numbers start: 1 25 49 73 97 121 145 169 + + # 8 combinations of each to test (64 inner loops) + # 3 tests in each loop, hence 192 subtests in all + + # Test numbers from loop above, plus (below) and the next 2 + #+ 1 4 7 10 13 + for T in "0${M}" "00${M}" "0${M}0" "0${M}00" "0${M}${M}" \ + "0${M}${M}0" "0${M}${M}${M}" "0${M}0${M}" + #+ 16 19 22 + do + # umask turns bits off, calculate which bits will be on... + + D=$(( 0777 & ~ T )) # for directories + F=$(( $D & ~ 0111 )) # and files with no 'x' bits + + # Note: $(( )) always produces decimal, so we test that format + # (see '%d' in printf of stat result) + + # need chmod or we might have no perm to rmdir TD + { chmod +rwx TF TFT TD; rm -fr TF TFT TD; } 2>/dev/null || : + + # check that the umask applies to files created by the shell + check \ + "umask $T; > TF; printf %d \$(stat -nf %#Lp TF)" \ + "$F" 0 "$F is $(printf %#o $F)" # 1 4 7 10 ... + + # and to files created by commands that the shell runs + check \ + "umask $T; touch TFT; printf %d \$(stat -nf %#Lp TFT)" \ + "$F" 0 "$F is $(printf %#o $F)" # 2 5 8 11 ... + + # and to directories created b ... (directories keep 'x') + check \ + "umask $T; mkdir TD; printf %d \$(stat -nf %#Lp TD)" \ + "$D" 0 "$D is $(printf %#o $D)" # 3 6 9 12 ... + done + done + + # Now add a few more tests with less regular u/g/m masks + # In here, include tests where umask value has no leading '0' + + # 10 loops, the same 3 tests in each loop, 30 more subtests + # from 193 .. 222 + + # 193 196 199 202 205 208 211 214 217 220 + for T in 013 047 722 0772 027 123 421 0124 0513 067 + do + D=$(( 0777 & ~ 0$T )) + F=$(( $D & ~ 0111 )) + + { chmod +rwx TF TFT TD; rm -fr TF TFT TD; } 2>/dev/null || : + + check \ + "umask $T; > TF; printf %d \$(stat -nf %#Lp TF)" \ + "$F" 0 "$F is $(printf %#o $F)" # +0 + + check \ + "umask $T; touch TFT; printf %d \$(stat -nf %#Lp TFT)" \ + "$F" 0 "$F is $(printf %#o $F)" # +1 + + check \ + "umask $T; mkdir TD; printf %d \$(stat -nf %#Lp TD)" \ + "$D" 0 "$D is $(printf %#o $D)" # +2 + done + + results +} + +atf_test_case unset +unset_head() { + atf_set "descr" "Tests the sh builtin 'unset'" +} +unset_body() { + have_builtin unset || return 0 +} + +atf_test_case hash +hash_head() { + atf_set "descr" "Tests the sh builtin 'hash' (ash extension)" +} +hash_body() { + have_builtin hash || return 0 +} + +atf_test_case jobid +jobid_head() { + atf_set "descr" "Tests sh builtin 'jobid' (NetBSD extension)" +} +jobid_body() { + + # have_builtin jobid || return 0 No simple jobid command test + $TEST_SH -c '(exit 0)& jobid $!' >/dev/null 2>&1 || { + atf_skip "${TEST_SH} has no 'jobid' built-in" + return 0 + } +} + +atf_test_case let +let_head() { + atf_set "descr" "Tests the sh builtin 'let' (common extension from ksh)" +} +let_body() { + have_builtin let "" "" 1 || return 0 +} + +atf_test_case local +local_head() { + atf_set "descr" "Tests the shell builtin 'local' (common extension)" +} +local_body() { + have_builtin local "" "f () {" "X; }; f" || return 0 +} + +atf_test_case setvar +setvar_head() { + atf_set "descr" "Tests the shell builtin 'setvar' (BSD extension)" +} +setvar_body() { + have_builtin setvar || return 0 + + atf_check -s exit:0 -e empty -o inline:foo ${TEST_SH} -c \ + 'unset PQ && setvar PQ foo; printf %s "${PQ-not set}"' + atf_check -s exit:0 -e empty -o inline:abcd ${TEST_SH} -c \ + 'for x in a b c d; do setvar "$x" "$x"; done; + printf %s "$a$b$c$d"' + atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c \ + 'a=1; b=2; c=3; d=4 + for x in a b c d; do setvar "$x" ""; done; + printf %s "$a$b$c$d"' +} + +atf_test_case fdflags +fdflags_head() { + atf_set "descr" \ + "Tests basic operation of sh builtin 'fdflags' (NetBSD extension)" +} +fdflags_body() { + have_builtin fdflags || return 0 +} + +atf_test_case fdflags__s +fdflags__s_head() { + atf_set "descr" "Checks setting/clearing flags on file descriptors" +} +fdflags__s_body() { + have_builtin fdflags || return 0 +} + +atf_test_case fdflags__v +fdflags__v_head() { + atf_set "descr" "Checks verbose operation of fdflags" +} +fdflags__v_body() { + have_builtin fdflags || return 0 +} + +atf_test_case fdflags__v_s +fdflags__v_s_head() { + atf_set "descr" "tests verbose operation of fdflags -s" +} +fdflags__v_s_body() { + have_builtin fdflags || return 0 +} + +atf_test_case fdflags_multiple_fd +fdflags_multiple_fd_head() { + atf_set "descr" "Checks operation of fdflags with more than one fd" +} +fdflags_multiple_fd_body() { + have_builtin fdflags || return 0 +} + +atf_test_case fdflags_one_flag_at_a_time +fdflags_one_flag_at_a_time_head() { + atf_set "descr" "Tests all possible fdflags flags, and combinations" +} +fdflags_one_flag_at_a_time_body() { + have_builtin fdflags || return 0 +} + +atf_test_case fdflags_save_restore +fdflags_save_restore_head() { + atf_set "descr" 'Verify that fd flags can be saved and restored' +} +fdflags_save_restore_body() { + have_builtin fdflags || return 0 +} + +atf_test_case fdflags_names_abbreviated +fdflags_names_abbreviated_head() { + atf_set "descr" 'Tests using abbreviated names for fdflags' +} +fdflags_names_abbreviated_body() { + have_builtin fdflags || return 0 +} + +atf_test_case fdflags_xx_errors +fdflags_xx_errors_head() { + atf_set "descr" 'Check various erroneous fdflags uses' +} +fdflags_xx_errors_body() { + have_builtin fdflags || return 0 +} + + +atf_init_test_cases() { + + # "standard" builtin commands in sh + + # no tests of the "very special" (almost syntax) builtins + # (break/continue/return) - they're tested enough elsewhere + + atf_add_test_case cd_pwd + atf_add_test_case colon + atf_add_test_case echo + atf_add_test_case eval + atf_add_test_case exec + atf_add_test_case export + atf_add_test_case getopts + atf_add_test_case jobs + atf_add_test_case read + atf_add_test_case readonly + atf_add_test_case true_false + atf_add_test_case type + atf_add_test_case ulimit + atf_add_test_case umask + atf_add_test_case unset + + # exit/wait/set/shift/trap/alias/unalias/. should have their own tests + # fc/times/fg/bg/% are too messy to contemplate for now + # command ?? (probably should have some tests) + + # Note that builtin versions of, printf, kill, ... are tested separately + # (these are all "optional" builtins) + # (echo is tested here because NetBSD sh builtin echo and /bin/echo + # are different) + + atf_add_test_case export_nbsd + atf_add_test_case hash + atf_add_test_case jobid + atf_add_test_case let + atf_add_test_case local + atf_add_test_case readonly_nbsd + atf_add_test_case setvar + # inputrc should probably be tested in libedit tests (somehow) + + # fdflags has a bunch of test cases + + # Always run one test, so we get at least "skipped" result + atf_add_test_case fdflags + + # but no need to say "skipped" lots more times... + have_builtin fdflags available && { + atf_add_test_case fdflags__s + atf_add_test_case fdflags__v + atf_add_test_case fdflags__v_s + atf_add_test_case fdflags_multiple_fd + atf_add_test_case fdflags_names_abbreviated + atf_add_test_case fdflags_one_flag_at_a_time + atf_add_test_case fdflags_save_restore + atf_add_test_case fdflags_xx_errors + } + return 0 +} diff --git a/bin/sh/t_cmdsub.sh b/bin/sh/t_cmdsub.sh old mode 100755 new mode 100644 --- a/bin/sh/t_cmdsub.sh +++ b/bin/sh/t_cmdsub.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_cmdsub.sh,v 1.4 2016/04/04 12:40:13 christos Exp $ +# $NetBSD: t_cmdsub.sh,v 1.5 2017/06/16 07:37:41 kre Exp $ # # Copyright (c) 2016 The NetBSD Foundation, Inc. # All rights reserved. @@ -678,31 +678,31 @@ ' } -atf_test_case t_nested_cmdsubs_in_heredoc -t_nested_cmdsubs_in_heredoc_head() { - atf_set "descr" "Checks nested command substitutions in here docs" +atf_test_case y_many_embedded_nl +y_many_embedded_nl_head() { + atf_set "descr" "Checks command substitutions that return many lines" } -t_nested_cmdsubs_in_heredoc_body() { - atf_require_prog cat - atf_require_prog rm +y_many_embedded_nl_body() { + atf_require_prog wc + atf_require_prog seq - rm -f * 2>/dev/null || : - echo "Hello" > File + # first make sure it should work + atf_check -s exit:0 -o match:'1002' -e empty \ + ${TEST_SH} -c "{ printf '%s\n' x + for a in \$( seq 1000 ); do printf '\n'; done + printf '%s\n' y; } | wc -l" - atf_check -s exit:0 -o inline:'Hello U\nHelp me!\n' -e empty \ - ${TEST_SH} -c 'cat <<- EOF - $(cat File) U - $( V=$(cat File); echo "${V%lo}p" ) me! - EOF' - - rm -f * 2>/dev/null || : - echo V>V ; echo A>A; echo R>R - echo Value>VAR + # then use a cmd-sub to get the same thing + atf_check -s exit:0 -o match:'1002' -e empty \ + ${TEST_SH} -c "printf '%s\n' \"\$( printf '%s\n' x + for a in \$( seq 1000 ); do printf '\n'; done + printf '%s\n' y )\" | wc -l" - atf_check -s exit:0 -o inline:'$2.50\n' -e empty \ - ${TEST_SH} -c 'cat <<- EOF - $(Value='\''$2.50'\'';eval echo $(eval $(cat V)$(cat A)$(cat R)=\'\''\$$(cat $(cat V)$(cat A)$(cat R))\'\''; eval echo \$$(set -- *;echo ${3}${1}${2}))) - EOF' + # and a much bigger one. + atf_check -s exit:0 -o match:'10002' -e empty \ + ${TEST_SH} -c "printf '%s\n' \"\$( printf '%s\n' x + for a in \$( seq 10000 ); do printf '\n'; done + printf '%s\n' y )\" | wc -l" } atf_test_case z_absurd_heredoc_cmdsub_combos @@ -779,5 +779,6 @@ atf_add_test_case v_cmdsub_paren_tests atf_add_test_case w_heredoc_outside_cmdsub atf_add_test_case x_heredoc_outside_backticks + atf_add_test_case y_many_embedded_nl atf_add_test_case z_absurd_heredoc_cmdsub_combos } diff --git a/bin/sh/t_evaltested.sh b/bin/sh/t_evaltested.sh old mode 100755 new mode 100644 diff --git a/bin/sh/t_exit.sh b/bin/sh/t_exit.sh old mode 100755 new mode 100644 diff --git a/bin/sh/t_expand.sh b/bin/sh/t_expand.sh old mode 100755 new mode 100644 --- a/bin/sh/t_expand.sh +++ b/bin/sh/t_expand.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_expand.sh,v 1.8 2016/04/29 18:29:17 christos Exp $ +# $NetBSD: t_expand.sh,v 1.22 2019/05/04 02:52:22 kre Exp $ # # Copyright (c) 2007, 2009 The NetBSD Foundation, Inc. # All rights reserved. @@ -46,7 +46,7 @@ atf_test_case dollar_at dollar_at_head() { - atf_set "descr" "Somewhere between 2.0.2 and 3.0 the expansion" \ + atf_set descr "Somewhere between 2.0.2 and 3.0 the expansion" \ "of the \$@ variable had been broken. Check for" \ "this behavior." } @@ -63,9 +63,31 @@ 'set -- -; shift; n_arg() { echo $#; }; n_arg "$@"' } +atf_test_case dollar_at_unquoted_or_conditional +dollar_at_unquoted_or_conditional_head() { + atf_set descr 'Sometime during 2013 the expansion of "${1+$@}"' \ + ' (where $1 and $2 (and maybe more) are set)' \ + ' seems to have broken. Check for this bug.' +} +dollar_at_unquoted_or_conditional_body() { + + atf_check -s exit:0 -o inline:'a\na\nb\nb\n' -e empty \ + ${TEST_SH} -c 'set -- "a a" "b b"; printf %s\\n $@' + atf_check -s exit:0 -o inline:'a\na\nb\nb\n' -e empty \ + ${TEST_SH} -c 'set -- "a a" "b b"; printf %s\\n ${1+$@}' + atf_check -s exit:0 -o inline:'a a\nb b\n' -e empty \ + ${TEST_SH} -c 'set -- "a a" "b b"; printf %s\\n "$@"' + atf_check -s exit:0 -o inline:'a a\nb b\n' -e empty \ + ${TEST_SH} -c 'set -- "a a" "b b"; printf %s\\n ${1+"$@"}' + + # This is the one that fails when the bug is present + atf_check -s exit:0 -o inline:'a a\nb b\n' -e empty \ + ${TEST_SH} -c 'set -- "a a" "b b"; printf %s\\n "${1+$@}"' +} + atf_test_case dollar_at_with_text dollar_at_with_text_head() { - atf_set "descr" "Test \$@ expansion when it is surrounded by text" \ + atf_set descr "Test \$@ expansion when it is surrounded by text" \ "within the quotes. PR bin/33956." } dollar_at_with_text_body() { @@ -105,30 +127,235 @@ for f in 1 2 do + atf_check -s exit:0 -o inline:'\n' -e empty ${TEST_SH} -c \ + ". ./h-f${f}; "'set -- ; delim_argv $@' atf_check -s exit:0 -o inline:'\n' -e empty ${TEST_SH} -c \ ". ./h-f${f}; "'set -- ; delim_argv "$@"' atf_check -s exit:0 -o inline:'>foobar<\n' -e empty \ ${TEST_SH} -c \ ". ./h-f${f}; "'set -- ; delim_argv "foo$@bar"' + atf_check -s exit:0 -o inline:'>foobar<\n' -e empty \ + ${TEST_SH} -c \ + ". ./h-f${f}; "'set -- ; delim_argv foo"$@"bar' atf_check -s exit:0 -o inline:'>foo bar<\n' -e empty \ ${TEST_SH} -c \ ". ./h-f${f}; "'set -- ; delim_argv "foo $@ bar"' + atf_check -s exit:0 -o inline:'>foo bar<\n' -e empty \ + ${TEST_SH} -c \ + ". ./h-f${f}; "'set -- ; delim_argv foo" $@ "bar' atf_check -s exit:0 -o inline:'>a< >b< >c<\n' -e empty \ ${TEST_SH} -c \ ". ./h-f${f}; "'set -- a b c; delim_argv "$@"' + atf_check -s exit:0 -o inline:'>fooa< >b< >cbar<\n' -e empty \ ${TEST_SH} -c \ ". ./h-f${f}; "'set -- a b c; delim_argv "foo$@bar"' + atf_check -s exit:0 -o inline:'>foo a< >b< >c bar<\n' -e empty \ ${TEST_SH} -c \ ". ./h-f${f}; "'set -- a b c; delim_argv "foo $@ bar"' done } +atf_test_case dollar_at_empty_and_conditional +dollar_at_empty_and_conditional_head() { + atf_set descr 'Test $@ expansion when there are no args, and ' \ + 'when conditionally expanded.' +} +dollar_at_empty_and_conditional_body() { + + # same task, implementation different from previous, + # that these work is also a test... + + cat <<'EOF' > h-f3 + +delim_argv() { + str= + for Arg; do + str="${str:+${str} }>${Arg}<" + done + printf '%s\n' "${str}" +} + +EOF + + chmod +x h-f3 + + # in these we give printf a first arg of "", which makes + # the first output char be \n, then the $@ produces anything else + # (we need to make sure we don't end up with: + # printf %s\\n + # -- that is, no operands for the %s, that's unspecified) + + atf_check -s exit:0 -o inline:'\na a\nb b\n' -e empty \ + ${TEST_SH} -c 'set -- "a a" "b b"; printf %s\\n "" "$@"' + atf_check -s exit:0 -o inline:'\n' -e empty \ + ${TEST_SH} -c 'set -- ; printf %s\\n "" "$@"' + atf_check -s exit:0 -o inline:'\n' -e empty \ + ${TEST_SH} -c 'set -- ; printf %s\\n ""${1+"$@"}' + atf_check -s exit:0 -o inline:'\n' -e empty \ + ${TEST_SH} -c 'set -- ; printf %s\\n """${1+$@}"' + + # in these we prefix (concat) the $@ expansion with "" to make + # sure there is always at least one arg for the %s in printf + # If there is anything else there, the prepended nothing vanishes + atf_check -s exit:0 -o inline:'a a\nb b\n' -e empty \ + ${TEST_SH} -c 'set -- "a a" "b b"; printf %s\\n """$@"' + atf_check -s exit:0 -o inline:'\n' -e empty \ + ${TEST_SH} -c 'set -- ; printf %s\\n """$@"' + atf_check -s exit:0 -o inline:'\n' -e empty \ + ${TEST_SH} -c 'set -- ; printf %s\\n ""${1+"$@"}' + atf_check -s exit:0 -o inline:'\n' -e empty \ + ${TEST_SH} -c 'set -- ; printf %s\\n """${1+$@}"' + + atf_check -s exit:0 -o inline:'>a< >b< >c<\n' -e empty ${TEST_SH} -c \ + '. ./h-f3; set -- a b c; delim_argv "${1+$@}"' + atf_check -s exit:0 -o inline:'>a< >b< >c<\n' -e empty ${TEST_SH} -c \ + '. ./h-f3; set -- a b c; delim_argv ${1+"$@"}' + atf_check -s exit:0 -o inline:'>fooa< >b< >cbar<\n' -e empty \ + ${TEST_SH} -c \ + '. ./h-f3; set -- a b c; delim_argv "foo${1+$@}bar"' + atf_check -s exit:0 -o inline:'>fooa< >b< >cbar<\n' -e empty \ + ${TEST_SH} -c \ + '. ./h-f3; set -- a b c; delim_argv foo${1+"$@"}bar' + atf_check -s exit:0 -o inline:'>foo a< >b< >c bar<\n' -e empty \ + ${TEST_SH} -c \ + '. ./h-f3; set -- a b c; delim_argv "foo ${1+$@} bar"' + atf_check -s exit:0 -o inline:'>foo a< >b< >c bar<\n' -e empty \ + ${TEST_SH} -c \ + '. ./h-f3; set -- a b c; delim_argv "foo "${1+"$@"}" bar"' + + # since $1 is not set, we get nothing ($@ is irrelevant) + # (note here we are not using printf, don't need to guarantee an arg) + atf_check -s exit:0 -o inline:'\n' -e empty ${TEST_SH} -c \ + '. ./h-f3; set -- ; delim_argv ${1+"$@"}' + + # here since $1 is not set we get "" as the special $@ properties + # do not apply, and ${foo+anything} generates nothing if foo is unset + atf_check -s exit:0 -o inline:'><\n' -e empty ${TEST_SH} -c \ + '. ./h-f3; set -- ; delim_argv "${1+$@}"' + + # in this one we get the initial "" followed by nothing + atf_check -s exit:0 -o inline:'><\n' -e empty ${TEST_SH} -c \ + '. ./h-f3; set -- ; delim_argv ""${1+"$@"}' + # which we verify by changing the "" to X, and including Y + atf_check -s exit:0 -o inline:'>X<\n' -e empty ${TEST_SH} -c \ + '. ./h-f3; set -- ; delim_argv X${1+"$@"Y}' + # and again, done differently (the ${1+...} produces nothing at all + atf_check -s exit:0 -o inline:'>X<\n' -e empty ${TEST_SH} -c \ + '. ./h-f3; set -- ; delim_argv X ${1+"$@"}' + # in these two we get the initial "" and then nothing + atf_check -s exit:0 -o inline:'><\n' -e empty ${TEST_SH} -c \ + '. ./h-f3; set -- ; delim_argv """${1+$@}"' + atf_check -s exit:0 -o inline:'>< ><\n' -e empty ${TEST_SH} -c \ + '. ./h-f3; set -- ; delim_argv "" "${1+$@}"' + + # now we repeat all those with $1 set (so we eval the $@) + atf_check -s exit:0 -o inline:'>a<\n' -e empty ${TEST_SH} -c \ + '. ./h-f3; set -- a ; delim_argv ""${1+"$@"}' + atf_check -s exit:0 -o inline:'>XaY<\n' -e empty ${TEST_SH} -c \ + '. ./h-f3; set -- a ; delim_argv X${1+"$@"Y}' + atf_check -s exit:0 -o inline:'>X< >a<\n' -e empty ${TEST_SH} -c \ + '. ./h-f3; set -- a ; delim_argv X ${1+"$@"}' + atf_check -s exit:0 -o inline:'>a<\n' -e empty ${TEST_SH} -c \ + '. ./h-f3; set -- a ; delim_argv """${1+$@}"' + atf_check -s exit:0 -o inline:'>< >a<\n' -e empty ${TEST_SH} -c \ + '. ./h-f3; set -- a ; delim_argv "" "${1+$@}"' + + # now we do all of those again, but testing $X instead of $1 (X set) + atf_check -s exit:0 -o inline:'\n' -e empty ${TEST_SH} -c \ + '. ./h-f3; X=1; set -- ; delim_argv ${X+"$@"}' + atf_check -s exit:0 -o inline:'><\n' -e empty ${TEST_SH} -c \ + '. ./h-f3; X=1; set -- ; delim_argv ""${X+"$@"}' + atf_check -s exit:0 -o inline:'>X<\n' -e empty ${TEST_SH} -c \ + '. ./h-f3; X=1; set -- ; delim_argv X${X+"$@"}' + atf_check -s exit:0 -o inline:'>X<\n' -e empty ${TEST_SH} -c \ + '. ./h-f3; X=1; set -- ; delim_argv X ${X+"$@"}' + atf_check -s exit:0 -o inline:'\n' -e empty ${TEST_SH} -c \ + '. ./h-f3; X=1; set -- ; delim_argv "${X+$@}"' + atf_check -s exit:0 -o inline:'><\n' -e empty ${TEST_SH} -c \ + '. ./h-f3; X=1; set -- ; delim_argv """${X+$@}"' + atf_check -s exit:0 -o inline:'><\n' -e empty ${TEST_SH} -c \ + '. ./h-f3; X=1; set -- ; delim_argv "" "${X+$@}"' + + atf_check -s exit:0 -o inline:'>a<\n' -e empty ${TEST_SH} -c \ + '. ./h-f3; X=1; set -- a; delim_argv ${X+"$@"}' + atf_check -s exit:0 -o inline:'>a< >b<\n' -e empty ${TEST_SH} -c \ + '. ./h-f3; X=1; set -- a b; delim_argv ${X+"$@"}' + atf_check -s exit:0 -o inline:'>a<\n' -e empty ${TEST_SH} -c \ + '. ./h-f3; X=1; set -- a ; delim_argv ""${X+"$@"}' + atf_check -s exit:0 -o inline:'>Xa<\n' -e empty ${TEST_SH} -c \ + '. ./h-f3; X=1; set -- a ; delim_argv X${X+"$@"}' + atf_check -s exit:0 -o inline:'>X< >a<\n' -e empty ${TEST_SH} -c \ + '. ./h-f3; X=1; set -- a ; delim_argv X ${X+"$@"}' + atf_check -s exit:0 -o inline:'>a<\n' -e empty ${TEST_SH} -c \ + '. ./h-f3; X=1; set -- a ; delim_argv """${X+$@}"' + atf_check -s exit:0 -o inline:'>a< >b<\n' -e empty ${TEST_SH} -c \ + '. ./h-f3; X=1; set -- a b ; delim_argv """${X+$@}"' + atf_check -s exit:0 -o inline:'>< >a<\n' -e empty ${TEST_SH} -c \ + '. ./h-f3; X=1; set -- a ; delim_argv "" "${X+$@}"' + atf_check -s exit:0 -o inline:'>< >a< >b<\n' -e empty ${TEST_SH} -c \ + '. ./h-f3; X=1; set -- a b ; delim_argv "" "${X+$@}"' + + # and again, but testing $X where X is unset + atf_check -s exit:0 -o inline:'\n' -e empty ${TEST_SH} -c \ + '. ./h-f3; unset X; set -- ; delim_argv ${X+"$@"}' + atf_check -s exit:0 -o inline:'><\n' -e empty ${TEST_SH} -c \ + '. ./h-f3; unset X; set -- ; delim_argv ""${X+"$@"}' + atf_check -s exit:0 -o inline:'>X<\n' -e empty ${TEST_SH} -c \ + '. ./h-f3; unset X; set -- ; delim_argv X${X+"$@"}' + atf_check -s exit:0 -o inline:'>X<\n' -e empty ${TEST_SH} -c \ + '. ./h-f3; unset X; set -- ; delim_argv X ${X+"$@"}' + atf_check -s exit:0 -o inline:'><\n' -e empty ${TEST_SH} -c \ + '. ./h-f3; unset X; set -- ; delim_argv "${X+$@}"' + atf_check -s exit:0 -o inline:'><\n' -e empty ${TEST_SH} -c \ + '. ./h-f3; unset X; set -- ; delim_argv """${X+$@}"' + atf_check -s exit:0 -o inline:'>< ><\n' -e empty ${TEST_SH} -c \ + '. ./h-f3; unset X; set -- ; delim_argv "" "${X+$@}"' + atf_check -s exit:0 -o inline:'\n' -e empty ${TEST_SH} -c \ + '. ./h-f3; unset X; set -- a; delim_argv ${X+"$@"}' + atf_check -s exit:0 -o inline:'\n' -e empty ${TEST_SH} -c \ + '. ./h-f3; unset X; set -- a b; delim_argv ${X+"$@"}' + atf_check -s exit:0 -o inline:'><\n' -e empty ${TEST_SH} -c \ + '. ./h-f3; unset X; set -- a ; delim_argv ""${X+"$@"}' + atf_check -s exit:0 -o inline:'>X<\n' -e empty ${TEST_SH} -c \ + '. ./h-f3; unset X; set -- a ; delim_argv X${X+"$@"}' + atf_check -s exit:0 -o inline:'>X<\n' -e empty ${TEST_SH} -c \ + '. ./h-f3; unset X; set -- a ; delim_argv X ${X+"$@"}' + atf_check -s exit:0 -o inline:'><\n' -e empty ${TEST_SH} -c \ + '. ./h-f3; unset X; set -- a ; delim_argv """${X+$@}"' + atf_check -s exit:0 -o inline:'>< ><\n' -e empty ${TEST_SH} -c \ + '. ./h-f3; unset X; set -- a; delim_argv "" "${X+$@}"' + + # a few that stretch belief... + + atf_check -s exit:0 -o inline:'>a< >b<\n' -e empty ${TEST_SH} -c \ + '. ./h-f3; X=1; set -- a b ; delim_argv ${X+${1+"$@"}}' + atf_check -s exit:0 -o inline:'\n' -e empty ${TEST_SH} -c \ + '. ./h-f3; X=1; set -- ; delim_argv ${X+${1+"$@"}}' + atf_check -s exit:0 -o inline:'\n' -e empty ${TEST_SH} -c \ + '. ./h-f3; unset X; set -- a b ; delim_argv ${X+${1+"$@"}}' + + # and now for something completely different + + atf_check -s exit:0 -o inline:'>a< >b< >ca< >b< >c<\n' -e empty \ + ${TEST_SH} -c '. ./h-f3; set -- a b c; delim_argv "$@$@"' + atf_check -s exit:0 -o inline:'>a< >b< >ca b c<\n' -e empty \ + ${TEST_SH} -c '. ./h-f3; set -- a b c; delim_argv "$@$*"' + atf_check -s exit:0 -o inline:'>a b ca< >b< >c<\n' -e empty \ + ${TEST_SH} -c '. ./h-f3; set -- a b c; delim_argv "$*$@"' + atf_check -s exit:0 -o inline:'>a a++b + ca a< >< >b < > c<\n' \ + -e empty ${TEST_SH} -c \ + '. ./h-f3; set -- "a a" "" "b " " c"; IFS=+; delim_argv "$*$@"' + atf_check -s exit:0 -o inline:'>a< >a< >< >b < > ca+a< >< >b < > c<\n' \ + -e empty ${TEST_SH} -c \ + '. ./h-f3; set -- "a+a" "" "b " " c"; IFS=+; delim_argv $*"$@"' +} + atf_test_case strip strip_head() { - atf_set "descr" "Checks that the %% operator works and strips" \ + atf_set descr "Checks that the %% operator works and strips" \ "the contents of a variable from the given point" \ "to the end" } @@ -156,9 +383,113 @@ done } +atf_test_case wrap_strip +wrap_strip_head() { + atf_set descr "Checks that the %% operator works and strips" \ + "the contents of a variable from the given point" \ + 'to the end, and that \ \n sequences do not break it' +} +wrap_strip_body() { + line='#define bindir "/usr/bin" /* comment */' + stripped='#define bindir "/usr/bin" ' + + for exp in \ + '${line\ +%%/\**}' \ + '${line%%"/\ +*"*}' \ + '${line%%'"'"'/*'"'"'\ +*}' \ + '"${li\ +ne%%/\**}"' \ + '"${line%%"\ +/*"*}"' \ + '"${line%\ +%'"'"'/*'"'"'*}"' \ + '${line\ +%\ +/\*\ +*\ +}' \ + '${line%"/*\ +"*\ +}' \ + '${line\ +%\ +'"'"'/*'"'"'*}' \ + '"$\ +{li\ +ne%\ +'"'"'/*'"'"'*}"' + do + atf_check -o inline:":$stripped:\n" -e empty ${TEST_SH} -c \ + "line='${line}'; echo :${exp}:" + done +} + +atf_test_case tilde +tilde_head() { + atf_set descr "Checks that the ~ expansions work" +} +tilde_body() { + for HOME in '' / /home/foo \ +/a/very/long/home/directory/path/that/might/push/the/tilde/expansion/code/beyond/what/it/would/normally/ever/see/on/any/sane/system/and/perhaps/expose/some/bugs + do + export HOME + + atf_check -s exit:0 -e empty \ + -o inline:'HOME\t'"${HOME}"' +~\t'"${HOME}"' +~/foobar\t'"${HOME}"'/foobar +"$V"\t'"${HOME}"' +"$X"\t~ +"$Y"\t'"${HOME}"':'"${HOME}"' +"$YY"\t'"${HOME}"'/foo:'"${HOME}"'/bar +"$Z"\t'"${HOME}"'/~ +${U:-~}\t'"${HOME}"' +$V\t'"${HOME}"' +$X\t~ +$Y\t'"${HOME}"':'"${HOME}"' +$YY\t'"${HOME}"'/foo:'"${HOME}"'/bar +$Z\t'"${HOME}"'/~ +${U:=~}\t'"${HOME}"' +${UU:=~:~}\t'"${HOME}"':'"${HOME}"' +${UUU:=~/:~}\t'"${HOME}"'/:'"${HOME}"' +${U4:=~/:~/}\t'"${HOME}"'/:'"${HOME}"'/\n' \ + ${TEST_SH} -s <<- \EOF + unset -v U UU UUU U4 + V=~ + X="~" + Y=~:~ YY=~/foo:~/bar + Z=~/~ + printf '%s\t%s\n' \ + 'HOME' "${HOME}" \ + '~' ~ \ + '~/foobar' ~/foobar \ + '"$V"' "$V" \ + '"$X"' "$X" \ + '"$Y"' "$Y" \ + '"$YY"' "$YY" \ + '"$Z"' "$Z" \ + '${U:-~}' ''${U:-~} \ + '$V' ''$V \ + '$X' ''$X \ + '$Y' ''$Y \ + '$YY' ''$YY \ + '$Z' ''$Z \ + '${U:=~}' ''${U:=~} \ + '${UU:=~:~}' ''${UU:=~:~} \ + '${UUU:=~/:~}' ''${UUU:=~/:~} \ + '${U4:=~/:~/}' ''${U4:=~/:~/} + EOF + done + + # Testing ~user is harder, so, perhaps later... +} + atf_test_case varpattern_backslashes varpattern_backslashes_head() { - atf_set "descr" "Tests that protecting wildcards with backslashes" \ + atf_set descr "Tests that protecting wildcards with backslashes" \ "works in variable patterns." } varpattern_backslashes_body() { @@ -170,7 +501,7 @@ atf_test_case arithmetic arithmetic_head() { - atf_set "descr" "POSIX requires shell arithmetic to use signed" \ + atf_set descr "POSIX requires shell arithmetic to use signed" \ "long or a wider type. We use intmax_t, so at" \ "least 64 bits should be available. Make sure" \ "this is true." @@ -187,7 +518,7 @@ atf_test_case iteration_on_null_parameter iteration_on_null_parameter_head() { - atf_set "descr" "Check iteration of \$@ in for loop when set to null;" \ + atf_set descr "Check iteration of \$@ in for loop when set to null;" \ "the error \"sh: @: parameter not set\" is incorrect." \ "PR bin/48202." } @@ -198,7 +529,7 @@ atf_test_case iteration_on_quoted_null_parameter iteration_on_quoted_null_parameter_head() { - atf_set "descr" \ + atf_set descr \ 'Check iteration of "$@" in for loop when set to null;' } iteration_on_quoted_null_parameter_body() { @@ -208,7 +539,7 @@ atf_test_case iteration_on_null_or_null_parameter iteration_on_null_or_null_parameter_head() { - atf_set "descr" \ + atf_set descr \ 'Check expansion of null parameter as default for another null' } iteration_on_null_or_null_parameter_body() { @@ -218,7 +549,7 @@ atf_test_case iteration_on_null_or_missing_parameter iteration_on_null_or_missing_parameter_head() { - atf_set "descr" \ + atf_set descr \ 'Check expansion of missing parameter as default for another null' } iteration_on_null_or_missing_parameter_body() { @@ -227,6 +558,8 @@ 'N=; set -- ${N:-}; for X; do echo "[$X]"; done' } +####### The remaining tests use the following helper functions ... + nl=' ' reset() @@ -257,6 +590,7 @@ # don't actually fail just because of wrong exit code # unless we either expected, or received "good" + # or something else is detected as incorrect as well. case "$3/${STATUS}" in (*/0|0/*) fail=true;; esac @@ -305,12 +639,17 @@ TEST_FAIL_COUNT=$(( $TEST_FAIL_COUNT + 1 )) return 0 } - $fail && atf_fail "Test[$TEST_NUM] of '$1' failed${nl}${MSG}" + $fail && atf_fail "Test[$TEST_NUM] failed: $( + # ATF does not like newlines in messages, so change them... + printf '%s' "${MSG}" | tr '\n' ';' + )" return 0 } results() { + test -n "$1" && atf_expect_fail "$1" + test -z "${TEST_ID}" && return 0 test -z "${TEST_FAILURES}" && return 0 @@ -318,13 +657,16 @@ echo >&2 "While testing '${TEST_ID}'" echo >&2 " - - - - - - - - - - - - - - - - -" echo >&2 "${TEST_FAILURES}" + atf_fail \ - "Test ${TEST_ID}: $TEST_FAIL_COUNT subtests (of $TEST_NUM) failed - see stderr" + "Test ${TEST_ID}: $TEST_FAIL_COUNT (of $TEST_NUM) subtests failed - see stderr" } +####### End helpers + atf_test_case shell_params shell_params_head() { - atf_set "descr" "Test correct operation of the numeric parameters" + atf_set descr "Test correct operation of the numeric parameters" } shell_params_body() { atf_require_prog mktemp @@ -363,18 +705,931 @@ 'abab?cbb:bab?cbb+abab?cb-b?cbb_a%bab?cbb=abab?cb/abab' 0 check 'set -- a "" c "" e; echo "${2:=b}"' '' 1 + check 'set -- a b c d; echo ${4294967297}' '' 0 # result 'a' => ${1} + check 'set -- a b c; echo ${01}' 'a' 0 + check "${TEST_SH} -c 'echo 0=\${00} 1=\${01} 2=\${02}' a b c" \ + '0=a 1=b 2=c' 0 + + # by special request, for PaulG... (${0...} is not octal!) + + # Posix XCU 2.5.1 (Issue 7 TC2 pg 2349 lines 74835..6): + # The digits denoting the positional parameters shall always + # be interpreted as a decimal value, even if there is a leading zero. + + check \ + 'set -- a b c d e f g h i j k l m; echo "$#: ${08} ${010} ${011}"' \ + '13: h j k' 0 + + results +} + +atf_test_case var_with_embedded_cmdsub +var_with_embedded_cmdsub_head() { + atf_set descr "Test expansion of vars with embedded cmdsub" +} +var_with_embedded_cmdsub_body() { + + reset var_with_embedded_cmdsub + + check 'unset x; echo ${x-$(echo a)}$(echo b)' 'ab' 0 #1 + check 'unset x; echo ${x:-$(echo a)}$(echo b)' 'ab' 0 #2 + check 'x=""; echo ${x-$(echo a)}$(echo b)' 'b' 0 #3 + check 'x=""; echo ${x:-$(echo a)}$(echo b)' 'ab' 0 #4 + check 'x=c; echo ${x-$(echo a)}$(echo b)' 'cb' 0 #5 + check 'x=c; echo ${x:-$(echo a)}$(echo b)' 'cb' 0 #6 + + check 'unset x; echo ${x+$(echo a)}$(echo b)' 'b' 0 #7 + check 'unset x; echo ${x:+$(echo a)}$(echo b)' 'b' 0 #8 + check 'x=""; echo ${x+$(echo a)}$(echo b)' 'ab' 0 #9 + check 'x=""; echo ${x:+$(echo a)}$(echo b)' 'b' 0 #10 + check 'x=c; echo ${x+$(echo a)}$(echo b)' 'ab' 0 #11 + check 'x=c; echo ${x:+$(echo a)}$(echo b)' 'ab' 0 #12 + + check 'unset x; echo ${x=$(echo a)}$(echo b)' 'ab' 0 #13 + check 'unset x; echo ${x:=$(echo a)}$(echo b)' 'ab' 0 #14 + check 'x=""; echo ${x=$(echo a)}$(echo b)' 'b' 0 #15 + check 'x=""; echo ${x:=$(echo a)}$(echo b)' 'ab' 0 #16 + check 'x=c; echo ${x=$(echo a)}$(echo b)' 'cb' 0 #17 + check 'x=c; echo ${x:=$(echo a)}$(echo b)' 'cb' 0 #18 + + check 'unset x; echo ${x?$(echo a)}$(echo b)' '' 2 #19 + check 'unset x; echo ${x:?$(echo a)}$(echo b)' '' 2 #20 + check 'x=""; echo ${x?$(echo a)}$(echo b)' 'b' 0 #21 + check 'x=""; echo ${x:?$(echo a)}$(echo b)' '' 2 #22 + check 'x=c; echo ${x?$(echo a)}$(echo b)' 'cb' 0 #23 + check 'x=c; echo ${x:?$(echo a)}$(echo b)' 'cb' 0 #24 + + check 'unset x; echo ${x%$(echo a)}$(echo b)' 'b' 0 #25 + check 'unset x; echo ${x%%$(echo a)}$(echo b)' 'b' 0 #26 + check 'x=""; echo ${x%$(echo a)}$(echo b)' 'b' 0 #27 + check 'x=""; echo ${x%%$(echo a)}$(echo b)' 'b' 0 #28 + check 'x=c; echo ${x%$(echo a)}$(echo b)' 'cb' 0 #29 + check 'x=c; echo ${x%%$(echo a)}$(echo b)' 'cb' 0 #30 + check 'x=aa; echo ${x%$(echo "*a")}$(echo b)' 'ab' 0 #31 + check 'x=aa; echo ${x%%$(echo "*a")}$(echo b)' 'b' 0 #32 + + check 'unset x; echo ${x#$(echo a)}$(echo b)' 'b' 0 #33 + check 'unset x; echo ${x##$(echo a)}$(echo b)' 'b' 0 #34 + check 'x=""; echo ${x#$(echo a)}$(echo b)' 'b' 0 #35 + check 'x=""; echo ${x##$(echo a)}$(echo b)' 'b' 0 #36 + check 'x=c; echo ${x#$(echo a)}$(echo b)' 'cb' 0 #37 + check 'x=c; echo ${x##$(echo a)}$(echo b)' 'cb' 0 #38 + check 'x=aa; echo ${x#$(echo "*a")}$(echo b)' 'ab' 0 #39 + check 'x=aa; echo ${x##$(echo "*a")}$(echo b)' 'b' 0 #40 + + results +} + +atf_test_case dollar_hash +dollar_hash_head() { + atf_set descr 'Test expansion of various aspects of $#' +} +dollar_hash_body() { + +# +# $# looks like it should be so simple that it doesn't really +# need a test of its own, and used in that way, it really doesn't. +# But when we add braces ${#} we need to deal with the three +# (almost 4) different meanings of a # inside a ${} expansion... +# +# Note that some of these are just how we treat expansions that +# are unspecified by posix (as noted below.) +# +# 1. ${#} is just $# (number of params) +# 1.a ${\#} is nothing at all (error: invalid expansion) +# 1.b ${\#...} (anything after) is the same (invalid) +# 2. ${#VAR} is the length of the value VAR +# 2.a Including ${##} - the length of ${#} +# 3 ${VAR#pat} is the value of VAR with leading pat removed +# 3.a Including ${VAR#} which just removes leading nothing +# This is relevant in case of ${VAR#${X}} with X='' +# nb: not required by posix, see XCU 2.6.2 +# 3.b ${##} is not a case of 3.a but rather 2.a +# 3.c Yet ${##pat} is a case of 3.a +# Including ${##${X}} where X='' or X='#' +# nb: not required by posix, see XCU 2.6.2 +# 3.d And ${#\#} is invalid (error) +# 3.e But ${##\#} removes a leading # from the value of $# +# (so is just $# as there is no leading # there) +# nb: not required by posix, see XCU 2.6.2 +# 4 ${VAR##pat} is the value of VAR with longest pat removed +# 4.a Including ${VAR##} which removes the longest nothing +# 4.b Which in this case includes ${###} (so is == $#) +# nb: not required by posix, see XCU 2.6.2 +# 4.c But not ${##\#} which is $# with a leading '#' removed +# (and so is also == $#), i.e.: like ${###} but different. +# nb: not required by posix, see XCU 2.6.2 +# 4.d As is ${###\#} or just ${####} - remove # (so just $#) +# nb: not required by posix, see XCU 2.6.2 +# + + reset dollar_hash + + check 'set -- ; echo $#' '0' 0 # 1 + check 'set -- a b c; echo $#' '3' 0 # 2 + check 'set -- a b c d e f g h i j; echo $#' '10' 0 # 3 +# rule 1 + check 'set -- ; echo ${#}' '0' 0 # 4 + check 'set -- a b c; echo ${#}' '3' 0 # 5 + check 'set -- a b c d e f g h i j; echo ${#}' '10' 0 # 6 +# rule 1.a + check 'set -- a b c; echo ${\#}' '' 2 # 7 +# rule 1.b + check 'set -- a b c; echo ${\#:-foo}' '' 2 # 8 +# rule 2 + check 'VAR=12345; echo ${#VAR}' '5' 0 # 9 + check 'VAR=123456789012; echo ${#VAR}' '12' 0 #10 +# rule 2.a + check 'set -- ; echo ${##}' '1' 0 #11 + check 'set -- a b c; echo ${##}' '1' 0 #12 + check 'set -- a b c d e f g h i j; echo ${##}' '2' 0 #13 +# rule 3 + check 'VAR=12345; echo ${VAR#1}' '2345' 0 #14 + check 'VAR=12345; echo ${VAR#2}' '12345' 0 #15 + check 'VAR=#2345; echo ${VAR#\#}' '2345' 0 #16 + check 'X=1; VAR=12345; echo ${VAR#${X}}' '2345' 0 #17 + check 'X=1; VAR=#2345; echo ${VAR#${X}}' '#2345' 0 #18 +# rule 3.a + check 'VAR=12345; echo ${VAR#}' '12345' 0 #19 + check 'X=; VAR=12345; echo ${VAR#${X}}' '12345' 0 #20 +# rule 3.b (tested above, rule 2.a) +# rule 3.c + check 'set -- ; echo ${##0}' '' 0 #21 + check 'set -- a b c; echo ${##1}' '3' 0 #22 + check 'set -- a b c d e f g h i j; echo ${##1}' '0' 0 #23 + check 'X=0; set -- ; echo ${##${X}}' '' 0 #24 + check 'X=; set -- ; echo ${##${X}}' '0' 0 #25 + check 'X=1; set -- a b c; echo ${##${X}}' '3' 0 #26 + check 'X=1; set -- a b c d e f g h i j; echo ${##${X}}' '0' 0 #27 + check 'X=; set -- a b c d e f g h i j; echo ${##${X}}' '10' 0 #28 + check 'X=#; VAR=#2345; echo ${VAR#${X}}' '2345' 0 #29 + check 'X=#; VAR=12345; echo ${VAR#${X}}' '12345' 0 #30 +# rule 3.d + check 'set -- a b c; echo ${#\#}' '' 2 #31 +# rule 3.e + check 'set -- ; echo ${##\#}' '0' 0 #32 + check 'set -- a b c d e f g h i j; echo ${##\#}' '10' 0 #33 + +# rule 4 + check 'VAR=12345; echo ${VAR##1}' '2345' 0 #34 + check 'VAR=12345; echo ${VAR##\1}' '2345' 0 #35 +# rule 4.a + check 'VAR=12345; echo ${VAR##}' '12345' 0 #36 +# rule 4.b + check 'set -- ; echo ${###}' '0' 0 #37 + check 'set -- a b c d e f g h i j; echo ${###}' '10' 0 #38 +# rule 4.c + check 'VAR=12345; echo ${VAR#\#}' '12345' 0 #39 + check 'VAR=12345; echo ${VAR#\#1}' '12345' 0 #40 + check 'VAR=#2345; echo ${VAR#\#}' '2345' 0 #41 + check 'VAR=#12345; echo ${VAR#\#1}' '2345' 0 #42 + check 'VAR=#2345; echo ${VAR#\#1}' '#2345' 0 #43 + check 'set -- ; echo ${####}' '0' 0 #44 + check 'set -- ; echo ${###\#}' '0' 0 #45 + check 'set -- a b c d e f g h i j; echo ${####}' '10' 0 #46 + check 'set -- a b c d e f g h i j; echo ${###\#}' '10' 0 #47 + +# now check for some more utter nonsense, not mentioned in the rules +# above (doesn't need to be) + + check 'x=hello; set -- a b c; echo ${#x:-1}' '' 2 #48 + check 'x=hello; set -- a b c; echo ${#x-1}' '' 2 #49 + check 'x=hello; set -- a b c; echo ${#x:+1}' '' 2 #50 + check 'x=hello; set -- a b c; echo ${#x+1}' '' 2 #51 + check 'x=hello; set -- a b c; echo ${#x+1}' '' 2 #52 + check 'x=hello; set -- a b c; echo ${#x:?msg}' '' 2 #53 + check 'x=hello; set -- a b c; echo ${#x?msg}' '' 2 #54 + check 'x=hello; set -- a b c; echo ${#x:=val}' '' 2 #55 + check 'x=hello; set -- a b c; echo ${#x=val}' '' 2 #56 + check 'x=hello; set -- a b c; echo ${#x#h}' '' 2 #57 + check 'x=hello; set -- a b c; echo ${#x#*l}' '' 2 #58 + check 'x=hello; set -- a b c; echo ${#x##*l}' '' 2 #59 + check 'x=hello; set -- a b c; echo ${#x%o}' '' 2 #60 + check 'x=hello; set -- a b c; echo ${#x%l*}' '' 2 #61 + check 'x=hello; set -- a b c; echo ${#x%%l*}' '' 2 #62 + +# but just to be complete, these ones should work + + check 'x=hello; set -- a b c; echo ${#%5}' '3' 0 #63 + check 'x=hello; set -- a b c; echo ${#%3}' '' 0 #64 + check 'x=hello; set -- a b c; echo ${#%?}' '' 0 #65 + check 'X=#; set -- a b c; echo ${#%${X}}' '3' 0 #66 + check 'X=3; set -- a b c; echo ${#%${X}}' '' 0 #67 + check 'set -- a b c; echo ${#%%5}' '3' 0 #68 + check 'set -- a b c; echo ${#%%3}' '' 0 #69 + check 'set -- a b c d e f g h i j k l; echo ${#%1}' '12' 0 #70 + check 'set -- a b c d e f g h i j k l; echo ${#%2}' '1' 0 #71 + check 'set -- a b c d e f g h i j k l; echo ${#%?}' '1' 0 #72 + check 'set -- a b c d e f g h i j k l; echo ${#%[012]}' '1' 0 #73 + check 'set -- a b c d e f g h i j k l; echo ${#%[0-4]}' '1' 0 #74 + check 'set -- a b c d e f g h i j k l; echo ${#%?2}' '' 0 #75 + check 'set -- a b c d e f g h i j k l; echo ${#%1*}' '' 0 #76 + check 'set -- a b c d e f g h i j k l; echo ${#%%2}' '1' 0 #77 + check 'set -- a b c d e f g h i j k l; echo ${#%%1*}' '' 0 #78 + +# and this lot are stupid, as $# is never unset or null, but they do work... + + check 'set -- a b c; echo ${#:-99}' '3' 0 #79 + check 'set -- a b c; echo ${#-99}' '3' 0 #80 + check 'set -- a b c; echo ${#:+99}' '99' 0 #81 + check 'set -- a b c; echo ${#+99}' '99' 0 #82 + check 'set -- a b c; echo ${#:?bogus}' '3' 0 #83 + check 'set -- a b c; echo ${#?bogus}' '3' 0 #84 + +# even this utter nonsense is OK, as while special params cannot be +# set this way, here, as $# is not unset, or null, the assignment +# never happens (isn't even attempted) + + check 'set -- a b c; echo ${#:=bogus}' '3' 0 #85 + check 'set -- a b c; echo ${#=bogus}' '3' 0 #86 + + for n in 0 1 10 25 100 #87 #88 #89 #90 #91 + do + check "(exit $n)"'; echo ${#?}' "${#n}" 0 + done + + results # results so far anyway... + +# now we have some harder to verify cases, as they (must) check unknown values +# and hence the resuls cannot just be built into the script, but we have +# to use some tricks to validate them, so for these, just do regular testing +# and don't attempt to use the check helper function, nor to include +# these tests in the result summary. If anything has already failed, we +# do not get this far... From here on, first failure ends this test. + + for opts in '' '-a' '-au' '-auf' '-aufe' # options safe enough to set + do + # Note the shell might have other (or these) opts set already + + RES=$(${TEST_SH} -c "test -n '${opts}' && set ${opts}; + printf '%s' \"\$-\";printf ' %s\\n' \"\${#-}\"") || + atf_fail '${#-} test exited with status '"$?" + LEN="${RES##* }" + DMINUS="${RES% ${LEN}}" + if [ "${#DMINUS}" != "${LEN}" ] + then + atf_fail \ + '${#-} test'" produced ${LEN} for opts ${DMINUS} (${RES})" + fi + done + + for seq in a b c d e f g h i j + do + # now we are tryin to generate different pids for $$ and $! + # so we just run the test a number of times, and hope... + # On NetBSD pid randomisation will usually help the tests + + eval "$(${TEST_SH} -c \ + '(exit 0)& BG=$! LBG=${#!}; + printf "SH=%s BG=%s LSH=%s LBG=%s" "$$" "$BG" "${#$}" "$LBG"; + wait')" + + if [ "${#SH}" != "${LSH}" ] || [ "${#BG}" != "${LBG}" ] + then + atf_fail \ + '${#!] of '"${BG} was ${LBG}, expected ${#BG}"'; ${#$} of '"${SH} was ${LSH}, expected ${#SH}" + fi + done +} + +atf_test_case dollar_star +dollar_star_head() { + atf_set descr 'Test expansion of various aspects of $*' +} +dollar_star_body() { + + reset dollar_star + + check 'set -- a b c; echo $# $*' '3 a b c' 0 # 1 + check 'set -- a b c; echo $# "$*"' '3 a b c' 0 # 2 + check 'set -- a "b c"; echo $# $*' '2 a b c' 0 # 3 + check 'set -- a "b c"; echo $# "$*"' '2 a b c' 0 # 4 + check 'set -- a b c; set -- $* ; echo $# $*' '3 a b c' 0 # 5 + check 'set -- a b c; set -- "$*" ; echo $# $*' '1 a b c' 0 # 6 + check 'set -- a "b c"; set -- $* ; echo $# $*' '3 a b c' 0 # 7 + check 'set -- a "b c"; set -- "$*" ; echo $# $*' \ + '1 a b c' 0 # 8 + + check 'IFS=". "; set -- a b c; echo $# $*' '3 a b c' 0 # 9 + check 'IFS=". "; set -- a b c; echo $# "$*"' '3 a.b.c' 0 #10 + check 'IFS=". "; set -- a "b c"; echo $# $*' '2 a b c' 0 #11 + check 'IFS=". "; set -- a "b c"; echo $# "$*"' '2 a.b c' 0 #12 + check 'IFS=". "; set -- a "b.c"; echo $# $*' '2 a b c' 0 #13 + check 'IFS=". "; set -- a "b.c"; echo $# "$*"' '2 a.b.c' 0 #14 + check 'IFS=". "; set -- a b c; set -- $* ; echo $# $*' \ + '3 a b c' 0 #15 + check 'IFS=". "; set -- a b c; set -- "$*" ; echo $# $*' \ + '1 a b c' 0 #16 + check 'IFS=". "; set -- a "b c"; set -- $* ; echo $# $*' \ + '3 a b c' 0 #17 + check 'IFS=". "; set -- a "b c"; set -- "$*" ; echo $# $*' \ + '1 a b c' 0 #18 + check 'IFS=". "; set -- a b c; set -- $* ; echo $# "$*"' \ + '3 a.b.c' 0 #19 + check 'IFS=". "; set -- a b c; set -- "$*" ; echo $# "$*"' \ + '1 a.b.c' 0 #20 + check 'IFS=". "; set -- a "b c"; set -- $* ; echo $# "$*"' \ + '3 a.b.c' 0 #21 + check 'IFS=". "; set -- a "b c"; set -- "$*" ; echo $# "$*"' \ + '1 a.b c' 0 #22 + + results +} + +atf_test_case dollar_star_in_word +dollar_star_in_word_head() { + atf_set descr 'Test expansion $* occurring in word of ${var:-word}' +} +dollar_star_in_word_body() { + + reset dollar_star_in_word + + unset xXx ; # just in case! + + # Note that the expected results for these tests are identical + # to those from the dollar_star test. It should never make + # a difference whether we expand $* or ${unset:-$*} + + # (note expanding ${unset:-"$*"} is different, that is not tested here) + + check 'set -- a b c; echo $# ${xXx:-$*}' '3 a b c' 0 # 1 + check 'set -- a b c; echo $# "${xXx:-$*}"' '3 a b c' 0 # 2 + check 'set -- a "b c"; echo $# ${xXx:-$*}' '2 a b c' 0 # 3 + check 'set -- a "b c"; echo $# "${xXx:-$*}"' '2 a b c' 0 # 4 + check 'set -- a b c; set -- ${xXx:-$*} ; echo $# $*' '3 a b c' 0 # 5 + check 'set -- a b c; set -- "${xXx:-$*}" ; echo $# $*' '1 a b c' 0 # 6 + check 'set -- a "b c"; set -- ${xXx:-$*} ; echo $# $*' '3 a b c' 0 # 7 + check 'set -- a "b c"; set -- "${xXx:-$*}" ; echo $# $*' \ + '1 a b c' 0 # 8 + + check 'IFS=". "; set -- a b c; echo $# ${xXx:-$*}' '3 a b c' 0 # 9 + check 'IFS=". "; set -- a b c; echo $# "${xXx:-$*}"' '3 a.b.c' 0 #10 + check 'IFS=". "; set -- a "b c"; echo $# ${xXx:-$*}' '2 a b c' 0 #11 + check 'IFS=". "; set -- a "b c"; echo $# "${xXx:-$*}"' '2 a.b c' 0 #12 + check 'IFS=". "; set -- a "b.c"; echo $# ${xXx:-$*}' '2 a b c' 0 #13 + check 'IFS=". "; set -- a "b.c"; echo $# "${xXx:-$*}"' '2 a.b.c' 0 #14 + check 'IFS=". ";set -- a b c;set -- ${xXx:-$*};echo $# ${xXx:-$*}' \ + '3 a b c' 0 #15 + check 'IFS=". ";set -- a b c;set -- "${xXx:-$*}";echo $# ${xXx:-$*}' \ + '1 a b c' 0 #16 + check 'IFS=". ";set -- a "b c";set -- ${xXx:-$*};echo $# ${xXx:-$*}' \ + '3 a b c' 0 #17 + check 'IFS=". ";set -- a "b c";set -- "${xXx:-$*}";echo $# ${xXx:-$*}' \ + '1 a b c' 0 #18 + check 'IFS=". ";set -- a b c;set -- ${xXx:-$*};echo $# "${xXx:-$*}"' \ + '3 a.b.c' 0 #19 + check 'IFS=". ";set -- a b c;set -- "$*";echo $# "$*"' \ + '1 a.b.c' 0 #20 + check 'IFS=". ";set -- a "b c";set -- $*;echo $# "$*"' \ + '3 a.b.c' 0 #21 + check 'IFS=". ";set -- a "b c";set -- "$*";echo $# "$*"' \ + '1 a.b c' 0 #22 + + results +} + +atf_test_case dollar_star_with_empty_ifs +dollar_star_with_empty_ifs_head() { + atf_set descr 'Test expansion of $* with IFS=""' +} +dollar_star_with_empty_ifs_body() { + + reset dollar_star_with_empty_ifs + + check 'IFS=""; set -- a b c; echo $# $*' '3 a b c' 0 # 1 + check 'IFS=""; set -- a b c; echo $# "$*"' '3 abc' 0 # 2 + check 'IFS=""; set -- a "b c"; echo $# $*' '2 a b c' 0 # 3 + check 'IFS=""; set -- a "b c"; echo $# "$*"' '2 ab c' 0 # 4 + check 'IFS=""; set -- a "b.c"; echo $# $*' '2 a b.c' 0 # 5 + check 'IFS=""; set -- a "b.c"; echo $# "$*"' '2 ab.c' 0 # 6 + check 'IFS=""; set -- a b c; set -- $* ; echo $# $*' \ + '3 a b c' 0 # 7 + check 'IFS=""; set -- a b c; set -- "$*" ; echo $# $*' \ + '1 abc' 0 # 8 + check 'IFS=""; set -- a "b c"; set -- $* ; echo $# $*' \ + '2 a b c' 0 # 9 + check 'IFS=""; set -- a "b c"; set -- "$*" ; echo $# $*' \ + '1 ab c' 0 #10 + check 'IFS=""; set -- a b c; set -- $* ; echo $# "$*"' \ + '3 abc' 0 #11 + check 'IFS=""; set -- a b c; set -- "$*" ; echo $# "$*"' \ + '1 abc' 0 #12 + check 'IFS=""; set -- a "b c"; set -- $* ; echo $# "$*"' \ + '2 ab c' 0 #13 + check 'IFS=""; set -- a "b c"; set -- "$*" ; echo $# "$*"' \ + '1 ab c' 0 #14 + + results # FIXED: 'PR bin/52090 expect 7 of 14 subtests to fail' +} + +atf_test_case dollar_star_in_word_empty_ifs +dollar_star_in_word_empty_ifs_head() { + atf_set descr 'Test expansion of ${unset:-$*} with IFS=""' +} +dollar_star_in_word_empty_ifs_body() { + + reset dollar_star_in_word_empty_ifs + + unset xXx ; # just in case + + # Note that the expected results for these tests are identical + # to those from the dollar_star_with_empty_ifs test. It should + # never make a difference whether we expand $* or ${unset:-$*} + + # (note expanding ${unset:-"$*"} is different, that is not tested here) + + check 'IFS="";set -- a b c;echo $# ${xXx:-$*}' '3 a b c' 0 # 1 + check 'IFS="";set -- a b c;echo $# "${xXx:-$*}"' '3 abc' 0 # 2 + check 'IFS="";set -- a "b c";echo $# ${xXx:-$*}' '2 a b c' 0 # 3 + check 'IFS="";set -- a "b c";echo $# "${xXx:-$*}"' '2 ab c' 0 # 4 + check 'IFS="";set -- a "b.c";echo $# ${xXx:-$*}' '2 a b.c' 0 # 5 + check 'IFS="";set -- a "b.c";echo $# "${xXx:-$*}"' '2 ab.c' 0 # 6 + check 'IFS="";set -- a b c;set -- ${xXx:-$*};echo $# ${xXx:-$*}' \ + '3 a b c' 0 # 7 + check 'IFS="";set -- a b c;set -- "${xXx:-$*}";echo $# ${xXx:-$*}' \ + '1 abc' 0 # 8 + check 'IFS="";set -- a "b c";set -- ${xXx:-$*};echo $# ${xXx:-$*}' \ + '2 a b c' 0 # 9 + check 'IFS="";set -- a "b c";set -- "${xXx:-$*}";echo $# ${xXx:-$*}' \ + '1 ab c' 0 #10 + check 'IFS="";set -- a b c;set -- ${xXx:-$*};echo $# "${xXx:-$*}"' \ + '3 abc' 0 #11 + check 'IFS="";set -- a b c;set -- "${xXx:-$*}";echo $# "${xXx:-$*}"' \ + '1 abc' 0 #12 + check 'IFS="";set -- a "b c";set -- ${xXx:-$*};echo $# "${xXx:-$*}"' \ + '2 ab c' 0 #13 + check 'IFS="";set -- a "b c";set -- "${xXx:-$*}";echo $# "${xXx:-$*}"' \ + '1 ab c' 0 #14 + + results # FIXED: 'PR bin/52090 expect 7 of 14 subtests to fail' +} + +atf_test_case dollar_star_in_quoted_word +dollar_star_in_quoted_word_head() { + atf_set descr 'Test expansion $* occurring in word of ${var:-"word"}' +} +dollar_star_in_quoted_word_body() { + + reset dollar_star_in_quoted_word + + unset xXx ; # just in case! + + check 'set -- a b c; echo $# ${xXx:-"$*"}' '3 a b c' 0 # 1 + check 'set -- a "b c"; echo $# ${xXx:-"$*"}' '2 a b c' 0 # 2 + check 'set -- a b c; set -- ${xXx:-"$*"} ; echo $# ${xXx-"$*"}' \ + '1 a b c' 0 # 3 + check 'set -- a "b c"; set -- ${xXx:-"$*"} ; echo $# ${xXx-"$*"}' \ + '1 a b c' 0 # 4 + check 'set -- a b c; set -- ${xXx:-"$*"} ; echo $# ${xXx-"$*"}' \ + '1 a b c' 0 # 5 + check 'set -- a "b c"; set -- ${xXx:-"$*"} ; echo $# ${xXx-$*}' \ + '1 a b c' 0 # 6 + check 'set -- a b c; set -- ${xXx:-$*} ; echo $# ${xXx-"$*"}' \ + '3 a b c' 0 # 7 + check 'set -- a "b c"; set -- ${xXx:-$*} ; echo $# ${xXx-"$*"}' \ + '3 a b c' 0 # 8 + + check 'IFS=". "; set -- a b c; echo $# ${xXx:-"$*"}' '3 a.b.c' 0 # 9 + check 'IFS=". "; set -- a "b c"; echo $# ${xXx:-"$*"}' '2 a.b c' 0 #10 + check 'IFS=". "; set -- a "b.c"; echo $# ${xXx:-"$*"}' '2 a.b.c' 0 #11 + check 'IFS=". ";set -- a b c;set -- ${xXx:-"$*"};echo $# ${xXx:-"$*"}' \ + '1 a.b.c' 0 #12 + check 'IFS=". ";set -- a "b c";set -- ${xXx:-"$*"};echo $# ${xXx:-"$*"}' \ + '1 a.b c' 0 #13 + check 'IFS=". ";set -- a b c;set -- ${xXx:-$*};echo $# ${xXx:-"$*"}' \ + '3 a.b.c' 0 #14 + check 'IFS=". ";set -- a "b c";set -- ${xXx:-$*};echo $# ${xXx:-"$*"}' \ + '3 a.b.c' 0 #15 + check 'IFS=". ";set -- a b c;set -- ${xXx:-"$*"};echo $# ${xXx:-$*}' \ + '1 a b c' 0 #16 + check 'IFS=". ";set -- a "b c";set -- ${xXx:-"$*"};echo $# ${xXx:-$*}' \ + '1 a b c' 0 #17 + + check 'IFS="";set -- a b c;echo $# ${xXx:-"$*"}' '3 abc' 0 #18 + check 'IFS="";set -- a "b c";echo $# ${xXx:-"$*"}' '2 ab c' 0 #19 + check 'IFS="";set -- a "b.c";echo $# ${xXx:-"$*"}' '2 ab.c' 0 #20 + check 'IFS="";set -- a b c;set -- ${xXx:-"$*"};echo $# ${xXx:-"$*"}' \ + '1 abc' 0 #21 + check 'IFS="";set -- a "b c";set -- ${xXx:-"$*"};echo $# ${xXx:-"$*"}' \ + '1 ab c' 0 #22 + check 'IFS="";set -- a b c;set -- ${xXx:-$*};echo $# ${xXx:-"$*"}' \ + '3 abc' 0 #23 + check 'IFS="";set -- a "b c";set -- ${xXx:-$*};echo $# ${xXx:-"$*"}' \ + '2 ab c' 0 #24 + check 'IFS="";set -- a b c;set -- ${xXx:-"$*"};echo $# ${xXx:-$*}' \ + '1 abc' 0 #25 + check 'IFS="";set -- a "b c";set -- ${xXx:-"$*"};echo $# ${xXx:-$*}' \ + '1 ab c' 0 #26 + + results # FIXED: 'PR bin/52090 - 2 of 26 subtests expected to fail' +} + +atf_test_case dollar_at_in_field_split_context +dollar_at_in_field_split_context_head() { + atf_set descr 'Test "$@" wth field splitting -- PR bin/54112' +} +dollar_at_in_field_split_context_body() { + reset dollar_at_in_field_split_context + + # the simple case (no field split) which always worked + check 'set -- ""; set -- ${0+"$@"}; echo $#' 1 0 #1 + + # The original failure case from the bash-bug list + check 'set -- ""; set -- ${0+"$@" "$@"}; echo $#' 2 0 #2 + + # slightly simpler cases that triggered the same issue + check 'set -- ""; set -- ${0+"$@" }; echo $#' 1 0 #3 + check 'set -- ""; set -- ${0+ "$@"}; echo $#' 1 0 #4 + check 'set -- ""; set -- ${0+ "$@" }; echo $#' 1 0 #5 + + # and the bizarre + check 'set -- ""; set -- ${0+"$@" "$@" "$@"}; echo $#' 3 0 #6 + + # repeat tests when there is more than one set empty numeric param + + check 'set -- "" ""; set -- ${0+"$@"}; echo $#' 2 0 #7 + check 'set -- "" ""; set -- ${0+"$@" "$@"}; echo $#' 4 0 #8 + check 'set -- "" ""; set -- ${0+"$@" }; echo $#' 2 0 #9 + check 'set -- "" ""; set -- ${0+ "$@"}; echo $#' 2 0 #10 + check 'set -- "" ""; set -- ${0+ "$@" }; echo $#' 2 0 #11 + check 'set -- "" ""; set -- ${0+"$@" "$@" "$@"}; echo $#' \ + 6 0 #12 + + # Next some checks of the way the NetBSD shell + # interprets some expressions that are POSIX unspecified. + # Other shells might fail these tests, without that + # being a problem. We retain these tests so accidental + # changes in our behaviour can be detected. + + check 'set --; X=; set -- "$X$@"; echo $#' 0 0 #13 + check 'set --; X=; set -- "$@$X"; echo $#' 0 0 #14 + check 'set --; X=; set -- "$X$@$X"; echo $#' 0 0 #15 + check 'set --; X=; set -- "$@$@"; echo $#' 0 0 #16 + + check 'set -- ""; X=; set -- "$X$@"; echo $#' 1 0 #17 + check 'set -- ""; X=; set -- "$@$X"; echo $#' 1 0 #19 + check 'set -- ""; X=; set -- "$X$@$X"; echo $#' 1 0 #19 + check 'set -- ""; X=; set -- "$@$@"; echo $#' 1 0 #20 + + check 'set -- "" ""; X=; set -- "$X$@"; echo $#' 2 0 #21 + check 'set -- "" ""; X=; set -- "$@$X"; echo $#' 2 0 #22 + check 'set -- "" ""; X=; set -- "$X$@$X"; echo $#' 2 0 #23 + # Yes, this next one really is (and should be) 3... + check 'set -- "" ""; X=; set -- "$@$@"; echo $#' 3 0 #24 + + results +} + +atf_test_case embedded_nl +embedded_nl_head() { + atf_set descr 'Test literal \n in xxx string in ${var-xxx}' +} +embedded_nl_body() { + + atf_check -s exit:0 -o inline:'a\nb\n' -e empty ${TEST_SH} <<- 'EOF' + unset V + X="${V-a + b}" + printf '%s\n' "${X}" + EOF + + atf_check -s exit:0 -o inline:'a\nb\n' -e empty ${TEST_SH} <<- 'EOF' + unset V + X=${V-"a + b"} + printf '%s\n' "${X}" + EOF + + # This should not generate a syntax error, see PR bin/53201 + atf_check -s exit:0 -o inline:'abc\n' -e empty ${TEST_SH} <<- 'EOF' + V=abc + X=${V-a + b} + printf '%s\n' "${X}" + EOF + + # Nor should any of these... + atf_check -s exit:0 -o inline:'a\nb\n' -e empty ${TEST_SH} <<- 'EOF' + unset V + X=${V-a + b} + printf '%s\n' "${X}" + EOF + + atf_check -s exit:0 -o inline:'a\nb\n' -e empty ${TEST_SH} <<- 'EOF' + unset V + X=${V:=a + b} + printf '%s\n' "${X}" + EOF + + atf_check -s exit:0 -o inline:'xa\nby\na\nb\n' -e empty \ + ${TEST_SH} <<- 'EOF' + unset V + X=x${V:=a + b}y + printf '%s\n' "${X}" "${V}" + EOF +} + +check3() +{ + check "X=foo; ${1}" "$2" 0 + check "X=; ${1}" "$3" 0 + check "unset X; ${1}" "$4" 0 +} + +atf_test_case alternative +alternative_head() { + atf_set descr 'Test various possibilities for ${var+xxx}' +} +alternative_body() { + reset alternative + + # just to verify (validate) that the test method works as expected + # (this is currently the very first test performed in this test set) + check 'printf %s a b' ab 0 # 1 + + check3 'set -- ${X+bar}; echo "$#:$1"' 1:bar 1:bar 0: # 4 + check3 'set -- ${X+}; echo "$#:$1"' 0: 0: 0: # 7 + check3 'set -- ${X+""}; echo "$#:$1"' 1: 1: 0: # 10 + check3 'set -- "${X+}"; echo "$#:$1"' 1: 1: 1: # 13 + check3 'set -- "${X+bar}"; echo "$#:$1"' 1:bar 1:bar 1: # 16 + + check3 'set -- ${X+a b c}; echo "$#:$1"' 3:a 3:a 0: # 19 + check3 'set -- ${X+"a b c"}; echo "$#:$1"' '1:a b c' '1:a b c' 0: + check3 'set -- "${X+a b c}"; echo "$#:$1"' '1:a b c' '1:a b c' 1: + check3 'set -- ${X+a b\ c}; echo "$#:$1"' 2:a 2:a 0: # 28 + check3 'set -- ${X+"a b" c}; echo "$#:$1"' '2:a b' '2:a b' 0: + + check3 'printf %s "" ${X+}' '' '' '' # 34 + check3 'printf %s ""${X+bar}' bar bar '' # 37 + + check3 'Y=bar; printf %s ${X+x}${Y+y}' xy xy y # 40 + check3 'Y=bar; printf %s ""${X+${Y+z}}' z z '' # 43 + check3 'Y=; printf %s ""${X+${Y+z}}' z z '' # 46 + check3 'unset Y; printf %s ""${X+${Y+z}}' '' '' '' # 49 + check3 'Y=1; printf %s a ${X+"${Y+z}"}' az az a # 52 + + check3 'printf %s ${X+}x}' x} x} x} # 55 + check3 'printf %s ${X+}}' } } } # 58 + check3 'printf %s "" ${X+"}"x}' }x }x '' # 61 + check3 'printf %s "" ${X+\}x}' }x }x '' # 64 + check3 'printf %s "${X+\}x}"' }x }x '' # 67 + check3 'printf %s "${X+\}}"' } } '' # 70 + + check3 'set -- ${X:+bar}; echo "$#:$1"' 1:bar 0: 0: # 73 + check3 'set -- ${X:+}; echo "$#:$1"' 0: 0: 0: # 76 + check3 'set -- ${X:+""}; echo "$#:$1"' 1: 0: 0: # 79 + check3 'set -- "${X:+}"; echo "$#:$1"' 1: 1: 1: # 80 + check3 'set -- "${X:+bar}"; echo "$#:$1"' 1:bar 1: 1: # 83 + + check3 'set -- ${X:+a b c}; echo "$#:$1"' 3:a 0: 0: # 86 + check3 'set -- ${X:+"a b c"}; echo "$#:$1"' '1:a b c' 0: 0: # 89 + check3 'set -- "${X:+a b c}"; echo "$#:$1"' '1:a b c' 1: 1: # 92 + check3 'set -- ${X:+a b\ c}; echo "$#:$1"' 2:a 0: 0: # 95 + check3 'set -- ${X:+"a b" c}; echo "$#:$1"' '2:a b' 0: 0: # 98 + + check3 'printf %s "" ${X:+}' '' '' '' #101 + check3 'printf %s ""${X:+bar}' bar '' '' #104 + + check3 'Y=bar; printf %s ${X:+x}${Y:+y}' xy y y #107 + check3 'Y=bar; printf %s ""${X:+${Y:+z}}' z '' '' #110 + check3 'Y=; printf %s ""${X:+${Y+z}}' z '' '' #113 + check3 'Y=; printf %s ""${X:+${Y:+z}}' '' '' '' #116 + check3 'unset Y; printf %s ""${X:+${Y:+z}}' '' '' '' #119 + check3 'Y=1; printf %s a ${X:+"${Y:+z}"}' az a a #122 + + check3 'printf %s ${X:+}x}' x} x} x} #125 + check3 'printf %s ${X:+}}' } } } #128 + check3 'printf %s "" ${X:+"}"x}' }x '' '' #131 + check3 'printf %s "" ${X:+\}x}' }x '' '' #134 + check3 'printf %s "${X:+\}x}"' }x '' '' #137 + check3 'printf %s "${X:+\}}"' } '' '' #140 + + results +} + +atf_test_case default +default_head() { + atf_set descr 'Test various possibilities for ${var-xxx}' +} +default_body() { + reset default + + check3 'set -- ${X-bar}; echo "$#:$1"' 1:foo 0: 1:bar # 3 + check3 'set -- ${X-}; echo "$#:$1"' 1:foo 0: 0: # 6 + check3 'set -- ${X-""}; echo "$#:$1"' 1:foo 0: 1: # 9 + check3 'set -- "${X-}"; echo "$#:$1"' 1:foo 1: 1: # 12 + check3 'set -- "${X-bar}"; echo "$#:$1"' 1:foo 1: 1:bar # 15 + + check3 'set -- ${X-a b c}; echo "$#:$1"' 1:foo 0: 3:a # 18 + check3 'set -- ${X-"a b c"}; echo "$#:$1"' 1:foo 0: '1:a b c' #21 + check3 'set -- "${X-a b c}"; echo "$#:$1"' 1:foo 1: '1:a b c' #24 + check3 'set -- ${X-a b\ c}; echo "$#:$1"' 1:foo 0: 2:a # 27 + check3 'set -- ${X-"a b" c}; echo "$#:$1"' 1:foo 0: '2:a b' #30 + + check3 'printf %s "" ${X-}' foo '' '' # 33 + check3 'printf %s ""${X-bar}' foo '' bar # 36 + + check3 'Y=bar; printf %s ${X-x}${Y-y}' foobar bar xbar # 39 + check3 'Y=bar; printf %s ""${X-${Y-z}}' foo '' bar # 42 + check3 'Y=; printf %s ""${X-${Y-z}}' foo '' '' # 45 + check3 'unset Y; printf %s ""${X-${Y-z}}' foo '' z # 48 + check3 'Y=1; printf %s a ${X-"${Y-z}"}' afoo a a1 # 51 + + check3 'printf %s ${X-}x}' foox} x} x} # 54 + check3 'printf %s ${X-}}' foo} } } # 57 + check3 'printf %s ${X-{}}' foo} } {} # 60 + check3 'printf %s "" ${X-"}"x}' foo '' }x # 63 + check3 'printf %s "" ${X-\}x}' foo '' }x # 66 + check3 'printf %s "${X-\}x}"' foo '' }x # 69 + check3 'printf %s "${X-\}}"' foo '' } # 72 + + check3 'set -- ${X:-bar}; echo "$#:$1"' 1:foo 1:bar 1:bar #75 + check3 'set -- ${X:-}; echo "$#:$1"' 1:foo 0: 0: # 78 + check3 'set -- ${X:-""}; echo "$#:$1"' 1:foo 1: 1: # 81 + check3 'set -- "${X:-}"; echo "$#:$1"' 1:foo 1: 1: # 84 + check3 'set -- "${X:-bar}"; echo "$#:$1"' 1:foo 1:bar 1:bar #87 + + check3 'set -- ${X:-a b c}; echo "$#:$1"' 1:foo 3:a 3:a # 90 + check3 'set -- ${X:-"a b c"}; echo "$#:$1"' 1:foo '1:a b c' '1:a b c' + check3 'set -- "${X:-a b c}"; echo "$#:$1"' 1:foo '1:a b c' '1:a b c' + check3 'set -- ${X:-a b\ c}; echo "$#:$1"' 1:foo 2:a 2:a # 99 + check3 'set -- ${X:-"a b" c}; echo "$#:$1"' 1:foo '2:a b' '2:a b' + + check3 'printf %s "" ${X:-}' foo '' '' #105 + check3 'printf %s ""${X:-bar}' foo bar bar #108 + + check3 'Y=bar; printf %s ${X:-x}${Y:-y}' foobar xbar xbar #111 + check3 'Y=bar; printf %s ""${X:-${Y:-z}}' foo bar bar #114 + check3 'Y=; printf %s ""${X:-${Y-z}}' foo '' '' #117 + check3 'Y=; printf %s ""${X:-${Y:-z}}' foo z z #120 + check3 'unset Y; printf %s ""${X:-${Y:-z}}' foo z z #123 + check3 'Y=1; printf %s a ${X:-"${Y:-z}"}' afoo a1 a1 #126 + + check3 'printf %s ${X:-}x}' foox} x} x} #129 + check3 'printf %s ${X:-}}' foo} } } #132 + check3 'printf %s ${X:-{}}' foo} {} {} #135 + check3 'printf %s "" ${X:-"}"x}' foo }x }x #138 + check3 'printf %s "" ${X:-\}x}' foo }x }x #141 + check3 'printf %s "${X:-\}x}"' foo }x }x #144 + check3 'printf %s "${X:-\}}"' foo } } #147 + + results +} + +atf_test_case assign +assign_head() { + atf_set descr 'Test various possibilities for ${var=xxx}' +} +assign_body() { + reset assign + + check3 'set -- ${X=bar}; echo "$#:$1"' 1:foo 0: 1:bar # 3 + check3 'set -- ${X=}; echo "$#:$1"' 1:foo 0: 0: # 6 + check3 'set -- ${X=""}; echo "$#:$1"' 1:foo 0: 0: # 9 + check3 'set -- "${X=}"; echo "$#:$1"' 1:foo 1: 1: # 12 + check3 'set -- "${X=bar}"; echo "$#:$1"' 1:foo 1: 1:bar # 15 + + check3 'set -- ${X=a b c}; echo "$#:$1"' 1:foo 0: 3:a # 18 + check3 'set -- ${X="a b c"}; echo "$#:$1"' 1:foo 0: 3:a # 21 + check3 'set -- "${X=a b c}"; echo "$#:$1"' 1:foo 1: '1:a b c' #24 + check3 'set -- ${X=a b\ c}; echo "$#:$1"' 1:foo 0: 3:a # 27 + check3 'set -- ${X="a b" c}; echo "$#:$1"' 1:foo 0: 3:a # 30 + + check3 'printf %s "" ${X=}' foo '' '' # 33 + check3 'printf %s ""${X=bar}' foo '' bar # 36 + + check3 'Y=bar; printf %s ${X=x}${Y=y}' foobar bar xbar # 39 + check3 'Y=bar; printf %s ""${X=${Y=z}}' foo '' bar # 42 + check3 'Y=; printf %s ""${X=${Y=z}}' foo '' '' # 45 + check3 'unset Y; printf %s ""${X=${Y=z}}' foo '' z # 48 + check3 'Y=1; printf %s a ${X="${Y=z}"}' afoo a a1 # 51 + + check3 'printf %s ${X=}x}' foox} x} x} # 54 + check3 'printf %s ${X=}}' foo} } } # 57 + check3 'printf %s ${X={}}' foo} } {} # 60 + check3 'printf %s "" ${X="}"x}' foo '' }x # 63 + check3 'printf %s "" ${X=\}x}' foo '' }x # 66 + check3 'printf %s "${X=\}x}"' foo '' }x # 69 + check3 'printf %s "${X=\}}"' foo '' } # 72 + + check3 'set -- ${X=a b c}; echo "$#:$1:$X"' 1:foo:foo 0:: '3:a:a b c' + check3 'set -- ${X="a b c"}; echo "$#:$1:$X"' 1:foo:foo 0:: '3:a:a b c' + check3 'set -- "${X=a b c}"; echo "$#:$1:$X"' \ + 1:foo:foo 1:: '1:a b c:a b c' + check3 'set -- ${X=a b\ c}; echo "$#:$1:$X"' 1:foo:foo 0:: '3:a:a b c' + check3 'set -- ${X="a b" c}; echo "$#:$1:$X"' 1:foo:foo 0:: '3:a:a b c' + + check3 'printf %s ${X=}x}; printf :%s "${X-U}"' foox}:foo x}: x}: #90 + check3 'printf %s ${X=}}; printf :%s "${X-U}"' foo}:foo }: }: #93 + check3 'printf %s ${X={}}; printf :%s "${X-U}"' foo}:foo }: {}:{ #96 + + check3 'set -- ${X:=bar}; echo "$#:$1"' 1:foo 1:bar 1:bar # 99 + check3 'set -- ${X:=}; echo "$#:$1"' 1:foo 0: 0: #102 + check3 'set -- ${X:=""}; echo "$#:$1"' 1:foo 0: 0: #105 + check3 'set -- "${X:=}"; echo "$#:$1"' 1:foo 1: 1: #108 + check3 'set -- "${X:=bar}"; echo "$#:$1"' 1:foo 1:bar 1:bar #111 + + check3 'set -- ${X:=a b c}; echo "$#:$1"' 1:foo 3:a 3:a #114 + check3 'set -- ${X:="a b c"}; echo "$#:$1"' 1:foo 3:a 3:a #117 + check3 'set -- "${X:=a b c}"; echo "$#:$1"' 1:foo '1:a b c' '1:a b c' + check3 'set -- ${X:=a b\ c}; echo "$#:$1"' 1:foo 3:a 3:a #123 + check3 'set -- ${X:="a b" c}; echo "$#:$1"' 1:foo 3:a 3:a #126 + + check3 'printf %s "" ${X:=}' foo '' '' #129 + check3 'printf %s ""${X:=bar}' foo bar bar #132 + + check3 'Y=bar; printf %s ${X:=x}${Y:=y}' foobar xbar xbar #135 + check3 'Y=bar; printf %s ""${X:=${Y:=z}}' foo bar bar #138 + check3 'Y=; printf %s ""${X:=${Y=z}}' foo '' '' #141 + check3 'Y=; printf %s ""${X:=${Y:=z}}' foo z z #144 + check3 'unset Y; printf %s ""${X:=${Y:=z}}' foo z z #147 + check3 'Y=1; printf %s a ${X:="${Y:=z}"}' afoo a1 a1 #150 + + check3 'printf %s ${X:=}x}' foox} x} x} #153 + check3 'printf %s ${X:=}}' foo} } } #156 + check3 'printf %s ${X:={}}' foo} {} {} #159 + check3 'printf %s "" ${X:="}"x}' foo }x }x #162 + check3 'printf %s "" ${X:=\}x}' foo }x }x #165 + check3 'printf %s "${X:=\}x}"' foo }x }x #168 + check3 'printf %s "${X:=\}}"' foo } } #171 + + check3 'set -- ${X:=a b c}; echo "$#:$1:$X"' \ + 1:foo:foo '3:a:a b c' '3:a:a b c' #174 + check3 'set -- ${X:="a b c"}; echo "$#:$1:$X"' \ + 1:foo:foo '3:a:a b c' '3:a:a b c' #177 + check3 'set -- "${X:=a b c}"; echo "$#:$1:$X"' \ + 1:foo:foo '1:a b c:a b c' '1:a b c:a b c' #180 + check3 'set -- ${X:=a b\ c}; echo "$#:$1:$X"' \ + 1:foo:foo '3:a:a b c' '3:a:a b c' #183 + check3 'set -- ${X:="a b" c}; echo "$#:$1:$X"' \ + 1:foo:foo '3:a:a b c' '3:a:a b c' #186 + + check3 'printf %s ${X:=}x}; printf :%s "${X-U}"' foox}:foo x}: x}: + check3 'printf %s ${X:=}}; printf :%s "${X-U}"' foo}:foo }: }: + check3 'printf %s ${X:=\}}; printf :%s "${X-U}"' foo:foo }:} }:} + check3 'printf %s ${X:={}}; printf :%s "${X-U}"' foo}:foo {}:{ {}:{ + #198 + + results +} + +atf_test_case error +error_head() { + atf_set descr 'Test various possibilities for ${var?xxx}' +} +error_body() { + reset error + + check 'X=foo; printf %s ${X?X is not set}' foo 0 #1 + check 'X=; printf %s ${X?X is not set}' '' 0 #2 + check 'unset X; printf %s ${X?X is not set}' '' 2 #3 + + check 'X=foo; printf %s ${X?}' foo 0 #4 + check 'X=; printf %s ${X?}' '' 0 #5 + check 'unset X; printf %s ${X?}' '' 2 #6 + + check 'X=foo; printf %s ${X:?X is not set}' foo 0 #7 + check 'X=; printf %s ${X:?X is not set}' '' 2 #8 + check 'unset X; printf %s ${X:?X is not set}' '' 2 #9 + + check 'X=foo; printf %s ${X:?}' foo 0 #10 + check 'X=; printf %s ${X:?}' '' 2 #11 + check 'unset X; printf %s ${X:?}' '' 2 #12 + results } atf_init_test_cases() { + # Listed here in the order ATF runs them, not the order from above + + atf_add_test_case alternative + atf_add_test_case arithmetic + atf_add_test_case assign + atf_add_test_case default atf_add_test_case dollar_at + atf_add_test_case dollar_at_empty_and_conditional + atf_add_test_case dollar_at_in_field_split_context + atf_add_test_case dollar_at_unquoted_or_conditional atf_add_test_case dollar_at_with_text - atf_add_test_case strip - atf_add_test_case varpattern_backslashes - atf_add_test_case arithmetic + atf_add_test_case dollar_hash + atf_add_test_case dollar_star + atf_add_test_case dollar_star_in_quoted_word + atf_add_test_case dollar_star_in_word + atf_add_test_case dollar_star_in_word_empty_ifs + atf_add_test_case dollar_star_with_empty_ifs + atf_add_test_case embedded_nl + atf_add_test_case error atf_add_test_case iteration_on_null_parameter atf_add_test_case iteration_on_quoted_null_parameter atf_add_test_case iteration_on_null_or_null_parameter atf_add_test_case iteration_on_null_or_missing_parameter atf_add_test_case shell_params + atf_add_test_case strip + atf_add_test_case tilde + atf_add_test_case wrap_strip + atf_add_test_case var_with_embedded_cmdsub + atf_add_test_case varpattern_backslashes } diff --git a/bin/sh/t_fsplit.sh b/bin/sh/t_fsplit.sh old mode 100755 new mode 100644 --- a/bin/sh/t_fsplit.sh +++ b/bin/sh/t_fsplit.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_fsplit.sh,v 1.4 2016/03/27 14:50:01 christos Exp $ +# $NetBSD: t_fsplit.sh,v 1.7 2017/06/24 11:06:17 kre Exp $ # # Copyright (c) 2007-2016 The NetBSD Foundation, Inc. # All rights reserved. @@ -33,7 +33,7 @@ # the "${x-" and "}" were absent from the input line. # # So: sh -c 'set ${x-a b c}; echo $#' should give 3. -# and: sh -c 'set -- ${x-}' echo $#' shold give 0 +# and: sh -c 'set -- ${x-}' echo $#' should give 0 # # the implementation of "sh" to test @@ -79,6 +79,8 @@ then atf_fail "TEST ${TEST} '$1' failed ($STATUS)" fi + + return 0 } atf_test_case for @@ -137,11 +139,20 @@ check 'for i in ${x-a ${x-b c} d}; do echo "z${i}z"; done' \ 'zaz zbz zcz zdz' - # I am not sure these two are correct, the rules on quoting word - # in ${var-word} are peculiar, and hard to fathom... - # They are what the NetBSD shell does, and bash, not the freebsd shell - # (as of Mar 1, 2016) + # I am not sure the first of these two is correct, the rules on + # quoting word in ${var-word} are peculiar, and hard to fathom... + # It is what the NetBSD shell does, and bash, not the freebsd shell + # and not ksh93 (as of Mar 1, 2016, and still in June 2017) + # The likely correct interp of the next one is 'za bz zcz zdz' + + # That and the "should be" below are correct as of POSIX 7 TC2 + # But this is going to change to "unspecified" in POSIX 8 + # (resolution of bug 221) so instead of being incorrect (as now) + # the NetBSD shell will simply be implementing is version + # of unspecified behaviour. Just beware that shells differ, + # a shell that fails this test is not incorrect because of it. + # should be: uuuu qqqqqq uuu q uuu (unquoted/quoted) no nesting. check 'for i in ${x-"a ${x-"b c"}" d}; do echo "z${i}z"; done' \ 'za b cz zdz' check 'for i in ${x-a ${x-"b c"} d}; do echo "z${i}z"; done' \ @@ -207,8 +218,13 @@ 'za bz zcz' check 'x=BOGUS; for i in ${x+"a ${x+b c}" d}; do echo "z${i}z"; done'\ 'za b cz zdz' + + # see the (extended) comment in the default_val test. This will be + # unspecified, hence we are OK (will be) but expect differences. + # also incorrect: uuuu qqqqqq uuu q uuu check 'x=BOGUS; for i in ${x+"a ${x+"b c"}" d}; do echo "z${i}z"; done'\ 'za b cz zdz' + check 'x=BOGUS; for i in ${x+a ${x+"b c"} d}; do echo "z${i}z"; done'\ 'zaz zb cz zdz' check 'x=BOGUS; for i in ${x+a ${x+b c} d}; do echo "z${i}z"; done'\ @@ -232,8 +248,13 @@ 'zaqbz zcz' check 'IFS=q; for i in ${x-"aq${x-bqc}"qd}; do echo "z${i}z"; done' \ 'zaqbqcz zdz' + + # this is another almost certainly incorrect expectation + # (but again, see comment in default_val test - becoming unspecified.) + # uu qqqqqq uuu q uu (quoted/unquoted) check 'IFS=q; for i in ${x-"aq${x-"bqc"}"qd}; do echo "z${i}z"; done' \ 'zaqbqcz zdz' + check 'IFS=q; for i in ${x-aq${x-"bqc"}qd}; do echo "z${i}z"; done' \ 'zaz zbqcz zdz' } @@ -249,7 +270,12 @@ TEST=0 # Some quote propagation checks check 'set "${x-a b c}"; echo $#' 1 + + # this is another almost certainly incorrect expectation + # (but again, see comment in default_val test - becoming unspecified.) + # qqqq uuu qqq (quoted/unquoted) $1 is a $# is 2 check 'set "${x-"a b" c}"; echo $1' 'a b c' + check 'for i in "${x-a b c}"; do echo "z${i}z"; done' 'za b cz' } @@ -337,6 +363,7 @@ long=12345678123456781234567812345678 long=$long$long$long$long export long + unset x # first test that the test method works... check 'set -u; : ${long}; echo ${#long}' '128' @@ -344,8 +371,72 @@ # Check that we apply IFS to ${#var} check 'echo ${#long}; IFS=2; echo ${#long}; set 1 ${#long};echo $#' \ '128 1 8 3' - check 'IFS=2; set ${x-${#long}}; IFS=" "; echo $* $#' '1 8 2' - check 'IFS=2; set ${x-"${#long}"}; IFS=" "; echo $* $#' '128 1' + check 'IFS=2; set ${x-${#long}}; IFS=" "; echo $* $#' '1 8 2' + check 'IFS=2; set ${x-"${#long}"}; IFS=" "; echo $* $#' '128 1' + check 'IFS=2; set "${x-${#long}}"; IFS=" "; echo $* $#' '128 1' + check 'IFS=2; set ${x-${#long}}; : ; echo $* $#' '1 8 ' + check 'IFS=2; set ${x-${#long}}; : ; echo $* "$#"' '1 8 2' + check 'IFS=2; set ${x-${#long}}; : ; echo "$*" "$#"' '128 2' + check 'IFS=2; set ${x-${#long}}; : ; echo "$@" "$#"' '1 8 2' +} + +atf_test_case split_arith +split_arith_head() { + atf_set "descr" "Checks that field splitting works when expanding" \ + "the results from arithmetic" +} +split_arith_body() { + TEST=0 + + # Check that we apply IFS to $(( expr )) + + # Note: we do not check the actual arithmetic operations here + # (there is a separate test just for that) so we just enter + # the "answer" inside $(( )) ... also makes it easier to visualise + + check 'IFS=5; echo $(( 123456789 ))' '1234 6789' + check 'IFS=5; echo "$(( 123456789 ))"' '123456789' + check 'IFS=37; echo $(( 123456789 ))' '12 456 89' + check 'IFS=37; echo "$(( 123456789 ))"' '123456789' + check 'IFS=159; echo $(( 123456789 ))' ' 234 678' + + check 'IFS=5; set -- $(( 123456789 )); echo $#: $1 $2 $3 $4' \ + '2: 1234 6789' + check 'IFS=5; set -- "$(( 123456789 ))"; echo $#: $1 $2 $3 $4' \ + '1: 1234 6789' # go ahead: explain it! + check 'IFS=5; set -- "$(( 123456789 ))"; echo "$#: $1 $2 $3 $4"' \ + '1: 123456789 ' # ah! + + check 'IFS=37; set -- $(( 123456789 )); echo $#: $1 $2 $3 $4' \ + ' : 12 456 89' # Tricky! + check 'IFS=5; set -- $(( 123456789 )); echo $#: $*' \ + '2: 1234 6789' + check 'IFS=47; set -- $(( 123456789 )); echo $#: $*' \ + '3: 123 56 89' + check 'IFS=5; set -- $(( 123456789 )); echo "$#: $*"' \ + '2: 123456789' + check 'IFS=37; set -- $(( 123456789 )); echo "$#: $*"' \ + '3: 123456389' # [sic] + check 'IFS=5; set -- $(( 123456789 )); echo $#: $@' \ + '2: 1234 6789' + check 'IFS=47; set -- $(( 123456789 )); echo $#: $@' \ + '3: 123 56 89' + check 'IFS=5; set -- $(( 123456789 )); echo "$#: $@"' \ + '2: 1234 6789' + check 'IFS=37; set -- $(( 123456789 )); echo "$#: $*"' \ + '3: 123456389' # [sic] + + check 'IFS=1; set -- $(( 1111 )); echo "$#:" $*' '4: ' + check 'IFS=" 1"; set -- $(( 1231231231 )); echo "$#: $*"' \ + '4: 23 23 23' + check 'IFS="1 "; set -- $(( 1231231231 )); echo "$#: $*"' \ + '4: 123123123' + + check 'IFS=5; echo 5$(( 123456789 ))5' '51234 67895' + check 'IFS=37; echo 73$(( 123456789 ))37' '7312 456 8937' + check 'IFS=159; echo 11$(( 123456789 ))95' '11 234 678 95' + check 'IFS="159 "; echo 11$(( 123456789 ))95' '11 234 678 95' + check 'IFS="159 "; echo 11$(( 11234567899 ))95' '11 234 678 95' } atf_init_test_cases() { @@ -357,4 +448,5 @@ atf_add_test_case dollar_at atf_add_test_case ifs atf_add_test_case var_length + atf_add_test_case split_arith } diff --git a/bin/sh/t_here.sh b/bin/sh/t_here.sh old mode 100755 new mode 100644 --- a/bin/sh/t_here.sh +++ b/bin/sh/t_here.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_here.sh,v 1.6 2016/03/31 16:21:52 christos Exp $ +# $NetBSD: t_here.sh,v 1.7 2019/01/22 14:31:53 kre Exp $ # # Copyright (c) 2007 The NetBSD Foundation, Inc. # All rights reserved. @@ -78,10 +78,13 @@ rm -f "${TEMP_FILE}" # Remove newlines (use local shell for this) - oifs="$IFS" - IFS="$nl" - result="$(echo $result)" - IFS="$oifs" + result="$( + IFS="$nl" + set -f + set -- $result + IFS=' ' + printf %s "$*" + )" if [ "$2" != "$result" ] then echo >&2 "[$TEST_NUM] Expected output '$2', received '$result'" @@ -473,6 +476,42 @@ EOF ' '5string1 line1?-line2string1 -line2 ""'\'\'' string1 66666' 0 + # check that \ only quotes the magic chars, otherwise is retained + check 'p=A; cat <<-EOF + ${p+\%$p\%} + ${p+%$p%} + EOF + ' '\%A\% %A%' 0 + + # and check that " is not magic, so \ does not quote it + check 'p=A; cat <<-EOF + ${p+\"$p\"} + ${p+"$p"} + EOF + ' '\"A\" "A"' 0 + + # except in a ${var%} word, base syntax reapplies, and + # there quotes are magic again + check 'p=ABCD; cat <<-EOF + ${p%B?D} + ${p%B\?D} + ${p%"BCD"} + "${p%??}" + ${p#"${p%??}"} + "${p#"${p%?"?"}"}" + EOF + ' 'A ABCD A "AB" CD ""' 0 + + check 'p=AB??; cat <<-EOF + ${p%B?D} + ${p%B\??} + ${p%"B??"} + "${p%??}" + ${p#"${p%??}"} + "${p#"${p%?"?"}"}" + EOF + ' 'AB?? A A "AB" ?? "??"' 0 + results } diff --git a/bin/sh/t_input.sh b/bin/sh/t_input.sh new file mode 100644 --- /dev/null +++ b/bin/sh/t_input.sh @@ -0,0 +1,178 @@ +# $NetBSD: t_input.sh,v 1.1 2021/02/16 09:46:24 kre Exp $ +# +# Copyright (c) 2021 The NetBSD Foundation, Inc. +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# +# the implementation of "sh" to test +: ${TEST_SH:="/bin/sh"} + +# This set of tests checks the low level shell (script) input +# systrem (reading script files, nested files, fines read while +# reading strings, ..) and correctly dropping nul bytes from file data + +# other shell input (the read builtin for example) is not covered here. + +atf_test_case nul_elimination + +nul_elimination_head() { + atf_set "descr" "verifies that \0 chars in input are properly ignored" +} + +nul_elimination_body() { + atf_require_prog printf + atf_require_prog stat + + # these really should always be present, but... + atf_require_prog dd + atf_require_prog cat + atf_require_prog rm + + # please do not make even trivial changes (like correcting spelling) + # to the following script without taking care to fix the following + # tests, even minor changes can defeat the purpose of the test + cat > helper.sh <<- EOF + # a line of commentary that does nothing + # another line of comments (these just make the script bigger) + printf A + eval "printf B" + if printf C + then + printf D + fi + for x in E F G H + do + printf "\$x" + done + printf \\ + I + printf '\n' + exit 0 + EOF + + # this first test simply verifies that the script works as + # expected, should it fail, it is not a problem with \0 chars + atf_check -s exit:0 -o 'inline:ABCDEFGHI\n' -e empty \ + ${TEST_SH} helper.sh + + size=$(stat -f %z helper.sh) + + # Now insert \0 chars at specifically chosen spots in script + for loc in 0 10 40 41 42 104 105 106 110 111 112 113 119 127 128 \ + 144 150 162 165 168 187 202 203 204 205 214 215 216 224 225 + do + # at each location try varying the number of \0's inserted + for N in 1 2 4 7 + do + OF="helper-${N}@${loc}.sh" + + test "${loc}" -gt 0 && + dd if=helper.sh bs=1 count="${loc}" \ + of="${OF}" >/dev/null 2>&1 + dd if=helper.sh bs=1 skip="${loc}" \ + oseek="$(( loc + N ))" \ + of="${OF}" >/dev/null 2>&1 + + test "$(stat -f %z "${OF}")" -eq "$(( size + N ))" || + atf_fail "${N}@${loc}: build failure" + + atf_check -s exit:0 -o 'inline:ABCDEFGHI\n' -e empty \ + ${TEST_SH} "${OF}" + + rm -f "${OF}" + done + + done + + # Next insert \0 chars at multiple chosen locations + # nb: offsets in each arg must be non-decreasing + for locs in '0 225' '0 224' '0 127 223' '0 10 15' '0 48 55' \ + '0 0 0 225 225 225' '0 0 127 128 224 224 225' \ + '104 106' '105 110' '113 119' '113 119 122' '113 127' \ + '129 130 131 132 133 136 139 140' '184 185 186 187' \ + '202 203 203 204 204 205 205 206 207' + do + set -- $locs + gaps=$# + + IFS=- + OF="helper-${*}.sh" + unset IFS + + loc=$1; shift + test "${loc}" -gt 0 && + dd if=helper.sh bs=1 count="${loc}" \ + of="${OF}" >/dev/null 2>&1 + G=1 + for N + do + count=$(( N - loc )) + if [ "${count}" -eq 0 ] + then + printf '\0' >> "${OF}" + else + dd if=helper.sh bs=1 skip="${loc}" \ + oseek="$(( loc + G ))" count="${count}" \ + of="${OF}" >/dev/null 2>&1 + fi + loc=$N + G=$(( G + 1 )) + done + + if [ "${loc}" -lt "${size}" ] + then + dd if=helper.sh bs=1 skip="${loc}" \ + oseek="$(( loc + G ))" of="${OF}" >/dev/null 2>&1 + else + printf '\0' >> "${OF}" + fi + + test "$(stat -f %z "${OF}")" -eq "$(( size + gaps ))" || + atf_fail "${locs}: build failure" + + atf_check -s exit:0 -o 'inline:ABCDEFGHI\n' -e empty \ + ${TEST_SH} "${OF}" + + rm -f "${OF}" + done + + # Now just insert \0's in random locations in the script, + # hoping that if the somewhat carefully selected insertions + # above fail to trigger a bug, then if any (bugs) remain, + # eventually Murphy will prevail, and one of these tests will catch it. + + test "${RANDOM}" = "${RANDOM}" && + atf_skip 'ATF shell does not support $RANDOM' + + # To be added later... + + return 0 +} + +atf_init_test_cases() { + atf_add_test_case nul_elimination + # atf_add_test_case file_recursion + # atf_add_test_case file_string_recursion + # atf_add_test_case file_recursion_nul + # atf_add_test_case file_string_recursion_nul +} diff --git a/bin/sh/t_option.sh b/bin/sh/t_option.sh old mode 100755 new mode 100644 --- a/bin/sh/t_option.sh +++ b/bin/sh/t_option.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_option.sh,v 1.3 2016/03/08 14:19:28 christos Exp $ +# $NetBSD: t_option.sh,v 1.7 2019/07/11 03:49:51 msaitoh Exp $ # # Copyright (c) 2016 The NetBSD Foundation, Inc. # All rights reserved. @@ -77,7 +77,7 @@ # if we do not do this, -x tracing splatters stderr # for some shells, -v does as well (is that correct?) case "${opt}" in - (*[xv]*) exec 2>/dev/null;; + (*[xXv]*) exec 2>/dev/null;; esac o="$-" @@ -258,7 +258,7 @@ atf_test_case set_n set_n_head() { - atf_set "descr" "Tests that 'set -n' supresses command execution " \ + atf_set "descr" "Tests that 'set -n' suppresses command execution " \ "and that it behaves as defined by the standard" } set_n_body() { @@ -356,6 +356,8 @@ set_u_body() { test_option_on_off u + unset ENV # make sure there is nothing there to cause problems + # first make sure it is OK to unset an unset variable atf_check -s exit:0 -o match:OK -e empty ${TEST_SH} -ce \ 'unset _UNSET_VARIABLE_; echo OK' @@ -496,6 +498,329 @@ 'set -x; for i in 111 222 333; do printf "%s" $i; done; echo; exit 0' } +atf_test_case set_X +set_X_head() { + atf_set "descr" "Tests that 'set -X' turns on command exec logging " \ + "and that it enables set -x and retains a single fd" +} +set_X_body() { + + # First we need to verify that $TEST_SH supports -X + test_optional_on_off X || + atf_skip "$TEST_SH does not support -X" + + # and that the -X it implements is the -X we expect + $TEST_SH -c 'exec 2>/dev/null; + set +x; set -X; + case "$-" in (*x*) exit 0;; esac; + exit 1' || + atf_skip "$TEST_SH supports -X but not 'the' -X" + + # Above has already tested that set -X => set -x + # Now test that set +X => set +x + # and that set -x and set +x do not affect -X + + atf_check -s exit:0 -o empty -e ignore ${TEST_SH} -c \ + 'set -x; set +X; case "$-" in (*x*) echo FAIL; exit 1;; esac' + + atf_check -s exit:0 -o empty -e ignore ${TEST_SH} -c \ + 'set -X; set +x; + case "$-" in (*x*) echo FAIL; exit 1;; esac + case "$-" in (*X*) exit 0;; esac; echo ERROR; exit 2' + + atf_check -s exit:0 -o empty -e ignore ${TEST_SH} -c \ + 'set -X; set +x; set -x; + case "$-" in (*x*X*|*X*x*) exit 0;; esac; echo ERROR; exit 2' + + atf_check -s exit:0 -o empty -e ignore ${TEST_SH} -c \ + 'set +X; set -x; + case "$-" in (*X*) echo FAIL; exit 1;; esac + case "$-" in (*x*) exit 0;; esac; echo ERROR; exit 2' + + atf_check -s exit:0 -o empty -e ignore ${TEST_SH} -c \ + 'set +X; set -x; set +x; + case "$-" in (*[xX]*) echo FAULT; exit 3;; esac' + + # The following just verify regular tracing using -X instead of -x + # These are the same tests as the -x test (set_x) performs. + + # check that cmd output appears after -X is enabled + atf_check -s exit:0 \ + -o match:OKOK -o not-match:echo -o not-match:printf \ + -e not-match:printf -e match:OK -e match:echo \ + ${TEST_SH} -ec 'printf "%s" OK; set -X; echo OK; exit 0' + + # and that it stops again afer -X is disabled + atf_check -s exit:0 \ + -o match:OKOK -o not-match:echo -o not-match:printf \ + -e match:printf -e match:OK -e not-match:echo \ + ${TEST_SH} -ec 'set -X; printf "%s" OK; set +X; echo OK; exit 0' + + # also check that PS4 is output correctly + atf_check -s exit:0 \ + -o match:OK -o not-match:echo \ + -e match:OK -e match:Run:echo \ + ${TEST_SH} -ec 'PS4=Run:; set -X; echo OK; exit 0' + + # end copies of -x tests ... + + # now check that we can move stderr around without affecting -X output + + atf_check -s exit:0 \ + -o match:OKOK -o not-match:echo -o not-match:printf \ + -e match:printf -e match:OK -e match:echo \ + ${TEST_SH} -ecX 'printf "%s" OK; exec 2>/dev/null; echo OK' + atf_check -s exit:0 \ + -o match:OKOK -o not-match:echo -o not-match:printf \ + -e match:printf -e match:OK -e match:echo \ + ${TEST_SH} -ecX 'printf "%s" OK; exec 2>&1; echo OK' + atf_check -s exit:0 \ + -o match:OKOK -o not-match:echo -o not-match:printf \ + -e match:printf -e match:OK -e match:echo \ + ${TEST_SH} -ecX 'printf "%s" OK; exec 2>&-; echo OK' + + # and that we can put tracing on an external file, leaving stderr alone + + atf_require_prog grep + + rm -f X-trace + atf_check -s exit:0 \ + -o match:OKOK -o not-match:echo -o not-match:printf \ + -e empty \ + ${TEST_SH} -ec 'PS4=; set -X 2>X-trace; printf "%s" OK; echo OK' + test -s X-trace || atf_fail "T1: Failed to create trace output file" + grep >/dev/null 2>&1 'printf.*%s.*OK' X-trace || + atf_fail "T1: -X tracing missing printf" + grep >/dev/null 2>&1 'echo.*OK' X-trace || + atf_fail "T1: -X tracing missing echo" + + rm -f X-trace + atf_check -s exit:0 \ + -o match:OKOK -o not-match:echo -o not-match:printf \ + -e empty \ + ${TEST_SH} -ec \ + 'PS4=; set -X 2>X-trace; + printf "%s" OK; + exec 2>/dev/null; + echo OK' + test -s X-trace || atf_fail "T2: Failed to create trace output file" + grep >/dev/null 2>&1 'printf.*%s.*OK' X-trace || + atf_fail "T2: -X tracing missing printf" + grep >/dev/null 2>&1 'exec' X-trace || + atf_fail "T2: -X tracing missing exec" + grep >/dev/null 2>&1 'echo.*OK' X-trace || + atf_fail "T2: -X tracing missing echo after stderr redirect" + + rm -f X-trace + atf_check -s exit:0 \ + -o match:OKOK -o not-match:echo -o not-match:printf \ + -e empty \ + ${TEST_SH} -ec \ + 'PS4=; set -X 2>X-trace; + printf "%s" OK; + set -X 2>/dev/null; + echo OK' + test -s X-trace || atf_fail "T3: Failed to create trace output file" + grep >/dev/null 2>&1 'printf.*%s.*OK' X-trace || + atf_fail "T3: -X tracing missing printf" + grep >/dev/null 2>&1 'set.*-X' X-trace || + atf_fail "T3: -X tracing missing set -X" + grep >/dev/null 2>&1 'echo.*OK' X-trace && + atf_fail "T3: -X tracing included echo after set -X redirect" + + rm -f X-trace + atf_check -s exit:0 \ + -o match:OKOK -o not-match:echo -o not-match:printf \ + -e match:echo -e match:OK -e not-match:printf \ + ${TEST_SH} -ec \ + 'PS4=; set -X 2>X-trace; + printf "%s" OK; + set -X; + echo OK' + test -s X-trace || atf_fail "T4: Failed to create trace output file" + grep >/dev/null 2>&1 'printf.*%s.*OK' X-trace || + atf_fail "T4: -X tracing missing printf" + grep >/dev/null 2>&1 'set.*-X' X-trace || + atf_fail "T4: -X tracing missing set -X" + grep >/dev/null 2>&1 'echo.*OK' X-trace && + atf_fail "T4: -X tracing included echo after set -X redirect" + + # Now check that -X and the tracing files work properly wrt functions + + # a shell that supports -X should support "local -" ... but verify + + ( ${TEST_SH} -c 'fn() { local - || exit 2; set -f; }; set +f; fn; + case "$-" in ("*f*") exit 1;; esac; exit 0' ) 2>/dev/null || + atf_skip "-X function test: 'local -' unsupported" + + rm -f X-trace X-trace-fn + atf_check -s exit:0 \ + -o match:OKhelloGOOD \ + -e empty \ + ${TEST_SH} -c ' + say() { + printf "%s" "$*" + } + funct() { + local - + + set -X 2>X-trace-fn + say hello + } + + set -X 2>X-trace + + printf OK + funct + echo GOOD + ' + test -s X-trace || atf_fail "T5: Failed to create trace output file" + test -s X-trace-fn || atf_fail "T5: Failed to create fn trace output" + grep >/dev/null 2>&1 'printf.*OK' X-trace || + atf_fail "T5: -X tracing missing printf" + grep >/dev/null 2>&1 funct X-trace || + atf_fail "T5: -X tracing missing funct" + grep >/dev/null 2>&1 'set.*-X' X-trace || + atf_fail "T5: -X tracing missing set -X from in funct" + grep >/dev/null 2>&1 'echo.*GOOD' X-trace || + atf_fail "T5: -X tracing missing echo after funct redirect" + grep >/dev/null 2>&1 'say.*hello' X-trace && + atf_fail "T5: -X tracing included 'say' after funct redirect" + grep >/dev/null 2>&1 'say.*hello' X-trace-fn || + atf_fail "T5: -X funct tracing missed 'say'" + + rm -f X-trace X-trace-fn + + atf_check -s exit:0 \ + -o match:OKhelloGOOD \ + -e empty \ + ${TEST_SH} -c ' + say() { + printf "%s" "$*" + } + funct() { + local - + + set +X + say hello + } + + set -X 2>X-trace + + printf OK + funct + echo GOOD + ' + test -s X-trace || atf_fail "T6: Failed to create trace output file" + grep >/dev/null 2>&1 'printf.*OK' X-trace || + atf_fail "T6: -X tracing missing printf" + grep >/dev/null 2>&1 funct X-trace || + atf_fail "T6: -X tracing missing funct" + grep >/dev/null 2>&1 'set.*+X' X-trace || + atf_fail "T6: -X tracing missing set +X from in funct" + grep >/dev/null 2>&1 'echo.*GOOD' X-trace || + atf_fail "T6: -X tracing missing echo after funct redirect" + grep >/dev/null 2>&1 'say.*hello' X-trace && + atf_fail "T6: -X tracing included 'say' after funct redirect" + + rm -f X-trace + + atf_check -s exit:0 \ + -o match:OKtracednotraceGOOD \ + -e match:say -e match:traced -e not-match:notrace \ + ${TEST_SH} -c ' + say() { + printf "%s" "$*" + } + funct() { + local - + + set +X -x + + say traced + exec 2>/dev/null + say notrace + + } + + set -X 2>X-trace + + printf OK + funct + echo GOOD + ' + test -s X-trace || atf_fail "T7: Failed to create trace output file" + grep >/dev/null 2>&1 'printf.*OK' X-trace || + atf_fail "T7: -X tracing missing printf" + grep >/dev/null 2>&1 funct X-trace || + atf_fail "T7: -X tracing missing funct" + grep >/dev/null 2>&1 'set.*+X.*-x' X-trace || + atf_fail "T7: -X tracing missing set +X -x from in funct" + grep >/dev/null 2>&1 'echo.*GOOD' X-trace || + atf_fail "T7: -X tracing missing echo after funct +X" + grep >/dev/null 2>&1 'say.*hello' X-trace && + atf_fail "T7: -X tracing included 'say' after funct +X" + + rm -f X-trace X-trace-fn + atf_check -s exit:0 \ + -o "match:OKg'daybye-bye.*hello.*GOOD" \ + -e empty \ + ${TEST_SH} -c ' + say() { + printf "%s" "$*" + } + fn1() { + local - + + set -X 2>>X-trace-fn + say "g'\''day" + "$@" + say bye-bye + } + fn2() { + set +X + say hello + "$@" + say goodbye + } + + set -X 2>X-trace + + printf OK + fn1 + fn1 fn2 + fn1 fn1 fn2 + fn1 fn2 fn1 fn2 fn1 + fn1 fn1 fn2 fn2 fn1 + echo GOOD + ' + + # That test generally succeeds if the earlier ones did + # and if it did not dump core! + + # But we can check a few things... + + test -s X-trace || atf_fail "T8: Failed to create trace output file" + test -s X-trace-fn || atf_fail "T8: Failed to create trace output file" + grep >/dev/null 2>&1 'printf.*OK' X-trace || + atf_fail "T8: -X tracing missing printf" + grep >/dev/null 2>&1 fn1 X-trace || + atf_fail "T8: -X tracing missing fn1" + grep >/dev/null 2>&1 'set.*-X' X-trace || + atf_fail "T8: -X tracing missing set -X from in fn1" + grep >/dev/null 2>&1 'echo.*GOOD' X-trace || + atf_fail "T8: -X tracing missing echo after fn1 redirect" + grep >/dev/null 2>&1 'say.*hello' X-trace && + atf_fail "T8: -X tracing included 'say' after fn2 +X" + grep >/dev/null 2>&1 'say.*hello' X-trace-fn && + atf_fail "T8: -X fn tracing included 'say' after fn2 +X" + + + rm -f X-trace + + return 0 +} + opt_test_setup() { test -n "$1" || { echo >&2 "Internal error"; exit 1; } @@ -596,6 +921,56 @@ ' } +atf_test_case pipefail +pipefail_head() { + atf_set "descr" "Basic tests of the pipefail option" +} +pipefail_body() { + ${TEST_SH} -c 'set -o pipefail' 2>/dev/null || + atf_skip "pipefail option not supported by ${TEST_SH}" + + atf_check -s exit:0 -o match:'pipefail.*off' -e empty ${TEST_SH} -c \ + 'set -o | grep pipefail' + atf_check -s exit:0 -o match:'pipefail.*on' -e empty ${TEST_SH} -c \ + 'set -o pipefail; set -o | grep pipefail' + + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + '(exit 1) | (exit 2) | (exit 0)' + atf_check -s exit:2 -o empty -e empty ${TEST_SH} -c \ + 'set -o pipefail; (exit 1) | (exit 2) | (exit 0)' + atf_check -s exit:1 -o empty -e empty ${TEST_SH} -c \ + 'set -o pipefail; (exit 1) | (exit 0) | (exit 0)' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'set -o pipefail; (exit 0) | (exit 0) | (exit 0)' + + atf_check -s exit:1 -o empty -e empty ${TEST_SH} -c \ + '! (exit 1) | (exit 2) | (exit 0)' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'set -o pipefail; ! (exit 1) | (exit 2) | (exit 0)' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'set -o pipefail; ! (exit 1) | (exit 0) | (exit 0)' + atf_check -s exit:1 -o empty -e empty ${TEST_SH} -c \ + 'set -o pipefail; ! (exit 0) | (exit 0) | (exit 0)' + + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + '(exit 1) | (exit 2) | (exit 0); echo $?' + atf_check -s exit:0 -o inline:'2\n' -e empty ${TEST_SH} -c \ + 'set -o pipefail; (exit 1) | (exit 2) | (exit 0); echo $?' + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'set -o pipefail; (exit 1) | (exit 0) | (exit 0); echo $?' + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'set -o pipefail; (exit 0) | (exit 0) | (exit 0); echo $?' + + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + '! (exit 1) | (exit 2) | (exit 0); echo $?' + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'set -o pipefail; ! (exit 1) | (exit 2) | (exit 0); echo $?' + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'set -o pipefail; ! (exit 1) | (exit 0) | (exit 0); echo $?' + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'set -o pipefail; ! (exit 0) | (exit 0) | (exit 0); echo $?' +} + atf_test_case xx_bogus xx_bogus_head() { atf_set "descr" "Tests that attempting to set a nonsense option fails." @@ -634,7 +1009,7 @@ # be accessable via the "set" command, just the command line. # We allow for -i to work with set, as that makes some sense, # -c and -s do not. - test_optional_on_off E i I p q V || true + test_optional_on_off E i I p q V X || true # Also test (some) option combinations ... # only testing posix options here, because it is easier... @@ -668,7 +1043,11 @@ atf_add_test_case set_u atf_add_test_case set_v atf_add_test_case set_x + atf_add_test_case set_X atf_add_test_case vi_emacs_VE_toggle + + atf_add_test_case pipefail + atf_add_test_case xx_bogus } diff --git a/bin/sh/t_patterns.sh b/bin/sh/t_patterns.sh new file mode 100644 --- /dev/null +++ b/bin/sh/t_patterns.sh @@ -0,0 +1,829 @@ +# $NetBSD: t_patterns.sh,v 1.5 2019/07/10 05:57:43 martin Exp $ +# +# Copyright (c) 2018 The NetBSD Foundation, Inc. +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# +# the implementation of "sh" to test +: ${TEST_SH:=/bin/sh} + +# +# This file tests pattern matching (glob) +# +# Three forms: +# standard filename expansion (echo *.c) +# case statements (case word in (*.c) ...;;) +# var expansions with substring matching ${var%*.c} +# +# Note: the emphasis here is on testing the various possible patterns, +# not that case statements, or var expansions (etc) work in general. + +### Helper functions + +nl=' +' +reset() +{ + TEST_NUM=0 + TEST_FAILURES='' + TEST_FAIL_COUNT=0 + TEST_ID="$1" +} + +# Test run & validate. +# +# $1 is the command to run (via sh -c) +# $2 is the expected output (with any \n's in output replaced by spaces) +# $3 is the expected exit status from sh +# +# Stderr is exxpected to be empty, unless the expected exit code ($3) is != 0 +# in which case some message there is expected (and nothing is a failure). +# When non-zero exit is expected, we note a different (non-zero) value +# observed, but do not fail the test because of that. + +check() +{ + fail=false + # Note TEMP_FILE must not be in the current directory (or nearby). + TEMP_FILE=$( mktemp /tmp/OUT.XXXXXX ) + TEST_NUM=$(( $TEST_NUM + 1 )) + MSG= + + # our local shell (ATF_SHELL) better do quoting correctly... + # some of the tests expect us to expand $nl internally... + CMD="$1" + + result="$( ${TEST_SH} -c "${CMD}" 2>"${TEMP_FILE}" )" + STATUS=$? + + if [ "${STATUS}" -ne "$3" ]; then + MSG="${MSG}${MSG:+${nl}}[$TEST_NUM]" + MSG="${MSG} expected exit code $3, got ${STATUS}" + + # don't actually fail just because of wrong exit code + # unless we either expected, or received "good" + # or something else is detected as incorrect as well. + case "$3/${STATUS}" in + (*/0|0/*) fail=true;; + esac + fi + + if [ "$3" -eq 0 ]; then + if [ -s "${TEMP_FILE}" ]; then + MSG="${MSG}${MSG:+${nl}}[$TEST_NUM]" + MSG="${MSG} Messages produced on stderr unexpected..." + MSG="${MSG}${nl}$( cat "${TEMP_FILE}" )" + fail=true + fi + else + if ! [ -s "${TEMP_FILE}" ]; then + MSG="${MSG}${MSG:+${nl}}[$TEST_NUM]" + MSG="${MSG} Expected messages on stderr," + MSG="${MSG} nothing produced" + fail=true + fi + fi + rm -f "${TEMP_FILE}" + + case "${result}" in + (*[!0-9" $nl"]*) + # A word of some kind: at least 1 char that is not digit or wsp + # Remove newlines (use local shell for this) + result="$( + set -f + IFS="$nl" + set -- $result + IFS=' ' + printf '%s' "$*" + )" + ;; + (*[0-9]*) + # a numeric result, return just the number, trim whitespace + result=$(( ${result} )) + ;; + (*) + # whitespace only, or empty string: just leave it as is + ;; + esac + + if [ "$2" != "${result}" ] + then + MSG="${MSG}${MSG:+${nl}}[$TEST_NUM]" + MSG="${MSG} Expected output '$2', received '$result'" + fail=true + fi + + if $fail + then + MSG="${MSG}${MSG:+${nl}}[$TEST_NUM]" + MSG="${MSG} Full command: <<${CMD}>>" + fi + + $fail && test -n "$TEST_ID" && { + TEST_FAILURES="${TEST_FAILURES}${TEST_FAILURES:+${nl}}" + TEST_FAILURES="${TEST_FAILURES}${TEST_ID}[$TEST_NUM]:" + TEST_FAILURES="${TEST_FAILURES} Test of '$1' failed."; + TEST_FAILURES="${TEST_FAILURES}${nl}${MSG}" + TEST_FAIL_COUNT=$(( $TEST_FAIL_COUNT + 1 )) + return 0 + } + $fail && atf_fail "Test[$TEST_NUM] failed: $( + # ATF does not like newlines in messages, so change them... + printf '%s' "${MSG}" | tr '\n' ';' + )" + return 0 +} + +results() +{ + test -n "$1" && atf_expect_fail "$1" + + test -z "${TEST_ID}" && return 0 + test -z "${TEST_FAILURES}" && return 0 + + echo >&2 "==========================================" + echo >&2 "While testing '${TEST_ID}'" + echo >&2 " - - - - - - - - - - - - - - - - -" + echo >&2 "${TEST_FAILURES}" + + atf_fail \ + "Test ${TEST_ID}: $TEST_FAIL_COUNT (of $TEST_NUM) subtests failed - see stderr" +} + +####### End helpers + +atf_test_case filename_expansion +filename_expansion() { + atf_set descr "Test correct operation of filename expansion" +} +filename_expansion_body() { + atf_require_prog mktemp + atf_require_prog wc + atf_require_prog mv + atf_require_prog rm + atf_require_prog mkdir + atf_require_prog df + atf_require_prog awk + + reset filename_expansion + + # First create a known set of filenames to match against + + # Note: This creates almost 17000 files/directories, so + # needs at least that many free inodes (only space consumed + # is for the directory contents, with a 1K frag size, it + # should be about 1.2MiB). Switching to making links would + # save inodes, but would require running "ln" many times, so + # would be a lot slower. + + free_inodes=$( df -i . | awk '/^Filesystem/{next}; { print $7 }' ) + if [ $free_inodes -lt 17000 ]; then + atf_skip "not enough space" + fi + + # This should work on a case insensitive, but preserving, + # filesystem - but case sensitive filesystems are preferred. + + D=$(mktemp -d "DIR.$$.XXXXXX") || atf_fail "mktemp -d failed" + cd "${D}" || atf_fail "cd to temp dir '$D' failed" + + # we need another level of directory, so we know what + # files to expect in ".." (ie: in $D) - only ".D". + mkdir .D && cd .D || atf_fail "failed to make or enter .D in $D" + + > Xx || atf_fail "Unable to make files in temporary directory" + case "$( printf '%s\n' *)" in + (Xx) rm Xx || atf_fail "Unable to delete file";; + (\*) atf_fail "Created file vanished";; + (xx|XX|xX) atf_skip "Case preserving filesystem required";; + (*) atf_fail "Unexpected file expansion for '*'";; + esac + + # from here on we make files/directories that we will be + # using in the tests. + + # CAUTION: Change *any* of this and the expected results from the + # tests will all need verifying and updating as well. + + mkdir D || atf_fail "mkdir D failed" + + for F in a b c e V W X Y 1 2 3 4 5 \\ \] \[ \* \? \- \! \^ \| \# \' \" + do + > "${F}" + > ".${F}" + > "${F}.${F}" + > "${F}-${F}" + > "${F}${F}${F}${F}" + > "x${F}z" + > ab"${F}"yz + > "m${F}n${F}p${F}q" + + > "D/${F}" + > "D/.${F}" + > "D/${F}${F}${F}${F}" + + mkdir "D${F}" || atf_fail "mkdir 'D${F}' failed" + mkdir ".D${F}" || atf_fail "mkdir '.D${F}' failed" + + for G in a b c e W X Y 0 2 4 6 \\ \] \[ \* \? \- \! \^ \| \# + do + > "${F}${G}" + > "${F}${G}${G}" + > "${F}${G}${F}" + > "${G}${F}${G}" + > "${F}${G}${G}${F}" + > "${G}${F}${F}${G}" + > "${F}.${G}" + > "${F}${G}.${G}${G}" + > "${F}${G}${F}${G}.${G}" + > "x${F}${G}y" + > "${F}z${G}" + > "${G}zz${F}" + > "${G}+${G}" + + > "D${F}/${G}" + > "D${F}/.${G}" + > "D${F}/${G}${F}${G}" + > "D${F}/.${G}${F}${G}" + + > ".D${F}/${G}" + > ".D${F}/.${G}" + > ".D${F}/${G}${F}${G}" + > ".D${F}/.${G}${F}${G}" + + mkdir "D${F}/D${G}" "D${F}/D${F}${G}" || + atf_fail \ + "subdir mkdirs failed D${F}/D${G} D${F}/D${F}${G}" + + > "D${F}/D${G}/${G}" + > "D${F}/D${G}.${G}" + > "D${F}/D${G}/${F}${G}" + > "D${F}/D${G}/${G}${F}${G}" + > "D${F}/D${G}/.${G}${F}${G}" + > "D${F}/D${G}/.${G}${F}${G}" + + > "D${F}/D${F}${G}/${G}" + > "D${F}/D${F}${G}.${G}" + > "D${F}/D${F}${G}/${G}${F}" + > "D${F}/D${F}${G}/${G}${G}${F}" + > "D${F}/D${F}${G}/.${F}${F}${G}" + > "D${F}/D${F}${G}/.${G}${F}${F}" + + done + done + + # Debug hooks ... run with environment var set to filename + + case "${ATF_TEST_SAVE_FILENAMES}" in + '') ;; + /*) ls -R >"${ATF_TEST_SAVE_FILENAMES}" ;; + *) ls -R >"${TMPDIR:-/tmp}/${ATF_TEST_SAVE_FILENAMES}" ;; + esac + case "${ATF_TEST_SAVE_FILES}" in + '') ;; + /*) (cd ../..; tar cf "${ATF_TEST_SAVE_FILES}" D) ;; + *) (cd ../..; tar cf "${TMPDIR:-/tmp}/${ATF_TEST_SAVE_FILES}" D) ;; + esac + + # Now we have lots of files, try some matching + + # First just check that "printf | wc -l" works properly... + check 'printf "%s\n" 1 2 3 | wc -l' '3' 0 #1 + + # Next a whole bunch of elementary patterns + check 'printf "%s\n" ab* | wc -l' '31' 0 + check 'printf "%s\n" x*y | wc -l' '525' 0 + check 'printf "%s\n" * | wc -l' '5718' 0 + check 'printf "%s\n" ? | wc -l' '26' 0 #5 + check 'printf "%s\n" ?? | wc -l' '550' 0 + check 'printf "%s\n" ??? | wc -l' '2297' 0 + check 'printf "%s\n" ???? | wc -l' '1745' 0 + check 'printf "%s\n" ????? | wc -l' '550' 0 + + check 'printf "%s\n" ?????? | wc -l' '525' 0 #10 + check 'printf "%s\n" ??????? | wc -l' '25' 0 + check 'printf "%s\n" ???????? | wc -l' '1' 0 + check 'printf "%s\n" ????????' '????????' 0 + check 'printf "%s\n" m* | wc -l' '25' 0 + check 'printf "%s\n" -* | wc -l' '206' 0 #15 + check 'printf "%s\n" *- | wc -l' '227' 0 + check 'printf "%s\n" -? | wc -l' '21' 0 + check 'printf "%s\n" ?- | wc -l' '26' 0 + check 'printf "%s\n" [ab] | wc -l' '2' 0 + + check 'printf "%s\n" [ab]* | wc -l' '437' 0 #20 + check 'printf "%s\n" [A-Z]* | wc -l' '815' 0 + check 'printf "%s\n" [0-4]* | wc -l' '830' 0 + check 'printf "%s\n" [-04]* | wc -l' '488' 0 + check 'printf "%s\n" [40-]* | wc -l' '488' 0 + check 'printf "%s\n" *[0-9] | wc -l' '1057' 0 #25 + check 'printf "%s\n" *[0-9]* | wc -l' '2109' 0 + check 'printf "%s\n" ?[0-9]* | wc -l' '855' 0 + check 'printf "%s\n" ?[0-9]? | wc -l' '270' 0 + check 'printf "%s\n" *[0-9]? | wc -l' '750' 0 + + check 'printf "%s\n" [a-c][0-9]? | wc -l' '33' 0 #30 + check 'printf "%s\n" [[:alpha:]] | wc -l' '9' 0 + check 'printf "%s\n" [[:alpha:][:digit:]] | wc -l' '14' 0 + check 'printf "%s\n" [[:alpha:]][[:digit:]] | wc -l' '37' 0 + check \ + 'printf "%s\n" [[:alpha:][:digit:]][[:alpha:][:digit:]] | wc -l' \ + '156' 0 + check 'printf "%s\n" D*/*a | wc -l' '152' 0 #35 + check 'printf "%s\n" D?/*a | wc -l' '150' 0 + check 'printf "%s\n" D*/?a | wc -l' '25' 0 + check 'printf "%s\n" D?/?a | wc -l' '25' 0 + check 'printf "%s\n" */*a | wc -l' '152' 0 + + check 'printf "%s\n" [A-Z]*/*a | wc -l' '152' 0 #40 + check 'printf "%s\n" ??/*a | wc -l' '150' 0 + check 'printf "%s\n" .*/*a | wc -l' '277' 0 + check 'printf "%s\n" .?*/*a | wc -l' '50' 0 + check 'printf "%s\n" *-/-* | wc -l' '2' 0 + check 'printf "%s\n" *-/-*' 'D-/- D-/---' 0 #45 + + # now some literal magic chars + check 'printf "%s\n" \?* | wc -l' '206' 0 + check 'printf "%s\n" *\?* | wc -l' '471' 0 + check 'printf "%s\n" \*? | wc -l' '21' 0 + check 'printf "%s\n" \** | wc -l' '206' 0 + + check 'printf "%s\n" *\?* | wc -l' '471' 0 #50 + check 'printf "%s\n" \[?] | wc -l' '3' 0 + check 'printf "%s\n" \[?]' '[.] []] [z]' 0 + check 'printf "%s\n" *\[* | wc -l' '471' 0 + check 'printf "%s\n" \?\?* | wc -l' '5' 0 + check 'printf "%s\n" \?\?*' '?? ??.?? ??? ???? ????.?' 0 #55 + check 'printf "%s\n" [A\-C]* | wc -l' '206' 0 + check 'printf "%s\n" [-AC]* | wc -l' '206' 0 + check 'printf "%s\n" [CA-]* | wc -l' '206' 0 + check 'printf "%s\n" [A\]-]? | wc -l' '42' 0 + + check 'printf "%s\n" []A\-]? | wc -l' '42' 0 #60 + check 'printf "%s\n" []A-]? | wc -l' '42' 0 + check 'printf "%s\n" \\* | wc -l' '206' 0 + check 'printf "%s\n" [[-\]]?\?* | wc -l' '12' 0 + check 'printf "%s\n" []\\[]?\? | wc -l' '9' 0 + check 'printf "%s\n" *\\\\ | wc -l' '52' 0 #65 + check 'printf "%s\n" [*][?]* | wc -l' '6' 0 + check 'printf "%s\n" "*?"* | wc -l' '6' 0 + check "printf '%s\\n' '\\'*\\\\ | wc -l" '61' 0 + check 'printf "%s\n" ["a-b"]* | wc -l' '643' 0 + + check 'printf "%s\n" ["A-C"]z[[] | wc -l' '1' 0 #70 + check 'printf "%s\n" ["A-C"]z[[]' '-z[' 0 + check 'printf "%s\n" ?"??"* | wc -l' '54' 0 + check 'printf "%s\n" \??\?* | wc -l' '52' 0 + check 'printf "%s\n" [?][\?]* | wc -l' '5' 0 + check 'printf "%s\n" [?][\?]*' '?? ??.?? ??? ???? ????.?' 0 #75 + check 'printf "%s\n" [!ab] | wc -l' '24' 0 + check 'printf "%s\n" [!ab]* | wc -l' '5281' 0 + check 'printf "%s\n" [!A-D]* | wc -l' '5692' 0 + check 'printf "%s\n" [!0-3]* | wc -l' '5094' 0 + + check 'printf "%s\n" [!-03]* | wc -l' '5265' 0 #80 + check 'printf "%s\n" [!30-]* | wc -l' '5265' 0 + check 'printf "%s\n" [!0\-3]* | wc -l' '5265' 0 + check 'printf "%s\n" [\!0-3]* | wc -l' '830' 0 + check 'printf "%s\n" [0-3!]* | wc -l' '830' 0 + check 'printf "%s\n" [0!-3]* | wc -l' '1790' 0 #85 + check 'printf "%s\n" *[!0-3] | wc -l' '5156' 0 + check 'printf "%s\n" *[!0-3]* | wc -l' '5680' 0 + check 'printf "%s\n" ?[!0-3]* | wc -l' '5231' 0 + check 'printf "%s\n" ?[!0-3]? | wc -l' '2151' 0 + + check 'printf "%s\n" *[!0-3]? | wc -l' '5284' 0 #90 + check 'printf "%s\n" [!a-c][!0-3]? | wc -l' '1899' 0 + check 'printf "%s\n" [![:alpha:]] | wc -l' '17' 0 + check 'printf "%s\n" [![:alpha:][:digit:]] | wc -l' '12' 0 + check 'printf "%s\n" [![:alpha:]][[:digit:]] | wc -l' '68' 0 + check 'printf "%s\n" [[:alpha:]][![:digit:]] | wc -l' '156' 0 #95 + check 'printf "%s\n" [![:alpha:]][![:digit:]] | wc -l' '289' 0 + check 'printf "%s\n" [!A-Z]*/*a | wc -l' '1' 0 + check 'printf "%s\n" [!A-Z]*/*a' '[!A-Z]*/*a' 0 + check 'printf "%s\n" [!A\-D]* | wc -l' '5486' 0 + + check 'printf "%s\n" [!-AD]* | wc -l' '5486' 0 #100 + check 'printf "%s\n" [!DA-]* | wc -l' '5486' 0 + check 'printf "%s\n" [!A\]-]? | wc -l' '508' 0 + check 'printf "%s\n" [!]A\-]? | wc -l' '508' 0 + check 'printf "%s\n" [!]A-]? | wc -l' '508' 0 + check 'printf "%s\n" [![-\]]?\?* | wc -l' '164' 0 #105 + check 'printf "%s\n" [!]\\[]?\? | wc -l' '93' 0 + check 'printf "%s\n" [!*][?]* | wc -l' '171' 0 + check 'printf "%s\n" [*][!?]* | wc -l' '199' 0 + check 'printf "%s\n" [!*][!?]* | wc -l' '5316' 0 + + check 'printf "%s\n" [!"a-b"]* | wc -l' '5075' 0 #110 + check 'printf "%s\n" ["!a-b"]* | wc -l' '849' 0 + check 'printf "%s\n" [!"A-D"]z[[] | wc -l' '24' 0 + check 'printf "%s\n" ["!A-D"]z[[] | wc -l' '2' 0 + check 'printf "%s\n" ["!A-D"]z[[]' '!z[ -z[' 0 + check 'printf "%s\n" ["A-D"]z[![] | wc -l' '20' 0 #115 + check 'printf "%s\n" [!"A-D"]z[![] | wc -l' '480' 0 + check 'printf "%s\n" ["!A-D"]z[![] | wc -l' '40' 0 + check 'printf "%s\n" [!?][\?]* | wc -l' '172' 0 + check 'printf "%s\n" [?][!\?]* | wc -l' '200' 0 + + check 'printf "%s\n" [!?][!\?]* | wc -l' '5315' 0 #120 + check 'printf "%s\n" [!?][?!]* | wc -l' '343' 0 + check 'printf "%s\n" [?][\?!]* | wc -l' '11' 0 + check "printf '%s\\n' [\']*[!#] | wc -l" '164' 0 + check 'printf "%s\n" [\"]*[\|] | wc -l' '6' 0 + check 'printf "%s\n" [\"]*[\|]' '".| "z| "| "|"|.| "|.|| "||' 0 #125 + check "printf '%s\\n' '\"['* | wc -l" '6' 0 + check "printf '%s\\n' '\"['*" '"[ "[" "["[.[ "[.[[ "[[ "[["' 0 + + # Now test cases where the pattern is the result of a + # variable expansion (will assume, for now, that cmdsub & arith + # work the same way, so omit tests using those) + # we need to check both unquoted & quoted var expansions, + # expansions that result from ${xxx-xxx} and ${xxx%yyy} + # and expansions that form just part of the eventual pattern. + + check 'var="x*y";printf "%s\n" ${var} | wc -l' '525' 0 + check 'var="[a-e]?[0-9]";printf "%s\n" ${var} | wc -l' '48' 0 + + check 'var="[a-e]?.*";printf "%s\n" ${var} | wc -l' '84' 0 #130 + check 'var="[a-e]\?.*";printf "%s\n" ${var} | wc -l' '4' 0 + check 'var="[a-e]\?.*";printf "%s\n" ${var}' 'a?.?? b?.?? c?.?? e?.??' 0 + + # and if you're looking for truly weird... + + check 'set -- a b; IFS=\?; printf "%s\n" "$*" | wc -l' '1' 0 + check 'set -- a b; IFS=\?; printf "%s\n" "$*"' 'a?b' 0 + check 'set -- a b; IFS=\?; printf "%s\n" $* | wc -l' '2' 0 #boring #135 + check 'set -- a b; IFS=\?; var=$*; unset IFS; printf "%s\n" ${var}' \ + 'a.b abb azb' 0 + check 'set -- a b; IFS=\?; var=$*; unset IFS; printf "%s\n" "${var}"' \ + 'a?b' 0 + check 'set -- a \?; IFS=\\; printf "%s\n" "$*"' 'a\?' 0 + check 'set -- a \?; IFS=\\; var=$*; unset IFS; printf "%s\n" "${var}"' \ + 'a\?' 0 + + check 'set -- a \?; IFS=\\; var=$*; unset IFS; printf "%s\n" ${var}' \ + 'a?' 0 #140 + mv 'a?' 'a@' + check 'set -- a \?; IFS=\\; var=$*; unset IFS; printf "%s\n" ${var}' \ + 'a\?' 0 + mv 'a@' 'a?' + + # This is unspecified by POSIX, but everyone (sane) does it this way + check 'printf "%s\n" D*[/*] | wc -l' '6' 0 + check 'printf "%s\n" D*[\/*] | wc -l' '6' 0 + check 'printf "%s\n" D*\[/*] | wc -l' '6' 0 + check 'printf "%s\n" D*\[\/*] | wc -l' '6' 0 #145 + check 'printf "%s\n" D*[/*]' \ + 'D[/D[] D[/D[].] D[/D] D[/D].] D[/] D[/][]' 0 + + # '^' as the first char in a bracket expr is unspecified by POSIX, + # but for compat with REs everyone (sane) makes it the same as ! + + # But just in case we are testing an insane shell ... + ${TEST_SH} -c 'case "^" in ([^V^]) exit 1;; (*) exit 0;; esac' && { + + check 'printf "%s\n" [^ab] | wc -l' '24' 0 + check 'printf "%s\n" [^ab]* | wc -l' '5281' 0 + check 'printf "%s\n" [^A-D]* | wc -l' '5692' 0 + + check 'printf "%s\n" [^0-3]* | wc -l' '5094' 0 #150 + check 'printf "%s\n" [^-03]* | wc -l' '5265' 0 + check 'printf "%s\n" [^0\-3]* | wc -l' '5265' 0 + check 'printf "%s\n" [^-a3]* | wc -l' '5110' 0 + check 'printf "%s\n" [\^-a3]* | wc -l' '608' 0 + check 'printf "%s\n" [\^0-3]* | wc -l' '830' 0 #155 + check 'printf "%s\n" [0-3^]* | wc -l' '830' 0 + check 'printf "%s\n" [0^-a]* | wc -l' '513' 0 + check 'printf "%s\n" *[^0-3] | wc -l' '5156' 0 + check 'printf "%s\n" [!^]? | wc -l' '529' 0 + + check 'printf "%s\n" [^!]? | wc -l' '529' 0 #160 + check 'printf "%s\n" [!!^]? | wc -l' '508' 0 + check 'printf "%s\n" [!^!]? | wc -l' '508' 0 + check 'printf "%s\n" [^!]? | wc -l' '529' 0 + check 'printf "%s\n" [^!^]? | wc -l' '508' 0 + check 'printf "%s\n" [^^!]? | wc -l' '508' 0 #165 + check 'printf "%s\n" [!^-b]? | wc -l' '487' 0 + check 'printf "%s\n" [^!-b]? | wc -l' '63' 0 + + } + + # No need to clean up the directory, we're in the ATF working + # directory, and ATF cleans up for us. + + results +} + +atf_test_case case_matching +case_matching_head() { + atf_set descr "Test expansion of vars with embedded cmdsub" +} + +# helper functions for case matching +# +# usage: cm word [ pattern ] [ preamble ] (expect word to match pattern) +# cf word [ pattern ] [ preamble ] (expect word to fail to match) +# +# The last used (non-null) pattern, and the last used preamble, are +# remembered and used again if only the word is given. To give a +# new preamble while using the last pattern, give '' as the pattern. +# +# nb: a null (empty) pattern is a syntax error, to get '' use "''" +# +cm() { + case "$2" in + '') set -- "$1" "${LAST_PATTERN}" "${3:-${LAST_PFX}}";; + *) LAST_PATTERN="$2";; + esac + LAST_PFX="$3" + + check \ + "${3:+${3}; }case $1 in ($2) printf M;; (*) printf X;; esac" M 0 +} +cf() { + case "$2" in + '') set -- "$1" "${LAST_PATTERN}" "${3:-${LAST_PFX}}";; + *) LAST_PATTERN="$2";; + esac + LAST_PFX="$3" + + check \ + "${3:+${3}; }case $1 in ($2) printf M;; (*) printf X;; esac" X 0 +} + +case_matching_body() { + + # nb: we are not testing execution of case, so no ;& or alternate + # patterns (etc) are needed here, we just want to validate the + # case variant of pattern matching, so simple one word, one pattern + # match or not match. + + reset case_matching + + cm abcd 'ab*'; cf bcda; cf aabce; cm ab # 4 + cm abcd '$var' 'var="ab*"'; cf abcd '"$var"' 'var="ab*"' # 6 + + cm xy 'x*y'; cm xyxy; cm '"x*y"'; cf xxyz # 10 + + cm '""' '*'; cm '\*'; cm '\?'; cm -; cm 12345 # 15 + cm abcd '$var' 'var="*"'; cf abcd '"$var"' 'var="*"' # 17 + cm '"*"' '\*'; cm '"*"' '"*"'; cm '"*"' '"$var"' 'var="*"' # 20 + + cm X '?'; cf XX '?'; cf X '"?"'; cm Y '$var' 'var="?"' # 24 + cf Z '"$var"' 'var="?"'; cm '"?"' '"$var"' 'var="?"' # 26 + + cm XA '??'; cf X '??'; cf XX '"??"'; cm YZ '$var' 'var="??"' # 30 + cf ZZ '"$var"' 'var="??"'; cm '"??"' '"$var"' 'var="??"' # 32 + + cm a '[ab]'; cm b; cf c; cf aa; cf '"[ab]"' # 37 + cm '"[ab]"' '"[ab]"'; cm '"[ab]"' '\[ab]' # 39 + cm a '$var' 'var="[ab]"'; cf a '"$var"' 'var="[ab]"' # 41 + cm '"[ab]"' '"$var"' 'var="[ab]"'; cm a '["$var"]' 'var=ab' # 43 + + cm b '[a-c]'; cm a '[a-c]'; cm c '[a-c]'; cf d '[a-c]' # 47 + cf '"[a-c]"' '[a-c]'; cm '"[a-c]"' '"[a-c]"' # 49 + cm '"[a-c]"' '\[a-c]'; cm '"[a-c]"' '[a-c\]' # 51 + cm a '$var' 'var="[a-c]"'; cf a '"$var"' 'var="[a-c]"' # 53 + cm '"[a-c]"' '"$var"' 'var="[a-c]"'; cf b '["$var"]' 'var=a-c' # 55 + + cm 2 '[0-4]'; cm 0 '[0-4]'; cf - '[0-4]'; cm 0 '[-04]' # 59 + cf 2 '[-04]'; cf 2 '[40-]'; cm 0 '[40-]'; cm - '[-04]' # 63 + cf 2 '[0\-4]'; cm - '[0\-4]'; cf 2 '["0-4"]'; cm - '["0-4"]' # 67 + cf 2 "[0'-'4]"; cm - "[0'-'4]"; cm 4 "[0'-'4]" # 70 + cm 0 "['0'-'4']"; cf '"\\"' '[0\-4]'; cm '"\\"' '[\\0-\\4]' # 73 + + cm a '[[:alpha:]]'; cf 0; cf '"["'; cm Z; cf aa; cf .; cf '""' # 80 + cf a '[[:digit:]]'; cm 0; cf '"["'; cm 9; cf 10; cf .; cf '""' # 87 + cm '"["' '[][:alpha:][]'; cf a '[\[:alpha:]]'; cf a '[[\:alpha:]]' #90 + cm a '[$var]' 'var="[:alpha:]"'; cm a '[[$var]]' 'var=":alpha:"' # 92 + cm a '[[:$var:]]' 'var=alpha'; cm B '[[:"$var":]]' 'var=alpha' # 94 + cf B '["$var"]' 'var="[:alpha:]"'; cf B '[["$var"]]' 'var=":alpha:"' #96 + cm '"["' '["$var"]' 'var="[:alpha:]"' # 97 + cm '"[]"' '[["$var"]]' 'var=":alpha:"'; # 98 + cm A3 '[[:alpha:]][[:digit:]]'; cf '"[["' #100 + cm 3 '[[:alpha:][:digit:]]'; cf '"["'; cm A; cf '":"' #104 + for W in AA A7 8x 77; do + cm "$W" '[[:alpha:][:digit:]][[:alpha:][:digit:]]' #108 + done + + cm dir/file '*/*'; cm /dir/file; cm /dir/file '*/file' #111 + for W in aa/bcd /x/y/z .x/.abc --/--- '\\//\\//' '[]/[][]' + do + cm "'$W'" '??/*'; cm "'$W'" '[-a/.\\[]?/??*[]dzc/-]' + done #123 + + cm '"?abc"' '\?*'; cf '"\\abc"'; cm '"?"' #126 + + cm '\\z' '"\\z"'; cf '\z'; cf z; cf '"\\"' #130 + + cm '"[x?abc"' '[[-\]]?\?*'; cm '"]x?abc"'; cm '"\\x?abc"' #133 + cf '"-x?abc"'; cf '"[xyzabc"'; cm '"[]?"' #136 + + cm '"[x?"' '[]\\[]?\?'; cm '"]x?"'; cm '"\\y?"'; cm '"[]?"' #140 + + cm "'\z'" '"\z"'; cf z; cm '\\z'; cm '$var' '' 'var="\z"' #144 + cm '${var}' '' "var='\z'"; cm '"${var}"' #146 + cf '${var}' '${var}' "var='\z'"; cm '${var}' '"${var}"' "var='\z'" #148 + cf "'${var}'"; cm "'${var}'" "'${var}'" "var='\z'" #150 + + cf abc '"$*"' 'IFS=?; set -- a c';cf '"a c"';cm "'a?c'";cm '"$*"' #154 + cf abc '"$*"' 'IFS=*; set -- a c';cf '"a c"';cm "'a*c'";cm '"$*"' #158 + cf abc '"$*"' 'IFS=\\;set -- a c';cf '"a c"';cm "'a\c'";cm '"$*"' #162 + cf abc '"$*"' 'IFS="";set -- a c';cf '"a c"';cm "'ac'"; cm '"$*"' #166 + + cm a '["$*"]' 'IFS=-; set -- a c';cf b;cm c;cm '-'; cf "']'" #171 + cm a '["$*"]' 'IFS=?; set -- a c';cf b;cm c;cm '"?"'; cf "'['" #176 + cm a '["$*"]' 'IFS=*; set -- a c';cf b;cm c;cm '"*"'; cf - #181 + cm a '["$*"]' 'IFS=\\;set -- a c';cf b;cm c;cm "'\\'";cf "'$'" #186 + cm a '["$*"]' 'IFS="";set -- a c';cf b;cm c #189 + + + # Now repeat the ones using bracket expressions, adding ! + + cf a '[!ab]'; cf b; cm c; cf aa; cf '"[!ab]"'; cm a '[ab!]'; cm ! #196 + cf a '$var' 'var="[!ab]"';cm x;cf a '"$var"' 'var="[!ab]"'; cf x #200 + cm '"[!ab]"' '"$var"' 'var="[!ab]"'; cf a; cf b; cf !; cf "'['" #205 + cf a '[!"$var"]' 'var=ab'; cm x; cm a '["!$var"]' 'var=ab' #208 + cf x; cm !; cm a '["$var"]' 'var=!ab'; cf x #212 + cf a '[$var]' 'var=!ab'; cm ! #214 + + cf b '[!a-c]'; cf a; cf c; cm d; cm !; cm -; cm _; cm '\\' #222 + cf a '$var' 'var="[!a-c]"'; cf b; cf c; cm d; cm !; cm - #228 + + cf 2 '[!0-4]'; cf 0; cm -; cf 4; cm !; cm "'['"; cm "']'" #235 + cm 2 '[!-04]'; cm 2 '[!40-]'; cf 0; cf -; cm !; #240 + cm 2 '[!0\-4]'; cf -; cm 2 '[!"0-4"]'; cf - #244 + + cf a '[![:alpha:]]'; cm 0; cm '"["'; cf aa; cm .; cf '""' #250 + cf '"["' '[!][:alpha:][!]'; cf a; cm 0; cf !; cf "']'"; cm % #256 + cf a '[$var]' 'var="![:alpha:]"'; cm 0; cm !; cm "']'"; cm @ #261 + + # Next some tests of patterns containing (intended literal) '\' + # The first of the "set" tests pair was reported as broken bu + # Martijn Dekker (private mail) (Nov 2018). + + cm "'\\'" "'\\'"; cf "'\\'" "'\\\\'" #263 + cm "'\\'" '"$var"' "var='\\'"; cf "'\\'" '$var' "var='\\'" #265 + cm '$1' '"$2"' 'set -- \\ \\'; cf '$1' '$2' 'set -- \\ \\' #267 + cf '$1' '"$2"' 'set -- \\ \\\\'; cm '$1' '$2' 'set -- \\ \\\\' #269 + cm "'\\'" "\$( echo '\\\\' )"; cf "'\\'" "\$( echo '\\' )" #271 + cm "'\\'" "\"\$( echo '\\' )\"" #272 + cf "'\\'" "\"\$( echo '\\\\' )\"" #273 + + if X=$( ${TEST_SH} -c 'printf %s '"\$'\\\\'" 2>/dev/null ) && + [ "$X" = '\' ] + then + # TEST_SH supports $'...' so we can test it as well + # note these are $'\\' and $'\\\\' as patterns. + # They should be identical to '\' and '\\' + cm "'\\'" "\$'\\\\'"; cf "'\\'" "\$'\\\\\\\\'" #275 + else + # uncomment this if we need to keep sub-test numbering sane + # it isn't needed as long as this remains last. + # (nb: this is just a repeat of sub-test 263) + + # cm "'\\'" "'\\'"; cf "'\\'" "'\\\\'" #275 + fi + + results +} + +atf_test_case var_substring_matching +var_substring_matching_head() { + atf_set descr 'Test pattern matching in var expansions' +} + +# Helper function for var substring matching +# $1 is the input string +# $2 the substring matching operator (# % ## or %%) +# $3 is the pattern to match (or reference to one) +# $4 is the expected output (result of applying op($2) with pat($3) to $1 +# $5 (if given, and not null) is a command (or commands) to run first +# $6 (if given, and not null) cause the var expansion to be quoted +# (ie "${var%pattern}" instead of just ${var%pattern}) +# any quotes needed in "pattern" should be in $3 +# Note: a variable called "var" is used (set to $1, then expanded). +vm() +{ + check "${5:+${5}; }var='$1';printf '%s\n' ${6:+\"}\${var$2$3}${6:+\"}" \ + "$4" 0 +} + +var_substring_matching_body() { + + reset var_substring_matching + + vm abc \# a bc; vm aaab \# a aab; vm aaab \## 'a*a' b # 3 + vm aaab % ab aa; vm xawab %% 'a*ab' x; vm abcd \# xyz abcd + vm file.c % .c 'f le' IFS=i ; vm file.c % .c file IFS=i Q + vm file.c % ?c file ; vm file.c % '"?c"' file.c # 9 10 + + vm abcabcabcded \# 'a*b' cabcabcded; vm abcabcabcded \## 'a*b' cded + vm abcabcabcded % 'c*d' abcabcab; vm abcabcabcded %% 'c*d' ab + + vm abc.jpg % '.[a-z][!0-9]?' abc # 15 + + vm xxxyyy \# '${P}' yyy P=xxx; vm xxxyyy \# '${P}' yyy 'P=x?x' + vm xxxyyy \# '${P}' yyy 'P=x?x' Q + vm 'x?xyyy' \# '${P}' yyy 'P=x[?]x' + vm xxxyyy \# '${P}' xxxyyy 'P=x[?]x' # 20 + vm 'x?xyyy' \# '${P}' yyy 'P=x?x' Q + vm xxxyyy \# '${P}' yyy 'P=x?x' Q + vm 'x?xyyy' \# '${P}' yyy 'P="x\?x"' + vm 'x?xyyy' \# '${P}' yyy 'P="x\?x"' Q + vm 'x?xyyy' \# '${P}' yyy 'P="x?x"' # 25 + vm 'x?xyyy' \# '${P}' yyy 'P="x?x"' Q + vm 'x?xyyy' \# '"${P}"' 'x?xyyy' 'P="x\?x"' + vm 'x?xyyy' \# '"${P}"' 'x?xyyy' 'P="x\?x"' Q + vm 'x?xyyy' \# '"${P}"' yyy 'P="x?x"' + vm 'x?xyyy' \# '"${P}"' yyy 'P="x?x"' Q # 30 + vm 'x%xyyy' \# '${P}' 'x%xyyy' 'P="x\?x"' + vm 'x%xyyy' \# '${P}' 'x%xyyy' 'P="x\?x"' Q + vm 'x%xyyy' \# '${P}' yyy 'P="x?x"' + vm 'x%xyyy' \# '${P}' yyy 'P="x?x"' Q + vm 'x%xyyy' \# '"${P}"' 'x%xyyy' 'P="x\?x"' # 35 + vm 'x%xyyy' \# '"${P}"' 'x%xyyy' 'P="x\?x"' Q + vm 'x%xyyy' \# '"${P}"' 'x%xyyy' 'P="x?x"' + vm 'x%xyyy' \# '"${P}"' 'x%xyyy' 'P="x?x"' Q + + vm abc \# '*' abc; vm abc \# '*' abc '' Q # 39 40 + vm abc \# '"*"' abc; vm abc \# '"*"' abc '' Q + vm abc \# '"a"' bc; vm abc \# '"a"' bc '' Q + vm abc \## '*' ''; vm abc \## '*' '' '' Q + vm abc % '*' abc; vm abc % '*' abc '' Q + vm abc %% '*' ''; vm abc %% '*' '' '' Q # 49 50 + vm abc \# '$P' abc 'P="*"'; vm abc \# '$P' abc 'P="*"' Q + vm abc \# '"$P"' abc 'P="*"'; vm abc \# '"$P"' abc 'P="*"' Q + vm abc \# '$P' bc 'P="[a]"'; vm abc \# '$P' bc 'P="[a]"' Q + vm abc \# '"$P"' abc 'P="[a]"'; vm abc \# '"$P"' abc 'P="[a]"' Q + vm '[a]bc' \# '$P' '[a]bc' 'P="[a]"' + vm '[a]bc' \# '"$P"' bc 'P="[a]"' # 60 + vm '[a]bc' \# '"$P"' bc 'P="[a]"' Q + + # The following two (62 & 63) are actually the same test. + # The double \\ turns into a single \ when parsed. + vm '[a]bc' \# '$P' bc 'P="\[a]"'; vm '[a]bc' \# '$P' bc 'P="\\[a]"' + vm '[a]bc' \# '"$P"' '[a]bc' 'P="\[a]"' + vm '\[a]bc' \# '"$P"' bc 'P="\[a]"' # 65 + + vm ababcdabcd \# '[ab]*[ab]' abcdabcd + vm ababcdabcd \## '[ab]*[ab]' cd + vm ababcdabcd \# '$P' abcdabcd 'P="[ab]*[ab]"' + vm ababcdabcd \## '$P' cd "P='[ab]*[ab]'" + vm ababcdabcd \# '$P' 'ab dab d' 'P="[ab]*[ab]"; IFS=c' # 70 + vm ababcdabcd \# '$P' abcdabcd 'P="[ab]*[ab]"; IFS=c' Q + + vm ababcdabcd \# '[ab]*[ba]' abcdabcd + vm ababcdabcd \# '[ab]*[a-b]' abcdabcd + vm ababcdabcd \## '[ba]*[ba]' cd + vm ababcdabcd \## '[a-b]*[ab]' cd # 75 + + vm abcde \# '?[b-d]?' de; vm abcde \## '?[b-d]?' de + vm abcde % '?[b-d]?' ab; vm abcde %% '?[b-d]?' ab + + vm .123. \# '.[0-9][1-8]' 3.; vm .123. % '[0-9][1-8].' .1 # 80 81 + vm .123. \# '?[0-9][1-8]' 3.; vm .123. % '[0-9][1-8]?' .1 + vm .123. \# '*[0-9][1-8]' 3.; vm .123. % '[0-9][1-8]*' .1 # 85 + vm .123. \## '*[0-9][1-8]' .; vm .123. %% '[0-9][1-8]*' . + vm .123. \# '[.][1][2]' 3. ; vm .123. % '[2][3][.]' .1 + vm .123. \# '[?]1[2]' .123. ; vm .123. % '2[3][?]' .123. # 90 91 + vm .123. \# '\.[0-9][1-8]' 3.;vm .123. % '[0-9][1-8]\.' .1 + + vm '[a-c]d-f' \# '[a-c\]' d-f + vm '[abcd]' \# '[[:alpha:]]' '[abcd]' # 95 + vm '[1?234' \# '[[-\]]?\?' 234 + vm '1-2-3-\?' % '-${P}' '1-2-3-\?' 'P="\\?"' + vm '1-2-3-\?' % '${P}' '1-2-3-\' 'P="\\?"' + vm '1-2-3-\?' % '-"${P}"' 1-2-3 'P="\\?"' # 99 + + results +} + + +atf_init_test_cases() { + # Listed here in the order ATF runs them, not the order from above + + atf_add_test_case filename_expansion + atf_add_test_case case_matching + atf_add_test_case var_substring_matching +} diff --git a/bin/sh/t_redir.sh b/bin/sh/t_redir.sh old mode 100755 new mode 100644 --- a/bin/sh/t_redir.sh +++ b/bin/sh/t_redir.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_redir.sh,v 1.9 2016/05/14 00:33:02 kre Exp $ +# $NetBSD: t_redir.sh,v 1.11 2021/05/19 22:43:18 kre Exp $ # # Copyright (c) 2016 The NetBSD Foundation, Inc. # All rights reserved. @@ -326,6 +326,74 @@ "#$T: Incorrect Out2: Should be 'line 1\\nline 2\\nline 3' is '$(cat Out2)'" } +atf_test_case do_redirect_input_output +do_redirect_input_output_head() +{ + atf_set "descr" "Test Input+Output (BiDir) redirections" +} +do_redirect_input_output_body() +{ +nl=' +' + T=0 + i() { T=$(expr "$T" + 1); } + + rm -f Output 2>/dev/null || : + test -f Output && atf_fail "Unable to remove Output file" +#1 + i; atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c '<> Output' + test -f Output || atf_fail "#$T: Did not make Output file" + +#2 + echo data >Output 2>/dev/null || : + i + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + '<>Output' + test -f Output || atf_fail "#$T: Removed Output file" + test -s Output || atf_fail "#$T: Did not keep data in Output file" + test "$(cat Output)" = "data" || + atf_fail "#$T: Incorrect Output: Should be 'data' is '$(cat Output)'" + +#3 + rm -f Output 2>/dev/null || : + i + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'echo Hello 1<>Output' + test -s Output || atf_fail "#$T: Did not keep non-empty Output file" + test "$(cat Output)" = "Hello" || + atf_fail "#$T: Incorrect Output: Should be 'Hello' is '$(cat Output)'" + +#4 + printf data >Output 2>/dev/null || : + i + atf_check -s exit:0 -o inline:'data' -e empty ${TEST_SH} -c \ + 'cat <>Output' + test -f Output || atf_fail "#$T: Removed Output file" + test -s Output || atf_fail "#$T: Did not keep data in Output file" + test "$(cat Output)" = "data" || + atf_fail "#$T: Incorrect Output: Should be 'data' is '$(cat Output)'" + +#5 + echo data >Output 2>/dev/null || : + i + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'echo Hello 1<>Output' + test -s Output || atf_fail "#$T: Did not make non-empty Output file" + test "$(cat Output)" = "Hello" || + atf_fail "#$T: Incorrect Output: Should be 'Hello' is '$(cat Output)'" + +#6 + printf data >Output 2>/dev/null || : + i + atf_check -s exit:0 -o inline:data -e empty ${TEST_SH} -c \ + '{ cat >&3; printf file; } <>Output 3>&1 >&0' + test -f Output || atf_fail "#$T: Removed Output file" + test -s Output || atf_fail "#$T: Did not keep data in Output file" + test "$(cat Output)" = "datafile" || + atf_fail \ + "#$T: Incorrect Output: Should be 'datafile' is '$(cat Output)'" +} + atf_test_case fd_redirections fd_redirections_head() { @@ -583,6 +651,37 @@ test -f '>' || atf_file "File '>' not created when it should" test "$(cat '>')" = 'A Line Output' || atf_fail \ "Output file ('>') contains '$(cat '>')' instead of 'A Line Output'" + + rm -fr OutDir + atf-check -s not-exit:0 -o empty -e not-empty \ + ${TEST_SH} -c ': > OutDir/stdout; printf foo' + atf-check -s not-exit:0 -o empty -e not-empty \ + ${TEST_SH} -c ': > OutDir/stdout || printf foo; printf bar' + atf-check -s exit:0 -o inline:bar -e not-empty \ + ${TEST_SH} -c '> OutDir/stdout; printf bar' + atf-check -s exit:0 -o inline:foobar -e not-empty \ + ${TEST_SH} -c '> OutDir/stdout || printf foo; printf bar' + atf-check -s exit:0 -o inline:bar -e not-empty \ + ${TEST_SH} -c 'command : > OutDir/stdout; printf bar' + atf-check -s exit:0 -o inline:foobar -e not-empty ${TEST_SH} -c \ + 'command : > OutDir/stdout || printf foo; printf bar' + atf-check -s not-exit:0 -o empty -e not-empty \ + ${TEST_SH} -c ': <> OutDir/stdout; printf foo' + + atf-check -s not-exit:0 -o empty -e not-empty \ + ${TEST_SH} -c ': >&8 ; printf foo' + atf-check -s not-exit:0 -o empty -e not-empty \ + ${TEST_SH} -c ': >&8 || printf foo; printf bar' + atf-check -s exit:0 -o inline:bar -e not-empty \ + ${TEST_SH} -c '>&8 ; printf bar' + atf-check -s exit:0 -o inline:foobar -e not-empty \ + ${TEST_SH} -c '>&8 || printf foo; printf bar' + atf-check -s exit:0 -o inline:bar -e not-empty \ + ${TEST_SH} -c 'command : >&7; printf bar' + atf-check -s exit:0 -o inline:foobar -e not-empty ${TEST_SH} -c \ + 'command : >&7 || printf foo; printf bar' + + return 0 } # Many more tests in t_here, so here we have just rudimentary checks @@ -767,11 +866,11 @@ ${TEST_SH} -c ". ./f-def; f ; printf '%s\n' success1" atf_check -s exit:0 -o inline:'success2\n' -e empty \ ${TEST_SH} -c ". ./f-def; f >/dev/null; printf '%s\n' success2" - atf_check -s exit:0 -o inline:'success3\n' -e empty \ + atf_check -s exit:0 -o inline:'success3\n' -e not-empty \ ${TEST_SH} -c ". ./f-def; f >&- ; printf '%s\n' success3" atf_check -s exit:0 -o inline:'In-Func\nsuccess4\n' -e empty \ ${TEST_SH} -c ". ./f-def; f & wait; printf '%s\n' success4" - atf_check -s exit:0 -o inline:'success5\n' -e empty \ + atf_check -s exit:0 -o inline:'success5\n' -e not-empty \ ${TEST_SH} -c ". ./f-def; f >&- & wait; printf '%s\n' success5" atf_check -s exit:0 -o inline:'In-Func\nIn-Func\nsuccess6\n' -e empty \ ${TEST_SH} -c ". ./f-def; f;f; printf '%s\n' success6" @@ -891,6 +990,7 @@ atf_add_test_case basic_test_method_test atf_add_test_case do_input_redirections atf_add_test_case do_output_redirections + atf_add_test_case do_redirect_input_output atf_add_test_case fd_redirections atf_add_test_case local_redirections atf_add_test_case incorrect_redirections diff --git a/bin/sh/t_redircloexec.sh b/bin/sh/t_redircloexec.sh old mode 100755 new mode 100644 --- a/bin/sh/t_redircloexec.sh +++ b/bin/sh/t_redircloexec.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_redircloexec.sh,v 1.3 2016/05/15 15:44:43 kre Exp $ +# $NetBSD: t_redircloexec.sh,v 1.5 2017/05/27 13:11:50 kre Exp $ # # Copyright (c) 2016 The NetBSD Foundation, Inc. # All rights reserved. @@ -39,10 +39,6 @@ echo "echo ${name}2" ">&${fd}" > ./"${name}2" } -runhelper() { - ${TEST_SH} "./${1}1" -} - cleanhelper() { # not really needed, atf cleans up... rm -f ./"${1}1" ./"${1}2" out @@ -54,6 +50,8 @@ } exec_redir_closed_body() { + [ -n "${POSIXLY_CORRECT+set}" ] && atf_skip "tests non-posix behaviour" + mkhelper exec 6 \ "exec 6> out; echo exec1 >&6; ${TEST_SH} exec2; exec 6>&-" @@ -168,6 +166,77 @@ cleanhelper comp } +atf_test_case posix_exec_redir +posix_exec_redir_head() { + atf_set "descr" "Tests that redirections created by exec" \ + " in posix mode are not closed on exec" +} +posix_exec_redir_body() { + + # This test mostly just expects the opposite results than + # exec_redir_closed ... + + # First work out how to get shell into posix mode + POSIX= + + # This should succeed only if "set -o posix" succeeds. + # If it fails, whether it fails and exits the shell, or + # just returns a "false" from set (exit != 0), with or + # without errs on stderr, should not matter + + if ${TEST_SH} -c "set -o posix && exit 0 || exit 1" 2>/dev/null + then + # If we have this method, use it, as we can expect + # this really should mean the shell is in posix mode. + + POSIX='set -o posix;' + + else + # This one is just a guess, and there is no assurance + # that it will do anything at all. What's more, since + # we do not know what the shell being tested does + # differently in posix and non-posix modes, if it + # even has that concept, there's nothing we can test + # to find out. + + # A shell that always operates in posix mode (at least + # with regard to redirects on exec and close-on-exec + # should pass this test, in any case. + + POSIXLY_CORRECT=true ; export POSIXLY_CORRECT + + fi + + mkhelper exec 6 \ + "${POSIX} exec 6> out; echo exec1 >&6; ${TEST_SH} exec2; exec 6>&-" + + atf_check -s exit:0 -o empty -e empty ${TEST_SH} ./exec1 + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -e ./exec1 + + mkhelper exec 9 \ + "${POSIX} exec 9> out; echo exec1 >&9; ${TEST_SH} exec2" + + atf_check -s exit:0 -o empty -e empty ${TEST_SH} ./exec1 + + mkhelper exec 8 \ + "${POSIX}" \ + "exec 8> out; printf OK; echo exec1 >&8;" \ + "printf OK; ${TEST_SH} exec2; printf GOOD" + + atf_check -s exit:0 -o match:OKOKGOOD -e empty \ + ${TEST_SH} -e ./exec1 + + mkhelper exec 7 \ + "${POSIX}" \ + "exec 7> out; printf OK; echo exec1 >&7;" \ + "printf OK; ${TEST_SH} exec2 || printf ERR" + + atf_check -s exit:0 -o match:OKOK -o not-match:ERR -e empty \ + ${TEST_SH} ./exec1 + + cleanhelper exec +} + atf_init_test_cases() { atf_add_test_case exec_redir_closed atf_add_test_case exec_redir_open @@ -175,4 +244,5 @@ atf_add_test_case compound_redir_open atf_add_test_case simple_redir_open atf_add_test_case subshell_redir_open + atf_add_test_case posix_exec_redir } diff --git a/bin/sh/t_set_e.sh b/bin/sh/t_set_e.sh old mode 100755 new mode 100644 diff --git a/bin/sh/t_shift.sh b/bin/sh/t_shift.sh old mode 100755 new mode 100644 diff --git a/bin/sh/t_syntax.sh b/bin/sh/t_syntax.sh new file mode 100644 --- /dev/null +++ b/bin/sh/t_syntax.sh @@ -0,0 +1,1251 @@ +# $NetBSD: t_syntax.sh,v 1.10 2018/11/14 02:37:51 kre Exp $ +# +# Copyright (c) 2017 The NetBSD Foundation, Inc. +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# +: ${TEST_SH:=/bin/sh} + +# This set of tests verifies various requirementgs relating to correct +# (and incorrect) syntax of shell input +# +# There is no intent in these tests to verify correct operation +# (though that sometimes cannot be separated from correct parsing.) +# That is (or should be) verified elsewhere. +# +# Also, some very basic syntax is tested in almost every test +# (they cannot work without basic parsing of elementary commands) +# so that is also not explicitly tested here. +# +# Similarly word expansion, field splitting, redirection, all have +# tests of their own (though we do test parsing of redirect ops). +# +# Note that in order to test the basic facilities, other shell operations +# are used - a test failure here does not necessarily mean that the +# operation intended to be tested is faulty, just that something is. + +atf_test_case a_basic_tokenisation +a_basic_tokenisation_head() { + atf_set "descr" "Test the shell correctly finds various tokens" +} +a_basic_tokenisation_body() { + atf_check -s exit:0 -o 'inline:3\n' -e empty ${TEST_SH} -c \ + 'set -- a b c; echo $#' + atf_check -s exit:0 -o 'inline:2\n' -e empty ${TEST_SH} -c \ + 'set -- a""b c; echo $#' + atf_check -s exit:0 -o 'inline:3\n' -e empty ${TEST_SH} -c \ + 'set -- a"" b c; echo $#' + atf_check -s exit:0 -o 'inline:3\n' -e empty ${TEST_SH} -c \ + 'set -- ""a b c\;; echo $#' + + atf_check -s exit:0 -o 'inline:3\n' -e empty ${TEST_SH} -c \ + 'set -- set -- c; echo $#' + atf_check -s exit:0 -o 'inline:1\n' -e empty ${TEST_SH} -c \ + 'set --;set -- c; echo $#' + atf_check -s exit:0 -o 'inline:1\n' -e empty ${TEST_SH} -c \ + 'set --&set -- c; echo $#' + atf_check -s exit:0 -o 'inline:1\n' -e empty ${TEST_SH} -c \ + 'set -- a b&&set -- c; echo $#' + atf_check -s exit:0 -o 'inline:2\n' -e empty ${TEST_SH} -c \ + 'set -- a b||set -- c; echo $#' +} + +atf_test_case b_comments +b_comments_head() { + atf_set "descr" "Test the shell correctly handles comments" +} +b_comments_body() { + + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c '#' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c '# exit 1' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c 'true # exit 1' + atf_check -s exit:1 -o empty -e empty ${TEST_SH} -c 'false # exit 0' + + atf_check -s exit:0 -o 'inline:foo\n' -e empty ${TEST_SH} -c \ + 'echo foo # bar' + atf_check -s exit:0 -o 'inline:foo # bar\n' -e empty ${TEST_SH} -c \ + 'echo foo \# bar' + atf_check -s exit:0 -o 'inline:foo\n' -e empty ${TEST_SH} -c \ + 'echo foo; # echo bar' + atf_check -s exit:0 -o 'inline:foo#bar\n' -e empty ${TEST_SH} -c \ + 'echo foo#bar' + atf_check -s exit:0 -o 'inline:foo# bar\n' -e empty ${TEST_SH} -c \ + 'echo foo# bar' + atf_check -s exit:0 -o 'inline:foo\n' -e empty ${TEST_SH} -c \ + 'x=foo; echo ${x#bar}' + + atf_check -s exit:0 -o 'inline:#\n' -e empty ${TEST_SH} -c \ + 'echo "#"' + atf_check -s exit:0 -o 'inline:#\n' -e empty ${TEST_SH} -c \ + "echo '#'" + atf_check -s exit:0 -o 'inline:#\n' -e empty ${TEST_SH} -c \ + 'echo \#' + atf_check -s exit:0 -o 'inline:##\n' -e empty ${TEST_SH} -c \ + 'echo "#"#' + atf_check -s exit:0 -o 'inline:##\n' -e empty ${TEST_SH} -c \ + "echo '#'#" + atf_check -s exit:0 -o 'inline:##\n' -e empty ${TEST_SH} -c \ + 'echo \##' + atf_check -s exit:0 -o 'inline:##\n' -e empty ${TEST_SH} -c \ + 'echo "#"# #"#"' + atf_check -s exit:0 -o 'inline:##\n' -e empty ${TEST_SH} -c \ + "echo '#'# #'#'" + atf_check -s exit:0 -o 'inline:##\n' -e empty ${TEST_SH} -c \ + 'echo \## #\#' + + cat <<-'DONE'|atf_check -s exit:0 -o inline:'foo\n' -e empty ${TEST_SH} + # test comments do not provoke synax errors !\ + echo foo # ( { " hello + while : # that's forever + do # the following command list + # starting with nothing ${unset?error} + break # done loop terminate $( echo bar; exit 1 ) + done ##################################################### + # "hello + exit 0 + DONE +} + +atf_test_case c_line_wrapping +c_line_wrapping_head() { + atf_set "descr" "check aspects of command line wrapping" +} +c_line_wrapping_body() { + atf_require_prog ls + atf_require_prog printf + + cat <<- 'DONE' | atf_check -s exit:0 -o ignore -e empty ${TEST_SH} -e + l\ + s + DONE + + cat <<- 'DONE' | atf_check -s exit:7 -o empty -e empty ${TEST_SH} + e\ + x\ + it \ + 7 + DONE + + # Have to do this twice as cannot say "any exit code but 0 or 7" ... + cat <<- 'DONE' | atf_check -s not-exit:0 -o empty -e not-empty \ + ${TEST_SH} + e\ + x\ + it\ + 7 + DONE + cat <<- 'DONE' | atf_check -s not-exit:7 -o empty -e not-empty \ + ${TEST_SH} + e\ + x\ + it\ + 7 + DONE + + cat <<- 'DONE' | atf_check -s exit:0 -o empty -e empty ${TEST_SH} + wh\ + il\ + e \ + f\a\ + \l\s\e + do + :\ + ; + done + DONE + + cat <<- 'DONE' | atf_check -s exit:0 -o inline:'hellohellohellohello' \ + -e empty ${TEST_SH} + V\ + AR=hel\ + lo + unset U V1 + pri\ + ntf '%s' ${\ + VAR\ + } + p\ + r\ + i\ + n\ + t\ + f\ + \ + '%s' \ + $\ + {\ + V\ + A\ + R} + printf '%s' ${U\ + -\ + "$\ + {V\ + 1:\ + =$\ + {V\ + AR+\ + ${V\ + AR}\ + }\ + }"} + printf '%s' ${V\ + 1?V1\ + \ + FAIL} + DONE + + cat <<- 'DONE' | atf_check -s exit:0 -o inline:'2\n' ${TEST_SH} + l\ + s=7 bi\ + n\ + =\ + 3 + echo $(\ + ( ls /bin )\ + ) + DONE + + # Inspired by src/etc/MAKEDEV.tmpl failure with (broken) + # sh LINENO code... avoid it happening again... + for VARS in 1:0:0:0 0:1:0:0 0:0:1:0 0:0:0:1 \ + 1:0:0:1 1:0:1:0 1:1:0:0 0:1:1:0 \ + 0:0:0:0 1:1:0:1 0:1:1:1 1:1:1:1 + do + eval $( + IFS=: + set -- $VARS + test $(( $1 + $2 + $3 + $4 )) -eq 1 && + R=OK || R=BAD + printf "R=%s;" $R + for v in a b c d + do + case $1 in + (0) printf "export %s=false;" $v;; + (1) printf "export %s=true;" $v;; + esac + shift + done + ) + + cat <<- 'DONE' | atf_check -s exit:0 -o inline:"${R}" ${TEST_SH} + case $(( $($a && echo 1 || echo 0) + \ + $($b && echo 1 || echo 0) + \ + $($c && echo 1 || echo 0) + \ + $($d && echo 1 || echo 0) )) + in + (1) printf OK ;; + (*) printf BAD ;; + esac + DONE + done + + # inspired by pkgsrc/pkgtools/cwrappers :: libnbcompat/configure + # failure with (broken) sh LINENO code .. avoid recurrence + # This test would have failed. + cat <<- 'DONE' | atf_check -s exit:0 -o inline:'/tmp\n' ${TEST_SH} + dn=/tmp/foo + + D=`dirname -- "${dn}" || + expr X"${dn}" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"${dn}" : 'X\(//\)[^/]' \| \ + X"${dn}" : 'X\(//\)$' \| \ + X"${dn}" : 'X\(/\)' \| . 2>/dev/null || + echo X"${dn}" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + + echo "${D}" + DONE + return 0 +} + +atf_test_case d_cstrings +d_cstrings_head() { + atf_set "descr" "Check processing of $' ' quoting (C style strings)" +} +d_cstrings_body() { + unset ENV + + if ! ${TEST_SH} -c ": \$'abc'" || + test $( ${TEST_SH} -c "printf %s \$'abc'" ) != abc + then + atf_skip "\$'...' (C style quoted strings) not supported" + fi + + # simple stuff + atf_check -s exit:0 -e empty -o inline:'abc\tdef\n' ${TEST_SH} -c \ + "printf '%s\\n' \$'abc\tdef'" + atf_check -s exit:0 -e empty -o inline:'abc\tdef\n' ${TEST_SH} -c \ + "printf '%s\\n' \$'abc\011def'" + atf_check -s exit:0 -e empty -o inline:'abc\tdef\n' ${TEST_SH} -c \ + "printf '%s\\n' \$'abc\x09'def" + atf_check -s exit:0 -e empty -o inline:'abc$def\n' ${TEST_SH} -c \ + "def=xyz; printf '%s\\n' \$'abc\$def'" + + # control chars (\c) and unicode \u + atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c \ + "test \$'\\1-\\2-\\3' = \$'\\ca-\\cb-\\cc'" + atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c \ + "test \$'\\r-\\n-\\f' = \$'\\cm-\\cj-\\cl'" + atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c \ + "unset LC_ALL; export LC_CTYPE=en_AU.UTF-8; + test \$'\\u0123' = \$'\\304\\243'" + atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c \ + "test \$'\\u0123' = \$'\\xC4\\xA3'" + atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c \ + "test \$'\\c\\\\' = \$'\\x1C'" + atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c \ + "test \$'\\c[\\c]\\c^\\c_\\c?' = \$'\\x1B\\x1D\\x1E\\x1F\\x7F'" + + # all the \X sequences for a single char X (ie: not hex/octal/unicode) + atf_check -s exit:0 -e empty -o inline:' \n\r\t \n' \ + ${TEST_SH} -c "printf '%s\\n' \$'\\a\\b\\e\\f\\n\\r\\t\\v'" + atf_check -s exit:0 -e empty -o inline:' \n\r\t \n' \ + ${TEST_SH} -c "printf '%s\\n' \$'\\cG\\cH\\x1b\\cl\\cJ\\cm\\cI\\ck'" + atf_check -s exit:0 -e empty -o inline:"'"'"\\\n' \ + ${TEST_SH} -c "printf '%s\\n' \$'\\'\\\"\\\\'" + + # various invalid $'...' sequences + atf_check -s not-exit:0 -e not-empty -o ignore ${TEST_SH} -c \ + ": \$'\\q'" + atf_check -s not-exit:0 -e not-empty -o ignore ${TEST_SH} -c \ + ": \$'\\c\\q'" + atf_check -s not-exit:0 -e not-empty -o ignore ${TEST_SH} -c \ + ": \$'\\uDEFF'" + atf_check -s not-exit:0 -e not-empty -o ignore ${TEST_SH} -c \ + ": \$'abcd" + atf_check -s not-exit:0 -e not-empty -o ignore ${TEST_SH} -c \ + ": \$'abcd\\" + + # anything that generates \0 ends the $'...' immediately + atf_check -s exit:0 -e empty -o inline:'aAaA' ${TEST_SH} -c \ + "printf '%s' \$'a\\0x'\$'A\\x00X'\$'a\\c@x'\$'A\\u0000X'" + + # \newline in a $'...' is dropped (just like in "" strings) + atf_check -s exit:0 -e empty -o inline:'abcdef' ${TEST_SH} -c \ +"printf '%s' \$'abc\\ +def'" + # but a normal newline in a $'...' is just a newline + atf_check -s exit:0 -e empty -o inline:'abc\ndef' ${TEST_SH} -c \ +"printf '%s' \$'abc +def'" + # and should work when elided line wrap occurs between $ and ' + atf_check -s exit:0 -e empty -o inline:'abc\ndef' ${TEST_SH} -c \ +"printf '%s' \$\\ +'abc\\ndef'" + + # $'...' only works when the $ is unquoted. + atf_check -s exit:0 -e empty -o inline:"abc\$'def'g" ${TEST_SH} -c \ + "printf '%s' \"abc\$'def'g\"" + atf_check -s exit:0 -e empty -o inline:'abc$defg' ${TEST_SH} -c \ + "printf '%s' abc\\\$'def'g" + atf_check -s exit:0 -e empty -o inline:'abc$def' ${TEST_SH} -c \ + "printf '%s' abc'\$'def" +} + +atf_test_case f_redirects +f_redirects_head() { + atf_set "descr" "Check parsing of redirect operators" +} +f_redirects_body() { + + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + '>/dev/null' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + '>/dev/null' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + '<>/dev/null' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + '/dev/null' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + '>|/dev/null' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + '>/dev/null>/dev/null>/dev/null' + + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'echo hello >/dev/null' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'echo >/dev/null hello' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + '>/dev/null echo hello' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'echo hello >/dev/null world' + atf_check -s exit:0 -o 'inline:hello world\n' -e empty ${TEST_SH} -c \ + 'echo hello &1' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + '2>& 1' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'FD=1; 2>&"${FD}"' + atf_check -s exit:0 -o 'inline:hello\n' -e empty ${TEST_SH} -c \ + 'FD=1; echo hello 2>&"${FD}" >&2' + + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + '2>&- 3<&- 4>&-' + + return 0 +} + +atf_test_case g_variable_syntax +g_variable_syntax_head() { + atf_set "descr" "Check that var names of all legal forms work" +} +g_variable_syntax_body() { + # don't test _ as a variable, it can be "unusual" + for vname in a ab _a _9 a123 a_1_2_3 __ ___ ____ __1__ _0 \ + A AA AAA AaBb _A_a A_a_ a1_ abc_123 ab_12_cd_ef_34_99 \ + abcdefghijklmnopqrstuvwzyz ABCDEFGHIJKLMNOPQRSTUVWXYZ_ \ + A_VERY_LONG_VARIABLE_NAME_that_is_probably_longer_than_most_used_in_the_average_shell_script_already_about_100_chars_in_this_one_but_there_is_not_supposed_to_be_any_limit_on_the_length_at_all \ + Then_Make_it_Even_Longer_by_Multiplying_it___A_VERY_LONG_VARIABLE_NAME_that_is_probably_longer_than_most_used_in_the_average_shell_script_already_about_100_chars_in_this_one_but_there_is_not_supposed_to_be_any_limit_on_the_length_at_all__A_VERY_LONG_VARIABLE_NAME_that_is_probably_longer_than_most_used_in_the_average_shell_script_already_about_100_chars_in_this_one_but_there_is_not_supposed_to_be_any_limit_on_the_length_at_all__A_VERY_LONG_VARIABLE_NAME_that_is_probably_longer_than_most_used_in_the_average_shell_script_already_about_100_chars_in_this_one_but_there_is_not_supposed_to_be_any_limit_on_the_length_at_all__A_VERY_LONG_VARIABLE_NAME_that_is_probably_longer_than_most_used_in_the_average_shell_script_already_about_100_chars_in_this_one_but_there_is_not_supposed_to_be_any_limit_on_the_length_at_all \ + xyzzy __0123454321__ _0_1_2_3_4_5_6_7_8_9_ ABBA X_ Y__ Z___ \ + _____________________________________________________________ + do + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + "unset ${vname}" + atf_check -s exit:0 -o match:OK -e empty ${TEST_SH} -c \ + "unset ${vname}; printf %s \${${vname}-OK}" + atf_check -s exit:0 -o match:GOOD -e empty ${TEST_SH} -c \ + "${vname}=GOOD; printf %s \${${vname}-OK}" + atf_check -s exit:0 -o match:GOOD -e empty ${TEST_SH} -c \ + "${vname}=GOOD; printf %s \$${vname}" + atf_check -s exit:0 -o match:GOOD -e empty ${TEST_SH} -c \ + "unset ${vname};${vname}=GOOD;printf %s \${${vname}-OK}" + atf_check -s exit:0 -o match:OK -e empty ${TEST_SH} -c \ + "${vname}=GOOD;unset ${vname};printf %s \${${vname}-OK}" + atf_check -s exit:0 -o match:GOOD -e empty ${TEST_SH} -c \ + "${vname}=GOOD; unset ${vname}x; printf %s \$${vname}" + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + "unset ${vname}x; ${vname}=GOOD; printf %s \$${vname}x" + atf_check -s exit:0 -o match:GOOD -e empty ${TEST_SH} -c \ + "${vname}=GOOD; ${vname}_=BAD; printf %s \$${vname}" + + case "${vname}" in + ?) continue;; + esac + + # The following tests do not work for 1 char var names. + # hence the check and "continue" above to skip the remaining + # tests for that case + + atf_check -s exit:0 -o match:GOOD -e empty ${TEST_SH} -c \ + "${vname}=GOOD; unset ${vname%?}; printf %s \$${vname}" + + # (this next would work, but becomes just a duplicate of + # an earlier test, so is pointless for 1 ch names) + atf_check -s exit:0 -o match:GOOD -e empty ${TEST_SH} -c \ + "${vname}=GOOD; unset ${vname}x ${vname%?}; printf %s \$${vname}" + + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + "unset ${vname%?};${vname}=GOOD; printf %s \$${vname%?}" + + atf_check -s exit:0 -o match:GOOD -e empty ${TEST_SH} -c \ + "${vname}=GOOD; ${vname%?}=BAD; printf %s \$${vname}" + + # all the remaining tests require the 2nd char of the + # variable name to be a legal first character. That + # is, not a digit, so skip the rest if we have a digit + # second... + case "${vname}" in + ?[0-9]*) continue;; + esac + + atf_check -s exit:0 -o match:GOOD -e empty ${TEST_SH} -c \ + "${vname}=GOOD; unset ${vname#?}; printf %s \$${vname}" + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + "unset ${vname#?};${vname}=GOOD; printf %s \$${vname#?}" + + atf_check -s exit:0 -o match:GOOD -e empty ${TEST_SH} -c \ + "${vname}=GOOD; ${vname#?}=BAD; printf %s \$${vname}" + + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + "unset ${vname%?} ${vname#?} ${vname}x; ${vname}=GOOD; + printf %s \$${vname%?}\$${vname#?}\$${vname}x" + + atf_check -s exit:0 -o match:GOOD -e empty ${TEST_SH} -c \ + "${vname}=GOOD; ${vname%?}=BAD; ${vname}_=BAD; + ${vname#?}=BAD; printf %s \$${vname}" + done + + # don't test '.' in var names, some shells permit that (in ${} anyway) + # this test also cannot check for embedded - + ? = : % or # + for vname in ,x -p +h :def 0_1 'x*x' '()' '"' "'" 'a b c' '?!' ';' + do + atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \ + "echo \${${vname}}" + done + + for vname in ,x -p +h :def 0_1 'x*x' '()' '"' "'" 'a b c' x,y,z '?!' \ + ';' a-b a+b 'a?b' 'a:b' 'a%b' 'a#b' 0 1 99 @ '*' '!' '?' + do + # failure modes will differ, but they should all fail somehow + atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \ + "${vname}=" + done + +} + +atf_test_case h_var_assign +h_var_assign_head() { + atf_set "descr" "Check var assignments " +} +h_var_assign_body() { + atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c \ + 'a=b' + atf_check -s not-exit:0 -e not-empty -o empty ${TEST_SH} -c \ + '\a=b' + atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c \ + 'a=b c=d' + atf_check -s exit:0 -e empty -o 'inline:e=f\n' ${TEST_SH} -c \ + 'a=b c=d echo e=f' + atf_check -s exit:0 -e empty -o 'inline:e=f\n' ${TEST_SH} -c \ + 'a=b 2>/dev/null c=d &1' '5<&3' + do + atf_check -s exit:0 -o inline:'foo\n' -e empty ${TEST_SH} -c \ + "${cmd} | ${xtra} cat" + + atf_check -s exit:0 -o inline:'foo\n' -e empty ${TEST_SH} -c \ + "${cmd} | ${pfx} cat" + + pfx="${pfx} ${xtra}" + done + cmd="${cmd} | ${pfx} cat" + done + + # pipelines are not required to contain commands ... + # they don't do anything useful (at all) but the syntax is legal + base='4>&2'; cmd="${base}" + for pipe in 'a=b' '3<&0' '>>/dev/null' 'a= b= c=' '${x}' 'cat' + do + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + "${base} | ${pipe}" + + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + "${cmd} | ${pipe}" + + cmd="${cmd} | ${pipe}" + done + + # but the command cannot be empty, or a reserved word used improperly + base='printf "%s\n" foo'; cmd="${base}" + for pipe in '' do done then else fi esac + do + atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \ + "${base} | ${pipe}" + + atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \ + "${pipe} | ${base}" + + atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \ + "${cmd} | ${pipe}" + + cmd="${cmd} | ${pipe}" + done +} + +atf_test_case j_and_or_lists +j_and_or_lists_head() { + atf_set "descr" "Check && and || command lists" +} +j_and_or_lists_body() { + and=true + or=false + and_or=false + for i in 1 2 3 4 5 6 7 8 9 10 + do + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + "${and}" + + atf_check -s exit:1 -o empty -e empty ${TEST_SH} -c \ + "${or}" + + atf_check -s exit:1 -o empty -e empty ${TEST_SH} -c \ + "${and_or}" + + and="${and} && true" + or="${or} || false" + and_or="${and_or} || true && false" + done + + atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \ + 'true &&' + atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \ + '&& true' + atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \ + '|| true' + atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \ + 'true ||' + atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \ + 'true || && false' + atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \ + 'false || && true' + + cmd='printf "%s" foo | cat | cat>/dev/null' + line="${cmd}" + for i in 1 2 3 4 + do + line="${line} && ! ${cmd} || ${cmd}" + + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + "${line}" + done + +} + +atf_test_case k_lists +k_lists_head() { + atf_set "descr" "Check ; command lists" +} +k_lists_body() { + line= + for N in 1 2 3 4 + do + for cmd in \ + true false : 'cat/dev/null' x=3 'exec 4>&-' + do + line="${line}${line:+;}${cmd}" + atf_check -s exit:0 -o 'inline:hello\nworld\n' \ + -e empty ${TEST_SH} -c \ + "echo hello; ${line}; echo world" + atf_check -s exit:0 -o 'inline:hello\nworld\n' \ + -e empty ${TEST_SH} -c \ + "echo hello; ${line}; echo world;" + done + done + + for cmd in ';' ';;' 'false ;; true' 'false; ;true' '; true' + do + atf_check -s not-exit:0 -o ignore -e not-empty \ + ${TEST_SH} -c "${cmd}" + done +} + +atf_test_case l_async_lists +l_async_lists_head() { + atf_set "descr" "Check & command lists" +} +l_async_lists_body() { + line= + for N in 1 2 3 4 + do + for cmd in \ + true false : 'cat/dev/null' x=3 'exec 4>&-' + do + line="${line:+${line}&}${cmd}" + atf_check -s exit:0 -o 'inline:hello\nworld\n' \ + -e empty ${TEST_SH} -c \ + "echo hello; ${line}& echo world" + atf_check -s exit:0 -o 'inline:hello\nworld\n' \ + -e empty ${TEST_SH} -c \ + "echo hello; ${line}& echo world" + done + done + + for cmd in '&' ';&' '&;' '& true' 'false& &true' + do + atf_check -s not-exit:0 -o ignore -e not-empty \ + ${TEST_SH} -c "${cmd}" + done +} + +atf_test_case m_compound_lists +m_compound_lists_head() { + atf_set "descr" "Check subshells () and { ;} command grouping" +} +m_compound_lists_body() { + # Note: (( is an unspecified (reserved) operator, don't use it... + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + '( true )' + atf_check -s exit:1 -o empty -e empty ${TEST_SH} -c \ + '( false )' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + '( (:) )' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + '( ( true ))' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + '( ( ( ( ( true )))))' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + '( ( ( ( (true);:));true))' + + atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \ + '()' + atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \ + '( )' + + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + '{ true; }' + atf_check -s exit:1 -o empty -e empty ${TEST_SH} -c \ + '{ false; }' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + '{ { :; };}' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + '{ { { { { :;};};};};}' + + atf_check -s exit:0 -o 'inline:}\n' -e empty ${TEST_SH} -c \ + '{ echo } ; }' + atf_check -s exit:0 -o 'inline:{\n' -e empty ${TEST_SH} -c \ + '{ echo { ; }' +} + +atf_test_case q_for_loop +q_for_loop_head() { + atf_set "descr" "Check for loop parsing" +} +q_for_loop_body() { + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'for x; do : ; done' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'for x in ; do : ; done' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'for x in a b c ; do : ; done' + + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'for x in in;do : ; done' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'for x in for;do : ; done' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'for x in do;do : ; done' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'for for in in;do :;done' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'for for in for;do :; done' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'for for in do;do : ;done' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'for in in in;do : ; done' + atf_check -s exit:0 -o 'inline:do\nin\ndo\n' -e empty ${TEST_SH} -c \ + 'for in in in do in;do case $in in in)echo do;;do)echo in;;esac; done' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'for in in for;do : ; done' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'for in in do;do : ; done' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'for do in in;do : ; done' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'for do in for;do : ; done' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'for do in do;do : ; done' + atf_check -s exit:0 -o 'inline:dodo\n' -e empty ${TEST_SH} -c \ + 'for do in do;do echo ${do}do ; done' +} + +atf_test_case r_case +r_case_head() { + atf_set "descr" "Check case statement parsing" +} +r_case_body() { + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'case x in esac' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'case x in x) esac' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'case x in (x) esac' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'case x in x) ;; esac' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'case x in (x) ;; esac' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'case x in x|y) ;; esac' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'case x in (x|y) ;; esac' + + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'case x in x|esac) ;; esac' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'case x in x|esac|y) ;; esac' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'case x in (x|esac) ;; esac' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'case x in (x|esac|y) ;; esac' + + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'case x in in) esac' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'case x in in) ;; esac' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'case x in x|in) ;; esac' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'case x in x|in|y) ;; esac' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'case x in (x|in) ;; esac' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'case x in (in|x) ;; esac' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'case x in (x|in|y) ;; esac' + + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'case case in case) esac' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'case in in in) esac' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'case esac in (in) esac' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'case in in esac|cat' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'case esac in esac|cat' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'case in in esac|case x in u) echo foo;; esac' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'case esac in esac|case x in u) echo foo;; esac' + atf_check -s exit:0 -o 'inline:foo\n' -e empty ${TEST_SH} -c \ + 'case in in esac|case x in x) echo foo;; esac' + + atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \ + 'case in in (esac|cat' + + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'case x in x );;esac' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'case x in ( x );;esac' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'case x in x | y );;esac' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'case x in ( x | y );;esac' + + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'case x + in + ( x | y ) + + ;; + + + esac + ' +} + +atf_test_case s_if +s_if_head() { + atf_set "descr" "Check if statement parsing" +} +s_if_body() { + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'if :; then :; fi' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'if :; then :; else :; fi' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'if :; then :; elif :; then :; else :; fi' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'if :; then :; elif :; then :; elif :; then :; else :; fi' + + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'if :; then : else :; fi' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'if : then :; then :; fi' + + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'if if :;then :;fi then :;fi' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'if if :;then if :;then :;fi fi;then :;fi' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'if if :;then :;fi;then :;else if :;then :;fi fi' + + for a in true false; do + for b in true false; do + for c in true false; do + + $a && out=a || { + $b && out=b || { + $c && out=c || { + out=d; };};} + + atf_check -s exit:0 -e empty \ + -o "inline:${out}"'\n' \ + ${TEST_SH} -c \ + "if $a; then echo a + elif $b; then echo b + elif $c; then echo c + else echo d + fi" + done + done + done +} + +atf_test_case t_loops +t_loops_head() { + atf_set "descr" "Check while/until loop parsing" +} +t_loops_body() { + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'while false; do :; done' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'while false; do \done; done' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'until :; do :; done' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'until :; do \done; done' + + atf_check -s exit:0 -o 'inline:x\n1\n' -e empty ${TEST_SH} -c \ + ':; while (exit) do echo x; false; done; echo $?' + atf_check -s exit:0 -o 'inline:x\n0\n' -e empty ${TEST_SH} -c \ + 'false; until (exit) do echo x; done; echo $?' +} + +atf_test_case u_case_cont +u_case_cont_head() { + atf_set "descr" "Check case stmt parsing using ;& [optional]" +} +u_case_cont_body() { + + ${TEST_SH} -c 'case x in (x) false ;& (y) : ;; esac' 2>/dev/null || + atf_skip ";& case list terminator unsupported in ${TEST_SH}" + + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'case x in x) ;& esac' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'case x in (x) ;& esac' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'case x in x|y) ;& esac' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'case x in (x|y) ;& esac' + + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'case x in x );&esac' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'case x in ( x );&esac' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'case x in x | y );&esac' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'case x in ( x | y );&esac' + + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'case x in x) ;& (y) esac' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'case x in (x) ;& esac' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'case x in x|y) ;& esac' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'case x in (x|y) ;& esac' +} + +atf_test_case x_functions +x_functions_head() { + atf_set "descr" "Check function definition parsing" +} +x_functions_body() { + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'func() { :; }' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'func() { :; }; func' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'func()(:)' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'func()if :; then false; else true; fi' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'func()while false; do :;done' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'func () for a in b c; do :; done' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'func() case x in (y) esac' + + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'f1() { f2() { :; }; }; f1; f2' + + # f2 should be not found, but f1 clears $? + atf_check -s exit:0 -o empty -e not-empty ${TEST_SH} -c \ + 'f1() { f2() { :; }; }; f2; f1' + + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'f1() { eval "$1() { :; }"; }; f1 f2; f2' +} + +atf_test_case z_PR_48498 +z_PR_48498_head() { + atf_set "descr" "Check for detecting the syntax error from PR bin/48498" +} +z_PR_48498_body() { + + # reserved words/operators that end things, + # were completely ignored after a ';' or '&' + # many of these tests lifted directly from the PR + + atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \ + 'true; fi' + atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \ + 'false; fi' + atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \ + 'false; then echo wut' + atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \ + 'true; then echo wut' + atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \ + 'true; do echo wut' + atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \ + 'true; then' + atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \ + 'true; else' + atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \ + 'true; do' + atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \ + 'true; done' + # { + atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \ + ': ; }' + # ( + atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \ + ':;)' + + atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \ + 'true& fi' + atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \ + 'false& fi' + atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \ + 'false& then echo wut' + atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \ + 'true& then echo wut' + atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \ + 'true& do echo wut' + atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \ + 'true& then' + atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \ + 'true& else' + atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \ + 'true& do' + atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \ + 'true&done' + # { + atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \ + ':&}' + # ( + atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \ + ':&)' +} + +atf_test_case z_PR_52426 +z_PR_52426_head() { + atf_set "descr" "Check for detecting the syntax error from PR bin/52426" +} +z_PR_52426_body() { + # Absoluely anything was permitted as a pattern of a case + # statement, any token (except 'esac') would serve + # What follows are a few "pretty" examples that were accepted. + # The first is the example from the PR + + # Note we use only ;; type case lists, ;& should do the same, but + # only for shells that support it, we do not want the shell to + # object to any of these just because it does not support ;& + + atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \ + 'case x in <|() ;; esac' + + atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \ + 'case x in ((|)) ;; esac' + atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \ + 'case x in _|() ;; esac' + atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \ + 'case x in ()|() ;; esac' + atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \ + 'case x in -|;) ;; esac' + atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \ + 'case x in (;|-) ;; esac' + atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \ + 'case x in ;;|;) ;; esac' + atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \ + 'case x in (|| | ||) ;; esac' + atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \ + 'case x in (<<|>>) ;; esac' + atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \ + 'case x in (&&|&) ;; (|||>&) ;; &) esac' + atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \ + 'case x in (>||<) ;; esac' + atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \ + 'case x in( || | || | || | || | || );; esac' + atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \ + 'case x in (||| ||| ||| ||| ||) ;; esac' + atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \ + 'case x in <> | + ) ;; esac' + + # now check some similar looking cases that are supposed to work + # That is, make sure the fix for the PR does not break valid cases. + + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'case fi in ({|}) ;; (!) ;; esac' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'case esac in ([|]);; (][);; !!!|!!!|!!!|!!!);; esac' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'case then in ({[]]}) ;; (^^);; (^|^);; ([!]);; (-);; esac' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'case while in while );;(if|then|elif|fi);;(do|done);; esac' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'case until in($);;($$);;($4.50);;(1/2);;0.3333);;esac' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'case return in !);; !$);; $!);; !#);; (@);; esac' + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'case break in (/);; (\/);; (/\|/\));; (\\//);; esac' +} + +atf_test_case z_PR_53712 +z_PR_53712_head() { + atf_set "descr" "Check for avoiding the core dump from PR bin/53712" +} +z_PR_53712_body() { + atf_require_prog sysctl + atf_require_prog rm + + # Don't want to have to deal with all the possible ways + # that the systcm might be configured to drop core files... + sysctl -w proc.$$.corename=core || + atf_skip "Unable to set file name for core dump file" + rm -f core + + ${TEST_SH} -c '{ } > out'; S=$? + test -f core && + atf_fail "PR bin/53712: ${TEST_SH} dumps core: status=$S" + test "$S" -lt 128 || + atf_fail "PR bin/53712: ${TEST_SH} reported status $S (core?)" + + # It doesn't matter here whether or not there was an error + # from the empty compound, or whether "out" was created + # just that no core dump appeared, and the shell did not + # exit because of a signal. + + return 0 +} + +atf_init_test_cases() { + atf_add_test_case a_basic_tokenisation + atf_add_test_case b_comments + atf_add_test_case c_line_wrapping + atf_add_test_case d_cstrings + atf_add_test_case f_redirects + atf_add_test_case g_variable_syntax + atf_add_test_case h_var_assign + atf_add_test_case i_pipelines + atf_add_test_case j_and_or_lists + atf_add_test_case k_lists + atf_add_test_case l_async_lists + atf_add_test_case m_compound_lists + atf_add_test_case q_for_loop + atf_add_test_case r_case + atf_add_test_case s_if + atf_add_test_case t_loops + atf_add_test_case u_case_cont + atf_add_test_case x_functions + + atf_add_test_case z_PR_48498 + atf_add_test_case z_PR_52426 + atf_add_test_case z_PR_53712 +} diff --git a/bin/sh/t_ulimit.sh b/bin/sh/t_ulimit.sh old mode 100755 new mode 100644 diff --git a/bin/sh/t_varquote.sh b/bin/sh/t_varquote.sh old mode 100755 new mode 100644 diff --git a/bin/sh/t_varval.sh b/bin/sh/t_varval.sh old mode 100755 new mode 100644 diff --git a/bin/sh/t_wait.sh b/bin/sh/t_wait.sh old mode 100755 new mode 100644 diff --git a/bin/sleep/t_sleep.sh b/bin/sleep/t_sleep.sh old mode 100755 new mode 100644 --- a/bin/sleep/t_sleep.sh +++ b/bin/sleep/t_sleep.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_sleep.sh,v 1.1 2012/03/30 09:27:10 jruoho Exp $ +# $NetBSD: t_sleep.sh,v 1.2 2019/01/21 13:19:18 kre Exp $ # # Copyright (c) 2012 The NetBSD Foundation, Inc. # All rights reserved. @@ -39,6 +39,13 @@ atf_check -s exit:0 -o empty -e empty -x "sleep 0.1" atf_check -s exit:0 -o empty -e empty -x "sleep 0.2" atf_check -s exit:0 -o empty -e empty -x "sleep 0.3" + + # check that '.' as the radix works, even when the + # locale is one which uses something different + atf_check -s exit:0 -o empty -e empty -x "LC_ALL=ru_RU.UTF-8 sleep 0.2" + + # and that it is possible to use the locale's radix char (',' here) + atf_check -s exit:0 -o empty -e empty -x "LC_ALL=ru_RU.UTF-8 sleep 0,2" } atf_test_case hex @@ -49,6 +56,8 @@ hex_body() { atf_check -s exit:0 -o empty -e empty -x "sleep 0x01" + atf_check -s exit:0 -o empty -e empty -x "sleep 0x0.F" + atf_check -s exit:0 -o empty -e empty -x "sleep 0x.B" } atf_test_case nonnumeric @@ -62,6 +71,17 @@ atf_check -s not-exit:0 -o empty -e not-empty -x "sleep xyz" atf_check -s not-exit:0 -o empty -e not-empty -x "sleep x21" atf_check -s not-exit:0 -o empty -e not-empty -x "sleep /3" + atf_check -s not-exit:0 -o empty -e not-empty -x "sleep 3+1" + atf_check -s not-exit:0 -o empty -e not-empty -x "sleep 0xFG" + + # This includes using an invalid radix char for the locale in use + atf_check -s not-exit:0 -o empty -e not-empty -x "LC_ALL=C sleep 3,1" + + # no arg at all (that's non-numeric, right?) + atf_check -s not-exit:0 -o empty -e not-empty -x "sleep" + + # and giving 2 or more args is also invalid, even if they are numeric + atf_check -s not-exit:0 -o empty -e not-empty -x "sleep 1 2" } atf_init_test_cases() { diff --git a/bin/tar/t_tar.sh b/bin/tar/t_tar.sh old mode 100755 new mode 100644 --- a/bin/tar/t_tar.sh +++ b/bin/tar/t_tar.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_tar.sh,v 1.1 2012/03/17 16:33:11 jruoho Exp $ +# $NetBSD: t_tar.sh,v 1.2 2018/11/30 00:53:41 christos Exp $ # # Copyright (c) 2007, 2008 The NetBSD Foundation, Inc. # All rights reserved. @@ -45,7 +45,53 @@ atf_check -s eq:0 -o empty -e empty cmp file1.tar file2.tar } +atf_test_case rd_base256_size +rd_base256_size_head() { + atf_set "descr" "Test extracting an archive whose member size" \ + "is encoded as base-256 number (GNU style)" +} +rd_base256_size_body() { + # prepare random file data for comparison + # take 0x1200CF bytes in order to test that we: + # - handle multiple bytes of size field correctly + # - do not fail on NUL bytes + # - do not fail on char values > 0x80 (with signed char) + dd if=/dev/urandom of=reference.bin bs=1179855 count=1 + # write test archive header + # - filename + printf 'output.bin' > test.tar + # - pad to 100 octets + head -c 90 /dev/zero >> test.tar + # - mode, uid, gid + printf '%07d\0%07d\0%07d\0' 644 177776 177775 >> test.tar + # - size (base-256) + printf '\x80\0\0\0\0\0\0\0\0\x12\x00\xCF' >> test.tar + # - timestamp, checksum + printf '%011d\0%06d\0 0' 13377546642 12460 >> test.tar + # - pad empty linkname (100 octets) + head -c 100 /dev/zero >> test.tar + # - magic, user name + printf 'ustar \0nobody' >> test.tar + # - pad user name field to 32 bytes + head -c 26 /dev/zero >> test.tar + # - group name + printf 'nogroup' >> test.tar + # - pad to full block + head -c 208 /dev/zero >> test.tar + # append file data to the test archive + cat reference.bin >> test.tar + # pad to full block + append two terminating null blocks + head -c 1450 /dev/zero >> test.tar + + # test extracting the test archive + atf_check -s eq:0 -o empty -e empty tar -xf test.tar + + # ensure that output.bin is equal to reference.bin + atf_check -s eq:0 -o empty -e empty cmp output.bin reference.bin +} + atf_init_test_cases() { atf_add_test_case append + atf_add_test_case rd_base256_size } diff --git a/crypto/libcrypto/Makefile b/crypto/libcrypto/Makefile --- a/crypto/libcrypto/Makefile +++ b/crypto/libcrypto/Makefile @@ -1,32 +1,25 @@ -# $NetBSD: Makefile,v 1.9 2012/07/14 04:06:17 christos Exp $ +# $NetBSD: Makefile,v 1.14 2018/09/23 13:34:57 christos Exp $ .include -.if ${MKCRYPTO} != "no" - -SUBDIR+=bf bn cast conf des dh dsa ec ecdh ecdsa engine evp hmac lhash \ - md2 md4 md5 rand rc2 rc4 ripemd rsa sha sha1 srp threads x509v3 +SUBDIR+=bf bn cast conf des dh dsa ec ecdh ecdsa engine evp hmac \ + md2 rc2 rc4 rsa srp threads SUBDIR+=idea mdc2 - -.if ${MKCRYPTO_RC5} != "no" SUBDIR+=rc5 + +.if ${HAVE_OPENSSL} == 10 +SUBDIR += lhash sha x509v3 .endif TESTSDIR= ${TESTSBASE}/crypto/libcrypto +.if ${HAVE_OPENSSL} == 10 TESTS_SH= t_certs +.endif TESTS_SH+= t_ciphers TESTS_SH+= t_hashes TESTS_SH+= t_libcrypto TESTS_SH+= t_pubkey .include - -.else - -NOPROG= - -.include - -.endif diff --git a/crypto/libcrypto/Makefile.inc b/crypto/libcrypto/Makefile.inc --- a/crypto/libcrypto/Makefile.inc +++ b/crypto/libcrypto/Makefile.inc @@ -1,4 +1,4 @@ -# $NetBSD: Makefile.inc,v 1.6 2011/06/11 18:03:17 christos Exp $ +# $NetBSD: Makefile.inc,v 1.10 2018/09/28 23:40:45 christos Exp $ .include .include "../Makefile.inc" @@ -13,16 +13,24 @@ MAN= # empty BINDIR= ${TESTSDIR} -DPADD+= ${LIBCRYPTO} ${LIBCRYPT} -LDADD+= -lcrypto -lcrypt - CWARNFLAGS.clang+= -Wno-format CPPFLAGS+= -DOPENSSL_FIPS CPPFLAGS+= -I${OPENSSLSRC} -I${OPENSSLSRC}/crypto +CPPFLAGS+= -I${OPENSSLSRC}/include -I${OPENSSLSRC}/../include +CPPFLAGS+= -I${OPENSSLSRC}/crypto/include CRYPTODIST= ${NETBSDSRCDIR}/crypto .include "${NETBSDSRCDIR}/crypto/Makefile.openssl" -.PATH: ${OPENSSLSRC}/crypto/${HELPER_DIR} +.if ${HAVE_OPENSSL} != 10 +.PATH: ${OPENSSLSRC}/test ${OPENSSLSRC}/test/testutil +.else +.PATH: ${OPENSSLSRC}/crypto/${HELPER_DIR} +.endif +PROGDPLIBS+= cryptotest ${OPENSSLSRC}/../lib/libcryptotest +PROGDPLIBS+= crypto ${OPENSSLSRC}/../lib/libcrypto +DPADD+= ${LIBCRYPT} +LDADD+= -lcrypt + .include .include diff --git a/crypto/libcrypto/bf/Makefile b/crypto/libcrypto/bf/Makefile --- a/crypto/libcrypto/bf/Makefile +++ b/crypto/libcrypto/bf/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.1 2009/02/13 20:58:14 jmmv Exp $ +# $NetBSD: Makefile,v 1.3 2018/02/08 23:52:26 christos Exp $ HELPER_NAME= bftest HELPER_DIR= bf diff --git a/crypto/libcrypto/bn/div/divtest.c b/crypto/libcrypto/bn/div/divtest.c new file mode 100644 --- /dev/null +++ b/crypto/libcrypto/bn/div/divtest.c @@ -0,0 +1,46 @@ +#include +#include + +static int Rand(void) +{ + unsigned char x[2]; + RAND_bytes(x, 2); + return (x[0] + 2 * x[1]); +} + +static void bug(const char *m, BIGNUM *a, BIGNUM *b) +{ + printf("%s!\na=", m); + BN_print_fp(stdout, a); + printf("\nb="); + BN_print_fp(stdout, b); + printf("\n"); + fflush(stdout); + exit(1); +} + +int +main(int argc, char *argv[]) +{ + BIGNUM *a = BN_new(), *b = BN_new(), *c = BN_new(), *d = BN_new(), + *C = BN_new(), *D = BN_new(); + BN_RECP_CTX *recp = BN_RECP_CTX_new(); + BN_CTX *ctx = BN_CTX_new(); + int i = 0; + + for (i = 0; i < 10000; i++) { + BN_pseudo_rand(a, Rand(), 0, 0); + BN_pseudo_rand(b, Rand(), 0, 0); + if (BN_is_zero(b)) + continue; + + BN_RECP_CTX_set(recp, b, ctx); + if (BN_div(C, D, a, b, ctx) != 1) + bug("BN_div failed", a, b); + if (BN_div_recp(c, d, a, recp, ctx) != 1) + bug("BN_div_recp failed", a, b); + else if (BN_cmp(c, C) != 0 || BN_cmp(c, C) != 0) + bug("mismatch", a, b); + } + exit(0); +} diff --git a/crypto/libcrypto/conf/test.c b/crypto/libcrypto/conf/test.c new file mode 100644 --- /dev/null +++ b/crypto/libcrypto/conf/test.c @@ -0,0 +1,98 @@ +/* crypto/conf/test.c */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * 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 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``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. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +#include +#include +#include +#include + +int +main(void) +{ + LHASH_OF(CONF_VALUE) *conf; + long eline; + char *s; + +#ifdef USE_WIN32 + CONF_set_default_method(CONF_WIN32); +#endif + conf = CONF_load(NULL, "ssleay.cnf", &eline); + if (conf == NULL) { + ERR_load_crypto_strings(); + printf("unable to load configuration, line %ld\n", eline); + ERR_print_errors_fp(stderr); + exit(1); + } + lh_stats((_LHASH *)conf,stdout); + lh_node_stats((_LHASH *)conf,stdout); + lh_node_usage_stats((_LHASH *)conf,stdout); + + s = CONF_get_string(conf, NULL, "init2"); + printf("init2=%s\n", (s == NULL) ? "NULL" : s); + + s = CONF_get_string(conf, NULL, "cipher1"); + printf("cipher1=%s\n", (s == NULL) ? "NULL" : s); + + s = CONF_get_string(conf, "s_client", "cipher1"); + printf("s_client:cipher1=%s\n", (s == NULL) ? "NULL" : s); + + printf("---------------------------- DUMP ------------------------\n"); + CONF_dump_fp(conf, stdout); + + exit(0); +} diff --git a/crypto/libcrypto/ecdh/ecdhtest.c b/crypto/libcrypto/ecdh/ecdhtest.c new file mode 100644 --- /dev/null +++ b/crypto/libcrypto/ecdh/ecdhtest.c @@ -0,0 +1,576 @@ +/* crypto/ecdh/ecdhtest.c */ +/* ==================================================================== + * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED. + * + * The Elliptic Curve Public-Key Crypto Library (ECC Code) included + * herein is developed by SUN MICROSYSTEMS, INC., and is contributed + * to the OpenSSL project. + * + * The ECC Code is licensed pursuant to the OpenSSL open source + * license provided below. + * + * The ECDH software is originally written by Douglas Stebila of + * Sun Microsystems Laboratories. + * + */ +/* ==================================================================== + * Copyright (c) 1998-2003 The OpenSSL Project. 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. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@openssl.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.openssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED 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 OpenSSL PROJECT OR + * ITS 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. + * ==================================================================== + * + * This product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ + +#include +#include +#include + +#include "../e_os.h" + +#include /* for OPENSSL_NO_ECDH */ +#include +#include +#include +#include +#include +#include +#include + +#ifdef OPENSSL_NO_ECDH +int main(int argc, char *argv[]) +{ + printf("No ECDH support\n"); + return (0); +} +#else +# include +# include + +# ifdef OPENSSL_SYS_WIN16 +# define MS_CALLBACK _far _loadds +# else +# define MS_CALLBACK +# endif + +# if 0 +static void MS_CALLBACK cb(int p, int n, void *arg); +# endif + +static const char rnd_seed[] = + "string to make the random number generator think it has entropy"; + +static const int KDF1_SHA1_len = 20; +static void *KDF1_SHA1(const void *in, size_t inlen, void *out, + size_t *outlen) +{ +# ifndef OPENSSL_NO_SHA + if (*outlen < SHA_DIGEST_LENGTH) + return NULL; + else + *outlen = SHA_DIGEST_LENGTH; + return SHA1(in, inlen, out); +# else + return NULL; +# endif +} + +static int test_ecdh_curve(int nid, const char *text, BN_CTX *ctx, BIO *out) +{ + EC_KEY *a = NULL; + EC_KEY *b = NULL; + BIGNUM *x_a = NULL, *y_a = NULL, *x_b = NULL, *y_b = NULL; + char buf[12]; + unsigned char *abuf = NULL, *bbuf = NULL; + int i, alen, blen, aout, bout, ret = 0; + const EC_GROUP *group; + + a = EC_KEY_new_by_curve_name(nid); + b = EC_KEY_new_by_curve_name(nid); + if (a == NULL || b == NULL) + goto err; + + group = EC_KEY_get0_group(a); + + if ((x_a = BN_new()) == NULL) + goto err; + if ((y_a = BN_new()) == NULL) + goto err; + if ((x_b = BN_new()) == NULL) + goto err; + if ((y_b = BN_new()) == NULL) + goto err; + + BIO_puts(out, "Testing key generation with "); + BIO_puts(out, text); +# ifdef NOISY + BIO_puts(out, "\n"); +# else + (void)BIO_flush(out); +# endif + + if (!EC_KEY_generate_key(a)) + goto err; + + if (EC_METHOD_get_field_type(EC_GROUP_method_of(group)) == + NID_X9_62_prime_field) { + if (!EC_POINT_get_affine_coordinates_GFp + (group, EC_KEY_get0_public_key(a), x_a, y_a, ctx)) + goto err; + } +# ifndef OPENSSL_NO_EC2M + else { + if (!EC_POINT_get_affine_coordinates_GF2m(group, + EC_KEY_get0_public_key(a), + x_a, y_a, ctx)) + goto err; + } +# endif +# ifdef NOISY + BIO_puts(out, " pri 1="); + BN_print(out, a->priv_key); + BIO_puts(out, "\n pub 1="); + BN_print(out, x_a); + BIO_puts(out, ","); + BN_print(out, y_a); + BIO_puts(out, "\n"); +# else + BIO_printf(out, " ."); + (void)BIO_flush(out); +# endif + + if (!EC_KEY_generate_key(b)) + goto err; + + if (EC_METHOD_get_field_type(EC_GROUP_method_of(group)) == + NID_X9_62_prime_field) { + if (!EC_POINT_get_affine_coordinates_GFp + (group, EC_KEY_get0_public_key(b), x_b, y_b, ctx)) + goto err; + } +# ifndef OPENSSL_NO_EC2M + else { + if (!EC_POINT_get_affine_coordinates_GF2m(group, + EC_KEY_get0_public_key(b), + x_b, y_b, ctx)) + goto err; + } +# endif + +# ifdef NOISY + BIO_puts(out, " pri 2="); + BN_print(out, b->priv_key); + BIO_puts(out, "\n pub 2="); + BN_print(out, x_b); + BIO_puts(out, ","); + BN_print(out, y_b); + BIO_puts(out, "\n"); +# else + BIO_printf(out, "."); + (void)BIO_flush(out); +# endif + + alen = KDF1_SHA1_len; + abuf = (unsigned char *)OPENSSL_malloc(alen); + aout = + ECDH_compute_key(abuf, alen, EC_KEY_get0_public_key(b), a, KDF1_SHA1); + +# ifdef NOISY + BIO_puts(out, " key1 ="); + for (i = 0; i < aout; i++) { + sprintf(buf, "%02X", abuf[i]); + BIO_puts(out, buf); + } + BIO_puts(out, "\n"); +# else + BIO_printf(out, "."); + (void)BIO_flush(out); +# endif + + blen = KDF1_SHA1_len; + bbuf = (unsigned char *)OPENSSL_malloc(blen); + bout = + ECDH_compute_key(bbuf, blen, EC_KEY_get0_public_key(a), b, KDF1_SHA1); + +# ifdef NOISY + BIO_puts(out, " key2 ="); + for (i = 0; i < bout; i++) { + sprintf(buf, "%02X", bbuf[i]); + BIO_puts(out, buf); + } + BIO_puts(out, "\n"); +# else + BIO_printf(out, "."); + (void)BIO_flush(out); +# endif + + if ((aout < 4) || (bout != aout) || (memcmp(abuf, bbuf, aout) != 0)) { +# ifndef NOISY + BIO_printf(out, " failed\n\n"); + BIO_printf(out, "key a:\n"); + BIO_printf(out, "private key: "); + BN_print(out, EC_KEY_get0_private_key(a)); + BIO_printf(out, "\n"); + BIO_printf(out, "public key (x,y): "); + BN_print(out, x_a); + BIO_printf(out, ","); + BN_print(out, y_a); + BIO_printf(out, "\nkey b:\n"); + BIO_printf(out, "private key: "); + BN_print(out, EC_KEY_get0_private_key(b)); + BIO_printf(out, "\n"); + BIO_printf(out, "public key (x,y): "); + BN_print(out, x_b); + BIO_printf(out, ","); + BN_print(out, y_b); + BIO_printf(out, "\n"); + BIO_printf(out, "generated key a: "); + for (i = 0; i < bout; i++) { + sprintf(buf, "%02X", bbuf[i]); + BIO_puts(out, buf); + } + BIO_printf(out, "\n"); + BIO_printf(out, "generated key b: "); + for (i = 0; i < aout; i++) { + sprintf(buf, "%02X", abuf[i]); + BIO_puts(out, buf); + } + BIO_printf(out, "\n"); +# endif + fprintf(stderr, "Error in ECDH routines\n"); + ret = 0; + } else { +# ifndef NOISY + BIO_printf(out, " ok\n"); +# endif + ret = 1; + } + err: + ERR_print_errors_fp(stderr); + + if (abuf != NULL) + OPENSSL_free(abuf); + if (bbuf != NULL) + OPENSSL_free(bbuf); + if (x_a) + BN_free(x_a); + if (y_a) + BN_free(y_a); + if (x_b) + BN_free(x_b); + if (y_b) + BN_free(y_b); + if (b) + EC_KEY_free(b); + if (a) + EC_KEY_free(a); + return (ret); +} + +/* Keys and shared secrets from RFC 7027 */ + +static const unsigned char bp256_da[] = { + 0x81, 0xDB, 0x1E, 0xE1, 0x00, 0x15, 0x0F, 0xF2, 0xEA, 0x33, 0x8D, 0x70, + 0x82, 0x71, 0xBE, 0x38, 0x30, 0x0C, 0xB5, 0x42, 0x41, 0xD7, 0x99, 0x50, + 0xF7, 0x7B, 0x06, 0x30, 0x39, 0x80, 0x4F, 0x1D +}; + +static const unsigned char bp256_db[] = { + 0x55, 0xE4, 0x0B, 0xC4, 0x1E, 0x37, 0xE3, 0xE2, 0xAD, 0x25, 0xC3, 0xC6, + 0x65, 0x45, 0x11, 0xFF, 0xA8, 0x47, 0x4A, 0x91, 0xA0, 0x03, 0x20, 0x87, + 0x59, 0x38, 0x52, 0xD3, 0xE7, 0xD7, 0x6B, 0xD3 +}; + +static const unsigned char bp256_Z[] = { + 0x89, 0xAF, 0xC3, 0x9D, 0x41, 0xD3, 0xB3, 0x27, 0x81, 0x4B, 0x80, 0x94, + 0x0B, 0x04, 0x25, 0x90, 0xF9, 0x65, 0x56, 0xEC, 0x91, 0xE6, 0xAE, 0x79, + 0x39, 0xBC, 0xE3, 0x1F, 0x3A, 0x18, 0xBF, 0x2B +}; + +static const unsigned char bp384_da[] = { + 0x1E, 0x20, 0xF5, 0xE0, 0x48, 0xA5, 0x88, 0x6F, 0x1F, 0x15, 0x7C, 0x74, + 0xE9, 0x1B, 0xDE, 0x2B, 0x98, 0xC8, 0xB5, 0x2D, 0x58, 0xE5, 0x00, 0x3D, + 0x57, 0x05, 0x3F, 0xC4, 0xB0, 0xBD, 0x65, 0xD6, 0xF1, 0x5E, 0xB5, 0xD1, + 0xEE, 0x16, 0x10, 0xDF, 0x87, 0x07, 0x95, 0x14, 0x36, 0x27, 0xD0, 0x42 +}; + +static const unsigned char bp384_db[] = { + 0x03, 0x26, 0x40, 0xBC, 0x60, 0x03, 0xC5, 0x92, 0x60, 0xF7, 0x25, 0x0C, + 0x3D, 0xB5, 0x8C, 0xE6, 0x47, 0xF9, 0x8E, 0x12, 0x60, 0xAC, 0xCE, 0x4A, + 0xCD, 0xA3, 0xDD, 0x86, 0x9F, 0x74, 0xE0, 0x1F, 0x8B, 0xA5, 0xE0, 0x32, + 0x43, 0x09, 0xDB, 0x6A, 0x98, 0x31, 0x49, 0x7A, 0xBA, 0xC9, 0x66, 0x70 +}; + +static const unsigned char bp384_Z[] = { + 0x0B, 0xD9, 0xD3, 0xA7, 0xEA, 0x0B, 0x3D, 0x51, 0x9D, 0x09, 0xD8, 0xE4, + 0x8D, 0x07, 0x85, 0xFB, 0x74, 0x4A, 0x6B, 0x35, 0x5E, 0x63, 0x04, 0xBC, + 0x51, 0xC2, 0x29, 0xFB, 0xBC, 0xE2, 0x39, 0xBB, 0xAD, 0xF6, 0x40, 0x37, + 0x15, 0xC3, 0x5D, 0x4F, 0xB2, 0xA5, 0x44, 0x4F, 0x57, 0x5D, 0x4F, 0x42 +}; + +static const unsigned char bp512_da[] = { + 0x16, 0x30, 0x2F, 0xF0, 0xDB, 0xBB, 0x5A, 0x8D, 0x73, 0x3D, 0xAB, 0x71, + 0x41, 0xC1, 0xB4, 0x5A, 0xCB, 0xC8, 0x71, 0x59, 0x39, 0x67, 0x7F, 0x6A, + 0x56, 0x85, 0x0A, 0x38, 0xBD, 0x87, 0xBD, 0x59, 0xB0, 0x9E, 0x80, 0x27, + 0x96, 0x09, 0xFF, 0x33, 0x3E, 0xB9, 0xD4, 0xC0, 0x61, 0x23, 0x1F, 0xB2, + 0x6F, 0x92, 0xEE, 0xB0, 0x49, 0x82, 0xA5, 0xF1, 0xD1, 0x76, 0x4C, 0xAD, + 0x57, 0x66, 0x54, 0x22 +}; + +static const unsigned char bp512_db[] = { + 0x23, 0x0E, 0x18, 0xE1, 0xBC, 0xC8, 0x8A, 0x36, 0x2F, 0xA5, 0x4E, 0x4E, + 0xA3, 0x90, 0x20, 0x09, 0x29, 0x2F, 0x7F, 0x80, 0x33, 0x62, 0x4F, 0xD4, + 0x71, 0xB5, 0xD8, 0xAC, 0xE4, 0x9D, 0x12, 0xCF, 0xAB, 0xBC, 0x19, 0x96, + 0x3D, 0xAB, 0x8E, 0x2F, 0x1E, 0xBA, 0x00, 0xBF, 0xFB, 0x29, 0xE4, 0xD7, + 0x2D, 0x13, 0xF2, 0x22, 0x45, 0x62, 0xF4, 0x05, 0xCB, 0x80, 0x50, 0x36, + 0x66, 0xB2, 0x54, 0x29 +}; + +static const unsigned char bp512_Z[] = { + 0xA7, 0x92, 0x70, 0x98, 0x65, 0x5F, 0x1F, 0x99, 0x76, 0xFA, 0x50, 0xA9, + 0xD5, 0x66, 0x86, 0x5D, 0xC5, 0x30, 0x33, 0x18, 0x46, 0x38, 0x1C, 0x87, + 0x25, 0x6B, 0xAF, 0x32, 0x26, 0x24, 0x4B, 0x76, 0xD3, 0x64, 0x03, 0xC0, + 0x24, 0xD7, 0xBB, 0xF0, 0xAA, 0x08, 0x03, 0xEA, 0xFF, 0x40, 0x5D, 0x3D, + 0x24, 0xF1, 0x1A, 0x9B, 0x5C, 0x0B, 0xEF, 0x67, 0x9F, 0xE1, 0x45, 0x4B, + 0x21, 0xC4, 0xCD, 0x1F +}; + +/* Given private value and NID, create EC_KEY structure */ + +static EC_KEY *mk_eckey(int nid, const unsigned char *p, size_t plen) +{ + int ok = 0; + EC_KEY *k = NULL; + BIGNUM *priv = NULL; + EC_POINT *pub = NULL; + const EC_GROUP *grp; + k = EC_KEY_new_by_curve_name(nid); + if (!k) + goto err; + priv = BN_bin2bn(p, plen, NULL); + if (!priv) + goto err; + if (!EC_KEY_set_private_key(k, priv)) + goto err; + grp = EC_KEY_get0_group(k); + pub = EC_POINT_new(grp); + if (!pub) + goto err; + if (!EC_POINT_mul(grp, pub, priv, NULL, NULL, NULL)) + goto err; + if (!EC_KEY_set_public_key(k, pub)) + goto err; + ok = 1; + err: + if (priv) + BN_clear_free(priv); + if (pub) + EC_POINT_free(pub); + if (ok) + return k; + else if (k) + EC_KEY_free(k); + return NULL; +} + +/* + * Known answer test: compute shared secret and check it matches expected + * value. + */ + +static int ecdh_kat(BIO *out, const char *cname, int nid, + const unsigned char *k1, size_t k1_len, + const unsigned char *k2, size_t k2_len, + const unsigned char *Z, size_t Zlen) +{ + int rv = 0; + EC_KEY *key1 = NULL, *key2 = NULL; + unsigned char *Ztmp = NULL; + size_t Ztmplen; + BIO_puts(out, "Testing ECDH shared secret with "); + BIO_puts(out, cname); + key1 = mk_eckey(nid, k1, k1_len); + key2 = mk_eckey(nid, k2, k2_len); + if (!key1 || !key2) + goto err; + Ztmplen = (EC_GROUP_get_degree(EC_KEY_get0_group(key1)) + 7) / 8; + if (Ztmplen != Zlen) + goto err; + Ztmp = OPENSSL_malloc(Ztmplen); + if (!ECDH_compute_key(Ztmp, Ztmplen, + EC_KEY_get0_public_key(key2), key1, 0)) + goto err; + if (memcmp(Ztmp, Z, Zlen)) + goto err; + memset(Ztmp, 0, Zlen); + if (!ECDH_compute_key(Ztmp, Ztmplen, + EC_KEY_get0_public_key(key1), key2, 0)) + goto err; + if (memcmp(Ztmp, Z, Zlen)) + goto err; + rv = 1; + err: + if (key1) + EC_KEY_free(key1); + if (key2) + EC_KEY_free(key2); + if (Ztmp) + OPENSSL_free(Ztmp); + if (rv) + BIO_puts(out, " ok\n"); + else { + fprintf(stderr, "Error in ECDH routines\n"); + ERR_print_errors_fp(stderr); + } + return rv; +} + +# define test_ecdh_kat(bio, curve, bits) \ + ecdh_kat(bio, curve, NID_brainpoolP##bits##r1, \ + bp##bits##_da, sizeof(bp##bits##_da), \ + bp##bits##_db, sizeof(bp##bits##_db), \ + bp##bits##_Z, sizeof(bp##bits##_Z)) + +int main(int argc, char *argv[]) +{ + BN_CTX *ctx = NULL; + int ret = 1; + BIO *out; + + CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON); + +# ifdef OPENSSL_SYS_WIN32 + CRYPTO_malloc_init(); +# endif + + RAND_seed(rnd_seed, sizeof rnd_seed); + + out = BIO_new(BIO_s_file()); + if (out == NULL) + EXIT(1); + BIO_set_fp(out, stdout, BIO_NOCLOSE); + + if ((ctx = BN_CTX_new()) == NULL) + goto err; + + /* NIST PRIME CURVES TESTS */ + if (!test_ecdh_curve + (NID_X9_62_prime192v1, "NIST Prime-Curve P-192", ctx, out)) + goto err; + if (!test_ecdh_curve(NID_secp224r1, "NIST Prime-Curve P-224", ctx, out)) + goto err; + if (!test_ecdh_curve + (NID_X9_62_prime256v1, "NIST Prime-Curve P-256", ctx, out)) + goto err; + if (!test_ecdh_curve(NID_secp384r1, "NIST Prime-Curve P-384", ctx, out)) + goto err; + if (!test_ecdh_curve(NID_secp521r1, "NIST Prime-Curve P-521", ctx, out)) + goto err; +# ifndef OPENSSL_NO_EC2M + /* NIST BINARY CURVES TESTS */ + if (!test_ecdh_curve(NID_sect163k1, "NIST Binary-Curve K-163", ctx, out)) + goto err; + if (!test_ecdh_curve(NID_sect163r2, "NIST Binary-Curve B-163", ctx, out)) + goto err; + if (!test_ecdh_curve(NID_sect233k1, "NIST Binary-Curve K-233", ctx, out)) + goto err; + if (!test_ecdh_curve(NID_sect233r1, "NIST Binary-Curve B-233", ctx, out)) + goto err; + if (!test_ecdh_curve(NID_sect283k1, "NIST Binary-Curve K-283", ctx, out)) + goto err; + if (!test_ecdh_curve(NID_sect283r1, "NIST Binary-Curve B-283", ctx, out)) + goto err; + if (!test_ecdh_curve(NID_sect409k1, "NIST Binary-Curve K-409", ctx, out)) + goto err; + if (!test_ecdh_curve(NID_sect409r1, "NIST Binary-Curve B-409", ctx, out)) + goto err; + if (!test_ecdh_curve(NID_sect571k1, "NIST Binary-Curve K-571", ctx, out)) + goto err; + if (!test_ecdh_curve(NID_sect571r1, "NIST Binary-Curve B-571", ctx, out)) + goto err; +# endif + if (!test_ecdh_kat(out, "Brainpool Prime-Curve brainpoolP256r1", 256)) + goto err; + if (!test_ecdh_kat(out, "Brainpool Prime-Curve brainpoolP384r1", 384)) + goto err; + if (!test_ecdh_kat(out, "Brainpool Prime-Curve brainpoolP512r1", 512)) + goto err; + + ret = 0; + + err: + ERR_print_errors_fp(stderr); + if (ctx) + BN_CTX_free(ctx); + BIO_free(out); + CRYPTO_cleanup_all_ex_data(); + EXIT(ret); + return (ret); +} + +# if 0 +static void MS_CALLBACK cb(int p, int n, void *arg) +{ + char c = '*'; + + if (p == 0) + c = '.'; + if (p == 1) + c = '+'; + if (p == 2) + c = '*'; + if (p == 3) + c = '\n'; + BIO_write((BIO *)arg, &c, 1); + (void)BIO_flush((BIO *)arg); +# ifdef LINT + p = n; +# endif +} +# endif +#endif diff --git a/crypto/libcrypto/evp/Makefile b/crypto/libcrypto/evp/Makefile --- a/crypto/libcrypto/evp/Makefile +++ b/crypto/libcrypto/evp/Makefile @@ -1,8 +1,18 @@ -# $NetBSD: Makefile,v 1.1 2011/06/09 05:25:17 spz Exp $ +# $NetBSD: Makefile,v 1.2 2018/09/23 13:34:57 christos Exp $ HELPER_NAME= evp_test HELPER_DIR= evp -FILES= evptests.txt +FILES= \ +evpcase.txt \ +evpciph.txt \ +evpdigest.txt \ +evpencod.txt \ +evpkdf.txt \ +evpmac.txt \ +evppbe.txt \ +evppkey.txt \ +evppkey_ecc.txt .include +.PATH: ${OPENSSLSRC}/test/recipes/30-test_evp_data diff --git a/crypto/libcrypto/idea/Makefile b/crypto/libcrypto/idea/Makefile --- a/crypto/libcrypto/idea/Makefile +++ b/crypto/libcrypto/idea/Makefile @@ -1,9 +1,8 @@ -# $NetBSD: Makefile,v 1.4 2012/07/14 04:06:17 christos Exp $ +# $NetBSD: Makefile,v 1.5 2018/09/23 13:34:58 christos Exp $ HELPER_NAME= ideatest HELPER_DIR= idea .include -LDADD= -lcrypto LDADD+= -Wl,--no-fatal-warnings diff --git a/crypto/libcrypto/mdc2/Makefile b/crypto/libcrypto/mdc2/Makefile --- a/crypto/libcrypto/mdc2/Makefile +++ b/crypto/libcrypto/mdc2/Makefile @@ -1,9 +1,8 @@ -# $NetBSD: Makefile,v 1.4 2012/07/14 04:06:17 christos Exp $ +# $NetBSD: Makefile,v 1.5 2018/09/23 13:34:58 christos Exp $ HELPER_NAME= mdc2test HELPER_DIR= mdc2 .include -LDADD= -lcrypto LDADD+= -Wl,--no-fatal-warnings diff --git a/crypto/libcrypto/rc5/Makefile b/crypto/libcrypto/rc5/Makefile --- a/crypto/libcrypto/rc5/Makefile +++ b/crypto/libcrypto/rc5/Makefile @@ -1,14 +1,8 @@ -# $NetBSD: Makefile,v 1.3 2011/07/05 10:03:10 spz Exp $ +# $NetBSD: Makefile,v 1.5 2018/09/23 13:34:58 christos Exp $ HELPER_NAME= rc5test HELPER_DIR= rc5 .include -.if ${MKCRYPTO_RC5} != "no" -LDADD= -lcrypto_rc5 -lcrypto -.else -CPPFLAGS+= -DOPENSSL_NO_RC5 -.endif - LDADD+= -Wl,--no-fatal-warnings diff --git a/crypto/libcrypto/t_certs.sh b/crypto/libcrypto/t_certs.sh old mode 100755 new mode 100644 diff --git a/crypto/libcrypto/t_ciphers.sh b/crypto/libcrypto/t_ciphers.sh old mode 100755 new mode 100644 --- a/crypto/libcrypto/t_ciphers.sh +++ b/crypto/libcrypto/t_ciphers.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_ciphers.sh,v 1.4 2012/07/14 16:04:06 spz Exp $ +# $NetBSD: t_ciphers.sh,v 1.8 2019/03/27 21:14:54 gson Exp $ # # Copyright (c) 2008, 2009, 2010 The NetBSD Foundation, Inc. # All rights reserved. @@ -60,10 +60,13 @@ evp_head() { atf_set "descr" "Checks EVP cipher" + atf_set "timeout" "1200" } evp_body() { - atf_check -o ignore -e ignore $(atf_get_srcdir)/h_evp_test $(atf_get_srcdir)/evptests.txt + for i in $(atf_get_srcdir)/evp*.txt; do + atf_check -o ignore -e ignore $(atf_get_srcdir)/h_evp_test $i + done } atf_test_case rc2 @@ -103,9 +106,6 @@ } rc5_body() { - [ -x "$(atf_get_srcdir)/h_rc5test" ] \ - || atf_skip "RC5 support not available; system built" \ - "with MKCRYPTO_RC5=no" atf_check -o ignore -e ignore "$(atf_get_srcdir)/h_rc5test" } diff --git a/crypto/libcrypto/t_hashes.sh b/crypto/libcrypto/t_hashes.sh old mode 100755 new mode 100644 --- a/crypto/libcrypto/t_hashes.sh +++ b/crypto/libcrypto/t_hashes.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_hashes.sh,v 1.2 2012/07/14 16:04:06 spz Exp $ +# $NetBSD: t_hashes.sh,v 1.4 2018/09/24 16:25:24 christos Exp $ # # Copyright (c) 2008, 2009, 2010 The NetBSD Foundation, Inc. # All rights reserved. @@ -45,47 +45,6 @@ atf_check -o ignore -e ignore "$(atf_get_srcdir)/h_md2test" } -atf_test_case md4 -md4_head() -{ - atf_set "descr" "Checks MD4 digest" -} -md4_body() -{ - atf_check -o ignore -e ignore "$(atf_get_srcdir)/h_md4test" -} - -atf_test_case md5 -md5_head() -{ - atf_set "descr" "Checks MD5 digest" -} -md5_body() -{ - atf_check -o ignore -e ignore "$(atf_get_srcdir)/h_md5test" -} - -atf_test_case ripemd -ripemd_head() -{ - atf_set "descr" "Checks RMD-160 digest" -} -ripemd_body() -{ - atf_check -o ignore -e ignore "$(atf_get_srcdir)/h_ripemdtest" -} - -atf_test_case sha -sha_head() -{ - atf_set "descr" "Checks SHA-1 digest" -} -sha_body() -{ - atf_check -o ignore -e ignore "$(atf_get_srcdir)/h_shatest" -} - - atf_test_case mdc2 mdc2_head() { @@ -100,9 +59,5 @@ { atf_add_test_case hmac atf_add_test_case md2 - atf_add_test_case md4 - atf_add_test_case md5 - atf_add_test_case ripemd - atf_add_test_case sha atf_add_test_case mdc2 } diff --git a/crypto/libcrypto/t_libcrypto.sh b/crypto/libcrypto/t_libcrypto.sh old mode 100755 new mode 100644 --- a/crypto/libcrypto/t_libcrypto.sh +++ b/crypto/libcrypto/t_libcrypto.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_libcrypto.sh,v 1.4 2016/10/13 09:25:37 martin Exp $ +# $NetBSD: t_libcrypto.sh,v 1.7 2020/04/11 16:55:33 gson Exp $ # # Copyright (c) 2008, 2009, 2010 The NetBSD Foundation, Inc. # All rights reserved. @@ -35,21 +35,11 @@ atf_check -o ignore -e ignore "$(atf_get_srcdir)/h_enginetest" } -atf_test_case rand -rand_head() -{ - atf_set "descr" "Checks peudo-random number generator" -} -rand_body() -{ - atf_check -o ignore -e ignore "$(atf_get_srcdir)/h_randtest" -} - atf_test_case bn bn_head() { atf_set "descr" "Checks BIGNUM library" - atf_set "timeout" "360" + atf_set "timeout" "720" } bn_body() { @@ -71,17 +61,6 @@ $(atf_get_srcdir)/h_conftest } -atf_test_case lhash -lhash_head() -{ - atf_set "descr" "Checks lhash - dynamic hash tables" -} -lhash_body() -{ - atf_check -o ignore -e ignore -x \ - "echo hoge | $(atf_get_srcdir)/h_lhashtest" -} - atf_test_case threads threads_head() { @@ -99,9 +78,7 @@ atf_init_test_cases() { atf_add_test_case engine - atf_add_test_case rand atf_add_test_case bn atf_add_test_case conf - atf_add_test_case lhash atf_add_test_case threads } diff --git a/crypto/libcrypto/t_pubkey.sh b/crypto/libcrypto/t_pubkey.sh old mode 100755 new mode 100644 --- a/crypto/libcrypto/t_pubkey.sh +++ b/crypto/libcrypto/t_pubkey.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_pubkey.sh,v 1.4 2016/10/13 09:25:37 martin Exp $ +# $NetBSD: t_pubkey.sh,v 1.6 2019/06/16 10:45:50 gson Exp $ # # Copyright (c) 2008, 2009, 2010 The NetBSD Foundation, Inc. # All rights reserved. @@ -60,7 +60,7 @@ ec_head() { atf_set "descr" "Checks EC cipher" - atf_set "timeout" "480" + atf_set "timeout" "960" } ec_body() { @@ -81,7 +81,7 @@ ecdsa_head() { atf_set "descr" "Checks ECDSA algorithm" - atf_set "timeout" "480" + atf_set "timeout" "3000" } ecdsa_body() { diff --git a/crypto/libcrypto/threads/Makefile b/crypto/libcrypto/threads/Makefile --- a/crypto/libcrypto/threads/Makefile +++ b/crypto/libcrypto/threads/Makefile @@ -1,8 +1,7 @@ -# $NetBSD: Makefile,v 1.2 2009/02/14 05:07:54 cube Exp $ +# $NetBSD: Makefile,v 1.5 2018/02/09 03:20:21 christos Exp $ HELPER_NAME= threadstest HELPER_DIR= threads -HELPER_SRCS= mttest.c CPPFLAGS+= -DPTHREADS CFLAGS+= -pthread @@ -16,6 +15,9 @@ FILES+= client.pem FILESNAME_client.pem= d_client.pem + +HELPER_SRCS= ${${HAVE_OPENSSL} == 10 :? mttest.c : threadstest.c} + .include .PATH: ${OPENSSLSRC}/apps diff --git a/crypto/libcrypto/x509v3/Makefile b/crypto/libcrypto/x509v3/Makefile --- a/crypto/libcrypto/x509v3/Makefile +++ b/crypto/libcrypto/x509v3/Makefile @@ -1,7 +1,8 @@ -# $NetBSD: Makefile,v 1.1 2009/02/13 20:58:16 jmmv Exp $ +# $NetBSD: Makefile,v 1.2 2018/02/08 21:59:11 christos Exp $ HELPER_NAME= x509v3test HELPER_DIR= x509v3 HELPER_SRCS= tabtest.c +CPPFLAGS+=-I${OPENSSLSRC}/crypto/x509v3 .include diff --git a/crypto/opencrypto/Makefile b/crypto/opencrypto/Makefile --- a/crypto/opencrypto/Makefile +++ b/crypto/opencrypto/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.2 2014/01/17 22:33:02 pgoyette Exp $ +# $NetBSD: Makefile,v 1.5 2019/12/03 04:20:45 hikaru Exp $ .include @@ -9,11 +9,13 @@ BINDIR= ${TESTSDIR} MKMAN= no +PROGS+= h_aescbc PROGS+= h_aesctr1 PROGS+= h_aesctr2 PROGS+= h_arc4 PROGS+= h_camellia PROGS+= h_cbcdes +PROGS+= h_cbc3des PROGS+= h_comp PROGS+= h_comp_zlib PROGS+= h_comp_zlib_rnd @@ -22,10 +24,11 @@ PROGS+= h_md5hmac PROGS+= h_null PROGS+= h_sha1hmac +PROGS+= h_sha2hmac PROGS+= h_xcbcmac +PROGS+= h_ioctl LDADD.h_comp_zlib+= -lz LDADD.h_comp_zlib_rnd+= -lz .include - diff --git a/crypto/opencrypto/h_aescbc.c b/crypto/opencrypto/h_aescbc.c new file mode 100644 --- /dev/null +++ b/crypto/opencrypto/h_aescbc.c @@ -0,0 +1,163 @@ +/* $NetBSD: h_aescbc.c,v 1.1 2017/04/17 03:59:37 knakahara Exp $ */ + +/*- + * Copyright (c) 2017 Internet Initiative Japan Inc. + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +#include +#include +#include +#include + +#include +#include + +#include + +/* + * Test vectors from RFC 3602 + */ + +const struct { + size_t len; + size_t key_len; + unsigned char key[16]; + unsigned char iv[16]; + unsigned char plaintx[64]; + unsigned char ciphertx[64]; +} tests[] = { + /* Case #1: Encrypting 16 bytes (1 block) using AES-CBC with 128-bit key */ + { 16, 16, + { 0x06, 0xa9, 0x21, 0x40, 0x36, 0xb8, 0xa1, 0x5b, + 0x51, 0x2e, 0x03, 0xd5, 0x34, 0x12, 0x00, 0x06, }, + { 0x3d, 0xaf, 0xba, 0x42, 0x9d, 0x9e, 0xb4, 0x30, + 0xb4, 0x22, 0xda, 0x80, 0x2c, 0x9f, 0xac, 0x41, }, + "Single block msg", + { 0xe3, 0x53, 0x77, 0x9c, 0x10, 0x79, 0xae, 0xb8, + 0x27, 0x08, 0x94, 0x2d, 0xbe, 0x77, 0x18, 0x1a, }, + }, + + /* Case #2: Encrypting 32 bytes (2 blocks) using AES-CBC with 128-bit key */ + { 32, 16, + { 0xc2, 0x86, 0x69, 0x6d, 0x88, 0x7c, 0x9a, 0xa0, + 0x61, 0x1b, 0xbb, 0x3e, 0x20, 0x25, 0xa4, 0x5a, }, + { 0x56, 0x2e, 0x17, 0x99, 0x6d, 0x09, 0x3d, 0x28, + 0xdd, 0xb3, 0xba, 0x69, 0x5a, 0x2e, 0x6f, 0x58, }, + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, }, + { 0xd2, 0x96, 0xcd, 0x94, 0xc2, 0xcc, 0xcf, 0x8a, + 0x3a, 0x86, 0x30, 0x28, 0xb5, 0xe1, 0xdc, 0x0a, + 0x75, 0x86, 0x60, 0x2d, 0x25, 0x3c, 0xff, 0xf9, + 0x1b, 0x82, 0x66, 0xbe, 0xa6, 0xd6, 0x1a, 0xb1, }, + }, + + /* Case #3: Encrypting 48 bytes (3 blocks) using AES-CBC with 128-bit key */ + { 48, 16, + { 0x6c, 0x3e, 0xa0, 0x47, 0x76, 0x30, 0xce, 0x21, + 0xa2, 0xce, 0x33, 0x4a, 0xa7, 0x46, 0xc2, 0xcd, }, + { 0xc7, 0x82, 0xdc, 0x4c, 0x09, 0x8c, 0x66, 0xcb, + 0xd9, 0xcd, 0x27, 0xd8, 0x25, 0x68, 0x2c, 0x81, }, + "This is a 48-byte message (exactly 3 AES blocks)", + { 0xd0, 0xa0, 0x2b, 0x38, 0x36, 0x45, 0x17, 0x53, + 0xd4, 0x93, 0x66, 0x5d, 0x33, 0xf0, 0xe8, 0x86, + 0x2d, 0xea, 0x54, 0xcd, 0xb2, 0x93, 0xab, 0xc7, + 0x50, 0x69, 0x39, 0x27, 0x67, 0x72, 0xf8, 0xd5, + 0x02, 0x1c, 0x19, 0x21, 0x6b, 0xad, 0x52, 0x5c, + 0x85, 0x79, 0x69, 0x5d, 0x83, 0xba, 0x26, 0x84, }, + }, + + /* Case #4: Encrypting 64 bytes (4 blocks) using AES-CBC with 128-bit key */ + { 64, 16, + { 0x56, 0xe4, 0x7a, 0x38, 0xc5, 0x59, 0x89, 0x74, + 0xbc, 0x46, 0x90, 0x3d, 0xba, 0x29, 0x03, 0x49, }, + { 0x8c, 0xe8, 0x2e, 0xef, 0xbe, 0xa0, 0xda, 0x3c, + 0x44, 0x69, 0x9e, 0xd7, 0xdb, 0x51, 0xb7, 0xd9, }, + { 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, }, + { 0xc3, 0x0e, 0x32, 0xff, 0xed, 0xc0, 0x77, 0x4e, + 0x6a, 0xff, 0x6a, 0xf0, 0x86, 0x9f, 0x71, 0xaa, + 0x0f, 0x3a, 0xf0, 0x7a, 0x9a, 0x31, 0xa9, 0xc6, + 0x84, 0xdb, 0x20, 0x7e, 0xb0, 0xef, 0x8e, 0x4e, + 0x35, 0x90, 0x7a, 0xa6, 0x32, 0xc3, 0xff, 0xdf, + 0x86, 0x8b, 0xb7, 0xb2, 0x9d, 0x3d, 0x46, 0xad, + 0x83, 0xce, 0x9f, 0x9a, 0x10, 0x2e, 0xe9, 0x9d, + 0x49, 0xa5, 0x3e, 0x87, 0xf4, 0xc3, 0xda, 0x55, + }, + }, +}; + +int +main(void) +{ + int fd, res; + size_t i; + struct session_op cs; + struct crypt_op co; + unsigned char buf[64]; + + for (i = 0; i < __arraycount(tests); i++) { + fd = open("/dev/crypto", O_RDWR, 0); + if (fd < 0) + err(1, "open %zu", i); + memset(&cs, 0, sizeof(cs)); + cs.cipher = CRYPTO_AES_CBC; + cs.keylen = tests[i].key_len; + cs.key = __UNCONST(&tests[i].key); + res = ioctl(fd, CIOCGSESSION, &cs); + if (res < 0) + err(1, "CIOCGSESSION %zu", i); + + memset(&co, 0, sizeof(co)); + co.ses = cs.ses; + co.op = COP_ENCRYPT; + co.len = tests[i].len; + co.src = __UNCONST(&tests[i].plaintx); + co.dst = buf; + co.dst_len = sizeof(buf); + co.iv = __UNCONST(&tests[i].iv); + res = ioctl(fd, CIOCCRYPT, &co); + if (res < 0) + err(1, "CIOCCRYPT %zu", i); + + if (memcmp(co.dst, tests[i].ciphertx, tests[i].len)) { + size_t j; + for (j = 0; j < tests[i].len; j++) + printf("0x%2zu: 0x%2x 0x%2x\n", j, + buf[j], tests[i].ciphertx[j]); + errx(1, "verification failed %zu", i); + } + close(fd); + } + return 0; +} diff --git a/crypto/opencrypto/h_cbc3des.c b/crypto/opencrypto/h_cbc3des.c new file mode 100644 --- /dev/null +++ b/crypto/opencrypto/h_cbc3des.c @@ -0,0 +1,103 @@ +/* $NetBSD: h_cbc3des.c,v 1.1 2017/04/17 03:59:37 knakahara Exp $ */ + +/*- + * Copyright (c) 2017 Internet Initiative Japan Inc. + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +#include +#include +#include + +#include +#include + +#include + +unsigned char key[] = + "abcdefgh" \ + "ijklmnop" \ + "qrstuvwx"; +unsigned char iv[8] = {0}; +char plaintx[16] = "1234567890123456"; + +const unsigned char ciphertx[16] = { + 0xe0, 0xb9, 0xe7, 0x20, 0x6c, 0xc7, 0xb0, 0x24, + 0xfa, 0xfc, 0x46, 0x1b, 0xad, 0xc1, 0xef, 0x4e, +}; + +int +main(void) +{ + int fd, res; + struct session_op cs; + struct crypt_op co, co2; + unsigned char buf[16], buf2[16]; + + fd = open("/dev/crypto", O_RDWR, 0); + if (fd < 0) + err(1, "open"); + memset(&cs, 0, sizeof(cs)); + cs.cipher = CRYPTO_3DES_CBC; + cs.keylen = 24; + cs.key = key; + res = ioctl(fd, CIOCGSESSION, &cs); + if (res < 0) + err(1, "CIOCGSESSION"); + + memset(&co, 0, sizeof(co)); + memset(&buf, 0, sizeof(buf)); + co.ses = cs.ses; + co.op = COP_ENCRYPT; + co.len = sizeof(plaintx); + co.src = plaintx; + co.dst = buf; + co.dst_len = sizeof(buf); + co.iv = iv; + res = ioctl(fd, CIOCCRYPT, &co); + if (res < 0) + err(1, "CIOCCRYPT encrypto"); + + if (memcmp(co.dst, ciphertx, sizeof(ciphertx))) + errx(1, "encrypto verification failed"); + + memset(&co2, 0, sizeof(co2)); + memset(&buf2, 0, sizeof(buf2)); + co2.ses = cs.ses; + co2.op = COP_DECRYPT; + co2.len = sizeof(buf); + co2.src = buf; + co2.dst = buf2; + co2.dst_len = sizeof(buf2); + co2.iv = iv; + res = ioctl(fd, CIOCCRYPT, &co2); + if (res < 0) + err(1, "CIOCCRYPT decrypto"); + + if (memcmp(co2.dst, plaintx, sizeof(plaintx))) + errx(1, "decrypto verification failed"); + + return 0; +} diff --git a/crypto/opencrypto/h_ioctl.c b/crypto/opencrypto/h_ioctl.c new file mode 100644 --- /dev/null +++ b/crypto/opencrypto/h_ioctl.c @@ -0,0 +1,400 @@ +/* $NetBSD: h_ioctl.c,v 1.3 2017/06/14 21:43:02 christos Exp $ */ + +/*- + * Copyright (c) 2017 Internet Initiative Japan Inc. + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +#include +#include +#include +#include + +#include +#include +#include + +#include + +/* copy from h_aescbc.c */ +#define AES_KEY_LEN 16 +unsigned char aes_key[AES_KEY_LEN] = +{ 0x06, 0xa9, 0x21, 0x40, 0x36, 0xb8, 0xa1, 0x5b, + 0x51, 0x2e, 0x03, 0xd5, 0x34, 0x12, 0x00, 0x06, }; + +#define AES_IV_LEN 16 +unsigned char aes_iv[AES_IV_LEN] = +{ 0x3d, 0xaf, 0xba, 0x42, 0x9d, 0x9e, 0xb4, 0x30, + 0xb4, 0x22, 0xda, 0x80, 0x2c, 0x9f, 0xac, 0x41, }; + +#define AES_PLAINTX_LEN 64 +unsigned char aes_plaintx[AES_PLAINTX_LEN] = "Single block msg"; + +#define AES_CIPHER_LEN 64 +unsigned char aes_cipher[AES_CIPHER_LEN] = +{ 0xe3, 0x53, 0x77, 0x9c, 0x10, 0x79, 0xae, 0xb8, + 0x27, 0x08, 0x94, 0x2d, 0xbe, 0x77, 0x18, 0x1a, }; + +#define COUNT 2 + +/* + * CRIOGET is deprecated. + */ + +/* + * CIOCNGSESSION + * Hmm, who uses? (1) + */ +static int +test_ngsession(int fd) +{ + int ret; + struct crypt_sgop sg; + struct session_n_op css[COUNT]; + + for (size_t i = 0; i < COUNT; i++) { + struct session_n_op *cs = &css[i]; + + memset(cs, 0, sizeof(*cs)); + cs->cipher = CRYPTO_AES_CBC; + cs->keylen = AES_KEY_LEN; + cs->key = __UNCONST(&aes_key); + } + memset(&sg, 0, sizeof(sg)); + sg.count = COUNT; + sg.sessions = css; + + ret = ioctl(fd, CIOCNGSESSION, &sg); + if (ret < 0) + fprintf(stderr, "failed: CIOCNGSESSION\n"); + + return ret; +} + +/* + * CIOCNFSESSION + * Hmm, who uses? (2) + */ +static int +test_nfsession(int fd) +{ + int ret; + struct crypt_sfop sf; + u_int32_t sids[COUNT]; + + memset(sids, 0, sizeof(sids)); + memset(&sf, 0, sizeof(sf)); + sf.count = COUNT; + sf.sesid = sids; + + ret = ioctl(fd, CIOCNFSESSION, &sf); + if (ret < 0) + fprintf(stderr, "failed: CIOCNFSESSION\n"); + + return ret; +} + +/* + * CIOCNCRYPTM + * Hmm, who uses? (3) + */ +static int +test_ncryptm(int fd) +{ + int ret; + struct crypt_mop mop; + struct crypt_n_op css[COUNT]; + + for (size_t i = 0; i < COUNT; i++) { + struct crypt_n_op *cs; + cs = &css[i]; + + memset(cs, 0, sizeof(*cs)); + cs->ses = 0; /* session id */ + cs->op = COP_ENCRYPT; + /* XXX */ + } + + memset(&mop, 0, sizeof(mop)); + mop.count = COUNT; + mop.reqs = css; + + ret = ioctl(fd, CIOCNCRYPTM, &mop); + if (ret < 0) + fprintf(stderr, "failed: CIOCNCRYPTM\n"); + + return ret; +} + +/* + * CIOCNCRYPTRETM + * Hmm, who uses? (4) + */ +static int +test_ncryptretm(int fd) +{ + int ret; + struct session_op cs; + + struct crypt_mop mop; + struct crypt_n_op cnos[COUNT]; + unsigned char cno_dst[COUNT][AES_CIPHER_LEN]; + struct cryptret cret; + struct crypt_result crs[COUNT]; + + memset(&cs, 0, sizeof(cs)); + cs.cipher = CRYPTO_AES_CBC; + cs.keylen = AES_KEY_LEN; + cs.key = __UNCONST(&aes_key); + ret = ioctl(fd, CIOCGSESSION, &cs); + if (ret < 0) { + fprintf(stderr, "failed: CIOCGSESSION\n"); + return ret; + } + + for (size_t i = 0; i < COUNT; i++) { + struct crypt_n_op *cno = &cnos[i]; + + memset(cno, 0, sizeof(*cno)); + cno->ses = cs.ses; + cno->op = COP_ENCRYPT; + cno->len = AES_PLAINTX_LEN; + cno->src = aes_plaintx; + cno->dst_len = AES_CIPHER_LEN; + cno->dst = cno_dst[i]; + } + + memset(&mop, 0, sizeof(mop)); + mop.count = COUNT; + mop.reqs = cnos; + ret = ioctl(fd, CIOCNCRYPTM, &mop); + if (ret < 0) + fprintf(stderr, "failed: CIOCNCRYPTM\n"); + + for (size_t i = 0; i < COUNT; i++) { + struct crypt_result *cr = &crs[i]; + + memset(cr, 0, sizeof(*cr)); + cr->reqid = cnos[i].reqid; + } + + memset(&cret, 0, sizeof(cret)); + cret.count = COUNT; + cret.results = crs; + ret = ioctl(fd, CIOCNCRYPTRETM, &cret); + if (ret < 0) + fprintf(stderr, "failed: CIOCNCRYPTRETM\n"); + + return ret; +} + +/* + * CIOCNCRYPTRET + * Hmm, who uses? (5) + */ +/* test when it does not request yet. */ +static int +test_ncryptret_noent(int fd) +{ + int ret; + struct crypt_result cr; + + memset(&cr, 0, sizeof(cr)); + + ret = ioctl(fd, CIOCNCRYPTRET, &cr); + if (ret == 0) { + fprintf(stderr, + "failed: CIOCNCRYPTRET unexpected success when no entry\n"); + ret = -1; + } else if (errno == EINPROGRESS) { + /* expected fail */ + ret = 0; + } + + return ret; +} + +static int +test_ncryptret_ent(int fd) +{ + int ret; + struct session_op cs; + + struct crypt_mop mop; + struct crypt_n_op cno; + unsigned char cno_dst[AES_CIPHER_LEN]; + + struct crypt_result cr; + + memset(&cs, 0, sizeof(cs)); + cs.cipher = CRYPTO_AES_CBC; + cs.keylen = AES_KEY_LEN; + cs.key = __UNCONST(&aes_key); + ret = ioctl(fd, CIOCGSESSION, &cs); + if (ret < 0) { + fprintf(stderr, "failed: CIOCGSESSION\n"); + return ret; + } + + memset(&cno, 0, sizeof(cno)); + cno.ses = cs.ses; + cno.op = COP_ENCRYPT; + cno.len = AES_PLAINTX_LEN; + cno.src = aes_plaintx; + cno.dst_len = AES_CIPHER_LEN; + cno.dst = cno_dst; + + memset(&mop, 0, sizeof(mop)); + mop.count = 1; + mop.reqs = &cno; + ret = ioctl(fd, CIOCNCRYPTM, &mop); + if (ret < 0) + fprintf(stderr, "failed: CIOCNCRYPTM\n"); + + memset(&cr, 0, sizeof(cr)); + cr.reqid = cno.reqid; + + ret = ioctl(fd, CIOCNCRYPTRET, &cr); + if (ret < 0) + fprintf(stderr, "failed: CIOCNCRYPTRET\n"); + + return ret; +} + +static int +test_ncryptret(int fd) +{ + int ret; + + ret = test_ncryptret_noent(fd); + if (ret < 0) + return ret; + + ret = test_ncryptret_ent(fd); + if (ret < 0) + return ret; + + return ret; +} + +/* + * CIOCASYMFEAT + */ +static int +set_userasymcrypto(int new, int *old) +{ + int ret; + + ret = sysctlbyname("kern.userasymcrypto", NULL, NULL, &new, sizeof(new)); + if (ret < 0) { + fprintf(stderr, "failed: kern.userasymcrypto=%d", new); + return ret; + } + + if (old != NULL) + *old = new; + + return ret; +} + +static int +test_asymfeat_each(int fd, u_int32_t *asymfeat, int userasym) +{ + int ret; + + ret = ioctl(fd, CIOCASYMFEAT, asymfeat); + if (ret < 0) + fprintf(stderr, "failed: CIOCASYMFEAT when userasym=%d\n", userasym); + + return ret; +} + +static int +test_asymfeat(int fd) +{ + int ret, new, orig; + u_int32_t asymfeat = 0; + + /* test for kern.userasymcrypto=1 */ + new = 1; + ret = set_userasymcrypto(new, &orig); + if (ret < 0) + return ret; + ret = test_asymfeat_each(fd, &asymfeat, new); + if (ret < 0) + return ret; + + /* test for kern.userasymcrypto=0 */ + new = 0; + ret = set_userasymcrypto(new, NULL); + if (ret < 0) + return ret; + ret = test_asymfeat_each(fd, &asymfeat, new); + if (ret < 0) + return ret; + + /* cleanup */ + ret = set_userasymcrypto(orig, NULL); + if (ret < 0) + fprintf(stderr, "failed: cleanup kern.userasymcrypto\n"); + + return ret; +} + +int +main(void) +{ + int fd, ret; + + fd = open("/dev/crypto", O_RDWR, 0); + if (fd < 0) + err(1, "open"); + + ret = test_ngsession(fd); + if (ret < 0) + err(1, "test_ngsession"); + + ret = test_nfsession(fd); + if (ret < 0) + err(1, "test_ngsession"); + + ret = test_ncryptm(fd); + if (ret < 0) + err(1, "test_ncryptm"); + + test_ncryptretm(fd); + if (ret < 0) + err(1, "test_ncryptretm"); + + ret = test_ncryptret(fd); + if (ret < 0) + err(1, "test_ncryptret"); + + ret = test_asymfeat(fd); + if (ret < 0) + err(1, "test_asymfeat"); + + return 0; +} diff --git a/crypto/opencrypto/h_sha2hmac.c b/crypto/opencrypto/h_sha2hmac.c new file mode 100644 --- /dev/null +++ b/crypto/opencrypto/h_sha2hmac.c @@ -0,0 +1,268 @@ +/* $NetBSD: h_sha2hmac.c,v 1.1 2019/12/03 04:20:45 hikaru Exp $ */ + +/*- + * Copyright (c) 2014 The NetBSD Foundation, Inc. + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +#include +#include +#include + +#include +#include + +#include + +/* Test data from RFC4868 */ +const struct { + int num; + int alg; + size_t key_len; + size_t len; + size_t mac_len; + unsigned char key[80]; + unsigned char data[80]; + unsigned char mac[80]; +} tests[] = { + { 1, CRYPTO_SHA2_256_HMAC, 32, 8, 16, + { 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b }, + "Hi There", + { 0x19, 0x8a, 0x60, 0x7e, 0xb4, 0x4b, 0xfb, 0xc6, + 0x99, 0x03, 0xa0, 0xf1, 0xcf, 0x2b, 0xbd, 0xc5 } + }, + { 2, CRYPTO_SHA2_256_HMAC, 32, 28, 16, + "JefeJefeJefeJefe" + "JefeJefeJefeJefe", + "what do ya want " + "for nothing?", + { 0x16, 0x7f, 0x92, 0x85, 0x88, 0xc5, 0xcc, 0x2e, + 0xef, 0x8e, 0x30, 0x93, 0xca, 0xa0, 0xe8, 0x7c } + }, + { 3, CRYPTO_SHA2_256_HMAC, 32, 50, 16, + { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa }, + { 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd }, + { 0xcd, 0xcb, 0x12, 0x20, 0xd1, 0xec, 0xcc, 0xea, + 0x91, 0xe5, 0x3a, 0xba, 0x30, 0x92, 0xf9, 0x62 } + }, + { 4, CRYPTO_SHA2_256_HMAC, 32, 50, 16, + { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20 }, + { 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd }, + { 0x37, 0x2e, 0xfc, 0xf9, 0xb4, 0x0b, 0x35, 0xc2, + 0x11, 0x5b, 0x13, 0x46, 0x90, 0x3d, 0x2e, 0xf4 } + }, + { 5, CRYPTO_SHA2_384_HMAC, 48, 8, 24, + { 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b }, + "Hi There", + { 0xb6, 0xa8, 0xd5, 0x63, 0x6f, 0x5c, 0x6a, 0x72, + 0x24, 0xf9, 0x97, 0x7d, 0xcf, 0x7e, 0xe6, 0xc7, + 0xfb, 0x6d, 0x0c, 0x48, 0xcb, 0xde, 0xe9, 0x73 } + }, + { 6, CRYPTO_SHA2_384_HMAC, 48, 28, 24, + "JefeJefeJefeJefe" + "JefeJefeJefeJefe" + "JefeJefeJefeJefe", + "what do ya want " + "for nothing?", + { 0x2c, 0x73, 0x53, 0x97, 0x4f, 0x18, 0x42, 0xfd, + 0x66, 0xd5, 0x3c, 0x45, 0x2c, 0xa4, 0x21, 0x22, + 0xb2, 0x8c, 0x0b, 0x59, 0x4c, 0xfb, 0x18, 0x4d } + }, + { 7, CRYPTO_SHA2_384_HMAC, 48, 50, 24, + { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa }, + { 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd }, + { 0x80, 0x9f, 0x43, 0x9b, 0xe0, 0x02, 0x74, 0x32, + 0x1d, 0x4a, 0x53, 0x86, 0x52, 0x16, 0x4b, 0x53, + 0x55, 0x4a, 0x50, 0x81, 0x84, 0xa0, 0xc3, 0x16 } + }, + { 8, CRYPTO_SHA2_384_HMAC, 48, 50, 24, + { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, + 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, + 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19 }, + { 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd }, + { 0x5b, 0x54, 0x00, 0x85, 0xc6, 0xe6, 0x35, 0x80, + 0x96, 0x53, 0x2b, 0x24, 0x93, 0x60, 0x9e, 0xd1, + 0xcb, 0x29, 0x8f, 0x77, 0x4f, 0x87, 0xbb, 0x5c } + }, + { 9, CRYPTO_SHA2_512_HMAC, 64, 8, 32, + { 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b }, + "Hi There", + { 0x63, 0x7e, 0xdc, 0x6e, 0x01, 0xdc, 0xe7, 0xe6, + 0x74, 0x2a, 0x99, 0x45, 0x1a, 0xae, 0x82, 0xdf, + 0x23, 0xda, 0x3e, 0x92, 0x43, 0x9e, 0x59, 0x0e, + 0x43, 0xe7, 0x61, 0xb3, 0x3e, 0x91, 0x0f, 0xb8 } + }, + { 10, CRYPTO_SHA2_512_HMAC, 64, 28, 32, + "JefeJefeJefeJefe" + "JefeJefeJefeJefe" + "JefeJefeJefeJefe" + "JefeJefeJefeJefe", + "what do ya want " + "for nothing?", + { 0xcb, 0x37, 0x09, 0x17, 0xae, 0x8a, 0x7c, 0xe2, + 0x8c, 0xfd, 0x1d, 0x8f, 0x47, 0x05, 0xd6, 0x14, + 0x1c, 0x17, 0x3b, 0x2a, 0x93, 0x62, 0xc1, 0x5d, + 0xf2, 0x35, 0xdf, 0xb2, 0x51, 0xb1, 0x54, 0x54 } + }, + { 11, CRYPTO_SHA2_512_HMAC, 64, 50, 32, + { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa }, + { 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd }, + { 0x2e, 0xe7, 0xac, 0xd7, 0x83, 0x62, 0x4c, 0xa9, + 0x39, 0x87, 0x10, 0xf3, 0xee, 0x05, 0xae, 0x41, + 0xb9, 0xf9, 0xb0, 0x51, 0x0c, 0x87, 0xe4, 0x9e, + 0x58, 0x6c, 0xc9, 0xbf, 0x96, 0x17, 0x33, 0xd8 + } + }, + { 12, CRYPTO_SHA2_512_HMAC, 64, 50, 32, + { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, + 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, + 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, + 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, + 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40 }, + { 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd }, + { 0x5e, 0x66, 0x88, 0xe5, 0xa3, 0xda, 0xec, 0x82, + 0x6c, 0xa3, 0x2e, 0xae, 0xa2, 0x24, 0xef, 0xf5, + 0xe7, 0x00, 0x62, 0x89, 0x47, 0x47, 0x0e, 0x13, + 0xad, 0x01, 0x30, 0x25, 0x61, 0xba, 0xb1, 0x08 } + }, +}; + +int +main(void) +{ + size_t i; + int fd, res; + struct session_op cs; + struct crypt_op co; + unsigned char buf[80]; + + fd = open("/dev/crypto", O_RDWR, 0); + if (fd < 0) + err(1, "open"); + for (i = 0; i < __arraycount(tests); i++) { + memset(&cs, 0, sizeof(cs)); + cs.mac = tests[i].alg; + cs.mackeylen = tests[i].key_len; + cs.mackey = __UNCONST(&tests[i].key); + res = ioctl(fd, CIOCGSESSION, &cs); + if (res < 0) + err(1, "CIOCGSESSION test %d", tests[i].num); + + memset(&co, 0, sizeof(co)); + memset(buf, 0, sizeof(buf)); + co.ses = cs.ses; + co.op = COP_ENCRYPT; + co.len = tests[i].len; + co.src = __UNCONST(&tests[i].data); + co.mac = buf; + res = ioctl(fd, CIOCCRYPT, &co); + if (res < 0) + err(1, "CIOCCRYPT test %d", tests[i].num); + + /* compare with trailing zeros */ + if (memcmp(co.mac, &tests[i].mac, sizeof(tests[i].mac))) + errx(1, "verification failed test %d", tests[i].num); + + res = ioctl(fd, CIOCFSESSION, &cs.ses); + if (res < 0) + err(1, "CIOCFSESSION test %d", tests[i].num); + } + return 0; +} diff --git a/crypto/opencrypto/t_opencrypto.sh b/crypto/opencrypto/t_opencrypto.sh old mode 100755 new mode 100644 --- a/crypto/opencrypto/t_opencrypto.sh +++ b/crypto/opencrypto/t_opencrypto.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_opencrypto.sh,v 1.6 2015/12/26 07:10:03 pgoyette Exp $ +# $NetBSD: t_opencrypto.sh,v 1.9 2019/12/03 04:20:45 hikaru Exp $ # # Copyright (c) 2014 The NetBSD Foundation, Inc. # All rights reserved. @@ -113,6 +113,19 @@ common_cleanup } +atf_test_case cbc3des cleanup +cbc3des_head() { + common_head "Test 3DES_CBC crypto" +} + +cbc3des_body() { + common_body h_cbc3des +} + +cbc3des_cleanup() { + common_cleanup +} + atf_test_case comp cleanup comp_head() { common_head "Test GZIP_COMP Compression" @@ -178,6 +191,19 @@ common_cleanup } +atf_test_case aescbc cleanup +aescbc_head() { + common_head "Test AES_CBC crypto" +} + +aescbc_body() { + common_body h_aescbc +} + +aescbc_cleanup() { + common_cleanup +} + atf_test_case gcm cleanup gcm_head() { common_head "Test AES_GCM_16 crypto" @@ -243,6 +269,19 @@ common_cleanup } +atf_test_case sha2_hmac cleanup +sha2_hmac_head() { + common_head "Test SHA2_HMAC crypto" +} + +sha2_hmac_body() { + common_body h_sha2hmac +} + +sha2_hmac_cleanup() { + common_cleanup +} + atf_test_case xcbcmac cleanup xcbcmac_head() { common_head "Test XCBC_MAC_96 crypto" @@ -256,21 +295,38 @@ common_cleanup } +atf_test_case ioctl cleanup +ioctl_head() { + common_head "Test ioctl for /dev/crypto" +} + +ioctl_body() { + common_body h_ioctl +} + +ioctl_cleanup() { + common_cleanup +} + atf_init_test_cases() { RUMP_SERVER="unix://t_opencrypto_socket" ; export RUMP_SERVER atf_add_test_case arc4 atf_add_test_case camellia atf_add_test_case cbcdes + atf_add_test_case cbc3des atf_add_test_case comp atf_add_test_case comp_deflate atf_add_test_case comp_zlib_rnd atf_add_test_case aesctr1 atf_add_test_case aesctr2 + atf_add_test_case aescbc atf_add_test_case gcm atf_add_test_case md5 atf_add_test_case md5_hmac atf_add_test_case null atf_add_test_case sha1_hmac + atf_add_test_case sha2_hmac atf_add_test_case xcbcmac + atf_add_test_case ioctl } diff --git a/dev/Makefile.inc b/dev/Makefile.inc new file mode 100644 --- /dev/null +++ b/dev/Makefile.inc @@ -0,0 +1,2 @@ +.include "../Makefile.inc" +CPPFLAGS+=-D_KERNTYPES diff --git a/dev/audio/Makefile b/dev/audio/Makefile --- a/dev/audio/Makefile +++ b/dev/audio/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.5 2016/01/23 21:22:48 christos Exp $ +# $NetBSD: Makefile,v 1.9 2020/03/02 04:25:08 isaki Exp $ # .include @@ -6,23 +6,30 @@ TESTSDIR= ${TESTSBASE}/dev/audio FILESDIR= ${TESTSDIR} -TESTS_SH= t_pad +PROGS+= audiotest +PROGS+= h_pad + +SRCS.audiotest= audiotest.c +LDADD.audiotest+= -lutil + +SRCS.h_pad= h_pad.c + +TESTS_SH+= t_audio +TESTS_SH+= t_pad BINDIR= ${TESTSDIR} MKMAN=no -PROGS= h_pad - CPPFLAGS+= -D_KERNTYPES -LDADD+= -lrumpdev_pad -lrumpdev_audio -lrumpdev -lrumpvfs -LDADD+= -lrump -LDADD+= -lrumpuser -LDADD+= -lrump -LDADD+= -lpthread - -FILES= t_pad_output.bz2.uue +LDADD+= -lrumpdev_pad -lrumpdev_audio -lrumpdev ${LIBRUMPBASE} WARNS= 4 NOMAN= +CLEANFILES+= t_audio.sh + +t_audio.sh: t_audio.awk audiotest.c + ${_MKTARGET_CREATE} + ${TOOL_AWK} -f ${.ALLSRC} > ${.TARGET} + .include diff --git a/dev/audio/audiotest.c b/dev/audio/audiotest.c new file mode 100644 --- /dev/null +++ b/dev/audio/audiotest.c @@ -0,0 +1,6696 @@ +/* $NetBSD: audiotest.c,v 1.15 2021/08/21 09:59:46 andvar Exp $ */ + +/* + * Copyright (C) 2019 Tetsuya Isaki. 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 ``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 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 +__RCSID("$NetBSD: audiotest.c,v 1.15 2021/08/21 09:59:46 andvar Exp $"); + +#include +#include +#define __STDC_FORMAT_MACROS /* for PRIx64 */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if !defined(NO_RUMP) +#include +#include +#endif + +#if !defined(AUDIO_ENCODING_SLINEAR_NE) +#if BYTE_ORDER == LITTLE_ENDIAN +#define AUDIO_ENCODING_SLINEAR_NE AUDIO_ENCODING_SLINEAR_LE +#define AUDIO_ENCODING_ULINEAR_NE AUDIO_ENCODING_ULINEAR_LE +#define AUDIO_ENCODING_SLINEAR_OE AUDIO_ENCODING_SLINEAR_BE +#define AUDIO_ENCODING_ULINEAR_OE AUDIO_ENCODING_ULINEAR_BE +#else +#define AUDIO_ENCODING_SLINEAR_NE AUDIO_ENCODING_SLINEAR_BE +#define AUDIO_ENCODING_ULINEAR_NE AUDIO_ENCODING_ULINEAR_BE +#define AUDIO_ENCODING_SLINEAR_OE AUDIO_ENCODING_SLINEAR_LE +#define AUDIO_ENCODING_ULINEAR_OE AUDIO_ENCODING_ULINEAR_LE +#endif +#endif + +struct testentry { + const char *name; + void (*func)(void); +}; + +void usage(void) __dead; +void xp_err(int, int, const char *, ...) __printflike(3, 4) __dead; +void xp_errx(int, int, const char *, ...) __printflike(3, 4) __dead; +bool match(const char *, const char *); +void xxx_close_wait(void); +int mixer_get_outputs_master(int); +void do_test(int); +int rump_or_open(const char *, int); +int rump_or_write(int, const void *, size_t); +int rump_or_read(int, void *, size_t); +int rump_or_ioctl(int, u_long, void *); +int rump_or_close(int); +int rump_or_fcntl(int, int, ...); +int rump_or_poll(struct pollfd *, nfds_t, int); +int rump_or_kqueue(void); +int rump_or_kevent(int, const struct kevent *, size_t, + struct kevent *, size_t, const struct timespec *); +int hw_canplay(void); +int hw_canrec(void); +int hw_bidir(void); +int hw_fulldup(void); +void init(int); +void *consumer_thread(void *); +void cleanup_audiofd(void); +void TEST(const char *, ...) __printflike(1, 2); +bool xp_fail(int, const char *, ...) __printflike(2, 3); +void xp_skip(int, const char *, ...) __printflike(2, 3); +bool xp_eq(int, int, int, const char *); +bool xp_eq_str(int, const char *, const char *, const char *); +bool xp_ne(int, int, int, const char *); +bool xp_if(int, bool, const char *); +bool xp_sys_eq(int, int, int, const char *); +bool xp_sys_ok(int, int, const char *); +bool xp_sys_ng(int, int, int, const char *); +bool xp_sys_ptr(int, int, void *, const char *); +int debug_open(int, const char *, int); +int debug_write(int, int, const void *, size_t); +int debug_read(int, int, void *, size_t); +int debug_ioctl(int, int, u_long, const char *, void *, const char *, ...) + __printflike(6, 7); +int debug_fcntl(int, int, int, const char *, ...) __printflike(4, 5); +int debug_close(int, int); +void *debug_mmap(int, void *, size_t, int, int, int, off_t); +int debug_munmap(int, void *, int); +const char *event_tostr(int); +int debug_poll(int, struct pollfd *, int, int); +int debug_kqueue(int); +int debug_kevent_set(int, int, const struct kevent *, size_t); +int debug_kevent_poll(int, int, struct kevent *, size_t, + const struct timespec *); +void debug_kev(int, const char *, const struct kevent *); +uid_t debug_getuid(int); +int debug_seteuid(int, uid_t); +int debug_sysctlbyname(int, const char *, void *, size_t *, const void *, + size_t); + +int openable_mode(void); +int mode2aumode(int); +int mode2play(int); +int mode2rec(int); +void reset_after_mmap(void); + +/* from audio.c */ +static const char *encoding_names[] __unused = { + "none", + AudioEmulaw, + AudioEalaw, + "pcm16", + "pcm8", + AudioEadpcm, + AudioEslinear_le, + AudioEslinear_be, + AudioEulinear_le, + AudioEulinear_be, + AudioEslinear, + AudioEulinear, + AudioEmpeg_l1_stream, + AudioEmpeg_l1_packets, + AudioEmpeg_l1_system, + AudioEmpeg_l2_stream, + AudioEmpeg_l2_packets, + AudioEmpeg_l2_system, + AudioEac3, +}; + +int debug; +int props; +int hwfull; +int netbsd; +bool opt_atf; +char testname[64]; +int testcount; +int failcount; +int skipcount; +int unit; +bool use_rump; +bool use_pad; +bool exact_match; +int padfd; +int maxfd; +pthread_t th; +char devicename[16]; /* "audioN" */ +char devaudio[16]; /* "/dev/audioN" */ +char devsound[16]; /* "/dev/soundN" */ +char devaudioctl[16]; /* "/dev/audioctlN" */ +char devmixer[16]; /* "/dev/mixerN" */ +extern struct testentry testtable[]; + +void +usage(void) +{ + fprintf(stderr, "usage:\t%s [] [...]\n", + getprogname()); + fprintf(stderr, "\t-A : make output suitable for ATF\n"); + fprintf(stderr, "\t-a : Test all\n"); + fprintf(stderr, "\t-d : Increase debug level\n"); + fprintf(stderr, "\t-e : Use exact match for testnames " + "(default is forward match)\n"); + fprintf(stderr, "\t-l : List all tests\n"); + fprintf(stderr, "\t-p : Open pad\n"); +#if !defined(NO_RUMP) + fprintf(stderr, "\t-R : Use rump (implies -p)\n"); +#endif + fprintf(stderr, "\t-u : Use audio (default:0)\n"); + exit(1); +} + +/* Customized err(3) */ +void +xp_err(int code, int line, const char *fmt, ...) +{ + va_list ap; + int backup_errno; + + backup_errno = errno; + printf("%s %d: ", (opt_atf ? "Line" : " ERROR:"), line); + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); + printf(": %s\n", strerror(backup_errno)); + + exit(code); +} + +/* Customized errx(3) */ +void +xp_errx(int code, int line, const char *fmt, ...) +{ + va_list ap; + + printf("%s %d: ", (opt_atf ? "Line" : " ERROR:"), line); + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); + printf("\n"); + + exit(code); +} + +int +main(int argc, char *argv[]) +{ + int i; + int j; + int c; + enum { + CMD_TEST, + CMD_ALL, + CMD_LIST, + } cmd; + bool found; + + props = -1; + hwfull = 0; + unit = 0; + cmd = CMD_TEST; + use_pad = false; + padfd = -1; + exact_match = false; + + while ((c = getopt(argc, argv, "AadelpRu:")) != -1) { + switch (c) { + case 'A': + opt_atf = true; + break; + case 'a': + cmd = CMD_ALL; + break; + case 'd': + debug++; + break; + case 'e': + exact_match = true; + break; + case 'l': + cmd = CMD_LIST; + break; + case 'p': + use_pad = true; + break; + case 'R': +#if !defined(NO_RUMP) + use_rump = true; + use_pad = true; +#else + usage(); +#endif + break; + case 'u': + unit = atoi(optarg); + break; + default: + usage(); + } + } + argc -= optind; + argv += optind; + + if (cmd == CMD_LIST) { + /* List all */ + for (i = 0; testtable[i].name != NULL; i++) + printf("%s\n", testtable[i].name); + return 0; + } + + init(unit); + + if (cmd == CMD_ALL) { + /* Test all */ + if (argc > 0) + usage(); + for (i = 0; testtable[i].name != NULL; i++) + do_test(i); + } else { + /* Test only matched */ + if (argc == 0) + usage(); + + found = false; + for (j = 0; j < argc; j++) { + for (i = 0; testtable[i].name != NULL; i++) { + if (match(argv[j], testtable[i].name)) { + do_test(i); + found = true; + } + } + } + if (!found) { + printf("test not found\n"); + exit(1); + } + } + + if (opt_atf == false) { + printf("Result: %d tests, %d success", + testcount, + testcount - failcount - skipcount); + if (failcount > 0) + printf(", %d failed", failcount); + if (skipcount > 0) + printf(", %d skipped", skipcount); + printf("\n"); + } + + if (skipcount > 0) + return 2; + if (failcount > 0) + return 1; + + return 0; +} + +bool +match(const char *arg, const char *name) +{ + if (exact_match) { + /* Exact match */ + if (strcmp(arg, name) == 0) + return true; + } else { + /* Forward match */ + if (strncmp(arg, name, strlen(arg)) == 0) + return true; + } + return false; +} + +/* + * XXX + * Some hardware drivers (e.g. hdafg(4)) require a little "rest" between + * close(2) and re-open(2). + * audio(4) uses hw_if->close() to tell the hardware to close. However, + * there is no agreement to wait for completion between MI and MD layer. + * audio(4) immediately shifts the "closed" state, and that is, the next + * open() will be acceptable immediately in audio layer. But the real + * hardware may not have been closed actually at that point. + * It's troublesome issue but should be fixed... + * + * However, the most frequently used pad(4) (for ATF tests) doesn't have + * such problem, so avoids it to reduce time. + */ +void +xxx_close_wait(void) +{ + + if (!use_pad) + usleep(500 * 1000); +} + +void +do_test(int testnumber) +{ + /* Sentinel */ + strlcpy(testname, "", sizeof(testname)); + /* Do test */ + testtable[testnumber].func(); + + cleanup_audiofd(); + xxx_close_wait(); +} + +/* + * system call wrappers for rump. + */ + +/* open(2) or rump_sys_open(3) */ +int +rump_or_open(const char *filename, int flag) +{ + int r; + +#if !defined(NO_RUMP) + if (use_rump) + r = rump_sys_open(filename, flag); + else +#endif + r = open(filename, flag); + + if (r > maxfd) + maxfd = r; + return r; +} + +/* write(2) or rump_sys_write(3) */ +int +rump_or_write(int fd, const void *buf, size_t len) +{ + int r; + +#if !defined(NO_RUMP) + if (use_rump) + r = rump_sys_write(fd, buf, len); + else +#endif + r = write(fd, buf, len); + return r; +} + +/* read(2) or rump_sys_read(3) */ +int +rump_or_read(int fd, void *buf, size_t len) +{ + int r; + +#if !defined(NO_RUMP) + if (use_rump) + r = rump_sys_read(fd, buf, len); + else +#endif + r = read(fd, buf, len); + return r; +} + +/* ioctl(2) or rump_sys_ioctl(3) */ +int +rump_or_ioctl(int fd, u_long cmd, void *arg) +{ + int r; + +#if !defined(NO_RUMP) + if (use_rump) + r = rump_sys_ioctl(fd, cmd, arg); + else +#endif + r = ioctl(fd, cmd, arg); + return r; +} + +/* close(2) or rump_sys_close(3) */ +int +rump_or_close(int fd) +{ + int r; + +#if !defined(NO_RUMP) + if (use_rump) + r = rump_sys_close(fd); + else +#endif + r = close(fd); + + /* maxfd-1 may not valid fd but no matter */ + if (fd == maxfd) + maxfd--; + return r; +} + +/* fcntl(2) or rump_sys_fcntl(3) */ +/* XXX Supported only with no arguments for now */ +int +rump_or_fcntl(int fd, int cmd, ...) +{ + int r; + +#if !defined(NO_RUMP) + if (use_rump) + r = rump_sys_fcntl(fd, cmd); + else +#endif + r = fcntl(fd, cmd); + return r; +} + +/* poll(2) or rump_sys_poll(3) */ +int +rump_or_poll(struct pollfd *fds, nfds_t nfds, int timeout) +{ + int r; + +#if !defined(NO_RUMP) + if (use_rump) + r = rump_sys_poll(fds, nfds, timeout); + else +#endif + r = poll(fds, nfds, timeout); + return r; +} + +/* kqueue(2) or rump_sys_kqueue(3) */ +int +rump_or_kqueue(void) +{ + int r; + +#if !defined(NO_RUMP) + if (use_rump) + r = rump_sys_kqueue(); + else +#endif + r = kqueue(); + return r; +} + +/* kevent(2) or rump_sys_kevent(3) */ +int +rump_or_kevent(int kq, const struct kevent *chlist, size_t nch, + struct kevent *evlist, size_t nev, + const struct timespec *timeout) +{ + int r; + +#if !defined(NO_RUMP) + if (use_rump) + r = rump_sys_kevent(kq, chlist, nch, evlist, nev, timeout); + else +#endif + r = kevent(kq, chlist, nch, evlist, nev, timeout); + return r; +} + +int +hw_canplay(void) +{ + return (props & AUDIO_PROP_PLAYBACK) ? 1 : 0; +} + +int +hw_canrec(void) +{ + return (props & AUDIO_PROP_CAPTURE) ? 1 : 0; +} + +int +hw_bidir(void) +{ + return hw_canplay() & hw_canrec(); +} + +int +hw_fulldup(void) +{ + return (props & AUDIO_PROP_FULLDUPLEX) ? 1 : 0; +} + +#define DPRINTF(fmt...) do { \ + if (debug) \ + printf(fmt); \ +} while (0) + +#define DPRINTFF(line, fmt...) do { \ + if (debug) { \ + printf(" > %d: ", line); \ + DPRINTF(fmt); \ + fflush(stdout); \ + } \ +} while (0) + +#define DRESULT(r) do { \ + int backup_errno = errno; \ + if (r == -1) { \ + DPRINTF(" = %d, err#%d %s\n", \ + r, backup_errno, \ + strerror(backup_errno)); \ + } else { \ + DPRINTF(" = %d\n", r); \ + } \ + errno = backup_errno; \ + return r; \ +} while (0) + +/* pointer variants for mmap */ +#define DRESULT_PTR(r) do { \ + int backup_errno = errno; \ + if (r == (void *)-1) { \ + DPRINTF(" = -1, err#%d %s\n", \ + backup_errno, \ + strerror(backup_errno)); \ + } else { \ + DPRINTF(" = %p\n", r); \ + } \ + errno = backup_errno; \ + return r; \ +} while (0) + + +/* + * requnit < 0: Use auto by pad (not implemented). + * requnit >= 0: Use audio. + */ +void +init(int requnit) +{ + struct audio_device devinfo; + size_t len; + int rel; + int fd; + int r; + + /* XXX */ + atexit(cleanup_audiofd); + + if (requnit < 0) { + xp_errx(1, __LINE__, "requnit < 0 not implemented."); + } else { + unit = requnit; + } + + /* Set device name */ + snprintf(devicename, sizeof(devicename), "audio%d", unit); + snprintf(devaudio, sizeof(devaudio), "/dev/audio%d", unit); + snprintf(devsound, sizeof(devsound), "/dev/sound%d", unit); + snprintf(devaudioctl, sizeof(devaudioctl), "/dev/audioctl%d", unit); + snprintf(devmixer, sizeof(devmixer), "/dev/mixer%d", unit); + + /* + * version + * audio2 is merged in 8.99.39. + */ + len = sizeof(rel); + r = sysctlbyname("kern.osrevision", &rel, &len, NULL, 0); + if (r == -1) + xp_err(1, __LINE__, "sysctl kern.osrevision"); + netbsd = rel / 100000000; + if (rel >= 899003900) + netbsd = 9; + +#if !defined(NO_RUMP) + if (use_rump) { + DPRINTF(" use rump\n"); + rump_init(); + } +#endif + + /* + * Open pad device before all accesses (including /dev/audioctl). + */ + if (use_pad) { + padfd = rump_or_open("/dev/pad0", O_RDONLY); + if (padfd == -1) + xp_err(1, __LINE__, "rump_or_open"); + + /* Create consumer thread */ + pthread_create(&th, NULL, consumer_thread, NULL); + /* Set this thread's name */ + pthread_setname_np(pthread_self(), "main", NULL); + } + + /* + * Get device properties, etc. + */ + fd = rump_or_open(devaudioctl, O_RDONLY); + if (fd == -1) + xp_err(1, __LINE__, "open %s", devaudioctl); + r = rump_or_ioctl(fd, AUDIO_GETPROPS, &props); + if (r == -1) + xp_err(1, __LINE__, "AUDIO_GETPROPS"); + r = rump_or_ioctl(fd, AUDIO_GETDEV, &devinfo); + if (r == -1) + xp_err(1, __LINE__, "AUDIO_GETDEV"); + rump_or_close(fd); + + if (debug) { + printf(" device = %s, %s, %s\n", + devinfo.name, devinfo.version, devinfo.config); + printf(" hw props ="); + if (hw_canplay()) + printf(" playback"); + if (hw_canrec()) + printf(" capture"); + if (hw_fulldup()) + printf(" fullduplex"); + printf("\n"); + } + +} + +/* Consumer thread used by pad */ +void * +consumer_thread(void *arg) +{ + char buf[1024]; + int r; + + pthread_setname_np(pthread_self(), "consumer", NULL); + pthread_detach(pthread_self()); + + /* throw away data anyway */ + for (;;) { + r = read(padfd, buf, sizeof(buf)); + if (r < 1) + break; + } + + pthread_exit(NULL); +} + +/* + * XXX + * Closing pad descriptor before audio descriptor causes panic (PR kern/54427). + * To avoid this, close non-pad descriptor first using atexit(3) for now. + * This is just a workaround and this function should be removed. + */ +void cleanup_audiofd() +{ + int fd; + + for (fd = 3; fd <= maxfd; fd++) { + if (fd != padfd) + close(fd); + } + maxfd = 3; +} + +/* + * Support functions + */ + +/* Set testname */ +void +TEST(const char *name, ...) +{ + va_list ap; + + va_start(ap, name); + vsnprintf(testname, sizeof(testname), name, ap); + va_end(ap); + if (opt_atf == false) { + printf("%s\n", testname); + fflush(stdout); + } +} + +/* + * XP_FAIL() should be called when this test fails. + * If caller already count up testcount, call xp_fail() instead. + */ +#define XP_FAIL(fmt...) do { \ + testcount++; \ + xp_fail(__LINE__, fmt); \ +} while (0) +bool xp_fail(int line, const char *fmt, ...) +{ + va_list ap; + + printf("%s %d: ", (opt_atf ? "Line" : " FAIL:"), line); + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); + printf("\n"); + fflush(stdout); + failcount++; + + return false; +} + +/* + * XP_SKIP() should be called when you want to skip this test. + * If caller already count up testcount, call xp_skip() instead. + */ +#define XP_SKIP(fmt...) do { \ + testcount++; \ + xp_skip(__LINE__, fmt); \ +} while (0) +void xp_skip(int line, const char *fmt, ...) +{ + va_list ap; + + printf("%s %d: ", (opt_atf ? "Line" : " SKIP:"), line); + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); + printf("\n"); + fflush(stdout); + skipcount++; +} + +#define XP_EQ(exp, act) xp_eq(__LINE__, exp, act, #act) +bool xp_eq(int line, int exp, int act, const char *varname) +{ + bool r = true; + + testcount++; + if (exp != act) { + r = xp_fail(line, "%s expects %d but %d", varname, exp, act); + } + return r; +} +#define XP_EQ_STR(exp, act) xp_eq_str(__LINE__, exp, act, #act) +bool xp_eq_str(int line, const char *exp, const char *act, const char *varname) +{ + bool r = true; + + testcount++; + if (strcmp(exp, act) != 0) { + r = xp_fail(line, "%s expects \"%s\" but \"%s\"", + varname, exp, act); + } + return r; +} + +#define XP_NE(exp, act) xp_ne(__LINE__, exp, act, #act) +bool xp_ne(int line, int exp, int act, const char *varname) +{ + bool r = true; + + testcount++; + if (exp == act) { + r = xp_fail(line, "%s expects != %d but %d", varname, exp, act); + } + return r; +} + +/* This expects that result is expressed in expr. */ +/* GCC extension */ +#define XP_IF(expr) xp_if(__LINE__, (expr), #expr) +bool xp_if(int line, bool expr, const char *exprname) +{ + bool r = true; + testcount++; + if (!expr) { + r = xp_fail(__LINE__, "(%s) is expected but not met", exprname); + } + return r; +} + +/* This expects that the system call returns 'exp'. */ +#define XP_SYS_EQ(exp, act) xp_sys_eq(__LINE__, exp, act, #act) +bool xp_sys_eq(int line, int exp, int act, const char *varname) +{ + bool r = true; + + testcount++; + if (act == -1) { + r = xp_fail(line, "%s expects %d but -1,err#%d(%s)", + varname, exp, errno, strerror(errno)); + } else { + r = xp_eq(line, exp, act, varname); + } + return r; +} + +/* + * This expects that system call succeeds. + * This is useful when you expect the system call succeeds but don't know + * the expected return value, such as open(2). + */ +#define XP_SYS_OK(act) xp_sys_ok(__LINE__, act, #act) +bool xp_sys_ok(int line, int act, const char *varname) +{ + bool r = true; + + testcount++; + if (act == -1) { + r = xp_fail(line, "%s expects success but -1,err#%d(%s)", + varname, errno, strerror(errno)); + } + return r; +} + +/* This expects that the system call fails with 'experrno'. */ +#define XP_SYS_NG(experrno, act) xp_sys_ng(__LINE__, experrno, act, #act) +bool xp_sys_ng(int line, int experrno, int act, const char *varname) +{ + bool r = true; + + testcount++; + if (act != -1) { + r = xp_fail(line, "%s expects -1,err#%d but %d", + varname, experrno, act); + } else if (experrno != errno) { + char acterrbuf[100]; + int acterrno = errno; + strlcpy(acterrbuf, strerror(acterrno), sizeof(acterrbuf)); + r = xp_fail(line, "%s expects -1,err#%d(%s) but -1,err#%d(%s)", + varname, experrno, strerror(experrno), + acterrno, acterrbuf); + } + return r; +} + +/* + * When exp == 0, this expects that the system call succeeds with returned + * pointer is not -1. + * When exp != 0, this expects that the system call fails with returned + * pointer is -1 and its errno is exp. + * It's only for mmap(). + */ +#define XP_SYS_PTR(exp, act) xp_sys_ptr(__LINE__, exp, act, #act) +bool xp_sys_ptr(int line, int exp, void *act, const char *varname) +{ + char errbuf[256]; + int actual_errno; + bool r = true; + + testcount++; + if (exp == 0) { + /* expects to succeed */ + if (act == (void *)-1) { + r = xp_fail(line, + "%s expects success but -1,err#%d(%s)", + varname, errno, strerror(errno)); + } + } else { + /* expects to fail */ + if (act != (void *)-1) { + r = xp_fail(line, + "%s expects -1,err#%d(%s) but success", + varname, exp, strerror(exp)); + } else if (exp != errno) { + actual_errno = errno; + strerror_r(actual_errno, errbuf, sizeof(errbuf)); + r = xp_fail(line, + "%s expects -1,err#%d(%s) but -1,err#%d(%s)", + varname, exp, strerror(exp), actual_errno, errbuf); + } + } + return r; +} + + +/* + * REQUIRED_* return immediately if condition does not meet. + */ +#define REQUIRED_EQ(e, a) do { if (!XP_EQ(e, a)) return; } while (0) +#define REQUIRED_NE(e, a) do { if (!XP_NE(e, a)) return; } while (0) +#define REQUIRED_IF(expr) do { if (!XP_IF(expr)) return; } while (0) +#define REQUIRED_SYS_EQ(e, a) do { if (!XP_SYS_EQ(e, a)) return; } while (0) +#define REQUIRED_SYS_OK(a) do { if (!XP_SYS_OK(a)) return; } while (0) + + +static const char *openmode_str[] = { + "O_RDONLY", + "O_WRONLY", + "O_RDWR", +}; + + +/* + * All system calls in following tests should be called with these macros. + */ + +#define OPEN(name, mode) \ + debug_open(__LINE__, name, mode) +int debug_open(int line, const char *name, int mode) +{ + char modestr[32]; + int n; + + if ((mode & 3) != 3) { + n = snprintf(modestr, sizeof(modestr), "%s", + openmode_str[mode & 3]); + } else { + n = snprintf(modestr, sizeof(modestr), "%d", mode & 3); + } + if ((mode & O_NONBLOCK)) + n += snprintf(modestr + n, sizeof(modestr) - n, "|O_NONBLOCK"); + + DPRINTFF(line, "open(\"%s\", %s)", name, modestr); + int r = rump_or_open(name, mode); + DRESULT(r); +} + +#define WRITE(fd, addr, len) \ + debug_write(__LINE__, fd, addr, len) +int debug_write(int line, int fd, const void *addr, size_t len) +{ + DPRINTFF(line, "write(%d, %p, %zd)", fd, addr, len); + int r = rump_or_write(fd, addr, len); + DRESULT(r); +} + +#define READ(fd, addr, len) \ + debug_read(__LINE__, fd, addr, len) +int debug_read(int line, int fd, void *addr, size_t len) +{ + DPRINTFF(line, "read(%d, %p, %zd)", fd, addr, len); + int r = rump_or_read(fd, addr, len); + DRESULT(r); +} + +/* + * addrstr is the comment for debug message. + * int onoff = 0; + * ioctl(fd, SWITCH, onoff); -> IOCTL(fd, SWITCH, onoff, "off"); + */ +#define IOCTL(fd, name, addr, addrfmt...) \ + debug_ioctl(__LINE__, fd, name, #name, addr, addrfmt) +int debug_ioctl(int line, int fd, u_long name, const char *namestr, + void *addr, const char *addrfmt, ...) +{ + char addrbuf[100]; + va_list ap; + + va_start(ap, addrfmt); + vsnprintf(addrbuf, sizeof(addrbuf), addrfmt, ap); + va_end(ap); + DPRINTFF(line, "ioctl(%d, %s, %s)", fd, namestr, addrbuf); + int r = rump_or_ioctl(fd, name, addr); + DRESULT(r); +} + +#define FCNTL(fd, name...) \ + debug_fcntl(__LINE__, fd, name, #name) +int debug_fcntl(int line, int fd, int name, const char *namestr, ...) +{ + int r; + + switch (name) { + case F_GETFL: /* no arguments */ + DPRINTFF(line, "fcntl(%d, %s)", fd, namestr); + r = rump_or_fcntl(fd, name); + break; + default: + __unreachable(); + } + DRESULT(r); + return r; +} + +#define CLOSE(fd) \ + debug_close(__LINE__, fd) +int debug_close(int line, int fd) +{ + DPRINTFF(line, "close(%d)", fd); + int r = rump_or_close(fd); + DRESULT(r); +} + +#define MMAP(ptr, len, prot, flags, fd, offset) \ + debug_mmap(__LINE__, ptr, len, prot, flags, fd, offset) +void *debug_mmap(int line, void *ptr, size_t len, int prot, int flags, int fd, + off_t offset) +{ + char protbuf[256]; + char flagbuf[256]; + int n; + +#define ADDFLAG(buf, var, name) do { \ + if (((var) & (name))) \ + n = strlcat(buf, "|" #name, sizeof(buf)); \ + var &= ~(name); \ +} while (0) + + n = 0; + protbuf[n] = '\0'; + if (prot == 0) { + strlcpy(protbuf, "|PROT_NONE", sizeof(protbuf)); + } else { + ADDFLAG(protbuf, prot, PROT_EXEC); + ADDFLAG(protbuf, prot, PROT_WRITE); + ADDFLAG(protbuf, prot, PROT_READ); + if (prot != 0) { + snprintf(protbuf + n, sizeof(protbuf) - n, + "|prot=0x%x", prot); + } + } + + n = 0; + flagbuf[n] = '\0'; + if (flags == 0) { + strlcpy(flagbuf, "|MAP_FILE", sizeof(flagbuf)); + } else { + ADDFLAG(flagbuf, flags, MAP_SHARED); + ADDFLAG(flagbuf, flags, MAP_PRIVATE); + ADDFLAG(flagbuf, flags, MAP_FIXED); + ADDFLAG(flagbuf, flags, MAP_INHERIT); + ADDFLAG(flagbuf, flags, MAP_HASSEMAPHORE); + ADDFLAG(flagbuf, flags, MAP_TRYFIXED); + ADDFLAG(flagbuf, flags, MAP_WIRED); + ADDFLAG(flagbuf, flags, MAP_ANON); + if (flags != 0) { + n += snprintf(flagbuf + n, sizeof(flagbuf) - n, + "|flag=0x%x", flags); + } + } + + DPRINTFF(line, "mmap(%p, %zd, %s, %s, %d, %jd)", + ptr, len, protbuf + 1, flagbuf + 1, fd, offset); + void *r = mmap(ptr, len, prot, flags, fd, offset); + DRESULT_PTR(r); +} + +#define MUNMAP(ptr, len) \ + debug_munmap(__LINE__, ptr, len) +int debug_munmap(int line, void *ptr, int len) +{ +#if !defined(NO_RUMP) + if (use_rump) + xp_errx(1, __LINE__, "rump doesn't support munmap"); +#endif + DPRINTFF(line, "munmap(%p, %d)", ptr, len); + int r = munmap(ptr, len); + DRESULT(r); +} + +const char * +event_tostr(int events) +{ + static char buf[64]; + + snprintb(buf, sizeof(buf), + "\177\020" \ + "b\10WRBAND\0" \ + "b\7RDBAND\0" "b\6RDNORM\0" "b\5NVAL\0" "b\4HUP\0" \ + "b\3ERR\0" "b\2OUT\0" "b\1PRI\0" "b\0IN\0", + events); + return buf; +} + +#define POLL(pfd, nfd, timeout) \ + debug_poll(__LINE__, pfd, nfd, timeout) +int debug_poll(int line, struct pollfd *pfd, int nfd, int timeout) +{ + char buf[256]; + int n = 0; + buf[n] = '\0'; + for (int i = 0; i < nfd; i++) { + n += snprintf(buf + n, sizeof(buf) - n, "{fd=%d,events=%s}", + pfd[i].fd, event_tostr(pfd[i].events)); + } + DPRINTFF(line, "poll(%s, %d, %d)", buf, nfd, timeout); + int r = rump_or_poll(pfd, nfd, timeout); + DRESULT(r); +} + +#define KQUEUE() \ + debug_kqueue(__LINE__) +int debug_kqueue(int line) +{ + DPRINTFF(line, "kqueue()"); + int r = rump_or_kqueue(); + DRESULT(r); +} + +#define KEVENT_SET(kq, kev, nev) \ + debug_kevent_set(__LINE__, kq, kev, nev) +int debug_kevent_set(int line, int kq, const struct kevent *kev, size_t nev) +{ + DPRINTFF(line, "kevent_set(%d, %p, %zd)", kq, kev, nev); + int r = rump_or_kevent(kq, kev, nev, NULL, 0, NULL); + DRESULT(r); +} + +#define KEVENT_POLL(kq, kev, nev, ts) \ + debug_kevent_poll(__LINE__, kq, kev, nev, ts) +int debug_kevent_poll(int line, int kq, struct kevent *kev, size_t nev, + const struct timespec *ts) +{ + char tsbuf[32]; + + if (ts == NULL) { + snprintf(tsbuf, sizeof(tsbuf), "NULL"); + } else if (ts->tv_sec == 0 && ts->tv_nsec == 0) { + snprintf(tsbuf, sizeof(tsbuf), "0.0"); + } else { + snprintf(tsbuf, sizeof(tsbuf), "%d.%09ld", + (int)ts->tv_sec, ts->tv_nsec); + } + DPRINTFF(line, "kevent_poll(%d, %p, %zd, %s)", kq, kev, nev, tsbuf); + int r = rump_or_kevent(kq, NULL, 0, kev, nev, ts); + DRESULT(r); +} + +#define DEBUG_KEV(name, kev) \ + debug_kev(__LINE__, name, kev) +void debug_kev(int line, const char *name, const struct kevent *kev) +{ + char flagbuf[256]; + const char *filterbuf; + uint32_t v; + int n; + + n = 0; + flagbuf[n] = '\0'; + if (kev->flags == 0) { + strcpy(flagbuf, "|0?"); + } else { + v = kev->flags; + ADDFLAG(flagbuf, v, EV_ADD); + if (v != 0) + snprintf(flagbuf + n, sizeof(flagbuf)-n, "|0x%x", v); + } + + switch (kev->filter) { + case EVFILT_READ: filterbuf = "EVFILT_READ"; break; + case EVFILT_WRITE: filterbuf = "EVFILT_WRITE"; break; + default: filterbuf = "EVFILT_?"; break; + } + + DPRINTFF(line, + "%s={id:%d,%s,%s,fflags:0x%x,data:0x%" PRIx64 ",udata:0x%x}\n", + name, + (int)kev->ident, + flagbuf + 1, + filterbuf, + kev->fflags, + kev->data, + (int)(intptr_t)kev->udata); +} + +/* XXX rump? */ +#define GETUID() \ + debug_getuid(__LINE__) +uid_t debug_getuid(int line) +{ + DPRINTFF(line, "getuid"); + uid_t r = getuid(); + /* getuid() never fails */ + DPRINTF(" = %u\n", r); + return r; +} + +/* XXX rump? */ +#define SETEUID(id) \ + debug_seteuid(__LINE__, id) +int debug_seteuid(int line, uid_t id) +{ + DPRINTFF(line, "seteuid(%d)", (int)id); + int r = seteuid(id); + DRESULT(r); +} + +#define SYSCTLBYNAME(name, oldp, oldlenp, newp, newlen) \ + debug_sysctlbyname(__LINE__, name, oldp, oldlenp, newp, newlen) +int debug_sysctlbyname(int line, const char *name, void *oldp, size_t *oldlenp, + const void *newp, size_t newlen) +{ + DPRINTFF(line, "sysctlbyname(\"%s\")", name); + int r = sysctlbyname(name, oldp, oldlenp, newp, newlen); + DRESULT(r); +} + + +/* Return openable mode on this hardware property */ +int +openable_mode(void) +{ + if (hw_bidir()) + return O_RDWR; + if (hw_canplay()) + return O_WRONLY; + else + return O_RDONLY; +} + +int mode2aumode_full[] = { + AUMODE_RECORD, /* O_RDONLY */ + AUMODE_PLAY | AUMODE_PLAY_ALL, /* O_WRONLY */ + AUMODE_PLAY | AUMODE_PLAY_ALL | AUMODE_RECORD, /* O_RDWR */ +}; + +/* Convert openmode(O_*) to AUMODE_*, with hardware property */ +int +mode2aumode(int mode) +{ + int aumode; + + aumode = mode2aumode_full[mode]; + if (hw_canplay() == 0) + aumode &= ~(AUMODE_PLAY | AUMODE_PLAY_ALL); + if (hw_canrec() == 0) + aumode &= ~AUMODE_RECORD; + + if (netbsd >= 9) { + /* half-duplex treats O_RDWR as O_WRONLY */ + if (mode == O_RDWR && hw_bidir() && hw_fulldup() == 0) + aumode &= ~AUMODE_RECORD; + } + + return aumode; +} + +/* Is this mode + hardware playable? */ +int +mode2play(int mode) +{ + int aumode; + + aumode = mode2aumode(mode); + return ((aumode & AUMODE_PLAY)) ? 1 : 0; +} + +/* Is this mode + hardware recordable? */ +int +mode2rec(int mode) +{ + int aumode; + + aumode = mode2aumode(mode); + return ((aumode & AUMODE_RECORD)) ? 1 : 0; +} + +/* + * On NetBSD7, open() after-closing-mmap fails due to a bug. + * It happens once every two times like flip-flop, so the workaround is + * to open it again. + */ +void +reset_after_mmap(void) +{ + int fd; + + if (netbsd < 8) { + fd = OPEN(devaudio, O_WRONLY); + if (fd != -1) + CLOSE(fd); + } +} + +/* + * Lookup "outputs.master" and return its mixer device index. + * It may not be strict but I'm not sure. + */ +int +mixer_get_outputs_master(int mixerfd) +{ + const char * const typename[] = { "CLASS", "ENUM", "SET", "VALUE" }; + mixer_devinfo_t di; + int class_outputs; + int i; + int r; + + class_outputs = -1; + for (i = 0; ; i++) { + memset(&di, 0, sizeof(di)); + di.index = i; + r = IOCTL(mixerfd, AUDIO_MIXER_DEVINFO, &di, "index=%d", i); + if (r < 0) + break; + DPRINTF(" > type=%s(%d) mixer_class=%d name=%s\n", + (0 <= di.type && di.type <= 3) ? typename[di.type] : "", + di.type, di.mixer_class, di.label.name); + if (di.type == AUDIO_MIXER_CLASS && + strcmp(di.label.name, "outputs") == 0) { + class_outputs = di.mixer_class; + DPRINTF(" > class_output=%d\n", class_outputs); + continue; + } + if (di.type == AUDIO_MIXER_VALUE && + di.mixer_class == class_outputs && + strcmp(di.label.name, "master") == 0) { + return i; + } + } + /* Not found */ + return -1; +} + +/* + * Tests + */ + +void test_open_mode(int); +void test_open(const char *, int); +void test_open_simul(int, int); +void try_open_multiuser(bool); +void test_open_multiuser(bool); +void test_rdwr_fallback(int, bool, bool); +void test_rdwr_two(int, int); +void test_mmap_mode(int, int); +void test_poll_mode(int, int, int); +void test_poll_in_open(const char *); +void test_kqueue_mode(int, int, int); +volatile int sigio_caught; +void signal_FIOASYNC(int); +void test_AUDIO_SETFD_xxONLY(int); +void test_AUDIO_SETINFO_mode(int, int, int, int); +void test_AUDIO_SETINFO_params_set(int, int, int); +void test_AUDIO_SETINFO_pause(int, int, int); +int getenc_make_table(int, int[][5]); +void xp_getenc(int[][5], int, int, int, struct audio_prinfo *); +void getenc_check_encodings(int, int[][5]); +void test_AUDIO_ERROR(int); +void test_audioctl_open_1(int, int); +void test_audioctl_open_2(int, int); +void try_audioctl_open_multiuser(const char *, const char *); +void test_audioctl_open_multiuser(bool, const char *, const char *); +void test_audioctl_rw(int); + +#define DEF(name) \ + void test__ ## name (void); \ + void test__ ## name (void) + +/* + * Whether it can be open()ed with specified mode. + */ +void +test_open_mode(int mode) +{ + int fd; + int r; + + TEST("open_mode_%s", openmode_str[mode] + 2); + + fd = OPEN(devaudio, mode); + if (mode2aumode(mode) != 0) { + XP_SYS_OK(fd); + } else { + XP_SYS_NG(ENXIO, fd); + } + + if (fd >= 0) { + r = CLOSE(fd); + XP_SYS_EQ(0, r); + } +} +DEF(open_mode_RDONLY) { test_open_mode(O_RDONLY); } +DEF(open_mode_WRONLY) { test_open_mode(O_WRONLY); } +DEF(open_mode_RDWR) { test_open_mode(O_RDWR); } + +/* + * Check the initial parameters and stickiness. + * /dev/audio + * The initial parameters are always the same whenever you open. + * /dev/sound and /dev/audioctl + * The initial parameters are inherited from the last /dev/sound or + * /dev/audio. + */ +void +test_open(const char *devname, int mode) +{ + struct audio_info ai; + struct audio_info ai0; + char devfile[16]; + int fd; + int r; + int can_play; + int can_rec; + int exp_mode; + int exp_encoding; + int exp_precision; + int exp_channels; + int exp_sample_rate; + int exp_pause; + int exp_popen; + int exp_ropen; + + TEST("open_%s_%s", devname, openmode_str[mode] + 2); + + snprintf(devfile, sizeof(devfile), "/dev/%s%d", devname, unit); + can_play = mode2play(mode); + can_rec = mode2rec(mode); + if (strcmp(devname, "audioctl") != 0) { + if (can_play + can_rec == 0) { + /* Check whether it cannot be opened */ + fd = OPEN(devaudio, mode); + XP_SYS_NG(ENXIO, fd); + return; + } + } + + /* /dev/audio is always initialized */ + if (strcmp(devname, "audio") == 0) { + exp_encoding = AUDIO_ENCODING_ULAW; + exp_precision = 8; + exp_channels = 1; + exp_sample_rate = 8000; + exp_pause = 0; + } else { + exp_encoding = AUDIO_ENCODING_SLINEAR_LE; + exp_precision = 16; + exp_channels = 2; + exp_sample_rate = 11025; + exp_pause = 1; + } + + /* /dev/audioctl is always "not opened" */ + if (strcmp(devname, "audioctl") == 0) { + exp_mode = 0; + exp_popen = 0; + exp_ropen = 0; + } else { + exp_mode = mode2aumode(mode); + exp_popen = can_play; + exp_ropen = can_rec; + } + + + /* + * At first, initialize the sticky parameters both of play and rec. + * This uses /dev/audio to verify /dev/audio. It's not good way but + * I don't have better one... + */ + fd = OPEN(devaudio, openable_mode()); + REQUIRED_SYS_OK(fd); + r = CLOSE(fd); + REQUIRED_SYS_EQ(0, r); + + /* + * Open target device and check the initial parameters + * At this moment, all devices are initialized by default. + */ + fd = OPEN(devfile, mode); + REQUIRED_SYS_OK(fd); + memset(&ai, 0, sizeof(ai)); + r = IOCTL(fd, AUDIO_GETBUFINFO, &ai, ""); + REQUIRED_SYS_EQ(0, r); + + XP_NE(0, ai.blocksize); + /* hiwat/lowat */ + XP_EQ(exp_mode, ai.mode); + /* ai.play */ + XP_EQ(8000, ai.play.sample_rate); + XP_EQ(1, ai.play.channels); + XP_EQ(8, ai.play.precision); + XP_EQ(AUDIO_ENCODING_ULAW, ai.play.encoding); + /* gain */ + /* port */ + XP_EQ(0, ai.play.seek); + /* avail_ports */ + XP_NE(0, ai.play.buffer_size); + XP_EQ(0, ai.play.samples); + XP_EQ(0, ai.play.eof); + XP_EQ(0, ai.play.pause); + XP_EQ(0, ai.play.error); + XP_EQ(0, ai.play.waiting); + /* balance */ + XP_EQ(exp_popen, ai.play.open); + XP_EQ(0, ai.play.active); + /* ai.record */ + XP_EQ(8000, ai.record.sample_rate); + XP_EQ(1, ai.record.channels); + XP_EQ(8, ai.record.precision); + XP_EQ(AUDIO_ENCODING_ULAW, ai.record.encoding); + /* gain */ + /* port */ + XP_EQ(0, ai.record.seek); + /* avail_ports */ + XP_NE(0, ai.record.buffer_size); + XP_EQ(0, ai.record.samples); + XP_EQ(0, ai.record.eof); + XP_EQ(0, ai.record.pause); + XP_EQ(0, ai.record.error); + XP_EQ(0, ai.record.waiting); + /* balance */ + XP_EQ(exp_ropen, ai.record.open); + if (netbsd < 9 && strcmp(devname, "sound") == 0) { + /* + * On NetBSD7/8, it doesn't seem to start recording on open + * for /dev/sound. It should be a bug. + */ + XP_EQ(0, ai.record.active); + } else { + XP_EQ(exp_ropen, ai.record.active); + } + /* Save it */ + ai0 = ai; + + /* + * Change much as possible + */ + AUDIO_INITINFO(&ai); + ai.mode = ai0.mode ^ AUMODE_PLAY_ALL; + ai.play.sample_rate = 11025; + ai.play.channels = 2; + ai.play.precision = 16; + ai.play.encoding = AUDIO_ENCODING_SLINEAR_LE; + ai.play.pause = 1; + ai.record.sample_rate = 11025; + ai.record.channels = 2; + ai.record.precision = 16; + ai.record.encoding = AUDIO_ENCODING_SLINEAR_LE; + ai.record.pause = 1; + r = IOCTL(fd, AUDIO_SETINFO, &ai, "ai"); + REQUIRED_SYS_EQ(0, r); + r = CLOSE(fd); + REQUIRED_SYS_EQ(0, r); + + /* + * Open the same target device again and check + */ + fd = OPEN(devfile, mode); + REQUIRED_SYS_OK(fd); + memset(&ai, 0, sizeof(ai)); + r = IOCTL(fd, AUDIO_GETBUFINFO, &ai, ""); + REQUIRED_SYS_EQ(0, r); + + XP_NE(0, ai.blocksize); + /* hiwat/lowat */ + if (netbsd < 8) { + /* + * On NetBSD7, the behavior when changing ai.mode on + * /dev/audioctl can not be explained yet but I won't + * verify it more over. + */ + } else { + /* On NetBSD9, changing mode never affects other fds */ + XP_EQ(exp_mode, ai.mode); + } + /* ai.play */ + XP_EQ(exp_sample_rate, ai.play.sample_rate); + XP_EQ(exp_channels, ai.play.channels); + XP_EQ(exp_precision, ai.play.precision); + XP_EQ(exp_encoding, ai.play.encoding); + /* gain */ + /* port */ + XP_EQ(0, ai.play.seek); + /* avail_ports */ + XP_NE(0, ai.play.buffer_size); + XP_EQ(0, ai.play.samples); + XP_EQ(0, ai.play.eof); + XP_EQ(exp_pause, ai.play.pause); + XP_EQ(0, ai.play.error); + XP_EQ(0, ai.play.waiting); + /* balance */ + XP_EQ(exp_popen, ai.play.open); + XP_EQ(0, ai.play.active); + /* ai.record */ + XP_EQ(exp_sample_rate, ai.record.sample_rate); + XP_EQ(exp_channels, ai.record.channels); + XP_EQ(exp_precision, ai.record.precision); + XP_EQ(exp_encoding, ai.record.encoding); + /* gain */ + /* port */ + XP_EQ(0, ai.record.seek); + /* avail_ports */ + XP_NE(0, ai.record.buffer_size); + XP_EQ(0, ai.record.samples); + XP_EQ(0, ai.record.eof); + XP_EQ(exp_pause, ai.record.pause); + XP_EQ(0, ai.record.error); + XP_EQ(0, ai.record.waiting); + /* balance */ + XP_EQ(exp_ropen, ai.record.open); + if (netbsd < 9 && strcmp(devname, "sound") == 0) { + /* + * On NetBSD7/8, it doesn't seem to start recording on open + * for /dev/sound. It should be a bug. + */ + XP_EQ(0, ai.record.active); + } else { + XP_EQ(exp_ropen, ai.record.active); + } + + r = CLOSE(fd); + REQUIRED_SYS_EQ(0, r); +} +DEF(open_audio_RDONLY) { test_open("audio", O_RDONLY); } +DEF(open_audio_WRONLY) { test_open("audio", O_WRONLY); } +DEF(open_audio_RDWR) { test_open("audio", O_RDWR); } +DEF(open_sound_RDONLY) { test_open("sound", O_RDONLY); } +DEF(open_sound_WRONLY) { test_open("sound", O_WRONLY); } +DEF(open_sound_RDWR) { test_open("sound", O_RDWR); } +DEF(open_audioctl_RDONLY) { test_open("audioctl", O_RDONLY); } +DEF(open_audioctl_WRONLY) { test_open("audioctl", O_WRONLY); } +DEF(open_audioctl_RDWR) { test_open("audioctl", O_RDWR); } + +/* + * Open (1) /dev/sound -> (2) /dev/audio -> (3) /dev/sound, + * Both of /dev/audio and /dev/sound share the sticky parameters, + * /dev/sound inherits and use it but /dev/audio initialize and use it. + * So 2nd audio descriptor affects 3rd sound descriptor. + */ +DEF(open_sound_sticky) +{ + struct audio_info ai; + int fd; + int r; + int openmode; + + TEST("open_sound_sticky"); + + openmode = openable_mode(); + + /* First, open /dev/sound and change encoding as a delegate */ + fd = OPEN(devsound, openmode); + REQUIRED_SYS_OK(fd); + AUDIO_INITINFO(&ai); + ai.play.encoding = AUDIO_ENCODING_SLINEAR_LE; + ai.record.encoding = AUDIO_ENCODING_SLINEAR_LE; + r = IOCTL(fd, AUDIO_SETINFO, &ai, ""); + REQUIRED_SYS_EQ(0, r); + r = CLOSE(fd); + REQUIRED_SYS_EQ(0, r); + + /* Next, open /dev/audio. It makes the encoding mulaw */ + fd = OPEN(devaudio, openmode); + REQUIRED_SYS_OK(fd); + r = CLOSE(fd); + REQUIRED_SYS_EQ(0, r); + + /* And then, open /dev/sound again */ + fd = OPEN(devsound, openmode); + REQUIRED_SYS_OK(fd); + memset(&ai, 0, sizeof(ai)); + r = IOCTL(fd, AUDIO_GETBUFINFO, &ai, ""); + REQUIRED_SYS_EQ(0, r); + XP_EQ(AUDIO_ENCODING_ULAW, ai.play.encoding); + XP_EQ(AUDIO_ENCODING_ULAW, ai.record.encoding); + r = CLOSE(fd); + REQUIRED_SYS_EQ(0, r); +} + +/* + * /dev/audioctl has stickiness like /dev/sound. + */ +DEF(open_audioctl_sticky) +{ + struct audio_info ai; + int fd; + int r; + int openmode; + + TEST("open_audioctl_sticky"); + + openmode = openable_mode(); + + /* First, open /dev/audio and change encoding */ + fd = OPEN(devaudio, openmode); + REQUIRED_SYS_OK(fd); + AUDIO_INITINFO(&ai); + ai.play.encoding = AUDIO_ENCODING_SLINEAR_LE; + ai.play.precision = 16; + ai.record.encoding = AUDIO_ENCODING_SLINEAR_LE; + ai.record.precision = 16; + r = IOCTL(fd, AUDIO_SETINFO, &ai, "SLINEAR_LE"); + REQUIRED_SYS_EQ(0, r); + r = CLOSE(fd); + REQUIRED_SYS_EQ(0, r); + + /* Next, open /dev/audioctl. It should be affected */ + fd = OPEN(devaudioctl, openmode); + REQUIRED_SYS_OK(fd); + memset(&ai, 0, sizeof(ai)); + r = IOCTL(fd, AUDIO_GETBUFINFO, &ai, ""); + REQUIRED_SYS_EQ(0, r); + XP_EQ(AUDIO_ENCODING_SLINEAR_LE, ai.play.encoding); + XP_EQ(16, ai.play.precision); + XP_EQ(AUDIO_ENCODING_SLINEAR_LE, ai.record.encoding); + XP_EQ(16, ai.record.precision); + + /* Then, change /dev/audioctl */ + AUDIO_INITINFO(&ai); + ai.play.encoding = AUDIO_ENCODING_ULAW; + ai.play.precision = 8; + ai.record.encoding = AUDIO_ENCODING_ULAW; + ai.record.precision = 8; + r = IOCTL(fd, AUDIO_SETINFO, &ai, "ULAW"); + REQUIRED_SYS_EQ(0, r); + r = CLOSE(fd); + REQUIRED_SYS_EQ(0, r); + + /* Finally, open /dev/sound. It also should be affected */ + fd = OPEN(devsound, openmode); + REQUIRED_SYS_OK(fd); + memset(&ai, 0, sizeof(ai)); + r = IOCTL(fd, AUDIO_GETBUFINFO, &ai, ""); + REQUIRED_SYS_EQ(0, r); + XP_EQ(AUDIO_ENCODING_ULAW, ai.play.encoding); + XP_EQ(8, ai.play.precision); + XP_EQ(AUDIO_ENCODING_ULAW, ai.record.encoding); + XP_EQ(8, ai.record.precision); + r = CLOSE(fd); + REQUIRED_SYS_EQ(0, r); +} + +/* + * Open two descriptors simultaneously. + */ +void +test_open_simul(int mode0, int mode1) +{ + struct audio_info ai; + int fd0, fd1; + int i; + int r; + int actmode; +#define AUMODE_BOTH (AUMODE_PLAY | AUMODE_RECORD) + struct { + int mode0; + int mode1; + } expfulltable[] = { + /* expected fd0 expected fd1 (-errno expects error) */ + { AUMODE_RECORD, AUMODE_RECORD }, // REC, REC + { AUMODE_RECORD, AUMODE_PLAY }, // REC, PLAY + { AUMODE_RECORD, AUMODE_BOTH }, // REC, BOTH + { AUMODE_PLAY, AUMODE_RECORD }, // PLAY, REC + { AUMODE_PLAY, AUMODE_PLAY }, // PLAY, PLAY + { AUMODE_PLAY, AUMODE_BOTH }, // PLAY, BOTH + { AUMODE_BOTH, AUMODE_RECORD }, // BOTH, REC + { AUMODE_BOTH, AUMODE_PLAY }, // BOTH, PLAY + { AUMODE_BOTH, AUMODE_BOTH }, // BOTH, BOTH + }, + exphalftable[] = { + /* expected fd0 expected fd1 (-errno expects error) */ + { AUMODE_RECORD, AUMODE_RECORD }, // REC, REC + { AUMODE_RECORD, -ENODEV }, // REC, PLAY + { AUMODE_RECORD, -ENODEV }, // REC, BOTH + { AUMODE_PLAY, -ENODEV }, // PLAY, REC + { AUMODE_PLAY, AUMODE_PLAY }, // PLAY, PLAY + { AUMODE_PLAY, AUMODE_PLAY }, // PLAY, BOTH + { AUMODE_PLAY, -ENODEV }, // BOTH, REC + { AUMODE_PLAY, AUMODE_PLAY }, // BOTH, PLAY + { AUMODE_PLAY, AUMODE_PLAY }, // BOTH, BOTH + }, *exptable; + + /* The expected values are different in half-duplex or full-duplex */ + if (hw_fulldup()) { + exptable = expfulltable; + } else { + exptable = exphalftable; + } + + TEST("open_simul_%s_%s", + openmode_str[mode0] + 2, + openmode_str[mode1] + 2); + + if (netbsd < 8) { + XP_SKIP("Multiple open is not supported"); + return; + } + + if (mode2aumode(mode0) == 0 || mode2aumode(mode1) == 0) { + XP_SKIP("Operation not allowed on this hardware property"); + return; + } + + i = mode0 * 3 + mode1; + + /* Open first one */ + fd0 = OPEN(devaudio, mode0); + REQUIRED_SYS_OK(fd0); + r = IOCTL(fd0, AUDIO_GETBUFINFO, &ai, ""); + REQUIRED_SYS_EQ(0, r); + actmode = ai.mode & AUMODE_BOTH; + XP_EQ(exptable[i].mode0, actmode); + + /* Open second one */ + fd1 = OPEN(devaudio, mode1); + if (exptable[i].mode1 >= 0) { + /* Case to expect to be able to open */ + REQUIRED_SYS_OK(fd1); + r = IOCTL(fd1, AUDIO_GETBUFINFO, &ai, ""); + XP_SYS_EQ(0, r); + if (r == 0) { + actmode = ai.mode & AUMODE_BOTH; + XP_EQ(exptable[i].mode1, actmode); + } + } else { + /* Case to expect not to be able to open */ + XP_SYS_NG(ENODEV, fd1); + if (fd1 == -1) { + XP_EQ(-exptable[i].mode1, errno); + } else { + r = IOCTL(fd1, AUDIO_GETBUFINFO, &ai, ""); + XP_SYS_EQ(0, r); + if (r == 0) { + actmode = ai.mode & AUMODE_BOTH; + XP_FAIL("expects error but %d", actmode); + } + } + } + + if (fd1 >= 0) { + r = CLOSE(fd1); + XP_SYS_EQ(0, r); + } + + r = CLOSE(fd0); + XP_SYS_EQ(0, r); +} +DEF(open_simul_RDONLY_RDONLY) { test_open_simul(O_RDONLY, O_RDONLY); } +DEF(open_simul_RDONLY_WRONLY) { test_open_simul(O_RDONLY, O_WRONLY); } +DEF(open_simul_RDONLY_RDWR) { test_open_simul(O_RDONLY, O_RDWR); } +DEF(open_simul_WRONLY_RDONLY) { test_open_simul(O_WRONLY, O_RDONLY); } +DEF(open_simul_WRONLY_WRONLY) { test_open_simul(O_WRONLY, O_WRONLY); } +DEF(open_simul_WRONLY_RDWR) { test_open_simul(O_WRONLY, O_RDWR); } +DEF(open_simul_RDWR_RDONLY) { test_open_simul(O_RDWR, O_RDONLY); } +DEF(open_simul_RDWR_WRONLY) { test_open_simul(O_RDWR, O_WRONLY); } +DEF(open_simul_RDWR_RDWR) { test_open_simul(O_RDWR, O_RDWR); } + +/* + * /dev/audio can be opened by other user who opens /dev/audio. + */ +void +try_open_multiuser(bool multiuser) +{ + int fd0; + int fd1; + int r; + uid_t ouid; + + /* + * Test1: Open as root first and then unprivileged user. + */ + + /* At first, open as root */ + fd0 = OPEN(devaudio, openable_mode()); + REQUIRED_SYS_OK(fd0); + + ouid = GETUID(); + r = SETEUID(1); + REQUIRED_SYS_EQ(0, r); + + /* Then, open as unprivileged user */ + fd1 = OPEN(devaudio, openable_mode()); + if (multiuser) { + /* If multiuser, another user also can open */ + XP_SYS_OK(fd1); + } else { + /* If not multiuser, another user cannot open */ + XP_SYS_NG(EPERM, fd1); + } + if (fd1 != -1) { + r = CLOSE(fd1); + XP_SYS_EQ(0, r); + } + + r = SETEUID(ouid); + REQUIRED_SYS_EQ(0, r); + + r = CLOSE(fd0); + XP_SYS_EQ(0, r); + + /* + * Test2: Open as unprivileged user first and then root. + */ + + /* At first, open as unprivileged user */ + ouid = GETUID(); + r = SETEUID(1); + REQUIRED_SYS_EQ(0, r); + + fd0 = OPEN(devaudio, openable_mode()); + REQUIRED_SYS_OK(fd0); + + /* Then open as root */ + r = SETEUID(ouid); + REQUIRED_SYS_EQ(0, r); + + /* root always can open */ + fd1 = OPEN(devaudio, openable_mode()); + XP_SYS_OK(fd1); + if (fd1 != -1) { + r = CLOSE(fd1); + XP_SYS_EQ(0, r); + } + + /* Close first one as unprivileged user */ + r = SETEUID(1); + REQUIRED_SYS_EQ(0, r); + r = CLOSE(fd0); + XP_SYS_EQ(0, r); + r = SETEUID(ouid); + REQUIRED_SYS_EQ(0, r); +} +/* + * This is a wrapper for open_multiuser. + * XXX XP_* macros are not compatible with on-error-goto, we need try-catch... + */ +void +test_open_multiuser(bool multiuser) +{ + char mibname[32]; + bool oldval; + size_t oldlen; + int r; + + TEST("open_multiuser_%d", multiuser); + if (netbsd < 8) { + XP_SKIP("Multiple open is not supported"); + return; + } + if (netbsd < 9) { + /* NetBSD8 has no way (difficult) to determine device name */ + XP_SKIP("NetBSD8 cannot determine device name"); + return; + } + if (geteuid() != 0) { + XP_SKIP("Must be run as a privileged user"); + return; + } + + /* Get current multiuser mode (and save it) */ + snprintf(mibname, sizeof(mibname), "hw.%s.multiuser", devicename); + oldlen = sizeof(oldval); + r = SYSCTLBYNAME(mibname, &oldval, &oldlen, NULL, 0); + REQUIRED_SYS_EQ(0, r); + DPRINTF(" > multiuser=%d\n", oldval); + + /* Change if necessary */ + if (oldval != multiuser) { + r = SYSCTLBYNAME(mibname, NULL, NULL, &multiuser, + sizeof(multiuser)); + REQUIRED_SYS_EQ(0, r); + DPRINTF(" > new multiuser=%d\n", multiuser); + } + + /* Do test */ + try_open_multiuser(multiuser); + + /* Restore multiuser mode */ + if (oldval != multiuser) { + DPRINTF(" > restore multiuser to %d\n", oldval); + r = SYSCTLBYNAME(mibname, NULL, NULL, &oldval, sizeof(oldval)); + REQUIRED_SYS_EQ(0, r); + } +} +DEF(open_multiuser_0) { test_open_multiuser(false); } +DEF(open_multiuser_1) { test_open_multiuser(true); } + +/* + * Normal playback (with PLAY_ALL). + * It does not verify real playback data. + */ +DEF(write_PLAY_ALL) +{ + char buf[8000]; + int fd; + int r; + + TEST("write_PLAY_ALL"); + + fd = OPEN(devaudio, O_WRONLY); + if (hw_canplay()) { + REQUIRED_SYS_OK(fd); + } else { + XP_SYS_NG(ENXIO, fd); + return; + } + + /* mulaw 1sec silence */ + memset(buf, 0xff, sizeof(buf)); + r = WRITE(fd, buf, sizeof(buf)); + XP_SYS_EQ(sizeof(buf), r); + + r = CLOSE(fd); + XP_SYS_EQ(0, r); +} + +/* + * Normal playback (without PLAY_ALL). + * It does not verify real playback data. + */ +DEF(write_PLAY) +{ + struct audio_info ai; + char *wav; + int wavsize; + int totalsize; + int fd; + int r; + + TEST("write_PLAY"); + + fd = OPEN(devaudio, O_WRONLY); + if (hw_canplay()) { + REQUIRED_SYS_OK(fd); + } else { + XP_SYS_NG(ENXIO, fd); + return; + } + + /* Drop PLAY_ALL */ + AUDIO_INITINFO(&ai); + ai.mode = AUMODE_PLAY; + r = IOCTL(fd, AUDIO_SETINFO, &ai, "mode"); + REQUIRED_SYS_EQ(0, r); + + /* Check mode and get blocksize */ + r = IOCTL(fd, AUDIO_GETBUFINFO, &ai, ""); + REQUIRED_SYS_EQ(0, r); + XP_EQ(AUMODE_PLAY, ai.mode); + + wavsize = ai.blocksize; + wav = (char *)malloc(wavsize); + REQUIRED_IF(wav != NULL); + memset(wav, 0xff, wavsize); + + /* Write blocks until 1sec */ + for (totalsize = 0; totalsize < 8000; ) { + r = WRITE(fd, wav, wavsize); + XP_SYS_EQ(wavsize, r); + if (r == -1) + break; /* XXX */ + totalsize += r; + } + + /* XXX What should I test it? */ + /* Check ai.play.error */ + r = IOCTL(fd, AUDIO_GETBUFINFO, &ai, ""); + REQUIRED_SYS_EQ(0, r); + XP_EQ(0, ai.play.error); + + /* Playback data is no longer necessary */ + r = IOCTL(fd, AUDIO_FLUSH, NULL, ""); + REQUIRED_SYS_EQ(0, r); + + r = CLOSE(fd); + REQUIRED_SYS_EQ(0, r); + + free(wav); +} + +/* + * Normal recording. + * It does not verify real recorded data. + */ +DEF(read) +{ + char buf[8000]; + int fd; + int r; + + TEST("read"); + + fd = OPEN(devaudio, O_RDONLY); + if (hw_canrec()) { + REQUIRED_SYS_OK(fd); + } else { + XP_SYS_NG(ENXIO, fd); + return; + } + + /* mulaw 1sec */ + r = READ(fd, buf, sizeof(buf)); + XP_SYS_EQ(sizeof(buf), r); + + r = CLOSE(fd); + XP_SYS_EQ(0, r); +} + +/* + * Repeat open-write-close cycle. + */ +DEF(rept_write) +{ + struct timeval start, end, result; + double res; + char buf[8000]; /* 1sec in 8bit-mulaw,1ch,8000Hz */ + int fd; + int r; + int n; + + TEST("rept_write"); + + if (hw_canplay() == 0) { + XP_SKIP("This test is only for playable device"); + return; + } + + /* XXX It may timeout on some hardware driver. */ + XP_SKIP("not yet"); + return; + + memset(buf, 0xff, sizeof(buf)); + n = 3; + gettimeofday(&start, NULL); + for (int i = 0; i < n; i++) { + fd = OPEN(devaudio, O_WRONLY); + REQUIRED_SYS_OK(fd); + + r = WRITE(fd, buf, sizeof(buf)); + XP_SYS_EQ(sizeof(buf), r); + + r = CLOSE(fd); + XP_SYS_EQ(0, r); + } + gettimeofday(&end, NULL); + timersub(&end, &start, &result); + res = (double)result.tv_sec + (double)result.tv_usec / 1000000; + /* Make judgement but not too strict */ + if (res >= n * 1.5) { + XP_FAIL("expects %d sec but %4.1f sec", n, res); + return; + } +} + +/* + * Repeat open-read-close cycle. + */ +DEF(rept_read) +{ + struct timeval start, end, result; + double res; + char buf[8000]; /* 1sec in 8bit-mulaw,1ch,8000Hz */ + int fd; + int r; + int n; + + TEST("rept_read"); + + if (hw_canrec() == 0) { + XP_SKIP("This test is only for recordable device"); + return; + } + + /* XXX It may timeout on some hardware driver. */ + XP_SKIP("not yet"); + return; + + n = 3; + gettimeofday(&start, NULL); + for (int i = 0; i < n; i++) { + fd = OPEN(devaudio, O_RDONLY); + REQUIRED_SYS_OK(fd); + + r = READ(fd, buf, sizeof(buf)); + XP_SYS_EQ(sizeof(buf), r); + + r = CLOSE(fd); + XP_SYS_EQ(0, r); + } + gettimeofday(&end, NULL); + timersub(&end, &start, &result); + res = (double)result.tv_sec + (double)result.tv_usec / 1000000; + /* Make judgement but not too strict */ + if (res >= n * 1.5) { + XP_FAIL("expects %d sec but %4.1f sec", n, res); + return; + } +} + +/* + * Opening with O_RDWR on half-duplex hardware falls back to O_WRONLY. + * expwrite: expected to be able to play. + * expread : expected to be able to recored. + */ +void +test_rdwr_fallback(int openmode, bool expwrite, bool expread) +{ + struct audio_info ai; + char buf[10]; + int fd; + int r; + + TEST("rdwr_fallback_%s", openmode_str[openmode] + 2); + + if (hw_bidir() == 0) { + XP_SKIP("This test is only for bi-directional device"); + return; + } + + AUDIO_INITINFO(&ai); + ai.play.pause = 1; + ai.record.pause = 1; + + fd = OPEN(devaudio, openmode); + REQUIRED_SYS_OK(fd); + + /* Set pause not to play noise */ + r = IOCTL(fd, AUDIO_SETINFO, &ai, "pause"); + REQUIRED_SYS_EQ(0, r); + + memset(buf, 0xff, sizeof(buf)); + r = WRITE(fd, buf, sizeof(buf)); + if (expwrite) { + XP_SYS_EQ(sizeof(buf), r); + } else { + XP_SYS_NG(EBADF, r); + } + + r = READ(fd, buf, 0); + if (expread) { + XP_SYS_EQ(0, r); + } else { + XP_SYS_NG(EBADF, r); + } + + r = CLOSE(fd); + REQUIRED_SYS_EQ(0, r); +} +DEF(rdwr_fallback_RDONLY) { test_rdwr_fallback(O_RDONLY, false, true); } +DEF(rdwr_fallback_WRONLY) { test_rdwr_fallback(O_WRONLY, true, false); } +DEF(rdwr_fallback_RDWR) { + bool expread; + /* + * On NetBSD7, O_RDWR on half-duplex is accepted. It's possible to + * read and write if they don't occur at the same time. + * On NetBSD9, O_RDWR on half-duplex falls back O_WRONLY. + */ + if (netbsd < 8) { + expread = true; + } else { + expread = hw_fulldup() ? true : false; + } + test_rdwr_fallback(O_RDWR, true, expread); +} + +/* + * On full-duplex hardware, the second descriptor's readablity/writability + * is not depend on the first descriptor('s open mode). + * On half-duplex hardware, it depends on the first descriptor's open mode. + */ +void +test_rdwr_two(int mode0, int mode1) +{ + struct audio_info ai; + char wbuf[100]; /* 1/80sec in 8bit-mulaw,1ch,8000Hz */ + char rbuf[100]; /* 1/80sec in 8bit-mulaw,1ch,8000Hz */ + bool canopen; + bool canwrite; + bool canread; + int fd0; + int fd1; + int r; + struct { + bool canopen; + bool canwrite; + bool canread; + } exptable_full[] = { + /* open write read 1st, 2nd mode */ + { 1, 0, 1 }, /* REC, REC */ + { 1, 1, 0 }, /* REC, PLAY */ + { 1, 1, 1 }, /* REC, BOTH */ + { 1, 0, 1 }, /* PLAY, REC */ + { 1, 1, 0 }, /* PLAY, PLAY */ + { 1, 1, 1 }, /* PLAY, BOTH */ + { 1, 0, 1 }, /* BOTH, REC */ + { 1, 1, 0 }, /* BOTH, PLAY */ + { 1, 1, 1 }, /* BOTH, BOTH */ + }, + exptable_half[] = { + { 1, 0, 1 }, /* REC, REC */ + { 0, 0, 0 }, /* REC, PLAY */ + { 0, 0, 0 }, /* REC, BOTH */ + { 0, 0, 0 }, /* PLAY, REC */ + { 1, 1, 0 }, /* PLAY, PLAY */ + { 1, 1, 0 }, /* PLAY, BOTH */ + { 0, 0, 0 }, /* BOTH, REC */ + { 1, 1, 0 }, /* BOTH, PLAY */ + { 0, 0, 0 }, /* BOTH, BOTH */ + }, *exptable; + + TEST("rdwr_two_%s_%s", + openmode_str[mode0] + 2, + openmode_str[mode1] + 2); + + if (netbsd < 8) { + XP_SKIP("Multiple open is not supported"); + return; + } + if (hw_bidir() == 0) { + XP_SKIP("This test is only for bi-directional device"); + return; + } + + exptable = hw_fulldup() ? exptable_full : exptable_half; + + canopen = exptable[mode0 * 3 + mode1].canopen; + canwrite = exptable[mode0 * 3 + mode1].canwrite; + canread = exptable[mode0 * 3 + mode1].canread; + + if (!canopen) { + XP_SKIP("This combination is not openable on half-duplex"); + return; + } + + fd0 = OPEN(devaudio, mode0); + REQUIRED_SYS_OK(fd0); + + fd1 = OPEN(devaudio, mode1); + REQUIRED_SYS_OK(fd1); + + /* Silent data to make no sound */ + memset(&wbuf, 0xff, sizeof(wbuf)); + /* Pause to make no sound */ + AUDIO_INITINFO(&ai); + ai.play.pause = 1; + r = IOCTL(fd0, AUDIO_SETINFO, &ai, "pause"); + XP_SYS_EQ(0, r); + + /* write(fd1) */ + r = WRITE(fd1, wbuf, sizeof(wbuf)); + if (canwrite) { + XP_SYS_EQ(100, r); + } else { + XP_SYS_NG(EBADF, r); + } + + /* read(fd1) */ + r = READ(fd1, rbuf, sizeof(rbuf)); + if (canread) { + XP_SYS_EQ(100, r); + } else { + XP_SYS_NG(EBADF, r); + } + + r = CLOSE(fd0); + XP_SYS_EQ(0, r); + r = CLOSE(fd1); + XP_SYS_EQ(0, r); +} +DEF(rdwr_two_RDONLY_RDONLY) { test_rdwr_two(O_RDONLY, O_RDONLY); } +DEF(rdwr_two_RDONLY_WRONLY) { test_rdwr_two(O_RDONLY, O_WRONLY); } +DEF(rdwr_two_RDONLY_RDWR) { test_rdwr_two(O_RDONLY, O_RDWR); } +DEF(rdwr_two_WRONLY_RDONLY) { test_rdwr_two(O_WRONLY, O_RDONLY); } +DEF(rdwr_two_WRONLY_WRONLY) { test_rdwr_two(O_WRONLY, O_WRONLY); } +DEF(rdwr_two_WRONLY_RDWR) { test_rdwr_two(O_WRONLY, O_RDWR); } +DEF(rdwr_two_RDWR_RDONLY) { test_rdwr_two(O_RDWR, O_RDONLY); } +DEF(rdwr_two_RDWR_WRONLY) { test_rdwr_two(O_RDWR, O_WRONLY); } +DEF(rdwr_two_RDWR_RDWR) { test_rdwr_two(O_RDWR, O_RDWR); } + +/* + * Read and write different descriptors simultaneously. + * Only on full-duplex. + */ +DEF(rdwr_simul) +{ + char wbuf[1000]; /* 1/8sec in mulaw,1ch,8kHz */ + char rbuf[1000]; + int fd0; + int fd1; + int r; + int status; + pid_t pid; + + TEST("rdwr_simul"); + if (netbsd < 8) { + XP_SKIP("Multiple open is not supported"); + return; + } + if (!hw_fulldup()) { + XP_SKIP("This test is only for full-duplex device"); + return; + } + + /* Silence data to make no sound */ + memset(wbuf, 0xff, sizeof(wbuf)); + + fd0 = OPEN(devaudio, O_WRONLY); + REQUIRED_SYS_OK(fd0); + fd1 = OPEN(devaudio, O_RDONLY); + REQUIRED_SYS_OK(fd1); + + fflush(stdout); + fflush(stderr); + pid = fork(); + if (pid == -1) + xp_err(1, __LINE__, "fork"); + + if (pid == 0) { + /* child (read) */ + for (int i = 0; i < 10; i++) { + r = READ(fd1, rbuf, sizeof(rbuf)); + if (r == -1) + xp_err(1, __LINE__, "read(i=%d)", i); + } + exit(0); + } else { + /* parent (write) */ + for (int i = 0; i < 10; i++) { + r = WRITE(fd0, wbuf, sizeof(wbuf)); + if (r == -1) + xp_err(1, __LINE__, "write(i=%d)", i); + } + waitpid(pid, &status, 0); + } + + CLOSE(fd0); + CLOSE(fd1); + /* If you reach here, consider as success */ + XP_EQ(0, 0); +} + +/* + * DRAIN should work even on incomplete data left. + */ +DEF(drain_incomplete) +{ + struct audio_info ai; + int r; + int fd; + + TEST("drain_incomplete"); + + if (hw_canplay() == 0) { + XP_SKIP("This test is only for playable device"); + return; + } + + fd = OPEN(devaudio, O_WRONLY); + REQUIRED_SYS_OK(fd); + + AUDIO_INITINFO(&ai); + /* let precision > 8 */ + ai.play.encoding = AUDIO_ENCODING_SLINEAR_LE; + ai.play.precision = 16; + ai.mode = AUMODE_PLAY; + r = IOCTL(fd, AUDIO_SETINFO, &ai, ""); + REQUIRED_SYS_EQ(0, r); + /* Write one byte and then close */ + r = WRITE(fd, &r, 1); + XP_SYS_EQ(1, r); + r = CLOSE(fd); + XP_SYS_EQ(0, r); +} + +/* + * DRAIN should work even in pause. + */ +DEF(drain_pause) +{ + struct audio_info ai; + int r; + int fd; + + TEST("drain_pause"); + + if (hw_canplay() == 0) { + XP_SKIP("This test is only for playable device"); + return; + } + + fd = OPEN(devaudio, O_WRONLY); + REQUIRED_SYS_OK(fd); + + /* Set pause */ + AUDIO_INITINFO(&ai); + ai.play.pause = 1; + r = IOCTL(fd, AUDIO_SETINFO, &ai, ""); + XP_SYS_EQ(0, r); + /* Write some data and then close */ + r = WRITE(fd, &r, 4); + XP_SYS_EQ(4, r); + r = CLOSE(fd); + XP_SYS_EQ(0, r); +} + +/* + * DRAIN does not affect for record-only descriptor. + */ +DEF(drain_onrec) +{ + int fd; + int r; + + TEST("drain_onrec"); + + if (hw_canrec() == 0) { + XP_SKIP("This test is only for recordable device"); + return; + } + + fd = OPEN(devaudio, O_RDONLY); + REQUIRED_SYS_OK(fd); + + r = IOCTL(fd, AUDIO_DRAIN, NULL, ""); + XP_SYS_EQ(0, r); + + r = CLOSE(fd); + XP_SYS_EQ(0, r); +} + +/* + * Whether mmap() succeeds with specified parameter. + */ +void +test_mmap_mode(int mode, int prot) +{ + char buf[10]; + struct audio_info ai; + const char *protstr; + int expected; + int fd; + int r; + int len; + void *ptr; + + if (prot == PROT_NONE) { + protstr = "NONE"; + } else if (prot == PROT_READ) { + protstr = "READ"; + } else if (prot == PROT_WRITE) { + protstr = "WRITE"; + } else if (prot == (PROT_READ | PROT_WRITE)) { + protstr = "READWRITE"; + } else { + xp_errx(1, __LINE__, "unknown prot %x\n", prot); + } + TEST("mmap_%s_%s", openmode_str[mode] + 2, protstr); + if ((props & AUDIO_PROP_MMAP) == 0) { + XP_SKIP("This test is only for mmap-able device"); + return; + } + if (mode2aumode(mode) == 0) { + XP_SKIP("Operation not allowed on this hardware property"); + return; + } +#if !defined(NO_RUMP) + if (use_rump) { + XP_SKIP("rump doesn't support mmap"); + return; + } +#endif + + /* + * On NetBSD7 and 8, mmap() always succeeds regardless of open mode. + * On NetBSD9, mmap() succeeds only for writable descriptor. + */ + expected = mode2play(mode); + if (netbsd < 9) { + expected = true; + } + + fd = OPEN(devaudio, mode); + REQUIRED_SYS_OK(fd); + + r = IOCTL(fd, AUDIO_GETBUFINFO, &ai, "get"); + REQUIRED_SYS_EQ(0, r); + + len = ai.play.buffer_size; + + /* Make it pause */ + AUDIO_INITINFO(&ai); + ai.play.pause = 1; + r = IOCTL(fd, AUDIO_SETINFO, &ai, "pause"); + REQUIRED_SYS_EQ(0, r); + + ptr = MMAP(NULL, len, prot, MAP_FILE, fd, 0); + XP_SYS_PTR(expected ? 0 : EACCES, ptr); + if (expected) { + /* XXX Doing mmap(2) doesn't inhibit read(2) */ + if (mode2rec(mode)) { + r = READ(fd, buf, 0); + XP_SYS_EQ(0, r); + } + /* Doing mmap(2) inhibits write(2) */ + if (mode2play(mode)) { + /* NetBSD9 changes errno */ + r = WRITE(fd, buf, 0); + if (netbsd < 9) { + XP_SYS_NG(EINVAL, r); + } else { + XP_SYS_NG(EPERM, r); + } + } + } + if (ptr != MAP_FAILED) { + r = MUNMAP(ptr, len); + XP_SYS_EQ(0, r); + } + + /* Whether the pause is still valid */ + if (mode2play(mode)) { + r = IOCTL(fd, AUDIO_GETBUFINFO, &ai, ""); + XP_SYS_EQ(0, r); + XP_EQ(1, ai.play.pause); + } + + r = CLOSE(fd); + XP_SYS_EQ(0, r); + + reset_after_mmap(); +} +#define PROT_READWRITE (PROT_READ | PROT_WRITE) +DEF(mmap_mode_RDONLY_NONE) { test_mmap_mode(O_RDONLY, PROT_NONE); } +DEF(mmap_mode_RDONLY_READ) { test_mmap_mode(O_RDONLY, PROT_READ); } +DEF(mmap_mode_RDONLY_WRITE) { test_mmap_mode(O_RDONLY, PROT_WRITE); } +DEF(mmap_mode_RDONLY_READWRITE) { test_mmap_mode(O_RDONLY, PROT_READWRITE); } +DEF(mmap_mode_WRONLY_NONE) { test_mmap_mode(O_WRONLY, PROT_NONE); } +DEF(mmap_mode_WRONLY_READ) { test_mmap_mode(O_WRONLY, PROT_READ); } +DEF(mmap_mode_WRONLY_WRITE) { test_mmap_mode(O_WRONLY, PROT_WRITE); } +DEF(mmap_mode_WRONLY_READWRITE) { test_mmap_mode(O_WRONLY, PROT_READWRITE); } +DEF(mmap_mode_RDWR_NONE) { test_mmap_mode(O_RDWR, PROT_NONE); } +DEF(mmap_mode_RDWR_READ) { test_mmap_mode(O_RDWR, PROT_READ); } +DEF(mmap_mode_RDWR_WRITE) { test_mmap_mode(O_RDWR, PROT_WRITE); } +DEF(mmap_mode_RDWR_READWRITE) { test_mmap_mode(O_RDWR, PROT_READWRITE); } + +/* + * Check mmap()'s length and offset. + */ +DEF(mmap_len) +{ + struct audio_info ai; + int fd; + int r; + size_t len; + off_t offset; + void *ptr; + int bufsize; + int pagesize; + int lsize; + + TEST("mmap_len"); + if ((props & AUDIO_PROP_MMAP) == 0) { + XP_SKIP("This test is only for mmap-able device"); + return; + } +#if !defined(NO_RUMP) + if (use_rump) { + XP_SKIP("rump doesn't support mmap"); + return; + } +#endif + + len = sizeof(pagesize); + r = SYSCTLBYNAME("hw.pagesize", &pagesize, &len, NULL, 0); + REQUIRED_SYS_EQ(0, r); + + fd = OPEN(devaudio, O_WRONLY); + REQUIRED_SYS_OK(r); + + /* Get buffer_size */ + r = IOCTL(fd, AUDIO_GETBUFINFO, &ai, ""); + REQUIRED_SYS_EQ(0, r); + bufsize = ai.play.buffer_size; + + /* + * XXX someone refers bufsize and another one does pagesize. + * I'm not sure. + */ + lsize = roundup2(bufsize, pagesize); + struct { + size_t len; + off_t offset; + int exp; + } table[] = { + /* len offset expected */ + + { 0, 0, 0 }, /* len is 0 */ + { 1, 0, 0 }, /* len is smaller than lsize */ + { lsize, 0, 0 }, /* len is the same as lsize */ + { lsize + 1, 0, EOVERFLOW }, /* len is larger */ + + { 0, -1, EINVAL }, /* offset is negative */ + { 0, lsize, 0 }, /* pointless param but ok */ + { 0, lsize + 1, EOVERFLOW }, /* exceed */ + { 1, lsize, EOVERFLOW }, /* exceed */ + + /* + * When you treat offset as 32bit, offset will be 0 + * and thus it incorrectly succeeds. + */ + { lsize, 1ULL<<32, EOVERFLOW }, + }; + + for (int i = 0; i < (int)__arraycount(table); i++) { + len = table[i].len; + offset = table[i].offset; + int exp = table[i].exp; + + ptr = MMAP(NULL, len, PROT_WRITE, MAP_FILE, fd, offset); + if (exp == 0) { + XP_SYS_PTR(0, ptr); + } else { + /* NetBSD8 introduces EOVERFLOW */ + if (netbsd < 8 && exp == EOVERFLOW) + exp = EINVAL; + XP_SYS_PTR(exp, ptr); + } + + if (ptr != MAP_FAILED) { + r = MUNMAP(ptr, len); + XP_SYS_EQ(0, r); + } + } + + r = CLOSE(fd); + XP_SYS_EQ(0, r); + + reset_after_mmap(); +} + +/* + * mmap() the same descriptor twice. + */ +DEF(mmap_twice) +{ + struct audio_info ai; + int fd; + int r; + int len; + void *ptr1; + void *ptr2; + + TEST("mmap_twice"); + if ((props & AUDIO_PROP_MMAP) == 0) { + XP_SKIP("This test is only for mmap-able device"); + return; + } +#if !defined(NO_RUMP) + if (use_rump) { + XP_SKIP("rump doesn't support mmap"); + return; + } +#endif + + fd = OPEN(devaudio, O_WRONLY); + REQUIRED_SYS_OK(fd); + + r = IOCTL(fd, AUDIO_GETBUFINFO, &ai, "get"); + REQUIRED_SYS_EQ(0, r); + len = ai.play.buffer_size; + + ptr1 = MMAP(NULL, len, PROT_WRITE, MAP_FILE, fd, 0); + XP_SYS_PTR(0, ptr1); + + /* XXX I'm not sure this sucess is intended. Anyway I follow it */ + ptr2 = MMAP(NULL, len, PROT_WRITE, MAP_FILE, fd, 0); + XP_SYS_PTR(0, ptr2); + + if (ptr2 != MAP_FAILED) { + r = MUNMAP(ptr2, len); + XP_SYS_EQ(0, r); + } + if (ptr1 != MAP_FAILED) { + r = MUNMAP(ptr1, len); + XP_SYS_EQ(0, r); + } + + r = CLOSE(fd); + XP_SYS_EQ(0, r); + + reset_after_mmap(); +} + +/* + * mmap() different descriptors. + */ +DEF(mmap_multi) +{ + struct audio_info ai; + int fd0; + int fd1; + int r; + int len; + void *ptr0; + void *ptr1; + + TEST("mmap_multi"); + if (netbsd < 8) { + XP_SKIP("Multiple open is not supported"); + return; + } + if ((props & AUDIO_PROP_MMAP) == 0) { + XP_SKIP("This test is only for mmap-able device"); + return; + } +#if !defined(NO_RUMP) + if (use_rump) { + XP_SKIP("rump doesn't support mmap"); + return; + } +#endif + + fd0 = OPEN(devaudio, O_WRONLY); + REQUIRED_SYS_OK(fd0); + + r = IOCTL(fd0, AUDIO_GETBUFINFO, &ai, "get"); + REQUIRED_SYS_EQ(0, r); + len = ai.play.buffer_size; + + fd1 = OPEN(devaudio, O_WRONLY); + REQUIRED_SYS_OK(fd1); + + ptr0 = MMAP(NULL, len, PROT_WRITE, MAP_FILE, fd0, 0); + XP_SYS_PTR(0, ptr0); + + ptr1 = MMAP(NULL, len, PROT_WRITE, MAP_FILE, fd1, 0); + XP_SYS_PTR(0, ptr1); + + if (ptr0 != MAP_FAILED) { + r = MUNMAP(ptr1, len); + XP_SYS_EQ(0, r); + } + + r = CLOSE(fd1); + XP_SYS_EQ(0, r); + + if (ptr1 != MAP_FAILED) { + r = MUNMAP(ptr0, len); + XP_SYS_EQ(0, r); + } + + r = CLOSE(fd0); + XP_SYS_EQ(0, r); + + reset_after_mmap(); +} + +#define IN POLLIN +#define OUT POLLOUT +/* + * Whether poll() succeeds with specified mode. + */ +void +test_poll_mode(int mode, int events, int expected_revents) +{ + struct pollfd pfd; + const char *events_str; + int fd; + int r; + int expected_r; + + if (events == IN) { + events_str = "IN"; + } else if (events == OUT) { + events_str = "OUT"; + } else if (events == (IN | OUT)) { + events_str = "INOUT"; + } else { + events_str = "?"; + } + TEST("poll_mode_%s_%s", openmode_str[mode] + 2, events_str); + if (mode2aumode(mode) == 0) { + XP_SKIP("Operation not allowed on this hardware property"); + return; + } + + expected_r = (expected_revents != 0) ? 1 : 0; + + fd = OPEN(devaudio, mode); + REQUIRED_SYS_OK(fd); + + /* Wait a bit to be recorded. */ + usleep(100 * 1000); + + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = fd; + pfd.events = events; + + r = POLL(&pfd, 1, 100); + /* It's a bit complicated.. */ + if (r < 0 || r > 1) { + /* + * Check these two cases first: + * - system call fails. + * - poll() with one nfds returns >1. It's strange. + */ + XP_SYS_EQ(expected_r, r); + } else { + /* + * Otherwise, poll() returned 0 or 1. + */ + DPRINTF(" > pfd.revents=%s\n", event_tostr(pfd.revents)); + + /* NetBSD7,8 have several strange behavior. It must be bug. */ + + XP_SYS_EQ(expected_r, r); + XP_EQ(expected_revents, pfd.revents); + } + r = CLOSE(fd); + XP_SYS_EQ(0, r); +} +DEF(poll_mode_RDONLY_IN) { test_poll_mode(O_RDONLY, IN, IN); } +DEF(poll_mode_RDONLY_OUT) { test_poll_mode(O_RDONLY, OUT, 0); } +DEF(poll_mode_RDONLY_INOUT) { test_poll_mode(O_RDONLY, IN|OUT, IN); } +DEF(poll_mode_WRONLY_IN) { test_poll_mode(O_WRONLY, IN, 0); } +DEF(poll_mode_WRONLY_OUT) { test_poll_mode(O_WRONLY, OUT, OUT); } +DEF(poll_mode_WRONLY_INOUT) { test_poll_mode(O_WRONLY, IN|OUT, OUT); } +DEF(poll_mode_RDWR_IN) { + /* On half-duplex, O_RDWR is the same as O_WRONLY. */ + if (hw_fulldup()) test_poll_mode(O_RDWR, IN, IN); + else test_poll_mode(O_RDWR, IN, 0); +} +DEF(poll_mode_RDWR_OUT) { test_poll_mode(O_RDWR, OUT, OUT); } +DEF(poll_mode_RDWR_INOUT) { + /* On half-duplex, O_RDWR is the same as O_WRONLY. */ + if (hw_fulldup()) test_poll_mode(O_RDWR, IN|OUT, IN|OUT); + else test_poll_mode(O_RDWR, IN|OUT, OUT); +} + +/* + * Poll(OUT) when buffer is empty. + */ +DEF(poll_out_empty) +{ + struct pollfd pfd; + int fd; + int r; + + TEST("poll_out_empty"); + + fd = OPEN(devaudio, O_WRONLY); + REQUIRED_SYS_OK(fd); + + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = fd; + pfd.events = POLLOUT; + + /* Check when empty. It should succeed even if timeout == 0 */ + r = POLL(&pfd, 1, 0); + XP_SYS_EQ(1, r); + XP_EQ(POLLOUT, pfd.revents); + + r = CLOSE(fd); + XP_SYS_EQ(0, r); +} + +/* + * Poll(OUT) when buffer is full. + */ +DEF(poll_out_full) +{ + struct audio_info ai; + struct pollfd pfd; + int fd; + int r; + char *buf; + int buflen; + + TEST("poll_out_full"); + + fd = OPEN(devaudio, O_WRONLY | O_NONBLOCK); + REQUIRED_SYS_OK(fd); + + /* Pause */ + AUDIO_INITINFO(&ai); + ai.play.pause = 1; + r = IOCTL(fd, AUDIO_SETINFO, &ai, ""); + XP_SYS_EQ(0, r); + + /* Get buffer size */ + r = IOCTL(fd, AUDIO_GETBUFINFO, &ai, ""); + XP_SYS_EQ(0, r); + + /* Write until full */ + buflen = ai.play.buffer_size; + buf = (char *)malloc(buflen); + REQUIRED_IF(buf != NULL); + memset(buf, 0xff, buflen); + do { + r = WRITE(fd, buf, buflen); + } while (r == buflen); + if (r == -1) { + XP_SYS_NG(EAGAIN, r); + } + + /* Do poll */ + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = fd; + pfd.events = POLLOUT; + r = POLL(&pfd, 1, 0); + XP_SYS_EQ(0, r); + XP_EQ(0, pfd.revents); + + r = CLOSE(fd); + XP_SYS_EQ(0, r); + free(buf); +} + +/* + * Poll(OUT) when buffer is full but hiwat sets lower than full. + */ +DEF(poll_out_hiwat) +{ + struct audio_info ai; + struct pollfd pfd; + int fd; + int r; + char *buf; + int buflen; + int newhiwat; + + TEST("poll_out_hiwat"); + + fd = OPEN(devaudio, O_WRONLY | O_NONBLOCK); + REQUIRED_SYS_OK(fd); + + /* Get buffer size and hiwat */ + r = IOCTL(fd, AUDIO_GETBUFINFO, &ai, ""); + XP_SYS_EQ(0, r); + /* Change hiwat some different value */ + newhiwat = ai.lowat; + + /* Set pause and hiwat */ + AUDIO_INITINFO(&ai); + ai.play.pause = 1; + ai.hiwat = newhiwat; + r = IOCTL(fd, AUDIO_SETINFO, &ai, "pause=1;hiwat"); + XP_SYS_EQ(0, r); + + /* Get the set hiwat again */ + r = IOCTL(fd, AUDIO_GETBUFINFO, &ai, ""); + XP_SYS_EQ(0, r); + + /* Write until full */ + buflen = ai.blocksize * ai.hiwat; + buf = (char *)malloc(buflen); + REQUIRED_IF(buf != NULL); + memset(buf, 0xff, buflen); + do { + r = WRITE(fd, buf, buflen); + } while (r == buflen); + if (r == -1) { + XP_SYS_NG(EAGAIN, r); + } + + /* Do poll */ + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = fd; + pfd.events = POLLOUT; + r = POLL(&pfd, 1, 0); + XP_SYS_EQ(0, r); + XP_EQ(0, pfd.revents); + + r = CLOSE(fd); + XP_SYS_EQ(0, r); + free(buf); +} + +/* + * Unpause from buffer full, POLLOUT should raise. + * XXX poll(2) on NetBSD7 is really incomplete and wierd. I don't test it. + */ +DEF(poll_out_unpause) +{ + struct audio_info ai; + struct pollfd pfd; + int fd; + int r; + char *buf; + int buflen; + u_int blocksize; + int hiwat; + int lowat; + + TEST("poll_out_unpause"); + if (netbsd < 8) { + XP_SKIP("NetBSD7's poll() is too incomplete to test."); + return; + } + + /* Non-blocking open */ + fd = OPEN(devaudio, O_WRONLY | O_NONBLOCK); + REQUIRED_SYS_OK(fd); + + /* Adjust block size and hiwat/lowat to make the test time 1sec */ + blocksize = 1000; /* 1/8 sec in mulaw,1ch,8000Hz */ + hiwat = 12; /* 1.5sec */ + lowat = 4; /* 0.5sec */ + AUDIO_INITINFO(&ai); + ai.blocksize = blocksize; + ai.hiwat = hiwat; + ai.lowat = lowat; + /* and also set encoding */ + /* + * XXX NetBSD7 has different results depending on whether the input + * encoding is emulated (AUDIO_ENCODINGFLAG_EMULATED) or not. It's + * not easy to ensure this situation on all hardware environment. + * On NetBSD9, the result is the same regardless of input encoding. + */ + r = IOCTL(fd, AUDIO_SETINFO, &ai, "blocksize=%d", blocksize); + XP_SYS_EQ(0, r); + r = IOCTL(fd, AUDIO_GETBUFINFO, &ai, ""); + if (ai.blocksize != blocksize) { + /* + * NetBSD9 can not change the blocksize. Then, + * adjust using hiwat/lowat. + */ + blocksize = ai.blocksize; + hiwat = howmany(8000 * 1.5, blocksize); + lowat = howmany(8000 * 0.5, blocksize); + } + /* Anyway, set the parameters */ + AUDIO_INITINFO(&ai); + ai.blocksize = blocksize; + ai.hiwat = hiwat; + ai.lowat = lowat; + ai.play.pause = 1; + r = IOCTL(fd, AUDIO_SETINFO, &ai, "pause=1"); + XP_SYS_EQ(0, r); + + /* Get the set parameters again */ + r = IOCTL(fd, AUDIO_GETBUFINFO, &ai, ""); + XP_SYS_EQ(0, r); + + /* Write until full */ + buflen = ai.blocksize * ai.hiwat; + buf = (char *)malloc(buflen); + REQUIRED_IF(buf != NULL); + memset(buf, 0xff, buflen); + do { + r = WRITE(fd, buf, buflen); + } while (r == buflen); + if (r == -1) { + XP_SYS_NG(EAGAIN, r); + } + + /* At this time, POLLOUT should not be set because buffer is full */ + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = fd; + pfd.events = POLLOUT; + r = POLL(&pfd, 1, 0); + XP_SYS_EQ(0, r); + XP_EQ(0, pfd.revents); + + /* Unpause */ + AUDIO_INITINFO(&ai); + ai.play.pause = 0; + r = IOCTL(fd, AUDIO_SETINFO, &ai, "pause=0"); + XP_SYS_EQ(0, r); + + /* + * When unpause occurs: + * - NetBSD7 (emul=0) -> the buffer remains. + * - NetBSD7 (emul=1) -> the buffer is cleared. + * - NetBSD8 -> the buffer remains. + * - NetBSD9 -> the buffer remains. + */ + + /* Check poll() up to 2sec */ + pfd.revents = 0; + r = POLL(&pfd, 1, 2000); + XP_SYS_EQ(1, r); + XP_EQ(POLLOUT, pfd.revents); + + /* + * Since POLLOUT is set, it should be writable. + * But at this time, no all buffer may be writable. + */ + r = WRITE(fd, buf, buflen); + XP_SYS_OK(r); + + /* Flush it because there is no need to play it */ + r = IOCTL(fd, AUDIO_FLUSH, NULL, ""); + XP_SYS_EQ(0, r); + + r = CLOSE(fd); + XP_SYS_EQ(0, r); + free(buf); +} + +/* + * poll(2) must not be affected by playback of other descriptors. + */ +DEF(poll_out_simul) +{ + struct audio_info ai; + struct pollfd pfd[2]; + int fd[2]; + int r; + char *buf; + u_int blocksize; + int hiwat; + int lowat; + int buflen; + int time; + + TEST("poll_out_simul"); + if (netbsd < 8) { + XP_SKIP("Multiple open is not supported"); + return; + } + + /* Make sure that it's not affected by descriptor order */ + for (int i = 0; i < 2; i++) { + int a = i; + int b = 1 - i; + + fd[0] = OPEN(devaudio, O_WRONLY | O_NONBLOCK); + REQUIRED_SYS_OK(fd[0]); + fd[1] = OPEN(devaudio, O_WRONLY | O_NONBLOCK); + REQUIRED_SYS_OK(fd[1]); + + /* + * Adjust block size and hiwat/lowat. + * I want to choice suitable blocksize (if possible). + */ + blocksize = 1000; /* 1/8 sec in mulaw,1ch,8000Hz */ + hiwat = 12; /* 1.5sec */ + lowat = 4; /* 0.5sec */ + AUDIO_INITINFO(&ai); + ai.blocksize = blocksize; + ai.hiwat = hiwat; + ai.lowat = lowat; + r = IOCTL(fd[0], AUDIO_SETINFO, &ai, "blocksize=1000"); + XP_SYS_EQ(0, r); + r = IOCTL(fd[0], AUDIO_GETBUFINFO, &ai, "read back blocksize"); + if (ai.blocksize != blocksize) { + /* + * NetBSD9 can not change the blocksize. Then, + * adjust using hiwat/lowat. + */ + blocksize = ai.blocksize; + hiwat = howmany(8000 * 1.5, blocksize); + lowat = howmany(8000 * 0.5, blocksize); + } + /* Anyway, set the parameters */ + AUDIO_INITINFO(&ai); + ai.blocksize = blocksize; + ai.hiwat = hiwat; + ai.lowat = lowat; + /* Pause fdA */ + ai.play.pause = 1; + r = IOCTL(fd[a], AUDIO_SETINFO, &ai, "pause=1"); + XP_SYS_EQ(0, r); + /* Unpause fdB */ + ai.play.pause = 0; + r = IOCTL(fd[b], AUDIO_SETINFO, &ai, "pause=0"); + XP_SYS_EQ(0, r); + + /* Get again. XXX two individual ioctls are correct */ + r = IOCTL(fd[0], AUDIO_GETBUFINFO, &ai, ""); + XP_SYS_EQ(0, r); + DPRINTF(" > blocksize=%d lowat=%d hiwat=%d\n", + ai.blocksize, ai.lowat, ai.hiwat); + + /* Enough long time than the playback time */ + time = (ai.hiwat - ai.lowat) * blocksize / 8; /*[msec]*/ + time *= 2; + + /* Write fdA full */ + buflen = blocksize * ai.lowat; + buf = (char *)malloc(buflen); + REQUIRED_IF(buf != NULL); + memset(buf, 0xff, buflen); + do { + r = WRITE(fd[a], buf, buflen); + } while (r == buflen); + if (r == -1) { + XP_SYS_NG(EAGAIN, r); + } + + /* POLLOUT should not be set, because fdA is buffer full */ + memset(pfd, 0, sizeof(pfd)); + pfd[0].fd = fd[a]; + pfd[0].events = POLLOUT; + r = POLL(pfd, 1, 0); + XP_SYS_EQ(0, r); + XP_EQ(0, pfd[0].revents); + + /* Write fdB at least lowat */ + r = WRITE(fd[b], buf, buflen); + XP_SYS_EQ(buflen, r); + r = WRITE(fd[b], buf, buflen); + if (r == -1) { + XP_SYS_NG(EAGAIN, r); + } + + /* Only fdB should become POLLOUT */ + memset(pfd, 0, sizeof(pfd)); + pfd[0].fd = fd[0]; + pfd[0].events = POLLOUT; + pfd[1].fd = fd[1]; + pfd[1].events = POLLOUT; + r = POLL(pfd, 2, time); + XP_SYS_EQ(1, r); + if (r != -1) { + XP_EQ(0, pfd[a].revents); + XP_EQ(POLLOUT, pfd[b].revents); + } + + /* Drop the rest */ + r = IOCTL(fd[0], AUDIO_FLUSH, NULL, ""); + XP_SYS_EQ(0, r); + r = IOCTL(fd[1], AUDIO_FLUSH, NULL, ""); + XP_SYS_EQ(0, r); + + r = CLOSE(fd[0]); + XP_SYS_EQ(0, r); + r = CLOSE(fd[1]); + XP_SYS_EQ(0, r); + free(buf); + + xxx_close_wait(); + } +} + +/* + * Open with READ mode starts recording immediately. + * Of course, audioctl doesn't start. + */ +void +test_poll_in_open(const char *devname) +{ + struct audio_info ai; + struct pollfd pfd; + char buf[4096]; + char devfile[16]; + int fd; + int r; + bool is_audioctl; + + TEST("poll_in_open_%s", devname); + if (hw_canrec() == 0) { + XP_SKIP("This test is only for recordable device"); + return; + } + + snprintf(devfile, sizeof(devfile), "/dev/%s%d", devname, unit); + is_audioctl = (strcmp(devname, "audioctl") == 0); + + fd = OPEN(devfile, O_RDONLY); + REQUIRED_SYS_OK(fd); + + r = IOCTL(fd, AUDIO_GETBUFINFO, &ai, ""); + REQUIRED_SYS_EQ(0, r); + if (is_audioctl) { + /* opening /dev/audioctl doesn't start recording. */ + XP_EQ(0, ai.record.active); + } else { + /* opening /dev/{audio,sound} starts recording. */ + /* + * On NetBSD7/8, opening /dev/sound doesn't start recording. + * It must be a bug. + */ + XP_EQ(1, ai.record.active); + } + + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = fd; + pfd.events = POLLIN; + r = POLL(&pfd, 1, 1000); + if (is_audioctl) { + /* + * poll-ing /dev/audioctl always fails. + * XXX Returning error instead of timeout should be better(?). + */ + REQUIRED_SYS_EQ(0, r); + } else { + /* + * poll-ing /dev/{audio,sound} will succeed when recorded + * data is arrived. + */ + /* + * On NetBSD7/8, opening /dev/sound doesn't start recording. + * It must be a bug. + */ + REQUIRED_SYS_EQ(1, r); + + /* In this case, read() should succeed. */ + r = READ(fd, buf, sizeof(buf)); + XP_SYS_OK(r); + XP_NE(0, r); + } + + r = CLOSE(fd); + XP_SYS_EQ(0, r); +} +DEF(poll_in_open_audio) { test_poll_in_open("audio"); } +DEF(poll_in_open_sound) { test_poll_in_open("sound"); } +DEF(poll_in_open_audioctl) { test_poll_in_open("audioctl"); } + +/* + * poll(2) must not be affected by other recording descriptors even if + * playback descriptor waits with POLLIN (though it's not normal usage). + * In other words, two POLLIN must not interfere. + */ +DEF(poll_in_simul) +{ + struct audio_info ai; + struct pollfd pfd; + int fd[2]; + int r; + char *buf; + int blocksize; + + TEST("poll_in_simul"); + if (netbsd < 8) { + XP_SKIP("Multiple open is not supported"); + return; + } + if (hw_fulldup() == 0) { + XP_SKIP("This test is only for full-duplex device"); + return; + } + + int play = 0; + int rec = 1; + + fd[play] = OPEN(devaudio, O_WRONLY | O_NONBLOCK); + REQUIRED_SYS_OK(fd[play]); + fd[rec] = OPEN(devaudio, O_RDONLY); + REQUIRED_SYS_OK(fd[rec]); + + /* Get block size */ + r = IOCTL(fd[rec], AUDIO_GETBUFINFO, &ai, ""); + XP_SYS_EQ(0, r); + blocksize = ai.blocksize; + + buf = (char *)malloc(blocksize); + REQUIRED_IF(buf != NULL); + + /* + * At first, make sure the playback one doesn't return POLLIN. + */ + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = fd[play]; + pfd.events = POLLIN; + r = POLL(&pfd, 1, 0); + if (r == 0 && pfd.revents == 0) { + XP_SYS_EQ(0, r); + XP_EQ(0, pfd.revents); + } else { + XP_FAIL("play fd returns POLLIN"); + goto abort; + } + + /* Start recording */ + r = READ(fd[rec], buf, blocksize); + XP_SYS_EQ(blocksize, r); + + /* Poll()ing playback descriptor with POLLIN should not raise */ + r = POLL(&pfd, 1, 1000); + XP_SYS_EQ(0, r); + XP_EQ(0, pfd.revents); + + /* Poll()ing recording descriptor with POLLIN should raise */ + pfd.fd = fd[rec]; + r = POLL(&pfd, 1, 0); + XP_SYS_EQ(1, r); + XP_EQ(POLLIN, pfd.revents); + +abort: + r = CLOSE(fd[play]); + XP_SYS_EQ(0, r); + r = CLOSE(fd[rec]); + XP_SYS_EQ(0, r); + free(buf); +} + +/* + * Whether kqueue() succeeds with specified mode. + */ +void +test_kqueue_mode(int openmode, int filt, int expected) +{ + struct kevent kev; + struct timespec ts; + int fd; + int kq; + int r; + + TEST("kqueue_mode_%s_%s", + openmode_str[openmode] + 2, + (filt == EVFILT_READ) ? "READ" : "WRITE"); + if (mode2aumode(openmode) == 0) { + XP_SKIP("Operation not allowed on this hardware property"); + return; + } + + ts.tv_sec = 0; + ts.tv_nsec = 100 * 1000 * 1000; // 100msec + + kq = KQUEUE(); + XP_SYS_OK(kq); + + fd = OPEN(devaudio, openmode); + REQUIRED_SYS_OK(fd); + + /* + * Check whether the specified filter can be set. + * Any filters can always be set, even if pointless combination. + * For example, EVFILT_READ can be set on O_WRONLY descriptor + * though it will never raise. + * I will not mention about good or bad of this behavior here. + */ + EV_SET(&kev, fd, filt, EV_ADD, 0, 0, 0); + r = KEVENT_SET(kq, &kev, 1); + XP_SYS_EQ(0, r); + + if (r == 0) { + /* If the filter can be set, try kevent(poll) */ + r = KEVENT_POLL(kq, &kev, 1, &ts); + XP_SYS_EQ(expected, r); + + /* Delete it */ + EV_SET(&kev, fd, filt, EV_DELETE, 0, 0, 0); + r = KEVENT_SET(kq, &kev, 1); + XP_SYS_EQ(0, r); + } + + r = CLOSE(fd); + XP_SYS_EQ(0, r); + r = CLOSE(kq); + XP_SYS_EQ(0, r); +} +DEF(kqueue_mode_RDONLY_READ) { + /* Should raise */ + test_kqueue_mode(O_RDONLY, EVFILT_READ, 1); +} +DEF(kqueue_mode_RDONLY_WRITE) { + /* Should never raise (NetBSD7 has bugs) */ + int expected = (netbsd < 8) ? 1 : 0; + test_kqueue_mode(O_RDONLY, EVFILT_WRITE, expected); +} +DEF(kqueue_mode_WRONLY_READ) { + /* Should never raise */ + test_kqueue_mode(O_WRONLY, EVFILT_READ, 0); +} +DEF(kqueue_mode_WRONLY_WRITE) { + /* Should raise */ + test_kqueue_mode(O_WRONLY, EVFILT_WRITE, 1); +} +DEF(kqueue_mode_RDWR_READ) { + /* Should raise on fulldup but not on halfdup, on NetBSD9 */ + int expected = hw_fulldup() ? 1 : 0; + test_kqueue_mode(O_RDWR, EVFILT_READ, expected); +} +DEF(kqueue_mode_RDWR_WRITE) { + /* Should raise */ + test_kqueue_mode(O_RDWR, EVFILT_WRITE, 1); +} + +/* + * kqueue(2) when buffer is empty. + */ +DEF(kqueue_empty) +{ + struct audio_info ai; + struct kevent kev; + struct timespec ts; + int kq; + int fd; + int r; + + TEST("kqueue_empty"); + + fd = OPEN(devaudio, O_WRONLY); + REQUIRED_SYS_OK(fd); + + r = IOCTL(fd, AUDIO_GETBUFINFO, &ai, ""); + XP_SYS_EQ(0, r); + + kq = KQUEUE(); + XP_SYS_OK(kq); + + EV_SET(&kev, fd, EV_ADD, EVFILT_WRITE, 0, 0, 0); + r = KEVENT_SET(kq, &kev, 1); + XP_SYS_EQ(0, r); + + /* When the buffer is empty, it should succeed even if timeout == 0 */ + memset(&ts, 0, sizeof(ts)); + r = KEVENT_POLL(kq, &kev, 1, &ts); + XP_SYS_EQ(1, r); + XP_EQ(fd, kev.ident); + /* + * XXX According to kqueue(2) manpage, returned kev.data contains + * "the amount of space remaining in the write buffer". + * NetBSD7 returns buffer_size. Shouldn't it be blocksize * hiwat? + */ + /* XP_EQ(ai.blocksize * ai.hiwat, kev.data); */ + XP_EQ(ai.play.buffer_size, kev.data); + + r = CLOSE(fd); + XP_SYS_EQ(0, r); + r = CLOSE(kq); + XP_SYS_EQ(0, r); +} + +/* + * kqueue(2) when buffer is full. + */ +DEF(kqueue_full) +{ + struct audio_info ai; + struct kevent kev; + struct timespec ts; + int kq; + int fd; + int r; + char *buf; + int buflen; + + TEST("kqueue_full"); + + fd = OPEN(devaudio, O_WRONLY | O_NONBLOCK); + REQUIRED_SYS_OK(fd); + + /* Pause */ + AUDIO_INITINFO(&ai); + ai.play.pause = 1; + r = IOCTL(fd, AUDIO_SETINFO, &ai, ""); + XP_SYS_EQ(0, r); + + /* Get buffer size */ + r = IOCTL(fd, AUDIO_GETBUFINFO, &ai, ""); + XP_SYS_EQ(0, r); + + /* Write until full */ + buflen = ai.play.buffer_size; + buf = (char *)malloc(buflen); + REQUIRED_IF(buf != NULL); + memset(buf, 0xff, buflen); + do { + r = WRITE(fd, buf, buflen); + } while (r == buflen); + if (r == -1) { + XP_SYS_NG(EAGAIN, r); + } + + kq = KQUEUE(); + XP_SYS_OK(kq); + + EV_SET(&kev, fd, EV_ADD, EVFILT_WRITE, 0, 0, 0); + r = KEVENT_SET(kq, &kev, 1); + XP_SYS_EQ(0, r); + + /* kevent() should not raise */ + ts.tv_sec = 0; + ts.tv_nsec = 100L * 1000 * 1000; /* 100msec */ + r = KEVENT_POLL(kq, &kev, 1, &ts); + XP_SYS_EQ(0, r); + if (r > 0) { + XP_EQ(fd, kev.ident); + XP_EQ(0, kev.data); + } + + r = CLOSE(fd); + XP_SYS_EQ(0, r); + r = CLOSE(kq); + XP_SYS_EQ(0, r); + free(buf); +} + +/* + * kqueue(2) when buffer is full but hiwat sets lower than full. + */ +DEF(kqueue_hiwat) +{ + struct audio_info ai; + struct kevent kev; + struct timespec ts; + int kq; + int fd; + int r; + char *buf; + int buflen; + int newhiwat; + + TEST("kqueue_hiwat"); + + fd = OPEN(devaudio, O_WRONLY | O_NONBLOCK); + REQUIRED_SYS_OK(fd); + + /* Get buffer size and hiwat */ + r = IOCTL(fd, AUDIO_GETBUFINFO, &ai, "hiwat"); + XP_SYS_EQ(0, r); + /* Change hiwat some different value */ + newhiwat = ai.hiwat - 1; + + /* Set pause and hiwat */ + AUDIO_INITINFO(&ai); + ai.play.pause = 1; + ai.hiwat = newhiwat; + r = IOCTL(fd, AUDIO_SETINFO, &ai, "pause=1;hiwat"); + XP_SYS_EQ(0, r); + + /* Get the set parameters again */ + r = IOCTL(fd, AUDIO_GETBUFINFO, &ai, ""); + XP_SYS_EQ(0, r); + XP_EQ(1, ai.play.pause); + XP_EQ(newhiwat, ai.hiwat); + + /* Write until full */ + buflen = ai.blocksize * ai.hiwat; + buf = (char *)malloc(buflen); + REQUIRED_IF(buf != NULL); + memset(buf, 0xff, buflen); + do { + r = WRITE(fd, buf, buflen); + } while (r == buflen); + if (r == -1) { + XP_SYS_NG(EAGAIN, r); + } + + kq = KQUEUE(); + XP_SYS_OK(kq); + + EV_SET(&kev, fd, EV_ADD, EVFILT_WRITE, 0, 0, 0); + r = KEVENT_SET(kq, &kev, 1); + XP_SYS_EQ(0, r); + + /* Should not raise because it's not possible to write */ + ts.tv_sec = 0; + ts.tv_nsec = 100L * 1000 * 1000; /* 100msec */ + r = KEVENT_POLL(kq, &kev, 1, &ts); + if (r > 0) + DEBUG_KEV("kev", &kev); + XP_SYS_EQ(0, r); + + r = CLOSE(fd); + XP_SYS_EQ(0, r); + r = CLOSE(kq); + XP_SYS_EQ(0, r); + free(buf); +} + +/* + * Unpause from buffer full, kevent() should raise. + */ +DEF(kqueue_unpause) +{ + struct audio_info ai; + struct kevent kev; + struct timespec ts; + int fd; + int r; + int kq; + char *buf; + int buflen; + u_int blocksize; + int hiwat; + int lowat; + + TEST("kqueue_unpause"); + + /* Non-blocking open */ + fd = OPEN(devaudio, O_WRONLY | O_NONBLOCK); + REQUIRED_SYS_OK(fd); + + /* Adjust block size and hiwat/lowat to make the test time 1sec */ + blocksize = 1000; /* 1/8 sec in mulaw,1ch,8000Hz */ + hiwat = 12; /* 1.5sec */ + lowat = 4; /* 0.5sec */ + AUDIO_INITINFO(&ai); + ai.blocksize = blocksize; + ai.hiwat = hiwat; + ai.lowat = lowat; + /* and also set encoding */ + /* + * XXX NetBSD7 has different results depending on whether the input + * encoding is emulated (AUDIO_ENCODINGFLAG_EMULATED) or not. It's + * not easy to ensure this situation on all hardware environment. + * On NetBSD9, the result is the same regardless of input encoding. + */ + r = IOCTL(fd, AUDIO_SETINFO, &ai, "blocksize=%d", blocksize); + XP_SYS_EQ(0, r); + r = IOCTL(fd, AUDIO_GETBUFINFO, &ai, ""); + if (ai.blocksize != blocksize) { + /* + * NetBSD9 can not change the blocksize. Then, + * adjust using hiwat/lowat. + */ + blocksize = ai.blocksize; + hiwat = howmany(8000 * 1.5, blocksize); + lowat = howmany(8000 * 0.5, blocksize); + } + /* Anyway, set the parameters */ + AUDIO_INITINFO(&ai); + ai.blocksize = blocksize; + ai.hiwat = hiwat; + ai.lowat = lowat; + ai.play.pause = 1; + r = IOCTL(fd, AUDIO_SETINFO, &ai, "pause=1"); + XP_SYS_EQ(0, r); + + /* Get the set parameters again */ + r = IOCTL(fd, AUDIO_GETBUFINFO, &ai, ""); + XP_SYS_EQ(0, r); + DPRINTF(" > blocksize=%d hiwat=%d lowat=%d buffer_size=%d\n", + ai.blocksize, ai.hiwat, ai.lowat, ai.play.buffer_size); + + /* Write until full */ + buflen = ai.blocksize * ai.hiwat; + buf = (char *)malloc(buflen); + REQUIRED_IF(buf != NULL); + memset(buf, 0xff, buflen); + do { + r = WRITE(fd, buf, buflen); + } while (r == buflen); + if (r == -1) { + XP_SYS_NG(EAGAIN, r); + } + + kq = KQUEUE(); + XP_SYS_OK(kq); + + EV_SET(&kev, fd, EV_ADD, EVFILT_WRITE, 0, 0, 0); + r = KEVENT_SET(kq, &kev, 1); + XP_SYS_EQ(0, r); + + /* Unpause */ + AUDIO_INITINFO(&ai); + ai.play.pause = 0; + r = IOCTL(fd, AUDIO_SETINFO, &ai, "pause=0"); + XP_SYS_EQ(0, r); + + /* Check kevent() up to 2sec */ + ts.tv_sec = 2; + ts.tv_nsec = 0; + r = KEVENT_POLL(kq, &kev, 1, &ts); + if (r >= 1) + DEBUG_KEV("kev", &kev); + if (netbsd < 8) { + /* + * NetBSD7 with EMULATED_FLAG unset has bugs. Unpausing + * unintentionally clears buffer (and therefore it becomes + * writable) but it doesn't raise EVFILT_WRITE. + */ + } else { + XP_SYS_EQ(1, r); + } + + /* Flush it because there is no need to play it */ + r = IOCTL(fd, AUDIO_FLUSH, NULL, ""); + XP_SYS_EQ(0, r); + + r = CLOSE(fd); + XP_SYS_EQ(0, r); + r = CLOSE(kq); + XP_SYS_EQ(0, r); + free(buf); +} + +/* + * kevent(2) must not be affected by other audio descriptors. + */ +DEF(kqueue_simul) +{ + struct audio_info ai; + struct audio_info ai2; + struct kevent kev[2]; + struct timespec ts; + int fd[2]; + int r; + int kq; + u_int blocksize; + int hiwat; + int lowat; + char *buf; + int buflen; + + TEST("kqueue_simul"); + if (netbsd < 8) { + XP_SKIP("Multiple open is not supported"); + return; + } + + memset(&ts, 0, sizeof(ts)); + + /* Make sure that it's not affected by descriptor order */ + for (int i = 0; i < 2; i++) { + int a = i; + int b = 1 - i; + + fd[0] = OPEN(devaudio, O_WRONLY | O_NONBLOCK); + REQUIRED_SYS_OK(fd[0]); + fd[1] = OPEN(devaudio, O_WRONLY | O_NONBLOCK); + REQUIRED_SYS_OK(fd[1]); + + /* + * Adjust block size and hiwat/lowat. + * I want to choice suitable blocksize (if possible). + */ + blocksize = 1000; /* 1/8 sec in mulaw,1ch,8000Hz */ + hiwat = 12; /* 1.5sec */ + lowat = 4; /* 0.5sec */ + AUDIO_INITINFO(&ai); + ai.blocksize = blocksize; + ai.hiwat = hiwat; + ai.lowat = lowat; + r = IOCTL(fd[0], AUDIO_SETINFO, &ai, "blocksize=1000"); + XP_SYS_EQ(0, r); + r = IOCTL(fd[0], AUDIO_GETBUFINFO, &ai, "read back blocksize"); + if (ai.blocksize != blocksize) { + /* + * NetBSD9 can not change the blocksize. Then, + * adjust using hiwat/lowat. + */ + blocksize = ai.blocksize; + hiwat = howmany(8000 * 1.5, blocksize); + lowat = howmany(8000 * 0.5, blocksize); + } + /* Anyway, set the parameters to both */ + AUDIO_INITINFO(&ai); + ai.blocksize = blocksize; + ai.hiwat = hiwat; + ai.lowat = lowat; + ai.play.pause = 1; + r = IOCTL(fd[a], AUDIO_SETINFO, &ai, "pause=1"); + XP_SYS_EQ(0, r); + r = IOCTL(fd[b], AUDIO_SETINFO, &ai, "pause=1"); + XP_SYS_EQ(0, r); + + /* Write both until full */ + buflen = ai.blocksize * ai.hiwat; + buf = (char *)malloc(buflen); + REQUIRED_IF(buf != NULL); + memset(buf, 0xff, buflen); + /* Write fdA */ + do { + r = WRITE(fd[a], buf, buflen); + } while (r == buflen); + if (r == -1) { + XP_SYS_NG(EAGAIN, r); + } + /* Write fdB */ + do { + r = WRITE(fd[b], buf, buflen); + } while (r == buflen); + if (r == -1) { + XP_SYS_NG(EAGAIN, r); + } + + /* Get fdB's initial seek for later */ + r = IOCTL(fd[b], AUDIO_GETBUFINFO, &ai2, ""); + XP_SYS_EQ(0, r); + + kq = KQUEUE(); + XP_SYS_OK(kq); + + /* Both aren't raised at this point */ + EV_SET(&kev[0], fd[a], EV_ADD, EVFILT_WRITE, 0, 0, 0); + EV_SET(&kev[1], fd[b], EV_ADD, EVFILT_WRITE, 0, 0, 0); + r = KEVENT_SET(kq, kev, 2); + XP_SYS_EQ(0, r); + + /* Unpause only fdA */ + AUDIO_INITINFO(&ai); + ai.play.pause = 0; + r = IOCTL(fd[a], AUDIO_SETINFO, &ai, "pause=0"); + XP_SYS_EQ(0, r); + + /* kevent() up to 2sec */ + ts.tv_sec = 2; + ts.tv_nsec = 0; + r = KEVENT_POLL(kq, &kev[0], 1, &ts); + if (r >= 1) + DEBUG_KEV("kev", &kev[0]); + /* fdA should raise */ + XP_SYS_EQ(1, r); + XP_EQ(fd[a], kev[0].ident); + + /* Make sure that fdB keeps whole data */ + r = IOCTL(fd[b], AUDIO_GETBUFINFO, &ai, ""); + XP_EQ(ai2.play.seek, ai.play.seek); + + /* Flush it because there is no need to play it */ + r = IOCTL(fd[0], AUDIO_FLUSH, NULL, ""); + XP_SYS_EQ(0, r); + r = IOCTL(fd[1], AUDIO_FLUSH, NULL, ""); + XP_SYS_EQ(0, r); + + r = CLOSE(fd[0]); + XP_SYS_EQ(0, r); + r = CLOSE(fd[1]); + XP_SYS_EQ(0, r); + r = CLOSE(kq); + XP_SYS_EQ(0, r); + free(buf); + + xxx_close_wait(); + } +} + +/* Shared data between threads for ioctl_while_write */ +struct ioctl_while_write_data { + int fd; + struct timeval start; + int terminated; +}; + +/* Test thread for ioctl_while_write */ +void *thread_ioctl_while_write(void *); +void * +thread_ioctl_while_write(void *arg) +{ + struct ioctl_while_write_data *data = arg; + struct timeval now, res; + struct audio_info ai; + int r; + + /* If 0.5 seconds have elapsed since writing, assume it's blocked */ + do { + usleep(100); + gettimeofday(&now, NULL); + timersub(&now, &data->start, &res); + } while (res.tv_usec < 500000); + + /* Then, do ioctl() */ + r = IOCTL(data->fd, AUDIO_GETBUFINFO, &ai, ""); + XP_SYS_EQ(0, r); + + /* Terminate */ + data->terminated = 1; + + /* Resume write() by unpause */ + AUDIO_INITINFO(&ai); + if (netbsd < 8) { + /* + * XXX NetBSD7 has bugs and it cannot be unpaused. + * However, it also has another bug and it clears buffer + * when encoding is changed. I use it. :-P + */ + ai.play.encoding = AUDIO_ENCODING_SLINEAR_LE; + } + ai.play.pause = 0; + r = IOCTL(data->fd, AUDIO_SETINFO, &ai, "pause=0"); + XP_SYS_EQ(0, r); + + return NULL; +} + +/* + * ioctl(2) can be issued while write(2)-ing. + */ +DEF(ioctl_while_write) +{ + struct audio_info ai; + struct ioctl_while_write_data data0, *data; + char buf[8000]; /* 1sec in mulaw,1ch,8000Hz */ + pthread_t tid; + int r; + + TEST("ioctl_while_write"); + + data = &data0; + memset(data, 0, sizeof(*data)); + memset(buf, 0xff, sizeof(buf)); + + data->fd = OPEN(devaudio, O_WRONLY); + REQUIRED_SYS_OK(data->fd); + + /* Pause to block write(2)ing */ + AUDIO_INITINFO(&ai); + ai.play.pause = 1; + r = IOCTL(data->fd, AUDIO_SETINFO, &ai, "pause=1"); + XP_SYS_EQ(0, r); + + gettimeofday(&data->start, NULL); + + pthread_create(&tid, NULL, thread_ioctl_while_write, data); + + /* Write until blocking */ + for (;;) { + r = WRITE(data->fd, buf, sizeof(buf)); + if (data->terminated) + break; + XP_SYS_EQ(sizeof(buf), r); + + /* Update written time */ + gettimeofday(&data->start, NULL); + } + + pthread_join(tid, NULL); + + /* Flush */ + r = IOCTL(data->fd, AUDIO_FLUSH, NULL, ""); + XP_SYS_EQ(0, r); + r = CLOSE(data->fd); + XP_SYS_EQ(0, r); +} + +volatile int sigio_caught; +void +signal_FIOASYNC(int signo) +{ + if (signo == SIGIO) { + sigio_caught = 1; + DPRINTF(" > %d: pid %d got SIGIO\n", __LINE__, (int)getpid()); + } +} + +/* + * FIOASYNC between two descriptors should be split. + */ +DEF(FIOASYNC_reset) +{ + int fd0, fd1; + int r; + int val; + + TEST("FIOASYNC_reset"); + if (netbsd < 8) { + XP_SKIP("Multiple open is not supported"); + return; + } + + /* The first one opens */ + fd0 = OPEN(devaudio, O_WRONLY); + REQUIRED_SYS_OK(fd0); + + /* The second one opens, enables ASYNC, and closes */ + fd1 = OPEN(devaudio, O_WRONLY); + REQUIRED_SYS_OK(fd1); + val = 1; + r = IOCTL(fd1, FIOASYNC, &val, "on"); + XP_SYS_EQ(0, r); + r = CLOSE(fd1); + XP_SYS_EQ(0, r); + + /* Again, the second one opens and enables ASYNC */ + fd1 = OPEN(devaudio, O_WRONLY); + REQUIRED_SYS_OK(fd1); + val = 1; + r = IOCTL(fd1, FIOASYNC, &val, "on"); + XP_SYS_EQ(0, r); /* NetBSD8 fails */ + r = CLOSE(fd1); + XP_SYS_EQ(0, r); + r = CLOSE(fd0); + XP_SYS_EQ(0, r); +} + +/* + * Whether SIGIO is emitted on plyaback. + * XXX I don't understand conditions that NetBSD7 emits signal. + */ +DEF(FIOASYNC_play_signal) +{ + struct audio_info ai; + int r; + int fd; + int val; + char *data; + int i; + + TEST("FIOASYNC_play_signal"); + if (hw_canplay() == 0) { + XP_SKIP("This test is only for playable device"); + return; + } + + signal(SIGIO, signal_FIOASYNC); + sigio_caught = 0; + + fd = OPEN(devaudio, O_WRONLY); + REQUIRED_SYS_OK(fd); + + r = IOCTL(fd, AUDIO_GETBUFINFO, &ai, ""); + REQUIRED_SYS_EQ(0, r); + REQUIRED_IF(ai.blocksize != 0); + data = (char *)malloc(ai.blocksize); + REQUIRED_IF(data != NULL); + memset(data, 0xff, ai.blocksize); + + val = 1; + r = IOCTL(fd, FIOASYNC, &val, "on"); + XP_SYS_EQ(0, r); + + r = WRITE(fd, data, ai.blocksize); + XP_SYS_EQ(ai.blocksize, r); + + /* Waits signal until 1sec */ + for (i = 0; i < 100 && sigio_caught == 0; i++) { + usleep(10000); + } + signal(SIGIO, SIG_IGN); + XP_EQ(1, sigio_caught); + + r = CLOSE(fd); + XP_SYS_EQ(0, r); + + free(data); + signal(SIGIO, SIG_IGN); + sigio_caught = 0; +} + +/* + * Whether SIGIO is emitted on recording. + */ +DEF(FIOASYNC_rec_signal) +{ + char buf[10]; + int r; + int fd; + int val; + int i; + + TEST("FIOASYNC_rec_signal"); + if (hw_canrec() == 0) { + XP_SKIP("This test is only for recordable device"); + return; + } + + signal(SIGIO, signal_FIOASYNC); + sigio_caught = 0; + + fd = OPEN(devaudio, O_RDONLY); + REQUIRED_SYS_OK(fd); + + val = 1; + r = IOCTL(fd, FIOASYNC, &val, "on"); + XP_SYS_EQ(0, r); + + r = READ(fd, buf, sizeof(buf)); + XP_SYS_EQ(sizeof(buf), r); + + /* Wait signal until 1sec */ + for (i = 0; i < 100 && sigio_caught == 0; i++) { + usleep(10000); + } + signal(SIGIO, SIG_IGN); + XP_EQ(1, sigio_caught); + + r = CLOSE(fd); + XP_SYS_EQ(0, r); + + signal(SIGIO, SIG_IGN); + sigio_caught = 0; +} + +/* + * FIOASYNC doesn't affect other descriptor. + * For simplify, test only for playback... + */ +DEF(FIOASYNC_multi) +{ + struct audio_info ai; + char *buf; + char pipebuf[1]; + int r; + int i; + int fd1; + int fd2; + int pd[2]; + int val; + pid_t pid; + int status; + + TEST("FIOASYNC_multi"); + if (netbsd < 8) { + XP_SKIP("Multiple open is not supported"); + return; + } + if (hw_canplay() == 0) { + XP_SKIP("This test is only for playable device"); + return; + } + + /* Pipe used between parent and child */ + r = pipe(pd); + REQUIRED_SYS_EQ(0, r); + + fd1 = OPEN(devaudio, O_WRONLY); + REQUIRED_SYS_OK(fd1); + fd2 = OPEN(devaudio, O_WRONLY); + REQUIRED_SYS_OK(fd2); + + /* Pause fd2 */ + AUDIO_INITINFO(&ai); + ai.play.pause = 1; + r = IOCTL(fd2, AUDIO_SETINFO, &ai, "pause"); + REQUIRED_SYS_EQ(0, r); + + /* Fill both */ + r = IOCTL(fd1, AUDIO_GETBUFINFO, &ai, ""); + REQUIRED_SYS_EQ(0, r); + REQUIRED_IF(ai.blocksize != 0); + buf = (char *)malloc(ai.blocksize); + REQUIRED_IF(buf != NULL); + memset(buf, 0xff, ai.blocksize); + r = WRITE(fd1, buf, ai.blocksize); + XP_SYS_EQ(ai.blocksize, r); + + sigio_caught = 0; + val = 1; + + fflush(stdout); + fflush(stderr); + pid = fork(); + if (pid == -1) { + REQUIRED_SYS_OK(pid); + } + if (pid == 0) { + /* Child */ + close(fd1); + + /* Child enables ASYNC on fd2 */ + signal(SIGIO, signal_FIOASYNC); + r = IOCTL(fd2, FIOASYNC, &val, "on"); + /* It cannot count errors because here is a child process */ + /* XP_SYS_EQ(0, r); */ + + /* + * Waits signal until 1sec. + * But fd2 is paused so it should never raise. + */ + for (i = 0; i < 100 && sigio_caught == 0; i++) { + usleep(10000); + } + signal(SIGIO, SIG_IGN); + pipebuf[0] = sigio_caught; + /* This is not WRITE() macro here */ + write(pd[1], pipebuf, sizeof(pipebuf)); + + /* XXX? */ + close(fd2); + sleep(1); + exit(0); + } else { + /* Parent */ + DPRINTF(" > fork() = %d\n", (int)pid); + + /* Parent enables ASYNC on fd1 */ + signal(SIGIO, signal_FIOASYNC); + r = IOCTL(fd1, FIOASYNC, &val, "on"); + XP_SYS_EQ(0, r); + + /* Waits signal until 1sec */ + for (i = 0; i < 100 && sigio_caught == 0; i++) { + usleep(10000); + } + signal(SIGIO, SIG_IGN); + XP_EQ(1, sigio_caught); + + /* Then read child's result from pipe */ + r = read(pd[0], pipebuf, sizeof(pipebuf)); + if (r != 1) { + XP_FAIL("reading from child failed"); + } + DPRINTF(" > child's sigio_cauht = %d\n", pipebuf[0]); + XP_EQ(0, pipebuf[0]); + + waitpid(pid, &status, 0); + } + + r = CLOSE(fd1); + XP_SYS_EQ(0, r); + r = CLOSE(fd2); + XP_SYS_EQ(0, r); + + signal(SIGIO, SIG_IGN); + sigio_caught = 0; + free(buf); +} + +/* + * Check AUDIO_WSEEK behavior. + */ +DEF(AUDIO_WSEEK) +{ + char buf[4]; + struct audio_info ai; + int r; + int fd; + u_long n; + + TEST("AUDIO_WSEEK"); + + fd = OPEN(devaudio, O_WRONLY); + REQUIRED_SYS_OK(fd); + + /* Pause to count sample data */ + AUDIO_INITINFO(&ai); + ai.play.pause = 1; + r = IOCTL(fd, AUDIO_SETINFO, &ai, "pause=1"); + REQUIRED_SYS_EQ(0, r); + + /* On the initial state, it should be 0 bytes */ + n = 0; + r = IOCTL(fd, AUDIO_WSEEK, &n, ""); + XP_SYS_EQ(0, r); + XP_EQ(0, n); + + /* When writing 4 bytes, it should be 4 bytes */ + memset(buf, 0xff, sizeof(buf)); + r = WRITE(fd, buf, sizeof(buf)); + REQUIRED_EQ(sizeof(buf), r); + r = IOCTL(fd, AUDIO_WSEEK, &n, ""); + XP_SYS_EQ(0, r); + if (netbsd < 9) { + /* + * On NetBSD7, it will return 0. + * Perhaps, WSEEK returns the number of pustream bytes but + * data has already advanced... + */ + XP_EQ(0, n); + } else { + /* Data less than one block remains here */ + XP_EQ(4, n); + } + + r = CLOSE(fd); + XP_SYS_EQ(0, r); +} + +/* + * Check AUDIO_SETFD behavior for O_*ONLY descriptor. + * On NetBSD7, SETFD modify audio layer's state (and MD driver's state) + * regardless of open mode. GETFD obtains audio layer's duplex. + * On NetBSD9, SETFD is obsoleted. GETFD obtains hardware's duplex. + */ +void +test_AUDIO_SETFD_xxONLY(int openmode) +{ + struct audio_info ai; + int r; + int fd; + int n; + + TEST("AUDIO_SETFD_%s", openmode_str[openmode] + 2); + if (openmode == O_RDONLY && hw_canrec() == 0) { + XP_SKIP("This test is for recordable device"); + return; + } + if (openmode == O_WRONLY && hw_canplay() == 0) { + XP_SKIP("This test is for playable device"); + return; + } + + fd = OPEN(devaudio, openmode); + REQUIRED_SYS_OK(fd); + + /* + * Just after open(2), + * - On NetBSD7, it's always half-duplex. + * - On NetBSD9, it's the same as hardware one regardless of openmode. + */ + n = 0; + r = IOCTL(fd, AUDIO_GETFD, &n, ""); + XP_SYS_EQ(0, r); + if (netbsd < 9) { + XP_EQ(0, n); + } else { + XP_EQ(hw_fulldup(), n); + } + + /* + * When trying to set to full-duplex, + * - On NetBSD7, it will succeed if the hardware is full-duplex, or + * will fail if the hardware is half-duplex. + * - On NetBSD9, it will always succeed but will not be modified. + */ + n = 1; + r = IOCTL(fd, AUDIO_SETFD, &n, "on"); + if (netbsd < 8) { + if (hw_fulldup()) { + XP_SYS_EQ(0, r); + } else { + XP_SYS_NG(ENOTTY, r); + } + } else if (netbsd == 8) { + XP_FAIL("expected result is unknown"); + } else { + XP_SYS_EQ(0, r); + } + + /* + * When obtain it, + * - On NetBSD7, it will be 1 if the hardware is full-duplex or + * 0 if half-duplex. + * - On NetBSD9, it will never be changed because it's the hardware + * property. + */ + n = 0; + r = IOCTL(fd, AUDIO_GETFD, &n, ""); + XP_SYS_EQ(0, r); + if (netbsd < 8) { + XP_EQ(hw_fulldup(), n); + } else if (netbsd == 8) { + XP_FAIL("expected result is unknown"); + } else { + XP_EQ(hw_fulldup(), n); + } + + /* Some track parameters like ai.*.open should not change */ + r = IOCTL(fd, AUDIO_GETBUFINFO, &ai, ""); + XP_SYS_EQ(0, r); + XP_EQ(mode2play(openmode), ai.play.open); + XP_EQ(mode2rec(openmode), ai.record.open); + + /* + * When trying to set to half-duplex, + * - On NetBSD7, it will succeed if the hardware is full-duplex, or + * it will succeed with nothing happens. + * - On NetBSD9, it will always succeed but nothing happens. + */ + n = 0; + r = IOCTL(fd, AUDIO_SETFD, &n, "off"); + XP_SYS_EQ(0, r); + + /* + * When obtain it again, + * - On NetBSD7, it will be 0 if the hardware is full-duplex, or + * still 0 if half-duplex. + * - On NetBSD9, it should not change. + */ + n = 0; + r = IOCTL(fd, AUDIO_GETFD, &n, ""); + XP_SYS_EQ(0, r); + if (netbsd < 9) { + XP_EQ(0, n); + } else { + XP_EQ(hw_fulldup(), n); + } + + /* Some track parameters like ai.*.open should not change */ + r = IOCTL(fd, AUDIO_GETBUFINFO, &ai, ""); + XP_SYS_EQ(0, r); + XP_EQ(mode2play(openmode), ai.play.open); + XP_EQ(mode2rec(openmode), ai.record.open); + + r = CLOSE(fd); + XP_SYS_EQ(0, r); +} +DEF(AUDIO_SETFD_RDONLY) { test_AUDIO_SETFD_xxONLY(O_RDONLY); } +DEF(AUDIO_SETFD_WRONLY) { test_AUDIO_SETFD_xxONLY(O_WRONLY); } + +/* + * Check AUDIO_SETFD behavior for O_RDWR descriptor. + */ +DEF(AUDIO_SETFD_RDWR) +{ + struct audio_info ai; + int r; + int fd; + int n; + + TEST("AUDIO_SETFD_RDWR"); + if (!hw_fulldup()) { + XP_SKIP("This test is only for full-duplex device"); + return; + } + + fd = OPEN(devaudio, O_RDWR); + REQUIRED_SYS_OK(fd); + + /* + * - audio(4) manpage until NetBSD7 said "If a full-duplex capable + * audio device is opened for both reading and writing it will + * start in half-duplex play mode", but implementation doesn't + * seem to follow it. It returns full-duplex. + * - On NetBSD9, it should return full-duplex on full-duplex, or + * half-duplex on half-duplex. + */ + n = 0; + r = IOCTL(fd, AUDIO_GETFD, &n, ""); + XP_SYS_EQ(0, r); + XP_EQ(hw_fulldup(), n); + + /* + * When trying to set to full-duplex, + * - On NetBSD7, it will succeed with nothing happens if full-duplex, + * or will fail if half-duplex. + * - On NetBSD9, it will always succeed with nothing happens. + */ + n = 1; + r = IOCTL(fd, AUDIO_SETFD, &n, "on"); + if (netbsd < 9) { + if (hw_fulldup()) { + XP_SYS_EQ(0, r); + } else { + XP_SYS_NG(ENOTTY, r); + } + } else { + XP_SYS_EQ(0, r); + } + + /* When obtains it, it retuns half/full-duplex as is */ + n = 0; + r = IOCTL(fd, AUDIO_GETFD, &n, ""); + XP_SYS_EQ(0, r); + XP_EQ(hw_fulldup(), n); + + /* Some track parameters like ai.*.open should not change */ + r = IOCTL(fd, AUDIO_GETBUFINFO, &ai, ""); + XP_SYS_EQ(0, r); + XP_EQ(1, ai.play.open); + XP_EQ(mode2rec(O_RDWR), ai.record.open); + + /* + * When trying to set to half-duplex, + * - On NetBSD7, it will succeed if the hardware is full-duplex, or + * it will succeed with nothing happens. + * - On NetBSD9, it will always succeed but nothing happens. + */ + n = 0; + r = IOCTL(fd, AUDIO_SETFD, &n, "off"); + if (netbsd < 8) { + XP_SYS_EQ(0, r); + } else if (netbsd == 8) { + XP_FAIL("expected result is unknown"); + } else { + XP_SYS_EQ(0, r); + } + + /* + * When obtain it again, + * - On NetBSD7, it will be 0 if the hardware is full-duplex, or + * still 0 if half-duplex. + * - On NetBSD9, it should be 1 if the hardware is full-duplex, or + * 0 if half-duplex. + */ + n = 0; + r = IOCTL(fd, AUDIO_GETFD, &n, ""); + XP_SYS_EQ(0, r); + if (netbsd < 8) { + XP_EQ(0, n); + } else if (netbsd == 8) { + XP_FAIL("expected result is unknown"); + } else { + XP_EQ(hw_fulldup(), n); + } + + /* Some track parameters like ai.*.open should not change */ + r = IOCTL(fd, AUDIO_GETBUFINFO, &ai, ""); + XP_SYS_EQ(0, r); + XP_EQ(1, ai.play.open); + XP_EQ(mode2rec(O_RDWR), ai.record.open); + + r = CLOSE(fd); + XP_SYS_EQ(0, r); +} + +/* + * Check AUDIO_GETINFO.eof behavior. + */ +DEF(AUDIO_GETINFO_eof) +{ + struct audio_info ai; + char buf[4]; + int r; + int fd, fd1; + + TEST("AUDIO_GETINFO_eof"); + if (hw_canplay() == 0) { + XP_SKIP("This test is for playable device"); + return; + } + + fd = OPEN(devaudio, O_RDWR); + REQUIRED_SYS_OK(fd); + + /* Pause to make no sound */ + AUDIO_INITINFO(&ai); + ai.play.pause = 1; + r = IOCTL(fd, AUDIO_SETINFO, &ai, "pause"); + REQUIRED_SYS_EQ(0, r); + + /* It should be 0 initially */ + r = IOCTL(fd, AUDIO_GETBUFINFO, &ai, ""); + XP_SYS_EQ(0, r); + XP_EQ(0, ai.play.eof); + XP_EQ(0, ai.record.eof); + + /* Writing zero bytes should increment it */ + r = WRITE(fd, &r, 0); + REQUIRED_SYS_OK(r); + r = IOCTL(fd, AUDIO_GETBUFINFO, &ai, ""); + XP_SYS_EQ(0, r); + XP_EQ(1, ai.play.eof); + XP_EQ(0, ai.record.eof); + + /* Writing one ore more bytes should noto increment it */ + memset(buf, 0xff, sizeof(buf)); + r = WRITE(fd, buf, sizeof(buf)); + REQUIRED_SYS_OK(r); + memset(&ai, 0, sizeof(ai)); + r = IOCTL(fd, AUDIO_GETBUFINFO, &ai, ""); + XP_SYS_EQ(0, r); + XP_EQ(1, ai.play.eof); + XP_EQ(0, ai.record.eof); + + /* Writing zero bytes again should increment it */ + r = WRITE(fd, buf, 0); + REQUIRED_SYS_OK(r); + memset(&ai, 0, sizeof(ai)); + r = IOCTL(fd, AUDIO_GETBUFINFO, &ai, ""); + XP_SYS_EQ(0, r); + XP_EQ(2, ai.play.eof); + XP_EQ(0, ai.record.eof); + + /* Reading zero bytes should not increment it */ + if (hw_fulldup()) { + r = READ(fd, buf, 0); + REQUIRED_SYS_OK(r); + memset(&ai, 0, sizeof(ai)); + r = IOCTL(fd, AUDIO_GETBUFINFO, &ai, ""); + XP_SYS_EQ(0, r); + XP_EQ(2, ai.play.eof); + XP_EQ(0, ai.record.eof); + } + + /* should not interfere with other descriptor */ + if (netbsd >= 8) { + fd1 = OPEN(devaudio, O_RDWR); + REQUIRED_SYS_OK(fd1); + memset(&ai, 0, sizeof(ai)); + r = IOCTL(fd1, AUDIO_GETBUFINFO, &ai, ""); + XP_SYS_EQ(0, r); + XP_EQ(0, ai.play.eof); + XP_EQ(0, ai.record.eof); + r = CLOSE(fd1); + XP_SYS_EQ(0, r); + } + + r = CLOSE(fd); + XP_SYS_EQ(0, r); + + xxx_close_wait(); + + /* When reopen, it should reset the counter */ + fd = OPEN(devaudio, O_RDWR); + REQUIRED_SYS_OK(fd); + + r = IOCTL(fd, AUDIO_GETBUFINFO, &ai, ""); + XP_SYS_EQ(0, r); + XP_EQ(0, ai.play.eof); + XP_EQ(0, ai.record.eof); + + r = CLOSE(fd); + XP_SYS_EQ(0, r); +} + +/* + * Check relationship between openmode and mode set by AUDIO_SETINFO. + */ +void +test_AUDIO_SETINFO_mode(int openmode, int index, int setmode, int expected) +{ + struct audio_info ai; + char buf[10]; + int inimode; + int r; + int fd; + bool canwrite; + bool canread; + + /* index was passed only for displaying here */ + TEST("AUDIO_SETINFO_mode_%s_%d", openmode_str[openmode] + 2, index); + if (mode2aumode(openmode) == 0) { + XP_SKIP("Operation not allowed on this hardware property"); + return; + } + + inimode = mode2aumode(openmode); + + fd = OPEN(devaudio, openmode); + REQUIRED_SYS_OK(fd); + + /* When just after opening */ + memset(&ai, 0, sizeof(ai)); + r = IOCTL(fd, AUDIO_GETINFO, &ai, ""); + REQUIRED_SYS_EQ(0, r); + XP_EQ(inimode, ai.mode); + XP_EQ(mode2play(openmode), ai.play.open); + XP_EQ(mode2rec(openmode), ai.record.open); + XP_NE(0, ai.play.buffer_size); + XP_NE(0, ai.record.buffer_size); + + /* Change mode (and pause here) */ + ai.mode = setmode; + ai.play.pause = 1; + ai.record.pause = 1; + r = IOCTL(fd, AUDIO_SETINFO, &ai, "mode"); + XP_SYS_EQ(0, r); + if (r == 0) { + r = IOCTL(fd, AUDIO_GETINFO, &ai, ""); + XP_SYS_EQ(0, r); + XP_EQ(expected, ai.mode); + + /* It seems to keep the initial openmode regardless of mode */ + XP_EQ(mode2play(openmode), ai.play.open); + XP_EQ(mode2rec(openmode), ai.record.open); + XP_NE(0, ai.play.buffer_size); + XP_NE(0, ai.record.buffer_size); + } + + /* + * On NetBSD7, whether writable depends openmode when open. + * On NetBSD9, whether writable should depend inimode when open. + * Modifying after open should not affect this mode. + */ + if (netbsd < 9) { + canwrite = (openmode != O_RDONLY); + } else { + canwrite = ((inimode & AUMODE_PLAY) != 0); + } + r = WRITE(fd, buf, 0); + if (canwrite) { + XP_SYS_EQ(0, r); + } else { + XP_SYS_NG(EBADF, r); + } + + /* + * On NetBSD7, whether readable depends openmode when open. + * On NetBSD9, whether readable should depend inimode when open. + * Modifying after open should not affect this mode. + */ + if (netbsd < 9) { + canread = (openmode != O_WRONLY); + } else { + canread = ((inimode & AUMODE_RECORD) != 0); + } + r = READ(fd, buf, 0); + if (canread) { + XP_SYS_EQ(0, r); + } else { + XP_SYS_NG(EBADF, r); + } + + r = CLOSE(fd); + XP_SYS_EQ(0, r); +} +/* + * XXX hmm... it's too complex + */ +/* shortcut for table form */ +#define P AUMODE_PLAY +#define A AUMODE_PLAY_ALL +#define R AUMODE_RECORD +struct setinfo_mode_t { + int setmode; /* mode used in SETINFO */ + int expmode7; /* expected mode on NetBSD7 */ + int expmode9; /* expected mode on NetBSD9 */ +}; +/* + * The following tables show this operation on NetBSD7 is almost 'undefined'. + * In contrast, NetBSD9 never changes mode by AUDIO_SETINFO except + * AUMODE_PLAY_ALL. + * + * setmode == 0 and 8 are out of range and invalid input samples. + * But NetBSD7 seems to accept it as is. + */ +struct setinfo_mode_t table_SETINFO_mode_O_RDONLY[] = { + /* setmode expmode7 expmode9 */ + { 0, 0, R }, + { P, P, R }, + { A , A|P, R }, + { A|P, A|P, R }, + { R , R , R }, + { R| P, P, R }, + { R|A , A|P, R }, + { R|A|P, A|P, R }, + { 8, 8, R }, +}; +struct setinfo_mode_t table_SETINFO_mode_O_WRONLY[] = { + /* setmode expmode7 expmode9 */ + { 0, 0, P }, + { P, P, P }, + { A , A|P, A|P }, + { A|P, A|P, A|P }, + { R , R , P }, + { R| P, P, P }, + { R|A , A|P, A|P }, + { R|A|P, A|P, A|P }, + { 8, 8, P }, +}; +#define f(openmode, index) do { \ + struct setinfo_mode_t *table = table_SETINFO_mode_##openmode; \ + int setmode = table[index].setmode; \ + int expected = (netbsd < 9) \ + ? table[index].expmode7 \ + : table[index].expmode9; \ + test_AUDIO_SETINFO_mode(openmode, index, setmode, expected); \ +} while (0) +DEF(AUDIO_SETINFO_mode_RDONLY_0) { f(O_RDONLY, 0); } +DEF(AUDIO_SETINFO_mode_RDONLY_1) { f(O_RDONLY, 1); } +DEF(AUDIO_SETINFO_mode_RDONLY_2) { f(O_RDONLY, 2); } +DEF(AUDIO_SETINFO_mode_RDONLY_3) { f(O_RDONLY, 3); } +DEF(AUDIO_SETINFO_mode_RDONLY_4) { f(O_RDONLY, 4); } +DEF(AUDIO_SETINFO_mode_RDONLY_5) { f(O_RDONLY, 5); } +DEF(AUDIO_SETINFO_mode_RDONLY_6) { f(O_RDONLY, 6); } +DEF(AUDIO_SETINFO_mode_RDONLY_7) { f(O_RDONLY, 7); } +DEF(AUDIO_SETINFO_mode_RDONLY_8) { f(O_RDONLY, 8); } +DEF(AUDIO_SETINFO_mode_WRONLY_0) { f(O_WRONLY, 0); } +DEF(AUDIO_SETINFO_mode_WRONLY_1) { f(O_WRONLY, 1); } +DEF(AUDIO_SETINFO_mode_WRONLY_2) { f(O_WRONLY, 2); } +DEF(AUDIO_SETINFO_mode_WRONLY_3) { f(O_WRONLY, 3); } +DEF(AUDIO_SETINFO_mode_WRONLY_4) { f(O_WRONLY, 4); } +DEF(AUDIO_SETINFO_mode_WRONLY_5) { f(O_WRONLY, 5); } +DEF(AUDIO_SETINFO_mode_WRONLY_6) { f(O_WRONLY, 6); } +DEF(AUDIO_SETINFO_mode_WRONLY_7) { f(O_WRONLY, 7); } +DEF(AUDIO_SETINFO_mode_WRONLY_8) { f(O_WRONLY, 8); } +#undef f +/* + * The following tables also show that NetBSD7's behavior is almost + * 'undefined'. + */ +struct setinfo_mode_t table_SETINFO_mode_O_RDWR_full[] = { + /* setmode expmode7 expmode9 */ + { 0, 0, R| P }, + { P, P, R| P }, + { A , A|P, R|A|P }, + { A|P, A|P, R|A|P }, + { R , R , R| P }, + { R| P, R| P, R| P }, + { R|A , R|A|P, R|A|P }, + { R|A|P, R|A|P, R|A|P }, + { 8, 8, R| P }, +}; +struct setinfo_mode_t table_SETINFO_mode_O_RDWR_half[] = { + /* setmode expmode7 expmode9 */ + { 0, 0, P }, + { P, P, P }, + { A , A|P, A|P }, + { A|P, A|P, A|P }, + { R , R , P }, + { R| P, P, P }, + { R|A , A|P, A|P }, + { R|A|P, A|P, A|P }, + { 8, 8, P }, +}; +#define f(index) do { \ + struct setinfo_mode_t *table = (hw_fulldup()) \ + ? table_SETINFO_mode_O_RDWR_full \ + : table_SETINFO_mode_O_RDWR_half; \ + int setmode = table[index].setmode; \ + int expected = (netbsd < 9) \ + ? table[index].expmode7 \ + : table[index].expmode9; \ + test_AUDIO_SETINFO_mode(O_RDWR, index, setmode, expected); \ +} while (0) +DEF(AUDIO_SETINFO_mode_RDWR_0) { f(0); } +DEF(AUDIO_SETINFO_mode_RDWR_1) { f(1); } +DEF(AUDIO_SETINFO_mode_RDWR_2) { f(2); } +DEF(AUDIO_SETINFO_mode_RDWR_3) { f(3); } +DEF(AUDIO_SETINFO_mode_RDWR_4) { f(4); } +DEF(AUDIO_SETINFO_mode_RDWR_5) { f(5); } +DEF(AUDIO_SETINFO_mode_RDWR_6) { f(6); } +DEF(AUDIO_SETINFO_mode_RDWR_7) { f(7); } +DEF(AUDIO_SETINFO_mode_RDWR_8) { f(8); } +#undef f +#undef P +#undef A +#undef R + +/* + * Check whether encoding params can be set. + */ +void +test_AUDIO_SETINFO_params_set(int openmode, int aimode, int pause) +{ + struct audio_info ai; + int r; + int fd; + + /* + * aimode is bool value that indicates whether to change ai.mode. + * pause is bool value that indicates whether to change ai.*.pause. + */ + + TEST("AUDIO_SETINFO_params_%s_%d_%d", + openmode_str[openmode] + 2, aimode, pause); + if (mode2aumode(openmode) == 0) { + XP_SKIP("Operation not allowed on this hardware property"); + return; + } + + /* On half-duplex, O_RDWR is the same as O_WRONLY, so skip it */ + if (!hw_fulldup() && openmode == O_RDWR) { + XP_SKIP("This is the same with O_WRONLY on half-duplex"); + return; + } + + fd = OPEN(devaudio, openmode); + REQUIRED_SYS_OK(fd); + + AUDIO_INITINFO(&ai); + /* + * It takes time and effort to check all parameters independently, + * so that use sample_rate as a representative. + */ + ai.play.sample_rate = 11025; + ai.record.sample_rate = 11025; + if (aimode) + ai.mode = mode2aumode(openmode) & ~AUMODE_PLAY_ALL; + if (pause) { + ai.play.pause = 1; + ai.record.pause = 1; + } + + r = IOCTL(fd, AUDIO_SETINFO, &ai, ""); + XP_SYS_EQ(0, r); + + r = IOCTL(fd, AUDIO_GETBUFINFO, &ai, ""); + XP_SYS_EQ(0, r); + int expmode = (aimode) + ? (mode2aumode(openmode) & ~AUMODE_PLAY_ALL) + : mode2aumode(openmode); + XP_EQ(expmode, ai.mode); + XP_EQ(11025, ai.play.sample_rate); + XP_EQ(pause, ai.play.pause); + XP_EQ(11025, ai.record.sample_rate); + XP_EQ(pause, ai.record.pause); + + r = CLOSE(fd); + XP_SYS_EQ(0, r); +} +#define f(a,b,c) test_AUDIO_SETINFO_params_set(a, b, c) +DEF(AUDIO_SETINFO_params_set_RDONLY_0) { f(O_RDONLY, 0, 0); } +DEF(AUDIO_SETINFO_params_set_RDONLY_1) { f(O_RDONLY, 0, 1); } +/* On RDONLY, ai.mode is not changable + * AUDIO_SETINFO_params_set_RDONLY_2) { f(O_RDONLY, 1, 0); } + * AUDIO_SETINFO_params_set_RDONLY_3) { f(O_RDONLY, 1, 1); } + */ +DEF(AUDIO_SETINFO_params_set_WRONLY_0) { f(O_WRONLY, 0, 0); } +DEF(AUDIO_SETINFO_params_set_WRONLY_1) { f(O_WRONLY, 0, 1); } +DEF(AUDIO_SETINFO_params_set_WRONLY_2) { f(O_WRONLY, 1, 0); } +DEF(AUDIO_SETINFO_params_set_WRONLY_3) { f(O_WRONLY, 1, 1); } +DEF(AUDIO_SETINFO_params_set_RDWR_0) { f(O_RDWR, 0, 0); } +DEF(AUDIO_SETINFO_params_set_RDWR_1) { f(O_RDWR, 0, 1); } +DEF(AUDIO_SETINFO_params_set_RDWR_2) { f(O_RDWR, 1, 0); } +DEF(AUDIO_SETINFO_params_set_RDWR_3) { f(O_RDWR, 1, 1); } +#undef f + +/* + * AUDIO_SETINFO for existing track should not be interfered by other + * descriptor. + * AUDIO_SETINFO for non-existing track affects/is affected sticky parameters + * for backward compatibility. + */ +DEF(AUDIO_SETINFO_params_simul) +{ + struct audio_info ai; + int fd0; + int fd1; + int r; + + TEST("AUDIO_SETINFO_params_simul"); + if (netbsd < 8) { + XP_SKIP("Multiple open is not supported"); + return; + } + if (hw_canplay() == 0) { + XP_SKIP("This test is for playable device"); + return; + } + + /* Open the 1st one as playback only */ + fd0 = OPEN(devaudio, O_WRONLY); + REQUIRED_SYS_OK(fd0); + + /* Open the 2nd one as both of playback and recording */ + fd1 = OPEN(devaudio, O_RDWR); + REQUIRED_SYS_OK(fd1); + + /* Change some parameters of both track on the 2nd one */ + AUDIO_INITINFO(&ai); + ai.play.sample_rate = 11025; + ai.record.sample_rate = 11025; + r = IOCTL(fd1, AUDIO_SETINFO, &ai, ""); + XP_SYS_EQ(0, r); + + /* + * The 1st one doesn't have recording track so that only recording + * parameter is affected by sticky parameter. + */ + memset(&ai, 0, sizeof(ai)); + r = IOCTL(fd0, AUDIO_GETBUFINFO, &ai, ""); + XP_SYS_EQ(0, r); + XP_EQ(8000, ai.play.sample_rate); + XP_EQ(11025, ai.record.sample_rate); + + /* Next, change some parameters of both track on the 1st one */ + AUDIO_INITINFO(&ai); + ai.play.sample_rate = 16000; + ai.record.sample_rate = 16000; + r = IOCTL(fd0, AUDIO_SETINFO, &ai, ""); + XP_SYS_EQ(0, r); + + /* + * On full-duplex device, the 2nd one has both track so that + * both track are not affected by sticky parameter. + * Otherwise, the 2nd one has only playback track so that + * playback track is not affected by sticky parameter. + */ + memset(&ai, 0, sizeof(ai)); + r = IOCTL(fd1, AUDIO_GETBUFINFO, &ai, ""); + XP_SYS_EQ(0, r); + XP_EQ(11025, ai.play.sample_rate); + if (hw_fulldup()) { + XP_EQ(11025, ai.record.sample_rate); + } else { + XP_EQ(16000, ai.record.sample_rate); + } + + r = CLOSE(fd0); + XP_SYS_EQ(0, r); + r = CLOSE(fd1); + XP_SYS_EQ(0, r); +} + +/* + * AUDIO_SETINFO(encoding/precision) is tested in AUDIO_GETENC_range below. + */ + +/* + * Check whether the number of channels can be set. + */ +DEF(AUDIO_SETINFO_channels) +{ + struct audio_info hwinfo; + struct audio_info ai; + int mode; + int r; + int fd; + int i; + unsigned int ch; + struct { + int ch; + bool expected; + } table[] = { + { 0, false }, + { 1, true }, /* monaural */ + { 2, true }, /* stereo */ + }; + + TEST("AUDIO_SETINFO_channels"); + if (netbsd < 8) { + /* + * On NetBSD7, the result depends the hardware and there is + * no way to know it. + */ + XP_SKIP("The test doesn't make sense on NetBSD7"); + return; + } + + mode = openable_mode(); + fd = OPEN(devaudio, mode); + REQUIRED_SYS_OK(fd); + + /* + * The audio layer always supports monaural and stereo regardless of + * the hardware capability. + */ + for (i = 0; i < (int)__arraycount(table); i++) { + ch = table[i].ch; + bool expected = table[i].expected; + + AUDIO_INITINFO(&ai); + if (mode != O_RDONLY) + ai.play.channels = ch; + if (mode != O_WRONLY) + ai.record.channels = ch; + r = IOCTL(fd, AUDIO_SETINFO, &ai, "channels=%d", ch); + if (expected) { + /* Expects to succeed */ + XP_SYS_EQ(0, r); + + r = IOCTL(fd, AUDIO_GETBUFINFO, &ai, ""); + XP_SYS_EQ(0, r); + if (mode != O_RDONLY) + XP_EQ(ch, ai.play.channels); + if (mode != O_WRONLY) + XP_EQ(ch, ai.record.channels); + } else { + /* Expects to fail */ + XP_SYS_NG(EINVAL, r); + } + } + + /* + * The maximum number of supported channels depends the hardware. + */ + /* Get the number of channels that the hardware supports */ + r = IOCTL(fd, AUDIO_GETFORMAT, &hwinfo, ""); + REQUIRED_SYS_EQ(0, r); + + if ((hwinfo.mode & AUMODE_PLAY)) { + DPRINTF(" > hwinfo.play.channels = %d\n", + hwinfo.play.channels); + for (ch = 3; ch <= hwinfo.play.channels; ch++) { + AUDIO_INITINFO(&ai); + ai.play.channels = ch; + r = IOCTL(fd, AUDIO_SETINFO, &ai, "channels=%d", ch); + XP_SYS_EQ(0, r); + + r = IOCTL(fd, AUDIO_GETBUFINFO, &ai, ""); + XP_SYS_EQ(0, r); + XP_EQ(ch, ai.play.channels); + } + + AUDIO_INITINFO(&ai); + ai.play.channels = ch; + r = IOCTL(fd, AUDIO_SETINFO, &ai, "channels=%d", ch); + XP_SYS_NG(EINVAL, r); + } + if ((hwinfo.mode & AUMODE_RECORD)) { + DPRINTF(" > hwinfo.record.channels = %d\n", + hwinfo.record.channels); + for (ch = 3; ch <= hwinfo.record.channels; ch++) { + AUDIO_INITINFO(&ai); + ai.record.channels = ch; + r = IOCTL(fd, AUDIO_SETINFO, &ai, "channels=%d", ch); + XP_SYS_EQ(0, r); + + r = IOCTL(fd, AUDIO_GETBUFINFO, &ai, ""); + XP_SYS_EQ(0, r); + XP_EQ(ch, ai.record.channels); + } + + AUDIO_INITINFO(&ai); + ai.record.channels = ch; + r = IOCTL(fd, AUDIO_SETINFO, &ai, "channels=%d", ch); + XP_SYS_NG(EINVAL, r); + } + + r = CLOSE(fd); + XP_SYS_EQ(0, r); +} + +/* + * Check whether the sample rate can be set. + */ +DEF(AUDIO_SETINFO_sample_rate) +{ + struct audio_info ai; + int mode; + int r; + int fd; + int i; + struct { + int freq; + bool expected; + } table[] = { + { 999, false }, + { 1000, true }, /* lower limit */ + { 48000, true }, + { 192000, true }, /* upper limit */ + { 192001, false }, + }; + + TEST("AUDIO_SETINFO_sample_rate"); + if (netbsd < 8) { + /* + * On NetBSD7, the result depends the hardware and there is + * no way to know it. + */ + XP_SKIP("The test doesn't make sense on NetBSD7"); + return; + } + + mode = openable_mode(); + fd = OPEN(devaudio, mode); + REQUIRED_SYS_OK(fd); + + for (i = 0; i < (int)__arraycount(table); i++) { + int freq = table[i].freq; + bool expected = table[i].expected; + + AUDIO_INITINFO(&ai); + if (mode != O_RDONLY) + ai.play.sample_rate = freq; + if (mode != O_WRONLY) + ai.record.sample_rate = freq; + r = IOCTL(fd, AUDIO_SETINFO, &ai, "sample_rate=%d", freq); + if (expected) { + /* Expects to succeed */ + XP_SYS_EQ(0, r); + + r = IOCTL(fd, AUDIO_GETBUFINFO, &ai, ""); + XP_SYS_EQ(0, r); + if (mode != O_RDONLY) + XP_EQ(freq, ai.play.sample_rate); + if (mode != O_WRONLY) + XP_EQ(freq, ai.record.sample_rate); + } else { + /* Expects to fail */ + XP_SYS_NG(EINVAL, r); + } + } + + r = CLOSE(fd); + XP_SYS_EQ(0, r); +} + +/* + * SETINFO(sample_rate = 0) should fail correctly. + */ +DEF(AUDIO_SETINFO_sample_rate_0) +{ + struct audio_info ai; + int mode; + int r; + int fd; + + TEST("AUDIO_SETINFO_sample_rate_0"); + if (netbsd < 9) { + /* + * On NetBSD7,8 this will block system call and you will not + * even be able to shutdown... + */ + XP_SKIP("This will cause an infinate loop in the kernel"); + return; + } + + mode = openable_mode(); + fd = OPEN(devaudio, mode); + REQUIRED_SYS_OK(fd); + + AUDIO_INITINFO(&ai); + ai.play.sample_rate = 0; + ai.record.sample_rate = 0; + r = IOCTL(fd, AUDIO_SETINFO, &ai, "sample_rate=0"); + /* Expects to fail */ + XP_SYS_NG(EINVAL, r); + + r = CLOSE(fd); + XP_SYS_EQ(0, r); +} + +/* + * Check whether the pause/unpause works. + */ +void +test_AUDIO_SETINFO_pause(int openmode, int aimode, int param) +{ + struct audio_info ai; + int r; + int fd; + + /* + * aimode is bool value that indicates whether to change ai.mode. + * param is bool value that indicates whether to change encoding + * parameters of ai.{play,record}.*. + */ + + TEST("AUDIO_SETINFO_pause_%s_%d_%d", + openmode_str[openmode] + 2, aimode, param); + if (mode2aumode(openmode) == 0) { + XP_SKIP("Operation not allowed on this hardware property"); + return; + } + + /* On half-duplex, O_RDWR is the same as O_WRONLY, so skip it */ + if (!hw_fulldup() && openmode == O_RDWR) { + XP_SKIP("This is the same with O_WRONLY on half-duplex"); + return; + } + + fd = OPEN(devaudio, openmode); + REQUIRED_SYS_OK(fd); + + /* Set pause */ + AUDIO_INITINFO(&ai); + ai.play.pause = 1; + ai.record.pause = 1; + if (aimode) + ai.mode = mode2aumode(openmode) & ~AUMODE_PLAY_ALL; + if (param) { + ai.play.sample_rate = 11025; + ai.record.sample_rate = 11025; + } + + r = IOCTL(fd, AUDIO_SETINFO, &ai, ""); + XP_SYS_EQ(0, r); + + r = IOCTL(fd, AUDIO_GETBUFINFO, &ai, ""); + XP_SYS_EQ(0, r); + int expmode = (aimode) + ? (mode2aumode(openmode) & ~AUMODE_PLAY_ALL) + : mode2aumode(openmode); + XP_EQ(expmode, ai.mode); + XP_EQ(1, ai.play.pause); + XP_EQ(param ? 11025 : 8000, ai.play.sample_rate); + XP_EQ(1, ai.record.pause); + XP_EQ(param ? 11025 : 8000, ai.record.sample_rate); + + /* Set unpause (?) */ + AUDIO_INITINFO(&ai); + ai.play.pause = 0; + ai.record.pause = 0; + if (aimode) + ai.mode = mode2aumode(openmode); + if (param) { + ai.play.sample_rate = 16000; + ai.record.sample_rate = 16000; + } + + r = IOCTL(fd, AUDIO_SETINFO, &ai, ""); + XP_SYS_EQ(0, r); + + r = IOCTL(fd, AUDIO_GETBUFINFO, &ai, ""); + XP_SYS_EQ(0, r); + XP_EQ(mode2aumode(openmode), ai.mode); + XP_EQ(0, ai.play.pause); + XP_EQ(0, ai.record.pause); + if (openmode != O_RDONLY) + XP_EQ(param ? 16000 : 8000, ai.play.sample_rate); + if (openmode != O_WRONLY) + XP_EQ(param ? 16000 : 8000, ai.record.sample_rate); + + r = CLOSE(fd); + XP_SYS_EQ(0, r); +} +DEF(AUDIO_SETINFO_pause_RDONLY_0) { test_AUDIO_SETINFO_pause(O_RDONLY, 0, 0); } +DEF(AUDIO_SETINFO_pause_RDONLY_1) { test_AUDIO_SETINFO_pause(O_RDONLY, 0, 1); } +/* On RDONLY, ai.mode is not changable + * AUDIO_SETINFO_pause_RDONLY_2) { test_AUDIO_SETINFO_pause(O_RDONLY, 1, 0); } + * AUDIO_SETINFO_pause_RDONLY_3) { test_AUDIO_SETINFO_pause(O_RDONLY, 1, 1); } + */ +DEF(AUDIO_SETINFO_pause_WRONLY_0) { test_AUDIO_SETINFO_pause(O_WRONLY, 0, 0); } +DEF(AUDIO_SETINFO_pause_WRONLY_1) { test_AUDIO_SETINFO_pause(O_WRONLY, 0, 1); } +DEF(AUDIO_SETINFO_pause_WRONLY_2) { test_AUDIO_SETINFO_pause(O_WRONLY, 1, 0); } +DEF(AUDIO_SETINFO_pause_WRONLY_3) { test_AUDIO_SETINFO_pause(O_WRONLY, 1, 1); } +DEF(AUDIO_SETINFO_pause_RDWR_0) { test_AUDIO_SETINFO_pause(O_RDWR, 0, 0); } +DEF(AUDIO_SETINFO_pause_RDWR_1) { test_AUDIO_SETINFO_pause(O_RDWR, 0, 1); } +DEF(AUDIO_SETINFO_pause_RDWR_2) { test_AUDIO_SETINFO_pause(O_RDWR, 1, 0); } +DEF(AUDIO_SETINFO_pause_RDWR_3) { test_AUDIO_SETINFO_pause(O_RDWR, 1, 1); } + +/* + * Check whether gain can be obtained/set. + * And the gain should work with rich mixer. + * PR kern/52781 + */ +DEF(AUDIO_SETINFO_gain) +{ + struct audio_info ai; + mixer_ctrl_t m; + int index; + int master; + int master_backup; + int gain; + int fd; + int mixerfd; + int r; + + TEST("AUDIO_SETINFO_gain"); + + /* Open /dev/mixer */ + mixerfd = OPEN(devmixer, O_RDWR); + REQUIRED_SYS_OK(mixerfd); + index = mixer_get_outputs_master(mixerfd); + if (index == -1) { + XP_SKIP("Hardware has no outputs.master"); + CLOSE(mixerfd); + return; + } + + /* + * Get current outputs.master. + * auich(4) requires class type (m.type) and number of channels + * (un.value.num_channels) in addition to the index (m.dev)... + * What is the index...? + */ + memset(&m, 0, sizeof(m)); + m.dev = index; + m.type = AUDIO_MIXER_VALUE; + m.un.value.num_channels = 1; /* dummy */ + r = IOCTL(mixerfd, AUDIO_MIXER_READ, &m, "m.dev=%d", m.dev); + REQUIRED_SYS_EQ(0, r); + master = m.un.value.level[0]; + DPRINTF(" > outputs.master = %d\n", master); + master_backup = master; + + /* Open /dev/audio */ + fd = OPEN(devaudio, O_WRONLY); + REQUIRED_SYS_OK(fd); + + /* Check ai.play.gain */ + r = IOCTL(fd, AUDIO_GETINFO, &ai, ""); + XP_SYS_EQ(0, r); + XP_EQ(master, ai.play.gain); + + /* Change it some different value */ + AUDIO_INITINFO(&ai); + if (master == 0) + gain = 255; + else + gain = 0; + ai.play.gain = gain; + r = IOCTL(fd, AUDIO_SETINFO, &ai, "play.gain=%d", ai.play.gain); + XP_SYS_EQ(0, r); + + /* Check gain has changed */ + r = IOCTL(fd, AUDIO_GETINFO, &ai, "play.gain"); + XP_SYS_EQ(0, r); + XP_NE(master, ai.play.gain); + + /* Check whether outputs.master work with gain */ + r = IOCTL(mixerfd, AUDIO_MIXER_READ, &m, ""); + XP_SYS_EQ(0, r); + XP_EQ(ai.play.gain, m.un.value.level[0]); + + /* Restore outputs.master */ + AUDIO_INITINFO(&ai); + ai.play.gain = master_backup; + r = IOCTL(fd, AUDIO_SETINFO, &ai, "play.gain=%d", ai.play.gain); + XP_SYS_EQ(0, r); + + r = CLOSE(fd); + XP_SYS_EQ(0, r); + r = CLOSE(mixerfd); + XP_SYS_EQ(0, r); +} + +/* + * Look if there are any (non-zero) gain values that can be changed. + * If any gain can be set, it is set to gain[0]. + * If another gain can be set, it is set to gain[1], otherwise gain[1] = -1. + * This is for AUDIO_SETINFO_gain_balance. + */ +static void +get_changeable_gain(int fd, int *gain, const char *dir, int offset) +{ + struct audio_info ai; + int *ai_gain; + int hi; + int lo; + int r; + + /* A hack to handle ai.{play,record}.gain in the same code.. */ + ai_gain = (int *)(((char *)&ai) + offset); + + /* Try to set the maximum gain */ + AUDIO_INITINFO(&ai); + *ai_gain = AUDIO_MAX_GAIN; + r = IOCTL(fd, AUDIO_SETINFO, &ai, "%s.gain=%d", dir, *ai_gain); + XP_SYS_EQ(0, r); + /* Get again. The value you set is not always used as is. */ + AUDIO_INITINFO(&ai); + r = IOCTL(fd, AUDIO_GETINFO, &ai, "&ai"); + XP_SYS_EQ(0, r); + hi = *ai_gain; + + /* Look for next configurable value. */ + for (lo = hi - 1; lo >= 0; lo--) { + AUDIO_INITINFO(&ai); + *ai_gain = lo; + r = IOCTL(fd, AUDIO_SETINFO, &ai, "%s.gain=%d", dir, *ai_gain); + XP_SYS_EQ(0, r); + /* Get again */ + r = IOCTL(fd, AUDIO_GETINFO, &ai, "&ai"); + XP_SYS_EQ(0, r); + if (*ai_gain != hi) { + lo = *ai_gain; + break; + } + } + + /* Now gain is lo(=gain[0]). */ + + /* + * hi lo + * --- --- + * <0 <0 : not available. + * >=0 <0 : available but not changeable. + * >=0 >=0 (hi!=lo) : available and changeable. + */ + if (hi < 0) { + gain[0] = -1; + gain[1] = -1; + DPRINTF(" > %s.gain cannot be set\n", dir); + } else if (lo < 0) { + gain[0] = hi; + gain[1] = -1; + DPRINTF(" > %s.gain can only be set %d\n", dir, gain[0]); + } else { + gain[0] = lo; + gain[1] = hi; + DPRINTF(" > %s.gain can be set %d, %d\n", + dir, gain[0], gain[1]); + } +} + +/* + * Look if there are any balance values that can be changed. + * If any balance value can be set, it is set to balance[0]. + * If another balance value can be set, it is set to balance[1], + * otherwise balance[1] = -1. + * This is for AUDIO_SETINFO_gain_balance. + */ +static void +get_changeable_balance(int fd, int *balance, const char *dir, int offset) +{ + struct audio_info ai; + u_char *ai_balance; + u_char left; + u_char right; + int r; + + /* A hack to handle ai.{play,record}.balance in the same code.. */ + ai_balance = ((u_char *)&ai) + offset; + + /* Look for the right side configurable value. */ + AUDIO_INITINFO(&ai); + *ai_balance = AUDIO_RIGHT_BALANCE; + r = IOCTL(fd, AUDIO_SETINFO, &ai, "%s.balance=%d", dir, *ai_balance); + XP_SYS_EQ(0, r); + /* Get again. The value you set is not always used as is. */ + r = IOCTL(fd, AUDIO_GETINFO, &ai, "&ai"); + XP_SYS_EQ(0, r); + right = *ai_balance; + + /* Look for the left side configurable value. */ + AUDIO_INITINFO(&ai); + *ai_balance = AUDIO_LEFT_BALANCE; + r = IOCTL(fd, AUDIO_SETINFO, &ai, "%s.balance=%d", dir, *ai_balance); + XP_SYS_EQ(0, r); + /* Get again */ + r = IOCTL(fd, AUDIO_GETINFO, &ai, "&ai"); + XP_SYS_EQ(0, r); + left = *ai_balance; + + /* Now balance is the left(=balance[0]). */ + + if (left == right) { + /* The driver has no balance feature. */ + balance[0] = left; + balance[1] = -1; + DPRINTF(" > %s.balance can only be set %d\n", + dir, balance[0]); + } else { + balance[0] = left; + balance[1] = right; + DPRINTF(" > %s.balance can be set %d, %d\n", + dir, balance[0], balance[1]); + } +} + +/* + * Check whether gain and balance can be set at the same time. + * PR kern/56308 + */ +DEF(AUDIO_SETINFO_gain_balance) +{ + struct audio_info oai; + struct audio_info ai; + int i; + int mode; + int fd; + int r; + int pgain[2]; + int pbalance[2]; + int rgain[2]; + int rbalance[2]; + bool ptest; + bool rtest; + + TEST("AUDIO_SETINFO_gain_balance"); + + mode = openable_mode(); + fd = OPEN(devaudio, mode); + REQUIRED_SYS_OK(fd); + + /* Backup current gain and balance */ + r = IOCTL(fd, AUDIO_GETINFO, &oai, "&oai"); + XP_SYS_EQ(0, r); + + if (debug) { + printf(" > old play.gain = %d\n", oai.play.gain); + printf(" > old play.balance = %d\n", oai.play.balance); + printf(" > old record.gain = %d\n", oai.record.gain); + printf(" > old record.balance = %d\n", oai.record.balance); + } + + for (i = 0; i < 2; i++) { + pgain[i] = -1; + pbalance[i] = -1; + rgain[i] = -1; + rbalance[i] = -1; + } + + /* + * First, check each one separately can be changed. + * + * The simplest two different gain values are zero and non-zero. + * But some device drivers seem to process balance differently + * when the gain is high enough and when the gain is zero or near. + * So I needed to select two different "non-zero (and high if + * possible)" gains. + */ + if (hw_canplay()) { + get_changeable_gain(fd, pgain, "play", + offsetof(struct audio_info, play.gain)); + get_changeable_balance(fd, pbalance, "play", + offsetof(struct audio_info, play.balance)); + } + if (hw_canrec()) { + get_changeable_gain(fd, rgain, "record", + offsetof(struct audio_info, record.gain)); + get_changeable_balance(fd, rbalance, "record", + offsetof(struct audio_info, record.balance)); + } + + /* + * [0] [1] + * --- --- + * -1 * : not available. + * >=0 -1 : available but not changeable. + * >=0 >=0 : available and changeable. It can be tested. + */ + ptest = (pgain[0] >= 0 && pgain[1] >= 0 && + pbalance[0] >= 0 && pbalance[1] >= 0); + rtest = (rgain[0] >= 0 && rgain[1] >= 0 && + rbalance[0] >= 0 && rbalance[1] >= 0); + + if (ptest == false && rtest == false) { + XP_SKIP( + "The test requires changeable gain and changeable balance"); + + /* Restore as possible */ + AUDIO_INITINFO(&ai); + ai.play.gain = oai.play.gain; + ai.play.balance = oai.play.balance; + ai.record.gain = oai.record.gain; + ai.record.balance = oai.record.balance; + r = IOCTL(fd, AUDIO_SETINFO, &ai, "restore all"); + XP_SYS_EQ(0, r); + + r = CLOSE(fd); + XP_SYS_EQ(0, r); + return; + } + + /* + * If both play.gain and play.balance are changeable, + * it should be able to set both at the same time. + */ + if (ptest) { + AUDIO_INITINFO(&ai); + ai.play.gain = pgain[1]; + ai.play.balance = pbalance[1]; + r = IOCTL(fd, AUDIO_SETINFO, &ai, "play.gain=%d/balance=%d", + ai.play.gain, ai.play.balance); + XP_SYS_EQ(0, r); + + AUDIO_INITINFO(&ai); + r = IOCTL(fd, AUDIO_GETINFO, &ai, "&ai"); + XP_SYS_EQ(0, r); + + DPRINTF(" > setting play.gain=%d/balance=%d: " + "result gain=%d/balance=%d\n", + pgain[1], pbalance[1], ai.play.gain, ai.play.balance); + XP_EQ(ai.play.gain, pgain[1]); + XP_EQ(ai.play.balance, pbalance[1]); + } + /* + * If both record.gain and record.balance are changeable, + * it should be able to set both at the same time. + */ + if (rtest) { + AUDIO_INITINFO(&ai); + ai.record.gain = rgain[1]; + ai.record.balance = rbalance[1]; + r = IOCTL(fd, AUDIO_SETINFO, &ai, "record.gain=%d/balance=%d", + ai.record.gain, ai.record.balance); + XP_SYS_EQ(0, r); + + AUDIO_INITINFO(&ai); + r = IOCTL(fd, AUDIO_GETINFO, &ai, "&ai"); + XP_SYS_EQ(0, r); + + DPRINTF(" > setting record.gain=%d/balance=%d: " + "result gain=%d/balance=%d\n", + rgain[1], rbalance[1], ai.record.gain, ai.record.balance); + XP_EQ(ai.record.gain, rgain[1]); + XP_EQ(ai.record.balance, rbalance[1]); + } + + /* + * Restore all values as possible at the same time. + * This restore is also a test. + */ + AUDIO_INITINFO(&ai); + ai.play.gain = oai.play.gain; + ai.play.balance = oai.play.balance; + ai.record.gain = oai.record.gain; + ai.record.balance = oai.record.balance; + r = IOCTL(fd, AUDIO_SETINFO, &ai, "restore all"); + XP_SYS_EQ(0, r); + + AUDIO_INITINFO(&ai); + r = IOCTL(fd, AUDIO_GETINFO, &ai, "&ai"); + XP_SYS_EQ(0, r); + XP_EQ(oai.play.gain, ai.play.gain); + XP_EQ(oai.play.balance, ai.play.balance); + XP_EQ(oai.record.gain, ai.record.gain); + XP_EQ(oai.record.balance, ai.record.balance); + + r = CLOSE(fd); + XP_SYS_EQ(0, r); +} + +#define NENC (AUDIO_ENCODING_AC3 + 1) +#define NPREC (5) +/* + * Make table of encoding+precision supported by this device. + * Return last used index . + * This function is called from test_AUDIO_GETENC_*() + */ +int +getenc_make_table(int fd, int expected[][5]) +{ + audio_encoding_t ae; + int idx; + int p; + int r; + + /* + * expected[][] is two dimensional table. + * encoding \ precision| 4 8 16 24 32 + * --------------------+----------------- + * AUDIO_ENCODING_NONE | + * AUDIO_ENCODING_ULAW | + * : + * + * Each cell has expected behavior. + * 0: the hardware doesn't support this encoding/precision. + * 1: the hardware supports this encoding/precision. + * 2: the hardware doesn't support this encoding/precision but + * audio layer will respond as supported for compatibility. + */ + for (idx = 0; ; idx++) { + memset(&ae, 0, sizeof(ae)); + ae.index = idx; + r = IOCTL(fd, AUDIO_GETENC, &ae, "index=%d", idx); + if (r != 0) { + XP_SYS_NG(EINVAL, r); + break; + } + + XP_EQ(idx, ae.index); + if (0 <= ae.encoding && ae.encoding <= AUDIO_ENCODING_AC3) { + XP_EQ_STR(encoding_names[ae.encoding], ae.name); + } else { + XP_FAIL("ae.encoding %d", ae.encoding); + } + + if (ae.precision != 4 && + ae.precision != 8 && + ae.precision != 16 && + ae.precision != 24 && + ae.precision != 32) + { + XP_FAIL("ae.precision %d", ae.precision); + } + /* Other bits should not be set */ + XP_EQ(0, (ae.flags & ~AUDIO_ENCODINGFLAG_EMULATED)); + + expected[ae.encoding][ae.precision / 8] = 1; + DPRINTF(" > encoding=%s precision=%d\n", + encoding_names[ae.encoding], ae.precision); + } + + /* + * Backward compatibility bandaid. + * + * - Some encoding/precision pairs are obviously inconsistent + * (e.g., encoding=AUDIO_ENCODING_PCM8, precision=16) but + * it's due to historical reasons. + * - It's incomplete for NetBSD7 and NetBSD8. I don't really + * understand thier rule... This is just memo, not specification. + */ +#define SET(x) do { \ + if ((x) == 0) \ + x = 2; \ + } while (0) +#define p4 (0) +#define p8 (1) +#define p16 (2) +#define p24 (3) +#define p32 (4) + + if (expected[AUDIO_ENCODING_SLINEAR][p8]) { + SET(expected[AUDIO_ENCODING_SLINEAR_LE][p8]); + SET(expected[AUDIO_ENCODING_SLINEAR_BE][p8]); + } + if (expected[AUDIO_ENCODING_ULINEAR][p8]) { + SET(expected[AUDIO_ENCODING_ULINEAR_LE][p8]); + SET(expected[AUDIO_ENCODING_ULINEAR_BE][p8]); + SET(expected[AUDIO_ENCODING_PCM8][p8]); + SET(expected[AUDIO_ENCODING_PCM16][p8]); + } + for (p = p16; p <= p32; p++) { +#if !defined(AUDIO_SUPPORT_LINEAR24) + if (p == p24) + continue; +#endif + if (expected[AUDIO_ENCODING_SLINEAR_NE][p]) { + SET(expected[AUDIO_ENCODING_SLINEAR][p]); + SET(expected[AUDIO_ENCODING_PCM16][p]); + } + if (expected[AUDIO_ENCODING_ULINEAR_NE][p]) { + SET(expected[AUDIO_ENCODING_ULINEAR][p]); + } + } + + if (netbsd < 9) { + if (expected[AUDIO_ENCODING_SLINEAR_LE][p16] || + expected[AUDIO_ENCODING_SLINEAR_BE][p16] || + expected[AUDIO_ENCODING_ULINEAR_LE][p16] || + expected[AUDIO_ENCODING_ULINEAR_BE][p16]) + { + SET(expected[AUDIO_ENCODING_PCM8][p8]); + SET(expected[AUDIO_ENCODING_PCM16][p8]); + SET(expected[AUDIO_ENCODING_SLINEAR_LE][p8]); + SET(expected[AUDIO_ENCODING_SLINEAR_BE][p8]); + SET(expected[AUDIO_ENCODING_ULINEAR_LE][p8]); + SET(expected[AUDIO_ENCODING_ULINEAR_BE][p8]); + SET(expected[AUDIO_ENCODING_SLINEAR][p8]); + SET(expected[AUDIO_ENCODING_ULINEAR][p8]); + } + } + + /* Return last used index */ + return idx; +#undef SET +#undef p4 +#undef p8 +#undef p16 +#undef p24 +#undef p32 +} + +/* + * This function is called from test_AUDIO_GETENC below. + */ +void +xp_getenc(int expected[][5], int enc, int j, int r, struct audio_prinfo *pr) +{ + int prec = (j == 0) ? 4 : j * 8; + + if (expected[enc][j]) { + /* expect to succeed */ + XP_SYS_EQ(0, r); + + XP_EQ(enc, pr->encoding); + XP_EQ(prec, pr->precision); + } else { + /* expect to fail */ + XP_SYS_NG(EINVAL, r); + } +} + +/* + * This function is called from test_AUDIO_GETENC below. + */ +void +getenc_check_encodings(int openmode, int expected[][5]) +{ + struct audio_info ai; + int fd; + int i, j; + int r; + + fd = OPEN(devaudio, openmode); + REQUIRED_SYS_OK(fd); + + for (i = 0; i < NENC; i++) { + for (j = 0; j < NPREC; j++) { + /* precisions are 4 and 8, 16, 24, 32 */ + int prec = (j == 0) ? 4 : j * 8; + + /* + * AUDIO_GETENC has no way to know range of + * supported channels and sample_rate. + */ + AUDIO_INITINFO(&ai); + ai.play.encoding = i; + ai.play.precision = prec; + ai.record.encoding = i; + ai.record.precision = prec; + + r = IOCTL(fd, AUDIO_SETINFO, &ai, "%s:%d", + encoding_names[i], prec); + if (mode2play(openmode)) + xp_getenc(expected, i, j, r, &ai.play); + if (mode2rec(openmode)) + xp_getenc(expected, i, j, r, &ai.record); + } + } + r = CLOSE(fd); + XP_SYS_EQ(0, r); +} + +/* + * Check whether encoding+precision obtained by AUDIO_GETENC can be set. + */ +DEF(AUDIO_GETENC_range) +{ + audio_encoding_t ae; + int fd; + int r; + int expected[NENC][NPREC]; + int i, j; + + TEST("AUDIO_GETENC_range"); + + fd = OPEN(devaudio, openable_mode()); + REQUIRED_SYS_OK(fd); + + memset(&expected, 0, sizeof(expected)); + i = getenc_make_table(fd, expected); + + /* When error has occured, the next index should also occur error */ + ae.index = i + 1; + r = IOCTL(fd, AUDIO_GETENC, &ae, "index=%d", ae.index); + XP_SYS_NG(EINVAL, r); + + r = CLOSE(fd); + XP_SYS_EQ(0, r); + + /* For debug */ + if (debug) { + for (i = 0; i < NENC; i++) { + printf("expected[%2d] %15s", i, encoding_names[i]); + for (j = 0; j < NPREC; j++) { + printf(" %d", expected[i][j]); + } + printf("\n"); + } + } + + /* Whether obtained encodings can be actually set */ + if (hw_fulldup()) { + /* Test both R/W at once using single descriptor */ + getenc_check_encodings(O_RDWR, expected); + } else { + /* Test playback and recording if available */ + if (hw_canplay()) { + getenc_check_encodings(O_WRONLY, expected); + } + if (hw_canplay() && hw_canrec()) { + xxx_close_wait(); + } + if (hw_canrec()) { + getenc_check_encodings(O_RDONLY, expected); + } + } +} +#undef NENC +#undef NPREC + +/* + * Check AUDIO_GETENC out of range. + */ +DEF(AUDIO_GETENC_error) +{ + audio_encoding_t e; + int fd; + int r; + + TEST("AUDIO_GETENC_error"); + + fd = OPEN(devaudio, openable_mode()); + REQUIRED_SYS_OK(fd); + + memset(&e, 0, sizeof(e)); + e.index = -1; + r = IOCTL(fd, AUDIO_GETENC, &e, "index=-1"); + /* NetBSD7 may not fail depending on hardware driver */ + XP_SYS_NG(EINVAL, r); + + r = CLOSE(fd); + XP_SYS_EQ(0, r); +} + +/* + * AUDIO_[PR]ERROR should be zero on the initial state even on non-existent + * track. + */ +void +test_AUDIO_ERROR(int openmode) +{ + int fd; + int r; + int errors; + + TEST("AUDIO_ERROR_%s", openmode_str[openmode] + 2); + if (mode2aumode(openmode) == 0) { + XP_SKIP("Operation not allowed on this hardware property"); + return; + } + + fd = OPEN(devaudio, openmode); + REQUIRED_SYS_OK(fd); + + /* Check PERROR */ + errors = 0xdeadbeef; + r = IOCTL(fd, AUDIO_PERROR, &errors, ""); + XP_SYS_EQ(0, r); + XP_EQ(0, errors); + + /* Check RERROR */ + errors = 0xdeadbeef; + r = IOCTL(fd, AUDIO_RERROR, &errors, ""); + XP_SYS_EQ(0, r); + XP_EQ(0, errors); + + r = CLOSE(fd); + XP_SYS_EQ(0, r); +} +DEF(AUDIO_ERROR_RDONLY) { test_AUDIO_ERROR(O_RDONLY); } +DEF(AUDIO_ERROR_WRONLY) { test_AUDIO_ERROR(O_WRONLY); } +DEF(AUDIO_ERROR_RDWR) { test_AUDIO_ERROR(O_RDWR); } + +/* + * /dev/audioctl can always be opened while /dev/audio is open. + */ +void +test_audioctl_open_1(int fmode, int cmode) +{ + int fd; + int ctl; + int r; + + TEST("audioctl_open_1_%s_%s", + openmode_str[fmode] + 2, openmode_str[cmode] + 2); + if (hw_canplay() == 0 && fmode == O_WRONLY) { + XP_SKIP("This test is for playable device"); + return; + } + if (hw_canrec() == 0 && fmode == O_RDONLY) { + XP_SKIP("This test is for recordable device"); + return; + } + + fd = OPEN(devaudio, fmode); + REQUIRED_SYS_OK(fd); + + ctl = OPEN(devaudioctl, cmode); + XP_SYS_OK(ctl); + + r = CLOSE(ctl); + XP_SYS_EQ(0, r); + + r = CLOSE(fd); + XP_SYS_EQ(0, r); +} +DEF(audioctl_open_1_RDONLY_RDONLY) { test_audioctl_open_1(O_RDONLY, O_RDONLY); } +DEF(audioctl_open_1_RDONLY_RWONLY) { test_audioctl_open_1(O_RDONLY, O_WRONLY); } +DEF(audioctl_open_1_RDONLY_RDWR) { test_audioctl_open_1(O_RDONLY, O_RDWR); } +DEF(audioctl_open_1_WRONLY_RDONLY) { test_audioctl_open_1(O_WRONLY, O_RDONLY); } +DEF(audioctl_open_1_WRONLY_RWONLY) { test_audioctl_open_1(O_WRONLY, O_WRONLY); } +DEF(audioctl_open_1_WRONLY_RDWR) { test_audioctl_open_1(O_WRONLY, O_RDWR); } +DEF(audioctl_open_1_RDWR_RDONLY) { test_audioctl_open_1(O_RDWR, O_RDONLY); } +DEF(audioctl_open_1_RDWR_RWONLY) { test_audioctl_open_1(O_RDWR, O_WRONLY); } +DEF(audioctl_open_1_RDWR_RDWR) { test_audioctl_open_1(O_RDWR, O_RDWR); } + +/* + * /dev/audio can always be opened while /dev/audioctl is open. + */ +void +test_audioctl_open_2(int fmode, int cmode) +{ + int fd; + int ctl; + int r; + + TEST("audioctl_open_2_%s_%s", + openmode_str[fmode] + 2, openmode_str[cmode] + 2); + if (hw_canplay() == 0 && fmode == O_WRONLY) { + XP_SKIP("This test is for playable device"); + return; + } + if (hw_canrec() == 0 && fmode == O_RDONLY) { + XP_SKIP("This test is for recordable device"); + return; + } + + ctl = OPEN(devaudioctl, cmode); + REQUIRED_SYS_OK(ctl); + + fd = OPEN(devaudio, fmode); + XP_SYS_OK(fd); + + r = CLOSE(fd); + XP_SYS_EQ(0, r); + + r = CLOSE(ctl); + XP_SYS_EQ(0, r); +} +DEF(audioctl_open_2_RDONLY_RDONLY) { test_audioctl_open_2(O_RDONLY, O_RDONLY); } +DEF(audioctl_open_2_RDONLY_RWONLY) { test_audioctl_open_2(O_RDONLY, O_WRONLY); } +DEF(audioctl_open_2_RDONLY_RDWR) { test_audioctl_open_2(O_RDONLY, O_RDWR); } +DEF(audioctl_open_2_WRONLY_RDONLY) { test_audioctl_open_2(O_WRONLY, O_RDONLY); } +DEF(audioctl_open_2_WRONLY_RWONLY) { test_audioctl_open_2(O_WRONLY, O_WRONLY); } +DEF(audioctl_open_2_WRONLY_RDWR) { test_audioctl_open_2(O_WRONLY, O_RDWR); } +DEF(audioctl_open_2_RDWR_RDONLY) { test_audioctl_open_2(O_RDWR, O_RDONLY); } +DEF(audioctl_open_2_RDWR_RWONLY) { test_audioctl_open_2(O_RDWR, O_WRONLY); } +DEF(audioctl_open_2_RDWR_RDWR) { test_audioctl_open_2(O_RDWR, O_RDWR); } + +/* + * Open multiple /dev/audioctl. + */ +DEF(audioctl_open_simul) +{ + int ctl0; + int ctl1; + int r; + + TEST("audioctl_open_simul"); + + ctl0 = OPEN(devaudioctl, O_RDWR); + REQUIRED_SYS_OK(ctl0); + + ctl1 = OPEN(devaudioctl, O_RDWR); + XP_SYS_OK(ctl1); + + r = CLOSE(ctl0); + XP_SYS_EQ(0, r); + + r = CLOSE(ctl1); + XP_SYS_EQ(0, r); +} + +/* + * /dev/audioctl can be opened by other user who opens /dev/audioctl, + * /dev/audioctl can be opened by other user who opens /dev/audio, + * /dev/audio can be opened by other user who opens /dev/audioctl, + * regardless of multiuser mode. + */ +void +try_audioctl_open_multiuser(const char *dev1, const char *dev2) +{ + int fd1; + int fd2; + int r; + uid_t ouid; + + /* + * At first, open dev1 as root. + * And then open dev2 as unprivileged user. + */ + + fd1 = OPEN(dev1, O_RDWR); + REQUIRED_SYS_OK(fd1); + + ouid = GETUID(); + r = SETEUID(1); + REQUIRED_SYS_EQ(0, r); + + fd2 = OPEN(dev2, O_RDWR); + XP_SYS_OK(fd2); + + /* Close */ + r = CLOSE(fd2); + XP_SYS_EQ(0, r); + + r = SETEUID(ouid); + REQUIRED_SYS_EQ(0, r); + + r = CLOSE(fd1); + XP_SYS_EQ(0, r); +} +/* + * This is a wrapper for audioctl_open_multiuser. + * XXX XP_* macros are not compatible with on-error-goto, we need try-catch... + */ +void +test_audioctl_open_multiuser(bool multiuser, + const char *dev1, const char *dev2) +{ + char mibname[32]; + bool oldval; + size_t oldlen; + int r; + + if (netbsd < 8 && multiuser == 1) { + XP_SKIP("multiuser is not supported"); + return; + } + if (netbsd < 9) { + /* NetBSD8 has no way (difficult) to determine device name */ + XP_SKIP("NetBSD8 cannot determine device name"); + return; + } + if (geteuid() != 0) { + XP_SKIP("This test must be priviledged user"); + return; + } + + /* Get current multiuser mode (and save it) */ + snprintf(mibname, sizeof(mibname), "hw.%s.multiuser", devicename); + oldlen = sizeof(oldval); + r = SYSCTLBYNAME(mibname, &oldval, &oldlen, NULL, 0); + REQUIRED_SYS_EQ(0, r); + DPRINTF(" > multiuser=%d\n", oldval); + + /* Change if necessary */ + if (oldval != multiuser) { + r = SYSCTLBYNAME(mibname, NULL, NULL, &multiuser, + sizeof(multiuser)); + REQUIRED_SYS_EQ(0, r); + DPRINTF(" > new multiuser=%d\n", multiuser); + } + + /* Do test */ + try_audioctl_open_multiuser(dev1, dev2); + + /* Restore multiuser mode */ + if (oldval != multiuser) { + DPRINTF(" > restore multiuser to %d\n", oldval); + r = SYSCTLBYNAME(mibname, NULL, NULL, &oldval, sizeof(oldval)); + XP_SYS_EQ(0, r); + } +} +DEF(audioctl_open_multiuser0_audio1) { + TEST("audioctl_open_multiuser0_audio1"); + test_audioctl_open_multiuser(false, devaudio, devaudioctl); +} +DEF(audioctl_open_multiuser1_audio1) { + TEST("audioctl_open_multiuser1_audio1"); + test_audioctl_open_multiuser(true, devaudio, devaudioctl); +} +DEF(audioctl_open_multiuser0_audio2) { + TEST("audioctl_open_multiuser0_audio2"); + test_audioctl_open_multiuser(false, devaudioctl, devaudio); +} +DEF(audioctl_open_multiuser1_audio2) { + TEST("audioctl_open_multiuser1_audio2"); + test_audioctl_open_multiuser(true, devaudioctl, devaudio); +} +DEF(audioctl_open_multiuser0_audioctl) { + TEST("audioctl_open_multiuser0_audioctl"); + test_audioctl_open_multiuser(false, devaudioctl, devaudioctl); +} +DEF(audioctl_open_multiuser1_audioctl) { + TEST("audioctl_open_multiuser1_audioctl"); + test_audioctl_open_multiuser(true, devaudioctl, devaudioctl); +} + +/* + * /dev/audioctl cannot be read/written regardless of its open mode. + */ +void +test_audioctl_rw(int openmode) +{ + char buf[1]; + int fd; + int r; + + TEST("audioctl_rw_%s", openmode_str[openmode] + 2); + + fd = OPEN(devaudioctl, openmode); + REQUIRED_SYS_OK(fd); + + if (mode2play(openmode)) { + r = WRITE(fd, buf, sizeof(buf)); + XP_SYS_NG(ENODEV, r); + } + + if (mode2rec(openmode)) { + r = READ(fd, buf, sizeof(buf)); + XP_SYS_NG(ENODEV, r); + } + + r = CLOSE(fd); + XP_SYS_EQ(0, r); +} +DEF(audioctl_rw_RDONLY) { test_audioctl_rw(O_RDONLY); } +DEF(audioctl_rw_WRONLY) { test_audioctl_rw(O_WRONLY); } +DEF(audioctl_rw_RDWR) { test_audioctl_rw(O_RDWR); } + +/* + * poll(2) for /dev/audioctl should never raise. + * I'm not sure about consistency between poll(2) and kqueue(2) but + * anyway I follow it. + * XXX Omit checking each openmode + */ +DEF(audioctl_poll) +{ + struct pollfd pfd; + int fd; + int r; + + TEST("audioctl_poll"); + + fd = OPEN(devaudioctl, O_WRONLY); + REQUIRED_SYS_OK(fd); + + pfd.fd = fd; + pfd.events = POLLOUT; + r = POLL(&pfd, 1, 100); + XP_SYS_EQ(0, r); + XP_EQ(0, pfd.revents); + + r = CLOSE(fd); + XP_SYS_EQ(0, r); +} + +/* + * kqueue(2) for /dev/audioctl fails. + * I'm not sure about consistency between poll(2) and kqueue(2) but + * anyway I follow it. + * XXX Omit checking each openmode + */ +DEF(audioctl_kqueue) +{ + struct kevent kev; + int fd; + int kq; + int r; + + TEST("audioctl_kqueue"); + + fd = OPEN(devaudioctl, O_WRONLY); + REQUIRED_SYS_OK(fd); + + kq = KQUEUE(); + XP_SYS_OK(kq); + + EV_SET(&kev, fd, EVFILT_WRITE, EV_ADD, 0, 0, 0); + r = KEVENT_SET(kq, &kev, 1); + /* + * NetBSD7 has a bug. It looks to wanted to treat it as successful + * but returned 1(== EPERM). + * On NetBSD9, I decided to return ENODEV. + */ + if (netbsd < 8) { + XP_SYS_NG(1/*EPERM*/, r); + } else { + XP_SYS_NG(ENODEV, r); + } + + r = CLOSE(fd); + XP_SYS_EQ(0, r); +} + + +/* + * This table is processed by t_audio.awk! + * Keep /^\tENT(testname),/ format in order to add to atf. + */ +#define ENT(x) { #x, test__ ## x } +struct testentry testtable[] = { + ENT(open_mode_RDONLY), + ENT(open_mode_WRONLY), + ENT(open_mode_RDWR), + ENT(open_audio_RDONLY), + ENT(open_audio_WRONLY), + ENT(open_audio_RDWR), + ENT(open_sound_RDONLY), + ENT(open_sound_WRONLY), + ENT(open_sound_RDWR), + ENT(open_audioctl_RDONLY), + ENT(open_audioctl_WRONLY), + ENT(open_audioctl_RDWR), + ENT(open_sound_sticky), + ENT(open_audioctl_sticky), + ENT(open_simul_RDONLY_RDONLY), + ENT(open_simul_RDONLY_WRONLY), + ENT(open_simul_RDONLY_RDWR), + ENT(open_simul_WRONLY_RDONLY), + ENT(open_simul_WRONLY_WRONLY), + ENT(open_simul_WRONLY_RDWR), + ENT(open_simul_RDWR_RDONLY), + ENT(open_simul_RDWR_WRONLY), + ENT(open_simul_RDWR_RDWR), +/**/ ENT(open_multiuser_0), // XXX TODO sysctl +/**/ ENT(open_multiuser_1), // XXX TODO sysctl + ENT(write_PLAY_ALL), + ENT(write_PLAY), + ENT(read), + ENT(rept_write), + ENT(rept_read), + ENT(rdwr_fallback_RDONLY), + ENT(rdwr_fallback_WRONLY), + ENT(rdwr_fallback_RDWR), + ENT(rdwr_two_RDONLY_RDONLY), + ENT(rdwr_two_RDONLY_WRONLY), + ENT(rdwr_two_RDONLY_RDWR), + ENT(rdwr_two_WRONLY_RDONLY), + ENT(rdwr_two_WRONLY_WRONLY), + ENT(rdwr_two_WRONLY_RDWR), + ENT(rdwr_two_RDWR_RDONLY), + ENT(rdwr_two_RDWR_WRONLY), + ENT(rdwr_two_RDWR_RDWR), + ENT(rdwr_simul), + ENT(drain_incomplete), + ENT(drain_pause), + ENT(drain_onrec), +/**/ ENT(mmap_mode_RDONLY_NONE), // XXX rump doesn't supprot mmap +/**/ ENT(mmap_mode_RDONLY_READ), // XXX rump doesn't supprot mmap +/**/ ENT(mmap_mode_RDONLY_WRITE), // XXX rump doesn't supprot mmap +/**/ ENT(mmap_mode_RDONLY_READWRITE),// XXX rump doesn't supprot mmap +/**/ ENT(mmap_mode_WRONLY_NONE), // XXX rump doesn't supprot mmap +/**/ ENT(mmap_mode_WRONLY_READ), // XXX rump doesn't supprot mmap +/**/ ENT(mmap_mode_WRONLY_WRITE), // XXX rump doesn't supprot mmap +/**/ ENT(mmap_mode_WRONLY_READWRITE),// XXX rump doesn't supprot mmap +/**/ ENT(mmap_mode_RDWR_NONE), // XXX rump doesn't supprot mmap +/**/ ENT(mmap_mode_RDWR_READ), // XXX rump doesn't supprot mmap +/**/ ENT(mmap_mode_RDWR_WRITE), // XXX rump doesn't supprot mmap +/**/ ENT(mmap_mode_RDWR_READWRITE), // XXX rump doesn't supprot mmap +/**/ ENT(mmap_len), // XXX rump doesn't supprot mmap +/**/ ENT(mmap_twice), // XXX rump doesn't supprot mmap +/**/ ENT(mmap_multi), // XXX rump doesn't supprot mmap + ENT(poll_mode_RDONLY_IN), + ENT(poll_mode_RDONLY_OUT), + ENT(poll_mode_RDONLY_INOUT), + ENT(poll_mode_WRONLY_IN), + ENT(poll_mode_WRONLY_OUT), + ENT(poll_mode_WRONLY_INOUT), + ENT(poll_mode_RDWR_IN), + ENT(poll_mode_RDWR_OUT), + ENT(poll_mode_RDWR_INOUT), + ENT(poll_out_empty), + ENT(poll_out_full), + ENT(poll_out_hiwat), +/**/ ENT(poll_out_unpause), // XXX does not seem to work on rump +/**/ ENT(poll_out_simul), // XXX does not seem to work on rump + ENT(poll_in_open_audio), + ENT(poll_in_open_sound), + ENT(poll_in_open_audioctl), + ENT(poll_in_simul), + ENT(kqueue_mode_RDONLY_READ), + ENT(kqueue_mode_RDONLY_WRITE), + ENT(kqueue_mode_WRONLY_READ), + ENT(kqueue_mode_WRONLY_WRITE), + ENT(kqueue_mode_RDWR_READ), + ENT(kqueue_mode_RDWR_WRITE), + ENT(kqueue_empty), + ENT(kqueue_full), + ENT(kqueue_hiwat), +/**/ ENT(kqueue_unpause), // XXX does not seem to work on rump +/**/ ENT(kqueue_simul), // XXX does not seem to work on rump + ENT(ioctl_while_write), + ENT(FIOASYNC_reset), + ENT(FIOASYNC_play_signal), + ENT(FIOASYNC_rec_signal), +/**/ ENT(FIOASYNC_multi), // XXX does not seem to work on rump + ENT(AUDIO_WSEEK), + ENT(AUDIO_SETFD_RDONLY), + ENT(AUDIO_SETFD_WRONLY), + ENT(AUDIO_SETFD_RDWR), + ENT(AUDIO_GETINFO_eof), + ENT(AUDIO_SETINFO_mode_RDONLY_0), + ENT(AUDIO_SETINFO_mode_RDONLY_1), + ENT(AUDIO_SETINFO_mode_RDONLY_2), + ENT(AUDIO_SETINFO_mode_RDONLY_3), + ENT(AUDIO_SETINFO_mode_RDONLY_4), + ENT(AUDIO_SETINFO_mode_RDONLY_5), + ENT(AUDIO_SETINFO_mode_RDONLY_6), + ENT(AUDIO_SETINFO_mode_RDONLY_7), + ENT(AUDIO_SETINFO_mode_RDONLY_8), + ENT(AUDIO_SETINFO_mode_WRONLY_0), + ENT(AUDIO_SETINFO_mode_WRONLY_1), + ENT(AUDIO_SETINFO_mode_WRONLY_2), + ENT(AUDIO_SETINFO_mode_WRONLY_3), + ENT(AUDIO_SETINFO_mode_WRONLY_4), + ENT(AUDIO_SETINFO_mode_WRONLY_5), + ENT(AUDIO_SETINFO_mode_WRONLY_6), + ENT(AUDIO_SETINFO_mode_WRONLY_7), + ENT(AUDIO_SETINFO_mode_WRONLY_8), + ENT(AUDIO_SETINFO_mode_RDWR_0), + ENT(AUDIO_SETINFO_mode_RDWR_1), + ENT(AUDIO_SETINFO_mode_RDWR_2), + ENT(AUDIO_SETINFO_mode_RDWR_3), + ENT(AUDIO_SETINFO_mode_RDWR_4), + ENT(AUDIO_SETINFO_mode_RDWR_5), + ENT(AUDIO_SETINFO_mode_RDWR_6), + ENT(AUDIO_SETINFO_mode_RDWR_7), + ENT(AUDIO_SETINFO_mode_RDWR_8), + ENT(AUDIO_SETINFO_params_set_RDONLY_0), + ENT(AUDIO_SETINFO_params_set_RDONLY_1), + ENT(AUDIO_SETINFO_params_set_WRONLY_0), + ENT(AUDIO_SETINFO_params_set_WRONLY_1), + ENT(AUDIO_SETINFO_params_set_WRONLY_2), + ENT(AUDIO_SETINFO_params_set_WRONLY_3), + ENT(AUDIO_SETINFO_params_set_RDWR_0), + ENT(AUDIO_SETINFO_params_set_RDWR_1), + ENT(AUDIO_SETINFO_params_set_RDWR_2), + ENT(AUDIO_SETINFO_params_set_RDWR_3), + ENT(AUDIO_SETINFO_params_simul), + ENT(AUDIO_SETINFO_channels), + ENT(AUDIO_SETINFO_sample_rate), + ENT(AUDIO_SETINFO_sample_rate_0), + ENT(AUDIO_SETINFO_pause_RDONLY_0), + ENT(AUDIO_SETINFO_pause_RDONLY_1), + ENT(AUDIO_SETINFO_pause_WRONLY_0), + ENT(AUDIO_SETINFO_pause_WRONLY_1), + ENT(AUDIO_SETINFO_pause_WRONLY_2), + ENT(AUDIO_SETINFO_pause_WRONLY_3), + ENT(AUDIO_SETINFO_pause_RDWR_0), + ENT(AUDIO_SETINFO_pause_RDWR_1), + ENT(AUDIO_SETINFO_pause_RDWR_2), + ENT(AUDIO_SETINFO_pause_RDWR_3), + ENT(AUDIO_SETINFO_gain), + ENT(AUDIO_SETINFO_gain_balance), + ENT(AUDIO_GETENC_range), + ENT(AUDIO_GETENC_error), + ENT(AUDIO_ERROR_RDONLY), + ENT(AUDIO_ERROR_WRONLY), + ENT(AUDIO_ERROR_RDWR), + ENT(audioctl_open_1_RDONLY_RDONLY), + ENT(audioctl_open_1_RDONLY_RWONLY), + ENT(audioctl_open_1_RDONLY_RDWR), + ENT(audioctl_open_1_WRONLY_RDONLY), + ENT(audioctl_open_1_WRONLY_RWONLY), + ENT(audioctl_open_1_WRONLY_RDWR), + ENT(audioctl_open_1_RDWR_RDONLY), + ENT(audioctl_open_1_RDWR_RWONLY), + ENT(audioctl_open_1_RDWR_RDWR), + ENT(audioctl_open_2_RDONLY_RDONLY), + ENT(audioctl_open_2_RDONLY_RWONLY), + ENT(audioctl_open_2_RDONLY_RDWR), + ENT(audioctl_open_2_WRONLY_RDONLY), + ENT(audioctl_open_2_WRONLY_RWONLY), + ENT(audioctl_open_2_WRONLY_RDWR), + ENT(audioctl_open_2_RDWR_RDONLY), + ENT(audioctl_open_2_RDWR_RWONLY), + ENT(audioctl_open_2_RDWR_RDWR), + ENT(audioctl_open_simul), +/**/ ENT(audioctl_open_multiuser0_audio1), // XXX TODO sysctl +/**/ ENT(audioctl_open_multiuser1_audio1), // XXX TODO sysctl +/**/ ENT(audioctl_open_multiuser0_audio2), // XXX TODO sysctl +/**/ ENT(audioctl_open_multiuser1_audio2), // XXX TODO sysctl +/**/ ENT(audioctl_open_multiuser0_audioctl), // XXX TODO sysctl +/**/ ENT(audioctl_open_multiuser1_audioctl), // XXX TODO sysctl + ENT(audioctl_rw_RDONLY), + ENT(audioctl_rw_WRONLY), + ENT(audioctl_rw_RDWR), + ENT(audioctl_poll), + ENT(audioctl_kqueue), + {.name = NULL}, +}; diff --git a/dev/audio/h_pad.c b/dev/audio/h_pad.c --- a/dev/audio/h_pad.c +++ b/dev/audio/h_pad.c @@ -1,4 +1,4 @@ -/* $NetBSD: h_pad.c,v 1.2 2016/10/15 07:08:06 nat Exp $ */ +/* $NetBSD: h_pad.c,v 1.3 2019/06/20 12:14:46 isaki Exp $ */ /* * Copyright (c) 2010 Antti Kantee. All Rights Reserved. @@ -32,29 +32,114 @@ #include #include +#include #include #include #include #include +#include #include "h_pad_musa.c" /* - * Stuff some audio into /dev/audio, read it from /dev/pad. Use in - * conjunction with t_pad, which tests that we got sensible output - * by comparing against a previous audibly good result. + * Stuff some audio into /dev/audio, read it from /dev/pad. */ #define BUFSIZE 1024 +static const int16_t mulaw_to_slinear16[256] = { + 0x8284, 0x8684, 0x8a84, 0x8e84, 0x9284, 0x9684, 0x9a84, 0x9e84, + 0xa284, 0xa684, 0xaa84, 0xae84, 0xb284, 0xb684, 0xba84, 0xbe84, + 0xc184, 0xc384, 0xc584, 0xc784, 0xc984, 0xcb84, 0xcd84, 0xcf84, + 0xd184, 0xd384, 0xd584, 0xd784, 0xd984, 0xdb84, 0xdd84, 0xdf84, + 0xe104, 0xe204, 0xe304, 0xe404, 0xe504, 0xe604, 0xe704, 0xe804, + 0xe904, 0xea04, 0xeb04, 0xec04, 0xed04, 0xee04, 0xef04, 0xf004, + 0xf0c4, 0xf144, 0xf1c4, 0xf244, 0xf2c4, 0xf344, 0xf3c4, 0xf444, + 0xf4c4, 0xf544, 0xf5c4, 0xf644, 0xf6c4, 0xf744, 0xf7c4, 0xf844, + 0xf8a4, 0xf8e4, 0xf924, 0xf964, 0xf9a4, 0xf9e4, 0xfa24, 0xfa64, + 0xfaa4, 0xfae4, 0xfb24, 0xfb64, 0xfba4, 0xfbe4, 0xfc24, 0xfc64, + 0xfc94, 0xfcb4, 0xfcd4, 0xfcf4, 0xfd14, 0xfd34, 0xfd54, 0xfd74, + 0xfd94, 0xfdb4, 0xfdd4, 0xfdf4, 0xfe14, 0xfe34, 0xfe54, 0xfe74, + 0xfe8c, 0xfe9c, 0xfeac, 0xfebc, 0xfecc, 0xfedc, 0xfeec, 0xfefc, + 0xff0c, 0xff1c, 0xff2c, 0xff3c, 0xff4c, 0xff5c, 0xff6c, 0xff7c, + 0xff88, 0xff90, 0xff98, 0xffa0, 0xffa8, 0xffb0, 0xffb8, 0xffc0, + 0xffc8, 0xffd0, 0xffd8, 0xffe0, 0xffe8, 0xfff0, 0xfff8, 0xfffc, + 0x7d7c, 0x797c, 0x757c, 0x717c, 0x6d7c, 0x697c, 0x657c, 0x617c, + 0x5d7c, 0x597c, 0x557c, 0x517c, 0x4d7c, 0x497c, 0x457c, 0x417c, + 0x3e7c, 0x3c7c, 0x3a7c, 0x387c, 0x367c, 0x347c, 0x327c, 0x307c, + 0x2e7c, 0x2c7c, 0x2a7c, 0x287c, 0x267c, 0x247c, 0x227c, 0x207c, + 0x1efc, 0x1dfc, 0x1cfc, 0x1bfc, 0x1afc, 0x19fc, 0x18fc, 0x17fc, + 0x16fc, 0x15fc, 0x14fc, 0x13fc, 0x12fc, 0x11fc, 0x10fc, 0x0ffc, + 0x0f3c, 0x0ebc, 0x0e3c, 0x0dbc, 0x0d3c, 0x0cbc, 0x0c3c, 0x0bbc, + 0x0b3c, 0x0abc, 0x0a3c, 0x09bc, 0x093c, 0x08bc, 0x083c, 0x07bc, + 0x075c, 0x071c, 0x06dc, 0x069c, 0x065c, 0x061c, 0x05dc, 0x059c, + 0x055c, 0x051c, 0x04dc, 0x049c, 0x045c, 0x041c, 0x03dc, 0x039c, + 0x036c, 0x034c, 0x032c, 0x030c, 0x02ec, 0x02cc, 0x02ac, 0x028c, + 0x026c, 0x024c, 0x022c, 0x020c, 0x01ec, 0x01cc, 0x01ac, 0x018c, + 0x0174, 0x0164, 0x0154, 0x0144, 0x0134, 0x0124, 0x0114, 0x0104, + 0x00f4, 0x00e4, 0x00d4, 0x00c4, 0x00b4, 0x00a4, 0x0094, 0x0084, + 0x0078, 0x0070, 0x0068, 0x0060, 0x0058, 0x0050, 0x0048, 0x0040, + 0x0038, 0x0030, 0x0028, 0x0020, 0x0018, 0x0010, 0x0008, 0x0000, +}; + +#define DPRINTF(n, fmt...) do { \ + if (debug >= (n)) \ + printf(fmt); \ +} while (0) + +int debug; + int main(int argc, char *argv[]) { - char buf[BUFSIZE]; - char zeros[BUFSIZE]; + struct audio_info ai; int padfd, audiofd; ssize_t n; + int i; + int nframe; + int outlen; + uint32_t *outbuf; + uint32_t *outp; + int inplen; + uint32_t *inpbuf; + uint32_t actual; + uint32_t expected; + int c; + enum { + PRE, + BODY, + POST, + } phase; + + while ((c = getopt(argc, argv, "d")) != -1) { + switch (c) { + case 'd': + debug++; + break; + default: + errx(1, "unknown option"); + } + } + + /* Make input buffer (and it is also expected data). */ + inplen = sizeof(musa) * 4; + inpbuf = (uint32_t *)malloc(inplen); + if (inpbuf == NULL) + err(1, "malloc: inpbuf"); + /* mulaw:mono to slinear_le16:stereo */ + for (i = 0; i < (int)sizeof(musa); i++) { + int16_t s = mulaw_to_slinear16[musa[i]]; + uint32_t v = htole16((uint16_t)s); + inpbuf[i] = (v << 16) | v; + } + + outlen = BUFSIZE; + outbuf = (uint32_t *)malloc(outlen); + if (outbuf == NULL) + err(1, "malloc: outbuf"); + + DPRINTF(1, "init\n"); rump_init(); padfd = rump_sys_open("/dev/pad0", O_RDONLY); if (padfd == -1) @@ -64,13 +149,85 @@ if (audiofd == -1) err(1, "open audio"); - if ((n = rump_sys_write(audiofd, musa, sizeof(musa))) != sizeof(musa)) + DPRINTF(1, "ioctl\n"); + /* pad is SLINEAR_LE, 16bit, 2ch, 44100Hz. */ + AUDIO_INITINFO(&ai); + ai.play.encoding = AUDIO_ENCODING_SLINEAR_LE; + ai.play.precision = 16; + ai.play.channels = 2; + ai.play.sample_rate = 44100; + n = rump_sys_ioctl(audiofd, AUDIO_SETINFO, &ai); + if (n == -1) + err(1, "ioctl"); + + DPRINTF(1, "write %d\n", inplen); + n = rump_sys_write(audiofd, inpbuf, inplen); + if (n == -1) err(1, "write"); + if (n != inplen) + errx(1, "write: n=%zd < %d", n, inplen); - memset(zeros, 0, sizeof(zeros)); - while ((n = rump_sys_read(padfd, buf, sizeof(buf))) > 0) { - if (memcmp(buf, zeros, sizeof(buf)) == 0) - break; - write(STDOUT_FILENO, buf, n); + phase = PRE; + i = 0; + nframe = 0; + outp = NULL; + for (;;) { + /* Read to outbuf when it is empty. */ + if (nframe == 0) { + n = rump_sys_read(padfd, outbuf, outlen); + if (n == -1) + err(1, "read"); + if (n == 0) + errx(1, "read: EOF"); + /* XXX Should I recover from this? */ + if (n % 4 != 0) + errx(1, "read: n=%zd", n); + + nframe = n / 4; + outp = outbuf; + } + + if (phase == PRE) { + /* Skip preceding silence part. */ + if (*outp == 0) { + outp++; + nframe--; + } else { + /* This is the first frame. */ + phase = BODY; + } + } else if (phase == BODY) { + /* Compare wavedata. */ + expected = le32dec(outp); + actual = le32dec(inpbuf + i); + DPRINTF(2, "[%d] %08x %08x\n", i, actual, expected); + if (actual != expected) { + errx(1, "bad output [%d] %08x %08x", + i, actual, expected); + } + outp++; + nframe--; + i++; + if (i >= (int)sizeof(musa)) { + phase = POST; + i = 0; + } + } else if (phase == POST) { + /* + * There is no way to determine the end of playback. + * Therefore it detects and terminates with some + * continuous silence. + */ + actual = le32dec(outp); + if (actual != 0) + errx(1, "bad post output: %08x", actual); + outp++; + nframe--; + i++; + if (i >= (int)ai.play.sample_rate / 100) + break; + } } + DPRINTF(1, "success\n"); + return 0; } diff --git a/dev/audio/t_audio.awk b/dev/audio/t_audio.awk new file mode 100644 --- /dev/null +++ b/dev/audio/t_audio.awk @@ -0,0 +1,76 @@ +# $NetBSD: t_audio.awk,v 1.2 2020/03/25 13:07:04 isaki Exp $ +# +# Copyright (C) 2019 Tetsuya Isaki. 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 ``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 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. +# + +BEGIN { + n = 0 + print "#" + print "# This file is regenerated by t_audio.awk" + print "#" + print "" + print "# Call real test program, and then dispatch the result for atf." + print "h_audio() {" + print " local testname=$1" + print " local outfile=/tmp/t_audio_$testname.$$" + print " $(atf_get_srcdir)/audiotest -ARe $testname > $outfile" + print " local retval=$?" + print " # Discard rump outputs..." + print " outmsg=`cat $outfile | grep -v '^\\['`" + print " rm -f $outfile" + print " if [ \"$retval\" = \"0\" ]; then" + print " atf_pass" + print " elif [ \"$retval\" = \"1\" ]; then" + print " atf_fail \"$outmsg\"" + print " elif [ \"$retval\" = \"2\" ]; then" + print " atf_skip \"$outmsg\"" + print " else" + print " atf_fail \"unknown error $retval\"" + print " fi" + print "}" + print "" +} +# Gather only tab-indented ENT(). I.e., +# ^\tENT(testname) ... Enable in audiotest and add it to atf +# ^//\tENT(testname) ... Disable in audiotest and also don't add to atf +# ^/**/\tENT(testname) ... Enable in audiotest but don't add to atf +match($0, /^\tENT\([^\)]*\)/) { + name = substr($0, RSTART + 5, RLENGTH - 6) + tests[n] = name + n++; + + print "atf_test_case " name + print name "_head() { }" + print name "_body() {" + print "\th_audio " name + print "}" + print "" +} +END { + print "atf_init_test_cases() {" + for (i = 0; i < length(tests); i++) { + print "\tatf_add_test_case " tests[i] + } + print "}" +} diff --git a/dev/audio/t_pad.sh b/dev/audio/t_pad.sh old mode 100755 new mode 100644 --- a/dev/audio/t_pad.sh +++ b/dev/audio/t_pad.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_pad.sh,v 1.3 2010/11/07 17:51:17 jmmv Exp $ +# $NetBSD: t_pad.sh,v 1.4 2019/06/20 12:14:46 isaki Exp $ # # Copyright (c) 2010 The NetBSD Foundation, Inc. # All rights reserved. @@ -29,15 +29,17 @@ pad_output_head() { - atf_set "descr" "Check pad driver output against known-good output" + atf_set "descr" "Check pad driver output" } pad_output_body() { - atf_check -s exit:0 uudecode $(atf_get_srcdir)/t_pad_output.bz2.uue - atf_check -s exit:0 bunzip2 t_pad_output.bz2 - atf_check -s exit:0 -o file:t_pad_output $(atf_get_srcdir)/h_pad + # h_pad(librumpdev_pad) may outputs an error message (on slow + # machines?). But we can ignore it. The audio driver was made + # for the real kernel and real hardware and realtime processing. + # librump + pad is at the opposite. + atf_check -o ignore $(atf_get_srcdir)/h_pad } atf_init_test_cases() diff --git a/dev/audio/t_pad_output.bz2.uue b/dev/audio/t_pad_output.bz2.uue deleted file mode 100644 --- a/dev/audio/t_pad_output.bz2.uue +++ /dev/null @@ -1,1035 +0,0 @@ -begin 644 t_pad_output.bz2 -M0EIH.3%!629369%IQ#X`MT#FU'+1O7<;MT;NYNNYW<[N[OO;K>W7+>^Y[M;[LV]??=M] -MYZ[MMMO>WO;6=W.KIOKNUF:EFEJVK5I"+:J;8:B5K1JJIJVUJV,K6IK5MJJ6 -M--%5FJLS55K158M5K:U5FU4RVMJ(K0BK-6Q0-L+6JVLJ6MK559M5556*M54J -MJMIMFS-"U6LJRUBRJJRV;55::M6U[G=0JI5-4W66J=KVZ]LNW=]W=FW=S;YM -MDO<]LW>Z[WW%E@WUOD-[G!SN<'=W$CD8@[KN<36:PVO=W;*I*E%4SC -MV]XE)5!54*"@*JJMH)J0H*`4&V4`!11(``!4BJI27O>Z(]5()`22)**%4``I -M0`!*B5`D(DB*5@:5``AH`"8F3"8`F0#3(Q,FF@#1H&(&C)H-#3330!ID,C`C -M!-,F)IIA`TQ-,!,33$831@@R9&$T`!,`@TD@FC$``````&@```:-`3`F$P0T -M,$!@`$``#03T!H-`U,`33$TTQJ-,$P$P%/$R8",!-02FDI"!$QJ>S,`0`$!H -M:!#":$Q-,28$V@0-`34]$:;*:>D]1I[0H'HGDRC:AD&@]0&@/4`]0R`R,AH- -M!H`VHS2`:`(4E)*)&R>J-^GBGL!2,:::2;48GI$IO%)Y)Y)[3TE/U1^FFC*C -MQ&H>IZC$T,AIZAZF31H>4``'J!DTTT---```#U```-`9#0T``T!(I((28-)B -M>A3TTF>14]3VT&IJ>R,@F$TQ'DT3&@FU3\4GFDT>J>333&IBFTPF$T]$-,32 -MGM-4]IDTR)ZI^1I3V332;(93-4_5/R>D#":GDU-D:FIZ>IM3U-HTH)-25)%1 -MM3:3]L/-2$FWD4S4U-B:::FB;4V3"4_4R4>H>U0;4]JGJ&33T]4T`#(T-&0] -M0!HT-`_5`!Z@T````9!IH:```````_Q$/Z:,D&8X$&L.'B.D00+!M12TQ2"> -MGH4)$$U776'(,NA5T"`@H&+#%`;<'^;2<4$`CK#I>?6\SY]KTOQX,LDD%=#] -M=K=W[.S5.*PD:9EE+#8V,8RRL@"_3]+%\'Y\OE<[XY25)9,&QL(&#!(B3@T. -MCTYNF5=LVA0D0'QD+"T]J]AX/R0)P"P$\13QX$Q,/GR(O5_U5.R7J.DE`0K( -MEC0PL$_ONQ0)A@@60_";^_OE&;Q_:\_3BPGL8?\=O:KUNG!-MF0A03X9PPMX -MF'WE;&SK)O(N2J"`80_J9Y6*=6(?DZ7*_=I%;(JB,Q$/X5S@P$*M6IZ6$>F. -M&L;Y&C($S\KC'O`Q#')%M:;\FTP-4ET5[;UV7JU<$0D7*H[&KC\]MFO+$`\& -MK%L^,?8,8N-=10VF1`%ZT<0V#-6C#OCSQ<+:D1!\*'M+14@>!P\/!T&+LDA` -MM>S!`Y))"PU-A7RRF1#XLD2TAAXFXPES0HB!="\G,MI5;+<6\:#8!?:^9F[% -ME',L8MFO;T)7.GR!;_`/D0:1+;UF7Y[3=J&V"``O/U.NE!"#*9$.X86#N,8I -M`#2>Y2VR.+WM3?[`$7UIE`R.12-/ADE3=;A$1!:]O_:**7`0NS1[]JFWB&;, -M9H]?3J1$29(D`##!T*ZI]K?Y2?W`!+<"-:V[)FM&)64CY`27=R&1(43Q0:.3 -M57.RL\G5'0B!%DUBC6R2:/8GH36'00V/_3FF$$BHG7;+O%I3X!8&S2LK'/>1 -M8OS=Z#;4Y$43X>>W=G@!;JCZJXZ+CG@"_SV[O7ZI=P8DJ:+_5D',I4)`;G=/ -M'LMX@K6`@UZ4;>CQD0!>G1L^>LPH@:>W<=.CS^7@)?+I``+POTESZ]3/ZA2! -M75?3@*BYG9V,AZN4Q4&IJ@(<=6[N*2:B4T)ITB]VC:@%[*8[4SK5EZR_]5WDD@B*BP]Z -MM&`(P"9NX?<(%_4:IX/(`!5M=`Y*J0ALV/=9UEH,?/)])PN4S.3Q*JX(OE@. -M2:Z?"+C-^RQK#(AH^YZF'"(N4H(.K.`QOV&@=HI"`,-P0@1,W5)#ZE916Z/V -MDMCV8^`4YS\-7R*L!D&Y@OT/@[O\HB+8?+K:EP*>6KQIHI#9&]*\I@AOG`B[ -ME!(1^\3.":TV[8($^"X\PV,D"7'@5IA -M_&HS5?4^K5(@17-'D>"%FJ8^%HE-%-GP07=Y"M@$+JL?O=<_H_;+X"$$)/+$ -M(#&Y%>VSVDIS6@!!PH[W&3U(!SXV;RV<\".VZ\"A!%NDZM&LY*JMTR0+GH5% -M-UD0(OWQ1PM=G=;!V2D>(AEP2@)MH6&EBU77QS:R7B0`5P)?N)=]4'"%!``L -M6>;IEY>3]?^J#F8Y((@VJ'9(3=(Y;EL/U*W3_M""&*6LOPA)`2G2\;XHZ?,1 -MN@9?--I!`7BU)`)6&?V2\RV=GPPO_+AI`13_]&!9NI8WF&D8=EAD>=1@A]-M -M2*,@.#^UP_^]`1,'Y@7\>&O=GPWW86]K`46*"9"107H9R&2FIS4QC(C9?S9E -M0`/+,`K\G03>F3F0D&K]O^I"WC##9]90$6;P/K*\,L":#V^TRY\OGS?!Z.>D -M($R4A>S*K^+BN!7^#D(`!):^.YO0(#Y_7L:3*O#7:[G5>S#2DD#:[ZDW8'GX -M2)284[['@*/]+TCLR$C2\R=8*W5,5CI?!P%6`2B(:+04!>;6K6S6]](_;T]*B(`QH.-:D+F0Y(DOX[O8A^D&ZOMFWV'@4'P8^/`G6' -M4MT7D&'K95`"'TM;8HC'LCRM8)T%MM8^D+N/=;2(V#3NCX`4ISMR_2ZY!(<< -MODMH/*.`-_8\FLT9T`6L6N;B -ML+\].#\V8R;3BE920@#2V1+PA(E.R3ONAYKUN//A^GSGB+*OAX\D`:G4 -M:N6A>'`4Q\%%^V;+*\"K@=4D2`OFKMZ-$0*_[`*>S-)N7WOTB9UK:P8!A_-* -M!#IK6Q(LWF_4F>(UJO>>(9:Z7`0D,+EMF0/N3>?#*1()V)O8Z86&8^!<^ZRS=X!-^V>ZVP;`ZT!\%Z,_M4O]>(!*Q -M%78V.MY[N[_RBAL(JLA?)W_"-+S/ZED0 -M;/3I;2@T6N\'?(;S])2EU,C6-+$@`&SZ+)688"[5QVM98;-GB%YN\`AY&9IE -MW6!=**T2J@+$5]."ANQ]8K\>S;J0'=Q541?&&5H]>H.:O/8H/BC(H+5TL-!% -MC&+N0$H<`3/0LN='R;P@R<.!'<-C*;7FPP#W3-Z"8=_X,P_=A -MXHKO&(2"G=D,QU$C((T6%6GP&!#S"XF5-4V6YAT`'5EB!2_H5R+\+"TB'N$` -M+T%X4&3]-]G#I0>*@2%GIH7 -MSW9O]0D`OIGB+^\1)&P36=Y]RD/%JD1%"5@`[_O_LUM7*JV1\K6;+I3/![/Q -M.#`S2;N_U[:(B>+8XRN,SF.'J%WW8:0"&DUMP7D:"XONX&O-1"LFTT^RG -MR+S^5?!8/;3Y9K[##DDM+1(B+E*B/MD%:Y4H%W4-P>!,]+^'3!:;H\PZRRFC -M,*N]1$`Y3OHLIC/@I?18G7MEW5J04*WZE9NVEJCX]O3A@!"*.^XTQR#K_H+B -MK`_GST-]:GHOXGP6;!7_\>WC/V4[-<>[Y"(A'>SUX`+BNS=W40.$/SF;@#%7 -M/G9ZN`.E)(9E.O"`OX'_/=0TANWS\@%ZGZ"Q`@[-KB[#G4C"?(+B9O2!?3V? -MU(RWC7&#H]VG6T@"3_+P0#-+4"/+:-K195`'/]=0!>5EY/E\%"0EO%WYN;I\ -M,!92L^L[-@(=H^*F`-%2+>M'^T?2W -M'C:U,6=Z7MD- -M!S,3Y&RP>BX!RT^M!(O4_R)^"A]\?+TU4;M'@"5;;:ZIS@]DEP@`GW#)=.K] -MO\7W&3'^J?:TH`>FSXA%M?%/2O67]%.K"6EKOD22)U:K(B^/TNND%OAGR,>$A17>EHS;U89'SEZ,TK*[C@G2H>W_/QJP2&S5GGO -MJE2]X=O]U=K22`\/)G.A*\;1D5Y8TB9-1"S]]V@!)?#_OC6ZNP(-L@?M71N) -MF5SY`+]]\X//8((6RI`6'=Q>`,79HH,_*7BDQ5B$ -MF#C;\$,9E\_=O%KQ8180+*XJ:0$1[TM:]]/]?3>UY$I,\SDOR,#I1E=JFS"! -M)D6KO$6H8D4OU,[!)!$>B\C[>U3$!Z^KJW(<\.5K:U&!G41!!%$5_Y/6QC+5MNVCN4"&]Z]DJO`(9_'6]7?*ZF8/$&+R1H`D$,1>.+._G@W*)DDB;]VG`*5YF7 -MWD]IU[X\&5T,-")):A_]LR"]=;MJN5!LX6;D=]@@KQ`@^-G`>;-=H$6+3?0; -MYG3YLP?<=K)H?)5`"JW_5R6!`Q9OZ8Z#_17/+4<,$0*_:]M -M['1B(#.(QC+-;7CH'N\LREOH(>W0-/OY\=&_S[F9)"7_77XA@06;X>OJ&IOK -M]_"/D$]\6"(#S']/.AK:OAKS"CC7-%WN:`*S+_$Q\,^Z5>:9-EA;.ZD`(GSF -MIQ:L*`\3U-]F](KQ2F<&;.+!Y/Y?&`$JI5%RF94'0/-]9,`:CO;'C<7'S6Y/ -MC(IK?C@3Z*)/UJ'6-^%6!`"074SY(^7^GX$0[^K\-)/;:';#_=4R9TB+O0^G -M[]]7D3#&W$Q$WJ'_>WZV-KK`!WGEP^O)3UHKSY6'>%*ZJN$_$'M>40"Y'4TKT[6!UHFC)`&?/)6ALNE4_=YKB+#=*OU0?%/4DSB6RY5I(*1T -MBS_(D<8`16E0,O"KK9W:_/'P9`7LW2]VZ=L6P&CX1W4GLP1+"_P[R>#N[U"/ -M6WJ(6I\K#MUC,*=$& -M1:KI+^\1_-\P/5`$`\H[779:X^25NX"RC<8`-!NW(+E)7#8<-$0NNC.=8%;Y -M""W5''H1A\USP=7IXRZHLP`5SU[KII-+72^YR&2"Z82`/&[YW!C^'UK5S(MO -MW&B.-PG"G%8BH=8XL2#/=7#/E$X>/AR(5Z^AT]%#>CG.$(E5&]2?[<@D>*0/ -MLZ<&PTK&10ST<\S7A0!X<3B>_3^EJE_\RHP"TFVYCEX^77UK9&C9T20A*]99 -M7K6E``:<-TKEE8"N>WK92J@43N+VH+CGZ#],-YHU(37CX]<0)-RJOCFS%/>< -MAQN%3[ZBT6R(:O24[5N6@(0PW=6K8XBYT._;F*0;7N`AGO:TU&>^N%)?1CJ+ -MGA$!F/HR`O_,-OQ]:S+*EE+7F; -M)8:Y8$Z23!0[=.AU<55;Y+G00EZ:%X+;4.QH1$7K^V(=S>;QJF5!%*(66:M6 -M[]<2>?+NA5:@B'.L/3K#D=IF8CCD"I:=]2]C(V:VB`Z -MOS\_5M\P`2K/>9)C:#G,^D1A32(@7>9.&JW(0D0'@NOJ*)ZVW.3&/)46P09QKXZA5D`53`!D=\)LX*R1 -M!$VM(PTC]$J02F,8P,".;C\K*?9X_7%;MH6>P6<2HYV8U=JFL3)L0P;&-M=6 -MQ"QWM=K46:O@8B=]_\<9C+E\'NRM?3;$*AU\"9&FVFFQH!@"],<7:RQP[:(X -M(.&1#:N&(>`2H-SY3E0:Q```RU<;/1R="%-3!('Y%^`@'U3/^:?U'4DDD^2K -M_QSJ>F@<3(!_5_39XX%+[S0)3Q!$2G$(CR,W5,0ALWVR5@^PNW\Z?G(" -M`E5V)'F?/MYL@H'05?I>U/0;5$JNX(9@PLAMWF/]5Y=G2)KZA/W]\6KGP7TW -M=W;`93G$(W[@47APG.]9PXDV619K+/GQ=Y"H#*8MA2K<.U0:]J -M:[P_S0(`0S@3L$R/6W=-GT;+A'"+='OL8`Z36_XSN)T+M;(@*I\0Q$P[W.QU -M75`U^S3T[*WDD"J.S-BXH)4"-NVA`QPE7A^H`7^G#K^Y#]GW?Z@6;MLN!LH;Y8V[TBAUSY!77`-3DZ).?CA%NN(3U]N[8L)PO7=2[[? -MK?+IP4G)+./7E[L39T%%XY?YT$-0U[8TLY4Z3UDGQ6P.(=`BEAE9/#\#15F/ -M`>_ME9OM"".@E3DR84IGAU)L@Y9W1:M_[OX_)?[G)6:Y=`;!--*(GTMVGOC@ -M"M.:85V$\*TYC78\0T]2+KO*6=_-F75H3!"RI[5E%MF-L:((=8,M2&70#ZW- -M$O9J/0@H[BV!#T_DCO-3-M>J1$32[58X'"UIXHV&4I<18I##UHY6XXEJN&P/ -M[+_)I,DH:BVYVQQZ_O,X=!-]/8)XA,][37V[KG"[%TA.S(Y(\1:.1<'+8>P! -MTC2B]U(SYL#9%1^*\-#T-R]I/8`]M.T2RK3'>,$!HM`\3#=>9@\`-?GY`S\] -M'^7GF;T%I/11:4V6BU8@[1!]C9"PV\A^?P9`WL8!?79D"YCUK:`64G<=<^![ -MM#C.5F/_>8BBW!5>]G?,QPK`MOE8>+$X\S@'%B3EB66;K,\3Q-+A\#K:&\@;TVTVP'I=^: -MY=HX3U'3"E-E/-<7;]7*'2RO(T-)_@5+]/#*EW,7;/R1#'2:/\X@06H4X2+? -M`);^=S>ZPX(A-FAP#!KYMMM\W+;4D_I/>>`'+KI;G\$G;W7IKB3!)@6L?=9B -M8*,D]4:LO-(+-(:$GC_"'/22E/38XZ!EYB]IL#7&,F0SV+_9H#_L$NH_NN$; -M`YY;>`WRW)2(R_EGS0Y>4R/2%0UT!H>XOSJ#,5UEL#1:S819LJ2Y9YXR$;:* -M+2R9H5&H;4'TW\@&]O?C08ZIYG"M.?O$WF_L;$_+KZ/I@,;.8VO8-DAQLE_@ -M*F+&8>S(8K^[BR.2[.:J/%0B[B^IOZ/H"=7ELT(5Z3P-M%;0Y0'"PW0%B^-L -M^CHCB]9>CRF/0VMBJ084\S(]"?`EVGY&D]WJF)<7_849]9\T%NQU1^_WH&,_ -M5^:^K6L2FR(J\PH/W*3)P.C^9',6$)OO0-%D1.;GY>Z%(/[H^5!\IJCTY>TVV!=^XZ] -M/D?;R_(6)'WW?`>S2].TI:(%$?*%L3XD%Z`_[1*CVU3[S"EBV,S3O&V(D7C? -MCX7#((A-/5OOM."4.=)P3%-$'!ZQ;SBG8+&8(;Y]WO/QB!8>_ -M1`09PN#_L;#77^>^Z%CSS_RF*D%4_LM6Y\0W(7R&+SJ10?Z[!0X^R(2VMM<7 -MZ#@QIZ_ML697.F[D/>_WWC^,Z;,J'`[$<)J(QAJ.R^W['JQ@*YSF!LV#'(0G -MINP0WMHSPE6@)+;2#N0I#RQP\XZOY8'[VWX$,"E;-W,?+W,3.!;[=@838['X -MN'5^;;`/^,,G%+:5H+V&^EX>L`)7RZ=4\T.LI[4M4,2"M_G>?%I`_EHW'7=U -M+'\NP(?YXV&)GD&EZ'@@71ZH48<^!BD^E^U2EAOE3@"A4:9C6">_N/?-+AH: -MV$R>6I6S($%7H_C2_"R$&>,CKU`#E3X^\=.6!@_1^I.?=:Q_$04>S+1J?7[P -MOT8K<\G7.YV1#T(@W_3_Y[^,B6: -MEF)6-335TB(-Y'S(@B&TWK?H@"KS3:49A_AC%$T#$SB3$_&C7A%PGX%@U%E# -MH3OAQ,#V+C%%TJMF6WRU[G>I.+H?NE^<7ZO7[4$JNYWJ7W[J"60W_3]NLWF+ -MRZ7*TDY,V)/9C`GOL=[=+.]8$$0`E5TIH!H:),G*`Y\F'SZ6&"/IHYD84M7- -MB@SK9"-\W8^A%&3A-3T0L_8UBV6.(/_'+6Y_4<_W0:X/95G9K[]6,VW;)RW[BL&"4QI*+FQIL3-;]0MTG,P&5];4I-/<#MZ[$G6F\QXH3/S1?6PP?`?+Q$ -M]R*YE='TQ>X[,Z+H34N>&#L0FK==ON$!U>%8>&^_LQ7N:&03DMT_&/"P/5EL4ZUQPD"B_ -M&<%C2OQET?ZHSC`WFH*3S?D6R3B,6V'?[-W>B$Q@W>M,_N;\<`M2)=NG;C07 -M>9]X%OU`=]09"9P(UX,A^TM<,V7&O82A?MG/#`EKFD#9CS(HRZ_O6S(@.2:6 -M92'[SCA]-D@A0XX!P&6[(P'*M+1[4=/><0R]>^8MH)B>5ZXD?R9(8"OG#+]/ -MXU#^`[.XMGMC-!X*,,;J-L\ZJ[9JT&"-'.[P5%`A]1L53J-Z8O^1B&F(?/F? -MEZ6-.7H;)E,M&+ISG+`U?ZT)VCVXY'G]3\C'Y&&]UV(.+R!'\3#[M"',]^NSG/W!-`R+(=B;KN?>=B0!7YM]TGPN?R]6>&E? -M2\]9^5R5!!-?`^QG\F?HCEEH)^F(2RF_6)I0OS;,8,&8'4?ISN> -M4>5)YWW/\][[2T/#PW\^+!DC(RF'_+C\3NG0WN\FGH(:C?6_:G#U_-QO`]/L -MS0O9V;_2/OARD3*]*Q#?(@?>WP$_1TUJX%51#[8U%HU5L747&5+ST6H,!2 -M$X;-DZ;MV/:X!CJ#M-XM6U]D;/`Z@0PP% -MF8::)8Q5B+;K[!7`EGD7FR+Y68,^N&P?O`G.GY'PAY'?!EACMOFL9%XRW$LB -M2'AGMOZ+&XPU;+URN9\?W'D-6+ZHR5Y\[7_/)CNNL@J\UM&A5Q%+NN]]&.=% -M$U4\8`_>`>'`,HH/SN[`*'XE]#\?44AD&_N:@Q(@E("%]S%59Y!(SB/1:].4 -MW5K]Z+_CFN-\O"ZRF5<<61,*,QBBQXIKL0[ZB\?,S(&W)JZN4#'B-$WN"P)EZU%:7!\\_ -MG(CG+4Z09+L97)['"&1WNL:^#0DW7G*"RRF!3,OP,:)>2;LNA)RV"3ZU,^R* -M4.IG6(!CK#(+B8.'T2_W^NROX2A*1LH=*[VT&VU\\HRN/SG/#=KGH['/O<.# -M:MD,GO^/']`S!X]Q"]Y&MNP8FX^)U,5ME6[+T>)FM#Q2_S3_%VXB)5LUEX -M>+7;'D@[U-(:=6'&SD-&)[Z=DZAX<8/0;,9:/#^/J'9@J"U5#Z%&CSR(T0)V -MS.Y84,1>/W<3VO2T.].F:1;VG!XQH@Z)_,\(KV&\\PX%;;=Y/2.8S_ZS4_/=LX2 -MVN:NW,>H7RAPLCFW?KW"Q0#G0^A/!= -MP<([\FPL?J-D>"ZJB[,K_26\6@VQVD7L_`CR2IQ76S4JX\.6S6#&)2VE,7G+ -M3%:>?-AUK$ZV[*N&],3ZW.J+FV#:*#KV,^YD]6>2I*C$JJL2\7L&MZ:5Y\?> -MFZC=+!FI!PQTM68V@V7$888RN<*)`:TP=[R\]U$&_C;4F:,-':'75;7BZ?)[ -M:->K&P^#J_ZP.M(R$//!/IKX:D:#WJ/O*#T_!J)VF3#.,JZ\9GIRTY"\23IL -M[7GQWV+W#DZXJ?S*:M^Z^94<4.IJO:%]CN=[Q=&N1;J5O3Q6`BS@%]",K!&? -M;D]5YWW9KDY8FPV5)@E1M^=X="Z=95@N+B?B8A2[V.&;T3V!F^:R\7J"K&^5 -M>B4GI=!W*.@Q0$I:WC\K%/[R=-92FE"N;C72E0_U$DRCA%.MAXU7G#%G=*5W -MI'''Q@<"!KUFH=&EX?'G-2+BX\H72GD2#6;J3 -M[M!IO)0^%X\.1R"A\;JB^=/["Q?9"(*5_A_;RO;D:8MIG2$S]>8SN=@NGQZ!RY9'U7`WG;M>";K1[:9B$VB\<#)XCYV; -M=D8C`ZWY0NRS5^#$]6^RGZ#%!X6[A\IPI9-IA:KV)FG'(/%)6F0^4=H#ORE_ -M*0>K[,L;Z_OVL8YXT+^_I!BDS)"A]8R=)C$#HPW9[!%AT8K$*`W/?%^($=7/ -M/-ZT&8!)T'',A*Z_B'#V'B]3DQ^DB%LP^Q@P=[K[VNN&\-&V'3QK#)OQF7R\ -MFB_T=B?K>K6-_0.T;6*KK4E4X#0C.>C4:EL'),%X?6]^.;BY*,:3(RVI;K0V -MI[FDQ7J;,?GN#^M[3F%'KBU\L#)1LG0X1[*W:)RE3#6CW,.?@//J)#2\"S.7 -M\V1XS`6JQ\L>$'K==I'+F\KS.N7_RHPC6R$*9!3;CI.^%Q=1UM%9F-Q?P-S> -M(&DQZ8'4\=.>PAJ9Y&'[81 -MBU!XS]NLQK6PEH;8R@A[%F-;+C15JNUH\4:WSVWW3D58ZQC'T*'*;JUU&XJ] -M#N=&QG:27'&21/0F)\3F"IFV').61IE7[!M*A;^'0B$[B$\*?;3:B4PT!,MM -MV]HA%3>-R?[A1Y@YT&.I$4SEF+:OI;\2-Z= -ML]CLQ!48G=#\+[HFA=8$Z`M'\!.(#Y[H%J!SPR$VV6OSF^<;N#%^;%&+/.9X -M<1/#3.YW.`TMZ^`)[L%G??@EE0P*+I1[JR1HX-+>6VK/.YF!JF1=*7EX2< -M&\7:DPEVSXTW??#PV/G7),\&,=;;=K]5#?].'AT6,RM<:`P/@U)IC%WH198Q -MC8S]Z`=Z<\:7=5](-56M?`RKAS^Z6_U>.S9=BA> -M)FZ)9&HQ^$]0W`M`C@`MH[9.[]RRC`@'1U3UI.T)PQ?1`T>:%K'RVC:S61OF -M96XXV.DU^PY,-S60)MG3"YC3OQ1/0^5!`.$YAEIHL@@R(=G'A:'QT<;8&&6B0)61_[4IJ!K'%5).TB*[WJ(['O&K_"$ -MP$-C#YE/FZP?AHLWGK#.ZO)7=WF;NSUC]+%,.U^W9IENONCJG0L,UFK*QS+I -M%S&LUI;]C3FAE2]T<$T!A,?%YRAGK\H[/*_Q)?;TJN)Q11Z^0ZXM@CO`,Q7B1->Q(S2S3NE6Q,$\^N-[]M -M1#Z^YM_X?!J&J"7*?,7LP:X$1;"MH6LVCJPR95[HKIT&G2N8VCU_J>[:,:=I -MJ#/6K>VTNSHABWO(Z7/Y-";,/35L^NXO69)D49Q#L]CE5QHC(,4+Q,T.9E6@ -M:5,#D!PM!]1[$]'\A\OH^![X>_)[X(P_1H3YA^>2:6T0BP#\<`OX=I&,1(?2 -ML7WQ4]NC\&D])_,[`,]4^F]_R_(,!I-!(`.QYJ5Y<\^:0[1&>5F:=3<86763 -M!N/,2PEV>JAC'AO9<=`8_-=]Z#%(C&5/W9CQ+BE0`- -M.9S0/H5)3M.4S:$9%/._OL]QH;?'^,3RS')<,<\?MF9[#C7.+@:5Z]ZEO,KR -ME&C4<'I%0M]O&,0GL[;V9P,XS?P2[@;&REA!(_8PSB01DFHU7")L([$DQ&<\`-5A(EH'/YZ& -M=,[$\]=\SCN#4*CN^;=T#K3V883?\TMN,R.2\4'>;]@ZS_72Y[?)_"W>'L7< -M6S9N9LKFPQX5>+Q[H."G=U6,'0O>D]RQTDUN/$"'[M9/W[T+`N8A!'?APTSB -MUX-PK>+,MGQ&37<+7?8)Z6O:AA4LBV5&3&998%K7DR'LO32L%#.]P:9?V-WN -M5F"FOXC?#\V_(()7\#XSU)[\7J,M%BE,%9#(/;5&H!5ML6(*M/+*UHXU]<#U(GFTE* -MIAIAV7%BBJF6EJ'QU#M"4C4X343C7D\K6(DS]"^KIX/PIJ;[N]&0_;AO"[MW -M1YGT_YZW*_/\5:_R -M@.?#1+X.-.[U4RU-((0^J_)?%17?S*GO@H5/`-LFI3L]0&&\"&M>> -MK3+DQ&F*>4+\=]-4F,H*JJJBEFR,^VJH-*J&VQ55U?=10H7!N*"'0$E,@8?D -M0<]8&>G6D=/Z?+9K_1DT?J-A(F!@1WXT)R4QD1AT^U7G946<)I]*P]2=QG[4 -M?J*3?,"%^*QR;#>]SV6%A?:S4VEMRJA.W1CV!+^`G?']ZBKBM1MXM7@4U,X7 -M^:-=#C%L<>8VH1\Y*&+2%M12J_`ALFD'S9_^S,=]FY3N?\74R_+_6+D#0K`- -MT996(1ZC!"&>_NE(M;>W#?"*WR<\F;P<:,$==RB8?65C6S-",BY")BF^G<*: -M^01=&:?=^"];C^^CO_V)=K"JHI^[6K87'QF7-<+]9E -MCXU-JIP89K=QN6ZV+HZJ_%\\^ZQ7M^RFI@CRYG!])\`F+C>`TK]HQSK*WYW_ -M>S(4V@?L.P5IF"2+BJF^D/-8"SE.1BXW,+[-XL."9%:OKI%"RDF>VR>>GEH; -MJ-?AXUC\+B)TS7N7IZ#^P-1^*8PYL\([&0QD6L0FR7W80IY-&(ZM/`O,#(,4 -M6$\C/6FHI[\5ZAU=1J\#V.K?;26[DZ>H3IZAJEA3*FI@(S%>X"@T/W6W\/55 -M`'U`*B3[R1_OYH+QC?'/MQM2M -MQ]E>Y&()C1, -M\JZ-O^KYF1&Y,L$N+Z-X3L\`PK+H\0ZT.LH>'D]K/._PL&MQ<:^N;K`\:%B# -MTD?I(+1L#TB/'3T_.P$4R3L[AY8GZWWZ50??!-3]]JQ`@BB;PX*,5L-A_:I(TIALLN=V?KPE63I3Z#68#WDT#*0 -M""I&=IDJE4$?6:_-4$-UWUDY -M*LR^RI'^7TO'-RPH'381J9M:5V>Z9<8O,$<U!QB!YP79`\>/!3-*Q(*#Z09/T@DE/A&J@HD#Z26L< -MOI9`2@,:;2Y+2P6<1@FE7QM7:*,N'"&"P:9@Y8N_>Y[!6`,1X6X5Z>`?TE3R -MY1ZJN(Y7N3UKJF)57>OWMAGD^E4O_?+VRJ+/(,4H!@;)1&PSZRR(6S4(/UPV -M;NN+-;=)77]FW -M85FS>;!F.S)_5")LW'2CW'RZ+IT="*+5:Z.N.'/D1=]'H;B2'AN<37AXKL+T*Z_9KOR7,/I=-+1_+?NCBO&6NVI^-ITR6QVBZC:97@K&2^6_JV -MF(2A@,"Q3<7%_SXJ]:P2%4H0:7CG#')";RDM,^?+9U3#21D':*7=*RJD5"M# -MDON*3<,;#HE^;A9%@<$+2BA7Q_=E_-IH9B+CM%)'22X<(N=-F[]F?L[WOYX^ -MO?%_!^NE$>+PX_@TQ)T?7#W1=_=&I%:&,*DV$-(EQ.[TY7,RU-I&A+".'68<*R6U[G4 -M:5"4/:5=4B@_RNQWM>EV[4%*:3QRUOC+JW@I=MUU#N,:Q'I/Q/FNY@-'+Q_8 -MR6*,8YE^C@U[.O5SL)H='FT^/@HDZ,USGWLF+\^GYGM-#M&W)Y/:@;7M)W/) -M\EH6.PR'"TEJ\TE>8J,\T6"*(0L`QD/,0Q@8XX1YB!HX""!C0XCK0TJ\ON9Y -M\5(R-SU;YR3\Q]Y$JLKBM(_QAL[!@JXPGA2V[E=JERN_#Q'E;N+I]?/:]@6% -MAA1K*L;7+:TJ/M^##4LS[3&8#C4S\$V'J/!ZMGJLQO%\LE/<^6:M2?+JHHA/ -M?6UBQM&TG,"'$F`@EJ)MH;$_K-Y9]34,[\_V#^#G9M7H$N;ZVK=M6JKM3YJO -M!K?P@3RYE3#I+LIJ?3:K9]?8+;UL&UG%32(SZRQ'VE7_&3LJO -M]_Q^4\@4UA'UVW>L;TP*ZNKJR\KZC3+J1_7%PT^@K>'[+8F1;+%/4TVS'*LPR@H4,,L,PQCU0 -M/[63`H)YB@X!.SR@X!/3S+F=\.DWUABBNIU7.WE`4%W[QV(N_'T>2R*0?+4Q -M7AB5@(3UN?\ND$VJ3-H[MZS.LJ_^_[P#2N+JU[UI>>.N%5@A&1?782V0H>K\ -M[?:VO.OZS7J>I.KO+/30Z83I#NT5"=,!G2T+(`J`&(Q`!`1\!Z3!,8)CB/GP -M>ERR"9^?CGUN?*E\M#JG.R/YK_^4Y6WWV[5I+[QQF6NE&L07E;V/:A-($MF3 -M9[U)?V,DI+">K(3JDLIZB;Z0;YC&2-\$D)54AWP[]5$12!1$8I#OC&3X0=^3UCUL4UC;:C\Y,UE'G^ -MKGAV>S_PO?Q#-AO2[I3XQ__(=FP6?24:T(+8Y8(?"E)E:J*2@E*6=HR<[Z]C -M@]B/I0EK2Q19]?G:7RMI;7WAKMKLMA/OU>)K<>WYZD26[Z5M"]ZLHGC[_'QA -MY7:SZEFD_7S'S/BHNK*)5\?HYVAT>?XGC^1T?'[?17R-3'[O%6R`^3"-A,1B -M<@[LR=,[IQ5(^<"AH0\X$D>>!\^M:)="><^[/J8.=0Z#OL[N-4'2UY7;2)_: -MXWALJV0D`I0N^IUIW;-EI104$;71QVZ.34/"R'/J]-R.!94"2UYVVQ:'*;-,?2HT"5(>+SNAT/%Y_/. -MAT?]W.^?^J1]UZM[%B/44&QH$D8PY^/$C$F!\9WZQ#2$,5$@;!G9:T9"'H39 -MKOQ&0F,`F0B#)GF6XS^?]^^^59,<^4OO!UJ+GN_??DI,1+/0$:84$A`C.(D1 -M])26$[,^I]H^]>UR>;S3FY:.:C<:>8.8YN8G-(C!!('(![D&665K,E#220ON -M20_(`]S,Q/<`Z.X=CHX6P?%YX#OPLAJZ;.JM\?K*C3FU3W90Z58.0*7O4S*Z -M155T:O9/+-$/7D:GH6,.,,=]\)BAB/$Q0$V)H`03H8Q,"$(3,W>!)44#WMY\ -MPS%\^N]-FR'8?,_6?XV?Q9];Y!YOK<>LX$LX,YI#3_JX_^6)E.0AADV+C"-QT$ -M%OWF[P_3;0AL^+S. -M=[:MM[;<\+(K+6K*+53L[0$DDF"TP)`-@!B1233`1DS(LTG!14X(:=(L:SD( -M?@UOHG!'#1X":+-3?:_.8/5R>NIH3FM`CZB0R)--34;-"M#8X'!P;VV:R*A>@297S%1F!T4'F^"[C#R7W67;^\U\' -M^[_&=_]I^A_Y_V]G\WYW,SL^'@^=X;_VY:*BAKEEV03E0E2G3.#4C"$0E>0, -M30/,:/;UH-49*'B6CQ2E4F20;0]DL[:VW>VK1R6_?W-S[#_/K?@^1G?1T/MS -M0S_QO4S]@RJK+GZ)G,UK-3,BS@EJRM*'882TFTE(@R`[,A,%10AT/2FJI"F?9,_R_V!^Z-#SNU;^#[K*^)]E(N7B_X\"\7AKI -M>82&+PD-:S**:$.T*I8A3M&[;"(UCBA$AMQX`-%K+87$T/4R=]RCY-9A8W-( -MV^C..RYZNN43Z_P9HFJ+V?9B/@.SCG)X\.08E!R62-2,+,%`'GS_*R<]J(99 -M5*A0+D(SQ9AXU/5]5/6/'W_F?+_*_TN?]OKD/Q>E9SXWKBYL'9@O(/UG[ -MWJZS:-JYH.%LR6[0K;F>]M"4>.@]H?'X]:-9B&EO'K0&#!-`'Q%/#!P:VQ;: -MU;!%NVXY;A;A`M'@J%U;]^^7;PK8@5ZZG -M"\$"XBXAQKS`D$$Y6IZ+9L$(`BI4\H>5HI/+W';YO;_5GZWU]SXGW3IM6BV. -MA+X7,+JEAM>S%,+T#>J\ZJ(6`&"(K)#8DET*5D*EJHN@)@S%6'@LB<=Q-CV< -M9O[_\C?^M_@]A_DD(;W^'EX/3+=NWAJZ:LEE#52R+F9JHB!D!F(1@,51DF89 -ML*4$"@6`YE.:"0H0,O7,O7];OGP^Q8AN]3\DG.665-)DER23*"":#"&F=W:6 -M9IG=,.S$%E)MQ'@B:@RUC@!P'4AA4+A/#.@PY,3.+'!G,A`X:)Z.W1_:R?6^ -M/A^B.\.YAZ6&S245550/%G+'Q&.O$\XYQ)XEU?'UB!H#Q892FQ!*HA:#25(9 -M994J;^4XS)JIQYZ.[I/-JU>;L>F&3\;\3?7YKKQ/+U![MXNW++%PMW&>Y`0D -M)!<&=!:@SS41IV+0#L`+%).I(,0FD>P2LLV.>71:6I^AJB_![$F:+J]7:CDC -MD(H1R00.!([C.,))),&Y&J:!W`[H##+%<-UE,IC[EFM0$VI+"P:DPIG1@^T^ -MJ\=R.WL7$;"'!Y%$.I"*1VDGA_`?SFLUFSPKM3T-FD*@H;(4RA(;,#9 -M84BR%2,8I#9BJ39V:H*ZLX=DV=7PML>]_*YO1[1MF_A-S?Q*)B"K,8.+02B& -M+`&&*2HPE28I!I#%PQ5%](ZVBX7T3W#7WT^O^G^57L:].E*HY]O/ESNWFC'GS(RH<1H38!0$U`:8$I@FD<&=F+A5A,@8K1,AV+1%0AP:`Z%,#S -M7Y^ILH2HZB]QP^'\CR^]DEEDRS#K*G>>>=W=)IB8.`L,+P%FS,%X)$02/!R$ -MWE,PDTS2J9H;%81BG6OS=[?HW_.ZVK_6_\^O^5N^'=N]E];=WGV%>O":[2CR -M%>[=N)VAVE4O:">50F![4.-0:UA2NA[2(\C)'E[=9VGSK+#R'D\??]#P]_A] -M<0JB;8[=1FW!2;<@]5H#;(BC(;9%),=)CVZ`6>[MTNDZQ:*-%\0ZV3!?KOL' -MA[511Q6)&D,W1N#<;@I-U!1&W`=R153,4)B,SMC)3"D[-V9MUV'I]C[;[GN[ -M>GU_#AY'1FM:RCIZ;,/7Z8]>RI.D=`TA$"=*!0$2'3`DI3EC"=(=,Y4A@69E -M#;-S*80X(0ROV,]T@I0%F>8A\4^/['C\?UWQOOA\TX3J^=[GFR\<1XR]JS/' -MX\#Q^,\9XPU/V?C1R#QK3*2%B2S(:IJ2A)0"PU!JRF@\/9[>YMG5V?0*.J=4 -M39LU):+$9U06*;+5JD+2'50*!"&R0LBQHJ`5#9#9.(VN'A-.K5Y/KNYZ_Q#B -M_2?*]ST]I],XG&X''3@8\`<4XV;,30IQ6XXG&<4I]_,B!T''#%X\L -MG8-%#, -M:@?M$*-XN\"MX5O-V_?1ORW-JC[>'CO?28:=GOTWRNGVCZ6'T/5_#'R#L]CU?-/0:=QXG=F9NL@S=E@1 -MNS*#!;=@;A"J0P'=A@.)MW;C=J/B;MQN\7Z'T;SD-LW-L]_[C-@))YLF/+A$ -M*IREZ2D8B9A)(&I9AY`PD>:ERQ.99''2<9V!SN9AT'/P5R-\S9I9M2_$2)F% -MK(&1?4HI94AB6M(]\FDFA`F8UF.Q`B.!(PL0)P#=..L`X-$H::99XXTPE*3% -MZ_1J4G7U*L'=>UP8.N<%JBIBJ:A4.J'4/%"8#D)L@,I@I`Y#J=1J$*`2H"BA -MWH>!0034$RG)YY\M&KAK82(B%$6[DD,PW1%!NC]%%$=QV=D9G9J.S69V)V02G -ML&8)V`'7&0.)%I`-B7T6M*L+IOIT\^[W<,*VC:-J;2S:-H-DE#0GA$I\,F$# -M@>&$PDJE?CU[.SQ:K_3W^K\7)9[^SLFRK(6=NFI% -M44,I,D!@9-"LA1!9,F%,A02DPE\04AS&(.>S6/,\3WYZ.'?]C^5ZNF/>ZNH[ -MO27P1=YV=C2=G9F8TT=@/7AB!@!V9@&(TP10GL#[$=CAWSE[!EHVY>?WN_M\ -MW[?HHI34SO0]`4!0[NT[!0AJ&9D@*&`2!J&)T,)4.BB>=(WM -MC"074X>#'P1UU%%%?C:-1D<84FU`B#9`[5:VP$@;8)R"MI''?@)G:33:27@E -M,V*PF':/:[7F'5-_?V+$S[CS\.!Y_B5QQN/"XV&5)Q7A. -MR'B)PCC:8#H1JD8B,P324)QJ@Z*'C<>.LM;?Z'K'9[_/HT:+F*7:I#$PPHQM -M:J7"JA6%J,4(=63B/7!Z\]$;"I=B4]<&69@F`QG)TP5S2KK2R"LY2)6MK(?5 -M_`^%>EEM)I`DM(BE4JU!@&\[)#?&R'!**!6(7>"1B<8K`)DS2!+*[I,X26I2 -M5\`1YD2/U+>\<9=TWS;XLP-#H=`:`T(H"30P*40E%TFBIT2!@FNC,M5`8!9A -MG98Y=F7EA@THPCC2+I<-N]-BFVR;S_!W35*2B_1?TPI*54D.U"(XWB?$63B> -M*;,4P79(85`&(9#5!4BM!V(#4U.R9W03U4E)=IH[L_8KV+$5?%&8B!M#;Z^W -M&-N&+B-#M!:3:!6V*`(0D@SW42(V=KHDT1BED5V]!Y9I;LO%K<&_RX<.'-BS -M7;A=OJO-Z#ROC\KY4P"E\H^2!\MJ!Q/+)080F+,)L"O+EF$'EC3E'E\KY#T= -MN9XO%\O^/EC[IEUY"6/3FU-6,9HV&2`C9@2",8C3,($(-4&@N8'$>7L[7QO@ -M_!,WT,PS.JKF%06/H5*51(;,$8L1`L0ALDV4A20J.=0&;5JA0YT[66YGT8_> -M>UOF]OX8"WQ,;8VCB@ABLK9B'6E!U%UFL`-*Z)"=1T]4#MO"?O]S-KZNOB>FJFI%105"35`5#,Z!U0,B3*JIA%("`LG`JAP91][FYRW+N]PJ&R/W?N7?ZURY[OO79['RH._;M-M-=^6$;B(W.Z('=F#@!NS!,$HW5& -M29FW6C=`6XV?)WWHST7HS@=]&7.0YR'?W?#^IWO1M'+M7,=TLU6K>\D0$BV[ -MN6RVG3.>.D]KQZT&*Y!H#4^.1E/'!E11@!;3""TK:<=EC+@0>FYM4[NWL<(D -M19(H\.31EHPW&49CB[LQ<4H=PKN1R-VL!V2$FD2"0C"/$^.52CUL9R -M[\K\,_6V?8_A>7O;V"S9JWH6L].DJ.FDTR:4C!RHI-(&ED$!=A"4,"@AI0I) -M41%)IFEJJB+F<>>FDK/O>KZ?XO9YSW._.PW[]V[:;C=6Z"MSNB$-UNLD)"W8 -M@Z&D<2C<5N*=V;MV[N+=N[[W#O\_VWU?>UM8GIN[\**A5((D544,E0P]#IFH -M3"$:"!V8H#("S$<4Y60YPUF$$5'(Y&XWW:M^4XNL;'6S38,2DCEDDP2X1Y@4 -MT$TR$,3`),PTJE`G`:2DWA54[^&_/: -MM6IRW&_VN'M7O9GO\MG+T_8^GS\F_LH[#KZNO'*,&"NO,#$ZX**`E.N,D<%R -M7J'KDEZ]=.!U7P&CKT;5O9ZZ-$]/U]?P]8O2<1=DT[UYDB<=VG@B<0F2)F%) -MQAD*WYB&"4<;(,`B.)7;QCB4+65+TE_4LC\'A]SI7C(\=C1B'B,Z#Q$0T2<0 -M:#,R3J"%>P"D*0[$J@["*@[-=G1I"KZ)?1QUTFHTXZ#&[>U2K4 -M5+X5)0*"DPD610+`7O04142I=ED*+E8E#A=J] -M%WM%5O*&\19L4J@:0W4FV+,B1P3LW80&62XD1HD'=/&\CH)&DP/>O]G?J]?3 -M.#M12J5WE4I(T$ -MO+9Q=_:DX)!0;HJ<7/B=Z7SJ$J!S$4I@1R`.8%`IEX*;$:45@PQ1HVL]EAM%ITE+MC)'$*:-LA(I! -M&$8J[@\<5:Q'A>0>&O'N'F:NJ2;UV]J6DIQ5^J9(M..,C:.*-1NXY&$8Q2;5VSDY#`4+0F -MU`VP$!V"2C:VA*,MRJ.0P+=VL1W.6V1S3#S2RRJ4E4J3L*44YF.^2#?8VZ34 -M4++0IO"ET--.\WY8;H,WQ&ZV:]K*+A=![A[GCW?H.X][?>7K+MRXL,2XQ27( -MI%"%X"S%"Y%`4H0$)A>(+-P,>IJV^+7PGNGI]WDT7Y#DU<0\1Y3><.#OUAPW -M[*.`.`7#!]B5P%RR0Q8F3,E,X"9Y(3#JZ.IT(U\E*>Q`""`0,ZL/7.EB1?.EPYN+E+N_?[=Z]=)&E=Y -M;LG!X!PF#?OA#A++5)9B2IOA.`-2;\ZRQ0X3A/2[7A[S[>[X^4W>7><9BF+4 -M*Q#:MA:K$ZHZNO#0FO!K2NA"ET]5UR%`0U#&6F-<5!Q:#I$Z=WR^#OZL,W3A -M>Y>]ZHPJ@L799DHEV2#)%4;T>YR=RO/K0:S;ZVX< -M/%A2XTN.*8T&&-+#B=<9+@-'39`/6!J,@ZR$^U,S!:$H92D-0TLE'L!Z -MQ>MAJU)H(GUHD*%]8;9A\>U(&"GK#]F'K7K?:>MF9LQS#CQFPDEB.SAQ=KN; -MG%:M;F6Y;MA;II*6HH=J0$F'8I=V'&&*6!T,%*I!TU%-%-%#VF@$*JLK(JX> -MWR=B68FPSF&4$)*4G@Y,$PD$KIQD!,[H09`P$0+P1VPO`'@[,,.#T.\X%;22 -M7J[F%^0V_*E+$A'(6[DI+,TR221+,FE0$PR3$TP!2;YV0ZAQ#A-#&RM_#,R# -MR<=W#P[W6Y.R8F[C>L+LNWBW-QN1$EV,(WJ`A%D-@A>&+3>FE"]38*B51@'! -MCP8+PZ/#V.]YVOU>3DQ;>9[-ZBFJF#JH2*G14DJL^`[-4[L5)F@TR01L,819 -M"DC)E5`B!9(6)E64&$!`J80@NWK5IK0M;6N'%FB]MHM?B?I5])AW=QM&8>KI -MP<0H0^R"BCJ"Z>AUU9!J;/7^3TFP[AXCU.][7LNG2XUS&6C/&KV-CIS:O'[7'R<$\'!6OO^IISSZAQ -M]=R3(TLV(4AF&8"A,LZC+KV<0-@&0%`0>S,>SD'H[\##9KQZPU.KTLWIU:N] -MO[VO=Q*;IHN8J"PN!BA+)+DLP62)%-BJEF2\4QO+U11>]ZKJ8%SBUE^V>CV^ -MO?%QV\<>O2I4O30,<;V$)/.)"S")%DNQ:8%03:HC%+MS%P/1-M];GU\_N?=O -MI?)[?+]N.GEZ3@/!EGEV3,SS0SI,Z0I4S'IRST^]/5R\WAZ?2WC#6G5O,;I,1;-K0HA=NEF%D$A9DB!YU4I* -M%A<,871V+6O>[YV/%K^&?/_'[^G/+`PF$<4*(F-1),80Q0$`PM03%DL%("D2 -M8CAMEDW*J8XY<"Y2]]'9[7E^Y]S@WNX]SSAY_E<3E%RM-N\/3Y>I8-+1TC2$C11I#LD-I)+IM2FE>HLE3`(NDFD -MDHI.ANDH3!:VZW-O2,CM7<[?O=WE\L\E^SK;INO4561:]H. -M--.K5*SU1-0B:5`U*$4U)$FG502U.,(&@F$R06W1;M,ZP-KW,2(/6850&20<6RY'+9OX% -MW\.[._O*39R]/(9.UYOKU&GO;.S/+.\Z<<@E.)A,$Z9(81"R'&$9#C+D&)OB -M/?LGB\BX]%=V'9 -MDA)IYT(E,I)+,^7?Y>2[VXRN:1L5_=S6:?-GL9V-LS9&P'LF0NLUFA.Q#80& -M1*`NAH2%$4-"6==]!KW9T;V]T6X^@[AXGA-/3;3=V/7M=VLPW6X#H1W9C0%! -MB[BS=FY=34ZG4-4LX5EGIJLC-3,, -MZI2I)FD9#-(1A'@SL2E#.G'=#50(/!D69Z<.&K!O:I:Y)Y8XT16(E@>.-HU# -MMU@8.W;CJ-2]$CB)L@90VPP;5&F<3)\`21R.\F"(\/L6-B+>NO%92C(FC0B- -M((FZMNI=ILP3%-L(2!2&V!A?!F68B8TG=HV2(V49''H&1HZM:6S[SYE_!EQ9 -MB\KVQ*TJ!22N6(;Y,C!-X1OEP`S,6AR&@88)20)22,5C8BZ]:O>KV+%LM -MW'N$5B)E!X*P,Q8$ZO8]CLV"FAU(RQ;'LUFK3V9KQ^38;KQ^2.'!>[W_-\KI -MW[F.Z,N7#07`T--:**)*A-"+`$#&J4"F1DO12E!L-F:#`QNZ+%%]C1K5L^!B -M.38WEZ_ON]Z/GO/ZG:S;FYL[ES*9+H[O=+MVZ@97+KH,'QR0ODO+(4LIY81A -MU&&8$4>5"9U=:RPKCJU;M]-]DW;TDLFL9IL>:AZTY.MBB`XZH;D\GE$W)Y1` -M3R6!*Y%;7I=3KWJY=(BS:%(6;1-*TS5G9PD4Q -M!5H,X;))3?(4,!NM^8#(4#2&+OG$2J`SDC8732P@^&3"_%-Q=[W$]<.D:)HM -MHH0:+,:)*R'3(9#B'3"$O2GM)TDW5F=/KGAZ?:T'PZ6[BR9-6HJ:J-Q$1%&X -M[$97K0:`"591IDS1K*@<1VUF/L2AH#XL?%@,#;09S#=""NTBYKJ-Z:V"MPQ& -M]O6]0L11#Q(2NMHM$D$:9$8&ZD4!@*!NDEV$$#=9NL*8D-TW1&;NDOEUST=> -MX;2FV[4V6SM(J6VD&*;0&T)3(5`#902;(@A6U0L-FIM*;4KK:LY]&A$M[M9. -MQU/6Z??\GP>AEWZ>79M6K]J^)%M[5M-2BV-;9.G":8A%!A-40+,*"=29I$>I -M1J.!S-1J-<=8-A;'?-3NYI8S(5Y(FBC=AVC0FC`KI(83,[9R3HDU#"[0S$`P -M=GD)C?/`<)WQB/ -M`E"`T30,!P(,G@F:#.[RIB#30K.#LE-=N2%R@GVJ?7W\9TM)%P\?2'0=2'5& -MR-0Z'J"&1V0E)#U&8N2P@TDYI#FD8:XA>/O^Z]YV>3O[&S@X2XKUV%X=%UY& -MEEK6H-6\B"MY$AO@LUOV!L##AT7ICM?-K -MJ0()SBPWRK"C?/`4%_S-2FE3$LSN[*4F@F:-3(0VL*-D;5=Q`=&L4-">`LER -M$@=\P*5#(E2$I3`387=R,DCDPMYO&W7.P98R3+>DD16XRK<;G=MQU.D-T&2X -MH4[(6`(02$2DA)6@[OW)'7"OI.]<"#U=>)#C-($-' -MQH!A>N:2E)-%)W*Z"%=W:"1!(M6[<Y#DG.'+6LUNN4ZU02"ZXBJ"CY@FC+S0Z$,)-0Y+`'FD)\V92FLUJ<.OL' -MGZ\O;W>,\7:[FY?$QQQIF`LQIH2&*,2#)Z#%BA$)3),8&*.PF)CA,3`R6!TG -M?6T\AC@NW,Y>2X\^9AF6%+R -M8"$YSSD.4II71%`LO.B6'EG1HV0;-5S/0'EO0>^?H^]N]OHY./T=\IPQ2Z71 -M,=BB\ED278%"2B1$)C)<8L(R&-5+B4R47"_43CJY3=8'OZV;'P][T?4]3V-[ -M?T\<^/&U.)Z1ZG!-4#U.PBH"IE(P,A@)#)%)3(5"9"(U14C,N"IE:THR%4[O -M@^%VL=''HQ,3%QQQG->K72T+K5AL4+:`WIH:`2%UC)&!=A%(D+HI!`NA25+H -MP2]`\VM->LX=?I^GX?:Z.CWO[V;OT]+;-G%CVML@/=K.[7;;%UU=>Z7;HURZ -M"2'=V+KXX!Y925\L'VLC`%+28/EK,*P+B=0NU@NM=%L[5O3O*Z=;COKDS=VXAQJI@>)-`P(;UXYB&+Q>)QH]^"8G((U#9U#'U>M%BCQ0>*.,2L!%$XXS. -MS$43.DDPR8&<&$$-`D2*:$B;DT=GLF'P^/#V/,?<##`<,\7$QF>/4L6ES8D+ -ME502ZL(D"XJ0$A+@6$R$]F%@B51&T:(JI.KK]O\[S?>[GO:V;=TZJ=RBEJ6I -MH#EPUSV#L4YP%+*=%1$#('T^Q[ -M[T3;-AF*N:JD#/1`R`RF2Y5*A41F0Q@*"PR@+UTDR(4,4`23@.YG0AT63,TN -M?K<_1US]__O?7[5KXW/2-$T:)?1!00#0*D$DT(BB`P*ZB2(.SH9!$-62KQ.A -M%@Y%CBWCN8ML_E%KM]HY/T7RS/5YO5SN8#F_.W<):X:SVNYQ']XYEOO]4[-K8M54U,J2@*$U+` -M[)W&*44ZS"@)3G$0)"SQ\9Q>E^/II>3'&-DCTW1 -M*2A*DTJ$U9$H[EWQ2$`6_'(`P4W5OL6MELM!6R.XY#'D%,1[6/3VIINM\'M5 -M_PSSO<9#U=,T[74JM<\M-:69T)O&'M>T.>UEK,B!,CVB$H2!*4>:%I#(!U:+ -M6FF?4+E8S0<@)75;3U/4X>=UBH\&>?!DY=O/(>O#@!F3*`,F#"<" -M*!P6HI(%B!DL6,A$DL'`!E5%&67`=O-[QWN?7Z=[P_;'^MUR>?J4ST\F["BR -MZJ*K)G)&JK3LK36F23)K0!>T>UFM0L)HD-D!@'M&S$P/:G),]I&:1TG`?\B_ -M#?AG?^)NUO&]NM;SO;"7EZ2]Z2E%Q"7&"D$DO54R*`P+F#0*1DJ#N5Q)J]^Q -M=MG>][H'9Y9HIGZQ8W(BU%;MRRC2LTHA,2DDANB(20W;\I&`RI"6\>'`Q-I? -M%G)X(2XTA(*6_N;9M'5ZF_A]+V7O7Y9>;(T1&A$9&-'$XSC1IEE#1$04%*0O8 -M42$.V2VE$3*)U&W34FOP%92S;$^*CZ7USR.O\/L[7?T-G0VGWQ<4,HFI%@D.(03B8AQ4TJ'$CPTG8.QPZ?C?E3Z -M_ZDNSY_NFT]W;V.LVEM3LVA=ED&MJ[&0A=I020#M*)2E@=M[$N;8C069M#Y& -M2>.+36,\SX7F:_K=7R]AZ_JUO>OW.(ZGJ\5;X6Y#F&VC*9S(ZDT,H4F_O`-]V11)-$9`9"XBA$DT#'0,.6"P6$E8;>-D7)ZLG!*?5 -M^ELEGW?Z8K?[\_ZG#!<4%]QMYOX/1[//Q>0UTZYQ=R+PZ]8:Y#7$@L!8)#7$ -M#4&*^:(BD(1\R^;Y.:\&!I-AGF\W=LSY/G7'R6S!/*R]48IY8@A(3R20'ELC@9L;FX- -MP@QBH."A#CA!P2@1<)"RASENG2ON,!Q^AC(3W8&4A)^C//5EF,7M?:O/[LA\'@X.PFE2)=^932\V8'9E,Z9/F$?'D@#@Q10[)3%=Q&0F -M(%!,)G0.$Q,!O%A1R2%.WV.WER_47+!Q,BB>1WGH>>M8<]G/[CT'V_MF?/_4_OOW -M-]?/IY;MWR'>;]YO@S`WVYH#%=VLP8)`MV4TY(&#^/.ZU#O#1OMYM=>_PK>3 -M9%V3WZJF/M38F1(&9F/(PT6K9 -M)B&^Z(AY&N,4)$*U:?L^8N9O]R[Q=?M<>UMP16(GW6>/64 -MD<4@%4RNZAIDD:-U5&Z,7"6`D@FL($TC.[B3<+1+6#64%8DUMZ7XWRN/ZAC_ -M$K<_P_4^SSGYO.SNO]-?`Y_0_2^S[[P:]YB_>:^=&Z%Y)7B\RO.DF9KB$DDP -M23AMQ!9CR4=2T*).H(`S64AQ37SO/SEG`N=WSVOP_R?E_,A?SOZ?TK3\S.K5 -MN9S.IZONKO`6^FBU::S9W!QVM%2;2V&B66`78(Y,"I`J(Q",CG0B9`64BET6 -M0LJH[9NEE[6;=?=6X=KZ'T^]_M\D]SUU6K>A>YF;WOK=IWYS>)\;>P>,K^ZV -M&_@;!A@-?AA0L,`%A3NS&%DWF@<0\]LUK12,)L)/^F<3O)([I[PPG*`!@XF< -MS1Z>;9,S80K*?Y?)8UO+]U$RJ:+E(3(>8J-34IAI2DT,QYI/-#2"9)#H@,L0 -M/$5XLR3KG$#X\)L_#L`$,%E`BR9\'++.GLAN)6FS=Z7TZJH?SOW'X7S?Q=WY -MSZ&A^9\W._7_CZ-;@YU;0S_O?D^)H>=P8/.;"8,`L`Q@#!:08`>"9BZ24A`U -MTRLAY6"?)%Y/+!Y?T?J^Z/5W9]S_&][DY?]O=M7.'YGC^+^)E)20T:)#MCUO -MD9*T<3I%Q<0XPH1N0C*3T2![:VHQ3VX38&;,]NT)D21('MQ1[>&4TDJY,FJ/->7D7W>;F^B\BOE>`<[A7LU:R[O3-Z-%FD!O#F#3?N4DWL -M0JZ`^J,SUO&)79@XJKDY*SC%!R<`Y.0/'9#C(=6"*,`XT4C).L"*?BF%=#25 -MMB+=S30?'OM3BO?&;,H21HTG? -M$1G,@XF%HZ1I^-,Z!U/68=XP^[!V,:9I*W[I17!U1X9;3JN9'Y+:%\/\R_HO -M6V(0SNAT.9G\_Q.=@PX7\3R/6.)L38C2383$R98G0"1B=#RG?+1DAB!WQ7?+ -MDC"&40R.9@1@H/@]3ZC[2E]54!ZE]50DC[_JS/PMFE1(48;;30PGE%%"=?@PM -MU%@L!DIJQ3WL457XVUUGV:DI])L.PU&12:I2OU)R>OT8WI4YZ1=G=X?9LDO: -MWJXXS#_;.O,:18>$*LMKB:##2S,:RLLZ$*3>R;?HYGMT:O[+;^!_$?@?.!@SLME -ME:CC/#<\M9AQXQAYCX7:BS?J7/[1,R^P0"7P3.74$F&AU.\M<_P/=&PX\V%` -MYY56;R<0JMXU;+X)%QH94;'PCA4ZRJ:V2P5]$RQ+`IL3E`LS9L7K'E,KM/E`XYY"\H\IOA -MI^$'X:\D`9.14$2A\,I(_>990,0$H86IP.,>YZCSRJ@E!E"A1M%#GH,P^<]- -M]^NQN\BMKF>GUEI]YOD86)25S9RJK%S]]QZ^\8/S(SZ;%)?18KRVTJ21A4V+ -MJ3/#.64OG;6!D9T3FW,AP\(L/3.FJNF -MQT_&882=#.R$;SM@)JU/XF.+LAP&^<&?5C']/!]`Q]5F/S_G^%;U2"//6FI= -M#TBXF\S%[9UTQCN>U*0A;_R9^71"DS$SD(<(DJ53_YF7O6*VNVWO64NMAL!X -M[:+60#:TH#[7UD>;K'@-RZLKRVA95MR:E^+S+,X$KM=,&=SQXV3'#Q#-MIG7$GW'@F@NLF<8WD&RX/!SK\IJ'WWP.8+S>P9G'0[LLWQ<]GA"YO)2,B@T5E";(XDXC@^W\"ZA4= -M_*FR5:S3UC)*'N@U5%T6]*Y/H#%Z,:I+C`SKBVRJ3$P\)5R4Y`[-Y;&40[&2 -M`Y>18#,NMB6,=V)9:&IN;$BRP!I6&`^=GE\?C/5\1X8=92'B62>PB,1!).YX -M*+,I4D80\]@@HA$D\#3*$9X/!/D8>'U_#ZGK^`G]MFI/PH]O1J*!E8>]MY7< -M^;?2L5JM%M$` -MUIEBVI5O;M'1<5M*DADH\GW+-6Q)65H:EL-+HE=FI?6`Y+OSQ[';Z_/S^][W -MO>3+M^,^K[)PP\3Y)-;Y!&/DZA`5&**+!)/(J)^QK]8DM`\"##V5#V3OD\IY -M?9]XXS9IBCHB/]AFKP>(]T-C#(>-V=_#^R6#U0QGO,XY7L:NK\>A2[;]5]LY -MK'%L-3RJD,OVP]$N+0/SYS)F6C[+_<;F)F56U)--:PZ34&O-:^WN#>QI,TNH -MW=68'!J:41\,+EQ=GR>3C\?C\6Z3PU7AW=@GL69&54HD\"(BD8$?G8?/B8*$ -MA;,^59#`8_//G:_WOH?0=G#Y_Y7SC]KF%:V!J#N#%!M6^I$,=U-[MJP_ZS)S -M;=M'2=Z(^+3P$_<&ZNP/4&\5F^)F<5]TY9Y\;!*(4T*9*F^O1,OO01REU(M; -MGD$-I4@P?"BC3T*HJ(QDE==T*6GF4_@+[&TL"H_Z9NG"=CD4S?1)Y0QWQ?$:2NUZA0+'[Q -M2WE,5H[QJ-SV#H0KK1I(_!U>5\PC6JA;Z:NONJ@HCNL31S5E.VK//!>S43^^ -MR:G/V=^_ENZ.YXVV48/&K_>GP^B&AKU=?L[:#L]ANR;-9,=E*6^['Q`!3SWS -M*/FJI!`*9OI0L(OF&#-@^<>[)W]R*"%.-(VF@DS6!X'OZ:_W%KHAB:OA969S -M43C,9<4%@/VOPR9?>0.73_BPZJ<*RU'6/5:.F6N`O^-2-5.QYYEKO0*M\U):WF"?[[.^=I&L+ZP)EMG? -MLWAK<7Q7#4M1#$_C^$CLMGO,T%^!SOL]Z3I_4WL._N^-9D-?>U]K`W[?ZQT] -MDN7/(/Y/N3TDN%JU@(>@_F'H+5@?-^L?5UAB'WTS40A]68AOWN9!3(E2-T1` -M1!"I!HN\!%(-D&,\/$&(C^O$&MD/M\4'X3XG_16SOGH`=);1T?75?BY]_:6& -M+FT%^Z7W?9-_TK0D!];^AB(/R$=CHKZG26/T]%JW+P:6,Y;4V!Z9H".;EJ:?'Z((@VO%,U>X;6FAG:!56R'M;Z-1.7$=[KMNMM?QEYO]G -M.23COHYBZ8K$`\0'9#&B?I%>6P.^/OR^;@&6D7/T631$10L+]]'U/O=:#IC! -M?D=&!F&5[MEY(Q-@_4_A,^'9L#'\/9GT#][F4@2E(Q^+B#_N+0>),Y^3&RNR -MTEOQ!36APMM+HG37+@P^']\4WP-Q\M3*\%<:K60_' -MX&92+LOS&'>.-V;AT>LW.$9O,.=#??D*RGB_/JMQLG+2YH[&A,/>+7Y1,[M9\0-6^1R -M=\<18SVQ_.37F&$E#9OEL-/`!-=-9Y@^,BLSX6C4\8*6:F5]6+LNCM*+4O5+ -M-!1;X2L+,S([83MX-/ORULE>KRMN+@'R;*MM*JM2LK,B%H1B,=K-(D".BJJ" -MA`2D2E"*D%D9"(A&)`60(L)!@?+3\:C_%6+*[-!@2?&)#(^/!$9!A^0_^-_+ -M)^0EM+4\/&_6S!O;;[7!WNN!>*/;%10]_#V'\1AW2D5J,2S/.;^=,.1A"W2E/8#J -MU<+M<9^A7F3E:$+HXG7J9#2=3J0LK^'$6[*5[%\W_37+S4\TGP7=4;)Q2G!4 -M[H:@D;6!MZYW)/:/N.CKS'X^3<0OU/H=FT.E(=!X[36&2D71[KW@7KJ!/U,/ -MH%%!+43UF'D/=5]8QF:7PH%;RM<'V#K5RA:>6RZ&MBF"J/QNAT-7CIJN.@+8 -MP=DP8+AH8QF@,[57.7F?R/@6)BSPX?I/TVCX:0^]:H_.0/Y=4444"?EIA9?# -M!0$>(R(;J?H[?SS5'0VKOH-CZ$=;4H^*? -M"[MQL#Y9)--Y>*2_R6J21P'OI\#!W_AUJ;7-<5<'2A -M'99R]BXQ^PX;JA;M@*[+''S>!!B('((BA"E*-OZ4`/"X>AI(>M@8]3^9B(3[-?IA(ILD?>:: -MBTU0QTOE3ACA<3-X]WG,T5@84>:!41KR]1H:A"[/B65Y]) -MYA?BS'ZZ!*JD_6R!@CRB#]=GZ_64C/QHJE_SIUX,\%&K']I^PPT'V1)?"QHS -M#6\-%HC11X$-"RLRD7S?.M(?;XF)Q?`@J0L[?4H)>P4FM]7.)WW$*03=!N;& -MH.\SF62KS\5R.\L&6GG1U2KT2?6A-EFIS>HXVO@KWIM+?5&1G;MA.3_ -MKK%.P8PT^S^^"U[O&\.`D@WRQS]Y.Y_3UPXZY"JV1HU+Z;XL/K%_K81C&^KM -M\(-\_\W[Y[A->IF?X*G[C>^P;B(`D\?#86,8L_49PPR4+W<.:RTM!Y"0W;^Z -MKJH?`TSS%NUNX[G$G8@9]G%^K5&4?Y*&PJZ>EQ-9.:JIZ).3QYJ1\^6+2BI6 -MR8A1O)[5D`J)QBK":D8BB&;;H497&3G-9!2MVQFLA<:ZRD9Z/#[9/F.NUAI= -M89JD73,B4>S6E>H%***JG9%]R,!&9-J,=6Y'-`N4G,9P;GNT,5EE!H9$\6=! -M@8OHY^,+^4U1NE#\H*D)$_*JJ_)S*OU__5FF)R[;$3;2&CVW.UE97,#'JXX0 -M:D!6!PV9,;SR]F,&1WFK[,5,Q!C*.JEX1\BQ-W%K2XV5%2^+"]UE\5?Q@.1R -M7AMH?+G?LX#[#Z'RTG\Y0?6>!L?/1S:AJVAK[W^Q_W8&8:AU#:1JD;&=U6=C -M]^8.L[WM?U=@^VZVWMVF9?[([7+#0RR&(-.A'PY/B1LUK?C'+O]+\MS;UC$\ -MC"P9J'55NY$"%/R:-]XIA6)N+#[E\H9VKW/6S,YXQ_YC29"^>*,X:5N1)R?H.=D99V8:*BQ -M&1X$BSEJSC08#0^O&/7UFON42L(?^ESRY6[E)<; -MQ`\22F?CL?53^ZCZ@4VF8EL-DG.+=:F[8A1FIMB?=9J:CJ948&DL5H:."K'9 -MA?"TKWG8@*98\D%\HV?6ZT@%=RLFC/,<5;F9+7.G:CU"KM6>7+(+6.+J--FN -M@:'6[%W`7TT'LV>XUX!XXM9>2Z8I\^"&R5MG;H#^S*"E/Y_\S]9\0V3$`?&C -M]A\M-F@P1TWGD<$]:/RZB*G_LP,@_-_8I^QUAL_5;#7[+#_/VPPX1NCM;^O. -M./FQFA`\C#(/L[G<'I1^WV>P.U?--D5VQYJ!.G_:CNPOP9QHQ?Z!O[+S<%@W -MSR_\K>C>BD21,8<73+]&G9[W>>H+OXPP6'5H,!XQ.TEVW68%@LB9N.`*G'/7 -M',''2,O[HH]LOF3>YUE+>,4SG$!C!M@`J3*TP>F,WZU@5HT2UX#`7+OD%%1S -MGM&G3OJP4F[?#(MN]=GU#"V37 -M>@Y^KD/)A\V6ZC/;7N3@5FZY+/UQ]9M$D1>Z!/`HACQH),ZXT.GQE?`%M&)6*Q -MGB[K)O(/[7^B/1+>'\ORT(FI(X15F$)FJ&7ETPM:O`3(F[C7',=(OA4TU7O5 -M3A9E46C7^0UM%K\P+^V>[!MG*II`;MC^28+)M&_[LU[G+C);'%*-/N-?48%\ -MZ@+$,[T^PNE]K"^G#]27F;OHO1,>,#X6=XV5GS7^U.4M)HQ1A1E8N_M-F'?: -M'#`FF.DO`4:@"&U-96>9`BO^NAJ,^UV;"0Z]_#;K;N4@+SXQ!H"CB4&."_K( -MP4_;0.?W@8T_J8<5*#P2?N>N\A:UW$B/%HGX']HQZ^:8@!I'^8_"U:$A^NK?^FH3;_B$@',-)T7HUOJ -MR'OHLBT-NOL?QBZ* -M2\K*O]D?*2NH\EG+J_VO\,8N>SJMIQ7ZQ$L<*#W,E<<,:_]N?CCY`4SU@;]AZN!@#8 -MST"]\>`6B[#B1_&GND-DZEK[G?FIUOHH_,Y,P1#8>@4RKE7^RD*VPF^"(D^7 -MS2C(K#ZN7:X'D-!I@=ZVG/"X`6:K$^!M*,Z<2-9TJW#@'SA)Y\E;:U1HF`H^?J!4]X'N5?YPS@2Z:\&47!)R -M;`O'Q@BOUO"VV@%U]3LW$3PRK5K^\$!2$FO]U6QO#7#+X^\\:DN*R=\^0SF^K]!&)CR* -MJ9:0"%H8S"ES>QI*V.L'8K%<$@!(K^5?,CO_/@KJT1I7T/C/9]!5N98:,>O@&8L,K$% -MT;,6P14,DMM-#&!/YZ$^2SB?):D2?C$J'SS%A/YWLT?U7^'>H'VQA?H7R*.I -M5DD'O6?PB-NU@E&",KSPR)J,8>ZFO-D^*V]0@IA!R#L: -M=/2Q)OU,.<=Q)[3ES\%G\I00Y<'`O14"#2IKA1IPWLK^:2_O>KJ6,ZQPZN;A -M>\8L[8X[M]@>WDR5X-;MG74S_3FY\94&,;)R5S2Y%%1^[M;@6ERKAVJ3-=S8$MJDF!!N79% -MY;)^J\4OQ^_TU:TFFG/?6/UOU&-QE^-^0]K73^@ACQ&4S -M;U5T\D`Y(\(I4^??LG"[V(?O!:,]?=&@]^MI!- -M<5;CW_8;$X"`@!J#?W5P\N<'&N\PA?-FK+S6WWQ/0FOUL_%]RWOF9KC?39=C -M\4[VEL)C#P.9DEP,'?'UNI>.@6S=],^GF954%[U-%P]YE,KRS`Z;6F$DP+'[ -M]:]'DT?%&JG2:9#99+;O_JOMM9TN9WI\"G432$RQ^',Q+3LF`YR_]]J5#*M* -MCYIOXMDEZRX=.+ENC30F-'OI%BZV6G3(S`$F8BAH>AU6XLDD;;SA%V<&XYXJ -MG[CX\V,FM5N@#'J_W?2&D,.5/1&$W47ENY8^\#DU* -MW^7!6WE=H9KI;VRU>&TT,O0'"[>SQ*C[;T?H9+A9'L5@(]857IY"E -ME!+ZZ[L6P<+G%,F'R:]@=X=7I-Q*&1T^W(F';ZNA)A@+FY&A"Z8G:J6)MZER -M>6667-Y[#9#7=,[BE`:],6]^APSV;K17V8XQNT*!1$;0M2:-V1?J'Y5"Z;*1 -MW)Z*G)G%85!"-,/VYG`*22+'*W_Z9B-56GBC,*BU?EJG,5/17G,K%D.$I4NL -M'B]:5_BNA%-1SK&I>E_#KI!G(("'^^WZF2V=X!U%4Y/6%N37P%'AI*RA+CU1 -MSI7%F^O/G'233ENB&KG=[@TVDF$_Z=AE8YXHJSY&*?BE\@=TM"5`M_QQ*S97 -M)>O+:FMPE[SR52T'!:";XB\^1L?5W^2MWG\#E3VL#?O*YLQN=*'W.)V"[FPB -M"";-H\>8()>Y)$)4\!S,67%8P[WM5;]]?,5M>LGKBF,&9PR&%^A#!%%+3/<* -M+X0XX>.UL3=\6`HL7P06+OOIQ+K0'IX4W50$>0PV1-4#]6']SJP@G[V#/CM2 -M_JF/S?Q<#;L3N4#A98H\*TQ0_WN^>::P?R>Z)Y.ZXF$F[@":SV/;2B#M0\1G -M^S.$[LW1Z:`"AU#\?V_Y0@8[!/J4SF'YJ7M^KSVJM;_]T,,;`X?&M*6A(*.Z -M8_>S&2SWAL^:N[X\)#`J&8?8-(]G@R22Q&=Z-AF"-T<7$=MUENMN;%QX[3#Q!1X.;X?\CI_.SUND2BB2MH&VOM;Q!4MA$D&>E%S;?+"<+A4_H&H.6Y@L_6VO]T&AH?HE:3M$_QV.I -M7CD&A&FY09.`6:_CI!I&W9G@MC<_X@_HO\AKSYF\W85]A'&'\36-&)U -MN\.;4'`<"%XOX\C27J^^/-*^& -M1C9ZP>S(S57HHXR.W7Y%X4M;F2B/Z^ONZ.%)SOH_#]2UBT8$Q4P1AML^*(`P -M3?1#"T^C&/'OSK3_9Y1VRM>9XS>S_W]D-9C,^R]TPJ -MJ_U]_:'P\0@_77#*89PU,(.M^75TGODAXWKTL6( -M43O0)-!Q.UJ/8P6($A8#P&KUA,"XQ4Z(R8,EYJ^;'P2;#W?,_#V=O(@:OR;8 -MT(8?OA&>J%#)F1IG>Y&&ZF=-TKVG,6]I+X>R?!?I$?F:&&!XP(*%IMMUSSE> -M6:08]B85"[?]'_KU)->WH'(,"!I[[O.X++S$$Z(CA4.!Z!=?;TP'7?3*N%)_O!V-O2RY9OTYD]H(&8+B4.'[>/;$/(WR9XMG8"<,2B77@ -MNIH6E_BZ)7<#LR!6=&<@3?`Q-#H,?J2#E&.IKU=I6QY0600$A(=#!G+P=M.S -M*.77NX!J="T;<:)U._N6(OY@#APKYP."LPX`'?,F`D,Y[#`+W)B5`7779$!"8F=YFP]@5ZL\L" -M6(S/CAF"$Y=[;W_*Y'F^M!*?T>#L>F8COJ'O]G&0CQ^(T%FYF`^7Z8![7%TT -M%F?9`\7G8FM1<<7\/L!PJ6Y=_!=#2WR`5+#WB.TBI:7NC;,B]^L_)L40_-_; -M?\.A?!`%(00K7[*``7(II^+9$2`4A$BE115143,55%1(@%04P`T(20[K]`R! -ML*"?ZD_K-G7_9HGUNP?!BQ40156;A?)],0$@,6Y]!P)_9*LU-SVO,_^ -M(./;XT4O]EGOL_.;@DN4W2G\KL$=9A0X`E-LEVGHNYD`'W -M&/U;K3J21`0'%AU-C%?^\Y[=(YG2&2.`]4U\L;`D\IKY!S%;Q[@T,XY&*9"E -MN)*XGE5]]^2DAW_!P'%F#>9(,CD>,5NFJNQ7=![`4%KOW,/B?4AJM=Q-OBAZ,-*@!: -MK/QWF-@ZG'XXZX@VJ:1$038,#*I<](GB*(R&3`@(RVDRD=6.4>"9AW$JA3P)W6R?X(2+O=A -M@3`N0PJIXEW+$IWLL*B(.)!2_\>+;<"M9/-K+";R6&"QO5_"<#[`N- -M6CO$)`/:[\A"]R@Y3U1Y\M,C,@:M\D55@1$"R1'C:?,^=M@I -M@-TS`N%,RGR-E:=UU>*HE]AYM47(;-^>*CWOJJ2;XSFQ9\"4 -M((-2.U];6QH%(DR:I,`:;1#S]5?3#876*V=OK*,W8,@46MC<;5N'W(1QE/X@ -MXF)KS13VED"LJV1.&QO.6][>K'!X.;-%FH73.AHJZ-SLL*/Z\"KU1P"]7+Q: -MFS8&'F/^UIE(HM;;&(W^$.SER>O^WU=KD7J/$(2MLL*M+^"O,BGWO.H"]5OL -M5$V);;U8'([XHC07/E5[?*\O198X0RQ0[;KCV.Q6G/`EILZU[067?=S($/AS -M)@@#MT9!?3'LU)"UE_]#(W\-5F@1U0H\B+Z^,EV//:=I+SIHA'LSB,N9@>R_ -M.'&/`MQ+(%*R>>FSZ)2!>QLTET0C(BE:U7AH_M9WYXA&&YCP?$V3O@J?#B"]TM,Z'F'"#WVJ>\H3!4FP^$T,Z8W/H@XL -MR&*U42.(G[5)@[5+CM#Y8?OY+E:1G@TE#\2&>8^47]9#-G -M@,/[KE1+D/6]6[1>'"Y-3W0-'GFC\XR8Y%.?(0GK!K7^8T1#A9F*(2O+F/=2 -MNAWHF`'Z&YLJFQ)!?_?SXR5CSH%,I3X#:FZS9&R^34`P>T:!NLL<=`NE^,L! -M)4G[-FR&'O^#9-?7!NGUM4;+0.TX!=AXV!S$.!V:IK$G/P9+RIKRX$4=4>O. -M!GC:KP?.`]*(%;9&!S*\0WZ=M#1J=K1''-FX/)PAV_^Z-&N&B8X/REJL['>L -MV!`=O!V0&5F=*P'P'^YYA>%E.'[U>M`QN^OSP/Z,"5A<3^/'WD)6J81UNW+Y -ML%O?S\W^G#RP3RU=G`&/2`L27L=<]<`Z6G+AN@.!L.]XC9&J6"5"GS&HKRZ/Z.+2)R.D2FO%E -M[_4V!L3>[B@4HWOQHK)>CRS\354Z/@*IX#;KGAY@6<9RS0<_N2=%%8Q/=IUW -MN'2'8_%0!4;"YD.@<`ZRYP`.M.XBX;=YD@R1^.V!MP=]%S/FNZTR75+SRIG2 -MTQ"HJ7.U&F;3I4?:4(R)2O\`I,''>P%ONF5O=E[,IJI\V5#1RQ%[-DQ=5CSL -M^I%^63(O1ZN[Z4>3OU(3\L1/SMOO0?D,T!2^;$^Z.0#4C3P9TAZQ#&&1Y\-6 -M@3D&JZ4VJ9X5]GF\"=!>[[>>RFS@W'`F@`S?4(O,,W/+E0?`>-%>#%,AGO\L -M@"AUJU>GBVE[0@"WDD -MI[K)\Z2-\G88%LUR@AS8M:@IC6'31(SW)*XGKHT%Y%$D!L5'UZR4LSI2ZK?T -M:(E1CQR`(ZUT1KX`4 -M%WG30=OSMB&:S<33<\`=*DQKV;+H0440NM<99Y4OA8.>:FZ$'0/3C9D7AJ<<^;8N' -MX%KS;XA'QQPVS/[<&%`":VX.0H^..E2!$4SM;]QHK`I:YMJ-=OB'>.Y -MMR-TW*(O+Y,6H1D8=WQ#40F-WS\<'.]TR`L[=X;EZ*!.'9X$'0:!"7^?B16. -MXQ.,".%]>$7X8QX/`@M&(">,`W)XTA%8*GF&AL!/':@X&G)^X?X%4<=W:=?!"TNG?&TR#(PH`9X>!X\3OYH^QDQ^4; -ME9,Z=R=R`ZT@XY0WAPX`X$IIL(^+_'D4TRXT\.\V5N"`'?\,ZROLG!EZ,1_@ -M#]GL+8$\S3SE-%^-!:D7NMO-8<(#A:#PXKL#\BR?8)L`*JTGSTR>\,FI.GQ1 -M^8>$OG%D34U8K_M5/`$OFS0_3Y?)7L+^RLJ!KT5T0$\SQ[_HD'#I/&!5]];I -MC]KZ]:;,)0`"L3%VZ.XB$@1A::%Q:$&2:0EG6))&0K[>:/I -M1Q!UJ%^ZT9[A7J.@-NQ(ZUB=^@04'C931XJU$<$>A6^UL[[_GRI*`KO]_Y0_ -M4XR7I,[*7E]KJK_*%D0VVFV-L;8VBA;>D>AWVCO/EZ:S1<9R@E8?]_1[K.Q6 -M!6`;3?(LN#B;/].ET_D@F/^N,\CD[CDY^2A2YM-![A._OKRDS)+S- -M'I^FGGP0A%G>_'[=5)/73K][:X,'0"5-U8[EZJG/:I.\_/MN'SAZ9AS>00D1 -M(DR*[G9VT+@?*`^,!4Y.`S5>`7->J5=J55(>ZG8]N8-:2@\#KZ&>'K-] -MM[W2QEP>!!-3,XF\?FWDIV\#<;F*6'0QO>0=(" -M0ZSIW'^)X?!0?PQ\C@(*EET/SX-GS_?TD&*8E#XZSHH2(+:XTSTMP_MTT.JR -M.K;6Z"HY1``5#L7/PZ3'8]8?/8_,=/3'@"5]A+.*1CV?V;NIW&] -M*[2``8MS0:)O)__$>-^F-`MX%G]=ZC(%V:#/ED1QV/GPJPYT$<(!4!!N8QSF -MPI<*M:)_A_WIG@0TFU^W$?RVU)MF%LFTRP:K):V:0`4SW83`H-:3[&<^R -MN>!"'O_#OO-OA.063TC[^V3F]#QDUHB`$WT?.TO4T-$T)D%OO/)\L^`2_YHA -MVF0)''_)?TT?S%B"G7*)2"(..*V7^01SSEKZF;Z2\(A7??Y^*@97!K6):OL\4=E6)R:I0Z"CWG/M:UEI#\@D'(W^U^E@Z[AM]['T_4`&ZL^U9 -MN^HSR+W7#B[Q^!G.;?]NYX@'&Y7NUT]/%HP2Y,:H)VBL$YM/D0V26U_CM"SY -M[CQ1-J,!M.]O50`KE\>_,XUPO]+$Y'S)!@ZE$1#!^GQNOY`0Z0U!$;B]-7*0 -M`ZN9?%/RA(?W&?Y*1CAF)Y_0D"2(R_@U,AMFO+*LUE_G.9VRHD1`6+[@!*G8 -M-6LTG*'@!IVC`"`/NXH[?SG0`P8W<$WY;=S&4B>JH^NIQ:(@$=A3RFZSU3TR -MCI:N2XEODORF<="1#0)\1UBZ>&/W/)#[FYJO:4(`$&XZ3MV0':I[3>[R29DV -M3'M0D"W*.)P5,N'T9/?\#=)(@OL@(OUL]KS)HJSFFX/P0#\VGP`I^S>!S=Z# -M7?B\SG^`)H=X5!M.;O-MDIPZ>!#&T&'<5N(=(*_D?TB%@B&15G'?4FQW]2IH`!GN3>E -M=:J8DP3OOYW,MNW>+X^`K(/M*DLRJ#>[MHGQ$T:XA!!=4O2V$4!%><'SFR@0 -MXJP\.]1D07GJ5P$(`PN:;=/(74ROQ-P?W,)->)U`0'T[F$!'J2TH<6[G(2+S7F'ZNN7]C=[P\A221X"AWT -MY_U:0[1,-127"0=`"\UOL/##L^DOE[56D-EP82X\LP0&%UOSS_%U\@1JJN:B -M-G20`"TOC4BK\6N:%A/;ZGVQ;L,K]^JE)GP^YKO8S:>]2`%4SLQ,+)>[Q[K) -M\[M)PI`#:^M,S-W``BW+0QW6SC]E]NI@N!P03AD,U'5]I4;&#EK>S_*RE$0= -M75L*\X:O!2+L8.&WX\`.;4D)_SR2^A4,$\11U&!*4W%=Z8,?IUQKY("`QE0] -M+IF!"2Y;/09[,?&J/D7&NB+RUN'#]XS@92:5DV#1@#AP(+S>H3J:@1_*QM%, -M%EWCS@(ZT)W/8OFT]"(`/_TB5;[P!\/--HQ7O/)HDMN0@M!`@8/I;A1)Q\'0 -M7'A?."->7=[3'X*WA6R`#1>D%GCZ+(M10U]R]7!-'(Y/Q0$3E5?W -MM]H`9L-]33<1;SFQG=]Z>,D`#+=7CD5`.FNJ'(UQT"9<78`6GW0J"^G??IC( -M">4J=3.KJ4"$1A1`*!SSS'O>ZHG/R^+;6/Z^_H1@B8.)Z"#!#7*=J@]9@SWC -MY%9(\R"US[ZPVZ;63M)&NA!RA5B2A$8`'XYU002QX>;>>W)L!X$N>:>`_NBL -M.4B1BO.^!ZVW?W78TJ(\1*7( -MF>D012>Y;[XX+_JN6U!"P$XM[QI:F>/26/3+H^I]$DD78`%+DS@M>7":?Z/E -M0-;L60)=::9DLB)>QE\`L6#?33Z&=VNPW[=5)!$&:1Z!)76;M<-S-G2&S5/F -M14_\U?=_[8QB@?`@\S4$6%]GUL_<8)&65,01J(BSG.O""G_.]_LG;;')GP4% -M:J_E`N^#F*++HMMP>:Y;QRBQ[THB"7^M7/`#TW[E -MAM3?&42,-3EY$@`?2.E%7P$)U-#.:#-!R7F(1Z)(GM%.+DNE$`U0DHMLBP5.TUT;J'.II@!D(2A -M94I0T'JM<%"1/.[R9#^)\NZ5JD=*1PX_C$43SA:VF20O[Y!(""^922+\:=HV -M3J<+QUUQZ02+6XJ9R9S(R@)E@9,]NG7)W=]QXJ&2`!FH<`?50]2P.0C?JND? -MU=&188`PI,4DV_'>48+^[*!(!^_YB51V!T6[,-EEM7A(06,^0%+A(] -MJY[8Q1;JF2>\%ZX200V'8E`5O%6*6T,&KGCX&$]Z>.4_."$M2=OZ8B7>ME.@ -M*1(M%BNT>&B4V5E&4RCGOD@`51$HC-+45]:EED,?4(05K5Q:.>!<#,;)22%% -M^]I\$O^2EJ`!EWV47;\5-X*+)H`51)R&7\H'OKOHB"-D2/[1$7N;%:_(1+NQ -M/L9O*FHY"F"^='1D01[^DC<1F?L?1*8%O@W&](MWWW2D/4VBBLY`$,A[D9`N -MR`/QMQOA6A/R?.I!P_F_(!!AH05%,=4FMRB -MYGC#-'0,C?S`()(@OMJ:H;*4XKNW9@(B*AO"*FN8%(LOD=#YZ90@/WU;_RM_ -M8:%-_K)CHLNW2@4GJXN^QX`YZA(064OA.]*F#6A!:T@%KG4\G(0GPB( -M@S];2D30M_45-;R64'B'OA4[3D&O^:%O2]2MH@$N^2].0*$R,@'F*KO3UJE='8:(1)!?:-,0<'9$0 -MT'G?][N@NQZE;4<")2C206+O@4/JA#@*262!=[[5&K2%5 -MWVN59(CLOH:\KX'IG300X-!0@!E\]F3B_.Z2>)#D(\ARG:($`Z4#43J?!"?K -MFF:_J;SB(2VF=O#G]UD>3W4!"VR/5!#V\+\1@G>XAG3Y*%CY)SM\4B'6:G-G -MRW".WDY9_:+`#!4U>\X:YT*-Q;$)",BVC_HT@.K8^>SE*1+C-'C$UN6=`_(R -M`D(TB&LQB5J!$]7S_M83O,B+M>*ZX!%CKU$$2+9\\\7P?N#F^,"'EO?(O=56 -MW![SJ6W-$*N>(G=%QW->Y5HYLB`F#GZA)("EE1VMZE(TI^9('S(@5_S2(6'A -MS]FN#(]#O/'0HC"`BVO=G`0U6OBFW/Z.@JN;:2&B2`$+>"%- -MX)1A\239!5UT@JH@%TQ_2',7-?]HYW:[8?S\]-<<-B$O2.OP@!5,*1 -M;6,@7-FFK&-/E[=K_R/$96($!LL]D]#BMFYU^JZ#&>X'2SO?9T>\3""^\PP! -M8S$RMV\!03.LX.]DO7B802@"VEIMIX@6@1C/[:GS,TC>JM"!8ZX@&SC7GU8A -MN91JAI=)1`IN_Q.B0#7190>6/G$A>/E]\F"T3G7]CH=)K/#AO2H"8''))S?B -MG<)DBF0[)%KLORUMBC@A+F+-8=_I$+$_G(SMJV54@K7/H\J^"'5?*F_T*=^I -M'.]\,5OR(?3=/VW9#(2]'J9*MJ0B!0^(C^>.R9`3S=U!GWM>Y!T\!L%@@%I< -MG[Z[IGWJH@Q)[_UY8B#)45W18%;6Z#.UT5DQ4)0!,01B!+:Y<8]6@#E;'-%# -M$A64Z5*`,PSD0/JM7OY^&F4&0B#RF0S]>0*2Z.VVJ1J0\>MX;^1!]:S:#@JA -MUVSW">?Q^;-?D%@$`]1&+OQ`27&QKIC=&[,"2%R29X2*(DBX^N3Y#5ISL2?( -M:NQ<_UQ2(MJW.VP9J<[-KT?I@"XE)UHE4L,CZ-)VT(>-W%RBIHP1/7F>,?]@ -MF.,)N^5"9%(+^.I`>+H3"1M5_IK`D5!""W?LH(@@5?Z"^NFT_#D`P(BR&F[U -MB`*_V^:!O-C%A#LK93!02&:``K*"T8+9D;74T^./JD8Z$1_7^7O\T:X=!$MI -M]!IU77]E.-K.I,H0-E41\7787$.@ABM#':CU(K[-S#G#1Z07VHO\D*K^L60& -M_FOO^L$UR2D578:+-D06K*AN<.G1RM;&BOTJ0A$$2N[N]?$R*)J/EC9O.D1: -MK9X'5-1=ZI#JGQXZO+<\B+RR=?O9_PYJSV+[/&00#0XY^5C?'4?!I2W46D=$ -MX(MBV*OIZ?YS[[FX=4+2XKC44PQ@B;*%^_:,J&(;*/"F&$81P"K]^$"*$R'` -MNZ-)GC`RBR>0`9SHKOFX.,`)>2Z3`2H@PE+SH@#5):^HB;1:(`1HD7=0IDO= -M'3*8ZKJNB"B[`B=MOV4MYN^NA&75TU$0C-;[+.XPE$$!S6GC,*NCQC](^[+Q -M[C$*I-+>8B((?VA%."V6X6;$P8O#`N(9G(.*N"]G@I[_/^=>5TKZ7=,99KIW -MDHF,^QKSX)ZE_NR1J!W/XT@.WU44%G5IEWA\NW_4Y[VF!.9?)@` -MH/1?W^?@?/=E"(F=';L*D<O7,&Y2%2N.MM_)SWER%]2H@":=@QZSN-*%"*1OCY;ZJ-A7+-:J`B#( -M9U56UH,+TV=ZU%"'ZM$FJSD:0HI5GOK_QX`@R.%94^3^%)+;#Y9.JU"[?0:W -MX7M`""(O=553(ZNYT)+MG>P;(@0%&8!,!@`EXPO]'">>%47\GMYU:^+(!^%! -M]:[I_CI%/P8U=15T0?HVQIC;6P:X=3+\"CI\!\_&Y4L!!UK,#O0/%Q,SIU4_.H@" -M"B1%>=6T4D!P1)@B('T&8,>[-W6G/$"$*VFS00@=%Q-110A5WC(_OC'@,-E' -MVW'NTSLOD_+;:SMI6]Z-Y#544T4-3*X*C[F%'F:_:,?7>WK8]!)N]-F-M9+C -M'BE^OM:;$VFQV6$&6P80@7RT=JJ^)'>3'/L_>=7U;33 -M$PY[K;)#P-WI_,SKB=(@]L3WOIEVT=OU]_L@6*,%T$U9O(K`T`/``-FFZ?EO -M^W[*/LK-NG$N<:._G+1;K_1ROO=BD$`'9-V@]9]C\)A-$V*!>]:#WS^C\B'[TLBW0&38H``"*E\]\EL%DN5PAHV.P4H;]VD0Q- -M[>U.*60`YV^^;MT,/6C@37+I&L;4`LI.3J>)V*VR:";D4$0ZJ_%P1NCX(KRT[&SOUN"RY*SU.,6G[*)B<"! -M3]#\:?1&\U/?!J9\:D;D0`"2R[FR_:9RF:G0]V4E=9WZ^+XD`-+SW5V4"-GZ -M^26[/5SR(`57$Y-OWTXG:>LW8QLXW-+ -M.%5_ZGON1TEP@13&6N''D2*LX[/./W^FEU/B<_7I!`!LF9'&#>3/?0]]GV1O(0@BM&>Y\[S& -M*X$#7I! -M^&G/\%YU;:VU743Y9$0*\WD!\./25O0S8&#Q:I[U';?J9`NG472C;`.@14E -M6Q*T7,^>4\B,B%[9\:J_]XG"D`NG2;9ZR\:?-37LX9&"*]EK\"P:'^G5A,FF -M?L.N[#;HR(I^UFMST;=<]J]H"$VT];C=%J>IH4OSY4C(*B4")8>\&>5H:5TF -M[2S&]DH-`0)(,OZ9&CJLN:Y*4/!CCP(9_MMP`5GFQTB]LX;K]J#'-6_W=+'] -M2DLB+Y[=XQ!`RP8_P\L<-1U\BY]]"0+G$!N(8<%;6V[7^'K[O1(@15VAC5+- -MD6J3(ZZGZ21:.8ZLLX@XJ>$"4"`E7OY\7@EQ6)^>W++?#=L2&S>5XT+$.LZB!"D] -M;U@;WFBWE=;=S[JKXJ>1;6RDD@!2&S[^@K2*%5-![7BW3*X@5,``.[J)VWG)*HB9BVRG-G;Y-]W&6R(=R5QV)VI+$PN;A@OLR-$WKF.;YS -M!7SZ,@/;=Z3<\@%R>-C^(__YTC>7ZKA62"(;X]Q[W[4'EYY%EDU-K@_9/+]? -M7*\-8S"2"$W'R06=`DK.=1.AX@,M64!!>A^N'+*NYT$*529ONZ$!M;M<[F,Q -MF@55S>6K)GUP9^ZJ;^SX-MH>K8+X!";\7=T7-(4]749F]V&1B[&AIT8`BK\B -M6U5PJL="23=Q*V];'9KS;4]+!$7?]_'!-WS?6A6Q:$M9XK468+%E];_H4G-@ -M$B(B&\<+KQ(R%9P7E=2>9JTF!NEA$`2)RFXH@:>L_*Q\Y1P%#KO-2)!$3Y]3 -M?:(N;Q<>F_3>L.7U/!@<*120![:6,(A8P?$Q)-_ZSNT\IXTK8)<+OUD4PB'? -MS&8N@\)C.SNS\VHRFYF;S7LTZ4`!S>-_J`%JDY -M*V6"`$U[K\`7#1W=$&VW>L&WB+>L>_W500TSGI`!HVN>A5G\6U?51VHEDT:4 -M`*'3(NWA\39LR5J;-_@ZS@6R2"'Z]48""6XID;//*:QOC]UDT9O7T50M)@(= -M[9S)`9UY']H9/G[^2QE1GN\SUJ40$LN8.IIR%NO^%TXUKTL=CG!D1`!3YGJ[ -M7RZG!!?AMIK-9:0:YRWPNDC!6VK!,R5S#)';&/5=PC!#10VW:2(1^XOQFL_GZ#I47O]2(B^=3]^N1=WE -M0G!XN%I-WUTWP]).200G7^04_N.FA>N<3;N=Y!?#SH2(O>DTE:[+Q%4O#-OM -MZNZ%A=[G]DI!4XK"`VIO^=K -M2C]S,_^)""$S(Z&;(!ZFF1DT"PL.$OQ>TB)A8."1!Z/W-5MIH]^X:OVWWF>K)+(# -M4XOK)``IF//,:=LY_L-Z-;]NY8^1I^Z^"S7D\@!;09`=."=D_CQ\W#-[W7T* -M00W](0'I12GI6[Z4K.+D/"M0G62?4C(A_/7\'A>2B+S35FEK7"SS<8R]Z/3@ -M\==52`AHK\R&-[K5#S4;>V4+VQ[D9`?RT2M7R0+@,V\N:*2812*JF0&\4/U- -M$/;,9^ERFQRE#%IDXQ=7<,:,$(8]G0`D^Q@6&RDN^(T9_D/5(DD!"*6)-D!] -MA:].>?VHT2L--GG$E/A*I`>=YY$_HB(2HYD#DJ#&GU;>HNJ9Z[L'R`8?QW5B5(#(I1;- -M42Z2=T&;Y;[/Z=)!>;L=ZN`A8!+0YCFW76S<:A`'\6T2VA>T,WRCY`O3NB`N -M-RXYKF^"`K*J\QZ(%+8%S*$4$B9MN?WW$2<"S7+U(`'"E[`@7>L1I'_M_C%, -MS1'U-EM-T&PHJ0,EXN]GIHP604)D`:+$I)9-D9V:T$^A(?BN(L3QYW5;SQP&5Z,%!AX -M5/G+(P0V?RO^_RR+:Z9X2(&P3-<.Z@`J"`A[YV\E+K9C'Q>FXSXD$.3_WNN; -M(`=W[TKTEN3VXK*<,8,56))`3;M`V)%LNGXQH4RS[;`X/Z9R+M)`'TR!!)B5 -M:[[:#WE -M_R(/VHB'/2:7H$0C/KU%S=>3T4GH;%1=\#;)2B6"G.-ZB+Q2'U3UOO9CFV3J -MR:M(!6]N"';6NG8?.UY'"X_9^L&\=Y*!"BS8!2\[F6QY$=ZXMDVSK]=C4O2@ -M]IA`;+./5R1#P!MVZWG^[E56]Z&_3/RD@#9\ZX!-W.M4X]4>;%2B`$UMO:(! -M.X>5_(BF7OUPTEML7\Q$Y;. -M:H3X97Z[18:,ED -M/.E`L;HG[[-H`DDZY]*6WS[%>-B:_:!)!10[`(0!RVY"AQ,IH?7J6?LS]+0= -MF16"`H'/>$-$Y;59P:U)2=+,)T8`^X%XDM'J:%5\$#P(7X8>2RG7Z2$#\)L."&X@VC0#:V.(B2O?\OW/>E+[=/3Z\;E -M;XNX8.+_4TB&;5;(%9J^\0#B/HU$PFRJ$$+SYRL#D*L"$HOT[;4[:9_/8-L$ -M1`0\O>?7Z2\.K$!O,]MY3-,Y["8/K(:+N*8/-J,@("S`#__=[FT]/[*#9^F) -MSB01,'F("F[/_6Y8N5JYP^!L\>10I_.SEXM;#._)`1,B\1#M0;MJXOC(Z_AC -ME;84K@EM\"E@!%KM-ZP`P$TZUM:3^@%:CODTE_RW'W_'[[W -M+S4A;4DW`K*:1#L^S[=F6_4""*7026@EJQ5W^\9]W"K%/;R7#B1\?*G@$8!! -M08,;?/G$Q?9XB07:JD@`5,2`5OILA#>>N3OAKY.M4.5U?:C(1;WVY1N(!Q'5 -MKOBEQU8MIM%(,0@J:2Z:6"$GQ*3N-&Q`+5G-"0S#L[KM2B('J":GB(M!Y -MHYYA3_6,9A-UPT4:FRG)&Z2P0JUR.`'ZSD_MNAX]OWEQ`!PLU-?V=(#Q^OV? -M"JUD"8M_8[.5#Q:FV60[B% -MFW-ZSIB,%\F*`(B\N>U(R7LG>K&.:L0@6\ACR!8M(W6,GE5+U8E(YS&T=."U:>]A=3_2X0*29O!BP!GD"C!X" -MS<[F/OE0@]>!+AT=,1!;]N[_WE[03>+6UZR>4@#Q*7%Z`5S4>R_P\7D5..K] -M/X$A"+7YL/X(?AO0E)&9@F3CUW"G$0*QR((>)A]1CBU_@OC^-GUGN9-PS-(E -MD!0QVS[K&*2W!5_U_B0U^XW$QN7J73E)I2(;SS9/NW-"")HM9RPX/LM5YQK@ -MXR+`OR:L"FJW'>G)D3B&_1T3SUTI*8HE"18?XB8C##U\\Z0$/;++7NH.;DHV -M7B!8_3H)0`U,55-?1`+#3\\Y6*?\M\H24ZGSB2!@S!$6?_%,&GW[Z)E:].7, -MJN:/-Q#8]]]?3R`7L$$<@^FG16^V\CJC"`BGW^XRWV`%F]^?QO^GV^<94C;N -MT\D@#1VEMMH1Y(!FMKW!JN/*3QIIW6LLD!3/9(%VFBU&3:>!#BIM:.!5?\2@ -M65-Y<^\VY$&%WKE2!W-K:X.AD;Z^^1KPM -MV\?4=I*0UC;70WH:&8&I]:@=$/\SF4E>B0+LR9SQ$!VI;N>Y_":W!HAAW%6PH.IU> -M71JR2A`[.M=NN0*-=ME+1KTPL+!;(0LM4J1#J1^1Y5O*/"!_FF%$!?*G#.P) -M$.]^G:W'8J,-O%EFP@C!,_E[ -M&HUX(G>C7[OW[R,S?8=,'QQ<@HJ.R[^FU]B/BH@@R2M9)$`(7DM8>LWHB"6S_>4AP!=_?B):9I99F1TJ$AI^2`'.!5$"DW*F\*VN5V<7?[:FQW5B'OFO1Y -M/YY:4B`RHQ>\B)OI+U,&X&E,!O"H$A#S]_SH0`$$;^E_AM+(K.[)+CHI`6L< -MUAK(#FQF_CTM\0M.9TNP1$&?Z6'N42!/]%;>17JZ[3041T,RD%`WM`>(AL.A -MD_I9[Y5QZ4@)1_4)-[B:!!'8M'>XAP(N>)-*!LA04$NM_Q1`)J[2SJT>L7NF -MT[>GCT@L9J51EX74]_(:`"%_[E)0T>N4O`CUG4ZC1%:80K&U*U<$\B'`Z/TJ -M/[!S?2(B02"CT8Q5W..P%?2.IAYE4,;QDL`,7ZHF/W;<`":^IV7U_YW6H/EE -MYMQ@LF1"5KN$J2K+'YUH9):E2$+'K8\$6U]M9\F5TB!`*MTMHP)"Y[-5B*,A -MYO9TY,@6=\U;*Y3`A^"[\UU5;+_P(NT=$Q)\N[)?6&/`B2R3DSVV^J;W@^YR -MKEE952&@RRRVV1`8E.[7"66,;54J($\?E1;]E(`'K/F.L-Y-5E5*E89O'32H -MMVU\H8M-!6.LVSN`-N%'-?'3+QQK2]],QR(BCO16_A]R`("'A*6FG+GQMN[@ -M\N+3J.*ZC%Y,Y"!M8G7@`M=J\;M9&E_/&A[[/LWC,1#I;I9;G`XA`KVLS/:7 -M+WJ,2-3%!F+4@6`P:ETZ*-&ZL"]2-B0H+3K4H`!)+VO[BDGHZ$5RPUGIHQEF -MY504K%*A$.=J\C)R4-I,^B$_!X.4'MN#[V`3"I:[D)=O^_19RW;8%6E55&3E:\"H4GNR3=("`V%YKL?J_+H -M%+O\X_IU%@]B4!#6E![8_&@A+;Y)B=Y3_+D`+9HNUI(3Y)?WY8H@4)9\3#D?1 -ME0HX7U@GG=GN>YD"%P_W6DC^W0[CPOFE:5H[3-12VMKJFO[EK8`"G=AT[Z2SPX,E9?G4#KR&08@0H5]\U8GT'Q\>UV[ -M*"(9J/D)1WDZL;[+[[FL,#D]$[I8%A^,KX>O2+K``0VKJXS-/!G$U/#XG(0O -M"OJ_&SOB`$`QJDECIERVTTTSVI6G-&2C?GU8O)+ @@ -9,16 +9,16 @@ TESTS_SH= t_cgd -.if ${MKCRYPTO} != "no" && ${MKRUMP} != "no" -TESTS_C= t_cgd_3des t_cgd_aes t_cgd_blowfish +.if ${MKRUMP} != "no" +TESTS_C+= t_cgd_3des +TESTS_C+= t_cgd_adiantum +TESTS_C+= t_cgd_aes +TESTS_C+= t_cgd_blowfish WARNS= 4 CPPFLAGS+= -D_KERNTYPES -LDADD+= -lrumpdev -lrumpdev_disk -lrumpdev_cgd -lrumpkern_crypto -lrumpvfs -LDADD+= -lrump -LDADD+= -lrumpuser -LDADD+= -lrump -lutil -LDADD+= -lpthread +LDADD+= -lrumpdev -lrumpdev_disk -lrumpdev_cgd -lrumpkern_crypto ${LIBRUMPBASE} +LDADD+= -lutil .endif .include diff --git a/dev/cgd/t_cgd.sh b/dev/cgd/t_cgd.sh old mode 100755 new mode 100644 --- a/dev/cgd/t_cgd.sh +++ b/dev/cgd/t_cgd.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_cgd.sh,v 1.11 2013/02/19 21:08:24 joerg Exp $ +# $NetBSD: t_cgd.sh,v 1.13 2019/04/10 06:13:21 kre Exp $ # # Copyright (c) 2010 The NetBSD Foundation, Inc. # All rights reserved. @@ -34,13 +34,12 @@ basic_head() { - atf_set "descr" "Tests that encrypt/decrypt works" - atf_set "require.progs" "rump_server" + atf_set descr "Tests that encrypt/decrypt works" + atf_set require.progs rump_server } basic_body() { - d=$(atf_get_srcdir) atf_check -s exit:0 \ ${cgdserver} -d key=/dev/dk,hostpath=dk.img,size=1m unix://csock @@ -57,7 +56,6 @@ basic_cleanup() { - env RUMP_SERVER=unix://csock rump.halt || true } @@ -65,28 +63,27 @@ wrongpass_head() { - atf_set "descr" "Tests that wrong password does not give original " \ - "plaintext" - atf_set "require.progs" "rump_server" + atf_set descr \ + "Tests that wrong password does not give original plaintext" + atf_set require.progs rump_server } wrongpass_body() { - d=$(atf_get_srcdir) atf_check -s exit:0 \ ${cgdserver} -d key=/dev/dk,hostpath=dk.img,size=1m unix://csock export RUMP_SERVER=unix://csock - atf_check -s exit:0 -x "echo 12345 | \ - rump.cgdconfig -p cgd0 /dev/dk ${d}/paramsfile" + atf_check -s exit:0 -x \ + "echo 12345 | rump.cgdconfig -p cgd0 /dev/dk ${d}/paramsfile" atf_check -s exit:0 -e ignore -x \ "dd if=${d}/t_cgd | rump.dd of=${rawcgd} count=2" # unconfig and reconfig cgd atf_check -s exit:0 rump.cgdconfig -u cgd0 - atf_check -s exit:0 -x "echo 54321 | \ - rump.cgdconfig -p cgd0 /dev/dk ${d}/paramsfile" + atf_check -s exit:0 -x \ + "echo 54321 | rump.cgdconfig -p cgd0 /dev/dk ${d}/paramsfile" atf_check -s exit:0 -e ignore dd if=${d}/t_cgd of=testfile count=2 atf_check -s exit:0 -e ignore -o not-file:testfile \ @@ -95,7 +92,6 @@ wrongpass_cleanup() { - env RUMP_SERVER=unix://csock rump.halt || true } @@ -103,9 +99,8 @@ atf_test_case unaligned_write cleanup unaligned_write_head() { - - atf_set "descr" "Attempt unaligned writes to a raw cgd device" - atf_set "require.progs" "rump_server" + atf_set descr "Attempt unaligned writes to a raw cgd device" + atf_set require.progs rump_server } unaligned_write_body() @@ -115,8 +110,8 @@ ${cgdserver} -d key=/dev/dk,hostpath=dk.img,size=1m unix://csock export RUMP_SERVER=unix://csock - atf_check -s exit:0 -x "echo 12345 | \ - rump.cgdconfig -p cgd0 /dev/dk ${d}/paramsfile" + atf_check -s exit:0 -x \ + "echo 12345 | rump.cgdconfig -p cgd0 /dev/dk ${d}/paramsfile" # Check that cgd rejects writes of totally bogus lengths. atf_check -s not-exit:0 -e ignore -x \ @@ -133,8 +128,8 @@ # packetizing the input on bogus boundaries and using the # bizarre behaviour of `bs=N' in dd. atf_check -s not-exit:0 -e ignore -x \ - "(echo -n x && sleep 1 && head -c 511 /dev/null + env RUMP_SERVER=unix://csock rump.halt || true + }" +} + +test_case_vmeth_failure disklabel +test_case_vmeth_failure ffs +test_case_vmeth_failure gpt +test_case_vmeth_failure mbr + +atf_init_test_cases() +{ atf_add_test_case basic atf_add_test_case wrongpass atf_add_test_case unaligned_write + atf_add_test_case vmeth_failure_disklabel + atf_add_test_case vmeth_failure_ffs + atf_add_test_case vmeth_failure_gpt + atf_add_test_case vmeth_failure_mbr } diff --git a/dev/cgd/t_cgd_3des.c b/dev/cgd/t_cgd_3des.c --- a/dev/cgd/t_cgd_3des.c +++ b/dev/cgd/t_cgd_3des.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_cgd_3des.c,v 1.2 2017/01/13 21:30:39 christos Exp $ */ +/* $NetBSD: t_cgd_3des.c,v 1.3 2020/08/15 10:03:10 mlelstv Exp $ */ /*- * Copyright (c) 2016 The NetBSD Foundation, Inc. * All rights reserved. @@ -68,7 +68,7 @@ 0 }; -static const uint8_t c3des_cbc_ptxt[SECSIZE] = +static const uint8_t c3des_cbc_ptxt[SECSIZE] __aligned(4) = " abcdefghijklmnop" " abcdefghijklmnop" " abcdefghijklmnop" diff --git a/dev/cgd/t_cgd_adiantum.c b/dev/cgd/t_cgd_adiantum.c new file mode 100644 --- /dev/null +++ b/dev/cgd/t_cgd_adiantum.c @@ -0,0 +1,403 @@ +/* $NetBSD: t_cgd_adiantum.c,v 1.5 2020/08/20 13:33:54 riastradh Exp $ */ + +/*- + * Copyright (c) 2020 The NetBSD Foundation, Inc. + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +#include "h_macros.h" + +#define MAXSECSIZE 512 /* for now; should be cgd parameter */ +#define IMGSIZE 0x101*512 + +/* Used as buffer for cgd device I/O, must be at least 32-bit aligned. */ +static const uint8_t zerosector[512] __aligned(4); + +static const struct { + uint8_t key[32]; + uint64_t blkno; + unsigned secsize; + const uint8_t *ptxt; + const uint8_t *ctxt; +} C[] = { + [0] = { + .key = {0}, + .blkno = 0, + .secsize = 512, + .ptxt = zerosector, + .ctxt = (const uint8_t[512]) { + 0x51,0x6d,0xe2,0x81, 0x26,0xd5,0xc8,0xd7, + 0xff,0xc6,0xc2,0xff, 0x39,0xbf,0x15,0x15, + 0x46,0x80,0x44,0x65, 0x76,0xa1,0x56,0xae, + 0xa0,0xb6,0x44,0x05, 0xb7,0xb1,0x32,0x23, + 0x80,0x07,0xdd,0x31, 0x57,0x69,0xf5,0x10, + 0x2d,0x53,0x54,0x8a, 0x1c,0x30,0x15,0x53, + 0x40,0xb4,0x75,0xb2, 0xa1,0x8a,0xbe,0xdf, + 0xf7,0x10,0xe0,0x38, 0xf9,0x70,0x29,0xda, + 0xf0,0x95,0xcd,0xe9, 0x47,0xa1,0x32,0xa3, + 0x83,0xca,0xe3,0x36, 0xc3,0x21,0x00,0xc2, + 0x0a,0xb4,0x0e,0x67, 0x69,0xe6,0xe8,0x14, + 0x74,0x98,0x69,0xd0, 0x6e,0xab,0x23,0xbc, + 0xa9,0x1e,0xf8,0x2d, 0x98,0x59,0x98,0x81, + 0x29,0x70,0xa8,0x1e, 0x26,0x13,0xba,0x53, + 0x9e,0x83,0xe9,0x35, 0x73,0x8c,0xf9,0xb6, + 0x10,0x17,0xda,0xe8, 0x21,0xcc,0x7d,0xd2, + 0x8e,0x23,0xb9,0x63, 0xde,0xcf,0xa7,0x53, + 0x56,0x1c,0xc8,0x53, 0x91,0x17,0x8f,0xec, + 0x93,0x66,0x8b,0x0f, 0x18,0x6e,0xa5,0x9d, + 0x8e,0x99,0x36,0x1c, 0x23,0xb6,0x0f,0x5d, + 0x75,0xc3,0xfd,0x35, 0xc5,0x68,0x9c,0xe1, + 0xba,0x19,0x1a,0x09, 0xca,0x40,0x1f,0xee, + 0x0f,0x76,0x84,0x92, 0x72,0xdf,0x62,0x1b, + 0x2e,0xa9,0x36,0xbe, 0xca,0x7e,0xc6,0x69, + 0xc6,0x27,0xf8,0x12, 0xbf,0x6e,0xd3,0xf0, + 0xb0,0x10,0x9c,0x67, 0x76,0x40,0xc8,0x36, + 0x8e,0x73,0xec,0xa2, 0xdb,0x4a,0x0a,0xd9, + 0x1b,0xa3,0x28,0x30, 0x84,0xa4,0xff,0xa0, + 0xe7,0x1e,0xf4,0xb2, 0xfe,0x59,0x79,0xdf, + 0x8d,0x66,0x12,0xac, 0xf6,0x1a,0x0f,0xa6, + 0x4e,0x86,0x8c,0x80, 0x95,0x11,0xee,0x55, + 0xe3,0xe0,0x43,0x56, 0xa2,0xfc,0xa2,0xbd, + 0xad,0x6f,0xfc,0xf9, 0x4c,0x04,0x51,0xf4, + 0xd9,0x17,0x96,0xdc, 0xd3,0xd0,0xd7,0xeb, + 0xa8,0xdc,0x34,0x65, 0xc7,0xcf,0xed,0x06, + 0xf8,0xa3,0xff,0x31, 0x3e,0x15,0x2f,0x62, + 0x8c,0x73,0x7f,0x8c, 0x80,0x4d,0x4b,0x6d, + 0xcf,0xc6,0xd0,0xdd, 0x7e,0x3a,0x1e,0x88, + 0xb7,0xdd,0x23,0xa6, 0xa0,0x0d,0x6c,0xaf, + 0xd6,0x5b,0xfd,0x76, 0x66,0xee,0x02,0xa6, + 0x10,0xda,0x42,0xfb, 0x15,0xc3,0xe4,0xa7, + 0x8b,0x2b,0xfa,0x5d, 0xba,0xce,0xcd,0x9f, + 0x76,0x38,0x66,0xff, 0x74,0x08,0x34,0xf3, + 0x3d,0x12,0xf4,0x8d, 0x5e,0x54,0x2b,0x37, + 0x06,0xd3,0x03,0xc9, 0xd9,0x29,0x53,0x65, + 0x76,0x00,0x24,0x50, 0x30,0x06,0x6c,0x69, + 0x31,0xcc,0x89,0x7c, 0x97,0xae,0xac,0x74, + 0x35,0x43,0xa3,0xe5, 0x40,0x58,0x3d,0xb9, + 0x08,0x46,0x5e,0x5f, 0x07,0xc5,0x41,0x32, + 0xab,0xa4,0x5a,0xab, 0x59,0x2b,0x54,0xee, + 0x24,0x92,0xd3,0x08, 0xb8,0x99,0x9e,0x13, + 0x3c,0x2c,0x05,0xe6, 0xc1,0x6f,0xa1,0x5d, + 0xa9,0x09,0x1a,0x96, 0x76,0xe4,0x31,0xc6, + 0xcc,0xad,0x28,0x58, 0x73,0x4d,0x1a,0x19, + 0x3d,0xcd,0xaf,0x8c, 0xd8,0x24,0xff,0x72, + 0xdc,0x4e,0x07,0x6e, 0xd8,0xbc,0x3b,0x2b, + 0xf5,0xe5,0xfa,0x30, 0x7d,0xaa,0x59,0x40, + 0x78,0x01,0xa4,0x55, 0xdc,0xe6,0x7b,0xae, + 0x87,0x8e,0x11,0xbb, 0x65,0xf7,0x8a,0x4f, + 0x37,0x7e,0xe1,0xac, 0x62,0xf1,0x64,0x8f, + 0xc1,0xfd,0x3e,0x34, 0x1f,0x60,0xba,0x61, + 0x98,0xae,0x19,0xce, 0x54,0x22,0x64,0x09, + 0x67,0x82,0x6b,0x4b, 0xdf,0x26,0x77,0xde, + 0xd6,0x13,0x00,0xee, 0x2c,0x18,0x49,0xd9, + }, + }, + [1] = { + .key = {0}, + .blkno = 1, + .secsize = 512, + .ptxt = zerosector, + .ctxt = (const uint8_t[512]) { + 0xf2,0x23,0x68,0x5a, 0x15,0x11,0x56,0xa1, + 0x71,0x57,0x5c,0x5e, 0x32,0xd4,0xdd,0xbb, + 0x7a,0x0c,0x84,0x23, 0xe9,0x2f,0x1b,0x63, + 0x3c,0x4d,0xad,0xfd, 0x6e,0xc0,0xdb,0x79, + 0x23,0xa5,0x13,0xfe, 0x17,0x3c,0x4a,0x27, + 0xb9,0xbc,0xf0,0xf6, 0x67,0x98,0xa8,0x64, + 0xce,0xf0,0x17,0x0a, 0xa8,0x05,0x0f,0xf2, + 0xff,0xb0,0x7a,0x9e, 0x1a,0xcf,0x5d,0x0e, + 0x9f,0xb0,0x9a,0xd0, 0x7c,0xf2,0x88,0x96, + 0xe2,0x8d,0xdb,0xa2, 0x19,0x30,0x3d,0x5d, + 0x66,0x28,0x40,0x53, 0xb9,0x8d,0xbb,0x24, + 0x3a,0x4c,0x00,0xac, 0x20,0x86,0x96,0x83, + 0x2c,0x77,0x5e,0x18, 0x0c,0xfa,0x27,0x51, + 0xe1,0x5d,0xd3,0xd9, 0xe1,0x5d,0x27,0x1f, + 0x49,0x74,0xfd,0x2a, 0xc3,0xe5,0xa0,0xf6, + 0x5a,0x58,0xe3,0x1f, 0x4a,0xa6,0xc2,0x25, + 0xe4,0xb5,0xc8,0x0d, 0x9f,0xa7,0xc0,0x6e, + 0xab,0xb3,0xfc,0x9f, 0xe1,0x72,0x8a,0x69, + 0xf1,0xc6,0x54,0xb8, 0xeb,0x70,0xed,0xfe, + 0x95,0xf7,0x0d,0x55, 0x95,0x13,0x7a,0x82, + 0xac,0x83,0xd2,0xa3, 0xdc,0x5b,0xba,0x4e, + 0xae,0xdd,0xe9,0x22, 0x9e,0xe2,0x72,0xaf, + 0x9a,0xc0,0x53,0x96, 0xb9,0x7d,0x47,0x28, + 0x4a,0x93,0x6a,0xfb, 0x59,0x25,0x49,0x39, + 0xda,0x23,0xe8,0x28, 0x42,0xba,0x58,0x26, + 0x29,0xf5,0x4c,0x85, 0xbb,0x62,0xfc,0x12, + 0x28,0xbd,0xec,0x3f, 0xf4,0x86,0x80,0xf0, + 0x69,0x81,0x99,0xe3, 0x95,0x0d,0xe8,0x8f, + 0xeb,0x60,0xb6,0x2a, 0xbf,0xf1,0x41,0xe4, + 0x68,0x4f,0x4b,0xe3, 0x49,0x2c,0x1e,0xad, + 0x0d,0x8f,0x63,0x40, 0xb9,0xee,0x4d,0x09, + 0x12,0x45,0x97,0x64, 0x97,0xd5,0x5f,0xa3, + 0xb0,0x4b,0xdf,0x3f, 0x59,0x9f,0xab,0x12, + 0x3d,0x4b,0x54,0xdc, 0xea,0xe0,0x55,0x5e, + 0x1c,0xfd,0xe9,0x7e, 0x40,0x24,0x88,0x6c, + 0x8d,0xfc,0xc2,0x57, 0xd2,0x37,0xb2,0x12, + 0xc2,0x03,0x0d,0xac, 0xb8,0x9b,0x62,0x61, + 0x23,0xc0,0x7a,0x06, 0xdb,0x62,0x86,0x06, + 0xaf,0xa5,0x98,0x75, 0xd9,0x4e,0x8a,0xf2, + 0xc5,0x64,0xad,0xf2, 0xf4,0xc2,0x7f,0xa2, + 0x25,0xf4,0xd0,0x44, 0x57,0x8b,0x89,0xe2, + 0x08,0xea,0x86,0x72, 0x37,0xe3,0x7e,0x92, + 0x22,0xa0,0x32,0x05, 0x30,0x90,0xcc,0x44, + 0x6f,0x2c,0x75,0xae, 0x28,0x90,0x34,0xe3, + 0x05,0x88,0xcd,0x77, 0x1d,0x6a,0x72,0x56, + 0x49,0x3f,0x3d,0x0b, 0x49,0x04,0x98,0x65, + 0x66,0x0e,0xfd,0x7d, 0xca,0x32,0x74,0x66, + 0xa0,0xd7,0x04,0xdb, 0x83,0x4b,0x7f,0x83, + 0x22,0x43,0x98,0x93, 0x0d,0x0b,0xb1,0x8d, + 0x8c,0x8b,0x9e,0x08, 0xb9,0xb0,0xd9,0x82, + 0xcd,0x20,0x5e,0x19, 0x5d,0xa0,0x6a,0x71, + 0x05,0xf9,0x18,0x3d, 0x6b,0xb7,0xb6,0x56, + 0x03,0xa3,0x53,0x58, 0x7d,0xf8,0x25,0xca, + 0x26,0x02,0xc1,0xa6, 0x72,0x70,0xc3,0xe3, + 0x59,0x64,0xe1,0x25, 0x34,0x79,0xb3,0x5e, + 0x08,0xe9,0xb8,0x91, 0xb6,0x5d,0x3a,0x44, + 0x20,0x60,0x61,0xf4, 0x28,0x93,0x8f,0x89, + 0xbe,0xea,0x55,0xda, 0x43,0x38,0x96,0xc8, + 0x50,0x01,0x09,0xaf, 0x76,0x92,0x83,0xae, + 0x3b,0x82,0x6f,0x49, 0x0b,0x18,0x9c,0xef, + 0x92,0x06,0x11,0xeb, 0x41,0x34,0xf4,0x7b, + 0xc4,0x9a,0x9f,0xe4, 0xb4,0xe7,0x1a,0x84, + 0xd8,0x8b,0x3a,0x29, 0xb5,0x4e,0xf3,0x97, + 0x6c,0xef,0xe9,0x62, 0x21,0x89,0x23,0xfd, + }, + }, + [2] = { + .key = {0}, + .blkno = 0x100, + .secsize = 512, + .ptxt = zerosector, + .ctxt = (const uint8_t[512]) { + 0x32,0x26,0xaf,0x56, 0xbc,0x43,0xac,0x37, + 0xb2,0x8d,0xa4,0xfb, 0x32,0xdc,0x09,0x03, + 0xd9,0x44,0xce,0x4e, 0x70,0xaf,0xed,0x83, + 0x4b,0x9c,0x85,0x11, 0xd2,0x6a,0x70,0x15, + 0xea,0x7b,0x5e,0xac, 0x5d,0x08,0x25,0xd7, + 0x8c,0x23,0x7c,0x15, 0xb7,0x20,0xd1,0x08, + 0xe0,0x81,0x71,0xbe, 0x68,0xca,0xe2,0xcd, + 0x98,0xe5,0x40,0xe0, 0xf5,0x84,0xcc,0x6a, + 0x3c,0xa0,0xe8,0x2c, 0x02,0x4c,0x95,0xb5, + 0x58,0x86,0x86,0x61, 0x71,0x7f,0xd7,0xf9, + 0xd9,0x64,0x80,0xf6, 0xea,0x92,0xbc,0x65, + 0x3b,0x07,0x77,0xaa, 0xb1,0xb1,0xf5,0xd6, + 0x6d,0x89,0x63,0x14, 0xc0,0xcc,0x7a,0x2b, + 0xc4,0x32,0x63,0xda, 0xa6,0xc6,0xc8,0xc6, + 0x4c,0x4e,0x10,0x63, 0x3b,0x93,0x80,0x77, + 0x3e,0x54,0xd2,0x38, 0x13,0x79,0xbc,0x6c, + 0x0b,0xd4,0x71,0x5c, 0x26,0xc0,0x81,0x09, + 0xc7,0xd8,0x7a,0x04, 0x58,0x2e,0x50,0x6a, + 0x3d,0xca,0xa1,0x66, 0x72,0xca,0xee,0x5a, + 0xdd,0x13,0x67,0xb1, 0x54,0x72,0x41,0x2d, + 0xfd,0x95,0x24,0xe3, 0x96,0x4a,0x41,0x03, + 0xeb,0xeb,0x99,0x49, 0x52,0xac,0x3a,0x28, + 0x81,0x54,0x1a,0xfb, 0xc3,0xcd,0x8e,0x9d, + 0x0c,0x64,0x95,0xbb, 0x27,0xb8,0x6b,0x51, + 0x7b,0xc4,0x57,0xc9, 0x29,0x4e,0x85,0x31, + 0x1c,0xaa,0x63,0x2e, 0x7a,0x37,0x2e,0x06, + 0xdc,0x58,0x39,0x3b, 0x60,0x34,0x59,0x15, + 0x4f,0xba,0x33,0x52, 0x13,0xb0,0x7b,0x7c, + 0x7e,0x00,0x0b,0x49, 0x15,0x9c,0x48,0xf4, + 0x67,0xdd,0xc6,0x72, 0x87,0xbe,0xe7,0xf7, + 0x21,0x95,0x82,0xc3, 0x41,0x3b,0x19,0xe3, + 0xf3,0x28,0xcc,0x14, 0x5f,0xae,0x6f,0x07, + 0x35,0x94,0x05,0x46, 0x02,0x5c,0x3c,0x46, + 0xb1,0x2d,0xeb,0x6e, 0xa0,0x0f,0xea,0x40, + 0x3e,0x35,0x6e,0x50, 0xc4,0x22,0xeb,0x93, + 0xba,0x49,0xfb,0xf0, 0x8e,0x2a,0xa1,0xaf, + 0xf4,0x91,0xb2,0xc5, 0x7d,0x8e,0xba,0x45, + 0x53,0x75,0xc3,0xcc, 0x3e,0x02,0x0e,0x4d, + 0x2e,0xda,0x45,0xd2, 0x31,0xc7,0x1b,0x6b, + 0x99,0x71,0x8d,0xd8, 0x8c,0x94,0xa2,0x02, + 0x6c,0xb0,0x32,0x8f, 0xce,0x04,0x61,0x0a, + 0x3f,0x17,0x3a,0x28, 0xda,0x31,0xdc,0xec, + 0xbc,0xea,0x1b,0x37, 0x9b,0x36,0x04,0xb1, + 0xb5,0x7f,0xfe,0x1a, 0xd8,0x11,0xb7,0x0a, + 0x77,0x2e,0x6d,0x22, 0x79,0x9e,0x54,0x47, + 0xea,0xf5,0x17,0x38, 0xd0,0xe2,0x23,0x68, + 0x92,0x88,0x42,0x59, 0x2c,0x61,0x53,0x2b, + 0x99,0xed,0x7b,0x85, 0xfb,0xb8,0xe8,0x0c, + 0x4b,0x81,0x1e,0x0f, 0x42,0x04,0x8b,0x55, + 0x2c,0x34,0x46,0x98, 0x9c,0x47,0x08,0x70, + 0x46,0x45,0x5e,0xa8, 0x62,0x92,0x94,0xcd, + 0x73,0x1c,0xef,0x8b, 0x96,0x5f,0x6d,0x76, + 0x07,0x99,0x6f,0xe0, 0x1d,0xdc,0x1d,0x1c, + 0x3f,0xb4,0x5f,0x9b, 0x34,0x0c,0x75,0x10, + 0x7e,0x0d,0xf8,0xbb, 0xc3,0x8a,0x2a,0x15, + 0x01,0x3a,0x56,0x73, 0x5b,0xe9,0x5f,0xf2, + 0x6a,0x1b,0x17,0xce, 0xf3,0x3e,0xc9,0xdf, + 0x76,0xe8,0xcd,0xf2, 0x6d,0xb1,0xdc,0x29, + 0x8c,0xa3,0x89,0x73, 0x69,0x86,0xa9,0x05, + 0xbe,0x63,0xc8,0x7c, 0x36,0xc0,0x88,0x74, + 0x64,0x91,0xdd,0xb7, 0x92,0x73,0x7e,0xc1, + 0x01,0x95,0xb3,0x95, 0x53,0x33,0x16,0xcd, + 0xe9,0xd7,0x56,0x61, 0x71,0x49,0x24,0x9b, + 0x9a,0x10,0x7e,0x50, 0x7e,0xd3,0xe2,0x9d, + }, + }, +}; + +static void +hexdump(const void *buf, size_t len) +{ + const unsigned char *p = buf; + size_t i; + + for (i = 0; i < len; i++) { + if (i % 16 == 8) + printf(" "); + printf(" %02hhx", p[i]); + if ((i + 1) % 16 == 0) + printf("\n"); + } + if (i % 16) + printf("\n"); +} + +static int +configure_cgd(int fd, const char *dkpath, const char *alg, + const char *ivmethod, const void *key, size_t keybytes) +{ + struct cgd_ioctl ci; + + memset(&ci, 0, sizeof(ci)); + ci.ci_disk = dkpath; + ci.ci_alg = alg; + ci.ci_ivmethod = ivmethod; + ci.ci_keylen = 8*keybytes; + ci.ci_key = key; + ci.ci_blocksize = (size_t)-1; + + return rump_sys_ioctl(fd, CGDIOCSET, &ci); +} + +static int +unconfigure_cgd(int fd) +{ + struct cgd_ioctl ci; + + return rump_sys_ioctl(fd, CGDIOCCLR, &ci); +} + +ATF_TC(cgd_adiantum); +ATF_TC_HEAD(cgd_adiantum, tc) +{ + + atf_tc_set_md_var(tc, "descr", "Adiantum tests"); +} + +ATF_TC_BODY(cgd_adiantum, tc) +{ + static uint8_t buf[MAXSECSIZE]; + static const char imgpath[] = "adiantum.img"; + static const char dkpath[] = "/dev/dk"; + char cgdpath[MAXPATHLEN]; + int dkfd, cgdfd; + unsigned i; + ssize_t nwrit, nread; + + rump_init(); + + RL(dkfd = open(imgpath, O_CREAT|O_RDWR|O_TRUNC, 0600)); + RL(ftruncate(dkfd, IMGSIZE)); + RL(rump_pub_etfs_register_withsize(dkpath, imgpath, RUMP_ETFS_BLK, 0, + IMGSIZE)); + snprintf(cgdpath, sizeof cgdpath, "/dev/rcgd0%c", + getrawpartition() + 'a'); + RL(cgdfd = rump_sys_open(cgdpath, O_RDWR)); + + for (i = 0; i < __arraycount(C); i++) { + /* write the plaintext out via cgd */ + RL(configure_cgd(cgdfd, dkpath, "adiantum", "encblkno1", + C[i].key, 32)); + RL(nwrit = rump_sys_pwrite(cgdfd, C[i].ptxt, C[i].secsize, + C[i].blkno * C[i].secsize)); + RL(unconfigure_cgd(cgdfd)); + if ((size_t)nwrit != C[i].secsize) { + atf_tc_fail_nonfatal("truncated write: %zd != %u", + nwrit, C[i].secsize); + continue; + } + + /* read the ciphertext out from the underlying file */ + RL(nread = pread(dkfd, buf, C[i].secsize, + C[i].blkno * C[i].secsize)); + if ((size_t)nread != C[i].secsize) { + atf_tc_fail_nonfatal("truncated read: %zd != %u", + nread, C[i].secsize); + continue; + } + if (memcmp(buf, C[i].ctxt, C[i].secsize)) { + hexdump(buf, C[i].secsize); + hexdump(C[i].ctxt, C[i].secsize); + atf_tc_fail_nonfatal("case %u ctxt mismatch", i); + continue; + } + + /* read the plaintext back via cgd */ + RL(configure_cgd(cgdfd, dkpath, "adiantum", "encblkno1", + C[i].key, 32)); + RL(nread = rump_sys_pread(cgdfd, buf, C[i].secsize, + C[i].blkno * C[i].secsize)); + RL(unconfigure_cgd(cgdfd)); + if ((size_t)nread != C[i].secsize) { + atf_tc_fail_nonfatal("truncated read: %zd != %u", + nread, C[i].secsize); + continue; + } + if (memcmp(buf, C[i].ptxt, C[i].secsize)) { + hexdump(buf, C[i].secsize); + atf_tc_fail_nonfatal("case %u ptxt mismatch", i); + continue; + } + } + + RL(rump_sys_close(cgdfd)); + RL(close(dkfd)); +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, cgd_adiantum); + + return atf_no_error(); +} diff --git a/dev/cgd/t_cgd_aes.c b/dev/cgd/t_cgd_aes.c --- a/dev/cgd/t_cgd_aes.c +++ b/dev/cgd/t_cgd_aes.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_cgd_aes.c,v 1.6 2017/01/13 21:30:39 christos Exp $ */ +/* $NetBSD: t_cgd_aes.c,v 1.8 2020/08/15 10:03:10 mlelstv Exp $ */ /*- * Copyright (c) 2016 The NetBSD Foundation, Inc. * Copyright (c) 2007 The Institute of Electrical and Electronics Engineers, Inc @@ -89,7 +89,7 @@ 0 }; -static const uint8_t aes_cbc_ptxt[SECSIZE] = +static const uint8_t aes_cbc_ptxt[SECSIZE] __aligned(4) = " abcdefghijklmnop" " abcdefghijklmnop" " abcdefghijklmnop" @@ -1954,7 +1954,7 @@ /* * Vector 4 from IEEE 1619/D16, blkno 0. */ -static const uint8_t aes_xts_256_vec4_ptxt[SECSIZE] = { +static const uint8_t aes_xts_256_vec4_ptxt[SECSIZE] __aligned(4) = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, @@ -2091,7 +2091,7 @@ /* * Vector 5 from IEEE 1619/D16, blkno 1. */ -static const uint8_t aes_xts_256_vec5_ptxt[SECSIZE] = { +static const uint8_t aes_xts_256_vec5_ptxt[SECSIZE] __aligned(4) = { 0x27, 0xa7, 0x47, 0x9b, 0xef, 0xa1, 0xd4, 0x76, 0x48, 0x9f, 0x30, 0x8c, 0xd4, 0xcf, 0xa6, 0xe2, 0xa9, 0x6e, 0x4b, 0xbe, 0x32, 0x08, 0xff, 0x25, @@ -2228,7 +2228,7 @@ /* * Vector 6 from IEEE 1619/D16, blkno 2. */ -static const uint8_t aes_xts_256_vec6_ptxt[SECSIZE] = { +static const uint8_t aes_xts_256_vec6_ptxt[SECSIZE] __aligned(4) = { 0x26, 0x4d, 0x3c, 0xa8, 0x51, 0x21, 0x94, 0xfe, 0xc3, 0x12, 0xc8, 0xc9, 0x89, 0x1f, 0x27, 0x9f, 0xef, 0xdd, 0x60, 0x8d, 0x0c, 0x02, 0x7b, 0x60, @@ -2365,7 +2365,7 @@ /* * Vector 7 from IEEE 1619/D16, blkno 0xfd. */ -static const uint8_t aes_xts_256_vec7_ptxt[SECSIZE] = { +static const uint8_t aes_xts_256_vec7_ptxt[SECSIZE] __aligned(4) = { 0x8e, 0x41, 0xb7, 0x8c, 0x39, 0x0b, 0x5a, 0xf9, 0xd7, 0x58, 0xbb, 0x21, 0x4a, 0x67, 0xe9, 0xf6, 0xbf, 0x77, 0x27, 0xb0, 0x9a, 0xc6, 0x12, 0x40, @@ -2502,7 +2502,7 @@ /* * Vector 8 from IEEE 1619/D16, blkno 0xfe. */ -static const uint8_t aes_xts_256_vec8_ptxt[SECSIZE] = { +static const uint8_t aes_xts_256_vec8_ptxt[SECSIZE] __aligned(4) = { 0xd5, 0x5f, 0x68, 0x4f, 0x81, 0xf4, 0x42, 0x6e, 0x9f, 0xde, 0x92, 0xa5, 0xff, 0x02, 0xdf, 0x2a, 0xc8, 0x96, 0xaf, 0x63, 0x96, 0x28, 0x88, 0xa9, @@ -2639,7 +2639,7 @@ /* * Vector 9 from IEEE 1619/D16, blkno 0xff. */ -static const uint8_t aes_xts_256_vec9_ptxt[SECSIZE] = { +static const uint8_t aes_xts_256_vec9_ptxt[SECSIZE] __aligned(4) = { 0x72, 0xef, 0xc1, 0xeb, 0xfe, 0x1e, 0xe2, 0x59, 0x75, 0xa6, 0xeb, 0x3a, 0xa8, 0x58, 0x9d, 0xda, 0x2b, 0x26, 0x1f, 0x1c, 0x85, 0xbd, 0xab, 0x44, @@ -3565,7 +3565,12 @@ rump_init(); - RL(dkfd = open_disk(dkpath, imgpath, dksize)); + dkfd = open_disk(dkpath, imgpath, dksize); + if (dkfd == -1 && errno == ENOSPC) { + atf_tc_skip("not enough space"); + } else { + ATF_CHECK_MSG(dkfd != -1, "open_disk: %s", strerror(errno)); + } RL(cgdfd = open_cgd(0)); RL(configure_cgd(cgdfd, dkpath, "aes-xts", "encblkno1", diff --git a/dev/cgd/t_cgd_blowfish.c b/dev/cgd/t_cgd_blowfish.c --- a/dev/cgd/t_cgd_blowfish.c +++ b/dev/cgd/t_cgd_blowfish.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_cgd_blowfish.c,v 1.2 2017/01/13 21:30:39 christos Exp $ */ +/* $NetBSD: t_cgd_blowfish.c,v 1.3 2020/08/15 10:03:10 mlelstv Exp $ */ /*- * Copyright (c) 2016 The NetBSD Foundation, Inc. * All rights reserved. @@ -92,7 +92,7 @@ 0 }; -static const uint8_t bf_cbc_ptxt[SECSIZE] = +static const uint8_t bf_cbc_ptxt[SECSIZE] __aligned(4) = " abcdefghijklmnop" " abcdefghijklmnop" " abcdefghijklmnop" diff --git a/dev/clock_subr/Makefile b/dev/clock_subr/Makefile new file mode 100644 --- /dev/null +++ b/dev/clock_subr/Makefile @@ -0,0 +1,15 @@ +# $NetBSD: Makefile,v 1.1 2016/08/14 14:55:42 jakllsch Exp $ + +NOMAN= # defined + +CPPFLAGS+= -I${.CURDIR}/../../../sys + +.include + +TESTSDIR= ${TESTSBASE}/dev/clock_subr + +.PATH: ${NETBSDSRCDIR}/sys/dev +TESTS_C+= t_clock_subr +SRCS.t_clock_subr= t_clock_subr.c clock_subr.c + +.include diff --git a/dev/clock_subr/clock_subr_test_data_gen.sh b/dev/clock_subr/clock_subr_test_data_gen.sh old mode 100755 new mode 100644 diff --git a/dev/dm/Makefile b/dev/dm/Makefile --- a/dev/dm/Makefile +++ b/dev/dm/Makefile @@ -1,5 +1,4 @@ - -# $NetBSD: Makefile,v 1.3 2016/01/23 21:22:48 christos Exp $ +# $NetBSD: Makefile,v 1.4 2020/03/01 18:08:12 christos Exp $ # .include @@ -16,10 +15,7 @@ CPPFLAGS+=-D_KERNTYPES LDADD+= -lprop -lutil -LDADD+= -lrumpdev_disk -lrumpdev_dm -LDADD+= -lrumpdev -lrumpvfs -LDADD+= -lrump -LDADD+= -lrumpuser -lpthread +LDADD+= -lrumpdev_disk -lrumpdev_dm ${LIBRUMPBASE} FILES= t_dm_disk_1.bz2.uue t_dm_disk_2.bz2.uue t_dm_disk_3.bz2.uue diff --git a/dev/dm/t_dm.sh b/dev/dm/t_dm.sh old mode 100755 new mode 100644 diff --git a/dev/fss/t_fss.sh b/dev/fss/t_fss.sh --- a/dev/fss/t_fss.sh +++ b/dev/fss/t_fss.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_fss.sh,v 1.2 2016/07/29 20:27:37 pgoyette Exp $ +# $NetBSD: t_fss.sh,v 1.4 2021/01/14 04:30:40 simonb Exp $ # # Copyright (c) 2006, 2007, 2008 The NetBSD Foundation, Inc. # All rights reserved. @@ -28,12 +28,20 @@ # Verify basic operation of fss(4) file system snapshot device # +vnddev=vnd0 +rawpart=$( sysctl -n kern.rawpartition | tr '01234' 'abcde' ) +vnd=/dev/${vnddev}${rawpart} + orig_data="Original data" repl_data="Replacement data" atf_test_case basic cleanup basic_body() { +# verify fss is available (or loadable as a module) + + fssconfig -l /dev/fss0 > /dev/null || atf_skip "FSS not available" + # create of mount-points for the file system and snapshot mkdir ./m1 @@ -43,9 +51,9 @@ # and mount it dd if=/dev/zero of=./image bs=32k count=64 - vndconfig -c vnd0 ./image - newfs /dev/vnd0a - mount /dev/vnd0a ./m1 + vndconfig -c ${vnddev} ./image + newfs -I ${vnd} + mount ${vnd} ./m1 echo "${orig_data}" > ./m1/text @@ -62,20 +70,14 @@ read test_data < ./m2/text atf_check_equal "${orig_data}" "${test_data}" - -# Unmount our temporary stuff - - umount /dev/fss0 || true - fssconfig -u fss0 || true - umount /dev/vnd0a || true - vndconfig -u vnd0 || true } basic_cleanup() { - umount /dev/vnd0a || true - fssconfig -u fss0 || true +# Unmount our temporary stuff umount /dev/fss0 || true - vndconfig -u vnd0 || true + fssconfig -u fss0 || true + umount ${vnd} || true + vndconfig -u ${vnddev} || true } atf_init_test_cases() diff --git a/dev/md/Makefile b/dev/md/Makefile --- a/dev/md/Makefile +++ b/dev/md/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.6 2016/01/23 21:22:48 christos Exp $ +# $NetBSD: Makefile,v 1.7 2020/03/01 18:08:13 christos Exp $ # .include @@ -14,10 +14,8 @@ PROGS= h_mdserv CPPFLAGS+= -D_KERNTYPES -LDADD+= -lrumpdev_md -lrumpdev_disk -lrumpdev -lrumpvfs -LDADD+= -lrumpkern_sysproxy -lrump -LDADD+= -lrumpuser -LDADD+= -lpthread +LDADD+= -lrumpdev_md -lrumpdev_disk -lrumpdev +LDADD+= -lrumpkern_sysproxy ${LIBRUMPBASE} WARNS= 4 NOMAN= diff --git a/dev/md/t_md.sh b/dev/md/t_md.sh old mode 100755 new mode 100644 diff --git a/dev/raidframe/t_raid.sh b/dev/raidframe/t_raid.sh old mode 100755 new mode 100644 --- a/dev/raidframe/t_raid.sh +++ b/dev/raidframe/t_raid.sh @@ -1,5 +1,5 @@ #! /usr/bin/atf-sh -# $NetBSD: t_raid.sh,v 1.12 2013/02/19 21:08:24 joerg Exp $ +# $NetBSD: t_raid.sh,v 1.15 2020/11/30 05:33:32 msaitoh Exp $ # # Copyright (c) 2010 The NetBSD Foundation, Inc. # All rights reserved. @@ -35,6 +35,22 @@ level=${1} ncol=${2} + printf "START array\n${ncol} 0\nSTART disks\n" > raid.conf + diskn=0 + while [ ${ncol} -gt ${diskn} ] ; do + echo "/disk${diskn}" >> raid.conf + diskn=$((diskn+1)) + done + + printf "START layout\n32 1 1 ${level}\nSTART queue\nfifo 100\n" \ + >> raid.conf +} + +makecfg_old() +{ + level=${1} + ncol=${2} + printf "START array\n1 ${ncol} 0\nSTART disks\n" > raid.conf diskn=0 while [ ${ncol} -gt ${diskn} ] ; do @@ -54,9 +70,8 @@ atf_set "require.progs" "rump_server" } -smalldisk_body() +smalldisk_body_backend() { - makecfg 1 2 export RUMP_SERVER=unix://sock atf_check -s exit:0 ${raidserver} \ -d key=/disk0,hostpath=disk0.img,size=1m \ @@ -66,15 +81,39 @@ atf_check -s exit:0 rump.raidctl -C raid.conf raid0 } +smalldisk_body() +{ + makecfg 1 2 + smalldisk_body_backend +} + smalldisk_cleanup() { export RUMP_SERVER=unix://sock rump.halt } +# The old configuration test case uses the smalldisk backend +atf_test_case old_numrows_config cleanup +old_numrows_config_head() +{ + atf_set "descr" "Checks the old numRows configuration works" + atf_set "require.progs" "rump_server" +} + +old_numrows_config_body() +{ + makecfg_old 1 2 + smalldisk_body_backend +} + +old_numrows_config_cleanup() +{ + export RUMP_SERVER=unix://sock + rump.halt +} -# make this smaller once 44239 is fixed -export RAID_MEDIASIZE=32m +export RAID_MEDIASIZE=4m atf_test_case raid1_compfail cleanup raid1_compfail_head() @@ -112,7 +151,7 @@ atf_check -s exit:0 rump.raidctl -c raid.conf raid0 - # check if we we get what we wrote + # check if we get what we wrote atf_check -s exit:0 -o file:testfile -e ignore \ rump.dd if=${rawraid} count=4 } @@ -200,7 +239,7 @@ atf_check -s exit:0 rump.raidctl -c raid.conf raid0 - # check if we we get what we wrote + # check if we get what we wrote atf_check -s exit:0 -o file:testfile -e ignore \ rump.dd if=${rawraid} count=4 @@ -251,7 +290,7 @@ atf_check -s exit:0 rump.raidctl -c raid.conf raid0 - # check if we we get what we wrote + # check if we get what we wrote atf_check -s exit:0 -o file:testfile -e ignore \ rump.dd if=${rawraid} count=4 } @@ -301,7 +340,7 @@ atf_check -s exit:0 rump.raidctl -c raid.conf raid0 - # check if we we get what we wrote + # check if we get what we wrote atf_check -s exit:0 -o file:testfile -e ignore \ rump.dd if=${rawraid} count=4 } @@ -315,6 +354,7 @@ atf_init_test_cases() { atf_add_test_case smalldisk + atf_add_test_case old_numrows_config atf_add_test_case raid1_normal atf_add_test_case raid1_comp0fail atf_add_test_case raid1_compfail diff --git a/dev/scsipi/Makefile b/dev/scsipi/Makefile --- a/dev/scsipi/Makefile +++ b/dev/scsipi/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.7 2016/01/23 21:22:48 christos Exp $ +# $NetBSD: Makefile,v 1.8 2020/03/01 18:08:13 christos Exp $ # .include @@ -15,10 +15,8 @@ LDFLAGS+= -L${SCSITESTDIR} LDADD+= -Wl,--whole-archive -lrumpdev_scsitest -Wl,--no-whole-archive -LDADD+= -lrumpdev_scsipi -lrumpdev_disk -lrumpdev -lrumpvfs -LDADD+= -lrump -lutil -LDADD+= -lrumpuser -lpthread -LDADD+= -lrump +LDADD+= -lrumpdev_scsipi -lrumpdev_disk -lrumpdev ${LIBRUMPBASE} +LDADD+= -lutil WARNS= 4 diff --git a/dev/scsipi/libscsitest/Makefile b/dev/scsipi/libscsitest/Makefile --- a/dev/scsipi/libscsitest/Makefile +++ b/dev/scsipi/libscsitest/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.3 2014/04/26 18:53:21 christos Exp $ +# $NetBSD: Makefile,v 1.4 2020/06/01 14:42:53 christos Exp $ # .include @@ -7,7 +7,7 @@ LIB= rumpdev_scsitest IOCONF= SCSITEST.ioconf -LIBISPRIVATE= #defined +LIBISPRIVATE= yes SRCS= scsitest.c SRCS+= scsitest_component.c diff --git a/dev/scsipi/libscsitest/scsitest.c b/dev/scsipi/libscsitest/scsitest.c --- a/dev/scsipi/libscsitest/scsitest.c +++ b/dev/scsipi/libscsitest/scsitest.c @@ -1,4 +1,4 @@ -/* $NetBSD: scsitest.c,v 1.2 2014/04/25 00:24:39 pooka Exp $ */ +/* $NetBSD: scsitest.c,v 1.5 2021/08/07 22:05:26 cjep Exp $ */ /* * Copyright (c) 2010 Antti Kantee. All Rights Reserved. @@ -35,7 +35,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: scsitest.c,v 1.2 2014/04/25 00:24:39 pooka Exp $"); +__KERNEL_RCSID(0, "$NetBSD: scsitest.c,v 1.5 2021/08/07 22:05:26 cjep Exp $"); #include #include @@ -123,9 +123,9 @@ memset(inqbuf, 0, sizeof(*inqbuf)); inqbuf->device = T_CDROM; inqbuf->dev_qual2 = SID_REMOVABLE; - strcpy(inqbuf->vendor, "RUMPHOBO"); - strcpy(inqbuf->product, "It's a LIE"); - strcpy(inqbuf->revision, "0.00"); + strncpy(inqbuf->vendor, "RUMPHOBO", sizeof inqbuf->vendor); + strncpy(inqbuf->product, "It's a LIE", sizeof inqbuf->product); + strncpy(inqbuf->revision, "0.00", sizeof inqbuf->revision); break; } case READ_CD_CAPACITY: { @@ -255,5 +255,6 @@ sc->sc_channel.chan_flags = SCSIPI_CHAN_NOSETTLE; sc->sc_channel.chan_adapter = &sc->sc_adapter; - config_found_ia(self, "scsi", &sc->sc_channel, scsiprint); + config_found(self, &sc->sc_channel, scsiprint, + CFARGS(.iattr = "scsi")); } diff --git a/dev/sysmon/Makefile b/dev/sysmon/Makefile --- a/dev/sysmon/Makefile +++ b/dev/sysmon/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.4 2016/01/23 21:22:48 christos Exp $ +# $NetBSD: Makefile,v 1.5 2020/03/01 18:08:13 christos Exp $ # .include @@ -8,11 +8,7 @@ TESTS_C= t_swwdog CPPFLAGS+= -D_KERNTYPES -LDADD+= -lrumpdev_sysmon -lrumpdev -lrumpvfs -LDADD+= -lrump -LDADD+= -lrumpuser -LDADD+= -lrump -LDADD+= -lpthread +LDADD+= -lrumpdev_sysmon -lrumpdev ${LIBRUMPBASE} WARNS= 4 diff --git a/dev/sysmon/t_swsensor.sh b/dev/sysmon/t_swsensor.sh old mode 100755 new mode 100644 --- a/dev/sysmon/t_swsensor.sh +++ b/dev/sysmon/t_swsensor.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_swsensor.sh,v 1.9 2015/04/23 23:23:28 pgoyette Exp $ +# $NetBSD: t_swsensor.sh,v 1.12 2021/06/13 14:45:36 riastradh Exp $ get_sensor_info() { rump.envstat -x | \ @@ -24,7 +24,7 @@ RUMP_SERVER=unix://t_swsensor_socket \ LD_PRELOAD=/usr/lib/librumphijack.so rndctl -l | \ grep "swsensor-sensor" | \ - awk '{print $2}' + awk '{print $3}' } check_powerd_event() { @@ -296,19 +296,16 @@ sleep 5 new_rnd_bits=$( get_rnd_bits_count ) if [ $new_rnd_bits -le $rnd_bits ] ; then - atf_expect_fail "PR kern/47661" atf_fail "14a: entropy bits did not increase after polling" fi rnd_bits=$new_rnd_bits sleep 5 new_rnd_bits=$( get_rnd_bits_count ) if [ $new_rnd_bits -gt $rnd_bits ] ; then - atf_expect_fail "PR kern/47661" atf_fail "14b: entropy bits increased after poll with no value change" fi # Step 15 - make sure entropy collected when device is interrogated - # rump.envstat -c env0.conf rump.sysctl -w hw.swsensor.cur_value=$3 get_sensor_key cur-value @@ -317,14 +314,12 @@ get_sensor_key cur-value new_rnd_bits=$( get_rnd_bits_count ) if [ $new_rnd_bits -le $rnd_bits ] ; then - atf_expect_fail "PR kern/47661" atf_fail "15a: entropy bits did not increase after interrogation" fi rnd_bits=$new_rnd_bits get_sensor_key cur-value new_rnd_bits=$( get_rnd_bits_count ) if [ $new_rnd_bits -gt $rnd_bits ] ; then - atf_expect_fail "PR kern/47661" atf_fail "15b: entropy bits increased after interrogation with no value change" fi } diff --git a/dev/usb/Makefile.inc b/dev/usb/Makefile.inc new file mode 100644 --- /dev/null +++ b/dev/usb/Makefile.inc @@ -0,0 +1,2 @@ +# $NetBSD: Makefile.inc,v 1.1 2020/03/01 20:19:54 christos Exp $ +.include "../Makefile.inc" diff --git a/dev/usb/libhid/Makefile b/dev/usb/libhid/Makefile --- a/dev/usb/libhid/Makefile +++ b/dev/usb/libhid/Makefile @@ -1,13 +1,13 @@ -# $NetBSD: Makefile,v 1.1 2016/01/05 17:22:39 jakllsch Exp $ +# $NetBSD: Makefile,v 1.3 2020/06/01 14:41:25 christos Exp $ # .include RUMPTOP= ${NETBSDSRCDIR}/sys/rump -.PATH: ${.CURDIR}/../../../../sys/dev/usb +.PATH: ${.CURDIR}/../../../../sys/dev/hid LIB= rumpdev_hid -LIBISPRIVATE= #defined +LIBISPRIVATE= yes SRCS= hid.c diff --git a/dev/usb/t_hid/Makefile b/dev/usb/t_hid/Makefile --- a/dev/usb/t_hid/Makefile +++ b/dev/usb/t_hid/Makefile @@ -1,14 +1,16 @@ -# $NetBSD: Makefile,v 1.1 2016/01/08 17:27:48 jakllsch Exp $ +# $NetBSD: Makefile,v 1.5 2020/03/01 20:19:54 christos Exp $ # -PROG= t_hid NOMAN= +.include -.PATH: ${.CURDIR}/.. +PROG= t_hid -CPPFLAGS.t_hid.c= -I${.CURDIR}/../../../../sys/dev/usb +.PATH: ${.CURDIR}/.. -.include +CPPFLAGS.t_hid.c= -I${.CURDIR}/../../../../sys/dev/hid +CPPFLAGS.t_hid.c+= -I${.CURDIR}/../../../../sys/dev/usb +CPPFLAGS.t_hid.c+= -D_RUMPKERNEL BINDIR= ${TESTSBASE}/dev/usb @@ -16,11 +18,8 @@ LDFLAGS+= -L${LIBHIDDIR} LDADD+= -Wl,--whole-archive -lrumpdev_hid -Wl,--no-whole-archive DPADD+= ${LIBHIDDIR}/librumpdev_hid.a -DPADD+= ${ATF_C} +DPADD+= ${ATF_C} -LDADD+= -latf-c -LDADD+= -lrump -LDADD+= -lrumpuser -LDADD+= -lpthread +LDADD+= ${LIBRUMPBASE} -latf-c .include diff --git a/fs/cd9660/t_high_ino_big_file.sh b/fs/cd9660/t_high_ino_big_file.sh old mode 100755 new mode 100644 diff --git a/fs/common/Makefile b/fs/common/Makefile --- a/fs/common/Makefile +++ b/fs/common/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.12 2014/03/04 21:06:47 joerg Exp $ +# $NetBSD: Makefile,v 1.16 2020/09/07 03:09:53 mrg Exp $ # .include @@ -27,7 +27,7 @@ CPPFLAGS+= -I${LIBC}/include -I${LIBC}/rpc -LIBISPRIVATE= # yup +LIBISPRIVATE= yes # NFS client stuff .PATH: ${NETBSDSRCDIR}/sbin/mount_nfs ${NETBSDSRCDIR}/sbin/mount @@ -40,5 +40,11 @@ CPPFLAGS+= -DUSE_RUMP -DLFS_CLEANER_AS_LIB CWARNFLAGS.clang+= -Wno-error=absolute-value +COPTS.fstest_nfs.c+= ${GCC_NO_FORMAT_TRUNCATION} +COPTS.clnt_bcast.c+= ${GCC_NO_CAST_FUNCTION_TYPE} +COPTS.clnt_generic.c+= ${GCC_NO_CAST_FUNCTION_TYPE} +COPTS.clnt_vc.c+= ${GCC_NO_CAST_FUNCTION_TYPE} +COPTS.rpcb_clnt.c+= ${GCC_NO_CAST_FUNCTION_TYPE} +COPTS.rpc_generic.c+= ${GCC_NO_CAST_FUNCTION_TYPE} .include diff --git a/fs/common/fstest_ext2fs.c b/fs/common/fstest_ext2fs.c --- a/fs/common/fstest_ext2fs.c +++ b/fs/common/fstest_ext2fs.c @@ -1,4 +1,4 @@ -/* $NetBSD: fstest_ext2fs.c,v 1.2 2010/07/30 16:15:05 pooka Exp $ */ +/* $NetBSD: fstest_ext2fs.c,v 1.3 2020/06/17 00:16:21 kamil Exp $ */ /*- * Copyright (c) 2010 The NetBSD Foundation, Inc. @@ -41,6 +41,7 @@ #include #include +#include #include #include "h_fsmacros.h" diff --git a/fs/common/fstest_ffs.c b/fs/common/fstest_ffs.c --- a/fs/common/fstest_ffs.c +++ b/fs/common/fstest_ffs.c @@ -1,4 +1,4 @@ -/* $NetBSD: fstest_ffs.c,v 1.6 2012/08/05 02:03:05 riastradh Exp $ */ +/* $NetBSD: fstest_ffs.c,v 1.7 2020/06/17 00:16:21 kamil Exp $ */ /*- * Copyright (c) 2010 The NetBSD Foundation, Inc. @@ -42,6 +42,7 @@ #include #include +#include #include #include "h_fsmacros.h" diff --git a/fs/common/fstest_lfs.c b/fs/common/fstest_lfs.c --- a/fs/common/fstest_lfs.c +++ b/fs/common/fstest_lfs.c @@ -1,4 +1,4 @@ -/* $NetBSD: fstest_lfs.c,v 1.5 2015/08/30 18:27:26 dholland Exp $ */ +/* $NetBSD: fstest_lfs.c,v 1.8 2020/06/17 00:16:21 kamil Exp $ */ /*- * Copyright (c) 2010 The NetBSD Foundation, Inc. @@ -43,6 +43,7 @@ #include #include +#include #include #include "h_fsmacros.h" @@ -125,9 +126,11 @@ { char thepath[MAXPATHLEN]; struct lfstestargs *args = arg; - const char *the_argv[7]; + const char *the_argv[9]; char buf[64]; + rump_pub_lwproc_newlwp(rump_sys_getpid()); + /* this inspired by the cleaner code. fixme */ sprintf(thepath, "/dev/r%s", args->ta_devpath+5); rump_pub_etfs_register(thepath, args->ta_hostpath, RUMP_ETFS_CHR); @@ -137,14 +140,18 @@ the_argv[1] = "-D"; /* don't fork() & detach */ the_argv[2] = "-S"; the_argv[3] = buf; - the_argv[4] = args->ta_mntpath; - the_argv[5] = NULL; + the_argv[4] = "-J"; + the_argv[5] = thepath; + the_argv[6] = args->ta_mntpath; + the_argv[7] = NULL; /* xxxatf */ optind = 1; opterr = 1; - lfs_cleaner_main(5, __UNCONST(the_argv)); + lfs_cleaner_main(7, __UNCONST(the_argv)); + + rump_pub_lwproc_releaselwp(); return NULL; } diff --git a/fs/common/fstest_msdosfs.c b/fs/common/fstest_msdosfs.c --- a/fs/common/fstest_msdosfs.c +++ b/fs/common/fstest_msdosfs.c @@ -1,4 +1,4 @@ -/* $NetBSD: fstest_msdosfs.c,v 1.3 2012/03/26 15:10:26 njoly Exp $ */ +/* $NetBSD: fstest_msdosfs.c,v 1.4 2020/06/17 00:16:21 kamil Exp $ */ /*- * Copyright (c) 2010 The NetBSD Foundation, Inc. @@ -41,6 +41,7 @@ #include #include +#include #include #include "h_fsmacros.h" diff --git a/fs/common/fstest_nfs.c b/fs/common/fstest_nfs.c --- a/fs/common/fstest_nfs.c +++ b/fs/common/fstest_nfs.c @@ -1,4 +1,4 @@ -/* $NetBSD: fstest_nfs.c,v 1.9 2011/02/28 21:08:46 pooka Exp $ */ +/* $NetBSD: fstest_nfs.c,v 1.12 2020/06/17 00:16:21 kamil Exp $ */ /* * Copyright (c) 2010 The NetBSD Foundation, Inc. @@ -46,6 +46,7 @@ #include #include +#include #include #include "h_fsmacros.h" @@ -87,10 +88,12 @@ * First, we start the nfs service. */ srcdir = atf_tc_get_config_var(tc, "srcdir"); - sprintf(nfsdpath, "%s/../nfs/nfsservice/rumpnfsd", srcdir); - sprintf(ethername, "/%s/%s.etherbus", getcwd(cwd, sizeof(cwd)), image); - sprintf(ethername_ro, "%s_ro", ethername); - sprintf(imagepath, "/%s/%s", cwd, image); + snprintf(nfsdpath, sizeof nfsdpath, + "%s/../nfs/nfsservice/rumpnfsd", srcdir); + snprintf(ethername, sizeof ethername, + "/%s/%s.etherbus", getcwd(cwd, sizeof(cwd)), image); + snprintf(ethername_ro, sizeof ethername_ro, "%s_ro", ethername); + snprintf(imagepath, sizeof imagepath, "/%s/%s", cwd, image); nfsdargv[0] = nfsdpath; nfsdargv[1] = ethername; @@ -113,8 +116,8 @@ close(pipes[0]); if (dup2(pipes[1], 3) == -1) err(1, "dup2"); - if (execvp(nfsdargv[0], nfsdargv) == -1) - err(1, "execvp"); + execvp(nfsdargv[0], nfsdargv); + err(1, "execvp"); case -1: return errno; default: diff --git a/fs/common/fstest_puffs.c b/fs/common/fstest_puffs.c --- a/fs/common/fstest_puffs.c +++ b/fs/common/fstest_puffs.c @@ -1,4 +1,4 @@ -/* $NetBSD: fstest_puffs.c,v 1.11 2013/09/09 19:47:38 pooka Exp $ */ +/* $NetBSD: fstest_puffs.c,v 1.13 2020/06/17 00:16:21 kamil Exp $ */ /* * Copyright (c) 2010, 2011 The NetBSD Foundation, Inc. @@ -46,6 +46,7 @@ #include #include +#include #include #include "h_fsmacros.h" @@ -277,8 +278,8 @@ if (setenv("PUFFS_COMFD", comfd, 1) == -1) return errno; - if (execvp(theargv[0], theargv) == -1) - return errno; + execvp(theargv[0], theargv); + return errno; case -1: return errno; default: diff --git a/fs/common/fstest_rumpfs.c b/fs/common/fstest_rumpfs.c --- a/fs/common/fstest_rumpfs.c +++ b/fs/common/fstest_rumpfs.c @@ -1,4 +1,4 @@ -/* $NetBSD: fstest_rumpfs.c,v 1.2 2014/03/16 10:28:03 njoly Exp $ */ +/* $NetBSD: fstest_rumpfs.c,v 1.3 2020/06/17 00:16:21 kamil Exp $ */ /*- * Copyright (c) 2010 The NetBSD Foundation, Inc. @@ -39,6 +39,7 @@ #include #include +#include #include #include "h_fsmacros.h" diff --git a/fs/common/fstest_sysvbfs.c b/fs/common/fstest_sysvbfs.c --- a/fs/common/fstest_sysvbfs.c +++ b/fs/common/fstest_sysvbfs.c @@ -1,4 +1,4 @@ -/* $NetBSD: fstest_sysvbfs.c,v 1.2 2010/07/30 16:15:05 pooka Exp $ */ +/* $NetBSD: fstest_sysvbfs.c,v 1.3 2020/06/17 00:16:21 kamil Exp $ */ /*- * Copyright (c) 2010 The NetBSD Foundation, Inc. @@ -41,6 +41,7 @@ #include #include +#include #include #include "h_fsmacros.h" diff --git a/fs/common/fstest_tmpfs.c b/fs/common/fstest_tmpfs.c --- a/fs/common/fstest_tmpfs.c +++ b/fs/common/fstest_tmpfs.c @@ -1,4 +1,4 @@ -/* $NetBSD: fstest_tmpfs.c,v 1.2 2010/07/30 16:15:05 pooka Exp $ */ +/* $NetBSD: fstest_tmpfs.c,v 1.3 2020/06/17 00:16:21 kamil Exp $ */ /*- * Copyright (c) 2010 The NetBSD Foundation, Inc. @@ -41,6 +41,7 @@ #include #include +#include #include #include "h_fsmacros.h" diff --git a/fs/common/fstest_udf.c b/fs/common/fstest_udf.c --- a/fs/common/fstest_udf.c +++ b/fs/common/fstest_udf.c @@ -1,4 +1,4 @@ -/* $NetBSD: fstest_udf.c,v 1.4 2013/07/02 15:00:55 reinoud Exp $ */ +/* $NetBSD: fstest_udf.c,v 1.5 2020/06/17 00:16:21 kamil Exp $ */ /*- * Copyright (c) 2010 The NetBSD Foundation, Inc. @@ -42,6 +42,7 @@ #include #include +#include #include #include "h_fsmacros.h" diff --git a/fs/common/fstest_v7fs.c b/fs/common/fstest_v7fs.c --- a/fs/common/fstest_v7fs.c +++ b/fs/common/fstest_v7fs.c @@ -1,4 +1,4 @@ -/* $NetBSD: fstest_v7fs.c,v 1.1 2011/08/11 10:52:12 uch Exp $ */ +/* $NetBSD: fstest_v7fs.c,v 1.2 2020/06/17 00:16:21 kamil Exp $ */ /*- * Copyright (c) 2011 The NetBSD Foundation, Inc. @@ -41,6 +41,7 @@ #include #include +#include #include #include "h_fsmacros.h" diff --git a/fs/common/fstest_zfs.c b/fs/common/fstest_zfs.c --- a/fs/common/fstest_zfs.c +++ b/fs/common/fstest_zfs.c @@ -1,4 +1,4 @@ -/* $NetBSD: fstest_zfs.c,v 1.1 2012/08/20 16:37:35 pooka Exp $ */ +/* $NetBSD: fstest_zfs.c,v 1.3 2020/06/17 00:16:21 kamil Exp $ */ /*- * Copyright (c) 2010, 2011 The NetBSD Foundation, Inc. @@ -39,6 +39,7 @@ #include #include +#include #include #include "h_fsmacros.h" @@ -104,8 +105,8 @@ /* set up the hijack env for running zpool */ setenv("RUMP_SERVER", SRVURL, 1); - snprintf(tmpbuf, sizeof(tmpbuf)-1, "blanket=/dev/zfs:%s:%s", - ZFSDEV, path); + snprintf(tmpbuf, sizeof(tmpbuf)-1, + "blanket=/dev/zfs:%s:%s,sysctl=yes,modctl=yes", ZFSDEV, path); setenv("RUMPHIJACK", tmpbuf, 1); setenv("LD_PRELOAD", "/usr/lib/librumphijack.so", 1); diff --git a/fs/common/h_fsmacros.h b/fs/common/h_fsmacros.h --- a/fs/common/h_fsmacros.h +++ b/fs/common/h_fsmacros.h @@ -1,4 +1,4 @@ -/* $NetBSD: h_fsmacros.h,v 1.41 2017/01/13 21:30:39 christos Exp $ */ +/* $NetBSD: h_fsmacros.h,v 1.44 2020/03/15 20:10:26 martin Exp $ */ /*- * Copyright (c) 2010 The NetBSD Foundation, Inc. @@ -77,12 +77,16 @@ #define FSTEST_CONSTRUCTOR(_tc_, _fs_, _args_) \ do { \ + struct statvfs fsstat; \ + if (statvfs(".", &fsstat) == 0 && \ + (fsstat.f_frsize * fsstat.f_bfree) <= FSTEST_IMGSIZE) \ + atf_tc_skip("not enough free space in work directory"); \ if (_fs_##_fstest_newfs(_tc_, &_args_, \ FSTEST_IMGNAME, FSTEST_IMGSIZE, NULL) != 0) \ atf_tc_fail_errno("newfs failed"); \ if (_fs_##_fstest_mount(_tc_, _args_, FSTEST_MNTNAME, 0) != 0) \ atf_tc_fail_errno("mount failed"); \ -} while (/*CONSTCOND*/0); +} while (/*CONSTCOND*/0) #define FSTEST_CONSTRUCTOR_FSPRIV(_tc_, _fs_, _args_, _privargs_) \ do { \ @@ -91,7 +95,7 @@ atf_tc_fail_errno("newfs failed"); \ if (_fs_##_fstest_mount(_tc_, _args_, FSTEST_MNTNAME, 0) != 0) \ atf_tc_fail_errno("mount failed"); \ -} while (/*CONSTCOND*/0); +} while (/*CONSTCOND*/0) #define FSTEST_DESTRUCTOR(_tc_, _fs_, _args_) \ do { \ @@ -101,7 +105,7 @@ } \ if (_fs_##_fstest_delfs(_tc_, _args_) != 0) \ atf_tc_fail_errno("delfs failed"); \ -} while (/*CONSTCOND*/0); +} while (/*CONSTCOND*/0) #define ATF_TC_FSADD(fs,type,func,desc) \ ATF_TC(fs##_##func); \ @@ -249,6 +253,14 @@ atf_check_fstype(const atf_tc_t *tc, const char *fs) { const char *fstype; + struct statvfs fsstat; + + if (strcmp(fs, "zfs") == 0) { + /* XXX ZFS hardcodes a minimal size */ + if (statvfs(".", &fsstat) == 0 && + (fsstat.f_frsize * fsstat.f_bfree) <= 64*1024*1024) + atf_tc_skip("not enough free space in work directory"); + } if (!atf_tc_has_config_var(tc, "fstype")) return true; diff --git a/fs/common/snapshot.c b/fs/common/snapshot.c --- a/fs/common/snapshot.c +++ b/fs/common/snapshot.c @@ -1,4 +1,4 @@ -/* $NetBSD: snapshot.c,v 1.7 2013/02/06 09:05:01 hannken Exp $ */ +/* $NetBSD: snapshot.c,v 1.8 2019/07/09 16:24:01 maya Exp $ */ #include #include @@ -224,5 +224,5 @@ { ATF_TP_ADD_TC(tp, snapshot); ATF_TP_ADD_TC(tp, snapshotstress); - return 0; + return atf_no_error(); } diff --git a/fs/ffs/Makefile b/fs/ffs/Makefile --- a/fs/ffs/Makefile +++ b/fs/ffs/Makefile @@ -1,6 +1,7 @@ -# $NetBSD: Makefile,v 1.18 2015/01/07 22:24:03 pooka Exp $ +# $NetBSD: Makefile,v 1.22 2020/05/15 23:32:28 christos Exp $ # +RUMPFIFO=yes # use the real rump fifofs .include TESTSDIR= ${TESTSBASE}/fs/ffs @@ -20,6 +21,7 @@ TESTS_SH_SRC_${name}= ffs_common.sh quotas_common.sh ${name}.sh .endfor +TESTS_C+= t_extattr TESTS_C+= t_fifos TESTS_C+= t_snapshot TESTS_C+= t_snapshot_log @@ -27,14 +29,13 @@ TESTS_C+= t_mount TESTS_C+= t_quota2_1 TESTS_C+= t_quota2_remount +TESTS_C+= t_update_log LDADD+=-lrumpfs_ffs # ffs LDADD+=-lrumpdev_fss # snapshot dev LDADD+=-lrumpdev_disk -lrumpdev # disk device -LDADD+=-lrumpvfs_fifofs -lrumpnet_local -lrumpnet_net -lrumpnet # fifos VFSTESTDIR != cd ${.CURDIR}/../common && ${PRINTOBJDIR} LDADD+=-L${VFSTESTDIR} -lvfstest -LDADD+=-lrumpvfs -lrumpkern_sysproxy -lrump -lrumpuser # base -LDADD+=-lpthread +LDADD+=-lrumpkern_sysproxy ${LIBRUMPBASE} .include diff --git a/fs/ffs/ffs_common.sh b/fs/ffs/ffs_common.sh old mode 100755 new mode 100644 --- a/fs/ffs/ffs_common.sh +++ b/fs/ffs/ffs_common.sh @@ -1,4 +1,4 @@ -# $NetBSD: ffs_common.sh,v 1.3 2016/10/08 13:23:53 gson Exp $ +# $NetBSD: ffs_common.sh,v 1.5 2020/08/20 07:23:20 gson Exp $ create_ffs() { @@ -23,17 +23,6 @@ ${sarg} ${IMG} ${RUMP_SERVER} } -rump_shutdown() -{ - for s in ${RUMP_SOCKETS_LIST}; do - atf_check -s exit:0 env RUMP_SERVER=unix://${s} rump.halt; - done -# check that the quota inode creation didn't corrupt the filesystem - atf_check -s exit:0 -o "match:already clean" \ - -o "match:Phase 6 - Check Quotas" \ - fsck_ffs -nf -F ${IMG} -} - # from tests/ipf/h_common.sh via tests/sbin/resize_ffs test_case() { @@ -41,7 +30,7 @@ local check_function="${1}"; shift local descr="${1}"; shift - atf_test_case "${name}" cleanup + atf_test_case "${name}" eval "${name}_head() { \ atf_set "descr" "${descr}" @@ -52,12 +41,6 @@ export RUMP_SERVER=unix://\${RUMP_SOCKET}; \ ${check_function} " "${@}" "; \ }" - eval "${name}_cleanup() { \ - for s in \${RUMP_SOCKETS_LIST}; do \ - export RUMP_SERVER=unix://\${s}; \ - atf_check -s exit:1 -o ignore -e ignore rump.halt; \ - done; \ - }" tests="${tests} ${name}" } @@ -67,7 +50,7 @@ local check_function="${1}"; shift local descr="${1}"; shift - atf_test_case "${name}" cleanup + atf_test_case "${name}" eval "${name}_head() { \ atf_set "descr" "${descr}" @@ -79,12 +62,6 @@ export RUMP_SERVER=unix://\${RUMP_SOCKET}; \ ${check_function} " "${@}" "; \ }" - eval "${name}_cleanup() { \ - for s in \${RUMP_SOCKETS_LIST}; do \ - export RUMP_SERVER=unix://\${s}; \ - atf_check -s exit:1 -o ignore -e ignore rump.halt; \ - done; \ - }" tests="${tests} ${name}" } diff --git a/fs/ffs/quotas_common.sh b/fs/ffs/quotas_common.sh old mode 100755 new mode 100644 diff --git a/fs/ffs/t_clearquota.sh b/fs/ffs/t_clearquota.sh old mode 100755 new mode 100644 diff --git a/fs/ffs/t_extattr.c b/fs/ffs/t_extattr.c new file mode 100644 --- /dev/null +++ b/fs/ffs/t_extattr.c @@ -0,0 +1,230 @@ +/* $NetBSD: t_extattr.c,v 1.2 2020/04/12 23:52:20 christos Exp $ */ + +/*- + * Copyright (c) 2020 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +__RCSID("$NetBSD: t_extattr.c,v 1.2 2020/04/12 23:52:20 christos Exp $"); + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "h_macros.h" + +#define IMGNAME "extattr.img" + +#define G "/garage" +#define M "mercedes" +#define C "carburator" +#define E "engine" +#define T "trunk" +#define S "suitcase" + +static const char *attrs[] = { E, T }; + +static void +check_list(const char *buf, ssize_t nr) +{ + size_t p = 0; + for (size_t i = 0; i < __arraycount(attrs); i++) { + size_t l = buf[p++]; + if (l != strlen(attrs[i])) + atf_tc_fail("got invalid len %zu expected %zu", l, + strlen(attrs[i])); + if (strncmp(&buf[p], attrs[i], l) != 0) + atf_tc_fail("got invalid str %.*s expected %s", (int)l, + &buf[p], attrs[i]); + p += l; + } +} + +// Make it ffsv2 +const char *newfs = "newfs -O 2 -F -s 10000 " IMGNAME; +#define FAKEBLK "/dev/formula1" + +static void +start(void) { + struct ufs_args args; + + if (system(newfs) == -1) + atf_tc_fail_errno("newfs failed"); + + memset(&args, 0, sizeof(args)); + args.fspec = __UNCONST(FAKEBLK); + + rump_init(); + if (rump_sys_mkdir(G, 0777) == -1) + atf_tc_fail_errno("cannot create mountpoint"); + rump_pub_etfs_register(FAKEBLK, IMGNAME, RUMP_ETFS_BLK); + if (rump_sys_mount(MOUNT_FFS, G, 0, &args, sizeof(args))==-1) + atf_tc_fail_errno("rump_sys_mount failed"); + + /* create extattr */ + if (rump_sys_chdir(G) == 1) + atf_tc_fail_errno("chdir"); +} + +static void +finish(void) { + if (rump_sys_chdir("/") == 1) + atf_tc_fail_errno("chdir"); + if (rump_sys_unmount(G, 0) == -1) + atf_tc_fail_errno("unmount failed"); +} + + +ATF_TC_WITH_CLEANUP(extattr_simple); +ATF_TC_HEAD(extattr_simple, tc) +{ + atf_tc_set_md_var(tc, "descr", "test extended attribute creation" + " and removal ffsv2"); + atf_tc_set_md_var(tc, "timeout", "5"); +} + +ATF_TC_BODY(extattr_simple, tc) +{ + ssize_t nr; + int fd; + char buf[512]; + + start(); + + if ((fd = rump_sys_open(M, O_RDWR | O_CREAT, 0600)) == -1) + atf_tc_fail_errno("open"); + if (rump_sys_write(fd, "hi mom\n", 7) != 7) + atf_tc_fail_errno("write"); + + if (rump_sys_extattr_set_fd(fd, EXTATTR_NAMESPACE_USER, E, C, 11) == -1) + atf_tc_fail_errno("exattr_set_fd"); + if ((nr = rump_sys_extattr_get_file(M, EXTATTR_NAMESPACE_USER, + E, buf, sizeof(buf))) != 11) + atf_tc_fail_errno("extattr_get_file"); + if (strcmp(buf, C) != 0) + atf_tc_fail("got invalid str %s expected %s", buf, C); + + if (rump_sys_extattr_set_file(M, EXTATTR_NAMESPACE_USER, T, S, 9) == -1) + atf_tc_fail_errno("extattr_set_file"); + if ((nr = rump_sys_extattr_get_fd(fd, EXTATTR_NAMESPACE_USER, + T, buf, sizeof(buf))) != 9) + atf_tc_fail_errno("extattr_get_fd"); + if (strcmp(buf, S) != 0) + atf_tc_fail("got invalid str %s expected %s", buf, S); + + if ((nr = rump_sys_extattr_list_fd(fd, EXTATTR_NAMESPACE_USER, + buf, sizeof(buf))) != 13) + atf_tc_fail_errno("extattr_list_fd %zd", nr); + check_list(buf, __arraycount(attrs)); + + if ((nr = rump_sys_extattr_list_file(M, EXTATTR_NAMESPACE_USER, + buf, sizeof(buf))) != 13) + atf_tc_fail_errno("extattr_list_file %zd", nr); + check_list(buf, __arraycount(attrs)); + + if (rump_sys_extattr_delete_fd(fd, EXTATTR_NAMESPACE_USER, T) == -1) + atf_tc_fail_errno("extattr_delete_fd"); + if ((nr = rump_sys_extattr_list_fd(fd, EXTATTR_NAMESPACE_USER, + buf, sizeof(buf))) != 7) + atf_tc_fail_errno("extattr_list_fd %zd", nr); + check_list(buf, 1); + + if (rump_sys_extattr_delete_file(M, EXTATTR_NAMESPACE_USER, E) == -1) + atf_tc_fail_errno("extattr_delete_file"); + if ((nr = rump_sys_extattr_list_fd(fd, EXTATTR_NAMESPACE_USER, + buf, sizeof(buf))) != 0) + atf_tc_fail_errno("extattr_list_fd %zd", nr); + + if (rump_sys_close(fd) == -1) + atf_tc_fail_errno("close"); + if (rump_sys_unlink(M) == -1) + atf_tc_fail_errno("unlink"); + + finish(); +} + +ATF_TC_CLEANUP(extattr_simple, tc) +{ + + unlink(IMGNAME); +} + +ATF_TC_WITH_CLEANUP(extattr_create_unlink); +ATF_TC_HEAD(extattr_create_unlink, tc) +{ + atf_tc_set_md_var(tc, "descr", "test extended attribute creation" + " and unlinking file with ACLs"); + atf_tc_set_md_var(tc, "timeout", "5"); +} + +ATF_TC_BODY(extattr_create_unlink, tc) +{ + int fd; + + start(); + if ((fd = rump_sys_open(M, O_RDWR | O_CREAT, 0600)) == -1) + atf_tc_fail_errno("open"); + + if (rump_sys_close(fd) == -1) + atf_tc_fail_errno("close"); + + /* create extattr */ + if (rump_sys_extattr_set_file(M, EXTATTR_NAMESPACE_USER, T, S, 9) == -1) + atf_tc_fail_errno("extattr_set_file"); + + if (rump_sys_unlink(M) == -1) + atf_tc_fail_errno("unlink"); + finish(); + +} + +ATF_TC_CLEANUP(extattr_create_unlink, tc) +{ + + unlink(IMGNAME); +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, extattr_simple); + ATF_TP_ADD_TC(tp, extattr_create_unlink); + return atf_no_error(); +} diff --git a/fs/ffs/t_fifos.c b/fs/ffs/t_fifos.c --- a/fs/ffs/t_fifos.c +++ b/fs/ffs/t_fifos.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_fifos.c,v 1.6 2017/01/13 21:30:39 christos Exp $ */ +/* $NetBSD: t_fifos.c,v 1.7 2019/07/09 16:24:01 maya Exp $ */ #include #include @@ -154,5 +154,5 @@ ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, fifos); - return 0; + return atf_no_error(); } diff --git a/fs/ffs/t_getquota.sh b/fs/ffs/t_getquota.sh old mode 100755 new mode 100644 diff --git a/fs/ffs/t_miscquota.sh b/fs/ffs/t_miscquota.sh old mode 100755 new mode 100644 diff --git a/fs/ffs/t_quota2_remount.c b/fs/ffs/t_quota2_remount.c --- a/fs/ffs/t_quota2_remount.c +++ b/fs/ffs/t_quota2_remount.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_quota2_remount.c,v 1.5 2017/01/13 21:30:39 christos Exp $ */ +/* $NetBSD: t_quota2_remount.c,v 1.6 2021/08/20 20:25:28 andvar Exp $ */ /* * Basic tests for quota2 @@ -44,7 +44,7 @@ uargs.fspec = __UNCONST("/diskdev"); - /* read-only doens't have quota enabled */ + /* read-only doesn't have quota enabled */ if (rump_sys_mount(MOUNT_FFS, FSTEST_MNTNAME, MNT_RDONLY, &uargs, sizeof(uargs)) == -1) atf_tc_fail_errno("mount ffs ro %s", FSTEST_MNTNAME); diff --git a/fs/ffs/t_quotalimit.sh b/fs/ffs/t_quotalimit.sh old mode 100755 new mode 100644 diff --git a/fs/ffs/t_setquota.sh b/fs/ffs/t_setquota.sh old mode 100755 new mode 100644 diff --git a/fs/ffs/t_update_log.c b/fs/ffs/t_update_log.c new file mode 100644 --- /dev/null +++ b/fs/ffs/t_update_log.c @@ -0,0 +1,148 @@ +/* $NetBSD: t_update_log.c,v 1.1 2017/03/22 21:33:53 jdolecek Exp $ */ + +/* + * Check log behaviour on mount updates + */ + +#include + +#define FSTEST_IMGSIZE (96 * 512) +#include "../common/h_fsmacros.h" + +#include +#include + +#include + +#include + +#include +#include + +#include "h_macros.h" + +ATF_TC(updaterwtolog); +ATF_TC_HEAD(updaterwtolog, tc) +{ + + atf_tc_set_md_var(tc, "descr", "mounts file system with " + "rw, then rw+log"); +} + +/* + * PR kern/52056 + * This doesn't trigger panic with old, despite same operations triggering + * it with live kernel. + * Steps: + * 1. boot singleuser + * 2. mount -u / + * 3. echo abc > abc + * 4. mount -u -o log / + * 5. echo abc >> abc + * + * Keeping around just in case. + */ +ATF_TC_BODY(updaterwtolog, tc) +{ + char buf[1024]; + struct ufs_args args; + int n = 10, fd; + const char *newfs_args = "-O2"; + const char teststring[] = "6c894efc21094525ca1f974c0189b3b0c4e1f28d"; + + snprintf(buf, sizeof(buf), "newfs -q user -q group -F -s 4000 -n %d " + "%s %s", (n + 3), newfs_args, FSTEST_IMGNAME); + if (system(buf) == -1) + atf_tc_fail_errno("cannot create file system"); + + rump_init(); + if (rump_sys_mkdir(FSTEST_MNTNAME, 0777) == -1) + atf_tc_fail_errno("mount point create"); + + rump_pub_etfs_register("/diskdev", FSTEST_IMGNAME, RUMP_ETFS_BLK); + + args.fspec = __UNCONST("/diskdev"); + + if (rump_sys_mount(MOUNT_FFS, FSTEST_MNTNAME, MNT_RDONLY, + &args, sizeof(args)) == -1) + atf_tc_fail_errno("mount ffs rw %s", FSTEST_MNTNAME); + + if (rump_sys_chdir(FSTEST_MNTNAME) == 1) + atf_tc_fail_errno("chdir"); + + if (rump_sys_mount(MOUNT_FFS, FSTEST_MNTNAME, MNT_UPDATE, + &args, sizeof(args)) == -1) + atf_tc_fail_errno("mount ffs rw %s", FSTEST_MNTNAME); + + RL(fd = rump_sys_open("dummy", O_CREAT | O_RDWR, 0755)); + RL(rump_sys_write(fd, teststring, strlen(teststring))); + rump_sys_close(fd); + + if (rump_sys_mount(MOUNT_FFS, FSTEST_MNTNAME, MNT_LOG|MNT_UPDATE, + &args, sizeof(args)) == -1) + atf_tc_fail_errno("mount ffs rw log update %s", FSTEST_MNTNAME); + + RL(fd = rump_sys_open("dummy", O_APPEND | O_RDWR, 0755)); + RL(rump_sys_write(fd, teststring, strlen(teststring))); + rump_sys_close(fd); + + /* otherwise we're do-ne */ +} + +ATF_TC(updaterwtolog_async); +ATF_TC_HEAD(updaterwtolog_async, tc) +{ + + atf_tc_set_md_var(tc, "descr", "mounts file system with " + "rw+async, then rw+async+log"); +} + +/* + * PR kern/52056 + */ +ATF_TC_BODY(updaterwtolog_async, tc) +{ + char buf[1024]; + struct ufs_args args; + int n = 10, fd; + const char *newfs_args = "-O2"; + + snprintf(buf, sizeof(buf), "newfs -q user -q group -F -s 4000 -n %d " + "%s %s", (n + 3), newfs_args, FSTEST_IMGNAME); + if (system(buf) == -1) + atf_tc_fail_errno("cannot create file system"); + + rump_init(); + if (rump_sys_mkdir(FSTEST_MNTNAME, 0777) == -1) + atf_tc_fail_errno("mount point create"); + + rump_pub_etfs_register("/diskdev", FSTEST_IMGNAME, RUMP_ETFS_BLK); + + args.fspec = __UNCONST("/diskdev"); + + if (rump_sys_mount(MOUNT_FFS, FSTEST_MNTNAME, 0, + &args, sizeof(args)) == -1) + atf_tc_fail_errno("mount ffs rw %s", FSTEST_MNTNAME); + + if (rump_sys_chdir(FSTEST_MNTNAME) == 1) + atf_tc_fail_errno("chdir"); + + RL(fd = rump_sys_open("dummy", O_CREAT | O_RDWR, 0755)); + sprintf(buf, "test file"); + RL(rump_sys_write(fd, buf, strlen(buf))); + rump_sys_close(fd); + + if (rump_sys_mount(MOUNT_FFS, FSTEST_MNTNAME, MNT_LOG|MNT_ASYNC|MNT_UPDATE, + &args, sizeof(args)) == -1) + atf_tc_fail_errno("mount ffs rw log update %s", FSTEST_MNTNAME); + + /* otherwise we're do-ne */ +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, updaterwtolog); + ATF_TP_ADD_TC(tp, updaterwtolog_async); + + return atf_no_error(); +} diff --git a/fs/hfs/Makefile b/fs/hfs/Makefile --- a/fs/hfs/Makefile +++ b/fs/hfs/Makefile @@ -1,6 +1,8 @@ -# $NetBSD: Makefile,v 1.1 2011/02/18 13:07:54 pooka Exp $ +# $NetBSD: Makefile,v 1.2 2020/03/01 18:08:13 christos Exp $ # +.include + TESTSDIR= ${TESTSBASE}/fs/hfs TESTS_C= t_pathconvert @@ -10,6 +12,6 @@ LDADD+= -lrumpfs_hfs # fs drivers LDADD+= -lrumpdev_disk -lrumpdev # disk drivers -LDADD+= -lrumpvfs -lrump -lrumpuser -lpthread # base +LDADD+= ${LIBRUMPBASE} .include diff --git a/fs/hfs/t_pathconvert.c b/fs/hfs/t_pathconvert.c --- a/fs/hfs/t_pathconvert.c +++ b/fs/hfs/t_pathconvert.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_pathconvert.c,v 1.6 2017/01/13 21:30:40 christos Exp $ */ +/* $NetBSD: t_pathconvert.c,v 1.7 2019/07/09 16:24:01 maya Exp $ */ #include #include @@ -79,5 +79,5 @@ ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, colonslash); - return 0; + return atf_no_error(); } diff --git a/fs/kernfs/Makefile b/fs/kernfs/Makefile --- a/fs/kernfs/Makefile +++ b/fs/kernfs/Makefile @@ -1,14 +1,15 @@ -# $NetBSD: Makefile,v 1.4 2014/06/10 04:28:39 he Exp $ +# $NetBSD: Makefile,v 1.5 2020/03/01 18:08:13 christos Exp $ # +.include + TESTSDIR= ${TESTSBASE}/fs/kernfs WARNS= 4 TESTS_C= t_basic -.include LDADD+= -lrumpfs_kernfs # fs driver -LDADD+= -lrumpvfs -lrump -lrumpuser -lrump -lpthread # base +LDADD+= ${LIBRUMPBASE} .include diff --git a/fs/lfs/Makefile b/fs/lfs/Makefile --- a/fs/lfs/Makefile +++ b/fs/lfs/Makefile @@ -1,13 +1,15 @@ -# $NetBSD: Makefile,v 1.1 2010/06/28 09:45:06 pooka Exp $ +# $NetBSD: Makefile,v 1.3 2020/08/18 03:02:50 perseant Exp $ # +.include + TESTSDIR= ${TESTSBASE}/fs/lfs WARNS= 4 -TESTS_C= t_pr +TESTS_C= t_pr t_rfw LDADD+=-lrumpfs_lfs -lrumpfs_ffs # fs drivers LDADD+=-lrumpdev_disk -lrumpdev # disk device -LDADD+=-lrumpvfs -lrump -lrumpuser -lpthread # base +LDADD+=${LIBRUMPBASE} .include diff --git a/fs/lfs/t_pr.c b/fs/lfs/t_pr.c --- a/fs/lfs/t_pr.c +++ b/fs/lfs/t_pr.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_pr.c,v 1.7 2017/01/13 21:30:40 christos Exp $ */ +/* $NetBSD: t_pr.c,v 1.8 2019/07/09 16:24:01 maya Exp $ */ #include #include @@ -56,5 +56,5 @@ { ATF_TP_ADD_TC(tp, mknod); - return 0; + return atf_no_error(); } diff --git a/fs/lfs/t_rfw.c b/fs/lfs/t_rfw.c new file mode 100644 --- /dev/null +++ b/fs/lfs/t_rfw.c @@ -0,0 +1,324 @@ +/* $NetBSD: t_rfw.c,v 1.3 2020/08/23 22:34:29 riastradh Exp $ */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include "h_macros.h" + +/* Debugging conditions */ +/* #define FORCE_SUCCESS */ /* Don't actually revert, everything worked */ + +/* Write a well-known byte pattern into a file, appending if it exists */ +int write_file(const char *, int); + +/* Check the byte pattern and size of the file */ +int check_file(const char *, int); + +/* Check the file system for consistency */ +int fsck(void); + +/* Actually run the test, differentiating the orphaned delete problem */ +void test(int); + +ATF_TC(rfw); +ATF_TC_HEAD(rfw, tc) +{ + atf_tc_set_md_var(tc, "descr", + "LFS roll-forward creates an inconsistent filesystem"); + atf_tc_set_md_var(tc, "timeout", "20"); +} + +#define MAXLINE 132 +#define CHUNKSIZE 300 + +#define IMGNAME "disk.img" +#define FAKEBLK "/dev/blk" +#define LOGFILE "newfs.log" +#define SBLOCK0_COPY "sb0.dd" +#define SBLOCK1_COPY "sb1.dd" + +#define MP "/mp" +#define UNCHANGED_CONTROL MP "/3-unchanged-control" +#define TO_BE_DELETED MP "/4-to-be-deleted" +#define TO_BE_APPENDED MP "/5-to-be-appended" +#define NEWLY_CREATED MP "/6-newly-created" + +long long sbaddr[2] = { -1, -1 }; +const char *sblock[2] = { SBLOCK0_COPY, SBLOCK1_COPY }; + +ATF_TC_BODY(rfw, tc) +{ + struct ufs_args args; + FILE *pipe; + char buf[MAXLINE]; + int i; + + setvbuf(stdout, NULL, _IONBF, 0); + + /* + * Initialize. + */ + atf_tc_expect_fail("roll-forward not yet implemented"); + + /* Create filesystem, note superblock locations */ + fprintf(stderr, "* Create file system\n"); + if (system("newfs_lfs -D -F -s 10000 ./" IMGNAME " > " LOGFILE) == -1) + atf_tc_fail_errno("newfs failed"); + pipe = fopen(LOGFILE, "r"); + if (pipe == NULL) + atf_tc_fail_errno("newfs failed to execute"); + while (fgets(buf, MAXLINE, pipe) != NULL) { + if (sscanf(buf, "%lld,%lld", sbaddr, sbaddr + 1) == 2) + break; + } + while (fgets(buf, MAXLINE, pipe) != NULL) + ; + fclose(pipe); + if (sbaddr[0] < 0 || sbaddr[1] < 0) + atf_tc_fail("superblock not found"); + fprintf(stderr, "* Superblocks at %lld and %lld\n", + sbaddr[0], sbaddr[1]); + + /* Set up rump */ + rump_init(); + if (rump_sys_mkdir(MP, 0777) == -1) + atf_tc_fail_errno("cannot create mountpoint"); + rump_pub_etfs_register(FAKEBLK, IMGNAME, RUMP_ETFS_BLK); + + /* + * Create initial filesystem state, from which + * we will attempt to roll forward. + */ + + /* Mount filesystem */ + fprintf(stderr, "* Mount fs [1, initial]\n"); + memset(&args, 0, sizeof(args)); + args.fspec = __UNCONST(FAKEBLK); + if (rump_sys_mount(MOUNT_LFS, MP, 0, &args, sizeof(args)) == -1) + atf_tc_fail_errno("rump_sys_mount failed"); + + /* Payload */ + fprintf(stderr, "* Initial payload\n"); + write_file(UNCHANGED_CONTROL, CHUNKSIZE); + write_file(TO_BE_DELETED, CHUNKSIZE); + write_file(TO_BE_APPENDED, CHUNKSIZE); + rump_sys_sync(); + rump_sys_sync(); + sleep(1); /* XXX yuck - but we need the superblocks dirty */ + + /* Make copies of superblocks */ + for (i = 0; i < 2; i++) { + sprintf(buf, "dd if=%s of=%s bs=512 iseek=%lld" + " count=16 conv=sync", IMGNAME, sblock[i], sbaddr[i]); + system(buf); + } + + /* Unmount */ + rump_sys_unmount(MP, 0); + + /* + * Make changes which we will attempt to roll forward. + */ + + /* Reconfigure and mount filesystem again */ + fprintf(stderr, "* Mount fs [2, after changes]\n"); + if (rump_sys_mount(MOUNT_LFS, MP, 0, &args, sizeof(args)) == -1) + atf_tc_fail_errno("rump_sys_mount failed [2]"); + + /* Add new file */ + write_file(NEWLY_CREATED, CHUNKSIZE); + + /* Append to existing file */ + write_file(TO_BE_APPENDED, CHUNKSIZE); + + /* Delete file */ + rump_sys_unlink(TO_BE_DELETED); + + /* Done with payload, unmount fs */ + rump_sys_unmount(MP, 0); + +#ifndef FORCE_SUCCESS + /* + * Copy back old superblocks, reverting FS to old state + */ + for (i = 0; i < 2; i++) { + sprintf(buf, "dd of=%s if=%s bs=512 oseek=%lld count=16" + " conv=sync,notrunc", IMGNAME, sblock[i], sbaddr[i]); + system(buf); + } + + if (fsck()) + atf_tc_fail_errno("fsck found errors with old superblocks"); +#endif + + /* + * Roll forward. + */ + + /* Mount filesystem; this will roll forward. */ + fprintf(stderr, "* Mount fs [3, to roll forward]\n"); + if (rump_sys_mount(MOUNT_LFS, MP, 0, &args, sizeof(args)) == -1) + atf_tc_fail_errno("rump_sys_mount failed [3]"); + + /* Unmount filesystem */ + if (rump_sys_unmount(MP, 0) != 0) + atf_tc_fail_errno("rump_sys_umount failed after roll-forward"); + + /* + * Use fsck_lfs to look for consistency errors. + */ + + if (fsck()) { + fprintf(stderr, "*** FAILED FSCK ***\n"); + atf_tc_fail("fsck found errors after roll forward"); + } + + /* + * Check file system contents + */ + + /* Mount filesystem one last time */ + fprintf(stderr, "* Mount fs [4, after roll forward complete]\n"); + if (rump_sys_mount(MOUNT_LFS, MP, 0, &args, sizeof(args)) == -1) + atf_tc_fail_errno("rump_sys_mount failed [4]"); + + if (check_file(UNCHANGED_CONTROL, CHUNKSIZE) != 0) + atf_tc_fail("Unchanged control file differs(!)"); + + if (check_file(TO_BE_APPENDED, 2 * CHUNKSIZE) != 0) + atf_tc_fail("Appended file differs"); + + if (rump_sys_access(NEWLY_CREATED, F_OK) != 0) + atf_tc_fail("Newly added file missing"); + + if (check_file(NEWLY_CREATED, CHUNKSIZE) != 0) + atf_tc_fail("Newly added file differs"); + + if (rump_sys_access(TO_BE_DELETED, F_OK) == 0) + atf_tc_fail("Removed file still present"); + + /* Umount filesystem */ + rump_sys_unmount(MP, 0); + + /* Final fsck to double check */ + if (fsck()) { + fprintf(stderr, "*** FAILED FSCK ***\n"); + atf_tc_fail("fsck found errors after final unmount"); + } +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, rfw); + return atf_no_error(); +} + +/* Write some data into a file */ +int write_file(const char *filename, int add) +{ + int fd, size, i; + struct stat statbuf; + unsigned char b; + int flags = O_CREAT|O_WRONLY; + + if (rump_sys_stat(filename, &statbuf) < 0) + size = 0; + else { + size = statbuf.st_size; + flags |= O_APPEND; + } + + fd = rump_sys_open(filename, flags); + + for (i = 0; i < add; i++) { + b = ((unsigned)(size + i)) & 0xff; + rump_sys_write(fd, &b, 1); + } + rump_sys_close(fd); + + return 0; +} + +/* Check file's existence, size and contents */ +int check_file(const char *filename, int size) +{ + int fd, i; + struct stat statbuf; + unsigned char b; + + if (rump_sys_stat(filename, &statbuf) < 0) { + fprintf(stderr, "%s: stat failed\n", filename); + return 1; + } + if (size != statbuf.st_size) { + fprintf(stderr, "%s: expected %d bytes, found %d\n", + filename, size, (int)statbuf.st_size); + return 2; + } + + fd = rump_sys_open(filename, O_RDONLY); + for (i = 0; i < size; i++) { + rump_sys_read(fd, &b, 1); + if (b != (((unsigned)i) & 0xff)) { + fprintf(stderr, "%s: byte %d: expected %x found %x\n", + filename, i, ((unsigned)(i)) & 0xff, b); + rump_sys_close(fd); + return 3; + } + } + rump_sys_close(fd); + fprintf(stderr, "%s: no problem\n", filename); + return 0; +} + +/* Run a file system consistency check */ +int fsck(void) +{ + char s[MAXLINE]; + int i, errors = 0; + FILE *pipe; + char cmd[MAXLINE]; + + for (i = 0; i < 2; i++) { + sprintf(cmd, "fsck_lfs -n -b %jd -f " IMGNAME, + (intmax_t)sbaddr[i]); + pipe = popen(cmd, "r"); + while (fgets(s, MAXLINE, pipe) != NULL) { + if (isdigit((int)s[0])) /* "5 files ... " */ + continue; + if (isspace((int)s[0]) || s[0] == '*') + continue; + if (strncmp(s, "Alternate", 9) == 0) + continue; + if (strncmp(s, "ROLL ", 5) == 0) + continue; + fprintf(stderr, "FSCK[sb@%lld]: %s", sbaddr[i], s); + ++errors; + } + pclose(pipe); + if (errors) { + break; + } + } + + return errors; +} diff --git a/fs/msdosfs/Makefile b/fs/msdosfs/Makefile --- a/fs/msdosfs/Makefile +++ b/fs/msdosfs/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.2 2015/01/07 23:12:31 pooka Exp $ +# $NetBSD: Makefile,v 1.3 2020/03/01 18:08:13 christos Exp $ # TESTSDIR= ${TESTSBASE}/fs/msdosfs @@ -9,7 +9,7 @@ LDADD+=-lrumpfs_msdos -lrumpfs_tmpfs # fs drivers LDADD+=-lrumpdev_fss # snapshot dev LDADD+=-lrumpdev_disk -lrumpdev # disk device -LDADD+=-lrumpvfs -lrumpkern_sysproxy -lrump -lrumpuser # base +LDADD+=-lrumpkern_sysproxy ${LIBRUMPBASE} LDADD+=-lpthread .include diff --git a/fs/nfs/Makefile b/fs/nfs/Makefile --- a/fs/nfs/Makefile +++ b/fs/nfs/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.10 2015/01/08 03:50:56 pooka Exp $ +# $NetBSD: Makefile,v 1.11 2020/03/01 18:08:14 christos Exp $ # .include @@ -18,13 +18,11 @@ LDADD+=-L${VFSTESTDIR} -lvfstest LDADD+=-lrumpfs_nfs # NFS -LDADD+=-lrumpfs_ffs -lrumpvfs # ffs +LDADD+=-lrumpfs_ffs # ffs LDADD+=-lrumpdev_disk -lrumpdev # disk device LDADD+=-lrumpnet_shmif # shmif LDADD+=-lrumpnet_netinet -lrumpnet_net -lrumpnet -LDADD+=-lrumpkern_sysproxy -lrump -lrumpuser # base -LDADD+=-lpthread - +LDADD+=-lrumpkern_sysproxy ${LIBRUMPBASE} # base LDADD+=-lutil .include diff --git a/fs/nfs/nfsservice/Makefile b/fs/nfs/nfsservice/Makefile --- a/fs/nfs/nfsservice/Makefile +++ b/fs/nfs/nfsservice/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.12 2016/08/13 11:20:00 christos Exp $ +# $NetBSD: Makefile,v 1.16 2021/03/07 15:09:12 christos Exp $ # NOMAN= 1 @@ -22,10 +22,10 @@ LDADD+= -lrumpnet_netinet6 -lrumpnet_netinet # TCP/IP LDADD+= -lrumpfs_nfsserver -lrumpfs_nfs # NFS support LDADD+= -lrumpdev_disk -lrumpdev # disk devices -LDADD+= -lrumpfs_ffs -lrumpvfs # FFS +LDADD+= -lrumpfs_ffs # FFS LDADD+= -lrumpnet_shmif # shmif -LDADD+= -lrumpnet -lrumpkern_sysproxy -lrump -lrumpuser # base -LDADD+= -lpthread -lutil +LDADD+= -lrumpnet -lrumpkern_sysproxy ${LIBRUMPBASE} # base +LDADD+= -lutil CPPFLAGS+= -DDEBUG -DMOUNT_NOMAIN -D_REENTRANT CPPFLAGS+= -DRUMP_SYS_NETWORKING -DMOUNTD_RUMP -DNFSD_RUMP @@ -45,7 +45,15 @@ # CPPFLAGS+= -DRPCBIND_DEBUG # CPPFLAGS+= -DSVC_RUN_DEBUG -LDADD+= -lwrap -lutil -DPADD+= ${LIBWRAP} ${LIBUTIL} +LDADD+= -lwrap -lblocklist -lutil +DPADD+= ${LIBWRAP} ${LIBBLOCKLIST} ${LIBUTIL} + +SANITIZER_RENAME_SYMBOL+= __getmntinfo13 + +COPTS.pmap_svc.c+= ${GCC_NO_CAST_FUNCTION_TYPE} +COPTS.rpcb_svc.c+= ${GCC_NO_CAST_FUNCTION_TYPE} +COPTS.rpcb_svc_4.c+= ${GCC_NO_CAST_FUNCTION_TYPE} +COPTS.rpcb_svc_com.c+= ${GCC_NO_CAST_FUNCTION_TYPE} +COPTS.mountd.c+= ${GCC_NO_CAST_FUNCTION_TYPE} .include diff --git a/fs/nfs/nfsservice/getmntinfo.c b/fs/nfs/nfsservice/getmntinfo.c --- a/fs/nfs/nfsservice/getmntinfo.c +++ b/fs/nfs/nfsservice/getmntinfo.c @@ -1,4 +1,4 @@ -/* $NetBSD: getmntinfo.c,v 1.1 2010/07/26 15:53:00 pooka Exp $ */ +/* $NetBSD: getmntinfo.c,v 1.2 2020/06/17 00:16:21 kamil Exp $ */ /* * Copyright (c) 1989, 1993 @@ -34,7 +34,7 @@ #if 0 static char sccsid[] = "@(#)getmntinfo.c 8.1 (Berkeley) 6/4/93"; #else -__RCSID("$NetBSD: getmntinfo.c,v 1.1 2010/07/26 15:53:00 pooka Exp $"); +__RCSID("$NetBSD: getmntinfo.c,v 1.2 2020/06/17 00:16:21 kamil Exp $"); #endif #endif /* LIBC_SCCS and not lint */ @@ -43,6 +43,7 @@ #include #include +#include #include #include diff --git a/fs/nfs/nfsservice/rumpnfsd.c b/fs/nfs/nfsservice/rumpnfsd.c --- a/fs/nfs/nfsservice/rumpnfsd.c +++ b/fs/nfs/nfsservice/rumpnfsd.c @@ -1,4 +1,4 @@ -/* $NetBSD: rumpnfsd.c,v 1.9 2015/11/08 02:45:16 christos Exp $ */ +/* $NetBSD: rumpnfsd.c,v 1.11 2020/06/17 00:16:21 kamil Exp $ */ /*- * Copyright (c) 2010 The NetBSD Foundation, Inc. @@ -45,11 +45,13 @@ sem_t gensem; +#define RUMPNFSD_NOATF #include "../../../net/config/netconfig.c" #include "../../common/h_fsmacros.h" #include "svc_fdset.h" #include +#include #include int @@ -65,9 +67,6 @@ pthread_t t; int rv; - /* for netcfg */ - noatf = 1; - /* use defaults? */ if (argc == 1) { ethername = "etherbus"; diff --git a/fs/nfs/t_mountd.c b/fs/nfs/t_mountd.c --- a/fs/nfs/t_mountd.c +++ b/fs/nfs/t_mountd.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_mountd.c,v 1.6 2017/01/13 21:30:40 christos Exp $ */ +/* $NetBSD: t_mountd.c,v 1.8 2021/06/04 10:48:07 hannken Exp $ */ /*- * Copyright (c) 2010 The NetBSD Foundation, Inc. @@ -63,7 +63,7 @@ rump_sys_chdir(FSTEST_MNTNAME); while (!quit) { - fd = rump_sys_open("file", O_RDWR | O_CREAT); + fd = rump_sys_open("file", O_RDWR | O_CREAT, 0600); if (fd == -1) { if (errno == EACCES) { fail++; @@ -105,11 +105,8 @@ FSTEST_DESTRUCTOR(tc, nfs, voidargs); - atf_tc_expect_fail("PR kern/5844"); if (fail) atf_tc_fail("op failed with EACCES"); - else - atf_tc_fail("race did not trigger this time"); } ATF_TP_ADD_TCS(tp) diff --git a/fs/nfs/t_rquotad.sh b/fs/nfs/t_rquotad.sh old mode 100755 new mode 100644 --- a/fs/nfs/t_rquotad.sh +++ b/fs/nfs/t_rquotad.sh @@ -1,8 +1,8 @@ -# $NetBSD: t_rquotad.sh,v 1.5 2016/08/10 23:25:39 kre Exp $ +# $NetBSD: t_rquotad.sh,v 1.9 2020/08/20 13:58:30 riastradh Exp $ # # Copyright (c) 2011 Manuel Bouyer # All rights reserved. -# +# # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: @@ -11,7 +11,7 @@ # 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 NETBSD FOUNDATION, INC. 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 @@ -24,10 +24,42 @@ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # + +# Like test_case_root() in ../ffs/ffs_common.sh, plus cleanup of both +# rump servers. + +test_case_rquotad() +{ + local name="${1}"; shift + local check_function="${1}"; shift + local descr="${1}"; shift + + atf_test_case "${name}" cleanup + + eval "${name}_head() { \ + atf_set "descr" "${descr}" + atf_set "require.user" "root" + atf_set "timeout" "360" + }" + eval "${name}_body() { \ + RUMP_SOCKETS_LIST=\${RUMP_SOCKET}; \ + export RUMP_SERVER=unix://\${RUMP_SOCKET}; \ + ${check_function} " "${@}" "; \ + }" + # Can't use RUMP_SOCKETS_LIST here because it is not set in + # the cleanup shell. + eval "${name}_cleanup() { \ + for s in \${RUMP_SOCKET} clientsock; do \ + RUMP_SERVER=unix://\${s} rump.halt 2>/dev/null || true; \ + done; \ + }" + tests="${tests} ${name}" +} + for e in le be; do for v in 1; do for q in "user" "group" "both"; do - test_case_root get_nfs_${e}_${v}_${q} get_nfs_quota \ + test_case_rquotad get_nfs_${e}_${v}_${q} get_nfs_quota \ "get NFS quota with ${q} enabled" ${e} ${v} ${q} done done @@ -54,7 +86,7 @@ ;; esac -#start a a nfs server +#start a nfs server atf_check -s exit:0 rump_server -lrumpvfs -lrumpdev -lrumpnet \ -lrumpnet_net -lrumpnet_netinet -lrumpnet_netinet6 \ @@ -114,7 +146,7 @@ unset RUMPHIJACK unset LD_PRELOAD - atf_check -s exit:0 rump_server -lrumpvfs -lrumpnet -lrumpdev \ + atf_check -s exit:0 rump_server -lrumpvfs -lrumpnet \ -lrumpnet_net -lrumpnet_netinet -lrumpnet_shmif -lrumpfs_nfs\ ${RUMP_SERVER} diff --git a/fs/nullfs/Makefile b/fs/nullfs/Makefile --- a/fs/nullfs/Makefile +++ b/fs/nullfs/Makefile @@ -1,12 +1,14 @@ -# $NetBSD: Makefile,v 1.4 2014/06/10 04:28:39 he Exp $ +# $NetBSD: Makefile,v 1.5 2020/03/01 18:08:14 christos Exp $ # +.include + TESTSDIR= ${TESTSBASE}/fs/nullfs WARNS= 4 TESTS_C= t_basic LDADD+= -lrumpfs_tmpfs -lrumpfs_null -lrumpvfs_layerfs # fs drivers -LDADD+= -lrumpvfs -lrump -lrumpuser -lrump -lpthread # base +LDADD+= ${LIBRUMPBASE} # base .include diff --git a/fs/nullfs/t_basic.c b/fs/nullfs/t_basic.c --- a/fs/nullfs/t_basic.c +++ b/fs/nullfs/t_basic.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_basic.c,v 1.4 2017/01/13 21:30:40 christos Exp $ */ +/* $NetBSD: t_basic.c,v 1.5 2020/06/26 07:50:12 jruoho Exp $ */ #include #include @@ -133,11 +133,11 @@ /* this is expected to fail until the PR is fixed */ atf_tc_set_md_var(tc, "descr", "\"recursive\" mounts deadlock" - " (kern/43439)"); + " (PR kern/43439)"); } /* - * Mapping to identifiers in kern/43439: + * Mapping to identifiers in PR kern/43439: * /td = /home/current/pkgsrc * /td/dist = /home/current/pkgsrc/distiles * /mp = /usr/pkgsrc diff --git a/fs/psshfs/sshd_config.in b/fs/psshfs/sshd_config.in --- a/fs/psshfs/sshd_config.in +++ b/fs/psshfs/sshd_config.in @@ -1,4 +1,4 @@ -# $NetBSD: sshd_config.in,v 1.2 2011/02/11 13:19:46 pooka Exp $ +# $NetBSD: sshd_config.in,v 1.3 2017/05/22 21:22:30 christos Exp $ # Basic settings. Port 10000 @@ -23,7 +23,6 @@ PidFile @WORKDIR@/sshd.pid Subsystem sftp @WORKDIR@/sftp-server UsePam no -UsePrivilegeSeparation no # The root user should also be able to run the tests. PermitRootLogin yes diff --git a/fs/psshfs/t_psshfs.sh b/fs/psshfs/t_psshfs.sh old mode 100755 new mode 100644 --- a/fs/psshfs/t_psshfs.sh +++ b/fs/psshfs/t_psshfs.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_psshfs.sh,v 1.8 2016/09/05 08:53:57 christos Exp $ +# $NetBSD: t_psshfs.sh,v 1.9 2017/05/24 15:29:51 christos Exp $ # # Copyright (c) 2007, 2008 The NetBSD Foundation, Inc. # All rights reserved. @@ -267,7 +267,7 @@ atf_set "descr" "Checks whether an empty file can be read" # This test is supposed to make sure psshfs does not hang # when reading from an empty file, hence the timeout. - atf_set "timeout" 8 + atf_set "timeout" 60 } read_empty_file_body() { require_puffs diff --git a/fs/ptyfs/Makefile b/fs/ptyfs/Makefile --- a/fs/ptyfs/Makefile +++ b/fs/ptyfs/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.5 2014/06/10 04:28:39 he Exp $ +# $NetBSD: Makefile,v 1.6 2020/03/01 18:08:14 christos Exp $ # TESTSDIR= ${TESTSBASE}/fs/ptyfs @@ -9,7 +9,7 @@ LDADD+= -lrumpfs_ptyfs # fs drivers LDADD+= -lrumpkern_tty # tty support -LDADD+= -lrumpvfs -lrump -lrumpuser -lrump -lpthread # base +LDADD+= ${LIBRUMPBASE} # base LDADD.t_nullpts+= -lrumpfs_null -lrumpvfs_layerfs diff --git a/fs/puffs/Makefile b/fs/puffs/Makefile --- a/fs/puffs/Makefile +++ b/fs/puffs/Makefile @@ -1,6 +1,7 @@ -# $NetBSD: Makefile,v 1.14 2014/06/10 04:28:39 he Exp $ +# $NetBSD: Makefile,v 1.16 2020/05/15 23:32:28 christos Exp $ # +RUMPFIFO=yes # Use the real rump fifo .include SUBDIR+= h_dtfs @@ -15,10 +16,8 @@ LDADD+=-L${VFSTESTDIR} -lvfstest LDADD+= -lrumpdev_putter -lrumpdev -LDADD+= -lrumpfs_syspuffs -lrumpvfs_fifofs -LDADD+= -lrumpnet_local -lrumpnet_net -lrumpnet -LDADD+= -lrumpvfs -LDADD+= -lrump -lrumpuser -lrump -lpthread +LDADD+= -lrumpfs_syspuffs +LDADD+= ${LIBRUMPBASE} # required by -DPUFFSDUMP #LDADD+= -lpuffs -lutil diff --git a/fs/puffs/h_dtfs/dtfs_vfsops.c b/fs/puffs/h_dtfs/dtfs_vfsops.c --- a/fs/puffs/h_dtfs/dtfs_vfsops.c +++ b/fs/puffs/h_dtfs/dtfs_vfsops.c @@ -1,4 +1,4 @@ -/* $NetBSD: dtfs_vfsops.c,v 1.3 2012/11/04 23:37:02 christos Exp $ */ +/* $NetBSD: dtfs_vfsops.c,v 1.4 2019/09/23 12:00:58 christos Exp $ */ /* * Copyright (c) 2006 Antti Kantee. All Rights Reserved. @@ -192,7 +192,7 @@ #define ROUND(a,b) (((a) + ((b)-1)) & ~((b)-1)) #define NFILES 1024*1024 int -dtfs_fs_statvfs(struct puffs_usermount *pu, struct statvfs *sbp) +dtfs_fs_statvfs(struct puffs_usermount *pu, struct puffs_statvfs *sbp) { struct rlimit rlim; struct dtfs_mount *dtm; @@ -201,7 +201,7 @@ dtm = puffs_getspecific(pu); pgsize = getpagesize(); - memset(sbp, 0, sizeof(struct statvfs)); + memset(sbp, 0, sizeof(struct puffs_statvfs)); /* * Use datasize rlimit as an _approximation_ for free size. diff --git a/fs/tmpfs/Makefile b/fs/tmpfs/Makefile --- a/fs/tmpfs/Makefile +++ b/fs/tmpfs/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.6 2010/06/07 03:43:51 riz Exp $ +# $NetBSD: Makefile,v 1.7 2020/03/01 18:08:14 christos Exp $ TESTSDIR= ${TESTSBASE}/fs/tmpfs WARNS= 4 @@ -30,7 +30,7 @@ TESTS_C+= t_renamerace -LDADD.t_renamerace+= -lrumpfs_tmpfs -lrumpvfs -lrump -lrumpuser -lpthread +LDADD.t_renamerace+= -lrumpfs_tmpfs ${LIBRUMPBASE} FILES= h_funcs.subr FILESDIR= ${TESTSDIR} diff --git a/fs/tmpfs/h_tools.c b/fs/tmpfs/h_tools.c --- a/fs/tmpfs/h_tools.c +++ b/fs/tmpfs/h_tools.c @@ -1,4 +1,4 @@ -/* $NetBSD: h_tools.c,v 1.4 2011/06/11 18:03:17 christos Exp $ */ +/* $NetBSD: h_tools.c,v 1.5 2018/01/17 00:22:29 maya Exp $ */ /* * Copyright (c) 2005, 2006 The NetBSD Foundation, Inc. @@ -45,6 +45,7 @@ #include #include #include +#include #include #include #include @@ -230,12 +231,13 @@ return EXIT_FAILURE; } + memset(&addr, 0, sizeof(addr)); (void)strlcpy(addr.sun_path, argv[1], sizeof(addr.sun_path)); addr.sun_family = PF_UNIX; - - error = bind(fd, (struct sockaddr *)&addr, sizeof(addr)); + error = bind(fd, (struct sockaddr *)&addr, SUN_LEN(&addr)); if (error == -1) { warn("connect"); + (void)close(fd); return EXIT_FAILURE; } diff --git a/fs/tmpfs/t_create.sh b/fs/tmpfs/t_create.sh old mode 100755 new mode 100644 diff --git a/fs/tmpfs/t_devices.sh b/fs/tmpfs/t_devices.sh old mode 100755 new mode 100644 diff --git a/fs/tmpfs/t_dots.sh b/fs/tmpfs/t_dots.sh old mode 100755 new mode 100644 diff --git a/fs/tmpfs/t_exec.sh b/fs/tmpfs/t_exec.sh old mode 100755 new mode 100644 diff --git a/fs/tmpfs/t_link.sh b/fs/tmpfs/t_link.sh old mode 100755 new mode 100644 diff --git a/fs/tmpfs/t_mkdir.sh b/fs/tmpfs/t_mkdir.sh old mode 100755 new mode 100644 diff --git a/fs/tmpfs/t_mknod.sh b/fs/tmpfs/t_mknod.sh old mode 100755 new mode 100644 diff --git a/fs/tmpfs/t_mount.sh b/fs/tmpfs/t_mount.sh old mode 100755 new mode 100644 diff --git a/fs/tmpfs/t_pipes.sh b/fs/tmpfs/t_pipes.sh old mode 100755 new mode 100644 diff --git a/fs/tmpfs/t_read_write.sh b/fs/tmpfs/t_read_write.sh old mode 100755 new mode 100644 diff --git a/fs/tmpfs/t_readdir.sh b/fs/tmpfs/t_readdir.sh old mode 100755 new mode 100644 diff --git a/fs/tmpfs/t_remove.sh b/fs/tmpfs/t_remove.sh old mode 100755 new mode 100644 diff --git a/fs/tmpfs/t_rename.sh b/fs/tmpfs/t_rename.sh old mode 100755 new mode 100644 diff --git a/fs/tmpfs/t_renamerace.c b/fs/tmpfs/t_renamerace.c --- a/fs/tmpfs/t_renamerace.c +++ b/fs/tmpfs/t_renamerace.c @@ -1,8 +1,8 @@ -/* $NetBSD: t_renamerace.c,v 1.14 2017/01/13 21:30:40 christos Exp $ */ +/* $NetBSD: t_renamerace.c,v 1.15 2020/06/26 07:50:12 jruoho Exp $ */ /* * Modified for rump and atf from a program supplied - * by Nicolas Joly in kern/40948 + * by Nicolas Joly in PR kern/40948 */ #include diff --git a/fs/tmpfs/t_rmdir.sh b/fs/tmpfs/t_rmdir.sh old mode 100755 new mode 100644 diff --git a/fs/tmpfs/t_setattr.sh b/fs/tmpfs/t_setattr.sh old mode 100755 new mode 100644 diff --git a/fs/tmpfs/t_sizes.sh b/fs/tmpfs/t_sizes.sh old mode 100755 new mode 100644 --- a/fs/tmpfs/t_sizes.sh +++ b/fs/tmpfs/t_sizes.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_sizes.sh,v 1.5 2010/11/07 17:51:18 jmmv Exp $ +# $NetBSD: t_sizes.sh,v 1.6 2018/01/17 00:23:17 maya Exp $ # # Copyright (c) 2005, 2006, 2007, 2008 The NetBSD Foundation, Inc. # All rights reserved. @@ -54,7 +54,7 @@ big_body() { test_mount -o -s10M - pagesize=$(sysctl hw.pagesize | cut -d ' ' -f 3) + pagesize=$(sysctl -n hw.pagesize) eval $($(atf_get_srcdir)/h_tools statvfs . | sed -e 's|^f_|cf_|') cf_bused=$((${cf_blocks} - ${cf_bfree})) diff --git a/fs/tmpfs/t_sockets.sh b/fs/tmpfs/t_sockets.sh old mode 100755 new mode 100644 diff --git a/fs/tmpfs/t_statvfs.sh b/fs/tmpfs/t_statvfs.sh old mode 100755 new mode 100644 --- a/fs/tmpfs/t_statvfs.sh +++ b/fs/tmpfs/t_statvfs.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_statvfs.sh,v 1.4 2010/11/07 17:51:18 jmmv Exp $ +# $NetBSD: t_statvfs.sh,v 1.5 2018/01/17 00:23:17 maya Exp $ # # Copyright (c) 2005, 2006, 2007 The NetBSD Foundation, Inc. # All rights reserved. @@ -38,7 +38,7 @@ values_body() { test_mount -o -s10M - pagesize=$(sysctl hw.pagesize | cut -d ' ' -f 3) + pagesize=$(sysctl -n hw.pagesize) eval $($(atf_get_srcdir)/h_tools statvfs .) [ ${pagesize} -eq ${f_bsize} ] || \ atf_fail "Invalid bsize" diff --git a/fs/tmpfs/t_symlink.sh b/fs/tmpfs/t_symlink.sh old mode 100755 new mode 100644 diff --git a/fs/tmpfs/t_times.sh b/fs/tmpfs/t_times.sh old mode 100755 new mode 100644 --- a/fs/tmpfs/t_times.sh +++ b/fs/tmpfs/t_times.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_times.sh,v 1.5 2010/11/07 17:51:18 jmmv Exp $ +# $NetBSD: t_times.sh,v 1.6 2021/06/17 00:03:05 riastradh Exp $ # # Copyright (c) 2005, 2006, 2007, 2008 The NetBSD Foundation, Inc. # All rights reserved. @@ -40,23 +40,32 @@ atf_check -s eq:0 -o empty -e empty touch a eval $(stat -s a | sed -e 's|st_|ost_|g') || atf_fail "stat failed" - [ ${ost_birthtime} -eq ${ost_atime} ] || atf_fail "Incorrect atime" - [ ${ost_birthtime} -eq ${ost_ctime} ] || atf_fail "Incorrect ctime" - [ ${ost_birthtime} -eq ${ost_mtime} ] || atf_fail "Incorrect mtime" + [ ${ost_birthtime} -eq ${ost_atime} ] || \ + atf_fail "Incorrect atime: ${ost_birthtime} != ${ost_atime}" + [ ${ost_birthtime} -eq ${ost_ctime} ] || \ + atf_fail "Incorrect ctime: ${ost_birthtime} != ${ost_ctime}" + [ ${ost_birthtime} -eq ${ost_mtime} ] || \ + atf_fail "Incorrect mtime: ${ost_birthtime} != ${ost_mtime}" sleep 1 atf_check -s eq:0 -o ignore -e empty cat a eval $(stat -s a) || atf_fail "stat failed" - [ ${st_atime} -gt ${ost_atime} ] || atf_fail "Incorrect atime" - [ ${st_ctime} -eq ${ost_ctime} ] || atf_fail "Incorrect ctime" - [ ${st_mtime} -eq ${ost_mtime} ] || atf_fail "Incorrect mtime" + [ ${st_atime} -gt ${ost_atime} ] || \ + atf_fail "Incorrect atime: ${st_atime} <= ${ost_atime}" + [ ${st_ctime} -eq ${ost_ctime} ] || \ + atf_fail "Incorrect ctime: ${st_ctime} != ${ost_ctime}" + [ ${st_mtime} -eq ${ost_mtime} ] || \ + atf_fail "Incorrect mtime: ${st_mtime} != ${ost_mtime}" sleep 1 echo foo >a || atf_fail "Write failed" eval $(stat -s a) || atf_fail "stat failed" - [ ${st_atime} -gt ${ost_atime} ] || atf_fail "Incorrect atime" - [ ${st_ctime} -gt ${ost_ctime} ] || atf_fail "Incorrect ctime" - [ ${st_mtime} -gt ${ost_mtime} ] || atf_fail "Incorrect mtime" + [ ${st_atime} -gt ${ost_atime} ] || \ + atf_fail "Incorrect atime: ${st_atime} <= ${ost_atime}" + [ ${st_ctime} -gt ${ost_ctime} ] || \ + atf_fail "Incorrect ctime: ${st_ctime} <= ${ost_ctime}" + [ ${st_mtime} -gt ${ost_mtime} ] || \ + atf_fail "Incorrect mtime: ${st_mtime} <= ${ost_mtime}" test_unmount } @@ -76,9 +85,12 @@ sleep 1 atf_check -s eq:0 -o ignore -e empty cat b eval $(stat -s b) || atf_fail "stat failed" - [ ${st_atime} -gt ${ost_atime} ] || atf_fail "Incorrect atime" - [ ${st_ctime} -eq ${ost_ctime} ] || atf_fail "Incorrect ctime" - [ ${st_mtime} -eq ${ost_mtime} ] || atf_fail "Incorrect mtime" + [ ${st_atime} -gt ${ost_atime} ] || \ + atf_fail "Incorrect atime: ${st_atime} <= ${ost_atime}" + [ ${st_ctime} -eq ${ost_ctime} ] || \ + atf_fail "Incorrect ctime: ${st_ctime} != ${ost_ctime}" + [ ${st_mtime} -eq ${ost_mtime} ] || \ + atf_fail "Incorrect mtime: ${st_mtime} != ${ost_mtime}" test_unmount } @@ -98,9 +110,12 @@ sleep 1 atf_check -s eq:0 -o empty -e empty ln c d eval $(stat -s c) || atf_fail "stat failed" - [ ${st_atime} -eq ${ost_atime} ] || atf_fail "Incorrect atime" - [ ${st_ctime} -gt ${ost_ctime} ] || atf_fail "Incorrect ctime" - [ ${st_mtime} -eq ${ost_mtime} ] || atf_fail "Incorrect mtime" + [ ${st_atime} -eq ${ost_atime} ] || \ + atf_fail "Incorrect atime: ${st_atime} != ${ost_atime}" + [ ${st_ctime} -gt ${ost_ctime} ] || \ + atf_fail "Incorrect ctime: ${st_ctime} <= ${ost_ctime}" + [ ${st_mtime} -eq ${ost_mtime} ] || \ + atf_fail "Incorrect mtime: ${st_mtime} != ${ost_mtime}" test_unmount } @@ -122,10 +137,14 @@ atf_check -s eq:0 -o empty -e empty mv e/a e/b eval $(stat -s e | sed -e 's|st_|dst_|g') || atf_fail "stat failed" eval $(stat -s e/b) || atf_fail "stat failed" - [ ${st_atime} -eq ${ost_atime} ] || atf_fail "Incorrect atime" - [ ${st_ctime} -gt ${ost_ctime} ] || atf_fail "Incorrect ctime" - [ ${st_mtime} -eq ${ost_mtime} ] || atf_fail "Incorrect mtime" - [ ${dst_mtime} -gt ${dost_mtime} ] || atf_fail "Incorrect mtime" + [ ${st_atime} -eq ${ost_atime} ] || \ + atf_fail "Incorrect atime: ${st_atime} != ${ost_atime}" + [ ${st_ctime} -gt ${ost_ctime} ] || \ + atf_fail "Incorrect ctime: ${st_ctime} <= ${ost_ctime}" + [ ${st_mtime} -eq ${ost_mtime} ] || \ + atf_fail "Incorrect mtime: ${st_mtime} != ${ost_mtime}" + [ ${dst_mtime} -gt ${dost_mtime} ] || \ + atf_fail "Incorrect mtime: ${dst_mtime} <= ${dost_mtime}" test_unmount } diff --git a/fs/tmpfs/t_trail_slash.sh b/fs/tmpfs/t_trail_slash.sh old mode 100755 new mode 100644 diff --git a/fs/tmpfs/t_truncate.sh b/fs/tmpfs/t_truncate.sh old mode 100755 new mode 100644 diff --git a/fs/tmpfs/t_vnd.sh b/fs/tmpfs/t_vnd.sh old mode 100755 new mode 100644 --- a/fs/tmpfs/t_vnd.sh +++ b/fs/tmpfs/t_vnd.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_vnd.sh,v 1.9 2016/07/29 05:23:24 pgoyette Exp $ +# $NetBSD: t_vnd.sh,v 1.11 2021/06/05 06:40:59 gson Exp $ # # Copyright (c) 2006, 2007, 2008 The NetBSD Foundation, Inc. # All rights reserved. @@ -28,6 +28,10 @@ # Verifies that vnd works with files stored in tmpfs. # +vnddev=vnd3 +rawpart=$( sysctl -n kern.rawpartition | tr '01234' 'abcde' ) +vnd=/dev/${vnddev}${rawpart} + atf_test_case basic cleanup basic_head() { atf_set "descr" "Verifies that vnd works with files stored in tmpfs" @@ -38,12 +42,12 @@ atf_check -s eq:0 -o ignore -e ignore \ dd if=/dev/zero of=disk.img bs=1m count=10 - atf_check -s eq:0 -o empty -e empty vndconfig /dev/vnd3 disk.img + atf_check -s eq:0 -o empty -e empty vndconfig -c ${vnddev} disk.img - atf_check -s eq:0 -o ignore -e ignore newfs /dev/rvnd3a + atf_check -s eq:0 -o ignore -e ignore newfs -I ${vnd} atf_check -s eq:0 -o empty -e empty mkdir mnt - atf_check -s eq:0 -o empty -e empty mount /dev/vnd3a mnt + atf_check -s eq:0 -o empty -e empty mount ${vnd} mnt echo "Creating test files" for f in $(jot -w %u 100 | uniq); do @@ -58,15 +62,15 @@ done atf_check -s eq:0 -o empty -e empty umount mnt - atf_check -s eq:0 -o empty -e empty vndconfig -u /dev/vnd3 + atf_check -s eq:0 -o empty -e empty vndconfig -u ${vnddev} test_unmount touch done } basic_cleanup() { if [ ! -f done ]; then - umount mnt 2>/dev/null 1>&2 - vndconfig -u /dev/vnd3 2>/dev/null 1>&2 + umount mntpt/mnt 2>/dev/null 1>&2 + vndconfig -u ${vnddev} 2>/dev/null 1>&2 fi } diff --git a/fs/tmpfs/t_vnode_leak.sh b/fs/tmpfs/t_vnode_leak.sh old mode 100755 new mode 100644 --- a/fs/tmpfs/t_vnode_leak.sh +++ b/fs/tmpfs/t_vnode_leak.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_vnode_leak.sh,v 1.6 2010/11/07 17:51:18 jmmv Exp $ +# $NetBSD: t_vnode_leak.sh,v 1.7 2018/01/17 00:23:17 maya Exp $ # # Copyright (c) 2005, 2006, 2007, 2008 The NetBSD Foundation, Inc. # All rights reserved. @@ -36,7 +36,7 @@ } main_body() { echo "Lowering kern.maxvnodes to 2000" - sysctl kern.maxvnodes | awk '{ print $3; }' >oldvnodes + sysctl -n kern.maxvnodes >oldvnodes atf_check -s eq:0 -o ignore -e empty sysctl -w kern.maxvnodes=2000 test_mount -o -s$(((4000 + 2) * 4096)) diff --git a/fs/umapfs/Makefile b/fs/umapfs/Makefile --- a/fs/umapfs/Makefile +++ b/fs/umapfs/Makefile @@ -1,12 +1,14 @@ -# $NetBSD: Makefile,v 1.4 2014/06/10 04:28:39 he Exp $ +# $NetBSD: Makefile,v 1.5 2020/03/01 18:08:14 christos Exp $ # +.include + TESTSDIR= ${TESTSBASE}/fs/umapfs WARNS= 4 TESTS_C= t_basic LDADD+= -lrumpfs_tmpfs -lrumpfs_umap -lrumpvfs_layerfs # fs drivers -LDADD+= -lrumpvfs -lrump -lrumpuser -lrump -lpthread # base +LDADD+= ${LIBRUMPBASE} # base .include diff --git a/fs/umapfs/t_basic.c b/fs/umapfs/t_basic.c --- a/fs/umapfs/t_basic.c +++ b/fs/umapfs/t_basic.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_basic.c,v 1.5 2017/01/13 21:30:40 christos Exp $ */ +/* $NetBSD: t_basic.c,v 1.6 2019/07/09 16:24:01 maya Exp $ */ #include #include @@ -141,5 +141,5 @@ ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, basic); - return 0; /*XXX?*/ + return atf_no_error(); } diff --git a/fs/union/Makefile b/fs/union/Makefile --- a/fs/union/Makefile +++ b/fs/union/Makefile @@ -1,6 +1,8 @@ -# $NetBSD: Makefile,v 1.4 2014/06/10 04:28:39 he Exp $ +# $NetBSD: Makefile,v 1.5 2020/03/01 18:08:14 christos Exp $ # +.include + TESTSDIR= ${TESTSBASE}/fs/union WARNS= 4 @@ -8,6 +10,6 @@ LDADD+= -lrumpfs_union -lrumpvfs_layerfs -lrumpfs_ffs # fs drivers LDADD+= -lrumpdev_disk -lrumpdev # disk device -LDADD+= -lrumpvfs -lrump -lrumpuser -lrump -lpthread # base +LDADD+= ${LIBRUMPBASE} # base .include diff --git a/fs/union/t_pr.c b/fs/union/t_pr.c --- a/fs/union/t_pr.c +++ b/fs/union/t_pr.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_pr.c,v 1.9 2017/01/13 21:30:40 christos Exp $ */ +/* $NetBSD: t_pr.c,v 1.13 2019/07/16 17:29:17 martin Exp $ */ #include #include @@ -53,6 +53,115 @@ rump_sys_mount(MOUNT_UNION, "/Tunion", 0,&unionargs,sizeof(unionargs)); } +ATF_TC(multilayer2); +ATF_TC_HEAD(multilayer2, tc) +{ + atf_tc_set_md_var(tc, "descr", "mount_union twice then unmount"); +} + +ATF_TC_BODY(multilayer2, tc) +{ + struct union_args unionargs; + + atf_tc_expect_signal(-1, "PR kern/2423"); + + rump_init(); + + if (rump_sys_mkdir("/Tunion", 0777) == -1) + atf_tc_fail_errno("mkdir mp1"); + if (rump_sys_mkdir("/Tunion2", 0777) == -1) + atf_tc_fail_errno("mkdir mp2"); + if (rump_sys_mkdir("/Tunion2/A", 0777) == -1) + atf_tc_fail_errno("mkdir A"); + if (rump_sys_mkdir("/Tunion2/B", 0777) == -1) + atf_tc_fail_errno("mkdir B"); + + unionargs.target = __UNCONST("/Tunion2/A"); + unionargs.mntflags = UNMNT_ABOVE; + + if (rump_sys_mount(MOUNT_UNION, "/Tunion", 0, + &unionargs, sizeof(unionargs)) == -1) + atf_tc_fail_errno("union mount"); + if (rump_sys_mkdir("/Tunion2/A/A", 0777) == -1) + atf_tc_fail_errno("mkdir A/A"); + + unionargs.target = __UNCONST("/Tunion2/A/A"); + unionargs.mntflags = UNMNT_ABOVE; + + rump_sys_mount(MOUNT_UNION, "/Tunion", 0,&unionargs,sizeof(unionargs)); + + rump_sys_unmount("/Tunion/A", 0); +} + +ATF_TC(cyclic); +ATF_TC_HEAD(cyclic, tc) +{ + atf_tc_set_md_var(tc, "descr", "cyclic mount_union"); +} + +ATF_TC_BODY(cyclic, tc) +{ + struct union_args unionargs; + + atf_tc_expect_signal(-1, "PR kern/3645"); + + rump_init(); + + if (rump_sys_mkdir("/Tunion", 0777) == -1) + atf_tc_fail_errno("mkdir mp1"); + if (rump_sys_mkdir("/Tunion/A", 0777) == -1) + atf_tc_fail_errno("mkdir mp2"); + + unionargs.target = __UNCONST("/Tunion/A"); + unionargs.mntflags = UNMNT_ABOVE; + + if (rump_sys_mount(MOUNT_UNION, "/Tunion/A", 0, + &unionargs, sizeof(unionargs)) == -1) + atf_tc_fail_errno("union mount"); + + if (rump_sys_mkdir("/Tunion/A/A", 0777) == -1) + atf_tc_fail_errno("mkdir failed"); +} + +ATF_TC(cyclic2); +ATF_TC_HEAD(cyclic2, tc) +{ + atf_tc_set_md_var(tc, "descr", "cyclic mount_union"); +} + +ATF_TC_BODY(cyclic2, tc) +{ + struct union_args unionargs; + + atf_tc_expect_signal(-1, "PR kern/4597"); + + rump_init(); + + if (rump_sys_mkdir("/Tunion", 0777) == -1) + atf_tc_fail_errno("mkdir mp1"); + if (rump_sys_mkdir("/Tunion/A", 0777) == -1) + atf_tc_fail_errno("mkdir mp2"); + if (rump_sys_mkdir("/Tunion/B", 0777) == -1) + atf_tc_fail_errno("mkdir mp3"); + + unionargs.target = __UNCONST("/Tunion/A"); + unionargs.mntflags = UNMNT_ABOVE; + + if (rump_sys_mount(MOUNT_UNION, "/Tunion/B", 0, + &unionargs, sizeof(unionargs)) == -1) + atf_tc_fail_errno("union mount"); + + unionargs.target = __UNCONST("/Tunion/B"); + unionargs.mntflags = UNMNT_ABOVE; + + if (rump_sys_mount(MOUNT_UNION, "/Tunion/A", 0, + &unionargs, sizeof(unionargs)) == -1) + atf_tc_fail_errno("union mount2"); + + if (rump_sys_mkdir("/Tunion/A/A", 0777) == -1) + atf_tc_fail_errno("mkdir failed"); +} + ATF_TC(devnull1); ATF_TC_HEAD(devnull1, tc) { @@ -77,7 +186,7 @@ &unionargs, sizeof(unionargs)) == -1) atf_tc_fail_errno("union mount"); - fd = rump_sys_open("/mp/null", O_WRONLY | O_CREAT | O_TRUNC); + fd = rump_sys_open("/mp/null", O_WRONLY | O_CREAT | O_TRUNC, 0600); if (fd == -1) atf_tc_fail_errno("open"); @@ -111,7 +220,7 @@ &unionargs, sizeof(unionargs)) == -1) atf_tc_fail_errno("union mount"); - fd = rump_sys_open("/mp/null", O_WRONLY | O_CREAT | O_APPEND); + fd = rump_sys_open("/mp/null", O_WRONLY | O_CREAT | O_APPEND, 0600); if (fd == -1) atf_tc_fail_errno("open"); @@ -123,6 +232,9 @@ ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, multilayer); + ATF_TP_ADD_TC(tp, multilayer2); + ATF_TP_ADD_TC(tp, cyclic); + ATF_TP_ADD_TC(tp, cyclic2); ATF_TP_ADD_TC(tp, devnull1); ATF_TP_ADD_TC(tp, devnull2); diff --git a/fs/vfs/Makefile b/fs/vfs/Makefile --- a/fs/vfs/Makefile +++ b/fs/vfs/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.22 2016/08/27 08:38:58 christos Exp $ +# $NetBSD: Makefile,v 1.27 2020/03/02 11:09:13 christos Exp $ # .include @@ -11,8 +11,11 @@ TESTS_C+= t_renamerace TESTS_C+= t_ro TESTS_C+= t_rmdirrace +TESTS_C+= t_rwtoro TESTS_C+= t_union TESTS_C+= t_unpriv +TESTS_C+= t_mtime_otrunc +TESTS_C+= t_mtime_write TESTS_C+= t_vfsops TESTS_C+= t_vnops @@ -30,19 +33,19 @@ LDADD+=-lrumpfs_union # union LDADD+=-lrumpfs_v7fs # v7fs LDADD+=-lrumpdev_disk -lrumpdev # disk device +LDADD+=-lrumpfs_null -lrumpvfs_layerfs # nullfs VFSTESTDIR != cd ${.CURDIR}/../common && ${PRINTOBJDIR} LDADD+=-L${VFSTESTDIR} -lvfstest -LDADD+=-lrumpvfs -lrumpkern_sysproxy -lrump -lrumpuser # base +LDADD+= ${LIBRUMPBASE} # base LDADD+=-lrumpnet # static linking -LDADD+=-lpthread LDADD+=-lutil .if (${MKZFS} != "no") -LDADD+=-lrumpfs_zfs -lrumpkern_solaris -lrumpdev_rnd +LDADD+=-lrumpfs_zfs -lrumpkern_solaris -lrumpdev_rnd -lrumpkern_sysproxy CPPFLAGS+=-DWANT_ZFS_TESTS .endif diff --git a/fs/vfs/t_full.c b/fs/vfs/t_full.c --- a/fs/vfs/t_full.c +++ b/fs/vfs/t_full.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_full.c,v 1.9 2017/01/13 21:30:40 christos Exp $ */ +/* $NetBSD: t_full.c,v 1.11 2019/07/16 17:29:17 martin Exp $ */ /*- * Copyright (c) 2010 The NetBSD Foundation, Inc. @@ -56,7 +56,8 @@ size_t bonus; int fd, i = 0; - if (FSTYPE_P2K_FFS(tc) || FSTYPE_PUFFS(tc) || FSTYPE_RUMPFS(tc)) { + if (FSTYPE_P2K_FFS(tc) || FSTYPE_PUFFS(tc) || FSTYPE_RUMPFS(tc) || + FSTYPE_ZFS(tc)) { atf_tc_skip("fs does not support explicit block allocation " "(GOP_ALLOC)"); } @@ -67,7 +68,7 @@ if (rump_sys_chdir(mp) == -1) atf_tc_fail_errno("chdir mountpoint"); - fd = rump_sys_open("afile", O_CREAT | O_RDWR); + fd = rump_sys_open("afile", O_CREAT | O_RDWR, 0600); if (fd == -1) atf_tc_fail_errno("create file"); @@ -77,8 +78,6 @@ if (n == -1) break; } - if (FSTYPE_ZFS(tc)) - atf_tc_expect_fail("PR kern/47656: Test known to be broken"); if (n == -1) { if (errno != ENOSPC) atf_tc_fail_errno("write"); diff --git a/fs/vfs/t_io.c b/fs/vfs/t_io.c --- a/fs/vfs/t_io.c +++ b/fs/vfs/t_io.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_io.c,v 1.17 2017/01/13 21:30:40 christos Exp $ */ +/* $NetBSD: t_io.c,v 1.19 2019/07/16 21:13:28 christos Exp $ */ /*- * Copyright (c) 2010 The NetBSD Foundation, Inc. @@ -85,7 +85,7 @@ FSTEST_ENTER(); RL(fd = rump_sys_open("testfile", - O_CREAT | O_RDWR | (seekcnt ? O_APPEND : 0))); + O_CREAT | O_RDWR | (seekcnt ? O_APPEND : 0), 0600)); RL(rump_sys_ftruncate(fd, seekcnt)); RL(rump_sys_fstat(fd, &sb)); ATF_REQUIRE_EQ(sb.st_size, seekcnt); diff --git a/fs/vfs/t_mtime_write.c b/fs/vfs/t_mtime_write.c new file mode 100644 --- /dev/null +++ b/fs/vfs/t_mtime_write.c @@ -0,0 +1,87 @@ +/*- + * Copyright (c) 2017 The NetBSD Foundation, Inc. + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +__COPYRIGHT("@(#) Copyright (c) 2013\ + The NetBSD Foundation, inc. All rights reserved."); +__RCSID("$NetBSD: t_mtime_write.c,v 1.1 2017/11/19 21:05:26 martin Exp $"); + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "../common/h_fsmacros.h" + +#define LOCKFILE "lock" + +static time_t +lock_it(void) +{ + struct stat st; + + if (rump_sys_stat(LOCKFILE, &st) != 0) + st.st_mtime = 0; + + int f = rump_sys_open(LOCKFILE, O_WRONLY|O_CREAT, 0666); + if (f == -1) return 0; + rump_sys_write(f, &st, sizeof st); + rump_sys_close(f); + + return st.st_mtime; +} + +static void +mtime_update_on_write(const atf_tc_t *tc, const char *path) +{ + time_t last_ts = 0; + int res; + + /* atf_tc_expect_fail("PR kern/52738"); */ + + res = rump_sys_chdir(path); + if (res == -1) + atf_tc_fail("chdir failed"); + + for (int i = 0; i < 5; i++) { + time_t l = lock_it(); + printf("last lock: %ld\n", (long)l); + ATF_REQUIRE_MSG(i == 0 || l > last_ts, + "iteration %d: lock time did not increase, old time %lu, " + "new time %lu", i, + (unsigned long)last_ts, (unsigned long)l); + last_ts = l; + sleep(2); + } + rump_sys_chdir("/"); +} + +ATF_FSAPPLY(mtime_update_on_write, "Checks for mtime updates by writing (PR kern/52738)"); diff --git a/fs/vfs/t_renamerace.c b/fs/vfs/t_renamerace.c --- a/fs/vfs/t_renamerace.c +++ b/fs/vfs/t_renamerace.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_renamerace.c,v 1.34 2017/01/13 21:30:40 christos Exp $ */ +/* $NetBSD: t_renamerace.c,v 1.41 2021/06/16 23:58:07 riastradh Exp $ */ /* * Modified for rump and atf from a program supplied @@ -86,6 +86,64 @@ return NULL; } +static void +w3_mkdir(void) +{ + + if (rump_sys_mkdir("c", 0777) == -1) + ATF_CHECK_MSG(errno == ENOENT || errno == EEXIST, + "mkdir: %s (errno=%d)\n", strerror(errno), errno); + if (rump_sys_mkdir("c/d", 0777) == -1) + ATF_CHECK_MSG(errno == ENOENT || errno == EEXIST, + "mkdir: %s (errno=%d)\n", strerror(errno), errno); + if (rump_sys_mkdir("c/d/e", 0777) == -1) + ATF_CHECK_MSG(errno == ENOENT || errno == EEXIST, + "mkdir: %s (errno=%d)\n", strerror(errno), errno); +} + +static void * +w3_rmdir(void *arg) +{ + + rump_pub_lwproc_newlwp(wrkpid); + + while (!quittingtime) { + w3_mkdir(); + rump_sys_rmdir("c/d/e"); + rump_sys_rmdir("c/d"); + } + + return NULL; +} + +static void * +w3_rename1(void *arg) +{ + + rump_pub_lwproc_newlwp(wrkpid); + + while (!quittingtime) { + w3_mkdir(); + rump_sys_rename("c", "c/d/e"); + } + + return NULL; +} + +static void * +w3_rename2(void *arg) +{ + + rump_pub_lwproc_newlwp(wrkpid); + + while (!quittingtime) { + w3_mkdir(); + rump_sys_rename("c/d/e", "c"); + } + + return NULL; +} + #define NWRK 8 static void renamerace(const atf_tc_t *tc, const char *mp) @@ -102,7 +160,7 @@ if (FSTYPE_RUMPFS(tc)) atf_tc_skip("rename not supported by file system"); if (FSTYPE_UDF(tc)) - atf_tc_expect_fail("PR kern/49046"); + atf_tc_expect_fail("PR kern/53865"); RZ(rump_pub_lwproc_rfork(RUMP_RFCFDG)); RL(wrkpid = rump_sys_getpid()); @@ -147,9 +205,10 @@ if (FSTYPE_SYSVBFS(tc)) atf_tc_skip("directories not supported by file system"); - if (FSTYPE_RUMPFS(tc)) atf_tc_skip("rename not supported by file system"); + if (FSTYPE_UDF(tc)) + atf_tc_expect_fail("PR kern/53865"); /* XXX: msdosfs also sometimes hangs */ if (FSTYPE_MSDOS(tc)) @@ -169,6 +228,60 @@ pthread_join(pt2, NULL); RL(rump_sys_chdir("/")); + if (FSTYPE_UDF(tc)) + atf_tc_fail("race did not trigger this time"); + + /* + * Doesn't always trigger when run on a slow backend + * (i.e. not on tmpfs/mfs). So do the usual kludge. + */ + if (FSTYPE_MSDOS(tc)) + abort(); +} + +static void +renamerace_cycle(const atf_tc_t *tc, const char *mp) +{ + pthread_t pt_rmdir, pt_rename1, pt_rename2; + + if (FSTYPE_SYSVBFS(tc)) + atf_tc_skip("directories not supported by file system"); + if (FSTYPE_RUMPFS(tc)) + atf_tc_skip("rename not supported by file system"); + if (FSTYPE_P2K_FFS(tc)) + atf_tc_expect_fail("assertion \"vp->v_size == ip->i_size\" failed"); + if (FSTYPE_PUFFS(tc)) + atf_tc_expect_fail("assertion \"dfd\" failed"); + if (FSTYPE_NFS(tc)) + atf_tc_expect_fail("mkdir fails with ESTALE"); + if (FSTYPE_UDF(tc)) + atf_tc_expect_fail("sometimes fails with ENOSPC, PR kern/56253"); + + /* XXX: msdosfs also sometimes hangs */ + if (FSTYPE_MSDOS(tc)) + atf_tc_expect_signal(-1, "PR kern/43626"); + + RZ(rump_pub_lwproc_rfork(RUMP_RFCFDG)); + RL(wrkpid = rump_sys_getpid()); + + RL(rump_sys_chdir(mp)); + pthread_create(&pt_rmdir, NULL, w3_rmdir, NULL); + pthread_create(&pt_rename1, NULL, w3_rename1, NULL); + pthread_create(&pt_rename2, NULL, w3_rename2, NULL); + + sleep(10); + quittingtime = 1; + + alarm(1); + pthread_join(pt_rmdir, NULL); + pthread_join(pt_rename1, NULL); + pthread_join(pt_rename2, NULL); + alarm(0); + RL(rump_sys_chdir("/")); + + if (FSTYPE_UDF(tc)) + atf_tc_fail("PR kern/56253 did not trigger this time"); + /* * Doesn't always trigger when run on a slow backend * (i.e. not on tmpfs/mfs). So do the usual kludge. @@ -179,12 +292,14 @@ ATF_TC_FSAPPLY(renamerace, "rename(2) race with file unlinked mid-operation"); ATF_TC_FSAPPLY(renamerace_dirs, "rename(2) race with directories"); +ATF_TC_FSAPPLY(renamerace_cycle, "rename(2) race making directory cycles"); ATF_TP_ADD_TCS(tp) { ATF_TP_FSAPPLY(renamerace); /* PR kern/41128 */ ATF_TP_FSAPPLY(renamerace_dirs); + ATF_TP_FSAPPLY(renamerace_cycle); return atf_no_error(); } diff --git a/fs/vfs/t_ro.c b/fs/vfs/t_ro.c --- a/fs/vfs/t_ro.c +++ b/fs/vfs/t_ro.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_ro.c,v 1.6 2017/01/13 21:30:40 christos Exp $ */ +/* $NetBSD: t_ro.c,v 1.8 2019/09/21 14:25:42 kre Exp $ */ /*- * Copyright (c) 2010 The NetBSD Foundation, Inc. @@ -79,7 +79,8 @@ { FSTEST_ENTER(); - ATF_REQUIRE_ERRNO(EROFS, rump_sys_open(AFILE, O_CREAT|O_RDONLY) == -1); + ATF_REQUIRE_ERRNO(EROFS, rump_sys_open(AFILE, O_CREAT|O_RDONLY, + 0600) == -1); FSTEST_EXIT(); } @@ -119,7 +120,7 @@ static void attrs(const atf_tc_t *tc, const char *mp) { - struct timeval sometvs[2]; + struct timeval sometvs[2] = { {0,0}, {0,0} }; struct stat sb; int fd; diff --git a/fs/vfs/t_rwtoro.c b/fs/vfs/t_rwtoro.c --- a/fs/vfs/t_rwtoro.c +++ b/fs/vfs/t_rwtoro.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_rwtoro.c,v 1.1 2017/01/27 10:45:11 hannken Exp $ */ +/* $NetBSD: t_rwtoro.c,v 1.2 2020/05/14 08:34:18 msaitoh Exp $ */ /*- * Copyright (c) 2017 The NetBSD Foundation, Inc. @@ -135,7 +135,7 @@ if (use_layer) { RL(rump_sys_mkdir(null_mount, 0777)); memset(&nargs, 0, sizeof(nargs)); - nargs.nulla_target = __UNCONST(mp);; + nargs.nulla_target = __UNCONST(mp); RL(rump_sys_mount(MOUNT_NULL, null_mount, 0, &nargs, sizeof(nargs))); } diff --git a/fs/vfs/t_unpriv.c b/fs/vfs/t_unpriv.c --- a/fs/vfs/t_unpriv.c +++ b/fs/vfs/t_unpriv.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_unpriv.c,v 1.13 2017/01/13 21:30:40 christos Exp $ */ +/* $NetBSD: t_unpriv.c,v 1.16 2018/11/28 10:01:28 hannken Exp $ */ /*- * Copyright (c) 2011 The NetBSD Foundation, Inc. @@ -94,8 +94,6 @@ rump_pub_lwproc_rfork(RUMP_RFCFDG); if (rump_sys_setuid(1) == -1) atf_tc_fail_errno("setuid"); - if (FSTYPE_ZFS(tc)) - atf_tc_expect_fail("PR kern/47656: Test known to be broken"); if (rump_sys_open(name, O_RDWR|O_CREAT, 0666) != -1 || errno != EACCES) atf_tc_fail_errno("open"); rump_pub_lwproc_releaselwp(); @@ -144,8 +142,6 @@ rump_pub_lwproc_rfork(RUMP_RFCFDG); if (rump_sys_setuid(1) == -1) atf_tc_fail_errno("setuid"); - if (FSTYPE_ZFS(tc)) - atf_tc_expect_fail("PR kern/47656: Test known to be broken"); if (rump_sys_utimes(name, NULL) != -1 || errno != EACCES) atf_tc_fail_errno("utimes"); rump_pub_lwproc_releaselwp(); @@ -191,20 +187,18 @@ if (rump_sys_stat(name, &st) == -1) atf_tc_fail_errno("stat"); - if (FSTYPE_ZFS(tc)) - atf_tc_expect_fail("PR kern/47656: Test known to be broken"); if (rump_sys_chflags(name, st.st_flags) == -1) { if (errno == EOPNOTSUPP) atf_tc_skip("file flags not supported by file system"); atf_tc_fail_errno("chflags"); } - fflags = st.st_flags | UF_IMMUTABLE; + fflags = st.st_flags | UF_NODUMP; rump_pub_lwproc_rfork(RUMP_RFCFDG); if (rump_sys_setuid(1) == -1) atf_tc_fail_errno("setuid"); - fflags |= UF_IMMUTABLE; + fflags |= UF_NODUMP; if (rump_sys_chflags(name, fflags) != -1 || errno != EPERM) atf_tc_fail_errno("chflags"); rump_pub_lwproc_releaselwp(); @@ -212,7 +206,7 @@ if (rump_sys_chflags(name, fflags) == -1) atf_tc_fail_errno("chflags"); - fflags &= ~UF_IMMUTABLE; + fflags &= ~UF_NODUMP; if (rump_sys_chflags(name, fflags) == -1) atf_tc_fail_errno("chflags"); diff --git a/fs/vfs/t_vnops.c b/fs/vfs/t_vnops.c --- a/fs/vfs/t_vnops.c +++ b/fs/vfs/t_vnops.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_vnops.c,v 1.59 2017/01/13 21:30:40 christos Exp $ */ +/* $NetBSD: t_vnops.c,v 1.60 2021/08/19 20:56:36 andvar Exp $ */ /*- * Copyright (c) 2010 The NetBSD Foundation, Inc. @@ -148,7 +148,7 @@ #undef FIELD #undef TIME - atf_tc_fail("stat results differ, see ouput for more details"); + atf_tc_fail("stat results differ, see output for more details"); } } diff --git a/fs/zfs/t_zpool.sh b/fs/zfs/t_zpool.sh old mode 100755 new mode 100644 --- a/fs/zfs/t_zpool.sh +++ b/fs/zfs/t_zpool.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_zpool.sh,v 1.3 2011/12/06 18:18:59 njoly Exp $ +# $NetBSD: t_zpool.sh,v 1.6 2020/03/15 20:10:26 martin Exp $ # # Copyright (c) 2011 The NetBSD Foundation, Inc. # All rights reserved. @@ -25,7 +25,7 @@ # POSSIBILITY OF SUCH DAMAGE. # -server='rump_server -lrumpvfs -lrumpkern_solaris -lrumpfs_zfs -lrumpdev -lrumpdev_rnd -d key=/dk,hostpath=zfs.img,size=100m' +server='rump_server -lrumpvfs -lrumpdev_disk -lrumpkern_solaris -lrumpfs_zfs -lrumpdev -lrumpdev_rnd -d key=/dk,hostpath=zfs.img,size=100m' export RUMP_SERVER=unix://zsuck @@ -43,10 +43,15 @@ create_body() { + AVAIL=$( df -m ${TMPDIR} | awk '{if (int($4) > 0) print $4}' ) + if [ $AVAIL -lt 65 ]; then + atf_skip "not enough free space in working directory" + fi + atf_check -s exit:0 -o ignore -e ignore ${server} ${RUMP_SERVER} export LD_PRELOAD=/usr/lib/librumphijack.so - export RUMPHIJACK=blanket=/dev/zfs:/dk:/jippo + export RUMPHIJACK=blanket=/dev/zfs:/dk:/jippo,sysctl=yes,modctl=yes atf_check -s exit:0 zpool create jippo /dk export RUMPHIJACK=vfs=all diff --git a/games/t_factor.sh b/games/t_factor.sh old mode 100755 new mode 100644 --- a/games/t_factor.sh +++ b/games/t_factor.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_factor.sh,v 1.9 2016/06/27 05:29:32 pgoyette Exp $ +# $NetBSD: t_factor.sh,v 1.11 2020/10/11 18:43:50 christos Exp $ # # Copyright (c) 2007, 2008, 2009 The NetBSD Foundation, Inc. # All rights reserved. @@ -28,7 +28,7 @@ expect() { echo "${2}" >expout ncrypt=$( ldd /usr/games/factor | grep -c -- -lcrypt ) - if [ "X$3" != "X" -a $ncrypt -eq 0 ] ; then + if [ -n "$3" ] && [ $ncrypt -eq 0 ] ; then atf_skip "crypto needed for huge non-prime factors - PR bin/23663" fi atf_check -s eq:0 -o file:expout -e empty /usr/games/factor ${1} @@ -72,10 +72,98 @@ expect '99999999999991' '99999999999991: 7 13 769231 1428571' Need_Crypto } + +atf_test_case h_overflow1 +h_overflow1_head() { + atf_set "descr" "Tests for h_overflow conditions" + atf_set "require.progs" "/usr/games/factor" +} +h_overflow1_body() { + expect '-h 8675309' '8675309: 8675309' +} + +atf_test_case h_overflow2 +h_overflow2_head() { + atf_set "descr" "Tests for h_overflow conditions" + atf_set "require.progs" "/usr/games/factor" +} +h_overflow2_body() { + expect '-h 6172538568' '6172538568: 2^3 3 7 17 2161253' +} + +atf_test_case h_loop1 +h_loop1_head() { + atf_set "descr" "Tests some cases that once locked the program" \ + "in an infinite h_loop" + atf_set "require.progs" "/usr/games/factor" +} +h_loop1_body() { + expect '-h 2147483647111311' '2147483647111311: 3^3 131 607148331103' +} + +atf_test_case h_loop2 +h_loop2_head() { + atf_set "descr" "Tests some cases that once locked the program" \ + "in an infinite h_loop" + atf_set "require.progs" "/usr/games/factor" +} +h_loop2_body() { + expect '-h 99999999999991' '99999999999991: 7 13 769231 1428571' Need_Crypto +} + + +atf_test_case hx_overflow1 +hx_overflow1_head() { + atf_set "descr" "Tests for hx_overflow conditions" + atf_set "require.progs" "/usr/games/factor" +} +hx_overflow1_body() { + expect '-hx 8675309' '0x845FED: 0x845FED' +} + +atf_test_case hx_overflow2 +hx_overflow2_head() { + atf_set "descr" "Tests for hx_overflow conditions" + atf_set "require.progs" "/usr/games/factor" +} +hx_overflow2_body() { + expect '-hx 6172538568' '0x16FE976C8: 0x2^3 0x3 0x7 0x11 0x20FA65' +} + +atf_test_case hx_loop1 +hx_loop1_head() { + atf_set "descr" "Tests some cases that once locked the program" \ + "in an infinite hx_loop" + atf_set "require.progs" "/usr/games/factor" +} +hx_loop1_body() { + expect '-hx 2147483647111311' '0x7A11FFFF2708F: 0x3^3 0x83 0x8D5CDC505F' +} + +atf_test_case hx_loop2 +hx_loop2_head() { + atf_set "descr" "Tests some cases that once locked the program" \ + "in an infinite hx_loop" + atf_set "require.progs" "/usr/games/factor" +} +hx_loop2_body() { + expect '-hx 99999999999991' '0x5AF3107A3FF7: 0x7 0xd 0xBBCCF 0x15CC5B' Need_Crypto +} + atf_init_test_cases() { atf_add_test_case overflow1 atf_add_test_case overflow2 atf_add_test_case loop1 atf_add_test_case loop2 + + atf_add_test_case h_overflow1 + atf_add_test_case h_overflow2 + atf_add_test_case h_loop1 + atf_add_test_case h_loop2 + + atf_add_test_case hx_overflow1 + atf_add_test_case hx_overflow2 + atf_add_test_case hx_loop1 + atf_add_test_case hx_loop2 } diff --git a/include/sys/Makefile b/include/sys/Makefile --- a/include/sys/Makefile +++ b/include/sys/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.12 2016/08/08 14:11:08 pgoyette Exp $ +# $NetBSD: Makefile,v 1.16 2020/05/31 16:36:07 kamil Exp $ NOMAN= # defined @@ -9,10 +9,18 @@ TESTS_C+= t_bitops TESTS_C+= t_bootblock TESTS_C+= t_cdefs +TESTS_C+= t_list TESTS_C+= t_pslist TESTS_C+= t_tree TESTS_C+= t_types +# NULL + 0 arithmetic raises LLVM UBSan warnings, specially in sys/pslist.h +# in the type-safe macros _PSLIST_VALIDATE_PTRS and _PSLIST_VALIDATE_CONTAINER. +# See also src/sys/rump/Makefile.rump +.if ${MKSANITIZER:Uno} == "yes" || ${MKLIBCSANITIZER:Uno} == "yes" +COPTS.t_pslist.c+= ${${ACTIVE_CC} == "clang":? -fno-delete-null-pointer-checks :} +.endif + CPPFLAGS.t_pslist.c+= -I${NETBSDSRCDIR}/sys LDADD.t_bitops+= -lm @@ -20,7 +28,7 @@ TESTS_C+= t_socket CPPFLAGS.t_socket.c+= -D_KERNTYPES LDADD.t_socket+= -lrumpnet_local -lrumpnet_net -lrumpnet -LDADD.t_socket+= -lrumpvfs -lrump -lrumpuser -lpthread -lrumpdev +LDADD.t_socket+= ${LIBRUMPBASE} .endif .include diff --git a/include/sys/t_bitops.c b/include/sys/t_bitops.c --- a/include/sys/t_bitops.c +++ b/include/sys/t_bitops.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_bitops.c,v 1.19 2015/03/21 05:50:19 isaki Exp $ */ +/* $NetBSD: t_bitops.c,v 1.20 2018/07/25 22:00:32 kamil Exp $ */ /*- * Copyright (c) 2011, 2012 The NetBSD Foundation, Inc. @@ -29,7 +29,7 @@ * POSSIBILITY OF SUCH DAMAGE. */ #include -__RCSID("$NetBSD: t_bitops.c,v 1.19 2015/03/21 05:50:19 isaki Exp $"); +__RCSID("$NetBSD: t_bitops.c,v 1.20 2018/07/25 22:00:32 kamil Exp $"); #include @@ -186,7 +186,7 @@ uint32_t x; for (i = 0; i < 32; i++) { - x = 1 << i; + x = 1U << i; ATF_REQUIRE(ilog2(x) == i); } } diff --git a/include/sys/t_list.c b/include/sys/t_list.c new file mode 100644 --- /dev/null +++ b/include/sys/t_list.c @@ -0,0 +1,89 @@ +/* $NetBSD: t_list.c,v 1.2 2017/10/02 05:14:29 pgoyette Exp $ */ + +/*- + * Copyright (c) 2017 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Paul Goyette + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +#include + +#include + +#include + +/* + * XXX This is a limited test to make sure the operations behave as + * described on a sequential machine. It does nothing to test the + * pserialize-safety of any operations. + */ + +ATF_TC(list_move); +ATF_TC_HEAD(list_move, tc) +{ + atf_tc_set_md_var(tc, "descr", "LIST_MOVE verification"); +} +ATF_TC_BODY(list_move, tc) +{ + LIST_HEAD(listhead, entry) old_head, new_head, old_copy; + struct entry { + LIST_ENTRY(entry) entries; + uint64_t value; + } *n1, *n2, *n3; + + LIST_INIT(&old_head); + + n1 = malloc(sizeof(struct entry)); + n1->value = 1; + LIST_INSERT_HEAD(&old_head, n1, entries); + + n2 = malloc(sizeof(struct entry)); + n2->value = 2; + LIST_INSERT_HEAD(&old_head, n2, entries); + + LIST_MOVE(&old_head, &new_head, entries); + + memcpy(&old_copy, &old_head, sizeof(old_head)); + + n3 = LIST_FIRST(&new_head); + ATF_CHECK_MSG(n3->value = 2, "Unexpected value for LIST_FIRST"); + + LIST_REMOVE(n3, entries); + ATF_CHECK_MSG(memcmp(&old_copy, &old_head, sizeof(old_head)) == 0, + "Unexpected modification of old_head during LIST_REMOVE"); + + LIST_REMOVE(LIST_FIRST(&new_head), entries); + ATF_CHECK_MSG(LIST_EMPTY(&new_head), "New list not empty!"); +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, list_move); + + return atf_no_error(); +} diff --git a/include/sys/t_pslist.c b/include/sys/t_pslist.c --- a/include/sys/t_pslist.c +++ b/include/sys/t_pslist.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_pslist.c,v 1.1 2016/04/09 04:39:47 riastradh Exp $ */ +/* $NetBSD: t_pslist.c,v 1.2 2019/12/01 15:28:20 riastradh Exp $ */ /*- * Copyright (c) 2016 The NetBSD Foundation, Inc. @@ -29,16 +29,23 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#include - -#include - /* * XXX This is a limited test to make sure the operations behave as * described on a sequential machine. It does nothing to test the - * pserialize-safety of any operations. + * pserialize-safety of any operations. The following definitions must + * be replaced in any parallel tests. */ +#define atomic_load_relaxed(p) (*(p)) +#define atomic_load_acquire(p) (*(p)) +#define atomic_load_consume(p) (*(p)) +#define atomic_store_relaxed(p,v) (*(p) = (v)) +#define atomic_store_release(p,v) (*(p) = (v)) + +#include + +#include + ATF_TC(misc); ATF_TC_HEAD(misc, tc) { diff --git a/include/sys/t_socket.c b/include/sys/t_socket.c --- a/include/sys/t_socket.c +++ b/include/sys/t_socket.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_socket.c,v 1.5 2017/01/13 21:30:41 christos Exp $ */ +/* $NetBSD: t_socket.c,v 1.6 2019/10/06 01:05:36 mrg Exp $ */ #include #include @@ -98,7 +98,7 @@ memset(&sun, 0, sizeof(sun)); sun.sun_family = AF_LOCAL; #define SOCKPATH "/com" - strncpy(sun.sun_path, SOCKPATH, sizeof(SOCKPATH)); + memcpy(sun.sun_path, SOCKPATH, sizeof(SOCKPATH) - 1); s1 = rump_sys_socket(AF_LOCAL, SOCK_STREAM, 0); if (s1 == -1) atf_tc_fail_errno("socket 1"); @@ -114,7 +114,7 @@ /* connect to unix domain socket */ memset(&sun, 0, sizeof(sun)); sun.sun_family = AF_LOCAL; - strncpy(sun.sun_path, SOCKPATH, sizeof(SOCKPATH)); + memcpy(sun.sun_path, SOCKPATH, sizeof(SOCKPATH) - 1); s2 = rump_sys_socket(AF_LOCAL, SOCK_STREAM, 0); if (s2 == -1) atf_tc_fail_errno("socket 2"); diff --git a/include/sys/t_types.c b/include/sys/t_types.c --- a/include/sys/t_types.c +++ b/include/sys/t_types.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_types.c,v 1.4 2012/03/18 07:14:08 jruoho Exp $ */ +/* $NetBSD: t_types.c,v 1.5 2018/07/25 21:51:32 kamil Exp $ */ /*- * Copyright (c) 2011 The NetBSD Foundation, Inc. @@ -29,7 +29,7 @@ * POSSIBILITY OF SUCH DAMAGE. */ #include -__RCSID("$NetBSD: t_types.c,v 1.4 2012/03/18 07:14:08 jruoho Exp $"); +__RCSID("$NetBSD: t_types.c,v 1.5 2018/07/25 21:51:32 kamil Exp $"); #include @@ -60,7 +60,7 @@ size = SSIZE_MAX; ATF_REQUIRE(size > 0); - size = size + 1; + size = (ssize_t)((size_t)size + 1); ATF_REQUIRE(size < 0); /* diff --git a/include/t_errno.c b/include/t_errno.c --- a/include/t_errno.c +++ b/include/t_errno.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_errno.c,v 1.1 2011/05/01 17:07:05 jruoho Exp $ */ +/* $NetBSD: t_errno.c,v 1.2 2020/03/08 22:09:43 mgorny Exp $ */ /*- * Copyright (c) 2011 The NetBSD Foundation, Inc. @@ -29,7 +29,7 @@ * POSSIBILITY OF SUCH DAMAGE. */ #include -__RCSID("$NetBSD: t_errno.c,v 1.1 2011/05/01 17:07:05 jruoho Exp $"); +__RCSID("$NetBSD: t_errno.c,v 1.2 2020/03/08 22:09:43 mgorny Exp $"); #include #include @@ -48,7 +48,6 @@ * The following definitions should be available * according to IEEE Std 1003.1-2008, issue 7. */ - atf_tc_expect_fail("PR standards/44921"); fail = true; diff --git a/include/t_paths.c b/include/t_paths.c --- a/include/t_paths.c +++ b/include/t_paths.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_paths.c,v 1.16 2015/05/07 06:23:23 pgoyette Exp $ */ +/* $NetBSD: t_paths.c,v 1.17 2019/02/03 03:19:28 mrg Exp $ */ /*- * Copyright (c) 2011 The NetBSD Foundation, Inc. @@ -29,7 +29,7 @@ * POSSIBILITY OF SUCH DAMAGE. */ #include -__RCSID("$NetBSD: t_paths.c,v 1.16 2015/05/07 06:23:23 pgoyette Exp $"); +__RCSID("$NetBSD: t_paths.c,v 1.17 2019/02/03 03:19:28 mrg Exp $"); #include #include @@ -163,6 +163,7 @@ paths[i].path, errno); } + /* FALLTHROUGH */ case EBUSY: /* FALLTHROUGH */ case ENXIO: /* FALLTHROUGH */ case ENOENT: /* FALLTHROUGH */ diff --git a/ipf/h_common.sh b/ipf/h_common.sh old mode 100755 new mode 100644 diff --git a/ipf/t_bpf.sh b/ipf/t_bpf.sh old mode 100755 new mode 100644 diff --git a/ipf/t_filter_exec.sh b/ipf/t_filter_exec.sh old mode 100755 new mode 100644 diff --git a/ipf/t_filter_parse.sh b/ipf/t_filter_parse.sh old mode 100755 new mode 100644 diff --git a/ipf/t_logging.sh b/ipf/t_logging.sh old mode 100755 new mode 100644 diff --git a/ipf/t_nat_exec.sh b/ipf/t_nat_exec.sh old mode 100755 new mode 100644 diff --git a/ipf/t_nat_ipf_exec.sh b/ipf/t_nat_ipf_exec.sh old mode 100755 new mode 100644 diff --git a/ipf/t_nat_parse.sh b/ipf/t_nat_parse.sh old mode 100755 new mode 100644 diff --git a/ipf/t_pools.sh b/ipf/t_pools.sh old mode 100755 new mode 100644 diff --git a/kernel/Makefile b/kernel/Makefile --- a/kernel/Makefile +++ b/kernel/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.44 2016/12/14 06:19:59 kamil Exp $ +# $NetBSD: Makefile,v 1.68 2020/07/01 13:49:26 jruoho Exp $ NOMAN= # defined @@ -6,31 +6,43 @@ TESTSDIR= ${TESTSBASE}/kernel -TESTS_SUBDIRS= arch TESTS_SUBDIRS+= kqueue -TESTS_C= t_lock +TESTS_C= t_fcntl +TESTS_C+= t_lock TESTS_C+= t_lockf -TESTS_C+= t_ptrace -TESTS_C+= t_ptrace_wait -TESTS_C+= t_ptrace_wait3 -TESTS_C+= t_ptrace_wait4 -TESTS_C+= t_ptrace_wait6 -TESTS_C+= t_ptrace_waitid -TESTS_C+= t_ptrace_waitpid TESTS_C+= t_pty TESTS_C+= t_mqueue +TESTS_C+= t_proccwd TESTS_C+= t_sysv TESTS_C+= t_subr_prf TESTS_C+= t_kauth_pr_47598 +TESTS_C+= t_ksem TESTS_C+= t_sysctl +TESTS_C+= t_timeleft +TESTS_C+= t_zombie TESTS_SH= t_umount TESTS_SH+= t_umountstress TESTS_SH+= t_ps_strings +TESTS_SH+= t_trapsignal +TESTS_SH+= t_interp +TESTS_SH+= t_magic_symlinks +TESTS_SH+= t_nointerpreter +TESTS_SH+= t_origin +TESTS_SH+= t_procpath +TESTS_SH+= t_fexecve +TESTS_SH+= t_fpufork BINDIR= ${TESTSDIR} -PROGS= h_ps_strings1 +PROGS= h_fexecve +PROGS+= h_ps_strings1 PROGS+= h_ps_strings2 +PROGS+= h_segv +PROGS+= h_getprocpath +PROGS+= h_fpufork + +SCRIPTSDIR= ${TESTSDIR} +SCRIPTS= h_interpreter.sh LDADD.t_mqueue+= -lrt @@ -41,14 +53,14 @@ TESTS_C+= t_extattrctl TESTS_C+= t_filedesc TESTS_C+= t_rnd -LDADD.t_extattrctl+= -lrumpvfs -lrump -lrumpuser -LDADD.t_extattrctl+= -lrump -lpthread +LDADD.t_extattrctl+= ${LIBRUMPBASE} LDADD.t_filedesc+= ${LDADD.t_rnd} -LDADD.t_rnd+= -lrumpvfs -lrumpdev_rnd -lrumpdev -lrump -lrumpuser -LDADD.t_rnd+= -lrump -lpthread +LDADD.t_rnd+= -lrumpdev_rnd -lrumpdev ${LIBRUMPBASE} .endif +LDADD.t_timeleft+= -lpthread + CPPFLAGS+= -D_KERNTYPES .PATH: ${NETBSDSRCDIR}/sys/kern @@ -59,8 +71,28 @@ t_subr_prf.c: gen_t_subr_prf ${NETBSDSRCDIR}/sys/kern/subr_prf.c ${HOST_SH} ${.ALLSRC} ${.TARGET} +.if ${MKSANITIZER:Uno} == "yes" + # These symbols will be redefined by MKSANITIZER + ${TOOL_SED} -i '/undef .*printf/d' ${.TARGET} +.endif + CPPFLAGS.t_subr_prf.c= -Wno-pointer-sign # XXX platform vs kernel SHA2 +.if defined(HAVE_GCC) && ${HAVE_GCC} >= 7 && ${ACTIVE_CC} == "gcc" +# Test explicitly tests failure modes. +CPPFLAGS.t_subr_prf.c+= -Wno-error=format-truncation +.endif + +SANITIZER_RENAME_CLASSES+= t_subr_prf +SANITIZER_RENAME_FILES.t_subr_prf+= t_subr_prf.c +SANITIZER_RENAME_SYMBOL.t_subr_prf+= snprintf +SANITIZER_RENAME_SYMBOL.t_subr_prf+= vsnprintf +SANITIZER_RENAME_SYMBOL.t_subr_prf+= sprintf +SANITIZER_RENAME_SYMBOL.t_subr_prf+= vsnprintf +SANITIZER_RENAME_SYMBOL.t_subr_prf+= vasprintf + CLEANFILES+= t_subr_prf.c +LDADD.h_segv+= -lm + .include diff --git a/kernel/arch/amd64/t_ptrace_wait.c b/kernel/arch/amd64/t_ptrace_wait.c deleted file mode 100644 --- a/kernel/arch/amd64/t_ptrace_wait.c +++ /dev/null @@ -1,1606 +0,0 @@ -/* $NetBSD: t_ptrace_wait.c,v 1.11 2017/01/18 05:14:34 kamil Exp $ */ - -/*- - * Copyright (c) 2016 The NetBSD Foundation, Inc. - * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 -__RCSID("$NetBSD: t_ptrace_wait.c,v 1.11 2017/01/18 05:14:34 kamil Exp $"); - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "h_macros.h" - -#include "../../t_ptrace_wait.h" - - -#if defined(HAVE_GPREGS) -ATF_TC(regs1); -ATF_TC_HEAD(regs1, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Call PT_GETREGS and iterate over General Purpose registers"); -} - -ATF_TC_BODY(regs1, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - struct reg r; - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Call GETREGS for the child process\n"); - ATF_REQUIRE(ptrace(PT_GETREGS, child, &r, 0) != -1); - - printf("RAX=%#" PRIxREGISTER "\n", r.regs[_REG_RAX]); - printf("RBX=%#" PRIxREGISTER "\n", r.regs[_REG_RBX]); - printf("RCX=%#" PRIxREGISTER "\n", r.regs[_REG_RCX]); - printf("RDX=%#" PRIxREGISTER "\n", r.regs[_REG_RDX]); - - printf("RDI=%#" PRIxREGISTER "\n", r.regs[_REG_RDI]); - printf("RSI=%#" PRIxREGISTER "\n", r.regs[_REG_RSI]); - - printf("GS=%#" PRIxREGISTER "\n", r.regs[_REG_GS]); - printf("FS=%#" PRIxREGISTER "\n", r.regs[_REG_FS]); - printf("ES=%#" PRIxREGISTER "\n", r.regs[_REG_ES]); - printf("DS=%#" PRIxREGISTER "\n", r.regs[_REG_DS]); - printf("CS=%#" PRIxREGISTER "\n", r.regs[_REG_CS]); - printf("SS=%#" PRIxREGISTER "\n", r.regs[_REG_SS]); - - printf("RSP=%#" PRIxREGISTER "\n", r.regs[_REG_RSP]); - printf("RIP=%#" PRIxREGISTER "\n", r.regs[_REG_RIP]); - - printf("RFLAGS=%#" PRIxREGISTER "\n", r.regs[_REG_RFLAGS]); - - printf("R8=%#" PRIxREGISTER "\n", r.regs[_REG_R8]); - printf("R9=%#" PRIxREGISTER "\n", r.regs[_REG_R9]); - printf("R10=%#" PRIxREGISTER "\n", r.regs[_REG_R10]); - printf("R11=%#" PRIxREGISTER "\n", r.regs[_REG_R11]); - printf("R12=%#" PRIxREGISTER "\n", r.regs[_REG_R12]); - printf("R13=%#" PRIxREGISTER "\n", r.regs[_REG_R13]); - printf("R14=%#" PRIxREGISTER "\n", r.regs[_REG_R14]); - printf("R15=%#" PRIxREGISTER "\n", r.regs[_REG_R15]); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} -#endif - -#if defined(__HAVE_PTRACE_WATCHPOINTS) -ATF_TC(watchpoint_count); -ATF_TC_HEAD(watchpoint_count, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Call PT_COUNT_WATCHPOINTS and assert four available watchpoints"); -} - -ATF_TC_BODY(watchpoint_count, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - int N; - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Call GETREGS for the child process\n"); - ATF_REQUIRE((N = ptrace(PT_COUNT_WATCHPOINTS, child, NULL, 0)) != -1); - printf("Reported %d watchpoints\n", N); - - ATF_REQUIRE_EQ_MSG(N, 4, "Expected 4 hw watchpoints - got %d", N); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} -#endif - -#if defined(__HAVE_PTRACE_WATCHPOINTS) -ATF_TC(watchpoint_read); -ATF_TC_HEAD(watchpoint_read, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Call PT_COUNT_WATCHPOINTS and assert four available watchpoints"); -} - -ATF_TC_BODY(watchpoint_read, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - int i, N; - struct ptrace_watchpoint pw; - int len = sizeof(pw); - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Call GETREGS for the child process\n"); - ATF_REQUIRE((N = ptrace(PT_COUNT_WATCHPOINTS, child, NULL, 0)) != -1); - - ATF_REQUIRE_EQ_MSG(N, 4, "Expected 4 hw watchpoints - got %d", N); - - for (i = 0; i < N; i++) { - printf("Before reading watchpoint %d\n", i); - pw.pw_index = i; - ATF_REQUIRE(ptrace(PT_READ_WATCHPOINT, child, &pw, len) != -1); - - printf("struct ptrace {\n"); - printf("\t.pw_index=%d\n", pw.pw_index); - printf("\t.pw_lwpid=%d\n", pw.pw_lwpid); - printf("\t.pw_type=%#x\n", pw.pw_type); - printf("\t.pw_md.md_address=%p\n", pw.pw_md.md_address); - printf("\t.pw_md.md_condition=%#x\n", pw.pw_md.md_condition); - printf("\t.pw_md.md_length=%#x\n", pw.pw_md.md_length); - printf("}\n"); - } - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} -#endif - -#if defined(__HAVE_PTRACE_WATCHPOINTS) -ATF_TC(watchpoint_write_unmodified); -ATF_TC_HEAD(watchpoint_write_unmodified, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Call PT_COUNT_WATCHPOINTS and assert functional write of " - "unmodified data"); -} - -ATF_TC_BODY(watchpoint_write_unmodified, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - int i, N; - struct ptrace_watchpoint pw; - int len = sizeof(pw); - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Call GETREGS for the child process\n"); - ATF_REQUIRE((N = ptrace(PT_COUNT_WATCHPOINTS, child, NULL, 0)) != -1); - - ATF_REQUIRE_EQ_MSG(N, 4, "Expected 4 hw watchpoints - got %d", N); - - for (i = 0; i < N; i++) { - printf("Before reading watchpoint %d\n", i); - pw.pw_index = i; - ATF_REQUIRE(ptrace(PT_READ_WATCHPOINT, child, &pw, len) != -1); - - printf("struct ptrace {\n"); - printf("\t.pw_index=%d\n", pw.pw_index); - printf("\t.pw_lwpid=%d\n", pw.pw_lwpid); - printf("\t.pw_type=%#x\n", pw.pw_type); - printf("\t.pw_md.md_address=%p\n", pw.pw_md.md_address); - printf("\t.pw_md.md_condition=%#x\n", pw.pw_md.md_condition); - printf("\t.pw_md.md_length=%#x\n", pw.pw_md.md_length); - printf("}\n"); - - printf("Before writing watchpoint %d (unmodified)\n", i); - ATF_REQUIRE(ptrace(PT_WRITE_WATCHPOINT, child, &pw, len) - != -1); - } - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} -#endif - -#if defined(__HAVE_PTRACE_WATCHPOINTS) -ATF_TC(watchpoint_trap_code0); -ATF_TC_HEAD(watchpoint_trap_code0, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Call PT_COUNT_WATCHPOINTS and test code trap with watchpoint 0"); -} - -ATF_TC_BODY(watchpoint_trap_code0, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - const int i = 0; - struct ptrace_watchpoint pw; - int len = sizeof(pw); - int watchme = 1234; - struct ptrace_siginfo info; - memset(&info, 0, sizeof(info)); - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - printf("check_happy(%d)=%d\n", watchme, check_happy(watchme)); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Preparing code watchpoint trap %d\n", i); - - pw.pw_index = i; - pw.pw_lwpid = 0; - pw.pw_type = PTRACE_PW_TYPE_DBREGS; - pw.pw_md.md_address = (void *)check_happy; - pw.pw_md.md_condition = X86_HW_WATCHPOINT_DR7_CONDITION_EXECUTION; - pw.pw_md.md_length = X86_HW_WATCHPOINT_DR7_LENGTH_BYTE; - - printf("struct ptrace {\n"); - printf("\t.pw_index=%d\n", pw.pw_index); - printf("\t.pw_lwpid=%d\n", pw.pw_lwpid); - printf("\t.pw_type=%#x\n", pw.pw_type); - printf("\t.pw_md.md_address=%p\n", pw.pw_md.md_address); - printf("\t.pw_md.md_condition=%#x\n", pw.pw_md.md_condition); - printf("\t.pw_md.md_length=%#x\n", pw.pw_md.md_length); - printf("}\n"); - - printf("Before writing watchpoint %d\n", i); - ATF_REQUIRE(ptrace(PT_WRITE_WATCHPOINT, child, &pw, len) != -1); - - printf("Before resuming the child process where it left off " - "and without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, SIGTRAP); - - printf("Before calling ptrace(2) with PT_GET_SIGINFO for child\n"); - ATF_REQUIRE(ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1); - - printf("Signal traced to lwpid=%d\n", info.psi_lwpid); - printf("Signal properties: si_signo=%#x si_code=%#x si_errno=%#x\n", - info.psi_siginfo.si_signo, info.psi_siginfo.si_code, - info.psi_siginfo.si_errno); - - printf("Before checking siginfo_t\n"); - ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, SIGTRAP); - ATF_REQUIRE_EQ(info.psi_siginfo.si_code, TRAP_HWWPT); - ATF_REQUIRE_EQ(info.psi_siginfo.si_trap2, 0); - ATF_REQUIRE_EQ(info.psi_siginfo.si_trap3, X86_HW_WATCHPOINT_EVENT_FIRED); - - pw.pw_md.md_address = NULL; - printf("Before writing watchpoint %d (disable it)\n", i); - ATF_REQUIRE(ptrace(PT_WRITE_WATCHPOINT, child, &pw, len) != -1); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} -#endif - -#if defined(__HAVE_PTRACE_WATCHPOINTS) -ATF_TC(watchpoint_trap_code1); -ATF_TC_HEAD(watchpoint_trap_code1, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Call PT_COUNT_WATCHPOINTS and test code trap with watchpoint 1"); -} - -ATF_TC_BODY(watchpoint_trap_code1, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - const int i = 1; - struct ptrace_watchpoint pw; - int len = sizeof(pw); - int watchme = 1234; - struct ptrace_siginfo info; - memset(&info, 0, sizeof(info)); - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - printf("check_happy(%d)=%d\n", watchme, check_happy(watchme)); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Preparing code watchpoint trap %d\n", i); - - pw.pw_index = i; - pw.pw_lwpid = 0; - pw.pw_type = PTRACE_PW_TYPE_DBREGS; - pw.pw_md.md_address = (void *)check_happy; - pw.pw_md.md_condition = X86_HW_WATCHPOINT_DR7_CONDITION_EXECUTION; - pw.pw_md.md_length = X86_HW_WATCHPOINT_DR7_LENGTH_BYTE; - - printf("struct ptrace {\n"); - printf("\t.pw_index=%d\n", pw.pw_index); - printf("\t.pw_lwpid=%d\n", pw.pw_lwpid); - printf("\t.pw_type=%d\n", pw.pw_type); - printf("\t.pw_md.md_address=%p\n", pw.pw_md.md_address); - printf("\t.pw_md.md_condition=%#x\n", pw.pw_md.md_condition); - printf("\t.pw_md.md_length=%#x\n", pw.pw_md.md_length); - printf("}\n"); - - printf("Before writing watchpoint %d\n", i); - ATF_REQUIRE(ptrace(PT_WRITE_WATCHPOINT, child, &pw, len) != -1); - - printf("Before resuming the child process where it left off " - "and without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, SIGTRAP); - - printf("Before calling ptrace(2) with PT_GET_SIGINFO for child\n"); - ATF_REQUIRE(ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1); - - printf("Signal traced to lwpid=%d\n", info.psi_lwpid); - printf("Signal properties: si_signo=%#x si_code=%#x si_errno=%#x\n", - info.psi_siginfo.si_signo, info.psi_siginfo.si_code, - info.psi_siginfo.si_errno); - - printf("Before checking siginfo_t\n"); - ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, SIGTRAP); - ATF_REQUIRE_EQ(info.psi_siginfo.si_code, TRAP_HWWPT); - ATF_REQUIRE_EQ(info.psi_siginfo.si_trap2, 1); - ATF_REQUIRE_EQ(info.psi_siginfo.si_trap3, X86_HW_WATCHPOINT_EVENT_FIRED); - - pw.pw_md.md_address = NULL; - printf("Before writing watchpoint %d (disable it)\n", i); - ATF_REQUIRE(ptrace(PT_WRITE_WATCHPOINT, child, &pw, len) != -1); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} -#endif - -#if defined(__HAVE_PTRACE_WATCHPOINTS) -ATF_TC(watchpoint_trap_code2); -ATF_TC_HEAD(watchpoint_trap_code2, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Call PT_COUNT_WATCHPOINTS and test code trap with watchpoint 2"); -} - -ATF_TC_BODY(watchpoint_trap_code2, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - const int i = 2; - struct ptrace_watchpoint pw; - int len = sizeof(pw); - int watchme = 1234; - struct ptrace_siginfo info; - memset(&info, 0, sizeof(info)); - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - printf("check_happy(%d)=%d\n", watchme, check_happy(watchme)); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Preparing code watchpoint trap %d\n", i); - - pw.pw_index = i; - pw.pw_lwpid = 0; - pw.pw_type = PTRACE_PW_TYPE_DBREGS; - pw.pw_md.md_address = (void *)check_happy; - pw.pw_md.md_condition = X86_HW_WATCHPOINT_DR7_CONDITION_EXECUTION; - pw.pw_md.md_length = X86_HW_WATCHPOINT_DR7_LENGTH_BYTE; - - printf("struct ptrace {\n"); - printf("\t.pw_index=%d\n", pw.pw_index); - printf("\t.pw_lwpid=%d\n", pw.pw_lwpid); - printf("\t.pw_type=%#x\n", pw.pw_type); - printf("\t.pw_md.md_address=%p\n", pw.pw_md.md_address); - printf("\t.pw_md.md_condition=%#x\n", pw.pw_md.md_condition); - printf("\t.pw_md.md_length=%#x\n", pw.pw_md.md_length); - printf("}\n"); - - printf("Before writing watchpoint %d\n", i); - ATF_REQUIRE(ptrace(PT_WRITE_WATCHPOINT, child, &pw, len) != -1); - - printf("Before resuming the child process where it left off " - "and without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, SIGTRAP); - - printf("Before calling ptrace(2) with PT_GET_SIGINFO for child\n"); - ATF_REQUIRE(ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1); - - printf("Signal traced to lwpid=%d\n", info.psi_lwpid); - printf("Signal properties: si_signo=%#x si_code=%#x si_errno=%#x\n", - info.psi_siginfo.si_signo, info.psi_siginfo.si_code, - info.psi_siginfo.si_errno); - - printf("Before checking siginfo_t\n"); - ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, SIGTRAP); - ATF_REQUIRE_EQ(info.psi_siginfo.si_code, TRAP_HWWPT); - ATF_REQUIRE_EQ(info.psi_siginfo.si_trap2, 2); - ATF_REQUIRE_EQ(info.psi_siginfo.si_trap3, X86_HW_WATCHPOINT_EVENT_FIRED); - - pw.pw_md.md_address = NULL; - printf("Before writing watchpoint %d (disable it)\n", i); - ATF_REQUIRE(ptrace(PT_WRITE_WATCHPOINT, child, &pw, len) != -1); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} -#endif - -#if defined(__HAVE_PTRACE_WATCHPOINTS) -ATF_TC(watchpoint_trap_code3); -ATF_TC_HEAD(watchpoint_trap_code3, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Call PT_COUNT_WATCHPOINTS and test code trap with watchpoint 3"); -} - -ATF_TC_BODY(watchpoint_trap_code3, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - const int i = 3; - struct ptrace_watchpoint pw; - int len = sizeof(pw); - int watchme = 1234; - struct ptrace_siginfo info; - memset(&info, 0, sizeof(info)); - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - printf("check_happy(%d)=%d\n", watchme, check_happy(watchme)); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Preparing code watchpoint trap %d\n", i); - - pw.pw_index = i; - pw.pw_lwpid = 0; - pw.pw_type = PTRACE_PW_TYPE_DBREGS; - pw.pw_md.md_address = (void *)check_happy; - pw.pw_md.md_condition = X86_HW_WATCHPOINT_DR7_CONDITION_EXECUTION; - pw.pw_md.md_length = X86_HW_WATCHPOINT_DR7_LENGTH_BYTE; - - printf("struct ptrace {\n"); - printf("\t.pw_index=%d\n", pw.pw_index); - printf("\t.pw_lwpid=%d\n", pw.pw_lwpid); - printf("\t.pw_type=%#x\n", pw.pw_type); - printf("\t.pw_md.md_address=%p\n", pw.pw_md.md_address); - printf("\t.pw_md.md_condition=%#x\n", pw.pw_md.md_condition); - printf("\t.pw_md.md_length=%#x\n", pw.pw_md.md_length); - printf("}\n"); - - printf("Before writing watchpoint %d\n", i); - ATF_REQUIRE(ptrace(PT_WRITE_WATCHPOINT, child, &pw, len) != -1); - - printf("Before resuming the child process where it left off " - "and without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, SIGTRAP); - - printf("Before calling ptrace(2) with PT_GET_SIGINFO for child\n"); - ATF_REQUIRE(ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1); - - printf("Signal traced to lwpid=%d\n", info.psi_lwpid); - printf("Signal properties: si_signo=%#x si_code=%#x si_errno=%#x\n", - info.psi_siginfo.si_signo, info.psi_siginfo.si_code, - info.psi_siginfo.si_errno); - - printf("Before checking siginfo_t\n"); - ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, SIGTRAP); - ATF_REQUIRE_EQ(info.psi_siginfo.si_code, TRAP_HWWPT); - ATF_REQUIRE_EQ(info.psi_siginfo.si_trap2, 3); - ATF_REQUIRE_EQ(info.psi_siginfo.si_trap3, X86_HW_WATCHPOINT_EVENT_FIRED); - - pw.pw_md.md_address = NULL; - printf("Before writing watchpoint %d (disable it)\n", i); - ATF_REQUIRE(ptrace(PT_WRITE_WATCHPOINT, child, &pw, len) != -1); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} -#endif - -#if defined(__HAVE_PTRACE_WATCHPOINTS) -ATF_TC(watchpoint_trap_data_write0); -ATF_TC_HEAD(watchpoint_trap_data_write0, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Call PT_COUNT_WATCHPOINTS and test write trap with watchpoint 0"); -} - -ATF_TC_BODY(watchpoint_trap_data_write0, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - const int i = 0; - struct ptrace_watchpoint pw; - int len = sizeof(pw); - int watchme = 1234; - struct ptrace_siginfo info; - memset(&info, 0, sizeof(info)); - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - ++watchme; - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Preparing code watchpoint trap %d\n", i); - - pw.pw_index = 0; - pw.pw_type = PTRACE_PW_TYPE_DBREGS; - pw.pw_md.md_address = &watchme; - pw.pw_md.md_condition = X86_HW_WATCHPOINT_DR7_CONDITION_DATA_WRITE; - pw.pw_md.md_length = X86_HW_WATCHPOINT_DR7_LENGTH_BYTE; - - printf("struct ptrace {\n"); - printf("\t.pw_index=%d\n", pw.pw_index); - printf("\t.pw_lwpid=%d\n", pw.pw_lwpid); - printf("\t.pw_type=%#x\n", pw.pw_type); - printf("\t.pw_md.md_address=%p\n", pw.pw_md.md_address); - printf("\t.pw_md.md_condition=%#x\n", pw.pw_md.md_condition); - printf("\t.pw_md.md_length=%#x\n", pw.pw_md.md_length); - printf("}\n"); - - printf("Before writing watchpoint %d\n", i); - ATF_REQUIRE(ptrace(PT_WRITE_WATCHPOINT, child, &pw, len) != -1); - - printf("Before resuming the child process where it left off " - "and without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, SIGTRAP); - - printf("Before calling ptrace(2) with PT_GET_SIGINFO for child\n"); - ATF_REQUIRE(ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1); - - printf("Signal traced to lwpid=%d\n", info.psi_lwpid); - printf("Signal properties: si_signo=%#x si_code=%#x si_errno=%#x\n", - info.psi_siginfo.si_signo, info.psi_siginfo.si_code, - info.psi_siginfo.si_errno); - - printf("Before checking siginfo_t\n"); - ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, SIGTRAP); - ATF_REQUIRE_EQ(info.psi_siginfo.si_code, TRAP_HWWPT); - ATF_REQUIRE_EQ(info.psi_siginfo.si_trap2, 0); - ATF_REQUIRE_EQ(info.psi_siginfo.si_trap3, X86_HW_WATCHPOINT_EVENT_FIRED); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} -#endif - -#if defined(__HAVE_PTRACE_WATCHPOINTS) -ATF_TC(watchpoint_trap_data_write1); -ATF_TC_HEAD(watchpoint_trap_data_write1, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Call PT_COUNT_WATCHPOINTS and test write trap with watchpoint 1"); -} - -ATF_TC_BODY(watchpoint_trap_data_write1, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - const int i = 1; - struct ptrace_watchpoint pw; - int len = sizeof(pw); - int watchme = 1234; - struct ptrace_siginfo info; - memset(&info, 0, sizeof(info)); - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - ++watchme; - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Preparing code watchpoint trap %d\n", i); - - pw.pw_index = i; - pw.pw_type = PTRACE_PW_TYPE_DBREGS; - pw.pw_md.md_address = &watchme; - pw.pw_md.md_condition = X86_HW_WATCHPOINT_DR7_CONDITION_DATA_WRITE; - pw.pw_md.md_length = X86_HW_WATCHPOINT_DR7_LENGTH_BYTE; - - printf("struct ptrace {\n"); - printf("\t.pw_index=%d\n", pw.pw_index); - printf("\t.pw_lwpid=%d\n", pw.pw_lwpid); - printf("\t.pw_type=%#x\n", pw.pw_type); - printf("\t.pw_md.md_address=%p\n", pw.pw_md.md_address); - printf("\t.pw_md.md_condition=%#x\n", pw.pw_md.md_condition); - printf("\t.pw_md.md_length=%#x\n", pw.pw_md.md_length); - printf("}\n"); - - printf("Before writing watchpoint %d\n", i); - ATF_REQUIRE(ptrace(PT_WRITE_WATCHPOINT, child, &pw, len) != -1); - - printf("Before resuming the child process where it left off " - "and without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, SIGTRAP); - - printf("Before calling ptrace(2) with PT_GET_SIGINFO for child\n"); - ATF_REQUIRE(ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1); - - printf("Signal traced to lwpid=%d\n", info.psi_lwpid); - printf("Signal properties: si_signo=%#x si_code=%#x si_errno=%#x\n", - info.psi_siginfo.si_signo, info.psi_siginfo.si_code, - info.psi_siginfo.si_errno); - - printf("Before checking siginfo_t\n"); - ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, SIGTRAP); - ATF_REQUIRE_EQ(info.psi_siginfo.si_code, TRAP_HWWPT); - ATF_REQUIRE_EQ(info.psi_siginfo.si_trap2, 1); - ATF_REQUIRE_EQ(info.psi_siginfo.si_trap3, X86_HW_WATCHPOINT_EVENT_FIRED); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} -#endif - -#if defined(__HAVE_PTRACE_WATCHPOINTS) -ATF_TC(watchpoint_trap_data_write2); -ATF_TC_HEAD(watchpoint_trap_data_write2, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Call PT_COUNT_WATCHPOINTS and test write trap with watchpoint 2"); -} - -ATF_TC_BODY(watchpoint_trap_data_write2, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - const int i = 2; - struct ptrace_watchpoint pw; - int len = sizeof(pw); - int watchme = 1234; - struct ptrace_siginfo info; - memset(&info, 0, sizeof(info)); - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - ++watchme; - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Preparing code watchpoint trap %d\n", i); - - pw.pw_index = i; - pw.pw_type = PTRACE_PW_TYPE_DBREGS; - pw.pw_md.md_address = &watchme; - pw.pw_md.md_condition = X86_HW_WATCHPOINT_DR7_CONDITION_DATA_WRITE; - pw.pw_md.md_length = X86_HW_WATCHPOINT_DR7_LENGTH_BYTE; - - printf("struct ptrace {\n"); - printf("\t.pw_index=%d\n", pw.pw_index); - printf("\t.pw_lwpid=%d\n", pw.pw_lwpid); - printf("\t.pw_type=%#x\n", pw.pw_type); - printf("\t.pw_md.md_address=%p\n", pw.pw_md.md_address); - printf("\t.pw_md.md_condition=%#x\n", pw.pw_md.md_condition); - printf("\t.pw_md.md_length=%#x\n", pw.pw_md.md_length); - printf("}\n"); - - printf("Before writing watchpoint %d\n", i); - ATF_REQUIRE(ptrace(PT_WRITE_WATCHPOINT, child, &pw, len) != -1); - - printf("Before resuming the child process where it left off " - "and without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, SIGTRAP); - - printf("Before calling ptrace(2) with PT_GET_SIGINFO for child\n"); - ATF_REQUIRE(ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1); - - printf("Signal traced to lwpid=%d\n", info.psi_lwpid); - printf("Signal properties: si_signo=%#x si_code=%#x si_errno=%#x\n", - info.psi_siginfo.si_signo, info.psi_siginfo.si_code, - info.psi_siginfo.si_errno); - - printf("Before checking siginfo_t\n"); - ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, SIGTRAP); - ATF_REQUIRE_EQ(info.psi_siginfo.si_code, TRAP_HWWPT); - ATF_REQUIRE_EQ(info.psi_siginfo.si_trap2, 2); - ATF_REQUIRE_EQ(info.psi_siginfo.si_trap3, X86_HW_WATCHPOINT_EVENT_FIRED); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} -#endif - - -#if defined(__HAVE_PTRACE_WATCHPOINTS) -ATF_TC(watchpoint_trap_data_write3); -ATF_TC_HEAD(watchpoint_trap_data_write3, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Call PT_COUNT_WATCHPOINTS and test write trap with watchpoint 3"); -} - -ATF_TC_BODY(watchpoint_trap_data_write3, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - const int i = 3; - struct ptrace_watchpoint pw; - int len = sizeof(pw); - int watchme = 1234; - struct ptrace_siginfo info; - memset(&info, 0, sizeof(info)); - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - ++watchme; - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Preparing code watchpoint trap %d\n", i); - - pw.pw_index = i; - pw.pw_type = PTRACE_PW_TYPE_DBREGS; - pw.pw_md.md_address = &watchme; - pw.pw_md.md_condition = X86_HW_WATCHPOINT_DR7_CONDITION_DATA_WRITE; - pw.pw_md.md_length = X86_HW_WATCHPOINT_DR7_LENGTH_BYTE; - - printf("struct ptrace {\n"); - printf("\t.pw_index=%d\n", pw.pw_index); - printf("\t.pw_lwpid=%d\n", pw.pw_lwpid); - printf("\t.pw_type=%#x\n", pw.pw_type); - printf("\t.pw_md.md_address=%p\n", pw.pw_md.md_address); - printf("\t.pw_md.md_condition=%#x\n", pw.pw_md.md_condition); - printf("\t.pw_md.md_length=%#x\n", pw.pw_md.md_length); - printf("}\n"); - - printf("Before writing watchpoint %d\n", i); - ATF_REQUIRE(ptrace(PT_WRITE_WATCHPOINT, child, &pw, len) != -1); - - printf("Before resuming the child process where it left off " - "and without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, SIGTRAP); - - printf("Before calling ptrace(2) with PT_GET_SIGINFO for child\n"); - ATF_REQUIRE(ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1); - - printf("Signal traced to lwpid=%d\n", info.psi_lwpid); - printf("Signal properties: si_signo=%#x si_code=%#x si_errno=%#x\n", - info.psi_siginfo.si_signo, info.psi_siginfo.si_code, - info.psi_siginfo.si_errno); - - printf("Before checking siginfo_t\n"); - ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, SIGTRAP); - ATF_REQUIRE_EQ(info.psi_siginfo.si_code, TRAP_HWWPT); - ATF_REQUIRE_EQ(info.psi_siginfo.si_trap2, 3); - ATF_REQUIRE_EQ(info.psi_siginfo.si_trap3, X86_HW_WATCHPOINT_EVENT_FIRED); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} -#endif - -#if defined(__HAVE_PTRACE_WATCHPOINTS) -ATF_TC(watchpoint_trap_data_rw0); -ATF_TC_HEAD(watchpoint_trap_data_rw0, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Call PT_COUNT_WATCHPOINTS and test write trap with watchpoint 0"); -} - -ATF_TC_BODY(watchpoint_trap_data_rw0, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - const int i = 0; - struct ptrace_watchpoint pw; - int len = sizeof(pw); - int watchme = 1234; - struct ptrace_siginfo info; - memset(&info, 0, sizeof(info)); - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - printf("watchme=%d\n", watchme); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Preparing code watchpoint trap %d\n", i); - - pw.pw_index = i; - pw.pw_type = PTRACE_PW_TYPE_DBREGS; - pw.pw_md.md_address = &watchme; - pw.pw_md.md_condition = X86_HW_WATCHPOINT_DR7_CONDITION_DATA_READWRITE; - pw.pw_md.md_length = X86_HW_WATCHPOINT_DR7_LENGTH_BYTE; - - printf("struct ptrace {\n"); - printf("\t.pw_index=%d\n", pw.pw_index); - printf("\t.pw_lwpid=%d\n", pw.pw_lwpid); - printf("\t.pw_type=%#x\n", pw.pw_type); - printf("\t.pw_md.md_address=%p\n", pw.pw_md.md_address); - printf("\t.pw_md.md_condition=%#x\n", pw.pw_md.md_condition); - printf("\t.pw_md.md_length=%#x\n", pw.pw_md.md_length); - printf("}\n"); - - printf("Before writing watchpoint %d\n", i); - ATF_REQUIRE(ptrace(PT_WRITE_WATCHPOINT, child, &pw, len) != -1); - - printf("Before resuming the child process where it left off " - "and without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, SIGTRAP); - - printf("Before calling ptrace(2) with PT_GET_SIGINFO for child\n"); - ATF_REQUIRE(ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1); - - printf("Signal traced to lwpid=%d\n", info.psi_lwpid); - printf("Signal properties: si_signo=%#x si_code=%#x si_errno=%#x\n", - info.psi_siginfo.si_signo, info.psi_siginfo.si_code, - info.psi_siginfo.si_errno); - - printf("Before checking siginfo_t\n"); - ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, SIGTRAP); - ATF_REQUIRE_EQ(info.psi_siginfo.si_code, TRAP_HWWPT); - ATF_REQUIRE_EQ(info.psi_siginfo.si_trap2, 0); - ATF_REQUIRE_EQ(info.psi_siginfo.si_trap3, X86_HW_WATCHPOINT_EVENT_FIRED); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} -#endif - -#if defined(__HAVE_PTRACE_WATCHPOINTS) -ATF_TC(watchpoint_trap_data_rw1); -ATF_TC_HEAD(watchpoint_trap_data_rw1, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Call PT_COUNT_WATCHPOINTS and test write trap with watchpoint 1"); -} - -ATF_TC_BODY(watchpoint_trap_data_rw1, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - const int i = 1; - struct ptrace_watchpoint pw; - int len = sizeof(pw); - int watchme = 1234; - struct ptrace_siginfo info; - memset(&info, 0, sizeof(info)); - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - printf("watchme=%d\n", watchme); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Preparing code watchpoint trap %d\n", i); - - pw.pw_index = i; - pw.pw_type = PTRACE_PW_TYPE_DBREGS; - pw.pw_md.md_address = &watchme; - pw.pw_md.md_condition = X86_HW_WATCHPOINT_DR7_CONDITION_DATA_READWRITE; - pw.pw_md.md_length = X86_HW_WATCHPOINT_DR7_LENGTH_BYTE; - - printf("struct ptrace {\n"); - printf("\t.pw_index=%d\n", pw.pw_index); - printf("\t.pw_lwpid=%d\n", pw.pw_lwpid); - printf("\t.pw_type=%#x\n", pw.pw_type); - printf("\t.pw_md.md_address=%p\n", pw.pw_md.md_address); - printf("\t.pw_md.md_condition=%#x\n", pw.pw_md.md_condition); - printf("\t.pw_md.md_length=%#x\n", pw.pw_md.md_length); - printf("}\n"); - - printf("Before writing watchpoint %d\n", i); - ATF_REQUIRE(ptrace(PT_WRITE_WATCHPOINT, child, &pw, len) != -1); - - printf("Before resuming the child process where it left off " - "and without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, SIGTRAP); - - printf("Before calling ptrace(2) with PT_GET_SIGINFO for child\n"); - ATF_REQUIRE(ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1); - - printf("Signal traced to lwpid=%d\n", info.psi_lwpid); - printf("Signal properties: si_signo=%#x si_code=%#x si_errno=%#x\n", - info.psi_siginfo.si_signo, info.psi_siginfo.si_code, - info.psi_siginfo.si_errno); - - printf("Before checking siginfo_t\n"); - ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, SIGTRAP); - ATF_REQUIRE_EQ(info.psi_siginfo.si_code, TRAP_HWWPT); - ATF_REQUIRE_EQ(info.psi_siginfo.si_trap2, 1); - ATF_REQUIRE_EQ(info.psi_siginfo.si_trap3, X86_HW_WATCHPOINT_EVENT_FIRED); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} -#endif - -#if defined(__HAVE_PTRACE_WATCHPOINTS) -ATF_TC(watchpoint_trap_data_rw2); -ATF_TC_HEAD(watchpoint_trap_data_rw2, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Call PT_COUNT_WATCHPOINTS and test write trap with watchpoint 2"); -} - -ATF_TC_BODY(watchpoint_trap_data_rw2, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - const int i = 2; - struct ptrace_watchpoint pw; - int len = sizeof(pw); - int watchme = 1234; - struct ptrace_siginfo info; - memset(&info, 0, sizeof(info)); - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - printf("watchme=%d\n", watchme); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Preparing code watchpoint trap %d\n", i); - - pw.pw_index = i; - pw.pw_type = PTRACE_PW_TYPE_DBREGS; - pw.pw_md.md_address = &watchme; - pw.pw_md.md_condition = X86_HW_WATCHPOINT_DR7_CONDITION_DATA_READWRITE; - pw.pw_md.md_length = X86_HW_WATCHPOINT_DR7_LENGTH_BYTE; - - printf("struct ptrace {\n"); - printf("\t.pw_index=%d\n", pw.pw_index); - printf("\t.pw_lwpid=%d\n", pw.pw_lwpid); - printf("\t.pw_type=%#x\n", pw.pw_type); - printf("\t.pw_md.md_address=%p\n", pw.pw_md.md_address); - printf("\t.pw_md.md_condition=%#x\n", pw.pw_md.md_condition); - printf("\t.pw_md.md_length=%#x\n", pw.pw_md.md_length); - printf("}\n"); - - printf("Before writing watchpoint %d\n", i); - ATF_REQUIRE(ptrace(PT_WRITE_WATCHPOINT, child, &pw, len) != -1); - - printf("Before resuming the child process where it left off " - "and without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, SIGTRAP); - - printf("Before calling ptrace(2) with PT_GET_SIGINFO for child\n"); - ATF_REQUIRE(ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1); - - printf("Signal traced to lwpid=%d\n", info.psi_lwpid); - printf("Signal properties: si_signo=%#x si_code=%#x si_errno=%#x\n", - info.psi_siginfo.si_signo, info.psi_siginfo.si_code, - info.psi_siginfo.si_errno); - - printf("Before checking siginfo_t\n"); - ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, SIGTRAP); - ATF_REQUIRE_EQ(info.psi_siginfo.si_code, TRAP_HWWPT); - ATF_REQUIRE_EQ(info.psi_siginfo.si_trap2, 2); - ATF_REQUIRE_EQ(info.psi_siginfo.si_trap3, X86_HW_WATCHPOINT_EVENT_FIRED); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} -#endif - -#if defined(__HAVE_PTRACE_WATCHPOINTS) -ATF_TC(watchpoint_trap_data_rw3); -ATF_TC_HEAD(watchpoint_trap_data_rw3, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Call PT_COUNT_WATCHPOINTS and test write trap with watchpoint 3"); -} - -ATF_TC_BODY(watchpoint_trap_data_rw3, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - const int i = 3; - struct ptrace_watchpoint pw; - int len = sizeof(pw); - int watchme = 1234; - struct ptrace_siginfo info; - memset(&info, 0, sizeof(info)); - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - printf("watchme=%d\n", watchme); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Preparing code watchpoint trap %d\n", i); - - pw.pw_index = i; - pw.pw_type = PTRACE_PW_TYPE_DBREGS; - pw.pw_md.md_address = &watchme; - pw.pw_md.md_condition = X86_HW_WATCHPOINT_DR7_CONDITION_DATA_READWRITE; - pw.pw_md.md_length = X86_HW_WATCHPOINT_DR7_LENGTH_BYTE; - - printf("struct ptrace {\n"); - printf("\t.pw_index=%d\n", pw.pw_index); - printf("\t.pw_lwpid=%d\n", pw.pw_lwpid); - printf("\t.pw_type=%#x\n", pw.pw_type); - printf("\t.pw_md.md_address=%p\n", pw.pw_md.md_address); - printf("\t.pw_md.md_condition=%#x\n", pw.pw_md.md_condition); - printf("\t.pw_md.md_length=%#x\n", pw.pw_md.md_length); - printf("}\n"); - - printf("Before writing watchpoint %d\n", i); - ATF_REQUIRE(ptrace(PT_WRITE_WATCHPOINT, child, &pw, len) != -1); - - printf("Before resuming the child process where it left off " - "and without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, SIGTRAP); - - printf("Before calling ptrace(2) with PT_GET_SIGINFO for child\n"); - ATF_REQUIRE(ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1); - - printf("Signal traced to lwpid=%d\n", info.psi_lwpid); - printf("Signal properties: si_signo=%#x si_code=%#x si_errno=%#x\n", - info.psi_siginfo.si_signo, info.psi_siginfo.si_code, - info.psi_siginfo.si_errno); - - printf("Before checking siginfo_t\n"); - ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, SIGTRAP); - ATF_REQUIRE_EQ(info.psi_siginfo.si_code, TRAP_HWWPT); - ATF_REQUIRE_EQ(info.psi_siginfo.si_trap2, 3); - ATF_REQUIRE_EQ(info.psi_siginfo.si_trap3, X86_HW_WATCHPOINT_EVENT_FIRED); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} -#endif - -ATF_TP_ADD_TCS(tp) -{ - setvbuf(stdout, NULL, _IONBF, 0); - setvbuf(stderr, NULL, _IONBF, 0); - - ATF_TP_ADD_TC_HAVE_GPREGS(tp, regs1); - - ATF_TP_ADD_TC_HAVE_PTRACE_WATCHPOINTS(tp, watchpoint_count); - ATF_TP_ADD_TC_HAVE_PTRACE_WATCHPOINTS(tp, watchpoint_read); - ATF_TP_ADD_TC_HAVE_PTRACE_WATCHPOINTS(tp, watchpoint_write_unmodified); - ATF_TP_ADD_TC_HAVE_PTRACE_WATCHPOINTS(tp, watchpoint_trap_code0); - ATF_TP_ADD_TC_HAVE_PTRACE_WATCHPOINTS(tp, watchpoint_trap_code1); - ATF_TP_ADD_TC_HAVE_PTRACE_WATCHPOINTS(tp, watchpoint_trap_code2); - ATF_TP_ADD_TC_HAVE_PTRACE_WATCHPOINTS(tp, watchpoint_trap_code3); - ATF_TP_ADD_TC_HAVE_PTRACE_WATCHPOINTS(tp, watchpoint_trap_data_write0); - ATF_TP_ADD_TC_HAVE_PTRACE_WATCHPOINTS(tp, watchpoint_trap_data_write1); - ATF_TP_ADD_TC_HAVE_PTRACE_WATCHPOINTS(tp, watchpoint_trap_data_write2); - ATF_TP_ADD_TC_HAVE_PTRACE_WATCHPOINTS(tp, watchpoint_trap_data_write3); - ATF_TP_ADD_TC_HAVE_PTRACE_WATCHPOINTS(tp, watchpoint_trap_data_rw0); - ATF_TP_ADD_TC_HAVE_PTRACE_WATCHPOINTS(tp, watchpoint_trap_data_rw1); - ATF_TP_ADD_TC_HAVE_PTRACE_WATCHPOINTS(tp, watchpoint_trap_data_rw2); - ATF_TP_ADD_TC_HAVE_PTRACE_WATCHPOINTS(tp, watchpoint_trap_data_rw3); - - return atf_no_error(); -} diff --git a/kernel/arch/amd64/t_ptrace_wait3.c b/kernel/arch/amd64/t_ptrace_wait3.c deleted file mode 100644 --- a/kernel/arch/amd64/t_ptrace_wait3.c +++ /dev/null @@ -1,30 +0,0 @@ -/* $NetBSD: t_ptrace_wait3.c,v 1.1 2016/12/02 05:54:15 kamil Exp $ */ - -/*- - * Copyright (c) 2016 The NetBSD Foundation, Inc. - * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. - */ - -#define TWAIT_WAIT3 -#include "t_ptrace_wait.c" diff --git a/kernel/arch/amd64/t_ptrace_wait4.c b/kernel/arch/amd64/t_ptrace_wait4.c deleted file mode 100644 --- a/kernel/arch/amd64/t_ptrace_wait4.c +++ /dev/null @@ -1,30 +0,0 @@ -/* $NetBSD: t_ptrace_wait4.c,v 1.1 2016/12/02 05:54:15 kamil Exp $ */ - -/*- - * Copyright (c) 2016 The NetBSD Foundation, Inc. - * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. - */ - -#define TWAIT_WAIT4 -#include "t_ptrace_wait.c" diff --git a/kernel/arch/amd64/t_ptrace_wait6.c b/kernel/arch/amd64/t_ptrace_wait6.c deleted file mode 100644 --- a/kernel/arch/amd64/t_ptrace_wait6.c +++ /dev/null @@ -1,30 +0,0 @@ -/* $NetBSD: t_ptrace_wait6.c,v 1.1 2016/12/02 05:54:15 kamil Exp $ */ - -/*- - * Copyright (c) 2016 The NetBSD Foundation, Inc. - * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. - */ - -#define TWAIT_WAIT6 -#include "t_ptrace_wait.c" diff --git a/kernel/arch/amd64/t_ptrace_waitid.c b/kernel/arch/amd64/t_ptrace_waitid.c deleted file mode 100644 --- a/kernel/arch/amd64/t_ptrace_waitid.c +++ /dev/null @@ -1,30 +0,0 @@ -/* $NetBSD: t_ptrace_waitid.c,v 1.1 2016/12/02 05:54:15 kamil Exp $ */ - -/*- - * Copyright (c) 2016 The NetBSD Foundation, Inc. - * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. - */ - -#define TWAIT_WAITID -#include "t_ptrace_wait.c" diff --git a/kernel/arch/amd64/t_ptrace_waitpid.c b/kernel/arch/amd64/t_ptrace_waitpid.c deleted file mode 100644 --- a/kernel/arch/amd64/t_ptrace_waitpid.c +++ /dev/null @@ -1,30 +0,0 @@ -/* $NetBSD: t_ptrace_waitpid.c,v 1.1 2016/12/02 05:54:15 kamil Exp $ */ - -/*- - * Copyright (c) 2016 The NetBSD Foundation, Inc. - * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. - */ - -#define TWAIT_WAITPID -#include "t_ptrace_wait.c" diff --git a/kernel/arch/i386/t_ptrace_wait.c b/kernel/arch/i386/t_ptrace_wait.c deleted file mode 100644 --- a/kernel/arch/i386/t_ptrace_wait.c +++ /dev/null @@ -1,141 +0,0 @@ -/* $NetBSD: t_ptrace_wait.c,v 1.2 2017/01/13 21:30:41 christos Exp $ */ - -/*- - * Copyright (c) 2016 The NetBSD Foundation, Inc. - * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 -__RCSID("$NetBSD: t_ptrace_wait.c,v 1.2 2017/01/13 21:30:41 christos Exp $"); - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "h_macros.h" - -#include "../../t_ptrace_wait.h" - - -#if defined(HAVE_GPREGS) -ATF_TC(regs1); -ATF_TC_HEAD(regs1, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Call PT_GETREGS and iterate over General Purpose registers"); -} - -ATF_TC_BODY(regs1, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - struct reg r; - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Call GETREGS for the child process\n"); - ATF_REQUIRE(ptrace(PT_GETREGS, child, &r, 0) != -1); - - printf("EAX=%#" PRIxREGISTER "\n", r.r_eax); - printf("EBX=%#" PRIxREGISTER "\n", r.r_ebx); - printf("ECX=%#" PRIxREGISTER "\n", r.r_ecx); - printf("EDX=%#" PRIxREGISTER "\n", r.r_edx); - - printf("ESP=%#" PRIxREGISTER "\n", r.r_esp); - printf("EBP=%#" PRIxREGISTER "\n", r.r_ebp); - - printf("ESI=%#" PRIxREGISTER "\n", r.r_esi); - printf("EDI=%#" PRIxREGISTER "\n", r.r_edi); - - printf("EIP=%#" PRIxREGISTER "\n", r.r_eip); - - printf("EFLAGS=%#" PRIxREGISTER "\n", r.r_eflags); - - printf("CS=%#" PRIxREGISTER "\n", r.r_cs); - printf("SS=%#" PRIxREGISTER "\n", r.r_ss); - printf("DS=%#" PRIxREGISTER "\n", r.r_ds); - printf("ES=%#" PRIxREGISTER "\n", r.r_es); - printf("FS=%#" PRIxREGISTER "\n", r.r_fs); - printf("GS=%#" PRIxREGISTER "\n", r.r_gs); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} -#endif - -ATF_TP_ADD_TCS(tp) -{ - setvbuf(stdout, NULL, _IONBF, 0); - setvbuf(stderr, NULL, _IONBF, 0); - - ATF_TP_ADD_TC_HAVE_GPREGS(tp, regs1); - - return atf_no_error(); -} diff --git a/kernel/arch/i386/t_ptrace_wait3.c b/kernel/arch/i386/t_ptrace_wait3.c deleted file mode 100644 --- a/kernel/arch/i386/t_ptrace_wait3.c +++ /dev/null @@ -1,30 +0,0 @@ -/* $NetBSD: t_ptrace_wait3.c,v 1.1 2016/12/13 18:00:10 kamil Exp $ */ - -/*- - * Copyright (c) 2016 The NetBSD Foundation, Inc. - * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. - */ - -#define TWAIT_WAIT3 -#include "t_ptrace_wait.c" diff --git a/kernel/arch/i386/t_ptrace_wait4.c b/kernel/arch/i386/t_ptrace_wait4.c deleted file mode 100644 --- a/kernel/arch/i386/t_ptrace_wait4.c +++ /dev/null @@ -1,30 +0,0 @@ -/* $NetBSD: t_ptrace_wait4.c,v 1.1 2016/12/13 18:00:10 kamil Exp $ */ - -/*- - * Copyright (c) 2016 The NetBSD Foundation, Inc. - * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. - */ - -#define TWAIT_WAIT4 -#include "t_ptrace_wait.c" diff --git a/kernel/arch/i386/t_ptrace_wait6.c b/kernel/arch/i386/t_ptrace_wait6.c deleted file mode 100644 --- a/kernel/arch/i386/t_ptrace_wait6.c +++ /dev/null @@ -1,30 +0,0 @@ -/* $NetBSD: t_ptrace_wait6.c,v 1.1 2016/12/13 18:00:10 kamil Exp $ */ - -/*- - * Copyright (c) 2016 The NetBSD Foundation, Inc. - * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. - */ - -#define TWAIT_WAIT6 -#include "t_ptrace_wait.c" diff --git a/kernel/arch/i386/t_ptrace_waitid.c b/kernel/arch/i386/t_ptrace_waitid.c deleted file mode 100644 --- a/kernel/arch/i386/t_ptrace_waitid.c +++ /dev/null @@ -1,30 +0,0 @@ -/* $NetBSD: t_ptrace_waitid.c,v 1.1 2016/12/13 18:00:10 kamil Exp $ */ - -/*- - * Copyright (c) 2016 The NetBSD Foundation, Inc. - * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. - */ - -#define TWAIT_WAITID -#include "t_ptrace_wait.c" diff --git a/kernel/arch/i386/t_ptrace_waitpid.c b/kernel/arch/i386/t_ptrace_waitpid.c deleted file mode 100644 --- a/kernel/arch/i386/t_ptrace_waitpid.c +++ /dev/null @@ -1,30 +0,0 @@ -/* $NetBSD: t_ptrace_waitpid.c,v 1.1 2016/12/13 18:00:10 kamil Exp $ */ - -/*- - * Copyright (c) 2016 The NetBSD Foundation, Inc. - * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. - */ - -#define TWAIT_WAITPID -#include "t_ptrace_wait.c" diff --git a/kernel/gen_t_subr_prf b/kernel/gen_t_subr_prf --- a/kernel/gen_t_subr_prf +++ b/kernel/gen_t_subr_prf @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -16,11 +17,14 @@ #undef vsnprintf #undef sprintf #undef vsprintf +#undef vasprintf #define KPRINTF_BUFSIZE 1024 #undef putchar #define putchar xputchar +#define kmem_alloc(n, f) malloc(n) + static int putchar(char c, int foo, void *b) { return fputc(c, stderr); @@ -53,12 +57,12 @@ ATF_TC(snprintf_print); ATF_TC_HEAD(snprintf_print, tc) { - atf_tc_set_md_var(tc, "descr", "checks snprintf print"); + atf_tc_set_md_var(tc, "descr", "checks snprintf print"); } ATF_TC_BODY(snprintf_print, tc) { - char buf[10]; + char buf[13]; int i; memset(buf, 'x', sizeof(buf)); @@ -70,7 +74,7 @@ ATF_TC(snprintf_print_overflow); ATF_TC_HEAD(snprintf_print_overflow, tc) { - atf_tc_set_md_var(tc, "descr", "checks snprintf print with overflow"); + atf_tc_set_md_var(tc, "descr", "checks snprintf print with overflow"); } ATF_TC_BODY(snprintf_print_overflow, tc) @@ -87,7 +91,7 @@ ATF_TC(snprintf_count); ATF_TC_HEAD(snprintf_count, tc) { - atf_tc_set_md_var(tc, "descr", "checks snprintf count"); + atf_tc_set_md_var(tc, "descr", "checks snprintf count"); } ATF_TC_BODY(snprintf_count, tc) @@ -101,7 +105,7 @@ ATF_TC(snprintf_count_overflow); ATF_TC_HEAD(snprintf_count_overflow, tc) { - atf_tc_set_md_var(tc, "descr", "checks snprintf count with overflow"); + atf_tc_set_md_var(tc, "descr", "checks snprintf count with overflow"); } ATF_TC_BODY(snprintf_count_overflow, tc) @@ -112,14 +116,46 @@ ATF_CHECK_EQ(i, 16); } +ATF_TC(vasprintf_print); +ATF_TC_HEAD(vasprintf_print, tc) +{ + atf_tc_set_md_var(tc, "descr", "checks vasprintf works"); +} + +static int __printflike(2, 3) +vasp_helper(char **buf, const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, fmt); + ret = vasprintf(buf, fmt, ap); + va_end(ap); + return ret; +} + +ATF_TC_BODY(vasprintf_print, tc) +{ + int i; + char *buf; + + buf = NULL; + i = vasp_helper(&buf, "N=%d C=%c S=%s", 7, 'x', "abc"); + ATF_CHECK_EQ(i, 13); + ATF_CHECK(buf != NULL); + ATF_CHECK_STREQ(buf, "N=7 C=x S=abc"); + free(buf); +} + ATF_TP_ADD_TCS(tp) { - ATF_TP_ADD_TC(tp, snprintf_print); - ATF_TP_ADD_TC(tp, snprintf_print_overflow); - ATF_TP_ADD_TC(tp, snprintf_count); - ATF_TP_ADD_TC(tp, snprintf_count_overflow); + ATF_TP_ADD_TC(tp, snprintf_print); + ATF_TP_ADD_TC(tp, snprintf_print_overflow); + ATF_TP_ADD_TC(tp, snprintf_count); + ATF_TP_ADD_TC(tp, snprintf_count_overflow); + ATF_TP_ADD_TC(tp, vasprintf_print); - return atf_no_error(); + return atf_no_error(); } _EOF diff --git a/kernel/h_fexecve.c b/kernel/h_fexecve.c new file mode 100644 --- /dev/null +++ b/kernel/h_fexecve.c @@ -0,0 +1,51 @@ +/* $NetBSD: h_fexecve.c,v 1.1 2019/09/15 16:53:58 christos Exp $ */ + +/*- + * Copyright (c) 2019 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +__RCSID("$NetBSD: h_fexecve.c,v 1.1 2019/09/15 16:53:58 christos Exp $"); + +#include +#include +#include +#include + +int +main(int argc, char *argv[]) +{ + char *args[] = { argv[1], NULL }; + int fd = open(args[0], O_RDONLY); + if (fd == -1) + err(EXIT_FAILURE, "open %s", args[0]); + if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) + err(EXIT_FAILURE, "fcntl"); + if (fexecve(fd, args, NULL) == -1) + err(EXIT_FAILURE, "fexecve"); + return EXIT_SUCCESS; +} diff --git a/kernel/h_fpufork.c b/kernel/h_fpufork.c new file mode 100644 --- /dev/null +++ b/kernel/h_fpufork.c @@ -0,0 +1,64 @@ +/* $NetBSD: h_fpufork.c,v 1.1 2020/02/11 03:15:10 riastradh Exp $ */ + +/*- + * Copyright (c) 2020 The NetBSD Foundation, Inc. + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +__RCSID("$NetBSD: h_fpufork.c,v 1.1 2020/02/11 03:15:10 riastradh Exp $"); + +#include + +#include +#include +#include +#include + +int +main(void) +{ + pid_t child, pid; + volatile double x = 1; + register double y = 2*x; + int status; + + pid = fork(); + switch (pid) { + case -1: /* error */ + err(1, "fork"); + case 0: /* child */ + _exit(y == 2 ? 0 : 1); + default: /* parent */ + break; + } + + if ((child = wait(&status)) == -1) + err(1, "wait"); + if (WIFSIGNALED(status)) + errx(1, "child exited on signal %d", WTERMSIG(status)); + if (!WIFEXITED(status)) + errx(1, "child didn't exit"); + return WEXITSTATUS(status); +} diff --git a/kernel/h_getprocpath.c b/kernel/h_getprocpath.c new file mode 100644 --- /dev/null +++ b/kernel/h_getprocpath.c @@ -0,0 +1,59 @@ +/*- + * Copyright (c) 2017 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +#include +#include +#include + +static int +getprocpath(char *path, size_t len, pid_t pid) +{ + const int name[] = { + CTL_KERN, KERN_PROC_ARGS, pid, KERN_PROC_PATHNAME, + }; + return sysctl(name, __arraycount(name), path, &len, NULL, 0); +} + +int +main(int argc, char *argv[]) +{ + if (argc != 2) { + fprintf(stderr, "Usage: %s \n", getprogname()); + return EXIT_FAILURE; + } + + char buf[MAXPATHLEN]; + pid_t pid = atoi(argv[1]); + + if (getprocpath(buf, sizeof(buf), pid) == -1) + err(EXIT_FAILURE, "sysctl: %d", pid); + printf("%s\n", buf); + return EXIT_SUCCESS; +} diff --git a/kernel/h_interpreter.sh b/kernel/h_interpreter.sh new file mode 100755 --- /dev/null +++ b/kernel/h_interpreter.sh @@ -0,0 +1,27 @@ +#!/bin/sh + +cmd=$1 +shift +case "${cmd}" in +interpreter) + "${1}" $$ + ;; +dot) + cp "${1}" . + z="./$(basename "${1}")" + x=$(${z} -1) + case ${x} in + /*) x=$(readlink "${x}");; + *) echo "non absolute path" 1>&2; exit 1;; + esac + + e=$(readlink "$(/bin/pwd)/${z}") + if [ "${x}" != "${e}" ]; then + echo bad: ${x} != ${e} 1>&2 + exit 1 + fi + ;; +*) + echo bad command ${cmd} +esac + diff --git a/kernel/h_segv.c b/kernel/h_segv.c new file mode 100644 --- /dev/null +++ b/kernel/h_segv.c @@ -0,0 +1,285 @@ +/* $NetBSD: h_segv.c,v 1.14 2019/04/25 19:37:09 kamil Exp $ */ + +/*- + * Copyright (c) 2017 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +__RCSID("$NetBSD: h_segv.c,v 1.14 2019/04/25 19:37:09 kamil Exp $"); + +#define __TEST_FENV + +#include +#include +#include + +#include +#include +#if (__arm__ && !__SOFTFP__) || __aarch64__ +#include /* only need for ARM Cortex/Neon hack */ +#endif +#include +#include +#include +#include +#include + +static int flags; +#define F_RECURSE 1 +#define F_HANDLE 2 +#define F_MASK 4 +#define F_IGNORE 8 +#define F_CHECK 16 + +static struct { + const char *n; + int v; +} nv[] = { + { "recurse", F_RECURSE }, + { "handle", F_HANDLE }, + { "mask", F_MASK }, + { "ignore", F_IGNORE }, + { "check", F_CHECK } +}; + +static int sig; +static struct { + const char *n; + int v; +} sn[] = { + { "segv", SIGSEGV }, + { "trap", SIGTRAP }, + { "ill", SIGILL }, + { "fpe", SIGFPE }, + { "bus", SIGBUS } +}; + +static void +trigger_segv(void) +{ + volatile int *p = (int *)(intptr_t)atoi("0"); + + *p = 1; +} + +static void +trigger_trap(void) +{ + +#ifdef PTRACE_BREAKPOINT_ASM + PTRACE_BREAKPOINT_ASM; +#else + /* port me */ +#endif +} + +static void +trigger_ill(void) +{ + +#ifdef PTRACE_ILLEGAL_ASM + PTRACE_ILLEGAL_ASM; +#else + /* port me */ +#endif +} + +static void +check_fpe(void) +{ +#if (__arm__ && !__SOFTFP__) || __aarch64__ + /* + * Some NEON fpus do not trap on IEEE 754 FP exceptions. + * Skip these tests if running on them and compiled for + * hard float. + */ + if (0 == fpsetmask(fpsetmask(FP_X_INV))) { + printf("FPU does not implement traps on FP exceptions\n"); + exit(EXIT_FAILURE); + } +#endif + exit(EXIT_SUCCESS); +} + +static void +trigger_fpe(void) +{ + volatile double a = getpid(); + volatile double b = strtol("0", NULL, 0); + +#ifdef __HAVE_FENV + feenableexcept(FE_ALL_EXCEPT); +#endif + + usleep((int)(a/b)); +} + +static void +trigger_bus(void) +{ + FILE *fp; + char *p; + + /* Open an empty file for writing. */ + fp = tmpfile(); + if (fp == NULL) + err(EXIT_FAILURE, "tmpfile"); + + /* + * Map an empty file with mmap(2) to a pointer. + * + * PROT_READ handles read-modify-write sequences emitted for + * certain combinations of CPUs and compilers (e.g. Alpha AXP). + */ + p = mmap(0, 1, PROT_READ|PROT_WRITE, MAP_PRIVATE, fileno(fp), 0); + if (p == MAP_FAILED) + err(EXIT_FAILURE, "mmap"); + + /* Invalid memory access causes CPU trap, translated to SIGBUS */ + *p = 'a'; +} + +static void +trigger(void) +{ + + switch (sig) { + case SIGSEGV: + trigger_segv(); + break; + case SIGTRAP: + trigger_trap(); + break; + case SIGILL: + trigger_ill(); + break; + case SIGFPE: + trigger_fpe(); + break; + case SIGBUS: + trigger_bus(); + break; + default: + break; + } +} + +static void +foo(int s) +{ + char buf[64]; + int i = snprintf(buf, sizeof(buf), "got %d\n", s); + write(2, buf, i); + + if (flags & F_RECURSE) + trigger(); + + exit(EXIT_SUCCESS); +} + +static __dead void +usage(void) +{ + const char *pname = getprogname(); + + fprintf(stderr, "Usage: %s segv|trap|ill|fpe|bus " + "[recurse|mask|handle|ignore|check] ...\n", pname); + + exit(EXIT_FAILURE); +} + +int +main(int argc, char *argv[]) +{ + + if (argc == 1) + usage(); + + for (int i = 1; i < argc; i++) { + size_t j; + for (j = 0; j < __arraycount(nv); j++) { + if (strcmp(nv[j].n, argv[i]) == 0) { + flags |= nv[j].v; + goto consumed; + } + } + for (j = 0; j < __arraycount(sn); j++) { + if (strcmp(sn[j].n, argv[i]) == 0) { + sig = sn[j].v; + goto consumed; + } + } + + usage(); + + consumed: + continue; + } + + if (flags == 0 || sig == 0) + usage(); + + if (flags & F_CHECK && sig != SIGFPE) { + fprintf(stderr, "can only check for fpe support\n"); + return 1; + } + if (flags & F_CHECK) + check_fpe(); + + if (flags & F_HANDLE) { + struct sigaction sa; + + sa.sa_flags = SA_RESTART; + sa.sa_handler = foo; + sigemptyset(&sa.sa_mask); + if (sigaction(sig, &sa, NULL) == -1) + err(EXIT_FAILURE, "sigaction"); + } + + if (flags & F_MASK) { + sigset_t set; + + sigemptyset(&set); + sigaddset(&set, sig); + if (sigprocmask(SIG_BLOCK, &set, NULL) == -1) + err(EXIT_FAILURE, "sigprocmask"); + } + + if (flags & F_IGNORE) { + struct sigaction sa; + + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = SIG_IGN; + sigemptyset(&sa.sa_mask); + if (sigaction(sig, &sa, NULL) == -1) + err(EXIT_FAILURE, "sigaction"); + } + + trigger(); + + return EXIT_SUCCESS; +} diff --git a/kernel/kqueue/read/t_ttypty.c b/kernel/kqueue/read/t_ttypty.c --- a/kernel/kqueue/read/t_ttypty.c +++ b/kernel/kqueue/read/t_ttypty.c @@ -1,7 +1,7 @@ -/* $NetBSD: t_ttypty.c,v 1.2 2017/01/13 21:30:41 christos Exp $ */ +/* $NetBSD: t_ttypty.c,v 1.3 2019/02/15 18:57:15 mgorny Exp $ */ /*- - * Copyright (c) 2002, 2008 The NetBSD Foundation, Inc. + * Copyright (c) 2002, 2008, 2019 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation @@ -30,11 +30,12 @@ */ #include -__COPYRIGHT("@(#) Copyright (c) 2008\ +__COPYRIGHT("@(#) Copyright (c) 2008, 2019\ The NetBSD Foundation, inc. All rights reserved."); -__RCSID("$NetBSD: t_ttypty.c,v 1.2 2017/01/13 21:30:41 christos Exp $"); +__RCSID("$NetBSD: t_ttypty.c,v 1.3 2019/02/15 18:57:15 mgorny Exp $"); #include +#include #include #include @@ -135,10 +136,48 @@ h_check(false); } +ATF_TC(closed_slave); +ATF_TC_HEAD(closed_slave, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Checks EVFILT_READ reporting for slave tty being closed"); +} +ATF_TC_BODY(closed_slave, tc) +{ + char slavetty[1024]; + struct kevent event[1]; + int amaster, aslave; + int kq, n; + struct timespec timeout = {5, 0}; + + RL(openpty(&amaster, &aslave, slavetty, NULL, NULL)); + + (void)printf("tty: openpty master %d slave %d tty '%s'\n", + amaster, aslave, slavetty); + + RL(kq = kqueue()); + + EV_SET(&event[0], amaster, EVFILT_READ, EV_ADD|EV_ENABLE, 0, 0, 0); + RL(kevent(kq, event, 1, NULL, 0, NULL)); + + RL(close(aslave)); + + RL(n = kevent(kq, NULL, 0, event, 1, &timeout)); + + (void)printf("kevent num %d filt %d flags: %#x, fflags: %#x, " + "data: %" PRId64 "\n", n, event[0].filter, event[0].flags, + event[0].fflags, event[0].data); + + ATF_REQUIRE_EQ(n, 1); + ATF_REQUIRE_EQ(event[0].filter, EVFILT_READ); + ATF_REQUIRE_EQ(event[0].flags & EV_EOF, EV_EOF); +} + ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, master); ATF_TP_ADD_TC(tp, slave); + ATF_TP_ADD_TC(tp, closed_slave); return atf_no_error(); } diff --git a/kernel/kqueue/t_ioctl.c b/kernel/kqueue/t_ioctl.c --- a/kernel/kqueue/t_ioctl.c +++ b/kernel/kqueue/t_ioctl.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_ioctl.c,v 1.3 2017/01/13 21:30:41 christos Exp $ */ +/* $NetBSD: t_ioctl.c,v 1.5 2020/10/31 14:57:02 christos Exp $ */ /*- * Copyright (c) 2002, 2008 The NetBSD Foundation, Inc. @@ -32,8 +32,9 @@ #include __COPYRIGHT("@(#) Copyright (c) 2008\ The NetBSD Foundation, inc. All rights reserved."); -__RCSID("$NetBSD: t_ioctl.c,v 1.3 2017/01/13 21:30:41 christos Exp $"); +__RCSID("$NetBSD: t_ioctl.c,v 1.5 2020/10/31 14:57:02 christos Exp $"); +#define EVFILT_NAMES #include #include @@ -61,13 +62,13 @@ km.name = buf; km.len = sizeof(buf) - 1; - for (i = 0; i < 7; ++i) { + for (i = 0; i < EVFILT_SYSCOUNT; ++i) { km.filter = i; RL(ioctl(kq, KFILTER_BYFILTER, &km)); (void)printf(" map %d -> %s\n", km.filter, km.name); } - km.filter = 7; + km.filter = EVFILT_SYSCOUNT; ATF_REQUIRE_EQ(ioctl(kq, KFILTER_BYFILTER, &km), -1); } @@ -78,27 +79,16 @@ } ATF_TC_BODY(kfilter_byname, tc) { - const char *tests[] = { - "EVFILT_READ", - "EVFILT_WRITE", - "EVFILT_AIO", - "EVFILT_VNODE", - "EVFILT_PROC", - "EVFILT_SIGNAL", - "EVFILT_TIMER", - NULL - }; char buf[32]; struct kfilter_mapping km; - const char **test; int kq; RL(kq = kqueue()); km.name = buf; - for (test = &tests[0]; *test != NULL; ++test) { - (void)strlcpy(buf, *test, sizeof(buf)); + for (size_t i = 0; i < EVFILT_SYSCOUNT; i++) { + (void)strlcpy(buf, evfiltnames[i], sizeof(buf)); RL(ioctl(kq, KFILTER_BYNAME, &km)); (void)printf(" map %s -> %d\n", km.name, km.filter); } diff --git a/kernel/msg.h b/kernel/msg.h deleted file mode 100644 --- a/kernel/msg.h +++ /dev/null @@ -1,136 +0,0 @@ -/* $NetBSD: msg.h,v 1.1 2016/12/05 20:10:10 christos Exp $ */ - -/*- - * Copyright (c) 2016 The NetBSD Foundation, Inc. - * All rights reserved. - * - * This code is derived from software contributed to The NetBSD Foundation - * by Christos Zoulas. - * - * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. - */ - -struct msg_fds { - int pfd[2]; - int cfd[2]; -}; - -#define CLOSEFD(fd) do { \ - if (fd != -1) { \ - close(fd); \ - fd = -1; \ - } \ -} while (/*CONSTCOND*/ 0) - -static int -msg_open(struct msg_fds *fds) -{ - if (pipe(fds->pfd) == -1) - return -1; - if (pipe(fds->cfd) == -1) { - close(fds->pfd[0]); - close(fds->pfd[1]); - return -1; - } - return 0; -} - -static void -msg_close(struct msg_fds *fds) -{ - CLOSEFD(fds->pfd[0]); - CLOSEFD(fds->pfd[1]); - CLOSEFD(fds->cfd[0]); - CLOSEFD(fds->cfd[1]); -} - -static int -msg_write_child(const char *info, struct msg_fds *fds, void *msg, size_t len) -{ - ssize_t rv; - CLOSEFD(fds->cfd[1]); - CLOSEFD(fds->pfd[0]); - - printf("Send %s\n", info); - rv = write(fds->pfd[1], msg, len); - if (rv != (ssize_t)len) - return 1; -// printf("Wait %s\n", info); - rv = read(fds->cfd[0], msg, len); - if (rv != (ssize_t)len) - return 1; - return 0; -} - -static int -msg_write_parent(const char *info, struct msg_fds *fds, void *msg, size_t len) -{ - ssize_t rv; - CLOSEFD(fds->pfd[1]); - CLOSEFD(fds->cfd[0]); - - printf("Send %s\n", info); - rv = write(fds->cfd[1], msg, len); - if (rv != (ssize_t)len) - return 1; -// printf("Wait %s\n", info); - rv = read(fds->pfd[0], msg, len); - if (rv != (ssize_t)len) - return 1; - return 0; -} - -static int -msg_read_parent(const char *info, struct msg_fds *fds, void *msg, size_t len) -{ - ssize_t rv; - CLOSEFD(fds->pfd[1]); - CLOSEFD(fds->cfd[0]); - - printf("Wait %s\n", info); - rv = read(fds->pfd[0], msg, len); - if (rv != (ssize_t)len) - return 1; -// printf("Send %s\n", info); - rv = write(fds->cfd[1], msg, len); - if (rv != (ssize_t)len) - return 1; - return 0; -} - -static int -msg_read_child(const char *info, struct msg_fds *fds, void *msg, size_t len) -{ - ssize_t rv; - CLOSEFD(fds->cfd[1]); - CLOSEFD(fds->pfd[0]); - - printf("Wait %s\n", info); - rv = read(fds->cfd[0], msg, len); - if (rv != (ssize_t)len) - return 1; -// printf("Send %s\n", info); - rv = write(fds->pfd[1], msg, len); - if (rv != (ssize_t)len) - return 1; - return 0; -} diff --git a/kernel/t_extattrctl.c b/kernel/t_extattrctl.c --- a/kernel/t_extattrctl.c +++ b/kernel/t_extattrctl.c @@ -9,7 +9,7 @@ ATF_TC_HEAD(extattrctl_namei, tc) { - atf_tc_set_md_var(tc, "descr", "extattrctl namei safety (kern/43328)"); + atf_tc_set_md_var(tc, "descr", "extattrctl namei safety (PR kern/43328)"); } ATF_TC_BODY(extattrctl_namei, tc) diff --git a/kernel/t_fcntl.c b/kernel/t_fcntl.c new file mode 100644 --- /dev/null +++ b/kernel/t_fcntl.c @@ -0,0 +1,89 @@ +/* $NetBSD: t_fcntl.c,v 1.2 2019/10/20 16:02:11 christos Exp $ */ + +/*- + * Copyright (c) 2019 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +#include +#include +#include +#include +#include +#include +#include + +ATF_TC(getpath); +ATF_TC_HEAD(getpath, tc) +{ + + atf_tc_set_md_var(tc, "descr", "Checks fcntl(2) F_GETPATH"); +} + +static const struct { + const char *name; + int rv; +} files[] = { + { "/bin/ls", 0 }, + { "/bin/sh", 0 }, + { "/dev/zero", 0 }, + { "/dev/null", 0 }, + { "/sbin/chown", 0 }, + { "/", ENOENT }, +}; + +ATF_TC_BODY(getpath, tc) +{ + char path[MAXPATHLEN]; + int fd, rv; + + for (size_t i = 0; i < __arraycount(files); i++) { + fd = open(files[i].name, O_RDONLY|O_NOFOLLOW); + ATF_REQUIRE_MSG(fd != -1, "Cannot open `%s'", files[i].name); + rv = fcntl(fd, F_GETPATH, path); + if (files[i].rv) { + ATF_REQUIRE_MSG(errno == files[i].rv, + "Unexpected error %d != %d for `%s'", errno, + files[i].rv, files[i].name); + } else { + ATF_REQUIRE_MSG(rv != -1, + "Can't get path for `%s' (%s)", files[i].name, + strerror(errno)); + ATF_REQUIRE_MSG(strcmp(files[i].name, path) == 0, + "Bad name `%s' != `%s'", path, files[i].name); + close(fd); + } + } +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, getpath); + + return atf_no_error(); +} diff --git a/kernel/t_fexecve.sh b/kernel/t_fexecve.sh new file mode 100644 --- /dev/null +++ b/kernel/t_fexecve.sh @@ -0,0 +1,72 @@ +# $NetBSD: t_fexecve.sh,v 1.1 2019/09/15 16:53:58 christos Exp $ +# +# Copyright (c) 2019 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Christos Zoulas. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +HELPER=$(atf_get_srcdir)/h_fexecve + +atf_test_case fexecve_elf +fexecve_elf() +{ + atf_set "descr" "Test fexecve with ELF executables" +} + +fexecve_elf_body() +{ + cat > hello.c << EOF +#include +int main(void) { + printf("hello world\n"); + return 0; +} +EOF + atf_check -s exit:0 -o ignore -e ignore cc -o hello hello.c + atf_check -o inline:"hello world\n" ${HELPER} ./hello +} + +atf_test_case fexecve_script +fexecve_script() +{ + atf_set "descr" "Test fexecve with a shell script" +} + +fexecve_script_body() +{ + cat > goodbye << EOF +#!/bin/sh +echo goodbye +EOF + atf_check -s exit:0 -o ignore -e ignore chmod +x goodbye + atf_check -s exit:0 -o inline:"goodbye\n" ${HELPER} ./goodbye +} + +atf_init_test_cases() +{ + atf_add_test_case fexecve_elf + atf_add_test_case fexecve_script +} diff --git a/kernel/t_fpufork.sh b/kernel/t_fpufork.sh new file mode 100644 --- /dev/null +++ b/kernel/t_fpufork.sh @@ -0,0 +1,41 @@ +# $NetBSD: t_fpufork.sh,v 1.1 2020/02/11 03:15:10 riastradh Exp $ +# +# Copyright (c) 2020 The NetBSD Foundation, Inc. +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + +atf_test_case fpufork +fpufork_head() +{ + atf_set "descr" "Verify fork preserves fpu" +} +fpufork_body() +{ + atf_check -s exit:0 -o inline: -e inline: \ + $(atf_get_srcdir)/h_fpufork +} + +atf_init_test_cases() +{ + atf_add_test_case fpufork +} diff --git a/kernel/t_interp.sh b/kernel/t_interp.sh new file mode 100644 --- /dev/null +++ b/kernel/t_interp.sh @@ -0,0 +1,45 @@ +# $NetBSD: t_interp.sh,v 1.1 2017/12/06 13:54:26 christos Exp $ +# +# This code is derived from software contributed to The NetBSD Foundation +# by Christos Zoulas. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# +atf_test_case procfs_interp +procfs_interp_head() { + atf_set "descr" "Tests the exe name in procfs is correct" +} +procfs_interp_body() { + atf_require_prog readlink + if [ ! -d /proc/curproc/. ]; then + atf_skip "procfs not mounted" + return + fi + local me=$(readlink /proc/$$/exe) + if [ "$me" != "/bin/sh" ]; then + atf_fail "Interpreter is not '/bin/sh': '$me'" + fi +} + +atf_init_test_cases() { + atf_add_test_case procfs_interp +} diff --git a/kernel/t_kauth_pr_47598.c b/kernel/t_kauth_pr_47598.c --- a/kernel/t_kauth_pr_47598.c +++ b/kernel/t_kauth_pr_47598.c @@ -27,7 +27,7 @@ #include __COPYRIGHT("@(#) Copyright (c) 2013\ The NetBSD Foundation, inc. All rights reserved."); -__RCSID("$NetBSD: t_kauth_pr_47598.c,v 1.3 2014/04/28 08:34:16 martin Exp $"); +__RCSID("$NetBSD: t_kauth_pr_47598.c,v 1.4 2020/02/10 16:51:48 riastradh Exp $"); #include #include @@ -120,21 +120,23 @@ * create a socket and bind it to some arbitray free port */ s = socket(PF_INET, SOCK_STREAM|SOCK_NONBLOCK, 0); - ATF_REQUIRE(s != -1); + ATF_REQUIRE_MSG(s != -1, "socket: %d", errno); memset(&sa, 0, sizeof(sa)); sa.sin_family = AF_INET; sa.sin_len = sizeof(sa); sa.sin_addr.s_addr = inet_addr("127.0.0.1"); - ATF_REQUIRE(bind(s, (struct sockaddr *)&sa, sizeof(sa))==0); - ATF_REQUIRE(listen(s, 16)==0); + ATF_REQUIRE_MSG(bind(s, (struct sockaddr *)&sa, sizeof(sa)) == 0, + "bind: %d", errno); + ATF_REQUIRE_MSG(listen(s, 16) == 0, "listen: %d", errno); /* * extract address and open a connection to the port */ slen = sizeof(sa); - ATF_REQUIRE(getsockname(s, (struct sockaddr *)&sa, &slen)==0); + ATF_REQUIRE_MSG(getsockname(s, (struct sockaddr *)&sa, &slen) == 0, + "getsockname: %d", errno); s2 = socket(PF_INET, SOCK_STREAM|SOCK_NONBLOCK, 0); - ATF_REQUIRE(s2 != -1); + ATF_REQUIRE_MSG(s2 != -1, "socket: %d", errno); printf("port is %d\n", ntohs(sa.sin_port)); err = connect(s2, (struct sockaddr *)&sa, sizeof(sa)); ATF_REQUIRE_MSG(err == -1 && errno == EINPROGRESS, diff --git a/kernel/t_ksem.c b/kernel/t_ksem.c new file mode 100644 --- /dev/null +++ b/kernel/t_ksem.c @@ -0,0 +1,145 @@ +/* $NetBSD: t_ksem.c,v 1.1 2019/02/03 03:20:24 thorpej Exp $ */ + +/* + * Copyright (c) 2019 The NetBSD Foundation, Inc. + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +__COPYRIGHT("@(#) Copyright (c) 2019\ + The NetBSD Foundation, inc. All rights reserved."); +__RCSID("$NetBSD: t_ksem.c,v 1.1 2019/02/03 03:20:24 thorpej Exp $"); + +#include +#include + +#include +#include +#include +#include + +#include + +#define _LIBC +#include + +ATF_TC(close_on_unnamed); +ATF_TC_HEAD(close_on_unnamed, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct error on close of unnamed semaphore"); +} +ATF_TC_BODY(close_on_unnamed, tc) +{ + intptr_t ksem; + + /* _ksem_close() is invalid on unnamed semaphore. */ + ksem = 0; + ATF_REQUIRE_EQ(_ksem_init(0, &ksem), 0); + ATF_REQUIRE(_ksem_close(ksem) == -1 && errno == EINVAL); + ATF_REQUIRE_EQ(_ksem_destroy(ksem), 0); +} + +ATF_TC(close_on_unnamed_pshared); +ATF_TC_HEAD(close_on_unnamed_pshared, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct error on close of unnamed pshared semaphore"); +} +ATF_TC_BODY(close_on_unnamed_pshared, tc) +{ + intptr_t ksem; + + /* Similar, but lifecycle of pshared is slightly different. */ + ksem = KSEM_PSHARED; + ATF_REQUIRE_EQ(_ksem_init(0, &ksem), 0); + ATF_REQUIRE(_ksem_close(ksem) == -1 && errno == EINVAL); + ATF_REQUIRE_EQ(_ksem_destroy(ksem), 0); +} + +ATF_TC_WITH_CLEANUP(destroy_on_named); +ATF_TC_HEAD(destroy_on_named, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct error on destroy of named semaphore"); +} +ATF_TC_BODY(destroy_on_named, tc) +{ + intptr_t ksem; + + /* Exercise open-unlinked semaphore lifecycle. */ + ATF_REQUIRE_EQ(_ksem_open("/ksem_x", O_CREAT | O_EXCL, 0644, 0, &ksem), + 0); + ATF_REQUIRE(_ksem_destroy(ksem) == -1 && errno == EINVAL); + ATF_REQUIRE_EQ(_ksem_close(ksem), 0); + ATF_REQUIRE_EQ(_ksem_unlink("/ksem_x"), 0); +} +ATF_TC_CLEANUP(destroy_on_named, tc) +{ + (void)_ksem_unlink("/ksem_x"); +} + +ATF_TC_WITH_CLEANUP(open_unlinked_lifecycle); +ATF_TC_HEAD(open_unlinked_lifecycle, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Exercises lifecycle of open-unlined semaphores"); +} +ATF_TC_BODY(open_unlinked_lifecycle, tc) +{ + intptr_t ksem, ksem1; + int val; + + /* Exercise open-unlinked semaphore lifecycle. */ + ATF_REQUIRE_EQ(_ksem_open("/ksem_b", O_CREAT | O_EXCL, 0644, 0, &ksem), + 0); + ATF_REQUIRE_EQ(_ksem_unlink("/ksem_b"), 0); + val = 255; + ATF_REQUIRE(_ksem_getvalue(ksem, &val) == 0 && val == 0); + + ATF_REQUIRE_EQ(_ksem_open("/ksem_b", O_CREAT | O_EXCL, 0644, 1, &ksem1), + 0); + ATF_REQUIRE_EQ(_ksem_unlink("/ksem_b"), 0); + val = 255; + ATF_REQUIRE(_ksem_getvalue(ksem1, &val) == 0 && val == 1); + + ATF_REQUIRE_EQ(_ksem_close(ksem), 0); + ATF_REQUIRE_EQ(_ksem_close(ksem1), 0); +} +ATF_TC_CLEANUP(open_unlinked_lifecycle, tc) +{ + (void)_ksem_unlink("/ksem_a"); + (void)_ksem_unlink("/ksem_b"); +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, close_on_unnamed); + ATF_TP_ADD_TC(tp, close_on_unnamed_pshared); + ATF_TP_ADD_TC(tp, destroy_on_named); + ATF_TP_ADD_TC(tp, open_unlinked_lifecycle); + + return atf_no_error(); +} diff --git a/kernel/t_magic_symlinks.sh b/kernel/t_magic_symlinks.sh new file mode 100644 --- /dev/null +++ b/kernel/t_magic_symlinks.sh @@ -0,0 +1,254 @@ +# $NetBSD: t_magic_symlinks.sh,v 1.1 2020/07/01 13:49:26 jruoho Exp $ +# +# Copyright (c) 2020 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Jukka Ruohonen. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# +tmpdir="/tmp/test-magic-symlink" + +init() { + + enabled=$(sysctl vfs.generic.magiclinks | awk '{print $3}') + + if [ $enabled -eq 0 ]; then + sysctl -w vfs.generic.magiclinks=1 >/dev/null 2>&1 + echo "Initialized vfs.generic.magiclinks=1" + fi + + mkdir "$tmpdir" + echo "$enabled" > "$tmpdir/enabled" +} + +clean() { + + enabled=$(cat "$tmpdir/enabled") + + if [ $enabled -eq 0 ]; then + sysctl -w vfs.generic.magiclinks=$enabled >/dev/null 2>&1 + echo "Restored vfs.generic.magiclinks=$enabled" + fi + + rm -rf $tmpdir +} + +check() { + + init + cd "$tmpdir" + mkdir "$1" + echo 1 > "$1/magic" + ln -s "$2" "link" + cd "link" + + if [ -z $(pwd | grep "$1") ]; then + atf_fail "kernel does not handle magic symlinks properly" + fi + + if [ ! $(cat "magic") -eq 1 ]; then + atf_fail "kernel does not handle magic symlinks properly" + fi +} + +# @domainname +# +atf_test_case domainname cleanup +domainname_head() { + atf_set "require.user" "root" + atf_set "descr" "Check that @domainname magic symlinks work" +} + +domainname_body() { + check "$(domainname)" "@domainname" +} + +domainname_cleanup() { + clean +} + +# @hostname +# +atf_test_case hostname cleanup +hostname_head() { + atf_set "require.user" "root" + atf_set "descr" "Check that @hostname magic symlinks work" +} + +hostname_body() { + check "$(hostname)" "@hostname" +} + +hostname_cleanup() { + clean +} + +# @machine +# +atf_test_case machine cleanup +machine_head() { + atf_set "require.user" "root" + atf_set "descr" "Check that @machine magic symlinks work" +} + +machine_body() { + check "$(uname -m)" "@machine" +} + +machine_cleanup() { + clean +} + +# @machine_arch +# +atf_test_case machine_arch cleanup +machine_arch_head() { + atf_set "require.user" "root" + atf_set "descr" "Check that @machine_arch magic symlinks work" +} + +machine_arch_body() { + check "$(uname -p)" "@machine_arch" +} + +machine_arch_cleanup() { + clean +} + +# @ostype +# +atf_test_case ostype cleanup +ostype_head() { + atf_set "require.user" "root" + atf_set "descr" "Check that @ostype magic symlinks work" +} + +ostype_body() { + check "$(uname -s)" "@ostype" +} + +ostype_cleanup() { + clean +} + +# @ruid +# +atf_test_case ruid cleanup +ruid_head() { + atf_set "require.user" "root" + atf_set "descr" "Check that @ruid magic symlinks work" +} + +ruid_body() { + check "$(id -ru)" "@ruid" +} + +ruid_cleanup() { + clean +} + +# @uid +# +atf_test_case uid cleanup +uid_head() { + atf_set "require.user" "root" + atf_set "descr" "Check that @uid magic symlinks work" +} + +uid_body() { + check "$(id -u)" "@uid" +} + +uid_cleanup() { + clean +} + +# @rgid +# +atf_test_case rgid cleanup +rgid_head() { + atf_set "require.user" "root" + atf_set "descr" "Check that @rgid magic symlinks work" +} + +rgid_body() { + check "$(id -rg)" "@rgid" +} + +rgid_cleanup() { + clean +} + +# @gid +# +atf_test_case gid cleanup +gid_head() { + atf_set "require.user" "root" + atf_set "descr" "Check that @gid magic symlinks work" +} + +gid_body() { + check "$(id -g)" "@gid" +} + +gid_cleanup() { + clean +} + +# realpath(1) +# +atf_test_case realpath cleanup +nointerpreter_head() { + atf_set "require.user" "root" + atf_set "descr" "Check that realpath(1) agrees with the " + "the kernel on magic symlink(7)'s (PR lib/55361)" +} + +realpath_body() { + + check "$(uname -r)" "@osrelease" + realpath "$tmpdir/link" + + if [ ! $? -eq 0 ]; then + atf_expect_fail "PR lib/55361" + atf_fail "realpath does not handle magic symlinks properly" + fi +} + +realpath_cleanup() { + clean +} + +atf_init_test_cases() { + atf_add_test_case domainname + atf_add_test_case hostname + atf_add_test_case machine + atf_add_test_case machine_arch + atf_add_test_case ostype + atf_add_test_case ruid + atf_add_test_case uid + atf_add_test_case rgid + atf_add_test_case gid + atf_add_test_case realpath +} diff --git a/kernel/t_nointerpreter.sh b/kernel/t_nointerpreter.sh new file mode 100644 --- /dev/null +++ b/kernel/t_nointerpreter.sh @@ -0,0 +1,59 @@ +# $NetBSD: t_nointerpreter.sh,v 1.1 2020/06/25 16:16:48 jruoho Exp $ +# +# Copyright (c) 2020 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Jukka Ruohonen. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# +tmp="/tmp/nointerpreter" + +atf_test_case nointerpreter cleanup +nointerpreter_head() { + atf_set "descr" "Check that executing shell scripts without a " \ + "specified interpreter does not spam (PR kern/52744)" +} + +nointerpreter_body() { + + echo "echo hello world" > $tmp + chmod u+x $tmp + . $tmp + spam=$(dmesg | grep "check exec failed") + + if [ ! -z $spam ]; then + atf_fail "Kernel spams harmless diagnostics" + fi +} + +nointerpreter_cleanup() { + + if [ -f $tmp ]; then + rm $tmp + fi +} + +atf_init_test_cases() { + atf_add_test_case nointerpreter +} diff --git a/kernel/t_origin.sh b/kernel/t_origin.sh new file mode 100644 --- /dev/null +++ b/kernel/t_origin.sh @@ -0,0 +1,122 @@ +# $NetBSD: t_origin.sh,v 1.1 2019/06/07 21:18:16 christos Exp $ +# +# Copyright (c) 2019 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Christos Zoulas. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +atf_test_case origin_simple +origin_simple_head() { + atf_set "descr" 'test native $ORIGIN support' + atf_set "require.progs" "cc" +} + +atf_test_case origin_simple_32 +origin_simple_32_head() { + atf_set "descr" 'test $ORIGIN support in 32 bit mode' + atf_set "require.progs" "cc" +} + +make_code() { + cat > origin$1.c << _EOF +#include +#include +#include +#include + +static const char lib[] = "libfoo$1.so"; +int +main(void) +{ + void *h = dlopen(lib, RTLD_NOW); + if (h == NULL) + errx(EXIT_FAILURE, "dlopen: %s", dlerror()); + dlclose(h); + return EXIT_SUCCESS; +} +_EOF + +cat > libfoo$1.c << EOF +void foo(void); +void foo(void) +{ +} +EOF +} + +test_code() { + local m32 + if [ -z "$1" ]; then + m32= + else + m32=-m32 + fi + atf_check -s exit:0 -o empty -e empty \ + cc -fPIC $m32 -o origin$1 origin$1.c -Wl,-R'$ORIGIN' + atf_check -s exit:0 -o empty -e empty \ + cc -shared -fPIC $m32 -o libfoo$1.so libfoo$1.c + atf_check -s exit:0 -o empty -e empty ./origin$1 +} + +check32() { + # check whether this arch is 64bit + if ! cc -dM -E - < /dev/null | fgrep -q _LP64; then + atf_skip "this is not a 64 bit architecture" + return 1 + fi + if ! cc -m32 -dM -E - < /dev/null 2>/dev/null > ./def32; then + atf_skip "c++ -m32 not supported on this architecture" + return 1 + else + if fgrep -q _LP64 ./def32; then + atf_fail "c++ -m32 does not generate netbsd32 binaries" + return 1 + fi + return 0 + fi +} + +origin_simple_body() { + make_code "" + test_code "" + +} + +origin_simple_32_body() { + if ! check32; then + return + fi + make_code "32" + test_code "32" +} + + +atf_init_test_cases() +{ + + atf_add_test_case origin_simple + atf_add_test_case origin_simple_32 +} diff --git a/kernel/t_proccwd.c b/kernel/t_proccwd.c new file mode 100644 --- /dev/null +++ b/kernel/t_proccwd.c @@ -0,0 +1,154 @@ +/* $NetBSD: t_proccwd.c,v 1.2 2019/06/01 22:18:23 kamil Exp $ */ +/*- + * Copyright (c) 2019 The NetBSD Foundation, Inc. + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +__COPYRIGHT("@(#) Copyright (c) 2019\ + The NetBSD Foundation, inc. All rights reserved."); +__RCSID("$NetBSD: t_proccwd.c,v 1.2 2019/06/01 22:18:23 kamil Exp $"); + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +static int +getproccwd(char *path, size_t *len, pid_t pid) +{ + const int name[] = { + CTL_KERN, KERN_PROC_ARGS, pid, KERN_PROC_CWD, + }; + + return sysctl(name, __arraycount(name), path, len, NULL, 0); +} + +ATF_TC(prompt_pid); +ATF_TC_HEAD(prompt_pid, tc) +{ + + atf_tc_set_md_var(tc, "descr", + "Prompt length of the current dir and assert that it is sane"); +} + +ATF_TC_BODY(prompt_pid, tc) +{ + char buf[MAXPATHLEN]; + char cwdbuf[MAXPATHLEN]; + size_t len, prompted_len; + size_t i; + + pid_t t[] = { + -1, + getpid(), + 1 /* /sbin/init */ + }; + + for (i = 0; i < __arraycount(t); i++) { + len = 0; + ATF_REQUIRE_EQ(getproccwd(NULL, &len, t[i]), 0); + + prompted_len = len; + ATF_REQUIRE_EQ(getproccwd(buf, &len, t[i]), 0); + + ATF_REQUIRE_EQ(strlen(buf) + 1, prompted_len); + ATF_REQUIRE(strlen(buf) > 0); + + if (t[i] == -1 || t[i] == getpid()) { + getcwd(cwdbuf, MAXPATHLEN); + ATF_REQUIRE_EQ(strcmp(buf, cwdbuf), 0); + ATF_REQUIRE(strlen(buf) > strlen("/")); + } else if (t[i] == 1) { + ATF_REQUIRE_EQ(strcmp(buf, "/"), 0); + } + } +} + +ATF_TC(chroot); +ATF_TC_HEAD(chroot, tc) +{ + + atf_tc_set_md_var(tc, "descr", + "prompt length of the current dir and assert that it is right"); + + atf_tc_set_md_var(tc, "require.user", "root"); +} + +#define ASSERT(x) do { if (!(x)) _exit(EXIT_FAILURE); } while (0) + +ATF_TC_BODY(chroot, tc) +{ + char buf[MAXPATHLEN]; + struct stat root_dir; + struct stat cur_dir; + size_t len; + pid_t child, wpid; + int status; + int rv; + + const pid_t pid_one = 1; /* /sbin/init */ + + ATF_REQUIRE(getcwd(buf, sizeof(buf)) != NULL); + + ATF_REQUIRE_EQ(stat(buf, &cur_dir), 0); + ATF_REQUIRE_EQ(stat("/", &root_dir), 0); + + if (cur_dir.st_ino == root_dir.st_ino) + atf_tc_skip("This test does not work in /"); + + child = atf_utils_fork(); + if (child == 0) { + len = 0; + + ASSERT(chroot(buf) == 0); + + errno = 0; + rv = getproccwd(buf, &len, pid_one); + ASSERT(rv == -1); + ASSERT(errno == ENOENT); + + _exit(EXIT_SUCCESS); + } + wpid = waitpid(child, &status, WEXITED); + ATF_REQUIRE_EQ(wpid, child); + ATF_REQUIRE_EQ(WEXITSTATUS(status), EXIT_SUCCESS); +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, prompt_pid); + ATF_TP_ADD_TC(tp, chroot); + + return atf_no_error(); +} diff --git a/kernel/t_procpath.sh b/kernel/t_procpath.sh new file mode 100644 --- /dev/null +++ b/kernel/t_procpath.sh @@ -0,0 +1,72 @@ +# $NetBSD: t_procpath.sh,v 1.1 2017/12/10 15:37:54 christos Exp $ +# +# Copyright (c) 2017 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Christos Zoulas. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +HELPER=$(atf_get_srcdir)/h_getprocpath +INTERP=$(atf_get_srcdir)/h_interpreter + +atf_test_case absolute_proc +absolute_proc_head() +{ + atf_set "descr" "Test absolute process argv0" +} +absolute_proc_body() +{ + atf_check -s exit:0 -o "inline:${HELPER}\n" -e "inline:" \ + ${HELPER} -1 +} + +atf_test_case interpeter_proc +interpreter_proc_head() +{ + atf_set "descr" "Test interpreter proc contains interpreter" +} +interpreter_proc_body() +{ + atf_check -s exit:0 -o "inline:/bin/sh\n" -e "inline:" \ + ${INTERP} interpreter ${HELPER} +} + +atf_test_case relative_proc +relative_proc_head() +{ + atf_set "descr" "Test that masking the trapped signal get reset" +} +relative_proc_body() +{ + atf_check -s exit:0 -o "inline:" -e "inline:" \ + ${INTERP} dot ${HELPER} +} + +atf_init_test_cases() +{ + atf_add_test_case absolute_proc + atf_add_test_case interpreter_proc + atf_add_test_case relative_proc +} diff --git a/kernel/t_ps_strings.sh b/kernel/t_ps_strings.sh old mode 100755 new mode 100644 diff --git a/kernel/t_ptrace.c b/kernel/t_ptrace.c deleted file mode 100644 --- a/kernel/t_ptrace.c +++ /dev/null @@ -1,208 +0,0 @@ -/* $NetBSD: t_ptrace.c,v 1.18 2017/01/13 21:30:41 christos Exp $ */ - -/*- - * Copyright (c) 2016 The NetBSD Foundation, Inc. - * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 -__RCSID("$NetBSD: t_ptrace.c,v 1.18 2017/01/13 21:30:41 christos Exp $"); - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "h_macros.h" - -/* - * A child process cannot call atf functions and expect them to magically - * work like in the parent. - * The printf(3) messaging from a child will not work out of the box as well - * without estabilishing a communication protocol with its parent. To not - * overcomplicate the tests - do not log from a child and use err(3)/errx(3) - * wrapped with FORKEE_ASSERT()/FORKEE_ASSERTX() as that is guaranteed to work. - */ -#define FORKEE_ASSERTX(x) \ -do { \ - int ret = (x); \ - if (!ret) \ - errx(EXIT_FAILURE, "%s:%d %s(): Assertion failed for: %s", \ - __FILE__, __LINE__, __func__, #x); \ -} while (0) - -#define FORKEE_ASSERT(x) \ -do { \ - int ret = (x); \ - if (!ret) \ - err(EXIT_FAILURE, "%s:%d %s(): Assertion failed for: %s", \ - __FILE__, __LINE__, __func__, #x); \ -} while (0) - -ATF_TC(attach_pid0); -ATF_TC_HEAD(attach_pid0, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Assert that a debugger cannot attach to PID 0"); -} - -ATF_TC_BODY(attach_pid0, tc) -{ - errno = 0; - ATF_REQUIRE_ERRNO(EPERM, ptrace(PT_ATTACH, 0, NULL, 0) == -1); -} - -ATF_TC(attach_pid1); -ATF_TC_HEAD(attach_pid1, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Assert that a debugger cannot attach to PID 1 (as non-root)"); - - atf_tc_set_md_var(tc, "require.user", "unprivileged"); -} - -ATF_TC_BODY(attach_pid1, tc) -{ - ATF_REQUIRE_ERRNO(EPERM, ptrace(PT_ATTACH, 1, NULL, 0) == -1); -} - -ATF_TC(attach_pid1_securelevel); -ATF_TC_HEAD(attach_pid1_securelevel, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Assert that a debugger cannot attach to PID 1 with " - "securelevel >= 1 (as root)"); - - atf_tc_set_md_var(tc, "require.user", "root"); -} - -ATF_TC_BODY(attach_pid1_securelevel, tc) -{ - int level; - size_t len = sizeof(level); - - ATF_REQUIRE(sysctlbyname("kern.securelevel", &level, &len, NULL, 0) - != -1); - - if (level < 1) { - atf_tc_skip("Test must be run with securelevel >= 1"); - } - - ATF_REQUIRE_ERRNO(EPERM, ptrace(PT_ATTACH, 1, NULL, 0) == -1); -} - -ATF_TC(attach_self); -ATF_TC_HEAD(attach_self, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Assert that a debugger cannot attach to self (as it's nonsense)"); -} - -ATF_TC_BODY(attach_self, tc) -{ - ATF_REQUIRE_ERRNO(EINVAL, ptrace(PT_ATTACH, getpid(), NULL, 0) == -1); -} - -ATF_TC(attach_chroot); -ATF_TC_HEAD(attach_chroot, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Assert that a debugger cannot trace another process unless the " - "process's root directory is at or below the tracing process's " - "root"); - - atf_tc_set_md_var(tc, "require.user", "root"); -} - -ATF_TC_BODY(attach_chroot, tc) -{ - char buf[PATH_MAX]; - pid_t child; - int fds_toparent[2], fds_fromparent[2]; - int rv; - uint8_t msg = 0xde; /* dummy message for IPC based on pipe(2) */ - - (void)memset(buf, '\0', sizeof(buf)); - ATF_REQUIRE(getcwd(buf, sizeof(buf)) != NULL); - (void)strlcat(buf, "/dir", sizeof(buf)); - - ATF_REQUIRE(mkdir(buf, 0500) == 0); - ATF_REQUIRE(chdir(buf) == 0); - - ATF_REQUIRE(pipe(fds_toparent) == 0); - ATF_REQUIRE(pipe(fds_fromparent) == 0); - child = atf_utils_fork(); - if (child == 0) { - FORKEE_ASSERT(close(fds_toparent[0]) == 0); - FORKEE_ASSERT(close(fds_fromparent[1]) == 0); - - FORKEE_ASSERT(chroot(buf) == 0); - - rv = write(fds_toparent[1], &msg, sizeof(msg)); - FORKEE_ASSERTX(rv == sizeof(msg)); - - ATF_REQUIRE_ERRNO(EPERM, - ptrace(PT_ATTACH, getppid(), NULL, 0) == -1); - - rv = read(fds_fromparent[0], &msg, sizeof(msg)); - FORKEE_ASSERTX(rv == sizeof(msg)); - - _exit(0); - } - ATF_REQUIRE(close(fds_toparent[1]) == 0); - ATF_REQUIRE(close(fds_fromparent[0]) == 0); - - printf("Waiting for chrooting of the child PID %d", child); - rv = read(fds_toparent[0], &msg, sizeof(msg)); - ATF_REQUIRE(rv == sizeof(msg)); - - printf("Child is ready, it will try to PT_ATTACH to parent\n"); - rv = write(fds_fromparent[1], &msg, sizeof(msg)); - ATF_REQUIRE(rv == sizeof(msg)); - - printf("fds_fromparent is no longer needed - close it\n"); - ATF_REQUIRE(close(fds_fromparent[1]) == 0); - - printf("fds_toparent is no longer needed - close it\n"); - ATF_REQUIRE(close(fds_toparent[0]) == 0); -} - -ATF_TP_ADD_TCS(tp) -{ - setvbuf(stdout, NULL, _IONBF, 0); - setvbuf(stderr, NULL, _IONBF, 0); - ATF_TP_ADD_TC(tp, attach_pid0); - ATF_TP_ADD_TC(tp, attach_pid1); - ATF_TP_ADD_TC(tp, attach_pid1_securelevel); - ATF_TP_ADD_TC(tp, attach_self); - ATF_TP_ADD_TC(tp, attach_chroot); - - return atf_no_error(); -} diff --git a/kernel/t_ptrace_wait.h b/kernel/t_ptrace_wait.h deleted file mode 100644 --- a/kernel/t_ptrace_wait.h +++ /dev/null @@ -1,431 +0,0 @@ -/* $NetBSD: t_ptrace_wait.h,v 1.7 2017/01/09 22:09:20 kamil Exp $ */ - -/*- - * Copyright (c) 2016 The NetBSD Foundation, Inc. - * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. - */ - -/* Detect plain wait(2) use-case */ -#if !defined(TWAIT_WAITPID) && \ - !defined(TWAIT_WAITID) && \ - !defined(TWAIT_WAIT3) && \ - !defined(TWAIT_WAIT4) && \ - !defined(TWAIT_WAIT6) -#define TWAIT_WAIT -#endif - -/* - * There are two classes of wait(2)-like functions: - * - wait4(2)-like accepting pid_t, optional options parameter, struct rusage* - * - wait6(2)-like accepting idtype_t, id_t, struct wrusage, mandatory options - * - * The TWAIT_FNAME value is to be used for convenience in debug messages. - * - * The TWAIT_GENERIC() macro is designed to reuse the same unmodified - * code with as many wait(2)-like functions as possible. - * - * In a common use-case wait4(2) and wait6(2)-like function can work the almost - * the same way, however there are few important differences: - * wait6(2) must specify P_PID for idtype to match wpid from wait4(2). - * To behave like wait4(2), wait6(2) the 'options' to wait must include - * WEXITED|WTRUNCATED. - * - * There are two helper macros (they purpose it to mach more than one - * wait(2)-like function): - * The TWAIT_HAVE_STATUS - specifies whether a function can retrieve - * status (as integer value). - * The TWAIT_HAVE_PID - specifies whether a function can request - * exact process identifier - * The TWAIT_HAVE_RUSAGE - specifies whether a function can request - * the struct rusage value - * - */ - -#if defined(TWAIT_WAIT) -# define TWAIT_FNAME "wait" -# define TWAIT_WAIT4TYPE(a,b,c,d) wait((b)) -# define TWAIT_GENERIC(a,b,c) wait((b)) -# define TWAIT_HAVE_STATUS 1 -#elif defined(TWAIT_WAITPID) -# define TWAIT_FNAME "waitpid" -# define TWAIT_WAIT4TYPE(a,b,c,d) waitpid((a),(b),(c)) -# define TWAIT_GENERIC(a,b,c) waitpid((a),(b),(c)) -# define TWAIT_HAVE_PID 1 -# define TWAIT_HAVE_STATUS 1 -#elif defined(TWAIT_WAITID) -# define TWAIT_FNAME "waitid" -# define TWAIT_GENERIC(a,b,c) \ - waitid(P_PID,(a),NULL,(c)|WEXITED|WTRAPPED) -# define TWAIT_WAIT6TYPE(a,b,c,d,e,f) waitid((a),(b),(f),(d)) -# define TWAIT_HAVE_PID 1 -#elif defined(TWAIT_WAIT3) -# define TWAIT_FNAME "wait3" -# define TWAIT_WAIT4TYPE(a,b,c,d) wait3((b),(c),(d)) -# define TWAIT_GENERIC(a,b,c) wait3((b),(c),NULL) -# define TWAIT_HAVE_STATUS 1 -# define TWAIT_HAVE_RUSAGE 1 -#elif defined(TWAIT_WAIT4) -# define TWAIT_FNAME "wait4" -# define TWAIT_WAIT4TYPE(a,b,c,d) wait4((a),(b),(c),(d)) -# define TWAIT_GENERIC(a,b,c) wait4((a),(b),(c),NULL) -# define TWAIT_HAVE_PID 1 -# define TWAIT_HAVE_STATUS 1 -# define TWAIT_HAVE_RUSAGE 1 -#elif defined(TWAIT_WAIT6) -# define TWAIT_FNAME "wait6" -# define TWAIT_WAIT6TYPE(a,b,c,d,e,f) wait6((a),(b),(c),(d),(e),(f)) -# define TWAIT_GENERIC(a,b,c) \ - wait6(P_PID,(a),(b),(c)|WEXITED|WTRAPPED,NULL,NULL) -# define TWAIT_HAVE_PID 1 -# define TWAIT_HAVE_STATUS 1 -#endif - -/* - * There are 3 groups of tests: - * - TWAIT_GENERIC() (wait, wait2, waitpid, wait3, wait4, wait6) - * - TWAIT_WAIT4TYPE() (wait2, waitpid, wait3, wait4) - * - TWAIT_WAIT6TYPE() (waitid, wait6) - * - * Tests only in the above categories are allowed. However some tests are not - * possible in the context requested functionality to be verified, therefore - * there are helper macros: - * - TWAIT_HAVE_PID (wait2, waitpid, waitid, wait4, wait6) - * - TWAIT_HAVE_STATUS (wait, wait2, waitpid, wait3, wait4, wait6) - * - TWAIT_HAVE_RUSAGE (wait3, wait4) - * - TWAIT_HAVE_RETPID (wait, wait2, waitpid, wait3, wait4, wait6) - * - * If there is an intention to test e.g. wait6(2) specific features in the - * ptrace(2) context, find the most matching group and with #ifdefs reduce - * functionality of less featured than wait6(2) interface (TWAIT_WAIT6TYPE). - * - * For clarity never use negative preprocessor checks, like: - * #if !defined(TWAIT_WAIT4) - * always refer to checks for positive values. - */ - -#define TEST_REQUIRE_EQ(x, y) \ -do { \ - uintmax_t vx = (x); \ - uintmax_t vy = (y); \ - int ret = vx == vy; \ - if (!ret) \ - ATF_REQUIRE_EQ_MSG(vx, vy, "%s(%ju) == %s(%ju)", \ - #x, vx, #y, vy); \ -} while (/*CONSTCOND*/0) - -/* - * A child process cannot call atf functions and expect them to magically - * work like in the parent. - * The printf(3) messaging from a child will not work out of the box as well - * without estabilishing a communication protocol with its parent. To not - * overcomplicate the tests - do not log from a child and use err(3)/errx(3) - * wrapped with FORKEE_ASSERT()/FORKEE_ASSERTX() as that is guaranteed to work. - */ -#define FORKEE_ASSERT_EQ(x, y) \ -do { \ - uintmax_t vx = (x); \ - uintmax_t vy = (y); \ - int ret = vx == vy; \ - if (!ret) \ - errx(EXIT_FAILURE, "%s:%d %s(): Assertion failed for: " \ - "%s(%ju) == %s(%ju)", __FILE__, __LINE__, __func__, \ - #x, vx, #y, vy); \ -} while (/*CONSTCOND*/0) - -#define FORKEE_ASSERTX(x) \ -do { \ - int ret = (x); \ - if (!ret) \ - errx(EXIT_FAILURE, "%s:%d %s(): Assertion failed for: %s",\ - __FILE__, __LINE__, __func__, #x); \ -} while (/*CONSTCOND*/0) - -#define FORKEE_ASSERT(x) \ -do { \ - int ret = (x); \ - if (!ret) \ - err(EXIT_FAILURE, "%s:%d %s(): Assertion failed for: %s",\ - __FILE__, __LINE__, __func__, #x); \ -} while (/*CONSTCOND*/0) - -/* - * Simplify logic for functions using general purpose registers add HAVE_GPREGS - * - * For platforms that do not implement all needed calls for simplicity assume - * that they are unsupported at all. - */ -#if defined(PT_GETREGS) \ - && defined(PT_SETREGS) \ - && defined(PTRACE_REG_PC) \ - && defined(PTRACE_REG_SET_PC) \ - && defined(PTRACE_REG_SP) \ - && defined(PTRACE_REG_INTRV) -#define HAVE_GPREGS -#endif - -/* Add guards for floating point registers */ -#if defined(PT_GETFPREGS) \ - && defined(PT_SETFPREGS) -#define HAVE_FPREGS -#endif - -/* Add guards for cpu debug registers */ -#if defined(PT_GETDBREGS) \ - && defined(PT_SETDBREGS) -#define HAVE_DBREGS -#endif - -/* - * If waitid(2) returns because one or more processes have a state change to - * report, 0 is returned. If an error is detected, a value of -1 is returned - * and errno is set to indicate the error. If WNOHANG is specified and there - * are no stopped, continued or exited children, 0 is returned. - */ -#if defined(TWAIT_WAITID) -#define TWAIT_REQUIRE_SUCCESS(a,b) TEST_REQUIRE_EQ((a), 0) -#define TWAIT_REQUIRE_FAILURE(a,b) ATF_REQUIRE_ERRNO((a),(b) == -1) -#define FORKEE_REQUIRE_SUCCESS(a,b) FORKEE_ASSERT_EQ(a, 0) -#define FORKEE_REQUIRE_FAILURE(a,b) \ - FORKEE_ASSERTX(((a) == errno) && ((b) == -1)) -#else -#define TWAIT_REQUIRE_SUCCESS(a,b) TEST_REQUIRE_EQ((a), (b)) -#define TWAIT_REQUIRE_FAILURE(a,b) ATF_REQUIRE_ERRNO((a),(b) == -1) -#define FORKEE_REQUIRE_SUCCESS(a,b) FORKEE_ASSERT_EQ(a, b) -#define FORKEE_REQUIRE_FAILURE(a,b) \ - FORKEE_ASSERTX(((a) == errno) && ((b) == -1)) -#endif - -/* - * Helper tools to verify whether status reports exited value - */ -#if TWAIT_HAVE_STATUS -static void __used -validate_status_exited(int status, int expected) -{ - ATF_REQUIRE_MSG(WIFEXITED(status), "Reported !exited process"); - ATF_REQUIRE_MSG(!WIFCONTINUED(status), "Reported continued process"); - ATF_REQUIRE_MSG(!WIFSIGNALED(status), "Reported signaled process"); - ATF_REQUIRE_MSG(!WIFSTOPPED(status), "Reported stopped process"); - - ATF_REQUIRE_EQ_MSG(WEXITSTATUS(status), expected, - "The process has exited with invalid value %d != %d", - WEXITSTATUS(status), expected); -} - -static void __used -forkee_status_exited(int status, int expected) -{ - FORKEE_ASSERTX(WIFEXITED(status)); - FORKEE_ASSERTX(!WIFCONTINUED(status)); - FORKEE_ASSERTX(!WIFSIGNALED(status)); - FORKEE_ASSERTX(!WIFSTOPPED(status)); - - FORKEE_ASSERT_EQ(WEXITSTATUS(status), expected); -} - -static void __used -validate_status_continued(int status) -{ - ATF_REQUIRE_MSG(!WIFEXITED(status), "Reported exited process"); - ATF_REQUIRE_MSG(WIFCONTINUED(status), "Reported !continued process"); - ATF_REQUIRE_MSG(!WIFSIGNALED(status), "Reported signaled process"); - ATF_REQUIRE_MSG(!WIFSTOPPED(status), "Reported stopped process"); -} - -static void __used -forkee_status_continued(int status) -{ - FORKEE_ASSERTX(!WIFEXITED(status)); - FORKEE_ASSERTX(WIFCONTINUED(status)); - FORKEE_ASSERTX(!WIFSIGNALED(status)); - FORKEE_ASSERTX(!WIFSTOPPED(status)); -} - -static void __used -validate_status_signaled(int status, int expected_termsig, int expected_core) -{ - ATF_REQUIRE_MSG(!WIFEXITED(status), "Reported exited process"); - ATF_REQUIRE_MSG(!WIFCONTINUED(status), "Reported continued process"); - ATF_REQUIRE_MSG(WIFSIGNALED(status), "Reported !signaled process"); - ATF_REQUIRE_MSG(!WIFSTOPPED(status), "Reported stopped process"); - - ATF_REQUIRE_EQ_MSG(WTERMSIG(status), expected_termsig, - "Unexpected signal received"); - - ATF_REQUIRE_EQ_MSG(WCOREDUMP(status), expected_core, - "Unexpectedly core file %s generated", expected_core ? "not" : ""); -} - -static void __used -forkee_status_signaled(int status, int expected_termsig, int expected_core) -{ - FORKEE_ASSERTX(!WIFEXITED(status)); - FORKEE_ASSERTX(!WIFCONTINUED(status)); - FORKEE_ASSERTX(WIFSIGNALED(status)); - FORKEE_ASSERTX(!WIFSTOPPED(status)); - - FORKEE_ASSERT_EQ(WTERMSIG(status), expected_termsig); - FORKEE_ASSERT_EQ(WCOREDUMP(status), expected_core); -} - -static void __used -validate_status_stopped(int status, int expected) -{ - ATF_REQUIRE_MSG(!WIFEXITED(status), "Reported exited process"); - ATF_REQUIRE_MSG(!WIFCONTINUED(status), "Reported continued process"); - ATF_REQUIRE_MSG(!WIFSIGNALED(status), "Reported signaled process"); - ATF_REQUIRE_MSG(WIFSTOPPED(status), "Reported !stopped process"); - - char st[128], ex[128]; - strlcpy(st, strsignal(WSTOPSIG(status)), sizeof(st)); - strlcpy(ex, strsignal(expected), sizeof(ex)); - - ATF_REQUIRE_EQ_MSG(WSTOPSIG(status), expected, - "Unexpected stop signal received [%s] != [%s]", st, ex); -} - -static void __used -forkee_status_stopped(int status, int expected) -{ - FORKEE_ASSERTX(!WIFEXITED(status)); - FORKEE_ASSERTX(!WIFCONTINUED(status)); - FORKEE_ASSERTX(!WIFSIGNALED(status)); - FORKEE_ASSERTX(WIFSTOPPED(status)); - - FORKEE_ASSERT_EQ(WSTOPSIG(status), expected); -} -#else -#define validate_status_exited(a,b) -#define forkee_status_exited(a,b) -#define validate_status_continued(a,b) -#define forkee_status_continued(a,b) -#define validate_status_signaled(a,b,c) -#define forkee_status_signaled(a,b,c) -#define validate_status_stopped(a,b) -#define forkee_status_stopped(a,b) -#endif - -/* This function is currently designed to be run in the main/parent process */ -static void __used -await_zombie(pid_t process) -{ - struct kinfo_proc2 p; - size_t len = sizeof(p); - - const int name[] = { - [0] = CTL_KERN, - [1] = KERN_PROC2, - [2] = KERN_PROC_PID, - [3] = process, - [4] = sizeof(p), - [5] = 1 - }; - - const size_t namelen = __arraycount(name); - - /* Await the process becoming a zombie */ - while(1) { - ATF_REQUIRE(sysctl(name, namelen, &p, &len, NULL, 0) == 0); - - if (p.p_stat == LSZOMB) - break; - - ATF_REQUIRE(usleep(1000) == 0); - } -} - -/* Happy number sequence -- this function is used to just consume cpu cycles */ -#define HAPPY_NUMBER 1 - -/* If n is not happy then its sequence ends in the cycle: - * 4, 16, 37, 58, 89, 145, 42, 20, 4, ... */ -#define SAD_NUMBER 4 - -/* Calculate the sum of the squares of the digits of n */ -static unsigned __used -dsum(unsigned n) -{ - unsigned sum, x; - for (sum = 0; n; n /= 10) { - x = n % 10; - sum += x * x; - } - return sum; -} - -/* - * XXX: Disabled optimization is required to make tests for hardware assisted - * traps in .text functional - * - * Tested with GCC 5.4 on NetBSD 7.99.47 amd64 - */ -static int __used -#ifdef __clang__ -__attribute__((__optnone__)) -#else -__attribute__((__optimize__("O0"))) -#endif -check_happy(unsigned n) -{ - for (;;) { - unsigned total = dsum(n); - - if (total == HAPPY_NUMBER) - return 1; - if (total == SAD_NUMBER) - return 0; - - n = total; - } -} - -#if defined(TWAIT_HAVE_PID) -#define ATF_TP_ADD_TC_HAVE_PID(a,b) ATF_TP_ADD_TC(a,b) -#else -#define ATF_TP_ADD_TC_HAVE_PID(a,b) -#endif - -#if defined(HAVE_GPREGS) -#define ATF_TP_ADD_TC_HAVE_GPREGS(a,b) ATF_TP_ADD_TC(a,b) -#else -#define ATF_TP_ADD_TC_HAVE_GPREGS(a,b) -#endif - -#if defined(HAVE_FPREGS) -#define ATF_TP_ADD_TC_HAVE_FPREGS(a,b) ATF_TP_ADD_TC(a,b) -#else -#define ATF_TP_ADD_TC_HAVE_FPREGS(a,b) -#endif - -#if defined(PT_STEP) -#define ATF_TP_ADD_TC_PT_STEP(a,b) ATF_TP_ADD_TC(a,b) -#else -#define ATF_TP_ADD_TC_PT_STEP(a,b) -#endif - -#if defined(__HAVE_PTRACE_WATCHPOINTS) -#define ATF_TP_ADD_TC_HAVE_PTRACE_WATCHPOINTS(a,b) ATF_TP_ADD_TC(a,b) -#else -#define ATF_TP_ADD_TC_HAVE_PTRACE_WATCHPOINTS(a,b) -#endif diff --git a/kernel/t_ptrace_wait.c b/kernel/t_ptrace_wait.c deleted file mode 100644 --- a/kernel/t_ptrace_wait.c +++ /dev/null @@ -1,6700 +0,0 @@ -/* $NetBSD: t_ptrace_wait.c,v 1.69 2017/01/27 16:43:07 kamil Exp $ */ - -/*- - * Copyright (c) 2016 The NetBSD Foundation, Inc. - * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 -__RCSID("$NetBSD: t_ptrace_wait.c,v 1.69 2017/01/27 16:43:07 kamil Exp $"); - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "h_macros.h" - -#include "t_ptrace_wait.h" -#include "msg.h" - -#define PARENT_TO_CHILD(info, fds, msg) \ - ATF_REQUIRE(msg_write_child(info " to child " # fds, &fds, &msg, sizeof(msg)) == 0) - -#define CHILD_FROM_PARENT(info, fds, msg) \ - FORKEE_ASSERT(msg_read_parent(info " from parent " # fds, &fds, &msg, sizeof(msg)) == 0) - -#define CHILD_TO_PARENT(info, fds, msg) \ - FORKEE_ASSERT(msg_write_parent(info " to parent " # fds, &fds, &msg, sizeof(msg)) == 0) - -#define PARENT_FROM_CHILD(info, fds, msg) \ - ATF_REQUIRE(msg_read_child(info " from parent " # fds, &fds, &msg, sizeof(msg)) == 0) - - -ATF_TC(traceme1); -ATF_TC_HEAD(traceme1, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify SIGSTOP followed by _exit(2) in a child"); -} - -ATF_TC_BODY(traceme1, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} - -ATF_TC(traceme2); -ATF_TC_HEAD(traceme2, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify SIGSTOP followed by _exit(2) in a child"); -} - -static int traceme2_caught = 0; - -static void -traceme2_sighandler(int sig) -{ - FORKEE_ASSERT_EQ(sig, SIGINT); - - ++traceme2_caught; -} - -ATF_TC_BODY(traceme2, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP, sigsent = SIGINT; - pid_t child, wpid; - struct sigaction sa; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - sa.sa_handler = traceme2_sighandler; - sa.sa_flags = SA_SIGINFO; - sigemptyset(&sa.sa_mask); - - FORKEE_ASSERT(sigaction(sigsent, &sa, NULL) != -1); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - FORKEE_ASSERT_EQ(traceme2_caught, 1); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Before resuming the child process where it left off and with " - "signal %s to be sent\n", strsignal(sigsent)); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, sigsent) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the exited child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} - -ATF_TC(traceme3); -ATF_TC_HEAD(traceme3, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify SIGSTOP followed by termination by a signal in a child"); -} - -ATF_TC_BODY(traceme3, tc) -{ - const int sigval = SIGSTOP, sigsent = SIGINT /* Without core-dump */; - pid_t child, wpid; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - /* NOTREACHED */ - FORKEE_ASSERTX(0 && - "Child should be terminated by a signal from its parent"); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Before resuming the child process where it left off and with " - "signal %s to be sent\n", strsignal(sigsent)); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, sigsent) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_signaled(status, sigsent, 0); - - printf("Before calling %s() for the exited child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} - -ATF_TC(traceme4); -ATF_TC_HEAD(traceme4, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify SIGSTOP followed by SIGCONT and _exit(2) in a child"); -} - -ATF_TC_BODY(traceme4, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP, sigsent = SIGCONT; - pid_t child, wpid; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - printf("Before raising %s from child\n", strsignal(sigsent)); - FORKEE_ASSERT(raise(sigsent) == 0); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(),child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigsent); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the exited child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} - -#if defined(TWAIT_HAVE_PID) -ATF_TC(attach1); -ATF_TC_HEAD(attach1, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Assert that tracer sees process termination before the parent"); -} - -ATF_TC_BODY(attach1, tc) -{ - struct msg_fds parent_tracee, parent_tracer; - const int exitval_tracee = 5; - const int exitval_tracer = 10; - pid_t tracee, tracer, wpid; - uint8_t msg = 0xde; /* dummy message for IPC based on pipe(2) */ -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - - printf("Spawn tracee\n"); - ATF_REQUIRE(msg_open(&parent_tracee) == 0); - tracee = atf_utils_fork(); - if (tracee == 0) { - // Wait for parent to let us exit - CHILD_FROM_PARENT("exit tracee", parent_tracee, msg); - _exit(exitval_tracee); - } - - printf("Spawn debugger\n"); - ATF_REQUIRE(msg_open(&parent_tracer) == 0); - tracer = atf_utils_fork(); - if (tracer == 0) { - printf("Before calling PT_ATTACH from tracee %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_ATTACH, tracee, NULL, 0) != -1); - - /* Wait for tracee and assert that it was stopped w/ SIGSTOP */ - FORKEE_REQUIRE_SUCCESS( - wpid = TWAIT_GENERIC(tracee, &status, 0), tracee); - - forkee_status_stopped(status, SIGSTOP); - - /* Resume tracee with PT_CONTINUE */ - FORKEE_ASSERT(ptrace(PT_CONTINUE, tracee, (void *)1, 0) != -1); - - /* Inform parent that tracer has attached to tracee */ - CHILD_TO_PARENT("tracer ready", parent_tracer, msg); - - /* Wait for parent to tell use that tracee should have exited */ - CHILD_FROM_PARENT("wait for tracee exit", parent_tracer, msg); - - /* Wait for tracee and assert that it exited */ - FORKEE_REQUIRE_SUCCESS( - wpid = TWAIT_GENERIC(tracee, &status, 0), tracee); - - forkee_status_exited(status, exitval_tracee); - printf("Tracee %d exited with %d\n", tracee, exitval_tracee); - - printf("Before exiting of the tracer process\n"); - _exit(exitval_tracer); - } - - printf("Wait for the tracer to attach to the tracee\n"); - PARENT_FROM_CHILD("tracer ready", parent_tracer, msg); - - printf("Resume the tracee and let it exit\n"); - PARENT_TO_CHILD("exit tracee", parent_tracee, msg); - - printf("Detect that tracee is zombie\n"); - await_zombie(tracee); - - - printf("Assert that there is no status about tracee %d - " - "Tracer must detect zombie first - calling %s()\n", tracee, - TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS( - wpid = TWAIT_GENERIC(tracee, &status, WNOHANG), 0); - - printf("Tell the tracer child should have exited\n"); - PARENT_TO_CHILD("wait for tracee exit", parent_tracer, msg); - printf("Wait for tracer to finish its job and exit - calling %s()\n", - TWAIT_FNAME); - - printf("Wait from tracer child to complete waiting for tracee\n"); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(tracer, &status, 0), - tracer); - - validate_status_exited(status, exitval_tracer); - - printf("Wait for tracee to finish its job and exit - calling %s()\n", - TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(tracee, &status, WNOHANG), - tracee); - - validate_status_exited(status, exitval_tracee); - - msg_close(&parent_tracer); - msg_close(&parent_tracee); -} -#endif - -#if defined(TWAIT_HAVE_PID) -ATF_TC(attach2); -ATF_TC_HEAD(attach2, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Assert that any tracer sees process termination before its " - "parent"); -} - -ATF_TC_BODY(attach2, tc) -{ - struct msg_fds parent_tracer, parent_tracee; - const int exitval_tracee = 5; - const int exitval_tracer1 = 10, exitval_tracer2 = 20; - pid_t tracee, tracer, wpid; - uint8_t msg = 0xde; /* dummy message for IPC based on pipe(2) */ -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - - printf("Spawn tracee\n"); - ATF_REQUIRE(msg_open(&parent_tracee) == 0); - tracee = atf_utils_fork(); - if (tracee == 0) { - /* Wait for message from the parent */ - CHILD_FROM_PARENT("Message 1", parent_tracee, msg); - _exit(exitval_tracee); - } - - printf("Spawn debugger\n"); - ATF_REQUIRE(msg_open(&parent_tracer) == 0); - tracer = atf_utils_fork(); - if (tracer == 0) { - /* Fork again and drop parent to reattach to PID 1 */ - tracer = atf_utils_fork(); - if (tracer != 0) - _exit(exitval_tracer1); - - printf("Before calling PT_ATTACH from tracee %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_ATTACH, tracee, NULL, 0) != -1); - - /* Wait for tracee and assert that it was stopped w/ SIGSTOP */ - FORKEE_REQUIRE_SUCCESS( - wpid = TWAIT_GENERIC(tracee, &status, 0), tracee); - - forkee_status_stopped(status, SIGSTOP); - - /* Resume tracee with PT_CONTINUE */ - FORKEE_ASSERT(ptrace(PT_CONTINUE, tracee, (void *)1, 0) != -1); - - /* Inform parent that tracer has attached to tracee */ - CHILD_TO_PARENT("Message 1", parent_tracer, msg); - CHILD_FROM_PARENT("Message 2", parent_tracer, msg); - - /* Wait for tracee and assert that it exited */ - FORKEE_REQUIRE_SUCCESS( - wpid = TWAIT_GENERIC(tracee, &status, 0), tracee); - - forkee_status_exited(status, exitval_tracee); - - printf("Before exiting of the tracer process\n"); - _exit(exitval_tracer2); - } - printf("Wait for the tracer process (direct child) to exit calling " - "%s()\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS( - wpid = TWAIT_GENERIC(tracer, &status, 0), tracer); - - validate_status_exited(status, exitval_tracer1); - - printf("Wait for the non-exited tracee process with %s()\n", - TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS( - wpid = TWAIT_GENERIC(tracee, NULL, WNOHANG), 0); - - printf("Wait for the tracer to attach to the tracee\n"); - PARENT_FROM_CHILD("Message 1", parent_tracer, msg); - printf("Resume the tracee and let it exit\n"); - PARENT_TO_CHILD("Message 1", parent_tracee, msg); - - printf("Detect that tracee is zombie\n"); - await_zombie(tracee); - - printf("Assert that there is no status about tracee - " - "Tracer must detect zombie first - calling %s()\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS( - wpid = TWAIT_GENERIC(tracee, &status, WNOHANG), 0); - - printf("Resume the tracer and let it detect exited tracee\n"); - PARENT_TO_CHILD("Message 2", parent_tracer, msg); - - printf("Wait for tracee to finish its job and exit - calling %s()\n", - TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(tracee, &status, WNOHANG), - tracee); - - validate_status_exited(status, exitval_tracee); - - msg_close(&parent_tracer); - msg_close(&parent_tracee); - -} -#endif - -ATF_TC(attach3); -ATF_TC_HEAD(attach3, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Assert that tracer parent can PT_ATTACH to its child"); -} - -ATF_TC_BODY(attach3, tc) -{ - struct msg_fds parent_tracee; - const int exitval_tracee = 5; - pid_t tracee, wpid; - uint8_t msg = 0xde; /* dummy message for IPC based on pipe(2) */ -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - - printf("Spawn tracee\n"); - ATF_REQUIRE(msg_open(&parent_tracee) == 0); - tracee = atf_utils_fork(); - if (tracee == 0) { - CHILD_FROM_PARENT("Message 1", parent_tracee, msg); - printf("Parent should now attach to tracee\n"); - - CHILD_FROM_PARENT("Message 2", parent_tracee, msg); - /* Wait for message from the parent */ - _exit(exitval_tracee); - } - PARENT_TO_CHILD("Message 1", parent_tracee, msg); - - printf("Before calling PT_ATTACH for tracee %d\n", tracee); - ATF_REQUIRE(ptrace(PT_ATTACH, tracee, NULL, 0) != -1); - - printf("Wait for the stopped tracee process with %s()\n", - TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS( - wpid = TWAIT_GENERIC(tracee, &status, 0), tracee); - - validate_status_stopped(status, SIGSTOP); - - printf("Resume tracee with PT_CONTINUE\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, tracee, (void *)1, 0) != -1); - - printf("Let the tracee exit now\n"); - PARENT_TO_CHILD("Message 2", parent_tracee, msg); - - printf("Wait for tracee to exit with %s()\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS( - wpid = TWAIT_GENERIC(tracee, &status, 0), tracee); - - validate_status_exited(status, exitval_tracee); - - printf("Before calling %s() for tracee\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, - wpid = TWAIT_GENERIC(tracee, &status, 0)); - - msg_close(&parent_tracee); -} - -ATF_TC(attach4); -ATF_TC_HEAD(attach4, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Assert that tracer child can PT_ATTACH to its parent"); -} - -ATF_TC_BODY(attach4, tc) -{ - struct msg_fds parent_tracee; - const int exitval_tracer = 5; - pid_t tracer, wpid; - uint8_t msg = 0xde; /* dummy message for IPC based on pipe(2) */ -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - - printf("Spawn tracer\n"); - ATF_REQUIRE(msg_open(&parent_tracee) == 0); - tracer = atf_utils_fork(); - if (tracer == 0) { - - /* Wait for message from the parent */ - CHILD_FROM_PARENT("Message 1", parent_tracee, msg); - - printf("Attach to parent PID %d with PT_ATTACH from child\n", - getppid()); - FORKEE_ASSERT(ptrace(PT_ATTACH, getppid(), NULL, 0) != -1); - - printf("Wait for the stopped parent process with %s()\n", - TWAIT_FNAME); - FORKEE_REQUIRE_SUCCESS( - wpid = TWAIT_GENERIC(getppid(), &status, 0), getppid()); - - forkee_status_stopped(status, SIGSTOP); - - printf("Resume parent with PT_DETACH\n"); - FORKEE_ASSERT(ptrace(PT_DETACH, getppid(), (void *)1, 0) - != -1); - - /* Tell parent we are ready */ - CHILD_TO_PARENT("Message 1", parent_tracee, msg); - - _exit(exitval_tracer); - } - - printf("Wait for the tracer to become ready\n"); - PARENT_TO_CHILD("Message 1", parent_tracee, msg); - printf("Allow the tracer to exit now\n"); - PARENT_FROM_CHILD("Message 1", parent_tracee, msg); - - printf("Wait for tracer to exit with %s()\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS( - wpid = TWAIT_GENERIC(tracer, &status, 0), tracer); - - validate_status_exited(status, exitval_tracer); - - printf("Before calling %s() for tracer\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, - wpid = TWAIT_GENERIC(tracer, &status, 0)); - - msg_close(&parent_tracee); -} - -#if defined(TWAIT_HAVE_PID) -ATF_TC(attach5); -ATF_TC_HEAD(attach5, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Assert that tracer sees its parent when attached to tracer " - "(check getppid(2))"); -} - -ATF_TC_BODY(attach5, tc) -{ - struct msg_fds parent_tracer, parent_tracee; - const int exitval_tracee = 5; - const int exitval_tracer = 10; - pid_t parent, tracee, tracer, wpid; - uint8_t msg = 0xde; /* dummy message for IPC based on pipe(2) */ -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - - printf("Spawn tracee\n"); - ATF_REQUIRE(msg_open(&parent_tracer) == 0); - ATF_REQUIRE(msg_open(&parent_tracee) == 0); - tracee = atf_utils_fork(); - if (tracee == 0) { - parent = getppid(); - - /* Emit message to the parent */ - CHILD_TO_PARENT("tracee ready", parent_tracee, msg); - CHILD_FROM_PARENT("exit tracee", parent_tracee, msg); - - FORKEE_ASSERT_EQ(parent, getppid()); - - _exit(exitval_tracee); - } - printf("Wait for child to record its parent identifier (pid)\n"); - PARENT_FROM_CHILD("tracee ready", parent_tracee, msg); - - printf("Spawn debugger\n"); - tracer = atf_utils_fork(); - if (tracer == 0) { - /* No IPC to communicate with the child */ - printf("Before calling PT_ATTACH from tracee %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_ATTACH, tracee, NULL, 0) != -1); - - /* Wait for tracee and assert that it was stopped w/ SIGSTOP */ - FORKEE_REQUIRE_SUCCESS( - wpid = TWAIT_GENERIC(tracee, &status, 0), tracee); - - forkee_status_stopped(status, SIGSTOP); - - /* Resume tracee with PT_CONTINUE */ - FORKEE_ASSERT(ptrace(PT_CONTINUE, tracee, (void *)1, 0) != -1); - - /* Inform parent that tracer has attached to tracee */ - CHILD_TO_PARENT("tracer ready", parent_tracer, msg); - - /* Wait for parent to tell use that tracee should have exited */ - CHILD_FROM_PARENT("wait for tracee exit", parent_tracer, msg); - - /* Wait for tracee and assert that it exited */ - FORKEE_REQUIRE_SUCCESS( - wpid = TWAIT_GENERIC(tracee, &status, 0), tracee); - - forkee_status_exited(status, exitval_tracee); - - printf("Before exiting of the tracer process\n"); - _exit(exitval_tracer); - } - - printf("Wait for the tracer to attach to the tracee\n"); - PARENT_FROM_CHILD("tracer ready", parent_tracer, msg); - - printf("Resume the tracee and let it exit\n"); - PARENT_TO_CHILD("exit tracee", parent_tracee, msg); - - printf("Detect that tracee is zombie\n"); - await_zombie(tracee); - - printf("Assert that there is no status about tracee - " - "Tracer must detect zombie first - calling %s()\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS( - wpid = TWAIT_GENERIC(tracee, &status, WNOHANG), 0); - - printf("Tell the tracer child should have exited\n"); - PARENT_TO_CHILD("wait for tracee exit", parent_tracer, msg); - - printf("Wait from tracer child to complete waiting for tracee\n"); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(tracer, &status, 0), - tracer); - - validate_status_exited(status, exitval_tracer); - - printf("Wait for tracee to finish its job and exit - calling %s()\n", - TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(tracee, &status, WNOHANG), - tracee); - - validate_status_exited(status, exitval_tracee); - - msg_close(&parent_tracer); - msg_close(&parent_tracee); -} -#endif - -#if defined(TWAIT_HAVE_PID) -ATF_TC(attach6); -ATF_TC_HEAD(attach6, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Assert that tracer sees its parent when attached to tracer " - "(check sysctl(7) and struct kinfo_proc2)"); -} - -ATF_TC_BODY(attach6, tc) -{ - struct msg_fds parent_tracee, parent_tracer; - const int exitval_tracee = 5; - const int exitval_tracer = 10; - pid_t parent, tracee, tracer, wpid; - uint8_t msg = 0xde; /* dummy message for IPC based on pipe(2) */ -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - int name[CTL_MAXNAME]; - struct kinfo_proc2 kp; - size_t len = sizeof(kp); - unsigned int namelen; - - printf("Spawn tracee\n"); - ATF_REQUIRE(msg_open(&parent_tracee) == 0); - ATF_REQUIRE(msg_open(&parent_tracer) == 0); - tracee = atf_utils_fork(); - if (tracee == 0) { - parent = getppid(); - - /* Emit message to the parent */ - CHILD_TO_PARENT("Message 1", parent_tracee, msg); - CHILD_FROM_PARENT("Message 2", parent_tracee, msg); - - namelen = 0; - name[namelen++] = CTL_KERN; - name[namelen++] = KERN_PROC2; - name[namelen++] = KERN_PROC_PID; - name[namelen++] = getpid(); - name[namelen++] = len; - name[namelen++] = 1; - - FORKEE_ASSERT(sysctl(name, namelen, &kp, &len, NULL, 0) == 0); - FORKEE_ASSERT_EQ(parent, kp.p_ppid); - - _exit(exitval_tracee); - } - - printf("Wait for child to record its parent identifier (pid)\n"); - PARENT_FROM_CHILD("Message 1", parent_tracee, msg); - - printf("Spawn debugger\n"); - tracer = atf_utils_fork(); - if (tracer == 0) { - /* No IPC to communicate with the child */ - printf("Before calling PT_ATTACH from tracee %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_ATTACH, tracee, NULL, 0) != -1); - - /* Wait for tracee and assert that it was stopped w/ SIGSTOP */ - FORKEE_REQUIRE_SUCCESS( - wpid = TWAIT_GENERIC(tracee, &status, 0), tracee); - - forkee_status_stopped(status, SIGSTOP); - - /* Resume tracee with PT_CONTINUE */ - FORKEE_ASSERT(ptrace(PT_CONTINUE, tracee, (void *)1, 0) != -1); - - /* Inform parent that tracer has attached to tracee */ - CHILD_TO_PARENT("Message 1", parent_tracer, msg); - - CHILD_FROM_PARENT("Message 2", parent_tracer, msg); - - /* Wait for tracee and assert that it exited */ - FORKEE_REQUIRE_SUCCESS( - wpid = TWAIT_GENERIC(tracee, &status, 0), tracee); - - forkee_status_exited(status, exitval_tracee); - - printf("Before exiting of the tracer process\n"); - _exit(exitval_tracer); - } - - printf("Wait for the tracer to attach to the tracee\n"); - PARENT_FROM_CHILD("Message 1", parent_tracer, msg); - - printf("Resume the tracee and let it exit\n"); - PARENT_TO_CHILD("Message 1", parent_tracee, msg); - - printf("Detect that tracee is zombie\n"); - await_zombie(tracee); - - printf("Assert that there is no status about tracee - " - "Tracer must detect zombie first - calling %s()\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS( - wpid = TWAIT_GENERIC(tracee, &status, WNOHANG), 0); - - printf("Resume the tracer and let it detect exited tracee\n"); - PARENT_TO_CHILD("Message 2", parent_tracer, msg); - - printf("Wait for tracer to finish its job and exit - calling %s()\n", - TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(tracer, &status, 0), - tracer); - - validate_status_exited(status, exitval_tracer); - - printf("Wait for tracee to finish its job and exit - calling %s()\n", - TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(tracee, &status, WNOHANG), - tracee); - - validate_status_exited(status, exitval_tracee); - - msg_close(&parent_tracee); - msg_close(&parent_tracer); -} -#endif - -#if defined(TWAIT_HAVE_PID) -ATF_TC(attach7); -ATF_TC_HEAD(attach7, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Assert that tracer sees its parent when attached to tracer " - "(check /proc/curproc/status 3rd column)"); -} - -ATF_TC_BODY(attach7, tc) -{ - struct msg_fds parent_tracee, parent_tracer; - int rv; - const int exitval_tracee = 5; - const int exitval_tracer = 10; - pid_t parent, tracee, tracer, wpid; - uint8_t msg = 0xde; /* dummy message for IPC based on pipe(2) */ -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - FILE *fp; - struct stat st; - const char *fname = "/proc/curproc/status"; - char s_executable[MAXPATHLEN]; - int s_pid, s_ppid; - /* - * Format: - * EXECUTABLE PID PPID ... - */ - - ATF_REQUIRE((rv = stat(fname, &st)) == 0 || (errno == ENOENT)); - if (rv != 0) { - atf_tc_skip("/proc/curproc/status not found"); - } - - printf("Spawn tracee\n"); - ATF_REQUIRE(msg_open(&parent_tracee) == 0); - ATF_REQUIRE(msg_open(&parent_tracer) == 0); - tracee = atf_utils_fork(); - if (tracee == 0) { - parent = getppid(); - - // Wait for parent to let us exit - CHILD_TO_PARENT("tracee ready", parent_tracee, msg); - CHILD_FROM_PARENT("tracee exit", parent_tracee, msg); - - FORKEE_ASSERT((fp = fopen(fname, "r")) != NULL); - fscanf(fp, "%s %d %d", s_executable, &s_pid, &s_ppid); - FORKEE_ASSERT(fclose(fp) == 0); - FORKEE_ASSERT_EQ(parent, s_ppid); - - _exit(exitval_tracee); - } - - printf("Wait for child to record its parent identifier (pid)\n"); - PARENT_FROM_CHILD("tracee ready", parent_tracee, msg); - - printf("Spawn debugger\n"); - tracer = atf_utils_fork(); - if (tracer == 0) { - printf("Before calling PT_ATTACH from tracee %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_ATTACH, tracee, NULL, 0) != -1); - - /* Wait for tracee and assert that it was stopped w/ SIGSTOP */ - FORKEE_REQUIRE_SUCCESS( - wpid = TWAIT_GENERIC(tracee, &status, 0), tracee); - - forkee_status_stopped(status, SIGSTOP); - - /* Resume tracee with PT_CONTINUE */ - FORKEE_ASSERT(ptrace(PT_CONTINUE, tracee, (void *)1, 0) != -1); - - /* Inform parent that tracer has attached to tracee */ - CHILD_TO_PARENT("tracer ready", parent_tracer, msg); - - /* Wait for parent to tell use that tracee should have exited */ - CHILD_FROM_PARENT("wait for tracee exit", parent_tracer, msg); - - /* Wait for tracee and assert that it exited */ - FORKEE_REQUIRE_SUCCESS( - wpid = TWAIT_GENERIC(tracee, &status, 0), tracee); - - forkee_status_exited(status, exitval_tracee); - - printf("Before exiting of the tracer process\n"); - _exit(exitval_tracer); - } - printf("Wait for the tracer to attach to the tracee\n"); - PARENT_FROM_CHILD("tracer ready", parent_tracer, msg); - printf("Resume the tracee and let it exit\n"); - PARENT_TO_CHILD("tracee exit", parent_tracee, msg); - - printf("Detect that tracee is zombie\n"); - await_zombie(tracee); - - printf("Assert that there is no status about tracee - " - "Tracer must detect zombie first - calling %s()\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS( - wpid = TWAIT_GENERIC(tracee, &status, WNOHANG), 0); - - printf("Resume the tracer and let it detect exited tracee\n"); - PARENT_TO_CHILD("Message 2", parent_tracer, msg); - - printf("Wait for tracer to finish its job and exit - calling %s()\n", - TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(tracer, &status, 0), - tracer); - - validate_status_exited(status, exitval_tracer); - - printf("Wait for tracee to finish its job and exit - calling %s()\n", - TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(tracee, &status, WNOHANG), - tracee); - - validate_status_exited(status, exitval_tracee); - - msg_close(&parent_tracee); - msg_close(&parent_tracer); -} -#endif - -ATF_TC(eventmask1); -ATF_TC_HEAD(eventmask1, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify that empty EVENT_MASK is preserved"); -} - -ATF_TC_BODY(eventmask1, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - ptrace_event_t set_event, get_event; - const int len = sizeof(ptrace_event_t); - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - set_event.pe_set_event = 0; - ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, child, &set_event, len) != -1); - ATF_REQUIRE(ptrace(PT_GET_EVENT_MASK, child, &get_event, len) != -1); - ATF_REQUIRE(memcmp(&set_event, &get_event, len) == 0); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} - -ATF_TC(eventmask2); -ATF_TC_HEAD(eventmask2, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify that PTRACE_FORK in EVENT_MASK is preserved"); -} - -ATF_TC_BODY(eventmask2, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - ptrace_event_t set_event, get_event; - const int len = sizeof(ptrace_event_t); - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - set_event.pe_set_event = PTRACE_FORK; - ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, child, &set_event, len) != -1); - ATF_REQUIRE(ptrace(PT_GET_EVENT_MASK, child, &get_event, len) != -1); - ATF_REQUIRE(memcmp(&set_event, &get_event, len) == 0); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} - -ATF_TC(eventmask3); -ATF_TC_HEAD(eventmask3, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify that PTRACE_VFORK in EVENT_MASK is preserved"); -} - -ATF_TC_BODY(eventmask3, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - ptrace_event_t set_event, get_event; - const int len = sizeof(ptrace_event_t); - - atf_tc_expect_fail("PR kern/51630"); - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - set_event.pe_set_event = PTRACE_VFORK; - ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, child, &set_event, len) != -1); - ATF_REQUIRE(ptrace(PT_GET_EVENT_MASK, child, &get_event, len) != -1); - ATF_REQUIRE(memcmp(&set_event, &get_event, len) == 0); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} - -ATF_TC(eventmask4); -ATF_TC_HEAD(eventmask4, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify that PTRACE_VFORK_DONE in EVENT_MASK is preserved"); -} - -ATF_TC_BODY(eventmask4, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - ptrace_event_t set_event, get_event; - const int len = sizeof(ptrace_event_t); - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - set_event.pe_set_event = PTRACE_VFORK_DONE; - ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, child, &set_event, len) != -1); - ATF_REQUIRE(ptrace(PT_GET_EVENT_MASK, child, &get_event, len) != -1); - ATF_REQUIRE(memcmp(&set_event, &get_event, len) == 0); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} - -ATF_TC(eventmask5); -ATF_TC_HEAD(eventmask5, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify that PTRACE_LWP_CREATE in EVENT_MASK is preserved"); -} - -ATF_TC_BODY(eventmask5, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - ptrace_event_t set_event, get_event; - const int len = sizeof(ptrace_event_t); - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - set_event.pe_set_event = PTRACE_LWP_CREATE; - ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, child, &set_event, len) != -1); - ATF_REQUIRE(ptrace(PT_GET_EVENT_MASK, child, &get_event, len) != -1); - ATF_REQUIRE(memcmp(&set_event, &get_event, len) == 0); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} - -ATF_TC(eventmask6); -ATF_TC_HEAD(eventmask6, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify that PTRACE_LWP_EXIT in EVENT_MASK is preserved"); -} - -ATF_TC_BODY(eventmask6, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - ptrace_event_t set_event, get_event; - const int len = sizeof(ptrace_event_t); - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - set_event.pe_set_event = PTRACE_LWP_EXIT; - ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, child, &set_event, len) != -1); - ATF_REQUIRE(ptrace(PT_GET_EVENT_MASK, child, &get_event, len) != -1); - ATF_REQUIRE(memcmp(&set_event, &get_event, len) == 0); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} - -#if defined(TWAIT_HAVE_PID) -ATF_TC(fork1); -ATF_TC_HEAD(fork1, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify that fork(2) is intercepted by ptrace(2) with EVENT_MASK " - "set to PTRACE_FORK"); -} - -ATF_TC_BODY(fork1, tc) -{ - const int exitval = 5; - const int exitval2 = 15; - const int sigval = SIGSTOP; - pid_t child, child2, wpid; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - ptrace_state_t state; - const int slen = sizeof(state); - ptrace_event_t event; - const int elen = sizeof(event); - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - FORKEE_ASSERT((child2 = fork()) != 1); - - if (child2 == 0) - _exit(exitval2); - - FORKEE_REQUIRE_SUCCESS - (wpid = TWAIT_GENERIC(child2, &status, 0), child2); - - forkee_status_exited(status, exitval2); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Enable PTRACE_FORK in EVENT_MASK for the child %d\n", child); - event.pe_set_event = PTRACE_FORK; - ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, child, &event, elen) != -1); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child %d\n", TWAIT_FNAME, child); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, SIGTRAP); - - ATF_REQUIRE(ptrace(PT_GET_PROCESS_STATE, child, &state, slen) != -1); - ATF_REQUIRE_EQ(state.pe_report_event, PTRACE_FORK); - - child2 = state.pe_other_pid; - printf("Reported PTRACE_FORK event with forkee %d\n", child2); - - printf("Before calling %s() for the forkee %d of the child %d\n", - TWAIT_FNAME, child2, child); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child2, &status, 0), - child2); - - validate_status_stopped(status, SIGTRAP); - - ATF_REQUIRE(ptrace(PT_GET_PROCESS_STATE, child2, &state, slen) != -1); - ATF_REQUIRE_EQ(state.pe_report_event, PTRACE_FORK); - ATF_REQUIRE_EQ(state.pe_other_pid, child); - - printf("Before resuming the forkee process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child2, (void *)1, 0) != -1); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the forkee - expected exited\n", - TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child2, &status, 0), - child2); - - validate_status_exited(status, exitval2); - - printf("Before calling %s() for the forkee - expected no process\n", - TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, - wpid = TWAIT_GENERIC(child2, &status, 0)); - - printf("Before calling %s() for the child - expected stopped " - "SIGCHLD\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, SIGCHLD); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child - expected exited\n", - TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child - expected no process\n", - TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} -#endif - -ATF_TC(fork2); -ATF_TC_HEAD(fork2, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify that fork(2) is not intercepted by ptrace(2) with empty " - "EVENT_MASK"); -} - -ATF_TC_BODY(fork2, tc) -{ - const int exitval = 5; - const int exitval2 = 15; - const int sigval = SIGSTOP; - pid_t child, child2, wpid; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - ptrace_event_t event; - const int elen = sizeof(event); - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - FORKEE_ASSERT((child2 = fork()) != 1); - - if (child2 == 0) - _exit(exitval2); - - FORKEE_REQUIRE_SUCCESS - (wpid = TWAIT_GENERIC(child2, &status, 0), child2); - - forkee_status_exited(status, exitval2); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Set empty EVENT_MASK for the child %d\n", child); - event.pe_set_event = 0; - ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, child, &event, elen) != -1); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child - expected stopped " - "SIGCHLD\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, SIGCHLD); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child - expected exited\n", - TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child - expected no process\n", - TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} - -#if defined(TWAIT_HAVE_PID) -ATF_TC(vfork1); -ATF_TC_HEAD(vfork1, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify that vfork(2) is intercepted by ptrace(2) with EVENT_MASK " - "set to PTRACE_VFORK"); -} - -ATF_TC_BODY(vfork1, tc) -{ - const int exitval = 5; - const int exitval2 = 15; - const int sigval = SIGSTOP; - pid_t child, child2, wpid; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - ptrace_state_t state; - const int slen = sizeof(state); - ptrace_event_t event; - const int elen = sizeof(event); - - atf_tc_expect_fail("PR kern/51630"); - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - FORKEE_ASSERT((child2 = vfork()) != 1); - - if (child2 == 0) - _exit(exitval2); - - FORKEE_REQUIRE_SUCCESS - (wpid = TWAIT_GENERIC(child2, &status, 0), child2); - - forkee_status_exited(status, exitval2); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Enable PTRACE_VFORK in EVENT_MASK for the child %d\n", child); - event.pe_set_event = PTRACE_VFORK; - ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, child, &event, elen) != -1); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child %d\n", TWAIT_FNAME, child); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, SIGTRAP); - - ATF_REQUIRE(ptrace(PT_GET_PROCESS_STATE, child, &state, slen) != -1); - ATF_REQUIRE_EQ(state.pe_report_event, PTRACE_VFORK); - - child2 = state.pe_other_pid; - printf("Reported PTRACE_VFORK event with forkee %d\n", child2); - - printf("Before calling %s() for the forkee %d of the child %d\n", - TWAIT_FNAME, child2, child); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child2, &status, 0), - child2); - - validate_status_stopped(status, SIGTRAP); - - ATF_REQUIRE(ptrace(PT_GET_PROCESS_STATE, child2, &state, slen) != -1); - ATF_REQUIRE_EQ(state.pe_report_event, PTRACE_VFORK); - ATF_REQUIRE_EQ(state.pe_other_pid, child); - - printf("Before resuming the forkee process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child2, (void *)1, 0) != -1); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the forkee - expected exited\n", - TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child2, &status, 0), - child2); - - validate_status_exited(status, exitval2); - - printf("Before calling %s() for the forkee - expected no process\n", - TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, - wpid = TWAIT_GENERIC(child2, &status, 0)); - - printf("Before calling %s() for the child - expected stopped " - "SIGCHLD\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, SIGCHLD); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child - expected exited\n", - TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child - expected no process\n", - TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} -#endif - -ATF_TC(vfork2); -ATF_TC_HEAD(vfork2, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify that vfork(2) is not intercepted by ptrace(2) with empty " - "EVENT_MASK"); -} - -ATF_TC_BODY(vfork2, tc) -{ - const int exitval = 5; - const int exitval2 = 15; - const int sigval = SIGSTOP; - pid_t child, child2, wpid; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - ptrace_event_t event; - const int elen = sizeof(event); - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - FORKEE_ASSERT((child2 = vfork()) != 1); - - if (child2 == 0) - _exit(exitval2); - - FORKEE_REQUIRE_SUCCESS - (wpid = TWAIT_GENERIC(child2, &status, 0), child2); - - forkee_status_exited(status, exitval2); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Set empty EVENT_MASK for the child %d\n", child); - event.pe_set_event = 0; - ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, child, &event, elen) != -1); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child - expected stopped " - "SIGCHLD\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, SIGCHLD); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child - expected exited\n", - TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child - expected no process\n", - TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} - -ATF_TC(vforkdone1); -ATF_TC_HEAD(vforkdone1, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify that vfork(2) is intercepted by ptrace(2) with EVENT_MASK " - "set to PTRACE_VFORK_DONE"); -} - -ATF_TC_BODY(vforkdone1, tc) -{ - const int exitval = 5; - const int exitval2 = 15; - const int sigval = SIGSTOP; - pid_t child, child2, wpid; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - ptrace_state_t state; - const int slen = sizeof(state); - ptrace_event_t event; - const int elen = sizeof(event); - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - FORKEE_ASSERT((child2 = vfork()) != 1); - - if (child2 == 0) - _exit(exitval2); - - FORKEE_REQUIRE_SUCCESS - (wpid = TWAIT_GENERIC(child2, &status, 0), child2); - - forkee_status_exited(status, exitval2); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Enable PTRACE_VFORK_DONE in EVENT_MASK for the child %d\n", - child); - event.pe_set_event = PTRACE_VFORK_DONE; - ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, child, &event, elen) != -1); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child %d\n", TWAIT_FNAME, child); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, SIGTRAP); - - ATF_REQUIRE(ptrace(PT_GET_PROCESS_STATE, child, &state, slen) != -1); - ATF_REQUIRE_EQ(state.pe_report_event, PTRACE_VFORK_DONE); - - child2 = state.pe_other_pid; - printf("Reported PTRACE_VFORK_DONE event with forkee %d\n", child2); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child - expected stopped " - "SIGCHLD\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, SIGCHLD); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child - expected exited\n", - TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child - expected no process\n", - TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} - -ATF_TC(vforkdone2); -ATF_TC_HEAD(vforkdone2, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify that vfork(2) is intercepted by ptrace(2) with EVENT_MASK " - "set to PTRACE_FORK | PTRACE_VFORK_DONE"); -} - -ATF_TC_BODY(vforkdone2, tc) -{ - const int exitval = 5; - const int exitval2 = 15; - const int sigval = SIGSTOP; - pid_t child, child2, wpid; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - ptrace_state_t state; - const int slen = sizeof(state); - ptrace_event_t event; - const int elen = sizeof(event); - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - FORKEE_ASSERT((child2 = vfork()) != 1); - - if (child2 == 0) - _exit(exitval2); - - FORKEE_REQUIRE_SUCCESS - (wpid = TWAIT_GENERIC(child2, &status, 0), child2); - - forkee_status_exited(status, exitval2); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Enable PTRACE_VFORK in EVENT_MASK for the child %d\n", child); - event.pe_set_event = PTRACE_FORK | PTRACE_VFORK_DONE; - ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, child, &event, elen) != -1); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child %d\n", TWAIT_FNAME, child); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, SIGTRAP); - - ATF_REQUIRE(ptrace(PT_GET_PROCESS_STATE, child, &state, slen) != -1); - ATF_REQUIRE_EQ(state.pe_report_event, PTRACE_VFORK_DONE); - - child2 = state.pe_other_pid; - printf("Reported PTRACE_VFORK_DONE event with forkee %d\n", child2); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child - expected stopped " - "SIGCHLD\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, SIGCHLD); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child - expected exited\n", - TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child - expected no process\n", - TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} - -ATF_TC(io_read_d1); -ATF_TC_HEAD(io_read_d1, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify PT_IO with PIOD_READ_D and len = sizeof(uint8_t)"); -} - -ATF_TC_BODY(io_read_d1, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; - uint8_t lookup_me = 0; - const uint8_t magic = 0xab; - struct ptrace_io_desc io = { - .piod_op = PIOD_READ_D, - .piod_offs = &lookup_me, - .piod_addr = &lookup_me, - .piod_len = sizeof(lookup_me) - }; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - lookup_me = magic; - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Read lookup_me from tracee (PID=%d) by tracer (PID=%d)\n", - child, getpid()); - ATF_REQUIRE(ptrace(PT_IO, child, &io, 0) != -1); - - ATF_REQUIRE_EQ_MSG(lookup_me, magic, - "got value %" PRIx8 " != expected %" PRIx8, lookup_me, magic); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} - -ATF_TC(io_read_d2); -ATF_TC_HEAD(io_read_d2, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify PT_IO with PIOD_READ_D and len = sizeof(uint16_t)"); -} - -ATF_TC_BODY(io_read_d2, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; - uint16_t lookup_me = 0; - const uint16_t magic = 0x1234; - struct ptrace_io_desc io = { - .piod_op = PIOD_READ_D, - .piod_offs = &lookup_me, - .piod_addr = &lookup_me, - .piod_len = sizeof(lookup_me) - }; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - lookup_me = magic; - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Read lookup_me from tracee (PID=%d) by tracer (PID=%d)\n", - child, getpid()); - ATF_REQUIRE(ptrace(PT_IO, child, &io, 0) != -1); - - ATF_REQUIRE_EQ_MSG(lookup_me, magic, - "got value %" PRIx16 " != expected %" PRIx16, lookup_me, magic); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} - -ATF_TC(io_read_d3); -ATF_TC_HEAD(io_read_d3, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify PT_IO with PIOD_READ_D and len = sizeof(uint32_t)"); -} - -ATF_TC_BODY(io_read_d3, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; - uint32_t lookup_me = 0; - const uint32_t magic = 0x1234abcd; - struct ptrace_io_desc io = { - .piod_op = PIOD_READ_D, - .piod_offs = &lookup_me, - .piod_addr = &lookup_me, - .piod_len = sizeof(lookup_me) - }; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - lookup_me = magic; - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Read lookup_me from tracee (PID=%d) by tracer (PID=%d)\n", - child, getpid()); - ATF_REQUIRE(ptrace(PT_IO, child, &io, 0) != -1); - - ATF_REQUIRE_EQ_MSG(lookup_me, magic, - "got value %" PRIx32 " != expected %" PRIx32, lookup_me, magic); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} - -ATF_TC(io_read_d4); -ATF_TC_HEAD(io_read_d4, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify PT_IO with PIOD_READ_D and len = sizeof(uint64_t)"); -} - -ATF_TC_BODY(io_read_d4, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; - uint64_t lookup_me = 0; - const uint64_t magic = 0x1234abcd9876dcfa; - struct ptrace_io_desc io = { - .piod_op = PIOD_READ_D, - .piod_offs = &lookup_me, - .piod_addr = &lookup_me, - .piod_len = sizeof(lookup_me) - }; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - lookup_me = magic; - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Read lookup_me from tracee (PID=%d) by tracer (PID=%d)\n", - child, getpid()); - ATF_REQUIRE(ptrace(PT_IO, child, &io, 0) != -1); - - ATF_REQUIRE_EQ_MSG(lookup_me, magic, - "got value %" PRIx64 " != expected %" PRIx64, lookup_me, magic); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} - -ATF_TC(io_write_d1); -ATF_TC_HEAD(io_write_d1, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify PT_IO with PIOD_WRITE_D and len = sizeof(uint8_t)"); -} - -ATF_TC_BODY(io_write_d1, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; - uint8_t lookup_me = 0; - const uint8_t magic = 0xab; - struct ptrace_io_desc io = { - .piod_op = PIOD_WRITE_D, - .piod_offs = &lookup_me, - .piod_addr = &lookup_me, - .piod_len = sizeof(lookup_me) - }; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - FORKEE_ASSERT_EQ(lookup_me, magic); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - lookup_me = magic; - - printf("Write new lookup_me to tracee (PID=%d) from tracer (PID=%d)\n", - child, getpid()); - ATF_REQUIRE(ptrace(PT_IO, child, &io, 0) != -1); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} - -ATF_TC(io_write_d2); -ATF_TC_HEAD(io_write_d2, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify PT_IO with PIOD_WRITE_D and len = sizeof(uint16_t)"); -} - -ATF_TC_BODY(io_write_d2, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; - uint16_t lookup_me = 0; - const uint16_t magic = 0xab12; - struct ptrace_io_desc io = { - .piod_op = PIOD_WRITE_D, - .piod_offs = &lookup_me, - .piod_addr = &lookup_me, - .piod_len = sizeof(lookup_me) - }; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - FORKEE_ASSERT_EQ(lookup_me, magic); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - lookup_me = magic; - - printf("Write new lookup_me to tracee (PID=%d) from tracer (PID=%d)\n", - child, getpid()); - ATF_REQUIRE(ptrace(PT_IO, child, &io, 0) != -1); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} - -ATF_TC(io_write_d3); -ATF_TC_HEAD(io_write_d3, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify PT_IO with PIOD_WRITE_D and len = sizeof(uint32_t)"); -} - -ATF_TC_BODY(io_write_d3, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; - uint32_t lookup_me = 0; - const uint32_t magic = 0xab127643; - struct ptrace_io_desc io = { - .piod_op = PIOD_WRITE_D, - .piod_offs = &lookup_me, - .piod_addr = &lookup_me, - .piod_len = sizeof(lookup_me) - }; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - FORKEE_ASSERT_EQ(lookup_me, magic); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - lookup_me = magic; - - printf("Write new lookup_me to tracee (PID=%d) from tracer (PID=%d)\n", - child, getpid()); - ATF_REQUIRE(ptrace(PT_IO, child, &io, 0) != -1); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} - -ATF_TC(io_write_d4); -ATF_TC_HEAD(io_write_d4, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify PT_IO with PIOD_WRITE_D and len = sizeof(uint64_t)"); -} - -ATF_TC_BODY(io_write_d4, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; - uint64_t lookup_me = 0; - const uint64_t magic = 0xab12764376490123; - struct ptrace_io_desc io = { - .piod_op = PIOD_WRITE_D, - .piod_offs = &lookup_me, - .piod_addr = &lookup_me, - .piod_len = sizeof(lookup_me) - }; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - FORKEE_ASSERT_EQ(lookup_me, magic); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - lookup_me = magic; - - printf("Write new lookup_me to tracee (PID=%d) from tracer (PID=%d)\n", - child, getpid()); - ATF_REQUIRE(ptrace(PT_IO, child, &io, 0) != -1); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} - -ATF_TC(io_read_auxv1); -ATF_TC_HEAD(io_read_auxv1, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify PT_READ_AUXV called for tracee"); -} - -ATF_TC_BODY(io_read_auxv1, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - AuxInfo ai[100], *aip; - struct ptrace_io_desc io = { - .piod_op = PIOD_READ_AUXV, - .piod_offs = 0, - .piod_addr = ai, - .piod_len = sizeof(ai) - }; - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Read new AUXV from tracee (PID=%d) by tracer (PID=%d)\n", - child, getpid()); - ATF_REQUIRE(ptrace(PT_IO, child, &io, 0) != -1); - - printf("Asserting that AUXV length (%zu) is > 0\n", io.piod_len); - ATF_REQUIRE(io.piod_len > 0); - - for (aip = ai; aip->a_type != AT_NULL; aip++) - printf("a_type=%#llx a_v=%#llx\n", - (long long int)aip->a_type, (long long int)aip->a_v); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} - -ATF_TC(read_d1); -ATF_TC_HEAD(read_d1, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify PT_READ_D called once"); -} - -ATF_TC_BODY(read_d1, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; - int lookup_me = 0; - const int magic = (int)random(); -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - lookup_me = magic; - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Read new lookup_me from tracee (PID=%d) by tracer (PID=%d)\n", - child, getpid()); - errno = 0; - lookup_me = ptrace(PT_READ_D, child, &lookup_me, 0); - ATF_REQUIRE_EQ(errno, 0); - - ATF_REQUIRE_EQ_MSG(lookup_me, magic, - "got value %#x != expected %#x", lookup_me, magic); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} - -ATF_TC(read_d2); -ATF_TC_HEAD(read_d2, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify PT_READ_D called twice"); -} - -ATF_TC_BODY(read_d2, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; - int lookup_me1 = 0; - int lookup_me2 = 0; - const int magic1 = (int)random(); - const int magic2 = (int)random(); -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - lookup_me1 = magic1; - lookup_me2 = magic2; - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Read new lookup_me1 from tracee (PID=%d) by tracer (PID=%d)\n", - child, getpid()); - errno = 0; - lookup_me1 = ptrace(PT_READ_D, child, &lookup_me1, 0); - ATF_REQUIRE_EQ(errno, 0); - - ATF_REQUIRE_EQ_MSG(lookup_me1, magic1, - "got value %#x != expected %#x", lookup_me1, magic1); - - printf("Read new lookup_me2 from tracee (PID=%d) by tracer (PID=%d)\n", - child, getpid()); - errno = 0; - lookup_me2 = ptrace(PT_READ_D, child, &lookup_me2, 0); - ATF_REQUIRE_EQ(errno, 0); - - ATF_REQUIRE_EQ_MSG(lookup_me2, magic2, - "got value %#x != expected %#x", lookup_me2, magic2); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} - -ATF_TC(read_d3); -ATF_TC_HEAD(read_d3, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify PT_READ_D called three times"); -} - -ATF_TC_BODY(read_d3, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; - int lookup_me1 = 0; - int lookup_me2 = 0; - int lookup_me3 = 0; - const int magic1 = (int)random(); - const int magic2 = (int)random(); - const int magic3 = (int)random(); -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - lookup_me1 = magic1; - lookup_me2 = magic2; - lookup_me3 = magic3; - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Read new lookup_me1 from tracee (PID=%d) by tracer (PID=%d)\n", - child, getpid()); - errno = 0; - lookup_me1 = ptrace(PT_READ_D, child, &lookup_me1, 0); - ATF_REQUIRE_EQ(errno, 0); - - ATF_REQUIRE_EQ_MSG(lookup_me1, magic1, - "got value %#x != expected %#x", lookup_me1, magic1); - - printf("Read new lookup_me2 from tracee (PID=%d) by tracer (PID=%d)\n", - child, getpid()); - errno = 0; - lookup_me2 = ptrace(PT_READ_D, child, &lookup_me2, 0); - ATF_REQUIRE_EQ(errno, 0); - - ATF_REQUIRE_EQ_MSG(lookup_me2, magic2, - "got value %#x != expected %#x", lookup_me2, magic2); - - printf("Read new lookup_me3 from tracee (PID=%d) by tracer (PID=%d)\n", - child, getpid()); - errno = 0; - lookup_me3 = ptrace(PT_READ_D, child, &lookup_me3, 0); - ATF_REQUIRE_EQ(errno, 0); - - ATF_REQUIRE_EQ_MSG(lookup_me3, magic3, - "got value %#x != expected %#x", lookup_me3, magic3); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} - -ATF_TC(read_d4); -ATF_TC_HEAD(read_d4, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify PT_READ_D called four times"); -} - -ATF_TC_BODY(read_d4, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; - int lookup_me1 = 0; - int lookup_me2 = 0; - int lookup_me3 = 0; - int lookup_me4 = 0; - const int magic1 = (int)random(); - const int magic2 = (int)random(); - const int magic3 = (int)random(); - const int magic4 = (int)random(); -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - lookup_me1 = magic1; - lookup_me2 = magic2; - lookup_me3 = magic3; - lookup_me4 = magic4; - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Read new lookup_me1 from tracee (PID=%d) by tracer (PID=%d)\n", - child, getpid()); - errno = 0; - lookup_me1 = ptrace(PT_READ_D, child, &lookup_me1, 0); - ATF_REQUIRE_EQ(errno, 0); - - ATF_REQUIRE_EQ_MSG(lookup_me1, magic1, - "got value %#x != expected %#x", lookup_me1, magic1); - - printf("Read new lookup_me2 from tracee (PID=%d) by tracer (PID=%d)\n", - child, getpid()); - errno = 0; - lookup_me2 = ptrace(PT_READ_D, child, &lookup_me2, 0); - ATF_REQUIRE_EQ(errno, 0); - - ATF_REQUIRE_EQ_MSG(lookup_me2, magic2, - "got value %#x != expected %#x", lookup_me2, magic2); - - printf("Read new lookup_me3 from tracee (PID=%d) by tracer (PID=%d)\n", - child, getpid()); - errno = 0; - lookup_me3 = ptrace(PT_READ_D, child, &lookup_me3, 0); - ATF_REQUIRE_EQ(errno, 0); - - ATF_REQUIRE_EQ_MSG(lookup_me3, magic3, - "got value %#x != expected %#x", lookup_me3, magic3); - - printf("Read new lookup_me4 from tracee (PID=%d) by tracer (PID=%d)\n", - child, getpid()); - errno = 0; - lookup_me4 = ptrace(PT_READ_D, child, &lookup_me4, 0); - ATF_REQUIRE_EQ(errno, 0); - - ATF_REQUIRE_EQ_MSG(lookup_me4, magic4, - "got value %#x != expected %#x", lookup_me4, magic4); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} - -ATF_TC(write_d1); -ATF_TC_HEAD(write_d1, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify PT_WRITE_D called once"); -} - -ATF_TC_BODY(write_d1, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; - int lookup_me = 0; - const int magic = (int)random(); -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - FORKEE_ASSERT_EQ(lookup_me, magic); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Write new lookup_me to tracee (PID=%d) from tracer (PID=%d)\n", - child, getpid()); - ATF_REQUIRE(ptrace(PT_WRITE_D, child, &lookup_me, magic) != -1); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} - -ATF_TC(write_d2); -ATF_TC_HEAD(write_d2, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify PT_WRITE_D called twice"); -} - -ATF_TC_BODY(write_d2, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; - int lookup_me1 = 0; - int lookup_me2 = 0; - const int magic1 = (int)random(); - const int magic2 = (int)random(); -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - FORKEE_ASSERT_EQ(lookup_me1, magic1); - FORKEE_ASSERT_EQ(lookup_me2, magic2); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Write new lookup_me1 to tracee (PID=%d) from tracer (PID=%d)\n", - child, getpid()); - ATF_REQUIRE(ptrace(PT_WRITE_D, child, &lookup_me1, magic1) != -1); - - printf("Write new lookup_me2 to tracee (PID=%d) from tracer (PID=%d)\n", - child, getpid()); - ATF_REQUIRE(ptrace(PT_WRITE_D, child, &lookup_me2, magic2) != -1); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} - -ATF_TC(write_d3); -ATF_TC_HEAD(write_d3, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify PT_WRITE_D called three times"); -} - -ATF_TC_BODY(write_d3, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; - int lookup_me1 = 0; - int lookup_me2 = 0; - int lookup_me3 = 0; - const int magic1 = (int)random(); - const int magic2 = (int)random(); - const int magic3 = (int)random(); -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - FORKEE_ASSERT_EQ(lookup_me1, magic1); - FORKEE_ASSERT_EQ(lookup_me2, magic2); - FORKEE_ASSERT_EQ(lookup_me3, magic3); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Write new lookup_me1 to tracee (PID=%d) from tracer (PID=%d)\n", - child, getpid()); - ATF_REQUIRE(ptrace(PT_WRITE_D, child, &lookup_me1, magic1) != -1); - - printf("Write new lookup_me2 to tracee (PID=%d) from tracer (PID=%d)\n", - child, getpid()); - ATF_REQUIRE(ptrace(PT_WRITE_D, child, &lookup_me2, magic2) != -1); - - printf("Write new lookup_me3 to tracee (PID=%d) from tracer (PID=%d)\n", - child, getpid()); - ATF_REQUIRE(ptrace(PT_WRITE_D, child, &lookup_me3, magic3) != -1); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} - -ATF_TC(write_d4); -ATF_TC_HEAD(write_d4, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify PT_WRITE_D called four times"); -} - -ATF_TC_BODY(write_d4, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; - int lookup_me1 = 0; - int lookup_me2 = 0; - int lookup_me3 = 0; - int lookup_me4 = 0; - const int magic1 = (int)random(); - const int magic2 = (int)random(); - const int magic3 = (int)random(); - const int magic4 = (int)random(); -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - FORKEE_ASSERT_EQ(lookup_me1, magic1); - FORKEE_ASSERT_EQ(lookup_me2, magic2); - FORKEE_ASSERT_EQ(lookup_me3, magic3); - FORKEE_ASSERT_EQ(lookup_me4, magic4); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Write new lookup_me1 to tracee (PID=%d) from tracer (PID=%d)\n", - child, getpid()); - ATF_REQUIRE(ptrace(PT_WRITE_D, child, &lookup_me1, magic1) != -1); - - printf("Write new lookup_me2 to tracee (PID=%d) from tracer (PID=%d)\n", - child, getpid()); - ATF_REQUIRE(ptrace(PT_WRITE_D, child, &lookup_me2, magic2) != -1); - - printf("Write new lookup_me3 to tracee (PID=%d) from tracer (PID=%d)\n", - child, getpid()); - ATF_REQUIRE(ptrace(PT_WRITE_D, child, &lookup_me3, magic3) != -1); - - printf("Write new lookup_me4 to tracee (PID=%d) from tracer (PID=%d)\n", - child, getpid()); - ATF_REQUIRE(ptrace(PT_WRITE_D, child, &lookup_me4, magic4) != -1); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} - -ATF_TC(io_read_d_write_d_handshake1); -ATF_TC_HEAD(io_read_d_write_d_handshake1, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify PT_IO with PIOD_READ_D and PIOD_WRITE_D handshake"); -} - -ATF_TC_BODY(io_read_d_write_d_handshake1, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; - uint8_t lookup_me_fromtracee = 0; - const uint8_t magic_fromtracee = (uint8_t)random(); - uint8_t lookup_me_totracee = 0; - const uint8_t magic_totracee = (uint8_t)random(); - struct ptrace_io_desc io_fromtracee = { - .piod_op = PIOD_READ_D, - .piod_offs = &lookup_me_fromtracee, - .piod_addr = &lookup_me_fromtracee, - .piod_len = sizeof(lookup_me_fromtracee) - }; - struct ptrace_io_desc io_totracee = { - .piod_op = PIOD_WRITE_D, - .piod_offs = &lookup_me_totracee, - .piod_addr = &lookup_me_totracee, - .piod_len = sizeof(lookup_me_totracee) - }; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - lookup_me_fromtracee = magic_fromtracee; - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - FORKEE_ASSERT_EQ(lookup_me_totracee, magic_totracee); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Read lookup_me_fromtracee PID=%d by tracer (PID=%d)\n", - child, getpid()); - ATF_REQUIRE(ptrace(PT_IO, child, &io_fromtracee, 0) != -1); - - ATF_REQUIRE_EQ_MSG(lookup_me_fromtracee, magic_fromtracee, - "got value %" PRIx8 " != expected %" PRIx8, lookup_me_fromtracee, - magic_fromtracee); - - lookup_me_totracee = magic_totracee; - - printf("Write lookup_me_totracee to PID=%d by tracer (PID=%d)\n", - child, getpid()); - ATF_REQUIRE(ptrace(PT_IO, child, &io_totracee, 0) != -1); - - ATF_REQUIRE_EQ_MSG(lookup_me_totracee, magic_totracee, - "got value %" PRIx8 " != expected %" PRIx8, lookup_me_totracee, - magic_totracee); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} - -ATF_TC(io_read_d_write_d_handshake2); -ATF_TC_HEAD(io_read_d_write_d_handshake2, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify PT_IO with PIOD_WRITE_D and PIOD_READ_D handshake"); -} - -ATF_TC_BODY(io_read_d_write_d_handshake2, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; - uint8_t lookup_me_fromtracee = 0; - const uint8_t magic_fromtracee = (uint8_t)random(); - uint8_t lookup_me_totracee = 0; - const uint8_t magic_totracee = (uint8_t)random(); - struct ptrace_io_desc io_fromtracee = { - .piod_op = PIOD_READ_D, - .piod_offs = &lookup_me_fromtracee, - .piod_addr = &lookup_me_fromtracee, - .piod_len = sizeof(lookup_me_fromtracee) - }; - struct ptrace_io_desc io_totracee = { - .piod_op = PIOD_WRITE_D, - .piod_offs = &lookup_me_totracee, - .piod_addr = &lookup_me_totracee, - .piod_len = sizeof(lookup_me_totracee) - }; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - lookup_me_fromtracee = magic_fromtracee; - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - FORKEE_ASSERT_EQ(lookup_me_totracee, magic_totracee); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - lookup_me_totracee = magic_totracee; - - printf("Write lookup_me_totracee to PID=%d by tracer (PID=%d)\n", - child, getpid()); - ATF_REQUIRE(ptrace(PT_IO, child, &io_totracee, 0) != -1); - - ATF_REQUIRE_EQ_MSG(lookup_me_totracee, magic_totracee, - "got value %" PRIx8 " != expected %" PRIx8, lookup_me_totracee, - magic_totracee); - - printf("Read lookup_me_fromtracee PID=%d by tracer (PID=%d)\n", - child, getpid()); - ATF_REQUIRE(ptrace(PT_IO, child, &io_fromtracee, 0) != -1); - - ATF_REQUIRE_EQ_MSG(lookup_me_fromtracee, magic_fromtracee, - "got value %" PRIx8 " != expected %" PRIx8, lookup_me_fromtracee, - magic_fromtracee); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} - -ATF_TC(read_d_write_d_handshake1); -ATF_TC_HEAD(read_d_write_d_handshake1, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify PT_READ_D with PT_WRITE_D handshake"); -} - -ATF_TC_BODY(read_d_write_d_handshake1, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; - int lookup_me_fromtracee = 0; - const int magic_fromtracee = (int)random(); - int lookup_me_totracee = 0; - const int magic_totracee = (int)random(); -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - lookup_me_fromtracee = magic_fromtracee; - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - FORKEE_ASSERT_EQ(lookup_me_totracee, magic_totracee); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Read new lookup_me_fromtracee PID=%d by tracer (PID=%d)\n", - child, getpid()); - errno = 0; - lookup_me_fromtracee = - ptrace(PT_READ_D, child, &lookup_me_fromtracee, 0); - ATF_REQUIRE_EQ(errno, 0); - - ATF_REQUIRE_EQ_MSG(lookup_me_fromtracee, magic_fromtracee, - "got value %#x != expected %#x", lookup_me_fromtracee, - magic_fromtracee); - - printf("Write new lookup_me_totracee to PID=%d from tracer (PID=%d)\n", - child, getpid()); - ATF_REQUIRE - (ptrace(PT_WRITE_D, child, &lookup_me_totracee, magic_totracee) - != -1); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} - -ATF_TC(read_d_write_d_handshake2); -ATF_TC_HEAD(read_d_write_d_handshake2, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify PT_WRITE_D with PT_READ_D handshake"); -} - -ATF_TC_BODY(read_d_write_d_handshake2, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; - int lookup_me_fromtracee = 0; - const int magic_fromtracee = (int)random(); - int lookup_me_totracee = 0; - const int magic_totracee = (int)random(); -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - lookup_me_fromtracee = magic_fromtracee; - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - FORKEE_ASSERT_EQ(lookup_me_totracee, magic_totracee); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Write new lookup_me_totracee to PID=%d from tracer (PID=%d)\n", - child, getpid()); - ATF_REQUIRE - (ptrace(PT_WRITE_D, child, &lookup_me_totracee, magic_totracee) - != -1); - - printf("Read new lookup_me_fromtracee PID=%d by tracer (PID=%d)\n", - child, getpid()); - errno = 0; - lookup_me_fromtracee = - ptrace(PT_READ_D, child, &lookup_me_fromtracee, 0); - ATF_REQUIRE_EQ(errno, 0); - - ATF_REQUIRE_EQ_MSG(lookup_me_fromtracee, magic_fromtracee, - "got value %#x != expected %#x", lookup_me_fromtracee, - magic_fromtracee); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} - -/* These dummy functions are used to be copied with ptrace(2) calls */ -static int __used -dummy_fn1(int a, int b, int c, int d) -{ - - a *= 1; - b += 2; - c -= 3; - d /= 4; - - return a + b * c - d; -} - -static int __used -dummy_fn2(int a, int b, int c, int d) -{ - - a *= 4; - b += 3; - c -= 2; - d /= 1; - - return a + b * c - d; -} - -static int __used -dummy_fn3(int a, int b, int c, int d) -{ - - a *= 10; - b += 20; - c -= 30; - d /= 40; - - return a + b * c - d; -} - -static int __used -dummy_fn4(int a, int b, int c, int d) -{ - - a *= 40; - b += 30; - c -= 20; - d /= 10; - - return a + b * c - d; -} - -ATF_TC(io_read_i1); -ATF_TC_HEAD(io_read_i1, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify PT_IO with PIOD_READ_I and len = sizeof(uint8_t)"); -} - -ATF_TC_BODY(io_read_i1, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; - uint8_t lookup_me = 0; - uint8_t magic; - memcpy(&magic, dummy_fn1, sizeof(magic)); - struct ptrace_io_desc io = { - .piod_op = PIOD_READ_I, - .piod_offs = dummy_fn1, - .piod_addr = &lookup_me, - .piod_len = sizeof(lookup_me) - }; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Read lookup_me from tracee (PID=%d) by tracer (PID=%d)\n", - child, getpid()); - ATF_REQUIRE(ptrace(PT_IO, child, &io, 0) != -1); - - ATF_REQUIRE_EQ_MSG(lookup_me, magic, - "got value %" PRIx8 " != expected %" PRIx8, lookup_me, magic); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} - -ATF_TC(io_read_i2); -ATF_TC_HEAD(io_read_i2, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify PT_IO with PIOD_READ_I and len = sizeof(uint16_t)"); -} - -ATF_TC_BODY(io_read_i2, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; - uint16_t lookup_me = 0; - uint16_t magic; - memcpy(&magic, dummy_fn1, sizeof(magic)); - struct ptrace_io_desc io = { - .piod_op = PIOD_READ_I, - .piod_offs = dummy_fn1, - .piod_addr = &lookup_me, - .piod_len = sizeof(lookup_me) - }; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Read lookup_me from tracee (PID=%d) by tracer (PID=%d)\n", - child, getpid()); - ATF_REQUIRE(ptrace(PT_IO, child, &io, 0) != -1); - - ATF_REQUIRE_EQ_MSG(lookup_me, magic, - "got value %" PRIx16 " != expected %" PRIx16, lookup_me, magic); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} - -ATF_TC(io_read_i3); -ATF_TC_HEAD(io_read_i3, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify PT_IO with PIOD_READ_I and len = sizeof(uint32_t)"); -} - -ATF_TC_BODY(io_read_i3, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; - uint32_t lookup_me = 0; - uint32_t magic; - memcpy(&magic, dummy_fn1, sizeof(magic)); - struct ptrace_io_desc io = { - .piod_op = PIOD_READ_I, - .piod_offs = dummy_fn1, - .piod_addr = &lookup_me, - .piod_len = sizeof(lookup_me) - }; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Read lookup_me from tracee (PID=%d) by tracer (PID=%d)\n", - child, getpid()); - ATF_REQUIRE(ptrace(PT_IO, child, &io, 0) != -1); - - ATF_REQUIRE_EQ_MSG(lookup_me, magic, - "got value %" PRIx32 " != expected %" PRIx32, lookup_me, magic); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} - -ATF_TC(io_read_i4); -ATF_TC_HEAD(io_read_i4, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify PT_IO with PIOD_READ_I and len = sizeof(uint64_t)"); -} - -ATF_TC_BODY(io_read_i4, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; - uint64_t lookup_me = 0; - uint64_t magic; - memcpy(&magic, dummy_fn1, sizeof(magic)); - struct ptrace_io_desc io = { - .piod_op = PIOD_READ_I, - .piod_offs = dummy_fn1, - .piod_addr = &lookup_me, - .piod_len = sizeof(lookup_me) - }; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Read lookup_me from tracee (PID=%d) by tracer (PID=%d)\n", - child, getpid()); - ATF_REQUIRE(ptrace(PT_IO, child, &io, 0) != -1); - - ATF_REQUIRE_EQ_MSG(lookup_me, magic, - "got value %" PRIx64 " != expected %" PRIx64, lookup_me, magic); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} - -ATF_TC(read_i1); -ATF_TC_HEAD(read_i1, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify PT_READ_I called once"); -} - -ATF_TC_BODY(read_i1, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; - int lookup_me = 0; - int magic; - memcpy(&magic, dummy_fn1, sizeof(magic)); -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Read new lookup_me from tracee (PID=%d) by tracer (PID=%d)\n", - child, getpid()); - errno = 0; - lookup_me = ptrace(PT_READ_I, child, dummy_fn1, 0); - ATF_REQUIRE_EQ(errno, 0); - - ATF_REQUIRE_EQ_MSG(lookup_me, magic, - "got value %#x != expected %#x", lookup_me, magic); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} - -ATF_TC(read_i2); -ATF_TC_HEAD(read_i2, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify PT_READ_I called twice"); -} - -ATF_TC_BODY(read_i2, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; - int lookup_me1 = 0; - int lookup_me2 = 0; - int magic1; - int magic2; - memcpy(&magic1, dummy_fn1, sizeof(magic1)); - memcpy(&magic2, dummy_fn2, sizeof(magic2)); -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Read new lookup_me1 from tracee (PID=%d) by tracer (PID=%d)\n", - child, getpid()); - errno = 0; - lookup_me1 = ptrace(PT_READ_I, child, dummy_fn1, 0); - ATF_REQUIRE_EQ(errno, 0); - - ATF_REQUIRE_EQ_MSG(lookup_me1, magic1, - "got value %#x != expected %#x", lookup_me1, magic1); - - printf("Read new lookup_me2 from tracee (PID=%d) by tracer (PID=%d)\n", - child, getpid()); - errno = 0; - lookup_me2 = ptrace(PT_READ_I, child, dummy_fn2, 0); - ATF_REQUIRE_EQ(errno, 0); - - ATF_REQUIRE_EQ_MSG(lookup_me2, magic2, - "got value %#x != expected %#x", lookup_me2, magic2); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} - -ATF_TC(read_i3); -ATF_TC_HEAD(read_i3, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify PT_READ_I called three times"); -} - -ATF_TC_BODY(read_i3, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; - int lookup_me1 = 0; - int lookup_me2 = 0; - int lookup_me3 = 0; - int magic1; - int magic2; - int magic3; - memcpy(&magic1, dummy_fn1, sizeof(magic1)); - memcpy(&magic2, dummy_fn2, sizeof(magic2)); - memcpy(&magic3, dummy_fn3, sizeof(magic3)); -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Read new lookup_me1 from tracee (PID=%d) by tracer (PID=%d)\n", - child, getpid()); - errno = 0; - lookup_me1 = ptrace(PT_READ_I, child, dummy_fn1, 0); - ATF_REQUIRE_EQ(errno, 0); - - ATF_REQUIRE_EQ_MSG(lookup_me1, magic1, - "got value %#x != expected %#x", lookup_me1, magic1); - - printf("Read new lookup_me2 from tracee (PID=%d) by tracer (PID=%d)\n", - child, getpid()); - errno = 0; - lookup_me2 = ptrace(PT_READ_I, child, dummy_fn2, 0); - ATF_REQUIRE_EQ(errno, 0); - - ATF_REQUIRE_EQ_MSG(lookup_me2, magic2, - "got value %#x != expected %#x", lookup_me2, magic2); - - printf("Read new lookup_me3 from tracee (PID=%d) by tracer (PID=%d)\n", - child, getpid()); - errno = 0; - lookup_me3 = ptrace(PT_READ_I, child, dummy_fn3, 0); - ATF_REQUIRE_EQ(errno, 0); - - ATF_REQUIRE_EQ_MSG(lookup_me3, magic3, - "got value %#x != expected %#x", lookup_me3, magic3); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} - -ATF_TC(read_i4); -ATF_TC_HEAD(read_i4, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify PT_READ_I called four times"); -} - -ATF_TC_BODY(read_i4, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; - int lookup_me1 = 0; - int lookup_me2 = 0; - int lookup_me3 = 0; - int lookup_me4 = 0; - int magic1; - int magic2; - int magic3; - int magic4; - memcpy(&magic1, dummy_fn1, sizeof(magic1)); - memcpy(&magic2, dummy_fn2, sizeof(magic2)); - memcpy(&magic3, dummy_fn3, sizeof(magic3)); - memcpy(&magic4, dummy_fn4, sizeof(magic4)); -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Read new lookup_me1 from tracee (PID=%d) by tracer (PID=%d)\n", - child, getpid()); - errno = 0; - lookup_me1 = ptrace(PT_READ_I, child, dummy_fn1, 0); - ATF_REQUIRE_EQ(errno, 0); - - ATF_REQUIRE_EQ_MSG(lookup_me1, magic1, - "got value %#x != expected %#x", lookup_me1, magic1); - - printf("Read new lookup_me2 from tracee (PID=%d) by tracer (PID=%d)\n", - child, getpid()); - errno = 0; - lookup_me2 = ptrace(PT_READ_I, child, dummy_fn2, 0); - ATF_REQUIRE_EQ(errno, 0); - - ATF_REQUIRE_EQ_MSG(lookup_me2, magic2, - "got value %#x != expected %#x", lookup_me2, magic2); - - printf("Read new lookup_me3 from tracee (PID=%d) by tracer (PID=%d)\n", - child, getpid()); - errno = 0; - lookup_me3 = ptrace(PT_READ_I, child, dummy_fn3, 0); - ATF_REQUIRE_EQ(errno, 0); - - ATF_REQUIRE_EQ_MSG(lookup_me3, magic3, - "got value %#x != expected %#x", lookup_me3, magic3); - - printf("Read new lookup_me4 from tracee (PID=%d) by tracer (PID=%d)\n", - child, getpid()); - errno = 0; - lookup_me4 = ptrace(PT_READ_I, child, dummy_fn4, 0); - ATF_REQUIRE_EQ(errno, 0); - - ATF_REQUIRE_EQ_MSG(lookup_me4, magic4, - "got value %#x != expected %#x", lookup_me4, magic4); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} - -#if defined(HAVE_GPREGS) -ATF_TC(regs1); -ATF_TC_HEAD(regs1, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify plain PT_GETREGS call without further steps"); -} - -ATF_TC_BODY(regs1, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - struct reg r; - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Call GETREGS for the child process\n"); - ATF_REQUIRE(ptrace(PT_GETREGS, child, &r, 0) != -1); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} -#endif - -#if defined(HAVE_GPREGS) -ATF_TC(regs2); -ATF_TC_HEAD(regs2, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify plain PT_GETREGS call and retrieve PC"); -} - -ATF_TC_BODY(regs2, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - struct reg r; - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Call GETREGS for the child process\n"); - ATF_REQUIRE(ptrace(PT_GETREGS, child, &r, 0) != -1); - - printf("Retrieved PC=%" PRIxREGISTER "\n", PTRACE_REG_PC(&r)); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} -#endif - -#if defined(HAVE_GPREGS) -ATF_TC(regs3); -ATF_TC_HEAD(regs3, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify plain PT_GETREGS call and retrieve SP"); -} - -ATF_TC_BODY(regs3, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - struct reg r; - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Call GETREGS for the child process\n"); - ATF_REQUIRE(ptrace(PT_GETREGS, child, &r, 0) != -1); - - printf("Retrieved SP=%" PRIxREGISTER "\n", PTRACE_REG_SP(&r)); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} -#endif - -#if defined(HAVE_GPREGS) -ATF_TC(regs4); -ATF_TC_HEAD(regs4, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify plain PT_GETREGS call and retrieve INTRV"); -} - -ATF_TC_BODY(regs4, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - struct reg r; - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Call GETREGS for the child process\n"); - ATF_REQUIRE(ptrace(PT_GETREGS, child, &r, 0) != -1); - - printf("Retrieved INTRV=%" PRIxREGISTER "\n", PTRACE_REG_INTRV(&r)); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} -#endif - -#if defined(HAVE_GPREGS) -ATF_TC(regs5); -ATF_TC_HEAD(regs5, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify PT_GETREGS and PT_SETREGS calls without changing regs"); -} - -ATF_TC_BODY(regs5, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - struct reg r; - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Call GETREGS for the child process\n"); - ATF_REQUIRE(ptrace(PT_GETREGS, child, &r, 0) != -1); - - printf("Call SETREGS for the child process (without changed regs)\n"); - ATF_REQUIRE(ptrace(PT_GETREGS, child, &r, 0) != -1); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} -#endif - -#if defined(HAVE_FPREGS) -ATF_TC(fpregs1); -ATF_TC_HEAD(fpregs1, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify plain PT_GETFPREGS call without further steps"); -} - -ATF_TC_BODY(fpregs1, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - struct fpreg r; - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Call GETFPREGS for the child process\n"); - ATF_REQUIRE(ptrace(PT_GETFPREGS, child, &r, 0) != -1); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} -#endif - -#if defined(HAVE_FPREGS) -ATF_TC(fpregs2); -ATF_TC_HEAD(fpregs2, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify PT_GETFPREGS and PT_SETFPREGS calls without changing " - "regs"); -} - -ATF_TC_BODY(fpregs2, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - struct fpreg r; - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Call GETFPREGS for the child process\n"); - ATF_REQUIRE(ptrace(PT_GETFPREGS, child, &r, 0) != -1); - - printf("Call SETFPREGS for the child (without changed regs)\n"); - ATF_REQUIRE(ptrace(PT_SETFPREGS, child, &r, 0) != -1); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} -#endif - -#if defined(PT_STEP) -ATF_TC(step1); -ATF_TC_HEAD(step1, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify single PT_STEP call"); -} - -ATF_TC_BODY(step1, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - int happy; - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - happy = check_happy(100); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - FORKEE_ASSERT_EQ(happy, check_happy(100)); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Before resuming the child process where it left off and " - "without signal to be sent (use PT_STEP)\n"); - ATF_REQUIRE(ptrace(PT_STEP, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, SIGTRAP); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} -#endif - -#if defined(PT_STEP) -ATF_TC(step2); -ATF_TC_HEAD(step2, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify PT_STEP called twice"); -} - -ATF_TC_BODY(step2, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - int happy; - int N = 2; - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - happy = check_happy(999); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - FORKEE_ASSERT_EQ(happy, check_happy(999)); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - while (N --> 0) { - printf("Before resuming the child process where it left off " - "and without signal to be sent (use PT_STEP)\n"); - ATF_REQUIRE(ptrace(PT_STEP, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), - child); - - validate_status_stopped(status, SIGTRAP); - } - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} -#endif - -#if defined(PT_STEP) -ATF_TC(step3); -ATF_TC_HEAD(step3, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify PT_STEP called three times"); -} - -ATF_TC_BODY(step3, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - int happy; - int N = 3; - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - happy = check_happy(999); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - FORKEE_ASSERT_EQ(happy, check_happy(999)); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - while (N --> 0) { - printf("Before resuming the child process where it left off " - "and without signal to be sent (use PT_STEP)\n"); - ATF_REQUIRE(ptrace(PT_STEP, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), - child); - - validate_status_stopped(status, SIGTRAP); - } - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} -#endif - -#if defined(PT_STEP) -ATF_TC(step4); -ATF_TC_HEAD(step4, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify PT_STEP called four times"); -} - -ATF_TC_BODY(step4, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - int happy; - int N = 4; - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - happy = check_happy(999); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - FORKEE_ASSERT_EQ(happy, check_happy(999)); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - while (N --> 0) { - printf("Before resuming the child process where it left off " - "and without signal to be sent (use PT_STEP)\n"); - ATF_REQUIRE(ptrace(PT_STEP, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), - child); - - validate_status_stopped(status, SIGTRAP); - } - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} -#endif - -ATF_TC(kill1); -ATF_TC_HEAD(kill1, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify that PT_CONTINUE with SIGKILL terminates child"); -} - -ATF_TC_BODY(kill1, tc) -{ - const int sigval = SIGSTOP, sigsent = SIGKILL; - pid_t child, wpid; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - /* NOTREACHED */ - FORKEE_ASSERTX(0 && - "Child should be terminated by a signal from its parent"); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, sigsent) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_signaled(status, sigsent, 0); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} - -ATF_TC(kill2); -ATF_TC_HEAD(kill2, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify that PT_KILL terminates child"); -} - -ATF_TC_BODY(kill2, tc) -{ - const int sigval = SIGSTOP; - pid_t child, wpid; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - /* NOTREACHED */ - FORKEE_ASSERTX(0 && - "Child should be terminated by a signal from its parent"); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_KILL, child, (void*)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_signaled(status, SIGKILL, 0); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} - -ATF_TC(lwpinfo1); -ATF_TC_HEAD(lwpinfo1, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify basic LWPINFO call for single thread (PT_TRACE_ME)"); -} - -ATF_TC_BODY(lwpinfo1, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - struct ptrace_lwpinfo info = {0, 0}; - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Before calling ptrace(2) with PT_LWPINFO for child\n"); - ATF_REQUIRE(ptrace(PT_LWPINFO, child, &info, sizeof(info)) != -1); - - printf("Assert that there exists a thread\n"); - ATF_REQUIRE(info.pl_lwpid > 0); - - printf("Assert that lwp thread %d received event PL_EVENT_SIGNAL\n", - info.pl_lwpid); - ATF_REQUIRE_EQ_MSG(info.pl_event, PL_EVENT_SIGNAL, - "Received event %d != expected event %d", - info.pl_event, PL_EVENT_SIGNAL); - - printf("Before calling ptrace(2) with PT_LWPINFO for child\n"); - ATF_REQUIRE(ptrace(PT_LWPINFO, child, &info, sizeof(info)) != -1); - - printf("Assert that there are no more lwp threads in child\n"); - ATF_REQUIRE_EQ(info.pl_lwpid, 0); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} - -#if defined(TWAIT_HAVE_PID) -ATF_TC(lwpinfo2); -ATF_TC_HEAD(lwpinfo2, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify basic LWPINFO call for single thread (PT_ATTACH from " - "tracer)"); -} - -ATF_TC_BODY(lwpinfo2, tc) -{ - struct msg_fds parent_tracee, parent_tracer; - const int exitval_tracee = 5; - const int exitval_tracer = 10; - pid_t tracee, tracer, wpid; - uint8_t msg = 0xde; /* dummy message for IPC based on pipe(2) */ -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - struct ptrace_lwpinfo info = {0, 0}; - - printf("Spawn tracee\n"); - ATF_REQUIRE(msg_open(&parent_tracee) == 0); - ATF_REQUIRE(msg_open(&parent_tracer) == 0); - tracee = atf_utils_fork(); - if (tracee == 0) { - - /* Wait for message from the parent */ - CHILD_TO_PARENT("tracee ready", parent_tracee, msg); - CHILD_FROM_PARENT("tracee exit", parent_tracee, msg); - - _exit(exitval_tracee); - } - PARENT_FROM_CHILD("tracee ready", parent_tracee, msg); - - printf("Spawn debugger\n"); - tracer = atf_utils_fork(); - if (tracer == 0) { - /* No IPC to communicate with the child */ - printf("Before calling PT_ATTACH from tracee %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_ATTACH, tracee, NULL, 0) != -1); - - /* Wait for tracee and assert that it was stopped w/ SIGSTOP */ - FORKEE_REQUIRE_SUCCESS( - wpid = TWAIT_GENERIC(tracee, &status, 0), tracee); - - forkee_status_stopped(status, SIGSTOP); - - printf("Before calling ptrace(2) with PT_LWPINFO for child\n"); - FORKEE_ASSERT(ptrace(PT_LWPINFO, tracee, &info, sizeof(info)) - != -1); - - printf("Assert that there exists a thread\n"); - FORKEE_ASSERTX(info.pl_lwpid > 0); - - printf("Assert that lwp thread %d received event " - "PL_EVENT_SIGNAL\n", info.pl_lwpid); - FORKEE_ASSERT_EQ(info.pl_event, PL_EVENT_SIGNAL); - - printf("Before calling ptrace(2) with PT_LWPINFO for child\n"); - FORKEE_ASSERT(ptrace(PT_LWPINFO, tracee, &info, sizeof(info)) - != -1); - - printf("Assert that there are no more lwp threads in child\n"); - FORKEE_ASSERTX(info.pl_lwpid == 0); - - /* Resume tracee with PT_CONTINUE */ - FORKEE_ASSERT(ptrace(PT_CONTINUE, tracee, (void *)1, 0) != -1); - - /* Inform parent that tracer has attached to tracee */ - CHILD_TO_PARENT("tracer ready", parent_tracer, msg); - /* Wait for parent */ - CHILD_FROM_PARENT("tracer wait", parent_tracer, msg); - - /* Wait for tracee and assert that it exited */ - FORKEE_REQUIRE_SUCCESS( - wpid = TWAIT_GENERIC(tracee, &status, 0), tracee); - - forkee_status_exited(status, exitval_tracee); - - printf("Before exiting of the tracer process\n"); - _exit(exitval_tracer); - } - - printf("Wait for the tracer to attach to the tracee\n"); - PARENT_FROM_CHILD("tracer ready", parent_tracer, msg); - - printf("Resume the tracee and let it exit\n"); - PARENT_TO_CHILD("tracee exit", parent_tracee, msg); - - printf("Detect that tracee is zombie\n"); - await_zombie(tracee); - - printf("Assert that there is no status about tracee - " - "Tracer must detect zombie first - calling %s()\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS( - wpid = TWAIT_GENERIC(tracee, &status, WNOHANG), 0); - - printf("Resume the tracer and let it detect exited tracee\n"); - PARENT_TO_CHILD("tracer wait", parent_tracer, msg); - - printf("Wait for tracer to finish its job and exit - calling %s()\n", - TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(tracer, &status, 0), - tracer); - - validate_status_exited(status, exitval_tracer); - - printf("Wait for tracee to finish its job and exit - calling %s()\n", - TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(tracee, &status, WNOHANG), - tracee); - - validate_status_exited(status, exitval_tracee); - - msg_close(&parent_tracer); - msg_close(&parent_tracee); -} -#endif - -ATF_TC(siginfo1); -ATF_TC_HEAD(siginfo1, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify basic PT_GET_SIGINFO call for SIGTRAP from tracee"); -} - -ATF_TC_BODY(siginfo1, tc) -{ - const int exitval = 5; - const int sigval = SIGTRAP; - pid_t child, wpid; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - struct ptrace_siginfo info; - memset(&info, 0, sizeof(info)); - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Before calling ptrace(2) with PT_GET_SIGINFO for child\n"); - ATF_REQUIRE(ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1); - - printf("Signal traced to lwpid=%d\n", info.psi_lwpid); - printf("Signal properties: si_signo=%#x si_code=%#x si_errno=%#x\n", - info.psi_siginfo.si_signo, info.psi_siginfo.si_code, - info.psi_siginfo.si_errno); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} - -ATF_TC(siginfo2); -ATF_TC_HEAD(siginfo2, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify basic PT_GET_SIGINFO and PT_SET_SIGINFO calls without " - "modification of SIGINT from tracee"); -} - -static int siginfo2_caught = 0; - -static void -siginfo2_sighandler(int sig) -{ - FORKEE_ASSERT_EQ(sig, SIGINT); - - ++siginfo2_caught; -} - -ATF_TC_BODY(siginfo2, tc) -{ - const int exitval = 5; - const int sigval = SIGINT; - pid_t child, wpid; - struct sigaction sa; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - struct ptrace_siginfo info; - memset(&info, 0, sizeof(info)); - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - sa.sa_handler = siginfo2_sighandler; - sa.sa_flags = SA_SIGINFO; - sigemptyset(&sa.sa_mask); - - FORKEE_ASSERT(sigaction(sigval, &sa, NULL) != -1); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - FORKEE_ASSERT_EQ(siginfo2_caught, 1); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Before calling ptrace(2) with PT_GET_SIGINFO for child\n"); - ATF_REQUIRE(ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1); - - printf("Signal traced to lwpid=%d\n", info.psi_lwpid); - printf("Signal properties: si_signo=%#x si_code=%#x si_errno=%#x\n", - info.psi_siginfo.si_signo, info.psi_siginfo.si_code, - info.psi_siginfo.si_errno); - - printf("Before calling ptrace(2) with PT_SET_SIGINFO for child\n"); - ATF_REQUIRE(ptrace(PT_SET_SIGINFO, child, &info, sizeof(info)) != -1); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, sigval) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} - -ATF_TC(siginfo3); -ATF_TC_HEAD(siginfo3, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify basic PT_GET_SIGINFO and PT_SET_SIGINFO calls with " - "setting signal to new value"); -} - -static int siginfo3_caught = 0; - -static void -siginfo3_sigaction(int sig, siginfo_t *info, void *ctx) -{ - FORKEE_ASSERT_EQ(sig, SIGTRAP); - - FORKEE_ASSERT_EQ(info->si_signo, SIGTRAP); - FORKEE_ASSERT_EQ(info->si_code, TRAP_BRKPT); - - ++siginfo3_caught; -} - -ATF_TC_BODY(siginfo3, tc) -{ - const int exitval = 5; - const int sigval = SIGINT; - const int sigfaked = SIGTRAP; - const int sicodefaked = TRAP_BRKPT; - pid_t child, wpid; - struct sigaction sa; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - struct ptrace_siginfo info; - memset(&info, 0, sizeof(info)); - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - sa.sa_sigaction = siginfo3_sigaction; - sa.sa_flags = SA_SIGINFO; - sigemptyset(&sa.sa_mask); - - FORKEE_ASSERT(sigaction(sigfaked, &sa, NULL) != -1); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - FORKEE_ASSERT_EQ(siginfo3_caught, 1); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Before calling ptrace(2) with PT_GET_SIGINFO for child\n"); - ATF_REQUIRE(ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1); - - printf("Signal traced to lwpid=%d\n", info.psi_lwpid); - printf("Signal properties: si_signo=%#x si_code=%#x si_errno=%#x\n", - info.psi_siginfo.si_signo, info.psi_siginfo.si_code, - info.psi_siginfo.si_errno); - - printf("Before setting new faked signal to signo=%d si_code=%d\n", - sigfaked, sicodefaked); - info.psi_siginfo.si_signo = sigfaked; - info.psi_siginfo.si_code = sicodefaked; - - printf("Before calling ptrace(2) with PT_SET_SIGINFO for child\n"); - ATF_REQUIRE(ptrace(PT_SET_SIGINFO, child, &info, sizeof(info)) != -1); - - printf("Before calling ptrace(2) with PT_GET_SIGINFO for child\n"); - ATF_REQUIRE(ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1); - - printf("Before checking siginfo_t\n"); - ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, sigfaked); - ATF_REQUIRE_EQ(info.psi_siginfo.si_code, sicodefaked); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, sigfaked) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} - -ATF_TC(siginfo4); -ATF_TC_HEAD(siginfo4, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Detect SIGTRAP TRAP_EXEC from tracee"); -} - -ATF_TC_BODY(siginfo4, tc) -{ - const int sigval = SIGTRAP; - pid_t child, wpid; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - - struct ptrace_siginfo info; - memset(&info, 0, sizeof(info)); - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - printf("Before calling execve(2) from child\n"); - execlp("/bin/echo", "/bin/echo", NULL); - - FORKEE_ASSERT(0 && "Not reached"); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Before calling ptrace(2) with PT_GET_SIGINFO for child\n"); - ATF_REQUIRE(ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1); - - printf("Signal traced to lwpid=%d\n", info.psi_lwpid); - printf("Signal properties: si_signo=%#x si_code=%#x si_errno=%#x\n", - info.psi_siginfo.si_signo, info.psi_siginfo.si_code, - info.psi_siginfo.si_errno); - - ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, sigval); - ATF_REQUIRE_EQ(info.psi_siginfo.si_code, TRAP_EXEC); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} - -#if defined(TWAIT_HAVE_PID) -ATF_TC(siginfo5); -ATF_TC_HEAD(siginfo5, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify that fork(2) is intercepted by ptrace(2) with EVENT_MASK " - "set to PTRACE_FORK and reports correct signal information"); -} - -ATF_TC_BODY(siginfo5, tc) -{ - const int exitval = 5; - const int exitval2 = 15; - const int sigval = SIGSTOP; - pid_t child, child2, wpid; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - ptrace_state_t state; - const int slen = sizeof(state); - ptrace_event_t event; - const int elen = sizeof(event); - struct ptrace_siginfo info; - - memset(&info, 0, sizeof(info)); - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - FORKEE_ASSERT((child2 = fork()) != 1); - - if (child2 == 0) - _exit(exitval2); - - FORKEE_REQUIRE_SUCCESS - (wpid = TWAIT_GENERIC(child2, &status, 0), child2); - - forkee_status_exited(status, exitval2); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Before calling ptrace(2) with PT_GET_SIGINFO for child\n"); - ATF_REQUIRE(ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1); - - printf("Before checking siginfo_t\n"); - ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, sigval); - ATF_REQUIRE_EQ(info.psi_siginfo.si_code, SI_LWP); - - printf("Enable PTRACE_FORK in EVENT_MASK for the child %d\n", child); - event.pe_set_event = PTRACE_FORK; - ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, child, &event, elen) != -1); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child %d\n", TWAIT_FNAME, child); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, SIGTRAP); - - printf("Before calling ptrace(2) with PT_GET_SIGINFO for child\n"); - ATF_REQUIRE(ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1); - - printf("Before checking siginfo_t\n"); - ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, SIGTRAP); - ATF_REQUIRE_EQ(info.psi_siginfo.si_code, TRAP_CHLD); - - ATF_REQUIRE(ptrace(PT_GET_PROCESS_STATE, child, &state, slen) != -1); - ATF_REQUIRE_EQ(state.pe_report_event, PTRACE_FORK); - - child2 = state.pe_other_pid; - printf("Reported PTRACE_FORK event with forkee %d\n", child2); - - printf("Before calling %s() for the forkee %d of the child %d\n", - TWAIT_FNAME, child2, child); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child2, &status, 0), - child2); - - validate_status_stopped(status, SIGTRAP); - - printf("Before calling ptrace(2) with PT_GET_SIGINFO for child\n"); - ATF_REQUIRE(ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1); - - printf("Before checking siginfo_t\n"); - ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, SIGTRAP); - ATF_REQUIRE_EQ(info.psi_siginfo.si_code, TRAP_CHLD); - - ATF_REQUIRE(ptrace(PT_GET_PROCESS_STATE, child2, &state, slen) != -1); - ATF_REQUIRE_EQ(state.pe_report_event, PTRACE_FORK); - ATF_REQUIRE_EQ(state.pe_other_pid, child); - - printf("Before resuming the forkee process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child2, (void *)1, 0) != -1); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the forkee - expected exited\n", - TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child2, &status, 0), - child2); - - validate_status_exited(status, exitval2); - - printf("Before calling %s() for the forkee - expected no process\n", - TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, - wpid = TWAIT_GENERIC(child2, &status, 0)); - - printf("Before calling %s() for the child - expected stopped " - "SIGCHLD\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, SIGCHLD); - - printf("Before calling ptrace(2) with PT_GET_SIGINFO for child\n"); - ATF_REQUIRE(ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1); - - printf("Before checking siginfo_t\n"); - ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, SIGCHLD); - ATF_REQUIRE_EQ(info.psi_siginfo.si_code, CLD_EXITED); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child - expected exited\n", - TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child - expected no process\n", - TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} -#endif - -#if defined(PT_STEP) -ATF_TC(siginfo6); -ATF_TC_HEAD(siginfo6, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify single PT_STEP call with signal information check"); -} - -ATF_TC_BODY(siginfo6, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - int happy; - struct ptrace_siginfo info; - - memset(&info, 0, sizeof(info)); - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - happy = check_happy(100); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - FORKEE_ASSERT_EQ(happy, check_happy(100)); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Before calling ptrace(2) with PT_GET_SIGINFO for child\n"); - ATF_REQUIRE(ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1); - - printf("Before checking siginfo_t\n"); - ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, sigval); - ATF_REQUIRE_EQ(info.psi_siginfo.si_code, SI_LWP); - - printf("Before resuming the child process where it left off and " - "without signal to be sent (use PT_STEP)\n"); - ATF_REQUIRE(ptrace(PT_STEP, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, SIGTRAP); - - printf("Before calling ptrace(2) with PT_GET_SIGINFO for child\n"); - ATF_REQUIRE(ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1); - - printf("Before checking siginfo_t\n"); - ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, SIGTRAP); - ATF_REQUIRE_EQ(info.psi_siginfo.si_code, TRAP_TRACE); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} -#endif - -volatile lwpid_t the_lwp_id = 0; - -static void -lwp_main_func(void *arg) -{ - the_lwp_id = _lwp_self(); - _lwp_exit(); -} - -ATF_TC(lwp_create1); -ATF_TC_HEAD(lwp_create1, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify that 1 LWP creation is intercepted by ptrace(2) with " - "EVENT_MASK set to PTRACE_LWP_CREATE"); -} - -ATF_TC_BODY(lwp_create1, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - ptrace_state_t state; - const int slen = sizeof(state); - ptrace_event_t event; - const int elen = sizeof(event); - ucontext_t uc; - lwpid_t lid; - static const size_t ssize = 16*1024; - void *stack; - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - printf("Before allocating memory for stack in child\n"); - FORKEE_ASSERT((stack = malloc(ssize)) != NULL); - - printf("Before making context for new lwp in child\n"); - _lwp_makecontext(&uc, lwp_main_func, NULL, NULL, stack, ssize); - - printf("Before creating new in child\n"); - FORKEE_ASSERT(_lwp_create(&uc, 0, &lid) == 0); - - printf("Before waiting for lwp %d to exit\n", lid); - FORKEE_ASSERT(_lwp_wait(lid, NULL) == 0); - - printf("Before verifying that reported %d and running lid %d " - "are the same\n", lid, the_lwp_id); - FORKEE_ASSERT_EQ(lid, the_lwp_id); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Set empty EVENT_MASK for the child %d\n", child); - event.pe_set_event = PTRACE_LWP_CREATE; - ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, child, &event, elen) != -1); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child - expected stopped " - "SIGTRAP\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, SIGTRAP); - - ATF_REQUIRE(ptrace(PT_GET_PROCESS_STATE, child, &state, slen) != -1); - - ATF_REQUIRE_EQ(state.pe_report_event, PTRACE_LWP_CREATE); - - lid = state.pe_lwp; - printf("Reported PTRACE_LWP_CREATE event with lid %d\n", lid); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child - expected exited\n", - TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child - expected no process\n", - TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} - -ATF_TC(lwp_exit1); -ATF_TC_HEAD(lwp_exit1, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify that 1 LWP creation is intercepted by ptrace(2) with " - "EVENT_MASK set to PTRACE_LWP_EXIT"); -} - -ATF_TC_BODY(lwp_exit1, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - pid_t child, wpid; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - ptrace_state_t state; - const int slen = sizeof(state); - ptrace_event_t event; - const int elen = sizeof(event); - ucontext_t uc; - lwpid_t lid; - static const size_t ssize = 16*1024; - void *stack; - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - printf("Before allocating memory for stack in child\n"); - FORKEE_ASSERT((stack = malloc(ssize)) != NULL); - - printf("Before making context for new lwp in child\n"); - _lwp_makecontext(&uc, lwp_main_func, NULL, NULL, stack, ssize); - - printf("Before creating new in child\n"); - FORKEE_ASSERT(_lwp_create(&uc, 0, &lid) == 0); - - printf("Before waiting for lwp %d to exit\n", lid); - FORKEE_ASSERT(_lwp_wait(lid, NULL) == 0); - - printf("Before verifying that reported %d and running lid %d " - "are the same\n", lid, the_lwp_id); - FORKEE_ASSERT_EQ(lid, the_lwp_id); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Set empty EVENT_MASK for the child %d\n", child); - event.pe_set_event = PTRACE_LWP_EXIT; - ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, child, &event, elen) != -1); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child - expected stopped " - "SIGTRAP\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, SIGTRAP); - - ATF_REQUIRE(ptrace(PT_GET_PROCESS_STATE, child, &state, slen) != -1); - - ATF_REQUIRE_EQ(state.pe_report_event, PTRACE_LWP_EXIT); - - lid = state.pe_lwp; - printf("Reported PTRACE_LWP_EXIT event with lid %d\n", lid); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child - expected exited\n", - TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child - expected no process\n", - TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} - -ATF_TC(signal1); -ATF_TC_HEAD(signal1, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify that masking single unrelated signal does not stop tracer " - "from catching other signals"); -} - -ATF_TC_BODY(signal1, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - const int sigmasked = SIGTRAP; - const int signotmasked = SIGINT; - pid_t child, wpid; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - sigset_t intmask; - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - sigemptyset(&intmask); - sigaddset(&intmask, sigmasked); - sigprocmask(SIG_BLOCK, &intmask, NULL); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - printf("Before raising %s from child\n", - strsignal(signotmasked)); - FORKEE_ASSERT(raise(signotmasked) == 0); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, signotmasked); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} - -ATF_TC(signal2); -ATF_TC_HEAD(signal2, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify that masking SIGTRAP in tracee stops tracer from " - "catching this raised signal"); -} - -ATF_TC_BODY(signal2, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - const int sigmasked = SIGTRAP; - pid_t child, wpid; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - sigset_t intmask; - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - sigemptyset(&intmask); - sigaddset(&intmask, sigmasked); - sigprocmask(SIG_BLOCK, &intmask, NULL); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - printf("Before raising %s breakpoint from child\n", - strsignal(sigmasked)); - FORKEE_ASSERT(raise(sigmasked) == 0); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} - -ATF_TC(signal3); -ATF_TC_HEAD(signal3, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify that masking SIGTRAP in tracee does not stop tracer from " - "catching software breakpoints"); -} - -ATF_TC_BODY(signal3, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - const int sigmasked = SIGTRAP; - pid_t child, wpid; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - sigset_t intmask; - - atf_tc_expect_fail("PR kern/51918"); - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - sigemptyset(&intmask); - sigaddset(&intmask, sigmasked); - sigprocmask(SIG_BLOCK, &intmask, NULL); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - printf("Before raising software breakpoint from child\n"); -#if defined(__x86_64__) - __asm__ __volatile__ ("int3\n;"); -#else - /* port me */ -#endif - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigmasked); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} - -#if defined(PT_STEP) -ATF_TC(signal4); -ATF_TC_HEAD(signal4, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify that masking SIGTRAP in tracee does not stop tracer from " - "catching single step trap"); -} - -ATF_TC_BODY(signal4, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - const int sigmasked = SIGTRAP; - pid_t child, wpid; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - sigset_t intmask; - int happy; - - atf_tc_expect_fail("PR kern/51918"); - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - happy = check_happy(100); - - sigemptyset(&intmask); - sigaddset(&intmask, sigmasked); - sigprocmask(SIG_BLOCK, &intmask, NULL); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - FORKEE_ASSERT_EQ(happy, check_happy(100)); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_STEP, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigmasked); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} -#endif - -ATF_TC(signal5); -ATF_TC_HEAD(signal5, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify that masking SIGTRAP in tracee does not stop tracer from " - "catching exec() breakpoint"); -} - -ATF_TC_BODY(signal5, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - const int sigmasked = SIGTRAP; - pid_t child, wpid; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - sigset_t intmask; - - atf_tc_expect_fail("PR kern/51918"); - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - sigemptyset(&intmask); - sigaddset(&intmask, sigmasked); - sigprocmask(SIG_BLOCK, &intmask, NULL); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - printf("Before calling execve(2) from child\n"); - execlp("/bin/echo", "/bin/echo", NULL); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigmasked); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} - -#if defined(TWAIT_HAVE_PID) -ATF_TC(signal6); -ATF_TC_HEAD(signal6, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify that masking SIGTRAP in tracee does not stop tracer from " - "catching PTRACE_FORK breakpoint"); -} - -ATF_TC_BODY(signal6, tc) -{ - const int exitval = 5; - const int exitval2 = 15; - const int sigval = SIGSTOP; - const int sigmasked = SIGTRAP; - pid_t child, child2, wpid; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - sigset_t intmask; - ptrace_state_t state; - const int slen = sizeof(state); - ptrace_event_t event; - const int elen = sizeof(event); - - atf_tc_expect_fail("PR kern/51918"); - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - sigemptyset(&intmask); - sigaddset(&intmask, sigmasked); - sigprocmask(SIG_BLOCK, &intmask, NULL); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - FORKEE_ASSERT((child2 = fork()) != 1); - - if (child2 == 0) - _exit(exitval2); - - FORKEE_REQUIRE_SUCCESS - (wpid = TWAIT_GENERIC(child2, &status, 0), child2); - - forkee_status_exited(status, exitval2); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Enable PTRACE_FORK in EVENT_MASK for the child %d\n", child); - event.pe_set_event = PTRACE_FORK; - ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, child, &event, elen) != -1); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigmasked); - - ATF_REQUIRE(ptrace(PT_GET_PROCESS_STATE, child, &state, slen) != -1); - ATF_REQUIRE_EQ(state.pe_report_event, PTRACE_FORK); - - child2 = state.pe_other_pid; - printf("Reported PTRACE_FORK event with forkee %d\n", child2); - - printf("Before calling %s() for the child2\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child2, &status, 0), - child2); - - validate_status_stopped(status, SIGTRAP); - - ATF_REQUIRE(ptrace(PT_GET_PROCESS_STATE, child2, &state, slen) != -1); - ATF_REQUIRE_EQ(state.pe_report_event, PTRACE_FORK); - ATF_REQUIRE_EQ(state.pe_other_pid, child); - - printf("Before resuming the forkee process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child2, (void *)1, 0) != -1); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the forkee - expected exited\n", - TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child2, &status, 0), - child2); - - validate_status_exited(status, exitval2); - - printf("Before calling %s() for the forkee - expected no process\n", - TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, - wpid = TWAIT_GENERIC(child2, &status, 0)); - - printf("Before calling %s() for the child - expected stopped " - "SIGCHLD\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, SIGCHLD); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child - expected exited\n", - TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child - expected no process\n", - TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} -#endif - -#if defined(TWAIT_HAVE_PID) -ATF_TC(signal7); -ATF_TC_HEAD(signal7, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify that masking SIGTRAP in tracee does not stop tracer from " - "catching PTRACE_VFORK breakpoint"); -} - -ATF_TC_BODY(signal7, tc) -{ - const int exitval = 5; - const int exitval2 = 15; - const int sigval = SIGSTOP; - const int sigmasked = SIGTRAP; - pid_t child, child2, wpid; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - sigset_t intmask; - ptrace_state_t state; - const int slen = sizeof(state); - ptrace_event_t event; - const int elen = sizeof(event); - - atf_tc_expect_fail("PR kern/51918 PR kern/51630"); - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - sigemptyset(&intmask); - sigaddset(&intmask, sigmasked); - sigprocmask(SIG_BLOCK, &intmask, NULL); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - FORKEE_ASSERT((child2 = fork()) != 1); - - if (child2 == 0) - _exit(exitval2); - - FORKEE_REQUIRE_SUCCESS - (wpid = TWAIT_GENERIC(child2, &status, 0), child2); - - forkee_status_exited(status, exitval2); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Enable PTRACE_VFORK in EVENT_MASK for the child %d\n", child); - event.pe_set_event = PTRACE_VFORK; - ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, child, &event, elen) != -1); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigmasked); - - ATF_REQUIRE(ptrace(PT_GET_PROCESS_STATE, child, &state, slen) != -1); - ATF_REQUIRE_EQ(state.pe_report_event, PTRACE_VFORK); - - child2 = state.pe_other_pid; - printf("Reported PTRACE_VFORK event with forkee %d\n", child2); - - printf("Before calling %s() for the child2\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child2, &status, 0), - child2); - - validate_status_stopped(status, SIGTRAP); - - ATF_REQUIRE(ptrace(PT_GET_PROCESS_STATE, child2, &state, slen) != -1); - ATF_REQUIRE_EQ(state.pe_report_event, PTRACE_VFORK); - ATF_REQUIRE_EQ(state.pe_other_pid, child); - - printf("Before resuming the forkee process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child2, (void *)1, 0) != -1); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the forkee - expected exited\n", - TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child2, &status, 0), - child2); - - validate_status_exited(status, exitval2); - - printf("Before calling %s() for the forkee - expected no process\n", - TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, - wpid = TWAIT_GENERIC(child2, &status, 0)); - - printf("Before calling %s() for the child - expected stopped " - "SIGCHLD\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, SIGCHLD); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child - expected exited\n", - TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child - expected no process\n", - TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} -#endif - -ATF_TC(signal8); -ATF_TC_HEAD(signal8, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify that masking SIGTRAP in tracee does not stop tracer from " - "catching PTRACE_VFORK_DONE breakpoint"); -} - -ATF_TC_BODY(signal8, tc) -{ - const int exitval = 5; - const int exitval2 = 15; - const int sigval = SIGSTOP; - const int sigmasked = SIGTRAP; - pid_t child, child2, wpid; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - sigset_t intmask; - ptrace_state_t state; - const int slen = sizeof(state); - ptrace_event_t event; - const int elen = sizeof(event); - - atf_tc_expect_fail("PR kern/51918"); - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - sigemptyset(&intmask); - sigaddset(&intmask, sigmasked); - sigprocmask(SIG_BLOCK, &intmask, NULL); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - FORKEE_ASSERT((child2 = vfork()) != 1); - - if (child2 == 0) - _exit(exitval2); - - FORKEE_REQUIRE_SUCCESS - (wpid = TWAIT_GENERIC(child2, &status, 0), child2); - - forkee_status_exited(status, exitval2); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Enable PTRACE_VFORK_DONE in EVENT_MASK for the child %d\n", - child); - event.pe_set_event = PTRACE_VFORK_DONE; - ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, child, &event, elen) != -1); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigmasked); - - ATF_REQUIRE(ptrace(PT_GET_PROCESS_STATE, child, &state, slen) != -1); - ATF_REQUIRE_EQ(state.pe_report_event, PTRACE_VFORK_DONE); - - child2 = state.pe_other_pid; - printf("Reported PTRACE_VFORK_DONE event with forkee %d\n", child2); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child - expected stopped " - "SIGCHLD\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, SIGCHLD); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child - expected exited\n", - TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child - expected no process\n", - TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} - -ATF_TC(signal9); -ATF_TC_HEAD(signal9, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify that masking SIGTRAP in tracee does not stop tracer from " - "catching PTRACE_LWP_CREATE breakpoint"); -} - -ATF_TC_BODY(signal9, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - const int sigmasked = SIGTRAP; - pid_t child, wpid; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - sigset_t intmask; - ptrace_state_t state; - const int slen = sizeof(state); - ptrace_event_t event; - const int elen = sizeof(event); - ucontext_t uc; - lwpid_t lid; - static const size_t ssize = 16*1024; - void *stack; - - atf_tc_expect_fail("PR kern/51918"); - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - sigemptyset(&intmask); - sigaddset(&intmask, sigmasked); - sigprocmask(SIG_BLOCK, &intmask, NULL); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - printf("Before allocating memory for stack in child\n"); - FORKEE_ASSERT((stack = malloc(ssize)) != NULL); - - printf("Before making context for new lwp in child\n"); - _lwp_makecontext(&uc, lwp_main_func, NULL, NULL, stack, ssize); - - printf("Before creating new in child\n"); - FORKEE_ASSERT(_lwp_create(&uc, 0, &lid) == 0); - - printf("Before waiting for lwp %d to exit\n", lid); - FORKEE_ASSERT(_lwp_wait(lid, NULL) == 0); - - printf("Before verifying that reported %d and running lid %d " - "are the same\n", lid, the_lwp_id); - FORKEE_ASSERT_EQ(lid, the_lwp_id); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Set empty EVENT_MASK for the child %d\n", child); - event.pe_set_event = PTRACE_LWP_CREATE; - ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, child, &event, elen) != -1); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child - expected stopped " - "SIGTRAP\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigmasked); - - ATF_REQUIRE(ptrace(PT_GET_PROCESS_STATE, child, &state, slen) != -1); - - ATF_REQUIRE_EQ(state.pe_report_event, PTRACE_LWP_CREATE); - - lid = state.pe_lwp; - printf("Reported PTRACE_LWP_CREATE event with lid %d\n", lid); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child - expected exited\n", - TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child - expected no process\n", - TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} - -ATF_TC(signal10); -ATF_TC_HEAD(signal10, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Verify that masking SIGTRAP in tracee does not stop tracer from " - "catching PTRACE_LWP_EXIT breakpoint"); -} - -ATF_TC_BODY(signal10, tc) -{ - const int exitval = 5; - const int sigval = SIGSTOP; - const int sigmasked = SIGTRAP; - pid_t child, wpid; -#if defined(TWAIT_HAVE_STATUS) - int status; -#endif - sigset_t intmask; - ptrace_state_t state; - const int slen = sizeof(state); - ptrace_event_t event; - const int elen = sizeof(event); - ucontext_t uc; - lwpid_t lid; - static const size_t ssize = 16*1024; - void *stack; - - atf_tc_expect_fail("PR kern/51918"); - - printf("Before forking process PID=%d\n", getpid()); - ATF_REQUIRE((child = fork()) != -1); - if (child == 0) { - printf("Before calling PT_TRACE_ME from child %d\n", getpid()); - FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); - - sigemptyset(&intmask); - sigaddset(&intmask, sigmasked); - sigprocmask(SIG_BLOCK, &intmask, NULL); - - printf("Before raising %s from child\n", strsignal(sigval)); - FORKEE_ASSERT(raise(sigval) == 0); - - printf("Before allocating memory for stack in child\n"); - FORKEE_ASSERT((stack = malloc(ssize)) != NULL); - - printf("Before making context for new lwp in child\n"); - _lwp_makecontext(&uc, lwp_main_func, NULL, NULL, stack, ssize); - - printf("Before creating new in child\n"); - FORKEE_ASSERT(_lwp_create(&uc, 0, &lid) == 0); - - printf("Before waiting for lwp %d to exit\n", lid); - FORKEE_ASSERT(_lwp_wait(lid, NULL) == 0); - - printf("Before verifying that reported %d and running lid %d " - "are the same\n", lid, the_lwp_id); - FORKEE_ASSERT_EQ(lid, the_lwp_id); - - printf("Before exiting of the child process\n"); - _exit(exitval); - } - printf("Parent process PID=%d, child's PID=%d\n", getpid(), child); - - printf("Before calling %s() for the child\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigval); - - printf("Set empty EVENT_MASK for the child %d\n", child); - event.pe_set_event = PTRACE_LWP_EXIT; - ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, child, &event, elen) != -1); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child - expected stopped " - "SIGTRAP\n", TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_stopped(status, sigmasked); - - ATF_REQUIRE(ptrace(PT_GET_PROCESS_STATE, child, &state, slen) != -1); - - ATF_REQUIRE_EQ(state.pe_report_event, PTRACE_LWP_EXIT); - - lid = state.pe_lwp; - printf("Reported PTRACE_LWP_EXIT event with lid %d\n", lid); - - printf("Before resuming the child process where it left off and " - "without signal to be sent\n"); - ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); - - printf("Before calling %s() for the child - expected exited\n", - TWAIT_FNAME); - TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); - - validate_status_exited(status, exitval); - - printf("Before calling %s() for the child - expected no process\n", - TWAIT_FNAME); - TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); -} - -ATF_TP_ADD_TCS(tp) -{ - setvbuf(stdout, NULL, _IONBF, 0); - setvbuf(stderr, NULL, _IONBF, 0); - ATF_TP_ADD_TC(tp, traceme1); - ATF_TP_ADD_TC(tp, traceme2); - ATF_TP_ADD_TC(tp, traceme3); - ATF_TP_ADD_TC(tp, traceme4); - - ATF_TP_ADD_TC_HAVE_PID(tp, attach1); - ATF_TP_ADD_TC_HAVE_PID(tp, attach2); - ATF_TP_ADD_TC(tp, attach3); - ATF_TP_ADD_TC(tp, attach4); - ATF_TP_ADD_TC_HAVE_PID(tp, attach5); - ATF_TP_ADD_TC_HAVE_PID(tp, attach6); - ATF_TP_ADD_TC_HAVE_PID(tp, attach7); - - ATF_TP_ADD_TC(tp, eventmask1); - ATF_TP_ADD_TC(tp, eventmask2); - ATF_TP_ADD_TC(tp, eventmask3); - ATF_TP_ADD_TC(tp, eventmask4); - ATF_TP_ADD_TC(tp, eventmask5); - ATF_TP_ADD_TC(tp, eventmask6); - - ATF_TP_ADD_TC_HAVE_PID(tp, fork1); - ATF_TP_ADD_TC(tp, fork2); - - ATF_TP_ADD_TC_HAVE_PID(tp, vfork1); - ATF_TP_ADD_TC(tp, vfork2); - - ATF_TP_ADD_TC(tp, vforkdone1); - ATF_TP_ADD_TC(tp, vforkdone2); - - ATF_TP_ADD_TC(tp, io_read_d1); - ATF_TP_ADD_TC(tp, io_read_d2); - ATF_TP_ADD_TC(tp, io_read_d3); - ATF_TP_ADD_TC(tp, io_read_d4); - - ATF_TP_ADD_TC(tp, io_write_d1); - ATF_TP_ADD_TC(tp, io_write_d2); - ATF_TP_ADD_TC(tp, io_write_d3); - ATF_TP_ADD_TC(tp, io_write_d4); - - ATF_TP_ADD_TC(tp, read_d1); - ATF_TP_ADD_TC(tp, read_d2); - ATF_TP_ADD_TC(tp, read_d3); - ATF_TP_ADD_TC(tp, read_d4); - - ATF_TP_ADD_TC(tp, write_d1); - ATF_TP_ADD_TC(tp, write_d2); - ATF_TP_ADD_TC(tp, write_d3); - ATF_TP_ADD_TC(tp, write_d4); - - ATF_TP_ADD_TC(tp, io_read_d_write_d_handshake1); - ATF_TP_ADD_TC(tp, io_read_d_write_d_handshake2); - - ATF_TP_ADD_TC(tp, read_d_write_d_handshake1); - ATF_TP_ADD_TC(tp, read_d_write_d_handshake2); - - ATF_TP_ADD_TC(tp, io_read_i1); - ATF_TP_ADD_TC(tp, io_read_i2); - ATF_TP_ADD_TC(tp, io_read_i3); - ATF_TP_ADD_TC(tp, io_read_i4); - - ATF_TP_ADD_TC(tp, read_i1); - ATF_TP_ADD_TC(tp, read_i2); - ATF_TP_ADD_TC(tp, read_i3); - ATF_TP_ADD_TC(tp, read_i4); - - ATF_TP_ADD_TC(tp, io_read_auxv1); - - ATF_TP_ADD_TC_HAVE_GPREGS(tp, regs1); - ATF_TP_ADD_TC_HAVE_GPREGS(tp, regs2); - ATF_TP_ADD_TC_HAVE_GPREGS(tp, regs3); - ATF_TP_ADD_TC_HAVE_GPREGS(tp, regs4); - ATF_TP_ADD_TC_HAVE_GPREGS(tp, regs5); - - ATF_TP_ADD_TC_HAVE_FPREGS(tp, fpregs1); - ATF_TP_ADD_TC_HAVE_FPREGS(tp, fpregs2); - - ATF_TP_ADD_TC_PT_STEP(tp, step1); - ATF_TP_ADD_TC_PT_STEP(tp, step2); - ATF_TP_ADD_TC_PT_STEP(tp, step3); - ATF_TP_ADD_TC_PT_STEP(tp, step4); - - ATF_TP_ADD_TC(tp, kill1); - ATF_TP_ADD_TC(tp, kill2); - - ATF_TP_ADD_TC(tp, lwpinfo1); - ATF_TP_ADD_TC_HAVE_PID(tp, lwpinfo2); - - ATF_TP_ADD_TC(tp, siginfo1); - ATF_TP_ADD_TC(tp, siginfo2); - ATF_TP_ADD_TC(tp, siginfo3); - ATF_TP_ADD_TC(tp, siginfo4); - ATF_TP_ADD_TC_HAVE_PID(tp, siginfo5); - ATF_TP_ADD_TC_PT_STEP(tp, siginfo6); - - ATF_TP_ADD_TC(tp, lwp_create1); - - ATF_TP_ADD_TC(tp, lwp_exit1); - - ATF_TP_ADD_TC(tp, signal1); - ATF_TP_ADD_TC(tp, signal2); - ATF_TP_ADD_TC(tp, signal3); - ATF_TP_ADD_TC_PT_STEP(tp, signal4); - ATF_TP_ADD_TC(tp, signal5); - ATF_TP_ADD_TC_HAVE_PID(tp, signal6); - ATF_TP_ADD_TC_HAVE_PID(tp, signal7); - ATF_TP_ADD_TC(tp, signal8); - ATF_TP_ADD_TC(tp, signal9); - ATF_TP_ADD_TC(tp, signal10); - - return atf_no_error(); -} diff --git a/kernel/t_ptrace_wait3.c b/kernel/t_ptrace_wait3.c deleted file mode 100644 --- a/kernel/t_ptrace_wait3.c +++ /dev/null @@ -1,30 +0,0 @@ -/* $NetBSD: t_ptrace_wait3.c,v 1.1 2016/11/07 21:09:03 kamil Exp $ */ - -/*- - * Copyright (c) 2016 The NetBSD Foundation, Inc. - * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. - */ - -#define TWAIT_WAIT3 -#include "t_ptrace_wait.c" diff --git a/kernel/t_ptrace_wait4.c b/kernel/t_ptrace_wait4.c deleted file mode 100644 --- a/kernel/t_ptrace_wait4.c +++ /dev/null @@ -1,30 +0,0 @@ -/* $NetBSD: t_ptrace_wait4.c,v 1.1 2016/11/07 21:09:03 kamil Exp $ */ - -/*- - * Copyright (c) 2016 The NetBSD Foundation, Inc. - * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. - */ - -#define TWAIT_WAIT4 -#include "t_ptrace_wait.c" diff --git a/kernel/t_ptrace_wait6.c b/kernel/t_ptrace_wait6.c deleted file mode 100644 --- a/kernel/t_ptrace_wait6.c +++ /dev/null @@ -1,30 +0,0 @@ -/* $NetBSD: t_ptrace_wait6.c,v 1.1 2016/11/07 21:09:03 kamil Exp $ */ - -/*- - * Copyright (c) 2016 The NetBSD Foundation, Inc. - * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. - */ - -#define TWAIT_WAIT6 -#include "t_ptrace_wait.c" diff --git a/kernel/t_ptrace_waitid.c b/kernel/t_ptrace_waitid.c deleted file mode 100644 --- a/kernel/t_ptrace_waitid.c +++ /dev/null @@ -1,30 +0,0 @@ -/* $NetBSD: t_ptrace_waitid.c,v 1.1 2016/11/07 21:09:03 kamil Exp $ */ - -/*- - * Copyright (c) 2016 The NetBSD Foundation, Inc. - * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. - */ - -#define TWAIT_WAITID -#include "t_ptrace_wait.c" diff --git a/kernel/t_ptrace_waitpid.c b/kernel/t_ptrace_waitpid.c deleted file mode 100644 --- a/kernel/t_ptrace_waitpid.c +++ /dev/null @@ -1,30 +0,0 @@ -/* $NetBSD: t_ptrace_waitpid.c,v 1.1 2016/11/07 21:09:03 kamil Exp $ */ - -/*- - * Copyright (c) 2016 The NetBSD Foundation, Inc. - * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. - */ - -#define TWAIT_WAITPID -#include "t_ptrace_wait.c" diff --git a/kernel/t_pty.c b/kernel/t_pty.c --- a/kernel/t_pty.c +++ b/kernel/t_pty.c @@ -1,4 +1,4 @@ -/* $Id: t_pty.c,v 1.2 2017/01/13 21:30:41 christos Exp $ */ +/* $Id: t_pty.c,v 1.5 2020/06/24 07:02:57 rin Exp $ */ /* * Allocates a pty(4) device, and sends the specified number of packets of the @@ -9,7 +9,7 @@ */ #include -__RCSID("$NetBSD: t_pty.c,v 1.2 2017/01/13 21:30:41 christos Exp $"); +__RCSID("$NetBSD: t_pty.c,v 1.5 2020/06/24 07:02:57 rin Exp $"); #include #include @@ -31,6 +31,8 @@ #include #ifdef STANDALONE +#define atf_tc_fail_errno(fmt, ...) err(EXIT_FAILURE, fmt, ## __VA_ARGS__) +#define atf_tc_fail(fmt, ...) errx(EXIT_FAILURE, fmt, ## __VA_ARGS__) static __dead void usage(const char *); static void parse_args(int, char **); #else @@ -59,7 +61,7 @@ int status; pid_t child; if ((dbuf = calloc(1, buffer_size)) == NULL) - err(EXIT_FAILURE, "malloc(%zu)", buffer_size); + atf_tc_fail_errno("malloc(%zu)", buffer_size); if (verbose) (void)printf( @@ -84,7 +86,7 @@ "parent: attempting to write %zu bytes to PTY\n", buffer_size); if ((size = write(pty, dbuf, buffer_size)) == -1) { - err(EXIT_FAILURE, "parent: write()"); + atf_tc_fail_errno("parent: write()"); break; } if (verbose) @@ -94,9 +96,9 @@ if (verbose) (void)printf("parent: waiting for child to exit\n"); if (waitpid(child, &status, 0) == -1) - err(EXIT_FAILURE, "waitpid"); + atf_tc_fail_errno("waitpid"); if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) - errx(EXIT_FAILURE, "child failed"); + atf_tc_fail("child failed"); if (verbose) (void)printf("parent: closing PTY\n"); @@ -113,19 +115,18 @@ if (qsize) { int opt = qsize; if (ioctl(fd, TIOCSQSIZE, &opt) == -1) - err(EXIT_FAILURE, "Couldn't set tty(4) buffer size"); + atf_tc_fail_errno("Couldn't set tty(4) buffer size"); if (ioctl(fd, TIOCGQSIZE, &opt) == -1) - err(EXIT_FAILURE, "Couldn't get tty(4) buffer size"); + atf_tc_fail_errno("Couldn't get tty(4) buffer size"); if (opt != qsize) - errx(EXIT_FAILURE, "Wrong qsize %d != %d\n", - qsize, opt); + atf_tc_fail("Wrong qsize %d != %d\n", qsize, opt); } if (tcgetattr(fd, &tios) == -1) - err(EXIT_FAILURE, "tcgetattr()"); + atf_tc_fail_errno("tcgetattr()"); cfmakeraw(&tios); cfsetspeed(&tios, B921600); if (tcsetattr(fd, TCSANOW, &tios) == -1) - err(EXIT_FAILURE, "tcsetattr()"); + atf_tc_fail_errno("tcsetattr()"); } static int @@ -134,17 +135,17 @@ int fd; if ((fd = posix_openpt(O_RDWR)) == -1) - err(EXIT_FAILURE, "Couldn't pty(4) device"); + atf_tc_fail_errno("Couldn't pty(4) device"); condition(fd); if (grantpt(fd) == -1) - err(EXIT_FAILURE, + atf_tc_fail_errno( "Couldn't grant permissions on tty(4) device"); condition(fd); if (unlockpt(fd) == -1) - err(EXIT_FAILURE, "unlockpt()"); + atf_tc_fail_errno("unlockpt()"); return fd; } @@ -155,13 +156,13 @@ int fd; if ((fd = open(ttydev, O_RDWR, 0)) == -1) - err(EXIT_FAILURE, "Couldn't open tty(4) device"); + atf_tc_fail_errno("Couldn't open tty(4) device"); #ifdef USE_PPP_DISCIPLINE { int opt = PPPDISC; if (ioctl(fd, TIOCSETD, &opt) == -1) - err(EXIT_FAILURE, + atf_tc_fail_errno( "Couldn't set tty(4) discipline to PPP"); } #endif @@ -177,9 +178,9 @@ int opt; if ((opt = fcntl(fd, F_GETFL, NULL)) == -1) - err(EXIT_FAILURE, "fcntl()"); + atf_tc_fail_errno("fcntl()"); if (fcntl(fd, F_SETFL, opt | O_NONBLOCK) == -1) - err(EXIT_FAILURE, "fcntl()"); + atf_tc_fail_errno("fcntl()"); } static pid_t @@ -191,7 +192,7 @@ size_t total = 0; if ((pid = fork()) == -1) - err(EXIT_FAILURE, "fork()"); + atf_tc_fail_errno("fork()"); (void)setsid(); if (pid != 0) return pid; @@ -230,11 +231,6 @@ break; err(EXIT_FAILURE, "child: read()"); } - if (qsize && size < qsize && - (size_t)size < buffer_size) - errx(EXIT_FAILURE, "read returned %zd " - "less than the queue size %d", - size, qsize); if (verbose) (void)printf( "child: read %zd bytes from TTY\n", diff --git a/kernel/t_rnd.c b/kernel/t_rnd.c --- a/kernel/t_rnd.c +++ b/kernel/t_rnd.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_rnd.c,v 1.10 2017/01/13 21:30:41 christos Exp $ */ +/* $NetBSD: t_rnd.c,v 1.11 2017/04/16 18:24:23 riastradh Exp $ */ /* * Copyright (c) 2009 The NetBSD Foundation, Inc. @@ -27,7 +27,7 @@ */ #include -__RCSID("$NetBSD: t_rnd.c,v 1.10 2017/01/13 21:30:41 christos Exp $"); +__RCSID("$NetBSD: t_rnd.c,v 1.11 2017/04/16 18:24:23 riastradh Exp $"); #include #include @@ -35,6 +35,7 @@ #include #include +#include #include #include @@ -98,10 +99,15 @@ { char buf[128]; int fd; + unsigned i; rump_init(); - RL(fd = rump_sys_open("/dev/random", O_RDONLY)); - RL(rump_sys_read(fd, buf, sizeof(buf))); + for (i = 0; i < 1000; i++) { + alarm(2); + RL(fd = rump_sys_open("/dev/random", RUMP_O_RDONLY)); + RL(rump_sys_read(fd, buf, sizeof(buf))); + RZ(rump_sys_close(fd)); + } } ATF_TP_ADD_TCS(tp) diff --git a/kernel/t_sysv.c b/kernel/t_sysv.c --- a/kernel/t_sysv.c +++ b/kernel/t_sysv.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_sysv.c,v 1.4 2014/03/02 20:13:12 jmmv Exp $ */ +/* $NetBSD: t_sysv.c,v 1.5 2018/02/03 02:57:15 pgoyette Exp $ */ /*- * Copyright (c) 1999, 2007 The NetBSD Foundation, Inc. @@ -72,7 +72,7 @@ #define MESSAGE_TEXT_LEN 256 -struct mymsg { +struct testmsg { long mtype; char mtext[MESSAGE_TEXT_LEN]; }; @@ -174,7 +174,7 @@ /* Create the file, since ftok() requires it to exist! */ - fd = open(token_key, O_RDWR | O_CREAT | O_EXCL); + fd = open(token_key, O_RDWR | O_CREAT | O_EXCL, 0600); if (fd == -1) { rmdir(tmpdir); atf_tc_fail("open() of temp file failed: %d", errno); @@ -183,6 +183,7 @@ close(fd); key = ftok(token_key, id); + ATF_REQUIRE_MSG(key != (key_t)-1, "ftok() failed"); ATF_REQUIRE_MSG(unlink(token_key) != -1, "unlink() failed: %d", errno); ATF_REQUIRE_MSG(rmdir(token_dir) != -1, "rmdir() failed: %d", errno); @@ -202,7 +203,7 @@ { struct sigaction sa; struct msqid_ds m_ds; - struct mymsg m; + struct testmsg m; sigset_t sigmask; int sender_msqid; int loop; @@ -347,9 +348,7 @@ } void -print_msqid_ds(mp, mode) - struct msqid_ds *mp; - mode_t mode; +print_msqid_ds(struct msqid_ds *mp, mode_t mode) { uid_t uid = geteuid(); gid_t gid = getegid(); @@ -381,9 +380,9 @@ } void -receiver() +receiver(void) { - struct mymsg m; + struct testmsg m; int msqid, loop; if ((msqid = msgget(msgkey, 0)) == -1) @@ -588,9 +587,7 @@ } void -print_semid_ds(sp, mode) - struct semid_ds *sp; - mode_t mode; +print_semid_ds(struct semid_ds *sp, mode_t mode) { uid_t uid = geteuid(); gid_t gid = getegid(); @@ -620,7 +617,7 @@ } void -waiter() +waiter(void) { struct sembuf s; int semid; @@ -789,9 +786,7 @@ } void -print_shmid_ds(sp, mode) - struct shmid_ds *sp; - mode_t mode; +print_shmid_ds(struct shmid_ds *sp, mode_t mode) { uid_t uid = geteuid(); gid_t gid = getegid(); @@ -819,11 +814,12 @@ ATF_REQUIRE_MSG(sp->shm_perm.gid == gid && sp->shm_perm.cgid == gid, "gid mismatch"); - ATF_REQUIRE_MSG((sp->shm_perm.mode & 0777) == mode, "mode mismatch"); + ATF_REQUIRE_MSG((sp->shm_perm.mode & 0777) == mode, + "mode mismatch %o != %o", sp->shm_perm.mode & 0777, mode); } void -sharer() +sharer(void) { int shmid; void *shm_buf; diff --git a/kernel/t_timeleft.c b/kernel/t_timeleft.c new file mode 100644 --- /dev/null +++ b/kernel/t_timeleft.c @@ -0,0 +1,137 @@ +/* $NetBSD: t_timeleft.c,v 1.4 2019/01/14 00:23:43 christos Exp $ */ + +/*- + * Copyright (c) 2017 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +__COPYRIGHT("@(#) Copyright (c) 2008\ + The NetBSD Foundation, inc. All rights reserved."); +__RCSID("$NetBSD: t_timeleft.c,v 1.4 2019/01/14 00:23:43 christos Exp $"); + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void +sighandler(int signo __unused) +{ +} + +struct info { + void (*fun)(struct timespec *); + struct timespec ts; +}; + +static void +timeleft__lwp_park(struct timespec *ts) +{ + ATF_REQUIRE_ERRNO(EINTR, _lwp_park(CLOCK_MONOTONIC, TIMER_RELTIME, + ts, 0, ts, NULL) == -1); +} + +#if 0 +static void +timeleft_pselect(struct timespec *ts) +{ + ATF_REQUIRE_ERRNO(EINTR, pselect(1, NULL, NULL, NULL, ts, NULL)); +} +#endif + +static void * +runner(void *arg) +{ + struct info *i = arg; + (*i->fun)(&i->ts); + return NULL; +} + +static void +tester(void (*fun)(struct timespec *)) +{ + const struct timespec ts = { 5, 0 }; + const struct timespec sts = { 1, 0 }; + struct info i = { fun, ts }; + pthread_t thr; + + ATF_REQUIRE(signal(SIGINT, sighandler) == 0); + ATF_REQUIRE(pthread_create(&thr, NULL, runner, &i) == 0); + + ATF_REQUIRE(nanosleep(&sts, NULL) == 0); + ATF_REQUIRE(pthread_kill(thr, SIGINT) == 0); + ATF_REQUIRE(pthread_join(thr, NULL) == 0); + printf("Orig time %ju.%lu\n", (intmax_t)ts.tv_sec, ts.tv_nsec); + printf("Time left %ju.%lu\n", (intmax_t)i.ts.tv_sec, i.ts.tv_nsec); + ATF_REQUIRE(timespeccmp(&i.ts, &ts, <)); +} + +ATF_TC(timeleft__lwp_park); +ATF_TC_HEAD(timeleft__lwp_park, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks that _lwp_park(2) returns " + "the time left to sleep after interrupted"); +} + +ATF_TC_BODY(timeleft__lwp_park, tc) +{ + tester(timeleft__lwp_park); +} + +#if 0 +ATF_TC(timeleft_pselect); +ATF_TC_HEAD(timeleft_pselect, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks that pselect(2) returns " + "the time left to sleep after interrupted"); +} + +ATF_TC_BODY(timeleft_pselect, tc) +{ + tester(timeleft_pselect); +} +#endif + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, timeleft__lwp_park); +#if 0 + ATF_TP_ADD_TC(tp, timeleft_pselect); +#endif + + return atf_no_error(); +} diff --git a/kernel/t_trapsignal.sh b/kernel/t_trapsignal.sh new file mode 100644 --- /dev/null +++ b/kernel/t_trapsignal.sh @@ -0,0 +1,362 @@ +# $NetBSD: t_trapsignal.sh,v 1.5 2019/01/26 16:44:30 martin Exp $ +# +# Copyright (c) 2017 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Christos Zoulas. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +HELPER=$(atf_get_srcdir)/h_segv + +# SIGSEGV + +atf_test_case segv_simple +segv_simple() +{ + atf_set "descr" "Test unhandled SIGSEGV with the right exit code" +} +segv_simple_body() +{ + atf_check -s signal:11 -o "inline:" -e "inline:" \ + ${HELPER} segv recurse +} + +atf_test_case segv_handle +segv_handle() +{ + atf_set "descr" "Test handled SIGSEGV traps call the signal handler" +} +segv_handle_body() +{ + atf_check -s exit:0 -o "inline:" -e "inline:got 11\n" \ + ${HELPER} segv handle +} + +atf_test_case segv_mask +segv_mask() +{ + atf_set "descr" "Test that masking SIGSEGV get reset" +} +segv_mask_body() +{ + atf_check -s signal:11 -o "inline:" -e "inline:" \ + ${HELPER} segv mask +} + +atf_test_case segv_handle_mask +segv_handle_mask() +{ + atf_set "descr" "Test handled and masked SIGSEGV traps get reset" +} +segv_handle_mask_body() +{ + atf_check -s signal:11 -o "inline:" -e "inline:" \ + ${HELPER} segv mask handle +} + +atf_test_case segv_handle_recurse +segv_handle_recurse() +{ + atf_set "descr" "Test that receiving SIGSEGV in the handler resets" +} + +segv_handle_recurse_body() +{ + atf_check -s signal:11 -o "inline:" -e "inline:got 11\n" \ + ${HELPER} segv handle recurse +} + +atf_test_case segv_ignore +segv_ignore() +{ + atf_set "descr" "Test ignored SIGSEGV trap with right exit code" +} + +segv_ignore_body() +{ + atf_check -s signal:11 -o "inline:" -e "inline:" \ + ${HELPER} segv ignore +} + +# SIGTRAP + +atf_test_case trap_simple +trap_simple() +{ + atf_set "descr" "Test unhandled SIGTRAP with the right exit code" +} +trap_simple_body() +{ + atf_check -s signal:5 -o "inline:" -e "inline:" \ + ${HELPER} trap recurse +} + +atf_test_case trap_handle +trap_handle() +{ + atf_set "descr" "Test handled SIGTRAP traps call the signal handler" +} +trap_handle_body() +{ + atf_check -s exit:0 -o "inline:" -e "inline:got 5\n" \ + ${HELPER} trap handle +} + +atf_test_case trap_mask +trap_mask() +{ + atf_set "descr" "Test that masking the trapped SIGTRAP signal get reset" +} +trap_mask_body() +{ + atf_check -s signal:5 -o "inline:" -e "inline:" \ + ${HELPER} trap mask +} + +atf_test_case trap_handle_mask +trap_handle_mask() +{ + atf_set "descr" "Test handled and masked SIGTRAP traps get reset" +} +trap_handle_mask_body() +{ + atf_check -s signal:5 -o "inline:" -e "inline:" \ + ${HELPER} trap mask handle +} + +atf_test_case trap_handle_recurse +trap_handle_recurse() +{ + atf_set "descr" "Test that receiving SIGTRAP in the handler resets" +} + +trap_handle_recurse_body() +{ + atf_check -s signal:5 -o "inline:" -e "inline:got 5\n" \ + ${HELPER} trap handle recurse +} + +atf_test_case trap_ignore +trap_ignore() +{ + atf_set "descr" "Test ignored trap with right exit code" +} + +trap_ignore_body() +{ + atf_check -s signal:5 -o "inline:" -e "inline:" \ + ${HELPER} trap ignore +} + +# SIGFPE + +fpe_available() +{ + if ${HELPER} fpe check > msg.$$ + then + rm -f msg.$$ + else + msg=$( cat msg.$$ ) + rm -f msg.$$ + atf_skip "$msg" + fi +} + +atf_test_case fpe_simple +fpe_simple() +{ + atf_set "descr" "Test unhandled SIGFPE with the right exit code" +} +fpe_simple_body() +{ + fpe_available + atf_check -s signal:8 -o "inline:" -e "inline:" \ + ${HELPER} fpe recurse +} + +atf_test_case fpe_handle +fpe_handle() +{ + atf_set "descr" "Test handled SIGFPE traps call the signal handler" +} +fpe_handle_body() +{ + fpe_available + atf_check -s exit:0 -o "inline:" -e "inline:got 8\n" \ + ${HELPER} fpe handle +} + +atf_test_case fpe_mask +fpe_mask() +{ + atf_set "descr" "Test that masking the trapped SIGFPE signal get reset" +} +fpe_mask_body() +{ + fpe_available + atf_check -s signal:8 -o "inline:" -e "inline:" \ + ${HELPER} fpe mask +} + +atf_test_case fpe_handle_mask +fpe_handle_mask() +{ + atf_set "descr" "Test handled and masked SIGFPE traps get reset" +} +fpe_handle_mask_body() +{ + fpe_available + atf_check -s signal:8 -o "inline:" -e "inline:" \ + ${HELPER} fpe mask handle +} + +atf_test_case fpe_handle_recurse +fpe_handle_recurse() +{ + atf_set "descr" "Test that receiving SIGFPE in the handler resets" +} + +fpe_handle_recurse_body() +{ + fpe_available + atf_check -s signal:8 -o "inline:" -e "inline:got 8\n" \ + ${HELPER} fpe handle recurse +} + +atf_test_case fpe_ignore +fpe_ignore() +{ + atf_set "descr" "Test ignored trap with right exit code" +} + +fpe_ignore_body() +{ + fpe_available + atf_check -s signal:8 -o "inline:" -e "inline:" \ + ${HELPER} fpe ignore +} + +# SIGBUS + +atf_test_case bus_simple +bus_simple() +{ + atf_set "descr" "Test unhandled SIGBUS with the right exit code" +} +bus_simple_body() +{ + atf_check -s signal:10 -o "inline:" -e "inline:" \ + ${HELPER} bus recurse +} + +atf_test_case bus_handle +bus_handle() +{ + atf_set "descr" "Test handled SIGBUS traps call the signal handler" +} +bus_handle_body() +{ + atf_check -s exit:0 -o "inline:" -e "inline:got 10\n" \ + ${HELPER} bus handle +} + +atf_test_case bus_mask +bus_mask() +{ + atf_set "descr" "Test that masking the trapped SIGBUS signal get reset" +} +bus_mask_body() +{ + atf_check -s signal:10 -o "inline:" -e "inline:" \ + ${HELPER} bus mask +} + +atf_test_case bus_handle_mask +bus_handle_mask() +{ + atf_set "descr" "Test handled and masked SIGBUS traps get reset" +} +bus_handle_mask_body() +{ + atf_check -s signal:10 -o "inline:" -e "inline:" \ + ${HELPER} bus mask handle +} + +atf_test_case bus_handle_recurse +bus_handle_recurse() +{ + atf_set "descr" "Test that receiving SIGBUS in the handler resets" +} + +bus_handle_recurse_body() +{ + atf_check -s signal:10 -o "inline:" -e "inline:got 10\n" \ + ${HELPER} bus handle recurse +} + +atf_test_case bus_ignore +bus_ignore() +{ + atf_set "descr" "Test ignored trap with right exit code" +} + +bus_ignore_body() +{ + atf_check -s signal:10 -o "inline:" -e "inline:" \ + ${HELPER} bus ignore +} + +atf_init_test_cases() +{ + atf_add_test_case segv_simple + atf_add_test_case segv_handle + atf_add_test_case segv_mask + atf_add_test_case segv_handle_recurse + atf_add_test_case segv_ignore + + atf_add_test_case trap_simple + atf_add_test_case trap_handle + atf_add_test_case trap_mask + atf_add_test_case trap_handle_recurse + atf_add_test_case trap_ignore + +# atf_add_test_case ill_simple +# atf_add_test_case ill_handle +# atf_add_test_case ill_mask +# atf_add_test_case ill_handle_recurse +# atf_add_test_case ill_ignore + + atf_add_test_case fpe_simple + atf_add_test_case fpe_handle + atf_add_test_case fpe_mask + atf_add_test_case fpe_handle_recurse + atf_add_test_case fpe_ignore + + atf_add_test_case bus_simple + atf_add_test_case bus_handle + atf_add_test_case bus_mask + atf_add_test_case bus_handle_recurse + atf_add_test_case bus_ignore +} diff --git a/kernel/t_umount.sh b/kernel/t_umount.sh old mode 100755 new mode 100644 diff --git a/kernel/t_umountstress.sh b/kernel/t_umountstress.sh old mode 100755 new mode 100644 diff --git a/kernel/t_zombie.c b/kernel/t_zombie.c new file mode 100644 --- /dev/null +++ b/kernel/t_zombie.c @@ -0,0 +1,276 @@ +/* $NetBSD: t_zombie.c,v 1.2 2018/05/18 00:25:30 kamil Exp $ */ + +/*- + * Copyright (c) 2018 The NetBSD Foundation, Inc. + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +__COPYRIGHT("@(#) Copyright (c) 2018\ + The NetBSD Foundation, inc. All rights reserved."); +__RCSID("$NetBSD: t_zombie.c,v 1.2 2018/05/18 00:25:30 kamil Exp $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static int debug = 0; + +#define DPRINTF(a, ...) \ +do { \ + if (debug) printf(a, ##__VA_ARGS__); \ +} while (/*CONSTCOND*/0) + +/* + * A child process cannot call atf functions and expect them to magically + * work like in the parent. + * The printf(3) messaging from a child will not work out of the box as well + * without estabilishing a communication protocol with its parent. To not + * overcomplicate the tests - do not log from a child and use err(3)/errx(3) + * wrapped with ASSERT_EQ()/ASSERT_NEQ() as that is guaranteed to work. + */ +#define ASSERT_EQ(x, y) \ +do { \ + uintmax_t vx = (x); \ + uintmax_t vy = (y); \ + int ret = vx == vy; \ + if (!ret) \ + errx(EXIT_FAILURE, "%s:%d %s(): Assertion failed for: " \ + "%s(%ju) == %s(%ju)", __FILE__, __LINE__, __func__, \ + #x, vx, #y, vy); \ +} while (/*CONSTCOND*/0) + +#define ASSERT_NEQ(x, y) \ +do { \ + uintmax_t vx = (x); \ + uintmax_t vy = (y); \ + int ret = vx != vy; \ + if (!ret) \ + errx(EXIT_FAILURE, "%s:%d %s(): Assertion failed for: " \ + "%s(%ju) != %s(%ju)", __FILE__, __LINE__, __func__, \ + #x, vx, #y, vy); \ +} while (/*CONSTCOND*/0) + +#define ASSERT(x) \ +do { \ + int ret = (x); \ + if (!ret) \ + errx(EXIT_FAILURE, "%s:%d %s(): Assertion failed for: %s", \ + __FILE__, __LINE__, __func__, #x); \ +} while (/*CONSTCOND*/0) + +static bool +check_zombie(pid_t process) +{ + struct kinfo_proc2 p; + size_t len = sizeof(p); + + const int name[] = { + [0] = CTL_KERN, + [1] = KERN_PROC2, + [2] = KERN_PROC_PID, + [3] = process, + [4] = sizeof(p), + [5] = 1 + }; + + const size_t namelen = __arraycount(name); + + ASSERT_EQ(sysctl(name, namelen, &p, &len, NULL, 0), 0); + + return (p.p_stat == LSZOMB); +} + +static void __used +await_zombie(pid_t process) +{ + + /* Await the process becoming a zombie */ + while (!check_zombie(process)) { + ASSERT_EQ(usleep(100), 0); + } +} + +static void +signal_raw(int sig) +{ + int status; + pid_t child1, child2, pid; + + child1 = atf_utils_fork(); + ATF_REQUIRE(child1 != -1); + if (child1 == 0) { + /* Just die and turn into a zombie */ + _exit(0); + } + + child2 = atf_utils_fork(); + ATF_REQUIRE(child2 != -1); + if (child2 == 0) { + await_zombie(child1); + + /* + * zombie does not process signals + * POSIX requires that zombie does not set errno ESRCH + * return value of kill() for a zombie is not specified + * + * Try to emit a signal towards it from an unrelated process. + */ + errno = 0; + kill(child1, sig); + ASSERT_NEQ(errno, ESRCH); + + /* A zombie is still a zombie waiting for collecting */ + ASSERT(check_zombie(child1)); + + _exit(0); + } + + pid = waitpid(child2, &status, WEXITED); + ATF_REQUIRE_EQ(pid, child2); + ATF_REQUIRE(WIFEXITED(status)); + ATF_REQUIRE(!WIFCONTINUED(status)); + ATF_REQUIRE(!WIFSIGNALED(status)); + ATF_REQUIRE(!WIFSTOPPED(status)); + ATF_REQUIRE_EQ(WEXITSTATUS(status), 0); + + /* Assert that child1 is still a zombie after collecting child2 */ + ATF_REQUIRE(check_zombie(child1)); + + /* + * zombie does not process signals + * POSIX requires that zombie does not set errno ESRCH + * return value of kill() for a zombie is not specified + * + * Try to emit a signal towards it from the parent. + */ + errno = 0; + kill(child1, sig); + // ATF_CHECK_NEQ not available + ASSERT_NEQ(errno, ESRCH); + + /* Assert that child1 is still a zombie after emitting a signal */ + ATF_REQUIRE(check_zombie(child1)); + + pid = waitpid(child1, &status, WEXITED); + ATF_REQUIRE_EQ(pid, child1); + ATF_REQUIRE(WIFEXITED(status)); + ATF_REQUIRE(!WIFCONTINUED(status)); + ATF_REQUIRE(!WIFSIGNALED(status)); + ATF_REQUIRE(!WIFSTOPPED(status)); + ATF_REQUIRE_EQ(WEXITSTATUS(status), 0); +} + +#define KILLABLE(test, sig) \ +ATF_TC(test); \ +ATF_TC_HEAD(test, tc) \ +{ \ + \ + atf_tc_set_md_var(tc, "descr", \ + "process is not killable with " #sig); \ +} \ + \ +ATF_TC_BODY(test, tc) \ +{ \ + \ + signal_raw(sig); \ +} + +KILLABLE(signal1, SIGKILL) /* non-maskable */ +KILLABLE(signal2, SIGSTOP) /* non-maskable */ +KILLABLE(signal3, SIGABRT) /* regular abort trap */ +KILLABLE(signal4, SIGHUP) /* hangup */ +KILLABLE(signal5, SIGCONT) /* continued? */ + +ATF_TC(race1); +ATF_TC_HEAD(race1, tc) +{ + + atf_tc_set_md_var(tc, "descr", + "check if there are any races with sending signals, killing and " + "lookup of a zombie"); +} + +ATF_TC_BODY(race1, tc) +{ + time_t start, end; + double diff; + unsigned long N = 0; + int sig; + + /* + * Assert that a dying process can be correctly looked up + * with sysctl(3) kern.proc and operation KERN_PROC_PID. + * + * This test has been inspired by a bug fixed in + * sys/kern/kern_proc.c 1.211 + * "Make sysctl_doeproc() more predictable" + */ + + start = time(NULL); + while (true) { + /* + * A signal number does not matter, but it does not harm to + * randomize it. + * + * Skip signal 0 as sending to it to a zombie is not + * defined in POSIX, and explicitly discouraged. + */ + sig = 1 + arc4random_uniform(NSIG - 2); + + DPRINTF("Step: %lu (signal: %s)\n", N, signalname(sig)); + + signal_raw(sig); + end = time(NULL); + diff = difftime(end, start); + if (diff >= 5.0) + break; + ++N; + } + DPRINTF("Iterations: %lu\n", N); +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, signal1); + ATF_TP_ADD_TC(tp, signal2); + ATF_TP_ADD_TC(tp, signal3); + ATF_TP_ADD_TC(tp, signal4); + ATF_TP_ADD_TC(tp, signal5); + + ATF_TP_ADD_TC(tp, race1); + + return atf_no_error(); +} diff --git a/kernel/threadpool_tester/Makefile b/kernel/threadpool_tester/Makefile new file mode 100644 --- /dev/null +++ b/kernel/threadpool_tester/Makefile @@ -0,0 +1,14 @@ +# $NetBSD: Makefile,v 1.1 2018/12/24 16:58:54 thorpej Exp $ + +.include + +KMOD= threadpool_tester +KMODULEDIR= ${DESTDIR}/${TESTSBASE}/kernel/${KMOD} + +SRCS= threadpool_tester.c + +ATFFILE= no +NOMAN= # defined + +.include +.include diff --git a/kernel/threadpool_tester/threadpool_tester.c b/kernel/threadpool_tester/threadpool_tester.c new file mode 100644 --- /dev/null +++ b/kernel/threadpool_tester/threadpool_tester.c @@ -0,0 +1,502 @@ +/* $NetBSD: threadpool_tester.c,v 1.3 2018/12/26 22:21:10 thorpej Exp $ */ + +/*- + * Copyright (c) 2018 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +__KERNEL_RCSID(0, "$NetBSD: threadpool_tester.c,v 1.3 2018/12/26 22:21:10 thorpej Exp $"); + +#include +#include +#include +#include +#include + +MODULE(MODULE_CLASS_MISC, threadpool_tester, NULL); + +#ifdef THREADPOOL_VERBOSE +#define TP_LOG(x) printf x +#else +#define TP_LOG(x) /* nothing */ +#endif /* THREADPOOL_VERBOSE */ + +static struct tester_context { + kmutex_t ctx_mutex; + struct sysctllog *ctx_sysctllog; + struct threadpool *ctx_unbound[PRI_COUNT + 1]; + struct threadpool_percpu *ctx_percpu[PRI_COUNT + 1]; + unsigned int ctx_value; + struct threadpool_job ctx_job; +} tester_ctx; + +#define pri_to_idx(pri) ((pri) == PRI_NONE ? PRI_COUNT : (pri)) + +static bool +pri_is_valid(pri_t pri) +{ + return (pri == PRI_NONE || (pri >= PRI_USER && pri < PRI_COUNT)); +} + +static int +threadpool_tester_get_unbound(SYSCTLFN_ARGS) +{ + struct tester_context *ctx; + struct threadpool *pool, *opool = NULL; + struct sysctlnode node; + int error, val; + + node = *rnode; + ctx = node.sysctl_data; + + val = -1; + node.sysctl_data = &val; + error = sysctl_lookup(SYSCTLFN_CALL(&node)); + if (error || newp == NULL) + return error; + + if (! pri_is_valid(val)) + return EINVAL; + + error = threadpool_get(&pool, val); + if (error) { + TP_LOG(("%s: threadpool_get(..., %d) failed -> %d\n", + __func__, val, error)); + return error; + } + + mutex_enter(&ctx->ctx_mutex); + if (ctx->ctx_unbound[pri_to_idx(val)] == NULL) + ctx->ctx_unbound[pri_to_idx(val)] = pool; + else + opool = ctx->ctx_unbound[pri_to_idx(val)]; + mutex_exit(&ctx->ctx_mutex); + + if (opool != NULL) { + /* Should have gotten reference to existing pool. */ + TP_LOG(("%s: found existing unbound pool for pri %d (%s)\n", + __func__, val, opool == pool ? "match" : "NO MATCH")); + KASSERT(opool == pool); + threadpool_put(pool, val); + error = EEXIST; + } else { + TP_LOG(("%s: created unbound pool for pri %d\n", + __func__, val)); + } + + return error; +} + +static int +threadpool_tester_put_unbound(SYSCTLFN_ARGS) +{ + struct tester_context *ctx; + struct threadpool *pool; + struct sysctlnode node; + int error, val; + + node = *rnode; + ctx = node.sysctl_data; + + val = -1; + node.sysctl_data = &val; + error = sysctl_lookup(SYSCTLFN_CALL(&node)); + if (error || newp == NULL) + return error; + + if (! pri_is_valid(val)) + return EINVAL; + + mutex_enter(&ctx->ctx_mutex); + /* We only ever maintain a single reference. */ + pool = ctx->ctx_unbound[pri_to_idx(val)]; + ctx->ctx_unbound[pri_to_idx(val)] = NULL; + mutex_exit(&ctx->ctx_mutex); + + if (pool == NULL) { + TP_LOG(("%s: no unbound pool for pri %d\n", + __func__, val)); + return ENODEV; + } + + threadpool_put(pool, val); + TP_LOG(("%s: released unbound pool for pri %d\n", + __func__, val)); + + return 0; +} + +static int +threadpool_tester_run_unbound(SYSCTLFN_ARGS) +{ + struct tester_context *ctx; + struct threadpool *pool; + struct sysctlnode node; + int error, val; + + node = *rnode; + ctx = node.sysctl_data; + + val = -1; + node.sysctl_data = &val; + error = sysctl_lookup(SYSCTLFN_CALL(&node)); + if (error || newp == NULL) + return error; + + if (! pri_is_valid(val)) + return EINVAL; + + mutex_enter(&ctx->ctx_mutex); + pool = ctx->ctx_unbound[pri_to_idx(val)]; + if (pool == NULL) { + TP_LOG(("%s: no unbound pool for pri %d\n", + __func__, val)); + mutex_exit(&ctx->ctx_mutex); + return ENODEV; + } + + threadpool_schedule_job(pool, &ctx->ctx_job); + TP_LOG(("%s: scheduled job on unbound pool for pri %d\n", + __func__, val)); + mutex_exit(&ctx->ctx_mutex); + + return 0; +} + +static int +threadpool_tester_get_percpu(SYSCTLFN_ARGS) +{ + struct tester_context *ctx; + struct threadpool_percpu *pcpu, *opcpu = NULL; + struct sysctlnode node; + int error, val; + + node = *rnode; + ctx = node.sysctl_data; + + val = -1; + node.sysctl_data = &val; + error = sysctl_lookup(SYSCTLFN_CALL(&node)); + if (error || newp == NULL) + return error; + + if (! pri_is_valid(val)) + return EINVAL; + + error = threadpool_percpu_get(&pcpu, val); + if (error) { + TP_LOG(("%s: threadpool_percpu_get(..., %d) failed -> %d\n", + __func__, val, error)); + return error; + } + + mutex_enter(&ctx->ctx_mutex); + if (ctx->ctx_percpu[pri_to_idx(val)] == NULL) + ctx->ctx_percpu[pri_to_idx(val)] = pcpu; + else + opcpu = ctx->ctx_percpu[pri_to_idx(val)]; + mutex_exit(&ctx->ctx_mutex); + + if (opcpu != NULL) { + /* Should have gotten reference to existing pool. */ + TP_LOG(("%s: found existing unbound pool for pri %d (%s)\n", + __func__, val, opcpu == pcpu ? "match" : "NO MATCH")); + KASSERT(opcpu == pcpu); + threadpool_percpu_put(pcpu, val); + error = EEXIST; + } else { + TP_LOG(("%s: created percpu pool for pri %d\n", + __func__, val)); + } + + return error; +} + +static int +threadpool_tester_put_percpu(SYSCTLFN_ARGS) +{ + struct tester_context *ctx; + struct threadpool_percpu *pcpu; + struct sysctlnode node; + int error, val; + + node = *rnode; + ctx = node.sysctl_data; + + val = -1; + node.sysctl_data = &val; + error = sysctl_lookup(SYSCTLFN_CALL(&node)); + if (error || newp == NULL) + return error; + + if (! pri_is_valid(val)) + return EINVAL; + + mutex_enter(&ctx->ctx_mutex); + /* We only ever maintain a single reference. */ + pcpu = ctx->ctx_percpu[pri_to_idx(val)]; + ctx->ctx_percpu[pri_to_idx(val)] = NULL; + mutex_exit(&ctx->ctx_mutex); + + if (pcpu == NULL) { + TP_LOG(("%s: no percpu pool for pri %d\n", + __func__, val)); + return ENODEV; + } + + threadpool_percpu_put(pcpu, val); + TP_LOG(("%s: released percpu pool for pri %d\n", + __func__, val)); + + return 0; +} + +static int +threadpool_tester_run_percpu(SYSCTLFN_ARGS) +{ + struct tester_context *ctx; + struct threadpool_percpu *pcpu; + struct threadpool *pool; + struct sysctlnode node; + int error, val; + + node = *rnode; + ctx = node.sysctl_data; + + val = -1; + node.sysctl_data = &val; + error = sysctl_lookup(SYSCTLFN_CALL(&node)); + if (error || newp == NULL) + return error; + + if (! pri_is_valid(val)) + return EINVAL; + + mutex_enter(&ctx->ctx_mutex); + pcpu = ctx->ctx_percpu[pri_to_idx(val)]; + if (pcpu == NULL) { + TP_LOG(("%s: no percpu pool for pri %d\n", + __func__, val)); + mutex_exit(&ctx->ctx_mutex); + return ENODEV; + } + + pool = threadpool_percpu_ref(pcpu); + KASSERT(pool != NULL); + + threadpool_schedule_job(pool, &ctx->ctx_job); + TP_LOG(("%s: scheduled job on percpu pool for pri %d\n", + __func__, val)); + mutex_exit(&ctx->ctx_mutex); + + return 0; +} + +static int +threadpool_tester_test_value(SYSCTLFN_ARGS) +{ + struct tester_context *ctx; + struct sysctlnode node; + unsigned int val; + int error; + + node = *rnode; + ctx = node.sysctl_data; + + mutex_enter(&ctx->ctx_mutex); + val = ctx->ctx_value; + node.sysctl_data = &val; + error = sysctl_lookup(SYSCTLFN_CALL(&node)); + if (error || newp == NULL) { + mutex_exit(&ctx->ctx_mutex); + return error; + } + ctx->ctx_value = val; + mutex_exit(&ctx->ctx_mutex); + + return 0; +} + +static void +threadpool_tester_job(struct threadpool_job *job) +{ + struct tester_context *ctx = + container_of(job, struct tester_context, ctx_job); + unsigned int oval, nval; + + TP_LOG(("%s: job = %p, ctx = %p\n", __func__, job, ctx)); + + mutex_enter(&ctx->ctx_mutex); + oval = ctx->ctx_value; + nval = oval + 1; /* always reference oval and nval */ + ctx->ctx_value = nval; + mutex_exit(&ctx->ctx_mutex); + + TP_LOG(("%s: %u -> %u\n", __func__, oval, nval)); + (void) kpause("tptestjob", false, hz, NULL); + + mutex_enter(&ctx->ctx_mutex); + threadpool_job_done(job); + mutex_exit(&ctx->ctx_mutex); +} + +#define RETURN_ERROR if (error) goto return_error + +static int +threadpool_tester_init(void) +{ + struct sysctllog **log = &tester_ctx.ctx_sysctllog; + const struct sysctlnode *rnode, *cnode; + int error; + + mutex_init(&tester_ctx.ctx_mutex, MUTEX_DEFAULT, IPL_NONE); + threadpool_job_init(&tester_ctx.ctx_job, threadpool_tester_job, + &tester_ctx.ctx_mutex, "tptest"); + + error = sysctl_createv(log, 0, NULL, &rnode, CTLFLAG_PERMANENT, + CTLTYPE_NODE, "threadpool_tester", + SYSCTL_DESCR("threadpool testing interface"), + NULL, 0, NULL, 0, CTL_KERN, CTL_CREATE, CTL_EOL); + RETURN_ERROR; + + error = sysctl_createv(log, 0, &rnode, &cnode, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT, "get_unbound", + SYSCTL_DESCR("get unbound pool of specified priority"), + threadpool_tester_get_unbound, 0, + (void *)&tester_ctx, 0, CTL_CREATE, CTL_EOL); + RETURN_ERROR; + + error = sysctl_createv(log, 0, &rnode, &cnode, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT, "put_unbound", + SYSCTL_DESCR("put unbound pool of specified priority"), + threadpool_tester_put_unbound, 0, + (void *)&tester_ctx, 0, CTL_CREATE, CTL_EOL); + RETURN_ERROR; + + error = sysctl_createv(log, 0, &rnode, &cnode, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT, "run_unbound", + SYSCTL_DESCR("run on unbound pool of specified priority"), + threadpool_tester_run_unbound, 0, + (void *)&tester_ctx, 0, CTL_CREATE, CTL_EOL); + RETURN_ERROR; + + error = sysctl_createv(log, 0, &rnode, &cnode, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT, "get_percpu", + SYSCTL_DESCR("get percpu pool of specified priority"), + threadpool_tester_get_percpu, 0, + (void *)&tester_ctx, 0, CTL_CREATE, CTL_EOL); + RETURN_ERROR; + + error = sysctl_createv(log, 0, &rnode, &cnode, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT, "put_percpu", + SYSCTL_DESCR("put percpu pool of specified priority"), + threadpool_tester_put_percpu, 0, + (void *)&tester_ctx, 0, CTL_CREATE, CTL_EOL); + RETURN_ERROR; + + error = sysctl_createv(log, 0, &rnode, &cnode, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT, "run_percpu", + SYSCTL_DESCR("run on percpu pool of specified priority"), + threadpool_tester_run_percpu, 0, + (void *)&tester_ctx, 0, CTL_CREATE, CTL_EOL); + RETURN_ERROR; + + error = sysctl_createv(log, 0, &rnode, &cnode, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT, "test_value", + SYSCTL_DESCR("test value that jobs increment"), + threadpool_tester_test_value, 0, + (void *)&tester_ctx, 0, CTL_CREATE, CTL_EOL); + RETURN_ERROR; + + return 0; + + return_error: + sysctl_teardown(log); + return error; +} + +static int +threadpool_tester_fini(void) +{ + pri_t pri; + + mutex_enter(&tester_ctx.ctx_mutex); + for (pri = PRI_NONE/*-1*/; pri < PRI_COUNT; pri++) { + struct threadpool *pool = + tester_ctx.ctx_unbound[pri_to_idx(pri)]; + struct threadpool_percpu *pcpu = + tester_ctx.ctx_percpu[pri_to_idx(pri)]; + + /* + * threadpool_cancel_job() may be called on a pool + * other than what the job is scheduled on. This is + * safe; see comment in threadpool_cancel_job_async(). + */ + + if (pool != NULL) { + threadpool_cancel_job(pool, &tester_ctx.ctx_job); + threadpool_put(pool, pri); + tester_ctx.ctx_unbound[pri_to_idx(pri)] = NULL; + } + if (pcpu != NULL) { + pool = threadpool_percpu_ref(pcpu); + threadpool_cancel_job(pool, &tester_ctx.ctx_job); + threadpool_percpu_put(pcpu, pri); + tester_ctx.ctx_percpu[pri_to_idx(pri)] = NULL; + } + } + mutex_exit(&tester_ctx.ctx_mutex); + threadpool_job_destroy(&tester_ctx.ctx_job); + mutex_destroy(&tester_ctx.ctx_mutex); + + sysctl_teardown(&tester_ctx.ctx_sysctllog); + + return 0; +} + +static int +threadpool_tester_modcmd(modcmd_t cmd, void *arg __unused) +{ + int error; + + switch (cmd) { + case MODULE_CMD_INIT: + error = threadpool_tester_init(); + break; + + case MODULE_CMD_FINI: + error = threadpool_tester_fini(); + break; + + case MODULE_CMD_STAT: + default: + error = ENOTTY; + } + + return error; +} diff --git a/kernel/tty/Makefile b/kernel/tty/Makefile --- a/kernel/tty/Makefile +++ b/kernel/tty/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.3 2016/01/23 21:22:49 christos Exp $ +# $NetBSD: Makefile,v 1.4 2020/03/01 18:08:15 christos Exp $ .include @@ -7,8 +7,6 @@ TESTS_C= t_pr CPPFLAGS+= -D_KERNTYPES -LDADD+= -lrumpkern_tty -lrumpvfs -lrump -lrumpuser -LDADD+= -lrump -LDADD+= -lpthread +LDADD+= -lrumpkern_tty ${LIBRUMPBASE} .include diff --git a/lib/Makefile b/lib/Makefile --- a/lib/Makefile +++ b/lib/Makefile @@ -1,19 +1,19 @@ -# $NetBSD: Makefile,v 1.27 2016/11/16 21:36:23 kamil Exp $ +# $NetBSD: Makefile,v 1.34 2020/12/11 12:29:27 nia Exp $ .include -TESTS_SUBDIRS= csu libbluetooth libc libcrypt libcurses libevent libexecinfo \ - libm libobjc libposix libppath libprop libpthread \ - libpthread_dbg librefuse librt libtre libusbhid libutil \ - semaphore +TESTS_SUBDIRS= csu libarchive libbluetooth libc libcrypt libcurses \ + libexecinfo libi386 libm libnvmm libobjc libposix libppath \ + libprop libpthread librefuse librt libtre libusbhid libutil \ + libossaudio semaphore + +TESTS_SUBDIR_INSTALL_ONLY= libevent .if (${MKRUMP} != "no") && !defined(BSD_MK_COMPAT_FILE) TESTS_SUBDIRS+= librumpclient librumphijack .endif -.if ${MKCRYPTO} != "no" TESTS_SUBDIRS+= libdes -.endif .if ${MKKYUA} != "no" # These tests come from external/bsd/lutok/tests/lib/liblutok. Ensure they diff --git a/lib/csu/Makefile b/lib/csu/Makefile --- a/lib/csu/Makefile +++ b/lib/csu/Makefile @@ -1,11 +1,11 @@ -# $NetBSD: Makefile,v 1.5 2016/04/04 09:52:02 joerg Exp $ +# $NetBSD: Makefile,v 1.7 2018/12/27 19:33:52 christos Exp $ NOMAN= # defined .include TESTSDIR= ${TESTSBASE}/lib/csu -TESTS_SH= t_crt0 +TESTS_SH= t_crt0 t_ifunc_static TESTS_SUBDIRS= SUBDIR+= dso @@ -24,4 +24,9 @@ ${SRCS_CHECK_STACK} LDADD.h_initfini3+= -Wl,-rpath,${TESTSDIR} +PROGS+= h_ifunc_static h_initfini_array +SRCS.h_ifunc_static= h_ifunc_static.c +SRCS.h_initfini_array= h_initfini_array.c +LDSTATIC.h_ifunc_static=-static + .include diff --git a/lib/csu/arch/powerpc/h_initfini_align.S b/lib/csu/arch/powerpc/h_initfini_align.S --- a/lib/csu/arch/powerpc/h_initfini_align.S +++ b/lib/csu/arch/powerpc/h_initfini_align.S @@ -1,8 +1,8 @@ -/* $NetBSD: h_initfini_align.S,v 1.1 2013/12/11 17:31:56 matt Exp $ */ +/* $NetBSD: h_initfini_align.S,v 1.2 2017/02/27 20:22:32 chs Exp $ */ #include -RCSID("$NetBSD: h_initfini_align.S,v 1.1 2013/12/11 17:31:56 matt Exp $") +RCSID("$NetBSD: h_initfini_align.S,v 1.2 2017/02/27 20:22:32 chs Exp $") /* * LINTSTUB: bool check_stack_alignment(void); @@ -10,7 +10,7 @@ _ENTRY(check_stack_alignment) li %r3,0 - andis. %r0,%r1,15 + andi. %r0,%r1,15 bnelr %cr0 li %r3,1 blr diff --git a/lib/csu/h_ifunc_static.c b/lib/csu/h_ifunc_static.c new file mode 100644 --- /dev/null +++ b/lib/csu/h_ifunc_static.c @@ -0,0 +1,72 @@ +/* $NetBSD: h_ifunc_static.c,v 1.4 2021/06/07 17:11:17 christos Exp $ */ + +/*- + * Copyright (c) 2018 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Joerg Sonnenberger. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +#if defined(__i386__) || defined(__x86_64__) || defined(__powerpc__) || defined(__sparc__) +#include +#include + +static long long +ifunc1(void) +{ + return 0xdeadbeefll; +} + +static long long +ifunc2(void) +{ + return 0xbeefdeadll; +} + +static __attribute__((used)) +long long (*resolve_ifunc(void))(void) +{ + const char *e = getenv("USE_IFUNC2"); + return e && strcmp(e, "1") == 0 ? ifunc2 : ifunc1; +} + +__ifunc(ifunc, resolve_ifunc); +extern long long ifunc(void); + +int +main(int argc, char **argv) +{ + + if (argc != 2) + return 1; + return atoll(argv[1]) != ifunc(); +} +#else +int +main(int argc, char **argv) +{ + return 127; +} +#endif diff --git a/lib/csu/h_initfini_array.c b/lib/csu/h_initfini_array.c new file mode 100644 --- /dev/null +++ b/lib/csu/h_initfini_array.c @@ -0,0 +1,16 @@ +static int x = 1; + +static void +foo(void) +{ + x = 0; +} + +static void (*fp) (void) __attribute__((__section__(".init_array"), __used__)) = + foo; + +int +main(void) +{ + return x; +} diff --git a/lib/csu/t_crt0.sh b/lib/csu/t_crt0.sh old mode 100755 new mode 100644 --- a/lib/csu/t_crt0.sh +++ b/lib/csu/t_crt0.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_crt0.sh,v 1.4 2011/12/11 14:57:07 joerg Exp $ +# $NetBSD: t_crt0.sh,v 1.5 2018/12/27 19:33:52 christos Exp $ # # Copyright (c) 2008 The NetBSD Foundation, Inc. # All rights reserved. @@ -95,10 +95,21 @@ atf_check -o file:expout -x "env LD_PRELOAD=$(atf_get_srcdir)/h_initfini3_dso.so $(atf_get_srcdir)/h_initfini1" } +atf_test_case initfini_array +initfini_array_head() +{ + atf_set "descr" "Checks support for init_array/fini_array sections" +} +initfini_array_body() +{ + atf_check -x "$(atf_get_srcdir)/h_initfini_array" +} + atf_init_test_cases() { atf_add_test_case initfini1 atf_add_test_case initfini2 atf_add_test_case initfini3 atf_add_test_case initfini4 + atf_add_test_case initfini_array } diff --git a/lib/csu/t_ifunc_static.sh b/lib/csu/t_ifunc_static.sh new file mode 100644 --- /dev/null +++ b/lib/csu/t_ifunc_static.sh @@ -0,0 +1,56 @@ +# $NetBSD: t_ifunc_static.sh,v 1.2 2019/01/30 12:42:53 martin Exp $ +# +# Copyright (c) 2018 The NetBSD Foundation, Inc. +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +atf_test_case ifunc_static +ifunc_static_head() +{ + atf_set "descr" "Checks support for ifunc relocations in static binaries" +} +ifunc_static_body() +{ + case `uname -p` in + i386|x86_64|powerpc|*sparc*|*arm*) + ;; + *) + atf_skip "ifunc is supposed only on ARM, i386, PowerPC, SPARC and x86-64" + ;; + esac + + USE_IFUNC2=0 "$(atf_get_srcdir)/h_ifunc_static" 3735928559 + atf_check_equal $? 0 + USE_IFUNC2=0 "$(atf_get_srcdir)/h_ifunc_static" 3203391149 + atf_check_equal $? 1 + USE_IFUNC2=1 "$(atf_get_srcdir)/h_ifunc_static" 3735928559 + atf_check_equal $? 1 + USE_IFUNC2=1 "$(atf_get_srcdir)/h_ifunc_static" 3203391149 + atf_check_equal $? 0 +} + +atf_init_test_cases() +{ + atf_add_test_case ifunc_static +} diff --git a/lib/libarchive/Makefile b/lib/libarchive/Makefile new file mode 100644 --- /dev/null +++ b/lib/libarchive/Makefile @@ -0,0 +1,613 @@ +# $NetBSD: Makefile,v 1.5 2020/06/07 23:20:52 fox Exp $ + +NOMAN= + +.include +LIBARCHIVE=${NETBSDSRCDIR}/external/bsd/libarchive/dist + +TESTSDIR= ${TESTSBASE}/lib/libarchive + +BINDIR= ${TESTSDIR} +PROGS+= h_libarchive +TESTS_SH+= t_libarchive + +CPPFLAGS+=-I${LIBARCHIVE}/test_utils -I${LIBARCHIVE}/libarchive -I. +CPPFLAGS+=-I${LIBARCHIVE}/libarchive/test -I${LIBARCHIVE}/../include + +.PATH: ${LIBARCHIVE}/libarchive/test ${LIBARCHIVE}/test_utils + +DPADD+= ${LIBARCHIVE} ${LIBEXPAT} ${LIBBZ2} ${LIBLZMA} ${LIBZ} \ + ${LIBCRYPTO} ${LIBPTHREAD} +LDADD+= -larchive -lexpat -lbz2 -llzma -lz -lcrypto -lpthread + +SRCS.h_libarchive= \ +test_main.c \ +test_utils.c \ +read_open_memory.c \ +test_acl_nfs4.c \ +test_acl_pax.c \ +test_acl_platform_nfs4.c \ +test_acl_platform_posix1e.c \ +test_acl_posix1e.c \ +test_acl_text.c \ +test_archive_api_feature.c \ +test_archive_clear_error.c \ +test_archive_cmdline.c \ +test_archive_digest.c \ +test_archive_getdate.c \ +test_archive_match_owner.c \ +test_archive_match_path.c \ +test_archive_match_time.c \ +test_archive_pathmatch.c \ +test_archive_read_add_passphrase.c \ +test_archive_read_close_twice.c \ +test_archive_read_close_twice_open_fd.c \ +test_archive_read_close_twice_open_filename.c \ +test_archive_read_multiple_data_objects.c \ +test_archive_read_next_header_empty.c \ +test_archive_read_next_header_raw.c \ +test_archive_read_open2.c \ +test_archive_read_set_filter_option.c \ +test_archive_read_set_format_option.c \ +test_archive_read_set_option.c \ +test_archive_read_set_options.c \ +test_archive_read_support.c \ +test_archive_set_error.c \ +test_archive_string.c \ +test_archive_string_conversion.c \ +test_archive_write_add_filter_by_name.c \ +test_archive_write_set_filter_option.c \ +test_archive_write_set_format_by_name.c \ +test_archive_write_set_format_filter_by_ext.c \ +test_archive_write_set_format_option.c \ +test_archive_write_set_option.c \ +test_archive_write_set_options.c \ +test_archive_write_set_passphrase.c \ +test_bad_fd.c \ +test_compat_bzip2.c \ +test_compat_cpio.c \ +test_compat_gtar.c \ +test_compat_gzip.c \ +test_compat_lz4.c \ +test_compat_lzip.c \ +test_compat_lzma.c \ +test_compat_lzop.c \ +test_compat_mac.c \ +test_compat_pax_libarchive_2x.c \ +test_compat_perl_archive_tar.c \ +test_compat_plexus_archiver_tar.c \ +test_compat_solaris_pax_sparse.c \ +test_compat_solaris_tar_acl.c \ +test_compat_star_acl.c \ +test_compat_tar_hardlink.c \ +test_compat_uudecode.c \ +test_compat_uudecode_large.c \ +test_compat_xz.c \ +test_compat_zip.c \ +test_compat_zstd.c \ +test_empty_write.c \ +test_entry.c \ +test_entry_strmode.c \ +test_extattr_freebsd.c \ +test_filter_count.c \ +test_fuzz.c \ +test_gnutar_filename_encoding.c \ +test_link_resolver.c \ +test_open_failure.c \ +test_open_fd.c \ +test_open_file.c \ +test_open_filename.c \ +test_pax_filename_encoding.c \ +test_read_data_large.c \ +test_read_disk.c \ +test_read_disk_directory_traversals.c \ +test_read_disk_entry_from_file.c \ +test_read_extract.c \ +test_read_file_nonexistent.c \ +test_read_filter_compress.c \ +test_read_filter_grzip.c \ +test_read_filter_lrzip.c \ +test_read_filter_lzop.c \ +test_read_filter_lzop_multiple_parts.c \ +test_read_filter_program.c \ +test_read_filter_program_signature.c \ +test_read_filter_uudecode.c \ +test_read_format_7zip.c \ +test_read_format_7zip_encryption_data.c \ +test_read_format_7zip_encryption_header.c \ +test_read_format_7zip_encryption_partially.c \ +test_read_format_7zip_malformed.c \ +test_read_format_ar.c \ +test_read_format_cab.c \ +test_read_format_cab_filename.c \ +test_read_format_cpio_afio.c \ +test_read_format_cpio_bin.c \ +test_read_format_cpio_bin_Z.c \ +test_read_format_cpio_bin_be.c \ +test_read_format_cpio_bin_bz2.c \ +test_read_format_cpio_bin_gz.c \ +test_read_format_cpio_bin_le.c \ +test_read_format_cpio_bin_lzip.c \ +test_read_format_cpio_bin_lzma.c \ +test_read_format_cpio_bin_xz.c \ +test_read_format_cpio_filename.c \ +test_read_format_cpio_odc.c \ +test_read_format_cpio_svr4_bzip2_rpm.c \ +test_read_format_cpio_svr4_gzip.c \ +test_read_format_cpio_svr4_gzip_rpm.c \ +test_read_format_cpio_svr4c_Z.c \ +test_read_format_empty.c \ +test_read_format_gtar_filename.c \ +test_read_format_gtar_gz.c \ +test_read_format_gtar_lzma.c \ +test_read_format_gtar_sparse.c \ +test_read_format_gtar_sparse_skip_entry.c \ +test_read_format_iso_Z.c \ +test_read_format_iso_multi_extent.c \ +test_read_format_iso_xorriso.c \ +test_read_format_isojoliet_bz2.c \ +test_read_format_isojoliet_long.c \ +test_read_format_isojoliet_rr.c \ +test_read_format_isojoliet_versioned.c \ +test_read_format_isorr_bz2.c \ +test_read_format_isorr_ce.c \ +test_read_format_isorr_new_bz2.c \ +test_read_format_isorr_rr_moved.c \ +test_read_format_isozisofs_bz2.c \ +test_read_format_lha.c \ +test_read_format_lha_bugfix_0.c \ +test_read_format_lha_filename.c \ +test_read_format_mtree.c \ +test_read_format_mtree_crash747.c \ +test_read_format_pax_bz2.c \ +test_read_format_rar.c \ +test_read_format_rar5.c \ +test_read_format_rar_encryption_data.c \ +test_read_format_rar_encryption_header.c \ +test_read_format_rar_encryption_partially.c \ +test_read_format_rar_invalid1.c \ +test_read_format_raw.c \ +test_read_format_tar.c \ +test_read_format_tar_concatenated.c \ +test_read_format_tar_empty_filename.c \ +test_read_format_tar_empty_pax.c \ +test_read_format_tar_empty_with_gnulabel.c \ +test_read_format_tar_filename.c \ +test_read_format_tbz.c \ +test_read_format_tgz.c \ +test_read_format_tlz.c \ +test_read_format_txz.c \ +test_read_format_tz.c \ +test_read_format_ustar_filename.c \ +test_read_format_warc.c \ +test_read_format_xar.c \ +test_read_format_zip.c \ +test_read_format_zip_7075_utf8_paths.c \ +test_read_format_zip_comment_stored.c \ +test_read_format_zip_encryption_data.c \ +test_read_format_zip_encryption_header.c \ +test_read_format_zip_encryption_partially.c \ +test_read_format_zip_extra_padding.c \ +test_read_format_zip_filename.c \ +test_read_format_zip_high_compression.c \ +test_read_format_zip_jar.c \ +test_read_format_zip_mac_metadata.c \ +test_read_format_zip_malformed.c \ +test_read_format_zip_msdos.c \ +test_read_format_zip_nested.c \ +test_read_format_zip_nofiletype.c \ +test_read_format_zip_padded.c \ +test_read_format_zip_sfx.c \ +test_read_format_zip_traditional_encryption_data.c \ +test_read_format_zip_winzip_aes.c \ +test_read_format_zip_winzip_aes_large.c \ +test_read_format_zip_with_invalid_traditional_eocd.c \ +test_read_format_zip_zip64.c \ +test_read_large.c \ +test_read_pax_schily_xattr.c \ +test_read_pax_truncated.c \ +test_read_position.c \ +test_read_set_format.c \ +test_read_too_many_filters.c \ +test_read_truncated.c \ +test_read_truncated_filter.c \ +test_sparse_basic.c \ +test_tar_filenames.c \ +test_tar_large.c \ +test_ustar_filename_encoding.c \ +test_ustar_filenames.c \ +test_warn_missing_hardlink_target.c \ +test_write_disk.c \ +test_write_disk_appledouble.c \ +test_write_disk_failures.c \ +test_write_disk_hardlink.c \ +test_write_disk_hfs_compression.c \ +test_write_disk_lookup.c \ +test_write_disk_mac_metadata.c \ +test_write_disk_no_hfs_compression.c \ +test_write_disk_perms.c \ +test_write_disk_secure.c \ +test_write_disk_secure744.c \ +test_write_disk_secure745.c \ +test_write_disk_secure746.c \ +test_write_disk_sparse.c \ +test_write_disk_symlink.c \ +test_write_disk_times.c \ +test_write_filter_b64encode.c \ +test_write_filter_bzip2.c \ +test_write_filter_compress.c \ +test_write_filter_gzip.c \ +test_write_filter_gzip_timestamp.c \ +test_write_filter_lrzip.c \ +test_write_filter_lz4.c \ +test_write_filter_lzip.c \ +test_write_filter_lzma.c \ +test_write_filter_lzop.c \ +test_write_filter_program.c \ +test_write_filter_uuencode.c \ +test_write_filter_xz.c \ +test_write_filter_zstd.c \ +test_write_format_7zip.c \ +test_write_format_7zip_empty.c \ +test_write_format_7zip_large.c \ +test_write_format_ar.c \ +test_write_format_cpio.c \ +test_write_format_cpio_empty.c \ +test_write_format_cpio_newc.c \ +test_write_format_cpio_odc.c \ +test_write_format_gnutar.c \ +test_write_format_gnutar_filenames.c \ +test_write_format_iso9660.c \ +test_write_format_iso9660_boot.c \ +test_write_format_iso9660_empty.c \ +test_write_format_iso9660_filename.c \ +test_write_format_iso9660_zisofs.c \ +test_write_format_mtree.c \ +test_write_format_mtree_absolute_path.c \ +test_write_format_mtree_classic.c \ +test_write_format_mtree_classic_indent.c \ +test_write_format_mtree_fflags.c \ +test_write_format_mtree_no_separator.c \ +test_write_format_mtree_quoted_filename.c \ +test_write_format_pax.c \ +test_write_format_raw.c \ +test_write_format_raw_b64.c \ +test_write_format_shar_empty.c \ +test_write_format_tar.c \ +test_write_format_tar_empty.c \ +test_write_format_tar_sparse.c \ +test_write_format_tar_ustar.c \ +test_write_format_tar_v7tar.c \ +test_write_format_warc.c \ +test_write_format_warc_empty.c \ +test_write_format_xar.c \ +test_write_format_xar_empty.c \ +test_write_format_zip.c \ +test_write_format_zip_compression_store.c \ +test_write_format_zip_empty.c \ +test_write_format_zip_empty_zip64.c \ +test_write_format_zip_file.c \ +test_write_format_zip_file_zip64.c \ +test_write_format_zip_large.c \ +test_write_format_zip_zip64.c \ +test_write_open_memory.c \ +test_write_read_format_zip.c \ +test_xattr_platform.c \ +test_zip_filename_encoding.c + +FILESDIR= ${TESTSDIR} +FILES=\ +test_acl_pax_nfs4.tar.uu \ +test_acl_pax_posix1e.tar.uu \ +test_archive_string_conversion.txt.Z.uu \ +test_compat_bzip2_1.tbz.uu \ +test_compat_bzip2_2.tbz.uu \ +test_compat_cpio_1.cpio.uu \ +test_compat_gtar_1.tar.uu \ +test_compat_gtar_2.tar.uu \ +test_compat_gzip_1.tgz.uu \ +test_compat_gzip_2.tgz.uu \ +test_compat_lz4_1.tar.lz4.uu \ +test_compat_lz4_2.tar.lz4.uu \ +test_compat_lz4_3.tar.lz4.uu \ +test_compat_lz4_B4.tar.lz4.uu \ +test_compat_lz4_B4BD.tar.lz4.uu \ +test_compat_lz4_B4BDBX.tar.lz4.uu \ +test_compat_lz4_B5.tar.lz4.uu \ +test_compat_lz4_B5BD.tar.lz4.uu \ +test_compat_lz4_B6.tar.lz4.uu \ +test_compat_lz4_B6BD.tar.lz4.uu \ +test_compat_lz4_B7.tar.lz4.uu \ +test_compat_lz4_B7BD.tar.lz4.uu \ +test_compat_lzip_1.tlz.uu \ +test_compat_lzip_2.tlz.uu \ +test_compat_lzma_1.tlz.uu \ +test_compat_lzma_2.tlz.uu \ +test_compat_lzma_3.tlz.uu \ +test_compat_lzop_1.tar.lzo.uu \ +test_compat_lzop_2.tar.lzo.uu \ +test_compat_lzop_3.tar.lzo.uu \ +test_compat_mac-1.tar.Z.uu \ +test_compat_mac-2.tar.Z.uu \ +test_compat_pax_libarchive_2x.tar.Z.uu \ +test_compat_perl_archive_tar.tar.uu \ +test_compat_plexus_archiver_tar.tar.uu \ +test_compat_solaris_pax_sparse_1.pax.Z.uu \ +test_compat_solaris_pax_sparse_2.pax.Z.uu \ +test_compat_solaris_tar_acl.tar.uu \ +test_compat_star_acl_nfs4.tar.uu \ +test_compat_star_acl_posix1e.tar.uu \ +test_compat_tar_hardlink_1.tar.uu \ +test_compat_uudecode_large.tar.Z.uu \ +test_compat_xz_1.txz.uu \ +test_compat_zip_1.zip.uu \ +test_compat_zip_2.zip.uu \ +test_compat_zip_3.zip.uu \ +test_compat_zip_4.zip.uu \ +test_compat_zip_5.zip.uu \ +test_compat_zip_6.zip.uu \ +test_compat_zip_7.xps.uu \ +test_compat_zip_8.zip.uu \ +test_compat_zstd_1.tar.zst.uu \ +test_fuzz.cab.uu \ +test_fuzz.lzh.uu \ +test_fuzz_1.iso.Z.uu \ +test_pax_filename_encoding.tar.uu \ +test_rar_multivolume_multiple_files.part1.rar.uu \ +test_rar_multivolume_multiple_files.part2.rar.uu \ +test_rar_multivolume_multiple_files.part3.rar.uu \ +test_rar_multivolume_multiple_files.part4.rar.uu \ +test_rar_multivolume_multiple_files.part5.rar.uu \ +test_rar_multivolume_multiple_files.part6.rar.uu \ +test_rar_multivolume_single_file.part1.rar.uu \ +test_rar_multivolume_single_file.part2.rar.uu \ +test_rar_multivolume_single_file.part3.rar.uu \ +test_rar_multivolume_uncompressed_files.part01.rar.uu \ +test_rar_multivolume_uncompressed_files.part02.rar.uu \ +test_rar_multivolume_uncompressed_files.part03.rar.uu \ +test_rar_multivolume_uncompressed_files.part04.rar.uu \ +test_rar_multivolume_uncompressed_files.part05.rar.uu \ +test_rar_multivolume_uncompressed_files.part06.rar.uu \ +test_rar_multivolume_uncompressed_files.part07.rar.uu \ +test_rar_multivolume_uncompressed_files.part08.rar.uu \ +test_rar_multivolume_uncompressed_files.part09.rar.uu \ +test_rar_multivolume_uncompressed_files.part10.rar.uu \ +test_read_filter_grzip.tar.grz.uu \ +test_read_filter_lrzip.tar.lrz.uu \ +test_read_filter_lzop.tar.lzo.uu \ +test_read_filter_lzop_multiple_parts.tar.lzo.uu \ +test_read_format_7zip_bcj2_bzip2.7z.uu \ +test_read_format_7zip_bcj2_copy_1.7z.uu \ +test_read_format_7zip_bcj2_copy_2.7z.uu \ +test_read_format_7zip_bcj2_copy_lzma.7z.uu \ +test_read_format_7zip_bcj2_deflate.7z.uu \ +test_read_format_7zip_bcj2_lzma1_1.7z.uu \ +test_read_format_7zip_bcj2_lzma1_2.7z.uu \ +test_read_format_7zip_bcj2_lzma2_1.7z.uu \ +test_read_format_7zip_bcj2_lzma2_2.7z.uu \ +test_read_format_7zip_bcj_bzip2.7z.uu \ +test_read_format_7zip_bcj_copy.7z.uu \ +test_read_format_7zip_bcj_deflate.7z.uu \ +test_read_format_7zip_bcj_lzma1.7z.uu \ +test_read_format_7zip_bcj_lzma2.7z.uu \ +test_read_format_7zip_bzip2.7z.uu \ +test_read_format_7zip_copy.7z.uu \ +test_read_format_7zip_copy_2.7z.uu \ +test_read_format_7zip_deflate.7z.uu \ +test_read_format_7zip_delta_lzma1.7z.uu \ +test_read_format_7zip_delta_lzma2.7z.uu \ +test_read_format_7zip_empty_archive.7z.uu \ +test_read_format_7zip_empty_file.7z.uu \ +test_read_format_7zip_encryption.7z.uu \ +test_read_format_7zip_encryption_header.7z.uu \ +test_read_format_7zip_encryption_partially.7z.uu \ +test_read_format_7zip_lzma1.7z.uu \ +test_read_format_7zip_lzma1_2.7z.uu \ +test_read_format_7zip_lzma1_lzma2.7z.uu \ +test_read_format_7zip_lzma2.7z.uu \ +test_read_format_7zip_malformed.7z.uu \ +test_read_format_7zip_malformed2.7z.uu \ +test_read_format_7zip_ppmd.7z.uu \ +test_read_format_7zip_symbolic_name.7z.uu \ +test_read_format_ar.ar.uu \ +test_read_format_cab_1.cab.uu \ +test_read_format_cab_2.cab.uu \ +test_read_format_cab_3.cab.uu \ +test_read_format_cab_filename_cp932.cab.uu \ +test_read_format_cpio_bin_be.cpio.uu \ +test_read_format_cpio_bin_le.cpio.uu \ +test_read_format_cpio_filename_cp866.cpio.uu \ +test_read_format_cpio_filename_eucjp.cpio.uu \ +test_read_format_cpio_filename_koi8r.cpio.uu \ +test_read_format_cpio_filename_utf8_jp.cpio.uu \ +test_read_format_cpio_filename_utf8_ru.cpio.uu \ +test_read_format_cpio_svr4_bzip2_rpm.rpm.uu \ +test_read_format_cpio_svr4_gzip_rpm.rpm.uu \ +test_read_format_gtar_filename_cp866.tar.Z.uu \ +test_read_format_gtar_filename_eucjp.tar.Z.uu \ +test_read_format_gtar_filename_koi8r.tar.Z.uu \ +test_read_format_gtar_sparse_1_13.tar.uu \ +test_read_format_gtar_sparse_1_17.tar.uu \ +test_read_format_gtar_sparse_1_17_posix00.tar.uu \ +test_read_format_gtar_sparse_1_17_posix01.tar.uu \ +test_read_format_gtar_sparse_1_17_posix10.tar.uu \ +test_read_format_gtar_sparse_1_17_posix10_modified.tar.uu \ +test_read_format_gtar_sparse_skip_entry.tar.Z.uu \ +test_read_format_iso.iso.Z.uu \ +test_read_format_iso_2.iso.Z.uu \ +test_read_format_iso_joliet.iso.Z.uu \ +test_read_format_iso_joliet_by_nero.iso.Z.uu \ +test_read_format_iso_joliet_long.iso.Z.uu \ +test_read_format_iso_joliet_rockridge.iso.Z.uu \ +test_read_format_iso_multi_extent.iso.Z.uu \ +test_read_format_iso_rockridge.iso.Z.uu \ +test_read_format_iso_rockridge_ce.iso.Z.uu \ +test_read_format_iso_rockridge_new.iso.Z.uu \ +test_read_format_iso_rockridge_rr_moved.iso.Z.uu \ +test_read_format_iso_xorriso.iso.Z.uu \ +test_read_format_iso_zisofs.iso.Z.uu \ +test_read_format_lha_bugfix_0.lzh.uu \ +test_read_format_lha_filename_cp932.lzh.uu \ +test_read_format_lha_header0.lzh.uu \ +test_read_format_lha_header1.lzh.uu \ +test_read_format_lha_header2.lzh.uu \ +test_read_format_lha_header3.lzh.uu \ +test_read_format_lha_lh0.lzh.uu \ +test_read_format_lha_lh6.lzh.uu \ +test_read_format_lha_lh7.lzh.uu \ +test_read_format_lha_withjunk.lzh.uu \ +test_read_format_mtree.mtree.uu \ +test_read_format_mtree_crash747.mtree.bz2.uu \ +test_read_format_mtree_nomagic.mtree.uu \ +test_read_format_mtree_nomagic2.mtree.uu \ +test_read_format_mtree_nomagic3.mtree.uu \ +test_read_format_mtree_noprint.mtree.uu \ +test_read_format_rar.rar.uu \ +test_read_format_rar5_arm.rar.uu \ +test_read_format_rar5_blake2.rar.uu \ +test_read_format_rar5_compressed.rar.uu \ +test_read_format_rar5_distance_overflow.rar.uu \ +test_read_format_rar5_extra_field_version.rar.uu \ +test_read_format_rar5_fileattr.rar.uu \ +test_read_format_rar5_hardlink.rar.uu \ +test_read_format_rar5_invalid_dict_reference.rar.uu \ +test_read_format_rar5_leftshift1.rar.uu \ +test_read_format_rar5_leftshift2.rar.uu \ +test_read_format_rar5_multiarchive.part01.rar.uu \ +test_read_format_rar5_multiarchive.part02.rar.uu \ +test_read_format_rar5_multiarchive.part03.rar.uu \ +test_read_format_rar5_multiarchive.part04.rar.uu \ +test_read_format_rar5_multiarchive.part05.rar.uu \ +test_read_format_rar5_multiarchive.part06.rar.uu \ +test_read_format_rar5_multiarchive.part07.rar.uu \ +test_read_format_rar5_multiarchive.part08.rar.uu \ +test_read_format_rar5_multiarchive_solid.part01.rar.uu \ +test_read_format_rar5_multiarchive_solid.part02.rar.uu \ +test_read_format_rar5_multiarchive_solid.part03.rar.uu \ +test_read_format_rar5_multiarchive_solid.part04.rar.uu \ +test_read_format_rar5_multiple_files.rar.uu \ +test_read_format_rar5_multiple_files_solid.rar.uu \ +test_read_format_rar5_nonempty_dir_stream.rar.uu \ +test_read_format_rar5_owner.rar.uu \ +test_read_format_rar5_readtables_overflow.rar.uu \ +test_read_format_rar5_solid.rar.uu \ +test_read_format_rar5_stored.rar.uu \ +test_read_format_rar5_stored_manyfiles.rar.uu \ +test_read_format_rar5_symlink.rar.uu \ +test_read_format_rar5_truncated_huff.rar.uu \ +test_read_format_rar5_win32.rar.uu \ +test_read_format_rar_binary_data.rar.uu \ +test_read_format_rar_compress_best.rar.uu \ +test_read_format_rar_compress_normal.rar.uu \ +test_read_format_rar_encryption_data.rar.uu \ +test_read_format_rar_encryption_header.rar.uu \ +test_read_format_rar_encryption_partially.rar.uu \ +test_read_format_rar_invalid1.rar.uu \ +test_read_format_rar_multi_lzss_blocks.rar.uu \ +test_read_format_rar_multivolume.part0001.rar.uu \ +test_read_format_rar_multivolume.part0002.rar.uu \ +test_read_format_rar_multivolume.part0003.rar.uu \ +test_read_format_rar_multivolume.part0004.rar.uu \ +test_read_format_rar_noeof.rar.uu \ +test_read_format_rar_ppmd_lzss_conversion.rar.uu \ +test_read_format_rar_ppmd_use_after_free.rar.uu \ +test_read_format_rar_sfx.exe.uu \ +test_read_format_rar_subblock.rar.uu \ +test_read_format_rar_unicode.rar.uu \ +test_read_format_rar_windows.rar.uu \ +test_read_format_raw.bufr.uu \ +test_read_format_raw.data.Z.uu \ +test_read_format_raw.data.gz.uu \ +test_read_format_raw.data.uu \ +test_read_format_tar_concatenated.tar.uu \ +test_read_format_tar_empty_filename.tar.uu \ +test_read_format_tar_empty_pax.tar.Z.uu \ +test_read_format_tar_empty_with_gnulabel.tar.uu \ +test_read_format_tar_filename_koi8r.tar.Z.uu \ +test_read_format_ustar_filename_cp866.tar.Z.uu \ +test_read_format_ustar_filename_eucjp.tar.Z.uu \ +test_read_format_ustar_filename_koi8r.tar.Z.uu \ +test_read_format_warc.warc.uu \ +test_read_format_zip.zip.uu \ +test_read_format_zip_7075_utf8_paths.zip.uu \ +test_read_format_zip_bz2_hang.zip.uu \ +test_read_format_zip_bzip2.zipx.uu \ +test_read_format_zip_bzip2_multi.zipx.uu \ +test_read_format_zip_comment_stored_1.zip.uu \ +test_read_format_zip_comment_stored_2.zip.uu \ +test_read_format_zip_encryption_data.zip.uu \ +test_read_format_zip_encryption_header.zip.uu \ +test_read_format_zip_encryption_partially.zip.uu \ +test_read_format_zip_extra_padding.zip.uu \ +test_read_format_zip_filename_cp866.zip.uu \ +test_read_format_zip_filename_cp932.zip.uu \ +test_read_format_zip_filename_koi8r.zip.uu \ +test_read_format_zip_filename_utf8_jp.zip.uu \ +test_read_format_zip_filename_utf8_ru.zip.uu \ +test_read_format_zip_filename_utf8_ru2.zip.uu \ +test_read_format_zip_high_compression.zip.uu \ +test_read_format_zip_jar.jar.uu \ +test_read_format_zip_length_at_end.zip.uu \ +test_read_format_zip_lzma.zipx.uu \ +test_read_format_zip_lzma_alone_leak.zipx.uu \ +test_read_format_zip_lzma_multi.zipx.uu \ +test_read_format_zip_mac_metadata.zip.uu \ +test_read_format_zip_malformed1.zip.uu \ +test_read_format_zip_msdos.zip.uu \ +test_read_format_zip_nested.zip.uu \ +test_read_format_zip_nofiletype.zip.uu \ +test_read_format_zip_padded1.zip.uu \ +test_read_format_zip_padded2.zip.uu \ +test_read_format_zip_padded3.zip.uu \ +test_read_format_zip_ppmd8.zipx.uu \ +test_read_format_zip_ppmd8_crash_1.zipx.uu \ +test_read_format_zip_ppmd8_crash_2.zipx.uu \ +test_read_format_zip_ppmd8_multi.zipx.uu \ +test_read_format_zip_sfx.uu \ +test_read_format_zip_symlink.zip.uu \ +test_read_format_zip_traditional_encryption_data.zip.uu \ +test_read_format_zip_ux.zip.uu \ +test_read_format_zip_winzip_aes128.zip.uu \ +test_read_format_zip_winzip_aes256.zip.uu \ +test_read_format_zip_winzip_aes256_large.zip.uu \ +test_read_format_zip_winzip_aes256_stored.zip.uu \ +test_read_format_zip_with_invalid_traditional_eocd.zip.uu \ +test_read_format_zip_xz_multi.zipx.uu \ +test_read_format_zip_zip64a.zip.uu \ +test_read_format_zip_zip64b.zip.uu \ +test_read_large_splitted_rar_aa.uu \ +test_read_large_splitted_rar_ab.uu \ +test_read_large_splitted_rar_ac.uu \ +test_read_large_splitted_rar_ad.uu \ +test_read_large_splitted_rar_ae.uu \ +test_read_pax_schily_xattr.tar.uu \ +test_read_splitted_rar_aa.uu \ +test_read_splitted_rar_ab.uu \ +test_read_splitted_rar_ac.uu \ +test_read_splitted_rar_ad.uu \ +test_read_too_many_filters.gz.uu \ +test_splitted_rar_seek_support_aa.uu \ +test_splitted_rar_seek_support_ab.uu \ +test_splitted_rar_seek_support_ac.uu \ +test_write_disk_appledouble.cpio.gz.uu \ +test_write_disk_hfs_compression.tgz.uu \ +test_write_disk_mac_metadata.tar.gz.uu \ +test_write_disk_no_hfs_compression.tgz.uu + +.include + +test_main.o test_main.d: list.h + +CLEANFILES+=list.h + +# XXX: We skip the truncated filter tests because they are broken +# for built-in decompressors. The reason is that the output buffer +# size is 64K, the truncated files are < 64K and the built-in decompressors +# bail out instead of returning incomplete results. +list.h: ${SRCS.h_libarchive} Makefile + ${TOOL_GREP} -h '^DEFINE_TEST(' ${.ALLSRC} | \ + ${TOOL_GREP} -v _truncated_filter_ | \ + ${TOOL_GREP} -v test_compat_pax_libarchive_2x > ${.TARGET} + +COPTS.test_archive_string_conversion.c+= ${GCC_NO_STRINGOP_TRUNCATION} + diff --git a/lib/libarchive/t_libarchive.sh b/lib/libarchive/t_libarchive.sh new file mode 100644 --- /dev/null +++ b/lib/libarchive/t_libarchive.sh @@ -0,0 +1,52 @@ +# $NetBSD: t_libarchive.sh,v 1.8 2021/02/19 18:36:50 martin Exp $ +# +# Copyright (c) 2020 The NetBSD Foundation, Inc. +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +atf_test_case libarchive + +export TMPDIR=$PWD + +libarchive_head() +{ + atf_set "descr" "test libarchive" + atf_set "timeout" "6000" +} + +libarchive_body() +{ + local m=$(( $( sysctl -n hw.usermem64 ) / 1024 / 1024 )) + if [ $m -lt 400 ]; then + atf_skip "Not enough RAM" + fi + local d=$(atf_get_srcdir) + atf_check -s exit:0 -o 'not-match:^Details for failing tests:.*' \ + "$d/h_libarchive" -r "$d" +} + +atf_init_test_cases() +{ + atf_add_test_case libarchive +} diff --git a/lib/libc/Makefile b/lib/libc/Makefile --- a/lib/libc/Makefile +++ b/lib/libc/Makefile @@ -1,12 +1,13 @@ -# $NetBSD: Makefile,v 1.47 2014/02/17 10:10:41 martin Exp $ +# $NetBSD: Makefile,v 1.50 2020/03/08 22:08:46 mgorny Exp $ .include "Makefile.inc" .include SUBDIR+= tls_dso .WAIT sync -TESTS_SUBDIRS+= c063 db gen hash inet locale net regex rpc setjmp stdlib -TESTS_SUBDIRS+= stdio string sys termios time tls ttyio +TESTS_SUBDIRS+= atomic +TESTS_SUBDIRS+= c063 db gen hash inet locale misc net nls regex rpc setjmp +TESTS_SUBDIRS+= stdlib stdio string sys termios time tls ttyio .if ${HAVE_SSP} == "yes" TESTS_SUBDIRS+= ssp diff --git a/lib/libc/arch/aarch64/exec_prot_support.c b/lib/libc/arch/aarch64/exec_prot_support.c --- a/lib/libc/arch/aarch64/exec_prot_support.c +++ b/lib/libc/arch/aarch64/exec_prot_support.c @@ -1,4 +1,4 @@ -/* $NetBSD: exec_prot_support.c,v 1.1 2014/08/10 05:47:38 matt Exp $ */ +/* $NetBSD: exec_prot_support.c,v 1.2 2018/07/27 07:09:15 ryo Exp $ */ /*- * Copyright (c) 2014 The NetBSD Foundation, Inc. @@ -30,12 +30,12 @@ */ #include -__RCSID("$NetBSD: exec_prot_support.c,v 1.1 2014/08/10 05:47:38 matt Exp $"); +__RCSID("$NetBSD: exec_prot_support.c,v 1.2 2018/07/27 07:09:15 ryo Exp $"); #include "../../common/exec_prot.h" int exec_prot_support(void) { - return NOTIMPL; + return PERPAGE_XP; } diff --git a/lib/libc/arch/alpha/return_one.S b/lib/libc/arch/alpha/return_one.S --- a/lib/libc/arch/alpha/return_one.S +++ b/lib/libc/arch/alpha/return_one.S @@ -1,8 +1,11 @@ -/* $NetBSD: return_one.S,v 1.1 2011/07/18 23:16:09 jym Exp $ */ +/* $NetBSD: return_one.S,v 1.2 2017/12/30 16:35:03 martin Exp $ */ #include .globl return_one, return_one_end; -return_one: return_one_end: +return_one: + lda v0,1 + ret +return_one_end: nop diff --git a/lib/libc/arch/hppa/return_one.S b/lib/libc/arch/hppa/return_one.S --- a/lib/libc/arch/hppa/return_one.S +++ b/lib/libc/arch/hppa/return_one.S @@ -1,8 +1,10 @@ -/* $NetBSD: return_one.S,v 1.1 2011/07/18 23:16:09 jym Exp $ */ +/* $NetBSD: return_one.S,v 1.2 2019/02/14 10:36:33 mrg Exp $ */ #include .globl return_one, return_one_end; -return_one: return_one_end: - nop +return_one: + bv %r0(%r2) + ldi 1,%r28 +return_one_end: diff --git a/lib/libc/arch/m68k/return_one.S b/lib/libc/arch/m68k/return_one.S --- a/lib/libc/arch/m68k/return_one.S +++ b/lib/libc/arch/m68k/return_one.S @@ -1,8 +1,9 @@ -/* $NetBSD: return_one.S,v 1.1 2011/07/18 23:16:09 jym Exp $ */ +/* $NetBSD: return_one.S,v 1.2 2019/02/10 09:54:39 mlelstv Exp $ */ #include -.globl return_one, return_one_end; - -return_one: return_one_end: - nop +_ENTRY(return_one) + moveq #1,%d0 + rts + .globl return_one_end +return_one_end: diff --git a/lib/libc/arch/mips/return_one.S b/lib/libc/arch/mips/return_one.S --- a/lib/libc/arch/mips/return_one.S +++ b/lib/libc/arch/mips/return_one.S @@ -1,8 +1,13 @@ -/* $NetBSD: return_one.S,v 1.1 2011/07/18 23:16:10 jym Exp $ */ +/* $NetBSD: return_one.S,v 1.3 2021/01/21 00:56:41 simonb Exp $ */ #include +.set noreorder + .globl return_one, return_one_end; -return_one: return_one_end: - nop +return_one: + jr ra + li v0,1 + +return_one_end: diff --git a/lib/libc/arch/powerpc64/return_one.S b/lib/libc/arch/powerpc64/return_one.S --- a/lib/libc/arch/powerpc64/return_one.S +++ b/lib/libc/arch/powerpc64/return_one.S @@ -1,8 +1,10 @@ -/* $NetBSD: return_one.S,v 1.1 2011/07/18 23:16:10 jym Exp $ */ +/* $NetBSD: return_one.S,v 1.2 2019/02/14 10:36:33 mrg Exp $ */ #include .globl return_one, return_one_end; -return_one: return_one_end: - nop +return_one: + li 3,1 + blr +return_one_end: diff --git a/lib/libc/arch/sh3/return_one.S b/lib/libc/arch/sh3/return_one.S --- a/lib/libc/arch/sh3/return_one.S +++ b/lib/libc/arch/sh3/return_one.S @@ -1,8 +1,10 @@ -/* $NetBSD: return_one.S,v 1.1 2011/07/18 23:16:10 jym Exp $ */ +/* $NetBSD: return_one.S,v 1.2 2017/07/20 01:13:47 uwe Exp $ */ #include -.globl return_one, return_one_end; - -return_one: return_one_end: - nop +NENTRY(return_one) + rts + mov #1, r0 + SET_ENTRY_SIZE(return_one) + .globl return_one_end +return_one_end: diff --git a/lib/libc/arch/sparc/exec_prot_support.c b/lib/libc/arch/sparc/exec_prot_support.c --- a/lib/libc/arch/sparc/exec_prot_support.c +++ b/lib/libc/arch/sparc/exec_prot_support.c @@ -1,4 +1,4 @@ -/* $NetBSD: exec_prot_support.c,v 1.1 2011/07/18 23:16:10 jym Exp $ */ +/* $NetBSD: exec_prot_support.c,v 1.2 2018/01/14 19:59:51 martin Exp $ */ /*- * Copyright (c) 2011 The NetBSD Foundation, Inc. @@ -30,12 +30,13 @@ */ #include -__RCSID("$NetBSD: exec_prot_support.c,v 1.1 2011/07/18 23:16:10 jym Exp $"); +__RCSID("$NetBSD: exec_prot_support.c,v 1.2 2018/01/14 19:59:51 martin Exp $"); #include "../../common/exec_prot.h" int exec_prot_support(void) { - return NOTIMPL; + + return PERPAGE_XP; } diff --git a/lib/libc/arch/sparc/return_one.S b/lib/libc/arch/sparc/return_one.S --- a/lib/libc/arch/sparc/return_one.S +++ b/lib/libc/arch/sparc/return_one.S @@ -1,8 +1,11 @@ -/* $NetBSD: return_one.S,v 1.1 2011/07/18 23:16:10 jym Exp $ */ +/* $NetBSD: return_one.S,v 1.2 2018/01/14 19:59:51 martin Exp $ */ #include -.globl return_one, return_one_end; +.global return_one_end -return_one: return_one_end: - nop +ENTRY(return_one) +return_one: + retl + mov 1, %o0 +return_one_end: diff --git a/lib/libc/arch/sparc64/return_one.S b/lib/libc/arch/sparc64/return_one.S --- a/lib/libc/arch/sparc64/return_one.S +++ b/lib/libc/arch/sparc64/return_one.S @@ -1,11 +1,10 @@ -/* $NetBSD: return_one.S,v 1.2 2016/12/31 11:51:20 martin Exp $ */ +/* $NetBSD: return_one.S,v 1.3 2021/02/08 23:50:25 joerg Exp $ */ #include .global return_one_end ENTRY(return_one) -return_one: retl mov 1, %o0 return_one_end: diff --git a/lib/libc/arch/vax/return_one.S b/lib/libc/arch/vax/return_one.S --- a/lib/libc/arch/vax/return_one.S +++ b/lib/libc/arch/vax/return_one.S @@ -1,8 +1,12 @@ -/* $NetBSD: return_one.S,v 1.1 2011/07/18 23:16:10 jym Exp $ */ +/* $NetBSD: return_one.S,v 1.2 2019/02/14 10:36:33 mrg Exp $ */ #include .globl return_one, return_one_end; -return_one: return_one_end: - nop +return_one: + .word 0 + subl2 $4,%sp + movl $1,%r0 + ret +return_one_end: diff --git a/lib/libc/atomic/Makefile b/lib/libc/atomic/Makefile new file mode 100644 --- /dev/null +++ b/lib/libc/atomic/Makefile @@ -0,0 +1,36 @@ +# $NetBSD: Makefile,v 1.2 2019/02/26 10:01:41 isaki Exp $ + +.include + +TESTSDIR= ${TESTSBASE}/lib/libc/atomic + +TESTS_C+= t_atomic_add +TESTS_C+= t_atomic_and +TESTS_C+= t_atomic_cas +TESTS_C+= t_atomic_dec +TESTS_C+= t_atomic_inc +TESTS_C+= t_atomic_or +TESTS_C+= t_atomic_swap + +TESTS_C+= t___sync_add +TESTS_C+= t___sync_sub +TESTS_C+= t___sync_or +TESTS_C+= t___sync_and +TESTS_C+= t___sync_xor +TESTS_C+= t___sync_nand +TESTS_C+= t___sync_compare_and_swap +TESTS_C+= t___sync_lock + +# The code conforms to new NAND semantics. So this warning is not +# necessary here. +.if "${ACTIVE_CC}" == "gcc" +CPPFLAGS.t___sync_nand.c+= -Wno-sync-nand +.elif "${ACTIVE_CC}" == "clang" +CPPFLAGS.t___sync_nand.c+= -Wno-sync-fetch-and-nand-semantics-changed +.endif + +MKMAN=no + +BINDIR= ${TESTSDIR} + +.include diff --git a/lib/libc/atomic/t___sync_add.c b/lib/libc/atomic/t___sync_add.c new file mode 100644 --- /dev/null +++ b/lib/libc/atomic/t___sync_add.c @@ -0,0 +1,128 @@ +/* $NetBSD: t___sync_add.c,v 1.1 2019/02/26 10:01:41 isaki Exp $ */ + +/* + * Copyright (C) 2019 Tetsuya Isaki. 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 ``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 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 +__RCSID("$NetBSD: t___sync_add.c,v 1.1 2019/02/26 10:01:41 isaki Exp $"); + +#include +#include +#include // for __HAVE_ATOMIC64_OPS + +/* + * These tests don't examine the atomicity. + */ + +/* XXX + * Depending on a combination of arch and compiler, __sync_* is + * implemented as compiler's builtin function. In that case, even + * if libc exports the function symbol, it is not used. As a result + * this tests will examine compiler's builtin functions. + * It's better to run only when target is actually in libc. + */ + +#define DST (0x1122334455667788UL) +#define SRC (0xf0e0d0c0b0a09081UL) +#define EXPECT (0x0203040506070809UL) + +#define atf_sync_prefetch(NAME, TYPE, FMT) \ +ATF_TC(NAME); \ +ATF_TC_HEAD(NAME, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", #NAME); \ +} \ +ATF_TC_BODY(NAME, tc) \ +{ \ + volatile TYPE val; \ + TYPE src; \ + TYPE res; \ + TYPE expval; \ + TYPE expres; \ + val = (TYPE)DST; \ + src = (TYPE)SRC; \ + expval = (TYPE)EXPECT; \ + expres = (TYPE)DST; \ + res = NAME(&val, src); \ + ATF_REQUIRE_MSG(val == expval, \ + "val expects 0x%" FMT " but 0x%" FMT, expval, val); \ + ATF_REQUIRE_MSG(res == expres, \ + "res expects 0x%" FMT " but 0x%" FMT, expres, res); \ +} + +atf_sync_prefetch(__sync_fetch_and_add_1, uint8_t, PRIx8); +atf_sync_prefetch(__sync_fetch_and_add_2, uint16_t, PRIx16); +atf_sync_prefetch(__sync_fetch_and_add_4, uint32_t, PRIx32); +#if defined(__HAVE_ATOMIC64_OPS) +atf_sync_prefetch(__sync_fetch_and_add_8, uint64_t, PRIx64); +#endif + +#define atf_sync_postfetch(NAME, TYPE, FMT) \ +ATF_TC(NAME); \ +ATF_TC_HEAD(NAME, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", #NAME); \ +} \ +ATF_TC_BODY(NAME, tc) \ +{ \ + volatile TYPE val; \ + TYPE src; \ + TYPE res; \ + TYPE exp; \ + val = (TYPE)DST; \ + src = (TYPE)SRC; \ + exp = (TYPE)EXPECT; \ + res = NAME(&val, src); \ + ATF_REQUIRE_MSG(val == exp, \ + "val expects 0x%" FMT " but 0x%" FMT, exp, val); \ + ATF_REQUIRE_MSG(res == exp, \ + "res expects 0x%" FMT " but 0x%" FMT, exp, res); \ +} + +atf_sync_postfetch(__sync_add_and_fetch_1, uint8_t, PRIx8); +atf_sync_postfetch(__sync_add_and_fetch_2, uint16_t, PRIx16); +atf_sync_postfetch(__sync_add_and_fetch_4, uint32_t, PRIx32); +#ifdef __HAVE_ATOMIC64_OPS +atf_sync_postfetch(__sync_add_and_fetch_8, uint64_t, PRIx64); +#endif + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, __sync_fetch_and_add_1); + ATF_TP_ADD_TC(tp, __sync_fetch_and_add_2); + ATF_TP_ADD_TC(tp, __sync_fetch_and_add_4); +#ifdef __HAVE_ATOMIC64_OPS + ATF_TP_ADD_TC(tp, __sync_fetch_and_add_8); +#endif + + ATF_TP_ADD_TC(tp, __sync_add_and_fetch_1); + ATF_TP_ADD_TC(tp, __sync_add_and_fetch_2); + ATF_TP_ADD_TC(tp, __sync_add_and_fetch_4); +#ifdef __HAVE_ATOMIC64_OPS + ATF_TP_ADD_TC(tp, __sync_add_and_fetch_8); +#endif + + return atf_no_error(); +} diff --git a/lib/libc/atomic/t___sync_and.c b/lib/libc/atomic/t___sync_and.c new file mode 100644 --- /dev/null +++ b/lib/libc/atomic/t___sync_and.c @@ -0,0 +1,128 @@ +/* $NetBSD: t___sync_and.c,v 1.3 2019/03/01 05:39:01 isaki Exp $ */ + +/* + * Copyright (C) 2019 Tetsuya Isaki. 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 ``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 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 +__RCSID("$NetBSD: t___sync_and.c,v 1.3 2019/03/01 05:39:01 isaki Exp $"); + +#include +#include +#include // for __HAVE_ATOMIC64_OPS + +/* + * These tests don't examine the atomicity. + */ + +/* XXX + * Depending on a combination of arch and compiler, __sync_* is + * implemented as compiler's builtin function. In that case, even + * if libc exports the function symbol, it is not used. As a result + * this tests will examine compiler's builtin functions. + * It's better to run only when target is actually in libc. + */ + +#define DST (0x1122334455667788UL) +#define SRC (0xf0f0f0f0f0f0f0f0UL) +#define EXPECT (0x1020304050607080UL) + +#define atf_sync_prefetch(NAME, TYPE, FMT) \ +ATF_TC(NAME); \ +ATF_TC_HEAD(NAME, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", #NAME); \ +} \ +ATF_TC_BODY(NAME, tc) \ +{ \ + volatile TYPE val; \ + TYPE src; \ + TYPE res; \ + TYPE expval; \ + TYPE expres; \ + val = (TYPE)DST; \ + src = (TYPE)SRC; \ + expval = (TYPE)EXPECT; \ + expres = (TYPE)DST; \ + res = NAME(&val, src); \ + ATF_REQUIRE_MSG(val == expval, \ + "val expects 0x%" FMT " but 0x%" FMT, expval, val); \ + ATF_REQUIRE_MSG(res == expres, \ + "res expects 0x%" FMT " but 0x%" FMT, expres, res); \ +} + +atf_sync_prefetch(__sync_fetch_and_and_1, uint8_t, PRIx8); +atf_sync_prefetch(__sync_fetch_and_and_2, uint16_t, PRIx16); +atf_sync_prefetch(__sync_fetch_and_and_4, uint32_t, PRIx32); +#if defined(__HAVE_ATOMIC64_OPS) +atf_sync_prefetch(__sync_fetch_and_and_8, uint64_t, PRIx64); +#endif + +#define atf_sync_postfetch(NAME, TYPE, FMT) \ +ATF_TC(NAME); \ +ATF_TC_HEAD(NAME, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", #NAME); \ +} \ +ATF_TC_BODY(NAME, tc) \ +{ \ + volatile TYPE val; \ + TYPE src; \ + TYPE res; \ + TYPE exp; \ + val = (TYPE)DST; \ + src = (TYPE)SRC; \ + exp = (TYPE)EXPECT; \ + res = NAME(&val, src); \ + ATF_REQUIRE_MSG(val == exp, \ + "val expects 0x%" FMT " but 0x%" FMT, exp, val); \ + ATF_REQUIRE_MSG(res == exp, \ + "res expects 0x%" FMT " but 0x%" FMT, exp, res); \ +} + +atf_sync_postfetch(__sync_and_and_fetch_1, uint8_t, PRIx8); +atf_sync_postfetch(__sync_and_and_fetch_2, uint16_t, PRIx16); +atf_sync_postfetch(__sync_and_and_fetch_4, uint32_t, PRIx32); +#ifdef __HAVE_ATOMIC64_OPS +atf_sync_postfetch(__sync_and_and_fetch_8, uint64_t, PRIx64); +#endif + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, __sync_fetch_and_and_1); + ATF_TP_ADD_TC(tp, __sync_fetch_and_and_2); + ATF_TP_ADD_TC(tp, __sync_fetch_and_and_4); +#ifdef __HAVE_ATOMIC64_OPS + ATF_TP_ADD_TC(tp, __sync_fetch_and_and_8); +#endif + + ATF_TP_ADD_TC(tp, __sync_and_and_fetch_1); + ATF_TP_ADD_TC(tp, __sync_and_and_fetch_2); + ATF_TP_ADD_TC(tp, __sync_and_and_fetch_4); +#ifdef __HAVE_ATOMIC64_OPS + ATF_TP_ADD_TC(tp, __sync_and_and_fetch_8); +#endif + + return atf_no_error(); +} diff --git a/lib/libc/atomic/t___sync_compare_and_swap.c b/lib/libc/atomic/t___sync_compare_and_swap.c new file mode 100644 --- /dev/null +++ b/lib/libc/atomic/t___sync_compare_and_swap.c @@ -0,0 +1,157 @@ +/* $NetBSD: t___sync_compare_and_swap.c,v 1.1 2019/02/26 10:01:41 isaki Exp $ */ + +/* + * Copyright (C) 2019 Tetsuya Isaki. 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 ``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 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 +__RCSID("$NetBSD: t___sync_compare_and_swap.c,v 1.1 2019/02/26 10:01:41 isaki Exp $"); + +#include +#include +#include // for __HAVE_ATOMIC64_OPS + +/* + * These tests don't examine the atomicity. + */ + +/* XXX + * Depending on a combination of arch and compiler, __sync_* is + * implemented as compiler's builtin function. In that case, even + * if libc exports the function symbol, it is not used. As a result + * this tests will examine compiler's builtin functions. + * It's better to run only when target is actually in libc. + */ + +#define OLDVAL (0x1122334455667788UL) +#define NEWVAL (0x8090a0b0c0d0e0f0UL) + +#define atf_sync_bool(NAME, TYPE, FMT) \ +ATF_TC(NAME); \ +ATF_TC_HEAD(NAME, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", #NAME); \ +} \ +ATF_TC_BODY(NAME, tc) \ +{ \ + volatile TYPE val; \ + TYPE oldval; \ + TYPE newval; \ + TYPE expval; \ + bool expres; \ + bool res; \ + /* If successful */ \ + val = (TYPE)OLDVAL; \ + oldval = (TYPE)OLDVAL; \ + newval = (TYPE)NEWVAL; \ + expval = (TYPE)NEWVAL; \ + expres = true; \ + res = NAME(&val, oldval, newval); \ + ATF_REQUIRE_MSG(val == expval, \ + "successful case: val expects 0x%" FMT " but 0x%" FMT, expval, val); \ + ATF_REQUIRE_MSG(res == expres, \ + "successful case: res expects %d but %d", expres, res); \ + /* If failure */ \ + val = (TYPE)OLDVAL; \ + oldval = (TYPE)(OLDVAL + 1); \ + newval = (TYPE)NEWVAL; \ + expval = (TYPE)OLDVAL; \ + expres = false; \ + res = NAME(&val, oldval, newval); \ + ATF_REQUIRE_MSG(val == expval, \ + "failure case: val expects 0x%" FMT " but 0x%" FMT, expval, val); \ + ATF_REQUIRE_MSG(res == expres, \ + "failure case: res expects %d but %d", expres, res); \ +} + +atf_sync_bool(__sync_bool_compare_and_swap_1, uint8_t, PRIx8); +atf_sync_bool(__sync_bool_compare_and_swap_2, uint16_t, PRIx16); +atf_sync_bool(__sync_bool_compare_and_swap_4, uint32_t, PRIx32); +#ifdef __HAVE_ATOMIC64_OPS +atf_sync_bool(__sync_bool_compare_and_swap_8, uint64_t, PRIx64); +#endif + +#define atf_sync_val(NAME, TYPE, FMT) \ +ATF_TC(NAME); \ +ATF_TC_HEAD(NAME, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", #NAME); \ +} \ +ATF_TC_BODY(NAME, tc) \ +{ \ + volatile TYPE val; \ + TYPE oldval; \ + TYPE newval; \ + TYPE expval; \ + TYPE expres; \ + TYPE res; \ + /* If successful */ \ + val = (TYPE)OLDVAL; \ + oldval = (TYPE)OLDVAL; \ + newval = (TYPE)NEWVAL; \ + expval = (TYPE)NEWVAL; \ + expres = (TYPE)OLDVAL; \ + res = NAME(&val, oldval, newval); \ + ATF_REQUIRE_MSG(val == expval, \ + "successful case: val expects 0x%" FMT " but 0x%" FMT, expval, val); \ + ATF_REQUIRE_MSG(res == expres, \ + "successful case: res expects 0x%" FMT " but 0x%" FMT, expres, res); \ + /* If failure */ \ + val = (TYPE)OLDVAL; \ + oldval = (TYPE)(OLDVAL + 1); \ + newval = (TYPE)NEWVAL; \ + expval = (TYPE)OLDVAL; \ + expres = (TYPE)OLDVAL; \ + res = NAME(&val, oldval, newval); \ + ATF_REQUIRE_MSG(val == expval, \ + "failure case: val expects 0x%" FMT " but 0x%" FMT, expval, val); \ + ATF_REQUIRE_MSG(res == expres, \ + "failure case: res expects 0x%" FMT " but 0x%" FMT, expres, res); \ +} + +atf_sync_val(__sync_val_compare_and_swap_1, uint8_t, PRIx8); +atf_sync_val(__sync_val_compare_and_swap_2, uint16_t, PRIx16); +atf_sync_val(__sync_val_compare_and_swap_4, uint32_t, PRIx32); +#ifdef __HAVE_ATOMIC64_OPS +atf_sync_val(__sync_val_compare_and_swap_8, uint64_t, PRIx64); +#endif + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, __sync_bool_compare_and_swap_1); + ATF_TP_ADD_TC(tp, __sync_bool_compare_and_swap_2); + ATF_TP_ADD_TC(tp, __sync_bool_compare_and_swap_4); +#ifdef __HAVE_ATOMIC64_OPS + ATF_TP_ADD_TC(tp, __sync_bool_compare_and_swap_8); +#endif + + ATF_TP_ADD_TC(tp, __sync_val_compare_and_swap_1); + ATF_TP_ADD_TC(tp, __sync_val_compare_and_swap_2); + ATF_TP_ADD_TC(tp, __sync_val_compare_and_swap_4); +#ifdef __HAVE_ATOMIC64_OPS + ATF_TP_ADD_TC(tp, __sync_val_compare_and_swap_8); +#endif + + return atf_no_error(); +} diff --git a/lib/libc/atomic/t___sync_lock.c b/lib/libc/atomic/t___sync_lock.c new file mode 100644 --- /dev/null +++ b/lib/libc/atomic/t___sync_lock.c @@ -0,0 +1,137 @@ +/* $NetBSD: t___sync_lock.c,v 1.1 2019/02/26 10:01:41 isaki Exp $ */ + +/* + * Copyright (C) 2019 Tetsuya Isaki. 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 ``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 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 +__RCSID("$NetBSD: t___sync_lock.c,v 1.1 2019/02/26 10:01:41 isaki Exp $"); + +#include +#include +#include // for __HAVE_ATOMIC64_OPS + +/* + * These tests don't examine the atomicity. + */ + +/* XXX + * Depending on a combination of arch and compiler, __sync_* is + * implemented as compiler's builtin function. In that case, even + * if libc exports the function symbol, it is not used. As a result + * this tests will examine compiler's builtin functions. + * It's better to run only when target is actually in libc. + */ + +#define OLDVAL (0x1122334455667788UL) +#define NEWVAL (0x8090a0b0c0d0e0f0UL) + +#define atf_sync_tas(NAME, TYPE, FMT) \ +ATF_TC(NAME); \ +ATF_TC_HEAD(NAME, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", #NAME); \ +} \ +ATF_TC_BODY(NAME, tc) \ +{ \ + volatile TYPE val; \ + TYPE newval; \ + TYPE expval; \ + TYPE expres; \ + TYPE res; \ + val = (TYPE)OLDVAL; \ + newval = (TYPE)NEWVAL; \ + expval = (TYPE)NEWVAL; \ + expres = (TYPE)OLDVAL; \ + res = NAME(&val, newval); \ + ATF_REQUIRE_MSG(val == expval, \ + "val expects 0x%" FMT " but 0x%" FMT, expval, val); \ + ATF_REQUIRE_MSG(res == expres, \ + "res expects 0x%" FMT " but 0x%" FMT, expres, res); \ +} + +atf_sync_tas(__sync_lock_test_and_set_1, uint8_t, PRIx8); +atf_sync_tas(__sync_lock_test_and_set_2, uint16_t, PRIx16); +atf_sync_tas(__sync_lock_test_and_set_4, uint32_t, PRIx32); +#ifdef __HAVE_ATOMIC64_OPS +atf_sync_tas(__sync_lock_test_and_set_8, uint64_t, PRIx64); +#endif + +#define atf_sync_rel(NAME, TYPE, FMT) \ +ATF_TC(NAME); \ +ATF_TC_HEAD(NAME, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", #NAME); \ +} \ +ATF_TC_BODY(NAME, tc) \ +{ \ + volatile TYPE val; \ + TYPE expval; \ + val = (TYPE)OLDVAL; \ + expval = (TYPE)0; \ + NAME(&val); \ + ATF_REQUIRE_MSG(val == expval, \ + "val expects 0x%" FMT " but 0x%" FMT, expval, val); \ +} + +atf_sync_rel(__sync_lock_release_1, uint8_t, PRIx8); +atf_sync_rel(__sync_lock_release_2, uint16_t, PRIx16); +atf_sync_rel(__sync_lock_release_4, uint32_t, PRIx32); +#ifdef __HAVE_ATOMIC64_OPS +atf_sync_rel(__sync_lock_release_8, uint64_t, PRIx64); +#endif + +/* + * __sync_synchronize(): This is just a link-time test. + */ +ATF_TC(__sync_synchronize); +ATF_TC_HEAD(__sync_synchronize, tc) +{ + atf_tc_set_md_var(tc, "descr", "__sync_synchronize"); +} +ATF_TC_BODY(__sync_synchronize, tc) +{ + __sync_synchronize(); +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, __sync_lock_test_and_set_1); + ATF_TP_ADD_TC(tp, __sync_lock_test_and_set_2); + ATF_TP_ADD_TC(tp, __sync_lock_test_and_set_4); +#ifdef __HAVE_ATOMIC64_OPS + ATF_TP_ADD_TC(tp, __sync_lock_test_and_set_8); +#endif + + ATF_TP_ADD_TC(tp, __sync_lock_release_1); + ATF_TP_ADD_TC(tp, __sync_lock_release_2); + ATF_TP_ADD_TC(tp, __sync_lock_release_4); +#ifdef __HAVE_ATOMIC64_OPS + ATF_TP_ADD_TC(tp, __sync_lock_release_8); +#endif + + ATF_TP_ADD_TC(tp, __sync_synchronize); + + return atf_no_error(); +} diff --git a/lib/libc/atomic/t___sync_nand.c b/lib/libc/atomic/t___sync_nand.c new file mode 100644 --- /dev/null +++ b/lib/libc/atomic/t___sync_nand.c @@ -0,0 +1,128 @@ +/* $NetBSD: t___sync_nand.c,v 1.1 2019/02/26 10:01:41 isaki Exp $ */ + +/* + * Copyright (C) 2019 Tetsuya Isaki. 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 ``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 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 +__RCSID("$NetBSD: t___sync_nand.c,v 1.1 2019/02/26 10:01:41 isaki Exp $"); + +#include +#include +#include // for __HAVE_ATOMIC64_OPS + +/* + * These tests don't examine the atomicity. + */ + +/* XXX + * Depending on a combination of arch and compiler, __sync_* is + * implemented as compiler's builtin function. In that case, even + * if libc exports the function symbol, it is not used. As a result + * this tests will examine compiler's builtin functions. + * It's better to run only when target is actually in libc. + */ + +#define DST (0x1122334455667788UL) +#define SRC (0xf0f0f0f0f0f0f0f0UL) +#define EXPECT (0xefdfcfbfaf9f8f7fUL) + +#define atf_sync_prefetch(NAME, TYPE, FMT) \ +ATF_TC(NAME); \ +ATF_TC_HEAD(NAME, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", #NAME); \ +} \ +ATF_TC_BODY(NAME, tc) \ +{ \ + volatile TYPE val; \ + TYPE src; \ + TYPE res; \ + TYPE expval; \ + TYPE expres; \ + val = (TYPE)DST; \ + src = (TYPE)SRC; \ + expval = (TYPE)EXPECT; \ + expres = (TYPE)DST; \ + res = NAME(&val, src); \ + ATF_REQUIRE_MSG(val == expval, \ + "val expects 0x%" FMT " but 0x%" FMT, expval, val); \ + ATF_REQUIRE_MSG(res == expres, \ + "res expects 0x%" FMT " but 0x%" FMT, expres, res); \ +} + +atf_sync_prefetch(__sync_fetch_and_nand_1, uint8_t, PRIx8); +atf_sync_prefetch(__sync_fetch_and_nand_2, uint16_t, PRIx16); +atf_sync_prefetch(__sync_fetch_and_nand_4, uint32_t, PRIx32); +#if defined(__HAVE_ATOMIC64_OPS) +atf_sync_prefetch(__sync_fetch_and_nand_8, uint64_t, PRIx64); +#endif + +#define atf_sync_postfetch(NAME, TYPE, FMT) \ +ATF_TC(NAME); \ +ATF_TC_HEAD(NAME, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", #NAME); \ +} \ +ATF_TC_BODY(NAME, tc) \ +{ \ + volatile TYPE val; \ + TYPE src; \ + TYPE res; \ + TYPE exp; \ + val = (TYPE)DST; \ + src = (TYPE)SRC; \ + exp = (TYPE)EXPECT; \ + res = NAME(&val, src); \ + ATF_REQUIRE_MSG(val == exp, \ + "val expects 0x%" FMT " but 0x%" FMT, exp, val); \ + ATF_REQUIRE_MSG(res == exp, \ + "res expects 0x%" FMT " but 0x%" FMT, exp, res); \ +} + +atf_sync_postfetch(__sync_nand_and_fetch_1, uint8_t, PRIx8); +atf_sync_postfetch(__sync_nand_and_fetch_2, uint16_t, PRIx16); +atf_sync_postfetch(__sync_nand_and_fetch_4, uint32_t, PRIx32); +#ifdef __HAVE_ATOMIC64_OPS +atf_sync_postfetch(__sync_nand_and_fetch_8, uint64_t, PRIx64); +#endif + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, __sync_fetch_and_nand_1); + ATF_TP_ADD_TC(tp, __sync_fetch_and_nand_2); + ATF_TP_ADD_TC(tp, __sync_fetch_and_nand_4); +#ifdef __HAVE_ATOMIC64_OPS + ATF_TP_ADD_TC(tp, __sync_fetch_and_nand_8); +#endif + + ATF_TP_ADD_TC(tp, __sync_nand_and_fetch_1); + ATF_TP_ADD_TC(tp, __sync_nand_and_fetch_2); + ATF_TP_ADD_TC(tp, __sync_nand_and_fetch_4); +#ifdef __HAVE_ATOMIC64_OPS + ATF_TP_ADD_TC(tp, __sync_nand_and_fetch_8); +#endif + + return atf_no_error(); +} diff --git a/lib/libc/atomic/t___sync_or.c b/lib/libc/atomic/t___sync_or.c new file mode 100644 --- /dev/null +++ b/lib/libc/atomic/t___sync_or.c @@ -0,0 +1,128 @@ +/* $NetBSD: t___sync_or.c,v 1.1 2019/02/26 10:01:41 isaki Exp $ */ + +/* + * Copyright (C) 2019 Tetsuya Isaki. 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 ``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 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 +__RCSID("$NetBSD: t___sync_or.c,v 1.1 2019/02/26 10:01:41 isaki Exp $"); + +#include +#include +#include // for __HAVE_ATOMIC64_OPS + +/* + * These tests don't examine the atomicity. + */ + +/* XXX + * Depending on a combination of arch and compiler, __sync_* is + * implemented as compiler's builtin function. In that case, even + * if libc exports the function symbol, it is not used. As a result + * this tests will examine compiler's builtin functions. + * It's better to run only when target is actually in libc. + */ + +#define DST (0x1122334455667788UL) +#define SRC (0xf0f0f0f0f0f0f0f0UL) +#define EXPECT (0xf1f2f3f4f5f6f7f8UL) + +#define atf_sync_prefetch(NAME, TYPE, FMT) \ +ATF_TC(NAME); \ +ATF_TC_HEAD(NAME, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", #NAME); \ +} \ +ATF_TC_BODY(NAME, tc) \ +{ \ + volatile TYPE val; \ + TYPE src; \ + TYPE res; \ + TYPE expval; \ + TYPE expres; \ + val = (TYPE)DST; \ + src = (TYPE)SRC; \ + expval = (TYPE)EXPECT; \ + expres = (TYPE)DST; \ + res = NAME(&val, src); \ + ATF_REQUIRE_MSG(val == expval, \ + "val expects 0x%" FMT " but 0x%" FMT, expval, val); \ + ATF_REQUIRE_MSG(res == expres, \ + "res expects 0x%" FMT " but 0x%" FMT, expres, res); \ +} + +atf_sync_prefetch(__sync_fetch_and_or_1, uint8_t, PRIx8); +atf_sync_prefetch(__sync_fetch_and_or_2, uint16_t, PRIx16); +atf_sync_prefetch(__sync_fetch_and_or_4, uint32_t, PRIx32); +#if defined(__HAVE_ATOMIC64_OPS) +atf_sync_prefetch(__sync_fetch_and_or_8, uint64_t, PRIx64); +#endif + +#define atf_sync_postfetch(NAME, TYPE, FMT) \ +ATF_TC(NAME); \ +ATF_TC_HEAD(NAME, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", #NAME); \ +} \ +ATF_TC_BODY(NAME, tc) \ +{ \ + volatile TYPE val; \ + TYPE src; \ + TYPE res; \ + TYPE exp; \ + val = (TYPE)DST; \ + src = (TYPE)SRC; \ + exp = (TYPE)EXPECT; \ + res = NAME(&val, src); \ + ATF_REQUIRE_MSG(val == exp, \ + "val expects 0x%" FMT " but 0x%" FMT, exp, val); \ + ATF_REQUIRE_MSG(res == exp, \ + "res expects 0x%" FMT " but 0x%" FMT, exp, res); \ +} + +atf_sync_postfetch(__sync_or_and_fetch_1, uint8_t, PRIx8); +atf_sync_postfetch(__sync_or_and_fetch_2, uint16_t, PRIx16); +atf_sync_postfetch(__sync_or_and_fetch_4, uint32_t, PRIx32); +#ifdef __HAVE_ATOMIC64_OPS +atf_sync_postfetch(__sync_or_and_fetch_8, uint64_t, PRIx64); +#endif + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, __sync_fetch_and_or_1); + ATF_TP_ADD_TC(tp, __sync_fetch_and_or_2); + ATF_TP_ADD_TC(tp, __sync_fetch_and_or_4); +#ifdef __HAVE_ATOMIC64_OPS + ATF_TP_ADD_TC(tp, __sync_fetch_and_or_8); +#endif + + ATF_TP_ADD_TC(tp, __sync_or_and_fetch_1); + ATF_TP_ADD_TC(tp, __sync_or_and_fetch_2); + ATF_TP_ADD_TC(tp, __sync_or_and_fetch_4); +#ifdef __HAVE_ATOMIC64_OPS + ATF_TP_ADD_TC(tp, __sync_or_and_fetch_8); +#endif + + return atf_no_error(); +} diff --git a/lib/libc/atomic/t___sync_sub.c b/lib/libc/atomic/t___sync_sub.c new file mode 100644 --- /dev/null +++ b/lib/libc/atomic/t___sync_sub.c @@ -0,0 +1,128 @@ +/* $NetBSD: t___sync_sub.c,v 1.1 2019/02/26 10:01:41 isaki Exp $ */ + +/* + * Copyright (C) 2019 Tetsuya Isaki. 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 ``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 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 +__RCSID("$NetBSD: t___sync_sub.c,v 1.1 2019/02/26 10:01:41 isaki Exp $"); + +#include +#include +#include // for __HAVE_ATOMIC64_OPS + +/* + * These tests don't examine the atomicity. + */ + +/* XXX + * Depending on a combination of arch and compiler, __sync_* is + * implemented as compiler's builtin function. In that case, even + * if libc exports the function symbol, it is not used. As a result + * this tests will examine compiler's builtin functions. + * It's better to run only when target is actually in libc. + */ + +#define DST (0xffeeddccbbaa9988UL) +#define SRC (0x1122334455667787UL) +#define EXPECT (0xeeccaa8866442201UL) + +#define atf_sync_prefetch(NAME, TYPE, FMT) \ +ATF_TC(NAME); \ +ATF_TC_HEAD(NAME, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", #NAME); \ +} \ +ATF_TC_BODY(NAME, tc) \ +{ \ + volatile TYPE val; \ + TYPE src; \ + TYPE res; \ + TYPE expval; \ + TYPE expres; \ + val = (TYPE)DST; \ + src = (TYPE)SRC; \ + expval = (TYPE)EXPECT; \ + expres = (TYPE)DST; \ + res = NAME(&val, src); \ + ATF_REQUIRE_MSG(val == expval, \ + "val expects 0x%" FMT " but 0x%" FMT, expval, val); \ + ATF_REQUIRE_MSG(res == expres, \ + "res expects 0x%" FMT " but 0x%" FMT, expres, res); \ +} + +atf_sync_prefetch(__sync_fetch_and_sub_1, uint8_t, PRIx8); +atf_sync_prefetch(__sync_fetch_and_sub_2, uint16_t, PRIx16); +atf_sync_prefetch(__sync_fetch_and_sub_4, uint32_t, PRIx32); +#if defined(__HAVE_ATOMIC64_OPS) +atf_sync_prefetch(__sync_fetch_and_sub_8, uint64_t, PRIx64); +#endif + +#define atf_sync_postfetch(NAME, TYPE, FMT) \ +ATF_TC(NAME); \ +ATF_TC_HEAD(NAME, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", #NAME); \ +} \ +ATF_TC_BODY(NAME, tc) \ +{ \ + volatile TYPE val; \ + TYPE src; \ + TYPE res; \ + TYPE exp; \ + val = (TYPE)DST; \ + src = (TYPE)SRC; \ + exp = (TYPE)EXPECT; \ + res = NAME(&val, src); \ + ATF_REQUIRE_MSG(val == exp, \ + "val expects 0x%" FMT " but 0x%" FMT, exp, val); \ + ATF_REQUIRE_MSG(res == exp, \ + "res expects 0x%" FMT " but 0x%" FMT, exp, res); \ +} + +atf_sync_postfetch(__sync_sub_and_fetch_1, uint8_t, PRIx8); +atf_sync_postfetch(__sync_sub_and_fetch_2, uint16_t, PRIx16); +atf_sync_postfetch(__sync_sub_and_fetch_4, uint32_t, PRIx32); +#ifdef __HAVE_ATOMIC64_OPS +atf_sync_postfetch(__sync_sub_and_fetch_8, uint64_t, PRIx64); +#endif + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, __sync_fetch_and_sub_1); + ATF_TP_ADD_TC(tp, __sync_fetch_and_sub_2); + ATF_TP_ADD_TC(tp, __sync_fetch_and_sub_4); +#ifdef __HAVE_ATOMIC64_OPS + ATF_TP_ADD_TC(tp, __sync_fetch_and_sub_8); +#endif + + ATF_TP_ADD_TC(tp, __sync_sub_and_fetch_1); + ATF_TP_ADD_TC(tp, __sync_sub_and_fetch_2); + ATF_TP_ADD_TC(tp, __sync_sub_and_fetch_4); +#ifdef __HAVE_ATOMIC64_OPS + ATF_TP_ADD_TC(tp, __sync_sub_and_fetch_8); +#endif + + return atf_no_error(); +} diff --git a/lib/libc/atomic/t___sync_xor.c b/lib/libc/atomic/t___sync_xor.c new file mode 100644 --- /dev/null +++ b/lib/libc/atomic/t___sync_xor.c @@ -0,0 +1,128 @@ +/* $NetBSD: t___sync_xor.c,v 1.1 2019/02/26 10:01:41 isaki Exp $ */ + +/* + * Copyright (C) 2019 Tetsuya Isaki. 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 ``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 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 +__RCSID("$NetBSD: t___sync_xor.c,v 1.1 2019/02/26 10:01:41 isaki Exp $"); + +#include +#include +#include // for __HAVE_ATOMIC64_OPS + +/* + * These tests don't examine the atomicity. + */ + +/* XXX + * Depending on a combination of arch and compiler, __sync_* is + * implemented as compiler's builtin function. In that case, even + * if libc exports the function symbol, it is not used. As a result + * this tests will examine compiler's builtin functions. + * It's better to run only when target is actually in libc. + */ + +#define DST (0x1122334455667788UL) +#define SRC (0xf0f0f0f0f0f0f0f0UL) +#define EXPECT (0xe1d2c3b4a5968778UL) + +#define atf_sync_prefetch(NAME, TYPE, FMT) \ +ATF_TC(NAME); \ +ATF_TC_HEAD(NAME, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", #NAME); \ +} \ +ATF_TC_BODY(NAME, tc) \ +{ \ + volatile TYPE val; \ + TYPE src; \ + TYPE res; \ + TYPE expval; \ + TYPE expres; \ + val = (TYPE)DST; \ + src = (TYPE)SRC; \ + expval = (TYPE)EXPECT; \ + expres = (TYPE)DST; \ + res = NAME(&val, src); \ + ATF_REQUIRE_MSG(val == expval, \ + "val expects 0x%" FMT " but 0x%" FMT, expval, val); \ + ATF_REQUIRE_MSG(res == expres, \ + "res expects 0x%" FMT " but 0x%" FMT, expres, res); \ +} + +atf_sync_prefetch(__sync_fetch_and_xor_1, uint8_t, PRIx8); +atf_sync_prefetch(__sync_fetch_and_xor_2, uint16_t, PRIx16); +atf_sync_prefetch(__sync_fetch_and_xor_4, uint32_t, PRIx32); +#if defined(__HAVE_ATOMIC64_OPS) +atf_sync_prefetch(__sync_fetch_and_xor_8, uint64_t, PRIx64); +#endif + +#define atf_sync_postfetch(NAME, TYPE, FMT) \ +ATF_TC(NAME); \ +ATF_TC_HEAD(NAME, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", #NAME); \ +} \ +ATF_TC_BODY(NAME, tc) \ +{ \ + volatile TYPE val; \ + TYPE src; \ + TYPE res; \ + TYPE exp; \ + val = (TYPE)DST; \ + src = (TYPE)SRC; \ + exp = (TYPE)EXPECT; \ + res = NAME(&val, src); \ + ATF_REQUIRE_MSG(val == exp, \ + "val expects 0x%" FMT " but 0x%" FMT, exp, val); \ + ATF_REQUIRE_MSG(res == exp, \ + "res expects 0x%" FMT " but 0x%" FMT, exp, res); \ +} + +atf_sync_postfetch(__sync_xor_and_fetch_1, uint8_t, PRIx8); +atf_sync_postfetch(__sync_xor_and_fetch_2, uint16_t, PRIx16); +atf_sync_postfetch(__sync_xor_and_fetch_4, uint32_t, PRIx32); +#ifdef __HAVE_ATOMIC64_OPS +atf_sync_postfetch(__sync_xor_and_fetch_8, uint64_t, PRIx64); +#endif + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, __sync_fetch_and_xor_1); + ATF_TP_ADD_TC(tp, __sync_fetch_and_xor_2); + ATF_TP_ADD_TC(tp, __sync_fetch_and_xor_4); +#ifdef __HAVE_ATOMIC64_OPS + ATF_TP_ADD_TC(tp, __sync_fetch_and_xor_8); +#endif + + ATF_TP_ADD_TC(tp, __sync_xor_and_fetch_1); + ATF_TP_ADD_TC(tp, __sync_xor_and_fetch_2); + ATF_TP_ADD_TC(tp, __sync_xor_and_fetch_4); +#ifdef __HAVE_ATOMIC64_OPS + ATF_TP_ADD_TC(tp, __sync_xor_and_fetch_8); +#endif + + return atf_no_error(); +} diff --git a/lib/libc/atomic/t_atomic_add.c b/lib/libc/atomic/t_atomic_add.c new file mode 100644 --- /dev/null +++ b/lib/libc/atomic/t_atomic_add.c @@ -0,0 +1,125 @@ +/* $NetBSD: t_atomic_add.c,v 1.1 2019/02/17 12:24:17 isaki Exp $ */ + +/* + * Copyright (C) 2019 Tetsuya Isaki. 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 ``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 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 +__RCSID("$NetBSD: t_atomic_add.c,v 1.1 2019/02/17 12:24:17 isaki Exp $"); + +#include +#include +#include + +/* + * These tests don't examine the atomicity. + */ + +#define DST (0x1122334455667788UL) +#define SRC (0xf0e0d0c0b0a09081UL) +#define EXPECT (0x0203040506070809UL) + +/* + * atomic_add_*() + */ +#define atf_add(NAME, DTYPE, STYPE, FMT) \ +ATF_TC(NAME); \ +ATF_TC_HEAD(NAME, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", #NAME); \ +} \ +ATF_TC_BODY(NAME, tc) \ +{ \ + volatile DTYPE val; \ + STYPE src; \ + DTYPE exp; \ + val = (DTYPE)DST; \ + src = (STYPE)SRC; \ + exp = (DTYPE)EXPECT; \ + NAME(&val, src); \ + ATF_REQUIRE_MSG(val == exp, \ + "val expects " FMT " but " FMT, exp, val); \ +} + +atf_add(atomic_add_32, uint32_t, int32_t, "0x%" PRIx32); +atf_add(atomic_add_int, unsigned int, int, "0x%x"); +atf_add(atomic_add_long, unsigned long, long, "0x%lx"); +atf_add(atomic_add_ptr, void *, ssize_t, "%p"); +#if defined(__HAVE_ATOMIC64_OPS) +atf_add(atomic_add_64, uint64_t, int64_t, "0x%" PRIx64); +#endif + +/* + * atomic_add_*_nv() + */ +#define atf_add_nv(NAME, DTYPE, STYPE, FMT) \ +ATF_TC(NAME); \ +ATF_TC_HEAD(NAME, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", #NAME); \ +} \ +ATF_TC_BODY(NAME, tc) \ +{ \ + volatile DTYPE val; \ + STYPE src; \ + DTYPE res; \ + DTYPE exp; \ + val = (DTYPE)DST; \ + src = (STYPE)SRC; \ + exp = (DTYPE)EXPECT; \ + res = NAME(&val, src); \ + ATF_REQUIRE_MSG(val == exp, \ + "val expects " FMT " but " FMT, exp, val); \ + ATF_REQUIRE_MSG(res == exp, \ + "res expects " FMT " but " FMT, exp, res); \ +} + +atf_add_nv(atomic_add_32_nv, uint32_t, int32_t, "0x%" PRIx32); +atf_add_nv(atomic_add_int_nv, unsigned int, int, "0x%x"); +atf_add_nv(atomic_add_long_nv, unsigned long, long, "0x%lx"); +atf_add_nv(atomic_add_ptr_nv, void *, ssize_t, "%p"); +#if defined(__HAVE_ATOMIC64_OPS) +atf_add_nv(atomic_add_64_nv, uint64_t, int64_t, "0x%" PRIx64); +#endif + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, atomic_add_32); + ATF_TP_ADD_TC(tp, atomic_add_int); + ATF_TP_ADD_TC(tp, atomic_add_long); + ATF_TP_ADD_TC(tp, atomic_add_ptr); +#if defined(__HAVE_ATOMIC64_OPS) + ATF_TP_ADD_TC(tp, atomic_add_64); +#endif + + ATF_TP_ADD_TC(tp, atomic_add_32_nv); + ATF_TP_ADD_TC(tp, atomic_add_int_nv); + ATF_TP_ADD_TC(tp, atomic_add_long_nv); + ATF_TP_ADD_TC(tp, atomic_add_ptr_nv); +#if defined(__HAVE_ATOMIC64_OPS) + ATF_TP_ADD_TC(tp, atomic_add_64_nv); +#endif + + return atf_no_error(); +} diff --git a/lib/libc/atomic/t_atomic_and.c b/lib/libc/atomic/t_atomic_and.c new file mode 100644 --- /dev/null +++ b/lib/libc/atomic/t_atomic_and.c @@ -0,0 +1,121 @@ +/* $NetBSD: t_atomic_and.c,v 1.1 2019/02/17 12:24:17 isaki Exp $ */ + +/* + * Copyright (C) 2019 Tetsuya Isaki. 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 ``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 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 +__RCSID("$NetBSD: t_atomic_and.c,v 1.1 2019/02/17 12:24:17 isaki Exp $"); + +#include +#include +#include + +/* + * These tests don't examine the atomicity. + */ + +#define DST (0x1122334455667788UL) +#define SRC (0xf0f0f0f0f0f0f0f0UL) +#define EXPECT (0x1020304050607080UL) + +/* + * atomic_and_*() + */ +#define atf_and(NAME, TYPE, FMT) \ +ATF_TC(NAME); \ +ATF_TC_HEAD(NAME, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", #NAME); \ +} \ +ATF_TC_BODY(NAME, tc) \ +{ \ + volatile TYPE val; \ + TYPE src; \ + TYPE exp; \ + val = (TYPE)DST; \ + src = (TYPE)SRC; \ + exp = (TYPE)EXPECT; \ + NAME(&val, src); \ + ATF_REQUIRE_MSG(val == exp, \ + "val expects 0x%" FMT " but 0x%" FMT, exp, val); \ +} + +atf_and(atomic_and_32, uint32_t, PRIx32); +atf_and(atomic_and_uint, unsigned int, "x"); +atf_and(atomic_and_ulong, unsigned long, "lx"); +#if defined(__HAVE_ATOMIC64_OPS) +atf_and(atomic_and_64, uint64_t, PRIx64); +#endif + +/* + * atomic_and_*_nv() + */ +#define atf_and_nv(NAME, TYPE, FMT) \ +ATF_TC(NAME); \ +ATF_TC_HEAD(NAME, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", #NAME); \ +} \ +ATF_TC_BODY(NAME, tc) \ +{ \ + volatile TYPE val; \ + TYPE src; \ + TYPE res; \ + TYPE exp; \ + val = (TYPE)DST; \ + src = (TYPE)SRC; \ + exp = (TYPE)EXPECT; \ + res = NAME(&val, src); \ + ATF_REQUIRE_MSG(val == exp, \ + "val expects 0x%" FMT " but 0x%" FMT, exp, val); \ + ATF_REQUIRE_MSG(res == exp, \ + "res expects 0x%" FMT " but 0x%" FMT, exp, res); \ +} + +atf_and_nv(atomic_and_32_nv, uint32_t, PRIx32); +atf_and_nv(atomic_and_uint_nv, unsigned int, "x"); +atf_and_nv(atomic_and_ulong_nv, unsigned long, "lx"); +#if defined(__HAVE_ATOMIC64_OPS) +atf_and_nv(atomic_and_64_nv, uint64_t, PRIx64); +#endif + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, atomic_and_32); + ATF_TP_ADD_TC(tp, atomic_and_uint); + ATF_TP_ADD_TC(tp, atomic_and_ulong); +#if defined(__HAVE_ATOMIC64_OPS) + ATF_TP_ADD_TC(tp, atomic_and_64); +#endif + + ATF_TP_ADD_TC(tp, atomic_and_32_nv); + ATF_TP_ADD_TC(tp, atomic_and_uint_nv); + ATF_TP_ADD_TC(tp, atomic_and_ulong_nv); +#if defined(__HAVE_ATOMIC64_OPS) + ATF_TP_ADD_TC(tp, atomic_and_64_nv); +#endif + + return atf_no_error(); +} diff --git a/lib/libc/atomic/t_atomic_cas.c b/lib/libc/atomic/t_atomic_cas.c new file mode 100644 --- /dev/null +++ b/lib/libc/atomic/t_atomic_cas.c @@ -0,0 +1,118 @@ +/* $NetBSD: t_atomic_cas.c,v 1.1 2019/02/17 12:24:17 isaki Exp $ */ + +/* + * Copyright (C) 2019 Tetsuya Isaki. 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 ``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 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 +__RCSID("$NetBSD: t_atomic_cas.c,v 1.1 2019/02/17 12:24:17 isaki Exp $"); + +#include +#include +#include + +/* + * These tests don't examine the atomicity. + */ + +#define OLDVAL (0x1122334455667788UL) +#define NEWVAL (0x8090a0b0c0d0e0f0UL) + +/* + * atomic_cas_*{,_ni}() + */ +#define atf_cas(NAME, TYPE, FMT) \ +ATF_TC(NAME); \ +ATF_TC_HEAD(NAME, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", #NAME); \ +} \ +ATF_TC_BODY(NAME, tc) \ +{ \ + volatile TYPE val; \ + TYPE oldval; \ + TYPE newval; \ + TYPE expval; \ + TYPE expres; \ + TYPE res; \ + /* If successful */ \ + val = (TYPE)OLDVAL; \ + oldval = (TYPE)OLDVAL; \ + newval = (TYPE)NEWVAL; \ + expval = (TYPE)NEWVAL; \ + expres = (TYPE)OLDVAL; \ + res = NAME(&val, oldval, newval); \ + ATF_REQUIRE_MSG(val == expval, \ + "successful case: val expects " FMT " but " FMT, expval, val); \ + ATF_REQUIRE_MSG(res == expres, \ + "successful case: res expects " FMT " but " FMT, expres, res); \ + /* If failure */ \ + val = (TYPE)OLDVAL; \ + oldval = (TYPE)(OLDVAL + 1); \ + newval = (TYPE)NEWVAL; \ + expval = (TYPE)OLDVAL; \ + expres = (TYPE)OLDVAL; \ + res = NAME(&val, oldval, newval); \ + ATF_REQUIRE_MSG(val == expval, \ + "failure case: val expects " FMT " but " FMT, expval, val); \ + ATF_REQUIRE_MSG(res == expres, \ + "failure case: res expects " FMT " but " FMT, expres, res); \ +} + +atf_cas(atomic_cas_32, uint32_t, "0x%" PRIx32); +atf_cas(atomic_cas_uint, unsigned int, "0x%x"); +atf_cas(atomic_cas_ulong, unsigned long, "0x%lx"); +atf_cas(atomic_cas_ptr, void *, "%p"); +#if defined(__HAVE_ATOMIC64_OPS) +atf_cas(atomic_cas_64, uint64_t, "0x%" PRIx64); +#endif + +atf_cas(atomic_cas_32_ni, uint32_t, "0x%" PRIx32); +atf_cas(atomic_cas_uint_ni, unsigned int, "0x%x"); +atf_cas(atomic_cas_ulong_ni, unsigned long, "0x%lx"); +atf_cas(atomic_cas_ptr_ni, void *, "%p"); +#if defined(__HAVE_ATOMIC64_OPS) +atf_cas(atomic_cas_64_ni, uint64_t, "0x%" PRIx64); +#endif + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, atomic_cas_32); + ATF_TP_ADD_TC(tp, atomic_cas_uint); + ATF_TP_ADD_TC(tp, atomic_cas_ulong); + ATF_TP_ADD_TC(tp, atomic_cas_ptr); +#if defined(__HAVE_ATOMIC64_OPS) + ATF_TP_ADD_TC(tp, atomic_cas_64); +#endif + + ATF_TP_ADD_TC(tp, atomic_cas_32_ni); + ATF_TP_ADD_TC(tp, atomic_cas_uint_ni); + ATF_TP_ADD_TC(tp, atomic_cas_ulong_ni); + ATF_TP_ADD_TC(tp, atomic_cas_ptr_ni); +#if defined(__HAVE_ATOMIC64_OPS) + ATF_TP_ADD_TC(tp, atomic_cas_64_ni); +#endif + + return atf_no_error(); +} diff --git a/lib/libc/atomic/t_atomic_dec.c b/lib/libc/atomic/t_atomic_dec.c new file mode 100644 --- /dev/null +++ b/lib/libc/atomic/t_atomic_dec.c @@ -0,0 +1,120 @@ +/* $NetBSD: t_atomic_dec.c,v 1.1 2019/02/17 12:24:17 isaki Exp $ */ + +/* + * Copyright (C) 2019 Tetsuya Isaki. 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 ``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 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 +__RCSID("$NetBSD: t_atomic_dec.c,v 1.1 2019/02/17 12:24:17 isaki Exp $"); + +#include +#include +#include + +/* + * These tests don't examine the atomicity. + */ + +#define VAL (0x1122334455667788UL) +#define EXPECT (0x1122334455667787UL) + +/* + * atomic_dec_*() + */ +#define atf_dec(NAME, TYPE, FMT) \ +ATF_TC(NAME); \ +ATF_TC_HEAD(NAME, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", #NAME); \ +} \ +ATF_TC_BODY(NAME, tc) \ +{ \ + volatile TYPE val; \ + TYPE exp; \ + val = (TYPE)VAL; \ + exp = (TYPE)EXPECT; \ + NAME(&val); \ + ATF_REQUIRE_MSG(val == exp, \ + "val expects " FMT " but " FMT, exp, val); \ +} + +atf_dec(atomic_dec_32, uint32_t, "0x%" PRIx32); +atf_dec(atomic_dec_uint, unsigned int, "0x%x"); +atf_dec(atomic_dec_ulong, unsigned long, "0x%lx"); +atf_dec(atomic_dec_ptr, void *, "%p"); +#if defined(__HAVE_ATOMIC64_OPS) +atf_dec(atomic_dec_64, uint64_t, "0x%" PRIx64); +#endif + +/* + * atomic_dec_*_nv() + */ +#define atf_dec_nv(NAME, TYPE, FMT) \ +ATF_TC(NAME); \ +ATF_TC_HEAD(NAME, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", #NAME); \ +} \ +ATF_TC_BODY(NAME, tc) \ +{ \ + volatile TYPE val; \ + TYPE res; \ + TYPE exp; \ + val = (TYPE)VAL; \ + exp = (TYPE)EXPECT; \ + res = NAME(&val); \ + ATF_REQUIRE_MSG(val == exp, \ + "val expects " FMT " but " FMT, exp, val); \ + ATF_REQUIRE_MSG(res == exp, \ + "res expects " FMT " but " FMT, exp, res); \ +} + +atf_dec_nv(atomic_dec_32_nv, uint32_t, "0x%" PRIx32); +atf_dec_nv(atomic_dec_uint_nv, unsigned int, "0x%x"); +atf_dec_nv(atomic_dec_ulong_nv, unsigned long, "0x%lx"); +atf_dec_nv(atomic_dec_ptr_nv, void *, "%p"); +#if defined(__HAVE_ATOMIC64_OPS) +atf_dec_nv(atomic_dec_64_nv, uint64_t, "0x%" PRIx64); +#endif + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, atomic_dec_32); + ATF_TP_ADD_TC(tp, atomic_dec_uint); + ATF_TP_ADD_TC(tp, atomic_dec_ulong); + ATF_TP_ADD_TC(tp, atomic_dec_ptr); +#if defined(__HAVE_ATOMIC64_OPS) + ATF_TP_ADD_TC(tp, atomic_dec_64); +#endif + + ATF_TP_ADD_TC(tp, atomic_dec_32_nv); + ATF_TP_ADD_TC(tp, atomic_dec_uint_nv); + ATF_TP_ADD_TC(tp, atomic_dec_ulong_nv); + ATF_TP_ADD_TC(tp, atomic_dec_ptr_nv); +#if defined(__HAVE_ATOMIC64_OPS) + ATF_TP_ADD_TC(tp, atomic_dec_64_nv); +#endif + + return atf_no_error(); +} diff --git a/lib/libc/atomic/t_atomic_inc.c b/lib/libc/atomic/t_atomic_inc.c new file mode 100644 --- /dev/null +++ b/lib/libc/atomic/t_atomic_inc.c @@ -0,0 +1,120 @@ +/* $NetBSD: t_atomic_inc.c,v 1.1 2019/02/17 12:24:17 isaki Exp $ */ + +/* + * Copyright (C) 2019 Tetsuya Isaki. 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 ``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 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 +__RCSID("$NetBSD: t_atomic_inc.c,v 1.1 2019/02/17 12:24:17 isaki Exp $"); + +#include +#include +#include + +/* + * These tests don't examine the atomicity. + */ + +#define VAL (0x1122334455667788UL) +#define EXPECT (0x1122334455667789UL) + +/* + * atomic_inc_*() + */ +#define atf_inc(NAME, TYPE, FMT) \ +ATF_TC(NAME); \ +ATF_TC_HEAD(NAME, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", #NAME); \ +} \ +ATF_TC_BODY(NAME, tc) \ +{ \ + volatile TYPE val; \ + TYPE exp; \ + val = (TYPE)VAL; \ + exp = (TYPE)EXPECT; \ + NAME(&val); \ + ATF_REQUIRE_MSG(val == exp, \ + "val expects " FMT " but " FMT, exp, val); \ +} + +atf_inc(atomic_inc_32, uint32_t, "0x%" PRIx32); +atf_inc(atomic_inc_uint, unsigned int, "0x%x"); +atf_inc(atomic_inc_ulong, unsigned long, "0x%lx"); +atf_inc(atomic_inc_ptr, void *, "%p"); +#if defined(__HAVE_ATOMIC64_OPS) +atf_inc(atomic_inc_64, uint64_t, "0x%" PRIx64); +#endif + +/* + * atomic_inc_*_nv() + */ +#define atf_inc_nv(NAME, TYPE, FMT) \ +ATF_TC(NAME); \ +ATF_TC_HEAD(NAME, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", #NAME); \ +} \ +ATF_TC_BODY(NAME, tc) \ +{ \ + volatile TYPE val; \ + TYPE res; \ + TYPE exp; \ + val = (TYPE)VAL; \ + exp = (TYPE)EXPECT; \ + res = NAME(&val); \ + ATF_REQUIRE_MSG(val == exp, \ + "val expects " FMT " but " FMT, exp, val); \ + ATF_REQUIRE_MSG(res == exp, \ + "res expects " FMT " but " FMT, exp, res); \ +} + +atf_inc_nv(atomic_inc_32_nv, uint32_t, "0x%" PRIx32); +atf_inc_nv(atomic_inc_uint_nv, unsigned int, "0x%x"); +atf_inc_nv(atomic_inc_ulong_nv, unsigned long, "0x%lx"); +atf_inc_nv(atomic_inc_ptr_nv, void *, "%p"); +#if defined(__HAVE_ATOMIC64_OPS) +atf_inc_nv(atomic_inc_64_nv, uint64_t, "0x%" PRIx64); +#endif + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, atomic_inc_32); + ATF_TP_ADD_TC(tp, atomic_inc_uint); + ATF_TP_ADD_TC(tp, atomic_inc_ulong); + ATF_TP_ADD_TC(tp, atomic_inc_ptr); +#if defined(__HAVE_ATOMIC64_OPS) + ATF_TP_ADD_TC(tp, atomic_inc_64); +#endif + + ATF_TP_ADD_TC(tp, atomic_inc_32_nv); + ATF_TP_ADD_TC(tp, atomic_inc_uint_nv); + ATF_TP_ADD_TC(tp, atomic_inc_ulong_nv); + ATF_TP_ADD_TC(tp, atomic_inc_ptr_nv); +#if defined(__HAVE_ATOMIC64_OPS) + ATF_TP_ADD_TC(tp, atomic_inc_64_nv); +#endif + + return atf_no_error(); +} diff --git a/lib/libc/atomic/t_atomic_or.c b/lib/libc/atomic/t_atomic_or.c new file mode 100644 --- /dev/null +++ b/lib/libc/atomic/t_atomic_or.c @@ -0,0 +1,121 @@ +/* $NetBSD: t_atomic_or.c,v 1.1 2019/02/17 12:24:17 isaki Exp $ */ + +/* + * Copyright (C) 2019 Tetsuya Isaki. 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 ``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 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 +__RCSID("$NetBSD: t_atomic_or.c,v 1.1 2019/02/17 12:24:17 isaki Exp $"); + +#include +#include +#include + +/* + * These tests don't examine the atomicity. + */ + +#define DST (0x1122334455667788UL) +#define SRC (0xf0f0f0f0f0f0f0f0UL) +#define EXPECT (0xf1f2f3f4f5f6f7f8UL) + +/* + * atomic_or_*() + */ +#define atf_or(NAME, TYPE, FMT) \ +ATF_TC(NAME); \ +ATF_TC_HEAD(NAME, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", #NAME); \ +} \ +ATF_TC_BODY(NAME, tc) \ +{ \ + volatile TYPE val; \ + TYPE src; \ + TYPE exp; \ + val = (TYPE)DST; \ + src = (TYPE)SRC; \ + exp = (TYPE)EXPECT; \ + NAME(&val, src); \ + ATF_REQUIRE_MSG(val == exp, \ + "val expects 0x%" FMT " but 0x%" FMT, exp, val); \ +} + +atf_or(atomic_or_32, uint32_t, PRIx32); +atf_or(atomic_or_uint, unsigned int, "x"); +atf_or(atomic_or_ulong, unsigned long, "lx"); +#if defined(__HAVE_ATOMIC64_OPS) +atf_or(atomic_or_64, uint64_t, PRIx64); +#endif + +/* + * atomic_or_*_nv() + */ +#define atf_or_nv(NAME, TYPE, FMT) \ +ATF_TC(NAME); \ +ATF_TC_HEAD(NAME, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", #NAME); \ +} \ +ATF_TC_BODY(NAME, tc) \ +{ \ + volatile TYPE val; \ + TYPE src; \ + TYPE res; \ + TYPE exp; \ + val = (TYPE)DST; \ + src = (TYPE)SRC; \ + exp = (TYPE)EXPECT; \ + res = NAME(&val, src); \ + ATF_REQUIRE_MSG(val == exp, \ + "val expects 0x%" FMT " but 0x%" FMT, exp, val); \ + ATF_REQUIRE_MSG(res == exp, \ + "res expects 0x%" FMT " but 0x%" FMT, exp, res); \ +} + +atf_or_nv(atomic_or_32_nv, uint32_t, PRIx32); +atf_or_nv(atomic_or_uint_nv, unsigned int, "x"); +atf_or_nv(atomic_or_ulong_nv, unsigned long, "lx"); +#if defined(__HAVE_ATOMIC64_OPS) +atf_or_nv(atomic_or_64_nv, uint64_t, PRIx64); +#endif + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, atomic_or_32); + ATF_TP_ADD_TC(tp, atomic_or_uint); + ATF_TP_ADD_TC(tp, atomic_or_ulong); +#if defined(__HAVE_ATOMIC64_OPS) + ATF_TP_ADD_TC(tp, atomic_or_64); +#endif + + ATF_TP_ADD_TC(tp, atomic_or_32_nv); + ATF_TP_ADD_TC(tp, atomic_or_uint_nv); + ATF_TP_ADD_TC(tp, atomic_or_ulong_nv); +#if defined(__HAVE_ATOMIC64_OPS) + ATF_TP_ADD_TC(tp, atomic_or_64_nv); +#endif + + return atf_no_error(); +} diff --git a/lib/libc/atomic/t_atomic_swap.c b/lib/libc/atomic/t_atomic_swap.c new file mode 100644 --- /dev/null +++ b/lib/libc/atomic/t_atomic_swap.c @@ -0,0 +1,88 @@ +/* $NetBSD: t_atomic_swap.c,v 1.1 2019/02/17 12:24:17 isaki Exp $ */ + +/* + * Copyright (C) 2019 Tetsuya Isaki. 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 ``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 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 +__RCSID("$NetBSD: t_atomic_swap.c,v 1.1 2019/02/17 12:24:17 isaki Exp $"); + +#include +#include +#include + +/* + * These tests don't examine the atomicity. + */ + +#define OLDVAL (0x1122334455667788UL) +#define NEWVAL (0x8090a0b0c0d0e0f0UL) + +/* + * atomic_swap_*() + */ +#define atf_swap(NAME, TYPE, FMT) \ +ATF_TC(NAME); \ +ATF_TC_HEAD(NAME, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", #NAME); \ +} \ +ATF_TC_BODY(NAME, tc) \ +{ \ + volatile TYPE val; \ + TYPE newval; \ + TYPE expval; \ + TYPE expres; \ + TYPE res; \ + val = (TYPE)OLDVAL; \ + newval = (TYPE)NEWVAL; \ + expval = (TYPE)NEWVAL; \ + expres = (TYPE)OLDVAL; \ + res = NAME(&val, newval); \ + ATF_REQUIRE_MSG(val == expval, \ + "val expects " FMT " but " FMT, expval, val); \ + ATF_REQUIRE_MSG(res == expres, \ + "res expects " FMT " but " FMT, expres, res); \ +} + +atf_swap(atomic_swap_32, uint32_t, "0x%" PRIx32); +atf_swap(atomic_swap_uint, unsigned int, "0x%x"); +atf_swap(atomic_swap_ulong, unsigned long, "0x%lx"); +atf_swap(atomic_swap_ptr, void *, "%p"); +#if defined(__HAVE_ATOMIC64_OPS) +atf_swap(atomic_swap_64, uint64_t, "0x%" PRIx64); +#endif + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, atomic_swap_32); + ATF_TP_ADD_TC(tp, atomic_swap_uint); + ATF_TP_ADD_TC(tp, atomic_swap_ulong); + ATF_TP_ADD_TC(tp, atomic_swap_ptr); +#if defined(__HAVE_ATOMIC64_OPS) + ATF_TP_ADD_TC(tp, atomic_swap_64); +#endif + + return atf_no_error(); +} diff --git a/lib/libc/c063/Makefile b/lib/libc/c063/Makefile --- a/lib/libc/c063/Makefile +++ b/lib/libc/c063/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.1 2012/11/18 17:41:54 manu Exp $ +# $NetBSD: Makefile,v 1.2 2019/09/15 23:39:13 christos Exp $ .include @@ -21,6 +21,4 @@ TESTS_C+= t_unlinkat TESTS_C+= t_utimensat -COPTS+= -D_INCOMPLETE_XOPEN_C063 - .include diff --git a/lib/libc/c063/t_mkfifoat.c b/lib/libc/c063/t_mkfifoat.c --- a/lib/libc/c063/t_mkfifoat.c +++ b/lib/libc/c063/t_mkfifoat.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_mkfifoat.c,v 1.4 2017/01/14 20:55:26 christos Exp $ */ +/* $NetBSD: t_mkfifoat.c,v 1.5 2019/06/20 03:31:53 kamil Exp $ */ /*- * Copyright (c) 2012 The NetBSD Foundation, Inc. @@ -29,7 +29,7 @@ * POSSIBILITY OF SUCH DAMAGE. */ #include -__RCSID("$NetBSD: t_mkfifoat.c,v 1.4 2017/01/14 20:55:26 christos Exp $"); +__RCSID("$NetBSD: t_mkfifoat.c,v 1.5 2019/06/20 03:31:53 kamil Exp $"); #include #include @@ -108,6 +108,32 @@ ATF_REQUIRE(mkfifoat(-1, FIFO, mode) == -1); } +ATF_TC(mknodat_s_ififo); +ATF_TC_HEAD(mknodat_s_ififo, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test mknodat(2) with S_IFIFO"); +} + +ATF_TC_BODY(mknodat_s_ififo, tc) +{ + struct stat st; + int dfd; + mode_t mode = S_IFIFO | 0600; + + (void)memset(&st, 0, sizeof(struct stat)); + + ATF_REQUIRE(mkdir(DIR, 0755) == 0); + ATF_REQUIRE((dfd = open(DIR, O_RDONLY, 0)) != -1); + ATF_REQUIRE(mknodat(dfd, BASEFIFO, mode, 0) != -1); + ATF_REQUIRE(access(FIFO, F_OK) == 0); + ATF_REQUIRE(stat(FIFO, &st) == 0); + + if (S_ISFIFO(st.st_mode) == 0) + atf_tc_fail("invalid mode from mknodat(2) with S_IFIFO"); + + (void)close(dfd); +} + ATF_TP_ADD_TCS(tp) { @@ -115,6 +141,7 @@ ATF_TP_ADD_TC(tp, mkfifoat_fdcwd); ATF_TP_ADD_TC(tp, mkfifoat_fdcwderr); ATF_TP_ADD_TC(tp, mkfifoat_fderr); + ATF_TP_ADD_TC(tp, mknodat_s_ififo); return atf_no_error(); } diff --git a/lib/libc/db/h_lfsr.c b/lib/libc/db/h_lfsr.c --- a/lib/libc/db/h_lfsr.c +++ b/lib/libc/db/h_lfsr.c @@ -27,7 +27,7 @@ * POSSIBILITY OF SUCH DAMAGE. */ #include -__RCSID("$NetBSD: h_lfsr.c,v 1.1 2015/11/18 18:35:35 christos Exp $"); +__RCSID("$NetBSD: h_lfsr.c,v 1.2 2017/06/24 10:25:23 gson Exp $"); #include #include @@ -37,7 +37,7 @@ #include #include -#define MAXKEY 0xffff +#define MAXKEY 1000 #ifdef DEBUG #define DPRINTF(...) printf(__VA_ARGS__) #else diff --git a/lib/libc/db/t_db.sh b/lib/libc/db/t_db.sh old mode 100755 new mode 100644 --- a/lib/libc/db/t_db.sh +++ b/lib/libc/db/t_db.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_db.sh,v 1.7 2016/09/24 20:12:33 christos Exp $ +# $NetBSD: t_db.sh,v 1.9 2020/03/12 14:10:59 martin Exp $ # # Copyright (c) 2008 The NetBSD Foundation, Inc. # All rights reserved. @@ -919,7 +919,15 @@ { TMPDIR="$(pwd)/db_dir"; export TMPDIR mkdir ${TMPDIR} - for i in 2048 4096 8192 16384 32768 65536 + AVAIL=$( df -m ${TMPDIR} | awk '{if (int($4) > 0) print $4}' ) + LIST="2048 4096 8192 16384" + if [ $AVAIL -gt 30 ]; then + LIST="$LIST 32768" + fi + if [ $AVAIL -gt 60 ]; then + LIST="$LIST 65536" + fi + for i in $LIST do atf_check "$(prog_lfsr)" $i done @@ -934,6 +942,7 @@ "be the only item on the left page results in index 0 of " \ "the right page being erroneously skipped; this only " \ "happens with one particular key+data length for each page size." + atf_set "timeout" "900" } btree_weird_page_split_body() { diff --git a/lib/libc/db/t_db_hash_seq.c b/lib/libc/db/t_db_hash_seq.c --- a/lib/libc/db/t_db_hash_seq.c +++ b/lib/libc/db/t_db_hash_seq.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_db_hash_seq.c,v 1.2 2015/06/22 22:35:51 christos Exp $ */ +/* $NetBSD: t_db_hash_seq.c,v 1.4 2020/09/07 00:28:44 mrg Exp $ */ /*- * Copyright (c) 2015 The NetBSD Foundation, Inc. @@ -29,7 +29,7 @@ * POSSIBILITY OF SUCH DAMAGE. */ #include -__RCSID("$NetBSD: t_db_hash_seq.c,v 1.2 2015/06/22 22:35:51 christos Exp $"); +__RCSID("$NetBSD: t_db_hash_seq.c,v 1.4 2020/09/07 00:28:44 mrg Exp $"); #include #include @@ -107,7 +107,8 @@ if (db == NULL) { if (errno == ENOENT && (flags & O_CREAT) == 0) return NULL; - DO_ERR("%s: can't open `%s'", __func__, dbname); + DO_ERR("%s: can't open `%s'", __func__, + dbname ? dbname : ""); } return db; } @@ -338,6 +339,6 @@ ATF_TP_ADD_TC(tp, test_hash_del_alt); ATF_TP_ADD_TC(tp, test_hash_del_every_7); - return 0; + return atf_no_error(); } #endif diff --git a/lib/libc/gen/Makefile b/lib/libc/gen/Makefile --- a/lib/libc/gen/Makefile +++ b/lib/libc/gen/Makefile @@ -1,10 +1,9 @@ -# $NetBSD: Makefile,v 1.50 2016/12/09 06:12:02 kamil Exp $ +# $NetBSD: Makefile,v 1.54 2020/02/22 19:14:57 kamil Exp $ .include TESTSDIR= ${TESTSBASE}/lib/libc/gen -TESTS_SUBDIRS= exect TESTS_SUBDIRS+= execve TESTS_SUBDIRS+= posix_spawn @@ -40,7 +39,12 @@ TESTS_C+= t_ttyname TESTS_C+= t_vis +.if ${MKSANITIZER:Uno} != "yes" && ${MKLIBCSANITIZER:Uno} != "yes" +COPTS.t_siginfo.c+= -DENABLE_TESTS +.endif + CPPFLAGS.t_siginfo.c+=-D__TEST_FENV +COPTS.t_fpsetround.c+=${${ACTIVE_CC} == "gcc":? -frounding-math :} LDADD.t_siginfo+= -lm DPADD.t_siginfo+= ${LIBM} diff --git a/lib/libc/gen/exect/t_exect.c b/lib/libc/gen/exect/t_exect.c deleted file mode 100644 --- a/lib/libc/gen/exect/t_exect.c +++ /dev/null @@ -1,90 +0,0 @@ -/* $NetBSD: t_exect.c,v 1.6 2016/12/12 10:34:55 joerg Exp $ */ - -/*- - * Copyright (c) 2014 The NetBSD Foundation, Inc. - * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 - -#include -#include -#include -#include -#include - -ATF_TC(t_exect_null); - -ATF_TC_HEAD(t_exect_null, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Tests an empty exect(2) executing"); -} - -static volatile sig_atomic_t caught = 0; - -static void -sigtrap_handler(int sig, siginfo_t *info, void *ctx) -{ - ATF_REQUIRE_EQ(sig, SIGTRAP); - ATF_REQUIRE_EQ(info->si_code, TRAP_TRACE); - - ++caught; -} - -ATF_TC_BODY(t_exect_null, tc) -{ - struct sigaction act; - - /* - * Currently exect(3) is misdesigned -- see PR port-amd64/51700 and it - * needs to be redone from scratch. - * - * This test affects amd64 releng machines causing tests to hang or - * fail. As there is little point to test interface that is still not, - * designed and implemented and is breaking tests - skip it - * unconditionally for all ports. - */ - /* Prevent static analysis from requiring t_exec_null to be __dead. */ - if (!caught) - atf_tc_skip("exect(3) misdesigned and hangs - PR port-amd64/51700"); - - ATF_REQUIRE(sigemptyset(&act.sa_mask) == 0); - act.sa_sigaction = sigtrap_handler; - act.sa_flags = SA_SIGINFO; - - ATF_REQUIRE(sigaction(SIGTRAP, &act, 0) == 0); - - ATF_REQUIRE_ERRNO(EFAULT, exect(NULL, NULL, NULL) == -1); - - ATF_REQUIRE_EQ_MSG(caught, 1, "expected caught (1) != received (%d)", - (int)caught); -} - -ATF_TP_ADD_TCS(tp) -{ - ATF_TP_ADD_TC(tp, t_exect_null); - - return atf_no_error(); -} diff --git a/lib/libc/gen/isqemu.h b/lib/libc/gen/isqemu.h --- a/lib/libc/gen/isqemu.h +++ b/lib/libc/gen/isqemu.h @@ -1,4 +1,4 @@ -/* $NetBSD: isqemu.h,v 1.4 2015/01/03 14:21:05 gson Exp $ */ +/* $NetBSD: isqemu.h,v 1.5 2020/08/23 11:00:18 gson Exp $ */ /*- * Copyright (c) 2013 The NetBSD Foundation, Inc. @@ -33,14 +33,36 @@ */ #include #include +#include +#include +#include #include #include #include #include #include +#include static __inline bool isQEMU(void) { + struct devlistargs dla = { + .l_devname = "qemufwcfg0", + .l_childname = NULL, + .l_children = 0, + }; + int fd = open(DRVCTLDEV, O_RDONLY, 0); + if (fd == -1) + return false; + if (ioctl(fd, DRVLISTDEV, &dla) == -1) { + close(fd); + return false; + } + close(fd); + return true; +} + +static __inline bool +isQEMU_TCG(void) { #if defined(__i386__) || defined(__x86_64__) char name[1024]; size_t len = sizeof(name); diff --git a/lib/libc/gen/posix_spawn/h_nonexec.sh b/lib/libc/gen/posix_spawn/h_nonexec.sh old mode 100755 new mode 100644 diff --git a/lib/libc/gen/posix_spawn/h_spawn.c b/lib/libc/gen/posix_spawn/h_spawn.c --- a/lib/libc/gen/posix_spawn/h_spawn.c +++ b/lib/libc/gen/posix_spawn/h_spawn.c @@ -1,4 +1,4 @@ -/* $NetBSD: h_spawn.c,v 1.1 2012/02/13 21:03:08 martin Exp $ */ +/* $NetBSD: h_spawn.c,v 1.2 2021/05/02 11:18:11 martin Exp $ */ /*- * Copyright (c) 2012 The NetBSD Foundation, Inc. @@ -32,6 +32,8 @@ #include #include +#include +#include int main(int argc, char **argv) @@ -39,11 +41,25 @@ unsigned long ret; char *endp; - if (argc < 2) { + if (argc == 2 && strcmp(argv[1], "--resetids") == 0) { + if (getuid() != geteuid() || getgid() != getegid()) { + fprintf(stderr, "uid/gid do not match effective ids, " + "uid: %d euid: %d gid: %d egid: %d\n", + getuid(), geteuid(), getgid(), getegid()); + exit(255); + } + return 0; + } else if (argc != 2) { fprintf(stderr, "usage:\n\t%s (retcode)\n", getprogname()); exit(255); } ret = strtoul(argv[1], &endp, 10); + if (*endp != 0) { + fprintf(stderr, + "invalid arg: %s\n" + "usage:\n\t%s (retcode)\n", endp, getprogname()); + exit(255); + } fprintf(stderr, "%s exiting with status %lu\n", getprogname(), ret); return ret; diff --git a/lib/libc/gen/posix_spawn/h_spawnattr.c b/lib/libc/gen/posix_spawn/h_spawnattr.c --- a/lib/libc/gen/posix_spawn/h_spawnattr.c +++ b/lib/libc/gen/posix_spawn/h_spawnattr.c @@ -1,4 +1,4 @@ -/* $NetBSD: h_spawnattr.c,v 1.1 2012/02/13 21:03:08 martin Exp $ */ +/* $NetBSD: h_spawnattr.c,v 1.2 2021/08/21 23:00:32 andvar Exp $ */ /*- * Copyright (c) 2012 The NetBSD Foundation, Inc. @@ -38,7 +38,7 @@ /* * Helper to test the hardcoded assumptions from t_spawnattr.c - * Exit with apropriate exit status and print diagnostics to + * Exit with appropriate exit status and print diagnostics to * stderr explaining what is wrong. */ int diff --git a/lib/libc/gen/posix_spawn/t_spawnattr.c b/lib/libc/gen/posix_spawn/t_spawnattr.c --- a/lib/libc/gen/posix_spawn/t_spawnattr.c +++ b/lib/libc/gen/posix_spawn/t_spawnattr.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_spawnattr.c,v 1.1 2012/02/13 21:03:08 martin Exp $ */ +/* $NetBSD: t_spawnattr.c,v 1.4 2021/05/02 11:18:11 martin Exp $ */ /*- * Copyright (c) 2012 The NetBSD Foundation, Inc. @@ -45,37 +45,33 @@ #define MAX(a, b) (a) > (b) ? (a) : (b) #define MIN(a, b) (a) > (b) ? (b) : (a) -static int get_different_scheduler(void); -static int get_different_priority(void); - static int -get_different_scheduler() +get_different_scheduler(void) { - int scheduler, max, min, new; - - max = MAX(MAX(SCHED_FIFO, SCHED_OTHER), SCHED_RR); - min = MIN(MIN(SCHED_FIFO, SCHED_OTHER), SCHED_RR); + /* + * We don't want to use SCHED_OTHER because it does not have + * different priorities. + */ /* get current schedule policy */ - scheduler = sched_getscheduler(0); - - /* new scheduler */ - new = (scheduler + 1); - if (new > max) - new = min; - - return new; + switch (sched_getscheduler(0)) { + case SCHED_RR: + return SCHED_FIFO; + case SCHED_FIFO: + case SCHED_OTHER: + return SCHED_RR; + default: + abort(); + } } static int -get_different_priority() +get_different_priority(int scheduler) { - int scheduler, max, min, new, priority; + int min, max, new, priority; struct sched_param param; - /* get current schedule policy */ - scheduler = sched_getscheduler(0); - + /* Get the priority range for the new scheduler */ max = sched_get_priority_max(scheduler); min = sched_get_priority_min(scheduler); @@ -83,10 +79,13 @@ priority = param.sched_priority; /* new schedule policy */ - new = (priority + 1); - if (new > max) - new = min; + for (new = min; new <= max; new++) + if (priority != new) + break; + ATF_REQUIRE_MSG(priority != new, "could not find different priority"); + printf("min %d max %d for scheduler %d, returning %d\n", + min, max, scheduler, new); return new; } @@ -119,8 +118,9 @@ posix_spawnattr_init(&attr); scheduler = get_different_scheduler(); - priority = get_different_priority(); + priority = get_different_priority(scheduler); sp.sched_priority = priority; + printf("using scheduler %d, priority %d\n", scheduler, priority); sigemptyset(&sig); sigaddset(&sig, SIGUSR1); @@ -165,9 +165,43 @@ posix_spawnattr_destroy(&attr); } +ATF_TC(t_spawn_resetids); + +ATF_TC_HEAD(t_spawn_resetids, tc) +{ + atf_tc_set_md_var(tc, "descr", + "posix_spawn a child and with POSIX_SPAWN_RESETIDS flag"); +} + +ATF_TC_BODY(t_spawn_resetids, tc) +{ + char buf[FILENAME_MAX]; + char * const args[] = { + __UNCONST("h_spawn"), __UNCONST("--resetids"), NULL + }; + posix_spawnattr_t attr; + int err, status; + pid_t pid; + + posix_spawnattr_init(&attr); + posix_spawnattr_setflags(&attr, POSIX_SPAWN_RESETIDS); + + snprintf(buf, sizeof buf, "%s/h_spawn", + atf_tc_get_config_var(tc, "srcdir")); + + err = posix_spawn(&pid, buf, NULL, &attr, args, NULL); + ATF_REQUIRE(err == 0); + ATF_REQUIRE(pid > 0); + waitpid(pid, &status, 0); + ATF_REQUIRE(WIFEXITED(status) && WEXITSTATUS(status) == 0); + + posix_spawnattr_destroy(&attr); +} + ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, t_spawnattr); + ATF_TP_ADD_TC(tp, t_spawn_resetids); return atf_no_error(); } diff --git a/lib/libc/gen/t_dir.c b/lib/libc/gen/t_dir.c --- a/lib/libc/gen/t_dir.c +++ b/lib/libc/gen/t_dir.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_dir.c,v 1.10 2017/01/11 18:15:02 christos Exp $ */ +/* $NetBSD: t_dir.c,v 1.11 2018/06/19 09:20:46 gson Exp $ */ /*- * Copyright (c) 2010 The NetBSD Foundation, Inc. @@ -61,7 +61,7 @@ "creat(%s, %x) failed: %s", (x), (m), \ strerror(errno)); \ (void)close(_creat_fd); \ - } while(0); + } while (0) ATF_REQUIRE_MSG(mkdir("t", 0755) == 0, "mkdir failed: %s", strerror(errno)); diff --git a/lib/libc/gen/t_fmtcheck.c b/lib/libc/gen/t_fmtcheck.c --- a/lib/libc/gen/t_fmtcheck.c +++ b/lib/libc/gen/t_fmtcheck.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_fmtcheck.c,v 1.3 2014/06/14 08:19:02 apb Exp $ */ +/* $NetBSD: t_fmtcheck.c,v 1.5 2017/12/13 06:47:04 rin Exp $ */ /*- * Copyright (c) 2000 The NetBSD Foundation, Inc. diff --git a/lib/libc/gen/t_fpsetmask.c b/lib/libc/gen/t_fpsetmask.c --- a/lib/libc/gen/t_fpsetmask.c +++ b/lib/libc/gen/t_fpsetmask.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_fpsetmask.c,v 1.16 2016/03/12 11:55:14 martin Exp $ */ +/* $NetBSD: t_fpsetmask.c,v 1.21 2020/08/23 11:04:58 gson Exp $ */ /*- * Copyright (c) 1995 The NetBSD Foundation, Inc. @@ -58,15 +58,15 @@ #include -#if __arm__ && !__SOFTFP__ +#if (__arm__ && !__SOFTFP__) || __aarch64__ /* - * Some NEON fpus do not implement IEEE exception handling, + * Some NEON fpus do not trap on IEEE 754 FP exceptions. * skip these tests if running on them and compiled for * hard float. */ #define FPU_PREREQ() \ if (0 == fpsetmask(fpsetmask(FP_X_INV))) \ - atf_tc_skip("FPU does not implement exception handling"); + atf_tc_skip("FPU does not implement traps on FP exceptions"); #endif #ifndef FPU_PREREQ @@ -311,10 +311,7 @@ \ FPU_PREREQ(); \ \ - if (strcmp(MACHINE, "macppc") == 0) \ - atf_tc_expect_fail("PR port-macppc/46319"); \ - \ - if (isQEMU()) \ + if (isQEMU_TCG()) \ atf_tc_expect_fail("PR misc/44767"); \ \ m(t##_ops); \ diff --git a/lib/libc/gen/t_ftok.c b/lib/libc/gen/t_ftok.c --- a/lib/libc/gen/t_ftok.c +++ b/lib/libc/gen/t_ftok.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_ftok.c,v 1.2 2017/01/10 15:19:52 christos Exp $ */ +/* $NetBSD: t_ftok.c,v 1.3 2019/07/16 17:29:18 martin Exp $ */ /*- * Copyright (c) 2011 The NetBSD Foundation, Inc. @@ -29,7 +29,7 @@ * POSSIBILITY OF SUCH DAMAGE. */ #include -__RCSID("$NetBSD: t_ftok.c,v 1.2 2017/01/10 15:19:52 christos Exp $"); +__RCSID("$NetBSD: t_ftok.c,v 1.3 2019/07/16 17:29:18 martin Exp $"); #include #include @@ -65,7 +65,7 @@ key_t k1, k2, k3; int fd; - fd = open(path, O_RDONLY | O_CREAT); + fd = open(path, O_RDONLY | O_CREAT, 0600); ATF_REQUIRE(fd >= 0); (void)close(fd); diff --git a/lib/libc/gen/t_glob.c b/lib/libc/gen/t_glob.c --- a/lib/libc/gen/t_glob.c +++ b/lib/libc/gen/t_glob.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_glob.c,v 1.5 2017/01/14 20:47:41 christos Exp $ */ +/* $NetBSD: t_glob.c,v 1.10 2020/03/13 23:27:54 rillig Exp $ */ /*- * Copyright (c) 2010 The NetBSD Foundation, Inc. * All rights reserved. @@ -32,7 +32,7 @@ */ #include -__RCSID("$NetBSD: t_glob.c,v 1.5 2017/01/14 20:47:41 christos Exp $"); +__RCSID("$NetBSD: t_glob.c,v 1.10 2020/03/13 23:27:54 rillig Exp $"); #include @@ -41,6 +41,7 @@ #include #include +#include #include #include #include @@ -55,42 +56,50 @@ #define DPRINTF(a) #endif -struct gl_file { +struct vfs_file { + char type; /* 'd' or '-', like in ls(1) */ const char *name; - int dir; }; -static struct gl_file a[] = { - { "1", 0 }, - { "b", 1 }, - { "3", 0 }, - { "4", 0 }, +static struct vfs_file a[] = { + { '-', "1" }, + { 'd', "b" }, + { '-', "3" }, + { '-', "4" }, }; -static struct gl_file b[] = { - { "x", 0 }, - { "y", 0 }, - { "z", 0 }, - { "w", 0 }, +static struct vfs_file b[] = { + { '-', "x" }, + { '-', "y" }, + { '-', "z" }, + { '-', "w" }, }; -struct gl_dir { - const char *name; /* directory name */ - const struct gl_file *dir; - size_t len, pos; +static struct vfs_file hidden_dir[] = { + { '-', "visible-file" }, + { '-', ".hidden-file" }, }; -static struct gl_dir d[] = { - { "a", a, __arraycount(a), 0 }, - { "a/b", b, __arraycount(b), 0 }, +static struct vfs_file dot[] = { + { 'd', "a" }, + { 'd', ".hidden-dir" }, }; -static const char *glob_star[] = { - "a/1", "a/3", "a/4", "a/b", "a/b/w", "a/b/x", "a/b/y", "a/b/z", +struct vfs_dir { + const char *name; /* full directory name */ + const struct vfs_file *entries; + size_t entries_len; + size_t pos; /* only between opendir/closedir */ }; -static const char *glob_star_not[] = { - "a/1", "a/3", "a/4", "a/b", +#define VFS_DIR_INIT(name, entries) \ + { name, entries, __arraycount(entries), 0 } + +static struct vfs_dir d[] = { + VFS_DIR_INIT("a", a), + VFS_DIR_INIT("a/b", b), + VFS_DIR_INIT(".", dot), + VFS_DIR_INIT(".hidden-dir", hidden_dir), }; static void @@ -105,7 +114,7 @@ } static void * -gl_opendir(const char *dir) +vfs_opendir(const char *dir) { size_t i; char buf[MAXPATHLEN]; @@ -113,24 +122,25 @@ for (i = 0; i < __arraycount(d); i++) if (strcmp(buf, d[i].name) == 0) { - DPRINTF(("opendir %s %zu\n", buf, i)); + DPRINTF(("opendir %s %p\n", buf, &d[i])); return &d[i]; } + DPRINTF(("opendir %s ENOENT\n", buf)); errno = ENOENT; return NULL; } static struct dirent * -gl_readdir(void *v) +vfs_readdir(void *v) { static struct dirent dir; - struct gl_dir *dd = v; - if (dd->pos < dd->len) { - const struct gl_file *f = &dd->dir[dd->pos++]; + struct vfs_dir *dd = v; + if (dd->pos < dd->entries_len) { + const struct vfs_file *f = &dd->entries[dd->pos++]; strcpy(dir.d_name, f->name); dir.d_namlen = strlen(f->name); dir.d_ino = dd->pos; - dir.d_type = f->dir ? DT_DIR : DT_REG; + dir.d_type = f->type == 'd' ? DT_DIR : DT_REG; DPRINTF(("readdir %s %d\n", dir.d_name, dir.d_type)); dir.d_reclen = _DIRENT_RECLEN(&dir, dir.d_namlen); return &dir; @@ -139,79 +149,142 @@ } static int -gl_stat(const char *name , __gl_stat_t *st) +vfs_stat(const char *name , __gl_stat_t *st) { char buf[MAXPATHLEN]; trim(buf, sizeof(buf), name); memset(st, 0, sizeof(*st)); - if (strcmp(buf, "a") == 0 || strcmp(buf, "a/b") == 0) { - st->st_mode |= S_IFDIR; - return 0; - } + for (size_t i = 0; i < __arraycount(d); i++) + if (strcmp(buf, d[i].name) == 0) { + st->st_mode = S_IFDIR | 0755; + goto out; + } - if (buf[0] == 'a' && buf[1] == '/') { - struct gl_file *f; - size_t offs, count; - - if (buf[2] == 'b' && buf[3] == '/') { - offs = 4; - count = __arraycount(b); - f = b; - } else { - offs = 2; - count = __arraycount(a); - f = a; + for (size_t i = 0; i < __arraycount(d); i++) { + size_t dir_len = strlen(d[i].name); + if (strncmp(buf, d[i].name, dir_len) != 0) + continue; + if (buf[dir_len] != '/') + continue; + const char *base = buf + dir_len + 1; + + for (size_t j = 0; j < d[i].entries_len; j++) { + const struct vfs_file *f = &d[i].entries[j]; + if (strcmp(f->name, base) != 0) + continue; + ATF_CHECK(f->type != 'd'); // handled above + st->st_mode = S_IFREG | 0644; + goto out; } - - for (size_t i = 0; i < count; i++) - if (strcmp(f[i].name, buf + offs) == 0) - return 0; } - DPRINTF(("stat %s %d\n", buf, st->st_mode)); + DPRINTF(("stat %s ENOENT\n", buf)); errno = ENOENT; return -1; +out: + DPRINTF(("stat %s %06o\n", buf, st->st_mode)); + return 0; } static int -gl_lstat(const char *name , __gl_stat_t *st) +vfs_lstat(const char *name , __gl_stat_t *st) { - return gl_stat(name, st); + return vfs_stat(name, st); } static void -gl_closedir(void *v) +vfs_closedir(void *v) { - struct gl_dir *dd = v; + struct vfs_dir *dd = v; dd->pos = 0; DPRINTF(("closedir %p\n", dd)); } static void -run(const char *p, int flags, const char **res, size_t len) +run(const char *p, int flags, /* const char *res */ ...) { glob_t gl; size_t i; + int e; + DPRINTF(("pattern %s\n", p)); memset(&gl, 0, sizeof(gl)); - gl.gl_opendir = gl_opendir; - gl.gl_readdir = gl_readdir; - gl.gl_closedir = gl_closedir; - gl.gl_stat = gl_stat; - gl.gl_lstat = gl_lstat; - - RZ(glob(p, GLOB_ALTDIRFUNC | flags, NULL, &gl)); + gl.gl_opendir = vfs_opendir; + gl.gl_readdir = vfs_readdir; + gl.gl_closedir = vfs_closedir; + gl.gl_stat = vfs_stat; + gl.gl_lstat = vfs_lstat; + + switch ((e = glob(p, GLOB_ALTDIRFUNC | flags, NULL, &gl))) { + case 0: + break; + case GLOB_NOSPACE: + fprintf(stderr, "Malloc call failed.\n"); + goto bad; + case GLOB_ABORTED: + fprintf(stderr, "Unignored error.\n"); + goto bad; + case GLOB_NOMATCH: + fprintf(stderr, "No match, and GLOB_NOCHECK was not set.\n"); + goto bad; + case GLOB_NOSYS: + fprintf(stderr, "Implementation does not support function.\n"); + goto bad; + default: + fprintf(stderr, "Unknown error %d.\n", e); + goto bad; + } for (i = 0; i < gl.gl_pathc; i++) - DPRINTF(("%s\n", gl.gl_pathv[i])); + DPRINTF(("glob result %zu: %s\n", i, gl.gl_pathv[i])); - ATF_CHECK(len == gl.gl_pathc); - for (i = 0; i < gl.gl_pathc; i++) - ATF_CHECK_STREQ(gl.gl_pathv[i], res[i]); + va_list res; + va_start(res, flags); + i = 0; + const char *name; + while ((name = va_arg(res, const char *)) != NULL && i < gl.gl_pathc) { + ATF_CHECK_STREQ(gl.gl_pathv[i], name); + i++; + } + va_end(res); + ATF_CHECK_EQ_MSG(i, gl.gl_pathc, + "expected %zu results, got %zu", i, gl.gl_pathc); + ATF_CHECK_EQ_MSG(name, NULL, + "\"%s\" should have been found, but wasn't", name); globfree(&gl); + return; +bad: + ATF_REQUIRE_MSG(e == 0, "No match for `%s'", p); +} + +#define run(p, flags, ...) (run)(p, flags, __VA_ARGS__, (const char *) 0) + +ATF_TC(glob_range); +ATF_TC_HEAD(glob_range, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Test glob(3) range"); +} + +ATF_TC_BODY(glob_range, tc) +{ + run("a/b/[x-z]", 0, + "a/b/x", "a/b/y", "a/b/z"); +} + +ATF_TC(glob_range_not); +ATF_TC_HEAD(glob_range_not, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Test glob(3) ! range"); } +ATF_TC_BODY(glob_range_not, tc) +{ + run("a/b/[!x-z]", 0, + "a/b/w"); +} ATF_TC(glob_star); ATF_TC_HEAD(glob_star, tc) @@ -222,7 +295,8 @@ ATF_TC_BODY(glob_star, tc) { - run("a/**", GLOB_STAR, glob_star, __arraycount(glob_star)); + run("a/**", GLOB_STAR, + "a/1", "a/3", "a/4", "a/b", "a/b/w", "a/b/x", "a/b/y", "a/b/z"); } ATF_TC(glob_star_not); @@ -232,10 +306,39 @@ "Test glob(3) ** without GLOB_STAR"); } - ATF_TC_BODY(glob_star_not, tc) { - run("a/**", 0, glob_star_not, __arraycount(glob_star_not)); + run("a/**", 0, + "a/1", "a/3", "a/4", "a/b"); +} + +ATF_TC(glob_star_star); +ATF_TC_HEAD(glob_star_star, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Test glob(3) with star-star"); +} + +ATF_TC_BODY(glob_star_star, tc) +{ + run("**", GLOB_STAR, + "a", + "a/1", "a/3", "a/4", "a/b", + "a/b/w", "a/b/x", "a/b/y", "a/b/z"); +} + +ATF_TC(glob_hidden); +ATF_TC_HEAD(glob_hidden, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Test glob(3) with hidden directory"); +} + +ATF_TC_BODY(glob_hidden, tc) +{ + run(".**", GLOB_STAR, + ".hidden-dir", + ".hidden-dir/visible-file"); } #if 0 @@ -262,6 +365,10 @@ { ATF_TP_ADD_TC(tp, glob_star); ATF_TP_ADD_TC(tp, glob_star_not); + ATF_TP_ADD_TC(tp, glob_range); + ATF_TP_ADD_TC(tp, glob_range_not); + ATF_TP_ADD_TC(tp, glob_star_star); + ATF_TP_ADD_TC(tp, glob_hidden); /* * Remove this test for now - the GLOB_NOCHECK return value has been * re-defined to return a modified pattern in revision 1.33 of glob.c diff --git a/lib/libc/gen/t_humanize_number.c b/lib/libc/gen/t_humanize_number.c --- a/lib/libc/gen/t_humanize_number.c +++ b/lib/libc/gen/t_humanize_number.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_humanize_number.c,v 1.9 2017/01/10 15:20:44 christos Exp $ */ +/* $NetBSD: t_humanize_number.c,v 1.10 2019/03/11 17:45:12 kre Exp $ */ /*- * Copyright (c) 2010, 2011 The NetBSD Foundation, Inc. @@ -287,6 +287,11 @@ ATF_REQUIRE(rv != -1); ATF_REQUIRE(strcmp(buf, "0%d%s%d%s%s%s") != 0); + /* + * PR lib/54053: before version 1.18 the output was nonsense + * with HN_AUTOSCALE and a buffer big enough to not need scaling + */ + ATF_REQUIRE(strcmp(buf, "10000") == 0); /* * Tight buffer. diff --git a/lib/libc/gen/t_siginfo.c b/lib/libc/gen/t_siginfo.c --- a/lib/libc/gen/t_siginfo.c +++ b/lib/libc/gen/t_siginfo.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_siginfo.c,v 1.30 2015/12/22 14:25:58 christos Exp $ */ +/* $NetBSD: t_siginfo.c,v 1.45 2021/01/13 06:44:55 skrll Exp $ */ /*- * Copyright (c) 2010 The NetBSD Foundation, Inc. @@ -28,7 +28,6 @@ #include -#include #include #include #include @@ -36,13 +35,14 @@ #include #include +#include +#include +#include #include #include #include #include #include -#include -#include #include #ifdef __HAVE_FENV @@ -53,6 +53,7 @@ #include "isqemu.h" +#ifdef ENABLE_TESTS /* for sigbus */ volatile char *addr; @@ -309,16 +310,14 @@ if (isQEMU()) atf_tc_skip("Test does not run correctly under QEMU"); -#if defined(__powerpc__) - atf_tc_skip("Test not valid on powerpc"); -#elif defined(__arm__) && !__SOFTFP__ +#if (__arm__ && !__SOFTFP__) || __aarch64__ /* - * Some NEON fpus do not implement IEEE exception handling, + * Some NEON fpus do not trap on IEEE 754 FP exceptions. * skip these tests if running on them and compiled for * hard float. */ if (0 == fpsetmask(fpsetmask(FP_X_INV))) - atf_tc_skip("FPU does not implement exception handling"); + atf_tc_skip("FPU does not implement traps on FP exceptions"); #endif if (sigsetjmp(sigfpe_flt_env, 0) == 0) { sa.sa_flags = SA_SIGINFO; @@ -368,8 +367,8 @@ struct sigaction sa; long l = strtol("0", NULL, 10); -#if defined(__powerpc__) - atf_tc_skip("Test not valid on powerpc"); +#if defined(__powerpc__) || defined(__aarch64__) + atf_tc_skip("Integer division by zero doesn't trap"); #endif if (sigsetjmp(sigfpe_int_env, 0) == 0) { sa.sa_flags = SA_SIGINFO; @@ -466,9 +465,27 @@ atf_tc_skip("No SIGBUS signal for unaligned accesses"); #endif - /* m68k (except sun2) never issue SIGBUS (PR lib/49653) */ - if (strcmp(MACHINE_ARCH, "m68k") == 0) - atf_tc_skip("No SIGBUS signal for unaligned accesses"); +#ifdef __powerpc__ + /* + * SIGBUS is not mandatory for powerpc; most processors (not all) + * can deal with unaligned accesses. + */ + atf_tc_skip("SIGBUS signal for unaligned accesses is " + "not mandatory for this architecture"); +#endif + + /* m68k (except sun2) never issue SIGBUS (PR lib/49653), + * same for armv8 or newer */ +#if (defined(__m68k__) && !defined(__mc68010__)) || \ + defined(__aarch64__) + atf_tc_skip("No SIGBUS signal for unaligned accesses"); +#endif + +#if defined(__mips__) + /* no way of detecting if on GXemul, so disable everywhere for now */ + atf_tc_skip("GXemul fails to trap unaligned accesses with " + "correct ENTRYHI"); +#endif sa.sa_flags = SA_SIGINFO; sa.sa_sigaction = sigbus_action; @@ -485,7 +502,7 @@ addr = calloc(2, sizeof(int)); ATF_REQUIRE(addr != NULL); - if (isQEMU()) + if (isQEMU_TCG()) atf_tc_expect_fail("QEMU fails to trap unaligned accesses"); /* Force an unaligned access */ @@ -496,9 +513,25 @@ atf_tc_fail("Test did not fault as expected"); } +#else +ATF_TC(dummy); +ATF_TC_HEAD(dummy, tc) +{ + atf_tc_set_md_var(tc, "descr", "A dummy test"); +} + +ATF_TC_BODY(dummy, tc) +{ + + // Dummy, skipped + // The ATF framework requires at least a single defined test. +} +#endif + ATF_TP_ADD_TCS(tp) { +#ifdef ENABLE_TESTS ATF_TP_ADD_TC(tp, sigalarm); ATF_TP_ADD_TC(tp, sigchild_normal); ATF_TP_ADD_TC(tp, sigchild_dump); @@ -507,6 +540,9 @@ ATF_TP_ADD_TC(tp, sigfpe_int); ATF_TP_ADD_TC(tp, sigsegv); ATF_TP_ADD_TC(tp, sigbus_adraln); +#else + ATF_TP_ADD_TC(tp, dummy); +#endif return atf_no_error(); } diff --git a/lib/libc/gen/t_syslog.c b/lib/libc/gen/t_syslog.c --- a/lib/libc/gen/t_syslog.c +++ b/lib/libc/gen/t_syslog.c @@ -1,7 +1,7 @@ -/* $NetBSD: t_syslog.c,v 1.2 2012/03/18 07:00:51 jruoho Exp $ */ +/* $NetBSD: t_syslog.c,v 1.3 2020/07/03 03:13:10 jruoho Exp $ */ /*- - * Copyright (c) 2010 The NetBSD Foundation, Inc. + * Copyright (c) 2010, 2020 The NetBSD Foundation, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -36,7 +36,6 @@ ATF_TC(syslog_pthread); ATF_TC_HEAD(syslog_pthread, tc) { - atf_tc_set_md_var(tc, "descr", "Test that syslog(3) " "works when linked to pthread(3) (PR lib/44248)"); atf_tc_set_md_var(tc, "timeout", "2"); @@ -47,10 +46,23 @@ syslog(LOG_DEBUG, "from tests/lib/libc/gen/t_syslog"); } +ATF_TC(syslog_invalid_priority); +ATF_TC_HEAD(syslog_invalid_priority, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test that syslog(3) does " + "not segfault from an invalid priority (PR lib/55041)"); +} + +ATF_TC_BODY(syslog_invalid_priority, tc) +{ + syslog(-1, "from tests/lib/libc/gen/t_syslog"); +} + ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, syslog_pthread); + ATF_TP_ADD_TC(tp, syslog_invalid_priority); return atf_no_error(); } diff --git a/lib/libc/hash/Makefile b/lib/libc/hash/Makefile --- a/lib/libc/hash/Makefile +++ b/lib/libc/hash/Makefile @@ -1,15 +1,13 @@ -# $NetBSD: Makefile,v 1.3 2016/07/02 14:52:09 christos Exp $ +# $NetBSD: Makefile,v 1.4 2017/05/21 15:28:42 riastradh Exp $ .include TESTSDIR= ${TESTSBASE}/lib/libc/hash TESTS_C+= t_sha2 -.if ${MKCRYPTO:Uno} != "no" TESTS_C+= t_hmac LDADD.t_hmac+= -lcrypto DDADD.t_hmac+= ${LIBCRYPTO} -.endif TESTS_SH+= t_hash diff --git a/lib/libc/hash/t_hash.sh b/lib/libc/hash/t_hash.sh old mode 100755 new mode 100644 diff --git a/lib/libc/hash/t_hmac.c b/lib/libc/hash/t_hmac.c --- a/lib/libc/hash/t_hmac.c +++ b/lib/libc/hash/t_hmac.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_hmac.c,v 1.1 2016/07/02 14:52:09 christos Exp $ */ +/* $NetBSD: t_hmac.c,v 1.2 2018/02/07 13:18:33 christos Exp $ */ /*- * Copyright (c) 2016 The NetBSD Foundation, Inc. @@ -29,7 +29,7 @@ * POSSIBILITY OF SUCH DAMAGE. */ #include -__RCSID("$NetBSD: t_hmac.c,v 1.1 2016/07/02 14:52:09 christos Exp $"); +__RCSID("$NetBSD: t_hmac.c,v 1.2 2018/02/07 13:18:33 christos Exp $"); #include #include @@ -49,7 +49,9 @@ int stop; void *e1; const void *evps[] = { +#if OPENSSL_VERSION_NUMBER < 0x10100000L EVP_md2(), +#endif EVP_md4(), EVP_md5(), EVP_ripemd160(), @@ -60,7 +62,9 @@ EVP_sha512(), }; const char *names[] = { +#if OPENSSL_VERSION_NUMBER < 0x10100000L "md2", +#endif "md4", "md5", "rmd160", diff --git a/lib/libc/locale/Makefile b/lib/libc/locale/Makefile --- a/lib/libc/locale/Makefile +++ b/lib/libc/locale/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.6 2013/05/28 16:57:56 joerg Exp $ +# $NetBSD: Makefile,v 1.13 2019/07/28 13:46:45 christos Exp $ .include @@ -10,11 +10,22 @@ TESTS_C+= t_mbtowc TESTS_C+= t_wcscspn TESTS_C+= t_wcspbrk +TESTS_C+= t_wcsrtombs TESTS_C+= t_wcsspn TESTS_C+= t_wcstod TESTS_C+= t_wctomb TESTS_C+= t_io +TESTS_C+= t_toupper +TESTS_C+= t_digittoint +TESTS_C+= t_sprintf +TESTS_C+= t_wctype +TESTS_C+= t_btowc +TESTS_C+= t_wcscoll +TESTS_C+= t_ducet +TESTS_C+= t_strfmon COPTS.t_wctomb.c += -Wno-stack-protector +COPTS.t_digittoint.c += -Wno-unused-variable +COPTS.t_btowc.c += -Wno-unused-variable .include diff --git a/lib/libc/locale/ducet_test.h b/lib/libc/locale/ducet_test.h new file mode 100644 index 0000000000000000000000000000000000000000..01bcfa7a11149d82878cf8a3da177c8ed0f185eb GIT binary patch literal 5524424 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 zkqmQ@40DkTbCC>lkqmQ@40DkTbCC>lkqmQ@40DkTbCC>lkqmQ@40DkTbCC>lkqmQ@ z40DkTbCC>lkqmQ@40DkTbCC>lkqmQ@40DkTbCC>lkqmQ@40DkTbCC>lkqmQ@40DkT zbCC>lkqmP=B*R=J!~FaI$u8oTcedO`{POvG zt6jt|l3^~AVJ?zkE|Os`l3^~AVJ?zkE|Os`l3^~AVJ?zkE|Os`l3^~AVJ?zkE|Os` zl3^~AVJ?zkE|Os`l3^~AVJ?zkE|Os`l3^~AVJ?zkE|Os`l3^~AVJ?zkE|Os`l3^~A zVJ?zkE|Os`l3^~AVJ?zkE|Os`l3^~AVJ?zkE|Os`l3^~AVJ?zkE|Os`l3^~AVJ?zk zE|Os`l3^~AVJ?zkE|Os`l3^~AVJ?zkE|Os`l3^~AVJ?zkE|Os`l3^~AVJ?zkE|Os` zl3^~AVJ?zkE|Os`l3^~AVJ?zkE|Os`l3^~AVJ?zkE|Os`l3^~AVJ?zkE|Os`l3^~A zVJ?zkE|Os`l3^~AVJ?zkE|Os`l3^~AVJ?zkE|Os`l3^~AVJ?zkE|Os`l3^~AVJ?zk zE|Os`l3^~AVJ?zkE|Os`l3^~AVJ?zkE|Os`l3^~AVXl&ztdg3nlA5fNnyiwVtdg3n zlA5fNnyiwVtdg3nlA5fNnyiwVtdg3nlA5fNnyiwVtdg3nlA5fNnyiwVtdg3nlA5fN znyiwVtdg3nlA5fNnyiwVtdg3nlA5fNnyiwVtdg3nlA5fNnyiwVtdg3nlA5fNnyiwV ztdg3nlA5fNnyiwVtdg3nlA5fNnyiwVtdg3nlA5fNnyiwVtdg3nlA5fNnyiwVtdg3n zlA5fNnyiwVtdg3nlA5fNnyiwVtdg3nlA5fNnyiwVtdg3nlA5fNnyiwVtdg3nlA5fN znyiwVtdg3nlA5fNnyiwVtdg3nlA5fNnyiwVtdg3nlA5fNnyiwVtdg3nlA5fNnyiwV ztdg3nlA5fNnyiwVtdg3nlA5fNnyiwVtdg3nlA5fNnyiwVtdg3nlA5fNnyiwVtdg3n zlA5fNnyiwVtdg3nlA5fNnyiwVtdg3nlA5fNnyiwVtdg3nlA5fNnyiwVtdg3nlA5fN znyiwVtdg3nlA5fNnyiwVtdg3nlA5fNnyiwVtdg3nlA5fNnyiwVtdg3nlA5fNnyiwV ztdg3nlA5fNnyiwVtdg3nlA5fNnyiwVtdg3nlA5fNnyiwVtdg3nlA5fNnyiwVtdg3n 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%0AZo{Q+!=-M+rEbHejE>T7bd+|Z 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≪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 literal 0 Hc$@ +__COPYRIGHT("@(#) Copyright (c) 2017\ + The NetBSD Foundation, inc. All rights reserved."); +__RCSID("$NetBSD: t_btowc.c,v 1.3 2017/08/10 19:08:43 perseant Exp $"); + +#include +#include +#include +#include +#include +#include + +#include + +struct test { + const char *locale; + const char *illegal; /* Illegal single-byte characters, if any */ + const char *legal; /* Legal single-byte characters */ + /* The next two are only used if __STDC_ISO_10646__ is defined */ + const wchar_t wlegal[8]; /* The same characters, but in ISO-10646 */ + const wchar_t willegal[8]; /* ISO-10646 that do not map into charset */ +} tests[] = { + { + "en_US.UTF-8", + "\200", + "ABC123@\t", + { 'A', 'B', 'C', '1', '2', '3', '@', '\t' }, + { 0xfdd0, 0x10fffe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0} + }, + { + "ru_RU.KOI8-R", + "", /* No illegal characters in KOI8-R */ + "A\xc2\xd7\xc7\xc4\xc5\xa3", + { 'A', 0x0431, 0x432, 0x0433, 0x0434, 0x0435, 0x0451, 0x0 }, + { 0x00c5, 0x00e6, 0x00fe, 0x0630, 0x06fc, 0x56cd, 0x0, 0x0 } + }, + { + NULL, + NULL, + NULL, + { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }, + { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 } + }, +}; + +#ifdef __STDC_ISO_10646__ +static void +h_iso10646(struct test *t) +{ + const char *cp; + int c, wc; + char *str; + const wchar_t *wcp; + + ATF_REQUIRE_STREQ(setlocale(LC_ALL, "C"), "C"); + printf("Trying locale: %s\n", t->locale); + ATF_REQUIRE(setlocale(LC_CTYPE, t->locale) != NULL); + ATF_REQUIRE((str = setlocale(LC_ALL, NULL)) != NULL); + (void)printf("Using locale: %s\n", str); + + /* These should have valid wchar representations */ + for (cp = t->legal, wcp = t->wlegal; *cp != '\0'; ++cp, ++wcp) { + c = (int)(unsigned char)*cp; + printf("Checking legal character 0x%x\n", c); + wc = btowc(c); + + if (errno != 0) + printf(" btowc() failed with errno=%d\n", errno); + + /* It should map to the known Unicode equivalent */ + printf("btowc(0x%2.2x) = 0x%x, expecting 0x%x\n", + c, wc, *wcp); + ATF_REQUIRE(btowc(c) == *wcp); + } + + /* These are invalid characters in the target set */ + for (wcp = t->willegal; *wcp != '\0'; ++wcp) { + printf("Checking illegal wide character 0x%lx\n", + (unsigned long)*wcp); + ATF_REQUIRE_EQ(wctob(*wcp), EOF); + } +} +#endif + +static void +h_btowc(struct test *t) +{ + const char *cp; + unsigned char c; + char *str; + const wchar_t *wcp; + + ATF_REQUIRE_STREQ(setlocale(LC_ALL, "C"), "C"); + printf("Trying locale: %s\n", t->locale); + ATF_REQUIRE(setlocale(LC_CTYPE, t->locale) != NULL); + ATF_REQUIRE((str = setlocale(LC_ALL, NULL)) != NULL); + (void)printf("Using locale: %s\n", str); + + /* btowc(EOF) -> WEOF */ + ATF_REQUIRE_EQ(btowc(EOF), WEOF); + + /* wctob(WEOF) -> EOF */ + ATF_REQUIRE_EQ(wctob(WEOF), EOF); + + /* Invalid in initial shift state -> WEOF */ + for (cp = t->illegal; *cp != '\0'; ++cp) { + printf("Checking illegal character 0x%x\n", + (unsigned char)*cp); + ATF_REQUIRE_EQ(btowc(*cp), WEOF); + } + + /* These should have valid wchar representations */ + for (cp = t->legal; *cp != '\0'; ++cp) { + c = (unsigned char)*cp; + printf("Checking legal character 0x%x\n", c); + + /* A legal character never maps to EOF */ + ATF_REQUIRE(btowc(c) != WEOF); + + /* And the mapping should be reversible */ + printf("0x%x -> wide 0x%x -> 0x%x\n", + c, btowc(c), (unsigned char)wctob(btowc(c))); + ATF_REQUIRE_EQ(wctob(btowc(c)), c); + } +} + +ATF_TC(btowc); +ATF_TC_HEAD(btowc, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks btowc(3) and wctob(3)"); +} +ATF_TC_BODY(btowc, tc) +{ + struct test *t; + + for (t = tests; t->locale != NULL; ++t) + h_btowc(t); +} + +ATF_TC(stdc_iso_10646); +ATF_TC_HEAD(stdc_iso_10646, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Checks btowc(3) conversion to ISO10646"); +} +ATF_TC_BODY(stdc_iso_10646, tc) +{ + struct test *t; + +#ifdef __STDC_ISO_10646__ + for (t = tests; t->locale != NULL; ++t) + h_iso10646(t); +#else /* ! __STDC_ISO_10646__ */ + atf_tc_skip("__STDC_ISO_10646__ not defined"); +#endif /* ! __STDC_ISO_10646__ */ +} + +ATF_TC(btowc_posix); +ATF_TC_HEAD(btowc_posix, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks btowc(3) and wctob(3) for POSIX locale"); +} +ATF_TC_BODY(btowc_posix, tc) +{ + const char *cp; + unsigned char c; + char *str; + const wchar_t *wcp; + int i; + + ATF_REQUIRE_STREQ(setlocale(LC_ALL, "POSIX"), "POSIX"); + + /* btowc(EOF) -> WEOF */ + ATF_REQUIRE_EQ(btowc(EOF), WEOF); + + /* wctob(WEOF) -> EOF */ + ATF_REQUIRE_EQ(wctob(WEOF), EOF); + + /* All characters from 0 to 255, inclusive, map + onto their unsigned char equivalent */ + for (i = 0; i <= 255; i++) { + ATF_REQUIRE_EQ(btowc(i), (wchar_t)(unsigned char)(i)); + ATF_REQUIRE_EQ((unsigned char)wctob(i), (wchar_t)i); + } +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, btowc); + ATF_TP_ADD_TC(tp, btowc_posix); + ATF_TP_ADD_TC(tp, stdc_iso_10646); + + return atf_no_error(); +} diff --git a/lib/libc/locale/t_digittoint.c b/lib/libc/locale/t_digittoint.c new file mode 100644 --- /dev/null +++ b/lib/libc/locale/t_digittoint.c @@ -0,0 +1,112 @@ +/* $NetBSD: t_digittoint.c,v 1.2 2017/06/01 15:45:02 perseant Exp $ */ + +/*- + * Copyright (c) 2017 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Konrad Schroder + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +__COPYRIGHT("@(#) Copyright (c) 2017\ + The NetBSD Foundation, inc. All rights reserved."); +__RCSID("$NetBSD: t_digittoint.c,v 1.2 2017/06/01 15:45:02 perseant Exp $"); + +#include +#include +#include +#include +#include + +#include + +/* Use this until we have a better way to tell if it is defined */ +#ifdef digittoint +# define DIGITTOINT_DEFINED +#endif + +static struct test { + const char *locale; + const char *digits; +} tests[] = { + { + "C", + "0123456789AbcDeF", + }, { + "en_US.UTF-8", + "0123456789AbcDeF", + }, { + "ru_RU.KOI-8", + "0123456789AbcDeF", + }, { + NULL, + NULL, + } +}; + +#ifdef DIGITTOINT_DEFINED +static void +h_digittoint(const struct test *t) +{ + int i; + + ATF_REQUIRE_STREQ(setlocale(LC_ALL, "C"), "C"); + printf("Trying locale %s...\n", t->locale); + ATF_REQUIRE(setlocale(LC_CTYPE, t->locale) != NULL); + + for (i = 0; i < 16; i++) { + printf(" char %2.2x in position %d\n", t->digits[i], i); + ATF_REQUIRE_EQ(digittoint(t->digits[i]), i); + } +} +#endif /* DIGITTOINT_DEFINED */ + +ATF_TC(digittoint); + +ATF_TC_HEAD(digittoint, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Checks digittoint under diferent locales"); +} + +ATF_TC_BODY(digittoint, tc) +{ + struct test *t; + +#ifdef DIGITTOINT_DEFINED + for (t = &tests[0]; t->locale != NULL; ++t) + h_digittoint(t); +#else /* ! DIGITTOINT_DEFINED */ + atf_tc_skip("digittoint(3) not present to test"); +#endif /* DIGITTOINT_DEFINED */ +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, digittoint); + + return atf_no_error(); +} diff --git a/lib/libc/locale/t_ducet.c b/lib/libc/locale/t_ducet.c new file mode 100644 --- /dev/null +++ b/lib/libc/locale/t_ducet.c @@ -0,0 +1,153 @@ +/* $NetBSD: t_ducet.c,v 1.2 2017/07/23 18:51:21 perseant Exp $ */ + +/*- + * Copyright (c) 2011 The NetBSD Foundation, Inc. + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +__COPYRIGHT("@(#) Copyright (c) 2011\ + The NetBSD Foundation, inc. All rights reserved."); +__RCSID("$NetBSD: t_ducet.c,v 1.2 2017/07/23 18:51:21 perseant Exp $"); + +#include +#include +#include +#include +#include +#include +#include +#include "ducet_test.h" + +#include + +ATF_TC(wcscoll_ducet); +ATF_TC_HEAD(wcscoll_ducet, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test collation algorithm against DUCET test data"); +} + +ATF_TC_BODY(wcscoll_ducet, tc) +{ +#ifndef __STDC_ISO_10646__ + atf_tc_skip("Cannot test DUCET without __STDC_ISO_10646__"); +#else + wchar_t *oline = NULL, *line; + int i, lineno = 0; + + setlocale(LC_COLLATE, "en_US.UTF-8"); /* should be "vanilla" DUCET, but en_US will do */ + ATF_REQUIRE_STREQ("en_US.UTF-8", setlocale(LC_COLLATE, NULL)); + + for (line = &ducet_test_data[0][0]; line[0]; line += MAX_TS_LEN * sizeof(*line)) { + ++lineno; + + if (oline == NULL) { + oline = line; + continue; + } + + printf(" line %d : ", lineno); + for (i = 0; oline[i] != 0; i++) + printf("0x%lx ", (long)oline[i]); + printf(" < "); + for (i = 0; line[i] != 0; i++) + printf("0x%lx ", (long)line[i]); + printf("\n"); + + /* Compare, expect oline <= line, a non-positive result */ + ATF_CHECK(wcscoll(oline, line) <= 0); + + oline = line; + } +#endif +} + +ATF_TC(wcsxfrm_ducet); +ATF_TC_HEAD(wcsxfrm_ducet, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test collation algorithm against DUCET test data"); +} + +#define BUFLEN 1024 + +ATF_TC_BODY(wcsxfrm_ducet, tc) +{ +#ifndef __STDC_ISO_10646__ + atf_tc_skip("Cannot test DUCET without __STDC_ISO_10646__"); +#else + wchar_t *tmp, *oline = NULL, *line; + wchar_t xfb1[BUFLEN], xfb2[BUFLEN]; /* Gross overestimates */ + wchar_t *oxf = xfb1, *xf = xfb2; + int i, lineno = 0, result; + + setlocale(LC_COLLATE, "en_US.UTF-8"); /* should be "vanilla" DUCET, but en_US will do */ + ATF_REQUIRE_STREQ("en_US.UTF-8", setlocale(LC_COLLATE, NULL)); + + memset(xfb1, 0, BUFLEN); + memset(xfb2, 0, BUFLEN); + for (line = &ducet_test_data[0][0]; line[0]; line += MAX_TS_LEN * sizeof(*line)) { + ++lineno; + + if (oline == NULL) { + oline = line; + continue; + } + + printf(" line %d : ", lineno); + for (i = 0; oline[i] != 0; i++) + printf("0x%lx ", (long)oline[i]); + printf(" < "); + for (i = 0; line[i] != 0; i++) + printf("0x%lx ", (long)line[i]); + printf("\n"); + + wcsxfrm(xf, line, BUFLEN); + result = wcscmp(oxf, xf); + if (result > 0) { + printf("FAILED result was %d\nweights were ", result); + for (i = 0; oxf[i] != 0; i++) + printf("0x%lx ", (long)oline[i]); + printf(" and "); + for (i = 0; xf[i] != 0; i++) + printf("0x%lx ", (long)line[i]); + printf("\n"); + + } + ATF_CHECK(result <= 0); + + /* Swap buffers */ + tmp = oxf; + oxf = xf; + xf = tmp; + oline = line; + } +#endif +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, wcscoll_ducet); + ATF_TP_ADD_TC(tp, wcsxfrm_ducet); + + return atf_no_error(); +} diff --git a/lib/libc/locale/t_io.c b/lib/libc/locale/t_io.c --- a/lib/libc/locale/t_io.c +++ b/lib/libc/locale/t_io.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_io.c,v 1.4 2014/01/21 00:32:16 yamt Exp $ */ +/* $NetBSD: t_io.c,v 1.5 2017/07/12 17:32:51 perseant Exp $ */ /*- * Copyright (c) 2013 The NetBSD Foundation, Inc. @@ -32,7 +32,7 @@ #include __COPYRIGHT("@(#) Copyright (c) 2011\ The NetBSD Foundation, inc. All rights reserved."); -__RCSID("$NetBSD: t_io.c,v 1.4 2014/01/21 00:32:16 yamt Exp $"); +__RCSID("$NetBSD: t_io.c,v 1.5 2017/07/12 17:32:51 perseant Exp $"); #include #include @@ -53,8 +53,14 @@ ATF_TC_BODY(bad_big5_wprintf, tc) { - /* XXX implementation detail knowledge (wchar_t encoding) */ - wchar_t ibuf[] = { 0xcf10, 0 }; + wchar_t ibuf[] = { +#ifdef __STDC_ISO_10646__ + 0x0978, 0 /* An arbitrarily chosen Devangari symbol */ +#else + /* XXX implementation detail knowledge (wchar_t encoding) */ + 0xcf10, 0 +#endif + }; setlocale(LC_CTYPE, "zh_TW.Big5"); ATF_REQUIRE_ERRNO(EILSEQ, wprintf(L"%ls\n", ibuf) < 0); ATF_REQUIRE(ferror(stdout)); @@ -68,8 +74,14 @@ ATF_TC_BODY(bad_big5_swprintf, tc) { - /* XXX implementation detail knowledge (wchar_t encoding) */ - wchar_t ibuf[] = { 0xcf10, 0 }; + wchar_t ibuf[] = { +#ifdef __STDC_ISO_10646__ + 0x0978, 0 /* An arbitrarily chosen Devangari symbol */ +#else + /* XXX implementation detail knowledge (wchar_t encoding) */ + 0xcf10, 0 +#endif + }; wchar_t obuf[20]; setlocale(LC_CTYPE, "zh_TW.Big5"); ATF_REQUIRE_ERRNO(EILSEQ, @@ -84,8 +96,14 @@ ATF_TC_BODY(good_big5_wprintf, tc) { - /* XXX implementation detail knowledge (wchar_t encoding) */ - wchar_t ibuf[] = { 0xcf40, 0 }; + wchar_t ibuf[] = { +#ifdef __STDC_ISO_10646__ + 0x67DC, 0 +#else + /* XXX implementation detail knowledge (wchar_t encoding) */ + 0xcf40, 0 +#endif + }; setlocale(LC_CTYPE, "zh_TW.Big5"); ATF_REQUIRE_EQ(wprintf(L"%ls\n", ibuf), 2); } @@ -98,8 +116,14 @@ ATF_TC_BODY(good_big5_swprintf, tc) { - /* XXX implementation detail knowledge (wchar_t encoding) */ - wchar_t ibuf[] = { 0xcf40, 0 }; + wchar_t ibuf[] = { +#ifdef __STDC_ISO_10646__ + 0x67DC, 0 +#else + /* XXX implementation detail knowledge (wchar_t encoding) */ + 0xcf40, 0 +#endif + }; wchar_t obuf[20]; setlocale(LC_CTYPE, "zh_TW.Big5"); ATF_REQUIRE_EQ(swprintf(obuf, sizeof(obuf), L"%ls\n", ibuf), 2); @@ -139,8 +163,14 @@ ATF_REQUIRE(fp != NULL); setlocale(LC_CTYPE, "zh_TW.Big5"); - /* XXX implementation detail knowledge (wchar_t encoding) */ - ATF_REQUIRE_EQ(getwc(fp), 0xcf40); + ATF_REQUIRE_EQ(getwc(fp), +#ifdef __STDC_ISO_10646__ + 0x67DC +#else + /* XXX implementation detail knowledge (wchar_t encoding) */ + 0xcf40 +#endif + ); fclose(fp); } diff --git a/lib/libc/locale/t_mbrtowc.c b/lib/libc/locale/t_mbrtowc.c --- a/lib/libc/locale/t_mbrtowc.c +++ b/lib/libc/locale/t_mbrtowc.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_mbrtowc.c,v 1.1 2011/07/15 07:35:21 jruoho Exp $ */ +/* $NetBSD: t_mbrtowc.c,v 1.2 2017/07/12 17:32:51 perseant Exp $ */ /*- * Copyright (c) 2011 The NetBSD Foundation, Inc. @@ -58,7 +58,7 @@ #include __COPYRIGHT("@(#) Copyright (c) 2011\ The NetBSD Foundation, inc. All rights reserved."); -__RCSID("$NetBSD: t_mbrtowc.c,v 1.1 2011/07/15 07:35:21 jruoho Exp $"); +__RCSID("$NetBSD: t_mbrtowc.c,v 1.2 2017/07/12 17:32:51 perseant Exp $"); #include #include @@ -98,19 +98,31 @@ }, { "ja_JP.ISO2022-JP2", "\033$BF|K\1348l\033(BA\033$B$\"\033(BB\033$B$$\033(B", +#ifdef __STDC_ISO_10646__ + { 0x65E5, 0x672C, 0x8A9E, 0x41, 0x3042, 0x42, 0x3044 }, +#else { 0x4200467c, 0x42004b5c, 0x4200386c, 0x41, 0x42002422, 0x42, 0x42002424 }, +#endif { 5, 2, 2, 4, 5, 4, 5 }, 7 }, { "ja_JP.SJIS", "\223\372\226{\214\352A\202\240B\202\242", - { 0x93fa, 0x967b, 0x8cea, 0x41, 0x82a0, 0x42, 0x82a2 }, +#ifdef __STDC_ISO_10646__ + { 0x65E5, 0x672C, 0x8A9E, 0x41, 0x3042, 0x42, 0x3044 }, +#else + { 0x93FA, 0x967B, 0x8CEA, 0x41, 0x82A0, 0x42, 0x82A2 }, +#endif { 2, 2, 2, 1, 2, 1, 2 }, 7 }, { "ja_JP.eucJP", "\306\374\313\334\270\354A\244\242B\244\244", - { 0xc6fc, 0xcbdc, 0xb8ec, 0x41, 0xa4a2, 0x42, 0xa4a4 }, +#ifdef __STDC_ISO_10646__ + { 0x65E5, 0x672C, 0x8A9E, 0x41, 0x3042, 0x42, 0x3044 }, +#else + { 0xC6FC, 0xCBDC, 0xB8EC, 0x41, 0xA4A2, 0x42, 0xA4A4 }, +#endif { 2, 2, 2, 1, 2, 1, 2 }, 7 }, { @@ -146,6 +158,8 @@ // mbrtowc(0, 0, 0, &st); /* XXX for ISO2022-JP */ stp = use_mbstate ? &st : 0; + printf("First using repeated mbrtowc\n"); + for (n = 9; n > 0; n--) { const char *src = t->data; wchar_t dst; @@ -196,6 +210,8 @@ "%zd (expected: %zd)", nchar, t->length); } + printf("Now using mbsrtowcs\n"); + { wchar_t wbuf[SIZE]; size_t rv; diff --git a/lib/libc/locale/t_mbstowcs.c b/lib/libc/locale/t_mbstowcs.c --- a/lib/libc/locale/t_mbstowcs.c +++ b/lib/libc/locale/t_mbstowcs.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_mbstowcs.c,v 1.1 2011/07/15 07:35:21 jruoho Exp $ */ +/* $NetBSD: t_mbstowcs.c,v 1.2 2017/07/12 17:32:51 perseant Exp $ */ /*- * Copyright (c) 2011 The NetBSD Foundation, Inc. @@ -55,7 +55,7 @@ #include __COPYRIGHT("@(#) Copyright (c) 2011\ The NetBSD Foundation, inc. All rights reserved."); -__RCSID("$NetBSD: t_mbstowcs.c,v 1.1 2011/07/15 07:35:21 jruoho Exp $"); +__RCSID("$NetBSD: t_mbstowcs.c,v 1.2 2017/07/12 17:32:51 perseant Exp $"); #include #include @@ -97,9 +97,15 @@ "ja_JP.ISO2022-JP", "\033$B#J#I#S$G$9!#\033(Baaaa\033$B$\"$$$&$($*\033(B", { +#ifdef __STDC_ISO_10646__ + 0xFF2A, 0xFF29, 0xFF33, 0x3067, 0x3059, + 0x3002, 0x61, 0x61, 0x61, 0x61, 0x3042, 0x3044, + 0x3046, 0x3048, 0x304A, 0x0A +#else 0x4200234A, 0x42002349, 0x42002353, 0x42002447, 0x42002439, 0x42002123, 0x61, 0x61, 0x61, 0x61, 0x42002422, 0x42002424, 0x42002426, 0x42002428, 0x4200242A, 0x0A +#endif }, { 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 2, 2, 2, 2, 2, -1 }, 26 @@ -108,8 +114,13 @@ "\202r\202i\202h\202r\202\305\202\267\201Baaaa\202\240\202\242" "\202\244\202\246\202\250", { +#ifdef __STDC_ISO_10646__ + 0xFF33, 0xFF2A, 0xFF29, 0xFF33, 0x3067, 0x3059, 0x3002, 0x61, + 0x61, 0x61, 0x61, 0x3042, 0x3044, 0x3046, 0x3048, 0x304A, 0x0A +#else 0x8272, 0x8269, 0x8268, 0x8272, 0x82C5, 0x82B7, 0x8142, 0x61, 0x61, 0x61, 0x61, 0x82A0, 0x82A2, 0x82A4, 0x82A6, 0x82A8, 0x0A +#endif }, { 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 2, 2, 2, 2, 2, -1 }, 28 @@ -118,8 +129,13 @@ "\243\305\243\325\243\303\244\307\244\271\241\243aaaa\244\242\244" "\244\244\246\244\250\244\252", { +#ifdef __STDC_ISO_10646__ + 0xFF25, 0xFF35, 0xFF23, 0x3067, 0x3059, 0x3002, 0x61, 0x61, 0x61, + 0x61, 0x3042, 0x3044, 0x3046, 0x3048, 0x304A, 0x0A +#else 0xA3C5, 0xA3D5, 0xA3C3, 0xA4C7, 0xA4B9, 0xA1A3, 0x61, 0x61, 0x61, 0x61, 0xA4A2, 0xA4A4, 0xA4A6, 0xA4A8, 0xA4AA, 0x0A +#endif }, { 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 2, 2, 2, 2, 2, -1 }, 26 diff --git a/lib/libc/locale/t_mbtowc.c b/lib/libc/locale/t_mbtowc.c --- a/lib/libc/locale/t_mbtowc.c +++ b/lib/libc/locale/t_mbtowc.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_mbtowc.c,v 1.1 2011/04/09 17:45:25 pgoyette Exp $ */ +/* $NetBSD: t_mbtowc.c,v 1.3 2020/06/30 16:09:40 jruoho Exp $ */ /*- * Copyright (c) 2011 The NetBSD Foundation, Inc. @@ -53,13 +53,42 @@ * */ +/*- + * Copyright (c) 2005 Miloslav Trmac + * 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 COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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 __COPYRIGHT("@(#) Copyright (c) 2011\ The NetBSD Foundation, inc. All rights reserved."); -__RCSID("$NetBSD: t_mbtowc.c,v 1.1 2011/04/09 17:45:25 pgoyette Exp $"); +__RCSID("$NetBSD: t_mbtowc.c,v 1.3 2020/06/30 16:09:40 jruoho Exp $"); #include +#include #include +#include #include #include #include @@ -69,21 +98,21 @@ #include static void -h_mbtowc(const char *locale, const char *illegal, const char *legal) +h_mbtowc(const char *locale, const char *illegal, + const char *legal, size_t stateful) { char buf[64]; - size_t stateful, ret; + size_t ret; char *str; ATF_REQUIRE_STREQ(setlocale(LC_ALL, "C"), "C"); + (void)printf("Trying locale: %s\n", locale); ATF_REQUIRE(setlocale(LC_CTYPE, locale) != NULL); ATF_REQUIRE((str = setlocale(LC_ALL, NULL)) != NULL); (void)printf("Using locale: %s\n", str); - - stateful = wctomb(NULL, L'\0'); (void)printf("Locale is state-%sdependent\n", - stateful ? "in" : ""); + !stateful ? "in" : ""); /* initialize internal state */ ret = mbtowc(NULL, NULL, 0); @@ -98,11 +127,13 @@ (void)printf("errno: %s\n", strerror(errno)); ATF_REQUIRE_EQ(errno, EILSEQ); - /* if this is stateless encoding, this re-initialization is not required. */ + /* + * If this is stateless encoding, this + * re-initialization is not required. + */ if (stateful) { /* re-initialize internal state */ - ret = mbtowc(NULL, NULL, 0); - ATF_REQUIRE(stateful ? ret : !ret); + mbtowc(NULL, NULL, 0); } /* valid multibyte sequence case */ @@ -119,25 +150,58 @@ (void)printf("Ok.\n"); } -ATF_TC(mbtowc); -ATF_TC_HEAD(mbtowc, tc) +ATF_TC(mbtowc_basic); +ATF_TC_HEAD(mbtowc_basic, tc) { - atf_tc_set_md_var(tc, "descr", "Checks mbtowc(3)"); + atf_tc_set_md_var(tc, "descr", "A basic test of mbtowc(3)"); } -ATF_TC_BODY(mbtowc, tc) + +ATF_TC_BODY(mbtowc_basic, tc) { - h_mbtowc("en_US.UTF-8", "\240", "\302\240"); - h_mbtowc("ja_JP.ISO2022-JP", "\033$B", "\033$B$\"\033(B"); - h_mbtowc("ja_JP.SJIS", "\202", "\202\240"); - h_mbtowc("ja_JP.eucJP", "\244", "\244\242"); - h_mbtowc("zh_CN.GB18030", "\241", "\241\241"); - h_mbtowc("zh_TW.Big5", "\241", "\241@"); - h_mbtowc("zh_TW.eucTW", "\241", "\241\241"); + h_mbtowc("en_US.UTF-8", "\240", "\302\240", 0); + h_mbtowc("ja_JP.ISO2022-JP", "\033$B", "\033$B$\"\033(B", 1); + h_mbtowc("ja_JP.SJIS", "\202", "\202\240", 0); + h_mbtowc("ja_JP.eucJP", "\244", "\244\242", 0); + h_mbtowc("zh_CN.GB18030", "\241", "\241\241", 0); + h_mbtowc("zh_TW.Big5", "\241", "\241@", 0); + h_mbtowc("zh_TW.eucTW", "\241", "\241\241", 0); +} + +ATF_TC(mbtowc_sign); +ATF_TC_HEAD(mbtowc_sign, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test mbtowc(3) sign conversion"); +} + +ATF_TC_BODY(mbtowc_sign, tc) +{ + char back[MB_LEN_MAX]; + wchar_t wc; + size_t i; + int ret; + + (void)setlocale(LC_ALL, ""); + (void)printf("Charset: %s\n", nl_langinfo(CODESET)); + ret = mbtowc(&wc, "\xe4", 1); + (void)printf("mbtowc(): %d\n", ret); + + if (ret > 0) { + (void)printf("Result: 0x%08lX\n",(unsigned long)wc); + ret = wctomb(back, wc); + (void)printf("wctomb(): %d\n", ret); + for(i = 0; ret > 0 && i < (size_t)ret; i++) + printf("%02X ",(unsigned char)back[i]); + putchar('\n'); + } + + ATF_REQUIRE(ret > 0); } ATF_TP_ADD_TCS(tp) { - ATF_TP_ADD_TC(tp, mbtowc); + + ATF_TP_ADD_TC(tp, mbtowc_basic); + ATF_TP_ADD_TC(tp, mbtowc_sign); return atf_no_error(); } diff --git a/lib/libc/locale/t_sprintf.c b/lib/libc/locale/t_sprintf.c new file mode 100644 --- /dev/null +++ b/lib/libc/locale/t_sprintf.c @@ -0,0 +1,242 @@ +/* $NetBSD: t_sprintf.c,v 1.8 2021/08/02 17:41:07 andvar Exp $ */ + +/*- + * Copyright (c) 2017 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Konrad Schroder. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +__COPYRIGHT("@(#) Copyright (c) 2017\ + The NetBSD Foundation, inc. All rights reserved."); +__RCSID("$NetBSD: t_sprintf.c,v 1.8 2021/08/02 17:41:07 andvar Exp $"); + +#include +#include +#include +#include +#include +#include +#include + +#include + +static struct test { + const char *locale; + const int int_value; + const char *int_result; + const char *int_input; + const double double_value; + const char *double_result; + const char *double_input; +} tests[] = { + { + "en_US.UTF-8", + -12345, + "-12,345", + "-12345", + -12345.6789, + "-12,345.678900", + "-12345.678900", + }, { + "fr_FR.ISO8859-1", + -12345, + "-12\240345", + "-12345", + -12345.6789, + "-12\240345,678900", + "-12345,678900", + }, { + "it_IT.ISO8859-1", + -12345, + "-12.345", + "-12345", + -12345.6789, + "-12.345,678900", + "-12345,678900", + }, { + "POSIX", + /* + * POSIX-1.2008 specifies that the C and POSIX + * locales shall be identical (section 7.2) and + * that the POSIX locale shall have an empty + * thousands separator and "" as its + * decimal point (section 7.3.4). *printf + * ought to honor these settings. + */ + -12345, + "-12345", + "-12345", + -12345.6789, + "-12345.678900", + "-12345.678900", + }, { + NULL, + 0, + NULL, + NULL, + 0.0, + NULL, + NULL, + } +}; + +static void +h_sprintf(const struct test *t) +{ + char buf[1024]; + + ATF_REQUIRE_STREQ(setlocale(LC_ALL, "C"), "C"); + printf("Trying locale %s...\n", t->locale); + ATF_REQUIRE(setlocale(LC_NUMERIC, t->locale) != NULL); + printf("Using locale: %s\n", setlocale(LC_ALL, NULL)); + + sprintf(buf, "%'f", t->double_value); + ATF_REQUIRE_STREQ(buf, t->double_result); + + sprintf(buf, "%'d", t->int_value); + ATF_REQUIRE_STREQ(buf, t->int_result); + + atf_tc_expect_pass(); +} + +static void +h_strto(const struct test *t) +{ + double d, diff; + + ATF_REQUIRE_STREQ(setlocale(LC_ALL, "C"), "C"); + printf("Trying locale %s...\n", t->locale); + ATF_REQUIRE(setlocale(LC_NUMERIC, t->locale) != NULL); + + ATF_REQUIRE_EQ((int)strtol(t->int_input, NULL, 10), t->int_value); + + /* + * Note that the C standard permits function values to be + * returned with more precision than is expected by (floating) + * data types, and on i386 (and potentially other implementations) + * that is exactly what happens, meaning that the result from + * strtod() is not identical to the expected value - it turns out + * that it is the same if the value is constrained to the number + * of mantissa bits in a double (so the %a values printed below + * show the exact same bit patterns) and on i386 -ffloat-store + * will cause gcc to constrain the result that way, but nothing + * demands that be true, so instead, we simply test that the + * value returned is very very close to that expected. + * + * 1e-12 is chosen as the allowable delta, as we know (from + * the data in the "struct test" earlier in this file) that + * its magnitude is ~ 10^5, with values of that magnitude, + * 10^-12 difference is a 10^-17 relative difference, and + * with a 56 bit mantissa (standard IEEE "double") a difference + * that small vanishes (requires at least 57 mantissa bits to + * be representable). If the data values were to change, then + * so might this delta (if they were not all the same, we would + * move the delta into the struct rather than having it a constant + * here.). + * + * Finally, note that our purpose here is not to test floating + * point arithmetic, we're testing locale dependent string to + * binary conversions. + */ + + d = (double)strtod(t->double_input, NULL); + diff = fabs(d - t->double_value); + if (diff >= 1e-12) + ATF_REQUIRE_EQ_MSG(d, t->double_value, "In %s: " + "d=strtod(t->double_input[%s], NULL)[%.12g = %a] != " + "t->double_value[%.12g = %a]: diff=%g", t->locale, + t->double_input, d, d, t->double_value, t->double_value, + diff); +} + +static void +h_sscanf(const struct test *t) +{ + int int_reported; + double double_reported; + + ATF_REQUIRE_STREQ(setlocale(LC_ALL, "C"), "C"); + printf("Trying locale %s...\n", t->locale); + ATF_REQUIRE(setlocale(LC_NUMERIC, t->locale) != NULL); + + sscanf(t->int_input, "%d", &int_reported); + ATF_REQUIRE_EQ(int_reported, t->int_value); + sscanf(t->double_input, "%lf", &double_reported); + ATF_REQUIRE_EQ(double_reported, t->double_value); +} + +ATF_TC(sprintf); +ATF_TC_HEAD(sprintf, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Checks sprintf %%'d and %%'f under different locales"); +} +ATF_TC_BODY(sprintf, tc) +{ + struct test *t; + + for (t = &tests[0]; t->locale != NULL; ++t) + h_sprintf(t); +} + +ATF_TC(strto); +ATF_TC_HEAD(strto, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Checks strtol and strtod under different locales"); +} +ATF_TC_BODY(strto, tc) +{ + struct test *t; + + for (t = &tests[0]; t->locale != NULL; ++t) + h_strto(t); +} + +ATF_TC(sscanf); +ATF_TC_HEAD(sscanf, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Checks sscanf under different locales"); +} +ATF_TC_BODY(sscanf, tc) +{ + struct test *t; + + for (t = &tests[0]; t->locale != NULL; ++t) + h_sscanf(t); +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, sprintf); + ATF_TP_ADD_TC(tp, sscanf); + ATF_TP_ADD_TC(tp, strto); + + return atf_no_error(); +} diff --git a/lib/libc/locale/t_strfmon.c b/lib/libc/locale/t_strfmon.c new file mode 100644 --- /dev/null +++ b/lib/libc/locale/t_strfmon.c @@ -0,0 +1,76 @@ +/* $NetBSD: t_strfmon.c,v 1.3 2021/08/02 17:41:07 andvar Exp $ */ + +/*- + * Copyright (c) 2017 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Joerg Sonnenberger. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +__RCSID("$NetBSD: t_strfmon.c,v 1.3 2021/08/02 17:41:07 andvar Exp $"); + +#include +#include +#include + +ATF_TC(strfmon); + +ATF_TC_HEAD(strfmon, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Checks strfmon_l under different locales"); +} + +ATF_TC_BODY(strfmon, tc) +{ + const struct { + const char *locale; + const char *expected; + } tests[] = { + { "C", "[ **1234.57] [ **1234.57]" }, + { "de_DE.UTF-8", "[ **1234,57 €] [ **1.234,57 EUR]" }, + { "en_GB.UTF-8", "[ £**1234.57] [ GBP**1,234.57]" }, + }; + locale_t loc; + size_t i; + char buf[80]; + for (i = 0; i < __arraycount(tests); ++i) { + loc = newlocale(LC_MONETARY_MASK, tests[i].locale, 0); + ATF_REQUIRE(loc != 0); + strfmon_l(buf, sizeof(buf), loc, "[%^=*#6n] [%=*#6i]", + 1234.567, 1234.567); + ATF_REQUIRE_STREQ(tests[i].expected, buf); + freelocale(loc); + } +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, strfmon); + + return atf_no_error(); +} diff --git a/lib/libc/locale/t_toupper.c b/lib/libc/locale/t_toupper.c new file mode 100644 --- /dev/null +++ b/lib/libc/locale/t_toupper.c @@ -0,0 +1,131 @@ +/* $NetBSD: t_toupper.c,v 1.2 2021/08/02 17:41:07 andvar Exp $ */ + +/*- + * Copyright (c) 2017 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Konrad Schroder + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +__COPYRIGHT("@(#) Copyright (c) 2017\ + The NetBSD Foundation, inc. All rights reserved."); +__RCSID("$NetBSD: t_toupper.c,v 1.2 2021/08/02 17:41:07 andvar Exp $"); + +#include +#include +#include +#include +#include + +#include + +static struct test { + const char *locale; + const char *lower; + const char *upper; +} tests[] = { + { + "C", + "abcde12345", + "ABCDE12345", + }, { + "ru_RU.KOI8-R", + "abcde12345\xc1\xc2\xd7\xc7\xc4\xc5\xa3", + "ABCDE12345\xe1\xe2\xf7\xe7\xe4\xe5\xb3", + }, { + NULL, + NULL, + NULL, + } +}; + +static void +h_swapcase(const struct test *t, int upperp) +{ + unsigned int i; + unsigned char answer, reported; + + ATF_REQUIRE_STREQ(setlocale(LC_ALL, "C"), "C"); + printf("Trying locale %s...\n", t->locale); + ATF_REQUIRE(setlocale(LC_CTYPE, t->locale) != NULL); + + for (i = 0; i < strlen(t->lower); i++) { + printf("Comparing char %d, lower %2.2x, with upper %2.2x\n", + i, (unsigned char)t->lower[i], (unsigned char)t->upper[i]); + if (upperp) { + answer = t->upper[i]; + reported = toupper((int)(unsigned char)t->lower[i]); + } else { + answer = t->lower[i]; + reported = tolower((int)(unsigned char)t->upper[i]); + } + printf(" expecting %2.2x, reported %2.2x\n", answer, reported); + ATF_REQUIRE_EQ(reported, answer); + } +} + +ATF_TC(toupper); + +ATF_TC_HEAD(toupper, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Checks toupper under different locales"); +} + +ATF_TC_BODY(toupper, tc) +{ + struct test *t; + + for (t = &tests[0]; t->locale != NULL; ++t) + h_swapcase(t, 1); +} + +ATF_TC(tolower); + +ATF_TC_HEAD(tolower, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Checks tolower under different locales"); +} + +ATF_TC_BODY(tolower, tc) +{ + struct test *t; + + /* atf_tc_expect_fail("%s", "LC_COLLATE not supported"); */ + for (t = &tests[0]; t->locale != NULL; ++t) + h_swapcase(t, 0); + /* atf_tc_expect_pass(); */ +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, toupper); + ATF_TP_ADD_TC(tp, tolower); + + return atf_no_error(); +} diff --git a/lib/libc/locale/t_wcscoll.c b/lib/libc/locale/t_wcscoll.c new file mode 100644 --- /dev/null +++ b/lib/libc/locale/t_wcscoll.c @@ -0,0 +1,151 @@ +/* $NetBSD: t_wcscoll.c,v 1.1 2017/07/14 14:57:43 perseant Exp $ */ + +/*- + * Copyright (c) 2017 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Konrad Schroder. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +__COPYRIGHT("@(#) Copyright (c) 2017\ + The NetBSD Foundation, inc. All rights reserved."); +__RCSID("$NetBSD: t_wcscoll.c,v 1.1 2017/07/14 14:57:43 perseant Exp $"); + +#include +#include +#include +#include +#include +#include + +#include + +static struct test { + const char *locale; + const wchar_t *in_order[10]; +} tests[] = { + { + "C", { + L"A string beginning with a" + L"Capital Letter", + L"always comes before", + L"another beginning lowercase", + L"assuming ASCII of course", + NULL } + } , { + "en_US.UTF-8", { + L"A string beginning with a" + L"Capital Letter", + L"always comes before", + L"another beginning lowercase", + L"assuming ASCII of course", + NULL } + }, + /* + * The rest of these examples are from Wikipedia. + */ + { + "de_DE.UTF-8", { + L"Arg", + L"Ärgerlich", + L"Arm", + L"Assistent", + L"Aßlar", + L"Assoziation", + NULL } + }, { + "ru_RU.KOI-8", { + L"едок", + L"ёж", + L"ездить", + NULL } + }, { /* Old-style Spanish collation, expect fail with DUCET */ + "es_ES.UTF-8", { + L"cinco", + L"credo", + L"chispa", + L"lomo", + L"luz", + L"llama", + NULL } + }, { /* We don't have Slovak locale files, expect fail */ + "sk_SK.UTF-8", { + L"baa", + L"baá", + L"báa", + L"bab", + L"báb", + L"bac", + L"bác", + L"bač", + L"báč", + NULL } + }, { + NULL, + { NULL } + } +}; + +static void +h_wcscoll(const struct test *t) +{ + const wchar_t * const *wcp; + const wchar_t * const *owcp; + + ATF_REQUIRE_STREQ(setlocale(LC_ALL, "C"), "C"); + printf("Trying locale %s...\n", t->locale); + ATF_REQUIRE(setlocale(LC_COLLATE, t->locale) != NULL); + printf("Using locale: %s\n", setlocale(LC_ALL, NULL)); + + for (wcp = &t->in_order[0], owcp = wcp++; + *wcp != NULL; owcp = wcp++) { + printf("Check L\"%S\" < L\"%S\"\n", *owcp, *wcp); + ATF_CHECK(wcscoll(*owcp, *wcp) < 0); + } +} + +ATF_TC(wcscoll); +ATF_TC_HEAD(wcscoll, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Checks collation using wcscoll(3) under different locales"); +} +ATF_TC_BODY(wcscoll, tc) +{ + struct test *t; + + atf_tc_expect_fail("LC_COLLATE support is not yet fully implemented"); + for (t = &tests[0]; t->locale != NULL; ++t) + h_wcscoll(t); +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, wcscoll); + + return atf_no_error(); +} diff --git a/lib/libc/locale/t_wcsrtombs.c b/lib/libc/locale/t_wcsrtombs.c new file mode 100644 --- /dev/null +++ b/lib/libc/locale/t_wcsrtombs.c @@ -0,0 +1,66 @@ +/* $NetBSD: t_wcsrtombs.c,v 1.1 2019/07/28 13:46:45 christos Exp $ */ + +/*- + * Copyright (c) 2019 The NetBSD Foundation, Inc. + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +__RCSID("$NetBSD: t_wcsrtombs.c,v 1.1 2019/07/28 13:46:45 christos Exp $"); + +#include +#include +#include +#include + +ATF_TC(wcsrtombs_advance); +ATF_TC_HEAD(wcsrtombs_advance, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test wcsrtombs(3) advances " + "the source pointer to the first illegal byte"); +} + +ATF_TC_BODY(wcsrtombs_advance, tc) +{ + wchar_t label[] = L"L" L"\u0403" L"bel"; + const wchar_t *wp = label; + char lbuf[128]; + mbstate_t mbstate; + size_t n; + + memset(&mbstate, 0, sizeof(mbstate)); + memset(lbuf, 0, sizeof(lbuf)); + n = wcsrtombs(lbuf, &wp, sizeof(lbuf), &mbstate); + + ATF_REQUIRE_EQ(n, (size_t)-1); + ATF_REQUIRE_EQ(errno, EILSEQ); + ATF_REQUIRE_EQ(wp - label, 1); +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, wcsrtombs_advance); + return atf_no_error(); +} diff --git a/lib/libc/locale/t_wcstod.c b/lib/libc/locale/t_wcstod.c --- a/lib/libc/locale/t_wcstod.c +++ b/lib/libc/locale/t_wcstod.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_wcstod.c,v 1.3 2011/10/01 17:56:11 christos Exp $ */ +/* $NetBSD: t_wcstod.c,v 1.5 2017/07/14 14:09:53 joerg Exp $ */ /*- * Copyright (c) 2011 The NetBSD Foundation, Inc. @@ -56,7 +56,7 @@ #include __COPYRIGHT("@(#) Copyright (c) 2011\ The NetBSD Foundation, inc. All rights reserved."); -__RCSID("$NetBSD: t_wcstod.c,v 1.3 2011/10/01 17:56:11 christos Exp $"); +__RCSID("$NetBSD: t_wcstod.c,v 1.5 2017/07/14 14:09:53 joerg Exp $"); #include #include @@ -384,6 +384,37 @@ }; #endif /* !defined(__vax__) */ + +ATF_TC(wcstombs); +ATF_TC_HEAD(wcstombs, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks wcstombs(3) on the inputs to the wcstod test"); +} +ATF_TC_BODY(wcstombs, tc) +{ +#if !defined(__vax__) + struct test *t; + size_t n; + char *buf; + + /* + * Previously this was part of the wcstod test. + */ + for (t = &tests[0]; t->wcs != NULL; ++t) { + printf("wcslen(\"%S\") = %zu\n", t->wcs, wcslen(t->wcs)); + n = wcstombs(NULL, t->wcs, 0); + printf("wcstombs(NULL, \"%S\", 0) = %d\n", t->wcs, (int)n); + ATF_REQUIRE((buf = (void *)calloc(1, n + 1)) != NULL); + (void)wcstombs(buf, t->wcs, n + 1); + printf("wcstombs(buf, \"%S\", %d) = \"%s\"\n", t->wcs, + (int)(n + 1), buf); + ATF_REQUIRE_EQ(strlen(buf), wcslen(t->wcs)); + + free(buf); + } +#endif +} + ATF_TC(wcstod); ATF_TC_HEAD(wcstod, tc) { @@ -398,17 +429,11 @@ #if !defined(__vax__) for (t = &tests[0]; t->wcs != NULL; ++t) { - double d; size_t n; + double d; wchar_t *tail; - char *buf; - /* we do not supported %ls nor %S yet. */ - n = wcstombs(NULL, t->wcs, 0); - ATF_REQUIRE((buf = (void *)malloc(n + 1)) != NULL); - (void)wcstombs(buf, t->wcs, n + 1); - (void)printf("Checking wcstod(\"%s\", &tail):\n", buf); - free(buf); + (void)printf("Checking wcstod(\"%S\", &tail):\n", t->wcs); errno = 0; d = wcstod(t->wcs, &tail); @@ -451,6 +476,7 @@ ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, wcstod); + ATF_TP_ADD_TC(tp, wcstombs); return atf_no_error(); } diff --git a/lib/libc/locale/t_wctomb.c b/lib/libc/locale/t_wctomb.c --- a/lib/libc/locale/t_wctomb.c +++ b/lib/libc/locale/t_wctomb.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_wctomb.c,v 1.3 2013/03/25 15:31:03 gson Exp $ */ +/* $NetBSD: t_wctomb.c,v 1.5 2017/07/12 17:32:51 perseant Exp $ */ /*- * Copyright (c) 2011 The NetBSD Foundation, Inc. @@ -55,7 +55,7 @@ #include __COPYRIGHT("@(#) Copyright (c) 2011\ The NetBSD Foundation, inc. All rights reserved."); -__RCSID("$NetBSD: t_wctomb.c,v 1.3 2013/03/25 15:31:03 gson Exp $"); +__RCSID("$NetBSD: t_wctomb.c,v 1.5 2017/07/12 17:32:51 perseant Exp $"); #include #include @@ -76,6 +76,7 @@ const char *data; size_t wclen; size_t mblen[16]; + size_t stateful; } tests[] = { { "ja_JP.ISO2022-JP", @@ -87,13 +88,15 @@ "\xb1\xb2\xb3" /* "aiu" */ "\x1b(B", /* ISO 646 */ 3 + 3 + 3, - { 3+2, 2, 2, 3+1, 1, 1, 3+1, 1, 1, 3+1 } + { 3+2, 2, 2, 3+1, 1, 1, 3+1, 1, 1, 3+1 }, + 1, }, { "C", "ABC", 3, - { 1, 1, 1, 1 } -}, { NULL, NULL, 0, { } } + { 1, 1, 1, 1 }, + 0, +}, { NULL, NULL, 0, { }, 0 } }; static void @@ -109,19 +112,24 @@ size_t sz, ret, i; ATF_REQUIRE_STREQ(setlocale(LC_ALL, "C"), "C"); + (void)printf("Trying locale: %s\n", t->locale); ATF_REQUIRE(setlocale(LC_CTYPE, t->locale) != NULL); + if (tc == TC_WCRTOMB_ST) { + (void)memset(&st, 0, sizeof(st)); + stp = &st; + } else { + (void)printf("Checking correct reporting of statefulness\n"); + ret = wctomb(NULL, 0); + ATF_REQUIRE_EQ(t->stateful, !!ret); + } + (void)strvis(buf, t->data, VIS_WHITE | VIS_OCTAL); (void)printf("Checking sequence: \"%s\"\n", buf); ATF_REQUIRE((str = setlocale(LC_ALL, NULL)) != NULL); (void)printf("Using locale: %s\n", str); - if (tc == TC_WCRTOMB_ST) { - (void)memset(&st, 0, sizeof(st)); - stp = &st; - } - wcs[t->wclen] = L'X'; /* poison */ pcs = t->data; sz = mbsrtowcs(wcs, &pcs, t->wclen + 2, NULL); @@ -141,6 +149,8 @@ (void)printf("At position %zd:\n", i); (void)printf(" expected: %zd\n", t->mblen[i]); (void)printf(" got : %zd\n", ret); + (void)strvis(buf, cs, VIS_WHITE | VIS_OCTAL); + (void)printf(" sequence: \"%s\"\n", buf); atf_tc_fail("Test failed"); /* NOTREACHED */ } diff --git a/lib/libc/locale/t_wctype.c b/lib/libc/locale/t_wctype.c new file mode 100644 --- /dev/null +++ b/lib/libc/locale/t_wctype.c @@ -0,0 +1,282 @@ +/* $NetBSD: t_wctype.c,v 1.2 2017/07/12 17:32:51 perseant Exp $ */ + +/*- + * Copyright (c) 2017 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Konrad Schroder + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +__COPYRIGHT("@(#) Copyright (c) 2017\ + The NetBSD Foundation, inc. All rights reserved."); +__RCSID("$NetBSD: t_wctype.c,v 1.2 2017/07/12 17:32:51 perseant Exp $"); + +#include +#include +#include +#include +#include +#include +#include + +#include + +static const char *typenames[] = { + "alnum", + "alpha", + "blank", + "cntrl", + "digit", + "graph", + "ideogram", + "lower", + "phonogram", + "print", + "punct", + "rune", + "space", + "special", + "upper", + "xdigit", +}; + +static struct test { + const char *locale; + const struct { + const char *in; + const char *out; + } classes[16]; +} tests[] = { + { + "C", + { + { /* alnum */ "abc123", "&^%$" }, + { /* alpha */ "ABCabc", "123*&^" }, + { /* blank */ " \t", "abc123%$^&\r\n" }, + { /* cntrl */ "\a", "abc123^&*" }, + { /* digit */ "123", "abc*()" }, + { /* graph */ "abc123ABC[];?~", " " }, + { /* ideogram */ "", "" }, + { /* lower */ "abc", "ABC123&*(" }, + { /* phonogram */ "", "" }, + { /* print */ "abcABC123%^&* ", "\r\n\t" }, + { /* punct */ ".,;:!?", "abc123 \n" }, + { /* rune */ "", "" }, + { /* space */ " \t\r\n", "abc123ABC&*(" }, + { /* special */ "", "" }, + { /* upper */ "ABC", "abc123&*(" }, + { /* xdigit */ "123abcABC", "xyz" }, + }, + }, { + "en_US.UTF-8", + { + { /* alnum */ "abc123", "&^%$" }, + { /* alpha */ "ABCabc", "123*&^" }, + { /* blank */ " \t", "abc123%$^&\r\n" }, + { /* cntrl */ "\a", "abc123^&*" }, + { /* digit */ "123", "abc*()" }, + { /* graph */ "abc123ABC[];?~", " " }, + { /* ideogram */ "", "" }, + { /* lower */ "abc", "ABC123&*(" }, + { /* phonogram */ "", "" }, + { /* print */ "abcABC123%^&* ", "\r\n\t" }, + { /* punct */ ".,;:!?", "abc123 \n" }, + { /* rune */ "", "" }, + { /* space */ " \t\r\n", "abc123ABC&*(" }, + { /* special */ "", "" }, + { /* upper */ "ABC", "abc123&*(" }, + { /* xdigit */ "123abcABC", "xyz" }, + }, + }, { + "ru_RU.KOI8-R", + { + { /* alnum */ "abc123i\xa3\xb3\xd6", "&^%$" }, + { /* alpha */ "ABCabci\xa3\xb3\xd6", "123*&^" }, + { /* blank */ " \t", "abc123%$^&\r\n" }, + { /* cntrl */ "\a", "abc123^&*" }, + { /* digit */ "123", "abc*()" }, + { /* graph */ "abc123ABC[];?~i\xa3\xb3\xd6", " " }, + { /* ideogram */ "", "" }, + { /* lower */ "abci\xa3\xd6", "ABC123&*(\xb3\xf6" }, + { /* phonogram */ "", "" }, + { /* print */ "abcABC123%^&* \xa3\xb3\xd6\xbf", "\r\n\t" }, + { /* punct */ ".,;:!?", "abc123 \n" }, + { /* rune */ "", "" }, + { /* space */ " \t\r\n", "abc123ABC&*(\xa3\xb3\xd6" }, + { /* special */ "", "" }, + { /* upper */ "ABC\xb3\xe0\xff", "abc123&*(\xa3\xc0\xdf" }, + { /* xdigit */ "123abcABC", "xyz\xc1\xc5" }, + } + }, { + NULL, + { {NULL, NULL}, {NULL, NULL}, {NULL, NULL}, {NULL, NULL}, {NULL, NULL}, {NULL, NULL}, {NULL, NULL}, {NULL, NULL}, {NULL, NULL}, {NULL, NULL}, {NULL, NULL}, {NULL, NULL}, {NULL, NULL}, {NULL, NULL}, {NULL, NULL}, {NULL, NULL}, } + } +}; + +static void testall(int, int, wchar_t, wctype_t, int); + +static void +h_ctype(const struct test *t) +{ + wctype_t wct; + wchar_t wc; + int i; + const char *cp; + + ATF_REQUIRE_STREQ(setlocale(LC_ALL, "C"), "C"); + printf("Trying locale %s...\n", t->locale); + ATF_REQUIRE(setlocale(LC_CTYPE, t->locale) != NULL); + printf("Using locale %s\n", setlocale(LC_CTYPE, NULL)); + + for (i = 0; i < 16; i++) { + /* Skip any type that has no chars */ + if (t->classes[i].in[0] == '\0' && + t->classes[i].out[0] == '\0') + continue; + + /* First examine wctype */ + wct = wctype(typenames[i]); + ATF_REQUIRE(wct != 0); + + /* Convert in to wide and check that they match */ + for (cp = (const char *)(t->classes[i].in); *cp; ++cp) { + wc = btowc((unsigned char)*cp); + testall(i, (unsigned char)*cp, wc, wct, 1); + } + /* Convert out to wide and check that they do not match */ + for (cp = (const char *)(t->classes[i].out); *cp; ++cp) { + wc = btowc((unsigned char)*cp); + testall(i, (unsigned char)*cp, wc, wct, 0); + } + } +} + +void testall(int idx, int c, wchar_t wc, wctype_t wct, int inout) +{ + printf("Testing class %d (%s), char %c (0x%2.2x), wc %x, wct %ld, expect %d\n", + idx, typenames[idx], c, c, wc, (long int)wct, inout); + ATF_REQUIRE(!!iswctype(wc, wct) == inout); + + /* Switch based on wct and call is{,w}* to check */ + switch (idx) { + case 0: /* alnum */ + ATF_REQUIRE_EQ(!!isalnum(c), inout); + ATF_REQUIRE_EQ(!!iswalnum(wc), inout); + break; + case 1: /* alpha */ + ATF_REQUIRE_EQ(!!isalpha(c), inout); + ATF_REQUIRE_EQ(!!iswalpha(wc), inout); + break; + case 2: /* blank */ + ATF_REQUIRE_EQ(!!isblank(c), inout); + ATF_REQUIRE_EQ(!!iswblank(wc), inout); + break; + case 3: /* cntrl */ + ATF_REQUIRE_EQ(!!iscntrl(c), inout); + ATF_REQUIRE_EQ(!!iswcntrl(wc), inout); + break; + case 4: /* digit */ + ATF_REQUIRE_EQ(!!isdigit(c), inout); + ATF_REQUIRE_EQ(!!iswdigit(wc), inout); + break; + case 5: /* graph */ + ATF_REQUIRE_EQ(!!isgraph(c), inout); + ATF_REQUIRE_EQ(!!iswgraph(wc), inout); + break; + case 6: /* ideogram */ +#if 0 + ATF_REQUIRE_EQ(!!isideogram(c), inout); + ATF_REQUIRE_EQ(!!iswideogram(wc), inout); +#endif + break; + case 7: /* lower */ + ATF_REQUIRE_EQ(!!islower(c), inout); + ATF_REQUIRE_EQ(!!iswlower(wc), inout); + break; + case 8: /* phonogram */ +#if 0 + ATF_REQUIRE_EQ(!!isphonogram(c), inout); + ATF_REQUIRE_EQ(!!iswphonogram(wc), inout); +#endif + break; + case 9: /* print */ + ATF_REQUIRE_EQ(!!isprint(c), inout); + ATF_REQUIRE_EQ(!!iswprint(wc), inout); + break; + case 10: /* punct */ + ATF_REQUIRE_EQ(!!ispunct(c), inout); + ATF_REQUIRE_EQ(!!iswpunct(wc), inout); + break; + case 11: /* rune */ +#if 0 + ATF_REQUIRE_EQ(!!isrune(c), inout); + ATF_REQUIRE_EQ(!!iswrune(wc), inout); +#endif + break; + case 12: /* space */ + ATF_REQUIRE_EQ(!!isspace(c), inout); + ATF_REQUIRE_EQ(!!iswspace(wc), inout); + break; + case 13: /* special */ +#if 0 + ATF_REQUIRE_EQ(!!isspecial(c), inout); + ATF_REQUIRE_EQ(!!iswspecial(wc), inout); +#endif + break; + case 14: /* upper */ + ATF_REQUIRE_EQ(!!isupper(c), inout); + ATF_REQUIRE_EQ(!!iswupper(wc), inout); + break; + case 15: /* xdigit */ + ATF_REQUIRE_EQ(!!isxdigit(c), inout); + ATF_REQUIRE_EQ(!!iswxdigit(wc), inout); + break; + } +} + +ATF_TC(ctype); + +ATF_TC_HEAD(ctype, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Checks is* and isw* under diferent locales"); +} + +ATF_TC_BODY(ctype, tc) +{ + struct test *t; + + for (t = &tests[0]; t->locale != NULL; ++t) + h_ctype(t); +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, ctype); + + return atf_no_error(); +} diff --git a/lib/libc/misc/Makefile b/lib/libc/misc/Makefile new file mode 100644 --- /dev/null +++ b/lib/libc/misc/Makefile @@ -0,0 +1,41 @@ +# $NetBSD: Makefile,v 1.7 2020/09/07 00:29:14 mrg Exp $ + +.include + +TESTSDIR= ${TESTSBASE}/lib/libc/misc + +TESTS_C+= t_ubsan +TESTS_CXX+= t_ubsanxx + +.PATH: ${NETBSDSRCDIR}/common/lib/libc/misc +SRCS.t_ubsan= t_ubsan.c +SRCS.t_ubsanxx= t_ubsanxx.cpp + +.if ${MKSANITIZER:Uno} != "yes" && ${MKLIBCSANITIZER:Uno} != "yes" +# These tests are designed to be used against micro-UBSan only. +# micro-UBSan is used in these tests as a standalone libary only. +CPPFLAGS+= -DENABLE_TESTS +SRCS.t_ubsan+= ubsan.c +SRCS.t_ubsanxx+= ubsan.c +UBSAN_FLAGS= -fsanitize=undefined +UBSAN_FLAGS+= ${${ACTIVE_CC} == "clang" :? -fsanitize=integer :} +UBSAN_FLAGS+= ${${ACTIVE_CC} == "clang" :? -fsanitize=nullability :} +CFLAGS+= ${UBSAN_FLAGS} +CXXFLAGS+= ${UBSAN_FLAGS} +CWARNFLAGS+= -Wno-return-type -Wno-strict-aliasing +CWARNFLAGS.clang+= -Wno-incompatible-pointer-types-discards-qualifiers +CWARNFLAGS.clang+= -Wno-nullability-completeness +.endif +COPTS.t_ubsan.c += -Wno-stack-protector +COPTS.t_ubsanxx.cpp += -Wno-stack-protector +COPTS.ubsan.c+= ${${ACTIVE_CC} == "clang" && ${MACHINE_ARCH} == "powerpc":? -O0 :} + +.if defined(HAVE_GCC) && ${HAVE_GCC} >= 7 && ${ACTIVE_CC} == "gcc" +COPTS.t_ubsan.c+= ${${ACTIVE_CC} == "gcc" && ${HAVE_GCC:U0} >= 7:? -Wno-int-in-bool-context :} +COPTS.t_ubsanxx.cpp+= ${${ACTIVE_CC} == "gcc" && ${HAVE_GCC:U0} >= 7:? -Wno-int-in-bool-context :} +.endif + +# XXX +COPTS.ubsan.c+= ${${ACTIVE_CC} == "gcc" && ${HAVE_GCC:U0} >= 9:? -Wno-error=builtin-declaration-mismatch :} + +.include diff --git a/lib/libc/misc/t_ubsan.c b/lib/libc/misc/t_ubsan.c new file mode 100644 --- /dev/null +++ b/lib/libc/misc/t_ubsan.c @@ -0,0 +1,913 @@ +/* $NetBSD: t_ubsan.c,v 1.6 2019/10/28 18:10:22 joerg Exp $ */ + +/*- + * Copyright (c) 2018 The NetBSD Foundation, Inc. + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +__COPYRIGHT("@(#) Copyright (c) 2018\ + The NetBSD Foundation, inc. All rights reserved."); +__RCSID("$NetBSD: t_ubsan.c,v 1.6 2019/10/28 18:10:22 joerg Exp $"); + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +#include +#define UBSAN_TC(a) ATF_TEST_CASE(a) +#define UBSAN_TC_HEAD(a, b) ATF_TEST_CASE_HEAD(a) +#define UBSAN_TC_BODY(a, b) ATF_TEST_CASE_BODY(a) +#define UBSAN_CASES(a) ATF_INIT_TEST_CASES(a) +#define UBSAN_TEST_CASE(a, b) ATF_ADD_TEST_CASE(a, b) +#define UBSAN_MD_VAR(a, b, c) set_md_var(b, c) +#define REINTERPRET_CAST(__dt, __st) reinterpret_cast<__dt>(__st) +#define STATIC_CAST(__dt, __st) static_cast<__dt>(__st) +#else +#include +#define UBSAN_TC(a) ATF_TC(a) +#define UBSAN_TC_HEAD(a, b) ATF_TC_HEAD(a, b) +#define UBSAN_TC_BODY(a, b) ATF_TC_BODY(a, b) +#define UBSAN_CASES(a) ATF_TP_ADD_TCS(a) +#define UBSAN_TEST_CASE(a, b) ATF_TP_ADD_TC(a, b) +#define UBSAN_MD_VAR(a, b, c) atf_tc_set_md_var(a, b, c) +#define REINTERPRET_CAST(__dt, __st) ((__dt)(__st)) +#define STATIC_CAST(__dt, __st) ((__dt)(__st)) +#endif + +#ifdef ENABLE_TESTS +static void +test_case(void (*fun)(void), const char *string) +{ + int filedes[2]; + pid_t pid; + FILE *fp; + size_t len; + char *buffer; + int status; + + /* + * Spawn a subprocess that triggers the issue. + * A child process either exits or is signaled with a crash signal. + */ + ATF_REQUIRE_EQ(pipe(filedes), 0); + pid = fork(); + ATF_REQUIRE(pid != -1); + if (pid == 0) { + ATF_REQUIRE(dup2(filedes[1], STDERR_FILENO) != -1); + ATF_REQUIRE(close(filedes[0]) == 0); + ATF_REQUIRE(close(filedes[1]) == 0); + + (*fun)(); + } + + ATF_REQUIRE(close(filedes[1]) == 0); + + fp = fdopen(filedes[0], "r"); + ATF_REQUIRE(fp != NULL); + + buffer = fgetln(fp, &len); + ATF_REQUIRE(buffer != 0); + ATF_REQUIRE(!ferror(fp)); + ATF_REQUIRE(strstr(buffer, string) != NULL); + ATF_REQUIRE(wait(&status) == pid); + ATF_REQUIRE(!WIFEXITED(status)); + ATF_REQUIRE(WIFSIGNALED(status)); + ATF_REQUIRE(!WIFSTOPPED(status)); + ATF_REQUIRE(!WIFCONTINUED(status)); +} + +UBSAN_TC(add_overflow_signed); +UBSAN_TC_HEAD(add_overflow_signed, tc) +{ + UBSAN_MD_VAR(tc, "descr", + "Checks -fsanitize=signed-integer-overflow"); +} + +static void +test_add_overflow_signed(void) +{ + volatile int a = INT_MAX; + volatile int b = atoi("1"); + + raise((a + b) ? SIGSEGV : SIGBUS); +} + +UBSAN_TC_BODY(add_overflow_signed, tc) +{ + + test_case(test_add_overflow_signed, " signed integer overflow: "); +} + +#ifdef __clang__ +UBSAN_TC(add_overflow_unsigned); +UBSAN_TC_HEAD(add_overflow_unsigned, tc) +{ + UBSAN_MD_VAR(tc, "descr", + "Checks -fsanitize=unsigned-integer-overflow"); +} + +static void +test_add_overflow_unsigned(void) +{ + volatile unsigned int a = UINT_MAX; + volatile unsigned int b = atoi("1"); + + raise((a + b) ? SIGSEGV : SIGBUS); +} + +UBSAN_TC_BODY(add_overflow_unsigned, tc) +{ + + test_case(test_add_overflow_unsigned, " unsigned integer overflow: "); +} +#endif + +UBSAN_TC(builtin_unreachable); +UBSAN_TC_HEAD(builtin_unreachable, tc) +{ + UBSAN_MD_VAR(tc, "descr", + "Checks -fsanitize=unreachable"); +} + +static void +test_builtin_unreachable(void) +{ + volatile int a = atoi("1"); + volatile int b = atoi("1"); + + if (a == b) { + __builtin_unreachable(); + } + // This shall not be reached + raise(SIGSEGV); +} + +UBSAN_TC_BODY(builtin_unreachable, tc) +{ + + test_case(test_builtin_unreachable, " calling __builtin_unreachable()"); +} + +UBSAN_TC(divrem_overflow_signed_div); +UBSAN_TC_HEAD(divrem_overflow_signed_div, tc) +{ + UBSAN_MD_VAR(tc, "descr", + "Checks -fsanitize=signed-integer-overflow"); +} + +static void +test_divrem_overflow_signed_div(void) +{ + volatile int a = INT_MIN; + volatile int b = atoi("-1"); + + raise((a / b) ? SIGSEGV : SIGBUS); // SIGFPE will be triggered before exiting +} + +UBSAN_TC_BODY(divrem_overflow_signed_div, tc) +{ + + test_case(test_divrem_overflow_signed_div, " signed integer overflow: "); +} + +UBSAN_TC(divrem_overflow_signed_mod); +UBSAN_TC_HEAD(divrem_overflow_signed_mod, tc) +{ + UBSAN_MD_VAR(tc, "descr", + "Checks -fsanitize=signed-integer-overflow"); +} + +static void +test_divrem_overflow_signed_mod(void) +{ + volatile int a = INT_MIN; + volatile int b = atoi("-1"); + + raise((a % b) ? SIGSEGV : SIGBUS); +} + +UBSAN_TC_BODY(divrem_overflow_signed_mod, tc) +{ + + test_case(test_divrem_overflow_signed_mod, " signed integer overflow: "); +} + +#if defined(__cplusplus) && defined(__clang__) && defined(__x86_64__) +UBSAN_TC(function_type_mismatch); +UBSAN_TC_HEAD(function_type_mismatch, tc) +{ + UBSAN_MD_VAR(tc, "descr", + "Checks -fsanitize=function"); +} + +static int +fun_type_mismatch(void) +{ + + return 0; +} + +static void +test_function_type_mismatch(void) +{ + + raise(reinterpret_cast + (reinterpret_cast(fun_type_mismatch))(1) ? SIGSEGV : SIGBUS); +} + +UBSAN_TC_BODY(function_type_mismatch, tc) +{ + + test_case(test_function_type_mismatch, " call to function "); +} +#endif + +#ifdef __clang__ +#define INVALID_BUILTIN(type) \ +UBSAN_TC(invalid_builtin_##type); \ +UBSAN_TC_HEAD(invalid_builtin_##type, tc) \ +{ \ + UBSAN_MD_VAR(tc, "descr", \ + "Checks -fsanitize=builtin"); \ +} \ + \ +static void \ +test_invalid_builtin_##type(void) \ +{ \ + \ + volatile int a = atoi("0"); \ + volatile int b = __builtin_##type(a); \ + raise(b ? SIGBUS : SIGSEGV); \ +} \ + \ +UBSAN_TC_BODY(invalid_builtin_##type, tc) \ +{ \ + \ + test_case(test_invalid_builtin_##type, \ + " passing zero to "); \ +} + +INVALID_BUILTIN(ctz) +INVALID_BUILTIN(ctzl) +INVALID_BUILTIN(ctzll) +INVALID_BUILTIN(clz) +INVALID_BUILTIN(clzl) +INVALID_BUILTIN(clzll) +#endif + +UBSAN_TC(load_invalid_value_bool); +UBSAN_TC_HEAD(load_invalid_value_bool, tc) +{ + UBSAN_MD_VAR(tc, "descr", + "Checks -fsanitize=bool"); +} + +static void +test_load_invalid_value_bool(void) +{ + volatile int a = INT_MAX - atoi("10"); + volatile bool b = *(REINTERPRET_CAST(volatile bool *, &a)); + + raise(b ? SIGSEGV : SIGBUS); +} + +UBSAN_TC_BODY(load_invalid_value_bool, tc) +{ + test_case(test_load_invalid_value_bool, " load of value "); +} + +#if defined(__cplusplus) // ? && (defined(__x86_64__) || defined(__i386__)) +UBSAN_TC(load_invalid_value_enum); +UBSAN_TC_HEAD(load_invalid_value_enum, tc) +{ + UBSAN_MD_VAR(tc, "descr", + "Checks -fsanitize=enum"); +} + +static void +test_load_invalid_value_enum(void) +{ + enum e { e1, e2, e3, e4 }; + volatile int a = INT_MAX - atoi("10"); + volatile enum e E = *(REINTERPRET_CAST(volatile enum e*, &a)); + + raise((E == e1) ? SIGSEGV : SIGBUS); +} + +UBSAN_TC_BODY(load_invalid_value_enum, tc) +{ + + test_case(test_load_invalid_value_enum, " load of value "); +} +#endif + +#ifdef __cplusplus +UBSAN_TC(missing_return); +UBSAN_TC_HEAD(missing_return, tc) +{ + UBSAN_MD_VAR(tc, "descr", + "Checks -fsanitize=return"); +} + +static int +fun_missing_return(void) +{ +} + +static void +test_missing_return(void) +{ + volatile int a = fun_missing_return(); + + // This interceptor shall be fatal, however if it won't generate + // a signal, do it on our own here: + raise(a ? SIGSEGV : SIGBUS); +} + +UBSAN_TC_BODY(missing_return, tc) +{ + + test_case(test_missing_return, + " execution reached the end of a value-returning function " + "without returning a value"); +} +#endif + +UBSAN_TC(mul_overflow_signed); +UBSAN_TC_HEAD(mul_overflow_signed, tc) +{ + UBSAN_MD_VAR(tc, "descr", + "Checks -fsanitize=signed-integer-overflow"); +} + +static void +test_mul_overflow_signed(void) +{ + volatile int a = INT_MAX; + volatile int b = atoi("2"); + + raise((a * b) ? SIGSEGV : SIGBUS); +} + +UBSAN_TC_BODY(mul_overflow_signed, tc) +{ + + test_case(test_mul_overflow_signed, " signed integer overflow: "); +} + +#ifdef __clang__ +UBSAN_TC(mul_overflow_unsigned); +UBSAN_TC_HEAD(mul_overflow_unsigned, tc) +{ + UBSAN_MD_VAR(tc, "descr", + "Checks -fsanitize=unsigned-integer-overflow"); +} + +static void +test_mul_overflow_unsigned(void) +{ + volatile unsigned int a = UINT_MAX; + volatile unsigned int b = atoi("2"); + + raise((a * b) ? SIGSEGV : SIGBUS); +} + +UBSAN_TC_BODY(mul_overflow_unsigned, tc) +{ + + test_case(test_mul_overflow_unsigned, " unsigned integer overflow: "); +} +#endif + +#ifdef __clang__ +UBSAN_TC(negate_overflow_signed); +UBSAN_TC_HEAD(negate_overflow_signed, tc) +{ + UBSAN_MD_VAR(tc, "descr", + "Checks -fsanitize=signed-integer-overflow"); +} + +static void +test_negate_overflow_signed(void) +{ + volatile int a = INT_MIN; + + raise(-a ? SIGSEGV : SIGBUS); +} + +UBSAN_TC_BODY(negate_overflow_signed, tc) +{ + + test_case(test_negate_overflow_signed, " negation of "); +} + +UBSAN_TC(negate_overflow_unsigned); +UBSAN_TC_HEAD(negate_overflow_unsigned, tc) +{ + UBSAN_MD_VAR(tc, "descr", + "Checks -fsanitize=unsigned-integer-overflow"); +} + +static void +test_negate_overflow_unsigned(void) +{ + volatile unsigned int a = UINT_MAX; + + raise(-a ? SIGSEGV : SIGBUS); +} + +UBSAN_TC_BODY(negate_overflow_unsigned, tc) +{ + + test_case(test_negate_overflow_unsigned, " negation of "); +} +#endif + +#ifdef __clang__ +UBSAN_TC(nonnull_arg); +UBSAN_TC_HEAD(nonnull_arg, tc) +{ + UBSAN_MD_VAR(tc, "descr", + "Checks -fsanitize=nullability-arg"); +} + +static void * +fun_nonnull_arg(void * _Nonnull ptr) +{ + + return ptr; +} + +static void +test_nonnull_arg(void) +{ + volatile intptr_t a = atoi("0"); + + raise(fun_nonnull_arg(REINTERPRET_CAST(void *, a)) ? SIGSEGV : SIGBUS); +} + +UBSAN_TC_BODY(nonnull_arg, tc) +{ + + test_case(test_nonnull_arg, " null pointer passed as argument "); +} + +UBSAN_TC(nonnull_assign); +UBSAN_TC_HEAD(nonnull_assign, tc) +{ + UBSAN_MD_VAR(tc, "descr", + "Checks -fsanitize=nullability-assign"); +} + +static volatile void * _Nonnull +fun_nonnull_assign(intptr_t a) +{ + volatile void *_Nonnull ptr; + + ptr = REINTERPRET_CAST(void *, a); + + return ptr; +} + +static void +test_nonnull_assign(void) +{ + volatile intptr_t a = atoi("0"); + + raise(fun_nonnull_assign(a) ? SIGSEGV : SIGBUS); +} + +UBSAN_TC_BODY(nonnull_assign, tc) +{ + + test_case(test_nonnull_assign, " _Nonnull binding to null pointer of type "); +} + +UBSAN_TC(nonnull_return); +UBSAN_TC_HEAD(nonnull_return, tc) +{ + UBSAN_MD_VAR(tc, "descr", + "Checks -fsanitize=nullability-return"); +} + +static void * _Nonnull +fun_nonnull_return(void) +{ + volatile intptr_t a = atoi("0"); + + return REINTERPRET_CAST(void *, a); +} + +static void +test_nonnull_return(void) +{ + + raise(fun_nonnull_return() ? SIGSEGV : SIGBUS); +} + +UBSAN_TC_BODY(nonnull_return, tc) +{ + + test_case(test_nonnull_return, " null pointer returned from function "); +} +#endif + +UBSAN_TC(out_of_bounds); +UBSAN_TC_HEAD(out_of_bounds, tc) +{ + UBSAN_MD_VAR(tc, "descr", + "Checks -fsanitize=bounds"); +} + +static void +test_out_of_bounds(void) +{ + int A[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + volatile int a = atoi("10"); + + raise(A[a] ? SIGSEGV : SIGBUS); +} + +UBSAN_TC_BODY(out_of_bounds, tc) +{ + + test_case(test_out_of_bounds, " index 10 is out of range for type "); +} + +#ifdef __clang__ +UBSAN_TC(pointer_overflow); +UBSAN_TC_HEAD(pointer_overflow, tc) +{ + UBSAN_MD_VAR(tc, "descr", + "Checks -fsanitize=pointer-overflow"); +} + +static void +test_pointer_overflow(void) +{ + volatile uintptr_t a = UINTPTR_MAX; + volatile uintptr_t b = atoi("1"); + volatile int *ptr = REINTERPRET_CAST(int *, a); + + raise((ptr + b) ? SIGSEGV : SIGBUS); +} + +UBSAN_TC_BODY(pointer_overflow, tc) +{ + + test_case(test_pointer_overflow, " pointer expression with base "); +} +#endif + +#ifndef __cplusplus +UBSAN_TC(shift_out_of_bounds_signednessbit); +UBSAN_TC_HEAD(shift_out_of_bounds_signednessbit, tc) +{ + UBSAN_MD_VAR(tc, "descr", + "Checks -fsanitize=shift"); +} + +static void +test_shift_out_of_bounds_signednessbit(void) +{ + volatile int32_t a = atoi("1"); + + raise((a << 31) != 0 ? SIGSEGV : SIGBUS); +} + +UBSAN_TC_BODY(shift_out_of_bounds_signednessbit, tc) +{ + + test_case(test_shift_out_of_bounds_signednessbit, " left shift of "); +} +#endif + +UBSAN_TC(shift_out_of_bounds_signedoverflow); +UBSAN_TC_HEAD(shift_out_of_bounds_signedoverflow, tc) +{ + UBSAN_MD_VAR(tc, "descr", + "Checks -fsanitize=shift"); +} + +static void +test_shift_out_of_bounds_signedoverflow(void) +{ + volatile int32_t a = atoi("1"); + volatile int32_t b = atoi("30"); + a <<= b; + + raise((a << 10) != 0 ? SIGSEGV : SIGBUS); +} + +UBSAN_TC_BODY(shift_out_of_bounds_signedoverflow, tc) +{ + + test_case(test_shift_out_of_bounds_signedoverflow, " left shift of "); +} + +UBSAN_TC(shift_out_of_bounds_negativeexponent); +UBSAN_TC_HEAD(shift_out_of_bounds_negativeexponent, tc) +{ + UBSAN_MD_VAR(tc, "descr", + "Checks -fsanitize=shift"); +} + +static void +test_shift_out_of_bounds_negativeexponent(void) +{ + volatile int32_t a = atoi("1"); + volatile int32_t b = atoi("-10"); + + raise((a << b) != 0 ? SIGSEGV : SIGBUS); +} + +UBSAN_TC_BODY(shift_out_of_bounds_negativeexponent, tc) +{ + + test_case(test_shift_out_of_bounds_negativeexponent, " shift exponent -"); +} + +UBSAN_TC(shift_out_of_bounds_toolargeexponent); +UBSAN_TC_HEAD(shift_out_of_bounds_toolargeexponent, tc) +{ + UBSAN_MD_VAR(tc, "descr", + "Checks -fsanitize=shift"); +} + +static void +test_shift_out_of_bounds_toolargeexponent(void) +{ + volatile int32_t a = atoi("1"); + volatile int32_t b = atoi("40"); + + raise((a << b) != 0 ? SIGSEGV : SIGBUS); +} + +UBSAN_TC_BODY(shift_out_of_bounds_toolargeexponent, tc) +{ + + test_case(test_shift_out_of_bounds_toolargeexponent, " shift exponent "); +} + +#ifdef __clang__ +UBSAN_TC(sub_overflow_signed); +UBSAN_TC_HEAD(sub_overflow_signed, tc) +{ + UBSAN_MD_VAR(tc, "descr", + "Checks -fsanitize=signed-integer-overflow"); +} + +static void +test_sub_overflow_signed(void) +{ + volatile int a = INT_MIN; + volatile int b = atoi("1"); + + raise((a - b) ? SIGSEGV : SIGBUS); +} + +UBSAN_TC_BODY(sub_overflow_signed, tc) +{ + + test_case(test_sub_overflow_signed, " signed integer overflow: "); +} + +UBSAN_TC(sub_overflow_unsigned); +UBSAN_TC_HEAD(sub_overflow_unsigned, tc) +{ + UBSAN_MD_VAR(tc, "descr", + "Checks -fsanitize=unsigned-integer-overflow"); +} + +static void +test_sub_overflow_unsigned(void) +{ + volatile unsigned int a = atoi("0"); + volatile unsigned int b = atoi("1"); + + raise((a - b) ? SIGSEGV : SIGBUS); +} + +UBSAN_TC_BODY(sub_overflow_unsigned, tc) +{ + + test_case(test_sub_overflow_unsigned, " unsigned integer overflow: "); +} +#endif + +#ifndef __clang__ +// The Clang/LLVM code generation does not catch every misaligned access +UBSAN_TC(type_mismatch_misaligned); +UBSAN_TC_HEAD(type_mismatch_misaligned, tc) +{ + UBSAN_MD_VAR(tc, "descr", + "Checks -fsanitize=alignment"); +} + +static void +test_type_mismatch_misaligned(void) +{ + volatile int8_t A[10] __aligned(4); + volatile int *b; + + memset(__UNVOLATILE(A), 0, sizeof(A)); + b = REINTERPRET_CAST(volatile int *, &A[1]); + + raise((*b) ? SIGSEGV : SIGBUS); +} + +UBSAN_TC_BODY(type_mismatch_misaligned, tc) +{ + + test_case(test_type_mismatch_misaligned, " load of misaligned address "); +} +#endif + +UBSAN_TC(vla_bound_not_positive); +UBSAN_TC_HEAD(vla_bound_not_positive, tc) +{ + UBSAN_MD_VAR(tc, "descr", + "Checks -fsanitize=vla-bound"); +} + +static void +test_vla_bound_not_positive(void) +{ + volatile int a = atoi("-1"); + int A[a]; + + raise(A[0] ? SIGBUS : SIGSEGV); +} + +UBSAN_TC_BODY(vla_bound_not_positive, tc) +{ + + test_case(test_vla_bound_not_positive, " variable length array bound value "); +} + +UBSAN_TC(integer_divide_by_zero); +UBSAN_TC_HEAD(integer_divide_by_zero, tc) +{ + UBSAN_MD_VAR(tc, "descr", + "Checks -fsanitize=integer-divide-by-zero"); +} + +static void +test_integer_divide_by_zero(void) +{ + volatile int a = atoi("-1"); + volatile int b = atoi("0"); + + raise((a / b) ? SIGSEGV : SIGBUS); +} + +UBSAN_TC_BODY(integer_divide_by_zero, tc) +{ + + test_case(test_integer_divide_by_zero, " signed integer overflow: "); +} + +#ifdef __clang__ +UBSAN_TC(float_divide_by_zero); +UBSAN_TC_HEAD(float_divide_by_zero, tc) +{ + UBSAN_MD_VAR(tc, "descr", + "Checks -fsanitize=float-divide-by-zero"); +} + +static void +test_float_divide_by_zero(void) +{ + volatile float a = strtof("1.5", NULL); + volatile float b = strtof("0.0", NULL); + + raise((a / b) > 0 ? SIGSEGV : SIGBUS); +} + +UBSAN_TC_BODY(float_divide_by_zero, tc) +{ + + test_case(test_float_divide_by_zero, " unsigned integer overflow: "); +} +#endif + +#else +UBSAN_TC(dummy); +UBSAN_TC_HEAD(dummy, tc) +{ + UBSAN_MD_VAR(tc, "descr", + "A dummy test"); +} + +UBSAN_TC_BODY(dummy, tc) +{ + + // Dummy, skipped + // The ATF framework requires at least a single defined test. +} +#endif + +UBSAN_CASES(tp) +{ +#ifdef ENABLE_TESTS + UBSAN_TEST_CASE(tp, add_overflow_signed); +#ifdef __clang__ + UBSAN_TEST_CASE(tp, add_overflow_unsigned); +#endif + UBSAN_TEST_CASE(tp, builtin_unreachable); +// UBSAN_TEST_CASE(tp, cfi_bad_type); // TODO +// UBSAN_TEST_CASE(tp, cfi_check_fail); // TODO + UBSAN_TEST_CASE(tp, divrem_overflow_signed_div); + UBSAN_TEST_CASE(tp, divrem_overflow_signed_mod); +// UBSAN_TEST_CASE(tp, dynamic_type_cache_miss); // Not supported in uUBSan +// UBSAN_TEST_CASE(tp, float_cast_overflow); // TODO +#if defined(__cplusplus) && defined(__clang__) && defined(__x86_64__) + UBSAN_TEST_CASE(tp, function_type_mismatch); +#endif +#ifdef __clang__ + UBSAN_TEST_CASE(tp, invalid_builtin_ctz); + UBSAN_TEST_CASE(tp, invalid_builtin_ctzl); + UBSAN_TEST_CASE(tp, invalid_builtin_ctzll); + UBSAN_TEST_CASE(tp, invalid_builtin_clz); + UBSAN_TEST_CASE(tp, invalid_builtin_clzl); + UBSAN_TEST_CASE(tp, invalid_builtin_clzll); +#endif + UBSAN_TEST_CASE(tp, load_invalid_value_bool); +#ifdef __cplusplus // ? && (defined(__x86_64__) || defined(__i386__)) + UBSAN_TEST_CASE(tp, load_invalid_value_enum); +#endif +#ifdef __cplusplus + UBSAN_TEST_CASE(tp, missing_return); +#endif + UBSAN_TEST_CASE(tp, mul_overflow_signed); +#ifdef __clang__ + UBSAN_TEST_CASE(tp, mul_overflow_unsigned); + UBSAN_TEST_CASE(tp, negate_overflow_signed); + UBSAN_TEST_CASE(tp, negate_overflow_unsigned); + // Clang/LLVM specific extension + // http://clang.llvm.org/docs/AttributeReference.html#nullability-attributes + UBSAN_TEST_CASE(tp, nonnull_arg); + UBSAN_TEST_CASE(tp, nonnull_assign); + UBSAN_TEST_CASE(tp, nonnull_return); +#endif + UBSAN_TEST_CASE(tp, out_of_bounds); +#ifdef __clang__ + UBSAN_TEST_CASE(tp, pointer_overflow); +#endif +#ifndef __cplusplus + // Acceptable in C++11 + UBSAN_TEST_CASE(tp, shift_out_of_bounds_signednessbit); +#endif + UBSAN_TEST_CASE(tp, shift_out_of_bounds_signedoverflow); + UBSAN_TEST_CASE(tp, shift_out_of_bounds_negativeexponent); + UBSAN_TEST_CASE(tp, shift_out_of_bounds_toolargeexponent); +#ifdef __clang__ + UBSAN_TEST_CASE(tp, sub_overflow_signed); + UBSAN_TEST_CASE(tp, sub_overflow_unsigned); +#endif +#ifndef __clang__ + UBSAN_TEST_CASE(tp, type_mismatch_misaligned); +#endif + UBSAN_TEST_CASE(tp, vla_bound_not_positive); + UBSAN_TEST_CASE(tp, integer_divide_by_zero); +#ifdef __clang__ + UBSAN_TEST_CASE(tp, float_divide_by_zero); +#endif +#else + UBSAN_TEST_CASE(tp, dummy); +#endif + +#ifndef __cplusplus + return atf_no_error(); +#endif +} diff --git a/lib/libc/misc/t_ubsanxx.cpp b/lib/libc/misc/t_ubsanxx.cpp new file mode 100644 --- /dev/null +++ b/lib/libc/misc/t_ubsanxx.cpp @@ -0,0 +1,29 @@ +/* $NetBSD: t_ubsanxx.cpp,v 1.1 2018/08/03 04:18:40 kamil Exp $ */ + +/*- + * Copyright (c) 2018 The NetBSD Foundation, Inc. + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 "t_ubsan.c" diff --git a/lib/libc/net/Makefile b/lib/libc/net/Makefile --- a/lib/libc/net/Makefile +++ b/lib/libc/net/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.10 2014/01/09 02:18:10 christos Exp $ +# $NetBSD: Makefile,v 1.14 2020/06/01 01:03:21 kamil Exp $ .include @@ -11,6 +11,7 @@ TESTS_C+= t_getprotoent TESTS_C+= t_ether_aton +TESTS_C+= t_if_nametoindex SRCS.t_ether_aton= aton_ether_subr.c t_ether_aton.c diff --git a/lib/libc/net/getaddrinfo/h_gai.c b/lib/libc/net/getaddrinfo/h_gai.c --- a/lib/libc/net/getaddrinfo/h_gai.c +++ b/lib/libc/net/getaddrinfo/h_gai.c @@ -1,4 +1,4 @@ -/* $NetBSD: h_gai.c,v 1.1 2011/01/12 02:58:40 pgoyette Exp $ */ +/* $NetBSD: h_gai.c,v 1.2 2019/02/03 10:48:47 mrg Exp $ */ /* * Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000, 2001, and 2002 WIDE Project. @@ -111,7 +111,7 @@ extern int optind; extern char *optarg; int c; - char nbuf[10]; + char nbuf[14]; memset(&ai, 0, sizeof(ai)); ai.ai_family = PF_UNSPEC; diff --git a/lib/libc/net/getaddrinfo/no_serv_v4.exp b/lib/libc/net/getaddrinfo/no_serv_v4.exp --- a/lib/libc/net/getaddrinfo/no_serv_v4.exp +++ b/lib/libc/net/getaddrinfo/no_serv_v4.exp @@ -11,4 +11,4 @@ ai2: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv 0 arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host (empty) serv (empty) -hostname nor servname provided, or not known +hostname or servname not provided or not known diff --git a/lib/libc/net/getaddrinfo/no_serv_v4v6.exp b/lib/libc/net/getaddrinfo/no_serv_v4v6.exp --- a/lib/libc/net/getaddrinfo/no_serv_v4v6.exp +++ b/lib/libc/net/getaddrinfo/no_serv_v4v6.exp @@ -13,4 +13,4 @@ ai4: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv 0 arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host (empty) serv (empty) -hostname nor servname provided, or not known +hostname or servname not provided or not known diff --git a/lib/libc/net/getaddrinfo/t_getaddrinfo.sh b/lib/libc/net/getaddrinfo/t_getaddrinfo.sh old mode 100755 new mode 100644 --- a/lib/libc/net/getaddrinfo/t_getaddrinfo.sh +++ b/lib/libc/net/getaddrinfo/t_getaddrinfo.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_getaddrinfo.sh,v 1.2 2011/06/15 07:54:32 jmmv Exp $ +# $NetBSD: t_getaddrinfo.sh,v 1.3 2019/01/10 11:13:50 pgoyette Exp $ # # Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000, 2001, and 2002 WIDE Project. @@ -55,7 +55,7 @@ fi cmp -s $(atf_get_srcdir)/data/${exp} out && return - diff -u $(atf_get_srcdir)/data/${exp} out && \ + diff -u $(atf_get_srcdir)/data/${exp} out atf_fail "Actual output does not match expected output" } diff --git a/lib/libc/net/h_nsd_recurse.c b/lib/libc/net/h_nsd_recurse.c --- a/lib/libc/net/h_nsd_recurse.c +++ b/lib/libc/net/h_nsd_recurse.c @@ -1,4 +1,4 @@ -/* $NetBSD: h_nsd_recurse.c,v 1.2 2011/01/13 02:24:51 pgoyette Exp $ */ +/* $NetBSD: h_nsd_recurse.c,v 1.3 2020/06/01 01:03:21 kamil Exp $ */ /*- * Copyright (c) 2008 The NetBSD Foundation, Inc. @@ -32,9 +32,11 @@ #include __COPYRIGHT("@(#) Copyright (c) 2008\ The NetBSD Foundation, inc. All rights reserved."); -__RCSID("$NetBSD: h_nsd_recurse.c,v 1.2 2011/01/13 02:24:51 pgoyette Exp $"); +__RCSID("$NetBSD: h_nsd_recurse.c,v 1.3 2020/06/01 01:03:21 kamil Exp $"); +#ifndef _REENTRANT #define _REENTRANT +#endif #include #include diff --git a/lib/libc/net/t_hostent.sh b/lib/libc/net/t_hostent.sh old mode 100755 new mode 100644 diff --git a/lib/libc/net/t_if_nametoindex.c b/lib/libc/net/t_if_nametoindex.c new file mode 100644 --- /dev/null +++ b/lib/libc/net/t_if_nametoindex.c @@ -0,0 +1,75 @@ +/* $NetBSD: t_if_nametoindex.c,v 1.1 2018/08/06 04:50:11 msaitoh Exp $ */ + +/*- + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +__RCSID("$NetBSD: t_if_nametoindex.c,v 1.1 2018/08/06 04:50:11 msaitoh Exp $"); + +#include +#include +#include +#include +#include +#include +#include +#include + +ATF_TC(tc_if_nametoindex); +ATF_TC_HEAD(tc_if_nametoindex, tc) +{ + atf_tc_set_md_var(tc, "descr", "Check that if_nametoindex(3) works"); +} + +ATF_TC_BODY(tc_if_nametoindex, tc) +{ + unsigned int r; + + r = if_nametoindex("lo0"); + if (r == 0) + atf_tc_fail("failed on lo0"); + + r = if_nametoindex("foo"); + if (errno != ENXIO) + atf_tc_fail("foo's errno != ENXIO"); +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, tc_if_nametoindex); + + return atf_no_error(); +} diff --git a/lib/libc/net/t_nsdispatch.sh b/lib/libc/net/t_nsdispatch.sh old mode 100755 new mode 100644 diff --git a/lib/libc/net/t_protoent.sh b/lib/libc/net/t_protoent.sh old mode 100755 new mode 100644 diff --git a/lib/libc/net/t_servent.sh b/lib/libc/net/t_servent.sh old mode 100755 new mode 100644 diff --git a/lib/libc/nls/Makefile b/lib/libc/nls/Makefile new file mode 100644 --- /dev/null +++ b/lib/libc/nls/Makefile @@ -0,0 +1,9 @@ +# $NetBSD: Makefile,v 1.1 2020/03/08 22:08:46 mgorny Exp $ + +.include + +TESTSDIR= ${TESTSBASE}/lib/libc/nls + +TESTS_C+= t_catalog + +.include diff --git a/lib/libc/nls/t_catalog.c b/lib/libc/nls/t_catalog.c new file mode 100644 --- /dev/null +++ b/lib/libc/nls/t_catalog.c @@ -0,0 +1,97 @@ +/* $NetBSD: t_catalog.c,v 1.1 2020/03/08 22:08:46 mgorny Exp $ */ + +/*- + * Copyright (c) 2020 The NetBSD Foundation, Inc. + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +__RCSID("$NetBSD: t_catalog.c,v 1.1 2020/03/08 22:08:46 mgorny Exp $"); + +#include +#include +#include /* Needed for sys_nerr on FreeBSD */ +#include +#include +#include +#include +#include + +ATF_TC(catalog_errno); +ATF_TC_HEAD(catalog_errno, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test whether C catalog covers all " + "errno values"); +} + +ATF_TC_BODY(catalog_errno, tc) +{ + int i; + nl_catd catd = catopen("libc", NL_CAT_LOCALE); + ATF_REQUIRE(catd); + + for (i = 1; i < sys_nerr; i++) { + const char *strerr = sys_errlist[i]; + const char *caterr = catgets(catd, 1, i, ""); + ATF_CHECK_MSG(!strcmp(strerr, caterr), + "Catalog message mismatch for errno=%d (sys_errlist: '%s', " + "catalog: '%s')\n", i, strerr, caterr); + } + + catclose(catd); +} + +ATF_TC(catalog_signal); +ATF_TC_HEAD(catalog_signal, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test whether C catalog covers all " + "signal values"); +} + +ATF_TC_BODY(catalog_signal, tc) +{ + int i; + nl_catd catd = catopen("libc", NL_CAT_LOCALE); + ATF_REQUIRE(catd); + + for (i = 1; i < SIGRTMIN-1; i++) { + const char *strerr = sys_siglist[i]; + const char *caterr = catgets(catd, 2, i, ""); + ATF_CHECK_MSG(!strcmp(strerr, caterr), + "Catalog message mismatch for signal=%d (sys_siglist: '%s', " + "catalog: '%s')\n", i, strerr, caterr); + } + + catclose(catd); +} + +ATF_TP_ADD_TCS(tp) +{ + (void)setlocale(LC_ALL, "C"); + + ATF_TP_ADD_TC(tp, catalog_errno); + ATF_TP_ADD_TC(tp, catalog_signal); + + return atf_no_error(); +} + diff --git a/lib/libc/regex/Makefile b/lib/libc/regex/Makefile --- a/lib/libc/regex/Makefile +++ b/lib/libc/regex/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.10 2016/08/26 01:31:43 darcy Exp $ +# $NetBSD: Makefile,v 1.11 2021/02/25 22:37:36 christos Exp $ NOMAN= @@ -10,7 +10,7 @@ BINDIR= ${TESTSDIR} PROGS?= h_regex SRCS.h_regex= main.c split.c debug.c -CPPFLAGS+= -I${NETBSDSRCDIR}/lib/libc/regex ${IMPLEMENTATION} +CPPFLAGS+= -I${NETBSDSRCDIR}/lib/libc/regex ${IMPLEMENTATION} -DNLS TESTS_SH?= t_regex TESTS_C= t_regex_att diff --git a/lib/libc/regex/data/meta.in b/lib/libc/regex/data/meta.in --- a/lib/libc/regex/data/meta.in +++ b/lib/libc/regex/data/meta.in @@ -4,7 +4,8 @@ a\*c & a*c a*c a\\b & a\b a\b a\\\*b & a\*b a\*b -a\bc & abc abc +# legacy escape +#a\bc & abc abc a\ &C EESCAPE a\\bc & a\bc a\bc \{ bC BADRPT diff --git a/lib/libc/regex/data/repet_bounded.in b/lib/libc/regex/data/repet_bounded.in --- a/lib/libc/regex/data/repet_bounded.in +++ b/lib/libc/regex/data/repet_bounded.in @@ -1,9 +1,10 @@ # the dreaded bounded repetitions -{ & { { -{abc & {abc {abc +# unclosed and with missing values are now errors +#{ & { { +#{abc & {abc {abc {1 C BADRPT {1} C BADRPT -a{b & a{b a{b +#a{b & a{b a{b a{1}b - ab ab a\{1\}b b ab ab a{1,}b - ab ab @@ -16,9 +17,9 @@ a\{1a bC EBRACE a{1a} C BADBR a\{1a\} bC BADBR -a{,2} - a{,2} a{,2} +#a{,2} - a{,2} a{,2} a\{,2\} bC BADBR -a{,} - a{,} a{,} +#a{,} - a{,} a{,} a\{,\} bC BADBR a{1,x} C BADBR a\{1,x\} bC BADBR diff --git a/lib/libc/regex/data/repet_multi.in b/lib/libc/regex/data/repet_multi.in --- a/lib/libc/regex/data/repet_multi.in +++ b/lib/libc/regex/data/repet_multi.in @@ -15,7 +15,8 @@ a{1}* C BADRPT a{1}+ C BADRPT a{1}? C BADRPT -a*{b} - a{b} a{b} +# repetition needs to be a number +#a*{b} - a{b} a{b} a\{1\}\{1\} bC BADRPT a*\{1\} bC BADRPT a\{1\}* bC BADRPT diff --git a/lib/libc/regex/debug.c b/lib/libc/regex/debug.c --- a/lib/libc/regex/debug.c +++ b/lib/libc/regex/debug.c @@ -1,4 +1,4 @@ -/* $NetBSD: debug.c,v 1.3 2017/01/14 00:50:56 christos Exp $ */ +/* $NetBSD: debug.c,v 1.7 2021/02/25 22:37:36 christos Exp $ */ /*- * Copyright (c) 1993 The NetBSD Foundation, Inc. @@ -33,17 +33,25 @@ #include #include #include -#include -#include +#ifndef __linux__ /* Don't sort these! */ #include "utils.h" #include "regex2.h" +#else +#define REGEX_NODEBUG +#endif + +#ifdef REGEX_TRE +#define REGEX_NODEBUG +#endif #include "test_regex.h" +#ifndef REGEX_NODEBUG static void s_print(struct re_guts *, FILE *); static char *regchar(int); +#endif /* * regprint - print a regexp for debugging @@ -51,15 +59,10 @@ void regprint(regex_t *r, FILE *d) { +#ifndef REGEX_NODEBUG struct re_guts *g = r->re_g; - int c; - int last; - int nincat[NC]; - fprintf(d, "%ld states, %zu categories", (long)g->nstates, - g->ncategories); - fprintf(d, ", first %ld last %ld", (long)g->firststate, - (long)g->laststate); + fprintf(d, ", first %u last %u", g->firststate, g->laststate); if (g->iflags&USEBOL) fprintf(d, ", USEBOL"); if (g->iflags&USEEOL) @@ -67,53 +70,20 @@ if (g->iflags&BAD) fprintf(d, ", BAD"); if (g->nsub > 0) - fprintf(d, ", nsub=%ld", (long)g->nsub); + fprintf(d, ", nsub=%zu", g->nsub); if (g->must != NULL) - fprintf(d, ", must(%ld) `%*s'", (long)g->mlen, (int)g->mlen, - g->must); + fprintf(d, ", must(%zu) `%*s'", g->mlen, (int)g->mlen, g->must); if (g->backrefs) fprintf(d, ", backrefs"); if (g->nplus > 0) - fprintf(d, ", nplus %ld", (long)g->nplus); + fprintf(d, ", nplus %u", g->nplus); fprintf(d, "\n"); s_print(g, d); - for (size_t i = 0; i < g->ncategories; i++) { - nincat[i] = 0; - for (c = CHAR_MIN; c <= CHAR_MAX; c++) - if (g->categories[c] == i) - nincat[i]++; - } - fprintf(d, "cc0#%d", nincat[0]); - for (size_t i = 1; i < g->ncategories; i++) - if (nincat[i] == 1) { - for (c = CHAR_MIN; c <= CHAR_MAX; c++) - if (g->categories[c] == i) - break; - fprintf(d, ", %zu=%s", i, regchar(c)); - } fprintf(d, "\n"); - for (size_t i = 1; i < g->ncategories; i++) - if (nincat[i] != 1) { - fprintf(d, "cc%zu\t", i); - last = -1; - for (c = CHAR_MIN; c <= CHAR_MAX+1; c++) /* +1 does flush */ - if (c <= CHAR_MAX && g->categories[c] == i) { - if (last < 0) { - fprintf(d, "%s", regchar(c)); - last = c; - } - } else { - if (last >= 0) { - if (last != c-1) - fprintf(d, "-%s", - regchar(c-1)); - last = -1; - } - } - fprintf(d, "\n"); - } +#endif } +#ifndef REGEX_NODEBUG /* * s_print - print the strip for debugging */ @@ -121,11 +91,9 @@ s_print(struct re_guts *g, FILE *d) { sop *s; - cset *cs; int done = 0; sop opnd; int col = 0; - ssize_t last; sopno offset = 2; # define GAP() { if (offset % 5 == 0) { \ if (col > 40) { \ @@ -171,79 +139,63 @@ fprintf(d, "."); break; case OANYOF: - fprintf(d, "[(%ld)", (long)opnd); - cs = &g->sets[opnd]; - last = -1; - for (size_t i = 0; i < g->csetsize+1; i++) /* +1 flushes */ - if (CHIN(cs, i) && i < g->csetsize) { - if (last < 0) { - fprintf(d, "%s", regchar(i)); - last = i; - } - } else { - if (last >= 0) { - if (last != (ssize_t)i - 1) - fprintf(d, "-%s", - regchar(i - 1)); - last = -1; - } - } + fprintf(d, "[(%u)", opnd); fprintf(d, "]"); break; case OBACK_: - fprintf(d, "(\\<%ld>", (long)opnd); + fprintf(d, "(\\<%u>", opnd); break; case O_BACK: - fprintf(d, "<%ld>\\)", (long)opnd); + fprintf(d, "<%u>\\)", opnd); break; case OPLUS_: fprintf(d, "(+"); if (OP(*(s+opnd)) != O_PLUS) - fprintf(d, "<%ld>", (long)opnd); + fprintf(d, "<%u>", opnd); break; case O_PLUS: if (OP(*(s-opnd)) != OPLUS_) - fprintf(d, "<%ld>", (long)opnd); + fprintf(d, "<%u>", opnd); fprintf(d, "+)"); break; case OQUEST_: fprintf(d, "(?"); if (OP(*(s+opnd)) != O_QUEST) - fprintf(d, "<%ld>", (long)opnd); + fprintf(d, "<%u>", opnd); break; case O_QUEST: if (OP(*(s-opnd)) != OQUEST_) - fprintf(d, "<%ld>", (long)opnd); + fprintf(d, "<%u>", opnd); fprintf(d, "?)"); break; case OLPAREN: - fprintf(d, "((<%ld>", (long)opnd); + fprintf(d, "((<%u>", opnd); break; case ORPAREN: - fprintf(d, "<%ld>))", (long)opnd); + fprintf(d, "<%u>))", opnd); break; case OCH_: fprintf(d, "<"); if (OP(*(s+opnd)) != OOR2) - fprintf(d, "<%ld>", (long)opnd); + fprintf(d, "<%u>", opnd); break; case OOR1: if (OP(*(s-opnd)) != OOR1 && OP(*(s-opnd)) != OCH_) - fprintf(d, "<%ld>", (long)opnd); + fprintf(d, "<%u>", opnd); fprintf(d, "|"); break; case OOR2: fprintf(d, "|"); if (OP(*(s+opnd)) != OOR2 && OP(*(s+opnd)) != O_CH) - fprintf(d, "<%ld>", (long)opnd); + fprintf(d, "<%u>", opnd); break; case O_CH: if (OP(*(s-opnd)) != OOR1) - fprintf(d, "<%ld>", (long)opnd); + fprintf(d, "<%u>", opnd); fprintf(d, ">"); break; default: - fprintf(d, "!%d(%d)!", OP(*s), opnd); + fprintf(d, "!%u(%u)!", OP(*s), opnd); break; } if (!done) @@ -265,3 +217,4 @@ sprintf(buf, "\\%o", ch); return(buf); } +#endif diff --git a/lib/libc/regex/main.c b/lib/libc/regex/main.c --- a/lib/libc/regex/main.c +++ b/lib/libc/regex/main.c @@ -1,4 +1,4 @@ -/* $NetBSD: main.c,v 1.2 2011/09/16 16:13:18 plunky Exp $ */ +/* $NetBSD: main.c,v 1.4 2021/02/23 17:13:44 christos Exp $ */ /*- * Copyright (c) 1993 The NetBSD Foundation, Inc. @@ -52,6 +52,16 @@ static char *eprint(int); static int efind(char *); +#ifndef REG_ATOI +#define REG_ATOI 0 +#define REG_ITOA 0 +#define REG_PEND 0 +#define REG_TRACE 0 +#define REG_BACKR 0 +#define REG_NOSPEC 0 +#define REG_LARGE 0 +#endif + /* * main - do the simple case, hand off to regress() for regression */ @@ -72,7 +82,7 @@ progname = argv[0]; - while ((c = getopt(argc, argv, "c:e:S:E:x")) != -1) + while ((c = getopt(argc, argv, "c:E:e:S:x")) != -1) switch (c) { case 'c': /* compile options */ copts = options('c', optarg); @@ -80,12 +90,12 @@ case 'e': /* execute options */ eopts = options('e', optarg); break; - case 'S': /* start offset */ - startoff = (regoff_t)atoi(optarg); - break; case 'E': /* end offset */ endoff = (regoff_t)atoi(optarg); break; + case 'S': /* start offset */ + startoff = (regoff_t)atoi(optarg); + break; case 'x': /* Debugging. */ debug++; break; @@ -211,7 +221,9 @@ erbuf, bpname); status = 1; } +#if REG_ATOI re.re_endp = bpname; +#endif ne = regerror(REG_ATOI, &re, erbuf, sizeof(erbuf)); if (atoi(erbuf) != (int)REG_BADPAT) { fprintf(stderr, "end: regerror() ATOI test gave `%s' not `%ld'\n", @@ -247,7 +259,9 @@ char f2copy[1000]; strcpy(f0copy, f0); +#if REG_ATOI re.re_endp = (opts®_PEND) ? f0copy + strlen(f0copy) : NULL; +#endif fixstr(f0copy); err = regcomp(&re, f0copy, opts); if (err != 0 && (!opt('C', f1) || err != efind(f2))) { @@ -338,7 +352,7 @@ { char *p; int o = (type == 'c') ? copts : eopts; - const char *legal = (type == 'c') ? "bisnmp" : "^$#tl"; + const char *legal = (type == 'c') ? "bisnmpg" : "^$#tl"; for (p = s; *p != '\0'; p++) if (strchr(legal, *p) != NULL) @@ -362,6 +376,9 @@ case 'p': o |= REG_PEND; break; + case 'g': + o |= REG_GNU; + break; case '^': o |= REG_NOTBOL; break; @@ -517,7 +534,9 @@ sprintf(efbuf, "REG_%s", name); assert(strlen(efbuf) < sizeof(efbuf)); +#if REG_ATOI re.re_endp = efbuf; +#endif (void) regerror(REG_ATOI, &re, efbuf, sizeof(efbuf)); return(atoi(efbuf)); } diff --git a/lib/libc/regex/t_exhaust.c b/lib/libc/regex/t_exhaust.c --- a/lib/libc/regex/t_exhaust.c +++ b/lib/libc/regex/t_exhaust.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_exhaust.c,v 1.9 2019/03/16 21:57:15 christos Exp $ */ +/* $NetBSD: t_exhaust.c,v 1.14 2021/06/09 21:09:20 christos Exp $ */ /*- * Copyright (c) 2011 The NetBSD Foundation, Inc. @@ -37,11 +37,20 @@ */ #include -__RCSID("$NetBSD: t_exhaust.c,v 1.9 2019/03/16 21:57:15 christos Exp $"); +__RCSID("$NetBSD: t_exhaust.c,v 1.14 2021/06/09 21:09:20 christos Exp $"); #include -#include #include + +#ifdef TEST +# include +# define ATF_REQUIRE(a) assert(a) +# define ATF_REQUIRE_MSG(a, fmt, ...) \ + if (!(a)) err(EXIT_FAILURE, fmt, __VA_ARGS__) +#else +# include +#endif + #include #include #include @@ -51,6 +60,34 @@ #define REGEX_MAXSIZE 9999 #endif +#ifdef TRACE + +#include +void * +malloc(size_t l) +{ + static void *(*m)(size_t); + static int q; + if (m == NULL) m = dlsym(RTLD_NEXT, "malloc"); + void *p = (*m)(l); + if (q) + return p; + q = 1; + printf("%p m %zu\n", p, l); + q = 0; + return p; +} + +void +free(void *p) +{ + static void (*f)(void *); + if (f == NULL) f = dlsym(RTLD_NEXT, "malloc"); + printf("%p f\n", p); + (*f)(p); +} +#endif + static char * mkstr(const char *str, size_t len) { @@ -69,7 +106,7 @@ size_t slen = strlen(s); char *p = malloc(dlen + slen + 1); - ATF_REQUIRE(p != NULL); + ATF_REQUIRE_MSG(p != NULL, "slen=%zu, dlen=%zu", slen, dlen); strcpy(p, d); strcpy(p + dlen, s); return p; @@ -168,43 +205,56 @@ { p6, REG_BASIC }, }; -ATF_TC(regcomp_too_big); - -ATF_TC_HEAD(regcomp_too_big, tc) -{ - - atf_tc_set_md_var(tc, "descr", "Check that large patterns don't" - " crash, but return a proper error code"); - // libtre needs it. - atf_tc_set_md_var(tc, "timeout", "600"); - atf_tc_set_md_var(tc, "require.memory", "64M"); -} - -ATF_TC_BODY(regcomp_too_big, tc) +static void +run(void) { regex_t re; int e; struct rlimit limit; + char *patterns[__arraycount(tests)]; + + for (size_t i = 0; i < __arraycount(patterns); i++) { + patterns[i] = (*tests[i].pattern)(REGEX_MAXSIZE); + } limit.rlim_cur = limit.rlim_max = 256 * 1024 * 1024; ATF_REQUIRE(setrlimit(RLIMIT_VMEM, &limit) != -1); for (size_t i = 0; i < __arraycount(tests); i++) { - char *d = (*tests[i].pattern)(REGEX_MAXSIZE); - e = regcomp(&re, d, tests[i].type); + e = regcomp(&re, patterns[i], tests[i].type); if (e) { char ebuf[1024]; (void)regerror(e, &re, ebuf, sizeof(ebuf)); ATF_REQUIRE_MSG(e == REG_ESPACE, - "regcomp returned %d (%s) for pattern %zu [%s]", e, ebuf, - i, d); - free(d); + "regcomp returned %d (%s) for pattern %zu [%s]", e, + ebuf, i, patterns[i]); continue; } - free(d); (void)regexec(&re, "aaaaaaaaaaa", 0, NULL, 0); regfree(&re); } + for (size_t i = 0; i < __arraycount(patterns); i++) { + free(patterns[i]); + } +} + +#ifndef TEST + +ATF_TC(regcomp_too_big); + +ATF_TC_HEAD(regcomp_too_big, tc) +{ + + atf_tc_set_md_var(tc, "descr", "Check that large patterns don't" + " crash, but return a proper error code"); + // libtre needs it. + atf_tc_set_md_var(tc, "timeout", "600"); + atf_tc_set_md_var(tc, "require.memory", "256M"); +} + +ATF_TC_BODY(regcomp_too_big, tc) +{ + run(); } ATF_TP_ADD_TCS(tp) @@ -213,3 +263,11 @@ ATF_TP_ADD_TC(tp, regcomp_too_big); return atf_no_error(); } +#else +int +main(void) +{ + run(); + return 0; +} +#endif diff --git a/lib/libc/regex/t_regex.sh b/lib/libc/regex/t_regex.sh old mode 100755 new mode 100644 diff --git a/lib/libc/regex/t_regex_att.c b/lib/libc/regex/t_regex_att.c --- a/lib/libc/regex/t_regex_att.c +++ b/lib/libc/regex/t_regex_att.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_regex_att.c,v 1.3 2017/01/14 20:59:23 christos Exp $ */ +/* $NetBSD: t_regex_att.c,v 1.4 2021/02/23 16:00:37 christos Exp $ */ /*- * Copyright (c) 2011 The NetBSD Foundation, Inc. @@ -37,7 +37,7 @@ */ #include -__RCSID("$NetBSD: t_regex_att.c,v 1.3 2017/01/14 20:59:23 christos Exp $"); +__RCSID("$NetBSD: t_regex_att.c,v 1.4 2021/02/23 16:00:37 christos Exp $"); #include @@ -284,7 +284,12 @@ _DO(EMPTY, COMP) _DO(ASSERT, COMP) _DO(INVARG, COMP) +#ifdef REG_ENOSYS _DO(ENOSYS, COMP) +#endif +#ifdef REG_ILLSEQ + _DO(ILLSEQ, COMP) +#endif #undef _DO }; *comp = 0; diff --git a/lib/libc/rpc/Makefile b/lib/libc/rpc/Makefile --- a/lib/libc/rpc/Makefile +++ b/lib/libc/rpc/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.2 2013/02/26 17:06:55 christos Exp $ +# $NetBSD: Makefile,v 1.3 2019/10/13 07:28:14 mrg Exp $ MKMAN= no @@ -18,6 +18,8 @@ RPC_INCS= ${RPCSRCS:.x=.h} RPC_XDRFILES= ${RPCSRCS:.x=_xdr.c} +COPTS.t_rpc.c+= ${GCC_NO_CAST_FUNCTION_TYPE} + .include .include diff --git a/lib/libc/rpc/t_rpc.c b/lib/libc/rpc/t_rpc.c --- a/lib/libc/rpc/t_rpc.c +++ b/lib/libc/rpc/t_rpc.c @@ -1,7 +1,7 @@ -/* $NetBSD: t_rpc.c,v 1.10 2016/08/27 14:36:22 christos Exp $ */ +/* $NetBSD: t_rpc.c,v 1.11 2019/02/04 04:20:13 mrg Exp $ */ #include -__RCSID("$NetBSD: t_rpc.c,v 1.10 2016/08/27 14:36:22 christos Exp $"); +__RCSID("$NetBSD: t_rpc.c,v 1.11 2019/02/04 04:20:13 mrg Exp $"); #include #include @@ -17,7 +17,10 @@ #ifndef TEST #include -#define ERRX(ev, msg, ...) ATF_REQUIRE_MSG(0, msg, __VA_ARGS__) +#define ERRX(ev, msg, ...) do { \ + ATF_REQUIRE_MSG(0, msg, __VA_ARGS__); \ + return ev; \ +} while(/*CONSTCOND*/0) #define SKIPX(ev, msg, ...) do { \ atf_tc_skip(msg, __VA_ARGS__); \ diff --git a/lib/libc/setjmp/t_setjmp.c b/lib/libc/setjmp/t_setjmp.c --- a/lib/libc/setjmp/t_setjmp.c +++ b/lib/libc/setjmp/t_setjmp.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_setjmp.c,v 1.2 2017/01/14 21:08:17 christos Exp $ */ +/* $NetBSD: t_setjmp.c,v 1.3 2021/03/21 16:36:32 christos Exp $ */ /*- * Copyright (c) 2008 The NetBSD Foundation, Inc. @@ -63,13 +63,14 @@ #include __COPYRIGHT("@(#) Copyright (c) 2008\ The NetBSD Foundation, inc. All rights reserved."); -__RCSID("$NetBSD: t_setjmp.c,v 1.2 2017/01/14 21:08:17 christos Exp $"); +__RCSID("$NetBSD: t_setjmp.c,v 1.3 2021/03/21 16:36:32 christos Exp $"); #include #include #include #include +#include #include #include #include @@ -83,6 +84,8 @@ #define TEST_U_SETJMP 1 #define TEST_SIGSETJMP_SAVE 2 #define TEST_SIGSETJMP_NOSAVE 3 +#define TEST_LONGJMP_ZERO 4 +#define TEST_U_LONGJMP_ZERO 5 static int expectsignal; @@ -101,12 +104,16 @@ sigjmp_buf sjb; sigset_t ss; int i, x; + volatile bool did_longjmp; i = getpid(); + did_longjmp = false; - if (test == TEST_SETJMP || test == TEST_SIGSETJMP_SAVE) + if (test == TEST_SETJMP || test == TEST_SIGSETJMP_SAVE || + test == TEST_LONGJMP_ZERO) expectsignal = 0; - else if (test == TEST_U_SETJMP || test == TEST_SIGSETJMP_NOSAVE) + else if (test == TEST_U_SETJMP || test == TEST_SIGSETJMP_NOSAVE || + test == TEST_U_LONGJMP_ZERO) expectsignal = 1; else atf_tc_fail("unknown test"); @@ -119,26 +126,37 @@ REQUIRE_ERRNO(sigaddset(&ss, SIGABRT) != -1); REQUIRE_ERRNO(sigprocmask(SIG_BLOCK, &ss, NULL) != -1); - if (test == TEST_SETJMP) + if (test == TEST_SETJMP || test == TEST_LONGJMP_ZERO) x = setjmp(jb); - else if (test == TEST_U_SETJMP) + else if (test == TEST_U_SETJMP || test == TEST_U_LONGJMP_ZERO) x = _setjmp(jb); else x = sigsetjmp(sjb, !expectsignal); if (x != 0) { - ATF_REQUIRE_MSG(x == i, "setjmp returned wrong value"); + if (test == TEST_LONGJMP_ZERO || test == TEST_U_LONGJMP_ZERO) + ATF_REQUIRE_MSG(x == 1, "setjmp returned wrong value"); + else + ATF_REQUIRE_MSG(x == i, "setjmp returned wrong value"); + kill(i, SIGABRT); ATF_REQUIRE_MSG(!expectsignal, "kill(SIGABRT) failed"); atf_tc_pass(); + } else if (did_longjmp) { + atf_tc_fail("setjmp returned zero after longjmp"); } REQUIRE_ERRNO(sigprocmask(SIG_UNBLOCK, &ss, NULL) != -1); + did_longjmp = true; if (test == TEST_SETJMP) longjmp(jb, i); + else if (test == TEST_LONGJMP_ZERO) + longjmp(jb, 0); else if (test == TEST_U_SETJMP) _longjmp(jb, i); + else if (test == TEST_U_LONGJMP_ZERO) + _longjmp(jb, 0); else siglongjmp(sjb, i); @@ -185,12 +203,34 @@ h_check(TEST_SIGSETJMP_NOSAVE); } +ATF_TC(longjmp_zero); +ATF_TC_HEAD(longjmp_zero, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks longjmp(3) with a zero value"); +} +ATF_TC_BODY(longjmp_zero, tc) +{ + h_check(TEST_LONGJMP_ZERO); +} + +ATF_TC(_longjmp_zero); +ATF_TC_HEAD(_longjmp_zero, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks _longjmp(3) with a zero value"); +} +ATF_TC_BODY(_longjmp_zero, tc) +{ + h_check(TEST_U_LONGJMP_ZERO); +} + ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, setjmp); ATF_TP_ADD_TC(tp, _setjmp); ATF_TP_ADD_TC(tp, sigsetjmp_save); ATF_TP_ADD_TC(tp, sigsetjmp_nosave); + ATF_TP_ADD_TC(tp, longjmp_zero); + ATF_TP_ADD_TC(tp, _longjmp_zero); return atf_no_error(); } diff --git a/lib/libc/ssp/Makefile b/lib/libc/ssp/Makefile --- a/lib/libc/ssp/Makefile +++ b/lib/libc/ssp/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.7 2014/04/06 19:28:59 christos Exp $ +# $NetBSD: Makefile,v 1.10 2019/10/13 07:28:14 mrg Exp $ NOMAN= # defined @@ -48,4 +48,7 @@ PROGS+= h_vsnprintf PROGS+= h_vsprintf +# Test exercises truncation +COPTS.h_snprintf.c+= ${GCC_NO_FORMAT_TRUNCATION} + .include diff --git a/lib/libc/ssp/t_ssp.sh b/lib/libc/ssp/t_ssp.sh old mode 100755 new mode 100644 diff --git a/lib/libc/stdio/Makefile b/lib/libc/stdio/Makefile --- a/lib/libc/stdio/Makefile +++ b/lib/libc/stdio/Makefile @@ -1,21 +1,26 @@ -# $NetBSD: Makefile,v 1.12 2014/10/15 21:55:34 justin Exp $ +# $NetBSD: Makefile,v 1.16 2021/07/08 12:30:20 christos Exp $ +NOMAN= .include TESTSDIR= ${TESTSBASE}/lib/libc/stdio +BINDIR= ${TESTSDIR} + TESTS_C+= t_clearerr TESTS_C+= t_fflush TESTS_C+= t_fmemopen TESTS_C+= t_fopen TESTS_C+= t_open_memstream TESTS_C+= t_fputc -TESTS_C+= t_mktemp TESTS_C+= t_popen TESTS_C+= t_printf TESTS_C+= t_scanf + +TESTS_SH+= t_intr + COPTS.t_printf.c += -Wno-format-nonliteral -LDADD.t_mktemp+= -Wl,--no-fatal-warnings +PROGS+= h_intr h_makenumbers h_testnumbers .include diff --git a/lib/libc/stdio/h_intr.c b/lib/libc/stdio/h_intr.c new file mode 100644 --- /dev/null +++ b/lib/libc/stdio/h_intr.c @@ -0,0 +1,548 @@ +/* $NetBSD: h_intr.c,v 1.5 2021/07/10 07:50:20 christos Exp $ */ + +/** + * Test of interrupted I/O to popen()ed commands. + * + * Example 1: + * ./h_intr -c "gzip -t" *.gz + * + * Example 2: + * while :; do ./h_intr -b $((12*1024)) -t 10 -c "bzip2 -t" *.bz2; sleep 2; done + * + * Example 3: + * Create checksum file: + * find /mnt -type f -exec sha512 -n {} + >SHA512 + * + * Check program: + * find /mnt -type f -exec ./h_intr -b 512 -c run.sh {} + + * + * ./run.sh: + #!/bin/sh + set -eu + grep -q "^$(sha512 -q)" SHA512 + * + * Author: RVP at sdf.org + */ + +#include +__RCSID("$NetBSD: h_intr.c,v 1.5 2021/07/10 07:50:20 christos Exp $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static bool process(const char *fn); +ssize_t maxread(FILE *fp, void *buf, size_t size); +ssize_t smaxread(FILE *fp, void *buf, size_t size); +ssize_t maxwrite(FILE *fp, const void *buf, size_t size); +ssize_t smaxwrite(FILE *fp, const void *buf, size_t size); +static int rndbuf(void); +static int rndmode(void); +static sig_t xsignal(int signo, sig_t handler); +static void alarmtimer(int wait); +static void pr_star(int signo); +static int do_opts(int argc, char *argv[]); +static void usage(FILE *fp); + +/* Globals */ +static struct options { + const char *cmd; /* cmd to run (which must read from stdin) */ + size_t bsize; /* block size to use */ + size_t asize; /* alt. stdio buffer size */ + int btype; /* buffering type: _IONBF, ... */ + int tmout; /* alarm timeout */ + int flush; /* call fflush() after write if 1 */ + int rndbuf; /* switch buffer randomly if 1 */ + int rndmod; /* switch buffering modes randomly if 1 */ +} opts; + +static const struct { + const char *name; + int value; +} btypes[] = { + { "IONBF", _IONBF }, + { "IOLBF", _IOLBF }, + { "IOFBF", _IOFBF }, +}; + +static void (*alarm_fn)(int); /* real/dummy alarm fn. */ +static int (*sintr_fn)(int, int); /* " siginterrupt fn. */ +static ssize_t (*rd_fn)(FILE *, void *, size_t); /* read fn. */ +static ssize_t (*wr_fn)(FILE *, const void *, size_t); /* write fn. */ + +enum { + MB = 1024 * 1024, /* a megabyte */ + BSIZE = 16 * 1024, /* default RW buffer size */ + DEF_MS = 100, /* interrupt 10x a second */ + MS = 1000, /* msecs. in a second */ +}; + + + + +/** + * M A I N + */ +int +main(int argc, char *argv[]) +{ + int i, rc = EXIT_SUCCESS; + + i = do_opts(argc, argv); + argc -= i; + argv += i; + + if (argc == 0) { + usage(stderr); + return rc; + } + + xsignal(SIGPIPE, SIG_IGN); + for (i = 0; i < argc; i++) { + char *s = strdup(argv[i]); + printf("%s...", basename(s)); + fflush(stdout); + free(s); + + sig_t osig = xsignal(SIGALRM, pr_star); + + if (process(argv[i]) == true) + printf(" OK\n"); + else + rc = EXIT_FAILURE; + + xsignal(SIGALRM, osig); + } + + return rc; +} + +static bool +process(const char *fn) +{ + FILE *ifp, *ofp; + char *buf, *abuf; + int rc = false; + size_t nw = 0; + ssize_t n; + + abuf = NULL; + + if ((buf = malloc(opts.bsize)) == NULL) { + warn("buffer alloc failed"); + return rc; + } + + if ((abuf = malloc(opts.asize)) == NULL) { + warn("alt. buffer alloc failed"); + goto fail; + } + + if ((ifp = fopen(fn, "r")) == NULL) { + warn("fopen failed: %s", fn); + goto fail; + } + + if ((ofp = popen(opts.cmd, "w")) == NULL) { + warn("popen failed `%s'", opts.cmd); + goto fail; + } + + setvbuf(ofp, NULL, opts.btype, opts.asize); + setvbuf(ifp, NULL, opts.btype, opts.asize); + + alarm_fn(opts.tmout); + + while ((n = rd_fn(ifp, buf, opts.bsize)) > 0) { + ssize_t i; + + if (opts.rndbuf || opts.rndmod) { + int r = rndbuf(); + setvbuf(ofp, r ? abuf : NULL, + rndmode(), r ? opts.asize : 0); + } + + sintr_fn(SIGALRM, 0); + + if ((i = wr_fn(ofp, buf, n)) == -1) { + sintr_fn(SIGALRM, 1); + warn("write failed"); + break; + } + + if (opts.flush) + if (fflush(ofp)) + warn("fflush failed"); + + sintr_fn(SIGALRM, 1); + nw += i; + } + + alarm_fn(0); + // printf("%zu\n", nw); + + fclose(ifp); + if (pclose(ofp) != 0) + warn("command failed `%s'", opts.cmd); + else + rc = true; + +fail: + free(abuf); + free(buf); + + return rc; +} + +/** + * maxread - syscall version + */ +ssize_t +smaxread(FILE* fp, void *buf, size_t size) +{ + char *p = buf; + ssize_t nrd = 0; + ssize_t n; + + while (size > 0) { + n = read(fileno(fp), p, size); + if (n < 0) { + if (errno == EINTR) + continue; + else + return -1; + } else if (n == 0) + break; + p += n; + nrd += n; + size -= n; + } + return nrd; +} + +/** + * maxread - stdio version + */ +ssize_t +maxread(FILE* fp, void *buf, size_t size) +{ + char *p = buf; + ssize_t nrd = 0; + size_t n; + + while (size > 0) { + errno = 0; + n = fread(p, 1, size, fp); + if (n == 0) { + printf("ir."); + fflush(stdout); + if (errno == EINTR) + continue; + if (feof(fp) || nrd > 0) + break; + return -1; + } + if (n != size) + clearerr(fp); + p += n; + nrd += n; + size -= n; + } + return nrd; +} + +/** + * maxwrite - syscall version + */ +ssize_t +smaxwrite(FILE* fp, const void *buf, size_t size) +{ + const char *p = buf; + ssize_t nwr = 0; + ssize_t n; + + while (size > 0) { + n = write(fileno(fp), p, size); + if (n <= 0) { + if (errno == EINTR) + n = 0; + else + return -1; + } + p += n; + nwr += n; + size -= n; + } + return nwr; +} + +/** + * maxwrite - stdio version (warning: substrate may be buggy) + */ +ssize_t +maxwrite(FILE* fp, const void *buf, size_t size) +{ + const char *p = buf; + ssize_t nwr = 0; + size_t n; + + while (size > 0) { + errno = 0; + n = fwrite(p, 1, size, fp); + if (n == 0) { + printf("iw."); + fflush(stdout); + if (errno == EINTR) + continue; + if (nwr > 0) + break; + return -1; + } + if (n != size) + clearerr(fp); + p += n; + nwr += n; + size -= n; + } + return nwr; +} + +static int +rndbuf(void) +{ + if (opts.rndbuf == 0) + return 0; + return arc4random_uniform(2); +} + +static int +rndmode(void) +{ + if (opts.rndmod == 0) + return opts.btype; + + switch (arc4random_uniform(3)) { + case 0: return _IONBF; + case 1: return _IOLBF; + case 2: return _IOFBF; + default: errx(EXIT_FAILURE, "programmer error!"); + } +} + +/** + * wrapper around sigaction() because we want POSIX semantics: + * no auto-restarting of interrupted slow syscalls. + */ +static sig_t +xsignal(int signo, sig_t handler) +{ + struct sigaction sa, osa; + + sa.sa_handler = handler; + sa.sa_flags = 0; + sigemptyset(&sa.sa_mask); + if (sigaction(signo, &sa, &osa) < 0) + return SIG_ERR; + return osa.sa_handler; +} + +static void +alarmtimer(int wait) +{ + struct itimerval itv; + + itv.it_value.tv_sec = wait / MS; + itv.it_value.tv_usec = (wait - itv.it_value.tv_sec * MS) * MS; + itv.it_interval = itv.it_value; + setitimer(ITIMER_REAL, &itv, NULL); +} + +static void +dummytimer(int dummy) +{ + (void)dummy; +} + +static int +dummysintr(int dum1, int dum2) +{ + (void)dum1; + (void)dum2; + return 0; /* OK */ +} + +/** + * Print a `*' each time an alarm signal occurs. + */ +static void +pr_star(int signo) +{ + int oe = errno; + (void)signo; + +#if 0 + write(1, "*", 1); +#endif + errno = oe; +} + +/** + * return true if not empty or blank; false otherwise. + */ +static bool +isvalid(const char *s) +{ + return strspn(s, " \t") != strlen(s); +} + +static const char * +btype2str(int val) +{ + for (size_t i = 0; i < __arraycount(btypes); i++) + if (btypes[i].value == val) + return btypes[i].name; + return "*invalid*"; +} + +static int +str2btype(const char *s) +{ + for (size_t i = 0; i < __arraycount(btypes); i++) + if (strcmp(btypes[i].name, s) == 0) + return btypes[i].value; + return EOF; +} + +/** + * Print usage information. + */ +static void +usage(FILE* fp) +{ + fprintf(fp, "Usage: %s [-a SIZE] [-b SIZE] [-fihmnrsw]" + " [-p TYPE] [-t TMOUT] -c CMD FILE...\n", + getprogname()); + fprintf(fp, "%s: Test interrupted writes to popen()ed CMD.\n", + getprogname()); + fprintf(fp, "\n"); + fprintf(fp, "Usual options:\n"); + fprintf(fp, " -a SIZE Alt. stdio buffer size (%zu)\n", opts.asize); + fprintf(fp, " -b SIZE Program buffer size (%zu)\n", opts.bsize); + fprintf(fp, " -c CMD Command to run on each FILE\n"); + fprintf(fp, " -h This message\n"); + fprintf(fp, " -p TYPE Buffering type (%s)\n", btype2str(opts.btype)); + fprintf(fp, " -t TMOUT Interrupt writing to CMD every (%d) ms\n", + opts.tmout); + fprintf(fp, "Debug options:\n"); + fprintf(fp, " -f Do fflush() after writing each block\n"); + fprintf(fp, " -i Use siginterrupt to block interrupts\n"); + fprintf(fp, " -m Use random buffering modes\n"); + fprintf(fp, " -n No interruptions (turns off -i)\n"); + fprintf(fp, " -r Use read() instead of fread()\n"); + fprintf(fp, " -s Switch between own/stdio buffers at random\n"); + fprintf(fp, " -w Use write() instead of fwrite()\n"); +} + +/** + * Process program options. + */ +static int +do_opts(int argc, char *argv[]) +{ + int opt, i; + + /* defaults */ + opts.cmd = ""; + opts.btype = _IONBF; + opts.asize = BSIZE; /* 16K */ + opts.bsize = BSIZE; /* 16K */ + opts.tmout = DEF_MS; /* 100ms */ + opts.flush = 0; /* no fflush() after each write */ + opts.rndbuf = 0; /* no random buffer switching */ + opts.rndmod = 0; /* no random mode " */ + alarm_fn = alarmtimer; + sintr_fn = dummysintr; /* don't protect writes with siginterrupt() */ + rd_fn = maxread; /* read using stdio funcs. */ + wr_fn = maxwrite; /* write " */ + + while ((opt = getopt(argc, argv, "a:b:c:fhimnp:rst:w")) != -1) { + switch (opt) { + case 'a': + i = atoi(optarg); + if (i <= 0 || i > MB) + errx(EXIT_FAILURE, + "alt. buffer size not in range (1 - %d): %d", + MB, i); + opts.asize = i; + break; + case 'b': + i = atoi(optarg); + if (i <= 0 || i > MB) + errx(EXIT_FAILURE, + "buffer size not in range (1 - %d): %d", + MB, i); + opts.bsize = i; + break; + case 'c': + opts.cmd = optarg; + break; + case 'f': + opts.flush = 1; + break; + case 'i': + sintr_fn = siginterrupt; + break; + case 'm': + opts.rndmod = 1; + break; + case 'n': + alarm_fn = dummytimer; + break; + case 'p': + i = str2btype(optarg); + if (i == EOF) + errx(EXIT_FAILURE, + "unknown buffering type: `%s'", optarg); + opts.btype = i; + break; + case 'r': + rd_fn = smaxread; + break; + case 'w': + wr_fn = smaxwrite; + break; + case 's': + opts.rndbuf = 1; + break; + case 't': + i = atoi(optarg); + if ((i < 10 || i > 10000) && i != 0) + errx(EXIT_FAILURE, + "timeout not in range (10ms - 10s): %d", i); + opts.tmout = i; + break; + case 'h': + usage(stdout); + exit(EXIT_SUCCESS); + default: + usage(stderr); + exit(EXIT_FAILURE); + } + } + + if (!isvalid(opts.cmd)) + errx(EXIT_FAILURE, "Please specify a valid command with -c"); + + /* don't call siginterrupt() if not interrupting */ + if (alarm_fn == dummytimer) + sintr_fn = dummysintr; + + return optind; +} diff --git a/lib/libc/stdio/h_makenumbers.c b/lib/libc/stdio/h_makenumbers.c new file mode 100644 --- /dev/null +++ b/lib/libc/stdio/h_makenumbers.c @@ -0,0 +1,19 @@ +#include +#include +#include + +int +main(int argc, char *argv[]) +{ + size_t i = 0; + size_t maxi; + + if (argc != 2) + errx(EXIT_FAILURE, "missing argument"); + + maxi = atoi(argv[1]); + + while (i < maxi) + printf("%zu\n", i++); + return EXIT_SUCCESS; +} diff --git a/lib/libc/stdio/h_testnumbers.c b/lib/libc/stdio/h_testnumbers.c new file mode 100644 --- /dev/null +++ b/lib/libc/stdio/h_testnumbers.c @@ -0,0 +1,16 @@ +#include +#include +#include + +int +main(void) +{ + char line[1024]; + size_t i = 0; + while (fgets(line, sizeof(line), stdin) != NULL) { + if ((size_t)atoi(line) != i) + errx(EXIT_FAILURE, "bad line %s\n", line); + i++; + } + return EXIT_SUCCESS; +} diff --git a/lib/libc/stdio/t_fmemopen.c b/lib/libc/stdio/t_fmemopen.c --- a/lib/libc/stdio/t_fmemopen.c +++ b/lib/libc/stdio/t_fmemopen.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_fmemopen.c,v 1.4 2013/10/19 17:45:00 christos Exp $ */ +/* $NetBSD: t_fmemopen.c,v 1.6 2021/07/10 13:22:01 martin Exp $ */ /*- * Copyright (c)2010 Takehiko NOZAKI, @@ -964,7 +964,9 @@ /* don't accept non nul character at end of buffer */ ATF_CHECK(fputc(0x1, fp) == EOF); - ATF_CHECK(ftello(fp) == (off_t)t->n); + ATF_CHECK_MSG(ftello(fp) == (off_t)t->n, + "%jd != %jd", (intmax_t)ftello(fp), + (intmax_t)t->n); ATF_CHECK(feof(fp) == 0); /* accept nul character at end of buffer */ diff --git a/lib/libc/stdio/t_fopen.c b/lib/libc/stdio/t_fopen.c --- a/lib/libc/stdio/t_fopen.c +++ b/lib/libc/stdio/t_fopen.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_fopen.c,v 1.3 2011/09/14 14:34:37 martin Exp $ */ +/* $NetBSD: t_fopen.c,v 1.8 2020/02/21 22:14:59 kamil Exp $ */ /*- * Copyright (c) 2011 The NetBSD Foundation, Inc. @@ -29,14 +29,18 @@ * POSSIBILITY OF SUCH DAMAGE. */ #include -__RCSID("$NetBSD: t_fopen.c,v 1.3 2011/09/14 14:34:37 martin Exp $"); +__RCSID("$NetBSD: t_fopen.c,v 1.8 2020/02/21 22:14:59 kamil Exp $"); +#include +#include +#include #include #include #include #include #include #include +#include #include #include @@ -58,7 +62,7 @@ * used to fdopen(3) a stream is * closed once the stream is closed. */ - fd = open(path, O_RDWR | O_CREAT); + fd = open(path, O_RDWR | O_CREAT, 0600); ATF_REQUIRE(fd >= 0); @@ -85,7 +89,7 @@ { int fd; - fd = open(path, O_RDONLY | O_CREAT); + fd = open(path, O_RDONLY | O_CREAT, 0600); ATF_REQUIRE(fd >= 0); errno = 0; @@ -126,7 +130,7 @@ * with the stream corresponds with the offset * set earlier for the file descriptor. */ - fd = open(path, O_RDWR | O_CREAT); + fd = open(path, O_RDWR | O_CREAT, 0600); ATF_REQUIRE(fd >= 0); ATF_REQUIRE(write(fd, "garbage", 7) == 7); @@ -253,9 +257,10 @@ static const char *mode[] = { "r", "r+", "w", "w+", "a", "a+", - "rb", "r+b", "wb", "w+b", "ab", "a+b" - "re", "r+e", "we", "w+e", "ae", "a+e" - "rf", "r+f", "wf", "w+f", "af", "a+f" + "rb", "r+b", "wb", "w+b", "ab", "a+b", + "re", "r+e", "we", "w+e", "ae", "a+e", + "rf", "r+f", "wf", "w+f", "af", "a+f", + "rl", "r+l", "wl", "w+l", "al", "a+l" }; f = fopen(path, "w+"); @@ -286,6 +291,116 @@ (void)unlink(path); } +static void +check_kernel_modular(void) +{ + int err; + + err = modctl(MODCTL_EXISTS, 0); + if (err == 0) return; + if (errno == ENOSYS) + atf_tc_skip("Kernel does not have 'options MODULAR'."); + if (errno == EPERM) + return; /* Module loading can be administratively forbidden */ + ATF_REQUIRE_EQ_MSG(errno, 0, "unexpected error %d from " + "modctl(MODCTL_EXISTS, 0)", errno); +} + +static bool +is_module_present(const char *name) +{ + bool found; + size_t len; + int count; + struct iovec iov; + modstat_t *ms; + modstat_t m; + + for (len = 8192; ;) { + iov.iov_base = malloc(len); + iov.iov_len = len; + + errno = 0; + + if (modctl(MODCTL_STAT, &iov) != 0) { + fprintf(stderr, "modctl(MODCTL_STAT) failed: %s\n", + strerror(errno)); + atf_tc_fail("Failed to query module status"); + } + if (len >= iov.iov_len) + break; + free(iov.iov_base); + len = iov.iov_len; + } + + found = false; + count = *(int *)iov.iov_base; + ms = (modstat_t *)((char *)iov.iov_base + sizeof(int)); + while (count > 0) { + memcpy(&m, ms, sizeof(m)); + if (strcmp(m.ms_name, name) == 0) { + found = true; + break; + } + ms++; + count--; + } + + free(iov.iov_base); + + return found; +} + +#define COMPAT10_MODNAME "compat_10" + +ATF_TC(fopen_nullptr); +ATF_TC_HEAD(fopen_nullptr, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test fopen(3) with NULL path (without " + COMPAT10_MODNAME ")"); +} + +ATF_TC_BODY(fopen_nullptr, tc) +{ + bool compat10; + + check_kernel_modular(); + compat10 = is_module_present(COMPAT10_MODNAME); + + if (compat10) + atf_tc_skip("Kernel does have the " COMPAT10_MODNAME + " module loaded into the kernel"); + + /* NULL shall trigger error */ + ATF_REQUIRE_ERRNO(EFAULT, fopen(NULL, "r") == NULL); +} + +ATF_TC(fopen_nullptr_compat10); +ATF_TC_HEAD(fopen_nullptr_compat10, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test fopen(3) with NULL path (with " + COMPAT10_MODNAME ")"); +} + +ATF_TC_BODY(fopen_nullptr_compat10, tc) +{ + FILE *fp; + bool compat10; + + check_kernel_modular(); + compat10 = is_module_present(COMPAT10_MODNAME); + + if (!compat10) + atf_tc_skip("Kernel does not have the " COMPAT10_MODNAME + " module loaded into the kernel"); + + /* NULL is translated to "." and shall success */ + fp = fopen(NULL, "r"); + + ATF_REQUIRE(fp != NULL); + ATF_REQUIRE(fclose(fp) == 0); +} + ATF_TC(fopen_perm); ATF_TC_HEAD(fopen_perm, tc) { @@ -327,13 +442,61 @@ if (f == NULL && errno == EFTYPE) continue; - if (f != NULL) + if (f != NULL) { (void)fclose(f); - atf_tc_fail_nonfatal("opened %s as %s", - devs[i], mode[j]); + atf_tc_fail_nonfatal("opened %s as %s", + devs[i], mode[j]); + } else { + atf_tc_fail_nonfatal( + "err %d (%s) from open of %s as %s", errno, + strerror(errno), devs[i], mode[j]); + } + } + } +} + +static char linkpath[] = "symlink"; + +ATF_TC_WITH_CLEANUP(fopen_symlink); +ATF_TC_HEAD(fopen_symlink, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test fopen(3) with 'l' mode"); +} + +ATF_TC_BODY(fopen_symlink, tc) +{ + static const char *mode[] = { "rl", "r+l", "wl", "w+l", "al", "a+l" }; + size_t j; + FILE *f; + + ATF_CHECK(symlink("/dev/null", linkpath) != -1); + + for (j = 0; j < __arraycount(mode); j++) { + + errno = 0; + f = fopen(linkpath, mode[j]); + + if (f == NULL && errno == EFTYPE) + continue; + + if (f != NULL) { + (void)fclose(f); + + atf_tc_fail_nonfatal("opened %s as %s", linkpath, + mode[j]); + } else { + atf_tc_fail_nonfatal( + "err %d (%s) from open of %s as %s", errno, + strerror(errno), linkpath, mode[j]); } } + ATF_REQUIRE(unlink(linkpath) == 0); +} + +ATF_TC_CLEANUP(fopen_symlink, tc) +{ + (void)unlink(linkpath); } ATF_TC_WITH_CLEANUP(fopen_seek); @@ -433,8 +596,11 @@ ATF_TP_ADD_TC(tp, fopen_append); ATF_TP_ADD_TC(tp, fopen_err); ATF_TP_ADD_TC(tp, fopen_mode); + ATF_TP_ADD_TC(tp, fopen_nullptr); + ATF_TP_ADD_TC(tp, fopen_nullptr_compat10); ATF_TP_ADD_TC(tp, fopen_perm); ATF_TP_ADD_TC(tp, fopen_regular); + ATF_TP_ADD_TC(tp, fopen_symlink); ATF_TP_ADD_TC(tp, fopen_seek); ATF_TP_ADD_TC(tp, freopen_std); diff --git a/lib/libc/stdio/t_intr.sh b/lib/libc/stdio/t_intr.sh new file mode 100644 --- /dev/null +++ b/lib/libc/stdio/t_intr.sh @@ -0,0 +1,81 @@ +# $NetBSD: t_intr.sh,v 1.2 2021/07/09 15:26:59 christos Exp $ +# +# Copyright (c) 2021 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Christos Zoulas. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +DIR=$(atf_get_srcdir) +MAX=10000000 +LMAX=1000000 +BSIZE=128000 +SSIZE=256000 +TMOUT=20 + +h_test() { + "${DIR}/h_makenumbers" "$1" > numbers.in + "${DIR}/h_intr" \ + -p "$2" -a ${SSIZE} -b ${BSIZE} -t ${TMOUT} \ + -c "dd of=numbers.out msgfmt=quiet" numbers.in + "${DIR}/h_testnumbers" < numbers.out +} + +atf_test_case stdio_intr_ionbf +stdio_intr_ionbf_head() +{ + atf_set "descr" "Checks stdio EINTR _IONBF" +} +stdio_intr_ionbf_body() +{ + h_test ${MAX} IONBF +} + +atf_test_case stdio_intr_iolbf +stdio_intr_iolbf_head() +{ + atf_set "descr" "Checks stdio EINTR _IOLBF" +} +stdio_intr_iolbf_body() +{ + h_test ${LMAX} IOLBF +} + +atf_test_case stdio_intr_iofbf +stdio_intr_iofbf_head() +{ + atf_set "descr" "Checks stdio EINTR _IOFBF" +} +stdio_intr_iofbf_body() +{ + h_test ${MAX} IOFBF +} + +atf_init_test_cases() +{ + atf_add_test_case stdio_intr_ionbf + atf_add_test_case stdio_intr_iolbf + atf_add_test_case stdio_intr_iofbf +} diff --git a/lib/libc/stdio/t_mktemp.c b/lib/libc/stdio/t_mktemp.c deleted file mode 100644 --- a/lib/libc/stdio/t_mktemp.c +++ /dev/null @@ -1,54 +0,0 @@ -/* $NetBSD: t_mktemp.c,v 1.1 2013/04/22 21:05:12 christos Exp $ */ - -/*- - * Copyright (c) 2013 The NetBSD Foundation, Inc. - * All rights reserved. - * - * This code is derived from software contributed to The NetBSD Foundation - * by Christos Zoulas. - * - * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 -__RCSID("$NetBSD: t_mktemp.c,v 1.1 2013/04/22 21:05:12 christos Exp $"); - -#include -#include - -ATF_TC(mktemp_not_exist); -ATF_TC_HEAD(mktemp_not_exist, tc) -{ - atf_tc_set_md_var(tc, "descr", "Test that mktemp does not fail on" - " a path that does not exist"); -} - -ATF_TC_BODY(mktemp_not_exist, tc) -{ - char template[] = "I will barf/XXXXXX"; - ATF_REQUIRE(mktemp(template) != NULL); -} - -ATF_TP_ADD_TCS(tp) -{ - ATF_TP_ADD_TC(tp, mktemp_not_exist); - return atf_no_error(); -} diff --git a/lib/libc/stdlib/Makefile b/lib/libc/stdlib/Makefile --- a/lib/libc/stdlib/Makefile +++ b/lib/libc/stdlib/Makefile @@ -1,9 +1,10 @@ -# $NetBSD: Makefile,v 1.28 2015/12/22 14:27:14 christos Exp $ +# $NetBSD: Makefile,v 1.33 2020/07/01 07:16:37 jruoho Exp $ .include TESTSDIR= ${TESTSBASE}/lib/libc/stdlib +TESTS_C+= t_a64l TESTS_C+= t_abs TESTS_C+= t_atoi TESTS_C+= t_div @@ -11,6 +12,7 @@ TESTS_C+= t_getenv_thread TESTS_C+= t_exit TESTS_C+= t_hsearch +TESTS_C+= t_mktemp TESTS_C+= t_mi_vector_hash TESTS_C+= t_posix_memalign TESTS_C+= t_random @@ -33,6 +35,7 @@ LDADD.t_strtod= -lm DPADD.t_strtod+= ${LIBM} -LDADD.t_getenv_thread= -lpthread +LDADD.t_getenv_thread= -lpthread +LDADD.t_mktemp+= -Wl,--no-fatal-warnings .include diff --git a/lib/libc/stdlib/t_a64l.c b/lib/libc/stdlib/t_a64l.c new file mode 100644 --- /dev/null +++ b/lib/libc/stdlib/t_a64l.c @@ -0,0 +1,111 @@ +/* $NetBSD: t_a64l.c,v 1.1 2020/07/01 07:16:37 jruoho Exp $ */ + +/*- + * Copyright (c) 2020 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jukka Ruohonen. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +__RCSID("$NetBSD: t_a64l.c,v 1.1 2020/07/01 07:16:37 jruoho Exp $"); + +#include +#include +#include + +struct test { + const char *str; + size_t len; + long val; +} t[] = { + { "", 0, 0L }, + { "///", 3, 4161L }, /* 1*64^0 + 1*64^1 + 1*64^2 */ + { "123", 3, 20739L }, /* 3*64^0 + 4*64^1 + 5*64^2 */ + { "zzz", 3, 262143L }, /* 63*64^0 + 63*64^1 + 63*64^2 */ + { "a64l", 4, 12870182L }, /* 38*64^0 + 8*64^1 + 6*64^2 + 49*64^3 */ + { "a64l...", 4, 12870182L }, /* 38*64^0 + 8*64^1 + 6*64^2 + 49*64^3 */ +}; + +#define MAXLEN 8 + +ATF_TC(a64l_basic); +ATF_TC_HEAD(a64l_basic, tc) +{ + atf_tc_set_md_var(tc, "descr", "A basic test of a64l(3)"); +} + +ATF_TC_BODY(a64l_basic, tc) +{ + for (size_t i = 0; i < __arraycount(t); i++) { + long val = a64l(t[i].str); + ATF_REQUIRE(val == t[i].val); + } + +} + +ATF_TC(l64a_basic); +ATF_TC_HEAD(l64a_basic, tc) +{ + atf_tc_set_md_var(tc, "descr", "A basic test of l64a(3)"); +} + +ATF_TC_BODY(l64a_basic, tc) +{ + for (size_t i = 0; i < __arraycount(t); i++) { + char *str = l64a(t[i].val); + ATF_REQUIRE(strlen(str) == t[i].len); + ATF_REQUIRE(strncmp(str, t[i].str, t[i].len) == 0); + } +} + +ATF_TC(l64a_r_basic); +ATF_TC_HEAD(l64a_r_basic, tc) +{ + atf_tc_set_md_var(tc, "descr", "A basic test of l64a_r(3)"); +} + +ATF_TC_BODY(l64a_r_basic, tc) +{ + char buf[MAXLEN]; + + for (size_t i = 0; i < __arraycount(t); i++) { + + (void)memset(buf, '\0', sizeof(buf)); + + ATF_REQUIRE(l64a_r(t[i].val, buf, t[i].len + 1) == 0); + ATF_REQUIRE(strlen(buf) == t[i].len); + ATF_REQUIRE(strncmp(buf, t[i].str, t[i].len) == 0); + } +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, a64l_basic); + ATF_TP_ADD_TC(tp, l64a_basic); + ATF_TP_ADD_TC(tp, l64a_r_basic); + + return atf_no_error(); +} diff --git a/lib/libc/stdlib/t_atexit.sh b/lib/libc/stdlib/t_atexit.sh old mode 100755 new mode 100644 --- a/lib/libc/stdlib/t_atexit.sh +++ b/lib/libc/stdlib/t_atexit.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_atexit.sh,v 1.1 2011/01/12 19:44:08 pgoyette Exp $ +# $NetBSD: t_atexit.sh,v 1.2 2017/07/10 21:43:33 joerg Exp $ # # Copyright (c) 2011 The NetBSD Foundation, Inc. # All rights reserved. @@ -33,7 +33,7 @@ atexit_body() { $(atf_get_srcdir)/h_atexit >out \ - || atf_fail "h_exit failed, see output of the test for details" + || atf_fail "h_atexit failed, see output of the test for details" cat >exp < -__RCSID("$NetBSD: t_atoi.c,v 1.2 2012/03/29 05:56:36 jruoho Exp $"); +__RCSID("$NetBSD: t_atoi.c,v 1.3 2019/02/03 10:48:46 mrg Exp $"); #include #include @@ -46,7 +46,7 @@ ATF_TC_BODY(atof_strtod, tc) { - char buf[128]; + char buf[320]; (void)snprintf(buf, sizeof(buf), "%f\n", DBL_MAX); diff --git a/lib/libc/stdlib/t_getopt.sh b/lib/libc/stdlib/t_getopt.sh old mode 100755 new mode 100644 diff --git a/lib/libc/stdlib/t_mktemp.c b/lib/libc/stdlib/t_mktemp.c new file mode 100644 --- /dev/null +++ b/lib/libc/stdlib/t_mktemp.c @@ -0,0 +1,270 @@ +/* $NetBSD: t_mktemp.c,v 1.4 2021/01/11 20:31:34 christos Exp $ */ + +/*- + * Copyright (c) 2013, 2020 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas and Jukka Ruohonen. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +__RCSID("$NetBSD: t_mktemp.c,v 1.4 2021/01/11 20:31:34 christos Exp $"); + +#include + +#include +#include +#include +#include +#include +#include + +static int +check_mode(struct stat sa, const int mode, const int dir) +{ + + if (dir == 0 && S_ISREG(sa.st_mode) == 0) + return -1; + + if (dir != 0 && S_ISDIR(sa.st_mode) == 0) + return -1; + + if ((sa.st_mode & mode) == 0) + return -1; + + return 0; +} + +ATF_TC(mktemp_not_exist); +ATF_TC_HEAD(mktemp_not_exist, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test that mktemp(3)" + " does not fail on a path that does not exist"); +} + +ATF_TC_BODY(mktemp_not_exist, tc) +{ + char template[] = "I will barf/XXXXXX"; + ATF_REQUIRE(mktemp(template) != NULL); +} + +ATF_TC(mktemp_large_template); +ATF_TC_HEAD(mktemp_large_template, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test that mktemp " + "accepts arbitrarily large templates (PR lib/55441)"); +} + +ATF_TC_BODY(mktemp_large_template, tc) +{ + const char *path = "/tmp/mktemp.XXXXXX"; + char *template; + size_t i; + long name_max; + size_t tlen; + + name_max = pathconf("/tmp/", _PC_NAME_MAX); + ATF_REQUIRE(name_max > 0); + + tlen = (size_t)name_max + sizeof("/tmp/"); + template = malloc(tlen); + ATF_REQUIRE(template != NULL); + + ATF_REQUIRE(strcpy(template, path) != NULL); + ATF_REQUIRE(mktemp(template) != NULL); + ATF_REQUIRE(strlen(template) == strlen(path)); + ATF_REQUIRE(strcmp(template, path) != 0); + ATF_REQUIRE(strncmp(template, "/tmp/mktemp.", 12) == 0); + + (void)memset(template, '\0', tlen); + (void)strcpy(template, "/tmp/mktemp."); + + for (i = 12; i < tlen - 1; i++) + template[i] = 'X'; + + ATF_REQUIRE(mktemp(template) != NULL); + ATF_REQUIRE(strlen(template) == tlen - 1); + ATF_REQUIRE(strcmp(template, path) != 0); + ATF_REQUIRE(strncmp(template, "/tmp/mktemp.", 12) == 0); + free(template); +} + +ATF_TC(mkstemp_basic); +ATF_TC_HEAD(mkstemp_basic, tc) +{ + atf_tc_set_md_var(tc, "descr", "A basic test of mkstemp(3)"); +} + +ATF_TC_BODY(mkstemp_basic, tc) +{ + char template[] = "/tmp/mktemp.XXXXXX"; + struct stat sa; + int fd; + + (void)memset(&sa, 0, sizeof(struct stat)); + + fd = mkstemp(template); + + ATF_REQUIRE(fd != -1); + ATF_REQUIRE(strncmp(template, "/tmp/mktemp.", 12) == 0); + ATF_REQUIRE(write(fd, "X", 1) == 1); + ATF_REQUIRE(fstat(fd, &sa) == 0); + ATF_REQUIRE(check_mode(sa, 0600, 0) == 0); + ATF_REQUIRE(close(fd) == 0); + ATF_REQUIRE(unlink(template) == 0); +} + +ATF_TC(mkstemps_basic); +ATF_TC_HEAD(mkstemps_basic, tc) +{ + atf_tc_set_md_var(tc, "descr", "A basic test of mkstemps(3)"); +} + +ATF_TC_BODY(mkstemps_basic, tc) +{ + char template[] = "/tmp/mktemp.XXXyyy"; + char *suffix = strchr(template, 'y'); + struct stat sa; + int fd; + + (void)memset(&sa, 0, sizeof(struct stat)); + + fd = mkstemps(template, 3); + + ATF_REQUIRE(fd != -1); + ATF_REQUIRE(strncmp(template, "/tmp/mktemp.", 12) == 0); + ATF_REQUIRE(strcmp(suffix, "yyy") == 0); + ATF_REQUIRE(write(fd, "X", 1) == 1); + ATF_REQUIRE(fstat(fd, &sa) == 0); + ATF_REQUIRE(check_mode(sa, 0600, 0) == 0); + ATF_REQUIRE(close(fd) == 0); + ATF_REQUIRE(unlink(template) == 0); +} + +ATF_TC(mkdtemp_basic); +ATF_TC_HEAD(mkdtemp_basic, tc) +{ + atf_tc_set_md_var(tc, "descr", "A basic test of mkdtemp(3)"); +} + +ATF_TC_BODY(mkdtemp_basic, tc) +{ + char template[] = "/tmp/mktemp.XXXXXX"; + char *path = mkdtemp(template); + struct stat sa; + + (void)memset(&sa, 0, sizeof(struct stat)); + + ATF_REQUIRE(path != NULL); + ATF_REQUIRE(strncmp(template, "/tmp/mktemp.", 12) == 0); + ATF_REQUIRE(stat(path, &sa) == 0); + ATF_REQUIRE(check_mode(sa, 0700, 1) == 0); + ATF_REQUIRE(rmdir(path) == 0); +} + +ATF_TC(mkostemp_basic); +ATF_TC_HEAD(mkostemp_basic, tc) +{ + atf_tc_set_md_var(tc, "descr", "A basic test of mkostemp(3)"); +} + +ATF_TC_BODY(mkostemp_basic, tc) +{ + static const int flags[] = { + O_APPEND, O_DIRECT, + O_SHLOCK, O_EXLOCK, + O_SYNC, O_CLOEXEC + }; + + char template[] = "/tmp/mktemp.XXXXXX"; + struct stat sa; + size_t i; + int fd; + + for (i = 0; i < __arraycount(flags); i++) { + + (void)memset(&sa, 0, sizeof(struct stat)); + + fd = mkostemp(template, flags[i]); + + ATF_REQUIRE(fd != -1); + ATF_REQUIRE(strncmp(template, "/tmp/mktemp.", 12) == 0); + ATF_REQUIRE(write(fd, "X", 1) == 1); + ATF_REQUIRE(fstat(fd, &sa) == 0); + ATF_REQUIRE(check_mode(sa, 0600 | flags[i], 0) == 0); + ATF_REQUIRE(close(fd) == 0); + ATF_REQUIRE(unlink(template) == 0); + } +} + +ATF_TC(mkostemps_basic); +ATF_TC_HEAD(mkostemps_basic, tc) +{ + atf_tc_set_md_var(tc, "descr", "A basic test of mkostemps(3)"); +} + +ATF_TC_BODY(mkostemps_basic, tc) +{ + static const int flags[] = { + O_APPEND, O_DIRECT, + O_SHLOCK, O_EXLOCK, + O_SYNC, O_CLOEXEC + }; + + char template[] = "/tmp/mktemp.XXXyyy"; + char *suffix = strchr(template, 'y'); + struct stat sa; + size_t i; + int fd; + + for (i = 0; i < __arraycount(flags); i++) { + + (void)memset(&sa, 0, sizeof(struct stat)); + + fd = mkostemps(template, 3, flags[i]); + + ATF_REQUIRE(fd != -1); + ATF_REQUIRE(strncmp(template, "/tmp/mktemp.", 12) == 0); + ATF_REQUIRE(strcmp(suffix, "yyy") == 0); + ATF_REQUIRE(write(fd, "X", 1) == 1); + ATF_REQUIRE(fstat(fd, &sa) == 0); + ATF_REQUIRE(check_mode(sa, 0600 | flags[i], 0) == 0); + ATF_REQUIRE(close(fd) == 0); + ATF_REQUIRE(unlink(template) == 0); + } +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, mktemp_not_exist); + ATF_TP_ADD_TC(tp, mktemp_large_template); + ATF_TP_ADD_TC(tp, mkstemp_basic); + ATF_TP_ADD_TC(tp, mkstemps_basic); + ATF_TP_ADD_TC(tp, mkdtemp_basic); + ATF_TP_ADD_TC(tp, mkostemp_basic); + ATF_TP_ADD_TC(tp, mkostemps_basic); + + return atf_no_error(); +} diff --git a/lib/libc/stdlib/t_posix_memalign.c b/lib/libc/stdlib/t_posix_memalign.c --- a/lib/libc/stdlib/t_posix_memalign.c +++ b/lib/libc/stdlib/t_posix_memalign.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_posix_memalign.c,v 1.4 2015/11/07 17:35:31 nros Exp $ */ +/* $NetBSD: t_posix_memalign.c,v 1.5 2018/07/29 01:45:25 maya Exp $ */ /*- * Copyright (c) 2008 The NetBSD Foundation, Inc. @@ -32,7 +32,7 @@ #include __COPYRIGHT("@(#) Copyright (c) 2008\ The NetBSD Foundation, inc. All rights reserved."); -__RCSID("$NetBSD: t_posix_memalign.c,v 1.4 2015/11/07 17:35:31 nros Exp $"); +__RCSID("$NetBSD: t_posix_memalign.c,v 1.5 2018/07/29 01:45:25 maya Exp $"); #include @@ -121,9 +121,6 @@ ATF_REQUIRE_EQ_MSG((align[i] - 1) & align[i], 0, "aligned_alloc: success when alignment was not " "a power of 2"); - ATF_REQUIRE_EQ_MSG(size[i] % align[i], 0, - "aligned_alloc: success when size was not an " - "integer multiple of alignment"); ATF_REQUIRE_EQ_MSG(((intptr_t)p) & (align[i] - 1), 0, "p = %p", p); free(p); diff --git a/lib/libc/stdlib/t_strtoi.c b/lib/libc/stdlib/t_strtoi.c --- a/lib/libc/stdlib/t_strtoi.c +++ b/lib/libc/stdlib/t_strtoi.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_strtoi.c,v 1.1 2015/05/01 14:17:56 christos Exp $ */ +/* $NetBSD: t_strtoi.c,v 1.2 2017/04/28 19:01:01 kamil Exp $ */ /*- * Copyright (c) 2015 The NetBSD Foundation, Inc. @@ -30,12 +30,12 @@ */ /* - * Created by Kamil Rytarowski, vesed on ID: + * Created by Kamil Rytarowski, based on ID: * NetBSD: t_strtol.c,v 1.5 2011/06/14 02:45:58 jruoho Exp */ #include -__RCSID("$NetBSD: t_strtoi.c,v 1.1 2015/05/01 14:17:56 christos Exp $"); +__RCSID("$NetBSD: t_strtoi.c,v 1.2 2017/04/28 19:01:01 kamil Exp $"); #include #include diff --git a/lib/libc/stdlib/t_strtol.c b/lib/libc/stdlib/t_strtol.c --- a/lib/libc/stdlib/t_strtol.c +++ b/lib/libc/stdlib/t_strtol.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_strtol.c,v 1.6 2016/06/01 01:12:02 pgoyette Exp $ */ +/* $NetBSD: t_strtol.c,v 1.7 2017/07/06 21:08:44 joerg Exp $ */ /*- * Copyright (c) 2011 The NetBSD Foundation, Inc. @@ -30,7 +30,7 @@ */ #include -__RCSID("$NetBSD: t_strtol.c,v 1.6 2016/06/01 01:12:02 pgoyette Exp $"); +__RCSID("$NetBSD: t_strtol.c,v 1.7 2017/07/06 21:08:44 joerg Exp $"); #include #include @@ -61,8 +61,8 @@ if ((t->end != NULL && strcmp(t->end, end) != 0) || (t->end == NULL && *end != '\0')) - atf_tc_fail_nonfatal("invalid end pointer ('%s') from " - "strtol(%s, &end, %d)", end, t->str, t->base); + atf_tc_fail_nonfatal("invalid end pointer (%p) from " + "strtol(%p, &end, %d)", end, t->str, t->base); } ATF_TC(strtol_base); @@ -93,12 +93,18 @@ { "1234567", 342391, 8, NULL }, { "01234567", 342391, 0, NULL }, { "0123456789", 123456789, 10, NULL }, - { "0x75bcd15", 123456789, 0, NULL }, + { "0x75bcd15", 123456789, 0, NULL }, + { " 0xX", 0, 0, "xX" }, + { " 0xX", 0, 16, "xX" }, + { " 0XX", 0, 0, "XX" }, + { " 0XX", 0, 16, "XX" }, }; long long int lli; long int li; - char *end; + long long int ulli; + long int uli; + char *end, *end2; size_t i; for (i = 0; i < __arraycount(t); i++) { @@ -106,7 +112,20 @@ li = strtol(t[i].str, &end, t[i].base); lli = strtoll(t[i].str, NULL, t[i].base); + uli = strtoul(t[i].str, &end2, t[i].base); + ulli = strtoull(t[i].str, NULL, t[i].base); + check(&t[i], li, lli, end); + + if (li != uli) + atf_tc_fail_nonfatal("strtoul(%s, NULL, %d) failed " + "(rv = %lu)", t[i].str, t[i].base, uli); + if (end != end2) + atf_tc_fail_nonfatal("invalid end pointer ('%p') from " + "strtoul(%s, &end, %d)", end2, t[i].str, t[i].base); + if (lli != ulli) + atf_tc_fail_nonfatal("strtoull(%s, NULL, %d) failed " + "(rv = %llu)", t[i].str, t[i].base, ulli); } } diff --git a/lib/libc/string/Makefile b/lib/libc/string/Makefile --- a/lib/libc/string/Makefile +++ b/lib/libc/string/Makefile @@ -1,8 +1,11 @@ -# $NetBSD: Makefile,v 1.9 2014/06/23 10:53:20 shm Exp $ +# $NetBSD: Makefile,v 1.12 2019/12/19 19:19:28 macallan Exp $ .include TESTSDIR= ${TESTSBASE}/lib/libc/string +DBG=-g + +COPTS.t_strcat.c+= ${GCC_NO_STRINGOP_TRUNCATION} TESTS_C+= t_bm TESTS_C+= t_memchr @@ -13,6 +16,7 @@ TESTS_C+= t_strcat TESTS_C+= t_strchr TESTS_C+= t_strcmp +TESTS_C+= t_strcoll TESTS_C+= t_strcpy TESTS_C+= t_strcspn TESTS_C+= t_strerror diff --git a/lib/libc/string/t_memmem.c b/lib/libc/string/t_memmem.c --- a/lib/libc/string/t_memmem.c +++ b/lib/libc/string/t_memmem.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_memmem.c,v 1.3 2017/01/11 18:07:37 christos Exp $ */ +/* $NetBSD: t_memmem.c,v 1.6 2020/11/27 16:50:02 christos Exp $ */ /*- * Copyright (c) 2005 The NetBSD Foundation, Inc. @@ -34,6 +34,7 @@ #include #include #include +#include char p0[] = ""; int lp0 = 0; @@ -94,10 +95,41 @@ expect(memmem(b2, lb2, p8, lp8) == NULL); } +ATF_TC(memmem_oob); +ATF_TC_HEAD(memmem_oob, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test memmem out of bounds read"); +} + +ATF_TC_BODY(memmem_oob, tc) +{ + static const char str[] = "abcde"; + size_t pg = getpagesize(); + char *src = mmap(NULL, 2 * pg, PROT_READ|PROT_WRITE, + MAP_ANON|MAP_PRIVATE, -1, (off_t)0); + ATF_CHECK(src != MAP_FAILED); + char *guard = mmap(src + pg, pg, + PROT_NONE, MAP_ANON|MAP_PRIVATE|MAP_FIXED, -1, (off_t)0); +printf("%p\n", guard); + for (size_t i = 2; i < 5; i++) { + char *search = src + pg - i; + char match[sizeof(str)]; + search[-1] = str[0]; + search[0] = str[0]; + search[1] = str[0]; + memcpy(match, str, i); + ATF_CHECK(memmem(search, i, match, i) != search); + } + munmap(guard, pg); + munmap(src, pg); +} + + ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, memmem_basic); + ATF_TP_ADD_TC(tp, memmem_oob); return atf_no_error(); } diff --git a/lib/libc/string/t_strcoll.c b/lib/libc/string/t_strcoll.c new file mode 100644 --- /dev/null +++ b/lib/libc/string/t_strcoll.c @@ -0,0 +1,106 @@ +/* $NetBSD: t_strcoll.c,v 1.2 2021/08/02 17:41:07 andvar Exp $ */ + +/*- + * Copyright (c) 2017 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Konrad Schroder + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +__COPYRIGHT("@(#) Copyright (c) 2017\ + The NetBSD Foundation, inc. All rights reserved."); +__RCSID("$NetBSD: t_strcoll.c,v 1.2 2021/08/02 17:41:07 andvar Exp $"); + +#include +#include +#include +#include + +#include + +static struct test { + const char *locale; + const char * const data[5]; +} tests[] = { + { + "C", + { "aardvark", "absolution", "zyzygy", NULL }, + }, { + "ru_RU.KOI8-R", + { "\xc5\xc4\xcf\xcb", "\xa3\xd6", "\xc5\xda\xc4\xc9\xd4\xd8", NULL }, + }, { + NULL, + { NULL, NULL, NULL, NULL }, + } +}; + +static void +h_ordering(const struct test *t) +{ + const char * const *a; + const char * const *b; + char buf_a[1024], buf_b[1024]; + + ATF_REQUIRE_STREQ(setlocale(LC_ALL, "C"), "C"); + printf("Trying locale %s...\n", t->locale); + ATF_REQUIRE(setlocale(LC_COLLATE, t->locale) != NULL); + + for (a = t->data; *a != NULL; ++a) { + strvis(buf_a, *a, VIS_WHITE | VIS_OCTAL); + for (b = a + 1; *b != NULL; ++b) { + strvis(buf_b, *b, VIS_WHITE | VIS_OCTAL); + printf("Checking \"%s\" < \"%s\"\n", buf_a, buf_b); + ATF_REQUIRE(strcoll(*a, *b) < 0); + printf("...good\n"); + } + } +} + +ATF_TC(ordering); + +ATF_TC_HEAD(ordering, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Checks collation ordering under different locales"); +} + +ATF_TC_BODY(ordering, tc) +{ + struct test *t; + + atf_tc_expect_fail("%s", "LC_COLLATE not supported"); + for (t = &tests[0]; t->locale != NULL; ++t) + h_ordering(t); + atf_tc_expect_pass(); +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, ordering); + + return atf_no_error(); +} diff --git a/lib/libc/string/t_stresep.c b/lib/libc/string/t_stresep.c --- a/lib/libc/string/t_stresep.c +++ b/lib/libc/string/t_stresep.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_stresep.c,v 1.3 2013/02/15 23:56:32 christos Exp $ */ +/* $NetBSD: t_stresep.c,v 1.4 2017/08/23 10:29:51 christos Exp $ */ /*- * Copyright (c) 2005 The NetBSD Foundation, Inc. @@ -61,6 +61,12 @@ expect("bar foo"); expect(" baz"); expect("bar "); + + char brkstr2[] = "aa bb cc\\ \\ \\ \\ dd-"; + q = brkstr2; + expect("aa"); + expect("bb"); + expect("cc dd-"); } ATF_TP_ADD_TCS(tp) diff --git a/lib/libc/sync/Makefile b/lib/libc/sync/Makefile --- a/lib/libc/sync/Makefile +++ b/lib/libc/sync/Makefile @@ -1,18 +1,13 @@ -# $NetBSD: Makefile,v 1.6 2014/10/18 02:22:35 joerg Exp $ +# $NetBSD: Makefile,v 1.8 2019/02/26 10:01:41 isaki Exp $ WARNS=0 NOMAN=1 .include -PROG= all_sync_ops_linkable - -.if "${ACTIVE_CC}" == "clang" || \ - ("${ACTIVE_CC}" == "gcc" && "${HAVE_GCC}" == "48") - +.if "${ACTIVE_CC}" == "clang" CXXFLAGS+= -std=c++11 PROG_CXX+= cpp_atomic_ops_linkable - .endif proginstall: diff --git a/lib/libc/sync/all_sync_ops_linkable.c b/lib/libc/sync/all_sync_ops_linkable.c deleted file mode 100644 --- a/lib/libc/sync/all_sync_ops_linkable.c +++ /dev/null @@ -1,168 +0,0 @@ -/* $NetBSD: all_sync_ops_linkable.c,v 1.4 2014/02/21 10:26:25 martin Exp $ */ - -/*- - * Copyright (c) 2014 The NetBSD Foundation, Inc. - * All rights reserved. - * - * This code is derived from software contributed to The NetBSD Foundation - * by Martin Husemann . - * - * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. - */ - -/* - * This is a simple link-time test to verify all builtin atomic sync - * operations are available. Depending on the exact cpu/arch code generator - * options, some of these need support functions (which on NetBSD we - * typically provide in src/common/lib/libc/atomic). - * - * The list of operations has been extracted from sync-builtins.def file - * in the gcc distribution (as of gcc 4.8.2). - */ - -#include -#include - -volatile uint8_t u8 = 0; -volatile uint16_t u16 = 0; -volatile uint32_t u32 = 0; - -#ifdef __HAVE_ATOMIC64_OPS -volatile uint64_t u64 = 0; -#endif - -int -main(int argc, char **argv) -{ - __sync_synchronize(); - __sync_add_and_fetch(&u8, 1); - __sync_add_and_fetch_1(&u8, 1); - __sync_add_and_fetch_2(&u16, 1); - __sync_add_and_fetch_4(&u32, 1); -#ifdef __HAVE_ATOMIC64_OPS - __sync_add_and_fetch_8(&u64, 1); -#endif - __sync_bool_compare_and_swap(&u8, 1, 2); - __sync_bool_compare_and_swap_1(&u8, 1, 2); - __sync_bool_compare_and_swap_2(&u16, 1, 2); - __sync_bool_compare_and_swap_4(&u32, 1, 2); -#ifdef __HAVE_ATOMIC64_OPS - __sync_bool_compare_and_swap_8(&u64, 1, 2); -#endif - __sync_fetch_and_add(&u8, 1); - __sync_fetch_and_add_1(&u8, 1); - __sync_fetch_and_add_2(&u16, 1); - __sync_fetch_and_add_4(&u32, 1); -#ifdef __HAVE_ATOMIC64_OPS - __sync_fetch_and_add_8(&u64, 1); -#endif - __sync_fetch_and_and(&u8, 0x80); - __sync_fetch_and_and_1(&u8, 0x80); - __sync_fetch_and_and_2(&u16, 0x80); - __sync_fetch_and_and_4(&u32, 0x80); -#ifdef __HAVE_ATOMIC64_OPS - __sync_fetch_and_and_8(&u64, 0x80); -#endif -#ifndef __clang__ - __sync_fetch_and_nand(&u8, 0x80); - __sync_fetch_and_nand_1(&u8, 0x80); - __sync_fetch_and_nand_2(&u16, 0x80); - __sync_fetch_and_nand_4(&u32, 0x80); -#ifdef __HAVE_ATOMIC64_OPS - __sync_fetch_and_nand_8(&u64, 0x80); -#endif -#endif - __sync_fetch_and_or(&u8, 0x80); - __sync_fetch_and_or_1(&u8, 0x80); - __sync_fetch_and_or_2(&u16, 0x80); - __sync_fetch_and_or_4(&u32, 0x80); -#ifdef __HAVE_ATOMIC64_OPS - __sync_fetch_and_or_8(&u64, 0x80); -#endif - __sync_fetch_and_sub(&u8, 0x80); - __sync_fetch_and_sub_1(&u8, 0x80); - __sync_fetch_and_sub_2(&u16, 0x80); - __sync_fetch_and_sub_4(&u32, 0x80); -#ifdef __HAVE_ATOMIC64_OPS - __sync_fetch_and_sub_8(&u64, 0x80); -#endif - __sync_fetch_and_xor(&u8, 0x80); - __sync_fetch_and_xor_1(&u8, 0x80); - __sync_fetch_and_xor_2(&u16, 0x80); - __sync_fetch_and_xor_4(&u32, 0x80); -#ifdef __HAVE_ATOMIC64_OPS - __sync_fetch_and_xor_8(&u64, 0x80); -#endif - __sync_lock_release(&u8); - __sync_lock_release_1(&u8); - __sync_lock_release_2(&u16); - __sync_lock_release_4(&u32); -#ifdef __HAVE_ATOMIC64_OPS - __sync_lock_release_8(&u64); -#endif - __sync_lock_test_and_set(&u8, 5); - __sync_lock_test_and_set_1(&u8, 5); - __sync_lock_test_and_set_2(&u16, 5); - __sync_lock_test_and_set_4(&u32, 5); -#ifdef __HAVE_ATOMIC64_OPS - __sync_lock_test_and_set_8(&u64, 5); -#endif -#ifndef __clang__ - __sync_nand_and_fetch(&u8, 5); - __sync_nand_and_fetch_1(&u8, 5); - __sync_nand_and_fetch_2(&u16, 5); - __sync_nand_and_fetch_4(&u32, 5); -#ifdef __HAVE_ATOMIC64_OPS - __sync_nand_and_fetch_8(&u64, 5); -#endif -#endif - __sync_or_and_fetch(&u8, 5); - __sync_or_and_fetch_1(&u8, 5); - __sync_or_and_fetch_2(&u16, 5); - __sync_or_and_fetch_4(&u32, 5); -#ifdef __HAVE_ATOMIC64_OPS - __sync_or_and_fetch_8(&u64, 5); -#endif - __sync_sub_and_fetch(&u8, 5); - __sync_sub_and_fetch_1(&u8, 5); - __sync_sub_and_fetch_2(&u16, 5); - __sync_sub_and_fetch_4(&u32, 5); -#ifdef __HAVE_ATOMIC64_OPS - __sync_sub_and_fetch_8(&u64, 5); -#endif - __sync_val_compare_and_swap(&u8, 5, 9); - __sync_val_compare_and_swap_1(&u8, 5, 9); - __sync_val_compare_and_swap_2(&u16, 5, 9); - __sync_val_compare_and_swap_4(&u32, 5, 9); -#ifdef __HAVE_ATOMIC64_OPS - __sync_val_compare_and_swap_8(&u64, 5, 9); -#endif - __sync_xor_and_fetch(&u8, 5); - __sync_xor_and_fetch_1(&u8, 5); - __sync_xor_and_fetch_2(&u16, 5); - __sync_xor_and_fetch_4(&u32, 5); -#ifdef __HAVE_ATOMIC64_OPS - __sync_xor_and_fetch_8(&u64, 5); -#endif - - return 0; -} diff --git a/lib/libc/sys/Makefile b/lib/libc/sys/Makefile --- a/lib/libc/sys/Makefile +++ b/lib/libc/sys/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.45 2016/11/11 15:30:44 njoly Exp $ +# $NetBSD: Makefile,v 1.68 2020/09/06 07:20:31 mrg Exp $ MKMAN= no @@ -16,12 +16,16 @@ TESTS_C+= t_clone TESTS_C+= t_connect TESTS_C+= t_dup +TESTS_C+= t_fork TESTS_C+= t_fsync +TESTS_C+= t_futex_ops +TESTS_C+= t_futex_robust TESTS_C+= t_getcontext TESTS_C+= t_getgroups TESTS_C+= t_getitimer TESTS_C+= t_getlogin TESTS_C+= t_getpid +TESTS_C+= t_getrandom TESTS_C+= t_getrusage TESTS_C+= t_getsid TESTS_C+= t_getsockname @@ -50,40 +54,98 @@ TESTS_C+= t_pipe TESTS_C+= t_pipe2 TESTS_C+= t_poll +TESTS_C+= t_pollts TESTS_C+= t_posix_fallocate +TESTS_C+= t_ppoll +TESTS_C+= t_ptrace +TESTS_C+= t_ptrace_sigchld +TESTS_C+= t_ptrace_wait +TESTS_C+= t_ptrace_wait3 +TESTS_C+= t_ptrace_wait4 +TESTS_C+= t_ptrace_wait6 +TESTS_C+= t_ptrace_waitid +TESTS_C+= t_ptrace_waitpid TESTS_C+= t_recvmmsg TESTS_C+= t_revoke TESTS_C+= t_select +TESTS_C+= t_sendmmsg +TESTS_C+= t_sendrecv TESTS_C+= t_setrlimit TESTS_C+= t_setuid TESTS_C+= t_sigaction +TESTS_C+= t_sigaltstack TESTS_C+= t_sigqueue TESTS_C+= t_sigtimedwait TESTS_C+= t_socketpair TESTS_C+= t_swapcontext TESTS_C+= t_stat +TESTS_C+= t_syscall TESTS_C+= t_timer_create TESTS_C+= t_truncate TESTS_C+= t_ucontext TESTS_C+= t_umask TESTS_C+= t_unlink +TESTS_C+= t_vfork TESTS_C+= t_wait TESTS_C+= t_wait_noproc TESTS_C+= t_wait_noproc_wnohang TESTS_C+= t_write -SRCS.t_mprotect= t_mprotect.c ${SRCS_EXEC_PROT} +SRCS.t_mprotect= t_mprotect.c ${SRCS_EXEC_PROT} t_mprotect_helper.c LDADD.t_getpid+= -lpthread +LDADD.t_ptrace_sigchld+= -pthread -lm + +LDADD.t_ptrace_wait+= -pthread -lm -lelf +LDADD.t_ptrace_wait3+= -pthread -lm -lelf +LDADD.t_ptrace_wait4+= -pthread -lm -lelf +LDADD.t_ptrace_wait6+= -pthread -lm -lelf +LDADD.t_ptrace_waitid+= -pthread -lm -lelf +LDADD.t_ptrace_waitpid+= -pthread -lm -lelf + .if (${MKRUMP} != "no") && !defined(BSD_MK_COMPAT_FILE) CPPFLAGS.t_posix_fadvise.c += -D_KERNTYPES TESTS_C+= t_posix_fadvise -LDADD.t_posix_fadvise+= -lrumpvfs -lrump -lrumpuser -lrump -lpthread +LDADD.t_posix_fadvise+= ${LIBRUMPBASE} +.endif + +CPPFLAGS.t_futex_ops.c += -I${.CURDIR}/../../../../lib +CPPFLAGS.t_futex_robust.c += -I${.CURDIR}/../../../../lib + +CPPFLAGS.t_lwp_create.c += -D_KERNTYPES +CPPFLAGS.t_ptrace_sigchld.c += -D__TEST_FENV +CPPFLAGS.t_ptrace_wait.c += -D_KERNTYPES -D__TEST_FENV +CPPFLAGS.t_ptrace_wait3.c += -D_KERNTYPES -D__TEST_FENV +CPPFLAGS.t_ptrace_wait4.c += -D_KERNTYPES -D__TEST_FENV +CPPFLAGS.t_ptrace_wait6.c += -D_KERNTYPES -D__TEST_FENV +CPPFLAGS.t_ptrace_waitid.c += -D_KERNTYPES -D__TEST_FENV +CPPFLAGS.t_ptrace_waitpid.c += -D_KERNTYPES -D__TEST_FENV +CPPFLAGS.t_ucontext.c += -D_KERNTYPES + +.if ${MKSANITIZER:Uno} != "yes" && ${MKLIBCSANITIZER:Uno} != "yes" +CPPFLAGS.t_ptrace_wait.c += -DENABLE_TESTS +CPPFLAGS.t_ptrace_wait3.c += -DENABLE_TESTS +CPPFLAGS.t_ptrace_wait4.c += -DENABLE_TESTS +CPPFLAGS.t_ptrace_wait6.c += -DENABLE_TESTS +CPPFLAGS.t_ptrace_waitid.c += -DENABLE_TESTS +CPPFLAGS.t_ptrace_waitpid.c += -DENABLE_TESTS .endif -CPPFLAGS.t_lwp_create.c += -D_KERNTYPES +FILES= truncate_test.root_owned +FILESBUILD= yes +FILESDIR_truncate_test.root_owned= ${TESTSDIR} +FILESMODE_truncate_test.root_owned= 0600 +FILESOWNER_truncate_test.root_owned= root +FILESGRP_truncate_test.root_owned= wheel + +CLEANFILES= truncate_test.root_owned +truncate_test.root_owned: + dd if=/dev/null bs=1 count=1 of=${.TARGET} WARNS= 4 +CWARNFLAGS.gcc+= ${GCC_NO_ADDR_OF_PACKED_MEMBER} \ + ${${ACTIVE_CC} == "gcc" && ${HAVE_GCC:U0} >= 8:? -Wno-error=deprecated :} + .include diff --git a/lib/libc/sys/msg.h b/lib/libc/sys/msg.h new file mode 100644 --- /dev/null +++ b/lib/libc/sys/msg.h @@ -0,0 +1,152 @@ +/* $NetBSD: msg.h,v 1.3 2020/03/06 14:06:56 kamil Exp $ */ + +/*- + * Copyright (c) 2016 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +struct msg_fds { + int pfd[2]; + int cfd[2]; +}; + +#define CLOSEFD(fd) do { \ + if (fd != -1) { \ + close(fd); \ + fd = -1; \ + } \ +} while (/*CONSTCOND*/ 0) + +static int __used +msg_open(struct msg_fds *fds) +{ + if (pipe(fds->pfd) == -1) + return -1; + if (pipe(fds->cfd) == -1) { + close(fds->pfd[0]); + close(fds->pfd[1]); + return -1; + } + return 0; +} + +static void __used +msg_close(struct msg_fds *fds) +{ + CLOSEFD(fds->pfd[0]); + CLOSEFD(fds->pfd[1]); + CLOSEFD(fds->cfd[0]); + CLOSEFD(fds->cfd[1]); +} + +static int __used +msg_write_child(const char *info, struct msg_fds *fds, void *msg, size_t len) +{ + ssize_t rv; + CLOSEFD(fds->cfd[1]); + CLOSEFD(fds->pfd[0]); + +// printf("Send %s\n", info); + rv = write(fds->pfd[1], msg, len); + if (rv != (ssize_t)len) + return 1; +// printf("Wait %s\n", info); + rv = read(fds->cfd[0], msg, len); + if (rv != (ssize_t)len) + return 1; + return 0; +} + +static int __used +msg_write_parent(const char *info, struct msg_fds *fds, void *msg, size_t len) +{ + ssize_t rv; + CLOSEFD(fds->pfd[1]); + CLOSEFD(fds->cfd[0]); + +// printf("Send %s\n", info); + rv = write(fds->cfd[1], msg, len); + if (rv != (ssize_t)len) + return 1; +// printf("Wait %s\n", info); + rv = read(fds->pfd[0], msg, len); + if (rv != (ssize_t)len) + return 1; + return 0; +} + +static int __used +msg_read_parent(const char *info, struct msg_fds *fds, void *msg, size_t len) +{ + ssize_t rv; + CLOSEFD(fds->pfd[1]); + CLOSEFD(fds->cfd[0]); + +// printf("Wait %s\n", info); + rv = read(fds->pfd[0], msg, len); + if (rv != (ssize_t)len) + return 1; +// printf("Send %s\n", info); + rv = write(fds->cfd[1], msg, len); + if (rv != (ssize_t)len) + return 1; + return 0; +} + +static int __used +msg_read_child(const char *info, struct msg_fds *fds, void *msg, size_t len) +{ + ssize_t rv; + CLOSEFD(fds->cfd[1]); + CLOSEFD(fds->pfd[0]); + +// printf("Wait %s\n", info); + rv = read(fds->cfd[0], msg, len); + if (rv != (ssize_t)len) + return 1; +// printf("Send %s\n", info); + rv = write(fds->pfd[1], msg, len); + if (rv != (ssize_t)len) + return 1; + return 0; +} + +#define PARENT_TO_CHILD(info, fds, msg) \ + SYSCALL_REQUIRE(msg_write_child(info " to child " # fds, &fds, &msg, \ + sizeof(msg)) == 0) + +#define CHILD_FROM_PARENT(info, fds, msg) \ + FORKEE_ASSERT(msg_read_parent(info " from parent " # fds, &fds, &msg, \ + sizeof(msg)) == 0) + +#define CHILD_TO_PARENT(info, fds, msg) \ + FORKEE_ASSERT(msg_write_parent(info " to parent " # fds, &fds, &msg, \ + sizeof(msg)) == 0) + +#define PARENT_FROM_CHILD(info, fds, msg) \ + SYSCALL_REQUIRE(msg_read_child(info " from parent " # fds, &fds, &msg, \ + sizeof(msg)) == 0) diff --git a/lib/libc/sys/t_access.c b/lib/libc/sys/t_access.c --- a/lib/libc/sys/t_access.c +++ b/lib/libc/sys/t_access.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_access.c,v 1.2 2017/01/10 22:36:29 christos Exp $ */ +/* $NetBSD: t_access.c,v 1.3 2019/07/16 17:29:18 martin Exp $ */ /*- * Copyright (c) 2011 The NetBSD Foundation, Inc. @@ -29,7 +29,7 @@ * POSSIBILITY OF SUCH DAMAGE. */ #include -__RCSID("$NetBSD: t_access.c,v 1.2 2017/01/10 22:36:29 christos Exp $"); +__RCSID("$NetBSD: t_access.c,v 1.3 2019/07/16 17:29:18 martin Exp $"); #include @@ -58,7 +58,7 @@ size_t i; int fd; - fd = open(path, O_RDONLY | O_CREAT); + fd = open(path, O_RDONLY | O_CREAT, 0600); if (fd < 0) return; diff --git a/lib/libc/sys/t_clone.c b/lib/libc/sys/t_clone.c --- a/lib/libc/sys/t_clone.c +++ b/lib/libc/sys/t_clone.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_clone.c,v 1.3 2011/12/12 20:55:44 joerg Exp $ */ +/* $NetBSD: t_clone.c,v 1.4 2017/05/23 15:56:55 christos Exp $ */ /*- * Copyright (c) 2008 The NetBSD Foundation, Inc. @@ -32,7 +32,7 @@ #include __COPYRIGHT("@(#) Copyright (c) 2008\ The NetBSD Foundation, inc. All rights reserved."); -__RCSID("$NetBSD: t_clone.c,v 1.3 2011/12/12 20:55:44 joerg Exp $"); +__RCSID("$NetBSD: t_clone.c,v 1.4 2017/05/23 15:56:55 christos Exp $"); #include #include @@ -99,7 +99,7 @@ volatile long frobme[2]; int stat; - allocstack = mmap(NULL, STACKSIZE, PROT_READ|PROT_WRITE|PROT_EXEC, + allocstack = mmap(NULL, STACKSIZE, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, (off_t) 0); ATF_REQUIRE_ERRNO(errno, allocstack != MAP_FAILED); @@ -193,7 +193,7 @@ void *allocstack, *stack; int rv; - allocstack = mmap(NULL, STACKSIZE, PROT_READ|PROT_WRITE|PROT_EXEC, + allocstack = mmap(NULL, STACKSIZE, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, (off_t) 0); ATF_REQUIRE_ERRNO(errno, allocstack != MAP_FAILED); stack = allocstack; diff --git a/lib/libc/sys/t_fork.c b/lib/libc/sys/t_fork.c new file mode 100644 --- /dev/null +++ b/lib/libc/sys/t_fork.c @@ -0,0 +1,377 @@ +/* $NetBSD: t_fork.c,v 1.4 2019/04/06 15:41:54 kamil Exp $ */ + +/*- + * Copyright (c) 2018, 2019 The NetBSD Foundation, Inc. + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +__COPYRIGHT("@(#) Copyright (c) 2018, 2019\ + The NetBSD Foundation, inc. All rights reserved."); +__RCSID("$NetBSD: t_fork.c,v 1.4 2019/04/06 15:41:54 kamil Exp $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifdef VFORK +#define FORK vfork +#else +#define FORK fork +#endif + +/* + * A child process cannot call atf functions and expect them to magically + * work like in the parent. + * The printf(3) messaging from a child will not work out of the box as well + * without estabilishing a communication protocol with its parent. To not + * overcomplicate the tests - do not log from a child and use err(3)/errx(3) + * wrapped with ASSERT_EQ()/ASSERT_NEQ() as that is guaranteed to work. + */ +#define ASSERT_EQ(x, y) \ +do { \ + uintmax_t vx = (x); \ + uintmax_t vy = (y); \ + int ret = vx == vy; \ + if (!ret) \ + errx(EXIT_FAILURE, "%s:%d %s(): Assertion failed for: " \ + "%s(%ju) == %s(%ju)", __FILE__, __LINE__, __func__, \ + #x, vx, #y, vy); \ +} while (/*CONSTCOND*/0) + +#define ASSERT_NEQ(x, y) \ +do { \ + uintmax_t vx = (x); \ + uintmax_t vy = (y); \ + int ret = vx != vy; \ + if (!ret) \ + errx(EXIT_FAILURE, "%s:%d %s(): Assertion failed for: " \ + "%s(%ju) != %s(%ju)", __FILE__, __LINE__, __func__, \ + #x, vx, #y, vy); \ +} while (/*CONSTCOND*/0) + +static pid_t +await_stopped_child(pid_t process) +{ + struct kinfo_proc2 *p = NULL; + size_t i, len; + pid_t child = -1; + + int name[] = { + [0] = CTL_KERN, + [1] = KERN_PROC2, + [2] = KERN_PROC_ALL, + [3] = 0, + [4] = sizeof(struct kinfo_proc2), + [5] = 0 + }; + + const size_t namelen = __arraycount(name); + + /* Await the process becoming a zombie */ + while(1) { + name[5] = 0; + + ASSERT_EQ(sysctl(name, namelen, 0, &len, NULL, 0), 0); + + ASSERT_EQ(reallocarr(&p, len, sizeof(struct kinfo_proc2)), 0); + + name[5] = len; + + ASSERT_EQ(sysctl(name, namelen, p, &len, NULL, 0), 0); + + for (i = 0; i < len/sizeof(struct kinfo_proc2); i++) { + if (p[i].p_pid == getpid()) + continue; + if (p[i].p_ppid != process) + continue; + if (p[i].p_stat != LSSTOP) + continue; + child = p[i].p_pid; + break; + } + + if (child != -1) + break; + + ASSERT_EQ(usleep(1000), 0); + } + + /* Free the buffer */ + ASSERT_EQ(reallocarr(&p, 0, sizeof(struct kinfo_proc2)), 0); + + return child; +} + +static void +raise_raw(int sig) +{ + int rv, status; + pid_t child, parent, watcher, wpid; + int expect_core = (sig == SIGABRT) ? 1 : 0; + + /* + * Spawn a dedicated thread to watch for a stopped child and emit + * the SIGKILL signal to it. + * + * This is required in vfork(2)ing parent and optional in fork(2). + * + * vfork(2) might clobber watcher, this means that it's safer and + * simpler to reparent this process to initproc and forget about it. + */ + if (sig == SIGSTOP +#ifndef VFORK + || (sig == SIGTSTP || sig == SIGTTIN || sig == SIGTTOU) +#endif + ) { + + parent = getpid(); + + watcher = fork(); + ATF_REQUIRE(watcher != 1); + if (watcher == 0) { + /* Double fork(2) trick to reparent to initproc */ + watcher = fork(); + ASSERT_NEQ(watcher, -1); + if (watcher != 0) + _exit(0); + + child = await_stopped_child(parent); + + errno = 0; + rv = kill(child, SIGKILL); + ASSERT_EQ(rv, 0); + ASSERT_EQ(errno, 0); + + /* This exit value will be collected by initproc */ + _exit(0); + } + + wpid = waitpid(watcher, &status, 0); + + ATF_REQUIRE_EQ(wpid, watcher); + + ATF_REQUIRE(WIFEXITED(status)); + ATF_REQUIRE(!WIFCONTINUED(status)); + ATF_REQUIRE(!WIFSIGNALED(status)); + ATF_REQUIRE(!WIFSTOPPED(status)); + ATF_REQUIRE_EQ(WEXITSTATUS(status), 0); + } + + child = FORK(); + ATF_REQUIRE(child != 1); + if (child == 0) { + rv = raise(sig); + ASSERT_EQ(rv, 0); + _exit(0); + } + wpid = waitpid(child, &status, 0); + + ATF_REQUIRE_EQ(wpid, child); + + switch (sig) { + case SIGKILL: + case SIGABRT: + case SIGHUP: + ATF_REQUIRE(!WIFEXITED(status)); + ATF_REQUIRE(!WIFCONTINUED(status)); + ATF_REQUIRE(WIFSIGNALED(status)); + ATF_REQUIRE(!WIFSTOPPED(status)); + ATF_REQUIRE_EQ(WTERMSIG(status), sig); + ATF_REQUIRE_EQ(!!WCOREDUMP(status), expect_core); + break; +#ifdef VFORK + case SIGTSTP: + case SIGTTIN: + case SIGTTOU: +#endif + case SIGCONT: + ATF_REQUIRE(WIFEXITED(status)); + ATF_REQUIRE(!WIFCONTINUED(status)); + ATF_REQUIRE(!WIFSIGNALED(status)); + ATF_REQUIRE(!WIFSTOPPED(status)); + ATF_REQUIRE_EQ(WEXITSTATUS(status), 0); + break; +#ifndef VFORK + case SIGTSTP: + case SIGTTIN: + case SIGTTOU: +#endif + case SIGSTOP: + ATF_REQUIRE(!WIFEXITED(status)); + ATF_REQUIRE(!WIFCONTINUED(status)); + ATF_REQUIRE(WIFSIGNALED(status)); + ATF_REQUIRE(!WIFSTOPPED(status)); + ATF_REQUIRE_EQ(WTERMSIG(status), SIGKILL); + ATF_REQUIRE_EQ(!!WCOREDUMP(status), 0); + } +} + +#define RAISE(test, sig) \ +ATF_TC(test); \ +ATF_TC_HEAD(test, tc) \ +{ \ + \ + atf_tc_set_md_var(tc, "descr", \ + "raise " #sig " in a child"); \ +} \ + \ +ATF_TC_BODY(test, tc) \ +{ \ + \ + raise_raw(sig); \ +} + +RAISE(raise1, SIGKILL) /* non-maskable */ +RAISE(raise2, SIGSTOP) /* non-maskable */ +RAISE(raise3, SIGTSTP) /* ignored in vfork(2) */ +RAISE(raise4, SIGTTIN) /* ignored in vfork(2) */ +RAISE(raise5, SIGTTOU) /* ignored in vfork(2) */ +RAISE(raise6, SIGABRT) /* regular abort trap */ +RAISE(raise7, SIGHUP) /* hangup */ +RAISE(raise8, SIGCONT) /* continued? */ + +/// ---------------------------------------------------------------------------- + +static int +clone_func(void *arg __unused) +{ + + return 0; +} + +static void +nested_raw(const char *fn, volatile int flags) +{ + int status; + pid_t child, child2, wpid; + const size_t stack_size = 1024 * 1024; + void *stack, *stack_base; + + stack = malloc(stack_size); + ATF_REQUIRE(stack != NULL); + +#ifdef __MACHINE_STACK_GROWS_UP + stack_base = stack; +#else + stack_base = (char *)stack + stack_size; +#endif + + flags |= SIGCHLD; + + child = FORK(); + ATF_REQUIRE(child != 1); + if (child == 0) { + if (strcmp(fn, "fork") == 0) + child2 = fork(); + else if (strcmp(fn, "vfork") == 0) + child2 = vfork(); + else if (strcmp(fn, "clone") == 0) + child2 = __clone(clone_func, stack_base, flags, NULL); + else + __unreachable(); + + ASSERT_NEQ(child2, -1); + + if ((strcmp(fn, "fork") == 0) || (strcmp(fn, "vfork") == 0)) { + if (child2 == 0) + _exit(0); + } + + wpid = waitpid(child2, &status, 0); + ASSERT_EQ(child2, wpid); + ASSERT_EQ(!!WIFEXITED(status), true); + ASSERT_EQ(!!WIFCONTINUED(status), false); + ASSERT_EQ(!!WIFSIGNALED(status), false); + ASSERT_EQ(!!WIFSTOPPED(status), false); + ASSERT_EQ(WEXITSTATUS(status), 0); + + _exit(0); + } + wpid = waitpid(child, &status, 0); + + ATF_REQUIRE_EQ(wpid, child); + ATF_REQUIRE_EQ(!!WIFEXITED(status), true); + ATF_REQUIRE_EQ(!!WIFCONTINUED(status), false); + ATF_REQUIRE_EQ(!!WIFSIGNALED(status), false); + ATF_REQUIRE_EQ(!!WIFSTOPPED(status), false); + ATF_REQUIRE_EQ(WEXITSTATUS(status), 0); +} + +#define NESTED(test, fn, flags) \ +ATF_TC(test); \ +ATF_TC_HEAD(test, tc) \ +{ \ + \ + atf_tc_set_md_var(tc, "descr", \ + "Test nested " #fn " in a child"); \ +} \ + \ +ATF_TC_BODY(test, tc) \ +{ \ + \ + nested_raw(#fn, flags); \ +} + +NESTED(nested_fork, fork, 0) +NESTED(nested_vfork, vfork, 0) +NESTED(nested_clone, clone, 0) +NESTED(nested_clone_vm, clone, CLONE_VM) +NESTED(nested_clone_fs, clone, CLONE_FS) +NESTED(nested_clone_files, clone, CLONE_FILES) +//NESTED(nested_clone_sighand, clone, CLONE_SIGHAND) // XXX +NESTED(nested_clone_vfork, clone, CLONE_VFORK) + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, raise1); + ATF_TP_ADD_TC(tp, raise2); + ATF_TP_ADD_TC(tp, raise3); + ATF_TP_ADD_TC(tp, raise4); + ATF_TP_ADD_TC(tp, raise5); + ATF_TP_ADD_TC(tp, raise6); + ATF_TP_ADD_TC(tp, raise7); + ATF_TP_ADD_TC(tp, raise8); + + ATF_TP_ADD_TC(tp, nested_fork); + ATF_TP_ADD_TC(tp, nested_vfork); + ATF_TP_ADD_TC(tp, nested_clone); + ATF_TP_ADD_TC(tp, nested_clone_vm); + ATF_TP_ADD_TC(tp, nested_clone_fs); + ATF_TP_ADD_TC(tp, nested_clone_files); +// ATF_TP_ADD_TC(tp, nested_clone_sighand); // XXX + ATF_TP_ADD_TC(tp, nested_clone_vfork); + + return atf_no_error(); +} diff --git a/lib/libc/sys/t_futex_ops.c b/lib/libc/sys/t_futex_ops.c new file mode 100644 --- /dev/null +++ b/lib/libc/sys/t_futex_ops.c @@ -0,0 +1,1517 @@ +/* $NetBSD: t_futex_ops.c,v 1.5 2020/05/06 05:14:27 thorpej Exp $ */ + +/*- + * Copyright (c) 2019, 2020 The NetBSD Foundation, Inc. + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +__COPYRIGHT("@(#) Copyright (c) 2019, 2020\ + The NetBSD Foundation, inc. All rights reserved."); +__RCSID("$NetBSD: t_futex_ops.c,v 1.5 2020/05/06 05:14:27 thorpej Exp $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#define LOAD(x) (*(volatile int *)(x)) +#define STORE(x, y) *(volatile int *)(x) = (y) + +#if 0 +#define DPRINTF(x) printf x +#else +#define DPRINTF(x) __nothing +#endif + +#define STACK_SIZE 65536 + +static volatile int futex_word; +static volatile int futex_word1; + +static volatile unsigned int nlwps_running; + +struct lwp_data { + ucontext_t context; + void (*func)(void *); + void *stack_base; + lwpid_t lwpid; + pid_t child; + lwpid_t threadid; + int wait_op; + int op_flags; + int bitset; + volatile int *futex_ptr; + volatile int *error_ptr; + int block_val; + + void (*exit_func)(void); + + int futex_error; +}; + +#define WAITER_LWP0 0 +#define WAITER_LWP1 1 +#define WAITER_LWP2 2 +#define WAITER_LWP3 3 +#define WAITER_LWP4 4 +#define WAITER_LWP5 5 +#define NLWPS 6 + +struct lwp_data lwp_data[NLWPS]; + +static const char *bs_path = "t_futex_ops_backing_store"; +static int bs_fd = -1; +static int *bs_addr = MAP_FAILED; +static void *bs_source_buffer = NULL; +static void *bs_verify_buffer = NULL; +static long bs_pagesize; + +static void +create_lwp_waiter(struct lwp_data *d) +{ + ATF_REQUIRE(_lwp_create(&d->context, 0, &d->lwpid) == 0); +} + +static void +exit_lwp_waiter(void) +{ + _lwp_exit(); +} + +static void +reap_lwp_waiter(struct lwp_data *d) +{ + ATF_REQUIRE(_lwp_wait(d->lwpid, NULL) == 0); +} + +static void +create_proc_waiter(struct lwp_data *d) +{ + pid_t pid; + + ATF_REQUIRE((pid = fork()) != -1); + if (pid == 0) { + (*d->func)(d); + _exit(666); /* backstop */ + } else + d->child = pid; +} + +static void +exit_proc_waiter(void) +{ + _exit(0); +} + +static void +reap_proc_waiter(struct lwp_data *d) +{ + int status; + + ATF_REQUIRE(waitpid(d->child, &status, 0) == d->child); + ATF_REQUIRE(WIFEXITED(status)); + ATF_REQUIRE(WEXITSTATUS(status) == 0); +} + +static void +setup_lwp_context(struct lwp_data *d, void (*func)(void *)) +{ + + memset(d, 0, sizeof(*d)); + d->stack_base = mmap(NULL, STACK_SIZE, PROT_READ | PROT_WRITE, + MAP_ANON | MAP_STACK | MAP_PRIVATE, -1, 0); + ATF_REQUIRE(d->stack_base != MAP_FAILED); + _lwp_makecontext(&d->context, func, d, NULL, d->stack_base, STACK_SIZE); + d->threadid = 0; + d->func = func; +} + +static void +simple_test_waiter_lwp(void *arg) +{ + struct lwp_data *d = arg; + + d->threadid = _lwp_self(); + + membar_producer(); + atomic_inc_uint(&nlwps_running); + membar_sync(); + + if (__futex(d->futex_ptr, d->wait_op | d->op_flags, + d->block_val, NULL, NULL, 0, d->bitset) == -1) { + d->futex_error = errno; + membar_sync(); + atomic_dec_uint(&nlwps_running); + _lwp_exit(); + } else { + d->futex_error = 0; + } + + membar_sync(); + atomic_dec_uint(&nlwps_running); + + _lwp_exit(); +} + +static bool +verify_zero_bs(void) +{ + + if (bs_verify_buffer == NULL) { + bs_verify_buffer = malloc(bs_pagesize); + ATF_REQUIRE(bs_verify_buffer != NULL); + } + + ATF_REQUIRE(pread(bs_fd, bs_verify_buffer, + bs_pagesize, 0) == bs_pagesize); + + return (memcmp(bs_verify_buffer, bs_source_buffer, bs_pagesize) == 0); +} + +static void +create_bs(int map_flags) +{ + + bs_pagesize = sysconf(_SC_PAGESIZE); + ATF_REQUIRE(bs_pagesize > 0); + + if ((map_flags & (MAP_FILE | MAP_ANON)) == MAP_FILE) { + bs_source_buffer = calloc(1, bs_pagesize); + ATF_REQUIRE(bs_source_buffer != NULL); + + bs_fd = open(bs_path, O_RDWR | O_CREAT | O_EXCL, 0644); + ATF_REQUIRE(bs_fd != -1); + + ATF_REQUIRE(pwrite(bs_fd, bs_source_buffer, + bs_pagesize, 0) == bs_pagesize); + ATF_REQUIRE(verify_zero_bs()); + } + + bs_addr = mmap(NULL, bs_pagesize, PROT_READ | PROT_WRITE, + map_flags | MAP_HASSEMAPHORE, bs_fd, 0); + ATF_REQUIRE(bs_addr != MAP_FAILED); +} + +static void +cleanup_bs(void) +{ + + if (bs_fd != -1) { + (void) close(bs_fd); + bs_fd = -1; + (void) unlink(bs_path); + } + if (bs_source_buffer != NULL) { + free(bs_source_buffer); + bs_source_buffer = NULL; + } + if (bs_verify_buffer != NULL) { + free(bs_verify_buffer); + bs_verify_buffer = NULL; + } + if (bs_addr != MAP_FAILED) { + munmap(bs_addr, bs_pagesize); + bs_addr = MAP_FAILED; + } +} + +static void +do_cleanup(void) +{ + int i; + + for (i = 0; i < NLWPS; i++) { + struct lwp_data *d = &lwp_data[i]; + if (d->stack_base != NULL && d->stack_base != MAP_FAILED) { + (void) munmap(d->stack_base, STACK_SIZE); + } + } + memset(lwp_data, 0, sizeof(lwp_data)); + STORE(&futex_word, 0); + STORE(&futex_word1, 0); + nlwps_running = 0; + + cleanup_bs(); +} + +/*****************************************************************************/ + +static void +wait_wake_test_waiter_lwp(void *arg) +{ + struct lwp_data *d = arg; + + d->threadid = _lwp_self(); + + STORE(d->futex_ptr, 1); + membar_sync(); + + /* This will block because *futex_ptr == 1. */ + if (__futex(d->futex_ptr, FUTEX_WAIT | d->op_flags, + 1, NULL, NULL, 0, 0) == -1) { + STORE(d->error_ptr, errno); + (*d->exit_func)(); + } else { + STORE(d->error_ptr, 0); + } + + do { + membar_sync(); + sleep(1); + } while (LOAD(d->futex_ptr) != 0); + + STORE(d->futex_ptr, 2); + membar_sync(); + + do { + membar_sync(); + sleep(1); + } while (LOAD(d->futex_ptr) != 3); + + /* This will not block because futex_word != 666. */ + if (__futex(d->futex_ptr, FUTEX_WAIT | d->op_flags, + 666, NULL, NULL, 0, 0) == -1) { + /* This SHOULD be EAGAIN. */ + STORE(d->error_ptr, errno); + } + + STORE(d->futex_ptr, 4); + membar_sync(); + + (*d->exit_func)(); +} + +static void +do_futex_wait_wake_test(volatile int *futex_ptr, volatile int *error_ptr, + void (*create_func)(struct lwp_data *), + void (*exit_func)(void), + void (*reap_func)(struct lwp_data *), + int flags) +{ + struct lwp_data *wlwp = &lwp_data[WAITER_LWP0]; + int tries; + + if (error_ptr == NULL) + error_ptr = &wlwp->futex_error; + + if (create_func == NULL) + create_func = create_lwp_waiter; + if (exit_func == NULL) + exit_func = exit_lwp_waiter; + if (reap_func == NULL) + reap_func = reap_lwp_waiter; + + setup_lwp_context(wlwp, wait_wake_test_waiter_lwp); + + DPRINTF(("futex_basic_wait_wake: testing with flags 0x%x\n", flags)); + wlwp->op_flags = flags; + wlwp->error_ptr = error_ptr; + STORE(error_ptr, -1); + wlwp->futex_ptr = futex_ptr; + STORE(futex_ptr, 0); + wlwp->exit_func = exit_func; + membar_sync(); + + DPRINTF(("futex_basic_wait_wake: creating watier LWP\n")); + (*create_func)(wlwp); + + DPRINTF(("futex_basic_wait_wake: waiting for LWP %d to enter futex\n", + wlwp->lwpid)); + for (tries = 0; tries < 5; tries++) { + membar_sync(); + if (LOAD(futex_ptr) == 1) + break; + sleep(1); + } + membar_sync(); + ATF_REQUIRE(LOAD(futex_ptr) == 1); + + /* + * If the LWP is blocked in the futex, it will not have yet + * modified *error_ptr. + */ + DPRINTF(("futex_basic_wait_wake: checking for successful wait (%d)\n", + LOAD(error_ptr))); + for (tries = 0; tries < 5; tries++) { + membar_sync(); + if (LOAD(error_ptr) == -1) + break; + sleep(1); + } + membar_sync(); + ATF_REQUIRE(LOAD(error_ptr) == -1); + + /* Make sure invalid #wakes in rejected. */ + ATF_REQUIRE_ERRNO(EINVAL, + __futex(futex_ptr, FUTEX_WAKE | flags, + -1, NULL, NULL, 0, 0) == -1); + + DPRINTF(("futex_basic_wait_wake: waking 1 waiter\n")); + ATF_REQUIRE(__futex(futex_ptr, FUTEX_WAKE | flags, + 1, NULL, NULL, 0, 0) == 1); + + DPRINTF(("futex_basic_wait_wake: checking for successful wake (%d)\n", + LOAD(error_ptr))); + for (tries = 0; tries < 5; tries++) { + membar_sync(); + if (LOAD(error_ptr) == 0) + break; + sleep(1); + } + membar_sync(); + ATF_REQUIRE(LOAD(error_ptr) == 0); + + STORE(futex_ptr, 0); + membar_sync(); + + DPRINTF(("futex_basic_wait_wake: waiting for LWP to advance (2)\n")); + for (tries = 0; tries < 5; tries++) { + membar_sync(); + if (LOAD(futex_ptr) == 2) + break; + sleep(1); + } + membar_sync(); + ATF_REQUIRE(LOAD(futex_ptr) == 2); + + STORE(futex_ptr, 3); + membar_sync(); + + DPRINTF(("futex_basic_wait_wake: waiting for LWP to advance (4)\n")); + for (tries = 0; tries < 5; tries++) { + membar_sync(); + if (LOAD(futex_ptr) == 4) + break; + sleep(1); + } + membar_sync(); + ATF_REQUIRE(LOAD(futex_ptr) == 4); + + DPRINTF(("futex_basic_wait_wake: checking for expected EGAIN\n")); + ATF_REQUIRE(LOAD(error_ptr) == EAGAIN); + + DPRINTF(("futex_basic_wait_wake: reaping LWP %d\n", wlwp->lwpid)); + (*reap_func)(wlwp); +} + +ATF_TC_WITH_CLEANUP(futex_basic_wait_wake_private); +ATF_TC_HEAD(futex_basic_wait_wake_private, tc) +{ + atf_tc_set_md_var(tc, "descr", + "tests basic futex WAIT + WAKE operations (PRIVATE)"); +} +ATF_TC_BODY(futex_basic_wait_wake_private, tc) +{ + do_futex_wait_wake_test(&futex_word, NULL, + NULL, NULL, NULL, + FUTEX_PRIVATE_FLAG); +} +ATF_TC_CLEANUP(futex_basic_wait_wake_private, tc) +{ + do_cleanup(); +} + +ATF_TC_WITH_CLEANUP(futex_basic_wait_wake_shared); +ATF_TC_HEAD(futex_basic_wait_wake_shared, tc) +{ + atf_tc_set_md_var(tc, "descr", + "tests basic futex WAIT + WAKE operations (SHARED)"); +} +ATF_TC_BODY(futex_basic_wait_wake_shared, tc) +{ + do_futex_wait_wake_test(&futex_word, NULL, + NULL, NULL, NULL, + 0); +} +ATF_TC_CLEANUP(futex_basic_wait_wake_shared, tc) +{ + do_cleanup(); +} + +ATF_TC_WITH_CLEANUP(futex_wait_wake_anon_bs_private); +ATF_TC_HEAD(futex_wait_wake_anon_bs_private, tc) +{ + atf_tc_set_md_var(tc, "descr", + "tests futex WAIT + WAKE operations (MAP_ANON + PRIVATE)"); +} +ATF_TC_BODY(futex_wait_wake_anon_bs_private, tc) +{ + create_bs(MAP_ANON | MAP_PRIVATE); + do_futex_wait_wake_test(&bs_addr[0], NULL, + NULL, NULL, NULL, + FUTEX_PRIVATE_FLAG); +} +ATF_TC_CLEANUP(futex_wait_wake_anon_bs_private, tc) +{ + do_cleanup(); +} + +ATF_TC_WITH_CLEANUP(futex_wait_wake_anon_bs_shared); +ATF_TC_HEAD(futex_wait_wake_anon_bs_shared, tc) +{ + atf_tc_set_md_var(tc, "descr", + "tests futex WAIT + WAKE operations (MAP_ANON + SHARED)"); +} +ATF_TC_BODY(futex_wait_wake_anon_bs_shared, tc) +{ + create_bs(MAP_ANON | MAP_PRIVATE); + do_futex_wait_wake_test(&bs_addr[0], NULL, + NULL, NULL, NULL, + 0); +} +ATF_TC_CLEANUP(futex_wait_wake_anon_bs_shared, tc) +{ + do_cleanup(); +} + +ATF_TC_WITH_CLEANUP(futex_wait_wake_file_bs_private); +ATF_TC_HEAD(futex_wait_wake_file_bs_private, tc) +{ + atf_tc_set_md_var(tc, "descr", + "tests futex WAIT + WAKE operations (MAP_FILE + PRIVATE)"); +} +ATF_TC_BODY(futex_wait_wake_file_bs_private, tc) +{ + /* + * This combination (non-COW mapped file + PRIVATE futex) + * doesn't really make sense, but we should make sure it + * works as expected. + */ + create_bs(MAP_FILE | MAP_SHARED); + do_futex_wait_wake_test(&bs_addr[0], NULL, + NULL, NULL, NULL, + FUTEX_PRIVATE_FLAG); + ATF_REQUIRE(! verify_zero_bs()); +} +ATF_TC_CLEANUP(futex_wait_wake_file_bs_private, tc) +{ + do_cleanup(); +} + +ATF_TC_WITH_CLEANUP(futex_wait_wake_file_bs_cow_private); +ATF_TC_HEAD(futex_wait_wake_file_bs_cow_private, tc) +{ + atf_tc_set_md_var(tc, "descr", + "tests futex WAIT + WAKE operations (MAP_FILE COW + PRIVATE)"); +} +ATF_TC_BODY(futex_wait_wake_file_bs_cow_private, tc) +{ + create_bs(MAP_FILE | MAP_PRIVATE); + do_futex_wait_wake_test(&bs_addr[0], NULL, + NULL, NULL, NULL, + FUTEX_PRIVATE_FLAG); + ATF_REQUIRE(verify_zero_bs()); +} +ATF_TC_CLEANUP(futex_wait_wake_file_bs_cow_private, tc) +{ + do_cleanup(); +} + +ATF_TC_WITH_CLEANUP(futex_wait_wake_file_bs_shared); +ATF_TC_HEAD(futex_wait_wake_file_bs_shared, tc) +{ + atf_tc_set_md_var(tc, "descr", + "tests futex WAIT + WAKE operations (MAP_FILE + SHARED)"); +} +ATF_TC_BODY(futex_wait_wake_file_bs_shared, tc) +{ + create_bs(MAP_FILE | MAP_SHARED); + do_futex_wait_wake_test(&bs_addr[0], NULL, + NULL, NULL, NULL, + 0); + ATF_REQUIRE(! verify_zero_bs()); +} +ATF_TC_CLEANUP(futex_wait_wake_file_bs_shared, tc) +{ + do_cleanup(); +} + +ATF_TC_WITH_CLEANUP(futex_wait_wake_file_bs_cow_shared); +ATF_TC_HEAD(futex_wait_wake_file_bs_cow_shared, tc) +{ + atf_tc_set_md_var(tc, "descr", + "tests futex WAIT + WAKE operations (MAP_FILE COW + SHARED)"); +} +ATF_TC_BODY(futex_wait_wake_file_bs_cow_shared, tc) +{ + /* + * This combination (COW mapped file + SHARED futex) + * doesn't really make sense, but we should make sure it + * works as expected. + */ + create_bs(MAP_FILE | MAP_PRIVATE); + do_futex_wait_wake_test(&bs_addr[0], NULL, + NULL, NULL, NULL, + 0); + ATF_REQUIRE(verify_zero_bs()); +} +ATF_TC_CLEANUP(futex_wait_wake_file_bs_cow_shared, tc) +{ + do_cleanup(); +} + +ATF_TC_WITH_CLEANUP(futex_wait_wake_anon_bs_shared_proc); +ATF_TC_HEAD(futex_wait_wake_anon_bs_shared_proc, tc) +{ + atf_tc_set_md_var(tc, "descr", + "tests multiproc futex WAIT + WAKE operations (MAP_ANON + SHARED)"); +} +ATF_TC_BODY(futex_wait_wake_anon_bs_shared_proc, tc) +{ + create_bs(MAP_ANON | MAP_SHARED); + do_futex_wait_wake_test(&bs_addr[0], &bs_addr[1], + create_proc_waiter, + exit_proc_waiter, + reap_proc_waiter, + 0); +} +ATF_TC_CLEANUP(futex_wait_wake_anon_bs_shared_proc, tc) +{ + do_cleanup(); +} + +ATF_TC_WITH_CLEANUP(futex_wait_wake_file_bs_shared_proc); +ATF_TC_HEAD(futex_wait_wake_file_bs_shared_proc, tc) +{ + atf_tc_set_md_var(tc, "descr", + "tests multiproc futex WAIT + WAKE operations (MAP_ANON + SHARED)"); +} +ATF_TC_BODY(futex_wait_wake_file_bs_shared_proc, tc) +{ + create_bs(MAP_FILE | MAP_SHARED); + do_futex_wait_wake_test(&bs_addr[0], &bs_addr[1], + create_proc_waiter, + exit_proc_waiter, + reap_proc_waiter, + 0); +} +ATF_TC_CLEANUP(futex_wait_wake_file_bs_shared_proc, tc) +{ + do_cleanup(); +} + +/*****************************************************************************/ + +ATF_TC(futex_wait_pointless_bitset); +ATF_TC_HEAD(futex_wait_pointless_bitset, tc) +{ + atf_tc_set_md_var(tc, "descr", + "tests basic futex WAIT + WAKE operations (SHARED)"); +} +ATF_TC_BODY(futex_wait_pointless_bitset, tc) +{ + + futex_word = 1; + ATF_REQUIRE_ERRNO(EINVAL, + __futex(&futex_word, FUTEX_WAIT_BITSET | FUTEX_PRIVATE_FLAG, + 1, NULL, NULL, 0, 0) == -1); +} + +static void +do_futex_wait_wake_bitset_test(int flags) +{ + struct lwp_data *wlwp0 = &lwp_data[WAITER_LWP0]; + struct lwp_data *wlwp1 = &lwp_data[WAITER_LWP1]; + int i, tries; + + for (i = WAITER_LWP0; i <= WAITER_LWP1; i++) { + setup_lwp_context(&lwp_data[i], simple_test_waiter_lwp); + lwp_data[i].op_flags = flags; + lwp_data[i].futex_error = -1; + lwp_data[i].bitset = __BIT(i); + lwp_data[i].wait_op = FUTEX_WAIT_BITSET; + lwp_data[i].futex_ptr = &futex_word; + lwp_data[i].block_val = 1; + } + + STORE(&futex_word, 1); + membar_sync(); + + ATF_REQUIRE(_lwp_create(&wlwp0->context, 0, &wlwp0->lwpid) == 0); + ATF_REQUIRE(_lwp_create(&wlwp1->context, 0, &wlwp1->lwpid) == 0); + + for (tries = 0; tries < 5; tries++) { + membar_sync(); + if (nlwps_running == 2) + break; + sleep(1); + } + membar_sync(); + ATF_REQUIRE_EQ_MSG(nlwps_running, 2, "waiters failed to start"); + + /* Ensure they're blocked. */ + ATF_REQUIRE(wlwp0->futex_error == -1); + ATF_REQUIRE(wlwp1->futex_error == -1); + + /* Make sure invalid #wakes in rejected. */ + ATF_REQUIRE_ERRNO(EINVAL, + __futex(&futex_word, FUTEX_WAKE_BITSET | flags, + -1, NULL, NULL, 0, 0) == -1); + + /* This should result in no wakeups because no bits are set. */ + ATF_REQUIRE(__futex(&futex_word, FUTEX_WAKE_BITSET | flags, + INT_MAX, NULL, NULL, 0, 0) == 0); + + /* This should result in no wakeups because the wrongs bits are set. */ + ATF_REQUIRE(__futex(&futex_word, FUTEX_WAKE_BITSET | flags, + INT_MAX, NULL, NULL, 0, + ~(wlwp0->bitset | wlwp1->bitset)) == 0); + + /* Trust, but verify. */ + sleep(1); + for (tries = 0; tries < 5; tries++) { + membar_sync(); + if (nlwps_running == 2) + break; + sleep(1); + } + membar_sync(); + ATF_REQUIRE_EQ_MSG(nlwps_running, 2, "waiters exited unexpectedly"); + + /* Wake up the first LWP. */ + ATF_REQUIRE(__futex(&futex_word, FUTEX_WAKE_BITSET | flags, + INT_MAX, NULL, NULL, 0, + wlwp0->bitset) == 1); + sleep(1); + for (tries = 0; tries < 5; tries++) { + membar_sync(); + if (nlwps_running == 1) + break; + sleep(1); + } + membar_sync(); + ATF_REQUIRE(nlwps_running == 1); + ATF_REQUIRE(wlwp0->futex_error == 0); + ATF_REQUIRE(_lwp_wait(wlwp0->lwpid, NULL) == 0); + + /* Wake up the second LWP. */ + ATF_REQUIRE(__futex(&futex_word, FUTEX_WAKE_BITSET | flags, + INT_MAX, NULL, NULL, 0, + wlwp1->bitset) == 1); + sleep(1); + for (tries = 0; tries < 5; tries++) { + membar_sync(); + if (nlwps_running == 0) + break; + sleep(1); + } + membar_sync(); + ATF_REQUIRE(nlwps_running == 0); + ATF_REQUIRE(wlwp1->futex_error == 0); + ATF_REQUIRE(_lwp_wait(wlwp1->lwpid, NULL) == 0); +} + +ATF_TC_WITH_CLEANUP(futex_wait_wake_bitset); +ATF_TC_HEAD(futex_wait_wake_bitset, tc) +{ + atf_tc_set_md_var(tc, "descr", + "tests futex WAIT_BITSET + WAKE_BITSET operations"); +} +ATF_TC_BODY(futex_wait_wake_bitset, tc) +{ + do_futex_wait_wake_bitset_test(FUTEX_PRIVATE_FLAG); +} +ATF_TC_CLEANUP(futex_wait_wake_bitset, tc) +{ + do_cleanup(); +} + +/*****************************************************************************/ + +static void +do_futex_requeue_test(int flags, int op) +{ + struct lwp_data *wlwp0 = &lwp_data[WAITER_LWP0]; + struct lwp_data *wlwp1 = &lwp_data[WAITER_LWP1]; + struct lwp_data *wlwp2 = &lwp_data[WAITER_LWP2]; + struct lwp_data *wlwp3 = &lwp_data[WAITER_LWP3]; + const int good_val3 = (op == FUTEX_CMP_REQUEUE) ? 1 : 0; + const int bad_val3 = (op == FUTEX_CMP_REQUEUE) ? 666 : 0; + int i, tries; + + for (i = WAITER_LWP0; i <= WAITER_LWP3; i++) { + setup_lwp_context(&lwp_data[i], simple_test_waiter_lwp); + lwp_data[i].op_flags = flags; + lwp_data[i].futex_error = -1; + lwp_data[i].futex_ptr = &futex_word; + lwp_data[i].block_val = 1; + lwp_data[i].bitset = 0; + lwp_data[i].wait_op = FUTEX_WAIT; + } + + STORE(&futex_word, 1); + STORE(&futex_word1, 1); + membar_sync(); + + ATF_REQUIRE(_lwp_create(&wlwp0->context, 0, &wlwp0->lwpid) == 0); + ATF_REQUIRE(_lwp_create(&wlwp1->context, 0, &wlwp1->lwpid) == 0); + ATF_REQUIRE(_lwp_create(&wlwp2->context, 0, &wlwp2->lwpid) == 0); + ATF_REQUIRE(_lwp_create(&wlwp3->context, 0, &wlwp3->lwpid) == 0); + + for (tries = 0; tries < 5; tries++) { + membar_sync(); + if (nlwps_running == 4) + break; + sleep(1); + } + membar_sync(); + ATF_REQUIRE_EQ_MSG(nlwps_running, 4, "waiters failed to start"); + + /* Ensure they're blocked. */ + ATF_REQUIRE(wlwp0->futex_error == -1); + ATF_REQUIRE(wlwp1->futex_error == -1); + ATF_REQUIRE(wlwp2->futex_error == -1); + ATF_REQUIRE(wlwp3->futex_error == -1); + + /* Make sure invalid #wakes and #requeues are rejected. */ + ATF_REQUIRE_ERRNO(EINVAL, + __futex(&futex_word, op | flags, + -1, NULL, &futex_word1, INT_MAX, bad_val3) == -1); + + ATF_REQUIRE_ERRNO(EINVAL, + __futex(&futex_word, op | flags, + 0, NULL, &futex_word1, -1, bad_val3) == -1); + + /* + * FUTEX 0: 4 LWPs + * FUTEX 1: 0 LWPs + */ + + if (op == FUTEX_CMP_REQUEUE) { + /* This should fail because the futex_word value is 1. */ + ATF_REQUIRE_ERRNO(EAGAIN, + __futex(&futex_word, op | flags, + 0, NULL, &futex_word1, INT_MAX, bad_val3) == -1); + } + + /* + * FUTEX 0: 4 LWPs + * FUTEX 1: 0 LWPs + */ + + /* Move all waiters from 0 to 1. */ + ATF_REQUIRE(__futex(&futex_word, op | flags, + 0, NULL, &futex_word1, INT_MAX, good_val3) == 0); + + /* + * FUTEX 0: 0 LWPs + * FUTEX 1: 4 LWPs + */ + + if (op == FUTEX_CMP_REQUEUE) { + /* This should fail because the futex_word1 value is 1. */ + ATF_REQUIRE_ERRNO(EAGAIN, + __futex(&futex_word1, op | flags, + 1, NULL, &futex_word, 1, bad_val3) == -1); + } + + /* + * FUTEX 0: 0 LWPs + * FUTEX 1: 4 LWPs + */ + + /* Wake one waiter on 1, move one waiter to 0. */ + ATF_REQUIRE(__futex(&futex_word1, op | flags, + 1, NULL, &futex_word, 1, good_val3) == 1); + + /* + * FUTEX 0: 1 LWP + * FUTEX 1: 2 LWPs + */ + + /* Wake all waiters on 0 (should be 1). */ + ATF_REQUIRE(__futex(&futex_word, FUTEX_WAKE | flags, + INT_MAX, NULL, NULL, 0, 0) == 1); + + /* Wake all waiters on 1 (should be 2). */ + ATF_REQUIRE(__futex(&futex_word1, FUTEX_WAKE | flags, + INT_MAX, NULL, NULL, 0, 0) == 2); + + /* Trust, but verify. */ + sleep(1); + for (tries = 0; tries < 5; tries++) { + membar_sync(); + if (nlwps_running == 0) + break; + sleep(1); + } + membar_sync(); + ATF_REQUIRE_EQ_MSG(nlwps_running, 0, "waiters failed to exit"); + + ATF_REQUIRE(_lwp_wait(wlwp0->lwpid, NULL) == 0); + ATF_REQUIRE(_lwp_wait(wlwp1->lwpid, NULL) == 0); + ATF_REQUIRE(_lwp_wait(wlwp2->lwpid, NULL) == 0); + ATF_REQUIRE(_lwp_wait(wlwp3->lwpid, NULL) == 0); +} + +ATF_TC_WITH_CLEANUP(futex_requeue); +ATF_TC_HEAD(futex_requeue, tc) +{ + atf_tc_set_md_var(tc, "descr", + "tests futex REQUEUE operations"); +} +ATF_TC_BODY(futex_requeue, tc) +{ + do_futex_requeue_test(FUTEX_PRIVATE_FLAG, FUTEX_REQUEUE); +} +ATF_TC_CLEANUP(futex_requeue, tc) +{ + do_cleanup(); +} + +ATF_TC_WITH_CLEANUP(futex_cmp_requeue); +ATF_TC_HEAD(futex_cmp_requeue, tc) +{ + atf_tc_set_md_var(tc, "descr", + "tests futex CMP_REQUEUE operations"); +} +ATF_TC_BODY(futex_cmp_requeue, tc) +{ + do_futex_requeue_test(FUTEX_PRIVATE_FLAG, FUTEX_CMP_REQUEUE); +} +ATF_TC_CLEANUP(futex_cmp_requeue, tc) +{ + do_cleanup(); +} + +/*****************************************************************************/ + +static void +do_futex_wake_op_op_test(int flags) +{ + int op; + + futex_word = 0; + futex_word1 = 0; + + /* + * The op= operations should work even if there are no waiters. + */ + + /* + * Because these operations use both futex addresses, exercise + * rejecting unaligned futex addresses here. + */ + op = FUTEX_OP(FUTEX_OP_SET, 1, FUTEX_OP_CMP_EQ, 0); + ATF_REQUIRE_ERRNO(EINVAL, + __futex((int *)1, FUTEX_WAKE_OP | flags, + 0, NULL, &futex_word1, 0, op) == -1); + ATF_REQUIRE(futex_word1 == 0); + + ATF_REQUIRE_ERRNO(EINVAL, + __futex(&futex_word, FUTEX_WAKE_OP | flags, + 0, NULL, (int *)1, 0, op) == -1); + ATF_REQUIRE(futex_word == 0); + + /* Check unmapped uaddr2 handling, too. */ + ATF_REQUIRE_ERRNO(EFAULT, + __futex(&futex_word, FUTEX_WAKE_OP | flags, + 0, NULL, NULL, 0, op) == -1); + ATF_REQUIRE(futex_word == 0); + + op = FUTEX_OP(FUTEX_OP_SET, 1, FUTEX_OP_CMP_EQ, 0); + ATF_REQUIRE(__futex(&futex_word, FUTEX_WAKE_OP | flags, + 0, NULL, &futex_word1, 0, op) == 0); + ATF_REQUIRE(futex_word1 == 1); + + op = FUTEX_OP(FUTEX_OP_ADD, 1, FUTEX_OP_CMP_EQ, 0); + ATF_REQUIRE(__futex(&futex_word, FUTEX_WAKE_OP | flags, + 0, NULL, &futex_word1, 0, op) == 0); + ATF_REQUIRE(futex_word1 == 2); + + op = FUTEX_OP(FUTEX_OP_OR, 2, FUTEX_OP_CMP_EQ, 0); + ATF_REQUIRE(__futex(&futex_word, FUTEX_WAKE_OP | flags, + 0, NULL, &futex_word1, 0, op) == 0); + ATF_REQUIRE(futex_word1 == 2); + + /* This should fail because of invalid shift value 32. */ + op = FUTEX_OP(FUTEX_OP_OR | FUTEX_OP_OPARG_SHIFT, 32, + FUTEX_OP_CMP_EQ, 0); + ATF_REQUIRE_ERRNO(EINVAL, + __futex(&futex_word, FUTEX_WAKE_OP | flags, + 0, NULL, &futex_word1, 0, op) == -1); + ATF_REQUIRE(futex_word1 == 2); + + op = FUTEX_OP(FUTEX_OP_OR | FUTEX_OP_OPARG_SHIFT, 31, + FUTEX_OP_CMP_EQ, 0); + ATF_REQUIRE(__futex(&futex_word, FUTEX_WAKE_OP | flags, + 0, NULL, &futex_word1, 0, op) == 0); + ATF_REQUIRE(futex_word1 == (int)0x80000002); + + op = FUTEX_OP(FUTEX_OP_ANDN | FUTEX_OP_OPARG_SHIFT, 31, + FUTEX_OP_CMP_EQ, 0); + ATF_REQUIRE(__futex(&futex_word, FUTEX_WAKE_OP | flags, + 0, NULL, &futex_word1, 0, op) == 0); + ATF_REQUIRE(futex_word1 == 2); + + op = FUTEX_OP(FUTEX_OP_XOR, 2, FUTEX_OP_CMP_EQ, 0); + ATF_REQUIRE(__futex(&futex_word, FUTEX_WAKE_OP | flags, + 0, NULL, &futex_word1, 0, op) == 0); + ATF_REQUIRE(futex_word1 == 0); +} + +ATF_TC_WITH_CLEANUP(futex_wake_op_op); +ATF_TC_HEAD(futex_wake_op_op, tc) +{ + atf_tc_set_md_var(tc, "descr", + "tests futex WAKE_OP OP operations"); +} +ATF_TC_BODY(futex_wake_op_op, tc) +{ + do_futex_wake_op_op_test(FUTEX_PRIVATE_FLAG); +} +ATF_TC_CLEANUP(futex_wake_op_op, tc) +{ + do_cleanup(); +} + +static void +create_wake_op_test_lwps(int flags) +{ + int i; + + futex_word1 = 0; + membar_sync(); + + for (i = WAITER_LWP0; i <= WAITER_LWP5; i++) { + setup_lwp_context(&lwp_data[i], simple_test_waiter_lwp); + lwp_data[i].op_flags = flags; + lwp_data[i].futex_error = -1; + lwp_data[i].futex_ptr = &futex_word1; + lwp_data[i].block_val = 0; + lwp_data[i].bitset = 0; + lwp_data[i].wait_op = FUTEX_WAIT; + ATF_REQUIRE(_lwp_create(&lwp_data[i].context, 0, + &lwp_data[i].lwpid) == 0); + } + + for (i = 0; i < 5; i++) { + membar_sync(); + if (nlwps_running == 6) + break; + sleep(1); + } + membar_sync(); + ATF_REQUIRE_EQ_MSG(nlwps_running, 6, "waiters failed to start"); + + /* Ensure they're blocked. */ + for (i = WAITER_LWP0; i <= WAITER_LWP5; i++) { + ATF_REQUIRE(lwp_data[i].futex_error == -1); + } +} + +static void +reap_wake_op_test_lwps(void) +{ + int i; + + for (i = WAITER_LWP0; i <= WAITER_LWP5; i++) { + ATF_REQUIRE(_lwp_wait(lwp_data[i].lwpid, NULL) == 0); + } +} + +static void +do_futex_wake_op_cmp_test(int flags) +{ + int tries, op; + + futex_word = 0; + membar_sync(); + + /* + * Verify and negative and positive for each individual + * compare. + */ + + create_wake_op_test_lwps(flags); + + /* #LWPs = 6 */ + op = FUTEX_OP(FUTEX_OP_SET, 0, FUTEX_OP_CMP_EQ, 1); + ATF_REQUIRE(__futex(&futex_word, FUTEX_WAKE_OP | flags, + 0, NULL, &futex_word1, 1, op) == 0); + ATF_REQUIRE(futex_word1 == 0); + + op = FUTEX_OP(FUTEX_OP_SET, 1, FUTEX_OP_CMP_EQ, 0); + ATF_REQUIRE(__futex(&futex_word, FUTEX_WAKE_OP | flags, + 0, NULL, &futex_word1, 1, op) == 1); + ATF_REQUIRE(futex_word1 == 1); + + /* #LWPs = 5 */ + op = FUTEX_OP(FUTEX_OP_SET, 1, FUTEX_OP_CMP_NE, 1); + ATF_REQUIRE(__futex(&futex_word, FUTEX_WAKE_OP | flags, + 0, NULL, &futex_word1, 1, op) == 0); + ATF_REQUIRE(futex_word1 == 1); + + op = FUTEX_OP(FUTEX_OP_SET, 2, FUTEX_OP_CMP_NE, 2); + ATF_REQUIRE(__futex(&futex_word, FUTEX_WAKE_OP | flags, + 0, NULL, &futex_word1, 1, op) == 1); + ATF_REQUIRE(futex_word1 == 2); + + /* #LWPs = 4 */ + op = FUTEX_OP(FUTEX_OP_SET, 2, FUTEX_OP_CMP_LT, 2); + ATF_REQUIRE(__futex(&futex_word, FUTEX_WAKE_OP | flags, + 0, NULL, &futex_word1, 1, op) == 0); + ATF_REQUIRE(futex_word1 == 2); + + op = FUTEX_OP(FUTEX_OP_SET, 2, FUTEX_OP_CMP_LT, 3); + ATF_REQUIRE(__futex(&futex_word, FUTEX_WAKE_OP | flags, + 0, NULL, &futex_word1, 1, op) == 1); + ATF_REQUIRE(futex_word1 == 2); + + /* #LWPs = 3 */ + op = FUTEX_OP(FUTEX_OP_SET, 1, FUTEX_OP_CMP_LE, 1); + ATF_REQUIRE(__futex(&futex_word, FUTEX_WAKE_OP | flags, + 0, NULL, &futex_word1, 1, op) == 0); + ATF_REQUIRE(futex_word1 == 1); + + op = FUTEX_OP(FUTEX_OP_SET, 1, FUTEX_OP_CMP_LE, 1); + ATF_REQUIRE(__futex(&futex_word, FUTEX_WAKE_OP | flags, + 0, NULL, &futex_word1, 1, op) == 1); + ATF_REQUIRE(futex_word1 == 1); + + /* #LWPs = 2 */ + op = FUTEX_OP(FUTEX_OP_SET, 3, FUTEX_OP_CMP_GT, 3); + ATF_REQUIRE(__futex(&futex_word, FUTEX_WAKE_OP | flags, + 0, NULL, &futex_word1, 1, op) == 0); + ATF_REQUIRE(futex_word1 == 3); + + op = FUTEX_OP(FUTEX_OP_SET, 2, FUTEX_OP_CMP_GT, 2); + ATF_REQUIRE(__futex(&futex_word, FUTEX_WAKE_OP | flags, + 0, NULL, &futex_word1, 1, op) == 1); + ATF_REQUIRE(futex_word1 == 2); + + /* #LWPs = 1 */ + op = FUTEX_OP(FUTEX_OP_SET, 3, FUTEX_OP_CMP_GE, 4); + ATF_REQUIRE(__futex(&futex_word, FUTEX_WAKE_OP | flags, + 0, NULL, &futex_word1, 1, op) == 0); + ATF_REQUIRE(futex_word1 == 3); + + op = FUTEX_OP(FUTEX_OP_SET, 2, FUTEX_OP_CMP_GE, 3); + ATF_REQUIRE(__futex(&futex_word, FUTEX_WAKE_OP | flags, + 0, NULL, &futex_word1, 1, op) == 1); + ATF_REQUIRE(futex_word1 == 2); + + /* #LWPs = 0 */ + + /* Trust, but verify. */ + sleep(1); + for (tries = 0; tries < 5; tries++) { + membar_sync(); + if (nlwps_running == 0) + break; + sleep(1); + } + membar_sync(); + ATF_REQUIRE_EQ_MSG(nlwps_running, 0, "waiters failed to exit"); + + reap_wake_op_test_lwps(); + + /* + * Verify wakes on uaddr work even if the uaddr2 comparison + * fails. + */ + + create_wake_op_test_lwps(flags); + + /* #LWPs = 6 */ + ATF_REQUIRE(futex_word == 0); + op = FUTEX_OP(FUTEX_OP_SET, 0, FUTEX_OP_CMP_EQ, 666); + ATF_REQUIRE(__futex(&futex_word1, FUTEX_WAKE_OP | flags, + INT_MAX, NULL, &futex_word, 0, op) == 6); + ATF_REQUIRE(futex_word == 0); + + /* #LWPs = 0 */ + + /* Trust, but verify. */ + sleep(1); + for (tries = 0; tries < 5; tries++) { + membar_sync(); + if (nlwps_running == 0) + break; + sleep(1); + } + membar_sync(); + ATF_REQUIRE_EQ_MSG(nlwps_running, 0, "waiters failed to exit"); + + reap_wake_op_test_lwps(); +} + +ATF_TC_WITH_CLEANUP(futex_wake_op_cmp); +ATF_TC_HEAD(futex_wake_op_cmp, tc) +{ + atf_tc_set_md_var(tc, "descr", + "tests futex WAKE_OP CMP operations"); +} +ATF_TC_BODY(futex_wake_op_cmp, tc) +{ + do_futex_wake_op_cmp_test(FUTEX_PRIVATE_FLAG); +} +ATF_TC_CLEANUP(futex_wake_op_cmp, tc) +{ + do_cleanup(); +} + +/*****************************************************************************/ + +static void +do_futex_wait_timeout(bool relative, clockid_t clock) +{ + struct timespec ts; + struct timespec deadline; + int op = relative ? FUTEX_WAIT : FUTEX_WAIT_BITSET; + + if (clock == CLOCK_REALTIME) + op |= FUTEX_CLOCK_REALTIME; + + ATF_REQUIRE(clock_gettime(clock, &deadline) == 0); + deadline.tv_sec += 2; + if (relative) { + ts.tv_sec = 2; + ts.tv_nsec = 0; + } else { + ts = deadline; + } + + futex_word = 1; + ATF_REQUIRE_ERRNO(ETIMEDOUT, + __futex(&futex_word, op | FUTEX_PRIVATE_FLAG, + 1, &ts, NULL, 0, FUTEX_BITSET_MATCH_ANY) == -1); + + /* Can't reliably check CLOCK_REALTIME in the presence of NTP. */ + if (clock != CLOCK_REALTIME) { + ATF_REQUIRE(clock_gettime(clock, &ts) == 0); + ATF_REQUIRE(ts.tv_sec >= deadline.tv_sec); + ATF_REQUIRE(ts.tv_sec > deadline.tv_sec || + ts.tv_nsec >= deadline.tv_nsec); + } +} + +ATF_TC(futex_wait_timeout_relative); +ATF_TC_HEAD(futex_wait_timeout_relative, tc) +{ + atf_tc_set_md_var(tc, "descr", + "tests futex WAIT with relative timeout"); +} +ATF_TC_BODY(futex_wait_timeout_relative, tc) +{ + do_futex_wait_timeout(true, CLOCK_MONOTONIC); +} + +ATF_TC(futex_wait_timeout_relative_rt); +ATF_TC_HEAD(futex_wait_timeout_relative_rt, tc) +{ + atf_tc_set_md_var(tc, "descr", + "tests futex WAIT with relative timeout (REALTIME)"); +} +ATF_TC_BODY(futex_wait_timeout_relative_rt, tc) +{ + do_futex_wait_timeout(true, CLOCK_REALTIME); +} + +ATF_TC(futex_wait_timeout_deadline); +ATF_TC_HEAD(futex_wait_timeout_deadline, tc) +{ + atf_tc_set_md_var(tc, "descr", + "tests futex WAIT with absolute deadline"); +} +ATF_TC_BODY(futex_wait_timeout_deadline, tc) +{ + do_futex_wait_timeout(false, CLOCK_MONOTONIC); +} + +ATF_TC(futex_wait_timeout_deadline_rt); +ATF_TC_HEAD(futex_wait_timeout_deadline_rt, tc) +{ + atf_tc_set_md_var(tc, "descr", + "tests futex WAIT with absolute deadline (REALTIME)"); +} +ATF_TC_BODY(futex_wait_timeout_deadline_rt, tc) +{ + do_futex_wait_timeout(false, CLOCK_REALTIME); +} + +/*****************************************************************************/ + +static void +sig_noop(int sig __unused) +{ +} + +static void (*old_act)(int) = SIG_DFL; + +static void +do_futex_wait_evil_unmapped(int map_flags) +{ + int i; + + create_bs(map_flags); + + old_act = signal(SIGUSR1, sig_noop); + ATF_REQUIRE(old_act != SIG_ERR); + + setup_lwp_context(&lwp_data[0], simple_test_waiter_lwp); + lwp_data[0].op_flags = 0; + lwp_data[0].futex_error = -1; + lwp_data[0].futex_ptr = &bs_addr[0]; + lwp_data[0].block_val = 0; + lwp_data[0].bitset = 0; + lwp_data[0].wait_op = FUTEX_WAIT; + ATF_REQUIRE(_lwp_create(&lwp_data[0].context, 0, + &lwp_data[0].lwpid) == 0); + + for (i = 0; i < 5; i++) { + membar_sync(); + if (nlwps_running == 1) + break; + sleep(1); + } + membar_sync(); + ATF_REQUIRE_EQ_MSG(nlwps_running, 1, "waiters failed to start"); + + /* Ensure it's blocked. */ + ATF_REQUIRE(lwp_data[0].futex_error == -1); + + /* Rudely unmap the backing store. */ + cleanup_bs(); + + /* Signal the waiter so that it leaves the futex. */ + ATF_REQUIRE(_lwp_kill(lwp_data[0].threadid, SIGUSR1) == 0); + + /* Yay! No panic! */ + + reap_lwp_waiter(&lwp_data[0]); +} + +ATF_TC_WITH_CLEANUP(futex_wait_evil_unmapped_anon); +ATF_TC_HEAD(futex_wait_evil_unmapped_anon, tc) +{ + atf_tc_set_md_var(tc, "descr", + "tests futex WAIT while futex is unmapped - anon memory"); +} +ATF_TC_BODY(futex_wait_evil_unmapped_anon, tc) +{ + do_futex_wait_evil_unmapped(MAP_ANON); +} +ATF_TC_CLEANUP(futex_wait_evil_unmapped_anon, tc) +{ + signal(SIGUSR1, old_act); + do_cleanup(); +} + +/*****************************************************************************/ + +static int pri_min; +static int pri_max; + +static void +lowpri_simple_test_waiter_lwp(void *arg) +{ + struct lwp_data *d = arg; + struct sched_param sp; + int policy; + + d->threadid = _lwp_self(); + + ATF_REQUIRE(_sched_getparam(getpid(), d->threadid, &policy, &sp) == 0); + policy = SCHED_RR; + sp.sched_priority = pri_min; + ATF_REQUIRE(_sched_setparam(getpid(), d->threadid, policy, &sp) == 0); + + simple_test_waiter_lwp(arg); +} + +static void +highpri_simple_test_waiter_lwp(void *arg) +{ + struct lwp_data *d = arg; + struct sched_param sp; + int policy; + + d->threadid = _lwp_self(); + + ATF_REQUIRE(_sched_getparam(getpid(), d->threadid, &policy, &sp) == 0); + policy = SCHED_RR; + sp.sched_priority = pri_max; + ATF_REQUIRE(_sched_setparam(getpid(), d->threadid, policy, &sp) == 0); + + simple_test_waiter_lwp(arg); +} + +static void +do_test_wake_highest_pri(void) +{ + lwpid_t waiter; + int tries; + long pri; + + ATF_REQUIRE((pri = sysconf(_SC_SCHED_PRI_MIN)) != -1); + pri_min = (int)pri; + ATF_REQUIRE((pri = sysconf(_SC_SCHED_PRI_MAX)) != -1); + pri_max = (int)pri; + + futex_word = 0; + membar_sync(); + + setup_lwp_context(&lwp_data[0], lowpri_simple_test_waiter_lwp); + lwp_data[0].op_flags = FUTEX_PRIVATE_FLAG; + lwp_data[0].futex_error = -1; + lwp_data[0].futex_ptr = &futex_word; + lwp_data[0].block_val = 0; + lwp_data[0].bitset = 0; + lwp_data[0].wait_op = FUTEX_WAIT; + ATF_REQUIRE(_lwp_create(&lwp_data[0].context, 0, + &lwp_data[0].lwpid) == 0); + + for (tries = 0; tries < 5; tries++) { + membar_sync(); + if (nlwps_running == 1) + break; + sleep(1); + } + membar_sync(); + ATF_REQUIRE_EQ_MSG(nlwps_running, 1, "lowpri waiter failed to start"); + + /* Ensure it's blocked. */ + ATF_REQUIRE(lwp_data[0].futex_error == -1); + + setup_lwp_context(&lwp_data[1], highpri_simple_test_waiter_lwp); + lwp_data[1].op_flags = FUTEX_PRIVATE_FLAG; + lwp_data[1].futex_error = -1; + lwp_data[1].futex_ptr = &futex_word; + lwp_data[1].block_val = 0; + lwp_data[1].bitset = 0; + lwp_data[1].wait_op = FUTEX_WAIT; + ATF_REQUIRE(_lwp_create(&lwp_data[1].context, 0, + &lwp_data[1].lwpid) == 0); + + for (tries = 0; tries < 5; tries++) { + membar_sync(); + if (nlwps_running == 2) + break; + sleep(1); + } + membar_sync(); + ATF_REQUIRE_EQ_MSG(nlwps_running, 2, "highpri waiter failed to start"); + + /* Ensure it's blocked. */ + ATF_REQUIRE(lwp_data[1].futex_error == -1); + + /* Wake the first LWP. We should get the highpri thread. */ + ATF_REQUIRE(__futex(&futex_word, FUTEX_WAKE | FUTEX_PRIVATE_FLAG, + 1, NULL, NULL, 0, 0) == 1); + sleep(1); + for (tries = 0; tries < 5; tries++) { + membar_sync(); + if (nlwps_running == 1) + break; + sleep(1); + } + membar_sync(); + ATF_REQUIRE(nlwps_running == 1); + ATF_REQUIRE(_lwp_wait(0, &waiter) == 0); + ATF_REQUIRE(waiter == lwp_data[1].threadid); + + /* Wake the second LWP. We should get the lowpri thread. */ + ATF_REQUIRE(__futex(&futex_word, FUTEX_WAKE | FUTEX_PRIVATE_FLAG, + 1, NULL, NULL, 0, 0) == 1); + sleep(1); + for (tries = 0; tries < 5; tries++) { + membar_sync(); + if (nlwps_running == 0) + break; + sleep(1); + } + membar_sync(); + ATF_REQUIRE(nlwps_running == 0); + ATF_REQUIRE(_lwp_wait(0, &waiter) == 0); + ATF_REQUIRE(waiter == lwp_data[0].threadid); +} + +ATF_TC_WITH_CLEANUP(futex_wake_highest_pri); +ATF_TC_HEAD(futex_wake_highest_pri, tc) +{ + atf_tc_set_md_var(tc, "descr", + "tests that futex WAKE wakes the highest priority waiter"); + atf_tc_set_md_var(tc, "require.user", "root"); +} +ATF_TC_BODY(futex_wake_highest_pri, tc) +{ + atf_tc_expect_fail("PR kern/55230"); + do_test_wake_highest_pri(); +} +ATF_TC_CLEANUP(futex_wake_highest_pri, tc) +{ + do_cleanup(); +} + +/*****************************************************************************/ + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, futex_basic_wait_wake_private); + ATF_TP_ADD_TC(tp, futex_basic_wait_wake_shared); + ATF_TP_ADD_TC(tp, futex_wait_wake_anon_bs_private); + ATF_TP_ADD_TC(tp, futex_wait_wake_anon_bs_shared); + ATF_TP_ADD_TC(tp, futex_wait_wake_file_bs_private); + ATF_TP_ADD_TC(tp, futex_wait_wake_file_bs_shared); + ATF_TP_ADD_TC(tp, futex_wait_wake_file_bs_cow_private); + ATF_TP_ADD_TC(tp, futex_wait_wake_file_bs_cow_shared); + + ATF_TP_ADD_TC(tp, futex_wait_wake_anon_bs_shared_proc); + ATF_TP_ADD_TC(tp, futex_wait_wake_file_bs_shared_proc); + + ATF_TP_ADD_TC(tp, futex_wait_pointless_bitset); + ATF_TP_ADD_TC(tp, futex_wait_wake_bitset); + + ATF_TP_ADD_TC(tp, futex_wait_timeout_relative); + ATF_TP_ADD_TC(tp, futex_wait_timeout_relative_rt); + ATF_TP_ADD_TC(tp, futex_wait_timeout_deadline); + ATF_TP_ADD_TC(tp, futex_wait_timeout_deadline_rt); + + ATF_TP_ADD_TC(tp, futex_wait_evil_unmapped_anon); + + ATF_TP_ADD_TC(tp, futex_requeue); + ATF_TP_ADD_TC(tp, futex_cmp_requeue); + + ATF_TP_ADD_TC(tp, futex_wake_op_op); + ATF_TP_ADD_TC(tp, futex_wake_op_cmp); + + ATF_TP_ADD_TC(tp, futex_wake_highest_pri); + + return atf_no_error(); +} diff --git a/lib/libc/sys/t_futex_robust.c b/lib/libc/sys/t_futex_robust.c new file mode 100644 --- /dev/null +++ b/lib/libc/sys/t_futex_robust.c @@ -0,0 +1,406 @@ +/* $NetBSD: t_futex_robust.c,v 1.2 2020/05/01 01:44:30 thorpej Exp $ */ + +/*- + * Copyright (c) 2019 The NetBSD Foundation, Inc. + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +__COPYRIGHT("@(#) Copyright (c) 2019\ + The NetBSD Foundation, inc. All rights reserved."); +__RCSID("$NetBSD: t_futex_robust.c,v 1.2 2020/05/01 01:44:30 thorpej Exp $"); + +#include +#include +#include +#include +#include + +#include + +#include + +#define STACK_SIZE 65536 +#define NLOCKS 16 + +struct futex_lock_pos { + struct futex_robust_list list; + int fword; +}; +struct futex_lock_pos pos_locks[NLOCKS]; + +struct futex_lock_neg { + int fword; + struct futex_robust_list list; +}; +struct futex_lock_neg neg_locks[NLOCKS]; + +struct lwp_data { + ucontext_t context; + void *stack_base; + lwpid_t lwpid; + lwpid_t threadid; + struct futex_robust_list_head rhead; + + /* Results to be asserted by main thread. */ + bool set_robust_list_failed; +}; + +struct lwp_data lwp_data; + +static void +setup_lwp_context(void (*func)(void *)) +{ + + memset(&lwp_data, 0, sizeof(lwp_data)); + lwp_data.stack_base = mmap(NULL, STACK_SIZE, + PROT_READ | PROT_WRITE, + MAP_ANON | MAP_STACK | MAP_PRIVATE, -1, 0); + ATF_REQUIRE(lwp_data.stack_base != MAP_FAILED); + _lwp_makecontext(&lwp_data.context, func, + &lwp_data, NULL, lwp_data.stack_base, STACK_SIZE); + lwp_data.threadid = 0; +} + +static void +do_cleanup(void) +{ + if (lwp_data.stack_base != NULL && + lwp_data.stack_base != MAP_FAILED) { + (void) munmap(lwp_data.stack_base, STACK_SIZE); + } + memset(&lwp_data, 0, sizeof(lwp_data)); + memset(pos_locks, 0, sizeof(pos_locks)); + memset(neg_locks, 0, sizeof(neg_locks)); +} + +static void +test_pos_robust_list(void *arg) +{ + struct lwp_data *d = arg; + int i; + + d->rhead.list.next = &d->rhead.list; + d->rhead.futex_offset = offsetof(struct futex_lock_pos, fword) - + offsetof(struct futex_lock_pos, list); + d->rhead.pending_list = NULL; + + if (__futex_set_robust_list(&d->rhead, sizeof(d->rhead)) != 0) { + d->set_robust_list_failed = true; + _lwp_exit(); + } + + memset(pos_locks, 0, sizeof(pos_locks)); + + d->threadid = _lwp_self(); + + for (i = 0; i < NLOCKS-1; i++) { + pos_locks[i].fword = _lwp_self(); + pos_locks[i].list.next = d->rhead.list.next; + d->rhead.list.next = &pos_locks[i].list; + } + + pos_locks[i].fword = _lwp_self(); + d->rhead.pending_list = &pos_locks[i].list; + + _lwp_exit(); +} + +static void +test_neg_robust_list(void *arg) +{ + struct lwp_data *d = arg; + int i; + + d->rhead.list.next = &d->rhead.list; + d->rhead.futex_offset = offsetof(struct futex_lock_neg, fword) - + offsetof(struct futex_lock_neg, list); + d->rhead.pending_list = NULL; + + if (__futex_set_robust_list(&d->rhead, sizeof(d->rhead)) != 0) { + d->set_robust_list_failed = true; + _lwp_exit(); + } + + memset(neg_locks, 0, sizeof(neg_locks)); + + d->threadid = _lwp_self(); + + for (i = 0; i < NLOCKS-1; i++) { + neg_locks[i].fword = _lwp_self(); + neg_locks[i].list.next = d->rhead.list.next; + d->rhead.list.next = &neg_locks[i].list; + } + + neg_locks[i].fword = _lwp_self(); + d->rhead.pending_list = &neg_locks[i].list; + + _lwp_exit(); +} + +static void +test_unmapped_robust_list(void *arg) +{ + struct lwp_data *d = arg; + + d->rhead.list.next = &d->rhead.list; + d->rhead.futex_offset = offsetof(struct futex_lock_pos, fword) - + offsetof(struct futex_lock_pos, list); + d->rhead.pending_list = NULL; + + if (__futex_set_robust_list((void *)sizeof(d->rhead), + sizeof(d->rhead)) != 0) { + d->set_robust_list_failed = true; + _lwp_exit(); + } + + memset(pos_locks, 0, sizeof(pos_locks)); + + d->threadid = _lwp_self(); + + _lwp_exit(); +} + +static void +test_evil_circular_robust_list(void *arg) +{ + struct lwp_data *d = arg; + int i; + + d->rhead.list.next = &d->rhead.list; + d->rhead.futex_offset = offsetof(struct futex_lock_pos, fword) - + offsetof(struct futex_lock_pos, list); + d->rhead.pending_list = NULL; + + if (__futex_set_robust_list(&d->rhead, sizeof(d->rhead)) != 0) { + d->set_robust_list_failed = true; + _lwp_exit(); + } + + memset(pos_locks, 0, sizeof(pos_locks)); + + d->threadid = _lwp_self(); + + for (i = 0; i < NLOCKS; i++) { + pos_locks[i].fword = _lwp_self(); + pos_locks[i].list.next = d->rhead.list.next; + d->rhead.list.next = &pos_locks[i].list; + } + + /* Make a loop. */ + pos_locks[0].list.next = pos_locks[NLOCKS-1].list.next; + + _lwp_exit(); +} + +static void +test_bad_pending_robust_list(void *arg) +{ + struct lwp_data *d = arg; + int i; + + d->rhead.list.next = &d->rhead.list; + d->rhead.futex_offset = offsetof(struct futex_lock_pos, fword) - + offsetof(struct futex_lock_pos, list); + d->rhead.pending_list = NULL; + + if (__futex_set_robust_list(&d->rhead, sizeof(d->rhead)) != 0) { + d->set_robust_list_failed = true; + _lwp_exit(); + } + + memset(pos_locks, 0, sizeof(pos_locks)); + + d->threadid = _lwp_self(); + + for (i = 0; i < NLOCKS; i++) { + pos_locks[i].fword = _lwp_self(); + pos_locks[i].list.next = d->rhead.list.next; + d->rhead.list.next = &pos_locks[i].list; + } + + d->rhead.pending_list = (void *)sizeof(d->rhead); + + _lwp_exit(); +} + +ATF_TC_WITH_CLEANUP(futex_robust_positive); +ATF_TC_HEAD(futex_robust_positive, tc) +{ + atf_tc_set_md_var(tc, "descr", + "checks futex robust list with positive futex word offset"); +} + +ATF_TC_BODY(futex_robust_positive, tc) +{ + int i; + + setup_lwp_context(test_pos_robust_list); + + ATF_REQUIRE(_lwp_create(&lwp_data.context, 0, &lwp_data.lwpid) == 0); + ATF_REQUIRE(_lwp_wait(lwp_data.lwpid, NULL) == 0); + + ATF_REQUIRE(lwp_data.set_robust_list_failed == false); + + for (i = 0; i < NLOCKS; i++) { + ATF_REQUIRE((pos_locks[i].fword & FUTEX_TID_MASK) == + lwp_data.threadid); + ATF_REQUIRE((pos_locks[i].fword & FUTEX_OWNER_DIED) != 0); + } +} + +ATF_TC_CLEANUP(futex_robust_positive, tc) +{ + do_cleanup(); +} + +ATF_TC_WITH_CLEANUP(futex_robust_negative); +ATF_TC_HEAD(futex_robust_negative, tc) +{ + atf_tc_set_md_var(tc, "descr", + "checks futex robust list with negative futex word offset"); +} + +ATF_TC_BODY(futex_robust_negative, tc) +{ + int i; + + setup_lwp_context(test_neg_robust_list); + + ATF_REQUIRE(_lwp_create(&lwp_data.context, 0, &lwp_data.lwpid) == 0); + ATF_REQUIRE(_lwp_wait(lwp_data.lwpid, NULL) == 0); + + ATF_REQUIRE(lwp_data.set_robust_list_failed == false); + + for (i = 0; i < NLOCKS; i++) { + ATF_REQUIRE((neg_locks[i].fword & FUTEX_TID_MASK) == + lwp_data.threadid); + ATF_REQUIRE((neg_locks[i].fword & FUTEX_OWNER_DIED) != 0); + } +} + +ATF_TC_CLEANUP(futex_robust_negative, tc) +{ + do_cleanup(); +} + +ATF_TC_WITH_CLEANUP(futex_robust_unmapped); +ATF_TC_HEAD(futex_robust_unmapped, tc) +{ + atf_tc_set_md_var(tc, "descr", + "checks futex robust list with unmapped robust list pointer"); +} + +ATF_TC_BODY(futex_robust_unmapped, tc) +{ + + setup_lwp_context(test_unmapped_robust_list); + + ATF_REQUIRE(_lwp_create(&lwp_data.context, 0, &lwp_data.lwpid) == 0); + ATF_REQUIRE(_lwp_wait(lwp_data.lwpid, NULL) == 0); + + ATF_REQUIRE(lwp_data.set_robust_list_failed == false); + + /* + * No additional validation; just exercises a code path + * in the kernel. + */ +} + +ATF_TC_CLEANUP(futex_robust_unmapped, tc) +{ + do_cleanup(); +} + +ATF_TC_WITH_CLEANUP(futex_robust_evil_circular); +ATF_TC_HEAD(futex_robust_evil_circular, tc) +{ + atf_tc_set_md_var(tc, "descr", + "checks futex robust list processing faced with a deliberately " + "ciruclar list"); +} + +ATF_TC_BODY(futex_robust_evil_circular, tc) +{ + int i; + + setup_lwp_context(test_evil_circular_robust_list); + + ATF_REQUIRE(_lwp_create(&lwp_data.context, 0, &lwp_data.lwpid) == 0); + ATF_REQUIRE(_lwp_wait(lwp_data.lwpid, NULL) == 0); + + ATF_REQUIRE(lwp_data.set_robust_list_failed == false); + + for (i = 0; i < NLOCKS; i++) { + ATF_REQUIRE((pos_locks[i].fword & FUTEX_TID_MASK) == + lwp_data.threadid); + ATF_REQUIRE((pos_locks[i].fword & FUTEX_OWNER_DIED) != 0); + } +} + +ATF_TC_CLEANUP(futex_robust_evil_circular, tc) +{ + do_cleanup(); +} + +ATF_TC_WITH_CLEANUP(futex_robust_bad_pending); +ATF_TC_HEAD(futex_robust_bad_pending, tc) +{ + atf_tc_set_md_var(tc, "descr", + "checks futex robust list processing with a bad pending pointer"); +} + +ATF_TC_BODY(futex_robust_bad_pending, tc) +{ + int i; + + setup_lwp_context(test_bad_pending_robust_list); + + ATF_REQUIRE(_lwp_create(&lwp_data.context, 0, &lwp_data.lwpid) == 0); + ATF_REQUIRE(_lwp_wait(lwp_data.lwpid, NULL) == 0); + + ATF_REQUIRE(lwp_data.set_robust_list_failed == false); + + for (i = 0; i < NLOCKS; i++) { + ATF_REQUIRE((pos_locks[i].fword & FUTEX_TID_MASK) == + lwp_data.threadid); + ATF_REQUIRE((pos_locks[i].fword & FUTEX_OWNER_DIED) != 0); + } +} + +ATF_TC_CLEANUP(futex_robust_bad_pending, tc) +{ + do_cleanup(); +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, futex_robust_positive); + ATF_TP_ADD_TC(tp, futex_robust_negative); + ATF_TP_ADD_TC(tp, futex_robust_unmapped); + ATF_TP_ADD_TC(tp, futex_robust_evil_circular); + ATF_TP_ADD_TC(tp, futex_robust_bad_pending); + + return atf_no_error(); +} diff --git a/lib/libc/sys/t_getitimer.c b/lib/libc/sys/t_getitimer.c --- a/lib/libc/sys/t_getitimer.c +++ b/lib/libc/sys/t_getitimer.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_getitimer.c,v 1.2 2012/03/22 18:20:46 christos Exp $ */ +/* $NetBSD: t_getitimer.c,v 1.3 2019/07/13 12:44:02 gson Exp $ */ /*- * Copyright (c) 2011 The NetBSD Foundation, Inc. @@ -29,7 +29,7 @@ * POSSIBILITY OF SUCH DAMAGE. */ #include -__RCSID("$NetBSD: t_getitimer.c,v 1.2 2012/03/22 18:20:46 christos Exp $"); +__RCSID("$NetBSD: t_getitimer.c,v 1.3 2019/07/13 12:44:02 gson Exp $"); #include @@ -175,11 +175,13 @@ struct itimerval it, ot; /* - * Make two calls; the second one - * should store the old values. + * Make two calls; the second one should store the old + * timer value which should be the same as that set in + * the first call, or slightly less due to time passing + * between the two calls. */ it.it_value.tv_sec = 4; - it.it_value.tv_usec = 3; + it.it_value.tv_usec = 999999; it.it_interval.tv_sec = 0; it.it_interval.tv_usec = 0; @@ -194,7 +196,8 @@ ATF_REQUIRE(setitimer(ITIMER_REAL, &it, &ot) == 0); - if (ot.it_value.tv_sec != 4 || ot.it_value.tv_usec != 3) + /* Check seconds only as microseconds may have decremented */ + if (ot.it_value.tv_sec != 4) atf_tc_fail("setitimer(2) did not store old values"); } diff --git a/lib/libc/sys/t_getrandom.c b/lib/libc/sys/t_getrandom.c new file mode 100644 --- /dev/null +++ b/lib/libc/sys/t_getrandom.c @@ -0,0 +1,290 @@ +/* $NetBSD: t_getrandom.c,v 1.3 2020/08/25 01:37:38 riastradh Exp $ */ + +/*- + * Copyright (c) 2020 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Taylor R. Campbell. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +__RCSID("$NetBSD: t_getrandom.c,v 1.3 2020/08/25 01:37:38 riastradh Exp $"); + +#include + +#include +#include +#include +#include +#include + +static uint8_t buf[65536]; +static uint8_t zero24[24]; + +static void +alarm_handler(int signo) +{ + + fprintf(stderr, "timeout\n"); +} + +static void +install_alarm_handler(void) +{ + struct sigaction sa; + + memset(&sa, 0, sizeof sa); + sa.sa_handler = alarm_handler; + sigfillset(&sa.sa_mask); + sa.sa_flags = 0; /* no SA_RESTART */ + + ATF_CHECK_MSG(sigaction(SIGALRM, &sa, NULL) != -1, + "sigaction(SIGALRM): %s", strerror(errno)); +} + +/* + * Probability of spurious failure is 1/2^192 for each of the memcmps. + * As long as there are fewer than 2^64 of them, the probability of + * spurious failure is at most 1/2^128, which is low enough that we + * don't care about it. + */ + +ATF_TC(getrandom_default); +ATF_TC_HEAD(getrandom_default, tc) +{ + atf_tc_set_md_var(tc, "descr", "getrandom(..., 0)"); + atf_tc_set_md_var(tc, "timeout", "2"); +} +ATF_TC_BODY(getrandom_default, tc) +{ + ssize_t n; + + /* default */ + install_alarm_handler(); + alarm(1); + memset(buf, 0, sizeof buf); + n = getrandom(buf, sizeof buf, 0); + if (n == -1) { + ATF_CHECK_EQ(errno, EINTR); + } else { + ATF_CHECK_EQ((size_t)n, sizeof buf); + ATF_CHECK(memcmp(buf, zero24, 24) != 0); + ATF_CHECK(memcmp(buf + sizeof buf - 24, zero24, 24) != 0); + } + alarm(0); +} + +ATF_TC(getrandom_nonblock); +ATF_TC_HEAD(getrandom_nonblock, tc) +{ + atf_tc_set_md_var(tc, "descr", "getrandom(..., GRND_NONBLOCK)"); +} +ATF_TC_BODY(getrandom_nonblock, tc) +{ + ssize_t n; + + /* default, nonblocking */ + memset(buf, 0, sizeof buf); + n = getrandom(buf, sizeof buf, GRND_NONBLOCK); + if (n == -1) { + ATF_CHECK_EQ(errno, EAGAIN); + } else { + ATF_CHECK_EQ((size_t)n, sizeof buf); + ATF_CHECK(memcmp(buf, zero24, 24) != 0); + ATF_CHECK(memcmp(buf + sizeof buf - 24, zero24, 24) != 0); + } +} + +ATF_TC(getrandom_insecure); +ATF_TC_HEAD(getrandom_insecure, tc) +{ + atf_tc_set_md_var(tc, "descr", "getrandom(..., GRND_INSECURE)"); +} +ATF_TC_BODY(getrandom_insecure, tc) +{ + ssize_t n; + + /* insecure */ + memset(buf, 0, sizeof buf); + n = getrandom(buf, sizeof buf, GRND_INSECURE); + ATF_CHECK(n != -1); + ATF_CHECK_EQ((size_t)n, sizeof buf); + ATF_CHECK(memcmp(buf, zero24, 24) != 0); + ATF_CHECK(memcmp(buf + sizeof buf - 24, zero24, 24) != 0); +} + +ATF_TC(getrandom_insecure_nonblock); +ATF_TC_HEAD(getrandom_insecure_nonblock, tc) +{ + atf_tc_set_md_var(tc, "descr", + "getrandom(..., GRND_INSECURE|GRND_NONBLOCK)"); +} +ATF_TC_BODY(getrandom_insecure_nonblock, tc) +{ + ssize_t n; + + /* insecure, nonblocking -- same as mere insecure */ + memset(buf, 0, sizeof buf); + n = getrandom(buf, sizeof buf, GRND_INSECURE|GRND_NONBLOCK); + ATF_CHECK(n != -1); + ATF_CHECK_EQ((size_t)n, sizeof buf); + ATF_CHECK(memcmp(buf, zero24, 24) != 0); + ATF_CHECK(memcmp(buf + sizeof buf - 24, zero24, 24) != 0); +} + +ATF_TC(getrandom_random); +ATF_TC_HEAD(getrandom_random, tc) +{ + atf_tc_set_md_var(tc, "descr", "getrandom(..., GRND_RANDOM)"); + atf_tc_set_md_var(tc, "timeout", "2"); +} +ATF_TC_BODY(getrandom_random, tc) +{ + ssize_t n; + + /* `random' (hokey) */ + install_alarm_handler(); + alarm(1); + memset(buf, 0, sizeof buf); + n = getrandom(buf, sizeof buf, GRND_RANDOM); + if (n == -1) { + ATF_CHECK_EQ(errno, EINTR); + } else { + ATF_CHECK(n != 0); + ATF_CHECK((size_t)n <= sizeof buf); + if ((size_t)n >= 24) { + ATF_CHECK(memcmp(buf, zero24, 24) != 0); + ATF_CHECK(memcmp(buf + n - 24, zero24, 24) != 0); + } + } + alarm(0); +} + +ATF_TC(getrandom_random_nonblock); +ATF_TC_HEAD(getrandom_random_nonblock, tc) +{ + atf_tc_set_md_var(tc, "descr", + "getrandom(..., GRND_RANDOM|GRND_NONBLOCK)"); +} +ATF_TC_BODY(getrandom_random_nonblock, tc) +{ + ssize_t n; + + /* `random' (hokey), nonblocking */ + memset(buf, 0, sizeof buf); + n = getrandom(buf, sizeof buf, GRND_RANDOM|GRND_NONBLOCK); + if (n == -1) { + ATF_CHECK_EQ(errno, EAGAIN); + } else { + ATF_CHECK(n != 0); + ATF_CHECK((size_t)n <= sizeof buf); + if ((size_t)n >= 24) { + ATF_CHECK(memcmp(buf, zero24, 24) != 0); + ATF_CHECK(memcmp(buf + n - 24, zero24, 24) != 0); + } + } +} + +ATF_TC(getrandom_random_insecure); +ATF_TC_HEAD(getrandom_random_insecure, tc) +{ + atf_tc_set_md_var(tc, "descr", + "getrandom(..., GRND_RANDOM|GRND_INSECURE)"); +} +ATF_TC_BODY(getrandom_random_insecure, tc) +{ + ssize_t n; + + /* random and insecure -- nonsensical */ + n = getrandom(buf, sizeof buf, GRND_RANDOM|GRND_INSECURE); + ATF_CHECK_EQ(n, -1); + ATF_CHECK_EQ(errno, EINVAL); +} + +ATF_TC(getrandom_random_insecure_nonblock); +ATF_TC_HEAD(getrandom_random_insecure_nonblock, tc) +{ + atf_tc_set_md_var(tc, "descr", + "getrandom(..., GRND_RANDOM|GRND_INSECURE|GRND_NONBLOCK)"); +} +ATF_TC_BODY(getrandom_random_insecure_nonblock, tc) +{ + ssize_t n; + + /* random and insecure, nonblocking -- nonsensical */ + n = getrandom(buf, sizeof buf, + GRND_RANDOM|GRND_INSECURE|GRND_NONBLOCK); + ATF_CHECK_EQ(n, -1); + ATF_CHECK_EQ(errno, EINVAL); +} + +ATF_TC(getrandom_invalid); +ATF_TC_HEAD(getrandom_invalid, tc) +{ + atf_tc_set_md_var(tc, "descr", "getrandom(..., )"); +} +ATF_TC_BODY(getrandom_invalid, tc) +{ + ssize_t n; + + /* invalid flags */ + __CTASSERT(~(GRND_RANDOM|GRND_INSECURE|GRND_NONBLOCK)); + n = getrandom(buf, sizeof buf, + ~(GRND_RANDOM|GRND_INSECURE|GRND_NONBLOCK)); + ATF_CHECK_EQ(n, -1); + ATF_CHECK_EQ(errno, EINVAL); +} + +ATF_TC(getrandom_fault); +ATF_TC_HEAD(getrandom_fault, tc) +{ + atf_tc_set_md_var(tc, "descr", "getrandom(NULL, ...)"); +} +ATF_TC_BODY(getrandom_fault, tc) +{ + ssize_t n; + + /* unmapped */ + n = getrandom(NULL, sizeof buf, GRND_INSECURE|GRND_NONBLOCK); + ATF_CHECK_EQ(n, -1); + ATF_CHECK_EQ(errno, EFAULT); +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, getrandom_default); + ATF_TP_ADD_TC(tp, getrandom_nonblock); + ATF_TP_ADD_TC(tp, getrandom_insecure); + ATF_TP_ADD_TC(tp, getrandom_insecure_nonblock); + ATF_TP_ADD_TC(tp, getrandom_random); + ATF_TP_ADD_TC(tp, getrandom_random_nonblock); + ATF_TP_ADD_TC(tp, getrandom_random_insecure); + ATF_TP_ADD_TC(tp, getrandom_random_insecure_nonblock); + ATF_TP_ADD_TC(tp, getrandom_invalid); + ATF_TP_ADD_TC(tp, getrandom_fault); + + return atf_no_error(); +} diff --git a/lib/libc/sys/t_getrusage.c b/lib/libc/sys/t_getrusage.c --- a/lib/libc/sys/t_getrusage.c +++ b/lib/libc/sys/t_getrusage.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_getrusage.c,v 1.5 2017/01/13 20:31:06 christos Exp $ */ +/* $NetBSD: t_getrusage.c,v 1.8 2018/05/09 08:45:03 mrg Exp $ */ /*- * Copyright (c) 2011 The NetBSD Foundation, Inc. @@ -29,17 +29,22 @@ * POSSIBILITY OF SUCH DAMAGE. */ #include -__RCSID("$NetBSD: t_getrusage.c,v 1.5 2017/01/13 20:31:06 christos Exp $"); +__RCSID("$NetBSD: t_getrusage.c,v 1.8 2018/05/09 08:45:03 mrg Exp $"); #include #include #include +#include #include #include #include #include +#include #include +#include +#include +#include static void work(void); static void sighandler(int); @@ -117,6 +122,74 @@ atf_tc_fail("getrusage(2) did not record signals"); } +ATF_TC(getrusage_maxrss); +ATF_TC_HEAD(getrusage_maxrss, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test maxrss growing with getrusage(2)"); +} + +ATF_TC_BODY(getrusage_maxrss, tc) +{ + struct rusage ru; + long maxrss; + int i, fd; + +#define DUMP_FILE "dump" + + fd = open(DUMP_FILE, O_WRONLY|O_CREAT|O_TRUNC, 0222); + ATF_REQUIRE(fd != -1); + + (void)memset(&ru, 0, sizeof(struct rusage)); + ATF_REQUIRE(getrusage(RUSAGE_SELF, &ru) == 0); + maxrss = ru.ru_maxrss; + +#define CHUNK (1024 * 1024) + for (i = 0; i < 40; i++) { + void *p = malloc(CHUNK); + memset(p, 0, CHUNK); + write(fd, p, CHUNK); + } + close(fd); + unlink(DUMP_FILE); + + ATF_REQUIRE(getrusage(RUSAGE_SELF, &ru) == 0); + ATF_REQUIRE_MSG(maxrss < ru.ru_maxrss, + "maxrss: %ld, ru.ru_maxrss: %ld", maxrss, ru.ru_maxrss); +} + +ATF_TC(getrusage_msgsnd); +ATF_TC_HEAD(getrusage_msgsnd, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test send growing with getrusage(2)"); +} + +ATF_TC_BODY(getrusage_msgsnd, tc) +{ + struct rusage ru; + long msgsnd; + int s, i; + struct sockaddr_in sin; + + ATF_REQUIRE(getrusage(RUSAGE_SELF, &ru) == 0); + msgsnd = ru.ru_msgsnd; + + s = socket(AF_INET, SOCK_DGRAM, 0); + ATF_REQUIRE(s >= 0); + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_len = sizeof(sin); + sin.sin_addr.s_addr = ntohl(INADDR_LOOPBACK); + sin.sin_port = htons(3333); + + for (i = 0; i < 10; i++) + ATF_REQUIRE(sendto(s, &sin, sizeof(sin), 0, (void *)&sin, + (socklen_t)sizeof(sin)) != -1); + + ATF_REQUIRE(getrusage(RUSAGE_SELF, &ru) == 0); + ATF_REQUIRE(msgsnd + 10 == ru.ru_msgsnd); + close(s); +} + ATF_TC(getrusage_utime_back); ATF_TC_HEAD(getrusage_utime_back, tc) { @@ -192,6 +265,8 @@ ATF_TP_ADD_TC(tp, getrusage_err); ATF_TP_ADD_TC(tp, getrusage_sig); + ATF_TP_ADD_TC(tp, getrusage_maxrss); + ATF_TP_ADD_TC(tp, getrusage_msgsnd); ATF_TP_ADD_TC(tp, getrusage_utime_back); ATF_TP_ADD_TC(tp, getrusage_utime_zero); diff --git a/lib/libc/sys/t_kevent.c b/lib/libc/sys/t_kevent.c --- a/lib/libc/sys/t_kevent.c +++ b/lib/libc/sys/t_kevent.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_kevent.c,v 1.7 2015/02/05 13:55:37 isaki Exp $ */ +/* $NetBSD: t_kevent.c,v 1.9 2020/10/31 01:08:32 christos Exp $ */ /*- * Copyright (c) 2011 The NetBSD Foundation, Inc. @@ -29,7 +29,7 @@ * POSSIBILITY OF SUCH DAMAGE. */ #include -__RCSID("$NetBSD: t_kevent.c,v 1.7 2015/02/05 13:55:37 isaki Exp $"); +__RCSID("$NetBSD: t_kevent.c,v 1.9 2020/10/31 01:08:32 christos Exp $"); #include #include @@ -85,7 +85,7 @@ ATF_REQUIRE((kq = kqueue()) != -1); - // atf_tc_skip("crashes kernel (PR 46463)"); + // atf_tc_skip("crashes kernel (PR kern/46463)"); ATF_REQUIRE(socketpair(AF_LOCAL, SOCK_STREAM, 0, s) != -1); msg = malloc(CMSG_SPACE(sizeof(int))); @@ -172,8 +172,40 @@ ATF_REQUIRE_ERRNO(EOPNOTSUPP, true); (void)close(fd); + (void)close(kq); } +ATF_TC(kqueue_EVFILT_USER); +ATF_TC_HEAD(kqueue_EVFILT_USER, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks usability of EVFILT_USER"); +} + +ATF_TC_BODY(kqueue_EVFILT_USER, tc) +{ + /* mqueue and semaphore use fnullop_kqueue also */ + int kq; + struct kevent ev, rev; + + ATF_REQUIRE((kq = kqueue()) != -1); + + EV_SET(&ev, 666, EVFILT_USER, EV_ADD | EV_ENABLE, 0, 0, 0); + ATF_REQUIRE(kevent(kq, &ev, 1, NULL, 0, NULL) == 0); + EV_SET(&ev, 666, EVFILT_USER, 0, NOTE_FFCOPY | NOTE_TRIGGER | 8, 0, 0); + ATF_REQUIRE(kevent(kq, &ev, 1, NULL, 0, NULL) == 0); + const struct timespec timeout = { + .tv_sec = 1, + .tv_nsec = 0, + }; + + ATF_REQUIRE(kevent(kq, NULL, 0, &rev, 1, &timeout) == 1); + ATF_REQUIRE(rev.ident == 666); + ATF_REQUIRE(rev.filter == EVFILT_USER); + ATF_REQUIRE(rev.fflags == 8); + (void)close(kq); +} + + ATF_TP_ADD_TCS(tp) { @@ -181,6 +213,7 @@ ATF_TP_ADD_TC(tp, kevent_zerotimer); ATF_TP_ADD_TC(tp, kqueue_desc_passing); ATF_TP_ADD_TC(tp, kqueue_unsupported_fd); + ATF_TP_ADD_TC(tp, kqueue_EVFILT_USER); return atf_no_error(); } diff --git a/lib/libc/sys/t_listen.c b/lib/libc/sys/t_listen.c --- a/lib/libc/sys/t_listen.c +++ b/lib/libc/sys/t_listen.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_listen.c,v 1.5 2017/01/13 20:41:50 christos Exp $ */ +/* $NetBSD: t_listen.c,v 1.6 2019/07/09 16:24:01 maya Exp $ */ /* * Copyright (c) 2007 The NetBSD Foundation, Inc. * All rights reserved. @@ -132,5 +132,5 @@ ATF_TP_ADD_TC(tp, listen_err); ATF_TP_ADD_TC(tp, listen_low_port); - return 0; + return atf_no_error(); } diff --git a/lib/libc/sys/t_lwp_create.c b/lib/libc/sys/t_lwp_create.c --- a/lib/libc/sys/t_lwp_create.c +++ b/lib/libc/sys/t_lwp_create.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_lwp_create.c,v 1.2 2012/05/22 09:23:39 martin Exp $ */ +/* $NetBSD: t_lwp_create.c,v 1.4 2021/08/22 20:18:39 andvar Exp $ */ /*- * Copyright (c) 2012 The NetBSD Foundation, Inc. @@ -119,6 +119,30 @@ ATF_REQUIRE(lid == the_lwp_id); } +ATF_TC(lwp_create_bad_lid_ptr); +ATF_TC_HEAD(lwp_create_bad_lid_ptr, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify _lwp_create() fails as expected with bad lid pointer"); +} + +ATF_TC_BODY(lwp_create_bad_lid_ptr, tc) +{ + ucontext_t uc; + int error; + int serrno; + void *stack; + static const size_t ssize = 16*1024; + + stack = malloc(ssize); + _lwp_makecontext(&uc, lwp_main_func, NULL, NULL, stack, ssize); + + error = _lwp_create(&uc, 0, NULL); + serrno = errno; + ATF_REQUIRE(error == -1); + ATF_REQUIRE(serrno == EFAULT); +} + INVALID_UCONTEXT(generic, no_uc_cpu, "not setting cpu registers") uc->uc_flags &= ~_UC_CPU; } @@ -161,7 +185,7 @@ INVALID_UCONTEXT(i386, untouchable_eflags, "changing forbidden eflags") uc->uc_mcontext.__gregs[_REG_EFL] |= PSL_IOPL; } -INVALID_UCONTEXT(i386, priv_escalation, "modifying priviledge level") +INVALID_UCONTEXT(i386, priv_escalation, "modifying privilege level") uc->uc_mcontext.__gregs[_REG_CS] &= ~SEL_RPL; } #endif @@ -206,6 +230,7 @@ ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, lwp_create_works); + ATF_TP_ADD_TC(tp, lwp_create_bad_lid_ptr); ATF_TP_ADD_TC(tp, lwp_create_generic_fail_no_uc_cpu); #ifdef __alpha__ ATF_TP_ADD_TC(tp, lwp_create_alpha_fail_pslset); diff --git a/lib/libc/sys/t_mincore.c b/lib/libc/sys/t_mincore.c --- a/lib/libc/sys/t_mincore.c +++ b/lib/libc/sys/t_mincore.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_mincore.c,v 1.10 2017/01/14 20:51:13 christos Exp $ */ +/* $NetBSD: t_mincore.c,v 1.15 2020/02/24 12:20:30 rin Exp $ */ /*- * Copyright (c) 2011 The NetBSD Foundation, Inc. @@ -59,7 +59,7 @@ * POSSIBILITY OF SUCH DAMAGE. */ #include -__RCSID("$NetBSD: t_mincore.c,v 1.10 2017/01/14 20:51:13 christos Exp $"); +__RCSID("$NetBSD: t_mincore.c,v 1.15 2020/02/24 12:20:30 rin Exp $"); #include #include @@ -69,6 +69,7 @@ #include #include #include +#include #include #include #include @@ -79,6 +80,12 @@ static const char path[] = "mincore"; static size_t check_residency(void *, size_t); +#define ATF_REQUIRE_STRERROR(a) ATF_REQUIRE_MSG(a, " (%s)", strerror(errno)) + +#ifndef PROT_MPROTECT +# define PROT_MPROTECT(flags) (0) +#endif + static size_t check_residency(void *addr, size_t npgs) { @@ -96,7 +103,7 @@ resident++; #if 0 - (void)fprintf(stderr, "page 0x%p is %sresident\n", + (void)fprintf(stderr, "page %p is %sresident\n", (char *)addr + (i * page), vec[i] ? "" : "not "); #endif } @@ -229,7 +236,8 @@ resident, npgs); addr2 = mmap(NULL, npgs * page, PROT_READ, MAP_ANON, -1, (off_t)0); - addr3 = mmap(NULL, npgs * page, PROT_NONE, MAP_ANON, -1, (off_t)0); + addr3 = mmap(NULL, npgs * page, PROT_MPROTECT(PROT_READ) | PROT_NONE, + MAP_ANON, -1, (off_t)0); if (addr2 == MAP_FAILED || addr3 == MAP_FAILED) atf_tc_skip("could not mmap more anonymous test pages with " @@ -238,18 +246,18 @@ ATF_REQUIRE(check_residency(addr2, npgs) == npgs); ATF_REQUIRE(check_residency(addr3, npgs) == 0); - ATF_REQUIRE(mprotect(addr3, npgs * page, PROT_READ) == 0); + ATF_REQUIRE_STRERROR(mprotect(addr3, npgs * page, PROT_READ) == 0); ATF_REQUIRE(check_residency(addr, npgs) == npgs); ATF_REQUIRE(check_residency(addr2, npgs) == npgs); (void)munlockall(); - ATF_REQUIRE(madvise(addr2, npgs * page, MADV_FREE) == 0); + ATF_REQUIRE_STRERROR(madvise(addr2, npgs * page, MADV_FREE) == 0); ATF_REQUIRE(check_residency(addr2, npgs) == 0); (void)memset(addr, 0, npgs * page); - ATF_REQUIRE(madvise(addr, npgs * page, MADV_FREE) == 0); + ATF_REQUIRE_STRERROR(madvise(addr, npgs * page, MADV_FREE) == 0); ATF_REQUIRE(check_residency(addr, npgs) == 0); (void)munmap(addr, npgs * page); @@ -264,6 +272,33 @@ (void)unlink(path); } +static volatile int sig_caught; + +static void +sigsys_handler(int signum) +{ + + sig_caught = signum; +} + +static int +no_kernel_sysvmsg(void) +{ + int id; + void (*osig)(int); + + sig_caught = 0; + osig = signal(SIGSYS, sigsys_handler); + id = shmget(IPC_PRIVATE, page, IPC_CREAT | S_IRUSR | S_IWUSR); + if (sig_caught || id == -1) + return 1; + + (void)shmctl(id, IPC_RMID, 0); + (void)signal(SIGSYS, osig); + + return 0; +} + ATF_TC(mincore_shmseg); ATF_TC_HEAD(mincore_shmseg, tc) { @@ -276,20 +311,23 @@ void *addr = NULL; int shmid; + if (no_kernel_sysvmsg()) + atf_tc_skip("No SYSVSHM in kernel"); + shmid = shmget(IPC_PRIVATE, npgs * page, IPC_CREAT | S_IRUSR | S_IWUSR); - ATF_REQUIRE(shmid != -1); + ATF_REQUIRE_STRERROR(shmid != -1); addr = shmat(shmid, NULL, 0); - ATF_REQUIRE(addr != NULL); + ATF_REQUIRE_STRERROR(addr != NULL); ATF_REQUIRE(check_residency(addr, npgs) == 0); (void)memset(addr, 0xff, npgs * page); ATF_REQUIRE(check_residency(addr, npgs) == npgs); - ATF_REQUIRE(madvise(addr, npgs * page, MADV_FREE) == 0); + ATF_REQUIRE_STRERROR(madvise(addr, npgs * page, MADV_FREE) == 0); /* * NOTE! Even though we have MADV_FREE'd the range, @@ -308,8 +346,8 @@ (void)fprintf(stderr, "%zu pages still resident\n", npgs); - ATF_REQUIRE(shmdt(addr) == 0); - ATF_REQUIRE(shmctl(shmid, IPC_RMID, NULL) == 0); + ATF_REQUIRE_STRERROR(shmdt(addr) == 0); + ATF_REQUIRE_STRERROR(shmctl(shmid, IPC_RMID, NULL) == 0); } ATF_TP_ADD_TCS(tp) diff --git a/lib/libc/sys/t_mkfifo.c b/lib/libc/sys/t_mkfifo.c --- a/lib/libc/sys/t_mkfifo.c +++ b/lib/libc/sys/t_mkfifo.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_mkfifo.c,v 1.2 2011/11/02 06:04:48 jruoho Exp $ */ +/* $NetBSD: t_mkfifo.c,v 1.3 2019/06/20 03:31:54 kamil Exp $ */ /*- * Copyright (c) 2011 The NetBSD Foundation, Inc. @@ -29,7 +29,7 @@ * POSSIBILITY OF SUCH DAMAGE. */ #include -__RCSID("$NetBSD: t_mkfifo.c,v 1.2 2011/11/02 06:04:48 jruoho Exp $"); +__RCSID("$NetBSD: t_mkfifo.c,v 1.3 2019/06/20 03:31:54 kamil Exp $"); #include #include @@ -263,6 +263,34 @@ (void)unlink(path); } +ATF_TC_WITH_CLEANUP(mknod_s_ififo); +ATF_TC_HEAD(mknod_s_ififo, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test mknod(2) with S_IFIFO"); +} + +ATF_TC_BODY(mknod_s_ififo, tc) +{ + struct stat st; + + support(); + + (void)memset(&st, 0, sizeof(struct stat)); + + ATF_REQUIRE(mknod(path, S_IFIFO | 0600, 0) == 0); + ATF_REQUIRE(stat(path, &st) == 0); + + if (S_ISFIFO(st.st_mode) == 0) + atf_tc_fail("invalid mode from mknod(2) with S_IFIFO"); + + ATF_REQUIRE(unlink(path) == 0); +} + +ATF_TC_CLEANUP(mknod_s_ififo, tc) +{ + (void)unlink(path); +} + ATF_TP_ADD_TCS(tp) { @@ -271,6 +299,7 @@ ATF_TP_ADD_TC(tp, mkfifo_nonblock); ATF_TP_ADD_TC(tp, mkfifo_perm); ATF_TP_ADD_TC(tp, mkfifo_stat); + ATF_TP_ADD_TC(tp, mknod_s_ififo); return atf_no_error(); } diff --git a/lib/libc/sys/t_mlock.c b/lib/libc/sys/t_mlock.c --- a/lib/libc/sys/t_mlock.c +++ b/lib/libc/sys/t_mlock.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_mlock.c,v 1.6 2016/08/09 12:02:44 kre Exp $ */ +/* $NetBSD: t_mlock.c,v 1.8 2020/01/24 08:45:16 skrll Exp $ */ /*- * Copyright (c) 2012 The NetBSD Foundation, Inc. @@ -29,7 +29,7 @@ * POSSIBILITY OF SUCH DAMAGE. */ #include -__RCSID("$NetBSD: t_mlock.c,v 1.6 2016/08/09 12:02:44 kre Exp $"); +__RCSID("$NetBSD: t_mlock.c,v 1.8 2020/01/24 08:45:16 skrll Exp $"); #include #include @@ -41,6 +41,7 @@ #include #include #include +#include #include static long page = 0; @@ -55,16 +56,26 @@ ATF_TC_BODY(mlock_clip, tc) { void *buf; + int err1, err2; buf = malloc(page); ATF_REQUIRE(buf != NULL); + fprintf(stderr, "mlock_clip: buf = %p (page=%ld)\n", buf, page); if (page < 1024) atf_tc_skip("page size too small"); for (size_t i = page; i >= 1; i = i - 1024) { - (void)mlock(buf, page - i); - (void)munlock(buf, page - i); + err1 = mlock(buf, page - i); + if (err1 != 0) + fprintf(stderr, "mlock_clip: page=%ld i=%zu," + " mlock(%p, %ld): %s\n", page, i, buf, page - i, + strerror(errno)); + err2 = munlock(buf, page - i); + if (err2 != 0) + fprintf(stderr, "mlock_clip: page=%ld i=%zu," + " munlock(%p, %ld): %s (mlock %s)\n", page, i, + buf, page - i, strerror(errno), err1?"failed":"ok"); } free(buf); @@ -81,6 +92,7 @@ { void *invalid_ptr; void *buf; + int mlock_err, munlock_err; /* * Any bad address must return ENOMEM (for lock & unlock) @@ -105,6 +117,7 @@ buf = malloc(page); ATF_REQUIRE(buf != NULL); + fprintf(stderr, "mlock_err: buf = %p (page=%ld)\n", buf, page); /* * unlocking memory that is not locked is an error... @@ -116,8 +129,18 @@ /* * These are permitted to fail (EINVAL) but do not on NetBSD */ - ATF_REQUIRE(mlock((void *)(((uintptr_t)buf) + page/3), page/5) == 0); - ATF_REQUIRE(munlock((void *)(((uintptr_t)buf) + page/3), page/5) == 0); + mlock_err = mlock((void *)(((uintptr_t)buf) + page/3), page/5); + if (mlock_err != 0) + fprintf(stderr, "mlock_err: mlock(%p, %ld): %d [%d] %s\n", + (void *)(((uintptr_t)buf) + page/3), page/5, mlock_err, + errno, strerror(errno)); + ATF_REQUIRE(mlock_err == 0); + munlock_err= munlock((void *)(((uintptr_t)buf) + page/3), page/5); + if (munlock_err != 0) + fprintf(stderr, "mlock_err: munlock(%p, %ld): %d [%d] %s\n", + (void *)(((uintptr_t)buf) + page/3), page/5, munlock_err, + errno, strerror(errno)); + ATF_REQUIRE(munlock_err == 0); (void)free(buf); @@ -150,6 +173,7 @@ buf = malloc(page); ATF_REQUIRE(buf != NULL); + fprintf(stderr, "mlock_limits: buf = %p (page=%ld)\n", buf, page); pid = fork(); ATF_REQUIRE(pid >= 0); @@ -161,7 +185,7 @@ res.rlim_cur = i - 1; res.rlim_max = i - 1; - (void)fprintf(stderr, "trying to lock %zd bytes " + (void)fprintf(stderr, "trying to lock %zu bytes " "with %zu byte limit\n", i, (size_t)res.rlim_cur); if (setrlimit(RLIMIT_MEMLOCK, &res) != 0) @@ -169,7 +193,9 @@ errno = 0; - if (mlock(buf, i) != -1 || errno != EAGAIN) { + if ((sta = mlock(buf, i)) != -1 || errno != EAGAIN) { + fprintf(stderr, "mlock(%p, %zu): %d [%d] %s\n", + buf, i, sta, errno, strerror(errno)); (void)munlock(buf, i); _exit(EXIT_FAILURE); } @@ -203,20 +229,39 @@ */ buf = mmap(NULL, page, PROT_READ | PROT_WRITE, flags, -1, 0); + if (buf == MAP_FAILED) + fprintf(stderr, + "mlock_mmap: mmap(NULL, %ld, %#x, %#x, -1, 0): MAP_FAILED" + " [%d] %s\n", page, PROT_READ | PROT_WRITE, flags, errno, + strerror(errno)); + ATF_REQUIRE(buf != MAP_FAILED); + + fprintf(stderr, "mlock_mmap: buf=%p, page=%ld\n", buf, page); + ATF_REQUIRE(mlock(buf, page) == 0); ATF_REQUIRE(munlock(buf, page) == 0); ATF_REQUIRE(munmap(buf, page) == 0); ATF_REQUIRE(munlock(buf, page) != 0); + fprintf(stderr, "mlock_mmap: first test succeeded\n"); + /* * But it should be impossible to mlock(2) a PROT_NONE mapping. */ buf = mmap(NULL, page, PROT_NONE, flags, -1, 0); + if (buf == MAP_FAILED) + fprintf(stderr, + "mlock_mmap: mmap(NULL, %ld, %#x, %#x, -1, 0): MAP_FAILED" + " [%d] %s\n", page, PROT_NONE, flags, errno, + strerror(errno)); + ATF_REQUIRE(buf != MAP_FAILED); ATF_REQUIRE(mlock(buf, page) != 0); ATF_REQUIRE(munmap(buf, page) == 0); + + fprintf(stderr, "mlock_mmap: second test succeeded\n"); } ATF_TC(mlock_nested); @@ -230,14 +275,26 @@ { const size_t maxiter = 100; void *buf; + int err; buf = malloc(page); ATF_REQUIRE(buf != NULL); + fprintf(stderr, "mlock_nested: buf = %p (page=%ld)\n", buf, page); + + for (size_t i = 0; i < maxiter; i++) { + err = mlock(buf, page); + if (err != 0) + fprintf(stderr, + "mlock_nested: i=%zu (of %zu) mlock(%p, %ld): %d [%d] %s\n", + i, maxiter, buf, page, err, errno, strerror(errno)); + ATF_REQUIRE(err == 0); + } - for (size_t i = 0; i < maxiter; i++) - ATF_REQUIRE(mlock(buf, page) == 0); - - ATF_REQUIRE(munlock(buf, page) == 0); + err = munlock(buf, page); + if (err != 0) + fprintf(stderr, "mlock_nested: munlock(%p, %ld): %d [%d] %s\n", + buf, page, err, errno, strerror(errno)); + ATF_REQUIRE(err == 0); free(buf); } diff --git a/lib/libc/sys/t_mmap.c b/lib/libc/sys/t_mmap.c --- a/lib/libc/sys/t_mmap.c +++ b/lib/libc/sys/t_mmap.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_mmap.c,v 1.12 2017/01/16 16:31:05 christos Exp $ */ +/* $NetBSD: t_mmap.c,v 1.14 2020/06/26 07:50:11 jruoho Exp $ */ /*- * Copyright (c) 2011 The NetBSD Foundation, Inc. @@ -55,7 +55,7 @@ * SUCH DAMAGE. */ #include -__RCSID("$NetBSD: t_mmap.c,v 1.12 2017/01/16 16:31:05 christos Exp $"); +__RCSID("$NetBSD: t_mmap.c,v 1.14 2020/06/26 07:50:11 jruoho Exp $"); #include #include @@ -171,20 +171,26 @@ size_t len; int fd = -1; - atf_tc_skip("The test case causes a panic (PR kern/38889, kern/46592)"); + atf_tc_skip("The test case causes a panic " \ + "(PR kern/38889, PR kern/46592)"); ATF_REQUIRE(sysctl(mib, miblen, NULL, &len, NULL, 0) == 0); drives = malloc(len); ATF_REQUIRE(drives != NULL); ATF_REQUIRE(sysctl(mib, miblen, drives, &len, NULL, 0) == 0); for (dk = strtok(drives, " "); dk != NULL; dk = strtok(NULL, " ")) { - sprintf(dev, _PATH_DEV "%s%c", dk, 'a'+RAW_PART); + if (strncmp(dk, "dk", 2) == 0) + snprintf(dev, sizeof(dev), _PATH_DEV "%s", dk); + else + snprintf(dev, sizeof(dev), _PATH_DEV "%s%c", dk, + 'a' + RAW_PART); fprintf(stderr, "trying: %s\n", dev); if ((fd = open(dev, O_RDONLY)) >= 0) { (void)fprintf(stderr, "using %s\n", dev); break; - } + } else + (void)fprintf(stderr, "%s: %s\n", dev, strerror(errno)); } free(drives); @@ -192,7 +198,7 @@ atf_tc_skip("failed to find suitable block device"); map = mmap(NULL, 4096, PROT_READ, MAP_FILE, fd, 0); - ATF_REQUIRE(map != MAP_FAILED); + ATF_REQUIRE_MSG(map != MAP_FAILED, "mmap: %s", strerror(errno)); (void)fprintf(stderr, "first byte %x\n", *map); ATF_REQUIRE(close(fd) == 0); diff --git a/lib/libc/sys/t_mprotect.c b/lib/libc/sys/t_mprotect.c --- a/lib/libc/sys/t_mprotect.c +++ b/lib/libc/sys/t_mprotect.c @@ -1,7 +1,7 @@ -/* $NetBSD: t_mprotect.c,v 1.4 2016/05/28 14:34:49 christos Exp $ */ +/* $NetBSD: t_mprotect.c,v 1.9 2020/04/18 17:44:53 christos Exp $ */ /*- - * Copyright (c) 2011 The NetBSD Foundation, Inc. + * Copyright (c) 2011, 2020 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation @@ -29,7 +29,7 @@ * POSSIBILITY OF SUCH DAMAGE. */ #include -__RCSID("$NetBSD: t_mprotect.c,v 1.4 2016/05/28 14:34:49 christos Exp $"); +__RCSID("$NetBSD: t_mprotect.c,v 1.9 2020/04/18 17:44:53 christos Exp $"); #include #include @@ -45,15 +45,13 @@ #include #include "../common/exec_prot.h" +#include "t_mprotect_helper.h" static long page = 0; -static int pax_global = -1; -static int pax_enabled = -1; static char path[] = "mmap"; static void sighandler(int); static bool paxinit(void); -static bool paxset(int, int); static void sighandler(int signo) @@ -65,42 +63,18 @@ paxinit(void) { size_t len = sizeof(int); + int pax_flags; int rv; - rv = sysctlbyname("security.pax.mprotect.global", - &pax_global, &len, NULL, 0); + rv = sysctlbyname("proc.curproc.paxflags", + &pax_flags, &len, NULL, 0); if (rv != 0) return false; - rv = sysctlbyname("security.pax.mprotect.enabled", - &pax_enabled, &len, NULL, 0); - - return rv == 0; + return ((pax_flags & CTL_PROC_PAXFLAGS_MPROTECT) != 0); } -static bool -paxset(int global, int enabled) -{ - size_t len = sizeof(int); - int rv; - - rv = sysctlbyname("security.pax.mprotect.global", - NULL, NULL, &global, len); - - if (rv != 0) - return false; - - rv = sysctlbyname("security.pax.mprotect.enabled", - NULL, NULL, &enabled, len); - - if (rv != 0) - return false; - - return true; -} - - ATF_TC_WITH_CLEANUP(mprotect_access); ATF_TC_HEAD(mprotect_access, tc) { @@ -114,7 +88,7 @@ size_t i; int fd; - fd = open(path, O_RDONLY | O_CREAT); + fd = open(path, O_RDONLY | O_CREAT, 0600); ATF_REQUIRE(fd >= 0); /* @@ -188,12 +162,6 @@ break; } - if (!paxinit()) - return; - if (pax_enabled == 1 && pax_global == 1) - atf_tc_skip("PaX MPROTECT restrictions enabled"); - - /* * Map a page read/write and copy a trivial assembly function inside. * We will then change the mapping rights: @@ -203,7 +171,8 @@ * a SIGSEGV on architectures that can enforce --x permissions. */ - map = mmap(NULL, page, PROT_WRITE|PROT_READ, MAP_ANON, -1, 0); + map = mmap(NULL, page, PROT_MPROTECT(PROT_EXEC)|PROT_WRITE|PROT_READ, + MAP_ANON, -1, 0); ATF_REQUIRE(map != MAP_FAILED); memcpy(map, (void *)return_one, @@ -261,8 +230,8 @@ size_t i; int rv; - if (!paxinit() || !paxset(1, 1)) - return; + if (!paxinit()) + atf_tc_skip("PaX MPROTECT restrictions not enabled"); /* * As noted in the original PaX documentation [1], @@ -275,8 +244,8 @@ * (3) making a non-executable mapping executable * * (4) making an executable/read-only file mapping - * writable except for performing relocations - * on an ET_DYN ELF file (non-PIC shared library) + * writable except for performing relocations + * on an ET_DYN ELF file (non-PIC shared library) * * The following will test only the case (3). * @@ -302,9 +271,6 @@ } out: - if (pax_global != -1 && pax_enabled != -1) - (void)paxset(pax_global, pax_enabled); - if (str != NULL) atf_tc_fail("%s", str); } @@ -347,6 +313,131 @@ ATF_REQUIRE(munmap(map, page) == 0); } +ATF_TC(mprotect_mremap_exec); +ATF_TC_HEAD(mprotect_mremap_exec, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Test mremap(2)+mprotect(2) executable space protections"); +} + +/* + * Trivial function -- should fit into a page + */ +ATF_TC_BODY(mprotect_mremap_exec, tc) +{ + void *map, *map2; + pid_t pid; + int sta; + + /* + * Map a page read/write/exec and duplicate it. + * Map the copy executable. + * Copy a trivial assembly function to the writeable mapping. + * Try to execute it. This should never create a SIGSEGV. + */ + + map = mmap(NULL, page, PROT_MPROTECT(PROT_EXEC|PROT_WRITE|PROT_READ), + MAP_ANON, -1, 0); + ATF_REQUIRE(map != MAP_FAILED); + map2 = mremap(map, page, NULL, page, MAP_REMAPDUP); + ATF_REQUIRE(map2 != MAP_FAILED); + ATF_REQUIRE(mprotect(map, page, PROT_WRITE|PROT_READ) == 0); + ATF_REQUIRE(mprotect(map2, page, PROT_EXEC|PROT_READ) == 0); + + memcpy(map, (void *)return_one, + (uintptr_t)return_one_end - (uintptr_t)return_one); + __builtin___clear_cache(map, (void *)((uintptr_t)map + page)); + + ATF_REQUIRE(((int (*)(void))map2)() == 1); + + /* Double check that the executable mapping is not writeable. */ + pid = fork(); + ATF_REQUIRE(pid >= 0); + + if (pid == 0) { + ATF_REQUIRE(signal(SIGSEGV, sighandler) != SIG_ERR); + ATF_REQUIRE(strlcpy(map2, "XXX", 3) == 0); + } + + (void)wait(&sta); + + ATF_REQUIRE(WIFEXITED(sta) != 0); + ATF_REQUIRE(WEXITSTATUS(sta) == SIGSEGV); + + if (exec_prot_support() == PERPAGE_XP) { + /* Double check that the writeable mapping is not executable. */ + pid = fork(); + ATF_REQUIRE(pid >= 0); + + if (pid == 0) { + ATF_REQUIRE(signal(SIGSEGV, sighandler) != SIG_ERR); + ATF_REQUIRE(((int (*)(void))map)() == 1); + } + + (void)wait(&sta); + + ATF_REQUIRE(WIFEXITED(sta) != 0); + ATF_REQUIRE(WEXITSTATUS(sta) == SIGSEGV); + } + + ATF_REQUIRE(munmap(map, page) == 0); + ATF_REQUIRE(munmap(map2, page) == 0); +} + +ATF_TC(mprotect_mremap_fork_exec); +ATF_TC_HEAD(mprotect_mremap_fork_exec, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Test mremap(2)+fork(2)+mprotect(2) executable space protections"); +} + +ATF_TC_BODY(mprotect_mremap_fork_exec, tc) +{ + void *map, *map2; + pid_t pid; + + atf_tc_expect_fail("PR lib/55177"); + + /* + * Map a page read/write/exec and duplicate it. + * Map the copy executable. + * Copy a function to the writeable mapping and execute it + * Fork a child and wait for it + * Copy a different function to the writeable mapping and execute it + * The original function shouldn't be called + */ + + map = mmap(NULL, page, PROT_READ|PROT_WRITE|PROT_MPROTECT(PROT_EXEC), + MAP_ANON, -1, 0); + ATF_REQUIRE(map != MAP_FAILED); + map2 = mremap(map, page, NULL, page, MAP_REMAPDUP); + ATF_REQUIRE(map2 != MAP_FAILED); + ATF_REQUIRE(mprotect(map2, page, PROT_EXEC|PROT_READ) == 0); + + memcpy(map, (void *)return_1, + (uintptr_t)return_2 - (uintptr_t)return_1); + __builtin___clear_cache(map, (void *)((uintptr_t)map + page)); + + ATF_REQUIRE(((int (*)(void))map2)() == 1); + + pid = fork(); + ATF_REQUIRE(pid >= 0); + + if (pid == 0) + _exit(0); + + (void)wait(NULL); + + memcpy(map, (void *)return_2, + (uintptr_t)return_3 - (uintptr_t)return_2); + __builtin___clear_cache(map, (void *)((uintptr_t)map + page)); + + ATF_REQUIRE(((int (*)(void))map2)() == 2); + + ATF_REQUIRE(munmap(map, page) == 0); + ATF_REQUIRE(munmap(map2, page) == 0); +} + ATF_TP_ADD_TCS(tp) { page = sysconf(_SC_PAGESIZE); @@ -357,6 +448,8 @@ ATF_TP_ADD_TC(tp, mprotect_exec); ATF_TP_ADD_TC(tp, mprotect_pax); ATF_TP_ADD_TC(tp, mprotect_write); + ATF_TP_ADD_TC(tp, mprotect_mremap_exec); + ATF_TP_ADD_TC(tp, mprotect_mremap_fork_exec); return atf_no_error(); } diff --git a/lib/libc/sys/t_mprotect_helper.h b/lib/libc/sys/t_mprotect_helper.h new file mode 100644 --- /dev/null +++ b/lib/libc/sys/t_mprotect_helper.h @@ -0,0 +1,3 @@ +int return_1(void); +int return_2(void); +int return_3(void); diff --git a/lib/libc/sys/t_mprotect_helper.c b/lib/libc/sys/t_mprotect_helper.c new file mode 100644 --- /dev/null +++ b/lib/libc/sys/t_mprotect_helper.c @@ -0,0 +1,17 @@ +#include "t_mprotect_helper.h" + +int +return_1(void) +{ + return 1; +} +int +return_2(void) +{ + return 2; +} +int +return_3(void) +{ + return 3; +} diff --git a/lib/libc/sys/t_msgctl.c b/lib/libc/sys/t_msgctl.c --- a/lib/libc/sys/t_msgctl.c +++ b/lib/libc/sys/t_msgctl.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_msgctl.c,v 1.5 2017/01/13 20:44:45 christos Exp $ */ +/* $NetBSD: t_msgctl.c,v 1.7 2017/10/07 17:15:44 kre Exp $ */ /*- * Copyright (c) 2011 The NetBSD Foundation, Inc. @@ -29,7 +29,7 @@ * POSSIBILITY OF SUCH DAMAGE. */ #include -__RCSID("$NetBSD: t_msgctl.c,v 1.5 2017/01/13 20:44:45 christos Exp $"); +__RCSID("$NetBSD: t_msgctl.c,v 1.7 2017/10/07 17:15:44 kre Exp $"); #include #include @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -346,14 +347,55 @@ clean(); } +static volatile int sig_caught; + +static void +sigsys_handler(int signum) +{ + + sig_caught = signum; +} + +static int +no_kernel_sysvmsg(void) +{ + int id; + void (*osig)(int); + + sig_caught = 0; + osig = signal(SIGSYS, sigsys_handler); + id = msgget(MSG_KEY, IPC_CREAT | 0600); + if (sig_caught || id == -1) + return 1; + + (void)msgctl(id, IPC_RMID, 0); + (void)signal(SIGSYS, osig); + + return 0; +} + +ATF_TC(msgctl_query); +ATF_TC_HEAD(msgctl_query, tc) +{ + atf_tc_set_md_var(tc, "descr", "Skip msgctl_* tests - no SYSVMSG"); +} +ATF_TC_BODY(msgctl_query, tc) +{ + atf_tc_skip("No SYSVMSG in kernel"); +} + ATF_TP_ADD_TCS(tp) { - ATF_TP_ADD_TC(tp, msgctl_err); - ATF_TP_ADD_TC(tp, msgctl_perm); - ATF_TP_ADD_TC(tp, msgctl_pid); - ATF_TP_ADD_TC(tp, msgctl_set); - ATF_TP_ADD_TC(tp, msgctl_time); + if (no_kernel_sysvmsg()) { + ATF_TP_ADD_TC(tp, msgctl_query); + } else { + ATF_TP_ADD_TC(tp, msgctl_err); + ATF_TP_ADD_TC(tp, msgctl_perm); + ATF_TP_ADD_TC(tp, msgctl_pid); + ATF_TP_ADD_TC(tp, msgctl_set); + ATF_TP_ADD_TC(tp, msgctl_time); + } return atf_no_error(); } diff --git a/lib/libc/sys/t_msgget.c b/lib/libc/sys/t_msgget.c --- a/lib/libc/sys/t_msgget.c +++ b/lib/libc/sys/t_msgget.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_msgget.c,v 1.2 2014/02/27 00:59:50 joerg Exp $ */ +/* $NetBSD: t_msgget.c,v 1.3 2017/10/08 08:31:05 kre Exp $ */ /*- * Copyright (c) 2011 The NetBSD Foundation, Inc. @@ -29,7 +29,7 @@ * POSSIBILITY OF SUCH DAMAGE. */ #include -__RCSID("$NetBSD: t_msgget.c,v 1.2 2014/02/27 00:59:50 joerg Exp $"); +__RCSID("$NetBSD: t_msgget.c,v 1.3 2017/10/08 08:31:05 kre Exp $"); #include #include @@ -278,15 +278,55 @@ clean(); } +static volatile int sig_caught; + +static void +sigsys_handler(int signum) +{ + + sig_caught = signum; +} + +static int +no_kernel_sysvmsg(void) +{ + int id; + void (*osig)(int); + + sig_caught = 0; + osig = signal(SIGSYS, sigsys_handler); + id = msgget(MSG_KEY, IPC_CREAT | 0600); + if (sig_caught || id == -1) + return 1; + + (void)msgctl(id, IPC_RMID, 0); + (void)signal(SIGSYS, osig); + + return 0; +} + +ATF_TC(msgget_query); +ATF_TC_HEAD(msgget_query, tc) +{ + atf_tc_set_md_var(tc, "descr", "Skip msgget_* tests - no SYSVMSG"); +} +ATF_TC_BODY(msgget_query, tc) +{ + atf_tc_skip("No SYSVMSG in kernel"); +} ATF_TP_ADD_TCS(tp) { - ATF_TP_ADD_TC(tp, msgget_excl); - ATF_TP_ADD_TC(tp, msgget_exit); - ATF_TP_ADD_TC(tp, msgget_init); - ATF_TP_ADD_TC(tp, msgget_limit); - ATF_TP_ADD_TC(tp, msgget_mode); + if (no_kernel_sysvmsg()) { + ATF_TP_ADD_TC(tp, msgget_query); + } else { + ATF_TP_ADD_TC(tp, msgget_excl); + ATF_TP_ADD_TC(tp, msgget_exit); + ATF_TP_ADD_TC(tp, msgget_init); + ATF_TP_ADD_TC(tp, msgget_limit); + ATF_TP_ADD_TC(tp, msgget_mode); + } return atf_no_error(); } diff --git a/lib/libc/sys/t_msgrcv.c b/lib/libc/sys/t_msgrcv.c --- a/lib/libc/sys/t_msgrcv.c +++ b/lib/libc/sys/t_msgrcv.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_msgrcv.c,v 1.4 2017/01/13 20:44:45 christos Exp $ */ +/* $NetBSD: t_msgrcv.c,v 1.5 2017/10/08 08:31:05 kre Exp $ */ /*- * Copyright (c) 2011 The NetBSD Foundation, Inc. @@ -29,7 +29,7 @@ * POSSIBILITY OF SUCH DAMAGE. */ #include -__RCSID("$NetBSD: t_msgrcv.c,v 1.4 2017/01/13 20:44:45 christos Exp $"); +__RCSID("$NetBSD: t_msgrcv.c,v 1.5 2017/10/08 08:31:05 kre Exp $"); #include #include @@ -329,15 +329,56 @@ clean(); } +static volatile int sig_caught; + +static void +sigsys_handler(int signum) +{ + + sig_caught = signum; +} + +static int +no_kernel_sysvmsg(void) +{ + int id; + void (*osig)(int); + + sig_caught = 0; + osig = signal(SIGSYS, sigsys_handler); + id = msgget(MSG_KEY, IPC_CREAT | 0600); + if (sig_caught || id == -1) + return 1; + + (void)msgctl(id, IPC_RMID, 0); + (void)signal(SIGSYS, osig); + + return 0; +} + +ATF_TC(msgrcv_query); +ATF_TC_HEAD(msgrcv_query, tc) +{ + atf_tc_set_md_var(tc, "descr", "Skip msgrcv_* tests - no SYSVMSG"); +} +ATF_TC_BODY(msgrcv_query, tc) +{ + atf_tc_skip("No SYSVMSG in kernel"); +} + ATF_TP_ADD_TCS(tp) { - ATF_TP_ADD_TC(tp, msgrcv_basic); - ATF_TP_ADD_TC(tp, msgrcv_block); - ATF_TP_ADD_TC(tp, msgrcv_err); - ATF_TP_ADD_TC(tp, msgrcv_mtype); - ATF_TP_ADD_TC(tp, msgrcv_nonblock); - ATF_TP_ADD_TC(tp, msgrcv_truncate); + if (no_kernel_sysvmsg()) { + ATF_TP_ADD_TC(tp, msgrcv_query); + } else { + ATF_TP_ADD_TC(tp, msgrcv_basic); + ATF_TP_ADD_TC(tp, msgrcv_block); + ATF_TP_ADD_TC(tp, msgrcv_err); + ATF_TP_ADD_TC(tp, msgrcv_mtype); + ATF_TP_ADD_TC(tp, msgrcv_nonblock); + ATF_TP_ADD_TC(tp, msgrcv_truncate); + } return atf_no_error(); } diff --git a/lib/libc/sys/t_msgsnd.c b/lib/libc/sys/t_msgsnd.c --- a/lib/libc/sys/t_msgsnd.c +++ b/lib/libc/sys/t_msgsnd.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_msgsnd.c,v 1.3 2017/01/13 20:44:45 christos Exp $ */ +/* $NetBSD: t_msgsnd.c,v 1.4 2017/10/08 08:31:05 kre Exp $ */ /*- * Copyright (c) 2011 The NetBSD Foundation, Inc. @@ -29,7 +29,7 @@ * POSSIBILITY OF SUCH DAMAGE. */ #include -__RCSID("$NetBSD: t_msgsnd.c,v 1.3 2017/01/13 20:44:45 christos Exp $"); +__RCSID("$NetBSD: t_msgsnd.c,v 1.4 2017/10/08 08:31:05 kre Exp $"); #include #include @@ -326,14 +326,55 @@ clean(); } +static volatile int sig_caught; + +static void +sigsys_handler(int signum) +{ + + sig_caught = signum; +} + +static int +no_kernel_sysvmsg(void) +{ + int id; + void (*osig)(int); + + sig_caught = 0; + osig = signal(SIGSYS, sigsys_handler); + id = msgget(MSG_KEY, IPC_CREAT | 0600); + if (sig_caught || id == -1) + return 1; + + (void)msgctl(id, IPC_RMID, 0); + (void)signal(SIGSYS, osig); + + return 0; +} + +ATF_TC(msgsnd_query); +ATF_TC_HEAD(msgsnd_query, tc) +{ + atf_tc_set_md_var(tc, "descr", "Skip msgsnd_* tests - no SYSVMSG"); +} +ATF_TC_BODY(msgsnd_query, tc) +{ + atf_tc_skip("No SYSVMSG in kernel"); +} + ATF_TP_ADD_TCS(tp) { - ATF_TP_ADD_TC(tp, msgsnd_block); - ATF_TP_ADD_TC(tp, msgsnd_count); - ATF_TP_ADD_TC(tp, msgsnd_err); - ATF_TP_ADD_TC(tp, msgsnd_nonblock); - ATF_TP_ADD_TC(tp, msgsnd_perm); + if (no_kernel_sysvmsg()) { + ATF_TP_ADD_TC(tp, msgsnd_query); + } else { + ATF_TP_ADD_TC(tp, msgsnd_block); + ATF_TP_ADD_TC(tp, msgsnd_count); + ATF_TP_ADD_TC(tp, msgsnd_err); + ATF_TP_ADD_TC(tp, msgsnd_nonblock); + ATF_TP_ADD_TC(tp, msgsnd_perm); + } return atf_no_error(); } diff --git a/lib/libc/sys/t_pipe.c b/lib/libc/sys/t_pipe.c --- a/lib/libc/sys/t_pipe.c +++ b/lib/libc/sys/t_pipe.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_pipe.c,v 1.5 2017/01/13 21:30:41 christos Exp $ */ +/* $NetBSD: t_pipe.c,v 1.7 2020/06/26 07:50:11 jruoho Exp $ */ /*- * Copyright (c) 2001, 2008 The NetBSD Foundation, Inc. @@ -29,7 +29,7 @@ #include __COPYRIGHT("@(#) Copyright (c) 2008\ The NetBSD Foundation, inc. All rights reserved."); -__RCSID("$NetBSD: t_pipe.c,v 1.5 2017/01/13 21:30:41 christos Exp $"); +__RCSID("$NetBSD: t_pipe.c,v 1.7 2020/06/26 07:50:11 jruoho Exp $"); #include #include @@ -70,7 +70,7 @@ { atf_tc_set_md_var(tc, "descr", "Checks that writing to pipe " "works correctly after being interrupted and restarted " - "(kern/14087)"); + "(PR kern/14087)"); } ATF_TC_BODY(pipe_restart, tc) @@ -99,7 +99,7 @@ /* child */ RL(close(pp[1])); - /* Do inital write. This should succeed, make + /* Do initial write. This should succeed, make * the other side do partial write and wait for us to pick * rest up. */ diff --git a/lib/libc/sys/t_poll.c b/lib/libc/sys/t_poll.c --- a/lib/libc/sys/t_poll.c +++ b/lib/libc/sys/t_poll.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_poll.c,v 1.3 2012/03/18 07:00:52 jruoho Exp $ */ +/* $NetBSD: t_poll.c,v 1.4 2020/07/17 15:34:16 kamil Exp $ */ /*- * Copyright (c) 2011 The NetBSD Foundation, Inc. @@ -82,8 +82,8 @@ (void)printf("child3 exit\n"); } -ATF_TC(poll_3way); -ATF_TC_HEAD(poll_3way, tc) +ATF_TC(3way); +ATF_TC_HEAD(3way, tc) { atf_tc_set_md_var(tc, "timeout", "15"); atf_tc_set_md_var(tc, "descr", @@ -94,7 +94,7 @@ "both be awaken. (kern/17517)"); } -ATF_TC_BODY(poll_3way, tc) +ATF_TC_BODY(3way, tc) { int pf[2]; int status, i; @@ -145,15 +145,15 @@ (void)printf("parent terminated\n"); } -ATF_TC(poll_basic); -ATF_TC_HEAD(poll_basic, tc) +ATF_TC(basic); +ATF_TC_HEAD(basic, tc) { atf_tc_set_md_var(tc, "timeout", "10"); atf_tc_set_md_var(tc, "descr", "Basis functionality test for poll(2)"); } -ATF_TC_BODY(poll_basic, tc) +ATF_TC_BODY(basic, tc) { int fds[2]; struct pollfd pfds[2]; @@ -212,13 +212,13 @@ ATF_REQUIRE_EQ(close(fds[1]), 0); } -ATF_TC(poll_err); -ATF_TC_HEAD(poll_err, tc) +ATF_TC(err); +ATF_TC_HEAD(err, tc) { atf_tc_set_md_var(tc, "descr", "Check errors from poll(2)"); } -ATF_TC_BODY(poll_err, tc) +ATF_TC_BODY(err, tc) { struct pollfd pfd; int fd = 0; @@ -233,160 +233,12 @@ ATF_REQUIRE_ERRNO(EINVAL, poll(&pfd, 1, -2) == -1); } -ATF_TC(pollts_basic); -ATF_TC_HEAD(pollts_basic, tc) -{ - atf_tc_set_md_var(tc, "timeout", "10"); - atf_tc_set_md_var(tc, "descr", - "Basis functionality test for pollts(2)"); -} - -ATF_TC_BODY(pollts_basic, tc) -{ - int fds[2]; - struct pollfd pfds[2]; - struct timespec timeout; - int ret; - - ATF_REQUIRE_EQ(pipe(fds), 0); - - pfds[0].fd = fds[0]; - pfds[0].events = POLLIN; - pfds[1].fd = fds[1]; - pfds[1].events = POLLOUT; - - /* Use a timeout of 1 second. */ - timeout.tv_sec = 1; - timeout.tv_nsec = 0; - - /* - * Check that we get a timeout waiting for data on the read end - * of our pipe. - */ - pfds[0].revents = -1; - pfds[1].revents = -1; - ATF_REQUIRE_EQ_MSG(ret = pollts(&pfds[0], 1, &timeout, NULL), 0, - "got: %d", ret); - ATF_REQUIRE_EQ_MSG(pfds[0].revents, 0, "got: %d", pfds[0].revents); - ATF_REQUIRE_EQ_MSG(pfds[1].revents, -1, "got: %d", pfds[1].revents); - - /* Check that the write end of the pipe as reported as ready. */ - pfds[0].revents = -1; - pfds[1].revents = -1; - ATF_REQUIRE_EQ_MSG(ret = pollts(&pfds[1], 1, &timeout, NULL), 1, - "got: %d", ret); - ATF_REQUIRE_EQ_MSG(pfds[0].revents, -1, "got: %d", pfds[0].revents); - ATF_REQUIRE_EQ_MSG(pfds[1].revents, POLLOUT, "got: %d",\ - pfds[1].revents); - - /* Check that only the write end of the pipe as reported as ready. */ - pfds[0].revents = -1; - pfds[1].revents = -1; - ATF_REQUIRE_EQ_MSG(ret = pollts(pfds, 2, &timeout, NULL), 1, - "got: %d", ret); - ATF_REQUIRE_EQ_MSG(pfds[0].revents, 0, "got: %d", pfds[0].revents); - ATF_REQUIRE_EQ_MSG(pfds[1].revents, POLLOUT, "got: %d", - pfds[1].revents); - - /* Write data to our pipe. */ - ATF_REQUIRE_EQ(write(fds[1], "", 1), 1); - - /* Check that both ends of our pipe are reported as ready. */ - pfds[0].revents = -1; - pfds[1].revents = -1; - ATF_REQUIRE_EQ_MSG(ret = pollts(pfds, 2, &timeout, NULL), 2, - "got: %d", ret); - ATF_REQUIRE_EQ_MSG(pfds[0].revents, POLLIN, "got: %d", - pfds[0].revents); - ATF_REQUIRE_EQ_MSG(pfds[1].revents, POLLOUT, "got: %d", - pfds[1].revents); - - ATF_REQUIRE_EQ(close(fds[0]), 0); - ATF_REQUIRE_EQ(close(fds[1]), 0); -} - -ATF_TC(pollts_err); -ATF_TC_HEAD(pollts_err, tc) -{ - atf_tc_set_md_var(tc, "descr", "Check errors from pollts(2)"); -} - -ATF_TC_BODY(pollts_err, tc) -{ - struct timespec timeout; - struct pollfd pfd; - int fd = 0; - - pfd.fd = fd; - pfd.events = POLLIN; - - timeout.tv_sec = 1; - timeout.tv_nsec = 0; - - errno = 0; - ATF_REQUIRE_ERRNO(EFAULT, pollts((void *)-1, 1, &timeout, NULL) == -1); - - timeout.tv_sec = -1; - timeout.tv_nsec = -1; - - errno = 0; - ATF_REQUIRE_ERRNO(EINVAL, pollts(&pfd, 1, &timeout, NULL) == -1); -} - -ATF_TC(pollts_sigmask); -ATF_TC_HEAD(pollts_sigmask, tc) -{ - atf_tc_set_md_var(tc, "timeout", "10"); - atf_tc_set_md_var(tc, "descr", - "Check that pollts(2) restores the signal mask (PR kern/44986)"); -} - -ATF_TC_BODY(pollts_sigmask, tc) -{ - int fd; - struct pollfd pfd; - struct timespec timeout; - sigset_t mask; - int ret; - - fd = open(_PATH_DEVNULL, O_RDONLY); - ATF_REQUIRE(fd >= 0); - - pfd.fd = fd; - pfd.events = POLLIN; - - /* Use a timeout of 1 second. */ - timeout.tv_sec = 1; - timeout.tv_nsec = 0; - - /* Unblock all signals. */ - ATF_REQUIRE_EQ(sigfillset(&mask), 0); - ATF_REQUIRE_EQ(sigprocmask(SIG_UNBLOCK, &mask, NULL), 0); - - /* - * Check that pollts(2) immediately returns. We block *all* - * signals during pollts(2). - */ - ATF_REQUIRE_EQ_MSG(ret = pollts(&pfd, 1, &timeout, &mask), 1, - "got: %d", ret); - - /* Check that signals are now longer blocked. */ - ATF_REQUIRE_EQ(sigprocmask(SIG_SETMASK, NULL, &mask), 0); - ATF_REQUIRE_EQ_MSG(sigismember(&mask, SIGUSR1), 0, - "signal mask was changed."); - - ATF_REQUIRE_EQ(close(fd), 0); -} - ATF_TP_ADD_TCS(tp) { - ATF_TP_ADD_TC(tp, poll_3way); - ATF_TP_ADD_TC(tp, poll_basic); - ATF_TP_ADD_TC(tp, poll_err); - ATF_TP_ADD_TC(tp, pollts_basic); - ATF_TP_ADD_TC(tp, pollts_err); - ATF_TP_ADD_TC(tp, pollts_sigmask); + ATF_TP_ADD_TC(tp, 3way); + ATF_TP_ADD_TC(tp, basic); + ATF_TP_ADD_TC(tp, err); return atf_no_error(); } diff --git a/lib/libc/sys/t_pollts.c b/lib/libc/sys/t_pollts.c new file mode 100644 --- /dev/null +++ b/lib/libc/sys/t_pollts.c @@ -0,0 +1,201 @@ +/* $NetBSD: t_pollts.c,v 1.1 2020/07/17 15:34:17 kamil Exp $ */ + +/*- + * Copyright (c) 2011, 2020 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Matthias Scheler. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef POLLTS +#define POLLTS pollts +#endif + +ATF_TC(basic); +ATF_TC_HEAD(basic, tc) +{ + atf_tc_set_md_var(tc, "timeout", "10"); + atf_tc_set_md_var(tc, "descr", + "Basis functionality test for ppoll(2)/pollts(2)"); +} + +ATF_TC_BODY(basic, tc) +{ + int fds[2]; + struct pollfd pfds[2]; + struct timespec timeout; + int ret; + + ATF_REQUIRE_EQ(pipe(fds), 0); + + pfds[0].fd = fds[0]; + pfds[0].events = POLLIN; + pfds[1].fd = fds[1]; + pfds[1].events = POLLOUT; + + /* Use a timeout of 1 second. */ + timeout.tv_sec = 1; + timeout.tv_nsec = 0; + + /* + * Check that we get a timeout waiting for data on the read end + * of our pipe. + */ + pfds[0].revents = -1; + pfds[1].revents = -1; + ATF_REQUIRE_EQ_MSG(ret = POLLTS(&pfds[0], 1, &timeout, NULL), 0, + "got: %d", ret); + ATF_REQUIRE_EQ_MSG(pfds[0].revents, 0, "got: %d", pfds[0].revents); + ATF_REQUIRE_EQ_MSG(pfds[1].revents, -1, "got: %d", pfds[1].revents); + + /* Check that the write end of the pipe as reported as ready. */ + pfds[0].revents = -1; + pfds[1].revents = -1; + ATF_REQUIRE_EQ_MSG(ret = POLLTS(&pfds[1], 1, &timeout, NULL), 1, + "got: %d", ret); + ATF_REQUIRE_EQ_MSG(pfds[0].revents, -1, "got: %d", pfds[0].revents); + ATF_REQUIRE_EQ_MSG(pfds[1].revents, POLLOUT, "got: %d",\ + pfds[1].revents); + + /* Check that only the write end of the pipe as reported as ready. */ + pfds[0].revents = -1; + pfds[1].revents = -1; + ATF_REQUIRE_EQ_MSG(ret = POLLTS(pfds, 2, &timeout, NULL), 1, + "got: %d", ret); + ATF_REQUIRE_EQ_MSG(pfds[0].revents, 0, "got: %d", pfds[0].revents); + ATF_REQUIRE_EQ_MSG(pfds[1].revents, POLLOUT, "got: %d", + pfds[1].revents); + + /* Write data to our pipe. */ + ATF_REQUIRE_EQ(write(fds[1], "", 1), 1); + + /* Check that both ends of our pipe are reported as ready. */ + pfds[0].revents = -1; + pfds[1].revents = -1; + ATF_REQUIRE_EQ_MSG(ret = POLLTS(pfds, 2, &timeout, NULL), 2, + "got: %d", ret); + ATF_REQUIRE_EQ_MSG(pfds[0].revents, POLLIN, "got: %d", + pfds[0].revents); + ATF_REQUIRE_EQ_MSG(pfds[1].revents, POLLOUT, "got: %d", + pfds[1].revents); + + ATF_REQUIRE_EQ(close(fds[0]), 0); + ATF_REQUIRE_EQ(close(fds[1]), 0); +} + +ATF_TC(err); +ATF_TC_HEAD(err, tc) +{ + atf_tc_set_md_var(tc, "descr", "Check errors from ppoll(2)/pollts(2)"); +} + +ATF_TC_BODY(err, tc) +{ + struct timespec timeout; + struct pollfd pfd; + int fd = 0; + + pfd.fd = fd; + pfd.events = POLLIN; + + timeout.tv_sec = 1; + timeout.tv_nsec = 0; + + errno = 0; + ATF_REQUIRE_ERRNO(EFAULT, POLLTS((void *)-1, 1, &timeout, NULL) == -1); + + timeout.tv_sec = -1; + timeout.tv_nsec = -1; + + errno = 0; + ATF_REQUIRE_ERRNO(EINVAL, POLLTS(&pfd, 1, &timeout, NULL) == -1); +} + +ATF_TC(sigmask); +ATF_TC_HEAD(sigmask, tc) +{ + atf_tc_set_md_var(tc, "timeout", "10"); + atf_tc_set_md_var(tc, "descr", + "Check that ppoll(2)/pollts(2) restores the signal mask (PR kern/44986)"); +} + +ATF_TC_BODY(sigmask, tc) +{ + int fd; + struct pollfd pfd; + struct timespec timeout; + sigset_t mask; + int ret; + + fd = open(_PATH_DEVNULL, O_RDONLY); + ATF_REQUIRE(fd >= 0); + + pfd.fd = fd; + pfd.events = POLLIN; + + /* Use a timeout of 1 second. */ + timeout.tv_sec = 1; + timeout.tv_nsec = 0; + + /* Unblock all signals. */ + ATF_REQUIRE_EQ(sigfillset(&mask), 0); + ATF_REQUIRE_EQ(sigprocmask(SIG_UNBLOCK, &mask, NULL), 0); + + /* + * Check that ppoll(2)/pollts(2) immediately returns. We block *all* + * signals during ppoll(2)/pollts(2). + */ + ATF_REQUIRE_EQ_MSG(ret = POLLTS(&pfd, 1, &timeout, &mask), 1, + "got: %d", ret); + + /* Check that signals are now longer blocked. */ + ATF_REQUIRE_EQ(sigprocmask(SIG_SETMASK, NULL, &mask), 0); + ATF_REQUIRE_EQ_MSG(sigismember(&mask, SIGUSR1), 0, + "signal mask was changed."); + + ATF_REQUIRE_EQ(close(fd), 0); +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, basic); + ATF_TP_ADD_TC(tp, err); + ATF_TP_ADD_TC(tp, sigmask); + + return atf_no_error(); +} diff --git a/lib/libc/sys/t_posix_fadvise.c b/lib/libc/sys/t_posix_fadvise.c --- a/lib/libc/sys/t_posix_fadvise.c +++ b/lib/libc/sys/t_posix_fadvise.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_posix_fadvise.c,v 1.2 2017/01/13 21:30:41 christos Exp $ */ +/* $NetBSD: t_posix_fadvise.c,v 1.3 2018/06/19 09:20:46 gson Exp $ */ /*- * Copyright (c) 2008 The NetBSD Foundation, Inc. @@ -58,7 +58,7 @@ #include __COPYRIGHT("@(#) Copyright (c) 2008\ The NetBSD Foundation, inc. All rights reserved."); -__RCSID("$NetBSD: t_posix_fadvise.c,v 1.2 2017/01/13 21:30:41 christos Exp $"); +__RCSID("$NetBSD: t_posix_fadvise.c,v 1.3 2018/06/19 09:20:46 gson Exp $"); #include @@ -112,7 +112,7 @@ ATF_CHECK_EQ_MSG(ret = (x), exp, "got: %d", ret); \ ATF_CHECK_EQ_MSG(errno, 999, "got: %s", strerror(errno)); \ errno = save; \ - } while (0); + } while (0) CE(posix_fadvise(fd, 0, 0, -1), EINVAL); CE(posix_fadvise(pipe_fds[0], 0, 0, POSIX_FADV_NORMAL), ESPIPE); diff --git a/lib/libc/sys/t_ppoll.c b/lib/libc/sys/t_ppoll.c new file mode 100644 --- /dev/null +++ b/lib/libc/sys/t_ppoll.c @@ -0,0 +1,33 @@ +/* $NetBSD: t_ppoll.c,v 1.1 2020/07/17 15:34:17 kamil Exp $ */ + +/*- + * Copyright (c) 2011, 2020 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Matthias Scheler. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +#define POLLTS ppoll +#include "t_pollts.c" diff --git a/lib/libc/sys/t_ptrace.c b/lib/libc/sys/t_ptrace.c new file mode 100644 --- /dev/null +++ b/lib/libc/sys/t_ptrace.c @@ -0,0 +1,228 @@ +/* $NetBSD: t_ptrace.c,v 1.4 2018/05/14 12:44:40 kamil Exp $ */ + +/*- + * Copyright (c) 2016 The NetBSD Foundation, Inc. + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +__RCSID("$NetBSD: t_ptrace.c,v 1.4 2018/05/14 12:44:40 kamil Exp $"); + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "h_macros.h" + +/* + * A child process cannot call atf functions and expect them to magically + * work like in the parent. + * The printf(3) messaging from a child will not work out of the box as well + * without estabilishing a communication protocol with its parent. To not + * overcomplicate the tests - do not log from a child and use err(3)/errx(3) + * wrapped with FORKEE_ASSERT()/FORKEE_ASSERTX() as that is guaranteed to work. + */ +#define FORKEE_ASSERTX(x) \ +do { \ + int ret = (x); \ + if (!ret) \ + errx(EXIT_FAILURE, "%s:%d %s(): Assertion failed for: %s", \ + __FILE__, __LINE__, __func__, #x); \ +} while (0) + +#define FORKEE_ASSERT(x) \ +do { \ + int ret = (x); \ + if (!ret) \ + err(EXIT_FAILURE, "%s:%d %s(): Assertion failed for: %s", \ + __FILE__, __LINE__, __func__, #x); \ +} while (0) + +ATF_TC(attach_pid0); +ATF_TC_HEAD(attach_pid0, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Assert that a debugger cannot attach to PID 0"); +} + +ATF_TC_BODY(attach_pid0, tc) +{ + errno = 0; + ATF_REQUIRE_ERRNO(EPERM, ptrace(PT_ATTACH, 0, NULL, 0) == -1); +} + +ATF_TC(attach_pid1); +ATF_TC_HEAD(attach_pid1, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Assert that a debugger cannot attach to PID 1 (as non-root)"); + + atf_tc_set_md_var(tc, "require.user", "unprivileged"); +} + +ATF_TC_BODY(attach_pid1, tc) +{ + ATF_REQUIRE_ERRNO(EPERM, ptrace(PT_ATTACH, 1, NULL, 0) == -1); +} + +ATF_TC(attach_pid1_securelevel); +ATF_TC_HEAD(attach_pid1_securelevel, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Assert that a debugger cannot attach to PID 1 with " + "securelevel >= 0 (as root)"); + + atf_tc_set_md_var(tc, "require.user", "root"); +} + +ATF_TC_BODY(attach_pid1_securelevel, tc) +{ + int level; + size_t len = sizeof(level); + + ATF_REQUIRE(sysctlbyname("kern.securelevel", &level, &len, NULL, 0) + != -1); + + if (level < 0) { + atf_tc_skip("Test must be run with securelevel >= 0"); + } + + ATF_REQUIRE_ERRNO(EPERM, ptrace(PT_ATTACH, 1, NULL, 0) == -1); +} + +ATF_TC(attach_self); +ATF_TC_HEAD(attach_self, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Assert that a debugger cannot attach to self (as it's nonsense)"); +} + +ATF_TC_BODY(attach_self, tc) +{ + ATF_REQUIRE_ERRNO(EINVAL, ptrace(PT_ATTACH, getpid(), NULL, 0) == -1); +} + +ATF_TC(attach_chroot); +ATF_TC_HEAD(attach_chroot, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Assert that a debugger cannot trace another process unless the " + "process's root directory is at or below the tracing process's " + "root"); + + atf_tc_set_md_var(tc, "require.user", "root"); +} + +ATF_TC_BODY(attach_chroot, tc) +{ + char buf[PATH_MAX]; + pid_t child; + int fds_toparent[2], fds_fromparent[2]; + int rv; + uint8_t msg = 0xde; /* dummy message for IPC based on pipe(2) */ + + (void)memset(buf, '\0', sizeof(buf)); + ATF_REQUIRE(getcwd(buf, sizeof(buf)) != NULL); + (void)strlcat(buf, "/dir", sizeof(buf)); + + ATF_REQUIRE(mkdir(buf, 0500) == 0); + ATF_REQUIRE(chdir(buf) == 0); + + ATF_REQUIRE(pipe(fds_toparent) == 0); + ATF_REQUIRE(pipe(fds_fromparent) == 0); + child = atf_utils_fork(); + if (child == 0) { + FORKEE_ASSERT(close(fds_toparent[0]) == 0); + FORKEE_ASSERT(close(fds_fromparent[1]) == 0); + + FORKEE_ASSERT(chroot(buf) == 0); + + rv = write(fds_toparent[1], &msg, sizeof(msg)); + FORKEE_ASSERTX(rv == sizeof(msg)); + + ATF_REQUIRE_ERRNO(EPERM, + ptrace(PT_ATTACH, getppid(), NULL, 0) == -1); + + rv = read(fds_fromparent[0], &msg, sizeof(msg)); + FORKEE_ASSERTX(rv == sizeof(msg)); + + _exit(0); + } + ATF_REQUIRE(close(fds_toparent[1]) == 0); + ATF_REQUIRE(close(fds_fromparent[0]) == 0); + + printf("Waiting for chrooting of the child PID %d", child); + rv = read(fds_toparent[0], &msg, sizeof(msg)); + ATF_REQUIRE(rv == sizeof(msg)); + + printf("Child is ready, it will try to PT_ATTACH to parent\n"); + rv = write(fds_fromparent[1], &msg, sizeof(msg)); + ATF_REQUIRE(rv == sizeof(msg)); + + printf("fds_fromparent is no longer needed - close it\n"); + ATF_REQUIRE(close(fds_fromparent[1]) == 0); + + printf("fds_toparent is no longer needed - close it\n"); + ATF_REQUIRE(close(fds_toparent[0]) == 0); +} + +ATF_TC(traceme_twice); +ATF_TC_HEAD(traceme_twice, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Assert that a process cannot mark its parent a debugger twice"); +} + +ATF_TC_BODY(traceme_twice, tc) +{ + + printf("Mark the parent process (PID %d) a debugger of PID %d", + getppid(), getpid()); + ATF_REQUIRE(ptrace(PT_TRACE_ME, 0, NULL, 0) == 0); + + printf("Mark the parent process (PID %d) a debugger of PID %d again", + getppid(), getpid()); + ATF_REQUIRE_ERRNO(EBUSY, ptrace(PT_TRACE_ME, 0, NULL, 0) == -1); +} + +ATF_TP_ADD_TCS(tp) +{ + setvbuf(stdout, NULL, _IONBF, 0); + setvbuf(stderr, NULL, _IONBF, 0); + ATF_TP_ADD_TC(tp, attach_pid0); + ATF_TP_ADD_TC(tp, attach_pid1); + ATF_TP_ADD_TC(tp, attach_pid1_securelevel); + ATF_TP_ADD_TC(tp, attach_self); + ATF_TP_ADD_TC(tp, attach_chroot); + ATF_TP_ADD_TC(tp, traceme_twice); + + return atf_no_error(); +} diff --git a/lib/libc/sys/t_ptrace_amd64_wait.h b/lib/libc/sys/t_ptrace_amd64_wait.h new file mode 100644 --- /dev/null +++ b/lib/libc/sys/t_ptrace_amd64_wait.h @@ -0,0 +1,121 @@ +/* $NetBSD: t_ptrace_amd64_wait.h,v 1.12 2020/01/08 17:23:15 mgorny Exp $ */ + +/*- + * Copyright (c) 2016, 2017, 2018, 2019 The NetBSD Foundation, Inc. + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +#if defined(__x86_64__) + +/// ---------------------------------------------------------------------------- + +ATF_TC(x86_64_regs1); +ATF_TC_HEAD(x86_64_regs1, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Call PT_GETREGS and iterate over General Purpose registers"); +} + +ATF_TC_BODY(x86_64_regs1, tc) +{ + const int exitval = 5; + const int sigval = SIGSTOP; + pid_t child, wpid; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + struct reg r; + + DPRINTF("Before forking process PID=%d\n", getpid()); + SYSCALL_REQUIRE((child = fork()) != -1); + if (child == 0) { + DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + DPRINTF("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + DPRINTF("Before exiting of the child process\n"); + _exit(exitval); + } + DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + DPRINTF("Call GETREGS for the child process\n"); + SYSCALL_REQUIRE(ptrace(PT_GETREGS, child, &r, 0) != -1); + + DPRINTF("RAX=%#" PRIxREGISTER "\n", r.regs[_REG_RAX]); + DPRINTF("RBX=%#" PRIxREGISTER "\n", r.regs[_REG_RBX]); + DPRINTF("RCX=%#" PRIxREGISTER "\n", r.regs[_REG_RCX]); + DPRINTF("RDX=%#" PRIxREGISTER "\n", r.regs[_REG_RDX]); + + DPRINTF("RDI=%#" PRIxREGISTER "\n", r.regs[_REG_RDI]); + DPRINTF("RSI=%#" PRIxREGISTER "\n", r.regs[_REG_RSI]); + + DPRINTF("GS=%#" PRIxREGISTER "\n", r.regs[_REG_GS]); + DPRINTF("FS=%#" PRIxREGISTER "\n", r.regs[_REG_FS]); + DPRINTF("ES=%#" PRIxREGISTER "\n", r.regs[_REG_ES]); + DPRINTF("DS=%#" PRIxREGISTER "\n", r.regs[_REG_DS]); + DPRINTF("CS=%#" PRIxREGISTER "\n", r.regs[_REG_CS]); + DPRINTF("SS=%#" PRIxREGISTER "\n", r.regs[_REG_SS]); + + DPRINTF("RSP=%#" PRIxREGISTER "\n", r.regs[_REG_RSP]); + DPRINTF("RIP=%#" PRIxREGISTER "\n", r.regs[_REG_RIP]); + + DPRINTF("RFLAGS=%#" PRIxREGISTER "\n", r.regs[_REG_RFLAGS]); + + DPRINTF("R8=%#" PRIxREGISTER "\n", r.regs[_REG_R8]); + DPRINTF("R9=%#" PRIxREGISTER "\n", r.regs[_REG_R9]); + DPRINTF("R10=%#" PRIxREGISTER "\n", r.regs[_REG_R10]); + DPRINTF("R11=%#" PRIxREGISTER "\n", r.regs[_REG_R11]); + DPRINTF("R12=%#" PRIxREGISTER "\n", r.regs[_REG_R12]); + DPRINTF("R13=%#" PRIxREGISTER "\n", r.regs[_REG_R13]); + DPRINTF("R14=%#" PRIxREGISTER "\n", r.regs[_REG_R14]); + DPRINTF("R15=%#" PRIxREGISTER "\n", r.regs[_REG_R15]); + + DPRINTF("Before resuming the child process where it left off and " + "without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_exited(status, exitval); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} + +/// ---------------------------------------------------------------------------- + + +#define ATF_TP_ADD_TCS_PTRACE_WAIT_AMD64() \ + ATF_TP_ADD_TC_HAVE_GPREGS(tp, x86_64_regs1); +#else +#define ATF_TP_ADD_TCS_PTRACE_WAIT_AMD64() +#endif diff --git a/lib/libc/sys/t_ptrace_bytetransfer_wait.h b/lib/libc/sys/t_ptrace_bytetransfer_wait.h new file mode 100644 --- /dev/null +++ b/lib/libc/sys/t_ptrace_bytetransfer_wait.h @@ -0,0 +1,907 @@ +/* $NetBSD: t_ptrace_bytetransfer_wait.h,v 1.1 2020/05/04 22:05:28 kamil Exp $ */ + +/*- + * Copyright (c) 2016, 2017, 2018, 2019, 2020 The NetBSD Foundation, Inc. + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + + +enum bytes_transfer_type { + BYTES_TRANSFER_DATA, + BYTES_TRANSFER_DATAIO, + BYTES_TRANSFER_TEXT, + BYTES_TRANSFER_TEXTIO, + BYTES_TRANSFER_AUXV +}; + +static int __used +bytes_transfer_dummy(int a, int b, int c, int d) +{ + int e, f, g, h; + + a *= 4; + b += 3; + c -= 2; + d /= 1; + + e = strtol("10", NULL, 10); + f = strtol("20", NULL, 10); + g = strtol("30", NULL, 10); + h = strtol("40", NULL, 10); + + return (a + b * c - d) + (e * f - g / h); +} + +static void +bytes_transfer(int operation, size_t size, enum bytes_transfer_type type) +{ + const int exitval = 5; + const int sigval = SIGSTOP; + pid_t child, wpid; + bool skip = false; + + int lookup_me = 0; + uint8_t lookup_me8 = 0; + uint16_t lookup_me16 = 0; + uint32_t lookup_me32 = 0; + uint64_t lookup_me64 = 0; + + int magic = 0x13579246; + uint8_t magic8 = 0xab; + uint16_t magic16 = 0x1234; + uint32_t magic32 = 0x98765432; + uint64_t magic64 = 0xabcdef0123456789; + + struct ptrace_io_desc io; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + /* 513 is just enough, for the purposes of ATF it's good enough */ + AuxInfo ai[513], *aip; + + ATF_REQUIRE(size < sizeof(ai)); + + /* Prepare variables for .TEXT transfers */ + switch (type) { + case BYTES_TRANSFER_TEXT: + memcpy(&magic, bytes_transfer_dummy, sizeof(magic)); + break; + case BYTES_TRANSFER_TEXTIO: + switch (size) { + case 8: + memcpy(&magic8, bytes_transfer_dummy, sizeof(magic8)); + break; + case 16: + memcpy(&magic16, bytes_transfer_dummy, sizeof(magic16)); + break; + case 32: + memcpy(&magic32, bytes_transfer_dummy, sizeof(magic32)); + break; + case 64: + memcpy(&magic64, bytes_transfer_dummy, sizeof(magic64)); + break; + } + break; + default: + break; + } + + /* Prepare variables for PIOD and AUXV transfers */ + switch (type) { + case BYTES_TRANSFER_TEXTIO: + case BYTES_TRANSFER_DATAIO: + io.piod_op = operation; + switch (size) { + case 8: + io.piod_offs = (type == BYTES_TRANSFER_TEXTIO) ? + (void *)bytes_transfer_dummy : + &lookup_me8; + io.piod_addr = &lookup_me8; + io.piod_len = sizeof(lookup_me8); + break; + case 16: + io.piod_offs = (type == BYTES_TRANSFER_TEXTIO) ? + (void *)bytes_transfer_dummy : + &lookup_me16; + io.piod_addr = &lookup_me16; + io.piod_len = sizeof(lookup_me16); + break; + case 32: + io.piod_offs = (type == BYTES_TRANSFER_TEXTIO) ? + (void *)bytes_transfer_dummy : + &lookup_me32; + io.piod_addr = &lookup_me32; + io.piod_len = sizeof(lookup_me32); + break; + case 64: + io.piod_offs = (type == BYTES_TRANSFER_TEXTIO) ? + (void *)bytes_transfer_dummy : + &lookup_me64; + io.piod_addr = &lookup_me64; + io.piod_len = sizeof(lookup_me64); + break; + default: + break; + } + break; + case BYTES_TRANSFER_AUXV: + io.piod_op = operation; + io.piod_offs = 0; + io.piod_addr = ai; + io.piod_len = size; + break; + default: + break; + } + + DPRINTF("Before forking process PID=%d\n", getpid()); + SYSCALL_REQUIRE((child = fork()) != -1); + if (child == 0) { + DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + switch (type) { + case BYTES_TRANSFER_DATA: + switch (operation) { + case PT_READ_D: + case PT_READ_I: + lookup_me = magic; + break; + default: + break; + } + break; + case BYTES_TRANSFER_DATAIO: + switch (operation) { + case PIOD_READ_D: + case PIOD_READ_I: + switch (size) { + case 8: + lookup_me8 = magic8; + break; + case 16: + lookup_me16 = magic16; + break; + case 32: + lookup_me32 = magic32; + break; + case 64: + lookup_me64 = magic64; + break; + default: + break; + } + break; + default: + break; + } + default: + break; + } + + DPRINTF("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + /* Handle PIOD and PT separately as operation values overlap */ + switch (type) { + case BYTES_TRANSFER_DATA: + switch (operation) { + case PT_WRITE_D: + case PT_WRITE_I: + FORKEE_ASSERT_EQ(lookup_me, magic); + break; + default: + break; + } + break; + case BYTES_TRANSFER_DATAIO: + switch (operation) { + case PIOD_WRITE_D: + case PIOD_WRITE_I: + switch (size) { + case 8: + FORKEE_ASSERT_EQ(lookup_me8, magic8); + break; + case 16: + FORKEE_ASSERT_EQ(lookup_me16, magic16); + break; + case 32: + FORKEE_ASSERT_EQ(lookup_me32, magic32); + break; + case 64: + FORKEE_ASSERT_EQ(lookup_me64, magic64); + break; + default: + break; + } + break; + default: + break; + } + break; + case BYTES_TRANSFER_TEXT: + FORKEE_ASSERT(memcmp(&magic, bytes_transfer_dummy, + sizeof(magic)) == 0); + break; + case BYTES_TRANSFER_TEXTIO: + switch (size) { + case 8: + FORKEE_ASSERT(memcmp(&magic8, + bytes_transfer_dummy, + sizeof(magic8)) == 0); + break; + case 16: + FORKEE_ASSERT(memcmp(&magic16, + bytes_transfer_dummy, + sizeof(magic16)) == 0); + break; + case 32: + FORKEE_ASSERT(memcmp(&magic32, + bytes_transfer_dummy, + sizeof(magic32)) == 0); + break; + case 64: + FORKEE_ASSERT(memcmp(&magic64, + bytes_transfer_dummy, + sizeof(magic64)) == 0); + break; + } + break; + default: + break; + } + + DPRINTF("Before exiting of the child process\n"); + _exit(exitval); + } + DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + /* Check PaX MPROTECT */ + if (!can_we_write_to_text(child)) { + switch (type) { + case BYTES_TRANSFER_TEXTIO: + switch (operation) { + case PIOD_WRITE_D: + case PIOD_WRITE_I: + skip = true; + break; + default: + break; + } + break; + case BYTES_TRANSFER_TEXT: + switch (operation) { + case PT_WRITE_D: + case PT_WRITE_I: + skip = true; + break; + default: + break; + } + break; + default: + break; + } + } + + /* Bailout cleanly killing the child process */ + if (skip) { + SYSCALL_REQUIRE(ptrace(PT_KILL, child, (void *)1, 0) != -1); + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), + child); + + validate_status_signaled(status, SIGKILL, 0); + + atf_tc_skip("PaX MPROTECT setup prevents writes to .text"); + } + + DPRINTF("Calling operation to transfer bytes between child=%d and " + "parent=%d\n", child, getpid()); + + switch (type) { + case BYTES_TRANSFER_TEXTIO: + case BYTES_TRANSFER_DATAIO: + case BYTES_TRANSFER_AUXV: + switch (operation) { + case PIOD_WRITE_D: + case PIOD_WRITE_I: + switch (size) { + case 8: + lookup_me8 = magic8; + break; + case 16: + lookup_me16 = magic16; + break; + case 32: + lookup_me32 = magic32; + break; + case 64: + lookup_me64 = magic64; + break; + default: + break; + } + break; + default: + break; + } + SYSCALL_REQUIRE(ptrace(PT_IO, child, &io, 0) != -1); + switch (operation) { + case PIOD_READ_D: + case PIOD_READ_I: + switch (size) { + case 8: + ATF_REQUIRE_EQ(lookup_me8, magic8); + break; + case 16: + ATF_REQUIRE_EQ(lookup_me16, magic16); + break; + case 32: + ATF_REQUIRE_EQ(lookup_me32, magic32); + break; + case 64: + ATF_REQUIRE_EQ(lookup_me64, magic64); + break; + default: + break; + } + break; + case PIOD_READ_AUXV: + DPRINTF("Asserting that AUXV length (%zu) is > 0\n", + io.piod_len); + ATF_REQUIRE(io.piod_len > 0); + for (aip = ai; aip->a_type != AT_NULL; aip++) + DPRINTF("a_type=%#llx a_v=%#llx\n", + (long long int)aip->a_type, + (long long int)aip->a_v); + break; + default: + break; + } + break; + case BYTES_TRANSFER_TEXT: + switch (operation) { + case PT_READ_D: + case PT_READ_I: + errno = 0; + lookup_me = ptrace(operation, child, + bytes_transfer_dummy, 0); + ATF_REQUIRE_EQ(lookup_me, magic); + SYSCALL_REQUIRE_ERRNO(errno, 0); + break; + case PT_WRITE_D: + case PT_WRITE_I: + SYSCALL_REQUIRE(ptrace(operation, child, + bytes_transfer_dummy, magic) + != -1); + break; + default: + break; + } + break; + case BYTES_TRANSFER_DATA: + switch (operation) { + case PT_READ_D: + case PT_READ_I: + errno = 0; + lookup_me = ptrace(operation, child, &lookup_me, 0); + ATF_REQUIRE_EQ(lookup_me, magic); + SYSCALL_REQUIRE_ERRNO(errno, 0); + break; + case PT_WRITE_D: + case PT_WRITE_I: + lookup_me = magic; + SYSCALL_REQUIRE(ptrace(operation, child, &lookup_me, + magic) != -1); + break; + default: + break; + } + break; + default: + break; + } + + DPRINTF("Before resuming the child process where it left off and " + "without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_exited(status, exitval); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} + +#define BYTES_TRANSFER(test, operation, size, type) \ +ATF_TC(test); \ +ATF_TC_HEAD(test, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", \ + "Verify bytes transfer operation" #operation " and size " #size \ + " of type " #type); \ +} \ + \ +ATF_TC_BODY(test, tc) \ +{ \ + \ + bytes_transfer(operation, size, BYTES_TRANSFER_##type); \ +} + +// DATA + +BYTES_TRANSFER(bytes_transfer_piod_read_d_8, PIOD_READ_D, 8, DATAIO) +BYTES_TRANSFER(bytes_transfer_piod_read_d_16, PIOD_READ_D, 16, DATAIO) +BYTES_TRANSFER(bytes_transfer_piod_read_d_32, PIOD_READ_D, 32, DATAIO) +BYTES_TRANSFER(bytes_transfer_piod_read_d_64, PIOD_READ_D, 64, DATAIO) + +BYTES_TRANSFER(bytes_transfer_piod_read_i_8, PIOD_READ_I, 8, DATAIO) +BYTES_TRANSFER(bytes_transfer_piod_read_i_16, PIOD_READ_I, 16, DATAIO) +BYTES_TRANSFER(bytes_transfer_piod_read_i_32, PIOD_READ_I, 32, DATAIO) +BYTES_TRANSFER(bytes_transfer_piod_read_i_64, PIOD_READ_I, 64, DATAIO) + +BYTES_TRANSFER(bytes_transfer_piod_write_d_8, PIOD_WRITE_D, 8, DATAIO) +BYTES_TRANSFER(bytes_transfer_piod_write_d_16, PIOD_WRITE_D, 16, DATAIO) +BYTES_TRANSFER(bytes_transfer_piod_write_d_32, PIOD_WRITE_D, 32, DATAIO) +BYTES_TRANSFER(bytes_transfer_piod_write_d_64, PIOD_WRITE_D, 64, DATAIO) + +BYTES_TRANSFER(bytes_transfer_piod_write_i_8, PIOD_WRITE_I, 8, DATAIO) +BYTES_TRANSFER(bytes_transfer_piod_write_i_16, PIOD_WRITE_I, 16, DATAIO) +BYTES_TRANSFER(bytes_transfer_piod_write_i_32, PIOD_WRITE_I, 32, DATAIO) +BYTES_TRANSFER(bytes_transfer_piod_write_i_64, PIOD_WRITE_I, 64, DATAIO) + +BYTES_TRANSFER(bytes_transfer_read_d, PT_READ_D, 32, DATA) +BYTES_TRANSFER(bytes_transfer_read_i, PT_READ_I, 32, DATA) +BYTES_TRANSFER(bytes_transfer_write_d, PT_WRITE_D, 32, DATA) +BYTES_TRANSFER(bytes_transfer_write_i, PT_WRITE_I, 32, DATA) + +// TEXT + +BYTES_TRANSFER(bytes_transfer_piod_read_d_8_text, PIOD_READ_D, 8, TEXTIO) +BYTES_TRANSFER(bytes_transfer_piod_read_d_16_text, PIOD_READ_D, 16, TEXTIO) +BYTES_TRANSFER(bytes_transfer_piod_read_d_32_text, PIOD_READ_D, 32, TEXTIO) +BYTES_TRANSFER(bytes_transfer_piod_read_d_64_text, PIOD_READ_D, 64, TEXTIO) + +BYTES_TRANSFER(bytes_transfer_piod_read_i_8_text, PIOD_READ_I, 8, TEXTIO) +BYTES_TRANSFER(bytes_transfer_piod_read_i_16_text, PIOD_READ_I, 16, TEXTIO) +BYTES_TRANSFER(bytes_transfer_piod_read_i_32_text, PIOD_READ_I, 32, TEXTIO) +BYTES_TRANSFER(bytes_transfer_piod_read_i_64_text, PIOD_READ_I, 64, TEXTIO) + +BYTES_TRANSFER(bytes_transfer_piod_write_d_8_text, PIOD_WRITE_D, 8, TEXTIO) +BYTES_TRANSFER(bytes_transfer_piod_write_d_16_text, PIOD_WRITE_D, 16, TEXTIO) +BYTES_TRANSFER(bytes_transfer_piod_write_d_32_text, PIOD_WRITE_D, 32, TEXTIO) +BYTES_TRANSFER(bytes_transfer_piod_write_d_64_text, PIOD_WRITE_D, 64, TEXTIO) + +BYTES_TRANSFER(bytes_transfer_piod_write_i_8_text, PIOD_WRITE_I, 8, TEXTIO) +BYTES_TRANSFER(bytes_transfer_piod_write_i_16_text, PIOD_WRITE_I, 16, TEXTIO) +BYTES_TRANSFER(bytes_transfer_piod_write_i_32_text, PIOD_WRITE_I, 32, TEXTIO) +BYTES_TRANSFER(bytes_transfer_piod_write_i_64_text, PIOD_WRITE_I, 64, TEXTIO) + +BYTES_TRANSFER(bytes_transfer_read_d_text, PT_READ_D, 32, TEXT) +BYTES_TRANSFER(bytes_transfer_read_i_text, PT_READ_I, 32, TEXT) +BYTES_TRANSFER(bytes_transfer_write_d_text, PT_WRITE_D, 32, TEXT) +BYTES_TRANSFER(bytes_transfer_write_i_text, PT_WRITE_I, 32, TEXT) + +// AUXV + +BYTES_TRANSFER(bytes_transfer_piod_read_auxv, PIOD_READ_AUXV, 4096, AUXV) + +/// ---------------------------------------------------------------------------- + +static void +bytes_transfer_alignment(const char *operation) +{ + const int exitval = 5; + const int sigval = SIGSTOP; + pid_t child, wpid; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + char *buffer; + int vector; + size_t len; + size_t i; + int op; + + struct ptrace_io_desc io; + struct ptrace_siginfo info; + + memset(&io, 0, sizeof(io)); + memset(&info, 0, sizeof(info)); + + /* Testing misaligned byte transfer crossing page boundaries */ + len = sysconf(_SC_PAGESIZE) * 2; + buffer = malloc(len); + ATF_REQUIRE(buffer != NULL); + + /* Initialize the buffer with random data */ + for (i = 0; i < len; i++) + buffer[i] = i & 0xff; + + DPRINTF("Before forking process PID=%d\n", getpid()); + SYSCALL_REQUIRE((child = fork()) != -1); + if (child == 0) { + DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + DPRINTF("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + DPRINTF("Before exiting of the child process\n"); + _exit(exitval); + } + DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + DPRINTF("Before calling ptrace(2) with PT_GET_SIGINFO for child\n"); + SYSCALL_REQUIRE(ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) + != -1); + + DPRINTF("Signal traced to lwpid=%d\n", info.psi_lwpid); + DPRINTF("Signal properties: si_signo=%#x si_code=%#x " + "si_errno=%#x\n", + info.psi_siginfo.si_signo, info.psi_siginfo.si_code, + info.psi_siginfo.si_errno); + + ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, sigval); + ATF_REQUIRE_EQ(info.psi_siginfo.si_code, SI_LWP); + + if (strcmp(operation, "PT_READ_I") == 0 || + strcmp(operation, "PT_READ_D") == 0) { + if (strcmp(operation, "PT_READ_I")) + op = PT_READ_I; + else + op = PT_READ_D; + + for (i = 0; i <= (len - sizeof(int)); i++) { + errno = 0; + vector = ptrace(op, child, buffer + i, 0); + ATF_REQUIRE_EQ(errno, 0); + ATF_REQUIRE(!memcmp(&vector, buffer + i, sizeof(int))); + } + } else if (strcmp(operation, "PT_WRITE_I") == 0 || + strcmp(operation, "PT_WRITE_D") == 0) { + if (strcmp(operation, "PT_WRITE_I")) + op = PT_WRITE_I; + else + op = PT_WRITE_D; + + for (i = 0; i <= (len - sizeof(int)); i++) { + memcpy(&vector, buffer + i, sizeof(int)); + SYSCALL_REQUIRE(ptrace(op, child, buffer + 1, vector) + != -1); + } + } else if (strcmp(operation, "PIOD_READ_I") == 0 || + strcmp(operation, "PIOD_READ_D") == 0) { + if (strcmp(operation, "PIOD_READ_I")) + op = PIOD_READ_I; + else + op = PIOD_READ_D; + + io.piod_op = op; + io.piod_addr = &vector; + io.piod_len = sizeof(int); + + for (i = 0; i <= (len - sizeof(int)); i++) { + io.piod_offs = buffer + i; + + SYSCALL_REQUIRE(ptrace(PT_IO, child, &io, sizeof(io)) + != -1); + ATF_REQUIRE(!memcmp(&vector, buffer + i, sizeof(int))); + } + } else if (strcmp(operation, "PIOD_WRITE_I") == 0 || + strcmp(operation, "PIOD_WRITE_D") == 0) { + if (strcmp(operation, "PIOD_WRITE_I")) + op = PIOD_WRITE_I; + else + op = PIOD_WRITE_D; + + io.piod_op = op; + io.piod_addr = &vector; + io.piod_len = sizeof(int); + + for (i = 0; i <= (len - sizeof(int)); i++) { + io.piod_offs = buffer + i; + + SYSCALL_REQUIRE(ptrace(PT_IO, child, &io, sizeof(io)) + != -1); + } + } else if (strcmp(operation, "PIOD_READ_AUXV") == 0) { + io.piod_op = PIOD_READ_AUXV; + io.piod_addr = &vector; + io.piod_len = sizeof(int); + + errno = 0; + i = 0; + /* Read the whole AUXV vector, it has no clear length */ + while (io.piod_len > 0) { + io.piod_offs = (void *)(intptr_t)i; + SYSCALL_REQUIRE(ptrace(PT_IO, child, &io, sizeof(io)) + != -1 || (io.piod_len == 0 && i > 0)); + ++i; + } + } + + DPRINTF("Before resuming the child process where it left off " + "and without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), + child); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} + +#define BYTES_TRANSFER_ALIGNMENT(test, operation) \ +ATF_TC(test); \ +ATF_TC_HEAD(test, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", \ + "Verify bytes transfer for potentially misaligned " \ + "operation " operation); \ +} \ + \ +ATF_TC_BODY(test, tc) \ +{ \ + \ + bytes_transfer_alignment(operation); \ +} + +BYTES_TRANSFER_ALIGNMENT(bytes_transfer_alignment_pt_read_i, "PT_READ_I") +BYTES_TRANSFER_ALIGNMENT(bytes_transfer_alignment_pt_read_d, "PT_READ_D") +BYTES_TRANSFER_ALIGNMENT(bytes_transfer_alignment_pt_write_i, "PT_WRITE_I") +BYTES_TRANSFER_ALIGNMENT(bytes_transfer_alignment_pt_write_d, "PT_WRITE_D") + +BYTES_TRANSFER_ALIGNMENT(bytes_transfer_alignment_piod_read_i, "PIOD_READ_I") +BYTES_TRANSFER_ALIGNMENT(bytes_transfer_alignment_piod_read_d, "PIOD_READ_D") +BYTES_TRANSFER_ALIGNMENT(bytes_transfer_alignment_piod_write_i, "PIOD_WRITE_I") +BYTES_TRANSFER_ALIGNMENT(bytes_transfer_alignment_piod_write_d, "PIOD_WRITE_D") + +BYTES_TRANSFER_ALIGNMENT(bytes_transfer_alignment_piod_read_auxv, "PIOD_READ_AUXV") + +/// ---------------------------------------------------------------------------- + +static void +bytes_transfer_eof(const char *operation) +{ + const int exitval = 5; + const int sigval = SIGSTOP; + pid_t child, wpid; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + FILE *fp; + char *p; + int vector; + int op; + + struct ptrace_io_desc io; + struct ptrace_siginfo info; + + memset(&io, 0, sizeof(io)); + memset(&info, 0, sizeof(info)); + + vector = 0; + + fp = tmpfile(); + ATF_REQUIRE(fp != NULL); + + p = mmap(0, 1, PROT_READ|PROT_WRITE, MAP_PRIVATE, fileno(fp), 0); + ATF_REQUIRE(p != MAP_FAILED); + + DPRINTF("Before forking process PID=%d\n", getpid()); + SYSCALL_REQUIRE((child = fork()) != -1); + if (child == 0) { + DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + DPRINTF("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + DPRINTF("Before exiting of the child process\n"); + _exit(exitval); + } + DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + DPRINTF("Before calling ptrace(2) with PT_GET_SIGINFO for child\n"); + SYSCALL_REQUIRE(ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) + != -1); + + DPRINTF("Signal traced to lwpid=%d\n", info.psi_lwpid); + DPRINTF("Signal properties: si_signo=%#x si_code=%#x " + "si_errno=%#x\n", + info.psi_siginfo.si_signo, info.psi_siginfo.si_code, + info.psi_siginfo.si_errno); + + ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, sigval); + ATF_REQUIRE_EQ(info.psi_siginfo.si_code, SI_LWP); + + if (strcmp(operation, "PT_READ_I") == 0 || + strcmp(operation, "PT_READ_D") == 0) { + if (strcmp(operation, "PT_READ_I")) + op = PT_READ_I; + else + op = PT_READ_D; + + errno = 0; + SYSCALL_REQUIRE(ptrace(op, child, p, 0) == -1); + ATF_REQUIRE_EQ(errno, EINVAL); + } else if (strcmp(operation, "PT_WRITE_I") == 0 || + strcmp(operation, "PT_WRITE_D") == 0) { + if (strcmp(operation, "PT_WRITE_I")) + op = PT_WRITE_I; + else + op = PT_WRITE_D; + + errno = 0; + SYSCALL_REQUIRE(ptrace(op, child, p, vector) == -1); + ATF_REQUIRE_EQ(errno, EINVAL); + } else if (strcmp(operation, "PIOD_READ_I") == 0 || + strcmp(operation, "PIOD_READ_D") == 0) { + if (strcmp(operation, "PIOD_READ_I")) + op = PIOD_READ_I; + else + op = PIOD_READ_D; + + io.piod_op = op; + io.piod_addr = &vector; + io.piod_len = sizeof(int); + io.piod_offs = p; + + errno = 0; + SYSCALL_REQUIRE(ptrace(PT_IO, child, &io, sizeof(io)) == -1); + ATF_REQUIRE_EQ(errno, EINVAL); + } else if (strcmp(operation, "PIOD_WRITE_I") == 0 || + strcmp(operation, "PIOD_WRITE_D") == 0) { + if (strcmp(operation, "PIOD_WRITE_I")) + op = PIOD_WRITE_I; + else + op = PIOD_WRITE_D; + + io.piod_op = op; + io.piod_addr = &vector; + io.piod_len = sizeof(int); + io.piod_offs = p; + + errno = 0; + SYSCALL_REQUIRE(ptrace(PT_IO, child, &io, sizeof(io)) == -1); + ATF_REQUIRE_EQ(errno, EINVAL); + } + + DPRINTF("Before resuming the child process where it left off " + "and without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), + child); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} + +#define BYTES_TRANSFER_EOF(test, operation) \ +ATF_TC(test); \ +ATF_TC_HEAD(test, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", \ + "Verify bytes EOF byte transfer for the " operation \ + " operation"); \ +} \ + \ +ATF_TC_BODY(test, tc) \ +{ \ + \ + bytes_transfer_eof(operation); \ +} + +BYTES_TRANSFER_EOF(bytes_transfer_eof_pt_read_i, "PT_READ_I") +BYTES_TRANSFER_EOF(bytes_transfer_eof_pt_read_d, "PT_READ_D") +BYTES_TRANSFER_EOF(bytes_transfer_eof_pt_write_i, "PT_WRITE_I") +BYTES_TRANSFER_EOF(bytes_transfer_eof_pt_write_d, "PT_WRITE_D") + +BYTES_TRANSFER_EOF(bytes_transfer_eof_piod_read_i, "PIOD_READ_I") +BYTES_TRANSFER_EOF(bytes_transfer_eof_piod_read_d, "PIOD_READ_D") +BYTES_TRANSFER_EOF(bytes_transfer_eof_piod_write_i, "PIOD_WRITE_I") +BYTES_TRANSFER_EOF(bytes_transfer_eof_piod_write_d, "PIOD_WRITE_D") + + +#define ATF_TP_ADD_TCS_PTRACE_WAIT_BYTETRANSFER() \ + ATF_TP_ADD_TC(tp, bytes_transfer_piod_read_d_8); \ + ATF_TP_ADD_TC(tp, bytes_transfer_piod_read_d_16); \ + ATF_TP_ADD_TC(tp, bytes_transfer_piod_read_d_32); \ + ATF_TP_ADD_TC(tp, bytes_transfer_piod_read_d_64); \ + ATF_TP_ADD_TC(tp, bytes_transfer_piod_read_i_8); \ + ATF_TP_ADD_TC(tp, bytes_transfer_piod_read_i_16); \ + ATF_TP_ADD_TC(tp, bytes_transfer_piod_read_i_32); \ + ATF_TP_ADD_TC(tp, bytes_transfer_piod_read_i_64); \ + ATF_TP_ADD_TC(tp, bytes_transfer_piod_write_d_8); \ + ATF_TP_ADD_TC(tp, bytes_transfer_piod_write_d_16); \ + ATF_TP_ADD_TC(tp, bytes_transfer_piod_write_d_32); \ + ATF_TP_ADD_TC(tp, bytes_transfer_piod_write_d_64); \ + ATF_TP_ADD_TC(tp, bytes_transfer_piod_write_i_8); \ + ATF_TP_ADD_TC(tp, bytes_transfer_piod_write_i_16); \ + ATF_TP_ADD_TC(tp, bytes_transfer_piod_write_i_32); \ + ATF_TP_ADD_TC(tp, bytes_transfer_piod_write_i_64); \ + ATF_TP_ADD_TC(tp, bytes_transfer_read_d); \ + ATF_TP_ADD_TC(tp, bytes_transfer_read_i); \ + ATF_TP_ADD_TC(tp, bytes_transfer_write_d); \ + ATF_TP_ADD_TC(tp, bytes_transfer_write_i); \ + ATF_TP_ADD_TC(tp, bytes_transfer_piod_read_d_8_text); \ + ATF_TP_ADD_TC(tp, bytes_transfer_piod_read_d_16_text); \ + ATF_TP_ADD_TC(tp, bytes_transfer_piod_read_d_32_text); \ + ATF_TP_ADD_TC(tp, bytes_transfer_piod_read_d_64_text); \ + ATF_TP_ADD_TC(tp, bytes_transfer_piod_read_i_8_text); \ + ATF_TP_ADD_TC(tp, bytes_transfer_piod_read_i_16_text); \ + ATF_TP_ADD_TC(tp, bytes_transfer_piod_read_i_32_text); \ + ATF_TP_ADD_TC(tp, bytes_transfer_piod_read_i_64_text); \ + ATF_TP_ADD_TC(tp, bytes_transfer_piod_write_d_8_text); \ + ATF_TP_ADD_TC(tp, bytes_transfer_piod_write_d_16_text); \ + ATF_TP_ADD_TC(tp, bytes_transfer_piod_write_d_32_text); \ + ATF_TP_ADD_TC(tp, bytes_transfer_piod_write_d_64_text); \ + ATF_TP_ADD_TC(tp, bytes_transfer_piod_write_i_8_text); \ + ATF_TP_ADD_TC(tp, bytes_transfer_piod_write_i_16_text); \ + ATF_TP_ADD_TC(tp, bytes_transfer_piod_write_i_32_text); \ + ATF_TP_ADD_TC(tp, bytes_transfer_piod_write_i_64_text); \ + ATF_TP_ADD_TC(tp, bytes_transfer_read_d_text); \ + ATF_TP_ADD_TC(tp, bytes_transfer_read_i_text); \ + ATF_TP_ADD_TC(tp, bytes_transfer_write_d_text); \ + ATF_TP_ADD_TC(tp, bytes_transfer_write_i_text); \ + ATF_TP_ADD_TC(tp, bytes_transfer_piod_read_auxv); \ + ATF_TP_ADD_TC(tp, bytes_transfer_alignment_pt_read_i); \ + ATF_TP_ADD_TC(tp, bytes_transfer_alignment_pt_read_d); \ + ATF_TP_ADD_TC(tp, bytes_transfer_alignment_pt_write_i); \ + ATF_TP_ADD_TC(tp, bytes_transfer_alignment_pt_write_d); \ + ATF_TP_ADD_TC(tp, bytes_transfer_alignment_piod_read_i); \ + ATF_TP_ADD_TC(tp, bytes_transfer_alignment_piod_read_d); \ + ATF_TP_ADD_TC(tp, bytes_transfer_alignment_piod_write_i); \ + ATF_TP_ADD_TC(tp, bytes_transfer_alignment_piod_write_d); \ + ATF_TP_ADD_TC(tp, bytes_transfer_alignment_piod_read_auxv); \ + ATF_TP_ADD_TC(tp, bytes_transfer_eof_pt_read_i); \ + ATF_TP_ADD_TC(tp, bytes_transfer_eof_pt_read_d); \ + ATF_TP_ADD_TC(tp, bytes_transfer_eof_pt_write_i); \ + ATF_TP_ADD_TC(tp, bytes_transfer_eof_pt_write_d); \ + ATF_TP_ADD_TC(tp, bytes_transfer_eof_piod_read_i); \ + ATF_TP_ADD_TC(tp, bytes_transfer_eof_piod_read_d); \ + ATF_TP_ADD_TC(tp, bytes_transfer_eof_piod_write_i); \ + ATF_TP_ADD_TC(tp, bytes_transfer_eof_piod_write_d); diff --git a/lib/libc/sys/t_ptrace_clone_wait.h b/lib/libc/sys/t_ptrace_clone_wait.h new file mode 100644 --- /dev/null +++ b/lib/libc/sys/t_ptrace_clone_wait.h @@ -0,0 +1,830 @@ +/* $NetBSD: t_ptrace_clone_wait.h,v 1.3 2020/05/11 21:18:11 kamil Exp $ */ + +/*- + * Copyright (c) 2016, 2017, 2018, 2019, 2020 The NetBSD Foundation, Inc. + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + + +static void +clone_body(int flags, bool trackfork, bool trackvfork, + bool trackvforkdone) +{ + const int exitval = 5; + const int exitval2 = 15; + const int sigval = SIGSTOP; + pid_t child, child2 = 0, wpid; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + ptrace_state_t state; + const int slen = sizeof(state); + ptrace_event_t event; + const int elen = sizeof(event); + + const size_t stack_size = 1024 * 1024; + void *stack, *stack_base; + + stack = malloc(stack_size); + ATF_REQUIRE(stack != NULL); + +#ifdef __MACHINE_STACK_GROWS_UP + stack_base = stack; +#else + stack_base = (char *)stack + stack_size; +#endif + + DPRINTF("Before forking process PID=%d\n", getpid()); + SYSCALL_REQUIRE((child = fork()) != -1); + if (child == 0) { + DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + DPRINTF("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + SYSCALL_REQUIRE((child2 = __clone(clone_func, stack_base, + flags|SIGCHLD, (void *)(intptr_t)exitval2)) != -1); + + DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), + child2); + + // XXX WALLSIG? + FORKEE_REQUIRE_SUCCESS + (wpid = TWAIT_GENERIC(child2, &status, WALLSIG), child2); + + forkee_status_exited(status, exitval2); + + DPRINTF("Before exiting of the child process\n"); + _exit(exitval); + } + DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + DPRINTF("Set 0%s%s%s in EVENT_MASK for the child %d\n", + trackfork ? "|PTRACE_FORK" : "", + trackvfork ? "|PTRACE_VFORK" : "", + trackvforkdone ? "|PTRACE_VFORK_DONE" : "", child); + event.pe_set_event = 0; + if (trackfork) + event.pe_set_event |= PTRACE_FORK; + if (trackvfork) + event.pe_set_event |= PTRACE_VFORK; + if (trackvforkdone) + event.pe_set_event |= PTRACE_VFORK_DONE; + SYSCALL_REQUIRE(ptrace(PT_SET_EVENT_MASK, child, &event, elen) != -1); + + DPRINTF("Before resuming the child process where it left off and " + "without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + +#if defined(TWAIT_HAVE_PID) + if ((trackfork && !(flags & CLONE_VFORK)) || + (trackvfork && (flags & CLONE_VFORK))) { + DPRINTF("Before calling %s() for the child %d\n", TWAIT_FNAME, + child); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), + child); + + validate_status_stopped(status, SIGTRAP); + + SYSCALL_REQUIRE( + ptrace(PT_GET_PROCESS_STATE, child, &state, slen) != -1); + if (trackfork && !(flags & CLONE_VFORK)) { + ATF_REQUIRE_EQ(state.pe_report_event & PTRACE_FORK, + PTRACE_FORK); + } + if (trackvfork && (flags & CLONE_VFORK)) { + ATF_REQUIRE_EQ(state.pe_report_event & PTRACE_VFORK, + PTRACE_VFORK); + } + + child2 = state.pe_other_pid; + DPRINTF("Reported ptrace event with forkee %d\n", child2); + + DPRINTF("Before calling %s() for the forkee %d of the child " + "%d\n", TWAIT_FNAME, child2, child); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child2, &status, 0), + child2); + + validate_status_stopped(status, SIGTRAP); + + SYSCALL_REQUIRE( + ptrace(PT_GET_PROCESS_STATE, child2, &state, slen) != -1); + if (trackfork && !(flags & CLONE_VFORK)) { + ATF_REQUIRE_EQ(state.pe_report_event & PTRACE_FORK, + PTRACE_FORK); + } + if (trackvfork && (flags & CLONE_VFORK)) { + ATF_REQUIRE_EQ(state.pe_report_event & PTRACE_VFORK, + PTRACE_VFORK); + } + + ATF_REQUIRE_EQ(state.pe_other_pid, child); + + DPRINTF("Before resuming the forkee process where it left off " + "and without signal to be sent\n"); + SYSCALL_REQUIRE( + ptrace(PT_CONTINUE, child2, (void *)1, 0) != -1); + + DPRINTF("Before resuming the child process where it left off " + "and without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + } +#endif + + if (trackvforkdone && (flags & CLONE_VFORK)) { + DPRINTF("Before calling %s() for the child %d\n", TWAIT_FNAME, + child); + TWAIT_REQUIRE_SUCCESS( + wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, SIGTRAP); + + SYSCALL_REQUIRE( + ptrace(PT_GET_PROCESS_STATE, child, &state, slen) != -1); + ATF_REQUIRE_EQ(state.pe_report_event, PTRACE_VFORK_DONE); + + child2 = state.pe_other_pid; + DPRINTF("Reported PTRACE_VFORK_DONE event with forkee %d\n", + child2); + + DPRINTF("Before resuming the child process where it left off " + "and without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + } + +#if defined(TWAIT_HAVE_PID) + if ((trackfork && !(flags & CLONE_VFORK)) || + (trackvfork && (flags & CLONE_VFORK))) { + DPRINTF("Before calling %s() for the forkee - expected exited" + "\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS( + wpid = TWAIT_GENERIC(child2, &status, 0), child2); + + validate_status_exited(status, exitval2); + + DPRINTF("Before calling %s() for the forkee - expected no " + "process\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, + wpid = TWAIT_GENERIC(child2, &status, 0)); + } +#endif + + DPRINTF("Before calling %s() for the child - expected stopped " + "SIGCHLD\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, SIGCHLD); + + DPRINTF("Before resuming the child process where it left off and " + "without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + DPRINTF("Before calling %s() for the child - expected exited\n", + TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_exited(status, exitval); + + DPRINTF("Before calling %s() for the child - expected no process\n", + TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} + +#define CLONE_TEST(name,flags,tfork,tvfork,tvforkdone) \ +ATF_TC(name); \ +ATF_TC_HEAD(name, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", "Verify clone(%s) " \ + "called with 0%s%s%s in EVENT_MASK", \ + #flags, \ + tfork ? "|PTRACE_FORK" : "", \ + tvfork ? "|PTRACE_VFORK" : "", \ + tvforkdone ? "|PTRACE_VFORK_DONE" : ""); \ +} \ + \ +ATF_TC_BODY(name, tc) \ +{ \ + \ + clone_body(flags, tfork, tvfork, tvforkdone); \ +} + +CLONE_TEST(clone1, 0, false, false, false) +#if defined(TWAIT_HAVE_PID) +CLONE_TEST(clone2, 0, true, false, false) +CLONE_TEST(clone3, 0, false, true, false) +CLONE_TEST(clone4, 0, true, true, false) +#endif +CLONE_TEST(clone5, 0, false, false, true) +#if defined(TWAIT_HAVE_PID) +CLONE_TEST(clone6, 0, true, false, true) +CLONE_TEST(clone7, 0, false, true, true) +CLONE_TEST(clone8, 0, true, true, true) +#endif + +CLONE_TEST(clone_vm1, CLONE_VM, false, false, false) +#if defined(TWAIT_HAVE_PID) +CLONE_TEST(clone_vm2, CLONE_VM, true, false, false) +CLONE_TEST(clone_vm3, CLONE_VM, false, true, false) +CLONE_TEST(clone_vm4, CLONE_VM, true, true, false) +#endif +CLONE_TEST(clone_vm5, CLONE_VM, false, false, true) +#if defined(TWAIT_HAVE_PID) +CLONE_TEST(clone_vm6, CLONE_VM, true, false, true) +CLONE_TEST(clone_vm7, CLONE_VM, false, true, true) +CLONE_TEST(clone_vm8, CLONE_VM, true, true, true) +#endif + +CLONE_TEST(clone_fs1, CLONE_FS, false, false, false) +#if defined(TWAIT_HAVE_PID) +CLONE_TEST(clone_fs2, CLONE_FS, true, false, false) +CLONE_TEST(clone_fs3, CLONE_FS, false, true, false) +CLONE_TEST(clone_fs4, CLONE_FS, true, true, false) +#endif +CLONE_TEST(clone_fs5, CLONE_FS, false, false, true) +#if defined(TWAIT_HAVE_PID) +CLONE_TEST(clone_fs6, CLONE_FS, true, false, true) +CLONE_TEST(clone_fs7, CLONE_FS, false, true, true) +CLONE_TEST(clone_fs8, CLONE_FS, true, true, true) +#endif + +CLONE_TEST(clone_files1, CLONE_FILES, false, false, false) +#if defined(TWAIT_HAVE_PID) +CLONE_TEST(clone_files2, CLONE_FILES, true, false, false) +CLONE_TEST(clone_files3, CLONE_FILES, false, true, false) +CLONE_TEST(clone_files4, CLONE_FILES, true, true, false) +#endif +CLONE_TEST(clone_files5, CLONE_FILES, false, false, true) +#if defined(TWAIT_HAVE_PID) +CLONE_TEST(clone_files6, CLONE_FILES, true, false, true) +CLONE_TEST(clone_files7, CLONE_FILES, false, true, true) +CLONE_TEST(clone_files8, CLONE_FILES, true, true, true) +#endif + +//CLONE_TEST(clone_sighand1, CLONE_SIGHAND, false, false, false) +#if defined(TWAIT_HAVE_PID) +//CLONE_TEST(clone_sighand2, CLONE_SIGHAND, true, false, false) +//CLONE_TEST(clone_sighand3, CLONE_SIGHAND, false, true, false) +//CLONE_TEST(clone_sighand4, CLONE_SIGHAND, true, true, false) +#endif +//CLONE_TEST(clone_sighand5, CLONE_SIGHAND, false, false, true) +#if defined(TWAIT_HAVE_PID) +//CLONE_TEST(clone_sighand6, CLONE_SIGHAND, true, false, true) +//CLONE_TEST(clone_sighand7, CLONE_SIGHAND, false, true, true) +//CLONE_TEST(clone_sighand8, CLONE_SIGHAND, true, true, true) +#endif + +CLONE_TEST(clone_vfork1, CLONE_VFORK, false, false, false) +#if defined(TWAIT_HAVE_PID) +CLONE_TEST(clone_vfork2, CLONE_VFORK, true, false, false) +CLONE_TEST(clone_vfork3, CLONE_VFORK, false, true, false) +CLONE_TEST(clone_vfork4, CLONE_VFORK, true, true, false) +#endif +CLONE_TEST(clone_vfork5, CLONE_VFORK, false, false, true) +#if defined(TWAIT_HAVE_PID) +CLONE_TEST(clone_vfork6, CLONE_VFORK, true, false, true) +CLONE_TEST(clone_vfork7, CLONE_VFORK, false, true, true) +CLONE_TEST(clone_vfork8, CLONE_VFORK, true, true, true) +#endif + +/// ---------------------------------------------------------------------------- + +#if defined(TWAIT_HAVE_PID) +static void +clone_body2(int flags, bool masked, bool ignored) +{ + const int exitval = 5; + const int exitval2 = 15; + const int sigval = SIGSTOP; + pid_t child, child2 = 0, wpid; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + ptrace_state_t state; + const int slen = sizeof(state); + ptrace_event_t event; + const int elen = sizeof(event); + struct sigaction sa; + struct ptrace_siginfo info; + sigset_t intmask; + struct kinfo_proc2 kp; + size_t len = sizeof(kp); + + int name[6]; + const size_t namelen = __arraycount(name); + ki_sigset_t kp_sigmask; + ki_sigset_t kp_sigignore; + + const size_t stack_size = 1024 * 1024; + void *stack, *stack_base; + + stack = malloc(stack_size); + ATF_REQUIRE(stack != NULL); + +#ifdef __MACHINE_STACK_GROWS_UP + stack_base = stack; +#else + stack_base = (char *)stack + stack_size; +#endif + + SYSCALL_REQUIRE((child = fork()) != -1); + if (child == 0) { + DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + if (masked) { + sigemptyset(&intmask); + sigaddset(&intmask, SIGTRAP); + sigprocmask(SIG_BLOCK, &intmask, NULL); + } + + if (ignored) { + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = SIG_IGN; + sigemptyset(&sa.sa_mask); + FORKEE_ASSERT(sigaction(SIGTRAP, &sa, NULL) != -1); + } + DPRINTF("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + DPRINTF("Before forking process PID=%d flags=%#x\n", getpid(), + flags); + SYSCALL_REQUIRE((child2 = __clone(clone_func, stack_base, + flags|SIGCHLD, (void *)(intptr_t)exitval2)) != -1); + + DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), + child2); + + // XXX WALLSIG? + FORKEE_REQUIRE_SUCCESS + (wpid = TWAIT_GENERIC(child2, &status, WALLSIG), child2); + + forkee_status_exited(status, exitval2); + + DPRINTF("Before exiting of the child process\n"); + _exit(exitval); + } + DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + DPRINTF("Before calling ptrace(2) with PT_GET_SIGINFO for child\n"); + SYSCALL_REQUIRE( + ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1); + + DPRINTF("Before checking siginfo_t\n"); + ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, sigval); + ATF_REQUIRE_EQ(info.psi_siginfo.si_code, SI_LWP); + + name[0] = CTL_KERN, + name[1] = KERN_PROC2, + name[2] = KERN_PROC_PID; + name[3] = child; + name[4] = sizeof(kp); + name[5] = 1; + + FORKEE_ASSERT_EQ(sysctl(name, namelen, &kp, &len, NULL, 0), 0); + + kp_sigmask = kp.p_sigmask; + kp_sigignore = kp.p_sigignore; + + DPRINTF("Set PTRACE_FORK | PTRACE_VFORK | PTRACE_VFORK_DONE in " + "EVENT_MASK for the child %d\n", child); + event.pe_set_event = PTRACE_FORK | PTRACE_VFORK | PTRACE_VFORK_DONE; + SYSCALL_REQUIRE(ptrace(PT_SET_EVENT_MASK, child, &event, elen) != -1); + + DPRINTF("Before resuming the child process where it left off and " + "without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + DPRINTF("Before calling %s() for the child %d\n", TWAIT_FNAME, + child); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), + child); + + validate_status_stopped(status, SIGTRAP); + + ATF_REQUIRE_EQ(sysctl(name, namelen, &kp, &len, NULL, 0), 0); + + if (masked) { + DPRINTF("kp_sigmask=" + "%#02" PRIx32 "%02" PRIx32 "%02" PRIx32 "%02" + PRIx32 "\n", + kp_sigmask.__bits[0], kp_sigmask.__bits[1], + kp_sigmask.__bits[2], kp_sigmask.__bits[3]); + + DPRINTF("kp.p_sigmask=" + "%#02" PRIx32 "%02" PRIx32 "%02" PRIx32 "%02" + PRIx32 "\n", + kp.p_sigmask.__bits[0], kp.p_sigmask.__bits[1], + kp.p_sigmask.__bits[2], kp.p_sigmask.__bits[3]); + + ATF_REQUIRE(sigismember((sigset_t *)&kp.p_sigmask, + SIGTRAP)); + } + + if (ignored) { + DPRINTF("kp_sigignore=" + "%#02" PRIx32 "%02" PRIx32 "%02" PRIx32 "%02" + PRIx32 "\n", + kp_sigignore.__bits[0], kp_sigignore.__bits[1], + kp_sigignore.__bits[2], kp_sigignore.__bits[3]); + + DPRINTF("kp.p_sigignore=" + "%#02" PRIx32 "%02" PRIx32 "%02" PRIx32 "%02" + PRIx32 "\n", + kp.p_sigignore.__bits[0], kp.p_sigignore.__bits[1], + kp.p_sigignore.__bits[2], kp.p_sigignore.__bits[3]); + + ATF_REQUIRE(sigismember((sigset_t *)&kp.p_sigignore, + SIGTRAP)); + } + + SYSCALL_REQUIRE( + ptrace(PT_GET_PROCESS_STATE, child, &state, slen) != -1); + DPRINTF("state.pe_report_event=%#x pid=%d\n", state.pe_report_event, + child2); + if (!(flags & CLONE_VFORK)) { + ATF_REQUIRE_EQ(state.pe_report_event & PTRACE_FORK, + PTRACE_FORK); + } else { + ATF_REQUIRE_EQ(state.pe_report_event & PTRACE_VFORK, + PTRACE_VFORK); + } + + child2 = state.pe_other_pid; + DPRINTF("Reported ptrace event with forkee %d\n", child2); + + DPRINTF("Before calling %s() for the forkee %d of the child " + "%d\n", TWAIT_FNAME, child2, child); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child2, &status, 0), + child2); + + validate_status_stopped(status, SIGTRAP); + + name[3] = child2; + ATF_REQUIRE_EQ(sysctl(name, namelen, &kp, &len, NULL, 0), 0); + + if (masked) { + DPRINTF("kp_sigmask=" + "%#02" PRIx32 "%02" PRIx32 "%02" PRIx32 "%02" + PRIx32 "\n", + kp_sigmask.__bits[0], kp_sigmask.__bits[1], + kp_sigmask.__bits[2], kp_sigmask.__bits[3]); + + DPRINTF("kp.p_sigmask=" + "%#02" PRIx32 "%02" PRIx32 "%02" PRIx32 "%02" + PRIx32 "\n", + kp.p_sigmask.__bits[0], kp.p_sigmask.__bits[1], + kp.p_sigmask.__bits[2], kp.p_sigmask.__bits[3]); + + ATF_REQUIRE(sigismember((sigset_t *)&kp.p_sigmask, + SIGTRAP)); + } + + if (ignored) { + DPRINTF("kp_sigignore=" + "%#02" PRIx32 "%02" PRIx32 "%02" PRIx32 "%02" + PRIx32 "\n", + kp_sigignore.__bits[0], kp_sigignore.__bits[1], + kp_sigignore.__bits[2], kp_sigignore.__bits[3]); + + DPRINTF("kp.p_sigignore=" + "%#02" PRIx32 "%02" PRIx32 "%02" PRIx32 "%02" + PRIx32 "\n", + kp.p_sigignore.__bits[0], kp.p_sigignore.__bits[1], + kp.p_sigignore.__bits[2], kp.p_sigignore.__bits[3]); + + ATF_REQUIRE(sigismember((sigset_t *)&kp.p_sigignore, + SIGTRAP)); + } + + SYSCALL_REQUIRE( + ptrace(PT_GET_PROCESS_STATE, child2, &state, slen) != -1); + if (!(flags & CLONE_VFORK)) { + ATF_REQUIRE_EQ(state.pe_report_event & PTRACE_FORK, + PTRACE_FORK); + } else { + ATF_REQUIRE_EQ(state.pe_report_event & PTRACE_VFORK, + PTRACE_VFORK); + } + + ATF_REQUIRE_EQ(state.pe_other_pid, child); + + DPRINTF("Before resuming the forkee process where it left off " + "and without signal to be sent\n"); + SYSCALL_REQUIRE( + ptrace(PT_CONTINUE, child2, (void *)1, 0) != -1); + + DPRINTF("Before resuming the child process where it left off " + "and without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + if (flags & CLONE_VFORK) { + DPRINTF("Before calling %s() for the child %d\n", TWAIT_FNAME, + child); + TWAIT_REQUIRE_SUCCESS( + wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, SIGTRAP); + + name[3] = child; + ATF_REQUIRE_EQ(sysctl(name, namelen, &kp, &len, NULL, 0), 0); + + /* + * SIGCHLD is now pending in the signal queue and + * the kernel presents it to userland as a masked signal. + */ + sigdelset((sigset_t *)&kp.p_sigmask, SIGCHLD); + + if (masked) { + DPRINTF("kp_sigmask=" + "%#02" PRIx32 "%02" PRIx32 "%02" PRIx32 "%02" + PRIx32 "\n", + kp_sigmask.__bits[0], kp_sigmask.__bits[1], + kp_sigmask.__bits[2], kp_sigmask.__bits[3]); + + DPRINTF("kp.p_sigmask=" + "%#02" PRIx32 "%02" PRIx32 "%02" PRIx32 "%02" + PRIx32 "\n", + kp.p_sigmask.__bits[0], kp.p_sigmask.__bits[1], + kp.p_sigmask.__bits[2], kp.p_sigmask.__bits[3]); + + ATF_REQUIRE(!memcmp(&kp_sigmask, &kp.p_sigmask, + sizeof(kp_sigmask))); + } + + if (ignored) { + DPRINTF("kp_sigignore=" + "%#02" PRIx32 "%02" PRIx32 "%02" PRIx32 "%02" + PRIx32 "\n", + kp_sigignore.__bits[0], kp_sigignore.__bits[1], + kp_sigignore.__bits[2], kp_sigignore.__bits[3]); + + DPRINTF("kp.p_sigignore=" + "%#02" PRIx32 "%02" PRIx32 "%02" PRIx32 "%02" + PRIx32 "\n", + kp.p_sigignore.__bits[0], kp.p_sigignore.__bits[1], + kp.p_sigignore.__bits[2], kp.p_sigignore.__bits[3]); + + ATF_REQUIRE(!memcmp(&kp_sigignore, &kp.p_sigignore, + sizeof(kp_sigignore))); + } + + SYSCALL_REQUIRE( + ptrace(PT_GET_PROCESS_STATE, child, &state, slen) != -1); + ATF_REQUIRE_EQ(state.pe_report_event, PTRACE_VFORK_DONE); + + child2 = state.pe_other_pid; + DPRINTF("Reported PTRACE_VFORK_DONE event with forkee %d\n", + child2); + + DPRINTF("Before resuming the child process where it left off " + "and without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + } + + DPRINTF("Before calling %s() for the forkee - expected exited" + "\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS( + wpid = TWAIT_GENERIC(child2, &status, 0), child2); + + validate_status_exited(status, exitval2); + + DPRINTF("Before calling %s() for the forkee - expected no " + "process\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, + wpid = TWAIT_GENERIC(child2, &status, 0)); + + DPRINTF("Before calling %s() for the child - expected stopped " + "SIGCHLD\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, SIGCHLD); + + DPRINTF("Before resuming the child process where it left off and " + "without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + DPRINTF("Before calling %s() for the child - expected exited\n", + TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_exited(status, exitval); + + DPRINTF("Before calling %s() for the child - expected no process\n", + TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} + +#define CLONE_TEST2(name,flags,masked,ignored) \ +ATF_TC(name); \ +ATF_TC_HEAD(name, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", "Verify that clone(%s) is caught"\ + " regardless of signal %s%s", \ + #flags, masked ? "masked" : "", ignored ? "ignored" : ""); \ +} \ + \ +ATF_TC_BODY(name, tc) \ +{ \ + \ + clone_body2(flags, masked, ignored); \ +} + +CLONE_TEST2(clone_signalignored, 0, true, false) +CLONE_TEST2(clone_signalmasked, 0, false, true) +CLONE_TEST2(clone_vm_signalignored, CLONE_VM, true, false) +CLONE_TEST2(clone_vm_signalmasked, CLONE_VM, false, true) +CLONE_TEST2(clone_fs_signalignored, CLONE_FS, true, false) +CLONE_TEST2(clone_fs_signalmasked, CLONE_FS, false, true) +CLONE_TEST2(clone_files_signalignored, CLONE_FILES, true, false) +CLONE_TEST2(clone_files_signalmasked, CLONE_FILES, false, true) +//CLONE_TEST2(clone_sighand_signalignored, CLONE_SIGHAND, true, false) // XXX +//CLONE_TEST2(clone_sighand_signalmasked, CLONE_SIGHAND, false, true) // XXX +CLONE_TEST2(clone_vfork_signalignored, CLONE_VFORK, true, false) +CLONE_TEST2(clone_vfork_signalmasked, CLONE_VFORK, false, true) +#endif + +/// ---------------------------------------------------------------------------- + +#if defined(TWAIT_HAVE_PID) +static void +traceme_vfork_clone_body(int flags) +{ + const int exitval = 5; + const int exitval2 = 15; + pid_t child, child2 = 0, wpid; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + + const size_t stack_size = 1024 * 1024; + void *stack, *stack_base; + + stack = malloc(stack_size); + ATF_REQUIRE(stack != NULL); + +#ifdef __MACHINE_STACK_GROWS_UP + stack_base = stack; +#else + stack_base = (char *)stack + stack_size; +#endif + + SYSCALL_REQUIRE((child = vfork()) != -1); + if (child == 0) { + DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + DPRINTF("Before forking process PID=%d flags=%#x\n", getpid(), + flags); + SYSCALL_REQUIRE((child2 = __clone(clone_func, stack_base, + flags|SIGCHLD, (void *)(intptr_t)exitval2)) != -1); + + DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), + child2); + + // XXX WALLSIG? + FORKEE_REQUIRE_SUCCESS + (wpid = TWAIT_GENERIC(child2, &status, WALLSIG), child2); + + forkee_status_exited(status, exitval2); + + DPRINTF("Before exiting of the child process\n"); + _exit(exitval); + } + DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + DPRINTF("Before calling %s() for the child - expected exited\n", + TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_exited(status, exitval); + + DPRINTF("Before calling %s() for the child - expected no process\n", + TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} + +#define TRACEME_VFORK_CLONE_TEST(name,flags) \ +ATF_TC(name); \ +ATF_TC_HEAD(name, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", "Verify that clone(%s) is " \ + "handled correctly with vfork(2)ed tracer", \ + #flags); \ +} \ + \ +ATF_TC_BODY(name, tc) \ +{ \ + \ + traceme_vfork_clone_body(flags); \ +} + +TRACEME_VFORK_CLONE_TEST(traceme_vfork_clone, 0) +TRACEME_VFORK_CLONE_TEST(traceme_vfork_clone_vm, CLONE_VM) +TRACEME_VFORK_CLONE_TEST(traceme_vfork_clone_fs, CLONE_FS) +TRACEME_VFORK_CLONE_TEST(traceme_vfork_clone_files, CLONE_FILES) +//TRACEME_VFORK_CLONE_TEST(traceme_vfork_clone_sighand, CLONE_SIGHAND) // XXX +TRACEME_VFORK_CLONE_TEST(traceme_vfork_clone_vfork, CLONE_VFORK) +#endif + +#define ATF_TP_ADD_TCS_PTRACE_WAIT_CLONE() \ + ATF_TP_ADD_TC(tp, clone1); \ + ATF_TP_ADD_TC_HAVE_PID(tp, clone2); \ + ATF_TP_ADD_TC_HAVE_PID(tp, clone3); \ + ATF_TP_ADD_TC_HAVE_PID(tp, clone4); \ + ATF_TP_ADD_TC(tp, clone5); \ + ATF_TP_ADD_TC_HAVE_PID(tp, clone6); \ + ATF_TP_ADD_TC_HAVE_PID(tp, clone7); \ + ATF_TP_ADD_TC_HAVE_PID(tp, clone8); \ + ATF_TP_ADD_TC(tp, clone_vm1); \ + ATF_TP_ADD_TC_HAVE_PID(tp, clone_vm2); \ + ATF_TP_ADD_TC_HAVE_PID(tp, clone_vm3); \ + ATF_TP_ADD_TC_HAVE_PID(tp, clone_vm4); \ + ATF_TP_ADD_TC(tp, clone_vm5); \ + ATF_TP_ADD_TC_HAVE_PID(tp, clone_vm6); \ + ATF_TP_ADD_TC_HAVE_PID(tp, clone_vm7); \ + ATF_TP_ADD_TC_HAVE_PID(tp, clone_vm8); \ + ATF_TP_ADD_TC(tp, clone_fs1); \ + ATF_TP_ADD_TC_HAVE_PID(tp, clone_fs2); \ + ATF_TP_ADD_TC_HAVE_PID(tp, clone_fs3); \ + ATF_TP_ADD_TC_HAVE_PID(tp, clone_fs4); \ + ATF_TP_ADD_TC(tp, clone_fs5); \ + ATF_TP_ADD_TC_HAVE_PID(tp, clone_fs6); \ + ATF_TP_ADD_TC_HAVE_PID(tp, clone_fs7); \ + ATF_TP_ADD_TC_HAVE_PID(tp, clone_fs8); \ + ATF_TP_ADD_TC(tp, clone_files1); \ + ATF_TP_ADD_TC_HAVE_PID(tp, clone_files2); \ + ATF_TP_ADD_TC_HAVE_PID(tp, clone_files3); \ + ATF_TP_ADD_TC_HAVE_PID(tp, clone_files4); \ + ATF_TP_ADD_TC(tp, clone_files5); \ + ATF_TP_ADD_TC_HAVE_PID(tp, clone_files6); \ + ATF_TP_ADD_TC_HAVE_PID(tp, clone_files7); \ + ATF_TP_ADD_TC_HAVE_PID(tp, clone_files8); \ + ATF_TP_ADD_TC(tp, clone_vfork1); \ + ATF_TP_ADD_TC_HAVE_PID(tp, clone_vfork2); \ + ATF_TP_ADD_TC_HAVE_PID(tp, clone_vfork3); \ + ATF_TP_ADD_TC_HAVE_PID(tp, clone_vfork4); \ + ATF_TP_ADD_TC(tp, clone_vfork5); \ + ATF_TP_ADD_TC_HAVE_PID(tp, clone_vfork6); \ + ATF_TP_ADD_TC_HAVE_PID(tp, clone_vfork7); \ + ATF_TP_ADD_TC_HAVE_PID(tp, clone_vfork8); \ + ATF_TP_ADD_TC_HAVE_PID(tp, clone_signalignored); \ + ATF_TP_ADD_TC_HAVE_PID(tp, clone_signalmasked); \ + ATF_TP_ADD_TC_HAVE_PID(tp, clone_vm_signalignored); \ + ATF_TP_ADD_TC_HAVE_PID(tp, clone_vm_signalmasked); \ + ATF_TP_ADD_TC_HAVE_PID(tp, clone_fs_signalignored); \ + ATF_TP_ADD_TC_HAVE_PID(tp, clone_fs_signalmasked); \ + ATF_TP_ADD_TC_HAVE_PID(tp, clone_files_signalignored); \ + ATF_TP_ADD_TC_HAVE_PID(tp, clone_files_signalmasked); \ + ATF_TP_ADD_TC_HAVE_PID(tp, clone_vfork_signalignored); \ + ATF_TP_ADD_TC_HAVE_PID(tp, clone_vfork_signalmasked); \ + ATF_TP_ADD_TC_HAVE_PID(tp, traceme_vfork_clone); \ + ATF_TP_ADD_TC_HAVE_PID(tp, traceme_vfork_clone_vm); \ + ATF_TP_ADD_TC_HAVE_PID(tp, traceme_vfork_clone_fs); \ + ATF_TP_ADD_TC_HAVE_PID(tp, traceme_vfork_clone_files); \ + ATF_TP_ADD_TC_HAVE_PID(tp, traceme_vfork_clone_vfork); + +// ATF_TP_ADD_TC(tp, clone_sighand1); // XXX +// ATF_TP_ADD_TC_HAVE_PID(tp, clone_sighand2); // XXX +// ATF_TP_ADD_TC_HAVE_PID(tp, clone_sighand3); // XXX +// ATF_TP_ADD_TC_HAVE_PID(tp, clone_sighand4); // XXX +// ATF_TP_ADD_TC(tp, clone_sighand5); // XXX +// ATF_TP_ADD_TC_HAVE_PID(tp, clone_sighand6); // XXX +// ATF_TP_ADD_TC_HAVE_PID(tp, clone_sighand7); // XXX +// ATF_TP_ADD_TC_HAVE_PID(tp, clone_sighand8); // XXX + +// ATF_TP_ADD_TC_HAVE_PID(tp, clone_sighand_signalignored); // XXX +// ATF_TP_ADD_TC_HAVE_PID(tp, clone_sighand_signalmasked); // XXX + +// ATF_TP_ADD_TC_HAVE_PID(tp, traceme_vfork_clone_sighand); // XXX diff --git a/lib/libc/sys/t_ptrace_core_wait.h b/lib/libc/sys/t_ptrace_core_wait.h new file mode 100644 --- /dev/null +++ b/lib/libc/sys/t_ptrace_core_wait.h @@ -0,0 +1,239 @@ +/* $NetBSD: t_ptrace_core_wait.h,v 1.4 2021/07/24 08:39:54 rin Exp $ */ + +/*- + * Copyright (c) 2016, 2017, 2018, 2019, 2020 The NetBSD Foundation, Inc. + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + + +/* + * Parse the core file and find the requested note. If the reading or parsing + * fails, the test is failed. If the note is found, it is read onto buf, up to + * buf_len. The actual length of the note is returned (which can be greater + * than buf_len, indicating that it has been truncated). If the note is not + * found, -1 is returned. + * + * If the note_name ends in '*', then we find the first note that matches + * the note_name prefix up to the '*' character, e.g.: + * + * NetBSD-CORE@* + * + * finds the first note whose name prefix matches "NetBSD-CORE@". + */ +static ssize_t core_find_note(const char *core_path, + const char *note_name, uint64_t note_type, void *buf, size_t buf_len) +{ + int core_fd; + Elf *core_elf; + size_t core_numhdr, i; + ssize_t ret = -1; + size_t name_len = strlen(note_name); + bool prefix_match = false; + + if (note_name[name_len - 1] == '*') { + prefix_match = true; + name_len--; + } else { + /* note: we assume note name will be null-terminated */ + name_len++; + } + + SYSCALL_REQUIRE((core_fd = open(core_path, O_RDONLY)) != -1); + SYSCALL_REQUIRE(elf_version(EV_CURRENT) != EV_NONE); + SYSCALL_REQUIRE((core_elf = elf_begin(core_fd, ELF_C_READ, NULL))); + + SYSCALL_REQUIRE(elf_getphnum(core_elf, &core_numhdr) != 0); + for (i = 0; i < core_numhdr && ret == -1; i++) { + GElf_Phdr core_hdr; + size_t offset; + SYSCALL_REQUIRE(gelf_getphdr(core_elf, i, &core_hdr)); + if (core_hdr.p_type != PT_NOTE) + continue; + + for (offset = core_hdr.p_offset; + offset < core_hdr.p_offset + core_hdr.p_filesz;) { + Elf64_Nhdr note_hdr; + char name_buf[64]; + + switch (gelf_getclass(core_elf)) { + case ELFCLASS64: + SYSCALL_REQUIRE(pread(core_fd, ¬e_hdr, + sizeof(note_hdr), offset) + == sizeof(note_hdr)); + offset += sizeof(note_hdr); + break; + case ELFCLASS32: + { + Elf32_Nhdr tmp_hdr; + SYSCALL_REQUIRE(pread(core_fd, &tmp_hdr, + sizeof(tmp_hdr), offset) + == sizeof(tmp_hdr)); + offset += sizeof(tmp_hdr); + note_hdr.n_namesz = tmp_hdr.n_namesz; + note_hdr.n_descsz = tmp_hdr.n_descsz; + note_hdr.n_type = tmp_hdr.n_type; + } + break; + } + + /* indicates end of notes */ + if (note_hdr.n_namesz == 0 || note_hdr.n_descsz == 0) + break; + if (((prefix_match && + note_hdr.n_namesz > name_len) || + (!prefix_match && + note_hdr.n_namesz == name_len)) && + note_hdr.n_namesz <= sizeof(name_buf)) { + SYSCALL_REQUIRE(pread(core_fd, name_buf, + note_hdr.n_namesz, offset) + == (ssize_t)(size_t)note_hdr.n_namesz); + + if (!strncmp(note_name, name_buf, name_len) && + note_hdr.n_type == note_type) + ret = note_hdr.n_descsz; + } + + offset += note_hdr.n_namesz; + /* fix to alignment */ + offset = roundup(offset, core_hdr.p_align); + + /* if name & type matched above */ + if (ret != -1) { + ssize_t read_len = MIN(buf_len, + note_hdr.n_descsz); + SYSCALL_REQUIRE(pread(core_fd, buf, + read_len, offset) == read_len); + break; + } + + offset += note_hdr.n_descsz; + /* fix to alignment */ + offset = roundup(offset, core_hdr.p_align); + } + } + + elf_end(core_elf); + close(core_fd); + + return ret; +} + +ATF_TC(core_dump_procinfo); +ATF_TC_HEAD(core_dump_procinfo, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Trigger a core dump and verify its contents."); +} + +ATF_TC_BODY(core_dump_procinfo, tc) +{ + const int exitval = 5; + pid_t child, wpid; +#if defined(TWAIT_HAVE_STATUS) + const int sigval = SIGTRAP; + int status; +#endif + char core_path[] = "/tmp/core.XXXXXX"; + int core_fd; + struct netbsd_elfcore_procinfo procinfo; + + DPRINTF("Before forking process PID=%d\n", getpid()); + SYSCALL_REQUIRE((child = fork()) != -1); + if (child == 0) { + DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + DPRINTF("Before triggering SIGTRAP\n"); + trigger_trap(); + + DPRINTF("Before exiting of the child process\n"); + _exit(exitval); + } + DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + SYSCALL_REQUIRE((core_fd = mkstemp(core_path)) != -1); + close(core_fd); + + DPRINTF("Call DUMPCORE for the child process\n"); + SYSCALL_REQUIRE(ptrace(PT_DUMPCORE, child, core_path, strlen(core_path)) + != -1); + + DPRINTF("Read core file\n"); + ATF_REQUIRE_EQ(core_find_note(core_path, "NetBSD-CORE", + ELF_NOTE_NETBSD_CORE_PROCINFO, &procinfo, sizeof(procinfo)), + sizeof(procinfo)); + + ATF_CHECK_EQ(procinfo.cpi_version, 1); + ATF_CHECK_EQ(procinfo.cpi_cpisize, sizeof(procinfo)); + ATF_CHECK_EQ(procinfo.cpi_signo, SIGTRAP); + ATF_CHECK_EQ(procinfo.cpi_pid, child); + ATF_CHECK_EQ(procinfo.cpi_ppid, getpid()); + ATF_CHECK_EQ(procinfo.cpi_pgrp, getpgid(child)); + ATF_CHECK_EQ(procinfo.cpi_sid, getsid(child)); + ATF_CHECK_EQ(procinfo.cpi_ruid, getuid()); + ATF_CHECK_EQ(procinfo.cpi_euid, geteuid()); + ATF_CHECK_EQ(procinfo.cpi_rgid, getgid()); + ATF_CHECK_EQ(procinfo.cpi_egid, getegid()); + ATF_CHECK_EQ(procinfo.cpi_nlwps, 1); + ATF_CHECK(procinfo.cpi_siglwp > 0); + + unlink(core_path); + + DPRINTF("Before resuming the child process where it left off and " + "without signal to be sent\n"); + +#if defined(__aarch64__) || defined(__arm__) || defined(__powerpc__) || \ + defined(__sh3__) + /* + * For these archs, program counter is not automatically incremented + * by a trap instruction. We cannot increment PC in the trap handler, + * which breaks applications depending on this behavior, e.g., GDB. + * Therefore, we need to pass PC++ instead of (void *)1 (== PC) to + * PT_CONTINUE here. + */ + struct reg r; + + SYSCALL_REQUIRE(ptrace(PT_GETREGS, child, &r, 0) != -1); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, + (void *)(PTRACE_REG_PC(&r) + PTRACE_BREAKPOINT_SIZE), 0) != -1); +#else + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); +#endif + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_exited(status, exitval); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} + +#define ATF_TP_ADD_TCS_PTRACE_WAIT_CORE() \ + ATF_TP_ADD_TC(tp, core_dump_procinfo); diff --git a/lib/libc/sys/t_ptrace_eventmask_wait.h b/lib/libc/sys/t_ptrace_eventmask_wait.h new file mode 100644 --- /dev/null +++ b/lib/libc/sys/t_ptrace_eventmask_wait.h @@ -0,0 +1,112 @@ +/* $NetBSD: t_ptrace_eventmask_wait.h,v 1.1 2020/05/05 00:01:14 kamil Exp $ */ + +/*- + * Copyright (c) 2016, 2017, 2018, 2019, 2020 The NetBSD Foundation, Inc. + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + + +static void +eventmask_preserved(int event) +{ + const int exitval = 5; + const int sigval = SIGSTOP; + pid_t child, wpid; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + ptrace_event_t set_event, get_event; + const int len = sizeof(ptrace_event_t); + + DPRINTF("Before forking process PID=%d\n", getpid()); + SYSCALL_REQUIRE((child = fork()) != -1); + if (child == 0) { + DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + DPRINTF("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + DPRINTF("Before exiting of the child process\n"); + _exit(exitval); + } + DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + set_event.pe_set_event = event; + SYSCALL_REQUIRE( + ptrace(PT_SET_EVENT_MASK, child, &set_event, len) != -1); + SYSCALL_REQUIRE( + ptrace(PT_GET_EVENT_MASK, child, &get_event, len) != -1); + DPRINTF("set_event=%#x get_event=%#x\n", set_event.pe_set_event, + get_event.pe_set_event); + ATF_REQUIRE(memcmp(&set_event, &get_event, len) == 0); + + DPRINTF("Before resuming the child process where it left off and " + "without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_exited(status, exitval); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} + +#define EVENTMASK_PRESERVED(test, event) \ +ATF_TC(test); \ +ATF_TC_HEAD(test, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", \ + "Verify that eventmask " #event " is preserved"); \ +} \ + \ +ATF_TC_BODY(test, tc) \ +{ \ + \ + eventmask_preserved(event); \ +} + +EVENTMASK_PRESERVED(eventmask_preserved_empty, 0) +EVENTMASK_PRESERVED(eventmask_preserved_fork, PTRACE_FORK) +EVENTMASK_PRESERVED(eventmask_preserved_vfork, PTRACE_VFORK) +EVENTMASK_PRESERVED(eventmask_preserved_vfork_done, PTRACE_VFORK_DONE) +EVENTMASK_PRESERVED(eventmask_preserved_lwp_create, PTRACE_LWP_CREATE) +EVENTMASK_PRESERVED(eventmask_preserved_lwp_exit, PTRACE_LWP_EXIT) +EVENTMASK_PRESERVED(eventmask_preserved_posix_spawn, PTRACE_POSIX_SPAWN) + +#define ATF_TP_ADD_TCS_PTRACE_WAIT_EVENTMASK() \ + ATF_TP_ADD_TC(tp, eventmask_preserved_empty); \ + ATF_TP_ADD_TC(tp, eventmask_preserved_fork); \ + ATF_TP_ADD_TC(tp, eventmask_preserved_vfork); \ + ATF_TP_ADD_TC(tp, eventmask_preserved_vfork_done); \ + ATF_TP_ADD_TC(tp, eventmask_preserved_lwp_create); \ + ATF_TP_ADD_TC(tp, eventmask_preserved_lwp_exit); \ + ATF_TP_ADD_TC(tp, eventmask_preserved_posix_spawn); diff --git a/lib/libc/sys/t_ptrace_exec_wait.h b/lib/libc/sys/t_ptrace_exec_wait.h new file mode 100644 --- /dev/null +++ b/lib/libc/sys/t_ptrace_exec_wait.h @@ -0,0 +1,326 @@ +/* $NetBSD: t_ptrace_exec_wait.h,v 1.1 2020/05/05 00:23:12 kamil Exp $ */ + +/*- + * Copyright (c) 2016, 2017, 2018, 2019, 2020 The NetBSD Foundation, Inc. + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +static void +traceme_vfork_exec(bool masked, bool ignored) +{ + const int sigval = SIGTRAP; + pid_t child, wpid; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + struct sigaction sa; + struct ptrace_siginfo info; + sigset_t intmask; + struct kinfo_proc2 kp; + size_t len = sizeof(kp); + + int name[6]; + const size_t namelen = __arraycount(name); + ki_sigset_t kp_sigmask; + ki_sigset_t kp_sigignore; + + memset(&info, 0, sizeof(info)); + + DPRINTF("Before forking process PID=%d\n", getpid()); + SYSCALL_REQUIRE((child = vfork()) != -1); + if (child == 0) { + DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + if (masked) { + sigemptyset(&intmask); + sigaddset(&intmask, sigval); + sigprocmask(SIG_BLOCK, &intmask, NULL); + } + + if (ignored) { + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = SIG_IGN; + sigemptyset(&sa.sa_mask); + FORKEE_ASSERT(sigaction(sigval, &sa, NULL) != -1); + } + + DPRINTF("Before calling execve(2) from child\n"); + execlp("/bin/echo", "/bin/echo", NULL); + + /* NOTREACHED */ + FORKEE_ASSERTX(0 && "Not reached"); + } + DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + name[0] = CTL_KERN, + name[1] = KERN_PROC2, + name[2] = KERN_PROC_PID; + name[3] = getpid(); + name[4] = sizeof(kp); + name[5] = 1; + + ATF_REQUIRE_EQ(sysctl(name, namelen, &kp, &len, NULL, 0), 0); + + if (masked) + kp_sigmask = kp.p_sigmask; + + if (ignored) + kp_sigignore = kp.p_sigignore; + + name[3] = getpid(); + + ATF_REQUIRE_EQ(sysctl(name, namelen, &kp, &len, NULL, 0), 0); + + if (masked) { + DPRINTF("kp_sigmask=" + "%#02" PRIx32 "%02" PRIx32 "%02" PRIx32 "%02" PRIx32"\n", + kp_sigmask.__bits[0], kp_sigmask.__bits[1], + kp_sigmask.__bits[2], kp_sigmask.__bits[3]); + + DPRINTF("kp.p_sigmask=" + "%#02" PRIx32 "%02" PRIx32 "%02" PRIx32 "%02" PRIx32"\n", + kp.p_sigmask.__bits[0], kp.p_sigmask.__bits[1], + kp.p_sigmask.__bits[2], kp.p_sigmask.__bits[3]); + + ATF_REQUIRE(!memcmp(&kp_sigmask, &kp.p_sigmask, + sizeof(kp_sigmask))); + } + + if (ignored) { + DPRINTF("kp_sigignore=" + "%#02" PRIx32 "%02" PRIx32 "%02" PRIx32 "%02" PRIx32"\n", + kp_sigignore.__bits[0], kp_sigignore.__bits[1], + kp_sigignore.__bits[2], kp_sigignore.__bits[3]); + + DPRINTF("kp.p_sigignore=" + "%#02" PRIx32 "%02" PRIx32 "%02" PRIx32 "%02" PRIx32"\n", + kp.p_sigignore.__bits[0], kp.p_sigignore.__bits[1], + kp.p_sigignore.__bits[2], kp.p_sigignore.__bits[3]); + + ATF_REQUIRE(!memcmp(&kp_sigignore, &kp.p_sigignore, + sizeof(kp_sigignore))); + } + + DPRINTF("Before calling ptrace(2) with PT_GET_SIGINFO for child\n"); + SYSCALL_REQUIRE( + ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1); + + DPRINTF("Signal traced to lwpid=%d\n", info.psi_lwpid); + DPRINTF("Signal properties: si_signo=%#x si_code=%#x si_errno=%#x\n", + info.psi_siginfo.si_signo, info.psi_siginfo.si_code, + info.psi_siginfo.si_errno); + + ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, sigval); + ATF_REQUIRE_EQ(info.psi_siginfo.si_code, TRAP_EXEC); + + DPRINTF("Before resuming the child process where it left off and " + "without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} + +#define TRACEME_VFORK_EXEC(test, masked, ignored) \ +ATF_TC(test); \ +ATF_TC_HEAD(test, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", \ + "Verify PT_TRACE_ME followed by exec(3) in a vfork(2)ed " \ + "child%s%s", masked ? " with masked signal" : "", \ + masked ? " with ignored signal" : ""); \ +} \ + \ +ATF_TC_BODY(test, tc) \ +{ \ + \ + traceme_vfork_exec(masked, ignored); \ +} + +TRACEME_VFORK_EXEC(traceme_vfork_exec, false, false) +TRACEME_VFORK_EXEC(traceme_vfork_signalmasked_exec, true, false) +TRACEME_VFORK_EXEC(traceme_vfork_signalignored_exec, false, true) + +/// ---------------------------------------------------------------------------- + +static void +traceme_exec(bool masked, bool ignored) +{ + const int sigval = SIGTRAP; + pid_t child, wpid; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + struct sigaction sa; + struct ptrace_siginfo info; + sigset_t intmask; + struct kinfo_proc2 kp; + size_t len = sizeof(kp); + + int name[6]; + const size_t namelen = __arraycount(name); + ki_sigset_t kp_sigmask; + ki_sigset_t kp_sigignore; + + memset(&info, 0, sizeof(info)); + + DPRINTF("Before forking process PID=%d\n", getpid()); + SYSCALL_REQUIRE((child = fork()) != -1); + if (child == 0) { + DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + if (masked) { + sigemptyset(&intmask); + sigaddset(&intmask, sigval); + sigprocmask(SIG_BLOCK, &intmask, NULL); + } + + if (ignored) { + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = SIG_IGN; + sigemptyset(&sa.sa_mask); + FORKEE_ASSERT(sigaction(sigval, &sa, NULL) != -1); + } + + DPRINTF("Before calling execve(2) from child\n"); + execlp("/bin/echo", "/bin/echo", NULL); + + FORKEE_ASSERT(0 && "Not reached"); + } + DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + name[0] = CTL_KERN, + name[1] = KERN_PROC2, + name[2] = KERN_PROC_PID; + name[3] = getpid(); + name[4] = sizeof(kp); + name[5] = 1; + + ATF_REQUIRE_EQ(sysctl(name, namelen, &kp, &len, NULL, 0), 0); + + if (masked) + kp_sigmask = kp.p_sigmask; + + if (ignored) + kp_sigignore = kp.p_sigignore; + + name[3] = getpid(); + + ATF_REQUIRE_EQ(sysctl(name, namelen, &kp, &len, NULL, 0), 0); + + if (masked) { + DPRINTF("kp_sigmask=" + "%#02" PRIx32 "%02" PRIx32 "%02" PRIx32 "%02" PRIx32"\n", + kp_sigmask.__bits[0], kp_sigmask.__bits[1], + kp_sigmask.__bits[2], kp_sigmask.__bits[3]); + + DPRINTF("kp.p_sigmask=" + "%#02" PRIx32 "%02" PRIx32 "%02" PRIx32 "%02" PRIx32"\n", + kp.p_sigmask.__bits[0], kp.p_sigmask.__bits[1], + kp.p_sigmask.__bits[2], kp.p_sigmask.__bits[3]); + + ATF_REQUIRE(!memcmp(&kp_sigmask, &kp.p_sigmask, + sizeof(kp_sigmask))); + } + + if (ignored) { + DPRINTF("kp_sigignore=" + "%#02" PRIx32 "%02" PRIx32 "%02" PRIx32 "%02" PRIx32"\n", + kp_sigignore.__bits[0], kp_sigignore.__bits[1], + kp_sigignore.__bits[2], kp_sigignore.__bits[3]); + + DPRINTF("kp.p_sigignore=" + "%#02" PRIx32 "%02" PRIx32 "%02" PRIx32 "%02" PRIx32"\n", + kp.p_sigignore.__bits[0], kp.p_sigignore.__bits[1], + kp.p_sigignore.__bits[2], kp.p_sigignore.__bits[3]); + + ATF_REQUIRE(!memcmp(&kp_sigignore, &kp.p_sigignore, + sizeof(kp_sigignore))); + } + + DPRINTF("Before calling ptrace(2) with PT_GET_SIGINFO for child\n"); + SYSCALL_REQUIRE( + ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1); + + DPRINTF("Signal traced to lwpid=%d\n", info.psi_lwpid); + DPRINTF("Signal properties: si_signo=%#x si_code=%#x si_errno=%#x\n", + info.psi_siginfo.si_signo, info.psi_siginfo.si_code, + info.psi_siginfo.si_errno); + + ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, sigval); + ATF_REQUIRE_EQ(info.psi_siginfo.si_code, TRAP_EXEC); + + DPRINTF("Before resuming the child process where it left off and " + "without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} + +#define TRACEME_EXEC(test, masked, ignored) \ +ATF_TC(test); \ +ATF_TC_HEAD(test, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", \ + "Detect SIGTRAP TRAP_EXEC from " \ + "child%s%s", masked ? " with masked signal" : "", \ + masked ? " with ignored signal" : ""); \ +} \ + \ +ATF_TC_BODY(test, tc) \ +{ \ + \ + traceme_exec(masked, ignored); \ +} + +TRACEME_EXEC(traceme_exec, false, false) +TRACEME_EXEC(traceme_signalmasked_exec, true, false) +TRACEME_EXEC(traceme_signalignored_exec, false, true) + +#define ATF_TP_ADD_TCS_PTRACE_WAIT_EXEC() \ + ATF_TP_ADD_TC(tp, traceme_vfork_exec); \ + ATF_TP_ADD_TC(tp, traceme_vfork_signalmasked_exec); \ + ATF_TP_ADD_TC(tp, traceme_vfork_signalignored_exec); \ + ATF_TP_ADD_TC(tp, traceme_exec); \ + ATF_TP_ADD_TC(tp, traceme_signalmasked_exec); \ + ATF_TP_ADD_TC(tp, traceme_signalignored_exec); diff --git a/lib/libc/sys/t_ptrace_fork_wait.h b/lib/libc/sys/t_ptrace_fork_wait.h new file mode 100644 --- /dev/null +++ b/lib/libc/sys/t_ptrace_fork_wait.h @@ -0,0 +1,1789 @@ +/* $NetBSD: t_ptrace_fork_wait.h,v 1.7 2020/06/09 00:28:57 kamil Exp $ */ + +/*- + * Copyright (c) 2016, 2017, 2018, 2020 The NetBSD Foundation, Inc. + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + + +static void +fork_body(const char *fn, bool trackspawn, bool trackfork, bool trackvfork, + bool trackvforkdone, bool newpgrp) +{ + const int exitval = 5; + const int exitval2 = 0; /* This matched exit status from /bin/echo */ + const int sigval = SIGSTOP; + pid_t child, child2 = 0, wpid; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + sigset_t set; + ptrace_state_t state; + const int slen = sizeof(state); + ptrace_event_t event; + const int elen = sizeof(event); + + char * const arg[] = { __UNCONST("/bin/echo"), NULL }; + + if (newpgrp) + atf_tc_skip("kernel panic (pg_jobc going negative)"); + + DPRINTF("Before forking process PID=%d\n", getpid()); + SYSCALL_REQUIRE((child = fork()) != -1); + if (child == 0) { + if (newpgrp) { + DPRINTF("Before entering new process group"); + setpgid(0, 0); + } + + DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + DPRINTF("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + if (strcmp(fn, "spawn") == 0) { + FORKEE_ASSERT_EQ(posix_spawn(&child2, + arg[0], NULL, NULL, arg, NULL), 0); + } else { + if (strcmp(fn, "fork") == 0) { + FORKEE_ASSERT((child2 = fork()) != -1); + } else if (strcmp(fn, "vfork") == 0) { + FORKEE_ASSERT((child2 = vfork()) != -1); + } + + if (child2 == 0) + _exit(exitval2); + } + FORKEE_REQUIRE_SUCCESS + (wpid = TWAIT_GENERIC(child2, &status, 0), child2); + + forkee_status_exited(status, exitval2); + + DPRINTF("Before exiting of the child process\n"); + _exit(exitval); + } + DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + DPRINTF("Set 0%s%s%s%s in EVENT_MASK for the child %d\n", + trackspawn ? "|PTRACE_POSIX_SPAWN" : "", + trackfork ? "|PTRACE_FORK" : "", + trackvfork ? "|PTRACE_VFORK" : "", + trackvforkdone ? "|PTRACE_VFORK_DONE" : "", child); + event.pe_set_event = 0; + if (trackspawn) + event.pe_set_event |= PTRACE_POSIX_SPAWN; + if (trackfork) + event.pe_set_event |= PTRACE_FORK; + if (trackvfork) + event.pe_set_event |= PTRACE_VFORK; + if (trackvforkdone) + event.pe_set_event |= PTRACE_VFORK_DONE; + SYSCALL_REQUIRE(ptrace(PT_SET_EVENT_MASK, child, &event, elen) != -1); + + /* + * Ignore interception of the SIGCHLD signals. + * + * SIGCHLD once blocked is discarded by the kernel as it has the + * SA_IGNORE property. During the fork(2) operation all signals can be + * shortly blocked and missed (unless there is a registered signal + * handler in the traced child). This leads to a race in this test if + * there would be an intention to catch SIGCHLD. + */ + sigemptyset(&set); + sigaddset(&set, SIGCHLD); + SYSCALL_REQUIRE(ptrace(PT_SET_SIGPASS, child, &set, sizeof(set)) != -1); + + DPRINTF("Before resuming the child process where it left off and " + "without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + +#if defined(TWAIT_HAVE_PID) + if ((trackspawn && strcmp(fn, "spawn") == 0) || + (trackfork && strcmp(fn, "fork") == 0) || + (trackvfork && strcmp(fn, "vfork") == 0)) { + DPRINTF("Before calling %s() for the child %d\n", TWAIT_FNAME, + child); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), + child); + + validate_status_stopped(status, SIGTRAP); + + SYSCALL_REQUIRE( + ptrace(PT_GET_PROCESS_STATE, child, &state, slen) != -1); + if (trackspawn && strcmp(fn, "spawn") == 0) { + ATF_REQUIRE_EQ( + state.pe_report_event & PTRACE_POSIX_SPAWN, + PTRACE_POSIX_SPAWN); + } + if (trackfork && strcmp(fn, "fork") == 0) { + ATF_REQUIRE_EQ(state.pe_report_event & PTRACE_FORK, + PTRACE_FORK); + } + if (trackvfork && strcmp(fn, "vfork") == 0) { + ATF_REQUIRE_EQ(state.pe_report_event & PTRACE_VFORK, + PTRACE_VFORK); + } + + child2 = state.pe_other_pid; + DPRINTF("Reported ptrace event with forkee %d\n", child2); + + DPRINTF("Before calling %s() for the forkee %d of the child " + "%d\n", TWAIT_FNAME, child2, child); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child2, &status, 0), + child2); + + validate_status_stopped(status, SIGTRAP); + + SYSCALL_REQUIRE( + ptrace(PT_GET_PROCESS_STATE, child2, &state, slen) != -1); + if (trackspawn && strcmp(fn, "spawn") == 0) { + ATF_REQUIRE_EQ( + state.pe_report_event & PTRACE_POSIX_SPAWN, + PTRACE_POSIX_SPAWN); + } + if (trackfork && strcmp(fn, "fork") == 0) { + ATF_REQUIRE_EQ(state.pe_report_event & PTRACE_FORK, + PTRACE_FORK); + } + if (trackvfork && strcmp(fn, "vfork") == 0) { + ATF_REQUIRE_EQ(state.pe_report_event & PTRACE_VFORK, + PTRACE_VFORK); + } + + ATF_REQUIRE_EQ(state.pe_other_pid, child); + + DPRINTF("Before resuming the forkee process where it left off " + "and without signal to be sent\n"); + SYSCALL_REQUIRE( + ptrace(PT_CONTINUE, child2, (void *)1, 0) != -1); + + DPRINTF("Before resuming the child process where it left off " + "and without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + } +#endif + + if (trackvforkdone && strcmp(fn, "vfork") == 0) { + DPRINTF("Before calling %s() for the child %d\n", TWAIT_FNAME, + child); + TWAIT_REQUIRE_SUCCESS( + wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, SIGTRAP); + + SYSCALL_REQUIRE( + ptrace(PT_GET_PROCESS_STATE, child, &state, slen) != -1); + ATF_REQUIRE_EQ(state.pe_report_event, PTRACE_VFORK_DONE); + + child2 = state.pe_other_pid; + DPRINTF("Reported PTRACE_VFORK_DONE event with forkee %d\n", + child2); + + DPRINTF("Before resuming the child process where it left off " + "and without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + } + +#if defined(TWAIT_HAVE_PID) + if ((trackspawn && strcmp(fn, "spawn") == 0) || + (trackfork && strcmp(fn, "fork") == 0) || + (trackvfork && strcmp(fn, "vfork") == 0)) { + DPRINTF("Before calling %s() for the forkee - expected exited" + "\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS( + wpid = TWAIT_GENERIC(child2, &status, 0), child2); + + validate_status_exited(status, exitval2); + + DPRINTF("Before calling %s() for the forkee - expected no " + "process\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, + wpid = TWAIT_GENERIC(child2, &status, 0)); + } +#endif + + DPRINTF("Before calling %s() for the child - expected exited\n", + TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_exited(status, exitval); + + DPRINTF("Before calling %s() for the child - expected no process\n", + TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} + +#define FORK_TEST2(name,fun,tspawn,tfork,tvfork,tvforkdone,newpgrp) \ +ATF_TC(name); \ +ATF_TC_HEAD(name, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", "Verify " fun "() " \ + "called with 0%s%s%s%s in EVENT_MASK%s", \ + tspawn ? "|PTRACE_POSIX_SPAWN" : "", \ + tfork ? "|PTRACE_FORK" : "", \ + tvfork ? "|PTRACE_VFORK" : "", \ + tvforkdone ? "|PTRACE_VFORK_DONE" : "", \ + newpgrp ? " and the traced processes call setpgrp(0,0)":"");\ +} \ + \ +ATF_TC_BODY(name, tc) \ +{ \ + \ + fork_body(fun, tspawn, tfork, tvfork, tvforkdone, newpgrp); \ +} + +#define FORK_TEST(name,fun,tspawn,tfork,tvfork,tvforkdone) \ + FORK_TEST2(name,fun,tspawn,tfork,tvfork,tvforkdone,false) + +FORK_TEST(fork1, "fork", false, false, false, false) +#if defined(TWAIT_HAVE_PID) +FORK_TEST(fork2, "fork", false, true, false, false) +FORK_TEST(fork3, "fork", false, false, true, false) +FORK_TEST(fork4, "fork", false, true, true, false) +#endif +FORK_TEST(fork5, "fork", false, false, false, true) +#if defined(TWAIT_HAVE_PID) +FORK_TEST(fork6, "fork", false, true, false, true) +FORK_TEST(fork7, "fork", false, false, true, true) +FORK_TEST(fork8, "fork", false, true, true, true) +#endif +FORK_TEST(fork9, "fork", true, false, false, false) +#if defined(TWAIT_HAVE_PID) +FORK_TEST(fork10, "fork", true, true, false, false) +FORK_TEST(fork11, "fork", true, false, true, false) +FORK_TEST(fork12, "fork", true, true, true, false) +#endif +FORK_TEST(fork13, "fork", true, false, false, true) +#if defined(TWAIT_HAVE_PID) +FORK_TEST(fork14, "fork", true, true, false, true) +FORK_TEST(fork15, "fork", true, false, true, true) +FORK_TEST(fork16, "fork", true, true, true, true) +#endif + +#if defined(TWAIT_HAVE_PID) +FORK_TEST2(fork_setpgid, "fork", true, true, true, true, true) +#endif + +FORK_TEST(vfork1, "vfork", false, false, false, false) +#if defined(TWAIT_HAVE_PID) +FORK_TEST(vfork2, "vfork", false, true, false, false) +FORK_TEST(vfork3, "vfork", false, false, true, false) +FORK_TEST(vfork4, "vfork", false, true, true, false) +#endif +FORK_TEST(vfork5, "vfork", false, false, false, true) +#if defined(TWAIT_HAVE_PID) +FORK_TEST(vfork6, "vfork", false, true, false, true) +FORK_TEST(vfork7, "vfork", false, false, true, true) +FORK_TEST(vfork8, "vfork", false, true, true, true) +#endif +FORK_TEST(vfork9, "vfork", true, false, false, false) +#if defined(TWAIT_HAVE_PID) +FORK_TEST(vfork10, "vfork", true, true, false, false) +FORK_TEST(vfork11, "vfork", true, false, true, false) +FORK_TEST(vfork12, "vfork", true, true, true, false) +#endif +FORK_TEST(vfork13, "vfork", true, false, false, true) +#if defined(TWAIT_HAVE_PID) +FORK_TEST(vfork14, "vfork", true, true, false, true) +FORK_TEST(vfork15, "vfork", true, false, true, true) +FORK_TEST(vfork16, "vfork", true, true, true, true) +#endif + +#if defined(TWAIT_HAVE_PID) +FORK_TEST2(vfork_setpgid, "vfork", true, true, true, true, true) +#endif + +FORK_TEST(posix_spawn1, "spawn", false, false, false, false) +FORK_TEST(posix_spawn2, "spawn", false, true, false, false) +FORK_TEST(posix_spawn3, "spawn", false, false, true, false) +FORK_TEST(posix_spawn4, "spawn", false, true, true, false) +FORK_TEST(posix_spawn5, "spawn", false, false, false, true) +FORK_TEST(posix_spawn6, "spawn", false, true, false, true) +FORK_TEST(posix_spawn7, "spawn", false, false, true, true) +FORK_TEST(posix_spawn8, "spawn", false, true, true, true) +#if defined(TWAIT_HAVE_PID) +FORK_TEST(posix_spawn9, "spawn", true, false, false, false) +FORK_TEST(posix_spawn10, "spawn", true, true, false, false) +FORK_TEST(posix_spawn11, "spawn", true, false, true, false) +FORK_TEST(posix_spawn12, "spawn", true, true, true, false) +FORK_TEST(posix_spawn13, "spawn", true, false, false, true) +FORK_TEST(posix_spawn14, "spawn", true, true, false, true) +FORK_TEST(posix_spawn15, "spawn", true, false, true, true) +FORK_TEST(posix_spawn16, "spawn", true, true, true, true) +#endif + +#if defined(TWAIT_HAVE_PID) +FORK_TEST2(posix_spawn_setpgid, "spawn", true, true, true, true, true) +#endif + +/// ---------------------------------------------------------------------------- + +#if defined(TWAIT_HAVE_PID) +static void +unrelated_tracer_fork_body(const char *fn, bool trackspawn, bool trackfork, + bool trackvfork, bool trackvforkdone, bool newpgrp) +{ + const int sigval = SIGSTOP; + struct msg_fds parent_tracee, parent_tracer; + const int exitval = 10; + const int exitval2 = 0; /* This matched exit status from /bin/echo */ + pid_t tracee, tracer, wpid; + pid_t tracee2 = 0; + uint8_t msg = 0xde; /* dummy message for IPC based on pipe(2) */ +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + + sigset_t set; + struct ptrace_siginfo info; + ptrace_state_t state; + const int slen = sizeof(state); + ptrace_event_t event; + const int elen = sizeof(event); + + char * const arg[] = { __UNCONST("/bin/echo"), NULL }; + + if (newpgrp) + atf_tc_skip("kernel panic (pg_jobc going negative)"); + + DPRINTF("Spawn tracee\n"); + SYSCALL_REQUIRE(msg_open(&parent_tracee) == 0); + tracee = atf_utils_fork(); + if (tracee == 0) { + if (newpgrp) { + DPRINTF("Before entering new process group"); + setpgid(0, 0); + } + + // Wait for parent to let us crash + CHILD_FROM_PARENT("exit tracee", parent_tracee, msg); + + DPRINTF("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + if (strcmp(fn, "spawn") == 0) { + FORKEE_ASSERT_EQ(posix_spawn(&tracee2, + arg[0], NULL, NULL, arg, NULL), 0); + } else { + if (strcmp(fn, "fork") == 0) { + FORKEE_ASSERT((tracee2 = fork()) != -1); + } else if (strcmp(fn, "vfork") == 0) { + FORKEE_ASSERT((tracee2 = vfork()) != -1); + } + + if (tracee2 == 0) + _exit(exitval2); + } + FORKEE_REQUIRE_SUCCESS + (wpid = TWAIT_GENERIC(tracee2, &status, 0), tracee2); + + forkee_status_exited(status, exitval2); + + DPRINTF("Before exiting of the child process\n"); + _exit(exitval); + } + + DPRINTF("Spawn debugger\n"); + SYSCALL_REQUIRE(msg_open(&parent_tracer) == 0); + tracer = atf_utils_fork(); + if (tracer == 0) { + /* Fork again and drop parent to reattach to PID 1 */ + tracer = atf_utils_fork(); + if (tracer != 0) + _exit(exitval); + + DPRINTF("Before calling PT_ATTACH from tracee %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_ATTACH, tracee, NULL, 0) != -1); + + /* Wait for tracee and assert that it was stopped w/ SIGSTOP */ + FORKEE_REQUIRE_SUCCESS( + wpid = TWAIT_GENERIC(tracee, &status, 0), tracee); + + forkee_status_stopped(status, SIGSTOP); + + DPRINTF("Before calling ptrace(2) with PT_GET_SIGINFO for the " + "traced process\n"); + SYSCALL_REQUIRE( + ptrace(PT_GET_SIGINFO, tracee, &info, sizeof(info)) != -1); + + DPRINTF("Signal traced to lwpid=%d\n", info.psi_lwpid); + DPRINTF("Signal properties: si_signo=%#x si_code=%#x " + "si_errno=%#x\n", info.psi_siginfo.si_signo, + info.psi_siginfo.si_code, info.psi_siginfo.si_errno); + + FORKEE_ASSERT_EQ(info.psi_siginfo.si_signo, SIGSTOP); + FORKEE_ASSERT_EQ(info.psi_siginfo.si_code, SI_USER); + + /* Resume tracee with PT_CONTINUE */ + FORKEE_ASSERT(ptrace(PT_CONTINUE, tracee, (void *)1, 0) != -1); + + /* Inform parent that tracer has attached to tracee */ + CHILD_TO_PARENT("tracer ready", parent_tracer, msg); + + /* Wait for parent to tell use that tracee should have exited */ + CHILD_FROM_PARENT("wait for tracee exit", parent_tracer, msg); + + /* Wait for tracee and assert that it exited */ + FORKEE_REQUIRE_SUCCESS( + wpid = TWAIT_GENERIC(tracee, &status, 0), tracee); + + forkee_status_stopped(status, sigval); + + DPRINTF("Before calling ptrace(2) with PT_GET_SIGINFO for the " + "traced process\n"); + SYSCALL_REQUIRE( + ptrace(PT_GET_SIGINFO, tracee, &info, sizeof(info)) != -1); + + DPRINTF("Signal traced to lwpid=%d\n", info.psi_lwpid); + DPRINTF("Signal properties: si_signo=%#x si_code=%#x " + "si_errno=%#x\n", info.psi_siginfo.si_signo, + info.psi_siginfo.si_code, info.psi_siginfo.si_errno); + + FORKEE_ASSERT_EQ(info.psi_siginfo.si_signo, sigval); + FORKEE_ASSERT_EQ(info.psi_siginfo.si_code, SI_LWP); + + DPRINTF("Set 0%s%s%s%s in EVENT_MASK for the child %d\n", + trackspawn ? "|PTRACE_POSIX_SPAWN" : "", + trackfork ? "|PTRACE_FORK" : "", + trackvfork ? "|PTRACE_VFORK" : "", + trackvforkdone ? "|PTRACE_VFORK_DONE" : "", tracee); + event.pe_set_event = 0; + if (trackspawn) + event.pe_set_event |= PTRACE_POSIX_SPAWN; + if (trackfork) + event.pe_set_event |= PTRACE_FORK; + if (trackvfork) + event.pe_set_event |= PTRACE_VFORK; + if (trackvforkdone) + event.pe_set_event |= PTRACE_VFORK_DONE; + SYSCALL_REQUIRE(ptrace(PT_SET_EVENT_MASK, tracee, &event, elen) + != -1); + + /* + * Ignore interception of the SIGCHLD signals. + * + * SIGCHLD once blocked is discarded by the kernel as it has the + * SA_IGNORE property. During the fork(2) operation all signals + * can be shortly blocked and missed (unless there is a + * registered signal handler in the traced child). This leads to + * a race in this test if there would be an intention to catch + * SIGCHLD. + */ + sigemptyset(&set); + sigaddset(&set, SIGCHLD); + SYSCALL_REQUIRE(ptrace(PT_SET_SIGPASS, tracee, &set, + sizeof(set)) != -1); + + DPRINTF("Before resuming the child process where it left off " + "and without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, tracee, (void *)1, 0) != -1); + + if ((trackspawn && strcmp(fn, "spawn") == 0) || + (trackfork && strcmp(fn, "fork") == 0) || + (trackvfork && strcmp(fn, "vfork") == 0)) { + DPRINTF("Before calling %s() for the tracee %d\n", TWAIT_FNAME, + tracee); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(tracee, &status, 0), + tracee); + + validate_status_stopped(status, SIGTRAP); + + SYSCALL_REQUIRE( + ptrace(PT_GET_PROCESS_STATE, tracee, &state, slen) != -1); + if (trackspawn && strcmp(fn, "spawn") == 0) { + ATF_REQUIRE_EQ( + state.pe_report_event & PTRACE_POSIX_SPAWN, + PTRACE_POSIX_SPAWN); + } + if (trackfork && strcmp(fn, "fork") == 0) { + ATF_REQUIRE_EQ(state.pe_report_event & PTRACE_FORK, + PTRACE_FORK); + } + if (trackvfork && strcmp(fn, "vfork") == 0) { + ATF_REQUIRE_EQ(state.pe_report_event & PTRACE_VFORK, + PTRACE_VFORK); + } + + tracee2 = state.pe_other_pid; + DPRINTF("Reported ptrace event with forkee %d\n", tracee2); + + DPRINTF("Before calling %s() for the forkee %d of the tracee " + "%d\n", TWAIT_FNAME, tracee2, tracee); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(tracee2, &status, 0), + tracee2); + + validate_status_stopped(status, SIGTRAP); + + SYSCALL_REQUIRE( + ptrace(PT_GET_PROCESS_STATE, tracee2, &state, slen) != -1); + if (trackspawn && strcmp(fn, "spawn") == 0) { + ATF_REQUIRE_EQ( + state.pe_report_event & PTRACE_POSIX_SPAWN, + PTRACE_POSIX_SPAWN); + } + if (trackfork && strcmp(fn, "fork") == 0) { + ATF_REQUIRE_EQ(state.pe_report_event & PTRACE_FORK, + PTRACE_FORK); + } + if (trackvfork && strcmp(fn, "vfork") == 0) { + ATF_REQUIRE_EQ(state.pe_report_event & PTRACE_VFORK, + PTRACE_VFORK); + } + + ATF_REQUIRE_EQ(state.pe_other_pid, tracee); + + DPRINTF("Before resuming the forkee process where it left off " + "and without signal to be sent\n"); + SYSCALL_REQUIRE( + ptrace(PT_CONTINUE, tracee2, (void *)1, 0) != -1); + + DPRINTF("Before resuming the tracee process where it left off " + "and without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, tracee, (void *)1, 0) != -1); + } + + if (trackvforkdone && strcmp(fn, "vfork") == 0) { + DPRINTF("Before calling %s() for the tracee %d\n", TWAIT_FNAME, + tracee); + TWAIT_REQUIRE_SUCCESS( + wpid = TWAIT_GENERIC(tracee, &status, 0), tracee); + + validate_status_stopped(status, SIGTRAP); + + SYSCALL_REQUIRE( + ptrace(PT_GET_PROCESS_STATE, tracee, &state, slen) != -1); + ATF_REQUIRE_EQ(state.pe_report_event, PTRACE_VFORK_DONE); + + tracee2 = state.pe_other_pid; + DPRINTF("Reported PTRACE_VFORK_DONE event with forkee %d\n", + tracee2); + + DPRINTF("Before resuming the tracee process where it left off " + "and without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, tracee, (void *)1, 0) != -1); + } + + + if ((trackspawn && strcmp(fn, "spawn") == 0) || + (trackfork && strcmp(fn, "fork") == 0) || + (trackvfork && strcmp(fn, "vfork") == 0)) { + DPRINTF("Before calling %s() for the forkee - expected exited" + "\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS( + wpid = TWAIT_GENERIC(tracee2, &status, 0), tracee2); + + validate_status_exited(status, exitval2); + + DPRINTF("Before calling %s() for the forkee - expected no " + "process\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, + wpid = TWAIT_GENERIC(tracee2, &status, 0)); + } + + DPRINTF("Before calling %s() for the tracee - expected exited\n", + TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(tracee, &status, 0), tracee); + + validate_status_exited(status, exitval); + + /* Inform parent that tracer is exiting normally */ + CHILD_TO_PARENT("tracer done", parent_tracer, msg); + + DPRINTF("Before exiting of the tracer process\n"); + _exit(0 /* collect by initproc */); + } + + DPRINTF("Wait for the tracer process (direct child) to exit " + "calling %s()\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS( + wpid = TWAIT_GENERIC(tracer, &status, 0), tracer); + + validate_status_exited(status, exitval); + + DPRINTF("Wait for the non-exited tracee process with %s()\n", + TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS( + wpid = TWAIT_GENERIC(tracee, NULL, WNOHANG), 0); + + DPRINTF("Wait for the tracer to attach to the tracee\n"); + PARENT_FROM_CHILD("tracer ready", parent_tracer, msg); + + DPRINTF("Resume the tracee and let it crash\n"); + PARENT_TO_CHILD("exit tracee", parent_tracee, msg); + + DPRINTF("Resume the tracer and let it detect crashed tracee\n"); + PARENT_TO_CHILD("Message 2", parent_tracer, msg); + + DPRINTF("Wait for tracee to finish its job and exit - calling %s()\n", + TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(tracee, &status, 0), tracee); + + validate_status_exited(status, exitval); + + DPRINTF("Await normal exit of tracer\n"); + PARENT_FROM_CHILD("tracer done", parent_tracer, msg); + + msg_close(&parent_tracer); + msg_close(&parent_tracee); +} + +#define UNRELATED_TRACER_FORK_TEST2(name,fun,tspawn,tfork,tvfork,tvforkdone,newpgrp)\ +ATF_TC(name); \ +ATF_TC_HEAD(name, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", "Verify " fun "() " \ + "called with 0%s%s%s%s in EVENT_MASK%s", \ + tspawn ? "|PTRACE_POSIX_SPAWN" : "", \ + tfork ? "|PTRACE_FORK" : "", \ + tvfork ? "|PTRACE_VFORK" : "", \ + tvforkdone ? "|PTRACE_VFORK_DONE" : "", \ + newpgrp ? " and the traced processes call setpgrp(0,0)":"");\ +} \ + \ +ATF_TC_BODY(name, tc) \ +{ \ + \ + unrelated_tracer_fork_body(fun, tspawn, tfork, tvfork, \ + tvforkdone, newpgrp); \ +} + +#define UNRELATED_TRACER_FORK_TEST(name,fun,tspawn,tfork,tvfork,tvforkdone) \ + UNRELATED_TRACER_FORK_TEST2(name,fun,tspawn,tfork,tvfork,tvforkdone,false) + +UNRELATED_TRACER_FORK_TEST(unrelated_tracer_fork1, "fork", false, false, false, false) +UNRELATED_TRACER_FORK_TEST(unrelated_tracer_fork2, "fork", false, true, false, false) +UNRELATED_TRACER_FORK_TEST(unrelated_tracer_fork3, "fork", false, false, true, false) +UNRELATED_TRACER_FORK_TEST(unrelated_tracer_fork4, "fork", false, true, true, false) +UNRELATED_TRACER_FORK_TEST(unrelated_tracer_fork5, "fork", false, false, false, true) +UNRELATED_TRACER_FORK_TEST(unrelated_tracer_fork6, "fork", false, true, false, true) +UNRELATED_TRACER_FORK_TEST(unrelated_tracer_fork7, "fork", false, false, true, true) +UNRELATED_TRACER_FORK_TEST(unrelated_tracer_fork8, "fork", false, true, true, true) +UNRELATED_TRACER_FORK_TEST(unrelated_tracer_fork9, "fork", true, false, false, false) +UNRELATED_TRACER_FORK_TEST(unrelated_tracer_fork10, "fork", true, true, false, false) +UNRELATED_TRACER_FORK_TEST(unrelated_tracer_fork11, "fork", true, false, true, false) +UNRELATED_TRACER_FORK_TEST(unrelated_tracer_fork12, "fork", true, true, true, false) +UNRELATED_TRACER_FORK_TEST(unrelated_tracer_fork13, "fork", true, false, false, true) +UNRELATED_TRACER_FORK_TEST(unrelated_tracer_fork14, "fork", true, true, false, true) +UNRELATED_TRACER_FORK_TEST(unrelated_tracer_fork15, "fork", true, false, true, true) +UNRELATED_TRACER_FORK_TEST(unrelated_tracer_fork16, "fork", true, true, true, true) + +UNRELATED_TRACER_FORK_TEST2(unrelated_tracer_fork_setpgid, "fork", true, true, true, true, true) + +UNRELATED_TRACER_FORK_TEST(unrelated_tracer_vfork1, "vfork", false, false, false, false) +UNRELATED_TRACER_FORK_TEST(unrelated_tracer_vfork2, "vfork", false, true, false, false) +UNRELATED_TRACER_FORK_TEST(unrelated_tracer_vfork3, "vfork", false, false, true, false) +UNRELATED_TRACER_FORK_TEST(unrelated_tracer_vfork4, "vfork", false, true, true, false) +UNRELATED_TRACER_FORK_TEST(unrelated_tracer_vfork5, "vfork", false, false, false, true) +UNRELATED_TRACER_FORK_TEST(unrelated_tracer_vfork6, "vfork", false, true, false, true) +UNRELATED_TRACER_FORK_TEST(unrelated_tracer_vfork7, "vfork", false, false, true, true) +UNRELATED_TRACER_FORK_TEST(unrelated_tracer_vfork8, "vfork", false, true, true, true) +UNRELATED_TRACER_FORK_TEST(unrelated_tracer_vfork9, "vfork", true, false, false, false) +UNRELATED_TRACER_FORK_TEST(unrelated_tracer_vfork10, "vfork", true, true, false, false) +UNRELATED_TRACER_FORK_TEST(unrelated_tracer_vfork11, "vfork", true, false, true, false) +UNRELATED_TRACER_FORK_TEST(unrelated_tracer_vfork12, "vfork", true, true, true, false) +UNRELATED_TRACER_FORK_TEST(unrelated_tracer_vfork13, "vfork", true, false, false, true) +UNRELATED_TRACER_FORK_TEST(unrelated_tracer_vfork14, "vfork", true, true, false, true) +UNRELATED_TRACER_FORK_TEST(unrelated_tracer_vfork15, "vfork", true, false, true, true) +UNRELATED_TRACER_FORK_TEST(unrelated_tracer_vfork16, "vfork", true, true, true, true) + +UNRELATED_TRACER_FORK_TEST2(unrelated_tracer_vfork_setpgid, "vfork", true, true, true, true, true) + +UNRELATED_TRACER_FORK_TEST(unrelated_tracer_posix_spawn1, "spawn", false, false, false, false) +UNRELATED_TRACER_FORK_TEST(unrelated_tracer_posix_spawn2, "spawn", false, true, false, false) +UNRELATED_TRACER_FORK_TEST(unrelated_tracer_posix_spawn3, "spawn", false, false, true, false) +UNRELATED_TRACER_FORK_TEST(unrelated_tracer_posix_spawn4, "spawn", false, true, true, false) +UNRELATED_TRACER_FORK_TEST(unrelated_tracer_posix_spawn5, "spawn", false, false, false, true) +UNRELATED_TRACER_FORK_TEST(unrelated_tracer_posix_spawn6, "spawn", false, true, false, true) +UNRELATED_TRACER_FORK_TEST(unrelated_tracer_posix_spawn7, "spawn", false, false, true, true) +UNRELATED_TRACER_FORK_TEST(unrelated_tracer_posix_spawn8, "spawn", false, true, true, true) +UNRELATED_TRACER_FORK_TEST(unrelated_tracer_posix_spawn9, "spawn", true, false, false, false) +UNRELATED_TRACER_FORK_TEST(unrelated_tracer_posix_spawn10, "spawn", true, true, false, false) +UNRELATED_TRACER_FORK_TEST(unrelated_tracer_posix_spawn11, "spawn", true, false, true, false) +UNRELATED_TRACER_FORK_TEST(unrelated_tracer_posix_spawn12, "spawn", true, true, true, false) +UNRELATED_TRACER_FORK_TEST(unrelated_tracer_posix_spawn13, "spawn", true, false, false, true) +UNRELATED_TRACER_FORK_TEST(unrelated_tracer_posix_spawn14, "spawn", true, true, false, true) +UNRELATED_TRACER_FORK_TEST(unrelated_tracer_posix_spawn15, "spawn", true, false, true, true) +UNRELATED_TRACER_FORK_TEST(unrelated_tracer_posix_spawn16, "spawn", true, true, true, true) + +UNRELATED_TRACER_FORK_TEST2(unrelated_tracer_posix_spawn_setpgid, "spawn", true, true, true, true, true) +#endif + +/// ---------------------------------------------------------------------------- + +#if defined(TWAIT_HAVE_PID) +static void +fork_detach_forker_body(const char *fn, bool kill_process) +{ + const int exitval = 5; + const int exitval2 = 0; /* Matches exit value from /bin/echo */ + const int sigval = SIGSTOP; + pid_t child, child2 = 0, wpid; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + ptrace_state_t state; + const int slen = sizeof(state); + ptrace_event_t event; + const int elen = sizeof(event); + + int op; + + char * const arg[] = { __UNCONST("/bin/echo"), NULL }; + + DPRINTF("Before forking process PID=%d\n", getpid()); + SYSCALL_REQUIRE((child = fork()) != -1); + if (child == 0) { + DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + DPRINTF("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + if (strcmp(fn, "spawn") == 0) { + FORKEE_ASSERT_EQ(posix_spawn(&child2, + arg[0], NULL, NULL, arg, NULL), 0); + } else { + if (strcmp(fn, "fork") == 0) { + FORKEE_ASSERT((child2 = fork()) != -1); + } else { + FORKEE_ASSERT((child2 = vfork()) != -1); + } + + if (child2 == 0) + _exit(exitval2); + } + + FORKEE_REQUIRE_SUCCESS + (wpid = TWAIT_GENERIC(child2, &status, 0), child2); + + forkee_status_exited(status, exitval2); + + DPRINTF("Before exiting of the child process\n"); + _exit(exitval); + } + DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + DPRINTF("Set EVENT_MASK for the child %d\n", child); + event.pe_set_event = PTRACE_POSIX_SPAWN | PTRACE_FORK | PTRACE_VFORK + | PTRACE_VFORK_DONE; + SYSCALL_REQUIRE(ptrace(PT_SET_EVENT_MASK, child, &event, elen) != -1); + + DPRINTF("Before resuming the child process where it left off and " + "without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + DPRINTF("Before calling %s() for the child %d\n", TWAIT_FNAME, child); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, SIGTRAP); + + SYSCALL_REQUIRE( + ptrace(PT_GET_PROCESS_STATE, child, &state, slen) != -1); + + if (strcmp(fn, "spawn") == 0) + op = PTRACE_POSIX_SPAWN; + else if (strcmp(fn, "fork") == 0) + op = PTRACE_FORK; + else + op = PTRACE_VFORK; + + ATF_REQUIRE_EQ(state.pe_report_event & op, op); + + child2 = state.pe_other_pid; + DPRINTF("Reported ptrace event with forkee %d\n", child2); + + if (strcmp(fn, "spawn") == 0 || strcmp(fn, "fork") == 0 || + strcmp(fn, "vfork") == 0) + op = kill_process ? PT_KILL : PT_DETACH; + else + op = PT_CONTINUE; + SYSCALL_REQUIRE(ptrace(op, child, (void *)1, 0) != -1); + + DPRINTF("Before calling %s() for the forkee %d of the child %d\n", + TWAIT_FNAME, child2, child); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child2, &status, 0), child2); + + validate_status_stopped(status, SIGTRAP); + + SYSCALL_REQUIRE( + ptrace(PT_GET_PROCESS_STATE, child2, &state, slen) != -1); + if (strcmp(fn, "spawn") == 0) + op = PTRACE_POSIX_SPAWN; + else if (strcmp(fn, "fork") == 0) + op = PTRACE_FORK; + else + op = PTRACE_VFORK; + + ATF_REQUIRE_EQ(state.pe_report_event & op, op); + ATF_REQUIRE_EQ(state.pe_other_pid, child); + + DPRINTF("Before resuming the forkee process where it left off " + "and without signal to be sent\n"); + SYSCALL_REQUIRE( + ptrace(PT_CONTINUE, child2, (void *)1, 0) != -1); + + if (strcmp(fn, "vforkdone") == 0) { + DPRINTF("Before calling %s() for the child %d\n", TWAIT_FNAME, + child); + TWAIT_REQUIRE_SUCCESS( + wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, SIGTRAP); + + SYSCALL_REQUIRE( + ptrace(PT_GET_PROCESS_STATE, child, &state, slen) != -1); + ATF_REQUIRE_EQ(state.pe_report_event, PTRACE_VFORK_DONE); + + child2 = state.pe_other_pid; + DPRINTF("Reported PTRACE_VFORK_DONE event with forkee %d\n", + child2); + + op = kill_process ? PT_KILL : PT_DETACH; + DPRINTF("Before resuming the child process where it left off " + "and without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(op, child, (void *)1, 0) != -1); + } + + DPRINTF("Before calling %s() for the forkee - expected exited\n", + TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child2, &status, 0), child2); + + validate_status_exited(status, exitval2); + + DPRINTF("Before calling %s() for the forkee - expected no process\n", + TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child2, &status, 0)); + + DPRINTF("Before calling %s() for the forkee - expected exited\n", + TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + if (kill_process) { + validate_status_signaled(status, SIGKILL, 0); + } else { + validate_status_exited(status, exitval); + } + + DPRINTF("Before calling %s() for the child - expected no process\n", + TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} + +#define FORK_DETACH_FORKER(name,event,kprocess) \ +ATF_TC(name); \ +ATF_TC_HEAD(name, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", "Verify %s " event, \ + kprocess ? "killed" : "detached"); \ +} \ + \ +ATF_TC_BODY(name, tc) \ +{ \ + \ + fork_detach_forker_body(event, kprocess); \ +} + +FORK_DETACH_FORKER(posix_spawn_detach_spawner, "spawn", false) +FORK_DETACH_FORKER(fork_detach_forker, "fork", false) +FORK_DETACH_FORKER(vfork_detach_vforker, "vfork", false) +FORK_DETACH_FORKER(vfork_detach_vforkerdone, "vforkdone", false) + +FORK_DETACH_FORKER(posix_spawn_kill_spawner, "spawn", true) +FORK_DETACH_FORKER(fork_kill_forker, "fork", true) +FORK_DETACH_FORKER(vfork_kill_vforker, "vfork", true) +FORK_DETACH_FORKER(vfork_kill_vforkerdone, "vforkdone", true) +#endif + +/// ---------------------------------------------------------------------------- + +#if defined(TWAIT_HAVE_PID) +static void +unrelated_tracer_fork_detach_forker_body(const char *fn, bool kill_process) +{ + const int sigval = SIGSTOP; + struct msg_fds parent_tracee, parent_tracer; + const int exitval = 10; + const int exitval2 = 0; /* This matched exit status from /bin/echo */ + pid_t tracee, tracer, wpid; + pid_t tracee2 = 0; + uint8_t msg = 0xde; /* dummy message for IPC based on pipe(2) */ +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + int op; + + struct ptrace_siginfo info; + ptrace_state_t state; + const int slen = sizeof(state); + ptrace_event_t event; + const int elen = sizeof(event); + + char * const arg[] = { __UNCONST("/bin/echo"), NULL }; + + DPRINTF("Spawn tracee\n"); + SYSCALL_REQUIRE(msg_open(&parent_tracee) == 0); + tracee = atf_utils_fork(); + if (tracee == 0) { + // Wait for parent to let us crash + CHILD_FROM_PARENT("exit tracee", parent_tracee, msg); + + DPRINTF("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + if (strcmp(fn, "spawn") == 0) { + FORKEE_ASSERT_EQ(posix_spawn(&tracee2, + arg[0], NULL, NULL, arg, NULL), 0); + } else { + if (strcmp(fn, "fork") == 0) { + FORKEE_ASSERT((tracee2 = fork()) != -1); + } else { + FORKEE_ASSERT((tracee2 = vfork()) != -1); + } + + if (tracee2 == 0) + _exit(exitval2); + } + + FORKEE_REQUIRE_SUCCESS + (wpid = TWAIT_GENERIC(tracee2, &status, 0), tracee2); + + forkee_status_exited(status, exitval2); + + DPRINTF("Before exiting of the child process\n"); + _exit(exitval); + } + + DPRINTF("Spawn debugger\n"); + SYSCALL_REQUIRE(msg_open(&parent_tracer) == 0); + tracer = atf_utils_fork(); + if (tracer == 0) { + /* Fork again and drop parent to reattach to PID 1 */ + tracer = atf_utils_fork(); + if (tracer != 0) + _exit(exitval); + + DPRINTF("Before calling PT_ATTACH from tracee %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_ATTACH, tracee, NULL, 0) != -1); + + /* Wait for tracee and assert that it was stopped w/ SIGSTOP */ + FORKEE_REQUIRE_SUCCESS( + wpid = TWAIT_GENERIC(tracee, &status, 0), tracee); + + forkee_status_stopped(status, SIGSTOP); + + DPRINTF("Before calling ptrace(2) with PT_GET_SIGINFO for the " + "traced process\n"); + SYSCALL_REQUIRE( + ptrace(PT_GET_SIGINFO, tracee, &info, sizeof(info)) != -1); + + DPRINTF("Signal traced to lwpid=%d\n", info.psi_lwpid); + DPRINTF("Signal properties: si_signo=%#x si_code=%#x " + "si_errno=%#x\n", info.psi_siginfo.si_signo, + info.psi_siginfo.si_code, info.psi_siginfo.si_errno); + + FORKEE_ASSERT_EQ(info.psi_siginfo.si_signo, SIGSTOP); + FORKEE_ASSERT_EQ(info.psi_siginfo.si_code, SI_USER); + + /* Resume tracee with PT_CONTINUE */ + FORKEE_ASSERT(ptrace(PT_CONTINUE, tracee, (void *)1, 0) != -1); + + /* Inform parent that tracer has attached to tracee */ + CHILD_TO_PARENT("tracer ready", parent_tracer, msg); + + /* Wait for parent to tell use that tracee should have exited */ + CHILD_FROM_PARENT("wait for tracee exit", parent_tracer, msg); + + /* Wait for tracee and assert that it exited */ + FORKEE_REQUIRE_SUCCESS( + wpid = TWAIT_GENERIC(tracee, &status, 0), tracee); + + forkee_status_stopped(status, sigval); + + DPRINTF("Before calling ptrace(2) with PT_GET_SIGINFO for the " + "traced process\n"); + SYSCALL_REQUIRE( + ptrace(PT_GET_SIGINFO, tracee, &info, sizeof(info)) != -1); + + DPRINTF("Signal traced to lwpid=%d\n", info.psi_lwpid); + DPRINTF("Signal properties: si_signo=%#x si_code=%#x " + "si_errno=%#x\n", info.psi_siginfo.si_signo, + info.psi_siginfo.si_code, info.psi_siginfo.si_errno); + + FORKEE_ASSERT_EQ(info.psi_siginfo.si_signo, sigval); + FORKEE_ASSERT_EQ(info.psi_siginfo.si_code, SI_LWP); + + DPRINTF("Set EVENT_MASK for the child %d\n", tracee); + event.pe_set_event = PTRACE_POSIX_SPAWN | PTRACE_FORK | PTRACE_VFORK + | PTRACE_VFORK_DONE; + SYSCALL_REQUIRE(ptrace(PT_SET_EVENT_MASK, tracee, &event, elen) != -1); + + DPRINTF("Before resuming the child process where it left off and " + "without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, tracee, (void *)1, 0) != -1); + + DPRINTF("Before calling %s() for the child %d\n", TWAIT_FNAME, tracee); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(tracee, &status, 0), tracee); + + validate_status_stopped(status, SIGTRAP); + + SYSCALL_REQUIRE( + ptrace(PT_GET_PROCESS_STATE, tracee, &state, slen) != -1); + + if (strcmp(fn, "spawn") == 0) + op = PTRACE_POSIX_SPAWN; + else if (strcmp(fn, "fork") == 0) + op = PTRACE_FORK; + else + op = PTRACE_VFORK; + + ATF_REQUIRE_EQ(state.pe_report_event & op, op); + + tracee2 = state.pe_other_pid; + DPRINTF("Reported ptrace event with forkee %d\n", tracee2); + if (strcmp(fn, "spawn") == 0 || strcmp(fn, "fork") == 0 || + strcmp(fn, "vfork") == 0) + op = kill_process ? PT_KILL : PT_DETACH; + else + op = PT_CONTINUE; + SYSCALL_REQUIRE(ptrace(op, tracee, (void *)1, 0) != -1); + + DPRINTF("Before calling %s() for the forkee %d of the tracee %d\n", + TWAIT_FNAME, tracee2, tracee); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(tracee2, &status, 0), tracee2); + + validate_status_stopped(status, SIGTRAP); + + SYSCALL_REQUIRE( + ptrace(PT_GET_PROCESS_STATE, tracee2, &state, slen) != -1); + if (strcmp(fn, "spawn") == 0) + op = PTRACE_POSIX_SPAWN; + else if (strcmp(fn, "fork") == 0) + op = PTRACE_FORK; + else + op = PTRACE_VFORK; + + ATF_REQUIRE_EQ(state.pe_report_event & op, op); + ATF_REQUIRE_EQ(state.pe_other_pid, tracee); + + DPRINTF("Before resuming the forkee process where it left off " + "and without signal to be sent\n"); + SYSCALL_REQUIRE( + ptrace(PT_CONTINUE, tracee2, (void *)1, 0) != -1); + + if (strcmp(fn, "vforkdone") == 0) { + DPRINTF("Before calling %s() for the child %d\n", TWAIT_FNAME, + tracee); + TWAIT_REQUIRE_SUCCESS( + wpid = TWAIT_GENERIC(tracee, &status, 0), tracee); + + validate_status_stopped(status, SIGTRAP); + + SYSCALL_REQUIRE( + ptrace(PT_GET_PROCESS_STATE, tracee, &state, slen) != -1); + ATF_REQUIRE_EQ(state.pe_report_event, PTRACE_VFORK_DONE); + + tracee2 = state.pe_other_pid; + DPRINTF("Reported PTRACE_VFORK_DONE event with forkee %d\n", + tracee2); + + op = kill_process ? PT_KILL : PT_DETACH; + DPRINTF("Before resuming the child process where it left off " + "and without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(op, tracee, (void *)1, 0) != -1); + } + + DPRINTF("Before calling %s() for the forkee - expected exited\n", + TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(tracee2, &status, 0), tracee2); + + validate_status_exited(status, exitval2); + + DPRINTF("Before calling %s() for the forkee - expected no process\n", + TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(tracee2, &status, 0)); + + if (kill_process) { + DPRINTF("Before calling %s() for the forkee - expected signaled\n", + TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(tracee, &status, 0), tracee); + + validate_status_signaled(status, SIGKILL, 0); + } + + /* Inform parent that tracer is exiting normally */ + CHILD_TO_PARENT("tracer done", parent_tracer, msg); + + DPRINTF("Before exiting of the tracer process\n"); + _exit(0 /* collect by initproc */); + } + + DPRINTF("Wait for the tracer process (direct child) to exit " + "calling %s()\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS( + wpid = TWAIT_GENERIC(tracer, &status, 0), tracer); + + validate_status_exited(status, exitval); + + DPRINTF("Wait for the non-exited tracee process with %s()\n", + TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS( + wpid = TWAIT_GENERIC(tracee, NULL, WNOHANG), 0); + + DPRINTF("Wait for the tracer to attach to the tracee\n"); + PARENT_FROM_CHILD("tracer ready", parent_tracer, msg); + + DPRINTF("Resume the tracee and let it crash\n"); + PARENT_TO_CHILD("exit tracee", parent_tracee, msg); + + DPRINTF("Resume the tracer and let it detect crashed tracee\n"); + PARENT_TO_CHILD("Message 2", parent_tracer, msg); + + DPRINTF("Wait for tracee to finish its job and exit - calling %s()\n", + TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(tracee, &status, 0), tracee); + + if (kill_process) { + validate_status_signaled(status, SIGKILL, 0); + } else { + validate_status_exited(status, exitval); + } + + DPRINTF("Await normal exit of tracer\n"); + PARENT_FROM_CHILD("tracer done", parent_tracer, msg); + + msg_close(&parent_tracer); + msg_close(&parent_tracee); +} + +#define UNRELATED_TRACER_FORK_DETACH_FORKER(name,event,kprocess) \ +ATF_TC(name); \ +ATF_TC_HEAD(name, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", "Verify %s " event, \ + kprocess ? "killed" : "detached"); \ +} \ + \ +ATF_TC_BODY(name, tc) \ +{ \ + \ + unrelated_tracer_fork_detach_forker_body(event, kprocess); \ +} + +UNRELATED_TRACER_FORK_DETACH_FORKER(unrelated_tracer_posix_spawn_detach_spawner, "spawn", false) +UNRELATED_TRACER_FORK_DETACH_FORKER(unrelated_tracer_fork_detach_forker, "fork", false) +UNRELATED_TRACER_FORK_DETACH_FORKER(unrelated_tracer_vfork_detach_vforker, "vfork", false) +UNRELATED_TRACER_FORK_DETACH_FORKER(unrelated_tracer_vfork_detach_vforkerdone, "vforkdone", false) + +UNRELATED_TRACER_FORK_DETACH_FORKER(unrelated_tracer_posix_spawn_kill_spawner, "spawn", true) +UNRELATED_TRACER_FORK_DETACH_FORKER(unrelated_tracer_fork_kill_forker, "fork", true) +UNRELATED_TRACER_FORK_DETACH_FORKER(unrelated_tracer_vfork_kill_vforker, "vfork", true) +UNRELATED_TRACER_FORK_DETACH_FORKER(unrelated_tracer_vfork_kill_vforkerdone, "vforkdone", true) +#endif + +/// ---------------------------------------------------------------------------- + +static void +traceme_vfork_fork_body(pid_t (*fn)(void)) +{ + const int exitval = 5; + const int exitval2 = 15; + pid_t child, child2 = 0, wpid; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + + DPRINTF("Before forking process PID=%d\n", getpid()); + SYSCALL_REQUIRE((child = vfork()) != -1); + if (child == 0) { + DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + FORKEE_ASSERT((child2 = (fn)()) != -1); + + if (child2 == 0) + _exit(exitval2); + + FORKEE_REQUIRE_SUCCESS + (wpid = TWAIT_GENERIC(child2, &status, 0), child2); + + forkee_status_exited(status, exitval2); + + DPRINTF("Before exiting of the child process\n"); + _exit(exitval); + } + DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + DPRINTF("Before calling %s() for the child - expected exited\n", + TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_exited(status, exitval); + + DPRINTF("Before calling %s() for the child - expected no process\n", + TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} + +#define TRACEME_VFORK_FORK_TEST(name,fun) \ +ATF_TC(name); \ +ATF_TC_HEAD(name, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", "Verify " #fun "(2) " \ + "called from vfork(2)ed child"); \ +} \ + \ +ATF_TC_BODY(name, tc) \ +{ \ + \ + traceme_vfork_fork_body(fun); \ +} + +TRACEME_VFORK_FORK_TEST(traceme_vfork_fork, fork) +TRACEME_VFORK_FORK_TEST(traceme_vfork_vfork, vfork) + +/// ---------------------------------------------------------------------------- + +#if defined(TWAIT_HAVE_PID) +static void +fork2_body(const char *fn, bool masked, bool ignored) +{ + const int exitval = 5; + const int exitval2 = 0; /* Match exit status from /bin/echo */ + const int sigval = SIGSTOP; + pid_t child, child2 = 0, wpid; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + ptrace_state_t state; + const int slen = sizeof(state); + ptrace_event_t event; + const int elen = sizeof(event); + struct sigaction sa; + struct ptrace_siginfo info; + sigset_t intmask; + struct kinfo_proc2 kp; + size_t len = sizeof(kp); + + int name[6]; + const size_t namelen = __arraycount(name); + sigset_t set; + ki_sigset_t kp_sigmask; + ki_sigset_t kp_sigignore; + + char * const arg[] = { __UNCONST("/bin/echo"), NULL }; + + DPRINTF("Before forking process PID=%d\n", getpid()); + SYSCALL_REQUIRE((child = fork()) != -1); + if (child == 0) { + DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + if (masked) { + sigemptyset(&intmask); + sigaddset(&intmask, SIGTRAP); + sigprocmask(SIG_BLOCK, &intmask, NULL); + } + + if (ignored) { + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = SIG_IGN; + sigemptyset(&sa.sa_mask); + FORKEE_ASSERT(sigaction(SIGTRAP, &sa, NULL) != -1); + } + + DPRINTF("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + if (strcmp(fn, "spawn") == 0) { + FORKEE_ASSERT_EQ(posix_spawn(&child2, + arg[0], NULL, NULL, arg, NULL), 0); + } else { + if (strcmp(fn, "fork") == 0) { + FORKEE_ASSERT((child2 = fork()) != -1); + } else { + FORKEE_ASSERT((child2 = vfork()) != -1); + } + if (child2 == 0) + _exit(exitval2); + } + + FORKEE_REQUIRE_SUCCESS + (wpid = TWAIT_GENERIC(child2, &status, 0), child2); + + forkee_status_exited(status, exitval2); + + DPRINTF("Before exiting of the child process\n"); + _exit(exitval); + } + DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + DPRINTF("Before calling ptrace(2) with PT_GET_SIGINFO for child\n"); + SYSCALL_REQUIRE( + ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1); + + DPRINTF("Before checking siginfo_t\n"); + ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, sigval); + ATF_REQUIRE_EQ(info.psi_siginfo.si_code, SI_LWP); + + name[0] = CTL_KERN, + name[1] = KERN_PROC2, + name[2] = KERN_PROC_PID; + name[3] = child; + name[4] = sizeof(kp); + name[5] = 1; + + FORKEE_ASSERT_EQ(sysctl(name, namelen, &kp, &len, NULL, 0), 0); + + kp_sigmask = kp.p_sigmask; + kp_sigignore = kp.p_sigignore; + + DPRINTF("Set 0%s%s%s%s in EVENT_MASK for the child %d\n", + strcmp(fn, "spawn") == 0 ? "|PTRACE_POSIX_SPAWN" : "", + strcmp(fn, "fork") == 0 ? "|PTRACE_FORK" : "", + strcmp(fn, "vfork") == 0 ? "|PTRACE_VFORK" : "", + strcmp(fn, "vforkdone") == 0 ? "|PTRACE_VFORK_DONE" : "", child); + event.pe_set_event = 0; + if (strcmp(fn, "spawn") == 0) + event.pe_set_event |= PTRACE_POSIX_SPAWN; + if (strcmp(fn, "fork") == 0) + event.pe_set_event |= PTRACE_FORK; + if (strcmp(fn, "vfork") == 0) + event.pe_set_event |= PTRACE_VFORK; + if (strcmp(fn, "vforkdone") == 0) + event.pe_set_event |= PTRACE_VFORK_DONE; + SYSCALL_REQUIRE(ptrace(PT_SET_EVENT_MASK, child, &event, elen) != -1); + + /* + * Ignore interception of the SIGCHLD signals. + * + * SIGCHLD once blocked is discarded by the kernel as it has the + * SA_IGNORE property. During the fork(2) operation all signals can be + * shortly blocked and missed (unless there is a registered signal + * handler in the traced child). This leads to a race in this test if + * there would be an intention to catch SIGCHLD. + */ + sigemptyset(&set); + sigaddset(&set, SIGCHLD); + SYSCALL_REQUIRE(ptrace(PT_SET_SIGPASS, child, &set, sizeof(set)) != -1); + + DPRINTF("Before resuming the child process where it left off and " + "without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + if (strcmp(fn, "spawn") == 0 || strcmp(fn, "fork") == 0 || + strcmp(fn, "vfork") == 0) { + DPRINTF("Before calling %s() for the child %d\n", TWAIT_FNAME, + child); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), + child); + + validate_status_stopped(status, SIGTRAP); + + ATF_REQUIRE_EQ(sysctl(name, namelen, &kp, &len, NULL, 0), 0); + + if (masked) { + DPRINTF("kp_sigmask=" + "%#02" PRIx32 "%02" PRIx32 "%02" PRIx32 "%02" + PRIx32 "\n", + kp_sigmask.__bits[0], kp_sigmask.__bits[1], + kp_sigmask.__bits[2], kp_sigmask.__bits[3]); + + DPRINTF("kp.p_sigmask=" + "%#02" PRIx32 "%02" PRIx32 "%02" PRIx32 "%02" + PRIx32 "\n", + kp.p_sigmask.__bits[0], kp.p_sigmask.__bits[1], + kp.p_sigmask.__bits[2], kp.p_sigmask.__bits[3]); + + ATF_REQUIRE(sigismember((sigset_t *)&kp.p_sigmask, + SIGTRAP)); + } + + if (ignored) { + DPRINTF("kp_sigignore=" + "%#02" PRIx32 "%02" PRIx32 "%02" PRIx32 "%02" + PRIx32 "\n", + kp_sigignore.__bits[0], kp_sigignore.__bits[1], + kp_sigignore.__bits[2], kp_sigignore.__bits[3]); + + DPRINTF("kp.p_sigignore=" + "%#02" PRIx32 "%02" PRIx32 "%02" PRIx32 "%02" + PRIx32 "\n", + kp.p_sigignore.__bits[0], kp.p_sigignore.__bits[1], + kp.p_sigignore.__bits[2], kp.p_sigignore.__bits[3]); + + ATF_REQUIRE(sigismember((sigset_t *)&kp.p_sigignore, + SIGTRAP)); + } + + SYSCALL_REQUIRE( + ptrace(PT_GET_PROCESS_STATE, child, &state, slen) != -1); + if (strcmp(fn, "spawn") == 0) { + ATF_REQUIRE_EQ( + state.pe_report_event & PTRACE_POSIX_SPAWN, + PTRACE_POSIX_SPAWN); + } + if (strcmp(fn, "fork") == 0) { + ATF_REQUIRE_EQ(state.pe_report_event & PTRACE_FORK, + PTRACE_FORK); + } + if (strcmp(fn, "vfork") == 0) { + ATF_REQUIRE_EQ(state.pe_report_event & PTRACE_VFORK, + PTRACE_VFORK); + } + + child2 = state.pe_other_pid; + DPRINTF("Reported ptrace event with forkee %d\n", child2); + + DPRINTF("Before calling %s() for the forkee %d of the child " + "%d\n", TWAIT_FNAME, child2, child); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child2, &status, 0), + child2); + + validate_status_stopped(status, SIGTRAP); + + name[3] = child2; + ATF_REQUIRE_EQ(sysctl(name, namelen, &kp, &len, NULL, 0), 0); + + if (masked) { + DPRINTF("kp_sigmask=" + "%#02" PRIx32 "%02" PRIx32 "%02" PRIx32 "%02" + PRIx32 "\n", + kp_sigmask.__bits[0], kp_sigmask.__bits[1], + kp_sigmask.__bits[2], kp_sigmask.__bits[3]); + + DPRINTF("kp.p_sigmask=" + "%#02" PRIx32 "%02" PRIx32 "%02" PRIx32 "%02" + PRIx32 "\n", + kp.p_sigmask.__bits[0], kp.p_sigmask.__bits[1], + kp.p_sigmask.__bits[2], kp.p_sigmask.__bits[3]); + + ATF_REQUIRE(sigismember((sigset_t *)&kp.p_sigmask, + SIGTRAP)); + } + + if (ignored) { + DPRINTF("kp_sigignore=" + "%#02" PRIx32 "%02" PRIx32 "%02" PRIx32 "%02" + PRIx32 "\n", + kp_sigignore.__bits[0], kp_sigignore.__bits[1], + kp_sigignore.__bits[2], kp_sigignore.__bits[3]); + + DPRINTF("kp.p_sigignore=" + "%#02" PRIx32 "%02" PRIx32 "%02" PRIx32 "%02" + PRIx32 "\n", + kp.p_sigignore.__bits[0], kp.p_sigignore.__bits[1], + kp.p_sigignore.__bits[2], kp.p_sigignore.__bits[3]); + + ATF_REQUIRE(sigismember((sigset_t *)&kp.p_sigignore, + SIGTRAP)); + } + + SYSCALL_REQUIRE( + ptrace(PT_GET_PROCESS_STATE, child2, &state, slen) != -1); + if (strcmp(fn, "spawn") == 0) { + ATF_REQUIRE_EQ( + state.pe_report_event & PTRACE_POSIX_SPAWN, + PTRACE_POSIX_SPAWN); + } + if (strcmp(fn, "fork") == 0) { + ATF_REQUIRE_EQ(state.pe_report_event & PTRACE_FORK, + PTRACE_FORK); + } + if (strcmp(fn, "vfork") == 0) { + ATF_REQUIRE_EQ(state.pe_report_event & PTRACE_VFORK, + PTRACE_VFORK); + } + + ATF_REQUIRE_EQ(state.pe_other_pid, child); + + DPRINTF("Before resuming the forkee process where it left off " + "and without signal to be sent\n"); + SYSCALL_REQUIRE( + ptrace(PT_CONTINUE, child2, (void *)1, 0) != -1); + + DPRINTF("Before resuming the child process where it left off " + "and without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + } + + if (strcmp(fn, "vforkdone") == 0) { + DPRINTF("Before calling %s() for the child %d\n", TWAIT_FNAME, + child); + TWAIT_REQUIRE_SUCCESS( + wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, SIGTRAP); + + name[3] = child; + ATF_REQUIRE_EQ(sysctl(name, namelen, &kp, &len, NULL, 0), 0); + + /* + * SIGCHLD is now pending in the signal queue and + * the kernel presents it to userland as a masked signal. + */ + sigdelset((sigset_t *)&kp.p_sigmask, SIGCHLD); + + if (masked) { + DPRINTF("kp_sigmask=" + "%#02" PRIx32 "%02" PRIx32 "%02" PRIx32 "%02" + PRIx32 "\n", + kp_sigmask.__bits[0], kp_sigmask.__bits[1], + kp_sigmask.__bits[2], kp_sigmask.__bits[3]); + + DPRINTF("kp.p_sigmask=" + "%#02" PRIx32 "%02" PRIx32 "%02" PRIx32 "%02" + PRIx32 "\n", + kp.p_sigmask.__bits[0], kp.p_sigmask.__bits[1], + kp.p_sigmask.__bits[2], kp.p_sigmask.__bits[3]); + + ATF_REQUIRE(sigismember((sigset_t *)&kp.p_sigmask, + SIGTRAP)); + } + + if (ignored) { + DPRINTF("kp_sigignore=" + "%#02" PRIx32 "%02" PRIx32 "%02" PRIx32 "%02" + PRIx32 "\n", + kp_sigignore.__bits[0], kp_sigignore.__bits[1], + kp_sigignore.__bits[2], kp_sigignore.__bits[3]); + + DPRINTF("kp.p_sigignore=" + "%#02" PRIx32 "%02" PRIx32 "%02" PRIx32 "%02" + PRIx32 "\n", + kp.p_sigignore.__bits[0], kp.p_sigignore.__bits[1], + kp.p_sigignore.__bits[2], kp.p_sigignore.__bits[3]); + + ATF_REQUIRE(sigismember((sigset_t *)&kp.p_sigignore, + SIGTRAP)); + } + + SYSCALL_REQUIRE( + ptrace(PT_GET_PROCESS_STATE, child, &state, slen) != -1); + ATF_REQUIRE_EQ(state.pe_report_event, PTRACE_VFORK_DONE); + + child2 = state.pe_other_pid; + DPRINTF("Reported PTRACE_VFORK_DONE event with forkee %d\n", + child2); + + DPRINTF("Before resuming the child process where it left off " + "and without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + } + + if (strcmp(fn, "spawn") == 0 || strcmp(fn, "fork") == 0 || + strcmp(fn, "vfork") == 0) { + DPRINTF("Before calling %s() for the forkee - expected exited" + "\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS( + wpid = TWAIT_GENERIC(child2, &status, 0), child2); + + validate_status_exited(status, exitval2); + + DPRINTF("Before calling %s() for the forkee - expected no " + "process\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, + wpid = TWAIT_GENERIC(child2, &status, 0)); + } + + DPRINTF("Before calling %s() for the child - expected exited\n", + TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_exited(status, exitval); + + DPRINTF("Before calling %s() for the child - expected no process\n", + TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} + +#define FORK2_TEST(name,fn,masked,ignored) \ +ATF_TC(name); \ +ATF_TC_HEAD(name, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", "Verify that " fn " is caught " \ + "regardless of signal %s%s", \ + masked ? "masked" : "", ignored ? "ignored" : ""); \ +} \ + \ +ATF_TC_BODY(name, tc) \ +{ \ + \ + fork2_body(fn, masked, ignored); \ +} + +FORK2_TEST(posix_spawn_signalmasked, "spawn", true, false) +FORK2_TEST(posix_spawn_signalignored, "spawn", false, true) +FORK2_TEST(fork_signalmasked, "fork", true, false) +FORK2_TEST(fork_signalignored, "fork", false, true) +FORK2_TEST(vfork_signalmasked, "vfork", true, false) +FORK2_TEST(vfork_signalignored, "vfork", false, true) +FORK2_TEST(vforkdone_signalmasked, "vforkdone", true, false) +FORK2_TEST(vforkdone_signalignored, "vforkdone", false, true) +#endif + +#define ATF_TP_ADD_TCS_PTRACE_WAIT_FORK() \ + ATF_TP_ADD_TC(tp, fork1); \ + ATF_TP_ADD_TC_HAVE_PID(tp, fork2); \ + ATF_TP_ADD_TC_HAVE_PID(tp, fork3); \ + ATF_TP_ADD_TC_HAVE_PID(tp, fork4); \ + ATF_TP_ADD_TC(tp, fork5); \ + ATF_TP_ADD_TC_HAVE_PID(tp, fork6); \ + ATF_TP_ADD_TC_HAVE_PID(tp, fork7); \ + ATF_TP_ADD_TC_HAVE_PID(tp, fork8); \ + ATF_TP_ADD_TC(tp, fork9); \ + ATF_TP_ADD_TC_HAVE_PID(tp, fork10); \ + ATF_TP_ADD_TC_HAVE_PID(tp, fork11); \ + ATF_TP_ADD_TC_HAVE_PID(tp, fork12); \ + ATF_TP_ADD_TC(tp, fork13); \ + ATF_TP_ADD_TC_HAVE_PID(tp, fork14); \ + ATF_TP_ADD_TC_HAVE_PID(tp, fork15); \ + ATF_TP_ADD_TC_HAVE_PID(tp, fork16); \ + ATF_TP_ADD_TC_HAVE_PID(tp, fork_setpgid); \ + ATF_TP_ADD_TC(tp, vfork1); \ + ATF_TP_ADD_TC_HAVE_PID(tp, vfork2); \ + ATF_TP_ADD_TC_HAVE_PID(tp, vfork3); \ + ATF_TP_ADD_TC_HAVE_PID(tp, vfork4); \ + ATF_TP_ADD_TC(tp, vfork5); \ + ATF_TP_ADD_TC_HAVE_PID(tp, vfork6); \ + ATF_TP_ADD_TC_HAVE_PID(tp, vfork7); \ + ATF_TP_ADD_TC_HAVE_PID(tp, vfork8); \ + ATF_TP_ADD_TC(tp, vfork9); \ + ATF_TP_ADD_TC_HAVE_PID(tp, vfork10); \ + ATF_TP_ADD_TC_HAVE_PID(tp, vfork11); \ + ATF_TP_ADD_TC_HAVE_PID(tp, vfork12); \ + ATF_TP_ADD_TC(tp, vfork13); \ + ATF_TP_ADD_TC_HAVE_PID(tp, vfork14); \ + ATF_TP_ADD_TC_HAVE_PID(tp, vfork15); \ + ATF_TP_ADD_TC_HAVE_PID(tp, vfork16); \ + ATF_TP_ADD_TC_HAVE_PID(tp, vfork_setpgid); \ + ATF_TP_ADD_TC(tp, posix_spawn1); \ + ATF_TP_ADD_TC(tp, posix_spawn2); \ + ATF_TP_ADD_TC(tp, posix_spawn3); \ + ATF_TP_ADD_TC(tp, posix_spawn4); \ + ATF_TP_ADD_TC(tp, posix_spawn5); \ + ATF_TP_ADD_TC(tp, posix_spawn6); \ + ATF_TP_ADD_TC(tp, posix_spawn7); \ + ATF_TP_ADD_TC(tp, posix_spawn8); \ + ATF_TP_ADD_TC_HAVE_PID(tp, posix_spawn9); \ + ATF_TP_ADD_TC_HAVE_PID(tp, posix_spawn10); \ + ATF_TP_ADD_TC_HAVE_PID(tp, posix_spawn11); \ + ATF_TP_ADD_TC_HAVE_PID(tp, posix_spawn12); \ + ATF_TP_ADD_TC_HAVE_PID(tp, posix_spawn13); \ + ATF_TP_ADD_TC_HAVE_PID(tp, posix_spawn14); \ + ATF_TP_ADD_TC_HAVE_PID(tp, posix_spawn15); \ + ATF_TP_ADD_TC_HAVE_PID(tp, posix_spawn16); \ + ATF_TP_ADD_TC_HAVE_PID(tp, posix_spawn_setpgid); \ + ATF_TP_ADD_TC_HAVE_PID(tp, unrelated_tracer_fork1); \ + ATF_TP_ADD_TC_HAVE_PID(tp, unrelated_tracer_fork2); \ + ATF_TP_ADD_TC_HAVE_PID(tp, unrelated_tracer_fork3); \ + ATF_TP_ADD_TC_HAVE_PID(tp, unrelated_tracer_fork4); \ + ATF_TP_ADD_TC_HAVE_PID(tp, unrelated_tracer_fork5); \ + ATF_TP_ADD_TC_HAVE_PID(tp, unrelated_tracer_fork6); \ + ATF_TP_ADD_TC_HAVE_PID(tp, unrelated_tracer_fork7); \ + ATF_TP_ADD_TC_HAVE_PID(tp, unrelated_tracer_fork8); \ + ATF_TP_ADD_TC_HAVE_PID(tp, unrelated_tracer_fork9); \ + ATF_TP_ADD_TC_HAVE_PID(tp, unrelated_tracer_fork10); \ + ATF_TP_ADD_TC_HAVE_PID(tp, unrelated_tracer_fork11); \ + ATF_TP_ADD_TC_HAVE_PID(tp, unrelated_tracer_fork12); \ + ATF_TP_ADD_TC_HAVE_PID(tp, unrelated_tracer_fork13); \ + ATF_TP_ADD_TC_HAVE_PID(tp, unrelated_tracer_fork14); \ + ATF_TP_ADD_TC_HAVE_PID(tp, unrelated_tracer_fork15); \ + ATF_TP_ADD_TC_HAVE_PID(tp, unrelated_tracer_fork16); \ + ATF_TP_ADD_TC_HAVE_PID(tp, unrelated_tracer_fork_setpgid); \ + ATF_TP_ADD_TC_HAVE_PID(tp, unrelated_tracer_vfork1); \ + ATF_TP_ADD_TC_HAVE_PID(tp, unrelated_tracer_vfork2); \ + ATF_TP_ADD_TC_HAVE_PID(tp, unrelated_tracer_vfork3); \ + ATF_TP_ADD_TC_HAVE_PID(tp, unrelated_tracer_vfork4); \ + ATF_TP_ADD_TC_HAVE_PID(tp, unrelated_tracer_vfork5); \ + ATF_TP_ADD_TC_HAVE_PID(tp, unrelated_tracer_vfork6); \ + ATF_TP_ADD_TC_HAVE_PID(tp, unrelated_tracer_vfork7); \ + ATF_TP_ADD_TC_HAVE_PID(tp, unrelated_tracer_vfork8); \ + ATF_TP_ADD_TC_HAVE_PID(tp, unrelated_tracer_vfork9); \ + ATF_TP_ADD_TC_HAVE_PID(tp, unrelated_tracer_vfork10); \ + ATF_TP_ADD_TC_HAVE_PID(tp, unrelated_tracer_vfork11); \ + ATF_TP_ADD_TC_HAVE_PID(tp, unrelated_tracer_vfork12); \ + ATF_TP_ADD_TC_HAVE_PID(tp, unrelated_tracer_vfork13); \ + ATF_TP_ADD_TC_HAVE_PID(tp, unrelated_tracer_vfork14); \ + ATF_TP_ADD_TC_HAVE_PID(tp, unrelated_tracer_vfork15); \ + ATF_TP_ADD_TC_HAVE_PID(tp, unrelated_tracer_vfork16); \ + ATF_TP_ADD_TC_HAVE_PID(tp, unrelated_tracer_vfork_setpgid); \ + ATF_TP_ADD_TC_HAVE_PID(tp, unrelated_tracer_posix_spawn1); \ + ATF_TP_ADD_TC_HAVE_PID(tp, unrelated_tracer_posix_spawn2); \ + ATF_TP_ADD_TC_HAVE_PID(tp, unrelated_tracer_posix_spawn3); \ + ATF_TP_ADD_TC_HAVE_PID(tp, unrelated_tracer_posix_spawn4); \ + ATF_TP_ADD_TC_HAVE_PID(tp, unrelated_tracer_posix_spawn5); \ + ATF_TP_ADD_TC_HAVE_PID(tp, unrelated_tracer_posix_spawn6); \ + ATF_TP_ADD_TC_HAVE_PID(tp, unrelated_tracer_posix_spawn7); \ + ATF_TP_ADD_TC_HAVE_PID(tp, unrelated_tracer_posix_spawn8); \ + ATF_TP_ADD_TC_HAVE_PID(tp, unrelated_tracer_posix_spawn9); \ + ATF_TP_ADD_TC_HAVE_PID(tp, unrelated_tracer_posix_spawn10); \ + ATF_TP_ADD_TC_HAVE_PID(tp, unrelated_tracer_posix_spawn11); \ + ATF_TP_ADD_TC_HAVE_PID(tp, unrelated_tracer_posix_spawn12); \ + ATF_TP_ADD_TC_HAVE_PID(tp, unrelated_tracer_posix_spawn13); \ + ATF_TP_ADD_TC_HAVE_PID(tp, unrelated_tracer_posix_spawn14); \ + ATF_TP_ADD_TC_HAVE_PID(tp, unrelated_tracer_posix_spawn15); \ + ATF_TP_ADD_TC_HAVE_PID(tp, unrelated_tracer_posix_spawn16); \ + ATF_TP_ADD_TC_HAVE_PID(tp, unrelated_tracer_posix_spawn_setpgid); \ + ATF_TP_ADD_TC_HAVE_PID(tp, posix_spawn_detach_spawner); \ + ATF_TP_ADD_TC_HAVE_PID(tp, fork_detach_forker); \ + ATF_TP_ADD_TC_HAVE_PID(tp, vfork_detach_vforker); \ + ATF_TP_ADD_TC_HAVE_PID(tp, vfork_detach_vforkerdone); \ + ATF_TP_ADD_TC_HAVE_PID(tp, posix_spawn_kill_spawner); \ + ATF_TP_ADD_TC_HAVE_PID(tp, fork_kill_forker); \ + ATF_TP_ADD_TC_HAVE_PID(tp, vfork_kill_vforker); \ + ATF_TP_ADD_TC_HAVE_PID(tp, vfork_kill_vforkerdone); \ + ATF_TP_ADD_TC_HAVE_PID(tp, unrelated_tracer_posix_spawn_detach_spawner); \ + ATF_TP_ADD_TC_HAVE_PID(tp, unrelated_tracer_fork_detach_forker); \ + ATF_TP_ADD_TC_HAVE_PID(tp, unrelated_tracer_vfork_detach_vforker); \ + ATF_TP_ADD_TC_HAVE_PID(tp, unrelated_tracer_vfork_detach_vforkerdone); \ + ATF_TP_ADD_TC_HAVE_PID(tp, unrelated_tracer_posix_spawn_kill_spawner); \ + ATF_TP_ADD_TC_HAVE_PID(tp, unrelated_tracer_fork_kill_forker); \ + ATF_TP_ADD_TC_HAVE_PID(tp, unrelated_tracer_vfork_kill_vforker); \ + ATF_TP_ADD_TC_HAVE_PID(tp, unrelated_tracer_vfork_kill_vforkerdone); \ + ATF_TP_ADD_TC(tp, traceme_vfork_fork); \ + ATF_TP_ADD_TC(tp, traceme_vfork_vfork); \ + ATF_TP_ADD_TC_HAVE_PID(tp, posix_spawn_signalmasked); \ + ATF_TP_ADD_TC_HAVE_PID(tp, posix_spawn_signalignored); \ + ATF_TP_ADD_TC_HAVE_PID(tp, fork_signalmasked); \ + ATF_TP_ADD_TC_HAVE_PID(tp, fork_signalignored); \ + ATF_TP_ADD_TC_HAVE_PID(tp, vfork_signalmasked); \ + ATF_TP_ADD_TC_HAVE_PID(tp, vfork_signalignored); \ + ATF_TP_ADD_TC_HAVE_PID(tp, vforkdone_signalmasked); \ + ATF_TP_ADD_TC_HAVE_PID(tp, vforkdone_signalignored); diff --git a/lib/libc/sys/t_ptrace_i386_wait.h b/lib/libc/sys/t_ptrace_i386_wait.h new file mode 100644 --- /dev/null +++ b/lib/libc/sys/t_ptrace_i386_wait.h @@ -0,0 +1,108 @@ +/* $NetBSD: t_ptrace_i386_wait.h,v 1.10 2020/01/08 17:23:15 mgorny Exp $ */ + +/*- + * Copyright (c) 2016, 2017, 2018, 2019 The NetBSD Foundation, Inc. + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +#if defined(__i386__) +ATF_TC(i386_regs1); +ATF_TC_HEAD(i386_regs1, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Call PT_GETREGS and iterate over General Purpose registers"); +} + +ATF_TC_BODY(i386_regs1, tc) +{ + const int exitval = 5; + const int sigval = SIGSTOP; + pid_t child, wpid; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + struct reg r; + + DPRINTF("Before forking process PID=%d\n", getpid()); + SYSCALL_REQUIRE((child = fork()) != -1); + if (child == 0) { + DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + DPRINTF("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + DPRINTF("Before exiting of the child process\n"); + _exit(exitval); + } + DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + DPRINTF("Call GETREGS for the child process\n"); + SYSCALL_REQUIRE(ptrace(PT_GETREGS, child, &r, 0) != -1); + + DPRINTF("EAX=%#" PRIxREGISTER "\n", r.r_eax); + DPRINTF("EBX=%#" PRIxREGISTER "\n", r.r_ebx); + DPRINTF("ECX=%#" PRIxREGISTER "\n", r.r_ecx); + DPRINTF("EDX=%#" PRIxREGISTER "\n", r.r_edx); + + DPRINTF("ESP=%#" PRIxREGISTER "\n", r.r_esp); + DPRINTF("EBP=%#" PRIxREGISTER "\n", r.r_ebp); + + DPRINTF("ESI=%#" PRIxREGISTER "\n", r.r_esi); + DPRINTF("EDI=%#" PRIxREGISTER "\n", r.r_edi); + + DPRINTF("EIP=%#" PRIxREGISTER "\n", r.r_eip); + + DPRINTF("EFLAGS=%#" PRIxREGISTER "\n", r.r_eflags); + + DPRINTF("CS=%#" PRIxREGISTER "\n", r.r_cs); + DPRINTF("SS=%#" PRIxREGISTER "\n", r.r_ss); + DPRINTF("DS=%#" PRIxREGISTER "\n", r.r_ds); + DPRINTF("ES=%#" PRIxREGISTER "\n", r.r_es); + DPRINTF("FS=%#" PRIxREGISTER "\n", r.r_fs); + DPRINTF("GS=%#" PRIxREGISTER "\n", r.r_gs); + + DPRINTF("Before resuming the child process where it left off and " + "without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_exited(status, exitval); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} + +#define ATF_TP_ADD_TCS_PTRACE_WAIT_I386() \ + ATF_TP_ADD_TC_HAVE_GPREGS(tp, i386_regs1); +#else +#define ATF_TP_ADD_TCS_PTRACE_WAIT_I386() +#endif diff --git a/lib/libc/sys/t_ptrace_kill_wait.h b/lib/libc/sys/t_ptrace_kill_wait.h new file mode 100644 --- /dev/null +++ b/lib/libc/sys/t_ptrace_kill_wait.h @@ -0,0 +1,99 @@ +/* $NetBSD: t_ptrace_kill_wait.h,v 1.1 2020/05/04 21:55:12 kamil Exp $ */ + +/*- + * Copyright (c) 2016, 2017, 2018, 2019, 2020 The NetBSD Foundation, Inc. + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +static void +ptrace_kill(const char *type) +{ + const int sigval = SIGSTOP; + pid_t child, wpid; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + + DPRINTF("Before forking process PID=%d\n", getpid()); + SYSCALL_REQUIRE((child = fork()) != -1); + if (child == 0) { + DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + DPRINTF("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + /* NOTREACHED */ + FORKEE_ASSERTX(0 && + "Child should be terminated by a signal from its parent"); + } + DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + DPRINTF("Before killing the child process with %s\n", type); + if (strcmp(type, "ptrace(PT_KILL)") == 0) { + SYSCALL_REQUIRE(ptrace(PT_KILL, child, (void*)1, 0) != -1); + } else if (strcmp(type, "kill(SIGKILL)") == 0) { + kill(child, SIGKILL); + } else if (strcmp(type, "killpg(SIGKILL)") == 0) { + setpgid(child, 0); + killpg(getpgid(child), SIGKILL); + } + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_signaled(status, SIGKILL, 0); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} + +#define PTRACE_KILL(test, type) \ +ATF_TC(test); \ +ATF_TC_HEAD(test, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", \ + "Verify killing the child with " type); \ +} \ + \ +ATF_TC_BODY(test, tc) \ +{ \ + \ + ptrace_kill(type); \ +} + +// PT_CONTINUE with SIGKILL is covered by traceme_sendsignal_simple1 +PTRACE_KILL(kill1, "ptrace(PT_KILL)") +PTRACE_KILL(kill2, "kill(SIGKILL)") +PTRACE_KILL(kill3, "killpg(SIGKILL)") + +#define ATF_TP_ADD_TCS_PTRACE_WAIT_KILL() \ + ATF_TP_ADD_TC(tp, kill1); \ + ATF_TP_ADD_TC(tp, kill2); \ + ATF_TP_ADD_TC(tp, kill3); diff --git a/lib/libc/sys/t_ptrace_lwp_wait.h b/lib/libc/sys/t_ptrace_lwp_wait.h new file mode 100644 --- /dev/null +++ b/lib/libc/sys/t_ptrace_lwp_wait.h @@ -0,0 +1,680 @@ +/* $NetBSD: t_ptrace_lwp_wait.h,v 1.1 2020/05/05 00:15:45 kamil Exp $ */ + +/*- + * Copyright (c) 2016, 2017, 2018, 2019, 2020 The NetBSD Foundation, Inc. + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + + +static int lwpinfo_thread_sigmask[] = {SIGXCPU, SIGPIPE, SIGALRM, SIGURG}; + +static pthread_mutex_t lwpinfo_thread_mtx = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t lwpinfo_thread_cnd = PTHREAD_COND_INITIALIZER; +static volatile size_t lwpinfo_thread_done; + +static void * +lwpinfo_thread(void *arg) +{ + sigset_t s; + volatile void **tcb; + + tcb = (volatile void **)arg; + + *tcb = _lwp_getprivate(); + DPRINTF("Storing tcb[] = %p from thread %d\n", *tcb, _lwp_self()); + + pthread_setname_np(pthread_self(), "thread %d", + (void *)(intptr_t)_lwp_self()); + + sigemptyset(&s); + pthread_mutex_lock(&lwpinfo_thread_mtx); + sigaddset(&s, lwpinfo_thread_sigmask[lwpinfo_thread_done]); + lwpinfo_thread_done++; + pthread_sigmask(SIG_BLOCK, &s, NULL); + pthread_cond_signal(&lwpinfo_thread_cnd); + pthread_mutex_unlock(&lwpinfo_thread_mtx); + + return infinite_thread(NULL); +} + +static void +traceme_lwpinfo(const size_t threads, const char *iter) +{ + const int sigval = SIGSTOP; + const int sigval2 = SIGINT; + pid_t child, wpid; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + struct ptrace_lwpinfo lwp = {0, 0}; + struct ptrace_lwpstatus lwpstatus = {0}; + struct ptrace_siginfo info; + void *private; + char *name; + char namebuf[PL_LNAMELEN]; + volatile void *tcb[4]; + bool found; + sigset_t s; + + /* Maximum number of supported threads in this test */ + pthread_t t[__arraycount(tcb) - 1]; + size_t n, m; + int rv; + size_t bytes_read; + + struct ptrace_io_desc io; + sigset_t sigmask; + + ATF_REQUIRE(__arraycount(t) >= threads); + memset(tcb, 0, sizeof(tcb)); + + DPRINTF("Before forking process PID=%d\n", getpid()); + SYSCALL_REQUIRE((child = fork()) != -1); + if (child == 0) { + DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + tcb[0] = _lwp_getprivate(); + DPRINTF("Storing tcb[0] = %p\n", tcb[0]); + + pthread_setname_np(pthread_self(), "thread %d", + (void *)(intptr_t)_lwp_self()); + + sigemptyset(&s); + sigaddset(&s, lwpinfo_thread_sigmask[lwpinfo_thread_done]); + pthread_sigmask(SIG_BLOCK, &s, NULL); + + DPRINTF("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + for (n = 0; n < threads; n++) { + rv = pthread_create(&t[n], NULL, lwpinfo_thread, + &tcb[n + 1]); + FORKEE_ASSERT(rv == 0); + } + + pthread_mutex_lock(&lwpinfo_thread_mtx); + while (lwpinfo_thread_done < threads) { + pthread_cond_wait(&lwpinfo_thread_cnd, + &lwpinfo_thread_mtx); + } + pthread_mutex_unlock(&lwpinfo_thread_mtx); + + DPRINTF("Before raising %s from child\n", strsignal(sigval2)); + FORKEE_ASSERT(raise(sigval2) == 0); + + /* NOTREACHED */ + FORKEE_ASSERTX(0 && "Not reached"); + } + DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + DPRINTF("Before calling ptrace(2) with PT_GET_SIGINFO for child"); + SYSCALL_REQUIRE( + ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1); + + DPRINTF("Signal traced to lwpid=%d\n", info.psi_lwpid); + DPRINTF("Signal properties: si_signo=%#x si_code=%#x si_errno=%#x\n", + info.psi_siginfo.si_signo, info.psi_siginfo.si_code, + info.psi_siginfo.si_errno); + + ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, sigval); + ATF_REQUIRE_EQ(info.psi_siginfo.si_code, SI_LWP); + + if (strstr(iter, "LWPINFO") != NULL) { + DPRINTF("Before calling ptrace(2) with PT_LWPINFO for child\n"); + SYSCALL_REQUIRE(ptrace(PT_LWPINFO, child, &lwp, sizeof(lwp)) + != -1); + + DPRINTF("Assert that there exists a single thread only\n"); + ATF_REQUIRE(lwp.pl_lwpid > 0); + + DPRINTF("Assert that lwp thread %d received event " + "PL_EVENT_SIGNAL\n", lwp.pl_lwpid); + FORKEE_ASSERT_EQ(lwp.pl_event, PL_EVENT_SIGNAL); + + if (strstr(iter, "LWPSTATUS") != NULL) { + DPRINTF("Before calling ptrace(2) with PT_LWPSTATUS " + "for child\n"); + lwpstatus.pl_lwpid = lwp.pl_lwpid; + SYSCALL_REQUIRE(ptrace(PT_LWPSTATUS, child, &lwpstatus, + sizeof(lwpstatus)) != -1); + } + + DPRINTF("Before calling ptrace(2) with PT_LWPINFO for child\n"); + SYSCALL_REQUIRE(ptrace(PT_LWPINFO, child, &lwp, sizeof(lwp)) + != -1); + + DPRINTF("Assert that there exists a single thread only\n"); + ATF_REQUIRE_EQ(lwp.pl_lwpid, 0); + } else { + DPRINTF("Before calling ptrace(2) with PT_LWPNEXT for child\n"); + SYSCALL_REQUIRE(ptrace(PT_LWPNEXT, child, &lwpstatus, + sizeof(lwpstatus)) != -1); + + DPRINTF("Assert that there exists a single thread only %d\n", lwpstatus.pl_lwpid); + ATF_REQUIRE(lwpstatus.pl_lwpid > 0); + + DPRINTF("Before calling ptrace(2) with PT_LWPNEXT for child\n"); + SYSCALL_REQUIRE(ptrace(PT_LWPNEXT, child, &lwpstatus, + sizeof(lwpstatus)) != -1); + + DPRINTF("Assert that there exists a single thread only\n"); + ATF_REQUIRE_EQ(lwpstatus.pl_lwpid, 0); + } + + DPRINTF("Before resuming the child process where it left off and " + "without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval2); + + DPRINTF("Before calling ptrace(2) with PT_GET_SIGINFO for child"); + SYSCALL_REQUIRE( + ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1); + + DPRINTF("Signal traced to lwpid=%d\n", info.psi_lwpid); + DPRINTF("Signal properties: si_signo=%#x si_code=%#x si_errno=%#x\n", + info.psi_siginfo.si_signo, info.psi_siginfo.si_code, + info.psi_siginfo.si_errno); + + ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, sigval2); + ATF_REQUIRE_EQ(info.psi_siginfo.si_code, SI_LWP); + + memset(&lwp, 0, sizeof(lwp)); + memset(&lwpstatus, 0, sizeof(lwpstatus)); + + memset(&io, 0, sizeof(io)); + + bytes_read = 0; + io.piod_op = PIOD_READ_D; + io.piod_len = sizeof(tcb); + + do { + io.piod_addr = (char *)&tcb + bytes_read; + io.piod_offs = io.piod_addr; + + rv = ptrace(PT_IO, child, &io, sizeof(io)); + ATF_REQUIRE(rv != -1 && io.piod_len != 0); + + bytes_read += io.piod_len; + io.piod_len = sizeof(tcb) - bytes_read; + } while (bytes_read < sizeof(tcb)); + + for (n = 0; n <= threads; n++) { + if (strstr(iter, "LWPINFO") != NULL) { + DPRINTF("Before calling ptrace(2) with PT_LWPINFO for " + "child\n"); + SYSCALL_REQUIRE( + ptrace(PT_LWPINFO, child, &lwp, sizeof(lwp)) != -1); + DPRINTF("LWP=%d\n", lwp.pl_lwpid); + + DPRINTF("Assert that the thread exists\n"); + ATF_REQUIRE(lwp.pl_lwpid > 0); + + DPRINTF("Assert that lwp thread %d received expected " + "event\n", lwp.pl_lwpid); + FORKEE_ASSERT_EQ(lwp.pl_event, + info.psi_lwpid == lwp.pl_lwpid ? + PL_EVENT_SIGNAL : PL_EVENT_NONE); + + if (strstr(iter, "LWPSTATUS") != NULL) { + DPRINTF("Before calling ptrace(2) with " + "PT_LWPSTATUS for child\n"); + lwpstatus.pl_lwpid = lwp.pl_lwpid; + SYSCALL_REQUIRE(ptrace(PT_LWPSTATUS, child, + &lwpstatus, sizeof(lwpstatus)) != -1); + + goto check_lwpstatus; + } + } else { + DPRINTF("Before calling ptrace(2) with PT_LWPNEXT for " + "child\n"); + SYSCALL_REQUIRE( + ptrace(PT_LWPNEXT, child, &lwpstatus, + sizeof(lwpstatus)) != -1); + DPRINTF("LWP=%d\n", lwpstatus.pl_lwpid); + + DPRINTF("Assert that the thread exists\n"); + ATF_REQUIRE(lwpstatus.pl_lwpid > 0); + + check_lwpstatus: + + if (strstr(iter, "pl_sigmask") != NULL) { + sigmask = lwpstatus.pl_sigmask; + + DPRINTF("Retrieved sigmask: " + "%02x%02x%02x%02x\n", + sigmask.__bits[0], sigmask.__bits[1], + sigmask.__bits[2], sigmask.__bits[3]); + + found = false; + for (m = 0; + m < __arraycount(lwpinfo_thread_sigmask); + m++) { + if (sigismember(&sigmask, + lwpinfo_thread_sigmask[m])) { + found = true; + lwpinfo_thread_sigmask[m] = 0; + break; + } + } + ATF_REQUIRE(found == true); + } else if (strstr(iter, "pl_name") != NULL) { + name = lwpstatus.pl_name; + + DPRINTF("Retrieved thread name: " + "%s\n", name); + + snprintf(namebuf, sizeof namebuf, "thread %d", + lwpstatus.pl_lwpid); + + ATF_REQUIRE(strcmp(name, namebuf) == 0); + } else if (strstr(iter, "pl_private") != NULL) { + private = lwpstatus.pl_private; + + DPRINTF("Retrieved thread private pointer: " + "%p\n", private); + + found = false; + for (m = 0; m < __arraycount(tcb); m++) { + DPRINTF("Comparing %p and %p\n", + private, tcb[m]); + if (private == tcb[m]) { + found = true; + break; + } + } + ATF_REQUIRE(found == true); + } + } + } + + if (strstr(iter, "LWPINFO") != NULL) { + DPRINTF("Before calling ptrace(2) with PT_LWPINFO for " + "child\n"); + SYSCALL_REQUIRE(ptrace(PT_LWPINFO, child, &lwp, sizeof(lwp)) + != -1); + DPRINTF("LWP=%d\n", lwp.pl_lwpid); + + DPRINTF("Assert that there are no more threads\n"); + ATF_REQUIRE_EQ(lwp.pl_lwpid, 0); + } else { + DPRINTF("Before calling ptrace(2) with PT_LWPNEXT for child\n"); + SYSCALL_REQUIRE(ptrace(PT_LWPNEXT, child, &lwpstatus, + sizeof(lwpstatus)) != -1); + + DPRINTF("Assert that there exists a single thread only\n"); + ATF_REQUIRE_EQ(lwpstatus.pl_lwpid, 0); + } + + DPRINTF("Before resuming the child process where it left off and " + "without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, SIGKILL) != -1); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_signaled(status, SIGKILL, 0); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} + +#define TRACEME_LWPINFO(test, threads, iter) \ +ATF_TC(test); \ +ATF_TC_HEAD(test, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", \ + "Verify " iter " with the child with " #threads \ + " spawned extra threads"); \ +} \ + \ +ATF_TC_BODY(test, tc) \ +{ \ + \ + traceme_lwpinfo(threads, iter); \ +} + +TRACEME_LWPINFO(traceme_lwpinfo0, 0, "LWPINFO") +TRACEME_LWPINFO(traceme_lwpinfo1, 1, "LWPINFO") +TRACEME_LWPINFO(traceme_lwpinfo2, 2, "LWPINFO") +TRACEME_LWPINFO(traceme_lwpinfo3, 3, "LWPINFO") + +TRACEME_LWPINFO(traceme_lwpinfo0_lwpstatus, 0, "LWPINFO+LWPSTATUS") +TRACEME_LWPINFO(traceme_lwpinfo1_lwpstatus, 1, "LWPINFO+LWPSTATUS") +TRACEME_LWPINFO(traceme_lwpinfo2_lwpstatus, 2, "LWPINFO+LWPSTATUS") +TRACEME_LWPINFO(traceme_lwpinfo3_lwpstatus, 3, "LWPINFO+LWPSTATUS") + +TRACEME_LWPINFO(traceme_lwpinfo0_lwpstatus_pl_sigmask, 0, + "LWPINFO+LWPSTATUS+pl_sigmask") +TRACEME_LWPINFO(traceme_lwpinfo1_lwpstatus_pl_sigmask, 1, + "LWPINFO+LWPSTATUS+pl_sigmask") +TRACEME_LWPINFO(traceme_lwpinfo2_lwpstatus_pl_sigmask, 2, + "LWPINFO+LWPSTATUS+pl_sigmask") +TRACEME_LWPINFO(traceme_lwpinfo3_lwpstatus_pl_sigmask, 3, + "LWPINFO+LWPSTATUS+pl_sigmask") + +TRACEME_LWPINFO(traceme_lwpinfo0_lwpstatus_pl_name, 0, + "LWPINFO+LWPSTATUS+pl_name") +TRACEME_LWPINFO(traceme_lwpinfo1_lwpstatus_pl_name, 1, + "LWPINFO+LWPSTATUS+pl_name") +TRACEME_LWPINFO(traceme_lwpinfo2_lwpstatus_pl_name, 2, + "LWPINFO+LWPSTATUS+pl_name") +TRACEME_LWPINFO(traceme_lwpinfo3_lwpstatus_pl_name, 3, + "LWPINFO+LWPSTATUS+pl_name") + +TRACEME_LWPINFO(traceme_lwpinfo0_lwpstatus_pl_private, 0, + "LWPINFO+LWPSTATUS+pl_private") +TRACEME_LWPINFO(traceme_lwpinfo1_lwpstatus_pl_private, 1, + "LWPINFO+LWPSTATUS+pl_private") +TRACEME_LWPINFO(traceme_lwpinfo2_lwpstatus_pl_private, 2, + "LWPINFO+LWPSTATUS+pl_private") +TRACEME_LWPINFO(traceme_lwpinfo3_lwpstatus_pl_private, 3, + "LWPINFO+LWPSTATUS+pl_private") + +TRACEME_LWPINFO(traceme_lwpnext0, 0, "LWPNEXT") +TRACEME_LWPINFO(traceme_lwpnext1, 1, "LWPNEXT") +TRACEME_LWPINFO(traceme_lwpnext2, 2, "LWPNEXT") +TRACEME_LWPINFO(traceme_lwpnext3, 3, "LWPNEXT") + +TRACEME_LWPINFO(traceme_lwpnext0_pl_sigmask, 0, "LWPNEXT+pl_sigmask") +TRACEME_LWPINFO(traceme_lwpnext1_pl_sigmask, 1, "LWPNEXT+pl_sigmask") +TRACEME_LWPINFO(traceme_lwpnext2_pl_sigmask, 2, "LWPNEXT+pl_sigmask") +TRACEME_LWPINFO(traceme_lwpnext3_pl_sigmask, 3, "LWPNEXT+pl_sigmask") + +TRACEME_LWPINFO(traceme_lwpnext0_pl_name, 0, "LWPNEXT+pl_name") +TRACEME_LWPINFO(traceme_lwpnext1_pl_name, 1, "LWPNEXT+pl_name") +TRACEME_LWPINFO(traceme_lwpnext2_pl_name, 2, "LWPNEXT+pl_name") +TRACEME_LWPINFO(traceme_lwpnext3_pl_name, 3, "LWPNEXT+pl_name") + +TRACEME_LWPINFO(traceme_lwpnext0_pl_private, 0, "LWPNEXT+pl_private") +TRACEME_LWPINFO(traceme_lwpnext1_pl_private, 1, "LWPNEXT+pl_private") +TRACEME_LWPINFO(traceme_lwpnext2_pl_private, 2, "LWPNEXT+pl_private") +TRACEME_LWPINFO(traceme_lwpnext3_pl_private, 3, "LWPNEXT+pl_private") + +/// ---------------------------------------------------------------------------- + +#if defined(TWAIT_HAVE_PID) +static void +attach_lwpinfo(const int threads) +{ + const int sigval = SIGINT; + struct msg_fds parent_tracee, parent_tracer; + const int exitval_tracer = 10; + pid_t tracee, tracer, wpid; + uint8_t msg = 0xde; /* dummy message for IPC based on pipe(2) */ +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + struct ptrace_lwpinfo lwp = {0, 0}; + struct ptrace_siginfo info; + + /* Maximum number of supported threads in this test */ + pthread_t t[3]; + int n, rv; + + DPRINTF("Spawn tracee\n"); + SYSCALL_REQUIRE(msg_open(&parent_tracee) == 0); + SYSCALL_REQUIRE(msg_open(&parent_tracer) == 0); + tracee = atf_utils_fork(); + if (tracee == 0) { + /* Wait for message from the parent */ + CHILD_TO_PARENT("tracee ready", parent_tracee, msg); + + CHILD_FROM_PARENT("spawn threads", parent_tracee, msg); + + for (n = 0; n < threads; n++) { + rv = pthread_create(&t[n], NULL, infinite_thread, NULL); + FORKEE_ASSERT(rv == 0); + } + + CHILD_TO_PARENT("tracee exit", parent_tracee, msg); + + DPRINTF("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + /* NOTREACHED */ + FORKEE_ASSERTX(0 && "Not reached"); + } + PARENT_FROM_CHILD("tracee ready", parent_tracee, msg); + + DPRINTF("Spawn debugger\n"); + tracer = atf_utils_fork(); + if (tracer == 0) { + /* No IPC to communicate with the child */ + DPRINTF("Before calling PT_ATTACH from tracee %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_ATTACH, tracee, NULL, 0) != -1); + + /* Wait for tracee and assert that it was stopped w/ SIGSTOP */ + FORKEE_REQUIRE_SUCCESS( + wpid = TWAIT_GENERIC(tracee, &status, 0), tracee); + + forkee_status_stopped(status, SIGSTOP); + + DPRINTF("Before calling ptrace(2) with PT_GET_SIGINFO for " + "tracee"); + FORKEE_ASSERT( + ptrace(PT_GET_SIGINFO, tracee, &info, sizeof(info)) != -1); + + DPRINTF("Signal traced to lwpid=%d\n", info.psi_lwpid); + DPRINTF("Signal properties: si_signo=%#x si_code=%#x " + "si_errno=%#x\n", + info.psi_siginfo.si_signo, info.psi_siginfo.si_code, + info.psi_siginfo.si_errno); + + FORKEE_ASSERT_EQ(info.psi_siginfo.si_signo, SIGSTOP); + FORKEE_ASSERT_EQ(info.psi_siginfo.si_code, SI_USER); + + DPRINTF("Before calling ptrace(2) with PT_LWPINFO for child\n"); + FORKEE_ASSERT(ptrace(PT_LWPINFO, tracee, &lwp, sizeof(lwp)) + != -1); + + DPRINTF("Assert that there exists a thread\n"); + FORKEE_ASSERTX(lwp.pl_lwpid > 0); + + DPRINTF("Assert that lwp thread %d received event " + "PL_EVENT_SIGNAL\n", lwp.pl_lwpid); + FORKEE_ASSERT_EQ(lwp.pl_event, PL_EVENT_SIGNAL); + + DPRINTF("Before calling ptrace(2) with PT_LWPINFO for " + "tracee\n"); + FORKEE_ASSERT(ptrace(PT_LWPINFO, tracee, &lwp, sizeof(lwp)) + != -1); + + DPRINTF("Assert that there are no more lwp threads in " + "tracee\n"); + FORKEE_ASSERT_EQ(lwp.pl_lwpid, 0); + + /* Resume tracee with PT_CONTINUE */ + FORKEE_ASSERT(ptrace(PT_CONTINUE, tracee, (void *)1, 0) != -1); + + /* Inform parent that tracer has attached to tracee */ + CHILD_TO_PARENT("tracer ready", parent_tracer, msg); + + /* Wait for parent */ + CHILD_FROM_PARENT("tracer wait", parent_tracer, msg); + + /* Wait for tracee and assert that it raised a signal */ + FORKEE_REQUIRE_SUCCESS( + wpid = TWAIT_GENERIC(tracee, &status, 0), tracee); + + forkee_status_stopped(status, SIGINT); + + DPRINTF("Before calling ptrace(2) with PT_GET_SIGINFO for " + "child"); + FORKEE_ASSERT( + ptrace(PT_GET_SIGINFO, tracee, &info, sizeof(info)) != -1); + + DPRINTF("Signal traced to lwpid=%d\n", info.psi_lwpid); + DPRINTF("Signal properties: si_signo=%#x si_code=%#x " + "si_errno=%#x\n", + info.psi_siginfo.si_signo, info.psi_siginfo.si_code, + info.psi_siginfo.si_errno); + + FORKEE_ASSERT_EQ(info.psi_siginfo.si_signo, sigval); + FORKEE_ASSERT_EQ(info.psi_siginfo.si_code, SI_LWP); + + memset(&lwp, 0, sizeof(lwp)); + + for (n = 0; n <= threads; n++) { + DPRINTF("Before calling ptrace(2) with PT_LWPINFO for " + "child\n"); + FORKEE_ASSERT(ptrace(PT_LWPINFO, tracee, &lwp, + sizeof(lwp)) != -1); + DPRINTF("LWP=%d\n", lwp.pl_lwpid); + + DPRINTF("Assert that the thread exists\n"); + FORKEE_ASSERT(lwp.pl_lwpid > 0); + + DPRINTF("Assert that lwp thread %d received expected " + "event\n", lwp.pl_lwpid); + FORKEE_ASSERT_EQ(lwp.pl_event, + info.psi_lwpid == lwp.pl_lwpid ? + PL_EVENT_SIGNAL : PL_EVENT_NONE); + } + DPRINTF("Before calling ptrace(2) with PT_LWPINFO for " + "tracee\n"); + FORKEE_ASSERT(ptrace(PT_LWPINFO, tracee, &lwp, sizeof(lwp)) + != -1); + DPRINTF("LWP=%d\n", lwp.pl_lwpid); + + DPRINTF("Assert that there are no more threads\n"); + FORKEE_ASSERT_EQ(lwp.pl_lwpid, 0); + + DPRINTF("Before resuming the child process where it left off " + "and without signal to be sent\n"); + FORKEE_ASSERT(ptrace(PT_CONTINUE, tracee, (void *)1, SIGKILL) + != -1); + + /* Wait for tracee and assert that it exited */ + FORKEE_REQUIRE_SUCCESS( + wpid = TWAIT_GENERIC(tracee, &status, 0), tracee); + + forkee_status_signaled(status, SIGKILL, 0); + + DPRINTF("Before exiting of the tracer process\n"); + _exit(exitval_tracer); + } + + DPRINTF("Wait for the tracer to attach to the tracee\n"); + PARENT_FROM_CHILD("tracer ready", parent_tracer, msg); + + DPRINTF("Resume the tracee and spawn threads\n"); + PARENT_TO_CHILD("spawn threads", parent_tracee, msg); + + DPRINTF("Resume the tracee and let it exit\n"); + PARENT_FROM_CHILD("tracee exit", parent_tracee, msg); + + DPRINTF("Resume the tracer and let it detect multiple threads\n"); + PARENT_TO_CHILD("tracer wait", parent_tracer, msg); + + DPRINTF("Wait for tracer to finish its job and exit - calling %s()\n", + TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(tracer, &status, 0), + tracer); + + validate_status_exited(status, exitval_tracer); + + DPRINTF("Wait for tracee to finish its job and exit - calling %s()\n", + TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(tracee, &status, WNOHANG), + tracee); + + validate_status_signaled(status, SIGKILL, 0); + + msg_close(&parent_tracer); + msg_close(&parent_tracee); +} + +#define ATTACH_LWPINFO(test, threads) \ +ATF_TC(test); \ +ATF_TC_HEAD(test, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", \ + "Verify LWPINFO with the child with " #threads \ + " spawned extra threads (tracer is not the original " \ + "parent)"); \ +} \ + \ +ATF_TC_BODY(test, tc) \ +{ \ + \ + attach_lwpinfo(threads); \ +} + +ATTACH_LWPINFO(attach_lwpinfo0, 0) +ATTACH_LWPINFO(attach_lwpinfo1, 1) +ATTACH_LWPINFO(attach_lwpinfo2, 2) +ATTACH_LWPINFO(attach_lwpinfo3, 3) +#endif + +#define ATF_TP_ADD_TCS_PTRACE_WAIT_LWP() \ + ATF_TP_ADD_TC(tp, traceme_lwpinfo0); \ + ATF_TP_ADD_TC(tp, traceme_lwpinfo1); \ + ATF_TP_ADD_TC(tp, traceme_lwpinfo2); \ + ATF_TP_ADD_TC(tp, traceme_lwpinfo3); \ + ATF_TP_ADD_TC(tp, traceme_lwpinfo0_lwpstatus); \ + ATF_TP_ADD_TC(tp, traceme_lwpinfo1_lwpstatus); \ + ATF_TP_ADD_TC(tp, traceme_lwpinfo2_lwpstatus); \ + ATF_TP_ADD_TC(tp, traceme_lwpinfo3_lwpstatus); \ + ATF_TP_ADD_TC(tp, traceme_lwpinfo0_lwpstatus_pl_sigmask); \ + ATF_TP_ADD_TC(tp, traceme_lwpinfo1_lwpstatus_pl_sigmask); \ + ATF_TP_ADD_TC(tp, traceme_lwpinfo2_lwpstatus_pl_sigmask); \ + ATF_TP_ADD_TC(tp, traceme_lwpinfo3_lwpstatus_pl_sigmask); \ + ATF_TP_ADD_TC(tp, traceme_lwpinfo0_lwpstatus_pl_name); \ + ATF_TP_ADD_TC(tp, traceme_lwpinfo1_lwpstatus_pl_name); \ + ATF_TP_ADD_TC(tp, traceme_lwpinfo2_lwpstatus_pl_name); \ + ATF_TP_ADD_TC(tp, traceme_lwpinfo3_lwpstatus_pl_name); \ + ATF_TP_ADD_TC(tp, traceme_lwpinfo0_lwpstatus_pl_private); \ + ATF_TP_ADD_TC(tp, traceme_lwpinfo1_lwpstatus_pl_private); \ + ATF_TP_ADD_TC(tp, traceme_lwpinfo2_lwpstatus_pl_private); \ + ATF_TP_ADD_TC(tp, traceme_lwpinfo3_lwpstatus_pl_private); \ + ATF_TP_ADD_TC(tp, traceme_lwpnext0); \ + ATF_TP_ADD_TC(tp, traceme_lwpnext1); \ + ATF_TP_ADD_TC(tp, traceme_lwpnext2); \ + ATF_TP_ADD_TC(tp, traceme_lwpnext3); \ + ATF_TP_ADD_TC(tp, traceme_lwpnext0_pl_sigmask); \ + ATF_TP_ADD_TC(tp, traceme_lwpnext1_pl_sigmask); \ + ATF_TP_ADD_TC(tp, traceme_lwpnext2_pl_sigmask); \ + ATF_TP_ADD_TC(tp, traceme_lwpnext3_pl_sigmask); \ + ATF_TP_ADD_TC(tp, traceme_lwpnext0_pl_name); \ + ATF_TP_ADD_TC(tp, traceme_lwpnext1_pl_name); \ + ATF_TP_ADD_TC(tp, traceme_lwpnext2_pl_name); \ + ATF_TP_ADD_TC(tp, traceme_lwpnext3_pl_name); \ + ATF_TP_ADD_TC(tp, traceme_lwpnext0_pl_private); \ + ATF_TP_ADD_TC(tp, traceme_lwpnext1_pl_private); \ + ATF_TP_ADD_TC(tp, traceme_lwpnext2_pl_private); \ + ATF_TP_ADD_TC(tp, traceme_lwpnext3_pl_private); \ + ATF_TP_ADD_TC_HAVE_PID(tp, attach_lwpinfo0); \ + ATF_TP_ADD_TC_HAVE_PID(tp, attach_lwpinfo1); \ + ATF_TP_ADD_TC_HAVE_PID(tp, attach_lwpinfo2); \ + ATF_TP_ADD_TC_HAVE_PID(tp, attach_lwpinfo3); diff --git a/lib/libc/sys/t_ptrace_misc_wait.h b/lib/libc/sys/t_ptrace_misc_wait.h new file mode 100644 --- /dev/null +++ b/lib/libc/sys/t_ptrace_misc_wait.h @@ -0,0 +1,118 @@ +/* $NetBSD: t_ptrace_misc_wait.h,v 1.1 2020/05/05 02:06:08 kamil Exp $ */ + +/*- + * Copyright (c) 2016, 2017, 2018, 2019, 2020 The NetBSD Foundation, Inc. + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +static void +user_va0_disable(int operation) +{ + pid_t child, wpid; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + const int sigval = SIGSTOP; + int rv; + + struct ptrace_siginfo info; + + if (get_user_va0_disable() == 0) + atf_tc_skip("vm.user_va0_disable is set to 0"); + + memset(&info, 0, sizeof(info)); + + DPRINTF("Before forking process PID=%d\n", getpid()); + SYSCALL_REQUIRE((child = fork()) != -1); + if (child == 0) { + DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + DPRINTF("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + /* NOTREACHED */ + FORKEE_ASSERTX(0 && "This shall not be reached"); + __unreachable(); + } + DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + DPRINTF("Before calling ptrace(2) with PT_GET_SIGINFO for " + "child\n"); + SYSCALL_REQUIRE(ptrace(PT_GET_SIGINFO, child, &info, + sizeof(info)) != -1); + + DPRINTF("Signal traced to lwpid=%d\n", info.psi_lwpid); + DPRINTF("Signal properties: si_signo=%#x si_code=%#x " + "si_errno=%#x\n", + info.psi_siginfo.si_signo, info.psi_siginfo.si_code, + info.psi_siginfo.si_errno); + + ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, sigval); + ATF_REQUIRE_EQ(info.psi_siginfo.si_code, SI_LWP); + + DPRINTF("Before resuming the child process in PC=0x0 " + "and without signal to be sent\n"); + errno = 0; + rv = ptrace(operation, child, (void *)0, 0); + ATF_REQUIRE_EQ(errno, EINVAL); + ATF_REQUIRE_EQ(rv, -1); + + SYSCALL_REQUIRE(ptrace(PT_KILL, child, NULL, 0) != -1); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + validate_status_signaled(status, SIGKILL, 0); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} + +#define USER_VA0_DISABLE(test, operation) \ +ATF_TC(test); \ +ATF_TC_HEAD(test, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", \ + "Verify behavior of " #operation " with PC set to 0x0"); \ +} \ + \ +ATF_TC_BODY(test, tc) \ +{ \ + \ + user_va0_disable(operation); \ +} + +USER_VA0_DISABLE(user_va0_disable_pt_continue, PT_CONTINUE) +USER_VA0_DISABLE(user_va0_disable_pt_syscall, PT_SYSCALL) +USER_VA0_DISABLE(user_va0_disable_pt_detach, PT_DETACH) + +#define ATF_TP_ADD_TCS_PTRACE_WAIT_MISC() \ + ATF_TP_ADD_TC(tp, user_va0_disable_pt_continue); \ + ATF_TP_ADD_TC(tp, user_va0_disable_pt_syscall); \ + ATF_TP_ADD_TC(tp, user_va0_disable_pt_detach); diff --git a/lib/libc/sys/t_ptrace_register_wait.h b/lib/libc/sys/t_ptrace_register_wait.h new file mode 100644 --- /dev/null +++ b/lib/libc/sys/t_ptrace_register_wait.h @@ -0,0 +1,204 @@ +/* $NetBSD: t_ptrace_register_wait.h,v 1.3 2020/05/11 12:17:57 kamil Exp $ */ + +/*- + * Copyright (c) 2016, 2017, 2018, 2019, 2020 The NetBSD Foundation, Inc. + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + + +#if defined(HAVE_GPREGS) || defined(HAVE_FPREGS) +static void +access_regs(const char *regset, const char *aux) +{ + const int exitval = 5; + const int sigval = SIGSTOP; + pid_t child, wpid; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif +#if defined(HAVE_GPREGS) + struct reg gpr; + register_t rgstr; +#endif +#if defined(HAVE_FPREGS) + struct fpreg fpr; +#endif + +#if !defined(HAVE_GPREGS) + if (strcmp(regset, "regs") == 0) + atf_tc_fail("Impossible test scenario!"); +#endif + +#if !defined(HAVE_FPREGS) + if (strcmp(regset, "fpregs") == 0) + atf_tc_fail("Impossible test scenario!"); +#endif + + DPRINTF("Before forking process PID=%d\n", getpid()); + SYSCALL_REQUIRE((child = fork()) != -1); + if (child == 0) { + DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + DPRINTF("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + DPRINTF("Before exiting of the child process\n"); + _exit(exitval); + } + DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + +#if defined(HAVE_GPREGS) + if (strcmp(regset, "regs") == 0) { + DPRINTF("Call GETREGS for the child process\n"); + SYSCALL_REQUIRE(ptrace(PT_GETREGS, child, &gpr, 0) != -1); + + if (strcmp(aux, "none") == 0) { + DPRINTF("Retrieved registers\n"); + } else if (strcmp(aux, "pc") == 0) { + rgstr = PTRACE_REG_PC(&gpr); + DPRINTF("Retrieved %" PRIxREGISTER "\n", rgstr); + } else if (strstr(aux, "set_pc") != NULL) { + rgstr = PTRACE_REG_PC(&gpr); + DPRINTF("Retrieved PC %" PRIxREGISTER "\n", rgstr); + if (strstr(aux, "0x1") != NULL) { + rgstr |= 0x1; + } else if (strstr(aux, "0x3") != NULL) { + rgstr |= 0x3; + } else if (strstr(aux, "0x7") != NULL) { + rgstr |= 0x7; + } + DPRINTF("Set PC %" PRIxREGISTER "\n", rgstr); + PTRACE_REG_SET_PC(&gpr, rgstr); + if (strcmp(aux, "set_pc") != 0) { + /* This call can fail with EINVAL or similar. */ + ptrace(PT_SETREGS, child, &gpr, 0); + } + } else if (strcmp(aux, "sp") == 0) { + rgstr = PTRACE_REG_SP(&gpr); + DPRINTF("Retrieved %" PRIxREGISTER "\n", rgstr); + } else if (strcmp(aux, "intrv") == 0) { + rgstr = PTRACE_REG_INTRV(&gpr); + DPRINTF("Retrieved %" PRIxREGISTER "\n", rgstr); + } else if (strcmp(aux, "setregs") == 0) { + DPRINTF("Call SETREGS for the child process\n"); + SYSCALL_REQUIRE( + ptrace(PT_SETREGS, child, &gpr, 0) != -1); + } + } +#endif + +#if defined(HAVE_FPREGS) + if (strcmp(regset, "fpregs") == 0) { + DPRINTF("Call GETFPREGS for the child process\n"); + SYSCALL_REQUIRE(ptrace(PT_GETFPREGS, child, &fpr, 0) != -1); + + if (strcmp(aux, "getfpregs") == 0) { + DPRINTF("Retrieved FP registers\n"); + } else if (strcmp(aux, "setfpregs") == 0) { + DPRINTF("Call SETFPREGS for the child\n"); + SYSCALL_REQUIRE( + ptrace(PT_SETFPREGS, child, &fpr, 0) != -1); + } + } +#endif + + DPRINTF("Before resuming the child process where it left off and " + "without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + if (strstr(aux, "unaligned") != NULL) { + DPRINTF("Before resuming the child process where it left off " + "and without signal to be sent\n"); + + ptrace(PT_KILL, child, NULL, 0); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), + child); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, + wpid = TWAIT_GENERIC(child, &status, 0)); + } else { + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS( + wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_exited(status, exitval); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, + wpid = TWAIT_GENERIC(child, &status, 0)); + } +} + +#define ACCESS_REGS(test, regset, aux) \ +ATF_TC(test); \ +ATF_TC_HEAD(test, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", \ + "Verify " regset " with auxiliary operation: " aux); \ +} \ + \ +ATF_TC_BODY(test, tc) \ +{ \ + \ + access_regs(regset, aux); \ +} +#endif + +#if defined(HAVE_GPREGS) +ACCESS_REGS(access_regs1, "regs", "none") +ACCESS_REGS(access_regs2, "regs", "pc") +ACCESS_REGS(access_regs3, "regs", "set_pc") +ACCESS_REGS(access_regs4, "regs", "sp") +ACCESS_REGS(access_regs5, "regs", "intrv") +ACCESS_REGS(access_regs6, "regs", "setregs") +ACCESS_REGS(access_regs_set_unaligned_pc_0x1, "regs", "set_pc+unaligned+0x1") +ACCESS_REGS(access_regs_set_unaligned_pc_0x3, "regs", "set_pc+unaligned+0x3") +ACCESS_REGS(access_regs_set_unaligned_pc_0x7, "regs", "set_pc+unaligned+0x7") +#endif +#if defined(HAVE_FPREGS) +ACCESS_REGS(access_fpregs1, "fpregs", "getfpregs") +ACCESS_REGS(access_fpregs2, "fpregs", "setfpregs") +#endif + +#define ATF_TP_ADD_TCS_PTRACE_WAIT_REGISTER() \ + ATF_TP_ADD_TC_HAVE_GPREGS(tp, access_regs1); \ + ATF_TP_ADD_TC_HAVE_GPREGS(tp, access_regs2); \ + ATF_TP_ADD_TC_HAVE_GPREGS(tp, access_regs3); \ + ATF_TP_ADD_TC_HAVE_GPREGS(tp, access_regs4); \ + ATF_TP_ADD_TC_HAVE_GPREGS(tp, access_regs5); \ + ATF_TP_ADD_TC_HAVE_GPREGS(tp, access_regs6); \ + ATF_TP_ADD_TC_HAVE_GPREGS(tp, access_regs_set_unaligned_pc_0x1); \ + ATF_TP_ADD_TC_HAVE_GPREGS(tp, access_regs_set_unaligned_pc_0x3); \ + ATF_TP_ADD_TC_HAVE_GPREGS(tp, access_regs_set_unaligned_pc_0x7); \ + ATF_TP_ADD_TC_HAVE_FPREGS(tp, access_fpregs1); \ + ATF_TP_ADD_TC_HAVE_FPREGS(tp, access_fpregs2); diff --git a/lib/libc/sys/t_ptrace_sigchld.c b/lib/libc/sys/t_ptrace_sigchld.c new file mode 100644 --- /dev/null +++ b/lib/libc/sys/t_ptrace_sigchld.c @@ -0,0 +1,251 @@ +/* $NetBSD: t_ptrace_sigchld.c,v 1.3 2020/05/05 18:12:20 kamil Exp $ */ + +/*- + * Copyright (c) 2020 The NetBSD Foundation, Inc. + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +__RCSID("$NetBSD: t_ptrace_sigchld.c,v 1.3 2020/05/05 18:12:20 kamil Exp $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "h_macros.h" +#include "msg.h" + +#include "t_ptrace_wait.h" + +#define SYSCALL_REQUIRE(expr) ATF_REQUIRE_MSG(expr, "%s: %s", # expr, \ + strerror(errno)) +#define SYSCALL_REQUIRE_ERRNO(res, exp) ATF_REQUIRE_MSG(res == exp, \ + "%d(%s) != %d", res, strerror(res), exp) + +static int debug = 0; + +#define DPRINTF(a, ...) do \ + if (debug) \ + printf("%s() %d.%d %s:%d " a, \ + __func__, getpid(), _lwp_self(), __FILE__, __LINE__, ##__VA_ARGS__); \ + while (/*CONSTCOND*/0) + +/// ---------------------------------------------------------------------------- + +static int expected_signo; +static int expected_code; +static int expected_status; +static pid_t expected_pid; + +static void +sigchld_action(int sig, siginfo_t *info, void *ctx) +{ + + FORKEE_ASSERT_EQ(info->si_signo, expected_signo); + FORKEE_ASSERT_EQ(info->si_code, expected_code); + FORKEE_ASSERT_EQ(info->si_uid, getuid()); + FORKEE_ASSERT_EQ(info->si_pid, expected_pid); + + if (WIFEXITED(info->si_status)) + ATF_REQUIRE_EQ(WEXITSTATUS(info->si_status), expected_status); + else if (WIFSTOPPED(info->si_status)) + ATF_REQUIRE_EQ(WSTOPSIG(info->si_status), expected_status); + else if (WIFSIGNALED(info->si_status)) + ATF_REQUIRE_EQ(WTERMSIG(info->si_status), expected_status); +/* + else if (WIFCONTINUED(info->si_status)) + ; +*/ +} + +static void +traceme_raise(int sigval) +{ + const int exitval = 5; + struct sigaction sa; + pid_t child; + struct msg_fds parent_child; + uint8_t msg = 0xde; /* dummy message for IPC based on pipe(2) */ + + struct ptrace_siginfo info; + memset(&info, 0, sizeof(info)); + + memset(&sa, 0, sizeof(sa)); + sa.sa_sigaction = sigchld_action; + sa.sa_flags = SA_SIGINFO | SA_NOCLDWAIT; + sigemptyset(&sa.sa_mask); + + atf_tc_skip("XXX: zombie is not collected before tracer's death"); + + SYSCALL_REQUIRE(sigaction(SIGCHLD, &sa, NULL) == 0); + + SYSCALL_REQUIRE(msg_open(&parent_child) == 0); + + DPRINTF("Before forking process PID=%d\n", getpid()); + SYSCALL_REQUIRE((child = fork()) != -1); + if (child == 0) { + DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + CHILD_FROM_PARENT("raise1 child", parent_child, msg); + + raise(sigval); + + CHILD_TO_PARENT("raise2 child", parent_child, msg); + + switch (sigval) { + case SIGKILL: + /* NOTREACHED */ + FORKEE_ASSERTX(0 && "This shall not be reached"); + __unreachable(); + default: + DPRINTF("Before exiting of the child process\n"); + _exit(exitval); + } + } + DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + expected_signo = SIGCHLD; + expected_pid = child; + switch (sigval) { + case SIGKILL: + expected_code = CLD_KILLED; + expected_status = SIGKILL; + break; + case SIGSTOP: + expected_code = CLD_STOPPED; + expected_status = SIGSTOP; + break; + default: + break; + } + + PARENT_TO_CHILD("raise1 child", parent_child, msg); + + switch (sigval) { + case SIGKILL: + break; + default: + PARENT_FROM_CHILD("raise2 child", parent_child, msg); + + DPRINTF("Before calling ptrace(2) with PT_GET_SIGINFO for " + "child\n"); + SYSCALL_REQUIRE(ptrace(PT_GET_SIGINFO, child, &info, + sizeof(info)) != -1); + + DPRINTF("Signal traced to lwpid=%d\n", info.psi_lwpid); + DPRINTF("Signal properties: si_signo=%#x si_code=%#x " + "si_errno=%#x\n", + info.psi_siginfo.si_signo, info.psi_siginfo.si_code, + info.psi_siginfo.si_errno); + + ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, sigval); + ATF_REQUIRE_EQ(info.psi_siginfo.si_code, SI_LWP); + + expected_code = CLD_EXITED; + expected_status = exitval; + + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0)); + + break; + } + + await_collected(child); /* XXX: Process is never collected */ +} + +#define TRACEME_RAISE(test, sig) \ +ATF_TC(test); \ +ATF_TC_HEAD(test, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", \ + "Verify " #sig " followed by _exit(2) in a child"); \ + atf_tc_set_md_var(tc, "timeout", "10"); \ +} \ + \ +ATF_TC_BODY(test, tc) \ +{ \ + \ + traceme_raise(sig); \ +} + +TRACEME_RAISE(traceme_raise1, SIGKILL) /* non-maskable */ +#if notyet +TRACEME_RAISE(traceme_raise2, SIGSTOP) /* non-maskable */ +TRACEME_RAISE(traceme_raise3, SIGABRT) /* regular abort trap */ +TRACEME_RAISE(traceme_raise4, SIGHUP) /* hangup */ +TRACEME_RAISE(traceme_raise5, SIGCONT) /* continued? */ +TRACEME_RAISE(traceme_raise6, SIGTRAP) /* crash signal */ +TRACEME_RAISE(traceme_raise7, SIGBUS) /* crash signal */ +TRACEME_RAISE(traceme_raise8, SIGILL) /* crash signal */ +TRACEME_RAISE(traceme_raise9, SIGFPE) /* crash signal */ +TRACEME_RAISE(traceme_raise10, SIGSEGV) /* crash signal */ +#endif + +ATF_TP_ADD_TCS(tp) +{ + setvbuf(stdout, NULL, _IONBF, 0); + setvbuf(stderr, NULL, _IONBF, 0); + + ATF_TP_ADD_TC(tp, traceme_raise1); +#if notyet + ATF_TP_ADD_TC(tp, traceme_raise2); + ATF_TP_ADD_TC(tp, traceme_raise3); + ATF_TP_ADD_TC(tp, traceme_raise4); + ATF_TP_ADD_TC(tp, traceme_raise5); + ATF_TP_ADD_TC(tp, traceme_raise6); + ATF_TP_ADD_TC(tp, traceme_raise7); + ATF_TP_ADD_TC(tp, traceme_raise8); + ATF_TP_ADD_TC(tp, traceme_raise9); + ATF_TP_ADD_TC(tp, traceme_raise10); +#endif + + return atf_no_error(); +} diff --git a/lib/libc/sys/t_ptrace_siginfo_wait.h b/lib/libc/sys/t_ptrace_siginfo_wait.h new file mode 100644 --- /dev/null +++ b/lib/libc/sys/t_ptrace_siginfo_wait.h @@ -0,0 +1,156 @@ +/* $NetBSD: t_ptrace_siginfo_wait.h,v 1.1 2020/05/05 00:57:34 kamil Exp $ */ + +/*- + * Copyright (c) 2016, 2017, 2018, 2019, 2020 The NetBSD Foundation, Inc. + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + + +static void +ptrace_siginfo(bool faked, void (*sah)(int a, siginfo_t *b, void *c), int *signal_caught) +{ + const int exitval = 5; + const int sigval = SIGINT; + const int sigfaked = SIGTRAP; + const int sicodefaked = TRAP_BRKPT; + pid_t child, wpid; + struct sigaction sa; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + struct ptrace_siginfo info; + memset(&info, 0, sizeof(info)); + + DPRINTF("Before forking process PID=%d\n", getpid()); + SYSCALL_REQUIRE((child = fork()) != -1); + if (child == 0) { + DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + sa.sa_sigaction = sah; + sa.sa_flags = SA_SIGINFO; + sigemptyset(&sa.sa_mask); + + FORKEE_ASSERT(sigaction(faked ? sigfaked : sigval, &sa, NULL) + != -1); + + DPRINTF("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + FORKEE_ASSERT_EQ(*signal_caught, 1); + + DPRINTF("Before exiting of the child process\n"); + _exit(exitval); + } + DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + DPRINTF("Before calling ptrace(2) with PT_GET_SIGINFO for child\n"); + SYSCALL_REQUIRE( + ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1); + + DPRINTF("Signal traced to lwpid=%d\n", info.psi_lwpid); + DPRINTF("Signal properties: si_signo=%#x si_code=%#x si_errno=%#x\n", + info.psi_siginfo.si_signo, info.psi_siginfo.si_code, + info.psi_siginfo.si_errno); + + if (faked) { + DPRINTF("Before setting new faked signal to signo=%d " + "si_code=%d\n", sigfaked, sicodefaked); + info.psi_siginfo.si_signo = sigfaked; + info.psi_siginfo.si_code = sicodefaked; + } + + DPRINTF("Before calling ptrace(2) with PT_SET_SIGINFO for child\n"); + SYSCALL_REQUIRE( + ptrace(PT_SET_SIGINFO, child, &info, sizeof(info)) != -1); + + if (faked) { + DPRINTF("Before calling ptrace(2) with PT_GET_SIGINFO for " + "child\n"); + SYSCALL_REQUIRE(ptrace(PT_GET_SIGINFO, child, &info, + sizeof(info)) != -1); + + DPRINTF("Before checking siginfo_t\n"); + ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, sigfaked); + ATF_REQUIRE_EQ(info.psi_siginfo.si_code, sicodefaked); + } + + DPRINTF("Before resuming the child process where it left off and " + "without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, + faked ? sigfaked : sigval) != -1); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_exited(status, exitval); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} + +#define PTRACE_SIGINFO(test, faked) \ +ATF_TC(test); \ +ATF_TC_HEAD(test, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", \ + "Verify basic PT_GET_SIGINFO and PT_SET_SIGINFO calls" \ + "with%s setting signal to new value", faked ? "" : "out"); \ +} \ + \ +static int test##_caught = 0; \ + \ +static void \ +test##_sighandler(int sig, siginfo_t *info, void *ctx) \ +{ \ + if (faked) { \ + FORKEE_ASSERT_EQ(sig, SIGTRAP); \ + FORKEE_ASSERT_EQ(info->si_signo, SIGTRAP); \ + FORKEE_ASSERT_EQ(info->si_code, TRAP_BRKPT); \ + } else { \ + FORKEE_ASSERT_EQ(sig, SIGINT); \ + FORKEE_ASSERT_EQ(info->si_signo, SIGINT); \ + FORKEE_ASSERT_EQ(info->si_code, SI_LWP); \ + } \ + \ + ++ test##_caught; \ +} \ + \ +ATF_TC_BODY(test, tc) \ +{ \ + \ + ptrace_siginfo(faked, test##_sighandler, & test##_caught); \ +} + +PTRACE_SIGINFO(siginfo_set_unmodified, false) +PTRACE_SIGINFO(siginfo_set_faked, true) + +#define ATF_TP_ADD_TCS_PTRACE_WAIT_SIGINFO() \ + ATF_TP_ADD_TC(tp, siginfo_set_unmodified); \ + ATF_TP_ADD_TC(tp, siginfo_set_faked); diff --git a/lib/libc/sys/t_ptrace_signal_wait.h b/lib/libc/sys/t_ptrace_signal_wait.h new file mode 100644 --- /dev/null +++ b/lib/libc/sys/t_ptrace_signal_wait.h @@ -0,0 +1,2224 @@ +/* $NetBSD: t_ptrace_signal_wait.h,v 1.5 2021/03/19 00:44:09 simonb Exp $ */ + +/*- + * Copyright (c) 2016, 2017, 2018, 2019, 2020 The NetBSD Foundation, Inc. + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + + +static void +traceme_raise(int sigval) +{ + const int exitval = 5; + pid_t child, wpid; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + + ptrace_state_t state, zero_state; + const int slen = sizeof(state); + struct ptrace_siginfo info; + memset(&zero_state, 0, sizeof(zero_state)); + memset(&info, 0, sizeof(info)); + + DPRINTF("Before forking process PID=%d\n", getpid()); + SYSCALL_REQUIRE((child = fork()) != -1); + if (child == 0) { + DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + DPRINTF("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + switch (sigval) { + case SIGKILL: + /* NOTREACHED */ + FORKEE_ASSERTX(0 && "This shall not be reached"); + __unreachable(); + default: + DPRINTF("Before exiting of the child process\n"); + _exit(exitval); + } + } + DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + switch (sigval) { + case SIGKILL: + validate_status_signaled(status, sigval, 0); + SYSCALL_REQUIRE( + ptrace(PT_GET_PROCESS_STATE, child, &state, slen) == -1); + + break; + default: + validate_status_stopped(status, sigval); + + DPRINTF("Before calling ptrace(2) with PT_GET_SIGINFO for " + "child\n"); + SYSCALL_REQUIRE(ptrace(PT_GET_SIGINFO, child, &info, + sizeof(info)) != -1); + + DPRINTF("Signal traced to lwpid=%d\n", info.psi_lwpid); + DPRINTF("Signal properties: si_signo=%#x si_code=%#x " + "si_errno=%#x\n", + info.psi_siginfo.si_signo, info.psi_siginfo.si_code, + info.psi_siginfo.si_errno); + + ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, sigval); + ATF_REQUIRE_EQ(info.psi_siginfo.si_code, SI_LWP); + + DPRINTF("Assert that PT_GET_PROCESS_STATE returns non-error\n"); + SYSCALL_REQUIRE( + ptrace(PT_GET_PROCESS_STATE, child, &state, slen) != -1); + ATF_REQUIRE(memcmp(&state, &zero_state, slen) == 0); + + DPRINTF("Before resuming the child process where it left off " + "and without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), + child); + break; + } + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} + +#define TRACEME_RAISE(test, sig) \ +ATF_TC(test); \ +ATF_TC_HEAD(test, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", \ + "Verify " #sig " followed by _exit(2) in a child"); \ +} \ + \ +ATF_TC_BODY(test, tc) \ +{ \ + \ + traceme_raise(sig); \ +} + +TRACEME_RAISE(traceme_raise1, SIGKILL) /* non-maskable */ +TRACEME_RAISE(traceme_raise2, SIGSTOP) /* non-maskable */ +TRACEME_RAISE(traceme_raise3, SIGABRT) /* regular abort trap */ +TRACEME_RAISE(traceme_raise4, SIGHUP) /* hangup */ +TRACEME_RAISE(traceme_raise5, SIGCONT) /* continued? */ +TRACEME_RAISE(traceme_raise6, SIGTRAP) /* crash signal */ +TRACEME_RAISE(traceme_raise7, SIGBUS) /* crash signal */ +TRACEME_RAISE(traceme_raise8, SIGILL) /* crash signal */ +TRACEME_RAISE(traceme_raise9, SIGFPE) /* crash signal */ +TRACEME_RAISE(traceme_raise10, SIGSEGV) /* crash signal */ + +/// ---------------------------------------------------------------------------- + +static void +traceme_raisesignal_ignored(int sigignored) +{ + const int exitval = 5; + const int sigval = SIGSTOP; + pid_t child, wpid; + struct sigaction sa; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + struct ptrace_siginfo info; + + memset(&info, 0, sizeof(info)); + + DPRINTF("Before forking process PID=%d\n", getpid()); + SYSCALL_REQUIRE((child = fork()) != -1); + if (child == 0) { + DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = SIG_IGN; + sigemptyset(&sa.sa_mask); + FORKEE_ASSERT(sigaction(sigignored, &sa, NULL) != -1); + + DPRINTF("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + DPRINTF("Before raising %s from child\n", + strsignal(sigignored)); + FORKEE_ASSERT(raise(sigignored) == 0); + + DPRINTF("Before exiting of the child process\n"); + _exit(exitval); + } + DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + DPRINTF("Before calling ptrace(2) with PT_GET_SIGINFO for child\n"); + SYSCALL_REQUIRE( + ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1); + + DPRINTF("Signal traced to lwpid=%d\n", info.psi_lwpid); + DPRINTF("Signal properties: si_signo=%#x si_code=%#x si_errno=%#x\n", + info.psi_siginfo.si_signo, info.psi_siginfo.si_code, + info.psi_siginfo.si_errno); + + ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, sigval); + ATF_REQUIRE_EQ(info.psi_siginfo.si_code, SI_LWP); + + DPRINTF("Before resuming the child process where it left off and " + "without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigignored); + + DPRINTF("Before calling ptrace(2) with PT_GET_SIGINFO for child\n"); + SYSCALL_REQUIRE( + ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1); + + DPRINTF("Signal traced to lwpid=%d\n", info.psi_lwpid); + DPRINTF("Signal properties: si_signo=%#x si_code=%#x si_errno=%#x\n", + info.psi_siginfo.si_signo, info.psi_siginfo.si_code, + info.psi_siginfo.si_errno); + + ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, sigignored); + ATF_REQUIRE_EQ(info.psi_siginfo.si_code, SI_LWP); + + DPRINTF("Before resuming the child process where it left off and " + "without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_exited(status, exitval); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} + +#define TRACEME_RAISESIGNAL_IGNORED(test, sig) \ +ATF_TC(test); \ +ATF_TC_HEAD(test, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", \ + "Verify that ignoring (with SIG_IGN) " #sig " in tracee " \ + "does not stop tracer from catching this raised signal"); \ +} \ + \ +ATF_TC_BODY(test, tc) \ +{ \ + \ + traceme_raisesignal_ignored(sig); \ +} + +// A signal handler for SIGKILL and SIGSTOP cannot be ignored. +TRACEME_RAISESIGNAL_IGNORED(traceme_raisesignal_ignored1, SIGABRT) /* abort */ +TRACEME_RAISESIGNAL_IGNORED(traceme_raisesignal_ignored2, SIGHUP) /* hangup */ +TRACEME_RAISESIGNAL_IGNORED(traceme_raisesignal_ignored3, SIGCONT) /* cont. */ +TRACEME_RAISESIGNAL_IGNORED(traceme_raisesignal_ignored4, SIGTRAP) /* crash */ +TRACEME_RAISESIGNAL_IGNORED(traceme_raisesignal_ignored5, SIGBUS) /* crash */ +TRACEME_RAISESIGNAL_IGNORED(traceme_raisesignal_ignored6, SIGILL) /* crash */ +TRACEME_RAISESIGNAL_IGNORED(traceme_raisesignal_ignored7, SIGFPE) /* crash */ +TRACEME_RAISESIGNAL_IGNORED(traceme_raisesignal_ignored8, SIGSEGV) /* crash */ + +/// ---------------------------------------------------------------------------- + +static void +traceme_raisesignal_masked(int sigmasked) +{ + const int exitval = 5; + const int sigval = SIGSTOP; + pid_t child, wpid; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + sigset_t intmask; + struct ptrace_siginfo info; + + memset(&info, 0, sizeof(info)); + + DPRINTF("Before forking process PID=%d\n", getpid()); + SYSCALL_REQUIRE((child = fork()) != -1); + if (child == 0) { + DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + sigemptyset(&intmask); + sigaddset(&intmask, sigmasked); + sigprocmask(SIG_BLOCK, &intmask, NULL); + + DPRINTF("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + DPRINTF("Before raising %s breakpoint from child\n", + strsignal(sigmasked)); + FORKEE_ASSERT(raise(sigmasked) == 0); + + DPRINTF("Before exiting of the child process\n"); + _exit(exitval); + } + DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + DPRINTF("Before calling ptrace(2) with PT_GET_SIGINFO for child\n"); + SYSCALL_REQUIRE( + ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1); + + DPRINTF("Signal traced to lwpid=%d\n", info.psi_lwpid); + DPRINTF("Signal properties: si_signo=%#x si_code=%#x si_errno=%#x\n", + info.psi_siginfo.si_signo, info.psi_siginfo.si_code, + info.psi_siginfo.si_errno); + + ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, sigval); + ATF_REQUIRE_EQ(info.psi_siginfo.si_code, SI_LWP); + + DPRINTF("Before resuming the child process where it left off and " + "without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_exited(status, exitval); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} + +#define TRACEME_RAISESIGNAL_MASKED(test, sig) \ +ATF_TC(test); \ +ATF_TC_HEAD(test, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", \ + "Verify that masking (with SIG_BLOCK) " #sig " in tracee " \ + "stops tracer from catching this raised signal"); \ +} \ + \ +ATF_TC_BODY(test, tc) \ +{ \ + \ + traceme_raisesignal_masked(sig); \ +} + +// A signal handler for SIGKILL and SIGSTOP cannot be masked. +TRACEME_RAISESIGNAL_MASKED(traceme_raisesignal_masked1, SIGABRT) /* abort trap */ +TRACEME_RAISESIGNAL_MASKED(traceme_raisesignal_masked2, SIGHUP) /* hangup */ +TRACEME_RAISESIGNAL_MASKED(traceme_raisesignal_masked3, SIGCONT) /* continued? */ +TRACEME_RAISESIGNAL_MASKED(traceme_raisesignal_masked4, SIGTRAP) /* crash sig. */ +TRACEME_RAISESIGNAL_MASKED(traceme_raisesignal_masked5, SIGBUS) /* crash sig. */ +TRACEME_RAISESIGNAL_MASKED(traceme_raisesignal_masked6, SIGILL) /* crash sig. */ +TRACEME_RAISESIGNAL_MASKED(traceme_raisesignal_masked7, SIGFPE) /* crash sig. */ +TRACEME_RAISESIGNAL_MASKED(traceme_raisesignal_masked8, SIGSEGV) /* crash sig. */ + +/// ---------------------------------------------------------------------------- + +static void +traceme_crash(int sig) +{ + pid_t child, wpid; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + struct ptrace_siginfo info; + +#ifndef PTRACE_ILLEGAL_ASM + if (sig == SIGILL) + atf_tc_skip("PTRACE_ILLEGAL_ASM not defined"); +#endif + + if (sig == SIGFPE && !are_fpu_exceptions_supported()) + atf_tc_skip("FP exceptions are not supported"); + + memset(&info, 0, sizeof(info)); + + DPRINTF("Before forking process PID=%d\n", getpid()); + SYSCALL_REQUIRE((child = fork()) != -1); + if (child == 0) { + DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + DPRINTF("Before executing a trap\n"); + switch (sig) { + case SIGTRAP: + trigger_trap(); + break; + case SIGSEGV: + trigger_segv(); + break; + case SIGILL: + trigger_ill(); + break; + case SIGFPE: + trigger_fpe(); + break; + case SIGBUS: + trigger_bus(); + break; + default: + /* NOTREACHED */ + FORKEE_ASSERTX(0 && "This shall not be reached"); + } + + /* NOTREACHED */ + FORKEE_ASSERTX(0 && "This shall not be reached"); + } + DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sig); + + DPRINTF("Before calling ptrace(2) with PT_GET_SIGINFO for child\n"); + SYSCALL_REQUIRE( + ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1); + + DPRINTF("Signal traced to lwpid=%d\n", info.psi_lwpid); + DPRINTF("Signal properties: si_signo=%#x si_code=%#x si_errno=%#x\n", + info.psi_siginfo.si_signo, info.psi_siginfo.si_code, + info.psi_siginfo.si_errno); + + ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, sig); + switch (sig) { + case SIGTRAP: + ATF_REQUIRE_EQ(info.psi_siginfo.si_code, TRAP_BRKPT); + break; + case SIGSEGV: + ATF_REQUIRE_EQ(info.psi_siginfo.si_code, SEGV_MAPERR); + break; + case SIGILL: + ATF_REQUIRE(info.psi_siginfo.si_code >= ILL_ILLOPC && + info.psi_siginfo.si_code <= ILL_BADSTK); + break; + case SIGFPE: +// XXXQEMU ATF_REQUIRE_EQ(info.psi_siginfo.si_code, FPE_FLTDIV); + break; + case SIGBUS: + ATF_REQUIRE_EQ(info.psi_siginfo.si_code, BUS_ADRERR); + break; + } + + SYSCALL_REQUIRE(ptrace(PT_KILL, child, NULL, 0) != -1); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_signaled(status, SIGKILL, 0); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} + +#define TRACEME_CRASH(test, sig) \ +ATF_TC(test); \ +ATF_TC_HEAD(test, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", \ + "Verify crash signal " #sig " in a child after PT_TRACE_ME"); \ +} \ + \ +ATF_TC_BODY(test, tc) \ +{ \ + \ + traceme_crash(sig); \ +} + +TRACEME_CRASH(traceme_crash_trap, SIGTRAP) +TRACEME_CRASH(traceme_crash_segv, SIGSEGV) +TRACEME_CRASH(traceme_crash_ill, SIGILL) +TRACEME_CRASH(traceme_crash_fpe, SIGFPE) +TRACEME_CRASH(traceme_crash_bus, SIGBUS) + +/// ---------------------------------------------------------------------------- + +static void +traceme_signalmasked_crash(int sig) +{ + const int sigval = SIGSTOP; + pid_t child, wpid; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + struct ptrace_siginfo info; + sigset_t intmask; + struct kinfo_proc2 kp; + size_t len = sizeof(kp); + + int name[6]; + const size_t namelen = __arraycount(name); + ki_sigset_t kp_sigmask; + +#ifndef PTRACE_ILLEGAL_ASM + if (sig == SIGILL) + atf_tc_skip("PTRACE_ILLEGAL_ASM not defined"); +#endif + + if (sig == SIGFPE && !are_fpu_exceptions_supported()) + atf_tc_skip("FP exceptions are not supported"); + + memset(&info, 0, sizeof(info)); + + DPRINTF("Before forking process PID=%d\n", getpid()); + SYSCALL_REQUIRE((child = fork()) != -1); + if (child == 0) { + DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + sigemptyset(&intmask); + sigaddset(&intmask, sig); + sigprocmask(SIG_BLOCK, &intmask, NULL); + + DPRINTF("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + DPRINTF("Before executing a trap\n"); + switch (sig) { + case SIGTRAP: + trigger_trap(); + break; + case SIGSEGV: + trigger_segv(); + break; + case SIGILL: + trigger_ill(); + break; + case SIGFPE: + trigger_fpe(); + break; + case SIGBUS: + trigger_bus(); + break; + default: + /* NOTREACHED */ + FORKEE_ASSERTX(0 && "This shall not be reached"); + } + + /* NOTREACHED */ + FORKEE_ASSERTX(0 && "This shall not be reached"); + } + DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + name[0] = CTL_KERN, + name[1] = KERN_PROC2, + name[2] = KERN_PROC_PID; + name[3] = child; + name[4] = sizeof(kp); + name[5] = 1; + + ATF_REQUIRE_EQ(sysctl(name, namelen, &kp, &len, NULL, 0), 0); + + kp_sigmask = kp.p_sigmask; + + DPRINTF("Before calling ptrace(2) with PT_GET_SIGINFO for child\n"); + SYSCALL_REQUIRE( + ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1); + + DPRINTF("Signal traced to lwpid=%d\n", info.psi_lwpid); + DPRINTF("Signal properties: si_signo=%#x si_code=%#x si_errno=%#x\n", + info.psi_siginfo.si_signo, info.psi_siginfo.si_code, + info.psi_siginfo.si_errno); + + ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, sigval); + ATF_REQUIRE_EQ(info.psi_siginfo.si_code, SI_LWP); + + DPRINTF("Before resuming the child process where it left off and " + "without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sig); + + DPRINTF("Before calling ptrace(2) with PT_GET_SIGINFO for child\n"); + SYSCALL_REQUIRE( + ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1); + + DPRINTF("Signal traced to lwpid=%d\n", info.psi_lwpid); + DPRINTF("Signal properties: si_signo=%#x si_code=%#x si_errno=%#x\n", + info.psi_siginfo.si_signo, info.psi_siginfo.si_code, + info.psi_siginfo.si_errno); + + ATF_REQUIRE_EQ(sysctl(name, namelen, &kp, &len, NULL, 0), 0); + + DPRINTF("kp_sigmask=" + "%#02" PRIx32 "%02" PRIx32 "%02" PRIx32 "%02" PRIx32"\n", + kp_sigmask.__bits[0], kp_sigmask.__bits[1], kp_sigmask.__bits[2], + kp_sigmask.__bits[3]); + + DPRINTF("kp.p_sigmask=" + "%#02" PRIx32 "%02" PRIx32 "%02" PRIx32 "%02" PRIx32"\n", + kp.p_sigmask.__bits[0], kp.p_sigmask.__bits[1], + kp.p_sigmask.__bits[2], kp.p_sigmask.__bits[3]); + + ATF_REQUIRE(!memcmp(&kp_sigmask, &kp.p_sigmask, sizeof(kp_sigmask))); + + ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, sig); + switch (sig) { + case SIGTRAP: + ATF_REQUIRE_EQ(info.psi_siginfo.si_code, TRAP_BRKPT); + break; + case SIGSEGV: + ATF_REQUIRE_EQ(info.psi_siginfo.si_code, SEGV_MAPERR); + break; + case SIGILL: + ATF_REQUIRE(info.psi_siginfo.si_code >= ILL_ILLOPC && + info.psi_siginfo.si_code <= ILL_BADSTK); + break; + case SIGFPE: +// XXXQEMU ATF_REQUIRE_EQ(info.psi_siginfo.si_code, FPE_FLTDIV); + break; + case SIGBUS: + ATF_REQUIRE_EQ(info.psi_siginfo.si_code, BUS_ADRERR); + break; + } + + SYSCALL_REQUIRE(ptrace(PT_KILL, child, NULL, 0) != -1); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_signaled(status, SIGKILL, 0); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} + +#define TRACEME_SIGNALMASKED_CRASH(test, sig) \ +ATF_TC(test); \ +ATF_TC_HEAD(test, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", \ + "Verify masked crash signal " #sig " in a child after " \ + "PT_TRACE_ME is delivered to its tracer"); \ +} \ + \ +ATF_TC_BODY(test, tc) \ +{ \ + \ + traceme_signalmasked_crash(sig); \ +} + +TRACEME_SIGNALMASKED_CRASH(traceme_signalmasked_crash_trap, SIGTRAP) +TRACEME_SIGNALMASKED_CRASH(traceme_signalmasked_crash_segv, SIGSEGV) +TRACEME_SIGNALMASKED_CRASH(traceme_signalmasked_crash_ill, SIGILL) +TRACEME_SIGNALMASKED_CRASH(traceme_signalmasked_crash_fpe, SIGFPE) +TRACEME_SIGNALMASKED_CRASH(traceme_signalmasked_crash_bus, SIGBUS) + +/// ---------------------------------------------------------------------------- + +static void +traceme_signalignored_crash(int sig) +{ + const int sigval = SIGSTOP; + pid_t child, wpid; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + struct sigaction sa; + struct ptrace_siginfo info; + struct kinfo_proc2 kp; + size_t len = sizeof(kp); + + int name[6]; + const size_t namelen = __arraycount(name); + ki_sigset_t kp_sigignore; + +#ifndef PTRACE_ILLEGAL_ASM + if (sig == SIGILL) + atf_tc_skip("PTRACE_ILLEGAL_ASM not defined"); +#endif + + if (sig == SIGFPE && !are_fpu_exceptions_supported()) + atf_tc_skip("FP exceptions are not supported"); + + memset(&info, 0, sizeof(info)); + + DPRINTF("Before forking process PID=%d\n", getpid()); + SYSCALL_REQUIRE((child = fork()) != -1); + if (child == 0) { + DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = SIG_IGN; + sigemptyset(&sa.sa_mask); + + FORKEE_ASSERT(sigaction(sig, &sa, NULL) != -1); + + DPRINTF("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + DPRINTF("Before executing a trap\n"); + switch (sig) { + case SIGTRAP: + trigger_trap(); + break; + case SIGSEGV: + trigger_segv(); + break; + case SIGILL: + trigger_ill(); + break; + case SIGFPE: + trigger_fpe(); + break; + case SIGBUS: + trigger_bus(); + break; + default: + /* NOTREACHED */ + FORKEE_ASSERTX(0 && "This shall not be reached"); + } + + /* NOTREACHED */ + FORKEE_ASSERTX(0 && "This shall not be reached"); + } + DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + name[0] = CTL_KERN, + name[1] = KERN_PROC2, + name[2] = KERN_PROC_PID; + name[3] = child; + name[4] = sizeof(kp); + name[5] = 1; + + ATF_REQUIRE_EQ(sysctl(name, namelen, &kp, &len, NULL, 0), 0); + + kp_sigignore = kp.p_sigignore; + + DPRINTF("Before calling ptrace(2) with PT_GET_SIGINFO for child\n"); + SYSCALL_REQUIRE( + ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1); + + DPRINTF("Signal traced to lwpid=%d\n", info.psi_lwpid); + DPRINTF("Signal properties: si_signo=%#x si_code=%#x si_errno=%#x\n", + info.psi_siginfo.si_signo, info.psi_siginfo.si_code, + info.psi_siginfo.si_errno); + + ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, sigval); + ATF_REQUIRE_EQ(info.psi_siginfo.si_code, SI_LWP); + + DPRINTF("Before resuming the child process where it left off and " + "without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sig); + + DPRINTF("Before calling ptrace(2) with PT_GET_SIGINFO for child\n"); + SYSCALL_REQUIRE( + ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1); + + DPRINTF("Signal traced to lwpid=%d\n", info.psi_lwpid); + DPRINTF("Signal properties: si_signo=%#x si_code=%#x si_errno=%#x\n", + info.psi_siginfo.si_signo, info.psi_siginfo.si_code, + info.psi_siginfo.si_errno); + + ATF_REQUIRE_EQ(sysctl(name, namelen, &kp, &len, NULL, 0), 0); + + DPRINTF("kp_sigignore=" + "%#02" PRIx32 "%02" PRIx32 "%02" PRIx32 "%02" PRIx32"\n", + kp_sigignore.__bits[0], kp_sigignore.__bits[1], + kp_sigignore.__bits[2], kp_sigignore.__bits[3]); + + DPRINTF("kp.p_sigignore=" + "%#02" PRIx32 "%02" PRIx32 "%02" PRIx32 "%02" PRIx32"\n", + kp.p_sigignore.__bits[0], kp.p_sigignore.__bits[1], + kp.p_sigignore.__bits[2], kp.p_sigignore.__bits[3]); + + ATF_REQUIRE(!memcmp(&kp_sigignore, &kp.p_sigignore, sizeof(kp_sigignore))); + + ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, sig); + switch (sig) { + case SIGTRAP: + ATF_REQUIRE_EQ(info.psi_siginfo.si_code, TRAP_BRKPT); + break; + case SIGSEGV: + ATF_REQUIRE_EQ(info.psi_siginfo.si_code, SEGV_MAPERR); + break; + case SIGILL: + ATF_REQUIRE(info.psi_siginfo.si_code >= ILL_ILLOPC && + info.psi_siginfo.si_code <= ILL_BADSTK); + break; + case SIGFPE: +// XXXQEMU ATF_REQUIRE_EQ(info.psi_siginfo.si_code, FPE_FLTDIV); + break; + case SIGBUS: + ATF_REQUIRE_EQ(info.psi_siginfo.si_code, BUS_ADRERR); + break; + } + + SYSCALL_REQUIRE(ptrace(PT_KILL, child, NULL, 0) != -1); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_signaled(status, SIGKILL, 0); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} + +#define TRACEME_SIGNALIGNORED_CRASH(test, sig) \ +ATF_TC(test); \ +ATF_TC_HEAD(test, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", \ + "Verify ignored crash signal " #sig " in a child after " \ + "PT_TRACE_ME is delivered to its tracer"); \ +} \ + \ +ATF_TC_BODY(test, tc) \ +{ \ + \ + traceme_signalignored_crash(sig); \ +} + +TRACEME_SIGNALIGNORED_CRASH(traceme_signalignored_crash_trap, SIGTRAP) +TRACEME_SIGNALIGNORED_CRASH(traceme_signalignored_crash_segv, SIGSEGV) +TRACEME_SIGNALIGNORED_CRASH(traceme_signalignored_crash_ill, SIGILL) +TRACEME_SIGNALIGNORED_CRASH(traceme_signalignored_crash_fpe, SIGFPE) +TRACEME_SIGNALIGNORED_CRASH(traceme_signalignored_crash_bus, SIGBUS) + +/// ---------------------------------------------------------------------------- + +static void +traceme_sendsignal_handle(int sigsent, void (*sah)(int a), int *traceme_caught) +{ + const int exitval = 5; + const int sigval = SIGSTOP; + pid_t child, wpid; + struct sigaction sa; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + struct ptrace_siginfo info; + + memset(&info, 0, sizeof(info)); + + DPRINTF("Before forking process PID=%d\n", getpid()); + SYSCALL_REQUIRE((child = fork()) != -1); + if (child == 0) { + DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + sa.sa_handler = sah; + sa.sa_flags = SA_SIGINFO; + sigemptyset(&sa.sa_mask); + + FORKEE_ASSERT(sigaction(sigsent, &sa, NULL) != -1); + + DPRINTF("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + FORKEE_ASSERT_EQ(*traceme_caught, 1); + + DPRINTF("Before exiting of the child process\n"); + _exit(exitval); + } + DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + DPRINTF("Before calling ptrace(2) with PT_GET_SIGINFO for child\n"); + SYSCALL_REQUIRE( + ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1); + + DPRINTF("Signal traced to lwpid=%d\n", info.psi_lwpid); + DPRINTF("Signal properties: si_signo=%#x si_code=%#x si_errno=%#x\n", + info.psi_siginfo.si_signo, info.psi_siginfo.si_code, + info.psi_siginfo.si_errno); + + ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, sigval); + ATF_REQUIRE_EQ(info.psi_siginfo.si_code, SI_LWP); + + DPRINTF("Before resuming the child process where it left off and with " + "signal %s to be sent\n", strsignal(sigsent)); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, sigsent) != -1); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_exited(status, exitval); + + DPRINTF("Before calling %s() for the exited child\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} + +#define TRACEME_SENDSIGNAL_HANDLE(test, sig) \ +ATF_TC(test); \ +ATF_TC_HEAD(test, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", \ + "Verify that a signal " #sig " emitted by a tracer to a child is " \ + "handled correctly and caught by a signal handler"); \ +} \ + \ +static int test##_caught = 0; \ + \ +static void \ +test##_sighandler(int arg) \ +{ \ + FORKEE_ASSERT_EQ(arg, sig); \ + \ + ++ test##_caught; \ +} \ + \ +ATF_TC_BODY(test, tc) \ +{ \ + \ + traceme_sendsignal_handle(sig, test##_sighandler, & test##_caught); \ +} + +// A signal handler for SIGKILL and SIGSTOP cannot be registered. +TRACEME_SENDSIGNAL_HANDLE(traceme_sendsignal_handle1, SIGABRT) /* abort trap */ +TRACEME_SENDSIGNAL_HANDLE(traceme_sendsignal_handle2, SIGHUP) /* hangup */ +TRACEME_SENDSIGNAL_HANDLE(traceme_sendsignal_handle3, SIGCONT) /* continued? */ +TRACEME_SENDSIGNAL_HANDLE(traceme_sendsignal_handle4, SIGTRAP) /* crash sig. */ +TRACEME_SENDSIGNAL_HANDLE(traceme_sendsignal_handle5, SIGBUS) /* crash sig. */ +TRACEME_SENDSIGNAL_HANDLE(traceme_sendsignal_handle6, SIGILL) /* crash sig. */ +TRACEME_SENDSIGNAL_HANDLE(traceme_sendsignal_handle7, SIGFPE) /* crash sig. */ +TRACEME_SENDSIGNAL_HANDLE(traceme_sendsignal_handle8, SIGSEGV) /* crash sig. */ + +/// ---------------------------------------------------------------------------- + +static void +traceme_sendsignal_masked(int sigsent) +{ + const int exitval = 5; + const int sigval = SIGSTOP; + pid_t child, wpid; + sigset_t set; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + struct ptrace_siginfo info; + + memset(&info, 0, sizeof(info)); + + DPRINTF("Before forking process PID=%d\n", getpid()); + SYSCALL_REQUIRE((child = fork()) != -1); + if (child == 0) { + DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + sigemptyset(&set); + sigaddset(&set, sigsent); + FORKEE_ASSERT(sigprocmask(SIG_BLOCK, &set, NULL) != -1); + + DPRINTF("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + _exit(exitval); + } + DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + DPRINTF("Before calling ptrace(2) with PT_GET_SIGINFO for child\n"); + SYSCALL_REQUIRE( + ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1); + + DPRINTF("Signal traced to lwpid=%d\n", info.psi_lwpid); + DPRINTF("Signal properties: si_signo=%#x si_code=%#x si_errno=%#x\n", + info.psi_siginfo.si_signo, info.psi_siginfo.si_code, + info.psi_siginfo.si_errno); + + ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, sigval); + ATF_REQUIRE_EQ(info.psi_siginfo.si_code, SI_LWP); + + DPRINTF("Before resuming the child process where it left off and with " + "signal %s to be sent\n", strsignal(sigsent)); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, sigsent) != -1); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_exited(status, exitval); + + DPRINTF("Before calling %s() for the exited child\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} + +#define TRACEME_SENDSIGNAL_MASKED(test, sig) \ +ATF_TC(test); \ +ATF_TC_HEAD(test, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", \ + "Verify that a signal " #sig " emitted by a tracer to a child is " \ + "handled correctly and the signal is masked by SIG_BLOCK"); \ +} \ + \ +ATF_TC_BODY(test, tc) \ +{ \ + \ + traceme_sendsignal_masked(sig); \ +} + +// A signal handler for SIGKILL and SIGSTOP cannot be masked. +TRACEME_SENDSIGNAL_MASKED(traceme_sendsignal_masked1, SIGABRT) /* abort trap */ +TRACEME_SENDSIGNAL_MASKED(traceme_sendsignal_masked2, SIGHUP) /* hangup */ +TRACEME_SENDSIGNAL_MASKED(traceme_sendsignal_masked3, SIGCONT) /* continued? */ +TRACEME_SENDSIGNAL_MASKED(traceme_sendsignal_masked4, SIGTRAP) /* crash sig. */ +TRACEME_SENDSIGNAL_MASKED(traceme_sendsignal_masked5, SIGBUS) /* crash sig. */ +TRACEME_SENDSIGNAL_MASKED(traceme_sendsignal_masked6, SIGILL) /* crash sig. */ +TRACEME_SENDSIGNAL_MASKED(traceme_sendsignal_masked7, SIGFPE) /* crash sig. */ +TRACEME_SENDSIGNAL_MASKED(traceme_sendsignal_masked8, SIGSEGV) /* crash sig. */ + +/// ---------------------------------------------------------------------------- + +static void +traceme_sendsignal_ignored(int sigsent) +{ + const int exitval = 5; + const int sigval = SIGSTOP; + pid_t child, wpid; + struct sigaction sa; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + struct ptrace_siginfo info; + + memset(&info, 0, sizeof(info)); + + DPRINTF("Before forking process PID=%d\n", getpid()); + SYSCALL_REQUIRE((child = fork()) != -1); + if (child == 0) { + DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid()); + + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = SIG_IGN; + sigemptyset(&sa.sa_mask); + FORKEE_ASSERT(sigaction(sigsent, &sa, NULL) != -1); + + DPRINTF("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + _exit(exitval); + } + DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + DPRINTF("Before calling ptrace(2) with PT_GET_SIGINFO for child\n"); + SYSCALL_REQUIRE( + ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1); + + DPRINTF("Signal traced to lwpid=%d\n", info.psi_lwpid); + DPRINTF("Signal properties: si_signo=%#x si_code=%#x si_errno=%#x\n", + info.psi_siginfo.si_signo, info.psi_siginfo.si_code, + info.psi_siginfo.si_errno); + + ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, sigval); + ATF_REQUIRE_EQ(info.psi_siginfo.si_code, SI_LWP); + + DPRINTF("Before resuming the child process where it left off and with " + "signal %s to be sent\n", strsignal(sigsent)); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, sigsent) != -1); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_exited(status, exitval); + + DPRINTF("Before calling %s() for the exited child\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} + +#define TRACEME_SENDSIGNAL_IGNORED(test, sig) \ +ATF_TC(test); \ +ATF_TC_HEAD(test, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", \ + "Verify that a signal " #sig " emitted by a tracer to a child is " \ + "handled correctly and the signal is masked by SIG_IGN"); \ +} \ + \ +ATF_TC_BODY(test, tc) \ +{ \ + \ + traceme_sendsignal_ignored(sig); \ +} + +// A signal handler for SIGKILL and SIGSTOP cannot be ignored. +TRACEME_SENDSIGNAL_IGNORED(traceme_sendsignal_ignored1, SIGABRT) /* abort */ +TRACEME_SENDSIGNAL_IGNORED(traceme_sendsignal_ignored2, SIGHUP) /* hangup */ +TRACEME_SENDSIGNAL_IGNORED(traceme_sendsignal_ignored3, SIGCONT) /* continued */ +TRACEME_SENDSIGNAL_IGNORED(traceme_sendsignal_ignored4, SIGTRAP) /* crash s. */ +TRACEME_SENDSIGNAL_IGNORED(traceme_sendsignal_ignored5, SIGBUS) /* crash s. */ +TRACEME_SENDSIGNAL_IGNORED(traceme_sendsignal_ignored6, SIGILL) /* crash s. */ +TRACEME_SENDSIGNAL_IGNORED(traceme_sendsignal_ignored7, SIGFPE) /* crash s. */ +TRACEME_SENDSIGNAL_IGNORED(traceme_sendsignal_ignored8, SIGSEGV) /* crash s. */ + +/// ---------------------------------------------------------------------------- + +static void +traceme_sendsignal_simple(int sigsent) +{ + const int sigval = SIGSTOP; + int exitval = 0; + pid_t child, wpid; +#if defined(TWAIT_HAVE_STATUS) + int status; + int expect_core; + + switch (sigsent) { + case SIGABRT: + case SIGTRAP: + case SIGBUS: + case SIGILL: + case SIGFPE: + case SIGSEGV: + expect_core = 1; + break; + default: + expect_core = 0; + break; + } +#endif + struct ptrace_siginfo info; + + memset(&info, 0, sizeof(info)); + + DPRINTF("Before forking process PID=%d\n", getpid()); + SYSCALL_REQUIRE((child = fork()) != -1); + if (child == 0) { + DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + DPRINTF("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + switch (sigsent) { + case SIGCONT: + case SIGSTOP: + _exit(exitval); + default: + /* NOTREACHED */ + FORKEE_ASSERTX(0 && "This shall not be reached"); + } + } + DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + DPRINTF("Before calling ptrace(2) with PT_GET_SIGINFO for child\n"); + SYSCALL_REQUIRE( + ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1); + + DPRINTF("Signal traced to lwpid=%d\n", info.psi_lwpid); + DPRINTF("Signal properties: si_signo=%#x si_code=%#x si_errno=%#x\n", + info.psi_siginfo.si_signo, info.psi_siginfo.si_code, + info.psi_siginfo.si_errno); + + ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, sigval); + ATF_REQUIRE_EQ(info.psi_siginfo.si_code, SI_LWP); + + DPRINTF("Before resuming the child process where it left off and with " + "signal %s to be sent\n", strsignal(sigsent)); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, sigsent) != -1); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + switch (sigsent) { + case SIGSTOP: + validate_status_stopped(status, sigsent); + DPRINTF("Before calling ptrace(2) with PT_GET_SIGINFO for " + "child\n"); + SYSCALL_REQUIRE(ptrace(PT_GET_SIGINFO, child, &info, + sizeof(info)) != -1); + + DPRINTF("Signal traced to lwpid=%d\n", info.psi_lwpid); + DPRINTF("Signal properties: si_signo=%#x si_code=%#x " + "si_errno=%#x\n", + info.psi_siginfo.si_signo, info.psi_siginfo.si_code, + info.psi_siginfo.si_errno); + + ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, sigval); + ATF_REQUIRE_EQ(info.psi_siginfo.si_code, SI_LWP); + + DPRINTF("Before resuming the child process where it left off " + "and with signal %s to be sent\n", strsignal(sigsent)); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), + child); + /* FALLTHROUGH */ + case SIGCONT: + validate_status_exited(status, exitval); + break; + default: + validate_status_signaled(status, sigsent, expect_core); + break; + } + + DPRINTF("Before calling %s() for the exited child\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} + +#define TRACEME_SENDSIGNAL_SIMPLE(test, sig) \ +ATF_TC(test); \ +ATF_TC_HEAD(test, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", \ + "Verify that a signal " #sig " emitted by a tracer to a child is " \ + "handled correctly in a child without a signal handler"); \ +} \ + \ +ATF_TC_BODY(test, tc) \ +{ \ + \ + traceme_sendsignal_simple(sig); \ +} + +TRACEME_SENDSIGNAL_SIMPLE(traceme_sendsignal_simple1, SIGKILL) /* non-maskable*/ +TRACEME_SENDSIGNAL_SIMPLE(traceme_sendsignal_simple2, SIGSTOP) /* non-maskable*/ +TRACEME_SENDSIGNAL_SIMPLE(traceme_sendsignal_simple3, SIGABRT) /* abort trap */ +TRACEME_SENDSIGNAL_SIMPLE(traceme_sendsignal_simple4, SIGHUP) /* hangup */ +TRACEME_SENDSIGNAL_SIMPLE(traceme_sendsignal_simple5, SIGCONT) /* continued? */ +TRACEME_SENDSIGNAL_SIMPLE(traceme_sendsignal_simple6, SIGTRAP) /* crash sig. */ +TRACEME_SENDSIGNAL_SIMPLE(traceme_sendsignal_simple7, SIGBUS) /* crash sig. */ +TRACEME_SENDSIGNAL_SIMPLE(traceme_sendsignal_simple8, SIGILL) /* crash sig. */ +TRACEME_SENDSIGNAL_SIMPLE(traceme_sendsignal_simple9, SIGFPE) /* crash sig. */ +TRACEME_SENDSIGNAL_SIMPLE(traceme_sendsignal_simple10, SIGSEGV) /* crash sig. */ + +/// ---------------------------------------------------------------------------- + +static void +traceme_vfork_raise(int sigval) +{ + const int exitval = 5, exitval_watcher = 10; + pid_t child, parent, watcher, wpid; + int rv; +#if defined(TWAIT_HAVE_STATUS) + int status; + + /* volatile workarounds GCC -Werror=clobbered */ + volatile int expect_core; + + switch (sigval) { + case SIGABRT: + case SIGTRAP: + case SIGBUS: + case SIGILL: + case SIGFPE: + case SIGSEGV: + expect_core = 1; + break; + default: + expect_core = 0; + break; + } +#endif + + /* + * Spawn a dedicated thread to watch for a stopped child and emit + * the SIGKILL signal to it. + * + * vfork(2) might clobber watcher, this means that it's safer and + * simpler to reparent this process to initproc and forget about it. + */ + if (sigval == SIGSTOP) { + parent = getpid(); + + watcher = fork(); + ATF_REQUIRE(watcher != 1); + if (watcher == 0) { + /* Double fork(2) trick to reparent to initproc */ + watcher = fork(); + FORKEE_ASSERT_NEQ(watcher, -1); + if (watcher != 0) + _exit(exitval_watcher); + + child = await_stopped_child(parent); + + errno = 0; + rv = kill(child, SIGKILL); + FORKEE_ASSERT_EQ(rv, 0); + FORKEE_ASSERT_EQ(errno, 0); + + /* This exit value will be collected by initproc */ + _exit(0); + } + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(watcher, &status, 0), + watcher); + + validate_status_exited(status, exitval_watcher); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, + wpid = TWAIT_GENERIC(watcher, &status, 0)); + } + + DPRINTF("Before forking process PID=%d\n", getpid()); + SYSCALL_REQUIRE((child = vfork()) != -1); + if (child == 0) { + DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + DPRINTF("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + switch (sigval) { + case SIGSTOP: + case SIGKILL: + case SIGABRT: + case SIGHUP: + case SIGTRAP: + case SIGBUS: + case SIGILL: + case SIGFPE: + case SIGSEGV: + /* NOTREACHED */ + FORKEE_ASSERTX(0 && "This shall not be reached"); + __unreachable(); + default: + DPRINTF("Before exiting of the child process\n"); + _exit(exitval); + } + } + DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + switch (sigval) { + case SIGKILL: + case SIGABRT: + case SIGHUP: + case SIGTRAP: + case SIGBUS: + case SIGILL: + case SIGFPE: + case SIGSEGV: + validate_status_signaled(status, sigval, expect_core); + break; + case SIGSTOP: + validate_status_signaled(status, SIGKILL, 0); + break; + case SIGCONT: + case SIGTSTP: + case SIGTTIN: + case SIGTTOU: + validate_status_exited(status, exitval); + break; + default: + /* NOTREACHED */ + ATF_REQUIRE(0 && "NOT IMPLEMENTED"); + break; + } + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} + +#define TRACEME_VFORK_RAISE(test, sig) \ +ATF_TC(test); \ +ATF_TC_HEAD(test, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", \ + "Verify PT_TRACE_ME followed by raise of " #sig " in a " \ + "vfork(2)ed child"); \ +} \ + \ +ATF_TC_BODY(test, tc) \ +{ \ + \ + traceme_vfork_raise(sig); \ +} + +TRACEME_VFORK_RAISE(traceme_vfork_raise1, SIGKILL) /* non-maskable */ +TRACEME_VFORK_RAISE(traceme_vfork_raise2, SIGSTOP) /* non-maskable */ +TRACEME_VFORK_RAISE(traceme_vfork_raise3, SIGTSTP) /* ignored in vfork(2) */ +TRACEME_VFORK_RAISE(traceme_vfork_raise4, SIGTTIN) /* ignored in vfork(2) */ +TRACEME_VFORK_RAISE(traceme_vfork_raise5, SIGTTOU) /* ignored in vfork(2) */ +TRACEME_VFORK_RAISE(traceme_vfork_raise6, SIGABRT) /* regular abort trap */ +TRACEME_VFORK_RAISE(traceme_vfork_raise7, SIGHUP) /* hangup */ +TRACEME_VFORK_RAISE(traceme_vfork_raise8, SIGCONT) /* continued? */ +TRACEME_VFORK_RAISE(traceme_vfork_raise9, SIGTRAP) /* crash signal */ +TRACEME_VFORK_RAISE(traceme_vfork_raise10, SIGBUS) /* crash signal */ +TRACEME_VFORK_RAISE(traceme_vfork_raise11, SIGILL) /* crash signal */ +TRACEME_VFORK_RAISE(traceme_vfork_raise12, SIGFPE) /* crash signal */ +TRACEME_VFORK_RAISE(traceme_vfork_raise13, SIGSEGV) /* crash signal */ + +/// ---------------------------------------------------------------------------- + +static void +traceme_vfork_crash(int sig) +{ + pid_t child, wpid; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + +#ifndef PTRACE_ILLEGAL_ASM + if (sig == SIGILL) + atf_tc_skip("PTRACE_ILLEGAL_ASM not defined"); +#endif + + if (sig == SIGFPE && !are_fpu_exceptions_supported()) + atf_tc_skip("FP exceptions are not supported"); + + DPRINTF("Before forking process PID=%d\n", getpid()); + SYSCALL_REQUIRE((child = vfork()) != -1); + if (child == 0) { + DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + DPRINTF("Before executing a trap\n"); + switch (sig) { + case SIGTRAP: + trigger_trap(); + break; + case SIGSEGV: + trigger_segv(); + break; + case SIGILL: + trigger_ill(); + break; + case SIGFPE: + trigger_fpe(); + break; + case SIGBUS: + trigger_bus(); + break; + default: + /* NOTREACHED */ + FORKEE_ASSERTX(0 && "This shall not be reached"); + } + + /* NOTREACHED */ + FORKEE_ASSERTX(0 && "This shall not be reached"); + } + DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_signaled(status, sig, 1); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} + +#define TRACEME_VFORK_CRASH(test, sig) \ +ATF_TC(test); \ +ATF_TC_HEAD(test, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", \ + "Verify PT_TRACE_ME followed by a crash signal " #sig " in a " \ + "vfork(2)ed child"); \ +} \ + \ +ATF_TC_BODY(test, tc) \ +{ \ + \ + traceme_vfork_crash(sig); \ +} + +TRACEME_VFORK_CRASH(traceme_vfork_crash_trap, SIGTRAP) +TRACEME_VFORK_CRASH(traceme_vfork_crash_segv, SIGSEGV) +TRACEME_VFORK_CRASH(traceme_vfork_crash_ill, SIGILL) +TRACEME_VFORK_CRASH(traceme_vfork_crash_fpe, SIGFPE) +TRACEME_VFORK_CRASH(traceme_vfork_crash_bus, SIGBUS) + +/// ---------------------------------------------------------------------------- + +static void +traceme_vfork_signalmasked_crash(int sig) +{ + pid_t child, wpid; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + sigset_t intmask; + +#ifndef PTRACE_ILLEGAL_ASM + if (sig == SIGILL) + atf_tc_skip("PTRACE_ILLEGAL_ASM not defined"); +#endif + + if (sig == SIGFPE && !are_fpu_exceptions_supported()) + atf_tc_skip("FP exceptions are not supported"); + + DPRINTF("Before forking process PID=%d\n", getpid()); + SYSCALL_REQUIRE((child = vfork()) != -1); + if (child == 0) { + DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + sigemptyset(&intmask); + sigaddset(&intmask, sig); + sigprocmask(SIG_BLOCK, &intmask, NULL); + + DPRINTF("Before executing a trap\n"); + switch (sig) { + case SIGTRAP: + trigger_trap(); + break; + case SIGSEGV: + trigger_segv(); + break; + case SIGILL: + trigger_ill(); + break; + case SIGFPE: + trigger_fpe(); + break; + case SIGBUS: + trigger_bus(); + break; + default: + /* NOTREACHED */ + FORKEE_ASSERTX(0 && "This shall not be reached"); + } + + /* NOTREACHED */ + FORKEE_ASSERTX(0 && "This shall not be reached"); + } + DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_signaled(status, sig, 1); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} + +#define TRACEME_VFORK_SIGNALMASKED_CRASH(test, sig) \ +ATF_TC(test); \ +ATF_TC_HEAD(test, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", \ + "Verify PT_TRACE_ME followed by a crash signal " #sig " in a " \ + "vfork(2)ed child with a masked signal"); \ +} \ + \ +ATF_TC_BODY(test, tc) \ +{ \ + \ + traceme_vfork_signalmasked_crash(sig); \ +} + +TRACEME_VFORK_SIGNALMASKED_CRASH(traceme_vfork_signalmasked_crash_trap, SIGTRAP) +TRACEME_VFORK_SIGNALMASKED_CRASH(traceme_vfork_signalmasked_crash_segv, SIGSEGV) +TRACEME_VFORK_SIGNALMASKED_CRASH(traceme_vfork_signalmasked_crash_ill, SIGILL) +TRACEME_VFORK_SIGNALMASKED_CRASH(traceme_vfork_signalmasked_crash_fpe, SIGFPE) +TRACEME_VFORK_SIGNALMASKED_CRASH(traceme_vfork_signalmasked_crash_bus, SIGBUS) + +/// ---------------------------------------------------------------------------- + +static void +traceme_vfork_signalignored_crash(int sig) +{ + pid_t child, wpid; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + struct sigaction sa; + +#ifndef PTRACE_ILLEGAL_ASM + if (sig == SIGILL) + atf_tc_skip("PTRACE_ILLEGAL_ASM not defined"); +#endif + + if (sig == SIGFPE && !are_fpu_exceptions_supported()) + atf_tc_skip("FP exceptions are not supported"); + + DPRINTF("Before forking process PID=%d\n", getpid()); + SYSCALL_REQUIRE((child = vfork()) != -1); + if (child == 0) { + DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = SIG_IGN; + sigemptyset(&sa.sa_mask); + + FORKEE_ASSERT(sigaction(sig, &sa, NULL) != -1); + + DPRINTF("Before executing a trap\n"); + switch (sig) { + case SIGTRAP: + trigger_trap(); + break; + case SIGSEGV: + trigger_segv(); + break; + case SIGILL: + trigger_ill(); + break; + case SIGFPE: + trigger_fpe(); + break; + case SIGBUS: + trigger_bus(); + break; + default: + /* NOTREACHED */ + FORKEE_ASSERTX(0 && "This shall not be reached"); + } + + /* NOTREACHED */ + FORKEE_ASSERTX(0 && "This shall not be reached"); + } + DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_signaled(status, sig, 1); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} + +#define TRACEME_VFORK_SIGNALIGNORED_CRASH(test, sig) \ +ATF_TC(test); \ +ATF_TC_HEAD(test, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", \ + "Verify PT_TRACE_ME followed by a crash signal " #sig " in a " \ + "vfork(2)ed child with ignored signal"); \ +} \ + \ +ATF_TC_BODY(test, tc) \ +{ \ + \ + traceme_vfork_signalignored_crash(sig); \ +} + +TRACEME_VFORK_SIGNALIGNORED_CRASH(traceme_vfork_signalignored_crash_trap, + SIGTRAP) +TRACEME_VFORK_SIGNALIGNORED_CRASH(traceme_vfork_signalignored_crash_segv, + SIGSEGV) +TRACEME_VFORK_SIGNALIGNORED_CRASH(traceme_vfork_signalignored_crash_ill, + SIGILL) +TRACEME_VFORK_SIGNALIGNORED_CRASH(traceme_vfork_signalignored_crash_fpe, + SIGFPE) +TRACEME_VFORK_SIGNALIGNORED_CRASH(traceme_vfork_signalignored_crash_bus, + SIGBUS) + +/// ---------------------------------------------------------------------------- + +#if defined(TWAIT_HAVE_PID) +static void +unrelated_tracer_sees_crash(int sig, bool masked, bool ignored) +{ + const int sigval = SIGSTOP; + struct msg_fds parent_tracee, parent_tracer; + const int exitval = 10; + pid_t tracee, tracer, wpid; + uint8_t msg = 0xde; /* dummy message for IPC based on pipe(2) */ +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + struct sigaction sa; + struct ptrace_siginfo info; + sigset_t intmask; + struct kinfo_proc2 kp; + size_t len = sizeof(kp); + + int name[6]; + const size_t namelen = __arraycount(name); + ki_sigset_t kp_sigmask; + ki_sigset_t kp_sigignore; + +#ifndef PTRACE_ILLEGAL_ASM + if (sig == SIGILL) + atf_tc_skip("PTRACE_ILLEGAL_ASM not defined"); +#endif + + if (sig == SIGFPE && !are_fpu_exceptions_supported()) + atf_tc_skip("FP exceptions are not supported"); + + memset(&info, 0, sizeof(info)); + + DPRINTF("Spawn tracee\n"); + SYSCALL_REQUIRE(msg_open(&parent_tracee) == 0); + tracee = atf_utils_fork(); + if (tracee == 0) { + // Wait for parent to let us crash + CHILD_FROM_PARENT("exit tracee", parent_tracee, msg); + + if (masked) { + sigemptyset(&intmask); + sigaddset(&intmask, sig); + sigprocmask(SIG_BLOCK, &intmask, NULL); + } + + if (ignored) { + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = SIG_IGN; + sigemptyset(&sa.sa_mask); + FORKEE_ASSERT(sigaction(sig, &sa, NULL) != -1); + } + + DPRINTF("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + DPRINTF("Before executing a trap\n"); + switch (sig) { + case SIGTRAP: + trigger_trap(); + break; + case SIGSEGV: + trigger_segv(); + break; + case SIGILL: + trigger_ill(); + break; + case SIGFPE: + trigger_fpe(); + break; + case SIGBUS: + trigger_bus(); + break; + default: + /* NOTREACHED */ + FORKEE_ASSERTX(0 && "This shall not be reached"); + } + + /* NOTREACHED */ + FORKEE_ASSERTX(0 && "This shall not be reached"); + } + + DPRINTF("Spawn debugger\n"); + SYSCALL_REQUIRE(msg_open(&parent_tracer) == 0); + tracer = atf_utils_fork(); + if (tracer == 0) { + /* Fork again and drop parent to reattach to PID 1 */ + tracer = atf_utils_fork(); + if (tracer != 0) + _exit(exitval); + + DPRINTF("Before calling PT_ATTACH from tracee %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_ATTACH, tracee, NULL, 0) != -1); + + /* Wait for tracee and assert that it was stopped w/ SIGSTOP */ + FORKEE_REQUIRE_SUCCESS( + wpid = TWAIT_GENERIC(tracee, &status, 0), tracee); + + forkee_status_stopped(status, SIGSTOP); + + DPRINTF("Before calling ptrace(2) with PT_GET_SIGINFO for the " + "traced process\n"); + SYSCALL_REQUIRE( + ptrace(PT_GET_SIGINFO, tracee, &info, sizeof(info)) != -1); + + DPRINTF("Signal traced to lwpid=%d\n", info.psi_lwpid); + DPRINTF("Signal properties: si_signo=%#x si_code=%#x " + "si_errno=%#x\n", info.psi_siginfo.si_signo, + info.psi_siginfo.si_code, info.psi_siginfo.si_errno); + + FORKEE_ASSERT_EQ(info.psi_siginfo.si_signo, SIGSTOP); + FORKEE_ASSERT_EQ(info.psi_siginfo.si_code, SI_USER); + + /* Resume tracee with PT_CONTINUE */ + FORKEE_ASSERT(ptrace(PT_CONTINUE, tracee, (void *)1, 0) != -1); + + /* Inform parent that tracer has attached to tracee */ + CHILD_TO_PARENT("tracer ready", parent_tracer, msg); + + /* Wait for parent to tell use that tracee should have exited */ + CHILD_FROM_PARENT("wait for tracee exit", parent_tracer, msg); + + /* Wait for tracee and assert that it exited */ + FORKEE_REQUIRE_SUCCESS( + wpid = TWAIT_GENERIC(tracee, &status, 0), tracee); + + forkee_status_stopped(status, sigval); + + DPRINTF("Before calling ptrace(2) with PT_GET_SIGINFO for the " + "traced process\n"); + SYSCALL_REQUIRE( + ptrace(PT_GET_SIGINFO, tracee, &info, sizeof(info)) != -1); + + DPRINTF("Signal traced to lwpid=%d\n", info.psi_lwpid); + DPRINTF("Signal properties: si_signo=%#x si_code=%#x " + "si_errno=%#x\n", info.psi_siginfo.si_signo, + info.psi_siginfo.si_code, info.psi_siginfo.si_errno); + + FORKEE_ASSERT_EQ(info.psi_siginfo.si_signo, sigval); + FORKEE_ASSERT_EQ(info.psi_siginfo.si_code, SI_LWP); + + name[0] = CTL_KERN, + name[1] = KERN_PROC2, + name[2] = KERN_PROC_PID; + name[3] = tracee; + name[4] = sizeof(kp); + name[5] = 1; + + FORKEE_ASSERT_EQ(sysctl(name, namelen, &kp, &len, NULL, 0), 0); + + if (masked) + kp_sigmask = kp.p_sigmask; + + if (ignored) + kp_sigignore = kp.p_sigignore; + + /* Resume tracee with PT_CONTINUE */ + FORKEE_ASSERT(ptrace(PT_CONTINUE, tracee, (void *)1, 0) != -1); + + /* Wait for tracee and assert that it exited */ + FORKEE_REQUIRE_SUCCESS( + wpid = TWAIT_GENERIC(tracee, &status, 0), tracee); + + forkee_status_stopped(status, sig); + + DPRINTF("Before calling ptrace(2) with PT_GET_SIGINFO for the " + "traced process\n"); + SYSCALL_REQUIRE( + ptrace(PT_GET_SIGINFO, tracee, &info, sizeof(info)) != -1); + + DPRINTF("Signal traced to lwpid=%d\n", info.psi_lwpid); + DPRINTF("Signal properties: si_signo=%#x si_code=%#x " + "si_errno=%#x\n", info.psi_siginfo.si_signo, + info.psi_siginfo.si_code, info.psi_siginfo.si_errno); + + FORKEE_ASSERT_EQ(info.psi_siginfo.si_signo, sig); + + FORKEE_ASSERT_EQ(sysctl(name, namelen, &kp, &len, NULL, 0), 0); + + if (masked) { + DPRINTF("kp_sigmask=" + "%#02" PRIx32 "%02" PRIx32 "%02" PRIx32 "%02" + PRIx32 "\n", + kp_sigmask.__bits[0], kp_sigmask.__bits[1], + kp_sigmask.__bits[2], kp_sigmask.__bits[3]); + + DPRINTF("kp.p_sigmask=" + "%#02" PRIx32 "%02" PRIx32 "%02" PRIx32 "%02" + PRIx32 "\n", + kp.p_sigmask.__bits[0], kp.p_sigmask.__bits[1], + kp.p_sigmask.__bits[2], kp.p_sigmask.__bits[3]); + + FORKEE_ASSERTX(!memcmp(&kp_sigmask, &kp.p_sigmask, + sizeof(kp_sigmask))); + } + + if (ignored) { + DPRINTF("kp_sigignore=" + "%#02" PRIx32 "%02" PRIx32 "%02" PRIx32 "%02" + PRIx32 "\n", + kp_sigignore.__bits[0], kp_sigignore.__bits[1], + kp_sigignore.__bits[2], kp_sigignore.__bits[3]); + + DPRINTF("kp.p_sigignore=" + "%#02" PRIx32 "%02" PRIx32 "%02" PRIx32 "%02" + PRIx32 "\n", + kp.p_sigignore.__bits[0], kp.p_sigignore.__bits[1], + kp.p_sigignore.__bits[2], kp.p_sigignore.__bits[3]); + + FORKEE_ASSERTX(!memcmp(&kp_sigignore, &kp.p_sigignore, + sizeof(kp_sigignore))); + } + + switch (sig) { + case SIGTRAP: + FORKEE_ASSERT_EQ(info.psi_siginfo.si_code, TRAP_BRKPT); + break; + case SIGSEGV: + FORKEE_ASSERT_EQ(info.psi_siginfo.si_code, SEGV_MAPERR); + break; + case SIGILL: + FORKEE_ASSERT(info.psi_siginfo.si_code >= ILL_ILLOPC && + info.psi_siginfo.si_code <= ILL_BADSTK); + break; + case SIGFPE: +// XXXQEMU FORKEE_ASSERT_EQ(info.psi_siginfo.si_code, FPE_FLTDIV); + break; + case SIGBUS: + FORKEE_ASSERT_EQ(info.psi_siginfo.si_code, BUS_ADRERR); + break; + } + + FORKEE_ASSERT(ptrace(PT_KILL, tracee, NULL, 0) != -1); + DPRINTF("Before calling %s() for the tracee\n", TWAIT_FNAME); + FORKEE_REQUIRE_SUCCESS( + wpid = TWAIT_GENERIC(tracee, &status, 0), tracee); + + forkee_status_signaled(status, SIGKILL, 0); + + /* Inform parent that tracer is exiting normally */ + CHILD_TO_PARENT("tracer done", parent_tracer, msg); + + DPRINTF("Before exiting of the tracer process\n"); + _exit(0 /* collect by initproc */); + } + + DPRINTF("Wait for the tracer process (direct child) to exit " + "calling %s()\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS( + wpid = TWAIT_GENERIC(tracer, &status, 0), tracer); + + validate_status_exited(status, exitval); + + DPRINTF("Wait for the non-exited tracee process with %s()\n", + TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS( + wpid = TWAIT_GENERIC(tracee, NULL, WNOHANG), 0); + + DPRINTF("Wait for the tracer to attach to the tracee\n"); + PARENT_FROM_CHILD("tracer ready", parent_tracer, msg); + + DPRINTF("Resume the tracee and let it crash\n"); + PARENT_TO_CHILD("exit tracee", parent_tracee, msg); + + DPRINTF("Resume the tracer and let it detect crashed tracee\n"); + PARENT_TO_CHILD("Message 2", parent_tracer, msg); + + DPRINTF("Wait for tracee to finish its job and exit - calling %s()\n", + TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(tracee, &status, 0), tracee); + + validate_status_signaled(status, SIGKILL, 0); + + DPRINTF("Await normal exit of tracer\n"); + PARENT_FROM_CHILD("tracer done", parent_tracer, msg); + + msg_close(&parent_tracer); + msg_close(&parent_tracee); +} + +#define UNRELATED_TRACER_SEES_CRASH(test, sig) \ +ATF_TC(test); \ +ATF_TC_HEAD(test, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", \ + "Assert that an unrelated tracer sees crash signal from " \ + "the debuggee"); \ +} \ + \ +ATF_TC_BODY(test, tc) \ +{ \ + \ + unrelated_tracer_sees_crash(sig, false, false); \ +} + +UNRELATED_TRACER_SEES_CRASH(unrelated_tracer_sees_crash_trap, SIGTRAP) +UNRELATED_TRACER_SEES_CRASH(unrelated_tracer_sees_crash_segv, SIGSEGV) +UNRELATED_TRACER_SEES_CRASH(unrelated_tracer_sees_crash_ill, SIGILL) +UNRELATED_TRACER_SEES_CRASH(unrelated_tracer_sees_crash_fpe, SIGFPE) +UNRELATED_TRACER_SEES_CRASH(unrelated_tracer_sees_crash_bus, SIGBUS) + +#define UNRELATED_TRACER_SEES_SIGNALMASKED_CRASH(test, sig) \ +ATF_TC(test); \ +ATF_TC_HEAD(test, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", \ + "Assert that an unrelated tracer sees crash signal from " \ + "the debuggee with masked signal"); \ +} \ + \ +ATF_TC_BODY(test, tc) \ +{ \ + \ + unrelated_tracer_sees_crash(sig, true, false); \ +} + +UNRELATED_TRACER_SEES_SIGNALMASKED_CRASH( + unrelated_tracer_sees_signalmasked_crash_trap, SIGTRAP) +UNRELATED_TRACER_SEES_SIGNALMASKED_CRASH( + unrelated_tracer_sees_signalmasked_crash_segv, SIGSEGV) +UNRELATED_TRACER_SEES_SIGNALMASKED_CRASH( + unrelated_tracer_sees_signalmasked_crash_ill, SIGILL) +UNRELATED_TRACER_SEES_SIGNALMASKED_CRASH( + unrelated_tracer_sees_signalmasked_crash_fpe, SIGFPE) +UNRELATED_TRACER_SEES_SIGNALMASKED_CRASH( + unrelated_tracer_sees_signalmasked_crash_bus, SIGBUS) + +#define UNRELATED_TRACER_SEES_SIGNALIGNORED_CRASH(test, sig) \ +ATF_TC(test); \ +ATF_TC_HEAD(test, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", \ + "Assert that an unrelated tracer sees crash signal from " \ + "the debuggee with signal ignored"); \ +} \ + \ +ATF_TC_BODY(test, tc) \ +{ \ + \ + unrelated_tracer_sees_crash(sig, false, true); \ +} + +UNRELATED_TRACER_SEES_SIGNALIGNORED_CRASH( + unrelated_tracer_sees_signalignored_crash_trap, SIGTRAP) +UNRELATED_TRACER_SEES_SIGNALIGNORED_CRASH( + unrelated_tracer_sees_signalignored_crash_segv, SIGSEGV) +UNRELATED_TRACER_SEES_SIGNALIGNORED_CRASH( + unrelated_tracer_sees_signalignored_crash_ill, SIGILL) +UNRELATED_TRACER_SEES_SIGNALIGNORED_CRASH( + unrelated_tracer_sees_signalignored_crash_fpe, SIGFPE) +UNRELATED_TRACER_SEES_SIGNALIGNORED_CRASH( + unrelated_tracer_sees_signalignored_crash_bus, SIGBUS) +#endif + +/// ---------------------------------------------------------------------------- + +ATF_TC(signal_mask_unrelated); +ATF_TC_HEAD(signal_mask_unrelated, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that masking single unrelated signal does not stop tracer " + "from catching other signals"); +} + +ATF_TC_BODY(signal_mask_unrelated, tc) +{ + const int exitval = 5; + const int sigval = SIGSTOP; + const int sigmasked = SIGTRAP; + const int signotmasked = SIGINT; + pid_t child, wpid; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + sigset_t intmask; + + DPRINTF("Before forking process PID=%d\n", getpid()); + SYSCALL_REQUIRE((child = fork()) != -1); + if (child == 0) { + DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + sigemptyset(&intmask); + sigaddset(&intmask, sigmasked); + sigprocmask(SIG_BLOCK, &intmask, NULL); + + DPRINTF("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + DPRINTF("Before raising %s from child\n", + strsignal(signotmasked)); + FORKEE_ASSERT(raise(signotmasked) == 0); + + DPRINTF("Before exiting of the child process\n"); + _exit(exitval); + } + DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + DPRINTF("Before resuming the child process where it left off and " + "without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, signotmasked); + + DPRINTF("Before resuming the child process where it left off and " + "without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_exited(status, exitval); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} + +/// ---------------------------------------------------------------------------- + +#define ATF_TP_ADD_TCS_PTRACE_WAIT_SIGNAL() \ + ATF_TP_ADD_TC(tp, traceme_raise1); \ + ATF_TP_ADD_TC(tp, traceme_raise2); \ + ATF_TP_ADD_TC(tp, traceme_raise3); \ + ATF_TP_ADD_TC(tp, traceme_raise4); \ + ATF_TP_ADD_TC(tp, traceme_raise5); \ + ATF_TP_ADD_TC(tp, traceme_raise6); \ + ATF_TP_ADD_TC(tp, traceme_raise7); \ + ATF_TP_ADD_TC(tp, traceme_raise8); \ + ATF_TP_ADD_TC(tp, traceme_raise9); \ + ATF_TP_ADD_TC(tp, traceme_raise10); \ + ATF_TP_ADD_TC(tp, traceme_raisesignal_ignored1); \ + ATF_TP_ADD_TC(tp, traceme_raisesignal_ignored2); \ + ATF_TP_ADD_TC(tp, traceme_raisesignal_ignored3); \ + ATF_TP_ADD_TC(tp, traceme_raisesignal_ignored4); \ + ATF_TP_ADD_TC(tp, traceme_raisesignal_ignored5); \ + ATF_TP_ADD_TC(tp, traceme_raisesignal_ignored6); \ + ATF_TP_ADD_TC(tp, traceme_raisesignal_ignored7); \ + ATF_TP_ADD_TC(tp, traceme_raisesignal_ignored8); \ + ATF_TP_ADD_TC(tp, traceme_raisesignal_masked1); \ + ATF_TP_ADD_TC(tp, traceme_raisesignal_masked2); \ + ATF_TP_ADD_TC(tp, traceme_raisesignal_masked3); \ + ATF_TP_ADD_TC(tp, traceme_raisesignal_masked4); \ + ATF_TP_ADD_TC(tp, traceme_raisesignal_masked5); \ + ATF_TP_ADD_TC(tp, traceme_raisesignal_masked6); \ + ATF_TP_ADD_TC(tp, traceme_raisesignal_masked7); \ + ATF_TP_ADD_TC(tp, traceme_raisesignal_masked8); \ + ATF_TP_ADD_TC(tp, traceme_crash_trap); \ + ATF_TP_ADD_TC(tp, traceme_crash_segv); \ + ATF_TP_ADD_TC(tp, traceme_crash_ill); \ + ATF_TP_ADD_TC(tp, traceme_crash_fpe); \ + ATF_TP_ADD_TC(tp, traceme_crash_bus); \ + ATF_TP_ADD_TC(tp, traceme_signalmasked_crash_trap); \ + ATF_TP_ADD_TC(tp, traceme_signalmasked_crash_segv); \ + ATF_TP_ADD_TC(tp, traceme_signalmasked_crash_ill); \ + ATF_TP_ADD_TC(tp, traceme_signalmasked_crash_fpe); \ + ATF_TP_ADD_TC(tp, traceme_signalmasked_crash_bus); \ + ATF_TP_ADD_TC(tp, traceme_signalignored_crash_trap); \ + ATF_TP_ADD_TC(tp, traceme_signalignored_crash_segv); \ + ATF_TP_ADD_TC(tp, traceme_signalignored_crash_ill); \ + ATF_TP_ADD_TC(tp, traceme_signalignored_crash_fpe); \ + ATF_TP_ADD_TC(tp, traceme_signalignored_crash_bus); \ + ATF_TP_ADD_TC(tp, traceme_sendsignal_handle1); \ + ATF_TP_ADD_TC(tp, traceme_sendsignal_handle2); \ + ATF_TP_ADD_TC(tp, traceme_sendsignal_handle3); \ + ATF_TP_ADD_TC(tp, traceme_sendsignal_handle4); \ + ATF_TP_ADD_TC(tp, traceme_sendsignal_handle5); \ + ATF_TP_ADD_TC(tp, traceme_sendsignal_handle6); \ + ATF_TP_ADD_TC(tp, traceme_sendsignal_handle7); \ + ATF_TP_ADD_TC(tp, traceme_sendsignal_handle8); \ + ATF_TP_ADD_TC(tp, traceme_sendsignal_masked1); \ + ATF_TP_ADD_TC(tp, traceme_sendsignal_masked2); \ + ATF_TP_ADD_TC(tp, traceme_sendsignal_masked3); \ + ATF_TP_ADD_TC(tp, traceme_sendsignal_masked4); \ + ATF_TP_ADD_TC(tp, traceme_sendsignal_masked5); \ + ATF_TP_ADD_TC(tp, traceme_sendsignal_masked6); \ + ATF_TP_ADD_TC(tp, traceme_sendsignal_masked7); \ + ATF_TP_ADD_TC(tp, traceme_sendsignal_masked8); \ + ATF_TP_ADD_TC(tp, traceme_sendsignal_ignored1); \ + ATF_TP_ADD_TC(tp, traceme_sendsignal_ignored2); \ + ATF_TP_ADD_TC(tp, traceme_sendsignal_ignored3); \ + ATF_TP_ADD_TC(tp, traceme_sendsignal_ignored4); \ + ATF_TP_ADD_TC(tp, traceme_sendsignal_ignored5); \ + ATF_TP_ADD_TC(tp, traceme_sendsignal_ignored6); \ + ATF_TP_ADD_TC(tp, traceme_sendsignal_ignored7); \ + ATF_TP_ADD_TC(tp, traceme_sendsignal_ignored8); \ + ATF_TP_ADD_TC(tp, traceme_sendsignal_simple1); \ + ATF_TP_ADD_TC(tp, traceme_sendsignal_simple2); \ + ATF_TP_ADD_TC(tp, traceme_sendsignal_simple3); \ + ATF_TP_ADD_TC(tp, traceme_sendsignal_simple4); \ + ATF_TP_ADD_TC(tp, traceme_sendsignal_simple5); \ + ATF_TP_ADD_TC(tp, traceme_sendsignal_simple6); \ + ATF_TP_ADD_TC(tp, traceme_sendsignal_simple7); \ + ATF_TP_ADD_TC(tp, traceme_sendsignal_simple8); \ + ATF_TP_ADD_TC(tp, traceme_sendsignal_simple9); \ + ATF_TP_ADD_TC(tp, traceme_sendsignal_simple10); \ + ATF_TP_ADD_TC(tp, traceme_vfork_raise1); \ + ATF_TP_ADD_TC(tp, traceme_vfork_raise2); \ + ATF_TP_ADD_TC(tp, traceme_vfork_raise3); \ + ATF_TP_ADD_TC(tp, traceme_vfork_raise4); \ + ATF_TP_ADD_TC(tp, traceme_vfork_raise5); \ + ATF_TP_ADD_TC(tp, traceme_vfork_raise6); \ + ATF_TP_ADD_TC(tp, traceme_vfork_raise7); \ + ATF_TP_ADD_TC(tp, traceme_vfork_raise8); \ + ATF_TP_ADD_TC(tp, traceme_vfork_raise9); \ + ATF_TP_ADD_TC(tp, traceme_vfork_raise10); \ + ATF_TP_ADD_TC(tp, traceme_vfork_raise11); \ + ATF_TP_ADD_TC(tp, traceme_vfork_raise12); \ + ATF_TP_ADD_TC(tp, traceme_vfork_raise13); \ + ATF_TP_ADD_TC(tp, traceme_vfork_crash_trap); \ + ATF_TP_ADD_TC(tp, traceme_vfork_crash_segv); \ + ATF_TP_ADD_TC(tp, traceme_vfork_crash_ill); \ + ATF_TP_ADD_TC(tp, traceme_vfork_crash_fpe); \ + ATF_TP_ADD_TC(tp, traceme_vfork_crash_bus); \ + ATF_TP_ADD_TC(tp, traceme_vfork_signalmasked_crash_trap); \ + ATF_TP_ADD_TC(tp, traceme_vfork_signalmasked_crash_segv); \ + ATF_TP_ADD_TC(tp, traceme_vfork_signalmasked_crash_ill); \ + ATF_TP_ADD_TC(tp, traceme_vfork_signalmasked_crash_fpe); \ + ATF_TP_ADD_TC(tp, traceme_vfork_signalmasked_crash_bus); \ + ATF_TP_ADD_TC(tp, traceme_vfork_signalignored_crash_trap); \ + ATF_TP_ADD_TC(tp, traceme_vfork_signalignored_crash_segv); \ + ATF_TP_ADD_TC(tp, traceme_vfork_signalignored_crash_ill); \ + ATF_TP_ADD_TC(tp, traceme_vfork_signalignored_crash_fpe); \ + ATF_TP_ADD_TC(tp, traceme_vfork_signalignored_crash_bus); \ + ATF_TP_ADD_TC_HAVE_PID(tp, unrelated_tracer_sees_crash_trap); \ + ATF_TP_ADD_TC_HAVE_PID(tp, unrelated_tracer_sees_crash_segv); \ + ATF_TP_ADD_TC_HAVE_PID(tp, unrelated_tracer_sees_crash_ill); \ + ATF_TP_ADD_TC_HAVE_PID(tp, unrelated_tracer_sees_crash_fpe); \ + ATF_TP_ADD_TC_HAVE_PID(tp, unrelated_tracer_sees_crash_bus); \ + ATF_TP_ADD_TC_HAVE_PID(tp, \ + unrelated_tracer_sees_signalmasked_crash_trap); \ + ATF_TP_ADD_TC_HAVE_PID(tp, \ + unrelated_tracer_sees_signalmasked_crash_segv); \ + ATF_TP_ADD_TC_HAVE_PID(tp, \ + unrelated_tracer_sees_signalmasked_crash_ill); \ + ATF_TP_ADD_TC_HAVE_PID(tp, \ + unrelated_tracer_sees_signalmasked_crash_fpe); \ + ATF_TP_ADD_TC_HAVE_PID(tp, \ + unrelated_tracer_sees_signalmasked_crash_bus); \ + ATF_TP_ADD_TC_HAVE_PID(tp, \ + unrelated_tracer_sees_signalignored_crash_trap); \ + ATF_TP_ADD_TC_HAVE_PID(tp, \ + unrelated_tracer_sees_signalignored_crash_segv); \ + ATF_TP_ADD_TC_HAVE_PID(tp, \ + unrelated_tracer_sees_signalignored_crash_ill); \ + ATF_TP_ADD_TC_HAVE_PID(tp, \ + unrelated_tracer_sees_signalignored_crash_fpe); \ + ATF_TP_ADD_TC_HAVE_PID(tp, \ + unrelated_tracer_sees_signalignored_crash_bus); \ + ATF_TP_ADD_TC(tp, signal_mask_unrelated); diff --git a/lib/libc/sys/t_ptrace_step_wait.h b/lib/libc/sys/t_ptrace_step_wait.h new file mode 100644 --- /dev/null +++ b/lib/libc/sys/t_ptrace_step_wait.h @@ -0,0 +1,257 @@ +/* $NetBSD: t_ptrace_step_wait.h,v 1.1 2020/05/04 21:33:20 kamil Exp $ */ + +/*- + * Copyright (c) 2016, 2017, 2018, 2019, 2020 The NetBSD Foundation, Inc. + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +#if defined(PT_STEP) +static void +ptrace_step(int N, int setstep, bool masked, bool ignored) +{ + const int exitval = 5; + const int sigval = SIGSTOP; + pid_t child, wpid; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + int happy; + struct sigaction sa; + struct ptrace_siginfo info; + sigset_t intmask; + struct kinfo_proc2 kp; + size_t len = sizeof(kp); + + int name[6]; + const size_t namelen = __arraycount(name); + ki_sigset_t kp_sigmask; + ki_sigset_t kp_sigignore; + +#if defined(__arm__) + /* PT_STEP not supported on arm 32-bit */ + atf_tc_expect_fail("PR kern/52119"); +#endif + + DPRINTF("Before forking process PID=%d\n", getpid()); + SYSCALL_REQUIRE((child = fork()) != -1); + if (child == 0) { + DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + if (masked) { + sigemptyset(&intmask); + sigaddset(&intmask, SIGTRAP); + sigprocmask(SIG_BLOCK, &intmask, NULL); + } + + if (ignored) { + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = SIG_IGN; + sigemptyset(&sa.sa_mask); + FORKEE_ASSERT(sigaction(SIGTRAP, &sa, NULL) != -1); + } + + happy = check_happy(999); + + DPRINTF("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + FORKEE_ASSERT_EQ(happy, check_happy(999)); + + DPRINTF("Before exiting of the child process\n"); + _exit(exitval); + } + DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + DPRINTF("Before calling ptrace(2) with PT_GET_SIGINFO for child\n"); + SYSCALL_REQUIRE( + ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1); + + DPRINTF("Before checking siginfo_t\n"); + ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, sigval); + ATF_REQUIRE_EQ(info.psi_siginfo.si_code, SI_LWP); + + name[0] = CTL_KERN, + name[1] = KERN_PROC2, + name[2] = KERN_PROC_PID; + name[3] = child; + name[4] = sizeof(kp); + name[5] = 1; + + FORKEE_ASSERT_EQ(sysctl(name, namelen, &kp, &len, NULL, 0), 0); + + if (masked) + kp_sigmask = kp.p_sigmask; + + if (ignored) + kp_sigignore = kp.p_sigignore; + + while (N --> 0) { + if (setstep) { + DPRINTF("Before resuming the child process where it " + "left off and without signal to be sent (use " + "PT_SETSTEP and PT_CONTINUE)\n"); + SYSCALL_REQUIRE(ptrace(PT_SETSTEP, child, 0, 0) != -1); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) + != -1); + } else { + DPRINTF("Before resuming the child process where it " + "left off and without signal to be sent (use " + "PT_STEP)\n"); + SYSCALL_REQUIRE(ptrace(PT_STEP, child, (void *)1, 0) + != -1); + } + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), + child); + + validate_status_stopped(status, SIGTRAP); + + DPRINTF("Before calling ptrace(2) with PT_GET_SIGINFO for child\n"); + SYSCALL_REQUIRE( + ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1); + + DPRINTF("Before checking siginfo_t\n"); + ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, SIGTRAP); + ATF_REQUIRE_EQ(info.psi_siginfo.si_code, TRAP_TRACE); + + if (setstep) { + SYSCALL_REQUIRE(ptrace(PT_CLEARSTEP, child, 0, 0) != -1); + } + + ATF_REQUIRE_EQ(sysctl(name, namelen, &kp, &len, NULL, 0), 0); + + if (masked) { + DPRINTF("kp_sigmask=" + "%#02" PRIx32 "%02" PRIx32 "%02" PRIx32 "%02" + PRIx32 "\n", + kp_sigmask.__bits[0], kp_sigmask.__bits[1], + kp_sigmask.__bits[2], kp_sigmask.__bits[3]); + + DPRINTF("kp.p_sigmask=" + "%#02" PRIx32 "%02" PRIx32 "%02" PRIx32 "%02" + PRIx32 "\n", + kp.p_sigmask.__bits[0], kp.p_sigmask.__bits[1], + kp.p_sigmask.__bits[2], kp.p_sigmask.__bits[3]); + + ATF_REQUIRE(!memcmp(&kp_sigmask, &kp.p_sigmask, + sizeof(kp_sigmask))); + } + + if (ignored) { + DPRINTF("kp_sigignore=" + "%#02" PRIx32 "%02" PRIx32 "%02" PRIx32 "%02" + PRIx32 "\n", + kp_sigignore.__bits[0], kp_sigignore.__bits[1], + kp_sigignore.__bits[2], kp_sigignore.__bits[3]); + + DPRINTF("kp.p_sigignore=" + "%#02" PRIx32 "%02" PRIx32 "%02" PRIx32 "%02" + PRIx32 "\n", + kp.p_sigignore.__bits[0], kp.p_sigignore.__bits[1], + kp.p_sigignore.__bits[2], kp.p_sigignore.__bits[3]); + + ATF_REQUIRE(!memcmp(&kp_sigignore, &kp.p_sigignore, + sizeof(kp_sigignore))); + } + } + + DPRINTF("Before resuming the child process where it left off and " + "without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_exited(status, exitval); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} + +#define PTRACE_STEP(test, N, setstep) \ +ATF_TC(test); \ +ATF_TC_HEAD(test, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", \ + "Verify " #N " (PT_SETSTEP set to: " #setstep ")"); \ +} \ + \ +ATF_TC_BODY(test, tc) \ +{ \ + \ + ptrace_step(N, setstep, false, false); \ +} + +PTRACE_STEP(step1, 1, 0) +PTRACE_STEP(step2, 2, 0) +PTRACE_STEP(step3, 3, 0) +PTRACE_STEP(step4, 4, 0) +PTRACE_STEP(setstep1, 1, 1) +PTRACE_STEP(setstep2, 2, 1) +PTRACE_STEP(setstep3, 3, 1) +PTRACE_STEP(setstep4, 4, 1) + +ATF_TC(step_signalmasked); +ATF_TC_HEAD(step_signalmasked, tc) +{ + atf_tc_set_md_var(tc, "descr", "Verify PT_STEP with masked SIGTRAP"); +} + +ATF_TC_BODY(step_signalmasked, tc) +{ + + ptrace_step(1, 0, true, false); +} + +ATF_TC(step_signalignored); +ATF_TC_HEAD(step_signalignored, tc) +{ + atf_tc_set_md_var(tc, "descr", "Verify PT_STEP with ignored SIGTRAP"); +} + +ATF_TC_BODY(step_signalignored, tc) +{ + + ptrace_step(1, 0, false, true); +} +#endif + +#define ATF_TP_ADD_TCS_PTRACE_WAIT_STEP() \ + ATF_TP_ADD_TC_PT_STEP(tp, step1); \ + ATF_TP_ADD_TC_PT_STEP(tp, step2); \ + ATF_TP_ADD_TC_PT_STEP(tp, step3); \ + ATF_TP_ADD_TC_PT_STEP(tp, step4); \ + ATF_TP_ADD_TC_PT_STEP(tp, setstep1); \ + ATF_TP_ADD_TC_PT_STEP(tp, setstep2); \ + ATF_TP_ADD_TC_PT_STEP(tp, setstep3); \ + ATF_TP_ADD_TC_PT_STEP(tp, setstep4); \ + ATF_TP_ADD_TC_PT_STEP(tp, step_signalmasked); \ + ATF_TP_ADD_TC_PT_STEP(tp, step_signalignored); diff --git a/lib/libc/sys/t_ptrace_syscall_wait.h b/lib/libc/sys/t_ptrace_syscall_wait.h new file mode 100644 --- /dev/null +++ b/lib/libc/sys/t_ptrace_syscall_wait.h @@ -0,0 +1,272 @@ +/* $NetBSD: t_ptrace_syscall_wait.h,v 1.1 2020/05/04 21:21:30 kamil Exp $ */ + +/*- + * Copyright (c) 2016, 2017, 2018, 2019, 2020 The NetBSD Foundation, Inc. + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + + +static int test_syscall_caught; + +static void +syscall_sighand(int arg) +{ + + DPRINTF("Caught a signal %d in process %d\n", arg, getpid()); + + FORKEE_ASSERT_EQ(arg, SIGINFO); + + ++test_syscall_caught; + + FORKEE_ASSERT_EQ(test_syscall_caught, 1); +} + +static void +syscall_body(const char *op) +{ + const int exitval = 5; + const int sigval = SIGSTOP; + pid_t child, wpid; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + struct ptrace_siginfo info; + + memset(&info, 0, sizeof(info)); + +#if defined(TWAIT_HAVE_STATUS) + if (strstr(op, "signal") != NULL) { + atf_tc_expect_fail("XXX: behavior under investigation"); + } +#endif + + DPRINTF("Before forking process PID=%d\n", getpid()); + SYSCALL_REQUIRE((child = fork()) != -1); + if (child == 0) { + DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + signal(SIGINFO, syscall_sighand); + + DPRINTF("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + syscall(SYS_getpid); + + if (strstr(op, "signal") != NULL) { + FORKEE_ASSERT_EQ(test_syscall_caught, 1); + } + + DPRINTF("Before exiting of the child process\n"); + _exit(exitval); + } + DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + DPRINTF("Before resuming the child process where it left off and " + "without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_SYSCALL, child, (void *)1, 0) != -1); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, SIGTRAP); + + DPRINTF("Before calling ptrace(2) with PT_GET_SIGINFO for child\n"); + SYSCALL_REQUIRE(ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1); + + DPRINTF("Before checking siginfo_t and lwpid\n"); + ATF_REQUIRE(info.psi_lwpid > 0); + ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, SIGTRAP); + ATF_REQUIRE_EQ(info.psi_siginfo.si_code, TRAP_SCE); + + if (strstr(op, "killed") != NULL) { + SYSCALL_REQUIRE(ptrace(PT_KILL, child, NULL, 0) != -1); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS( + wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_signaled(status, SIGKILL, 0); + } else { + if (strstr(op, "signal") != NULL) { + DPRINTF("Before resuming the child %d and sending a " + "signal SIGINFO\n", child); + SYSCALL_REQUIRE( + ptrace(PT_CONTINUE, child, (void *)1, SIGINFO) + != -1); + } else if (strstr(op, "detach") != NULL) { + DPRINTF("Before detaching the child %d\n", child); + SYSCALL_REQUIRE( + ptrace(PT_DETACH, child, (void *)1, 0) != -1); + } else { + DPRINTF("Before resuming the child process where it " + "left off and without signal to be sent\n"); + SYSCALL_REQUIRE( + ptrace(PT_SYSCALL, child, (void *)1, 0) != -1); + + DPRINTF("Before calling %s() for the child\n", + TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS( + wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, SIGTRAP); + + DPRINTF("Before calling ptrace(2) with PT_GET_SIGINFO " + "for child\n"); + SYSCALL_REQUIRE( + ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) + != -1); + + DPRINTF("Before checking siginfo_t and lwpid\n"); + ATF_REQUIRE(info.psi_lwpid > 0); + ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, SIGTRAP); + ATF_REQUIRE_EQ(info.psi_siginfo.si_code, TRAP_SCX); + + DPRINTF("Before resuming the child process where it " + "left off and without signal to be sent\n"); + SYSCALL_REQUIRE( + ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + } + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS( + wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_exited(status, exitval); + } + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} + +#define SYSCALL_TEST(name,op) \ +ATF_TC(name); \ +ATF_TC_HEAD(name, tc) \ +{ \ + atf_tc_set_md_var(tc, "timeout", "15"); \ + atf_tc_set_md_var(tc, "descr", \ + "Verify that getpid(2) can be traced with PT_SYSCALL %s", \ + #op ); \ +} \ + \ +ATF_TC_BODY(name, tc) \ +{ \ + \ + syscall_body(op); \ +} + +SYSCALL_TEST(syscall, "") +SYSCALL_TEST(syscall_killed_on_sce, "and killed") +SYSCALL_TEST(syscall_signal_on_sce, "and signaled") +SYSCALL_TEST(syscall_detach_on_sce, "and detached") + +/// ---------------------------------------------------------------------------- + +ATF_TC(syscallemu1); +ATF_TC_HEAD(syscallemu1, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that exit(2) can be intercepted with PT_SYSCALLEMU"); +} + +ATF_TC_BODY(syscallemu1, tc) +{ + const int exitval = 5; + const int sigval = SIGSTOP; + pid_t child, wpid; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + +#if defined(__sparc__) && !defined(__sparc64__) + /* syscallemu does not work on sparc (32-bit) */ + atf_tc_expect_fail("PR kern/52166"); +#endif + + DPRINTF("Before forking process PID=%d\n", getpid()); + SYSCALL_REQUIRE((child = fork()) != -1); + if (child == 0) { + DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + DPRINTF("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + syscall(SYS_exit, 100); + + DPRINTF("Before exiting of the child process\n"); + _exit(exitval); + } + DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + DPRINTF("Before resuming the child process where it left off and " + "without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_SYSCALL, child, (void *)1, 0) != -1); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, SIGTRAP); + + DPRINTF("Set SYSCALLEMU for intercepted syscall\n"); + SYSCALL_REQUIRE(ptrace(PT_SYSCALLEMU, child, (void *)1, 0) != -1); + + DPRINTF("Before resuming the child process where it left off and " + "without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_SYSCALL, child, (void *)1, 0) != -1); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, SIGTRAP); + + DPRINTF("Before resuming the child process where it left off and " + "without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_exited(status, exitval); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} + +#define ATF_TP_ADD_TCS_PTRACE_WAIT_SYSCALL() \ + ATF_TP_ADD_TC(tp, syscall); \ + ATF_TP_ADD_TC(tp, syscall_killed_on_sce); \ + ATF_TP_ADD_TC(tp, syscall_signal_on_sce); \ + ATF_TP_ADD_TC(tp, syscall_detach_on_sce); \ + ATF_TP_ADD_TC(tp, syscallemu1); diff --git a/lib/libc/sys/t_ptrace_threads_wait.h b/lib/libc/sys/t_ptrace_threads_wait.h new file mode 100644 --- /dev/null +++ b/lib/libc/sys/t_ptrace_threads_wait.h @@ -0,0 +1,1069 @@ +/* $NetBSD: t_ptrace_threads_wait.h,v 1.1 2020/05/05 00:50:39 kamil Exp $ */ + +/*- + * Copyright (c) 2016, 2017, 2018, 2019, 2020 The NetBSD Foundation, Inc. + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + + +#define TRACE_THREADS_NUM 100 + +static volatile int done; +pthread_mutex_t trace_threads_mtx = PTHREAD_MUTEX_INITIALIZER; + +static void * +trace_threads_cb(void *arg __unused) +{ + + pthread_mutex_lock(&trace_threads_mtx); + done++; + pthread_mutex_unlock(&trace_threads_mtx); + + while (done < TRACE_THREADS_NUM) + sched_yield(); + + return NULL; +} + +static void +trace_threads(bool trace_create, bool trace_exit, bool masked) +{ + const int sigval = SIGSTOP; + pid_t child, wpid; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + ptrace_state_t state; + const int slen = sizeof(state); + ptrace_event_t event; + const int elen = sizeof(event); + struct ptrace_siginfo info; + + sigset_t intmask; + + pthread_t t[TRACE_THREADS_NUM]; + int rv; + size_t n; + lwpid_t lid; + + /* Track created and exited threads */ + struct lwp_event_count traced_lwps[__arraycount(t)] = {{0, 0}}; + + DPRINTF("Before forking process PID=%d\n", getpid()); + SYSCALL_REQUIRE((child = fork()) != -1); + if (child == 0) { + DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + if (masked) { + sigemptyset(&intmask); + sigaddset(&intmask, SIGTRAP); + sigprocmask(SIG_BLOCK, &intmask, NULL); + } + + DPRINTF("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + for (n = 0; n < __arraycount(t); n++) { + rv = pthread_create(&t[n], NULL, trace_threads_cb, + NULL); + FORKEE_ASSERT(rv == 0); + } + + for (n = 0; n < __arraycount(t); n++) { + rv = pthread_join(t[n], NULL); + FORKEE_ASSERT(rv == 0); + } + + /* + * There is race between _exit() and pthread_join() detaching + * a thread. For simplicity kill the process after detecting + * LWP events. + */ + while (true) + continue; + + FORKEE_ASSERT(0 && "Not reached"); + } + DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + DPRINTF("Before calling ptrace(2) with PT_GET_SIGINFO for child\n"); + SYSCALL_REQUIRE( + ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1); + + DPRINTF("Signal traced to lwpid=%d\n", info.psi_lwpid); + DPRINTF("Signal properties: si_signo=%#x si_code=%#x si_errno=%#x\n", + info.psi_siginfo.si_signo, info.psi_siginfo.si_code, + info.psi_siginfo.si_errno); + + ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, sigval); + ATF_REQUIRE_EQ(info.psi_siginfo.si_code, SI_LWP); + + DPRINTF("Set LWP event mask for the child %d\n", child); + memset(&event, 0, sizeof(event)); + if (trace_create) + event.pe_set_event |= PTRACE_LWP_CREATE; + if (trace_exit) + event.pe_set_event |= PTRACE_LWP_EXIT; + SYSCALL_REQUIRE(ptrace(PT_SET_EVENT_MASK, child, &event, elen) != -1); + + DPRINTF("Before resuming the child process where it left off and " + "without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + for (n = 0; n < (trace_create ? __arraycount(t) : 0); n++) { + DPRINTF("Before calling %s() for the child - expected stopped " + "SIGTRAP\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), + child); + + validate_status_stopped(status, SIGTRAP); + + DPRINTF("Before calling ptrace(2) with PT_GET_SIGINFO for " + "child\n"); + SYSCALL_REQUIRE( + ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1); + + DPRINTF("Signal traced to lwpid=%d\n", info.psi_lwpid); + DPRINTF("Signal properties: si_signo=%#x si_code=%#x " + "si_errno=%#x\n", + info.psi_siginfo.si_signo, info.psi_siginfo.si_code, + info.psi_siginfo.si_errno); + + ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, SIGTRAP); + ATF_REQUIRE_EQ(info.psi_siginfo.si_code, TRAP_LWP); + + SYSCALL_REQUIRE( + ptrace(PT_GET_PROCESS_STATE, child, &state, slen) != -1); + + ATF_REQUIRE_EQ_MSG(state.pe_report_event, PTRACE_LWP_CREATE, + "%d != %d", state.pe_report_event, PTRACE_LWP_CREATE); + + lid = state.pe_lwp; + DPRINTF("Reported PTRACE_LWP_CREATE event with lid %d\n", lid); + + *FIND_EVENT_COUNT(traced_lwps, lid) += 1; + + DPRINTF("Before resuming the child process where it left off " + "and without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + } + + for (n = 0; n < (trace_exit ? __arraycount(t) : 0); n++) { + DPRINTF("Before calling %s() for the child - expected stopped " + "SIGTRAP\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), + child); + + validate_status_stopped(status, SIGTRAP); + + DPRINTF("Before calling ptrace(2) with PT_GET_SIGINFO for " + "child\n"); + SYSCALL_REQUIRE( + ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1); + + DPRINTF("Signal traced to lwpid=%d\n", info.psi_lwpid); + DPRINTF("Signal properties: si_signo=%#x si_code=%#x " + "si_errno=%#x\n", + info.psi_siginfo.si_signo, info.psi_siginfo.si_code, + info.psi_siginfo.si_errno); + + ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, SIGTRAP); + ATF_REQUIRE_EQ(info.psi_siginfo.si_code, TRAP_LWP); + + SYSCALL_REQUIRE( + ptrace(PT_GET_PROCESS_STATE, child, &state, slen) != -1); + + ATF_REQUIRE_EQ_MSG(state.pe_report_event, PTRACE_LWP_EXIT, + "%d != %d", state.pe_report_event, PTRACE_LWP_EXIT); + + lid = state.pe_lwp; + DPRINTF("Reported PTRACE_LWP_EXIT event with lid %d\n", lid); + + if (trace_create) { + int *count = FIND_EVENT_COUNT(traced_lwps, lid); + ATF_REQUIRE_EQ(*count, 1); + *count = 0; + } + + DPRINTF("Before resuming the child process where it left off " + "and without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + } + + kill(child, SIGKILL); + + DPRINTF("Before calling %s() for the child - expected exited\n", + TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_signaled(status, SIGKILL, 0); + + DPRINTF("Before calling %s() for the child - expected no process\n", + TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} + +#define TRACE_THREADS(test, trace_create, trace_exit, mask) \ +ATF_TC(test); \ +ATF_TC_HEAD(test, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", \ + "Verify spawning threads with%s tracing LWP create and" \ + "with%s tracing LWP exit", trace_create ? "" : "out", \ + trace_exit ? "" : "out"); \ +} \ + \ +ATF_TC_BODY(test, tc) \ +{ \ + \ + trace_threads(trace_create, trace_exit, mask); \ +} + +TRACE_THREADS(trace_thread_nolwpevents, false, false, false) +TRACE_THREADS(trace_thread_lwpexit, false, true, false) +TRACE_THREADS(trace_thread_lwpcreate, true, false, false) +TRACE_THREADS(trace_thread_lwpcreate_and_exit, true, true, false) + +TRACE_THREADS(trace_thread_lwpexit_masked_sigtrap, false, true, true) +TRACE_THREADS(trace_thread_lwpcreate_masked_sigtrap, true, false, true) +TRACE_THREADS(trace_thread_lwpcreate_and_exit_masked_sigtrap, true, true, true) + +/// ---------------------------------------------------------------------------- + +static void * +thread_and_exec_thread_cb(void *arg __unused) +{ + + execlp("/bin/echo", "/bin/echo", NULL); + + abort(); +} + +static void +threads_and_exec(void) +{ + const int sigval = SIGSTOP; + pid_t child, wpid; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + ptrace_state_t state; + const int slen = sizeof(state); + ptrace_event_t event; + const int elen = sizeof(event); + struct ptrace_siginfo info; + + pthread_t t; + lwpid_t lid; + + DPRINTF("Before forking process PID=%d\n", getpid()); + SYSCALL_REQUIRE((child = fork()) != -1); + if (child == 0) { + DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + DPRINTF("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + FORKEE_ASSERT(pthread_create(&t, NULL, + thread_and_exec_thread_cb, NULL) == 0); + + for (;;) + continue; + + FORKEE_ASSERT(0 && "Not reached"); + } + DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + DPRINTF("Before calling ptrace(2) with PT_GET_SIGINFO for child\n"); + SYSCALL_REQUIRE( + ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1); + + DPRINTF("Signal traced to lwpid=%d\n", info.psi_lwpid); + DPRINTF("Signal properties: si_signo=%#x si_code=%#x si_errno=%#x\n", + info.psi_siginfo.si_signo, info.psi_siginfo.si_code, + info.psi_siginfo.si_errno); + + ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, sigval); + ATF_REQUIRE_EQ(info.psi_siginfo.si_code, SI_LWP); + + DPRINTF("Set LWP event mask for the child %d\n", child); + memset(&event, 0, sizeof(event)); + event.pe_set_event |= PTRACE_LWP_CREATE | PTRACE_LWP_EXIT; + SYSCALL_REQUIRE(ptrace(PT_SET_EVENT_MASK, child, &event, elen) != -1); + + DPRINTF("Before resuming the child process where it left off and " + "without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + DPRINTF("Before calling %s() for the child - expected stopped " + "SIGTRAP\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), + child); + + validate_status_stopped(status, SIGTRAP); + + DPRINTF("Before calling ptrace(2) with PT_GET_SIGINFO for " + "child\n"); + SYSCALL_REQUIRE( + ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1); + + DPRINTF("Signal traced to lwpid=%d\n", info.psi_lwpid); + DPRINTF("Signal properties: si_signo=%#x si_code=%#x " + "si_errno=%#x\n", + info.psi_siginfo.si_signo, info.psi_siginfo.si_code, + info.psi_siginfo.si_errno); + + ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, SIGTRAP); + ATF_REQUIRE_EQ(info.psi_siginfo.si_code, TRAP_LWP); + + SYSCALL_REQUIRE( + ptrace(PT_GET_PROCESS_STATE, child, &state, slen) != -1); + + ATF_REQUIRE_EQ_MSG(state.pe_report_event, PTRACE_LWP_CREATE, + "%d != %d", state.pe_report_event, PTRACE_LWP_CREATE); + + lid = state.pe_lwp; + DPRINTF("Reported PTRACE_LWP_CREATE event with lid %d\n", lid); + + DPRINTF("Before resuming the child process where it left off " + "and without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + DPRINTF("Before calling %s() for the child - expected stopped " + "SIGTRAP\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), + child); + + validate_status_stopped(status, SIGTRAP); + + DPRINTF("Before calling ptrace(2) with PT_GET_SIGINFO for " + "child\n"); + SYSCALL_REQUIRE( + ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1); + + DPRINTF("Signal traced to lwpid=%d\n", info.psi_lwpid); + DPRINTF("Signal properties: si_signo=%#x si_code=%#x " + "si_errno=%#x\n", + info.psi_siginfo.si_signo, info.psi_siginfo.si_code, + info.psi_siginfo.si_errno); + + ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, SIGTRAP); + ATF_REQUIRE_EQ(info.psi_siginfo.si_code, TRAP_LWP); + + SYSCALL_REQUIRE( + ptrace(PT_GET_PROCESS_STATE, child, &state, slen) != -1); + + ATF_REQUIRE_EQ_MSG(state.pe_report_event, PTRACE_LWP_EXIT, + "%d != %d", state.pe_report_event, PTRACE_LWP_EXIT); + + lid = state.pe_lwp; + DPRINTF("Reported PTRACE_LWP_EXIT event with lid %d\n", lid); + + DPRINTF("Before resuming the child process where it left off " + "and without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + DPRINTF("Before calling %s() for the child - expected stopped " + "SIGTRAP\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), + child); + + validate_status_stopped(status, SIGTRAP); + + DPRINTF("Before calling ptrace(2) with PT_GET_SIGINFO for " + "child\n"); + SYSCALL_REQUIRE( + ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1); + + DPRINTF("Signal traced to lwpid=%d\n", info.psi_lwpid); + DPRINTF("Signal properties: si_signo=%#x si_code=%#x " + "si_errno=%#x\n", + info.psi_siginfo.si_signo, info.psi_siginfo.si_code, + info.psi_siginfo.si_errno); + + ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, SIGTRAP); + ATF_REQUIRE_EQ(info.psi_siginfo.si_code, TRAP_EXEC); + + SYSCALL_REQUIRE(ptrace(PT_KILL, child, NULL, 0) != -1); + + DPRINTF("Before calling %s() for the child - expected exited\n", + TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_signaled(status, SIGKILL, 0); + + DPRINTF("Before calling %s() for the child - expected no process\n", + TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} + +ATF_TC(threads_and_exec); +ATF_TC_HEAD(threads_and_exec, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that multithreaded application on exec() will report " + "LWP_EXIT events"); +} + +ATF_TC_BODY(threads_and_exec, tc) +{ + + threads_and_exec(); +} + +/// ---------------------------------------------------------------------------- + +ATF_TC(suspend_no_deadlock); +ATF_TC_HEAD(suspend_no_deadlock, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that the while the only thread within a process is " + "suspended, the whole process cannot be unstopped"); +} + +ATF_TC_BODY(suspend_no_deadlock, tc) +{ + const int exitval = 5; + const int sigval = SIGSTOP; + pid_t child, wpid; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + struct ptrace_siginfo psi; + + DPRINTF("Before forking process PID=%d\n", getpid()); + SYSCALL_REQUIRE((child = fork()) != -1); + if (child == 0) { + DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + DPRINTF("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + DPRINTF("Before exiting of the child process\n"); + _exit(exitval); + } + DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + DPRINTF("Before reading siginfo and lwpid_t\n"); + SYSCALL_REQUIRE(ptrace(PT_GET_SIGINFO, child, &psi, sizeof(psi)) != -1); + + DPRINTF("Before suspending LWP %d\n", psi.psi_lwpid); + SYSCALL_REQUIRE(ptrace(PT_SUSPEND, child, NULL, psi.psi_lwpid) != -1); + + DPRINTF("Before resuming the child process where it left off and " + "without signal to be sent\n"); + ATF_REQUIRE_ERRNO(EDEADLK, + ptrace(PT_CONTINUE, child, (void *)1, 0) == -1); + + DPRINTF("Before resuming LWP %d\n", psi.psi_lwpid); + SYSCALL_REQUIRE(ptrace(PT_RESUME, child, NULL, psi.psi_lwpid) != -1); + + DPRINTF("Before resuming the child process where it left off and " + "without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + DPRINTF("Before calling %s() for the child - expected exited\n", + TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_exited(status, exitval); + + DPRINTF("Before calling %s() for the child - expected no process\n", + TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} + +/// ---------------------------------------------------------------------------- + +static pthread_barrier_t barrier1_resume; +static pthread_barrier_t barrier2_resume; + +static void * +resume_thread(void *arg) +{ + + raise(SIGUSR1); + + pthread_barrier_wait(&barrier1_resume); + + /* Debugger will suspend the process here */ + + pthread_barrier_wait(&barrier2_resume); + + raise(SIGUSR2); + + return infinite_thread(arg); +} + +ATF_TC(resume); +ATF_TC_HEAD(resume, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that a thread can be suspended by a debugger and later " + "resumed by the debugger"); +} + +ATF_TC_BODY(resume, tc) +{ + const int sigval = SIGSTOP; + pid_t child, wpid; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + lwpid_t lid; + struct ptrace_siginfo psi; + pthread_t t; + + DPRINTF("Before forking process PID=%d\n", getpid()); + SYSCALL_REQUIRE((child = fork()) != -1); + if (child == 0) { + DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + pthread_barrier_init(&barrier1_resume, NULL, 2); + pthread_barrier_init(&barrier2_resume, NULL, 2); + + DPRINTF("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + DPRINTF("Before creating new thread in child\n"); + FORKEE_ASSERT(pthread_create(&t, NULL, resume_thread, NULL) == 0); + + pthread_barrier_wait(&barrier1_resume); + + pthread_barrier_wait(&barrier2_resume); + + infinite_thread(NULL); + } + DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + DPRINTF("Before resuming the child process where it left off and " + "without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + DPRINTF("Before calling %s() for the child - expected stopped " + "SIGUSR1\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, SIGUSR1); + + DPRINTF("Before reading siginfo and lwpid_t\n"); + SYSCALL_REQUIRE(ptrace(PT_GET_SIGINFO, child, &psi, sizeof(psi)) != -1); + + DPRINTF("Before suspending LWP %d\n", psi.psi_lwpid); + SYSCALL_REQUIRE(ptrace(PT_SUSPEND, child, NULL, psi.psi_lwpid) != -1); + + lid = psi.psi_lwpid; + + DPRINTF("Before resuming the child process where it left off and " + "without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + DPRINTF("Before suspending the parent for 1 second, we expect no signals\n"); + SYSCALL_REQUIRE(sleep(1) == 0); + +#if defined(TWAIT_HAVE_OPTIONS) + DPRINTF("Before calling %s() for the child - expected no status\n", + TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, WNOHANG), 0); +#endif + + DPRINTF("Before resuming the child process where it left off and " + "without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_STOP, child, NULL, 0) != -1); + + DPRINTF("Before calling %s() for the child - expected stopped " + "SIGSTOP\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, SIGSTOP); + + DPRINTF("Before resuming LWP %d\n", lid); + SYSCALL_REQUIRE(ptrace(PT_RESUME, child, NULL, lid) != -1); + + DPRINTF("Before resuming the child process where it left off and " + "without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + DPRINTF("Before calling %s() for the child - expected stopped " + "SIGUSR2\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, SIGUSR2); + + DPRINTF("Before resuming the child process where it left off and " + "without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_KILL, child, (void *)1, 0) != -1); + + DPRINTF("Before calling %s() for the child - expected exited\n", + TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_signaled(status, SIGKILL, 0); + + DPRINTF("Before calling %s() for the child - expected no process\n", + TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} + +/// ---------------------------------------------------------------------------- + +#if defined(TWAIT_HAVE_STATUS) + +#define THREAD_CONCURRENT_BREAKPOINT_NUM 50 +#define THREAD_CONCURRENT_SIGNALS_NUM 50 +#define THREAD_CONCURRENT_WATCHPOINT_NUM 50 + +/* List of signals to use for the test */ +const int thread_concurrent_signals_list[] = { + SIGIO, + SIGXCPU, + SIGXFSZ, + SIGVTALRM, + SIGPROF, + SIGWINCH, + SIGINFO, + SIGUSR1, + SIGUSR2 +}; + +enum thread_concurrent_signal_handling { + /* the signal is discarded by debugger */ + TCSH_DISCARD, + /* the handler is set to SIG_IGN */ + TCSH_SIG_IGN, + /* an actual handler is used */ + TCSH_HANDLER +}; + +static pthread_barrier_t thread_concurrent_barrier; +static pthread_key_t thread_concurrent_key; +static uint32_t thread_concurrent_watchpoint_var = 0; + +static void * +thread_concurrent_breakpoint_thread(void *arg) +{ + static volatile int watchme = 1; + pthread_barrier_wait(&thread_concurrent_barrier); + DPRINTF("Before entering breakpoint func from LWP %d\n", _lwp_self()); + check_happy(watchme); + return NULL; +} + +static void +thread_concurrent_sig_handler(int sig) +{ + void *tls_val = pthread_getspecific(thread_concurrent_key); + DPRINTF("Before increment, LWP %d tls_val=%p\n", _lwp_self(), tls_val); + FORKEE_ASSERT(pthread_setspecific(thread_concurrent_key, + (void*)((uintptr_t)tls_val + 1)) == 0); +} + +static void * +thread_concurrent_signals_thread(void *arg) +{ + int sigval = thread_concurrent_signals_list[ + _lwp_self() % __arraycount(thread_concurrent_signals_list)]; + enum thread_concurrent_signal_handling *signal_handle = arg; + void *tls_val; + + pthread_barrier_wait(&thread_concurrent_barrier); + DPRINTF("Before raising %s from LWP %d\n", strsignal(sigval), + _lwp_self()); + pthread_kill(pthread_self(), sigval); + if (*signal_handle == TCSH_HANDLER) { + tls_val = pthread_getspecific(thread_concurrent_key); + DPRINTF("After raising, LWP %d tls_val=%p\n", _lwp_self(), tls_val); + FORKEE_ASSERT(tls_val == (void*)1); + } + return NULL; +} + +static void * +thread_concurrent_watchpoint_thread(void *arg) +{ + pthread_barrier_wait(&thread_concurrent_barrier); + DPRINTF("Before modifying var from LWP %d\n", _lwp_self()); + thread_concurrent_watchpoint_var = 1; + return NULL; +} + +#if defined(__i386__) || defined(__x86_64__) +enum thread_concurrent_sigtrap_event { + TCSE_UNKNOWN, + TCSE_BREAKPOINT, + TCSE_WATCHPOINT +}; + +static void +thread_concurrent_lwp_setup(pid_t child, lwpid_t lwpid); +static enum thread_concurrent_sigtrap_event +thread_concurrent_handle_sigtrap(pid_t child, ptrace_siginfo_t *info); +#endif + +static void +thread_concurrent_test(enum thread_concurrent_signal_handling signal_handle, + int breakpoint_threads, int signal_threads, int watchpoint_threads) +{ + const int exitval = 5; + const int sigval = SIGSTOP; + pid_t child, wpid; + int status; + struct lwp_event_count signal_counts[THREAD_CONCURRENT_SIGNALS_NUM] + = {{0, 0}}; + struct lwp_event_count bp_counts[THREAD_CONCURRENT_BREAKPOINT_NUM] + = {{0, 0}}; + struct lwp_event_count wp_counts[THREAD_CONCURRENT_BREAKPOINT_NUM] + = {{0, 0}}; + ptrace_event_t event; + int i; + +#if defined(HAVE_DBREGS) + if (!can_we_set_dbregs()) { + atf_tc_skip("Either run this test as root or set sysctl(3) " + "security.models.extensions.user_set_dbregs to 1"); + } +#endif + + atf_tc_skip("PR kern/54960"); + + /* Protect against out-of-bounds array access. */ + ATF_REQUIRE(breakpoint_threads <= THREAD_CONCURRENT_BREAKPOINT_NUM); + ATF_REQUIRE(signal_threads <= THREAD_CONCURRENT_SIGNALS_NUM); + ATF_REQUIRE(watchpoint_threads <= THREAD_CONCURRENT_WATCHPOINT_NUM); + + DPRINTF("Before forking process PID=%d\n", getpid()); + SYSCALL_REQUIRE((child = fork()) != -1); + if (child == 0) { + pthread_t bp_threads[THREAD_CONCURRENT_BREAKPOINT_NUM]; + pthread_t sig_threads[THREAD_CONCURRENT_SIGNALS_NUM]; + pthread_t wp_threads[THREAD_CONCURRENT_WATCHPOINT_NUM]; + + DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + DPRINTF("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + if (signal_handle != TCSH_DISCARD) { + struct sigaction sa; + unsigned int j; + + memset(&sa, 0, sizeof(sa)); + if (signal_handle == TCSH_SIG_IGN) + sa.sa_handler = SIG_IGN; + else + sa.sa_handler = thread_concurrent_sig_handler; + sigemptyset(&sa.sa_mask); + + for (j = 0; + j < __arraycount(thread_concurrent_signals_list); + j++) + FORKEE_ASSERT(sigaction( + thread_concurrent_signals_list[j], &sa, NULL) + != -1); + } + + DPRINTF("Before starting threads from the child\n"); + FORKEE_ASSERT(pthread_barrier_init( + &thread_concurrent_barrier, NULL, + breakpoint_threads + signal_threads + watchpoint_threads) + == 0); + FORKEE_ASSERT(pthread_key_create(&thread_concurrent_key, NULL) + == 0); + + for (i = 0; i < signal_threads; i++) { + FORKEE_ASSERT(pthread_create(&sig_threads[i], NULL, + thread_concurrent_signals_thread, + &signal_handle) == 0); + } + for (i = 0; i < breakpoint_threads; i++) { + FORKEE_ASSERT(pthread_create(&bp_threads[i], NULL, + thread_concurrent_breakpoint_thread, NULL) == 0); + } + for (i = 0; i < watchpoint_threads; i++) { + FORKEE_ASSERT(pthread_create(&wp_threads[i], NULL, + thread_concurrent_watchpoint_thread, NULL) == 0); + } + + DPRINTF("Before joining threads from the child\n"); + for (i = 0; i < watchpoint_threads; i++) { + FORKEE_ASSERT(pthread_join(wp_threads[i], NULL) == 0); + } + for (i = 0; i < breakpoint_threads; i++) { + FORKEE_ASSERT(pthread_join(bp_threads[i], NULL) == 0); + } + for (i = 0; i < signal_threads; i++) { + FORKEE_ASSERT(pthread_join(sig_threads[i], NULL) == 0); + } + + FORKEE_ASSERT(pthread_key_delete(thread_concurrent_key) == 0); + FORKEE_ASSERT(pthread_barrier_destroy( + &thread_concurrent_barrier) == 0); + + DPRINTF("Before exiting of the child process\n"); + _exit(exitval); + } + DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + DPRINTF("Set LWP event mask for the child process\n"); + memset(&event, 0, sizeof(event)); + event.pe_set_event |= PTRACE_LWP_CREATE; + SYSCALL_REQUIRE(ptrace(PT_SET_EVENT_MASK, child, &event, sizeof(event)) + != -1); + + DPRINTF("Before resuming the child process where it left off\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + DPRINTF("Before entering signal collection loop\n"); + while (1) { + ptrace_siginfo_t info; + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), + child); + if (WIFEXITED(status)) + break; + /* Note: we use validate_status_stopped() to get nice error + * message. Signal is irrelevant since it won't be reached. + */ + else if (!WIFSTOPPED(status)) + validate_status_stopped(status, 0); + + DPRINTF("Before calling PT_GET_SIGINFO\n"); + SYSCALL_REQUIRE(ptrace(PT_GET_SIGINFO, child, &info, + sizeof(info)) != -1); + + DPRINTF("Received signal %d from LWP %d (wait: %d)\n", + info.psi_siginfo.si_signo, info.psi_lwpid, + WSTOPSIG(status)); + + ATF_CHECK_EQ_MSG(info.psi_siginfo.si_signo, WSTOPSIG(status), + "lwp=%d, WSTOPSIG=%d, psi_siginfo=%d", info.psi_lwpid, + WSTOPSIG(status), info.psi_siginfo.si_signo); + + if (WSTOPSIG(status) != SIGTRAP) { + int expected_sig = + thread_concurrent_signals_list[info.psi_lwpid % + __arraycount(thread_concurrent_signals_list)]; + ATF_CHECK_EQ_MSG(WSTOPSIG(status), expected_sig, + "lwp=%d, expected %d, got %d", info.psi_lwpid, + expected_sig, WSTOPSIG(status)); + + *FIND_EVENT_COUNT(signal_counts, info.psi_lwpid) += 1; + } else if (info.psi_siginfo.si_code == TRAP_LWP) { +#if defined(__i386__) || defined(__x86_64__) + thread_concurrent_lwp_setup(child, info.psi_lwpid); +#endif + } else { +#if defined(__i386__) || defined(__x86_64__) + switch (thread_concurrent_handle_sigtrap(child, &info)) { + case TCSE_UNKNOWN: + /* already reported inside the function */ + break; + case TCSE_BREAKPOINT: + *FIND_EVENT_COUNT(bp_counts, + info.psi_lwpid) += 1; + break; + case TCSE_WATCHPOINT: + *FIND_EVENT_COUNT(wp_counts, + info.psi_lwpid) += 1; + break; + } +#else + ATF_CHECK_MSG(0, "Unexpected SIGTRAP, si_code=%d\n", + info.psi_siginfo.si_code); +#endif + } + + DPRINTF("Before resuming the child process\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, + signal_handle != TCSH_DISCARD && WSTOPSIG(status) != SIGTRAP + ? WSTOPSIG(status) : 0) != -1); + } + + for (i = 0; i < signal_threads; i++) + ATF_CHECK_EQ_MSG(signal_counts[i].lec_count, 1, + "signal_counts[%d].lec_count=%d; lec_lwp=%d", + i, signal_counts[i].lec_count, signal_counts[i].lec_lwp); + for (i = signal_threads; i < THREAD_CONCURRENT_SIGNALS_NUM; i++) + ATF_CHECK_EQ_MSG(signal_counts[i].lec_count, 0, + "extraneous signal_counts[%d].lec_count=%d; lec_lwp=%d", + i, signal_counts[i].lec_count, signal_counts[i].lec_lwp); + + for (i = 0; i < breakpoint_threads; i++) + ATF_CHECK_EQ_MSG(bp_counts[i].lec_count, 1, + "bp_counts[%d].lec_count=%d; lec_lwp=%d", + i, bp_counts[i].lec_count, bp_counts[i].lec_lwp); + for (i = breakpoint_threads; i < THREAD_CONCURRENT_BREAKPOINT_NUM; i++) + ATF_CHECK_EQ_MSG(bp_counts[i].lec_count, 0, + "extraneous bp_counts[%d].lec_count=%d; lec_lwp=%d", + i, bp_counts[i].lec_count, bp_counts[i].lec_lwp); + + for (i = 0; i < watchpoint_threads; i++) + ATF_CHECK_EQ_MSG(wp_counts[i].lec_count, 1, + "wp_counts[%d].lec_count=%d; lec_lwp=%d", + i, wp_counts[i].lec_count, wp_counts[i].lec_lwp); + for (i = watchpoint_threads; i < THREAD_CONCURRENT_WATCHPOINT_NUM; i++) + ATF_CHECK_EQ_MSG(wp_counts[i].lec_count, 0, + "extraneous wp_counts[%d].lec_count=%d; lec_lwp=%d", + i, wp_counts[i].lec_count, wp_counts[i].lec_lwp); + + validate_status_exited(status, exitval); +} + +#define THREAD_CONCURRENT_TEST(test, sig_hdl, bps, sigs, wps, descr) \ +ATF_TC(test); \ +ATF_TC_HEAD(test, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", descr); \ +} \ + \ +ATF_TC_BODY(test, tc) \ +{ \ + thread_concurrent_test(sig_hdl, bps, sigs, wps); \ +} + +THREAD_CONCURRENT_TEST(thread_concurrent_signals, TCSH_DISCARD, + 0, THREAD_CONCURRENT_SIGNALS_NUM, 0, + "Verify that concurrent signals issued to a single thread are reported " + "correctly"); +THREAD_CONCURRENT_TEST(thread_concurrent_signals_sig_ign, TCSH_SIG_IGN, + 0, THREAD_CONCURRENT_SIGNALS_NUM, 0, + "Verify that concurrent signals issued to a single thread are reported " + "correctly and passed back to SIG_IGN handler"); +THREAD_CONCURRENT_TEST(thread_concurrent_signals_handler, TCSH_HANDLER, + 0, THREAD_CONCURRENT_SIGNALS_NUM, 0, + "Verify that concurrent signals issued to a single thread are reported " + "correctly and passed back to a handler function"); + +#if defined(__i386__) || defined(__x86_64__) +THREAD_CONCURRENT_TEST(thread_concurrent_breakpoints, TCSH_DISCARD, + THREAD_CONCURRENT_BREAKPOINT_NUM, 0, 0, + "Verify that concurrent breakpoints are reported correctly"); +THREAD_CONCURRENT_TEST(thread_concurrent_watchpoints, TCSH_DISCARD, + 0, 0, THREAD_CONCURRENT_WATCHPOINT_NUM, + "Verify that concurrent breakpoints are reported correctly"); +THREAD_CONCURRENT_TEST(thread_concurrent_bp_wp, TCSH_DISCARD, + THREAD_CONCURRENT_BREAKPOINT_NUM, 0, THREAD_CONCURRENT_WATCHPOINT_NUM, + "Verify that concurrent breakpoints and watchpoints are reported " + "correctly"); + +THREAD_CONCURRENT_TEST(thread_concurrent_bp_sig, TCSH_DISCARD, + THREAD_CONCURRENT_BREAKPOINT_NUM, THREAD_CONCURRENT_SIGNALS_NUM, 0, + "Verify that concurrent breakpoints and signals are reported correctly"); +THREAD_CONCURRENT_TEST(thread_concurrent_bp_sig_sig_ign, TCSH_SIG_IGN, + THREAD_CONCURRENT_BREAKPOINT_NUM, THREAD_CONCURRENT_SIGNALS_NUM, 0, + "Verify that concurrent breakpoints and signals are reported correctly " + "and passed back to SIG_IGN handler"); +THREAD_CONCURRENT_TEST(thread_concurrent_bp_sig_handler, TCSH_HANDLER, + THREAD_CONCURRENT_BREAKPOINT_NUM, THREAD_CONCURRENT_SIGNALS_NUM, 0, + "Verify that concurrent breakpoints and signals are reported correctly " + "and passed back to a handler function"); + +THREAD_CONCURRENT_TEST(thread_concurrent_wp_sig, TCSH_DISCARD, + 0, THREAD_CONCURRENT_SIGNALS_NUM, THREAD_CONCURRENT_WATCHPOINT_NUM, + "Verify that concurrent watchpoints and signals are reported correctly"); +THREAD_CONCURRENT_TEST(thread_concurrent_wp_sig_sig_ign, TCSH_SIG_IGN, + 0, THREAD_CONCURRENT_SIGNALS_NUM, THREAD_CONCURRENT_WATCHPOINT_NUM, + "Verify that concurrent watchpoints and signals are reported correctly " + "and passed back to SIG_IGN handler"); +THREAD_CONCURRENT_TEST(thread_concurrent_wp_sig_handler, TCSH_HANDLER, + 0, THREAD_CONCURRENT_SIGNALS_NUM, THREAD_CONCURRENT_WATCHPOINT_NUM, + "Verify that concurrent watchpoints and signals are reported correctly " + "and passed back to a handler function"); + +THREAD_CONCURRENT_TEST(thread_concurrent_bp_wp_sig, TCSH_DISCARD, + THREAD_CONCURRENT_BREAKPOINT_NUM, THREAD_CONCURRENT_SIGNALS_NUM, + THREAD_CONCURRENT_WATCHPOINT_NUM, + "Verify that concurrent breakpoints, watchpoints and signals are reported " + "correctly"); +THREAD_CONCURRENT_TEST(thread_concurrent_bp_wp_sig_sig_ign, TCSH_SIG_IGN, + THREAD_CONCURRENT_BREAKPOINT_NUM, THREAD_CONCURRENT_SIGNALS_NUM, + THREAD_CONCURRENT_WATCHPOINT_NUM, + "Verify that concurrent breakpoints, watchpoints and signals are reported " + "correctly and passed back to SIG_IGN handler"); +THREAD_CONCURRENT_TEST(thread_concurrent_bp_wp_sig_handler, TCSH_HANDLER, + THREAD_CONCURRENT_BREAKPOINT_NUM, THREAD_CONCURRENT_SIGNALS_NUM, + THREAD_CONCURRENT_WATCHPOINT_NUM, + "Verify that concurrent breakpoints, watchpoints and signals are reported " + "correctly and passed back to a handler function"); +#endif + +#endif /*defined(TWAIT_HAVE_STATUS)*/ + +#define ATF_TP_ADD_TCS_PTRACE_WAIT_THREADS() \ + ATF_TP_ADD_TC(tp, trace_thread_nolwpevents); \ + ATF_TP_ADD_TC(tp, trace_thread_lwpexit); \ + ATF_TP_ADD_TC(tp, trace_thread_lwpcreate); \ + ATF_TP_ADD_TC(tp, trace_thread_lwpcreate_and_exit); \ + ATF_TP_ADD_TC(tp, trace_thread_lwpexit_masked_sigtrap); \ + ATF_TP_ADD_TC(tp, trace_thread_lwpcreate_masked_sigtrap); \ + ATF_TP_ADD_TC(tp, trace_thread_lwpcreate_and_exit_masked_sigtrap); \ + ATF_TP_ADD_TC(tp, threads_and_exec); \ + ATF_TP_ADD_TC(tp, suspend_no_deadlock); \ + ATF_TP_ADD_TC(tp, resume); \ + ATF_TP_ADD_TC_HAVE_STATUS(tp, thread_concurrent_signals); \ + ATF_TP_ADD_TC_HAVE_STATUS(tp, thread_concurrent_signals_sig_ign); \ + ATF_TP_ADD_TC_HAVE_STATUS(tp, thread_concurrent_signals_handler); \ + ATF_TP_ADD_TC_HAVE_STATUS_X86(tp, thread_concurrent_breakpoints); \ + ATF_TP_ADD_TC_HAVE_STATUS_X86(tp, thread_concurrent_watchpoints); \ + ATF_TP_ADD_TC_HAVE_STATUS_X86(tp, thread_concurrent_bp_wp); \ + ATF_TP_ADD_TC_HAVE_STATUS_X86(tp, thread_concurrent_bp_sig); \ + ATF_TP_ADD_TC_HAVE_STATUS_X86(tp, thread_concurrent_bp_sig_sig_ign); \ + ATF_TP_ADD_TC_HAVE_STATUS_X86(tp, thread_concurrent_bp_sig_handler); \ + ATF_TP_ADD_TC_HAVE_STATUS_X86(tp, thread_concurrent_wp_sig); \ + ATF_TP_ADD_TC_HAVE_STATUS_X86(tp, thread_concurrent_wp_sig_sig_ign); \ + ATF_TP_ADD_TC_HAVE_STATUS_X86(tp, thread_concurrent_wp_sig_handler); \ + ATF_TP_ADD_TC_HAVE_STATUS_X86(tp, thread_concurrent_bp_wp_sig); \ + ATF_TP_ADD_TC_HAVE_STATUS_X86(tp, thread_concurrent_bp_wp_sig_sig_ign); \ + ATF_TP_ADD_TC_HAVE_STATUS_X86(tp, thread_concurrent_bp_wp_sig_handler); diff --git a/lib/libc/sys/t_ptrace_topology_wait.h b/lib/libc/sys/t_ptrace_topology_wait.h new file mode 100644 --- /dev/null +++ b/lib/libc/sys/t_ptrace_topology_wait.h @@ -0,0 +1,721 @@ +/* $NetBSD: t_ptrace_topology_wait.h,v 1.1 2020/05/05 00:33:37 kamil Exp $ */ + +/*- + * Copyright (c) 2016, 2017, 2018, 2019, 2020 The NetBSD Foundation, Inc. + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + + +ATF_TC(traceme_pid1_parent); +ATF_TC_HEAD(traceme_pid1_parent, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that PT_TRACE_ME is not allowed when our parent is PID1"); +} + +ATF_TC_BODY(traceme_pid1_parent, tc) +{ + struct msg_fds parent_child; + int exitval_child1 = 1, exitval_child2 = 2; + pid_t child1, child2, wpid; + uint8_t msg = 0xde; /* dummy message for IPC based on pipe(2) */ +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + + SYSCALL_REQUIRE(msg_open(&parent_child) == 0); + + DPRINTF("Before forking process PID=%d\n", getpid()); + SYSCALL_REQUIRE((child1 = fork()) != -1); + if (child1 == 0) { + DPRINTF("Before forking process PID=%d\n", getpid()); + SYSCALL_REQUIRE((child2 = fork()) != -1); + if (child2 != 0) { + DPRINTF("Parent process PID=%d, child2's PID=%d\n", + getpid(), child2); + _exit(exitval_child1); + } + CHILD_FROM_PARENT("exit child1", parent_child, msg); + + DPRINTF("Assert that our parent is PID1 (initproc)\n"); + FORKEE_ASSERT_EQ(getppid(), 1); + + DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) == -1); + SYSCALL_REQUIRE_ERRNO(errno, EPERM); + + CHILD_TO_PARENT("child2 exiting", parent_child, msg); + + _exit(exitval_child2); + } + DPRINTF("Parent process PID=%d, child1's PID=%d\n", getpid(), child1); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS( + wpid = TWAIT_GENERIC(child1, &status, WEXITED), child1); + + validate_status_exited(status, exitval_child1); + + DPRINTF("Notify that child1 is dead\n"); + PARENT_TO_CHILD("exit child1", parent_child, msg); + + DPRINTF("Wait for exiting of child2\n"); + PARENT_FROM_CHILD("child2 exiting", parent_child, msg); +} + +/// ---------------------------------------------------------------------------- + +#if defined(TWAIT_HAVE_PID) +static void +tracer_sees_terminaton_before_the_parent_raw(bool notimeout, bool unrelated, + bool stopped) +{ + /* + * notimeout - disable timeout in await zombie function + * unrelated - attach from unrelated tracer reparented to initproc + * stopped - attach to a stopped process + */ + + struct msg_fds parent_tracee, parent_tracer; + const int exitval_tracee = 5; + const int exitval_tracer = 10; + pid_t tracee, tracer, wpid; + uint8_t msg = 0xde; /* dummy message for IPC based on pipe(2) */ +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + + /* + * Only a subset of options are supported. + */ + ATF_REQUIRE((!notimeout && !unrelated && !stopped) || + (!notimeout && unrelated && !stopped) || + (notimeout && !unrelated && !stopped) || + (!notimeout && unrelated && stopped)); + + DPRINTF("Spawn tracee\n"); + SYSCALL_REQUIRE(msg_open(&parent_tracee) == 0); + tracee = atf_utils_fork(); + if (tracee == 0) { + if (stopped) { + DPRINTF("Stop self PID %d\n", getpid()); + raise(SIGSTOP); + } + + // Wait for parent to let us exit + CHILD_FROM_PARENT("exit tracee", parent_tracee, msg); + _exit(exitval_tracee); + } + + DPRINTF("Spawn debugger\n"); + SYSCALL_REQUIRE(msg_open(&parent_tracer) == 0); + tracer = atf_utils_fork(); + if (tracer == 0) { + if(unrelated) { + /* Fork again and drop parent to reattach to PID 1 */ + tracer = atf_utils_fork(); + if (tracer != 0) + _exit(exitval_tracer); + } + + if (stopped) { + DPRINTF("Await for a stopped parent PID %d\n", tracee); + await_stopped(tracee); + } + + DPRINTF("Before calling PT_ATTACH from tracee %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_ATTACH, tracee, NULL, 0) != -1); + + /* Wait for tracee and assert that it was stopped w/ SIGSTOP */ + FORKEE_REQUIRE_SUCCESS( + wpid = TWAIT_GENERIC(tracee, &status, 0), tracee); + + forkee_status_stopped(status, SIGSTOP); + + /* Resume tracee with PT_CONTINUE */ + FORKEE_ASSERT(ptrace(PT_CONTINUE, tracee, (void *)1, 0) != -1); + + /* Inform parent that tracer has attached to tracee */ + CHILD_TO_PARENT("tracer ready", parent_tracer, msg); + + /* Wait for parent to tell use that tracee should have exited */ + CHILD_FROM_PARENT("wait for tracee exit", parent_tracer, msg); + + /* Wait for tracee and assert that it exited */ + FORKEE_REQUIRE_SUCCESS( + wpid = TWAIT_GENERIC(tracee, &status, 0), tracee); + + forkee_status_exited(status, exitval_tracee); + DPRINTF("Tracee %d exited with %d\n", tracee, exitval_tracee); + + DPRINTF("Before exiting of the tracer process\n"); + _exit(unrelated ? 0 /* collect by initproc */ : exitval_tracer); + } + + if (unrelated) { + DPRINTF("Wait for the tracer process (direct child) to exit " + "calling %s()\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS( + wpid = TWAIT_GENERIC(tracer, &status, 0), tracer); + + validate_status_exited(status, exitval_tracer); + + DPRINTF("Wait for the non-exited tracee process with %s()\n", + TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS( + wpid = TWAIT_GENERIC(tracee, NULL, WNOHANG), 0); + } + + DPRINTF("Wait for the tracer to attach to the tracee\n"); + PARENT_FROM_CHILD("tracer ready", parent_tracer, msg); + + DPRINTF("Resume the tracee and let it exit\n"); + PARENT_TO_CHILD("exit tracee", parent_tracee, msg); + + DPRINTF("Detect that tracee is zombie\n"); + if (notimeout) + await_zombie_raw(tracee, 0); + else + await_zombie(tracee); + + DPRINTF("Assert that there is no status about tracee %d - " + "Tracer must detect zombie first - calling %s()\n", tracee, + TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS( + wpid = TWAIT_GENERIC(tracee, &status, WNOHANG), 0); + + if (unrelated) { + DPRINTF("Resume the tracer and let it detect exited tracee\n"); + PARENT_TO_CHILD("Message 2", parent_tracer, msg); + } else { + DPRINTF("Tell the tracer child should have exited\n"); + PARENT_TO_CHILD("wait for tracee exit", parent_tracer, msg); + DPRINTF("Wait for tracer to finish its job and exit - calling " + "%s()\n", TWAIT_FNAME); + + DPRINTF("Wait from tracer child to complete waiting for " + "tracee\n"); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(tracer, &status, 0), + tracer); + + validate_status_exited(status, exitval_tracer); + } + + DPRINTF("Wait for tracee to finish its job and exit - calling %s()\n", + TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(tracee, &status, 0), tracee); + + validate_status_exited(status, exitval_tracee); + + msg_close(&parent_tracer); + msg_close(&parent_tracee); +} + +ATF_TC(tracer_sees_terminaton_before_the_parent); +ATF_TC_HEAD(tracer_sees_terminaton_before_the_parent, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Assert that tracer sees process termination before the parent"); +} + +ATF_TC_BODY(tracer_sees_terminaton_before_the_parent, tc) +{ + + tracer_sees_terminaton_before_the_parent_raw(false, false, false); +} + +ATF_TC(tracer_sysctl_lookup_without_duplicates); +ATF_TC_HEAD(tracer_sysctl_lookup_without_duplicates, tc) +{ + atf_tc_set_md_var(tc, "timeout", "15"); + atf_tc_set_md_var(tc, "descr", + "Assert that await_zombie() in attach1 always finds a single " + "process and no other error is reported"); +} + +ATF_TC_BODY(tracer_sysctl_lookup_without_duplicates, tc) +{ + time_t start, end; + double diff; + unsigned long N = 0; + + /* + * Reuse this test with tracer_sees_terminaton_before_the_parent_raw(). + * This test body isn't specific to this race, however it's just good + * enough for this purposes, no need to invent a dedicated code flow. + */ + + start = time(NULL); + while (true) { + DPRINTF("Step: %lu\n", N); + tracer_sees_terminaton_before_the_parent_raw(true, false, + false); + end = time(NULL); + diff = difftime(end, start); + if (diff >= 5.0) + break; + ++N; + } + DPRINTF("Iterations: %lu\n", N); +} + +ATF_TC(unrelated_tracer_sees_terminaton_before_the_parent); +ATF_TC_HEAD(unrelated_tracer_sees_terminaton_before_the_parent, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Assert that tracer sees process termination before the parent"); +} + +ATF_TC_BODY(unrelated_tracer_sees_terminaton_before_the_parent, tc) +{ + + tracer_sees_terminaton_before_the_parent_raw(false, true, false); +} + +ATF_TC(tracer_attach_to_unrelated_stopped_process); +ATF_TC_HEAD(tracer_attach_to_unrelated_stopped_process, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Assert that tracer can attach to an unrelated stopped process"); +} + +ATF_TC_BODY(tracer_attach_to_unrelated_stopped_process, tc) +{ + + tracer_sees_terminaton_before_the_parent_raw(false, true, true); +} +#endif + +/// ---------------------------------------------------------------------------- + +static void +parent_attach_to_its_child(bool stopped) +{ + struct msg_fds parent_tracee; + const int exitval_tracee = 5; + pid_t tracee, wpid; + uint8_t msg = 0xde; /* dummy message for IPC based on pipe(2) */ +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + + DPRINTF("Spawn tracee\n"); + SYSCALL_REQUIRE(msg_open(&parent_tracee) == 0); + tracee = atf_utils_fork(); + if (tracee == 0) { + CHILD_FROM_PARENT("Message 1", parent_tracee, msg); + DPRINTF("Parent should now attach to tracee\n"); + + if (stopped) { + DPRINTF("Stop self PID %d\n", getpid()); + SYSCALL_REQUIRE(raise(SIGSTOP) != -1); + } + + CHILD_FROM_PARENT("Message 2", parent_tracee, msg); + /* Wait for message from the parent */ + _exit(exitval_tracee); + } + PARENT_TO_CHILD("Message 1", parent_tracee, msg); + + if (stopped) { + DPRINTF("Await for a stopped tracee PID %d\n", tracee); + await_stopped(tracee); + } + + DPRINTF("Before calling PT_ATTACH for tracee %d\n", tracee); + SYSCALL_REQUIRE(ptrace(PT_ATTACH, tracee, NULL, 0) != -1); + + DPRINTF("Wait for the stopped tracee process with %s()\n", + TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS( + wpid = TWAIT_GENERIC(tracee, &status, 0), tracee); + + validate_status_stopped(status, SIGSTOP); + + DPRINTF("Resume tracee with PT_CONTINUE\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, tracee, (void *)1, 0) != -1); + + DPRINTF("Let the tracee exit now\n"); + PARENT_TO_CHILD("Message 2", parent_tracee, msg); + + DPRINTF("Wait for tracee to exit with %s()\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS( + wpid = TWAIT_GENERIC(tracee, &status, 0), tracee); + + validate_status_exited(status, exitval_tracee); + + DPRINTF("Before calling %s() for tracee\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, + wpid = TWAIT_GENERIC(tracee, &status, 0)); + + msg_close(&parent_tracee); +} + +ATF_TC(parent_attach_to_its_child); +ATF_TC_HEAD(parent_attach_to_its_child, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Assert that tracer parent can PT_ATTACH to its child"); +} + +ATF_TC_BODY(parent_attach_to_its_child, tc) +{ + + parent_attach_to_its_child(false); +} + +ATF_TC(parent_attach_to_its_stopped_child); +ATF_TC_HEAD(parent_attach_to_its_stopped_child, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Assert that tracer parent can PT_ATTACH to its stopped child"); +} + +ATF_TC_BODY(parent_attach_to_its_stopped_child, tc) +{ + + parent_attach_to_its_child(true); +} + +/// ---------------------------------------------------------------------------- + +static void +child_attach_to_its_parent(bool stopped) +{ + struct msg_fds parent_tracee; + const int exitval_tracer = 5; + pid_t tracer, wpid; + uint8_t msg = 0xde; /* dummy message for IPC based on pipe(2) */ +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + + DPRINTF("Spawn tracer\n"); + SYSCALL_REQUIRE(msg_open(&parent_tracee) == 0); + tracer = atf_utils_fork(); + if (tracer == 0) { + /* Wait for message from the parent */ + CHILD_FROM_PARENT("Message 1", parent_tracee, msg); + + if (stopped) { + DPRINTF("Await for a stopped parent PID %d\n", + getppid()); + await_stopped(getppid()); + } + + DPRINTF("Attach to parent PID %d with PT_ATTACH from child\n", + getppid()); + FORKEE_ASSERT(ptrace(PT_ATTACH, getppid(), NULL, 0) != -1); + + DPRINTF("Wait for the stopped parent process with %s()\n", + TWAIT_FNAME); + FORKEE_REQUIRE_SUCCESS( + wpid = TWAIT_GENERIC(getppid(), &status, 0), getppid()); + + forkee_status_stopped(status, SIGSTOP); + + DPRINTF("Resume parent with PT_DETACH\n"); + FORKEE_ASSERT(ptrace(PT_DETACH, getppid(), (void *)1, 0) + != -1); + + /* Tell parent we are ready */ + CHILD_TO_PARENT("Message 1", parent_tracee, msg); + + _exit(exitval_tracer); + } + + DPRINTF("Wait for the tracer to become ready\n"); + PARENT_TO_CHILD("Message 1", parent_tracee, msg); + + if (stopped) { + DPRINTF("Stop self PID %d\n", getpid()); + SYSCALL_REQUIRE(raise(SIGSTOP) != -1); + } + + DPRINTF("Allow the tracer to exit now\n"); + PARENT_FROM_CHILD("Message 1", parent_tracee, msg); + + DPRINTF("Wait for tracer to exit with %s()\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS( + wpid = TWAIT_GENERIC(tracer, &status, 0), tracer); + + validate_status_exited(status, exitval_tracer); + + DPRINTF("Before calling %s() for tracer\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, + wpid = TWAIT_GENERIC(tracer, &status, 0)); + + msg_close(&parent_tracee); +} + +ATF_TC(child_attach_to_its_parent); +ATF_TC_HEAD(child_attach_to_its_parent, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Assert that tracer child can PT_ATTACH to its parent"); +} + +ATF_TC_BODY(child_attach_to_its_parent, tc) +{ + + child_attach_to_its_parent(false); +} + +ATF_TC(child_attach_to_its_stopped_parent); +ATF_TC_HEAD(child_attach_to_its_stopped_parent, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Assert that tracer child can PT_ATTACH to its stopped parent"); +} + +ATF_TC_BODY(child_attach_to_its_stopped_parent, tc) +{ + /* + * The ATF framework (atf-run) does not tolerate raise(SIGSTOP), as + * this causes a pipe (established from atf-run) to be broken. + * atf-run uses this mechanism to monitor whether a test is alive. + * + * As a workaround spawn this test as a subprocess. + */ + + const int exitval = 15; + pid_t child, wpid; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + + SYSCALL_REQUIRE((child = fork()) != -1); + if (child == 0) { + child_attach_to_its_parent(true); + _exit(exitval); + } else { + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_exited(status, exitval); + + DPRINTF("Before calling %s() for the exited child\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); + } +} + +/// ---------------------------------------------------------------------------- + +#if defined(TWAIT_HAVE_PID) + +enum tracee_sees_its_original_parent_type { + TRACEE_SEES_ITS_ORIGINAL_PARENT_GETPPID, + TRACEE_SEES_ITS_ORIGINAL_PARENT_SYSCTL_KINFO_PROC2, + TRACEE_SEES_ITS_ORIGINAL_PARENT_PROCFS_STATUS +}; + +static void +tracee_sees_its_original_parent(enum tracee_sees_its_original_parent_type type) +{ + struct msg_fds parent_tracer, parent_tracee; + const int exitval_tracee = 5; + const int exitval_tracer = 10; + pid_t parent, tracee, tracer, wpid; + uint8_t msg = 0xde; /* dummy message for IPC based on pipe(2) */ +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + /* sysctl(3) - kinfo_proc2 */ + int name[CTL_MAXNAME]; + struct kinfo_proc2 kp; + size_t len = sizeof(kp); + unsigned int namelen; + + /* procfs - status */ + FILE *fp; + struct stat st; + const char *fname = "/proc/curproc/status"; + char s_executable[MAXPATHLEN]; + int s_pid, s_ppid; + int rv; + + if (type == TRACEE_SEES_ITS_ORIGINAL_PARENT_PROCFS_STATUS) { + SYSCALL_REQUIRE( + (rv = stat(fname, &st)) == 0 || (errno == ENOENT)); + if (rv != 0) + atf_tc_skip("/proc/curproc/status not found"); + } + + DPRINTF("Spawn tracee\n"); + SYSCALL_REQUIRE(msg_open(&parent_tracer) == 0); + SYSCALL_REQUIRE(msg_open(&parent_tracee) == 0); + tracee = atf_utils_fork(); + if (tracee == 0) { + parent = getppid(); + + /* Emit message to the parent */ + CHILD_TO_PARENT("tracee ready", parent_tracee, msg); + CHILD_FROM_PARENT("exit tracee", parent_tracee, msg); + + switch (type) { + case TRACEE_SEES_ITS_ORIGINAL_PARENT_GETPPID: + FORKEE_ASSERT_EQ(parent, getppid()); + break; + case TRACEE_SEES_ITS_ORIGINAL_PARENT_SYSCTL_KINFO_PROC2: + namelen = 0; + name[namelen++] = CTL_KERN; + name[namelen++] = KERN_PROC2; + name[namelen++] = KERN_PROC_PID; + name[namelen++] = getpid(); + name[namelen++] = len; + name[namelen++] = 1; + + FORKEE_ASSERT_EQ( + sysctl(name, namelen, &kp, &len, NULL, 0), 0); + FORKEE_ASSERT_EQ(parent, kp.p_ppid); + break; + case TRACEE_SEES_ITS_ORIGINAL_PARENT_PROCFS_STATUS: + /* + * Format: + * EXECUTABLE PID PPID ... + */ + FORKEE_ASSERT((fp = fopen(fname, "r")) != NULL); + fscanf(fp, "%s %d %d", s_executable, &s_pid, &s_ppid); + FORKEE_ASSERT_EQ(fclose(fp), 0); + FORKEE_ASSERT_EQ(parent, s_ppid); + break; + } + + _exit(exitval_tracee); + } + DPRINTF("Wait for child to record its parent identifier (pid)\n"); + PARENT_FROM_CHILD("tracee ready", parent_tracee, msg); + + DPRINTF("Spawn debugger\n"); + tracer = atf_utils_fork(); + if (tracer == 0) { + /* No IPC to communicate with the child */ + DPRINTF("Before calling PT_ATTACH from tracee %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_ATTACH, tracee, NULL, 0) != -1); + + /* Wait for tracee and assert that it was stopped w/ SIGSTOP */ + FORKEE_REQUIRE_SUCCESS( + wpid = TWAIT_GENERIC(tracee, &status, 0), tracee); + + forkee_status_stopped(status, SIGSTOP); + + /* Resume tracee with PT_CONTINUE */ + FORKEE_ASSERT(ptrace(PT_CONTINUE, tracee, (void *)1, 0) != -1); + + /* Inform parent that tracer has attached to tracee */ + CHILD_TO_PARENT("tracer ready", parent_tracer, msg); + + /* Wait for parent to tell use that tracee should have exited */ + CHILD_FROM_PARENT("wait for tracee exit", parent_tracer, msg); + + /* Wait for tracee and assert that it exited */ + FORKEE_REQUIRE_SUCCESS( + wpid = TWAIT_GENERIC(tracee, &status, 0), tracee); + + forkee_status_exited(status, exitval_tracee); + + DPRINTF("Before exiting of the tracer process\n"); + _exit(exitval_tracer); + } + + DPRINTF("Wait for the tracer to attach to the tracee\n"); + PARENT_FROM_CHILD("tracer ready", parent_tracer, msg); + + DPRINTF("Resume the tracee and let it exit\n"); + PARENT_TO_CHILD("exit tracee", parent_tracee, msg); + + DPRINTF("Detect that tracee is zombie\n"); + await_zombie(tracee); + + DPRINTF("Assert that there is no status about tracee - " + "Tracer must detect zombie first - calling %s()\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS( + wpid = TWAIT_GENERIC(tracee, &status, WNOHANG), 0); + + DPRINTF("Tell the tracer child should have exited\n"); + PARENT_TO_CHILD("wait for tracee exit", parent_tracer, msg); + + DPRINTF("Wait from tracer child to complete waiting for tracee\n"); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(tracer, &status, 0), + tracer); + + validate_status_exited(status, exitval_tracer); + + DPRINTF("Wait for tracee to finish its job and exit - calling %s()\n", + TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(tracee, &status, WNOHANG), + tracee); + + validate_status_exited(status, exitval_tracee); + + msg_close(&parent_tracer); + msg_close(&parent_tracee); +} + +#define TRACEE_SEES_ITS_ORIGINAL_PARENT(test, type, descr) \ +ATF_TC(test); \ +ATF_TC_HEAD(test, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", \ + "Assert that tracee sees its original parent when being traced " \ + "(check " descr ")"); \ +} \ + \ +ATF_TC_BODY(test, tc) \ +{ \ + \ + tracee_sees_its_original_parent(type); \ +} + +TRACEE_SEES_ITS_ORIGINAL_PARENT( + tracee_sees_its_original_parent_getppid, + TRACEE_SEES_ITS_ORIGINAL_PARENT_GETPPID, + "getppid(2)"); +TRACEE_SEES_ITS_ORIGINAL_PARENT( + tracee_sees_its_original_parent_sysctl_kinfo_proc2, + TRACEE_SEES_ITS_ORIGINAL_PARENT_SYSCTL_KINFO_PROC2, + "sysctl(3) and kinfo_proc2"); +TRACEE_SEES_ITS_ORIGINAL_PARENT( + tracee_sees_its_original_parent_procfs_status, + TRACEE_SEES_ITS_ORIGINAL_PARENT_PROCFS_STATUS, + "the status file in procfs"); +#endif + +#define ATF_TP_ADD_TCS_PTRACE_WAIT_TOPOLOGY() \ + ATF_TP_ADD_TC(tp, traceme_pid1_parent); \ + ATF_TP_ADD_TC_HAVE_PID(tp, tracer_sees_terminaton_before_the_parent); \ + ATF_TP_ADD_TC_HAVE_PID(tp, tracer_sysctl_lookup_without_duplicates); \ + ATF_TP_ADD_TC_HAVE_PID(tp, \ + unrelated_tracer_sees_terminaton_before_the_parent); \ + ATF_TP_ADD_TC_HAVE_PID(tp, tracer_attach_to_unrelated_stopped_process); \ + ATF_TP_ADD_TC(tp, parent_attach_to_its_child); \ + ATF_TP_ADD_TC(tp, parent_attach_to_its_stopped_child); \ + ATF_TP_ADD_TC(tp, child_attach_to_its_parent); \ + ATF_TP_ADD_TC(tp, child_attach_to_its_stopped_parent); \ + ATF_TP_ADD_TC_HAVE_PID(tp, \ + tracee_sees_its_original_parent_getppid); \ + ATF_TP_ADD_TC_HAVE_PID(tp, \ + tracee_sees_its_original_parent_sysctl_kinfo_proc2); \ + ATF_TP_ADD_TC_HAVE_PID(tp, \ + tracee_sees_its_original_parent_procfs_status); diff --git a/lib/libc/sys/t_ptrace_wait.h b/lib/libc/sys/t_ptrace_wait.h new file mode 100644 --- /dev/null +++ b/lib/libc/sys/t_ptrace_wait.h @@ -0,0 +1,794 @@ +/* $NetBSD: t_ptrace_wait.h,v 1.33 2021/05/24 10:44:06 gson Exp $ */ + +/*- + * Copyright (c) 2016, 2017, 2018, 2019 The NetBSD Foundation, Inc. + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +/* Detect plain wait(2) use-case */ +#if !defined(TWAIT_WAITPID) && \ + !defined(TWAIT_WAITID) && \ + !defined(TWAIT_WAIT3) && \ + !defined(TWAIT_WAIT4) && \ + !defined(TWAIT_WAIT6) +#define TWAIT_WAIT +#endif + +/* + * There are two classes of wait(2)-like functions: + * - wait4(2)-like accepting pid_t, optional options parameter, struct rusage* + * - wait6(2)-like accepting idtype_t, id_t, struct wrusage, mandatory options + * + * The TWAIT_FNAME value is to be used for convenience in debug messages. + * + * The TWAIT_GENERIC() macro is designed to reuse the same unmodified + * code with as many wait(2)-like functions as possible. + * + * In a common use-case wait4(2) and wait6(2)-like function can work the almost + * the same way, however there are few important differences: + * wait6(2) must specify P_PID for idtype to match wpid from wait4(2). + * To behave like wait4(2), wait6(2) the 'options' to wait must include + * WEXITED|WTRUNCATED. + * + * There are two helper macros (they purpose it to mach more than one + * wait(2)-like function): + * The TWAIT_HAVE_STATUS - specifies whether a function can retrieve + * status (as integer value). + * The TWAIT_HAVE_PID - specifies whether a function can request + * exact process identifier + * The TWAIT_HAVE_RUSAGE - specifies whether a function can request + * the struct rusage value + * + */ + +#if defined(TWAIT_WAIT) +# define TWAIT_FNAME "wait" +# define TWAIT_WAIT4TYPE(a,b,c,d) wait((b)) +# define TWAIT_GENERIC(a,b,c) wait((b)) +# define TWAIT_HAVE_STATUS 1 +#elif defined(TWAIT_WAITPID) +# define TWAIT_FNAME "waitpid" +# define TWAIT_WAIT4TYPE(a,b,c,d) waitpid((a),(b),(c)) +# define TWAIT_GENERIC(a,b,c) waitpid((a),(b),(c)) +# define TWAIT_HAVE_PID 1 +# define TWAIT_HAVE_STATUS 1 +# define TWAIT_HAVE_OPTIONS 1 +#elif defined(TWAIT_WAITID) +# define TWAIT_FNAME "waitid" +# define TWAIT_GENERIC(a,b,c) \ + waitid(P_PID,(a),NULL,(c)|WEXITED|WTRAPPED) +# define TWAIT_WAIT6TYPE(a,b,c,d,e,f) waitid((a),(b),(f),(d)) +# define TWAIT_HAVE_PID 1 +# define TWAIT_HAVE_OPTIONS 1 +#elif defined(TWAIT_WAIT3) +# define TWAIT_FNAME "wait3" +# define TWAIT_WAIT4TYPE(a,b,c,d) wait3((b),(c),(d)) +# define TWAIT_GENERIC(a,b,c) wait3((b),(c),NULL) +# define TWAIT_HAVE_STATUS 1 +# define TWAIT_HAVE_RUSAGE 1 +# define TWAIT_HAVE_OPTIONS 1 +#elif defined(TWAIT_WAIT4) +# define TWAIT_FNAME "wait4" +# define TWAIT_WAIT4TYPE(a,b,c,d) wait4((a),(b),(c),(d)) +# define TWAIT_GENERIC(a,b,c) wait4((a),(b),(c),NULL) +# define TWAIT_HAVE_PID 1 +# define TWAIT_HAVE_STATUS 1 +# define TWAIT_HAVE_RUSAGE 1 +# define TWAIT_HAVE_OPTIONS 1 +#elif defined(TWAIT_WAIT6) +# define TWAIT_FNAME "wait6" +# define TWAIT_WAIT6TYPE(a,b,c,d,e,f) wait6((a),(b),(c),(d),(e),(f)) +# define TWAIT_GENERIC(a,b,c) \ + wait6(P_PID,(a),(b),(c)|WEXITED|WTRAPPED,NULL,NULL) +# define TWAIT_HAVE_PID 1 +# define TWAIT_HAVE_STATUS 1 +# define TWAIT_HAVE_OPTIONS 1 +#endif + +/* + * There are 3 groups of tests: + * - TWAIT_GENERIC() (wait, wait2, waitpid, wait3, wait4, wait6) + * - TWAIT_WAIT4TYPE() (wait2, waitpid, wait3, wait4) + * - TWAIT_WAIT6TYPE() (waitid, wait6) + * + * Tests only in the above categories are allowed. However some tests are not + * possible in the context requested functionality to be verified, therefore + * there are helper macros: + * - TWAIT_HAVE_PID (wait2, waitpid, waitid, wait4, wait6) + * - TWAIT_HAVE_STATUS (wait, wait2, waitpid, wait3, wait4, wait6) + * - TWAIT_HAVE_RUSAGE (wait3, wait4) + * - TWAIT_HAVE_RETPID (wait, wait2, waitpid, wait3, wait4, wait6) + * + * If there is an intention to test e.g. wait6(2) specific features in the + * ptrace(2) context, find the most matching group and with #ifdefs reduce + * functionality of less featured than wait6(2) interface (TWAIT_WAIT6TYPE). + * + * For clarity never use negative preprocessor checks, like: + * #if !defined(TWAIT_WAIT4) + * always refer to checks for positive values. + */ + +#define TEST_REQUIRE_EQ(x, y) \ +do { \ + uintmax_t vx = (x); \ + uintmax_t vy = (y); \ + int ret = vx == vy; \ + if (!ret) \ + ATF_REQUIRE_EQ_MSG(vx, vy, "%s(%ju) == %s(%ju)", \ + #x, vx, #y, vy); \ +} while (/*CONSTCOND*/0) + +/* + * A child process cannot call atf functions and expect them to magically + * work like in the parent. + * The printf(3) messaging from a child will not work out of the box as well + * without estabilishing a communication protocol with its parent. To not + * overcomplicate the tests - do not log from a child and use err(3)/errx(3) + * wrapped with FORKEE_ASSERT()/FORKEE_ASSERTX() as that is guaranteed to work. + */ +#define FORKEE_ASSERT_EQ(x, y) \ +do { \ + uintmax_t vx = (x); \ + uintmax_t vy = (y); \ + int ret = vx == vy; \ + if (!ret) \ + errx(EXIT_FAILURE, "%s:%d %s(): Assertion failed for: " \ + "%s(%ju) == %s(%ju)", __FILE__, __LINE__, __func__, \ + #x, vx, #y, vy); \ +} while (/*CONSTCOND*/0) + +#define FORKEE_ASSERT_NEQ(x, y) \ +do { \ + uintmax_t vx = (x); \ + uintmax_t vy = (y); \ + int ret = vx != vy; \ + if (!ret) \ + errx(EXIT_FAILURE, "%s:%d %s(): Assertion failed for: " \ + "%s(%ju) != %s(%ju)", __FILE__, __LINE__, __func__, \ + #x, vx, #y, vy); \ +} while (/*CONSTCOND*/0) + +#define FORKEE_ASSERTX(x) \ +do { \ + int ret = (x); \ + if (!ret) \ + errx(EXIT_FAILURE, "%s:%d %s(): Assertion failed for: %s",\ + __FILE__, __LINE__, __func__, #x); \ +} while (/*CONSTCOND*/0) + +#define FORKEE_ASSERT(x) \ +do { \ + int ret = (x); \ + if (!ret) \ + err(EXIT_FAILURE, "%s:%d %s(): Assertion failed for: %s",\ + __FILE__, __LINE__, __func__, #x); \ +} while (/*CONSTCOND*/0) + +/* + * Simplify logic for functions using general purpose registers add HAVE_GPREGS + * + * For platforms that do not implement all needed calls for simplicity assume + * that they are unsupported at all. + */ +#if defined(PT_GETREGS) \ + && defined(PT_SETREGS) \ + && defined(PTRACE_REG_PC) \ + && defined(PTRACE_REG_SET_PC) \ + && defined(PTRACE_REG_SP) \ + && defined(PTRACE_REG_INTRV) +#define HAVE_GPREGS +#endif + +/* Add guards for floating point registers */ +#if defined(PT_GETFPREGS) \ + && defined(PT_SETFPREGS) +#define HAVE_FPREGS +#endif + +/* Add guards for cpu debug registers */ +#if defined(PT_GETDBREGS) \ + && defined(PT_SETDBREGS) +#define HAVE_DBREGS +#endif + +/* + * If waitid(2) returns because one or more processes have a state change to + * report, 0 is returned. If an error is detected, a value of -1 is returned + * and errno is set to indicate the error. If WNOHANG is specified and there + * are no stopped, continued or exited children, 0 is returned. + */ +#if defined(TWAIT_WAITID) +#define TWAIT_REQUIRE_SUCCESS(a,b) TEST_REQUIRE_EQ((a), 0) +#define TWAIT_REQUIRE_FAILURE(a,b) ATF_REQUIRE_ERRNO((a),(b) == -1) +#define FORKEE_REQUIRE_SUCCESS(a,b) FORKEE_ASSERT_EQ(a, 0) +#define FORKEE_REQUIRE_FAILURE(a,b) \ + FORKEE_ASSERTX(((a) == errno) && ((b) == -1)) +#else +#define TWAIT_REQUIRE_SUCCESS(a,b) TEST_REQUIRE_EQ((a), (b)) +#define TWAIT_REQUIRE_FAILURE(a,b) ATF_REQUIRE_ERRNO((a),(b) == -1) +#define FORKEE_REQUIRE_SUCCESS(a,b) FORKEE_ASSERT_EQ(a, b) +#define FORKEE_REQUIRE_FAILURE(a,b) \ + FORKEE_ASSERTX(((a) == errno) && ((b) == -1)) +#endif + +/* + * Helper tools to verify whether status reports exited value + */ +#if TWAIT_HAVE_STATUS +static void __used +validate_status_exited(int status, int expected) +{ + ATF_REQUIRE_MSG(WIFEXITED(status), "Reported !exited process"); + ATF_REQUIRE_MSG(!WIFCONTINUED(status), "Reported continued process"); + ATF_REQUIRE_MSG(!WIFSIGNALED(status), "Reported signaled process"); + ATF_REQUIRE_MSG(!WIFSTOPPED(status), "Reported stopped process"); + + ATF_REQUIRE_EQ_MSG(WEXITSTATUS(status), expected, + "The process has exited with invalid value %d != %d", + WEXITSTATUS(status), expected); +} + +static void __used +forkee_status_exited(int status, int expected) +{ + FORKEE_ASSERTX(WIFEXITED(status)); + FORKEE_ASSERTX(!WIFCONTINUED(status)); + FORKEE_ASSERTX(!WIFSIGNALED(status)); + FORKEE_ASSERTX(!WIFSTOPPED(status)); + + FORKEE_ASSERT_EQ(WEXITSTATUS(status), expected); +} + +static void __used +validate_status_continued(int status) +{ + ATF_REQUIRE_MSG(!WIFEXITED(status), "Reported exited process"); + ATF_REQUIRE_MSG(WIFCONTINUED(status), "Reported !continued process"); + ATF_REQUIRE_MSG(!WIFSIGNALED(status), "Reported signaled process"); + ATF_REQUIRE_MSG(!WIFSTOPPED(status), "Reported stopped process"); +} + +static void __used +forkee_status_continued(int status) +{ + FORKEE_ASSERTX(!WIFEXITED(status)); + FORKEE_ASSERTX(WIFCONTINUED(status)); + FORKEE_ASSERTX(!WIFSIGNALED(status)); + FORKEE_ASSERTX(!WIFSTOPPED(status)); +} + +static void __used +validate_status_signaled(int status, int expected_termsig, int expected_core) +{ + ATF_REQUIRE_MSG(!WIFEXITED(status), "Reported exited process"); + ATF_REQUIRE_MSG(!WIFCONTINUED(status), "Reported continued process"); + ATF_REQUIRE_MSG(WIFSIGNALED(status), "Reported !signaled process"); + ATF_REQUIRE_MSG(!WIFSTOPPED(status), "Reported stopped process"); + + ATF_REQUIRE_EQ_MSG(WTERMSIG(status), expected_termsig, + "Unexpected signal received"); + + ATF_REQUIRE_EQ_MSG(!!WCOREDUMP(status), expected_core, + "Unexpectedly core file %s generated", expected_core ? "not" : ""); +} + +static void __used +forkee_status_signaled(int status, int expected_termsig, int expected_core) +{ + FORKEE_ASSERTX(!WIFEXITED(status)); + FORKEE_ASSERTX(!WIFCONTINUED(status)); + FORKEE_ASSERTX(WIFSIGNALED(status)); + FORKEE_ASSERTX(!WIFSTOPPED(status)); + + FORKEE_ASSERT_EQ(WTERMSIG(status), expected_termsig); + FORKEE_ASSERT_EQ(!!WCOREDUMP(status), expected_core); +} + +static void __used +validate_status_stopped(int status, int expected) +{ + ATF_REQUIRE_MSG(!WIFEXITED(status), "Reported exited process"); + ATF_REQUIRE_MSG(!WIFCONTINUED(status), "Reported continued process"); + ATF_REQUIRE_MSG(!WIFSIGNALED(status), "Reported signaled process"); + ATF_REQUIRE_MSG(WIFSTOPPED(status), "Reported !stopped process"); + + char st[128], ex[128]; + strlcpy(st, strsignal(WSTOPSIG(status)), sizeof(st)); + strlcpy(ex, strsignal(expected), sizeof(ex)); + + ATF_REQUIRE_EQ_MSG(WSTOPSIG(status), expected, + "Unexpected stop signal received [%s] != [%s]", st, ex); +} + +static void __used +forkee_status_stopped(int status, int expected) +{ + FORKEE_ASSERTX(!WIFEXITED(status)); + FORKEE_ASSERTX(!WIFCONTINUED(status)); + FORKEE_ASSERTX(!WIFSIGNALED(status)); + FORKEE_ASSERTX(WIFSTOPPED(status)); + + FORKEE_ASSERT_EQ(WSTOPSIG(status), expected); +} +#else +#define validate_status_exited(a,b) +#define forkee_status_exited(a,b) +#define validate_status_continued(a,b) +#define forkee_status_continued(a,b) +#define validate_status_signaled(a,b,c) +#define forkee_status_signaled(a,b,c) +#define validate_status_stopped(a,b) +#define forkee_status_stopped(a,b) +#endif + +/* This function is currently designed to be run in the main/parent process */ +static void __used +await_zombie_raw(pid_t process, useconds_t ms) +{ + struct kinfo_proc2 p; + size_t len = sizeof(p); + + const int name[] = { + [0] = CTL_KERN, + [1] = KERN_PROC2, + [2] = KERN_PROC_PID, + [3] = process, + [4] = sizeof(p), + [5] = 1 + }; + + const size_t namelen = __arraycount(name); + + /* Await the process becoming a zombie */ + while(1) { + ATF_REQUIRE(sysctl(name, namelen, &p, &len, NULL, 0) == 0); + + if (p.p_stat == LSZOMB) + break; + + if (ms > 0) { + ATF_REQUIRE(usleep(ms) == 0); + } + } +} + +static void __used +await_zombie(pid_t process) +{ + + await_zombie_raw(process, 1000); +} + +static void __used +await_stopped(pid_t process) +{ + struct kinfo_proc2 p; + size_t len = sizeof(p); + + const int name[] = { + [0] = CTL_KERN, + [1] = KERN_PROC2, + [2] = KERN_PROC_PID, + [3] = process, + [4] = sizeof(p), + [5] = 1 + }; + + const size_t namelen = __arraycount(name); + + /* Await the process becoming a zombie */ + while(1) { + ATF_REQUIRE(sysctl(name, namelen, &p, &len, NULL, 0) == 0); + + if (p.p_stat == LSSTOP) + break; + + ATF_REQUIRE(usleep(1000) == 0); + } +} + +static pid_t __used +await_stopped_child(pid_t process) +{ + struct kinfo_proc2 *p = NULL; + size_t i, len; + pid_t child = -1; + + int name[] = { + [0] = CTL_KERN, + [1] = KERN_PROC2, + [2] = KERN_PROC_ALL, + [3] = 0, + [4] = sizeof(struct kinfo_proc2), + [5] = 0 + }; + + const size_t namelen = __arraycount(name); + + /* Await the process becoming a zombie */ + while(1) { + name[5] = 0; + + FORKEE_ASSERT_EQ(sysctl(name, namelen, 0, &len, NULL, 0), 0); + + FORKEE_ASSERT_EQ(reallocarr(&p, + len, + sizeof(struct kinfo_proc2)), 0); + + name[5] = len; + + FORKEE_ASSERT_EQ(sysctl(name, namelen, p, &len, NULL, 0), 0); + + for (i = 0; i < len/sizeof(struct kinfo_proc2); i++) { + if (p[i].p_pid == getpid()) + continue; + if (p[i].p_ppid != process) + continue; + if (p[i].p_stat != LSSTOP) + continue; + child = p[i].p_pid; + break; + } + + if (child != -1) + break; + + FORKEE_ASSERT_EQ(usleep(1000), 0); + } + + /* Free the buffer */ + FORKEE_ASSERT_EQ(reallocarr(&p, 0, sizeof(struct kinfo_proc2)), 0); + + return child; +} + +static void __used +await_collected(pid_t process) +{ + struct kinfo_proc2 p; + size_t len = sizeof(p); + + const int name[] = { + [0] = CTL_KERN, + [1] = KERN_PROC2, + [2] = KERN_PROC_PID, + [3] = process, + [4] = sizeof(p), + [5] = 1 + }; + + const size_t namelen = __arraycount(name); + + /* Await the process to disappear */ + while(1) { + FORKEE_ASSERT_EQ(sysctl(name, namelen, &p, &len, NULL, 0), 0); + if (len == 0) + break; + + ATF_REQUIRE(usleep(1000) == 0); + } +} + +/* Happy number sequence -- this function is used to just consume cpu cycles */ +#define HAPPY_NUMBER 1 + +/* If n is not happy then its sequence ends in the cycle: + * 4, 16, 37, 58, 89, 145, 42, 20, 4, ... */ +#define SAD_NUMBER 4 + +/* Calculate the sum of the squares of the digits of n */ +static unsigned __used +dsum(unsigned n) +{ + unsigned sum, x; + for (sum = 0; n; n /= 10) { + x = n % 10; + sum += x * x; + } + return sum; +} + +/* + * XXX: Disabled optimization is required to make tests for hardware assisted + * traps in .text functional + * + * Tested with GCC 5.4 on NetBSD 7.99.47 amd64 + */ +static int __used +#ifdef __clang__ +__attribute__((__optnone__)) +#else +__attribute__((__optimize__("O0"))) +#endif +check_happy(unsigned n) +{ + for (;;) { + unsigned total = dsum(n); + + if (total == HAPPY_NUMBER) + return 1; + if (total == SAD_NUMBER) + return 0; + + n = total; + } +} + +static void * __used +infinite_thread(void *arg __unused) +{ + + while (true) + continue; + + __unreachable(); +} + +static int __used +clone_func(void *arg) +{ + int ret; + + ret = (int)(intptr_t)arg; + + return ret; +} + +#if defined(HAVE_DBREGS) +static bool __used +can_we_set_dbregs(void) +{ + static long euid = -1; + static int user_set_dbregs = -1; + size_t user_set_dbregs_len = sizeof(user_set_dbregs); + + if (euid == -1) + euid = geteuid(); + + if (euid == 0) + return true; + + if (user_set_dbregs == -1) { + if (sysctlbyname("security.models.extensions.user_set_dbregs", + &user_set_dbregs, &user_set_dbregs_len, NULL, 0) + == -1) { + return false; + } + } + + if (user_set_dbregs > 0) + return true; + else + return false; +} +#endif + +static bool __used +get_user_va0_disable(void) +{ + static int user_va0_disable = -1; + size_t user_va0_disable_len = sizeof(user_va0_disable); + + if (user_va0_disable == -1) { + if (sysctlbyname("vm.user_va0_disable", + &user_va0_disable, &user_va0_disable_len, NULL, 0) + == -1) { + return true; + } + } + + if (user_va0_disable > 0) + return true; + else + return false; +} + +static bool __used +can_we_write_to_text(pid_t pid) +{ + int mib[3]; + int paxflags; + size_t len = sizeof(int); + + mib[0] = CTL_PROC; + mib[1] = pid; + mib[2] = PROC_PID_PAXFLAGS; + + if (sysctl(mib, 3, &paxflags, &len, NULL, 0) == -1) + return false; + + return !(paxflags & CTL_PROC_PAXFLAGS_MPROTECT); +} + +static void __used +trigger_trap(void) +{ + + /* Software breakpoint causes CPU trap, translated to SIGTRAP */ +#ifdef PTRACE_BREAKPOINT_ASM + PTRACE_BREAKPOINT_ASM; +#else + /* port me */ +#endif +} + +static void __used +trigger_segv(void) +{ + static volatile char *ptr = NULL; + + /* Access to unmapped memory causes CPU trap, translated to SIGSEGV */ + *ptr = 1; +} + +static void __used +trigger_ill(void) +{ + + /* Illegal instruction causes CPU trap, translated to SIGILL */ +#ifdef PTRACE_ILLEGAL_ASM +#ifndef __mips__ /* To avoid GXemul crash */ + PTRACE_ILLEGAL_ASM; +#endif +#else + /* port me */ +#endif +} + +#include + +#if (__arm__ && !__SOFTFP__) || __aarch64__ +#include /* only need for ARM Cortex/Neon hack */ + +static bool __used +are_fpu_exceptions_supported(void) +{ + /* + * Some NEON fpus do not trap on IEEE 754 FP exceptions. + * Skip these tests if running on them and compiled for + * hard float. + */ + if (0 == fpsetmask(fpsetmask(FP_X_INV))) + return false; + return true; +} +#else +#define are_fpu_exceptions_supported() 1 +#endif + +static void __used +trigger_fpe(void) +{ +#if __i386__ || __x86_64__ + /* + * XXX + * Hack for QEMU bug #1668041, by which floating-point division by + * zero is not trapped correctly. Also, assertions for si_code in + * ptrace_signal_wait.h are commented out. Clean them up after the + * bug is fixed. + */ + volatile int a, b; +#else + volatile double a, b; +#endif + + a = getpid(); + b = atoi("0"); + +#ifdef __HAVE_FENV + feenableexcept(FE_ALL_EXCEPT); +#endif + + /* Division by zero causes CPU trap, translated to SIGFPE */ + usleep((int)(a / b)); +} + +static void __used +trigger_bus(void) +{ + FILE *fp; + char *p; + + /* Open an empty file for writing. */ + fp = tmpfile(); + FORKEE_ASSERT_NEQ((uintptr_t)fp, (uintptr_t)NULL); + + /* + * Map an empty file with mmap(2) to a pointer. + * + * PROT_READ handles read-modify-write sequences emitted for + * certain combinations of CPUs and compilers (e.g. Alpha AXP). + */ + p = mmap(0, 1, PROT_READ|PROT_WRITE, MAP_PRIVATE, fileno(fp), 0); + FORKEE_ASSERT_NEQ((uintptr_t)p, (uintptr_t)MAP_FAILED); + + /* Invalid memory access causes CPU trap, translated to SIGBUS */ + *p = 'a'; +} + +struct lwp_event_count { + lwpid_t lec_lwp; + int lec_count; +}; + +static int * __used +find_event_count(struct lwp_event_count list[], lwpid_t lwp, size_t max_lwps) +{ + size_t i; + + for (i = 0; i < max_lwps; i++) { + if (list[i].lec_lwp == 0) + list[i].lec_lwp = lwp; + if (list[i].lec_lwp == lwp) + return &list[i].lec_count; + } + + atf_tc_fail("More LWPs reported than expected"); +} + +#define FIND_EVENT_COUNT(list, lwp) \ + find_event_count(list, lwp, __arraycount(list)) + + +#if defined(TWAIT_HAVE_PID) +#define ATF_TP_ADD_TC_HAVE_PID(a,b) ATF_TP_ADD_TC(a,b) +#else +#define ATF_TP_ADD_TC_HAVE_PID(a,b) +#endif + +#if defined(TWAIT_HAVE_STATUS) +#define ATF_TP_ADD_TC_HAVE_STATUS(a,b) ATF_TP_ADD_TC(a,b) +#else +#define ATF_TP_ADD_TC_HAVE_STATUS(a,b) +#endif + +#if defined(TWAIT_HAVE_STATUS) && (defined(__i386__) || defined(__x86_64__)) +#define ATF_TP_ADD_TC_HAVE_STATUS_X86(a,b) ATF_TP_ADD_TC(a,b) +#else +#define ATF_TP_ADD_TC_HAVE_STATUS_X86(a,b) +#endif + +#if defined(HAVE_GPREGS) +#define ATF_TP_ADD_TC_HAVE_GPREGS(a,b) ATF_TP_ADD_TC(a,b) +#else +#define ATF_TP_ADD_TC_HAVE_GPREGS(a,b) +#endif + +#if defined(HAVE_FPREGS) +#define ATF_TP_ADD_TC_HAVE_FPREGS(a,b) ATF_TP_ADD_TC(a,b) +#else +#define ATF_TP_ADD_TC_HAVE_FPREGS(a,b) +#endif + +#if defined(HAVE_DBREGS) +#define ATF_TP_ADD_TC_HAVE_DBREGS(a,b) ATF_TP_ADD_TC(a,b) +#else +#define ATF_TP_ADD_TC_HAVE_DBREGS(a,b) +#endif + +#if defined(PT_STEP) +#define ATF_TP_ADD_TC_PT_STEP(a,b) ATF_TP_ADD_TC(a,b) +#else +#define ATF_TP_ADD_TC_PT_STEP(a,b) +#endif diff --git a/lib/libc/sys/t_ptrace_wait.c b/lib/libc/sys/t_ptrace_wait.c new file mode 100644 --- /dev/null +++ b/lib/libc/sys/t_ptrace_wait.c @@ -0,0 +1,178 @@ +/* $NetBSD: t_ptrace_wait.c,v 1.191 2020/05/05 02:06:08 kamil Exp $ */ + +/*- + * Copyright (c) 2016, 2017, 2018, 2019, 2020 The NetBSD Foundation, Inc. + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +__RCSID("$NetBSD: t_ptrace_wait.c,v 1.191 2020/05/05 02:06:08 kamil Exp $"); + +#define __LEGACY_PT_LWPINFO + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(__i386__) || defined(__x86_64__) +#include +#include +#include +#endif + +#include +#include + +#include + +#ifdef ENABLE_TESTS + +/* Assumptions in the kernel code that must be kept. */ +__CTASSERT(sizeof(((struct ptrace_state *)0)->pe_report_event) == + sizeof(((siginfo_t *)0)->si_pe_report_event)); +__CTASSERT(sizeof(((struct ptrace_state *)0)->pe_other_pid) == + sizeof(((siginfo_t *)0)->si_pe_other_pid)); +__CTASSERT(sizeof(((struct ptrace_state *)0)->pe_lwp) == + sizeof(((siginfo_t *)0)->si_pe_lwp)); +__CTASSERT(sizeof(((struct ptrace_state *)0)->pe_other_pid) == + sizeof(((struct ptrace_state *)0)->pe_lwp)); + +#include "h_macros.h" + +#include "t_ptrace_wait.h" +#include "msg.h" + +#define SYSCALL_REQUIRE(expr) ATF_REQUIRE_MSG(expr, "%s: %s", # expr, \ + strerror(errno)) +#define SYSCALL_REQUIRE_ERRNO(res, exp) ATF_REQUIRE_MSG(res == exp, \ + "%d(%s) != %d", res, strerror(res), exp) + +static int debug = 0; + +#define DPRINTF(a, ...) do \ + if (debug) \ + printf("%s() %d.%d %s:%d " a, \ + __func__, getpid(), _lwp_self(), __FILE__, __LINE__, ##__VA_ARGS__); \ + while (/*CONSTCOND*/0) + +/// ---------------------------------------------------------------------------- + +#include "t_ptrace_register_wait.h" +#include "t_ptrace_syscall_wait.h" +#include "t_ptrace_step_wait.h" +#include "t_ptrace_kill_wait.h" +#include "t_ptrace_bytetransfer_wait.h" +#include "t_ptrace_clone_wait.h" +#include "t_ptrace_fork_wait.h" +#include "t_ptrace_signal_wait.h" +#include "t_ptrace_eventmask_wait.h" +#include "t_ptrace_lwp_wait.h" +#include "t_ptrace_exec_wait.h" +#include "t_ptrace_topology_wait.h" +#include "t_ptrace_threads_wait.h" +#include "t_ptrace_siginfo_wait.h" +#include "t_ptrace_core_wait.h" +#include "t_ptrace_misc_wait.h" + +/// ---------------------------------------------------------------------------- + +#include "t_ptrace_amd64_wait.h" +#include "t_ptrace_i386_wait.h" +#include "t_ptrace_x86_wait.h" + +/// ---------------------------------------------------------------------------- + +#else +ATF_TC(dummy); +ATF_TC_HEAD(dummy, tc) +{ + atf_tc_set_md_var(tc, "descr", "A dummy test"); +} + +ATF_TC_BODY(dummy, tc) +{ + + // Dummy, skipped + // The ATF framework requires at least a single defined test. +} +#endif + +ATF_TP_ADD_TCS(tp) +{ + setvbuf(stdout, NULL, _IONBF, 0); + setvbuf(stderr, NULL, _IONBF, 0); + +#ifdef ENABLE_TESTS + ATF_TP_ADD_TCS_PTRACE_WAIT_REGISTER(); + ATF_TP_ADD_TCS_PTRACE_WAIT_SYSCALL(); + ATF_TP_ADD_TCS_PTRACE_WAIT_STEP(); + ATF_TP_ADD_TCS_PTRACE_WAIT_KILL(); + ATF_TP_ADD_TCS_PTRACE_WAIT_BYTETRANSFER(); + ATF_TP_ADD_TCS_PTRACE_WAIT_CLONE(); + ATF_TP_ADD_TCS_PTRACE_WAIT_FORK(); + ATF_TP_ADD_TCS_PTRACE_WAIT_SIGNAL(); + ATF_TP_ADD_TCS_PTRACE_WAIT_EVENTMASK(); + ATF_TP_ADD_TCS_PTRACE_WAIT_LWP(); + ATF_TP_ADD_TCS_PTRACE_WAIT_EXEC(); + ATF_TP_ADD_TCS_PTRACE_WAIT_TOPOLOGY(); + ATF_TP_ADD_TCS_PTRACE_WAIT_THREADS(); + ATF_TP_ADD_TCS_PTRACE_WAIT_SIGINFO(); + ATF_TP_ADD_TCS_PTRACE_WAIT_CORE(); + ATF_TP_ADD_TCS_PTRACE_WAIT_MISC(); + + ATF_TP_ADD_TCS_PTRACE_WAIT_AMD64(); + ATF_TP_ADD_TCS_PTRACE_WAIT_I386(); + ATF_TP_ADD_TCS_PTRACE_WAIT_X86(); + +#else + ATF_TP_ADD_TC(tp, dummy); +#endif + + return atf_no_error(); +} diff --git a/lib/libc/sys/t_ptrace_wait3.c b/lib/libc/sys/t_ptrace_wait3.c new file mode 100644 --- /dev/null +++ b/lib/libc/sys/t_ptrace_wait3.c @@ -0,0 +1,30 @@ +/* $NetBSD: t_ptrace_wait3.c,v 1.3 2020/05/04 23:49:31 kamil Exp $ */ + +/*- + * Copyright (c) 2016, 2017, 2018, 2019, 2020 The NetBSD Foundation, Inc. + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +#define TWAIT_WAIT3 +#include "t_ptrace_wait.c" diff --git a/lib/libc/sys/t_ptrace_wait4.c b/lib/libc/sys/t_ptrace_wait4.c new file mode 100644 --- /dev/null +++ b/lib/libc/sys/t_ptrace_wait4.c @@ -0,0 +1,30 @@ +/* $NetBSD: t_ptrace_wait4.c,v 1.3 2020/05/04 23:49:31 kamil Exp $ */ + +/*- + * Copyright (c) 2016, 2017, 2018, 2019, 2020 The NetBSD Foundation, Inc. + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +#define TWAIT_WAIT4 +#include "t_ptrace_wait.c" diff --git a/lib/libc/sys/t_ptrace_wait6.c b/lib/libc/sys/t_ptrace_wait6.c new file mode 100644 --- /dev/null +++ b/lib/libc/sys/t_ptrace_wait6.c @@ -0,0 +1,30 @@ +/* $NetBSD: t_ptrace_wait6.c,v 1.3 2020/05/04 23:49:31 kamil Exp $ */ + +/*- + * Copyright (c) 2016, 2017, 2018, 2019, 2020 The NetBSD Foundation, Inc. + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +#define TWAIT_WAIT6 +#include "t_ptrace_wait.c" diff --git a/lib/libc/sys/t_ptrace_waitid.c b/lib/libc/sys/t_ptrace_waitid.c new file mode 100644 --- /dev/null +++ b/lib/libc/sys/t_ptrace_waitid.c @@ -0,0 +1,30 @@ +/* $NetBSD: t_ptrace_waitid.c,v 1.3 2020/05/04 23:49:31 kamil Exp $ */ + +/*- + * Copyright (c) 2016, 2017, 2018, 2019, 2020 The NetBSD Foundation, Inc. + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +#define TWAIT_WAITID +#include "t_ptrace_wait.c" diff --git a/lib/libc/sys/t_ptrace_waitpid.c b/lib/libc/sys/t_ptrace_waitpid.c new file mode 100644 --- /dev/null +++ b/lib/libc/sys/t_ptrace_waitpid.c @@ -0,0 +1,30 @@ +/* $NetBSD: t_ptrace_waitpid.c,v 1.3 2020/05/04 23:49:31 kamil Exp $ */ + +/*- + * Copyright (c) 2016, 2017, 2018, 2019, 2020 The NetBSD Foundation, Inc. + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +#define TWAIT_WAITPID +#include "t_ptrace_wait.c" diff --git a/lib/libc/sys/t_ptrace_x86_wait.h b/lib/libc/sys/t_ptrace_x86_wait.h new file mode 100644 --- /dev/null +++ b/lib/libc/sys/t_ptrace_x86_wait.h @@ -0,0 +1,4554 @@ +/* $NetBSD: t_ptrace_x86_wait.h,v 1.31 2020/10/27 08:32:36 mgorny Exp $ */ + +/*- + * Copyright (c) 2016, 2017, 2018, 2019 The NetBSD Foundation, Inc. + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +#if defined(__i386__) || defined(__x86_64__) +union u { + unsigned long raw; + struct { + unsigned long local_dr0_breakpoint : 1; /* 0 */ + unsigned long global_dr0_breakpoint : 1; /* 1 */ + unsigned long local_dr1_breakpoint : 1; /* 2 */ + unsigned long global_dr1_breakpoint : 1; /* 3 */ + unsigned long local_dr2_breakpoint : 1; /* 4 */ + unsigned long global_dr2_breakpoint : 1; /* 5 */ + unsigned long local_dr3_breakpoint : 1; /* 6 */ + unsigned long global_dr3_breakpoint : 1; /* 7 */ + unsigned long local_exact_breakpt : 1; /* 8 */ + unsigned long global_exact_breakpt : 1; /* 9 */ + unsigned long reserved_10 : 1; /* 10 */ + unsigned long rest_trans_memory : 1; /* 11 */ + unsigned long reserved_12 : 1; /* 12 */ + unsigned long general_detect_enable : 1; /* 13 */ + unsigned long reserved_14 : 1; /* 14 */ + unsigned long reserved_15 : 1; /* 15 */ + unsigned long condition_dr0 : 2; /* 16-17 */ + unsigned long len_dr0 : 2; /* 18-19 */ + unsigned long condition_dr1 : 2; /* 20-21 */ + unsigned long len_dr1 : 2; /* 22-23 */ + unsigned long condition_dr2 : 2; /* 24-25 */ + unsigned long len_dr2 : 2; /* 26-27 */ + unsigned long condition_dr3 : 2; /* 28-29 */ + unsigned long len_dr3 : 2; /* 30-31 */ + } bits; +}; + +ATF_TC(dbregs_print); +ATF_TC_HEAD(dbregs_print, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify plain PT_GETDBREGS with printing Debug Registers"); +} + +ATF_TC_BODY(dbregs_print, tc) +{ + const int exitval = 5; + const int sigval = SIGSTOP; + pid_t child, wpid; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + struct dbreg r; + size_t i; + + DPRINTF("Before forking process PID=%d\n", getpid()); + SYSCALL_REQUIRE((child = fork()) != -1); + if (child == 0) { + DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + DPRINTF("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + DPRINTF("Before exiting of the child process\n"); + _exit(exitval); + } + DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + DPRINTF("Call GETDBREGS for the child process\n"); + SYSCALL_REQUIRE(ptrace(PT_GETDBREGS, child, &r, 0) != -1); + + DPRINTF("State of the debug registers:\n"); + for (i = 0; i < __arraycount(r.dr); i++) + DPRINTF("r[%zu]=%" PRIxREGISTER "\n", i, r.dr[i]); + + DPRINTF("Before resuming the child process where it left off and " + "without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_exited(status, exitval); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} + + +enum dbreg_preserve_mode { + dbreg_preserve_mode_none, + dbreg_preserve_mode_yield, + dbreg_preserve_mode_continued +}; + +static void +dbreg_preserve(int reg, enum dbreg_preserve_mode mode) +{ + const int exitval = 5; + const int sigval = SIGSTOP; + pid_t child, wpid; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + struct dbreg r1; + struct dbreg r2; + size_t i; + int watchme; + + if (!can_we_set_dbregs()) { + atf_tc_skip("Either run this test as root or set sysctl(3) " + "security.models.extensions.user_set_dbregs to 1"); + } + + DPRINTF("Before forking process PID=%d\n", getpid()); + SYSCALL_REQUIRE((child = fork()) != -1); + if (child == 0) { + DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + DPRINTF("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + if (mode == dbreg_preserve_mode_continued) { + DPRINTF("Before raising %s from child\n", + strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + } + + DPRINTF("Before exiting of the child process\n"); + _exit(exitval); + } + DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + DPRINTF("Call GETDBREGS for the child process (r1)\n"); + SYSCALL_REQUIRE(ptrace(PT_GETDBREGS, child, &r1, 0) != -1); + + DPRINTF("State of the debug registers (r1):\n"); + for (i = 0; i < __arraycount(r1.dr); i++) + DPRINTF("r1[%zu]=%" PRIxREGISTER "\n", i, r1.dr[i]); + + r1.dr[reg] = (long)(intptr_t)&watchme; + DPRINTF("Set DR0 (r1.dr[%d]) to new value %" PRIxREGISTER "\n", + reg, r1.dr[reg]); + + DPRINTF("New state of the debug registers (r1):\n"); + for (i = 0; i < __arraycount(r1.dr); i++) + DPRINTF("r1[%zu]=%" PRIxREGISTER "\n", i, r1.dr[i]); + + DPRINTF("Call SETDBREGS for the child process (r1)\n"); + SYSCALL_REQUIRE(ptrace(PT_SETDBREGS, child, &r1, 0) != -1); + + switch (mode) { + case dbreg_preserve_mode_none: + break; + case dbreg_preserve_mode_yield: + DPRINTF("Yields a processor voluntarily and gives other " + "threads a chance to run without waiting for an " + "involuntary preemptive switch\n"); + sched_yield(); + break; + case dbreg_preserve_mode_continued: + DPRINTF("Call CONTINUE for the child process\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + break; + } + + DPRINTF("Call GETDBREGS for the child process (r2)\n"); + SYSCALL_REQUIRE(ptrace(PT_GETDBREGS, child, &r2, 0) != -1); + + DPRINTF("Assert that (r1) and (r2) are the same\n"); + SYSCALL_REQUIRE(memcmp(&r1, &r2, sizeof(r1)) == 0); + + DPRINTF("Before resuming the child process where it left off and " + "without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_exited(status, exitval); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} + + +ATF_TC(dbregs_preserve_dr0); +ATF_TC_HEAD(dbregs_preserve_dr0, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that setting DR0 is preserved across ptrace(2) calls"); +} + +ATF_TC_BODY(dbregs_preserve_dr0, tc) +{ + dbreg_preserve(0, dbreg_preserve_mode_none); +} + +ATF_TC(dbregs_preserve_dr1); +ATF_TC_HEAD(dbregs_preserve_dr1, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that setting DR1 is preserved across ptrace(2) calls"); +} + +ATF_TC_BODY(dbregs_preserve_dr1, tc) +{ + dbreg_preserve(1, dbreg_preserve_mode_none); +} + +ATF_TC(dbregs_preserve_dr2); +ATF_TC_HEAD(dbregs_preserve_dr2, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that setting DR2 is preserved across ptrace(2) calls"); +} + +ATF_TC_BODY(dbregs_preserve_dr2, tc) +{ + dbreg_preserve(2, dbreg_preserve_mode_none); +} + +ATF_TC(dbregs_preserve_dr3); +ATF_TC_HEAD(dbregs_preserve_dr3, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that setting DR3 is preserved across ptrace(2) calls"); +} + +ATF_TC_BODY(dbregs_preserve_dr3, tc) +{ + dbreg_preserve(3, dbreg_preserve_mode_none); +} + +ATF_TC(dbregs_preserve_dr0_yield); +ATF_TC_HEAD(dbregs_preserve_dr0_yield, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that setting DR0 is preserved across ptrace(2) calls with " + "scheduler yield"); +} + +ATF_TC_BODY(dbregs_preserve_dr0_yield, tc) +{ + dbreg_preserve(0, dbreg_preserve_mode_yield); +} + +ATF_TC(dbregs_preserve_dr1_yield); +ATF_TC_HEAD(dbregs_preserve_dr1_yield, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that setting DR1 is preserved across ptrace(2) calls with " + "scheduler yield"); +} + +ATF_TC_BODY(dbregs_preserve_dr1_yield, tc) +{ + dbreg_preserve(0, dbreg_preserve_mode_yield); +} + +ATF_TC(dbregs_preserve_dr2_yield); +ATF_TC_HEAD(dbregs_preserve_dr2_yield, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that setting DR2 is preserved across ptrace(2) calls with " + "scheduler yield"); +} + +ATF_TC_BODY(dbregs_preserve_dr2_yield, tc) +{ + dbreg_preserve(0, dbreg_preserve_mode_yield); +} + + +ATF_TC(dbregs_preserve_dr3_yield); +ATF_TC_HEAD(dbregs_preserve_dr3_yield, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that setting DR3 is preserved across ptrace(2) calls with " + "scheduler yield"); +} + +ATF_TC_BODY(dbregs_preserve_dr3_yield, tc) +{ + dbreg_preserve(3, dbreg_preserve_mode_yield); +} + +ATF_TC(dbregs_preserve_dr0_continued); +ATF_TC_HEAD(dbregs_preserve_dr0_continued, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that setting DR0 is preserved across ptrace(2) calls and " + "with continued child"); +} + +ATF_TC_BODY(dbregs_preserve_dr0_continued, tc) +{ + dbreg_preserve(0, dbreg_preserve_mode_continued); +} + +ATF_TC(dbregs_preserve_dr1_continued); +ATF_TC_HEAD(dbregs_preserve_dr1_continued, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that setting DR1 is preserved across ptrace(2) calls and " + "with continued child"); +} + +ATF_TC_BODY(dbregs_preserve_dr1_continued, tc) +{ + dbreg_preserve(1, dbreg_preserve_mode_continued); +} + +ATF_TC(dbregs_preserve_dr2_continued); +ATF_TC_HEAD(dbregs_preserve_dr2_continued, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that setting DR2 is preserved across ptrace(2) calls and " + "with continued child"); +} + +ATF_TC_BODY(dbregs_preserve_dr2_continued, tc) +{ + dbreg_preserve(2, dbreg_preserve_mode_continued); +} + +ATF_TC(dbregs_preserve_dr3_continued); +ATF_TC_HEAD(dbregs_preserve_dr3_continued, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that setting DR3 is preserved across ptrace(2) calls and " + "with continued child"); +} + +ATF_TC_BODY(dbregs_preserve_dr3_continued, tc) +{ + dbreg_preserve(3, dbreg_preserve_mode_continued); +} + + +static void +dbregs_trap_variable(int reg, int cond, int len, bool write) +{ + const int exitval = 5; + const int sigval = SIGSTOP; + pid_t child, wpid; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + struct dbreg r1; + size_t i; + volatile int watchme = 0; + union u dr7; + + struct ptrace_siginfo info; + memset(&info, 0, sizeof(info)); + + if (!can_we_set_dbregs()) { + atf_tc_skip("Either run this test as root or set sysctl(3) " + "security.models.extensions.user_set_dbregs to 1"); + } + + dr7.raw = 0; + switch (reg) { + case 0: + dr7.bits.global_dr0_breakpoint = 1; + dr7.bits.condition_dr0 = cond; + dr7.bits.len_dr0 = len; + break; + case 1: + dr7.bits.global_dr1_breakpoint = 1; + dr7.bits.condition_dr1 = cond; + dr7.bits.len_dr1 = len; + break; + case 2: + dr7.bits.global_dr2_breakpoint = 1; + dr7.bits.condition_dr2 = cond; + dr7.bits.len_dr2 = len; + break; + case 3: + dr7.bits.global_dr3_breakpoint = 1; + dr7.bits.condition_dr3 = cond; + dr7.bits.len_dr3 = len; + break; + } + + DPRINTF("Before forking process PID=%d\n", getpid()); + SYSCALL_REQUIRE((child = fork()) != -1); + if (child == 0) { + DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + DPRINTF("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + if (write) + watchme = 1; + else + printf("watchme=%d\n", watchme); + + DPRINTF("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + DPRINTF("Before exiting of the child process\n"); + _exit(exitval); + } + DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + DPRINTF("Call GETDBREGS for the child process (r1)\n"); + SYSCALL_REQUIRE(ptrace(PT_GETDBREGS, child, &r1, 0) != -1); + + DPRINTF("State of the debug registers (r1):\n"); + for (i = 0; i < __arraycount(r1.dr); i++) + DPRINTF("r1[%zu]=%" PRIxREGISTER "\n", i, r1.dr[i]); + + r1.dr[reg] = (long)(intptr_t)&watchme; + DPRINTF("Set DR%d (r1.dr[%d]) to new value %" PRIxREGISTER "\n", + reg, reg, r1.dr[reg]); + + r1.dr[7] = dr7.raw; + DPRINTF("Set DR7 (r1.dr[7]) to new value %" PRIxREGISTER "\n", + r1.dr[7]); + + DPRINTF("New state of the debug registers (r1):\n"); + for (i = 0; i < __arraycount(r1.dr); i++) + DPRINTF("r1[%zu]=%" PRIxREGISTER "\n", i, r1.dr[i]); + + DPRINTF("Call SETDBREGS for the child process (r1)\n"); + SYSCALL_REQUIRE(ptrace(PT_SETDBREGS, child, &r1, 0) != -1); + + DPRINTF("Call CONTINUE for the child process\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, SIGTRAP); + + DPRINTF("Before calling ptrace(2) with PT_GET_SIGINFO for child\n"); + SYSCALL_REQUIRE(ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1); + + DPRINTF("Signal traced to lwpid=%d\n", info.psi_lwpid); + DPRINTF("Signal properties: si_signo=%#x si_code=%#x si_errno=%#x\n", + info.psi_siginfo.si_signo, info.psi_siginfo.si_code, + info.psi_siginfo.si_errno); + + DPRINTF("Before checking siginfo_t\n"); + ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, SIGTRAP); + ATF_REQUIRE_EQ(info.psi_siginfo.si_code, TRAP_DBREG); + + DPRINTF("Call CONTINUE for the child process\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + DPRINTF("Before resuming the child process where it left off and " + "without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_exited(status, exitval); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} + +ATF_TC(dbregs_dr0_trap_variable_writeonly_byte); +ATF_TC_HEAD(dbregs_dr0_trap_variable_writeonly_byte, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that setting trap with DR0 triggers SIGTRAP " + "(break on data writes only and 1 byte mode)"); +} + +ATF_TC_BODY(dbregs_dr0_trap_variable_writeonly_byte, tc) +{ + /* 0b01 -- break on data write only */ + /* 0b00 -- 1 byte */ + + dbregs_trap_variable(0, 1, 0, true); +} + +ATF_TC(dbregs_dr1_trap_variable_writeonly_byte); +ATF_TC_HEAD(dbregs_dr1_trap_variable_writeonly_byte, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that setting trap with DR1 triggers SIGTRAP " + "(break on data writes only and 1 byte mode)"); +} + +ATF_TC_BODY(dbregs_dr1_trap_variable_writeonly_byte, tc) +{ + /* 0b01 -- break on data write only */ + /* 0b00 -- 1 byte */ + + dbregs_trap_variable(1, 1, 0, true); +} + +ATF_TC(dbregs_dr2_trap_variable_writeonly_byte); +ATF_TC_HEAD(dbregs_dr2_trap_variable_writeonly_byte, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that setting trap with DR2 triggers SIGTRAP " + "(break on data writes only and 1 byte mode)"); +} + +ATF_TC_BODY(dbregs_dr2_trap_variable_writeonly_byte, tc) +{ + /* 0b01 -- break on data write only */ + /* 0b00 -- 1 byte */ + + dbregs_trap_variable(2, 1, 0, true); +} + +ATF_TC(dbregs_dr3_trap_variable_writeonly_byte); +ATF_TC_HEAD(dbregs_dr3_trap_variable_writeonly_byte, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that setting trap with DR3 triggers SIGTRAP " + "(break on data writes only and 1 byte mode)"); +} + +ATF_TC_BODY(dbregs_dr3_trap_variable_writeonly_byte, tc) +{ + /* 0b01 -- break on data write only */ + /* 0b00 -- 1 byte */ + + dbregs_trap_variable(3, 1, 0, true); +} + +ATF_TC(dbregs_dr0_trap_variable_writeonly_2bytes); +ATF_TC_HEAD(dbregs_dr0_trap_variable_writeonly_2bytes, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that setting trap with DR0 triggers SIGTRAP " + "(break on data writes only and 2 bytes mode)"); +} + +ATF_TC_BODY(dbregs_dr0_trap_variable_writeonly_2bytes, tc) +{ + /* 0b01 -- break on data write only */ + /* 0b01 -- 2 bytes */ + + dbregs_trap_variable(0, 1, 1, true); +} + +ATF_TC(dbregs_dr1_trap_variable_writeonly_2bytes); +ATF_TC_HEAD(dbregs_dr1_trap_variable_writeonly_2bytes, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that setting trap with DR1 triggers SIGTRAP " + "(break on data writes only and 2 bytes mode)"); +} + +ATF_TC_BODY(dbregs_dr1_trap_variable_writeonly_2bytes, tc) +{ + /* 0b01 -- break on data write only */ + /* 0b01 -- 2 bytes */ + + dbregs_trap_variable(1, 1, 1, true); +} + +ATF_TC(dbregs_dr2_trap_variable_writeonly_2bytes); +ATF_TC_HEAD(dbregs_dr2_trap_variable_writeonly_2bytes, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that setting trap with DR2 triggers SIGTRAP " + "(break on data writes only and 2 bytes mode)"); +} + +ATF_TC_BODY(dbregs_dr2_trap_variable_writeonly_2bytes, tc) +{ + /* 0b01 -- break on data write only */ + /* 0b01 -- 2 bytes */ + + dbregs_trap_variable(2, 1, 1, true); +} + +ATF_TC(dbregs_dr3_trap_variable_writeonly_2bytes); +ATF_TC_HEAD(dbregs_dr3_trap_variable_writeonly_2bytes, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that setting trap with DR3 triggers SIGTRAP " + "(break on data writes only and 2 bytes mode)"); +} + +ATF_TC_BODY(dbregs_dr3_trap_variable_writeonly_2bytes, tc) +{ + /* 0b01 -- break on data write only */ + /* 0b01 -- 2 bytes */ + + dbregs_trap_variable(3, 1, 1, true); +} + +ATF_TC(dbregs_dr0_trap_variable_writeonly_4bytes); +ATF_TC_HEAD(dbregs_dr0_trap_variable_writeonly_4bytes, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that setting trap with DR0 triggers SIGTRAP " + "(break on data writes only and 4 bytes mode)"); +} + +ATF_TC_BODY(dbregs_dr0_trap_variable_writeonly_4bytes, tc) +{ + /* 0b01 -- break on data write only */ + /* 0b11 -- 4 bytes */ + + dbregs_trap_variable(0, 1, 3, true); +} + +ATF_TC(dbregs_dr1_trap_variable_writeonly_4bytes); +ATF_TC_HEAD(dbregs_dr1_trap_variable_writeonly_4bytes, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that setting trap with DR1 triggers SIGTRAP " + "(break on data writes only and 4 bytes mode)"); +} + +ATF_TC_BODY(dbregs_dr1_trap_variable_writeonly_4bytes, tc) +{ + /* 0b01 -- break on data write only */ + /* 0b11 -- 4 bytes */ + + dbregs_trap_variable(1, 1, 3, true); +} + +ATF_TC(dbregs_dr2_trap_variable_writeonly_4bytes); +ATF_TC_HEAD(dbregs_dr2_trap_variable_writeonly_4bytes, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that setting trap with DR2 triggers SIGTRAP " + "(break on data writes only and 4 bytes mode)"); +} + +ATF_TC_BODY(dbregs_dr2_trap_variable_writeonly_4bytes, tc) +{ + /* 0b01 -- break on data write only */ + /* 0b11 -- 4 bytes */ + + dbregs_trap_variable(2, 1, 3, true); +} + +ATF_TC(dbregs_dr3_trap_variable_writeonly_4bytes); +ATF_TC_HEAD(dbregs_dr3_trap_variable_writeonly_4bytes, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that setting trap with DR3 triggers SIGTRAP " + "(break on data writes only and 4 bytes mode)"); +} + +ATF_TC_BODY(dbregs_dr3_trap_variable_writeonly_4bytes, tc) +{ + /* 0b01 -- break on data write only */ + /* 0b11 -- 4 bytes */ + + dbregs_trap_variable(3, 1, 3, true); +} + +ATF_TC(dbregs_dr0_trap_variable_readwrite_write_byte); +ATF_TC_HEAD(dbregs_dr0_trap_variable_readwrite_write_byte, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that setting trap with DR0 triggers SIGTRAP " + "(break on data read/write trap in read 1 byte mode)"); +} + +ATF_TC_BODY(dbregs_dr0_trap_variable_readwrite_write_byte, tc) +{ + /* 0b11 -- break on data write&read */ + /* 0b00 -- 1 byte */ + + dbregs_trap_variable(0, 3, 0, true); +} + +ATF_TC(dbregs_dr1_trap_variable_readwrite_write_byte); +ATF_TC_HEAD(dbregs_dr1_trap_variable_readwrite_write_byte, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that setting trap with DR1 triggers SIGTRAP " + "(break on data read/write trap in read 1 byte mode)"); +} + +ATF_TC_BODY(dbregs_dr1_trap_variable_readwrite_write_byte, tc) +{ + /* 0b11 -- break on data write&read */ + /* 0b00 -- 1 byte */ + + dbregs_trap_variable(1, 3, 0, true); +} + +ATF_TC(dbregs_dr2_trap_variable_readwrite_write_byte); +ATF_TC_HEAD(dbregs_dr2_trap_variable_readwrite_write_byte, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that setting trap with DR2 triggers SIGTRAP " + "(break on data read/write trap in read 1 byte mode)"); +} + +ATF_TC_BODY(dbregs_dr2_trap_variable_readwrite_write_byte, tc) +{ + /* 0b11 -- break on data write&read */ + /* 0b00 -- 1 byte */ + + dbregs_trap_variable(2, 3, 0, true); +} + +ATF_TC(dbregs_dr3_trap_variable_readwrite_write_byte); +ATF_TC_HEAD(dbregs_dr3_trap_variable_readwrite_write_byte, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that setting trap with DR3 triggers SIGTRAP " + "(break on data read/write trap in read 1 byte mode)"); +} + +ATF_TC_BODY(dbregs_dr3_trap_variable_readwrite_write_byte, tc) +{ + /* 0b11 -- break on data write&read */ + /* 0b00 -- 1 byte */ + + dbregs_trap_variable(3, 3, 0, true); +} + +ATF_TC(dbregs_dr0_trap_variable_readwrite_write_2bytes); +ATF_TC_HEAD(dbregs_dr0_trap_variable_readwrite_write_2bytes, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that setting trap with DR0 triggers SIGTRAP " + "(break on data read/write trap in read 2 bytes mode)"); +} + +ATF_TC_BODY(dbregs_dr0_trap_variable_readwrite_write_2bytes, tc) +{ + /* 0b11 -- break on data write&read */ + /* 0b01 -- 2 bytes */ + + dbregs_trap_variable(0, 3, 1, true); +} + +ATF_TC(dbregs_dr1_trap_variable_readwrite_write_2bytes); +ATF_TC_HEAD(dbregs_dr1_trap_variable_readwrite_write_2bytes, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that setting trap with DR1 triggers SIGTRAP " + "(break on data read/write trap in read 2 bytes mode)"); +} + +ATF_TC_BODY(dbregs_dr1_trap_variable_readwrite_write_2bytes, tc) +{ + /* 0b11 -- break on data write&read */ + /* 0b01 -- 2 bytes */ + + dbregs_trap_variable(1, 3, 1, true); +} + +ATF_TC(dbregs_dr2_trap_variable_readwrite_write_2bytes); +ATF_TC_HEAD(dbregs_dr2_trap_variable_readwrite_write_2bytes, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that setting trap with DR2 triggers SIGTRAP " + "(break on data read/write trap in read 2 bytes mode)"); +} + +ATF_TC_BODY(dbregs_dr2_trap_variable_readwrite_write_2bytes, tc) +{ + /* 0b11 -- break on data write&read */ + /* 0b01 -- 2 bytes */ + + dbregs_trap_variable(2, 3, 1, true); +} + +ATF_TC(dbregs_dr3_trap_variable_readwrite_write_2bytes); +ATF_TC_HEAD(dbregs_dr3_trap_variable_readwrite_write_2bytes, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that setting trap with DR3 triggers SIGTRAP " + "(break on data read/write trap in read 2 bytes mode)"); +} + +ATF_TC_BODY(dbregs_dr3_trap_variable_readwrite_write_2bytes, tc) +{ + /* 0b11 -- break on data write&read */ + /* 0b01 -- 2 bytes */ + + dbregs_trap_variable(3, 3, 1, true); +} + +ATF_TC(dbregs_dr0_trap_variable_readwrite_write_4bytes); +ATF_TC_HEAD(dbregs_dr0_trap_variable_readwrite_write_4bytes, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that setting trap with DR0 triggers SIGTRAP " + "(break on data read/write trap in read 4 bytes mode)"); +} + +ATF_TC_BODY(dbregs_dr0_trap_variable_readwrite_write_4bytes, tc) +{ + /* 0b11 -- break on data write&read */ + /* 0b11 -- 4 bytes */ + + dbregs_trap_variable(0, 3, 3, true); +} + +ATF_TC(dbregs_dr1_trap_variable_readwrite_write_4bytes); +ATF_TC_HEAD(dbregs_dr1_trap_variable_readwrite_write_4bytes, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that setting trap with DR1 triggers SIGTRAP " + "(break on data read/write trap in read 4 bytes mode)"); +} + +ATF_TC_BODY(dbregs_dr1_trap_variable_readwrite_write_4bytes, tc) +{ + /* 0b11 -- break on data write&read */ + /* 0b11 -- 4 bytes */ + + dbregs_trap_variable(1, 3, 3, true); +} + +ATF_TC(dbregs_dr2_trap_variable_readwrite_write_4bytes); +ATF_TC_HEAD(dbregs_dr2_trap_variable_readwrite_write_4bytes, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that setting trap with DR2 triggers SIGTRAP " + "(break on data read/write trap in read 4 bytes mode)"); +} + +ATF_TC_BODY(dbregs_dr2_trap_variable_readwrite_write_4bytes, tc) +{ + /* 0b11 -- break on data write&read */ + /* 0b11 -- 4 bytes */ + + dbregs_trap_variable(2, 3, 3, true); +} + +ATF_TC(dbregs_dr3_trap_variable_readwrite_write_4bytes); +ATF_TC_HEAD(dbregs_dr3_trap_variable_readwrite_write_4bytes, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that setting trap with DR3 triggers SIGTRAP " + "(break on data read/write trap in read 4 bytes mode)"); +} + +ATF_TC_BODY(dbregs_dr3_trap_variable_readwrite_write_4bytes, tc) +{ + /* 0b11 -- break on data write&read */ + /* 0b11 -- 4 bytes */ + + dbregs_trap_variable(3, 3, 3, true); +} + +ATF_TC(dbregs_dr0_trap_variable_readwrite_read_byte); +ATF_TC_HEAD(dbregs_dr0_trap_variable_readwrite_read_byte, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that setting trap with DR0 triggers SIGTRAP " + "(break on data read/write trap in write 1 byte mode)"); +} + +ATF_TC_BODY(dbregs_dr0_trap_variable_readwrite_read_byte, tc) +{ + /* 0b11 -- break on data write&read */ + /* 0b00 -- 1 byte */ + + dbregs_trap_variable(0, 3, 0, false); +} + +ATF_TC(dbregs_dr1_trap_variable_readwrite_read_byte); +ATF_TC_HEAD(dbregs_dr1_trap_variable_readwrite_read_byte, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that setting trap with DR1 triggers SIGTRAP " + "(break on data read/write trap in write 1 byte mode)"); +} + +ATF_TC_BODY(dbregs_dr1_trap_variable_readwrite_read_byte, tc) +{ + /* 0b11 -- break on data write&read */ + /* 0b00 -- 1 byte */ + + dbregs_trap_variable(1, 3, 0, false); +} + +ATF_TC(dbregs_dr2_trap_variable_readwrite_read_byte); +ATF_TC_HEAD(dbregs_dr2_trap_variable_readwrite_read_byte, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that setting trap with DR2 triggers SIGTRAP " + "(break on data read/write trap in write 1 byte mode)"); +} + +ATF_TC_BODY(dbregs_dr2_trap_variable_readwrite_read_byte, tc) +{ + /* 0b11 -- break on data write&read */ + /* 0b00 -- 1 byte */ + + dbregs_trap_variable(2, 3, 0, false); +} + +ATF_TC(dbregs_dr3_trap_variable_readwrite_read_byte); +ATF_TC_HEAD(dbregs_dr3_trap_variable_readwrite_read_byte, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that setting trap with DR3 triggers SIGTRAP " + "(break on data read/write trap in write 1 byte mode)"); +} + +ATF_TC_BODY(dbregs_dr3_trap_variable_readwrite_read_byte, tc) +{ + /* 0b11 -- break on data write&read */ + /* 0b00 -- 1 byte */ + + dbregs_trap_variable(3, 3, 0, false); +} + +ATF_TC(dbregs_dr0_trap_variable_readwrite_read_2bytes); +ATF_TC_HEAD(dbregs_dr0_trap_variable_readwrite_read_2bytes, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that setting trap with DR0 triggers SIGTRAP " + "(break on data read/write trap in write 2 bytes mode)"); +} + +ATF_TC_BODY(dbregs_dr0_trap_variable_readwrite_read_2bytes, tc) +{ + /* 0b11 -- break on data write&read */ + /* 0b01 -- 2 bytes */ + + dbregs_trap_variable(0, 3, 1, false); +} + +ATF_TC(dbregs_dr1_trap_variable_readwrite_read_2bytes); +ATF_TC_HEAD(dbregs_dr1_trap_variable_readwrite_read_2bytes, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that setting trap with DR1 triggers SIGTRAP " + "(break on data read/write trap in write 2 bytes mode)"); +} + +ATF_TC_BODY(dbregs_dr1_trap_variable_readwrite_read_2bytes, tc) +{ + /* 0b11 -- break on data write&read */ + /* 0b01 -- 2 bytes */ + + dbregs_trap_variable(1, 3, 1, false); +} + +ATF_TC(dbregs_dr2_trap_variable_readwrite_read_2bytes); +ATF_TC_HEAD(dbregs_dr2_trap_variable_readwrite_read_2bytes, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that setting trap with DR2 triggers SIGTRAP " + "(break on data read/write trap in write 2 bytes mode)"); +} + +ATF_TC_BODY(dbregs_dr2_trap_variable_readwrite_read_2bytes, tc) +{ + /* 0b11 -- break on data write&read */ + /* 0b01 -- 2 bytes */ + + dbregs_trap_variable(2, 3, 1, false); +} + +ATF_TC(dbregs_dr3_trap_variable_readwrite_read_2bytes); +ATF_TC_HEAD(dbregs_dr3_trap_variable_readwrite_read_2bytes, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that setting trap with DR3 triggers SIGTRAP " + "(break on data read/write trap in write 2 bytes mode)"); +} + +ATF_TC_BODY(dbregs_dr3_trap_variable_readwrite_read_2bytes, tc) +{ + /* 0b11 -- break on data write&read */ + /* 0b01 -- 2 bytes */ + + dbregs_trap_variable(3, 3, 1, false); +} + +ATF_TC(dbregs_dr0_trap_variable_readwrite_read_4bytes); +ATF_TC_HEAD(dbregs_dr0_trap_variable_readwrite_read_4bytes, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that setting trap with DR0 triggers SIGTRAP " + "(break on data read/write trap in write 4 bytes mode)"); +} + +ATF_TC_BODY(dbregs_dr0_trap_variable_readwrite_read_4bytes, tc) +{ + /* 0b11 -- break on data write&read */ + /* 0b11 -- 4 bytes */ + + dbregs_trap_variable(0, 3, 3, false); +} + +ATF_TC(dbregs_dr1_trap_variable_readwrite_read_4bytes); +ATF_TC_HEAD(dbregs_dr1_trap_variable_readwrite_read_4bytes, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that setting trap with DR1 triggers SIGTRAP " + "(break on data read/write trap in write 4 bytes mode)"); +} + +ATF_TC_BODY(dbregs_dr1_trap_variable_readwrite_read_4bytes, tc) +{ + /* 0b11 -- break on data write&read */ + /* 0b11 -- 4 bytes */ + + dbregs_trap_variable(1, 3, 3, false); +} + +ATF_TC(dbregs_dr2_trap_variable_readwrite_read_4bytes); +ATF_TC_HEAD(dbregs_dr2_trap_variable_readwrite_read_4bytes, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that setting trap with DR2 triggers SIGTRAP " + "(break on data read/write trap in write 4 bytes mode)"); +} + +ATF_TC_BODY(dbregs_dr2_trap_variable_readwrite_read_4bytes, tc) +{ + /* 0b11 -- break on data write&read */ + /* 0b11 -- 4 bytes */ + + dbregs_trap_variable(2, 3, 3, false); +} + +ATF_TC(dbregs_dr3_trap_variable_readwrite_read_4bytes); +ATF_TC_HEAD(dbregs_dr3_trap_variable_readwrite_read_4bytes, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that setting trap with DR3 triggers SIGTRAP " + "(break on data read/write trap in write 4 bytes mode)"); +} + +ATF_TC_BODY(dbregs_dr3_trap_variable_readwrite_read_4bytes, tc) +{ + /* 0b11 -- break on data write&read */ + /* 0b11 -- 4 bytes */ + + dbregs_trap_variable(3, 3, 3, false); +} + +#if defined(HAVE_DBREGS) +ATF_TC(dbregs_dr0_trap_code); +ATF_TC_HEAD(dbregs_dr0_trap_code, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that setting trap with DR0 triggers SIGTRAP " + "(break on code execution trap)"); +} + +ATF_TC_BODY(dbregs_dr0_trap_code, tc) +{ + const int exitval = 5; + const int sigval = SIGSTOP; + pid_t child, wpid; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + struct dbreg r1; + size_t i; + volatile int watchme = 1; + union u dr7; + + struct ptrace_siginfo info; + memset(&info, 0, sizeof(info)); + + if (!can_we_set_dbregs()) { + atf_tc_skip("Either run this test as root or set sysctl(3) " + "security.models.extensions.user_set_dbregs to 1"); + } + + dr7.raw = 0; + dr7.bits.global_dr0_breakpoint = 1; + dr7.bits.condition_dr0 = 0; /* 0b00 -- break on code execution */ + dr7.bits.len_dr0 = 0; /* 0b00 -- 1 byte */ + + DPRINTF("Before forking process PID=%d\n", getpid()); + SYSCALL_REQUIRE((child = fork()) != -1); + if (child == 0) { + DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + DPRINTF("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + printf("check_happy(%d)=%d\n", watchme, check_happy(watchme)); + + DPRINTF("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + DPRINTF("Before exiting of the child process\n"); + _exit(exitval); + } + DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + DPRINTF("Call GETDBREGS for the child process (r1)\n"); + SYSCALL_REQUIRE(ptrace(PT_GETDBREGS, child, &r1, 0) != -1); + + DPRINTF("State of the debug registers (r1):\n"); + for (i = 0; i < __arraycount(r1.dr); i++) + DPRINTF("r1[%zu]=%" PRIxREGISTER "\n", i, r1.dr[i]); + + r1.dr[0] = (long)(intptr_t)check_happy; + DPRINTF("Set DR0 (r1.dr[0]) to new value %" PRIxREGISTER "\n", + r1.dr[0]); + + r1.dr[7] = dr7.raw; + DPRINTF("Set DR7 (r1.dr[7]) to new value %" PRIxREGISTER "\n", + r1.dr[7]); + + DPRINTF("New state of the debug registers (r1):\n"); + for (i = 0; i < __arraycount(r1.dr); i++) + DPRINTF("r1[%zu]=%" PRIxREGISTER "\n", i, r1.dr[i]); + + DPRINTF("Call SETDBREGS for the child process (r1)\n"); + SYSCALL_REQUIRE(ptrace(PT_SETDBREGS, child, &r1, 0) != -1); + + DPRINTF("Call CONTINUE for the child process\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, SIGTRAP); + + DPRINTF("Before calling ptrace(2) with PT_GET_SIGINFO for child\n"); + SYSCALL_REQUIRE(ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1); + + DPRINTF("Signal traced to lwpid=%d\n", info.psi_lwpid); + DPRINTF("Signal properties: si_signo=%#x si_code=%#x si_errno=%#x\n", + info.psi_siginfo.si_signo, info.psi_siginfo.si_code, + info.psi_siginfo.si_errno); + + DPRINTF("Before checking siginfo_t\n"); + ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, SIGTRAP); + ATF_REQUIRE_EQ(info.psi_siginfo.si_code, TRAP_DBREG); + + DPRINTF("Remove code trap from check_happy=%p\n", check_happy); + dr7.bits.global_dr0_breakpoint = 0; + r1.dr[7] = dr7.raw; + DPRINTF("Set DR7 (r1.dr[7]) to new value %" PRIxREGISTER "\n", + r1.dr[7]); + + DPRINTF("Call SETDBREGS for the child process (r1)\n"); + SYSCALL_REQUIRE(ptrace(PT_SETDBREGS, child, &r1, 0) != -1); + + DPRINTF("Call CONTINUE for the child process\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + DPRINTF("Before resuming the child process where it left off and " + "without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_exited(status, exitval); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} +#endif + +#if defined(HAVE_DBREGS) +ATF_TC(dbregs_dr1_trap_code); +ATF_TC_HEAD(dbregs_dr1_trap_code, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that setting trap with DR1 triggers SIGTRAP " + "(break on code execution trap)"); +} + +ATF_TC_BODY(dbregs_dr1_trap_code, tc) +{ + const int exitval = 5; + const int sigval = SIGSTOP; + pid_t child, wpid; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + struct dbreg r1; + size_t i; + volatile int watchme = 1; + union u dr7; + + struct ptrace_siginfo info; + memset(&info, 0, sizeof(info)); + + if (!can_we_set_dbregs()) { + atf_tc_skip("Either run this test as root or set sysctl(3) " + "security.models.extensions.user_set_dbregs to 1"); + } + + dr7.raw = 0; + dr7.bits.global_dr1_breakpoint = 1; + dr7.bits.condition_dr1 = 0; /* 0b00 -- break on code execution */ + dr7.bits.len_dr1 = 0; /* 0b00 -- 1 byte */ + + DPRINTF("Before forking process PID=%d\n", getpid()); + SYSCALL_REQUIRE((child = fork()) != -1); + if (child == 0) { + DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + DPRINTF("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + printf("check_happy(%d)=%d\n", watchme, check_happy(watchme)); + + DPRINTF("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + DPRINTF("Before exiting of the child process\n"); + _exit(exitval); + } + DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + DPRINTF("Call GETDBREGS for the child process (r1)\n"); + SYSCALL_REQUIRE(ptrace(PT_GETDBREGS, child, &r1, 0) != -1); + + DPRINTF("State of the debug registers (r1):\n"); + for (i = 0; i < __arraycount(r1.dr); i++) + DPRINTF("r1[%zu]=%" PRIxREGISTER "\n", i, r1.dr[i]); + + r1.dr[1] = (long)(intptr_t)check_happy; + DPRINTF("Set DR1 (r1.dr[1]) to new value %" PRIxREGISTER "\n", + r1.dr[1]); + + r1.dr[7] = dr7.raw; + DPRINTF("Set DR7 (r1.dr[7]) to new value %" PRIxREGISTER "\n", + r1.dr[7]); + + DPRINTF("New state of the debug registers (r1):\n"); + for (i = 0; i < __arraycount(r1.dr); i++) + DPRINTF("r1[%zu]=%" PRIxREGISTER "\n", i, r1.dr[i]); + + DPRINTF("Call SETDBREGS for the child process (r1)\n"); + SYSCALL_REQUIRE(ptrace(PT_SETDBREGS, child, &r1, 0) != -1); + + DPRINTF("Call CONTINUE for the child process\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, SIGTRAP); + + DPRINTF("Before calling ptrace(2) with PT_GET_SIGINFO for child\n"); + SYSCALL_REQUIRE(ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1); + + DPRINTF("Signal traced to lwpid=%d\n", info.psi_lwpid); + DPRINTF("Signal properties: si_signo=%#x si_code=%#x si_errno=%#x\n", + info.psi_siginfo.si_signo, info.psi_siginfo.si_code, + info.psi_siginfo.si_errno); + + DPRINTF("Before checking siginfo_t\n"); + ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, SIGTRAP); + ATF_REQUIRE_EQ(info.psi_siginfo.si_code, TRAP_DBREG); + + DPRINTF("Remove code trap from check_happy=%p\n", check_happy); + dr7.bits.global_dr1_breakpoint = 0; + r1.dr[7] = dr7.raw; + DPRINTF("Set DR7 (r1.dr[7]) to new value %" PRIxREGISTER "\n", + r1.dr[7]); + + DPRINTF("Call SETDBREGS for the child process (r1)\n"); + SYSCALL_REQUIRE(ptrace(PT_SETDBREGS, child, &r1, 0) != -1); + + DPRINTF("Call CONTINUE for the child process\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + DPRINTF("Before resuming the child process where it left off and " + "without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_exited(status, exitval); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} +#endif + +#if defined(HAVE_DBREGS) +ATF_TC(dbregs_dr2_trap_code); +ATF_TC_HEAD(dbregs_dr2_trap_code, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that setting trap with DR2 triggers SIGTRAP " + "(break on code execution trap)"); +} + +ATF_TC_BODY(dbregs_dr2_trap_code, tc) +{ + const int exitval = 5; + const int sigval = SIGSTOP; + pid_t child, wpid; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + struct dbreg r1; + size_t i; + volatile int watchme = 1; + union u dr7; + + struct ptrace_siginfo info; + memset(&info, 0, sizeof(info)); + + if (!can_we_set_dbregs()) { + atf_tc_skip("Either run this test as root or set sysctl(3) " + "security.models.extensions.user_set_dbregs to 1"); + } + + dr7.raw = 0; + dr7.bits.global_dr2_breakpoint = 1; + dr7.bits.condition_dr2 = 0; /* 0b00 -- break on code execution */ + dr7.bits.len_dr2 = 0; /* 0b00 -- 1 byte */ + + DPRINTF("Before forking process PID=%d\n", getpid()); + SYSCALL_REQUIRE((child = fork()) != -1); + if (child == 0) { + DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + DPRINTF("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + printf("check_happy(%d)=%d\n", watchme, check_happy(watchme)); + + DPRINTF("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + DPRINTF("Before exiting of the child process\n"); + _exit(exitval); + } + DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + DPRINTF("Call GETDBREGS for the child process (r1)\n"); + SYSCALL_REQUIRE(ptrace(PT_GETDBREGS, child, &r1, 0) != -1); + + DPRINTF("State of the debug registers (r1):\n"); + for (i = 0; i < __arraycount(r1.dr); i++) + DPRINTF("r1[%zu]=%" PRIxREGISTER "\n", i, r1.dr[i]); + + r1.dr[2] = (long)(intptr_t)check_happy; + DPRINTF("Set DR2 (r1.dr[2]) to new value %" PRIxREGISTER "\n", + r1.dr[2]); + + r1.dr[7] = dr7.raw; + DPRINTF("Set DR7 (r1.dr[7]) to new value %" PRIxREGISTER "\n", + r1.dr[7]); + + DPRINTF("New state of the debug registers (r1):\n"); + for (i = 0; i < __arraycount(r1.dr); i++) + DPRINTF("r1[%zu]=%" PRIxREGISTER "\n", i, r1.dr[i]); + + DPRINTF("Call SETDBREGS for the child process (r1)\n"); + SYSCALL_REQUIRE(ptrace(PT_SETDBREGS, child, &r1, 0) != -1); + + DPRINTF("Call CONTINUE for the child process\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, SIGTRAP); + + DPRINTF("Before calling ptrace(2) with PT_GET_SIGINFO for child\n"); + SYSCALL_REQUIRE(ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1); + + DPRINTF("Signal traced to lwpid=%d\n", info.psi_lwpid); + DPRINTF("Signal properties: si_signo=%#x si_code=%#x si_errno=%#x\n", + info.psi_siginfo.si_signo, info.psi_siginfo.si_code, + info.psi_siginfo.si_errno); + + DPRINTF("Before checking siginfo_t\n"); + ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, SIGTRAP); + ATF_REQUIRE_EQ(info.psi_siginfo.si_code, TRAP_DBREG); + + DPRINTF("Remove code trap from check_happy=%p\n", check_happy); + dr7.bits.global_dr2_breakpoint = 0; + r1.dr[7] = dr7.raw; + DPRINTF("Set DR7 (r1.dr[7]) to new value %" PRIxREGISTER "\n", + r1.dr[7]); + + DPRINTF("Call SETDBREGS for the child process (r1)\n"); + SYSCALL_REQUIRE(ptrace(PT_SETDBREGS, child, &r1, 0) != -1); + + DPRINTF("Call CONTINUE for the child process\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + DPRINTF("Before resuming the child process where it left off and " + "without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_exited(status, exitval); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} +#endif + +#if defined(HAVE_DBREGS) +ATF_TC(dbregs_dr3_trap_code); +ATF_TC_HEAD(dbregs_dr3_trap_code, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that setting trap with DR3 triggers SIGTRAP " + "(break on code execution trap)"); +} + +ATF_TC_BODY(dbregs_dr3_trap_code, tc) +{ + const int exitval = 5; + const int sigval = SIGSTOP; + pid_t child, wpid; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + struct dbreg r1; + size_t i; + volatile int watchme = 1; + union u dr7; + + struct ptrace_siginfo info; + memset(&info, 0, sizeof(info)); + + if (!can_we_set_dbregs()) { + atf_tc_skip("Either run this test as root or set sysctl(3) " + "security.models.extensions.user_set_dbregs to 1"); + } + + dr7.raw = 0; + dr7.bits.global_dr3_breakpoint = 1; + dr7.bits.condition_dr3 = 0; /* 0b00 -- break on code execution */ + dr7.bits.len_dr3 = 0; /* 0b00 -- 1 byte */ + + DPRINTF("Before forking process PID=%d\n", getpid()); + SYSCALL_REQUIRE((child = fork()) != -1); + if (child == 0) { + DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + DPRINTF("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + printf("check_happy(%d)=%d\n", watchme, check_happy(watchme)); + + DPRINTF("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + DPRINTF("Before exiting of the child process\n"); + _exit(exitval); + } + DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + DPRINTF("Call GETDBREGS for the child process (r1)\n"); + SYSCALL_REQUIRE(ptrace(PT_GETDBREGS, child, &r1, 0) != -1); + + DPRINTF("State of the debug registers (r1):\n"); + for (i = 0; i < __arraycount(r1.dr); i++) + DPRINTF("r1[%zu]=%" PRIxREGISTER "\n", i, r1.dr[i]); + + r1.dr[3] = (long)(intptr_t)check_happy; + DPRINTF("Set DR3 (r1.dr[3]) to new value %" PRIxREGISTER "\n", + r1.dr[3]); + + r1.dr[7] = dr7.raw; + DPRINTF("Set DR7 (r1.dr[7]) to new value %" PRIxREGISTER "\n", + r1.dr[7]); + + DPRINTF("New state of the debug registers (r1):\n"); + for (i = 0; i < __arraycount(r1.dr); i++) + DPRINTF("r1[%zu]=%" PRIxREGISTER "\n", i, r1.dr[i]); + + DPRINTF("Call SETDBREGS for the child process (r1)\n"); + SYSCALL_REQUIRE(ptrace(PT_SETDBREGS, child, &r1, 0) != -1); + + DPRINTF("Call CONTINUE for the child process\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, SIGTRAP); + + DPRINTF("Before calling ptrace(2) with PT_GET_SIGINFO for child\n"); + SYSCALL_REQUIRE(ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1); + + DPRINTF("Signal traced to lwpid=%d\n", info.psi_lwpid); + DPRINTF("Signal properties: si_signo=%#x si_code=%#x si_errno=%#x\n", + info.psi_siginfo.si_signo, info.psi_siginfo.si_code, + info.psi_siginfo.si_errno); + + DPRINTF("Before checking siginfo_t\n"); + ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, SIGTRAP); + ATF_REQUIRE_EQ(info.psi_siginfo.si_code, TRAP_DBREG); + + DPRINTF("Remove code trap from check_happy=%p\n", check_happy); + dr7.bits.global_dr3_breakpoint = 0; + r1.dr[7] = dr7.raw; + DPRINTF("Set DR7 (r1.dr[7]) to new value %" PRIxREGISTER "\n", + r1.dr[7]); + + DPRINTF("Call SETDBREGS for the child process (r1)\n"); + SYSCALL_REQUIRE(ptrace(PT_SETDBREGS, child, &r1, 0) != -1); + + DPRINTF("Call CONTINUE for the child process\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + DPRINTF("Before resuming the child process where it left off and " + "without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_exited(status, exitval); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} +#endif + +static void * __used +x86_main_func(void *arg) +{ + + return arg; +} + +static void +dbregs_dont_inherit_lwp(int reg) +{ + const int exitval = 5; + const int sigval = SIGSTOP; + pid_t child, wpid; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + ptrace_state_t state; + const int slen = sizeof(state); + ptrace_event_t event; + const int elen = sizeof(event); + pthread_t t; + lwpid_t lid; + size_t i; + struct dbreg r1; + struct dbreg r2; + + if (!can_we_set_dbregs()) { + atf_tc_skip("Either run this test as root or set sysctl(3) " + "security.models.extensions.user_set_dbregs to 1"); + } + + DPRINTF("Before forking process PID=%d\n", getpid()); + SYSCALL_REQUIRE((child = fork()) != -1); + if (child == 0) { + DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + DPRINTF("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + FORKEE_ASSERT(!pthread_create(&t, NULL, x86_main_func, NULL)); + + DPRINTF("Before waiting for thread to exit\n"); + FORKEE_ASSERT(!pthread_join(t, NULL)); + + DPRINTF("Before exiting of the child process\n"); + _exit(exitval); + } + DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + DPRINTF("Set empty EVENT_MASK for the child %d\n", child); + event.pe_set_event = PTRACE_LWP_CREATE; + SYSCALL_REQUIRE(ptrace(PT_SET_EVENT_MASK, child, &event, elen) != -1); + + DPRINTF("Call GETDBREGS for the child process (r1)\n"); + SYSCALL_REQUIRE(ptrace(PT_GETDBREGS, child, &r1, 0) != -1); + + DPRINTF("State of the debug registers (r1):\n"); + for (i = 0; i < __arraycount(r1.dr); i++) + DPRINTF("r1[%zu]=%" PRIxREGISTER "\n", i, r1.dr[i]); + + r1.dr[reg] = (long)(intptr_t)check_happy; + DPRINTF("Set DR%d (r1.dr[%d]) to new value %" PRIxREGISTER "\n", + reg, reg, r1.dr[0]); + + DPRINTF("New state of the debug registers (r1):\n"); + for (i = 0; i < __arraycount(r1.dr); i++) + DPRINTF("r1[%zu]=%" PRIxREGISTER "\n", i, r1.dr[i]); + + DPRINTF("Call SETDBREGS for the child process (r1)\n"); + SYSCALL_REQUIRE(ptrace(PT_SETDBREGS, child, &r1, 0) != -1); + + DPRINTF("Before resuming the child process where it left off and " + "without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + DPRINTF("Before calling %s() for the child - expected stopped " + "SIGTRAP\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, SIGTRAP); + + SYSCALL_REQUIRE(ptrace(PT_GET_PROCESS_STATE, child, &state, slen) != -1); + + ATF_REQUIRE_EQ(state.pe_report_event, PTRACE_LWP_CREATE); + + lid = state.pe_lwp; + DPRINTF("Reported PTRACE_LWP_CREATE event with lid %d\n", lid); + + DPRINTF("Call GETDBREGS for the child process new lwp (r2)\n"); + SYSCALL_REQUIRE(ptrace(PT_GETDBREGS, child, &r2, lid) != -1); + + DPRINTF("State of the debug registers (r2):\n"); + for (i = 0; i < __arraycount(r2.dr); i++) + DPRINTF("r2[%zu]=%" PRIxREGISTER "\n", i, r2.dr[i]); + + DPRINTF("Assert that (r1) and (r2) are not the same\n"); + ATF_REQUIRE(memcmp(&r1, &r2, sizeof(r1)) != 0); + + DPRINTF("Before resuming the child process where it left off and " + "without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + DPRINTF("Before calling %s() for the child - expected exited\n", + TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_exited(status, exitval); + + DPRINTF("Before calling %s() for the child - expected no process\n", + TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} + +ATF_TC(dbregs_dr0_dont_inherit_lwp); +ATF_TC_HEAD(dbregs_dr0_dont_inherit_lwp, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that 1 LWP creation is intercepted by ptrace(2) with " + "EVENT_MASK set to PTRACE_LWP_CREATE and Debug Register 0 from " + "the forker thread is not inherited"); +} + +ATF_TC_BODY(dbregs_dr0_dont_inherit_lwp, tc) +{ + dbregs_dont_inherit_lwp(0); +} + +ATF_TC(dbregs_dr1_dont_inherit_lwp); +ATF_TC_HEAD(dbregs_dr1_dont_inherit_lwp, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that 1 LWP creation is intercepted by ptrace(2) with " + "EVENT_MASK set to PTRACE_LWP_CREATE and Debug Register 1 from " + "the forker thread is not inherited"); +} + +ATF_TC_BODY(dbregs_dr1_dont_inherit_lwp, tc) +{ + dbregs_dont_inherit_lwp(1); +} + +ATF_TC(dbregs_dr2_dont_inherit_lwp); +ATF_TC_HEAD(dbregs_dr2_dont_inherit_lwp, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that 1 LWP creation is intercepted by ptrace(2) with " + "EVENT_MASK set to PTRACE_LWP_CREATE and Debug Register 2 from " + "the forker thread is not inherited"); +} + +ATF_TC_BODY(dbregs_dr2_dont_inherit_lwp, tc) +{ + dbregs_dont_inherit_lwp(2); +} + +ATF_TC(dbregs_dr3_dont_inherit_lwp); +ATF_TC_HEAD(dbregs_dr3_dont_inherit_lwp, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that 1 LWP creation is intercepted by ptrace(2) with " + "EVENT_MASK set to PTRACE_LWP_CREATE and Debug Register 3 from " + "the forker thread is not inherited"); +} + +ATF_TC_BODY(dbregs_dr3_dont_inherit_lwp, tc) +{ + dbregs_dont_inherit_lwp(3); +} + +static void +dbregs_dont_inherit_execve(int reg) +{ + const int sigval = SIGTRAP; + pid_t child, wpid; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + size_t i; + struct dbreg r1; + struct dbreg r2; + + struct ptrace_siginfo info; + memset(&info, 0, sizeof(info)); + + if (!can_we_set_dbregs()) { + atf_tc_skip("Either run this test as root or set sysctl(3) " + "security.models.extensions.user_set_dbregs to 1"); + } + + DPRINTF("Before forking process PID=%d\n", getpid()); + SYSCALL_REQUIRE((child = fork()) != -1); + if (child == 0) { + DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + DPRINTF("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + DPRINTF("Before calling execve(2) from child\n"); + execlp("/bin/echo", "/bin/echo", NULL); + + FORKEE_ASSERT(0 && "Not reached"); + } + DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + DPRINTF("Call GETDBREGS for the child process (r1)\n"); + SYSCALL_REQUIRE(ptrace(PT_GETDBREGS, child, &r1, 0) != -1); + + DPRINTF("State of the debug registers (r1):\n"); + for (i = 0; i < __arraycount(r1.dr); i++) + DPRINTF("r1[%zu]=%" PRIxREGISTER "\n", i, r1.dr[i]); + + r1.dr[reg] = (long)(intptr_t)check_happy; + DPRINTF("Set DR%d (r1.dr[%d]) to new value %" PRIxREGISTER "\n", + reg, reg, r1.dr[reg]); + + DPRINTF("New state of the debug registers (r1):\n"); + for (i = 0; i < __arraycount(r1.dr); i++) + DPRINTF("r1[%zu]=%" PRIxREGISTER "\n", i, r1.dr[i]); + + DPRINTF("Call SETDBREGS for the child process (r1)\n"); + SYSCALL_REQUIRE(ptrace(PT_SETDBREGS, child, &r1, 0) != -1); + + DPRINTF("Before resuming the child process where it left off and " + "without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + DPRINTF("Before calling ptrace(2) with PT_GET_SIGINFO for child\n"); + SYSCALL_REQUIRE(ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1); + + DPRINTF("Signal traced to lwpid=%d\n", info.psi_lwpid); + DPRINTF("Signal properties: si_signo=%#x si_code=%#x si_errno=%#x\n", + info.psi_siginfo.si_signo, info.psi_siginfo.si_code, + info.psi_siginfo.si_errno); + + ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, sigval); + ATF_REQUIRE_EQ(info.psi_siginfo.si_code, TRAP_EXEC); + + DPRINTF("Call GETDBREGS for the child process after execve(2)\n"); + SYSCALL_REQUIRE(ptrace(PT_GETDBREGS, child, &r2, 0) != -1); + + DPRINTF("State of the debug registers (r2):\n"); + for (i = 0; i < __arraycount(r2.dr); i++) + DPRINTF("r2[%zu]=%" PRIxREGISTER "\n", i, r2.dr[i]); + + DPRINTF("Assert that (r1) and (r2) are not the same\n"); + ATF_REQUIRE(memcmp(&r1, &r2, sizeof(r1)) != 0); + + DPRINTF("Before resuming the child process where it left off and " + "without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} + +ATF_TC(dbregs_dr0_dont_inherit_execve); +ATF_TC_HEAD(dbregs_dr0_dont_inherit_execve, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that execve(2) is intercepted by tracer and Debug " + "Register 0 is reset"); +} + +ATF_TC_BODY(dbregs_dr0_dont_inherit_execve, tc) +{ + dbregs_dont_inherit_execve(0); +} + +ATF_TC(dbregs_dr1_dont_inherit_execve); +ATF_TC_HEAD(dbregs_dr1_dont_inherit_execve, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that execve(2) is intercepted by tracer and Debug " + "Register 1 is reset"); +} + +ATF_TC_BODY(dbregs_dr1_dont_inherit_execve, tc) +{ + dbregs_dont_inherit_execve(1); +} + +ATF_TC(dbregs_dr2_dont_inherit_execve); +ATF_TC_HEAD(dbregs_dr2_dont_inherit_execve, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that execve(2) is intercepted by tracer and Debug " + "Register 2 is reset"); +} + +ATF_TC_BODY(dbregs_dr2_dont_inherit_execve, tc) +{ + dbregs_dont_inherit_execve(2); +} + +ATF_TC(dbregs_dr3_dont_inherit_execve); +ATF_TC_HEAD(dbregs_dr3_dont_inherit_execve, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that execve(2) is intercepted by tracer and Debug " + "Register 3 is reset"); +} + +ATF_TC_BODY(dbregs_dr3_dont_inherit_execve, tc) +{ + dbregs_dont_inherit_execve(3); +} + +/// ---------------------------------------------------------------------------- + +ATF_TC(x86_cve_2018_8897); +ATF_TC_HEAD(x86_cve_2018_8897, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify mitigation for CVE-2018-8897 (POP SS debug exception)"); +} + +#define X86_CVE_2018_8897_PAGE 0x5000 /* page addressable by 32-bit registers */ + +static void +x86_cve_2018_8897_trigger(void) +{ + /* + * A function to trigger the POP SS (CVE-2018-8897) vulnerability + * + * ifdef __x86_64__ + * + * We need to switch to 32-bit mode execution on 64-bit kernel. + * This is achieved with far jump instruction and GDT descriptor + * set to 32-bit CS selector. The 32-bit CS selector is kernel + * specific, in the NetBSD case registered as GUCODE32_SEL + * that is equal to (14 (decimal) << 3) with GDT and user + * privilege level (this makes it 0x73). + * + * In UNIX as(1) assembly x86_64 far jump is coded as ljmp. + * amd64 ljmp requires an indirect address with cs:RIP. + * + * When we are running in 32-bit mode, it's similar to the + * mode as if the binary had been launched in netbsd32. + * + * There are two versions of this exploit, one with RIP + * relative code and the other with static addresses. + * The first one is PIE code aware, the other no-PIE one. + * + * + * After switching to the 32-bit mode we can move on to the remaining + * part of the exploit. + * + * endif // __x86_64__ + * + * Set the stack pointer to the page we allocated earlier. Remember + * that we put an SS selector exactly at this address, so we can pop. + * + * movl $0x5000,%esp + * + * Pop the SS selector off the stack. This reloads the SS selector, + * which is fine. Remember that we set DR0 at address 0x5000, which + * we are now reading. Therefore, on this instruction, the CPU will + * raise a #DB exception. + * + * But the "pop %ss" instruction is special: it blocks exceptions + * until the next instruction is executed. So the #DB that we just + * raised is actually blocked. + * + * pop %ss + * + * We are still here, and didn't receive the #DB. After we execute + * this instruction, the effect of "pop %ss" will disappear, and + * we will receive the #DB for real. + * + * int $4 + * + * Here the bug happens. We executed "int $4", so we entered the + * kernel, with interrupts disabled. The #DB that was pending is + * received. But, it is received immediately in kernel mode, and is + * _NOT_ received when interrupts are enabled again. + * + * It means that, in the first instruction of the $4 handler, we + * think we are safe with interrupts disabled. But we aren't, and + * just got interrupted. + * + * The new interrupt handler doesn't handle this particular context: + * we are entered in kernel mode, the previous context was kernel + * mode too but it still had the user context loaded. + * + * We find ourselves not doing a 'swapgs'. At the end of the day, it + * means that we call trap() with a curcpu() that is fully + * controllable by userland. From then on, it is easy to escalate + * privileges. + * + * With SVS it also means we don't switch CR3, so this results in a + * triple fault, which this time cannot be turned to a privilege + * escalation. + */ + +#if __x86_64__ +#if __PIE__ + void *csRIP; + + csRIP = malloc(sizeof(int) + sizeof(short)); + FORKEE_ASSERT(csRIP != NULL); + + __asm__ __volatile__( + " leal 24(%%eip), %%eax\n\t" + " movq %0, %%rdx\n\t" + " movl %%eax, (%%rdx)\n\t" + " movw $0x73, 4(%%rdx)\n\t" + " movq %1, %%rax\n\t" + " ljmp *(%%rax)\n\t" + " .code32\n\t" + " movl $0x5000, %%esp\n\t" + " pop %%ss\n\t" + " int $4\n\t" + " .code64\n\t" + : "=m"(csRIP) + : "m"(csRIP) + : "%rax", "%rdx", "%rsp" + ); +#else /* !__PIE__ */ + __asm__ __volatile__( + " movq $farjmp32%=, %%rax\n\t" + " ljmp *(%%rax)\n\t" + "farjmp32%=:\n\t" + " .long trigger32%=\n\t" + " .word 0x73\n\t" + " .code32\n\t" + "trigger32%=:\n\t" + " movl $0x5000, %%esp\n\t" + " pop %%ss\n\t" + " int $4\n\t" + " .code64\n\t" + : + : + : "%rax", "%rsp" + ); +#endif +#elif __i386__ + __asm__ __volatile__( + "movl $0x5000, %%esp\n\t" + "pop %%ss\n\t" + "int $4\n\t" + : + : + : "%esp" + ); +#endif +} + +ATF_TC_BODY(x86_cve_2018_8897, tc) +{ + const int sigval = SIGSTOP; + pid_t child, wpid; +#if defined(TWAIT_HAVE_STATUS) + int status; +#endif + char *trap_page; + struct dbreg db; + + + if (!can_we_set_dbregs()) { + atf_tc_skip("Either run this test as root or set sysctl(3) " + "security.models.extensions.user_set_dbregs to 1"); + } + + DPRINTF("Before forking process PID=%d\n", getpid()); + SYSCALL_REQUIRE((child = fork()) != -1); + if (child == 0) { + DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + trap_page = mmap((void *)X86_CVE_2018_8897_PAGE, + sysconf(_SC_PAGESIZE), PROT_READ|PROT_WRITE, + MAP_FIXED|MAP_ANON|MAP_PRIVATE, -1, 0); + + /* trigger page fault */ + memset(trap_page, 0, sysconf(_SC_PAGESIZE)); + + // kernel GDT +#if __x86_64__ + /* SS selector (descriptor 9 (0x4f >> 3)) */ + *trap_page = 0x4f; +#elif __i386__ + /* SS selector (descriptor 4 (0x23 >> 3)) */ + *trap_page = 0x23; +#endif + + DPRINTF("Before raising %s from child\n", strsignal(sigval)); + FORKEE_ASSERT(raise(sigval) == 0); + + x86_cve_2018_8897_trigger(); + + /* NOTREACHED */ + FORKEE_ASSERTX(0 && "This shall not be reached"); + } + DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + DPRINTF("Call GETDBREGS for the child process\n"); + SYSCALL_REQUIRE(ptrace(PT_GETDBREGS, child, &db, 0) != -1); + + /* + * Set up the dbregs. We put the 0x5000 address in DR0. + * It means that, the first time we touch this, the CPU will trigger a + * #DB exception. + */ + db.dr[0] = X86_CVE_2018_8897_PAGE; + db.dr[7] = 0x30003; + + DPRINTF("Call SETDBREGS for the child process\n"); + SYSCALL_REQUIRE(ptrace(PT_SETDBREGS, child, &db, 0) != -1); + + DPRINTF("Before resuming the child process where it left off and " + "without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + // In this test we receive SIGFPE, is this appropriate? +// validate_status_stopped(status, SIGFPE); + + DPRINTF("Kill the child process\n"); + SYSCALL_REQUIRE(ptrace(PT_KILL, child, NULL, 0) != -1); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_signaled(status, SIGKILL, 0); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} + +/// ---------------------------------------------------------------------------- + +union x86_test_register { + struct { + uint64_t a, b, c, d, e, f, g, h; + } zmm; + struct { + uint64_t a, b, c, d; + } ymm; + struct { + uint64_t a, b; + } xmm; + uint64_t u64; + uint32_t u32; +}; + +struct x86_test_fpu_registers { + struct { + uint64_t mantissa; + uint16_t sign_exp; + } __aligned(16) st[8]; + + uint16_t cw; + uint16_t sw; + uint16_t tw; + uint8_t tw_abridged; + uint16_t opcode; + union fp_addr ip; + union fp_addr dp; +}; + +enum x86_test_regset { + TEST_GPREGS, + TEST_FPREGS, + TEST_XMMREGS, + TEST_XSTATE +}; + +/* Please keep them grouped by acceptable x86_test_regset. */ +enum x86_test_registers { + /* TEST_GPREGS */ + GPREGS_32, + GPREGS_32_EBP_ESP, + GPREGS_64, + GPREGS_64_R8, + /* TEST_FPREGS/TEST_XMMREGS */ + FPREGS_FPU, + FPREGS_MM, + FPREGS_XMM, + /* TEST_XSTATE */ + FPREGS_YMM, + FPREGS_ZMM +}; + +enum x86_test_regmode { + TEST_GETREGS, + TEST_SETREGS, + TEST_COREDUMP +}; + +static __inline void get_gp32_regs(union x86_test_register out[]) +{ +#if defined(__i386__) + const uint32_t fill = 0x0F0F0F0F; + + __asm__ __volatile__( + /* fill registers with clobber pattern */ + "movl %6, %%eax\n\t" + "movl %6, %%ebx\n\t" + "movl %6, %%ecx\n\t" + "movl %6, %%edx\n\t" + "movl %6, %%esi\n\t" + "movl %6, %%edi\n\t" + "\n\t" + "int3\n\t" + : "=a"(out[0].u32), "=b"(out[1].u32), "=c"(out[2].u32), + "=d"(out[3].u32), "=S"(out[4].u32), "=D"(out[5].u32) + : "g"(fill) + ); +#else + __unreachable(); +#endif +} + +static __inline void set_gp32_regs(const union x86_test_register data[]) +{ +#if defined(__i386__) + __asm__ __volatile__( + "int3\n\t" + : + : "a"(data[0].u32), "b"(data[1].u32), "c"(data[2].u32), + "d"(data[3].u32), "S"(data[4].u32), "D"(data[5].u32) + : + ); +#else + __unreachable(); +#endif +} + +static __inline void get_gp32_ebp_esp_regs(union x86_test_register out[]) +{ +#if defined(__i386__) + const uint32_t fill = 0x0F0F0F0F; + + __asm__ __volatile__( + /* save original ebp & esp using our output registers */ + "movl %%esp, %0\n\t" + "movl %%ebp, %1\n\t" + /* fill them with clobber pattern */ + "movl %2, %%esp\n\t" + "movl %2, %%ebp\n\t" + "\n\t" + "int3\n\t" + "\n\t" + /* restore ebp & esp, and save the result */ + "xchgl %%esp, %0\n\t" + "xchgl %%ebp, %1\n\t" + : "=r"(out[0].u32), "=r"(out[1].u32) + : "g"(fill) + : + ); +#else + __unreachable(); +#endif +} + +static __inline void set_gp32_ebp_esp_regs(const union x86_test_register data[]) +{ +#if defined(__i386__) + __asm__ __volatile__( + /* ebp & ebp are a bit tricky, we must not clobber them */ + "movl %%esp, %%eax\n\t" + "movl %%ebp, %%ebx\n\t" + "movl %0, %%esp\n\t" + "movl %1, %%ebp\n\t" + "\n\t" + "int3\n\t" + "\n\t" + "movl %%eax, %%esp\n\t" + "movl %%ebx, %%ebp\n\t" + : + : "ri"(data[0].u32), "ri"(data[1].u32) + : "%eax", "%ebx" + ); +#else + __unreachable(); +#endif +} + +static __inline void get_gp64_regs(union x86_test_register out[]) +{ +#if defined(__x86_64__) + const uint64_t fill = 0x0F0F0F0F0F0F0F0F; + + __asm__ __volatile__( + /* save rsp & rbp */ + "movq %%rsp, %6\n\t" + "movq %%rbp, %7\n\t" + "\n\t" + /* fill registers with clobber pattern */ + "movq %8, %%rax\n\t" + "movq %8, %%rbx\n\t" + "movq %8, %%rcx\n\t" + "movq %8, %%rdx\n\t" + "movq %8, %%rsp\n\t" + "movq %8, %%rbp\n\t" + "movq %8, %%rsi\n\t" + "movq %8, %%rdi\n\t" + "\n\t" + "int3\n\t" + "\n\t" + /* swap saved & current rsp & rbp */ + "xchgq %%rsp, %6\n\t" + "xchgq %%rbp, %7\n\t" + : "=a"(out[0].u64), "=b"(out[1].u64), "=c"(out[2].u64), + "=d"(out[3].u64), "=S"(out[4].u64), "=D"(out[5].u64), + "=r"(out[6].u64), "=r"(out[7].u64) + : "g"(fill) + ); +#else + __unreachable(); +#endif +} + +static __inline void set_gp64_regs(const union x86_test_register data[]) +{ +#if defined(__x86_64__) + __asm__ __volatile__( + /* rbp & rbp are a bit tricky, we must not clobber them */ + "movq %%rsp, %%r8\n\t" + "movq %%rbp, %%r9\n\t" + "movq %6, %%rsp\n\t" + "movq %7, %%rbp\n\t" + "\n\t" + "int3\n\t" + "\n\t" + "movq %%r8, %%rsp\n\t" + "movq %%r9, %%rbp\n\t" + : + : "a"(data[0].u64), "b"(data[1].u64), "c"(data[2].u64), + "d"(data[3].u64), "S"(data[4].u64), "D"(data[5].u64), + "r"(data[6].u64), "r"(data[7].u64) + : "%r8", "%r9" + ); +#else + __unreachable(); +#endif +} + +static __inline void get_gp64_r8_regs(union x86_test_register out[]) +{ +#if defined(__x86_64__) + const uint64_t fill = 0x0F0F0F0F0F0F0F0F; + + __asm__ __volatile__( + /* fill registers with clobber pattern */ + "movq %1, %%r8\n\t" + "movq %1, %%r9\n\t" + "movq %1, %%r10\n\t" + "movq %1, %%r11\n\t" + "movq %1, %%r12\n\t" + "movq %1, %%r13\n\t" + "movq %1, %%r14\n\t" + "movq %1, %%r15\n\t" + "\n\t" + "int3\n\t" + "\n\t" + "movq %%r8, 0x000(%0)\n\t" + "movq %%r9, 0x040(%0)\n\t" + "movq %%r10, 0x080(%0)\n\t" + "movq %%r11, 0x0C0(%0)\n\t" + "movq %%r12, 0x100(%0)\n\t" + "movq %%r13, 0x140(%0)\n\t" + "movq %%r14, 0x180(%0)\n\t" + "movq %%r15, 0x1C0(%0)\n\t" + : + : "a"(out), "m"(fill) + : "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15" + ); +#else + __unreachable(); +#endif +} + +static __inline void set_gp64_r8_regs(const union x86_test_register data[]) +{ +#if defined(__x86_64__) + __asm__ __volatile__( + "movq 0x000(%0), %%r8\n\t" + "movq 0x040(%0), %%r9\n\t" + "movq 0x080(%0), %%r10\n\t" + "movq 0x0C0(%0), %%r11\n\t" + "movq 0x100(%0), %%r12\n\t" + "movq 0x140(%0), %%r13\n\t" + "movq 0x180(%0), %%r14\n\t" + "movq 0x1C0(%0), %%r15\n\t" + "int3\n\t" + : + : "b"(data) + : "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15" + ); +#else + __unreachable(); +#endif +} + +static __inline void get_fpu_regs(struct x86_test_fpu_registers *out) +{ + struct save87 fsave; + struct fxsave fxsave; + + __CTASSERT(sizeof(out->st[0]) == 16); + + __asm__ __volatile__( + "finit\n\t" + "int3\n\t" +#if defined(__x86_64__) + "fxsave64 %2\n\t" +#else + "fxsave %2\n\t" +#endif + "fnstenv %1\n\t" + "fnclex\n\t" + "fstpt 0x00(%0)\n\t" + "fstpt 0x10(%0)\n\t" + "fstpt 0x20(%0)\n\t" + "fstpt 0x30(%0)\n\t" + "fstpt 0x40(%0)\n\t" + "fstpt 0x50(%0)\n\t" + "fstpt 0x60(%0)\n\t" + "fstpt 0x70(%0)\n\t" + : + : "a"(out->st), "m"(fsave), "m"(fxsave) + : "st", "memory" + ); + + FORKEE_ASSERT(fsave.s87_cw == fxsave.fx_cw); + FORKEE_ASSERT(fsave.s87_sw == fxsave.fx_sw); + + /* fsave contains full tw */ + out->cw = fsave.s87_cw; + out->sw = fsave.s87_sw; + out->tw = fsave.s87_tw; + out->tw_abridged = fxsave.fx_tw; + out->opcode = fxsave.fx_opcode; + out->ip = fxsave.fx_ip; + out->dp = fxsave.fx_dp; +} + +/* used as single-precision float */ +uint32_t x86_test_zero = 0; + +static __inline void set_fpu_regs(const struct x86_test_fpu_registers *data) +{ + __CTASSERT(sizeof(data->st[0]) == 16); + + __asm__ __volatile__( + "finit\n\t" + "fldcw %1\n\t" + /* load on stack in reverse order to make it easier to read */ + "fldt 0x70(%0)\n\t" + "fldt 0x60(%0)\n\t" + "fldt 0x50(%0)\n\t" + "fldt 0x40(%0)\n\t" + "fldt 0x30(%0)\n\t" + "fldt 0x20(%0)\n\t" + "fldt 0x10(%0)\n\t" + "fldt 0x00(%0)\n\t" + /* free st7 */ + "ffree %%st(7)\n\t" + /* this should trigger a divide-by-zero */ + "fdivs (%2)\n\t" + "int3\n\t" + : + : "a"(&data->st), "m"(data->cw), "b"(&x86_test_zero) + : "st" + ); +} + +__attribute__((target("mmx"))) +static __inline void get_mm_regs(union x86_test_register out[]) +{ + const uint64_t fill = 0x0F0F0F0F0F0F0F0F; + + __asm__ __volatile__( + /* fill registers with clobber pattern */ + "movq %1, %%mm0\n\t" + "movq %1, %%mm1\n\t" + "movq %1, %%mm2\n\t" + "movq %1, %%mm3\n\t" + "movq %1, %%mm4\n\t" + "movq %1, %%mm5\n\t" + "movq %1, %%mm6\n\t" + "movq %1, %%mm7\n\t" + "\n\t" + "int3\n\t" + "\n\t" + "movq %%mm0, 0x000(%0)\n\t" + "movq %%mm1, 0x040(%0)\n\t" + "movq %%mm2, 0x080(%0)\n\t" + "movq %%mm3, 0x0C0(%0)\n\t" + "movq %%mm4, 0x100(%0)\n\t" + "movq %%mm5, 0x140(%0)\n\t" + "movq %%mm6, 0x180(%0)\n\t" + "movq %%mm7, 0x1C0(%0)\n\t" + : + : "a"(out), "m"(fill) + : "%mm0", "%mm1", "%mm2", "%mm3", "%mm4", "%mm5", "%mm6", "%mm7" + ); +} + +__attribute__((target("mmx"))) +static __inline void set_mm_regs(const union x86_test_register data[]) +{ + __asm__ __volatile__( + "movq 0x000(%0), %%mm0\n\t" + "movq 0x040(%0), %%mm1\n\t" + "movq 0x080(%0), %%mm2\n\t" + "movq 0x0C0(%0), %%mm3\n\t" + "movq 0x100(%0), %%mm4\n\t" + "movq 0x140(%0), %%mm5\n\t" + "movq 0x180(%0), %%mm6\n\t" + "movq 0x1C0(%0), %%mm7\n\t" + "int3\n\t" + : + : "b"(data) + : "%mm0", "%mm1", "%mm2", "%mm3", "%mm4", "%mm5", "%mm6", "%mm7" + ); +} + +__attribute__((target("sse"))) +static __inline void get_xmm_regs(union x86_test_register out[]) +{ + union x86_test_register fill __aligned(32) = { + .xmm={ 0x0F0F0F0F0F0F0F0F, 0x0F0F0F0F0F0F0F0F } + }; + + __asm__ __volatile__( + /* fill registers with clobber pattern */ + "movaps %1, %%xmm0\n\t" + "movaps %1, %%xmm1\n\t" + "movaps %1, %%xmm2\n\t" + "movaps %1, %%xmm3\n\t" + "movaps %1, %%xmm4\n\t" + "movaps %1, %%xmm5\n\t" + "movaps %1, %%xmm6\n\t" + "movaps %1, %%xmm7\n\t" +#if defined(__x86_64__) + "movaps %1, %%xmm8\n\t" + "movaps %1, %%xmm9\n\t" + "movaps %1, %%xmm10\n\t" + "movaps %1, %%xmm11\n\t" + "movaps %1, %%xmm12\n\t" + "movaps %1, %%xmm13\n\t" + "movaps %1, %%xmm14\n\t" + "movaps %1, %%xmm15\n\t" +#endif + "\n\t" + "int3\n\t" + "\n\t" + "movaps %%xmm0, 0x000(%0)\n\t" + "movaps %%xmm1, 0x040(%0)\n\t" + "movaps %%xmm2, 0x080(%0)\n\t" + "movaps %%xmm3, 0x0C0(%0)\n\t" + "movaps %%xmm4, 0x100(%0)\n\t" + "movaps %%xmm5, 0x140(%0)\n\t" + "movaps %%xmm6, 0x180(%0)\n\t" + "movaps %%xmm7, 0x1C0(%0)\n\t" +#if defined(__x86_64__) + "movaps %%xmm8, 0x200(%0)\n\t" + "movaps %%xmm9, 0x240(%0)\n\t" + "movaps %%xmm10, 0x280(%0)\n\t" + "movaps %%xmm11, 0x2C0(%0)\n\t" + "movaps %%xmm12, 0x300(%0)\n\t" + "movaps %%xmm13, 0x340(%0)\n\t" + "movaps %%xmm14, 0x380(%0)\n\t" + "movaps %%xmm15, 0x3C0(%0)\n\t" +#endif + : + : "a"(out), "m"(fill) + : "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7" +#if defined(__x86_64__) + , "%xmm8", "%xmm9", "%xmm10", "%xmm11", "%xmm12", "%xmm13", "%xmm14", + "%xmm15" +#endif + ); +} + +__attribute__((target("sse"))) +static __inline void set_xmm_regs(const union x86_test_register data[]) +{ + __asm__ __volatile__( + "movaps 0x000(%0), %%xmm0\n\t" + "movaps 0x040(%0), %%xmm1\n\t" + "movaps 0x080(%0), %%xmm2\n\t" + "movaps 0x0C0(%0), %%xmm3\n\t" + "movaps 0x100(%0), %%xmm4\n\t" + "movaps 0x140(%0), %%xmm5\n\t" + "movaps 0x180(%0), %%xmm6\n\t" + "movaps 0x1C0(%0), %%xmm7\n\t" +#if defined(__x86_64__) + "movaps 0x200(%0), %%xmm8\n\t" + "movaps 0x240(%0), %%xmm9\n\t" + "movaps 0x280(%0), %%xmm10\n\t" + "movaps 0x2C0(%0), %%xmm11\n\t" + "movaps 0x300(%0), %%xmm12\n\t" + "movaps 0x340(%0), %%xmm13\n\t" + "movaps 0x380(%0), %%xmm14\n\t" + "movaps 0x3C0(%0), %%xmm15\n\t" +#endif + "int3\n\t" + : + : "b"(data) + : "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", + "%xmm7" +#if defined(__x86_64__) + , "%xmm8", "%xmm9", "%xmm10", "%xmm11", "%xmm12", "%xmm13", + "%xmm14", "%xmm15" +#endif + ); +} + +__attribute__((target("avx"))) +static __inline void get_ymm_regs(union x86_test_register out[]) +{ + union x86_test_register fill __aligned(32) = { + .ymm = { + 0x0F0F0F0F0F0F0F0F, 0x0F0F0F0F0F0F0F0F, + 0x0F0F0F0F0F0F0F0F, 0x0F0F0F0F0F0F0F0F + } + }; + + __asm__ __volatile__( + /* fill registers with clobber pattern */ + "vmovaps %1, %%ymm0\n\t" + "vmovaps %1, %%ymm1\n\t" + "vmovaps %1, %%ymm2\n\t" + "vmovaps %1, %%ymm3\n\t" + "vmovaps %1, %%ymm4\n\t" + "vmovaps %1, %%ymm5\n\t" + "vmovaps %1, %%ymm6\n\t" + "vmovaps %1, %%ymm7\n\t" +#if defined(__x86_64__) + "vmovaps %1, %%ymm8\n\t" + "vmovaps %1, %%ymm9\n\t" + "vmovaps %1, %%ymm10\n\t" + "vmovaps %1, %%ymm11\n\t" + "vmovaps %1, %%ymm12\n\t" + "vmovaps %1, %%ymm13\n\t" + "vmovaps %1, %%ymm14\n\t" + "vmovaps %1, %%ymm15\n\t" +#endif + "\n\t" + "int3\n\t" + "\n\t" + "vmovaps %%ymm0, 0x000(%0)\n\t" + "vmovaps %%ymm1, 0x040(%0)\n\t" + "vmovaps %%ymm2, 0x080(%0)\n\t" + "vmovaps %%ymm3, 0x0C0(%0)\n\t" + "vmovaps %%ymm4, 0x100(%0)\n\t" + "vmovaps %%ymm5, 0x140(%0)\n\t" + "vmovaps %%ymm6, 0x180(%0)\n\t" + "vmovaps %%ymm7, 0x1C0(%0)\n\t" +#if defined(__x86_64__) + "vmovaps %%ymm8, 0x200(%0)\n\t" + "vmovaps %%ymm9, 0x240(%0)\n\t" + "vmovaps %%ymm10, 0x280(%0)\n\t" + "vmovaps %%ymm11, 0x2C0(%0)\n\t" + "vmovaps %%ymm12, 0x300(%0)\n\t" + "vmovaps %%ymm13, 0x340(%0)\n\t" + "vmovaps %%ymm14, 0x380(%0)\n\t" + "vmovaps %%ymm15, 0x3C0(%0)\n\t" +#endif + : + : "a"(out), "m"(fill) + : "%ymm0", "%ymm1", "%ymm2", "%ymm3", "%ymm4", "%ymm5", "%ymm6", "%ymm7" +#if defined(__x86_64__) + , "%ymm8", "%ymm9", "%ymm10", "%ymm11", "%ymm12", "%ymm13", "%ymm14", + "%ymm15" +#endif + ); +} + +__attribute__((target("avx"))) +static __inline void set_ymm_regs(const union x86_test_register data[]) +{ + __asm__ __volatile__( + "vmovaps 0x000(%0), %%ymm0\n\t" + "vmovaps 0x040(%0), %%ymm1\n\t" + "vmovaps 0x080(%0), %%ymm2\n\t" + "vmovaps 0x0C0(%0), %%ymm3\n\t" + "vmovaps 0x100(%0), %%ymm4\n\t" + "vmovaps 0x140(%0), %%ymm5\n\t" + "vmovaps 0x180(%0), %%ymm6\n\t" + "vmovaps 0x1C0(%0), %%ymm7\n\t" +#if defined(__x86_64__) + "vmovaps 0x200(%0), %%ymm8\n\t" + "vmovaps 0x240(%0), %%ymm9\n\t" + "vmovaps 0x280(%0), %%ymm10\n\t" + "vmovaps 0x2C0(%0), %%ymm11\n\t" + "vmovaps 0x300(%0), %%ymm12\n\t" + "vmovaps 0x340(%0), %%ymm13\n\t" + "vmovaps 0x380(%0), %%ymm14\n\t" + "vmovaps 0x3C0(%0), %%ymm15\n\t" +#endif + "int3\n\t" + : + : "b"(data) + : "%ymm0", "%ymm1", "%ymm2", "%ymm3", "%ymm4", "%ymm5", "%ymm6", + "%ymm7" +#if defined(__x86_64__) + , "%ymm8", "%ymm9", "%ymm10", "%ymm11", "%ymm12", "%ymm13", + "%ymm14", "%ymm15" +#endif + ); +} + +__attribute__((target("avx512f"))) +static __inline void get_zmm_regs(union x86_test_register out[]) +{ + union x86_test_register fill __aligned(64) = { + .zmm = { + 0x0F0F0F0F0F0F0F0F, 0x0F0F0F0F0F0F0F0F, + 0x0F0F0F0F0F0F0F0F, 0x0F0F0F0F0F0F0F0F, + 0x0F0F0F0F0F0F0F0F, 0x0F0F0F0F0F0F0F0F, + 0x0F0F0F0F0F0F0F0F, 0x0F0F0F0F0F0F0F0F + } + }; + + __asm__ __volatile__( + /* fill registers with clobber pattern */ + "vmovaps %1, %%zmm0\n\t" + "vmovaps %1, %%zmm1\n\t" + "vmovaps %1, %%zmm2\n\t" + "vmovaps %1, %%zmm3\n\t" + "vmovaps %1, %%zmm4\n\t" + "vmovaps %1, %%zmm5\n\t" + "vmovaps %1, %%zmm6\n\t" + "vmovaps %1, %%zmm7\n\t" +#if defined(__x86_64__) + "vmovaps %1, %%zmm8\n\t" + "vmovaps %1, %%zmm9\n\t" + "vmovaps %1, %%zmm10\n\t" + "vmovaps %1, %%zmm11\n\t" + "vmovaps %1, %%zmm12\n\t" + "vmovaps %1, %%zmm13\n\t" + "vmovaps %1, %%zmm14\n\t" + "vmovaps %1, %%zmm15\n\t" + "vmovaps %1, %%zmm16\n\t" + "vmovaps %1, %%zmm17\n\t" + "vmovaps %1, %%zmm18\n\t" + "vmovaps %1, %%zmm19\n\t" + "vmovaps %1, %%zmm20\n\t" + "vmovaps %1, %%zmm21\n\t" + "vmovaps %1, %%zmm22\n\t" + "vmovaps %1, %%zmm23\n\t" + "vmovaps %1, %%zmm24\n\t" + "vmovaps %1, %%zmm25\n\t" + "vmovaps %1, %%zmm26\n\t" + "vmovaps %1, %%zmm27\n\t" + "vmovaps %1, %%zmm28\n\t" + "vmovaps %1, %%zmm29\n\t" + "vmovaps %1, %%zmm30\n\t" + "vmovaps %1, %%zmm31\n\t" +#endif + "kmovq %1, %%k0\n\t" + "kmovq %1, %%k1\n\t" + "kmovq %1, %%k2\n\t" + "kmovq %1, %%k3\n\t" + "kmovq %1, %%k4\n\t" + "kmovq %1, %%k5\n\t" + "kmovq %1, %%k6\n\t" + "kmovq %1, %%k7\n\t" + "\n\t" + "int3\n\t" + "\n\t" + "vmovaps %%zmm0, 0x000(%0)\n\t" + "vmovaps %%zmm1, 0x040(%0)\n\t" + "vmovaps %%zmm2, 0x080(%0)\n\t" + "vmovaps %%zmm3, 0x0C0(%0)\n\t" + "vmovaps %%zmm4, 0x100(%0)\n\t" + "vmovaps %%zmm5, 0x140(%0)\n\t" + "vmovaps %%zmm6, 0x180(%0)\n\t" + "vmovaps %%zmm7, 0x1C0(%0)\n\t" +#if defined(__x86_64__) + "vmovaps %%zmm8, 0x200(%0)\n\t" + "vmovaps %%zmm9, 0x240(%0)\n\t" + "vmovaps %%zmm10, 0x280(%0)\n\t" + "vmovaps %%zmm11, 0x2C0(%0)\n\t" + "vmovaps %%zmm12, 0x300(%0)\n\t" + "vmovaps %%zmm13, 0x340(%0)\n\t" + "vmovaps %%zmm14, 0x380(%0)\n\t" + "vmovaps %%zmm15, 0x3C0(%0)\n\t" + "vmovaps %%zmm16, 0x400(%0)\n\t" + "vmovaps %%zmm17, 0x440(%0)\n\t" + "vmovaps %%zmm18, 0x480(%0)\n\t" + "vmovaps %%zmm19, 0x4C0(%0)\n\t" + "vmovaps %%zmm20, 0x500(%0)\n\t" + "vmovaps %%zmm21, 0x540(%0)\n\t" + "vmovaps %%zmm22, 0x580(%0)\n\t" + "vmovaps %%zmm23, 0x5C0(%0)\n\t" + "vmovaps %%zmm24, 0x600(%0)\n\t" + "vmovaps %%zmm25, 0x640(%0)\n\t" + "vmovaps %%zmm26, 0x680(%0)\n\t" + "vmovaps %%zmm27, 0x6C0(%0)\n\t" + "vmovaps %%zmm28, 0x700(%0)\n\t" + "vmovaps %%zmm29, 0x740(%0)\n\t" + "vmovaps %%zmm30, 0x780(%0)\n\t" + "vmovaps %%zmm31, 0x7C0(%0)\n\t" +#endif + "kmovq %%k0, 0x800(%0)\n\t" + "kmovq %%k1, 0x808(%0)\n\t" + "kmovq %%k2, 0x810(%0)\n\t" + "kmovq %%k3, 0x818(%0)\n\t" + "kmovq %%k4, 0x820(%0)\n\t" + "kmovq %%k5, 0x828(%0)\n\t" + "kmovq %%k6, 0x830(%0)\n\t" + "kmovq %%k7, 0x838(%0)\n\t" + : + : "a"(out), "m"(fill) + : "%zmm0", "%zmm1", "%zmm2", "%zmm3", "%zmm4", "%zmm5", "%zmm6", "%zmm7" +#if defined(__x86_64__) + , "%zmm8", "%zmm9", "%zmm10", "%zmm11", "%zmm12", "%zmm13", "%zmm14", + "%zmm15", "%zmm16", "%zmm17", "%zmm18", "%zmm19", "%zmm20", "%zmm21", + "%zmm22", "%zmm23", "%zmm24", "%zmm25", "%zmm26", "%zmm27", "%zmm28", + "%zmm29", "%zmm30", "%zmm31" +#endif + , "%k0", "%k1", "%k2", "%k3", "%k4", "%k5", "%k6", "%k7" + ); +} + +__attribute__((target("avx512f"))) +static __inline void set_zmm_regs(const union x86_test_register data[]) +{ + __asm__ __volatile__( + "vmovaps 0x000(%0), %%zmm0\n\t" + "vmovaps 0x040(%0), %%zmm1\n\t" + "vmovaps 0x080(%0), %%zmm2\n\t" + "vmovaps 0x0C0(%0), %%zmm3\n\t" + "vmovaps 0x100(%0), %%zmm4\n\t" + "vmovaps 0x140(%0), %%zmm5\n\t" + "vmovaps 0x180(%0), %%zmm6\n\t" + "vmovaps 0x1C0(%0), %%zmm7\n\t" +#if defined(__x86_64__) + "vmovaps 0x200(%0), %%zmm8\n\t" + "vmovaps 0x240(%0), %%zmm9\n\t" + "vmovaps 0x280(%0), %%zmm10\n\t" + "vmovaps 0x2C0(%0), %%zmm11\n\t" + "vmovaps 0x300(%0), %%zmm12\n\t" + "vmovaps 0x340(%0), %%zmm13\n\t" + "vmovaps 0x380(%0), %%zmm14\n\t" + "vmovaps 0x3C0(%0), %%zmm15\n\t" + "vmovaps 0x400(%0), %%zmm16\n\t" + "vmovaps 0x440(%0), %%zmm17\n\t" + "vmovaps 0x480(%0), %%zmm18\n\t" + "vmovaps 0x4C0(%0), %%zmm19\n\t" + "vmovaps 0x500(%0), %%zmm20\n\t" + "vmovaps 0x540(%0), %%zmm21\n\t" + "vmovaps 0x580(%0), %%zmm22\n\t" + "vmovaps 0x5C0(%0), %%zmm23\n\t" + "vmovaps 0x600(%0), %%zmm24\n\t" + "vmovaps 0x640(%0), %%zmm25\n\t" + "vmovaps 0x680(%0), %%zmm26\n\t" + "vmovaps 0x6C0(%0), %%zmm27\n\t" + "vmovaps 0x700(%0), %%zmm28\n\t" + "vmovaps 0x740(%0), %%zmm29\n\t" + "vmovaps 0x780(%0), %%zmm30\n\t" + "vmovaps 0x7C0(%0), %%zmm31\n\t" +#endif + "kmovq 0x800(%0), %%k0\n\t" + "kmovq 0x808(%0), %%k1\n\t" + "kmovq 0x810(%0), %%k2\n\t" + "kmovq 0x818(%0), %%k3\n\t" + "kmovq 0x820(%0), %%k4\n\t" + "kmovq 0x828(%0), %%k5\n\t" + "kmovq 0x830(%0), %%k6\n\t" + "kmovq 0x838(%0), %%k7\n\t" + "int3\n\t" + : + : "b"(data) + : "%zmm0", "%zmm1", "%zmm2", "%zmm3", "%zmm4", "%zmm5", "%zmm6", "%zmm7" +#if defined(__x86_64__) + , "%zmm8", "%zmm9", "%zmm10", "%zmm11", "%zmm12", "%zmm13", "%zmm14", + "%zmm15", "%zmm16", "%zmm17", "%zmm18", "%zmm19", "%zmm20", "%zmm21", + "%zmm22", "%zmm23", "%zmm24", "%zmm25", "%zmm26", "%zmm27", "%zmm28", + "%zmm29", "%zmm30", "%zmm31" +#endif + , "%k0", "%k1", "%k2", "%k3", "%k4", + "%k5", "%k6", "%k7" + ); +} + +static void +x86_register_test(enum x86_test_regset regset, enum x86_test_registers regs, + enum x86_test_regmode regmode) +{ + const int exitval = 5; + pid_t child, wpid; +#if defined(TWAIT_HAVE_STATUS) + const int sigval = SIGTRAP; + int status; +#endif + struct reg gpr; + struct fpreg fpr; +#if defined(__i386__) + struct xmmregs xmm; +#endif + struct xstate xst; + struct iovec iov; + struct fxsave* fxs = NULL; + uint64_t xst_flags = 0; + char core_path[] = "/tmp/core.XXXXXX"; + int core_fd; + + const union x86_test_register expected[] __aligned(64) = { + {{ 0x0706050403020100, 0x0F0E0D0C0B0A0908, + 0x1716151413121110, 0x1F1E1D1C1B1A1918, + 0x2726252423222120, 0x2F2E2D2C2B2A2928, + 0x3736353433323130, 0x3F3E3D3C3B3A3938, }}, + {{ 0x0807060504030201, 0x100F0E0D0C0B0A09, + 0x1817161514131211, 0x201F1E1D1C1B1A19, + 0x2827262524232221, 0x302F2E2D2C2B2A29, + 0x3837363534333231, 0x403F3E3D3C3B3A39, }}, + {{ 0x0908070605040302, 0x11100F0E0D0C0B0A, + 0x1918171615141312, 0x21201F1E1D1C1B1A, + 0x2928272625242322, 0x31302F2E2D2C2B2A, + 0x3938373635343332, 0x41403F3E3D3C3B3A, }}, + {{ 0x0A09080706050403, 0x1211100F0E0D0C0B, + 0x1A19181716151413, 0x2221201F1E1D1C1B, + 0x2A29282726252423, 0x3231302F2E2D2C2B, + 0x3A39383736353433, 0x4241403F3E3D3C3B, }}, + {{ 0x0B0A090807060504, 0x131211100F0E0D0C, + 0x1B1A191817161514, 0x232221201F1E1D1C, + 0x2B2A292827262524, 0x333231302F2E2D2C, + 0x3B3A393837363534, 0x434241403F3E3D3C, }}, + {{ 0x0C0B0A0908070605, 0x14131211100F0E0D, + 0x1C1B1A1918171615, 0x24232221201F1E1D, + 0x2C2B2A2928272625, 0x34333231302F2E2D, + 0x3C3B3A3938373635, 0x44434241403F3E3D, }}, + {{ 0x0D0C0B0A09080706, 0x1514131211100F0E, + 0x1D1C1B1A19181716, 0x2524232221201F1E, + 0x2D2C2B2A29282726, 0x3534333231302F2E, + 0x3D3C3B3A39383736, 0x4544434241403F3E, }}, + {{ 0x0E0D0C0B0A090807, 0x161514131211100F, + 0x1E1D1C1B1A191817, 0x262524232221201F, + 0x2E2D2C2B2A292827, 0x363534333231302F, + 0x3E3D3C3B3A393837, 0x464544434241403F, }}, + {{ 0x0F0E0D0C0B0A0908, 0x1716151413121110, + 0x1F1E1D1C1B1A1918, 0x2726252423222120, + 0x2F2E2D2C2B2A2928, 0x3736353433323130, + 0x3F3E3D3C3B3A3938, 0x4746454443424140, }}, + {{ 0x100F0E0D0C0B0A09, 0x1817161514131211, + 0x201F1E1D1C1B1A19, 0x2827262524232221, + 0x302F2E2D2C2B2A29, 0x3837363534333231, + 0x403F3E3D3C3B3A39, 0x4847464544434241, }}, + {{ 0x11100F0E0D0C0B0A, 0x1918171615141312, + 0x21201F1E1D1C1B1A, 0x2928272625242322, + 0x31302F2E2D2C2B2A, 0x3938373635343332, + 0x41403F3E3D3C3B3A, 0x4948474645444342, }}, + {{ 0x1211100F0E0D0C0B, 0x1A19181716151413, + 0x2221201F1E1D1C1B, 0x2A29282726252423, + 0x3231302F2E2D2C2B, 0x3A39383736353433, + 0x4241403F3E3D3C3B, 0x4A49484746454443, }}, + {{ 0x131211100F0E0D0C, 0x1B1A191817161514, + 0x232221201F1E1D1C, 0x2B2A292827262524, + 0x333231302F2E2D2C, 0x3B3A393837363534, + 0x434241403F3E3D3C, 0x4B4A494847464544, }}, + {{ 0x14131211100F0E0D, 0x1C1B1A1918171615, + 0x24232221201F1E1D, 0x2C2B2A2928272625, + 0x34333231302F2E2D, 0x3C3B3A3938373635, + 0x44434241403F3E3D, 0x4C4B4A4948474645, }}, + {{ 0x1514131211100F0E, 0x1D1C1B1A19181716, + 0x2524232221201F1E, 0x2D2C2B2A29282726, + 0x3534333231302F2E, 0x3D3C3B3A39383736, + 0x4544434241403F3E, 0x4D4C4B4A49484746, }}, + {{ 0x161514131211100F, 0x1E1D1C1B1A191817, + 0x262524232221201F, 0x2E2D2C2B2A292827, + 0x363534333231302F, 0x3E3D3C3B3A393837, + 0x464544434241403F, 0x4E4D4C4B4A494847, }}, + {{ 0x1716151413121110, 0x1F1E1D1C1B1A1918, + 0x2726252423222120, 0x2F2E2D2C2B2A2928, + 0x3736353433323130, 0x3F3E3D3C3B3A3938, + 0x4746454443424140, 0x4F4E4D4C4B4A4948, }}, + {{ 0x1817161514131211, 0x201F1E1D1C1B1A19, + 0x2827262524232221, 0x302F2E2D2C2B2A29, + 0x3837363534333231, 0x403F3E3D3C3B3A39, + 0x4847464544434241, 0x504F4E4D4C4B4A49, }}, + {{ 0x1918171615141312, 0x21201F1E1D1C1B1A, + 0x2928272625242322, 0x31302F2E2D2C2B2A, + 0x3938373635343332, 0x41403F3E3D3C3B3A, + 0x4948474645444342, 0x51504F4E4D4C4B4A, }}, + {{ 0x1A19181716151413, 0x2221201F1E1D1C1B, + 0x2A29282726252423, 0x3231302F2E2D2C2B, + 0x3A39383736353433, 0x4241403F3E3D3C3B, + 0x4A49484746454443, 0x5251504F4E4D4C4B, }}, + {{ 0x1B1A191817161514, 0x232221201F1E1D1C, + 0x2B2A292827262524, 0x333231302F2E2D2C, + 0x3B3A393837363534, 0x434241403F3E3D3C, + 0x4B4A494847464544, 0x535251504F4E4D4C, }}, + {{ 0x1C1B1A1918171615, 0x24232221201F1E1D, + 0x2C2B2A2928272625, 0x34333231302F2E2D, + 0x3C3B3A3938373635, 0x44434241403F3E3D, + 0x4C4B4A4948474645, 0x54535251504F4E4D, }}, + {{ 0x1D1C1B1A19181716, 0x2524232221201F1E, + 0x2D2C2B2A29282726, 0x3534333231302F2E, + 0x3D3C3B3A39383736, 0x4544434241403F3E, + 0x4D4C4B4A49484746, 0x5554535251504F4E, }}, + {{ 0x1E1D1C1B1A191817, 0x262524232221201F, + 0x2E2D2C2B2A292827, 0x363534333231302F, + 0x3E3D3C3B3A393837, 0x464544434241403F, + 0x4E4D4C4B4A494847, 0x565554535251504F, }}, + {{ 0x1F1E1D1C1B1A1918, 0x2726252423222120, + 0x2F2E2D2C2B2A2928, 0x3736353433323130, + 0x3F3E3D3C3B3A3938, 0x4746454443424140, + 0x4F4E4D4C4B4A4948, 0x5756555453525150, }}, + {{ 0x201F1E1D1C1B1A19, 0x2827262524232221, + 0x302F2E2D2C2B2A29, 0x3837363534333231, + 0x403F3E3D3C3B3A39, 0x4847464544434241, + 0x504F4E4D4C4B4A49, 0x5857565554535251, }}, + {{ 0x21201F1E1D1C1B1A, 0x2928272625242322, + 0x31302F2E2D2C2B2A, 0x3938373635343332, + 0x41403F3E3D3C3B3A, 0x4948474645444342, + 0x51504F4E4D4C4B4A, 0x5958575655545352, }}, + {{ 0x2221201F1E1D1C1B, 0x2A29282726252423, + 0x3231302F2E2D2C2B, 0x3A39383736353433, + 0x4241403F3E3D3C3B, 0x4A49484746454443, + 0x5251504F4E4D4C4B, 0x5A59585756555453, }}, + {{ 0x232221201F1E1D1C, 0x2B2A292827262524, + 0x333231302F2E2D2C, 0x3B3A393837363534, + 0x434241403F3E3D3C, 0x4B4A494847464544, + 0x535251504F4E4D4C, 0x5B5A595857565554, }}, + {{ 0x24232221201F1E1D, 0x2C2B2A2928272625, + 0x34333231302F2E2D, 0x3C3B3A3938373635, + 0x44434241403F3E3D, 0x4C4B4A4948474645, + 0x54535251504F4E4D, 0x5C5B5A5958575655, }}, + {{ 0x2524232221201F1E, 0x2D2C2B2A29282726, + 0x3534333231302F2E, 0x3D3C3B3A39383736, + 0x4544434241403F3E, 0x4D4C4B4A49484746, + 0x5554535251504F4E, 0x5D5C5B5A59585756, }}, + {{ 0x262524232221201F, 0x2E2D2C2B2A292827, + 0x363534333231302F, 0x3E3D3C3B3A393837, + 0x464544434241403F, 0x4E4D4C4B4A494847, + 0x565554535251504F, 0x5E5D5C5B5A595857, }}, + /* k0..k7 */ + {{ 0x2726252423222120, 0x2F2E2D2C2B2A2928, + 0x3736353433323130, 0x3F3E3D3C3B3A3938, + 0x4746454443424140, 0x4F4E4D4C4B4A4948, + 0x5756555453525150, 0x5F5E5D5C5B5A5958, }}, + }; + + const struct x86_test_fpu_registers expected_fpu = { + .st = { + {0x8000000000000000, 0x4000}, /* +2.0 */ + {0x3f00000000000000, 0x0000}, /* 1.654785e-4932 */ + {0x0000000000000000, 0x0000}, /* +0 */ + {0x0000000000000000, 0x8000}, /* -0 */ + {0x8000000000000000, 0x7fff}, /* +inf */ + {0x8000000000000000, 0xffff}, /* -inf */ + {0xc000000000000000, 0xffff}, /* nan */ + /* st(7) will be freed to test tag word better */ + {0x0000000000000000, 0x0000}, /* +0 */ + }, + /* 0000 0011 0111 1011 + * PU OZDI -- unmask divide-by-zero exc. + * RR --------- reserved + * PC ------------ 64-bit precision + * RC -------------- round to nearest + * I ----------------- allow interrupts (unused) + */ + .cw = 0x037b, + /* 1000 0000 1000 0100 + * SPU OZDI -- divide-by-zero exception + * I ---------- interrupt (exception handling) + * C CCC ------------ condition codes + * TO P --------------- top register is 0 + * B -------------------- FPU is busy + */ + .sw = 0x8084, + /* 1110 1010 0101 1000 + * R7R6 R5R4 R3R2 R1R0 + * nz -- non-zero (+2.0) + * sp ---- special (denormal) + * zrzr ------- zeroes + * sp spsp ------------ specials (NaN + infinities) + * em ------------------- empty register + */ + .tw = 0xea58, + /* 0111 1111 -- registers 0 to 6 are used */ + .tw_abridged = 0x7f, + /* FDIV */ + .opcode = 0x0033, + /* random bits for IP/DP write test + * keep it below 48 bits since it can be truncated + */ + .ip = {.fa_64 = 0x00000a9876543210}, + .dp = {.fa_64 = 0x0000056789abcdef}, + }; + + bool need_32 = false, need_64 = false, need_cpuid = false; + + switch (regs) { + case GPREGS_32: + case GPREGS_32_EBP_ESP: + need_32 = true; + break; + case GPREGS_64: + case GPREGS_64_R8: + need_64 = true; + break; + case FPREGS_FPU: + break; + case FPREGS_MM: + case FPREGS_XMM: + case FPREGS_YMM: + case FPREGS_ZMM: + need_cpuid = true; + break; + } + + if (need_32) { +#if defined(__x86_64__) + atf_tc_skip("Test requires 32-bit mode"); +#endif + } + if (need_64) { +#if defined(__i386__) + atf_tc_skip("Test requires 64-bit mode"); +#endif + } + + if (need_cpuid) { + /* verify whether needed instruction sets are supported here */ + unsigned int eax, ebx, ecx, edx; + unsigned int eax7, ebx7, ecx7, edx7; + + DPRINTF("Before invoking cpuid\n"); + if (!__get_cpuid(1, &eax, &ebx, &ecx, &edx)) + atf_tc_skip("CPUID is not supported by the CPU"); + + DPRINTF("cpuid[eax=1]: ECX = %08x, EDX = %08xd\n", ecx, edx); + + switch (regs) { + case FPREGS_ZMM: + /* ZMM is in EAX=7, ECX=0 */ + if (!__get_cpuid_count(7, 0, &eax7, &ebx7, &ecx7, &edx7)) + atf_tc_skip( + "AVX512F is not supported by the CPU"); + DPRINTF("cpuid[eax=7,ecx=0]: EBX = %08x\n", ebx7); + if (!(ebx7 & bit_AVX512F)) + atf_tc_skip( + "AVX512F is not supported by the CPU"); + /*FALLTHROUGH*/ + case FPREGS_YMM: + if (!(ecx & bit_AVX)) + atf_tc_skip("AVX is not supported by the CPU"); + /*FALLTHROUGH*/ + case FPREGS_XMM: + if (!(edx & bit_SSE)) + atf_tc_skip("SSE is not supported by the CPU"); + break; + case FPREGS_MM: + if (!(edx & bit_MMX)) + atf_tc_skip("MMX is not supported by the CPU"); + break; + case GPREGS_32: + case GPREGS_32_EBP_ESP: + case GPREGS_64: + case GPREGS_64_R8: + case FPREGS_FPU: + __unreachable(); + } + } + + DPRINTF("Before forking process PID=%d\n", getpid()); + SYSCALL_REQUIRE((child = fork()) != -1); + if (child == 0) { + union x86_test_register vals[__arraycount(expected)] __aligned(64); + struct x86_test_fpu_registers vals_fpu; + + DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid()); + FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); + + DPRINTF("Before running assembly from child\n"); + switch (regmode) { + case TEST_GETREGS: + case TEST_COREDUMP: + switch (regs) { + case GPREGS_32: + set_gp32_regs(expected); + break; + case GPREGS_32_EBP_ESP: + set_gp32_ebp_esp_regs(expected); + break; + case GPREGS_64: + set_gp64_regs(expected); + break; + case GPREGS_64_R8: + set_gp64_r8_regs(expected); + break; + case FPREGS_FPU: + set_fpu_regs(&expected_fpu); + break; + case FPREGS_MM: + set_mm_regs(expected); + break; + case FPREGS_XMM: + set_xmm_regs(expected); + break; + case FPREGS_YMM: + set_ymm_regs(expected); + break; + case FPREGS_ZMM: + set_zmm_regs(expected); + break; + } + break; + case TEST_SETREGS: + switch (regs) { + case GPREGS_32: + get_gp32_regs(vals); + break; + case GPREGS_32_EBP_ESP: + get_gp32_ebp_esp_regs(vals); + break; + case GPREGS_64: + get_gp64_regs(vals); + break; + case GPREGS_64_R8: + get_gp64_r8_regs(vals); + break; + case FPREGS_FPU: + get_fpu_regs(&vals_fpu); + break; + case FPREGS_MM: + get_mm_regs(vals); + break; + case FPREGS_XMM: + get_xmm_regs(vals); + break; + case FPREGS_YMM: + get_ymm_regs(vals); + break; + case FPREGS_ZMM: + get_zmm_regs(vals); + break; + } + + DPRINTF("Before comparing results\n"); + switch (regs) { + case GPREGS_32: + FORKEE_ASSERT(!memcmp(&vals[5].u32, + &expected[5].u32, sizeof(vals->u32))); + FORKEE_ASSERT(!memcmp(&vals[4].u32, + &expected[4].u32, sizeof(vals->u32))); + FORKEE_ASSERT(!memcmp(&vals[3].u32, + &expected[3].u32, sizeof(vals->u32))); + FORKEE_ASSERT(!memcmp(&vals[2].u32, + &expected[2].u32, sizeof(vals->u32))); + /*FALLTHROUGH*/ + case GPREGS_32_EBP_ESP: + FORKEE_ASSERT(!memcmp(&vals[1].u32, + &expected[1].u32, sizeof(vals->u32))); + FORKEE_ASSERT(!memcmp(&vals[0].u32, + &expected[0].u32, sizeof(vals->u32))); + break; + case GPREGS_64: + case GPREGS_64_R8: + case FPREGS_MM: + FORKEE_ASSERT(!memcmp(&vals[0].u64, + &expected[0].u64, sizeof(vals->u64))); + FORKEE_ASSERT(!memcmp(&vals[1].u64, + &expected[1].u64, sizeof(vals->u64))); + FORKEE_ASSERT(!memcmp(&vals[2].u64, + &expected[2].u64, sizeof(vals->u64))); + FORKEE_ASSERT(!memcmp(&vals[3].u64, + &expected[3].u64, sizeof(vals->u64))); + FORKEE_ASSERT(!memcmp(&vals[4].u64, + &expected[4].u64, sizeof(vals->u64))); + FORKEE_ASSERT(!memcmp(&vals[5].u64, + &expected[5].u64, sizeof(vals->u64))); + FORKEE_ASSERT(!memcmp(&vals[6].u64, + &expected[6].u64, sizeof(vals->u64))); + FORKEE_ASSERT(!memcmp(&vals[7].u64, + &expected[7].u64, sizeof(vals->u64))); + break; + case FPREGS_FPU: + FORKEE_ASSERT(vals_fpu.cw == expected_fpu.cw); + FORKEE_ASSERT(vals_fpu.sw == expected_fpu.sw); + FORKEE_ASSERT(vals_fpu.tw == expected_fpu.tw); + FORKEE_ASSERT(vals_fpu.tw_abridged + == expected_fpu.tw_abridged); + FORKEE_ASSERT(vals_fpu.ip.fa_64 + == expected_fpu.ip.fa_64); + FORKEE_ASSERT(vals_fpu.dp.fa_64 + == expected_fpu.dp.fa_64); + + FORKEE_ASSERT(vals_fpu.st[0].sign_exp + == expected_fpu.st[0].sign_exp); + FORKEE_ASSERT(vals_fpu.st[0].mantissa + == expected_fpu.st[0].mantissa); + FORKEE_ASSERT(vals_fpu.st[1].sign_exp + == expected_fpu.st[1].sign_exp); + FORKEE_ASSERT(vals_fpu.st[1].mantissa + == expected_fpu.st[1].mantissa); + FORKEE_ASSERT(vals_fpu.st[2].sign_exp + == expected_fpu.st[2].sign_exp); + FORKEE_ASSERT(vals_fpu.st[2].mantissa + == expected_fpu.st[2].mantissa); + FORKEE_ASSERT(vals_fpu.st[3].sign_exp + == expected_fpu.st[3].sign_exp); + FORKEE_ASSERT(vals_fpu.st[3].mantissa + == expected_fpu.st[3].mantissa); + FORKEE_ASSERT(vals_fpu.st[4].sign_exp + == expected_fpu.st[4].sign_exp); + FORKEE_ASSERT(vals_fpu.st[4].mantissa + == expected_fpu.st[4].mantissa); + FORKEE_ASSERT(vals_fpu.st[5].sign_exp + == expected_fpu.st[5].sign_exp); + FORKEE_ASSERT(vals_fpu.st[5].mantissa + == expected_fpu.st[5].mantissa); + FORKEE_ASSERT(vals_fpu.st[6].sign_exp + == expected_fpu.st[6].sign_exp); + FORKEE_ASSERT(vals_fpu.st[6].mantissa + == expected_fpu.st[6].mantissa); + /* st(7) is left empty == undefined */ + break; + case FPREGS_XMM: + FORKEE_ASSERT(!memcmp(&vals[0].xmm, + &expected[0].xmm, sizeof(vals->xmm))); + FORKEE_ASSERT(!memcmp(&vals[1].xmm, + &expected[1].xmm, sizeof(vals->xmm))); + FORKEE_ASSERT(!memcmp(&vals[2].xmm, + &expected[2].xmm, sizeof(vals->xmm))); + FORKEE_ASSERT(!memcmp(&vals[3].xmm, + &expected[3].xmm, sizeof(vals->xmm))); + FORKEE_ASSERT(!memcmp(&vals[4].xmm, + &expected[4].xmm, sizeof(vals->xmm))); + FORKEE_ASSERT(!memcmp(&vals[5].xmm, + &expected[5].xmm, sizeof(vals->xmm))); + FORKEE_ASSERT(!memcmp(&vals[6].xmm, + &expected[6].xmm, sizeof(vals->xmm))); + FORKEE_ASSERT(!memcmp(&vals[7].xmm, + &expected[7].xmm, sizeof(vals->xmm))); +#if defined(__x86_64__) + FORKEE_ASSERT(!memcmp(&vals[8].xmm, + &expected[8].xmm, sizeof(vals->xmm))); + FORKEE_ASSERT(!memcmp(&vals[9].xmm, + &expected[9].xmm, sizeof(vals->xmm))); + FORKEE_ASSERT(!memcmp(&vals[10].xmm, + &expected[10].xmm, sizeof(vals->xmm))); + FORKEE_ASSERT(!memcmp(&vals[11].xmm, + &expected[11].xmm, sizeof(vals->xmm))); + FORKEE_ASSERT(!memcmp(&vals[12].xmm, + &expected[12].xmm, sizeof(vals->xmm))); + FORKEE_ASSERT(!memcmp(&vals[13].xmm, + &expected[13].xmm, sizeof(vals->xmm))); + FORKEE_ASSERT(!memcmp(&vals[14].xmm, + &expected[14].xmm, sizeof(vals->xmm))); + FORKEE_ASSERT(!memcmp(&vals[15].xmm, + &expected[15].xmm, sizeof(vals->xmm))); +#endif + break; + case FPREGS_YMM: + FORKEE_ASSERT(!memcmp(&vals[0].ymm, + &expected[0].ymm, sizeof(vals->ymm))); + FORKEE_ASSERT(!memcmp(&vals[1].ymm, + &expected[1].ymm, sizeof(vals->ymm))); + FORKEE_ASSERT(!memcmp(&vals[2].ymm, + &expected[2].ymm, sizeof(vals->ymm))); + FORKEE_ASSERT(!memcmp(&vals[3].ymm, + &expected[3].ymm, sizeof(vals->ymm))); + FORKEE_ASSERT(!memcmp(&vals[4].ymm, + &expected[4].ymm, sizeof(vals->ymm))); + FORKEE_ASSERT(!memcmp(&vals[5].ymm, + &expected[5].ymm, sizeof(vals->ymm))); + FORKEE_ASSERT(!memcmp(&vals[6].ymm, + &expected[6].ymm, sizeof(vals->ymm))); + FORKEE_ASSERT(!memcmp(&vals[7].ymm, + &expected[7].ymm, sizeof(vals->ymm))); +#if defined(__x86_64__) + FORKEE_ASSERT(!memcmp(&vals[8].ymm, + &expected[8].ymm, sizeof(vals->ymm))); + FORKEE_ASSERT(!memcmp(&vals[9].ymm, + &expected[9].ymm, sizeof(vals->ymm))); + FORKEE_ASSERT(!memcmp(&vals[10].ymm, + &expected[10].ymm, sizeof(vals->ymm))); + FORKEE_ASSERT(!memcmp(&vals[11].ymm, + &expected[11].ymm, sizeof(vals->ymm))); + FORKEE_ASSERT(!memcmp(&vals[12].ymm, + &expected[12].ymm, sizeof(vals->ymm))); + FORKEE_ASSERT(!memcmp(&vals[13].ymm, + &expected[13].ymm, sizeof(vals->ymm))); + FORKEE_ASSERT(!memcmp(&vals[14].ymm, + &expected[14].ymm, sizeof(vals->ymm))); + FORKEE_ASSERT(!memcmp(&vals[15].ymm, + &expected[15].ymm, sizeof(vals->ymm))); +#endif + break; + case FPREGS_ZMM: + FORKEE_ASSERT(!memcmp(&vals[0].zmm, + &expected[0].zmm, sizeof(vals->zmm))); + FORKEE_ASSERT(!memcmp(&vals[1].zmm, + &expected[1].zmm, sizeof(vals->zmm))); + FORKEE_ASSERT(!memcmp(&vals[2].zmm, + &expected[2].zmm, sizeof(vals->zmm))); + FORKEE_ASSERT(!memcmp(&vals[3].zmm, + &expected[3].zmm, sizeof(vals->zmm))); + FORKEE_ASSERT(!memcmp(&vals[4].zmm, + &expected[4].zmm, sizeof(vals->zmm))); + FORKEE_ASSERT(!memcmp(&vals[5].zmm, + &expected[5].zmm, sizeof(vals->zmm))); + FORKEE_ASSERT(!memcmp(&vals[6].zmm, + &expected[6].zmm, sizeof(vals->zmm))); + FORKEE_ASSERT(!memcmp(&vals[7].zmm, + &expected[7].zmm, sizeof(vals->zmm))); +#if defined(__x86_64__) + FORKEE_ASSERT(!memcmp(&vals[8].zmm, + &expected[8].zmm, sizeof(vals->zmm))); + FORKEE_ASSERT(!memcmp(&vals[9].zmm, + &expected[9].zmm, sizeof(vals->zmm))); + FORKEE_ASSERT(!memcmp(&vals[10].zmm, + &expected[10].zmm, sizeof(vals->zmm))); + FORKEE_ASSERT(!memcmp(&vals[11].zmm, + &expected[11].zmm, sizeof(vals->zmm))); + FORKEE_ASSERT(!memcmp(&vals[12].zmm, + &expected[12].zmm, sizeof(vals->zmm))); + FORKEE_ASSERT(!memcmp(&vals[13].zmm, + &expected[13].zmm, sizeof(vals->zmm))); + FORKEE_ASSERT(!memcmp(&vals[14].zmm, + &expected[14].zmm, sizeof(vals->zmm))); + FORKEE_ASSERT(!memcmp(&vals[15].zmm, + &expected[15].zmm, sizeof(vals->zmm))); + FORKEE_ASSERT(!memcmp(&vals[16].zmm, + &expected[16].zmm, sizeof(vals->zmm))); + FORKEE_ASSERT(!memcmp(&vals[17].zmm, + &expected[17].zmm, sizeof(vals->zmm))); + FORKEE_ASSERT(!memcmp(&vals[18].zmm, + &expected[18].zmm, sizeof(vals->zmm))); + FORKEE_ASSERT(!memcmp(&vals[19].zmm, + &expected[19].zmm, sizeof(vals->zmm))); + FORKEE_ASSERT(!memcmp(&vals[20].zmm, + &expected[20].zmm, sizeof(vals->zmm))); + FORKEE_ASSERT(!memcmp(&vals[21].zmm, + &expected[21].zmm, sizeof(vals->zmm))); + FORKEE_ASSERT(!memcmp(&vals[22].zmm, + &expected[22].zmm, sizeof(vals->zmm))); + FORKEE_ASSERT(!memcmp(&vals[23].zmm, + &expected[23].zmm, sizeof(vals->zmm))); + FORKEE_ASSERT(!memcmp(&vals[24].zmm, + &expected[24].zmm, sizeof(vals->zmm))); + FORKEE_ASSERT(!memcmp(&vals[25].zmm, + &expected[25].zmm, sizeof(vals->zmm))); + FORKEE_ASSERT(!memcmp(&vals[26].zmm, + &expected[26].zmm, sizeof(vals->zmm))); + FORKEE_ASSERT(!memcmp(&vals[27].zmm, + &expected[27].zmm, sizeof(vals->zmm))); + FORKEE_ASSERT(!memcmp(&vals[28].zmm, + &expected[28].zmm, sizeof(vals->zmm))); + FORKEE_ASSERT(!memcmp(&vals[29].zmm, + &expected[29].zmm, sizeof(vals->zmm))); + FORKEE_ASSERT(!memcmp(&vals[30].zmm, + &expected[30].zmm, sizeof(vals->zmm))); + FORKEE_ASSERT(!memcmp(&vals[31].zmm, + &expected[31].zmm, sizeof(vals->zmm))); +#endif + /* k0..k7 */ + FORKEE_ASSERT(vals[32].zmm.a == expected[32].zmm.a); + FORKEE_ASSERT(vals[32].zmm.b == expected[32].zmm.b); + FORKEE_ASSERT(vals[32].zmm.c == expected[32].zmm.c); + FORKEE_ASSERT(vals[32].zmm.d == expected[32].zmm.d); + FORKEE_ASSERT(vals[32].zmm.e == expected[32].zmm.e); + FORKEE_ASSERT(vals[32].zmm.f == expected[32].zmm.f); + FORKEE_ASSERT(vals[32].zmm.g == expected[32].zmm.g); + FORKEE_ASSERT(vals[32].zmm.h == expected[32].zmm.h); + break; + } + break; + } + + DPRINTF("Before exiting of the child process\n"); + _exit(exitval); + } + DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_stopped(status, sigval); + + if (regset == TEST_XSTATE) { + switch (regs) { + case FPREGS_FPU: + case FPREGS_MM: + xst_flags |= XCR0_X87; + break; + case FPREGS_ZMM: + xst_flags |= XCR0_Opmask | XCR0_ZMM_Hi256; +#if defined(__x86_64__) + xst_flags |= XCR0_Hi16_ZMM; +#endif + /*FALLTHROUGH*/ + case FPREGS_YMM: + xst_flags |= XCR0_YMM_Hi128; + /*FALLTHROUGH*/ + case FPREGS_XMM: + xst_flags |= XCR0_SSE; + break; + case GPREGS_32: + case GPREGS_32_EBP_ESP: + case GPREGS_64: + case GPREGS_64_R8: + __unreachable(); + break; + } + } + + switch (regmode) { + case TEST_GETREGS: + case TEST_SETREGS: + if (regset == TEST_GPREGS || regs == FPREGS_FPU) { + DPRINTF("Call GETREGS for the child process\n"); + SYSCALL_REQUIRE(ptrace(PT_GETREGS, child, &gpr, 0) + != -1); + } + + switch (regset) { + case TEST_GPREGS: + /* already handled above */ + break; + case TEST_XMMREGS: +#if defined(__i386__) + ATF_REQUIRE(regs >= FPREGS_FPU && regs < FPREGS_YMM); + DPRINTF("Call GETXMMREGS for the child process\n"); + SYSCALL_REQUIRE(ptrace(PT_GETXMMREGS, child, &xmm, 0) + != -1); + fxs = &xmm.fxstate; + break; +#else + /*FALLTHROUGH*/ +#endif + case TEST_FPREGS: +#if defined(__x86_64__) + ATF_REQUIRE(regs >= FPREGS_FPU && regs < FPREGS_YMM); + fxs = &fpr.fxstate; +#else + ATF_REQUIRE(regs >= FPREGS_FPU && regs < FPREGS_XMM); +#endif + DPRINTF("Call GETFPREGS for the child process\n"); + SYSCALL_REQUIRE(ptrace(PT_GETFPREGS, child, &fpr, 0) + != -1); + break; + case TEST_XSTATE: + ATF_REQUIRE(regs >= FPREGS_FPU); + iov.iov_base = &xst; + iov.iov_len = sizeof(xst); + + DPRINTF("Call GETXSTATE for the child process\n"); + SYSCALL_REQUIRE(ptrace(PT_GETXSTATE, child, &iov, 0) + != -1); + + ATF_REQUIRE((xst.xs_rfbm & xst_flags) == xst_flags); + switch (regmode) { + case TEST_SETREGS: + xst.xs_rfbm = xst_flags; + xst.xs_xstate_bv = xst_flags; + break; + case TEST_GETREGS: + ATF_REQUIRE((xst.xs_xstate_bv & xst_flags) + == xst_flags); + break; + case TEST_COREDUMP: + __unreachable(); + break; + } + + fxs = &xst.xs_fxsave; + break; + } + break; + case TEST_COREDUMP: + SYSCALL_REQUIRE((core_fd = mkstemp(core_path)) != -1); + close(core_fd); + + DPRINTF("Call DUMPCORE for the child process\n"); + SYSCALL_REQUIRE(ptrace(PT_DUMPCORE, child, core_path, + strlen(core_path)) != -1); + + if (regset == TEST_GPREGS || regs == FPREGS_FPU) { + DPRINTF("Parse core file for PT_GETREGS\n"); + ATF_REQUIRE_EQ(core_find_note(core_path, + "NetBSD-CORE@*", PT_GETREGS, &gpr, sizeof(gpr)), + sizeof(gpr)); + } + + switch (regset) { + case TEST_GPREGS: + /* handled above */ + break; + case TEST_XMMREGS: +#if defined(__i386__) + ATF_REQUIRE(regs >= FPREGS_FPU && regs < FPREGS_YMM); + unlink(core_path); + atf_tc_skip("XMMREGS not supported in core dumps"); + break; +#else + /*FALLTHROUGH*/ +#endif + case TEST_FPREGS: +#if defined(__x86_64__) + ATF_REQUIRE(regs >= FPREGS_FPU && regs < FPREGS_YMM); + fxs = &fpr.fxstate; +#else + ATF_REQUIRE(regs >= FPREGS_FPU && regs < FPREGS_XMM); +#endif + DPRINTF("Parse core file for PT_GETFPREGS\n"); + ATF_REQUIRE_EQ(core_find_note(core_path, + "NetBSD-CORE@*", PT_GETFPREGS, &fpr, sizeof(fpr)), + sizeof(fpr)); + break; + case TEST_XSTATE: + ATF_REQUIRE(regs >= FPREGS_FPU); + DPRINTF("Parse core file for PT_GETXSTATE\n"); + ATF_REQUIRE_EQ(core_find_note(core_path, + "NetBSD-CORE@*", PT_GETXSTATE, &xst, sizeof(xst)), + sizeof(xst)); + ATF_REQUIRE((xst.xs_xstate_bv & xst_flags) + == xst_flags); + fxs = &xst.xs_fxsave; + break; + } + unlink(core_path); + } + +#if defined(__x86_64__) +#define ST_EXP(n) fxs->fx_87_ac[n].r.f87_exp_sign +#define ST_MAN(n) fxs->fx_87_ac[n].r.f87_mantissa +#else +#define ST_EXP(n) *( \ + regset == TEST_FPREGS \ + ? &fpr.fstate.s87_ac[n].f87_exp_sign \ + : &fxs->fx_87_ac[n].r.f87_exp_sign \ + ) +#define ST_MAN(n) *( \ + regset == TEST_FPREGS \ + ? &fpr.fstate.s87_ac[n].f87_mantissa \ + : &fxs->fx_87_ac[n].r.f87_mantissa \ + ) +#endif + + switch (regmode) { + case TEST_GETREGS: + case TEST_COREDUMP: + switch (regs) { + case GPREGS_32: +#if defined(__i386__) + ATF_CHECK_EQ((uint32_t)gpr.r_eax, expected[0].u32); + ATF_CHECK_EQ((uint32_t)gpr.r_ebx, expected[1].u32); + ATF_CHECK_EQ((uint32_t)gpr.r_ecx, expected[2].u32); + ATF_CHECK_EQ((uint32_t)gpr.r_edx, expected[3].u32); + ATF_CHECK_EQ((uint32_t)gpr.r_esi, expected[4].u32); + ATF_CHECK_EQ((uint32_t)gpr.r_edi, expected[5].u32); +#endif + break; + case GPREGS_32_EBP_ESP: +#if defined(__i386__) + ATF_CHECK_EQ((uint32_t)gpr.r_esp, expected[0].u32); + ATF_CHECK_EQ((uint32_t)gpr.r_ebp, expected[1].u32); +#endif + break; + case GPREGS_64: +#if defined(__x86_64__) + ATF_CHECK_EQ((uint64_t)gpr.regs[_REG_RAX], + expected[0].u64); + ATF_CHECK_EQ((uint64_t)gpr.regs[_REG_RBX], + expected[1].u64); + ATF_CHECK_EQ((uint64_t)gpr.regs[_REG_RCX], + expected[2].u64); + ATF_CHECK_EQ((uint64_t)gpr.regs[_REG_RDX], + expected[3].u64); + ATF_CHECK_EQ((uint64_t)gpr.regs[_REG_RSI], + expected[4].u64); + ATF_CHECK_EQ((uint64_t)gpr.regs[_REG_RDI], + expected[5].u64); + ATF_CHECK_EQ((uint64_t)gpr.regs[_REG_RSP], + expected[6].u64); + ATF_CHECK_EQ((uint64_t)gpr.regs[_REG_RBP], + expected[7].u64); +#endif + break; + case GPREGS_64_R8: +#if defined(__x86_64__) + ATF_CHECK_EQ((uint64_t)gpr.regs[_REG_R8], + expected[0].u64); + ATF_CHECK_EQ((uint64_t)gpr.regs[_REG_R9], + expected[1].u64); + ATF_CHECK_EQ((uint64_t)gpr.regs[_REG_R10], + expected[2].u64); + ATF_CHECK_EQ((uint64_t)gpr.regs[_REG_R11], + expected[3].u64); + ATF_CHECK_EQ((uint64_t)gpr.regs[_REG_R12], + expected[4].u64); + ATF_CHECK_EQ((uint64_t)gpr.regs[_REG_R13], + expected[5].u64); + ATF_CHECK_EQ((uint64_t)gpr.regs[_REG_R14], + expected[6].u64); + ATF_CHECK_EQ((uint64_t)gpr.regs[_REG_R15], + expected[7].u64); +#endif + break; + case FPREGS_FPU: +#if defined(__i386__) + if (regset == TEST_FPREGS) { + /* GETFPREGS on i386 */ + ATF_CHECK_EQ(fpr.fstate.s87_cw, + expected_fpu.cw); + ATF_CHECK_EQ(fpr.fstate.s87_sw, + expected_fpu.sw); + ATF_CHECK_EQ(fpr.fstate.s87_tw, + expected_fpu.tw); + ATF_CHECK_EQ(fpr.fstate.s87_opcode, + expected_fpu.opcode); + ATF_CHECK_EQ(fpr.fstate.s87_ip.fa_32.fa_off, + (uint32_t)gpr.r_eip - 3); + ATF_CHECK_EQ(fpr.fstate.s87_dp.fa_32.fa_off, + (uint32_t)&x86_test_zero); + /* note: fa_seg is missing on newer CPUs */ + } else +#endif + { + /* amd64 or GETXSTATE on i386 */ + ATF_CHECK_EQ(fxs->fx_cw, expected_fpu.cw); + ATF_CHECK_EQ(fxs->fx_sw, expected_fpu.sw); + ATF_CHECK_EQ(fxs->fx_tw, + expected_fpu.tw_abridged); + ATF_CHECK_EQ(fxs->fx_opcode, + expected_fpu.opcode); +#if defined(__x86_64__) + ATF_CHECK_EQ(fxs->fx_ip.fa_64, + ((uint64_t)gpr.regs[_REG_RIP]) - 3); + ATF_CHECK_EQ(fxs->fx_dp.fa_64, + (uint64_t)&x86_test_zero); +#else + ATF_CHECK_EQ(fxs->fx_ip.fa_32.fa_off, + (uint32_t)gpr.r_eip - 3); + ATF_CHECK_EQ(fxs->fx_dp.fa_32.fa_off, + (uint32_t)&x86_test_zero); + /* note: fa_seg is missing on newer CPUs */ +#endif + } + + ATF_CHECK_EQ(ST_EXP(0), expected_fpu.st[0].sign_exp); + ATF_CHECK_EQ(ST_MAN(0), expected_fpu.st[0].mantissa); + ATF_CHECK_EQ(ST_EXP(1), expected_fpu.st[1].sign_exp); + ATF_CHECK_EQ(ST_MAN(1), expected_fpu.st[1].mantissa); + ATF_CHECK_EQ(ST_EXP(2), expected_fpu.st[2].sign_exp); + ATF_CHECK_EQ(ST_MAN(2), expected_fpu.st[2].mantissa); + ATF_CHECK_EQ(ST_EXP(3), expected_fpu.st[3].sign_exp); + ATF_CHECK_EQ(ST_MAN(3), expected_fpu.st[3].mantissa); + ATF_CHECK_EQ(ST_EXP(4), expected_fpu.st[4].sign_exp); + ATF_CHECK_EQ(ST_MAN(4), expected_fpu.st[4].mantissa); + ATF_CHECK_EQ(ST_EXP(5), expected_fpu.st[5].sign_exp); + ATF_CHECK_EQ(ST_MAN(5), expected_fpu.st[5].mantissa); + ATF_CHECK_EQ(ST_EXP(6), expected_fpu.st[6].sign_exp); + ATF_CHECK_EQ(ST_MAN(6), expected_fpu.st[6].mantissa); + ATF_CHECK_EQ(ST_EXP(7), expected_fpu.st[7].sign_exp); + ATF_CHECK_EQ(ST_MAN(7), expected_fpu.st[7].mantissa); + break; + case FPREGS_MM: + ATF_CHECK_EQ(ST_MAN(0), expected[0].u64); + ATF_CHECK_EQ(ST_MAN(1), expected[1].u64); + ATF_CHECK_EQ(ST_MAN(2), expected[2].u64); + ATF_CHECK_EQ(ST_MAN(3), expected[3].u64); + ATF_CHECK_EQ(ST_MAN(4), expected[4].u64); + ATF_CHECK_EQ(ST_MAN(5), expected[5].u64); + ATF_CHECK_EQ(ST_MAN(6), expected[6].u64); + ATF_CHECK_EQ(ST_MAN(7), expected[7].u64); + break; + case FPREGS_ZMM: + /* zmm0..zmm15 are split between xmm, ymm_hi128 and zmm_hi256 */ + ATF_CHECK(!memcmp(&xst.xs_zmm_hi256.xs_zmm[0], + &expected[0].zmm.e, sizeof(expected->zmm)/2)); + ATF_CHECK(!memcmp(&xst.xs_zmm_hi256.xs_zmm[1], + &expected[1].zmm.e, sizeof(expected->zmm)/2)); + ATF_CHECK(!memcmp(&xst.xs_zmm_hi256.xs_zmm[2], + &expected[2].zmm.e, sizeof(expected->zmm)/2)); + ATF_CHECK(!memcmp(&xst.xs_zmm_hi256.xs_zmm[3], + &expected[3].zmm.e, sizeof(expected->zmm)/2)); + ATF_CHECK(!memcmp(&xst.xs_zmm_hi256.xs_zmm[4], + &expected[4].zmm.e, sizeof(expected->zmm)/2)); + ATF_CHECK(!memcmp(&xst.xs_zmm_hi256.xs_zmm[5], + &expected[5].zmm.e, sizeof(expected->zmm)/2)); + ATF_CHECK(!memcmp(&xst.xs_zmm_hi256.xs_zmm[6], + &expected[6].zmm.e, sizeof(expected->zmm)/2)); + ATF_CHECK(!memcmp(&xst.xs_zmm_hi256.xs_zmm[7], + &expected[7].zmm.e, sizeof(expected->zmm)/2)); +#if defined(__x86_64__) + ATF_CHECK(!memcmp(&xst.xs_zmm_hi256.xs_zmm[8], + &expected[8].zmm.e, sizeof(expected->zmm)/2)); + ATF_CHECK(!memcmp(&xst.xs_zmm_hi256.xs_zmm[9], + &expected[9].zmm.e, sizeof(expected->zmm)/2)); + ATF_CHECK(!memcmp(&xst.xs_zmm_hi256.xs_zmm[10], + &expected[10].zmm.e, sizeof(expected->zmm)/2)); + ATF_CHECK(!memcmp(&xst.xs_zmm_hi256.xs_zmm[11], + &expected[11].zmm.e, sizeof(expected->zmm)/2)); + ATF_CHECK(!memcmp(&xst.xs_zmm_hi256.xs_zmm[12], + &expected[12].zmm.e, sizeof(expected->zmm)/2)); + ATF_CHECK(!memcmp(&xst.xs_zmm_hi256.xs_zmm[13], + &expected[13].zmm.e, sizeof(expected->zmm)/2)); + ATF_CHECK(!memcmp(&xst.xs_zmm_hi256.xs_zmm[14], + &expected[14].zmm.e, sizeof(expected->zmm)/2)); + ATF_CHECK(!memcmp(&xst.xs_zmm_hi256.xs_zmm[15], + &expected[15].zmm.e, sizeof(expected->zmm)/2)); + /* zmm16..zmm31 are stored as a whole */ + ATF_CHECK(!memcmp(&xst.xs_hi16_zmm.xs_hi16_zmm[0], + &expected[16].zmm, sizeof(expected->zmm))); + ATF_CHECK(!memcmp(&xst.xs_hi16_zmm.xs_hi16_zmm[1], + &expected[17].zmm, sizeof(expected->zmm))); + ATF_CHECK(!memcmp(&xst.xs_hi16_zmm.xs_hi16_zmm[2], + &expected[18].zmm, sizeof(expected->zmm))); + ATF_CHECK(!memcmp(&xst.xs_hi16_zmm.xs_hi16_zmm[3], + &expected[19].zmm, sizeof(expected->zmm))); + ATF_CHECK(!memcmp(&xst.xs_hi16_zmm.xs_hi16_zmm[4], + &expected[20].zmm, sizeof(expected->zmm))); + ATF_CHECK(!memcmp(&xst.xs_hi16_zmm.xs_hi16_zmm[5], + &expected[21].zmm, sizeof(expected->zmm))); + ATF_CHECK(!memcmp(&xst.xs_hi16_zmm.xs_hi16_zmm[6], + &expected[22].zmm, sizeof(expected->zmm))); + ATF_CHECK(!memcmp(&xst.xs_hi16_zmm.xs_hi16_zmm[7], + &expected[23].zmm, sizeof(expected->zmm))); + ATF_CHECK(!memcmp(&xst.xs_hi16_zmm.xs_hi16_zmm[8], + &expected[24].zmm, sizeof(expected->zmm))); + ATF_CHECK(!memcmp(&xst.xs_hi16_zmm.xs_hi16_zmm[9], + &expected[25].zmm, sizeof(expected->zmm))); + ATF_CHECK(!memcmp(&xst.xs_hi16_zmm.xs_hi16_zmm[10], + &expected[26].zmm, sizeof(expected->zmm))); + ATF_CHECK(!memcmp(&xst.xs_hi16_zmm.xs_hi16_zmm[11], + &expected[27].zmm, sizeof(expected->zmm))); + ATF_CHECK(!memcmp(&xst.xs_hi16_zmm.xs_hi16_zmm[12], + &expected[28].zmm, sizeof(expected->zmm))); + ATF_CHECK(!memcmp(&xst.xs_hi16_zmm.xs_hi16_zmm[13], + &expected[29].zmm, sizeof(expected->zmm))); + ATF_CHECK(!memcmp(&xst.xs_hi16_zmm.xs_hi16_zmm[14], + &expected[30].zmm, sizeof(expected->zmm))); + ATF_CHECK(!memcmp(&xst.xs_hi16_zmm.xs_hi16_zmm[15], + &expected[31].zmm, sizeof(expected->zmm))); +#endif + /* k0..k7 */ + ATF_CHECK(xst.xs_opmask.xs_k[0] == expected[32].zmm.a); + ATF_CHECK(xst.xs_opmask.xs_k[1] == expected[32].zmm.b); + ATF_CHECK(xst.xs_opmask.xs_k[2] == expected[32].zmm.c); + ATF_CHECK(xst.xs_opmask.xs_k[3] == expected[32].zmm.d); + ATF_CHECK(xst.xs_opmask.xs_k[4] == expected[32].zmm.e); + ATF_CHECK(xst.xs_opmask.xs_k[5] == expected[32].zmm.f); + ATF_CHECK(xst.xs_opmask.xs_k[6] == expected[32].zmm.g); + ATF_CHECK(xst.xs_opmask.xs_k[7] == expected[32].zmm.h); + /*FALLTHROUGH*/ + case FPREGS_YMM: + ATF_CHECK(!memcmp(&xst.xs_ymm_hi128.xs_ymm[0], + &expected[0].ymm.c, sizeof(expected->ymm)/2)); + ATF_CHECK(!memcmp(&xst.xs_ymm_hi128.xs_ymm[1], + &expected[1].ymm.c, sizeof(expected->ymm)/2)); + ATF_CHECK(!memcmp(&xst.xs_ymm_hi128.xs_ymm[2], + &expected[2].ymm.c, sizeof(expected->ymm)/2)); + ATF_CHECK(!memcmp(&xst.xs_ymm_hi128.xs_ymm[3], + &expected[3].ymm.c, sizeof(expected->ymm)/2)); + ATF_CHECK(!memcmp(&xst.xs_ymm_hi128.xs_ymm[4], + &expected[4].ymm.c, sizeof(expected->ymm)/2)); + ATF_CHECK(!memcmp(&xst.xs_ymm_hi128.xs_ymm[5], + &expected[5].ymm.c, sizeof(expected->ymm)/2)); + ATF_CHECK(!memcmp(&xst.xs_ymm_hi128.xs_ymm[6], + &expected[6].ymm.c, sizeof(expected->ymm)/2)); + ATF_CHECK(!memcmp(&xst.xs_ymm_hi128.xs_ymm[7], + &expected[7].ymm.c, sizeof(expected->ymm)/2)); +#if defined(__x86_64__) + ATF_CHECK(!memcmp(&xst.xs_ymm_hi128.xs_ymm[8], + &expected[8].ymm.c, sizeof(expected->ymm)/2)); + ATF_CHECK(!memcmp(&xst.xs_ymm_hi128.xs_ymm[9], + &expected[9].ymm.c, sizeof(expected->ymm)/2)); + ATF_CHECK(!memcmp(&xst.xs_ymm_hi128.xs_ymm[10], + &expected[10].ymm.c, sizeof(expected->ymm)/2)); + ATF_CHECK(!memcmp(&xst.xs_ymm_hi128.xs_ymm[11], + &expected[11].ymm.c, sizeof(expected->ymm)/2)); + ATF_CHECK(!memcmp(&xst.xs_ymm_hi128.xs_ymm[12], + &expected[12].ymm.c, sizeof(expected->ymm)/2)); + ATF_CHECK(!memcmp(&xst.xs_ymm_hi128.xs_ymm[13], + &expected[13].ymm.c, sizeof(expected->ymm)/2)); + ATF_CHECK(!memcmp(&xst.xs_ymm_hi128.xs_ymm[14], + &expected[14].ymm.c, sizeof(expected->ymm)/2)); + ATF_CHECK(!memcmp(&xst.xs_ymm_hi128.xs_ymm[15], + &expected[15].ymm.c, sizeof(expected->ymm)/2)); +#endif + /*FALLTHROUGH*/ + case FPREGS_XMM: + ATF_CHECK(!memcmp(&fxs->fx_xmm[0], &expected[0].ymm.a, + sizeof(expected->ymm)/2)); + ATF_CHECK(!memcmp(&fxs->fx_xmm[1], &expected[1].ymm.a, + sizeof(expected->ymm)/2)); + ATF_CHECK(!memcmp(&fxs->fx_xmm[2], &expected[2].ymm.a, + sizeof(expected->ymm)/2)); + ATF_CHECK(!memcmp(&fxs->fx_xmm[3], &expected[3].ymm.a, + sizeof(expected->ymm)/2)); + ATF_CHECK(!memcmp(&fxs->fx_xmm[4], &expected[4].ymm.a, + sizeof(expected->ymm)/2)); + ATF_CHECK(!memcmp(&fxs->fx_xmm[5], &expected[5].ymm.a, + sizeof(expected->ymm)/2)); + ATF_CHECK(!memcmp(&fxs->fx_xmm[6], &expected[6].ymm.a, + sizeof(expected->ymm)/2)); + ATF_CHECK(!memcmp(&fxs->fx_xmm[7], &expected[7].ymm.a, + sizeof(expected->ymm)/2)); +#if defined(__x86_64__) + ATF_CHECK(!memcmp(&fxs->fx_xmm[8], &expected[8].ymm.a, + sizeof(expected->ymm)/2)); + ATF_CHECK(!memcmp(&fxs->fx_xmm[9], &expected[9].ymm.a, + sizeof(expected->ymm)/2)); + ATF_CHECK(!memcmp(&fxs->fx_xmm[10], &expected[10].ymm.a, + sizeof(expected->ymm)/2)); + ATF_CHECK(!memcmp(&fxs->fx_xmm[11], &expected[11].ymm.a, + sizeof(expected->ymm)/2)); + ATF_CHECK(!memcmp(&fxs->fx_xmm[12], &expected[12].ymm.a, + sizeof(expected->ymm)/2)); + ATF_CHECK(!memcmp(&fxs->fx_xmm[13], &expected[13].ymm.a, + sizeof(expected->ymm)/2)); + ATF_CHECK(!memcmp(&fxs->fx_xmm[14], &expected[14].ymm.a, + sizeof(expected->ymm)/2)); + ATF_CHECK(!memcmp(&fxs->fx_xmm[15], &expected[15].ymm.a, + sizeof(expected->ymm)/2)); +#endif + break; + } + break; + case TEST_SETREGS: + switch (regs) { + case GPREGS_32: +#if defined(__i386__) + gpr.r_eax = expected[0].u32; + gpr.r_ebx = expected[1].u32; + gpr.r_ecx = expected[2].u32; + gpr.r_edx = expected[3].u32; + gpr.r_esi = expected[4].u32; + gpr.r_edi = expected[5].u32; +#endif + break; + case GPREGS_32_EBP_ESP: +#if defined(__i386__) + gpr.r_esp = expected[0].u32; + gpr.r_ebp = expected[1].u32; +#endif + break; + case GPREGS_64: +#if defined(__x86_64__) + gpr.regs[_REG_RAX] = expected[0].u64; + gpr.regs[_REG_RBX] = expected[1].u64; + gpr.regs[_REG_RCX] = expected[2].u64; + gpr.regs[_REG_RDX] = expected[3].u64; + gpr.regs[_REG_RSI] = expected[4].u64; + gpr.regs[_REG_RDI] = expected[5].u64; + gpr.regs[_REG_RSP] = expected[6].u64; + gpr.regs[_REG_RBP] = expected[7].u64; +#endif + break; + case GPREGS_64_R8: +#if defined(__x86_64__) + gpr.regs[_REG_R8] = expected[0].u64; + gpr.regs[_REG_R9] = expected[1].u64; + gpr.regs[_REG_R10] = expected[2].u64; + gpr.regs[_REG_R11] = expected[3].u64; + gpr.regs[_REG_R12] = expected[4].u64; + gpr.regs[_REG_R13] = expected[5].u64; + gpr.regs[_REG_R14] = expected[6].u64; + gpr.regs[_REG_R15] = expected[7].u64; +#endif + break; + case FPREGS_FPU: +#if defined(__i386__) + if (regset == TEST_FPREGS) { + /* SETFPREGS on i386 */ + fpr.fstate.s87_cw = expected_fpu.cw; + fpr.fstate.s87_sw = expected_fpu.sw; + fpr.fstate.s87_tw = expected_fpu.tw; + fpr.fstate.s87_opcode = expected_fpu.opcode; + fpr.fstate.s87_ip = expected_fpu.ip; + fpr.fstate.s87_dp = expected_fpu.dp; + } else +#endif /*defined(__i386__)*/ + { + /* amd64 or SETXSTATE on i386 */ + fxs->fx_cw = expected_fpu.cw; + fxs->fx_sw = expected_fpu.sw; + fxs->fx_tw = expected_fpu.tw_abridged; + fxs->fx_opcode = expected_fpu.opcode; + fxs->fx_ip = expected_fpu.ip; + fxs->fx_dp = expected_fpu.dp; + } + + ST_EXP(0) = expected_fpu.st[0].sign_exp; + ST_MAN(0) = expected_fpu.st[0].mantissa; + ST_EXP(1) = expected_fpu.st[1].sign_exp; + ST_MAN(1) = expected_fpu.st[1].mantissa; + ST_EXP(2) = expected_fpu.st[2].sign_exp; + ST_MAN(2) = expected_fpu.st[2].mantissa; + ST_EXP(3) = expected_fpu.st[3].sign_exp; + ST_MAN(3) = expected_fpu.st[3].mantissa; + ST_EXP(4) = expected_fpu.st[4].sign_exp; + ST_MAN(4) = expected_fpu.st[4].mantissa; + ST_EXP(5) = expected_fpu.st[5].sign_exp; + ST_MAN(5) = expected_fpu.st[5].mantissa; + ST_EXP(6) = expected_fpu.st[6].sign_exp; + ST_MAN(6) = expected_fpu.st[6].mantissa; + ST_EXP(7) = expected_fpu.st[7].sign_exp; + ST_MAN(7) = expected_fpu.st[7].mantissa; + break; + case FPREGS_MM: + ST_MAN(0) = expected[0].u64; + ST_MAN(1) = expected[1].u64; + ST_MAN(2) = expected[2].u64; + ST_MAN(3) = expected[3].u64; + ST_MAN(4) = expected[4].u64; + ST_MAN(5) = expected[5].u64; + ST_MAN(6) = expected[6].u64; + ST_MAN(7) = expected[7].u64; + break; + case FPREGS_ZMM: + /* zmm0..zmm15 are split between xmm, ymm_hi128, zmm_hi256 */ + memcpy(&xst.xs_zmm_hi256.xs_zmm[0], + &expected[0].zmm.e, sizeof(expected->zmm)/2); + memcpy(&xst.xs_zmm_hi256.xs_zmm[1], + &expected[1].zmm.e, sizeof(expected->zmm)/2); + memcpy(&xst.xs_zmm_hi256.xs_zmm[2], + &expected[2].zmm.e, sizeof(expected->zmm)/2); + memcpy(&xst.xs_zmm_hi256.xs_zmm[3], + &expected[3].zmm.e, sizeof(expected->zmm)/2); + memcpy(&xst.xs_zmm_hi256.xs_zmm[4], + &expected[4].zmm.e, sizeof(expected->zmm)/2); + memcpy(&xst.xs_zmm_hi256.xs_zmm[5], + &expected[5].zmm.e, sizeof(expected->zmm)/2); + memcpy(&xst.xs_zmm_hi256.xs_zmm[6], + &expected[6].zmm.e, sizeof(expected->zmm)/2); + memcpy(&xst.xs_zmm_hi256.xs_zmm[7], + &expected[7].zmm.e, sizeof(expected->zmm)/2); +#if defined(__x86_64__) + memcpy(&xst.xs_zmm_hi256.xs_zmm[8], + &expected[8].zmm.e, sizeof(expected->zmm)/2); + memcpy(&xst.xs_zmm_hi256.xs_zmm[9], + &expected[9].zmm.e, sizeof(expected->zmm)/2); + memcpy(&xst.xs_zmm_hi256.xs_zmm[10], + &expected[10].zmm.e, sizeof(expected->zmm)/2); + memcpy(&xst.xs_zmm_hi256.xs_zmm[11], + &expected[11].zmm.e, sizeof(expected->zmm)/2); + memcpy(&xst.xs_zmm_hi256.xs_zmm[12], + &expected[12].zmm.e, sizeof(expected->zmm)/2); + memcpy(&xst.xs_zmm_hi256.xs_zmm[13], + &expected[13].zmm.e, sizeof(expected->zmm)/2); + memcpy(&xst.xs_zmm_hi256.xs_zmm[14], + &expected[14].zmm.e, sizeof(expected->zmm)/2); + memcpy(&xst.xs_zmm_hi256.xs_zmm[15], + &expected[15].zmm.e, sizeof(expected->zmm)/2); + /* zmm16..zmm31 are stored as a whole */ + memcpy(&xst.xs_hi16_zmm.xs_hi16_zmm[0], + &expected[16].zmm, sizeof(expected->zmm)); + memcpy(&xst.xs_hi16_zmm.xs_hi16_zmm[1], + &expected[17].zmm, sizeof(expected->zmm)); + memcpy(&xst.xs_hi16_zmm.xs_hi16_zmm[2], + &expected[18].zmm, sizeof(expected->zmm)); + memcpy(&xst.xs_hi16_zmm.xs_hi16_zmm[3], + &expected[19].zmm, sizeof(expected->zmm)); + memcpy(&xst.xs_hi16_zmm.xs_hi16_zmm[4], + &expected[20].zmm, sizeof(expected->zmm)); + memcpy(&xst.xs_hi16_zmm.xs_hi16_zmm[5], + &expected[21].zmm, sizeof(expected->zmm)); + memcpy(&xst.xs_hi16_zmm.xs_hi16_zmm[6], + &expected[22].zmm, sizeof(expected->zmm)); + memcpy(&xst.xs_hi16_zmm.xs_hi16_zmm[7], + &expected[23].zmm, sizeof(expected->zmm)); + memcpy(&xst.xs_hi16_zmm.xs_hi16_zmm[8], + &expected[24].zmm, sizeof(expected->zmm)); + memcpy(&xst.xs_hi16_zmm.xs_hi16_zmm[9], + &expected[25].zmm, sizeof(expected->zmm)); + memcpy(&xst.xs_hi16_zmm.xs_hi16_zmm[10], + &expected[26].zmm, sizeof(expected->zmm)); + memcpy(&xst.xs_hi16_zmm.xs_hi16_zmm[11], + &expected[27].zmm, sizeof(expected->zmm)); + memcpy(&xst.xs_hi16_zmm.xs_hi16_zmm[12], + &expected[28].zmm, sizeof(expected->zmm)); + memcpy(&xst.xs_hi16_zmm.xs_hi16_zmm[13], + &expected[29].zmm, sizeof(expected->zmm)); + memcpy(&xst.xs_hi16_zmm.xs_hi16_zmm[14], + &expected[30].zmm, sizeof(expected->zmm)); + memcpy(&xst.xs_hi16_zmm.xs_hi16_zmm[15], + &expected[31].zmm, sizeof(expected->zmm)); +#endif + /* k0..k7 */ + xst.xs_opmask.xs_k[0] = expected[32].zmm.a; + xst.xs_opmask.xs_k[1] = expected[32].zmm.b; + xst.xs_opmask.xs_k[2] = expected[32].zmm.c; + xst.xs_opmask.xs_k[3] = expected[32].zmm.d; + xst.xs_opmask.xs_k[4] = expected[32].zmm.e; + xst.xs_opmask.xs_k[5] = expected[32].zmm.f; + xst.xs_opmask.xs_k[6] = expected[32].zmm.g; + xst.xs_opmask.xs_k[7] = expected[32].zmm.h; + /*FALLTHROUGH*/ + case FPREGS_YMM: + memcpy(&xst.xs_ymm_hi128.xs_ymm[0], + &expected[0].ymm.c, sizeof(expected->ymm)/2); + memcpy(&xst.xs_ymm_hi128.xs_ymm[1], + &expected[1].ymm.c, sizeof(expected->ymm)/2); + memcpy(&xst.xs_ymm_hi128.xs_ymm[2], + &expected[2].ymm.c, sizeof(expected->ymm)/2); + memcpy(&xst.xs_ymm_hi128.xs_ymm[3], + &expected[3].ymm.c, sizeof(expected->ymm)/2); + memcpy(&xst.xs_ymm_hi128.xs_ymm[4], + &expected[4].ymm.c, sizeof(expected->ymm)/2); + memcpy(&xst.xs_ymm_hi128.xs_ymm[5], + &expected[5].ymm.c, sizeof(expected->ymm)/2); + memcpy(&xst.xs_ymm_hi128.xs_ymm[6], + &expected[6].ymm.c, sizeof(expected->ymm)/2); + memcpy(&xst.xs_ymm_hi128.xs_ymm[7], + &expected[7].ymm.c, sizeof(expected->ymm)/2); +#if defined(__x86_64__) + memcpy(&xst.xs_ymm_hi128.xs_ymm[8], + &expected[8].ymm.c, sizeof(expected->ymm)/2); + memcpy(&xst.xs_ymm_hi128.xs_ymm[9], + &expected[9].ymm.c, sizeof(expected->ymm)/2); + memcpy(&xst.xs_ymm_hi128.xs_ymm[10], + &expected[10].ymm.c, sizeof(expected->ymm)/2); + memcpy(&xst.xs_ymm_hi128.xs_ymm[11], + &expected[11].ymm.c, sizeof(expected->ymm)/2); + memcpy(&xst.xs_ymm_hi128.xs_ymm[12], + &expected[12].ymm.c, sizeof(expected->ymm)/2); + memcpy(&xst.xs_ymm_hi128.xs_ymm[13], + &expected[13].ymm.c, sizeof(expected->ymm)/2); + memcpy(&xst.xs_ymm_hi128.xs_ymm[14], + &expected[14].ymm.c, sizeof(expected->ymm)/2); + memcpy(&xst.xs_ymm_hi128.xs_ymm[15], + &expected[15].ymm.c, sizeof(expected->ymm)/2); +#endif + /*FALLTHROUGH*/ + case FPREGS_XMM: + memcpy(&fxs->fx_xmm[0], &expected[0].ymm.a, + sizeof(expected->ymm)/2); + memcpy(&fxs->fx_xmm[1], &expected[1].ymm.a, + sizeof(expected->ymm)/2); + memcpy(&fxs->fx_xmm[2], &expected[2].ymm.a, + sizeof(expected->ymm)/2); + memcpy(&fxs->fx_xmm[3], &expected[3].ymm.a, + sizeof(expected->ymm)/2); + memcpy(&fxs->fx_xmm[4], &expected[4].ymm.a, + sizeof(expected->ymm)/2); + memcpy(&fxs->fx_xmm[5], &expected[5].ymm.a, + sizeof(expected->ymm)/2); + memcpy(&fxs->fx_xmm[6], &expected[6].ymm.a, + sizeof(expected->ymm)/2); + memcpy(&fxs->fx_xmm[7], &expected[7].ymm.a, + sizeof(expected->ymm)/2); +#if defined(__x86_64__) + memcpy(&fxs->fx_xmm[8], &expected[8].ymm.a, + sizeof(expected->ymm)/2); + memcpy(&fxs->fx_xmm[9], &expected[9].ymm.a, + sizeof(expected->ymm)/2); + memcpy(&fxs->fx_xmm[10], &expected[10].ymm.a, + sizeof(expected->ymm)/2); + memcpy(&fxs->fx_xmm[11], &expected[11].ymm.a, + sizeof(expected->ymm)/2); + memcpy(&fxs->fx_xmm[12], &expected[12].ymm.a, + sizeof(expected->ymm)/2); + memcpy(&fxs->fx_xmm[13], &expected[13].ymm.a, + sizeof(expected->ymm)/2); + memcpy(&fxs->fx_xmm[14], &expected[14].ymm.a, + sizeof(expected->ymm)/2); + memcpy(&fxs->fx_xmm[15], &expected[15].ymm.a, + sizeof(expected->ymm)/2); +#endif + break; + } + + switch (regset) { + case TEST_GPREGS: + DPRINTF("Call SETREGS for the child process\n"); + SYSCALL_REQUIRE(ptrace(PT_SETREGS, child, &gpr, 0) + != -1); + break; + case TEST_XMMREGS: +#if defined(__i386__) + DPRINTF("Call SETXMMREGS for the child process\n"); + SYSCALL_REQUIRE(ptrace(PT_SETXMMREGS, child, &xmm, 0) + != -1); + break; +#else + /*FALLTHROUGH*/ +#endif + case TEST_FPREGS: + DPRINTF("Call SETFPREGS for the child process\n"); + SYSCALL_REQUIRE(ptrace(PT_SETFPREGS, child, &fpr, 0) + != -1); + break; + case TEST_XSTATE: + DPRINTF("Call SETXSTATE for the child process\n"); + SYSCALL_REQUIRE(ptrace(PT_SETXSTATE, child, &iov, 0) + != -1); + break; + } + break; + } + +#undef ST_EXP +#undef ST_MAN + + DPRINTF("Before resuming the child process where it left off and " + "without signal to be sent\n"); + SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child); + + validate_status_exited(status, exitval); + + DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME); + TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0)); +} + +#define X86_REGISTER_TEST(test, regset, regs, regmode, descr) \ +ATF_TC(test); \ +ATF_TC_HEAD(test, tc) \ +{ \ + atf_tc_set_md_var(tc, "descr", descr); \ +} \ + \ +ATF_TC_BODY(test, tc) \ +{ \ + x86_register_test(regset, regs, regmode); \ +} + +X86_REGISTER_TEST(x86_gpregs32_read, TEST_GPREGS, GPREGS_32, TEST_GETREGS, + "Test reading basic 32-bit gp registers from debugged program " + "via PT_GETREGS."); +X86_REGISTER_TEST(x86_gpregs32_write, TEST_GPREGS, GPREGS_32, TEST_SETREGS, + "Test writing basic 32-bit gp registers into debugged program " + "via PT_SETREGS."); +X86_REGISTER_TEST(x86_gpregs32_core, TEST_GPREGS, GPREGS_32, TEST_COREDUMP, + "Test reading basic 32-bit gp registers from core dump."); +X86_REGISTER_TEST(x86_gpregs32_ebp_esp_read, TEST_GPREGS, GPREGS_32_EBP_ESP, + TEST_GETREGS, "Test reading ebp & esp registers from debugged program " + "via PT_GETREGS."); +X86_REGISTER_TEST(x86_gpregs32_ebp_esp_write, TEST_GPREGS, GPREGS_32_EBP_ESP, + TEST_SETREGS, "Test writing ebp & esp registers into debugged program " + "via PT_SETREGS."); +X86_REGISTER_TEST(x86_gpregs32_ebp_esp_core, TEST_GPREGS, GPREGS_32_EBP_ESP, + TEST_COREDUMP, "Test reading ebp & esp registers from core dump."); + +X86_REGISTER_TEST(x86_gpregs64_read, TEST_GPREGS, GPREGS_64, TEST_GETREGS, + "Test reading basic 64-bit gp registers from debugged program " + "via PT_GETREGS."); +X86_REGISTER_TEST(x86_gpregs64_write, TEST_GPREGS, GPREGS_64, TEST_SETREGS, + "Test writing basic 64-bit gp registers into debugged program " + "via PT_SETREGS."); +X86_REGISTER_TEST(x86_gpregs64_core, TEST_GPREGS, GPREGS_64, TEST_COREDUMP, + "Test reading basic 64-bit gp registers from core dump."); +X86_REGISTER_TEST(x86_gpregs64_r8_read, TEST_GPREGS, GPREGS_64_R8, TEST_GETREGS, + "Test reading r8..r15 registers from debugged program via PT_GETREGS."); +X86_REGISTER_TEST(x86_gpregs64_r8_write, TEST_GPREGS, GPREGS_64_R8, + TEST_SETREGS, "Test writing r8..r15 registers into debugged program " + "via PT_SETREGS."); +X86_REGISTER_TEST(x86_gpregs64_r8_core, TEST_GPREGS, GPREGS_64_R8, + TEST_COREDUMP, "Test reading r8..r15 registers from core dump."); + +X86_REGISTER_TEST(x86_fpregs_fpu_read, TEST_FPREGS, FPREGS_FPU, TEST_GETREGS, + "Test reading base FPU registers from debugged program via PT_GETFPREGS."); +X86_REGISTER_TEST(x86_fpregs_fpu_write, TEST_FPREGS, FPREGS_FPU, TEST_SETREGS, + "Test writing base FPU registers into debugged program via PT_SETFPREGS."); +X86_REGISTER_TEST(x86_fpregs_fpu_core, TEST_FPREGS, FPREGS_FPU, TEST_COREDUMP, + "Test reading base FPU registers from coredump."); +X86_REGISTER_TEST(x86_fpregs_mm_read, TEST_FPREGS, FPREGS_MM, TEST_GETREGS, + "Test reading mm0..mm7 registers from debugged program " + "via PT_GETFPREGS."); +X86_REGISTER_TEST(x86_fpregs_mm_write, TEST_FPREGS, FPREGS_MM, TEST_SETREGS, + "Test writing mm0..mm7 registers into debugged program " + "via PT_SETFPREGS."); +X86_REGISTER_TEST(x86_fpregs_mm_core, TEST_FPREGS, FPREGS_MM, TEST_COREDUMP, + "Test reading mm0..mm7 registers from coredump."); +X86_REGISTER_TEST(x86_fpregs_xmm_read, TEST_XMMREGS, FPREGS_XMM, TEST_GETREGS, + "Test reading xmm0..xmm15 (..xmm7 on i386) from debugged program " + "via PT_GETFPREGS (PT_GETXMMREGS on i386)."); +X86_REGISTER_TEST(x86_fpregs_xmm_write, TEST_XMMREGS, FPREGS_XMM, TEST_SETREGS, + "Test writing xmm0..xmm15 (..xmm7 on i386) into debugged program " + "via PT_SETFPREGS (PT_SETXMMREGS on i386)."); +X86_REGISTER_TEST(x86_fpregs_xmm_core, TEST_XMMREGS, FPREGS_XMM, TEST_COREDUMP, + "Test reading xmm0..xmm15 (..xmm7 on i386) from coredump."); + +X86_REGISTER_TEST(x86_xstate_fpu_read, TEST_XSTATE, FPREGS_FPU, TEST_GETREGS, + "Test reading base FPU registers from debugged program via PT_GETXSTATE."); +X86_REGISTER_TEST(x86_xstate_fpu_write, TEST_XSTATE, FPREGS_FPU, TEST_SETREGS, + "Test writing base FPU registers into debugged program via PT_SETXSTATE."); +X86_REGISTER_TEST(x86_xstate_fpu_core, TEST_XSTATE, FPREGS_FPU, TEST_COREDUMP, + "Test reading base FPU registers from core dump via XSTATE note."); +X86_REGISTER_TEST(x86_xstate_mm_read, TEST_XSTATE, FPREGS_MM, TEST_GETREGS, + "Test reading mm0..mm7 registers from debugged program " + "via PT_GETXSTATE."); +X86_REGISTER_TEST(x86_xstate_mm_write, TEST_XSTATE, FPREGS_MM, TEST_SETREGS, + "Test writing mm0..mm7 registers into debugged program " + "via PT_SETXSTATE."); +X86_REGISTER_TEST(x86_xstate_mm_core, TEST_XSTATE, FPREGS_MM, TEST_COREDUMP, + "Test reading mm0..mm7 registers from core dump via XSTATE note."); +X86_REGISTER_TEST(x86_xstate_xmm_read, TEST_XSTATE, FPREGS_XMM, TEST_GETREGS, + "Test reading xmm0..xmm15 (..xmm7 on i386) from debugged program " + "via PT_GETXSTATE."); +X86_REGISTER_TEST(x86_xstate_xmm_write, TEST_XSTATE, FPREGS_XMM, TEST_SETREGS, + "Test writing xmm0..xmm15 (..xmm7 on i386) into debugged program " + "via PT_SETXSTATE."); +X86_REGISTER_TEST(x86_xstate_xmm_core, TEST_XSTATE, FPREGS_XMM, TEST_COREDUMP, + "Test reading xmm0..xmm15 (..xmm7 on i386) from coredump via XSTATE note."); +X86_REGISTER_TEST(x86_xstate_ymm_read, TEST_XSTATE, FPREGS_YMM, TEST_GETREGS, + "Test reading ymm0..ymm15 (..ymm7 on i386) from debugged program " + "via PT_GETXSTATE."); +X86_REGISTER_TEST(x86_xstate_ymm_write, TEST_XSTATE, FPREGS_YMM, TEST_SETREGS, + "Test writing ymm0..ymm15 (..ymm7 on i386) into debugged program " + "via PT_SETXSTATE."); +X86_REGISTER_TEST(x86_xstate_ymm_core, TEST_XSTATE, FPREGS_YMM, TEST_COREDUMP, + "Test reading ymm0..ymm15 (..ymm7 on i386) from coredump via XSTATE note."); +X86_REGISTER_TEST(x86_xstate_zmm_read, TEST_XSTATE, FPREGS_ZMM, TEST_GETREGS, + "Test reading zmm0..zmm31 (..zmm7 on i386), k0..k7 from debugged program " + "via PT_GETXSTATE."); +X86_REGISTER_TEST(x86_xstate_zmm_write, TEST_XSTATE, FPREGS_ZMM, TEST_SETREGS, + "Test writing zmm0..zmm31 (..zmm7 on i386), k0..k7 into debugged program " + "via PT_SETXSTATE."); +X86_REGISTER_TEST(x86_xstate_zmm_core, TEST_XSTATE, FPREGS_ZMM, TEST_COREDUMP, + "Test reading zmm0..zmm31 (..zmm7 on i386), k0..k7 from coredump " + "via XSTATE note."); + +/// ---------------------------------------------------------------------------- + +#if defined(TWAIT_HAVE_STATUS) + +static void +thread_concurrent_lwp_setup(pid_t child, lwpid_t lwpid) +{ + struct dbreg r; + union u dr7; + + /* We need to set debug registers for every child */ + DPRINTF("Call GETDBREGS for LWP %d\n", lwpid); + SYSCALL_REQUIRE(ptrace(PT_GETDBREGS, child, &r, lwpid) != -1); + + dr7.raw = 0; + /* should be set to 1 according to Intel manual, 17.2 */ + dr7.bits.reserved_10 = 1; + dr7.bits.local_exact_breakpt = 1; + dr7.bits.global_exact_breakpt = 1; + /* use DR0 for breakpoints */ + dr7.bits.global_dr0_breakpoint = 1; + dr7.bits.condition_dr0 = 0; /* exec */ + dr7.bits.len_dr0 = 0; + /* use DR1 for watchpoints */ + dr7.bits.global_dr1_breakpoint = 1; + dr7.bits.condition_dr1 = 1; /* write */ + dr7.bits.len_dr1 = 3; /* 4 bytes */ + r.dr[7] = dr7.raw; + r.dr[0] = (long)(intptr_t)check_happy; + r.dr[1] = (long)(intptr_t)&thread_concurrent_watchpoint_var; + DPRINTF("dr0=%" PRIxREGISTER "\n", r.dr[0]); + DPRINTF("dr1=%" PRIxREGISTER "\n", r.dr[1]); + DPRINTF("dr7=%" PRIxREGISTER "\n", r.dr[7]); + + DPRINTF("Call SETDBREGS for LWP %d\n", lwpid); + SYSCALL_REQUIRE(ptrace(PT_SETDBREGS, child, &r, lwpid) != -1); +} + +static enum thread_concurrent_sigtrap_event +thread_concurrent_handle_sigtrap(pid_t child, ptrace_siginfo_t *info) +{ + enum thread_concurrent_sigtrap_event ret = TCSE_UNKNOWN; + struct dbreg r; + union u dr7; + + ATF_CHECK_EQ_MSG(info->psi_siginfo.si_code, TRAP_DBREG, + "lwp=%d, expected TRAP_DBREG (%d), got %d", info->psi_lwpid, + TRAP_DBREG, info->psi_siginfo.si_code); + + DPRINTF("Call GETDBREGS for LWP %d\n", info->psi_lwpid); + SYSCALL_REQUIRE(ptrace(PT_GETDBREGS, child, &r, info->psi_lwpid) != -1); + DPRINTF("dr6=%" PRIxREGISTER ", dr7=%" PRIxREGISTER "\n", + r.dr[6], r.dr[7]); + + ATF_CHECK_MSG(r.dr[6] & 3, "lwp=%d, got DR6=%" PRIxREGISTER, + info->psi_lwpid, r.dr[6]); + + /* Handle only one event at a time, we should get + * a separate SIGTRAP for the other one. + */ + if (r.dr[6] & 1) { + r.dr[6] &= ~1; + + /* We need to disable the breakpoint to move + * past it. + * + * TODO: single-step and reenable it? + */ + dr7.raw = r.dr[7]; + dr7.bits.global_dr0_breakpoint = 0; + r.dr[7] = dr7.raw; + + ret = TCSE_BREAKPOINT; + } else if (r.dr[6] & 2) { + r.dr[6] &= ~2; + ret = TCSE_WATCHPOINT; + } + + DPRINTF("Call SETDBREGS for LWP %d\n", info->psi_lwpid); + DPRINTF("dr6=%" PRIxREGISTER ", dr7=%" PRIxREGISTER "\n", + r.dr[6], r.dr[7]); + SYSCALL_REQUIRE(ptrace(PT_SETDBREGS, child, &r, info->psi_lwpid) != -1); + + return ret; +} + +#endif /*defined(TWAIT_HAVE_STATUS)*/ + +/// ---------------------------------------------------------------------------- + +#define ATF_TP_ADD_TCS_PTRACE_WAIT_X86() \ + ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_print); \ + ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_preserve_dr0); \ + ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_preserve_dr1); \ + ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_preserve_dr2); \ + ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_preserve_dr3); \ + ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_preserve_dr0_yield); \ + ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_preserve_dr1_yield); \ + ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_preserve_dr2_yield); \ + ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_preserve_dr3_yield); \ + ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_preserve_dr0_continued); \ + ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_preserve_dr1_continued); \ + ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_preserve_dr2_continued); \ + ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_preserve_dr3_continued); \ + ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr0_trap_variable_writeonly_byte); \ + ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr1_trap_variable_writeonly_byte); \ + ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr2_trap_variable_writeonly_byte); \ + ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr3_trap_variable_writeonly_byte); \ + ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr0_trap_variable_writeonly_2bytes); \ + ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr1_trap_variable_writeonly_2bytes); \ + ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr2_trap_variable_writeonly_2bytes); \ + ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr3_trap_variable_writeonly_2bytes); \ + ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr0_trap_variable_writeonly_4bytes); \ + ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr1_trap_variable_writeonly_4bytes); \ + ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr2_trap_variable_writeonly_4bytes); \ + ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr3_trap_variable_writeonly_4bytes); \ + ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr0_trap_variable_readwrite_write_byte); \ + ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr1_trap_variable_readwrite_write_byte); \ + ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr2_trap_variable_readwrite_write_byte); \ + ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr3_trap_variable_readwrite_write_byte); \ + ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr0_trap_variable_readwrite_write_2bytes); \ + ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr1_trap_variable_readwrite_write_2bytes); \ + ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr2_trap_variable_readwrite_write_2bytes); \ + ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr3_trap_variable_readwrite_write_2bytes); \ + ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr0_trap_variable_readwrite_write_4bytes); \ + ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr1_trap_variable_readwrite_write_4bytes); \ + ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr2_trap_variable_readwrite_write_4bytes); \ + ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr3_trap_variable_readwrite_write_4bytes); \ + ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr0_trap_variable_readwrite_read_byte); \ + ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr1_trap_variable_readwrite_read_byte); \ + ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr2_trap_variable_readwrite_read_byte); \ + ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr3_trap_variable_readwrite_read_byte); \ + ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr0_trap_variable_readwrite_read_2bytes); \ + ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr1_trap_variable_readwrite_read_2bytes); \ + ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr2_trap_variable_readwrite_read_2bytes); \ + ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr3_trap_variable_readwrite_read_2bytes); \ + ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr0_trap_variable_readwrite_read_4bytes); \ + ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr1_trap_variable_readwrite_read_4bytes); \ + ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr2_trap_variable_readwrite_read_4bytes); \ + ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr3_trap_variable_readwrite_read_4bytes); \ + ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr0_trap_code); \ + ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr1_trap_code); \ + ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr2_trap_code); \ + ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr3_trap_code); \ + ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr0_dont_inherit_lwp); \ + ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr1_dont_inherit_lwp); \ + ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr2_dont_inherit_lwp); \ + ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr3_dont_inherit_lwp); \ + ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr0_dont_inherit_execve); \ + ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr1_dont_inherit_execve); \ + ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr2_dont_inherit_execve); \ + ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr3_dont_inherit_execve); \ + ATF_TP_ADD_TC_HAVE_DBREGS(tp, x86_cve_2018_8897); \ + ATF_TP_ADD_TC(tp, x86_gpregs32_read); \ + ATF_TP_ADD_TC(tp, x86_gpregs32_write); \ + ATF_TP_ADD_TC(tp, x86_gpregs32_core); \ + ATF_TP_ADD_TC(tp, x86_gpregs32_ebp_esp_read); \ + ATF_TP_ADD_TC(tp, x86_gpregs32_ebp_esp_write); \ + ATF_TP_ADD_TC(tp, x86_gpregs32_ebp_esp_core); \ + ATF_TP_ADD_TC(tp, x86_gpregs64_read); \ + ATF_TP_ADD_TC(tp, x86_gpregs64_write); \ + ATF_TP_ADD_TC(tp, x86_gpregs64_core); \ + ATF_TP_ADD_TC(tp, x86_gpregs64_r8_read); \ + ATF_TP_ADD_TC(tp, x86_gpregs64_r8_write); \ + ATF_TP_ADD_TC(tp, x86_gpregs64_r8_core); \ + ATF_TP_ADD_TC(tp, x86_fpregs_fpu_read); \ + ATF_TP_ADD_TC(tp, x86_fpregs_fpu_write); \ + ATF_TP_ADD_TC(tp, x86_fpregs_fpu_core); \ + ATF_TP_ADD_TC(tp, x86_fpregs_mm_read); \ + ATF_TP_ADD_TC(tp, x86_fpregs_mm_write); \ + ATF_TP_ADD_TC(tp, x86_fpregs_mm_core); \ + ATF_TP_ADD_TC(tp, x86_fpregs_xmm_read); \ + ATF_TP_ADD_TC(tp, x86_fpregs_xmm_write); \ + ATF_TP_ADD_TC(tp, x86_fpregs_xmm_core); \ + ATF_TP_ADD_TC(tp, x86_xstate_fpu_read); \ + ATF_TP_ADD_TC(tp, x86_xstate_fpu_write); \ + ATF_TP_ADD_TC(tp, x86_xstate_fpu_core); \ + ATF_TP_ADD_TC(tp, x86_xstate_mm_read); \ + ATF_TP_ADD_TC(tp, x86_xstate_mm_write); \ + ATF_TP_ADD_TC(tp, x86_xstate_mm_core); \ + ATF_TP_ADD_TC(tp, x86_xstate_xmm_read); \ + ATF_TP_ADD_TC(tp, x86_xstate_xmm_write); \ + ATF_TP_ADD_TC(tp, x86_xstate_xmm_core); \ + ATF_TP_ADD_TC(tp, x86_xstate_ymm_read); \ + ATF_TP_ADD_TC(tp, x86_xstate_ymm_write); \ + ATF_TP_ADD_TC(tp, x86_xstate_ymm_core); \ + ATF_TP_ADD_TC(tp, x86_xstate_zmm_read); \ + ATF_TP_ADD_TC(tp, x86_xstate_zmm_write); \ + ATF_TP_ADD_TC(tp, x86_xstate_zmm_core); +#else +#define ATF_TP_ADD_TCS_PTRACE_WAIT_X86() +#endif diff --git a/lib/libc/sys/t_recvmmsg.c b/lib/libc/sys/t_recvmmsg.c --- a/lib/libc/sys/t_recvmmsg.c +++ b/lib/libc/sys/t_recvmmsg.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_recvmmsg.c,v 1.1 2012/06/22 18:45:23 christos Exp $ */ +/* $NetBSD: t_recvmmsg.c,v 1.4 2018/08/21 10:39:21 christos Exp $ */ /*- * Copyright (c) 2012 The NetBSD Foundation, Inc. @@ -36,7 +36,7 @@ * POSSIBILITY OF SUCH DAMAGE. */ #include -__RCSID("$NetBSD: t_recvmmsg.c,v 1.1 2012/06/22 18:45:23 christos Exp $"); +__RCSID("$NetBSD: t_recvmmsg.c,v 1.4 2018/08/21 10:39:21 christos Exp $"); #include #include @@ -47,6 +47,7 @@ #include #include #include +#include #include #include #include @@ -57,7 +58,14 @@ #define min(a, b) ((a) < (b) ? (a) : (b)) static int debug; +static volatile sig_atomic_t rdied; +static void +handle_sigchld(__unused int pid) +{ + + rdied = 1; +} ATF_TC(recvmmsg_basic); ATF_TC_HEAD(recvmmsg_basic, tc) @@ -75,7 +83,9 @@ int status; off_t off; uint8_t DGRAM[1316] = { 0, 2, 3, 4, 5, 6, 7, 8, 9, }; - + struct sigaction sa; + ssize_t overf = 0; + error = socketpair(AF_UNIX, SOCK_DGRAM, 0, fd); ATF_REQUIRE_MSG(error != -1, "socketpair failed (%s)", strerror(errno)); @@ -98,6 +108,14 @@ mmsghdr[n].msg_hdr.msg_namelen = 0; } + memset(&sa, 0, sizeof(sa)); + sa.sa_flags = SA_RESTART; + sa.sa_handler = &handle_sigchld; + sigemptyset(&sa.sa_mask); + error = sigaction(SIGCHLD, &sa, 0); + ATF_REQUIRE_MSG(error != -1, "sigaction failed (%s)", + strerror(errno)); + switch (fork()) { case -1: ATF_REQUIRE_MSG(0, "fork failed (%s)", strerror(errno)); @@ -112,6 +130,13 @@ struct timespec ts = { 1, 0 }; cnt = recvmmsg(fd[1], mmsghdr, min(mmsgcnt, n), MSG_WAITALL, &ts); + if (cnt == -1 && errno == ENOBUFS) { + overf++; + if (debug) + printf("receive buffer overflowed" + " (%zu)\n",overf); + continue; + } ATF_REQUIRE_MSG(cnt != -1, "recvmmsg failed (%s)", strerror(errno)); ATF_REQUIRE_MSG(cnt != 0, "recvmmsg timeout"); @@ -138,16 +163,19 @@ printf("sending packet %u/%u...\n", (n+1), NPKTS); do { + if (rdied) + break; DGRAM[0] = n; error = send(fd[0], DGRAM, sizeof(DGRAM), 0); } while (error == -1 && errno == ENOBUFS); - if (error == -1) - ATF_REQUIRE_MSG(error != -1, "send failed (%s)", - strerror(errno)); + ATF_REQUIRE_MSG(error != -1, "send failed (%s)", + strerror(errno)); } error = wait(&status); ATF_REQUIRE_MSG(error != -1, "wait failed (%s)", strerror(errno)); + ATF_REQUIRE_MSG(WIFEXITED(status) && WEXITSTATUS(status) == 0, + "receiver died"); break; } } diff --git a/lib/libc/sys/t_sendmmsg.c b/lib/libc/sys/t_sendmmsg.c new file mode 100644 --- /dev/null +++ b/lib/libc/sys/t_sendmmsg.c @@ -0,0 +1,212 @@ +/* $NetBSD: t_sendmmsg.c,v 1.3 2019/03/16 21:46:43 christos Exp $ */ + +/*- + * Copyright (c) 2018 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +__RCSID("$NetBSD: t_sendmmsg.c,v 1.3 2019/03/16 21:46:43 christos Exp $"); + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BUFSIZE 65536 + +#define min(a, b) ((a) < (b) ? (a) : (b)) +static int debug = 1; +static volatile sig_atomic_t rdied; + +static void +handle_sigchld(__unused int pid) +{ + + rdied = 1; +} + +ATF_TC(sendmmsg_basic); +ATF_TC_HEAD(sendmmsg_basic, tc) +{ + atf_tc_set_md_var(tc, "descr", "A basic test of sendmmsg(2)"); +} + +static void +setsock(int fd, int type) +{ + int buflen = BUFSIZE; + socklen_t socklen = sizeof(buflen); + + ATF_REQUIRE_MSG(setsockopt(fd, SOL_SOCKET, type, + &buflen, socklen) != -1, "%s (%s)", + type == SO_RCVBUF ? "rcv" : "snd", strerror(errno)); +} + +ATF_TC_BODY(sendmmsg_basic, tc) +{ + int fd[2], error, cnt; + uint8_t *buf; + struct mmsghdr *mmsghdr; + struct iovec *iov; + unsigned int mmsgcnt, n; + int status; + off_t off; + uint8_t DGRAM[1316] = { 0, 2, 3, 4, 5, 6, 7, 8, 9, }; + uint8_t rgram[sizeof(DGRAM)]; + struct sigaction sa; + ssize_t overf = 0; + + error = socketpair(AF_UNIX, SOCK_DGRAM, 0, fd); + ATF_REQUIRE_MSG(error != -1, "socketpair failed (%s)", strerror(errno)); + + buf = malloc(BUFSIZE); + ATF_REQUIRE_MSG(buf != NULL, "malloc failed (%s)", strerror(errno)); + + setsock(fd[1], SO_SNDBUF); +// setsock(fd[0], SO_RCVBUF); + + mmsgcnt = BUFSIZE / sizeof(DGRAM); + mmsghdr = calloc(mmsgcnt, sizeof(*mmsghdr)); + ATF_REQUIRE_MSG(mmsghdr != NULL, "malloc failed (%s)", strerror(errno)); + iov = malloc(sizeof(*iov) * mmsgcnt); + ATF_REQUIRE_MSG(iov != NULL, "malloc failed (%s)", strerror(errno)); + + for (off = 0, n = 0; n < mmsgcnt; n++) { + iov[n].iov_base = buf + off; + memcpy(iov[n].iov_base, DGRAM, sizeof(DGRAM)); + *(buf + off) = n; + iov[n].iov_len = sizeof(DGRAM); + off += iov[n].iov_len; + mmsghdr[n].msg_hdr.msg_iov = &iov[n]; + mmsghdr[n].msg_hdr.msg_iovlen = 1; + mmsghdr[n].msg_hdr.msg_name = NULL; + mmsghdr[n].msg_hdr.msg_namelen = 0; + } + + memset(&sa, 0, sizeof(sa)); + sa.sa_flags = SA_RESTART; + sa.sa_handler = &handle_sigchld; + sigemptyset(&sa.sa_mask); + error = sigaction(SIGCHLD, &sa, 0); + ATF_REQUIRE_MSG(error != -1, "sigaction failed (%s)", + strerror(errno)); + + switch (fork()) { + case -1: + ATF_REQUIRE_MSG(0, "fork failed (%s)", strerror(errno)); + break; + case 0: + sched_yield(); + if (debug) + printf("sending %u messages (max %u per syscall)\n", n, + mmsgcnt); + for (n = 0; n < mmsgcnt;) { + if (debug) + printf("sending packet %u/%u...\n", n, + mmsgcnt); + // XXX: ENOBUFS bug, on the receive side!!! + // in npkt = min(mmsgsize, mmsgcnt - n); + int npkt = min(3, mmsgcnt - n), a; + do { + a = 0; + ATF_REQUIRE(ioctl(fd[1], FIONSPACE, &a) != -1); + printf("1 %d\n", a); + ATF_REQUIRE(ioctl(fd[0], FIONSPACE, &a) != -1); + printf("0 %d\n", a); + } while ((size_t)a < sizeof(DGRAM)); + cnt = sendmmsg(fd[1], mmsghdr + n, npkt, 0); + if (cnt == -1 && errno == ENOBUFS) { + overf++; + if (debug) + printf("send buffer overflowed" + " (%zu)\n",overf); + if (overf > 100) + exit(1); + sched_yield(); + sched_yield(); + sched_yield(); + continue; + } + ATF_REQUIRE_MSG(cnt != -1, "sendmmsg %u failed (%s)", + n, strerror(errno)); + if (debug) + printf("sendmmsg: sent %u messages\n", cnt); + n += cnt; + sched_yield(); + sched_yield(); + sched_yield(); + } + if (debug) + printf("done!\n"); + exit(0); + /*NOTREACHED*/ + default: + for (n = 0; n < mmsgcnt; n++) { + if (debug) + printf("receiving packet %u/%u...\n", n, + mmsgcnt); + do { + if (rdied) + break; + cnt = recv(fd[0], rgram, sizeof(rgram), 0); + ATF_REQUIRE_MSG(cnt != -1 || errno != ENOBUFS, + "recv failed (%s)", strerror(errno)); + ATF_CHECK_EQ_MSG(cnt, sizeof(rgram), + "packet length"); + ATF_CHECK_EQ_MSG(rgram[0], n, + "number %u != %u", rgram[0], n); + ATF_REQUIRE_MSG(memcmp(rgram + 1, DGRAM + 1, + sizeof(rgram) - 1) == 0, "bad data"); + } while (cnt == -1 && errno == ENOBUFS); + } + error = wait(&status); + ATF_REQUIRE_MSG(error != -1, "wait failed (%s)", + strerror(errno)); + ATF_REQUIRE_MSG(WIFEXITED(status) && WEXITSTATUS(status) == 0, + "receiver died"); + break; + } +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, sendmmsg_basic); + + return atf_no_error(); +} diff --git a/lib/libc/sys/t_sendrecv.c b/lib/libc/sys/t_sendrecv.c new file mode 100644 --- /dev/null +++ b/lib/libc/sys/t_sendrecv.c @@ -0,0 +1,188 @@ +/* $NetBSD: t_sendrecv.c,v 1.8 2021/03/28 17:30:01 christos Exp $ */ + +/*- + * Copyright (c) 2018 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +__RCSID("$NetBSD: t_sendrecv.c,v 1.8 2021/03/28 17:30:01 christos Exp $"); + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + + +#define COUNT 100 + +union packet { + uint8_t buf[1316]; + uintmax_t seq; +}; + +static volatile sig_atomic_t rdied; + +static void +handle_sigchld(__unused int pid) +{ + + rdied = 1; +} + +static void +sender(int sd) +{ + union packet p; + ssize_t n; + p.seq = 0; + for (size_t i = 0; i < COUNT; i++) { + for (; (n = send(sd, &p, sizeof(p), 0)) == sizeof(p); + p.seq++) + continue; +// printf(">>%zd %d %ju\n", n, errno, p.seq); + ATF_REQUIRE_MSG(errno == ENOBUFS, "send %s", strerror(errno)); + } + close(sd); +// printf("sender done\n"); +} + +static void +receiver(int sd) +{ + union packet p; + ssize_t n; + uintmax_t seq = 0; + + for (size_t i = 0; i < COUNT; i++) { + if (rdied) + return; + while ((n = recv(sd, &p, sizeof(p), 0), sizeof(p)) + == sizeof(p)) + { + if (rdied) + return; + if (p.seq != seq) + printf("%ju != %ju\n", p.seq, seq); + if (seq % 10 == 0) + sched_yield(); + seq = p.seq + 1; + } +// printf("<<%zd %d %ju\n", n, errno, seq); + if (n == 0) + return; + ATF_REQUIRE_EQ(n, -1); + ATF_REQUIRE_MSG(errno == ENOBUFS, "recv %s", strerror(errno)); + } + close(sd); +} + +static void +sendrecv(int rerror) +{ + int fd[2], sd[2], error; + char c = 0; + struct sigaction sa; + + error = socketpair(AF_UNIX, SOCK_DGRAM, 0, sd); + ATF_REQUIRE_MSG(error != -1, "socketpair failed (%s)", strerror(errno)); + error = pipe(fd); + ATF_REQUIRE_MSG(error != -1, "pipe failed (%s)", strerror(errno)); + + for (size_t i = 0; i < __arraycount(sd); i++) { + error = setsockopt(sd[i], SOL_SOCKET, SO_RERROR, &rerror, + sizeof(rerror)); + ATF_REQUIRE_MSG(error != -1, + "setsockopt(SO_RERROR) failed (%s)", strerror(errno)); + } + + memset(&sa, 0, sizeof(sa)); + sa.sa_flags = 0; + sa.sa_handler = &handle_sigchld; + sigemptyset(&sa.sa_mask); + error = sigaction(SIGCHLD, &sa, 0); + ATF_REQUIRE_MSG(error != -1, "sigaction failed (%s)", + strerror(errno)); + + switch (fork()) { + case -1: + ATF_REQUIRE_MSG(errno == 0, + "fork failed (%s)", strerror(errno)); + __unreachable(); + /*NOTREACHED*/ + case 0: + read(fd[1], &c, sizeof(c)); + sender(sd[0]); + close(sd[0]); + exit(EXIT_SUCCESS); + /*NOTREACHED*/ + default: + write(fd[0], &c, sizeof(c)); + receiver(sd[1]); + return; + } +} + +ATF_TC(sendrecv_basic); + +ATF_TC_HEAD(sendrecv_basic, tc) +{ + atf_tc_set_md_var(tc, "descr", "A basic test of send/recv(2)"); +} + +ATF_TC_BODY(sendrecv_basic, tc) +{ + sendrecv(0); +} + +ATF_TC(sendrecv_rerror); + +ATF_TC_HEAD(sendrecv_rerror, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test send/recv(2) with receiver error"); +} + +ATF_TC_BODY(sendrecv_rerror, tc) +{ + sendrecv(1); +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, sendrecv_basic); + ATF_TP_ADD_TC(tp, sendrecv_rerror); + + return atf_no_error(); +} diff --git a/lib/libc/sys/t_setrlimit.c b/lib/libc/sys/t_setrlimit.c --- a/lib/libc/sys/t_setrlimit.c +++ b/lib/libc/sys/t_setrlimit.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_setrlimit.c,v 1.6 2017/01/13 21:16:38 christos Exp $ */ +/* $NetBSD: t_setrlimit.c,v 1.7 2020/10/13 06:58:57 rin Exp $ */ /*- * Copyright (c) 2011 The NetBSD Foundation, Inc. @@ -29,7 +29,7 @@ * POSSIBILITY OF SUCH DAMAGE. */ #include -__RCSID("$NetBSD: t_setrlimit.c,v 1.6 2017/01/13 21:16:38 christos Exp $"); +__RCSID("$NetBSD: t_setrlimit.c,v 1.7 2020/10/13 06:58:57 rin Exp $"); #include #include @@ -517,7 +517,7 @@ struct rlimit res; /* Ensure soft limit is not bigger than hard limit */ - res.rlim_cur = res.rlim_max = 4192256; + res.rlim_cur = res.rlim_max = 6 * 1024 * 1024; ATF_REQUIRE(setrlimit(RLIMIT_STACK, &res) == 0); ATF_REQUIRE(getrlimit(RLIMIT_STACK, &res) == 0); ATF_CHECK(res.rlim_cur <= res.rlim_max); diff --git a/lib/libc/sys/t_sigaltstack.c b/lib/libc/sys/t_sigaltstack.c new file mode 100644 --- /dev/null +++ b/lib/libc/sys/t_sigaltstack.c @@ -0,0 +1,100 @@ +/* $NetBSD: t_sigaltstack.c,v 1.2 2020/05/01 21:35:30 christos Exp $ */ + +/*- + * Copyright (c) 2020 The NetBSD Foundation, Inc. + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +__RCSID("$NetBSD: t_sigaltstack.c,v 1.2 2020/05/01 21:35:30 christos Exp $"); + +#include +#include + +#include + +#include "h_macros.h" + +static stack_t sigstk; +static bool handler_called; +static bool handler_use_altstack; + +static void +handler(int signo __unused) +{ + char sp[128]; + + handler_called = true; + + /* checking if the stack pointer is within the range of altstack */ + if ((char *)sigstk.ss_sp <= sp && + ((char *)sigstk.ss_sp + sigstk.ss_size) > sp) + handler_use_altstack = true; + else + handler_use_altstack = false; +} + +ATF_TC(sigaltstack_onstack); +ATF_TC_HEAD(sigaltstack_onstack, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Checks for using signal stack with SA_ONSTACK"); +} + +ATF_TC_BODY(sigaltstack_onstack, tc) +{ + struct sigaction sa; + int i; + + /* set a signal handler use alternative stack */ + memset(&sigstk, 0, sizeof(sigstk)); + sigstk.ss_sp = malloc(SIGSTKSZ); + ATF_REQUIRE(sigstk.ss_sp != NULL); + sigstk.ss_size = SIGSTKSZ; + sigstk.ss_flags = 0; + ATF_REQUIRE(sigaltstack(&sigstk, 0) == 0); + + sigemptyset(&sa.sa_mask); + sa.sa_handler = handler; + sa.sa_flags = SA_ONSTACK; + sigaction(SIGUSR1, &sa, NULL); + + /* test several times */ + for (i = 1; i <= 5; i++) { + handler_called = false; + kill(getpid(), SIGUSR1); + + if (!handler_called) + atf_tc_fail("signal handler wasn't called (count=%d)", i); + if (!handler_use_altstack) + atf_tc_fail("alternative stack wasn't used (count=%d)", i); + } +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, sigaltstack_onstack); + + return atf_no_error(); +} diff --git a/lib/libc/sys/t_stat.c b/lib/libc/sys/t_stat.c --- a/lib/libc/sys/t_stat.c +++ b/lib/libc/sys/t_stat.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_stat.c,v 1.5 2017/01/13 20:06:50 christos Exp $ */ +/* $NetBSD: t_stat.c,v 1.6 2019/07/16 17:29:18 martin Exp $ */ /*- * Copyright (c) 2011 The NetBSD Foundation, Inc. @@ -29,7 +29,7 @@ * POSSIBILITY OF SUCH DAMAGE. */ #include -__RCSID("$NetBSD: t_stat.c,v 1.5 2017/01/13 20:06:50 christos Exp $"); +__RCSID("$NetBSD: t_stat.c,v 1.6 2019/07/16 17:29:18 martin Exp $"); #include #include @@ -64,7 +64,7 @@ (void)memset(&sa, 0, sizeof(struct stat)); (void)memset(&sb, 0, sizeof(struct stat)); - fd = open(path, O_RDONLY | O_CREAT); + fd = open(path, O_RDONLY | O_CREAT, 0600); ATF_REQUIRE(fd != -1); ATF_REQUIRE(stat(path, &sa) == 0); @@ -210,7 +210,7 @@ (void)memset(&sa, 0, sizeof(struct stat)); (void)memset(&sb, 0, sizeof(struct stat)); - fd[i] = open(path, O_WRONLY | O_CREAT); + fd[i] = open(path, O_WRONLY | O_CREAT, 0600); ATF_REQUIRE(fd[i] != -1); ATF_REQUIRE(write(fd[i], "X", 1) == 1); @@ -254,7 +254,7 @@ uid = getuid(); gid = getgid(); - fd = open(path, O_RDONLY | O_CREAT); + fd = open(path, O_RDONLY | O_CREAT, 0600); ATF_REQUIRE(fd != -1); ATF_REQUIRE(fstat(fd, &sa) == 0); @@ -288,7 +288,7 @@ size_t i; int fd; - fd = open(path, O_WRONLY | O_CREAT); + fd = open(path, O_WRONLY | O_CREAT, 0600); ATF_REQUIRE(fd >= 0); for (i = 0; i < n; i++) { @@ -377,7 +377,7 @@ (void)memset(&sa, 0, sizeof(struct stat)); (void)memset(&sb, 0, sizeof(struct stat)); - fd = open(path, O_WRONLY | O_CREAT); + fd = open(path, O_WRONLY | O_CREAT, 0600); ATF_REQUIRE(fd >= 0); ATF_REQUIRE(symlink(path, pathlink) == 0); diff --git a/lib/libc/sys/t_syscall.c b/lib/libc/sys/t_syscall.c new file mode 100644 --- /dev/null +++ b/lib/libc/sys/t_syscall.c @@ -0,0 +1,118 @@ +/* $NetBSD: t_syscall.c,v 1.4 2021/01/18 05:44:20 simonb Exp $ */ + +/*- + * Copyright (c) 2018 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Martin Husemann. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +__RCSID("$NetBSD: t_syscall.c,v 1.4 2021/01/18 05:44:20 simonb Exp $"); + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if !defined(_LP64) && BYTE_ORDER == _BIG_ENDIAN +#define __SYSCALL_TO_UINTPTR_T(V) ((uintptr_t)((V)>>32)) +#else +#define __SYSCALL_TO_UINTPTR_T(V) ((uintptr_t)(V)) +#endif + +static const char secrect_data[1024] = { + "my secret key\n" +}; + +#define FILE_NAME "dummy" + +#ifndef _LP64 +ATF_TC(mmap_syscall); + +ATF_TC_HEAD(mmap_syscall, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests mmap(2) via syscall(2)"); +} + +ATF_TC_BODY(mmap_syscall, tc) +{ + int fd; + const char *p; + + fd = open(FILE_NAME, O_RDWR|O_CREAT|O_TRUNC, 0666); + ATF_REQUIRE(fd != -1); + + write(fd, secrect_data, sizeof(secrect_data)); + + p = (const char *)syscall(SYS_mmap, + 0, sizeof(secrect_data), PROT_READ, MAP_PRIVATE, fd, 0, 0, 0); + ATF_REQUIRE(p != MAP_FAILED); + + ATF_REQUIRE(strcmp(p, secrect_data) == 0); +} +#endif + +ATF_TC(mmap___syscall); + +ATF_TC_HEAD(mmap___syscall, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests mmap(2) via __syscall(2)"); +} + +ATF_TC_BODY(mmap___syscall, tc) +{ + int fd; + const char *p; + + fd = open(FILE_NAME, O_RDWR|O_CREAT|O_TRUNC, 0666); + ATF_REQUIRE(fd != -1); + + write(fd, secrect_data, sizeof(secrect_data)); + + p = (const char *)__SYSCALL_TO_UINTPTR_T(__syscall(SYS_mmap, + 0, sizeof(secrect_data), PROT_READ, MAP_PRIVATE, fd, + /* pad*/ 0, (off_t)0)); + ATF_REQUIRE(p != MAP_FAILED); + + ATF_REQUIRE(strcmp(p, secrect_data) == 0); +} + +ATF_TP_ADD_TCS(tp) +{ + +#ifndef _LP64 + ATF_TP_ADD_TC(tp, mmap_syscall); +#endif + ATF_TP_ADD_TC(tp, mmap___syscall); + + return atf_no_error(); +} diff --git a/lib/libc/sys/t_ucontext.c b/lib/libc/sys/t_ucontext.c --- a/lib/libc/sys/t_ucontext.c +++ b/lib/libc/sys/t_ucontext.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_ucontext.c,v 1.1 2011/10/15 06:54:52 jruoho Exp $ */ +/* $NetBSD: t_ucontext.c,v 1.5 2018/02/27 12:20:35 kamil Exp $ */ /* * Copyright (c) 2008 The NetBSD Foundation, Inc. @@ -29,11 +29,12 @@ #include __COPYRIGHT("@(#) Copyright (c) 2008\ The NetBSD Foundation, inc. All rights reserved."); -__RCSID("$NetBSD: t_ucontext.c,v 1.1 2011/10/15 06:54:52 jruoho Exp $"); +__RCSID("$NetBSD: t_ucontext.c,v 1.5 2018/02/27 12:20:35 kamil Exp $"); #include #include #include +#include ATF_TC(ucontext_basic); ATF_TC_HEAD(ucontext_basic, tc) @@ -68,9 +69,73 @@ ATF_REQUIRE_EQ(y, 21); } +ATF_TC(ucontext_sp); +ATF_TC_HEAD(ucontext_sp, tc) +{ + atf_tc_set_md_var(tc, "descr", "Retrieve _UC_MACHINE_SP()"); +} + +ATF_TC_BODY(ucontext_sp, tc) +{ + ucontext_t u; + + getcontext(&u); + + printf("_UC_MACHINE_SP(u)=%" PRIxREGISTER "\n", (register_t)_UC_MACHINE_SP(&u)); +} + +ATF_TC(ucontext_fp); +ATF_TC_HEAD(ucontext_fp, tc) +{ + atf_tc_set_md_var(tc, "descr", "Retrieve _UC_MACHINE_FP()"); +} + +ATF_TC_BODY(ucontext_fp, tc) +{ + ucontext_t u; + + getcontext(&u); + + printf("_UC_MACHINE_FP(u)=%" PRIxREGISTER "\n", (register_t)_UC_MACHINE_FP(&u)); +} + +ATF_TC(ucontext_pc); +ATF_TC_HEAD(ucontext_pc, tc) +{ + atf_tc_set_md_var(tc, "descr", "Retrieve _UC_MACHINE_PC()"); +} + +ATF_TC_BODY(ucontext_pc, tc) +{ + ucontext_t u; + + getcontext(&u); + + printf("_UC_MACHINE_PC(u)=%" PRIxREGISTER "\n", (register_t)_UC_MACHINE_PC(&u)); +} + +ATF_TC(ucontext_intrv); +ATF_TC_HEAD(ucontext_intrv, tc) +{ + atf_tc_set_md_var(tc, "descr", "Retrieve _UC_MACHINE_INTRV()"); +} + +ATF_TC_BODY(ucontext_intrv, tc) +{ + ucontext_t u; + + getcontext(&u); + + printf("_UC_MACHINE_INTRV(u)=%" PRIxREGISTER "\n", (register_t)_UC_MACHINE_INTRV(&u)); +} + ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, ucontext_basic); + ATF_TP_ADD_TC(tp, ucontext_sp); + ATF_TP_ADD_TC(tp, ucontext_fp); + ATF_TP_ADD_TC(tp, ucontext_pc); + ATF_TP_ADD_TC(tp, ucontext_intrv); return atf_no_error(); } diff --git a/lib/libc/sys/t_vfork.c b/lib/libc/sys/t_vfork.c new file mode 100644 --- /dev/null +++ b/lib/libc/sys/t_vfork.c @@ -0,0 +1,30 @@ +/* $NetBSD: t_vfork.c,v 1.1 2018/05/18 06:39:58 kamil Exp $ */ + +/*- + * Copyright (c) 2018 The NetBSD Foundation, Inc. + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +#define VFORK +#include "t_fork.c" diff --git a/lib/libc/sys/t_wait.c b/lib/libc/sys/t_wait.c --- a/lib/libc/sys/t_wait.c +++ b/lib/libc/sys/t_wait.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_wait.c,v 1.8 2017/01/13 19:28:55 christos Exp $ */ +/* $NetBSD: t_wait.c,v 1.10 2021/07/17 14:03:35 martin Exp $ */ /*- * Copyright (c) 2016 The NetBSD Foundation, Inc. @@ -29,7 +29,7 @@ * POSSIBILITY OF SUCH DAMAGE. */ #include -__RCSID("$NetBSD: t_wait.c,v 1.8 2017/01/13 19:28:55 christos Exp $"); +__RCSID("$NetBSD: t_wait.c,v 1.10 2021/07/17 14:03:35 martin Exp $"); #include #include @@ -76,15 +76,16 @@ pid_t pid; switch (pid = fork()) { - case -1: - ATF_REQUIRE(pid > 0); case 0: exit(0x5a5a5a5a); /*NOTREACHED*/ + case -1: + ATF_REQUIRE(pid > 0); + __unreachable(); default: ATF_REQUIRE(wait6(P_PID, pid, &st, WEXITED, &wru, &si) == pid); ATF_REQUIRE(WIFEXITED(st) && WEXITSTATUS(st) == 0x5a); - ATF_REQUIRE(si.si_status = 0x5a5a5a5a); + ATF_REQUIRE(si.si_status == 0x5a5a5a5a); ATF_REQUIRE(si.si_pid == pid); ATF_REQUIRE(si.si_uid == getuid()); ATF_REQUIRE(si.si_code == CLD_EXITED); @@ -114,6 +115,7 @@ /*FALLTHROUGH*/ case -1: ATF_REQUIRE(pid > 0); + __unreachable(); default: ATF_REQUIRE(kill(pid, SIGTERM) == 0); ATF_REQUIRE(wait6(P_PID, pid, &st, WEXITED, &wru, &si) == pid); @@ -150,6 +152,7 @@ /*FALLTHROUGH*/ case -1: ATF_REQUIRE(pid > 0); + __unreachable(); default: ATF_REQUIRE(wait6(P_PID, pid, &st, WEXITED, &wru, &si) == pid); ATF_REQUIRE(WIFSIGNALED(st) && WTERMSIG(st) == SIGSEGV @@ -186,6 +189,7 @@ /*FALLTHROUGH*/ case -1: ATF_REQUIRE(pid > 0); + __unreachable(); default: ATF_REQUIRE(kill(pid, SIGSTOP) == 0); ATF_REQUIRE(wait6(P_PID, pid, &st, WSTOPPED, &wru, &si) == pid); @@ -252,6 +256,7 @@ /*FALLTHROUGH*/ case -1: ATF_REQUIRE(pid > 0); + __unreachable(); } printf("Before loop of SIGSTOP/SIGCONT sequence %zu times\n", N); diff --git a/lib/libc/sys/t_wait_noproc.c b/lib/libc/sys/t_wait_noproc.c --- a/lib/libc/sys/t_wait_noproc.c +++ b/lib/libc/sys/t_wait_noproc.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_wait_noproc.c,v 1.5 2016/11/09 17:50:19 kamil Exp $ */ +/* $NetBSD: t_wait_noproc.c,v 1.6 2020/06/15 13:57:45 christos Exp $ */ /*- * Copyright (c) 2016 The NetBSD Foundation, Inc. @@ -27,7 +27,7 @@ */ #include -__RCSID("$NetBSD: t_wait_noproc.c,v 1.5 2016/11/09 17:50:19 kamil Exp $"); +__RCSID("$NetBSD: t_wait_noproc.c,v 1.6 2020/06/15 13:57:45 christos Exp $"); #include #include @@ -141,7 +141,7 @@ */ const int matrix[] = { - WNOWAIT, /* First in order to blacklist it easily */ + WNOWAIT, /* First in order to exclude it easily */ WEXITED, WUNTRACED, WSTOPPED, /* SUS compatibility, equal to WUNTRACED */ diff --git a/lib/libc/sys/t_write.c b/lib/libc/sys/t_write.c --- a/lib/libc/sys/t_write.c +++ b/lib/libc/sys/t_write.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_write.c,v 1.3 2017/01/13 19:27:23 christos Exp $ */ +/* $NetBSD: t_write.c,v 1.7 2019/07/16 17:29:18 martin Exp $ */ /*- * Copyright (c) 2001, 2008 The NetBSD Foundation, Inc. @@ -29,9 +29,10 @@ #include __COPYRIGHT("@(#) Copyright (c) 2008\ The NetBSD Foundation, inc. All rights reserved."); -__RCSID("$NetBSD: t_write.c,v 1.3 2017/01/13 19:27:23 christos Exp $"); +__RCSID("$NetBSD: t_write.c,v 1.7 2019/07/16 17:29:18 martin Exp $"); #include +#include #include #include @@ -42,6 +43,7 @@ #include #include #include +#include static void sighandler(int); @@ -69,7 +71,7 @@ errno = 0; ATF_REQUIRE_ERRNO(EBADF, write(-1, wbuf, sizeof(wbuf)) == -1); - fd = open(path, O_RDWR | O_CREAT); + fd = open(path, O_RDWR | O_CREAT, 0600); if (fd >= 0) { @@ -141,7 +143,7 @@ size_t i; int fd; - fd = open(path, O_RDWR | O_CREAT); + fd = open(path, O_RDWR | O_CREAT, 0600); ATF_REQUIRE(fd >= 0); for (i = 0; i < n; i++) { @@ -171,7 +173,7 @@ size_t i, j; int fd; - fd = open(path, O_WRONLY | O_CREAT); + fd = open(path, O_WRONLY | O_CREAT, 0600); ATF_REQUIRE(fd >= 0); (void)memset(buf, 'x', sizeof(buf)); @@ -213,6 +215,60 @@ ATF_REQUIRE_EQ_MSG(errno, EINVAL, "got: %s", strerror(errno)); } +ATF_TC(write_fault); + +ATF_TC_HEAD(write_fault, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Check that writing to non-permitted space returns EFAULT"); +} + +#define SIZE 8192 + +ATF_TC_BODY(write_fault, tc) +{ + int fd[2]; + ATF_REQUIRE(pipe(fd) != -1); + // Can't use /dev/null cause it doesn't access the buffer. + + void *map = mmap(NULL, SIZE, PROT_NONE, MAP_ANON | MAP_PRIVATE, -1, 0); + ATF_REQUIRE(map != MAP_FAILED); + + ssize_t retval = write(fd[1], map, SIZE); + + ATF_REQUIRE_EQ_MSG(retval, -1, "got: %zd", retval); + ATF_REQUIRE_EQ_MSG(errno, EFAULT, "got: %s", strerror(errno)); + + munmap(map, SIZE); + close(fd[0]); + close(fd[1]); +} + +ATF_TC(read_fault); + +ATF_TC_HEAD(read_fault, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Check that reading from non-permitted space returns EFAULT"); +} + +ATF_TC_BODY(read_fault, tc) +{ + int fd = open(_PATH_DEVZERO, O_RDONLY); + ATF_REQUIRE(fd != -1); + + void *map = mmap(NULL, SIZE, PROT_NONE, MAP_ANON | MAP_PRIVATE, -1, 0); + ATF_REQUIRE(map != MAP_FAILED); + + ssize_t retval = read(fd, map, SIZE); + + ATF_REQUIRE_EQ_MSG(retval, -1, "got: %zd", retval); + ATF_REQUIRE_EQ_MSG(errno, EFAULT, "got: %s", strerror(errno)); + + munmap(map, SIZE); + close(fd); +} + ATF_TP_ADD_TCS(tp) { @@ -221,6 +277,8 @@ ATF_TP_ADD_TC(tp, write_pos); ATF_TP_ADD_TC(tp, write_ret); ATF_TP_ADD_TC(tp, writev_iovmax); + ATF_TP_ADD_TC(tp, write_fault); + ATF_TP_ADD_TC(tp, read_fault); return atf_no_error(); } diff --git a/lib/libc/time/t_mktime.c b/lib/libc/time/t_mktime.c --- a/lib/libc/time/t_mktime.c +++ b/lib/libc/time/t_mktime.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_mktime.c,v 1.5 2012/03/18 07:33:58 jruoho Exp $ */ +/* $NetBSD: t_mktime.c,v 1.6 2017/10/27 00:55:27 kre Exp $ */ /*- * Copyright (c) 2011 The NetBSD Foundation, Inc. @@ -68,11 +68,12 @@ time_t t; (void)memset(&tms, 0, sizeof(tms)); - tms.tm_year = ~0; + tms.tm_year = -1; + tms.tm_mday = 1; errno = 0; t = mktime(&tms); - ATF_REQUIRE_ERRNO(0, t != (time_t)-1); + ATF_REQUIRE(t != (time_t)-1); } ATF_TC(timegm_epoch); @@ -92,7 +93,7 @@ tms.tm_year = 1970 - 1900; tms.tm_mday = 1; t = timegm(&tms); - ATF_REQUIRE_ERRNO(0, t == (time_t)0); + ATF_REQUIRE(t == (time_t)0); /* one second after midnight on 1 Jan 1970 */ (void)memset(&tms, 0, sizeof(tms)); @@ -101,7 +102,7 @@ tms.tm_mday = 1; tms.tm_sec = 1; t = timegm(&tms); - ATF_REQUIRE_ERRNO(0, t == (time_t)1); + ATF_REQUIRE(t == (time_t)1); /* * 1969-12-31 23:59:59 = one second before the epoch. @@ -116,7 +117,8 @@ tms.tm_min = 59; tms.tm_sec = 59; t = timegm(&tms); - ATF_REQUIRE_ERRNO(0, t == (time_t)-1); + ATF_REQUIRE(t == (time_t)-1); + /* ATF_REQUIRE(errno == 0); does not work, errno is kept clear */ /* * Another way of getting one second before the epoch: @@ -128,7 +130,7 @@ tms.tm_mday = 1; tms.tm_sec = -1; t = timegm(&tms); - ATF_REQUIRE_ERRNO(0, t == (time_t)-1); + ATF_REQUIRE(t == (time_t)-1); /* * Two seconds before the epoch. @@ -139,7 +141,7 @@ tms.tm_mday = 1; tms.tm_sec = -2; t = timegm(&tms); - ATF_REQUIRE_ERRNO(0, t == (time_t)-2); + ATF_REQUIRE(t == (time_t)-2); } diff --git a/lib/libc/time/t_strptime.c b/lib/libc/time/t_strptime.c --- a/lib/libc/time/t_strptime.c +++ b/lib/libc/time/t_strptime.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_strptime.c,v 1.12 2015/10/31 02:25:11 christos Exp $ */ +/* $NetBSD: t_strptime.c,v 1.15 2018/06/03 08:48:37 maya Exp $ */ /*- * Copyright (c) 1998, 2008 The NetBSD Foundation, Inc. @@ -32,7 +32,7 @@ #include __COPYRIGHT("@(#) Copyright (c) 2008\ The NetBSD Foundation, inc. All rights reserved."); -__RCSID("$NetBSD: t_strptime.c,v 1.12 2015/10/31 02:25:11 christos Exp $"); +__RCSID("$NetBSD: t_strptime.c,v 1.15 2018/06/03 08:48:37 maya Exp $"); #include #include @@ -51,12 +51,12 @@ exp = buf + len; ret = strptime(buf, fmt, &tm); - ATF_REQUIRE_MSG(ret == exp, + ATF_CHECK_MSG(ret == exp, "strptime(\"%s\", \"%s\", tm): incorrect return code: " "expected: %p, got: %p", buf, fmt, exp, ret); #define H_REQUIRE_FIELD(field) \ - ATF_REQUIRE_MSG(tm.field == field, \ + ATF_CHECK_MSG(tm.field == field, \ "strptime(\"%s\", \"%s\", tm): incorrect %s: " \ "expected: %d, but got: %d", buf, fmt, \ ___STRING(field), field, tm.field) @@ -78,7 +78,7 @@ { struct tm tm = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, NULL }; - ATF_REQUIRE_MSG(strptime(buf, fmt, &tm) == NULL, "strptime(\"%s\", " + ATF_CHECK_MSG(strptime(buf, fmt, &tm) == NULL, "strptime(\"%s\", " "\"%s\", &tm) should fail, but it didn't", buf, fmt); } @@ -126,28 +126,28 @@ { "+1060", -1 }, { "-1060", -1 }, - { "A", -3600 }, - { "B", -7200 }, - { "C", -10800 }, - { "D", -14400 }, - { "E", -18000 }, - { "F", -21600 }, - { "G", -25200 }, - { "H", -28800 }, - { "I", -32400 }, - { "L", -39600 }, - { "M", -43200 }, - { "N", 3600 }, - { "O", 7200 }, - { "P", 10800 }, - { "Q", 14400 }, - { "R", 18000 }, - { "T", 25200 }, - { "U", 28800 }, - { "V", 32400 }, - { "W", 36000 }, - { "X", 39600 }, - { "Y", 43200 }, + { "A", 3600 }, + { "B", 7200 }, + { "C", 10800 }, + { "D", 14400 }, + { "E", 18000 }, + { "F", 21600 }, + { "G", 25200 }, + { "H", 28800 }, + { "I", 32400 }, + { "L", 39600 }, + { "M", 43200 }, + { "N", -3600 }, + { "O", -7200 }, + { "P", -10800 }, + { "Q", -14400 }, + { "R", -18000 }, + { "T", -25200 }, + { "U", -28800 }, + { "V", -32400 }, + { "W", -36000 }, + { "X", -39600 }, + { "Y", -43200 }, { "J", -2 }, @@ -182,7 +182,7 @@ break; } - ATF_REQUIRE_MSG(tm.tm_gmtoff == value, + ATF_CHECK_MSG(tm.tm_gmtoff == value, "strptime(\"%s\", \"%s\", &tm): " "expected: tm.tm_gmtoff=%ld, got: tm.tm_gmtoff=%ld", name, fmt, value, tm.tm_gmtoff); @@ -438,7 +438,7 @@ ATF_TC_BODY(Zone, tc) { - ztest("%z"); + ztest("%Z"); } ATF_TP_ADD_TCS(tp) diff --git a/lib/libc/tls/t_tls_static.c b/lib/libc/tls/t_tls_static.c --- a/lib/libc/tls/t_tls_static.c +++ b/lib/libc/tls/t_tls_static.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_tls_static.c,v 1.2 2012/01/17 20:34:57 joerg Exp $ */ +/* $NetBSD: t_tls_static.c,v 1.4 2017/10/28 19:25:31 christos Exp $ */ /*- * Copyright (c) 2011 The NetBSD Foundation, Inc. * All rights reserved. @@ -32,7 +32,7 @@ */ #include -__RCSID("$NetBSD: t_tls_static.c,v 1.2 2012/01/17 20:34:57 joerg Exp $"); +__RCSID("$NetBSD: t_tls_static.c,v 1.4 2017/10/28 19:25:31 christos Exp $"); #include #include @@ -59,11 +59,13 @@ static void * testf(void *dummy) { - ATF_CHECK_EQ(var1, 1); - ATF_CHECK_EQ(var2, 0); +#define CHECK(a, b) \ + ATF_CHECK_EQ_MSG(var ## a, b, "var%d[%d] != %d", a, var ## a, b) + CHECK(1, 1); + CHECK(2, 0); testf_helper(); - ATF_CHECK_EQ(var1, -1); - ATF_CHECK_EQ(var2, -1); + CHECK(1, -1); + CHECK(2, -1); return NULL; } diff --git a/lib/libcurses/Makefile b/lib/libcurses/Makefile --- a/lib/libcurses/Makefile +++ b/lib/libcurses/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.3 2012/06/03 23:19:11 joerg Exp $ +# $NetBSD: Makefile,v 1.4 2020/02/07 22:05:16 uwe Exp $ NOMAN= # defined @@ -23,7 +23,11 @@ realall: ${TERMINFO_DB}.cdb -${TERMINFO_DB}.cdb: ${TOOL_TIC} ${TEST_TERMINFO} +.if ${USETOOLS} == "yes" +DPTOOL_TIC = ${TOOL_TIC} +.endif + +${TERMINFO_DB}.cdb: ${DPTOOL_TIC} ${TEST_TERMINFO} ${TOOL_TIC} -o ${.TARGET} ${.CURDIR}/${TEST_TERMINFO} .include diff --git a/lib/libcurses/atf.terminfo b/lib/libcurses/atf.terminfo --- a/lib/libcurses/atf.terminfo +++ b/lib/libcurses/atf.terminfo @@ -1,44 +1,134 @@ +# $NetBSD: atf.terminfo,v 1.3 2021/02/13 16:43:12 rillig Exp $ +# # Based on xterm capabilities -atf|atf automatic test frame pseudo terminal, - am, bce, ccc, km, mc5i, mir, msgr, npc, xenl, - colors#8, cols#80, it#8, lines#24, pairs#64, +# +atf|Automated Test Framework pseudo terminal, + am, + bce, + ccc, + km, + mc5i, + mir, + msgr, + npc, + xenl, + colors#8, + cols#80, + it#8, + lines#24, + pairs#64, acsc=++\,\,--..00``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~, - bel=bel, blink=blink, bold=bold, cbt=cbt, civis=civis, clear=clear, - cnorm=cnorm, cr=^M, csr=csr%i%p1%d;%p2%dX, cub=cub%p1%dX, - cub1=^H, cud=cud%p1%dX, cud1=^J, cuf=cuf%p1%dX, cuf1=, - cup=cup%i%p1%d;%p2%dX, cuu=cuu%p1%dX, cuu1=, cvvis=cvvis, - dch=dch%p1%dX, dch1=, dl=dl%p1%dX, dl1= , dim=dim, ech=ech%p1%dX, - ed=ed, el=el, el1=el1, enacs=enacs, flash=flash, home=home, - hpa=hpa%i%p1%dX, ht=^I, hts=hts, ich=ich%p1%dX, il=il%p1%dX, - il1=il1, ind=^M, indn=indn%p1%dX, invis=invis, - is2=is2, kDC=\E[3;2~, kEND=\E[1;2F, kHOM=\E[1;2H, - kIC=\E[2;2~, kLFT=\E[1;2D, kNXT=\E[6;2~, kPRV=\E[5;2~, - kRIT=\E[1;2C, kb2=\EOE, kbs=^H, kcbt=\E[Z, kcub1=\EOD, kcud1=\EOB, - kcuf1=\EOC, kcuu1=\EOA, kdch1=\E[3~, kend=\EOF, kent=\EOM, - kf1=\EOP, kf10=\E[21~, kf11=\E[23~, kf12=\E[24~, kf13=\E[1;2P, - kf14=\E[1;2Q, kf15=\E[1;2R, kf16=\E[1;2S, kf17=\E[15;2~, - kf18=\E[17;2~, kf19=\E[18;2~, kf2=\EOQ, kf20=\E[19;2~, - kf21=\E[20;2~, kf22=\E[21;2~, kf23=\E[23;2~, kf24=\E[24;2~, - kf25=\E[1;5P, kf26=\E[1;5Q, kf27=\E[1;5R, kf28=\E[1;5S, - kf29=\E[15;5~, kf3=\EOR, kf30=\E[17;5~, kf31=\E[18;5~, - kf32=\E[19;5~, kf33=\E[20;5~, kf34=\E[21;5~, kf35=\E[23;5~, - kf36=\E[24;5~, kf37=\E[1;6P, kf38=\E[1;6Q, kf39=\E[1;6R, kf4=\EOS, - kf40=\E[1;6S, kf41=\E[15;6~, kf42=\E[17;6~, kf43=\E[18;6~, - kf44=\E[19;6~, kf45=\E[20;6~, kf46=\E[21;6~, kf47=\E[23;6~, - kf48=\E[24;6~, kf49=\E[1;3P, kf5=\E[15~, kf50=\E[1;3Q, - kf51=\E[1;3R, kf52=\E[1;3S, kf53=\E[15;3~, kf54=\E[17;3~, - kf55=\E[18;3~, kf56=\E[19;3~, kf57=\E[20;3~, kf58=\E[21;3~, - kf59=\E[23;3~, kf6=\E[17~, kf60=\E[24;3~, kf61=\E[1;4P, - kf62=\E[1;4Q, kf63=\E[1;4R, kf7=\E[18~, kf8=\E[19~, kf9=\E[20~, - khome=\EOH, kich1=\E[2~, kind=\E[1;2B, kmous=\E[M, knp=\E[6~, - kpp=\E[5~, kri=\E[1;2A, mc0=mc0, mc4=mc4, mc5=mc5, - op=op, rc=rc, rev=rev, ri=ri, rin=rin%p1%dX, rmacs=rmacs, - rmam=rmam, rmcup=rmcup, rmir=rmir, rmkx=rmkx, - rmm=rmm, rmso=rmso, rmul=rmul, rs1=rs1, - rs2=rs2, sc=sc, setab=setab%p1%dX, - setaf=setaf%p1%dX, setb=setb%p1%dX, setf=setf%p1%dX, + bel=bel, + blink=blink, + bold=bold, + cbt=cbt, + civis=civis, + clear=clear, + cnorm=cnorm, + cr=^M, + csr=csr%i%p1%d;%p2%dX, + cub=cub%p1%dX, cub1=^H, + cud=cud%p1%dX, cud1=^J, + cuf=cuf%p1%dX, cuf1=^F, + cup=cup%i%p1%d;%p2%dX, + cuu=cuu%p1%dX, cuu1=^U, + cvvis=cvvis, + dch=dch%p1%dX, dch1=^D, + dl=dl%p1%dX, dl1=^K, + dim=dim, + ech=ech%p1%dX, + ed=ed, + el=el, + el1=el1, + enacs=enacs, + flash=flash, + home=home, + hpa=hpa%i%p1%dX, + ht=^I, + hts=hts, + ich=ich%p1%dX, + il=il%p1%dX, il1=il1, + ind=^M, + indn=indn%p1%dX, + invis=invis, + is2=is2, + kDC=\E[3;2~, + kEND=\E[1;2F, + kHOM=\E[1;2H, + kIC=\E[2;2~, + kLFT=\E[1;2D, + kNXT=\E[6;2~, + kPRV=\E[5;2~, + kRIT=\E[1;2C, + kb2=\EOE, + kbs=^H, + kcbt=\E[Z, + kcub1=\EOD, + kcud1=\EOB, + kcuf1=\EOC, + kcuu1=\EOA, + kdch1=\E[3~, + kend=\EOF, + kent=\EOM, + kf1=\EOP, kf2=\EOQ, kf3=\EOR, kf4=\EOS, + kf5=\E[15~, kf6=\E[17~, kf7=\E[18~, kf8=\E[19~, + kf9=\E[20~, kf10=\E[21~, kf11=\E[23~, kf12=\E[24~, + kf13=\E[1;2P, kf14=\E[1;2Q, kf15=\E[1;2R, kf16=\E[1;2S, + kf17=\E[15;2~, kf18=\E[17;2~, kf19=\E[18;2~, kf20=\E[19;2~, + kf21=\E[20;2~, kf22=\E[21;2~, kf23=\E[23;2~, kf24=\E[24;2~, + kf25=\E[1;5P, kf26=\E[1;5Q, kf27=\E[1;5R, kf28=\E[1;5S, + kf29=\E[15;5~, kf30=\E[17;5~, kf31=\E[18;5~, kf32=\E[19;5~, + kf33=\E[20;5~, kf34=\E[21;5~, kf35=\E[23;5~, kf36=\E[24;5~, + kf37=\E[1;6P, kf38=\E[1;6Q, kf39=\E[1;6R, kf40=\E[1;6S, + kf41=\E[15;6~, kf42=\E[17;6~, kf43=\E[18;6~, kf44=\E[19;6~, + kf45=\E[20;6~, kf46=\E[21;6~, kf47=\E[23;6~, kf48=\E[24;6~, + kf49=\E[1;3P, kf50=\E[1;3Q, kf51=\E[1;3R, kf52=\E[1;3S, + kf53=\E[15;3~, kf54=\E[17;3~, kf55=\E[18;3~, kf56=\E[19;3~, + kf57=\E[20;3~, kf58=\E[21;3~, kf59=\E[23;3~, kf60=\E[24;3~, + kf61=\E[1;4P, kf62=\E[1;4Q, kf63=\E[1;4R, + khome=\EOH, + kich1=\E[2~, + kind=\E[1;2B, + kmous=\E[M, + knp=\E[6~, + kpp=\E[5~, + kri=\E[1;2A, + mc0=mc0, + mc4=mc4, + mc5=mc5, + op=op, + rc=rc, + rev=rev, + ri=ri, + rin=rin%p1%dX, + rmacs=rmacs, + rmam=rmam, + rmcup=rmcup, + rmir=rmir, + rmkx=rmkx, + rmm=rmm, + rmso=rmso, + rmul=rmul, + rs1=rs1, + rs2=rs2, + sc=sc, + setab=setab%p1%dX, + setaf=setaf%p1%dX, + setb=setb%p1%dX, + setf=setf%p1%dX, sgr=sgr%p1%d;%p2%d;%p3%d;%p4%d;%p5%d;%p6%d;%p7%d;%p8;%d;%p9%dX, - sgr0=sgr0, smacs=smacs, smam=smam, smcup=smcup, - smir=smir, smkx=smkx, smm=smm, smso=smso, smul=smul, - tbc=tbc, u6=u6%d;%dX, u7=u7, u8=u8, u9=u9, + sgr0=sgr0, + smacs=smacs, + smam=smam, + smcup=smcup, + smir=smir, + smkx=smkx, + smm=smm, + smso=smso, + smul=smul, + tbc=tbc, + u6=u6%d;%dX, + u7=u7, + u8=u8, + u9=u9, vpa=vpa%p1%dX diff --git a/lib/libcurses/check_files/Makefile b/lib/libcurses/check_files/Makefile --- a/lib/libcurses/check_files/Makefile +++ b/lib/libcurses/check_files/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.2 2011/09/08 18:44:38 jmmv Exp $ +# $NetBSD: Makefile,v 1.7 2021/06/06 04:57:58 blymn Exp $ NOMAN= # defined @@ -10,20 +10,42 @@ FILESDIR= ${TESTSDIR}/check_files FILES= curses_start.chk +FILES+= add_wch1.chk +FILES+= add_wch2.chk +FILES+= add_wch3.chk FILES+= addch.chk FILES+= addchstr.chk +FILES+= addchstr2.chk +FILES+= addchstr3.chk +FILES+= addnstr.chk +FILES+= addnwstr1.chk +FILES+= addnwstr2.chk FILES+= addstr.chk +FILES+= addstr2.chk +FILES+= addstr3.chk +FILES+= addwstr1.chk +FILES+= addwstr2.chk +FILES+= addwstr3.chk FILES+= attributes.chk -FILES+= bell.chk FILES+= background1.chk FILES+= background2.chk FILES+= background3.chk FILES+= background4.chk FILES+= background5.chk +FILES+= bell.chk +FILES+= bkgdset1.chk +FILES+= blank.chk +FILES+= border_set1.chk +FILES+= border_set2.chk +FILES+= box_set1.chk +FILES+= box_set2.chk +FILES+= box_standout.chk FILES+= chgat1.chk FILES+= chgat2.chk FILES+= chgat3.chk +FILES+= clear0.chk FILES+= clear1.chk +FILES+= clear10.chk FILES+= clear2.chk FILES+= clear3.chk FILES+= clear4.chk @@ -32,14 +54,21 @@ FILES+= clear7.chk FILES+= clear8.chk FILES+= clear9.chk -FILES+= clear10.chk +FILES+= clearok1.chk +FILES+= clearok2.chk +FILES+= clearok3.chk FILES+= color_blank_draw.chk -FILES+= color_start.chk -FILES+= color_default.chk FILES+= color_blue_back.chk +FILES+= color_default.chk FILES+= color_red_fore.chk FILES+= color_set.chk +FILES+= color_start.chk FILES+= copywin1.chk +FILES+= copywin10.chk +FILES+= copywin11.chk +FILES+= copywin12.chk +FILES+= copywin13.chk +FILES+= copywin14.chk FILES+= copywin2.chk FILES+= copywin3.chk FILES+= copywin4.chk @@ -48,26 +77,199 @@ FILES+= copywin7.chk FILES+= copywin8.chk FILES+= copywin9.chk -FILES+= copywin10.chk -FILES+= copywin11.chk -FILES+= copywin12.chk -FILES+= copywin13.chk -FILES+= copywin14.chk FILES+= curs_set1.chk FILES+= curs_set2.chk FILES+= curs_set3.chk +FILES+= delay_output.chk +FILES+= delch1.chk +FILES+= delch2.chk +FILES+= delch3.chk +FILES+= delch4.chk +FILES+= delch5.chk +FILES+= delch6.chk +FILES+= delch7.chk +FILES+= deleteln1.chk +FILES+= deleteln2.chk +FILES+= deleteln3.chk +FILES+= deleteln4.chk +FILES+= deleteln5.chk +FILES+= deleteln6.chk +FILES+= delwin1.chk +FILES+= delwin2.chk +FILES+= derwin1.chk +FILES+= derwin2.chk +FILES+= doupdate.chk +FILES+= dupwin1.chk +FILES+= dupwin2.chk +FILES+= echochar1.chk +FILES+= echochar2.chk FILES+= fill.chk +FILES+= fill_screen_numbers.chk +FILES+= fill_window_numbers.chk +FILES+= flash.chk +FILES+= get_wstr.chk +FILES+= getch.chk +FILES+= getn_wstr.chk +FILES+= hline.chk +FILES+= hline1.chk +FILES+= hline2.chk +FILES+= hline_set.chk FILES+= home.chk +FILES+= immedok.chk +FILES+= ins_nwstr1.chk +FILES+= ins_wch1.chk +FILES+= ins_wch2.chk +FILES+= ins_wch3.chk +FILES+= ins_wstr1.chk +FILES+= ins_wstr2.chk +FILES+= insch.chk +FILES+= insch1.chk +FILES+= insch2.chk +FILES+= insdelln1.chk +FILES+= insdelln3.chk +FILES+= insdelln4.chk +FILES+= insdelln5.chk +FILES+= insdelln6.chk +FILES+= insdelln7.chk +FILES+= insdelln_1.chk +FILES+= insdelln_3.chk +FILES+= insdelln_4.chk +FILES+= insdelln_5.chk +FILES+= insdelln_6.chk +FILES+= insertln1.chk +FILES+= insertln3.chk +FILES+= insertln4.chk +FILES+= insertln5.chk +FILES+= insertln6.chk +FILES+= leaveok.chk +FILES+= meta1.chk +FILES+= meta2.chk +FILES+= mutt_test1.chk +FILES+= mutt_test2.chk +FILES+= mutt_test3.chk +FILES+= mutt_test4.chk +FILES+= mutt_test5.chk +FILES+= mutt_test6.chk +FILES+= mutt_test7.chk +FILES+= mutt_test8.chk +FILES+= mutt_test9.chk +FILES+= mvaddch.chk +FILES+= mvaddchnstr.chk +FILES+= mvaddchnstr2.chk +FILES+= mvaddchstr.chk +FILES+= mvaddnstr.chk +FILES+= mvaddnstr2.chk +FILES+= mvaddnwstr1.chk +FILES+= mvaddstr.chk +FILES+= mvaddstr2.chk +FILES+= mvaddwstr1.chk +FILES+= mvchgat.chk +FILES+= mvchgat2.chk +FILES+= mvcur.chk +FILES+= mvderwin1.chk +FILES+= mvderwin2.chk +FILES+= mvget_wch.chk +FILES+= mvgetnstr1.chk +FILES+= mvgetnstr2.chk +FILES+= mvgetnstr3.chk +FILES+= mvgetnstr4.chk +FILES+= mvgetstr1.chk +FILES+= mvgetstr2.chk +FILES+= mvgetstr3.chk +FILES+= mvins_wch.chk +FILES+= mvprintw.chk +FILES+= mvvline1.chk +FILES+= mvvline2.chk +FILES+= mvvline3.chk +FILES+= mvwaddnwstr1.chk +FILES+= mvwaddstr.chk +FILES+= mvwaddwstr1.chk +FILES+= mvwaddwstr2.chk +FILES+= mvwaddwstr3.chk +FILES+= mvwchgat1.chk +FILES+= mvwchgat2.chk +FILES+= mvwins_wch.chk +FILES+= notimeout.chk +FILES+= overlay1.chk +FILES+= overlay2.chk +FILES+= overwrite1.chk +FILES+= overwrite2.chk +FILES+= overwrite3.chk +FILES+= overwrite4.chk +FILES+= pad1.chk +FILES+= pad2.chk +FILES+= pad3.chk +FILES+= pechochar1.chk +FILES+= pechochar2.chk +FILES+= redrawwin1.chk +FILES+= redrawwin2.chk +FILES+= redrawwin3.chk +FILES+= scroll1.chk +FILES+= scroll2.chk +FILES+= setscrreg.chk +FILES+= slk1.chk +FILES+= slk2.chk +FILES+= slk3.chk +FILES+= slk4.chk +FILES+= slk5.chk +FILES+= slk6.chk +FILES+= slk_init.chk FILES+= timeout.chk -FILES+= box_standout.chk +FILES+= touchline1.chk +FILES+= touchline2.chk +FILES+= touchoverlap1.chk +FILES+= touchoverlap2.chk +FILES+= touchwin.chk +FILES+= two_window.chk +FILES+= untouchwin.chk +FILES+= vline_set.chk +FILES+= wadd_wch1.chk +FILES+= wadd_wch2.chk +FILES+= waddch.chk +FILES+= waddchnstr.chk +FILES+= waddchstr.chk +FILES+= waddnwstr1.chk +FILES+= waddnwstr2.chk +FILES+= waddstr.chk +FILES+= waddwstr1.chk +FILES+= waddwstr2.chk +FILES+= waddwstr3.chk FILES+= wborder.chk FILES+= wborder_refresh.chk -FILES+= window.chk -FILES+= wscrl1.chk -FILES+= wscrl2.chk +FILES+= wborder_set1.chk +FILES+= wborder_set2.chk +FILES+= wchgat1.chk +FILES+= wchgat2.chk +FILES+= wchgat3.chk +FILES+= wcolor_set.chk +FILES+= wget_wstr.chk +FILES+= wgetch.chk +FILES+= wgetn_wstr.chk FILES+= wgetstr.chk FILES+= wgetstr_refresh.chk +FILES+= whline1.chk +FILES+= whline2.chk +FILES+= whline_set.chk +FILES+= window.chk +FILES+= window2.chk +FILES+= window_hierarchy.chk +FILES+= wins_wch1.chk +FILES+= wins_wch2.chk +FILES+= wins_wch3.chk +FILES+= wins_wstr1.chk +FILES+= wins_wstr2.chk +FILES+= winsch1.chk +FILES+= winsch2.chk FILES+= wprintw_refresh.chk +FILES+= wredrawln1.chk +FILES+= wredrawln2.chk +FILES+= wscrl1.chk +FILES+= wscrl2.chk +FILES+= wsetscrreg.chk +FILES+= wtouchln.chk +FILES+= wvline1.chk +FILES+= wvline2.chk +FILES+= wvline_set.chk CLEANFILES= diff --git a/lib/libcurses/check_files/add_wch1.chk b/lib/libcurses/check_files/add_wch1.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/add_wch1.chk @@ -0,0 +1 @@ +cup15;15XHHH \ No newline at end of file diff --git a/lib/libcurses/check_files/add_wch2.chk b/lib/libcurses/check_files/add_wch2.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/add_wch2.chk @@ -0,0 +1 @@ +cup15;80XHcup16;1XHHHHHHHH \ No newline at end of file diff --git a/lib/libcurses/check_files/add_wch3.chk b/lib/libcurses/check_files/add_wch3.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/add_wch3.chk @@ -0,0 +1,2 @@ +smso㐁rmsohome +㐁㐁  \ No newline at end of file diff --git a/lib/libcurses/check_files/addch.chk b/lib/libcurses/check_files/addch.chk --- a/lib/libcurses/check_files/addch.chk +++ b/lib/libcurses/check_files/addch.chk @@ -1 +1,2 @@ -smsotrmso \ No newline at end of file +smsotrmsocup6;4Xsmsosmulermsormulcup7;9X8 +0123456 8 diff --git a/lib/libcurses/check_files/addchstr2.chk b/lib/libcurses/check_files/addchstr2.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/addchstr2.chk @@ -0,0 +1 @@ +smulrevfgh rmulsgr0 \ No newline at end of file diff --git a/lib/libcurses/check_files/addchstr3.chk b/lib/libcurses/check_files/addchstr3.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/addchstr3.chk @@ -0,0 +1 @@ +cup2;79Xsmulijcup2;79Xrmul \ No newline at end of file diff --git a/lib/libcurses/check_files/addstr.chk b/lib/libcurses/check_files/addnstr.chk copy from lib/libcurses/check_files/addstr.chk copy to lib/libcurses/check_files/addnstr.chk diff --git a/lib/libcurses/check_files/addnwstr1.chk b/lib/libcurses/check_files/addnwstr1.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/addnwstr1.chk @@ -0,0 +1 @@ +AB㐁C \ No newline at end of file diff --git a/lib/libcurses/check_files/addnwstr2.chk b/lib/libcurses/check_files/addnwstr2.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/addnwstr2.chk @@ -0,0 +1 @@ +homeb \ No newline at end of file diff --git a/lib/libcurses/check_files/addstr.chk b/lib/libcurses/check_files/addstr.chk --- a/lib/libcurses/check_files/addstr.chk +++ b/lib/libcurses/check_files/addstr.chk @@ -1 +1,2 @@ -abcde \ No newline at end of file +abcdecup3;9X8 +0123456 8 diff --git a/lib/libcurses/check_files/addstr2.chk b/lib/libcurses/check_files/addstr2.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/addstr2.chk @@ -0,0 +1 @@ +cup24;1X0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890123456cup24;1Xclearcup24;76Xa rmamhsmamcup24;1X \ No newline at end of file diff --git a/lib/libcurses/check_files/addstr3.chk b/lib/libcurses/check_files/addstr3.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/addstr3.chk @@ -0,0 +1,4 @@ +csr2;24Xhomehome +rin1Xcsr1;24Xhomehomecup24;1Xhome +()cup23;1X0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*cup2;3Xclear +hellocup23;76Xacup2;6X \ No newline at end of file diff --git a/lib/libcurses/check_files/addwstr1.chk b/lib/libcurses/check_files/addwstr1.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/addwstr1.chk @@ -0,0 +1 @@ +AB㐁CDE \ No newline at end of file diff --git a/lib/libcurses/check_files/addwstr2.chk b/lib/libcurses/check_files/addwstr2.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/addwstr2.chk @@ -0,0 +1 @@ +home \ No newline at end of file diff --git a/lib/libcurses/check_files/addwstr3.chk b/lib/libcurses/check_files/addwstr3.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/addwstr3.chk @@ -0,0 +1 @@ +cup3;78XABcup4;1X㐁CDE \ No newline at end of file diff --git a/lib/libcurses/check_files/background1.chk b/lib/libcurses/check_files/background1.chk --- a/lib/libcurses/check_files/background1.chk +++ b/lib/libcurses/check_files/background1.chk @@ -1 +1 @@ -smulAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcup2;1XAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcup3;1XAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcup4;1XAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcup5;1XAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcup6;1XAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcup7;1XAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcup8;1XAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcup9;1XAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcup10;1XAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcup11;1XAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcup12;1XAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcup13;1XAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcup14;1XAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcup15;1XAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcup16;1XAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcup17;1XAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcup18;1XAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcup19;1XAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcup20;1XAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcup21;1XAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcup22;1XAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcup23;1XAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcup24;1XAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcup1;1Xrmul \ No newline at end of file +smulAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcup2;1XAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcup3;1XAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcup4;1XAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcup5;1XAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcup6;1XAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcup7;1XAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcup8;1XAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcup9;1XAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcup10;1XAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcup11;1XAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcup12;1XAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcup13;1XAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcup14;1XAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcup15;1XAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcup16;1XAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcup17;1XAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcup18;1XAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcup19;1XAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcup20;1XAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcup21;1XAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcup22;1XAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcup23;1XAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcup24;1XAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAArmamAsmamcup1;1Xrmul diff --git a/lib/libcurses/check_files/bkgdset1.chk b/lib/libcurses/check_files/bkgdset1.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/bkgdset1.chk @@ -0,0 +1 @@ +smulthis is a testrmul \ No newline at end of file diff --git a/lib/libcurses/check_files/blank.chk b/lib/libcurses/check_files/blank.chk new file mode 100644 diff --git a/lib/libcurses/check_files/border_set1.chk b/lib/libcurses/check_files/border_set1.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/border_set1.chk @@ -0,0 +1 @@ +EsmsoCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCrmsoFcup2;1XAcup2;80XBcup3;1XAcup3;80XBcup4;1XAcup4;80XBcup5;1XAcup5;80XBcup6;1XAcup6;80XBcup7;1XAcup7;80XBcup8;1XAcup8;80XBcup9;1XAcup9;80XBcup10;1XAcup10;80XBcup11;1XAcup11;80XBcup12;1XAcup12;80XBcup13;1XAcup13;80XBcup14;1XAcup14;80XBcup15;1XAcup15;80XBcup16;1XAcup16;80XBcup17;1XAcup17;80XBcup18;1XAcup18;80XBcup19;1XAcup19;80XBcup20;1XAcup20;80XBcup21;1XAcup21;80XBcup22;1XAcup22;80XBcup23;1XAcup23;80XBcup24;1XsmsoGrmsoDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDrmamHsmamcup1;1X \ No newline at end of file diff --git a/lib/libcurses/check_files/border_set2.chk b/lib/libcurses/check_files/border_set2.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/border_set2.chk @@ -0,0 +1 @@ +smacslqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqkrmacscup2;1Xsmacsxrmacscup2;80Xsmacsxrmacscup3;1Xsmacsxrmacscup3;80Xsmacsxrmacscup4;1Xsmacsxrmacscup4;80Xsmacsxrmacscup5;1Xsmacsxrmacscup5;80Xsmacsxrmacscup6;1Xsmacsxrmacscup6;80Xsmacsxrmacscup7;1Xsmacsxrmacscup7;80Xsmacsxrmacscup8;1Xsmacsxrmacscup8;80Xsmacsxrmacscup9;1Xsmacsxrmacscup9;80Xsmacsxrmacscup10;1Xsmacsxrmacscup10;80Xsmacsxrmacscup11;1Xsmacsxrmacscup11;80Xsmacsxrmacscup12;1Xsmacsxrmacscup12;80Xsmacsxrmacscup13;1Xsmacsxrmacscup13;80Xsmacsxrmacscup14;1Xsmacsxrmacscup14;80Xsmacsxrmacscup15;1Xsmacsxrmacscup15;80Xsmacsxrmacscup16;1Xsmacsxrmacscup16;80Xsmacsxrmacscup17;1Xsmacsxrmacscup17;80Xsmacsxrmacscup18;1Xsmacsxrmacscup18;80Xsmacsxrmacscup19;1Xsmacsxrmacscup19;80Xsmacsxrmacscup20;1Xsmacsxrmacscup20;80Xsmacsxrmacscup21;1Xsmacsxrmacscup21;80Xsmacsxrmacscup22;1Xsmacsxrmacscup22;80Xsmacsxrmacscup23;1Xsmacsxrmacscup23;80Xsmacsxrmacscup24;1Xsmacsmqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrmacsrmamsmacsjsmamrmacscup1;1X \ No newline at end of file diff --git a/lib/libcurses/check_files/box_set1.chk b/lib/libcurses/check_files/box_set1.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/box_set1.chk @@ -0,0 +1,6 @@ +smacslrmacssmulBBBBrmulsmacskrmacs + smsoAA +AA +AA +AA +rmsosmacsmrmacssmulBBBBrmulsmacsjrmacscup3;6X \ No newline at end of file diff --git a/lib/libcurses/check_files/box_set2.chk b/lib/libcurses/check_files/box_set2.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/box_set2.chk @@ -0,0 +1,5 @@ +smacsqqqqrmacs + smacsxrmacs smacsxrmacs + smacsxrmacs smacsxrmacs + smacsxrmacs smacsxrmacs + smacsxrmacs smacsxrmacscup8;7Xsmacsqqqqrmacscup3;6X \ No newline at end of file diff --git a/lib/libcurses/check_files/clear0.chk b/lib/libcurses/check_files/clear0.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/clear0.chk @@ -0,0 +1,2 @@ +abcdecup3;9X8 +0123456 8 diff --git a/lib/libcurses/check_files/clearok1.chk b/lib/libcurses/check_files/clearok1.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/clearok1.chk @@ -0,0 +1 @@ +smsoarmsoclearsmsoabrmsosmsocrmso \ No newline at end of file diff --git a/lib/libcurses/check_files/clearok2.chk b/lib/libcurses/check_files/clearok2.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/clearok2.chk @@ -0,0 +1 @@ +smsoarmsosmsobrmsosmsocrmso \ No newline at end of file diff --git a/lib/libcurses/check_files/clearok3.chk b/lib/libcurses/check_files/clearok3.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/clearok3.chk @@ -0,0 +1 @@ +cup1;4Xsmsoarmsoclearsmsoabcabcup3;6Xabccup1;6Xrmsosmsocrmso \ No newline at end of file diff --git a/lib/libcurses/check_files/copywin10.chk b/lib/libcurses/check_files/copywin10.chk --- a/lib/libcurses/check_files/copywin10.chk +++ b/lib/libcurses/check_files/copywin10.chk @@ -1 +1 @@ -cup11;15Xt s i cup12;15Xg e t cup13;15Xn t s cup14;15X n t scup15;15Xt n t cup16;15X t n t \ No newline at end of file +cup11;15Xt s i cup12;15Xg e t cup13;15Xn t s cup14;15X n t scup15;15Xt n t cup16;16Xt n t \ No newline at end of file diff --git a/lib/libcurses/check_files/copywin11.chk b/lib/libcurses/check_files/copywin11.chk --- a/lib/libcurses/check_files/copywin11.chk +++ b/lib/libcurses/check_files/copywin11.chk @@ -1,4 +1,4 @@ cup3;6Xel el elcup6;7Xel - elcup8;7Xelcup3;6Xcup11;15Xelcup12;15Xelcup13;15Xelcup14;16Xelcup15;15Xelcup16;16Xelcup11;15X \ No newline at end of file + elcup8;7Xelcup3;6Xcup11;15Xelcup12;15Xelcup13;15Xelcup14;16Xelcup15;15Xelcup16;16Xelcup17;15Xel \ No newline at end of file diff --git a/lib/libcurses/check_files/copywin12.chk b/lib/libcurses/check_files/copywin12.chk --- a/lib/libcurses/check_files/copywin12.chk +++ b/lib/libcurses/check_files/copywin12.chk @@ -1,4 +1,4 @@ cup3;6Xt s i g e t n t scup6;7Xn t s - t n tcup8;7Xt n tcup8;11X \ No newline at end of file + t n tcup8;7Xt n t \ No newline at end of file diff --git a/lib/libcurses/check_files/copywin13.chk b/lib/libcurses/check_files/copywin13.chk --- a/lib/libcurses/check_files/copywin13.chk +++ b/lib/libcurses/check_files/copywin13.chk @@ -1 +1 @@ -cup11;16Xe t ncup12;16Xt s icup13;16Xg e tcup14;15Xi g ecup15;16Xi g ecup16;15Xs i g \ No newline at end of file +cup11;16Xe t ncup12;16Xt s icup13;16Xg e tcup14;15Xi g ecup15;16Xi g ecup17;15Xs i ge \ No newline at end of file diff --git a/lib/libcurses/check_files/copywin14.chk b/lib/libcurses/check_files/copywin14.chk --- a/lib/libcurses/check_files/copywin14.chk +++ b/lib/libcurses/check_files/copywin14.chk @@ -1 +1 @@ -cup11;15Xtesticup12;15Xgtestcup13;15Xngtescup14;16Xngtescup15;15Xtingtcup16;16Xtingt \ No newline at end of file +cup11;15Xtesticup12;15Xgtestcup13;15Xngtescup14;16Xngtescup15;15Xtingtcup16;16Xt n t \ No newline at end of file diff --git a/lib/libcurses/check_files/copywin2.chk b/lib/libcurses/check_files/copywin2.chk --- a/lib/libcurses/check_files/copywin2.chk +++ b/lib/libcurses/check_files/copywin2.chk @@ -3,4 +3,4 @@ ngtest ingtes tingte - stingtcup8;11X \ No newline at end of file + stingt \ No newline at end of file diff --git a/lib/libcurses/check_files/copywin5.chk b/lib/libcurses/check_files/copywin5.chk --- a/lib/libcurses/check_files/copywin5.chk +++ b/lib/libcurses/check_files/copywin5.chk @@ -1 +1 @@ -testingtecup12;15Xstingtestcup13;15Xingtestincup14;15Xgtestingtcup15;15Xestingtescup16;15Xtingtesticup16;23X \ No newline at end of file +testingtecup12;15Xstingtestcup13;15Xingtestincup14;15Xgtestingtcup15;15Xestingtescup17;15Xtingtesticup16;15X \ No newline at end of file diff --git a/lib/libcurses/check_files/copywin6.chk b/lib/libcurses/check_files/copywin6.chk --- a/lib/libcurses/check_files/copywin6.chk +++ b/lib/libcurses/check_files/copywin6.chk @@ -1,6 +1,4 @@ -cup3;6Xtestin - stingt +cup3;6Xstingt ingtes gtesti - esting - tingtecup8;11X \ No newline at end of file + estingcup4;6X \ No newline at end of file diff --git a/lib/libcurses/check_files/copywin7.chk b/lib/libcurses/check_files/copywin7.chk --- a/lib/libcurses/check_files/copywin7.chk +++ b/lib/libcurses/check_files/copywin7.chk @@ -1,6 +1,4 @@ -cup3;6Xel +el el el - el - el - elcup11;15Xelcup12;15Xelcup13;15Xelcup14;15Xelcup15;15Xelcup16;15Xel \ No newline at end of file + elcup11;15Xelcup12;15Xelcup13;15Xelcup14;15Xelcup15;15Xelcup17;15Xel \ No newline at end of file diff --git a/lib/libcurses/check_files/copywin8.chk b/lib/libcurses/check_files/copywin8.chk --- a/lib/libcurses/check_files/copywin8.chk +++ b/lib/libcurses/check_files/copywin8.chk @@ -1,4 +1,4 @@ cup3;6Xt s i g e t n t scup6;7Xn t s - t n tcup8;7Xt n tcup8;11X \ No newline at end of file + t n tcup8;7Xt n t \ No newline at end of file diff --git a/lib/libcurses/check_files/copywin9.chk b/lib/libcurses/check_files/copywin9.chk --- a/lib/libcurses/check_files/copywin9.chk +++ b/lib/libcurses/check_files/copywin9.chk @@ -1 +1 @@ -cup11;16Xe t ncup12;16Xt s icup13;16Xg e tcup14;15Xi g ecup15;16Xi g ecup16;15Xs i g \ No newline at end of file +cup11;16Xe t ncup12;16Xt s icup13;16Xg e tcup14;15Xi g ecup15;16Xi g ecup17;15Xs i ge \ No newline at end of file diff --git a/lib/libcurses/check_files/delay_output.chk b/lib/libcurses/check_files/delay_output.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/delay_output.chk @@ -0,0 +1 @@ +@@@@@@@@@@@@@@@test \ No newline at end of file diff --git a/lib/libcurses/check_files/delch1.chk b/lib/libcurses/check_files/delch1.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/delch1.chk @@ -0,0 +1,3 @@ + + + teest \ No newline at end of file diff --git a/lib/libcurses/check_files/delch2.chk b/lib/libcurses/check_files/delch2.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/delch2.chk @@ -0,0 +1 @@ + test te \ No newline at end of file diff --git a/lib/libcurses/check_files/delch3.chk b/lib/libcurses/check_files/delch3.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/delch3.chk @@ -0,0 +1 @@ +stsmsotrmso tessmsotrmso tes \ No newline at end of file diff --git a/lib/libcurses/check_files/delch4.chk b/lib/libcurses/check_files/delch4.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/delch4.chk @@ -0,0 +1 @@ +clear \ No newline at end of file diff --git a/lib/libcurses/check_files/delch5.chk b/lib/libcurses/check_files/delch5.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/delch5.chk @@ -0,0 +1,2 @@ + + tesst \ No newline at end of file diff --git a/lib/libcurses/check_files/delch6.chk b/lib/libcurses/check_files/delch6.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/delch6.chk @@ -0,0 +1 @@ +cup4;9Xt cup4;9X \ No newline at end of file diff --git a/lib/libcurses/check_files/delch7.chk b/lib/libcurses/check_files/delch7.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/delch7.chk @@ -0,0 +1 @@ +tsmsotrmsocup4;9Xsmsotrmso cup4;9X \ No newline at end of file diff --git a/lib/libcurses/check_files/deleteln1.chk b/lib/libcurses/check_files/deleteln1.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/deleteln1.chk @@ -0,0 +1 @@ +csr16;24Xhomehomecup24;1Xindn1Xcsr1;24Xhomehomecup24;1Xcup16;1X \ No newline at end of file diff --git a/lib/libcurses/check_files/deleteln2.chk b/lib/libcurses/check_files/deleteln2.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/deleteln2.chk @@ -0,0 +1 @@ +clear \ No newline at end of file diff --git a/lib/libcurses/check_files/deleteln3.chk b/lib/libcurses/check_files/deleteln3.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/deleteln3.chk @@ -0,0 +1 @@ +cup13;1X33333333333333333333333333333333333333333333333333333333333333333333333333333333cup14;1X44444444444444444444444444444444444444444444444444444444444444444444444444444444cup15;1X55555555555555555555555555555555555555555555555555555555555555555555555555555555cup16;1X66666666666666666666666666666666666666666666666666666666666666666666666666666666cup17;1Xel \ No newline at end of file diff --git a/lib/libcurses/check_files/deleteln4.chk b/lib/libcurses/check_files/deleteln4.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/deleteln4.chk @@ -0,0 +1,3 @@ +555555 + 666666 + el \ No newline at end of file diff --git a/lib/libcurses/check_files/deleteln5.chk b/lib/libcurses/check_files/deleteln5.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/deleteln5.chk @@ -0,0 +1,5 @@ +el + el + el + el + el \ No newline at end of file diff --git a/lib/libcurses/check_files/deleteln6.chk b/lib/libcurses/check_files/deleteln6.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/deleteln6.chk @@ -0,0 +1,2 @@ +555555 + el \ No newline at end of file diff --git a/lib/libcurses/check_files/delwin1.chk b/lib/libcurses/check_files/delwin1.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/delwin1.chk @@ -0,0 +1,10 @@ +cup3;6X0000000000 + 0000000000 + 0000000000 + 0000000000 + 0000000000 + 0000000000 + 0000000000 + 0000000000 + 0000000000 + 0000000000 cup4;7X22222cup5;7X22222cup6;7X22222 \ No newline at end of file diff --git a/lib/libcurses/check_files/delwin2.chk b/lib/libcurses/check_files/delwin2.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/delwin2.chk @@ -0,0 +1,8 @@ +csr4;17Xhomehomecup17;1Xindn3Xcsr1;24Xhomehome + + + + +cup10;6X0000000000 + 00000000000 + 00000000000 \ No newline at end of file diff --git a/lib/libcurses/check_files/derwin1.chk b/lib/libcurses/check_files/derwin1.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/derwin1.chk @@ -0,0 +1,6 @@ +000000 + 000000 + 000000 + 000000 + 000000 + 000000 \ No newline at end of file diff --git a/lib/libcurses/check_files/derwin2.chk b/lib/libcurses/check_files/derwin2.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/derwin2.chk @@ -0,0 +1 @@ +0222cup5;7X222cup6;7X222cup8;6X \ No newline at end of file diff --git a/lib/libcurses/check_files/doupdate.chk b/lib/libcurses/check_files/doupdate.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/doupdate.chk @@ -0,0 +1 @@ +hello world \ No newline at end of file diff --git a/lib/libcurses/check_files/dupwin1.chk b/lib/libcurses/check_files/dupwin1.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/dupwin1.chk @@ -0,0 +1,6 @@ +111111 + 222222 + 333333 + 444444 + 555555 + 666666 cup9;13X111111cup10;13X222222cup11;13X333333cup12;13X444444cup13;13X555555cup14;13X666666 \ No newline at end of file diff --git a/lib/libcurses/check_files/dupwin2.chk b/lib/libcurses/check_files/dupwin2.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/dupwin2.chk @@ -0,0 +1 @@ +cup9;13Xaaaaaacup10;13Xbbbbbbcup11;13Xcccccccup12;13Xddddddcup13;13Xeeeeeecup14;13Xffffffcup14;13X \ No newline at end of file diff --git a/lib/libcurses/check_files/echochar1.chk b/lib/libcurses/check_files/echochar1.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/echochar1.chk @@ -0,0 +1 @@ +smultrmul \ No newline at end of file diff --git a/lib/libcurses/check_files/echochar2.chk b/lib/libcurses/check_files/echochar2.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/echochar2.chk @@ -0,0 +1 @@ +smsosrmso \ No newline at end of file diff --git a/lib/libcurses/check_files/fill_screen_numbers.chk b/lib/libcurses/check_files/fill_screen_numbers.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/fill_screen_numbers.chk @@ -0,0 +1 @@ +00000000000000000000000000000000000000000000000000000000000000000000000000000000cup2;1X11111111111111111111111111111111111111111111111111111111111111111111111111111111cup3;1X22222222222222222222222222222222222222222222222222222222222222222222222222222222cup4;1X33333333333333333333333333333333333333333333333333333333333333333333333333333333cup5;1X44444444444444444444444444444444444444444444444444444444444444444444444444444444cup6;1X55555555555555555555555555555555555555555555555555555555555555555555555555555555cup7;1X66666666666666666666666666666666666666666666666666666666666666666666666666666666cup8;1X77777777777777777777777777777777777777777777777777777777777777777777777777777777cup9;1X88888888888888888888888888888888888888888888888888888888888888888888888888888888cup10;1X99999999999999999999999999999999999999999999999999999999999999999999999999999999cup11;1X00000000000000000000000000000000000000000000000000000000000000000000000000000000cup12;1X11111111111111111111111111111111111111111111111111111111111111111111111111111111cup13;1X22222222222222222222222222222222222222222222222222222222222222222222222222222222cup14;1X33333333333333333333333333333333333333333333333333333333333333333333333333333333cup15;1X44444444444444444444444444444444444444444444444444444444444444444444444444444444cup16;1X55555555555555555555555555555555555555555555555555555555555555555555555555555555cup17;1X66666666666666666666666666666666666666666666666666666666666666666666666666666666cup18;1X77777777777777777777777777777777777777777777777777777777777777777777777777777777cup19;1X88888888888888888888888888888888888888888888888888888888888888888888888888888888cup20;1X99999999999999999999999999999999999999999999999999999999999999999999999999999999cup21;1X00000000000000000000000000000000000000000000000000000000000000000000000000000000cup22;1X11111111111111111111111111111111111111111111111111111111111111111111111111111111cup23;1X22222222222222222222222222222222222222222222222222222222222222222222222222222222cup24;1X3333333333333333333333333333333333333333333333333333333333333333333333333333333rmam3smamcup24;1X \ No newline at end of file diff --git a/lib/libcurses/check_files/fill_window_numbers.chk b/lib/libcurses/check_files/fill_window_numbers.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/fill_window_numbers.chk @@ -0,0 +1,6 @@ +111111 + 222222 + 333333 + 444444 + 555555 + 666666 \ No newline at end of file diff --git a/lib/libcurses/check_files/flash.chk b/lib/libcurses/check_files/flash.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/flash.chk @@ -0,0 +1 @@ +flash \ No newline at end of file diff --git a/lib/libcurses/check_files/get_wstr.chk b/lib/libcurses/check_files/get_wstr.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/get_wstr.chk @@ -0,0 +1,6 @@ + + + 㐁 cup3;6X +a cup4;2X ab cup4;3X cup4;1Xc cup4;2X cd cup4;3X cde cup4;4X cd cup4;3X cdf cup4;4X + a cup5;6X ab cup5;7X a cup5;6X ac cup5;7X acd cup5;8X acd cup5;9X acdO cup5;10X acdOD cup5;11Xsmkx + a cup6;3X ab cup6;4X abc cup6;5X el d cup6;3X \ No newline at end of file diff --git a/lib/libcurses/check_files/getch.chk b/lib/libcurses/check_files/getch.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/getch.chk @@ -0,0 +1,3 @@ +i + + j \ No newline at end of file diff --git a/lib/libcurses/check_files/getn_wstr.chk b/lib/libcurses/check_files/getn_wstr.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/getn_wstr.chk @@ -0,0 +1,6 @@ + + + 㐁 cup3;6X +a cup4;2X ab cup4;3X cup4;1Xc cup4;2X cd cup4;3X cde cup4;4X cd cup4;3X cdf cup4;4X cdf cup4;4X cdf cup4;4X cdf cup4;4X + a cup5;6X ab cup5;7X a cup5;6X ac cup5;7X acd cup5;8X acd cup5;9X acdO cup5;10X acdOD cup5;11Xsmkx + a cup6;3X ab cup6;4X abc cup6;5X el d cup6;3X \ No newline at end of file diff --git a/lib/libcurses/check_files/hline.chk b/lib/libcurses/check_files/hline.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/hline.chk @@ -0,0 +1 @@ +cup6;11XAAAAAAAAAAAAAAAcup8;11XrevBBBBBBBBBBBBBBBcup8;11Xsgr0 \ No newline at end of file diff --git a/lib/libcurses/check_files/hline1.chk b/lib/libcurses/check_files/hline1.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/hline1.chk @@ -0,0 +1 @@ +cup6;11XAAAAAAAAAAAAAAAcup8;11XrevBBBBBBBBBBBBBBBcup8;11Xsgr0 \ No newline at end of file diff --git a/lib/libcurses/check_files/hline2.chk b/lib/libcurses/check_files/hline2.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/hline2.chk @@ -0,0 +1 @@ +cup4;76XAAAAAcup4;76X \ No newline at end of file diff --git a/lib/libcurses/check_files/hline_set.chk b/lib/libcurses/check_files/hline_set.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/hline_set.chk @@ -0,0 +1 @@ +cup1;75Xsmso㐁㐁㐁cup1;75Xrmsocup11;11Xsmso㐁㐁㐁㐁㐁㐁㐁㐁㐁㐁cup11;11Xrmso \ No newline at end of file diff --git a/lib/libcurses/check_files/immedok.chk b/lib/libcurses/check_files/immedok.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/immedok.chk @@ -0,0 +1,3 @@ +cup3;6Xhello! + cup11;6Xworld! + \ No newline at end of file diff --git a/lib/libcurses/check_files/ins_nwstr1.chk b/lib/libcurses/check_files/ins_nwstr1.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/ins_nwstr1.chk @@ -0,0 +1 @@ +cup5;5XAAA㐁 \ No newline at end of file diff --git a/lib/libcurses/check_files/ins_wch1.chk b/lib/libcurses/check_files/ins_wch1.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/ins_wch1.chk @@ -0,0 +1 @@ +smulArmulsmso㐁rmsohome \ No newline at end of file diff --git a/lib/libcurses/check_files/ins_wch2.chk b/lib/libcurses/check_files/ins_wch2.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/ins_wch2.chk @@ -0,0 +1 @@ +cup11;76XAAAAcup11;11Xsmso㐁rmsosmso㐁rmsocup11;76X Acup11;11X \ No newline at end of file diff --git a/lib/libcurses/check_files/ins_wch3.chk b/lib/libcurses/check_files/ins_wch3.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/ins_wch3.chk @@ -0,0 +1 @@ +cup11;80Xelcup11;76XsmulArmul \ No newline at end of file diff --git a/lib/libcurses/check_files/ins_wstr1.chk b/lib/libcurses/check_files/ins_wstr1.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/ins_wstr1.chk @@ -0,0 +1 @@ +AAA㐁AAhome \ No newline at end of file diff --git a/lib/libcurses/check_files/ins_wstr2.chk b/lib/libcurses/check_files/ins_wstr2.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/ins_wstr2.chk @@ -0,0 +1 @@ +cup11;76XAAAcup11;11XAAAAcup11;76X Acup11;11X \ No newline at end of file diff --git a/lib/libcurses/check_files/insch.chk b/lib/libcurses/check_files/insch.chk new file mode 100644 diff --git a/lib/libcurses/check_files/insch1.chk b/lib/libcurses/check_files/insch1.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/insch1.chk @@ -0,0 +1 @@ +smsoEhomermso \ No newline at end of file diff --git a/lib/libcurses/check_files/insch2.chk b/lib/libcurses/check_files/insch2.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/insch2.chk @@ -0,0 +1 @@ +cup2;78Xaaacup3;1Xcup2;11Xxcup2;78X cup2;11X \ No newline at end of file diff --git a/lib/libcurses/check_files/insdelln1.chk b/lib/libcurses/check_files/insdelln1.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/insdelln1.chk @@ -0,0 +1 @@ +csr16;24Xhomehomecup16;1Xrin2Xcsr1;24Xhomehomecup24;1Xcup16;1X \ No newline at end of file diff --git a/lib/libcurses/check_files/insdelln3.chk b/lib/libcurses/check_files/insdelln3.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/insdelln3.chk @@ -0,0 +1,3 @@ +cup13;1Xel +el +22222222222222222222222222222222222222222222222222222222222222222222222222222222cup16;1X33333333333333333333333333333333333333333333333333333333333333333333333333333333cup17;1X44444444444444444444444444444444444444444444444444444444444444444444444444444444cup13;1X \ No newline at end of file diff --git a/lib/libcurses/check_files/insdelln4.chk b/lib/libcurses/check_files/insdelln4.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/insdelln4.chk @@ -0,0 +1,3 @@ +el + el + 444444cup6;6X \ No newline at end of file diff --git a/lib/libcurses/check_files/insdelln5.chk b/lib/libcurses/check_files/insdelln5.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/insdelln5.chk @@ -0,0 +1,3 @@ +el + el + elcup8;6Xel \ No newline at end of file diff --git a/lib/libcurses/check_files/insdelln6.chk b/lib/libcurses/check_files/insdelln6.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/insdelln6.chk @@ -0,0 +1,2 @@ +el + el \ No newline at end of file diff --git a/lib/libcurses/check_files/insdelln7.chk b/lib/libcurses/check_files/insdelln7.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/insdelln7.chk @@ -0,0 +1 @@ +clearcup3;6Xhome \ No newline at end of file diff --git a/lib/libcurses/check_files/insdelln_1.chk b/lib/libcurses/check_files/insdelln_1.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/insdelln_1.chk @@ -0,0 +1 @@ +csr16;24Xhomehomecup24;1Xindn2Xcsr1;24Xhomehomecup24;1Xcup16;1X \ No newline at end of file diff --git a/lib/libcurses/check_files/insdelln_3.chk b/lib/libcurses/check_files/insdelln_3.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/insdelln_3.chk @@ -0,0 +1,2 @@ +cup13;1X44444444444444444444444444444444444444444444444444444444444444444444444444444444cup14;1X55555555555555555555555555555555555555555555555555555555555555555555555555555555cup15;1X66666666666666666666666666666666666666666666666666666666666666666666666666666666cup16;1Xel +el \ No newline at end of file diff --git a/lib/libcurses/check_files/insdelln_4.chk b/lib/libcurses/check_files/insdelln_4.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/insdelln_4.chk @@ -0,0 +1,3 @@ +666666 + el + el \ No newline at end of file diff --git a/lib/libcurses/check_files/insdelln_5.chk b/lib/libcurses/check_files/insdelln_5.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/insdelln_5.chk @@ -0,0 +1,4 @@ +el + el + el + el \ No newline at end of file diff --git a/lib/libcurses/check_files/insdelln_6.chk b/lib/libcurses/check_files/insdelln_6.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/insdelln_6.chk @@ -0,0 +1,2 @@ +el + el \ No newline at end of file diff --git a/lib/libcurses/check_files/insertln1.chk b/lib/libcurses/check_files/insertln1.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/insertln1.chk @@ -0,0 +1 @@ +csr16;24Xhomehomecup16;1Xrin1Xcsr1;24Xhomehomecup24;1Xcup16;1X \ No newline at end of file diff --git a/lib/libcurses/check_files/insertln3.chk b/lib/libcurses/check_files/insertln3.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/insertln3.chk @@ -0,0 +1,2 @@ +cup13;1Xel +22222222222222222222222222222222222222222222222222222222222222222222222222222222cup15;1X33333333333333333333333333333333333333333333333333333333333333333333333333333333cup16;1X44444444444444444444444444444444444444444444444444444444444444444444444444444444cup17;1X55555555555555555555555555555555555555555555555555555555555555555555555555555555cup13;1X \ No newline at end of file diff --git a/lib/libcurses/check_files/insertln4.chk b/lib/libcurses/check_files/insertln4.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/insertln4.chk @@ -0,0 +1,3 @@ +el + 444444 + 555555cup6;6X \ No newline at end of file diff --git a/lib/libcurses/check_files/insertln5.chk b/lib/libcurses/check_files/insertln5.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/insertln5.chk @@ -0,0 +1,4 @@ +el + el + elcup7;6Xel + el \ No newline at end of file diff --git a/lib/libcurses/check_files/insertln6.chk b/lib/libcurses/check_files/insertln6.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/insertln6.chk @@ -0,0 +1,2 @@ +el + 444444cup6;6X \ No newline at end of file diff --git a/lib/libcurses/check_files/leaveok.chk b/lib/libcurses/check_files/leaveok.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/leaveok.chk @@ -0,0 +1 @@ +abcd \ No newline at end of file diff --git a/lib/libcurses/check_files/meta1.chk b/lib/libcurses/check_files/meta1.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/meta1.chk @@ -0,0 +1 @@ +smm \ No newline at end of file diff --git a/lib/libcurses/check_files/meta2.chk b/lib/libcurses/check_files/meta2.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/meta2.chk @@ -0,0 +1 @@ +rmm \ No newline at end of file diff --git a/lib/libcurses/check_files/mutt_test1.chk b/lib/libcurses/check_files/mutt_test1.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/mutt_test1.chk @@ -0,0 +1 @@ +setaf7Xsetab0X \ No newline at end of file diff --git a/lib/libcurses/check_files/mutt_test2.chk b/lib/libcurses/check_files/mutt_test2.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/mutt_test2.chk @@ -0,0 +1 @@ +op \ No newline at end of file diff --git a/lib/libcurses/check_files/mutt_test3.chk b/lib/libcurses/check_files/mutt_test3.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/mutt_test3.chk @@ -0,0 +1 @@ +homesetaf7Xsetab0XboldEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEcup13;1XEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEcup23;1XEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEsgr0op \ No newline at end of file diff --git a/lib/libcurses/check_files/mutt_test4.chk b/lib/libcurses/check_files/mutt_test4.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/mutt_test4.chk @@ -0,0 +1,2 @@ + +setaf7Xsetab0Xrev123456789 sgr0opsetaf7Xsetab0Xiop \ No newline at end of file diff --git a/lib/libcurses/check_files/mutt_test5.chk b/lib/libcurses/check_files/mutt_test5.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/mutt_test5.chk @@ -0,0 +1,4 @@ +cup13;1Xsetaf7Xsetab0XEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE +boldEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEsgr0op setaf7Xsetab0XEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEj +boldEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEsgr0op setaf7Xsetab0XEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEj +boldEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEsgr0op \ No newline at end of file diff --git a/lib/libcurses/check_files/mutt_test6.chk b/lib/libcurses/check_files/mutt_test6.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/mutt_test6.chk @@ -0,0 +1 @@ +cup15;11Xsetaf7Xsetab0XsmsoRRRRRRRRRRRcup24;1Xrmsoopsetaf7Xsetab0Xjopcup15;15Xsetaf7Xsetab0XHHHopsetaf7Xsetab0Xjop \ No newline at end of file diff --git a/lib/libcurses/check_files/mutt_test7.chk b/lib/libcurses/check_files/mutt_test7.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/mutt_test7.chk @@ -0,0 +1 @@ +smkxop \ No newline at end of file diff --git a/lib/libcurses/check_files/mutt_test8.chk b/lib/libcurses/check_files/mutt_test8.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/mutt_test8.chk @@ -0,0 +1 @@ +smkxop setaf7Xsetab0XWWWWWWWWcup13;73XEEEEEEEEcup14;1XboldWWWWWWWWEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEcup15;1Xsgr0opsetaf7Xsetab0XWWWWWWWWEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEcup15;1XboldWWWWWWWWEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEcup16;1Xsgr0opsetaf7Xsetab0XWWWWWWWWEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEcup16;1XboldWWWWWWWWcup16;73XEEEEEEEEcup17;1Xsgr0op \ No newline at end of file diff --git a/lib/libcurses/check_files/mutt_test9.chk b/lib/libcurses/check_files/mutt_test9.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/mutt_test9.chk @@ -0,0 +1 @@ +cup15;11Xsetaf7Xsetab0XsmsoRRRRRRRRRRRcup24;1Xrmsoopopcup15;15Xsetaf7Xsetab0XHHHopop \ No newline at end of file diff --git a/lib/libcurses/check_files/mvaddch.chk b/lib/libcurses/check_files/mvaddch.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/mvaddch.chk @@ -0,0 +1 @@ +cup6;9XsmulErmul \ No newline at end of file diff --git a/lib/libcurses/check_files/mvaddchnstr.chk b/lib/libcurses/check_files/mvaddchnstr.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/mvaddchnstr.chk @@ -0,0 +1 @@ +cup7;8XsmulABCDEcup7;8Xrmul diff --git a/lib/libcurses/check_files/mvaddchnstr2.chk b/lib/libcurses/check_files/mvaddchnstr2.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/mvaddchnstr2.chk @@ -0,0 +1 @@ +cup9;76Xrev12345cup9;76Xsgr0 diff --git a/lib/libcurses/check_files/mvaddchstr.chk b/lib/libcurses/check_files/mvaddchstr.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/mvaddchstr.chk @@ -0,0 +1 @@ +cup9;76Xrev12345cup9;76Xsgr0 diff --git a/lib/libcurses/check_files/mvaddnstr.chk b/lib/libcurses/check_files/mvaddnstr.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/mvaddnstr.chk @@ -0,0 +1 @@ +cup8;11XABCDE \ No newline at end of file diff --git a/lib/libcurses/check_files/mvaddnstr2.chk b/lib/libcurses/check_files/mvaddnstr2.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/mvaddnstr2.chk @@ -0,0 +1 @@ +cup10;76X12345cup11;1X67890 diff --git a/lib/libcurses/check_files/mvaddnwstr1.chk b/lib/libcurses/check_files/mvaddnwstr1.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/mvaddnwstr1.chk @@ -0,0 +1 @@ +cup11;13XAB㐁C \ No newline at end of file diff --git a/lib/libcurses/check_files/mvaddstr.chk b/lib/libcurses/check_files/mvaddstr.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/mvaddstr.chk @@ -0,0 +1 @@ +cup9;13XDEADBEEF \ No newline at end of file diff --git a/lib/libcurses/check_files/mvaddstr2.chk b/lib/libcurses/check_files/mvaddstr2.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/mvaddstr2.chk @@ -0,0 +1 @@ +cup13;76XBADCOcup14;1XFFEE diff --git a/lib/libcurses/check_files/mvaddwstr1.chk b/lib/libcurses/check_files/mvaddwstr1.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/mvaddwstr1.chk @@ -0,0 +1 @@ +cup11;13XAB㐁CDE \ No newline at end of file diff --git a/lib/libcurses/check_files/mvchgat.chk b/lib/libcurses/check_files/mvchgat.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/mvchgat.chk @@ -0,0 +1 @@ +cup6;7Xsetaf1Xsetab4Xrev R cup6;10Xsgr0op \ No newline at end of file diff --git a/lib/libcurses/check_files/mvchgat2.chk b/lib/libcurses/check_files/mvchgat2.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/mvchgat2.chk @@ -0,0 +1 @@ +cup9;76Xsetaf1Xsetab4Xsmul T cup9;79Xrmulop \ No newline at end of file diff --git a/lib/libcurses/check_files/mvcur.chk b/lib/libcurses/check_files/mvcur.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/mvcur.chk @@ -0,0 +1 @@ +cup7;6Xcup11;13X \ No newline at end of file diff --git a/lib/libcurses/check_files/mvderwin1.chk b/lib/libcurses/check_files/mvderwin1.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/mvderwin1.chk @@ -0,0 +1 @@ +cup3;6XAAAAcup5;21X \ No newline at end of file diff --git a/lib/libcurses/check_files/mvderwin2.chk b/lib/libcurses/check_files/mvderwin2.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/mvderwin2.chk @@ -0,0 +1 @@ +cup3;10Xcup5;21XAAAAcup5;21X \ No newline at end of file diff --git a/lib/libcurses/check_files/mvget_wch.chk b/lib/libcurses/check_files/mvget_wch.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/mvget_wch.chk @@ -0,0 +1 @@ +cup11;11Xxxxxxxxxxxcup12;11Xyyyyyyyyyycup11;11XAcup12;12Xaysmkxxxxxx cup11;14X \ No newline at end of file diff --git a/lib/libcurses/check_files/mvgetnstr1.chk b/lib/libcurses/check_files/mvgetnstr1.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/mvgetnstr1.chk @@ -0,0 +1,4 @@ + + +testing +esting \ No newline at end of file diff --git a/lib/libcurses/check_files/mvgetnstr2.chk b/lib/libcurses/check_files/mvgetnstr2.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/mvgetnstr2.chk @@ -0,0 +1,2 @@ + + 1234567 diff --git a/lib/libcurses/check_files/mvgetnstr3.chk b/lib/libcurses/check_files/mvgetnstr3.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/mvgetnstr3.chk @@ -0,0 +1,2 @@ + + abc  def^[ODgh diff --git a/lib/libcurses/check_files/mvgetnstr4.chk b/lib/libcurses/check_files/mvgetnstr4.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/mvgetnstr4.chk @@ -0,0 +1 @@ +smkxcup3;3Xabc  def^[ODgh diff --git a/lib/libcurses/check_files/mvgetstr1.chk b/lib/libcurses/check_files/mvgetstr1.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/mvgetstr1.chk @@ -0,0 +1,5 @@ + + + +testing +esting \ No newline at end of file diff --git a/lib/libcurses/check_files/mvgetstr2.chk b/lib/libcurses/check_files/mvgetstr2.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/mvgetstr2.chk @@ -0,0 +1 @@ +cup2;5Xabc  def^[ODgh diff --git a/lib/libcurses/check_files/mvgetstr3.chk b/lib/libcurses/check_files/mvgetstr3.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/mvgetstr3.chk @@ -0,0 +1,2 @@ +smkx + abc  def^[ODgh diff --git a/lib/libcurses/check_files/mvins_wch.chk b/lib/libcurses/check_files/mvins_wch.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/mvins_wch.chk @@ -0,0 +1 @@ +cup3;6XsmulArmulsmso㐁rmso \ No newline at end of file diff --git a/lib/libcurses/check_files/mvprintw.chk b/lib/libcurses/check_files/mvprintw.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/mvprintw.chk @@ -0,0 +1 @@ +cup11;15Xtesting 1 2 3 \ No newline at end of file diff --git a/lib/libcurses/check_files/mvvline1.chk b/lib/libcurses/check_files/mvvline1.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/mvvline1.chk @@ -0,0 +1,15 @@ +cup4;6XB + B + B + B + B + B + B + B + B + B + B + B + B + B + Bcup4;6X \ No newline at end of file diff --git a/lib/libcurses/check_files/mvvline2.chk b/lib/libcurses/check_files/mvvline2.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/mvvline2.chk @@ -0,0 +1 @@ +cup11;13XrevCcup12;13XCcup13;13XCcup14;13XCcup15;13XCcup16;13XCcup17;13XCcup18;13XCcup19;13XCcup20;13XCcup21;13XCcup22;13XCcup11;13Xsgr0 \ No newline at end of file diff --git a/lib/libcurses/check_files/mvvline3.chk b/lib/libcurses/check_files/mvvline3.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/mvvline3.chk @@ -0,0 +1 @@ +cup19;9XsmulrevDcup20;9XDcup21;9XDcup22;9XDcup23;9XDcup24;9XDcup19;9Xrmulsgr0 \ No newline at end of file diff --git a/lib/libcurses/check_files/mvwaddnwstr1.chk b/lib/libcurses/check_files/mvwaddnwstr1.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/mvwaddnwstr1.chk @@ -0,0 +1 @@ +cup8;7XAB㐁cup3;10X \ No newline at end of file diff --git a/lib/libcurses/check_files/mvwaddstr.chk b/lib/libcurses/check_files/mvwaddstr.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/mvwaddstr.chk @@ -0,0 +1 @@ +cup5;7Xabcd \ No newline at end of file diff --git a/lib/libcurses/check_files/mvwaddwstr1.chk b/lib/libcurses/check_files/mvwaddwstr1.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/mvwaddwstr1.chk @@ -0,0 +1,2 @@ +cup5;7XAB㐁C + \ No newline at end of file diff --git a/lib/libcurses/check_files/mvwaddwstr2.chk b/lib/libcurses/check_files/mvwaddwstr2.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/mvwaddwstr2.chk @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/lib/libcurses/check_files/mvwaddwstr3.chk b/lib/libcurses/check_files/mvwaddwstr3.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/mvwaddwstr3.chk @@ -0,0 +1,2 @@ +cup6;8XAB㐁 + C \ No newline at end of file diff --git a/lib/libcurses/check_files/mvwchgat1.chk b/lib/libcurses/check_files/mvwchgat1.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/mvwchgat1.chk @@ -0,0 +1 @@ +cup8;7Xsetaf1Xsetab4Xsmul R cup8;9Xrmulop \ No newline at end of file diff --git a/lib/libcurses/check_files/mvwchgat2.chk b/lib/libcurses/check_files/mvwchgat2.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/mvwchgat2.chk @@ -0,0 +1 @@ +setaf1Xsetab4XrevT cup5;11Xsgr0op \ No newline at end of file diff --git a/lib/libcurses/check_files/mvwins_wch.chk b/lib/libcurses/check_files/mvwins_wch.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/mvwins_wch.chk @@ -0,0 +1 @@ +cup5;9XsmulArmulsmso㐁cup5;9Xrmso \ No newline at end of file diff --git a/lib/libcurses/check_files/notimeout.chk b/lib/libcurses/check_files/notimeout.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/notimeout.chk @@ -0,0 +1 @@ +smkx \ No newline at end of file diff --git a/lib/libcurses/check_files/overlay1.chk b/lib/libcurses/check_files/overlay1.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/overlay1.chk @@ -0,0 +1 @@ +cup6;9XA Acup7;11XAAAcup3;6X \ No newline at end of file diff --git a/lib/libcurses/check_files/overlay2.chk b/lib/libcurses/check_files/overlay2.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/overlay2.chk @@ -0,0 +1,2 @@ +444A4Acup7;11XA + \ No newline at end of file diff --git a/lib/libcurses/check_files/overwrite1.chk b/lib/libcurses/check_files/overwrite1.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/overwrite1.chk @@ -0,0 +1,6 @@ +AAAAAA + BBBBBB + CCCCCC + DDDDDD + EEEEEE + FFFFF \ No newline at end of file diff --git a/lib/libcurses/check_files/overwrite2.chk b/lib/libcurses/check_files/overwrite2.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/overwrite2.chk @@ -0,0 +1 @@ +cup7;8XAAAAAAcup8;8XBBBBBBcup9;8XCCCCCCcup10;8X \ No newline at end of file diff --git a/lib/libcurses/check_files/overwrite3.chk b/lib/libcurses/check_files/overwrite3.chk new file mode 100644 diff --git a/lib/libcurses/check_files/overwrite4.chk b/lib/libcurses/check_files/overwrite4.chk new file mode 100644 diff --git a/lib/libcurses/check_files/pad1.chk b/lib/libcurses/check_files/pad1.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/pad1.chk @@ -0,0 +1,3 @@ +cup12;6Xtestingtest + testingtest + testingtest diff --git a/lib/libcurses/check_files/pad2.chk b/lib/libcurses/check_files/pad2.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/pad2.chk @@ -0,0 +1 @@ +cup12;11X223333cup13;11X223333cup14;11X223333 \ No newline at end of file diff --git a/lib/libcurses/check_files/pad3.chk b/lib/libcurses/check_files/pad3.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/pad3.chk @@ -0,0 +1,6 @@ +cup12;7Xel + tel + t 444444444cup15;9X444444 + testi22333333 + testi22333333 + testi22333333 \ No newline at end of file diff --git a/lib/libcurses/check_files/pechochar1.chk b/lib/libcurses/check_files/pechochar1.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/pechochar1.chk @@ -0,0 +1,2 @@ +smulA +rmulsmsoBhomermso \ No newline at end of file diff --git a/lib/libcurses/check_files/pechochar2.chk b/lib/libcurses/check_files/pechochar2.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/pechochar2.chk @@ -0,0 +1 @@ +rev㐁cup3;2X㐁sgr0 \ No newline at end of file diff --git a/lib/libcurses/check_files/redrawwin1.chk b/lib/libcurses/check_files/redrawwin1.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/redrawwin1.chk @@ -0,0 +1 @@ +foocup3;11Xbar \ No newline at end of file diff --git a/lib/libcurses/check_files/redrawwin2.chk b/lib/libcurses/check_files/redrawwin2.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/redrawwin2.chk @@ -0,0 +1 @@ +homefoo cup2;1X cup3;1X bar cup4;1X cup5;1X cup6;1X cup7;1X cup8;1X cup9;1X cup10;1X cup11;1X cup12;1X cup13;1X cup14;1X cup15;1X cup16;1X cup17;1X cup18;1X cup19;1X cup20;1X cup21;1X cup22;1X cup23;1X cup24;1Xelcup3;14Xhome \ No newline at end of file diff --git a/lib/libcurses/check_files/redrawwin3.chk b/lib/libcurses/check_files/redrawwin3.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/redrawwin3.chk @@ -0,0 +1,6 @@ +test b + el + el + el + el + el \ No newline at end of file diff --git a/lib/libcurses/check_files/scroll1.chk b/lib/libcurses/check_files/scroll1.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/scroll1.chk @@ -0,0 +1 @@ +csr1;24Xhomehomecup24;1Xindn1Xcsr1;24Xhomehomecup24;1Xcup6;6X \ No newline at end of file diff --git a/lib/libcurses/check_files/scroll2.chk b/lib/libcurses/check_files/scroll2.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/scroll2.chk @@ -0,0 +1,6 @@ +222222 + 333333 + 444444 + 555555 + 666666 + el555 \ No newline at end of file diff --git a/lib/libcurses/check_files/setscrreg.chk b/lib/libcurses/check_files/setscrreg.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/setscrreg.chk @@ -0,0 +1,2 @@ +cup6;1X77777777777777777777777777777777777777777777777777777777777777777777777777777777cup7;1X88888888888888888888888888888888888888888888888888888888888888888888888888888888cup8;1Xel +elcup24;1X \ No newline at end of file diff --git a/lib/libcurses/check_files/slk1.chk b/lib/libcurses/check_files/slk1.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/slk1.chk @@ -0,0 +1 @@ +cup24;1Xrevone sgr0 smsosmulrev one rmsormulsgr0 smsorev onermsosgr0cup24;42Xsmulrev five cup24;73Xeight!!rmam!smamcup24;50Xrmulsgr0 \ No newline at end of file diff --git a/lib/libcurses/check_files/slk2.chk b/lib/libcurses/check_files/slk2.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/slk2.chk @@ -0,0 +1 @@ + el \ No newline at end of file diff --git a/lib/libcurses/check_files/slk3.chk b/lib/libcurses/check_files/slk3.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/slk3.chk @@ -0,0 +1 @@ +smulrev one    five   eight!!rmam!smamrmulsgr0 \ No newline at end of file diff --git a/lib/libcurses/check_files/slk4.chk b/lib/libcurses/check_files/slk4.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/slk4.chk @@ -0,0 +1 @@ +cup24;10Xsmulrevtwohomermulsgr0 \ No newline at end of file diff --git a/lib/libcurses/check_files/slk5.chk b/lib/libcurses/check_files/slk5.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/slk5.chk @@ -0,0 +1 @@ +cup24;1Xsmulrev onesgr0 smulrev two sgr0 smulrev sgr0 smulrev sgr0 smulrev five sgr0 smulrev sgr0 smulrev sgr0 smulreveight!!rmam!smamcup24;18Xrmulsgr0 \ No newline at end of file diff --git a/lib/libcurses/check_files/slk6.chk b/lib/libcurses/check_files/slk6.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/slk6.chk @@ -0,0 +1 @@ +op setaf7Xsetab0Xsmulrev onesgr0setaf7Xsetab0X smulrev two sgr0setaf7Xsetab0X smulrev sgr0setaf7Xsetab0X setaf1Xsetab2Xsmulrev foursgr0setaf7Xsetab0X smulrev five sgr0setaf7Xsetab0X smulrev sgr0setaf7Xsetab0X smulrev sgr0setaf7Xsetab0X smulreveight!!rmam!smamcup24;40Xrmulsgr0op \ No newline at end of file diff --git a/lib/libcurses/check_files/slk_init.chk b/lib/libcurses/check_files/slk_init.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/slk_init.chk @@ -0,0 +1 @@ +enacsenacssmcupcnormclearcup24;1Xrev sgr0 rev sgr0 rev sgr0 rev sgr0 rev sgr0 rev sgr0 rev sgr0 rev rmam smamcup1;1Xsgr0 \ No newline at end of file diff --git a/lib/libcurses/check_files/touchline1.chk b/lib/libcurses/check_files/touchline1.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/touchline1.chk @@ -0,0 +1,4 @@ +2999cup5;7X999 + 499944 + 555555 + 666666 \ No newline at end of file diff --git a/lib/libcurses/check_files/touchline2.chk b/lib/libcurses/check_files/touchline2.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/touchline2.chk @@ -0,0 +1 @@ +cup16;16Xaaaaaacup17;16Xbbbbbbcup18;16Xcccccccup19;16Xdddddd  bbbbbb cup18;1X cccccc cup19;22X \ No newline at end of file diff --git a/lib/libcurses/check_files/touchoverlap1.chk b/lib/libcurses/check_files/touchoverlap1.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/touchoverlap1.chk @@ -0,0 +1 @@ + 444cup7;9X555cup8;9X666 \ No newline at end of file diff --git a/lib/libcurses/check_files/touchoverlap2.chk b/lib/libcurses/check_files/touchoverlap2.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/touchoverlap2.chk @@ -0,0 +1 @@ + 22cup5;7X33cup8;6X \ No newline at end of file diff --git a/lib/libcurses/check_files/touchwin.chk b/lib/libcurses/check_files/touchwin.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/touchwin.chk @@ -0,0 +1,2 @@ +444windowcup7;9Xcup6;6Xxxxxxx + xxxwindowcup7;9Xyyyyyycup8;9Xelcup9;9Xelcup10;9Xelcup11;9Xel \ No newline at end of file diff --git a/lib/libcurses/check_files/two_window.chk b/lib/libcurses/check_files/two_window.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/two_window.chk @@ -0,0 +1 @@ +cup6;9Xcup3;6X \ No newline at end of file diff --git a/lib/libcurses/check_files/untouchwin.chk b/lib/libcurses/check_files/untouchwin.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/untouchwin.chk @@ -0,0 +1,6 @@ +cup8;6X111111 + 222222 + 333333 + 444444 + 555555 + 666666 \ No newline at end of file diff --git a/lib/libcurses/check_files/vline_set.chk b/lib/libcurses/check_files/vline_set.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/vline_set.chk @@ -0,0 +1,4 @@ +cup21;1Xsmso㐁 +㐁 +㐁 +㐁 rmsocup11;11Xsmso㐁cup12;11X㐁cup13;11X㐁cup14;11X㐁cup15;11X㐁cup16;11X㐁cup17;11X㐁cup18;11X㐁cup19;11X㐁cup20;11X㐁cup11;11Xrmso \ No newline at end of file diff --git a/lib/libcurses/check_files/wadd_wch1.chk b/lib/libcurses/check_files/wadd_wch1.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/wadd_wch1.chk @@ -0,0 +1 @@ +HHH \ No newline at end of file diff --git a/lib/libcurses/check_files/wadd_wch2.chk b/lib/libcurses/check_files/wadd_wch2.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/wadd_wch2.chk @@ -0,0 +1,3 @@ + + smso㐁rmso + 㐁㐁 \ No newline at end of file diff --git a/lib/libcurses/check_files/waddch.chk b/lib/libcurses/check_files/waddch.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/waddch.chk @@ -0,0 +1 @@ +smsoarmsocup5;9Xsmsobrmso \ No newline at end of file diff --git a/lib/libcurses/check_files/waddchnstr.chk b/lib/libcurses/check_files/waddchnstr.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/waddchnstr.chk @@ -0,0 +1,2 @@ + + revabc sgr0cup5;8Xrevabccup5;8Xsgr0cup6;9Xrevabccup6;9Xsgr0 \ No newline at end of file diff --git a/lib/libcurses/check_files/waddchstr.chk b/lib/libcurses/check_files/waddchstr.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/waddchstr.chk @@ -0,0 +1 @@ +revabc sgr0cup4;7Xsmulrevdefghcup4;7Xrmulsgr0 \ No newline at end of file diff --git a/lib/libcurses/check_files/waddnwstr1.chk b/lib/libcurses/check_files/waddnwstr1.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/waddnwstr1.chk @@ -0,0 +1 @@ +AB㐁 \ No newline at end of file diff --git a/lib/libcurses/check_files/waddnwstr2.chk b/lib/libcurses/check_files/waddnwstr2.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/waddnwstr2.chk @@ -0,0 +1 @@ + b \ No newline at end of file diff --git a/lib/libcurses/check_files/waddstr.chk b/lib/libcurses/check_files/waddstr.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/waddstr.chk @@ -0,0 +1 @@ +home \ No newline at end of file diff --git a/lib/libcurses/check_files/waddwstr1.chk b/lib/libcurses/check_files/waddwstr1.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/waddwstr1.chk @@ -0,0 +1 @@ +AB㐁C \ No newline at end of file diff --git a/lib/libcurses/check_files/waddwstr2.chk b/lib/libcurses/check_files/waddwstr2.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/waddwstr2.chk @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/lib/libcurses/check_files/waddwstr3.chk b/lib/libcurses/check_files/waddwstr3.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/waddwstr3.chk @@ -0,0 +1,2 @@ +cup5;9XAB + 㐁C \ No newline at end of file diff --git a/lib/libcurses/check_files/wborder_set1.chk b/lib/libcurses/check_files/wborder_set1.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/wborder_set1.chk @@ -0,0 +1,6 @@ +EsmsoCCCCrmsoF + A B + A B + A B + A B + smsoGrmsoDDDDHcup3;6X \ No newline at end of file diff --git a/lib/libcurses/check_files/wborder_set2.chk b/lib/libcurses/check_files/wborder_set2.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/wborder_set2.chk @@ -0,0 +1,6 @@ +smacslqqqqkrmacs + smacsxrmacs smacsxrmacs + smacsxrmacs smacsxrmacs + smacsxrmacs smacsxrmacs + smacsxrmacs smacsxrmacs + smacsmqqqqjrmacscup3;6X \ No newline at end of file diff --git a/lib/libcurses/check_files/wchgat1.chk b/lib/libcurses/check_files/wchgat1.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/wchgat1.chk @@ -0,0 +1 @@ +setaf7Xsetab0Xdsetaf3Xsetab6Xsmul rmulsetaf7Xsetab0X cup3;7Xop \ No newline at end of file diff --git a/lib/libcurses/check_files/wchgat2.chk b/lib/libcurses/check_files/wchgat2.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/wchgat2.chk @@ -0,0 +1 @@ +setaf7Xsetab0Xeop \ No newline at end of file diff --git a/lib/libcurses/check_files/wchgat3.chk b/lib/libcurses/check_files/wchgat3.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/wchgat3.chk @@ -0,0 +1 @@ + setaf3Xsetab6Xrevde sgr0op \ No newline at end of file diff --git a/lib/libcurses/check_files/wcolor_set.chk b/lib/libcurses/check_files/wcolor_set.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/wcolor_set.chk @@ -0,0 +1 @@ +cup3;6Xsetaf1Xsetab2Xtestop \ No newline at end of file diff --git a/lib/libcurses/check_files/wget_wstr.chk b/lib/libcurses/check_files/wget_wstr.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/wget_wstr.chk @@ -0,0 +1,3 @@ +cup5;6X 㐁 cup5;11X + a cup6;7X ab cup6;8X elc cup6;7X cd cup6;8X cde cup6;9X cd cup6;8X cdf cup6;9X + a cup7;7X ab cup7;8X a cup7;7X ac cup7;8X ac cup7;9X acO cup7;10X acOD cup7;11Xsmkxcup8;7X a cup8;8X ab cup8;9X abc cup8;10X el d cup8;8X \ No newline at end of file diff --git a/lib/libcurses/check_files/wgetch.chk b/lib/libcurses/check_files/wgetch.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/wgetch.chk @@ -0,0 +1 @@ +icup5;9Xj \ No newline at end of file diff --git a/lib/libcurses/check_files/wgetn_wstr.chk b/lib/libcurses/check_files/wgetn_wstr.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/wgetn_wstr.chk @@ -0,0 +1,3 @@ +cup5;6X 㐁 cup5;11X + a cup6;7X ab cup6;8X elc cup6;7X cd cup6;8X cde cup6;9X cd cup6;8X cdf cup6;9X cdf cup6;9X cdf cup6;9X cdf cup6;9X + a cup7;7X ab cup7;8X a cup7;7X ac cup7;8X ac cup7;9X acO cup7;10X acOD cup7;11Xsmkxcup8;7X a cup8;8X ab cup8;9X abc cup8;10X el d cup8;8X \ No newline at end of file diff --git a/lib/libcurses/check_files/whline1.chk b/lib/libcurses/check_files/whline1.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/whline1.chk @@ -0,0 +1 @@ + revBBBcup4;8Xsgr0AAAAcup3;7X \ No newline at end of file diff --git a/lib/libcurses/check_files/whline2.chk b/lib/libcurses/check_files/whline2.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/whline2.chk @@ -0,0 +1 @@ +cup7;8XAAAAcup7;8Xcup6;7XrevBBBcup6;7Xsgr0 \ No newline at end of file diff --git a/lib/libcurses/check_files/whline_set.chk b/lib/libcurses/check_files/whline_set.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/whline_set.chk @@ -0,0 +1 @@ + smso㐁㐁cup3;8Xrmsocup4;7Xsmso㐁㐁cup4;7Xrmso \ No newline at end of file diff --git a/lib/libcurses/check_files/window2.chk b/lib/libcurses/check_files/window2.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/window2.chk @@ -0,0 +1 @@ +cup11;6X \ No newline at end of file diff --git a/lib/libcurses/check_files/window_hierarchy.chk b/lib/libcurses/check_files/window_hierarchy.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/window_hierarchy.chk @@ -0,0 +1 @@ +cup6;9Xcup5;8Xcup4;7Xsmulxxxxxcup5;7Xxxxxxcup6;7Xxxxxxcup7;7Xxxxxxcup8;7Xxxxxxcup4;7Xrmulcup3;6X \ No newline at end of file diff --git a/lib/libcurses/check_files/wins_wch1.chk b/lib/libcurses/check_files/wins_wch1.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/wins_wch1.chk @@ -0,0 +1 @@ +smulArmulsmso㐁rmso \ No newline at end of file diff --git a/lib/libcurses/check_files/wins_wch2.chk b/lib/libcurses/check_files/wins_wch2.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/wins_wch2.chk @@ -0,0 +1 @@ +cup5;9XAAcup5;7Xsmso㐁rmso Acup5;7X \ No newline at end of file diff --git a/lib/libcurses/check_files/wins_wch3.chk b/lib/libcurses/check_files/wins_wch3.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/wins_wch3.chk @@ -0,0 +1 @@ + elcup5;10XsmulArmul \ No newline at end of file diff --git a/lib/libcurses/check_files/wins_wstr1.chk b/lib/libcurses/check_files/wins_wstr1.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/wins_wstr1.chk @@ -0,0 +1 @@ +cup5;7XA㐁Acup5;7X \ No newline at end of file diff --git a/lib/libcurses/check_files/wins_wstr2.chk b/lib/libcurses/check_files/wins_wstr2.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/wins_wstr2.chk @@ -0,0 +1,2 @@ +cup7;10XBB +  AA elcup7;7X \ No newline at end of file diff --git a/lib/libcurses/check_files/winsch1.chk b/lib/libcurses/check_files/winsch1.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/winsch1.chk @@ -0,0 +1 @@ +smsoE rmso \ No newline at end of file diff --git a/lib/libcurses/check_files/winsch2.chk b/lib/libcurses/check_files/winsch2.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/winsch2.chk @@ -0,0 +1,2 @@ +cup4;9Xaaa +  x cup4;7X \ No newline at end of file diff --git a/lib/libcurses/check_files/wprintw_refresh.chk b/lib/libcurses/check_files/wprintw_refresh.chk --- a/lib/libcurses/check_files/wprintw_refresh.chk +++ b/lib/libcurses/check_files/wprintw_refresh.chk @@ -1 +1,2 @@ -cup3;6Xhellocup3;10X \ No newline at end of file +cup3;6Xhello + \ No newline at end of file diff --git a/lib/libcurses/check_files/wredrawln1.chk b/lib/libcurses/check_files/wredrawln1.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/wredrawln1.chk @@ -0,0 +1,3 @@ +cup6;6X444444 + 555555 + \ No newline at end of file diff --git a/lib/libcurses/check_files/wredrawln2.chk b/lib/libcurses/check_files/wredrawln2.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/wredrawln2.chk @@ -0,0 +1,4 @@ +el + elcup6;6X444444 + 555555 + 666666 \ No newline at end of file diff --git a/lib/libcurses/check_files/wsetscrreg.chk b/lib/libcurses/check_files/wsetscrreg.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/wsetscrreg.chk @@ -0,0 +1,2 @@ +el + 333333cup8;6X \ No newline at end of file diff --git a/lib/libcurses/check_files/wtouchln.chk b/lib/libcurses/check_files/wtouchln.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/wtouchln.chk @@ -0,0 +1,2 @@ + + 222222cup6;6X444444cup3;6X \ No newline at end of file diff --git a/lib/libcurses/check_files/wvline1.chk b/lib/libcurses/check_files/wvline1.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/wvline1.chk @@ -0,0 +1 @@ + smsoAcup4;7XArmsoBcup5;7XsmsoArmsoBcup6;8XBcup7;8XBcup8;8XBcup3;7X \ No newline at end of file diff --git a/lib/libcurses/check_files/wvline2.chk b/lib/libcurses/check_files/wvline2.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/wvline2.chk @@ -0,0 +1 @@ +elcup4;7Xelcup5;7Xelcup6;8Xelcup7;8Xelcup8;8Xelcup3;6X \ No newline at end of file diff --git a/lib/libcurses/check_files/wvline_set.chk b/lib/libcurses/check_files/wvline_set.chk new file mode 100644 --- /dev/null +++ b/lib/libcurses/check_files/wvline_set.chk @@ -0,0 +1,6 @@ + + smso㐁 +㐁 +㐁 +㐁 +㐁cup4;6Xrmsocup5;8Xsmso㐁cup6;8X㐁cup7;8X㐁cup5;8Xrmsocup4;7Xsmsocup5;7Xcup6;7Xrmso \ No newline at end of file diff --git a/lib/libcurses/chk_gen b/lib/libcurses/chk_gen new file mode 100755 --- /dev/null +++ b/lib/libcurses/chk_gen @@ -0,0 +1,16 @@ +#!/bin/sh +# +# Simple script that will generate a check file from input pasted +# in, translate the octal \012 to \n and \015 to \r and then printf +# the resulting string. This allows output from the verbose cursts +# test to be translated into a check file simply. +# +OUT="" +while read -r line +do + next=`echo $line | sed -e 's/%/%%/g' -e 's/\n//' -e 's/\\015/\\r/g' -e 's/\\012/\\n/g'` + OUT="${OUT}${next}" +done +OUT=`echo "${OUT}" | sed 's/\n//'` + +printf "${OUT}" diff --git a/lib/libcurses/debug_test b/lib/libcurses/debug_test new file mode 100755 --- /dev/null +++ b/lib/libcurses/debug_test @@ -0,0 +1,94 @@ +#!/bin/sh +# +# +BASEDIR="/usr/tests/lib/libcurses" +CHECK_PATH="${BASEDIR}/check_files/" +export CHECK_PATH +INCLUDE_PATH="${BASEDIR}/tests/" +export INCLUDE_PATH +# +CURSES_TRACE_FILE="/tmp/ctrace" +SLAVE="${BASEDIR}/slave" + +usage() { + echo "Set up the environment to run the test frame. Option flags:" + echo + echo " -c : Set up curses tracing, assumes the curses lib has been built with" + echo " debug enabled. Default trace mask traces input, can be overridden" + echo " by setting the trace mask in the environment before calling the" + echo " The trace file output goes to /tmp/ctrace" + echo " script." + echo " -F : Specify the file name for curses tracing the default is" + echo " ${CURSES_TRACE_FILE}" + echo " -L : Add the argument as a prefix to LD_LIBRARY_PATH to" + echo " use an alternate libcurses version" + echo " -s : Specify the slave command. Defaults to \"../slave/slave\"" + echo " -v : Enable verbose output" + echo " -g : Enable check file generation if the file does not exists" + echo " -f : Forces check file generation if -g flag is set" + echo +} + +# This is needed for getwin/putwin test case and /tmp can be used for any file +# related tests. +#rm -rf /tmp/* + +# +#ARGS="-T ${BASEDIR} -I ${INCLUDE_PATH} -C ${CHECK_PATH}" +ARGS="-T ${BASEDIR} -C ${CHECK_PATH}" +# +while getopts cf:L:s:vg opt +do + case "${opt}" in + c) + if [ "X$CURSES_TRACE_MASK" = "X" ]; then + CURSES_TRACE_MASK=0x00000082 + fi + export CURSES_TRACE_FILE + export CURSES_TRACE_MASK + ;; + + F) + CURSES_TRACE_FILE=${OPTARG} + ;; + + L) + LD_LIBRARY_PATH=${OPTARG}:${LD_LIBRARY_PATH} + ;; + + s) + SLAVE=${OPTARG} + ;; + + v) + ARGS="-v ${ARGS}" + ;; + + g) + ARGS="-g ${ARGS}" + ;; + + f) + ARGS="-f ${ARGS}" + ;; + + \?) + usage + exit 1 + ;; + esac +done +# +shift $((OPTIND - 1)) +# +if [ -z "${1}" ] +then + echo + echo "A test name needs to be specified." + echo + usage + echo + exit 1 +fi +# +exec ${BASEDIR}/director ${ARGS} -s ${SLAVE} "${INCLUDE_PATH}/$1" diff --git a/lib/libcurses/director/director.h b/lib/libcurses/director/director.h new file mode 100644 --- /dev/null +++ b/lib/libcurses/director/director.h @@ -0,0 +1,41 @@ +/* $NetBSD: director.h,v 1.4 2021/02/13 08:14:46 rillig Exp $ */ + +/*- + * Copyright 2020 Naman Jain , this code was + * created as part of the Google Summer of Code 2020. + * Copyright 2021 Roland Illig + * + * All rights reserved. + * + * This code has been donated to The NetBSD Foundation by the Author. + * + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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. + */ + +#ifndef CTF_DIRECTOR_H +#define CTF_DIRECTOR_H + +#define GEN_CHECK_FILE 1 +#define FORCE_GEN 2 + +extern int to_slave; +extern int from_slave; + +#endif diff --git a/lib/libcurses/director/director.c b/lib/libcurses/director/director.c --- a/lib/libcurses/director/director.c +++ b/lib/libcurses/director/director.c @@ -1,7 +1,8 @@ -/* $NetBSD: director.c,v 1.10 2012/06/03 23:19:11 joerg Exp $ */ +/* $NetBSD: director.c,v 1.29 2021/06/10 07:21:07 mcf Exp $ */ /*- * Copyright 2009 Brett Lymn + * Copyright 2021 Roland Illig * * All rights reserved. * @@ -13,7 +14,7 @@ * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. The name of the author may not be used to endorse or promote products - * derived from this software withough specific prior written permission + * derived from this software without specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES @@ -25,13 +26,12 @@ * 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 #include #include +#include #include #include #include @@ -43,6 +43,7 @@ #include #include #include "returns.h" +#include "director.h" void yyparse(void); #define DEF_TERMPATH "." @@ -50,27 +51,29 @@ #define DEF_SLAVE "./slave" const char *def_check_path = "./"; /* default check path */ -const char *def_include_path = "./"; /* default include path */ -extern size_t nvars; /* In testlang_conf.y */ +extern size_t nvars; /* In testlang_conf.y */ saved_data_t saved_output; /* In testlang_conf.y */ -int cmdpipe[2]; /* command pipe between director and slave */ -int slvpipe[2]; /* reply pipe back from slave */ -int master; /* pty to the slave */ -int verbose; /* control verbosity of tests */ -const char *check_path; /* path to prepend to check files for output - validation */ -const char *include_path; /* path to prepend to include files */ -char *cur_file; /* name of file currently being read */ +int to_slave; +int from_slave; +int master; /* pty to the slave */ +int verbose; /* control verbosity of tests */ +int check_file_flag; /* control check-file generation */ +const char *check_path; /* path to prepend to check files for output + validation */ +char *cur_file; /* name of file currently being read */ -void init_parse_variables(int); /* in testlang_parse.y */ +void init_parse_variables(int); /* in testlang_parse.y */ /* * Handle the slave exiting unexpectedly, try to recover the exit message * and print it out. + * + * FIXME: Must not use stdio in a signal handler. This leads to incomplete + * output in verbose mode, truncating the useful part of the error message. */ static void -slave_died(int param) +slave_died(int signo) { char last_words[256]; size_t count; @@ -79,8 +82,11 @@ if (saved_output.count > 0) { fprintf(stderr, "output from slave: "); for (count = 0; count < saved_output.count; count ++) { - if (isprint((unsigned char)saved_output.data[count])) - fprintf(stderr, "%c", saved_output.data[count]); + unsigned char b = saved_output.data[count]; + if (isprint(b)) + fprintf(stderr, "%c", b); + else + fprintf(stderr, "\\x%02x", b); } fprintf(stderr, "\n"); } @@ -98,17 +104,18 @@ static void usage(void) { - fprintf(stderr, "Usage: %s [-v] [-I include-path] [-C check-path] " + fprintf(stderr, "Usage: %s [-vgf] [-I include-path] [-C check-path] " "[-T terminfo-file] [-s pathtoslave] [-t term] " "commandfile\n", getprogname()); fprintf(stderr, " where:\n"); fprintf(stderr, " -v enables verbose test output\n"); + fprintf(stderr, " -g generates check-files if they do not exist\n"); + fprintf(stderr, " -f overwrites check-files with the actual data\n"); fprintf(stderr, " -T is a directory containing the terminfo.cdb " - "file, or a file holding the terminfo description n"); + "file, or a file holding the terminfo description\n"); fprintf(stderr, " -s is the path to the slave executable\n"); fprintf(stderr, " -t is value to set TERM to for the test\n"); - fprintf(stderr, " -I is the directory to include files\n"); - fprintf(stderr, " -C is the directory for config files\n"); + fprintf(stderr, " -C is the directory for check-files\n"); fprintf(stderr, " commandfile is a file of test directives\n"); exit(1); } @@ -123,27 +130,23 @@ int ch; pid_t slave_pid; extern FILE *yyin; - char *arg1, *arg2, *arg3, *arg4; + char *arg1, *arg2; struct termios term_attr; struct stat st; + int pipe_to_slave[2], pipe_from_slave[2]; termpath = term = slave = NULL; verbose = 0; + check_file_flag = 0; - while ((ch = getopt(argc, argv, "vC:I:p:s:t:T:")) != -1) { - switch(ch) { - case 'I': - include_path = optarg; - break; + while ((ch = getopt(argc, argv, "vgfC:s:t:T:")) != -1) { + switch (ch) { case 'C': check_path = optarg; break; case 'T': termpath = optarg; break; - case 'p': - termpath = optarg; - break; case 's': slave = optarg; break; @@ -153,6 +156,12 @@ case 'v': verbose = 1; break; + case 'g': + check_file_flag |= GEN_CHECK_FILE; + break; + case 'f': + check_file_flag |= FORCE_GEN; + break; case '?': default: usage(); @@ -162,7 +171,7 @@ argc -= optind; argv += optind; - if (argc < 1) + if (argc != 1) usage(); if (termpath == NULL) @@ -177,23 +186,18 @@ if (check_path == NULL) check_path = getenv("CHECK_PATH"); if ((check_path == NULL) || (check_path[0] == '\0')) { - warn("$CHECK_PATH not set, defaulting to %s", def_check_path); + warnx("$CHECK_PATH not set, defaulting to %s", def_check_path); check_path = def_check_path; } - if (include_path == NULL) - include_path = getenv("INCLUDE_PATH"); - if ((include_path == NULL) || (include_path[0] == '\0')) { - warn("$INCLUDE_PATH not set, defaulting to %s", - def_include_path); - include_path = def_include_path; - } - signal(SIGCHLD, slave_died); if (setenv("TERM", term, 1) != 0) err(2, "Failed to set TERM variable"); + if (unsetenv("ESCDELAY") != 0) + err(2, "Failed to unset ESCDELAY variable"); + if (stat(termpath, &st) == -1) err(1, "Cannot stat %s", termpath); @@ -221,11 +225,13 @@ munmap(tinfo, (size_t)st.st_size); } - if (pipe(cmdpipe) < 0) + if (pipe(pipe_to_slave) < 0) err(1, "Command pipe creation failed"); + to_slave = pipe_to_slave[1]; - if (pipe(slvpipe) < 0) + if (pipe(pipe_from_slave) < 0) err(1, "Slave pipe creation failed"); + from_slave = pipe_from_slave[0]; /* * Create default termios settings for later use @@ -244,24 +250,23 @@ if (slave_pid == 0) { /* slave side, just exec the slave process */ - if (asprintf(&arg1, "%d", cmdpipe[0]) < 0) + if (asprintf(&arg1, "%d", pipe_to_slave[0]) < 0) err(1, "arg1 conversion failed"); + close(pipe_to_slave[1]); - if (asprintf(&arg2, "%d", cmdpipe[1]) < 0) + close(pipe_from_slave[0]); + if (asprintf(&arg2, "%d", pipe_from_slave[1]) < 0) err(1, "arg2 conversion failed"); - if (asprintf(&arg3, "%d", slvpipe[0]) < 0) - err(1, "arg3 conversion failed"); - - if (asprintf(&arg4, "%d", slvpipe[1]) < 0) - err(1, "arg4 conversion failed"); - - if (execl(slave, slave, arg1, arg2, arg3, arg4, NULL) < 0) + if (execl(slave, slave, arg1, arg2, (char *)0) < 0) err(1, "Exec of slave %s failed", slave); /* NOT REACHED */ } + (void)close(pipe_to_slave[0]); + (void)close(pipe_from_slave[1]); + fcntl(master, F_SETFL, O_NONBLOCK); if ((yyin = fopen(argv[0], "r")) == NULL) @@ -275,5 +280,12 @@ yyparse(); fclose(yyin); + signal(SIGCHLD, SIG_DFL); + (void)close(to_slave); + (void)close(from_slave); + + int status; + (void)waitpid(slave_pid, &status, 0); + exit(0); } diff --git a/lib/libcurses/director/returns.h b/lib/libcurses/director/returns.h --- a/lib/libcurses/director/returns.h +++ b/lib/libcurses/director/returns.h @@ -1,7 +1,8 @@ -/* $NetBSD: returns.h,v 1.1 2011/04/10 09:55:09 blymn Exp $ */ +/* $NetBSD: returns.h,v 1.6 2021/02/13 08:14:46 rillig Exp $ */ /*- * Copyright 2009 Brett Lymn + * Copyright 2021 Roland Illig * * All rights reserved. * @@ -13,7 +14,7 @@ * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. The name of the author may not be used to endorse or promote products - * derived from this software withough specific prior written permission + * derived from this software without specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES @@ -25,36 +26,37 @@ * 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. - * - * */ -#ifndef CTF_RETURNS_H -#define CTF_RETURNS_H 1 +#ifndef CTF_RETURNS_H +#define CTF_RETURNS_H typedef enum { - ret_number = 1, - ret_string, - ret_byte, - ret_err, - ret_ok, - ret_null, - ret_nonnull, - ret_var, - ret_ref, - ret_count, - ret_slave_error -} returns_enum_t; + data_number = 1, + data_static, + data_string, + data_byte, + data_cchar, + data_wchar, + data_err, + data_ok, + data_null, + data_nonnull, + data_var, + data_ref, + data_count, + data_slave_error +} data_enum_t; typedef struct { - returns_enum_t return_type; - void *return_value; /* used if return_type is ret_num or - or ret_byte or ret_string */ - size_t return_len; /* number of bytes in return_value iff - return_type is ret_byte */ - int return_index; /* index into var array for return - if return_type is ret_var */ -} returns_t; + data_enum_t data_type; + void *data_value; /* used if data_type is data_num + or data_byte or data_string */ + size_t data_len; /* number of bytes in return_value iff + return_type is data_byte */ + int data_index; /* index into var array for return + if data_type is data_var */ +} ct_data_t; typedef struct { size_t count; @@ -63,4 +65,4 @@ char *data; } saved_data_t; -#endif /* CTF_RETURNS_H */ +#endif diff --git a/lib/libcurses/director/testlang_conf.l b/lib/libcurses/director/testlang_conf.l --- a/lib/libcurses/director/testlang_conf.l +++ b/lib/libcurses/director/testlang_conf.l @@ -1,8 +1,9 @@ %{ -/* $NetBSD: testlang_conf.l,v 1.7 2013/11/21 11:06:04 blymn Exp $ */ +/* $NetBSD: testlang_conf.l,v 1.25 2021/02/25 00:50:10 rillig Exp $ */ /*- * Copyright 2009 Brett Lymn + * Copyright 2021 Roland Illig * * All rights reserved. * @@ -14,7 +15,7 @@ * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. The name of the author may not be used to endorse or promote products - * derived from this software withough specific prior written permission + * derived from this software without specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES @@ -26,8 +27,6 @@ * 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 @@ -45,7 +44,6 @@ int yylex(void); extern size_t line; -extern char *include_path; /* from director.c */ extern char *cur_file; /* from director.c */ static int include_stack[MAX_INCLUDES]; @@ -61,9 +59,8 @@ *len = 0; p = (const unsigned char *)s; while (*p) { - if (*p == '\\' && *(p+1)) { - if (isdigit(*(p+1)) && *(p+2) && isdigit(*(p+2)) && - *(p+3) && isdigit(*(p+3))) + if (*p == '\\' && p[1]) { + if (isdigit(p[1]) && isdigit(p[2]) && isdigit(p[3])) p += 3; else ++p; @@ -79,51 +76,59 @@ p = (const unsigned char *)s; q = buf; while (*p) { - if (*p == '\\' && *(p+1)) { + if (*p == '\\' && p[1]) { ++p; - if (isdigit(*p)) { - if (*(p+1) && isdigit(*(p+1)) && *(p+2) && - isdigit(*(p+2))) { - *q++ = ((*p - '0') * 8 + (*(p+1) - '0')) * 8 + (*(p+2) - '0'); + + if (isdigit(p[0])) { + if (isdigit(p[1]) && isdigit(p[2])) { + *q++ = (p[0] - '0') * 64 + + (p[1] - '0') * 8 + + (p[2] - '0'); p += 3; } else { *q++ = *p++; } - } else { - switch (*p) { - case 'e': - /* escape */ - *q++ = '\e'; - p++; - break; - - case 'n': - /* newline */ - *q++ = '\n'; - p++; - break; - - case 'r': - /* carriage return */ - *q++ = '\r'; - p++; - break; - - case 't': - /* tab */ - *q++ = '\t'; - p++; - break; - - case '\\': - /* backslash */ - *q++ = '\\'; - p++; - break; - - default: - *q++ = *p++; - } + continue; + } + + switch (*p) { + case 'e': + /* escape */ + *q++ = '\e'; + p++; + break; + + case 'n': + /* newline */ + *q++ = '\n'; + p++; + break; + + case 'r': + /* carriage return */ + *q++ = '\r'; + p++; + break; + + case 't': + /* tab */ + *q++ = '\t'; + p++; + break; + + case '\\': + /* backslash */ + *q++ = '\\'; + p++; + break; + + default: + if (isalpha(*p)) + errx(2, + "%s:%zu: Invalid escape sequence " + "'\\%c' in string literal", + cur_file, line, *p); + *q++ = *p++; } } else *q++ = *p++; @@ -137,28 +142,34 @@ HEX 0[xX][0-9a-zA-Z]+ STRING [0-9a-z!#-&(-^ \t%._\\]+ numeric [-0-9]+ -PCHAR (\\.|[^ \t\n]) -ASSIGN [aA][sS][sS][iI][gG][nN] -CALL2 [cC][aA][lL][lL]2 -CALL3 [cC][aA][lL][lL]3 -CALL4 [cC][aA][lL][lL]4 -CALL [cC][aA][lL][lL] -CHECK [cC][hH][eE][cC][kK] -DELAY [dD][eE][lL][aA][yY] -INPUT [iI][nN][pP][uU][tT] -NOINPUT [nN][oO][iI][nN][pP][uU][tT] -OK_RET [oO][kK] -ERR_RET [eE][rR][rR] -COMPARE [cC][oO][mM][pP][aA][rR][eE] -COMPAREND [cC][oO][mM][pP][aA][rR][eE][Nn][Dd] +PCHAR (\\.|[!-~]) +ASSIGN assign +CALL2 call2 +CALL3 call3 +CALL4 call4 +CALL call +CHECK check +DELAY delay +INPUT input +NOINPUT noinput +OK_RET OK +ERR_RET ERR +COMPARE compare +COMPAREND comparend FILENAME [A-Za-z0-9.][A-Za-z0-9./_-]+ VARNAME [A-Za-z][A-Za-z0-9_-]+ NULL_RET NULL NON_NULL NON_NULL +CCHAR cchar +WCHAR wchar BYTE BYTE OR \| -LHB \( -RHB \) +LPAREN \( +RPAREN \) +LBRACK \[ +RBRACK \] +MULTIPLIER \* +COMMA , %x incl %option noinput nounput @@ -167,34 +178,37 @@ include BEGIN(incl); -[ \t]* /* eat the whitespace */ -[^ \t\n]+ { /* got the include file name */ - char inc_file[MAXPATHLEN]; +[ \t]* /* eat the whitespace */ +[^ \t\n]+ { /* got the include file name */ + char *inc_file; if (include_ptr > MAX_INCLUDES) { - fprintf(stderr, - "Maximum number of nested includes exceeded " - "at line %zu of file %s\n", line, cur_file); - exit(2); + errx(2, + "%s:%zu: Maximum number of nested includes " + "exceeded", cur_file, line); } - if (yytext[0] != '/') { - if (strlcpy(inc_file, include_path, sizeof(inc_file)) - >= sizeof(inc_file)) - err(2, "CHECK_PATH too long"); - if ((include_path[strlen(include_path) - 1] != '/') && - ((strlcat(inc_file, "/", sizeof(inc_file)) - >= sizeof(inc_file)))) - err(2, "Could not append / to include file path"); + const char *dir_begin; + int dir_len; + if (yytext[0] == '/') { + dir_begin = ""; + dir_len = 0; } else { - inc_file[0] = '\0'; + dir_begin = cur_file; + const char *dir_end = strrchr(cur_file, '/'); + if (dir_end != NULL) { + dir_len = (int)(dir_end + 1 - dir_begin); + } else { + dir_begin = "."; + dir_len = 1; + } } - if (strlcat(inc_file, yytext, sizeof(inc_file)) - >= sizeof(inc_file)) - err(2, "Path to include file path overflowed"); + if (asprintf(&inc_file, "%.*s%s", + dir_len, dir_begin, yytext) == -1) + err(2, "Cannot construct include path"); - yyin = fopen(inc_file, "r" ); + yyin = fopen(inc_file, "r"); if (!yyin) err(1, "Error opening %s", inc_file); @@ -203,118 +217,66 @@ include_stack[include_ptr] = line; include_files[include_ptr++] = cur_file; - cur_file = strdup(inc_file); - if (cur_file == NULL) - err(2, "Cannot allocate new include file string"); - line = 0; + cur_file = inc_file; + line = 1; BEGIN(INITIAL); } <> { yypop_buffer_state(); - if ( !YY_CURRENT_BUFFER ) - { + if (!YY_CURRENT_BUFFER) yyterminate(); - } if (--include_ptr < 0) - err(2, "Include stack underflow"); + errx(2, "Include stack underflow"); free(cur_file); cur_file = include_files[include_ptr]; line = include_stack[include_ptr]; } -{ASSIGN} { - return ASSIGN; - } - -{CALL2} { - return CALL2; - } - -{CALL3} { - return CALL3; - } - -{CALL4} { - return CALL4; - } - -{CALL} { - return CALL; - } - -{CHECK} { - return CHECK; - } - -{DELAY} { - return DELAY; - } - -{INPUT} { - return INPUT; - } - -{NOINPUT} { - return NOINPUT; - } - -{COMPARE} { - return COMPARE; - } - -{COMPAREND} { - return COMPAREND; - } - -{NON_NULL} { - return NON_NULL; - } - -{NULL_RET} { - return NULL_RET; - } - -{OK_RET} { - return OK_RET; - } - -{ERR_RET} { - return ERR_RET; - } - -{OR} { - return OR; - } - -{LHB} { - return LHB; - } - -{RHB} { - return RHB; - } +{ASSIGN} return ASSIGN; +{CALL2} return CALL2; +{CALL3} return CALL3; +{CALL4} return CALL4; +{CALL} return CALL; +{CHECK} return CHECK; +{DELAY} return DELAY; +{INPUT} return INPUT; +{NOINPUT} return NOINPUT; +{COMPARE} return COMPARE; +{COMPAREND} return COMPAREND; +{NON_NULL} return NON_NULL; +{NULL_RET} return NULL_RET; +{OK_RET} return OK_RET; +{ERR_RET} return ERR_RET; +{MULTIPLIER} return MULTIPLIER; +{COMMA} return COMMA; +{CCHAR} return CCHAR; +{WCHAR} return WCHAR; +{OR} return OR; +{LPAREN} return LPAREN; +{RPAREN} return RPAREN; +{LBRACK} return LBRACK; +{RBRACK} return RBRACK; {HEX} { /* Hex value, convert to decimal and return numeric */ unsigned long val; if (sscanf(yytext, "%lx", &val) != 1) - err(1, "Bad hex conversion"); + errx(1, "Bad hex conversion"); asprintf(&yylval.string, "%ld", val); return numeric; } - -{numeric} { +{numeric} { if ((yylval.string = strdup(yytext)) == NULL) err(1, "Cannot allocate numeric string"); return numeric; -} + } {VARNAME} { if ((yylval.string = strdup(yytext)) == NULL) @@ -342,17 +304,17 @@ char *p; size_t len; - if ((yylval.retval = malloc(sizeof(returns_t))) == NULL) + if ((yylval.retval = malloc(sizeof(ct_data_t))) == NULL) err(1, "Cannot allocate return struct"); p = yytext; p++; /* skip the leading ' */ - if ((yylval.retval->return_value = dequote(p, &len)) + if ((yylval.retval->data_value = dequote(p, &len)) == NULL) err(1, "Cannot allocate string"); - yylval.retval->return_type = ret_byte; + yylval.retval->data_type = data_byte; /* trim trailing ' */ - yylval.retval->return_len = len - 1; + yylval.retval->data_len = len - 1; return BYTE; } @@ -362,10 +324,10 @@ size_t i; chtype *rv; - if ((yylval.retval = malloc(sizeof(returns_t))) == NULL) + if ((yylval.retval = malloc(sizeof(ct_data_t))) == NULL) err(1, "Cannot allocate return struct"); p = yytext; - p++; /* skip the leading ' */ + p++; /* skip the leading ` */ if ((str = dequote(p, &len)) == NULL) err(1, "Cannot allocate string"); len--; /* trim trailing ` */ @@ -373,16 +335,16 @@ len--; chlen = ((len / 2) + 1) * sizeof(chtype); - if ((yylval.retval->return_value = malloc(chlen)) + if ((yylval.retval->data_value = malloc(chlen)) == NULL) err(1, "Cannot allocate chtype array"); - rv = yylval.retval->return_value; + rv = yylval.retval->data_value; for (i = 0; i < len; i += 2) *rv++ = (str[i] << 8) | str[i+1]; - *rv = __NORMAL | '\0'; /* terminates chtype array */ - yylval.retval->return_type = ret_byte; - yylval.retval->return_len = chlen; + *rv = __NORMAL | '\0'; /* terminates chtype array */ + yylval.retval->data_type = data_byte; + yylval.retval->data_len = chlen; return BYTE; } @@ -409,23 +371,29 @@ err(1, "Cannot allocate string for varname"); return VARIABLE; } - - /* comments, white-outs */ + + /* whitespace, comments */ [ \t\r] | #.* ; -^#.*\n | -#.*\n | + +^[ \t\r]*#.*\n | \\\n | -^\n { -line++; } +^\n line++; /* eol on a line with data. need to process, return eol */ +#.*\n | \n { line++; return EOL; } . { + if (isprint((unsigned char)yytext[0])) + errx(1, "%s:%zu: Invalid character '%c'", + cur_file, line + 1, yytext[0]); + else + errx(1, "%s:%zu: Invalid character '0x%02x'", + cur_file, line + 1, yytext[0]); } %% diff --git a/lib/libcurses/director/testlang_parse.y b/lib/libcurses/director/testlang_parse.y --- a/lib/libcurses/director/testlang_parse.y +++ b/lib/libcurses/director/testlang_parse.y @@ -1,8 +1,9 @@ %{ -/* $NetBSD: testlang_parse.y,v 1.14 2015/01/04 20:19:46 christos Exp $ */ +/* $NetBSD: testlang_parse.y,v 1.53 2021/06/13 11:06:20 rillig Exp $ */ /*- * Copyright 2009 Brett Lymn + * Copyright 2021 Roland Illig * * All rights reserved. * @@ -14,7 +15,7 @@ * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. The name of the author may not be used to endorse or promote products - * derived from this software withough specific prior written permission + * derived from this software without specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES @@ -26,9 +27,8 @@ * 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 #include #include @@ -45,12 +45,12 @@ #include #include #include "returns.h" +#include "director.h" #define YYDEBUG 1 extern int verbose; -extern int cmdpipe[2]; -extern int slvpipe[2]; +extern int check_file_flag; extern int master; extern struct pollfd readfd; extern char *check_path; @@ -58,7 +58,7 @@ int yylex(void); -size_t line; +size_t line = 1; static int input_delay; @@ -79,27 +79,17 @@ static char *input_str; /* string to feed in as input */ static bool no_input; /* don't need more input */ -#define READ_PIPE 0 -#define WRITE_PIPE 1 +static wchar_t *vals = NULL; /* wchars to attach to a cchar type */ +static unsigned nvals; /* number of wchars */ -const char *returns_enum_names[] = { - "unused", "numeric", "string", "byte", "ERR", "OK", "NULL", "not NULL", - "variable", "reference", "returns count", "slave error" -}; - -typedef enum { - arg_static, - arg_byte, - arg_var, - arg_null -} args_state_t; - -static const char *args_enum_names[] = { - "static", "byte", "var", "NULL" +const char *const enum_names[] = { /* for data_enum_t */ + "unused", "numeric", "static", "string", "byte", "cchar", "wchar", "ERR", + "OK", "NULL", "not NULL", "variable", "reference", "return count", + "slave error" }; typedef struct { - args_state_t arg_type; + data_enum_t arg_type; size_t arg_len; char *arg_string; int var_index; @@ -108,7 +98,7 @@ typedef struct { char *function; int nrets; /* number of returns */ - returns_t *returns; /* array of expected returns */ + ct_data_t *returns; /* array of expected returns */ int nargs; /* number of arguments */ args_t *args; /* arguments for the call */ } cmd_line_t; @@ -118,52 +108,69 @@ typedef struct { char *name; size_t len; - returns_enum_t type; + data_enum_t type; void *value; + cchar_t cchar; } var_t; static size_t nvars; /* Number of declared variables */ static var_t *vars; /* Variables defined during the test. */ -static int check_function_table(char *, const char *[], int); +static int check_function_table(char *, const char *const[], int); static int find_var_index(const char *); -static void assign_arg(args_state_t, void *); -static int assign_var(char *); +static void assign_arg(data_enum_t, void *); +static int assign_var(const char *); void init_parse_variables(int); static void validate(int, void *); static void validate_return(const char *, const char *, int); -static void validate_variable(int, returns_enum_t, const void *, int, int); -static void validate_byte(returns_t *, returns_t *, int); +static void validate_variable(int, data_enum_t, const void *, int, int); +static void validate_byte(ct_data_t *, ct_data_t *, int); +static void validate_cchar(cchar_t *, cchar_t *, int); +static void validate_wchar(wchar_t *, wchar_t *, int); static void write_cmd_pipe(char *); -static void write_cmd_pipe_args(args_state_t, void *); -static void read_cmd_pipe(returns_t *); +static void write_cmd_pipe_args(data_enum_t, void *); +static void read_cmd_pipe(ct_data_t *); static void write_func_and_args(void); -static void compare_streams(char *, bool); +static void compare_streams(const char *, bool); static void do_function_call(size_t); +static void check(void); +static void delay_millis(const char *); +static void do_input(const char *); +static void do_noinput(void); static void save_slave_output(bool); -static void validate_type(returns_enum_t, returns_t *, int); -static void set_var(returns_enum_t, char *, void *); +static void validate_type(data_enum_t, ct_data_t *, int); +static void set_var(data_enum_t, const char *, void *); static void validate_reference(int, void *); -static char *numeric_or(char *, char *); -static char *get_numeric_var(const char *); +static char * numeric_or(char *, char *); +static char * get_numeric_var(const char *); static void perform_delay(struct timespec *); - -static const char *input_functions[] = { - "getch", "getnstr", "getstr", "mvgetnstr", "mvgetstr", "mvgetnstr", - "mvgetstr", "mvscanw", "mvwscanw", "scanw", "wgetch", "wgetnstr", - "wgetstr" +static void set_cchar(char *, void *); +static void set_wchar(char *); +static wchar_t *add_to_vals(data_enum_t, void *); + +#define variants(fn) "" fn, "mv" fn, "w" fn, "mvw" fn +static const char *const input_functions[] = { + variants("getch"), + variants("getnstr"), + variants("getstr"), + variants("getn_wstr"), + variants("get_wch"), + variants("get_wstr"), + variants("scanw"), }; +#undef variants static const unsigned ninput_functions = - sizeof(input_functions) / sizeof(char *); + sizeof(input_functions) / sizeof(input_functions[0]); -saved_data_t saved_output; +extern saved_data_t saved_output; %} %union { char *string; - returns_t *retval; + ct_data_t *retval; + wchar_t *vals; } %token PATH @@ -183,253 +190,289 @@ %token COMPARE %token COMPAREND %token ASSIGN -%token EOL CALL CHECK NOINPUT OR LHB RHB -%token CALL2 CALL3 CALL4 DRAIN +%token CCHAR +%token WCHAR +%token EOL CALL CHECK NOINPUT OR MULTIPLIER LPAREN RPAREN LBRACK RBRACK +%token COMMA +%token CALL2 CALL3 CALL4 + +%type attributes expr +%type array_elements array_element %nonassoc OR %% -statement : /* empty */ - | assign statement - | call statement - | call2 statement - | call3 statement - | call4 statement - | check statement - | delay statement - | input statement - | noinput statement - | compare statement - | comparend statement - | eol statement +statements : /* empty */ + | statement EOL statements ; -assign : ASSIGN VARNAME numeric {set_var(ret_number, $2, $3);} eol - | ASSIGN VARNAME LHB expr RHB {set_var(ret_number, $2, $4);} eol - | ASSIGN VARNAME STRING {set_var(ret_string, $2, $3);} eol - | ASSIGN VARNAME BYTE {set_var(ret_byte, $2, $3);} eol +statement : assign + | call + | call2 + | call3 + | call4 + | check + | delay + | input + | noinput + | compare + | comparend + | cchar + | wchar + | /* empty */ ; -call : CALL result fn_name args eol { - do_function_call(1); -} +assign : ASSIGN VARNAME numeric { + set_var(data_number, $2, $3); + } + | ASSIGN VARNAME LPAREN expr RPAREN { + set_var(data_number, $2, $4); + } + | ASSIGN VARNAME STRING { + set_var(data_string, $2, $3); + } + | ASSIGN VARNAME BYTE { + set_var(data_byte, $2, $3); + } ; -call2 : CALL2 result result fn_name args eol { - do_function_call(2); -} +cchar : CCHAR VARNAME attributes char_vals { + set_cchar($2, $3); + } ; -call3 : CALL3 result result result fn_name args eol { - do_function_call(3); -} +wchar : WCHAR VARNAME char_vals { + set_wchar($2); + } ; -call4 : CALL4 result result result result fn_name args eol { - do_function_call(4); - } +attributes : numeric + | LPAREN expr RPAREN { + $$ = $2; + } + | VARIABLE { + $$ = get_numeric_var($1); + } ; -check : CHECK var returns eol { - returns_t retvar; - var_t *vptr; - if (command.returns[0].return_index == -1) - err(1, "Undefined variable in check statement, line %zu" - " of file %s", line, cur_file); - - if (verbose) { - fprintf(stderr, "Checking contents of variable %s for %s\n", - vars[command.returns[0].return_index].name, - returns_enum_names[command.returns[1].return_type]); - } - - if (((command.returns[1].return_type == ret_byte) && - (vars[command.returns[0].return_index].type != ret_byte)) || - vars[command.returns[0].return_index].type != ret_string) - err(1, "Var type %s (%d) does not match return type %s (%d)", - returns_enum_names[ - vars[command.returns[0].return_index].type], - vars[command.returns[0].return_index].type, - returns_enum_names[command.returns[1].return_type], - command.returns[1].return_type); - - switch (command.returns[1].return_type) { - case ret_err: - validate_variable(0, ret_string, "ERR", - command.returns[0].return_index, 0); - break; - - case ret_ok: - validate_variable(0, ret_string, "OK", - command.returns[0].return_index, 0); - break; - - case ret_null: - validate_variable(0, ret_string, "NULL", - command.returns[0].return_index, 0); - break; - - case ret_nonnull: - validate_variable(0, ret_string, "NULL", - command.returns[0].return_index, 1); - break; - - case ret_string: - case ret_number: - if (verbose) { - fprintf(stderr, " %s == returned %s\n", - (const char *)command.returns[1].return_value, - (const char *) - vars[command.returns[0].return_index].value); +char_vals : numeric { + add_to_vals(data_number, $1); + } + | LBRACK array_elements RBRACK + | VARIABLE { + add_to_vals(data_var, $1); + } + | STRING { + add_to_vals(data_string, $1); + } + | BYTE { + add_to_vals(data_byte, $1); } - validate_variable(0, ret_string, - command.returns[1].return_value, - command.returns[0].return_index, 0); - break; - - case ret_byte: - vptr = &vars[command.returns[0].return_index]; - retvar.return_len = vptr->len; - retvar.return_type = vptr->type; - retvar.return_value = vptr->value; - validate_byte(&retvar, &command.returns[1], 0); - break; - - default: - err(1, "Malformed check statement at line %zu " - "of file %s", line, cur_file); - break; - } - - init_parse_variables(0); - } ; -delay : DELAY numeric eol { - /* set the inter-character delay */ - if (sscanf($2, "%d", &input_delay) == 0) - err(1, "delay specification %s could not be converted to " - "numeric at line %zu of file %s", $2, line, cur_file); - if (verbose) { - fprintf(stderr, "Set input delay to %d ms\n", input_delay); - } - - if (input_delay < DELAY_MIN) - input_delay = DELAY_MIN; - /* - * Fill in the timespec structure now ready for use later. - * The delay is specified in milliseconds so convert to timespec - * values - */ - delay_spec.tv_sec = input_delay / 1000; - delay_spec.tv_nsec = (input_delay - 1000 * delay_spec.tv_sec) * 1000; - if (verbose) { - fprintf(stderr, "set delay to %jd.%jd\n", - (intmax_t)delay_spec.tv_sec, - (intmax_t)delay_spec.tv_nsec); - } - - init_parse_variables(0); - } - ; +call : CALL result fn_name args { + do_function_call(1); + } + ; -input : INPUT STRING eol { - if (input_str != NULL) { - warnx("%s, %zu: Discarding unused input string", - cur_file, line); - free(input_str); - } +call2 : CALL2 result result fn_name args { + do_function_call(2); + } + ; - if ((input_str = malloc(strlen($2) + 1)) == NULL) - err(2, "Cannot allocate memory for input string"); +call3 : CALL3 result result result fn_name args { + do_function_call(3); + } + ; - strlcpy(input_str, $2, strlen($2) + 1); -} - ; +call4 : CALL4 result result result result fn_name args { + do_function_call(4); + } + ; +check : CHECK var returns { + check(); + } + ; -noinput : NOINPUT eol { - if (input_str != NULL) { - warnx("%s, %zu: Discarding unused input string", - cur_file, line); - free(input_str); - } +delay : DELAY numeric { + delay_millis($2); + } + ; - no_input = true; - } +input : INPUT STRING { + do_input($2); + } + ; -compare : COMPARE PATH eol - | COMPARE FILENAME eol -{ - compare_streams($2, true); -} - ; +noinput : NOINPUT { + do_noinput(); + } + ; +compare : COMPARE PATH { + compare_streams($2, true); + } + | COMPARE FILENAME { + compare_streams($2, true); + } + ; -comparend : COMPAREND PATH eol - | COMPAREND FILENAME eol -{ - compare_streams($2, false); -} - ; +comparend : COMPAREND PATH { + compare_streams($2, false); + } + | COMPAREND FILENAME { + compare_streams($2, false); + } + ; result : returns - | var | reference ; -returns : numeric { assign_rets(ret_number, $1); } - | LHB expr RHB { assign_rets(ret_number, $2); } - | STRING { assign_rets(ret_string, $1); } - | BYTE { assign_rets(ret_byte, (void *) $1); } - | ERR_RET { assign_rets(ret_err, NULL); } - | OK_RET { assign_rets(ret_ok, NULL); } - | NULL_RET { assign_rets(ret_null, NULL); } - | NON_NULL { assign_rets(ret_nonnull, NULL); } +returns : numeric { + assign_rets(data_number, $1); + } + | LPAREN expr RPAREN { + assign_rets(data_number, $2); + } + | STRING { + assign_rets(data_string, $1); + } + | BYTE { + assign_rets(data_byte, (void *) $1); + } + | ERR_RET { + assign_rets(data_err, NULL); + } + | OK_RET { + assign_rets(data_ok, NULL); + } + | NULL_RET { + assign_rets(data_null, NULL); + } + | NON_NULL { + assign_rets(data_nonnull, NULL); + } + | var ; var : VARNAME { - assign_rets(ret_var, $1); - } + assign_rets(data_var, $1); + } ; reference : VARIABLE { - assign_rets(ret_ref, $1); - } + assign_rets(data_ref, $1); + } + ; fn_name : VARNAME { - if (command.function != NULL) - free(command.function); + if (command.function != NULL) + free(command.function); + + command.function = malloc(strlen($1) + 1); + if (command.function == NULL) + err(1, "Could not allocate memory for function name"); + strcpy(command.function, $1); + } + ; + +array_elements : array_element + | array_element COMMA array_elements + ; + +array_element : numeric { + $$ = add_to_vals(data_number, $1); + } + | VARIABLE { + $$ = add_to_vals(data_number, get_numeric_var($1)); + } + | BYTE { + $$ = add_to_vals(data_byte, (void *) $1); + } + | STRING { + $$ = add_to_vals(data_string, (void *) $1); + } + | numeric MULTIPLIER numeric { + unsigned long i; + unsigned long acount; - command.function = malloc(strlen($1) + 1); - if (command.function == NULL) - err(1, "Could not allocate memory for function name"); - strcpy(command.function, $1); - } + acount = strtoul($3, NULL, 10); + for (i = 0; i < acount; i++) { + $$ = add_to_vals(data_number, $1); + } + } + | VARIABLE MULTIPLIER numeric { + unsigned long i, acount; + char *val; + + acount = strtoul($3, NULL, 10); + val = get_numeric_var($1); + for (i = 0; i < acount; i++) { + $$ = add_to_vals(data_number, val); + } + } + | BYTE MULTIPLIER numeric { + unsigned long i, acount; + + acount = strtoul($3, NULL, 10); + for (i = 0; i < acount; i++) { + $$ = add_to_vals(data_byte, (void *) $1); + } + } + | STRING MULTIPLIER numeric { + unsigned long i, acount; + + acount = strtoul($3, NULL, 10); + for (i = 0; i < acount; i++) { + $$ = add_to_vals(data_string, (void *) $1); + } + } ; expr : numeric - | VARIABLE - { $$ = get_numeric_var($1); } - | expr OR expr - { $$ = numeric_or($1, $3); } + | VARIABLE { + $$ = get_numeric_var($1); + } + | expr OR expr { + $$ = numeric_or($1, $3); + } ; args : /* empty */ - | LHB expr RHB { assign_arg(arg_static, $2); } args - | numeric { assign_arg(arg_static, $1); } args - | STRING { assign_arg(arg_static, $1); } args - | BYTE { assign_arg(arg_byte, $1); } args - | PATH { assign_arg(arg_static, $1); } args - | FILENAME { assign_arg(arg_static, $1); } args - | VARNAME { assign_arg(arg_static, $1); } args - | VARIABLE { assign_arg(arg_var, $1); } args - | NULL_RET { assign_arg(arg_null, $1); } args + | arg args ; -eol : EOL +arg : LPAREN expr RPAREN { + assign_arg(data_static, $2); + } + | numeric { + assign_arg(data_static, $1); + } + | STRING { + assign_arg(data_static, $1); + } + | BYTE { + assign_arg(data_byte, $1); + } + | PATH { + assign_arg(data_static, $1); + } + | FILENAME { + assign_arg(data_static, $1); + } + | VARNAME { + assign_arg(data_static, $1); + } + | VARIABLE { + assign_arg(data_var, $1); + } + | NULL_RET { + assign_arg(data_null, $1); + } ; %% @@ -447,7 +490,7 @@ if (strnvisx(dst, dstlen, data, datalen, VIS_WHITE | VIS_OCTAL) == -1) err(1, "strnvisx"); - warnx("%s, %zu: [%s] Excess %zu bytes%s [%s]", + warnx("%s:%zu: [%s] Excess %zu bytes%s [%s]", fname, lineno, func, datalen, comment, dst); free(dst); } @@ -462,10 +505,10 @@ int i; if ((i = find_var_index(var)) < 0) - err(1, "Variable %s is undefined", var); + errx(1, "Variable %s is undefined", var); - if (vars[i].type != ret_number) - err(1, "Variable %s is not a numeric type", var); + if (vars[i].type != data_number) + errx(1, "Variable %s is not a numeric type", var); return vars[i].value; } @@ -511,22 +554,99 @@ } } +/* + * Add to temporary vals array + */ +static wchar_t * +add_to_vals(data_enum_t argtype, void *arg) +{ + wchar_t *retval = NULL; + int have_malloced; + int i; + ct_data_t *ret; + + have_malloced = 0; + + if (nvals == 0) { + have_malloced = 1; + retval = malloc(sizeof(wchar_t)); + } else { + retval = realloc(vals, (nvals + 1) * sizeof(wchar_t)); + } + + if (retval == NULL) + return retval; + + vals = retval; + + switch (argtype) { + case data_number: + vals[nvals++] = (wchar_t) strtoul((char *) arg, NULL, 10); + break; + + case data_string: + vals[nvals++] = (wchar_t) ((char *)arg)[0]; + break; + + case data_byte: + ret = (ct_data_t *) arg; + vals[nvals++] = *((wchar_t *) ret->data_value); + break; + + case data_var: + if ((i = find_var_index((char *) arg)) < 0) + errx(1, "%s:%zu: Variable %s is undefined", + cur_file, line, (const char *) arg); + + switch (vars[i].type) { + + case data_number: + case data_string: + case data_byte: + retval = add_to_vals(vars[i].type, vars[i].value); + break; + + default: + errx(1, + "%s:%zu: Variable %s has invalid type for cchar", + cur_file, line, (const char *) arg); + break; + + } + break; + + default: + errx(1, "%s:%zu: Internal error: Unhandled type for vals array", + cur_file, line); + + /* if we get here without a value then tidy up */ + if ((nvals == 0) && (have_malloced == 1)) { + free(retval); + retval = vals; + } + break; + + } + + return retval; +} + /* * Assign the value given to the named variable. */ static void -set_var(returns_enum_t type, char *name, void *value) +set_var(data_enum_t type, const char *name, void *value) { int i; char *number; - returns_t *ret; + ct_data_t *ret; i = find_var_index(name); if (i < 0) i = assign_var(name); vars[i].type = type; - if ((type == ret_number) || (type == ret_string)) { + if ((type == data_number) || (type == data_string)) { number = value; vars[i].len = strlen(number) + 1; vars[i].value = malloc(vars[i].len + 1); @@ -536,20 +656,82 @@ } else { /* can only be a byte value */ ret = value; - vars[i].len = ret->return_len; + vars[i].len = ret->data_len; vars[i].value = malloc(vars[i].len); if (vars[i].value == NULL) err(1, "Could not malloc memory to assign byte string"); - memcpy(vars[i].value, ret->return_value, vars[i].len); + memcpy(vars[i].value, ret->data_value, vars[i].len); } } +/* + * Form up a complex character type from the given components. + */ +static void +set_cchar(char *name, void *attributes) +{ + int i; + unsigned j; + attr_t attribs; + + if (nvals >= CURSES_CCHAR_MAX) + errx(1, "%s:%zu: %s: too many characters in complex char type", + cur_file, line, __func__); + + i = find_var_index(name); + if (i < 0) + i = assign_var(name); + + if (sscanf((char *) attributes, "%d", &attribs) != 1) + errx(1, + "%s:%zu: %s: conversion of attributes to integer failed", + cur_file, line, __func__); + + vars[i].type = data_cchar; + vars[i].cchar.attributes = attribs; + vars[i].cchar.elements = nvals; + for (j = 0; j < nvals; j++) + vars[i].cchar.vals[j] = vals[j]; + + nvals = 0; + vals = NULL; + +} + +/* + * Form up a wide character string type from the given components. + */ +static void +set_wchar(char *name) +{ + int i; + unsigned j; + wchar_t *wcval; + + i = find_var_index(name); + if (i < 0) + i = assign_var(name); + + vars[i].type = data_wchar; + vars[i].len = (nvals+1) * sizeof(wchar_t); + vars[i].value = malloc(vars[i].len); + if (vars[i].value == NULL) + err(1, "Could not malloc memory to assign wchar string"); + wcval = vars[i].value; + for(j = 0; j < nvals; j++) + wcval[j] = vals[j]; + wcval[nvals] = L'\0'; + nvals = 0; + vals = NULL; + +} + /* * Add a new variable to the vars array, the value will be assigned later, * when a test function call returns. */ static int -assign_var(char *varname) +assign_var(const char *varname) { var_t *temp; char *name; @@ -576,42 +758,34 @@ * Allocate and assign a new argument of the given type. */ static void -assign_arg(args_state_t arg_type, void *arg) +assign_arg(data_enum_t arg_type, void *arg) { args_t *temp, cur; char *str = arg; - returns_t *ret; + ct_data_t *ret; if (verbose) { - fprintf(stderr, "function is >%s<, adding arg >%s< type %s\n", - command.function, str, args_enum_names[arg_type]); + fprintf(stderr, "function is >%s<, adding arg >%s< type %s (%d)\n", + command.function, str, enum_names[arg_type], arg_type); } cur.arg_type = arg_type; - switch (arg_type) { - case arg_var: + if (cur.arg_type == data_var) { cur.var_index = find_var_index(arg); if (cur.var_index < 0) - err(1, "Invalid variable %s at line %zu of file %s", - str, line, cur_file); - cur.arg_type = ret_string; - break; - - case arg_byte: + errx(1, "%s:%zu: Invalid variable %s", + cur_file, line, str); + } else if (cur.arg_type == data_byte) { ret = arg; - cur.arg_len = ret->return_len; + cur.arg_len = ret->data_len; cur.arg_string = malloc(cur.arg_len); if (cur.arg_string == NULL) err(1, "Could not malloc memory for arg bytes"); - memcpy(cur.arg_string, ret->return_value, cur.arg_len); - break; - - case arg_null: + memcpy(cur.arg_string, ret->data_value, cur.arg_len); + } else if (cur.arg_type == data_null) { cur.arg_len = 0; cur.arg_string = NULL; - break; - - default: + } else { cur.arg_len = strlen(str); cur.arg_string = malloc(cur.arg_len + 1); if (cur.arg_string == NULL) @@ -631,46 +805,46 @@ * Allocate and assign a new return. */ static void -assign_rets(returns_enum_t ret_type, void *ret) +assign_rets(data_enum_t ret_type, void *ret) { - returns_t *temp, cur; + ct_data_t *temp, cur; char *ret_str; - returns_t *ret_ret; + ct_data_t *ret_ret; - cur.return_type = ret_type; - if (ret_type != ret_var) { - if ((ret_type == ret_number) || (ret_type == ret_string)) { + cur.data_type = ret_type; + if (ret_type != data_var) { + if ((ret_type == data_number) || (ret_type == data_string)) { ret_str = ret; - cur.return_len = strlen(ret_str) + 1; - cur.return_value = malloc(cur.return_len + 1); - if (cur.return_value == NULL) + cur.data_len = strlen(ret_str) + 1; + cur.data_value = malloc(cur.data_len + 1); + if (cur.data_value == NULL) err(1, "Could not malloc memory for arg string"); - strcpy(cur.return_value, ret_str); - } else if (ret_type == ret_byte) { + strcpy(cur.data_value, ret_str); + } else if (ret_type == data_byte) { ret_ret = ret; - cur.return_len = ret_ret->return_len; - cur.return_value = malloc(cur.return_len); - if (cur.return_value == NULL) + cur.data_len = ret_ret->data_len; + cur.data_value = malloc(cur.data_len); + if (cur.data_value == NULL) err(1, "Could not malloc memory for byte string"); - memcpy(cur.return_value, ret_ret->return_value, - cur.return_len); - } else if (ret_type == ret_ref) { - if ((cur.return_index = find_var_index(ret)) < 0) - err(1, "Undefined variable reference"); + memcpy(cur.data_value, ret_ret->data_value, + cur.data_len); + } else if (ret_type == data_ref) { + if ((cur.data_index = find_var_index(ret)) < 0) + errx(1, "Undefined variable reference"); } } else { - cur.return_index = find_var_index(ret); - if (cur.return_index < 0) - cur.return_index = assign_var(ret); + cur.data_index = find_var_index(ret); + if (cur.data_index < 0) + cur.data_index = assign_var(ret); } temp = realloc(command.returns, sizeof(*temp) * (command.nrets + 1)); if (temp == NULL) err(1, "Failed to reallocate returns"); command.returns = temp; - memcpy(&command.returns[command.nrets], &cur, sizeof(returns_t)); + memcpy(&command.returns[command.nrets], &cur, sizeof(ct_data_t)); command.nrets++; } @@ -700,14 +874,13 @@ * Check the given function name in the given table of names, return 1 if * there is a match. */ -static int check_function_table(char *function, const char *table[], - int nfunctions) +static int +check_function_table(char *function, const char *const table[], int nfunctions) { int i; for (i = 0; i < nfunctions; i++) { - if ((strlen(function) == strlen(table[i])) && - (strcmp(function, table[i]) == 0)) + if (strcmp(function, table[i]) == 0) return 1; } @@ -719,7 +892,7 @@ * any differences. */ static void -compare_streams(char *filename, bool discard) +compare_streams(const char *filename, bool discard) { char check_file[PATH_MAX], drain[100], ref, data; struct pollfd fds[2]; @@ -734,25 +907,41 @@ if (filename[0] != '/') { if (strlcpy(check_file, check_path, sizeof(check_file)) >= sizeof(check_file)) - err(2, "CHECK_PATH too long"); + errx(2, "CHECK_PATH too long"); if (strlcat(check_file, "/", sizeof(check_file)) >= sizeof(check_file)) - err(2, "Could not append / to check file path"); + errx(2, "Could not append / to check file path"); } else { check_file[0] = '\0'; } if (strlcat(check_file, filename, sizeof(check_file)) >= sizeof(check_file)) - err(2, "Path to check file path overflowed"); + errx(2, "Path to check file path overflowed"); - if ((check_fd = open(check_file, O_RDONLY, 0)) < 0) - err(2, "failed to open file %s line %zu of file %s", - check_file, line, cur_file); + int create_check_file = 0; + + if (check_file_flag == (GEN_CHECK_FILE | FORCE_GEN)) + create_check_file = 1; + else if ((check_fd = open(check_file, O_RDONLY, 0)) < 0) { + if (check_file_flag & GEN_CHECK_FILE) + create_check_file = 1; + else + err(2, "%s:%zu: failed to open file %s", + cur_file, line, check_file); + } + + if (create_check_file) { + check_fd = open(check_file, O_WRONLY | O_CREAT, 0644); + if (check_fd < 0) { + err(2, "%s:%zu: failed to create file %s", + cur_file, line, check_file); + } + } fds[0].fd = check_fd; - fds[0].events = POLLIN; + fds[0].events = create_check_file ? POLLOUT:POLLIN; fds[1].fd = master; fds[1].events = POLLIN; @@ -766,13 +955,16 @@ offs = 0; while (poll(fds, nfd, 500) == nfd) { - if (fds[0].revents & POLLIN) { - if ((result = read(check_fd, &ref, 1)) < 1) { - if (result != 0) { - err(2, - "Bad read on file %s", check_file); - } else { - break; + /* Read from check file if doing comparison */ + if (!create_check_file) { + if (fds[0].revents & POLLIN) { + if ((result = read(check_fd, &ref, 1)) < 1) { + if (result != 0) { + err(2, "Bad read on file %s", + check_file); + } else { + break; + } } } } @@ -785,24 +977,37 @@ if (saved_output.count == 0) nfd = 2; } else { - if (fds[0].revents & POLLIN) { + int revent = (create_check_file == 1) ? POLLOUT:POLLIN; + if (fds[0].revents & revent) { if (read(master, &data, 1) < 1) err(2, "Bad read on slave pty"); } else continue; } + if (create_check_file) { + if ((result = write(check_fd, &data, 1)) < 1) + err(2, "Bad write on file %s", check_file); + ref = data; + } + if (verbose) { - fprintf(stderr, "Comparing reference byte 0x%x (%c)" - " against slave byte 0x%x (%c)\n", - ref, (ref >= ' ') ? ref : '-', - data, (data >= ' ' )? data : '-'); + if (create_check_file) + fprintf(stderr, "Saving reference byte 0x%x (%c)" + " against slave byte 0x%x (%c)\n", + ref, (ref >= ' ') ? ref : '-', + data, (data >= ' ' )? data : '-'); + else + fprintf(stderr, "Comparing reference byte 0x%x (%c)" + " against slave byte 0x%x (%c)\n", + ref, (ref >= ' ') ? ref : '-', + data, (data >= ' ' )? data : '-'); } - if (ref != data) { - errx(2, "%s, %zu: refresh data from slave does " - "not match expected from file %s offs %zu " - "[reference 0x%x (%c) != slave 0x%x (%c)]", + if (!create_check_file && ref != data) { + errx(2, "%s:%zu: refresh data from slave does " + "not match expected from file %s offset %zu " + "[reference 0x%02x (%c) != slave 0x%02x (%c)]", cur_file, line, check_file, offs, ref, (ref >= ' ') ? ref : '-', data, (data >= ' ') ? data : '-'); @@ -811,10 +1016,17 @@ offs++; } - - if (saved_output.count > 0) - excess(cur_file, line, __func__, " from slave", - &saved_output.data[saved_output.readp], saved_output.count); + /* + * if creating a check file, there shouldn't be + * anymore saved output + */ + if (saved_output.count > 0) { + if (create_check_file) + errx(2, "Slave output not flushed correctly"); + else + excess(cur_file, line, __func__, " from slave", + &saved_output.data[saved_output.readp], saved_output.count); + } /* discard any excess saved output if required */ if (discard) { @@ -822,7 +1034,7 @@ saved_output.readp = 0; } - if ((result = poll(&fds[0], 2, 0)) != 0) { + if (!create_check_file && (result = poll(fds, 2, 0)) != 0) { if (result == -1) err(2, "poll of file descriptors failed"); @@ -864,7 +1076,7 @@ int do_input; size_t i; struct pollfd fds[3]; - returns_t response[MAX_RESULTS], returns_count; + ct_data_t response[MAX_RESULTS], returns_count; assert(nresults <= MAX_RESULTS); do_input = check_function_table(command.function, input_functions, @@ -877,15 +1089,15 @@ * doing input otherwise it will confuse the input poll */ read_cmd_pipe(&returns_count); - if (returns_count.return_type != ret_count) - err(2, "expected return type of ret_count but received %s", - returns_enum_names[returns_count.return_type]); + if (returns_count.data_type != data_count) + errx(2, "expected return type of data_count but received %s", + enum_names[returns_count.data_type]); perform_delay(&delay_post_call); /* let slave catch up */ if (verbose) { fprintf(stderr, "Expect %zu results from slave, slave " - "reported %zu\n", nresults, returns_count.return_len); + "reported %zu\n", nresults, returns_count.data_len); } if ((no_input == false) && (do_input == 1)) { @@ -895,22 +1107,22 @@ } if (input_str == NULL) - errx(2, "%s, %zu: Call to input function " + errx(2, "%s:%zu: Call to input function " "but no input defined", cur_file, line); - fds[0].fd = slvpipe[READ_PIPE]; + fds[0].fd = from_slave; fds[0].events = POLLIN; fds[1].fd = master; fds[1].events = POLLOUT; p = input_str; save_slave_output(false); - while(*p != '\0') { + while (*p != '\0') { perform_delay(&delay_spec); if (poll(fds, 2, 0) < 0) err(2, "poll failed"); if (fds[0].revents & POLLIN) { - warnx("%s, %zu: Slave function " + warnx("%s:%zu: Slave function " "returned before end of input string", cur_file, line); break; @@ -922,7 +1134,7 @@ *p); } if (write(master, p, 1) != 1) { - warn("%s, %zu: Slave function write error", + warn("%s:%zu: Slave function write error", cur_file, line); break; } @@ -941,10 +1153,10 @@ } if (verbose) { - fds[0].fd = slvpipe[READ_PIPE]; + fds[0].fd = to_slave; fds[0].events = POLLIN; - fds[1].fd = slvpipe[WRITE_PIPE]; + fds[1].fd = from_slave; fds[1].events = POLLOUT; fds[2].fd = master; @@ -961,7 +1173,7 @@ /* drain any trailing output */ save_slave_output(false); - for (i = 0; i < returns_count.return_len; i++) { + for (i = 0; i < returns_count.data_len; i++) { read_cmd_pipe(&response[i]); } @@ -971,42 +1183,49 @@ * expect but in this case we should report the slave error * instead of a return count mismatch. */ - if ((returns_count.return_len > 0) && - (response[0].return_type == ret_slave_error)) - err(2, "Slave returned error: %s", - (const char *)response[0].return_value); + if ((returns_count.data_len > 0) && + (response[0].data_type == data_slave_error)) + errx(2, "Slave returned error: %s", + (const char *)response[0].data_value); - if (returns_count.return_len != nresults) - err(2, "Incorrect number of returns from slave, expected %zu " - "but received %zu", nresults, returns_count.return_len); + if (returns_count.data_len != nresults) + errx(2, "Incorrect number of returns from slave, expected %zu " + "but received %zu", nresults, returns_count.data_len); if (verbose) { for (i = 0; i < nresults; i++) { - if ((response[i].return_type != ret_byte) && - (response[i].return_type != ret_err) && - (response[i].return_type != ret_ok)) + if ((response[i].data_type != data_byte) && + (response[i].data_type != data_err) && + (response[i].data_type != data_ok)) fprintf(stderr, "received response >%s< " "expected", - (const char *)response[i].return_value); + (const char *)response[i].data_value); else fprintf(stderr, "received"); - fprintf(stderr, " return_type %s\n", - returns_enum_names[command.returns[i].return_type]); + fprintf(stderr, " data_type %s\n", + enum_names[command.returns[i].data_type]); } } for (i = 0; i < nresults; i++) { - if (command.returns[i].return_type != ret_var) { + if (command.returns[i].data_type != data_var) { validate(i, &response[i]); } else { - vars[command.returns[i].return_index].len = - response[i].return_len; - vars[command.returns[i].return_index].value = - response[i].return_value; - vars[command.returns[i].return_index].type = - response[i].return_type; + vars[command.returns[i].data_index].len = + response[i].data_len; + + if (response[i].data_type == data_cchar) { + vars[command.returns[i].data_index].cchar = + *((cchar_t *)response[i].data_value); + } else { + vars[command.returns[i].data_index].value = + response[i].data_value; + } + + vars[command.returns[i].data_index].type = + response[i].data_type; } } @@ -1031,7 +1250,7 @@ write_cmd_pipe(command.function); for (i = 0; i < command.nargs; i++) { - if (command.args[i].arg_type == arg_var) + if (command.args[i].arg_type == data_var) write_cmd_pipe_args(command.args[i].arg_type, &vars[command.args[i].var_index]); else @@ -1042,6 +1261,151 @@ write_cmd_pipe(NULL); /* signal end of arguments */ } +static void +check(void) +{ + ct_data_t retvar; + var_t *vptr; + + if (command.returns[0].data_index == -1) + errx(1, "%s:%zu: Undefined variable in check statement", + cur_file, line); + + if (command.returns[1].data_type == data_var) { + vptr = &vars[command.returns[1].data_index]; + command.returns[1].data_type = vptr->type; + command.returns[1].data_len = vptr->len; + if (vptr->type != data_cchar) + command.returns[1].data_value = vptr->value; + else + command.returns[1].data_value = &vptr->cchar; + } + + if (verbose) { + fprintf(stderr, "Checking contents of variable %s for %s\n", + vars[command.returns[0].data_index].name, + enum_names[command.returns[1].data_type]); + } + + /* + * Check if var and return have same data types + */ + if (((command.returns[1].data_type == data_byte) && + (vars[command.returns[0].data_index].type != data_byte))) + errx(1, "Var type %s (%d) does not match return type %s (%d)", + enum_names[vars[command.returns[0].data_index].type], + vars[command.returns[0].data_index].type, + enum_names[command.returns[1].data_type], + command.returns[1].data_type); + + switch (command.returns[1].data_type) { + case data_err: + case data_ok: + validate_type(vars[command.returns[0].data_index].type, + &command.returns[1], 0); + break; + + case data_null: + validate_variable(0, data_string, "NULL", + command.returns[0].data_index, 0); + break; + + case data_nonnull: + validate_variable(0, data_string, "NULL", + command.returns[0].data_index, 1); + break; + + case data_string: + case data_number: + if (verbose) { + fprintf(stderr, " %s == returned %s\n", + (const char *)command.returns[1].data_value, + (const char *) + vars[command.returns[0].data_index].value); + } + validate_variable(0, data_string, + command.returns[1].data_value, + command.returns[0].data_index, 0); + break; + + case data_byte: + vptr = &vars[command.returns[0].data_index]; + retvar.data_len = vptr->len; + retvar.data_type = vptr->type; + retvar.data_value = vptr->value; + validate_byte(&retvar, &command.returns[1], 0); + break; + + case data_cchar: + validate_cchar(&vars[command.returns[0].data_index].cchar, + (cchar_t *) command.returns[1].data_value, 0); + break; + + case data_wchar: + validate_wchar((wchar_t *) vars[command.returns[0].data_index].value, + (wchar_t *) command.returns[1].data_value, 0); + break; + + default: + errx(1, "%s:%zu: Malformed check statement", cur_file, line); + break; + } + + init_parse_variables(0); +} + +static void +delay_millis(const char *millis) +{ + /* set the inter-character delay */ + if (sscanf(millis, "%d", &input_delay) == 0) + errx(1, "%s:%zu: Delay specification %s must be an int", + cur_file, line, millis); + if (verbose) { + fprintf(stderr, "Set input delay to %d ms\n", input_delay); + } + + if (input_delay < DELAY_MIN) + input_delay = DELAY_MIN; + /* + * Fill in the timespec structure now ready for use later. + * The delay is specified in milliseconds so convert to timespec + * values + */ + delay_spec.tv_sec = input_delay / 1000; + delay_spec.tv_nsec = (input_delay - 1000 * delay_spec.tv_sec) * 1000; + if (verbose) { + fprintf(stderr, "set delay to %jd.%jd\n", + (intmax_t)delay_spec.tv_sec, + (intmax_t)delay_spec.tv_nsec); + } + + init_parse_variables(0); +} + +static void +do_input(const char *s) +{ + if (input_str != NULL) { + warnx("%s:%zu: Discarding unused input string", cur_file, line); + free(input_str); + } + + if ((input_str = strdup(s)) == NULL) + err(2, "Cannot allocate memory for input string"); +} + +static void +do_noinput(void) +{ + if (input_str != NULL) { + warnx("%s:%zu: Discarding unused input string", cur_file, line); + free(input_str); + } + + no_input = true; +} + /* * Initialise the command structure - if initial is non-zero then just set * everything to sane values otherwise free any memory that was allocated @@ -1056,18 +1420,18 @@ if (initial == 0) { free(command.function); for (i = 0; i < command.nrets; i++) { - if (command.returns[i].return_type == ret_number) - free(command.returns[i].return_value); + if (command.returns[i].data_type == data_number) + free(command.returns[i].data_value); } free(command.returns); for (i = 0; i < command.nargs; i++) { - if (command.args[i].arg_type != arg_var) + if (command.args[i].arg_type != data_var) free(command.args[i].arg_string); } free(command.args); } else { - line = 0; + line = 1; input_delay = 0; vars = NULL; nvars = 0; @@ -1099,7 +1463,7 @@ if (result < 0) err(2, "Poll of slave pty failed"); else if (result > 0) - warnx("%s, %zu: Unexpected data from slave", cur_file, line); + warnx("%s:%zu: Unexpected data from slave", cur_file, line); } /* @@ -1110,58 +1474,57 @@ validate(int i, void *data) { char *response; - returns_t *byte_response; + ct_data_t *byte_response; byte_response = data; - if ((command.returns[i].return_type != ret_byte) && - (command.returns[i].return_type != ret_err) && - (command.returns[i].return_type != ret_ok)) { - if ((byte_response->return_type == ret_byte) || - (byte_response->return_type == ret_err) || - (byte_response->return_type == ret_ok)) - err(1, "%s: expecting type %s, received type %s" - " at line %zu of file %s", __func__, - returns_enum_names[command.returns[i].return_type], - returns_enum_names[byte_response->return_type], - line, cur_file); - - response = byte_response->return_value; + if ((command.returns[i].data_type != data_byte) && + (command.returns[i].data_type != data_err) && + (command.returns[i].data_type != data_ok)) { + if ((byte_response->data_type == data_byte) || + (byte_response->data_type == data_err) || + (byte_response->data_type == data_ok)) + errx(1, + "%s:%zu: %s: expecting type %s, received type %s", + cur_file, line, __func__, + enum_names[command.returns[i].data_type], + enum_names[byte_response->data_type]); + + response = byte_response->data_value; } - switch (command.returns[i].return_type) { - case ret_err: - validate_type(ret_err, byte_response, 0); + switch (command.returns[i].data_type) { + case data_err: + validate_type(data_err, byte_response, 0); break; - case ret_ok: - validate_type(ret_ok, byte_response, 0); + case data_ok: + validate_type(data_ok, byte_response, 0); break; - case ret_null: + case data_null: validate_return("NULL", response, 0); break; - case ret_nonnull: + case data_nonnull: validate_return("NULL", response, 1); break; - case ret_string: - case ret_number: - validate_return(command.returns[i].return_value, + case data_string: + case data_number: + validate_return(command.returns[i].data_value, response, 0); break; - case ret_ref: + case data_ref: validate_reference(i, response); break; - case ret_byte: + case data_byte: validate_byte(&command.returns[i], byte_response, 0); break; default: - err(1, "Malformed statement at line %zu of file %s", - line, cur_file); + errx(1, "%s:%zu: Malformed statement", cur_file, line); break; } } @@ -1173,36 +1536,44 @@ validate_reference(int i, void *data) { char *response; - returns_t *byte_response; + ct_data_t *byte_response; var_t *varp; - varp = &vars[command.returns[i].return_index]; + varp = &vars[command.returns[i].data_index]; byte_response = data; - if (command.returns[i].return_type != ret_byte) + if (command.returns[i].data_type != data_byte) response = data; if (verbose) { fprintf(stderr, "%s: return type of %s, value %s \n", __func__, - returns_enum_names[varp->type], - (const char *)varp->value); + enum_names[varp->type], + (varp->type != data_cchar && varp->type != data_wchar) + ? (const char *)varp->value : "-"); } switch (varp->type) { - case ret_string: - case ret_number: + case data_string: + case data_number: validate_return(varp->value, response, 0); break; - case ret_byte: + case data_byte: validate_byte(varp->value, byte_response, 0); break; + case data_cchar: + validate_cchar(&(varp->cchar), (cchar_t *) response, 0); + break; + + case data_wchar: + validate_wchar((wchar_t *) varp->value, (wchar_t *) response, 0); + break; + default: - err(1, - "Invalid return type for reference at line %zu of file %s", - line, cur_file); + errx(1, "%s:%zu: Invalid return type for reference", + cur_file, line); break; } } @@ -1212,21 +1583,22 @@ * if they don't match. */ static void -validate_type(returns_enum_t expected, returns_t *value, int check) +validate_type(data_enum_t expected, ct_data_t *value, int check) { - if (((check == 0) && (expected != value->return_type)) || - ((check == 1) && (expected == value->return_type))) - err(1, "Validate expected type %s %s %s line %zu of file %s", - returns_enum_names[expected], + if (((check == 0) && (expected != value->data_type)) || + ((check == 1) && (expected == value->data_type))) + errx(1, "%s:%zu: Validate expected type %s %s %s", + cur_file, line, + enum_names[expected], (check == 0)? "matching" : "not matching", - returns_enum_names[value->return_type], line, cur_file); + enum_names[value->data_type]); if (verbose) { - fprintf(stderr, "Validate expected type %s %s %s line %zu" - " of file %s\n", - returns_enum_names[expected], + fprintf(stderr, "%s:%zu: Validated expected type %s %s %s\n", + cur_file, line, + enum_names[expected], (check == 0)? "matching" : "not matching", - returns_enum_names[value->return_type], line, cur_file); + enum_names[value->data_type]); } } @@ -1239,15 +1611,18 @@ { if (((check == 0) && strcmp(expected, value) != 0) || ((check == 1) && strcmp(expected, value) == 0)) - errx(1, "Validate expected %s %s %s line %zu of file %s", + errx(1, "%s:%zu: Validate expected >%s< %s >%s<", + cur_file, line, expected, - (check == 0)? "matching" : "not matching", value, - line, cur_file); + (check == 0)? "matching" : "not matching", + value); if (verbose) { - fprintf(stderr, "Validated expected value %s %s %s " - "at line %zu of file %s\n", expected, + fprintf(stderr, + "%s:%zu: Validated expected value >%s< %s >%s<\n", + cur_file, line, + expected, (check == 0)? "matches" : "does not match", - value, line, cur_file); + value); } } @@ -1256,22 +1631,22 @@ * if they don't match expectations. */ static void -validate_byte(returns_t *expected, returns_t *value, int check) +validate_byte(ct_data_t *expected, ct_data_t *value, int check) { char *ch; size_t i; if (verbose) { - ch = value->return_value; + ch = value->data_value; fprintf(stderr, "checking returned byte stream: "); - for (i = 0; i < value->return_len; i++) + for (i = 0; i < value->data_len; i++) fprintf(stderr, "%s0x%x", (i != 0)? ", " : "", ch[i]); fprintf(stderr, "\n"); fprintf(stderr, "%s byte stream: ", (check == 0)? "matches" : "does not match"); - ch = (char *) expected->return_value; - for (i = 0; i < expected->return_len; i++) + ch = (char *) expected->data_value; + for (i = 0; i < expected->data_len; i++) fprintf(stderr, "%s0x%x", (i != 0)? ", " : "", ch[i]); fprintf(stderr, "\n"); } @@ -1279,28 +1654,174 @@ /* * No chance of a match if lengths differ... */ - if ((check == 0) && (expected->return_len != value->return_len)) - errx(1, "Byte validation failed, length mismatch, expected %zu," - "received %zu", expected->return_len, value->return_len); + if ((check == 0) && (expected->data_len != value->data_len)) + errx(1, + "Byte validation failed, length mismatch, " + "expected %zu, received %zu", + expected->data_len, value->data_len); /* * If check is 0 then we want to throw an error IFF the byte streams * do not match, if check is 1 then throw an error if the byte * streams match. */ - if (((check == 0) && memcmp(expected->return_value, value->return_value, - value->return_len) != 0) || - ((check == 1) && (expected->return_len == value->return_len) && - memcmp(expected->return_value, value->return_value, - value->return_len) == 0)) - errx(1, "Validate expected %s byte stream at line %zu" - "of file %s", - (check == 0)? "matching" : "not matching", line, cur_file); + if (((check == 0) && memcmp(expected->data_value, value->data_value, + value->data_len) != 0) || + ((check == 1) && (expected->data_len == value->data_len) && + memcmp(expected->data_value, value->data_value, + value->data_len) == 0)) + errx(1, "%s:%zu: Validate expected %s byte stream", + cur_file, line, + (check == 0)? "matching" : "not matching"); if (verbose) { - fprintf(stderr, "Validated expected %s byte stream " - "at line %zu of file %s\n", - (check == 0)? "matching" : "not matching", - line, cur_file); + fprintf(stderr, "%s:%zu: Validated expected %s byte stream\n", + cur_file, line, + (check == 0)? "matching" : "not matching"); + } +} + +/* + * Validate the return cchar against the expected cchar, throw an error + * if they don't match expectations. + */ +static void +validate_cchar(cchar_t *expected, cchar_t *value, int check) +{ + unsigned j; + + /* + * No chance of a match if elements count differ... + */ + if ((expected->elements != value->elements)) { + if (check == 0) + errx(1, + "cchar validation failed, elements count mismatch, " + "expected %d, received %d", + expected->elements, value->elements); + else { + if (verbose) + fprintf(stderr, + "%s:%zu: Validated expected %s cchar", + cur_file, line, "not matching"); + return; + } + } + + /* + * No chance of a match if attributes differ... + */ + + if ((expected->attributes & WA_ATTRIBUTES) != + (value->attributes & WA_ATTRIBUTES )) { + if (check == 0) + errx(1, + "cchar validation failed, attributes mismatch, " + "expected 0x%x, received 0x%x", + expected->attributes & WA_ATTRIBUTES, + value->attributes & WA_ATTRIBUTES); + else { + if (verbose) + fprintf(stderr, + "%s:%zu: Validated expected %s cchar\n", + cur_file, line, "not matching"); + return; + } + } + + /* + * If check is 0 then we want to throw an error IFF the vals + * do not match, if check is 1 then throw an error if the vals + * streams match. + */ + for(j = 0; j < expected->elements; j++) { + if (expected->vals[j] != value->vals[j]) { + if (check == 0) + errx(1, + "cchar validation failed, vals mismatch, " + "expected 0x%x, received 0x%x", + expected->vals[j], value->vals[j]); + else { + if (verbose) + fprintf(stderr, + "%s:%zu: Validated expected %s " + "cchar\n", + cur_file, line, "not matching"); + return; + } + } + } + + if (verbose) { + fprintf(stderr, + "%s:%zu: Validated expected %s cchar\n", + cur_file, line, (check == 0)? "matching" : "not matching"); + } +} + +/* + * Validate the return wchar string against the expected wchar, throw an + * error if they don't match expectations. + */ +static void +validate_wchar(wchar_t *expected, wchar_t *value, int check) +{ + unsigned j; + + unsigned len1 = 0; + unsigned len2 = 0; + wchar_t *p; + + p = expected; + while (*p++ != L'\0') + len1++; + + p = value; + while (*p++ != L'\0') + len2++; + + /* + * No chance of a match if length differ... + */ + if (len1 != len2) { + if (check == 0) + errx(1, + "wchar string validation failed, length mismatch, " + "expected %d, received %d", + len1, len2); + else { + if (verbose) + fprintf(stderr, + "%s:%zu: Validated expected %s wchar\n", + cur_file, line, "not matching"); + return; + } + } + + /* + * If check is 0 then we want to throw an error IFF the vals + * do not match, if check is 1 then throw an error if the vals + * streams match. + */ + for(j = 0; j < len1; j++) { + if (expected[j] != value[j]) { + if (check == 0) + errx(1, "wchar validation failed at index %d, expected %d," + "received %d", j, expected[j], value[j]); + else { + if (verbose) + fprintf(stderr, + "%s:%zu: Validated expected %s wchar\n", + cur_file, line, "not matching"); + return; + } + } + } + + if (verbose) { + fprintf(stderr, + "%s:%zu: Validated expected %s wchar\n", + cur_file, line, + (check == 0)? "matching" : "not matching"); } } @@ -1310,61 +1831,61 @@ * negated. */ static void -validate_variable(int ret, returns_enum_t type, const void *value, int i, +validate_variable(int ret, data_enum_t type, const void *value, int i, int check) { - returns_t *retval; + ct_data_t *retval; var_t *varptr; retval = &command.returns[ret]; - varptr = &vars[command.returns[ret].return_index]; + varptr = &vars[command.returns[ret].data_index]; if (varptr->value == NULL) - err(1, "Variable %s has no value assigned to it", varptr->name); + errx(1, "Variable %s has no value assigned to it", varptr->name); if (varptr->type != type) - err(1, "Variable %s is not the expected type", varptr->name); + errx(1, "Variable %s is not the expected type", varptr->name); - if (type != ret_byte) { + if (type != data_byte) { if ((((check == 0) && strcmp(value, varptr->value) != 0)) || ((check == 1) && strcmp(value, varptr->value) == 0)) - err(1, "Variable %s contains %s instead of %s" - " value %s at line %zu of file %s", + errx(1, "%s:%zu: Variable %s contains %s instead of %s" + " value %s", + cur_file, line, varptr->name, (const char *)varptr->value, (check == 0)? "expected" : "not matching", - (const char *)value, - line, cur_file); + (const char *)value); if (verbose) { - fprintf(stderr, "Variable %s contains %s value " - "%s at line %zu of file %s\n", + fprintf(stderr, + "%s:%zu: Variable %s contains %s value %s\n", + cur_file, line, varptr->name, (check == 0)? "expected" : "not matching", - (const char *)varptr->value, line, cur_file); + (const char *)varptr->value); } } else { - if ((check == 0) && (retval->return_len != varptr->len)) - err(1, "Byte validation failed, length mismatch"); + if ((check == 0) && (retval->data_len != varptr->len)) + errx(1, "Byte validation failed, length mismatch"); /* * If check is 0 then we want to throw an error IFF * the byte streams do not match, if check is 1 then * throw an error if the byte streams match. */ - if (((check == 0) && memcmp(retval->return_value, varptr->value, + if (((check == 0) && memcmp(retval->data_value, varptr->value, varptr->len) != 0) || - ((check == 1) && (retval->return_len == varptr->len) && - memcmp(retval->return_value, varptr->value, + ((check == 1) && (retval->data_len == varptr->len) && + memcmp(retval->data_value, varptr->value, varptr->len) == 0)) - err(1, "Validate expected %s byte stream at line %zu" - " of file %s", - (check == 0)? "matching" : "not matching", - line, cur_file); + errx(1, "%s:%zu: Validate expected %s byte stream", + cur_file, line, + (check == 0)? "matching" : "not matching"); if (verbose) { - fprintf(stderr, "Validated expected %s byte stream " - "at line %zu of file %s\n", - (check == 0)? "matching" : "not matching", - line, cur_file); + fprintf(stderr, + "%s:%zu: Validated expected %s byte stream\n", + cur_file, line, + (check == 0)? "matching" : "not matching"); } } } @@ -1385,7 +1906,7 @@ else len = strlen(cmd); - arg.arg_type = arg_static; + arg.arg_type = data_static; arg.arg_len = len; arg.arg_string = cmd; write_cmd_pipe_args(arg.arg_type, &arg); @@ -1393,7 +1914,7 @@ } static void -write_cmd_pipe_args(args_state_t type, void *data) +write_cmd_pipe_args(data_enum_t type, void *data) { var_t *var_data; args_t *arg_data; @@ -1402,18 +1923,34 @@ arg_data = data; switch (type) { - case arg_var: + case data_var: var_data = data; len = var_data->len; cmd = var_data->value; - if (type == arg_byte) - send_type = ret_byte; - else - send_type = ret_string; + + switch (var_data->type) { + case data_byte: + send_type = data_byte; + break; + + case data_cchar: + send_type = data_cchar; + cmd = (void *) &var_data->cchar; + len = sizeof(cchar_t); + break; + + case data_wchar: + send_type = data_wchar; + break; + + default: + send_type = data_string; + break; + } break; - case arg_null: - send_type = ret_null; + case data_null: + send_type = data_null; len = 0; break; @@ -1423,25 +1960,33 @@ else len = arg_data->arg_len; cmd = arg_data->arg_string; - if (type == arg_byte) - send_type = ret_byte; + if (type == data_byte) + send_type = data_byte; else - send_type = ret_string; + send_type = data_string; } if (verbose) { fprintf(stderr, "Writing type %s to command pipe\n", - returns_enum_names[send_type]); + enum_names[send_type]); } - if (write(cmdpipe[WRITE_PIPE], &send_type, sizeof(int)) < 0) + if (write(to_slave, &send_type, sizeof(int)) < 0) err(1, "command pipe write for type failed"); if (verbose) { - fprintf(stderr, "Writing length %d to command pipe\n", len); + if (send_type == data_cchar) + fprintf(stderr, + "Writing cchar to command pipe\n"); + else if (send_type == data_wchar) + fprintf(stderr, + "Writing wchar(%d sized) to command pipe\n", len); + else + fprintf(stderr, + "Writing length %d to command pipe\n", len); } - if (write(cmdpipe[WRITE_PIPE], &len, sizeof(int)) < 0) + if (write(to_slave, &len, sizeof(int)) < 0) err(1, "command pipe write for length failed"); if (len > 0) { @@ -1449,7 +1994,7 @@ fprintf(stderr, "Writing data >%s< to command pipe\n", (const char *)cmd); } - if (write(cmdpipe[WRITE_PIPE], cmd, len) < 0) + if (write(to_slave, cmd, len) < 0) err(1, "command pipe write of data failed"); } } @@ -1459,7 +2004,7 @@ * length of the response then the actual data. */ static void -read_cmd_pipe(returns_t *response) +read_cmd_pipe(ct_data_t *response) { int len, type; struct pollfd rfd[2]; @@ -1471,14 +2016,14 @@ * output from the slave because the slave may be blocked waiting * for a flush on its stdout. */ - rfd[0].fd = slvpipe[READ_PIPE]; + rfd[0].fd = from_slave; rfd[0].events = POLLIN; rfd[1].fd = master; rfd[1].events = POLLIN; do { if (poll(rfd, 2, 4000) == 0) - errx(2, "%s, %zu: Command pipe read timeout", + errx(2, "%s:%zu: Command pipe read timeout", cur_file, line); if ((rfd[1].revents & POLLIN) == POLLIN) { @@ -1489,49 +2034,49 @@ save_slave_output(false); } } - while((rfd[1].revents & POLLIN) == POLLIN); + while ((rfd[1].revents & POLLIN) == POLLIN); - if (read(slvpipe[READ_PIPE], &type, sizeof(int)) < 0) + if (read(from_slave, &type, sizeof(int)) < 0) err(1, "command pipe read for type failed"); - response->return_type = type; + response->data_type = type; - if ((type != ret_ok) && (type != ret_err) && (type != ret_count)) { - if (read(slvpipe[READ_PIPE], &len, sizeof(int)) < 0) + if ((type != data_ok) && (type != data_err) && (type != data_count)) { + if (read(from_slave, &len, sizeof(int)) < 0) err(1, "command pipe read for length failed"); - response->return_len = len; + response->data_len = len; if (verbose) { fprintf(stderr, "Reading %d bytes from command pipe\n", len); } - if ((response->return_value = malloc(len + 1)) == NULL) + if ((response->data_value = malloc(len + 1)) == NULL) err(1, "Failed to alloc memory for cmd pipe read"); - if (read(slvpipe[READ_PIPE], response->return_value, len) < 0) + if (read(from_slave, response->data_value, len) < 0) err(1, "command pipe read of data failed"); - if (response->return_type != ret_byte) { - str = response->return_value; + if (response->data_type != data_byte) { + str = response->data_value; str[len] = '\0'; if (verbose) { fprintf(stderr, "Read data >%s< from pipe\n", - (const char *)response->return_value); + (const char *)response->data_value); } } } else { - response->return_value = NULL; - if (type == ret_count) { - if (read(slvpipe[READ_PIPE], &len, sizeof(int)) < 0) + response->data_value = NULL; + if (type == data_count) { + if (read(from_slave, &len, sizeof(int)) < 0) err(1, "command pipe read for number of " "returns failed"); - response->return_len = len; + response->data_len = len; } if (verbose) { fprintf(stderr, "Read type %s from pipe\n", - returns_enum_names[type]); + enum_names[type]); } } } @@ -1578,9 +2123,9 @@ } if (verbose) { - fprintf(stderr, "count = %zu, " - "allocated = %zu\n", saved_output.count, - saved_output.allocated); + fprintf(stderr, + "count = %zu, allocated = %zu\n", + saved_output.count, saved_output.allocated); for (i = 0; i < (size_t)result; i++) { fprintf(stderr, "Saving slave output " "at %zu: 0x%x (%c)\n", @@ -1594,9 +2139,9 @@ saved_output.count += result; if (verbose) { - fprintf(stderr, "count = %zu, " - "allocated = %zu\n", saved_output.count, - saved_output.allocated); + fprintf(stderr, + "count = %zu, allocated = %zu\n", + saved_output.count, saved_output.allocated); } } else { if (verbose) { @@ -1614,5 +2159,5 @@ static void yyerror(const char *msg) { - warnx("%s in line %zu of file %s", msg, line, cur_file); + errx(1, "%s:%zu: %s", cur_file, line, msg); } diff --git a/lib/libcurses/slave/command_table.h b/lib/libcurses/slave/command_table.h --- a/lib/libcurses/slave/command_table.h +++ b/lib/libcurses/slave/command_table.h @@ -1,7 +1,8 @@ -/* $NetBSD: command_table.h,v 1.3 2011/09/15 11:46:19 blymn Exp $ */ +/* $NetBSD: command_table.h,v 1.7 2021/02/13 08:14:46 rillig Exp $ */ /*- * Copyright 2009 Brett Lymn + * Copyright 2021 Roland Illig * * All rights reserved. * @@ -13,7 +14,7 @@ * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. The name of the author may not be used to endorse or promote products - * derived from this software withough specific prior written permission + * derived from this software without specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES @@ -25,19 +26,28 @@ * 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. - * - * */ -#ifndef _COMMAND_TABLE_H_ -#define _COMMAND_TABLE_H_ +#ifndef CTF_COMMAND_TABLE_H +#define CTF_COMMAND_TABLE_H #include "curses_commands.h" +static const char *restricted_commands[] = { + "filter", "ripoffline", "use_env", + "slk_init", "initscr", "newterm" +}; + +size_t nrcmds = sizeof(restricted_commands) / sizeof(char *); + /* * Curses commands */ struct command_def commands[] = { + {"filter", cmd_filter}, + {"ripoffline", cmd_ripoffline}, + {"use_env", cmd_use_env}, + {"slk_init", cmd_slk_init}, {"DRAIN", cmd_DRAIN}, {"addbytes", cmd_addbytes}, {"addch", cmd_addch}, @@ -151,6 +161,10 @@ {"getpary", cmd_getpary}, {"getparx", cmd_getparx}, {"getparyx", cmd_getparyx}, + {"getmaxyx", cmd_getmaxyx}, + {"getbegyx", cmd_getbegyx}, + {"setsyx", cmd_setsyx}, + {"getsyx", cmd_getsyx}, {"gettmode", cmd_gettmode}, {"getwin", cmd_getwin}, {"halfdelay", cmd_halfdelay}, @@ -169,9 +183,11 @@ {"is_wintouched", cmd_is_wintouched}, {"keyok", cmd_keyok}, {"keypad", cmd_keypad}, + {"is_keypad", cmd_is_keypad}, {"keyname", cmd_keyname}, {"killchar", cmd_killchar}, {"leaveok", cmd_leaveok}, + {"is_leaveok", cmd_is_leaveok}, {"meta", cmd_meta}, {"mvcur", cmd_mvcur}, {"mvderwin", cmd_mvderwin}, @@ -390,8 +406,28 @@ {"wbkgrnd", cmd_wbkgrnd}, {"wbkgrndset", cmd_wbkgrndset}, {"wgetbkgrnd", cmd_wgetbkgrnd}, + {"immedok", cmd_immedok}, + {"syncok", cmd_syncok}, + {"wcursyncup", cmd_wcursyncup}, + {"wsyncup", cmd_wsyncup}, + {"wsyncdown", cmd_wsyncdown}, + {"slk_attroff", cmd_slk_attroff}, + {"slk_attr_off", cmd_slk_attr_off}, + {"slk_attron", cmd_slk_attron}, + {"slk_attr_on", cmd_slk_attr_on}, + {"slk_attrset", cmd_slk_attrset}, + {"slk_attr_set", cmd_slk_attr_set}, + {"slk_clear", cmd_slk_clear}, + {"slk_color", cmd_slk_color}, + {"slk_label", cmd_slk_label}, + {"slk_noutrefresh", cmd_slk_noutrefresh}, + {"slk_refresh", cmd_slk_refresh}, + {"slk_restore", cmd_slk_restore}, + {"slk_set", cmd_slk_set}, + {"slk_touch", cmd_slk_touch}, + {"slk_wset", cmd_slk_wset}, }; size_t ncmds = sizeof(commands) / sizeof(struct command_def); -#endif /* _COMMAND_TABLE_H */ +#endif diff --git a/lib/libcurses/slave/commands.c b/lib/libcurses/slave/commands.c --- a/lib/libcurses/slave/commands.c +++ b/lib/libcurses/slave/commands.c @@ -1,7 +1,8 @@ -/* $NetBSD: commands.c,v 1.4 2011/09/15 11:46:19 blymn Exp $ */ +/* $NetBSD: commands.c,v 1.15 2021/06/13 12:46:01 rillig Exp $ */ /*- * Copyright 2009 Brett Lymn + * Copyright 2021 Roland Illig * * All rights reserved. * @@ -13,7 +14,7 @@ * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. The name of the author may not be used to endorse or promote products - * derived from this software withough specific prior written permission + * derived from this software without specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES @@ -25,8 +26,6 @@ * 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 @@ -36,15 +35,14 @@ #include #include #include + #include "returns.h" #include "slave.h" #include "command_table.h" -extern int cmdpipe[2]; -extern int slvpipe[2]; +extern int initdone; -static void report_type(returns_enum_t); -static void report_message(int, const char *); +static void report_message(data_enum_t, const char *); /* * Match the passed command string and execute the associated test @@ -53,11 +51,29 @@ void command_execute(char *func, int nargs, char **args) { - size_t i; + size_t i, j; i = 0; while (i < ncmds) { - if (strcasecmp(func, commands[i].name) == 0) { + if (strcmp(func, commands[i].name) == 0) { + /* Check only restricted set of functions is called before + * initscr/newterm */ + if (!initdone) { + j = 0; + while (j < nrcmds) { + if (strcmp(func, restricted_commands[j]) == 0) { + if (strcmp(func, "initscr") == 0 || + strcmp(func, "newterm") == 0) + initdone = 1; + /* matched function */ + commands[i].func(nargs, args); + return; + } + j++; + } + report_status("YOU NEED TO CALL INITSCR/NEWTERM FIRST"); + return; + } /* matched function */ commands[i].func(nargs, args); return; @@ -68,6 +84,29 @@ report_status("UNKNOWN_FUNCTION"); } +static void +write_to_director(const void *mem, size_t size) +{ + ssize_t nwritten = write(to_director, mem, size); + if (nwritten == -1) + err(1, "writing to director failed"); + if ((size_t)nwritten != size) + errx(1, "short write to director, expected %zu, got %zd", + size, nwritten); +} + +static void +write_to_director_int(int i) +{ + write_to_director(&i, sizeof(i)); +} + +static void +write_to_director_type(data_enum_t return_type) +{ + write_to_director_int(return_type); +} + /* * Report an pointer value back to the director */ @@ -104,41 +143,23 @@ report_return(int status) { if (status == ERR) - report_type(ret_err); + write_to_director_type(data_err); else if (status == OK) - report_type(ret_ok); + write_to_director_type(data_ok); + else if (status == KEY_CODE_YES) + report_int(status); else report_status("INVALID_RETURN"); } -/* - * Report the type back to the director via the command pipe - */ -static void -report_type(returns_enum_t return_type) -{ - int type; - - type = return_type; - if (write(slvpipe[WRITE_PIPE], &type, sizeof(int)) < 0) - err(1, "command pipe write for status type failed"); - -} - /* * Report the number of returns back to the director via the command pipe */ void report_count(int count) { - int type; - - type = ret_count; - if (write(slvpipe[WRITE_PIPE], &type, sizeof(int)) < 0) - err(1, "command pipe write for count type failed"); - - if (write(slvpipe[WRITE_PIPE], &count, sizeof(int)) < 0) - err(1, "command pipe write for count"); + write_to_director_type(data_count); + write_to_director_int(count); } /* @@ -147,7 +168,7 @@ void report_status(const char *status) { - report_message(ret_string, status); + report_message(data_string, status); } /* @@ -156,7 +177,7 @@ void report_error(const char *status) { - report_message(ret_slave_error, status); + report_message(data_slave_error, status); } /* @@ -164,20 +185,12 @@ * command pipe. */ static void -report_message(int type, const char *status) +report_message(data_enum_t type, const char *status) { - int len; - - len = strlen(status); - - if (write(slvpipe[WRITE_PIPE], &type, sizeof(int)) < 0) - err(1, "command pipe write for message type failed"); - - if (write(slvpipe[WRITE_PIPE], &len, sizeof(int)) < 0) - err(1, "command pipe write for message length failed"); - - if (write(slvpipe[WRITE_PIPE], status, len) < 0) - err(1, "command pipe write of message data failed"); + size_t len = strlen(status); + write_to_director_type(type); + write_to_director_int(len); + write_to_director(status, len); } /* @@ -199,31 +212,62 @@ void report_nstr(chtype *string) { - int len, type; + size_t size; chtype *p; - len = 0; - p = string; + for (p = string; (*p & __CHARTEXT) != 0; p++) + continue; - while ((*p++ & __CHARTEXT) != 0) { - len++; - } + size = (size_t)(p + 1 - string) * sizeof(*p); + + write_to_director_type(data_byte); + write_to_director_int(size); + write_to_director(string, size); +} + +/* + * Report a cchar_t back to the director via the command pipe. + */ +void +report_cchar(cchar_t c) +{ + + write_to_director_type(data_cchar); + write_to_director_int(sizeof(c)); + write_to_director(&c, sizeof(c)); +} - len++; /* add in the termination chtype */ - len *= sizeof(chtype); +/* + * Report a wchar_t back to the director via the command pipe. + */ +void +report_wchar(wchar_t ch) +{ + wchar_t wstr[2]; + + wstr[0] = ch; + wstr[1] = L'\0'; + report_wstr(wstr); +} + + +/* + * Report a string of wchar_t back to the director via the command pipe. + */ +void +report_wstr(wchar_t *wstr) +{ + size_t size; + wchar_t *p; - type = ret_byte; - if (write(slvpipe[WRITE_PIPE], &type, sizeof(int)) < 0) - err(1, "%s: command pipe write for status type failed", - __func__); + for (p = wstr; *p != L'\0'; p++) + continue; + size = (size_t)(p + 1 - wstr) * sizeof(*p); - if (write(slvpipe[WRITE_PIPE], &len, sizeof(int)) < 0) - err(1, "%s: command pipe write for status length failed", - __func__); - if (write(slvpipe[WRITE_PIPE], string, len) < 0) - err(1, "%s: command pipe write of status data failed", - __func__); + write_to_director_type(data_wchar); + write_to_director_int(size); + write_to_director(wstr, size); } /* @@ -236,8 +280,8 @@ if (nargs != expected) { report_count(1); report_error("INCORRECT_ARGUMENT_NUMBER"); - return(1); + return 1; } - return(0); + return 0; } diff --git a/lib/libcurses/slave/curses_commands.h b/lib/libcurses/slave/curses_commands.h --- a/lib/libcurses/slave/curses_commands.h +++ b/lib/libcurses/slave/curses_commands.h @@ -1,7 +1,8 @@ -/* $NetBSD: curses_commands.h,v 1.3 2011/09/15 11:46:19 blymn Exp $ */ +/* $NetBSD: curses_commands.h,v 1.8 2021/06/13 19:17:53 rillig Exp $ */ /*- * Copyright 2009 Brett Lymn + * Copyright 2021 Roland Illig * * All rights reserved. * @@ -13,7 +14,7 @@ * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. The name of the author may not be used to endorse or promote products - * derived from this software withough specific prior written permission + * derived from this software without specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES @@ -25,12 +26,10 @@ * 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. - * - * */ -#ifndef _CURSES_COMMANDS_H_ -#define _CURSES_COMMANDS_H_ +#ifndef CTF_CURSES_COMMANDS_H +#define CTF_CURSES_COMMANDS_H struct command_def { const char *name; @@ -40,8 +39,15 @@ /* * prototypes for test commands */ + void cmd_DRAIN(int, char **); /* not a curses function */ +/* These functions can be called before initscr*/ +void cmd_filter(int, char **); +void cmd_ripoffline(int, char **); +void cmd_use_env(int, char **); +void cmd_slk_init(int, char **); + void cmd_addbytes(int, char **); void cmd_addch(int, char **); void cmd_addchnstr(int, char **); @@ -156,6 +162,10 @@ void cmd_getpary(int, char **); void cmd_getparx(int, char **); void cmd_getparyx(int, char **); +void cmd_getmaxyx(int, char **); +void cmd_getbegyx(int, char **); +void cmd_setsyx(int, char **); +void cmd_getsyx(int, char **); void cmd_gettmode(int, char **); void cmd_getwin(int, char **); void cmd_halfdelay(int, char **); @@ -174,9 +184,11 @@ void cmd_is_wintouched(int, char **); void cmd_keyok(int, char **); void cmd_keypad(int, char **); +void cmd_is_keypad(int, char **); void cmd_keyname(int, char **); void cmd_killchar(int, char **); void cmd_leaveok(int, char **); +void cmd_is_leaveok(int, char **); void cmd_meta(int, char **); void cmd_mvchgat(int, char **); void cmd_mvcur(int, char **); @@ -305,10 +317,6 @@ void cmd_winsnstr(int, char **); void cmd_winsstr(int, char **); -void cmd_chgat(int, char **); -void cmd_wchgat(int, char **); -void cmd_mvchgat(int, char **); -void cmd_mvwchgat(int, char **); void cmd_add_wch(int, char **); void cmd_wadd_wch(int, char **); void cmd_mvadd_wch(int, char **); @@ -416,7 +424,27 @@ void cmd_wbkgrndset(int, char **); void cmd_wgetbkgrnd(int, char **); +void cmd_immedok(int, char **); +void cmd_syncok(int, char **); +void cmd_wcursyncup(int, char **); +void cmd_wsyncup(int, char **); +void cmd_wsyncdown(int, char **); +void cmd_slk_attroff(int, char**); +void cmd_slk_attr_off(int, char**); +void cmd_slk_attron(int, char**); +void cmd_slk_attr_on(int, char**); +void cmd_slk_attrset(int, char**); +void cmd_slk_attr_set(int, char**); +void cmd_slk_clear(int, char**); +void cmd_slk_color(int, char**); +void cmd_slk_label(int, char**); +void cmd_slk_noutrefresh(int, char**); +void cmd_slk_refresh(int, char**); +void cmd_slk_restore(int, char**); +void cmd_slk_set(int, char**); +void cmd_slk_touch(int, char**); +void cmd_slk_wset(int, char**); -#endif /* !_CURSES_COMMAND_H_ */ +#endif diff --git a/lib/libcurses/slave/curses_commands.c b/lib/libcurses/slave/curses_commands.c --- a/lib/libcurses/slave/curses_commands.c +++ b/lib/libcurses/slave/curses_commands.c @@ -1,7 +1,8 @@ -/* $NetBSD: curses_commands.c,v 1.7 2012/09/19 11:51:08 blymn Exp $ */ +/* $NetBSD: curses_commands.c,v 1.29 2021/06/13 21:54:51 rillig Exp $ */ /*- * Copyright 2009 Brett Lymn + * Copyright 2021 Roland Illig * * All rights reserved. * @@ -13,7 +14,7 @@ * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. The name of the author may not be used to endorse or promote products - * derived from this software withough specific prior written permission + * derived from this software without specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES @@ -25,8 +26,6 @@ * 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 @@ -39,10 +38,136 @@ #include "slave.h" #include "curses_commands.h" +static int +set_int(const char *arg, int *x) +{ + if (sscanf(arg, "%d", x) == 0) { + report_count(1); + report_error("BAD ARGUMENT"); + return -1; + } + + return 0; +} + +static int +set_uint(const char *arg, unsigned int *x) +{ + if (sscanf(arg, "%u", x) == 0) { + report_count(1); + report_error("BAD ARGUMENT"); + return -1; + } + + return 0; +} + +static int +set_short(const char *arg, short *x) +{ + if (sscanf(arg, "%hd", x) == 0) { + report_count(1); + report_error("BAD ARGUMENT"); + return -1; + } + + return 0; +} + +static int +set_win(const char *arg, WINDOW **x) +{ + if (sscanf(arg, "%p", x) == 0) { + report_count(1); + report_error("BAD ARGUMENT"); + return -1; + } + + return 0; +} + +static int +set_scrn(const char *arg, SCREEN **x) +{ + if (sscanf(arg, "%p", x) == 0) { + report_count(1); + report_error("BAD ARGUMENT"); + return -1; + } + + return 0; +} + +#define ARGC(n) \ + if (check_arg_count(nargs, n) == 1) \ + return + +#define ARG_SHORT(arg) \ + short arg; \ + if (set_short(*args++, &arg) != 0) \ + return + +#define ARG_INT(arg) \ + int arg; \ + if (set_int(*args++, &arg) != 0) \ + return + +#define ARG_UINT(arg) \ + unsigned int arg; \ + if (set_uint(*args++, &arg) != 0) \ + return + +#define ARG_CHTYPE(arg) \ + chtype arg = ((const chtype *)*args++)[0] + +#define ARG_WCHAR(arg) \ + wchar_t arg = ((const wchar_t *)*args++)[0] + +#define ARG_STRING(arg) \ + const char *arg = *args++ + +/* Only used for legacy interfaces that are missing the 'const'. */ +#define ARG_MODIFIABLE_STRING(arg) \ + char *arg = *args++ + +#define ARG_CHTYPE_STRING(arg) \ + const chtype *arg = (const chtype *)*args++ + +#define ARG_CCHAR_STRING(arg) \ + const cchar_t *arg = (const cchar_t *)*args++ + +#define ARG_WCHAR_STRING(arg) \ + wchar_t *arg = (wchar_t *)*args++ + +#define ARG_WINDOW(arg) \ + WINDOW *arg; \ + if (set_win(*args++, &arg) != 0) \ + return + +#define ARG_SCREEN(arg) \ + SCREEN *arg; \ + if (set_scrn(*args++, &arg) != 0) \ + return + +/* + * Required by the API, intended for future extensions, but this + * implementation does not support the extension. + */ +#define ARG_NULL() \ + args++ + +#define ARG_IGNORE() \ + args++ + void cmd_DRAIN(int nargs, char **args) { - while (getch() != ERR); + ARGC(1); + ARG_WINDOW(win); + + while (wgetch(win) != ERR) + continue; + report_count(1); report_return(OK); } @@ -50,93 +175,69 @@ void cmd_addbytes(int nargs, char **args) { - int count; - - if (check_arg_count(nargs, 2) == 1) - return; - - if (sscanf(args[1], "%d", &count) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(2); + ARG_STRING(str); + ARG_INT(count); report_count(1); - report_return(addbytes(args[0], count)); + report_return(addbytes(str, count)); } void cmd_addch(int nargs, char **args) { - chtype *ch; - - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(1); + ARG_CHTYPE(ch); - ch = (chtype *) args[0]; report_count(1); - report_return(addch(ch[0])); + report_return(addch(ch)); } void cmd_addchnstr(int nargs, char **args) { - int count; - - if (check_arg_count(nargs, 2) == 1) - return; - - if (sscanf(args[1], "%d", &count) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(2); + ARG_CHTYPE_STRING(chstr); + ARG_INT(count); report_count(1); - report_return(addchnstr((chtype *) args[0], count)); + report_return(addchnstr(chstr, count)); } void cmd_addchstr(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(1); + ARG_CHTYPE_STRING(chstr); report_count(1); - report_return(addchstr((chtype *) args[0])); + report_return(addchstr(chstr)); } void cmd_addnstr(int nargs, char **args) { - int count; - - if (check_arg_count(nargs, 2) == 1) - return; - - if (sscanf(args[1], "%d", &count) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(2); + ARG_STRING(str); + ARG_INT(count); report_count(1); - report_return(addnstr(args[0], count)); + report_return(addnstr(str, count)); } void cmd_addstr(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(1); + ARG_STRING(str); report_count(1); - report_return(addstr(args[0])); + report_return(addstr(str)); } @@ -147,12 +248,10 @@ short colours; int retval; - if (check_arg_count(nargs, 0) == 1) - return; + ARGC(0); retval = attr_get(&attrs, &colours, NULL); - /* XXXX - call3 */ report_count(3); report_return(retval); report_int(attrs); @@ -163,16 +262,8 @@ void cmd_attr_off(int nargs, char **args) { - int attrib; - - if (check_arg_count(nargs, 1) == 1) - return; - - if (sscanf(args[0], "%d", &attrib) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(1); + ARG_INT(attrib); report_count(1); report_return(attr_off(attrib, NULL)); @@ -182,16 +273,8 @@ void cmd_attr_on(int nargs, char **args) { - int attrib; - - if (check_arg_count(nargs, 1) == 1) - return; - - if (sscanf(args[0], "%d", &attrib) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(1); + ARG_INT(attrib); report_count(1); report_return(attr_on(attrib, NULL)); @@ -201,23 +284,9 @@ void cmd_attr_set(int nargs, char **args) { - int attrib; - short pair; - - if (check_arg_count(nargs, 2) == 1) - return; - - if (sscanf(args[0], "%d", &attrib) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%hd", &pair) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(2); + ARG_INT(attrib); + ARG_SHORT(pair); report_count(1); report_return(attr_set(attrib, pair, NULL)); @@ -227,16 +296,8 @@ void cmd_attroff(int nargs, char **args) { - int attrib; - - if (check_arg_count(nargs, 1) == 1) - return; - - if (sscanf(args[0], "%d", &attrib) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(1); + ARG_INT(attrib); report_count(1); report_return(attroff(attrib)); @@ -246,16 +307,8 @@ void cmd_attron(int nargs, char **args) { - int attrib; - - if (check_arg_count(nargs, 1) == 1) - return; - - if (sscanf(args[0], "%d", &attrib) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(1); + ARG_INT(attrib); report_count(1); report_return(attron(attrib)); @@ -265,16 +318,8 @@ void cmd_attrset(int nargs, char **args) { - int attrib; - - if (check_arg_count(nargs, 1) == 1) - return; - - if (sscanf(args[0], "%d", &attrib) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(1); + ARG_INT(attrib); report_count(1); report_return(attrset(attrib)); @@ -284,27 +329,21 @@ void cmd_bkgd(int nargs, char **args) { - chtype *ch; - - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(1); + ARG_CHTYPE(ch); - ch = (chtype *) args[0]; report_count(1); - report_return(bkgd(ch[0])); + report_return(bkgd(ch)); } void cmd_bkgdset(int nargs, char **args) { - chtype *ch; + ARGC(1); + ARG_CHTYPE(ch); - if (check_arg_count(nargs, 1) == 1) - return; - - ch = (chtype *) args[0]; - bkgdset(ch[0]); /* returns void */ + bkgdset(ch); /* returns void */ report_count(1); report_return(OK); } @@ -313,51 +352,15 @@ void cmd_border(int nargs, char **args) { - int ls, rs, ts, bs, tl, tr, bl, br; - - if (check_arg_count(nargs, 8) == 1) - return; - - if (sscanf(args[0], "%d", &ls) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - if (sscanf(args[1], "%d", &rs) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - if (sscanf(args[2], "%d", &ts) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - if (sscanf(args[3], "%d", &bs) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - if (sscanf(args[4], "%d", &tl) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - if (sscanf(args[5], "%d", &tr) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - if (sscanf(args[6], "%d", &bl) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - if (sscanf(args[7], "%d", &br) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(8); + ARG_INT(ls); + ARG_INT(rs); + ARG_INT(ts); + ARG_INT(bs); + ARG_INT(tl); + ARG_INT(tr); + ARG_INT(bl); + ARG_INT(br); report_count(1); report_return(border(ls, rs, ts, bs, tl, tr, bl, br)); @@ -367,8 +370,7 @@ void cmd_clear(int nargs, char **args) { - if (check_arg_count(nargs, 0) == 1) - return; + ARGC(0); report_count(1); report_return(clear()); @@ -378,8 +380,7 @@ void cmd_clrtobot(int nargs, char **args) { - if (check_arg_count(nargs, 0) == 1) - return; + ARGC(0); report_count(1); report_return(clrtobot()); @@ -389,8 +390,7 @@ void cmd_clrtoeol(int nargs, char **args) { - if (check_arg_count(nargs, 0) == 1) - return; + ARGC(0); report_count(1); report_return(clrtoeol()); @@ -400,16 +400,9 @@ void cmd_color_set(int nargs, char **args) { - short colour_pair; - - if (check_arg_count(nargs, 2) == 1) - return; - - if (sscanf(args[0], "%hd", &colour_pair) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(2); + ARG_SHORT(colour_pair); + ARG_NULL(); report_count(1); report_return(color_set(colour_pair, NULL)); @@ -419,8 +412,7 @@ void cmd_delch(int nargs, char **args) { - if (check_arg_count(nargs, 0) == 1) - return; + ARGC(0); report_count(1); report_return(delch()); @@ -430,8 +422,7 @@ void cmd_deleteln(int nargs, char **args) { - if (check_arg_count(nargs, 0) == 1) - return; + ARGC(0); report_count(1); report_return(deleteln()); @@ -441,20 +432,19 @@ void cmd_echochar(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(1); + ARG_CHTYPE(ch); /* XXX causes refresh */ report_count(1); - report_return(echochar(args[0][0])); + report_return(echochar(ch)); } void cmd_erase(int nargs, char **args) { - if (check_arg_count(nargs, 0) == 1) - return; + ARGC(0); report_count(1); report_return(erase()); @@ -464,8 +454,7 @@ void cmd_getch(int nargs, char **args) { - if (check_arg_count(nargs, 0) == 1) - return; + ARGC(0); /* XXX causes refresh */ report_count(1); @@ -476,17 +465,10 @@ void cmd_getnstr(int nargs, char **args) { - int limit; char *string; - if (check_arg_count(nargs, 1) == 1) - return; - - if (sscanf(args[0], "%d", &limit) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(1); + ARG_INT(limit); if ((string = malloc(limit + 1)) == NULL) { report_count(1); @@ -494,7 +476,6 @@ return; } - /* XXX call2 */ report_count(2); report_return(getnstr(string, limit)); report_status(string); @@ -507,10 +488,8 @@ { char string[256]; - if (check_arg_count(nargs, 0) == 1) - return; + ARGC(0); - /* XXX call2 */ report_count(2); report_return(getstr(string)); report_status(string); @@ -520,9 +499,7 @@ void cmd_inch(int nargs, char **args) { - if (check_arg_count(nargs, 0) == 1) - return; - + ARGC(0); report_count(1); report_byte(inch()); @@ -532,17 +509,10 @@ void cmd_inchnstr(int nargs, char **args) { - int limit; chtype *string; - if (check_arg_count(nargs, 1) == 1) - return; - - if (sscanf(args[0], "%d", &limit) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(1); + ARG_INT(limit); if ((string = malloc((limit + 1) * sizeof(chtype))) == NULL) { report_count(1); @@ -550,7 +520,6 @@ return; } - /* XXX call2 */ report_count(2); report_return(inchnstr(string, limit)); report_nstr(string); @@ -563,10 +532,8 @@ { chtype string[256]; - if (check_arg_count(nargs, 0) == 1) - return; + ARGC(0); - /* XXX call2 */ report_count(2); report_return(inchstr(string)); report_nstr(string); @@ -576,17 +543,10 @@ void cmd_innstr(int nargs, char **args) { - int limit; char *string; - if (check_arg_count(nargs, 1) == 1) - return; - - if (sscanf(args[0], "%d", &limit) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(1); + ARG_INT(limit); if ((string = malloc(limit + 1)) == NULL) { report_count(1); @@ -594,7 +554,6 @@ return; } - /* XXX call2 */ report_count(2); report_int(innstr(string, limit)); report_status(string); @@ -605,27 +564,19 @@ void cmd_insch(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(1); + ARG_CHTYPE(ch); report_count(1); - report_return(insch(args[0][0])); + report_return(insch(ch)); } void cmd_insdelln(int nargs, char **args) { - int nlines; - - if (check_arg_count(nargs, 1) == 1) - return; - - if (sscanf(args[0], "%d", &nlines) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(1); + ARG_INT(nlines); report_count(1); report_return(insdelln(nlines)); @@ -635,8 +586,7 @@ void cmd_insertln(int nargs, char **args) { - if (check_arg_count(nargs, 0) == 1) - return; + ARGC(0); report_count(1); report_return(insertln()); @@ -648,10 +598,8 @@ { char string[256]; - if (check_arg_count(nargs, 0) == 1) - return; + ARGC(0); - /* XXX call2 */ report_count(2); report_return(instr(string)); report_status(string); @@ -661,22 +609,9 @@ void cmd_move(int nargs, char **args) { - int y, x; - - if (check_arg_count(nargs, 2) == 1) - return; - - if (sscanf(args[0], "%d", &y) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &x) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(2); + ARG_INT(y); + ARG_INT(x); report_count(1); report_return(move(y, x)); @@ -686,8 +621,7 @@ void cmd_refresh(int nargs, char **args) { - if (check_arg_count(nargs, 0) == 1) - return; + ARGC(0); report_count(1); report_return(refresh()); @@ -697,16 +631,8 @@ void cmd_scrl(int nargs, char **args) { - int nlines; - - if (check_arg_count(nargs, 1) == 1) - return; - - if (sscanf(args[0], "%d", &nlines) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(1); + ARG_INT(nlines); report_count(1); report_return(scrl(nlines)); @@ -716,22 +642,9 @@ void cmd_setscrreg(int nargs, char **args) { - int top, bottom; - - if (check_arg_count(nargs, 2) == 1) - return; - - if (sscanf(args[0], "%d", &top) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &bottom) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(2); + ARG_INT(top); + ARG_INT(bottom); report_count(1); report_return(setscrreg(top, bottom)); @@ -741,40 +654,30 @@ void cmd_standend(int nargs, char **args) { - if (check_arg_count(nargs, 0) == 1) - return; + ARGC(0); report_count(1); - report_return(standend()); + report_int(standend()); } void cmd_standout(int nargs, char **args) { - if (check_arg_count(nargs, 0) == 1) - return; + ARGC(0); report_count(1); - report_return(standout()); + report_int(standout()); } void cmd_timeout(int nargs, char **args) { - int tval; - - if (check_arg_count(nargs, 1) == 1) - return; - - if (sscanf(args[0], "%d", &tval) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(1); + ARG_INT(tval); - timeout(tval); /* void return */ + timeout(tval); /* void return */ report_count(1); report_return(OK); } @@ -783,259 +686,135 @@ void cmd_underscore(int nargs, char **args) { - if (check_arg_count(nargs, 0) == 1) - return; + ARGC(0); report_count(1); - report_return(underscore()); + report_int(underscore()); } void cmd_underend(int nargs, char **args) { - if (check_arg_count(nargs, 0) == 1) - return; + ARGC(0); report_count(1); - report_return(underend()); + report_int(underend()); } void cmd_waddbytes(int nargs, char **args) { - WINDOW *win; - int count; - - if (check_arg_count(nargs, 3) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[2], "%d", &count) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(3); + ARG_WINDOW(win); + ARG_STRING(str); + ARG_INT(count); report_count(1); - report_return(waddbytes(win, args[1], count)); + report_return(waddbytes(win, str, count)); } void cmd_waddstr(int nargs, char **args) { - WINDOW *win; - - if (check_arg_count(nargs, 2) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(2); + ARG_WINDOW(win); + ARG_STRING(str); report_count(1); - report_return(waddstr(win, args[1])); + report_return(waddstr(win, str)); } void cmd_mvaddbytes(int nargs, char **args) { - int y, x, count; - - if (check_arg_count(nargs, 4) == 1) - return; - - if (sscanf(args[0], "%d", &y) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &x) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[3], "%d", &count) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(4); + ARG_INT(y); + ARG_INT(x); + ARG_STRING(str); + ARG_INT(count); report_count(1); - report_return(mvaddbytes(y, x, args[2], count)); + report_return(mvaddbytes(y, x, str, count)); } void cmd_mvaddch(int nargs, char **args) { - int y, x; - chtype *ch; - - if (check_arg_count(nargs, 3) == 1) - return; - - if (sscanf(args[0], "%d", &y) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &x) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(3); + ARG_INT(y); + ARG_INT(x); + ARG_CHTYPE(ch); - ch = (chtype *) args[2]; report_count(1); - report_return(mvaddch(y, x, ch[0])); + report_return(mvaddch(y, x, ch)); } void cmd_mvaddchnstr(int nargs, char **args) { - int y, x, count; - - if (check_arg_count(nargs, 4) == 1) - return; - - if (sscanf(args[0], "%d", &y) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &x) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[3], "%d", &count) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(4); + ARG_INT(y); + ARG_INT(x); + ARG_CHTYPE_STRING(chstr); + ARG_INT(count); report_count(1); - report_return(mvaddchnstr(y, x, (chtype *) args[2], count)); + report_return(mvaddchnstr(y, x, chstr, count)); } void cmd_mvaddchstr(int nargs, char **args) { - int y, x; - - if (check_arg_count(nargs, 3) == 1) - return; - - if (sscanf(args[0], "%d", &y) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &x) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(3); + ARG_INT(y); + ARG_INT(x); + ARG_CHTYPE_STRING(chstr); report_count(1); - report_return(mvaddchstr(y, x, (chtype *) args[2])); + report_return(mvaddchstr(y, x, chstr)); } void cmd_mvaddnstr(int nargs, char **args) { - int y, x, count; - - if (check_arg_count(nargs, 4) == 1) - return; - - if (sscanf(args[0], "%d", &y) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &x) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[3], "%d", &count) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(4); + ARG_INT(y); + ARG_INT(x); + ARG_STRING(str); + ARG_INT(count); report_count(1); - report_return(mvaddnstr(y, x, args[2], count)); + report_return(mvaddnstr(y, x, str, count)); } void cmd_mvaddstr(int nargs, char **args) { - int y, x; - - if (check_arg_count(nargs, 3) == 1) - return; - - if (sscanf(args[0], "%d", &y) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &x) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(3); + ARG_INT(y); + ARG_INT(x); + ARG_STRING(str); report_count(1); - report_return(mvaddstr(y, x, args[2])); + report_return(mvaddstr(y, x, str)); } void cmd_mvdelch(int nargs, char **args) { - int y, x; - - if (check_arg_count(nargs, 2) == 1) - return; - - if (sscanf(args[0], "%d", &y) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &x) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(2); + ARG_INT(y); + ARG_INT(x); report_count(1); report_return(mvdelch(y, x)); @@ -1045,22 +824,9 @@ void cmd_mvgetch(int nargs, char **args) { - int y, x; - - if (check_arg_count(nargs, 2) == 1) - return; - - if (sscanf(args[0], "%d", &y) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &x) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(2); + ARG_INT(y); + ARG_INT(x); report_count(1); report_int(mvgetch(y, x)); @@ -1070,29 +836,12 @@ void cmd_mvgetnstr(int nargs, char **args) { - int y, x, count; char *string; - if (check_arg_count(nargs, 3) == 1) - return; - - if (sscanf(args[0], "%d", &y) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &x) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[2], "%d", &count) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(3); + ARG_INT(y); + ARG_INT(x); + ARG_INT(count); if ((string = malloc(count + 1)) == NULL) { report_count(1); @@ -1100,7 +849,6 @@ return; } - /* XXX call2 */ report_count(2); report_return(mvgetnstr(y, x, string, count)); report_status(string); @@ -1111,25 +859,12 @@ void cmd_mvgetstr(int nargs, char **args) { - int y, x; char string[256]; - if (check_arg_count(nargs, 2) == 1) - return; - - if (sscanf(args[0], "%d", &y) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &x) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(2); + ARG_INT(y); + ARG_INT(x); - /* XXX call2 */ report_count(2); report_return(mvgetstr(y, x, string)); report_status(string); @@ -1139,54 +874,24 @@ void cmd_mvinch(int nargs, char **args) { - int y, x; - - if (check_arg_count(nargs, 2) == 1) - return; - - if (sscanf(args[0], "%d", &y) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &x) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(2); + ARG_INT(y); + ARG_INT(x); report_count(1); - report_int(mvinch(y, x)); + report_byte(mvinch(y, x)); } void cmd_mvinchnstr(int nargs, char **args) { - int y, x, count; chtype *string; - if (check_arg_count(nargs, 3) == 1) - return; - - if (sscanf(args[0], "%d", &y) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &x) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[2], "%d", &count) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(3); + ARG_INT(y); + ARG_INT(x); + ARG_INT(count); if ((string = malloc((count + 1) * sizeof(chtype))) == NULL) { report_count(1); @@ -1194,7 +899,6 @@ return; } - /* XXX call2 */ report_count(2); report_return(mvinchnstr(y, x, string, count)); report_nstr(string); @@ -1205,25 +909,12 @@ void cmd_mvinchstr(int nargs, char **args) { - int y, x; chtype string[256]; - if (check_arg_count(nargs, 2) == 1) - return; - - if (sscanf(args[0], "%d", &y) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &x) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(2); + ARG_INT(y); + ARG_INT(x); - /* XXX call2 */ report_count(2); report_return(mvinchstr(y, x, string)); report_nstr(string); @@ -1233,39 +924,21 @@ void cmd_mvinnstr(int nargs, char **args) { - int y, x, count; char *string; - if (check_arg_count(nargs, 3) == 1) - return; - - if (sscanf(args[0], "%d", &y) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &x) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[2], "%d", &count) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(3); + ARG_INT(y); + ARG_INT(x); + ARG_INT(count); if ((string = malloc(count + 1)) == NULL) { report_count(1); - report_error("MALLOC_FAILED"); + report_error("MALLOC_FAILED"); return; } - /* XXX call2 */ report_count(2); - report_return(mvinnstr(y, x, string, count)); + report_int(mvinnstr(y, x, string, count)); report_status(string); free(string); } @@ -1274,28 +947,10 @@ void cmd_mvinsch(int nargs, char **args) { - int y, x, ch; - - if (check_arg_count(nargs, 3) == 1) - return; - - if (sscanf(args[0], "%d", &y) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &x) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[2], "%d", &ch) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(3); + ARG_INT(y); + ARG_INT(x); + ARG_CHTYPE(ch); report_count(1); report_return(mvinsch(y, x, ch)); @@ -1305,265 +960,112 @@ void cmd_mvinstr(int nargs, char **args) { - int y, x; - - if (check_arg_count(nargs, 3) == 1) - return; - - if (sscanf(args[0], "%d", &y) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + char string[256]; - if (sscanf(args[1], "%d", &x) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(2); + ARG_INT(y); + ARG_INT(x); - report_count(1); - report_return(mvinstr(y, x, args[2])); + report_count(2); + report_return(mvinstr(y, x, string)); + report_status(string); } - void cmd_mvwaddbytes(int nargs, char **args) { - int y, x, count; - WINDOW *win; - - if (check_arg_count(nargs, 5) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &y) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[2], "%d", &x) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[4], "%d", &count) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(5); + ARG_WINDOW(win); + ARG_INT(y); + ARG_INT(x); + ARG_STRING(str); + ARG_INT(count); report_count(1); - report_return(mvwaddbytes(win, y, x, args[3], count)); + report_return(mvwaddbytes(win, y, x, str, count)); } void cmd_mvwaddch(int nargs, char **args) { - int y, x; - WINDOW *win; - - if (check_arg_count(nargs, 4) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &y) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[2], "%d", &x) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(4); + ARG_WINDOW(win); + ARG_INT(y); + ARG_INT(x); + ARG_CHTYPE(ch); report_count(1); - report_return(mvwaddch(win, y, x, args[3][0])); + report_return(mvwaddch(win, y, x, ch)); } void cmd_mvwaddchnstr(int nargs, char **args) { - int y, x, count; - WINDOW *win; - - if (check_arg_count(nargs, 5) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &y) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[2], "%d", &x) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[4], "%d", &count) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(5); + ARG_WINDOW(win); + ARG_INT(y); + ARG_INT(x); + ARG_CHTYPE_STRING(chstr); + ARG_INT(count); report_count(1); - report_return(mvwaddchnstr(win, y, x, (chtype *) args[3], count)); + report_return(mvwaddchnstr(win, y, x, chstr, count)); } void cmd_mvwaddchstr(int nargs, char **args) { - int y, x; - WINDOW *win; - - if (check_arg_count(nargs, 4) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &y) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[2], "%d", &x) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(4); + ARG_WINDOW(win); + ARG_INT(y); + ARG_INT(x); + ARG_CHTYPE_STRING(chstr); report_count(1); - report_return(mvwaddchstr(win, y, x, (chtype *) args[3])); + report_return(mvwaddchstr(win, y, x, chstr)); } void cmd_mvwaddnstr(int nargs, char **args) { - int y, x, count; - WINDOW *win; - - if (check_arg_count(nargs, 5) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &y) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[2], "%d", &x) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[4], "%d", &count) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(5); + ARG_WINDOW(win); + ARG_INT(y); + ARG_INT(x); + ARG_STRING(str); + ARG_INT(count); report_count(1); - report_return(mvwaddnstr(win, y, x, args[3], count)); + report_return(mvwaddnstr(win, y, x, str, count)); } void cmd_mvwaddstr(int nargs, char **args) { - int y, x; - WINDOW *win; - - if (check_arg_count(nargs, 4) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &y) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[2], "%d", &x) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(4); + ARG_WINDOW(win); + ARG_INT(y); + ARG_INT(x); + ARG_STRING(str); report_count(1); - report_return(mvwaddstr(win, y, x, args[3])); + report_return(mvwaddstr(win, y, x, str)); } void cmd_mvwdelch(int nargs, char **args) { - int y, x; - WINDOW *win; - - if (check_arg_count(nargs, 3) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &y) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[2], "%d", &x) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(3); + ARG_WINDOW(win); + ARG_INT(y); + ARG_INT(x); report_count(1); report_return(mvwdelch(win, y, x)); @@ -1573,29 +1075,10 @@ void cmd_mvwgetch(int nargs, char **args) { - int y, x; - WINDOW *win; - - if (check_arg_count(nargs, 3) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &y) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[2], "%d", &x) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(3); + ARG_WINDOW(win); + ARG_INT(y); + ARG_INT(x); /* XXX - implicit refresh */ report_count(1); @@ -1606,36 +1089,13 @@ void cmd_mvwgetnstr(int nargs, char **args) { - int y, x, count; char *string; - WINDOW *win; - - if (check_arg_count(nargs, 4) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &y) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[2], "%d", &x) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - if (sscanf(args[3], "%d", &count) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(4); + ARG_WINDOW(win); + ARG_INT(y); + ARG_INT(x); + ARG_INT(count); if ((string = malloc(count + 1)) == NULL) { report_count(1); @@ -1643,7 +1103,6 @@ return; } - /* XXX call2 */ report_count(2); report_return(mvwgetnstr(win, y, x, string, count)); report_status(string); @@ -1654,32 +1113,13 @@ void cmd_mvwgetstr(int nargs, char **args) { - int y, x; - WINDOW *win; char string[256]; - if (check_arg_count(nargs, 3) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &y) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[2], "%d", &x) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(3); + ARG_WINDOW(win); + ARG_INT(y); + ARG_INT(x); - /* XXX - call2 */ report_count(2); report_return(mvwgetstr(win, y, x, string)); report_status(string); @@ -1689,86 +1129,36 @@ void cmd_mvwinch(int nargs, char **args) { - int y, x; - WINDOW *win; - - if (check_arg_count(nargs, 3) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &y) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[2], "%d", &x) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(3); + ARG_WINDOW(win); + ARG_INT(y); + ARG_INT(x); report_count(1); - report_int(mvwinch(win, y, x)); + report_byte(mvwinch(win, y, x)); } void cmd_mvwinsch(int nargs, char **args) { - int y, x; - WINDOW *win; - - if (check_arg_count(nargs, 4) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &y) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[2], "%d", &x) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(4); + ARG_WINDOW(win); + ARG_INT(y); + ARG_INT(x); + ARG_CHTYPE(ch); report_count(1); - report_int(mvwinsch(win, y, x, args[3][0])); + report_return(mvwinsch(win, y, x, ch)); } void cmd_assume_default_colors(int nargs, char **args) { - short fore, back; - - if (check_arg_count(nargs, 2) == 1) - return; - - if (sscanf(args[0], "%hd", &fore) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%hd", &back) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(2); + ARG_SHORT(fore); + ARG_SHORT(back); report_count(1); report_return(assume_default_colors(fore, back)); @@ -1778,8 +1168,7 @@ void cmd_baudrate(int nargs, char **args) { - if (check_arg_count(nargs, 0) == 1) - return; + ARGC(0); report_count(1); report_int(baudrate()); @@ -1789,8 +1178,7 @@ void cmd_beep(int nargs, char **args) { - if (check_arg_count(nargs, 0) == 1) - return; + ARGC(0); report_count(1); report_return(beep()); @@ -1800,30 +1188,20 @@ void cmd_box(int nargs, char **args) { - WINDOW *win; - chtype *vertical, *horizontal; - - if (check_arg_count(nargs, 3) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(3); + ARG_WINDOW(win); + ARG_CHTYPE(vertical); + ARG_CHTYPE(horizontal); - vertical = (chtype *) args[1]; - horizontal = (chtype *) args[2]; report_count(1); - report_return(box(win, vertical[0], horizontal[0])); + report_return(box(win, vertical, horizontal)); } void cmd_can_change_color(int nargs, char **args) { - if (check_arg_count(nargs, 0) == 1) - return; + ARGC(0); report_count(1); report_int(can_change_color()); @@ -1833,8 +1211,7 @@ void cmd_cbreak(int nargs, char **args) { - if (check_arg_count(nargs, 0) == 1) - return; + ARGC(0); report_count(1); report_return(cbreak()); @@ -1844,23 +1221,9 @@ void cmd_clearok(int nargs, char **args) { - WINDOW *win; - int flag; - - if (check_arg_count(nargs, 2) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &flag) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(2); + ARG_WINDOW(win); + ARG_INT(flag); report_count(1); report_return(clearok(win, flag)); @@ -1870,20 +1233,14 @@ void cmd_color_content(int nargs, char **args) { - short colour, red, green, blue; - - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(1); + ARG_SHORT(colour); - if (sscanf(args[0], "%hd", &colour) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + short red, green, blue; + int ret = color_content(colour, &red, &green, &blue); - /* XXX - call4 */ report_count(4); - report_return(color_content(colour, &red, &green, &blue)); + report_return(ret); report_int(red); report_int(green); report_int(blue); @@ -1893,85 +1250,28 @@ void cmd_copywin(int nargs, char **args) { - int sminrow, smincol, dminrow, dmincol, dmaxrow, dmaxcol, ovlay; - WINDOW *source, *destination; - - if (check_arg_count(nargs, 9) == 1) - return; - - if (sscanf(args[0], "%p", &source) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%p", &destination) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[2], "%d", &sminrow) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[3], "%d", &smincol) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[4], "%d", &dminrow) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[5], "%d", &dmincol) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[6], "%d", &dmaxrow) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[7], "%d", &dmaxcol) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[8], "%d", &ovlay) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(9); + ARG_WINDOW(source); + ARG_WINDOW(destination); + ARG_INT(sminrow); + ARG_INT(smincol); + ARG_INT(dminrow); + ARG_INT(dmincol); + ARG_INT(dmaxrow); + ARG_INT(dmaxcol); + ARG_INT(ovlay); report_count(1); report_return(copywin(source, destination, sminrow, smincol, dminrow, - dmincol, dmaxrow, dmaxcol, ovlay)); + dmincol, dmaxrow, dmaxcol, ovlay)); } void cmd_curs_set(int nargs, char **args) { - int vis; - - if (check_arg_count(nargs, 1) == 1) - return; - - if (sscanf(args[0], "%d", &vis) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(1); + ARG_INT(vis); report_count(1); report_int(curs_set(vis)); @@ -1981,8 +1281,7 @@ void cmd_def_prog_mode(int nargs, char **args) { - if (check_arg_count(nargs, 0) == 1) - return; + ARGC(0); report_count(1); report_return(def_prog_mode()); @@ -1992,8 +1291,7 @@ void cmd_def_shell_mode(int nargs, char **args) { - if (check_arg_count(nargs, 0) == 1) - return; + ARGC(0); report_count(1); report_return(def_shell_mode()); @@ -2003,35 +1301,20 @@ void cmd_define_key(int nargs, char **args) { - int symbol; - - if (check_arg_count(nargs, 2) == 1) - return; - - if (sscanf(args[1], "%d", &symbol) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(2); + ARG_MODIFIABLE_STRING(sequence); + ARG_INT(symbol); report_count(1); - report_return(define_key(args[0], symbol)); + report_return(define_key(sequence, symbol)); } void cmd_delay_output(int nargs, char **args) { - int dtime; - - if (check_arg_count(nargs, 1) == 1) - return; - - if (sscanf(args[0], "%d", &dtime) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(1); + ARG_INT(dtime); report_count(1); report_return(delay_output(dtime)); @@ -2041,18 +1324,11 @@ void cmd_delscreen(int nargs, char **args) { - SCREEN *scrn; - - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(1); + ARG_SCREEN(scrn); - if (sscanf(args[0], "%p", &scrn) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + delscreen(scrn); /* void return */ - delscreen(scrn); /* void return */ report_count(1); report_return(OK); } @@ -2061,16 +1337,8 @@ void cmd_delwin(int nargs, char **args) { - WINDOW *win; - - if (check_arg_count(nargs, 1) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(1); + ARG_WINDOW(win); report_count(1); report_return(delwin(win)); @@ -2080,41 +1348,12 @@ void cmd_derwin(int nargs, char **args) { - int lines, cols, y, x; - WINDOW *win; - - if (check_arg_count(nargs, 5) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &lines) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[2], "%d", &cols) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[3], "%d", &y) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[4], "%d", &x) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(5); + ARG_WINDOW(win); + ARG_INT(lines); + ARG_INT(cols); + ARG_INT(y); + ARG_INT(x); report_count(1); report_ptr(derwin(win, lines, cols, y, x)); @@ -2124,16 +1363,8 @@ void cmd_dupwin(int nargs, char **args) { - WINDOW *win; - - if (check_arg_count(nargs, 1) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(1); + ARG_WINDOW(win); report_count(1); report_ptr(dupwin(win)); @@ -2143,8 +1374,7 @@ void cmd_doupdate(int nargs, char **args) { - if (check_arg_count(nargs, 0) == 1) - return; + ARGC(0); /* XXX - implicit refresh */ report_count(1); @@ -2155,8 +1385,7 @@ void cmd_echo(int nargs, char **args) { - if (check_arg_count(nargs, 0) == 1) - return; + ARGC(0); report_count(1); report_return(echo()); @@ -2166,8 +1395,7 @@ void cmd_endwin(int nargs, char **args) { - if (check_arg_count(nargs, 0) == 1) - return; + ARGC(0); report_count(1); report_return(endwin()); @@ -2177,8 +1405,7 @@ void cmd_erasechar(int nargs, char **args) { - if (check_arg_count(nargs, 0) == 1) - return; + ARGC(0); report_count(1); report_int(erasechar()); @@ -2188,8 +1415,7 @@ void cmd_flash(int nargs, char **args) { - if (check_arg_count(nargs, 0) == 1) - return; + ARGC(0); report_count(1); report_return(flash()); @@ -2199,8 +1425,7 @@ void cmd_flushinp(int nargs, char **args) { - if (check_arg_count(nargs, 0) == 1) - return; + ARGC(0); report_count(1); report_return(flushinp()); @@ -2210,23 +1435,9 @@ void cmd_flushok(int nargs, char **args) { - int flag; - WINDOW *win; - - if (check_arg_count(nargs, 2) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &flag) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(2); + ARG_WINDOW(win); + ARG_INT(flag); report_count(1); report_return(flushok(win, flag)); @@ -2238,12 +1449,11 @@ { char string[256]; - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(1); + ARG_STRING(termbuf); - /* XXX - call2 */ report_count(2); - report_status(fullname(args[0], string)); + report_status(fullname(termbuf, string)); report_status(string); } @@ -2251,16 +1461,8 @@ void cmd_getattrs(int nargs, char **args) { - WINDOW *win; - - if (check_arg_count(nargs, 1) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(1); + ARG_WINDOW(win); report_count(1); report_int(getattrs(win)); @@ -2270,16 +1472,8 @@ void cmd_getbkgd(int nargs, char **args) { - WINDOW *win; - - if (check_arg_count(nargs, 1) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(1); + ARG_WINDOW(win); report_count(1); report_byte(getbkgd(win)); @@ -2289,16 +1483,8 @@ void cmd_getcury(int nargs, char **args) { - WINDOW *win; - - if (check_arg_count(nargs, 1) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(1); + ARG_WINDOW(win); report_count(1); report_int(getcury(win)); @@ -2308,16 +1494,8 @@ void cmd_getcurx(int nargs, char **args) { - WINDOW *win; - - if (check_arg_count(nargs, 1) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(1); + ARG_WINDOW(win); report_count(1); report_int(getcurx(win)); @@ -2327,18 +1505,10 @@ void cmd_getyx(int nargs, char **args) { - WINDOW *win; - int y, x; - - if (check_arg_count(nargs, 1) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(1); + ARG_WINDOW(win); + int y, x; getyx(win, y, x); report_count(2); report_int(y); @@ -2349,16 +1519,8 @@ void cmd_getbegy(int nargs, char **args) { - WINDOW *win; - - if (check_arg_count(nargs, 1) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(1); + ARG_WINDOW(win); report_count(1); report_int(getbegy(win)); @@ -2368,16 +1530,8 @@ void cmd_getbegx(int nargs, char **args) { - WINDOW *win; - - if (check_arg_count(nargs, 1) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(1); + ARG_WINDOW(win); report_count(1); report_int(getbegx(win)); @@ -2387,16 +1541,8 @@ void cmd_getmaxy(int nargs, char **args) { - WINDOW *win; - - if (check_arg_count(nargs, 1) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(1); + ARG_WINDOW(win); report_count(1); report_int(getmaxy(win)); @@ -2406,16 +1552,8 @@ void cmd_getmaxx(int nargs, char **args) { - WINDOW *win; - - if (check_arg_count(nargs, 1) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(1); + ARG_WINDOW(win); report_count(1); report_int(getmaxx(win)); @@ -2425,16 +1563,8 @@ void cmd_getpary(int nargs, char **args) { - WINDOW *win; - - if (check_arg_count(nargs, 1) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(1); + ARG_WINDOW(win); report_count(1); report_int(getpary(win)); @@ -2444,16 +1574,8 @@ void cmd_getparx(int nargs, char **args) { - WINDOW *win; - - if (check_arg_count(nargs, 1) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(1); + ARG_WINDOW(win); report_count(1); report_int(getparx(win)); @@ -2463,69 +1585,104 @@ void cmd_getparyx(int nargs, char **args) { - WINDOW *win; - int y, x; - - if (check_arg_count(nargs, 1) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(1); + ARG_WINDOW(win); + int y, x; report_count(2); getparyx(win, y, x); report_int(y); report_int(x); } - void -cmd_gettmode(int nargs, char **args) +cmd_getmaxyx(int nargs, char **args) { - if (check_arg_count(nargs, 0) == 1) - return; + ARGC(1); + ARG_WINDOW(win); - report_count(1); - report_return(gettmode()); -} + int y, x; + getmaxyx(win, y, x); + report_count(2); + report_int(y); + report_int(x); +} void -cmd_getwin(int nargs, char **args) +cmd_getbegyx(int nargs, char **args) { - FILE *fp; + ARGC(1); + ARG_WINDOW(win); - if (check_arg_count(nargs, 1) == 1) - return; + int y, x; + getbegyx(win, y, x); - if ((fp = fopen(args[0], "r")) == NULL) { - report_count(1); - report_error("BAD FILE_ARGUMENT"); - return; - } + report_count(2); + report_int(y); + report_int(x); +} + +void +cmd_setsyx(int nargs, char **args) +{ + ARGC(2); + ARG_INT(y); + ARG_INT(x); report_count(1); - report_ptr(getwin(fp)); - fclose(fp); + setsyx(y, x); + report_return(OK); } +void +cmd_getsyx(int nargs, char **args) +{ + int y, x; + + ARGC(0); + + report_count(3); + getsyx(y, x); + report_return(OK); + report_int(y); + report_int(x); +} void -cmd_halfdelay(int nargs, char **args) +cmd_gettmode(int nargs, char **args) { - int ms; + ARGC(0); - if (check_arg_count(nargs, 1) == 1) - return; + report_count(1); + report_return(gettmode()); +} + + +void +cmd_getwin(int nargs, char **args) +{ + FILE *fp; - if (sscanf(args[0], "%d", &ms) == 0) { + ARGC(1); + ARG_STRING(filename); + + if ((fp = fopen(filename, "r")) == NULL) { report_count(1); - report_error("BAD ARGUMENT"); + report_error("BAD FILE_ARGUMENT"); return; } + report_count(1); + report_ptr(getwin(fp)); + fclose(fp); +} + + +void +cmd_halfdelay(int nargs, char **args) +{ + ARGC(1); + ARG_INT(ms); report_count(1); report_return(halfdelay(ms)); @@ -2535,8 +1692,7 @@ void cmd_has_colors(int nargs, char **args) { - if (check_arg_count(nargs, 0) == 1) - return; + ARGC(0); report_count(1); report_int(has_colors()); @@ -2546,8 +1702,7 @@ void cmd_has_ic(int nargs, char **args) { - if (check_arg_count(nargs, 0) == 1) - return; + ARGC(0); report_count(1); report_int(has_ic()); @@ -2557,8 +1712,7 @@ void cmd_has_il(int nargs, char **args) { - if (check_arg_count(nargs, 0) == 1) - return; + ARGC(0); report_count(1); report_int(has_il()); @@ -2568,45 +1722,21 @@ void cmd_hline(int nargs, char **args) { - int count; - chtype *ch; - - if (check_arg_count(nargs, 2) == 1) - return; - - ch = (chtype *) args[0]; - - if (sscanf(args[1], "%d", &count) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(2); + ARG_CHTYPE(ch); + ARG_INT(count); report_count(1); - report_return(hline(ch[0], count)); + report_return(hline(ch, count)); } void cmd_idcok(int nargs, char **args) { - int flag; - WINDOW *win; - - if (check_arg_count(nargs, 2) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &flag) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(2); + ARG_WINDOW(win); + ARG_INT(flag); report_count(1); report_return(idcok(win, flag)); @@ -2616,23 +1746,9 @@ void cmd_idlok(int nargs, char **args) { - int flag; - WINDOW *win; - - if (check_arg_count(nargs, 2) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &flag) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(2); + ARG_WINDOW(win); + ARG_INT(flag); report_count(1); report_return(idlok(win, flag)); @@ -2642,34 +1758,11 @@ void cmd_init_color(int nargs, char **args) { - short colour, red, green, blue; - - if (check_arg_count(nargs, 4) == 1) - return; - - if (sscanf(args[0], "%hd", &colour) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%hd", &red) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[2], "%hd", &green) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[3], "%hd", &blue) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(4); + ARG_SHORT(colour); + ARG_SHORT(red); + ARG_SHORT(green); + ARG_SHORT(blue); report_count(1); report_return(init_color(colour, red, green, blue)); @@ -2679,28 +1772,10 @@ void cmd_init_pair(int nargs, char **args) { - short pair, fore, back; - - if (check_arg_count(nargs, 3) == 1) - return; - - if (sscanf(args[0], "%hd", &pair) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%hd", &fore) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[2], "%hd", &back) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(3); + ARG_SHORT(pair); + ARG_SHORT(fore); + ARG_SHORT(back); report_count(1); report_return(init_pair(pair, fore, back)); @@ -2710,8 +1785,7 @@ void cmd_initscr(int nargs, char **args) { - if (check_arg_count(nargs, 0) == 1) - return; + ARGC(0); report_count(1); report_ptr(initscr()); @@ -2721,23 +1795,9 @@ void cmd_intrflush(int nargs, char **args) { - int flag; - WINDOW *win; - - if (check_arg_count(nargs, 2) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &flag) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(2); + ARG_WINDOW(win); + ARG_INT(flag); report_count(1); report_return(intrflush(win, flag)); @@ -2747,8 +1807,7 @@ void cmd_isendwin(int nargs, char **args) { - if (check_arg_count(nargs, 0) == 1) - return; + ARGC(0); report_count(1); report_int(isendwin()); @@ -2758,23 +1817,9 @@ void cmd_is_linetouched(int nargs, char **args) { - int line; - WINDOW *win; - - if (check_arg_count(nargs, 2) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &line) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(2); + ARG_WINDOW(win); + ARG_INT(line); report_count(1); report_int(is_linetouched(win, line)); @@ -2784,16 +1829,8 @@ void cmd_is_wintouched(int nargs, char **args) { - WINDOW *win; - - if (check_arg_count(nargs, 1) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(1); + ARG_WINDOW(win); report_count(1); report_int(is_wintouched(win)); @@ -2803,22 +1840,9 @@ void cmd_keyok(int nargs, char **args) { - int keysym, flag; - - if (check_arg_count(nargs, 2) == 1) - return; - - if (sscanf(args[0], "%d", &keysym) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &flag) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(2); + ARG_INT(keysym); + ARG_INT(flag); report_count(1); report_return(keyok(keysym, flag)); @@ -2828,42 +1852,29 @@ void cmd_keypad(int nargs, char **args) { - int flag; - WINDOW *win; - - if (check_arg_count(nargs, 2) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &flag) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(2); + ARG_WINDOW(win); + ARG_INT(flag); report_count(1); report_return(keypad(win, flag)); } - void -cmd_keyname(int nargs, char **args) +cmd_is_keypad(int nargs, char **args) { - unsigned int key; + ARGC(1); + ARG_WINDOW(win); - if (check_arg_count(nargs, 1) == 1) - return; + report_count(1); + report_int(is_keypad(win)); +} - if (sscanf(args[0], "%d", &key) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } +void +cmd_keyname(int nargs, char **args) +{ + ARGC(1); + ARG_UINT(key); report_count(1); report_status(keyname(key)); @@ -2873,8 +1884,7 @@ void cmd_killchar(int nargs, char **args) { - if (check_arg_count(nargs, 0) == 1) - return; + ARGC(0); report_count(1); report_int(killchar()); @@ -2884,49 +1894,30 @@ void cmd_leaveok(int nargs, char **args) { - int flag; - WINDOW *win; - - if (check_arg_count(nargs, 2) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &flag) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(2); + ARG_WINDOW(win); + ARG_INT(flag); report_count(1); report_return(leaveok(win, flag)); } - void -cmd_meta(int nargs, char **args) +cmd_is_leaveok(int nargs, char **args) { - int flag; - WINDOW *win; - - if (check_arg_count(nargs, 2) == 1) - return; + ARGC(1); + ARG_WINDOW(win); - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + report_count(1); + report_int(is_leaveok(win)); +} - if (sscanf(args[1], "%d", &flag) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } +void +cmd_meta(int nargs, char **args) +{ + ARGC(2); + ARG_WINDOW(win); + ARG_INT(flag); report_count(1); report_return(meta(win, flag)); @@ -2936,231 +1927,111 @@ void cmd_mvcur(int nargs, char **args) { - int oldy, oldx, y, x; + ARGC(4); + ARG_INT(oldy); + ARG_INT(oldx); + ARG_INT(y); + ARG_INT(x); - if (check_arg_count(nargs, 4) == 1) - return; + report_count(1); + report_return(mvcur(oldy, oldx, y, x)); +} - if (sscanf(args[0], "%d", &oldy) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - if (sscanf(args[1], "%d", &oldx) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } +void +cmd_mvderwin(int nargs, char **args) +{ + ARGC(3); + ARG_WINDOW(win); + ARG_INT(y); + ARG_INT(x); - if (sscanf(args[2], "%d", &y) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + report_count(1); + report_return(mvderwin(win, y, x)); +} - if (sscanf(args[3], "%d", &x) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + +void +cmd_mvhline(int nargs, char **args) +{ + ARGC(4); + ARG_INT(y); + ARG_INT(x); + ARG_CHTYPE(ch); + ARG_INT(n); report_count(1); - report_return(mvcur(oldy, oldx, y, x)); + report_return(mvhline(y, x, ch, n)); } void -cmd_mvderwin(int nargs, char **args) +cmd_mvprintw(int nargs, char **args) { - int y, x; - WINDOW *win; + ARGC(4); + ARG_INT(y); + ARG_INT(x); + ARG_STRING(fmt); /* Must have a single "%s" in this test. */ + ARG_STRING(arg); - if (check_arg_count(nargs, 3) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &y) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[2], "%d", &x) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - report_count(1); - report_return(mvderwin(win, y, x)); -} - - -void -cmd_mvhline(int nargs, char **args) -{ - int y, x, n; - chtype *ch; - - if (check_arg_count(nargs, 4) == 1) - return; - - if (sscanf(args[0], "%d", &y) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &x) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - ch = (chtype *) args[2]; - - if (sscanf(args[3], "%d", &n) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - report_count(1); - report_return(mvhline(y, x, ch[0], n)); -} - - -void -cmd_mvprintw(int nargs, char **args) -{ - int y, x; - - if (check_arg_count(nargs, 4) == 1) - return; - - if (sscanf(args[0], "%d", &y) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &x) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - report_count(1); - report_return(mvprintw(y, x, args[2], args[3])); -} + report_count(1); + report_return(mvprintw(y, x, fmt, arg)); +} void cmd_mvscanw(int nargs, char **args) { - int y, x; + int ret; char string[256]; - if (check_arg_count(nargs, 3) == 1) - return; - - if (sscanf(args[0], "%d", &y) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &x) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(3); + ARG_INT(y); + ARG_INT(x); + ARG_STRING(fmt); - /* XXX - call2 */ report_count(2); - report_return(mvscanw(y, x, args[2], &string)); - report_status(string); + if (strchr(fmt, 's') != NULL) { + report_return(ret = mvscanw(y, x, fmt, string)); + } else { + int val; /* XXX assume 32-bit integer */ + report_return(ret = mvscanw(y, x, fmt, &val)); + if (ret == ERR) + goto out; + snprintf(string, sizeof(string), fmt, val); + } +out: + /* + * When mvscanw(3) fails, string is not modified. + * Let's ignore the 2nd result for this case. + */ + report_status(ret == ERR ? "ERR" : string); } void cmd_mvvline(int nargs, char **args) { - int y, x, n; - chtype *ch; - - if (check_arg_count(nargs, 4) == 1) - return; - - if (sscanf(args[0], "%d", &y) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &x) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - ch = (chtype *) args[2]; - - if (sscanf(args[3], "%d", &n) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(4); + ARG_INT(y); + ARG_INT(x); + ARG_CHTYPE(ch); + ARG_INT(n); report_count(1); - report_return(mvvline(y, x, ch[0], n)); + report_return(mvvline(y, x, ch, n)); } void cmd_mvwhline(int nargs, char **args) { - int y, x, ch, n; - WINDOW *win; - - if (check_arg_count(nargs, 5) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &y) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[2], "%d", &x) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[3], "%d", &ch) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[4], "%d", &n) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(5); + ARG_WINDOW(win); + ARG_INT(y); + ARG_INT(x); + ARG_CHTYPE(ch); + ARG_INT(n); report_count(1); report_return(mvwhline(win, y, x, ch, n)); @@ -3170,70 +2041,25 @@ void cmd_mvwvline(int nargs, char **args) { - int y, x, n; - WINDOW *win; - chtype *ch; - - if (check_arg_count(nargs, 5) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &y) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[2], "%d", &x) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - ch = (chtype *) args[3]; - - if (sscanf(args[4], "%d", &n) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(5); + ARG_WINDOW(win); + ARG_INT(y); + ARG_INT(x); + ARG_CHTYPE(ch); + ARG_INT(n); report_count(1); - report_return(mvwvline(win, y, x, ch[0], n)); + report_return(mvwvline(win, y, x, ch, n)); } void cmd_mvwin(int nargs, char **args) { - int y, x; - WINDOW *win; - - if (check_arg_count(nargs, 3) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &y) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[2], "%d", &x) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(3); + ARG_WINDOW(win); + ARG_INT(y); + ARG_INT(x); report_count(1); report_return(mvwin(win, y, x)); @@ -3243,36 +2069,13 @@ void cmd_mvwinchnstr(int nargs, char **args) { - int y, x, count; chtype *string; - WINDOW *win; - - if (check_arg_count(nargs, 4) == 1) - return; - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &y) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[2], "%d", &x) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[3], "%d", &count) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(4); + ARG_WINDOW(win); + ARG_INT(y); + ARG_INT(x); + ARG_INT(count); if ((string = malloc((count + 1) * sizeof(chtype))) == NULL) { report_count(1); @@ -3280,7 +2083,6 @@ return; } - /* XXX call2 */ report_count(2); report_return(mvwinchnstr(win, y, x, string, count)); report_nstr(string); @@ -3291,32 +2093,13 @@ void cmd_mvwinchstr(int nargs, char **args) { - int y, x; chtype string[256]; - WINDOW *win; - - if (check_arg_count(nargs, 3) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &y) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - if (sscanf(args[2], "%d", &x) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(3); + ARG_WINDOW(win); + ARG_INT(y); + ARG_INT(x); - /* XXX call2 */ report_count(2); report_return(mvwinchstr(win, y, x, string)); report_nstr(string); @@ -3326,36 +2109,13 @@ void cmd_mvwinnstr(int nargs, char **args) { - int y, x, count; char *string; - WINDOW *win; - - if (check_arg_count(nargs, 4) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &y) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - if (sscanf(args[2], "%d", &x) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[3], "%d", &count) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(4); + ARG_WINDOW(win); + ARG_INT(y); + ARG_INT(x); + ARG_INT(count); if ((string = malloc(count + 1)) == NULL) { report_count(1); @@ -3363,9 +2123,8 @@ return; } - /* XXX call2 */ report_count(2); - report_return(mvwinnstr(win, y, x, string, count)); + report_int(mvwinnstr(win, y, x, string, count)); report_status(string); free(string); } @@ -3374,32 +2133,13 @@ void cmd_mvwinstr(int nargs, char **args) { - int y, x; char string[256]; - WINDOW *win; - - if (check_arg_count(nargs, 3) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - if (sscanf(args[1], "%d", &y) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(3); + ARG_WINDOW(win); + ARG_INT(y); + ARG_INT(x); - if (sscanf(args[2], "%d", &x) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - /* XXX call2 */ report_count(2); report_return(mvwinstr(win, y, x, string)); report_status(string); @@ -3409,66 +2149,31 @@ void cmd_mvwprintw(int nargs, char **args) { - int y, x; - WINDOW *win; - - if (check_arg_count(nargs, 5) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &y) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[2], "%d", &x) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(5); + ARG_WINDOW(win); + ARG_INT(y); + ARG_INT(x); + ARG_STRING(fmt); /* Must have a single "%s" in this test. */ + ARG_STRING(arg); report_count(1); - report_return(mvwprintw(win, y, x, args[3], args[4])); + report_return(mvwprintw(win, y, x, fmt, arg)); } void cmd_mvwscanw(int nargs, char **args) { - int y, x; - WINDOW *win; char string[256]; - if (check_arg_count(nargs, 4) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &y) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[2], "%d", &x) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(4); + ARG_WINDOW(win); + ARG_INT(y); + ARG_INT(x); + ARG_STRING(fmt); /* Must have a single "%s" in this test. */ - /* XXX - call2 */ report_count(2); - report_int(mvwscanw(win, y, x, args[3], &string)); + report_int(mvwscanw(win, y, x, fmt, &string)); report_status(string); } @@ -3476,16 +2181,8 @@ void cmd_napms(int nargs, char **args) { - int naptime; - - if (check_arg_count(nargs, 1) == 1) - return; - - if (sscanf(args[0], "%d", &naptime) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(1); + ARG_INT(naptime); report_count(1); report_return(napms(naptime)); @@ -3495,22 +2192,9 @@ void cmd_newpad(int nargs, char **args) { - int y, x; - - if (check_arg_count(nargs, 2) == 1) - return; - - if (sscanf(args[0], "%d", &y) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &x) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(2); + ARG_INT(y); + ARG_INT(x); report_count(1); report_ptr(newpad(y, x)); @@ -3522,58 +2206,35 @@ { FILE *in, *out; - if (check_arg_count(nargs, 3) == 1) - return; + ARGC(3); + ARG_MODIFIABLE_STRING(type); + ARG_STRING(in_fname); + ARG_STRING(out_fname); - if ((in = fopen(args[1], "rw")) == NULL) { + if ((in = fopen(in_fname, "rw")) == NULL) { report_count(1); report_error("BAD FILE_ARGUMENT"); return; } - - - if ((out = fopen(args[2], "rw")) == NULL) { + if ((out = fopen(out_fname, "rw")) == NULL) { report_count(1); report_error("BAD FILE_ARGUMENT"); return; } report_count(1); - report_ptr(newterm(args[0], out, in)); + report_ptr(newterm(type, out, in)); } void cmd_newwin(int nargs, char **args) { - int lines, cols, begin_y, begin_x; - - if (check_arg_count(nargs, 4) == 1) - return; - - if (sscanf(args[0], "%d", &lines) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &cols) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[2], "%d", &begin_y) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[3], "%d", &begin_x) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(4); + ARG_INT(lines); + ARG_INT(cols); + ARG_INT(begin_y); + ARG_INT(begin_x); report_count(1); report_ptr(newwin(lines, cols, begin_y, begin_x)); @@ -3583,8 +2244,7 @@ void cmd_nl(int nargs, char **args) { - if (check_arg_count(nargs, 0) == 1) - return; + ARGC(0); report_count(1); report_return(nl()); @@ -3594,8 +2254,7 @@ void cmd_no_color_attributes(int nargs, char **args) { - if (check_arg_count(nargs, 0) == 1) - return; + ARGC(0); report_count(1); report_int(no_color_attributes()); @@ -3605,8 +2264,7 @@ void cmd_nocbreak(int nargs, char **args) { - if (check_arg_count(nargs, 0) == 1) - return; + ARGC(0); report_count(1); report_return(nocbreak()); @@ -3616,23 +2274,9 @@ void cmd_nodelay(int nargs, char **args) { - int flag; - WINDOW *win; - - if (check_arg_count(nargs, 2) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &flag) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(2); + ARG_WINDOW(win); + ARG_INT(flag); report_count(1); report_return(nodelay(win, flag)); @@ -3642,8 +2286,7 @@ void cmd_noecho(int nargs, char **args) { - if (check_arg_count(nargs, 0) == 1) - return; + ARGC(0); report_count(1); report_return(noecho()); @@ -3653,8 +2296,7 @@ void cmd_nonl(int nargs, char **args) { - if (check_arg_count(nargs, 0) == 1) - return; + ARGC(0); report_count(1); report_return(nonl()); @@ -3664,20 +2306,18 @@ void cmd_noqiflush(int nargs, char **args) { - if (check_arg_count(nargs, 0) == 1) - return; + ARGC(0); noqiflush(); report_count(1); - report_return(OK); /* fake a return, the call returns void */ + report_return(OK); /* fake a return, the call returns void */ } void cmd_noraw(int nargs, char **args) { - if (check_arg_count(nargs, 0) == 1) - return; + ARGC(0); report_count(1); report_return(noraw()); @@ -3687,23 +2327,9 @@ void cmd_notimeout(int nargs, char **args) { - int flag; - WINDOW *win; - - if (check_arg_count(nargs, 2) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &flag) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(2); + ARG_WINDOW(win); + ARG_INT(flag); report_count(1); report_return(notimeout(win, flag)); @@ -3713,22 +2339,9 @@ void cmd_overlay(int nargs, char **args) { - WINDOW *source, *dest; - - if (check_arg_count(nargs, 2) == 1) - return; - - if (sscanf(args[0], "%p", &source) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%p", &dest) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(2); + ARG_WINDOW(source); + ARG_WINDOW(dest); report_count(1); report_return(overlay(source, dest)); @@ -3738,22 +2351,9 @@ void cmd_overwrite(int nargs, char **args) { - WINDOW *source, *dest; - - if (check_arg_count(nargs, 2) == 1) - return; - - if (sscanf(args[0], "%p", &source) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%p", &dest) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(2); + ARG_WINDOW(source); + ARG_WINDOW(dest); report_count(1); report_return(overwrite(source, dest)); @@ -3763,20 +2363,14 @@ void cmd_pair_content(int nargs, char **args) { - short pair, fore, back; + ARGC(1); + ARG_SHORT(pair); - if (check_arg_count(nargs, 1) == 1) - return; - - if (sscanf(args[0], "%hd", &pair) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + short fore, back; + int ret = pair_content(pair, &fore, &back); - /* XXX - call3 */ report_count(3); - report_return(pair_content(pair, &fore, &back)); + report_return(ret); report_int(fore); report_int(back); } @@ -3785,23 +2379,9 @@ void cmd_pechochar(int nargs, char **args) { - int ch; - WINDOW *pad; - - if (check_arg_count(nargs, 2) == 1) - return; - - if (sscanf(args[0], "%p", &pad) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &ch) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(2); + ARG_WINDOW(pad); + ARG_CHTYPE(ch); report_count(1); report_return(pechochar(pad, ch)); @@ -3811,147 +2391,61 @@ void cmd_pnoutrefresh(int nargs, char **args) { - int pbeg_y, pbeg_x, sbeg_y, sbeg_x, smax_y, smax_x; - WINDOW *pad; - - if (check_arg_count(nargs, 7) == 1) - return; - - if (sscanf(args[0], "%p", &pad) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &pbeg_y) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[2], "%d", &pbeg_x) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[3], "%d", &sbeg_y) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[4], "%d", &sbeg_x) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[5], "%d", &smax_y) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[6], "%d", &smax_x) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(7); + ARG_WINDOW(pad); + ARG_INT(pbeg_y); + ARG_INT(pbeg_x); + ARG_INT(sbeg_y); + ARG_INT(sbeg_x); + ARG_INT(smax_y); + ARG_INT(smax_x); report_count(1); report_return(pnoutrefresh(pad, pbeg_y, pbeg_x, sbeg_y, sbeg_x, smax_y, - smax_x)); + smax_x)); } void cmd_prefresh(int nargs, char **args) { - int pbeg_y, pbeg_x, sbeg_y, sbeg_x, smax_y, smax_x; - WINDOW *pad; - - if (check_arg_count(nargs, 7) == 1) - return; - - if (sscanf(args[0], "%p", &pad) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &pbeg_y) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[2], "%d", &pbeg_x) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[3], "%d", &sbeg_y) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[4], "%d", &sbeg_x) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[5], "%d", &smax_y) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[6], "%d", &smax_x) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(7); + ARG_WINDOW(pad); + ARG_INT(pbeg_y); + ARG_INT(pbeg_x); + ARG_INT(sbeg_y); + ARG_INT(sbeg_x); + ARG_INT(smax_y); + ARG_INT(smax_x); /* XXX causes refresh */ report_count(1); report_return(prefresh(pad, pbeg_y, pbeg_x, sbeg_y, sbeg_x, smax_y, - smax_x)); - + smax_x)); } void cmd_printw(int nargs, char **args) { - if (check_arg_count(nargs, 2) == 1) - return; - + ARGC(2); + ARG_STRING(fmt); /* Must have a single "%s" in this test. */ + ARG_STRING(arg); report_count(1); - report_return(printw(args[0], args[1])); + report_return(printw(fmt, arg)); } void cmd_putwin(int nargs, char **args) { - FILE *fp; - WINDOW *win; - - if (check_arg_count(nargs, 2) == 1) - return; + ARGC(2); + ARG_WINDOW(win); + ARG_STRING(filename); - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if ((fp = fopen(args[1], "rw")) == NULL) { + FILE *fp; + if ((fp = fopen(filename, "w")) == NULL) { report_count(1); report_error("BAD FILE_ARGUMENT"); return; @@ -3959,26 +2453,25 @@ report_count(1); report_return(putwin(win, fp)); + fclose(fp); } void cmd_qiflush(int nargs, char **args) { - if (check_arg_count(nargs, 0) == 1) - return; + ARGC(0); qiflush(); report_count(1); - report_return(OK); /* fake a return because call returns void */ + report_return(OK); /* fake a return because call returns void */ } void cmd_raw(int nargs, char **args) { - if (check_arg_count(nargs, 0) == 1) - return; + ARGC(0); report_count(1); report_return(raw()); @@ -3988,16 +2481,8 @@ void cmd_redrawwin(int nargs, char **args) { - WINDOW *win; - - if (check_arg_count(nargs, 1) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(1); + ARG_WINDOW(win); report_count(1); report_return(redrawwin(win)); @@ -4007,8 +2492,7 @@ void cmd_reset_prog_mode(int nargs, char **args) { - if (check_arg_count(nargs, 0) == 1) - return; + ARGC(0); report_count(1); report_return(reset_prog_mode()); @@ -4018,8 +2502,7 @@ void cmd_reset_shell_mode(int nargs, char **args) { - if (check_arg_count(nargs, 0) == 1) - return; + ARGC(0); report_count(1); report_return(reset_shell_mode()); @@ -4029,33 +2512,19 @@ void cmd_resetty(int nargs, char **args) { - if (check_arg_count(nargs, 0) == 1) - return; + ARGC(0); report_count(1); report_return(resetty()); } - -void -cmd_resizeterm(int nargs, char **args) -{ - int rows, cols; - - if (check_arg_count(nargs, 2) == 1) - return; - - if (sscanf(args[0], "%d", &rows) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &cols) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + +void +cmd_resizeterm(int nargs, char **args) +{ + ARGC(2); + ARG_INT(rows); + ARG_INT(cols); report_count(1); report_return(resizeterm(rows, cols)); @@ -4065,8 +2534,7 @@ void cmd_savetty(int nargs, char **args) { - if (check_arg_count(nargs, 0) == 1) - return; + ARGC(0); report_count(1); report_return(savetty()); @@ -4078,10 +2546,8 @@ { char string[256]; - if (check_arg_count(nargs, 0) == 1) - return; + ARGC(0); - /* XXX call2 */ report_count(2); report_return(scanw("%s", string)); report_status(string); @@ -4091,16 +2557,8 @@ void cmd_scroll(int nargs, char **args) { - WINDOW *win; - - if (check_arg_count(nargs, 1) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(1); + ARG_WINDOW(win); report_count(1); report_return(scroll(win)); @@ -4110,23 +2568,9 @@ void cmd_scrollok(int nargs, char **args) { - WINDOW *win; - int flag; - - if (check_arg_count(nargs, 2) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &flag) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(2); + ARG_WINDOW(win); + ARG_INT(flag); report_count(1); report_return(scrollok(win, flag)); @@ -4136,27 +2580,19 @@ void cmd_setterm(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(1); + ARG_MODIFIABLE_STRING(name); report_count(1); - report_return(setterm(args[0])); + report_return(setterm(name)); } void cmd_set_term(int nargs, char **args) { - SCREEN *scrn; - - if (check_arg_count(nargs, 1) == 1) - return; - - if (sscanf(args[0], "%p", &scrn) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(1); + ARG_SCREEN(scrn); report_count(1); report_ptr(set_term(scrn)); @@ -4166,8 +2602,7 @@ void cmd_start_color(int nargs, char **args) { - if (check_arg_count(nargs, 0) == 1) - return; + ARGC(0); report_count(1); report_return(start_color()); @@ -4177,41 +2612,12 @@ void cmd_subpad(int nargs, char **args) { - WINDOW *pad; - int lines, cols, begin_y, begin_x; - - if (check_arg_count(nargs, 5) == 1) - return; - - if (sscanf(args[0], "%p", &pad) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &lines) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[2], "%d", &cols) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[3], "%d", &begin_y) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[4], "%d", &begin_x) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(5); + ARG_WINDOW(pad); + ARG_INT(lines); + ARG_INT(cols); + ARG_INT(begin_y); + ARG_INT(begin_x); report_count(1); report_ptr(subpad(pad, lines, cols, begin_y, begin_x)); @@ -4221,41 +2627,12 @@ void cmd_subwin(int nargs, char **args) { - WINDOW *win; - int lines, cols, begin_y, begin_x; - - if (check_arg_count(nargs, 5) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &lines) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[2], "%d", &cols) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[3], "%d", &begin_y) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[4], "%d", &begin_x) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(5); + ARG_WINDOW(win); + ARG_INT(lines); + ARG_INT(cols); + ARG_INT(begin_y); + ARG_INT(begin_x); report_count(1); report_ptr(subwin(win, lines, cols, begin_y, begin_x)); @@ -4265,8 +2642,7 @@ void cmd_termattrs(int nargs, char **args) { - if (check_arg_count(nargs, 0) == 1) - return; + ARGC(0); report_count(1); report_int(termattrs()); @@ -4276,8 +2652,7 @@ void cmd_term_attrs(int nargs, char **args) { - if (check_arg_count(nargs, 0) == 1) - return; + ARGC(0); report_count(1); report_int(term_attrs()); @@ -4287,29 +2662,10 @@ void cmd_touchline(int nargs, char **args) { - WINDOW *win; - int start, count; - - if (check_arg_count(nargs, 3) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &start) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[2], "%d", &count) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(3); + ARG_WINDOW(win); + ARG_INT(start); + ARG_INT(count); report_count(1); report_return(touchline(win, start, count)); @@ -4319,22 +2675,9 @@ void cmd_touchoverlap(int nargs, char **args) { - WINDOW *win1, *win2; - - if (check_arg_count(nargs, 2) == 1) - return; - - if (sscanf(args[0], "%p", &win1) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%p", &win2) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(2); + ARG_WINDOW(win1); + ARG_WINDOW(win2); report_count(1); report_return(touchoverlap(win1, win2)); @@ -4344,16 +2687,8 @@ void cmd_touchwin(int nargs, char **args) { - WINDOW *win; - - if (check_arg_count(nargs, 1) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(1); + ARG_WINDOW(win); report_count(1); report_return(touchwin(win)); @@ -4363,16 +2698,8 @@ void cmd_ungetch(int nargs, char **args) { - int ch; - - if (check_arg_count(nargs, 1) == 1) - return; - - if (sscanf(args[0], "%d", &ch) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(1); + ARG_INT(ch); report_count(1); report_return(ungetch(ch)); @@ -4382,16 +2709,8 @@ void cmd_untouchwin(int nargs, char **args) { - WINDOW *win; - - if (check_arg_count(nargs, 1) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(1); + ARG_WINDOW(win); report_count(1); report_return(untouchwin(win)); @@ -4401,8 +2720,7 @@ void cmd_use_default_colors(int nargs, char **args) { - if (check_arg_count(nargs, 0) == 1) - return; + ARGC(0); report_count(1); report_return(use_default_colors()); @@ -4412,33 +2730,23 @@ void cmd_vline(int nargs, char **args) { - int count; - chtype *ch; - - if (check_arg_count(nargs, 2) == 1) - return; - - ch = (chtype *) args[0]; - - if (sscanf(args[1], "%d", &count) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(2); + ARG_CHTYPE(ch); + ARG_INT(count); report_count(1); - report_return(vline(ch[0], count)); + report_return(vline(ch, count)); } static int -internal_vw_printw(WINDOW *win, char *arg1, ...) +internal_vw_printw(WINDOW * win, const char *fmt, ...) { va_list va; int rv; - va_start(va, arg1); - rv = vw_printw(win, arg1, va); + va_start(va, fmt); + rv = vw_printw(win, fmt, va); va_end(va); return rv; @@ -4447,30 +2755,24 @@ void cmd_vw_printw(int nargs, char **args) { - WINDOW *win; - - if (check_arg_count(nargs, 3) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(3); + ARG_WINDOW(win); + ARG_STRING(fmt); /* Must have a single "%s" in this test. */ + ARG_STRING(arg); report_count(1); - report_return(internal_vw_printw(win, args[1], args[2])); + report_return(internal_vw_printw(win, fmt, arg)); } static int -internal_vw_scanw(WINDOW *win, char *arg1, ...) +internal_vw_scanw(WINDOW * win, const char *fmt, ...) { va_list va; int rv; - va_start(va, arg1); - rv = vw_scanw(win, arg1, va); + va_start(va, fmt); + rv = vw_scanw(win, fmt, va); va_end(va); return rv; @@ -4479,21 +2781,14 @@ void cmd_vw_scanw(int nargs, char **args) { - WINDOW *win; char string[256]; - if (check_arg_count(nargs, 2) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(2); + ARG_WINDOW(win); + ARG_STRING(fmt); - /* XXX - call2 */ report_count(2); - report_int(internal_vw_scanw(win, args[1], string)); + report_int(internal_vw_scanw(win, fmt, string)); report_status(string); } @@ -4515,93 +2810,50 @@ void cmd_waddch(int nargs, char **args) { - WINDOW *win; - chtype *ch; - - if (check_arg_count(nargs, 2) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - ch = (chtype *) args[1]; + ARGC(2); + ARG_WINDOW(win); + ARG_CHTYPE(ch); report_count(1); - report_return(waddch(win, ch[0])); + report_return(waddch(win, ch)); } void cmd_waddchnstr(int nargs, char **args) { - WINDOW *win; - int count; - - if (check_arg_count(nargs, 3) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[2], "%d", &count) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(3); + ARG_WINDOW(win); + ARG_CHTYPE_STRING(chstr); + ARG_INT(count); report_count(1); - report_return(waddchnstr(win, (chtype *) args[1], count)); + report_return(waddchnstr(win, chstr, count)); } void cmd_waddchstr(int nargs, char **args) { - WINDOW *win; - - if (check_arg_count(nargs, 2) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(2); + ARG_WINDOW(win); + ARG_CHTYPE_STRING(chstr); report_count(1); - report_return(waddchstr(win, (chtype *) args[1])); + report_return(waddchstr(win, chstr)); } void cmd_waddnstr(int nargs, char **args) { - WINDOW *win; - int count; - - if (check_arg_count(nargs, 1) == 3) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[2], "%d", &count) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(3); + ARG_WINDOW(win); + ARG_STRING(str); + ARG_INT(count); report_count(1); - report_return(waddnstr(win, args[1], count)); + report_return(waddnstr(win, str, count)); } @@ -4609,20 +2861,12 @@ void cmd_wattr_get(int nargs, char **args) { - WINDOW *win; int attr; short pair; - if (check_arg_count(nargs, 1) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(1); + ARG_WINDOW(win); - /* XXX - call3 */ report_count(3); report_return(wattr_get(win, &attr, &pair, NULL)); report_int(attr); @@ -4633,23 +2877,9 @@ void cmd_wattr_off(int nargs, char **args) { - WINDOW *win; - int attr; - - if (check_arg_count(nargs, 2) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &attr) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(2); + ARG_WINDOW(win); + ARG_INT(attr); report_count(1); report_return(wattr_off(win, attr, NULL)); @@ -4659,23 +2889,9 @@ void cmd_wattr_on(int nargs, char **args) { - WINDOW *win; - int attr; - - if (check_arg_count(nargs, 2) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &attr) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(2); + ARG_WINDOW(win); + ARG_INT(attr); report_count(1); report_return(wattr_on(win, attr, NULL)); @@ -4685,30 +2901,10 @@ void cmd_wattr_set(int nargs, char **args) { - WINDOW *win; - int attr; - short pair; - - if (check_arg_count(nargs, 3) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &attr) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[2], "%hd", &pair) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(3); + ARG_WINDOW(win); + ARG_INT(attr); + ARG_SHORT(pair); report_count(1); report_return(wattr_set(win, attr, pair, NULL)); @@ -4718,23 +2914,9 @@ void cmd_wattroff(int nargs, char **args) { - WINDOW *win; - int attr; - - if (check_arg_count(nargs, 2) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &attr) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(2); + ARG_WINDOW(win); + ARG_INT(attr); report_count(1); report_return(wattroff(win, attr)); @@ -4744,23 +2926,9 @@ void cmd_wattron(int nargs, char **args) { - WINDOW *win; - int attr; - - if (check_arg_count(nargs, 2) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &attr) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(2); + ARG_WINDOW(win); + ARG_INT(attr); report_count(1); report_return(wattron(win, attr)); @@ -4770,23 +2938,9 @@ void cmd_wattrset(int nargs, char **args) { - WINDOW *win; - int attr; - - if (check_arg_count(nargs, 2) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &attr) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(2); + ARG_WINDOW(win); + ARG_INT(attr); report_count(1); report_return(wattrset(win, attr)); @@ -4796,46 +2950,23 @@ void cmd_wbkgd(int nargs, char **args) { - WINDOW *win; - chtype *ch; - - if (check_arg_count(nargs, 2) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(2); + ARG_WINDOW(win); + ARG_CHTYPE(ch); - ch = (chtype *) args[1]; report_count(1); - report_return(wbkgd(win, ch[0])); + report_return(wbkgd(win, ch)); } void cmd_wbkgdset(int nargs, char **args) { - WINDOW *win; - int ch; - - if (check_arg_count(nargs, 2) == 1) - return; + ARGC(2); + ARG_WINDOW(win); + ARG_CHTYPE(ch); - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &ch) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - wbkgdset(win, ch); /* void return */ + wbkgdset(win, ch); /* void return */ report_count(1); report_return(OK); } @@ -4844,65 +2975,16 @@ void cmd_wborder(int nargs, char **args) { - WINDOW *win; - int ls, rs, ts, bs, tl, tr, bl, br; - - if (check_arg_count(nargs, 9) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &ls) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[2], "%d", &rs) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[3], "%d", &ts) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[4], "%d", &bs) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[5], "%d", &tl) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[6], "%d", &tr) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[7], "%d", &bl) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[8], "%d", &br) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(9); + ARG_WINDOW(win); + ARG_INT(ls); + ARG_INT(rs); + ARG_INT(ts); + ARG_INT(bs); + ARG_INT(tl); + ARG_INT(tr); + ARG_INT(bl); + ARG_INT(br); report_count(1); report_return(wborder(win, ls, rs, ts, bs, tl, tr, bl, br)); @@ -4912,16 +2994,8 @@ void cmd_wclear(int nargs, char **args) { - WINDOW *win; - - if (check_arg_count(nargs, 1) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(1); + ARG_WINDOW(win); report_count(1); report_return(wclear(win)); @@ -4931,16 +3005,8 @@ void cmd_wclrtobot(int nargs, char **args) { - WINDOW *win; - - if (check_arg_count(nargs, 1) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(1); + ARG_WINDOW(win); report_count(1); report_return(wclrtobot(win)); @@ -4950,16 +3016,8 @@ void cmd_wclrtoeol(int nargs, char **args) { - WINDOW *win; - - if (check_arg_count(nargs, 1) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(1); + ARG_WINDOW(win); report_count(1); report_return(wclrtoeol(win)); @@ -4970,23 +3028,10 @@ void cmd_wcolor_set(int nargs, char **args) { - WINDOW *win; - short pair; - - if (check_arg_count(nargs, 2) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%hd", &pair) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(3); + ARG_WINDOW(win); + ARG_SHORT(pair); + ARG_NULL(); report_count(1); report_return(wcolor_set(win, pair, NULL)); @@ -4996,16 +3041,8 @@ void cmd_wdelch(int nargs, char **args) { - WINDOW *win; - - if (check_arg_count(nargs, 1) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(1); + ARG_WINDOW(win); report_count(1); report_return(wdelch(win)); @@ -5015,16 +3052,8 @@ void cmd_wdeleteln(int nargs, char **args) { - WINDOW *win; - - if (check_arg_count(nargs, 1) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(1); + ARG_WINDOW(win); report_count(1); report_return(wdeleteln(win)); @@ -5035,23 +3064,9 @@ void cmd_wechochar(int nargs, char **args) { - WINDOW *win; - int ch; - - if (check_arg_count(nargs, 2) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &ch) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(2); + ARG_WINDOW(win); + ARG_CHTYPE(ch); report_count(1); report_return(wechochar(win, ch)); @@ -5061,16 +3076,8 @@ void cmd_werase(int nargs, char **args) { - WINDOW *win; - - if (check_arg_count(nargs, 1) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(1); + ARG_WINDOW(win); report_count(1); report_return(werase(win)); @@ -5080,16 +3087,8 @@ void cmd_wgetch(int nargs, char **args) { - WINDOW *win; - - if (check_arg_count(nargs, 1) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(1); + ARG_WINDOW(win); report_count(1); report_int(wgetch(win)); @@ -5099,26 +3098,12 @@ void cmd_wgetnstr(int nargs, char **args) { - WINDOW *win; - int count; char string[256]; - if (check_arg_count(nargs, 2) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &count) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(2); + ARG_WINDOW(win); + ARG_INT(count); - /* XXX - call2 */ report_count(2); report_return(wgetnstr(win, string, count)); report_status(string); @@ -5128,18 +3113,10 @@ void cmd_wgetstr(int nargs, char **args) { - WINDOW *win; char string[256]; - - if (check_arg_count(nargs, 1) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(1); + ARG_WINDOW(win); string[0] = '\0'; @@ -5152,29 +3129,10 @@ void cmd_whline(int nargs, char **args) { - WINDOW *win; - int ch, count; - - if (check_arg_count(nargs, 3) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &ch) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[2], "%d", &count) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(3); + ARG_WINDOW(win); + ARG_CHTYPE(ch); + ARG_INT(count); report_count(1); report_return(whline(win, ch, count)); @@ -5184,45 +3142,23 @@ void cmd_winch(int nargs, char **args) { - WINDOW *win; - - if (check_arg_count(nargs, 1) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(1); + ARG_WINDOW(win); report_count(1); - report_int(winch(win)); + report_byte(winch(win)); } void cmd_winchnstr(int nargs, char **args) { - WINDOW *win; chtype string[256]; - int count; - - if (check_arg_count(nargs, 2) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - if (sscanf(args[1], "%d", &count) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(2); + ARG_WINDOW(win); + ARG_INT(count); - /* XXX - call2 */ report_count(2); report_return(winchnstr(win, string, count)); report_nstr(string); @@ -5232,19 +3168,11 @@ void cmd_winchstr(int nargs, char **args) { - WINDOW *win; chtype string[256]; - if (check_arg_count(nargs, 1) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(1); + ARG_WINDOW(win); - /* XXX - call2 */ report_count(2); report_return(winchstr(win, string)); report_nstr(string); @@ -5254,52 +3182,24 @@ void cmd_winnstr(int nargs, char **args) { - WINDOW *win; char string[256]; - int count; - - if (check_arg_count(nargs, 2) == 1) - return; - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &count) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(2); + ARG_WINDOW(win); + ARG_INT(count); - /* XXX - call2 */ report_count(2); - report_return(winnstr(win, string, count)); + report_int(winnstr(win, string, count)); report_status(string); } -void -cmd_winsch(int nargs, char **args) -{ - WINDOW *win; - int ch; - - if (check_arg_count(nargs, 2) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &ch) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } +void +cmd_winsch(int nargs, char **args) +{ + ARGC(2); + ARG_WINDOW(win); + ARG_CHTYPE(ch); report_count(1); report_return(winsch(win, ch)); @@ -5309,23 +3209,9 @@ void cmd_winsdelln(int nargs, char **args) { - WINDOW *win; - int count; - - if (check_arg_count(nargs, 2) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &count) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(2); + ARG_WINDOW(win); + ARG_INT(count); report_count(1); report_return(winsdelln(win, count)); @@ -5335,16 +3221,8 @@ void cmd_winsertln(int nargs, char **args) { - WINDOW *win; - - if (check_arg_count(nargs, 1) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(1); + ARG_WINDOW(win); report_count(1); report_return(winsertln(win)); @@ -5354,19 +3232,11 @@ void cmd_winstr(int nargs, char **args) { - WINDOW *win; char string[256]; - if (check_arg_count(nargs, 1) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(1); + ARG_WINDOW(win); - /* XXX - call2 */ report_count(2); report_return(winstr(win, string)); report_status(string); @@ -5376,29 +3246,10 @@ void cmd_wmove(int nargs, char **args) { - WINDOW *win; - int y, x; - - if (check_arg_count(nargs, 3) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &y) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[2], "%d", &x) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(3); + ARG_WINDOW(win); + ARG_INT(y); + ARG_INT(x); report_count(1); report_return(wmove(win, y, x)); @@ -5408,16 +3259,8 @@ void cmd_wnoutrefresh(int nargs, char **args) { - WINDOW *win; - - if (check_arg_count(nargs, 1) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(1); + ARG_WINDOW(win); report_count(1); report_return(wnoutrefresh(win)); @@ -5427,48 +3270,23 @@ void cmd_wprintw(int nargs, char **args) { - WINDOW *win; - - if (check_arg_count(nargs, 3) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(3); + ARG_WINDOW(win); + ARG_STRING(fmt); + ARG_STRING(arg); report_count(1); - report_return(wprintw(win, args[1], args[2])); + report_return(wprintw(win, fmt, arg)); } void cmd_wredrawln(int nargs, char **args) { - WINDOW *win; - int beg_line, num_lines; - - if (check_arg_count(nargs, 3) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &beg_line) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[2], "%d", &num_lines) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(3); + ARG_WINDOW(win); + ARG_INT(beg_line); + ARG_INT(num_lines); report_count(1); report_return(wredrawln(win, beg_line, num_lines)); @@ -5478,16 +3296,8 @@ void cmd_wrefresh(int nargs, char **args) { - WINDOW *win; - - if (check_arg_count(nargs, 1) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(1); + ARG_WINDOW(win); /* XXX - generates output */ report_count(1); @@ -5498,29 +3308,10 @@ void cmd_wresize(int nargs, char **args) { - WINDOW *win; - int lines, cols; - - if (check_arg_count(nargs, 3) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &lines) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[2], "%d", &cols) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(3); + ARG_WINDOW(win); + ARG_INT(lines); + ARG_INT(cols); report_count(1); report_return(wresize(win, lines, cols)); @@ -5530,43 +3321,23 @@ void cmd_wscanw(int nargs, char **args) { - WINDOW *win; char string[256]; - if (check_arg_count(nargs, 2) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(2); + ARG_WINDOW(win); + ARG_STRING(fmt); report_count(1); - report_return(wscanw(win, args[1], &string)); + report_return(wscanw(win, fmt, &string)); } void cmd_wscrl(int nargs, char **args) { - WINDOW *win; - int n; - - if (check_arg_count(nargs, 2) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &n) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(2); + ARG_WINDOW(win); + ARG_INT(n); report_count(1); report_return(wscrl(win, n)); @@ -5576,29 +3347,10 @@ void cmd_wsetscrreg(int nargs, char **args) { - WINDOW *win; - int top, bottom; - - if (check_arg_count(nargs, 3) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &top) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[2], "%d", &bottom) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(3); + ARG_WINDOW(win); + ARG_INT(top); + ARG_INT(bottom); report_count(1); report_return(wsetscrreg(win, top, bottom)); @@ -5608,882 +3360,935 @@ void cmd_wstandend(int nargs, char **args) { - WINDOW *win; - - if (check_arg_count(nargs, 1) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(1); + ARG_WINDOW(win); report_count(1); - report_return(wstandend(win)); + report_int(wstandend(win)); } void cmd_wstandout(int nargs, char **args) { - WINDOW *win; - - if (check_arg_count(nargs, 1) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(1); + ARG_WINDOW(win); report_count(1); - report_return(wstandout(win)); + report_int(wstandout(win)); } void cmd_wtimeout(int nargs, char **args) { - WINDOW *win; - int tval; + ARGC(2); + ARG_WINDOW(win); + ARG_INT(tval); - if (check_arg_count(nargs, 2) == 1) - return; + wtimeout(win, tval); /* void return */ + report_count(1); + report_return(OK); +} - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - if (sscanf(args[1], "%d", &tval) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } +void +cmd_wtouchln(int nargs, char **args) +{ + ARGC(4); + ARG_WINDOW(win); + ARG_INT(line); + ARG_INT(n); + ARG_INT(changed); - wtimeout(win, tval); /* void return */ report_count(1); - report_return(OK); + report_return(wtouchln(win, line, n, changed)); } void -cmd_wtouchln(int nargs, char **args) +cmd_wunderend(int nargs, char **args) { - WINDOW *win; - int line, n, changed; + ARGC(1); + ARG_WINDOW(win); - if (check_arg_count(nargs, 4) == 1) - return; + report_count(1); + report_int(wunderend(win)); +} - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - if (sscanf(args[1], "%d", &line) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } +void +cmd_wunderscore(int nargs, char **args) +{ + ARGC(1); + ARG_WINDOW(win); - if (sscanf(args[2], "%d", &n) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + report_count(1); + report_int(wunderscore(win)); +} - if (sscanf(args[3], "%d", &changed) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + +void +cmd_wvline(int nargs, char **args) +{ + ARGC(3); + ARG_WINDOW(win); + ARG_CHTYPE(ch); + ARG_INT(n); report_count(1); - report_return(wtouchln(win, line, n, changed)); + report_return(wvline(win, ch, n)); } void -cmd_wunderend(int nargs, char **args) +cmd_insnstr(int nargs, char **args) { - WINDOW *win; + ARGC(2); + ARG_STRING(str); + ARG_INT(n); - if (check_arg_count(nargs, 1) == 1) - return; + report_count(1); + report_return(insnstr(str, n)); +} - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + +void +cmd_insstr(int nargs, char **args) +{ + ARGC(1); + ARG_STRING(str); report_count(1); - report_return(wunderend(win)); + report_return(insstr(str)); } void -cmd_wunderscore(int nargs, char **args) +cmd_mvinsnstr(int nargs, char **args) { - WINDOW *win; + ARGC(4); + ARG_INT(y); + ARG_INT(x); + ARG_STRING(str); + ARG_INT(n); - if (check_arg_count(nargs, 1) == 1) - return; + report_count(1); + report_return(mvinsnstr(y, x, str, n)); +} - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + +void +cmd_mvinsstr(int nargs, char **args) +{ + ARGC(3); + ARG_INT(y); + ARG_INT(x); + ARG_STRING(str); report_count(1); - report_return(wunderscore(win)); + report_return(mvinsstr(y, x, str)); } void -cmd_wvline(int nargs, char **args) +cmd_mvwinsnstr(int nargs, char **args) { - WINDOW *win; - int n; - chtype *ch; + ARGC(5); + ARG_WINDOW(win); + ARG_INT(y); + ARG_INT(x); + ARG_STRING(str); + ARG_INT(n); - if (check_arg_count(nargs, 3) == 1) - return; + report_count(1); + report_return(mvwinsnstr(win, y, x, str, n)); - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } +} - ch = (chtype *) args[1]; - if (sscanf(args[2], "%d", &n) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } +void +cmd_mvwinsstr(int nargs, char **args) +{ + ARGC(4); + ARG_WINDOW(win); + ARG_INT(y); + ARG_INT(x); + ARG_STRING(str); + + report_count(1); + report_return(mvwinsstr(win, y, x, str)); +} + + +void +cmd_winsnstr(int nargs, char **args) +{ + ARGC(3); + ARG_WINDOW(win); + ARG_STRING(str); + ARG_INT(n); report_count(1); - report_return(wvline(win, ch[0], n)); + report_return(winsnstr(win, str, n)); } void -cmd_insnstr(int nargs, char **args) +cmd_winsstr(int nargs, char **args) { - int n; - - if (check_arg_count(nargs, 2) == 1) - return; - - if (sscanf(args[1], "%d", &n) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(2); + ARG_WINDOW(win); + ARG_STRING(str); report_count(1); - report_return(insnstr(args[0], n)); + report_return(winsstr(win, str)); } void -cmd_insstr(int nargs, char **args) +cmd_chgat(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(4); + ARG_INT(n); + ARG_INT(attr); + ARG_INT(colour); + ARG_NULL(); report_count(1); - report_return(insstr(args[0])); + report_return(chgat(n, attr, colour, NULL)); } void -cmd_mvinsnstr(int nargs, char **args) +cmd_wchgat(int nargs, char **args) { - int y, x, n; - - if (check_arg_count(nargs, 4) == 1) - return; + ARGC(5); + ARG_WINDOW(win); + ARG_INT(n); + ARG_INT(attr); + ARG_SHORT(colour); + ARG_NULL(); - if (sscanf(args[0], "%d", &y) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + report_count(1); + report_return(wchgat(win, n, attr, colour, NULL)); +} - if (sscanf(args[1], "%d", &x) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - if (sscanf(args[3], "%d", &n) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } +void +cmd_mvchgat(int nargs, char **args) +{ + ARGC(6); + ARG_INT(y); + ARG_INT(x); + ARG_INT(n); + ARG_INT(attr); + ARG_SHORT(colour); + ARG_NULL(); report_count(1); - report_return(mvinsnstr(y, x, args[2], n)); + report_return(mvchgat(y, x, n, attr, colour, NULL)); } void -cmd_mvinsstr(int nargs, char **args) +cmd_mvwchgat(int nargs, char **args) { - int y, x; + ARGC(7); + ARG_WINDOW(win); + ARG_INT(y); + ARG_INT(x); + ARG_INT(n); + ARG_INT(attr); + ARG_SHORT(colour); + ARG_NULL(); - if (check_arg_count(nargs, 3) == 1) - return; + report_count(1); + report_return(mvwchgat(win, y, x, n, attr, colour, NULL)); +} - if (sscanf(args[0], "%d", &y) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - if (sscanf(args[1], "%d", &x) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } +void +cmd_add_wch(int nargs, char **args) +{ + ARGC(1); + ARG_CCHAR_STRING(ch); report_count(1); - report_return(mvinsstr(y, x, args[2])); + report_return(add_wch(ch)); } void -cmd_mvwinsnstr(int nargs, char **args) +cmd_wadd_wch(int nargs, char **args) { - WINDOW *win; - int y, x, n; + ARGC(2); + ARG_WINDOW(win); + ARG_CCHAR_STRING(ch); - if (check_arg_count(nargs, 5) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - - if (sscanf(args[1], "%d", &y) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + report_count(1); + report_return(wadd_wch(win, ch)); +} - if (sscanf(args[2], "%d", &x) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - if (sscanf(args[4], "%d", &n) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } +void +cmd_mvadd_wch(int nargs, char **args) +{ + ARGC(3); + ARG_INT(y); + ARG_INT(x); + ARG_CCHAR_STRING(ch); report_count(1); - report_return(mvwinsnstr(win, y, x, args[3], n)); - + report_return(mvadd_wch(y, x, ch)); } void -cmd_mvwinsstr(int nargs, char **args) +cmd_mvwadd_wch(int nargs, char **args) { - WINDOW *win; - int y, x; + ARGC(4); + ARG_WINDOW(win); + ARG_INT(y); + ARG_INT(x); + ARG_CCHAR_STRING(ch); - if (check_arg_count(nargs, 4) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + report_count(1); + report_return(mvwadd_wch(win, y, x, ch)); +} - if (sscanf(args[1], "%d", &y) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - if (sscanf(args[2], "%d", &x) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } +void +cmd_add_wchnstr(int nargs, char **args) +{ + ARGC(1); + ARG_IGNORE(); report_count(1); - report_return(mvwinsstr(win, y, x, args[3])); + report_error("UNSUPPORTED"); } void -cmd_winsnstr(int nargs, char **args) +cmd_add_wchstr(int nargs, char **args) { - WINDOW *win; - int n; + ARGC(1); + ARG_IGNORE(); - if (check_arg_count(nargs, 3) == 1) - return; + report_count(1); + report_error("UNSUPPORTED"); +} - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - if (sscanf(args[2], "%d", &n) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } +void +cmd_wadd_wchnstr(int nargs, char **args) +{ + ARGC(1); + ARG_IGNORE(); report_count(1); - report_return(winsnstr(win, args[1], n)); + report_error("UNSUPPORTED"); } void -cmd_winsstr(int nargs, char **args) +cmd_wadd_wchstr(int nargs, char **args) { - WINDOW *win; - - if (check_arg_count(nargs, 2) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(1); + ARG_IGNORE(); report_count(1); - report_return(winsstr(win, args[1])); + report_error("UNSUPPORTED"); } - void -cmd_chgat(int nargs, char **args) +cmd_mvadd_wchnstr(int nargs, char **args) { - int n, attr, colour; - - if (check_arg_count(nargs, 4) == 1) - return; + ARGC(1); + ARG_IGNORE(); - if (sscanf(args[0], "%d", &n) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + report_count(1); + report_error("UNSUPPORTED"); +} - if (sscanf(args[1], "%d", &attr) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - if (sscanf(args[2], "%d", &colour) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } +void +cmd_mvadd_wchstr(int nargs, char **args) +{ + ARGC(1); + ARG_IGNORE(); - /* Note: 4th argument unused in current curses implementation */ report_count(1); - report_return(chgat(n, attr, colour, NULL)); + report_error("UNSUPPORTED"); } void -cmd_wchgat(int nargs, char **args) +cmd_mvwadd_wchnstr(int nargs, char **args) { - WINDOW *win; - int n, attr; - short colour; - - if (check_arg_count(nargs, 4) == 1) - return; - - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(1); + ARG_IGNORE(); - if (sscanf(args[1], "%d", &n) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + report_count(1); + report_error("UNSUPPORTED"); +} - if (sscanf(args[2], "%d", &attr) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - if (sscanf(args[3], "%hd", &colour) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } +void +cmd_mvwadd_wchstr(int nargs, char **args) +{ + ARGC(1); + ARG_IGNORE(); report_count(1); - report_return(wchgat(win, n, attr, colour, NULL)); + report_error("UNSUPPORTED"); } void -cmd_mvchgat(int nargs, char **args) +cmd_addnwstr(int nargs, char **args) { - int y, x, n, attr; - short colour; + ARGC(2); + ARG_WCHAR_STRING(wstr); + ARG_INT(n); - if (check_arg_count(nargs, 6) == 1) - return; + report_count(1); + report_return(addnwstr(wstr, n)); +} - if (sscanf(args[0], "%d", &y) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - if (sscanf(args[1], "%d", &x) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } +void +cmd_addwstr(int nargs, char **args) +{ + ARGC(1); + ARG_WCHAR_STRING(wstr); - if (sscanf(args[2], "%d", &n) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + report_count(1); + report_return(addwstr(wstr)); +} - if (sscanf(args[3], "%d", &attr) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - if (sscanf(args[4], "%hd", &colour) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } +void +cmd_mvaddnwstr(int nargs, char **args) +{ + ARGC(4); + ARG_INT(y); + ARG_INT(x); + ARG_WCHAR_STRING(wstr); + ARG_INT(n); report_count(1); - report_return(mvchgat(y, x, n, attr, colour, NULL)); + report_return(mvaddnwstr(y, x, wstr, n)); } void -cmd_mvwchgat(int nargs, char **args) +cmd_mvaddwstr(int nargs, char **args) { - WINDOW *win; - int y, x, n, attr, colour; - - if (check_arg_count(nargs, 6) == 1) - return; + ARGC(3); + ARG_INT(y); + ARG_INT(x); + ARG_WCHAR_STRING(wstr); - if (sscanf(args[0], "%p", &win) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + report_count(1); + report_return(mvaddwstr(y, x, wstr)); +} - if (sscanf(args[1], "%d", &y) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - if (sscanf(args[2], "%d", &x) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } +void +cmd_mvwaddnwstr(int nargs, char **args) +{ + ARGC(5); + ARG_WINDOW(win); + ARG_INT(y); + ARG_INT(x); + ARG_WCHAR_STRING(wstr); + ARG_INT(n); - if (sscanf(args[3], "%d", &n) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + report_count(1); + report_return(mvwaddnwstr(win, y, x, wstr, n)); +} - if (sscanf(args[4], "%d", &attr) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } - if (sscanf(args[5], "%d", &colour) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } +void +cmd_mvwaddwstr(int nargs, char **args) +{ + ARGC(4); + ARG_WINDOW(win); + ARG_INT(y); + ARG_INT(x); + ARG_WCHAR_STRING(wstr); report_count(1); - report_return(mvwchgat(win, y, x, n, attr, colour, NULL)); + report_return(mvwaddwstr(win, y, x, wstr)); } void -cmd_add_wch(int nargs, char **args) +cmd_waddnwstr(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(3); + ARG_WINDOW(win); + ARG_WCHAR_STRING(wstr); + ARG_INT(n); report_count(1); - report_error("UNSUPPORTED"); + report_return(waddnwstr(win, wstr, n)); } void -cmd_wadd_wch(int nargs, char **args) +cmd_waddwstr(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(2); + ARG_WINDOW(win); + ARG_WCHAR_STRING(wstr); report_count(1); - report_error("UNSUPPORTED"); + report_return(waddwstr(win, wstr)); } void -cmd_mvadd_wch(int nargs, char **args) +cmd_echo_wchar(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(1); + ARG_CCHAR_STRING(ch); report_count(1); - report_error("UNSUPPORTED"); + report_return(echo_wchar(ch)); } void -cmd_mvwadd_wch(int nargs, char **args) +cmd_wecho_wchar(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(2); + ARG_WINDOW(win); + ARG_CCHAR_STRING(ch); report_count(1); - report_error("UNSUPPORTED"); + report_return(wecho_wchar(win, ch)); } - void -cmd_add_wchnstr(int nargs, char **args) +cmd_pecho_wchar(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(2); + ARG_WINDOW(pad); + ARG_CCHAR_STRING(wch); report_count(1); - report_error("UNSUPPORTED"); + report_return(pecho_wchar(pad, wch)); } +/* insert */ void -cmd_add_wchstr(int nargs, char **args) +cmd_ins_wch(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(1); + ARG_CCHAR_STRING(wch); report_count(1); - report_error("UNSUPPORTED"); + report_return(ins_wch(wch)); } void -cmd_wadd_wchnstr(int nargs, char **args) +cmd_wins_wch(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(2); + ARG_WINDOW(win); + ARG_CCHAR_STRING(wch); report_count(1); - report_error("UNSUPPORTED"); + report_return(wins_wch(win, wch)); } void -cmd_wadd_wchstr(int nargs, char **args) +cmd_mvins_wch(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(3); + ARG_INT(y); + ARG_INT(x); + ARG_CCHAR_STRING(wch); report_count(1); - report_error("UNSUPPORTED"); + report_return(mvins_wch(y, x, wch)); } void -cmd_mvadd_wchnstr(int nargs, char **args) +cmd_mvwins_wch(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(4); + ARG_WINDOW(win); + ARG_INT(y); + ARG_INT(x); + ARG_CCHAR_STRING(wch); report_count(1); - report_error("UNSUPPORTED"); + report_return(mvwins_wch(win, y, x, wch)); } void -cmd_mvadd_wchstr(int nargs, char **args) +cmd_ins_nwstr(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(2); + ARG_WCHAR_STRING(wstr); + ARG_INT(n); report_count(1); - report_error("UNSUPPORTED"); + report_return(ins_nwstr(wstr, n)); } void -cmd_mvwadd_wchnstr(int nargs, char **args) +cmd_ins_wstr(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(1); + ARG_WCHAR_STRING(wstr); report_count(1); - report_error("UNSUPPORTED"); + report_return(ins_wstr(wstr)); } void -cmd_mvwadd_wchstr(int nargs, char **args) +cmd_mvins_nwstr(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(4); + ARG_INT(y); + ARG_INT(x); + ARG_WCHAR_STRING(wstr); + ARG_INT(n); report_count(1); - report_error("UNSUPPORTED"); + report_return(mvins_nwstr(y, x, wstr, n)); } - void -cmd_addnwstr(int nargs, char **args) +cmd_mvins_wstr(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(3); + ARG_INT(y); + ARG_INT(x); + ARG_WCHAR_STRING(wstr); report_count(1); - report_error("UNSUPPORTED"); + report_return(mvins_wstr(y, x, wstr)); } void -cmd_addwstr(int nargs, char **args) +cmd_mvwins_nwstr(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(5); + ARG_WINDOW(win); + ARG_INT(y); + ARG_INT(x); + ARG_WCHAR_STRING(wstr); + ARG_INT(n); report_count(1); - report_error("UNSUPPORTED"); + report_return(mvwins_nwstr(win, y, x, wstr, n)); } void -cmd_mvaddnwstr(int nargs, char **args) +cmd_mvwins_wstr(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(4); + ARG_WINDOW(win); + ARG_INT(y); + ARG_INT(x); + ARG_WCHAR_STRING(wstr); report_count(1); - report_error("UNSUPPORTED"); + report_return(mvwins_wstr(win, y, x, wstr)); } void -cmd_mvaddwstr(int nargs, char **args) +cmd_wins_nwstr(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(3); + ARG_WINDOW(win); + ARG_WCHAR_STRING(wstr); + ARG_INT(n); report_count(1); - report_error("UNSUPPORTED"); + report_return(wins_nwstr(win, wstr, n)); } void -cmd_mvwaddnwstr(int nargs, char **args) +cmd_wins_wstr(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(2); + ARG_WINDOW(win); + ARG_WCHAR_STRING(wstr); report_count(1); - report_error("UNSUPPORTED"); + report_return(wins_wstr(win, wstr)); } +/* input */ void -cmd_mvwaddwstr(int nargs, char **args) +cmd_get_wch(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + wchar_t ch; + ARGC(0); - report_count(1); - report_error("UNSUPPORTED"); + report_count(2); + report_return(get_wch(&ch)); + report_wchar(ch); } void -cmd_waddnwstr(int nargs, char **args) +cmd_unget_wch(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(1); + ARG_WCHAR(wch); report_count(1); - report_error("UNSUPPORTED"); + report_return(unget_wch(wch)); } void -cmd_waddwstr(int nargs, char **args) +cmd_mvget_wch(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + wchar_t ch; - report_count(1); - report_error("UNSUPPORTED"); + ARGC(2); + ARG_INT(y); + ARG_INT(x); + + report_count(2); + report_return(mvget_wch(y, x, &ch)); + report_wchar(ch); } +void +cmd_mvwget_wch(int nargs, char **args) +{ + wchar_t ch; + + ARGC(3); + ARG_WINDOW(win); + ARG_INT(y); + ARG_INT(x); + + report_count(2); + report_return(mvwget_wch(win, y, x, &ch)); + report_wchar(ch); +} + void -cmd_echo_wchar(int nargs, char **args) +cmd_wget_wch(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + wchar_t ch; - report_count(1); - report_error("UNSUPPORTED"); + ARGC(1); + ARG_WINDOW(win); + + report_count(2); + report_return(wget_wch(win, &ch)); + report_wchar(ch); } void -cmd_wecho_wchar(int nargs, char **args) +cmd_getn_wstr(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + wchar_t wstr[256]; - report_count(1); - report_error("UNSUPPORTED"); + ARGC(1); + ARG_INT(n); + + report_count(2); + report_return(getn_wstr(wstr, n)); + report_wstr(wstr); } void -cmd_pecho_wchar(int nargs, char **args) +cmd_get_wstr(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + wchar_t wstr[256]; - report_count(1); - report_error("UNSUPPORTED"); + ARGC(0); + + report_count(2); + report_return(get_wstr(wstr)); + report_wstr(wstr); +} + +void +cmd_mvgetn_wstr(int nargs, char **args) +{ + wchar_t wstr[256]; + + ARGC(3); + ARG_INT(y); + ARG_INT(x); + ARG_INT(n); + + report_count(2); + report_return(mvgetn_wstr(y, x, wstr, n)); + report_wstr(wstr); } +void +cmd_mvget_wstr(int nargs, char **args) +{ + wchar_t wstr[256]; + + ARGC(2); + ARG_INT(y); + ARG_INT(x); + + report_count(2); + report_return(mvget_wstr(y, x, wstr)); + report_wstr(wstr); +} -/* insert */ void -cmd_ins_wch(int nargs, char **args) +cmd_mvwgetn_wstr(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + wchar_t wstr[256]; - report_count(1); - report_error("UNSUPPORTED"); + ARGC(4); + ARG_WINDOW(win); + ARG_INT(y); + ARG_INT(x); + ARG_INT(n); + + report_count(2); + report_return(mvwgetn_wstr(win, y, x, wstr, n)); + report_wstr(wstr); } void -cmd_wins_wch(int nargs, char **args) +cmd_mvwget_wstr(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + wchar_t wstr[256]; - report_count(1); - report_error("UNSUPPORTED"); + ARGC(3); + ARG_WINDOW(win); + ARG_INT(y); + ARG_INT(x); + + report_count(2); + report_return(mvwget_wstr(win, y, x, wstr)); + report_wstr(wstr); } void -cmd_mvins_wch(int nargs, char **args) +cmd_wgetn_wstr(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + wchar_t wstr[256]; - report_count(1); - report_error("UNSUPPORTED"); + ARGC(2); + ARG_WINDOW(win); + ARG_INT(n); + + report_count(2); + report_return(wgetn_wstr(win, wstr, n)); + report_wstr(wstr); } void -cmd_mvwins_wch(int nargs, char **args) +cmd_wget_wstr(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + wchar_t wstr[256]; - report_count(1); - report_error("UNSUPPORTED"); + ARGC(1); + ARG_WINDOW(win); + + report_count(2); + report_return(wget_wstr(win, wstr)); + report_wstr(wstr); } +void +cmd_in_wch(int nargs, char **args) +{ + cchar_t wcval; + ARGC(0); + + report_count(2); + report_return(in_wch(&wcval)); + report_cchar(wcval); +} + void -cmd_ins_nwstr(int nargs, char **args) +cmd_mvin_wch(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + cchar_t wcval; - report_count(1); - report_error("UNSUPPORTED"); + ARGC(2); + ARG_INT(y); + ARG_INT(x); + + report_count(2); + report_return(mvin_wch(y, x, &wcval)); + report_cchar(wcval); } void -cmd_ins_wstr(int nargs, char **args) +cmd_mvwin_wch(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + cchar_t wcval; - report_count(1); - report_error("UNSUPPORTED"); + ARGC(3); + ARG_WINDOW(win); + ARG_INT(y); + ARG_INT(x); + + report_count(2); + report_return(mvwin_wch(win, y, x, &wcval)); + report_cchar(wcval); } void -cmd_mvins_nwstr(int nargs, char **args) +cmd_win_wch(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + cchar_t wcval; - report_count(1); - report_error("UNSUPPORTED"); + ARGC(1); + ARG_WINDOW(win); + + report_count(2); + report_return(win_wch(win, &wcval)); + report_cchar(wcval); } void -cmd_mvins_wstr(int nargs, char **args) +cmd_in_wchnstr(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(1); + ARG_IGNORE(); report_count(1); report_error("UNSUPPORTED"); @@ -6491,10 +4296,10 @@ void -cmd_mvwins_nwstr(int nargs, char **args) +cmd_in_wchstr(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(1); + ARG_IGNORE(); report_count(1); report_error("UNSUPPORTED"); @@ -6502,10 +4307,10 @@ void -cmd_mvwins_wstr(int nargs, char **args) +cmd_mvin_wchnstr(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(1); + ARG_IGNORE(); report_count(1); report_error("UNSUPPORTED"); @@ -6513,10 +4318,10 @@ void -cmd_wins_nwstr(int nargs, char **args) +cmd_mvin_wchstr(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(1); + ARG_IGNORE(); report_count(1); report_error("UNSUPPORTED"); @@ -6524,23 +4329,21 @@ void -cmd_wins_wstr(int nargs, char **args) +cmd_mvwin_wchnstr(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(1); + ARG_IGNORE(); report_count(1); report_error("UNSUPPORTED"); } - -/* input */ void -cmd_get_wch(int nargs, char **args) +cmd_mvwin_wchstr(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(1); + ARG_IGNORE(); report_count(1); report_error("UNSUPPORTED"); @@ -6548,10 +4351,10 @@ void -cmd_unget_wch(int nargs, char **args) +cmd_win_wchnstr(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(1); + ARG_IGNORE(); report_count(1); report_error("UNSUPPORTED"); @@ -6559,10 +4362,10 @@ void -cmd_mvget_wch(int nargs, char **args) +cmd_win_wchstr(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(1); + ARG_IGNORE(); report_count(1); report_error("UNSUPPORTED"); @@ -6570,596 +4373,685 @@ void -cmd_mvwget_wch(int nargs, char **args) +cmd_innwstr(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + wchar_t wstr[256]; - report_count(1); - report_error("UNSUPPORTED"); + ARGC(1); + ARG_INT(n); + + report_count(2); + report_int(innwstr(wstr, n)); + report_wstr(wstr); } void -cmd_wget_wch(int nargs, char **args) +cmd_inwstr(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + wchar_t wstr[256]; + ARGC(0); - report_count(1); - report_error("UNSUPPORTED"); + report_count(2); + report_return(inwstr(wstr)); + report_wstr(wstr); +} + + +void +cmd_mvinnwstr(int nargs, char **args) +{ + wchar_t wstr[256]; + + ARGC(3); + ARG_INT(y); + ARG_INT(x); + ARG_INT(n); + + report_count(2); + report_int(mvinnwstr(y, x, wstr, n)); + report_wstr(wstr); } +void +cmd_mvinwstr(int nargs, char **args) +{ + wchar_t wstr[256]; + + ARGC(2); + ARG_INT(y); + ARG_INT(x); + + report_count(2); + report_return(mvinwstr(y, x, wstr)); + report_wstr(wstr); +} + void -cmd_getn_wstr(int nargs, char **args) +cmd_mvwinnwstr(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + wchar_t wstr[256]; - report_count(1); - report_error("UNSUPPORTED"); + ARGC(4); + ARG_WINDOW(win); + ARG_INT(y); + ARG_INT(x); + ARG_INT(n); + + report_count(2); + report_int(mvwinnwstr(win, y, x, wstr, n)); + report_wstr(wstr); } void -cmd_get_wstr(int nargs, char **args) +cmd_mvwinwstr(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + wchar_t wstr[256]; - report_count(1); - report_error("UNSUPPORTED"); + ARGC(3); + ARG_WINDOW(win); + ARG_INT(y); + ARG_INT(x); + + report_count(2); + report_return(mvwinwstr(win, y, x, wstr)); + report_wstr(wstr); } void -cmd_mvgetn_wstr(int nargs, char **args) +cmd_winnwstr(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + wchar_t wstr[256]; - report_count(1); - report_error("UNSUPPORTED"); + ARGC(2); + ARG_WINDOW(win); + ARG_INT(n); + + report_count(2); + report_int(winnwstr(win, wstr, n)); + report_wstr(wstr); } void -cmd_mvget_wstr(int nargs, char **args) +cmd_winwstr(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + wchar_t wstr[256]; - report_count(1); - report_error("UNSUPPORTED"); + ARGC(1); + ARG_WINDOW(win); + + report_count(2); + report_return(winwstr(win, wstr)); + report_wstr(wstr); } +/* cchar handling */ void -cmd_mvwgetn_wstr(int nargs, char **args) +cmd_setcchar(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + cchar_t wcval; - report_count(1); - report_error("UNSUPPORTED"); + ARGC(4); + ARG_WCHAR_STRING(wch); + ARG_INT(attrs); + ARG_SHORT(color_pair); + ARG_NULL(); + + report_count(2); + report_return(setcchar(&wcval, wch, attrs, color_pair, NULL)); + report_cchar(wcval); } void -cmd_mvwget_wstr(int nargs, char **args) +cmd_getcchar(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + wchar_t wch[256]; + attr_t attrs; + short color_pair; - report_count(1); - report_error("UNSUPPORTED"); + /* + * XXX - not handling passing of wch as NULL + */ + + ARGC(2); + ARG_CCHAR_STRING(wcval); + ARG_NULL(); + + report_count(4); + report_return(getcchar(wcval, wch, &attrs, &color_pair, NULL)); + report_wstr(wch); + report_int(attrs); + report_int(color_pair); } +/* misc */ void -cmd_wgetn_wstr(int nargs, char **args) +cmd_key_name(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(1); + ARG_WCHAR(w); report_count(1); - report_error("UNSUPPORTED"); + report_status(key_name(w)); } void -cmd_wget_wstr(int nargs, char **args) +cmd_border_set(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(8); + ARG_CCHAR_STRING(ls); + ARG_CCHAR_STRING(rs); + ARG_CCHAR_STRING(ts); + ARG_CCHAR_STRING(bs); + ARG_CCHAR_STRING(tl); + ARG_CCHAR_STRING(tr); + ARG_CCHAR_STRING(bl); + ARG_CCHAR_STRING(br); report_count(1); - report_error("UNSUPPORTED"); + report_return(border_set(ls, rs, ts, bs, tl, tr, bl, br)); } - void -cmd_in_wch(int nargs, char **args) +cmd_wborder_set(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(9); + ARG_WINDOW(win); + ARG_CCHAR_STRING(ls); + ARG_CCHAR_STRING(rs); + ARG_CCHAR_STRING(ts); + ARG_CCHAR_STRING(bs); + ARG_CCHAR_STRING(tl); + ARG_CCHAR_STRING(tr); + ARG_CCHAR_STRING(bl); + ARG_CCHAR_STRING(br); report_count(1); - report_error("UNSUPPORTED"); + report_return(wborder_set(win, ls, rs, ts, bs, tl, tr, bl, br)); } void -cmd_mvin_wch(int nargs, char **args) +cmd_box_set(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(3); + ARG_WINDOW(win); + ARG_CCHAR_STRING(verch); + ARG_CCHAR_STRING(horch); report_count(1); - report_error("UNSUPPORTED"); + report_return(box_set(win, verch, horch)); } void -cmd_mvwin_wch(int nargs, char **args) +cmd_erasewchar(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + wchar_t ch; - report_count(1); - report_error("UNSUPPORTED"); + ARGC(0); + + report_count(2); + report_return(erasewchar(&ch)); + report_wchar(ch); } void -cmd_win_wch(int nargs, char **args) +cmd_killwchar(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + wchar_t ch; - report_count(1); - report_error("UNSUPPORTED"); -} + ARGC(0); + report_count(2); + report_return(killwchar(&ch)); + report_wchar(ch); +} void -cmd_in_wchnstr(int nargs, char **args) +cmd_hline_set(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(2); + ARG_CCHAR_STRING(wch); + ARG_INT(n); report_count(1); - report_error("UNSUPPORTED"); + report_return(hline_set(wch, n)); } void -cmd_in_wchstr(int nargs, char **args) +cmd_mvhline_set(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(4); + ARG_INT(y); + ARG_INT(x); + ARG_CCHAR_STRING(wch); + ARG_INT(n); report_count(1); - report_error("UNSUPPORTED"); + report_return(mvhline_set(y, x, wch, n)); } void -cmd_mvin_wchnstr(int nargs, char **args) +cmd_mvvline_set(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(4); + ARG_INT(y); + ARG_INT(x); + ARG_CCHAR_STRING(wch); + ARG_INT(n); report_count(1); - report_error("UNSUPPORTED"); + report_return(mvvline_set(y, x, wch, n)); } void -cmd_mvin_wchstr(int nargs, char **args) +cmd_mvwhline_set(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(5); + ARG_WINDOW(win); + ARG_INT(y); + ARG_INT(x); + ARG_CCHAR_STRING(wch); + ARG_INT(n); report_count(1); - report_error("UNSUPPORTED"); + report_return(mvwhline_set(win, y, x, wch, n)); } void -cmd_mvwin_wchnstr(int nargs, char **args) +cmd_mvwvline_set(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(5); + ARG_WINDOW(win); + ARG_INT(y); + ARG_INT(x); + ARG_CCHAR_STRING(wch); + ARG_INT(n); report_count(1); - report_error("UNSUPPORTED"); + report_return(mvwvline_set(win, y, x, wch, n)); } void -cmd_mvwin_wchstr(int nargs, char **args) +cmd_vline_set(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(2); + ARG_CCHAR_STRING(wch); + ARG_INT(n); report_count(1); - report_error("UNSUPPORTED"); + report_return(vline_set(wch, n)); } void -cmd_win_wchnstr(int nargs, char **args) +cmd_whline_set(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(3); + ARG_WINDOW(win); + ARG_CCHAR_STRING(wch); + ARG_INT(n); report_count(1); - report_error("UNSUPPORTED"); + report_return(whline_set(win, wch, n)); } void -cmd_win_wchstr(int nargs, char **args) +cmd_wvline_set(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(3); + ARG_WINDOW(win); + ARG_CCHAR_STRING(wch); + ARG_INT(n); report_count(1); - report_error("UNSUPPORTED"); + report_return(wvline_set(win, wch, n)); } - void -cmd_innwstr(int nargs, char **args) +cmd_bkgrnd(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(1); + ARG_CCHAR_STRING(wch); report_count(1); - report_error("UNSUPPORTED"); + report_return(bkgrnd(wch)); } void -cmd_inwstr(int nargs, char **args) +cmd_bkgrndset(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(1); + ARG_CCHAR_STRING(wch); report_count(1); - report_error("UNSUPPORTED"); + bkgrndset(wch); + report_return(OK); } void -cmd_mvinnwstr(int nargs, char **args) +cmd_getbkgrnd(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + cchar_t wch; + ARGC(0); - report_count(1); - report_error("UNSUPPORTED"); + report_count(2); + report_return(getbkgrnd(&wch)); + report_cchar(wch); } void -cmd_mvinwstr(int nargs, char **args) +cmd_wbkgrnd(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(2); + ARG_WINDOW(win); + ARG_CCHAR_STRING(wch); report_count(1); - report_error("UNSUPPORTED"); + report_return(wbkgrnd(win, wch)); } void -cmd_mvwinnwstr(int nargs, char **args) +cmd_wbkgrndset(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(2); + ARG_WINDOW(win); + ARG_CCHAR_STRING(wch); report_count(1); - report_error("UNSUPPORTED"); + wbkgrndset(win, wch); + report_return(OK); } void -cmd_mvwinwstr(int nargs, char **args) +cmd_wgetbkgrnd(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + cchar_t wch; + ARGC(1); + ARG_WINDOW(win); - report_count(1); - report_error("UNSUPPORTED"); + report_count(2); + report_return(wgetbkgrnd(win, &wch)); + report_cchar(wch); } void -cmd_winnwstr(int nargs, char **args) +cmd_immedok(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(2); + ARG_WINDOW(win); + ARG_INT(bf); report_count(1); - report_error("UNSUPPORTED"); + immedok(win, bf); + report_return(OK); } - void -cmd_winwstr(int nargs, char **args) +cmd_syncok(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(2); + ARG_WINDOW(win); + ARG_INT(bf); report_count(1); - report_error("UNSUPPORTED"); + report_return(syncok(win, bf)); } - - -/* cchar handlgin */ void -cmd_setcchar(int nargs, char **args) +cmd_wcursyncup(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(1); + ARG_WINDOW(win); report_count(1); - report_error("UNSUPPORTED"); + wcursyncup(win); + report_return(OK); } - void -cmd_getcchar(int nargs, char **args) +cmd_wsyncup(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(1); + ARG_WINDOW(win); report_count(1); - report_error("UNSUPPORTED"); + wsyncup(win); + report_return(OK); } - - -/* misc */ void -cmd_key_name(int nargs, char **args) +cmd_wsyncdown(int nargs, char **args) { - int w; - - if (check_arg_count(nargs, 1) == 1) - return; - - if (sscanf(args[0], "%d", &w) == 0) { - report_count(1); - report_error("BAD ARGUMENT"); - return; - } + ARGC(1); + ARG_WINDOW(win); report_count(1); - report_status(key_name(w)); + wsyncdown(win); + report_return(OK); } +/* Soft label key routines */ void -cmd_border_set(int nargs, char **args) +cmd_slk_attroff(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(1); + ARG_CHTYPE(ch); report_count(1); - report_error("UNSUPPORTED"); + report_return(slk_attroff(ch)); } - void -cmd_wborder_set(int nargs, char **args) +cmd_slk_attr_off(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(1); + ARG_INT(attrs); report_count(1); - report_error("UNSUPPORTED"); + report_return(slk_attr_off(attrs, NULL)); } - void -cmd_box_set(int nargs, char **args) +cmd_slk_attron(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(1); + ARG_CHTYPE(ch); report_count(1); - report_error("UNSUPPORTED"); + report_return(slk_attron(ch)); } - void -cmd_erasewchar(int nargs, char **args) +cmd_slk_attr_on(int nargs, char **args) { - wchar_t ch; + ARGC(1); + ARG_INT(attrs); - if (check_arg_count(nargs, 0) == 1) - return; - - /* XXX - call2 */ - report_count(2); - report_return(erasewchar(&ch)); - report_int(ch); + report_count(1); + report_return(slk_attr_on(attrs, NULL)); } - void -cmd_killwchar(int nargs, char **args) +cmd_slk_attrset(int nargs, char **args) { - wchar_t ch; + ARGC(1); + ARG_CHTYPE(ch); - if (check_arg_count(nargs, 0) == 1) - return; - - /* XXX - call2 */ - report_count(2); - report_return(erasewchar(&ch)); - report_int(ch); + report_count(1); + report_return(slk_attrset(ch)); } - void -cmd_hline_set(int nargs, char **args) +cmd_slk_attr_set(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(2); + ARG_INT(attrs); + ARG_SHORT(color_pair_number); report_count(1); - report_error("UNSUPPORTED"); + report_return(slk_attr_set(attrs, color_pair_number, NULL)); } - void -cmd_mvhline_set(int nargs, char **args) +cmd_slk_clear(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(0); report_count(1); - report_error("UNSUPPORTED"); + report_return(slk_clear()); } - void -cmd_mvvline_set(int nargs, char **args) +cmd_slk_color(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(1); + ARG_SHORT(color_pair_number); report_count(1); - report_error("UNSUPPORTED"); + report_return(slk_color(color_pair_number)); } - void -cmd_mvwhline_set(int nargs, char **args) +cmd_slk_label(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + char *label; + ARGC(1); + ARG_INT(labnum); + + label = slk_label(labnum); report_count(1); - report_error("UNSUPPORTED"); + if (label == NULL) + report_status("NULL"); + else + report_status(label); } - void -cmd_mvwvline_set(int nargs, char **args) +cmd_slk_noutrefresh(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(0); report_count(1); - report_error("UNSUPPORTED"); + report_return(slk_noutrefresh()); } - void -cmd_vline_set(int nargs, char **args) +cmd_slk_refresh(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(0); report_count(1); - report_error("UNSUPPORTED"); + report_return(slk_refresh()); } - void -cmd_whline_set(int nargs, char **args) +cmd_slk_restore(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(0); report_count(1); - report_error("UNSUPPORTED"); + report_return(slk_restore()); } - void -cmd_wvline_set(int nargs, char **args) +cmd_slk_set(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(3); + ARG_INT(labnum); + ARG_STRING(label); + ARG_INT(justify); report_count(1); - report_error("UNSUPPORTED"); + report_return(slk_set(labnum, label, justify)); } - void -cmd_bkgrnd(int nargs, char **args) +cmd_slk_touch(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(0); report_count(1); - report_error("UNSUPPORTED"); + report_return(slk_touch()); } - void -cmd_bkgrndset(int nargs, char **args) +cmd_slk_wset(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(3); + ARG_INT(labnum); + ARG_WCHAR_STRING(label); + ARG_INT(justify); report_count(1); - report_error("UNSUPPORTED"); + report_return(slk_wset(labnum, label, justify)); } void -cmd_getbkgrnd(int nargs, char **args) +cmd_slk_init(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(1); + ARG_INT(fmt); report_count(1); - report_error("UNSUPPORTED"); + report_return(slk_init(fmt)); } - void -cmd_wbkgrnd(int nargs, char **args) +cmd_use_env(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(1); + ARG_IGNORE(); report_count(1); report_error("UNSUPPORTED"); } - void -cmd_wbkgrndset(int nargs, char **args) +cmd_ripoffline(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(1); + ARG_IGNORE(); report_count(1); report_error("UNSUPPORTED"); } - void -cmd_wgetbkgrnd(int nargs, char **args) +cmd_filter(int nargs, char **args) { - if (check_arg_count(nargs, 1) == 1) - return; + ARGC(0); report_count(1); - report_error("UNSUPPORTED"); + filter(); + report_return(OK); } diff --git a/lib/libcurses/slave/lint.lua b/lib/libcurses/slave/lint.lua new file mode 100644 --- /dev/null +++ b/lib/libcurses/slave/lint.lua @@ -0,0 +1,73 @@ +#! /usr/bin/lua +-- $NetBSD: lint.lua,v 1.6 2021/06/13 19:50:18 rillig Exp $ + +--[[ + +usage: lua ./lint.lua + +Check that the argument handling code does not contain unintended +inconsistencies. + +]] + +---@return string[] +local function load_lines(fname) + local lines = {} + + local f = assert(io.open(fname, "r")) + for line in f:lines() do + table.insert(lines, line) + end + + f:close() + + return lines +end + +local had_errors = false +local function print_error(fmt, ...) + print(fmt:format(...)) + had_errors = true +end + + +local function num(s) + if s == nil then return nil end + return tonumber(s) +end + + +-- After each macro ARGC, there must be the corresponding macros for ARG. +local function check_args() + local fname = "curses_commands.c" + local lines = load_lines(fname) + local curr_argc, curr_arg ---@type number|nil, number|nil + + for lineno, line in ipairs(lines) do + + local line_argc = num(line:match("^\tARGC%((%d+)")) + if line_argc and line_argc > 0 then + curr_argc, curr_arg = line_argc, 0 + goto next + end + + local line_arg = line:match("^\tARG_[%w_]+%(") + if line_arg and curr_arg then + curr_arg = curr_arg + 1 + if curr_arg == curr_argc then + curr_argc, curr_arg = nil, nil + end + elseif line_arg then + print_error("%s:%d: ARG without preceding ARGC", fname, lineno) + elseif curr_arg then + print_error("%s:%d: expecting ARG %d, got %s", + fname, lineno, curr_arg, line) + curr_argc, curr_arg = nil, nil + end + + ::next:: + end +end + +check_args() +os.exit(not had_errors) diff --git a/lib/libcurses/slave/slave.h b/lib/libcurses/slave/slave.h --- a/lib/libcurses/slave/slave.h +++ b/lib/libcurses/slave/slave.h @@ -1,7 +1,8 @@ -/* $NetBSD: slave.h,v 1.3 2011/09/15 11:46:19 blymn Exp $ */ +/* $NetBSD: slave.h,v 1.7 2021/02/13 08:17:15 rillig Exp $ */ /*- * Copyright 2009 Brett Lymn + * Copyright 2021 Roland Illig * * All rights reserved. * @@ -13,7 +14,7 @@ * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. The name of the author may not be used to endorse or promote products - * derived from this software withough specific prior written permission + * derived from this software without specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES @@ -25,16 +26,15 @@ * 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. - * - * */ -#ifndef CURTEST_SLAVE_H -#define CURTEST_SLAVE_H + +#ifndef CTF_SLAVE_H +#define CTF_SLAVE_H #include -#define READ_PIPE 0 -#define WRITE_PIPE 1 +extern int from_director; +extern int to_director; void command_execute(char *, int, char **); void report_count(int); @@ -43,6 +43,9 @@ void report_byte(chtype); void report_return(int); void report_nstr(chtype *); +void report_cchar(cchar_t); +void report_wchar(wchar_t); +void report_wstr(wchar_t *); void report_status(const char *); void report_ptr(void *); int check_arg_count(int, int); diff --git a/lib/libcurses/slave/slave.c b/lib/libcurses/slave/slave.c --- a/lib/libcurses/slave/slave.c +++ b/lib/libcurses/slave/slave.c @@ -1,7 +1,8 @@ -/* $NetBSD: slave.c,v 1.6 2011/09/15 11:46:19 blymn Exp $ */ +/* $NetBSD: slave.c,v 1.17 2021/06/13 12:46:01 rillig Exp $ */ /*- * Copyright 2009 Brett Lymn + * Copyright 2021 Roland Illig * * All rights reserved. * @@ -13,7 +14,7 @@ * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. The name of the author may not be used to endorse or promote products - * derived from this software withough specific prior written permission + * derived from this software without specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES @@ -25,13 +26,13 @@ * 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 #include #include #include +#include #include #include #include @@ -39,8 +40,9 @@ #include "returns.h" #include "slave.h" -int cmdpipe[2]; -int slvpipe[2]; +int from_director; +int to_director; +int initdone = 0; #if 0 static const char *returns_enum_names[] = { @@ -49,29 +51,94 @@ }; #endif +static bool +try_read_from_director(void *data, size_t n) +{ + ssize_t nread = read(from_director, data, n); + if (nread < 0) + err(2, "error reading from command pipe"); + if (nread == 0) + return false; + if ((size_t)nread != n) + errx(2, "short read from command pipe: expected %zu, got %zu", + n, (size_t)nread); + return true; +} + +static void +read_from_director(void *data, size_t n) +{ + ssize_t nread = read(from_director, data, n); + if (nread < 0) + err(2, "error reading from command pipe"); + if ((size_t)nread != n) + errx(2, "short read from command pipe: expected %zu, got %zu", + n, (size_t)nread); +} + +static bool +read_command_argument(char ***pargs, int argslen) +{ + int type, len; + char **args = *pargs; + + read_from_director(&type, sizeof(type)); + read_from_director(&len, sizeof(len)); + if (len < 0) + return false; + + args = realloc(args, (argslen + 1) * sizeof(args[0])); + if (args == NULL) + err(1, "slave realloc of args array failed"); + *pargs = args; + + if (type != data_null) { + args[argslen] = malloc(len + 1); + + if (args[argslen] == NULL) + err(1, "slave alloc of %d bytes for args failed", len); + } + + if (len == 0) { + if (type == data_null) + args[argslen] = NULL; + else + args[argslen][0] = '\0'; + } else { + read_from_director(args[argslen], len); + if (type != data_byte) + args[argslen][len] = '\0'; + + if (len == 6 && strcmp(args[argslen], "STDSCR") == 0) { + char *stdscr_buf; + if (asprintf(&stdscr_buf, "%p", stdscr) < 0) + err(2, "asprintf of stdscr failed"); + free(args[argslen]); + args[argslen] = stdscr_buf; + } + } + return true; +} + /* * Read the command pipe for the function to execute, gather the args * and then process the command. */ static void -process_commands(WINDOW *mainscr) +process_commands(void) { - int len, maxlen, argslen, i, ret, type; - char *cmdbuf, *tmpbuf, **args, **tmpargs; + int len, maxlen, argslen, i, type; + char *cmdbuf, *tmpbuf, **args; len = maxlen = 30; if ((cmdbuf = malloc(maxlen)) == NULL) err(1, "slave cmdbuf malloc failed"); - while(1) { - if (read(cmdpipe[READ_PIPE], &type, sizeof(int)) < 0) - err(1, "slave command type read failed"); - - if (type != ret_string) + while (try_read_from_director(&type, sizeof(type))) { + if (type != data_string) errx(1, "Unexpected type for command, got %d", type); - if (read(cmdpipe[READ_PIPE], &len, sizeof(int)) < 0) - err(1, "slave command len read failed"); + read_from_director(&len, sizeof(len)); if ((len + 1) > maxlen) { maxlen = len + 1; @@ -81,65 +148,13 @@ cmdbuf = tmpbuf; } - if (read(cmdpipe[READ_PIPE], cmdbuf, len) < 0) - err(1, "slave command read failed"); + read_from_director(cmdbuf, len); cmdbuf[len] = '\0'; argslen = 0; args = NULL; - do { - if (read(cmdpipe[READ_PIPE], &type, sizeof(int)) < 0) - err(1, "slave arg type read failed"); - - if (read(cmdpipe[READ_PIPE], &len, sizeof(int)) < 0) - err(1, "slave arg len read failed"); - - if (len >= 0) { - tmpargs = realloc(args, - (argslen + 1) * sizeof(char *)); - if (tmpargs == NULL) - err(1, "slave realloc of args array " - "failed"); - - args = tmpargs; - if (type != ret_null) { - args[argslen] = malloc(len + 1); - - if (args[argslen] == NULL) - err(1, "slave alloc of %d bytes" - " for args failed", len); - } - - if (len == 0) { - if (type == ret_null) - args[argslen] = NULL; - else - args[argslen][0] = '\0'; - } else { - read(cmdpipe[READ_PIPE], args[argslen], - len); - if (type != ret_byte) - args[argslen][len] = '\0'; - - if (len == 6) { - if (strcmp(args[argslen], - "STDSCR") == 0) { - ret = asprintf(&tmpbuf, - "%p", - stdscr); - if (ret < 0) - err(2, - "asprintf of stdscr failed"); - free(args[argslen]); - args[argslen] = tmpbuf; - } - } - } - - argslen++; - } - } - while(len >= 0); + while (read_command_argument(&args, argslen)) + argslen++; command_execute(cmdbuf, argslen, args); @@ -155,23 +170,14 @@ int main(int argc, char *argv[]) { - WINDOW *mainscr; - - if (argc != 5) { - fprintf(stderr, "Usage: %s slvout>\n", - getprogname()); + if (argc != 3) { + fprintf(stderr, "usage: %s \n", getprogname()); return 0; } - sscanf(argv[1], "%d", &cmdpipe[0]); - sscanf(argv[2], "%d", &cmdpipe[1]); - sscanf(argv[3], "%d", &slvpipe[0]); - sscanf(argv[4], "%d", &slvpipe[1]); - - mainscr = initscr(); - if (mainscr == NULL) - err(1, "initscr failed"); + sscanf(argv[1], "%d", &from_director); + sscanf(argv[2], "%d", &to_director); - process_commands(mainscr); + process_commands(); return 0; } diff --git a/lib/libcurses/t_curses.sh b/lib/libcurses/t_curses.sh old mode 100755 new mode 100644 --- a/lib/libcurses/t_curses.sh +++ b/lib/libcurses/t_curses.sh @@ -1,18 +1,38 @@ - h_run() { - file="$(atf_get_srcdir)/tests/${1}" + file=$1 + if [ -z "$2" ]; then + export LC_ALL=C + r_run $file + else + locale=`locale -a | grep -i $2` + if [ -z "${locale}" ]; then + atf_fail "test ${file} failed because locale ${locale} not available" + else + # export the locale and shift the parametes by two and pass the rest + export LC_ALL=$locale + shift 2 + r_run $file $@ + fi + fi +} +r_run() +{ + file="$(atf_get_srcdir)/tests/${1}" export COLUMNS=80 export LINES=24 - $(atf_get_srcdir)/director \ - -T $(atf_get_srcdir) \ - -t atf \ - -I $(atf_get_srcdir)/tests \ - -C $(atf_get_srcdir)/check_files \ - -s $(atf_get_srcdir)/slave $file || atf_fail "test ${file} failed" + $(atf_get_srcdir)/director $2 \ + -T $(atf_get_srcdir) \ + -t atf \ + -C $(atf_get_srcdir)/check_files \ + -s $(atf_get_srcdir)/slave $file || atf_fail "test ${file} failed" } +########################################## +# testframe utility functions +########################################## + atf_test_case startup startup_head() { @@ -23,6 +43,70 @@ h_run start } +atf_test_case window +window_head() +{ + atf_set "descr" "Checks window creation" +} +window_body() +{ + h_run window +} + +atf_test_case start_slk +start_slk_head() +{ + atf_set "descr" "Checks curses initialisation sequence with soft key labels" +} +start_slk_body() +{ + h_run start_slk +} + +atf_test_case window_hierarchy +window_hierarchy_head() +{ + atf_set "descr" "Checks creating a hierarchy of windows" +} +window_hierarchy_body() +{ + h_run window_hierarchy +} + +atf_test_case two_window +two_window_head() +{ + atf_set "descr" "Checks creating 2 windows" +} +two_window_body() +{ + h_run two_window +} + +atf_test_case varcheck +varcheck_head() +{ + atf_set "descr" "Checks if the testframe CHECK command works" +} +varcheck_body() +{ + h_run varcheck +} + +########################################## +# curses add characters to window routines +########################################## + +atf_test_case addbytes +addbytes_head() +{ + atf_set "descr" "Tests adding bytes to stdscr" +} +addbytes_body() +{ + h_run addbytes +} + atf_test_case addch addch_head() { @@ -33,6 +117,26 @@ h_run addch } +atf_test_case waddch +waddch_head() +{ + atf_set "descr" "Tests adding a chtype to window - tests mvwaddch too" +} +waddch_body() +{ + h_run waddch +} + +atf_test_case mvaddch +mvaddch_head() +{ + atf_set "descr" "Move the cursor and add a character to stdscr" +} +mvaddch_body() +{ + h_run mvaddch +} + atf_test_case addchstr addchstr_head() { @@ -43,6 +147,16 @@ h_run addchstr } +atf_test_case waddchstr +waddchstr_head() +{ + atf_set "descr" "Tests adding a chtype string to window" +} +waddchstr_body() +{ + h_run waddchstr +} + atf_test_case addchnstr addchnstr_head() { @@ -53,6 +167,56 @@ h_run addchnstr } +atf_test_case waddchnstr +waddchnstr_head() +{ + atf_set "descr" "Tests adding bytes from a chtype string to window" +} +waddchnstr_body() +{ + h_run waddchnstr +} + +atf_test_case mvaddchstr +mvaddchstr_head() +{ + atf_set "descr" "Move the cursor and add a ch string to stdscr" +} +mvaddchstr_body() +{ + h_run mvaddchstr +} + +atf_test_case mvwaddchstr +mvwaddchstr_head() +{ + atf_set "descr" "Move the cursor and add a ch string to window" +} +mvwaddchstr_body() +{ + h_run mvwaddchstr +} + +atf_test_case mvaddchnstr +mvaddchnstr_head() +{ + atf_set "descr" "Move the cursor and add a limited ch string to stdscr" +} +mvaddchnstr_body() +{ + h_run mvaddchnstr +} + +atf_test_case mvwaddchnstr +mvwaddchnstr_head() +{ + atf_set "descr" "Move the cursor and add a limited ch string to window" +} +mvwaddchnstr_body() +{ + h_run mvwaddchnstr +} + atf_test_case addstr addstr_head() { @@ -63,6 +227,36 @@ h_run addstr } +atf_test_case addwstr +addwstr_head() +{ + atf_set "descr" "Tests adding wide character string to stdscr" +} +addwstr_body() +{ + h_run addwstr en_US.UTF-8 +} + +atf_test_case waddstr +waddstr_head() +{ + atf_set "descr" "Tests adding bytes from a string to window" +} +waddstr_body() +{ + h_run waddstr +} + +atf_test_case waddwstr +waddwstr_head() +{ + atf_set "descr" "Tests adding wide character string to window" +} +waddwstr_body() +{ + h_run waddwstr en_US.UTF-8 +} + atf_test_case addnstr addnstr_head() { @@ -73,84 +267,218 @@ h_run addnstr } +atf_test_case addnwstr +addnwstr_head() +{ + atf_set "descr" "Tests adding wide characters from string to stdscr" +} +addnwstr_body() +{ + h_run addnwstr en_US.UTF-8 +} + +atf_test_case waddnstr +waddnstr_head() +{ + atf_set "descr" "Tests adding wide characters from string to window" +} +waddnstr_body() +{ + h_run waddnstr +} + +atf_test_case waddnwstr +waddnwstr_head() +{ + atf_set "descr" "Move the cursor and add wide characters from string to stdscr" +} +waddnwstr_body() +{ + h_run waddnwstr en_US.UTF-8 +} + +atf_test_case mvwaddnwstr +mvwaddnwstr_head() +{ + atf_set "descr" "Move the cursor and add wide characters from string to stdscr" +} +mvwaddnwstr_body() +{ + h_run mvwaddnwstr en_US.UTF-8 +} + +atf_test_case mvaddstr +mvaddstr_head() +{ + atf_set "descr" "Move the cursor and add a string to stdscr" +} +mvaddstr_body() +{ + h_run mvaddstr +} + +atf_test_case mvaddwstr +mvaddwstr_head() +{ + atf_set "descr" "Move the cursor and add wide character string to stdscr" +} +mvaddwstr_body() +{ + h_run mvaddwstr en_US.UTF-8 +} + +atf_test_case mvwaddwstr +mvwaddwstr_head() +{ + atf_set "descr" "Move the cursor and add wide character string to window" +} +mvwaddwstr_body() +{ + h_run mvwaddwstr en_US.UTF-8 +} + +atf_test_case mvwaddstr +mvwaddstr_head() +{ + atf_set "descr" "Move the cursor and add a string to window" +} +mvwaddstr_body() +{ + h_run mvwaddstr en_US.UTF-8 +} + +atf_test_case mvaddnstr +mvaddnstr_head() +{ + atf_set "descr" "Move the cursor and add a limited string to stdscr" +} +mvaddnstr_body() +{ + h_run mvaddnstr +} + +atf_test_case mvaddnwstr +mvaddnwstr_head() +{ + atf_set "descr" "Move the cursor and add wide characters from string to stdscr" +} +mvaddnwstr_body() +{ + h_run mvaddnwstr en_US.UTF-8 +} + +atf_test_case mvwaddnstr +mvwaddnstr_head() +{ + atf_set "descr" "Move the cursor and add wide characters from string to window" +} +mvwaddnstr_body() +{ + h_run mvwaddnstr +} + +atf_test_case add_wch +add_wch_head() +{ + atf_set "descr" "Test adding complex character to stdscr" +} +add_wch_body() +{ + h_run add_wch en_US.UTF-8 +} + +atf_test_case wadd_wch +wadd_wch_head() +{ + atf_set "descr" "Test adding complex character to window" +} +wadd_wch_body() +{ + h_run wadd_wch en_US.UTF-8 +} + +########################################## +# curses input stream routines +########################################## + atf_test_case getch getch_head() { - atf_set "descr" "Checks reading a character input" + atf_set "descr" "Checks reading a character input - tests mvgetch also" } getch_body() { h_run getch } -atf_test_case timeout -timeout_head() +atf_test_case wgetch +wgetch_head() { - atf_set "descr" "Checks timeout when reading a character" + atf_set "descr" "Checks reading a character input from window - tests mvwgetch also" } -timeout_body() +wgetch_body() { - h_run timeout + h_run wgetch } -atf_test_case window -window_head() +atf_test_case define_key +define_key_head() { - atf_set "descr" "Checks window creation" + atf_set "descr" "Check defining a key and removing the definition works" } -window_body() +define_key_body() { - h_run window + h_run define_key } -atf_test_case wborder -wborder_head() +atf_test_case keyok +keyok_head() { - atf_set "descr" "Checks drawing a border around a window" + atf_set "descr" "Check the ability to disable interpretation of a multichar key sequence" } -wborder_body() +keyok_body() { - h_run wborder + h_run keyok } -atf_test_case box -box_head() +atf_test_case getnstr +getnstr_head() { - atf_set "descr" "Checks drawing a box around a window" + atf_set "descr" "Check getting a string with a limit" } -box_body() +getnstr_body() { - h_run box + h_run getnstr } -atf_test_case wprintw -wprintw_head() +atf_test_case wgetnstr +wgetnstr_head() { - atf_set "descr" "Checks printing to a window" + atf_set "descr" "Check getting a string on window input with a limit" } -wprintw_body() +wgetnstr_body() { - h_run wprintw + h_run wgetnstr } -atf_test_case wscrl -wscrl_head() +atf_test_case mvgetnstr +mvgetnstr_head() { - atf_set "descr" "Check window scrolling" + atf_set "descr" "Move the cursor and get a limited number of characters" } -wscrl_body() +mvgetnstr_body() { - h_run wscrl + h_run mvgetnstr } -atf_test_case mvwin -mvwin_head() +atf_test_case mvwgetnstr +mvwgetnstr_head() { - atf_set "descr" "Check moving a window" + atf_set "descr" "Move the cursor and get a limited number of characters on window input" } -mvwin_body() +mvwgetnstr_body() { - h_run mvwin + h_run mvwgetnstr } atf_test_case getstr @@ -163,132 +491,1938 @@ h_run getstr } -atf_test_case termattrs -termattrs_head() +atf_test_case wgetstr +wgetstr_head() { - atf_set "descr" "Check the terminal attributes" + atf_set "descr" "Check getting a string from window input" } -termattrs_body() +wgetstr_body() { - h_run termattrs + h_run wgetstr } -atf_test_case assume_default_colors -assume_default_colors_head() +atf_test_case mvgetstr +mvgetstr_head() { - atf_set "descr" "Check setting the default color pair" + atf_set "descr" "Move the cursor and get characters" } -assume_default_colors_body() +mvgetstr_body() { - h_run assume_default_colors + h_run mvgetstr } -atf_test_case attributes -attributes_head() +atf_test_case mvwgetstr +mvwgetstr_head() { - atf_set "descr" "Check setting, clearing and getting of attributes" + atf_set "descr" "Move the cursor and get characters on window input" } -attributes_body() +mvwgetstr_body() { - h_run attributes + h_run mvwgetstr } -atf_test_case beep -beep_head() +atf_test_case keyname +keyname_head() { - atf_set "descr" "Check sending a beep" + atf_set "descr" "Convert integers into printable key names" } -beep_body() +keyname_body() { - h_run beep + h_run keyname } -atf_test_case background -background_head() +atf_test_case key_name +key_name_head() { - atf_set "descr" "Check setting background character and attributes for both stdscr and a window." + atf_set "descr" "Convert integers into printable key names" } -background_body() +key_name_body() { - h_run background + h_run key_name en_US.UTF-8 } -atf_test_case can_change_color -can_change_color_head() +atf_test_case keypad +keypad_head() { - atf_set "descr" "Check if the terminal can change colours" + atf_set "descr" "Checks enable/disable abbreviation of function keys - tests is_keypad also" } -can_change_color_body() +keypad_body() { - h_run can_change_color + h_run keypad } -atf_test_case cbreak -cbreak_head() +atf_test_case notimeout +notimeout_head() { - atf_set "descr" "Check cbreak mode works" + atf_set "descr" "Checks notimeout when reading a character" } -cbreak_body() +notimeout_body() { - h_run cbreak + h_run notimeout } -atf_test_case clear -clear_head() +atf_test_case timeout +timeout_head() { - atf_set "descr" "Check clear and erase work" + atf_set "descr" "Checks timeout when reading a character" } -clear_body() +timeout_body() { - h_run clear + h_run timeout } -atf_test_case copywin -copywin_head() +atf_test_case wtimeout +wtimeout_head() { - atf_set "descr" "Check all the modes of copying a window work" + atf_set "descr" "Checks timeout when reading a character on window" } -copywin_body() +wtimeout_body() { - h_run copywin + h_run wtimeout } -atf_test_case curs_set -curs_set_head() +atf_test_case nodelay +nodelay_head() { - atf_set "descr" "Check setting the cursor visibility works" + atf_set "descr" "Test that the nodelay call causes wget to not block" } -curs_set_body() +nodelay_body() { - h_run curs_set + h_run nodelay } -atf_init_test_cases() +atf_test_case unget_wch +unget_wch_head() { - atf_add_test_case startup - atf_add_test_case addch - atf_add_test_case addchstr - atf_add_test_case addchnstr + atf_set "descr" "Checks pushing of character into input queue - tests ungetch also" +} +unget_wch_body() +{ + h_run unget_wch en_US.UTF-8 +} + +atf_test_case getn_wstr +getn_wstr_head() +{ + atf_set "descr" "Checks getting limited characters from wide string through queue" +} +getn_wstr_body() +{ + h_run getn_wstr en_US.UTF-8 +} + +atf_test_case wgetn_wstr +wgetn_wstr_head() +{ + atf_set "descr" "Checks getting limited characters from wide string on window through queue" +} +wgetn_wstr_body() +{ + h_run wgetn_wstr en_US.UTF-8 +} + +atf_test_case get_wstr +get_wstr_head() +{ + atf_set "descr" "Checks getting characters from wide string through queue" +} +get_wstr_body() +{ + h_run get_wstr en_US.UTF-8 +} + +atf_test_case wget_wstr +wget_wstr_head() +{ + atf_set "descr" "Checks getting characters from wide string on window through queue" +} +wget_wstr_body() +{ + h_run wget_wstr en_US.UTF-8 +} + +atf_test_case mvgetn_wstr +mvgetn_wstr_head() +{ + atf_set "descr" "Move the cursor and get limited characters from wide string through queue" +} +mvgetn_wstr_body() +{ + h_run mvgetn_wstr en_US.UTF-8 +} + +atf_test_case mvwgetn_wstr +mvwgetn_wstr_head() +{ + atf_set "descr" "Move the cursor and get limited characters from wide string on window through queue" +} +mvwgetn_wstr_body() +{ + h_run mvwgetn_wstr en_US.UTF-8 +} + +atf_test_case mvget_wstr +mvget_wstr_head() +{ + atf_set "descr" "Move the cursor and get characters from wide string through queue" +} +mvget_wstr_body() +{ + h_run mvget_wstr en_US.UTF-8 +} + +atf_test_case mvwget_wstr +mvwget_wstr_head() +{ + atf_set "descr" "Move the cursor and get characters from wide string on window through queue" +} +mvwget_wstr_body() +{ + h_run mvwget_wstr en_US.UTF-8 +} + +atf_test_case get_wch +get_wch_head() +{ + atf_set "descr" "Checks reading a complex character through input queue" +} +get_wch_body() +{ + h_run get_wch en_US.UTF-8 +} + +########################################## +# curses read screen contents routines +########################################## + +atf_test_case inch +inch_head() +{ + atf_set "descr" "Get the character under the cursor on stdscr" +} +inch_body() +{ + h_run inch +} + +atf_test_case winch +winch_head() +{ + atf_set "descr" "Get the character under the cursor on window" +} +winch_body() +{ + h_run winch +} + +atf_test_case mvinch +mvinch_head() +{ + atf_set "descr" "Move the cursor and get the character under the cursor on stdscr" +} +mvinch_body() +{ + h_run mvinch +} + +atf_test_case mvwinch +mvwinch_head() +{ + atf_set "descr" "Move the cursor and get the character under the cursor on window" +} +mvwinch_body() +{ + h_run mvwinch +} + +atf_test_case inchnstr +inchnstr_head() +{ + atf_set "descr" "Get a limited chtype string from the stdscr - tests inchstr too" +} +inchnstr_body() +{ + h_run inchnstr +} + +atf_test_case winchnstr +winchnstr_head() +{ + atf_set "descr" "Get a limited chtype string from the window - tests winchstr too" +} +winchnstr_body() +{ + h_run winchnstr +} + +atf_test_case mvinchnstr +mvinchnstr_head() +{ + atf_set "descr" "Move the cursor read characters from stdscr - tests both mvinchstr and mvinchnstr" +} +mvinchnstr_body() +{ + h_run mvinchnstr +} + +atf_test_case mvwinchnstr +mvwinchnstr_head() +{ + atf_set "descr" "Move the cursor read characters from window - tests both mvinchstr and mvinchnstr" +} +mvwinchnstr_body() +{ + h_run mvwinchnstr +} + +atf_test_case innstr +innstr_head() +{ + atf_set "descr" "Get a limited string starting at the cursor from stdscr - tests instr also" +} +innstr_body() +{ + h_run innstr +} + +atf_test_case winnstr +winnstr_head() +{ + atf_set "descr" "Get a limited string starting at the cursor from window - tests instr also" +} +winnstr_body() +{ + h_run winnstr +} + +atf_test_case mvinnstr +mvinnstr_head() +{ + atf_set "descr" "Move the cursor read limited characters from stdscr - tests mvinstr also" +} +mvinnstr_body() +{ + h_run mvinnstr +} + +atf_test_case mvwinnstr +mvwinnstr_head() +{ + atf_set "descr" "Move the cursor read limited characters from window - tests mvwinstr also" +} +mvwinnstr_body() +{ + h_run mvwinnstr +} + +atf_test_case in_wch +in_wch_head() +{ + atf_set "descr" "Read the complex character from stdscr - tests mvin_wch too" +} +in_wch_body() +{ + h_run in_wch en_US.UTF-8 +} + +atf_test_case win_wch +win_wch_head() +{ + atf_set "descr" "Read the complex character from window - tests mvwin_wch too" +} +win_wch_body() +{ + h_run win_wch en_US.UTF-8 +} + +atf_test_case innwstr +innwstr_head() +{ + atf_set "descr" "Get a limited wide string starting at the cursor from stdscr" +} +innwstr_body() +{ + h_run innwstr en_US.UTF-8 +} + +atf_test_case winnwstr +winnwstr_head() +{ + atf_set "descr" "Get a limited wide string starting at the cursor from window" +} +winnwstr_body() +{ + h_run winnwstr en_US.UTF-8 +} + +atf_test_case inwstr +inwstr_head() +{ + atf_set "descr" "Get a wide string starting at the cursor from stdscr" +} +inwstr_body() +{ + h_run inwstr en_US.UTF-8 +} + +atf_test_case winwstr +winwstr_head() +{ + atf_set "descr" "Get a wide string starting at the cursor from window" +} +winwstr_body() +{ + h_run winwstr en_US.UTF-8 +} + +atf_test_case mvinnwstr +mvinnwstr_head() +{ + atf_set "descr" "Move the cursor and get a limited wide string starting at the cursor from stdscr" +} +mvinnwstr_body() +{ + h_run mvinnwstr en_US.UTF-8 +} + +atf_test_case mvwinnwstr +mvwinnwstr_head() +{ + atf_set "descr" "Move the cursor and get a limited wide string starting at the cursor from window" +} +mvwinnwstr_body() +{ + h_run mvwinnwstr en_US.UTF-8 +} + +atf_test_case mvinwstr +mvinwstr_head() +{ + atf_set "descr" "Move the cursor and get a wide string starting at the cursor from stdscr" +} +mvinwstr_body() +{ + h_run mvinwstr en_US.UTF-8 +} + +atf_test_case mvwinwstr +mvwinwstr_head() +{ + atf_set "descr" "Move the cursor and get a limited wide string starting at the cursor from window" +} +mvwinwstr_body() +{ + h_run mvwinwstr en_US.UTF-8 +} + +########################################## +# curses insert character to window routines +########################################## + +atf_test_case insch +insch_head() +{ + atf_set "descr" "Tests inserting a chtype to stdscr" +} +insch_body() +{ + h_run insch +} + +atf_test_case winsch +winsch_head() +{ + atf_set "descr" "Tests inserting a chtype to window" +} +winsch_body() +{ + h_run winsch +} + +atf_test_case mvinsch +mvinsch_head() +{ + atf_set "descr" "Move the cursor and insert a chtype to stdscr" +} +mvinsch_body() +{ + h_run mvinsch +} + +atf_test_case mvwinsch +mvwinsch_head() +{ + atf_set "descr" "Move the cursor and insert a chtype to window" +} +mvwinsch_body() +{ + h_run mvwinsch +} + +atf_test_case ins_wch +ins_wch_head() +{ + atf_set "descr" "Tests inserting complex character to stdscr" +} +ins_wch_body() +{ + h_run ins_wch en_US.UTF-8 +} + +atf_test_case wins_wch +wins_wch_head() +{ + atf_set "descr" "Tests inserting complex character to window" +} +wins_wch_body() +{ + h_run wins_wch en_US.UTF-8 +} + +atf_test_case mvins_wch +mvins_wch_head() +{ + atf_set "descr" "Move the cursor and insert complex character to stdscr" +} +mvins_wch_body() +{ + h_run mvins_wch en_US.UTF-8 +} + +atf_test_case mvwins_wch +mvwins_wch_head() +{ + atf_set "descr" "Move the cursor and insert complex character to window" +} +mvwins_wch_body() +{ + h_run mvwins_wch en_US.UTF-8 +} + +atf_test_case ins_nwstr +ins_nwstr_head() +{ + atf_set "descr" "Tests inserting a limited wide character string to stdscr" +} +ins_nwstr_body() +{ + h_run ins_nwstr en_US.UTF-8 +} + +atf_test_case wins_nwstr +wins_nwstr_head() +{ + atf_set "descr" "Tests inserting a limited wide character string to window" +} +wins_nwstr_body() +{ + h_run wins_nwstr en_US.UTF-8 +} + +atf_test_case ins_wstr +ins_wstr_head() +{ + atf_set "descr" "Tests inserting a wide character string to stdscr" +} +ins_wstr_body() +{ + h_run ins_wstr en_US.UTF-8 +} + +atf_test_case wins_wstr +wins_wstr_head() +{ + atf_set "descr" "Tests inserting a wide character string to window" +} +wins_wstr_body() +{ + h_run wins_wstr en_US.UTF-8 +} + +atf_test_case mvins_nwstr +mvins_nwstr_head() +{ + atf_set "descr" "Move the cursor and insert a limited wide character string to stdscr" +} +mvins_nwstr_body() +{ + h_run mvins_nwstr en_US.UTF-8 +} + +atf_test_case mvwins_nwstr +mvwins_nwstr_head() +{ + atf_set "descr" "Move the cursor and insert a limited wide character string to window" +} +mvwins_nwstr_body() +{ + h_run mvwins_nwstr en_US.UTF-8 +} + +atf_test_case mvins_wstr +mvins_wstr_head() +{ + atf_set "descr" "Move the cursor and insert a wide character string to stdscr" +} +mvins_wstr_body() +{ + h_run mvins_wstr en_US.UTF-8 +} + +atf_test_case mvwins_wstr +mvwins_wstr_head() +{ + atf_set "descr" "Move the cursor and insert a wide character string to window" +} +mvwins_wstr_body() +{ + h_run mvwins_wstr en_US.UTF-8 +} + +########################################## +# curses delete characters routines +########################################## + +atf_test_case delch +delch_head() +{ + atf_set "descr" "Tests deleting a character from stdscr and window both" +} +delch_body() +{ + h_run delch +} + +atf_test_case mvdelch +mvdelch_head() +{ + atf_set "descr" "Move the cursor, deletes the character from stdscr and window" +} +mvdelch_body() +{ + h_run mvdelch +} + +########################################## +# curses terminal manipulation routines +########################################## + +atf_test_case beep +beep_head() +{ + atf_set "descr" "Check sending a beep" +} +beep_body() +{ + h_run beep +} + +atf_test_case flash +flash_head() +{ + atf_set "descr" "Validate curses can flash the screen" +} +flash_body() +{ + h_run flash +} + +atf_test_case curs_set +curs_set_head() +{ + atf_set "descr" "Check setting the cursor visibility works" +} +curs_set_body() +{ + h_run curs_set +} + +atf_test_case delay_output +delay_output_head() +{ + atf_set "descr" "Tests pausing the output" +} +delay_output_body() +{ + h_run delay_output +} + +atf_test_case erasechar +erasechar_head() +{ + atf_set "descr" "Validate erase char can be retrieved" +} +erasechar_body() +{ + h_run erasechar +} + +atf_test_case erasewchar +erasewchar_head() +{ + atf_set "descr" "Validate erase wide char can be retrieved" +} +erasewchar_body() +{ + h_run erasewchar en_US.UTF-8 +} + +atf_test_case echochar +echochar_head() +{ + atf_set "descr" "echo single-byte character and rendition to a stdscr/window and refresh" +} +echochar_body() +{ + h_run echochar +} + +atf_test_case echo_wchar +echo_wchar_head() +{ + atf_set "descr" "echo wide character and rendition to a stdscr and refresh" +} +echo_wchar_body() +{ + h_run echo_wchar en_US.UTF-8 +} + +atf_test_case wecho_wchar +wecho_wchar_head() +{ + atf_set "descr" "echo wide character and rendition to a window and refresh" +} +wecho_wchar_body() +{ + h_run wecho_wchar en_US.UTF-8 +} + +atf_test_case halfdelay +halfdelay_head() +{ + atf_set "descr" "Tests setting the input mode to half delay" +} +halfdelay_body() +{ + h_run halfdelay +} + +atf_test_case has_ic +has_ic_head() +{ + atf_set "descr" "Check if the terminal can insert characters and lines" +} +has_ic_body() +{ + h_run has_ic +} + +atf_test_case killchar +killchar_head() +{ + atf_set "descr" "Get the value of the terminals kill character" +} +killchar_body() +{ + h_run killchar +} + +atf_test_case killwchar +killwchar_head() +{ + atf_set "descr" "Get the value of the terminals wide kill character" +} +killwchar_body() +{ + h_run killwchar en_US.UTF-8 +} + +atf_test_case meta +meta_head() +{ + atf_set "descr" "Check setting and clearing the meta flag on a window" +} +meta_body() +{ + h_run meta +} + +atf_test_case cbreak +cbreak_head() +{ + atf_set "descr" "Check cbreak mode works" +} +cbreak_body() +{ + h_run cbreak +} + +atf_test_case nocbreak +nocbreak_head() +{ + atf_set "descr" "Test that the nocbreak call returns the terminal to canonical character processing" +} +nocbreak_body() +{ + h_run nocbreak +} + +########################################## +# curses general attribute manipulation routines +########################################## + +atf_test_case attributes +attributes_head() +{ + atf_set "descr" "Check setting, clearing and getting of attributes of stdscr" +} +attributes_body() +{ + h_run attributes +} + +atf_test_case wattributes +wattributes_head() +{ + atf_set "descr" "Check setting, clearing and getting of attributes of window" +} +wattributes_body() +{ + h_run wattributes +} + +atf_test_case getattrs +getattrs_head() +{ + atf_set "descr" "Validate curses can get and set attributes on a window" +} +getattrs_body() +{ + h_run getattrs +} + +atf_test_case color_set +color_set_head() +{ + atf_set "descr" "Validate curses can set the color pair for stdscr" +} +color_set_body() +{ + h_run color_set +} + +atf_test_case wcolor_set +wcolor_set_head() +{ + atf_set "descr" "Validate curses can set the color pair for window" +} +wcolor_set_body() +{ + h_run wcolor_set +} + +atf_test_case termattrs +termattrs_head() +{ + atf_set "descr" "Check the terminal attributes" +} +termattrs_body() +{ + h_run termattrs +} + +########################################## +# curses on-screen attribute manipulation routines +########################################## + +atf_test_case chgat +chgat_head() +{ + atf_set "descr" "Check changing attributes works on stdscr" +} +chgat_body() +{ + h_run chgat +} + +atf_test_case wchgat +wchgat_head() +{ + atf_set "descr" "Check changing attributes works on window" +} +wchgat_body() +{ + h_run wchgat +} + +atf_test_case mvchgat +mvchgat_head() +{ + atf_set "descr" "Move the cursor and change the attributes on the screen" +} +mvchgat_body() +{ + h_run mvchgat +} + +atf_test_case mvwchgat +mvwchgat_head() +{ + atf_set "descr" "Move the cursor and change the attributes on the window" +} +mvwchgat_body() +{ + h_run mvwchgat +} + +########################################## +# curses standout attribute manipulation routines +########################################## + +atf_test_case standout +standout_head() +{ + atf_set "descr" "Checks tuning on/off of standard attribute on stdscr" +} +standout_body() +{ + h_run standout +} + +atf_test_case wstandout +wstandout_head() +{ + atf_set "descr" "Checks tuning on/off of standard attribute on window" +} +wstandout_body() +{ + h_run wstandout +} + +########################################## +# curses color manipulation routines +########################################## + +atf_test_case has_colors +has_colors_head() +{ + atf_set "descr" "Check if the terminal can support colours" +} +has_colors_body() +{ + h_run has_colors +} + +atf_test_case can_change_color +can_change_color_head() +{ + atf_set "descr" "Check if the terminal can change colours" +} +can_change_color_body() +{ + h_run can_change_color +} + +atf_test_case start_color +start_color_head() +{ + atf_set "descr" "Check if curses can enable use of colours" +} +start_color_body() +{ + h_run start_color +} + +atf_test_case pair_content +pair_content_head() +{ + atf_set "descr" "Checks color pair initialization and retrieval" +} +pair_content_body() +{ + h_run pair_content +} + +atf_test_case init_color +init_color_head() +{ + atf_set "descr" "Set a custom color entry" +} +init_color_body() +{ + h_run init_color +} + +atf_test_case color_content +color_content_head() +{ + atf_set "descr" "Check if curses can extract the color intensity from colors" +} +color_content_body() +{ + h_run color_content +} + +atf_test_case assume_default_colors +assume_default_colors_head() +{ + atf_set "descr" "Check setting the default color pair" +} +assume_default_colors_body() +{ + h_run assume_default_colors +} + +########################################## +# curses clear window routines +########################################## + +atf_test_case clear +clear_head() +{ + atf_set "descr" "Check clear,erase,clrtobot,clrtoeol work - tests window routines too" +} +clear_body() +{ + h_run clear +} + +atf_test_case clearok +clearok_head() +{ + atf_set "descr" "Check clearing of screen during a refresh if correspnding flag is set" +} +clearok_body() +{ + h_run clearok +} + +########################################## +# curses terminal update routines +########################################## + +atf_test_case doupdate +doupdate_head() +{ + atf_set "descr" "Check doupdate performs an update - test wnoutrefresh too" +} +doupdate_body() +{ + h_run doupdate +} + +atf_test_case immedok +immedok_head() +{ + atf_set "descr" "Checks if the screen is refreshed whenever window is changed" +} +immedok_body() +{ + h_run immedok +} + +atf_test_case leaveok +leaveok_head() +{ + atf_set "descr" "Checks cursor positioning from refresh operations - tests is_leaveok too" +} +leaveok_body() +{ + h_run leaveok +} + +########################################## +# curses window scrolling routines +########################################## + +atf_test_case wscrl +wscrl_head() +{ + atf_set "descr" "Check window scrolling" +} +wscrl_body() +{ + h_run wscrl +} + +atf_test_case scroll +scroll_head() +{ + atf_set "descr" "Checks scrolling" +} +scroll_body() +{ + h_run scroll +} + +atf_test_case setscrreg +setscrreg_head() +{ + atf_set "descr" "Checks if setting the scrolling region works for stdscr" +} +setscrreg_body() +{ + h_run setscrreg +} + +atf_test_case wsetscrreg +wsetscrreg_head() +{ + atf_set "descr" "Checks if setting the scrolling region works for window" +} +wsetscrreg_body() +{ + h_run wsetscrreg +} + +########################################## +# curses window modification routines +########################################## + +atf_test_case touchline +touchline_head() +{ + atf_set "descr" "Checks touchline to touch lines" +} +touchline_body() +{ + h_run touchline +} + +atf_test_case touchoverlap +touchoverlap_head() +{ + atf_set "descr" "Check touching of partial and full overlap of windows" +} +touchoverlap_body() +{ + h_run touchoverlap +} + +atf_test_case touchwin +touchwin_head() +{ + atf_set "descr" "Tests touching of window to completely refresh it" +} +touchwin_body() +{ + h_run touchwin +} + +atf_test_case untouchwin +untouchwin_head() +{ + atf_set "descr" "Tests untouching the changes made to window so they are not reflected in refresh" +} +untouchwin_body() +{ + h_run untouchwin +} + +atf_test_case wtouchln +wtouchln_head() +{ + atf_set "descr" "Tests touching of mulitple lines in window" +} +wtouchln_body() +{ + h_run wtouchln +} + +atf_test_case is_linetouched +is_linetouched_head() +{ + atf_set "descr" "Check if a line has been modified in a window" +} +is_linetouched_body() +{ + h_run is_linetouched +} + +atf_test_case is_wintouched +is_wintouched_head() +{ + atf_set "descr" "Check if a window has been modified" +} +is_wintouched_body() +{ + h_run is_wintouched +} + +atf_test_case redrawwin +redrawwin_head() +{ + atf_set "descr" "Tests marking whole window as touched and redraw it" +} +redrawwin_body() +{ + h_run redrawwin +} + +atf_test_case wredrawln +wredrawln_head() +{ + atf_set "descr" "Tests marking line in window as touched and redraw it" +} +wredrawln_body() +{ + h_run wredrawln +} + +########################################## +# curses soft label key routines +########################################## + +atf_test_case slk +slk_head() +{ + atf_set "descr" "Tests routines related to soft key labels" +} +slk_body() +{ + h_run slk en_US.UTF-8 +} + +########################################## +# curses draw lines on windows routines +########################################## + +atf_test_case hline +hline_head() +{ + atf_set "descr" "Draw a horizontal line on stdscr" +} +hline_body() +{ + h_run hline +} + +atf_test_case whline +whline_head() +{ + atf_set "descr" "Draw a horizontal line on window - tests mvwhline too" +} +whline_body() +{ + h_run whline +} + +atf_test_case mvhline +mvhline_head() +{ + atf_set "descr" "Move the cursor and draw a horizontal line" +} +mvhline_body() +{ + h_run mvhline +} + +atf_test_case wvline +wvline_head() +{ + atf_set "descr" "Draw a vertical line on window - tests mvwvline too" +} +wvline_body() +{ + h_run wvline +} + +atf_test_case mvvline +mvvline_head() +{ + atf_set "descr" "Move the cursor and draw a vertical line - tests vline too" +} +mvvline_body() +{ + h_run mvvline +} + +atf_test_case hline_set +hline_set_head() +{ + atf_set "descr" "Draws a horizontal line on stdscr using complex character" +} +hline_set_body() +{ + h_run hline_set en_US.UTF-8 +} + +atf_test_case whline_set +whline_set_head() +{ + atf_set "descr" "Draws a horizontal line on window using complex character" +} +whline_set_body() +{ + h_run whline_set en_US.UTF-8 +} + +atf_test_case vline_set +vline_set_head() +{ + atf_set "descr" "Draws a vertical line on stdscr using complex character" +} +vline_set_body() +{ + h_run vline_set en_US.UTF-8 +} + +atf_test_case wvline_set +wvline_set_head() +{ + atf_set "descr" "Draws a vertical line on window using complex character" +} +wvline_set_body() +{ + h_run wvline_set en_US.UTF-8 +} + +########################################## +# curses pad routines +########################################## + +atf_test_case pad +pad_head() +{ + atf_set "descr" "Test the newpad, subpad, pnoutrefresh and prefresh functions" +} +pad_body() +{ + h_run pad +} + +atf_test_case pechochar +pechochar_head() +{ + atf_set "descr" "Tests pechochar and pecho_wchar functions" +} +pechochar_body() +{ + h_run pechochar en_US.UTF-8 +} + +########################################## +# curses cursor and window location and positioning routines +########################################## + +atf_test_case cursor +cursor_head() +{ + atf_set "descr" "Tests cursor positioning and window location routines" +} +cursor_body() +{ + h_run cursor +} + +atf_test_case getcurx +getcurx_head() +{ + atf_set "descr" "Validate curses getting cursor locations in a window" +} +getcurx_body() +{ + h_run getcurx +} + +atf_test_case getmaxx +getmaxx_head() +{ + atf_set "descr" "Validate curses getting the maximum x value of a window" +} +getmaxx_body() +{ + h_run getmaxx +} + +atf_test_case getmaxy +getmaxy_head() +{ + atf_set "descr" "Validate curses getting the maximum y value of a window" +} +getmaxy_body() +{ + h_run getmaxy +} + +atf_test_case getparx +getparx_head() +{ + atf_set "descr" "Check getting the location of a window relative to its parent" +} +getparx_body() +{ + h_run getparx +} + +atf_test_case getbegy +getbegy_head() +{ + atf_set "descr" "Validate curses getting the maximum y value of a window" +} +getbegy_body() +{ + h_run getbegy +} + +atf_test_case getbegx +getbegx_head() +{ + atf_set "descr" "Validate curses getting the maximum y value of a window" +} +getbegx_body() +{ + h_run getbegx +} + +atf_test_case mvcur +mvcur_head() +{ + atf_set "descr" "Move the cursor on the screen" +} +mvcur_body() +{ + h_run mvcur +} + + +########################################## +# curses window routines +########################################## + +atf_test_case copywin +copywin_head() +{ + atf_set "descr" "Check all the modes of copying a window work" +} +copywin_body() +{ + h_run copywin +} + +atf_test_case dupwin +dupwin_head() +{ + atf_set "descr" "Check duplicating a window works" +} +dupwin_body() +{ + h_run dupwin +} + +atf_test_case delwin +delwin_head() +{ + atf_set "descr" "Tests deleting a window" +} +delwin_body() +{ + h_run delwin +} + +atf_test_case derwin +derwin_head() +{ + atf_set "descr" "Check derived subwindow creation behaves correctly." +} +derwin_body() +{ + h_run derwin +} + +atf_test_case mvwin +mvwin_head() +{ + atf_set "descr" "Check moving a window" +} +mvwin_body() +{ + h_run mvwin +} + +atf_test_case mvderwin +mvderwin_head() +{ + atf_set "descr" "Move the mapping of a region relative to the parent" +} +mvderwin_body() +{ + h_run mvderwin +} + +atf_test_case newwin +newwin_head() +{ + atf_set "descr" "Check creating a new window" +} +newwin_body() +{ + h_run newwin +} + +atf_test_case overlay +overlay_head() +{ + atf_set "descr" "Checks overlaying the overlapping portion of two windows" +} +overlay_body() +{ + h_run overlay +} + +atf_test_case overwrite +overwrite_head() +{ + atf_set "descr" "Checks overwriting the overlapping portion of two windows" +} +overwrite_body() +{ + h_run overwrite en_US.UTF-8 +} + +atf_test_case getwin +getwin_head() +{ + atf_set "descr" "Tests dumping window to, and reloading window from, a file" +} +getwin_body() +{ + h_run getwin +} + +########################################## +# curses background attribute manipulation routines +########################################## + +atf_test_case background +background_head() +{ + atf_set "descr" "Check setting background character and attributes for both stdscr and a window." +} +background_body() +{ + h_run background +} + +atf_test_case bkgdset +bkgdset_head() +{ + atf_set "descr" "Validate curses set the background attributes on stdscr" +} +bkgdset_body() +{ + h_run bkgdset +} + +atf_test_case getbkgd +getbkgd_head() +{ + atf_set "descr" "Validate curses getting the background attributes on stdscr" +} +getbkgd_body() +{ + h_run getbkgd +} + +########################################## +# curses border drawing routines +########################################## + +atf_test_case box +box_head() +{ + atf_set "descr" "Checks drawing a box around a window" +} +box_body() +{ + h_run box +} + +atf_test_case box_set +box_set_head() +{ + atf_set "descr" "Checks drawing the box from complex character" +} +box_set_body() +{ + h_run box_set en_US.UTF-8 +} + +atf_test_case wborder +wborder_head() +{ + atf_set "descr" "Checks drawing a border around a window" +} +wborder_body() +{ + h_run wborder +} + +atf_test_case border_set +border_set_head() +{ + atf_set "descr" "Checks drawing borders from complex characters and renditions on stdscr" +} +border_set_body() +{ + h_run border_set en_US.UTF-8 +} + +atf_test_case wborder_set +wborder_set_head() +{ + atf_set "descr" "Checks drawing borders from complex characters and renditions on window" +} +wborder_set_body() +{ + h_run wborder_set en_US.UTF-8 +} + +########################################## +# curses insert or delete lines routines +########################################## + +atf_test_case deleteln +deleteln_head() +{ + atf_set "descr" "Checks curses can delete lines from stdscr and window both" +} +deleteln_body() +{ + h_run deleteln +} + +atf_test_case insertln +insertln_head() +{ + atf_set "descr" "Checks curses can insert lines from stdscr and window both" +} +insertln_body() +{ + h_run insertln +} + +atf_test_case insdelln +insdelln_head() +{ + atf_set "descr" "Checks curses can insert/delete lines from stdscr and window both based on argument" +} +insdelln_body() +{ + h_run insdelln +} + +########################################## +# curses print formatted strings on windows routines +########################################## + +atf_test_case wprintw +wprintw_head() +{ + atf_set "descr" "Checks printing to a window" +} +wprintw_body() +{ + h_run wprintw +} + +atf_test_case mvprintw +mvprintw_head() +{ + atf_set "descr" "Move the cursor and print a string" +} +mvprintw_body() +{ + h_run mvprintw +} + +atf_test_case mvscanw +mvscanw_head() +{ + atf_set "descr" "Move the cursor and scan for input patterns" +} +mvscanw_body() +{ + h_run mvscanw +} + +########################################## +# curses underscore attribute manipulation routines +########################################## + +atf_test_case underscore +underscore_head() +{ + atf_set "descr" "Manipulate underscore attribute on stdscr" +} +underscore_body() +{ + h_run underscore +} + +atf_test_case wunderscore +wunderscore_head() +{ + atf_set "descr" "Manipulate underscore attribute on window" +} +wunderscore_body() +{ + h_run wunderscore +} + +atf_init_test_cases() +{ + # testframe utility functions + atf_add_test_case startup + atf_add_test_case window + atf_add_test_case start_slk + atf_add_test_case window_hierarchy + atf_add_test_case two_window + atf_add_test_case varcheck + + # curses add characters to window routines + atf_add_test_case addbytes + atf_add_test_case addch + atf_add_test_case waddch + atf_add_test_case mvaddch + atf_add_test_case addchstr + atf_add_test_case waddchstr + atf_add_test_case addchnstr + atf_add_test_case waddchnstr + atf_add_test_case mvaddchstr + atf_add_test_case mvwaddchstr + atf_add_test_case mvaddchnstr + atf_add_test_case mvwaddchnstr atf_add_test_case addstr + atf_add_test_case addwstr + atf_add_test_case waddstr + atf_add_test_case waddwstr atf_add_test_case addnstr + atf_add_test_case addnwstr + atf_add_test_case waddnstr + atf_add_test_case waddnwstr + atf_add_test_case mvwaddnwstr + atf_add_test_case mvaddstr + atf_add_test_case mvaddwstr + atf_add_test_case mvwaddwstr + atf_add_test_case mvwaddstr + atf_add_test_case mvaddnstr + atf_add_test_case mvaddnwstr + atf_add_test_case mvwaddnstr + atf_add_test_case add_wch + atf_add_test_case wadd_wch + + # curses input stream routines atf_add_test_case getch - atf_add_test_case timeout - atf_add_test_case window - atf_add_test_case wborder - atf_add_test_case box - atf_add_test_case wprintw - atf_add_test_case wscrl - atf_add_test_case mvwin + #atf_add_test_case wgetch [test is missing] + atf_add_test_case define_key + atf_add_test_case keyok + atf_add_test_case getnstr + atf_add_test_case wgetnstr + atf_add_test_case mvgetnstr + atf_add_test_case mvwgetnstr atf_add_test_case getstr + atf_add_test_case wgetstr + atf_add_test_case mvgetstr + atf_add_test_case mvwgetstr + atf_add_test_case keyname + atf_add_test_case key_name + atf_add_test_case keypad + atf_add_test_case notimeout + atf_add_test_case timeout + atf_add_test_case wtimeout + atf_add_test_case nodelay + atf_add_test_case unget_wch + atf_add_test_case getn_wstr + atf_add_test_case wgetn_wstr + atf_add_test_case get_wstr + atf_add_test_case wget_wstr + atf_add_test_case mvgetn_wstr + atf_add_test_case mvwgetn_wstr + atf_add_test_case mvget_wstr + atf_add_test_case mvwget_wstr + atf_add_test_case get_wch + + # curses read screen contents routines + atf_add_test_case inch + atf_add_test_case winch + atf_add_test_case mvinch + atf_add_test_case mvwinch + atf_add_test_case inchnstr + atf_add_test_case winchnstr + atf_add_test_case mvinchnstr + atf_add_test_case mvwinchnstr + atf_add_test_case innstr + atf_add_test_case winnstr + atf_add_test_case mvinnstr + atf_add_test_case mvwinnstr + atf_add_test_case in_wch + atf_add_test_case win_wch + atf_add_test_case innwstr + atf_add_test_case winnwstr + atf_add_test_case inwstr + atf_add_test_case winwstr + atf_add_test_case mvinnwstr + atf_add_test_case mvwinnwstr + atf_add_test_case mvinwstr + atf_add_test_case mvwinwstr + + # curses insert character to window routines + atf_add_test_case insch + atf_add_test_case winsch + atf_add_test_case mvinsch + atf_add_test_case mvwinsch + atf_add_test_case ins_wch + atf_add_test_case wins_wch + atf_add_test_case mvins_wch + atf_add_test_case mvwins_wch + atf_add_test_case ins_nwstr + atf_add_test_case wins_nwstr + atf_add_test_case ins_wstr + atf_add_test_case wins_wstr + atf_add_test_case mvins_nwstr + atf_add_test_case mvwins_nwstr + atf_add_test_case mvins_wstr + atf_add_test_case mvwins_wstr + + # curses delete characters routines + atf_add_test_case delch + atf_add_test_case mvdelch + + # curses terminal manipulation routines + atf_add_test_case beep + atf_add_test_case flash + atf_add_test_case curs_set + # atf_add_test_case delay_output [WORKS CORRECTLY BUT FAILS IN TESTFRAME] + atf_add_test_case erasechar + atf_add_test_case erasewchar + atf_add_test_case echochar + atf_add_test_case echo_wchar + atf_add_test_case wecho_wchar + atf_add_test_case halfdelay + atf_add_test_case has_ic + atf_add_test_case killchar + atf_add_test_case killwchar + atf_add_test_case meta + atf_add_test_case cbreak + atf_add_test_case nocbreak + + # curses general attribute manipulation routines + atf_add_test_case attributes + atf_add_test_case wattributes + atf_add_test_case getattrs + atf_add_test_case color_set + atf_add_test_case wcolor_set atf_add_test_case termattrs + + # curses on-screen attribute manipulation routines + atf_add_test_case chgat + atf_add_test_case wchgat + atf_add_test_case mvchgat + atf_add_test_case mvwchgat + + # curses standout attribute manipulation routines + atf_add_test_case standout + atf_add_test_case wstandout + + # curses color manipulation routines + atf_add_test_case has_colors atf_add_test_case can_change_color + atf_add_test_case start_color + atf_add_test_case pair_content + atf_add_test_case init_color + atf_add_test_case color_content atf_add_test_case assume_default_colors - atf_add_test_case attributes -# atf_add_test_case beep # comment out for now - return is wrong - atf_add_test_case background - atf_add_test_case cbreak + + # curses clear window routines atf_add_test_case clear + atf_add_test_case clearok + + # curses terminal update routines + atf_add_test_case doupdate + atf_add_test_case immedok + atf_add_test_case leaveok + + # curses window scrolling routines + atf_add_test_case wscrl + atf_add_test_case scroll + atf_add_test_case setscrreg + atf_add_test_case wsetscrreg + + # curses window modification routines + atf_add_test_case touchline + atf_add_test_case touchoverlap + atf_add_test_case touchwin + atf_add_test_case untouchwin + atf_add_test_case wtouchln + atf_add_test_case is_linetouched + atf_add_test_case is_wintouched + atf_add_test_case redrawwin + atf_add_test_case wredrawln + + # curses soft label key routines + atf_add_test_case slk + + # curses draw lines on windows routines + atf_add_test_case hline + atf_add_test_case whline + atf_add_test_case mvhline + atf_add_test_case wvline + atf_add_test_case mvvline + atf_add_test_case hline_set + atf_add_test_case whline_set + atf_add_test_case vline_set + atf_add_test_case wvline_set + + # curses pad routines + atf_add_test_case pad + atf_add_test_case pechochar + + # curses cursor and window location and positioning routines + atf_add_test_case cursor + atf_add_test_case getcurx + atf_add_test_case getmaxx + atf_add_test_case getmaxy + atf_add_test_case getparx + atf_add_test_case getbegy + atf_add_test_case getbegx + atf_add_test_case mvcur + + # curses window routines atf_add_test_case copywin - atf_add_test_case curs_set -} + atf_add_test_case dupwin + # atf_add_test_case delwin [FAILING] + atf_add_test_case derwin + atf_add_test_case mvwin + atf_add_test_case mvderwin + atf_add_test_case newwin + atf_add_test_case overlay + atf_add_test_case overwrite + atf_add_test_case getwin + + # curses background attribute manipulation routines + atf_add_test_case background + atf_add_test_case bkgdset + atf_add_test_case getbkgd + + # curses border drawing routines + atf_add_test_case box + atf_add_test_case box_set + atf_add_test_case wborder + atf_add_test_case border_set + atf_add_test_case wborder_set + # curses insert or delete lines routines + atf_add_test_case deleteln + atf_add_test_case insertln + atf_add_test_case insdelln + + # curses print formatted strings on windows routines + atf_add_test_case wprintw + atf_add_test_case mvprintw + atf_add_test_case mvscanw + + # curses underscore attribute manipulation routines + atf_add_test_case underscore + atf_add_test_case wunderscore +} diff --git a/lib/libcurses/testframe.txt b/lib/libcurses/testframe.txt --- a/lib/libcurses/testframe.txt +++ b/lib/libcurses/testframe.txt @@ -1,3 +1,4 @@ +$NetBSD: testframe.txt,v 1.8 2021/02/09 20:31:02 rillig Exp $ CURSES TESTFRAME ---------------- @@ -7,9 +8,9 @@ The curses library is a complex piece of software and, often, changes made to the library may introduce subtle bugs that are hidden by other actions so a visual check of the curses output may look correct in -some circumstances and the bug only show itself after a certain +some circumstances and the bug only shows itself after a certain sequence of actions. To assist with validating that changes made to -the curses library have no undesired effects an automated test is +the curses library have no undesired effects, an automated test is needed to detect and highlight any changes in the curses application output stream. The programmer can then analyse the output changes and either correct a bug or update the automated test to accept the new @@ -62,29 +63,47 @@ overwritten on each subsequent use. call, call2, call3, call4: - All these are used to call curses routines, the only difference - between then is the number of expected return values. Call + + All these are used to call curses routines. The only difference + between them is the number of expected return values. Call expects one return value, call2 expects 2, call3 expects 3 and - call4 expects four. Any parameters that are passed by reference - and updated by the call are treated like returns. So, for - example, calling the function getyx() which has three - parameters, the window, a pointer to storage for y and a pointer - to storage for x would be called like this: - - call3 OK 4 5 getyx $win1 - - Which calls getyx, the first (and possibly only) return is the - return status of the function call, in this case we expect "OK" - indicating that the call succeeded. The next two returns are - the values of y and x respectively, the parameter $win1 is a - variable that was assigned by a previous call. Any return can - be assigned to a variable by including the variable name in a - call return list. Variables are referenced in a call parameter - list by prefixing the name with a $ character. All returns are - validated against the expected values and an error raised if - there is a mismatch. The only exception to this is when the - return is assigned to a variable. Valid values for the returns - list are: + call4 expects 4. Any parameters that are passed by reference and + updated by the call are treated like returns and are listed after + the regular return value. The general form of a call is: + + call[234] + + As an example, the macro 'getyx' has 3 parameters, as seen by a C + programmer, and it has return type 'void'. If its return type + were not void, that would be its first return value. The first + parameter is a window and is passed by value. The other two, 'y' + and 'x' are references to return values, thus they do not count as + parameters. In summary, there are 2 return values, therefore + 'call2' is used: + + call2 4 5 getyx $win1 + + The two return values from the actual function call are validated + to be '4' and '5'. The parameter '$win' refers to a variable that + has been defined by a previous call or variable assignment. + + Instead of directly validating the return values, they can also be + assigned to variables: + + call2 y x getyx $win1 + check y 4 + check x 5 + + Another example is 'mvwaddwstr', which has a single return value + (OK or ERR) and 4 parameters. It is called like this: + + call OK mvwaddwstr $win1 4 5 "text" + + Variables are referenced in a call parameter list by prefixing the + name with a '$' sign. All returns are validated against the + expected values and an error raised if there is a mismatch. The + only exception to this is when the return is assigned to a + variable. Valid values for the returns list are: variable - assign the return to the given variable name. @@ -101,6 +120,27 @@ that is the label STDSCR. This parameter will be substituted by the value of stdscr when the function call is made. +cchar: + Defines a variable as a complex character type (cchar_t). The + syntax is: + + cchar var_name attributes elements + + Where attributes is the attributes for the complex character, this + can be an integer, a variable or bitwise or'ed list (see below + for a description of the syntax). The elements is one or more wide + characters that make up the elements of the complex character. A + single element of either a numeric, variable, byte or single + character string can simply be wrtten at the end of the line. + Multiple elements need to be space separated and enclosed in + square ([]) brackets, the types allowed are the same as for the + single element case. In addition an element can be repeated by + following an element with a '*' then a number which will cause + the last element to be repeatedly added to the array the number + of times specified by number. Once the complex character variable + has been defined it can be used in calls to curses routines + requiring a complex character type argument. + check: Validate the value of a variable. This allows a variable to be checked for an expected return after it has been assigned in a @@ -221,21 +261,18 @@ be used as an expected result for a call. In addition to all the curses calls being supported by the slave, -there is one more special call called "drain". This call repeatedly -called getch() until there are no more characters in stdin. The call +there is one more special call called "DRAIN". This call repeatedly +calls wgetch(win) until there are no more characters in win. The call assumes that the curses input is either in no delay or timed input mode otherwise the test will time out and fail. This call can be used -to clear any pending input when testing testing a timed read to -prevent the input being used in a later test. +to clear any pending input when testing a timed read, to prevent the +input from being used in a later test. 4. Slave The user has no direct interaction with the slave process. The slave -is forked off by the director communicates to the director over a set -of pipes and a pseudo-tty connected to its standard i/o file +is forked off by the director and communicates to the director over a +set of pipes and a pseudo-tty connected to its standard i/o file descriptors. The slave executes the passed curses calls and passes back return values to the director. The slave automatically calls -initscr() on start up. - - - +initscr() on startup. diff --git a/lib/libcurses/tests/Makefile b/lib/libcurses/tests/Makefile --- a/lib/libcurses/tests/Makefile +++ b/lib/libcurses/tests/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.2 2011/09/08 18:44:38 jmmv Exp $ +# $NetBSD: Makefile,v 1.5 2021/02/13 06:29:45 rillig Exp $ NOMAN= # defined @@ -10,37 +10,216 @@ FILESDIR= ${TESTSDIR}/tests FILES= start -FILES+= std_defines -FILES+= attributes +FILES+= add_wch +FILES+= addbytes FILES+= addch -FILES+= addchstr FILES+= addchnstr -FILES+= addstr +FILES+= addchstr FILES+= addnstr -FILES+= beep +FILES+= addnwstr +FILES+= addstr +FILES+= addwstr +FILES+= assume_default_colors +FILES+= attributes FILES+= background +FILES+= beep +FILES+= bkgdset +FILES+= border_set +FILES+= box +FILES+= box_set FILES+= can_change_color FILES+= cbreak +FILES+= chgat +FILES+= clear +FILES+= clearok +FILES+= color_content FILES+= color_set FILES+= copywin FILES+= curs_set +FILES+= cursor +FILES+= define_key +FILES+= delay_output +FILES+= delch +FILES+= deleteln +FILES+= delwin +FILES+= derwin +FILES+= doupdate +FILES+= dupwin +FILES+= echo +FILES+= echo_wchar +FILES+= echochar +FILES+= erasechar +FILES+= erasewchar +FILES+= fill_screen +FILES+= fill_screen_numbers +FILES+= fill_window_numbers +FILES+= flash +FILES+= get_wch +FILES+= get_wstr +FILES+= getattrs +FILES+= getbegx +FILES+= getbegy +FILES+= getbkgd +FILES+= getcap FILES+= getch +FILES+= getcurx +FILES+= getmaxx +FILES+= getmaxy +FILES+= getn_wstr +FILES+= getnstr +FILES+= getparx +FILES+= getstr +FILES+= getwin +FILES+= halfdelay +FILES+= has_colors +FILES+= has_ic +FILES+= hline +FILES+= hline_set +FILES+= immedok +FILES+= in_wch +FILES+= inch +FILES+= inchnstr +FILES+= init_color +FILES+= innstr +FILES+= innwstr +FILES+= ins_nwstr +FILES+= ins_wch +FILES+= ins_wstr +FILES+= insch +FILES+= insdelln +FILES+= insertln +FILES+= inwstr +FILES+= is_linetouched +FILES+= is_wintouched +FILES+= key_name +FILES+= keyname +FILES+= keyok +FILES+= keypad +FILES+= killchar +FILES+= killwchar +FILES+= leaveok +FILES+= meta +FILES+= mutt_test +FILES+= mvaddch +FILES+= mvaddchnstr +FILES+= mvaddchstr +FILES+= mvaddnstr +FILES+= mvaddnwstr +FILES+= mvaddstr +FILES+= mvaddwstr +FILES+= mvchgat +FILES+= mvcur +FILES+= mvdelch +FILES+= mvderwin +FILES+= mvget_wstr +FILES+= mvgetn_wstr +FILES+= mvgetnstr +FILES+= mvgetstr +FILES+= mvhline +FILES+= mvinch +FILES+= mvinchnstr +FILES+= mvinnstr +FILES+= mvinnwstr +FILES+= mvins_nwstr +FILES+= mvins_wch +FILES+= mvins_wstr +FILES+= mvinsch +FILES+= mvinwstr +FILES+= mvprintw +FILES+= mvscanw +FILES+= mvvline +FILES+= mvwaddchnstr +FILES+= mvwaddchstr +FILES+= mvwaddnstr +FILES+= mvwaddnwstr +FILES+= mvwaddstr +FILES+= mvwaddwstr +FILES+= mvwchgat +FILES+= mvwget_wstr +FILES+= mvwgetn_wstr +FILES+= mvwgetnstr +FILES+= mvwgetstr +FILES+= mvwin +FILES+= mvwinch +FILES+= mvwinchnstr +FILES+= mvwinnstr +FILES+= mvwinnwstr +FILES+= mvwins_nwstr +FILES+= mvwins_wch +FILES+= mvwins_wstr +FILES+= mvwinsch +FILES+= mvwinwstr +FILES+= newwin +FILES+= nocbreak +FILES+= nodelay +FILES+= notimeout +FILES+= overlay +FILES+= overwrite +FILES+= pad +FILES+= pair_content +FILES+= pechochar +FILES+= redrawwin +FILES+= scroll +FILES+= setscrreg +FILES+= slk +FILES+= standout FILES+= start_color -FILES+= assume_default_colors +FILES+= start_slk +FILES+= std_defines FILES+= termattrs FILES+= timeout +FILES+= touchline +FILES+= touchoverlap +FILES+= touchwin +FILES+= two_window +FILES+= underscore +FILES+= unget_wch +FILES+= untouchwin +FILES+= varcheck +FILES+= vline_set +FILES+= wadd_wch +FILES+= waddch +FILES+= waddchnstr +FILES+= waddchstr +FILES+= waddnstr +FILES+= waddnwstr +FILES+= waddstr +FILES+= waddwstr +FILES+= wattributes +FILES+= wborder +FILES+= wborder_set +FILES+= wchgat +FILES+= wcolor_set +FILES+= wecho_wchar +FILES+= wget_wstr +FILES+= wgetn_wstr +FILES+= wgetnstr +FILES+= wgetstr +FILES+= whline +FILES+= whline_set +FILES+= win_wch +FILES+= winch +FILES+= winchnstr FILES+= window FILES+= window_create -FILES+= wborder -FILES+= box +FILES+= window_hierarchy +FILES+= winnstr +FILES+= winnwstr +FILES+= wins_nwstr +FILES+= wins_wch +FILES+= wins_wstr +FILES+= winsch +FILES+= winwstr FILES+= wprintw +FILES+= wredrawln FILES+= wscrl -FILES+= mvwin -FILES+= getstr -FILES+= chgat -FILES+= clear -FILES+= color_content -FILES+= fill_screen +FILES+= wsetscrreg +FILES+= wstandout +FILES+= wtimeout +FILES+= wtouchln +FILES+= wunderscore +FILES+= wvline +FILES+= wvline_set .include .include diff --git a/lib/libcurses/tests/add_wch b/lib/libcurses/tests/add_wch new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/add_wch @@ -0,0 +1,58 @@ +include start +call OK move 14 14 +cchar HCHAR 0 "H" +call OK add_wch $HCHAR +call2 14 15 getyx STDSCR +call OK add_wch $HCHAR +call OK add_wch $HCHAR +call OK refresh +compare add_wch1.chk +call OK move 14 79 +call OK add_wch $HCHAR +call2 15 0 getyx STDSCR +call OK refresh +call2 15 0 getyx STDSCR +call OK add_wch $HCHAR +call2 15 1 getyx STDSCR +call OK refresh +call OK add_wch $HCHAR +call OK add_wch $HCHAR +call OK add_wch $HCHAR +call OK add_wch $HCHAR +call OK add_wch $HCHAR +call OK add_wch $HCHAR +call OK add_wch $HCHAR +call OK refresh +compare add_wch2.chk +call2 15 8 getyx STDSCR + +call OK clear +call OK refresh +compare clear1.chk + +# tests for multi-column characters +cchar ACHAR 0x100 0x3401 +call OK add_wch $ACHAR +call OK refresh +call2 0 2 getyx STDSCR + +cchar CHAR 0 0x3401 +# test for wrapping +call OK move 0 79 +call OK add_wch $CHAR +call OK add_wch $CHAR +call OK refresh +call2 1 4 getyx STDSCR + +# test for special character processing +cchar BACK 0 0x8 +call OK add_wch $BACK +call OK refresh +compare add_wch3.chk + +# we are now at middle of multi-col char +# (unexpected output) [BUGGY??] +# cursor should be at 5 +# call OK add_wch $CHAR +# call OK refresh +# call2 1 5 getyx STDSCR diff --git a/lib/libcurses/tests/addbytes b/lib/libcurses/tests/addbytes new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/addbytes @@ -0,0 +1,40 @@ +# $NetBSD: addbytes,v 1.4 2021/02/25 01:07:43 rillig Exp $ +# +# Tests adding bytes to stdscr. +# +# Note that addbytes is not part of the official curses API, it is merely +# an internal helper function. + +include start + +call OK addbytes "\t" 1 +call2 0 8 getyx STDSCR + +call OK addbytes "123456" 6 +call2 0 14 getyx STDSCR + +call OK addbytes "\t" 1 +call2 0 16 getyx STDSCR + +call OK addbytes "\n" 1 +call2 1 0 getyx STDSCR + +# Ensure that backspace works as intended. +# See tests/addch. +call OK addbytes "12345\010" 6 +call2 1 4 getyx STDSCR + +call OK addbytes "\010\010\010\010" 4 +call2 1 0 getyx STDSCR + +# If curx is already 0, backspace is a no-op. +call OK addbytes "\010" 1 +call2 1 0 getyx STDSCR + +call OK addbytes "123\t" 4 +call2 1 8 getyx STDSCR + +# Backspace affects the cursor position, no matter whether the previously +# output char was a tab or a space. +call OK addbytes "\010" 1 +call2 1 7 getyx STDSCR diff --git a/lib/libcurses/tests/addch b/lib/libcurses/tests/addch --- a/lib/libcurses/tests/addch +++ b/lib/libcurses/tests/addch @@ -1,4 +1,61 @@ +# $NetBSD: addch,v 1.8 2021/02/25 01:07:43 rillig Exp $ +# +# Between at least 2012 and 2016, addstr did not advance win->curx for a '\t', +# but addch did. This was inconsistent. +# +# On 2016.11.28.18.25.26 on NetBSD 7.99.43, this inconsistency was fixed. +# Now both functions advanced win->curx. +# +# On 2019.05.20.22.17.41 on NetBSD 8.99.41, the fix was modified, which +# introduced another inconsistency. Since then, addstr advanced win->curx as +# expected, but addch advanced it by twice the amount. +# +# On 2021.02.13.14.30.37 on NetBSD 9.99.80, this inconsistency was fixed. +# Now both functions advanced win->curx again. + include start call OK addch `\001t` +call OK refresh +call OK mvaddch 5 3 `\003e` +call OK refresh +call OK addch `\000\n` + +call OK addch `\000\t` +call2 6 8 getyx STDSCR +call OK addch `\0008` +call OK addch `\000\n` + +call OK addstr "0123456" +call OK addch `\000\t` +call2 7 8 getyx STDSCR +call OK addch `\0008` +call OK addch `\000\n` + call OK refresh compare addch.chk + +# Ensure that backspace works as intended. +# See tests/addbytes. +call OK move 1 0 +call OK addstr "12345" +call OK addch `\000\010` +call2 1 4 getyx STDSCR + +call OK addch `\000\010` +call OK addch `\000\010` +call OK addch `\000\010` +call OK addch `\000\010` +call2 1 0 getyx STDSCR + +# If curx is already 0, backspace is a no-op. +call OK addch `\000\010` +call2 1 0 getyx STDSCR + +call OK addstr "123" +call OK addch `\000\t` +call2 1 8 getyx STDSCR + +# Backspace affects the cursor position, no matter whether the previously +# output char was a tab or a space. +call OK addch `\000\010` +call2 1 7 getyx STDSCR diff --git a/lib/libcurses/tests/addchstr b/lib/libcurses/tests/addchstr --- a/lib/libcurses/tests/addchstr +++ b/lib/libcurses/tests/addchstr @@ -2,3 +2,13 @@ call OK addchstr `\004a\004b\004c\004d\004e` call OK refresh compare addchstr.chk +call OK move 0 5 +call OK bkgdset `\002\000` +call OK addchstr `\004f\004g\004h` +call OK refresh +compare addchstr2.chk +# check wrapping (should not wrap) +call OK move 1 78 +call OK addchstr `\000i\000j\000k` +call OK refresh +compare addchstr3.chk diff --git a/lib/libcurses/tests/addnstr b/lib/libcurses/tests/addnstr --- a/lib/libcurses/tests/addnstr +++ b/lib/libcurses/tests/addnstr @@ -1,5 +1,4 @@ include start call OK addnstr "abcdefg" 5 call OK refresh -# should be the same as addstr -compare addstr.chk +compare addnstr.chk diff --git a/lib/libcurses/tests/addnwstr b/lib/libcurses/tests/addnwstr new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/addnwstr @@ -0,0 +1,20 @@ +include start +# 0x3401 takes 2 columns +# test multi-column +wchar WSTR [0x41, 0x42, 0x3401, 0x43, 0x44, 0x45] +call OK addnwstr $WSTR 4 +call OK refresh +call2 0 5 getyx STDSCR +compare addnwstr1.chk + +# test special character processing +wchar CR ["\r", "a", "\r", "b", "c"] +call OK addnwstr $CR 4 +call OK refresh +compare addnwstr2.chk + +# test wrapping +call OK move 2 77 +call OK addnwstr $WSTR -1 +call OK refresh +compare addwstr3.chk diff --git a/lib/libcurses/tests/addstr b/lib/libcurses/tests/addstr --- a/lib/libcurses/tests/addstr +++ b/lib/libcurses/tests/addstr @@ -1,4 +1,28 @@ include start -call OK addstr "abcde" +call OK addstr "abcde\n" +call OK addstr "\n" +call OK addstr "\t8\n" +call OK addstr "0123456\t8\n" call OK refresh compare addstr.chk +# +# Checks for PR#56224 +# +call OK move 23 0 +# the addstr should be truncated since no scrolling and will return ERR +call ERR addstr "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890123456789" +call OK refresh +call OK clear +call OK move 23 75 +call ERR addstr "a\thello" +call OK refresh +compare addstr2.chk +call OK scrollok STDSCR $TRUE +# the addstr should be not truncated since scrolling is enabled +call OK addstr "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()" +call OK refresh +call OK clear +call OK move 23 75 +call OK addstr "a\thello" +call OK refresh +compare addstr3.chk diff --git a/lib/libcurses/tests/addwstr b/lib/libcurses/tests/addwstr new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/addwstr @@ -0,0 +1,20 @@ +include start +# 0x3401 takes 2 columns +# test multi-column +wchar WSTR [0x41, 0x42, 0x3401, 0x43, 0x44, 0x45] +call OK addwstr $WSTR +call OK refresh +call2 0 7 getyx STDSCR +compare addwstr1.chk + +# test special character processing +wchar CR "\r" +call OK addwstr $CR +call OK refresh +compare addwstr2.chk + +# test wrapping +call OK move 2 77 +call OK addwstr $WSTR +call OK refresh +compare addwstr3.chk diff --git a/lib/libcurses/tests/beep b/lib/libcurses/tests/beep --- a/lib/libcurses/tests/beep +++ b/lib/libcurses/tests/beep @@ -1,5 +1,4 @@ include start -# SUSv2 says this should return OK but we return 1 -call 1 beep +call OK beep call OK refresh compare bell.chk diff --git a/lib/libcurses/tests/bkgdset b/lib/libcurses/tests/bkgdset new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/bkgdset @@ -0,0 +1,9 @@ +include start +# background attributes set to underline and A for space +call OK bkgdset `\002A` +call OK erase +call OK refresh +compare background1.chk +call OK addstr "this is a test" +call OK refresh +compare bkgdset1.chk diff --git a/lib/libcurses/tests/border_set b/lib/libcurses/tests/border_set new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/border_set @@ -0,0 +1,17 @@ +include start +cchar ls 0 "A" +cchar rs 0 "B" +cchar ts 0x100 "C" +cchar bs 0 "D" +cchar tl 0 "E" +cchar tr 0 "F" +cchar bl 0x100 "G" +cchar br 0 "H" +call OK border_set $ls $rs $ts $bs $tl $tr $bl $br +call OK refresh +compare border_set1.chk + +# test in case of default values +call OK border_set NULL NULL NULL NULL NULL NULL NULL NULL +call OK refresh +compare border_set2.chk diff --git a/lib/libcurses/tests/box_set b/lib/libcurses/tests/box_set new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/box_set @@ -0,0 +1,11 @@ +include window +cchar verch 0x100 "A" +cchar horch 0x200 "B" +call OK box_set $win1 $verch $horch +call OK wrefresh $win1 +compare box_set1.chk + +# test in case of default values +call OK box_set $win1 NULL NULL +call OK wrefresh $win1 +compare box_set2.chk diff --git a/lib/libcurses/tests/chgat b/lib/libcurses/tests/chgat --- a/lib/libcurses/tests/chgat +++ b/lib/libcurses/tests/chgat @@ -12,4 +12,3 @@ call OK chgat -1 $UNDERSCORE 3 0 call OK refresh compare chgat3.chk - diff --git a/lib/libcurses/tests/clear b/lib/libcurses/tests/clear --- a/lib/libcurses/tests/clear +++ b/lib/libcurses/tests/clear @@ -1,4 +1,10 @@ -include addstr +include start +call OK addstr "abcde\n" +call OK addstr "\n" +call OK addstr "\t8\n" +call OK addstr "0123456\t8\n" +call OK refresh +compare clear0.chk call OK clear call OK refresh compare clear1.chk diff --git a/lib/libcurses/tests/clearok b/lib/libcurses/tests/clearok new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/clearok @@ -0,0 +1,40 @@ +include start +call OK addch `\001a` +call OK refresh +# forces complete redraw on next refresh +call OK addch `\001b` +call OK clearok STDSCR 1 +call OK refresh +# will only update new changes in the screen +call OK addch `\001c` +call OK clearok STDSCR 0 +call OK refresh +compare clearok1.chk + +include window_create +call OK waddch $win1 `\001a` +call OK wrefresh $win1 +# forces complete redraw on next refresh +call OK waddch $win1 `\001b` +call OK clearok $win1 1 +call OK wrefresh $win1 +# will only update new changes in the screen +call OK waddch $win1 `\001c` +call OK clearok $win1 0 +call OK wrefresh $win1 +compare clearok2.chk + +call OK addch `\001a` +call OK refresh +# forces complete redraw on next refresh +call OK addch `\001b` +call OK clearok STDSCR 1 +call OK refresh +# will only update new changes in the screen +call OK addch `\001c` +call OK clearok STDSCR 0 +call OK refresh +compare clearok3.chk + +call `\001a` mvinch 0 0 +call `\001a` mvwinch $win1 0 0 diff --git a/lib/libcurses/tests/color_content b/lib/libcurses/tests/color_content --- a/lib/libcurses/tests/color_content +++ b/lib/libcurses/tests/color_content @@ -1,12 +1,10 @@ include start call OK start_color call4 OK 0 0 0 color_content $COLOR_BLACK -call4 OK 1000 0 0 color_content $COLOR_RED -call4 OK 0 1000 0 color_content $COLOR_GREEN -call4 OK 1000 1000 0 color_content $COLOR_YELLOW -call4 OK 0 0 1000 color_content $COLOR_BLUE -call4 OK 1000 0 1000 color_content $COLOR_MAGENTA -call4 OK 0 1000 1000 color_content $COLOR_CYAN -call4 OK 1000 1000 1000 color_content $COLOR_WHITE - - +call4 OK 680 0 0 color_content $COLOR_RED +call4 OK 0 680 0 color_content $COLOR_GREEN +call4 OK 680 680 0 color_content $COLOR_YELLOW +call4 OK 0 0 680 color_content $COLOR_BLUE +call4 OK 680 0 680 color_content $COLOR_MAGENTA +call4 OK 0 680 680 color_content $COLOR_CYAN +call4 OK 680 680 680 color_content $COLOR_WHITE diff --git a/lib/libcurses/tests/color_set b/lib/libcurses/tests/color_set --- a/lib/libcurses/tests/color_set +++ b/lib/libcurses/tests/color_set @@ -4,8 +4,7 @@ comparend color_start.chk compare color_blank_draw.chk call OK init_pair 4 $COLOR_RED $COLOR_GREEN -call OK color_set 4 0 +call OK color_set 4 NULL call OK printw "%s" "testing" call OK refresh compare color_set.chk - diff --git a/lib/libcurses/tests/copywin b/lib/libcurses/tests/copywin --- a/lib/libcurses/tests/copywin +++ b/lib/libcurses/tests/copywin @@ -4,12 +4,16 @@ check win2 NON_NULL call OK wrefresh $win2 compare copywin1.chk +call OK scrollok $win1 $TRUE +call OK scrollok $win2 $TRUE +call OK mvwprintw $win1 5 0 "%s" "stingt" +call OK wmove $win1 4 0 +call OK wscrl $win1 -1 call OK mvwprintw $win1 0 0 "%s" "testin" call OK mvwprintw $win1 1 0 "%s" "gtesti" call OK mvwprintw $win1 2 0 "%s" "ngtest" call OK mvwprintw $win1 3 0 "%s" "ingtes" call OK mvwprintw $win1 4 0 "%s" "tingte" -call OK mvwprintw $win1 5 0 "%s" "stingt" call OK wrefresh $win1 compare copywin2.chk call OK copywin $win1 $win2 0 0 1 1 7 7 0 @@ -20,12 +24,13 @@ call OK wrefresh $win1 call OK wrefresh $win2 compare copywin4.chk +call OK mvwprintw $win2 5 0 "%s" "tingtesti" +call OK wscrl $win2 -1 call OK mvwprintw $win2 0 0 "%s" "testingte" call OK mvwprintw $win2 1 0 "%s" "stingtest" call OK mvwprintw $win2 2 0 "%s" "ingtestin" call OK mvwprintw $win2 3 0 "%s" "gtestingt" call OK mvwprintw $win2 4 0 "%s" "estingtes" -call OK mvwprintw $win2 5 0 "%s" "tingtesti" call OK wrefresh $win2 compare copywin5.chk call OK copywin $win2 $win1 0 0 0 0 7 9 0 @@ -36,20 +41,22 @@ call OK wrefresh $win1 call OK wrefresh $win2 compare copywin7.chk +call OK mvwprintw $win1 5 0 "%s" " t n t" +call OK wscrl $win1 -1 call OK mvwprintw $win1 0 0 "%s" "t s i " call OK mvwprintw $win1 1 0 "%s" "g e t " call OK mvwprintw $win1 2 0 "%s" "n t s " call OK mvwprintw $win1 3 0 "%s" " n t s" call OK mvwprintw $win1 4 0 "%s" "t n t " -call OK mvwprintw $win1 5 0 "%s" " t n t" call OK wrefresh $win1 compare copywin8.chk +call OK mvwprintw $win2 5 0 "%s" "s i g " +call OK wscrl $win2 -1 call OK mvwprintw $win2 0 0 "%s" " e t n" call OK mvwprintw $win2 1 0 "%s" " t s i" call OK mvwprintw $win2 2 0 "%s" " g e t" call OK mvwprintw $win2 3 0 "%s" "i g e " call OK mvwprintw $win2 4 0 "%s" " i g e" -call OK mvwprintw $win2 5 0 "%s" "s i g " call OK wrefresh $win2 compare copywin9.chk call OK copywin $win1 $win2 0 0 0 0 6 6 0 @@ -60,20 +67,22 @@ call OK wrefresh $win1 call OK wrefresh $win2 compare copywin11.chk +call OK mvwprintw $win1 5 0 "%s" " t n t" +call OK wscrl $win1 -1 call OK mvwprintw $win1 0 0 "%s" "t s i " call OK mvwprintw $win1 1 0 "%s" "g e t " call OK mvwprintw $win1 2 0 "%s" "n t s " call OK mvwprintw $win1 3 0 "%s" " n t s" call OK mvwprintw $win1 4 0 "%s" "t n t " -call OK mvwprintw $win1 5 0 "%s" " t n t" call OK wrefresh $win1 compare copywin12.chk +call OK mvwprintw $win2 5 0 "%s" "s i g " +call OK wscrl $win2 -1 call OK mvwprintw $win2 0 0 "%s" " e t n" call OK mvwprintw $win2 1 0 "%s" " t s i" call OK mvwprintw $win2 2 0 "%s" " g e t" call OK mvwprintw $win2 3 0 "%s" "i g e " call OK mvwprintw $win2 4 0 "%s" " i g e" -call OK mvwprintw $win2 5 0 "%s" "s i g " call OK wrefresh $win2 compare copywin13.chk call OK copywin $win1 $win2 0 0 0 0 6 6 1 diff --git a/lib/libcurses/tests/cursor b/lib/libcurses/tests/cursor new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/cursor @@ -0,0 +1,30 @@ +include window_hierarchy + +call OK wmove $win3 2 3 +call OK wrefresh $win3 +call 2 getcury $win3 +call 3 getcurx $win3 + +call 4 getbegy $win3 +call 7 getbegx $win3 +call2 4 7 getbegyx $win3 + +call 5 getmaxx $win2 +call 4 getmaxy $win3 +call2 5 5 getmaxyx $win2 + +call 1 getpary $win3 +call 1 getparx $win3 +call2 1 1 getparyx $win3 + +call2 0 0 getyx $win1 +call2 0 0 getyx $win2 +call2 2 3 getyx $win3 +call2 0 0 getyx $win4 + +# wcursyncup syncs the cursor of all the ancestor windows +call OK wcursyncup $win3 +call2 4 5 getyx $win1 +call2 3 4 getyx $win2 +call2 2 3 getyx $win3 +call2 0 0 getyx $win4 diff --git a/lib/libcurses/tests/define_key b/lib/libcurses/tests/define_key new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/define_key @@ -0,0 +1,9 @@ +include start +call OK define_key "\etest" 1024 +call OK keypad STDSCR $TRUE +input "\etest" +call 1024 getch +call OK define_key NULL 1024 +input "\etest" +# sequence should now be unknown so we should just get first char back. +call 0x1b getch diff --git a/lib/libcurses/tests/delay_output b/lib/libcurses/tests/delay_output new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/delay_output @@ -0,0 +1,5 @@ +include start +call OK delay_output 30 +call OK addstr "test" +call OK refresh +compare delay_output.chk diff --git a/lib/libcurses/tests/delch b/lib/libcurses/tests/delch new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/delch @@ -0,0 +1,36 @@ +include start +call OK move 2 2 +call OK addstr "teest" +call OK refresh +compare delch1.chk +call OK move 2 4 +call OK delch +call OK refresh +compare delch2.chk +call OK mvaddch 2 6 `\001t` +call OK refresh +call OK move 2 5 +call OK delch +call OK refresh +compare delch3.chk + +call OK clear +call OK move 0 0 +call OK refresh +compare delch4.chk + +include window_create +call OK wmove $win1 1 0 +call OK waddstr $win1 "tesst" +call OK wrefresh $win1 +compare delch5.chk +call OK wmove $win1 1 3 +call OK wdelch $win1 +call OK wrefresh $win1 +compare delch6.chk +call OK mvwaddch $win1 1 4 `\001t` +call OK wrefresh $win1 +call OK wmove $win1 1 3 +call OK wdelch $win1 +call OK wrefresh $win1 +compare delch7.chk diff --git a/lib/libcurses/tests/deleteln b/lib/libcurses/tests/deleteln new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/deleteln @@ -0,0 +1,53 @@ +include start +include fill_screen_numbers +call OK refresh +compare fill_screen_numbers.chk + +call OK move 15 0 +call OK deleteln +call OK refresh +compare deleteln1.chk + +call OK move 0 0 +call OK clear +include fill_screen_numbers +call OK refresh +comparend deleteln2.chk +compare fill_screen_numbers.chk + +call OK setscrreg 8 16 +call OK move 12 0 +call OK deleteln +call OK refresh +compare deleteln3.chk + +call OK move 0 0 +call OK clear +call OK setscrreg 0 23 +call OK refresh +compare deleteln2.chk + +include window_create +include fill_window_numbers +call OK wrefresh $win1 +compare fill_window_numbers.chk + +call OK wmove $win1 3 0 +call OK wdeleteln $win1 +call OK wrefresh $win1 +compare deleteln4.chk + +call OK wmove $win1 0 0 +call OK wclear $win1 +call OK wrefresh $win1 +compare deleteln5.chk + +include fill_window_numbers +call OK wrefresh $win1 +compare fill_window_numbers.chk + +call OK wmove $win1 3 0 +call OK wsetscrreg $win1 2 4 +call OK wdeleteln $win1 +call OK wrefresh $win1 +compare deleteln6.chk diff --git a/lib/libcurses/tests/delwin b/lib/libcurses/tests/delwin new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/delwin @@ -0,0 +1,39 @@ +include start +call win1 newwin 15 10 2 5 +check win1 NON_NULL +call OK wmove $win1 0 0 +call OK waddstr $win1 "0000000000" +call OK wmove $win1 1 0 +call OK waddstr $win1 "0000000000" +call OK wmove $win1 2 0 +call OK waddstr $win1 "0000000000" +call OK wmove $win1 3 0 +call OK waddstr $win1 "0000000000" +call OK wmove $win1 4 0 +call OK waddstr $win1 "0000000000" +call OK wmove $win1 5 0 +call OK waddstr $win1 "0000000000" +call OK wmove $win1 6 0 +call OK waddstr $win1 "0000000000" +call OK wmove $win1 7 0 +call OK waddstr $win1 "0000000000" +call OK wmove $win1 8 0 +call OK waddstr $win1 "0000000000" +call OK wmove $win1 9 0 +call OK waddstr $win1 "0000000000" +call win2 newwin 6 5 3 6 +check win2 NON_NULL +call OK wmove $win2 0 0 +call OK waddstr $win2 "22222" +call OK wmove $win2 1 0 +call OK waddstr $win2 "22222" +call OK wmove $win2 2 0 +call OK waddstr $win2 "22222" +call OK wrefresh $win1 +call OK wrefresh $win2 +compare delwin1.chk +call OK delwin $win2 +call OK touchwin $win1 +call OK wrefresh $win1 +call OK wrefresh $win2 +compare /dev/null diff --git a/lib/libcurses/tests/derwin b/lib/libcurses/tests/derwin new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/derwin @@ -0,0 +1,33 @@ +include start +include window_create +call OK scrollok $win1 $TRUE +call OK wmove $win1 5 0 +call OK waddstr $win1 "000000" +call OK wmove $win1 4 0 +call OK wscrl $win1 -1 +call OK wmove $win1 0 0 +call OK waddstr $win1 "000000" +call OK wmove $win1 1 0 +call OK waddstr $win1 "000000" +call OK wmove $win1 2 0 +call OK waddstr $win1 "000000" +call OK wmove $win1 3 0 +call OK waddstr $win1 "000000" +call OK wmove $win1 4 0 +call OK waddstr $win1 "000000" +call OK wrefresh $win1 +compare derwin1.chk +call win2 derwin $win1 3 3 1 1 +check win2 NON_NULL +call OK scrollok $win2 $TRUE +call OK wmove $win2 2 0 +call OK waddstr $win2 "222" +call OK wmove $win2 1 0 +call OK wscrl $win2 -1 +call OK wmove $win2 0 0 +call OK waddstr $win2 "222" +call OK wmove $win2 1 0 +call OK waddstr $win2 "222" +call OK wrefresh $win1 +compare derwin2.chk + diff --git a/lib/libcurses/tests/doupdate b/lib/libcurses/tests/doupdate new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/doupdate @@ -0,0 +1,5 @@ +include start +call OK addstr "hello world" +call OK wnoutrefresh STDSCR +call OK doupdate +compare doupdate.chk diff --git a/lib/libcurses/tests/dupwin b/lib/libcurses/tests/dupwin new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/dupwin @@ -0,0 +1,26 @@ +include window +include fill_window_numbers +call win2 dupwin $win1 +check win2 NON_NULL +call OK mvwin $win2 8 12 +call OK wrefresh $win1 +call OK wrefresh $win2 +compare dupwin1.chk +call OK scrollok $win2 $TRUE +call OK wmove $win2 5 0 +call OK waddstr $win2 "ffffff" +call OK wmove $win2 4 0 +call OK wscrl $win2 -1 +call OK wmove $win2 0 0 +call OK waddstr $win2 "aaaaaa" +call OK wmove $win2 1 0 +call OK waddstr $win2 "bbbbbb" +call OK wmove $win2 2 0 +call OK waddstr $win2 "cccccc" +call OK wmove $win2 3 0 +call OK waddstr $win2 "dddddd" +call OK wmove $win2 4 0 +call OK waddstr $win2 "eeeeee" +call OK wrefresh $win1 +call OK wrefresh $win2 +compare dupwin2.chk diff --git a/lib/libcurses/tests/echo b/lib/libcurses/tests/echo new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/echo @@ -0,0 +1,18 @@ +# +# noecho does not behave like solaris nor ncurses +# ours turns cbreak off when noecho is in effect, others don't.. +# should we fix? This test will fail as written due to this behaviour +# +include start +call OK echo +input "a" +call 0x61 getch +compare /dev/null +call OK noecho +input "b" +call 0x62 getch +compare /dev/null +call OK echo +input "c" +call 0x63 getch +compare /dev/null diff --git a/lib/libcurses/tests/echo_wchar b/lib/libcurses/tests/echo_wchar new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/echo_wchar @@ -0,0 +1,48 @@ +include start +call OK move 14 14 +cchar HCHAR 0 "H" +call OK echo_wchar $HCHAR +call2 14 15 getyx STDSCR +call OK echo_wchar $HCHAR +call OK echo_wchar $HCHAR +compare add_wch1.chk +call OK move 14 79 +call OK echo_wchar $HCHAR +call2 15 0 getyx STDSCR +call OK echo_wchar $HCHAR +call2 15 1 getyx STDSCR +call OK echo_wchar $HCHAR +call OK echo_wchar $HCHAR +call OK echo_wchar $HCHAR +call OK echo_wchar $HCHAR +call OK echo_wchar $HCHAR +call OK echo_wchar $HCHAR +call OK echo_wchar $HCHAR +compare add_wch2.chk +call2 15 8 getyx STDSCR + +call OK clear +call OK refresh +compare clear1.chk + +# tests for multi-column characters +cchar ACHAR 0x100 0x3401 +call OK echo_wchar $ACHAR +call2 0 2 getyx STDSCR + +cchar CHAR 0 0x3401 +# test for wrapping +call OK move 0 79 +call OK echo_wchar $CHAR +call OK echo_wchar $CHAR +call2 1 4 getyx STDSCR + +# test for special character processing +cchar BACK 0 0x8 +call OK echo_wchar $BACK +#compare add_wch3.chk + +# we are now at middle of multi-col char +# (unexpected output) +# call OK echo_wchar $CHAR +# call2 1 4 getyx STDSCR diff --git a/lib/libcurses/tests/echochar b/lib/libcurses/tests/echochar new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/echochar @@ -0,0 +1,6 @@ +include start +call OK echochar `\002t` +compare echochar1.chk +include window_create +call OK wechochar $win1 `\001s` +compare echochar2.chk diff --git a/lib/libcurses/tests/erasechar b/lib/libcurses/tests/erasechar new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/erasechar @@ -0,0 +1,2 @@ +include start +call 0x08 erasechar diff --git a/lib/libcurses/tests/erasewchar b/lib/libcurses/tests/erasewchar new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/erasewchar @@ -0,0 +1,3 @@ +include start +wchar echar 0x8 +call2 OK $echar erasewchar diff --git a/lib/libcurses/tests/fill_screen_numbers b/lib/libcurses/tests/fill_screen_numbers new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/fill_screen_numbers @@ -0,0 +1,30 @@ +# +# Fill the screen with numbered lines for testing scrolling +# +call OK scrollok STDSCR $TRUE +call OK mvaddstr 23 0 "33333333333333333333333333333333333333333333333333333333333333333333333333333333" +call OK scrl -1 +call OK mvaddstr 0 0 "00000000000000000000000000000000000000000000000000000000000000000000000000000000" +call OK mvaddstr 1 0 "11111111111111111111111111111111111111111111111111111111111111111111111111111111" +call OK mvaddstr 2 0 "22222222222222222222222222222222222222222222222222222222222222222222222222222222" +call OK mvaddstr 3 0 "33333333333333333333333333333333333333333333333333333333333333333333333333333333" +call OK mvaddstr 4 0 "44444444444444444444444444444444444444444444444444444444444444444444444444444444" +call OK mvaddstr 5 0 "55555555555555555555555555555555555555555555555555555555555555555555555555555555" +call OK mvaddstr 6 0 "66666666666666666666666666666666666666666666666666666666666666666666666666666666" +call OK mvaddstr 7 0 "77777777777777777777777777777777777777777777777777777777777777777777777777777777" +call OK mvaddstr 8 0 "88888888888888888888888888888888888888888888888888888888888888888888888888888888" +call OK mvaddstr 9 0 "99999999999999999999999999999999999999999999999999999999999999999999999999999999" +call OK mvaddstr 10 0 "00000000000000000000000000000000000000000000000000000000000000000000000000000000" +call OK mvaddstr 11 0 "11111111111111111111111111111111111111111111111111111111111111111111111111111111" +call OK mvaddstr 12 0 "22222222222222222222222222222222222222222222222222222222222222222222222222222222" +call OK mvaddstr 13 0 "33333333333333333333333333333333333333333333333333333333333333333333333333333333" +call OK mvaddstr 14 0 "44444444444444444444444444444444444444444444444444444444444444444444444444444444" +call OK mvaddstr 15 0 "55555555555555555555555555555555555555555555555555555555555555555555555555555555" +call OK mvaddstr 16 0 "66666666666666666666666666666666666666666666666666666666666666666666666666666666" +call OK mvaddstr 17 0 "77777777777777777777777777777777777777777777777777777777777777777777777777777777" +call OK mvaddstr 18 0 "88888888888888888888888888888888888888888888888888888888888888888888888888888888" +call OK mvaddstr 19 0 "99999999999999999999999999999999999999999999999999999999999999999999999999999999" +call OK mvaddstr 20 0 "00000000000000000000000000000000000000000000000000000000000000000000000000000000" +call OK mvaddstr 21 0 "11111111111111111111111111111111111111111111111111111111111111111111111111111111" +call OK mvaddstr 22 0 "22222222222222222222222222222222222222222222222222222222222222222222222222222222" +call OK scrollok STDSCR $FALSE diff --git a/lib/libcurses/tests/fill_window_numbers b/lib/libcurses/tests/fill_window_numbers new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/fill_window_numbers @@ -0,0 +1,16 @@ +call OK scrollok $win1 $TRUE +call OK wmove $win1 5 0 +call OK waddstr $win1 "666666" +call OK wmove $win1 4 0 +call OK wscrl $win1 -1 +call OK wmove $win1 0 0 +call OK waddstr $win1 "111111" +call OK wmove $win1 1 0 +call OK waddstr $win1 "222222" +call OK wmove $win1 2 0 +call OK waddstr $win1 "333333" +call OK wmove $win1 3 0 +call OK waddstr $win1 "444444" +call OK wmove $win1 4 0 +call OK waddstr $win1 "555555" +call OK scrollok $win1 $FALSE diff --git a/lib/libcurses/tests/flash b/lib/libcurses/tests/flash new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/flash @@ -0,0 +1,3 @@ +include start +call OK flash +compare flash.chk diff --git a/lib/libcurses/tests/get_wch b/lib/libcurses/tests/get_wch new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/get_wch @@ -0,0 +1,22 @@ +include start +# test using the input queue +wchar ACHAR "A" +call OK unget_wch $ACHAR +noinput +call2 $KEY_CODE_YES $ACHAR get_wch + +wchar res ["a"] +input "a\n" +call2 OK $res get_wch + +# test keypad translations +wchar esc "\e" +input "\eOD" +call2 OK $esc get_wch + +# character at cursor position is cleared +call OK keypad STDSCR $TRUE +wchar key $KEY_LEFT +input "\eOD" +call2 $KEY_CODE_YES $key get_wch +call OK refresh diff --git a/lib/libcurses/tests/get_wstr b/lib/libcurses/tests/get_wstr new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/get_wstr @@ -0,0 +1,31 @@ +include start +# test input through unget_wch queue +wchar ACHAR 0x3401 +wchar NL "\n" +call OK unget_wch $NL +call OK unget_wch $ACHAR +call OK move 2 3 +noinput +call2 OK $ACHAR get_wstr + +# test erase and kill character processing, +# enable canonical mode; kill should erase whole line +call OK cbreak +wchar res ["c", "d", "f"] +call OK move 3 0 +input "ab\025cde\010f\n" +call2 OK $res get_wstr + +# test keypad translations +wchar res ["a", "c", "d", "\e", "O", "D"] +call OK move 4 4 +input "ab\010cd\eOD\n" +call2 OK $res get_wstr + +call OK keypad STDSCR $TRUE +wchar res ["a", "b", "d"] +call OK move 5 1 +input "abc\eODd\n" +call2 OK $res get_wstr + +compare get_wstr.chk diff --git a/lib/libcurses/tests/getattrs b/lib/libcurses/tests/getattrs new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/getattrs @@ -0,0 +1,8 @@ +include window +call 0 getattrs $win1 +call OK wattrset $win1 ($REVERSE | $BOLD) +call ($REVERSE | $BOLD) getattrs $win1 +call OK wattron $win1 $UNDERSCORE +call ($REVERSE | $BOLD | $UNDERSCORE) getattrs $win1 +call OK wattroff $win1 $REVERSE +call ($BOLD | $UNDERSCORE) getattrs $win1 diff --git a/lib/libcurses/tests/getbegx b/lib/libcurses/tests/getbegx new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/getbegx @@ -0,0 +1,2 @@ +include window +call 5 getbegx $win1 diff --git a/lib/libcurses/tests/getbegy b/lib/libcurses/tests/getbegy new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/getbegy @@ -0,0 +1,2 @@ +include window +call 2 getbegy $win1 diff --git a/lib/libcurses/tests/getbkgd b/lib/libcurses/tests/getbkgd new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/getbkgd @@ -0,0 +1,2 @@ +include bkgdset +call `\002A` getbkgd STDSCR diff --git a/lib/libcurses/tests/getcap b/lib/libcurses/tests/getcap new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/getcap @@ -0,0 +1,2 @@ +include start +call "flash" getcap "flash" diff --git a/lib/libcurses/tests/getch b/lib/libcurses/tests/getch --- a/lib/libcurses/tests/getch +++ b/lib/libcurses/tests/getch @@ -1,3 +1,8 @@ include start input "i" call 105 getch + +input "j" +call 106 mvgetch 2 3 +call OK refresh +compare getch.chk diff --git a/lib/libcurses/tests/getcurx b/lib/libcurses/tests/getcurx new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/getcurx @@ -0,0 +1,5 @@ +include window +call OK wmove $win1 3 4 +call 4 getcurx $win1 +call 3 getcury $win1 +call2 3 4 getyx $win1 diff --git a/lib/libcurses/tests/getmaxx b/lib/libcurses/tests/getmaxx new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/getmaxx @@ -0,0 +1,2 @@ +include window +call 6 getmaxx $win1 diff --git a/lib/libcurses/tests/getmaxy b/lib/libcurses/tests/getmaxy new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/getmaxy @@ -0,0 +1,2 @@ +include window +call 6 getmaxy $win1 diff --git a/lib/libcurses/tests/getn_wstr b/lib/libcurses/tests/getn_wstr new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/getn_wstr @@ -0,0 +1,31 @@ +include start +# test input through unget_wch queue +wchar ACHAR 0x3401 +wchar NL "\n" +call OK unget_wch $NL +call OK unget_wch $ACHAR +call OK move 2 3 +noinput +call2 OK $ACHAR getn_wstr 2 + +# test erase and kill character processing, +# enable canonical mode; kill should erase whole line +call OK cbreak +wchar res ["c", "d", "f"] +call OK move 3 0 +input "ab\025cde\010fghi\n" +call2 OK $res getn_wstr 4 + +# test keypad translations +wchar res ["a", "c", "d", "\e", "O", "D"] +call OK move 4 4 +input "ab\010cd\eOD\n" +call2 OK $res getn_wstr 7 + +call OK keypad STDSCR $TRUE +wchar res ["a", "b", "d"] +call OK move 5 1 +input "abc\eODd\n" +call2 OK $res getn_wstr 4 + +compare getn_wstr.chk diff --git a/lib/libcurses/tests/getnstr b/lib/libcurses/tests/getnstr new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/getnstr @@ -0,0 +1,13 @@ +include start +input "testing\n" +call2 OK "testing" getnstr 10 +input "1234567\n" +call2 OK "123" getnstr 4 +# try a couple of backspaces, this should erase characters, we have +# embedded a cursor left sequence too but this should not be interpreted +input "abc\010\010def\eODgh\n" +call2 OK "adef" getnstr 5 +# turn on keypad so the embedded cursor key will affect the result +call OK keypad STDSCR $TRUE +input "abc\010\010def\eODgh\n" +call2 OK "adeg" getnstr 5 diff --git a/lib/libcurses/tests/getparx b/lib/libcurses/tests/getparx new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/getparx @@ -0,0 +1,7 @@ +include window +call win2 derwin $win1 2 3 3 2 +check win2 NON_NULL +call 2 getparx $win2 +call 3 getpary $win2 +call2 3 2 getparyx $win2 + diff --git a/lib/libcurses/tests/getstr b/lib/libcurses/tests/getstr --- a/lib/libcurses/tests/getstr +++ b/lib/libcurses/tests/getstr @@ -1,6 +1,11 @@ -include window -input "input\n" -call2 OK "input" wgetstr $win1 -compare wgetstr.chk -call OK wrefresh $win1 -compare wgetstr_refresh.chk +include start +input "testing\n" +call2 OK "testing" getstr +# try a couple of backspaces, this should erase characters, we have +# embedded a cursor left sequence too but this should not be interpreted +input "abc\010\010def\eODgh\n" +call2 OK "adef\eODgh" getstr +# turn on keypad so the embedded cursor key will affect the result +call OK keypad STDSCR $TRUE +input "abc\010\010def\eODgh\n" +call2 OK "adegh" getstr diff --git a/lib/libcurses/tests/getwin b/lib/libcurses/tests/getwin new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/getwin @@ -0,0 +1,9 @@ +include window +call OK mvwaddch $win1 2 3 `\001a` +call OK wrefresh $win1 +assign FILE "/tmp/getwin.win" +call OK putwin $win1 $FILE +call win2 getwin $FILE +check win2 NON_NULL +call OK wrefresh $win2 +call `\001a` mvwinch $win2 2 3 diff --git a/lib/libcurses/tests/halfdelay b/lib/libcurses/tests/halfdelay new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/halfdelay @@ -0,0 +1,18 @@ +include start +delay 1000 +# input delay 1000 equals to 10 tenths of seconds +# getch must fail for halfdelay(5) and pass for halfdelay(15) +input "a" +call OK halfdelay 15 +call 0x61 getch +call OK halfdelay 5 +input "a" +call -1 getch + +# leave halfdelay mode using nocbreak +# setting noecho stops getch setting cbreak itself. + +call OK noecho +call OK nocbreak +input "a\n" +call 0x61 getch diff --git a/lib/libcurses/tests/has_colors b/lib/libcurses/tests/has_colors new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/has_colors @@ -0,0 +1,2 @@ +include start +call $TRUE has_colors diff --git a/lib/libcurses/tests/has_ic b/lib/libcurses/tests/has_ic new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/has_ic @@ -0,0 +1,3 @@ +include start +call $FALSE has_ic +call $TRUE has_il diff --git a/lib/libcurses/tests/hline b/lib/libcurses/tests/hline new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/hline @@ -0,0 +1,11 @@ +include start +call OK move 5 10 +call OK hline `\000A` 15 +call OK move 7 10 +call OK hline `\004B` 15 +call OK refresh +compare hline1.chk + +call OK mvhline 3 75 `\000A` 10 +call OK refresh +compare hline2.chk diff --git a/lib/libcurses/tests/hline_set b/lib/libcurses/tests/hline_set new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/hline_set @@ -0,0 +1,8 @@ +include start +cchar ch 0x100 [0x3401] +call OK move 0 74 +call OK hline_set $ch 10 +call OK refresh +call OK mvhline_set 10 10 $ch 10 +call OK refresh +compare hline_set.chk diff --git a/lib/libcurses/tests/immedok b/lib/libcurses/tests/immedok new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/immedok @@ -0,0 +1,18 @@ +include window +call win2 newwin 6 6 10 5 +check win2 NON_NULL +call OK wrefresh $win2 +compare window2.chk + +# windows are not refreshed +call OK waddstr $win1 "hello" +call OK waddstr $win2 "world" +compare blank.chk + +call OK immedok $win1 $TRUE +call OK immedok $win2 $TRUE + +# windows are refreshed now +call OK waddstr $win1 "!" +call OK waddstr $win2 "!" +compare immedok.chk diff --git a/lib/libcurses/tests/in_wch b/lib/libcurses/tests/in_wch new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/in_wch @@ -0,0 +1,14 @@ +include start +cchar ch1 0x100 [0x3401, 0x300] +cchar ch2 0 ["A", 0x300] +cchar ch3 0x200 ["B"] +call OK add_wch $ch1 +call OK add_wch $ch2 +call OK add_wch $ch3 +call OK refresh + +call OK move 0 0 +call2 OK $ch1 in_wch +call2 OK $ch2 mvin_wch 0 2 +call2 0 2 getyx STDSCR +call2 OK $ch3 mvin_wch 0 3 diff --git a/lib/libcurses/tests/inch b/lib/libcurses/tests/inch new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/inch @@ -0,0 +1,5 @@ +include addchstr +call OK move 0 2 +call `\004c` inch +call OK move 0 6 +call `\006g` inch diff --git a/lib/libcurses/tests/inchnstr b/lib/libcurses/tests/inchnstr new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/inchnstr @@ -0,0 +1,6 @@ +include addchstr +call OK move 0 2 +call2 OK `\004c\004d\004e\006f\006g` inchnstr 6 +call OK move 0 74 +call2 OK `\000 \000 \000 \000 \000 \000 ` inchnstr 10 +call2 OK `\000 \000 \000 \000 \000 \000 ` inchstr diff --git a/lib/libcurses/tests/init_color b/lib/libcurses/tests/init_color new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/init_color @@ -0,0 +1,6 @@ +include start +call OK start_color +# init_color is not implemented, it will return ERR +call ERR init_color $COLOR_CYAN 1000 500 300 +# but we should be able to read back the settings +call4 OK 1000 500 300 color_content $COLOR_CYAN diff --git a/lib/libcurses/tests/innstr b/lib/libcurses/tests/innstr new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/innstr @@ -0,0 +1,8 @@ +include addchstr +call OK move 0 2 +call2 5 "cdefg" innstr 6 +call OK move 0 76 +call OK addstr "123" +call OK move 0 75 +call2 5 " 123 " innstr 10 +call2 OK " 123 " instr diff --git a/lib/libcurses/tests/innwstr b/lib/libcurses/tests/innwstr new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/innwstr @@ -0,0 +1,3 @@ +include addwstr +call OK move 0 0 +call2 6 $WSTR innwstr 7 diff --git a/lib/libcurses/tests/ins_nwstr b/lib/libcurses/tests/ins_nwstr new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/ins_nwstr @@ -0,0 +1,17 @@ +include start +wchar WSTR ["A", "A"*2, 0x3401, "A"*2] +call OK move 4 4 +call OK ins_nwstr $WSTR 4 +call OK refresh +compare ins_nwstr1.chk + +call OK mvaddstr 10 75 "AAA" +call OK refresh + +# test shifting of characters +wchar NSTR ["A"*4] +call OK move 10 10 +call OK ins_nwstr $NSTR -1 +call OK refresh +call2 10 10 getyx STDSCR +compare ins_wstr2.chk diff --git a/lib/libcurses/tests/ins_wch b/lib/libcurses/tests/ins_wch new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/ins_wch @@ -0,0 +1,33 @@ +include start +cchar ch1 0x100 [0x3401] +cchar ch2 0x200 "A" +call OK ins_wch $ch1 +call OK ins_wch $ch2 +call OK refresh +call2 0 0 getyx STDSCR +compare ins_wch1.chk + +call OK mvaddstr 10 75 "AAAA" +call OK refresh + +# test shifting of above added characters, test do not wrap property +call OK move 10 10 +call OK ins_wch $ch1 +call OK ins_wch $ch1 +call OK refresh +call2 10 10 getyx STDSCR +compare ins_wch2.chk + +# test special character processing +call OK move 10 75 +cchar NL 0x000 "\n" +call OK ins_wch $NL +call OK refresh + +call OK add_wch $ch2 +call OK refresh +call2 10 76 getyx STDSCR +cchar CR 0x000 "\r" +call OK ins_wch $CR +call OK refresh +compare ins_wch3.chk diff --git a/lib/libcurses/tests/ins_wstr b/lib/libcurses/tests/ins_wstr new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/ins_wstr @@ -0,0 +1,28 @@ +include start +wchar WSTR ["A", "A"*2, 0x3401, "A"*2] +call OK ins_wstr $WSTR +call OK refresh +call2 0 0 getyx STDSCR +compare ins_wstr1.chk + +# first character is non-spacing, hence it must fail +wchar FWSTR [0x300, "A"*2] +call ERR ins_wstr $FWSTR + +call OK mvaddstr 10 75 "AAA" +call OK refresh + +# test shifting of above added characters, test do not wrap property +wchar NSTR ["A"*4] +call OK move 10 10 +call OK ins_wstr $NSTR +call OK refresh +call2 10 10 getyx STDSCR +compare ins_wstr2.chk + +# lib/55433: Bug in special character handling of ins_wstr() of libcurses +# test special character processing [FAILING] +# wchar NSTR ["A"*3, "\r", "B"*2] +# call OK move 20 0 +# call OK ins_wstr $NSTR +# call OK refresh diff --git a/lib/libcurses/tests/insch b/lib/libcurses/tests/insch new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/insch @@ -0,0 +1,11 @@ +include start +call OK insch `\001E` +call OK refresh +compare insch1.chk + +call OK mvaddstr 1 77 "aaa" +call OK refresh +call OK move 1 10 +call OK insch `\000x` +call OK refresh +compare insch2.chk diff --git a/lib/libcurses/tests/insdelln b/lib/libcurses/tests/insdelln new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/insdelln @@ -0,0 +1,54 @@ +include start +include fill_screen_numbers +call OK refresh +compare fill_screen_numbers.chk + +call OK move 15 0 +call OK insdelln 2 +call OK refresh +compare insdelln1.chk + +call OK move 0 0 +call OK clear +call OK refresh +compare clear1.chk + +include fill_screen_numbers +call OK refresh +compare fill_screen_numbers.chk + +call OK setscrreg 8 16 +call OK move 12 0 +call OK insdelln 2 +call OK refresh +compare insdelln3.chk + +call OK move 0 0 +call OK clear +call OK setscrreg 0 23 +call OK refresh +compare clear1.chk + +include window_create +include fill_window_numbers +call OK wrefresh $win1 +compare fill_window_numbers.chk + +call OK wmove $win1 3 0 +call OK winsdelln $win1 2 +call OK wrefresh $win1 +compare insdelln4.chk + +call OK wmove $win1 0 0 +call OK wclear $win1 +call OK wrefresh $win1 +compare insdelln5.chk + +include fill_window_numbers +call OK wrefresh $win1 +compare fill_window_numbers.chk +call OK wmove $win1 3 0 +call OK wsetscrreg $win1 2 4 +call OK winsdelln $win1 2 +call OK wrefresh $win1 +compare insdelln6.chk diff --git a/lib/libcurses/tests/insertln b/lib/libcurses/tests/insertln new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/insertln @@ -0,0 +1,53 @@ +include start +include fill_screen_numbers +call OK refresh +compare fill_screen_numbers.chk +call OK move 15 0 +call OK insertln +call OK refresh +compare insertln1.chk + +call OK move 0 0 +call OK clear +call OK refresh +compare clear1.chk + +include fill_screen_numbers +call OK refresh +compare fill_screen_numbers.chk + +call OK setscrreg 8 16 +call OK move 12 0 +call OK insertln +call OK refresh +compare insertln3.chk + +call OK move 0 0 +call OK clear +call OK setscrreg 0 23 +call OK refresh +compare clear1.chk + +include window_create +include fill_window_numbers +call OK wrefresh $win1 +compare fill_window_numbers.chk + +call OK wmove $win1 3 0 +call OK winsertln $win1 +call OK wrefresh $win1 +compare insertln4.chk + +call OK wmove $win1 0 0 +call OK wclear $win1 +call OK wrefresh $win1 +compare insertln5.chk + +include fill_window_numbers +call OK wrefresh $win1 +compare fill_window_numbers.chk +call OK wmove $win1 3 0 +call OK wsetscrreg $win1 2 4 +call OK winsertln $win1 +call OK wrefresh $win1 +compare insertln6.chk diff --git a/lib/libcurses/tests/inwstr b/lib/libcurses/tests/inwstr new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/inwstr @@ -0,0 +1,13 @@ +include start +wchar WSTR ["A"*3, 0x3401, "A"*2] +call OK mvaddwstr 0 73 $WSTR +call OK refresh +call OK move 0 73 +call2 OK $WSTR inwstr + +# this one fails (non-spacing character should also be recieved) +# wchar WSTR2 [0x3401, 0x300] +# call OK mvaddwstr 2 78 $WSTR2 +# call OK refresh +# call OK move 2 78 +# call2 OK $WSTR2 inwstr diff --git a/lib/libcurses/tests/is_linetouched b/lib/libcurses/tests/is_linetouched new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/is_linetouched @@ -0,0 +1,10 @@ +include window +call $FALSE is_linetouched $win1 2 +call OK wmove $win1 0 2 +call OK waddch $win1 `\000A` +call $FALSE is_linetouched $win1 2 +call OK wmove $win1 2 2 +call OK waddch $win1 `\000A` +call $TRUE is_linetouched $win1 2 +call OK wrefresh $win1 +call $FALSE is_linetouched $win1 2 diff --git a/lib/libcurses/tests/is_wintouched b/lib/libcurses/tests/is_wintouched new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/is_wintouched @@ -0,0 +1,7 @@ +include window +call $FALSE is_wintouched $win1 +call OK wmove $win1 2 2 +call OK waddch $win1 `\002F` +call $TRUE is_wintouched $win1 +call OK wrefresh $win1 +call $FALSE is_wintouched $win1 diff --git a/lib/libcurses/tests/key_name b/lib/libcurses/tests/key_name new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/key_name @@ -0,0 +1,9 @@ +include start +wchar key 0x03 +call "^C" key_name $key +wchar key 0x43 +call "C" key_name $key +wchar key 0x7f +call "^?" key_name $key +wchar key 0x205 +call "UNKNOWN KEY" key_name $key diff --git a/lib/libcurses/tests/keyname b/lib/libcurses/tests/keyname new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/keyname @@ -0,0 +1,9 @@ +include start +call "^C" keyname 0x03 +call "C" keyname 0x43 +call "^?" keyname 0x7f +call "M-^C" keyname 0x83 +call "M-C" keyname 0xc3 +call "M-^?" keyname 0xff +call "KEY_RIGHT" keyname $KEY_RIGHT +call "UNKNOWN KEY" keyname 0x205 diff --git a/lib/libcurses/tests/keyok b/lib/libcurses/tests/keyok new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/keyok @@ -0,0 +1,9 @@ +include start +call OK keypad STDSCR $TRUE +input "\eOC" +call $KEY_RIGHT getch +call OK keyok $KEY_RIGHT $FALSE +input "\eOA" +call $KEY_UP getch +input "\eOC" +call 27 getch diff --git a/lib/libcurses/tests/keypad b/lib/libcurses/tests/keypad new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/keypad @@ -0,0 +1,18 @@ +include window +call $FALSE is_keypad $win1 +input "\eOA" +call 0x1b wgetch $win1 +call OK keypad $win1 $TRUE +input "\eOA" +call $KEY_UP wgetch $win1 + +# disable assembly of KEY_UP; other multi-character keys should work +call OK keyok $KEY_UP $FALSE +input "\eOA" +call 0x1b wgetch $win1 +noinput +call Ox4f wgetch $win1 +noinput +call 0x41 wgetch $win1 +input "\eOB" +call $KEY_DOWN wgetch $win1 diff --git a/lib/libcurses/tests/killchar b/lib/libcurses/tests/killchar new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/killchar @@ -0,0 +1,2 @@ +include start +call 0x15 killchar diff --git a/lib/libcurses/tests/killwchar b/lib/libcurses/tests/killwchar new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/killwchar @@ -0,0 +1,3 @@ +include start +wchar kchar 0x15 +call2 OK $kchar killwchar diff --git a/lib/libcurses/tests/leaveok b/lib/libcurses/tests/leaveok new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/leaveok @@ -0,0 +1,19 @@ +include window + +call OK waddstr $win1 "abcd" +call OK wrefresh $win1 +compare leaveok.chk + +# as leaveok for stdscr is false, the cursor is not moved to home on refresh +call $FALSE is_leaveok STDSCR +call OK leaveok STDSCR $TRUE +call $TRUE is_leaveok STDSCR +call OK refresh +call OK refresh +compare /dev/null + +# now the cursor would be moved to home as leaveok is false for stdscr +call OK leaveok STDSCR $FALSE +call $FALSE is_leaveok STDSCR +call OK refresh +compare home.chk diff --git a/lib/libcurses/tests/meta b/lib/libcurses/tests/meta new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/meta @@ -0,0 +1,5 @@ +include start +call OK meta STDSCR $TRUE +compare meta1.chk +call OK meta STDSCR $FALSE +compare meta2.chk diff --git a/lib/libcurses/tests/mutt_test b/lib/libcurses/tests/mutt_test new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/mutt_test @@ -0,0 +1,105 @@ +include start_color +include fill_screen +comparend mutt_test1.chk +comparend fill.chk +compare mutt_test2.chk +call OK attron $BOLD +call OK mvaddstr 0 0 "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE" +call OK mvaddstr 12 0 "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE" +call OK mvaddstr 22 0 "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE" +call OK refresh +call OK attroff $BOLD +compare mutt_test3.chk +# check add on bottom row, this sbould be 123456789 in normal rendition +call OK mvaddchstr 23 0 `\0041\0042\0043\0044\0045\0046\0047\0048\0049` +input "i" +call 105 getch +call OK refresh +compare mutt_test4.chk +call OK move 12 0 +input "j" +call 106 getch +call OK mvaddstr 12 0 "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE" +call OK attron $BOLD +call OK mvaddstr 13 0 "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE" +call OK attroff $BOLD +input "j" +call 106 getch +call OK mvaddstr 13 0 "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE" +call OK attron $BOLD +call OK mvaddstr 14 0 "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE" +call OK attroff $BOLD +input "j" +call 106 getch +call OK mvaddstr 14 0 "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE" +call OK attron $BOLD +call OK mvaddstr 15 0 "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE" +call OK attroff $BOLD +call OK refresh +compare mutt_test5.chk +call OK move 23 0 +call OK mvaddchstr 14 10 `\001R\001R\001R\001R\001R\001R\001R\001R\001R\001R\001R` +call OK move 23 0 +input "j" +call 106 getch +call OK refresh +call OK move 14 14 +cchar HCHAR 0 "H" +call OK add_wch $HCHAR +call OK add_wch $HCHAR +call OK add_wch $HCHAR +input "j" +call 106 getch +call OK refresh +compare mutt_test6.chk +call OK cbreak +call OK noecho +delay 2000 +input "a" +call 97 getch +call OK timeout -1 +call OK keypad STDSCR 1 +delay 0 +input "\eOA" +call $KEY_UP getch +call OK refresh +call OK move 12 0 +input "j" +call 106 getch +call OK mvaddstr 12 0 "WWWWWWWWEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE" +call OK attron $BOLD +call OK mvaddstr 13 0 "WWWWWWWWEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE" +call OK attroff $BOLD +input "j" +call 106 getch +call OK mvaddstr 13 0 "WWWWWWWWEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE" +call OK attron $BOLD +call OK mvaddstr 14 0 "WWWWWWWWEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE" +call OK attroff $BOLD +input "j" +call 106 getch +call OK mvaddstr 14 0 "WWWWWWWWEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE" +call OK attron $BOLD +call OK mvaddstr 15 0 "WWWWWWWWEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE" +call OK attroff $BOLD +call OK refresh +compare mutt_test8.chk +call OK move 23 0 +call OK mvaddchstr 14 10 `\001R\001R\001R\001R\001R\001R\001R\001R\001R\001R\001R` +call OK move 23 0 +call OK move 8 8 +call OK move 8 20 +call OK move 8 8 +call OK move 23 0 +input "j" +call 106 getch +call OK refresh +call OK move 14 14 +call OK add_wch $HCHAR +call OK add_wch $HCHAR +call OK add_wch $HCHAR +input "j" +call 106 getch +call OK refresh +compare mutt_test9.chk + diff --git a/lib/libcurses/tests/mvaddch b/lib/libcurses/tests/mvaddch new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/mvaddch @@ -0,0 +1,4 @@ +include start +call OK mvaddch 5 8 `\002E` +call OK refresh +compare mvaddch.chk diff --git a/lib/libcurses/tests/mvaddchnstr b/lib/libcurses/tests/mvaddchnstr new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/mvaddchnstr @@ -0,0 +1,7 @@ +include start +call OK mvaddchnstr 6 7 `\002A\002B\002C\002D\002E\002F\002G` 5 +call OK refresh +compare mvaddchnstr.chk +call OK mvaddchnstr 8 75 `\0041\0042\0043\0044\0045\0046\0047\0048\0049` 10 +call OK refresh +compare mvaddchnstr2.chk diff --git a/lib/libcurses/tests/mvaddchstr b/lib/libcurses/tests/mvaddchstr new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/mvaddchstr @@ -0,0 +1,7 @@ +include start +call OK mvaddchstr 6 7 `\002A\002B\002C\002D\002E` +call OK refresh +compare mvaddchnstr.chk +call OK mvaddchstr 8 75 `\0041\0042\0043\0044\0045\0046\0047\0048\0049` +call OK refresh +compare mvaddchstr.chk diff --git a/lib/libcurses/tests/mvaddnstr b/lib/libcurses/tests/mvaddnstr new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/mvaddnstr @@ -0,0 +1,7 @@ +include start +call OK mvaddnstr 7 10 "ABCDEFGH" 5 +call OK refresh +compare mvaddnstr.chk +call OK mvaddnstr 9 75 "1234567890" 10 +call OK refresh +compare mvaddnstr2.chk diff --git a/lib/libcurses/tests/mvaddnwstr b/lib/libcurses/tests/mvaddnwstr new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/mvaddnwstr @@ -0,0 +1,19 @@ +include start +# 0x3401 takes 2 columns +# test multi-column +wchar WSTR [0x41, 0x42, 0x3401, 0x43, 0x44, 0x45] +call OK mvaddnwstr 10 12 $WSTR 4 +call OK refresh +call2 10 17 getyx STDSCR +compare mvaddnwstr1.chk + +# test special character processing +wchar CR ["\r", "a", "\r", "b", "c"] +call OK mvaddnwstr 0 12 $CR 4 +call OK refresh +compare addnwstr2.chk + +# test wrapping +call OK mvaddnwstr 2 77 $WSTR -1 +call OK refresh +compare addwstr3.chk diff --git a/lib/libcurses/tests/mvaddstr b/lib/libcurses/tests/mvaddstr new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/mvaddstr @@ -0,0 +1,7 @@ +include start +call OK mvaddstr 8 12 "DEADBEEF" +call OK refresh +compare mvaddstr.chk +call OK mvaddstr 12 75 "BADCOFFEE" +call OK refresh +compare mvaddstr2.chk diff --git a/lib/libcurses/tests/mvaddwstr b/lib/libcurses/tests/mvaddwstr new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/mvaddwstr @@ -0,0 +1,19 @@ +include start +# 0x3401 takes 2 columns +# test multi-column +wchar WSTR [0x41, 0x42, 0x3401, 0x43, 0x44, 0x45] +call OK mvaddwstr 10 12 $WSTR +call OK refresh +call2 10 19 getyx STDSCR +compare mvaddwstr1.chk + +# test special character processing +wchar CR "\r" +call OK mvaddwstr 0 12 $CR +call OK refresh +compare addwstr2.chk + +# test wrapping +call OK mvaddwstr 2 77 $WSTR +call OK refresh +compare addwstr3.chk diff --git a/lib/libcurses/tests/mvchgat b/lib/libcurses/tests/mvchgat new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/mvchgat @@ -0,0 +1,10 @@ +include start_color +call OK init_pair 3 $COLOR_RED $COLOR_BLUE +call OK mvaddch 5 8 `\000R` +call OK mvchgat 5 6 10 $REVERSE 3 NULL +call OK refresh +compare mvchgat.chk +call OK mvaddch 8 77 `\000T` +call OK mvchgat 8 75 10 $UNDERSCORE 3 NULL +call OK refresh +compare mvchgat2.chk diff --git a/lib/libcurses/tests/mvcur b/lib/libcurses/tests/mvcur new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/mvcur @@ -0,0 +1,6 @@ +include start +call OK move 6 5 +call OK refresh +call OK mvcur 6 5 10 12 +call OK refresh +compare mvcur.chk diff --git a/lib/libcurses/tests/mvdelch b/lib/libcurses/tests/mvdelch new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/mvdelch @@ -0,0 +1,30 @@ +include start +call OK mvaddstr 2 2 "teest" +call OK refresh +compare delch1.chk +call OK mvdelch 2 4 +call OK refresh +compare delch2.chk +call OK mvaddch 2 6 `\001t` +call OK refresh +call OK mvdelch 2 5 +call OK refresh +compare delch3.chk + +call OK clear +call OK move 0 0 +call OK refresh +compare delch4.chk + +include window_create +call OK mvwaddstr $win1 1 0 "tesst" +call OK wrefresh $win1 +compare delch5.chk +call OK mvwdelch $win1 1 3 +call OK wrefresh $win1 +compare delch6.chk +call OK mvwaddch $win1 1 4 `\001t` +call OK wrefresh $win1 +call OK mvwdelch $win1 1 3 +call OK wrefresh $win1 +compare delch7.chk diff --git a/lib/libcurses/tests/mvderwin b/lib/libcurses/tests/mvderwin new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/mvderwin @@ -0,0 +1,30 @@ +include start +call win1 newwin 10 20 2 5 +check win1 NON_NULL +call win2 derwin $win1 4 4 2 15 +check win2 NON_NULL +call win3 newwin 5 6 15 5 +check win3 NON_NULL +# move non-subwin, should error +call ERR mvderwin $win3 5 5 +call OK waddstr $win1 "AAAA" +call OK wrefresh $win1 +call OK touchwin $win2 +call OK wrefresh $win2 +compare mvderwin1.chk +# try move +call OK mvderwin $win2 0 0 +call OK wrefresh $win1 +call OK touchwin $win2 +call OK wrefresh $win2 +compare mvderwin2.chk +call OK mvwaddstr $win1 7 7 "BBBBB" +call OK wrefresh $win1 +compare /dev/null +call OK mvderwin $win2 5 7 +call OK touchwin $win2 +call OK wrefresh $win2 +compare /dev/null +# try to move subwin outside parent, should fail +call ERR mvderwin $win2 6 17 +call ERR mvderwin $win2 7 6 diff --git a/lib/libcurses/tests/mvget_wstr b/lib/libcurses/tests/mvget_wstr new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/mvget_wstr @@ -0,0 +1,27 @@ +include start +# test input through unget_wch queue +wchar ACHAR 0x3401 +wchar NL "\n" +call OK unget_wch $NL +call OK unget_wch $ACHAR +noinput +call2 OK $ACHAR mvget_wstr 2 3 + +# test erase and kill character processing, +# enable canonical mode; kill should erase whole line +call OK cbreak +wchar res ["c", "d", "f"] +input "ab\025cde\010f\n" +call2 OK $res mvget_wstr 3 0 + +# test keypad translations +wchar res ["a", "c", "d", "\e", "O", "D"] +input "ab\010cd\eOD\n" +call2 OK $res mvget_wstr 4 4 + +call OK keypad STDSCR $TRUE +wchar res ["a", "b", "d"] +input "abc\eODd\n" +call2 OK $res mvget_wstr 5 1 + +compare get_wstr.chk diff --git a/lib/libcurses/tests/mvgetn_wstr b/lib/libcurses/tests/mvgetn_wstr new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/mvgetn_wstr @@ -0,0 +1,27 @@ +include start +# test input through unget_wch queue +wchar ACHAR 0x3401 +wchar NL "\n" +call OK unget_wch $NL +call OK unget_wch $ACHAR +noinput +call2 OK $ACHAR mvgetn_wstr 2 3 2 + +# test erase and kill character processing, +# enable canonical mode; kill should erase whole line +call OK cbreak +wchar res ["c", "d", "f"] +input "ab\025cde\010fghi\n" +call2 OK $res mvgetn_wstr 3 0 4 + +# test keypad translations +wchar res ["a", "c", "d", "\e", "O", "D"] +input "ab\010cd\eOD\n" +call2 OK $res mvgetn_wstr 4 4 7 + +call OK keypad STDSCR $TRUE +wchar res ["a", "b", "d"] +input "abc\eODd\n" +call2 OK $res mvgetn_wstr 5 1 4 + +compare getn_wstr.chk diff --git a/lib/libcurses/tests/mvgetnstr b/lib/libcurses/tests/mvgetnstr new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/mvgetnstr @@ -0,0 +1,17 @@ +include start +input "testing\n" +call2 OK "testing" mvgetnstr 2 1 10 +compare mvgetnstr1.chk +input "1234567\n" +call2 OK "123" mvgetnstr 3 2 4 +compare mvgetnstr2.chk +# try a couple of backspaces, this should erase characters, we have +# embedded a cursor left sequence too but this should not be interpreted +input "abc\010\010def\eODgh\n" +call2 OK "adef" mvgetnstr 4 1 5 +compare mvgetnstr3.chk +# turn on keypad so the embedded cursor key will affect the result +call OK keypad STDSCR $TRUE +input "abc\010\010def\eODgh\n" +call2 OK "adeg" mvgetnstr 2 2 5 +compare mvgetnstr4.chk diff --git a/lib/libcurses/tests/mvgetstr b/lib/libcurses/tests/mvgetstr new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/mvgetstr @@ -0,0 +1,14 @@ +include start +input "testing\n" +call2 OK "testing" mvgetstr 3 2 +compare mvgetstr1.chk +# try a couple of backspaces, this should erase characters, we have +# embedded a cursor left sequence too but this should not be interpreted +input "abc\010\010def\eODgh\n" +call2 OK "adef\eODgh" mvgetstr 1 4 +compare mvgetstr2.chk +# turn on keypad so the embedded cursor key will affect the result +call OK keypad STDSCR $TRUE +input "abc\010\010def\eODgh\n" +call2 OK "adegh" mvgetstr 2 1 +compare mvgetstr3.chk diff --git a/lib/libcurses/tests/mvhline b/lib/libcurses/tests/mvhline new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/mvhline @@ -0,0 +1,5 @@ +include start +call OK mvhline 5 10 `\000A` 15 +call OK mvhline 7 10 `\004B` 15 +call OK refresh +compare hline.chk diff --git a/lib/libcurses/tests/mvinch b/lib/libcurses/tests/mvinch new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/mvinch @@ -0,0 +1,3 @@ +include addchstr +call `\004c` mvinch 0 2 +call `\006g` mvinch 0 6 diff --git a/lib/libcurses/tests/mvinchnstr b/lib/libcurses/tests/mvinchnstr new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/mvinchnstr @@ -0,0 +1,4 @@ +include addchstr +call2 OK `\004c\004d\004e\006f\006g` mvinchnstr 0 2 6 +call2 OK `\000 \000 \000 \000 \000 \000 ` mvinchnstr 0 74 10 +call2 OK `\000 \000 \000 \000 \000 \000 \000 ` mvinchstr 0 73 diff --git a/lib/libcurses/tests/mvinnstr b/lib/libcurses/tests/mvinnstr new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/mvinnstr @@ -0,0 +1,5 @@ +include addchstr +call2 5 "cdefg" mvinnstr 0 2 6 +call OK mvaddstr 0 76 "123" +call2 5 " 123 " mvinnstr 0 75 10 +call2 OK " 123 " mvinstr 0 75 diff --git a/lib/libcurses/tests/mvinnwstr b/lib/libcurses/tests/mvinnwstr new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/mvinnwstr @@ -0,0 +1,4 @@ +include addwstr +call OK move 15 15 +call OK refresh +call2 6 $WSTR mvinnwstr 0 0 7 diff --git a/lib/libcurses/tests/mvins_nwstr b/lib/libcurses/tests/mvins_nwstr new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/mvins_nwstr @@ -0,0 +1,15 @@ +include start +wchar WSTR ["A", "A"*2, 0x3401, "A"*2] +call OK mvins_nwstr 4 4 $WSTR 4 +call OK refresh +compare ins_nwstr1.chk + +call OK mvaddstr 10 75 "AAA" +call OK refresh + +# test shifting of characters +wchar NSTR ["A"*4] +call OK mvins_nwstr 10 10 $NSTR -1 +call OK refresh +call2 10 10 getyx STDSCR +compare ins_wstr2.chk diff --git a/lib/libcurses/tests/mvins_wch b/lib/libcurses/tests/mvins_wch new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/mvins_wch @@ -0,0 +1,7 @@ +include start +cchar ch1 0x100 [0x3401] +cchar ch2 0x200 "A" +call OK mvins_wch 2 5 $ch1 +call OK ins_wch $ch2 +call OK refresh +compare mvins_wch.chk diff --git a/lib/libcurses/tests/mvins_wstr b/lib/libcurses/tests/mvins_wstr new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/mvins_wstr @@ -0,0 +1,17 @@ +include start +wchar WSTR ["A", "A"*2, 0x3401, "A"*2] +call OK move 15 15 +call OK mvins_wstr 0 0 $WSTR +call OK refresh +call2 0 0 getyx STDSCR +compare ins_wstr1.chk + +call OK mvaddstr 10 75 "AAA" +call OK refresh + +# test shifting of above added characters, test do not wrap property +wchar NSTR ["A"*4] +call OK mvins_wstr 10 10 $NSTR +call OK refresh +call2 10 10 getyx STDSCR +compare ins_wstr2.chk diff --git a/lib/libcurses/tests/mvinsch b/lib/libcurses/tests/mvinsch new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/mvinsch @@ -0,0 +1,10 @@ +include start +call OK mvinsch 0 0 `\001E` +call OK refresh +compare insch1.chk + +call OK mvaddstr 1 77 "aaa" +call OK refresh +call OK mvinsch 1 10 `\000x` +call OK refresh +compare insch2.chk diff --git a/lib/libcurses/tests/mvinwstr b/lib/libcurses/tests/mvinwstr new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/mvinwstr @@ -0,0 +1,5 @@ +include start +wchar WSTR ["A"*3, 0x3401, "A"*2] +call OK mvaddwstr 0 73 $WSTR +call OK refresh +call2 OK $WSTR mvinwstr 0 73 diff --git a/lib/libcurses/tests/mvprintw b/lib/libcurses/tests/mvprintw new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/mvprintw @@ -0,0 +1,4 @@ +include start +call OK mvprintw 10 14 "%s" "testing 1 2 3" +call OK refresh +compare mvprintw.chk diff --git a/lib/libcurses/tests/mvscanw b/lib/libcurses/tests/mvscanw new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/mvscanw @@ -0,0 +1,13 @@ +# XXX +# For this test, only one string or 32-bit integer are supported as +# conversion specifiers at the moment. +include start +input "testing 1 2 3\n" +call2 OK "testing" mvscanw 3 5 "%s" +input "testing 1 2 3\n" +call2 OK "test" mvscanw 3 5 "%4s" +input "50 12\n" +call2 OK "50" mvscanw 3 5 "%d" +input "aa bb 50 12\n" +# expect ERR because input has alpha and scanw wants integer +call2 ERR "ERR" mvscanw 3 5 "%d" diff --git a/lib/libcurses/tests/mvvline b/lib/libcurses/tests/mvvline new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/mvvline @@ -0,0 +1,11 @@ +include start +call OK move 3 5 +call OK vline `\000B` 15 +call OK refresh +compare mvvline1.chk +call OK mvvline 10 12 `\004C` 12 +call OK refresh +compare mvvline2.chk +call OK mvvline 18 8 `\006D` 20 +call OK refresh +compare mvvline3.chk diff --git a/lib/libcurses/tests/mvwaddchnstr b/lib/libcurses/tests/mvwaddchnstr new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/mvwaddchnstr @@ -0,0 +1,11 @@ +include window +# note that there are more characters in the array, check we only get 3 out +call OK mvwaddchnstr $win1 1 0 `\004a\004b\004c\004d` 3 +call OK wrefresh $win1 +# we should get all 3 characters +call OK mvwaddchnstr $win1 2 2 `\004a\004b\004c` -1 +call OK wrefresh $win1 +# we should get only 3 characters +call OK mvwaddchnstr $win1 3 3 `\004a\004b\004c\004d` -1 +call OK wrefresh $win1 +compare waddchnstr.chk diff --git a/lib/libcurses/tests/mvwaddchstr b/lib/libcurses/tests/mvwaddchstr new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/mvwaddchstr @@ -0,0 +1,8 @@ +include window +call OK mvwaddchstr $win1 0 0 `\004a\004b\004c` +call OK wrefresh $win1 +# test should not wrap property and overlay property +call OK wbkgdset $win1 `\002\000` +call OK mvwaddchstr $win1 1 1 `\004d\004e\004f\004g\004h\004i` +call OK wrefresh $win1 +compare waddchstr.chk diff --git a/lib/libcurses/tests/mvwaddnstr b/lib/libcurses/tests/mvwaddnstr new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/mvwaddnstr @@ -0,0 +1,4 @@ +include window +call OK mvwaddnstr $win1 2 1 "abcdef" 4 +call OK wrefresh $win1 +compare mvwaddstr.chk diff --git a/lib/libcurses/tests/mvwaddnwstr b/lib/libcurses/tests/mvwaddnwstr new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/mvwaddnwstr @@ -0,0 +1,22 @@ +include window +# 0x3401 takes 2 columns +# test multi-column +wchar WSTR [0x41, 0x42, 0x3401, 0x43] +call OK mvwaddnwstr $win1 5 1 $WSTR 3 +call OK wrefresh $win1 +call2 5 5 getyx $win1 +call OK wmove $win1 0 4 +call OK wrefresh $win1 +compare mvwaddnwstr1.chk + +# test special character processing +wchar CR ["\r", "a", "\r", "b", "c"] +call OK mvwaddnwstr $win1 0 3 $CR 4 +call OK wrefresh $win1 +call2 0 1 getyx $win1 +compare waddnwstr2.chk + +# test wrapping +call OK mvwaddnwstr $win1 2 3 $WSTR -1 +call OK wrefresh $win1 +compare waddwstr3.chk diff --git a/lib/libcurses/tests/mvwaddstr b/lib/libcurses/tests/mvwaddstr new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/mvwaddstr @@ -0,0 +1,4 @@ +include window +call OK mvwaddstr $win1 2 1 "abcd" +call OK wrefresh $win1 +compare mvwaddstr.chk diff --git a/lib/libcurses/tests/mvwaddwstr b/lib/libcurses/tests/mvwaddwstr new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/mvwaddwstr @@ -0,0 +1,20 @@ +include window +# 0x3401 takes 2 columns +# test multi-column +wchar WSTR [0x41, 0x42, 0x3401, 0x43] +call OK mvwaddwstr $win1 2 1 $WSTR +call OK wrefresh $win1 +call2 3 0 getyx $win1 +compare mvwaddwstr1.chk + +# test special character processing +wchar CR "\r" +call OK mvwaddwstr $win1 1 4 $CR +call OK wrefresh $win1 +call2 1 0 getyx $win1 +compare mvwaddwstr2.chk + +# test wrapping +call OK mvwaddwstr $win1 3 2 $WSTR +call OK wrefresh $win1 +compare mvwaddwstr3.chk diff --git a/lib/libcurses/tests/mvwchgat b/lib/libcurses/tests/mvwchgat new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/mvwchgat @@ -0,0 +1,11 @@ +include start_color +call OK init_pair 3 $COLOR_RED $COLOR_BLUE +include window_create +call OK mvwaddch $win1 5 2 `\000R` +call OK mvwchgat $win1 5 1 10 $UNDERSCORE 3 NULL +call OK wrefresh $win1 +compare mvwchgat1.chk +call OK mvwaddch $win1 2 4 `\000T` +call OK mvwchgat $win1 2 4 2 $REVERSE 3 NULL +call OK wrefresh $win1 +compare mvwchgat2.chk diff --git a/lib/libcurses/tests/mvwget_wstr b/lib/libcurses/tests/mvwget_wstr new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/mvwget_wstr @@ -0,0 +1,27 @@ +include window +# test input through unget_wch queue +wchar ACHAR 0x3401 +wchar NL "\n" +call OK unget_wch $NL +call OK unget_wch $ACHAR +noinput +call2 OK $ACHAR mvwget_wstr $win1 2 3 + +# test erase and kill character processing, +# enable canonical mode; kill should erase whole line +call OK cbreak +wchar res ["c", "d", "f"] +input "ab\025cde\010f\n" +call2 OK $res mvwget_wstr $win1 3 0 + +# test keypad translations +wchar res ["a", "c", "\e", "O", "D"] +input "ab\010c\eOD\n" +call2 OK $res mvwget_wstr $win1 4 0 + +call OK keypad $win1 $TRUE +wchar res ["a", "b", "d"] +input "abc\eODd\n" +call2 OK $res mvwget_wstr $win1 5 1 + +compare wget_wstr.chk diff --git a/lib/libcurses/tests/mvwgetn_wstr b/lib/libcurses/tests/mvwgetn_wstr new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/mvwgetn_wstr @@ -0,0 +1,27 @@ +include window +# test input through unget_wch queue +wchar ACHAR 0x3401 +wchar NL "\n" +call OK unget_wch $NL +call OK unget_wch $ACHAR +noinput +call2 OK $ACHAR mvwgetn_wstr $win1 2 3 2 + +# test erase and kill character processing, +# enable canonical mode; kill should erase whole line +call OK cbreak +wchar res ["c", "d", "f"] +input "ab\025cde\010fghi\n" +call2 OK $res mvwgetn_wstr $win1 3 0 4 + +# test keypad translations +wchar res ["a", "c", "\e", "O", "D"] +input "ab\010c\eOD\n" +call2 OK $res mvwgetn_wstr $win1 4 0 6 + +call OK keypad $win1 $TRUE +wchar res ["a", "b", "d"] +input "abc\eODd\n" +call2 OK $res mvwgetn_wstr $win1 5 1 4 + +compare wgetn_wstr.chk diff --git a/lib/libcurses/tests/mvwgetnstr b/lib/libcurses/tests/mvwgetnstr new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/mvwgetnstr @@ -0,0 +1,13 @@ +include window +input "test\n" +call2 OK "test" mvwgetnstr $win1 2 1 10 +input "1234\n" +call2 OK "123" mvwgetnstr $win1 3 2 4 +# try a couple of backspaces, this should erase characters, we have +# embedded a cursor left sequence too but this should not be interpreted +input "abc\010\010def\eODgh\n" +call2 OK "adef" mvwgetnstr $win1 4 1 5 +# turn on keypad so the embedded cursor key will affect the result +call OK keypad $win1 $TRUE +input "abc\010\010def\eODgh\n" +call2 OK "adeg" mvwgetnstr $win1 2 2 5 diff --git a/lib/libcurses/tests/mvwgetstr b/lib/libcurses/tests/mvwgetstr new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/mvwgetstr @@ -0,0 +1,11 @@ +include window +input "test\n" +call2 OK "test" mvwgetstr $win1 3 2 +# try a couple of backspaces, this should erase characters, we have +# embedded a cursor left sequence too but this should not be interpreted +input "abc\010\010def\eODgh\n" +call2 OK "adef\eODgh" mvwgetstr $win1 1 4 +# turn on keypad so the embedded cursor key will affect the result +call OK keypad $win1 $TRUE +input "abc\010\010def\eODgh\n" +call2 OK "adegh" mvwgetstr $win1 2 1 diff --git a/lib/libcurses/tests/mvwin b/lib/libcurses/tests/mvwin --- a/lib/libcurses/tests/mvwin +++ b/lib/libcurses/tests/mvwin @@ -2,11 +2,11 @@ call OK wmove $win1 1 1 call OK wprintw $win1 "%s" "xxxx" call OK wrefresh $win1 -compare /dev/zero +compare /dev/null call OK refresh -compare /dev/zero +compare /dev/null call OK mvwin $win1 4 7 call OK wrefresh $win1 -compare /dev/zero +compare /dev/null call OK refresh -compare /dev/zero +compare /dev/null diff --git a/lib/libcurses/tests/mvwinch b/lib/libcurses/tests/mvwinch new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/mvwinch @@ -0,0 +1,3 @@ +include waddchstr +call `\004a` mvwinch $win1 0 0 +call `\006e` mvwinch $win1 1 2 diff --git a/lib/libcurses/tests/mvwinchnstr b/lib/libcurses/tests/mvwinchnstr new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/mvwinchnstr @@ -0,0 +1,4 @@ +include waddchstr +call2 OK `\004a\004b\004c\000 ` mvwinchnstr $win1 0 0 5 +call2 OK `\000 \006d\006e\006f` mvwinchnstr $win1 1 0 5 +call2 OK `\000 \006d\006e\006f\006g\006h` mvwinchstr $win1 1 0 diff --git a/lib/libcurses/tests/mvwinnstr b/lib/libcurses/tests/mvwinnstr new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/mvwinnstr @@ -0,0 +1,7 @@ +include window +call OK waddstr $win1 "abcde" +call2 2 "cd" mvwinnstr $win1 0 2 3 +call OK wmove $win1 2 3 +call OK waddstr $win1 "123" +call2 4 " 123" mvwinnstr $win1 2 2 10 +call2 OK " 123" mvwinstr $win1 2 2 diff --git a/lib/libcurses/tests/mvwinnwstr b/lib/libcurses/tests/mvwinnwstr new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/mvwinnwstr @@ -0,0 +1,3 @@ +include waddwstr +wchar WC [0x42, 0x3401] +call2 2 $WC mvwinnwstr $win1 0 1 3 diff --git a/lib/libcurses/tests/mvwins_nwstr b/lib/libcurses/tests/mvwins_nwstr new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/mvwins_nwstr @@ -0,0 +1,16 @@ +include window +wchar WSTR ["A", 0x3401, "A"*3] +call OK mvwins_nwstr $win1 2 1 $WSTR 3 +call OK wrefresh $win1 +call2 2 1 getyx $win1 +compare wins_wstr1.chk + +call OK mvwaddstr $win1 4 4 "BB" +call OK wrefresh $win1 + +# test shifting of above added characters, test do not wrap property +wchar NSTR ["A"*2] +call OK mvwins_nwstr $win1 4 1 $NSTR -1 +call OK wrefresh $win1 +call2 4 1 getyx $win1 +compare wins_wstr2.chk diff --git a/lib/libcurses/tests/mvwins_wch b/lib/libcurses/tests/mvwins_wch new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/mvwins_wch @@ -0,0 +1,7 @@ +include window +cchar ch1 0x100 [0x3401] +cchar ch2 0x200 "A" +call OK mvwins_wch $win1 2 3 $ch1 +call OK wins_wch $win1 $ch2 +call OK wrefresh $win1 +compare mvwins_wch.chk diff --git a/lib/libcurses/tests/mvwins_wstr b/lib/libcurses/tests/mvwins_wstr new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/mvwins_wstr @@ -0,0 +1,16 @@ +include window +wchar WSTR ["A", 0x3401, "A"] +call OK mvwins_wstr $win1 2 1 $WSTR +call OK wrefresh $win1 +call2 2 1 getyx $win1 +compare wins_wstr1.chk + +call OK mvwaddstr $win1 4 4 "BB" +call OK wrefresh $win1 + +# test shifting of above added characters, test do not wrap property +wchar NSTR ["A"*2] +call OK mvwins_wstr $win1 4 1 $NSTR +call OK wrefresh $win1 +call2 4 1 getyx $win1 +compare wins_wstr2.chk diff --git a/lib/libcurses/tests/mvwinsch b/lib/libcurses/tests/mvwinsch new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/mvwinsch @@ -0,0 +1,10 @@ +include window +call OK mvwinsch $win1 0 0 `\001E` +call OK wrefresh $win1 +compare winsch1.chk + +call OK mvwaddstr $win1 1 3 "aaa" +call OK wrefresh $win1 +call OK mvwinsch $win1 1 1 `\000x` +call OK wrefresh $win1 +compare winsch2.chk diff --git a/lib/libcurses/tests/mvwinwstr b/lib/libcurses/tests/mvwinwstr new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/mvwinwstr @@ -0,0 +1,5 @@ +include window +wchar WSTR ["A"*2, 0x3401, "A"] +call OK mvwaddwstr $win1 2 1 $WSTR +call OK wrefresh $win1 +call2 OK $WSTR mvwinwstr $win1 2 1 diff --git a/lib/libcurses/tests/newwin b/lib/libcurses/tests/newwin new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/newwin @@ -0,0 +1,11 @@ +include start +call win1 newwin 6 6 2 5 +check win1 NON_NULL +call2 2 5 getbegyx $win1 +call2 6 6 getmaxyx $win1 + +# test window creation with negative parameters +call win2 newwin -5 -10 1 1 +check win2 NON_NULL +call2 1 1 getbegyx $win2 +call2 18 69 getmaxyx $win2 diff --git a/lib/libcurses/tests/nocbreak b/lib/libcurses/tests/nocbreak new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/nocbreak @@ -0,0 +1,18 @@ +include cbreak +# The above tests should leave input in cbreak mode, switch back to +# no cbreak mode and check. Set a timeout and input delay such that +# the input delay is greater than the timeout. IFF we are not in cbreak +# mode the timeouts will be ignored (canonical processing) so we should +# see all the characters arrive, if the nocbreak() does not work then +# we will get timeouts and fail. +# +call OK nocbreak +delay 200 +call OK timeout 100 +input "fg\n" +# should fail - no newline +call 0x66 getch +noinput +call 0x67 getch +noinput +call 0x0a getch diff --git a/lib/libcurses/tests/nodelay b/lib/libcurses/tests/nodelay new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/nodelay @@ -0,0 +1,7 @@ +include window +call OK nodelay $win1 $TRUE +noinput +call -1 wgetch $win1 +call OK nodelay $win1 $FALSE +input "j" +call 106 wgetch $win1 diff --git a/lib/libcurses/tests/notimeout b/lib/libcurses/tests/notimeout new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/notimeout @@ -0,0 +1,33 @@ +include start +call OK keypad STDSCR $TRUE +delay 0 +input "\eOA" +call $KEY_UP getch +call OK refresh + +# despite of having keypad enabled, the input for assembled characters is +# broken, ESC_DELAY is by default 300ms and we have set delay 1000ms which is +# greater than that +delay 1000 +input "\eOA" +call 0x1b getch +call OK refresh + +call OK notimeout STDSCR $TRUE +input "\eOA" +call $KEY_UP getch +call OK refresh + +compare notimeout.chk + +# check the same for window +include window_create +call OK keypad $win1 $TRUE +input "\eOA" +call 0x1b wgetch $win1 +call OK wrefresh $win1 + +call OK notimeout $win1 $TRUE +input "\eOA" +call $KEY_UP wgetch $win1 +call OK wrefresh $win1 diff --git a/lib/libcurses/tests/overlay b/lib/libcurses/tests/overlay new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/overlay @@ -0,0 +1,15 @@ +include two_window + +call OK mvwaddstr $win2 0 0 "A A" +call OK mvwaddstr $win2 1 0 " AAA" +call OK wrefresh $win2 +call OK wrefresh $win1 +compare overlay1.chk + +include fill_window_numbers +call OK wrefresh $win1 +compare fill_window_numbers.chk + +call OK overlay $win2 $win1 +call OK wrefresh $win1 +compare overlay2.chk diff --git a/lib/libcurses/tests/overwrite b/lib/libcurses/tests/overwrite new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/overwrite @@ -0,0 +1,35 @@ +include start + +include window_create +call OK mvwaddstr $win1 0 0 "AAAAAA" +call OK mvwaddstr $win1 1 0 "BBBBBB" +call OK mvwaddstr $win1 2 0 "CCCCCC" +call OK mvwaddstr $win1 3 0 "DDDDDD" +call OK mvwaddstr $win1 4 0 "EEEEEE" +# last character cannot be there due to wrapping +call OK mvwaddstr $win1 5 0 "FFFFF" +call OK wrefresh $win1 +compare overwrite1.chk + +call win2 newwin 6 6 6 7 +call OK mvwaddstr $win2 0 0 "AAAAAA" +call OK mvwaddstr $win2 1 0 "BBBBBB" +call OK mvwaddstr $win2 2 0 "CCCCCC" +call OK wrefresh $win2 +compare overwrite2.chk + +call OK overwrite $win1 $win2 +compare overwrite3.chk + +call2 OK "EEEEAA" mvwinstr $win2 0 0 +call2 OK "FFF BB" mvwinstr $win2 1 0 +call2 OK "CCCCCC" mvwinstr $win2 2 0 + +# Check for multi-byte characters, 0x3401 is chinese multi-byte character +cchar CHAR 0 [0x3401, 0] +call OK mvwadd_wch $win1 4 1 $CHAR + +call OK overwrite $win1 $win2 +compare overwrite4.chk + +call2 OK " EEEAA" mvwinstr $win2 0 0 diff --git a/lib/libcurses/tests/pad b/lib/libcurses/tests/pad new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/pad @@ -0,0 +1,30 @@ +include start +call pad newpad 24 120 +check pad NON_NULL +call OK mvwprintw $pad 12 100 "%s" "testingtestingtestingtesting" +call OK mvwprintw $pad 13 100 "%s" "testingtestingtestingtesting" +call OK mvwprintw $pad 14 100 "%s" "testingtestingtestingtesting" +call OK prefresh $pad 11 100 10 5 14 15 +compare pad1.chk + +call OK mvwprintw $pad 12 105 "%s" "222222222222" +call OK mvwprintw $pad 13 105 "%s" "222222222222" +call OK mvwprintw $pad 14 105 "%s" "222222222222" +call OK pnoutrefresh $pad 11 100 10 5 14 15 +call OK mvwprintw $pad 12 107 "%s" "333333333333" +call OK mvwprintw $pad 13 107 "%s" "333333333333" +call OK mvwprintw $pad 14 107 "%s" "333333333333" +call OK pnoutrefresh $pad 11 100 10 5 14 15 +call OK doupdate +compare pad2.chk + +call sub subpad $pad 6 10 9 102 +check sub NON_NULL +call OK mvwprintw $sub 1 1 "%s" "4444444444" +call OK mvwprintw $sub 1 2 "%s" "4444444444" +call OK mvwprintw $sub 1 3 "%s" "4444444444" +call OK mvwprintw $sub 1 4 "%s" "4444444444" +call OK mvwprintw $sub 1 5 "%s" "4444444444" +call OK mvwprintw $sub 1 6 "%s" "4444444444" +call OK prefresh $pad 5 100 8 6 18 18 +compare pad3.chk diff --git a/lib/libcurses/tests/pair_content b/lib/libcurses/tests/pair_content new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/pair_content @@ -0,0 +1,7 @@ +include start_color +call OK init_pair 3 $COLOR_YELLOW $COLOR_CYAN +call3 OK $COLOR_YELLOW $COLOR_CYAN pair_content 3 + +# check for default: init_pair for 0 is nop +call OK init_pair 0 $COLOR_YELLOW $COLOR_CYAN +call3 OK $COLOR_WHITE $COLOR_BLACK pair_content 0 diff --git a/lib/libcurses/tests/pechochar b/lib/libcurses/tests/pechochar new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/pechochar @@ -0,0 +1,17 @@ +include start + +call pad newpad 5 5 +call sub subpad $pad 3 3 1 1 +# the following prefresh mapping is used in pechochar/pecho_wchar +call OK prefresh $pad 0 0 0 0 5 5 + +# reversing the order of following lines will require calling touchwin on pad +call OK pechochar $sub `\001B` +call OK pechochar $pad `\002A` +compare pechochar1.chk + +cchar wch 0x400 [0x3401] +call OK wmove $sub 1 0 +call OK pecho_wchar $sub $wch +call OK pecho_wchar $pad $wch +compare pechochar2.chk diff --git a/lib/libcurses/tests/redrawwin b/lib/libcurses/tests/redrawwin new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/redrawwin @@ -0,0 +1,20 @@ +include start +call OK mvaddstr 0 0 "foo" +call OK mvaddstr 2 10 "bar" +call OK refresh +compare redrawwin1.chk + +call OK redrawwin STDSCR +call OK mvaddstr 2 10 "bar" +call OK refresh +call2 2 13 getyx STDSCR +call OK move 0 0 +call OK refresh +compare redrawwin2.chk + +include window_create +call OK mvwaddstr $win1 0 0 "test b" +# 'b' should also be written and curses should not perform any optimization +call OK redrawwin $win1 +call OK wrefresh $win1 +compare redrawwin3.chk diff --git a/lib/libcurses/tests/scroll b/lib/libcurses/tests/scroll new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/scroll @@ -0,0 +1,25 @@ +include start +include fill_screen_numbers +call OK refresh +compare fill_screen_numbers.chk +call OK move 5 5 +call OK scrollok STDSCR $TRUE +call OK scroll STDSCR +call OK refresh +call2 5 5 getyx STDSCR +compare scroll1.chk + +call OK clear +call OK refresh +compare clear1.chk + +include window_create +include fill_window_numbers +call OK wrefresh $win1 +compare fill_window_numbers.chk +call OK wmove $win1 3 3 +call OK scrollok $win1 $TRUE +call OK scroll $win1 +call OK wrefresh $win1 +call2 3 3 getyx $win1 +compare scroll2.chk diff --git a/lib/libcurses/tests/setscrreg b/lib/libcurses/tests/setscrreg new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/setscrreg @@ -0,0 +1,10 @@ +include start +include fill_screen_numbers +call OK refresh +compare fill_screen_numbers.chk + +call OK setscrreg 5 8 +call OK scrollok STDSCR $TRUE +call OK scrl 2 +call OK refresh +compare setscrreg.chk diff --git a/lib/libcurses/tests/slk b/lib/libcurses/tests/slk new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/slk @@ -0,0 +1,60 @@ +include start_slk + +# test slk attributes +call OK slk_set 1 "one" 0 +call OK slk_refresh + +call OK slk_attron `\003\000` +call OK slk_set 1 "one" 1 +call OK slk_refresh + +call OK slk_attroff `\002\000` +call OK slk_set 1 "one" 2 +call OK slk_refresh + +call OK slk_attrset `\006\000` +call OK slk_set 8 "eight!!!" 1 +call OK slk_set 5 "five" 1 +call OK slk_refresh + +# test slk_label +call "one" slk_label 1 +call "eight!!!" slk_label 8 +call "five" slk_label 5 +call NULL slk_label 2 +compare slk1.chk + +# test slk_clear +call OK slk_clear +compare slk2.chk + +# test slk_restore +call OK slk_restore +#call OK slk_refresh +compare slk3.chk + +# test slk_noutrefresh +call OK slk_set 2 "two" 0 +call OK slk_noutrefresh +call OK refresh +compare slk4.chk + +# test slk_touch +call OK slk_touch +call OK slk_set 2 "two" 1 +call OK slk_refresh +compare slk5.chk + +# test slk_color +call OK start_color +call OK init_pair 4 $COLOR_RED $COLOR_GREEN +call OK slk_color 4 +call OK slk_set 4 "four" 2 +call OK slk_refresh +compare slk6.chk + +# test slk_wset +# [buggy??] 'b' should be printed as (wcswidth(wstr) == 6) <= 8 +#wchar WSTR [0x3401, "a", 0x3401, "b"] +#call OK slk_wset 3 $WSTR 1 +#call OK slk_refresh diff --git a/lib/libcurses/tests/standout b/lib/libcurses/tests/standout new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/standout @@ -0,0 +1,8 @@ +include start +call3 OK 0 0 attr_get +call OK attr_set ($REVERSE | $BOLD) 2 +call3 OK ($ACS_IS_WACS | $REVERSE | $BOLD) 2 attr_get +call 1 standout +call3 OK ($ACS_IS_WACS | $REVERSE | $BOLD | $STANDOUT) 2 attr_get +call 1 standend +call3 OK $NORMAL 0 attr_get diff --git a/lib/libcurses/tests/start b/lib/libcurses/tests/start --- a/lib/libcurses/tests/start +++ b/lib/libcurses/tests/start @@ -1,3 +1,5 @@ include std_defines +call mainscr initscr +check mainscr NON_NULL call OK refresh compare curses_start.chk diff --git a/lib/libcurses/tests/start_slk b/lib/libcurses/tests/start_slk new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/start_slk @@ -0,0 +1,6 @@ +include std_defines +call OK slk_init 0 +call mainscr initscr +check mainscr NON_NULL +call OK refresh +compare slk_init.chk diff --git a/lib/libcurses/tests/std_defines b/lib/libcurses/tests/std_defines --- a/lib/libcurses/tests/std_defines +++ b/lib/libcurses/tests/std_defines @@ -135,4 +135,4 @@ assign KEY_UNDO 0x198 assign KEY_MOUSE 0x199 assign KEY_RESIZE 0x200 - +assign KEY_CODE_YES 0x241 diff --git a/lib/libcurses/tests/timeout b/lib/libcurses/tests/timeout --- a/lib/libcurses/tests/timeout +++ b/lib/libcurses/tests/timeout @@ -10,8 +10,8 @@ # since delay is in effect and we set timeout the following getch should # return ERR not the character b. call -1 getch -# drain input.... -call OK drain +# drain input ... +call OK DRAIN STDSCR call OK timeout -1 call OK keypad STDSCR 1 delay 0 diff --git a/lib/libcurses/tests/touchline b/lib/libcurses/tests/touchline new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/touchline @@ -0,0 +1,26 @@ +include window +include fill_window_numbers +call OK wrefresh $win1 +compare fill_window_numbers.chk + +call win2 subwin $win1 3 3 3 6 +check win2 NON_NULL +call OK wmove $win2 0 0 +call OK winsstr $win2 "999" +call OK wmove $win2 1 0 +call OK winsstr $win2 "999" +call OK wmove $win2 2 0 +call OK winsstr $win2 "999" +call OK touchline $win1 3 3 +call OK wrefresh $win1 +compare touchline1.chk + +# test on stdscr +call OK mvaddstr 15 15 "aaaaaa" +call OK mvaddstr 16 15 "bbbbbb" +call OK mvaddstr 17 15 "cccccc" +call OK mvaddstr 18 15 "dddddd" +call OK refresh +call OK touchline STDSCR 16 2 +call OK refresh +compare touchline2.chk diff --git a/lib/libcurses/tests/touchoverlap b/lib/libcurses/tests/touchoverlap new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/touchoverlap @@ -0,0 +1,16 @@ +include two_window +include fill_window_numbers +call OK untouchwin $win1 +call OK wrefresh $win1 +compare blank.chk + +# partial overlap +call OK touchoverlap $win2 $win1 +call OK wrefresh $win1 +compare touchoverlap1.chk + +# full overlap +call win3 newwin 2 2 3 6 +call OK touchoverlap $win3 $win1 +call OK wrefresh $win1 +compare touchoverlap2.chk diff --git a/lib/libcurses/tests/touchwin b/lib/libcurses/tests/touchwin new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/touchwin @@ -0,0 +1,16 @@ +include two_window +include fill_window_numbers +call OK wrefresh $win1 +compare fill_window_numbers.chk + +call2 5 0 getyx $win1 +call OK waddstr $win2 "window" +call OK wrefresh $win2 +call OK mvwaddstr $win1 3 0 "xxxxxx" +call OK wrefresh $win1 + +# must redraw the whole win2 including "window" text +call OK mvwaddstr $win2 1 0 "yyyyyy" +call OK touchwin $win2 +call OK wrefresh $win2 +compare touchwin.chk diff --git a/lib/libcurses/tests/two_window b/lib/libcurses/tests/two_window new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/two_window @@ -0,0 +1,9 @@ +include window +call win2 newwin 6 6 5 8 +check win2 NON_NULL + +# this ordering of refresh enables uniform checking of other files +# (say fill_window_numbers) +call OK wrefresh $win2 +call OK wrefresh $win1 +compare two_window.chk diff --git a/lib/libcurses/tests/underscore b/lib/libcurses/tests/underscore new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/underscore @@ -0,0 +1,8 @@ +include start +call3 OK 0 0 attr_get +call OK attr_set ($REVERSE | $BOLD) 2 +call3 OK ($ACS_IS_WACS | $REVERSE | $BOLD) 2 attr_get +call 1 underscore +call3 OK ($ACS_IS_WACS | $REVERSE | $BOLD | $UNDERSCORE) 2 attr_get +call 1 underend +call3 OK ($ACS_IS_WACS | $REVERSE | $BOLD) 2 attr_get diff --git a/lib/libcurses/tests/unget_wch b/lib/libcurses/tests/unget_wch new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/unget_wch @@ -0,0 +1,8 @@ +include start +wchar CH 0x3401 +call OK ungetch 97 +call OK unget_wch $CH +noinput +call2 $KEY_CODE_YES $CH get_wch +noinput +call 97 getch diff --git a/lib/libcurses/tests/untouchwin b/lib/libcurses/tests/untouchwin new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/untouchwin @@ -0,0 +1,7 @@ +include window +include fill_window_numbers +call OK untouchwin $win1 +call OK wrefresh $win1 +call OK touchwin $win1 +call OK wrefresh $win1 +compare untouchwin.chk diff --git a/lib/libcurses/tests/varcheck b/lib/libcurses/tests/varcheck new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/varcheck @@ -0,0 +1,35 @@ +include start +call OK addstr "ABCDE" +call OK refresh +call2 1 STR1 mvinnstr 0 0 2 +call2 1 STR2 mvinnstr 0 0 2 +call okvar move 2 5 +check okvar okvar +check okvar OK + +call2 Y1 X1 getyx STDSCR +call2 Y2 X2 getyx STDSCR +check STR1 STR2 +check Y1 Y2 +check X1 X2 + +# check complex character +cchar CHAR 0x300 [0x3401, 0x300] +call OK move 5 5 +call OK add_wch $CHAR +call OK refresh +call OK move 5 5 +call OK refresh +call2 OK NCHAR in_wch +check CHAR NCHAR + +# check wide character string +wchar CHARW ['a', 'b', 0x3401] +call OK clear +call OK move 0 0 +call OK refresh +call OK addwstr $CHARW +call OK refresh +call OK move 0 0 +call2 3 $CHARW innwstr 4 + diff --git a/lib/libcurses/tests/vline_set b/lib/libcurses/tests/vline_set new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/vline_set @@ -0,0 +1,8 @@ +include start +cchar ch 0x100 [0x3401] +call OK move 20 0 +call OK vline_set $ch 10 +call OK refresh +call OK mvvline_set 10 10 $ch 10 +call OK refresh +compare vline_set.chk diff --git a/lib/libcurses/tests/wadd_wch b/lib/libcurses/tests/wadd_wch new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/wadd_wch @@ -0,0 +1,30 @@ +include window +cchar HCHAR 0 "H" +call OK wadd_wch $win1 $HCHAR +call2 0 1 getyx $win1 +call OK wadd_wch $win1 $HCHAR +call OK wadd_wch $win1 $HCHAR +call OK wrefresh $win1 +compare wadd_wch1.chk + +# tests for multi-column characters +call OK wmove $win1 1 0 +cchar ACHAR 0x100 0x3401 +call OK wadd_wch $win1 $ACHAR +call OK wrefresh $win1 +call2 1 2 getyx $win1 + +cchar CHAR 0 0x3401 +# test for wrapping +call OK wmove $win1 1 5 +call OK wadd_wch $win1 $CHAR +call OK wadd_wch $win1 $CHAR +call OK wrefresh $win1 +call2 2 4 getyx $win1 +compare wadd_wch2.chk + +# test for special character processing +cchar BACK 0 0x8 +call OK wadd_wch $win1 $BACK +call OK wrefresh $win1 +call2 2 3 getyx $win1 diff --git a/lib/libcurses/tests/waddch b/lib/libcurses/tests/waddch new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/waddch @@ -0,0 +1,6 @@ +include window +call OK waddch $win1 `\001a` +call OK wrefresh $win1 +call OK mvwaddch $win1 2 3 `\001b` +call OK wrefresh $win1 +compare waddch.chk diff --git a/lib/libcurses/tests/waddchnstr b/lib/libcurses/tests/waddchnstr new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/waddchnstr @@ -0,0 +1,14 @@ +include window +# note that there are more characters in the array, check we only get 3 out +call OK wmove $win1 1 0 +call OK waddchnstr $win1 `\004a\004b\004c\004d` 3 +call OK wrefresh $win1 +call OK wmove $win1 2 2 +# we should get all 3 characters +call OK waddchnstr $win1 `\004a\004b\004c` -1 +call OK wrefresh $win1 +call OK wmove $win1 3 3 +# we should get only 3 characters +call OK waddchnstr $win1 `\004a\004b\004c\004d` -1 +call OK wrefresh $win1 +compare waddchnstr.chk diff --git a/lib/libcurses/tests/waddchstr b/lib/libcurses/tests/waddchstr new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/waddchstr @@ -0,0 +1,9 @@ +include window +call OK waddchstr $win1 `\004a\004b\004c` +call OK wrefresh $win1 +call OK wmove $win1 1 1 +# test should not wrap property and overlay property +call OK wbkgdset $win1 `\002\000` +call OK waddchstr $win1 `\004d\004e\004f\004g\004h\004i` +call OK wrefresh $win1 +compare waddchstr.chk diff --git a/lib/libcurses/tests/waddnstr b/lib/libcurses/tests/waddnstr new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/waddnstr @@ -0,0 +1,4 @@ +include window +call OK waddnstr $win1 "abcdefg" 5 +call OK wrefresh $win1 +compare addnstr.chk diff --git a/lib/libcurses/tests/waddnwstr b/lib/libcurses/tests/waddnwstr new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/waddnwstr @@ -0,0 +1,21 @@ +include window +# 0x3401 takes 2 columns +# test multi-column +wchar WSTR [0x41, 0x42, 0x3401, 0x43] +call OK waddnwstr $win1 $WSTR 3 +call OK wrefresh $win1 +call2 0 4 getyx $win1 +compare waddnwstr1.chk + +# test special character processing +wchar CR ["\r", "a", "\r", "b", "c"] +call OK waddnwstr $win1 $CR 4 +call OK wrefresh $win1 +call2 0 1 getyx $win1 +compare waddnwstr2.chk + +# test wrapping +call OK wmove $win1 2 3 +call OK waddnwstr $win1 $WSTR -1 +call OK wrefresh $win1 +compare waddwstr3.chk diff --git a/lib/libcurses/tests/waddstr b/lib/libcurses/tests/waddstr new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/waddstr @@ -0,0 +1,9 @@ +include window +call OK waddstr $win1 "abcde\n" +call OK waddstr $win1 "\n" +call OK waddstr $win1 "\t8\n" +# XXX: The following line causes an unexpected "ERR Resource temporarily +# unavailable" on NetBSD/amd64 9.99.80. +#call OK waddstr $win1 "0123456\t8\n" +call OK refresh +compare waddstr.chk diff --git a/lib/libcurses/tests/waddwstr b/lib/libcurses/tests/waddwstr new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/waddwstr @@ -0,0 +1,21 @@ +include window +# 0x3401 takes 2 columns +# test multi-column +wchar WSTR [0x41, 0x42, 0x3401, 0x43] +call OK waddwstr $win1 $WSTR +call OK wrefresh $win1 +call2 0 5 getyx $win1 +compare waddwstr1.chk + +# test special character processing +wchar CR "\r" +call OK waddwstr $win1 $CR +call OK wrefresh $win1 +call2 0 0 getyx $win1 +compare waddwstr2.chk + +# test wrapping +call OK wmove $win1 2 3 +call OK waddwstr $win1 $WSTR +call OK wrefresh $win1 +compare waddwstr3.chk diff --git a/lib/libcurses/tests/wattributes b/lib/libcurses/tests/wattributes new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/wattributes @@ -0,0 +1,23 @@ +include window +# no attributes, no color +call3 OK 0 0 wattr_get $win1 +# set reverse and bold +call OK wattr_set $win1 ($BOLD | $REVERSE) 2 +# returned attributes includes color information +call3 OK ($ACS_IS_WACS | $BOLD | $REVERSE) 2 wattr_get $win1 +# turn off reverse +call OK wattr_off $win1 $REVERSE +call3 OK ($ACS_IS_WACS | $BOLD) 2 wattr_get $win1 +# turn on standout +call OK wattr_on $win1 $STANDOUT +call3 OK ($ACS_IS_WACS | $BOLD | $STANDOUT) 2 wattr_get $win1 +# turn on blink +call OK wattron $win1 $BLINK +call3 OK ($ACS_IS_WACS | $BOLD | $STANDOUT | $BLINK) 2 wattr_get $win1 +# turn off bold +call OK wattroff $win1 $BOLD +call3 OK ($ACS_IS_WACS | $STANDOUT | $BLINK) 2 wattr_get $win1 +# print out something to check our attributes are there, standout and blink +call OK wprintw $win1 "%s" "hello" +call OK wrefresh $win1 +compare attributes.chk diff --git a/lib/libcurses/tests/wborder_set b/lib/libcurses/tests/wborder_set new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/wborder_set @@ -0,0 +1,17 @@ +include window +cchar ls 0 "A" +cchar rs 0 "B" +cchar ts 0x100 "C" +cchar bs 0 "D" +cchar tl 0 "E" +cchar tr 0 "F" +cchar bl 0x100 "G" +cchar br 0 "H" +call OK wborder_set $win1 $ls $rs $ts $bs $tl $tr $bl $br +call OK wrefresh $win1 +compare wborder_set1.chk + +# test in case of default values +call OK wborder_set $win1 NULL NULL NULL NULL NULL NULL NULL NULL +call OK wrefresh $win1 +compare wborder_set2.chk diff --git a/lib/libcurses/tests/wchgat b/lib/libcurses/tests/wchgat new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/wchgat @@ -0,0 +1,15 @@ +include start_color +call OK init_pair 3 $COLOR_YELLOW $COLOR_CYAN +include window_create +call OK waddch $win1 `\000d` +call OK wchgat $win1 3 $UNDERSCORE 3 NULL +call OK wrefresh $win1 +compare wchgat1.chk +call OK waddch $win1 `\000e` +call OK wrefresh $win1 +compare wchgat2.chk +call OK wmove $win1 0 0 +# The following should apply the attribute and colour to the whole line +call OK wchgat $win1 -1 $REVERSE 3 NULL +call OK wrefresh $win1 +compare wchgat3.chk diff --git a/lib/libcurses/tests/wcolor_set b/lib/libcurses/tests/wcolor_set new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/wcolor_set @@ -0,0 +1,11 @@ +include window +call OK start_color +call OK refresh +comparend color_start.chk +comparend home.chk +compare color_blank_draw.chk +call OK init_pair 4 $COLOR_RED $COLOR_GREEN +call OK wcolor_set $win1 4 NULL +call OK wprintw $win1 "%s" "test" +call OK wrefresh $win1 +compare wcolor_set.chk diff --git a/lib/libcurses/tests/wecho_wchar b/lib/libcurses/tests/wecho_wchar new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/wecho_wchar @@ -0,0 +1,28 @@ +include window +cchar HCHAR 0 "H" +call OK wecho_wchar $win1 $HCHAR +call2 0 1 getyx $win1 +call OK wecho_wchar $win1 $HCHAR +call OK wecho_wchar $win1 $HCHAR +compare wadd_wch1.chk + +# tests for multi-column characters +call OK wmove $win1 1 0 +cchar ACHAR 0x100 0x3401 +call OK wecho_wchar $win1 $ACHAR +call2 1 2 getyx $win1 + +cchar CHAR 0 0x3401 +# test for wrapping +call OK wmove $win1 1 5 +call OK wecho_wchar $win1 $CHAR +call OK wecho_wchar $win1 $CHAR +call OK wrefresh $win1 +call2 2 4 getyx $win1 +compare wadd_wch2.chk + +# test for special character processing +cchar BACK 0 0x8 +call OK wecho_wchar $win1 $BACK +call OK wrefresh $win1 +call2 2 3 getyx $win1 diff --git a/lib/libcurses/tests/wget_wstr b/lib/libcurses/tests/wget_wstr new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/wget_wstr @@ -0,0 +1,31 @@ +include window +# test input through unget_wch queue +wchar ACHAR 0x3401 +wchar NL "\n" +call OK unget_wch $NL +call OK unget_wch $ACHAR +call OK wmove $win1 2 3 +noinput +call2 OK $ACHAR wget_wstr $win1 + +# test erase and kill character processing, +# enable canonical mode; kill should erase whole line +call OK cbreak +wchar res ["c", "d", "f"] +call OK wmove $win1 3 0 +input "ab\025cde\010f\n" +call2 OK $res wget_wstr $win1 + +# test keypad translations +wchar res ["a", "c", "\e", "O", "D"] +call OK wmove $win1 4 0 +input "ab\010c\eOD\n" +call2 OK $res wget_wstr $win1 + +call OK keypad $win1 $TRUE +wchar res ["a", "b", "d"] +call OK wmove $win1 5 1 +input "abc\eODd\n" +call2 OK $res wget_wstr $win1 + +compare wget_wstr.chk diff --git a/lib/libcurses/tests/wgetn_wstr b/lib/libcurses/tests/wgetn_wstr new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/wgetn_wstr @@ -0,0 +1,31 @@ +include window +# test input through unget_wch queue +wchar ACHAR 0x3401 +wchar NL "\n" +call OK unget_wch $NL +call OK unget_wch $ACHAR +call OK wmove $win1 2 3 +noinput +call2 OK $ACHAR wgetn_wstr $win1 2 + +# test erase and kill character processing, +# enable canonical mode; kill should erase whole line +call OK cbreak +wchar res ["c", "d", "f"] +call OK wmove $win1 3 0 +input "ab\025cde\010fghi\n" +call2 OK $res wgetn_wstr $win1 4 + +# test keypad translations +wchar res ["a", "c", "\e", "O", "D"] +call OK wmove $win1 4 0 +input "ab\010c\eOD\n" +call2 OK $res wgetn_wstr $win1 6 + +call OK keypad $win1 $TRUE +wchar res ["a", "b", "d"] +call OK wmove $win1 5 1 +input "abc\eODd\n" +call2 OK $res wgetn_wstr $win1 4 + +compare wgetn_wstr.chk diff --git a/lib/libcurses/tests/wgetnstr b/lib/libcurses/tests/wgetnstr new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/wgetnstr @@ -0,0 +1,13 @@ +include window +input "test\n" +call2 OK "test" wgetnstr $win1 10 +input "12345\n" +call2 OK "123" wgetnstr $win1 4 +# try a couple of backspaces, this should erase characters, we have +# embedded a cursor left sequence too but this should not be interpreted +input "abc\010\010def\eODgh\n" +call2 OK "adef" wgetnstr $win1 5 +# turn on keypad so the embedded cursor key will affect the result +call OK keypad $win1 $TRUE +input "abc\010\010def\eODgh\n" +call2 OK "adeg" wgetnstr $win1 5 diff --git a/lib/libcurses/tests/wgetstr b/lib/libcurses/tests/wgetstr new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/wgetstr @@ -0,0 +1,11 @@ +include window +input "test\n" +call2 OK "test" wgetstr $win1 +# try a couple of backspaces, this should erase characters, we have +# embedded a cursor left sequence too but this should not be interpreted +input "abc\010\010de\eODf\n" +call2 OK "ade\eODf" wgetstr $win1 +# turn on keypad so the embedded cursor key will affect the result +call OK keypad $win1 $TRUE +input "abc\010\010de\eODf\n" +call2 OK "adf" wgetstr $win1 diff --git a/lib/libcurses/tests/whline b/lib/libcurses/tests/whline new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/whline @@ -0,0 +1,13 @@ +include window +call OK wmove $win1 1 2 +call OK whline $win1 `\000A` 15 +call OK wmove $win1 0 1 +call OK whline $win1 `\004B` 3 +call OK wrefresh $win1 +compare whline1.chk + +call OK mvwhline $win1 4 2 `\000A` 15 +call OK wrefresh $win1 +call OK mvwhline $win1 3 1 `\004B` 3 +call OK wrefresh $win1 +compare whline2.chk diff --git a/lib/libcurses/tests/whline_set b/lib/libcurses/tests/whline_set new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/whline_set @@ -0,0 +1,8 @@ +include window +cchar ch 0x100 [0x3401] +call OK wmove $win1 0 2 +call OK whline_set $win1 $ch 10 +call OK wrefresh $win1 +call OK mvwhline_set $win1 1 1 $ch 2 +call OK wrefresh $win1 +compare whline_set.chk diff --git a/lib/libcurses/tests/win_wch b/lib/libcurses/tests/win_wch new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/win_wch @@ -0,0 +1,14 @@ +include window +cchar ch1 0x100 [0x3401, 0x300] +cchar ch2 0 ["A", 0x300] +cchar ch3 0x200 ["B"] +call OK wadd_wch $win1 $ch1 +call OK wadd_wch $win1 $ch2 +call OK wadd_wch $win1 $ch3 +call OK wrefresh $win1 + +call OK wmove $win1 0 0 +call2 OK $ch1 win_wch $win1 +call2 OK $ch2 mvwin_wch $win1 0 2 +call2 0 2 getyx $win1 +call2 OK $ch3 mvwin_wch $win1 0 3 diff --git a/lib/libcurses/tests/winch b/lib/libcurses/tests/winch new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/winch @@ -0,0 +1,5 @@ +include waddchstr +call OK wmove $win1 0 0 +call `\004a` winch $win1 +call OK wmove $win1 1 2 +call `\006e` winch $win1 diff --git a/lib/libcurses/tests/winchnstr b/lib/libcurses/tests/winchnstr new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/winchnstr @@ -0,0 +1,6 @@ +include waddchstr +call OK wmove $win1 0 0 +call2 OK `\004a\004b\004c\000 ` winchnstr $win1 5 +call OK wmove $win1 1 0 +call2 OK `\000 \006d\006e\006f` winchnstr $win1 5 +call2 OK `\000 \006d\006e\006f\006g\006h` winchstr $win1 diff --git a/lib/libcurses/tests/window_hierarchy b/lib/libcurses/tests/window_hierarchy new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/window_hierarchy @@ -0,0 +1,23 @@ +include window + +# creates hierarchy of 4 windows +# win2,win3,win4 have same background `\001x` as inherited from win2 +# squared sized windows have 6,5,4,3 side length + +call win2 subwin $win1 5 5 3 6 +check win2 NON_NULL +call OK wbkgd $win2 `\002x` +# window background is inherited from ancestor windows + +call win3 subwin $win2 4 4 4 7 +check win3 NON_NULL + +call win4 subwin $win3 3 3 5 8 +check win4 NON_NULL + +call OK wrefresh $win4 +call OK wrefresh $win3 +call OK wrefresh $win2 +call OK wrefresh $win1 + +compare window_hierarchy.chk diff --git a/lib/libcurses/tests/winnstr b/lib/libcurses/tests/winnstr new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/winnstr @@ -0,0 +1,9 @@ +include window +call OK waddstr $win1 "abcde" +call OK wmove $win1 0 2 +call2 2 "cd" winnstr $win1 3 +call OK wmove $win1 2 3 +call OK waddstr $win1 "123" +call OK wmove $win1 2 2 +call2 4 " 123" winnstr $win1 10 +call2 OK " 123" winstr $win1 diff --git a/lib/libcurses/tests/winnwstr b/lib/libcurses/tests/winnwstr new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/winnwstr @@ -0,0 +1,4 @@ +include waddwstr +wchar WC [0x42, 0x3401] +call OK wmove $win1 0 1 +call2 2 $WC winnwstr $win1 3 diff --git a/lib/libcurses/tests/wins_nwstr b/lib/libcurses/tests/wins_nwstr new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/wins_nwstr @@ -0,0 +1,18 @@ +include window +wchar WSTR ["A", 0x3401, "A"*3] +call OK wmove $win1 2 1 +call OK wins_nwstr $win1 $WSTR 3 +call OK wrefresh $win1 +call2 2 1 getyx $win1 +compare wins_wstr1.chk + +call OK mvwaddstr $win1 4 4 "BB" +call OK wrefresh $win1 + +# test shifting of above added characters, test do not wrap property +wchar NSTR ["A"*2] +call OK wmove $win1 4 1 +call OK wins_nwstr $win1 $NSTR -1 +call OK wrefresh $win1 +call2 4 1 getyx $win1 +compare wins_wstr2.chk diff --git a/lib/libcurses/tests/wins_wch b/lib/libcurses/tests/wins_wch new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/wins_wch @@ -0,0 +1,32 @@ +include window +cchar ch1 0x100 [0x3401] +cchar ch2 0x200 "A" +call OK wins_wch $win1 $ch1 +call OK wins_wch $win1 $ch2 +call OK wrefresh $win1 +call2 0 0 getyx $win1 +compare wins_wch1.chk + +call OK mvwaddstr $win1 2 3 "AA" +call OK wrefresh $win1 + +# test shifting of above added characters, test do not wrap property +call OK wmove $win1 2 1 +call OK wins_wch $win1 $ch1 +call OK wrefresh $win1 +call2 2 1 getyx $win1 +compare wins_wch2.chk + +# test special character processing +call OK wmove $win1 2 4 +cchar NL 0x000 "\n" +call OK wins_wch $win1 $NL +call OK wrefresh $win1 + +call OK wadd_wch $win1 $ch2 +call OK wrefresh $win1 +call2 2 5 getyx $win1 +cchar CR 0x000 "\r" +call OK wins_wch $win1 $CR +call OK wrefresh $win1 +compare wins_wch3.chk diff --git a/lib/libcurses/tests/wins_wstr b/lib/libcurses/tests/wins_wstr new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/wins_wstr @@ -0,0 +1,18 @@ +include window +wchar WSTR ["A", 0x3401, "A"] +call OK wmove $win1 2 1 +call OK wins_wstr $win1 $WSTR +call OK wrefresh $win1 +call2 2 1 getyx $win1 +compare wins_wstr1.chk + +call OK mvwaddstr $win1 4 4 "BB" +call OK wrefresh $win1 + +# test shifting of above added characters, test do not wrap property +wchar NSTR ["A"*2] +call OK wmove $win1 4 1 +call OK wins_wstr $win1 $NSTR +call OK wrefresh $win1 +call2 4 1 getyx $win1 +compare wins_wstr2.chk diff --git a/lib/libcurses/tests/winsch b/lib/libcurses/tests/winsch new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/winsch @@ -0,0 +1,11 @@ +include window +call OK winsch $win1 `\001E` +call OK wrefresh $win1 +compare winsch1.chk + +call OK mvwaddstr $win1 1 3 "aaa" +call OK wrefresh $win1 +call OK wmove $win1 1 1 +call OK winsch $win1 `\000x` +call OK wrefresh $win1 +compare winsch2.chk diff --git a/lib/libcurses/tests/winwstr b/lib/libcurses/tests/winwstr new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/winwstr @@ -0,0 +1,6 @@ +include window +wchar WSTR ["A"*2, 0x3401, "A"] +call OK mvwaddwstr $win1 2 1 $WSTR +call OK wrefresh $win1 +call OK wmove $win1 2 1 +call2 OK $WSTR winwstr $win1 diff --git a/lib/libcurses/tests/wredrawln b/lib/libcurses/tests/wredrawln new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/wredrawln @@ -0,0 +1,15 @@ +include window +include fill_window_numbers +call OK untouchwin $win1 +call OK wredrawln $win1 3 2 +call OK wrefresh $win1 +compare wredrawln1.chk + +call OK wclear $win1 +call OK wrefresh $win1 + +include fill_window_numbers +call OK untouchwin $win1 +call OK wredrawln $win1 3 4 +call OK wrefresh $win1 +compare wredrawln2.chk diff --git a/lib/libcurses/tests/wsetscrreg b/lib/libcurses/tests/wsetscrreg new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/wsetscrreg @@ -0,0 +1,12 @@ +include window + +include fill_window_numbers +call OK wrefresh $win1 +compare fill_window_numbers.chk + +call2 5 0 getyx $win1 +call OK wsetscrreg $win1 2 3 +call OK scrollok $win1 $TRUE +call OK wscrl $win1 -1 +call OK wrefresh $win1 +compare wsetscrreg.chk diff --git a/lib/libcurses/tests/wstandout b/lib/libcurses/tests/wstandout new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/wstandout @@ -0,0 +1,8 @@ +include window +call3 OK 0 0 wattr_get $win1 +call OK wattr_set $win1 ($REVERSE | $BOLD) 2 +call3 OK ($ACS_IS_WACS | $REVERSE | $BOLD) 2 wattr_get $win1 +call 1 wstandout $win1 +call3 OK ($ACS_IS_WACS | $REVERSE | $BOLD | $STANDOUT) 2 wattr_get $win1 +call 1 wstandend $win1 +call3 OK $NORMAL 0 wattr_get $win1 diff --git a/lib/libcurses/tests/wtimeout b/lib/libcurses/tests/wtimeout new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/wtimeout @@ -0,0 +1,21 @@ +# +# Validate the timeout works. +# +include window +delay 2000 +input "a" +call 97 wgetch $win1 +call OK wtimeout $win1 100 +input "b" +# since delay is in effect and we set timeout the following getch should +# return ERR not the character b. +call -1 wgetch $win1 +# drain input ... +call OK DRAIN $win1 +call OK wtimeout $win1 -1 +call OK keypad $win1 1 +delay 0 +input "\eOA" +call $KEY_UP wgetch $win1 +call OK wrefresh $win1 +#compare timeout.chk diff --git a/lib/libcurses/tests/wtouchln b/lib/libcurses/tests/wtouchln new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/wtouchln @@ -0,0 +1,10 @@ +include window +include fill_window_numbers +call OK wmove $win1 0 0 +call OK wtouchln $win1 0 6 $FALSE +call OK wtouchln $win1 2 2 $TRUE +call OK wtouchln $win1 0 3 $FALSE +call OK wtouchln $win1 1 1 $TRUE +call OK wtouchln $win1 1 0 $FALSE +call OK wrefresh $win1 +compare wtouchln.chk diff --git a/lib/libcurses/tests/wunderscore b/lib/libcurses/tests/wunderscore new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/wunderscore @@ -0,0 +1,8 @@ +include window +call3 OK 0 0 wattr_get $win1 +call OK wattr_set $win1 ($REVERSE | $BOLD) 2 +call3 OK ($ACS_IS_WACS | $REVERSE | $BOLD) 2 wattr_get $win1 +call 1 wunderscore $win1 +call3 OK ($ACS_IS_WACS | $REVERSE | $BOLD | $UNDERSCORE) 2 wattr_get $win1 +call 1 wunderend $win1 +call3 OK ($ACS_IS_WACS | $REVERSE | $BOLD) 2 wattr_get $win1 diff --git a/lib/libcurses/tests/wvline b/lib/libcurses/tests/wvline new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/wvline @@ -0,0 +1,16 @@ +include window +call OK wmove $win1 1 2 +call OK wvline $win1 `\000B` 15 +call OK wmove $win1 0 1 +call OK wvline $win1 `\001A` 3 +call OK wrefresh $win1 +compare wvline1.chk + +call OK wclear $win1 +call OK wrefresh $win1 +compare wvline2.chk + +call OK mvwvline $win1 1 2 `\000B` 15 +call OK mvwvline $win1 0 1 `\001A` 3 +call OK wrefresh $win1 +compare wvline1.chk diff --git a/lib/libcurses/tests/wvline_set b/lib/libcurses/tests/wvline_set new file mode 100644 --- /dev/null +++ b/lib/libcurses/tests/wvline_set @@ -0,0 +1,14 @@ +include window +cchar ch 0x100 [0x3401] +call OK wmove $win1 1 0 +call OK wvline_set $win1 $ch 10 +call OK wrefresh $win1 +call OK mvwvline_set $win1 2 2 $ch 3 +call OK wrefresh $win1 + +# no character will be printed as character of width 2 cannot be inserted in +# last column +call OK mvwvline_set $win1 0 5 $ch 3 +call OK wrefresh $win1 +call2 0 5 getyx $win1 +compare wvline_set.chk diff --git a/lib/libevent/Makefile b/lib/libevent/Makefile --- a/lib/libevent/Makefile +++ b/lib/libevent/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.2 2013/04/11 17:00:12 christos Exp $ +# $NetBSD: Makefile,v 1.6 2021/04/12 06:08:31 mrg Exp $ TESTSDIR= ${TESTSBASE}/lib/libevent @@ -17,15 +17,16 @@ regress_rpc.c regress_main.c tinytest.c regress_testutils.c \ regress_minheap.c regress_util.c regress_thread.c \ regress_buffer.c regress_bufferevent.c regress_listener.c \ - regress_zlib.c regress_et.c + regress_zlib.c regress_et.c regress_finalize.c -.if ${MKCRYPTO} == "yes" SRCS+= regress_ssl.c DPADD+= ${LIBEVENT_OPENSSL} ${LIBSSL} ${LIBCRYPTO} LDADD+= -levent_openssl -lssl -lcrypto CPPFLAGS+= -DLIBEVENT_CRYPTO -.endif DPADD+= ${LIBEVENT} ${LIBEVENT_PTHREADS} ${LIBZ} ${LIBPTHREAD} LDADD+= -levent_pthreads -levent -lz -lpthread +COPTS.regress_rpc.c+= ${GCC_NO_CAST_FUNCTION_TYPE} +COPTS.regress_dns.c+= ${GCC_NO_MAYBE_UNINITIALIZED} + .include diff --git a/lib/libevent/t_event.sh b/lib/libevent/t_event.sh old mode 100755 new mode 100644 diff --git a/lib/libi386/Makefile b/lib/libi386/Makefile new file mode 100644 --- /dev/null +++ b/lib/libi386/Makefile @@ -0,0 +1,15 @@ +# $NetBSD: Makefile,v 1.4 2020/05/11 21:51:25 yhardy Exp $ + +.include + +TESTSDIR= ${TESTSBASE}/lib/libi386 + +.if ${MACHINE} == "amd64" && ${MKCOMPAT} == "yes" +SHLIBINSTALLDIR= /usr/lib/i386 +COPTS+= -m32 +LDFLAGS+= -m32 +LDADD+= -li386 +TESTS_C+= t_user_ldt +.endif + +.include diff --git a/lib/libi386/t_user_ldt.c b/lib/libi386/t_user_ldt.c new file mode 100644 --- /dev/null +++ b/lib/libi386/t_user_ldt.c @@ -0,0 +1,351 @@ +/* $NetBSD: t_user_ldt.c,v 1.6 2021/04/30 13:53:30 christos Exp $ */ + +/* + * Copyright (c) 2020 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Maxime Villard. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +static uint8_t *ldt_base; +static bool user_ldt_supported; + +static void +user_ldt_detect(void) +{ + union descriptor desc; + int ret; + + ret = i386_get_ldt(0, &desc, 1); + user_ldt_supported = (ret != -1) || (errno != ENOSYS && errno != EPERM); +} + +static void +init_ldt_base(void) +{ + ldt_base = (uint8_t *)mmap(NULL, 2 * PAGE_SIZE, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON, -1, 0); + if (ldt_base == MAP_FAILED) + atf_tc_fail("mmap failed"); + munmap(ldt_base + PAGE_SIZE, PAGE_SIZE); +} + +static void +build_desc(union descriptor *desc, void *basep, uint32_t limit, int type, + int dpl, int def32, int gran) +{ + uintptr_t base = (uintptr_t)basep; + + limit--; + + desc->sd.sd_lolimit = limit & 0x0000ffff; + desc->sd.sd_lobase = base & 0x00ffffff; + desc->sd.sd_type = type & 0x1F; + desc->sd.sd_dpl = dpl & 0x3; + desc->sd.sd_p = 1; + desc->sd.sd_hilimit = (limit & 0x00ff0000) >> 16; + desc->sd.sd_xx = 0; + desc->sd.sd_def32 = def32 ? 1 : 0; + desc->sd.sd_gran = gran ? 1 : 0; + desc->sd.sd_hibase = (base & 0xff000000) >> 24; +} + +static void +set_ds(unsigned int val) +{ + __asm volatile("mov %0,%%ds"::"r" ((unsigned short)val)); +} + +static void +set_fs(unsigned int val) +{ + __asm volatile("mov %0,%%fs"::"r" ((unsigned short)val)); +} + +static uint8_t __noinline +get_fs_byte(const char *addr) +{ + uint8_t val; + __asm volatile ( + ".globl fs_read_begin; fs_read_begin:\n" + "movb %%fs:%1,%0\n" + ".globl fs_read_end; fs_read_end:\n" + : "=q" (val) : "m" (*addr) + ); + return val; +} + +/* -------------------------------------------------------------------------- */ + +ATF_TC(filter_ops); +ATF_TC_HEAD(filter_ops, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Ensure that the kernel correctly filters the descriptors"); +} +ATF_TC_BODY(filter_ops, tc) +{ + union descriptor desc; + const int forbidden_types[] = { + SDT_SYS286TSS, + SDT_SYSLDT, + SDT_SYS286BSY, + SDT_SYS286CGT, + SDT_SYSTASKGT, + SDT_SYS286IGT, + SDT_SYS286TGT, + SDT_SYSNULL2, + SDT_SYS386TSS, + SDT_SYSNULL3, + SDT_SYS386BSY, + SDT_SYS386CGT, + SDT_SYSNULL4, + SDT_SYS386IGT, + SDT_SYS386TGT + }; + size_t i; + + if (!user_ldt_supported) { + atf_tc_skip("USER_LDT disabled"); + } + + /* The first LDT slots should not be settable. */ + for (i = 0; i < 10; i++) { + build_desc(&desc, ldt_base, PAGE_SIZE, SDT_MEMRW, + SEL_UPL, 1, 0); + ATF_REQUIRE_EQ(i386_set_ldt(i, &desc, 1), -1); + ATF_REQUIRE_EQ(errno, EINVAL); + } + + /* SEL_KPL should not be allowed. */ + build_desc(&desc, ldt_base, PAGE_SIZE, SDT_MEMRW, SEL_KPL, 1, 0); + ATF_REQUIRE_EQ(i386_set_ldt(256, &desc, 1), -1); + ATF_REQUIRE_EQ(errno, EACCES); + + /* Long-mode segments should not be allowed. */ + build_desc(&desc, ldt_base, PAGE_SIZE, SDT_MEMRW, SEL_UPL, 1, 0); + desc.sd.sd_xx = 0b11; /* sd_avl | sd_long */ + ATF_REQUIRE_EQ(i386_set_ldt(256, &desc, 1), -1); + ATF_REQUIRE_EQ(errno, EACCES); + + /* No forbidden type should be allowed. */ + for (i = 0; i < __arraycount(forbidden_types); i++) { + build_desc(&desc, ldt_base, PAGE_SIZE, forbidden_types[i], + SEL_UPL, 1, 0); + ATF_REQUIRE_EQ(i386_set_ldt(256, &desc, 1), -1); + ATF_REQUIRE_EQ(errno, EACCES); + } + + /* Check the slot limit. */ + build_desc(&desc, ldt_base, PAGE_SIZE, SDT_MEMRW, SEL_UPL, 1, 0); + ATF_REQUIRE_EQ(i386_set_ldt(MAX_USERLDT_SLOTS-1, &desc, 1), + MAX_USERLDT_SLOTS-1); + ATF_REQUIRE_EQ(i386_set_ldt(MAX_USERLDT_SLOTS, &desc, 1), -1); + ATF_REQUIRE_EQ(errno, EINVAL); +} + +/* -------------------------------------------------------------------------- */ + +static void +iretq_gp__gp_handler(int signo, siginfo_t *sig, void *ctx) +{ + ATF_REQUIRE(sig->si_signo == SIGSEGV); + ATF_REQUIRE(sig->si_code == SEGV_ACCERR); + atf_tc_pass(); + /* NOTREACHED */ +} + +ATF_TC(iretq_gp); +ATF_TC_HEAD(iretq_gp, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Ensure that the kernel correctly handles iretq #GP faults"); +} +ATF_TC_BODY(iretq_gp, tc) +{ + union descriptor desc; + struct sigaction act; + + if (!user_ldt_supported) { + atf_tc_skip("USER_LDT disabled"); + } + + /* Build a desc, set %ds to it. */ + build_desc(&desc, 0, 0xFFFFFUL, SDT_MEMRWA, SEL_UPL, 1, 1); + ATF_REQUIRE_EQ(i386_set_ldt(256, &desc, 1), 256); + set_ds(LSEL(256, SEL_UPL)); + + /* Register the fault handler. */ + memset(&act, 0, sizeof(act)); + act.sa_sigaction = iretq_gp__gp_handler; + act.sa_flags = SA_SIGINFO; + ATF_REQUIRE_EQ(sigaction(SIGSEGV, &act, NULL), 0); + + /* Set NULL on %ds, iretq should fault. */ + memset(&desc, 0, sizeof(desc)); + ATF_REQUIRE_EQ(i386_set_ldt(256, &desc, 1), 256); + + atf_tc_fail("test did not fault as expected"); +} + +/* -------------------------------------------------------------------------- */ + +static void +iretq_np__np_handler(int signo, siginfo_t *sig, void *ctx) +{ + ATF_REQUIRE(sig->si_signo == SIGBUS); + ATF_REQUIRE(sig->si_code == BUS_ADRERR); + atf_tc_pass(); + /* NOTREACHED */ +} + +ATF_TC(iretq_np); +ATF_TC_HEAD(iretq_np, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Ensure that the kernel correctly handles iretq #NP faults"); +} +ATF_TC_BODY(iretq_np, tc) +{ + union descriptor desc; + struct sigaction act; + + if (!user_ldt_supported) { + atf_tc_skip("USER_LDT disabled"); + } + + /* Build a desc, set %ds to it. */ + build_desc(&desc, 0, 0xFFFFFFFFUL, SDT_MEMRWA, SEL_UPL, 1, 1); + ATF_REQUIRE_EQ(i386_set_ldt(256, &desc, 1), 256); + set_ds(LSEL(256, SEL_UPL)); + + /* Register the fault handler. */ + memset(&act, 0, sizeof(act)); + act.sa_sigaction = iretq_np__np_handler; + act.sa_flags = SA_SIGINFO; + ATF_REQUIRE_EQ(sigaction(SIGBUS, &act, NULL), 0); + + /* Set non-present on %ds, iretq should fault. */ + desc.sd.sd_p = 0; + ATF_REQUIRE_EQ(i386_set_ldt(256, &desc, 1), 256); + + atf_tc_fail("test did not fault as expected"); +} + +/* -------------------------------------------------------------------------- */ + +static volatile bool expect_crash; + +static void +user_ldt__gp_handler(int signo, siginfo_t *sig, void *ctx) +{ + ucontext_t *uctx = ctx; + extern uint8_t fs_read_begin; + + if (!expect_crash) { + atf_tc_fail("unexpected #GP"); + } + + ATF_REQUIRE(sig->si_signo == SIGSEGV); + ATF_REQUIRE(sig->si_code == SEGV_ACCERR); + + if (uctx->uc_mcontext.__gregs[_REG_EIP] != (intptr_t)&fs_read_begin) { + atf_tc_fail("got #GP on the wrong instruction"); + } + + set_fs(GSEL(GUDATA_SEL, SEL_UPL)); + + atf_tc_pass(); + /* NOTREACHED */ +} + +ATF_TC(user_ldt); +ATF_TC_HEAD(user_ldt, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Ensure that USER_LDT works as expected"); +} +ATF_TC_BODY(user_ldt, tc) +{ + union descriptor desc; + struct sigaction act; + + if (!user_ldt_supported) { + atf_tc_skip("USER_LDT disabled"); + } + + memset(&act, 0, sizeof(act)); + act.sa_sigaction = user_ldt__gp_handler; + act.sa_flags = SA_SIGINFO; + ATF_REQUIRE_EQ(sigaction(SIGSEGV, &act, NULL), 0); + + build_desc(&desc, ldt_base, PAGE_SIZE, SDT_MEMRW, SEL_UPL, 1, 0); + ATF_REQUIRE_EQ(i386_set_ldt(256, &desc, 1), 256); + + set_fs(LSEL(256, SEL_UPL)); + + ldt_base[666] = 123; + ldt_base[PAGE_SIZE-1] = 213; + __insn_barrier(); + ATF_REQUIRE_EQ(get_fs_byte((char *)666), 123); + ATF_REQUIRE_EQ(get_fs_byte((char *)PAGE_SIZE-1), 213); + + /* This one should fault, and it concludes our test case. */ + expect_crash = true; + get_fs_byte((char *)PAGE_SIZE); + + atf_tc_fail("test did not fault as expected"); +} + +/* -------------------------------------------------------------------------- */ + +ATF_TP_ADD_TCS(tp) +{ + user_ldt_detect(); + init_ldt_base(); + + ATF_TP_ADD_TC(tp, filter_ops); + ATF_TP_ADD_TC(tp, iretq_gp); + ATF_TP_ADD_TC(tp, iretq_np); + ATF_TP_ADD_TC(tp, user_ldt); + + return atf_no_error(); +} diff --git a/lib/libm/Makefile b/lib/libm/Makefile --- a/lib/libm/Makefile +++ b/lib/libm/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.42 2016/12/20 06:13:19 maya Exp $ +# $NetBSD: Makefile,v 1.47 2020/06/21 06:58:16 lukem Exp $ .include @@ -7,15 +7,18 @@ .if ${MACHINE} == "alpha" COPTS+= -mfloat-ieee -mieee-with-inexact -mfp-trap-mode=sui -mtrap-precision=i .endif +COPTS+= -fno-builtin CPPFLAGS.t_fenv.c+= -D__TEST_FENV CPPFLAGS.t_fe_round.c+= -D__TEST_FENV CPPFLAGS.t_ilogb.c+= -D__TEST_FENV +CPPFLAGS.t_scalbn.c+= -D__TEST_FENV CPPFLAGS.t_fmod.c+= -I${.CURDIR}/../libc/gen TESTS_C+= t_acos TESTS_C+= t_asin TESTS_C+= t_atan +TESTS_C+= t_bit TESTS_C+= t_casinh TESTS_C+= t_cbrt TESTS_C+= t_ceil @@ -41,6 +44,9 @@ TESTS_C+= t_sqrt TESTS_C+= t_tan TESTS_C+= t_tanh +TESTS_CXX+= t_cabsl + +SRCS.t_cabsl= t_cabsl.cxx LDADD+= -lm #COPTS+= -Wfloat-equal diff --git a/lib/libm/t_acos.c b/lib/libm/t_acos.c --- a/lib/libm/t_acos.c +++ b/lib/libm/t_acos.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_acos.c,v 1.10 2014/03/05 20:14:46 dsl Exp $ */ +/* $NetBSD: t_acos.c,v 1.11 2018/11/07 03:59:36 riastradh Exp $ */ /*- * Copyright (c) 2011 The NetBSD Foundation, Inc. @@ -72,7 +72,7 @@ { 0, M_PI / 2, }, { 0.1, 1.470628905633337, }, { 0.5, 1.047197551196598, }, - { 0.99, 0.141539473324427, }, + { 0.99, 0.1415394733244273, }, }; unsigned int i; diff --git a/lib/libm/t_asin.c b/lib/libm/t_asin.c --- a/lib/libm/t_asin.c +++ b/lib/libm/t_asin.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_asin.c,v 1.3 2014/03/03 10:39:08 martin Exp $ */ +/* $NetBSD: t_asin.c,v 1.4 2018/11/07 03:59:36 riastradh Exp $ */ /*- * Copyright (c) 2011 The NetBSD Foundation, Inc. @@ -30,6 +30,7 @@ */ #include +#include #include static const struct { @@ -117,13 +118,14 @@ ATF_TC_BODY(asin_inrange, tc) { - const double eps = 1.0e-15; - double y; + const double eps = DBL_EPSILON; size_t i; for (i = 0; i < __arraycount(values); i++) { - y = asin(values[i].x); - if (fabs(y - values[i].y) > eps) + double x = values[i].x; + double y = values[i].y; + + if (!(fabs((asin(x) - y)/y) <= eps)) atf_tc_fail_nonfatal("asin(%g) != %g", values[i].x, values[i].y); } @@ -230,16 +232,23 @@ ATF_TC_BODY(asinf_inrange, tc) { - const float eps = 1.0e-6; - float x; - float y; + const float eps = FLT_EPSILON; size_t i; for (i = 0; i < __arraycount(values); i++) { - x = values[i].x; - y = values[i].y; - if (fabs(asinf(x) - y) > eps) - atf_tc_fail_nonfatal("asinf(%g) != %g", x, y); + float x = values[i].x; + float y = values[i].y; + + if (fabs(x) == 0.5) + atf_tc_expect_fail("asinf is busted," + " gives ~2ulp error"); + if (!(fabsf((asinf(x) - y)/y) <= eps)) { + atf_tc_fail_nonfatal("asinf(%.8g) = %.8g != %.8g," + " error=~%.1fulp", + x, asinf(x), y, fabsf(((asinf(x) - y)/y)/eps)); + } + if (fabs(x) == 0.5) + atf_tc_expect_pass(); } } diff --git a/lib/libm/t_bit.c b/lib/libm/t_bit.c new file mode 100644 --- /dev/null +++ b/lib/libm/t_bit.c @@ -0,0 +1,102 @@ +/* $NetBSD: t_bit.c,v 1.1 2019/04/26 08:52:16 maya Exp $ */ + +/* + * Written by Maya Rashish + * Public domain. + * + * Testing signbit{,f,l} function correctly + */ + +#include +#include +#include +#include +#include +#include + +static const struct { + double input; + bool is_negative; +} values[] = { + { -1, true}, + { -123, true}, + { -123E6, true}, +#ifdef INFINITY + { -INFINITY, true}, + { INFINITY, false}, +#endif + { 123E6, false}, + { 0, false}, + { -FLT_MIN, true}, + { FLT_MIN, false}, + /* + * Cannot be accurately represented as float, + * but sign should be preserved + */ + { DBL_MAX, false}, + { -DBL_MAX, true}, +}; + +#ifdef __HAVE_LONG_DOUBLE +static const struct { + long double input; + bool is_negative; +} ldbl_values[] = { + { -LDBL_MIN, true}, + { LDBL_MIN, false}, + { LDBL_MAX, false}, + { -LDBL_MAX, true}, +}; +#endif + +ATF_TC(signbit); +ATF_TC_HEAD(signbit, tc) +{ + atf_tc_set_md_var(tc, "descr","Check that signbit functions correctly"); +} + +ATF_TC_BODY(signbit, tc) +{ + double iterator_d; + float iterator_f; + + for (unsigned int i = 0; i < __arraycount(values); i++) { + iterator_d = values[i].input; + iterator_f = (float) values[i].input; + if (signbit(iterator_f) != values[i].is_negative) + atf_tc_fail("%s:%d iteration %d signbitf is wrong" + " about the sign of %f", __func__, + __LINE__, i, iterator_f); + if (signbit(iterator_d) != values[i].is_negative) + atf_tc_fail("%s:%d iteration %d signbit is wrong" + "about the sign of %f", __func__, + __LINE__,i, iterator_d); + +#ifdef __HAVE_LONG_DOUBLE + long double iterator_l = values[i].input; + if (signbit(iterator_l) != values[i].is_negative) + atf_tc_fail("%s:%d iteration %d signbitl is wrong" + " about the sign of %Lf", __func__, + __LINE__, i, iterator_l); +#endif + } + +#ifdef __HAVE_LONG_DOUBLE + for (unsigned int i = 0; i < __arraycount(ldbl_values); i++) { + if (signbit(ldbl_values[i].input) != ldbl_values[i].is_negative) + atf_tc_fail("%s:%d iteration %d signbitl is" + "wrong about the sign of %Lf", + __func__, __LINE__, i, + ldbl_values[i].input); + } +#endif + +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, signbit); + + return atf_no_error(); +} diff --git a/lib/libm/t_cabsl.cxx b/lib/libm/t_cabsl.cxx new file mode 100644 --- /dev/null +++ b/lib/libm/t_cabsl.cxx @@ -0,0 +1,66 @@ +/*- + * Copyright (c) 2018 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Maya Rashish + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +/* + * Test that C++ "cabsl" is usable. PR lib/50646 + */ + +#include +#include + +ATF_TEST_CASE(cabsl); +ATF_TEST_CASE_HEAD(cabsl) +{ + set_md_var("descr", "Check that cabsl is usable from C++"); +} +ATF_TEST_CASE_BODY(cabsl) +{ + int sum = 0; + +#ifdef __HAVE_LONG_DOUBLE + std::complex cld(3.0,4.0); + sum += std::abs(cld); +#endif + std::complex cd(3.0,4.0); + sum += std::abs(cd); + + std::complex cf(3.0,4.0); + sum += std::abs(cf); + +#ifdef __HAVE_LONG_DOUBLE + ATF_REQUIRE_EQ(sum, 3*5); +#else + ATF_REQUIRE_EQ(sum, 2*5); +#endif +} + +ATF_INIT_TEST_CASES(tcs) +{ + ATF_ADD_TEST_CASE(tcs, cabsl); +} diff --git a/lib/libm/t_cbrt.c b/lib/libm/t_cbrt.c --- a/lib/libm/t_cbrt.c +++ b/lib/libm/t_cbrt.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_cbrt.c,v 1.3 2014/03/03 10:39:08 martin Exp $ */ +/* $NetBSD: t_cbrt.c,v 1.5 2018/11/15 05:14:20 riastradh Exp $ */ /*- * Copyright (c) 2011 The NetBSD Foundation, Inc. @@ -29,9 +29,10 @@ * POSSIBILITY OF SUCH DAMAGE. */ #include -__RCSID("$NetBSD: t_cbrt.c,v 1.3 2014/03/03 10:39:08 martin Exp $"); +__RCSID("$NetBSD: t_cbrt.c,v 1.5 2018/11/15 05:14:20 riastradh Exp $"); #include +#include #include #include @@ -61,18 +62,26 @@ ATF_TC_BODY(cbrt_pow, tc) { const double x[] = { 0.0, 0.005, 1.0, 99.0, 123.123, 9999.0 }; - const double eps = 1.0e-14; - double y, z; + /* Neither cbrt nor pow is required to be correctly rounded. */ + const double eps = 2*DBL_EPSILON; size_t i; for (i = 0; i < __arraycount(x); i++) { - - y = cbrt(x[i]); - z = pow(x[i], 1.0 / 3.0); - - if (fabs(y - z) > eps) - atf_tc_fail_nonfatal("cbrt(%0.03f) != " - "pow(%0.03f, 1/3)\n", x[i], x[i]); + double x_cbrt = cbrt(x[i]); + double x_pow13 = pow(x[i], 1.0 / 3.0); + bool ok; + + if (x[i] == 0) { + ok = (x_cbrt == x_pow13); + } else { + ok = (fabs((x_cbrt - x_pow13)/x_cbrt) <= eps); + } + + if (!ok) { + atf_tc_fail_nonfatal("cbrt(%.17g) = %.17g != " + "pow(%.17g, 1/3) = %.17g\n", + x[i], x_cbrt, x[i], x_pow13); + } } } @@ -162,18 +171,27 @@ ATF_TC_BODY(cbrtf_powf, tc) { const float x[] = { 0.0, 0.005, 1.0, 99.0, 123.123, 9999.0 }; - const float eps = 1.0e-5; - float y, z; + /* Neither cbrt nor pow is required to be correctly rounded. */ + const float eps = 2*FLT_EPSILON; size_t i; for (i = 0; i < __arraycount(x); i++) { - - y = cbrtf(x[i]); - z = powf(x[i], 1.0 / 3.0); - - if (fabsf(y - z) > eps) - atf_tc_fail_nonfatal("cbrtf(%0.03f) != " - "powf(%0.03f, 1/3)\n", x[i], x[i]); + float x_cbrt = cbrtf(x[i]); + float x_pow13 = powf(x[i], 1.0 / 3.0); + bool ok; + + if (x[i] == 0) { + ok = (x_cbrt == x_pow13); + } else { + ok = (fabsf((x_cbrt - x_pow13)/x_cbrt) <= eps); + } + + if (!ok) { + atf_tc_fail_nonfatal("cbrtf(%.9g) = %.9g. != " + "powf(%.9g, 1/3) = %.9g\n", + (double)x[i], (double)x_cbrt, + (double)x[i], (double)x_pow13); + } } } @@ -263,18 +281,29 @@ ATF_TC_BODY(cbrtl_powl, tc) { const long double x[] = { 0.0, 0.005, 1.0, 99.0, 123.123, 9999.0 }; - const long double eps = 1.0e-15; - long double y, z; + /* Neither cbrt nor pow is required to be correctly rounded. */ + const long double eps = 2*LDBL_EPSILON; size_t i; +#if LDBL_MANT_DIG > DBL_MANT_DIG + atf_tc_expect_fail("powl not yet implemented with full precision"); +#endif for (i = 0; i < __arraycount(x); i++) { - - y = cbrtl(x[i]); - z = powl(x[i], 1.0 / 3.0); - - if (fabsl(y - z) > eps * fabsl(1 + x[i])) - atf_tc_fail_nonfatal("cbrtl(%0.03Lf) != " - "powl(%0.03Lf, 1/3)\n", x[i], x[i]); + long double x_cbrt = cbrtl(x[i]); + long double x_pow13 = powl(x[i], 1.0 / 3.0); + bool ok; + + if (x[i] == 0) { + ok = (x_cbrt == x_pow13); + } else { + ok = (fabsl((x_cbrt - x_pow13)/x_cbrt) <= eps); + } + + if (!ok) { + atf_tc_fail_nonfatal("cbrtl(%.35Lg) = %.35Lg != " + "powl(%.35Lg, 1/3) = %.35Lg\n", + x[i], x_cbrt, x[i], x_pow13); + } } } diff --git a/lib/libm/t_cos.c b/lib/libm/t_cos.c --- a/lib/libm/t_cos.c +++ b/lib/libm/t_cos.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_cos.c,v 1.4 2014/03/03 10:39:08 martin Exp $ */ +/* $NetBSD: t_cos.c,v 1.9 2019/05/27 00:10:36 maya Exp $ */ /*- * Copyright (c) 2011 The NetBSD Foundation, Inc. @@ -29,31 +29,138 @@ * POSSIBILITY OF SUCH DAMAGE. */ +#include #include +#include #include static const struct { int angle; double x; double y; + float fy; } angles[] = { - { -180, -3.141592653589793, -1.0000000000000000 }, - { -135, -2.356194490192345, -0.7071067811865476 }, - { -90, -1.570796326794897, 0.0000000000000000 }, - { -45, -0.785398163397448, 0.7071067811865476 }, - { 0, 0.000000000000000, 1.0000000000000000 }, - { 30, 0.523598775598299, 0.8660254037844386 }, - { 45, 0.785398163397448, 0.7071067811865476 }, - { 60, 1.047197551196598, 0.5000000000000000 }, - { 90, 1.570796326794897, 0.0000000000000000 }, - { 120, 2.094395102393195, -0.5000000000000000 }, - { 135, 2.356194490192345, -0.7071067811865476 }, - { 150, 2.617993877991494, -0.8660254037844386 }, - { 180, 3.141592653589793, -1.0000000000000000 }, - { 270, 4.712388980384690, 0.0000000000000000 }, - { 360, 6.283185307179586, 1.0000000000000000 } + { -180, -3.141592653589793, -1.0000000000000000, 999 }, + { -135, -2.356194490192345, -0.7071067811865476, 999 }, + { -90, -1.5707963267948966, 6.123233995736766e-17, -4.3711388e-08 }, + { -90, -1.5707963267948968, -1.6081226496766366e-16, -4.3711388e-08 }, + { -45, -0.785398163397448, 0.7071067811865478, 999 }, + { 0, 0.000000000000000, 1.0000000000000000, 999 }, + { 30, 0.523598775598299, 0.8660254037844386, 999 }, + { 45, 0.785398163397448, 0.7071067811865478, 999 }, + { 60, 1.0471975511965976, 0.5000000000000001, 999 }, + { 60, 1.0471975511965979, 0.4999999999999999, 999 }, + { 90, 1.570796326794897, -3.8285686989269494e-16, -4.3711388e-08 }, + { 120, 2.0943951023931953, -0.4999999999999998, 999 }, + { 120, 2.0943951023931957, -0.5000000000000002, 999 }, + { 135, 2.356194490192345, -0.7071067811865476, 999 }, + { 150, 2.617993877991494, -0.8660254037844386, 999 }, + { 180, 3.141592653589793, -1.0000000000000000, 999 }, + { 270, 4.712388980384690, -1.8369701987210297e-16, 1.1924881e-08 }, + { 360, 6.283185307179586, 1.0000000000000000, 999 }, }; +#ifdef __HAVE_LONG_DOUBLE +/* + * cosl(3) + */ +ATF_TC(cosl_angles); +ATF_TC_HEAD(cosl_angles, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test some selected angles"); +} + +ATF_TC_BODY(cosl_angles, tc) +{ + /* + * XXX The given data is for double, so take that + * into account and expect less precise results.. + */ + const long double eps = DBL_EPSILON; + size_t i; + + for (i = 0; i < __arraycount(angles); i++) { + int deg = angles[i].angle; + long double theta = angles[i].x; + long double cos_theta = angles[i].y; + + assert(cos_theta != 0); + if (!(fabsl((cosl(theta) - cos_theta)/cos_theta) <= eps)) { + atf_tc_fail_nonfatal("cos(%d deg = %.17Lg) = %.17Lg" + " != %.17Lg", + deg, theta, cosl(theta), cos_theta); + } + } +} + +ATF_TC(cosl_nan); +ATF_TC_HEAD(cosl_nan, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test cosl(NaN) == NaN"); +} + +ATF_TC_BODY(cosl_nan, tc) +{ + const long double x = 0.0L / 0.0L; + + ATF_CHECK(isnan(x) != 0); + ATF_CHECK(isnan(cosl(x)) != 0); +} + +ATF_TC(cosl_inf_neg); +ATF_TC_HEAD(cosl_inf_neg, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test cosl(-Inf) == NaN"); +} + +ATF_TC_BODY(cosl_inf_neg, tc) +{ + const long double x = -1.0L / 0.0L; + + ATF_CHECK(isnan(cosl(x)) != 0); +} + +ATF_TC(cosl_inf_pos); +ATF_TC_HEAD(cosl_inf_pos, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test cosl(+Inf) == NaN"); +} + +ATF_TC_BODY(cosl_inf_pos, tc) +{ + const long double x = 1.0L / 0.0L; + + ATF_CHECK(isnan(cosl(x)) != 0); +} + + +ATF_TC(cosl_zero_neg); +ATF_TC_HEAD(cosl_zero_neg, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test cosl(-0.0) == 1.0"); +} + +ATF_TC_BODY(cosl_zero_neg, tc) +{ + const long double x = -0.0L; + + ATF_CHECK(cosl(x) == 1.0); +} + +ATF_TC(cosl_zero_pos); +ATF_TC_HEAD(cosl_zero_pos, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test cosl(+0.0) == 1.0"); +} + +ATF_TC_BODY(cosl_zero_pos, tc) +{ + const long double x = 0.0L; + + ATF_CHECK(cosl(x) == 1.0); +} +#endif + /* * cos(3) */ @@ -65,14 +172,20 @@ ATF_TC_BODY(cos_angles, tc) { - const double eps = 1.0e-15; + const double eps = DBL_EPSILON; size_t i; for (i = 0; i < __arraycount(angles); i++) { - - if (fabs(cos(angles[i].x) - angles[i].y) > eps) - atf_tc_fail_nonfatal("cos(%d deg) != %0.01f", - angles[i].angle, angles[i].y); + int deg = angles[i].angle; + double theta = angles[i].x; + double cos_theta = angles[i].y; + + assert(cos_theta != 0); + if (!(fabs((cos(theta) - cos_theta)/cos_theta) <= eps)) { + atf_tc_fail_nonfatal("cos(%d deg = %.17g) = %.17g" + " != %.17g", + deg, theta, cos(theta), cos_theta); + } } } @@ -154,18 +267,33 @@ ATF_TC_BODY(cosf_angles, tc) { - const float eps = 1.0e-7; - float x, y; + const float eps = FLT_EPSILON; size_t i; for (i = 0; i < __arraycount(angles); i++) { - - x = angles[i].x; - y = angles[i].y; - - if (fabsf(cosf(x) - y) > eps) - atf_tc_fail_nonfatal("cosf(%d deg) != %0.01f", - angles[i].angle, angles[i].y); + int deg = angles[i].angle; + float theta = angles[i].x; + float cos_theta = angles[i].fy; + + /* + * Force rounding to float even if FLT_EVAL_METHOD=2, + * as is the case on i386. + * + * The volatile should not be necessary, by C99 Sec. + * 5.2.4.2.2. para. 8 on p. 24 which specifies that + * assignment and cast remove all extra range and precision, + * but seems to be needed to work around a compiler bug. + */ + volatile float result = cosf(theta); + + if (cos_theta == 999) + cos_theta = angles[i].y; + + assert(cos_theta != 0); + if (!(fabsf((result - cos_theta)/cos_theta) <= eps)) { + atf_tc_fail_nonfatal("cosf(%d deg = %.8g) = %.8g" + " != %.8g", deg, theta, result, cos_theta); + } } } @@ -244,6 +372,14 @@ ATF_TP_ADD_TCS(tp) { +#ifdef __HAVE_LONG_DOUBLE + ATF_TP_ADD_TC(tp, cosl_angles); + ATF_TP_ADD_TC(tp, cosl_nan); + ATF_TP_ADD_TC(tp, cosl_inf_neg); + ATF_TP_ADD_TC(tp, cosl_inf_pos); + ATF_TP_ADD_TC(tp, cosl_zero_neg); + ATF_TP_ADD_TC(tp, cosl_zero_pos); +#endif ATF_TP_ADD_TC(tp, cos_angles); ATF_TP_ADD_TC(tp, cos_nan); diff --git a/lib/libm/t_cosh.c b/lib/libm/t_cosh.c --- a/lib/libm/t_cosh.c +++ b/lib/libm/t_cosh.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_cosh.c,v 1.6 2014/03/03 10:39:08 martin Exp $ */ +/* $NetBSD: t_cosh.c,v 1.7 2018/11/07 03:59:36 riastradh Exp $ */ /*- * Copyright (c) 2011 The NetBSD Foundation, Inc. @@ -29,28 +29,28 @@ * POSSIBILITY OF SUCH DAMAGE. */ #include -__RCSID("$NetBSD: t_cosh.c,v 1.6 2014/03/03 10:39:08 martin Exp $"); +__RCSID("$NetBSD: t_cosh.c,v 1.7 2018/11/07 03:59:36 riastradh Exp $"); #include +#include #include #include static const struct { double x; double y; - double e; } values[] = { - { -10, 11013.23292010332, 1e4, }, - { -2, 3.762195691083631, 1, }, - { -1, 1.543080634815244, 1, }, - { -0.05, 1.001250260438369, 1, }, - { -0.001, 1.000000500000042, 1, }, - { 0, 1, 1, }, - { 0.001, 1.000000500000042, 1, }, - { 0.05, 1.001250260438369, 1, }, - { 1, 1.543080634815244, 1, }, - { 2, 3.762195691083631, 1, }, - { 10, 11013.23292010332, 1e4, }, + { -10, 11013.232920103323, }, + { -2, 3.762195691083631, }, + { -1, 1.543080634815244, }, + { -0.05, 1.001250260438369, }, + { -0.001, 1.0000005000000418, }, + { 0, 1, }, + { 0.001, 1.0000005000000418, }, + { 0.05, 1.001250260438369, }, + { 1, 1.543080634815244, }, + { 2, 3.762195691083631, }, + { 10, 11013.232920103323, }, }; /* @@ -64,18 +64,17 @@ ATF_TC_BODY(cosh_inrange, tc) { - double eps; - double x; - double y; + const double eps = DBL_EPSILON; size_t i; for (i = 0; i < __arraycount(values); i++) { - x = values[i].x; - y = values[i].y; - eps = 1e-15 * values[i].e; + double x = values[i].x; + double cosh_x = values[i].y; - if (fabs(cosh(x) - y) > eps) - atf_tc_fail_nonfatal("cosh(%g) != %g\n", x, y); + if (!(fabs((cosh(x) - cosh_x)/cosh_x) <= eps)) { + atf_tc_fail_nonfatal("cosh(%.17g) = %.17g != %.17g\n", + x, cosh(x), cosh_x); + } } } @@ -162,18 +161,17 @@ ATF_TC_BODY(coshf_inrange, tc) { - float eps; - float x; - float y; + const float eps = FLT_EPSILON; size_t i; for (i = 0; i < __arraycount(values); i++) { - x = values[i].x; - y = values[i].y; - eps = 1e-6 * values[i].e; + float x = values[i].x; + float cosh_x = values[i].y; - if (fabsf(coshf(x) - y) > eps) - atf_tc_fail_nonfatal("coshf(%g) != %g\n", x, y); + if (!(fabsf((coshf(x) - cosh_x)/cosh_x) <= eps)) { + atf_tc_fail_nonfatal("coshf(%.17g) = %.17g != %.17g\n", + x, coshf(x), cosh_x); + } } } diff --git a/lib/libm/t_exp.c b/lib/libm/t_exp.c --- a/lib/libm/t_exp.c +++ b/lib/libm/t_exp.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_exp.c,v 1.8 2014/10/07 16:53:44 gson Exp $ */ +/* $NetBSD: t_exp.c,v 1.9 2018/11/07 03:59:36 riastradh Exp $ */ /*- * Copyright (c) 2011 The NetBSD Foundation, Inc. @@ -30,6 +30,7 @@ */ #include +#include #include #include "t_libm.h" @@ -37,17 +38,16 @@ static const struct { double x; double y; - double e; } exp_values[] = { - { -10, 0.4539992976248485e-4, 1e-4, }, - { -5, 0.6737946999085467e-2, 1e-2, }, - { -1, 0.3678794411714423, 1e-1, }, - { -0.1, 0.9048374180359595, 1e-1, }, - { 0, 1.0000000000000000, 1, }, - { 0.1, 1.1051709180756477, 1, }, - { 1, 2.7182818284590452, 1, }, - { 5, 148.41315910257660, 1e2, }, - { 10, 22026.465794806718, 1e4, }, + { -10, 0.4539992976248485e-4, }, + { -5, 0.6737946999085467e-2, }, + { -1, 0.3678794411714423, }, + { -0.1, 0.9048374180359595, }, + { 0, 1.0000000000000000, }, + { 0.1, 1.1051709180756477, }, + { 1, 2.7182818284590452, }, + { 5, 148.41315910257660, }, + { 10, 22026.465794806718, }, }; /* @@ -234,18 +234,17 @@ ATF_TC_BODY(exp_product, tc) { - double eps; - double x; - double y; + const double eps = DBL_EPSILON; size_t i; for (i = 0; i < __arraycount(exp_values); i++) { - x = exp_values[i].x; - y = exp_values[i].y; - eps = 1e-15 * exp_values[i].e; + double x = exp_values[i].x; + double e_x = exp_values[i].y; - if (fabs(exp(x) - y) > eps) - atf_tc_fail_nonfatal("exp(%0.01f) != %18.18e", x, y); + if (!(fabs((exp(x) - e_x)/e_x) <= eps)) { + atf_tc_fail_nonfatal("exp(%.17g) = %.17g != %.17g", + x, exp(x), e_x); + } } } @@ -332,18 +331,17 @@ ATF_TC_BODY(expf_product, tc) { - float eps; - float x; - float y; + const float eps = FLT_EPSILON; size_t i; for (i = 0; i < __arraycount(exp_values); i++) { - x = exp_values[i].x; - y = exp_values[i].y; - eps = 1e-6 * exp_values[i].e; + float x = exp_values[i].x; + float e_x = exp_values[i].y; - if (fabsf(expf(x) - y) > eps) - atf_tc_fail_nonfatal("expf(%0.01f) != %18.18e", x, y); + if (!(fabsf((expf(x) - e_x)/e_x) <= eps)) { + atf_tc_fail_nonfatal("expf(%.8g) = %.8g != %.8g", + x, exp(x), e_x); + } } } diff --git a/lib/libm/t_fe_round.c b/lib/libm/t_fe_round.c --- a/lib/libm/t_fe_round.c +++ b/lib/libm/t_fe_round.c @@ -89,7 +89,97 @@ (fegetround() == values[i].round_mode), "Didn't get the same rounding mode out!\n" "(index %d) fed in %d rounding mode, got %d out\n", - i, fegetround(), values[i].round_mode); + i, values[i].round_mode, fegetround()); + } +} + +ATF_TC(fe_nearbyint); +ATF_TC_HEAD(fe_nearbyint, tc) +{ + atf_tc_set_md_var(tc, "descr","Checking IEEE 754 rounding modes using nearbyint"); +} + +ATF_TC_BODY(fe_nearbyint, tc) +{ + double received; + + for (unsigned int i = 0; i < __arraycount(values); i++) { + fesetround(values[i].round_mode); + + received = nearbyint(values[i].input); + ATF_CHECK_MSG( + (fabs(received - values[i].expected) < EPSILON), + "nearbyint rounding wrong, difference too large\n" + "input: %f (index %d): got %f, expected %ld\n", + values[i].input, i, received, values[i].expected); + + /* Do we get the same rounding mode out? */ + ATF_CHECK_MSG( + (fegetround() == values[i].round_mode), + "Didn't get the same rounding mode out!\n" + "(index %d) fed in %d rounding mode, got %d out\n", + i, values[i].round_mode, fegetround()); + } +} + +static const struct { + double input; + double toward; + double expected; +} values2[] = { + { 10.0, 11.0, 10.0 }, + { -5.0, -6.0, -5.0 }, +}; + +ATF_TC(fe_nextafter); +ATF_TC_HEAD(fe_nextafter, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checking IEEE 754 rounding using nextafter()"); +} + +ATF_TC_BODY(fe_nextafter, tc) +{ + double received; + int res; + + for (unsigned int i = 0; i < __arraycount(values2); i++) { + received = nextafter(values2[i].input, values2[i].toward); + if (values2[i].input < values2[i].toward) { + res = (received > values2[i].input); + } else { + res = (received < values2[i].input); + } + ATF_CHECK_MSG( + res && (fabs(received - values2[i].expected) < EPSILON), + "nextafter() rounding wrong, difference too large\n" + "input: %f (index %d): got %f, expected %f, res %d\n", + values2[i].input, i, received, values2[i].expected, res); + } +} + +ATF_TC(fe_nexttoward); +ATF_TC_HEAD(fe_nexttoward, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checking IEEE 754 rounding using nexttoward()"); +} + +ATF_TC_BODY(fe_nexttoward, tc) +{ + double received; + int res; + + for (unsigned int i = 0; i < __arraycount(values2); i++) { + received = nexttoward(values2[i].input, values2[i].toward); + if (values2[i].input < values2[i].toward) { + res = (received > values2[i].input); + } else { + res = (received < values2[i].input); + } + ATF_CHECK_MSG( + res && (fabs(received - values2[i].expected) < EPSILON), + "nexttoward() rounding wrong, difference too large\n" + "input: %f (index %d): got %f, expected %f, res %d\n", + values2[i].input, i, received, values2[i].expected, res); } } @@ -97,6 +187,9 @@ { ATF_TP_ADD_TC(tp, fe_round); + ATF_TP_ADD_TC(tp, fe_nearbyint); + ATF_TP_ADD_TC(tp, fe_nextafter); + ATF_TP_ADD_TC(tp, fe_nexttoward); return atf_no_error(); } @@ -109,15 +202,56 @@ "dummy test case - no fenv.h support"); } - ATF_TC_BODY(t_nofe_round, tc) { atf_tc_skip("no fenv.h support on this architecture"); } +ATF_TC(t_nofe_nearbyint); + +ATF_TC_HEAD(t_nofe_nearbyint, tc) +{ + atf_tc_set_md_var(tc, "descr", + "dummy test case - no fenv.h support"); +} + +ATF_TC_BODY(t_nofe_nearbyint, tc) +{ + atf_tc_skip("no fenv.h support on this architecture"); +} + +ATF_TC(t_nofe_nextafter); + +ATF_TC_HEAD(t_nofe_nextafter, tc) +{ + atf_tc_set_md_var(tc, "descr", + "dummy test case - no fenv.h support"); +} + +ATF_TC_BODY(t_nofe_nextafter, tc) +{ + atf_tc_skip("no fenv.h support on this architecture"); +} + +ATF_TC(t_nofe_nexttoward); + +ATF_TC_HEAD(t_nofe_nexttoward, tc) +{ + atf_tc_set_md_var(tc, "descr", + "dummy test case - no fenv.h support"); +} + +ATF_TC_BODY(t_nofe_nexttoward, tc) +{ + atf_tc_skip("no fenv.h support on this architecture"); +} + ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, t_nofe_round); + ATF_TP_ADD_TC(tp, t_nofe_nearbyint); + ATF_TP_ADD_TC(tp, t_nofe_nextafter); + ATF_TP_ADD_TC(tp, t_nofe_nexttoward); return atf_no_error(); } diff --git a/lib/libm/t_fenv.c b/lib/libm/t_fenv.c --- a/lib/libm/t_fenv.c +++ b/lib/libm/t_fenv.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_fenv.c,v 1.3 2015/12/22 14:20:59 christos Exp $ */ +/* $NetBSD: t_fenv.c,v 1.6 2019/04/25 20:48:54 kamil Exp $ */ /*- * Copyright (c) 2014 The NetBSD Foundation, Inc. @@ -29,7 +29,7 @@ * POSSIBILITY OF SUCH DAMAGE. */ #include -__RCSID("$NetBSD: t_fenv.c,v 1.3 2015/12/22 14:20:59 christos Exp $"); +__RCSID("$NetBSD: t_fenv.c,v 1.6 2019/04/25 20:48:54 kamil Exp $"); #include @@ -40,15 +40,15 @@ #include -#if __arm__ && !__SOFTFP__ +#if (__arm__ && !__SOFTFP__) || __aarch64__ /* - * Some NEON fpus do not implement IEEE exception handling, - * skip these tests if running on them and compiled for + * Some NEON fpus do not trap on IEEE 754 FP exceptions. + * Skip these tests if running on them and compiled for * hard float. */ #define FPU_EXC_PREREQ() \ if (0 == fpsetmask(fpsetmask(FP_X_INV))) \ - atf_tc_skip("FPU does not implement exception handling"); + atf_tc_skip("FPU does not implement traps on FP exceptions"); /* * Same as above: some don't allow configuring the rounding mode. diff --git a/lib/libm/t_fmod.c b/lib/libm/t_fmod.c --- a/lib/libm/t_fmod.c +++ b/lib/libm/t_fmod.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_fmod.c,v 1.3 2015/01/03 14:23:53 gson Exp $ */ +/* $NetBSD: t_fmod.c,v 1.4 2020/08/25 13:39:16 gson Exp $ */ /*- * Copyright (c) 2013 The NetBSD Foundation, Inc. @@ -43,7 +43,7 @@ ATF_TC_BODY(fmod, tc) { - if (isQEMU()) + if (isQEMU_TCG()) atf_tc_expect_fail("PR misc/44767"); ATF_CHECK(fmodf(2.0, 1.0) == 0); diff --git a/lib/libm/t_hypot.c b/lib/libm/t_hypot.c --- a/lib/libm/t_hypot.c +++ b/lib/libm/t_hypot.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_hypot.c,v 1.1 2016/01/24 20:26:47 gson Exp $ */ +/* $NetBSD: t_hypot.c,v 1.2 2020/06/25 11:12:03 jruoho Exp $ */ /*- * Copyright (c) 2016 The NetBSD Foundation, Inc. @@ -59,7 +59,7 @@ ATF_TC(pr50698); ATF_TC_HEAD(pr50698, tc) { - atf_tc_set_md_var(tc, "descr", "Check for the bug of PR 50698"); + atf_tc_set_md_var(tc, "descr", "Check for the bug of PR lib/50698"); } ATF_TC_BODY(pr50698, tc) diff --git a/lib/libm/t_ilogb.c b/lib/libm/t_ilogb.c --- a/lib/libm/t_ilogb.c +++ b/lib/libm/t_ilogb.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_ilogb.c,v 1.7 2017/01/13 19:23:40 christos Exp $ */ +/* $NetBSD: t_ilogb.c,v 1.9 2018/06/14 21:57:25 maya Exp $ */ /*- * Copyright (c) 2016 The NetBSD Foundation, Inc. @@ -29,7 +29,7 @@ * POSSIBILITY OF SUCH DAMAGE. */ #include -__RCSID("$NetBSD: t_ilogb.c,v 1.7 2017/01/13 19:23:40 christos Exp $"); +__RCSID("$NetBSD: t_ilogb.c,v 1.9 2018/06/14 21:57:25 maya Exp $"); #include #include @@ -44,7 +44,9 @@ #else # define ATF_CHECK_RAISED_INVALID do { \ int r = fetestexcept(FE_ALL_EXCEPT); \ - ATF_CHECK_MSG(r == FE_INVALID, "r=%#x != %#x\n", r, FE_INVALID); \ + ATF_CHECK_MSG((r & FE_INVALID) != 0, \ + "r & FE_INVALID == 0 (r=%#x, FE_INVALID=%#x)\n", \ + r, FE_INVALID); \ (void)feclearexcept(FE_ALL_EXCEPT); \ } while (/*CONSTCOND*/0) diff --git a/lib/libm/t_ldexp.c b/lib/libm/t_ldexp.c --- a/lib/libm/t_ldexp.c +++ b/lib/libm/t_ldexp.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_ldexp.c,v 1.16 2016/08/25 00:32:31 maya Exp $ */ +/* $NetBSD: t_ldexp.c,v 1.17 2018/11/07 03:59:36 riastradh Exp $ */ /*- * Copyright (c) 2011 The NetBSD Foundation, Inc. @@ -29,14 +29,15 @@ * POSSIBILITY OF SUCH DAMAGE. */ #include -__RCSID("$NetBSD: t_ldexp.c,v 1.16 2016/08/25 00:32:31 maya Exp $"); +__RCSID("$NetBSD: t_ldexp.c,v 1.17 2018/11/07 03:59:36 riastradh Exp $"); #include #include -#include +#include #include +#include #include #include @@ -195,22 +196,17 @@ ATF_TC_BODY(ldexp_exp2, tc) { const double n[] = { 1, 2, 3, 10, 50, 100 }; -#if __DBL_MIN_10_EXP__ <= -40 - const double eps = 1.0e-40; -#else - const double eps = __DBL_MIN__*4.0; -#endif + const double eps = DBL_EPSILON; const double x = 12.0; - double y; size_t i; for (i = 0; i < __arraycount(n); i++) { + double y = ldexp(x, n[i]); - y = ldexp(x, n[i]); - - if (fabs(y - (x * exp2(n[i]))) > eps) { - atf_tc_fail_nonfatal("ldexp(%0.01f, %0.01f) " - "!= %0.01f * exp2(%0.01f)", x, n[i], x, n[i]); + if (!(fabs((y - (x * exp2(n[i])))/y) <= eps)) { + atf_tc_fail_nonfatal("ldexp(%.17g, %.17g) = %.17g " + "!= %.17g * exp2(%.17g) = %.17g", + x, n[i], y, x, n[i], (x * exp2(n[i]))); } } } @@ -320,18 +316,17 @@ ATF_TC_BODY(ldexpf_exp2f, tc) { const float n[] = { 1, 2, 3, 10, 50, 100 }; - const float eps = 1.0e-9; + const float eps = FLT_EPSILON; const float x = 12.0; - float y; size_t i; for (i = 0; i < __arraycount(n); i++) { + float y = ldexpf(x, n[i]); - y = ldexpf(x, n[i]); - - if (fabsf(y - (x * exp2f(n[i]))) > eps) { - atf_tc_fail_nonfatal("ldexpf(%0.01f, %0.01f) " - "!= %0.01f * exp2f(%0.01f)", x, n[i], x, n[i]); + if (!(fabsf((y - (x * exp2f(n[i])))/y) <= eps)) { + atf_tc_fail_nonfatal("ldexpf(%.17g, %.17g) = %.17g " + "!= %.17g * exp2f(%.17g) = %.17g", + x, n[i], y, x, n[i], (x * exp2f(n[i]))); } } } diff --git a/lib/libm/t_libm.h b/lib/libm/t_libm.h --- a/lib/libm/t_libm.h +++ b/lib/libm/t_libm.h @@ -1,4 +1,4 @@ -/* $NetBSD: t_libm.h,v 1.6 2014/03/25 17:30:14 joerg Exp $ */ +/* $NetBSD: t_libm.h,v 1.7 2018/11/07 03:59:36 riastradh Exp $ */ /* * Check result of fn(arg) is correct within the bounds. @@ -11,8 +11,8 @@ long double epsilon = epsilon_; \ long double expect = expect_; \ long double r = fn(arg); \ - long double e = fabsl(r - expect); \ - if (r != expect && e > epsilon) \ + long double e = fabsl((r - expect)/expect); \ + if (!(r == expect || e <= epsilon)) \ atf_tc_fail_nonfatal( \ "subtest %u: " #fn "(%g) is %Lg (%.14La) " \ "not %Lg (%.13La), error %Lg (%.6La) > %Lg", \ diff --git a/lib/libm/t_log.c b/lib/libm/t_log.c --- a/lib/libm/t_log.c +++ b/lib/libm/t_log.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_log.c,v 1.13 2015/02/09 19:39:48 martin Exp $ */ +/* $NetBSD: t_log.c,v 1.14 2018/11/07 03:59:36 riastradh Exp $ */ /*- * Copyright (c) 2011 The NetBSD Foundation, Inc. @@ -29,10 +29,11 @@ * POSSIBILITY OF SUCH DAMAGE. */ #include -__RCSID("$NetBSD: t_log.c,v 1.13 2015/02/09 19:39:48 martin Exp $"); +__RCSID("$NetBSD: t_log.c,v 1.14 2018/11/07 03:59:36 riastradh Exp $"); #include +#include #include #include #include @@ -614,10 +615,10 @@ ATF_TC_BODY(log_base, tc) { - const double eps = 1.0e-38; + const double eps = DBL_EPSILON; - if (fabs(log(M_E) - 1.0) > eps) - atf_tc_fail_nonfatal("log(e) != 1"); + if (!(fabs(log(M_E) - 1.0) <= eps)) + atf_tc_fail_nonfatal("log(e) = %.17g != 1", log(M_E)); } ATF_TC(log_nan); @@ -714,10 +715,11 @@ ATF_TC_BODY(logf_base, tc) { - const float eps = 1.0e-7; + const float eps = FLT_EPSILON; - if (fabsf(logf(M_E) - 1.0f) > eps) - atf_tc_fail_nonfatal("logf(e) != 1"); + if (!(fabsf(logf(M_E) - 1.0f) <= eps)) + atf_tc_fail_nonfatal("logf(e) = %.17g != 1", + (double)logf(M_E)); } ATF_TC(logf_nan); diff --git a/lib/libm/t_round.c b/lib/libm/t_round.c --- a/lib/libm/t_round.c +++ b/lib/libm/t_round.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_round.c,v 1.4 2013/11/11 23:57:34 joerg Exp $ */ +/* $NetBSD: t_round.c,v 1.9 2017/09/03 13:41:19 wiz Exp $ */ /*- * Copyright (c) 2011 The NetBSD Foundation, Inc. @@ -26,9 +26,13 @@ * POSSIBILITY OF SUCH DAMAGE. */ +#include + #include #include #include +#include +#include /* * This tests for a bug in the initial implementation where @@ -76,10 +80,58 @@ ATF_CHECK(fabsl(cl) < SMALL_NUM); } +ATF_TC(rounding_alpha); +ATF_TC_HEAD(rounding_alpha, tc) +{ + atf_tc_set_md_var(tc, "descr","Checking MPFR's config failure with -mieee on Alpha"); +} + +typedef uint64_t gimpy_limb_t; +#define GIMPY_NUMB_BITS 64 + +ATF_TC_BODY(rounding_alpha, tc) +{ + double d; + gimpy_limb_t u; + int i; + + d = 1.0; + for (i = 0; i < GIMPY_NUMB_BITS - 1; i++) + d = d + d; + + printf("d = %g\n", d); + u = (gimpy_limb_t) d; + + for (; i > 0; i--) { + ATF_CHECK_MSG((u % 2 == 0), + "%"PRIu64" is not an even number! (iteration %d)", u , i); + u = u >> 1; + } +} + +ATF_TC(rounding_alpha_simple); +ATF_TC_HEAD(rounding_alpha_simple, tc) +{ + atf_tc_set_md_var(tc, "descr","Checking double to uint64_t edge case"); +} + + +double rounding_alpha_simple_even = 9223372036854775808.000000; /* 2^63 */ + +ATF_TC_BODY(rounding_alpha_simple, tc) +{ + uint64_t unsigned_even = rounding_alpha_simple_even; + + ATF_CHECK_MSG(unsigned_even % 2 == 0, + "2^63 cast to uint64_t is odd (got %"PRIu64")", unsigned_even); + +} ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, round_dir); + ATF_TP_ADD_TC(tp, rounding_alpha); + ATF_TP_ADD_TC(tp, rounding_alpha_simple); return atf_no_error(); } diff --git a/lib/libm/t_scalbn.c b/lib/libm/t_scalbn.c --- a/lib/libm/t_scalbn.c +++ b/lib/libm/t_scalbn.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_scalbn.c,v 1.14 2017/01/13 21:09:12 agc Exp $ */ +/* $NetBSD: t_scalbn.c,v 1.16 2018/11/07 03:59:36 riastradh Exp $ */ /*- * Copyright (c) 2011 The NetBSD Foundation, Inc. @@ -29,12 +29,13 @@ * POSSIBILITY OF SUCH DAMAGE. */ #include -__RCSID("$NetBSD: t_scalbn.c,v 1.14 2017/01/13 21:09:12 agc Exp $"); +__RCSID("$NetBSD: t_scalbn.c,v 1.16 2018/11/07 03:59:36 riastradh Exp $"); #include #include #include #include +#include #include @@ -46,16 +47,17 @@ double inval; double result; int error; + int except; }; struct testcase test_vals[] = { - { 0, 1.00085, 1.00085, 0 }, - { 0, 0.99755, 0.99755, 0 }, - { 0, -1.00085, -1.00085, 0 }, - { 0, -0.99755, -0.99755, 0 }, - { 1, 1.00085, 2.0* 1.00085, 0 }, - { 1, 0.99755, 2.0* 0.99755, 0 }, - { 1, -1.00085, 2.0* -1.00085, 0 }, - { 1, -0.99755, 2.0* -0.99755, 0 }, + { 0, 1.00085, 1.00085, 0, 0 }, + { 0, 0.99755, 0.99755, 0, 0 }, + { 0, -1.00085, -1.00085, 0, 0 }, + { 0, -0.99755, -0.99755, 0, 0 }, + { 1, 1.00085, 2.0* 1.00085, 0, 0 }, + { 1, 0.99755, 2.0* 0.99755, 0, 0 }, + { 1, -1.00085, 2.0* -1.00085, 0, 0 }, + { 1, -0.99755, 2.0* -0.99755, 0, 0 }, /* * We could add more corner test cases here, but we would have to @@ -82,13 +84,25 @@ for (i = 0; i < tcnt; i++) { errno = 0; +#ifndef __vax__ + feclearexcept(FE_ALL_EXCEPT); +#endif rv = scalbn(tests[i].inval, tests[i].exp); ATF_CHECK_EQ_MSG(errno, tests[i].error, "test %zu: errno %d instead of %d", i, errno, tests[i].error); - ATF_CHECK_MSG(fabs(rv-tests[i].result)<2.0*DBL_EPSILON, - "test %zu: return value %g instead of %g (difference %g)", - i, rv, tests[i].result, tests[i].result-rv); +#ifndef __vax__ + ATF_CHECK_EQ_MSG(errno, tests[i].error, + "test %zu: fetestexcept %d instead of %d", i, + fetestexcept(FE_INVALID | FE_DIVBYZERO | FE_OVERFLOW | FE_UNDERFLOW), + tests[i].except); +#endif + /* scalbn is always exact except for underflow or overflow. */ + ATF_CHECK_MSG(rv == tests[i].result, + "test %zu: return value %.17g instead of %.17g" + " (error %.17g)", + i, rv, tests[i].result, + fabs((tests[i].result - rv)/tests[i].result)); } } @@ -228,9 +242,12 @@ ATF_CHECK_EQ_MSG(errno, tests[i].error, "test %zu: errno %d instead of %d", i, errno, tests[i].error); - ATF_CHECK_MSG(fabs(rv-tests[i].result)<2.0*FLT_EPSILON, - "test %zu: return value %g instead of %g (difference %g)", - i, rv, tests[i].result, tests[i].result-rv); + /* scalbn is always exact except for underflow or overflow. */ + ATF_CHECK_MSG(rv == (float)tests[i].result, + "test %zu: return value %.8g instead of %.8g" + " (error %.8g)", + i, rv, tests[i].result, + fabsf((tests[i].result - rv)/tests[i].result)); } } @@ -373,9 +390,12 @@ ATF_CHECK_EQ_MSG(errno, tests[i].error, "test %zu: errno %d instead of %d", i, errno, tests[i].error); - ATF_CHECK_MSG(fabsl(rv-(long double)tests[i].result)<2.0*LDBL_EPSILON, - "test %zu: return value %Lg instead of %Lg (difference %Lg)", - i, rv, (long double)tests[i].result, (long double)tests[i].result-rv); + /* scalbn is always exact except for underflow or overflow. */ + ATF_CHECK_MSG(rv == (long double)tests[i].result, + "test %zu: return value %.35Lg instead of %.35Lg" + " (error %.35Lg)", + i, rv, (long double)tests[i].result, + fabsl(((long double)tests[i].result - rv)/tests[i].result)); } #endif } diff --git a/lib/libm/t_sin.c b/lib/libm/t_sin.c --- a/lib/libm/t_sin.c +++ b/lib/libm/t_sin.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_sin.c,v 1.4 2014/03/03 10:39:08 martin Exp $ */ +/* $NetBSD: t_sin.c,v 1.7 2019/05/27 00:24:37 maya Exp $ */ /*- * Copyright (c) 2011 The NetBSD Foundation, Inc. @@ -29,29 +29,33 @@ * POSSIBILITY OF SUCH DAMAGE. */ +#include #include +#include #include static const struct { int angle; double x; double y; + float fy; } angles[] = { - { -180, -3.141592653589793, 0.0000000000000000 }, - { -135, -2.356194490192345, -0.7071067811865476 }, - { -90, -1.570796326794897, -1.0000000000000000 }, - { -45, -0.785398163397448, -0.7071067811865476 }, - { 0, 0.000000000000000, 0.0000000000000000 }, - { 30, 0.523598775598299, 0.5000000000000000 }, - { 45, 0.785398163397448, 0.7071067811865476 }, - { 60, 1.047197551196598, 0.8660254037844386 }, - { 90, 1.570796326794897, 1.0000000000000000 }, - { 120, 2.094395102393195, 0.8660254037844386 }, - { 135, 2.356194490192345, 0.7071067811865476 }, - { 150, 2.617993877991494, 0.5000000000000000 }, - { 180, 3.141592653589793, 0.0000000000000000 }, - { 270, 4.712388980384690, -1.0000000000000000 }, - { 360, 6.283185307179586, 0.0000000000000000 } + { -360, -6.283185307179586, 2.4492935982947064e-16, -1.7484555e-07 }, + { -180, -3.141592653589793, -1.2246467991473532e-16, 8.7422777e-08 }, + { -135, -2.356194490192345, -0.7071067811865476, 999 }, + { -90, -1.570796326794897, -1.0000000000000000, 999 }, + { -45, -0.785398163397448, -0.7071067811865472, 999 }, + { 0, 0.000000000000000, 0.0000000000000000, 999 }, + { 30, 0.5235987755982989, 0.5000000000000000, 999 }, + { 45, 0.785398163397448, 0.7071067811865472, 999 }, + { 60, 1.047197551196598, 0.8660254037844388, 999 }, + { 90, 1.570796326794897, 1.0000000000000000, 999 }, + { 120, 2.094395102393195, 0.8660254037844389, 999 }, + { 135, 2.356194490192345, 0.7071067811865476, 999 }, + { 150, 2.617993877991494, 0.5000000000000003, 999 }, + { 180, 3.141592653589793, 1.2246467991473532e-16, -8.7422777e-08 }, + { 270, 4.712388980384690, -1.0000000000000000, 999 }, + { 360, 6.283185307179586, -2.4492935982947064e-16, 1.7484555e-07 }, }; /* @@ -65,14 +69,29 @@ ATF_TC_BODY(sin_angles, tc) { - const double eps = 1.0e-15; + const double eps = DBL_EPSILON; size_t i; for (i = 0; i < __arraycount(angles); i++) { - - if (fabs(sin(angles[i].x) - angles[i].y) > eps) - atf_tc_fail_nonfatal("sin(%d deg) != %0.01f", - angles[i].angle, angles[i].y); + int deg = angles[i].angle; + double theta = angles[i].x; + double sin_theta = angles[i].y; + bool ok; + + if (sin_theta == 0) { + /* Should be computed exactly. */ + assert(sin_theta == 0); + ok = (sin(theta) == 0); + } else { + assert(sin_theta != 0); + ok = (fabs((sin(theta) - sin_theta)/sin_theta) <= eps); + } + + if (!ok) { + atf_tc_fail_nonfatal("sin(%d deg = %.17g) = %.17g" + " != %.17g", + deg, theta, sin(theta), sin_theta); + } } } @@ -154,18 +173,30 @@ ATF_TC_BODY(sinf_angles, tc) { - const float eps = 1.0e-6; - float x, y; + const float eps = FLT_EPSILON; size_t i; for (i = 0; i < __arraycount(angles); i++) { - - x = angles[i].x; - y = angles[i].y; - - if (fabsf(sinf(x) - y) > eps) - atf_tc_fail_nonfatal("sinf(%d deg) != %0.01f", - angles[i].angle, angles[i].y); + int deg = angles[i].angle; + float theta = angles[i].x; + float sin_theta = angles[i].fy; + bool ok; + + if (sin_theta == 999) + sin_theta = angles[i].y; + + if (sin_theta == 0) { + /* Should be computed exactly. */ + ok = (sinf(theta) == 0); + } else { + ok = (fabsf((sinf(theta) - sin_theta)/sin_theta) + <= eps); + } + + if (!ok) { + atf_tc_fail_nonfatal("sinf(%d deg) = %.8g != %.8g", + deg, sinf(theta), sin_theta); + } } } diff --git a/lib/libm/t_sinh.c b/lib/libm/t_sinh.c --- a/lib/libm/t_sinh.c +++ b/lib/libm/t_sinh.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_sinh.c,v 1.6 2014/03/03 10:39:08 martin Exp $ */ +/* $NetBSD: t_sinh.c,v 1.7 2018/11/07 03:59:36 riastradh Exp $ */ /*- * Copyright (c) 2011 The NetBSD Foundation, Inc. @@ -29,27 +29,27 @@ * POSSIBILITY OF SUCH DAMAGE. */ #include -__RCSID("$NetBSD: t_sinh.c,v 1.6 2014/03/03 10:39:08 martin Exp $"); +__RCSID("$NetBSD: t_sinh.c,v 1.7 2018/11/07 03:59:36 riastradh Exp $"); #include +#include #include #include static const struct { double x; double y; - double e; } values[] = { - { -10, -11013.23287470339, 1e4, }, - { -2, -3.626860407847019, 1, }, - { -1, -1.175201193643801, 1, }, - { -0.05, -0.050020835937655, 1, }, - { -0.001,-0.001000000166667, 1, }, - { 0.001, 0.001000000166667, 1, }, - { 0.05, 0.050020835937655, 1, }, - { 1, 1.175201193643801, 1, }, - { 2, 3.626860407847019, 1, }, - { 10, 11013.23287470339, 1e4, }, + { -10, -11013.232874703393, }, + { -2, -3.626860407847019, }, + { -1, -1.1752011936438014, }, + { -0.05, -0.050020835937655016, }, + { -0.001,-0.0010000001666666751, }, + { 0.001, 0.0010000001666666751, }, + { 0.05, 0.050020835937655016, }, + { 1, 1.1752011936438014, }, + { 2, 3.626860407847019, }, + { 10, 11013.232874703393, }, }; /* @@ -63,18 +63,17 @@ ATF_TC_BODY(sinh_inrange, tc) { - double eps; - double x; - double y; + const double eps = DBL_EPSILON; size_t i; for (i = 0; i < __arraycount(values); i++) { - x = values[i].x; - y = values[i].y; - eps = 1e-15 * values[i].e; + double x = values[i].x; + double sinh_x = values[i].y; - if (fabs(sinh(x) - y) > eps) - atf_tc_fail_nonfatal("sinh(%g) != %g\n", x, y); + if (!(fabs((sinh(x) - sinh_x)/sinh_x) <= eps)) { + atf_tc_fail_nonfatal("sinh(%.17g) = %.17g != %.17g\n", + x, sinh(x), sinh_x); + } } } @@ -163,18 +162,17 @@ ATF_TC_BODY(sinhf_inrange, tc) { - float eps; - float x; - float y; + const float eps = FLT_EPSILON; size_t i; for (i = 0; i < __arraycount(values); i++) { - x = values[i].x; - y = values[i].y; - eps = 1e-6 * values[i].e; + float x = values[i].x; + float sinh_x = values[i].y; - if (fabsf(sinhf(x) - y) > eps) - atf_tc_fail_nonfatal("sinhf(%g) != %g\n", x, y); + if (!(fabsf((sinhf(x) - sinh_x)/sinh_x) <= eps)) { + atf_tc_fail_nonfatal("sinhf(%.8g) = %.8g != %.8g\n", + (double)x, (double)sinhf(x), (double)sinh_x); + } } } diff --git a/lib/libm/t_sqrt.c b/lib/libm/t_sqrt.c --- a/lib/libm/t_sqrt.c +++ b/lib/libm/t_sqrt.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_sqrt.c,v 1.7 2014/03/12 21:40:07 martin Exp $ */ +/* $NetBSD: t_sqrt.c,v 1.8 2018/11/07 03:59:36 riastradh Exp $ */ /*- * Copyright (c) 2011 The NetBSD Foundation, Inc. @@ -29,7 +29,7 @@ * POSSIBILITY OF SUCH DAMAGE. */ #include -__RCSID("$NetBSD: t_sqrt.c,v 1.7 2014/03/12 21:40:07 martin Exp $"); +__RCSID("$NetBSD: t_sqrt.c,v 1.8 2018/11/07 03:59:36 riastradh Exp $"); #include #include @@ -62,22 +62,25 @@ ATF_TC_BODY(sqrt_pow, tc) { const double x[] = { 0.0, 0.005, 1.0, 99.0, 123.123, 9999.9999 }; -#if __DBL_MIN_10_EXP__ <= -40 - const double eps = 1.0e-40; -#else - const double eps = __DBL_MIN__*4.0; -#endif - double y, z; + const double eps = DBL_EPSILON; size_t i; for (i = 0; i < __arraycount(x); i++) { - - y = sqrt(x[i]); - z = pow(x[i], 1.0 / 2.0); - - if (fabs(y - z) > eps) - atf_tc_fail_nonfatal("sqrt(%0.03f) != " - "pow(%0.03f, 1/2)\n", x[i], x[i]); + double x_sqrt = sqrt(x[i]); + double x_pow12 = pow(x[i], 1.0 / 2.0); + bool ok; + + if (x[i] == 0) { + ok = (x_sqrt == x_pow12); + } else { + ok = (fabs((x_sqrt - x_pow12)/x_sqrt) <= eps); + } + + if (!ok) { + atf_tc_fail_nonfatal("sqrt(%.17g) = %.17g != " + "pow(%.17g, 1/2) = %.17g\n", + x[i], x_sqrt, x[i], x_pow12); + } } } @@ -166,18 +169,26 @@ ATF_TC_BODY(sqrtf_powf, tc) { const float x[] = { 0.0, 0.005, 1.0, 99.0, 123.123, 9999.9999 }; - const float eps = 1.0e-30; - volatile float y, z; + const float eps = FLT_EPSILON; size_t i; for (i = 0; i < __arraycount(x); i++) { - - y = sqrtf(x[i]); - z = powf(x[i], 1.0 / 2.0); - - if (fabsf(y - z) > eps) - atf_tc_fail_nonfatal("sqrtf(%0.03f) != " - "powf(%0.03f, 1/2)\n", x[i], x[i]); + float x_sqrt = sqrtf(x[i]); + float x_pow12 = powf(x[i], 1.0 / 2.0); + bool ok; + + if (x[i] == 0) { + ok = (x_sqrt == x_pow12); + } else { + ok = (fabsf((x_sqrt - x_pow12)/x_sqrt) <= eps); + } + + if (!ok) { + atf_tc_fail_nonfatal("sqrtf(%.8g) = %.8g != " + "powf(%.8g, 1/2) = %.8g\n", + (double)x[i], (double)x_sqrt, + (double)x[i], (double)x_pow12); + } } } @@ -266,18 +277,25 @@ ATF_TC_BODY(sqrtl_powl, tc) { const long double x[] = { 0.0, 0.005, 1.0, 99.0, 123.123, 9999.9999 }; - const long double eps = 5.0*DBL_EPSILON; /* XXX powl == pow for now */ - volatile long double y, z; + const long double eps = DBL_EPSILON; /* XXX powl == pow for now */ size_t i; for (i = 0; i < __arraycount(x); i++) { - - y = sqrtl(x[i]); - z = powl(x[i], 1.0 / 2.0); - - if (fabsl(y - z) > eps) - atf_tc_fail_nonfatal("sqrtl(%0.03Lf) != " - "powl(%0.03Lf, 1/2)\n", x[i], x[i]); + long double x_sqrt = sqrtl(x[i]); + long double x_pow12 = powl(x[i], 1.0 / 2.0); + bool ok; + + if (x[i] == 0) { + ok = (x_sqrt == x_pow12); + } else { + ok = (fabsl((x_sqrt - x_pow12)/x_sqrt) <= eps); + } + + if (!ok) { + atf_tc_fail_nonfatal("sqrtl(%.35Lg) = %.35Lg != " + "powl(%.35Lg, 1/2) = %.35Lg\n", + x[i], x_sqrt, x[i], x_pow12); + } } } diff --git a/lib/libm/t_tan.c b/lib/libm/t_tan.c --- a/lib/libm/t_tan.c +++ b/lib/libm/t_tan.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_tan.c,v 1.5 2014/03/03 10:39:08 martin Exp $ */ +/* $NetBSD: t_tan.c,v 1.7 2018/11/07 04:00:13 riastradh Exp $ */ /*- * Copyright (c) 2011 The NetBSD Foundation, Inc. @@ -29,26 +29,29 @@ * POSSIBILITY OF SUCH DAMAGE. */ +#include #include +#include #include static const struct { int angle; double x; double y; + float fy; } angles[] = { - { -180, -3.141592653589793, 0.0000000000000000 }, - { -135, -2.356194490192345, 1.0000000000000000 }, - { -45, -0.785398163397448, -1.0000000000000000 }, - { 0, 0.000000000000000, 0.0000000000000000 }, - { 30, 0.523598775598299, 0.5773502691896258 }, - { 45, 0.785398163397448, 1.0000000000000000 }, - { 60, 1.047197551196598, 1.7320508075688773 }, - { 120, 2.094395102393195, -1.7320508075688773 }, - { 135, 2.356194490192345, -1.0000000000000000 }, - { 150, 2.617993877991494, -0.5773502691896258 }, - { 180, 3.141592653589793, 0.0000000000000000 }, - { 360, 6.283185307179586, 0.0000000000000000 } + { -180, -3.141592653589793, 1.2246467991473532e-16, -8.7422777e-08 }, + { -135, -2.356194490192345, 1.0000000000000002, 999 }, + { -45, -0.785398163397448, -0.9999999999999992, 999 }, + { 0, 0.000000000000000, 0.0000000000000000, 999 }, + { 30, 0.5235987755982988, 0.57735026918962573, 999 }, + { 45, 0.785398163397448, 0.9999999999999992, 999 }, + { 60, 1.047197551196598, 1.7320508075688785, 1.7320509 }, + { 120, 2.094395102393195, -1.7320508075688801, -1.7320505 }, + { 135, 2.356194490192345, -1.0000000000000002, 999 }, + { 150, 2.617993877991494, -0.57735026918962629, -0.57735032 }, + { 180, 3.141592653589793, -1.2246467991473532e-16, 8.7422777e-08 }, + { 360, 6.283185307179586, -2.4492935982947064e-16, 1.7484555e-07 }, }; /* @@ -62,14 +65,29 @@ ATF_TC_BODY(tan_angles, tc) { - const double eps = 1.0e-14; + const double eps = DBL_EPSILON; size_t i; for (i = 0; i < __arraycount(angles); i++) { - - if (fabs(tan(angles[i].x) - angles[i].y) > eps) - atf_tc_fail_nonfatal("tan(%d deg) != %0.01f", - angles[i].angle, angles[i].y); + int deg = angles[i].angle; + double theta = angles[i].x; + double tan_theta = angles[i].y; + bool ok; + + if (theta == 0) { + /* Should be computed exactly. */ + assert(tan_theta == 0); + ok = (tan(theta) == 0); + } else { + assert(tan_theta != 0); + ok = (fabs((tan(theta) - tan_theta)/tan_theta) <= eps); + } + + if (!ok) { + atf_tc_fail_nonfatal("tan(%d deg = %.17g) = %.17g" + " != %.17g", + deg, theta, tan(theta), tan_theta); + } } } @@ -151,18 +169,32 @@ ATF_TC_BODY(tanf_angles, tc) { - const float eps = 1.0e-6; - float x, y; + const float eps = FLT_EPSILON; size_t i; for (i = 0; i < __arraycount(angles); i++) { - - x = angles[i].x; - y = angles[i].y; - - if (fabsf(tanf(x) - y) > eps) - atf_tc_fail_nonfatal("tanf(%d deg) != %0.01f", - angles[i].angle, angles[i].y); + int deg = angles[i].angle; + float theta = angles[i].x; + float tan_theta = angles[i].fy; + bool ok; + + if (tan_theta == 999) + tan_theta = angles[i].y; + + if (theta == 0) { + /* Should be computed exactly. */ + assert(tan_theta == 0); + ok = (tan(theta) == 0); + } else { + assert(tan_theta != 0); + ok = (fabsf((tanf(theta) - tan_theta)/tan_theta) + <= eps); + } + + if (!ok) { + atf_tc_fail_nonfatal("tanf(%d deg) = %.8g != %.8g", + deg, tanf(theta), tan_theta); + } } } diff --git a/lib/libnvmm/Makefile b/lib/libnvmm/Makefile new file mode 100644 --- /dev/null +++ b/lib/libnvmm/Makefile @@ -0,0 +1,26 @@ +# $NetBSD: Makefile,v 1.5 2019/02/05 13:00:03 maxv Exp $ + +NOMAN= # defined + +.include + +TESTSDIR= ${TESTSBASE}/lib/libnvmm + +COPTS+= -Wall +LDADD+= -lnvmm + +BINDIR= ${TESTSDIR} + +.if ${MACHINE} == "amd64" +# I/O Assist +TESTS_SH= t_io_assist +PROGS= h_io_assist +SRCS.h_io_assist= h_io_assist.c h_io_assist_asm.S + +# Mem Assist +TESTS_SH+= t_mem_assist +PROGS+= h_mem_assist +SRCS.h_mem_assist= h_mem_assist.c h_mem_assist_asm.S +.endif + +.include diff --git a/lib/libnvmm/h_io_assist.c b/lib/libnvmm/h_io_assist.c new file mode 100644 --- /dev/null +++ b/lib/libnvmm/h_io_assist.c @@ -0,0 +1,389 @@ +/* $NetBSD: h_io_assist.c,v 1.12 2020/09/05 07:22:26 maxv Exp $ */ + +/* + * Copyright (c) 2018-2020 Maxime Villard, m00nbsd.net + * All rights reserved. + * + * This code is part of the NVMM hypervisor. + * + * 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 ``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 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define PAGE_SIZE 4096 +#define IO_SIZE 128 + +static char iobuf[IO_SIZE]; + +static char *databuf; +static uint8_t *instbuf; + +static void +init_seg(struct nvmm_x64_state_seg *seg, int type, int sel) +{ + seg->selector = sel; + seg->attrib.type = type; + seg->attrib.s = (type & 0b10000) != 0; + seg->attrib.dpl = 0; + seg->attrib.p = 1; + seg->attrib.avl = 1; + seg->attrib.l = 1; + seg->attrib.def = 0; + seg->attrib.g = 1; + seg->limit = 0x0000FFFF; + seg->base = 0x00000000; +} + +static void +reset_machine(struct nvmm_machine *mach, struct nvmm_vcpu *vcpu) +{ + struct nvmm_x64_state *state = vcpu->state; + + memset(state, 0, sizeof(*state)); + + /* Default. */ + state->gprs[NVMM_X64_GPR_RFLAGS] = PSL_MBO; + init_seg(&state->segs[NVMM_X64_SEG_CS], SDT_MEMERA, GSEL(GCODE_SEL, SEL_KPL)); + init_seg(&state->segs[NVMM_X64_SEG_SS], SDT_MEMRWA, GSEL(GDATA_SEL, SEL_KPL)); + init_seg(&state->segs[NVMM_X64_SEG_DS], SDT_MEMRWA, GSEL(GDATA_SEL, SEL_KPL)); + init_seg(&state->segs[NVMM_X64_SEG_ES], SDT_MEMRWA, GSEL(GDATA_SEL, SEL_KPL)); + init_seg(&state->segs[NVMM_X64_SEG_FS], SDT_MEMRWA, GSEL(GDATA_SEL, SEL_KPL)); + init_seg(&state->segs[NVMM_X64_SEG_GS], SDT_MEMRWA, GSEL(GDATA_SEL, SEL_KPL)); + + /* Blank. */ + init_seg(&state->segs[NVMM_X64_SEG_GDT], 0, 0); + init_seg(&state->segs[NVMM_X64_SEG_IDT], 0, 0); + init_seg(&state->segs[NVMM_X64_SEG_LDT], SDT_SYSLDT, 0); + init_seg(&state->segs[NVMM_X64_SEG_TR], SDT_SYS386BSY, 0); + + /* Protected mode enabled. */ + state->crs[NVMM_X64_CR_CR0] = CR0_PG|CR0_PE|CR0_NE|CR0_TS|CR0_MP|CR0_WP|CR0_AM; + + /* 64bit mode enabled. */ + state->crs[NVMM_X64_CR_CR4] = CR4_PAE; + state->msrs[NVMM_X64_MSR_EFER] = EFER_LME | EFER_SCE | EFER_LMA; + + /* Stolen from x86/pmap.c */ +#define PATENTRY(n, type) (type << ((n) * 8)) +#define PAT_UC 0x0ULL +#define PAT_WC 0x1ULL +#define PAT_WT 0x4ULL +#define PAT_WP 0x5ULL +#define PAT_WB 0x6ULL +#define PAT_UCMINUS 0x7ULL + state->msrs[NVMM_X64_MSR_PAT] = + PATENTRY(0, PAT_WB) | PATENTRY(1, PAT_WT) | + PATENTRY(2, PAT_UCMINUS) | PATENTRY(3, PAT_UC) | + PATENTRY(4, PAT_WB) | PATENTRY(5, PAT_WT) | + PATENTRY(6, PAT_UCMINUS) | PATENTRY(7, PAT_UC); + + /* Page tables. */ + state->crs[NVMM_X64_CR_CR3] = 0x3000; + + state->gprs[NVMM_X64_GPR_RIP] = 0x2000; + + if (nvmm_vcpu_setstate(mach, vcpu, NVMM_X64_STATE_ALL) == -1) + err(errno, "nvmm_vcpu_setstate"); +} + +static void +map_pages(struct nvmm_machine *mach) +{ + pt_entry_t *L4, *L3, *L2, *L1; + int ret; + + instbuf = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, + -1, 0); + if (instbuf == MAP_FAILED) + err(errno, "mmap"); + databuf = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, + -1, 0); + if (databuf == MAP_FAILED) + err(errno, "mmap"); + + if (nvmm_hva_map(mach, (uintptr_t)instbuf, PAGE_SIZE) == -1) + err(errno, "nvmm_hva_map"); + if (nvmm_hva_map(mach, (uintptr_t)databuf, PAGE_SIZE) == -1) + err(errno, "nvmm_hva_map"); + ret = nvmm_gpa_map(mach, (uintptr_t)instbuf, 0x2000, PAGE_SIZE, + PROT_READ|PROT_EXEC); + if (ret == -1) + err(errno, "nvmm_gpa_map"); + ret = nvmm_gpa_map(mach, (uintptr_t)databuf, 0x1000, PAGE_SIZE, + PROT_READ|PROT_WRITE); + if (ret == -1) + err(errno, "nvmm_gpa_map"); + + L4 = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, + -1, 0); + if (L4 == MAP_FAILED) + err(errno, "mmap"); + L3 = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, + -1, 0); + if (L3 == MAP_FAILED) + err(errno, "mmap"); + L2 = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, + -1, 0); + if (L2 == MAP_FAILED) + err(errno, "mmap"); + L1 = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, + -1, 0); + if (L1 == MAP_FAILED) + err(errno, "mmap"); + + if (nvmm_hva_map(mach, (uintptr_t)L4, PAGE_SIZE) == -1) + err(errno, "nvmm_hva_map"); + if (nvmm_hva_map(mach, (uintptr_t)L3, PAGE_SIZE) == -1) + err(errno, "nvmm_hva_map"); + if (nvmm_hva_map(mach, (uintptr_t)L2, PAGE_SIZE) == -1) + err(errno, "nvmm_hva_map"); + if (nvmm_hva_map(mach, (uintptr_t)L1, PAGE_SIZE) == -1) + err(errno, "nvmm_hva_map"); + + ret = nvmm_gpa_map(mach, (uintptr_t)L4, 0x3000, PAGE_SIZE, + PROT_READ|PROT_WRITE); + if (ret == -1) + err(errno, "nvmm_gpa_map"); + ret = nvmm_gpa_map(mach, (uintptr_t)L3, 0x4000, PAGE_SIZE, + PROT_READ|PROT_WRITE); + if (ret == -1) + err(errno, "nvmm_gpa_map"); + ret = nvmm_gpa_map(mach, (uintptr_t)L2, 0x5000, PAGE_SIZE, + PROT_READ|PROT_WRITE); + if (ret == -1) + err(errno, "nvmm_gpa_map"); + ret = nvmm_gpa_map(mach, (uintptr_t)L1, 0x6000, PAGE_SIZE, + PROT_READ|PROT_WRITE); + if (ret == -1) + err(errno, "nvmm_gpa_map"); + + memset(L4, 0, PAGE_SIZE); + memset(L3, 0, PAGE_SIZE); + memset(L2, 0, PAGE_SIZE); + memset(L1, 0, PAGE_SIZE); + + L4[0] = PTE_P | PTE_W | 0x4000; + L3[0] = PTE_P | PTE_W | 0x5000; + L2[0] = PTE_P | PTE_W | 0x6000; + L1[0x2000 / PAGE_SIZE] = PTE_P | PTE_W | 0x2000; + L1[0x1000 / PAGE_SIZE] = PTE_P | PTE_W | 0x1000; +} + +/* -------------------------------------------------------------------------- */ + +static size_t iobuf_off = 0; + +static void +io_callback(struct nvmm_io *io) +{ + if (io->port != 123) { + printf("Wrong port\n"); + exit(-1); + } + + if (io->in) { + memcpy(io->data, iobuf + iobuf_off, io->size); + } else { + memcpy(iobuf + iobuf_off, io->data, io->size); + } + iobuf_off += io->size; + +} + +static int +handle_io(struct nvmm_machine *mach, struct nvmm_vcpu *vcpu) +{ + int ret; + + ret = nvmm_assist_io(mach, vcpu); + if (ret == -1) { + err(errno, "nvmm_assist_io"); + } + + return 0; +} + +static void +run_machine(struct nvmm_machine *mach, struct nvmm_vcpu *vcpu) +{ + struct nvmm_vcpu_exit *exit = vcpu->exit; + + while (1) { + if (nvmm_vcpu_run(mach, vcpu) == -1) + err(errno, "nvmm_vcpu_run"); + + switch (exit->reason) { + case NVMM_VCPU_EXIT_NONE: + break; + + case NVMM_VCPU_EXIT_RDMSR: + /* Stop here. */ + return; + + case NVMM_VCPU_EXIT_IO: + handle_io(mach, vcpu); + break; + + case NVMM_VCPU_EXIT_SHUTDOWN: + printf("Shutting down!\n"); + return; + + default: + printf("Invalid!\n"); + return; + } + } +} + +/* -------------------------------------------------------------------------- */ + +struct test { + const char *name; + uint8_t *code_begin; + uint8_t *code_end; + const char *wanted; + bool in; +}; + +static void +run_test(struct nvmm_machine *mach, struct nvmm_vcpu *vcpu, + const struct test *test) +{ + size_t size; + char *res; + + size = (size_t)test->code_end - (size_t)test->code_begin; + + reset_machine(mach, vcpu); + + iobuf_off = 0; + memset(iobuf, 0, IO_SIZE); + memset(databuf, 0, PAGE_SIZE); + memcpy(instbuf, test->code_begin, size); + + if (test->in) { + strcpy(iobuf, test->wanted); + } else { + strcpy(databuf, test->wanted); + } + + run_machine(mach, vcpu); + + if (test->in) { + res = databuf; + } else { + res = iobuf; + } + + if (!strcmp(res, test->wanted)) { + printf("Test '%s' passed\n", test->name); + } else { + printf("Test '%s' failed, wanted '%s', got '%s'\n", test->name, + test->wanted, res); + } +} + +/* -------------------------------------------------------------------------- */ + +extern uint8_t test1_begin, test1_end; +extern uint8_t test2_begin, test2_end; +extern uint8_t test3_begin, test3_end; +extern uint8_t test4_begin, test4_end; +extern uint8_t test5_begin, test5_end; +extern uint8_t test6_begin, test6_end; +extern uint8_t test7_begin, test7_end; +extern uint8_t test8_begin, test8_end; +extern uint8_t test9_begin, test9_end; +extern uint8_t test10_begin, test10_end; +extern uint8_t test11_begin, test11_end; +extern uint8_t test12_begin, test12_end; + +static const struct test tests[] = { + { "test1 - INB", &test1_begin, &test1_end, "12", true }, + { "test2 - INW", &test2_begin, &test2_end, "1234", true }, + { "test3 - INL", &test3_begin, &test3_end, "12345678", true }, + { "test4 - INSB+REP", &test4_begin, &test4_end, "12345", true }, + { "test5 - INSW+REP", &test5_begin, &test5_end, + "Comment est votre blanquette", true }, + { "test6 - INSL+REP", &test6_begin, &test6_end, + "123456789abcdefghijklmnopqrs", true }, + { "test7 - OUTB", &test7_begin, &test7_end, "12", false }, + { "test8 - OUTW", &test8_begin, &test8_end, "1234", false }, + { "test9 - OUTL", &test9_begin, &test9_end, "12345678", false }, + { "test10 - OUTSB+REP", &test10_begin, &test10_end, "12345", false }, + { "test11 - OUTSW+REP", &test11_begin, &test11_end, + "Ah, Herr Bramard", false }, + { "test12 - OUTSL+REP", &test12_begin, &test12_end, + "123456789abcdefghijklmnopqrs", false }, + { NULL, NULL, NULL, NULL, false } +}; + +static struct nvmm_assist_callbacks callbacks = { + .io = io_callback, + .mem = NULL +}; + +/* + * 0x1000: Data, mapped + * 0x2000: Instructions, mapped + * 0x3000: L4 + * 0x4000: L3 + * 0x5000: L2 + * 0x6000: L1 + */ +int main(int argc, char *argv[]) +{ + struct nvmm_machine mach; + struct nvmm_vcpu vcpu; + size_t i; + + if (nvmm_init() == -1) + err(errno, "nvmm_init"); + if (nvmm_machine_create(&mach) == -1) + err(errno, "nvmm_machine_create"); + if (nvmm_vcpu_create(&mach, 0, &vcpu) == -1) + err(errno, "nvmm_vcpu_create"); + nvmm_vcpu_configure(&mach, &vcpu, NVMM_VCPU_CONF_CALLBACKS, &callbacks); + map_pages(&mach); + + for (i = 0; tests[i].name != NULL; i++) { + run_test(&mach, &vcpu, &tests[i]); + } + + return 0; +} diff --git a/lib/libnvmm/h_io_assist_asm.S b/lib/libnvmm/h_io_assist_asm.S new file mode 100644 --- /dev/null +++ b/lib/libnvmm/h_io_assist_asm.S @@ -0,0 +1,218 @@ +/* $NetBSD: h_io_assist_asm.S,v 1.3 2020/09/05 07:22:26 maxv Exp $ */ + +/* + * Copyright (c) 2019-2020 Maxime Villard, m00nbsd.net + * All rights reserved. + * + * This code is part of the NVMM hypervisor. + * + * 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 ``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 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. + */ + + .globl test1_begin, test1_end + .globl test2_begin, test2_end + .globl test3_begin, test3_end + .globl test4_begin, test4_end + .globl test5_begin, test5_end + .globl test6_begin, test6_end + .globl test7_begin, test7_end + .globl test8_begin, test8_end + .globl test9_begin, test9_end + .globl test10_begin, test10_end + .globl test11_begin, test11_end + .globl test12_begin, test12_end + .text + .code64 + +#define TEST_END \ + movq $0xFFFFFFFFFFFFFFFF,%rcx; \ + rdmsr ; + +/* + * IN + */ + + .align 64 +test1_begin: + movq $0x1000,%rbx + + inb $123 + movb %al,(%rbx) + incq %rbx + + movq $123,%rdx + inb %dx + movb %al,(%rbx) + + TEST_END +test1_end: + + .align 64 +test2_begin: + movq $0x1000,%rbx + + inw $123 + movw %ax,(%rbx) + addq $2,%rbx + + movq $123,%rdx + inw %dx + movw %ax,(%rbx) + + TEST_END +test2_end: + + .align 64 +test3_begin: + movq $0x1000,%rbx + + inl $123 + movl %eax,(%rbx) + addq $4,%rbx + + movq $123,%rdx + inl %dx + movl %eax,(%rbx) + + TEST_END +test3_end: + + .align 64 +test4_begin: + movq $0x1000,%rdi + movq $5,%rcx + + movq $123,%rdx + rep + insb + + TEST_END +test4_end: + + .align 64 +test5_begin: + movq $0x1000,%rdi + movq $14,%rcx + + movq $123,%rdx + rep + insw + + TEST_END +test5_end: + + .align 64 +test6_begin: + movq $0x1000,%rdi + movq $7,%rcx + + movq $123,%rdx + rep + insl + + TEST_END +test6_end: + +/* + * OUT + */ + + .align 64 +test7_begin: + movq $0x1000,%rbx + + movb (%rbx),%al + outb $123 + incq %rbx + + movb (%rbx),%al + movq $123,%rdx + outb %dx + + TEST_END +test7_end: + + .align 64 +test8_begin: + movq $0x1000,%rbx + + movw (%rbx),%ax + outw $123 + addq $2,%rbx + + movw (%rbx),%ax + movq $123,%rdx + outw %dx + + TEST_END +test8_end: + + .align 64 +test9_begin: + movq $0x1000,%rbx + + movl (%rbx),%eax + outl $123 + addq $4,%rbx + + movl (%rbx),%eax + movq $123,%rdx + outl %dx + + TEST_END +test9_end: + + .align 64 +test10_begin: + movq $0x1000,%rsi + movq $5,%rcx + + movq $123,%rdx + rep + outsb + + TEST_END +test10_end: + + .align 64 +test11_begin: + movq $0x1000,%rsi + movq $8,%rcx + + movq $123,%rdx + rep + outsw + + TEST_END +test11_end: + + .align 64 +test12_begin: + movq $0x1000,%rsi + movq $7,%rcx + + movq $123,%rdx + rep + outsl + + TEST_END +test12_end: diff --git a/lib/libnvmm/h_mem_assist.c b/lib/libnvmm/h_mem_assist.c new file mode 100644 --- /dev/null +++ b/lib/libnvmm/h_mem_assist.c @@ -0,0 +1,475 @@ +/* $NetBSD: h_mem_assist.c,v 1.20 2020/12/27 20:56:14 reinoud Exp $ */ + +/* + * Copyright (c) 2018-2020 Maxime Villard, m00nbsd.net + * All rights reserved. + * + * This code is part of the NVMM hypervisor. + * + * 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 ``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 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define PAGE_SIZE 4096 + +static uint8_t mmiobuf[PAGE_SIZE]; +static uint8_t *instbuf; + +/* -------------------------------------------------------------------------- */ + +static void +mem_callback(struct nvmm_mem *mem) +{ + size_t off; + + if (mem->gpa < 0x1000 || mem->gpa + mem->size > 0x1000 + PAGE_SIZE) { + printf("Out of page\n"); + exit(-1); + } + + off = mem->gpa - 0x1000; + + printf("-> gpa = %p\n", (void *)mem->gpa); + + if (mem->write) { + memcpy(mmiobuf + off, mem->data, mem->size); + } else { + memcpy(mem->data, mmiobuf + off, mem->size); + } +} + +static int +handle_memory(struct nvmm_machine *mach, struct nvmm_vcpu *vcpu) +{ + int ret; + + ret = nvmm_assist_mem(mach, vcpu); + if (ret == -1) { + err(errno, "nvmm_assist_mem"); + } + + return 0; +} + +static void +run_machine(struct nvmm_machine *mach, struct nvmm_vcpu *vcpu) +{ + struct nvmm_vcpu_exit *exit = vcpu->exit; + + while (1) { + if (nvmm_vcpu_run(mach, vcpu) == -1) + err(errno, "nvmm_vcpu_run"); + + switch (exit->reason) { + case NVMM_VCPU_EXIT_NONE: + break; + + case NVMM_VCPU_EXIT_RDMSR: + /* Stop here. */ + return; + + case NVMM_VCPU_EXIT_MEMORY: + handle_memory(mach, vcpu); + break; + + case NVMM_VCPU_EXIT_SHUTDOWN: + printf("Shutting down!\n"); + return; + + default: + printf("Invalid VMEXIT: 0x%lx\n", exit->reason); + return; + } + } +} + +static struct nvmm_assist_callbacks callbacks = { + .io = NULL, + .mem = mem_callback +}; + +/* -------------------------------------------------------------------------- */ + +struct test { + const char *name; + uint8_t *code_begin; + uint8_t *code_end; + uint64_t wanted; + uint64_t off; +}; + +static void +run_test(struct nvmm_machine *mach, struct nvmm_vcpu *vcpu, + const struct test *test) +{ + uint64_t *res; + size_t size; + + size = (size_t)test->code_end - (size_t)test->code_begin; + + memset(mmiobuf, 0, PAGE_SIZE); + memcpy(instbuf, test->code_begin, size); + + run_machine(mach, vcpu); + + res = (uint64_t *)(mmiobuf + test->off); + if (*res == test->wanted) { + printf("Test '%s' passed\n", test->name); + } else { + printf("Test '%s' failed, wanted 0x%lx, got 0x%lx\n", test->name, + test->wanted, *res); + } +} + +/* -------------------------------------------------------------------------- */ + +extern uint8_t test1_begin, test1_end; +extern uint8_t test2_begin, test2_end; +extern uint8_t test3_begin, test3_end; +extern uint8_t test4_begin, test4_end; +extern uint8_t test5_begin, test5_end; +extern uint8_t test6_begin, test6_end; +extern uint8_t test7_begin, test7_end; +extern uint8_t test8_begin, test8_end; +extern uint8_t test9_begin, test9_end; +extern uint8_t test10_begin, test10_end; +extern uint8_t test11_begin, test11_end; +extern uint8_t test12_begin, test12_end; +extern uint8_t test13_begin, test13_end; +extern uint8_t test14_begin, test14_end; +extern uint8_t test_64bit_15_begin, test_64bit_15_end; +extern uint8_t test_64bit_16_begin, test_64bit_16_end; +extern uint8_t test17_begin, test17_end; + +static const struct test tests64[] = { + { "64bit test1 - MOV", &test1_begin, &test1_end, 0x3004, 0 }, + { "64bit test2 - OR", &test2_begin, &test2_end, 0x16FF, 0 }, + { "64bit test3 - AND", &test3_begin, &test3_end, 0x1FC0, 0 }, + { "64bit test4 - XOR", &test4_begin, &test4_end, 0x10CF, 0 }, + { "64bit test5 - Address Sizes", &test5_begin, &test5_end, 0x1F00, 0 }, + { "64bit test6 - DMO", &test6_begin, &test6_end, 0xFFAB, 0 }, + { "64bit test7 - STOS", &test7_begin, &test7_end, 0x00123456, 0 }, + { "64bit test8 - LODS", &test8_begin, &test8_end, 0x12345678, 0 }, + { "64bit test9 - MOVS", &test9_begin, &test9_end, 0x12345678, 0 }, + { "64bit test10 - MOVZXB", &test10_begin, &test10_end, 0x00000078, 0 }, + { "64bit test11 - MOVZXW", &test11_begin, &test11_end, 0x00005678, 0 }, + { "64bit test12 - CMP", &test12_begin, &test12_end, 0x00000001, 0 }, + { "64bit test13 - SUB", &test13_begin, &test13_end, 0x0000000F0000A0FF, 0 }, + { "64bit test14 - TEST", &test14_begin, &test14_end, 0x00000001, 0 }, + { "64bit test15 - XCHG", &test_64bit_15_begin, &test_64bit_15_end, 0x123456, 0 }, + { "64bit test16 - XCHG", &test_64bit_16_begin, &test_64bit_16_end, + 0x123456, 0 }, + { "64bit test17 - CMPS", &test17_begin, &test17_end, 0x00001, 0 }, + { NULL, NULL, NULL, -1, 0 } +}; + +static void +init_seg(struct nvmm_x64_state_seg *seg, int type, int sel) +{ + seg->selector = sel; + seg->attrib.type = type; + seg->attrib.s = (type & 0b10000) != 0; + seg->attrib.dpl = 0; + seg->attrib.p = 1; + seg->attrib.avl = 1; + seg->attrib.l = 1; + seg->attrib.def = 0; + seg->attrib.g = 1; + seg->limit = 0x0000FFFF; + seg->base = 0x00000000; +} + +static void +reset_machine64(struct nvmm_machine *mach, struct nvmm_vcpu *vcpu) +{ + struct nvmm_x64_state *state = vcpu->state; + + if (nvmm_vcpu_getstate(mach, vcpu, NVMM_X64_STATE_ALL) == -1) + err(errno, "nvmm_vcpu_getstate"); + + memset(state, 0, sizeof(*state)); + + /* Default. */ + state->gprs[NVMM_X64_GPR_RFLAGS] = PSL_MBO; + init_seg(&state->segs[NVMM_X64_SEG_CS], SDT_MEMERA, GSEL(GCODE_SEL, SEL_KPL)); + init_seg(&state->segs[NVMM_X64_SEG_SS], SDT_MEMRWA, GSEL(GDATA_SEL, SEL_KPL)); + init_seg(&state->segs[NVMM_X64_SEG_DS], SDT_MEMRWA, GSEL(GDATA_SEL, SEL_KPL)); + init_seg(&state->segs[NVMM_X64_SEG_ES], SDT_MEMRWA, GSEL(GDATA_SEL, SEL_KPL)); + init_seg(&state->segs[NVMM_X64_SEG_FS], SDT_MEMRWA, GSEL(GDATA_SEL, SEL_KPL)); + init_seg(&state->segs[NVMM_X64_SEG_GS], SDT_MEMRWA, GSEL(GDATA_SEL, SEL_KPL)); + + /* Blank. */ + init_seg(&state->segs[NVMM_X64_SEG_GDT], 0, 0); + init_seg(&state->segs[NVMM_X64_SEG_IDT], 0, 0); + init_seg(&state->segs[NVMM_X64_SEG_LDT], SDT_SYSLDT, 0); + init_seg(&state->segs[NVMM_X64_SEG_TR], SDT_SYS386BSY, 0); + + /* Protected mode enabled. */ + state->crs[NVMM_X64_CR_CR0] = CR0_PG|CR0_PE|CR0_NE|CR0_TS|CR0_MP|CR0_WP|CR0_AM; + + /* 64bit mode enabled. */ + state->crs[NVMM_X64_CR_CR4] = CR4_PAE; + state->msrs[NVMM_X64_MSR_EFER] = EFER_LME | EFER_SCE | EFER_LMA; + + /* Stolen from x86/pmap.c */ +#define PATENTRY(n, type) (type << ((n) * 8)) +#define PAT_UC 0x0ULL +#define PAT_WC 0x1ULL +#define PAT_WT 0x4ULL +#define PAT_WP 0x5ULL +#define PAT_WB 0x6ULL +#define PAT_UCMINUS 0x7ULL + state->msrs[NVMM_X64_MSR_PAT] = + PATENTRY(0, PAT_WB) | PATENTRY(1, PAT_WT) | + PATENTRY(2, PAT_UCMINUS) | PATENTRY(3, PAT_UC) | + PATENTRY(4, PAT_WB) | PATENTRY(5, PAT_WT) | + PATENTRY(6, PAT_UCMINUS) | PATENTRY(7, PAT_UC); + + /* Page tables. */ + state->crs[NVMM_X64_CR_CR3] = 0x3000; + + state->gprs[NVMM_X64_GPR_RIP] = 0x2000; + + if (nvmm_vcpu_setstate(mach, vcpu, NVMM_X64_STATE_ALL) == -1) + err(errno, "nvmm_vcpu_setstate"); +} + +static void +map_pages64(struct nvmm_machine *mach) +{ + pt_entry_t *L4, *L3, *L2, *L1; + int ret; + + instbuf = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, + -1, 0); + if (instbuf == MAP_FAILED) + err(errno, "mmap"); + + if (nvmm_hva_map(mach, (uintptr_t)instbuf, PAGE_SIZE) == -1) + err(errno, "nvmm_hva_map"); + ret = nvmm_gpa_map(mach, (uintptr_t)instbuf, 0x2000, PAGE_SIZE, + PROT_READ|PROT_EXEC); + if (ret == -1) + err(errno, "nvmm_gpa_map"); + + L4 = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, + -1, 0); + if (L4 == MAP_FAILED) + err(errno, "mmap"); + L3 = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, + -1, 0); + if (L3 == MAP_FAILED) + err(errno, "mmap"); + L2 = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, + -1, 0); + if (L2 == MAP_FAILED) + err(errno, "mmap"); + L1 = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, + -1, 0); + if (L1 == MAP_FAILED) + err(errno, "mmap"); + + if (nvmm_hva_map(mach, (uintptr_t)L4, PAGE_SIZE) == -1) + err(errno, "nvmm_hva_map"); + if (nvmm_hva_map(mach, (uintptr_t)L3, PAGE_SIZE) == -1) + err(errno, "nvmm_hva_map"); + if (nvmm_hva_map(mach, (uintptr_t)L2, PAGE_SIZE) == -1) + err(errno, "nvmm_hva_map"); + if (nvmm_hva_map(mach, (uintptr_t)L1, PAGE_SIZE) == -1) + err(errno, "nvmm_hva_map"); + + ret = nvmm_gpa_map(mach, (uintptr_t)L4, 0x3000, PAGE_SIZE, + PROT_READ|PROT_WRITE); + if (ret == -1) + err(errno, "nvmm_gpa_map"); + ret = nvmm_gpa_map(mach, (uintptr_t)L3, 0x4000, PAGE_SIZE, + PROT_READ|PROT_WRITE); + if (ret == -1) + err(errno, "nvmm_gpa_map"); + ret = nvmm_gpa_map(mach, (uintptr_t)L2, 0x5000, PAGE_SIZE, + PROT_READ|PROT_WRITE); + if (ret == -1) + err(errno, "nvmm_gpa_map"); + ret = nvmm_gpa_map(mach, (uintptr_t)L1, 0x6000, PAGE_SIZE, + PROT_READ|PROT_WRITE); + if (ret == -1) + err(errno, "nvmm_gpa_map"); + + memset(L4, 0, PAGE_SIZE); + memset(L3, 0, PAGE_SIZE); + memset(L2, 0, PAGE_SIZE); + memset(L1, 0, PAGE_SIZE); + + L4[0] = PTE_P | PTE_W | 0x4000; + L3[0] = PTE_P | PTE_W | 0x5000; + L2[0] = PTE_P | PTE_W | 0x6000; + L1[0x2000 / PAGE_SIZE] = PTE_P | PTE_W | 0x2000; + L1[0x1000 / PAGE_SIZE] = PTE_P | PTE_W | 0x1000; +} + +/* + * 0x1000: MMIO address, unmapped + * 0x2000: Instructions, mapped + * 0x3000: L4 + * 0x4000: L3 + * 0x5000: L2 + * 0x6000: L1 + */ +static void +test_vm64(void) +{ + struct nvmm_machine mach; + struct nvmm_vcpu vcpu; + size_t i; + + if (nvmm_machine_create(&mach) == -1) + err(errno, "nvmm_machine_create"); + if (nvmm_vcpu_create(&mach, 0, &vcpu) == -1) + err(errno, "nvmm_vcpu_create"); + nvmm_vcpu_configure(&mach, &vcpu, NVMM_VCPU_CONF_CALLBACKS, &callbacks); + map_pages64(&mach); + + for (i = 0; tests64[i].name != NULL; i++) { + reset_machine64(&mach, &vcpu); + run_test(&mach, &vcpu, &tests64[i]); + } + + if (nvmm_vcpu_destroy(&mach, &vcpu) == -1) + err(errno, "nvmm_vcpu_destroy"); + if (nvmm_machine_destroy(&mach) == -1) + err(errno, "nvmm_machine_destroy"); +} + +/* -------------------------------------------------------------------------- */ + +extern uint8_t test_16bit_1_begin, test_16bit_1_end; +extern uint8_t test_16bit_2_begin, test_16bit_2_end; +extern uint8_t test_16bit_3_begin, test_16bit_3_end; +extern uint8_t test_16bit_4_begin, test_16bit_4_end; +extern uint8_t test_16bit_5_begin, test_16bit_5_end; +extern uint8_t test_16bit_6_begin, test_16bit_6_end; + +static const struct test tests16[] = { + { "16bit test1 - MOV single", &test_16bit_1_begin, &test_16bit_1_end, + 0x023, 0x10f1 - 0x1000 }, + { "16bit test2 - MOV dual", &test_16bit_2_begin, &test_16bit_2_end, + 0x123, 0x10f3 - 0x1000 }, + { "16bit test3 - MOV dual+disp", &test_16bit_3_begin, &test_16bit_3_end, + 0x678, 0x10f1 - 0x1000 }, + { "16bit test4 - Mixed", &test_16bit_4_begin, &test_16bit_4_end, + 0x1011, 0x10f6 - 0x1000 }, + { "16bit test5 - disp16-only", &test_16bit_5_begin, &test_16bit_5_end, + 0x12, 0x1234 - 0x1000 }, + { "16bit test6 - XCHG", &test_16bit_6_begin, &test_16bit_6_end, + 0x1234, 0x1234 - 0x1000 }, + { NULL, NULL, NULL, -1, -1 } +}; + +static void +reset_machine16(struct nvmm_machine *mach, struct nvmm_vcpu *vcpu) +{ + struct nvmm_x64_state *state = vcpu->state; + + if (nvmm_vcpu_getstate(mach, vcpu, NVMM_X64_STATE_ALL) == -1) + err(errno, "nvmm_vcpu_getstate"); + + state->segs[NVMM_X64_SEG_CS].base = 0; + state->segs[NVMM_X64_SEG_CS].limit = 0x2FFF; + state->gprs[NVMM_X64_GPR_RIP] = 0x2000; + + if (nvmm_vcpu_setstate(mach, vcpu, NVMM_X64_STATE_ALL) == -1) + err(errno, "nvmm_vcpu_setstate"); +} + +static void +map_pages16(struct nvmm_machine *mach) +{ + int ret; + + instbuf = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, + -1, 0); + if (instbuf == MAP_FAILED) + err(errno, "mmap"); + + if (nvmm_hva_map(mach, (uintptr_t)instbuf, PAGE_SIZE) == -1) + err(errno, "nvmm_hva_map"); + ret = nvmm_gpa_map(mach, (uintptr_t)instbuf, 0x2000, PAGE_SIZE, + PROT_READ|PROT_EXEC); + if (ret == -1) + err(errno, "nvmm_gpa_map"); +} + +/* + * 0x1000: MMIO address, unmapped + * 0x2000: Instructions, mapped + */ +static void +test_vm16(void) +{ + struct nvmm_machine mach; + struct nvmm_vcpu vcpu; + size_t i; + + if (nvmm_machine_create(&mach) == -1) + err(errno, "nvmm_machine_create"); + if (nvmm_vcpu_create(&mach, 0, &vcpu) == -1) + err(errno, "nvmm_vcpu_create"); + nvmm_vcpu_configure(&mach, &vcpu, NVMM_VCPU_CONF_CALLBACKS, &callbacks); + map_pages16(&mach); + + for (i = 0; tests16[i].name != NULL; i++) { + reset_machine16(&mach, &vcpu); + run_test(&mach, &vcpu, &tests16[i]); + } + + if (nvmm_vcpu_destroy(&mach, &vcpu) == -1) + err(errno, "nvmm_vcpu_destroy"); + if (nvmm_machine_destroy(&mach) == -1) + err(errno, "nvmm_machine_destroy"); +} + +/* -------------------------------------------------------------------------- */ + +int main(int argc, char *argv[]) +{ + if (nvmm_init() == -1) + err(errno, "nvmm_init"); + test_vm64(); + test_vm16(); + return 0; +} diff --git a/lib/libnvmm/h_mem_assist_asm.S b/lib/libnvmm/h_mem_assist_asm.S new file mode 100644 --- /dev/null +++ b/lib/libnvmm/h_mem_assist_asm.S @@ -0,0 +1,432 @@ +/* $NetBSD: h_mem_assist_asm.S,v 1.10 2020/12/27 20:56:14 reinoud Exp $ */ + +/* + * Copyright (c) 2018-2020 Maxime Villard, m00nbsd.net + * All rights reserved. + * + * This code is part of the NVMM hypervisor. + * + * 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 ``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 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. + */ + + .globl test1_begin, test1_end + .globl test2_begin, test2_end + .globl test3_begin, test3_end + .globl test4_begin, test4_end + .globl test5_begin, test5_end + .globl test6_begin, test6_end + .globl test7_begin, test7_end + .globl test8_begin, test8_end + .globl test9_begin, test9_end + .globl test10_begin, test10_end + .globl test11_begin, test11_end + .globl test12_begin, test12_end + .globl test13_begin, test13_end + .globl test14_begin, test14_end + .globl test_64bit_15_begin, test_64bit_15_end + .globl test_64bit_16_begin, test_64bit_16_end + .globl test17_begin, test17_end + .text + .code64 + +#define TEST_END \ + movq $0xFFFFFFFFFFFFFFFF,%rcx; \ + rdmsr ; + + .align 64 +test1_begin: + movq $0x1000,%rax + movq $0x1000,%rbp + + movq $0x1000,(%rax) + + movq $1,%r11 + movq $0x2000,(%rax,%r11,8) + + movq (%rbp),%r8 + movq 8(%rbp),%rbx + addq %rbx,%r8 + movq %r8,(%rbp) + movb $4,(%rbp) + + TEST_END +test1_end: + + .align 64 +test2_begin: + movq $0x1000,%rax + + movq $0x1000,(%rax) + movq $0x00FF,%rbx + orb %bl,(%rax) + movq $0x0400,%rcx + orw %cx,(%rax) + + movq $0x0200,%rcx + orq (%rax),%rcx + movq %rcx,(%rax) + + TEST_END +test2_end: + + .align 64 +test3_begin: + movq $0x1000,%rax + + movq $0x1FFF,(%rax) + movq $0x1FF0,%rbx + andq %rbx,(%rax) + movq $0x10C1,%rcx + andb %cl,(%rax) + + TEST_END +test3_end: + + .align 64 +test4_begin: + movq $0x1000,%rax + + movq $0x1FFF,(%rax) + movq $0x1FF0,%rbx + xorq %rbx,(%rax) + movq $0x10C0,%rcx + xorw %cx,(%rax) + + TEST_END +test4_end: + + .align 64 +test5_begin: + movq $0xFFFFFFFF00001000,%rax + + movq $0x1FFF,(%eax) + movb $0,(%eax,%ebx,1) + + TEST_END +test5_end: + + .align 64 +test6_begin: + movq $0xFFA0,%rax + movabs %rax,0x1000 + + movabs 0x1000,%al + orb $0x0B,%al + movabs %al,0x1000 + + TEST_END +test6_end: + + .align 64 +test7_begin: + movq $0x56,%rax + + movq $1,%rcx + movq $0x1000,%rdi + rep stosb + + movq $0x1234,%rax + stosw + + TEST_END +test7_end: + + .align 64 +test8_begin: + movq $0x1008,%rsi + movq $0x12345678,(%rsi) + + movq $0x1000,%rdi + + lodsw + movw %ax,(%rdi) + addq $2,%rdi + + lodsb + movb %al,(%rdi) + addq $1,%rdi + + lodsb + movb %al,(%rdi) + addq $2,%rdi + + TEST_END +test8_end: + + .align 64 +test9_begin: + movq $0x1000,%rax + + movq $0x12345678,8(%rax) + + movq $0x1008,%rsi + movq $0x1000,%rdi + + movq $4,%rcx + rep movsb + + movq $2,%rcx + rep movsw + + TEST_END +test9_end: + + .align 64 +test10_begin: + movq $0x1000,%rax + movq $0x12345678,(%rax) + + movq $0xFFFFFFFFFFFFFFFF,%rbx + movzbl (%rax),%ebx + movq %rbx,(%rax) + + TEST_END +test10_end: + + .align 64 +test11_begin: + movq $0x1000,%rax + movq $0x12345678,(%rax) + + movq $0xFFFFFFFFFFFFFFFF,%rbx + movzwq (%rax),%rbx + movq %rbx,(%rax) + + TEST_END +test11_end: + + .align 64 +test12_begin: + movq $0x1000,%rax + movq $0xFFFFFFFFF2345678,(%rax) + + cmpb $0x78,(%rax) + jne .L12_failure + cmpb $0x77,(%rax) + jl .L12_failure + cmpb $0x79,(%rax) + jg .L12_failure + + cmpw $0x5678,(%rax) + jne .L12_failure + cmpw $0x5677,(%rax) + jl .L12_failure + cmpw $0x5679,(%rax) + jg .L12_failure + + cmpl $0xF2345678,(%rax) + jne .L12_failure + cmpl $0xF2345677,(%rax) + jl .L12_failure + cmpl $0xF2345679,(%rax) + jg .L12_failure + + cmpq $0xFFFFFFFFF2345678,(%rax) + jne .L12_failure + cmpq $0xFFFFFFFFF2345677,(%rax) + jl .L12_failure + cmpq $0xFFFFFFFFF2345679,(%rax) + jg .L12_failure + +.L12_success: + movq $1,(%rax) + TEST_END +.L12_failure: + movq $0,(%rax) + TEST_END +test12_end: + + .align 64 +test13_begin: + movq $0x1000,%rax + movq $0x000000001000A0FF,(%rax) + + movq $0xFFFF,%rcx + subb %cl,(%rax) + + movq $0xA000,%rcx + subw %cx,(%rax) + + movq $0x0000000F1000A0FF,%rcx + subq (%rax),%rcx + + movq %rcx,(%rax) + + TEST_END +test13_end: + + .align 64 +test14_begin: + movq $0x1000,%rax + movq $0xA0FF,(%rax) + + testb $0x0F,(%rax) + jz .L14_failure + + testw $0x0F00,(%rax) + jnz .L14_failure + + testl $0xA000,(%rax) + jz .L14_failure + +.L14_success: + movq $1,(%rax) + TEST_END +.L14_failure: + movq $0,(%rax) + TEST_END +test14_end: + + .align 64 +test_64bit_15_begin: + movq $0x1000,%rax + movq $0x120000,%rbx + movq $0x003400,%rcx + movq $0x000056,%rdx + + xchgq %rbx,(%rax) + xchgw (%rax),%cx + xchgb %dl,(%rax) + + TEST_END +test_64bit_15_end: + + .align 64 +test_64bit_16_begin: + movq $0x1000,%rax + movq $0x000000,%rbx + movq $0x000000,%rcx + movq $0x000000,%rdx + + movq $0x123456,(%rax) + xchgq %rbx,(%eax) + movq $0,(%rax) + xchgq %rbx,(%eax) + + TEST_END +test_64bit_16_end: + + .align 64 +test17_begin: + movq $0x1000,%rax + movq $0xdeadbeefcafe, %rbx + movq %rbx,0x00(%rax) + movq %rbx,0x08(%rax) + + movq $0xdeadbeefcafe, %rbx + movq %rbx,0x20(%rax) + movq $0, %rbx + movq %rbx,0x28(%rax) + + movq $0x1000,%rsi + movq $0x1020,%rdi + + movq $3,%rcx + repe cmpsq + + movq %rcx,(%rax) + TEST_END +test17_end: + +/* -------------------------------------------------------------------------- */ + + .globl test_16bit_1_begin, test_16bit_1_end + .globl test_16bit_2_begin, test_16bit_2_end + .globl test_16bit_3_begin, test_16bit_3_end + .globl test_16bit_4_begin, test_16bit_4_end + .globl test_16bit_5_begin, test_16bit_5_end + .globl test_16bit_6_begin, test_16bit_6_end + +#define TEST16_END \ + rdmsr + + .code16 + + .align 64 +test_16bit_1_begin: + movw $0x10f1,%bx + movw $0x123,%dx + + movb %dl,(%bx) + + TEST16_END +test_16bit_1_end: + + .align 64 +test_16bit_2_begin: + movw $0x10f1,%bx + movw $2,%di + movw $0x123,%dx + + movw %dx,(%bx,%di) + + TEST16_END +test_16bit_2_end: + + .align 64 +test_16bit_3_begin: + movw $0x10f1,%bp + movw $2,%si + movw $0x678,%dx + + movw %dx,-2(%bp,%si) + + TEST16_END +test_16bit_3_end: + + .align 64 +test_16bit_4_begin: + movw $0x10f0,%bp + movw $2,%si + movw $2+4+4,%di + movw $0xFFFF,%dx + movl $0x0001,%eax + movl $0x0010,%ebx + movl $0x1000,%ecx + + movw %dx,4(%bp,%si) /* 16bit opr 16bit adr */ + andl %eax,4(%bp,%si) /* 32bit opr 16bit adr */ + orw %bx,4(%ebp,%esi) /* 16bit opr 32bit adr */ + orl %ecx,-4(%bp,%di) /* 32bit opr 16bit adr, negative */ + + TEST16_END +test_16bit_4_end: + + .align 64 +test_16bit_5_begin: + movb $0x12,0x1234 + + TEST16_END +test_16bit_5_end: + + .align 64 +test_16bit_6_begin: + movw $0x1234,%bp + movw $4,%di + movw $0x1200,%bx + movw $0x0034,%cx + + xchgw %bx,(%bp) + xchgb -4(%bp,%di),%cl + + TEST16_END +test_16bit_6_end: diff --git a/lib/libnvmm/t_io_assist.sh b/lib/libnvmm/t_io_assist.sh new file mode 100644 --- /dev/null +++ b/lib/libnvmm/t_io_assist.sh @@ -0,0 +1,52 @@ +# $NetBSD: t_io_assist.sh,v 1.2 2020/09/05 07:22:26 maxv Exp $ +# +# Copyright (c) 2019-2020 Maxime Villard, m00nbsd.net +# All rights reserved. +# +# This code is part of the NVMM hypervisor. +# +# 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 ``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 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. +# + +atf_test_case io_assist +io_assist_head() +{ + atf_set "descr" "Check the I/O Assist provided in libnvmm" +} + +io_assist_body() +{ + $(atf_get_srcdir)/h_io_assist + + exitcode=$? + + if [ $exitcode -eq 6 ] ; then + atf_skip "NVMM driver not loaded" + elif [ $exitcode -ne 0 ] ; then + atf_fail "I/O Assist failed with errno $exitcode" + fi +} + +atf_init_test_cases() +{ + atf_add_test_case io_assist +} diff --git a/lib/libnvmm/t_mem_assist.sh b/lib/libnvmm/t_mem_assist.sh new file mode 100644 --- /dev/null +++ b/lib/libnvmm/t_mem_assist.sh @@ -0,0 +1,52 @@ +# $NetBSD: t_mem_assist.sh,v 1.2 2020/09/05 07:22:26 maxv Exp $ +# +# Copyright (c) 2018-2020 Maxime Villard, m00nbsd.net +# All rights reserved. +# +# This code is part of the NVMM hypervisor. +# +# 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 ``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 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. +# + +atf_test_case mem_assist +mem_assist_head() +{ + atf_set "descr" "Check the Mem Assist provided in libnvmm" +} + +mem_assist_body() +{ + $(atf_get_srcdir)/h_mem_assist + + exitcode=$? + + if [ $exitcode -eq 6 ] ; then + atf_skip "NVMM driver not loaded" + elif [ $exitcode -ne 0 ] ; then + atf_fail "Mem Assist failed with errno $exitcode" + fi +} + +atf_init_test_cases() +{ + atf_add_test_case mem_assist +} diff --git a/lib/libobjc/Makefile b/lib/libobjc/Makefile --- a/lib/libobjc/Makefile +++ b/lib/libobjc/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.2 2011/05/20 13:03:45 joerg Exp $ +# $NetBSD: Makefile,v 1.3 2018/05/09 13:18:02 joerg Exp $ TESTSDIR= ${TESTSBASE}/lib/libobjc @@ -7,7 +7,7 @@ UNSUPPORTED_COMPILER.clang= # defined UNSUPPORTED_COMPILER.pcc= # defined -.if !empty(AVAILABLE_COMPILER:Mgcc) +.if ${HAVE_GCC:U0} > 0 TESTS_C= t_threads .endif diff --git a/lib/libossaudio/Makefile b/lib/libossaudio/Makefile new file mode 100644 --- /dev/null +++ b/lib/libossaudio/Makefile @@ -0,0 +1,13 @@ +# $NetBSD: Makefile,v 1.1 2020/12/11 12:29:28 nia Exp $ + +NOMAN= # defined + +.include + +TESTSDIR= ${TESTSBASE}/lib/libossaudio + +LDADD+= -lossaudio + +TESTS_C= t_ossaudio + +.include diff --git a/lib/libossaudio/t_ossaudio.c b/lib/libossaudio/t_ossaudio.c new file mode 100644 --- /dev/null +++ b/lib/libossaudio/t_ossaudio.c @@ -0,0 +1,465 @@ +/*- + * Copyright (c) 2020 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Nia Alarie. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +#include +#include +#include +#include +#include +#include + +ATF_TC(oss_dsp_init); +ATF_TC_HEAD(oss_dsp_init, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests dsp init ioctls"); +} + +ATF_TC_BODY(oss_dsp_init, tc) +{ + struct audio_info hwinfo; + struct audio_info info; + int fd, channels, fmt, rate; + + if ((fd = open("/dev/audio", O_WRONLY)) == -1) + atf_tc_skip("Audio device unavailable for playback"); + + if (ioctl(fd, AUDIO_GETFORMAT, &hwinfo) < 0) { + atf_tc_fail("ioctl AUDIO_GETFORMAT failed"); + close(fd); + } + + /* Verify SNDCTL_DSP_CHANNELS sets the device to mono. */ + + channels = 1; + if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) < 0) + atf_tc_fail("ioctl SNDCTL_DSP_CHANNELS (1) failed"); + ATF_REQUIRE_EQ(channels, 1); + + if (ioctl(fd, AUDIO_GETBUFINFO, &info) < 0) + atf_tc_fail("ioctl AUDIO_GETBUFINFO failed"); + ATF_REQUIRE_EQ(info.play.channels, 1); + + /* Verify SNDCTL_DSP_CHANNELS sets the device to stereo. */ + + channels = 2; + if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) < 0) + atf_tc_fail("ioctl SNDCTL_DSP_CHANNELS (2) failed"); + ATF_REQUIRE_EQ(channels, 2); + + if (ioctl(fd, AUDIO_GETBUFINFO, &info) < 0) + atf_tc_fail("ioctl AUDIO_GETBUFINFO failed"); + ATF_REQUIRE_EQ(info.play.channels, 2); + + /* + * Verify an invalid argument to SNDCTL_DSP_CHANNELS leaves the device + * at the hardware channel count. + */ + + channels = 0; + if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) < 0) + atf_tc_fail("ioctl SNDCTL_DSP_CHANNELS (0) failed"); + ATF_REQUIRE_EQ(channels, (int)hwinfo.play.channels); + + if (ioctl(fd, AUDIO_GETBUFINFO, &info) < 0) + atf_tc_fail("ioctl AUDIO_GETBUFINFO failed"); + ATF_REQUIRE_EQ(info.play.channels, hwinfo.play.channels); + + /* + * SNDCTL_DSP_STEREO is an older alternative to SNDCTL_DSP_CHANNELS + * that simply takes a boolean argument. + */ + + /* Set the device to mono with SNDCTL_DSP_STEREO = 0 */ + + channels = 0; + if (ioctl(fd, SNDCTL_DSP_STEREO, &channels) < 0) + atf_tc_fail("ioctl SNDCTL_DSP_STEREO (0) failed"); + ATF_REQUIRE_EQ(channels, 0); + + if (ioctl(fd, AUDIO_GETBUFINFO, &info) < 0) + atf_tc_fail("ioctl AUDIO_GETBUFINFO failed"); + + ATF_REQUIRE_EQ(info.play.channels, 1); + + /* Set the device to stereo with SNDCTL_DSP_STEREO = 1 */ + + channels = 1; + if (ioctl(fd, SNDCTL_DSP_STEREO, &channels) < 0) + atf_tc_fail("ioctl SNDCTL_DSP_STEREO (1) failed"); + ATF_REQUIRE_EQ(channels, 1); + + if (ioctl(fd, AUDIO_GETBUFINFO, &info) < 0) + atf_tc_fail("ioctl AUDIO_GETBUFINFO failed"); + ATF_REQUIRE_EQ(info.play.channels, 2); + + /* Verify SNDCTL_DSP_SETFMT works with common audio formats */ + + fmt = AFMT_MU_LAW; + if (ioctl(fd, SNDCTL_DSP_SETFMT, &fmt) < 0) + atf_tc_fail("ioctl SNDCTL_DSP_SETFMT (AFMT_MU_LAW) failed"); + ATF_REQUIRE_EQ(fmt, AFMT_MU_LAW); + + if (ioctl(fd, AUDIO_GETBUFINFO, &info) < 0) + atf_tc_fail("ioctl AUDIO_GETBUFINFO failed"); + ATF_REQUIRE_EQ(info.play.encoding, AUDIO_ENCODING_ULAW); + ATF_REQUIRE_EQ(info.play.precision, 8); + + fmt = AFMT_A_LAW; + if (ioctl(fd, SNDCTL_DSP_SETFMT, &fmt) < 0) + atf_tc_fail("ioctl SNDCTL_DSP_SETFMT (AFMT_A_LAW) failed"); + ATF_REQUIRE_EQ(fmt, AFMT_A_LAW); + + if (ioctl(fd, AUDIO_GETBUFINFO, &info) < 0) + atf_tc_fail("ioctl AUDIO_GETBUFINFO failed"); + ATF_REQUIRE_EQ(info.play.encoding, AUDIO_ENCODING_ALAW); + ATF_REQUIRE_EQ(info.play.precision, 8); + + fmt = AFMT_S16_LE; + if (ioctl(fd, SNDCTL_DSP_SETFMT, &fmt) < 0) + atf_tc_fail("ioctl SNDCTL_DSP_SETFMT (AFMT_S16_LE) failed"); + ATF_REQUIRE_EQ(fmt, AFMT_S16_LE); + + if (ioctl(fd, AUDIO_GETBUFINFO, &info) < 0) + atf_tc_fail("ioctl AUDIO_GETBUFINFO failed"); + ATF_REQUIRE_EQ(info.play.encoding, AUDIO_ENCODING_SLINEAR_LE); + ATF_REQUIRE_EQ(info.play.precision, 16); + + fmt = AFMT_S16_BE; + if (ioctl(fd, SNDCTL_DSP_SETFMT, &fmt) < 0) + atf_tc_fail("ioctl SNDCTL_DSP_SETFMT (AFMT_S16_BE) failed"); + ATF_REQUIRE_EQ(fmt, AFMT_S16_BE); + + if (ioctl(fd, AUDIO_GETBUFINFO, &info) < 0) + atf_tc_fail("ioctl AUDIO_GETBUFINFO failed"); + ATF_REQUIRE_EQ(info.play.encoding, AUDIO_ENCODING_SLINEAR_BE); + ATF_REQUIRE_EQ(info.play.precision, 16); + + fmt = AFMT_U16_LE; + if (ioctl(fd, SNDCTL_DSP_SETFMT, &fmt) < 0) + atf_tc_fail("ioctl SNDCTL_DSP_SETFMT (AFMT_U16_LE) failed"); + ATF_REQUIRE_EQ(fmt, AFMT_U16_LE); + + if (ioctl(fd, AUDIO_GETBUFINFO, &info) < 0) + atf_tc_fail("ioctl AUDIO_GETBUFINFO failed"); + ATF_REQUIRE_EQ(info.play.encoding, AUDIO_ENCODING_ULINEAR_LE); + ATF_REQUIRE_EQ(info.play.precision, 16); + + fmt = AFMT_U16_BE; + if (ioctl(fd, SNDCTL_DSP_SETFMT, &fmt) < 0) + atf_tc_fail("ioctl SNDCTL_DSP_SETFMT (AFMT_U16_BE) failed"); + ATF_REQUIRE_EQ(fmt, AFMT_U16_BE); + + if (ioctl(fd, AUDIO_GETBUFINFO, &info) < 0) + atf_tc_fail("ioctl AUDIO_GETBUFINFO failed"); + ATF_REQUIRE_EQ(info.play.encoding, AUDIO_ENCODING_ULINEAR_BE); + ATF_REQUIRE_EQ(info.play.precision, 16); + + fmt = AFMT_S32_LE; + if (ioctl(fd, SNDCTL_DSP_SETFMT, &fmt) < 0) + atf_tc_fail("ioctl SNDCTL_DSP_SETFMT (AFMT_S32_LE) failed"); + ATF_REQUIRE_EQ(fmt, AFMT_S32_LE); + + if (ioctl(fd, AUDIO_GETBUFINFO, &info) < 0) + atf_tc_fail("ioctl AUDIO_GETBUFINFO failed"); + ATF_REQUIRE_EQ(info.play.encoding, AUDIO_ENCODING_SLINEAR_LE); + ATF_REQUIRE_EQ(info.play.precision, 32); + + /* Verify some supported sample rates. */ + + rate = 8000; + if (ioctl(fd, SNDCTL_DSP_SPEED, &rate) < 0) + atf_tc_fail("ioctl SNDCTL_DSP_SPEED (8000) failed"); + + if (ioctl(fd, AUDIO_GETBUFINFO, &info) < 0) + atf_tc_fail("ioctl AUDIO_GETBUFINFO failed"); + ATF_REQUIRE_EQ(rate, (int)info.play.sample_rate); + + rate = 32000; + if (ioctl(fd, SNDCTL_DSP_SPEED, &rate) < 0) + atf_tc_fail("ioctl SNDCTL_DSP_SPEED (32000) failed"); + + if (ioctl(fd, AUDIO_GETBUFINFO, &info) < 0) + atf_tc_fail("ioctl AUDIO_GETBUFINFO failed"); + ATF_REQUIRE_EQ(info.play.sample_rate, 32000); + ATF_REQUIRE_EQ(rate, (int)info.play.sample_rate); + + rate = 44100; + if (ioctl(fd, SNDCTL_DSP_SPEED, &rate) < 0) + atf_tc_fail("ioctl SNDCTL_DSP_SPEED (44100) failed"); + + if (ioctl(fd, AUDIO_GETBUFINFO, &info) < 0) + atf_tc_fail("ioctl AUDIO_GETBUFINFO failed"); + ATF_REQUIRE_EQ(info.play.sample_rate, 44100); + ATF_REQUIRE_EQ(rate, (int)info.play.sample_rate); + + rate = 48000; + if (ioctl(fd, SNDCTL_DSP_SPEED, &rate) < 0) + atf_tc_fail("ioctl SNDCTL_DSP_SPEED (48000) failed"); + + if (ioctl(fd, AUDIO_GETBUFINFO, &info) < 0) + atf_tc_fail("ioctl AUDIO_GETBUFINFO failed"); + ATF_REQUIRE_EQ(info.play.sample_rate, 48000); + ATF_REQUIRE_EQ(rate, (int)info.play.sample_rate); + + rate = 96000; + if (ioctl(fd, SNDCTL_DSP_SPEED, &rate) < 0) + atf_tc_fail("ioctl SNDCTL_DSP_SPEED (96000) failed"); + + if (ioctl(fd, AUDIO_GETBUFINFO, &info) < 0) + atf_tc_fail("ioctl AUDIO_GETBUFINFO failed"); + ATF_REQUIRE_EQ(info.play.sample_rate, 96000); + ATF_REQUIRE_EQ(rate, (int)info.play.sample_rate); + + rate = 192000; + if (ioctl(fd, SNDCTL_DSP_SPEED, &rate) < 0) + atf_tc_fail("ioctl SNDCTL_DSP_SPEED (192000) failed"); + + if (ioctl(fd, AUDIO_GETBUFINFO, &info) < 0) + atf_tc_fail("ioctl AUDIO_GETBUFINFO failed"); + ATF_REQUIRE_EQ(info.play.sample_rate, 192000); + ATF_REQUIRE_EQ(rate, (int)info.play.sample_rate); + + /* + * and some unsupported sample rates... "best effort" + */ + + /* closest supported rate is 1000 */ + rate = 900; + if (ioctl(fd, SNDCTL_DSP_SPEED, &rate) < 0) + atf_tc_fail("ioctl SNDCTL_DSP_SPEED (900) failed"); + if (ioctl(fd, AUDIO_GETBUFINFO, &info) < 0) + atf_tc_fail("ioctl AUDIO_GETBUFINFO failed"); + ATF_REQUIRE((fabs(900.0 - info.play.sample_rate) / 900.0) < 0.2); + ATF_REQUIRE_EQ(rate, (int)info.play.sample_rate); + + /* closest supported rate is 192000 */ + rate = 197000; + if (ioctl(fd, SNDCTL_DSP_SPEED, &rate) < 0) + atf_tc_fail("ioctl SNDCTL_DSP_SPEED (197000) failed"); + if (ioctl(fd, AUDIO_GETBUFINFO, &info) < 0) + atf_tc_fail("ioctl AUDIO_GETBUFINFO failed"); + ATF_REQUIRE((fabs(197000.0 - info.play.sample_rate) / 197000.0) < 0.2); + ATF_REQUIRE_EQ(rate, (int)info.play.sample_rate); + + /* 0 should return the hardware rate. */ + + rate = 0; + if (ioctl(fd, SNDCTL_DSP_SPEED, &rate) < 0) + atf_tc_fail("ioctl SNDCTL_DSP_SPEED (0) failed"); + if (ioctl(fd, AUDIO_GETBUFINFO, &info) < 0) + atf_tc_fail("ioctl AUDIO_GETBUFINFO failed"); + ATF_REQUIRE_EQ(hwinfo.play.sample_rate, info.play.sample_rate); + + close(fd); +} + +ATF_TC(oss_dsp_trigger_read); +ATF_TC_HEAD(oss_dsp_trigger_read, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests SNDCTL_DSP_SETTRIGGER correctly " + "changes the recording pause state"); +} + +ATF_TC_BODY(oss_dsp_trigger_read, tc) +{ + struct audio_info info; + int fd, bits; + +#if defined(__sparc__) + atf_tc_skip("PR port-sparc/55876"); +#endif + + if ((fd = open("/dev/audio", O_RDONLY)) == -1) + atf_tc_skip("Audio device unavailable for recording"); + + /* pause everything ... */ + + bits = 0; + if (ioctl(fd, SNDCTL_DSP_SETTRIGGER, &bits) < 0) + atf_tc_fail("ioctl SNDCTL_DSP_SETTRIGGER (0) failed"); + + if (ioctl(fd, SNDCTL_DSP_GETTRIGGER, &bits) < 0) + atf_tc_fail("ioctl SNDCTL_DSP_GETTRIGGER failed"); + ATF_REQUIRE_EQ(bits, 0); + + if (ioctl(fd, AUDIO_GETBUFINFO, &info) < 0) + atf_tc_fail("ioctl AUDIO_GETBUFINFO failed"); + + ATF_REQUIRE_EQ(info.record.pause, 1); + + /* unpause everything ... */ + + bits = PCM_ENABLE_INPUT; + if (ioctl(fd, SNDCTL_DSP_SETTRIGGER, &bits) < 0) + atf_tc_fail("ioctl SNDCTL_DSP_SETTRIGGER " + "(PCM_ENABLE_INPUT) failed"); + + if (ioctl(fd, SNDCTL_DSP_GETTRIGGER, &bits) < 0) + atf_tc_fail("ioctl SNDCTL_DSP_GETTRIGGER failed"); + ATF_REQUIRE_EQ(bits, PCM_ENABLE_INPUT); + + if (ioctl(fd, AUDIO_GETBUFINFO, &info) < 0) + atf_tc_fail("ioctl AUDIO_GETBUFINFO failed"); + + ATF_REQUIRE_EQ(info.record.pause, 0); + + close(fd); +} + +ATF_TC(oss_dsp_trigger_write); +ATF_TC_HEAD(oss_dsp_trigger_write, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests SNDCTL_DSP_SETTRIGGER correctly " + "changes the playback pause state"); +} + +ATF_TC_BODY(oss_dsp_trigger_write, tc) +{ + struct audio_info info; + int fd, bits; + + if ((fd = open("/dev/audio", O_WRONLY)) == -1) + atf_tc_skip("Audio device unavailable for playback"); + + /* pause everything ... */ + + bits = 0; + if (ioctl(fd, SNDCTL_DSP_SETTRIGGER, &bits) < 0) + atf_tc_fail("ioctl SNDCTL_DSP_SETTRIGGER (0) failed"); + + if (ioctl(fd, SNDCTL_DSP_GETTRIGGER, &bits) < 0) + atf_tc_fail("ioctl SNDCTL_DSP_GETTRIGGER failed"); + ATF_REQUIRE_EQ(bits, 0); + + if (ioctl(fd, AUDIO_GETBUFINFO, &info) < 0) + atf_tc_fail("ioctl AUDIO_GETBUFINFO failed"); + + ATF_REQUIRE_EQ(info.play.pause, 1); + + /* unpause everything ... */ + + bits = PCM_ENABLE_OUTPUT; + if (ioctl(fd, SNDCTL_DSP_SETTRIGGER, &bits) < 0) + atf_tc_fail("ioctl SNDCTL_DSP_SETTRIGGER " + "(PCM_ENABLE_OUTPUT) failed"); + + if (ioctl(fd, SNDCTL_DSP_GETTRIGGER, &bits) < 0) + atf_tc_fail("ioctl SNDCTL_DSP_GETTRIGGER failed"); + ATF_REQUIRE_EQ(bits, PCM_ENABLE_OUTPUT); + + if (ioctl(fd, AUDIO_GETBUFINFO, &info) < 0) + atf_tc_fail("ioctl AUDIO_GETBUFINFO failed"); + + ATF_REQUIRE_EQ(info.play.pause, 0); + + close(fd); +} + +ATF_TC(oss_dsp_caps); +ATF_TC_HEAD(oss_dsp_caps, tc) +{ + atf_tc_set_md_var(tc, "descr", "Verifies that OSS device capabilities " + "are the same as native capabilities"); +} + +ATF_TC_BODY(oss_dsp_caps, tc) +{ + unsigned i; + char dev[16]; + bool dev_tested = false; + int fd; + int caps, props, fmts; + + for (i = 0; i < 16; ++i) { + (void)snprintf(dev, sizeof(dev), "/dev/audio%u", i); + + if ((fd = open(dev, O_WRONLY)) == -1) { + if ((fd = open(dev, O_RDONLY)) == -1) + break; + } + + if (ioctl(fd, SNDCTL_DSP_GETCAPS, &caps) < 0) + atf_tc_fail("ioctl SNDCTL_DSP_GETCAPS failed"); + + if (ioctl(fd, AUDIO_GETPROPS, &props) < 0) + atf_tc_fail("ioctl AUDIO_GETPROPS failed"); + + ATF_REQUIRE(!(caps & DSP_CAP_DUPLEX) == + !(props & AUDIO_PROP_FULLDUPLEX)); + + ATF_REQUIRE(!(caps & DSP_CAP_MMAP) == + !(props & AUDIO_PROP_MMAP)); + + ATF_REQUIRE(!(caps & DSP_CAP_INPUT) == + !(props & AUDIO_PROP_CAPTURE)); + + ATF_REQUIRE(!(caps & DSP_CAP_OUTPUT) == + !(props & AUDIO_PROP_PLAYBACK)); + + /* Trigger is always supported in this implementation. */ + ATF_REQUIRE(caps & DSP_CAP_TRIGGER); + + if (ioctl(fd, SNDCTL_DSP_GETFMTS, &fmts) < 0) + atf_tc_fail("ioctl SNDCTL_DSP_GETFMTS failed"); + + /* All supported by the kernel mixer. */ + ATF_REQUIRE(fmts & AFMT_MU_LAW); + ATF_REQUIRE(fmts & AFMT_A_LAW); + ATF_REQUIRE(fmts & AFMT_S8); + ATF_REQUIRE(fmts & AFMT_U8); + ATF_REQUIRE(fmts & AFMT_S16_LE); + ATF_REQUIRE(fmts & AFMT_S16_BE); + ATF_REQUIRE(fmts & AFMT_U16_LE); + ATF_REQUIRE(fmts & AFMT_U16_BE); + ATF_REQUIRE(fmts & AFMT_S32_LE); + ATF_REQUIRE(fmts & AFMT_S32_BE); + + /* Sanity test... */ + ATF_REQUIRE_EQ(fmts & AFMT_MPEG, 0); + + close(fd); + + dev_tested = true; + } + + if (!dev_tested) + atf_tc_skip("No testable audio device available"); +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, oss_dsp_init); + ATF_TP_ADD_TC(tp, oss_dsp_caps); + ATF_TP_ADD_TC(tp, oss_dsp_trigger_read); + ATF_TP_ADD_TC(tp, oss_dsp_trigger_write); + + return atf_no_error(); +} + diff --git a/lib/libppath/t_ppath.c b/lib/libppath/t_ppath.c --- a/lib/libppath/t_ppath.c +++ b/lib/libppath/t_ppath.c @@ -1,9 +1,9 @@ -/* $Id: t_ppath.c,v 1.1 2011/08/25 19:09:46 dyoung Exp $ */ +/* $Id: t_ppath.c,v 1.2 2020/05/14 08:34:18 msaitoh Exp $ */ /* Copyright (c) 2010 David Young. All rights reserved. */ #include -__RCSID("$Id: t_ppath.c,v 1.1 2011/08/25 19:09:46 dyoung Exp $"); +__RCSID("$Id: t_ppath.c,v 1.2 2020/05/14 08:34:18 msaitoh Exp $"); #include #include @@ -1483,7 +1483,7 @@ ATF_TC_BODY(get_string_success, tc) { int rc; - const char *v = NULL;; + const char *v = NULL; prop_dictionary_t d; ppath_t *p; diff --git a/lib/libprop/Makefile b/lib/libprop/Makefile --- a/lib/libprop/Makefile +++ b/lib/libprop/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.1 2010/07/16 13:56:31 jmmv Exp $ +# $NetBSD: Makefile,v 1.2 2020/06/06 21:26:00 thorpej Exp $ NOMAN= # defined @@ -8,6 +8,6 @@ LDADD+= -lprop -TESTS_C= t_basic +TESTS_C= t_proplib .include diff --git a/lib/libprop/t_basic.c b/lib/libprop/t_basic.c deleted file mode 100644 --- a/lib/libprop/t_basic.c +++ /dev/null @@ -1,203 +0,0 @@ -/* $NetBSD: t_basic.c,v 1.4 2011/04/20 20:02:58 martin Exp $ */ - -/* - * Copyright (c) 2008 The NetBSD Foundation, Inc. - * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. - */ - -/* - * Written by Jason Thorpe 5/26/2006. - * Public domain. - */ - -#include -__COPYRIGHT("@(#) Copyright (c) 2008\ - The NetBSD Foundation, inc. All rights reserved."); -__RCSID("$NetBSD: t_basic.c,v 1.4 2011/04/20 20:02:58 martin Exp $"); - -#include -#include -#include - -#include - -static const char compare1[] = -"\n" -"\n" -"\n" -"\n" -" false-val\n" -" \n" -" one\n" -" 1\n" -" three\n" -" \n" -" \n" -" one\n" -" 1\n" -" two\n" -" number-two\n" -" \n" -" \n" -" one\n" -" 1\n" -" two\n" -" number-two\n" -" \n" -" \n" -" one\n" -" 1\n" -" two\n" -" number-two\n" -" \n" -" \n" -" true-val\n" -" \n" -" two\n" -" number-two\n" -"\n" -"\n"; - -ATF_TC(prop_basic); -ATF_TC_HEAD(prop_basic, tc) -{ - atf_tc_set_md_var(tc, "descr", "A basic test of proplib(3)"); -} - -ATF_TC_BODY(prop_basic, tc) -{ - prop_dictionary_t dict; - char *ext1; - - dict = prop_dictionary_create(); - ATF_REQUIRE(dict != NULL); - - { - prop_number_t num = prop_number_create_integer(1); - ATF_REQUIRE(num != NULL); - - ATF_REQUIRE_EQ(prop_dictionary_set(dict, "one", num), true); - prop_object_release(num); - } - - { - prop_string_t str = prop_string_create_cstring("number-two"); - ATF_REQUIRE(str != NULL); - - ATF_REQUIRE_EQ(prop_dictionary_set(dict, "two", str), true); - prop_object_release(str); - } - - { - prop_array_t arr; - prop_dictionary_t dict_copy; - int i; - - arr = prop_array_create(); - ATF_REQUIRE(arr != NULL); - - for (i = 0; i < 3; ++i) { - dict_copy = prop_dictionary_copy(dict); - ATF_REQUIRE(dict_copy != NULL); - ATF_REQUIRE_EQ(prop_array_add(arr, dict_copy), true); - prop_object_release(dict_copy); - } - - ATF_REQUIRE_EQ(prop_dictionary_set(dict, "three", arr), true); - prop_object_release(arr); - } - - { - prop_bool_t val = prop_bool_create(true); - ATF_REQUIRE(val != NULL); - ATF_REQUIRE_EQ(prop_dictionary_set(dict, "true-val", val), true); - prop_object_release(val); - - val = prop_bool_create(false); - ATF_REQUIRE(val != NULL); - ATF_REQUIRE_EQ(prop_dictionary_set(dict, "false-val", val), true); - prop_object_release(val); - } - - ext1 = prop_dictionary_externalize(dict); - ATF_REQUIRE(ext1 != NULL); - ATF_REQUIRE_STREQ(compare1, ext1); - - { - prop_dictionary_t dict2; - char *ext2; - - dict2 = prop_dictionary_internalize(ext1); - ATF_REQUIRE(dict2 != NULL); - ext2 = prop_dictionary_externalize(dict2); - ATF_REQUIRE(ext2 != NULL); - ATF_REQUIRE_STREQ(ext1, ext2); - prop_object_release(dict2); - free(ext2); - } - - prop_object_release(dict); - free(ext1); -} - -ATF_TC(prop_dictionary_equals); -ATF_TC_HEAD(prop_dictionary_equals, tc) -{ - atf_tc_set_md_var(tc, "descr", "Test prop_dictionary_equals(3)"); -} - -ATF_TC_BODY(prop_dictionary_equals, tc) -{ - prop_dictionary_t c, d; - - /* - * Fixed, should not fail any more... - * - atf_tc_expect_death("PR lib/43964"); - * - */ - - d = prop_dictionary_internalize(compare1); - - ATF_REQUIRE(d != NULL); - - c = prop_dictionary_copy(d); - - ATF_REQUIRE(c != NULL); - - if (prop_dictionary_equals(c, d) != true) - atf_tc_fail("dictionaries are not equal"); - - prop_object_release(c); - prop_object_release(d); -} - -ATF_TP_ADD_TCS(tp) -{ - - ATF_TP_ADD_TC(tp, prop_basic); - ATF_TP_ADD_TC(tp, prop_dictionary_equals); - - return atf_no_error(); -} diff --git a/lib/libprop/t_proplib.c b/lib/libprop/t_proplib.c new file mode 100644 --- /dev/null +++ b/lib/libprop/t_proplib.c @@ -0,0 +1,917 @@ +/* $NetBSD: t_proplib.c,v 1.4 2020/06/24 14:28:10 thorpej Exp $ */ + +/* + * Copyright (c) 2008, 2020 The NetBSD Foundation, Inc. + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +/* + * Written by Jason Thorpe 5/26/2006. + * Public domain. + */ + +#include +__COPYRIGHT("@(#) Copyright (c) 2008, 2020\ + The NetBSD Foundation, inc. All rights reserved."); +__RCSID("$NetBSD: t_proplib.c,v 1.4 2020/06/24 14:28:10 thorpej Exp $"); + +#include +#include +#include +#include + +#include + +static const char compare1[] = +"\n" +"\n" +"\n" +"\n" +" false-val\n" +" \n" +" one\n" +" 1\n" +" three\n" +" \n" +" \n" +" one\n" +" 1\n" +" two\n" +" number-two\n" +" \n" +" \n" +" one\n" +" 1\n" +" two\n" +" number-two\n" +" \n" +" \n" +" one\n" +" 1\n" +" two\n" +" number-two\n" +" \n" +" \n" +" true-val\n" +" \n" +" two\n" +" number-two\n" +"\n" +"\n"; + +static const char const_data1[] = { + 0xde, 0xad, 0xbe, 0xef +}; + +static const char const_data2[] = { + 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55 +}; + +static const char const_string1[] = + "The quick brown fox jumps over the lazy dog."; + +static const char const_string2[] = + "Lorem ipsum dolor sit amet, consectetur adipiscing elit, " + "sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."; + +ATF_TC(prop_basic); +ATF_TC_HEAD(prop_basic, tc) +{ + atf_tc_set_md_var(tc, "descr", "A basic test of proplib(3)"); +} + +ATF_TC_BODY(prop_basic, tc) +{ + prop_dictionary_t dict; + char *ext1; + + dict = prop_dictionary_create(); + ATF_REQUIRE(dict != NULL); + + { + prop_number_t num = prop_number_create_signed(1); + ATF_REQUIRE(num != NULL); + + ATF_REQUIRE_EQ(prop_dictionary_set(dict, "one", num), true); + prop_object_release(num); + } + + { + prop_string_t str = prop_string_create_copy("number-two"); + ATF_REQUIRE(str != NULL); + + ATF_REQUIRE_EQ(prop_dictionary_set(dict, "two", str), true); + prop_object_release(str); + } + + { + prop_array_t arr; + prop_dictionary_t dict_copy; + int i; + + arr = prop_array_create(); + ATF_REQUIRE(arr != NULL); + + for (i = 0; i < 3; ++i) { + dict_copy = prop_dictionary_copy(dict); + ATF_REQUIRE(dict_copy != NULL); + ATF_REQUIRE_EQ(prop_array_add(arr, dict_copy), true); + prop_object_release(dict_copy); + } + + ATF_REQUIRE_EQ(prop_dictionary_set(dict, "three", arr), true); + prop_object_release(arr); + } + + { + prop_bool_t val = prop_bool_create(true); + ATF_REQUIRE(val != NULL); + ATF_REQUIRE_EQ(prop_dictionary_set(dict, "true-val", val), true); + prop_object_release(val); + + val = prop_bool_create(false); + ATF_REQUIRE(val != NULL); + ATF_REQUIRE_EQ(prop_dictionary_set(dict, "false-val", val), true); + prop_object_release(val); + } + + ext1 = prop_dictionary_externalize(dict); + ATF_REQUIRE(ext1 != NULL); + ATF_REQUIRE_STREQ(compare1, ext1); + + { + prop_dictionary_t dict2; + char *ext2; + + dict2 = prop_dictionary_internalize(ext1); + ATF_REQUIRE(dict2 != NULL); + ext2 = prop_dictionary_externalize(dict2); + ATF_REQUIRE(ext2 != NULL); + ATF_REQUIRE_STREQ(ext1, ext2); + prop_object_release(dict2); + free(ext2); + } + + prop_object_release(dict); + free(ext1); +} + +ATF_TC(prop_dictionary_equals); +ATF_TC_HEAD(prop_dictionary_equals, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test prop_dictionary_equals(3)"); +} + +ATF_TC_BODY(prop_dictionary_equals, tc) +{ + prop_dictionary_t c, d; + + /* + * Fixed, should not fail any more... + * + atf_tc_expect_death("PR lib/43964"); + * + */ + + d = prop_dictionary_internalize(compare1); + + ATF_REQUIRE(d != NULL); + + c = prop_dictionary_copy(d); + + ATF_REQUIRE(c != NULL); + + if (prop_dictionary_equals(c, d) != true) + atf_tc_fail("dictionaries are not equal"); + + prop_object_release(c); + prop_object_release(d); +} + +ATF_TC(prop_data_basic); +ATF_TC_HEAD(prop_data_basic, tc) +{ + atf_tc_set_md_var(tc, "descr", "tests prop_data basics"); +} +ATF_TC_BODY(prop_data_basic, tc) +{ + prop_data_t d1, d2; + char buf[sizeof(const_data1)]; + + /* + * This test exercises implementation details, not only + * API contract. + */ + + d1 = prop_data_create_copy(const_data1, 0); + ATF_REQUIRE(d1 != NULL); + ATF_REQUIRE(prop_data_value(d1) == NULL); + prop_object_release(d1); + + d1 = prop_data_create_copy(NULL, sizeof(const_data1)); + ATF_REQUIRE(d1 != NULL); + ATF_REQUIRE(prop_data_value(d1) == NULL); + prop_object_release(d1); + + d1 = prop_data_create_nocopy(const_data1, 0); + ATF_REQUIRE(d1 != NULL); + ATF_REQUIRE(prop_data_value(d1) == NULL); + prop_object_release(d1); + + d1 = prop_data_create_nocopy(NULL, sizeof(const_data1)); + ATF_REQUIRE(d1 != NULL); + ATF_REQUIRE(prop_data_value(d1) == NULL); + prop_object_release(d1); + + d1 = prop_data_create_nocopy(const_data1, sizeof(const_data1)); + ATF_REQUIRE(d1 != NULL); + ATF_REQUIRE(prop_data_value(d1) == const_data1); + d2 = prop_data_copy(d1); + ATF_REQUIRE(d2 != NULL); + ATF_REQUIRE(d2 == d1); + prop_object_release(d1); + prop_object_release(d2); + + d1 = prop_data_create_copy(const_data1, sizeof(const_data1)); + ATF_REQUIRE(d1 != NULL); + ATF_REQUIRE(prop_data_value(d1) != const_data1); + d2 = prop_data_copy(d1); + ATF_REQUIRE(d2 != NULL); + ATF_REQUIRE(d2 == d1); + ATF_REQUIRE(prop_data_equals(d1, d2)); + prop_object_release(d2); + + d2 = prop_data_create_copy(const_data2, sizeof(const_data2)); + ATF_REQUIRE(d2 != NULL); + ATF_REQUIRE(prop_data_value(d2) != const_data2); + ATF_REQUIRE(!prop_data_equals(d1, d2)); + + ATF_REQUIRE(prop_data_size(d1) == sizeof(const_data1)); + ATF_REQUIRE(prop_data_size(d2) == sizeof(const_data2)); + + ATF_REQUIRE(prop_data_copy_value(d1, buf, sizeof(buf))); + ATF_REQUIRE(memcmp(buf, const_data1, sizeof(buf)) == 0); + ATF_REQUIRE(!prop_data_copy_value(d2, buf, sizeof(buf))); + + prop_object_release(d1); + prop_object_release(d2); +} + +ATF_TC(prop_number_basic); +ATF_TC_HEAD(prop_number_basic, tc) +{ + atf_tc_set_md_var(tc, "descr", "tests prop_number basics"); +} +ATF_TC_BODY(prop_number_basic, tc) +{ + prop_number_t s1, s2, u1, u2, u3, u4; + + /* + * This test exercises implementation details, not only + * API contract. + */ + + s1 = prop_number_create_signed(INTMAX_MAX); + ATF_REQUIRE(s1 != NULL); + ATF_REQUIRE(prop_number_unsigned(s1) == false); + ATF_REQUIRE(prop_number_signed_value(s1) == INTMAX_MAX); + ATF_REQUIRE(prop_number_unsigned_value(s1) == INTMAX_MAX); + ATF_REQUIRE(prop_number_equals_signed(s1, INTMAX_MAX) == true); + + s2 = prop_number_create_signed(INTMAX_MAX); + ATF_REQUIRE(s2 == s1); + ATF_REQUIRE(prop_number_unsigned(s2) == false); + + u1 = prop_number_create_unsigned(UINTMAX_MAX); + ATF_REQUIRE(u1 != NULL); + ATF_REQUIRE(prop_number_unsigned(u1) == true); + ATF_REQUIRE(prop_number_unsigned_value(u1) == UINTMAX_MAX); + ATF_REQUIRE(prop_number_equals_unsigned(u1, UINTMAX_MAX) == true); + + u2 = prop_number_create_unsigned(0); + ATF_REQUIRE(u2 != NULL); + ATF_REQUIRE(u2 != u1); + ATF_REQUIRE(prop_number_unsigned(u2) == true); + ATF_REQUIRE(prop_number_unsigned_value(u2) == 0); + + u3 = prop_number_copy(u1); + ATF_REQUIRE(u3 == u1); + ATF_REQUIRE(prop_number_unsigned(u3) == true); + ATF_REQUIRE(prop_number_unsigned_value(u3) == UINTMAX_MAX); + + u4 = prop_number_create_unsigned(INTMAX_MAX); + ATF_REQUIRE(u4 != NULL); + ATF_REQUIRE(u4 != s1); + ATF_REQUIRE(prop_number_equals_signed(u4, INTMAX_MAX) == true); + ATF_REQUIRE(prop_number_equals_unsigned(u4, INTMAX_MAX) == true); + + prop_object_release(s1); + prop_object_release(s2); + + prop_object_release(u1); + prop_object_release(u2); + prop_object_release(u3); + prop_object_release(u4); +} + +ATF_TC(prop_number_range_check); +ATF_TC_HEAD(prop_number_range_check, tc) +{ + atf_tc_set_md_var(tc, "descr", "tests prop_number range checking"); +} +ATF_TC_BODY(prop_number_range_check, tc) +{ + union { + signed char v_schar; + short v_shrt; + int v_int; + long v_long; + long long v_llong; + intptr_t v_intptr; + int8_t v_int8; + int16_t v_int16; + int32_t v_int32; + int64_t v_int64; + + unsigned char v_uchar; + unsigned short v_ushrt; + unsigned int v_uint; + unsigned long v_ulong; + unsigned long long v_ullong; + uintptr_t v_uintptr; + uint8_t v_uint8; + uint16_t v_uint16; + uint32_t v_uint32; + uint64_t v_uint64; + } val; + + prop_number_t n_schar_max = prop_number_create_signed(SCHAR_MAX); + prop_number_t n_schar_min = prop_number_create_signed(SCHAR_MIN); + prop_number_t n_uchar_max = prop_number_create_unsigned(UCHAR_MAX); + ATF_REQUIRE(n_schar_max != NULL); + ATF_REQUIRE(n_schar_min != NULL); + ATF_REQUIRE(n_uchar_max != NULL); + + prop_number_t n_shrt_max = prop_number_create_signed(SHRT_MAX); + prop_number_t n_shrt_min = prop_number_create_signed(SHRT_MIN); + prop_number_t n_ushrt_max = prop_number_create_unsigned(USHRT_MAX); + ATF_REQUIRE(n_shrt_max != NULL); + ATF_REQUIRE(n_shrt_min != NULL); + ATF_REQUIRE(n_ushrt_max != NULL); + + prop_number_t n_int_max = prop_number_create_signed(INT_MAX); + prop_number_t n_int_min = prop_number_create_signed(INT_MIN); + prop_number_t n_uint_max = prop_number_create_unsigned(UINT_MAX); + ATF_REQUIRE(n_int_max != NULL); + ATF_REQUIRE(n_int_min != NULL); + ATF_REQUIRE(n_uint_max != NULL); + + prop_number_t n_long_max = prop_number_create_signed(LONG_MAX); + prop_number_t n_long_min = prop_number_create_signed(LONG_MIN); + prop_number_t n_ulong_max = prop_number_create_unsigned(ULONG_MAX); + ATF_REQUIRE(n_long_max != NULL); + ATF_REQUIRE(n_long_min != NULL); + ATF_REQUIRE(n_ulong_max != NULL); + + prop_number_t n_llong_max = prop_number_create_signed(LLONG_MAX); + prop_number_t n_llong_min = prop_number_create_signed(LLONG_MIN); + prop_number_t n_ullong_max = prop_number_create_unsigned(ULLONG_MAX); + ATF_REQUIRE(n_llong_max != NULL); + ATF_REQUIRE(n_llong_min != NULL); + ATF_REQUIRE(n_ullong_max != NULL); + + prop_number_t n_intptr_max = prop_number_create_signed(INTPTR_MAX); + prop_number_t n_intptr_min = prop_number_create_signed(INTPTR_MIN); + prop_number_t n_uintptr_max = prop_number_create_unsigned(UINTPTR_MAX); + ATF_REQUIRE(n_intptr_max != NULL); + ATF_REQUIRE(n_intptr_min != NULL); + ATF_REQUIRE(n_uintptr_max != NULL); + + prop_number_t n_int8_max = prop_number_create_signed(INT8_MAX); + prop_number_t n_int8_min = prop_number_create_signed(INT8_MIN); + prop_number_t n_uint8_max = prop_number_create_unsigned(UINT8_MAX); + ATF_REQUIRE(n_int8_max != NULL); + ATF_REQUIRE(n_int8_min != NULL); + ATF_REQUIRE(n_uint8_max != NULL); + + prop_number_t n_int16_max = prop_number_create_signed(INT16_MAX); + prop_number_t n_int16_min = prop_number_create_signed(INT16_MIN); + prop_number_t n_uint16_max = prop_number_create_unsigned(UINT16_MAX); + ATF_REQUIRE(n_int16_max != NULL); + ATF_REQUIRE(n_int16_min != NULL); + ATF_REQUIRE(n_uint16_max != NULL); + + prop_number_t n_int32_max = prop_number_create_signed(INT32_MAX); + prop_number_t n_int32_min = prop_number_create_signed(INT32_MIN); + prop_number_t n_uint32_max = prop_number_create_unsigned(UINT32_MAX); + ATF_REQUIRE(n_int32_max != NULL); + ATF_REQUIRE(n_int32_min != NULL); + ATF_REQUIRE(n_uint32_max != NULL); + + prop_number_t n_int64_max = prop_number_create_signed(INT64_MAX); + prop_number_t n_int64_min = prop_number_create_signed(INT64_MIN); + prop_number_t n_uint64_max = prop_number_create_unsigned(UINT64_MAX); + ATF_REQUIRE(n_int64_max != NULL); + ATF_REQUIRE(n_int64_min != NULL); + ATF_REQUIRE(n_uint64_max != NULL); + + /* signed / unsigned char */ + ATF_REQUIRE(prop_number_schar_value(n_schar_max, &val.v_schar) && + val.v_schar == SCHAR_MAX); + ATF_REQUIRE(prop_number_schar_value(n_schar_min, &val.v_schar) && + val.v_schar == SCHAR_MIN); + ATF_REQUIRE(!prop_number_schar_value(n_uchar_max, &val.v_schar)); + + ATF_REQUIRE(prop_number_uchar_value(n_schar_max, &val.v_uchar) && + val.v_uchar == SCHAR_MAX); + ATF_REQUIRE(!prop_number_uchar_value(n_schar_min, &val.v_uchar)); + ATF_REQUIRE(prop_number_uchar_value(n_uchar_max, &val.v_uchar) && + val.v_uchar == UCHAR_MAX); + + ATF_REQUIRE(!prop_number_schar_value(n_shrt_min, &val.v_schar)); + ATF_REQUIRE(!prop_number_uchar_value(n_shrt_max, &val.v_uchar)); + + /* short / unsigned short */ + ATF_REQUIRE(prop_number_short_value(n_uchar_max, &val.v_shrt) && + val.v_shrt == UCHAR_MAX); + + ATF_REQUIRE(prop_number_short_value(n_shrt_max, &val.v_shrt) && + val.v_shrt == SHRT_MAX); + ATF_REQUIRE(prop_number_short_value(n_shrt_min, &val.v_shrt) && + val.v_shrt == SHRT_MIN); + ATF_REQUIRE(!prop_number_short_value(n_ushrt_max, &val.v_shrt)); + + ATF_REQUIRE(prop_number_ushort_value(n_shrt_max, &val.v_ushrt) && + val.v_ushrt == SHRT_MAX); + ATF_REQUIRE(!prop_number_ushort_value(n_shrt_min, &val.v_ushrt)); + ATF_REQUIRE(prop_number_ushort_value(n_ushrt_max, &val.v_ushrt) && + val.v_ushrt == USHRT_MAX); + + ATF_REQUIRE(!prop_number_short_value(n_int_min, &val.v_shrt)); + ATF_REQUIRE(!prop_number_ushort_value(n_int_max, &val.v_ushrt)); + + /* int / unsigned int */ + ATF_REQUIRE(prop_number_int_value(n_ushrt_max, &val.v_int) && + val.v_int == USHRT_MAX); + + ATF_REQUIRE(prop_number_int_value(n_int_max, &val.v_int) && + val.v_int == INT_MAX); + ATF_REQUIRE(prop_number_int_value(n_int_min, &val.v_int) && + val.v_int == INT_MIN); + ATF_REQUIRE(!prop_number_int_value(n_uint_max, &val.v_int)); + + ATF_REQUIRE(prop_number_uint_value(n_int_max, &val.v_uint) && + val.v_uint == INT_MAX); + ATF_REQUIRE(!prop_number_uint_value(n_int_min, &val.v_uint)); + ATF_REQUIRE(prop_number_uint_value(n_uint_max, &val.v_uint) && + val.v_uint == UINT_MAX); + +#ifdef _LP64 + ATF_REQUIRE(!prop_number_int_value(n_long_min, &val.v_int)); + ATF_REQUIRE(!prop_number_uint_value(n_long_max, &val.v_uint)); +#else + ATF_REQUIRE(!prop_number_int_value(n_llong_min, &val.v_int)); + ATF_REQUIRE(!prop_number_uint_value(n_llong_max, &val.v_uint)); +#endif /* _LP64 */ + + /* long / unsigned long */ +#ifdef _LP64 + ATF_REQUIRE(prop_number_long_value(n_uint_max, &val.v_long) && + val.v_long == UINT_MAX); +#endif + + ATF_REQUIRE(prop_number_long_value(n_long_max, &val.v_long) && + val.v_long == LONG_MAX); + ATF_REQUIRE(prop_number_long_value(n_long_min, &val.v_long) && + val.v_long == LONG_MIN); + ATF_REQUIRE(!prop_number_long_value(n_ulong_max, &val.v_long)); + + ATF_REQUIRE(prop_number_ulong_value(n_long_max, &val.v_ulong) && + val.v_ulong == LONG_MAX); + ATF_REQUIRE(!prop_number_ulong_value(n_long_min, &val.v_ulong)); + ATF_REQUIRE(prop_number_ulong_value(n_ulong_max, &val.v_ulong) && + val.v_ulong == ULONG_MAX); + +#ifndef _LP64 + ATF_REQUIRE(!prop_number_long_value(n_llong_min, &val.v_long)); + ATF_REQUIRE(!prop_number_ulong_value(n_llong_max, &val.v_ulong)); +#endif + + /* intptr_t / uintptr_t */ +#ifdef _LP64 + ATF_REQUIRE(prop_number_intptr_value(n_uint_max, &val.v_intptr) && + val.v_intptr == UINT_MAX); +#endif + + ATF_REQUIRE(prop_number_intptr_value(n_intptr_max, &val.v_intptr) && + val.v_intptr == INTPTR_MAX); + ATF_REQUIRE(prop_number_intptr_value(n_intptr_min, &val.v_intptr) && + val.v_intptr == INTPTR_MIN); + ATF_REQUIRE(!prop_number_intptr_value(n_uintptr_max, &val.v_intptr)); + + ATF_REQUIRE(prop_number_uintptr_value(n_intptr_max, &val.v_uintptr) && + val.v_uintptr == INTPTR_MAX); + ATF_REQUIRE(!prop_number_uintptr_value(n_intptr_min, &val.v_uintptr)); + ATF_REQUIRE(prop_number_uintptr_value(n_uintptr_max, &val.v_uintptr) && + val.v_uintptr == UINTPTR_MAX); + +#ifndef _LP64 + ATF_REQUIRE(!prop_number_intptr_value(n_llong_min, &val.v_intptr)); + ATF_REQUIRE(!prop_number_uintptr_value(n_llong_max, &val.v_uintptr)); +#endif + + /* long long / unsigned long long */ +#ifdef _LP64 + ATF_REQUIRE(prop_number_longlong_value(n_uint_max, &val.v_llong) && + val.v_llong == UINT_MAX); +#else + ATF_REQUIRE(prop_number_longlong_value(n_ulong_max, &val.v_llong) && + val.v_llong == ULONG_MAX); +#endif + + ATF_REQUIRE(prop_number_longlong_value(n_llong_max, &val.v_llong) && + val.v_llong == LLONG_MAX); + ATF_REQUIRE(prop_number_longlong_value(n_llong_min, &val.v_llong) && + val.v_llong == LLONG_MIN); + ATF_REQUIRE(!prop_number_longlong_value(n_ullong_max, &val.v_llong)); + + ATF_REQUIRE(prop_number_ulonglong_value(n_llong_max, &val.v_ullong) && + val.v_ullong == LLONG_MAX); + ATF_REQUIRE(!prop_number_ulonglong_value(n_llong_min, &val.v_ullong)); + ATF_REQUIRE(prop_number_ulonglong_value(n_ullong_max, &val.v_ullong) && + val.v_ullong == ULLONG_MAX); + + /* int8_t / uint8_t */ + ATF_REQUIRE(prop_number_int8_value(n_int8_max, &val.v_int8) && + val.v_int8 == INT8_MAX); + ATF_REQUIRE(prop_number_int8_value(n_int8_min, &val.v_int8) && + val.v_int8 == INT8_MIN); + ATF_REQUIRE(!prop_number_int8_value(n_uint8_max, &val.v_int8)); + + ATF_REQUIRE(prop_number_uint8_value(n_int8_max, &val.v_uint8) && + val.v_uint8 == INT8_MAX); + ATF_REQUIRE(!prop_number_uint8_value(n_int8_min, &val.v_uint8)); + ATF_REQUIRE(prop_number_uint8_value(n_uint8_max, &val.v_uint8) && + val.v_uint8 == UINT8_MAX); + + ATF_REQUIRE(!prop_number_int8_value(n_int16_min, &val.v_int8)); + ATF_REQUIRE(!prop_number_uint8_value(n_int16_max, &val.v_uint8)); + + /* int16_t / uint16_t */ + ATF_REQUIRE(prop_number_int16_value(n_uint8_max, &val.v_int16) && + val.v_int16 == UINT8_MAX); + + ATF_REQUIRE(prop_number_int16_value(n_int16_max, &val.v_int16) && + val.v_int16 == INT16_MAX); + ATF_REQUIRE(prop_number_int16_value(n_int16_min, &val.v_int16) && + val.v_int16 == INT16_MIN); + ATF_REQUIRE(!prop_number_int16_value(n_uint16_max, &val.v_int16)); + + ATF_REQUIRE(prop_number_uint16_value(n_int16_max, &val.v_uint16) && + val.v_uint16 == INT16_MAX); + ATF_REQUIRE(!prop_number_uint16_value(n_int16_min, &val.v_uint16)); + ATF_REQUIRE(prop_number_uint16_value(n_uint16_max, &val.v_uint16) && + val.v_uint16 == UINT16_MAX); + + ATF_REQUIRE(!prop_number_int16_value(n_int32_min, &val.v_int16)); + ATF_REQUIRE(!prop_number_uint16_value(n_int32_max, &val.v_uint16)); + + /* int32_t / uint32_t */ + ATF_REQUIRE(prop_number_int32_value(n_uint16_max, &val.v_int32) && + val.v_int32 == UINT16_MAX); + + ATF_REQUIRE(prop_number_int32_value(n_int32_max, &val.v_int32) && + val.v_int32 == INT32_MAX); + ATF_REQUIRE(prop_number_int32_value(n_int32_min, &val.v_int32) && + val.v_int32 == INT32_MIN); + ATF_REQUIRE(!prop_number_int32_value(n_uint32_max, &val.v_int32)); + + ATF_REQUIRE(prop_number_uint32_value(n_int32_max, &val.v_uint32) && + val.v_uint32 == INT32_MAX); + ATF_REQUIRE(!prop_number_uint32_value(n_int32_min, &val.v_uint32)); + ATF_REQUIRE(prop_number_uint32_value(n_uint32_max, &val.v_uint32) && + val.v_uint32 == UINT32_MAX); + + ATF_REQUIRE(!prop_number_int32_value(n_int64_min, &val.v_int32)); + ATF_REQUIRE(!prop_number_uint32_value(n_int64_max, &val.v_uint32)); + + /* int64_t / uint64_t */ + ATF_REQUIRE(prop_number_int64_value(n_uint32_max, &val.v_int64) && + val.v_int64 == UINT32_MAX); + + ATF_REQUIRE(prop_number_int64_value(n_int64_max, &val.v_int64) && + val.v_int64 == INT64_MAX); + ATF_REQUIRE(prop_number_int64_value(n_int64_min, &val.v_int64) && + val.v_int64 == INT64_MIN); + ATF_REQUIRE(!prop_number_int64_value(n_uint64_max, &val.v_int64)); + + ATF_REQUIRE(prop_number_uint64_value(n_int64_max, &val.v_uint64) && + val.v_uint64 == INT64_MAX); + ATF_REQUIRE(!prop_number_uint64_value(n_int64_min, &val.v_uint64)); + ATF_REQUIRE(prop_number_uint64_value(n_uint64_max, &val.v_uint64) && + val.v_uint64 == UINT64_MAX); + + prop_object_release(n_schar_max); + prop_object_release(n_schar_min); + prop_object_release(n_uchar_max); + + prop_object_release(n_shrt_max); + prop_object_release(n_shrt_min); + prop_object_release(n_ushrt_max); + + prop_object_release(n_int_max); + prop_object_release(n_int_min); + prop_object_release(n_uint_max); + + prop_object_release(n_long_max); + prop_object_release(n_long_min); + prop_object_release(n_ulong_max); + + prop_object_release(n_llong_max); + prop_object_release(n_llong_min); + prop_object_release(n_ullong_max); + + prop_object_release(n_intptr_max); + prop_object_release(n_intptr_min); + prop_object_release(n_uintptr_max); + + prop_object_release(n_int8_max); + prop_object_release(n_int8_min); + prop_object_release(n_uint8_max); + + prop_object_release(n_int16_max); + prop_object_release(n_int16_min); + prop_object_release(n_uint16_max); + + prop_object_release(n_int32_max); + prop_object_release(n_int32_min); + prop_object_release(n_uint32_max); + + prop_object_release(n_int64_max); + prop_object_release(n_int64_min); + prop_object_release(n_uint64_max); +} + +ATF_TC(prop_string_basic); +ATF_TC_HEAD(prop_string_basic, tc) +{ + atf_tc_set_md_var(tc, "descr", "tests prop_string basics"); +} +ATF_TC_BODY(prop_string_basic, tc) +{ + prop_string_t s1, s2, s3; + prop_number_t num; + char buf[sizeof(const_string1)]; + + /* + * This test exercises implementation details, not only + * API contract. + */ + + s1 = prop_string_create_nocopy(const_string1); + ATF_REQUIRE(s1 != NULL); + s2 = prop_string_create_copy(const_string1); + ATF_REQUIRE(s2 != NULL); + ATF_REQUIRE(s2 == s1); + ATF_REQUIRE(prop_string_value(s1) == const_string1); + prop_object_release(s1); + prop_object_release(s2); + + s1 = prop_string_create_copy(const_string1); + ATF_REQUIRE(s1 != NULL); + s2 = prop_string_create_nocopy(const_string1); + ATF_REQUIRE(s2 != NULL); + ATF_REQUIRE(s2 == s1); + ATF_REQUIRE(prop_string_value(s1) != const_string1); + prop_object_release(s1); + prop_object_release(s2); + + s1 = prop_string_create_format("%d-%d", 12345, 67890); + ATF_REQUIRE(s1 != NULL); + ATF_REQUIRE(strcmp(prop_string_value(s1), "12345-67890") == 0); + ATF_REQUIRE(prop_string_equals_string(s1, "12345-67890")); + prop_object_release(s1); + + s1 = prop_string_create_nocopy(const_string1); + ATF_REQUIRE(s1 != NULL); + s2 = prop_string_create_nocopy(const_string2); + ATF_REQUIRE(s2 != NULL); + ATF_REQUIRE(prop_string_size(s1) == strlen(const_string1)); + ATF_REQUIRE(prop_string_size(s2) == strlen(const_string2)); + ATF_REQUIRE(prop_string_copy_value(s1, buf, sizeof(buf))); + ATF_REQUIRE(!prop_string_copy_value(s2, buf, sizeof(buf))); + prop_object_release(s1); + prop_object_release(s2); + + s1 = prop_string_create_copy("a"); + ATF_REQUIRE(s1 != NULL); + s2 = prop_string_create_copy("b"); + ATF_REQUIRE(s2 != NULL); + s3 = prop_string_copy(s2); + ATF_REQUIRE(s3 != NULL); + ATF_REQUIRE(s3 == s2); + num = prop_number_create_signed(666); + ATF_REQUIRE(num != NULL); + ATF_REQUIRE(!prop_string_equals(s1, s2)); + ATF_REQUIRE(prop_string_equals(s2, s3)); + ATF_REQUIRE(prop_string_compare(s1, s2) < 0); + ATF_REQUIRE(prop_string_compare(s2, s1) > 0); + ATF_REQUIRE(prop_string_compare(s2, s3) == 0); + ATF_REQUIRE(prop_string_compare_string(s1, "b") < 0); + ATF_REQUIRE(prop_string_compare_string(s2, "a") > 0); + ATF_REQUIRE(prop_string_compare_string(s3, "b") == 0); + ATF_REQUIRE(prop_string_compare(s1, (prop_string_t)num) != 0); + ATF_REQUIRE(prop_string_compare((prop_string_t)num, s1) != 0); + ATF_REQUIRE(prop_string_compare_string((prop_string_t)num, "666") != 0); + prop_object_release(s1); + prop_object_release(s2); + prop_object_release(s3); + prop_object_release(num); +} + +ATF_TC(prop_dict_util); +ATF_TC_HEAD(prop_dict_util, tc) +{ + atf_tc_set_md_var(tc, "descr", "tests prop_dictionary_util basics"); +} +ATF_TC_BODY(prop_dict_util, tc) +{ + union { + signed char v_schar; + short v_shrt; + int v_int; + long v_long; + long long v_llong; + intptr_t v_intptr; + int8_t v_int8; + int16_t v_int16; + int32_t v_int32; + int64_t v_int64; + + unsigned char v_uchar; + unsigned short v_ushrt; + unsigned int v_uint; + unsigned long v_ulong; + unsigned long long v_ullong; + uintptr_t v_uintptr; + uint8_t v_uint8; + uint16_t v_uint16; + uint32_t v_uint32; + uint64_t v_uint64; + } val; + prop_dictionary_t dict; + const char *cp; + const void *v; + size_t size; + + dict = prop_dictionary_create(); + ATF_REQUIRE(dict != NULL); + + ATF_REQUIRE(prop_dictionary_set_schar(dict, "schar", SCHAR_MIN)); + ATF_REQUIRE(prop_dictionary_get_schar(dict, "schar", &val.v_schar)); + ATF_REQUIRE(val.v_schar == SCHAR_MIN); + + ATF_REQUIRE(prop_dictionary_set_short(dict, "shrt", SHRT_MIN)); + ATF_REQUIRE(prop_dictionary_get_short(dict, "shrt", &val.v_shrt)); + ATF_REQUIRE(val.v_shrt == SHRT_MIN); + + ATF_REQUIRE(prop_dictionary_set_int(dict, "int", INT_MIN)); + ATF_REQUIRE(prop_dictionary_get_int(dict, "int", &val.v_int)); + ATF_REQUIRE(val.v_int == INT_MIN); + + ATF_REQUIRE(prop_dictionary_set_long(dict, "long", LONG_MIN)); + ATF_REQUIRE(prop_dictionary_get_long(dict, "long", &val.v_long)); + ATF_REQUIRE(val.v_long == LONG_MIN); + + ATF_REQUIRE(prop_dictionary_set_longlong(dict, "longlong", LLONG_MIN)); + ATF_REQUIRE(prop_dictionary_get_longlong(dict, "longlong", + &val.v_llong)); + ATF_REQUIRE(val.v_llong == LLONG_MIN); + + ATF_REQUIRE(prop_dictionary_set_intptr(dict, "intptr", INTPTR_MIN)); + ATF_REQUIRE(prop_dictionary_get_intptr(dict, "intptr", &val.v_intptr)); + ATF_REQUIRE(val.v_intptr == INTPTR_MIN); + + ATF_REQUIRE(prop_dictionary_set_int8(dict, "int8", INT8_MIN)); + ATF_REQUIRE(prop_dictionary_get_int8(dict, "int8", &val.v_int8)); + ATF_REQUIRE(val.v_int8 == INT8_MIN); + + ATF_REQUIRE(prop_dictionary_set_int16(dict, "int16", INT16_MIN)); + ATF_REQUIRE(prop_dictionary_get_int16(dict, "int16", &val.v_int16)); + ATF_REQUIRE(val.v_int16 == INT16_MIN); + + ATF_REQUIRE(prop_dictionary_set_int32(dict, "int32", INT32_MIN)); + ATF_REQUIRE(prop_dictionary_get_int32(dict, "int32", &val.v_int32)); + ATF_REQUIRE(val.v_int32 == INT32_MIN); + + ATF_REQUIRE(prop_dictionary_set_int64(dict, "int64", INT64_MIN)); + ATF_REQUIRE(prop_dictionary_get_int64(dict, "int64", &val.v_int64)); + ATF_REQUIRE(val.v_int64 == INT64_MIN); + + + ATF_REQUIRE(prop_dictionary_set_uchar(dict, "uchar", UCHAR_MAX)); + ATF_REQUIRE(prop_dictionary_get_uchar(dict, "uchar", &val.v_uchar)); + ATF_REQUIRE(val.v_uchar == UCHAR_MAX); + + ATF_REQUIRE(prop_dictionary_set_ushort(dict, "ushrt", USHRT_MAX)); + ATF_REQUIRE(prop_dictionary_get_ushort(dict, "ushrt", &val.v_ushrt)); + ATF_REQUIRE(val.v_ushrt == USHRT_MAX); + + ATF_REQUIRE(prop_dictionary_set_uint(dict, "uint", UINT_MAX)); + ATF_REQUIRE(prop_dictionary_get_uint(dict, "uint", &val.v_uint)); + ATF_REQUIRE(val.v_uint == UINT_MAX); + + ATF_REQUIRE(prop_dictionary_set_ulong(dict, "ulong", ULONG_MAX)); + ATF_REQUIRE(prop_dictionary_get_ulong(dict, "ulong", &val.v_ulong)); + ATF_REQUIRE(val.v_ulong == ULONG_MAX); + + ATF_REQUIRE(prop_dictionary_set_ulonglong(dict, "ulonglong", + ULLONG_MAX)); + ATF_REQUIRE(prop_dictionary_get_ulonglong(dict, "ulonglong", + &val.v_ullong)); + ATF_REQUIRE(val.v_ullong == ULLONG_MAX); + + ATF_REQUIRE(prop_dictionary_set_uintptr(dict, "uintptr", UINTPTR_MAX)); + ATF_REQUIRE(prop_dictionary_get_uintptr(dict, "uintptr", + &val.v_uintptr)); + ATF_REQUIRE(val.v_uintptr == UINTPTR_MAX); + + ATF_REQUIRE(prop_dictionary_set_uint8(dict, "uint8", UINT8_MAX)); + ATF_REQUIRE(prop_dictionary_get_uint8(dict, "uint8", &val.v_uint8)); + ATF_REQUIRE(val.v_uint8 == UINT8_MAX); + + ATF_REQUIRE(prop_dictionary_set_uint16(dict, "uint16", UINT16_MAX)); + ATF_REQUIRE(prop_dictionary_get_uint16(dict, "uint16", &val.v_uint16)); + ATF_REQUIRE(val.v_uint16 == UINT16_MAX); + + ATF_REQUIRE(prop_dictionary_set_uint32(dict, "uint32", UINT32_MAX)); + ATF_REQUIRE(prop_dictionary_get_uint32(dict, "uint32", &val.v_uint32)); + ATF_REQUIRE(val.v_uint32 == UINT32_MAX); + + ATF_REQUIRE(prop_dictionary_set_uint64(dict, "uint64", UINT64_MAX)); + ATF_REQUIRE(prop_dictionary_get_uint64(dict, "uint64", &val.v_uint64)); + ATF_REQUIRE(val.v_uint64 == UINT64_MAX); + + ATF_REQUIRE(prop_dictionary_set_string_nocopy(dict, "string", + const_string1)); + ATF_REQUIRE(prop_dictionary_get_string(dict, "string", &cp)); + ATF_REQUIRE(cp == const_string1); + prop_dictionary_remove(dict, "string"); + + ATF_REQUIRE(prop_dictionary_set_string(dict, "string", const_string1)); + ATF_REQUIRE(prop_dictionary_get_string(dict, "string", &cp)); + ATF_REQUIRE(cp != const_string1); + ATF_REQUIRE(strcmp(cp, const_string1) == 0); + + ATF_REQUIRE(prop_dictionary_set_data_nocopy(dict, "data", + const_data1, + sizeof(const_data1))); + ATF_REQUIRE(prop_dictionary_get_data(dict, "data", &v, NULL)); + ATF_REQUIRE(v == const_data1); + prop_dictionary_remove(dict, "data"); + + size = 0xdeadbeef; + ATF_REQUIRE(prop_dictionary_set_data(dict, "data", const_data1, + sizeof(const_data1))); + ATF_REQUIRE(prop_dictionary_get_data(dict, "data", &v, &size)); + ATF_REQUIRE(v != const_data1); + ATF_REQUIRE(size == sizeof(const_data1)); + ATF_REQUIRE(memcmp(v, const_data1, size) == 0); + + prop_object_release(dict); +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, prop_basic); + ATF_TP_ADD_TC(tp, prop_dictionary_equals); + ATF_TP_ADD_TC(tp, prop_dict_util); + ATF_TP_ADD_TC(tp, prop_data_basic); + ATF_TP_ADD_TC(tp, prop_number_basic); + ATF_TP_ADD_TC(tp, prop_number_range_check); + ATF_TP_ADD_TC(tp, prop_string_basic); + + return atf_no_error(); +} diff --git a/lib/libpthread/Makefile b/lib/libpthread/Makefile --- a/lib/libpthread/Makefile +++ b/lib/libpthread/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.12 2016/10/30 16:17:16 kamil Exp $ +# $NetBSD: Makefile,v 1.15 2020/06/21 07:06:05 lukem Exp $ NOMAN= # defined @@ -38,6 +38,7 @@ TESTS_C+= t_siglongjmp TESTS_C+= t_sleep TESTS_C+= t_swapcontext +TESTS_SH+= t_thread_local_dtor TESTS_C+= t_timedmutex LDADD.t_sem+= -lrt @@ -47,6 +48,14 @@ PROGS+= h_cancel PROGS+= h_exit PROGS+= h_resolv +PROGS_CXX+= h_thread_local_dtor + +TESTS_C+= t_call_once t_cnd t_mtx t_thrd t_tss # C11 threads(3) + +COPTS.h_thread_local_dtor.cpp+= -std=c++11 +# Deal with questionable warning and header quality in libstdc++. +COPTS.h_thread_local_dtor.cpp+= ${${ACTIVE_CC} == "gcc" :? -Wno-ctor-dtor-privacy -Wno-sign-compare -Wno-shadow :} +SRCS.h_thread_local_dtor= h_thread_local_dtor.cpp FILESDIR= ${TESTSDIR} FILES= d_mach diff --git a/lib/libpthread/h_resolv.c b/lib/libpthread/h_resolv.c --- a/lib/libpthread/h_resolv.c +++ b/lib/libpthread/h_resolv.c @@ -1,4 +1,4 @@ -/* $NetBSD: h_resolv.c,v 1.2 2010/11/03 16:10:22 christos Exp $ */ +/* $NetBSD: h_resolv.c,v 1.3 2019/03/06 01:20:15 christos Exp $ */ /*- * Copyright (c) 2004, 2008 The NetBSD Foundation, Inc. @@ -32,7 +32,7 @@ #include __COPYRIGHT("@(#) Copyright (c) 2008\ The NetBSD Foundation, inc. All rights reserved."); -__RCSID("$NetBSD: h_resolv.c,v 1.2 2010/11/03 16:10:22 christos Exp $"); +__RCSID("$NetBSD: h_resolv.c,v 1.3 2019/03/06 01:20:15 christos Exp $"); #include #include @@ -56,7 +56,7 @@ static void load(const char *); static void resolvone(int); static void *resolvloop(void *); -static void run(int *); +static pthread_t run(int *); static pthread_mutex_t stats = PTHREAD_MUTEX_INITIALIZER; @@ -77,7 +77,7 @@ char *line; if ((fp = fopen(fname, "r")) == NULL) - err(1, "Cannot open `%s'", fname); + err(EXIT_FAILURE, "Cannot open `%s'", fname); while ((line = fgetln(fp, &len)) != NULL) { char c = line[len]; char *ptr; @@ -130,18 +130,20 @@ return NULL; } -static void +static pthread_t run(int *nhosts) { pthread_t self = pthread_self(); if (pthread_create(&self, NULL, resolvloop, nhosts) != 0) - err(1, "pthread_create"); + err(EXIT_FAILURE, "pthread_create"); + return self; } int main(int argc, char *argv[]) { int nthreads = NTHREADS; + pthread_t *threads; int nhosts = NHOSTS; int i, c, done, *nleft; hosts = sl_init(); @@ -170,16 +172,18 @@ usage(); if ((nleft = malloc(nthreads * sizeof(int))) == NULL) - err(1, "malloc"); + err(EXIT_FAILURE, "malloc"); if ((ask = calloc(hosts->sl_cur, sizeof(int))) == NULL) - err(1, "calloc"); + err(EXIT_FAILURE, "calloc"); if ((got = calloc(hosts->sl_cur, sizeof(int))) == NULL) - err(1, "calloc"); + err(EXIT_FAILURE, "calloc"); + if ((threads = malloc(nthreads * sizeof(pthread_t))) == NULL) + err(EXIT_FAILURE, "calloc"); for (i = 0; i < nthreads; i++) { nleft[i] = nhosts; - run(&nleft[i]); + threads[i] = run(&nleft[i]); } for (done = 0; !done;) { @@ -200,6 +204,9 @@ c++; } } + for (i = 0; i < nthreads; i++) + pthread_join(threads[i], NULL); + free(threads); free(nleft); free(ask); free(got); diff --git a/lib/libpthread/h_thread_local_dtor.cpp b/lib/libpthread/h_thread_local_dtor.cpp new file mode 100644 --- /dev/null +++ b/lib/libpthread/h_thread_local_dtor.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2016 Tavian Barnes. 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +__RCSID("$NetBSD: h_thread_local_dtor.cpp,v 1.1 2017/07/11 15:21:36 joerg Exp $"); + +#include +#include + +static int seq; + +class OrderChecker { +public: + explicit OrderChecker(int n) : n_{n} { } + + ~OrderChecker() { + if (seq != n_) { + printf("Unexpected sequence point: %d\n", 3); + _Exit(1); + } + ++seq; + } + +private: + int n_; +}; + +template +class CreatesThreadLocalInDestructor { +public: + ~CreatesThreadLocalInDestructor() { + thread_local OrderChecker checker{ID}; + } +}; + +OrderChecker global{7}; + +void thread_fn() { + static OrderChecker fn_static{5}; + thread_local CreatesThreadLocalInDestructor<2> creates_tl2; + thread_local OrderChecker fn_thread_local{1}; + thread_local CreatesThreadLocalInDestructor<0> creates_tl0; +} + +int main() { + static OrderChecker fn_static{6}; + + std::thread{thread_fn}.join(); + if (seq != 3) { + printf("Unexpected sequence point: %d\n", 3); + _Exit(1); + } + + thread_local OrderChecker fn_thread_local{4}; + thread_local CreatesThreadLocalInDestructor<3> creates_tl; + + return 0; +} diff --git a/lib/libpthread/t_atexit.sh b/lib/libpthread/t_atexit.sh old mode 100755 new mode 100644 diff --git a/lib/libpthread/t_call_once.c b/lib/libpthread/t_call_once.c new file mode 100644 --- /dev/null +++ b/lib/libpthread/t_call_once.c @@ -0,0 +1,89 @@ +/* $NetBSD: t_call_once.c,v 1.1 2019/04/24 11:43:19 kamil Exp $ */ + +/*- + * Copyright (c) 2019 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Kamil Rytarowski. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +__COPYRIGHT("@(#) Copyright (c) 2019\ + The NetBSD Foundation, inc. All rights reserved."); +__RCSID("$NetBSD: t_call_once.c,v 1.1 2019/04/24 11:43:19 kamil Exp $"); + +#include + +#include + +ATF_TC(call_once); +ATF_TC_HEAD(call_once, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test C11 call_once(3)"); +} + +#define CO_THREADS 8 + +static int co_val; +static once_flag oflag = ONCE_FLAG_INIT; + +static void +called_once(void) +{ + + ++co_val; +} + +static int +co_func(void *arg __unused) +{ + + call_once(&oflag, called_once); + + return 0; +} + +ATF_TC_BODY(call_once, tc) +{ + thrd_t t[CO_THREADS]; + size_t i; + + for (i = 0; i < CO_THREADS; i++) { + ATF_REQUIRE_EQ(thrd_create(&t[i], co_func, NULL), thrd_success); + } + + for (i = 0; i < CO_THREADS; i++) { + ATF_REQUIRE_EQ(thrd_join(t[i], NULL), thrd_success); + } + + ATF_REQUIRE_EQ(co_val, 1); +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, call_once); + + return atf_no_error(); +} diff --git a/lib/libpthread/t_cancel.sh b/lib/libpthread/t_cancel.sh old mode 100755 new mode 100644 diff --git a/lib/libpthread/t_cnd.c b/lib/libpthread/t_cnd.c new file mode 100644 --- /dev/null +++ b/lib/libpthread/t_cnd.c @@ -0,0 +1,175 @@ +/* $NetBSD: t_cnd.c,v 1.1 2019/04/24 11:43:19 kamil Exp $ */ + +/*- + * Copyright (c) 2019 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Kamil Rytarowski. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +__COPYRIGHT("@(#) Copyright (c) 2019\ + The NetBSD Foundation, inc. All rights reserved."); +__RCSID("$NetBSD: t_cnd.c,v 1.1 2019/04/24 11:43:19 kamil Exp $"); + +#include +#include +#include + +#include + +ATF_TC(cnd_init); +ATF_TC_HEAD(cnd_init, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test C11 cnd_init(3)"); +} + +ATF_TC_BODY(cnd_init, tc) +{ + cnd_t c; + + ATF_REQUIRE_EQ(cnd_init(&c), thrd_success); + cnd_destroy(&c); +} + +#define B_THREADS 8 + +static mtx_t b_m; +static cnd_t b_c; +static bool b_finished; + +static int +b_func(void *arg) +{ + bool signal; + + signal = (bool)(intptr_t)arg; + + ATF_REQUIRE_EQ(mtx_lock(&b_m), thrd_success); + while (!b_finished) { + ATF_REQUIRE_EQ(cnd_wait(&b_c, &b_m), thrd_success); + } + ATF_REQUIRE_EQ(mtx_unlock(&b_m), thrd_success); + + if (signal) { + ATF_REQUIRE_EQ(cnd_signal(&b_c), thrd_success); + } + + return 0; +} + +static void +cnd_notify(bool signal) +{ + size_t i; + thrd_t t[B_THREADS]; + void *n; + + n = (void *)(intptr_t)signal; + + ATF_REQUIRE_EQ(mtx_init(&b_m, mtx_plain), thrd_success); + ATF_REQUIRE_EQ(cnd_init(&b_c), thrd_success); + + for (i = 0; i < B_THREADS; i++) { + ATF_REQUIRE_EQ(thrd_create(&t[i], b_func, n), thrd_success); + } + + ATF_REQUIRE_EQ(mtx_lock(&b_m), thrd_success); + b_finished = true; + ATF_REQUIRE_EQ(mtx_unlock(&b_m), thrd_success); + + if (signal) { + ATF_REQUIRE_EQ(cnd_signal(&b_c), thrd_success); + } else { + ATF_REQUIRE_EQ(cnd_broadcast(&b_c), thrd_success); + } + + for (i = 0; i < B_THREADS; i++) { + ATF_REQUIRE_EQ(thrd_join(t[i], NULL), thrd_success); + } + + mtx_destroy(&b_m); + cnd_destroy(&b_c); +} + +ATF_TC(cnd_broadcast); +ATF_TC_HEAD(cnd_broadcast, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test C11 cnd_broadcast(3)"); +} + +ATF_TC_BODY(cnd_broadcast, tc) +{ + + cnd_notify(false); +} + +ATF_TC(cnd_signal); +ATF_TC_HEAD(cnd_signal, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test C11 cnd_signal(3)"); +} + +ATF_TC_BODY(cnd_signal, tc) +{ + + cnd_notify(true); +} + +ATF_TC(cnd_timedwait); +ATF_TC_HEAD(cnd_timedwait, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test C11 cnd_timedwait(3)"); +} + +ATF_TC_BODY(cnd_timedwait, tc) +{ + mtx_t m; + cnd_t c; + struct timespec ts; + + ts.tv_sec = 0; + ts.tv_nsec = 1; + + ATF_REQUIRE_EQ(mtx_init(&m, mtx_plain), thrd_success); + ATF_REQUIRE_EQ(cnd_init(&c), thrd_success); + + ATF_REQUIRE_EQ(mtx_lock(&m), thrd_success); + ATF_REQUIRE_EQ(cnd_timedwait(&c, &m, &ts), thrd_timedout); + ATF_REQUIRE_EQ(mtx_unlock(&m), thrd_success); + + mtx_destroy(&m); + cnd_destroy(&c); +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, cnd_init); + ATF_TP_ADD_TC(tp, cnd_broadcast); + ATF_TP_ADD_TC(tp, cnd_signal); + ATF_TP_ADD_TC(tp, cnd_timedwait); + + return atf_no_error(); +} diff --git a/lib/libpthread/t_cond.c b/lib/libpthread/t_cond.c --- a/lib/libpthread/t_cond.c +++ b/lib/libpthread/t_cond.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_cond.c,v 1.7 2016/07/03 14:24:59 christos Exp $ */ +/* $NetBSD: t_cond.c,v 1.8 2020/06/10 21:46:50 ad Exp $ */ /* * Copyright (c) 2008 The NetBSD Foundation, Inc. @@ -29,7 +29,7 @@ #include __COPYRIGHT("@(#) Copyright (c) 2008\ The NetBSD Foundation, inc. All rights reserved."); -__RCSID("$NetBSD: t_cond.c,v 1.7 2016/07/03 14:24:59 christos Exp $"); +__RCSID("$NetBSD: t_cond.c,v 1.8 2020/06/10 21:46:50 ad Exp $"); #include @@ -329,7 +329,7 @@ * Sometimes we catch ESRCH. * This should never happen. */ - ATF_REQUIRE(rv == ETIMEDOUT); + ATF_REQUIRE(rv == ETIMEDOUT || rv == 0); PTHREAD_REQUIRE(pthread_mutex_unlock(&static_mutex)); } } diff --git a/lib/libpthread/t_condwait.c b/lib/libpthread/t_condwait.c --- a/lib/libpthread/t_condwait.c +++ b/lib/libpthread/t_condwait.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_condwait.c,v 1.5 2017/01/16 16:29:19 christos Exp $ */ +/* $NetBSD: t_condwait.c,v 1.8 2019/08/11 11:42:23 martin Exp $ */ /* * Copyright (c) 2013 The NetBSD Foundation, Inc. @@ -26,7 +26,7 @@ * POSSIBILITY OF SUCH DAMAGE. */ #include -__RCSID("$NetBSD: t_condwait.c,v 1.5 2017/01/16 16:29:19 christos Exp $"); +__RCSID("$NetBSD: t_condwait.c,v 1.8 2019/08/11 11:42:23 martin Exp $"); #include #include @@ -50,7 +50,7 @@ static void * run(void *param) { - struct timespec ts, to, te; + struct timespec ts, to, te, twmin, twmax; clockid_t clck; pthread_condattr_t attr; pthread_cond_t cond; @@ -85,14 +85,23 @@ printf("elapsed: %lld.%09ld sec\n", (long long)to.tv_sec, to.tv_nsec); } + twmin.tv_sec = WAITTIME; + twmin.tv_nsec = 0; if (isQEMU()) { - double to_seconds = to.tv_sec + 1e-9 * to.tv_nsec; - ATF_REQUIRE(to_seconds >= WAITTIME * 0.9); - /* Loose upper limit because of qemu timing bugs */ - ATF_REQUIRE(to_seconds < WAITTIME * 2.5); + struct timespec td, t; + // td.tv_sec = 0; + // td.tv_nsec = 900000000; + t = twmin; + // timespecsub(&t, &td, &twmin); + td.tv_sec = 2; + td.tv_nsec = 500000000; + timespecadd(&t, &td, &twmax); } else { - ATF_REQUIRE_EQ(to.tv_sec, WAITTIME); + twmax = twmin; + twmax.tv_sec++; } + ATF_REQUIRE(timespeccmp(&to, &twmin, >=)); + ATF_REQUIRE(timespeccmp(&to, &twmax, <=)); break; default: ATF_REQUIRE_MSG(0, "pthread_cond_timedwait: %s", strerror(ret)); @@ -141,5 +150,5 @@ { ATF_TP_ADD_TC(tp, cond_wait_real); ATF_TP_ADD_TC(tp, cond_wait_mono); - return 0; + return atf_no_error(); } diff --git a/lib/libpthread/t_exit.sh b/lib/libpthread/t_exit.sh old mode 100755 new mode 100644 diff --git a/lib/libpthread/t_join.c b/lib/libpthread/t_join.c --- a/lib/libpthread/t_join.c +++ b/lib/libpthread/t_join.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_join.c,v 1.8 2012/03/12 20:17:16 joerg Exp $ */ +/* $NetBSD: t_join.c,v 1.10 2020/01/29 13:40:23 kamil Exp $ */ /*- * Copyright (c) 2010 The NetBSD Foundation, Inc. @@ -29,7 +29,7 @@ * POSSIBILITY OF SUCH DAMAGE. */ #include -__RCSID("$NetBSD: t_join.c,v 1.8 2012/03/12 20:17:16 joerg Exp $"); +__RCSID("$NetBSD: t_join.c,v 1.10 2020/01/29 13:40:23 kamil Exp $"); #include #include @@ -104,6 +104,7 @@ error = true; ATF_REQUIRE(pthread_attr_setstacksize(&attr, STACKSIZE * (i + 1)) == 0); + ATF_REQUIRE(pthread_attr_setguardsize(&attr, STACKSIZE * (i + 2)) == 0); rv = pthread_create(&thread[i], &attr, threadfunc2, (void *)i); @@ -148,13 +149,15 @@ static uintptr_t i = 0; uintptr_t j; pthread_attr_t attr; - size_t stacksize; + size_t stacksize, guardsize; j = (uintptr_t)arg; - ATF_REQUIRE(pthread_attr_get_np(pthread_self(), &attr) == 0); + ATF_REQUIRE(pthread_getattr_np(pthread_self(), &attr) == 0); ATF_REQUIRE(pthread_attr_getstacksize(&attr, &stacksize) == 0); ATF_REQUIRE(stacksize == STACKSIZE * (j + 1)); + ATF_REQUIRE(pthread_attr_getguardsize(&attr, &guardsize) == 0); + ATF_REQUIRE(guardsize == STACKSIZE * (j + 2)); ATF_REQUIRE(pthread_attr_destroy(&attr) == 0); if (i++ == j) diff --git a/lib/libpthread/t_mtx.c b/lib/libpthread/t_mtx.c new file mode 100644 --- /dev/null +++ b/lib/libpthread/t_mtx.c @@ -0,0 +1,212 @@ +/* $NetBSD: t_mtx.c,v 1.2 2020/06/11 11:40:54 martin Exp $ */ + +/*- + * Copyright (c) 2019 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Kamil Rytarowski. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +__COPYRIGHT("@(#) Copyright (c) 2019\ + The NetBSD Foundation, inc. All rights reserved."); +__RCSID("$NetBSD: t_mtx.c,v 1.2 2020/06/11 11:40:54 martin Exp $"); + +#include +#include + +#include + +ATF_TC(mtx_init); +ATF_TC_HEAD(mtx_init, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test C11 mtx_init(3)"); +} + +ATF_TC_BODY(mtx_init, tc) +{ + mtx_t m; + + ATF_REQUIRE_EQ(mtx_init(&m, mtx_plain), thrd_success); + mtx_destroy(&m); + + ATF_REQUIRE_EQ(mtx_init(&m, mtx_plain | mtx_recursive), thrd_success); + mtx_destroy(&m); + + ATF_REQUIRE_EQ(mtx_init(&m, mtx_timed), thrd_success); + mtx_destroy(&m); + + ATF_REQUIRE_EQ(mtx_init(&m, mtx_timed | mtx_recursive), thrd_success); + mtx_destroy(&m); + + ATF_REQUIRE_EQ(mtx_init(&m, mtx_recursive), thrd_error); + + ATF_REQUIRE_EQ(mtx_init(&m, mtx_plain | mtx_timed), thrd_error); + + ATF_REQUIRE_EQ(mtx_init(&m, -1), thrd_error); +} + +ATF_TC(mtx_lock); +ATF_TC_HEAD(mtx_lock, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test C11 mtx_lock(3)"); +} + +ATF_TC_BODY(mtx_lock, tc) +{ + mtx_t m; + + ATF_REQUIRE_EQ(mtx_init(&m, mtx_plain), thrd_success); + ATF_REQUIRE_EQ(mtx_lock(&m), thrd_success); + ATF_REQUIRE_EQ(mtx_unlock(&m), thrd_success); + mtx_destroy(&m); + + ATF_REQUIRE_EQ(mtx_init(&m, mtx_timed), thrd_success); + ATF_REQUIRE_EQ(mtx_lock(&m), thrd_success); + ATF_REQUIRE_EQ(mtx_unlock(&m), thrd_success); + mtx_destroy(&m); + + ATF_REQUIRE_EQ(mtx_init(&m, mtx_plain | mtx_recursive), thrd_success); + ATF_REQUIRE_EQ(mtx_lock(&m), thrd_success); + ATF_REQUIRE_EQ(mtx_lock(&m), thrd_success); + ATF_REQUIRE_EQ(mtx_lock(&m), thrd_success); + ATF_REQUIRE_EQ(mtx_unlock(&m), thrd_success); + ATF_REQUIRE_EQ(mtx_unlock(&m), thrd_success); + ATF_REQUIRE_EQ(mtx_unlock(&m), thrd_success); + mtx_destroy(&m); + + ATF_REQUIRE_EQ(mtx_init(&m, mtx_timed | mtx_recursive), thrd_success); + ATF_REQUIRE_EQ(mtx_lock(&m), thrd_success); + ATF_REQUIRE_EQ(mtx_lock(&m), thrd_success); + ATF_REQUIRE_EQ(mtx_lock(&m), thrd_success); + ATF_REQUIRE_EQ(mtx_unlock(&m), thrd_success); + ATF_REQUIRE_EQ(mtx_unlock(&m), thrd_success); + ATF_REQUIRE_EQ(mtx_unlock(&m), thrd_success); + mtx_destroy(&m); +} + +ATF_TC(mtx_timedlock); +ATF_TC_HEAD(mtx_timedlock, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test C11 mtx_timedlock(3)"); +} + +ATF_TC_BODY(mtx_timedlock, tc) +{ + mtx_t m; + struct timespec ts; + + ts.tv_sec = 0; + ts.tv_nsec = 1; + + ATF_REQUIRE_EQ(mtx_init(&m, mtx_timed), thrd_success); + ATF_REQUIRE_EQ(mtx_timedlock(&m, &ts), thrd_success); + ATF_REQUIRE_EQ(mtx_unlock(&m), thrd_success); + mtx_destroy(&m); + + ATF_REQUIRE_EQ(mtx_init(&m, mtx_timed), thrd_success); + ATF_REQUIRE_EQ(mtx_lock(&m), thrd_success); + ATF_REQUIRE_EQ(mtx_timedlock(&m, &ts), thrd_timedout); + ATF_REQUIRE_EQ(mtx_unlock(&m), thrd_success); + mtx_destroy(&m); + + ATF_REQUIRE_EQ(mtx_init(&m, mtx_timed | mtx_recursive), thrd_success); + ATF_REQUIRE_EQ(mtx_lock(&m), thrd_success); + ATF_REQUIRE_EQ(mtx_timedlock(&m, &ts), thrd_success); + ATF_REQUIRE_EQ(mtx_unlock(&m), thrd_success); + ATF_REQUIRE_EQ(mtx_unlock(&m), thrd_success); + mtx_destroy(&m); + + ATF_REQUIRE_EQ(mtx_init(&m, mtx_timed | mtx_recursive), thrd_success); + ATF_REQUIRE_EQ(mtx_timedlock(&m, &ts), thrd_success); + ATF_REQUIRE_EQ(mtx_timedlock(&m, &ts), thrd_success); + ATF_REQUIRE_EQ(mtx_unlock(&m), thrd_success); + ATF_REQUIRE_EQ(mtx_unlock(&m), thrd_success); + mtx_destroy(&m); + + ATF_REQUIRE_EQ(mtx_init(&m, mtx_timed | mtx_recursive), thrd_success); + ATF_REQUIRE_EQ(mtx_timedlock(&m, &ts), thrd_success); + ATF_REQUIRE_EQ(mtx_lock(&m), thrd_success); + ATF_REQUIRE_EQ(mtx_unlock(&m), thrd_success); + ATF_REQUIRE_EQ(mtx_unlock(&m), thrd_success); + mtx_destroy(&m); +} + +ATF_TC(mtx_trylock); +ATF_TC_HEAD(mtx_trylock, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test C11 mtx_trylock(3)"); +} + +ATF_TC_BODY(mtx_trylock, tc) +{ + mtx_t m; + + ATF_REQUIRE_EQ(mtx_init(&m, mtx_plain), thrd_success); + ATF_REQUIRE_EQ(mtx_trylock(&m), thrd_success); + ATF_REQUIRE_EQ(mtx_unlock(&m), thrd_success); + mtx_destroy(&m); + + ATF_REQUIRE_EQ(mtx_init(&m, mtx_plain), thrd_success); + ATF_REQUIRE_EQ(mtx_lock(&m), thrd_success); + ATF_REQUIRE_EQ(mtx_trylock(&m), thrd_busy); + ATF_REQUIRE_EQ(mtx_unlock(&m), thrd_success); + mtx_destroy(&m); + + ATF_REQUIRE_EQ(mtx_init(&m, mtx_plain | mtx_recursive), thrd_success); + ATF_REQUIRE_EQ(mtx_lock(&m), thrd_success); + ATF_REQUIRE_EQ(mtx_trylock(&m), thrd_success); + ATF_REQUIRE_EQ(mtx_unlock(&m), thrd_success); + ATF_REQUIRE_EQ(mtx_unlock(&m), thrd_success); + mtx_destroy(&m); + + ATF_REQUIRE_EQ(mtx_init(&m, mtx_timed), thrd_success); + ATF_REQUIRE_EQ(mtx_trylock(&m), thrd_success); + ATF_REQUIRE_EQ(mtx_unlock(&m), thrd_success); + mtx_destroy(&m); + + ATF_REQUIRE_EQ(mtx_init(&m, mtx_timed), thrd_success); + ATF_REQUIRE_EQ(mtx_lock(&m), thrd_success); + ATF_REQUIRE_EQ(mtx_trylock(&m), thrd_busy); + ATF_REQUIRE_EQ(mtx_unlock(&m), thrd_success); + mtx_destroy(&m); + + ATF_REQUIRE_EQ(mtx_init(&m, mtx_timed | mtx_recursive), thrd_success); + ATF_REQUIRE_EQ(mtx_lock(&m), thrd_success); + ATF_REQUIRE_EQ(mtx_trylock(&m), thrd_success); + ATF_REQUIRE_EQ(mtx_unlock(&m), thrd_success); + ATF_REQUIRE_EQ(mtx_unlock(&m), thrd_success); + mtx_destroy(&m); +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, mtx_init); + ATF_TP_ADD_TC(tp, mtx_lock); + ATF_TP_ADD_TC(tp, mtx_timedlock); + ATF_TP_ADD_TC(tp, mtx_trylock); + + return atf_no_error(); +} diff --git a/lib/libpthread/t_mutex.c b/lib/libpthread/t_mutex.c --- a/lib/libpthread/t_mutex.c +++ b/lib/libpthread/t_mutex.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_mutex.c,v 1.15 2017/01/16 16:23:41 christos Exp $ */ +/* $NetBSD: t_mutex.c,v 1.19 2017/12/01 13:25:29 kre Exp $ */ /* * Copyright (c) 2008 The NetBSD Foundation, Inc. @@ -29,7 +29,7 @@ #include __COPYRIGHT("@(#) Copyright (c) 2008\ The NetBSD Foundation, inc. All rights reserved."); -__RCSID("$NetBSD: t_mutex.c,v 1.15 2017/01/16 16:23:41 christos Exp $"); +__RCSID("$NetBSD: t_mutex.c,v 1.19 2017/12/01 13:25:29 kre Exp $"); #include /* For timespecadd */ #include /* For UINT16_MAX */ @@ -148,9 +148,6 @@ ATF_TC_HEAD(mutex2, tc) { atf_tc_set_md_var(tc, "descr", "Checks mutexes"); -#if defined(__powerpc__) - atf_tc_set_md_var(tc, "timeout", "40"); -#endif } ATF_TC_BODY(mutex2, tc) { @@ -160,10 +157,6 @@ printf("1: Mutex-test 2\n"); -#if defined(__powerpc__) - atf_tc_expect_timeout("PR port-powerpc/44387"); -#endif - PTHREAD_REQUIRE(pthread_mutex_init(&mutex, NULL)); global_x = 0; @@ -188,14 +181,6 @@ printf("1: Thread joined. X was %d. Return value (long) was %ld\n", global_x, (long)joinval); ATF_REQUIRE_EQ(global_x, 20000000); - -#if defined(__powerpc__) - /* XXX force a timeout in ppc case since an un-triggered race - otherwise looks like a "failure" */ - /* We sleep for longer than the timeout to make ATF not - complain about unexpected success */ - sleep(41); -#endif } static void * @@ -219,9 +204,6 @@ { atf_tc_set_md_var(tc, "descr", "Checks mutexes using a static " "initializer"); -#if defined(__powerpc__) - atf_tc_set_md_var(tc, "timeout", "40"); -#endif } ATF_TC_BODY(mutex3, tc) { @@ -231,10 +213,6 @@ printf("1: Mutex-test 3\n"); -#if defined(__powerpc__) - atf_tc_expect_timeout("PR port-powerpc/44387"); -#endif - global_x = 0; count = count2 = 10000000; @@ -257,14 +235,6 @@ printf("1: Thread joined. X was %d. Return value (long) was %ld\n", global_x, (long)joinval); ATF_REQUIRE_EQ(global_x, 20000000); - -#if defined(__powerpc__) - /* XXX force a timeout in ppc case since an un-triggered race - otherwise looks like a "failure" */ - /* We sleep for longer than the timeout to make ATF not - complain about unexpected success */ - sleep(41); -#endif } static void * @@ -401,131 +371,6 @@ PTHREAD_REQUIRE(pthread_join(child, NULL)); } -static pthread_mutex_t mutex6; -static int start = 0; -static uintmax_t high_cnt = 0, low_cnt = 0, MAX_LOOP = 100000000; - -static void * -high_prio(void* arg) -{ - struct sched_param param; - int policy; - param.sched_priority = min_fifo_prio + 10; - pthread_t childid = pthread_self(); - - PTHREAD_REQUIRE(pthread_setschedparam(childid, 1, ¶m)); - PTHREAD_REQUIRE(pthread_getschedparam(childid, &policy, ¶m)); - printf("high protect = %d, prio = %d\n", - _sched_protect(-2), param.sched_priority); - ATF_REQUIRE_EQ(policy, 1); - printf("high prio = %d\n", param.sched_priority); - sleep(1); - long tmp = 0; - for (int i = 0; i < 20; i++) { - while (high_cnt < MAX_LOOP) { - tmp += (123456789 % 1234) * (987654321 % 54321); - high_cnt += 1; - } - high_cnt = 0; - sleep(1); - } - PTHREAD_REQUIRE(mutex_lock(&mutex6, &ts_lengthy)); - if (start == 0) start = 2; - PTHREAD_REQUIRE(pthread_mutex_unlock(&mutex6)); - - return 0; -} - -static void * -low_prio(void* arg) -{ - struct sched_param param; - int policy; - param.sched_priority = min_fifo_prio; - pthread_t childid = pthread_self(); - int res = _sched_protect(max_fifo_prio); - ATF_REQUIRE_EQ(res, 0); - PTHREAD_REQUIRE(pthread_setschedparam(childid, 1, ¶m)); - PTHREAD_REQUIRE(pthread_getschedparam(childid, &policy, ¶m)); - printf("low protect = %d, prio = %d\n", _sched_protect(-2), - param.sched_priority); - ATF_REQUIRE_EQ(policy, 1); - printf("low prio = %d\n", param.sched_priority); - sleep(1); - long tmp = 0; - for (int i = 0; i < 20; i++) { - while (low_cnt < MAX_LOOP) { - tmp += (123456789 % 1234) * (987654321 % 54321); - low_cnt += 1; - } - low_cnt = 0; - sleep(1); - } - PTHREAD_REQUIRE(mutex_lock(&mutex6, &ts_lengthy)); - if (start == 0) - start = 1; - PTHREAD_REQUIRE(pthread_mutex_unlock(&mutex6)); - - return 0; -} - -ATF_TC(mutex6); -ATF_TC_HEAD(mutex6, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Checks scheduling for priority ceiling"); - atf_tc_set_md_var(tc, "require.user", "root"); -} - -/* - * 1. main thread sets itself to be a realtime task and launched two tasks, - * one has higher priority and the other has lower priority. - * 2. each child thread(low and high priority thread) sets its scheduler and - * priority. - * 3. each child thread did several rounds of computation, after each round it - * sleep 1 second. - * 4. the child thread with low priority will call _sched_protect to increase - * its protect priority. - * 5. We verify the thread with low priority runs first. - * - * Why does it work? From the main thread, we launched the high - * priority thread first. This gives this thread the benefit of - * starting first. The low priority thread did not call _sched_protect(2). - * The high priority thread should finish the task first. After each - * round of computation, we call sleep, to put the task into the - * sleep queue, and wake up again after the timer expires. This - * gives the scheduler the chance to decide which task to run. So, - * the thread with real high priority will always block the thread - * with real low priority. - * - */ -ATF_TC_BODY(mutex6, tc) -{ - struct sched_param param; - int res; - pthread_t high, low; - - min_fifo_prio = sched_get_priority_min(SCHED_FIFO); - max_fifo_prio = sched_get_priority_max(SCHED_FIFO); - PTHREAD_REQUIRE(pthread_mutex_init(&mutex, NULL)); - printf("min_fifo_prio = %d, max_fifo_info = %d\n", min_fifo_prio, - max_fifo_prio); - - param.sched_priority = min_fifo_prio; - res = sched_setscheduler(getpid(), SCHED_FIFO, ¶m); - printf("previous policy used = %d\n", res); - - res = sched_getscheduler(getpid()); - ATF_REQUIRE_EQ(res, 1); - PTHREAD_REQUIRE(pthread_create(&high, NULL, high_prio, NULL)); - PTHREAD_REQUIRE(pthread_create(&low, NULL, low_prio, NULL)); - sleep(5); - PTHREAD_REQUIRE(pthread_join(low, NULL)); - PTHREAD_REQUIRE(pthread_join(high, NULL)); - - ATF_REQUIRE_EQ(start, 1); -} - ATF_TC(mutexattr1); ATF_TC_HEAD(mutexattr1, tc) { @@ -710,7 +555,6 @@ ATF_TP_ADD_TC(tp, mutex3); ATF_TP_ADD_TC(tp, mutex4); ATF_TP_ADD_TC(tp, mutex5); - ATF_TP_ADD_TC(tp, mutex6); ATF_TP_ADD_TC(tp, mutexattr1); ATF_TP_ADD_TC(tp, mutexattr2); diff --git a/lib/libpthread/t_once.c b/lib/libpthread/t_once.c --- a/lib/libpthread/t_once.c +++ b/lib/libpthread/t_once.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_once.c,v 1.1 2010/07/16 15:42:53 jmmv Exp $ */ +/* $NetBSD: t_once.c,v 1.2 2017/08/25 22:59:47 ginsbach Exp $ */ /* * Copyright (c) 2008 The NetBSD Foundation, Inc. @@ -29,8 +29,9 @@ #include __COPYRIGHT("@(#) Copyright (c) 2008\ The NetBSD Foundation, inc. All rights reserved."); -__RCSID("$NetBSD: t_once.c,v 1.1 2010/07/16 15:42:53 jmmv Exp $"); +__RCSID("$NetBSD: t_once.c,v 1.2 2017/08/25 22:59:47 ginsbach Exp $"); +#include #include #include #include diff --git a/lib/libpthread/t_resolv.sh b/lib/libpthread/t_resolv.sh old mode 100755 new mode 100644 diff --git a/lib/libpthread/t_swapcontext.c b/lib/libpthread/t_swapcontext.c --- a/lib/libpthread/t_swapcontext.c +++ b/lib/libpthread/t_swapcontext.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_swapcontext.c,v 1.3 2017/01/16 16:27:06 christos Exp $ */ +/* $NetBSD: t_swapcontext.c,v 1.9 2018/02/28 21:36:50 uwe Exp $ */ /* * Copyright (c) 2012 Emmanuel Dreyfus. All rights reserved. @@ -26,7 +26,7 @@ */ #include -__RCSID("$NetBSD"); +__RCSID("$NetBSD: t_swapcontext.c,v 1.9 2018/02/28 21:36:50 uwe Exp $"); #include #include @@ -49,24 +49,18 @@ void *nself; int val1, val2; -/* ARGSUSED0 */ static void -swapfunc(void *arg) +swapfunc(void) { /* - * If the test fails, we are very likely to crash + * If the test fails, we are very likely to crash * without the opportunity to report - */ + */ nself = (void *)pthread_self(); printf("after swapcontext self = %p\n", nself); ATF_REQUIRE_EQ(oself, nself); printf("Test succeeded\n"); - /* Go back in main */ - ATF_REQUIRE(swapcontext(&nctx, &octx)); - - /* NOTREACHED */ - return; } /* ARGSUSED0 */ @@ -75,15 +69,15 @@ { nctx.uc_stack.ss_sp = stack; nctx.uc_stack.ss_size = sizeof(stack); - - makecontext(&nctx, (void *)*swapfunc, 0); - + nctx.uc_link = &octx; + + makecontext(&nctx, swapfunc, 0); + oself = (void *)pthread_self(); printf("before swapcontext self = %p\n", oself); ATF_REQUIRE_MSG(swapcontext(&octx, &nctx) != -1, "swapcontext failed: %s", strerror(errno)); - /* NOTREACHED */ return NULL; } diff --git a/lib/libpthread/t_thrd.c b/lib/libpthread/t_thrd.c new file mode 100644 --- /dev/null +++ b/lib/libpthread/t_thrd.c @@ -0,0 +1,237 @@ +/* $NetBSD: t_thrd.c,v 1.2 2020/05/14 08:34:19 msaitoh Exp $ */ + +/*- + * Copyright (c) 2019 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Kamil Rytarowski. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +__COPYRIGHT("@(#) Copyright (c) 2019\ + The NetBSD Foundation, inc. All rights reserved."); +__RCSID("$NetBSD: t_thrd.c,v 1.2 2020/05/14 08:34:19 msaitoh Exp $"); + +#include +#include +#include +#include + +#include + +ATF_TC(thrd_create); +ATF_TC_HEAD(thrd_create, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test C11 thrd_create(3)"); +} + +#define TC_ADDON 5 + +static int +tcr_func(void *arg) +{ + int a; + + a = (int)(intptr_t)arg; + + return a + TC_ADDON; +} + +ATF_TC_BODY(thrd_create, tc) +{ + thrd_t t; + const int a = 5; + int b; + void *v; + + v = (void *)(intptr_t)a; + + ATF_REQUIRE_EQ(thrd_create(&t, tcr_func, v), thrd_success); + ATF_REQUIRE_EQ(thrd_join(t, &b), thrd_success); + ATF_REQUIRE_EQ(a + TC_ADDON, b); +} + +ATF_TC(thrd_current); +ATF_TC_HEAD(thrd_current, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test C11 thrd_current(3)"); +} + +static int +tcur_func(void *arg __unused) +{ + + return 0; +} + +ATF_TC_BODY(thrd_current, tc) +{ + thrd_t s, t; + + s = thrd_current(); + + ATF_REQUIRE(thrd_equal(s, s) != 0); + ATF_REQUIRE_EQ(thrd_create(&t, tcur_func, NULL), thrd_success); + ATF_REQUIRE(thrd_equal(t, s) == 0); + ATF_REQUIRE(thrd_equal(s, t) == 0); + ATF_REQUIRE(thrd_equal(t, t) != 0); + + ATF_REQUIRE_EQ(thrd_join(t, NULL), thrd_success); +} + +ATF_TC(thrd_detach); +ATF_TC_HEAD(thrd_detach, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test C11 thrd_detach(3)"); +} + +static int +tdet_func(void *arg __unused) +{ + + return 0; +} + +ATF_TC_BODY(thrd_detach, tc) +{ + thrd_t t; + + ATF_REQUIRE_EQ(thrd_create(&t, tdet_func, NULL), thrd_success); + ATF_REQUIRE_EQ(thrd_detach(t), thrd_success); +} + +ATF_TC(thrd_exit); +ATF_TC_HEAD(thrd_exit, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test C11 thrd_exit(3)"); +} + +static void +tex_func2(void) +{ + + thrd_exit(1); +} + +static int +tex_func(void *arg __unused) +{ + + tex_func2(); + + return -1; +} + +ATF_TC_BODY(thrd_exit, tc) +{ + thrd_t t; + int b = 0; + + ATF_REQUIRE_EQ(thrd_create(&t, tex_func, NULL), thrd_success); + ATF_REQUIRE_EQ(thrd_join(t, &b), thrd_success); + ATF_REQUIRE_EQ(b, 1); +} + +ATF_TC(thrd_sleep); +ATF_TC_HEAD(thrd_sleep, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test C11 thrd_sleep(3)"); +} + +static int alarmed; + +static void +alarm_handler(int signum) +{ + + ATF_REQUIRE_EQ(signum, SIGALRM); + ++alarmed; +} + +ATF_TC_BODY(thrd_sleep, tc) +{ + struct timespec start, stop, diff; + struct timespec ts, rem; + struct timespec zero; + struct sigaction sa; + struct itimerval timer; + + zero.tv_sec = 0; + zero.tv_nsec = 0; + + ts.tv_sec = 1; + ts.tv_nsec = -1; + ATF_REQUIRE_EQ(!thrd_sleep(&ts, NULL), 0); + + ts.tv_sec = 0; + ts.tv_nsec = 1000000000/100; /* 1/100 sec */ + ATF_REQUIRE_EQ(clock_gettime(CLOCK_MONOTONIC, &start), 0); + ATF_REQUIRE_EQ(thrd_sleep(&ts, &rem), 0); + ATF_REQUIRE_EQ(clock_gettime(CLOCK_MONOTONIC, &stop), 0); + timespecsub(&stop, &start, &diff); + ATF_REQUIRE(timespeccmp(&diff, &ts, >=)); + ATF_REQUIRE(timespeccmp(&zero, &rem, ==)); + + ts.tv_sec = 1; + ts.tv_nsec = 0; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = alarm_handler; + sigaction(SIGALRM, &sa, NULL); + memset(&timer, 0, sizeof(timer)); + timer.it_value.tv_sec = 0; + timer.it_value.tv_usec = 100000; /* 100 msec */ + ATF_REQUIRE_EQ(setitimer(ITIMER_MONOTONIC, &timer, NULL), 0); + ATF_REQUIRE_EQ(clock_gettime(CLOCK_MONOTONIC, &start), 0); + ATF_REQUIRE_EQ(!thrd_sleep(&ts, &rem), 0); + ATF_REQUIRE_EQ(clock_gettime(CLOCK_MONOTONIC, &stop), 0); + timespecsub(&stop, &start, &diff); + ATF_REQUIRE(timespeccmp(&diff, &ts, <)); + ATF_REQUIRE(timespeccmp(&zero, &rem, !=)); + ATF_REQUIRE_EQ(alarmed, 1); +} + +ATF_TC(thrd_yield); +ATF_TC_HEAD(thrd_yield, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test C11 thrd_yield(3)"); +} + +ATF_TC_BODY(thrd_yield, tc) +{ + + thrd_yield(); +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, thrd_create); + ATF_TP_ADD_TC(tp, thrd_current); + ATF_TP_ADD_TC(tp, thrd_detach); + ATF_TP_ADD_TC(tp, thrd_exit); + ATF_TP_ADD_TC(tp, thrd_sleep); + ATF_TP_ADD_TC(tp, thrd_yield); + + return atf_no_error(); +} diff --git a/lib/libpthread/t_thread_local_dtor.sh b/lib/libpthread/t_thread_local_dtor.sh new file mode 100644 --- /dev/null +++ b/lib/libpthread/t_thread_local_dtor.sh @@ -0,0 +1,41 @@ +# $NetBSD: t_thread_local_dtor.sh,v 1.1 2017/07/11 15:21:36 joerg Exp $ +# +# Copyright (c) 2008 The NetBSD Foundation, Inc. +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +atf_test_case thread_local_dtor_order +thread_local_dtor_order_head() +{ + atf_set "descr" "Checks destructor order for thread_local objects" +} +thread_local_dtor_order_body() +{ + atf_check -o ignore "$(atf_get_srcdir)/h_thread_local_dtor" +} + +atf_init_test_cases() +{ + atf_add_test_case thread_local_dtor_order +} diff --git a/lib/libpthread/t_tss.c b/lib/libpthread/t_tss.c new file mode 100644 --- /dev/null +++ b/lib/libpthread/t_tss.c @@ -0,0 +1,174 @@ +/* $NetBSD: t_tss.c,v 1.1 2019/04/24 11:43:19 kamil Exp $ */ + +/*- + * Copyright (c) 2019 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Kamil Rytarowski. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +__COPYRIGHT("@(#) Copyright (c) 2019\ + The NetBSD Foundation, inc. All rights reserved."); +__RCSID("$NetBSD: t_tss.c,v 1.1 2019/04/24 11:43:19 kamil Exp $"); + +#include +#include + +#include + +ATF_TC(tss_create); +ATF_TC_HEAD(tss_create, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test C11 tss_create(3)"); +} + +ATF_TC_BODY(tss_create, tc) +{ + tss_t s; + + ATF_REQUIRE_EQ(tss_create(&s, NULL), thrd_success); + tss_delete(s); +} + +ATF_TC(tss_set); +ATF_TC_HEAD(tss_set, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test C11 tss_set(3)"); +} + +ATF_TC_BODY(tss_set, tc) +{ + tss_t s; + int b = 5; + void *v; + + v = (void *)(intptr_t)b; + + ATF_REQUIRE_EQ(tss_create(&s, NULL), thrd_success); + ATF_REQUIRE_EQ(tss_get(s), NULL); + ATF_REQUIRE_EQ(tss_set(s, v), thrd_success); + ATF_REQUIRE_EQ(tss_get(s), v); + + tss_delete(s); +} + +ATF_TC(tss_destructor_main_thread); +ATF_TC_HEAD(tss_destructor_main_thread, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Test C11 tss(3) destructor in main thread"); +} + +static void +c_destructor_main(void *arg) +{ + + abort(); +} + +ATF_TC_BODY(tss_destructor_main_thread, tc) +{ + tss_t s; + int b = 5; + void *v; + + v = (void *)(intptr_t)b; + + ATF_REQUIRE_EQ(tss_create(&s, c_destructor_main), thrd_success); + ATF_REQUIRE_EQ(tss_set(s, v), thrd_success); + + tss_delete(s); + + /* Destructor must NOT be called for tss_delete(3) */ +} + +ATF_TC(tss_destructor_thread_exit); +ATF_TC_HEAD(tss_destructor_thread_exit, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Test C11 tss(3) destructor on thread exit"); +} + +static tss_t s_empty, s_nonempty; +static int c_iter_empty, c_iter_nonempty; + +static void +c_destructor_thread_empty(void *arg) +{ + + tss_set(s_empty, arg); + ++c_iter_empty; +} + +static void +c_destructor_thread_nonempty(void *arg) +{ + + tss_set(s_nonempty, arg); + ++c_iter_nonempty; +} + +static int +t_func(void *arg __unused) +{ + int b = 5; + void *v; + + v = (void *)(intptr_t)b; + + ATF_REQUIRE_EQ(tss_set(s_nonempty, v), thrd_success); + + return 0; +} + +ATF_TC_BODY(tss_destructor_thread_exit, tc) +{ + thrd_t t; + + ATF_REQUIRE_EQ(tss_create(&s_empty, c_destructor_thread_empty), + thrd_success); + ATF_REQUIRE_EQ(tss_create(&s_nonempty, c_destructor_thread_nonempty), + thrd_success); + + ATF_REQUIRE_EQ(thrd_create(&t, t_func, NULL), thrd_success); + ATF_REQUIRE_EQ(thrd_join(t, NULL), thrd_success); + + ATF_REQUIRE_EQ(c_iter_empty, 0); + ATF_REQUIRE_EQ(c_iter_nonempty, TSS_DTOR_ITERATIONS); + + tss_delete(s_empty); + tss_delete(s_nonempty); +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, tss_create); + ATF_TP_ADD_TC(tp, tss_set); + ATF_TP_ADD_TC(tp, tss_destructor_main_thread); + ATF_TP_ADD_TC(tp, tss_destructor_thread_exit); + + return atf_no_error(); +} diff --git a/lib/libpthread_dbg/h_common.h b/lib/libpthread_dbg/h_common.h deleted file mode 100644 --- a/lib/libpthread_dbg/h_common.h +++ /dev/null @@ -1,128 +0,0 @@ -/* $NetBSD: h_common.h,v 1.2 2016/11/19 02:30:54 kamil Exp $ */ - -/*- - * Copyright (c) 2016 The NetBSD Foundation, Inc. - * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. - */ - - -#ifndef H_COMMON_H -#define H_COMMON_H - -#include -#include -#include -#include - -#include - -#define PTHREAD_REQUIRE(x) \ - do { \ - int ret = (x); \ - ATF_REQUIRE_MSG(ret == 0, "%s: %s", #x, strerror(ret)); \ - } while (0) - -#define PTHREAD_REQUIRE_STATUS(x, v) \ - do { \ - int ret = (x); \ - ATF_REQUIRE_MSG(ret == (v), "%s: %s", #x, strerror(ret)); \ - } while (0) - -static int __used -dummy_proc_read(void *arg, caddr_t addr, void *buf, size_t size) -{ - return TD_ERR_ERR; -} - -static int __used -dummy_proc_write(void *arg, caddr_t addr, void *buf, size_t size) -{ - return TD_ERR_ERR; -} - -static int __used -dummy_proc_lookup(void *arg, const char *sym, caddr_t *addr) -{ - return TD_ERR_ERR; -} - -static int __used -dummy_proc_regsize(void *arg, int regset, size_t *size) -{ - return TD_ERR_ERR; -} - -static int __used -dummy_proc_getregs(void *arg, int regset, int lwp, void *buf) -{ - return TD_ERR_ERR; -} - -static int __used -dummy_proc_setregs(void *arg, int regset, int lwp, void *buf) -{ - return TD_ERR_ERR; -} - -/* Minimalistic basic implementation */ - -static int __used -basic_proc_read(void *arg, caddr_t addr, void *buf, size_t size) -{ - memcpy(buf, addr, size); - - return TD_ERR_OK; -} - -static int __used -basic_proc_write(void *arg, caddr_t addr, void *buf, size_t size) -{ - memcpy(addr, buf, size); - - return TD_ERR_OK; -} - -static int __used -basic_proc_lookup(void *arg, const char *sym, caddr_t *addr) -{ - void *handle; - void *symbol; - - ATF_REQUIRE_MSG((handle = dlopen(NULL, RTLD_LOCAL | RTLD_LAZY)) - != NULL, "dlopen(3) failed: %s", dlerror()); - - symbol = dlsym(handle, sym); - - ATF_REQUIRE_MSG(dlclose(handle) == 0, "dlclose(3) failed: %s", - dlerror()); - - if (!symbol) - return TD_ERR_NOSYM; - - *addr = (caddr_t)(uintptr_t)symbol; - - return TD_ERR_OK; -} - -#endif // H_COMMON_H diff --git a/lib/libpthread_dbg/t_dummy.c b/lib/libpthread_dbg/t_dummy.c deleted file mode 100644 --- a/lib/libpthread_dbg/t_dummy.c +++ /dev/null @@ -1,131 +0,0 @@ -/* $NetBSD: t_dummy.c,v 1.6 2016/11/19 15:13:46 kamil Exp $ */ - -/*- - * Copyright (c) 2016 The NetBSD Foundation, Inc. - * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 -__RCSID("$NetBSD: t_dummy.c,v 1.6 2016/11/19 15:13:46 kamil Exp $"); - -#include "h_common.h" -#include -#include - -#include - - -ATF_TC(dummy1); -ATF_TC_HEAD(dummy1, tc) -{ - - atf_tc_set_md_var(tc, "descr", - "Asserts that dummy lookup functions stop td_open() with failure"); -} - -ATF_TC_BODY(dummy1, tc) -{ - - struct td_proc_callbacks_t dummy_callbacks; - td_proc_t *main_ta; - - dummy_callbacks.proc_read = dummy_proc_read; - dummy_callbacks.proc_write = dummy_proc_write; - dummy_callbacks.proc_lookup = dummy_proc_lookup; - dummy_callbacks.proc_regsize = dummy_proc_regsize; - dummy_callbacks.proc_getregs = dummy_proc_getregs; - dummy_callbacks.proc_setregs = dummy_proc_setregs; - - ATF_REQUIRE(td_open(&dummy_callbacks, NULL, &main_ta) == TD_ERR_ERR); -} - -ATF_TC(dummy2); -ATF_TC_HEAD(dummy2, tc) -{ - - atf_tc_set_md_var(tc, "descr", - "Asserts that td_open() for basic proc_{read,write,lookup} works"); -} - -ATF_TC_BODY(dummy2, tc) -{ - struct td_proc_callbacks_t dummy_callbacks; - td_proc_t *main_ta; - - dummy_callbacks.proc_read = basic_proc_read; - dummy_callbacks.proc_write = basic_proc_write; - dummy_callbacks.proc_lookup = basic_proc_lookup; - dummy_callbacks.proc_regsize = dummy_proc_regsize; - dummy_callbacks.proc_getregs = dummy_proc_getregs; - dummy_callbacks.proc_setregs = dummy_proc_setregs; - - printf("Calling td_open(3)\n"); - ATF_REQUIRE(td_open(&dummy_callbacks, NULL, &main_ta) == TD_ERR_OK); - - printf("Calling td_close(3)\n"); - ATF_REQUIRE(td_close(main_ta) == TD_ERR_OK); -} - -ATF_TC(dummy3); -ATF_TC_HEAD(dummy3, tc) -{ - - atf_tc_set_md_var(tc, "descr", - "Asserts that calling twice td_open() for the same process fails"); -} - -ATF_TC_BODY(dummy3, tc) -{ - struct td_proc_callbacks_t dummy_callbacks; - td_proc_t *main_ta1; - td_proc_t *main_ta2; - - dummy_callbacks.proc_read = basic_proc_read; - dummy_callbacks.proc_write = basic_proc_write; - dummy_callbacks.proc_lookup = basic_proc_lookup; - dummy_callbacks.proc_regsize = dummy_proc_regsize; - dummy_callbacks.proc_getregs = dummy_proc_getregs; - dummy_callbacks.proc_setregs = dummy_proc_setregs; - - printf("Calling td_open(3) for the first time - expecting success\n"); - ATF_REQUIRE(td_open(&dummy_callbacks, NULL, &main_ta1) == TD_ERR_OK); - - printf("Calling td_open(3) for the first time - expecting in-use\n"); - ATF_REQUIRE(td_open(&dummy_callbacks, NULL, &main_ta2) == - TD_ERR_INUSE); - - printf("Calling td_close(3) for the first successful call\n"); - ATF_REQUIRE(td_close(main_ta1) == TD_ERR_OK); -} - -ATF_TP_ADD_TCS(tp) -{ - - ATF_TP_ADD_TC(tp, dummy1); - ATF_TP_ADD_TC(tp, dummy2); - ATF_TP_ADD_TC(tp, dummy3); - - return atf_no_error(); -} diff --git a/lib/libpthread_dbg/t_threads.c b/lib/libpthread_dbg/t_threads.c deleted file mode 100644 --- a/lib/libpthread_dbg/t_threads.c +++ /dev/null @@ -1,719 +0,0 @@ -/* $NetBSD: t_threads.c,v 1.9 2017/01/13 05:18:22 christos Exp $ */ - -/*- - * Copyright (c) 2016 The NetBSD Foundation, Inc. - * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 -__RCSID("$NetBSD: t_threads.c,v 1.9 2017/01/13 05:18:22 christos Exp $"); - -#include -#include -#include -#include -#include -#include - -#include - -#include "h_common.h" - -#define MAX_THREADS (size_t)10 - -ATF_TC(threads1); -ATF_TC_HEAD(threads1, tc) -{ - - atf_tc_set_md_var(tc, "descr", - "Asserts that td_thr_iter() call without extra logic works"); -} - -static volatile int exiting1; - -static void * -busyFunction1(void *arg) -{ - - while (exiting1 == 0) - usleep(50000); - - return NULL; -} - -static int -iterateThreads1(td_thread_t *thread, void *arg) -{ - - return TD_ERR_OK; -} - -ATF_TC_BODY(threads1, tc) -{ - struct td_proc_callbacks_t dummy_callbacks; - td_proc_t *main_ta; - size_t i; - pthread_t threads[MAX_THREADS]; - - dummy_callbacks.proc_read = basic_proc_read; - dummy_callbacks.proc_write = basic_proc_write; - dummy_callbacks.proc_lookup = basic_proc_lookup; - dummy_callbacks.proc_regsize = dummy_proc_regsize; - dummy_callbacks.proc_getregs = dummy_proc_getregs; - dummy_callbacks.proc_setregs = dummy_proc_setregs; - - for (i = 0; i < MAX_THREADS; i++) { - printf("Creating thread %zu\n", i); - PTHREAD_REQUIRE - (pthread_create(&threads[i], NULL, busyFunction1, NULL)); - } - - printf("Calling td_open(3)\n"); - ATF_REQUIRE(td_open(&dummy_callbacks, NULL, &main_ta) == TD_ERR_OK); - - ATF_REQUIRE(td_thr_iter(main_ta, iterateThreads1, NULL) == TD_ERR_OK); - - exiting1 = 1; - - printf("Calling td_close(3)\n"); - ATF_REQUIRE(td_close(main_ta) == TD_ERR_OK); -} - -ATF_TC(threads2); -ATF_TC_HEAD(threads2, tc) -{ - - atf_tc_set_md_var(tc, "descr", - "Asserts that td_thr_iter() call is executed for each thread once"); -} - -static volatile int exiting2; - -static void * -busyFunction2(void *arg) -{ - - while (exiting2 == 0) - usleep(50000); - - return NULL; -} - -static int -iterateThreads2(td_thread_t *thread, void *arg) -{ - int *counter = (int *)arg; - - ++(*counter); - - return TD_ERR_OK; -} - -ATF_TC_BODY(threads2, tc) -{ - struct td_proc_callbacks_t dummy_callbacks; - td_proc_t *main_ta; - size_t i; - pthread_t threads[MAX_THREADS]; - int count = 0; - - dummy_callbacks.proc_read = basic_proc_read; - dummy_callbacks.proc_write = basic_proc_write; - dummy_callbacks.proc_lookup = basic_proc_lookup; - dummy_callbacks.proc_regsize = dummy_proc_regsize; - dummy_callbacks.proc_getregs = dummy_proc_getregs; - dummy_callbacks.proc_setregs = dummy_proc_setregs; - - - for (i = 0; i < MAX_THREADS; i++) { - printf("Creating thread %zu\n", i); - PTHREAD_REQUIRE - (pthread_create(&threads[i], NULL, busyFunction2, NULL)); - } - - printf("Calling td_open(3)\n"); - ATF_REQUIRE(td_open(&dummy_callbacks, NULL, &main_ta) == TD_ERR_OK); - - ATF_REQUIRE(td_thr_iter(main_ta, iterateThreads2, &count) == TD_ERR_OK); - - exiting2 = 1; - - printf("Calling td_close(3)\n"); - ATF_REQUIRE(td_close(main_ta) == TD_ERR_OK); - - ATF_REQUIRE_EQ_MSG(count, MAX_THREADS + 1, - "counted threads (%d) != expected threads (%zu)", - count, MAX_THREADS + 1); -} - -ATF_TC(threads3); -ATF_TC_HEAD(threads3, tc) -{ - - atf_tc_set_md_var(tc, "descr", - "Asserts that for each td_thr_iter() call td_thr_info() is valid"); -} - -static volatile int exiting3; - -static void * -busyFunction3(void *arg) -{ - - while (exiting3 == 0) - usleep(50000); - - return NULL; -} - -static int -iterateThreads3(td_thread_t *thread, void *arg) -{ - int *counter = (int *)arg; - td_thread_info_t info; - - ATF_REQUIRE(td_thr_info(thread, &info) == TD_ERR_OK); - - ++(*counter); - - return TD_ERR_OK; -} - -ATF_TC_BODY(threads3, tc) -{ - struct td_proc_callbacks_t dummy_callbacks; - td_proc_t *main_ta; - size_t i; - pthread_t threads[MAX_THREADS]; - int count = 0; - - dummy_callbacks.proc_read = basic_proc_read; - dummy_callbacks.proc_write = basic_proc_write; - dummy_callbacks.proc_lookup = basic_proc_lookup; - dummy_callbacks.proc_regsize = dummy_proc_regsize; - dummy_callbacks.proc_getregs = dummy_proc_getregs; - dummy_callbacks.proc_setregs = dummy_proc_setregs; - - - for (i = 0; i < MAX_THREADS; i++) { - printf("Creating thread %zu\n", i); - PTHREAD_REQUIRE - (pthread_create(&threads[i], NULL, busyFunction3, NULL)); - } - - printf("Calling td_open(3)\n"); - ATF_REQUIRE(td_open(&dummy_callbacks, NULL, &main_ta) == TD_ERR_OK); - - ATF_REQUIRE(td_thr_iter(main_ta, iterateThreads3, &count) == TD_ERR_OK); - - exiting3 = 1; - - printf("Calling td_close(3)\n"); - ATF_REQUIRE(td_close(main_ta) == TD_ERR_OK); - - ATF_REQUIRE_EQ_MSG(count, MAX_THREADS + 1, - "counted threads (%d) != expected threads (%zu)", - count, MAX_THREADS + 1); -} - -ATF_TC(threads4); -ATF_TC_HEAD(threads4, tc) -{ - - atf_tc_set_md_var(tc, "descr", - "Asserts that for each td_thr_iter() call td_thr_getname() is " - "valid"); -} - -static volatile int exiting4; - -static void * -busyFunction4(void *arg) -{ - - while (exiting4 == 0) - usleep(50000); - - return NULL; -} - -static int -iterateThreads4(td_thread_t *thread, void *arg) -{ - int *counter = (int *)arg; - char name[PTHREAD_MAX_NAMELEN_NP]; - - ATF_REQUIRE(td_thr_getname(thread, name, sizeof(name)) == TD_ERR_OK); - - printf("Thread name: %s\n", name); - - ++(*counter); - - return TD_ERR_OK; -} - -ATF_TC_BODY(threads4, tc) -{ - struct td_proc_callbacks_t dummy_callbacks; - td_proc_t *main_ta; - size_t i; - pthread_t threads[MAX_THREADS]; - int count = 0; - - dummy_callbacks.proc_read = basic_proc_read; - dummy_callbacks.proc_write = basic_proc_write; - dummy_callbacks.proc_lookup = basic_proc_lookup; - dummy_callbacks.proc_regsize = dummy_proc_regsize; - dummy_callbacks.proc_getregs = dummy_proc_getregs; - dummy_callbacks.proc_setregs = dummy_proc_setregs; - - for (i = 0; i < MAX_THREADS; i++) { - printf("Creating thread %zu\n", i); - PTHREAD_REQUIRE - (pthread_create(&threads[i], NULL, busyFunction4, NULL)); - } - - for (i = 0; i < MAX_THREADS; i++) { - PTHREAD_REQUIRE - (pthread_setname_np(threads[i], "test_%d", (void*)i)); - } - - printf("Calling td_open(3)\n"); - ATF_REQUIRE(td_open(&dummy_callbacks, NULL, &main_ta) == TD_ERR_OK); - - ATF_REQUIRE(td_thr_iter(main_ta, iterateThreads4, &count) == TD_ERR_OK); - - exiting4 = 1; - - printf("Calling td_close(3)\n"); - ATF_REQUIRE(td_close(main_ta) == TD_ERR_OK); - - ATF_REQUIRE_EQ_MSG(count, MAX_THREADS + 1, - "counted threads (%d) != expected threads (%zu)", - count, MAX_THREADS + 1); -} - -ATF_TC(threads5); -ATF_TC_HEAD(threads5, tc) -{ - - atf_tc_set_md_var(tc, "descr", - "Asserts that td_thr_getname() handles shorter buffer parameter " - "and the result is properly truncated"); -} - -static volatile int exiting5; - -static void * -busyFunction5(void *arg) -{ - - while (exiting5 == 0) - usleep(50000); - - return NULL; -} - -static int -iterateThreads5(td_thread_t *thread, void *arg) -{ - int *counter = (int *)arg; - /* Arbitrarily short string buffer */ - char name[3]; - - ATF_REQUIRE(td_thr_getname(thread, name, sizeof(name)) == TD_ERR_OK); - - printf("Thread name: %s\n", name); - - /* strlen(3) does not count including a '\0' character */ - ATF_REQUIRE(strlen(name) < sizeof(name)); - - ++(*counter); - - return TD_ERR_OK; -} - -ATF_TC_BODY(threads5, tc) -{ - struct td_proc_callbacks_t dummy_callbacks; - td_proc_t *main_ta; - size_t i; - pthread_t threads[MAX_THREADS]; - int count = 0; - - dummy_callbacks.proc_read = basic_proc_read; - dummy_callbacks.proc_write = basic_proc_write; - dummy_callbacks.proc_lookup = basic_proc_lookup; - dummy_callbacks.proc_regsize = dummy_proc_regsize; - dummy_callbacks.proc_getregs = dummy_proc_getregs; - dummy_callbacks.proc_setregs = dummy_proc_setregs; - - for (i = 0; i < MAX_THREADS; i++) { - printf("Creating thread %zu\n", i); - PTHREAD_REQUIRE - (pthread_create(&threads[i], NULL, busyFunction5, NULL)); - } - - for (i = 0; i < MAX_THREADS; i++) { - PTHREAD_REQUIRE - (pthread_setname_np(threads[i], "test_%d", (void*)i)); - } - - printf("Calling td_open(3)\n"); - ATF_REQUIRE(td_open(&dummy_callbacks, NULL, &main_ta) == TD_ERR_OK); - - ATF_REQUIRE(td_thr_iter(main_ta, iterateThreads5, &count) == TD_ERR_OK); - - exiting5 = 1; - - printf("Calling td_close(3)\n"); - ATF_REQUIRE(td_close(main_ta) == TD_ERR_OK); - - ATF_REQUIRE_EQ_MSG(count, MAX_THREADS + 1, - "counted threads (%d) != expected threads (%zu)", - count, MAX_THREADS + 1); -} - -ATF_TC(threads6); -ATF_TC_HEAD(threads6, tc) -{ - - atf_tc_set_md_var(tc, "descr", - "Asserts that pthread_t can be translated with td_map_pth2thr() " - "to td_thread_t -- and assert earlier that td_thr_iter() call is " - "valid"); -} - -static volatile int exiting6; - -static void * -busyFunction6(void *arg) -{ - - while (exiting6 == 0) - usleep(50000); - - return NULL; -} - -static int -iterateThreads6(td_thread_t *thread, void *arg) -{ - int *counter = (int *)arg; - - ++(*counter); - - return TD_ERR_OK; -} - -ATF_TC_BODY(threads6, tc) -{ - struct td_proc_callbacks_t dummy_callbacks; - td_proc_t *main_ta; - size_t i; - pthread_t threads[MAX_THREADS]; - int count = 0; - - dummy_callbacks.proc_read = basic_proc_read; - dummy_callbacks.proc_write = basic_proc_write; - dummy_callbacks.proc_lookup = basic_proc_lookup; - dummy_callbacks.proc_regsize = dummy_proc_regsize; - dummy_callbacks.proc_getregs = dummy_proc_getregs; - dummy_callbacks.proc_setregs = dummy_proc_setregs; - - for (i = 0; i < MAX_THREADS; i++) { - printf("Creating thread %zu\n", i); - PTHREAD_REQUIRE - (pthread_create(&threads[i], NULL, busyFunction6, NULL)); - } - - printf("Calling td_open(3)\n"); - ATF_REQUIRE(td_open(&dummy_callbacks, NULL, &main_ta) == TD_ERR_OK); - - ATF_REQUIRE(td_thr_iter(main_ta, iterateThreads6, &count) == TD_ERR_OK); - - for (i = 0; i < MAX_THREADS; i++) { - td_thread_t *td_thread; - ATF_REQUIRE(td_map_pth2thr(main_ta, threads[i], &td_thread) - == TD_ERR_OK); - } - - exiting6 = 1; - - printf("Calling td_close(3)\n"); - ATF_REQUIRE(td_close(main_ta) == TD_ERR_OK); - - ATF_REQUIRE_EQ_MSG(count, MAX_THREADS + 1, - "counted threads (%d) != expected threads (%zu)", - count, MAX_THREADS + 1); -} - -ATF_TC(threads7); -ATF_TC_HEAD(threads7, tc) -{ - - atf_tc_set_md_var(tc, "descr", - "Asserts that pthread_t can be translated with td_map_pth2thr() " - "to td_thread_t -- and assert later that td_thr_iter() call is " - "valid"); -} - -static volatile int exiting7; - -static void * -busyFunction7(void *arg) -{ - - while (exiting7 == 0) - usleep(50000); - - return NULL; -} - -static int -iterateThreads7(td_thread_t *thread, void *arg) -{ - int *counter = (int *)arg; - - ++(*counter); - - return TD_ERR_OK; -} - -ATF_TC_BODY(threads7, tc) -{ - struct td_proc_callbacks_t dummy_callbacks; - td_proc_t *main_ta; - size_t i; - pthread_t threads[MAX_THREADS]; - int count = 0; - - dummy_callbacks.proc_read = basic_proc_read; - dummy_callbacks.proc_write = basic_proc_write; - dummy_callbacks.proc_lookup = basic_proc_lookup; - dummy_callbacks.proc_regsize = dummy_proc_regsize; - dummy_callbacks.proc_getregs = dummy_proc_getregs; - dummy_callbacks.proc_setregs = dummy_proc_setregs; - - for (i = 0; i < MAX_THREADS; i++) { - printf("Creating thread %zu\n", i); - PTHREAD_REQUIRE - (pthread_create(&threads[i], NULL, busyFunction7, NULL)); - } - - printf("Calling td_open(3)\n"); - ATF_REQUIRE(td_open(&dummy_callbacks, NULL, &main_ta) == TD_ERR_OK); - - for (i = 0; i < MAX_THREADS; i++) { - td_thread_t *td_thread; - ATF_REQUIRE(td_map_pth2thr(main_ta, threads[i], &td_thread) - == TD_ERR_OK); - } - - ATF_REQUIRE(td_thr_iter(main_ta, iterateThreads7, &count) == TD_ERR_OK); - - exiting7 = 1; - - printf("Calling td_close(3)\n"); - ATF_REQUIRE(td_close(main_ta) == TD_ERR_OK); - - ATF_REQUIRE_EQ_MSG(count, MAX_THREADS + 1, - "counted threads (%d) != expected threads (%zu)", - count, MAX_THREADS + 1); -} - -ATF_TC(threads8); -ATF_TC_HEAD(threads8, tc) -{ - - atf_tc_set_md_var(tc, "descr", - "Asserts that pthread_t can be translated with td_map_pth2thr() " - "to td_thread_t -- compare thread's name of pthread_t and " - "td_thread_t"); -} - -static volatile int exiting8; - -static void * -busyFunction8(void *arg) -{ - - while (exiting8 == 0) - usleep(50000); - - return NULL; -} - -static int -iterateThreads8(td_thread_t *thread, void *arg) -{ - int *counter = (int *)arg; - - ++(*counter); - - return TD_ERR_OK; -} - -ATF_TC_BODY(threads8, tc) -{ - struct td_proc_callbacks_t dummy_callbacks; - td_proc_t *main_ta; - size_t i; - pthread_t threads[MAX_THREADS]; - int count = 0; - - dummy_callbacks.proc_read = basic_proc_read; - dummy_callbacks.proc_write = basic_proc_write; - dummy_callbacks.proc_lookup = basic_proc_lookup; - dummy_callbacks.proc_regsize = dummy_proc_regsize; - dummy_callbacks.proc_getregs = dummy_proc_getregs; - dummy_callbacks.proc_setregs = dummy_proc_setregs; - - for (i = 0; i < MAX_THREADS; i++) { - printf("Creating thread %zu\n", i); - PTHREAD_REQUIRE - (pthread_create(&threads[i], NULL, busyFunction8, NULL)); - } - - printf("Calling td_open(3)\n"); - ATF_REQUIRE(td_open(&dummy_callbacks, NULL, &main_ta) == TD_ERR_OK); - - ATF_REQUIRE(td_thr_iter(main_ta, iterateThreads8, &count) == TD_ERR_OK); - - for (i = 0; i < MAX_THREADS; i++) { - td_thread_t *td_thread; - char td_threadname[PTHREAD_MAX_NAMELEN_NP]; - char pth_threadname[PTHREAD_MAX_NAMELEN_NP]; - ATF_REQUIRE(td_map_pth2thr(main_ta, threads[i], &td_thread) - == TD_ERR_OK); - ATF_REQUIRE(td_thr_getname(td_thread, td_threadname, - sizeof(td_threadname)) == TD_ERR_OK); - PTHREAD_REQUIRE(pthread_getname_np(threads[i], pth_threadname, - sizeof(pth_threadname))); - ATF_REQUIRE(strcmp(td_threadname, pth_threadname) == 0); - } - - exiting8 = 1; - - printf("Calling td_close(3)\n"); - ATF_REQUIRE(td_close(main_ta) == TD_ERR_OK); - - ATF_REQUIRE_EQ_MSG(count, MAX_THREADS + 1, - "counted threads (%d) != expected threads (%zu)", - count, MAX_THREADS + 1); -} - -ATF_TC(threads9); -ATF_TC_HEAD(threads9, tc) -{ - - atf_tc_set_md_var(tc, "descr", - "Asserts that pthread_t can be translated with td_map_pth2thr() " - "to td_thread_t -- assert that thread is in the TD_STATE_RUNNING " - "state"); -} - -static volatile int exiting9; - -static void * -busyFunction9(void *arg) -{ - - while (exiting9 == 0) - usleep(50000); - - return NULL; -} - -static int -iterateThreads9(td_thread_t *thread, void *arg) -{ - int *counter = (int *)arg; - - ++(*counter); - - return TD_ERR_OK; -} - -ATF_TC_BODY(threads9, tc) -{ - struct td_proc_callbacks_t dummy_callbacks; - td_proc_t *main_ta; - size_t i; - pthread_t threads[MAX_THREADS]; - int count = 0; - - dummy_callbacks.proc_read = basic_proc_read; - dummy_callbacks.proc_write = basic_proc_write; - dummy_callbacks.proc_lookup = basic_proc_lookup; - dummy_callbacks.proc_regsize = dummy_proc_regsize; - dummy_callbacks.proc_getregs = dummy_proc_getregs; - dummy_callbacks.proc_setregs = dummy_proc_setregs; - - for (i = 0; i < MAX_THREADS; i++) { - printf("Creating thread %zu\n", i); - PTHREAD_REQUIRE - (pthread_create(&threads[i], NULL, busyFunction9, NULL)); - } - - printf("Calling td_open(3)\n"); - ATF_REQUIRE(td_open(&dummy_callbacks, NULL, &main_ta) == TD_ERR_OK); - - for (i = 0; i < MAX_THREADS; i++) { - td_thread_t *td_thread; - td_thread_info_t info; - ATF_REQUIRE(td_map_pth2thr(main_ta, threads[i], &td_thread) - == TD_ERR_OK); - ATF_REQUIRE(td_thr_info(td_thread, &info) == TD_ERR_OK); - ATF_REQUIRE_EQ(info.thread_state, TD_STATE_RUNNING); - } - - ATF_REQUIRE(td_thr_iter(main_ta, iterateThreads9, &count) == TD_ERR_OK); - - exiting9 = 1; - - printf("Calling td_close(3)\n"); - ATF_REQUIRE(td_close(main_ta) == TD_ERR_OK); - - ATF_REQUIRE_EQ_MSG(count, MAX_THREADS + 1, - "counted threads (%d) != expected threads (%zu)", - count, MAX_THREADS + 1); -} - -ATF_TP_ADD_TCS(tp) -{ - - ATF_TP_ADD_TC(tp, threads1); - ATF_TP_ADD_TC(tp, threads2); - ATF_TP_ADD_TC(tp, threads3); - ATF_TP_ADD_TC(tp, threads4); - ATF_TP_ADD_TC(tp, threads5); - ATF_TP_ADD_TC(tp, threads6); - ATF_TP_ADD_TC(tp, threads7); - ATF_TP_ADD_TC(tp, threads8); - ATF_TP_ADD_TC(tp, threads9); - - return atf_no_error(); -} diff --git a/lib/librefuse/Makefile b/lib/librefuse/Makefile new file mode 100644 --- /dev/null +++ b/lib/librefuse/Makefile @@ -0,0 +1,14 @@ +# $NetBSD: Makefile,v 1.1 2016/11/14 16:10:31 pho Exp $ + +NOMAN= # defined + +.include + +TESTSDIR= ${TESTSBASE}/lib/librefuse + +DPADD+= ${LIBREFUSE} +LDADD+= -lrefuse + +TESTS_C= t_refuse_opt + +.include diff --git a/lib/librt/t_sched.c b/lib/librt/t_sched.c --- a/lib/librt/t_sched.c +++ b/lib/librt/t_sched.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_sched.c,v 1.5 2012/03/25 04:11:42 christos Exp $ */ +/* $NetBSD: t_sched.c,v 1.6 2017/12/24 17:37:23 christos Exp $ */ /*- * Copyright (c) 2011 The NetBSD Foundation, Inc. @@ -29,8 +29,9 @@ * POSSIBILITY OF SUCH DAMAGE. */ #include -__RCSID("$NetBSD: t_sched.c,v 1.5 2012/03/25 04:11:42 christos Exp $"); +__RCSID("$NetBSD: t_sched.c,v 1.6 2017/12/24 17:37:23 christos Exp $"); +#include /* PRI_NONE */ #include #include #include @@ -94,10 +95,14 @@ pmax = sched_get_priority_max(pol[i]); pmin = sched_get_priority_min(pol[i]); - - ATF_REQUIRE(pmax != -1); - ATF_REQUIRE(pmin != -1); - ATF_REQUIRE(pmax > pmin); + if (pol[i] == SCHED_OTHER) { + ATF_REQUIRE(pmax == PRI_NONE); + ATF_REQUIRE(pmin == PRI_NONE); + } else { + ATF_REQUIRE(pmax != -1); + ATF_REQUIRE(pmin != -1); + ATF_REQUIRE(pmax > pmin); + } } } diff --git a/lib/librt/t_sem.c b/lib/librt/t_sem.c --- a/lib/librt/t_sem.c +++ b/lib/librt/t_sem.c @@ -1,7 +1,7 @@ -/* $NetBSD: t_sem.c,v 1.3 2017/01/14 20:58:20 christos Exp $ */ +/* $NetBSD: t_sem.c,v 1.5 2020/05/14 08:34:19 msaitoh Exp $ */ /* - * Copyright (c) 2008, 2010 The NetBSD Foundation, Inc. + * Copyright (c) 2008, 2010, 2019 The NetBSD Foundation, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -56,10 +56,11 @@ */ #include -__COPYRIGHT("@(#) Copyright (c) 2008, 2010\ +__COPYRIGHT("@(#) Copyright (c) 2008, 2010, 2019\ The NetBSD Foundation, inc. All rights reserved."); -__RCSID("$NetBSD: t_sem.c,v 1.3 2017/01/14 20:58:20 christos Exp $"); +__RCSID("$NetBSD: t_sem.c,v 1.5 2020/05/14 08:34:19 msaitoh Exp $"); +#include #include #include @@ -173,11 +174,152 @@ (void)sem_unlink("/sem_a"); } +ATF_TC_WITH_CLEANUP(pshared); +ATF_TC_HEAD(pshared, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks using pshared unnamed " + "semaphores to synchronize a master with multiple slave processes"); +} + +struct shared_region { + sem_t the_sem; +}; + +static struct shared_region * +get_shared_region(int o_flags) +{ + + int fd = shm_open("/shm_semtest_a", o_flags, 0644); + ATF_REQUIRE(fd != -1); + + ATF_REQUIRE_EQ(ftruncate(fd, sizeof(struct shared_region)), 0); + + void *rv = mmap(NULL, sizeof(struct shared_region), + PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + ATF_REQUIRE(rv != MAP_FAILED); + + (void)close(fd); + + return rv; +} + +static void +put_shared_region(struct shared_region *r) +{ + ATF_REQUIRE_EQ(munmap(r, sizeof(struct shared_region)), 0); +} + +ATF_TC_BODY(pshared, tc) +{ + struct shared_region *master_region, *slave_region; + + if (sysconf(_SC_SEMAPHORES) == -1) + atf_tc_skip("POSIX semaphores not supported"); + + /* + * Create a shared memory region to contain the pshared + * semaphore, create the semaphore there, and then detach + * from the shared memory region to ensure that our child + * processes will be getting at it from scratch. + */ + master_region = get_shared_region(O_RDWR | O_CREAT | O_EXCL); + ATF_REQUIRE(sem_init(&master_region->the_sem, 1, 0) == 0); + put_shared_region(master_region); + + /* + * Now execute a test that's essentially equivalent to the + * "child" test above, but using the pshared semaphore in the + * shared memory region. + */ + + pid_t pid, children[NCHILDREN]; + unsigned i, j; + int status; + + for (j = 1; j <= 2; j++) { + for (i = 0; i < NCHILDREN; i++) { + switch ((pid = fork())) { + case -1: + atf_tc_fail("fork() returned -1"); + case 0: + slave_region = get_shared_region(O_RDWR); + printf("PID %d waiting for semaphore...\n", + getpid()); + ATF_REQUIRE_MSG(sem_wait(&slave_region->the_sem) + == 0, + "sem_wait failed; iteration %d", j); + printf("PID %d got semaphore\n", getpid()); + _exit(0); + default: + children[i] = pid; + break; + } + } + + master_region = get_shared_region(O_RDWR); + + for (i = 0; i < NCHILDREN; i++) { + sleep(1); + printf("main loop %d: posting...\n", j); + ATF_REQUIRE_EQ(sem_post(&master_region->the_sem), 0); + } + + put_shared_region(master_region); + + for (i = 0; i < NCHILDREN; i++) { + ATF_REQUIRE_EQ(waitpid(children[i], &status, 0), children[i]); + ATF_REQUIRE(WIFEXITED(status)); + ATF_REQUIRE_EQ(WEXITSTATUS(status), 0); + } + } + + master_region = get_shared_region(O_RDWR); + ATF_REQUIRE_EQ(sem_destroy(&master_region->the_sem), 0); + put_shared_region(master_region); + + ATF_REQUIRE_EQ(shm_unlink("/shm_semtest_a"), 0); +} +ATF_TC_CLEANUP(pshared, tc) +{ + /* + * The kernel will g/c the pshared semaphore when the process that + * created it exits, so no need to include that in the cleanup here. + */ + (void)shm_unlink("/shm_semtest_a"); +} + +ATF_TC_WITH_CLEANUP(invalid_ops); +ATF_TC_HEAD(invalid_ops, tc) +{ + atf_tc_set_md_var(tc, "descr", "Validates behavior when calling " + "bad operations for the semaphore type"); +} +ATF_TC_BODY(invalid_ops, tc) +{ + sem_t *sem; + sem_t the_sem; + + sem = sem_open("/sem_c", O_CREAT | O_EXCL, 0644, 0); + ATF_REQUIRE(sem != SEM_FAILED); + ATF_REQUIRE(sem_destroy(sem) == -1 && errno == EINVAL); + ATF_REQUIRE_EQ(sem_close(sem), 0); + + ATF_REQUIRE_EQ(sem_init(&the_sem, 0, 0), 0); + ATF_REQUIRE(sem_close(&the_sem) == -1 && errno == EINVAL); + ATF_REQUIRE_EQ(sem_destroy(&the_sem), 0); +} +ATF_TC_CLEANUP(invalid_ops, tc) +{ + (void)sem_unlink("/sem_c"); +} + ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, basic); ATF_TP_ADD_TC(tp, child); + ATF_TP_ADD_TC(tp, pshared); + ATF_TP_ADD_TC(tp, invalid_ops); return atf_no_error(); } diff --git a/lib/librumpclient/Makefile b/lib/librumpclient/Makefile --- a/lib/librumpclient/Makefile +++ b/lib/librumpclient/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.9 2016/10/06 20:14:11 christos Exp $ +# $NetBSD: Makefile,v 1.10 2019/05/13 17:55:08 bad Exp $ # .include @@ -17,8 +17,6 @@ CPPFLAGS+= -D_KERNTYPES LDADD+= -lrumpclient -LDADD+= -lrumpdev -LDADD+= -lrumpvfs LDADD.h_execthr= -lpthread .include diff --git a/lib/librumpclient/t_exec.sh b/lib/librumpclient/t_exec.sh old mode 100755 new mode 100644 --- a/lib/librumpclient/t_exec.sh +++ b/lib/librumpclient/t_exec.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_exec.sh,v 1.9 2016/08/10 21:10:18 kre Exp $ +# $NetBSD: t_exec.sh,v 1.12 2020/11/08 08:54:50 martin Exp $ # # Copyright (c) 2011 The NetBSD Foundation, Inc. # All rights reserved. @@ -25,7 +25,7 @@ # POSSIBILITY OF SUCH DAMAGE. # -rumpsrv='rump_server -lrumpnet -lrumpnet_net -lrumpnet_netinet -lrumpdev -lrumpvfs' +rumpsrv='rump_server -lrumpnet -lrumpnet_net -lrumpnet_netinet' export RUMP_SERVER=unix://csock export RUMPHIJACK_RETRYCONNECT='die' @@ -135,6 +135,7 @@ threxec_body() { + # atf_skip "PR 55338: triggers a scheduler bug" atf_check -s exit:0 rump_server ${RUMP_SERVER} atf_check -s exit:0 $(atf_get_srcdir)/h_execthr } diff --git a/lib/librumpclient/t_fd.c b/lib/librumpclient/t_fd.c --- a/lib/librumpclient/t_fd.c +++ b/lib/librumpclient/t_fd.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_fd.c,v 1.6 2017/01/13 21:30:41 christos Exp $ */ +/* $NetBSD: t_fd.c,v 1.7 2019/05/13 17:55:08 bad Exp $ */ /* * Copyright (c) 2011 The NetBSD Foundation, Inc. @@ -110,7 +110,7 @@ signal(SIGIO, gotsig); RZ(system("rump_server -lrumpnet -lrumpnet_net -lrumpnet_netinet " - "-lrumpdev -lrumpvfs " RUMPSERV)); + RUMPSERV)); RL(setenv("RUMP_SERVER", RUMPSERV, 1)); RL(rumpclient_init()); diff --git a/lib/librumphijack/h_client.c b/lib/librumphijack/h_client.c --- a/lib/librumphijack/h_client.c +++ b/lib/librumphijack/h_client.c @@ -1,4 +1,4 @@ -/* $NetBSD: h_client.c,v 1.8 2012/04/20 05:15:11 jruoho Exp $ */ +/* $NetBSD: h_client.c,v 1.9 2019/10/04 09:02:00 mrg Exp $ */ /* * Copyright (c) 2011 The NetBSD Foundation, Inc. @@ -71,16 +71,18 @@ errx(EXIT_FAILURE, "stdin fileno is still set"); return EXIT_SUCCESS; } else if (strcmp(argv[1], "select_allunset") == 0) { - fd_set fds; + fd_set rfds, wfds, efds; struct timeval tv; int rv; tv.tv_sec = 0; tv.tv_usec = 1; - FD_ZERO(&fds); + FD_ZERO(&rfds); + FD_ZERO(&wfds); + FD_ZERO(&efds); - rv = select(100, &fds, &fds, &fds, &tv); + rv = select(100, &rfds, &wfds, &efds, &tv); if (rv == -1) err(EXIT_FAILURE, "select"); if (rv != 0) diff --git a/lib/librumphijack/t_asyncio.sh b/lib/librumphijack/t_asyncio.sh old mode 100755 new mode 100644 diff --git a/lib/librumphijack/t_config.sh b/lib/librumphijack/t_config.sh old mode 100755 new mode 100644 diff --git a/lib/librumphijack/t_cwd.sh b/lib/librumphijack/t_cwd.sh old mode 100755 new mode 100644 diff --git a/lib/librumphijack/t_sh.sh b/lib/librumphijack/t_sh.sh old mode 100755 new mode 100644 diff --git a/lib/librumphijack/t_tcpip.sh b/lib/librumphijack/t_tcpip.sh old mode 100755 new mode 100644 --- a/lib/librumphijack/t_tcpip.sh +++ b/lib/librumphijack/t_tcpip.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_tcpip.sh,v 1.18 2016/08/13 11:22:11 christos Exp $ +# $NetBSD: t_tcpip.sh,v 1.19 2019/05/13 17:55:08 bad Exp $ # # Copyright (c) 2011 The NetBSD Foundation, Inc. # All rights reserved. @@ -25,8 +25,7 @@ # POSSIBILITY OF SUCH DAMAGE. # -rumpnetlibs="-lrumpnet -lrumpnet_net -lrumpnet_netinet6 -lrumpnet_netinet" -rumpnetsrv="rump_server $rumpnetlibs -lrumpdev" +rumpnetsrv="rump_server -lrumpnet -lrumpnet_net -lrumpnet_netinet" export RUMP_SERVER=unix://csock atf_test_case http cleanup @@ -38,7 +37,7 @@ http_body() { - atf_check -s exit:0 ${rumpnetsrv} ${RUMP_SERVER} + atf_check -s exit:0 ${rumpnetsrv} -lrumpnet_netinet6 ${RUMP_SERVER} # start bozo in daemon mode atf_check -s exit:0 env LD_PRELOAD=/usr/lib/librumphijack.so \ @@ -206,7 +205,7 @@ unset LD_PRELOAD # at least the kernel server is easier - atf_check -s exit:0 rump_server -lrumpvfs -lrumpnet -lrumpdev \ + atf_check -s exit:0 rump_server -lrumpvfs -lrumpnet \ -lrumpnet_net -lrumpnet_netinet -lrumpnet_shmif -lrumpfs_nfs\ ${RUMP_SERVER} @@ -233,7 +232,7 @@ nfs_body() { test_nfs -lrumpvfs -lrumpdev -lrumpnet -lrumpnet_net \ - -lrumpnet_netinet -lrumpnet_local -lrumpnet_shmif -lrumpdev \ + -lrumpnet_netinet -lrumpnet_local -lrumpnet_shmif \ -lrumpdev_disk -lrumpfs_ffs -lrumpfs_nfs -lrumpfs_nfsserver \ -d key=/dk,hostpath=ffs.img,size=host } @@ -255,7 +254,7 @@ { [ `uname -m` = "i386" ] || atf_skip "test currently valid only on i386" test_nfs -lrumpvfs -lrumpdev -lrumpnet -lrumpnet_net \ - -lrumpnet_netinet -lrumpnet_local -lrumpnet_shmif -lrumpdev \ + -lrumpnet_netinet -lrumpnet_local -lrumpnet_shmif \ -lrumpdev_disk -d key=/dk,hostpath=ffs.img,size=host } diff --git a/lib/librumphijack/t_vfs.sh b/lib/librumphijack/t_vfs.sh old mode 100755 new mode 100644 diff --git a/lib/libutil/t_parsedate.c b/lib/libutil/t_parsedate.c --- a/lib/libutil/t_parsedate.c +++ b/lib/libutil/t_parsedate.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_parsedate.c,v 1.25 2016/06/22 15:01:38 kre Exp $ */ +/* $NetBSD: t_parsedate.c,v 1.31 2020/10/19 15:06:49 kre Exp $ */ /*- * Copyright (c) 2010, 2015 The NetBSD Foundation, Inc. * All rights reserved. @@ -29,7 +29,7 @@ */ #include -__RCSID("$NetBSD: t_parsedate.c,v 1.25 2016/06/22 15:01:38 kre Exp $"); +__RCSID("$NetBSD: t_parsedate.c,v 1.31 2020/10/19 15:06:49 kre Exp $"); #include #include @@ -84,6 +84,9 @@ ATF_CHECK_MSG((t = parsedate(datestr, reftime, zoff)) != -1, "parsedate(%s) returned -1\n", argstr); + if (t == -1) + return; + ATF_CHECK(time_to_tm(&t, &tm) != NULL); if (year != ANY) ATF_CHECK_MSG(tm.tm_year + 1900 == year, @@ -122,12 +125,12 @@ ATF_TC_BODY(dates, tc) { + parsecheck("9/10/68", NULL, NULL, localtime_r, + 2068, 9, 10, 0, 0, 0); /* year < 69: add 2000 */ parsecheck("9/10/69", NULL, NULL, localtime_r, - 2069, 9, 10, 0, 0, 0); /* year < 70: add 2000 */ - parsecheck("9/10/70", NULL, NULL, localtime_r, - 1970, 9, 10, 0, 0, 0); /* 70 <= year < 100: add 1900 */ - parsecheck("69-09-10", NULL, NULL, localtime_r, - 69, 9, 10, 0, 0, 0); /* ISO8601 year remains unchanged */ + 1969, 9, 10, 0, 0, 0); /* 69 <= year < 100: add 1900 */ + parsecheck("68-09-10", NULL, NULL, localtime_r, + 68, 9, 10, 0, 0, 0); /* ISO8601 year remains unchanged */ parsecheck("70-09-10", NULL, NULL, localtime_r, 70, 9, 10, 0, 0, 0); /* ISO8601 year remains unchanged */ parsecheck("2006-11-17", NULL, NULL, localtime_r, @@ -180,6 +183,51 @@ ANY, ANY, ANY, 0, 0, 0); parsecheck("noon", NULL, NULL, localtime_r, ANY, ANY, ANY, 12, 0, 0); + + /* + * The following tests used to trigger the bug from PR lib/52101 + * but that is fixed now. + * + atf_tc_expect_fail("PR lib/52101"); + */ + + parsecheck("12:30 am", NULL, NULL, localtime_r, + ANY, ANY, ANY, 0, 30, 0); + parsecheck("12:30 pm", NULL, NULL, localtime_r, + ANY, ANY, ANY, 12, 30, 0); + + /* + * Technically, these are invalid, noon and midnight + * are neither am, nor pm, but this is what people expect... + */ + parsecheck("12:00:00 am", NULL, NULL, localtime_r, + ANY, ANY, ANY, 0, 0, 0); + parsecheck("12:00:00 pm", NULL, NULL, localtime_r, + ANY, ANY, ANY, 12, 0, 0); + parsecheck("12am", NULL, NULL, localtime_r, + ANY, ANY, ANY, 0, 0, 0); + parsecheck("12pm", NULL, NULL, localtime_r, + ANY, ANY, ANY, 12, 0, 0); + + /* end 52101 bug tests */ + + parsecheck("12 noon", NULL, NULL, localtime_r, + ANY, ANY, ANY, 12, 0, 0); + parsecheck("12 midnight", NULL, NULL, localtime_r, + ANY, ANY, ANY, 0, 0, 0); + parsecheck("12 midday", NULL, NULL, localtime_r, /* unlikely! */ + ANY, ANY, ANY, 12, 0, 0); + parsecheck("12 mn", NULL, NULL, localtime_r, + ANY, ANY, ANY, 0, 0, 0); + + parsecheck("12:00 noon", NULL, NULL, localtime_r, + ANY, ANY, ANY, 12, 0, 0); + parsecheck("12:00 midnight", NULL, NULL, localtime_r, + ANY, ANY, ANY, 0, 0, 0); + parsecheck("12:00:00 noon", NULL, NULL, localtime_r, + ANY, ANY, ANY, 12, 0, 0); + parsecheck("12:00:00 midnight", NULL, NULL, localtime_r, + ANY, ANY, ANY, 0, 0, 0); } ATF_TC(dsttimes); diff --git a/lib/libutil/t_snprintb.c b/lib/libutil/t_snprintb.c --- a/lib/libutil/t_snprintb.c +++ b/lib/libutil/t_snprintb.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_snprintb.c,v 1.4 2014/06/06 06:59:21 shm Exp $ */ +/* $NetBSD: t_snprintb.c,v 1.8 2019/12/08 17:37:16 christos Exp $ */ /* * Copyright (c) 2002, 2004, 2008, 2010 The NetBSD Foundation, Inc. @@ -31,7 +31,7 @@ #include __COPYRIGHT("@(#) Copyright (c) 2008, 2010\ The NetBSD Foundation, inc. All rights reserved."); -__RCSID("$NetBSD: t_snprintb.c,v 1.4 2014/06/06 06:59:21 shm Exp $"); +__RCSID("$NetBSD: t_snprintb.c,v 1.8 2019/12/08 17:37:16 christos Exp $"); #include #include @@ -39,16 +39,17 @@ #include static void -h_snprintb(const char *fmt, uint64_t val, const char *res) +h_snprintb(const char *fmt, uint64_t val, const char *expected) { - char buf[1024]; - int len, slen; + char actual[1024]; + int len, rlen; - len = snprintb(buf, sizeof(buf), fmt, val); - slen = (int) strlen(res); + len = snprintb(actual, sizeof(actual), fmt, val); + rlen = (int)strlen(actual); - ATF_REQUIRE_STREQ(res, buf); - ATF_REQUIRE_EQ(len, slen); + ATF_REQUIRE_STREQ_MSG(expected, actual, "format=%s val=%" PRIu64, + fmt, val); + ATF_REQUIRE_EQ_MSG(rlen, len, "expected=%d actual=%d", rlen, len); } ATF_TC(snprintb); @@ -59,25 +60,69 @@ ATF_TC_BODY(snprintb, tc) { h_snprintb("\10\2BITTWO\1BITONE", 3, "03"); - h_snprintb("\177\20b\0A\0\0", 0, "0x0"); - + h_snprintb("\177\20b\0A\0\0", 0, "0"); + h_snprintb("\177\20b\05NOTBOOT\0b\06FPP\0b\013SDVMA\0b\015VIDEO\0" "b\020LORES\0b\021FPA\0b\022DIAG\0b\016CACHE\0" "b\017IOCACHE\0b\022LOOPBACK\0b\04DBGCACHE\0", 0xe860, "0xe860"); + + h_snprintb("\177\20f\0\4FOO\0=\1ONE\0=\2TWO\0\0", 1, + "0x1"); + h_snprintb("\177\20f\0\4X\0=\1ONE\0=\2TWO\0\0", 1, + "0x1"); + h_snprintb("\177\020F\0\4\0:\1ONE\0:\2TWO\0\0", 1, + "0x1"); + + h_snprintb("\177\20f\0\4FOO\0=\1ONE\0=\2TWO\0\0", 2, + "0x2"); + h_snprintb("\177\20f\0\4X\0=\1ONE\0=\2TWO\0\0", 2, + "0x2"); + h_snprintb("\177\020F\0\4\0:\1ONE\0:\2TWO\0\0", 2, + "0x2"); + + h_snprintb("\177\20f\0\4FOO\0=\1ONE\0=\2TWO\0*=OTHER\0\0", 3, + "0x3"); + h_snprintb("\177\20f\0\4X\0=\1ONE\0=\2TWO\0*=Other(%jd)\0\0", 3, + "0x3"); + h_snprintb("\177\20f\0\x8X\0=\1ONE\0=\2TWO\0*=other(%jo)\0\0", 0x20, + "0x20"); + h_snprintb("\177\020F\0\4\0:\1ONE\0:\2TWO\0\0", 3, + "0x3<>"); + + h_snprintb("\177\20f\0\4Field_1\0=\1ONE\0=\2TWO\0" + "f\4\4Field_2\0=\1ONE\0=\2TWO\0\0", 0x12, + "0x12"); + + h_snprintb("\177\20f\0\4Field_1\0=\1ONE\0=\2TWO\0" + "F\x8\4\0*Field_3=%jd\0" + "f\4\4Field_2\0:\1:ONE\0:\2:TWO\0\0", 0xD12, + "0xd12"); } static void h_snprintb_m(const char *fmt, uint64_t val, int line_max, const char *res, - int res_len) + int rlen) { char buf[1024]; int len; len = snprintb_m(buf, sizeof(buf), fmt, val, line_max); - ATF_REQUIRE_EQ(len, res_len); - ATF_REQUIRE_EQ(0, memcmp(res, buf, res_len + 1)); + const char *expected = res; + char *actual = buf; + int l = 0; + for (size_t i = 0; l < rlen; i++) { + l = (int)strlen(actual); + if (l == 0) + break; + ATF_REQUIRE_STREQ_MSG(expected, actual, + "%zu: fmt=%s val=%" PRIu64, i, fmt, val); + actual += l; + expected += l; + } + + ATF_REQUIRE_EQ_MSG(rlen, len, "expected=%d actual=%d", rlen, len); } ATF_TC(snprintb_m); @@ -92,19 +137,19 @@ "b\x1fMSB\0\0", 0x800f0701, 33, - "0x800f0701\0" + "0x800f0701\0" "0x800f0701\0\0", - 62); + 60); h_snprintb_m("\177\020b\0LSB\0b\1_BITONE\0f\4\4NIBBLE2\0" "f\x10\4BURST\0=\4FOUR\0=\xfSIXTEEN\0" "b\x1fMSB\0\0", 0x800f0701, 32, - "0x800f0701\0" + "0x800f0701\0" "0x800f0701\0" "0x800f0701\0\0", - 74); + 72); } ATF_TP_ADD_TCS(tp) diff --git a/lib/semaphore/pthread/Makefile b/lib/semaphore/pthread/Makefile --- a/lib/semaphore/pthread/Makefile +++ b/lib/semaphore/pthread/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.4 2016/01/23 21:22:49 christos Exp $ +# $NetBSD: Makefile,v 1.6 2020/03/01 18:08:15 christos Exp $ .include @@ -8,7 +8,7 @@ CPPFLAGS+= -I${.CURDIR}/.. -D_KERNTYPES -LDADD+= -lrump -lrumpuser -lrump -lpthread +LDADD+= ${LIBRUMPBASE} WARNS= 4 diff --git a/libexec/ld.elf_so/Makefile b/libexec/ld.elf_so/Makefile --- a/libexec/ld.elf_so/Makefile +++ b/libexec/ld.elf_so/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.8 2014/08/25 20:40:53 joerg Exp $ +# $NetBSD: Makefile,v 1.12 2021/05/30 02:26:51 joerg Exp $ # NOMAN= # defined @@ -7,7 +7,7 @@ .if ${MKPIC} != "no" -SUBDIR+= helper_dso1 .WAIT helper_dso2 .WAIT \ +SUBDIR+= helper_dso1 helper_dso3 .WAIT helper_dso2 .WAIT \ helper_ifunc_dso \ helper_symver_dso0 .WAIT helper_symver_dso1 .WAIT \ helper_symver_dso2 .WAIT \ @@ -16,13 +16,16 @@ TESTSDIR= ${TESTSBASE}/libexec/ld.elf_so TESTS_C+= t_dlerror-cleared t_dlerror-false t_dlinfo t_dlvsym t_ifunc +TESTS_C+= t_rtld_r_debug + +COPTS.t_rtld_r_debug.c += ${${ACTIVE_CC} == "gcc" :? -Wno-maybe-uninitialized :} LDADD.t_dlerror-false= -Wl,-rpath,/var/nonexistent/lib LDADD.t_dlvsym= -Wl,-rpath,${TESTSDIR}/h_helper_symver_dso2 LDADD.t_ifunc= -Wl,-rpath,${TESTSDIR} -lutil DPADD.t_ifunc= ${LIBUTIL} -TESTS_SH+= t_df_1_noopen t_dl_symver +TESTS_SH+= t_df_1_noopen t_dl_symver t_thread_local_dtor BINDIR= ${TESTSDIR} PROGS+= h_df_1_noopen1 @@ -32,6 +35,9 @@ SRCS.h_df_1_noopen2= h_df_1_noopen.c LDADD.h_df_1_noopen2= -lpthread +PROGS+= h_thread_local_dtor +LDADD.h_thread_local_dtor= -Wl,-rpath,${TESTSDIR} -lpthread + PROGS+= h_ifunc SRCS.h_ifunc= h_ifunc.c IFUNCDIR!= cd ${.CURDIR}/helper_ifunc_dso && ${PRINTOBJDIR} diff --git a/libexec/ld.elf_so/h_ifunc.c b/libexec/ld.elf_so/h_ifunc.c --- a/libexec/ld.elf_so/h_ifunc.c +++ b/libexec/ld.elf_so/h_ifunc.c @@ -1,4 +1,4 @@ -/* $NetBSD: h_ifunc.c,v 1.1 2014/08/25 20:40:53 joerg Exp $ */ +/* $NetBSD: h_ifunc.c,v 1.3 2020/05/05 20:47:14 skrll Exp $ */ /*- * Copyright (c) 2014 The NetBSD Foundation, Inc. @@ -31,7 +31,7 @@ #include -extern int ifunc(void); +extern long long ifunc(void); int main(int argc, char **argv) @@ -39,5 +39,12 @@ if (argc != 2) return 1; - return ifunc() != atoi(argv[1]); +/* + * Not supported on hppa + */ +#if defined(__hppa__) + return 1; +#else + return ifunc() != atoll(argv[1]); +#endif } diff --git a/libexec/ld.elf_so/h_thread_local_dtor.c b/libexec/ld.elf_so/h_thread_local_dtor.c new file mode 100644 --- /dev/null +++ b/libexec/ld.elf_so/h_thread_local_dtor.c @@ -0,0 +1,93 @@ +/*- + * Copyright (c) 2017 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Joerg Sonnenberger. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +#include +#include +#include + +static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t cond1 = PTHREAD_COND_INITIALIZER; +static pthread_cond_t cond2 = PTHREAD_COND_INITIALIZER; + +static void * +thread_helper(void *arg) +{ + void (*testfunc)(void) = arg; + testfunc(); + + pthread_mutex_lock(&mutex); + pthread_cond_broadcast(&cond1); + pthread_mutex_unlock(&mutex); + + pthread_mutex_lock(&mutex); + pthread_cond_wait(&cond2, &mutex); + pthread_mutex_unlock(&mutex); + + return NULL; +} + +int +main(void) +{ + void *dso; + void (*testfunc)(void); + pthread_t thread; + + dso = dlopen("libh_helper_dso3.so", RTLD_LAZY); + if (dso == NULL) + errx(1, "%s", dlerror()); + testfunc = dlsym(dso, "testfunc"); + if (testfunc == NULL) + errx(1, "%s", dlerror()); + + pthread_mutex_lock(&mutex); + + if (pthread_create(&thread, NULL, thread_helper, testfunc)) + err(1, "pthread_create"); + + pthread_cond_wait(&cond1, &mutex); + pthread_mutex_unlock(&mutex); + + printf("before dlclose\n"); + dlclose(dso); + printf("after dlclose\n"); + dso = dlopen("libh_helper_dso3.so", RTLD_LAZY); + if (dso == NULL) + errx(1, "%s", dlerror()); + dlclose(dso); + + pthread_mutex_lock(&mutex); + pthread_cond_signal(&cond2); + pthread_mutex_unlock(&mutex); + + if (pthread_join(thread, NULL)) + err(1, "pthread_join"); + return 0; +} diff --git a/libexec/ld.elf_so/helper_dso3/Makefile b/libexec/ld.elf_so/helper_dso3/Makefile new file mode 100644 --- /dev/null +++ b/libexec/ld.elf_so/helper_dso3/Makefile @@ -0,0 +1,22 @@ +# $NetBSD: Makefile,v 1.1 2017/07/11 15:21:36 joerg Exp $ + +.include + +LIB= h_helper_dso3 +LIBISCXX= yes +SRCS= h_helper_dso3.cpp + +LIBDIR= ${TESTSBASE}/libexec/ld.elf_so +SHLIBDIR= ${TESTSBASE}/libexec/ld.elf_so +SHLIB_MAJOR= 1 + +MKSTATICLIB= no +MKPROFILE= no +MKPICINSTALL= no +MKLINT= no + +NOMAN= # defined + +CXXFLAGS+= -std=c++11 + +.include diff --git a/libexec/ld.elf_so/helper_dso3/h_helper_dso3.cpp b/libexec/ld.elf_so/helper_dso3/h_helper_dso3.cpp new file mode 100644 --- /dev/null +++ b/libexec/ld.elf_so/helper_dso3/h_helper_dso3.cpp @@ -0,0 +1,46 @@ +/*- + * Copyright (c) 2017 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Joerg Sonnenberger. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 + +struct VerboseDestructor { + const char *identifier; + VerboseDestructor(const char *identifier_) : identifier(identifier_) { + printf("in ctor: %s\n", identifier); + } + ~VerboseDestructor() { + printf("in dtor: %s\n", identifier); + } +}; + +static VerboseDestructor global_dtor("global_dtor"); + +extern "C" void testfunc(void) { + static thread_local VerboseDestructor tls_dtor("thread_local"); +} diff --git a/libexec/ld.elf_so/helper_ifunc_dso/h_helper_ifunc.c b/libexec/ld.elf_so/helper_ifunc_dso/h_helper_ifunc.c --- a/libexec/ld.elf_so/helper_ifunc_dso/h_helper_ifunc.c +++ b/libexec/ld.elf_so/helper_ifunc_dso/h_helper_ifunc.c @@ -30,23 +30,52 @@ #include #include -static int +/* + * Not supported on hppa + */ +#if !defined(__hppa__) + +static long long ifunc1(void) { - return 0xdeadbeef; + return 0xdeadbeefll; } -static int +static long long ifunc2(void) { - return 0xbeefdead; + return 0xbeefdeadll; } static __attribute__((used)) -int (*resolve_ifunc(void))(void) +long long (*resolve_ifunc(void))(void) { const char *e = getenv("USE_IFUNC2"); return e && strcmp(e, "1") == 0 ? ifunc2 : ifunc1; } +static __attribute__((used)) +long long (*resolve_ifunc2(void))(void) +{ + const char *e = getenv("USE_IFUNC2"); + return e && strcmp(e, "1") == 0 ? ifunc1 : ifunc2; +} + __ifunc(ifunc, resolve_ifunc); +__hidden_ifunc(ifunc_hidden, resolve_ifunc2); + +long long ifunc_hidden(void); +long long ifunc_plt(void); + +long long ifunc_plt(void) +{ + return ifunc_hidden(); +} + +long long (*ifunc_indirect(void))(void); + +long long (*ifunc_indirect(void))(void) +{ + return ifunc_hidden; +} +#endif diff --git a/libexec/ld.elf_so/t_df_1_noopen.sh b/libexec/ld.elf_so/t_df_1_noopen.sh old mode 100755 new mode 100644 diff --git a/libexec/ld.elf_so/t_dl_symver.sh b/libexec/ld.elf_so/t_dl_symver.sh old mode 100755 new mode 100644 diff --git a/libexec/ld.elf_so/t_dlerror-cleared.c b/libexec/ld.elf_so/t_dlerror-cleared.c --- a/libexec/ld.elf_so/t_dlerror-cleared.c +++ b/libexec/ld.elf_so/t_dlerror-cleared.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_dlerror-cleared.c,v 1.2 2017/01/13 21:30:42 christos Exp $ */ +/* $NetBSD: t_dlerror-cleared.c,v 1.3 2019/07/09 16:24:01 maya Exp $ */ /* * Copyright (c) 2009 The NetBSD Foundation, Inc. @@ -63,5 +63,5 @@ ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, rtld_dlerror_cleared); - return 0; + return atf_no_error(); } diff --git a/libexec/ld.elf_so/t_dlinfo.c b/libexec/ld.elf_so/t_dlinfo.c --- a/libexec/ld.elf_so/t_dlinfo.c +++ b/libexec/ld.elf_so/t_dlinfo.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_dlinfo.c,v 1.5 2017/01/13 21:30:42 christos Exp $ */ +/* $NetBSD: t_dlinfo.c,v 1.6 2019/07/09 16:24:01 maya Exp $ */ /* * Copyright (c) 2009 The NetBSD Foundation, Inc. @@ -116,5 +116,5 @@ ATF_TP_ADD_TC(tp, rtld_dlinfo_linkmap_inval); ATF_TP_ADD_TC(tp, rtld_dlinfo_linkmap_dlopen); ATF_TP_ADD_TC(tp, rtld_dlinfo_linkmap_dlopen_iter); - return 0; + return atf_no_error(); } diff --git a/libexec/ld.elf_so/t_ifunc.c b/libexec/ld.elf_so/t_ifunc.c --- a/libexec/ld.elf_so/t_ifunc.c +++ b/libexec/ld.elf_so/t_ifunc.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_ifunc.c,v 1.2 2017/01/13 21:30:42 christos Exp $ */ +/* $NetBSD: t_ifunc.c,v 1.9 2019/07/09 16:24:01 maya Exp $ */ /* * Copyright (c) 2014 The NetBSD Foundation, Inc. @@ -35,6 +35,12 @@ #include "h_macros.h" +#if defined( __arm__) || defined(__i386__) || defined(__x86_64__) || defined(__powerpc__) || defined(__sparc__) +#define LINKER_SUPPORT 1 +#else +#define LINKER_SUPPORT 0 +#endif + ATF_TC(rtld_ifunc); ATF_TC_HEAD(rtld_ifunc, tc) @@ -47,15 +53,18 @@ const char *envstr[] = { "0", "1" }; - int expected_result[] = { - 0xdeadbeef, 0xbeefdead + long long expected_result[] = { + 0xdeadbeefll, 0xbeefdeadll }; void *handle; - int (*sym)(void); - int result; + long long (*sym)(void); + long long result; const char *error; size_t i; + if (!LINKER_SUPPORT) + atf_tc_skip("Missing linker support for ifunc relocations"); + for (i = 0; i < __arraycount(envstr); ++i) { setenv("USE_IFUNC2", envstr[i], 1); @@ -77,7 +86,7 @@ ATF_CHECK(error == NULL); char *command; - easprintf(&command, "%s/h_ifunc %d", + easprintf(&command, "%s/h_ifunc %lld", atf_tc_get_config_var(tc, "srcdir"), expected_result[i]); if (system(command) != EXIT_SUCCESS) atf_tc_fail("Test failed; see output for details"); @@ -85,8 +94,103 @@ } } +ATF_TC(rtld_hidden_ifunc); + +ATF_TC_HEAD(rtld_hidden_ifunc, tc) +{ + atf_tc_set_md_var(tc, "descr", "hidden ifunc functions are resolved"); +} + +ATF_TC_BODY(rtld_hidden_ifunc, tc) +{ + const char *envstr[] = { + "0", "1" + }; + long long expected_result[] = { + 0xdeadbeefll, 0xbeefdeadll + }; + void *handle; + long long (*sym)(void); + long long (*(*sym2)(void))(void); + long long result; + const char *error; + size_t i; + + if (!LINKER_SUPPORT) + atf_tc_skip("Missing linker support for ifunc relocations"); + + for (i = 0; i < __arraycount(envstr); ++i) { + setenv("USE_IFUNC2", envstr[i], 1); + + handle = dlopen("libh_helper_ifunc_dso.so", RTLD_LAZY); + error = dlerror(); + ATF_CHECK(error == NULL); + ATF_CHECK(handle != NULL); + + sym = dlsym(handle, "ifunc_plt"); + error = dlerror(); + ATF_CHECK(error == NULL); + ATF_CHECK(sym != NULL); + + result = (*sym)(); + ATF_CHECK(result == expected_result[!i]); + + sym2 = dlsym(handle, "ifunc_indirect"); + error = dlerror(); + ATF_CHECK(error == NULL); + ATF_CHECK(sym2 != NULL); + + sym = (*sym2)(); + result = (*sym)(); + ATF_CHECK(result == expected_result[!i]); + + dlclose(handle); + error = dlerror(); + ATF_CHECK(error == NULL); + + char *command; + easprintf(&command, "%s/h_ifunc %lld", + atf_tc_get_config_var(tc, "srcdir"), expected_result[i]); + if (system(command) != EXIT_SUCCESS) + atf_tc_fail("Test failed; see output for details"); + free(command); + } +} + +ATF_TC(rtld_main_ifunc); +ATF_TC_HEAD(rtld_main_ifunc, tc) +{ + atf_tc_set_md_var(tc, "descr", + "ifunc functions are resolved in the executable"); +} + +#if LINKER_SUPPORT +static long long +ifunc_helper(void) +{ + return 0xdeadbeefll; +} + +static __attribute__((used)) +long long (*resolve_ifunc(void))(void) +{ + return ifunc_helper; +} +__hidden_ifunc(ifunc, resolve_ifunc); +#endif +long long ifunc(void); + +ATF_TC_BODY(rtld_main_ifunc, tc) +{ + if (!LINKER_SUPPORT) + atf_tc_skip("Missing linker support for ifunc relocations"); + ATF_CHECK(ifunc() == 0xdeadbeefll); +} + ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, rtld_ifunc); - return 0; + ATF_TP_ADD_TC(tp, rtld_hidden_ifunc); + ATF_TP_ADD_TC(tp, rtld_main_ifunc); + return atf_no_error(); } diff --git a/libexec/ld.elf_so/t_rtld_r_debug.c b/libexec/ld.elf_so/t_rtld_r_debug.c new file mode 100644 --- /dev/null +++ b/libexec/ld.elf_so/t_rtld_r_debug.c @@ -0,0 +1,164 @@ +/* $NetBSD: t_rtld_r_debug.c,v 1.3 2020/09/29 16:35:42 roy Exp $ */ + +/* + * Copyright (c) 2020 The NetBSD Foundation, Inc. + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 + +#include +#include +#include +#include + +#include "h_macros.h" + +static long int +getauxval(unsigned int type) +{ + const AuxInfo *aux; + + for (aux = _dlauxinfo(); aux->a_type != AT_NULL; ++aux) { + if (type == aux->a_type) + return aux->a_v; + } + + return 0; +} + +static Elf_Dyn * +get_dynamic_section(void) +{ + uintptr_t relocbase = (uintptr_t)~0U; + const Elf_Phdr *phdr; + Elf_Half phnum; + const Elf_Phdr *phlimit, *dynphdr; + + phdr = (void *)getauxval(AT_PHDR); + phnum = (Elf_Half)getauxval(AT_PHNUM); + + ATF_CHECK(phdr != NULL); + ATF_CHECK(phnum != (Elf_Half)~0); + + phlimit = phdr + phnum; + dynphdr = NULL; + + for (; phdr < phlimit; ++phdr) { + if (phdr->p_type == PT_DYNAMIC) + dynphdr = phdr; + if (phdr->p_type == PT_PHDR) + relocbase = (uintptr_t)phdr - phdr->p_vaddr; + } + + return (Elf_Dyn *)((uint8_t *)dynphdr->p_vaddr + relocbase); +} + +static struct r_debug * +get_rtld_r_debug(void) +{ + struct r_debug *debug = NULL; + Elf_Dyn *dynp; + + for (dynp = get_dynamic_section(); dynp->d_tag != DT_NULL; dynp++) { + if (dynp->d_tag == DT_DEBUG) { + debug = (void *)dynp->d_un.d_val; + break; + } + } + ATF_CHECK(debug != NULL); + + return debug; +} + +static void +check_r_debug_return_link_map(const char *name, struct link_map **rmap) +{ + struct r_debug *debug; + struct link_map *map; + void *loader; + bool found; + + loader = NULL; + debug = get_rtld_r_debug(); + ATF_CHECK(debug != NULL); + ATF_CHECK(debug->r_version == R_DEBUG_VERSION); + map = debug->r_map; + ATF_CHECK(map != NULL); + + for (found = false; map; map = map->l_next) { + if (strstr(map->l_name, name) != NULL) { + if (rmap) + *rmap = map; + found = true; + } else if (strstr(map->l_name, "ld.elf_so") != NULL) { + loader = (void *)map->l_addr; + } + } + ATF_CHECK(found); + ATF_CHECK(loader != NULL); + ATF_CHECK(debug->r_brk != NULL); + ATF_CHECK(debug->r_state == RT_CONSISTENT); + ATF_CHECK(debug->r_ldbase == loader); +} + +ATF_TC(self); +ATF_TC_HEAD(self, tc) +{ + atf_tc_set_md_var(tc, "descr", "check whether r_debug is well-formed"); +} +ATF_TC_BODY(self, tc) +{ + check_r_debug_return_link_map("t_rtld_r_debug", NULL); +} + +ATF_TC(dlopen); +ATF_TC_HEAD(dlopen, tc) +{ + atf_tc_set_md_var(tc, "descr", + "check whether r_debug is well-formed after an dlopen(3) call"); +} +ATF_TC_BODY(dlopen, tc) +{ + void *handle; + struct link_map *map, *r_map; + + handle = dlopen("libutil.so", RTLD_LAZY); + ATF_CHECK(handle); + + check_r_debug_return_link_map("libutil.so", &r_map); + + RZ(dlinfo(handle, RTLD_DI_LINKMAP, &map)); + + ATF_CHECK(map == r_map); + dlclose(handle); +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, self); + ATF_TP_ADD_TC(tp, dlopen); + return atf_no_error(); +} diff --git a/libexec/ld.elf_so/t_thread_local_dtor.sh b/libexec/ld.elf_so/t_thread_local_dtor.sh new file mode 100644 --- /dev/null +++ b/libexec/ld.elf_so/t_thread_local_dtor.sh @@ -0,0 +1,54 @@ +# $NetBSD: t_thread_local_dtor.sh,v 1.1 2017/07/11 15:21:36 joerg Exp $ +# +# Copyright (c) 2017 The NetBSD Foundation, Inc. +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +atf_test_case thread_local_dtor +thread_local_dtor_head() +{ + atf_set "descr" "Checks dlclose vs thread_local" +} +thread_local_dtor_body() +{ + $(atf_get_srcdir)/h_thread_local_dtor >out \ + || atf_fail "h_thread_local_dtor failed, see output of the test for details" + + cat >exp < @@ -12,15 +12,31 @@ # Atffile into it. TESTS_C= t_modctl TESTS_C+= t_builtin -LDADD= -lprop -LDADD+= -lrumpfs_kernfs -lrumpvfs -lrump -lrumpuser -lrump -lpthread +TESTS_C+= t_kcov +TESTS_C+= t_ufetchstore +CPPFLAGS.t_ufetchstore.c+=-I${.CURDIR}/ufetchstore +.for i in t_modctl t_builtin t_kcov +LDADD.${i}= -lprop +LDADD.${i}+= -lrumpfs_kernfs ${LIBRUMPBASE} +.endfor + +.if ${MACHINE} == "amd64" +TESTS_C+= t_x86_pte +.endif TESTS_SH= t_abi_uvm TESTS_SH+= t_modload +TESTS_SH+= t_klua_pr_52864 +TESTS_SH+= t_threadpool SUBDIR= k_helper SUBDIR+= k_helper2 SUBDIR+= k_helper3 SUBDIR+= k_uvm +SUBDIR+= threadpool_tester +SUBDIR+= ufetchstore +.if ${MACHINE} == "amd64" +SUBDIR+= x86_pte_tester +.endif .include diff --git a/modules/k_helper/Makefile b/modules/k_helper/Makefile --- a/modules/k_helper/Makefile +++ b/modules/k_helper/Makefile @@ -1,9 +1,9 @@ -# $NetBSD: Makefile,v 1.5 2010/07/13 21:13:28 jmmv Exp $ +# $NetBSD: Makefile,v 1.6 2020/05/01 22:24:18 christos Exp $ .include KMOD= k_helper -KMODULEDIR= ${DESTDIR}/${TESTSBASE}/modules/${KMOD} +KMODULEDIR= ${TESTSBASE}/modules/${KMOD} SRCS= k_helper.c diff --git a/modules/k_helper/k_helper.c b/modules/k_helper/k_helper.c --- a/modules/k_helper/k_helper.c +++ b/modules/k_helper/k_helper.c @@ -1,4 +1,4 @@ -/* $NetBSD: k_helper.c,v 1.6 2012/06/03 10:59:44 dsl Exp $ */ +/* $NetBSD: k_helper.c,v 1.7 2020/02/22 19:54:35 pgoyette Exp $ */ /* * Copyright (c) 2008 The NetBSD Foundation, Inc. * All rights reserved. @@ -27,12 +27,13 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: k_helper.c,v 1.6 2012/06/03 10:59:44 dsl Exp $"); +__KERNEL_RCSID(0, "$NetBSD: k_helper.c,v 1.7 2020/02/22 19:54:35 pgoyette Exp $"); #include #include #include #include +#include #include @@ -45,7 +46,6 @@ /* TODO: Change the integer variables below that represent booleans to * bools, once sysctl(8) supports CTLTYPE_BOOL nodes. */ -static struct sysctllog *clogp; static int present = 1; static int prop_str_ok; static char prop_str_val[128]; @@ -53,6 +53,11 @@ static int64_t prop_int_val; static int prop_int_load; +static struct evcnt my_counter = + EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL, "k_helper", "my_counter"); + +EVCNT_ATTACH_STATIC(my_counter); + #define K_HELPER 0x12345678 #define K_HELPER_PRESENT 0 #define K_HELPER_PROP_STR_OK 1 @@ -163,8 +168,6 @@ } else prop_int_load = -2; - sysctl_k_helper_setup(&clogp); - return 0; } @@ -173,8 +176,6 @@ k_helper_fini(void *arg) { - sysctl_teardown(&clogp); - return 0; } diff --git a/modules/k_helper2/Makefile b/modules/k_helper2/Makefile --- a/modules/k_helper2/Makefile +++ b/modules/k_helper2/Makefile @@ -1,9 +1,9 @@ -# $NetBSD: Makefile,v 1.1 2010/08/21 13:21:48 pgoyette Exp $ +# $NetBSD: Makefile,v 1.2 2020/05/01 22:24:18 christos Exp $ .include KMOD= k_helper2 -KMODULEDIR= ${DESTDIR}/${TESTSBASE}/modules/${KMOD} +KMODULEDIR= ${TESTSBASE}/modules/${KMOD} SRCS= k_helper2.c diff --git a/modules/k_uvm/Makefile b/modules/k_uvm/Makefile --- a/modules/k_uvm/Makefile +++ b/modules/k_uvm/Makefile @@ -1,9 +1,9 @@ -# $NetBSD: Makefile,v 1.1 2012/02/17 22:36:50 jmmv Exp $ +# $NetBSD: Makefile,v 1.2 2020/05/01 22:24:19 christos Exp $ .include KMOD= k_uvm -KMODULEDIR= ${DESTDIR}/${TESTSBASE}/modules/${KMOD} +KMODULEDIR= ${TESTSBASE}/modules/${KMOD} SRCS= k_uvm.c diff --git a/modules/t_abi_uvm.sh b/modules/t_abi_uvm.sh old mode 100755 new mode 100644 diff --git a/modules/t_builtin.c b/modules/t_builtin.c --- a/modules/t_builtin.c +++ b/modules/t_builtin.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_builtin.c,v 1.3 2017/01/13 21:30:42 christos Exp $ */ +/* $NetBSD: t_builtin.c,v 1.5 2020/02/22 00:18:55 kamil Exp $ */ /*- * Copyright (c) 2010 The NetBSD Foundation, Inc. All rights reserved. @@ -139,9 +139,11 @@ ATF_TC_BODY(disabledstat, tc) { - struct modstat ms[128]; + modstat_t *ms; + modstat_t m; struct iovec iov; - size_t i; + size_t len; + int count; bool found = false; rump_init(); @@ -149,16 +151,36 @@ RL(rump_sys_modctl(MODCTL_UNLOAD, kernfs)); - iov.iov_base = ms; - iov.iov_len = sizeof(ms); - RL(rump_sys_modctl(MODCTL_STAT, &iov)); + for (len = 8192; ;) { + iov.iov_base = malloc(len); + iov.iov_len = len; - for (i = 0; i < __arraycount(ms); i++) { - if (strcmp(ms[i].ms_name, kernfs) == 0) { - ATF_REQUIRE_EQ(ms[i].ms_refcnt, (u_int)-1); - found = 1; + errno = 0; + + if (rump_sys_modctl(MODCTL_STAT, &iov) != 0) { + int err = errno; + fprintf(stderr, "modctl(MODCTL_STAT) failed: %s\n", + strerror(err)); + atf_tc_fail("Failed to query module status"); + } + if (len >= iov.iov_len) + break; + free(iov.iov_base); + len = iov.iov_len; + } + + count = *(int *)iov.iov_base; + ms = (modstat_t *)((char *)iov.iov_base + sizeof(int)); + while ( count ) { + memcpy(&m, ms, sizeof(m)); + if (strcmp(m.ms_name, kernfs) == 0) { + ATF_REQUIRE_EQ(m.ms_refcnt, (u_int)-1); + found = true; break; } + ms++; + count--; + } ATF_REQUIRE(found); } diff --git a/modules/t_kcov.c b/modules/t_kcov.c new file mode 100644 --- /dev/null +++ b/modules/t_kcov.c @@ -0,0 +1,576 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2018, 2019 Andrew Turner + * + * This software was developed by SRI International and the University of + * Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-10-C-0237 + * ("CTSRD"), as part of the DARPA CRASH research programme. + * + * 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 + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#define PAGE_SIZE sysconf(_SC_PAGESIZE) + +static int +open_kcov(void) +{ + int fd; + + fd = open("/dev/kcov", O_RDWR); + if (fd == -1) + atf_tc_skip("Failed to open /dev/kcov"); + + return fd; +} + +static int +pick_unassigned_fd(int greater_than_fd) +{ + int fd2; + + fd2 = greater_than_fd; + do { + ++fd2; + } while (fcntl(fd2, F_GETFL) != -1 || errno != EBADF); + + return fd2; +} + +ATF_TC_WITHOUT_HEAD(kcov_dup2); +ATF_TC_BODY(kcov_dup2, tc) +{ + int fd1, fd2; + fd1 = open_kcov(); + + fd2 = pick_unassigned_fd(fd1); + + /* Test the dup2(2) trick used by syzkaller */ + ATF_REQUIRE_EQ(dup2(fd1, fd2), fd2); + + close(fd1); + close(fd2); +} + +ATF_TC_WITHOUT_HEAD(kcov_multiopen); +ATF_TC_BODY(kcov_multiopen, tc) +{ + int fd1, fd2; + fd1 = open_kcov(); + + fd2 = open("/dev/kcov", O_RDWR); + ATF_REQUIRE(fd2 != -1); + + close(fd1); + close(fd2); +} + +ATF_TC_WITHOUT_HEAD(kcov_open_close_open); +ATF_TC_BODY(kcov_open_close_open, tc) +{ + int fd; + + fd = open_kcov(); + close(fd); + fd = open("/dev/kcov", O_RDWR); + ATF_REQUIRE(fd != -1); + + close(fd); +} + +ATF_TC_WITHOUT_HEAD(kcov_bufsize); +ATF_TC_BODY(kcov_bufsize, tc) +{ + int fd; + uint64_t size; + fd = open_kcov(); + + size = 0; + ATF_CHECK(ioctl(fd, KCOV_IOC_SETBUFSIZE, &size) == -1); + size = 2; + ATF_CHECK(ioctl(fd, KCOV_IOC_SETBUFSIZE, &size) == 0); + + close(fd); +} + +ATF_TC_WITHOUT_HEAD(kcov_mmap); +ATF_TC_BODY(kcov_mmap, tc) +{ + void *data; + int fd; + uint64_t size = 2 * PAGE_SIZE / KCOV_ENTRY_SIZE; + + fd = open_kcov(); + + ATF_CHECK(mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, + fd, 0) == MAP_FAILED); + + ATF_REQUIRE(ioctl(fd, KCOV_IOC_SETBUFSIZE, &size) == 0); + + ATF_REQUIRE((data = mmap(NULL, 2 * PAGE_SIZE, PROT_READ | PROT_WRITE, + MAP_SHARED, fd, 0)) != MAP_FAILED); + + munmap(data, 2 * PAGE_SIZE); + + close(fd); +} + +/* This shouldn't panic */ +ATF_TC_WITHOUT_HEAD(kcov_mmap_no_munmap); +ATF_TC_BODY(kcov_mmap_no_munmap, tc) +{ + int fd; + uint64_t size = PAGE_SIZE / KCOV_ENTRY_SIZE; + + fd = open_kcov(); + + ATF_REQUIRE(ioctl(fd, KCOV_IOC_SETBUFSIZE, &size) ==0); + + ATF_CHECK(mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, + fd, 0) != MAP_FAILED); + + close(fd); +} + +ATF_TC_WITHOUT_HEAD(kcov_mmap_no_munmap_no_close); +ATF_TC_BODY(kcov_mmap_no_munmap_no_close, tc) +{ + int fd; + uint64_t size = PAGE_SIZE / KCOV_ENTRY_SIZE; + + fd = open_kcov(); + + ATF_REQUIRE(ioctl(fd, KCOV_IOC_SETBUFSIZE, &size) ==0); + + ATF_CHECK(mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, + fd, 0) != MAP_FAILED); +} + +static sem_t sem1, sem2; + +static void * +kcov_mmap_enable_thread(void *data) +{ + int fd; + uint64_t size = PAGE_SIZE / KCOV_ENTRY_SIZE; + int mode; + + fd = open_kcov(); + *(int *)data = fd; + + ATF_REQUIRE(ioctl(fd, KCOV_IOC_SETBUFSIZE, &size) ==0); + ATF_CHECK(mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, + fd, 0) != MAP_FAILED); + mode = KCOV_MODE_NONE; + ATF_CHECK(ioctl(fd, KCOV_IOC_ENABLE, &mode) == 0); + + sem_post(&sem1); + sem_wait(&sem2); + + return NULL; +} + +ATF_TC_WITHOUT_HEAD(kcov_mmap_enable_thread_close); +ATF_TC_BODY(kcov_mmap_enable_thread_close, tc) +{ + pthread_t thread; + int fd; + + sem_init(&sem1, 0, 0); + sem_init(&sem2, 0, 0); + pthread_create(&thread, NULL, + kcov_mmap_enable_thread, &fd); + sem_wait(&sem1); + close(fd); + sem_post(&sem2); + pthread_join(thread, NULL); +} + +ATF_TC_WITHOUT_HEAD(kcov_enable); +ATF_TC_BODY(kcov_enable, tc) +{ + int fd; + uint64_t size = PAGE_SIZE / KCOV_ENTRY_SIZE; + int mode; + + fd = open_kcov(); + + mode = KCOV_MODE_NONE; + ATF_CHECK(ioctl(fd, KCOV_IOC_ENABLE, &mode) == -1); + + ATF_REQUIRE(ioctl(fd, KCOV_IOC_SETBUFSIZE, &size) ==0); + + /* We need to enable before disable */ + ATF_CHECK(ioctl(fd, KCOV_IOC_DISABLE) == -1); + + /* Check enabling works only with a valid trace method */ + ATF_CHECK(ioctl(fd, KCOV_IOC_ENABLE, &mode) == 0); + ATF_CHECK(ioctl(fd, KCOV_IOC_ENABLE, &mode) == -1); + + /* Disable should only be called once */ + ATF_CHECK(ioctl(fd, KCOV_IOC_DISABLE) == 0); + ATF_CHECK(ioctl(fd, KCOV_IOC_DISABLE) == -1); + + /* Re-enabling and changing mode should also work */ + mode = KCOV_MODE_NONE; + ATF_CHECK(ioctl(fd, KCOV_IOC_ENABLE, &mode) == 0); + ATF_CHECK(ioctl(fd, KCOV_IOC_DISABLE) == 0); + mode = KCOV_MODE_TRACE_PC; + ATF_CHECK(ioctl(fd, KCOV_IOC_ENABLE, &mode) == 0); + ATF_CHECK(ioctl(fd, KCOV_IOC_DISABLE) == 0); + mode = KCOV_MODE_TRACE_CMP; + ATF_CHECK(ioctl(fd, KCOV_IOC_ENABLE, &mode) == 0); + ATF_CHECK(ioctl(fd, KCOV_IOC_DISABLE) == 0); + + close(fd); +} + +ATF_TC_WITHOUT_HEAD(kcov_enable_no_disable); +ATF_TC_BODY(kcov_enable_no_disable, tc) +{ + int fd; + uint64_t size = PAGE_SIZE / KCOV_ENTRY_SIZE; + int mode; + + fd = open_kcov(); + ATF_REQUIRE(ioctl(fd, KCOV_IOC_SETBUFSIZE, &size) ==0); + mode = KCOV_MODE_NONE; + ATF_CHECK(ioctl(fd, KCOV_IOC_ENABLE, &mode) == 0); + close(fd); +} + +ATF_TC_WITHOUT_HEAD(kcov_enable_no_disable_no_close); +ATF_TC_BODY(kcov_enable_no_disable_no_close, tc) +{ + int fd; + uint64_t size = PAGE_SIZE / KCOV_ENTRY_SIZE; + int mode; + + fd = open_kcov(); + ATF_REQUIRE(ioctl(fd, KCOV_IOC_SETBUFSIZE, &size) ==0); + mode = KCOV_MODE_NONE; + ATF_CHECK(ioctl(fd, KCOV_IOC_ENABLE, &mode) == 0); +} + +static void * +common_head_raw(bool fd_dup, int *fdp) +{ + void *data; + int fd, fd2; + uint64_t size = PAGE_SIZE / KCOV_ENTRY_SIZE; + + fd = open_kcov(); + + /* Test the dup2(2) trick used by syzkaller */ + if (fd_dup) { + fd2 = pick_unassigned_fd(fd); + ATF_REQUIRE_EQ(dup2(fd, fd2), fd2); + close(fd); + fd = fd2; + } + + ATF_REQUIRE_MSG(ioctl(fd, KCOV_IOC_SETBUFSIZE, &size) == 0, + "Unable to set the kcov buffer size"); + + data = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + ATF_REQUIRE_MSG(data != MAP_FAILED, "Unable to mmap the kcov buffer"); + + *fdp = fd; + return data; +} + +static void * +common_head(int *fdp) +{ + + return common_head_raw(false, fdp); +} + +static void +common_tail(int fd, kcov_int_t *data) +{ + + ATF_REQUIRE_MSG(munmap(__UNVOLATILE(data), PAGE_SIZE) == 0, + "Unable to unmap the kcov buffer"); + + close(fd); +} + +static void +kcov_basic(bool fd_dup, int mode) +{ + kcov_int_t *buf; + int fd; + + buf = common_head_raw(fd_dup, &fd); + ATF_REQUIRE_MSG(ioctl(fd, KCOV_IOC_ENABLE, &mode) == 0, + "Unable to enable kcov "); + + buf[0] = 0; + + sleep(0); /* XXX: Is it enough for all trace types? */ + ATF_REQUIRE_MSG(buf[0] != 0, "No records found"); + + ATF_REQUIRE_MSG(ioctl(fd, KCOV_IOC_DISABLE) == 0, + "Unable to disable kcov"); + + common_tail(fd, buf); +} + +ATF_TC_WITHOUT_HEAD(kcov_basic_pc); +ATF_TC_BODY(kcov_basic_pc, tc) +{ + + kcov_basic(false, KCOV_MODE_TRACE_PC); +} + +ATF_TC_WITHOUT_HEAD(kcov_basic_cmp); +ATF_TC_BODY(kcov_basic_cmp, tc) +{ + + atf_tc_skip("XXX: GCC8 needed"); + + kcov_basic(false, KCOV_MODE_TRACE_CMP); +} + +ATF_TC_WITHOUT_HEAD(kcov_basic_dup2_pc); +ATF_TC_BODY(kcov_basic_dup2_pc, tc) +{ + + kcov_basic(true, KCOV_MODE_TRACE_PC); +} + +ATF_TC_WITHOUT_HEAD(kcov_basic_dup2_cmp); +ATF_TC_BODY(kcov_basic_dup2_cmp, tc) +{ + + atf_tc_skip("XXX: GCC8 needed"); + + kcov_basic(true, KCOV_MODE_TRACE_CMP); +} + +ATF_TC_WITHOUT_HEAD(kcov_multienable_on_the_same_thread); +ATF_TC_BODY(kcov_multienable_on_the_same_thread, tc) +{ + kcov_int_t *buf1, *buf2; + int fd1, fd2; + int mode; + + buf1 = common_head(&fd1); + buf2 = common_head(&fd2); + mode = KCOV_MODE_NONE; + ATF_REQUIRE_MSG(ioctl(fd1, KCOV_IOC_ENABLE, &mode) == 0, + "Unable to enable kcov"); + ATF_REQUIRE_ERRNO(EBUSY, ioctl(fd2, KCOV_IOC_ENABLE, &mode) != 0); + + ATF_REQUIRE_MSG(ioctl(fd1, KCOV_IOC_DISABLE) == 0, + "Unable to disable kcov"); + + common_tail(fd1, buf1); + common_tail(fd2, buf2); +} + +static void * +thread_buffer_access_test_helper(void *ptr) +{ + kcov_int_t *buf = ptr; + + /* Test mapped buffer access from a custom thread */ + buf[0] = buf[0]; + + return NULL; +} + +ATF_TC_WITHOUT_HEAD(kcov_buffer_access_from_custom_thread); +ATF_TC_BODY(kcov_buffer_access_from_custom_thread, tc) +{ + pthread_t thread; + kcov_int_t *buf; + int fd; + int mode; + + buf = common_head(&fd); + + mode = KCOV_MODE_TRACE_PC; + ATF_REQUIRE_MSG(ioctl(fd, KCOV_IOC_ENABLE, &mode) == 0, + "Unable to enable kcov "); + + pthread_create(&thread, NULL, thread_buffer_access_test_helper, + __UNVOLATILE(buf)); + pthread_join(thread, NULL); + + ATF_REQUIRE_MSG(ioctl(fd, KCOV_IOC_DISABLE) == 0, + "Unable to disable kcov"); + + common_tail(fd, buf); +} + +static void * +thread_test_helper(void *ptr) +{ + volatile int i; + + /* It does not matter what operation is in action. */ + for (i = 0; i < 1000; i++) { + if (getpid() == 0) + break; + } + + return NULL; +} + +ATF_TC_WITHOUT_HEAD(kcov_thread); +ATF_TC_BODY(kcov_thread, tc) +{ + pthread_t thread; + kcov_int_t *buf; + int fd; + int mode; + volatile int i; + + buf = common_head(&fd); + + mode = KCOV_MODE_TRACE_PC; + ATF_REQUIRE_MSG(ioctl(fd, KCOV_IOC_ENABLE, &mode) == 0, + "Unable to enable kcov "); + + /* The thread does something, does not matter what exactly. */ + pthread_create(&thread, NULL, thread_test_helper, __UNVOLATILE(buf)); + + buf[0] = 0; + for (i = 0; i < 10000; i++) + continue; + ATF_REQUIRE_EQ_MSG(buf[0], 0, + "Records changed in blocked thread"); + + pthread_join(thread, NULL); + + ATF_REQUIRE_EQ_MSG(ioctl(fd, KCOV_IOC_DISABLE), 0, + "Unable to disable kcov"); + + common_tail(fd, buf); +} + +static void * +multiple_threads_helper(void *ptr __unused) +{ + kcov_int_t *buf; + int fd; + int mode; + + buf = common_head(&fd); + mode = KCOV_MODE_TRACE_PC; + ATF_REQUIRE_MSG(ioctl(fd, KCOV_IOC_ENABLE, &mode) == 0, + "Unable to enable kcov "); + + buf[0] = 0; + + sleep(0); + ATF_REQUIRE_MSG(buf[0] != 0, "No records found"); + + ATF_REQUIRE_MSG(ioctl(fd, KCOV_IOC_DISABLE) == 0, + "Unable to disable kcov"); + + common_tail(fd, buf); + + return NULL; +} + +static void +kcov_multiple_threads(size_t N) +{ + pthread_t thread[32]; + size_t i; + int fd; + + /* + * Check if /dev/kcov is available, if not bail out. + * Verifying it on a per-thread basis is flaky. + */ + fd = open_kcov(); + ATF_REQUIRE(close(fd) == 0); + + ATF_REQUIRE(__arraycount(thread) >= N); + + for (i = 0; i < N; i++) + pthread_create(&thread[i], NULL, multiple_threads_helper, NULL); + + for (i = 0; i < N; i++) + pthread_join(thread[i], NULL); +} + +#define KCOV_MULTIPLE_THREADS(n) \ +ATF_TC_WITHOUT_HEAD(kcov_multiple_threads##n); \ +ATF_TC_BODY(kcov_multiple_threads##n, tc) \ +{ \ + \ + kcov_multiple_threads(n); \ +} + +KCOV_MULTIPLE_THREADS(2) +KCOV_MULTIPLE_THREADS(4) +KCOV_MULTIPLE_THREADS(8) +KCOV_MULTIPLE_THREADS(16) +KCOV_MULTIPLE_THREADS(32) + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, kcov_dup2); + ATF_TP_ADD_TC(tp, kcov_multiopen); + ATF_TP_ADD_TC(tp, kcov_open_close_open); + ATF_TP_ADD_TC(tp, kcov_bufsize); + ATF_TP_ADD_TC(tp, kcov_mmap); + ATF_TP_ADD_TC(tp, kcov_mmap_no_munmap); + ATF_TP_ADD_TC(tp, kcov_mmap_no_munmap_no_close); + ATF_TP_ADD_TC(tp, kcov_enable); + ATF_TP_ADD_TC(tp, kcov_enable_no_disable); + ATF_TP_ADD_TC(tp, kcov_enable_no_disable_no_close); + ATF_TP_ADD_TC(tp, kcov_mmap_enable_thread_close); + ATF_TP_ADD_TC(tp, kcov_basic_pc); + ATF_TP_ADD_TC(tp, kcov_basic_cmp); + ATF_TP_ADD_TC(tp, kcov_basic_dup2_pc); + ATF_TP_ADD_TC(tp, kcov_basic_dup2_cmp); + ATF_TP_ADD_TC(tp, kcov_multienable_on_the_same_thread); + ATF_TP_ADD_TC(tp, kcov_buffer_access_from_custom_thread); + ATF_TP_ADD_TC(tp, kcov_thread); + ATF_TP_ADD_TC(tp, kcov_multiple_threads2); + ATF_TP_ADD_TC(tp, kcov_multiple_threads4); + ATF_TP_ADD_TC(tp, kcov_multiple_threads8); + ATF_TP_ADD_TC(tp, kcov_multiple_threads16); + ATF_TP_ADD_TC(tp, kcov_multiple_threads32); + return atf_no_error(); +} diff --git a/modules/t_klua_pr_52864.sh b/modules/t_klua_pr_52864.sh new file mode 100644 --- /dev/null +++ b/modules/t_klua_pr_52864.sh @@ -0,0 +1,59 @@ +#! /usr/bin/atf-sh +# $NetBSD: t_klua_pr_52864.sh,v 1.2 2018/01/09 15:16:02 martin Exp $ +# +# Copyright (c) 2018 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Sevan Janiyan +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +atf_test_case luastate cleanup +luastate_head() { + atf_set "descr" "Test that once lua(4) is loaded and a state is " \ + "created, system does not crash when listing states" \ + "(PR port-macppc/52864)" + atf_set "require.user" "root" +} + +luastate_body() { + err=$( modstat -e 2>&1 ) + if [ $? -gt 0 ]; then + atf_skip "${err##modstat:}" + fi + sysctl -q kern.lua.verbose + if [ $? -eq 1 ]; then + atf_check -s eq:0 modload lua + fi + atf_check -s eq:0 luactl -q create atfluastate + atf_check -s eq:0 -o ignore luactl +} + +luastate_cleanup() { + modunload lua >/dev/null 2>&1 +} + +atf_init_test_cases() { + atf_add_test_case luastate +} diff --git a/modules/t_modctl.c b/modules/t_modctl.c --- a/modules/t_modctl.c +++ b/modules/t_modctl.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_modctl.c,v 1.12 2012/08/20 08:07:52 martin Exp $ */ +/* $NetBSD: t_modctl.c,v 1.16 2020/02/22 19:54:34 pgoyette Exp $ */ /* * Copyright (c) 2008 The NetBSD Foundation, Inc. * All rights reserved. @@ -27,7 +27,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: t_modctl.c,v 1.12 2012/08/20 08:07:52 martin Exp $"); +__KERNEL_RCSID(0, "$NetBSD: t_modctl.c,v 1.16 2020/02/22 19:54:34 pgoyette Exp $"); #include #include @@ -39,18 +39,20 @@ #include #include #include +#include #include #include -enum presence_check { both_checks, stat_check, sysctl_check }; +enum presence_check { all_checks, stat_check, sysctl_check, evcnt_check }; static void check_permission(void); static bool get_modstat_info(const char *, modstat_t *); static bool get_sysctl(const char *, void *buf, const size_t); static bool k_helper_is_present_stat(void); static bool k_helper_is_present_sysctl(void); +static bool k_helper_is_present_evcnt(void); static bool k_helper_is_present(enum presence_check); static int load(prop_dictionary_t, bool, const char *, ...); static int unload(const char *, bool); @@ -61,7 +63,7 @@ /* --------------------------------------------------------------------- */ /* - * A function checking wether we are allowed to load modules currently + * A function checking whether we are allowed to load modules currently * (either the kernel is not modular, or securelevel may prevent it) */ static void @@ -84,11 +86,13 @@ { bool found; size_t len; + int count; struct iovec iov; modstat_t *ms; + modstat_t m; check_permission(); - for (len = 4096; ;) { + for (len = 8192; ;) { iov.iov_base = malloc(len); iov.iov_len = len; @@ -107,14 +111,18 @@ } found = false; - len = iov.iov_len / sizeof(modstat_t); - for (ms = (modstat_t *)iov.iov_base; len != 0 && !found; - ms++, len--) { - if (strcmp(ms->ms_name, name) == 0) { + count = *(int *)iov.iov_base; + ms = (modstat_t *)((char *)iov.iov_base + sizeof(int)); + while ( count ) { + memcpy(&m, ms, sizeof(m)); + if (strcmp(m.ms_name, name) == 0) { if (msdest != NULL) - *msdest = *ms; + memcpy(msdest, &m, sizeof(*msdest)); found = true; + break; } + ms++; + count--; } free(iov.iov_base); @@ -165,6 +173,56 @@ sizeof(present)); } +/* + * Returns a boolean indicating if the k_helper module was loaded + * successfully. This implementation uses the module's evcnt + * to do the check. + */ +static bool +k_helper_is_present_evcnt(void) +{ + const int mib[4] = {CTL_KERN, KERN_EVCNT, EVCNT_TYPE_ANY, + KERN_EVCNT_COUNT_ANY }; + int error; + size_t newlen, buflen = 0; + void *buf0, *buf = NULL; + const struct evcnt_sysctl *evs, *last_evs; + + for (;;) { + if (buflen) + buf = malloc(buflen); + error = sysctl(mib, __arraycount(mib), buf, &newlen, NULL, 0); + if (error) { + if (buf) + free(buf); + return false; + } + if (newlen <= buflen) { + buflen = newlen; + break; + } + if (buf) + free(buf); + buflen = newlen; + } + evs = buf0 = buf; + last_evs = (void *)((char *)buf + buflen); + buflen /= sizeof(uint64_t); + while (evs < last_evs + && buflen >= sizeof(*evs)/sizeof(uint64_t) + && buflen >= evs->ev_len) { + if ( strncmp(evs->ev_strings, "k_helper", evs->ev_grouplen) + == 0) { + free(buf); + return true; + } + buflen -= evs->ev_len; + evs = (const void *)((const uint64_t *)evs + evs->ev_len); + } + free(buf); + return false; +} + /* * Returns a boolean indicating if the k_helper module was loaded * successfully. The 'how' parameter specifies the implementation to @@ -176,9 +234,10 @@ bool found; switch (how) { - case both_checks: + case all_checks: found = k_helper_is_present_stat(); ATF_CHECK(k_helper_is_present_sysctl() == found); + ATF_CHECK(k_helper_is_present_evcnt() == found); break; case stat_check: @@ -189,6 +248,10 @@ found = k_helper_is_present_sysctl(); break; + case evcnt_check: + found = k_helper_is_present_evcnt(); + break; + default: found = false; assert(found); @@ -429,11 +492,11 @@ } ATF_TC_BODY(cmd_stat, tc) { - ATF_CHECK(!k_helper_is_present(both_checks)); + ATF_CHECK(!k_helper_is_present(all_checks)); load(NULL, true, "%s/k_helper/k_helper.kmod", atf_tc_get_config_var(tc, "srcdir")); - ATF_CHECK(k_helper_is_present(both_checks)); + ATF_CHECK(k_helper_is_present(all_checks)); { modstat_t ms; ATF_CHECK(get_modstat_info("k_helper", &ms)); @@ -444,7 +507,7 @@ } unload("k_helper", true); - ATF_CHECK(!k_helper_is_present(both_checks)); + ATF_CHECK(!k_helper_is_present(all_checks)); } ATF_TC_CLEANUP(cmd_stat, tc) { diff --git a/modules/t_modload.sh b/modules/t_modload.sh old mode 100755 new mode 100644 diff --git a/modules/t_threadpool.sh b/modules/t_threadpool.sh new file mode 100644 --- /dev/null +++ b/modules/t_threadpool.sh @@ -0,0 +1,140 @@ +# $NetBSD: t_threadpool.sh,v 1.1 2019/01/25 18:34:45 christos Exp $ +# +# Copyright (c) 2018 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Jason R. Thorpe. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +# Pick an arbitrary priority that is not likely to be used. +tp_pri=5 + +# The kernel test jig includes a 1 second delay in the job. We need to +# wait longer for it to complete. +job_delay=2 + +read_sysctl() { + echo "${1} = ${2}" >expout + atf_check -s eq:0 -o file:expout -e empty sysctl ${1} +} + +write_sysctl() { + atf_check -s eq:0 -o ignore -e empty sysctl -w "${1}=${2}" +} + +write_sysctl_fail() { + echo "${3}" >experr + atf_check -s eq:1 -o ignore -e file:experr sysctl -w "${1}=${2}" +} + +atf_test_case unbound cleanup +unbound_head() { + atf_set "descr" "Test unbound thread pools" + atf_set "require.user" "root" +} +unbound_body() { + modload $(atf_get_srcdir)/threadpool_tester/threadpool_tester.kmod + if [ $? -ne 0 ]; then + atf_skip "cannot load threadpool_tester.kmod" + fi + + # Ensure that the state is clean. + read_sysctl kern.threadpool_tester.test_value 0 + + # Create an unbound pool. + write_sysctl kern.threadpool_tester.get_unbound $tp_pri + + # Do it again. We expect this to fail, but the test jig will + # do some additional threadpool object lifecycle validation. + # (It will not hold the additional reference.) + write_sysctl_fail kern.threadpool_tester.get_unbound $tp_pri \ + "sysctl: kern.threadpool_tester.get_unbound: File exists" + + # Schedule the test jig job on the pool. + # Wait for a short period of time and then check that the job + # successfully ran. + write_sysctl kern.threadpool_tester.run_unbound $tp_pri + sleep $job_delay + read_sysctl kern.threadpool_tester.test_value 1 + + # ...and again. + write_sysctl kern.threadpool_tester.run_unbound $tp_pri + sleep $job_delay + read_sysctl kern.threadpool_tester.test_value 2 + + # Now destroy the threadpool. + write_sysctl kern.threadpool_tester.put_unbound $tp_pri +} +unbound_cleanup() { + modunload threadpool_tester >/dev/null 2>&1 +} + +atf_test_case percpu cleanup +percpu_head() { + atf_set "descr" "Test percpu thread pools" + atf_set "require.user" "root" +} +percpu_body() { + modload $(atf_get_srcdir)/threadpool_tester/threadpool_tester.kmod + if [ $? -ne 0 ]; then + atf_skip "cannot load threadpool_tester.kmod" + fi + + # Ensure that the state is clean. + read_sysctl kern.threadpool_tester.test_value 0 + + # Create an percpu pool. + write_sysctl kern.threadpool_tester.get_percpu $tp_pri + + # Do it again. We expect this to fail, but the test jig will + # do some additional threadpool object lifecycle validation. + # (It will not hold the additional reference.) + write_sysctl_fail kern.threadpool_tester.get_percpu $tp_pri \ + "sysctl: kern.threadpool_tester.get_percpu: File exists" + + # Schedule the test jig job on the pool. + # Wait for a short period of time and then check that the job + # successfully ran. + write_sysctl kern.threadpool_tester.run_percpu $tp_pri + sleep $job_delay + read_sysctl kern.threadpool_tester.test_value 1 + + # ...and again. + write_sysctl kern.threadpool_tester.run_percpu $tp_pri + sleep $job_delay + read_sysctl kern.threadpool_tester.test_value 2 + + # Now destroy the threadpool. + write_sysctl kern.threadpool_tester.put_percpu $tp_pri +} +percpu_cleanup() { + modunload threadpool_tester >/dev/null 2>&1 +} + +atf_init_test_cases() +{ + atf_add_test_case unbound + atf_add_test_case percpu +} diff --git a/modules/t_ufetchstore.c b/modules/t_ufetchstore.c new file mode 100644 --- /dev/null +++ b/modules/t_ufetchstore.c @@ -0,0 +1,1315 @@ +/* $NetBSD: t_ufetchstore.c,v 1.1 2019/04/15 23:41:23 christos Exp $ */ + +/* + * Copyright (c) 2019 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +__COPYRIGHT("@(#) Copyright (c) 2019\ + The NetBSD Foundation, inc. All rights reserved."); +__RCSID("$NetBSD: t_ufetchstore.c,v 1.1 2019/04/15 23:41:23 christos Exp $"); + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "common.h" + +#define mib_name "kern.ufetchstore_test.test" + +static bool module_loaded; + +#define MODULE_PATH \ + "/usr/tests/modules/ufetchstore_tester/ufetchstore_tester.kmod" +#define MODULE_NAME "ufetchstore_tester" + +#define CHECK_MODULE() \ +do { \ + load_module(); \ + if (! module_loaded) { \ + atf_tc_skip("loading '%s' module failed.", MODULE_NAME);\ + } \ +} while (/*CONSTCOND*/0) + +static void +load_module(void) +{ +#ifndef SKIP_MODULE + if (module_loaded) + return; + + modctl_load_t params = { + .ml_filename = MODULE_PATH, + .ml_flags = MODCTL_NO_PROP, + }; + + if (modctl(MODCTL_LOAD, ¶ms) != 0) { + warn("failed to load module '%s'", MODULE_PATH); + } else { + module_loaded = true; + } +#else + module_loaded = true; +#endif /* ! SKIP_MODULE */ +} + +#define UADDR(x) ((uintptr_t)(x)) + +static void +unload_module(void) +{ +#ifndef SKIP_MODULE + char module_name[] = MODULE_NAME; + + if (modctl(MODCTL_UNLOAD, module_name) != 0) { + warn("failed to unload module '%s'", MODULE_NAME); + } else { + module_loaded = false; + } +#endif /* ! SKIP_MODULE */ +} + +static unsigned long +vm_max_address_raw(void) +{ + static unsigned long max_addr = 0; + int rv; + + if (max_addr == 0) { + size_t max_addr_size = sizeof(max_addr); + rv = sysctlbyname("vm.maxaddress", &max_addr, &max_addr_size, + NULL, 0); + if (rv != 0) + err(1, "sysctlbyname('vm.maxaddress')"); + } + return max_addr; +} + +static void * +vm_max_address(void) +{ + return (void *)vm_max_address_raw(); +} + +static void * +vm_max_address_minus(unsigned int adj) +{ + return (void *)(vm_max_address_raw() - adj); +} + +static int +do_sysctl(struct ufetchstore_test_args *args) +{ + uint64_t arg_addr64 = (uintptr_t)args; + int rv; + + args->fetchstore_error = EBADF; /* poison */ + args->pointer_size = (int)sizeof(void *); + + /* + * Yes, the intent is to provide the pointer, not the structure, + * to the kernel side of the test harness. + */ + rv = sysctlbyname(mib_name, NULL, NULL, &arg_addr64, + sizeof(arg_addr64)); + if (rv != 0) { + rv = errno; + warn("sysctlbyname('%s') -> %d", mib_name, rv); + return rv; + } + return 0; +} + +static int +do_ufetch_8(const uint8_t *uaddr, uint8_t *res) +{ + struct ufetchstore_test_args args = { + .uaddr64 = UADDR(uaddr), + .test_op = OP_LOAD, + .size = 8, + }; + + ATF_REQUIRE_EQ(do_sysctl(&args), 0); + *res = args.val8; + return args.fetchstore_error; +} + +static int +do_ufetch_16(const uint16_t *uaddr, uint16_t *res) +{ + struct ufetchstore_test_args args = { + .uaddr64 = UADDR(uaddr), + .test_op = OP_LOAD, + .size = 16, + }; + + ATF_REQUIRE_EQ(do_sysctl(&args), 0); + *res = args.val16; + return args.fetchstore_error; +} + +static int +do_ufetch_32(const uint32_t *uaddr, uint32_t *res) +{ + struct ufetchstore_test_args args = { + .uaddr64 = UADDR(uaddr), + .test_op = OP_LOAD, + .size = 32, + }; + + ATF_REQUIRE_EQ(do_sysctl(&args), 0); + *res = args.val32; + return args.fetchstore_error; +} + +#ifdef _LP64 +static int +do_ufetch_64(const uint64_t *uaddr, uint64_t *res) +{ + struct ufetchstore_test_args args = { + .uaddr64 = UADDR(uaddr), + .test_op = OP_LOAD, + .size = 64, + }; + + ATF_REQUIRE_EQ(do_sysctl(&args), 0); + *res = args.val64; + return args.fetchstore_error; +} +#endif /* _LP64 */ + +static int +do_ustore_8(uint8_t *uaddr, uint8_t val) +{ + struct ufetchstore_test_args args = { + .uaddr64 = UADDR(uaddr), + .test_op = OP_STORE, + .size = 8, + .val8 = val, + }; + + ATF_REQUIRE_EQ(do_sysctl(&args), 0); + return args.fetchstore_error; +} + +static int +do_ustore_16(uint16_t *uaddr, uint16_t val) +{ + struct ufetchstore_test_args args = { + .uaddr64 = UADDR(uaddr), + .test_op = OP_STORE, + .size = 16, + .val16 = val, + }; + + ATF_REQUIRE_EQ(do_sysctl(&args), 0); + return args.fetchstore_error; +} + +static int +do_ustore_32(uint32_t *uaddr, uint32_t val) +{ + struct ufetchstore_test_args args = { + .uaddr64 = UADDR(uaddr), + .test_op = OP_STORE, + .size = 32, + .val32 = val, + }; + + ATF_REQUIRE_EQ(do_sysctl(&args), 0); + return args.fetchstore_error; +} + +#ifdef _LP64 +static int +do_ustore_64(uint64_t *uaddr, uint64_t val) +{ + struct ufetchstore_test_args args = { + .uaddr64 = UADDR(uaddr), + .test_op = OP_STORE, + .size = 64, + .val64 = val, + }; + + ATF_REQUIRE_EQ(do_sysctl(&args), 0); + return args.fetchstore_error; +} +#endif /* _LP64 */ + +static int +do_ucas_32(uint32_t *uaddr, uint32_t expected, uint32_t new, uint32_t *actualp) +{ + struct ufetchstore_test_args args = { + .uaddr64 = UADDR(uaddr), + .test_op = OP_CAS, + .size = 32, + .val32 = new, + .ea_val32 = expected, + }; + + ATF_REQUIRE_EQ(do_sysctl(&args), 0); + *actualp = args.ea_val32; + return args.fetchstore_error; +} + +#ifdef _LP64 +static int +do_ucas_64(uint64_t *uaddr, uint64_t expected, uint64_t new, uint64_t *actualp) +{ + struct ufetchstore_test_args args = { + .uaddr64 = UADDR(uaddr), + .test_op = OP_CAS, + .size = 64, + .val64 = new, + .ea_val64 = expected, + }; + + ATF_REQUIRE_EQ(do_sysctl(&args), 0); + *actualp = args.ea_val64; + return args.fetchstore_error; +} +#endif /* _LP64 */ + +struct memory_cell { + unsigned long guard0; + union { + unsigned long test_cell; +#ifdef _LP64 + uint64_t val64; +#endif + uint32_t val32[sizeof(long) / 4]; + uint16_t val16[sizeof(long) / 2]; + uint8_t val8 [sizeof(long) ]; + }; + unsigned long guard1; +}; + +#define index8 1 +#define index16 1 +#define index32 0 + +#define test_pattern8 0xa5 +#define test_pattern16 0x5a6b +#define test_pattern32 0xb01cafe1 +#ifdef _LP64 +#define test_pattern64 0xcafedeadfeedbabe +#endif + +#if _BYTE_ORDER == _LITTLE_ENDIAN +#define test_cell_val8 ((unsigned long)test_pattern8 << (index8 * NBBY)) +#define test_cell_val16 ((unsigned long)test_pattern16 << (index16 * NBBY*2)) +#define test_cell_val32 ((unsigned long)test_pattern32 << (index32 * NBBY*4)) +#ifdef _LP64 +#define test_cell_val64 ((unsigned long)test_pattern64) +#endif +#endif /* _BYTE_ORDER == _LITTLE_ENDIAN */ + +#if _BYTE_ORDER == _BIG_ENDIAN +#ifdef _LP64 +#define test_cell_val8 ((unsigned long)test_pattern8 << (56-(index8 * NBBY))) +#define test_cell_val16 ((unsigned long)test_pattern16 << (48-(index16 * NBBY*2))) +#define test_cell_val32 ((unsigned long)test_pattern32 << (32-(index32 * NBBY*4))) +#define test_cell_val64 ((unsigned long)test_pattern64) +#else /* ! _LP64 */ +#define test_cell_val8 ((unsigned long)test_pattern8 << (24-(index8 * NBBY))) +#define test_cell_val16 ((unsigned long)test_pattern16 << (16-(index16 * NBBY*2))) +#define test_cell_val32 ((unsigned long)test_pattern32) +#endif /* _LP64 */ +#endif /* #if _BYTE_ORDER == _BIG_ENDIAN */ + +#define read_test_cell(cell) (cell)->test_cell +#define write_test_cell(cell, v) (cell)->test_cell = (v) + +#define memory_cell_initializer \ + { \ + .guard0 = ULONG_MAX, \ + .test_cell = 0, \ + .guard1 = ULONG_MAX, \ + } + +static bool +memory_cell_check_guard(const struct memory_cell * const cell) +{ + return cell->guard0 == ULONG_MAX && + cell->guard1 == ULONG_MAX; +} + +ATF_TC_WITH_CLEANUP(ufetch_8); +ATF_TC_HEAD(ufetch_8, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ufetch_8 behavior"); +} +ATF_TC_BODY(ufetch_8, tc) +{ + struct memory_cell cell = memory_cell_initializer; + uint8_t res; + + CHECK_MODULE(); + + write_test_cell(&cell, test_cell_val8); + ATF_REQUIRE_EQ(do_ufetch_8(&cell.val8[index8], &res), 0); + ATF_REQUIRE(memory_cell_check_guard(&cell)); + ATF_REQUIRE(res == test_pattern8); +} +ATF_TC_CLEANUP(ufetch_8, tc) +{ + unload_module(); +} + +ATF_TC_WITH_CLEANUP(ufetch_16); +ATF_TC_HEAD(ufetch_16, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ufetch_16 behavior"); +} +ATF_TC_BODY(ufetch_16, tc) +{ + struct memory_cell cell = memory_cell_initializer; + uint16_t res; + + CHECK_MODULE(); + + write_test_cell(&cell, test_cell_val16); + ATF_REQUIRE_EQ(do_ufetch_16(&cell.val16[index16], &res), 0); + ATF_REQUIRE(memory_cell_check_guard(&cell)); + ATF_REQUIRE(res == test_pattern16); +} +ATF_TC_CLEANUP(ufetch_16, tc) +{ + unload_module(); +} + +ATF_TC_WITH_CLEANUP(ufetch_32); +ATF_TC_HEAD(ufetch_32, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ufetch_32 behavior"); +} +ATF_TC_BODY(ufetch_32, tc) +{ + struct memory_cell cell = memory_cell_initializer; + uint32_t res; + + CHECK_MODULE(); + + write_test_cell(&cell, test_cell_val32); + ATF_REQUIRE_EQ(do_ufetch_32(&cell.val32[index32], &res), 0); + ATF_REQUIRE(memory_cell_check_guard(&cell)); + ATF_REQUIRE(res == test_pattern32); +} +ATF_TC_CLEANUP(ufetch_32, tc) +{ + unload_module(); +} + +#ifdef _LP64 +ATF_TC_WITH_CLEANUP(ufetch_64); +ATF_TC_HEAD(ufetch_64, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ufetch_64 behavior"); +} +ATF_TC_BODY(ufetch_64, tc) +{ + struct memory_cell cell = memory_cell_initializer; + uint64_t res; + + CHECK_MODULE(); + + write_test_cell(&cell, test_cell_val64); + ATF_REQUIRE_EQ(do_ufetch_64(&cell.val64, &res), 0); + ATF_REQUIRE(memory_cell_check_guard(&cell)); + ATF_REQUIRE(res == test_pattern64); +} +ATF_TC_CLEANUP(ufetch_64, tc) +{ + unload_module(); +} +#endif /* _LP64 */ + +ATF_TC_WITH_CLEANUP(ufetch_8_null); +ATF_TC_HEAD(ufetch_8_null, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ufetch_8 NULL pointer behavior"); +} +ATF_TC_BODY(ufetch_8_null, tc) +{ + uint8_t res; + + CHECK_MODULE(); + + ATF_REQUIRE_EQ(do_ufetch_8(NULL, &res), EFAULT); +} +ATF_TC_CLEANUP(ufetch_8_null, tc) +{ + unload_module(); +} + +ATF_TC_WITH_CLEANUP(ufetch_16_null); +ATF_TC_HEAD(ufetch_16_null, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ufetch_16 NULL pointer behavior"); +} +ATF_TC_BODY(ufetch_16_null, tc) +{ + uint16_t res; + + CHECK_MODULE(); + + ATF_REQUIRE_EQ(do_ufetch_16(NULL, &res), EFAULT); +} +ATF_TC_CLEANUP(ufetch_16_null, tc) +{ + unload_module(); +} + +ATF_TC_WITH_CLEANUP(ufetch_32_null); +ATF_TC_HEAD(ufetch_32_null, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ufetch_32 NULL pointer behavior"); +} +ATF_TC_BODY(ufetch_32_null, tc) +{ + uint32_t res; + + CHECK_MODULE(); + + ATF_REQUIRE_EQ(do_ufetch_32(NULL, &res), EFAULT); +} +ATF_TC_CLEANUP(ufetch_32_null, tc) +{ + unload_module(); +} + +#ifdef _LP64 +ATF_TC_WITH_CLEANUP(ufetch_64_null); +ATF_TC_HEAD(ufetch_64_null, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ufetch_64 NULL pointer behavior"); +} +ATF_TC_BODY(ufetch_64_null, tc) +{ + uint64_t res; + + CHECK_MODULE(); + + ATF_REQUIRE_EQ(do_ufetch_64(NULL, &res), EFAULT); +} +ATF_TC_CLEANUP(ufetch_64_null, tc) +{ + unload_module(); +} +#endif /* _LP64 */ + +ATF_TC_WITH_CLEANUP(ufetch_8_max); +ATF_TC_HEAD(ufetch_8_max, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ufetch_8 VM_MAX_ADDRESS pointer behavior"); +} +ATF_TC_BODY(ufetch_8_max, tc) +{ + uint8_t res; + + CHECK_MODULE(); + + ATF_REQUIRE_EQ(do_ufetch_8(vm_max_address(), &res), EFAULT); +} +ATF_TC_CLEANUP(ufetch_8_max, tc) +{ + unload_module(); +} + +ATF_TC_WITH_CLEANUP(ufetch_16_max); +ATF_TC_HEAD(ufetch_16_max, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ufetch_16 VM_MAX_ADDRESS pointer behavior"); +} +ATF_TC_BODY(ufetch_16_max, tc) +{ + uint16_t res; + + CHECK_MODULE(); + + ATF_REQUIRE_EQ(do_ufetch_16(vm_max_address(), &res), EFAULT); +} +ATF_TC_CLEANUP(ufetch_16_max, tc) +{ + unload_module(); +} + +ATF_TC_WITH_CLEANUP(ufetch_32_max); +ATF_TC_HEAD(ufetch_32_max, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ufetch_32 VM_MAX_ADDRESS pointer behavior"); +} +ATF_TC_BODY(ufetch_32_max, tc) +{ + uint32_t res; + + CHECK_MODULE(); + + ATF_REQUIRE_EQ(do_ufetch_32(vm_max_address(), &res), EFAULT); +} +ATF_TC_CLEANUP(ufetch_32_max, tc) +{ + unload_module(); +} + +#ifdef _LP64 +ATF_TC_WITH_CLEANUP(ufetch_64_max); +ATF_TC_HEAD(ufetch_64_max, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ufetch_64 VM_MAX_ADDRESS pointer behavior"); +} +ATF_TC_BODY(ufetch_64_max, tc) +{ + uint64_t res; + + CHECK_MODULE(); + + ATF_REQUIRE_EQ(do_ufetch_64(vm_max_address(), &res), EFAULT); +} +ATF_TC_CLEANUP(ufetch_64_max, tc) +{ + unload_module(); +} +#endif /* _LP64 */ + +ATF_TC_WITH_CLEANUP(ufetch_16_nearmax_overflow); +ATF_TC_HEAD(ufetch_16_nearmax_overflow, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ufetch_16 near-VM_MAX_ADDRESS pointer behavior"); +} +ATF_TC_BODY(ufetch_16_nearmax_overflow, tc) +{ + uint16_t res; + + CHECK_MODULE(); + + /* + * For no-strict-alignment platforms: address checks must return + * EFAULT. + * + * For strict-alignment platforms: alignment checks must return + * EFAULT. + */ + ATF_REQUIRE_EQ(do_ufetch_16(vm_max_address_minus(1), &res), EFAULT); +} +ATF_TC_CLEANUP(ufetch_16_nearmax_overflow, tc) +{ + unload_module(); +} + +ATF_TC_WITH_CLEANUP(ufetch_32_nearmax_overflow); +ATF_TC_HEAD(ufetch_32_nearmax_overflow, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ufetch_32 near-VM_MAX_ADDRESS pointer behavior"); +} +ATF_TC_BODY(ufetch_32_nearmax_overflow, tc) +{ + uint32_t res; + + CHECK_MODULE(); + + /* + * For no-strict-alignment platforms: address checks must return + * EFAULT. + * + * For strict-alignment platforms: alignment checks must return + * EFAULT. + */ + ATF_REQUIRE_EQ(do_ufetch_32(vm_max_address_minus(3), &res), EFAULT); +} +ATF_TC_CLEANUP(ufetch_32_nearmax_overflow, tc) +{ + unload_module(); +} + +#ifdef _LP64 +ATF_TC_WITH_CLEANUP(ufetch_64_nearmax_overflow); +ATF_TC_HEAD(ufetch_64_nearmax_overflow, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ufetch_64 near-VM_MAX_ADDRESS pointer behavior"); +} +ATF_TC_BODY(ufetch_64_nearmax_overflow, tc) +{ + uint64_t res; + + CHECK_MODULE(); + + /* + * For no-strict-alignment platforms: address checks must return + * EFAULT. + * + * For strict-alignment platforms: alignment checks must return + * EFAULT. + */ + ATF_REQUIRE_EQ(do_ufetch_64(vm_max_address_minus(7), &res), EFAULT); +} +ATF_TC_CLEANUP(ufetch_64_nearmax_overflow, tc) +{ + unload_module(); +} +#endif /* _LP64 */ + + +ATF_TC_WITH_CLEANUP(ustore_8); +ATF_TC_HEAD(ustore_8, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ustore_8 behavior"); +} +ATF_TC_BODY(ustore_8, tc) +{ + struct memory_cell cell = memory_cell_initializer; + + CHECK_MODULE(); + + ATF_REQUIRE_EQ(do_ustore_8(&cell.val8[index8], test_pattern8), 0); + ATF_REQUIRE(memory_cell_check_guard(&cell)); + ATF_REQUIRE(read_test_cell(&cell) == test_cell_val8); +} +ATF_TC_CLEANUP(ustore_8, tc) +{ + unload_module(); +} + +ATF_TC_WITH_CLEANUP(ustore_16); +ATF_TC_HEAD(ustore_16, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ustore_16 behavior"); +} +ATF_TC_BODY(ustore_16, tc) +{ + struct memory_cell cell = memory_cell_initializer; + + CHECK_MODULE(); + + ATF_REQUIRE_EQ(do_ustore_16(&cell.val16[index16], test_pattern16), 0); + ATF_REQUIRE(memory_cell_check_guard(&cell)); + ATF_REQUIRE(read_test_cell(&cell) == test_cell_val16); +} +ATF_TC_CLEANUP(ustore_16, tc) +{ + unload_module(); +} + +ATF_TC_WITH_CLEANUP(ustore_32); +ATF_TC_HEAD(ustore_32, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ustore_32 behavior"); +} +ATF_TC_BODY(ustore_32, tc) +{ + struct memory_cell cell = memory_cell_initializer; + + CHECK_MODULE(); + + ATF_REQUIRE_EQ(do_ustore_32(&cell.val32[index32], test_pattern32), 0); + ATF_REQUIRE(memory_cell_check_guard(&cell)); + ATF_REQUIRE(read_test_cell(&cell) == test_cell_val32); +} +ATF_TC_CLEANUP(ustore_32, tc) +{ + unload_module(); +} + +#ifdef _LP64 +ATF_TC_WITH_CLEANUP(ustore_64); +ATF_TC_HEAD(ustore_64, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ustore_64 behavior"); +} +ATF_TC_BODY(ustore_64, tc) +{ + struct memory_cell cell = memory_cell_initializer; + + CHECK_MODULE(); + + ATF_REQUIRE_EQ(do_ustore_64(&cell.val64, test_pattern64), 0); + ATF_REQUIRE(memory_cell_check_guard(&cell)); + ATF_REQUIRE(read_test_cell(&cell) == test_cell_val64); +} +ATF_TC_CLEANUP(ustore_64, tc) +{ + unload_module(); +} +#endif /* _LP64 */ + +ATF_TC_WITH_CLEANUP(ustore_8_null); +ATF_TC_HEAD(ustore_8_null, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ustore_8 NULL pointer behavior"); +} +ATF_TC_BODY(ustore_8_null, tc) +{ + CHECK_MODULE(); + + ATF_REQUIRE_EQ(do_ustore_8(NULL, 0), EFAULT); +} +ATF_TC_CLEANUP(ustore_8_null, tc) +{ + unload_module(); +} + +ATF_TC_WITH_CLEANUP(ustore_16_null); +ATF_TC_HEAD(ustore_16_null, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ustore_16 NULL pointer behavior"); +} +ATF_TC_BODY(ustore_16_null, tc) +{ + CHECK_MODULE(); + + ATF_REQUIRE_EQ(do_ustore_16(NULL, 0), EFAULT); +} +ATF_TC_CLEANUP(ustore_16_null, tc) +{ + unload_module(); +} + +ATF_TC_WITH_CLEANUP(ustore_32_null); +ATF_TC_HEAD(ustore_32_null, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ustore_32 NULL pointer behavior"); +} +ATF_TC_BODY(ustore_32_null, tc) +{ + CHECK_MODULE(); + + ATF_REQUIRE_EQ(do_ustore_32(NULL, 0), EFAULT); +} +ATF_TC_CLEANUP(ustore_32_null, tc) +{ + unload_module(); +} + +#ifdef _LP64 +ATF_TC_WITH_CLEANUP(ustore_64_null); +ATF_TC_HEAD(ustore_64_null, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ustore_64 NULL pointer behavior"); +} +ATF_TC_BODY(ustore_64_null, tc) +{ + CHECK_MODULE(); + + ATF_REQUIRE_EQ(do_ustore_64(NULL, 0), EFAULT); +} +ATF_TC_CLEANUP(ustore_64_null, tc) +{ + unload_module(); +} +#endif /* _LP64 */ + +ATF_TC_WITH_CLEANUP(ustore_8_max); +ATF_TC_HEAD(ustore_8_max, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ustore_8 VM_MAX_ADDRESS pointer behavior"); +} +ATF_TC_BODY(ustore_8_max, tc) +{ + CHECK_MODULE(); + + ATF_REQUIRE_EQ(do_ustore_8(vm_max_address(), 0), EFAULT); +} +ATF_TC_CLEANUP(ustore_8_max, tc) +{ + unload_module(); +} + +ATF_TC_WITH_CLEANUP(ustore_16_max); +ATF_TC_HEAD(ustore_16_max, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ustore_16 VM_MAX_ADDRESS pointer behavior"); +} +ATF_TC_BODY(ustore_16_max, tc) +{ + CHECK_MODULE(); + + ATF_REQUIRE_EQ(do_ustore_16(vm_max_address(), 0), EFAULT); +} +ATF_TC_CLEANUP(ustore_16_max, tc) +{ + unload_module(); +} + +ATF_TC_WITH_CLEANUP(ustore_32_max); +ATF_TC_HEAD(ustore_32_max, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ustore_32 VM_MAX_ADDRESS pointer behavior"); +} +ATF_TC_BODY(ustore_32_max, tc) +{ + CHECK_MODULE(); + + ATF_REQUIRE_EQ(do_ustore_32(vm_max_address(), 0), EFAULT); +} +ATF_TC_CLEANUP(ustore_32_max, tc) +{ + unload_module(); +} + +#ifdef _LP64 +ATF_TC_WITH_CLEANUP(ustore_64_max); +ATF_TC_HEAD(ustore_64_max, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ustore_64 VM_MAX_ADDRESS pointer behavior"); +} +ATF_TC_BODY(ustore_64_max, tc) +{ + CHECK_MODULE(); + + ATF_REQUIRE_EQ(do_ustore_64(vm_max_address(), 0), EFAULT); +} +ATF_TC_CLEANUP(ustore_64_max, tc) +{ + unload_module(); +} +#endif /* _LP64 */ + +ATF_TC_WITH_CLEANUP(ustore_16_nearmax_overflow); +ATF_TC_HEAD(ustore_16_nearmax_overflow, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ustore_16 VM_MAX_ADDRESS pointer behavior"); +} +ATF_TC_BODY(ustore_16_nearmax_overflow, tc) +{ + CHECK_MODULE(); + + /* + * For no-strict-alignment platforms: address checks must return + * EFAULT. + * + * For strict-alignment platforms: alignment checks must return + * EFAULT. + */ + ATF_REQUIRE_EQ(do_ustore_16(vm_max_address_minus(1), 0), EFAULT); +} +ATF_TC_CLEANUP(ustore_16_nearmax_overflow, tc) +{ + unload_module(); +} + +ATF_TC_WITH_CLEANUP(ustore_32_nearmax_overflow); +ATF_TC_HEAD(ustore_32_nearmax_overflow, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ustore_32 VM_MAX_ADDRESS pointer behavior"); +} +ATF_TC_BODY(ustore_32_nearmax_overflow, tc) +{ + CHECK_MODULE(); + + /* + * For no-strict-alignment platforms: address checks must return + * EFAULT. + * + * For strict-alignment platforms: alignment checks must return + * EFAULT. + */ + ATF_REQUIRE_EQ(do_ustore_32(vm_max_address_minus(3), 0), EFAULT); +} +ATF_TC_CLEANUP(ustore_32_nearmax_overflow, tc) +{ + unload_module(); +} + +#ifdef _LP64 +ATF_TC_WITH_CLEANUP(ustore_64_nearmax_overflow); +ATF_TC_HEAD(ustore_64_nearmax_overflow, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ustore_64 VM_MAX_ADDRESS pointer behavior"); +} +ATF_TC_BODY(ustore_64_nearmax_overflow, tc) +{ + CHECK_MODULE(); + + /* + * For no-strict-alignment platforms: address checks must return + * EFAULT. + * + * For strict-alignment platforms: alignment checks must return + * EFAULT. + */ + ATF_REQUIRE_EQ(do_ustore_64(vm_max_address_minus(7), 0), EFAULT); +} +ATF_TC_CLEANUP(ustore_64_nearmax_overflow, tc) +{ + unload_module(); +} +#endif /* _LP64 */ + + +ATF_TC_WITH_CLEANUP(ucas_32); +ATF_TC_HEAD(ucas_32, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ucas_32 behavior"); +} +ATF_TC_BODY(ucas_32, tc) +{ + uint32_t cell = 0xdeadbeef; + uint32_t actual = 0; + + CHECK_MODULE(); + + ATF_REQUIRE_EQ(do_ucas_32(&cell, 0xdeadbeef, 0xbeefdead, &actual), 0); + ATF_REQUIRE(actual == 0xdeadbeef); + ATF_REQUIRE(cell == 0xbeefdead); +} +ATF_TC_CLEANUP(ucas_32, tc) +{ + unload_module(); +} + +#ifdef _LP64 +ATF_TC_WITH_CLEANUP(ucas_64); +ATF_TC_HEAD(ucas_64, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ucas_64 behavior"); +} +ATF_TC_BODY(ucas_64, tc) +{ + uint64_t cell = 0xdeadbeef; + uint64_t actual = 0; + + CHECK_MODULE(); + + ATF_REQUIRE_EQ(do_ucas_64(&cell, 0xdeadbeef, 0xbeefdead, &actual), 0); + ATF_REQUIRE(actual == 0xdeadbeef); + ATF_REQUIRE(cell == 0xbeefdead); +} +ATF_TC_CLEANUP(ucas_64, tc) +{ + unload_module(); +} +#endif /* _LP64 */ + +ATF_TC_WITH_CLEANUP(ucas_32_miscompare); +ATF_TC_HEAD(ucas_32_miscompare, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ucas_32 behavior with miscompare"); +} +ATF_TC_BODY(ucas_32_miscompare, tc) +{ + uint32_t cell = 0xa5a5a5a5; + uint32_t actual = 0; + + CHECK_MODULE(); + + ATF_REQUIRE_EQ(do_ucas_32(&cell, 0xdeadbeef, 0xbeefdead, &actual), 0); + ATF_REQUIRE(actual == 0xa5a5a5a5); + ATF_REQUIRE(cell == 0xa5a5a5a5); +} +ATF_TC_CLEANUP(ucas_32_miscompare, tc) +{ + unload_module(); +} + +#ifdef _LP64 +ATF_TC_WITH_CLEANUP(ucas_64_miscompare); +ATF_TC_HEAD(ucas_64_miscompare, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ucas_64 behavior with miscompare"); +} +ATF_TC_BODY(ucas_64_miscompare, tc) +{ + uint64_t cell = 0xa5a5a5a5; + uint64_t actual = 0; + + CHECK_MODULE(); + + ATF_REQUIRE_EQ(do_ucas_64(&cell, 0xdeadbeef, 0xbeefdead, &actual), 0); + ATF_REQUIRE(actual == 0xa5a5a5a5); + ATF_REQUIRE(cell == 0xa5a5a5a5); +} +ATF_TC_CLEANUP(ucas_64_miscompare, tc) +{ + unload_module(); +} +#endif /* _LP64 */ + +ATF_TC_WITH_CLEANUP(ucas_32_null); +ATF_TC_HEAD(ucas_32_null, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ucas_32 NULL pointer behavior"); +} +ATF_TC_BODY(ucas_32_null, tc) +{ + uint32_t actual = 0; + + CHECK_MODULE(); + + ATF_REQUIRE_EQ(do_ucas_32(NULL, 0xdeadbeef, 0xbeefdead, &actual), + EFAULT); +} +ATF_TC_CLEANUP(ucas_32_null, tc) +{ + unload_module(); +} + +#ifdef _LP64 +ATF_TC_WITH_CLEANUP(ucas_64_null); +ATF_TC_HEAD(ucas_64_null, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ucas_64 NULL pointer behavior"); +} +ATF_TC_BODY(ucas_64_null, tc) +{ + uint64_t actual = 0; + + CHECK_MODULE(); + + ATF_REQUIRE_EQ(do_ucas_64(NULL, 0xdeadbeef, 0xbeefdead, &actual), + EFAULT); +} +ATF_TC_CLEANUP(ucas_64_null, tc) +{ + unload_module(); +} +#endif /* _LP64 */ + +ATF_TC_WITH_CLEANUP(ucas_32_max); +ATF_TC_HEAD(ucas_32_max, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ucas_32 VM_MAX_ADDRESS pointer behavior"); +} +ATF_TC_BODY(ucas_32_max, tc) +{ + uint32_t actual = 0; + + CHECK_MODULE(); + + ATF_REQUIRE_EQ(do_ucas_32(vm_max_address(), 0xdeadbeef, 0xbeefdead, + &actual), EFAULT); +} +ATF_TC_CLEANUP(ucas_32_max, tc) +{ + unload_module(); +} + +#ifdef _LP64 +ATF_TC_WITH_CLEANUP(ucas_64_max); +ATF_TC_HEAD(ucas_64_max, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ucas_64 VM_MAX_ADDRESS pointer behavior"); +} +ATF_TC_BODY(ucas_64_max, tc) +{ + uint64_t actual = 0; + + CHECK_MODULE(); + + ATF_REQUIRE_EQ(do_ucas_64(vm_max_address(), 0xdeadbeef, 0xbeefdead, + &actual), EFAULT); +} +ATF_TC_CLEANUP(ucas_64_max, tc) +{ + unload_module(); +} +#endif /* _LP64 */ + +ATF_TC_WITH_CLEANUP(ucas_32_nearmax_overflow); +ATF_TC_HEAD(ucas_32_nearmax_overflow, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ucas_32 near-VM_MAX_ADDRESS pointer behavior"); +} +ATF_TC_BODY(ucas_32_nearmax_overflow, tc) +{ + uint32_t actual = 0; + + CHECK_MODULE(); + + /* + * For no-strict-alignment platforms: address checks must return + * EFAULT. + * + * For strict-alignment platforms: alignment checks must return + * EFAULT. + */ + ATF_REQUIRE_EQ(do_ucas_32(vm_max_address_minus(3), 0xdeadbeef, + 0xbeefdead, &actual), EFAULT); +} +ATF_TC_CLEANUP(ucas_32_nearmax_overflow, tc) +{ + unload_module(); +} + +#ifdef _LP64 +ATF_TC_WITH_CLEANUP(ucas_64_nearmax_overflow); +ATF_TC_HEAD(ucas_64_nearmax_overflow, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ucas_64 near-VM_MAX_ADDRESS pointer behavior"); +} +ATF_TC_BODY(ucas_64_nearmax_overflow, tc) +{ + uint64_t actual = 0; + + CHECK_MODULE(); + + /* + * For no-strict-alignment platforms: address checks must return + * EFAULT. + * + * For strict-alignment platforms: alignment checks must return + * EFAULT. + */ + ATF_REQUIRE_EQ(do_ucas_64(vm_max_address_minus(7), 0xdeadbeef, + 0xbeefdead, &actual), EFAULT); +} +ATF_TC_CLEANUP(ucas_64_nearmax_overflow, tc) +{ + unload_module(); +} +#endif /* _LP64 */ + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, ufetch_8); + ATF_TP_ADD_TC(tp, ufetch_16); + ATF_TP_ADD_TC(tp, ufetch_32); +#ifdef _LP64 + ATF_TP_ADD_TC(tp, ufetch_64); +#endif + + ATF_TP_ADD_TC(tp, ufetch_8_null); + ATF_TP_ADD_TC(tp, ufetch_16_null); + ATF_TP_ADD_TC(tp, ufetch_32_null); +#ifdef _LP64 + ATF_TP_ADD_TC(tp, ufetch_64_null); +#endif + + ATF_TP_ADD_TC(tp, ufetch_8_max); + ATF_TP_ADD_TC(tp, ufetch_16_max); + ATF_TP_ADD_TC(tp, ufetch_32_max); +#ifdef _LP64 + ATF_TP_ADD_TC(tp, ufetch_64_max); +#endif + + ATF_TP_ADD_TC(tp, ufetch_16_nearmax_overflow); + ATF_TP_ADD_TC(tp, ufetch_32_nearmax_overflow); +#ifdef _LP64 + ATF_TP_ADD_TC(tp, ufetch_64_nearmax_overflow); +#endif + + ATF_TP_ADD_TC(tp, ustore_8); + ATF_TP_ADD_TC(tp, ustore_16); + ATF_TP_ADD_TC(tp, ustore_32); +#ifdef _LP64 + ATF_TP_ADD_TC(tp, ustore_64); +#endif + + ATF_TP_ADD_TC(tp, ustore_8_null); + ATF_TP_ADD_TC(tp, ustore_16_null); + ATF_TP_ADD_TC(tp, ustore_32_null); +#ifdef _LP64 + ATF_TP_ADD_TC(tp, ustore_64_null); +#endif + + ATF_TP_ADD_TC(tp, ustore_8_max); + ATF_TP_ADD_TC(tp, ustore_16_max); + ATF_TP_ADD_TC(tp, ustore_32_max); +#ifdef _LP64 + ATF_TP_ADD_TC(tp, ustore_64_max); +#endif + + ATF_TP_ADD_TC(tp, ustore_16_nearmax_overflow); + ATF_TP_ADD_TC(tp, ustore_32_nearmax_overflow); +#ifdef _LP64 + ATF_TP_ADD_TC(tp, ustore_64_nearmax_overflow); +#endif + + ATF_TP_ADD_TC(tp, ucas_32); +#ifdef _LP64 + ATF_TP_ADD_TC(tp, ucas_64); +#endif + + ATF_TP_ADD_TC(tp, ucas_32_miscompare); +#ifdef _LP64 + ATF_TP_ADD_TC(tp, ucas_64_miscompare); +#endif + + ATF_TP_ADD_TC(tp, ucas_32_null); +#ifdef _LP64 + ATF_TP_ADD_TC(tp, ucas_64_null); +#endif + + ATF_TP_ADD_TC(tp, ucas_32_max); +#ifdef _LP64 + ATF_TP_ADD_TC(tp, ucas_64_max); +#endif + + ATF_TP_ADD_TC(tp, ucas_32_nearmax_overflow); +#ifdef _LP64 + ATF_TP_ADD_TC(tp, ucas_64_nearmax_overflow); +#endif + + return atf_no_error(); +} diff --git a/modules/t_x86_pte.c b/modules/t_x86_pte.c new file mode 100644 --- /dev/null +++ b/modules/t_x86_pte.c @@ -0,0 +1,202 @@ +/* $NetBSD: t_x86_pte.c,v 1.2 2020/04/26 11:56:38 maxv Exp $ */ + +/* + * Copyright (c) 2020 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Maxime Villard. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +__COPYRIGHT("@(#) Copyright (c) 2020\ + The NetBSD Foundation, inc. All rights reserved."); +__RCSID("$NetBSD: t_x86_pte.c,v 1.2 2020/04/26 11:56:38 maxv Exp $"); + +#include +#include +#include +#include +#include +#include + +#define mib_name "kern.x86_pte_test.test" +#define MODULE_PATH "/usr/tests/modules/x86_pte_tester/x86_pte_tester.kmod" +#define MODULE_NAME "x86_pte_tester" + +static struct { + size_t n_rwx; + size_t n_shstk; + bool kernel_map_with_low_ptes; + bool pte_is_user_accessible; + size_t n_user_space_is_kernel; + size_t n_kernel_space_is_user; + size_t n_svs_g_bit_set; +} x86_pte_results; + +static void +fetch_results(void) +{ + static bool fetched = false; + size_t len = sizeof(x86_pte_results); + char module_name[] = MODULE_NAME; + modctl_load_t params = { + .ml_filename = MODULE_PATH, + .ml_flags = MODCTL_NO_PROP, + }; + int rv; + + if (fetched) + return; + + if (modctl(MODCTL_LOAD, ¶ms) != 0) + atf_tc_skip("loading '%s' module failed.", MODULE_NAME); + + rv = sysctlbyname(mib_name, &x86_pte_results, &len, 0, 0); + ATF_REQUIRE_EQ(rv, 0); + + if (modctl(MODCTL_UNLOAD, module_name) != 0) + warn("failed to unload module '%s'", MODULE_NAME); + + fetched = true; +} + +/* -------------------------------------------------------------------------- */ + +ATF_TC(rwx); +ATF_TC_HEAD(rwx, tc) +{ + atf_tc_set_md_var(tc, "descr", + "ensure there is no RWX page in the kernel"); +} +ATF_TC_BODY(rwx, tc) +{ + fetch_results(); + ATF_REQUIRE_EQ(x86_pte_results.n_rwx, 0); +} + +/* -------------------------------------------------------------------------- */ + +ATF_TC(shstk); +ATF_TC_HEAD(shstk, tc) +{ + atf_tc_set_md_var(tc, "descr", + "ensure there is no SHSTK page in the kernel"); +} +ATF_TC_BODY(shstk, tc) +{ + fetch_results(); + atf_tc_expect_fail("there are %zu SHSTK pages", + x86_pte_results.n_shstk); + ATF_REQUIRE_EQ(x86_pte_results.n_shstk, 0); +} + +/* -------------------------------------------------------------------------- */ + +ATF_TC(kernel_map_with_low_ptes); +ATF_TC_HEAD(kernel_map_with_low_ptes, tc) +{ + atf_tc_set_md_var(tc, "descr", + "ensure the kernel map has no user mapping"); +} +ATF_TC_BODY(kernel_map_with_low_ptes, tc) +{ + fetch_results(); + ATF_REQUIRE_EQ(x86_pte_results.kernel_map_with_low_ptes, false); +} + +/* -------------------------------------------------------------------------- */ + +ATF_TC(pte_is_user_accessible); +ATF_TC_HEAD(pte_is_user_accessible, tc) +{ + atf_tc_set_md_var(tc, "descr", + "ensure the PTE space does not have user permissions"); +} +ATF_TC_BODY(pte_is_user_accessible, tc) +{ + fetch_results(); + ATF_REQUIRE_EQ(x86_pte_results.pte_is_user_accessible, false); +} + +/* -------------------------------------------------------------------------- */ + +ATF_TC(user_space_is_kernel); +ATF_TC_HEAD(user_space_is_kernel, tc) +{ + atf_tc_set_md_var(tc, "descr", + "ensure no page in the user space has kernel permissions"); +} +ATF_TC_BODY(user_space_is_kernel, tc) +{ + fetch_results(); + ATF_REQUIRE_EQ(x86_pte_results.n_user_space_is_kernel, 0); +} + +/* -------------------------------------------------------------------------- */ + +ATF_TC(kernel_space_is_user); +ATF_TC_HEAD(kernel_space_is_user, tc) +{ + atf_tc_set_md_var(tc, "descr", + "ensure no page in the kernel space has user permissions"); +} +ATF_TC_BODY(kernel_space_is_user, tc) +{ + fetch_results(); + ATF_REQUIRE_EQ(x86_pte_results.n_kernel_space_is_user, 0); +} + +/* -------------------------------------------------------------------------- */ + +ATF_TC(svs_g_bit_set); +ATF_TC_HEAD(svs_g_bit_set, tc) +{ + atf_tc_set_md_var(tc, "descr", + "ensure that no page in the SVS map has the G bit set"); +} +ATF_TC_BODY(svs_g_bit_set, tc) +{ + fetch_results(); + if (x86_pte_results.n_svs_g_bit_set != (size_t)-1) { + ATF_REQUIRE_EQ(x86_pte_results.n_svs_g_bit_set, 0); + } else { + atf_tc_skip("SVS is disabled."); + } +} + +/* -------------------------------------------------------------------------- */ + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, rwx); + ATF_TP_ADD_TC(tp, shstk); + ATF_TP_ADD_TC(tp, kernel_map_with_low_ptes); + ATF_TP_ADD_TC(tp, pte_is_user_accessible); + ATF_TP_ADD_TC(tp, user_space_is_kernel); + ATF_TP_ADD_TC(tp, kernel_space_is_user); + ATF_TP_ADD_TC(tp, svs_g_bit_set); + + return atf_no_error(); +} diff --git a/modules/threadpool_tester/Makefile b/modules/threadpool_tester/Makefile new file mode 100644 --- /dev/null +++ b/modules/threadpool_tester/Makefile @@ -0,0 +1,14 @@ +# $NetBSD: Makefile,v 1.2 2020/05/01 22:24:19 christos Exp $ + +.include + +KMOD= threadpool_tester +KMODULEDIR= ${TESTSBASE}/modules/${KMOD} + +SRCS= threadpool_tester.c + +ATFFILE= no +NOMAN= # defined + +.include +.include diff --git a/modules/threadpool_tester/threadpool_tester.c b/modules/threadpool_tester/threadpool_tester.c new file mode 100644 --- /dev/null +++ b/modules/threadpool_tester/threadpool_tester.c @@ -0,0 +1,502 @@ +/* $NetBSD: threadpool_tester.c,v 1.1 2019/01/25 18:33:59 christos Exp $ */ + +/*- + * Copyright (c) 2018 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +__KERNEL_RCSID(0, "$NetBSD: threadpool_tester.c,v 1.1 2019/01/25 18:33:59 christos Exp $"); + +#include +#include +#include +#include +#include + +MODULE(MODULE_CLASS_MISC, threadpool_tester, NULL); + +#ifdef THREADPOOL_VERBOSE +#define TP_LOG(x) printf x +#else +#define TP_LOG(x) /* nothing */ +#endif /* THREADPOOL_VERBOSE */ + +static struct tester_context { + kmutex_t ctx_mutex; + struct sysctllog *ctx_sysctllog; + struct threadpool *ctx_unbound[PRI_COUNT + 1]; + struct threadpool_percpu *ctx_percpu[PRI_COUNT + 1]; + unsigned int ctx_value; + struct threadpool_job ctx_job; +} tester_ctx; + +#define pri_to_idx(pri) ((pri) == PRI_NONE ? PRI_COUNT : (pri)) + +static bool +pri_is_valid(pri_t pri) +{ + return (pri == PRI_NONE || (pri >= PRI_USER && pri < PRI_COUNT)); +} + +static int +threadpool_tester_get_unbound(SYSCTLFN_ARGS) +{ + struct tester_context *ctx; + struct threadpool *pool, *opool = NULL; + struct sysctlnode node; + int error, val; + + node = *rnode; + ctx = node.sysctl_data; + + val = -1; + node.sysctl_data = &val; + error = sysctl_lookup(SYSCTLFN_CALL(&node)); + if (error || newp == NULL) + return error; + + if (! pri_is_valid(val)) + return EINVAL; + + error = threadpool_get(&pool, val); + if (error) { + TP_LOG(("%s: threadpool_get(..., %d) failed -> %d\n", + __func__, val, error)); + return error; + } + + mutex_enter(&ctx->ctx_mutex); + if (ctx->ctx_unbound[pri_to_idx(val)] == NULL) + ctx->ctx_unbound[pri_to_idx(val)] = pool; + else + opool = ctx->ctx_unbound[pri_to_idx(val)]; + mutex_exit(&ctx->ctx_mutex); + + if (opool != NULL) { + /* Should have gotten reference to existing pool. */ + TP_LOG(("%s: found existing unbound pool for pri %d (%s)\n", + __func__, val, opool == pool ? "match" : "NO MATCH")); + KASSERT(opool == pool); + threadpool_put(pool, val); + error = EEXIST; + } else { + TP_LOG(("%s: created unbound pool for pri %d\n", + __func__, val)); + } + + return error; +} + +static int +threadpool_tester_put_unbound(SYSCTLFN_ARGS) +{ + struct tester_context *ctx; + struct threadpool *pool; + struct sysctlnode node; + int error, val; + + node = *rnode; + ctx = node.sysctl_data; + + val = -1; + node.sysctl_data = &val; + error = sysctl_lookup(SYSCTLFN_CALL(&node)); + if (error || newp == NULL) + return error; + + if (! pri_is_valid(val)) + return EINVAL; + + mutex_enter(&ctx->ctx_mutex); + /* We only ever maintain a single reference. */ + pool = ctx->ctx_unbound[pri_to_idx(val)]; + ctx->ctx_unbound[pri_to_idx(val)] = NULL; + mutex_exit(&ctx->ctx_mutex); + + if (pool == NULL) { + TP_LOG(("%s: no unbound pool for pri %d\n", + __func__, val)); + return ENODEV; + } + + threadpool_put(pool, val); + TP_LOG(("%s: released unbound pool for pri %d\n", + __func__, val)); + + return 0; +} + +static int +threadpool_tester_run_unbound(SYSCTLFN_ARGS) +{ + struct tester_context *ctx; + struct threadpool *pool; + struct sysctlnode node; + int error, val; + + node = *rnode; + ctx = node.sysctl_data; + + val = -1; + node.sysctl_data = &val; + error = sysctl_lookup(SYSCTLFN_CALL(&node)); + if (error || newp == NULL) + return error; + + if (! pri_is_valid(val)) + return EINVAL; + + mutex_enter(&ctx->ctx_mutex); + pool = ctx->ctx_unbound[pri_to_idx(val)]; + if (pool == NULL) { + TP_LOG(("%s: no unbound pool for pri %d\n", + __func__, val)); + mutex_exit(&ctx->ctx_mutex); + return ENODEV; + } + + threadpool_schedule_job(pool, &ctx->ctx_job); + TP_LOG(("%s: scheduled job on unbound pool for pri %d\n", + __func__, val)); + mutex_exit(&ctx->ctx_mutex); + + return 0; +} + +static int +threadpool_tester_get_percpu(SYSCTLFN_ARGS) +{ + struct tester_context *ctx; + struct threadpool_percpu *pcpu, *opcpu = NULL; + struct sysctlnode node; + int error, val; + + node = *rnode; + ctx = node.sysctl_data; + + val = -1; + node.sysctl_data = &val; + error = sysctl_lookup(SYSCTLFN_CALL(&node)); + if (error || newp == NULL) + return error; + + if (! pri_is_valid(val)) + return EINVAL; + + error = threadpool_percpu_get(&pcpu, val); + if (error) { + TP_LOG(("%s: threadpool_percpu_get(..., %d) failed -> %d\n", + __func__, val, error)); + return error; + } + + mutex_enter(&ctx->ctx_mutex); + if (ctx->ctx_percpu[pri_to_idx(val)] == NULL) + ctx->ctx_percpu[pri_to_idx(val)] = pcpu; + else + opcpu = ctx->ctx_percpu[pri_to_idx(val)]; + mutex_exit(&ctx->ctx_mutex); + + if (opcpu != NULL) { + /* Should have gotten reference to existing pool. */ + TP_LOG(("%s: found existing unbound pool for pri %d (%s)\n", + __func__, val, opcpu == pcpu ? "match" : "NO MATCH")); + KASSERT(opcpu == pcpu); + threadpool_percpu_put(pcpu, val); + error = EEXIST; + } else { + TP_LOG(("%s: created percpu pool for pri %d\n", + __func__, val)); + } + + return error; +} + +static int +threadpool_tester_put_percpu(SYSCTLFN_ARGS) +{ + struct tester_context *ctx; + struct threadpool_percpu *pcpu; + struct sysctlnode node; + int error, val; + + node = *rnode; + ctx = node.sysctl_data; + + val = -1; + node.sysctl_data = &val; + error = sysctl_lookup(SYSCTLFN_CALL(&node)); + if (error || newp == NULL) + return error; + + if (! pri_is_valid(val)) + return EINVAL; + + mutex_enter(&ctx->ctx_mutex); + /* We only ever maintain a single reference. */ + pcpu = ctx->ctx_percpu[pri_to_idx(val)]; + ctx->ctx_percpu[pri_to_idx(val)] = NULL; + mutex_exit(&ctx->ctx_mutex); + + if (pcpu == NULL) { + TP_LOG(("%s: no percpu pool for pri %d\n", + __func__, val)); + return ENODEV; + } + + threadpool_percpu_put(pcpu, val); + TP_LOG(("%s: released percpu pool for pri %d\n", + __func__, val)); + + return 0; +} + +static int +threadpool_tester_run_percpu(SYSCTLFN_ARGS) +{ + struct tester_context *ctx; + struct threadpool_percpu *pcpu; + struct threadpool *pool; + struct sysctlnode node; + int error, val; + + node = *rnode; + ctx = node.sysctl_data; + + val = -1; + node.sysctl_data = &val; + error = sysctl_lookup(SYSCTLFN_CALL(&node)); + if (error || newp == NULL) + return error; + + if (! pri_is_valid(val)) + return EINVAL; + + mutex_enter(&ctx->ctx_mutex); + pcpu = ctx->ctx_percpu[pri_to_idx(val)]; + if (pcpu == NULL) { + TP_LOG(("%s: no percpu pool for pri %d\n", + __func__, val)); + mutex_exit(&ctx->ctx_mutex); + return ENODEV; + } + + pool = threadpool_percpu_ref(pcpu); + KASSERT(pool != NULL); + + threadpool_schedule_job(pool, &ctx->ctx_job); + TP_LOG(("%s: scheduled job on percpu pool for pri %d\n", + __func__, val)); + mutex_exit(&ctx->ctx_mutex); + + return 0; +} + +static int +threadpool_tester_test_value(SYSCTLFN_ARGS) +{ + struct tester_context *ctx; + struct sysctlnode node; + unsigned int val; + int error; + + node = *rnode; + ctx = node.sysctl_data; + + mutex_enter(&ctx->ctx_mutex); + val = ctx->ctx_value; + node.sysctl_data = &val; + error = sysctl_lookup(SYSCTLFN_CALL(&node)); + if (error || newp == NULL) { + mutex_exit(&ctx->ctx_mutex); + return error; + } + ctx->ctx_value = val; + mutex_exit(&ctx->ctx_mutex); + + return 0; +} + +static void +threadpool_tester_job(struct threadpool_job *job) +{ + struct tester_context *ctx = + container_of(job, struct tester_context, ctx_job); + unsigned int oval, nval; + + TP_LOG(("%s: job = %p, ctx = %p\n", __func__, job, ctx)); + + mutex_enter(&ctx->ctx_mutex); + oval = ctx->ctx_value; + nval = oval + 1; /* always reference oval and nval */ + ctx->ctx_value = nval; + mutex_exit(&ctx->ctx_mutex); + + TP_LOG(("%s: %u -> %u\n", __func__, oval, nval)); + (void) kpause("tptestjob", false, hz, NULL); + + mutex_enter(&ctx->ctx_mutex); + threadpool_job_done(job); + mutex_exit(&ctx->ctx_mutex); +} + +#define RETURN_ERROR if (error) goto return_error + +static int +threadpool_tester_init(void) +{ + struct sysctllog **log = &tester_ctx.ctx_sysctllog; + const struct sysctlnode *rnode, *cnode; + int error; + + mutex_init(&tester_ctx.ctx_mutex, MUTEX_DEFAULT, IPL_NONE); + threadpool_job_init(&tester_ctx.ctx_job, threadpool_tester_job, + &tester_ctx.ctx_mutex, "tptest"); + + error = sysctl_createv(log, 0, NULL, &rnode, CTLFLAG_PERMANENT, + CTLTYPE_NODE, "threadpool_tester", + SYSCTL_DESCR("threadpool testing interface"), + NULL, 0, NULL, 0, CTL_KERN, CTL_CREATE, CTL_EOL); + RETURN_ERROR; + + error = sysctl_createv(log, 0, &rnode, &cnode, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT, "get_unbound", + SYSCTL_DESCR("get unbound pool of specified priority"), + threadpool_tester_get_unbound, 0, + (void *)&tester_ctx, 0, CTL_CREATE, CTL_EOL); + RETURN_ERROR; + + error = sysctl_createv(log, 0, &rnode, &cnode, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT, "put_unbound", + SYSCTL_DESCR("put unbound pool of specified priority"), + threadpool_tester_put_unbound, 0, + (void *)&tester_ctx, 0, CTL_CREATE, CTL_EOL); + RETURN_ERROR; + + error = sysctl_createv(log, 0, &rnode, &cnode, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT, "run_unbound", + SYSCTL_DESCR("run on unbound pool of specified priority"), + threadpool_tester_run_unbound, 0, + (void *)&tester_ctx, 0, CTL_CREATE, CTL_EOL); + RETURN_ERROR; + + error = sysctl_createv(log, 0, &rnode, &cnode, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT, "get_percpu", + SYSCTL_DESCR("get percpu pool of specified priority"), + threadpool_tester_get_percpu, 0, + (void *)&tester_ctx, 0, CTL_CREATE, CTL_EOL); + RETURN_ERROR; + + error = sysctl_createv(log, 0, &rnode, &cnode, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT, "put_percpu", + SYSCTL_DESCR("put percpu pool of specified priority"), + threadpool_tester_put_percpu, 0, + (void *)&tester_ctx, 0, CTL_CREATE, CTL_EOL); + RETURN_ERROR; + + error = sysctl_createv(log, 0, &rnode, &cnode, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT, "run_percpu", + SYSCTL_DESCR("run on percpu pool of specified priority"), + threadpool_tester_run_percpu, 0, + (void *)&tester_ctx, 0, CTL_CREATE, CTL_EOL); + RETURN_ERROR; + + error = sysctl_createv(log, 0, &rnode, &cnode, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT, "test_value", + SYSCTL_DESCR("test value that jobs increment"), + threadpool_tester_test_value, 0, + (void *)&tester_ctx, 0, CTL_CREATE, CTL_EOL); + RETURN_ERROR; + + return 0; + + return_error: + sysctl_teardown(log); + return error; +} + +static int +threadpool_tester_fini(void) +{ + pri_t pri; + + mutex_enter(&tester_ctx.ctx_mutex); + for (pri = PRI_NONE/*-1*/; pri < PRI_COUNT; pri++) { + struct threadpool *pool = + tester_ctx.ctx_unbound[pri_to_idx(pri)]; + struct threadpool_percpu *pcpu = + tester_ctx.ctx_percpu[pri_to_idx(pri)]; + + /* + * threadpool_cancel_job() may be called on a pool + * other than what the job is scheduled on. This is + * safe; see comment in threadpool_cancel_job_async(). + */ + + if (pool != NULL) { + threadpool_cancel_job(pool, &tester_ctx.ctx_job); + threadpool_put(pool, pri); + tester_ctx.ctx_unbound[pri_to_idx(pri)] = NULL; + } + if (pcpu != NULL) { + pool = threadpool_percpu_ref(pcpu); + threadpool_cancel_job(pool, &tester_ctx.ctx_job); + threadpool_percpu_put(pcpu, pri); + tester_ctx.ctx_percpu[pri_to_idx(pri)] = NULL; + } + } + mutex_exit(&tester_ctx.ctx_mutex); + threadpool_job_destroy(&tester_ctx.ctx_job); + mutex_destroy(&tester_ctx.ctx_mutex); + + sysctl_teardown(&tester_ctx.ctx_sysctllog); + + return 0; +} + +static int +threadpool_tester_modcmd(modcmd_t cmd, void *arg __unused) +{ + int error; + + switch (cmd) { + case MODULE_CMD_INIT: + error = threadpool_tester_init(); + break; + + case MODULE_CMD_FINI: + error = threadpool_tester_fini(); + break; + + case MODULE_CMD_STAT: + default: + error = ENOTTY; + } + + return error; +} diff --git a/modules/ufetchstore/Makefile b/modules/ufetchstore/Makefile new file mode 100644 --- /dev/null +++ b/modules/ufetchstore/Makefile @@ -0,0 +1,14 @@ +# $NetBSD: Makefile,v 1.4 2020/05/01 22:24:19 christos Exp $ + +.include + +KMOD= ufetchstore_tester +KMODULEDIR= ${TESTSBASE}/modules/${KMOD} + +SRCS= ufetchstore_tester.c + +ATFFILE= no +NOMAN= # defined + +.include +.include diff --git a/modules/ufetchstore/common.h b/modules/ufetchstore/common.h new file mode 100644 --- /dev/null +++ b/modules/ufetchstore/common.h @@ -0,0 +1,59 @@ +/* $NetBSD: common.h,v 1.1 2019/04/15 23:41:23 christos Exp $ */ + +/*- + * Copyright (c) 2019 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +#ifndef _UFETCHSTORE_TESTER_COMMON_H_ +#define _UFETCHSTORE_TESTER_COMMON_H_ + +#define OP_LOAD 0 +#define OP_STORE 1 +#define OP_CAS 2 + +struct ufetchstore_test_args { + int pointer_size; + int test_op; + int size; + int fetchstore_error; + uint64_t uaddr64; + union { + uint8_t val8; + uint16_t val16; + uint32_t val32; + uint64_t val64; + }; + union { + uint8_t ea_val8; + uint16_t ea_val16; + uint32_t ea_val32; + uint64_t ea_val64; + }; +}; + +#endif /* _UFETCHSTORE_TESTER_COMMON_H_ */ diff --git a/modules/ufetchstore/t_ufetchstore.c b/modules/ufetchstore/t_ufetchstore.c new file mode 100644 --- /dev/null +++ b/modules/ufetchstore/t_ufetchstore.c @@ -0,0 +1,1315 @@ +/* $NetBSD: t_ufetchstore.c,v 1.4 2019/04/07 15:50:12 thorpej Exp $ */ + +/* + * Copyright (c) 2019 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +__COPYRIGHT("@(#) Copyright (c) 2019\ + The NetBSD Foundation, inc. All rights reserved."); +__RCSID("$NetBSD: t_ufetchstore.c,v 1.4 2019/04/07 15:50:12 thorpej Exp $"); + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "common.h" + +#define mib_name "kern.ufetchstore_test.test" + +static bool module_loaded; + +#define MODULE_PATH \ + "/usr/tests/modules/ufetchstore_tester/ufetchstore_tester.kmod" +#define MODULE_NAME "ufetchstore_tester" + +#define CHECK_MODULE() \ +do { \ + load_module(); \ + if (! module_loaded) { \ + atf_tc_skip("loading '%s' module failed.", MODULE_NAME);\ + } \ +} while (/*CONSTCOND*/0) + +static void +load_module(void) +{ +#ifndef SKIP_MODULE + if (module_loaded) + return; + + modctl_load_t params = { + .ml_filename = MODULE_PATH, + .ml_flags = MODCTL_NO_PROP, + }; + + if (modctl(MODCTL_LOAD, ¶ms) != 0) { + warn("failed to load module '%s'", MODULE_PATH); + } else { + module_loaded = true; + } +#else + module_loaded = true; +#endif /* ! SKIP_MODULE */ +} + +#define UADDR(x) ((uintptr_t)(x)) + +static void +unload_module(void) +{ +#ifndef SKIP_MODULE + char module_name[] = MODULE_NAME; + + if (modctl(MODCTL_UNLOAD, module_name) != 0) { + warn("failed to unload module '%s'", MODULE_NAME); + } else { + module_loaded = false; + } +#endif /* ! SKIP_MODULE */ +} + +static unsigned long +vm_max_address_raw(void) +{ + static unsigned long max_addr = 0; + int rv; + + if (max_addr == 0) { + size_t max_addr_size = sizeof(max_addr); + rv = sysctlbyname("vm.maxaddress", &max_addr, &max_addr_size, + NULL, 0); + if (rv != 0) + err(1, "sysctlbyname('vm.maxaddress')"); + } + return max_addr; +} + +static void * +vm_max_address(void) +{ + return (void *)vm_max_address_raw(); +} + +static void * +vm_max_address_minus(unsigned int adj) +{ + return (void *)(vm_max_address_raw() - adj); +} + +static int +do_sysctl(struct ufetchstore_test_args *args) +{ + uint64_t arg_addr64 = (uintptr_t)args; + int rv; + + args->fetchstore_error = EBADF; /* poison */ + args->pointer_size = (int)sizeof(void *); + + /* + * Yes, the intent is to provide the pointer, not the structure, + * to the kernel side of the test harness. + */ + rv = sysctlbyname(mib_name, NULL, NULL, &arg_addr64, + sizeof(arg_addr64)); + if (rv != 0) { + rv = errno; + warn("sysctlbyname('%s') -> %d", mib_name, rv); + return rv; + } + return 0; +} + +static int +do_ufetch_8(const uint8_t *uaddr, uint8_t *res) +{ + struct ufetchstore_test_args args = { + .uaddr64 = UADDR(uaddr), + .test_op = OP_LOAD, + .size = 8, + }; + + ATF_REQUIRE_EQ(do_sysctl(&args), 0); + *res = args.val8; + return args.fetchstore_error; +} + +static int +do_ufetch_16(const uint16_t *uaddr, uint16_t *res) +{ + struct ufetchstore_test_args args = { + .uaddr64 = UADDR(uaddr), + .test_op = OP_LOAD, + .size = 16, + }; + + ATF_REQUIRE_EQ(do_sysctl(&args), 0); + *res = args.val16; + return args.fetchstore_error; +} + +static int +do_ufetch_32(const uint32_t *uaddr, uint32_t *res) +{ + struct ufetchstore_test_args args = { + .uaddr64 = UADDR(uaddr), + .test_op = OP_LOAD, + .size = 32, + }; + + ATF_REQUIRE_EQ(do_sysctl(&args), 0); + *res = args.val32; + return args.fetchstore_error; +} + +#ifdef _LP64 +static int +do_ufetch_64(const uint64_t *uaddr, uint64_t *res) +{ + struct ufetchstore_test_args args = { + .uaddr64 = UADDR(uaddr), + .test_op = OP_LOAD, + .size = 64, + }; + + ATF_REQUIRE_EQ(do_sysctl(&args), 0); + *res = args.val64; + return args.fetchstore_error; +} +#endif /* _LP64 */ + +static int +do_ustore_8(uint8_t *uaddr, uint8_t val) +{ + struct ufetchstore_test_args args = { + .uaddr64 = UADDR(uaddr), + .test_op = OP_STORE, + .size = 8, + .val8 = val, + }; + + ATF_REQUIRE_EQ(do_sysctl(&args), 0); + return args.fetchstore_error; +} + +static int +do_ustore_16(uint16_t *uaddr, uint16_t val) +{ + struct ufetchstore_test_args args = { + .uaddr64 = UADDR(uaddr), + .test_op = OP_STORE, + .size = 16, + .val16 = val, + }; + + ATF_REQUIRE_EQ(do_sysctl(&args), 0); + return args.fetchstore_error; +} + +static int +do_ustore_32(uint32_t *uaddr, uint32_t val) +{ + struct ufetchstore_test_args args = { + .uaddr64 = UADDR(uaddr), + .test_op = OP_STORE, + .size = 32, + .val32 = val, + }; + + ATF_REQUIRE_EQ(do_sysctl(&args), 0); + return args.fetchstore_error; +} + +#ifdef _LP64 +static int +do_ustore_64(uint64_t *uaddr, uint64_t val) +{ + struct ufetchstore_test_args args = { + .uaddr64 = UADDR(uaddr), + .test_op = OP_STORE, + .size = 64, + .val64 = val, + }; + + ATF_REQUIRE_EQ(do_sysctl(&args), 0); + return args.fetchstore_error; +} +#endif /* _LP64 */ + +static int +do_ucas_32(uint32_t *uaddr, uint32_t expected, uint32_t new, uint32_t *actualp) +{ + struct ufetchstore_test_args args = { + .uaddr64 = UADDR(uaddr), + .test_op = OP_CAS, + .size = 32, + .val32 = new, + .ea_val32 = expected, + }; + + ATF_REQUIRE_EQ(do_sysctl(&args), 0); + *actualp = args.ea_val32; + return args.fetchstore_error; +} + +#ifdef _LP64 +static int +do_ucas_64(uint64_t *uaddr, uint64_t expected, uint64_t new, uint64_t *actualp) +{ + struct ufetchstore_test_args args = { + .uaddr64 = UADDR(uaddr), + .test_op = OP_CAS, + .size = 64, + .val64 = new, + .ea_val64 = expected, + }; + + ATF_REQUIRE_EQ(do_sysctl(&args), 0); + *actualp = args.ea_val64; + return args.fetchstore_error; +} +#endif /* _LP64 */ + +struct memory_cell { + unsigned long guard0; + union { + unsigned long test_cell; +#ifdef _LP64 + uint64_t val64; +#endif + uint32_t val32[sizeof(long) / 4]; + uint16_t val16[sizeof(long) / 2]; + uint8_t val8 [sizeof(long) ]; + }; + unsigned long guard1; +}; + +#define index8 1 +#define index16 1 +#define index32 0 + +#define test_pattern8 0xa5 +#define test_pattern16 0x5a6b +#define test_pattern32 0xb01cafe1 +#ifdef _LP64 +#define test_pattern64 0xcafedeadfeedbabe +#endif + +#if _BYTE_ORDER == _LITTLE_ENDIAN +#define test_cell_val8 ((unsigned long)test_pattern8 << (index8 * NBBY)) +#define test_cell_val16 ((unsigned long)test_pattern16 << (index16 * NBBY*2)) +#define test_cell_val32 ((unsigned long)test_pattern32 << (index32 * NBBY*4)) +#ifdef _LP64 +#define test_cell_val64 ((unsigned long)test_pattern64) +#endif +#endif /* _BYTE_ORDER == _LITTLE_ENDIAN */ + +#if _BYTE_ORDER == _BIG_ENDIAN +#ifdef _LP64 +#define test_cell_val8 ((unsigned long)test_pattern8 << (56-(index8 * NBBY))) +#define test_cell_val16 ((unsigned long)test_pattern16 << (48-(index16 * NBBY*2))) +#define test_cell_val32 ((unsigned long)test_pattern32 << (32-(index32 * NBBY*4))) +#define test_cell_val64 ((unsigned long)test_pattern64) +#else /* ! _LP64 */ +#define test_cell_val8 ((unsigned long)test_pattern8 << (24-(index8 * NBBY))) +#define test_cell_val16 ((unsigned long)test_pattern16 << (16-(index16 * NBBY*2))) +#define test_cell_val32 ((unsigned long)test_pattern32) +#endif /* _LP64 */ +#endif /* #if _BYTE_ORDER == _BIG_ENDIAN */ + +#define read_test_cell(cell) (cell)->test_cell +#define write_test_cell(cell, v) (cell)->test_cell = (v) + +#define memory_cell_initializer \ + { \ + .guard0 = ULONG_MAX, \ + .test_cell = 0, \ + .guard1 = ULONG_MAX, \ + } + +static bool +memory_cell_check_guard(const struct memory_cell * const cell) +{ + return cell->guard0 == ULONG_MAX && + cell->guard1 == ULONG_MAX; +} + +ATF_TC_WITH_CLEANUP(ufetch_8); +ATF_TC_HEAD(ufetch_8, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ufetch_8 behavior"); +} +ATF_TC_BODY(ufetch_8, tc) +{ + struct memory_cell cell = memory_cell_initializer; + uint8_t res; + + CHECK_MODULE(); + + write_test_cell(&cell, test_cell_val8); + ATF_REQUIRE_EQ(do_ufetch_8(&cell.val8[index8], &res), 0); + ATF_REQUIRE(memory_cell_check_guard(&cell)); + ATF_REQUIRE(res == test_pattern8); +} +ATF_TC_CLEANUP(ufetch_8, tc) +{ + unload_module(); +} + +ATF_TC_WITH_CLEANUP(ufetch_16); +ATF_TC_HEAD(ufetch_16, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ufetch_16 behavior"); +} +ATF_TC_BODY(ufetch_16, tc) +{ + struct memory_cell cell = memory_cell_initializer; + uint16_t res; + + CHECK_MODULE(); + + write_test_cell(&cell, test_cell_val16); + ATF_REQUIRE_EQ(do_ufetch_16(&cell.val16[index16], &res), 0); + ATF_REQUIRE(memory_cell_check_guard(&cell)); + ATF_REQUIRE(res == test_pattern16); +} +ATF_TC_CLEANUP(ufetch_16, tc) +{ + unload_module(); +} + +ATF_TC_WITH_CLEANUP(ufetch_32); +ATF_TC_HEAD(ufetch_32, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ufetch_32 behavior"); +} +ATF_TC_BODY(ufetch_32, tc) +{ + struct memory_cell cell = memory_cell_initializer; + uint32_t res; + + CHECK_MODULE(); + + write_test_cell(&cell, test_cell_val32); + ATF_REQUIRE_EQ(do_ufetch_32(&cell.val32[index32], &res), 0); + ATF_REQUIRE(memory_cell_check_guard(&cell)); + ATF_REQUIRE(res == test_pattern32); +} +ATF_TC_CLEANUP(ufetch_32, tc) +{ + unload_module(); +} + +#ifdef _LP64 +ATF_TC_WITH_CLEANUP(ufetch_64); +ATF_TC_HEAD(ufetch_64, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ufetch_64 behavior"); +} +ATF_TC_BODY(ufetch_64, tc) +{ + struct memory_cell cell = memory_cell_initializer; + uint64_t res; + + CHECK_MODULE(); + + write_test_cell(&cell, test_cell_val64); + ATF_REQUIRE_EQ(do_ufetch_64(&cell.val64, &res), 0); + ATF_REQUIRE(memory_cell_check_guard(&cell)); + ATF_REQUIRE(res == test_pattern64); +} +ATF_TC_CLEANUP(ufetch_64, tc) +{ + unload_module(); +} +#endif /* _LP64 */ + +ATF_TC_WITH_CLEANUP(ufetch_8_null); +ATF_TC_HEAD(ufetch_8_null, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ufetch_8 NULL pointer behavior"); +} +ATF_TC_BODY(ufetch_8_null, tc) +{ + uint8_t res; + + CHECK_MODULE(); + + ATF_REQUIRE_EQ(do_ufetch_8(NULL, &res), EFAULT); +} +ATF_TC_CLEANUP(ufetch_8_null, tc) +{ + unload_module(); +} + +ATF_TC_WITH_CLEANUP(ufetch_16_null); +ATF_TC_HEAD(ufetch_16_null, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ufetch_16 NULL pointer behavior"); +} +ATF_TC_BODY(ufetch_16_null, tc) +{ + uint16_t res; + + CHECK_MODULE(); + + ATF_REQUIRE_EQ(do_ufetch_16(NULL, &res), EFAULT); +} +ATF_TC_CLEANUP(ufetch_16_null, tc) +{ + unload_module(); +} + +ATF_TC_WITH_CLEANUP(ufetch_32_null); +ATF_TC_HEAD(ufetch_32_null, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ufetch_32 NULL pointer behavior"); +} +ATF_TC_BODY(ufetch_32_null, tc) +{ + uint32_t res; + + CHECK_MODULE(); + + ATF_REQUIRE_EQ(do_ufetch_32(NULL, &res), EFAULT); +} +ATF_TC_CLEANUP(ufetch_32_null, tc) +{ + unload_module(); +} + +#ifdef _LP64 +ATF_TC_WITH_CLEANUP(ufetch_64_null); +ATF_TC_HEAD(ufetch_64_null, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ufetch_64 NULL pointer behavior"); +} +ATF_TC_BODY(ufetch_64_null, tc) +{ + uint64_t res; + + CHECK_MODULE(); + + ATF_REQUIRE_EQ(do_ufetch_64(NULL, &res), EFAULT); +} +ATF_TC_CLEANUP(ufetch_64_null, tc) +{ + unload_module(); +} +#endif /* _LP64 */ + +ATF_TC_WITH_CLEANUP(ufetch_8_max); +ATF_TC_HEAD(ufetch_8_max, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ufetch_8 VM_MAX_ADDRESS pointer behavior"); +} +ATF_TC_BODY(ufetch_8_max, tc) +{ + uint8_t res; + + CHECK_MODULE(); + + ATF_REQUIRE_EQ(do_ufetch_8(vm_max_address(), &res), EFAULT); +} +ATF_TC_CLEANUP(ufetch_8_max, tc) +{ + unload_module(); +} + +ATF_TC_WITH_CLEANUP(ufetch_16_max); +ATF_TC_HEAD(ufetch_16_max, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ufetch_16 VM_MAX_ADDRESS pointer behavior"); +} +ATF_TC_BODY(ufetch_16_max, tc) +{ + uint16_t res; + + CHECK_MODULE(); + + ATF_REQUIRE_EQ(do_ufetch_16(vm_max_address(), &res), EFAULT); +} +ATF_TC_CLEANUP(ufetch_16_max, tc) +{ + unload_module(); +} + +ATF_TC_WITH_CLEANUP(ufetch_32_max); +ATF_TC_HEAD(ufetch_32_max, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ufetch_32 VM_MAX_ADDRESS pointer behavior"); +} +ATF_TC_BODY(ufetch_32_max, tc) +{ + uint32_t res; + + CHECK_MODULE(); + + ATF_REQUIRE_EQ(do_ufetch_32(vm_max_address(), &res), EFAULT); +} +ATF_TC_CLEANUP(ufetch_32_max, tc) +{ + unload_module(); +} + +#ifdef _LP64 +ATF_TC_WITH_CLEANUP(ufetch_64_max); +ATF_TC_HEAD(ufetch_64_max, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ufetch_64 VM_MAX_ADDRESS pointer behavior"); +} +ATF_TC_BODY(ufetch_64_max, tc) +{ + uint64_t res; + + CHECK_MODULE(); + + ATF_REQUIRE_EQ(do_ufetch_64(vm_max_address(), &res), EFAULT); +} +ATF_TC_CLEANUP(ufetch_64_max, tc) +{ + unload_module(); +} +#endif /* _LP64 */ + +ATF_TC_WITH_CLEANUP(ufetch_16_nearmax_overflow); +ATF_TC_HEAD(ufetch_16_nearmax_overflow, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ufetch_16 near-VM_MAX_ADDRESS pointer behavior"); +} +ATF_TC_BODY(ufetch_16_nearmax_overflow, tc) +{ + uint16_t res; + + CHECK_MODULE(); + + /* + * For no-strict-alignment platforms: address checks must return + * EFAULT. + * + * For strict-alignment platforms: alignment checks must return + * EFAULT. + */ + ATF_REQUIRE_EQ(do_ufetch_16(vm_max_address_minus(1), &res), EFAULT); +} +ATF_TC_CLEANUP(ufetch_16_nearmax_overflow, tc) +{ + unload_module(); +} + +ATF_TC_WITH_CLEANUP(ufetch_32_nearmax_overflow); +ATF_TC_HEAD(ufetch_32_nearmax_overflow, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ufetch_32 near-VM_MAX_ADDRESS pointer behavior"); +} +ATF_TC_BODY(ufetch_32_nearmax_overflow, tc) +{ + uint32_t res; + + CHECK_MODULE(); + + /* + * For no-strict-alignment platforms: address checks must return + * EFAULT. + * + * For strict-alignment platforms: alignment checks must return + * EFAULT. + */ + ATF_REQUIRE_EQ(do_ufetch_32(vm_max_address_minus(3), &res), EFAULT); +} +ATF_TC_CLEANUP(ufetch_32_nearmax_overflow, tc) +{ + unload_module(); +} + +#ifdef _LP64 +ATF_TC_WITH_CLEANUP(ufetch_64_nearmax_overflow); +ATF_TC_HEAD(ufetch_64_nearmax_overflow, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ufetch_64 near-VM_MAX_ADDRESS pointer behavior"); +} +ATF_TC_BODY(ufetch_64_nearmax_overflow, tc) +{ + uint64_t res; + + CHECK_MODULE(); + + /* + * For no-strict-alignment platforms: address checks must return + * EFAULT. + * + * For strict-alignment platforms: alignment checks must return + * EFAULT. + */ + ATF_REQUIRE_EQ(do_ufetch_64(vm_max_address_minus(7), &res), EFAULT); +} +ATF_TC_CLEANUP(ufetch_64_nearmax_overflow, tc) +{ + unload_module(); +} +#endif /* _LP64 */ + + +ATF_TC_WITH_CLEANUP(ustore_8); +ATF_TC_HEAD(ustore_8, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ustore_8 behavior"); +} +ATF_TC_BODY(ustore_8, tc) +{ + struct memory_cell cell = memory_cell_initializer; + + CHECK_MODULE(); + + ATF_REQUIRE_EQ(do_ustore_8(&cell.val8[index8], test_pattern8), 0); + ATF_REQUIRE(memory_cell_check_guard(&cell)); + ATF_REQUIRE(read_test_cell(&cell) == test_cell_val8); +} +ATF_TC_CLEANUP(ustore_8, tc) +{ + unload_module(); +} + +ATF_TC_WITH_CLEANUP(ustore_16); +ATF_TC_HEAD(ustore_16, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ustore_16 behavior"); +} +ATF_TC_BODY(ustore_16, tc) +{ + struct memory_cell cell = memory_cell_initializer; + + CHECK_MODULE(); + + ATF_REQUIRE_EQ(do_ustore_16(&cell.val16[index16], test_pattern16), 0); + ATF_REQUIRE(memory_cell_check_guard(&cell)); + ATF_REQUIRE(read_test_cell(&cell) == test_cell_val16); +} +ATF_TC_CLEANUP(ustore_16, tc) +{ + unload_module(); +} + +ATF_TC_WITH_CLEANUP(ustore_32); +ATF_TC_HEAD(ustore_32, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ustore_32 behavior"); +} +ATF_TC_BODY(ustore_32, tc) +{ + struct memory_cell cell = memory_cell_initializer; + + CHECK_MODULE(); + + ATF_REQUIRE_EQ(do_ustore_32(&cell.val32[index32], test_pattern32), 0); + ATF_REQUIRE(memory_cell_check_guard(&cell)); + ATF_REQUIRE(read_test_cell(&cell) == test_cell_val32); +} +ATF_TC_CLEANUP(ustore_32, tc) +{ + unload_module(); +} + +#ifdef _LP64 +ATF_TC_WITH_CLEANUP(ustore_64); +ATF_TC_HEAD(ustore_64, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ustore_64 behavior"); +} +ATF_TC_BODY(ustore_64, tc) +{ + struct memory_cell cell = memory_cell_initializer; + + CHECK_MODULE(); + + ATF_REQUIRE_EQ(do_ustore_64(&cell.val64, test_pattern64), 0); + ATF_REQUIRE(memory_cell_check_guard(&cell)); + ATF_REQUIRE(read_test_cell(&cell) == test_cell_val64); +} +ATF_TC_CLEANUP(ustore_64, tc) +{ + unload_module(); +} +#endif /* _LP64 */ + +ATF_TC_WITH_CLEANUP(ustore_8_null); +ATF_TC_HEAD(ustore_8_null, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ustore_8 NULL pointer behavior"); +} +ATF_TC_BODY(ustore_8_null, tc) +{ + CHECK_MODULE(); + + ATF_REQUIRE_EQ(do_ustore_8(NULL, 0), EFAULT); +} +ATF_TC_CLEANUP(ustore_8_null, tc) +{ + unload_module(); +} + +ATF_TC_WITH_CLEANUP(ustore_16_null); +ATF_TC_HEAD(ustore_16_null, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ustore_16 NULL pointer behavior"); +} +ATF_TC_BODY(ustore_16_null, tc) +{ + CHECK_MODULE(); + + ATF_REQUIRE_EQ(do_ustore_16(NULL, 0), EFAULT); +} +ATF_TC_CLEANUP(ustore_16_null, tc) +{ + unload_module(); +} + +ATF_TC_WITH_CLEANUP(ustore_32_null); +ATF_TC_HEAD(ustore_32_null, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ustore_32 NULL pointer behavior"); +} +ATF_TC_BODY(ustore_32_null, tc) +{ + CHECK_MODULE(); + + ATF_REQUIRE_EQ(do_ustore_32(NULL, 0), EFAULT); +} +ATF_TC_CLEANUP(ustore_32_null, tc) +{ + unload_module(); +} + +#ifdef _LP64 +ATF_TC_WITH_CLEANUP(ustore_64_null); +ATF_TC_HEAD(ustore_64_null, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ustore_64 NULL pointer behavior"); +} +ATF_TC_BODY(ustore_64_null, tc) +{ + CHECK_MODULE(); + + ATF_REQUIRE_EQ(do_ustore_64(NULL, 0), EFAULT); +} +ATF_TC_CLEANUP(ustore_64_null, tc) +{ + unload_module(); +} +#endif /* _LP64 */ + +ATF_TC_WITH_CLEANUP(ustore_8_max); +ATF_TC_HEAD(ustore_8_max, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ustore_8 VM_MAX_ADDRESS pointer behavior"); +} +ATF_TC_BODY(ustore_8_max, tc) +{ + CHECK_MODULE(); + + ATF_REQUIRE_EQ(do_ustore_8(vm_max_address(), 0), EFAULT); +} +ATF_TC_CLEANUP(ustore_8_max, tc) +{ + unload_module(); +} + +ATF_TC_WITH_CLEANUP(ustore_16_max); +ATF_TC_HEAD(ustore_16_max, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ustore_16 VM_MAX_ADDRESS pointer behavior"); +} +ATF_TC_BODY(ustore_16_max, tc) +{ + CHECK_MODULE(); + + ATF_REQUIRE_EQ(do_ustore_16(vm_max_address(), 0), EFAULT); +} +ATF_TC_CLEANUP(ustore_16_max, tc) +{ + unload_module(); +} + +ATF_TC_WITH_CLEANUP(ustore_32_max); +ATF_TC_HEAD(ustore_32_max, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ustore_32 VM_MAX_ADDRESS pointer behavior"); +} +ATF_TC_BODY(ustore_32_max, tc) +{ + CHECK_MODULE(); + + ATF_REQUIRE_EQ(do_ustore_32(vm_max_address(), 0), EFAULT); +} +ATF_TC_CLEANUP(ustore_32_max, tc) +{ + unload_module(); +} + +#ifdef _LP64 +ATF_TC_WITH_CLEANUP(ustore_64_max); +ATF_TC_HEAD(ustore_64_max, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ustore_64 VM_MAX_ADDRESS pointer behavior"); +} +ATF_TC_BODY(ustore_64_max, tc) +{ + CHECK_MODULE(); + + ATF_REQUIRE_EQ(do_ustore_64(vm_max_address(), 0), EFAULT); +} +ATF_TC_CLEANUP(ustore_64_max, tc) +{ + unload_module(); +} +#endif /* _LP64 */ + +ATF_TC_WITH_CLEANUP(ustore_16_nearmax_overflow); +ATF_TC_HEAD(ustore_16_nearmax_overflow, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ustore_16 VM_MAX_ADDRESS pointer behavior"); +} +ATF_TC_BODY(ustore_16_nearmax_overflow, tc) +{ + CHECK_MODULE(); + + /* + * For no-strict-alignment platforms: address checks must return + * EFAULT. + * + * For strict-alignment platforms: alignment checks must return + * EFAULT. + */ + ATF_REQUIRE_EQ(do_ustore_16(vm_max_address_minus(1), 0), EFAULT); +} +ATF_TC_CLEANUP(ustore_16_nearmax_overflow, tc) +{ + unload_module(); +} + +ATF_TC_WITH_CLEANUP(ustore_32_nearmax_overflow); +ATF_TC_HEAD(ustore_32_nearmax_overflow, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ustore_32 VM_MAX_ADDRESS pointer behavior"); +} +ATF_TC_BODY(ustore_32_nearmax_overflow, tc) +{ + CHECK_MODULE(); + + /* + * For no-strict-alignment platforms: address checks must return + * EFAULT. + * + * For strict-alignment platforms: alignment checks must return + * EFAULT. + */ + ATF_REQUIRE_EQ(do_ustore_32(vm_max_address_minus(3), 0), EFAULT); +} +ATF_TC_CLEANUP(ustore_32_nearmax_overflow, tc) +{ + unload_module(); +} + +#ifdef _LP64 +ATF_TC_WITH_CLEANUP(ustore_64_nearmax_overflow); +ATF_TC_HEAD(ustore_64_nearmax_overflow, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ustore_64 VM_MAX_ADDRESS pointer behavior"); +} +ATF_TC_BODY(ustore_64_nearmax_overflow, tc) +{ + CHECK_MODULE(); + + /* + * For no-strict-alignment platforms: address checks must return + * EFAULT. + * + * For strict-alignment platforms: alignment checks must return + * EFAULT. + */ + ATF_REQUIRE_EQ(do_ustore_64(vm_max_address_minus(7), 0), EFAULT); +} +ATF_TC_CLEANUP(ustore_64_nearmax_overflow, tc) +{ + unload_module(); +} +#endif /* _LP64 */ + + +ATF_TC_WITH_CLEANUP(ucas_32); +ATF_TC_HEAD(ucas_32, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ucas_32 behavior"); +} +ATF_TC_BODY(ucas_32, tc) +{ + uint32_t cell = 0xdeadbeef; + uint32_t actual = 0; + + CHECK_MODULE(); + + ATF_REQUIRE_EQ(do_ucas_32(&cell, 0xdeadbeef, 0xbeefdead, &actual), 0); + ATF_REQUIRE(actual == 0xdeadbeef); + ATF_REQUIRE(cell == 0xbeefdead); +} +ATF_TC_CLEANUP(ucas_32, tc) +{ + unload_module(); +} + +#ifdef _LP64 +ATF_TC_WITH_CLEANUP(ucas_64); +ATF_TC_HEAD(ucas_64, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ucas_64 behavior"); +} +ATF_TC_BODY(ucas_64, tc) +{ + uint64_t cell = 0xdeadbeef; + uint64_t actual = 0; + + CHECK_MODULE(); + + ATF_REQUIRE_EQ(do_ucas_64(&cell, 0xdeadbeef, 0xbeefdead, &actual), 0); + ATF_REQUIRE(actual == 0xdeadbeef); + ATF_REQUIRE(cell == 0xbeefdead); +} +ATF_TC_CLEANUP(ucas_64, tc) +{ + unload_module(); +} +#endif /* _LP64 */ + +ATF_TC_WITH_CLEANUP(ucas_32_miscompare); +ATF_TC_HEAD(ucas_32_miscompare, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ucas_32 behavior with miscompare"); +} +ATF_TC_BODY(ucas_32_miscompare, tc) +{ + uint32_t cell = 0xa5a5a5a5; + uint32_t actual = 0; + + CHECK_MODULE(); + + ATF_REQUIRE_EQ(do_ucas_32(&cell, 0xdeadbeef, 0xbeefdead, &actual), 0); + ATF_REQUIRE(actual == 0xa5a5a5a5); + ATF_REQUIRE(cell == 0xa5a5a5a5); +} +ATF_TC_CLEANUP(ucas_32_miscompare, tc) +{ + unload_module(); +} + +#ifdef _LP64 +ATF_TC_WITH_CLEANUP(ucas_64_miscompare); +ATF_TC_HEAD(ucas_64_miscompare, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ucas_64 behavior with miscompare"); +} +ATF_TC_BODY(ucas_64_miscompare, tc) +{ + uint64_t cell = 0xa5a5a5a5; + uint64_t actual = 0; + + CHECK_MODULE(); + + ATF_REQUIRE_EQ(do_ucas_64(&cell, 0xdeadbeef, 0xbeefdead, &actual), 0); + ATF_REQUIRE(actual == 0xa5a5a5a5); + ATF_REQUIRE(cell == 0xa5a5a5a5); +} +ATF_TC_CLEANUP(ucas_64_miscompare, tc) +{ + unload_module(); +} +#endif /* _LP64 */ + +ATF_TC_WITH_CLEANUP(ucas_32_null); +ATF_TC_HEAD(ucas_32_null, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ucas_32 NULL pointer behavior"); +} +ATF_TC_BODY(ucas_32_null, tc) +{ + uint32_t actual = 0; + + CHECK_MODULE(); + + ATF_REQUIRE_EQ(do_ucas_32(NULL, 0xdeadbeef, 0xbeefdead, &actual), + EFAULT); +} +ATF_TC_CLEANUP(ucas_32_null, tc) +{ + unload_module(); +} + +#ifdef _LP64 +ATF_TC_WITH_CLEANUP(ucas_64_null); +ATF_TC_HEAD(ucas_64_null, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ucas_64 NULL pointer behavior"); +} +ATF_TC_BODY(ucas_64_null, tc) +{ + uint64_t actual = 0; + + CHECK_MODULE(); + + ATF_REQUIRE_EQ(do_ucas_64(NULL, 0xdeadbeef, 0xbeefdead, &actual), + EFAULT); +} +ATF_TC_CLEANUP(ucas_64_null, tc) +{ + unload_module(); +} +#endif /* _LP64 */ + +ATF_TC_WITH_CLEANUP(ucas_32_max); +ATF_TC_HEAD(ucas_32_max, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ucas_32 VM_MAX_ADDRESS pointer behavior"); +} +ATF_TC_BODY(ucas_32_max, tc) +{ + uint32_t actual = 0; + + CHECK_MODULE(); + + ATF_REQUIRE_EQ(do_ucas_32(vm_max_address(), 0xdeadbeef, 0xbeefdead, + &actual), EFAULT); +} +ATF_TC_CLEANUP(ucas_32_max, tc) +{ + unload_module(); +} + +#ifdef _LP64 +ATF_TC_WITH_CLEANUP(ucas_64_max); +ATF_TC_HEAD(ucas_64_max, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ucas_64 VM_MAX_ADDRESS pointer behavior"); +} +ATF_TC_BODY(ucas_64_max, tc) +{ + uint64_t actual = 0; + + CHECK_MODULE(); + + ATF_REQUIRE_EQ(do_ucas_64(vm_max_address(), 0xdeadbeef, 0xbeefdead, + &actual), EFAULT); +} +ATF_TC_CLEANUP(ucas_64_max, tc) +{ + unload_module(); +} +#endif /* _LP64 */ + +ATF_TC_WITH_CLEANUP(ucas_32_nearmax_overflow); +ATF_TC_HEAD(ucas_32_nearmax_overflow, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ucas_32 near-VM_MAX_ADDRESS pointer behavior"); +} +ATF_TC_BODY(ucas_32_nearmax_overflow, tc) +{ + uint32_t actual = 0; + + CHECK_MODULE(); + + /* + * For no-strict-alignment platforms: address checks must return + * EFAULT. + * + * For strict-alignment platforms: alignment checks must return + * EFAULT. + */ + ATF_REQUIRE_EQ(do_ucas_32(vm_max_address_minus(3), 0xdeadbeef, + 0xbeefdead, &actual), EFAULT); +} +ATF_TC_CLEANUP(ucas_32_nearmax_overflow, tc) +{ + unload_module(); +} + +#ifdef _LP64 +ATF_TC_WITH_CLEANUP(ucas_64_nearmax_overflow); +ATF_TC_HEAD(ucas_64_nearmax_overflow, tc) +{ + atf_tc_set_md_var(tc, "descr", + "test for correct ucas_64 near-VM_MAX_ADDRESS pointer behavior"); +} +ATF_TC_BODY(ucas_64_nearmax_overflow, tc) +{ + uint64_t actual = 0; + + CHECK_MODULE(); + + /* + * For no-strict-alignment platforms: address checks must return + * EFAULT. + * + * For strict-alignment platforms: alignment checks must return + * EFAULT. + */ + ATF_REQUIRE_EQ(do_ucas_64(vm_max_address_minus(7), 0xdeadbeef, + 0xbeefdead, &actual), EFAULT); +} +ATF_TC_CLEANUP(ucas_64_nearmax_overflow, tc) +{ + unload_module(); +} +#endif /* _LP64 */ + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, ufetch_8); + ATF_TP_ADD_TC(tp, ufetch_16); + ATF_TP_ADD_TC(tp, ufetch_32); +#ifdef _LP64 + ATF_TP_ADD_TC(tp, ufetch_64); +#endif + + ATF_TP_ADD_TC(tp, ufetch_8_null); + ATF_TP_ADD_TC(tp, ufetch_16_null); + ATF_TP_ADD_TC(tp, ufetch_32_null); +#ifdef _LP64 + ATF_TP_ADD_TC(tp, ufetch_64_null); +#endif + + ATF_TP_ADD_TC(tp, ufetch_8_max); + ATF_TP_ADD_TC(tp, ufetch_16_max); + ATF_TP_ADD_TC(tp, ufetch_32_max); +#ifdef _LP64 + ATF_TP_ADD_TC(tp, ufetch_64_max); +#endif + + ATF_TP_ADD_TC(tp, ufetch_16_nearmax_overflow); + ATF_TP_ADD_TC(tp, ufetch_32_nearmax_overflow); +#ifdef _LP64 + ATF_TP_ADD_TC(tp, ufetch_64_nearmax_overflow); +#endif + + ATF_TP_ADD_TC(tp, ustore_8); + ATF_TP_ADD_TC(tp, ustore_16); + ATF_TP_ADD_TC(tp, ustore_32); +#ifdef _LP64 + ATF_TP_ADD_TC(tp, ustore_64); +#endif + + ATF_TP_ADD_TC(tp, ustore_8_null); + ATF_TP_ADD_TC(tp, ustore_16_null); + ATF_TP_ADD_TC(tp, ustore_32_null); +#ifdef _LP64 + ATF_TP_ADD_TC(tp, ustore_64_null); +#endif + + ATF_TP_ADD_TC(tp, ustore_8_max); + ATF_TP_ADD_TC(tp, ustore_16_max); + ATF_TP_ADD_TC(tp, ustore_32_max); +#ifdef _LP64 + ATF_TP_ADD_TC(tp, ustore_64_max); +#endif + + ATF_TP_ADD_TC(tp, ustore_16_nearmax_overflow); + ATF_TP_ADD_TC(tp, ustore_32_nearmax_overflow); +#ifdef _LP64 + ATF_TP_ADD_TC(tp, ustore_64_nearmax_overflow); +#endif + + ATF_TP_ADD_TC(tp, ucas_32); +#ifdef _LP64 + ATF_TP_ADD_TC(tp, ucas_64); +#endif + + ATF_TP_ADD_TC(tp, ucas_32_miscompare); +#ifdef _LP64 + ATF_TP_ADD_TC(tp, ucas_64_miscompare); +#endif + + ATF_TP_ADD_TC(tp, ucas_32_null); +#ifdef _LP64 + ATF_TP_ADD_TC(tp, ucas_64_null); +#endif + + ATF_TP_ADD_TC(tp, ucas_32_max); +#ifdef _LP64 + ATF_TP_ADD_TC(tp, ucas_64_max); +#endif + + ATF_TP_ADD_TC(tp, ucas_32_nearmax_overflow); +#ifdef _LP64 + ATF_TP_ADD_TC(tp, ucas_64_nearmax_overflow); +#endif + + return atf_no_error(); +} diff --git a/modules/ufetchstore/ufetchstore_tester.c b/modules/ufetchstore/ufetchstore_tester.c new file mode 100644 --- /dev/null +++ b/modules/ufetchstore/ufetchstore_tester.c @@ -0,0 +1,230 @@ +/* $NetBSD: ufetchstore_tester.c,v 1.1 2019/04/15 23:41:23 christos Exp $ */ + +/*- + * Copyright (c) 2019 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +#include +#include +#include + +#include "common.h" + +static struct tester_ctx { + struct sysctllog *ctx_sysctllog; +} tester_ctx; + +static int +test_ufetch(void * const uaddr, struct ufetchstore_test_args * const args) +{ + int error = 0; + + switch (args->size) { + case 8: + args->fetchstore_error = ufetch_8(uaddr, &args->val8); + break; + case 16: + args->fetchstore_error = ufetch_16(uaddr, &args->val16); + break; + case 32: + args->fetchstore_error = ufetch_32(uaddr, &args->val32); + break; +#ifdef _LP64 + case 64: + args->fetchstore_error = ufetch_64(uaddr, &args->val64); + break; +#endif /* _LP64 */ + default: + error = EINVAL; + } + + return error; +} + +static int +test_ustore(void * const uaddr, struct ufetchstore_test_args * const args) +{ + int error = 0; + + switch (args->size) { + case 8: + args->fetchstore_error = ustore_8(uaddr, args->val8); + break; + case 16: + args->fetchstore_error = ustore_16(uaddr, args->val16); + break; + case 32: + args->fetchstore_error = ustore_32(uaddr, args->val32); + break; +#ifdef _LP64 + case 64: + args->fetchstore_error = ustore_64(uaddr, args->val64); + break; +#endif /* _LP64 */ + default: + error = EINVAL; + } + + return error; +} + +static int +test_ucas(void * const uaddr, struct ufetchstore_test_args * const args) +{ + int error = 0; + + switch (args->size) { + case 32: + args->fetchstore_error = ucas_32(uaddr, + args->ea_val32, args->val32, &args->ea_val32); + break; +#ifdef _LP64 + case 64: + args->fetchstore_error = ucas_64(uaddr, + args->ea_val64, args->val64, &args->ea_val64); + break; +#endif /* _LP64 */ + default: + error = EINVAL; + } + + return error; +} + +static int +do_ufetchstore_test(SYSCTLFN_ARGS) +{ + struct sysctlnode node; + struct ufetchstore_test_args *uargs, args; + uint64_t args64; + int error; + + node = *rnode; + + uargs = NULL; + node.sysctl_data = &args64; + error = sysctl_lookup(SYSCTLFN_CALL(&node)); + if (error) + return error; + if (newp == NULL) + return EINVAL; + + uargs = (void *)(uintptr_t)args64; + + error = copyin(uargs, &args, sizeof(args)); + if (error) + return error; + + args.fetchstore_error = EBADF; /* poison */ + + void *uaddr = (void *)(uintptr_t)args.uaddr64; + + switch (args.test_op) { + case OP_LOAD: + error = test_ufetch(uaddr, &args); + break; + + case OP_STORE: + error = test_ustore(uaddr, &args); + break; + + case OP_CAS: + error = test_ucas(uaddr, &args); + break; + + default: + error = EINVAL; + } + + if (error == 0) + error = copyout(&args, uargs, sizeof(args)); + return error; +} + +static int +ufetchstore_tester_init(void) +{ + struct sysctllog **log = &tester_ctx.ctx_sysctllog; + const struct sysctlnode *rnode, *cnode; + int error; + + error = sysctl_createv(log, 0, NULL, &rnode, CTLFLAG_PERMANENT, + CTLTYPE_NODE, "ufetchstore_test", + SYSCTL_DESCR("ufetchstore testing interface"), + NULL, 0, NULL, 0, CTL_KERN, CTL_CREATE, CTL_EOL); + if (error) + goto return_error; + + error = sysctl_createv(log, 0, &rnode, &cnode, + /* + * It's really a pointer to our argument structure, because + * we want to have precise control over when copyin / copyout + * happens. + */ + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_QUAD, "test", + SYSCTL_DESCR("execute a ufetchstore test"), + do_ufetchstore_test, 0, + (void *)&tester_ctx, 0, CTL_CREATE, CTL_EOL); + + return_error: + if (error) + sysctl_teardown(log); + return error; +} + +static int +ufetchstore_tester_fini(void) +{ + sysctl_teardown(&tester_ctx.ctx_sysctllog); + return 0; +} + +static int +ufetchstore_tester_modcmd(modcmd_t cmd, void *arg __unused) +{ + int error; + + switch (cmd) { + case MODULE_CMD_INIT: + error = ufetchstore_tester_init(); + break; + + case MODULE_CMD_FINI: + error = ufetchstore_tester_fini(); + break; + + case MODULE_CMD_STAT: + default: + error = ENOTTY; + } + + return error; +} + +MODULE(MODULE_CLASS_MISC, ufetchstore_tester, NULL); diff --git a/modules/x86_pte_tester/Makefile b/modules/x86_pte_tester/Makefile new file mode 100644 --- /dev/null +++ b/modules/x86_pte_tester/Makefile @@ -0,0 +1,14 @@ +# $NetBSD: Makefile,v 1.2 2020/05/01 22:24:19 christos Exp $ + +.include + +KMOD= x86_pte_tester +KMODULEDIR= ${TESTSBASE}/modules/${KMOD} + +SRCS= x86_pte_tester.c + +ATFFILE= no +NOMAN= # defined + +.include +.include diff --git a/modules/x86_pte_tester/x86_pte_tester.c b/modules/x86_pte_tester/x86_pte_tester.c new file mode 100644 --- /dev/null +++ b/modules/x86_pte_tester/x86_pte_tester.c @@ -0,0 +1,498 @@ +/* $NetBSD: x86_pte_tester.c,v 1.2 2020/04/26 11:56:38 maxv Exp $ */ + +/* + * Copyright (c) 2016 The NetBSD Foundation, Inc. + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +#define __HAVE_DIRECT_MAP +#define __HAVE_PCPU_AREA +#define SVS + +#include +#include +#include +#include +#include +#include +#include + +#if defined(__x86_64__) +# include +# define NLEVEL 4 +#else +# error "Unsupported configuration" +#endif + +static struct { + struct sysctllog *ctx_sysctllog; + vaddr_t levels[NLEVEL]; + struct { + size_t l4; + size_t l3; + size_t l2; + size_t l1; + } coord; + struct { + size_t n_rwx; + size_t n_shstk; + bool kernel_map_with_low_ptes; + bool pte_is_user_accessible; + size_t n_user_space_is_kernel; + size_t n_kernel_space_is_user; + size_t n_svs_g_bit_set; + } results; +} tester_ctx; + +typedef enum { + WALK_NEXT, /* go to the next level */ + WALK_SKIP, /* skip the next level, but keep iterating on the current one */ + WALK_STOP /* stop the iteration on the current level */ +} walk_type; + +/* -------------------------------------------------------------------------- */ + +#define is_flag(__ent, __flag) (((__ent) & __flag) != 0) +#define is_valid(__ent) is_flag(__ent, PTE_P) +#define get_pa(__pde) (__pde & PTE_FRAME) + +#define L4_MAX_NENTRIES (PAGE_SIZE / sizeof(pd_entry_t)) +#define L3_MAX_NENTRIES (PAGE_SIZE / sizeof(pd_entry_t)) +#define L2_MAX_NENTRIES (PAGE_SIZE / sizeof(pd_entry_t)) +#define L1_MAX_NENTRIES (PAGE_SIZE / sizeof(pd_entry_t)) + +static void +scan_l1(paddr_t pa, walk_type (fn)(pd_entry_t pde, size_t slot, int lvl)) +{ + pd_entry_t *pd = (pd_entry_t *)tester_ctx.levels[0]; + size_t i; + + pmap_kenter_pa(tester_ctx.levels[0], pa, VM_PROT_READ, 0); + pmap_update(pmap_kernel()); + + for (i = 0; i < L1_MAX_NENTRIES; i++) { + tester_ctx.coord.l1 = i; + if (is_valid(pd[i])) { + fn(pd[i], i, 1); + } + } + + pmap_kremove(tester_ctx.levels[0], PAGE_SIZE); + pmap_update(pmap_kernel()); +} + +static void +scan_l2(paddr_t pa, walk_type (fn)(pd_entry_t pde, size_t slot, int lvl)) +{ + pd_entry_t *pd = (pd_entry_t *)tester_ctx.levels[1]; + walk_type ret; + size_t i; + + pmap_kenter_pa(tester_ctx.levels[1], pa, VM_PROT_READ, 0); + pmap_update(pmap_kernel()); + + for (i = 0; i < L2_MAX_NENTRIES; i++) { + tester_ctx.coord.l2 = i; + if (!is_valid(pd[i])) + continue; + ret = fn(pd[i], i, 2); + if (ret == WALK_STOP) + break; + if (is_flag(pd[i], PTE_PS)) + continue; + if (ret == WALK_NEXT) + scan_l1(get_pa(pd[i]), fn); + } + + pmap_kremove(tester_ctx.levels[1], PAGE_SIZE); + pmap_update(pmap_kernel()); +} + +static void +scan_l3(paddr_t pa, walk_type (fn)(pd_entry_t pde, size_t slot, int lvl)) +{ + pd_entry_t *pd = (pd_entry_t *)tester_ctx.levels[2]; + walk_type ret; + size_t i; + + pmap_kenter_pa(tester_ctx.levels[2], pa, VM_PROT_READ, 0); + pmap_update(pmap_kernel()); + + for (i = 0; i < L3_MAX_NENTRIES; i++) { + tester_ctx.coord.l3 = i; + if (!is_valid(pd[i])) + continue; + ret = fn(pd[i], i, 3); + if (ret == WALK_STOP) + break; + if (is_flag(pd[i], PTE_PS)) + continue; + if (ret == WALK_NEXT) + scan_l2(get_pa(pd[i]), fn); + } + + pmap_kremove(tester_ctx.levels[2], PAGE_SIZE); + pmap_update(pmap_kernel()); +} + +static void +scan_l4(paddr_t pa, walk_type (fn)(pd_entry_t pde, size_t slot, int lvl)) +{ + pd_entry_t *pd = (pd_entry_t *)tester_ctx.levels[3]; + walk_type ret; + size_t i; + + pmap_kenter_pa(tester_ctx.levels[3], pa, VM_PROT_READ, 0); + pmap_update(pmap_kernel()); + + for (i = 0; i < L4_MAX_NENTRIES; i++) { + tester_ctx.coord.l4 = i; + if (!is_valid(pd[i])) + continue; + ret = fn(pd[i], i, 4); + if (ret == WALK_STOP) + break; + if (is_flag(pd[i], PTE_PS)) + continue; + if (ret == WALK_NEXT) + scan_l3(get_pa(pd[i]), fn); + } + + pmap_kremove(tester_ctx.levels[3], PAGE_SIZE); + pmap_update(pmap_kernel()); +} + +static void +scan_tree(paddr_t pa, walk_type (fn)(pd_entry_t pde, size_t slot, int lvl)) +{ + scan_l4(pa, fn); +} + +/* -------------------------------------------------------------------------- */ + +/* + * Rule: the number of kernel RWX pages should be zero. + */ +static walk_type +count_krwx(pd_entry_t pde, size_t slot, int lvl) +{ + if (lvl == NLEVEL && slot < 256) { + return WALK_SKIP; + } + if (is_flag(pde, PTE_NX) || !is_flag(pde, PTE_W)) { + return WALK_SKIP; + } + if (lvl != 1 && !is_flag(pde, PTE_PS)) { + return WALK_NEXT; + } + + if (lvl == 4) { + tester_ctx.results.n_rwx += (NBPD_L4 / PAGE_SIZE); + } else if (lvl == 3) { + tester_ctx.results.n_rwx += (NBPD_L3 / PAGE_SIZE); + } else if (lvl == 2) { + tester_ctx.results.n_rwx += (NBPD_L2 / PAGE_SIZE); + } else if (lvl == 1) { + tester_ctx.results.n_rwx += (NBPD_L1 / PAGE_SIZE); + } + + return WALK_NEXT; +} + +/* + * Rule: the number of kernel SHSTK pages should be zero. + */ +static walk_type +count_kshstk(pd_entry_t pde, size_t slot, int lvl) +{ + if (lvl == NLEVEL && slot < 256) { + return WALK_SKIP; + } + + if (is_flag(pde, PTE_PS) || lvl == 1) { + if (!is_flag(pde, PTE_W) && is_flag(pde, PTE_D)) { + if (lvl == 4) { + tester_ctx.results.n_shstk += (NBPD_L4 / PAGE_SIZE); + } else if (lvl == 3) { + tester_ctx.results.n_shstk += (NBPD_L3 / PAGE_SIZE); + } else if (lvl == 2) { + tester_ctx.results.n_shstk += (NBPD_L2 / PAGE_SIZE); + } else if (lvl == 1) { + tester_ctx.results.n_shstk += (NBPD_L1 / PAGE_SIZE); + } + } + return WALK_SKIP; + } + + if (!is_flag(pde, PTE_W)) { + return WALK_SKIP; + } + + return WALK_NEXT; +} + +/* + * Rule: the lower half of the kernel map must be zero. + */ +static walk_type +check_kernel_map(pd_entry_t pde, size_t slot, int lvl) +{ + if (lvl != NLEVEL) { + return WALK_STOP; + } + if (slot >= 256) { + return WALK_SKIP; + } + if (pde != 0) { + tester_ctx.results.kernel_map_with_low_ptes |= true; + } + return WALK_SKIP; +} + +/* + * Rule: the PTE space must not have user permissions. + */ +static walk_type +check_pte_space(pd_entry_t pde, size_t slot, int lvl) +{ + if (lvl != NLEVEL) { + return WALK_STOP; + } + if (slot != PDIR_SLOT_PTE) { + return WALK_SKIP; + } + if (is_flag(pde, PTE_U)) { + tester_ctx.results.pte_is_user_accessible |= true; + } + return WALK_SKIP; +} + +/* + * Rule: each page in the lower half must have user permissions. + */ +static walk_type +check_user_space(pd_entry_t pde, size_t slot, int lvl) +{ + if (lvl == NLEVEL && slot >= 256) { + return WALK_SKIP; + } + if (!is_flag(pde, PTE_U)) { + tester_ctx.results.n_user_space_is_kernel += 1; + return WALK_SKIP; + } + return WALK_NEXT; +} + +/* + * Rule: each page in the higher half must have kernel permissions. + */ +static walk_type +check_kernel_space(pd_entry_t pde, size_t slot, int lvl) +{ + if (lvl == NLEVEL && slot < 256) { + return WALK_SKIP; + } + if (lvl == NLEVEL && slot == PDIR_SLOT_PTE) { + return WALK_SKIP; + } + if (is_flag(pde, PTE_U)) { + tester_ctx.results.n_kernel_space_is_user += 1; + return WALK_SKIP; + } + return WALK_NEXT; +} + +/* + * Rule: the SVS map is allowed to use the G bit only on the PCPU area. + */ +static walk_type +check_svs_g_bit(pd_entry_t pde, size_t slot, int lvl) +{ + if (lvl == NLEVEL && slot == PDIR_SLOT_PCPU) { + return WALK_SKIP; + } + if (is_flag(pde, PTE_G)) { + tester_ctx.results.n_svs_g_bit_set += 1; + return WALK_SKIP; + } + return WALK_NEXT; +} + +/* -------------------------------------------------------------------------- */ + +static void +scan_svs(void) +{ + extern bool svs_enabled; + paddr_t pa0; + + if (!svs_enabled) { + tester_ctx.results.n_svs_g_bit_set = -1; + return; + } + + kpreempt_disable(); + pa0 = curcpu()->ci_svs_updirpa; + scan_tree(pa0, &check_user_space); + scan_tree(pa0, &check_kernel_space); + scan_tree(pa0, &check_svs_g_bit); + kpreempt_enable(); +} + +static void +scan_proc(struct proc *p) +{ + struct pmap *pmap = p->p_vmspace->vm_map.pmap; + paddr_t pa0; + + mutex_enter(&pmap->pm_lock); + + kpreempt_disable(); + pa0 = (paddr_t)pmap->pm_pdirpa[0]; + scan_tree(pa0, &check_user_space); + scan_tree(pa0, &check_kernel_space); + scan_tree(pa0, &check_pte_space); + kpreempt_enable(); + + mutex_exit(&pmap->pm_lock); +} + +static void +x86_pte_run_scans(void) +{ + struct pmap *kpm = pmap_kernel(); + paddr_t pa0; + + memset(&tester_ctx.results, 0, sizeof(tester_ctx.results)); + + /* Scan the current user process. */ + scan_proc(curproc); + + /* Scan the SVS mapping. */ + scan_svs(); + + /* Scan the kernel map. */ + pa0 = (paddr_t)kpm->pm_pdirpa[0]; + scan_tree(pa0, &count_krwx); + scan_tree(pa0, &count_kshstk); + scan_tree(pa0, &check_kernel_map); +} + +static void +x86_pte_levels_init(void) +{ + size_t i; + for (i = 0; i < NLEVEL; i++) { + tester_ctx.levels[i] = uvm_km_alloc(kernel_map, PAGE_SIZE, 0, + UVM_KMF_VAONLY); + } +} + +static void +x86_pte_levels_destroy(void) +{ + size_t i; + for (i = 0; i < NLEVEL; i++) { + uvm_km_free(kernel_map, tester_ctx.levels[i], PAGE_SIZE, + UVM_KMF_VAONLY); + } +} + +/* -------------------------------------------------------------------------- */ + +static int +x86_pte_sysctl_run(SYSCTLFN_ARGS) +{ + if (oldlenp == NULL) + return EINVAL; + + x86_pte_run_scans(); + + if (oldp == NULL) { + *oldlenp = sizeof(tester_ctx.results); + return 0; + } + + if (*oldlenp < sizeof(tester_ctx.results)) + return ENOMEM; + + return copyout(&tester_ctx.results, oldp, sizeof(tester_ctx.results)); +} + +static int +x86_pte_sysctl_init(void) +{ + struct sysctllog **log = &tester_ctx.ctx_sysctllog; + const struct sysctlnode *rnode, *cnode; + int error; + + error = sysctl_createv(log, 0, NULL, &rnode, CTLFLAG_PERMANENT, + CTLTYPE_NODE, "x86_pte_test", + SYSCTL_DESCR("x86_pte testing interface"), + NULL, 0, NULL, 0, CTL_KERN, CTL_CREATE, CTL_EOL); + if (error) + goto out; + + error = sysctl_createv(log, 0, &rnode, &cnode, CTLFLAG_PERMANENT, + CTLTYPE_STRUCT, "test", + SYSCTL_DESCR("execute a x86_pte test"), + x86_pte_sysctl_run, 0, NULL, 0, CTL_CREATE, CTL_EOL); + +out: + if (error) + sysctl_teardown(log); + return error; +} + +static void +x86_pte_sysctl_destroy(void) +{ + sysctl_teardown(&tester_ctx.ctx_sysctllog); +} + +/* -------------------------------------------------------------------------- */ + +MODULE(MODULE_CLASS_MISC, x86_pte_tester, NULL); + +static int +x86_pte_tester_modcmd(modcmd_t cmd, void *arg __unused) +{ + int error = 0; + + switch (cmd) { + case MODULE_CMD_INIT: + x86_pte_levels_init(); + error = x86_pte_sysctl_init(); + break; + case MODULE_CMD_FINI: + x86_pte_sysctl_destroy(); + x86_pte_levels_destroy(); + break; + default: + error = ENOTTY; + break; + } + + return error; +} diff --git a/net/Makefile b/net/Makefile --- a/net/Makefile +++ b/net/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.30 2016/11/26 03:19:48 ozaki-r Exp $ +# $NetBSD: Makefile,v 1.39 2021/07/14 03:22:33 ozaki-r Exp $ .include @@ -6,8 +6,10 @@ TESTS_SUBDIRS= fdpass in_cksum net sys .if (${MKRUMP} != "no") && !defined(BSD_MK_COMPAT_FILE) -TESTS_SUBDIRS+= arp bpf bpfilter carp icmp if if_bridge if_gif if_loop -TESTS_SUBDIRS+= if_pppoe if_tap if_tun mcast mpls ndp npf route if_vlan +TESTS_SUBDIRS+= altq arp bpf bpfilter can carp icmp if if_bridge if_gif +TESTS_SUBDIRS+= if_ipsec if_l2tp if_lagg if_loop if_pppoe if_tap +TESTS_SUBDIRS+= if_tun if_vether if_vlan if_wg ipsec mcast mpls +TESTS_SUBDIRS+= ndp npf route .if (${MKSLJIT} != "no") TESTS_SUBDIRS+= bpfjit .endif diff --git a/net/altq/Makefile b/net/altq/Makefile new file mode 100644 --- /dev/null +++ b/net/altq/Makefile @@ -0,0 +1,13 @@ +# $NetBSD: Makefile,v 1.1 2021/07/14 03:22:33 ozaki-r Exp $ +# + +.include + +TESTSDIR= ${TESTSBASE}/net/altq + +.for name in cbq + TESTS_SH+= t_${name} + TESTS_SH_SRC_t_${name}= ../net_common.sh t_${name}.sh +.endfor + +.include diff --git a/net/altq/t_cbq.sh b/net/altq/t_cbq.sh new file mode 100644 --- /dev/null +++ b/net/altq/t_cbq.sh @@ -0,0 +1,452 @@ +# $NetBSD: t_cbq.sh,v 1.3 2021/07/16 02:33:32 ozaki-r Exp $ +# +# Copyright (c) 2021 Internet Initiative Japan Inc. +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +SOCK_LOCAL=unix://altq_local +SOCK_REMOTE=unix://altq_remote +BUS=bus_altq +TIMEOUT=3 + +# rumphijack can't handle AF_LOCAL socket (/var/run/altq_quip) correctly, +# so use the socket via the host. +HIJACKING_ALTQ="$HIJACKING,blanket=/dev/altq/altq:/dev/altq/cbq:/etc/altq.conf:/var/run/altqd.pid" + +DEBUG=${DEBUG:-false} + +IP_LOCAL1=10.0.0.1 +IP_LOCAL2=10.0.1.1 +IP_REMOTE11=10.0.0.2 +IP_REMOTE12=10.0.0.22 +IP_REMOTE13=10.0.0.23 +IP_REMOTE21=10.0.1.2 +IP_REMOTE22=10.0.1.22 +ALTQD_PIDFILE=./pid + +start_altqd() +{ + + $HIJACKING_ALTQ altqd + + sleep 0.1 + if $HIJACKING_ALTQ test ! -f /var/run/altqd.pid; then + sleep 1 + fi + + $HIJACKING_ALTQ test -f /var/run/altqd.pid + if [ $? != 0 ]; then + atf_fail "starting altqd failed" + fi + + $HIJACKING_ALTQ cat /var/run/altqd.pid > $ALTQD_PIDFILE +} + +start_altqd_basic() +{ + + export RUMP_SERVER=$SOCK_LOCAL + + $HIJACKING_ALTQ mkdir -p /rump/etc + $HIJACKING_ALTQ mkdir -p /rump/var/run + + cat > ./altq.conf <<-EOF + interface shmif0 cbq + class cbq shmif0 root_class NULL pbandwidth 100 + class cbq shmif0 normal_class root_class pbandwidth 50 default + filter shmif0 normal_class $IP_REMOTE11 0 0 0 0 + class cbq shmif0 drop_class root_class pbandwidth 0 + filter shmif0 drop_class $IP_REMOTE12 0 0 0 0 + EOF + $DEBUG && cat ./altq.conf + atf_check -s exit:0 $HIJACKING_ALTQ cp ./altq.conf /rump/etc/altq.conf + atf_check -s exit:0 $HIJACKING_ALTQ test -f /rump/etc/altq.conf + + start_altqd + + $DEBUG && $HIJACKING_ALTQ altqstat -s + $HIJACKING_ALTQ altqstat -c 1 >./out + $DEBUG && cat ./out + atf_check -s exit:0 \ + -o match:"altqstat: cbq on interface shmif0" \ + -o match:'Class 1 on Interface shmif0: root_class' \ + -o match:'Class 2 on Interface shmif0: normal_class' \ + -o match:'Class 3 on Interface shmif0: ctl_class' \ + -o match:'Class 4 on Interface shmif0: drop_class' \ + cat ./out + rm -f ./out +} + +shutdown_altqd() +{ + local pid="$(cat $ALTQD_PIDFILE)" + + if [ -n "$pid" ]; then + pgrep -x altqd | grep -q $pid + if [ $? = 0 ]; then + kill $(cat $ALTQD_PIDFILE) + sleep 1 + fi + $DEBUG && pgrep -x altqd + fi +} + +check_counter() +{ + local file=$1 + local name=$2 + local match="$3" + + grep -A 8 ${name}_class $file > $file.$name + atf_check -s exit:0 -o match:"$match" cat $file.$name + rm -f $file.$name +} + +test_altq_cbq_basic_ipv4() +{ + local ifconfig="atf_check -s exit:0 rump.ifconfig" + local ping="atf_check -s exit:0 -o ignore rump.ping" + local opts="-q -c 1 -w 1" + + rump_server_fs_start $SOCK_LOCAL local altq + rump_server_start $SOCK_REMOTE + + rump_server_add_iface $SOCK_LOCAL shmif0 $BUS + rump_server_add_iface $SOCK_REMOTE shmif0 $BUS + + export RUMP_SERVER=$SOCK_LOCAL + $ifconfig shmif0 inet $IP_LOCAL1/24 + export RUMP_SERVER=$SOCK_REMOTE + $ifconfig shmif0 inet $IP_REMOTE11/24 + $ifconfig shmif0 inet $IP_REMOTE12/24 alias + $ifconfig -w 10 + + export RUMP_SERVER=$SOCK_LOCAL + # Invoke ARP + $ping $opts $IP_REMOTE11 + $ping $opts $IP_REMOTE12 + + start_altqd_basic + + export RUMP_SERVER=$SOCK_LOCAL + $ping $opts $IP_REMOTE11 + + $HIJACKING_ALTQ altqstat -c 1 >./out + $DEBUG && cat ./out + + check_counter ./out normal 'pkts: 1' + check_counter ./out root 'pkts: 1' + check_counter ./out drop 'pkts: 0' + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s not-exit:0 -o ignore -e match:"No buffer space available" \ + rump.ping $opts $IP_REMOTE12 + + $HIJACKING_ALTQ altqstat -c 1 >./out + $DEBUG && cat ./out + + check_counter ./out drop 'drops: 1' + check_counter ./out drop 'pkts: 0' + check_counter ./out normal 'pkts: 1' + check_counter ./out root 'pkts: 1' + + rm -f ./out + + shutdown_altqd + + rump_server_destroy_ifaces +} + +start_altqd_multi_ifaces() +{ + + export RUMP_SERVER=$SOCK_LOCAL + + $HIJACKING_ALTQ mkdir -p /rump/etc + $HIJACKING_ALTQ mkdir -p /rump/var/run + + cat > ./altq.conf <<-EOF + interface shmif0 cbq + class cbq shmif0 root_class NULL pbandwidth 100 + class cbq shmif0 normal_class root_class pbandwidth 50 default + filter shmif0 normal_class $IP_REMOTE11 0 0 0 0 + class cbq shmif0 drop_class root_class pbandwidth 0 + filter shmif0 drop_class $IP_REMOTE12 0 0 0 0 + interface shmif1 cbq + class cbq shmif1 root_class NULL pbandwidth 100 + class cbq shmif1 normal_class root_class pbandwidth 50 default + filter shmif1 normal_class $IP_REMOTE21 0 0 0 0 + class cbq shmif1 drop_class root_class pbandwidth 0 + filter shmif1 drop_class $IP_REMOTE22 0 0 0 0 + EOF + $DEBUG && cat ./altq.conf + atf_check -s exit:0 $HIJACKING_ALTQ cp ./altq.conf /rump/etc/altq.conf + $HIJACKING_ALTQ test -f /rump/etc/altq.conf + + start_altqd + + $DEBUG && $HIJACKING_ALTQ altqstat -s + + $HIJACKING_ALTQ altqstat -c 1 -i shmif0 >./out + $DEBUG && cat ./out + atf_check -s exit:0 \ + -o match:"altqstat: cbq on interface shmif0" \ + -o match:'Class 1 on Interface shmif0: root_class' \ + -o match:'Class 2 on Interface shmif0: normal_class' \ + -o match:'Class 3 on Interface shmif0: ctl_class' \ + -o match:'Class 4 on Interface shmif0: drop_class' \ + cat ./out + + $HIJACKING_ALTQ altqstat -c 1 -i shmif1 >./out + $DEBUG && cat ./out + atf_check -s exit:0 \ + -o match:"altqstat: cbq on interface shmif1" \ + -o match:'Class 1 on Interface shmif1: root_class' \ + -o match:'Class 2 on Interface shmif1: normal_class' \ + -o match:'Class 3 on Interface shmif1: ctl_class' \ + -o match:'Class 4 on Interface shmif1: drop_class' \ + cat ./out + + rm -f ./out +} + +test_altq_cbq_multi_ifaces_ipv4() +{ + local ifconfig="atf_check -s exit:0 rump.ifconfig" + local ping="atf_check -s exit:0 -o ignore rump.ping" + local opts="-q -c 1 -w 1" + + rump_server_fs_start $SOCK_LOCAL local altq + rump_server_start $SOCK_REMOTE + + rump_server_add_iface $SOCK_LOCAL shmif0 $BUS + rump_server_add_iface $SOCK_LOCAL shmif1 $BUS + rump_server_add_iface $SOCK_REMOTE shmif0 $BUS + + export RUMP_SERVER=$SOCK_LOCAL + $ifconfig shmif0 inet $IP_LOCAL1/24 + $ifconfig shmif1 inet $IP_LOCAL2/24 + export RUMP_SERVER=$SOCK_REMOTE + $ifconfig shmif0 inet $IP_REMOTE11/24 + $ifconfig shmif0 inet $IP_REMOTE12/24 alias + $ifconfig shmif0 inet $IP_REMOTE21/24 alias + $ifconfig shmif0 inet $IP_REMOTE22/24 alias + $ifconfig -w 10 + + export RUMP_SERVER=$SOCK_LOCAL + # Invoke ARP + $ping $opts $IP_REMOTE11 + $ping $opts $IP_REMOTE12 + $ping $opts $IP_REMOTE21 + $ping $opts $IP_REMOTE22 + + start_altqd_multi_ifaces + + export RUMP_SERVER=$SOCK_LOCAL + $ping $opts $IP_REMOTE11 + + $HIJACKING_ALTQ altqstat -c 1 -i shmif0 >./out + $DEBUG && cat ./out + + check_counter ./out normal 'pkts: 1' + check_counter ./out root 'pkts: 1' + check_counter ./out drop 'pkts: 0' + + $ping $opts $IP_REMOTE21 + + $HIJACKING_ALTQ altqstat -c 1 -i shmif1 >./out + $DEBUG && cat ./out + + check_counter ./out normal 'pkts: 1' + check_counter ./out root 'pkts: 1' + check_counter ./out drop 'pkts: 0' + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s not-exit:0 -o ignore -e match:"No buffer space available" \ + rump.ping $opts $IP_REMOTE12 + + $HIJACKING_ALTQ altqstat -c 1 -i shmif0 >./out + $DEBUG && cat ./out + + check_counter ./out drop 'drops: 1' + check_counter ./out drop 'pkts: 0' + check_counter ./out normal 'pkts: 1' + check_counter ./out root 'pkts: 1' + + atf_check -s not-exit:0 -o ignore -e match:"No buffer space available" \ + rump.ping $opts $IP_REMOTE22 + + $HIJACKING_ALTQ altqstat -c 1 -i shmif1 >./out + $DEBUG && cat ./out + + check_counter ./out drop 'drops: 1' + check_counter ./out drop 'pkts: 0' + check_counter ./out normal 'pkts: 1' + check_counter ./out root 'pkts: 1' + + rm -f ./out + + shutdown_altqd + + rump_server_destroy_ifaces +} + +start_altqd_options() +{ + + export RUMP_SERVER=$SOCK_LOCAL + + $HIJACKING_ALTQ mkdir -p /rump/etc + $HIJACKING_ALTQ mkdir -p /rump/var/run + + # - no-tbr and no-control are specified + # - root_class is the default class + cat > ./altq.conf <<-EOF + interface shmif0 cbq no-tbr no-control + class cbq shmif0 root_class NULL pbandwidth 100 default + class cbq shmif0 normal_class root_class pbandwidth 50 + filter shmif0 normal_class $IP_REMOTE11 0 0 0 0 + class cbq shmif0 drop_class root_class pbandwidth 0 + filter shmif0 drop_class $IP_REMOTE12 0 0 0 0 + EOF + $DEBUG && cat ./altq.conf + atf_check -s exit:0 $HIJACKING_ALTQ cp ./altq.conf /rump/etc/altq.conf + $HIJACKING_ALTQ test -f /rump/etc/altq.conf + + start_altqd + + $DEBUG && $HIJACKING_ALTQ altqstat -s + $HIJACKING_ALTQ altqstat -c 1 >./out + $DEBUG && cat ./out + atf_check -s exit:0 \ + -o match:"altqstat: cbq on interface shmif0" \ + -o match:'Class 1 on Interface shmif0: root_class' \ + -o match:'Class 2 on Interface shmif0: normal_class' \ + -o match:'Class 3 on Interface shmif0: drop_class' \ + cat ./out + atf_check -s exit:0 -o not-match:'shmif0: ctl_class' cat ./out + + rm -f ./out +} + +test_altq_cbq_options_ipv4() +{ + local ifconfig="atf_check -s exit:0 rump.ifconfig" + local ping="atf_check -s exit:0 -o ignore rump.ping" + local opts="-q -c 1 -w 1" + + rump_server_fs_start $SOCK_LOCAL local altq + rump_server_start $SOCK_REMOTE + + rump_server_add_iface $SOCK_LOCAL shmif0 $BUS + rump_server_add_iface $SOCK_REMOTE shmif0 $BUS + + export RUMP_SERVER=$SOCK_LOCAL + $ifconfig shmif0 inet $IP_LOCAL1/24 + export RUMP_SERVER=$SOCK_REMOTE + $ifconfig shmif0 inet $IP_REMOTE11/24 + $ifconfig shmif0 inet $IP_REMOTE12/24 alias + $ifconfig shmif0 inet $IP_REMOTE13/24 alias + $ifconfig -w 10 + + export RUMP_SERVER=$SOCK_LOCAL + # Invoke ARP + $ping $opts $IP_REMOTE11 + $ping $opts $IP_REMOTE12 + $ping $opts $IP_REMOTE13 + + start_altqd_options + + export RUMP_SERVER=$SOCK_LOCAL + $ping $opts $IP_REMOTE11 + + $HIJACKING_ALTQ altqstat -c 1 >./out + $DEBUG && cat ./out + + check_counter ./out normal 'pkts: 1' + check_counter ./out root 'pkts: 1' + check_counter ./out drop 'pkts: 0' + + atf_check -s not-exit:0 -o ignore -e match:"No buffer space available" \ + rump.ping $opts $IP_REMOTE12 + + $HIJACKING_ALTQ altqstat -c 1 >./out + $DEBUG && cat ./out + + check_counter ./out drop 'drops: 1' + check_counter ./out drop 'pkts: 0' + check_counter ./out normal 'pkts: 1' + check_counter ./out root 'pkts: 1' + + # The packet goes to the default class + $ping $opts $IP_REMOTE13 + + $HIJACKING_ALTQ altqstat -c 1 >./out + $DEBUG && cat ./out + + check_counter ./out drop 'pkts: 0' + check_counter ./out normal 'pkts: 1' + check_counter ./out root 'pkts: 2' + + rm -f ./out + + shutdown_altqd + + rump_server_destroy_ifaces +} + +add_test_case() +{ + local algo=$1 + local type=$2 + local ipproto=$3 + + name="altq_${algo}_${type}_${ipproto}" + desc="Tests for ALTQ $algo (${type}) on ${ipproto}" + + atf_test_case ${name} cleanup + eval " + ${name}_head() { + atf_set descr \"$desc\" + atf_set require.progs rump_server altqd altqstat + } + ${name}_body() { + test_altq_${algo}_${type}_${ipproto} + } + ${name}_cleanup() { + shutdown_altqd + \$DEBUG && dump + cleanup + } + " + atf_add_test_case ${name} +} + +atf_init_test_cases() +{ + + add_test_case cbq basic ipv4 + add_test_case cbq multi_ifaces ipv4 + add_test_case cbq options ipv4 +} diff --git a/net/arp/t_arp.sh b/net/arp/t_arp.sh old mode 100755 new mode 100644 --- a/net/arp/t_arp.sh +++ b/net/arp/t_arp.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_arp.sh,v 1.22 2016/11/25 08:51:16 ozaki-r Exp $ +# $NetBSD: t_arp.sh,v 1.45 2020/09/18 16:33:49 roy Exp $ # # Copyright (c) 2015 The NetBSD Foundation, Inc. # All rights reserved. @@ -28,32 +28,30 @@ SOCKSRC=unix://commsock1 SOCKDST=unix://commsock2 IP4SRC=10.0.1.1 +IP4SRC2=10.0.1.5 +IP4NET=10.0.1.0 IP4DST=10.0.1.2 IP4DST_PROXYARP1=10.0.1.3 IP4DST_PROXYARP2=10.0.1.4 +IP4DST_FAIL1=10.0.1.99 +IP4DST_FAIL2=10.0.99.99 DEBUG=${DEBUG:-false} TIMEOUT=1 -atf_test_case arp_cache_expiration_5s cleanup -atf_test_case arp_cache_expiration_10s cleanup +atf_test_case arp_cache_expiration cleanup atf_test_case arp_command cleanup atf_test_case arp_garp cleanup +atf_test_case arp_garp_without_dad cleanup atf_test_case arp_cache_overwriting cleanup atf_test_case arp_proxy_arp_pub cleanup atf_test_case arp_proxy_arp_pubproxy cleanup atf_test_case arp_link_activation cleanup atf_test_case arp_static cleanup -arp_cache_expiration_5s_head() +arp_cache_expiration_head() { - atf_set "descr" "Tests for ARP cache expiration (5s)" - atf_set "require.progs" "rump_server" -} - -arp_cache_expiration_10s_head() -{ - atf_set "descr" "Tests for ARP cache expiration (10s)" + atf_set "descr" "Tests for ARP cache expiration" atf_set "require.progs" "rump_server" } @@ -69,6 +67,13 @@ atf_set "require.progs" "rump_server" } +arp_garp_without_dad_head() +{ + + atf_set "descr" "Tests for GARP with DAD disabled" + atf_set "require.progs" "rump_server" +} + arp_cache_overwriting_head() { atf_set "descr" "Tests for behavior of overwriting ARP caches" @@ -111,16 +116,22 @@ $DEBUG && rump.ifconfig shmif0 $DEBUG && rump.arp -n -a + $DEBUG && rump.netstat -nr -f inet } setup_src_server() { - local keep=$1 + local keep=${1:-0} export RUMP_SERVER=$SOCKSRC - # Adjust ARP parameters - atf_check -s exit:0 -o ignore rump.sysctl -w net.inet.arp.keep=$keep + # Shorten the expire time of cache entries + if [ $keep != 0 ]; then + # Convert to ms + keep=$(($keep * 1000)) + atf_check -s exit:0 -o ignore \ + rump.sysctl -w net.inet.arp.nd_reachable=$keep + fi # Setup an interface rump_server_add_iface $SOCKSRC shmif0 bus1 @@ -131,14 +142,22 @@ # Sanity check $DEBUG && rump.ifconfig shmif0 $DEBUG && rump.arp -n -a - atf_check -s exit:0 -o ignore rump.arp -n $IP4SRC - atf_check -s not-exit:0 -e ignore rump.arp -n $IP4DST + $DEBUG && rump.netstat -nr -f inet + atf_check -s not-exit:0 -e match:'no entry' rump.arp -n $IP4SRC + atf_check -s not-exit:0 -e match:'no entry' rump.arp -n $IP4DST } -test_cache_expiration() +get_timeout() { - local arp_keep=$1 - local bonus=2 + local addr="$1" + local timeout=$(env RUMP_SERVER=$SOCKSRC rump.arp -n $addr |grep $addr|awk '{print $7;}') + timeout=${timeout%s} + echo $timeout +} + +arp_cache_expiration_body() +{ + local arp_keep=7 rump_server_start $SOCKSRC rump_server_start $SOCKDST @@ -146,37 +165,51 @@ setup_dst_server setup_src_server $arp_keep + # Make a permanent cache entry to avoid sending an NS packet disturbing + # the test + macaddr=$(get_macaddr $SOCKSRC shmif0) + export RUMP_SERVER=$SOCKDST + atf_check -s exit:0 -o ignore rump.arp -s $IP4SRC $macaddr + + export RUMP_SERVER=$SOCKSRC + # # Check if a cache is expired expectedly # - export RUMP_SERVER=$SOCKSRC atf_check -s exit:0 -o ignore rump.ping -n -w $TIMEOUT -c 1 $IP4DST $DEBUG && rump.arp -n -a - atf_check -s exit:0 -o ignore rump.arp -n $IP4SRC + atf_check -s not-exit:0 -o ignore -e match:'no entry' rump.arp -n $IP4SRC # Should be cached - atf_check -s exit:0 -o ignore rump.arp -n $IP4DST + atf_check -s exit:0 -o not-match:'permanent' rump.arp -n $IP4DST - atf_check -s exit:0 sleep $(($arp_keep + $bonus)) + timeout=$(get_timeout $IP4DST) - $DEBUG && rump.arp -n -a - atf_check -s exit:0 -o ignore rump.arp -n $IP4SRC - # Should be expired - atf_check -s not-exit:0 -e ignore rump.arp -n $IP4DST -} + atf_check -s exit:0 sleep $(($timeout + 1)) -arp_cache_expiration_5s_body() -{ + $DEBUG && rump.arp -n -a + atf_check -s not-exit:0 -o ignore -e match:'no entry' rump.arp -n $IP4SRC + # Expired but remains until GC sweaps it (1 day) + atf_check -s exit:0 -o match:"$ONEDAYISH" rump.arp -n $IP4DST - test_cache_expiration 5 rump_server_destroy_ifaces } -arp_cache_expiration_10s_body() +check_arp_static_entry() { + local ip=$1 + local mac=$2 + local type=$3 + local flags= - test_cache_expiration 10 - rump_server_destroy_ifaces + atf_check -s exit:0 -o match:"$mac" rump.arp -n $ip + if [ $type = 'permanent' ]; then + atf_check -s exit:0 -o match:'permanent' rump.arp -n $ip + check_route $ip "$mac" UHLS shmif0 + else + atf_check -s exit:0 -o not-match:'permanent' rump.arp -n $ip + check_route $ip "$mac" UHL shmif0 + fi } arp_command_body() @@ -192,18 +225,17 @@ export RUMP_SERVER=$SOCKSRC - # We can delete the entry for the interface's IP address - atf_check -s exit:0 -o ignore rump.arp -d $IP4SRC - # Add and delete a static entry $DEBUG && rump.arp -n -a atf_check -s exit:0 -o ignore rump.arp -s 10.0.1.10 b2:a0:20:00:00:10 $DEBUG && rump.arp -n -a - atf_check -s exit:0 -o match:'b2:a0:20:00:00:10' rump.arp -n 10.0.1.10 - atf_check -s exit:0 -o match:'permanent' rump.arp -n 10.0.1.10 + $DEBUG && rump.netstat -nr -f inet + check_arp_static_entry 10.0.1.10 'b2:a0:20:00:00:10' permanent atf_check -s exit:0 -o ignore rump.arp -d 10.0.1.10 $DEBUG && rump.arp -n -a + $DEBUG && rump.netstat -nr -f inet atf_check -s not-exit:0 -e ignore rump.arp -n 10.0.1.10 + check_route_no_entry 10.0.1.10 # Add multiple entries via a file cat - > ./list <<-EOF @@ -214,18 +246,15 @@ 10.0.1.15 b2:a0:20:00:00:15 EOF $DEBUG && rump.arp -n -a + $DEBUG && rump.netstat -nr -f inet atf_check -s exit:0 -o ignore rump.arp -f ./list $DEBUG && rump.arp -n -a - atf_check -s exit:0 -o match:'b2:a0:20:00:00:11' rump.arp -n 10.0.1.11 - atf_check -s exit:0 -o match:'permanent' rump.arp -n 10.0.1.11 - atf_check -s exit:0 -o match:'b2:a0:20:00:00:12' rump.arp -n 10.0.1.12 - atf_check -s exit:0 -o match:'permanent' rump.arp -n 10.0.1.12 - atf_check -s exit:0 -o match:'b2:a0:20:00:00:13' rump.arp -n 10.0.1.13 - atf_check -s exit:0 -o match:'permanent' rump.arp -n 10.0.1.13 - atf_check -s exit:0 -o match:'b2:a0:20:00:00:14' rump.arp -n 10.0.1.14 - atf_check -s exit:0 -o match:'permanent' rump.arp -n 10.0.1.14 - atf_check -s exit:0 -o match:'b2:a0:20:00:00:15' rump.arp -n 10.0.1.15 - atf_check -s exit:0 -o match:'permanent' rump.arp -n 10.0.1.15 + $DEBUG && rump.netstat -nr -f inet + check_arp_static_entry 10.0.1.11 'b2:a0:20:00:00:11' permanent + check_arp_static_entry 10.0.1.12 'b2:a0:20:00:00:12' permanent + check_arp_static_entry 10.0.1.13 'b2:a0:20:00:00:13' permanent + check_arp_static_entry 10.0.1.14 'b2:a0:20:00:00:14' permanent + check_arp_static_entry 10.0.1.15 'b2:a0:20:00:00:15' permanent # Test arp -a atf_check -s exit:0 -o match:'10.0.1.11' rump.arp -n -a @@ -236,6 +265,7 @@ # Flush all entries $DEBUG && rump.arp -n -a + $DEBUG && rump.netstat -nr -f inet atf_check -s exit:0 -o ignore rump.arp -d -a atf_check -s not-exit:0 -e ignore rump.arp -n 10.0.1.11 atf_check -s not-exit:0 -e ignore rump.arp -n 10.0.1.12 @@ -243,17 +273,23 @@ atf_check -s not-exit:0 -e ignore rump.arp -n 10.0.1.14 atf_check -s not-exit:0 -e ignore rump.arp -n 10.0.1.15 atf_check -s not-exit:0 -e ignore rump.arp -n 10.0.1.1 + check_route_no_entry 10.0.1.11 + check_route_no_entry 10.0.1.12 + check_route_no_entry 10.0.1.13 + check_route_no_entry 10.0.1.14 + check_route_no_entry 10.0.1.15 # Test temp option $DEBUG && rump.arp -n -a atf_check -s exit:0 -o ignore rump.arp -s 10.0.1.10 b2:a0:20:00:00:10 temp $DEBUG && rump.arp -n -a - atf_check -s exit:0 -o match:'b2:a0:20:00:00:10' rump.arp -n 10.0.1.10 - atf_check -s exit:0 -o not-match:'permanent' rump.arp -n 10.0.1.10 + $DEBUG && rump.netstat -nr -f inet + check_arp_static_entry 10.0.1.10 'b2:a0:20:00:00:10' temp # Hm? the cache doesn't expire... - atf_check -s exit:0 sleep $(($arp_keep + $bonus)) - $DEBUG && rump.arp -n -a + #atf_check -s exit:0 sleep $(($arp_keep + $bonus)) + #$DEBUG && rump.arp -n -a + #$DEBUG && rump.netstat -nr -f inet #atf_check -s not-exit:0 -e ignore rump.arp -n 10.0.1.10 rump_server_destroy_ifaces @@ -263,67 +299,123 @@ { local target=$1 local sender=$2 - pkt="> ff:ff:ff:ff:ff:ff, ethertype ARP (0x0806), length 42:" + pkt="> ff:ff:ff:ff:ff:ff, ethertype ARP \(0x0806\), length 42:" pkt="$pkt Request who-has $target tell $sender, length 28" echo $pkt } -arp_garp_body() +test_garp_common() { + local no_dad=$1 local pkt= rump_server_start $SOCKSRC export RUMP_SERVER=$SOCKSRC + if $no_dad; then + atf_check -s exit:0 -o match:'3 -> 0' \ + rump.sysctl -w net.inet.ip.dad_count=0 + fi + # Setup an interface rump_server_add_iface $SOCKSRC shmif0 bus1 atf_check -s exit:0 rump.ifconfig shmif0 inet 10.0.0.1/24 - atf_check -s exit:0 rump.ifconfig shmif0 inet 10.0.0.2/24 alias atf_check -s exit:0 rump.ifconfig shmif0 up $DEBUG && rump.ifconfig shmif0 atf_check -s exit:0 sleep 1 - shmif_dumpbus -p - bus1 2>/dev/null| tcpdump -n -e -r - > ./out + extract_new_packets bus1 > ./out + # + # Assign an address to an interface without IFF_UP + # # A GARP packet is sent for the primary address pkt=$(make_pkt_str_arpreq 10.0.0.1 10.0.0.1) - atf_check -s exit:0 -x "cat ./out |grep -q '$pkt'" - # No GARP packet is sent for the alias address + atf_check -s exit:0 -o match:"$pkt" cat ./out + + atf_check -s exit:0 rump.ifconfig shmif0 down + atf_check -s exit:0 rump.ifconfig shmif0 inet 10.0.0.2/24 alias + + atf_check -s exit:0 sleep 1 + extract_new_packets bus1 > ./out + + # A GARP packet is sent for the alias address pkt=$(make_pkt_str_arpreq 10.0.0.2 10.0.0.2) - atf_check -s not-exit:0 -x "cat ./out |grep -q '$pkt'" + atf_check -s exit:0 -o match:"$pkt" cat ./out - atf_check -s exit:0 rump.ifconfig -w 10 + # Clean up + atf_check -s exit:0 rump.ifconfig shmif0 inet 10.0.0.1/24 delete + atf_check -s exit:0 rump.ifconfig shmif0 inet 10.0.0.2/24 delete + + # + # Assign an address to an interface with IFF_UP + # + atf_check -s exit:0 rump.ifconfig shmif0 up + + # Primary address atf_check -s exit:0 rump.ifconfig shmif0 inet 10.0.0.3/24 - atf_check -s exit:0 rump.ifconfig shmif0 inet 10.0.0.4/24 alias - # No GARP packets are sent during IFF_UP - shmif_dumpbus -p - bus1 2>/dev/null| tcpdump -n -e -r - > ./out + atf_check -s exit:0 sleep 1 + extract_new_packets bus1 > ./out + pkt=$(make_pkt_str_arpreq 10.0.0.3 10.0.0.3) - atf_check -s not-exit:0 -x "cat ./out |grep -q '$pkt'" + if $no_dad; then + # A GARP packet is sent + atf_check -s exit:0 -o match:"$pkt" cat ./out + else + # No GARP packet is sent + atf_check -s exit:0 -o not-match:"$pkt" cat ./out + fi + + # Alias address + atf_check -s exit:0 rump.ifconfig shmif0 inet 10.0.0.4/24 alias + + atf_check -s exit:0 sleep 1 + extract_new_packets bus1 > ./out + pkt=$(make_pkt_str_arpreq 10.0.0.4 10.0.0.4) - atf_check -s not-exit:0 -x "cat ./out |grep -q '$pkt'" + if $no_dad; then + # A GARP packet is sent + atf_check -s exit:0 -o match:"$pkt" cat ./out + else + # No GARP packet is sent + atf_check -s exit:0 -o not-match:"$pkt" cat ./out + fi rump_server_destroy_ifaces } +arp_garp_body() +{ + + test_garp_common false +} + +arp_garp_without_dad_body() +{ + + test_garp_common true +} + arp_cache_overwriting_body() { - local arp_keep=5 - local bonus=2 rump_server_start $SOCKSRC rump_server_start $SOCKDST setup_dst_server - setup_src_server $arp_keep + setup_src_server export RUMP_SERVER=$SOCKSRC # Cannot overwrite a permanent cache - atf_check -s not-exit:0 -e match:'File exists' \ - rump.arp -s $IP4SRC b2:a0:20:00:00:ff + atf_check -s exit:0 rump.arp -s $IP4DST b2:a0:20:00:00:ff $DEBUG && rump.arp -n -a + atf_check -s not-exit:0 -e match:'File exists' \ + rump.arp -s $IP4DST b2:a0:20:00:00:fe + # cleanup + atf_check -s exit:0 rump.arp -d $IP4DST atf_check -s exit:0 -o ignore rump.ping -n -w $TIMEOUT -c 1 $IP4DST $DEBUG && rump.arp -n -a @@ -366,15 +458,14 @@ test_proxy_arp() { - local arp_keep=5 local opts= title= flags= local type=$1 rump_server_start $SOCKSRC - rump_server_start $SOCKDST tap + rump_server_fs_start $SOCKDST tap setup_dst_server - setup_src_server $arp_keep + setup_src_server export RUMP_SERVER=$SOCKDST atf_check -s exit:0 -o ignore rump.sysctl -w net.inet.ip.forwarding=1 @@ -382,17 +473,17 @@ if [ "$type" = "pub" ]; then opts="pub" - title="permanent published" else opts="pub proxy" - title='permanent published \(proxy only\)' fi + # Always proxy only since migrating to lltable/llentry + title='published \(proxy only\)' # # Test#1: First setup an endpoint then create proxy arp entry # export RUMP_SERVER=$SOCKDST - atf_check -s exit:0 rump.ifconfig tap1 create + rump_server_add_iface $SOCKDST tap1 atf_check -s exit:0 rump.ifconfig tap1 $IP4DST_PROXYARP1/24 up atf_check -s exit:0 rump.ifconfig -w 10 @@ -400,6 +491,8 @@ export RUMP_SERVER=$SOCKSRC atf_check -s not-exit:0 -o ignore -e ignore \ rump.ping -n -w 1 -c 1 $IP4DST_PROXYARP1 + # Remove ARP entry as it may hang around in WAITDELETE a few seconds + atf_check -s ignore rump.arp -d $IP4DST_PROXYARP1 # Flushing extract_new_packets bus1 > ./out @@ -412,26 +505,14 @@ # Try to ping export RUMP_SERVER=$SOCKSRC - if [ "$type" = "pub" ]; then - # XXX fails - atf_check -s not-exit:0 -o ignore -e ignore \ - rump.ping -n -w 1 -c 1 $IP4DST_PROXYARP1 - else - atf_check -s exit:0 -o ignore \ - rump.ping -n -w 1 -c 1 $IP4DST_PROXYARP1 - fi + atf_check -s exit:0 -o ignore rump.ping -n -w 1 -c 1 $IP4DST_PROXYARP1 extract_new_packets bus1 > ./out $DEBUG && cat ./out pkt1=$(make_pkt_str_arprep $IP4DST_PROXYARP1 $macaddr_dst) pkt2=$(make_pkt_str_garp $IP4DST_PROXYARP1 $macaddr_dst) - if [ "$type" = "pub" ]; then - atf_check -s not-exit:0 -x \ - "cat ./out |grep -q -e '$pkt1' -e '$pkt2'" - else - atf_check -s exit:0 -x "cat ./out |grep -q -e '$pkt1' -e '$pkt2'" - fi + atf_check -s exit:0 -x "cat ./out |grep -q -e '$pkt1' -e '$pkt2'" # # Test#2: Create proxy arp entry then set up an endpoint @@ -446,6 +527,8 @@ export RUMP_SERVER=$SOCKSRC atf_check -s not-exit:0 -o ignore -e ignore \ rump.ping -n -w 1 -c 1 $IP4DST_PROXYARP2 + # Remove ARP entry as it may hang around in WAITDELETE a few seconds + atf_check -s ignore rump.arp -d $IP4DST_PROXYARP2 extract_new_packets bus1 > ./out $DEBUG && cat ./out @@ -455,7 +538,7 @@ atf_check -s exit:0 -x "cat ./out |grep -q '$pkt'" export RUMP_SERVER=$SOCKDST - atf_check -s exit:0 rump.ifconfig tap2 create + rump_server_add_iface $SOCKDST tap2 atf_check -s exit:0 rump.ifconfig tap2 $IP4DST_PROXYARP2/24 up atf_check -s exit:0 rump.ifconfig -w 10 @@ -480,14 +563,12 @@ arp_link_activation_body() { - local arp_keep=5 - local bonus=2 rump_server_start $SOCKSRC rump_server_start $SOCKDST setup_dst_server - setup_src_server $arp_keep + setup_src_server # flush old packets extract_new_packets bus1 > ./out @@ -502,7 +583,7 @@ $DEBUG && cat ./out pkt=$(make_pkt_str_arpreq $IP4SRC $IP4SRC) - atf_check -s not-exit:0 -x "cat ./out |grep -q '$pkt'" + atf_check -s exit:0 -o not-match:"$pkt" cat ./out atf_check -s exit:0 -o ignore rump.ifconfig shmif0 link \ b2:a1:00:00:00:02 active @@ -512,22 +593,20 @@ $DEBUG && cat ./out pkt=$(make_pkt_str_arpreq $IP4SRC $IP4SRC) - atf_check -s exit:0 -x \ - "cat ./out |grep '$pkt' |grep -q 'b2:a1:00:00:00:02'" + atf_check -s exit:0 -o match:"b2:a1:00:00:00:02 $pkt" cat ./out rump_server_destroy_ifaces } arp_static_body() { - local arp_keep=5 local macaddr_src= rump_server_start $SOCKSRC rump_server_start $SOCKDST setup_dst_server - setup_src_server $arp_keep + setup_src_server macaddr_src=$(get_macaddr $SOCKSRC shmif0) @@ -544,26 +623,27 @@ rump_server_destroy_ifaces } -arp_cache_expiration_5s_cleanup() +arp_cache_expiration_cleanup() { $DEBUG && dump cleanup } -arp_cache_expiration_10s_cleanup() +arp_command_cleanup() { $DEBUG && dump cleanup } -arp_command_cleanup() +arp_garp_cleanup() { $DEBUG && dump cleanup } -arp_garp_cleanup() +arp_garp_without_dad_cleanup() { + $DEBUG && dump cleanup } @@ -598,15 +678,310 @@ cleanup } +atf_test_case arp_rtm cleanup +arp_rtm_head() +{ + + atf_set "descr" "Tests for routing messages on operations of ARP entries" + atf_set "require.progs" "rump_server" +} + +arp_rtm_body() +{ + local macaddr_src= macaddr_dst= + local file=./tmp + local pid= hdr= what= addr= + + rump_server_start $SOCKSRC + rump_server_start $SOCKDST + + setup_dst_server + setup_src_server + + macaddr_src=$(get_macaddr $SOCKSRC shmif0) + macaddr_dst=$(get_macaddr $SOCKDST shmif0) + + export RUMP_SERVER=$SOCKSRC + + # Test ping and a resulting routing message (RTM_ADD) + rump.route -n monitor -c 1 > $file & + pid=$! + sleep 1 + atf_check -s exit:0 -o ignore rump.ping -n -w 1 -c 1 $IP4DST + wait $pid + $DEBUG && cat $file + + hdr="RTM_ADD.+" + what="" + addr="$IP4DST $macaddr_dst" + atf_check -s exit:0 -o match:"$hdr" -o match:"$what" -o match:"$addr" \ + cat $file + + # Test ping and a resulting routing message (RTM_MISS) on subnet + rump.route -n monitor -c 1 > $file & + pid=$! + sleep 1 + atf_check -s exit:2 -o ignore -e ignore \ + rump.ping -n -w 1 -c 1 $IP4DST_FAIL1 + wait $pid + $DEBUG && cat $file + + hdr="RTM_MISS.+" + what="" + addr="$IP4DST_FAIL1 link#2 $IP4SRC" + atf_check -s exit:0 -o match:"$hdr" -o match:"$what" -o match:"$addr" \ + cat $file + + # Test ping and a resulting routing message (RTM_MISS) off subnet + rump.route -n monitor -c 1 > $file & + pid=$! + sleep 1 + atf_check -s exit:2 -o ignore -e ignore \ + rump.ping -n -w 1 -c 1 $IP4DST_FAIL2 + wait $pid + $DEBUG && cat $file + + hdr="RTM_MISS.+" + what="" + addr="$IP4DST_FAIL2" + atf_check -s exit:0 -o match:"$hdr" -o match:"$what" -o match:"$addr" \ + cat $file + + # Test arp -d and resulting routing messages (RTM_DELETE) + rump.route -n monitor -c 1 > $file & + pid=$! + sleep 1 + atf_check -s exit:0 -o ignore rump.arp -d $IP4DST + wait $pid + $DEBUG && cat $file + + hdr="RTM_DELETE.+" + what="" + addr="$IP4DST $macaddr_dst" + atf_check -s exit:0 -o match:"$hdr" -o match:"$what" -o match:"$addr" \ + grep -A 3 RTM_DELETE $file + + rump_server_destroy_ifaces +} + +arp_rtm_cleanup() +{ + + $DEBUG && dump + cleanup +} + +atf_test_case arp_purge_on_route_change cleanup +arp_purge_on_route_change_head() +{ + + atf_set "descr" "Tests if ARP entries are removed on route change" + atf_set "require.progs" "rump_server" +} + +arp_purge_on_route_change_body() +{ + + rump_server_start $SOCKSRC + rump_server_start $SOCKDST + + setup_dst_server + setup_src_server + + rump_server_add_iface $SOCKSRC shmif1 bus1 + export RUMP_SERVER=$SOCKSRC + atf_check -s exit:0 rump.ifconfig shmif1 inet $IP4SRC2/24 + atf_check -s exit:0 rump.ifconfig -w 10 + + $DEBUG && rump.netstat -nr -f inet + atf_check -s exit:0 -o ignore rump.ping -n -w 1 -c 1 $IP4DST + $DEBUG && rump.arp -na + atf_check -s exit:0 -o ignore \ + rump.route change -net $IP4NET -ifp shmif1 + $DEBUG && rump.netstat -nr -f inet + $DEBUG && rump.arp -na + # The entry was already removed on route change + atf_check -s not-exit:0 -e match:'no entry' rump.arp -n $IP4DST + + rump_server_destroy_ifaces +} + +arp_purge_on_route_change_cleanup() +{ + + $DEBUG && dump + cleanup +} + +atf_test_case arp_purge_on_route_delete cleanup +arp_purge_on_route_delete_head() +{ + + atf_set "descr" "Tests if ARP entries are removed on route delete" + atf_set "require.progs" "rump_server" +} + +arp_purge_on_route_delete_body() +{ + + rump_server_start $SOCKSRC + rump_server_start $SOCKDST + + setup_dst_server + setup_src_server + + $DEBUG && rump.netstat -nr -f inet + atf_check -s exit:0 -o ignore rump.ping -n -w 1 -c 1 $IP4DST + $DEBUG && rump.arp -na + + atf_check -s exit:0 -o ignore rump.route delete -net $IP4NET + $DEBUG && rump.netstat -nr -f inet + $DEBUG && rump.arp -na + + # The entry was already removed on route delete + atf_check -s not-exit:0 -e match:'no entry' rump.arp -n $IP4DST + + rump_server_destroy_ifaces +} + +arp_purge_on_route_delete_cleanup() +{ + + $DEBUG && dump + cleanup +} + +atf_test_case arp_purge_on_ifdown cleanup +arp_purge_on_ifdown_head() +{ + + atf_set "descr" "Tests if ARP entries are removed on interface down" + atf_set "require.progs" "rump_server" +} + +arp_purge_on_ifdown_body() +{ + + rump_server_start $SOCKSRC + rump_server_start $SOCKDST + + setup_dst_server + setup_src_server + + $DEBUG && rump.netstat -nr -f inet + atf_check -s exit:0 -o ignore rump.ping -n -w 1 -c 1 $IP4DST + atf_check -s exit:0 -o match:'shmif0' rump.arp -n $IP4DST + + # Shutdown the interface + atf_check -s exit:0 rump.ifconfig shmif0 down + $DEBUG && rump.netstat -nr -f inet + $DEBUG && rump.arp -na + + atf_check -s not-exit:0 -e match:'no entry' rump.arp -n $IP4DST + + rump_server_destroy_ifaces +} + +arp_purge_on_ifdown_cleanup() +{ + + $DEBUG && dump + cleanup +} + +atf_test_case arp_stray_entries cleanup +arp_stray_entries_head() +{ + + atf_set "descr" "Tests if ARP entries are removed on route change" + atf_set "require.progs" "rump_server" +} + +arp_stray_entries_body() +{ + + rump_server_start $SOCKSRC + rump_server_start $SOCKDST + + setup_dst_server + setup_src_server + + rump_server_add_iface $SOCKSRC shmif1 bus1 + + export RUMP_SERVER=$SOCKSRC + atf_check -s exit:0 rump.ifconfig shmif1 inet $IP4SRC2/24 + atf_check -s exit:0 rump.ifconfig -w 10 + + $DEBUG && rump.netstat -nr -f inet + atf_check -s exit:0 -o ignore rump.ping -n -w 1 -c 1 $IP4DST + $DEBUG && rump.arp -na + atf_check -s exit:0 -o match:'shmif0' rump.arp -n $IP4DST + atf_check -s exit:0 -o not-match:'shmif1' rump.arp -n $IP4DST + + # Clean up + atf_check -s exit:0 -o ignore rump.arp -da + atf_check -s not-exit:0 -e match:'no entry' rump.arp -n $IP4DST + + # ping from a different source address + atf_check -s exit:0 -o ignore \ + rump.ping -n -w 1 -c 1 -I $IP4SRC2 $IP4DST + $DEBUG && rump.arp -na + atf_check -s exit:0 -o match:'shmif0' rump.arp -n $IP4DST + # ARP reply goes back via shmif1, so a cache is created on shmif1 + atf_check -s exit:0 -o match:'shmif1' rump.arp -n $IP4DST + + # Clean up by arp -da + atf_check -s exit:0 -o ignore rump.arp -da + atf_check -s not-exit:0 -e match:'no entry' rump.arp -n $IP4DST + + # ping from a different source address again + atf_check -s exit:0 -o ignore \ + rump.ping -n -w 1 -c 1 -I $IP4SRC2 $IP4DST + atf_check -s exit:0 -o match:'shmif0' rump.arp -n $IP4DST + # ARP reply doen't come + atf_check -s exit:0 -o not-match:'shmif1' rump.arp -n $IP4DST + + # Cleanup caches on the destination + export RUMP_SERVER=$SOCKDST + atf_check -s exit:0 -o ignore rump.arp -da + export RUMP_SERVER=$SOCKSRC + + # ping from a different source address again + atf_check -s exit:0 -o ignore \ + rump.ping -n -w 1 -c 1 -I $IP4SRC2 $IP4DST + atf_check -s exit:0 -o match:'shmif0' rump.arp -n $IP4DST + # ARP reply goes back via shmif1 + atf_check -s exit:0 -o match:'shmif1' rump.arp -n $IP4DST + + # Clean up by arp -d + atf_check -s exit:0 -o ignore rump.arp -d $IP4DST + # Both entries should be deleted + atf_check -s not-exit:0 -e match:'no entry' rump.arp -n $IP4DST + + rump_server_destroy_ifaces +} + +arp_stray_entries_cleanup() +{ + + $DEBUG && dump + cleanup +} + atf_init_test_cases() { - atf_add_test_case arp_cache_expiration_5s - atf_add_test_case arp_cache_expiration_10s + atf_add_test_case arp_cache_expiration atf_add_test_case arp_command atf_add_test_case arp_garp + atf_add_test_case arp_garp_without_dad atf_add_test_case arp_cache_overwriting atf_add_test_case arp_proxy_arp_pub atf_add_test_case arp_proxy_arp_pubproxy atf_add_test_case arp_link_activation atf_add_test_case arp_static + atf_add_test_case arp_rtm + atf_add_test_case arp_purge_on_route_change + atf_add_test_case arp_purge_on_route_delete + atf_add_test_case arp_purge_on_ifdown + atf_add_test_case arp_stray_entries } diff --git a/net/arp/t_dad.sh b/net/arp/t_dad.sh old mode 100755 new mode 100644 --- a/net/arp/t_dad.sh +++ b/net/arp/t_dad.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_dad.sh,v 1.13 2016/11/25 08:51:16 ozaki-r Exp $ +# $NetBSD: t_dad.sh,v 1.15 2017/03/11 02:01:10 ozaki-r Exp $ # # Copyright (c) 2015 The NetBSD Foundation, Inc. # All rights reserved. @@ -64,7 +64,7 @@ { local target=$1 local sender=$2 - pkt="> ff:ff:ff:ff:ff:ff, ethertype ARP (0x0806), length 42:" + pkt="> ff:ff:ff:ff:ff:ff, ethertype ARP \(0x0806\), length 42:" pkt="$pkt Request who-has $target tell $sender, length 28" echo $pkt } @@ -78,6 +78,10 @@ export RUMP_SERVER=$SOCKLOCAL + # Increase the number of trials, which makes the tests stable + atf_check -s exit:0 -o match:'3 -> 5' \ + rump.sysctl -w net.inet.ip.dad_count=5 + atf_check -s exit:0 rump.ifconfig shmif0 inet 10.0.0.1/24 atf_check -s exit:0 rump.ifconfig shmif0 inet 10.0.0.2/24 alias $DEBUG && rump.ifconfig shmif0 @@ -87,10 +91,9 @@ $DEBUG && cat ./out # The primary address doesn't start with tentative state - atf_check -s not-exit:0 -x "cat ./out |grep 10.0.0.1 |grep -iq tentative" + atf_check -s exit:0 -o not-match:'10\.0\.0\.1.+TENTATIVE' cat ./out # The alias address starts with tentative state - # XXX we have no stable way to check this, so skip for now - #atf_check -s exit:0 -x "cat ./out |grep 10.0.0.2 |grep -iq tentative" + atf_check -s exit:0 -o match:'10\.0\.0\.2.+TENTATIVE' cat ./out atf_check -s exit:0 sleep 2 extract_new_packets bus1 > ./out @@ -98,23 +101,24 @@ # Check DAD probe packets pkt=$(make_pkt_str 10.0.0.2 0.0.0.0) - atf_check -s exit:0 -x "cat ./out |grep -q '$pkt'" + atf_check -s exit:0 -o match:"$pkt" cat ./out # No DAD for the primary address pkt=$(make_pkt_str 10.0.0.1 0.0.0.0) - atf_check -s not-exit:0 -x "cat ./out |grep -q '$pkt'" + atf_check -s exit:0 -o not-match:"$pkt" cat ./out # Waiting for DAD complete atf_check -s exit:0 rump.ifconfig -w 10 # Give a chance to send a DAD announce packet - atf_check -s exit:0 sleep 1 + atf_check -s exit:0 sleep 2 extract_new_packets bus1 > ./out $DEBUG && cat ./out # Check the DAD announce packet pkt=$(make_pkt_str 10.0.0.2 10.0.0.2) - atf_check -s exit:0 -x "cat ./out |grep -q '$pkt'" + atf_check -s exit:0 -o match:"$pkt" cat ./out # The alias address left tentative - atf_check -s not-exit:0 -x "rump.ifconfig shmif0 |grep 10.0.0.2 |grep -iq tentative" + atf_check -s exit:0 -o not-match:'10\.0\.0\.2.+TENTATIVE' \ + rump.ifconfig shmif0 # # Add a new address on the fly @@ -122,28 +126,29 @@ atf_check -s exit:0 rump.ifconfig shmif0 inet 10.0.0.3/24 alias # The new address starts with tentative state - # XXX we have no stable way to check this, so skip for now - #atf_check -s exit:0 -x "rump.ifconfig shmif0 |grep 10.0.0.3 |grep -iq tentative" + atf_check -s exit:0 -o match:'10\.0\.0\.3.+TENTATIVE' \ + rump.ifconfig shmif0 # Check DAD probe packets atf_check -s exit:0 sleep 2 extract_new_packets bus1 > ./out $DEBUG && cat ./out pkt=$(make_pkt_str 10.0.0.3 0.0.0.0) - atf_check -s exit:0 -x "cat ./out |grep -q '$pkt'" + atf_check -s exit:0 -o match:"$pkt" cat ./out # Waiting for DAD complete atf_check -s exit:0 rump.ifconfig -w 10 # Give a chance to send a DAD announce packet - atf_check -s exit:0 sleep 1 + atf_check -s exit:0 sleep 2 extract_new_packets bus1 > ./out $DEBUG && cat ./out # Check the DAD announce packet pkt=$(make_pkt_str 10.0.0.3 10.0.0.3) - atf_check -s exit:0 -x "cat ./out |grep -q '$pkt'" + atf_check -s exit:0 -o match:"$pkt" cat ./out # The new address left tentative - atf_check -s not-exit:0 -x "rump.ifconfig shmif0 |grep 10.0.0.3 |grep -iq tentative" + atf_check -s exit:0 -o not-match:'10\.0\.0\.3.+TENTATIVE' \ + rump.ifconfig shmif0 rump_server_destroy_ifaces } @@ -163,21 +168,24 @@ export RUMP_SERVER=$SOCKLOCAL # The primary address isn't marked as duplicated - atf_check -s not-exit:0 -x "rump.ifconfig shmif0 |grep $localip1 |grep -iq duplicated" + atf_check -s exit:0 -o not-match:"${localip1}.+DUPLICATED" \ + rump.ifconfig shmif0 # # Add a new address duplicated with the peer server # atf_check -s exit:0 rump.ifconfig shmif0 inet $peerip alias - atf_check -s exit:0 sleep 1 + atf_check -s exit:0 sleep 2 # The new address is marked as duplicated - atf_check -s exit:0 -x "rump.ifconfig shmif0 |grep $peerip |grep -iq duplicated" + atf_check -s exit:0 -o match:"${peerip}.+DUPLICATED" \ + rump.ifconfig shmif0 # A unique address isn't marked as duplicated atf_check -s exit:0 rump.ifconfig shmif0 inet $localip2 alias - atf_check -s exit:0 sleep 1 - atf_check -s not-exit:0 -x "rump.ifconfig shmif0 |grep $localip2 |grep -iq duplicated" + atf_check -s exit:0 sleep 2 + atf_check -s exit:0 -o not-match:"${localip2}.+DUPLICATED" \ + rump.ifconfig shmif0 rump_server_destroy_ifaces } diff --git a/net/bpf/Makefile b/net/bpf/Makefile --- a/net/bpf/Makefile +++ b/net/bpf/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.5 2014/07/07 19:41:22 alnsn Exp $ +# $NetBSD: Makefile,v 1.6 2020/03/01 18:08:15 christos Exp $ # .include @@ -11,6 +11,6 @@ LDADD+= -lrumpnet_shmif LDADD+= -lrumpdev_bpf -lrumpdev -lrumpnet_netinet -lrumpnet_net -LDADD+= -lrumpnet -lrumpvfs -lrump -lrumpuser -lrump -lpthread +LDADD+= -lrumpnet ${LIBRUMPBASE} .include diff --git a/net/bpf/t_bpf.c b/net/bpf/t_bpf.c --- a/net/bpf/t_bpf.c +++ b/net/bpf/t_bpf.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_bpf.c,v 1.7 2017/02/01 08:04:49 ozaki-r Exp $ */ +/* $NetBSD: t_bpf.c,v 1.8 2017/02/09 02:18:13 ozaki-r Exp $ */ /*- * Copyright (c) 2010 Antti Kantee. All Rights Reserved. @@ -25,7 +25,7 @@ * SUCH DAMAGE. */ #include -__RCSID("$NetBSD: t_bpf.c,v 1.7 2017/02/01 08:04:49 ozaki-r Exp $"); +__RCSID("$NetBSD: t_bpf.c,v 1.8 2017/02/09 02:18:13 ozaki-r Exp $"); #include #include @@ -37,6 +37,7 @@ #include #include +#include #include #include @@ -199,6 +200,138 @@ "Don't allow to change buflen after binding bpf to an interface"); } +ATF_TC(bpf_ioctl_PROMISC); +ATF_TC_HEAD(bpf_ioctl_PROMISC, tc) +{ + + atf_tc_set_md_var(tc, "descr", "Checks behaviors of BIOCPROMISC"); +} + +ATF_TC_BODY(bpf_ioctl_PROMISC, tc) +{ + struct ifreq ifr; + int ifnum, bpfd; + + RZ(rump_init()); + RZ(rump_pub_shmif_create(NULL, &ifnum)); + sprintf(ifr.ifr_name, "shmif%d", ifnum); + + RL(bpfd = rump_sys_open("/dev/bpf", O_RDWR)); + + ATF_REQUIRE_EQ_MSG(rump_sys_ioctl(bpfd, BIOCPROMISC, NULL), -1, + "Don't allow to call ioctl(BIOCPROMISC) without interface"); + + RL(rump_sys_ioctl(bpfd, BIOCSETIF, &ifr)); + + RL(rump_sys_ioctl(bpfd, BIOCPROMISC, NULL)); + /* TODO check if_flags */ +} + +ATF_TC(bpf_ioctl_SETIF); +ATF_TC_HEAD(bpf_ioctl_SETIF, tc) +{ + + atf_tc_set_md_var(tc, "descr", "Checks behaviors of BIOCSETIF"); +} + +ATF_TC_BODY(bpf_ioctl_SETIF, tc) +{ + struct ifreq ifr; + int ifnum, bpfd; + + RZ(rump_init()); + + RL(bpfd = rump_sys_open("/dev/bpf", O_RDWR)); + + RZ(rump_pub_shmif_create(NULL, &ifnum)); + sprintf(ifr.ifr_name, "shmif%d", ifnum); + RL(rump_sys_ioctl(bpfd, BIOCSETIF, &ifr)); + + /* Change the listening interface */ + RZ(rump_pub_shmif_create(NULL, &ifnum)); + sprintf(ifr.ifr_name, "shmif%d", ifnum); + RL(rump_sys_ioctl(bpfd, BIOCSETIF, &ifr)); +} + +ATF_TC(bpf_ioctl_DLT); +ATF_TC_HEAD(bpf_ioctl_DLT, tc) +{ + + atf_tc_set_md_var(tc, "descr", "Checks behaviors of BIOCGDLT and " + "BIOCSDLT"); +} + +ATF_TC_BODY(bpf_ioctl_DLT, tc) +{ + struct ifreq ifr; + int ifnum, bpfd; + u_int dlt; + + RZ(rump_init()); + RL(bpfd = rump_sys_open("/dev/bpf", O_RDWR)); + RZ(rump_pub_shmif_create(NULL, &ifnum)); + sprintf(ifr.ifr_name, "shmif%d", ifnum); + + ATF_REQUIRE_EQ_MSG(rump_sys_ioctl(bpfd, BIOCGDLT, &dlt), -1, + "Don't allow to get a DLT without interfaces"); + + RL(rump_sys_ioctl(bpfd, BIOCSETIF, &ifr)); + + RL(rump_sys_ioctl(bpfd, BIOCGDLT, &dlt)); + ATF_REQUIRE(dlt == DLT_EN10MB); + + dlt = DLT_NULL; + ATF_REQUIRE_EQ_MSG(rump_sys_ioctl(bpfd, BIOCSDLT, &dlt), -1, + "Don't allow to set a DLT that doesn't match any listening " + "interfaces"); +} + +ATF_TC(bpf_ioctl_GDLTLIST); +ATF_TC_HEAD(bpf_ioctl_GDLTLIST, tc) +{ + + atf_tc_set_md_var(tc, "descr", "Checks behaviors of BIOCGDLTLIST"); +} + +ATF_TC_BODY(bpf_ioctl_GDLTLIST, tc) +{ + struct ifreq ifr; + int ifnum, bpfd; + struct bpf_dltlist dltlist; + + RZ(rump_init()); + RL(bpfd = rump_sys_open("/dev/bpf", O_RDWR)); + RZ(rump_pub_shmif_create(NULL, &ifnum)); + sprintf(ifr.ifr_name, "shmif%d", ifnum); + + dltlist.bfl_len = 0; + dltlist.bfl_list = NULL; + ATF_REQUIRE_EQ_MSG(rump_sys_ioctl(bpfd, BIOCGDLTLIST, &dltlist), -1, + "Don't allow to get a DLT list without interfaces"); + + RL(rump_sys_ioctl(bpfd, BIOCSETIF, &ifr)); + + /* Get the size of an avaiable DLT list */ + dltlist.bfl_len = 0; + dltlist.bfl_list = NULL; + RL(rump_sys_ioctl(bpfd, BIOCGDLTLIST, &dltlist)); + ATF_REQUIRE(dltlist.bfl_len == 1); + + /* Get an avaiable DLT list */ + dltlist.bfl_list = calloc(sizeof(u_int), 1); + dltlist.bfl_len = 1; + RL(rump_sys_ioctl(bpfd, BIOCGDLTLIST, &dltlist)); + ATF_REQUIRE(dltlist.bfl_len == 1); + ATF_REQUIRE(dltlist.bfl_list[0] == DLT_EN10MB); + + /* Get an avaiable DLT list with a less buffer (fake with bfl_len) */ + dltlist.bfl_len = 0; + ATF_REQUIRE_EQ_MSG(rump_sys_ioctl(bpfd, BIOCGDLTLIST, &dltlist), -1, + "This should fail with ENOMEM"); + + free(dltlist.bfl_list); +} + ATF_TP_ADD_TCS(tp) { @@ -207,5 +340,9 @@ ATF_TP_ADD_TC(tp, bpfwritetrunc); #endif ATF_TP_ADD_TC(tp, bpf_ioctl_BLEN); + ATF_TP_ADD_TC(tp, bpf_ioctl_PROMISC); + ATF_TP_ADD_TC(tp, bpf_ioctl_SETIF); + ATF_TP_ADD_TC(tp, bpf_ioctl_DLT); + ATF_TP_ADD_TC(tp, bpf_ioctl_GDLTLIST); return atf_no_error(); } diff --git a/net/bpfilter/Makefile b/net/bpfilter/Makefile --- a/net/bpfilter/Makefile +++ b/net/bpfilter/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.2 2014/06/10 04:28:40 he Exp $ +# $NetBSD: Makefile,v 1.3 2020/03/01 18:08:15 christos Exp $ # .include @@ -9,6 +9,6 @@ LDADD+= -lrumpnet_shmif LDADD+= -lrumpdev_bpf -lrumpdev -lrumpnet_netinet -lrumpnet_net -LDADD+= -lrumpnet -lrumpvfs -lrump -lrumpuser -lrump -lpthread +LDADD+= -lrumpnet ${LIBRUMPBASE} .include diff --git a/net/bpfjit/Makefile b/net/bpfjit/Makefile --- a/net/bpfjit/Makefile +++ b/net/bpfjit/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.6 2016/08/08 15:01:59 pgoyette Exp $ +# $NetBSD: Makefile,v 1.9 2020/03/01 18:08:15 christos Exp $ # .include @@ -14,6 +14,6 @@ LDADD+= -lrumpnet_bpfjit -lrumpkern_sljit LDADD+= -lrumpdev_bpf -lrumpnet_net -lrumpnet -LDADD+= -lrump -lrumpuser -lpthread -lrumpdev -lrumpvfs +LDADD+= ${LIBRUMPBASE} .include diff --git a/net/can/Makefile b/net/can/Makefile new file mode 100644 --- /dev/null +++ b/net/can/Makefile @@ -0,0 +1,21 @@ +# $NetBSD: Makefile,v 1.3 2020/03/01 18:08:16 christos Exp $ +# + +.include + +TESTSDIR= ${TESTSBASE}/net/can + +TESTS_C= t_can t_canfilter + +SRCS.t_can= t_can.c h_canutils.c + +SRCS.t_canfilter= t_canfilter.c h_canutils.c + +# XXX we don't use INET here, but we need rumpnet_netinet anyway: +# common code in if.c is compiled with -DINET and will dereference ip_pktq, +# which is NULL if rumpnet_netinet is not inclued. +# +LDADD+= -lrumpnet_netcan -lrumpnet_netinet -lrumpnet_net -lrumpnet +LDADD+= ${LIBRUMPBASE} + +.include diff --git a/net/can/h_canutils.h b/net/can/h_canutils.h new file mode 100644 --- /dev/null +++ b/net/can/h_canutils.h @@ -0,0 +1,37 @@ +/* $NetBSD: h_canutils.h,v 1.2 2017/05/27 21:02:56 bouyer Exp $ */ + +/*- + * Copyright (c) 2017 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Manuel Bouyer + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +void cancfg_rump_createif(const char *); +int can_recvfrom(int, struct can_frame *, int *, struct sockaddr_can *); +int can_read(int, struct can_frame *, int *); +int can_bind(int s, const char *); +int can_socket_with_own(void); diff --git a/net/can/h_canutils.c b/net/can/h_canutils.c new file mode 100644 --- /dev/null +++ b/net/can/h_canutils.c @@ -0,0 +1,202 @@ +/* $NetBSD: h_canutils.c,v 1.4 2019/10/13 07:46:16 mrg Exp $ */ + +/*- + * Copyright (c) 2017 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Manuel Bouyer + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +#ifndef lint +__RCSID("$NetBSD: h_canutils.c,v 1.4 2019/10/13 07:46:16 mrg Exp $"); +#endif /* not lint */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "h_macros.h" +#include "h_canutils.h" + +void +cancfg_rump_createif(const char *ifname) +{ + int s, rv; + struct ifreq ifr; + + s = -1; + if ((s = rump_sys_socket(AF_CAN, SOCK_RAW, CAN_RAW)) < 0) { + atf_tc_fail_errno("if config socket"); + } + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, ifname, IFNAMSIZ-1); + ifr.ifr_name[IFNAMSIZ - 1] = '\0'; + + if ((rv = rump_sys_ioctl(s, SIOCIFCREATE, &ifr)) < 0) { + atf_tc_fail_errno("if config create"); + } + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, ifname, IFNAMSIZ-1); + ifr.ifr_name[IFNAMSIZ - 1] = '\0'; + + if ((rv = rump_sys_ioctl(s, SIOCGIFFLAGS, &ifr)) < 0) { + atf_tc_fail_errno("if config get flags"); + } + + ifr.ifr_flags |= IFF_UP; + if ((rv = rump_sys_ioctl(s, SIOCSIFFLAGS, &ifr)) < 0) { + atf_tc_fail_errno("if config set flags"); + } +} + +int +can_recvfrom(int s, struct can_frame *cf, int *len, struct sockaddr_can *sa) +{ + socklen_t salen; + fd_set rfds; + struct timeval tmout; + int rv; + + memset(cf, 0, sizeof(struct can_frame)); + FD_ZERO(&rfds); + FD_SET(s, &rfds); + /* wait 1 second for the message (in some tests we expect no message) */ + tmout.tv_sec = 1; + tmout.tv_usec = 0; + rv = rump_sys_select(s + 1, &rfds, NULL, NULL, &tmout); + switch(rv) { + case -1: + atf_tc_fail_errno("select"); + /* NOTREACHED */ + case 0: + /* timeout */ + errno = EWOULDBLOCK; + return -1; + default: break; + } + ATF_CHECK_MSG(FD_ISSET(s, &rfds), "select returns but s not in set"); + salen = sizeof(struct sockaddr_can); + if (( *len = rump_sys_recvfrom(s, cf, sizeof(struct can_frame), + 0, (struct sockaddr *)sa, &salen)) < 0) { + atf_tc_fail_errno("recvfrom"); + } + ATF_CHECK_MSG(rv > 0, "short read on socket"); + ATF_CHECK_MSG(sa->can_family == AF_CAN, + "recvfrom provided wrong %d family", sa->can_family); + ATF_CHECK_MSG(salen == sizeof(struct sockaddr_can), + "recvfrom provided wrong size %d (!= %zu)", salen, sizeof(sa)); + return 0; +} + +int +can_read(int s, struct can_frame *cf, int *len) +{ + fd_set rfds; + struct timeval tmout; + int rv; + + memset(cf, 0, sizeof(struct can_frame)); + FD_ZERO(&rfds); + FD_SET(s, &rfds); + /* wait 1 second for the message (in some tests we expect no message) */ + tmout.tv_sec = 1; + tmout.tv_usec = 0; + rv = rump_sys_select(s + 1, &rfds, NULL, NULL, &tmout); + switch(rv) { + case -1: + atf_tc_fail_errno("select"); + /* NOTREACHED */ + case 0: + /* timeout */ + errno = EWOULDBLOCK; + return -1; + default: break; + } + ATF_CHECK_MSG(FD_ISSET(s, &rfds), "select returns but s not in set"); + if (( *len = rump_sys_read(s, cf, sizeof(struct can_frame))) < 0) { + atf_tc_fail_errno("read"); + } + ATF_CHECK_MSG(rv > 0, "short read on socket"); + return 0; +} + +int +can_bind(int s, const char *ifname) +{ + struct ifreq ifr; + struct sockaddr_can sa; + + strcpy(ifr.ifr_name, ifname ); + if (rump_sys_ioctl(s, SIOCGIFINDEX, &ifr) < 0) { + atf_tc_fail_errno("SIOCGIFINDEX"); + } + ATF_CHECK_MSG(ifr.ifr_ifindex > 0, "%s index is %d (not > 0)", + ifname, ifr.ifr_ifindex); + + sa.can_family = AF_CAN; + sa.can_ifindex = ifr.ifr_ifindex; + + if (rump_sys_bind(s, (struct sockaddr *)&sa, sizeof(sa)) < 0) { + atf_tc_fail_errno("bind"); + } + return ifr.ifr_ifindex; +} + +int +can_socket_with_own(void) +{ + int s; + int v; + + if ((s = rump_sys_socket(AF_CAN, SOCK_RAW, CAN_RAW)) < 0) { + atf_tc_fail_errno("CAN socket"); + } + v = 1; + if (rump_sys_setsockopt(s, SOL_CAN_RAW, CAN_RAW_RECV_OWN_MSGS, + &v, sizeof(v)) < 0) { + atf_tc_fail_errno("setsockopt(CAN_RAW_RECV_OWN_MSGS)"); + } + return s; +} diff --git a/net/can/t_can.c b/net/can/t_can.c new file mode 100644 --- /dev/null +++ b/net/can/t_can.c @@ -0,0 +1,760 @@ +/* $NetBSD: t_can.c,v 1.8 2021/08/20 20:25:28 andvar Exp $ */ + +/*- + * Copyright (c) 2017 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Manuel Bouyer + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +#ifndef lint +__RCSID("$NetBSD: t_can.c,v 1.8 2021/08/20 20:25:28 andvar Exp $"); +#endif /* not lint */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "h_macros.h" +#include "h_canutils.h" + +ATF_TC(canlocreate); +ATF_TC_HEAD(canlocreate, tc) +{ + + atf_tc_set_md_var(tc, "descr", "check CAN loopback create/destroy"); + atf_tc_set_md_var(tc, "timeout", "5"); +} + +ATF_TC_BODY(canlocreate, tc) +{ + const char ifname[] = "canlo0"; + int s, rv; + struct ifreq ifr; + + rump_init(); + cancfg_rump_createif(ifname); + + s = -1; + if ((s = rump_sys_socket(AF_CAN, SOCK_RAW, CAN_RAW)) < 0) { + atf_tc_fail_errno("if config socket(2)"); + } + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, ifname, IFNAMSIZ); + + if ((rv = rump_sys_ioctl(s, SIOCIFDESTROY, &ifr)) < 0) { + atf_tc_fail_errno("if config destroy"); + } +} + +ATF_TC(cannoown); +ATF_TC_HEAD(cannoown, tc) +{ + + atf_tc_set_md_var(tc, "descr", "check that CAN sockets don't gets its own message"); + atf_tc_set_md_var(tc, "timeout", "5"); +} + +ATF_TC_BODY(cannoown, tc) +{ + const char ifname[] = "canlo0"; + int s, rv, v; + socklen_t vlen; + struct sockaddr_can sa; + int ifindex; + struct can_frame cf_send, cf_receive; + + rump_init(); + cancfg_rump_createif(ifname); + + if ((s = rump_sys_socket(AF_CAN, SOCK_RAW, CAN_RAW)) < 0) { + atf_tc_fail_errno("CAN socket"); + } + + ifindex = can_bind(s, ifname); + + /* check sockopt CAN_RAW_LOOPBACK */ + vlen = sizeof(v); + if (rump_sys_getsockopt(s, SOL_CAN_RAW, CAN_RAW_LOOPBACK, + &v, &vlen) < 0) { + atf_tc_fail_errno("getsockopt(CAN_RAW_LOOPBACK)"); + } + ATF_CHECK_MSG(vlen == sizeof(v), "getsockopt(CAN_RAW_LOOPBACK) returns wrong len %d", vlen); + ATF_CHECK_MSG(v == 1, "CAN_RAW_LOOPBACK is not on by default"); + + /* check sockopt CAN_RAW_RECV_OWN_MSGS, and set it */ + vlen = sizeof(v); + if (rump_sys_getsockopt(s, SOL_CAN_RAW, CAN_RAW_RECV_OWN_MSGS, + &v, &vlen) < 0) { + atf_tc_fail_errno("getsockopt(CAN_RAW_RECV_OWN_MSGS)"); + } + ATF_CHECK_MSG(vlen == sizeof(v), "getsockopt(CAN_RAW_RECV_OWN_MSGS) returns wrong len %d", vlen); + ATF_CHECK_MSG(v == 0, "CAN_RAW_RECV_OWN_MSGS is not off by default"); + + /* + * send a single byte message, but make sure remaining payload is + * not 0. + */ + + memset(&cf_send, 0, sizeof(cf_send)); + cf_send.can_id = 1; + cf_send.can_dlc = 1; + cf_send.data[0] = 0xde; + cf_send.data[1] = 0xad; + cf_send.data[2] = 0xbe; + cf_send.data[3] = 0xef; + + if (rump_sys_write(s, &cf_send, sizeof(cf_send) - 7) < 0) { + atf_tc_fail_errno("write"); + } + + /* now try to read */ + if (can_recvfrom(s, &cf_receive, &rv, &sa) < 0) { + if (errno == EWOULDBLOCK) + return; /* expected timeout */ + atf_tc_fail_errno("can_recvfrom"); + } + + ATF_CHECK_MSG(sa.can_ifindex == ifindex, + "recvfrom provided wrong ifindex %d (!= %d)", + sa.can_ifindex, ifindex); + atf_tc_fail("we got our own message"); +} + +ATF_TC(canwritelo); +ATF_TC_HEAD(canwritelo, tc) +{ + + atf_tc_set_md_var(tc, "descr", "check that CAN sockets gets its own message via write"); + atf_tc_set_md_var(tc, "timeout", "5"); +} + +ATF_TC_BODY(canwritelo, tc) +{ + const char ifname[] = "canlo0"; + int s, rv, v; + socklen_t vlen; + struct can_frame cf_send, cf_receive; + + rump_init(); + cancfg_rump_createif(ifname); + + s = can_socket_with_own(); + + can_bind(s, ifname); + + /* check sockopt CAN_RAW_LOOPBACK */ + vlen = sizeof(v); + if (rump_sys_getsockopt(s, SOL_CAN_RAW, CAN_RAW_LOOPBACK, + &v, &vlen) < 0) { + atf_tc_fail_errno("getsockopt(CAN_RAW_LOOPBACK)"); + } + ATF_CHECK_MSG(vlen == sizeof(v), "getsockopt(CAN_RAW_LOOPBACK) returns wrong len %d", vlen); + ATF_CHECK_MSG(v == 1, "CAN_RAW_LOOPBACK is not on by default"); + + /* check that sockopt CAN_RAW_RECV_OWN_MSGS is on */ + vlen = sizeof(v); + if (rump_sys_getsockopt(s, SOL_CAN_RAW, CAN_RAW_RECV_OWN_MSGS, + &v, &vlen) < 0) { + atf_tc_fail_errno("getsockopt(CAN_RAW_RECV_OWN_MSGS)"); + } + ATF_CHECK_MSG(v == 1, "CAN_RAW_RECV_OWN_MSGS is not on"); + + /* + * send a single byte message, but make sure remaining payload is + * not 0. + */ + + memset(&cf_send, 0, sizeof(cf_send)); + cf_send.can_id = 1; + cf_send.can_dlc = 1; + cf_send.data[0] = 0xde; + cf_send.data[1] = 0xad; + cf_send.data[2] = 0xbe; + cf_send.data[3] = 0xef; + + if (rump_sys_write(s, &cf_send, sizeof(cf_send) - 7) < 0) { + atf_tc_fail_errno("write"); + } + + if (can_read(s, &cf_receive, &rv) < 0) { + atf_tc_fail_errno("can_read"); + } + + memset(&cf_send, 0, sizeof(cf_send)); + cf_send.can_id = 1; + cf_send.can_dlc = 1; + cf_send.data[0] = 0xde; + /* other data[] are expected to be 0 */ + + ATF_CHECK_MSG(memcmp(&cf_send, &cf_receive, sizeof(cf_send)) == 0, + "received packet is not what we sent"); +} + +ATF_TC(canwriteunbound); +ATF_TC_HEAD(canwriteunbound, tc) +{ + + atf_tc_set_md_var(tc, "descr", "check that write to unbound CAN sockets fails"); + atf_tc_set_md_var(tc, "timeout", "5"); +} + +ATF_TC_BODY(canwriteunbound, tc) +{ + const char ifname[] = "canlo0"; + int s, rv; + struct can_frame cf_send; + + rump_init(); + cancfg_rump_createif(ifname); + + s = -1; + if ((s = rump_sys_socket(AF_CAN, SOCK_RAW, CAN_RAW)) < 0) { + atf_tc_fail_errno("CAN socket"); + } + + /* + * send a single byte message. + * not 0. + */ + + memset(&cf_send, 0, sizeof(cf_send)); + cf_send.can_id = 1; + cf_send.can_dlc = 1; + cf_send.data[0] = 0xde; + + rv = rump_sys_write(s, &cf_send, sizeof(cf_send) - 7); + ATF_CHECK_MSG(rv < 0 && errno == EDESTADDRREQ, + "write to unbound socket didn't fail"); +} + +ATF_TC(cansendtolo); +ATF_TC_HEAD(cansendtolo, tc) +{ + + atf_tc_set_md_var(tc, "descr", "check that CAN sockets gets its own message via sendto"); + atf_tc_set_md_var(tc, "timeout", "5"); +} + +ATF_TC_BODY(cansendtolo, tc) +{ + const char ifname[] = "canlo0"; + int s, rv; + struct sockaddr_can sa; + struct ifreq ifr; + struct can_frame cf_send, cf_receive; + + rump_init(); + cancfg_rump_createif(ifname); + + s = can_socket_with_own(); + + strcpy(ifr.ifr_name, ifname ); + if ((rv = rump_sys_ioctl(s, SIOCGIFINDEX, &ifr)) < 0) { + atf_tc_fail_errno("SIOCGIFINDEX"); + } + ATF_CHECK_MSG(ifr.ifr_ifindex > 0, "%s index is %d (not > 0)", + ifname, ifr.ifr_ifindex); + + sa.can_family = AF_CAN; + sa.can_ifindex = ifr.ifr_ifindex; + + /* + * send a single byte message, but make sure remaining payload is + * not 0. + */ + + memset(&cf_send, 0, sizeof(cf_send)); + cf_send.can_id = 1; + cf_send.can_dlc = 1; + cf_send.data[0] = 0xde; + cf_send.data[1] = 0xad; + cf_send.data[2] = 0xbe; + cf_send.data[3] = 0xef; + + if (rump_sys_sendto(s, &cf_send, sizeof(cf_send) - 7, + 0, (struct sockaddr *)&sa, sizeof(sa)) < 0) { + atf_tc_fail_errno("sendto"); + } + + if (can_read(s, &cf_receive, &rv) < 0) { + atf_tc_fail_errno("read"); + } + + memset(&cf_send, 0, sizeof(cf_send)); + cf_send.can_id = 1; + cf_send.can_dlc = 1; + cf_send.data[0] = 0xde; + /* other data[] are expected to be 0 */ + + ATF_CHECK_MSG(memcmp(&cf_send, &cf_receive, sizeof(cf_send)) == 0, + "received packet is not what we sent"); +} + +ATF_TC(cansendtowrite); +ATF_TC_HEAD(cansendtowrite, tc) +{ + + atf_tc_set_md_var(tc, "descr", "check that write after sendto on unbound socket fails"); + atf_tc_set_md_var(tc, "timeout", "5"); +} + +ATF_TC_BODY(cansendtowrite, tc) +{ + const char ifname[] = "canlo0"; + int s, rv; + struct sockaddr_can sa; + struct ifreq ifr; + struct can_frame cf_send, cf_receive; + + rump_init(); + cancfg_rump_createif(ifname); + + s = can_socket_with_own(); + + strcpy(ifr.ifr_name, ifname ); + if ((rv = rump_sys_ioctl(s, SIOCGIFINDEX, &ifr)) < 0) { + atf_tc_fail_errno("SIOCGIFINDEX"); + } + ATF_CHECK_MSG(ifr.ifr_ifindex > 0, "%s index is %d (not > 0)", + ifname, ifr.ifr_ifindex); + + sa.can_family = AF_CAN; + sa.can_ifindex = ifr.ifr_ifindex; + + /* + * send a single byte message. + * not 0. + */ + + memset(&cf_send, 0, sizeof(cf_send)); + cf_send.can_id = 1; + cf_send.can_dlc = 1; + cf_send.data[0] = 0xde; + + if (rump_sys_sendto(s, &cf_send, sizeof(cf_send) - 7, + 0, (struct sockaddr *)&sa, sizeof(sa)) < 0) { + atf_tc_fail_errno("sendto"); + } + + if (can_read(s, &cf_receive, &rv) < 0) { + atf_tc_fail_errno("read"); + } + + memset(&cf_send, 0, sizeof(cf_send)); + cf_send.can_id = 1; + cf_send.can_dlc = 1; + cf_send.data[0] = 0xde; + /* other data[] are expected to be 0 */ + + ATF_CHECK_MSG(memcmp(&cf_send, &cf_receive, sizeof(cf_send)) == 0, + "received packet is not what we sent"); + + rv = rump_sys_write(s, &cf_send, sizeof(cf_send) - 7); + ATF_CHECK_MSG(rv < 0 && errno == EDESTADDRREQ, + "write to unbound socket didn't fail"); +} + +ATF_TC(canreadlocal); +ATF_TC_HEAD(canreadlocal, tc) +{ + + atf_tc_set_md_var(tc, "descr", "check all CAN sockets get messages"); + atf_tc_set_md_var(tc, "timeout", "5"); +} + +ATF_TC_BODY(canreadlocal, tc) +{ + const char ifname[] = "canlo0"; + int s1, rv1; + int s2, rv2; + struct can_frame cf_send, cf_receive1, cf_receive2; + + rump_init(); + cancfg_rump_createif(ifname); + + memset(&cf_send, 0, sizeof(cf_send)); + cf_send.can_id = 1; + cf_send.can_dlc = 8; + cf_send.data[0] = 0xde; + cf_send.data[1] = 0xad; + cf_send.data[2] = 0xbe; + cf_send.data[3] = 0xef; + + + if ((s1 = rump_sys_socket(AF_CAN, SOCK_RAW, CAN_RAW)) < 0) { + atf_tc_fail_errno("CAN socket"); + } + + /* create a second socket */ + + s2 = can_socket_with_own(); + + can_bind(s2, ifname); + + /* + * send a single byte message, but make sure remaining payload is + * not 0. + */ + + if (rump_sys_write(s2, &cf_send, sizeof(cf_send)) < 0) { + atf_tc_fail_errno("write"); + } + + if (can_read(s2, &cf_receive2, &rv2) < 0) { + atf_tc_fail_errno("can_read"); + } + + ATF_CHECK_MSG(memcmp(&cf_send, &cf_receive2, sizeof(cf_send)) == 0, + "received (2) packet is not what we sent"); + + /* now check first socket */ + if (can_read(s1, &cf_receive1, &rv1) < 0) { + atf_tc_fail_errno("can_read"); + } + + ATF_CHECK_MSG(memcmp(&cf_send, &cf_receive1, sizeof(cf_send)) == 0, + "received (1) packet is not what we sent"); +} + +ATF_TC(canrecvfrom); +ATF_TC_HEAD(canrecvfrom, tc) +{ + + atf_tc_set_md_var(tc, "descr", "check that recvfrom gets the CAN interface"); + atf_tc_set_md_var(tc, "timeout", "5"); +} + +ATF_TC_BODY(canrecvfrom, tc) +{ + const char ifname[] = "canlo0"; + int s1, rv1; + int s2, rv2; + struct can_frame cf_send, cf_receive1, cf_receive2; + int ifindex; + struct sockaddr_can sa; + + rump_init(); + cancfg_rump_createif(ifname); + + memset(&cf_send, 0, sizeof(cf_send)); + cf_send.can_id = 1; + cf_send.can_dlc = 8; + cf_send.data[0] = 0xde; + cf_send.data[1] = 0xad; + cf_send.data[2] = 0xbe; + cf_send.data[3] = 0xef; + + + s1 = -1; + if ((s1 = rump_sys_socket(AF_CAN, SOCK_RAW, CAN_RAW)) < 0) { + atf_tc_fail_errno("CAN socket"); + } + + /* create a second socket */ + + s2 = can_socket_with_own(); + + ifindex = can_bind(s2, ifname); + + if (rump_sys_write(s2, &cf_send, sizeof(cf_send)) < 0) { + atf_tc_fail_errno("write"); + } + + if (can_read(s2, &cf_receive2, &rv2) < 0) { + atf_tc_fail_errno("can_read"); + } + + ATF_CHECK_MSG(memcmp(&cf_send, &cf_receive2, sizeof(cf_send)) == 0, + "received (2) packet is not what we sent"); + + /* now check first socket */ + memset(&sa, 0, sizeof(sa)); + if (can_recvfrom(s1, &cf_receive1, &rv1, &sa) < 0) { + atf_tc_fail_errno("can_recvfrom"); + } + + ATF_CHECK_MSG(memcmp(&cf_send, &cf_receive1, sizeof(cf_send)) == 0, + "recvfrom (1) packet is not what we sent"); + ATF_CHECK_MSG(sa.can_ifindex == ifindex, + "recvfrom provided wrong ifindex %d (!= %d)", + sa.can_ifindex, ifindex); +} + +ATF_TC(canbindfilter); +ATF_TC_HEAD(canbindfilter, tc) +{ + + atf_tc_set_md_var(tc, "descr", "check that socket bound to an interface doesn't get other interface's messages"); + atf_tc_set_md_var(tc, "timeout", "5"); +} + +ATF_TC_BODY(canbindfilter, tc) +{ + const char ifname[] = "canlo0"; + const char ifname2[] = "canlo1"; + int s1, rv1; + int s2, rv2; + struct sockaddr_can sa; + int ifindex2; + struct can_frame cf_send, cf_receive1, cf_receive2; + + rump_init(); + cancfg_rump_createif(ifname); + cancfg_rump_createif(ifname2); + + memset(&cf_send, 0, sizeof(cf_send)); + cf_send.can_id = 1; + cf_send.can_dlc = 8; + cf_send.data[0] = 0xde; + cf_send.data[1] = 0xad; + cf_send.data[2] = 0xbe; + cf_send.data[3] = 0xef; + + + s1 = can_socket_with_own(); + + can_bind(s1, ifname); + + /* create a second socket */ + + s2 = can_socket_with_own(); + + ifindex2 = can_bind(s2, ifname2); + + if (rump_sys_write(s2, &cf_send, sizeof(cf_send)) < 0) { + atf_tc_fail_errno("write"); + } + + if (can_read(s2, &cf_receive2, &rv2) < 0) { + atf_tc_fail_errno("read"); + } + + ATF_CHECK_MSG(memcmp(&cf_send, &cf_receive2, sizeof(cf_send)) == 0, + "received (2) packet is not what we sent"); + + /* now check first socket */ + if (can_recvfrom(s1, &cf_receive1, &rv1, &sa) < 0) { + if (errno == EWOULDBLOCK) { + /* expected case */ + return; + } + atf_tc_fail_errno("can_recvfrom"); + } + ATF_CHECK_MSG(memcmp(&cf_send, &cf_receive1, sizeof(cf_send)) == 0, + "recvfrom (1) packet is not what we sent"); + ATF_CHECK_MSG(sa.can_ifindex == ifindex2, + "recvfrom provided wrong ifindex %d (!= %d)", + sa.can_ifindex, ifindex2); + atf_tc_fail("we got message from other interface"); +} + +ATF_TC(cannoloop); +ATF_TC_HEAD(cannoloop, tc) +{ + + atf_tc_set_md_var(tc, "descr", "check that disabling loopback works"); + atf_tc_set_md_var(tc, "timeout", "5"); +} + +ATF_TC_BODY(cannoloop, tc) +{ + const char ifname[] = "canlo0"; + int s1, rv1; + int s2, rv2; + int v; + socklen_t vlen; + struct sockaddr_can sa; + struct can_frame cf_send, cf_receive1, cf_receive2; + socklen_t salen; + int ifindex; + fd_set rfds; + struct timeval tmout; + + rump_init(); + cancfg_rump_createif(ifname); + + memset(&cf_send, 0, sizeof(cf_send)); + cf_send.can_id = 1; + cf_send.can_dlc = 8; + cf_send.data[0] = 0xde; + cf_send.data[1] = 0xad; + cf_send.data[2] = 0xbe; + cf_send.data[3] = 0xef; + + + s1 = can_socket_with_own(); + v = 0; + if (rump_sys_setsockopt(s1, SOL_CAN_RAW, CAN_RAW_LOOPBACK, + &v, sizeof(v)) < 0) { + atf_tc_fail_errno("setsockopt(LOOPBACK)"); + } + v = -1; + vlen = sizeof(v); + if (rump_sys_getsockopt(s1, SOL_CAN_RAW, CAN_RAW_LOOPBACK, + &v, &vlen) < 0) { + atf_tc_fail_errno("getsockopt(CAN_RAW_LOOPBACK)"); + } + ATF_CHECK_MSG(vlen == sizeof(v), "getsockopt(CAN_RAW_LOOPBACK) returns wrong len %d", vlen); + ATF_CHECK_MSG(v == 0, "CAN_RAW_LOOPBACK is not off"); + + ifindex = can_bind(s1, ifname); + + /* create a second socket */ + s2 = can_socket_with_own(); + + if (rump_sys_write(s1, &cf_send, sizeof(cf_send)) < 0) { + atf_tc_fail_errno("write"); + } + + + /* now check the sockets */ + memset(&cf_receive1, 0, sizeof(cf_receive1)); + memset(&cf_receive2, 0, sizeof(cf_receive2)); + FD_ZERO(&rfds); + FD_SET(s1, &rfds); + FD_SET(s2, &rfds); + /* we should receive no message; wait for 1 seconds */ + tmout.tv_sec = 1; + tmout.tv_usec = 0; + rv1 = rump_sys_select(MAX(s1,s2) + 1, &rfds, NULL, NULL, &tmout); + switch(rv1) { + case -1: + atf_tc_fail_errno("select"); + break; + case 0: + /* timeout: expected case */ + return; + default: break; + } + salen = sizeof(sa); + ATF_CHECK_MSG(FD_ISSET(s1, &rfds) || FD_ISSET(s2, &rfds), + "select returns but s1 nor s2 is in set"); + if (FD_ISSET(s1, &rfds)) { + if (( rv1 = rump_sys_recvfrom(s1, &cf_receive1, + sizeof(cf_receive1), 0, + (struct sockaddr *)&sa, &salen)) < 0) { + atf_tc_fail_errno("recvfrom"); + } + + ATF_CHECK_MSG(rv1 > 0, "short read on socket"); + + ATF_CHECK_MSG(memcmp(&cf_send, &cf_receive1, + sizeof(cf_send)) == 0, + "recvfrom (1) packet is not what we sent"); + ATF_CHECK_MSG(sa.can_family == AF_CAN, + "recvfrom provided wrong %d family", sa.can_family); + ATF_CHECK_MSG(salen == sizeof(sa), + "recvfrom provided wrong size %u (!= %zu)", + salen, sizeof(sa)); + ATF_CHECK_MSG(sa.can_ifindex == ifindex, + "recvfrom provided wrong ifindex %d (!= %d)", + sa.can_ifindex, ifindex); + atf_tc_fail_nonfatal("we got message on s1"); + } + if (FD_ISSET(s2, &rfds)) { + if (( rv2 = rump_sys_recvfrom(s2, &cf_receive2, + sizeof(cf_receive2), 0, + (struct sockaddr *)&sa, &salen)) < 0) { + atf_tc_fail_errno("recvfrom"); + } + + ATF_CHECK_MSG(rv2 > 0, "short read on socket"); + + ATF_CHECK_MSG(memcmp(&cf_send, &cf_receive2, + sizeof(cf_send)) == 0, + "recvfrom (2) packet is not what we sent"); + ATF_CHECK_MSG(sa.can_family == AF_CAN, + "recvfrom provided wrong %d family", sa.can_family); + ATF_CHECK_MSG(salen == sizeof(sa), + "recvfrom provided wrong size %u (!= %zu)", + salen, sizeof(sa)); + ATF_CHECK_MSG(sa.can_ifindex == ifindex, + "recvfrom provided wrong ifindex %d (!= %d)", + sa.can_ifindex, ifindex); + atf_tc_fail_nonfatal("we got message on s2"); + } +} + +ATF_TC(canbindunknown); +ATF_TC_HEAD(canbindunknown, tc) +{ + + atf_tc_set_md_var(tc, "descr", "check that bind to unknown interface fails"); + atf_tc_set_md_var(tc, "timeout", "5"); +} + +ATF_TC_BODY(canbindunknown, tc) +{ + struct sockaddr_can sa; + int r, s; + + rump_init(); + + s = can_socket_with_own(); + + sa.can_family = AF_CAN; + sa.can_ifindex = 10; /* should not exist */ + + r = rump_sys_bind(s, (struct sockaddr *)&sa, sizeof(sa)); + + ATF_CHECK_MSG(r < 0, "bind() didn't fail (%d)", r); +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, canlocreate); + ATF_TP_ADD_TC(tp, cannoown); + ATF_TP_ADD_TC(tp, canwritelo); + ATF_TP_ADD_TC(tp, canwriteunbound); + ATF_TP_ADD_TC(tp, cansendtolo); + ATF_TP_ADD_TC(tp, cansendtowrite); + ATF_TP_ADD_TC(tp, canreadlocal); + ATF_TP_ADD_TC(tp, canrecvfrom); + ATF_TP_ADD_TC(tp, canbindfilter); + ATF_TP_ADD_TC(tp, cannoloop); + ATF_TP_ADD_TC(tp, canbindunknown); + return atf_no_error(); +} diff --git a/net/can/t_canfilter.c b/net/can/t_canfilter.c new file mode 100644 --- /dev/null +++ b/net/can/t_canfilter.c @@ -0,0 +1,464 @@ +/* $NetBSD: t_canfilter.c,v 1.2 2017/05/27 21:02:56 bouyer Exp $ */ + +/*- + * Copyright (c) 2017 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Manuel Bouyer + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +#ifndef lint +__RCSID("$NetBSD: t_canfilter.c,v 1.2 2017/05/27 21:02:56 bouyer Exp $"); +#endif /* not lint */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "h_macros.h" +#include "h_canutils.h" + +ATF_TC(canfilter_basic); +ATF_TC_HEAD(canfilter_basic, tc) +{ + + atf_tc_set_md_var(tc, "descr", "check a simple CAN filter"); + atf_tc_set_md_var(tc, "timeout", "5"); +} + +ATF_TC_BODY(canfilter_basic, tc) +{ + const char ifname[] = "canlo0"; + int s, rv; + struct can_frame cf_send, cf_receive; + struct can_filter cfi; + + rump_init(); + cancfg_rump_createif(ifname); + + s = can_socket_with_own(); + + can_bind(s, ifname); + + /* set filter */ +#define MY_ID 1 + cfi.can_id = MY_ID; + cfi.can_mask = CAN_SFF_MASK | CAN_EFF_FLAG; + if (rump_sys_setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, + &cfi, sizeof(cfi)) < 0) { + atf_tc_fail_errno("setsockopt(CAN_RAW_FILTER)"); + } + + /* + * send a single byte message, but make sure remaining payload is + * not 0. + */ + + memset(&cf_send, 0, sizeof(cf_send)); + cf_send.can_id = MY_ID; + cf_send.can_dlc = 1; + cf_send.data[0] = 0xde; + cf_send.data[1] = 0xad; + cf_send.data[2] = 0xbe; + cf_send.data[3] = 0xef; + + if (rump_sys_write(s, &cf_send, sizeof(cf_send) - 7) < 0) { + atf_tc_fail_errno("write"); + } + + if (can_read(s, &cf_receive, &rv) < 0) { + atf_tc_fail_errno("read"); + } + + ATF_CHECK_MSG(rv > 0, "short read on socket"); + + memset(&cf_send, 0, sizeof(cf_send)); + cf_send.can_id = MY_ID; + cf_send.can_dlc = 1; + cf_send.data[0] = 0xde; + /* other data[] are expected to be 0 */ + + ATF_CHECK_MSG(memcmp(&cf_send, &cf_receive, sizeof(cf_send)) == 0, + "received packet is not what we sent"); + + /* now send a packet with CAN_RTR_FLAG. Should pass too */ + + memset(&cf_send, 0, sizeof(cf_send)); + cf_send.can_id = MY_ID | CAN_RTR_FLAG; + cf_send.can_dlc = 1; + cf_send.data[0] = 0xde; + cf_send.data[1] = 0xad; + cf_send.data[2] = 0xbe; + cf_send.data[3] = 0xef; + + if (rump_sys_write(s, &cf_send, sizeof(cf_send) - 7) < 0) { + atf_tc_fail_errno("write"); + } + + if (can_read(s, &cf_receive, &rv) < 0) { + atf_tc_fail_errno("read"); + } + + ATF_CHECK_MSG(rv > 0, "short read on socket"); + + memset(&cf_send, 0, sizeof(cf_send)); + cf_send.can_id = MY_ID | CAN_RTR_FLAG; + cf_send.can_dlc = 1; + cf_send.data[0] = 0xde; + /* other data[] are expected to be 0 */ + + ATF_CHECK_MSG(memcmp(&cf_send, &cf_receive, sizeof(cf_send)) == 0, + "received packet is not what we sent"); + + /* now send a packet for a different id. Should not pass */ + + memset(&cf_send, 0, sizeof(cf_send)); + cf_send.can_id = MY_ID + 1; + cf_send.can_dlc = 1; + cf_send.data[0] = 0xde; + cf_send.data[1] = 0xad; + cf_send.data[2] = 0xbe; + cf_send.data[3] = 0xef; + + if (rump_sys_write(s, &cf_send, sizeof(cf_send) - 7) < 0) { + atf_tc_fail_errno("write"); + } + + if (can_read(s, &cf_receive, &rv) < 0) { + if (errno == EWOULDBLOCK) + return; /* expected timeout */ + atf_tc_fail_errno("read"); + } + + ATF_CHECK_MSG(rv > 0, "short read on socket"); + + memset(&cf_send, 0, sizeof(cf_send)); + cf_send.can_id = MY_ID + 1; + cf_send.can_dlc = 1; + cf_send.data[0] = 0xde; + /* other data[] are expected to be 0 */ + + ATF_CHECK_MSG(memcmp(&cf_send, &cf_receive, sizeof(cf_send)) == 0, + "received packet is not what we sent"); + atf_tc_fail("we got our own message"); +#undef MY_ID +} + +ATF_TC(canfilter_null); +ATF_TC_HEAD(canfilter_null, tc) +{ + + atf_tc_set_md_var(tc, "descr", "check a NULL CAN filter"); + atf_tc_set_md_var(tc, "timeout", "5"); +} + +ATF_TC_BODY(canfilter_null, tc) +{ + const char ifname[] = "canlo0"; + int s, rv; + struct can_frame cf_send, cf_receive; + struct can_filter cfi[2]; + socklen_t cfilen; + + rump_init(); + cancfg_rump_createif(ifname); + + s = can_socket_with_own(); + can_bind(s, ifname); + + if (rump_sys_setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, + NULL, 0) < 0) { + atf_tc_fail_errno("setsockopt(CAN_RAW_FILTER)"); + } + + /* get filter: should be NULL */ + cfilen = sizeof(cfi); + if (rump_sys_getsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, + &cfi, &cfilen) < 0) { + atf_tc_fail_errno("getsockopt(CAN_RAW_FILTER)"); + } + ATF_CHECK_MSG(cfilen == 0, + "CAN_RAW_FILTER returns wrong len (%d)", cfilen); + /* + * send a single byte message, but make sure remaining payload is + * not 0. + */ +#define MY_ID 1 + + memset(&cf_send, 0, sizeof(cf_send)); + cf_send.can_id = MY_ID; + cf_send.can_dlc = 1; + cf_send.data[0] = 0xde; + cf_send.data[1] = 0xad; + cf_send.data[2] = 0xbe; + cf_send.data[3] = 0xef; + + if (rump_sys_write(s, &cf_send, sizeof(cf_send) - 7) < 0) { + atf_tc_fail_errno("write"); + } + + if (can_read(s, &cf_receive, &rv) < 0) { + if (errno == EWOULDBLOCK) + return; /* expected timeout */ + atf_tc_fail_errno("read"); + } + + ATF_CHECK_MSG(rv > 0, "short read on socket"); + + memset(&cf_send, 0, sizeof(cf_send)); + cf_send.can_id = MY_ID; + cf_send.can_dlc = 1; + cf_send.data[0] = 0xde; + /* other data[] are expected to be 0 */ + + ATF_CHECK_MSG(memcmp(&cf_send, &cf_receive, sizeof(cf_send)) == 0, + "received packet is not what we sent"); + atf_tc_fail("we got our own message"); +#undef MY_ID +} + +ATF_TC(canfilter_multiple); +ATF_TC_HEAD(canfilter_multiple, tc) +{ + + atf_tc_set_md_var(tc, "descr", "check multiple CAN filters"); + atf_tc_set_md_var(tc, "timeout", "5"); +} + +ATF_TC_BODY(canfilter_multiple, tc) +{ + const char ifname[] = "canlo0"; + int s, rv; + struct can_frame cf_send, cf_receive; + struct can_filter cfi[2]; + + rump_init(); + cancfg_rump_createif(ifname); + + s = can_socket_with_own(); + + can_bind(s, ifname); + + /* set filter: accept MY_ID and MY_ID+1 */ +#define MY_ID 1 + cfi[0].can_id = MY_ID; + cfi[0].can_mask = CAN_SFF_MASK | CAN_EFF_FLAG; + cfi[1].can_id = MY_ID + 1; + cfi[1].can_mask = CAN_SFF_MASK | CAN_EFF_FLAG; + if (rump_sys_setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, + &cfi, sizeof(cfi)) < 0) { + atf_tc_fail_errno("setsockopt(CAN_RAW_FILTER)"); + } + + /* + * send a single byte message, but make sure remaining payload is + * not 0. + */ + + memset(&cf_send, 0, sizeof(cf_send)); + cf_send.can_id = MY_ID; + cf_send.can_dlc = 1; + cf_send.data[0] = 0xde; + cf_send.data[1] = 0xad; + cf_send.data[2] = 0xbe; + cf_send.data[3] = 0xef; + + if (rump_sys_write(s, &cf_send, sizeof(cf_send) - 7) < 0) { + atf_tc_fail_errno("write"); + } + + if (can_read(s, &cf_receive, &rv) < 0) { + atf_tc_fail_errno("read"); + } + + ATF_CHECK_MSG(rv > 0, "short read on socket"); + + memset(&cf_send, 0, sizeof(cf_send)); + cf_send.can_id = MY_ID; + cf_send.can_dlc = 1; + cf_send.data[0] = 0xde; + /* other data[] are expected to be 0 */ + + ATF_CHECK_MSG(memcmp(&cf_send, &cf_receive, sizeof(cf_send)) == 0, + "received packet is not what we sent"); + + /* now send a packet with MY_ID+1. Should pass too */ + + memset(&cf_send, 0, sizeof(cf_send)); + cf_send.can_id = MY_ID + 1; + cf_send.can_dlc = 1; + cf_send.data[0] = 0xde; + cf_send.data[1] = 0xad; + cf_send.data[2] = 0xbe; + cf_send.data[3] = 0xef; + + if (rump_sys_write(s, &cf_send, sizeof(cf_send) - 7) < 0) { + atf_tc_fail_errno("write"); + } + + if (can_read(s, &cf_receive, &rv) < 0) { + atf_tc_fail_errno("read"); + } + + ATF_CHECK_MSG(rv > 0, "short read on socket"); + + memset(&cf_send, 0, sizeof(cf_send)); + cf_send.can_id = MY_ID + 1; + cf_send.can_dlc = 1; + cf_send.data[0] = 0xde; + /* other data[] are expected to be 0 */ + + ATF_CHECK_MSG(memcmp(&cf_send, &cf_receive, sizeof(cf_send)) == 0, + "received packet is not what we sent"); + + /* now send a packet with MY_ID + 2. Should not pass */ + + memset(&cf_send, 0, sizeof(cf_send)); + cf_send.can_id = MY_ID + 2; + cf_send.can_dlc = 1; + cf_send.data[0] = 0xde; + cf_send.data[1] = 0xad; + cf_send.data[2] = 0xbe; + cf_send.data[3] = 0xef; + + if (rump_sys_write(s, &cf_send, sizeof(cf_send) - 7) < 0) { + atf_tc_fail_errno("write"); + } + + if (can_read(s, &cf_receive, &rv) < 0) { + if (errno == EWOULDBLOCK) + return; /* expected timeout */ + atf_tc_fail_errno("read"); + } + + ATF_CHECK_MSG(rv > 0, "short read on socket"); + + memset(&cf_send, 0, sizeof(cf_send)); + cf_send.can_id = MY_ID + 2; + cf_send.can_dlc = 1; + cf_send.data[0] = 0xde; + /* other data[] are expected to be 0 */ + + ATF_CHECK_MSG(memcmp(&cf_send, &cf_receive, sizeof(cf_send)) == 0, + "received packet is not what we sent"); + atf_tc_fail("we got our own message"); +#undef MY_ID +} + +ATF_TC(canfilter_get); +ATF_TC_HEAD(canfilter_get, tc) +{ + + atf_tc_set_md_var(tc, "descr", "check reading CAN filters"); + atf_tc_set_md_var(tc, "timeout", "5"); +} + +ATF_TC_BODY(canfilter_get, tc) +{ + const char ifname[] = "canlo0"; + int s; + struct can_filter cfi[2]; + socklen_t cfilen; + + rump_init(); + cancfg_rump_createif(ifname); + + s = -1; + if ((s = rump_sys_socket(AF_CAN, SOCK_RAW, CAN_RAW)) < 0) { + atf_tc_fail_errno("CAN socket"); + } + + cfilen = sizeof(cfi); + /* get filter: should be default filter */ + if (rump_sys_getsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, + &cfi, &cfilen) < 0) { + atf_tc_fail_errno("getsockopt(CAN_RAW_FILTER)"); + } + ATF_CHECK_MSG(cfilen == sizeof(struct can_filter), + "CAN_RAW_FILTER returns wrong len (%d)", cfilen); + ATF_CHECK_MSG(cfi[0].can_id == 0 && cfi[0].can_mask == 0, + "CAN_RAW_FILTER returns wrong filter (%d, %d)", + cfi[0].can_id, cfi[0].can_mask); + + /* set filter: accept MY_ID and MY_ID+1 */ +#define MY_ID 1 + cfi[0].can_id = MY_ID; + cfi[0].can_mask = CAN_SFF_MASK | CAN_EFF_FLAG; + cfi[1].can_id = MY_ID + 1; + cfi[1].can_mask = CAN_SFF_MASK; + if (rump_sys_setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, + &cfi, sizeof(cfi)) < 0) { + atf_tc_fail_errno("setsockopt(CAN_RAW_FILTER)"); + } + + /* and read back */ + cfilen = sizeof(cfi); + memset(cfi, 0, cfilen); + if (rump_sys_getsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, + &cfi, &cfilen) < 0) { + atf_tc_fail_errno("getsockopt(CAN_RAW_FILTER)"); + } + ATF_CHECK_MSG(cfilen == sizeof(struct can_filter) * 2, + "CAN_RAW_FILTER returns wrong len (%d)", cfilen); + ATF_CHECK_MSG(cfi[0].can_id == MY_ID && + cfi[0].can_mask == (CAN_SFF_MASK | CAN_EFF_FLAG), + "CAN_RAW_FILTER returns wrong filter 0 (%d, %d)", + cfi[0].can_id, cfi[0].can_mask); + ATF_CHECK_MSG(cfi[1].can_id == MY_ID + 1 && + cfi[1].can_mask == CAN_SFF_MASK, + "CAN_RAW_FILTER returns wrong filter 1 (%d, %d)", + cfi[1].can_id, cfi[1].can_mask); + +#undef MY_ID +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, canfilter_basic); + ATF_TP_ADD_TC(tp, canfilter_null); + ATF_TP_ADD_TC(tp, canfilter_multiple); + ATF_TP_ADD_TC(tp, canfilter_get); + return atf_no_error(); +} + diff --git a/net/carp/Makefile b/net/carp/Makefile --- a/net/carp/Makefile +++ b/net/carp/Makefile @@ -1,13 +1,13 @@ -# $NetBSD: Makefile,v 1.5 2016/08/08 14:40:19 pgoyette Exp $ +# $NetBSD: Makefile,v 1.6 2017/01/16 08:18:11 ozaki-r Exp $ # .include TESTSDIR= ${TESTSBASE}/net/carp -TESTS_C= t_basic - -LDADD+= -lrumpnet_shmif -lrumpnet_netinet -lrumpnet_net -lrumpdev -LDADD+= -lrumpnet -lrump -lrumpuser -lrumpvfs -lpthread +.for name in basic +TESTS_SH+= t_${name} +TESTS_SH_SRC_t_${name}= ../net_common.sh t_${name}.sh +.endfor .include diff --git a/net/carp/t_basic.sh b/net/carp/t_basic.sh old mode 100755 new mode 100644 --- a/net/carp/t_basic.sh +++ b/net/carp/t_basic.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_basic.sh,v 1.1 2017/01/16 08:18:11 ozaki-r Exp $ +# $NetBSD: t_basic.sh,v 1.8 2019/08/19 03:22:05 ozaki-r Exp $ # # Copyright (c) 2017 Internet Initiative Japan Inc. # All rights reserved. @@ -29,25 +29,20 @@ SOCK_MASTER=unix://carp_master SOCK_BACKUP=unix://carp_backup BUS=bus_carp +TIMEOUT=3 + +DEBUG=${DEBUG:-false} + IP_CLIENT=10.1.1.240 IP_MASTER=10.1.1.1 IP_BACKUP=10.1.1.2 IP_CARP=10.1.1.100 -TIMEOUT=3 - -atf_test_case carp_handover cleanup - -carp_handover_head() -{ - - atf_set "descr" "Tests for CARP handover" - atf_set "require.progs" "rump_server" -} setup_carp() { local sock=$1 local master=$2 + local carpdevip=$3 local carpif= ip= advskew= if $master; then @@ -61,11 +56,22 @@ fi export RUMP_SERVER=$sock - atf_check -s exit:0 rump.ifconfig $carpif create - atf_check -s exit:0 rump.ifconfig shmif0 $ip/24 up - atf_check -s exit:0 rump.ifconfig $carpif \ - vhid 175 advskew $advskew advbase 1 pass s3cret \ - $IP_CARP netmask 255.255.255.0 + if $DEBUG; then + atf_check -s exit:0 -o match:'0.->.1' \ + rump.sysctl -w net.inet.carp.log=1 + fi + rump_server_add_iface $sock $carpif + if [ $carpdevip = yes ]; then + atf_check -s exit:0 rump.ifconfig shmif0 $ip/24 up + atf_check -s exit:0 rump.ifconfig $carpif \ + vhid 175 advskew $advskew advbase 1 pass s3cret \ + $IP_CARP netmask 255.255.255.0 + else + atf_check -s exit:0 rump.ifconfig shmif0 up + atf_check -s exit:0 rump.ifconfig $carpif \ + vhid 175 advskew $advskew advbase 1 pass s3cret \ + carpdev shmif0 $IP_CARP netmask 255.255.255.0 + fi atf_check -s exit:0 rump.ifconfig -w 10 } @@ -91,8 +97,10 @@ fi } -carp_handover_body() +test_carp_handover_ipv4() { + local op=$1 + local carpdevip=$2 rump_server_start $SOCK_CLIENT rump_server_start $SOCK_MASTER @@ -102,18 +110,20 @@ rump_server_add_iface $SOCK_MASTER shmif0 $BUS rump_server_add_iface $SOCK_BACKUP shmif0 $BUS - setup_carp $SOCK_MASTER true - setup_carp $SOCK_BACKUP false + setup_carp $SOCK_MASTER true $carpdevip + setup_carp $SOCK_BACKUP false $carpdevip export RUMP_SERVER=$SOCK_CLIENT atf_check -s exit:0 rump.ifconfig shmif0 $IP_CLIENT/24 up atf_check -s exit:0 rump.ifconfig -w 10 - # Check that the primary addresses are up - atf_check -s exit:0 -o ignore \ - rump.ping -n -w $TIMEOUT -c 1 $IP_MASTER - atf_check -s exit:0 -o ignore \ - rump.ping -n -w $TIMEOUT -c 1 $IP_BACKUP + if [ $carpdevip = yes ]; then + # Check that the primary addresses are up + atf_check -s exit:0 -o ignore \ + rump.ping -n -w $TIMEOUT -c 1 $IP_MASTER + atf_check -s exit:0 -o ignore \ + rump.ping -n -w $TIMEOUT -c 1 $IP_BACKUP + fi # Give carp a while to croak sleep 4 @@ -134,12 +144,20 @@ rump.ping -n -w $TIMEOUT -c 1 $IP_CARP # KILLING SPREE - env RUMP_SERVER=$SOCK_MASTER rump.halt + if [ $op = halt ]; then + env RUMP_SERVER=$SOCK_MASTER rump.halt + elif [ $op = ifdown ]; then + env RUMP_SERVER=$SOCK_MASTER rump.ifconfig shmif0 down + fi sleep 1 # Check that primary is now dead - atf_check -s not-exit:0 -o ignore \ - rump.ping -n -w $TIMEOUT -c 1 $IP_MASTER + if [ $carpdevip = yes ]; then + atf_check -s not-exit:0 -o ignore \ + rump.ping -n -w $TIMEOUT -c 1 $IP_MASTER + else + # XXX how to check? + fi # Do it in installments. carp will cluck meanwhile wait_handover @@ -149,17 +167,201 @@ $DEBUG && rump.ifconfig atf_check -s exit:0 -o match:'carp: MASTER carpdev shmif0' \ rump.ifconfig carp1 + + if [ $op = ifdown ]; then + rump_server_destroy_ifaces + fi +} + +IP6_CLIENT=fd00:1::240 +IP6_MASTER=fd00:1::1 +IP6_BACKUP=fd00:1::2 +IP6_CARP=fd00:1::100 + +setup_carp6() +{ + local sock=$1 + local master=$2 + local carpdevip=$3 + local carpif= ip= advskew= + + if $master; then + carpif=carp0 + ip=$IP6_MASTER + advskew=0 + else + carpif=carp1 + ip=$IP6_BACKUP + advskew=200 + fi + + export RUMP_SERVER=$sock + if $DEBUG; then + atf_check -s exit:0 -o match:'0.->.1' \ + rump.sysctl -w net.inet.carp.log=1 + fi + rump_server_add_iface $sock $carpif + if [ $carpdevip = yes ]; then + atf_check -s exit:0 rump.ifconfig shmif0 inet6 $ip up + atf_check -s exit:0 rump.ifconfig $carpif inet6 \ + vhid 175 advskew $advskew advbase 1 pass s3cret $IP6_CARP + else + atf_check -s exit:0 rump.ifconfig shmif0 up + atf_check -s exit:0 rump.ifconfig $carpif inet6 \ + vhid 175 advskew $advskew advbase 1 pass s3cret \ + carpdev shmif0 $IP6_CARP + fi + atf_check -s exit:0 rump.ifconfig -w 10 +} + +wait_carp6_handover() +{ + local i=0 + + export RUMP_SERVER=$SOCK_CLIENT + + while [ $i -ne 5 ]; do + $DEBUG && echo "Trying ping6 $IP6_CARP" + rump.ping6 -n -X 1 -c 1 $IP6_CARP >/dev/null + if [ $? = 0 ]; then + $DEBUG && echo "Passed ping $IP6_CARP" + break; + fi + $DEBUG && echo "Failed ping6 $IP6_CARP" + i=$((i + 1)) + done + + if [ $i -eq 5 ]; then + atf_fail "Failed to failover (5 sec)" + fi +} + +test_carp_handover_ipv6() +{ + local op=$1 + local carpdevip=$2 + + rump_server_start $SOCK_CLIENT netinet6 + rump_server_start $SOCK_MASTER netinet6 + rump_server_start $SOCK_BACKUP netinet6 + + rump_server_add_iface $SOCK_CLIENT shmif0 $BUS + rump_server_add_iface $SOCK_MASTER shmif0 $BUS + rump_server_add_iface $SOCK_BACKUP shmif0 $BUS + + setup_carp6 $SOCK_MASTER true $carpdevip + setup_carp6 $SOCK_BACKUP false $carpdevip + + export RUMP_SERVER=$SOCK_CLIENT + atf_check -s exit:0 rump.ifconfig shmif0 inet6 $IP6_CLIENT up + atf_check -s exit:0 rump.ifconfig -w 10 + + if [ $carpdevip = yes ]; then + # Check that the primary addresses are up + atf_check -s exit:0 -o ignore \ + rump.ping6 -n -X $TIMEOUT -c 1 $IP6_MASTER + atf_check -s exit:0 -o ignore \ + rump.ping6 -n -X $TIMEOUT -c 1 $IP6_BACKUP + fi + + # Give carp a while to croak + sleep 4 + + # Check state + export RUMP_SERVER=$SOCK_MASTER + $DEBUG && rump.ifconfig + atf_check -s exit:0 -o match:'carp: MASTER carpdev shmif0' \ + rump.ifconfig carp0 + export RUMP_SERVER=$SOCK_BACKUP + $DEBUG && rump.ifconfig + atf_check -s exit:0 -o match:'carp: BACKUP carpdev shmif0' \ + rump.ifconfig carp1 + export RUMP_SERVER=$SOCK_CLIENT + + # Check that the shared IP works + atf_check -s exit:0 -o ignore \ + rump.ping6 -n -X $TIMEOUT -c 1 $IP6_CARP + + # KILLING SPREE + if [ $op = halt ]; then + env RUMP_SERVER=$SOCK_MASTER rump.halt + elif [ $op = ifdown ]; then + env RUMP_SERVER=$SOCK_MASTER rump.ifconfig shmif0 down + fi + sleep 1 + + # Check that primary is now dead + if [ $carpdevip = yes ]; then + atf_check -s not-exit:0 -o ignore \ + rump.ping6 -n -X $TIMEOUT -c 1 $IP6_MASTER + else + # XXX how to check? + fi + + # Do it in installments. carp will cluck meanwhile + wait_carp6_handover + + # Check state + export RUMP_SERVER=$SOCK_BACKUP + $DEBUG && rump.ifconfig + atf_check -s exit:0 -o match:'carp: MASTER carpdev shmif0' \ + rump.ifconfig carp1 + + if [ $op = ifdown ]; then + rump_server_destroy_ifaces + fi } -carp_handover_cleanup() +add_test_case() { + local ipproto=$1 + local halt=$2 + local carpdevip=$3 + local expected_failure_code= - $DEBUG && dump - cleanup + name="carp_handover_${ipproto}_${halt}" + desc="Tests for CARP (${ipproto}) handover on ${halt}" + if [ $carpdevip = yes ]; then + name="${name}_carpdevip" + desc="$desc with carpdev IP" + else + name="${name}_nocarpdevip" + desc="$desc without carpdev IP" + fi + if [ $ipproto = ipv6 -a $carpdevip = no ]; then + expected_failure_code="atf_expect_fail 'nd6 needs to be fixed';" + fi + + atf_test_case ${name} cleanup + eval " + ${name}_head() { + atf_set descr \"$desc\" + atf_set require.progs rump_server + } + ${name}_body() { + $expected_failure_code + test_carp_handover_${ipproto} $halt $carpdevip + if [ $halt != halt ]; then + rump_server_destroy_ifaces + fi + } + ${name}_cleanup() { + \$DEBUG && dump + cleanup + } + " + atf_add_test_case ${name} } atf_init_test_cases() { + local proto= halt= carpdevip= - atf_add_test_case carp_handover + for proto in ipv4 ipv6; do + for halt in halt ifdown; do + for carpdevip in yes no; do + add_test_case $proto $halt $carpdevip + done + done + done } diff --git a/net/config/netconfig.c b/net/config/netconfig.c --- a/net/config/netconfig.c +++ b/net/config/netconfig.c @@ -1,4 +1,4 @@ -/* $NetBSD: netconfig.c,v 1.9 2017/01/13 21:30:42 christos Exp $ */ +/* $NetBSD: netconfig.c,v 1.10 2020/04/23 00:31:51 joerg Exp $ */ /*- * Copyright (c) 2010 The NetBSD Foundation, Inc. @@ -29,7 +29,7 @@ #include #ifndef lint -__RCSID("$NetBSD: netconfig.c,v 1.9 2017/01/13 21:30:42 christos Exp $"); +__RCSID("$NetBSD: netconfig.c,v 1.10 2020/04/23 00:31:51 joerg Exp $"); #endif /* not lint */ #include @@ -55,7 +55,6 @@ #include "h_macros.h" -int noatf; static void __unused netcfg_rump_makeshmif(const char *busname, char *ifname) @@ -63,10 +62,11 @@ int rv, ifnum; if ((rv = rump_pub_shmif_create(busname, &ifnum)) != 0) { - if (noatf) - err(1, "makeshmif: rump_pub_shmif_create %d", rv); - else - atf_tc_fail("makeshmif: rump_pub_shmif_create %d", rv); +#ifdef RUMPNFSD_NOATF + err(1, "makeshmif: rump_pub_shmif_create %d", rv); +#else + atf_tc_fail("makeshmif: rump_pub_shmif_create %d", rv); +#endif } sprintf(ifname, "shmif%d", ifnum); } @@ -81,10 +81,11 @@ s = -1; if ((s = rump_sys_socket(PF_INET, SOCK_DGRAM, 0)) < 0) { - if (noatf) - err(1, "if config socket"); - else - atf_tc_fail_errno("if config socket"); +#ifdef RUMPNFSD_NOATF + err(1, "if config socket"); +#else + atf_tc_fail_errno("if config socket"); +#endif } inaddr = inet_addr(addr); @@ -112,10 +113,11 @@ rv = rump_sys_ioctl(s, SIOCAIFADDR, &ia); if (rv == -1) { - if (noatf) - err(1, "SIOCAIFADDR"); - else - atf_tc_fail_errno("SIOCAIFADDR"); +#ifdef RUMPNFSD_NOATF + err(1, "SIOCAIFADDR"); +#else + atf_tc_fail_errno("SIOCAIFADDR"); +#endif } rump_sys_close(s); } @@ -135,10 +137,11 @@ s = rump_sys_socket(PF_ROUTE, SOCK_RAW, 0); if (s == -1) { - if (noatf) - err(1, "routing socket"); - else - atf_tc_fail_errno("routing socket"); +#ifdef RUMPNFSD_NOATF + err(1, "routing socket"); +#else + atf_tc_fail_errno("routing socket"); +#endif } memset(&m_rtmsg, 0, sizeof(m_rtmsg)); @@ -177,10 +180,11 @@ rv = rump_sys_write(s, &m_rtmsg, len); if (rv != (int)len) { - if (noatf) - err(1, "write routing message"); - else - atf_tc_fail_errno("write routing message"); +#ifdef RUMPNFSD_NOATF + err(1, "write routing message"); +#else + atf_tc_fail_errno("write routing message"); +#endif } rump_sys_close(s); } diff --git a/net/fdpass/t_fdpass.sh b/net/fdpass/t_fdpass.sh old mode 100755 new mode 100644 diff --git a/net/icmp/Makefile b/net/icmp/Makefile --- a/net/icmp/Makefile +++ b/net/icmp/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.9 2016/11/24 09:06:09 ozaki-r Exp $ +# $NetBSD: Makefile,v 1.12 2020/03/01 18:08:16 christos Exp $ # .include @@ -14,6 +14,6 @@ .endfor LDADD+= -lrumpnet_shmif -lrumpnet_netinet -lrumpnet_net -lrumpnet -LDADD+= -lrump -lrumpuser -lrump -lpthread -lrumpdev -lrumpvfs +LDADD+= ${LIBRUMPBASE} .include diff --git a/net/icmp/t_icmp6_redirect.sh b/net/icmp/t_icmp6_redirect.sh old mode 100755 new mode 100644 --- a/net/icmp/t_icmp6_redirect.sh +++ b/net/icmp/t_icmp6_redirect.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_icmp6_redirect.sh,v 1.7 2016/11/25 08:51:16 ozaki-r Exp $ +# $NetBSD: t_icmp6_redirect.sh,v 1.8 2017/05/26 01:14:38 ozaki-r Exp $ # # Copyright (c) 2015 Internet Initiative Japan Inc. # All rights reserved. @@ -42,7 +42,7 @@ REDIRECT_TIMEOUT=5 -DEBUG=${DEBUG:-true} +DEBUG=${DEBUG:-false} atf_test_case icmp6_redirect_basic cleanup diff --git a/net/icmp/t_icmp_redirect.sh b/net/icmp/t_icmp_redirect.sh old mode 100755 new mode 100644 diff --git a/net/icmp/t_ping.c b/net/icmp/t_ping.c --- a/net/icmp/t_ping.c +++ b/net/icmp/t_ping.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_ping.c,v 1.17 2017/01/13 21:30:42 christos Exp $ */ +/* $NetBSD: t_ping.c,v 1.24 2019/06/11 08:34:01 gson Exp $ */ /*- * Copyright (c) 2010 The NetBSD Foundation, Inc. @@ -29,7 +29,7 @@ #include #ifndef lint -__RCSID("$NetBSD: t_ping.c,v 1.17 2017/01/13 21:30:42 christos Exp $"); +__RCSID("$NetBSD: t_ping.c,v 1.24 2019/06/11 08:34:01 gson Exp $"); #endif /* not lint */ #include @@ -182,17 +182,23 @@ icmp->icmp_seq = htons(loop); icmp->icmp_cksum = 0; icmp->icmp_cksum = in_cksum(icmp, pktsize); - RL(rump_sys_sendto(s, icmp, pktsize, 0, - (struct sockaddr *)&dst, sizeof(dst))); + + n = rump_sys_sendto(s, icmp, pktsize, 0, + (struct sockaddr *)&dst, sizeof(dst)); + if (n == -1) { + if (errno == ENOBUFS) + continue; + atf_tc_fail_errno("sendto failed"); + } RL(rump_sys_fcntl(s, F_SETFL, xnon)); while ((n = rump_sys_recvfrom(s, recvbuf, sizeof(recvbuf), 0, (struct sockaddr *)&pingee, &slen)) > 0) { succ++; } - if (n == -1 && errno == EAGAIN) + if (n == -1 && (errno == EAGAIN || errno == ENOBUFS)) continue; - atf_tc_fail_errno("recv failed"); + atf_tc_fail_errno("recv failed (n == %zd)", n); } rump_sys_close(s); @@ -274,7 +280,7 @@ { char ifname[IFNAMSIZ]; pid_t cpid; - int succ, i; + int sent, succ, i; cpid = fork(); rump_init(); @@ -293,21 +299,27 @@ netcfg_rump_if(ifname, "1.1.1.20", "255.255.255.0"); - succ = 0; + succ = sent = 0; /* small sizes */ - for (i = 0 ; i < IP_MAXPACKET - 60000; i++) + for (i = 0 ; i < IP_MAXPACKET - 60000; i++) { + sent++; succ += doping("1.1.1.10", 1, i); + } /* medium sizes */ - for (i = IP_MAXPACKET - 60000; i < IP_MAXPACKET - 100; i += 1000) + for (i = IP_MAXPACKET - 60000; i < IP_MAXPACKET - 100; i += 1000) { + sent++; succ += doping("1.1.1.10", 1, i); + } /* big sizes */ - for (i = IP_MAXPACKET - 100; i < IP_MAXPACKET; i += 10) + for (i = IP_MAXPACKET - 100; i < IP_MAXPACKET; i += 10) { + sent++; succ += doping("1.1.1.10", 1, i); + } - printf("got %d/%d\n", succ, IP_MAXPACKET); + printf("got %d/%d\n", succ, sent); kill(cpid, SIGKILL); } @@ -329,6 +341,7 @@ pid_t cpid; size_t tot, frag; int s, x, loop; + ssize_t error; cpid = fork(); rump_init(); @@ -412,15 +425,22 @@ ip->ip_off |= IP_MF; } - RL(rump_sys_sendto(s, data, frag, 0, - (struct sockaddr *)&dst, sizeof(dst))); + error = rump_sys_sendto(s, data, frag, 0, + (struct sockaddr *)&dst, sizeof(dst)); + if (error == -1) { + if (errno == ENOBUFS) + continue; + atf_tc_fail_errno("sendto failed"); + } + if ((size_t)error != frag) + atf_tc_fail("sendto did not write all data"); } if (waitpid(-1, &status, WNOHANG) > 0) { if (WIFEXITED(status) && WEXITSTATUS(status) == 0) break; atf_tc_fail("child did not exit clean"); } - + usleep(10000); } } diff --git a/net/icmp/t_ping2.sh b/net/icmp/t_ping2.sh old mode 100755 new mode 100644 --- a/net/icmp/t_ping2.sh +++ b/net/icmp/t_ping2.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_ping2.sh,v 1.5 2016/08/10 22:17:44 kre Exp $ +# $NetBSD: t_ping2.sh,v 1.6 2019/05/13 17:55:08 bad Exp $ # # Copyright (c) 2010 The NetBSD Foundation, Inc. # All rights reserved. @@ -26,7 +26,7 @@ # netserver=\ -"rump_server -lrumpnet -lrumpnet_net -lrumpnet_netinet -lrumpnet_shmif -lrumpdev" +"rump_server -lrumpnet -lrumpnet_net -lrumpnet_netinet -lrumpnet_shmif" atf_test_case basic cleanup basic_head() diff --git a/net/if/Makefile b/net/if/Makefile --- a/net/if/Makefile +++ b/net/if/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.6 2016/08/08 14:54:27 pgoyette Exp $ +# $NetBSD: Makefile,v 1.10 2020/03/01 18:08:16 christos Exp $ # .include @@ -6,14 +6,16 @@ TESTSDIR= ${TESTSBASE}/net/if TESTS_C= t_compat -TESTS_SH= t_ifconf -TESTS_SH+= t_ifconfig +.for name in ifconf ifconfig +TESTS_SH+= t_${name} +TESTS_SH_SRC_t_${name}= ../net_common.sh t_${name}.sh +.endfor PROGS= ifconf MAN.ifconf= # empty BINDIR.ifconf= ${TESTSDIR} -LDADD.t_compat= -lrumpnet_shmif -lrumpnet_netinet -lrumpnet_net -lrumpnet -lrump -LDADD.t_compat+= -lrumpuser -lrump -lpthread -lrumpdev -lrumpvfs +LDADD.t_compat= -lrumpnet_shmif -lrumpnet_netinet -lrumpnet_net -lrumpnet +LDADD.t_compat+= ${LIBRUMPBASE} .include diff --git a/net/if/t_ifconf.sh b/net/if/t_ifconf.sh old mode 100755 new mode 100644 --- a/net/if/t_ifconf.sh +++ b/net/if/t_ifconf.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_ifconf.sh,v 1.3 2016/08/10 22:30:02 kre Exp $ +# $NetBSD: t_ifconf.sh,v 1.4 2019/05/13 17:55:08 bad Exp $ # # Copyright (c) 2014 The NetBSD Foundation, Inc. # All rights reserved. @@ -28,7 +28,7 @@ RUMP_SERVER1=unix://./r1 RUMP_FLAGS=\ -"-lrumpnet -lrumpnet_net -lrumpnet_netinet -lrumpnet_shmif -lrumpdev" +"-lrumpnet -lrumpnet_net -lrumpnet_netinet -lrumpnet_shmif" atf_test_case basic cleanup basic_head() diff --git a/net/if/t_ifconfig.sh b/net/if/t_ifconfig.sh old mode 100755 new mode 100644 --- a/net/if/t_ifconfig.sh +++ b/net/if/t_ifconfig.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_ifconfig.sh,v 1.15 2017/01/20 08:35:33 ozaki-r Exp $ +# $NetBSD: t_ifconfig.sh,v 1.22 2021/08/17 22:00:33 andvar Exp $ # # Copyright (c) 2015 The NetBSD Foundation, Inc. # All rights reserved. @@ -30,7 +30,7 @@ RUMP_FLAGS=\ "-lrumpnet -lrumpnet_net -lrumpnet_netinet -lrumpnet_netinet6 -lrumpnet_shmif" -RUMP_FLAGS="${RUMP_FLAGS} -lrumpdev" +RUMP_FLAGS="${RUMP_FLAGS}" TIMEOUT=3 @@ -69,6 +69,11 @@ atf_check -s exit:0 rump.ifconfig shmif0 up atf_check -s exit:0 rump.ifconfig shmif0 destroy + # Check if ifconfig (ioctl) works after a failure of ifconfig destroy + atf_check -s exit:0 -o ignore rump.ifconfig lo0 + atf_check -s not-exit:0 -e ignore rump.ifconfig lo0 destroy + atf_check -s exit:0 -o ignore rump.ifconfig lo0 + unset RUMP_SERVER } @@ -163,7 +168,7 @@ atf_check -s exit:0 -o ignore rump.ifconfig shmif0 up # ifconfig -l [-bdsu] - # -l shows only inteface names + # -l shows only interface names atf_check -s exit:0 -o match:'lo0' rump.ifconfig -l atf_check -s exit:0 -o match:'shmif0' rump.ifconfig -l atf_check -s exit:0 -o match:'shmif0' rump.ifconfig -l -b @@ -324,10 +329,202 @@ env RUMP_SERVER=${RUMP_SERVER2} rump.halt } +ifconfig_up_down_common() +{ + local family=$1 + local ip=$2 + + if [ $family = inet6 ]; then + rump_server_start $RUMP_SERVER1 netinet6 + else + rump_server_start $RUMP_SERVER1 + fi + rump_server_add_iface $RUMP_SERVER1 shmif0 bus1 + + export RUMP_SERVER=$RUMP_SERVER1 + rump.ifconfig shmif0 + + # Set the same number of trials to make the following test + # work for both IPv4 and IPv6 + if [ $family = inet6 ]; then + atf_check -s exit:0 -o ignore \ + rump.sysctl -w net.inet6.ip6.dad_count=5 + else + atf_check -s exit:0 -o ignore \ + rump.sysctl -w net.inet.ip.dad_count=5 + fi + + # + # Assign an address and up the interface at once + # + atf_check -s exit:0 rump.ifconfig shmif0 $family $ip/24 up + # UP + atf_check -s exit:0 \ + -o match:'shmif0.*UP.*RUNNING' rump.ifconfig shmif0 + # The address is TENTATIVE + atf_check -s exit:0 \ + -o match:"$ip.*TENTATIVE" rump.ifconfig shmif0 + # Waiting for DAD completion + atf_check -s exit:0 rump.ifconfig -w 10 + # The address left TENTATIVE + atf_check -s exit:0 \ + -o not-match:"$ip.*TENTATIVE" rump.ifconfig shmif0 + + # + # ifconfig down + # + atf_check -s exit:0 rump.ifconfig shmif0 down + atf_check -s exit:0 \ + -o not-match:'shmif0.*UP.*RUNNING' rump.ifconfig shmif0 + # The address becomes DETATCHED + atf_check -s exit:0 \ + -o match:"$ip.*DETACHED" rump.ifconfig shmif0 + # ifconfig up + atf_check -s exit:0 rump.ifconfig shmif0 up + # The address becomes TENTATIVE + atf_check -s exit:0 \ + -o match:"$ip.*TENTATIVE" rump.ifconfig shmif0 + # Waiting for DAD completion + atf_check -s exit:0 rump.ifconfig -w 10 + # The address left TENTATIVE + atf_check -s exit:0 \ + -o not-match:"$ip.*TENTATIVE" rump.ifconfig shmif0 + + # Clean up + atf_check -s exit:0 rump.ifconfig shmif0 $family $ip delete + + # + # Assign an address + # + atf_check -s exit:0 rump.ifconfig shmif0 $family $ip/24 + # UP automatically + atf_check -s exit:0 \ + -o match:'shmif0.*UP.*RUNNING' rump.ifconfig shmif0 + # Need some delay + sleep 1 + # The IP becomes TENTATIVE + atf_check -s exit:0 \ + -o match:"$ip.*TENTATIVE" rump.ifconfig shmif0 + # Waiting for DAD completion + atf_check -s exit:0 rump.ifconfig -w 10 + # The address left TENTATIVE + atf_check -s exit:0 \ + -o not-match:"$ip.*TENTATIVE" rump.ifconfig shmif0 + + rump_server_destroy_ifaces +} + +atf_test_case ifconfig_up_down_ipv4 cleanup +ifconfig_up_down_ipv4_head() +{ + atf_set "descr" "tests of interface up/down (IPv4)" + atf_set "require.progs" "rump_server" +} + +ifconfig_up_down_ipv4_body() +{ + + ifconfig_up_down_common inet 10.0.0.1 +} + +ifconfig_up_down_ipv4_cleanup() +{ + + $DEBUG && dump + cleanup +} + +atf_test_case ifconfig_up_down_ipv6 cleanup +ifconfig_up_down_ipv6_head() +{ + atf_set "descr" "tests of interface up/down (IPv6)" + atf_set "require.progs" "rump_server" +} + +ifconfig_up_down_ipv6_body() +{ + + ifconfig_up_down_common inet6 fc00::1 +} + +ifconfig_up_down_ipv6_cleanup() +{ + + $DEBUG && dump + cleanup +} + +atf_test_case ifconfig_number cleanup +ifconfig_number_head() +{ + atf_set "descr" "tests of passing a number (if_index) to ifconfig" + atf_set "require.progs" "rump_server" +} + +ifconfig_number_body() +{ + + rump_server_start $RUMP_SERVER1 + rump_server_add_iface $RUMP_SERVER1 shmif0 bus1 + + export RUMP_SERVER=$RUMP_SERVER1 + atf_check -s not-exit:0 -e match:'Device not configured' rump.ifconfig 0 + atf_check -s exit:0 rump.ifconfig 1 # lo0 + atf_check -s exit:0 rump.ifconfig 2 # shmif0 + atf_check -s not-exit:0 -e match:'Device not configured' rump.ifconfig 3 + + rump_server_destroy_ifaces +} + +ifconfig_number_cleanup() +{ + + $DEBUG && dump + cleanup +} + +atf_test_case ifconfig_description cleanup +ifconfig_description_head() +{ + atf_set "descr" "tests of setting and unsetting interface description" + atf_set "require.progs" "rump_server" +} + +ifconfig_description_body() +{ + + rump_server_start $RUMP_SERVER1 + + export RUMP_SERVER=$RUMP_SERVER1 + for descr in description descr; do + atf_check -s exit:0 rump.ifconfig lo0 $descr DESCRIPTION-TEST + atf_check -s exit:0 -o match:"DESCRIPTION-TEST" rump.ifconfig lo0 + atf_check -s exit:0 rump.ifconfig lo0 $descr DESCRIPTION-TEST-MODIFIED + atf_check -s exit:0 -o match:"DESCRIPTION-TEST-MODIFIED" rump.ifconfig lo0 + atf_check -s exit:0 rump.ifconfig lo0 -$descr + atf_check -s exit:0 -o not-match:'DESCRIPTION-TEST-MODIFIED' rump.ifconfig lo0 + + atf_check -s exit:0 rump.ifconfig lo0 $descr `printf "%063d" 0` + atf_check -s not-exit:0 -e match:"description too long" rump.ifconfig lo0 $descr `printf "%064d" 0` + atf_check -s exit:0 rump.ifconfig lo0 $descr "" + done +} + +ifconfig_description_cleanup() +{ + + $DEBUG && dump + cleanup +} + atf_init_test_cases() { atf_add_test_case ifconfig_create_destroy atf_add_test_case ifconfig_options atf_add_test_case ifconfig_parameters + atf_add_test_case ifconfig_up_down_ipv4 + atf_add_test_case ifconfig_up_down_ipv6 + atf_add_test_case ifconfig_number + atf_add_test_case ifconfig_description } diff --git a/net/if_bridge/Makefile b/net/if_bridge/Makefile --- a/net/if_bridge/Makefile +++ b/net/if_bridge/Makefile @@ -1,11 +1,11 @@ -# $NetBSD: Makefile,v 1.2 2016/11/24 09:07:09 ozaki-r Exp $ +# $NetBSD: Makefile,v 1.3 2017/03/11 04:24:52 ozaki-r Exp $ # .include TESTSDIR= ${TESTSBASE}/net/if_bridge -.for name in bridge +.for name in bridge rtable TESTS_SH+= t_${name} TESTS_SH_SRC_t_${name}= ../net_common.sh t_${name}.sh .endfor diff --git a/net/if_bridge/t_bridge.sh b/net/if_bridge/t_bridge.sh old mode 100755 new mode 100644 --- a/net/if_bridge/t_bridge.sh +++ b/net/if_bridge/t_bridge.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_bridge.sh,v 1.16 2016/11/25 08:51:16 ozaki-r Exp $ +# $NetBSD: t_bridge.sh,v 1.19 2019/08/19 03:22:05 ozaki-r Exp $ # # Copyright (c) 2014 The NetBSD Foundation, Inc. # All rights reserved. @@ -40,27 +40,28 @@ DEBUG=${DEBUG:-false} TIMEOUT=5 +atf_test_case bridge_create_destroy cleanup atf_test_case bridge_ipv4 cleanup atf_test_case bridge_ipv6 cleanup -atf_test_case bridge_rtable cleanup atf_test_case bridge_member_ipv4 cleanup atf_test_case bridge_member_ipv6 cleanup -bridge_ipv4_head() +bridge_create_destroy_head() { - atf_set "descr" "Does simple if_bridge tests" + + atf_set "descr" "Test creating/destroying bridge interfaces" atf_set "require.progs" "rump_server" } -bridge_ipv6_head() +bridge_ipv4_head() { - atf_set "descr" "Does simple if_bridge tests (IPv6)" + atf_set "descr" "Does simple if_bridge tests" atf_set "require.progs" "rump_server" } -bridge_rtable_head() +bridge_ipv6_head() { - atf_set "descr" "Tests route table operations of if_bridge" + atf_set "descr" "Does simple if_bridge tests (IPv6)" atf_set "require.progs" "rump_server" } @@ -168,7 +169,7 @@ setup_bridge() { export RUMP_SERVER=$SOCK2 - atf_check -s exit:0 rump.ifconfig bridge0 create + rump_server_add_iface $SOCK2 bridge0 atf_check -s exit:0 rump.ifconfig bridge0 up export LD_PRELOAD=/usr/lib/librumphijack.so @@ -319,61 +320,12 @@ rump.ifconfig -v shmif0 } -get_number_of_caches() +bridge_create_destroy_body() { - export RUMP_SERVER=$SOCK2 - export LD_PRELOAD=/usr/lib/librumphijack.so - echo $(($(/sbin/brconfig bridge0 |grep -A 100 "Address cache" |wc -l) - 1)) - unset LD_PRELOAD -} - -test_brconfig_maxaddr() -{ - addr1= addr3= n= - - # Get MAC addresses of the endpoints. - addr1=$(get_macaddr $SOCK1 shmif0) - addr3=$(get_macaddr $SOCK3 shmif0) - - # Refill the MAC addresses of the endpoints. - export RUMP_SERVER=$SOCK1 - atf_check -s exit:0 -o ignore rump.ping -n -w $TIMEOUT -c 1 $IP2 - export RUMP_SERVER=$SOCK2 - export LD_PRELOAD=/usr/lib/librumphijack.so - /sbin/brconfig bridge0 - atf_check -s exit:0 -o match:"$addr1 shmif0" /sbin/brconfig bridge0 - atf_check -s exit:0 -o match:"$addr3 shmif1" /sbin/brconfig bridge0 - - # Check the default # of caches is 100 - atf_check -s exit:0 -o match:"max cache: 100" /sbin/brconfig bridge0 - - # Test two MAC addresses are cached - n=$(get_number_of_caches) - atf_check_equal $n 2 - - # Limit # of caches to one - atf_check -s exit:0 -o ignore /sbin/brconfig bridge0 maxaddr 1 - atf_check -s exit:0 -o match:"max cache: 1" /sbin/brconfig bridge0 - /sbin/brconfig bridge0 - - # Test just one address is cached - n=$(get_number_of_caches) - atf_check_equal $n 1 - # Increase # of caches to two - atf_check -s exit:0 -o ignore /sbin/brconfig bridge0 maxaddr 2 - atf_check -s exit:0 -o match:"max cache: 2" /sbin/brconfig bridge0 - unset LD_PRELOAD + rump_server_start $SOCK1 bridge - # Test we can cache two addresses again - export RUMP_SERVER=$SOCK1 - atf_check -s exit:0 -o ignore rump.ping -n -w $TIMEOUT -c 1 $IP2 - export RUMP_SERVER=$SOCK2 - export LD_PRELOAD=/usr/lib/librumphijack.so - /sbin/brconfig bridge0 - atf_check -s exit:0 -o match:"$addr1 shmif0" /sbin/brconfig bridge0 - atf_check -s exit:0 -o match:"$addr3 shmif1" /sbin/brconfig bridge0 - unset LD_PRELOAD + test_create_destroy_common $SOCK1 bridge0 } bridge_ipv4_body() @@ -413,78 +365,6 @@ rump_server_destroy_ifaces } -bridge_rtable_body() -{ - addr1= addr3= - - setup - setup_bridge - - # Get MAC addresses of the endpoints. - addr1=$(get_macaddr $SOCK1 shmif0) - addr3=$(get_macaddr $SOCK3 shmif0) - - # Confirm there is no MAC address caches. - export RUMP_SERVER=$SOCK2 - export LD_PRELOAD=/usr/lib/librumphijack.so - $DEBUG && /sbin/brconfig bridge0 - atf_check -s exit:0 -o not-match:"$addr1" /sbin/brconfig bridge0 - atf_check -s exit:0 -o not-match:"$addr3" /sbin/brconfig bridge0 - unset LD_PRELOAD - - # Make the bridge learn the MAC addresses of the endpoints. - export RUMP_SERVER=$SOCK1 - atf_check -s exit:0 -o ignore rump.ping -n -w $TIMEOUT -c 1 $IP2 - unset RUMP_SERVER - - # Tests the addresses are in the cache. - export RUMP_SERVER=$SOCK2 - export LD_PRELOAD=/usr/lib/librumphijack.so - $DEBUG && /sbin/brconfig bridge0 - atf_check -s exit:0 -o match:"$addr1 shmif0" /sbin/brconfig bridge0 - atf_check -s exit:0 -o match:"$addr3 shmif1" /sbin/brconfig bridge0 - - # Tests brconfig deladdr - atf_check -s exit:0 -o ignore /sbin/brconfig bridge0 deladdr "$addr1" - atf_check -s exit:0 -o not-match:"$addr1 shmif0" /sbin/brconfig bridge0 - atf_check -s exit:0 -o ignore /sbin/brconfig bridge0 deladdr "$addr3" - atf_check -s exit:0 -o not-match:"$addr3 shmif1" /sbin/brconfig bridge0 - unset LD_PRELOAD - - # Refill the MAC addresses of the endpoints. - export RUMP_SERVER=$SOCK1 - atf_check -s exit:0 -o ignore rump.ping -n -w $TIMEOUT -c 1 $IP2 - unset RUMP_SERVER - export RUMP_SERVER=$SOCK2 - export LD_PRELOAD=/usr/lib/librumphijack.so - $DEBUG && /sbin/brconfig bridge0 - atf_check -s exit:0 -o match:"$addr1 shmif0" /sbin/brconfig bridge0 - atf_check -s exit:0 -o match:"$addr3 shmif1" /sbin/brconfig bridge0 - - # Tests brconfig flush. - atf_check -s exit:0 -o ignore /sbin/brconfig bridge0 flush - atf_check -s exit:0 -o not-match:"$addr1 shmif0" /sbin/brconfig bridge0 - atf_check -s exit:0 -o not-match:"$addr3 shmif1" /sbin/brconfig bridge0 - unset LD_PRELOAD - - # Tests brconfig timeout. - export RUMP_SERVER=$SOCK2 - export LD_PRELOAD=/usr/lib/librumphijack.so - atf_check -s exit:0 -o match:"timeout: 1200" /sbin/brconfig bridge0 - atf_check -s exit:0 -o ignore /sbin/brconfig bridge0 timeout 10 - atf_check -s exit:0 -o match:"timeout: 10" /sbin/brconfig bridge0 - unset LD_PRELOAD - - # Tests brconfig maxaddr. - test_brconfig_maxaddr - - # TODO: brconfig static/flushall/discover/learn - # TODO: cache expiration; it takes 5 minutes at least and we want to - # wait here so long. Should we have a sysctl to change the period? - - rump_server_destroy_ifaces -} - bridge_member_ipv4_body() { setup @@ -528,21 +408,21 @@ rump_server_destroy_ifaces } -bridge_ipv4_cleanup() +bridge_create_destroy_cleanup() { $DEBUG && dump cleanup } -bridge_ipv6_cleanup() +bridge_ipv4_cleanup() { $DEBUG && dump cleanup } -bridge_rtable_cleanup() +bridge_ipv6_cleanup() { $DEBUG && dump @@ -565,9 +445,10 @@ atf_init_test_cases() { + + atf_add_test_case bridge_create_destroy atf_add_test_case bridge_ipv4 atf_add_test_case bridge_ipv6 - atf_add_test_case bridge_rtable atf_add_test_case bridge_member_ipv4 atf_add_test_case bridge_member_ipv6 } diff --git a/net/if_bridge/t_rtable.sh b/net/if_bridge/t_rtable.sh new file mode 100644 --- /dev/null +++ b/net/if_bridge/t_rtable.sh @@ -0,0 +1,505 @@ +# $NetBSD: t_rtable.sh,v 1.7 2019/08/19 03:22:05 ozaki-r Exp $ +# +# Copyright (c) 2017 Internet Initiative Japan Inc. +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +SOCK1=unix://commsock1 +SOCK2=unix://commsock2 +SOCK3=unix://commsock3 +IP1=10.0.0.1 +IP2=10.0.0.2 + +DEBUG=${DEBUG:-false} +TIMEOUT=5 + +setup_endpoint() +{ + local sock=${1} + local addr=${2} + local bus=${3} + local mode=${4} + + rump_server_add_iface $sock shmif0 $bus + export RUMP_SERVER=${sock} + if [ $mode = "ipv6" ]; then + atf_check -s exit:0 rump.ifconfig shmif0 inet6 ${addr} + else + atf_check -s exit:0 rump.ifconfig shmif0 inet ${addr} netmask 0xffffff00 + fi + + atf_check -s exit:0 rump.ifconfig shmif0 up + $DEBUG && rump.ifconfig shmif0 +} + +setup_bridge_server() +{ + + rump_server_add_iface $SOCK2 shmif0 bus1 + rump_server_add_iface $SOCK2 shmif1 bus2 + export RUMP_SERVER=$SOCK2 + atf_check -s exit:0 rump.ifconfig shmif0 up + atf_check -s exit:0 rump.ifconfig shmif1 up +} + +setup() +{ + + rump_server_start $SOCK1 bridge + rump_server_start $SOCK2 bridge + rump_server_start $SOCK3 bridge + + setup_endpoint $SOCK1 $IP1 bus1 ipv4 + setup_endpoint $SOCK3 $IP2 bus2 ipv4 + setup_bridge_server +} + +setup_bridge() +{ + + export RUMP_SERVER=$SOCK2 + rump_server_add_iface $SOCK2 bridge0 + atf_check -s exit:0 rump.ifconfig bridge0 up + + export LD_PRELOAD=/usr/lib/librumphijack.so + atf_check -s exit:0 /sbin/brconfig bridge0 add shmif0 + atf_check -s exit:0 /sbin/brconfig bridge0 add shmif1 + /sbin/brconfig bridge0 + unset LD_PRELOAD + rump.ifconfig shmif0 + rump.ifconfig shmif1 +} + +get_number_of_caches() +{ + + export RUMP_SERVER=$SOCK2 + export LD_PRELOAD=/usr/lib/librumphijack.so + echo $(/sbin/brconfig bridge0 addr |wc -l) + unset LD_PRELOAD +} + + +atf_test_case bridge_rtable_basic cleanup +bridge_rtable_basic_head() +{ + + atf_set "descr" "Tests basic opearaions of bridge's learning table" + atf_set "require.progs" "rump_server" +} + +bridge_rtable_basic_body() +{ + local addr1= addr3= + + setup + setup_bridge + + # Get MAC addresses of the endpoints. + addr1=$(get_macaddr $SOCK1 shmif0) + addr3=$(get_macaddr $SOCK3 shmif0) + + # Confirm there is no MAC address caches. + export RUMP_SERVER=$SOCK2 + export LD_PRELOAD=/usr/lib/librumphijack.so + $DEBUG && /sbin/brconfig bridge0 + atf_check -s exit:0 -o not-match:"$addr1" /sbin/brconfig bridge0 + atf_check -s exit:0 -o not-match:"$addr3" /sbin/brconfig bridge0 + unset LD_PRELOAD + + # Make the bridge learn the MAC addresses of the endpoints. + export RUMP_SERVER=$SOCK1 + atf_check -s exit:0 -o ignore rump.ping -n -w $TIMEOUT -c 1 $IP2 + unset RUMP_SERVER + + # Tests the addresses are in the cache. + export RUMP_SERVER=$SOCK2 + export LD_PRELOAD=/usr/lib/librumphijack.so + $DEBUG && /sbin/brconfig bridge0 + atf_check -s exit:0 -o match:"$addr1 shmif0" /sbin/brconfig bridge0 + atf_check -s exit:0 -o match:"$addr3 shmif1" /sbin/brconfig bridge0 + + # Tests brconfig deladdr + atf_check -s exit:0 -o ignore /sbin/brconfig bridge0 deladdr "$addr1" + atf_check -s exit:0 -o not-match:"$addr1 shmif0" /sbin/brconfig bridge0 + atf_check -s exit:0 -o ignore /sbin/brconfig bridge0 deladdr "$addr3" + atf_check -s exit:0 -o not-match:"$addr3 shmif1" /sbin/brconfig bridge0 + unset LD_PRELOAD + + rump_server_destroy_ifaces +} + +bridge_rtable_basic_cleanup() +{ + + $DEBUG && dump + cleanup +} + + +atf_test_case bridge_rtable_flush cleanup +bridge_rtable_flush_head() +{ + + atf_set "descr" "Tests brconfig flush" + atf_set "require.progs" "rump_server" +} + +bridge_rtable_flush_body() +{ + local addr1= addr3= + local n= + + setup + setup_bridge + + # Get MAC addresses of the endpoints. + addr1=$(get_macaddr $SOCK1 shmif0) + addr3=$(get_macaddr $SOCK3 shmif0) + + # Make the bridge learn the MAC addresses of the endpoints. + export RUMP_SERVER=$SOCK1 + atf_check -s exit:0 -o ignore rump.ping -n -w $TIMEOUT -c 1 $IP2 + unset RUMP_SERVER + + # Tests the addresses are in the cache. + export RUMP_SERVER=$SOCK2 + export LD_PRELOAD=/usr/lib/librumphijack.so + $DEBUG && /sbin/brconfig bridge0 + atf_check -s exit:0 -o match:"$addr1 shmif0" /sbin/brconfig bridge0 + atf_check -s exit:0 -o match:"$addr3 shmif1" /sbin/brconfig bridge0 + + # Tests brconfig flush. + atf_check -s exit:0 -o ignore /sbin/brconfig bridge0 flush + atf_check -s exit:0 -o not-match:"$addr1 shmif0" /sbin/brconfig bridge0 + atf_check -s exit:0 -o not-match:"$addr3 shmif1" /sbin/brconfig bridge0 + unset LD_PRELOAD + + # Add extra interfaces and addresses + export RUMP_SERVER=$SOCK1 + rump_server_add_iface $SOCK1 shmif1 bus1 + atf_check -s exit:0 rump.ifconfig shmif1 10.0.0.11/24 + atf_check -s exit:0 rump.ifconfig -w 10 + + export RUMP_SERVER=$SOCK3 + rump_server_add_iface $SOCK3 shmif1 bus2 + atf_check -s exit:0 rump.ifconfig shmif1 10.0.0.12/24 + atf_check -s exit:0 rump.ifconfig -w 10 + + # Let cache entries + export RUMP_SERVER=$SOCK1 + atf_check -s exit:0 -o ignore rump.ping -n -w $TIMEOUT -c 1 10.0.0.12 + export RUMP_SERVER=$SOCK3 + atf_check -s exit:0 -o ignore rump.ping -n -w $TIMEOUT -c 1 10.0.0.11 + + export RUMP_SERVER=$SOCK2 + export LD_PRELOAD=/usr/lib/librumphijack.so + $DEBUG && /sbin/brconfig bridge0 + n=$(get_number_of_caches) + atf_check_equal $n 4 + + atf_check -s exit:0 -o ignore /sbin/brconfig bridge0 flush + n=$(get_number_of_caches) + atf_check_equal $n 0 + unset LD_PRELOAD + + rump_server_destroy_ifaces +} + +bridge_rtable_flush_cleanup() +{ + + $DEBUG && dump + cleanup +} + + +atf_test_case bridge_rtable_timeout cleanup +bridge_rtable_timeout_head() +{ + + atf_set "descr" "Tests cache timeout of bridge's learning table" + atf_set "require.progs" "rump_server" +} + +bridge_rtable_timeout_body() +{ + local addr1= addr3= + local timeout=5 + + setup + setup_bridge + + # Get MAC addresses of the endpoints. + addr1=$(get_macaddr $SOCK1 shmif0) + addr3=$(get_macaddr $SOCK3 shmif0) + + # Tests brconfig timeout. + export RUMP_SERVER=$SOCK2 + export LD_PRELOAD=/usr/lib/librumphijack.so + atf_check -s exit:0 -o match:"timeout: 1200" /sbin/brconfig bridge0 + atf_check -s exit:0 -o ignore /sbin/brconfig bridge0 timeout $timeout + atf_check -s exit:0 -o match:"timeout: $timeout" /sbin/brconfig bridge0 + unset LD_PRELOAD + + # Make the bridge learn the MAC addresses of the endpoints. + export RUMP_SERVER=$SOCK1 + atf_check -s exit:0 -o ignore rump.ping -n -w $TIMEOUT -c 1 $IP2 + unset RUMP_SERVER + + # Tests the addresses are in the cache. + export RUMP_SERVER=$SOCK2 + export LD_PRELOAD=/usr/lib/librumphijack.so + $DEBUG && /sbin/brconfig bridge0 + atf_check -s exit:0 -o match:"$addr1 shmif0" /sbin/brconfig bridge0 + atf_check -s exit:0 -o match:"$addr3 shmif1" /sbin/brconfig bridge0 + + # TODO: cache expiration + # The initial timeout value of a cache is changed to $timeout and + # after $timeout elapsed the cache is ready to be sweeped. However, + # the GC of rtable runs every 5 minutes and the cache remains until + # then. Should we have a sysctl to change the period? + + #sleep $(($timeout + 2)) + # + ## Tests the addresses are not in the cache. + #export RUMP_SERVER=$SOCK2 + #export LD_PRELOAD=/usr/lib/librumphijack.so + #$DEBUG && /sbin/brconfig bridge0 + #atf_check -s exit:0 -o not-match:"$addr1 shmif0" /sbin/brconfig bridge0 + #atf_check -s exit:0 -o not-match:"$addr3 shmif1" /sbin/brconfig bridge0 + + rump_server_destroy_ifaces +} + +bridge_rtable_timeout_cleanup() +{ + + $DEBUG && dump + cleanup +} + + +atf_test_case bridge_rtable_maxaddr cleanup +bridge_rtable_maxaddr_head() +{ + + atf_set "descr" "Tests brconfig maxaddr" + atf_set "require.progs" "rump_server" +} + +bridge_rtable_maxaddr_body() +{ + local addr1= addr3= + + setup + setup_bridge + + # Get MAC addresses of the endpoints. + addr1=$(get_macaddr $SOCK1 shmif0) + addr3=$(get_macaddr $SOCK3 shmif0) + + # Fill the MAC addresses of the endpoints. + export RUMP_SERVER=$SOCK1 + atf_check -s exit:0 -o ignore rump.ping -n -w $TIMEOUT -c 1 $IP2 + export RUMP_SERVER=$SOCK2 + export LD_PRELOAD=/usr/lib/librumphijack.so + /sbin/brconfig bridge0 + atf_check -s exit:0 -o match:"$addr1 shmif0" /sbin/brconfig bridge0 + atf_check -s exit:0 -o match:"$addr3 shmif1" /sbin/brconfig bridge0 + + # Check the default # of caches is 100 + atf_check -s exit:0 -o match:"max cache: 100" /sbin/brconfig bridge0 + + # Test two MAC addresses are cached + n=$(get_number_of_caches) + atf_check_equal $n 2 + + # Limit # of caches to one + atf_check -s exit:0 -o ignore /sbin/brconfig bridge0 maxaddr 1 + atf_check -s exit:0 -o match:"max cache: 1" /sbin/brconfig bridge0 + /sbin/brconfig bridge0 + + # Check a cache is flushed out + n=$(get_number_of_caches) + atf_check_equal $n 1 + + # Test a new address cache is not created + export RUMP_SERVER=$SOCK1 + atf_check -s exit:0 -o ignore rump.ping -n -w $TIMEOUT -c 1 $IP2 + export RUMP_SERVER=$SOCK2 + n=$(get_number_of_caches) + atf_check_equal $n 1 + + # Increase # of caches to two + atf_check -s exit:0 -o ignore /sbin/brconfig bridge0 maxaddr 2 + atf_check -s exit:0 -o match:"max cache: 2" /sbin/brconfig bridge0 + unset LD_PRELOAD + + # Test we can cache two addresses again + export RUMP_SERVER=$SOCK1 + atf_check -s exit:0 -o ignore rump.ping -n -w $TIMEOUT -c 1 $IP2 + export RUMP_SERVER=$SOCK2 + export LD_PRELOAD=/usr/lib/librumphijack.so + /sbin/brconfig bridge0 + atf_check -s exit:0 -o match:"$addr1 shmif0" /sbin/brconfig bridge0 + atf_check -s exit:0 -o match:"$addr3 shmif1" /sbin/brconfig bridge0 + unset LD_PRELOAD + + rump_server_destroy_ifaces +} + +bridge_rtable_maxaddr_cleanup() +{ + + $DEBUG && dump + cleanup +} + + +atf_test_case bridge_rtable_delete_member cleanup +bridge_rtable_delete_member_head() +{ + + atf_set "descr" "Tests belonging rtable entries are removed on deleting an interface" + atf_set "require.progs" "rump_server" +} + +bridge_rtable_delete_member_body() +{ + local addr10= addr30= addr11= addr31= + local n= + + setup + setup_bridge + + # Add extra interfaces and addresses + export RUMP_SERVER=$SOCK1 + rump_server_add_iface $SOCK1 shmif1 bus1 + atf_check -s exit:0 rump.ifconfig shmif1 10.0.0.11/24 + atf_check -s exit:0 rump.ifconfig -w 10 + + export RUMP_SERVER=$SOCK3 + rump_server_add_iface $SOCK3 shmif1 bus2 + atf_check -s exit:0 rump.ifconfig shmif1 10.0.0.12/24 + atf_check -s exit:0 rump.ifconfig -w 10 + + # Get MAC addresses of the endpoints. + addr10=$(get_macaddr $SOCK1 shmif0) + addr30=$(get_macaddr $SOCK3 shmif0) + addr11=$(get_macaddr $SOCK1 shmif1) + addr31=$(get_macaddr $SOCK3 shmif1) + + # Make the bridge learn the MAC addresses of the endpoints. + export RUMP_SERVER=$SOCK1 + atf_check -s exit:0 -o ignore rump.ping -n -w $TIMEOUT -c 1 10.0.0.12 + export RUMP_SERVER=$SOCK3 + atf_check -s exit:0 -o ignore rump.ping -n -w $TIMEOUT -c 1 10.0.0.11 + + export RUMP_SERVER=$SOCK2 + export LD_PRELOAD=/usr/lib/librumphijack.so + $DEBUG && /sbin/brconfig bridge0 + atf_check -s exit:0 -o match:"$addr10 shmif0" /sbin/brconfig bridge0 + atf_check -s exit:0 -o match:"$addr11 shmif0" /sbin/brconfig bridge0 + atf_check -s exit:0 -o match:"$addr30 shmif1" /sbin/brconfig bridge0 + atf_check -s exit:0 -o match:"$addr31 shmif1" /sbin/brconfig bridge0 + + atf_check -s exit:0 -o ignore /sbin/brconfig bridge0 delete shmif0 + atf_check -s exit:0 -o not-match:"$addr10 shmif0" /sbin/brconfig bridge0 + atf_check -s exit:0 -o not-match:"$addr11 shmif0" /sbin/brconfig bridge0 + atf_check -s exit:0 -o match:"$addr30 shmif1" /sbin/brconfig bridge0 + atf_check -s exit:0 -o match:"$addr31 shmif1" /sbin/brconfig bridge0 + + atf_check -s exit:0 -o ignore /sbin/brconfig bridge0 delete shmif1 + atf_check -s exit:0 -o not-match:"$addr10 shmif0" /sbin/brconfig bridge0 + atf_check -s exit:0 -o not-match:"$addr11 shmif0" /sbin/brconfig bridge0 + atf_check -s exit:0 -o not-match:"$addr30 shmif1" /sbin/brconfig bridge0 + atf_check -s exit:0 -o not-match:"$addr31 shmif1" /sbin/brconfig bridge0 + + rump_server_destroy_ifaces +} + +bridge_rtable_delete_member_cleanup() +{ + + $DEBUG && dump + cleanup +} + + +atf_test_case bridge_rtable_manyaddrs cleanup +bridge_rtable_manyaddrs_head() +{ + + atf_set "descr" "Tests brconfig addr under many MAC addresses" + atf_set "require.progs" "rump_server" + atf_set "timeout" "1200" +} + +bridge_rtable_manyaddrs_body() +{ + local addr= + + setup + setup_bridge + + export RUMP_SERVER=$SOCK2 + export LD_PRELOAD=/usr/lib/librumphijack.so + atf_check -s exit:0 -o ignore /sbin/brconfig bridge0 maxaddr 1024 + + # Fill the MAC addresses + for i in 1 2 3 4; do + for j in $(seq 0 255); do + addr="00:11:22:33:4$i:$(printf "%02x" $j)" + atf_check -s exit:0 -o empty \ + /sbin/brconfig bridge0 static shmif0 $addr + done + + n=$(get_number_of_caches) + atf_check_equal $n $((i * 256)) + done + + + rump_server_destroy_ifaces +} + +bridge_rtable_manyaddrs_cleanup() +{ + + $DEBUG && dump + cleanup +} + + +atf_init_test_cases() +{ + + atf_add_test_case bridge_rtable_basic + atf_add_test_case bridge_rtable_flush + atf_add_test_case bridge_rtable_timeout + atf_add_test_case bridge_rtable_maxaddr + atf_add_test_case bridge_rtable_delete_member + atf_add_test_case bridge_rtable_manyaddrs + # TODO: brconfig static/flushall/discover/learn +} diff --git a/net/if_gif/t_gif.sh b/net/if_gif/t_gif.sh old mode 100755 new mode 100644 --- a/net/if_gif/t_gif.sh +++ b/net/if_gif/t_gif.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_gif.sh,v 1.9 2016/12/21 09:46:39 ozaki-r Exp $ +# $NetBSD: t_gif.sh,v 1.13 2019/08/19 03:22:05 ozaki-r Exp $ # # Copyright (c) 2015 Internet Initiative Japan Inc. # All rights reserved. @@ -61,9 +61,32 @@ ROUTER2_GIFIP6_RECURSIVE1=fc00:104::1 ROUTER2_GIFIP6_RECURSIVE2=fc00:204::1 -DEBUG=${DEBUG:-true} +DEBUG=${DEBUG:-false} TIMEOUT=5 +atf_test_case gif_create_destroy cleanup +gif_create_destroy_head() +{ + + atf_set "descr" "Test creating/destroying gif interfaces" + atf_set "require.progs" "rump_server" +} + +gif_create_destroy_body() +{ + + rump_server_start $SOCK1 netinet6 gif + + test_create_destroy_common $SOCK1 gif0 true +} + +gif_create_destroy_cleanup() +{ + + $DEBUG && dump + cleanup +} + setup_router() { sock=${1} @@ -198,7 +221,7 @@ peernet=${7} export RUMP_SERVER=${sock} - atf_check -s exit:0 rump.ifconfig gif0 create + rump_server_add_iface $sock gif0 atf_check -s exit:0 rump.ifconfig gif0 tunnel ${src} ${dst} if [ ${inner} = "ipv6" ]; then atf_check -s exit:0 rump.ifconfig gif0 inet6 ${addr}/128 ${remote} @@ -312,7 +335,7 @@ dst=${6} export RUMP_SERVER=${sock} - atf_check -s exit:0 rump.ifconfig gif1 create + rump_server_add_iface $sock gif1 atf_check -s exit:0 rump.ifconfig gif1 tunnel ${src} ${dst} if [ ${inner} = "ipv6" ]; then atf_check -s exit:0 rump.ifconfig gif1 inet6 ${addr}/128 ${remote} @@ -399,7 +422,7 @@ dst=${7} export RUMP_SERVER=${sock} - atf_check -s exit:0 rump.ifconfig ${gif} create + rump_server_add_iface $sock $gif atf_check -s exit:0 rump.ifconfig ${gif} tunnel ${src} ${dst} if [ ${inner} = "ipv6" ]; then atf_check -s exit:0 rump.ifconfig ${gif} inet6 ${addr}/128 ${remote} @@ -727,19 +750,19 @@ fulldesc="Does ${inner} over ${outer} if_gif ${desc}" atf_test_case ${name} cleanup - eval "${name}_head() { \ - atf_set \"descr\" \"${fulldesc}\"; \ - atf_set \"require.progs\" \"rump_server\"; \ - }; \ - ${name}_body() { \ - ${category}_setup ${inner} ${outer}; \ - ${category}_test ${inner} ${outer}; \ - ${category}_teardown ${inner} ${outer}; \ - rump_server_destroy_ifaces; \ - }; \ - ${name}_cleanup() { \ - $DEBUG && dump; \ - cleanup; \ + eval "${name}_head() { + atf_set descr \"${fulldesc}\" + atf_set require.progs rump_server + } + ${name}_body() { + ${category}_setup ${inner} ${outer} + ${category}_test ${inner} ${outer} + ${category}_teardown ${inner} ${outer} + rump_server_destroy_ifaces + } + ${name}_cleanup() { + \$DEBUG && dump + cleanup }" atf_add_test_case ${name} } @@ -757,6 +780,9 @@ atf_init_test_cases() { + + atf_add_test_case gif_create_destroy + add_test_allproto basic "basic tests" add_test_allproto ioctl "ioctl tests" add_test_allproto recursive "recursive check tests" diff --git a/net/if_ipsec/Makefile b/net/if_ipsec/Makefile new file mode 100644 --- /dev/null +++ b/net/if_ipsec/Makefile @@ -0,0 +1,14 @@ +# $NetBSD: Makefile,v 1.3 2019/01/17 02:49:11 knakahara Exp $ +# + +.include + +TESTSDIR= ${TESTSBASE}/net/if_ipsec + +.for name in ipsec ipsec_natt ipsec_pfil +TESTS_SH+= t_${name} +TESTS_SH_SRC_t_${name}= ../net_common.sh t_${name}.sh \ + ../ipsec/common.sh ../ipsec/algorithms.sh +.endfor + +.include diff --git a/net/if_ipsec/t_ipsec.sh b/net/if_ipsec/t_ipsec.sh new file mode 100644 --- /dev/null +++ b/net/if_ipsec/t_ipsec.sh @@ -0,0 +1,965 @@ +# $NetBSD: t_ipsec.sh,v 1.11 2020/08/05 01:10:50 knakahara Exp $ +# +# Copyright (c) 2017 Internet Initiative Japan Inc. +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +SOCK1=unix://commsock1 # for ROUTER1 +SOCK2=unix://commsock2 # for ROUTER2 +ROUTER1_LANIP=192.168.1.1 +ROUTER1_LANNET=192.168.1.0/24 +ROUTER1_WANIP=10.0.0.1 +ROUTER1_IPSECIP=172.16.1.1 +ROUTER1_WANIP_DUMMY=10.0.0.11 +ROUTER1_IPSECIP_DUMMY=172.16.11.1 +ROUTER1_IPSECIP_RECURSIVE1=172.16.101.1 +ROUTER1_IPSECIP_RECURSIVE2=172.16.201.1 +ROUTER2_LANIP=192.168.2.1 +ROUTER2_LANNET=192.168.2.0/24 +ROUTER2_WANIP=10.0.0.2 +ROUTER2_IPSECIP=172.16.2.1 +ROUTER2_WANIP_DUMMY=10.0.0.12 +ROUTER2_IPSECIP_DUMMY=172.16.12.1 +ROUTER2_IPSECIP_RECURSIVE1=172.16.102.1 +ROUTER2_IPSECIP_RECURSIVE2=172.16.202.1 + +ROUTER1_LANIP6=fc00:1::1 +ROUTER1_LANNET6=fc00:1::/64 +ROUTER1_WANIP6=fc00::1 +ROUTER1_IPSECIP6=fc00:3::1 +ROUTER1_WANIP6_DUMMY=fc00::11 +ROUTER1_IPSECIP6_DUMMY=fc00:13::1 +ROUTER1_IPSECIP6_RECURSIVE1=fc00:103::1 +ROUTER1_IPSECIP6_RECURSIVE2=fc00:203::1 +ROUTER2_LANIP6=fc00:2::1 +ROUTER2_LANNET6=fc00:2::/64 +ROUTER2_WANIP6=fc00::2 +ROUTER2_IPSECIP6=fc00:4::1 +ROUTER2_WANIP6_DUMMY=fc00::12 +ROUTER2_IPSECIP6_DUMMY=fc00:14::1 +ROUTER2_IPSECIP6_RECURSIVE1=fc00:104::1 +ROUTER2_IPSECIP6_RECURSIVE2=fc00:204::1 + +DEBUG=${DEBUG:-false} +TIMEOUT=7 + +atf_test_case ipsecif_create_destroy cleanup +ipsecif_create_destroy_head() +{ + + atf_set "descr" "Test creating/destroying gif interfaces" + atf_set "require.progs" "rump_server" +} + +ipsecif_create_destroy_body() +{ + + rump_server_start $SOCK1 ipsec + + test_create_destroy_common $SOCK1 ipsec0 +} + +ipsecif_create_destroy_cleanup() +{ + + $DEBUG && dump + cleanup +} + +setup_router() +{ + local sock=${1} + local lan=${2} + local lan_mode=${3} + local wan=${4} + local wan_mode=${5} + + rump_server_add_iface $sock shmif0 bus0 + rump_server_add_iface $sock shmif1 bus1 + + export RUMP_SERVER=${sock} + + atf_check -s exit:0 rump.sysctl -q -w net.inet.ip.dad_count=0 + atf_check -s exit:0 rump.sysctl -q -w net.inet6.ip6.dad_count=0 + + if [ ${lan_mode} = "ipv6" ]; then + atf_check -s exit:0 rump.ifconfig shmif0 inet6 ${lan} + else + atf_check -s exit:0 rump.ifconfig shmif0 inet ${lan} netmask 0xffffff00 + fi + atf_check -s exit:0 rump.ifconfig shmif0 up + $DEBUG && rump.ifconfig shmif0 + + if [ ${wan_mode} = "ipv6" ]; then + atf_check -s exit:0 rump.ifconfig shmif1 inet6 ${wan} + else + atf_check -s exit:0 rump.ifconfig shmif1 inet ${wan} netmask 0xff000000 + fi + atf_check -s exit:0 rump.ifconfig shmif1 up + atf_check -s exit:0 rump.ifconfig -w 10 + $DEBUG && rump.ifconfig shmif1 + + unset RUMP_SERVER +} + +test_router() +{ + local sock=${1} + local lan=${2} + local lan_mode=${3} + local wan=${4} + local wan_mode=${5} + + export RUMP_SERVER=${sock} + atf_check -s exit:0 -o match:shmif0 rump.ifconfig + if [ ${lan_mode} = "ipv6" ]; then + atf_check -s exit:0 -o ignore rump.ping6 -n -c 1 -X $TIMEOUT ${lan} + else + atf_check -s exit:0 -o ignore rump.ping -n -c 1 -w $TIMEOUT ${lan} + fi + + atf_check -s exit:0 -o match:shmif1 rump.ifconfig + if [ ${wan_mode} = "ipv6" ]; then + atf_check -s exit:0 -o ignore rump.ping6 -n -c 1 -X $TIMEOUT ${wan} + else + atf_check -s exit:0 -o ignore rump.ping -n -c 1 -w $TIMEOUT ${wan} + fi + unset RUMP_SERVER +} + +setup() +{ + local inner=${1} + local outer=${2} + + rump_server_crypto_start $SOCK1 netipsec netinet6 ipsec + rump_server_crypto_start $SOCK2 netipsec netinet6 ipsec + + router1_lan="" + router1_lan_mode="" + router2_lan="" + router2_lan_mode="" + if [ ${inner} = "ipv6" ]; then + router1_lan=$ROUTER1_LANIP6 + router1_lan_mode="ipv6" + router2_lan=$ROUTER2_LANIP6 + router2_lan_mode="ipv6" + else + router1_lan=$ROUTER1_LANIP + router1_lan_mode="ipv4" + router2_lan=$ROUTER2_LANIP + router2_lan_mode="ipv4" + fi + + if [ ${outer} = "ipv6" ]; then + setup_router $SOCK1 ${router1_lan} ${router1_lan_mode} \ + $ROUTER1_WANIP6 ipv6 + setup_router $SOCK2 ${router2_lan} ${router2_lan_mode} \ + $ROUTER2_WANIP6 ipv6 + else + setup_router $SOCK1 ${router1_lan} ${router1_lan_mode} \ + $ROUTER1_WANIP ipv4 + setup_router $SOCK2 ${router2_lan} ${router2_lan_mode} \ + $ROUTER2_WANIP ipv4 + fi +} + +test_setup() +{ + local inner=${1} + local outer=${2} + + local router1_lan="" + local router1_lan_mode="" + local router2_lan="" + local router2_lan_mode="" + if [ ${inner} = "ipv6" ]; then + router1_lan=$ROUTER1_LANIP6 + router1_lan_mode="ipv6" + router2_lan=$ROUTER2_LANIP6 + router2_lan_mode="ipv6" + else + router1_lan=$ROUTER1_LANIP + router1_lan_mode="ipv4" + router2_lan=$ROUTER2_LANIP + router2_lan_mode="ipv4" + fi + if [ ${outer} = "ipv6" ]; then + test_router $SOCK1 ${router1_lan} ${router1_lan_mode} \ + $ROUTER1_WANIP6 ipv6 + test_router $SOCK2 ${router2_lan} ${router2_lan_mode} \ + $ROUTER2_WANIP6 ipv6 + else + test_router $SOCK1 ${router1_lan} ${router1_lan_mode} \ + $ROUTER1_WANIP ipv4 + test_router $SOCK2 ${router2_lan} ${router2_lan_mode} \ + $ROUTER2_WANIP ipv4 + fi +} + +get_if_ipsec_unique() +{ + local sock=${1} + local src=${2} + local proto=${3} + local unique="" + + export RUMP_SERVER=${sock} + unique=`$HIJACKING setkey -DP | grep -A2 "^${src}.*(${proto})$" | grep unique | sed 's/.*unique#//'` + unset RUMP_SERVER + + echo $unique +} + +setup_if_ipsec() +{ + local sock=${1} + local addr=${2} + local remote=${3} + local inner=${4} + local src=${5} + local dst=${6} + local peernet=${7} + + export RUMP_SERVER=${sock} + rump_server_add_iface $sock ipsec0 + atf_check -s exit:0 rump.ifconfig ipsec0 tunnel ${src} ${dst} + if [ ${inner} = "ipv6" ]; then + atf_check -s exit:0 rump.ifconfig ipsec0 inet6 ${addr}/128 ${remote} + atf_check -s exit:0 -o ignore rump.route add -inet6 ${peernet} ${addr} + else + atf_check -s exit:0 rump.ifconfig ipsec0 inet ${addr}/32 ${remote} + atf_check -s exit:0 -o ignore rump.route add -inet ${peernet} ${addr} + fi + + atf_check -s exit:0 rump.ifconfig -w 10 + + $DEBUG && rump.ifconfig ipsec0 + $DEBUG && rump.route -nL show +} + +setup_if_ipsec_sa() +{ + local sock=${1} + local src=${2} + local dst=${3} + local mode=${4} + local proto=${5} + local algo=${6} + local dir=${7} + + local tmpfile=./tmp + local inunique="" + local outunique="" + local inid="" + local outid="" + local algo_args="$(generate_algo_args $proto $algo)" + + inunique=`get_if_ipsec_unique ${sock} ${dst} ${mode}` + atf_check -s exit:0 test "X$inunique" != "X" + outunique=`get_if_ipsec_unique ${sock} ${src} ${mode}` + atf_check -s exit:0 test "X$outunique" != "X" + + if [ ${dir} = "1to2" ] ; then + if [ ${mode} = "ipv6" ] ; then + inid="10010" + outid="10011" + else + inid="10000" + outid="10001" + fi + else + if [ ${mode} = "ipv6" ] ; then + inid="10011" + outid="10010" + else + inid="10001" + outid="10000" + fi + fi + + cat > $tmpfile <<-EOF + add $dst $src $proto $inid -u $inunique -m transport $algo_args; + add $src $dst $proto $outid -u $outunique -m transport $algo_args; + EOF + $DEBUG && cat $tmpfile + export RUMP_SERVER=$sock + atf_check -s exit:0 -o empty $HIJACKING setkey -c < $tmpfile + $DEBUG && $HIJACKING setkey -D + $DEBUG && $HIJACKING setkey -DP + unset RUMP_SERVER +} + +setup_tunnel() +{ + local inner=${1} + local outer=${2} + local proto=${3} + local algo=${4} + + local addr="" + local remote="" + local src="" + local dst="" + local peernet="" + + if [ ${inner} = "ipv6" ]; then + addr=$ROUTER1_IPSECIP6 + remote=$ROUTER2_IPSECIP6 + peernet=$ROUTER2_LANNET6 + else + addr=$ROUTER1_IPSECIP + remote=$ROUTER2_IPSECIP + peernet=$ROUTER2_LANNET + fi + if [ ${outer} = "ipv6" ]; then + src=$ROUTER1_WANIP6 + dst=$ROUTER2_WANIP6 + else + src=$ROUTER1_WANIP + dst=$ROUTER2_WANIP + fi + setup_if_ipsec $SOCK1 ${addr} ${remote} ${inner} \ + ${src} ${dst} ${peernet} + + if [ $inner = "ipv6" -a $outer = "ipv4" ]; then + setup_if_ipsec_sa $SOCK1 ${src} ${dst} ${outer} ${proto} ${algo} "1to2" + fi + setup_if_ipsec_sa $SOCK1 ${src} ${dst} ${inner} ${proto} ${algo} "1to2" + + if [ $inner = "ipv6" ]; then + addr=$ROUTER2_IPSECIP6 + remote=$ROUTER1_IPSECIP6 + peernet=$ROUTER1_LANNET6 + else + addr=$ROUTER2_IPSECIP + remote=$ROUTER1_IPSECIP + peernet=$ROUTER1_LANNET + fi + if [ $outer = "ipv6" ]; then + src=$ROUTER2_WANIP6 + dst=$ROUTER1_WANIP6 + else + src=$ROUTER2_WANIP + dst=$ROUTER1_WANIP + fi + setup_if_ipsec $SOCK2 ${addr} ${remote} ${inner} \ + ${src} ${dst} ${peernet} ${proto} ${algo} + if [ $inner = "ipv6" -a $outer = "ipv4" ]; then + setup_if_ipsec_sa $SOCK2 ${src} ${dst} ${outer} ${proto} ${algo} "2to1" + fi + setup_if_ipsec_sa $SOCK2 ${src} ${dst} ${inner} ${proto} ${algo} "2to1" +} + +test_setup_tunnel() +{ + local mode=${1} + + local peernet="" + local opt="" + if [ ${mode} = "ipv6" ]; then + peernet=$ROUTER2_LANNET6 + opt="-inet6" + else + peernet=$ROUTER2_LANNET + opt="-inet" + fi + export RUMP_SERVER=$SOCK1 + atf_check -s exit:0 -o match:ipsec0 rump.ifconfig + atf_check -s exit:0 -o match:ipsec0 rump.route -nL get ${opt} ${peernet} + + if [ ${mode} = "ipv6" ]; then + peernet=$ROUTER1_LANNET6 + opt="-inet6" + else + peernet=$ROUTER1_LANNET + opt="-inet" + fi + export RUMP_SERVER=$SOCK2 + atf_check -s exit:0 -o match:ipsec0 rump.ifconfig + atf_check -s exit:0 -o match:ipsec0 rump.route -nL get ${opt} ${peernet} +} + +teardown_tunnel() +{ + export RUMP_SERVER=$SOCK1 + atf_check -s exit:0 rump.ifconfig ipsec0 deletetunnel + atf_check -s exit:0 rump.ifconfig ipsec0 destroy + $HIJACKING setkey -F + + export RUMP_SERVER=$SOCK2 + atf_check -s exit:0 rump.ifconfig ipsec0 deletetunnel + atf_check -s exit:0 rump.ifconfig ipsec0 destroy + $HIJACKING setkey -F + + unset RUMP_SERVER +} + +setup_dummy_if_ipsec() +{ + local sock=${1} + local addr=${2} + local remote=${3} + local inner=${4} + local src=${5} + local dst=${6} + + export RUMP_SERVER=${sock} + rump_server_add_iface $sock ipsec1 + atf_check -s exit:0 rump.ifconfig ipsec1 tunnel ${src} ${dst} + if [ ${inner} = "ipv6" ]; then + atf_check -s exit:0 rump.ifconfig ipsec1 inet6 ${addr}/128 ${remote} + else + atf_check -s exit:0 rump.ifconfig ipsec1 inet ${addr}/32 ${remote} + fi + atf_check -s exit:0 rump.ifconfig -w 10 + + $DEBUG && rump.ifconfig ipsec1 + unset RUMP_SERVER +} + +setup_dummy_if_ipsec_sa() +{ + local sock=${1} + local src=${2} + local dst=${3} + local mode=${4} + local proto=${5} + local algo=${6} + local dir=${7} + + local tmpfile=./tmp + local inunique="" + local outunique="" + local inid="" + local outid="" + local algo_args="$(generate_algo_args $proto $algo)" + + inunique=`get_if_ipsec_unique ${sock} ${dst} ${mode}` + atf_check -s exit:0 test "X$inunique" != "X" + outunique=`get_if_ipsec_unique ${sock} ${src} ${mode}` + atf_check -s exit:0 test "X$outunique" != "X" + + if [ ${dir} = "1to2" ] ; then + inid="20000" + outid="20001" + else + inid="20001" + outid="20000" + fi + + cat > $tmpfile <<-EOF + add $dst $src $proto $inid -u $inunique $algo_args; + add $src $dst $proto $outid -u $outunique $algo_args; + EOF + $DEBUG && cat $tmpfile + export RUMP_SERVER=$sock + atf_check -s exit:0 -o empty $HIJACKING setkey -c < $tmpfile + $DEBUG && $HIJACKING setkey -D + $DEBUG && $HIJACKING setkey -DP + unset RUMP_SERVER +} + +setup_dummy_tunnel() +{ + local inner=${1} + local outer=${2} + local proto=${3} + local algo=${4} + + local addr="" + local remote="" + local src="" + local dst="" + + if [ ${inner} = "ipv6" ]; then + addr=$ROUTER1_IPSECIP6_DUMMY + remote=$ROUTER2_IPSECIP6_DUMMY + else + addr=$ROUTER1_IPSECIP_DUMMY + remote=$ROUTER2_IPSECIP_DUMMY + fi + if [ ${outer} = "ipv6" ]; then + src=$ROUTER1_WANIP6_DUMMY + dst=$ROUTER2_WANIP6_DUMMY + else + src=$ROUTER1_WANIP_DUMMY + dst=$ROUTER2_WANIP_DUMMY + fi + setup_dummy_if_ipsec $SOCK1 ${addr} ${remote} ${inner} \ + ${src} ${dst} ${proto} ${algo} "1to2" + setup_dummy_if_ipsec_sa $SOCK1 ${src} ${dst} ${inner} ${proto} ${algo} "1to2" + + if [ $inner = "ipv6" ]; then + addr=$ROUTER2_IPSECIP6_DUMMY + remote=$ROUTER1_IPSECIP6_DUMMY + else + addr=$ROUTER2_IPSECIP_DUMMY + remote=$ROUTER1_IPSECIP_DUMMY + fi + if [ $outer = "ipv6" ]; then + src=$ROUTER2_WANIP6_DUMMY + dst=$ROUTER1_WANIP6_DUMMY + else + src=$ROUTER2_WANIP_DUMMY + dst=$ROUTER1_WANIP_DUMMY + fi + setup_dummy_if_ipsec $SOCK2 ${addr} ${remote} ${inner} \ + ${src} ${dst} ${proto} ${algo} "2to1" + setup_dummy_if_ipsec_sa $SOCK2 ${src} ${dst} ${inner} ${proto} ${algo} "2to1" +} + +test_setup_dummy_tunnel() +{ + export RUMP_SERVER=$SOCK1 + atf_check -s exit:0 -o match:ipsec1 rump.ifconfig + + export RUMP_SERVER=$SOCK2 + atf_check -s exit:0 -o match:ipsec1 rump.ifconfig + + unset RUMP_SERVER +} + +teardown_dummy_tunnel() +{ + export RUMP_SERVER=$SOCK1 + atf_check -s exit:0 rump.ifconfig ipsec1 deletetunnel + atf_check -s exit:0 rump.ifconfig ipsec1 destroy + + export RUMP_SERVER=$SOCK2 + atf_check -s exit:0 rump.ifconfig ipsec1 deletetunnel + atf_check -s exit:0 rump.ifconfig ipsec1 destroy + + unset RUMP_SERVER +} + +setup_recursive_if_ipsec() +{ + local sock=${1} + local ipsec=${2} + local addr=${3} + local remote=${4} + local inner=${5} + local src=${6} + local dst=${7} + local proto=${8} + local algo=${9} + local dir=${10} + + export RUMP_SERVER=${sock} + rump_server_add_iface $sock $ipsec + atf_check -s exit:0 rump.ifconfig ${ipsec} tunnel ${src} ${dst} + if [ ${inner} = "ipv6" ]; then + atf_check -s exit:0 rump.ifconfig ${ipsec} inet6 ${addr}/128 ${remote} + else + atf_check -s exit:0 rump.ifconfig ${ipsec} inet ${addr}/32 ${remote} + fi + atf_check -s exit:0 rump.ifconfig -w 10 + setup_if_ipsec_sa $sock ${src} ${dst} ${inner} ${proto} ${algo} ${dir} + + export RUMP_SERVER=${sock} + $DEBUG && rump.ifconfig ${ipsec} + unset RUMP_SERVER +} + +# test in ROUTER1 only +setup_recursive_tunnels() +{ + local mode=${1} + local proto=${2} + local algo=${3} + + local addr="" + local remote="" + local src="" + local dst="" + + if [ ${mode} = "ipv6" ]; then + addr=$ROUTER1_IPSECIP6_RECURSIVE1 + remote=$ROUTER2_IPSECIP6_RECURSIVE1 + src=$ROUTER1_IPSECIP6 + dst=$ROUTER2_IPSECIP6 + else + addr=$ROUTER1_IPSECIP_RECURSIVE1 + remote=$ROUTER2_IPSECIP_RECURSIVE1 + src=$ROUTER1_IPSECIP + dst=$ROUTER2_IPSECIP + fi + setup_recursive_if_ipsec $SOCK1 ipsec1 ${addr} ${remote} ${mode} \ + ${src} ${dst} ${proto} ${algo} "1to2" + + if [ ${mode} = "ipv6" ]; then + addr=$ROUTER1_IPSECIP6_RECURSIVE2 + remote=$ROUTER2_IPSECIP6_RECURSIVE2 + src=$ROUTER1_IPSECIP6_RECURSIVE1 + dst=$ROUTER2_IPSECIP6_RECURSIVE1 + else + addr=$ROUTER1_IPSECIP_RECURSIVE2 + remote=$ROUTER2_IPSECIP_RECURSIVE2 + src=$ROUTER1_IPSECIP_RECURSIVE1 + dst=$ROUTER2_IPSECIP_RECURSIVE1 + fi + setup_recursive_if_ipsec $SOCK1 ipsec2 ${addr} ${remote} ${mode} \ + ${src} ${dst} ${proto} ${algo} "1to2" +} + +# test in router1 only +test_recursive_check() +{ + local mode=$1 + + export RUMP_SERVER=$SOCK1 + if [ ${mode} = "ipv6" ]; then + atf_check -s not-exit:0 -o ignore -e ignore \ + rump.ping6 -n -X $TIMEOUT -c 1 $ROUTER2_IPSECIP6_RECURSIVE2 + else + atf_check -s not-exit:0 -o ignore -e ignore \ + rump.ping -n -w $TIMEOUT -c 1 $ROUTER2_IPSECIP_RECURSIVE2 + fi + + atf_check -o match:'ipsec0: recursively called too many times' \ + -x "$HIJACKING dmesg" + + $HIJACKING dmesg + + unset RUMP_SERVER +} + +teardown_recursive_tunnels() +{ + export RUMP_SERVER=$SOCK1 + atf_check -s exit:0 rump.ifconfig ipsec1 deletetunnel + atf_check -s exit:0 rump.ifconfig ipsec1 destroy + atf_check -s exit:0 rump.ifconfig ipsec2 deletetunnel + atf_check -s exit:0 rump.ifconfig ipsec2 destroy + unset RUMP_SERVER +} + +test_ping_failure() +{ + local mode=$1 + + export RUMP_SERVER=$SOCK1 + if [ ${mode} = "ipv6" ]; then + atf_check -s not-exit:0 -o ignore -e ignore \ + rump.ping6 -n -X $TIMEOUT -c 1 -S $ROUTER1_LANIP6 \ + $ROUTER2_LANIP6 + else + atf_check -s not-exit:0 -o ignore -e ignore \ + rump.ping -n -w $TIMEOUT -c 1 -I $ROUTER1_LANIP \ + $ROUTER2_LANIP + fi + + export RUMP_SERVER=$SOCK2 + if [ ${mode} = "ipv6" ]; then + atf_check -s not-exit:0 -o ignore -e ignore \ + rump.ping6 -n -X $TIMEOUT -c 1 -S $ROUTER2_LANIP6 \ + $ROUTER1_LANIP6 + else + atf_check -s not-exit:0 -o ignore -e ignore \ + rump.ping -n -w $TIMEOUT -c 1 -I $ROUTER1_LANIP \ + $ROUTER2_LANIP + fi + + unset RUMP_SERVER +} + +test_ping_success() +{ + mode=$1 + + export RUMP_SERVER=$SOCK1 + $DEBUG && rump.ifconfig -v ipsec0 + if [ ${mode} = "ipv6" ]; then + # XXX + # rump.ping6 rarely fails with the message that + # "failed to get receiving hop limit". + # This is a known issue being analyzed. + atf_check -s exit:0 -o ignore \ + rump.ping6 -n -X $TIMEOUT -c 1 -S $ROUTER1_LANIP6 \ + $ROUTER2_LANIP6 + else + atf_check -s exit:0 -o ignore \ + rump.ping -n -w $TIMEOUT -c 1 -I $ROUTER1_LANIP \ + $ROUTER2_LANIP + fi + $DEBUG && rump.ifconfig -v ipsec0 + + export RUMP_SERVER=$SOCK2 + $DEBUG && rump.ifconfig -v ipsec0 + if [ ${mode} = "ipv6" ]; then + atf_check -s exit:0 -o ignore \ + rump.ping6 -n -X $TIMEOUT -c 1 -S $ROUTER2_LANIP6 \ + $ROUTER1_LANIP6 + else + atf_check -s exit:0 -o ignore \ + rump.ping -n -w $TIMEOUT -c 1 -I $ROUTER2_LANIP \ + $ROUTER1_LANIP + fi + $DEBUG && rump.ifconfig -v ipsec0 + + unset RUMP_SERVER +} + +test_change_tunnel_duplicate() +{ + local mode=$1 + + local newsrc="" + local newdst="" + if [ ${mode} = "ipv6" ]; then + newsrc=$ROUTER1_WANIP6_DUMMY + newdst=$ROUTER2_WANIP6_DUMMY + else + newsrc=$ROUTER1_WANIP_DUMMY + newdst=$ROUTER2_WANIP_DUMMY + fi + export RUMP_SERVER=$SOCK1 + $DEBUG && rump.ifconfig -v ipsec0 + $DEBUG && rump.ifconfig -v ipsec1 + atf_check -s exit:0 -e match:SIOCSLIFPHYADDR \ + rump.ifconfig ipsec0 tunnel ${newsrc} ${newdst} + $DEBUG && rump.ifconfig -v ipsec0 + $DEBUG && rump.ifconfig -v ipsec1 + + if [ ${mode} = "ipv6" ]; then + newsrc=$ROUTER2_WANIP6_DUMMY + newdst=$ROUTER1_WANIP6_DUMMY + else + newsrc=$ROUTER2_WANIP_DUMMY + newdst=$ROUTER1_WANIP_DUMMY + fi + export RUMP_SERVER=$SOCK2 + $DEBUG && rump.ifconfig -v ipsec0 + $DEBUG && rump.ifconfig -v ipsec1 + atf_check -s exit:0 -e match:SIOCSLIFPHYADDR \ + rump.ifconfig ipsec0 tunnel ${newsrc} ${newdst} + $DEBUG && rump.ifconfig -v ipsec0 + $DEBUG && rump.ifconfig -v ipsec1 + + unset RUMP_SERVER +} + +test_change_tunnel_success() +{ + local mode=$1 + + local newsrc="" + local newdst="" + if [ ${mode} = "ipv6" ]; then + newsrc=$ROUTER1_WANIP6_DUMMY + newdst=$ROUTER2_WANIP6_DUMMY + else + newsrc=$ROUTER1_WANIP_DUMMY + newdst=$ROUTER2_WANIP_DUMMY + fi + export RUMP_SERVER=$SOCK1 + $DEBUG && rump.ifconfig -v ipsec0 + atf_check -s exit:0 \ + rump.ifconfig ipsec0 tunnel ${newsrc} ${newdst} + $DEBUG && rump.ifconfig -v ipsec0 + + if [ ${mode} = "ipv6" ]; then + newsrc=$ROUTER2_WANIP6_DUMMY + newdst=$ROUTER1_WANIP6_DUMMY + else + newsrc=$ROUTER2_WANIP_DUMMY + newdst=$ROUTER1_WANIP_DUMMY + fi + export RUMP_SERVER=$SOCK2 + $DEBUG && rump.ifconfig -v ipsec0 + atf_check -s exit:0 \ + rump.ifconfig ipsec0 tunnel ${newsrc} ${newdst} + $DEBUG && rump.ifconfig -v ipsec0 + + unset RUMP_SERVER +} + +basic_setup() +{ + local inner=$1 + local outer=$2 + local proto=$3 + local algo=$4 + + setup ${inner} ${outer} + test_setup ${inner} ${outer} + + # Enable once PR kern/49219 is fixed + #test_ping_failure + + setup_tunnel ${inner} ${outer} ${proto} ${algo} + sleep 1 + test_setup_tunnel ${inner} +} + +basic_test() +{ + local inner=$1 + local outer=$2 # not use + + test_ping_success ${inner} +} + +basic_teardown() +{ + local inner=$1 + local outer=$2 # not use + + teardown_tunnel + test_ping_failure ${inner} +} + +ioctl_setup() +{ + local inner=$1 + local outer=$2 + local proto=$3 + local algo=$4 + + setup ${inner} ${outer} + test_setup ${inner} ${outer} + + # Enable once PR kern/49219 is fixed + #test_ping_failure + + setup_tunnel ${inner} ${outer} ${proto} ${algo} + setup_dummy_tunnel ${inner} ${outer} ${proto} ${algo} + sleep 1 + test_setup_tunnel ${inner} +} + +ioctl_test() +{ + local inner=$1 + local outer=$2 + + test_ping_success ${inner} + + test_change_tunnel_duplicate ${outer} + + teardown_dummy_tunnel + test_change_tunnel_success ${outer} +} + +ioctl_teardown() +{ + local inner=$1 + local outer=$2 # not use + + teardown_tunnel + test_ping_failure ${inner} +} + +recursive_setup() +{ + local inner=$1 + local outer=$2 + local proto=$3 + local algo=$4 + + setup ${inner} ${outer} + test_setup ${inner} ${outer} + + # Enable once PR kern/49219 is fixed + #test_ping_failure + + setup_tunnel ${inner} ${outer} ${proto} ${algo} + setup_recursive_tunnels ${inner} ${proto} ${algo} + sleep 1 + test_setup_tunnel ${inner} +} + +recursive_test() +{ + local inner=$1 + local outer=$2 # not use + + test_recursive_check ${inner} +} + +recursive_teardown() +{ + local inner=$1 # not use + local outer=$2 # not use + + teardown_recursive_tunnels + teardown_tunnel +} + +add_test() +{ + local category=$1 + local desc=$2 + local inner=$3 + local outer=$4 + local proto=$5 + local algo=$6 + local _algo=$(echo $algo | sed 's/-//g') + + name="ipsecif_${category}_${inner}over${outer}_${proto}_${_algo}" + fulldesc="Does ${inner} over ${outer} if_ipsec ${desc}" + + atf_test_case ${name} cleanup + eval "${name}_head() { + atf_set descr \"${fulldesc}\" + atf_set require.progs rump_server setkey + } + ${name}_body() { + ${category}_setup ${inner} ${outer} ${proto} ${algo} + ${category}_test ${inner} ${outer} + ${category}_teardown ${inner} ${outer} + rump_server_destroy_ifaces + } + ${name}_cleanup() { + \$DEBUG && dump + cleanup + }" + atf_add_test_case ${name} +} + +add_test_allproto() +{ + local category=$1 + local desc=$2 + + for algo in $ESP_ENCRYPTION_ALGORITHMS_MINIMUM; do + add_test ${category} "${desc}" ipv4 ipv4 esp $algo + add_test ${category} "${desc}" ipv4 ipv6 esp $algo + add_test ${category} "${desc}" ipv6 ipv4 esp $algo + add_test ${category} "${desc}" ipv6 ipv6 esp $algo + done + + # ah does not support yet +} + +atf_init_test_cases() +{ + + atf_add_test_case ipsecif_create_destroy + + add_test_allproto basic "basic tests" + add_test_allproto ioctl "ioctl tests" + add_test_allproto recursive "recursive check tests" +} diff --git a/net/if_ipsec/t_ipsec_natt.sh b/net/if_ipsec/t_ipsec_natt.sh new file mode 100644 --- /dev/null +++ b/net/if_ipsec/t_ipsec_natt.sh @@ -0,0 +1,524 @@ +# $NetBSD: t_ipsec_natt.sh,v 1.5 2020/06/05 03:24:58 knakahara Exp $ +# +# Copyright (c) 2018 Internet Initiative Japan Inc. +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +SOCK_LOCAL_A=unix://ipsec_natt_local_a +SOCK_LOCAL_B=unix://ipsec_natt_local_b +SOCK_NAT=unix://ipsec_natt_nat +SOCK_REMOTE=unix://ipsec_natt_remote +BUS_LOCAL=./bus_ipsec_natt_local +BUS_NAT=./bus_ipsec_natt_nat + +DEBUG=${DEBUG:-false} + +setup_servers() +{ + + rump_server_crypto_start $SOCK_LOCAL_A netipsec ipsec + rump_server_crypto_start $SOCK_LOCAL_B netipsec ipsec + rump_server_npf_start $SOCK_NAT + rump_server_crypto_start $SOCK_REMOTE netipsec ipsec + rump_server_add_iface $SOCK_LOCAL_A shmif0 $BUS_LOCAL + rump_server_add_iface $SOCK_LOCAL_B shmif0 $BUS_LOCAL + rump_server_add_iface $SOCK_NAT shmif0 $BUS_LOCAL + rump_server_add_iface $SOCK_NAT shmif1 $BUS_NAT + rump_server_add_iface $SOCK_REMOTE shmif0 $BUS_NAT +} + +setup_ipsecif() +{ + local sock=$1 + local ifid=$2 + local src_ip=$3 + local src_port=$4 + local dst_ip=$5 + local dst_port=$6 + local ipsecif_ip=$7 + local peer_ip=$8 + + export RUMP_SERVER=$sock + rump_server_add_iface $sock ipsec$ifid + atf_check -s exit:0 rump.ifconfig ipsec$ifid link0 # enable NAT-T + atf_check -s exit:0 rump.ifconfig ipsec$ifid tunnel ${src_ip},${src_port} ${dst_ip},${dst_port} + atf_check -s exit:0 rump.ifconfig ipsec$ifid ${ipsecif_ip}/32 + atf_check -s exit:0 -o ignore \ + rump.route -n add ${peer_ip}/32 $ipsecif_ip +} + +add_sa() +{ + local sock=$1 + local proto=$2 + local algo_args="$3" + local src_ip=$4 + local src_port=$5 + local dst_ip=$6 + local dst_port=$7 + local out_spi=$8 + local in_spi=$9 + local tmpfile=./tmp + + export RUMP_SERVER=$sock + cat > $tmpfile <<-EOF + add $src_ip [$src_port] $dst_ip [$dst_port] $proto $out_spi -m transport $algo_args; + add $dst_ip [$dst_port] $src_ip [$src_port] $proto $in_spi -m transport $algo_args; + EOF + $DEBUG && cat $tmpfile + atf_check -s exit:0 -o empty $HIJACKING setkey -c < $tmpfile + # XXX it can be expired if $lifetime is very short + #check_sa_entries $SOCK_LOCAL $ip_local $ip_remote +} + +prepare_file() +{ + local file=$1 + local data="0123456789" + + touch $file + for i in `seq 1 512` + do + echo $data >> $file + done +} + +build_npf_conf() +{ + local outfile=$1 + local localnet=$2 + + cat > $outfile <<-EOF + set bpf.jit off + \$int_if = inet4(shmif0) + \$ext_if = inet4(shmif1) + \$localnet = { $localnet } + map \$ext_if dynamic \$localnet -> \$ext_if + group "external" on \$ext_if { + pass stateful out final all + } + group "internal" on \$int_if { + block in all + pass in final from \$localnet + pass out final all + } + group default { + pass final on lo0 all + block all + } + EOF +} + +PIDSFILE=./terminator.pids +start_natt_terminator() +{ + local sock=$1 + local ip=$2 + local port=$3 + local pidsfile=$4 + local backup=$RUMP_SERVER + local pid= + local terminator="$(atf_get_srcdir)/../ipsec/natt_terminator" + + export RUMP_SERVER=$sock + + env LD_PRELOAD=/usr/lib/librumphijack.so \ + $terminator $ip $port & + pid=$! + if [ ! -f $PIDSFILE ]; then + touch $PIDSFILE + fi + echo $pid >> $PIDSFILE + + $DEBUG && rump.netstat -a -f inet + + export RUMP_SERVER=$backup + + sleep 1 +} + +stop_natt_terminators() +{ + local pid= + + if [ ! -f $PIDSFILE ]; then + return + fi + + for pid in $(cat $PIDSFILE); do + kill -9 $pid + done + rm -f $PIDSFILE +} + +check_ping_packets() +{ + local sock=$1 + local bus=$2 + local from_ip=$3 + local to_ip=$4 + + local outfile=./out.ping + + extract_new_packets $bus > $outfile + + export RUMP_SERVER=$sock + atf_check -s exit:0 -o ignore rump.ping -c 1 -n -w 3 $to_ip + + extract_new_packets $bus > $outfile + $DEBUG && cat $outfile + atf_check -s exit:0 \ + -o match:"$from_ip > $to_ip: ICMP echo request" \ + cat $outfile + atf_check -s exit:0 \ + -o match:"$to_ip > $from_ip: ICMP echo reply" \ + cat $outfile +} + +check_ping_packets_over_ipsecif() +{ + local sock=$1 + local bus=$2 + local to_ip=$3 + local nat_from_ip=$4 + local nat_from_port=$5 + local nat_to_ip=$6 + local nat_to_port=$7 + + local outfile=./out.ping_over_ipsecif + + extract_new_packets $bus > $outfile + + export RUMP_SERVER=$sock + atf_check -s exit:0 -o ignore rump.ping -c 1 -n -w 7 $to_ip + + # Check both ports and UDP encapsulation + extract_new_packets $bus > $outfile + $DEBUG && cat $outfile + atf_check -s exit:0 \ + -o match:"${nat_from_ip}\.$nat_from_port > ${nat_to_ip}\.${nat_to_port}: UDP-encap" \ + cat $outfile + atf_check -s exit:0 \ + -o match:"${nat_to_ip}\.${nat_to_port} > ${nat_from_ip}\.${nat_from_port}: UDP-encap" \ + cat $outfile +} + +check_tcp_com_prepare() +{ + local server_sock=$1 + local client_sock=$2 + local bus=$3 + local to_ip=$4 + local nat_from_ip=$5 + local nat_to_ip=$6 + + local outfile=./out.prepare + local file_send=./file.send.prepare + local file_recv=./file.recv.prepare + + extract_new_packets $bus > $outfile + + start_nc_server $server_sock 4501 $file_recv ipv4 + + prepare_file $file_send + export RUMP_SERVER=$client_sock + atf_check -s exit:0 $HIJACKING nc -w 3 $to_ip 4501 < $file_send + atf_check -s exit:0 diff -q $file_send $file_recv + extract_new_packets $bus > $outfile + $DEBUG && cat $outfile + atf_check -s exit:0 \ + -o match:"${nat_from_ip}\.[0-9]+ > ${nat_to_ip}\.4501" \ + cat $outfile + atf_check -s exit:0 \ + -o match:"${nat_to_ip}\.4501 > ${nat_from_ip}\.[0-9]+" \ + cat $outfile + + stop_nc_server +} + +check_tcp_com_over_ipsecif() +{ + local server_sock=$1 + local client_sock=$2 + local bus=$3 + local to_ip=$4 + local nat_from_ip=$5 + local nat_from_port=$6 + local nat_to_ip=$7 + local nat_to_port=$8 + + local outfile=./out.ipsecif + local file_send=./file.send.ipsecif + local file_recv=./file.recv.ipsecif + + extract_new_packets $bus > $outfile + + start_nc_server $server_sock 4501 $file_recv ipv4 + prepare_file $file_send + export RUMP_SERVER=$client_sock + atf_check -s exit:0 -o ignore $HIJACKING nc -w 7 $to_ip 4501 < $file_send + atf_check -s exit:0 diff -q $file_send $file_recv + stop_nc_server + + # Check both ports and UDP encapsulation + extract_new_packets $bus > $outfile + $DEBUG && cat $outfile + atf_check -s exit:0 \ + -o match:"${nat_from_ip}\.$nat_from_port > ${nat_to_ip}\.${nat_to_port}: UDP-encap" \ + cat $outfile + atf_check -s exit:0 \ + -o match:"${nat_to_ip}\.${nat_to_port} > ${nat_from_ip}\.${nat_from_port}: UDP-encap" \ + cat $outfile +} + +test_ipsecif_natt_transport() +{ + local algo=$1 + local ip_local_a=192.168.0.2 + local ip_local_b=192.168.0.3 + local ip_nat_local=192.168.0.1 + local ip_nat_remote=10.0.0.1 + local ip_remote=10.0.0.2 + local subnet_local=192.168.0.0 + local ip_local_ipsecif_a=172.16.100.1 + local ip_local_ipsecif_b=172.16.110.1 + local ip_remote_ipsecif_a=172.16.10.1 + local ip_remote_ipsecif_b=172.16.11.1 + + local npffile=./npf.conf + local file_send=./file.send + local algo_args="$(generate_algo_args esp-udp $algo)" + local pid= port_a= port_b= + + setup_servers + + export RUMP_SERVER=$SOCK_LOCAL_A + atf_check -s exit:0 rump.sysctl -q -w net.inet.ip.dad_count=0 + atf_check -s exit:0 rump.ifconfig shmif0 $ip_local_a/24 + atf_check -s exit:0 -o ignore \ + rump.route -n add default $ip_nat_local + + export RUMP_SERVER=$SOCK_LOCAL_B + atf_check -s exit:0 rump.sysctl -q -w net.inet.ip.dad_count=0 + atf_check -s exit:0 rump.ifconfig shmif0 $ip_local_b/24 + atf_check -s exit:0 -o ignore \ + rump.route -n add default $ip_nat_local + + export RUMP_SERVER=$SOCK_NAT + atf_check -s exit:0 rump.sysctl -q -w net.inet.ip.dad_count=0 + atf_check -s exit:0 rump.ifconfig shmif0 $ip_nat_local/24 + atf_check -s exit:0 rump.ifconfig shmif1 $ip_nat_remote/24 + atf_check -s exit:0 rump.sysctl -q -w net.inet.ip.forwarding=1 + + export RUMP_SERVER=$SOCK_REMOTE + atf_check -s exit:0 rump.sysctl -q -w net.inet.ip.dad_count=0 + atf_check -s exit:0 rump.ifconfig shmif0 $ip_remote/24 + atf_check -s exit:0 -o ignore \ + rump.route -n add -net $subnet_local $ip_nat_remote + + # There is no NAT/NAPT. ping should just work. + check_ping_packets $SOCK_LOCAL_A $BUS_NAT $ip_local_a $ip_remote + check_ping_packets $SOCK_LOCAL_B $BUS_NAT $ip_local_b $ip_remote + + # Setup an NAPT with npf + build_npf_conf $npffile "$subnet_local/24" + + export RUMP_SERVER=$SOCK_NAT + atf_check -s exit:0 $HIJACKING_NPF npfctl reload $npffile + atf_check -s exit:0 $HIJACKING_NPF npfctl start + $DEBUG && ${HIJACKING},"blanket=/dev/npf" npfctl show + + # There is an NAPT. ping works but source IP/port are translated + check_ping_packets $SOCK_LOCAL_A $BUS_NAT $ip_nat_remote $ip_remote + check_ping_packets $SOCK_LOCAL_B $BUS_NAT $ip_nat_remote $ip_remote + + # Try TCP communications just in case + check_tcp_com_prepare $SOCK_REMOTE $SOCK_LOCAL_A $BUS_NAT \ + $ip_remote $ip_nat_remote $ip_remote + check_tcp_com_prepare $SOCK_REMOTE $SOCK_LOCAL_B $BUS_NAT \ + $ip_remote $ip_nat_remote $ip_remote + + # Launch a nc server as a terminator of NAT-T on outside the NAPT + start_natt_terminator $SOCK_REMOTE $ip_remote 4500 + echo zzz > $file_send + + #################### Test for primary ipsecif(4) NAT-T. + + export RUMP_SERVER=$SOCK_LOCAL_A + # Send a UDP packet to the remote server at port 4500 from the local + # host of port 4500. This makes a mapping on the NAPT between them + atf_check -s exit:0 $HIJACKING \ + nc -u -w 3 -p 4500 $ip_remote 4500 < $file_send + # Launch a nc server as a terminator of NAT-T on inside the NAPT, + # taking over port 4500 of the local host. + start_natt_terminator $SOCK_LOCAL_A $ip_local_a 4500 + + # We need to keep the servers for NAT-T + + export RUMP_SERVER=$SOCK_LOCAL_A + $DEBUG && rump.netstat -na -f inet + export RUMP_SERVER=$SOCK_REMOTE + $DEBUG && rump.netstat -na -f inet + + # Get a translated port number from 4500 on the NAPT + export RUMP_SERVER=$SOCK_NAT + $DEBUG && $HIJACKING_NPF npfctl list + # 192.168.0.2:4500 10.0.0.2:4500 via shmif1:65248 + port_a=$(get_natt_port $ip_local_a $ip_nat_remote) + $DEBUG && echo port_a=$port_a + if [ -z "$port_a" ]; then + atf_fail "Failed to get a translated port on NAPT" + fi + + # Setup ESP-UDP ipsecif(4) for first client under NAPT + setup_ipsecif $SOCK_LOCAL_A 0 $ip_local_a 4500 $ip_remote 4500 \ + $ip_local_ipsecif_a $ip_remote_ipsecif_a + setup_ipsecif $SOCK_REMOTE 0 $ip_remote 4500 $ip_nat_remote $port_a \ + $ip_remote_ipsecif_a $ip_local_ipsecif_a + + add_sa $SOCK_LOCAL_A "esp-udp" "$algo_args" \ + $ip_local_a 4500 $ip_remote 4500 10000 10001 + add_sa $SOCK_REMOTE "esp-udp" "$algo_args" \ + $ip_remote 4500 $ip_nat_remote $port_a 10001 10000 + + export RUMP_SERVER=$SOCK_LOCAL_A + # ping should still work + atf_check -s exit:0 -o ignore rump.ping -c 1 -n -w 3 $ip_remote + + # Try ping over the ESP-UDP ipsecif(4) + check_ping_packets_over_ipsecif $SOCK_LOCAL_A $BUS_NAT \ + $ip_remote_ipsecif_a $ip_nat_remote $port_a $ip_remote 4500 + + # Try TCP communications over the ESP-UDP ipsecif(4) + check_tcp_com_over_ipsecif $SOCK_REMOTE $SOCK_LOCAL_A $BUS_NAT \ + $ip_remote_ipsecif_a $ip_nat_remote $port_a $ip_remote 4500 + + #################### Test for secondary ipsecif(4) NAT-T. + + export RUMP_SERVER=$SOCK_REMOTE + $HIJACKING setkey -D + $HIJACKING setkey -DP + + export RUMP_SERVER=$SOCK_LOCAL_B + # Send a UDP packet to the remote server at port 4500 from the local + # host of port 4500. This makes a mapping on the NAPT between them + atf_check -s exit:0 $HIJACKING \ + nc -u -w 3 -p 4500 $ip_remote 4500 < $file_send + # Launch a nc server as a terminator of NAT-T on inside the NAPT, + # taking over port 4500 of the local host. + start_natt_terminator $SOCK_LOCAL_B $ip_local_b 4500 + + # We need to keep the servers for NAT-T + + export RUMP_SERVER=$SOCK_LOCAL_B + $DEBUG && rump.netstat -na -f inet + export RUMP_SERVER=$SOCK_REMOTE + $DEBUG && rump.netstat -na -f inet + + # Get a translated port number from 4500 on the NAPT + export RUMP_SERVER=$SOCK_NAT + $DEBUG && $HIJACKING_NPF npfctl list + # 192.168.0.2:4500 10.0.0.2:4500 via shmif1:65248 + port_b=$(get_natt_port $ip_local_b $ip_nat_remote) + $DEBUG && echo port_b=$port_b + if [ -z "$port_b" ]; then + atf_fail "Failed to get a translated port on NAPT" + fi + + # Setup ESP-UDP ipsecif(4) for first client under NAPT + setup_ipsecif $SOCK_LOCAL_B 0 $ip_local_b 4500 $ip_remote 4500 \ + $ip_local_ipsecif_b $ip_remote_ipsecif_b + setup_ipsecif $SOCK_REMOTE 1 $ip_remote 4500 $ip_nat_remote $port_b \ + $ip_remote_ipsecif_b $ip_local_ipsecif_b + + check_ping_packets_over_ipsecif $SOCK_LOCAL_A $BUS_NAT \ + $ip_remote_ipsecif_a $ip_nat_remote $port_a $ip_remote 4500 + + add_sa $SOCK_LOCAL_B "esp-udp" "$algo_args" \ + $ip_local_b 4500 $ip_remote 4500 11000 11001 + add_sa $SOCK_REMOTE "esp-udp" "$algo_args" \ + $ip_remote 4500 $ip_nat_remote $port_b 11001 11000 + + export RUMP_SERVER=$SOCK_LOCAL_B + # ping should still work + atf_check -s exit:0 -o ignore rump.ping -c 1 -n -w 3 $ip_remote + + # Try ping over the ESP-UDP ipsecif(4) + check_ping_packets_over_ipsecif $SOCK_LOCAL_B $BUS_NAT \ + $ip_remote_ipsecif_b $ip_nat_remote $port_b $ip_remote 4500 + + + # Try TCP communications over the ESP-UDP ipsecif(4) + check_tcp_com_over_ipsecif $SOCK_REMOTE $SOCK_LOCAL_B $BUS_NAT \ + $ip_remote_ipsecif_b $ip_nat_remote $port_b $ip_remote 4500 + + # Try ping over the ESP-UDP ipsecif(4) for primary again + check_ping_packets_over_ipsecif $SOCK_LOCAL_A $BUS_NAT \ + $ip_remote_ipsecif_a $ip_nat_remote $port_a $ip_remote 4500 + + # Try TCP communications over the ESP-UDP ipsecif(4) for primary again + check_tcp_com_over_ipsecif $SOCK_REMOTE $SOCK_LOCAL_A $BUS_NAT \ + $ip_remote_ipsecif_a $ip_nat_remote $port_a $ip_remote 4500 + + # Kill the NAT-T terminator + stop_natt_terminators +} + +add_test_ipsecif_natt_transport() +{ + local algo=$1 + local _algo=$(echo $algo | sed 's/-//g') + local name= desc= + + desc="Test ipsecif(4) NAT-T ($algo)" + name="ipsecif_natt_transport_${_algo}" + + atf_test_case ${name} cleanup + eval " + ${name}_head() { + atf_set descr \"$desc\" + atf_set require.progs rump_server setkey nc + } + ${name}_body() { + test_ipsecif_natt_transport $algo + rump_server_destroy_ifaces + } + ${name}_cleanup() { + stop_nc_server + stop_natt_terminators + \$DEBUG && dump + cleanup + } + " + atf_add_test_case ${name} +} + +atf_init_test_cases() +{ + local algo= + + for algo in $ESP_ENCRYPTION_ALGORITHMS_MINIMUM; do + add_test_ipsecif_natt_transport $algo + done +} diff --git a/net/if_ipsec/t_ipsec_pfil.sh b/net/if_ipsec/t_ipsec_pfil.sh new file mode 100644 --- /dev/null +++ b/net/if_ipsec/t_ipsec_pfil.sh @@ -0,0 +1,364 @@ +# $NetBSD: t_ipsec_pfil.sh,v 1.3 2020/08/05 01:10:50 knakahara Exp $ +# +# Copyright (c) 2019 Internet Initiative Japan Inc. +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +SOCK_ROUTER1=unix://router1 +SOCK_ROUTER2=unix://router2 +ROUTER1_LANIP=192.168.1.1 +ROUTER1_LANNET=192.168.1.0/24 +ROUTER1_WANIP=10.0.0.1 +ROUTER1_IPSECIP=172.16.1.1 +ROUTER2_LANIP=192.168.2.1 +ROUTER2_LANNET=192.168.2.0/24 +ROUTER2_WANIP=10.0.0.2 +ROUTER2_IPSECIP=172.16.2.1 + +DEBUG=${DEBUG:-false} +TIMEOUT=7 +HIJACKING_NPF="${HIJACKING},blanket=/dev/npf" + +setup_router() +{ + local sock=$1 + local lan=$2 + local wan=$3 + + rump_server_add_iface $sock shmif0 bus0 + rump_server_add_iface $sock shmif1 bus1 + + export RUMP_SERVER=${sock} + atf_check -s exit:0 rump.sysctl -q -w net.inet.ip.dad_count=0 + + atf_check -s exit:0 rump.ifconfig shmif0 inet ${lan} netmask 0xffffff00 + atf_check -s exit:0 rump.ifconfig shmif0 up + # Ensure shmif0 is running + atf_check -s exit:0 -o ignore rump.ping -n -c 1 -w $TIMEOUT ${lan} + $DEBUG && rump.ifconfig shmif0 + + atf_check -s exit:0 rump.ifconfig shmif1 inet ${wan} netmask 0xff000000 + atf_check -s exit:0 rump.ifconfig shmif1 up + # Ensure shmif1 is running + atf_check -s exit:0 -o ignore rump.ping -n -c 1 -w $TIMEOUT ${wan} + $DEBUG && rump.ifconfig shmif1 + + unset RUMP_SERVER +} + +setup_if_ipsec() +{ + local addr=$1 + local remote=$2 + local src=$3 + local dst=$4 + local peernet=$5 + + rump_server_add_iface $RUMP_SERVER ipsec0 + atf_check -s exit:0 rump.ifconfig ipsec0 tunnel $src $dst + atf_check -s exit:0 rump.ifconfig ipsec0 inet ${addr}/32 $remote + atf_check -s exit:0 -o ignore rump.route add -inet $peernet $addr + + $DEBUG && rump.ifconfig ipsec0 + $DEBUG && rump.route -nL show -inet +} + +get_if_ipsec_unique() +{ + local src=$1 + local proto=$2 + local unique="" + + unique=`$HIJACKING setkey -DP | grep -A2 "^${src}.*(${proto})$" | grep unique | sed 's/.*unique#//'` + + echo $unique +} + +setup_if_ipsec_sa() +{ + local src=$1 + local dst=$2 + local inid=$3 + local outid=$4 + local proto=$5 + local algo=$6 + + local tmpfile=./tmp + local inunique="" + local outunique="" + local algo_args="$(generate_algo_args $proto $algo)" + + inunique=`get_if_ipsec_unique $dst "ipv4"` + atf_check -s exit:0 test "X$inunique" != "X" + outunique=`get_if_ipsec_unique $src "ipv4"` + atf_check -s exit:0 test "X$outunique" != "X" + + cat > $tmpfile <<-EOF + add $dst $src $proto $inid -u $inunique -m transport $algo_args; + add $src $dst $proto $outid -u $outunique -m transport $algo_args; + EOF + $DEBUG && cat $tmpfile + atf_check -s exit:0 -o empty $HIJACKING setkey -c < $tmpfile + $DEBUG && $HIJACKING setkey -D + $DEBUG && $HIJACKING setkey -DP +} + +setup_tunnel() +{ + local proto=$1 + local algo=$2 + + local addr= remote= src= dst= peernet= + + export RUMP_SERVER=$SOCK_ROUTER1 + addr=$ROUTER1_IPSECIP + remote=$ROUTER2_IPSECIP + src=$ROUTER1_WANIP + dst=$ROUTER2_WANIP + peernet=$ROUTER2_LANNET + setup_if_ipsec $addr $remote $src $dst $peernet + setup_if_ipsec_sa $src $dst "10000" "10001" $proto $algo + + export RUMP_SERVER=$SOCK_ROUTER2 + addr=$ROUTER2_IPSECIP + remote=$ROUTER1_IPSECIP + src=$ROUTER2_WANIP + dst=$ROUTER1_WANIP + peernet=$ROUTER1_LANNET + setup_if_ipsec $addr $remote $src $dst $peernet + setup_if_ipsec_sa $src $dst "10001" "10000" $proto $algo + + # Ensure ipsecif(4) settings have completed. + export RUMP_SERVER=$SOCK_ROUTER1 + atf_check -s exit:0 -o ignore \ + rump.ping -n -w $TIMEOUT -c 1 -I $ROUTER1_LANIP \ + $ROUTER2_LANIP + + export RUMP_SERVER=$SOCK_ROUTER2 + atf_check -s exit:0 -o ignore \ + rump.ping -n -w $TIMEOUT -c 1 -I $ROUTER2_LANIP \ + $ROUTER1_LANIP + + unset RUMP_SERVER +} + +ipsecif_pfil_setup() +{ + local proto=$1 + local algo=$2 + + rump_server_crypto_npf_start $SOCK_ROUTER1 netipsec ipsec + rump_server_crypto_npf_start $SOCK_ROUTER2 netipsec ipsec + + setup_router $SOCK_ROUTER1 $ROUTER1_LANIP $ROUTER1_WANIP + setup_router $SOCK_ROUTER2 $ROUTER2_LANIP $ROUTER2_WANIP + + setup_tunnel $proto $algo +} + +prepare_file() +{ + local file=$1 + local data="0123456789" + + touch $file + for i in `seq 1 512` + do + echo $data >> $file + done +} + +build_npf_conf() +{ + local outfile=$1 + local subnet=$2 + local direction=$3 + + local reverse= + if [ "X${direction}" = "Xin" ] ; then + reverse="out" + else + reverse="in" + fi + + cat > $outfile <<-EOF + set bpf.jit off + \$if = inet4(ipsec0) + \$subnet = { $subnet } + + procedure "log0" { + log: npflog0 + } + + group default { + block $direction on \$if proto tcp from \$subnet apply "log0" + pass $reverse on \$if proto tcp from \$subnet + pass in on \$if proto icmp from 0.0.0.0/0 + pass out on \$if proto icmp from 0.0.0.0/0 + pass final on shmif0 all + pass final on shmif1 all + } + EOF +} + +ipsecif_pfil_test() +{ + local outfile=./out + local npffile=./npf.conf + local file_send=./file.send + local file_recv=./file.recv + + local subnet="172.16.0.0/16" + + # Try TCP communications just in case. + start_nc_server $SOCK_ROUTER2 8888 $file_recv ipv4 + prepare_file $file_send + export RUMP_SERVER=$SOCK_ROUTER1 + atf_check -s exit:0 $HIJACKING nc -w 3 $ROUTER2_IPSECIP 8888 < $file_send + atf_check -s exit:0 diff -q $file_send $file_recv + stop_nc_server + + # Setup npf to block *out* direction for ipsecif(4). + build_npf_conf $npffile $subnet "out" + $DEBUG && cat $npffile + + export RUMP_SERVER=$SOCK_ROUTER1 + atf_check -s exit:0 $HIJACKING_NPF npfctl reload $npffile + atf_check -s exit:0 $HIJACKING_NPF npfctl start + $DEBUG && ${HIJACKING},"blanket=/dev/npf" npfctl show + + # ping should still work + export RUMP_SERVER=$SOCK_ROUTER1 + atf_check -s exit:0 -o ignore \ + rump.ping -n -w $TIMEOUT -c 1 -I $ROUTER1_LANIP \ + $ROUTER2_LANIP + + export RUMP_SERVER=$SOCK_ROUTER2 + atf_check -s exit:0 -o ignore \ + rump.ping -n -w $TIMEOUT -c 1 -I $ROUTER2_LANIP \ + $ROUTER1_LANIP + + # TCP communications should be blocked. + start_nc_server $SOCK_ROUTER2 8888 $file_recv ipv4 + prepare_file $file_send + export RUMP_SERVER=$SOCK_ROUTER1 + atf_check -s exit:1 -o ignore $HIJACKING nc -w 3 $ROUTER2_IPSECIP 8888 < $file_send + stop_nc_server + + atf_check -s exit:0 $HIJACKING_NPF npfctl stop + $DEBUG && ${HIJACKING},"blanket=/dev/npf" npfctl show + + # Setup npf to block *in* direction for ipsecif(4). + build_npf_conf $npffile $subnet "in" + $DEBUG && cat $npffile + + export RUMP_SERVER=$SOCK_ROUTER2 + atf_check -s exit:0 $HIJACKING_NPF npfctl reload $npffile + atf_check -s exit:0 $HIJACKING_NPF npfctl start + $DEBUG && ${HIJACKING},"blanket=/dev/npf" npfctl show + + # ping should still work. + export RUMP_SERVER=$SOCK_ROUTER1 + atf_check -s exit:0 -o ignore \ + rump.ping -n -w $TIMEOUT -c 1 -I $ROUTER1_LANIP \ + $ROUTER2_LANIP + + export RUMP_SERVER=$SOCK_ROUTER2 + atf_check -s exit:0 -o ignore \ + rump.ping -n -w $TIMEOUT -c 1 -I $ROUTER2_LANIP \ + $ROUTER1_LANIP + + # TCP communications should be blocked. + start_nc_server $SOCK_ROUTER2 8888 $file_recv ipv4 + prepare_file $file_send + export RUMP_SERVER=$SOCK_ROUTER1 + atf_check -s exit:1 -o ignore $HIJACKING nc -w 3 $ROUTER2_IPSECIP 8888 < $file_send + stop_nc_server + + atf_check -s exit:0 $HIJACKING_NPF npfctl stop + $DEBUG && ${HIJACKING},"blanket=/dev/npf" npfctl show + + + unset RUMP_SERVER +} + +ipsecif_pfil_teardown() +{ + + export RUMP_SERVER=$SOCK_ROUTER1 + atf_check -s exit:0 rump.ifconfig ipsec0 deletetunnel + atf_check -s exit:0 rump.ifconfig ipsec0 destroy + $HIJACKING setkey -F + + export RUMP_SERVER=$SOCK_ROUTER2 + atf_check -s exit:0 rump.ifconfig ipsec0 deletetunnel + atf_check -s exit:0 rump.ifconfig ipsec0 destroy + $HIJACKING setkey -F + + unset RUMP_SERVER +} + +add_test() +{ + local proto=$1 + local algo=$2 + local _algo=$(echo $algo | sed 's/-//g') + + name="ipsecif_pfil_${proto}_${_algo}" + desc="Does ipsecif filter tests" + + atf_test_case ${name} cleanup + eval "${name}_head() { + atf_set descr \"${desc}\" + atf_set require.progs rump_server setkey + } + ${name}_body() { + ipsecif_pfil_setup ${proto} ${algo} + ipsecif_pfil_test + ipsecif_pfil_teardown + rump_server_destroy_ifaces + } + ${name}_cleanup() { + \$DEBUG && dump + cleanup + }" + atf_add_test_case ${name} +} + +add_test_allalgo() +{ + local desc=$1 + + for algo in $ESP_ENCRYPTION_ALGORITHMS_MINIMUM; do + add_test esp $algo + done + + # ah does not support yet +} + +atf_init_test_cases() +{ + + add_test_allalgo ipsecif_pfil +} diff --git a/net/if_l2tp/Makefile b/net/if_l2tp/Makefile new file mode 100644 --- /dev/null +++ b/net/if_l2tp/Makefile @@ -0,0 +1,13 @@ +# $NetBSD: Makefile,v 1.1 2017/02/16 08:44:47 knakahara Exp $ +# + +.include + +TESTSDIR= ${TESTSBASE}/net/if_l2tp + +.for name in l2tp +TESTS_SH+= t_${name} +TESTS_SH_SRC_t_${name}= ../net_common.sh t_${name}.sh +.endfor + +.include diff --git a/net/if_l2tp/t_l2tp.sh b/net/if_l2tp/t_l2tp.sh new file mode 100644 --- /dev/null +++ b/net/if_l2tp/t_l2tp.sh @@ -0,0 +1,466 @@ +# $NetBSD: t_l2tp.sh,v 1.5 2019/08/19 03:22:05 ozaki-r Exp $ +# +# Copyright (c) 2017 Internet Initiative Japan Inc. +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +LAC1SOCK=unix://commsock1 +LAC2SOCK=unix://commsock2 +CLIENT1SOCK=unix://commsock3 +CLIENT2SOCK=unix://commsock4 + +WAN_LINK=bus0 +LAC1_LAN_LINK=bus1 +LAC2_LAN_LINK=bus2 + +LAC1_WANIP=10.0.0.1 +LAC1_SESSION=1234 +CLIENT1_LANIP=192.168.1.1 +LAC2_WANIP=10.0.0.2 +LAC2_SESSION=4321 +CLIENT2_LANIP=192.168.1.2 + +LAC1_WANIP6=fc00::1 +CLIENT1_LANIP6=fc00:1::1 +LAC2_WANIP6=fc00::2 +CLIENT2_LANIP6=fc00:1::2 + +TIMEOUT=5 +DEBUG=${DEBUG:-false} + +atf_test_case l2tp_create_destroy cleanup +l2tp_create_destroy_head() +{ + + atf_set "descr" "Test creating/destroying l2tp interfaces" + atf_set "require.progs" "rump_server" +} + +l2tp_create_destroy_body() +{ + + rump_server_start $LAC1SOCK l2tp + + test_create_destroy_common $LAC1SOCK l2tp0 +} + +l2tp_create_destroy_cleanup() +{ + + $DEBUG && dump + cleanup +} + +setup_lac() +{ + sock=${1} + lanlink=${2} + wan=${3} + wan_mode=${4} + + + rump_server_add_iface ${sock} shmif0 ${lanlink} + rump_server_add_iface ${sock} shmif1 ${WAN_LINK} + + export RUMP_SERVER=${sock} + + if [ ${wan_mode} = "ipv6" ]; then + atf_check -s exit:0 rump.ifconfig shmif1 inet6 ${wan} + else + atf_check -s exit:0 rump.ifconfig shmif1 inet ${wan} netmask 0xff000000 + fi + atf_check -s exit:0 rump.ifconfig shmif0 up + atf_check -s exit:0 rump.ifconfig shmif1 up + + unset RUMP_SERVER +} + +test_lac() +{ + sock=${1} + wan=${2} + wan_mode=${3} + + export RUMP_SERVER=${sock} + + atf_check -s exit:0 -o match:shmif0 rump.ifconfig + atf_check -s exit:0 -o match:shmif1 rump.ifconfig + if [ ${wan_mode} = "ipv6" ]; then + atf_check -s exit:0 -o ignore rump.ping6 -n -c 1 -X $TIMEOUT ${wan} + else + atf_check -s exit:0 -o ignore rump.ping -n -c 1 -w $TIMEOUT ${wan} + fi + + unset RUMP_SERVER +} + +setup_client() +{ + sock=${1} + lanlink=${2} + lan=${3} + lan_mode=${4} + + rump_server_add_iface ${sock} shmif0 ${lanlink} + + export RUMP_SERVER=${sock} + if [ ${lan_mode} = "ipv6" ]; then + atf_check -s exit:0 rump.ifconfig shmif0 inet6 ${lan} + else + atf_check -s exit:0 rump.ifconfig shmif0 inet ${lan} netmask 0xffffff00 + fi + atf_check -s exit:0 rump.ifconfig shmif0 up + + unset RUMP_SERVER +} + +test_client() +{ + sock=${1} + lan=${2} + lan_mode=${3} + + export RUMP_SERVER=${sock} + + atf_check -s exit:0 -o match:shmif0 rump.ifconfig + if [ ${lan_mode} = "ipv6" ]; then + atf_check -s exit:0 -o ignore rump.ping6 -n -c 1 -X $TIMEOUT ${lan} + else + atf_check -s exit:0 -o ignore rump.ping -n -c 1 -w $TIMEOUT ${lan} + fi + + unset RUMP_SERVER +} + +setup() +{ + lan_mode=${1} + wan_mode=${2} + + rump_server_start $LAC1SOCK netinet6 bridge l2tp + rump_server_start $LAC2SOCK netinet6 bridge l2tp + rump_server_start $CLIENT1SOCK netinet6 bridge l2tp + rump_server_start $CLIENT2SOCK netinet6 bridge l2tp + + client1_lan="" + client2_lan="" + if [ ${lan_mode} = "ipv6" ]; then + client1_lan=${CLIENT1_LANIP6} + client2_lan=${CLIENT2_LANIP6} + else + client1_lan=${CLIENT1_LANIP} + client2_lan=${CLIENT2_LANIP} + fi + + if [ ${wan_mode} = "ipv6" ]; then + setup_lac $LAC1SOCK $LAC1_LAN_LINK $LAC1_WANIP6 ${wan_mode} + setup_lac $LAC2SOCK $LAC2_LAN_LINK $LAC2_WANIP6 ${wan_mode} + setup_client $CLIENT1SOCK $LAC1_LAN_LINK \ + ${client1_lan} ${lan_mode} + setup_client $CLIENT2SOCK $LAC2_LAN_LINK \ + ${client2_lan} ${lan_mode} + else + setup_lac $LAC1SOCK $LAC1_LAN_LINK $LAC1_WANIP ${wan_mode} + setup_lac $LAC2SOCK $LAC2_LAN_LINK $LAC2_WANIP ${wan_mode} + setup_client $CLIENT1SOCK $LAC1_LAN_LINK \ + ${client1_lan} ${lan_mode} + setup_client $CLIENT2SOCK $LAC2_LAN_LINK \ + ${client2_lan} ${lan_mode} + fi +} + +test_setup() +{ + lan_mode=${1} + wan_mode=${2} + + client1_lan="" + client2_lan="" + if [ ${lan_mode} = "ipv6" ]; then + client1_lan=$CLIENT1_LANIP6 + client2_lan=$CLIENT2_LANIP6 + else + client1_lan=$CLIENT1_LANIP + client2_lan=$CLIENT2_LANIP + fi + if [ ${wan_mode} = "ipv6" ]; then + test_lac ${LAC1SOCK} $LAC1_WANIP6 ${wan_mode} + test_lac ${LAC2SOCK} $LAC2_WANIP6 ${wan_mode} + test_client ${CLIENT1SOCK} ${client1_lan} ${lan_mode} + test_client ${CLIENT2SOCK} ${client2_lan} ${lan_mode} + else + test_lac ${LAC1SOCK} $LAC1_WANIP ${wan_mode} + test_lac ${LAC2SOCK} $LAC2_WANIP ${wan_mode} + test_client ${CLIENT1SOCK} ${client1_lan} ${lan_mode} + test_client ${CLIENT2SOCK} ${client2_lan} ${lan_mode} + fi +} + +setup_if_l2tp() +{ + sock=${1} + src=${2} + dst=${3} + src_session=${4} + dst_session=${5} + + export RUMP_SERVER=${sock} + + rump_server_add_iface $sock l2tp0 + atf_check -s exit:0 rump.ifconfig l2tp0 tunnel ${src} ${dst} + atf_check -s exit:0 rump.ifconfig l2tp0 session ${src_session} ${dst_session} + atf_check -s exit:0 rump.ifconfig l2tp0 up + + rump_server_add_iface $sock bridge0 + atf_check -s exit:0 rump.ifconfig bridge0 up + export LD_PRELOAD=/usr/lib/librumphijack.so + atf_check -s exit:0 brconfig bridge0 add shmif0 + atf_check -s exit:0 brconfig bridge0 add l2tp0 + unset LD_PRELOAD + + $DEBUG && rump.ifconfig -v l2tp0 + $DEBUG && rump.ifconfig -v bridge0 + + unset RUMP_SERVER +} + +setup_tunnel() +{ + wan_mode=${1} + + src="" + dst="" + src_session="" + dst_session="" + + if [ ${wan_mode} = "ipv6" ]; then + src=$LAC1_WANIP6 + dst=$LAC2_WANIP6 + else + src=$LAC1_WANIP + dst=$LAC2_WANIP + fi + src_session=${LAC1_SESSION} + dst_session=${LAC2_SESSION} + setup_if_l2tp $LAC1SOCK ${src} ${dst} ${src_session} ${dst_session} + + if [ ${wan_mode} = "ipv6" ]; then + src=$LAC2_WANIP6 + dst=$LAC1_WANIP6 + else + src=$LAC2_WANIP + dst=$LAC1_WANIP + fi + src_session=${LAC2_SESSION} + dst_session=${LAC1_SESSION} + setup_if_l2tp $LAC2SOCK ${src} ${dst} ${src_session} ${dst_session} +} + +test_setup_tunnel() +{ + mode=${1} + + if [ ${mode} = "ipv6" ]; then + lac1_wan=$LAC1_WANIP6 + lac2_wan=$LAC2_WANIP6 + else + lac1_wan=$LAC1_WANIP + lac2_wan=$LAC2_WANIP + fi + export RUMP_SERVER=$LAC1SOCK + atf_check -s exit:0 -o match:l2tp0 rump.ifconfig + if [ ${mode} = "ipv6" ]; then + atf_check -s exit:0 -o ignore rump.ping6 -n -c 1 -X $TIMEOUT ${lac2_wan} + else + atf_check -s exit:0 -o ignore rump.ping -n -c 1 -w $TIMEOUT ${lac2_wan} + fi + + export RUMP_SERVER=$LAC2SOCK + atf_check -s exit:0 -o match:l2tp0 rump.ifconfig + if [ ${mode} = "ipv6" ]; then + atf_check -s exit:0 -o ignore rump.ping6 -n -c 1 -X $TIMEOUT ${lac1_wan} + else + atf_check -s exit:0 -o ignore rump.ping -n -c 1 -w $TIMEOUT ${lac1_wan} + fi + + unset RUMP_SERVER +} + +teardown_tunnel() +{ + export RUMP_SERVER=$LAC1SOCK + atf_check -s exit:0 rump.ifconfig bridge0 destroy + atf_check -s exit:0 rump.ifconfig l2tp0 deletetunnel + atf_check -s exit:0 rump.ifconfig l2tp0 destroy + + export RUMP_SERVER=$LAC2SOCK + atf_check -s exit:0 rump.ifconfig bridge0 destroy + atf_check -s exit:0 rump.ifconfig l2tp0 deletetunnel + atf_check -s exit:0 rump.ifconfig l2tp0 destroy + + unset RUMP_SERVER +} + +test_ping_failure() +{ + mode=$1 + + export RUMP_SERVER=$CLIENT1SOCK + if [ ${mode} = "ipv6" ]; then + atf_check -s not-exit:0 -o ignore -e ignore \ + rump.ping6 -n -X $TIMEOUT -c 1 $CLIENT2_LANIP6 + else + atf_check -s not-exit:0 -o ignore -e ignore \ + rump.ping -n -w $TIMEOUT -c 1 $CLIENT2_LANIP + fi + + export RUMP_SERVER=$CLIENT2SOCK + if [ ${mode} = "ipv6" ]; then + atf_check -s not-exit:0 -o ignore -e ignore \ + rump.ping6 -n -X $TIMEOUT -c 1 $CLIENT1_LANIP6 + else + atf_check -s not-exit:0 -o ignore -e ignore \ + rump.ping -n -w $TIMEOUT -c 1 $CLIENT1_LANIP + fi + + unset RUMP_SERVER +} + +test_ping_success() +{ + mode=$1 + + export RUMP_SERVER=$CLIENT1SOCK + if [ ${mode} = "ipv6" ]; then + # XXX + # rump.ping6 rarely fails with the message that + # "failed to get receiving hop limit". + # This is a known issue being analyzed. + atf_check -s exit:0 -o ignore \ + rump.ping6 -n -X $TIMEOUT -c 1 $CLIENT2_LANIP6 + else + atf_check -s exit:0 -o ignore \ + rump.ping -n -w $TIMEOUT -c 1 $CLIENT2_LANIP + fi + export RUMP_SERVER=$LAC1SOCK + $DEBUG && rump.ifconfig -v l2tp0 + $DEBUG && rump.ifconfig -v bridge0 + $DEBUG && rump.ifconfig -v shmif0 + + export RUMP_SERVER=$CLIENT2SOCK + if [ ${mode} = "ipv6" ]; then + atf_check -s exit:0 -o ignore \ + rump.ping6 -n -X $TIMEOUT -c 1 $CLIENT1_LANIP6 + else + atf_check -s exit:0 -o ignore \ + rump.ping -n -w $TIMEOUT -c 1 $CLIENT1_LANIP + fi + export RUMP_SERVER=$LAC2SOCK + $DEBUG && rump.ifconfig -v l2tp0 + $DEBUG && rump.ifconfig -v bridge0 + $DEBUG && rump.ifconfig -v shmif0 + + unset RUMP_SERVER +} + +basic_setup() +{ + lan_mode=$1 + wan_mode=$2 + + setup ${lan_mode} ${wan_mode} + test_setup ${lan_mode} ${wan_mode} + + # Enable once PR kern/49219 is fixed + #test_ping_failure + + setup_tunnel ${wan_mode} + sleep 1 + test_setup_tunnel ${wan_mode} +} + +basic_test() +{ + lan_mode=$1 + wan_mode=$2 # not use + + test_ping_success ${lan_mode} +} + +basic_teardown() +{ + lan_mode=$1 + wan_mode=$2 # not use + + teardown_tunnel + test_ping_failure ${lan_mode} +} + +add_test() +{ + category=$1 + desc=$2 + lan_mode=$3 + wan_mode=$4 + + name="l2tp_${category}_${lan_mode}over${wan_mode}" + fulldesc="Does ${lan_mode} over ${wan_mode} if_l2tp ${desc}" + + atf_test_case ${name} cleanup + eval "${name}_head() { + atf_set descr \"${fulldesc}\" + atf_set require.progs rump_server + } + ${name}_body() { + ${category}_setup ${lan_mode} ${wan_mode} + ${category}_test ${lan_mode} ${wan_mode} + ${category}_teardown ${lan_mode} ${wan_mode} + rump_server_destroy_ifaces + } + ${name}_cleanup() { + \$DEBUG && dump + cleanup + }" + atf_add_test_case ${name} +} + +add_test_allproto() +{ + category=$1 + desc=$2 + + add_test ${category} "${desc}" ipv4 ipv4 + add_test ${category} "${desc}" ipv4 ipv6 + add_test ${category} "${desc}" ipv6 ipv4 + add_test ${category} "${desc}" ipv6 ipv6 +} + +atf_init_test_cases() +{ + + atf_add_test_case l2tp_create_destroy + + add_test_allproto basic "basic tests" +# add_test_allproto recursive "recursive check tests" +} diff --git a/net/if_lagg/Makefile b/net/if_lagg/Makefile new file mode 100644 --- /dev/null +++ b/net/if_lagg/Makefile @@ -0,0 +1,12 @@ +# $NetBSD: Makefile,v 1.1 2021/05/17 04:07:44 yamaguchi Exp $ + +.include + +TESTSDIR= ${TESTSBASE}/net/if_lagg + +.for name in lagg +TESTS_SH+= t_${name} +TESTS_SH_SRC_t_${name}= ../net_common.sh t_${name}.sh +.endfor + +.include diff --git a/net/if_lagg/t_lagg.sh b/net/if_lagg/t_lagg.sh new file mode 100644 --- /dev/null +++ b/net/if_lagg/t_lagg.sh @@ -0,0 +1,1060 @@ +# $NetBSD: t_lagg.sh,v 1.2 2021/05/25 00:38:30 yamaguchi Exp $ +# +# Copyright (c) 2021 Internet Initiative Japan Inc. +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +SOCK_HOST0=unix://commsock0 +SOCK_HOST1=unix://commsock1 +SOCK_HOST2=unix://commsock2 +BUS0=bus0 +BUS1=bus1 +BUS2=bus2 +IP4ADDR0=192.168.0.1 +IP4ADDR1=192.168.0.2 +IP4ADDR2=192.168.1.1 +IP4ADDR3=192.168.1.2 +IP6ADDR0=fc00::1 +IP6ADDR1=fc00::2 +IP6ADDR2=fc00:1::1 +IP6ADDR3=fc00:1::2 +WAITTIME=20 + +DEBUG=${DEBUG:-false} + +wait_state() +{ + local state=$1 + local if_lagg=$2 + local if_port=$3 + + local n=$WAITTIME + local cmd_grep="grep -q ${state}" + + if [ x"$if_port" != x"" ]; then + cmd_grep="grep $if_port | $cmd_grep" + fi + + for i in $(seq $n); do + rump.ifconfig $if_lagg | eval $cmd_grep + if [ $? = 0 ] ; then + $DEBUG && echo "wait for $i seconds." + return 0 + fi + + sleep 1 + done + + $DEBUG && rump.ifconfig -v $if_lagg + atf_fail "Couldn't be ${state} for $n seconds." +} +wait_for_distributing() +{ + + wait_state "DISTRIBUTING" $* +} + +expected_inactive() +{ + local if_lagg=$1 + local if_port=$2 + + sleep 3 # wait a little + atf_check -s exit:0 -o not-match:"${if_port}.*ACTIVE" \ + rump.ifconfig ${if_lagg} +} + +atf_test_case lagg_ifconfig cleanup +lagg_ifconfig_head() +{ + + atf_set "descr" "tests for create, destroy, and ioctl of lagg(4)" + atf_set "require.progs" "rump_server" +} + +lagg_ifconfig_body() +{ + local atf_ifconfig="atf_check -s exit:0 rump.ifconfig" + + rump_server_start $SOCK_HOST0 lagg + + export RUMP_SERVER=$SOCK_HOST0 + $atf_ifconfig lagg0 create + $atf_ifconfig lagg0 destroy + + $atf_ifconfig lagg0 create + $atf_ifconfig shmif0 create + + $atf_ifconfig lagg0 laggproto none + atf_check -s exit:0 -o match:'laggproto none' \ + rump.ifconfig lagg0 + + # cannot add a port while protocol is none + atf_check -s not-exit:0 -e ignore \ + rump.ifconfig lagg0 laggport shmif0 + + $atf_ifconfig lagg0 laggproto lacp + atf_check -s exit:0 -o match:'laggproto lacp' \ + rump.ifconfig lagg0 + + # add a port and an added port + $atf_ifconfig lagg0 laggport shmif0 + atf_check -s not-exit:0 -e ignore \ + rump.ifconfig lagg0 laggport shmif0 + + # remove an added port and a removed port + $atf_ifconfig lagg0 -laggport shmif0 + atf_check -s not-exit:0 -e ignore \ + rump.ifconfig lagg0 -laggport shmif0 + + # re-add a removed port + $atf_ifconfig lagg0 laggport shmif0 + + # detach protocol even if the I/F has ports + $atf_ifconfig lagg0 laggproto none + + # destroy the interface while grouping ports + $atf_ifconfig lagg0 destroy + + $atf_ifconfig lagg0 create + $atf_ifconfig shmif1 create + + $atf_ifconfig lagg0 laggproto lacp + $atf_ifconfig lagg0 laggport shmif0 + $atf_ifconfig lagg0 laggport shmif1 + + $atf_ifconfig lagg0 -laggport shmif0 + $atf_ifconfig lagg0 laggport shmif0 + $atf_ifconfig lagg0 -laggport shmif1 + $atf_ifconfig lagg0 laggport shmif1 + + # destroy a LAGed port + atf_check -s exit:0 -o match:shmif0 rump.ifconfig lagg0 + atf_check -s exit:0 -o match:shmif1 rump.ifconfig lagg0 + $atf_ifconfig shmif0 destroy + $atf_ifconfig shmif1 destroy + + $atf_ifconfig lagg0 laggproto none + atf_check -s exit:0 -o ignore rump.ifconfig lagg0 +} + +lagg_ifconfig_cleanup() +{ + $DEBG && dump + cleanup +} + +atf_test_case lagg_macaddr cleanup +lagg_macaddr_head() +{ + atf_set "descr" "tests for a MAC address to assign to lagg(4)" + atf_set "require.progs" "rump_server" +} + +lagg_macaddr_body() +{ + local atf_ifconfig="atf_check -s exit:0 rump.ifconfig" + + rump_server_start $SOCK_HOST0 lagg + + export RUMP_SERVER=$SOCK_HOST0 + $atf_ifconfig lagg0 create + rump_server_add_iface $SOCK_HOST0 shmif0 $BUS0 + rump_server_add_iface $SOCK_HOST0 shmif1 $BUS1 + + maddr=$(get_macaddr $SOCK_HOST0 lagg0) + maddr0=$(get_macaddr $SOCK_HOST0 shmif0) + maddr1=$(get_macaddr $SOCK_HOST0 shmif1) + + $atf_ifconfig lagg0 laggproto lacp + + $atf_ifconfig lagg0 laggport shmif0 + atf_check -s exit:0 -o match:$maddr0 rump.ifconfig lagg0 + + $atf_ifconfig lagg0 laggport shmif1 + atf_check -s exit:0 -o match:$maddr0 rump.ifconfig lagg0 + atf_check -s exit:0 -o match:$maddr0 rump.ifconfig shmif1 + + $atf_ifconfig lagg0 -laggport shmif0 + atf_check -s exit:0 -o match:$maddr1 rump.ifconfig lagg0 + atf_check -s exit:0 -o match:$maddr0 rump.ifconfig shmif0 + + $atf_ifconfig lagg0 laggport shmif0 + atf_check -s exit:0 -o match:$maddr1 rump.ifconfig lagg0 + atf_check -s exit:0 -o match:$maddr1 rump.ifconfig shmif0 + + $atf_ifconfig lagg0 -laggport shmif0 + atf_check -s exit:0 -o match:$maddr0 rump.ifconfig shmif0 + + $atf_ifconfig lagg0 -laggport shmif1 + atf_check -s exit:0 -o match:$maddr rump.ifconfig lagg0 +} + +lagg_macaddr_cleanup() +{ + $DEBUG && dump + cleanup +} + +atf_test_case lagg_ipv6lla cleanup +lagg_ipv6lla_head() +{ + atf_set "descr" "tests for a IPV6 LLA to assign to lagg(4)" + atf_set "require.progs" "rump_server" +} + +lagg_ipv6lla_body() +{ + local atf_ifconfig="atf_check -s exit:0 rump.ifconfig" + + rump_server_start $SOCK_HOST0 netinet6 lagg + + export RUMP_SERVER=$SOCK_HOST0 + $atf_ifconfig lagg0 create + rump_server_add_iface $SOCK_HOST0 shmif0 $BUS0 + rump_server_add_iface $SOCK_HOST0 shmif1 $BUS1 + + $atf_ifconfig lagg0 laggproto lacp + + $atf_ifconfig shmif0 up + atf_check -s exit:0 -o match:'inet6 fe80:' rump.ifconfig shmif0 + + $atf_ifconfig lagg0 laggproto lacp laggport shmif0 + atf_check -s exit:0 -o not-match:'inet6 fe80:' rump.ifconfig shmif0 + + $atf_ifconfig lagg0 laggport shmif1 + $atf_ifconfig shmif1 up + atf_check -s exit:0 -o not-match:'inet6 fe80:' rump.ifconfig shmif1 + + $atf_ifconfig lagg0 -laggport shmif0 + atf_check -s exit:0 -o match:'inet6 fe80:' rump.ifconfig shmif0 + + $atf_ifconfig shmif1 down + $atf_ifconfig lagg0 -laggport shmif1 + atf_check -s exit:0 -o not-match:'inet fe80:' rump.ifconfig shmif1 +} + +lagg_ipv6lla_cleanup() +{ + $DEBUG && dump + cleanup +} + +atf_test_case lagg_lacp_basic cleanup +lagg_lacp_basic_head() +{ + + atf_set "descr" "tests for LACP basic functions" + atf_set "require.progs" "rump_server" +} + +lagg_lacp_basic_body() +{ + local atf_ifconfig="atf_check -s exit:0 rump.ifconfig" + + rump_server_start $SOCK_HOST0 lagg + rump_server_start $SOCK_HOST1 lagg + rump_server_start $SOCK_HOST2 lagg + + export RUMP_SERVER=$SOCK_HOST0 + + # added running interface + $atf_ifconfig shmif0 create + $atf_ifconfig shmif0 linkstr $BUS0 + + $atf_ifconfig shmif1 create + $atf_ifconfig shmif1 linkstr $BUS1 + + $atf_ifconfig lagg0 create + $atf_ifconfig lagg0 laggproto lacp + + $atf_ifconfig shmif0 up + $atf_ifconfig shmif1 up + $atf_ifconfig lagg0 up + + $atf_ifconfig lagg0 laggport shmif0 + $atf_ifconfig lagg0 laggport shmif1 + $atf_ifconfig -w 10 + + $atf_ifconfig lagg0 -laggport shmif0 + $atf_ifconfig lagg0 -laggport shmif1 + $atf_ifconfig lagg0 down + + # add the same interfaces again + $atf_ifconfig lagg0 up + $atf_ifconfig lagg0 laggport shmif0 + $atf_ifconfig lagg0 laggport shmif1 + + # detach and re-attach protocol + $atf_ifconfig lagg0 laggproto none + $atf_ifconfig lagg0 laggproto lacp \ + laggport shmif0 laggport shmif1 + + $atf_ifconfig lagg0 -laggport shmif0 -laggport shmif1 + $atf_ifconfig lagg0 destroy + $atf_ifconfig shmif0 destroy + $atf_ifconfig shmif1 destroy + + # tests for a loopback condition + $atf_ifconfig shmif0 create + $atf_ifconfig shmif0 linkstr $BUS0 + $atf_ifconfig shmif1 create + $atf_ifconfig shmif1 linkstr $BUS0 + $atf_ifconfig lagg0 create + $atf_ifconfig lagg0 laggproto lacp \ + laggport shmif0 laggport shmif1 + $atf_ifconfig shmif0 up + $atf_ifconfig shmif1 up + $atf_ifconfig lagg0 up + + expected_inactive lagg0 + + $atf_ifconfig shmif0 down + $atf_ifconfig shmif0 destroy + $atf_ifconfig shmif1 down + $atf_ifconfig shmif1 destroy + $atf_ifconfig lagg0 down + $atf_ifconfig lagg0 destroy + + export RUMP_SERVER=$SOCK_HOST0 + $atf_ifconfig shmif0 create + $atf_ifconfig shmif0 linkstr $BUS0 + $atf_ifconfig shmif0 up + + $atf_ifconfig shmif1 create + $atf_ifconfig shmif1 linkstr $BUS1 + $atf_ifconfig shmif1 up + + $atf_ifconfig shmif2 create + $atf_ifconfig shmif2 linkstr $BUS2 + $atf_ifconfig shmif2 up + + $atf_ifconfig lagg0 create + $atf_ifconfig lagg0 laggproto lacp laggport shmif0 \ + laggport shmif1 laggport shmif2 + $atf_ifconfig lagg0 up + + export RUMP_SERVER=$SOCK_HOST1 + $atf_ifconfig shmif0 create + $atf_ifconfig shmif0 linkstr $BUS0 + $atf_ifconfig shmif0 up + + $atf_ifconfig shmif1 create + $atf_ifconfig shmif1 linkstr $BUS1 + $atf_ifconfig shmif1 up + + $atf_ifconfig lagg0 create + $atf_ifconfig lagg0 laggproto lacp + $atf_ifconfig lagg1 create + $atf_ifconfig lagg1 laggproto lacp + + $atf_ifconfig lagg0 laggport shmif0 + $atf_ifconfig lagg0 up + wait_for_distributing lagg0 shmif0 + + $atf_ifconfig lagg1 laggport shmif1 + $atf_ifconfig lagg1 up + + export RUMP_SERVER=$SOCK_HOST2 + $atf_ifconfig shmif0 create + $atf_ifconfig shmif0 linkstr $BUS2 + $atf_ifconfig shmif0 up + + $atf_ifconfig lagg0 create + $atf_ifconfig lagg0 laggproto lacp laggport shmif0 + $atf_ifconfig lagg0 up + + export RUMP_SERVER=$SOCK_HOST0 + wait_for_distributing lagg0 shmif0 + expected_inactive lagg0 shmif1 + expected_inactive lagg0 shmif2 +} + +lagg_lacp_basic_cleanup() +{ + + $DEBUG && dump + cleanup +} + +lagg_lacp_ping() +{ + local atf_ifconfig="atf_check -s exit:0 rump.ifconfig" + + local af=$1 + local atf_ping="atf_check -s exit:0 -o ignore rump.ping -c 1" + local ping=rump.ping + local rumplib="" + local pfx=24 + local addr_host0=$IP4ADDR0 + local addr_host1=$IP4ADDR1 + + case $af in + "inet") + # do nothing + ;; + "inet6") + atf_ping="atf_check -s exit:0 -o ignore rump.ping6 -c 1" + rumplib="netinet6" + pfx=64 + addr_host0=$IP6ADDR0 + addr_host1=$IP6ADDR1 + ;; + esac + + rump_server_start $SOCK_HOST0 lagg $rumplib + rump_server_start $SOCK_HOST1 lagg $rumplib + + rump_server_add_iface $SOCK_HOST0 shmif0 $BUS0 + rump_server_add_iface $SOCK_HOST0 shmif1 $BUS1 + rump_server_add_iface $SOCK_HOST0 shmif2 $BUS2 + + rump_server_add_iface $SOCK_HOST1 shmif0 $BUS0 + rump_server_add_iface $SOCK_HOST1 shmif1 $BUS1 + rump_server_add_iface $SOCK_HOST1 shmif2 $BUS2 + + export RUMP_SERVER=$SOCK_HOST0 + $atf_ifconfig lagg0 create + $atf_ifconfig lagg0 laggproto lacp laggport shmif0 + $atf_ifconfig lagg0 $af $addr_host0/$pfx + $atf_ifconfig shmif0 up + $atf_ifconfig lagg0 up + + export RUMP_SERVER=$SOCK_HOST1 + $atf_ifconfig lagg0 create + $atf_ifconfig lagg0 laggproto lacp laggport shmif0 + $atf_ifconfig lagg0 $af $addr_host1/$pfx + $atf_ifconfig shmif0 up + $atf_ifconfig lagg0 up + + export RUMP_SERVER=$SOCK_HOST0 + wait_for_distributing lagg0 + $atf_ifconfig -w 10 + + export RUMP_SERVER=$SOCK_HOST1 + wait_for_distributing lagg0 + $atf_ifconfig -w 10 + + $atf_ping $addr_host0 + + export RUMP_SERVER=$SOCK_HOST0 + $atf_ifconfig shmif1 up + $atf_ifconfig lagg0 laggport shmif1 laggport shmif2 + $atf_ifconfig shmif2 up + + export RUMP_SERVER=$SOCK_HOST1 + $atf_ifconfig shmif1 up + $atf_ifconfig lagg0 laggport shmif1 laggport shmif2 + $atf_ifconfig shmif2 up + + export RUMP_SERVER=$SOCK_HOST0 + wait_for_distributing lagg0 shmif1 + wait_for_distributing lagg0 shmif2 + + export RUMP_SERVER=$SOCK_HOST1 + wait_for_distributing lagg0 shmif1 + wait_for_distributing lagg0 shmif2 + + $atf_ping $addr_host0 +} + +atf_test_case lagg_lacp_ipv4 cleanup +lagg_lacp_ipv4_head() +{ + + atf_set "descr" "tests for IPv4 with LACP" + atf_set "require.progs" "rump_server" +} + +lagg_lacp_ipv4_body() +{ + + lagg_lacp_ping "inet" +} + +lagg_lacp_ipv4_cleanup() +{ + + $DEBUG && dump + cleanup +} + +atf_test_case lagg_lacp_ipv6 cleanup +lagg_lacp_ipv6_head() +{ + + atf_set "descr" "tests for IPv6 with LACP" + atf_set "require.progs" "rump_server" +} + +lagg_lacp_ipv6_body() +{ + + lagg_lacp_ping "inet6" +} + +lagg_lacp_ipv6_cleanup() +{ + + $DEBUG && dump + cleanup +} + +lagg_lacp_vlan() +{ + local atf_ifconfig="atf_check -s exit:0 rump.ifconfig" + + local af=$1 + local atf_ping="atf_check -s exit:0 -o ignore rump.ping -c 1" + local rumplib="vlan" + local pfx=24 + local vlan0_addr_host0=$IP4ADDR0 + local host0addr0=$IP4ADDR0 + local host1addr0=$IP4ADDR1 + local host0addr1=$IP4ADDR2 + local host1addr1=$IP4ADDR3 + + case $af in + "inet") + # do nothing + ;; + "inet6") + atf_ping="atf_check -s exit:0 -o ignore rump.ping6 -c 1" + rumplib="netinet6" + pfx=64 + host0addr0=$IP6ADDR0 + host1addr0=$IP6ADDR1 + host0addr1=$IP6ADDR2 + host1addr1=$IP6ADDR3 + ;; + esac + + rump_server_start $SOCK_HOST0 lagg $rumplib + rump_server_start $SOCK_HOST1 lagg $rumplib + + rump_server_add_iface $SOCK_HOST0 shmif0 $BUS0 + rump_server_add_iface $SOCK_HOST0 shmif1 $BUS1 + rump_server_add_iface $SOCK_HOST0 shmif2 $BUS2 + + rump_server_add_iface $SOCK_HOST1 shmif0 $BUS0 + rump_server_add_iface $SOCK_HOST1 shmif1 $BUS1 + rump_server_add_iface $SOCK_HOST1 shmif2 $BUS2 + + export RUMP_SERVER=$SOCK_HOST0 + $atf_ifconfig lagg0 create + $atf_ifconfig lagg0 laggproto lacp laggport shmif0 + $atf_ifconfig shmif0 up + $atf_ifconfig lagg0 up + + export RUMP_SERVER=$SOCK_HOST1 + $atf_ifconfig lagg0 create + $atf_ifconfig lagg0 laggproto lacp laggport shmif0 + $atf_ifconfig shmif0 up + $atf_ifconfig lagg0 up + + export RUMP_SERVER=$SOCK_HOST0 + wait_for_distributing lagg0 + + $atf_ifconfig vlan0 create + $atf_ifconfig vlan0 vlan 10 vlanif lagg0 + $atf_ifconfig vlan0 $af $host0addr0/$pfx + $atf_ifconfig vlan0 up + + $atf_ifconfig vlan1 create + $atf_ifconfig vlan1 vlan 11 vlanif lagg0 + $atf_ifconfig vlan1 $af $host0addr1/$pfx + $atf_ifconfig vlan1 up + + export RUMP_SERVER=$SOCK_HOST1 + $atf_ifconfig vlan0 create + $atf_ifconfig vlan0 vlan 10 vlanif lagg0 + $atf_ifconfig vlan0 $af $host1addr0/$pfx + $atf_ifconfig vlan0 up + + $atf_ifconfig vlan1 create + $atf_ifconfig vlan1 vlan 11 vlanif lagg0 + $atf_ifconfig vlan1 $af $host1addr1/$pfx + $atf_ifconfig vlan1 up + + export RUMP_SERVER=$SOCK_HOST0 + $atf_ifconfig -w 10 + export RUMP_SERVER=$SOCK_HOST1 + $atf_ifconfig -w 10 + + export RUMP_SERVER=$SOCK_HOST0 + $atf_ping $host1addr0 + $atf_ping $host1addr1 + + $atf_ifconfig lagg0 laggport shmif1 + $atf_ifconfig shmif1 up + + export RUMP_SERVER=$SOCK_HOST1 + $atf_ifconfig lagg0 laggport shmif1 + $atf_ifconfig shmif1 up + + export RUMP_SERVER=$SOCK_HOST0 + wait_for_distributing lagg0 shmif1 + + export RUMP_SERVER=$SOCK_HOST1 + wait_for_distributing lagg0 shmif1 + + $atf_ping $host0addr0 + $atf_ping $host0addr1 +} + +atf_test_case lagg_lacp_vlan_ipv4 cleanup +lagg_lacp_vlan_ipv4_head() +{ + + atf_set "descr" "tests for IPv4 VLAN frames over LACP LAG" + atf_set "require.progs" "rump_server" +} + +lagg_lacp_vlan_ipv4_body() +{ + + lagg_lacp_vlan "inet" +} + +lagg_lacp_vlan_ipv4_cleanup() +{ + $DEBUG && dump + cleanup +} + +atf_test_case lagg_lacp_vlan_ipv6 cleanup +lagg_lacp_vlan_ipv6_head() +{ + + atf_set "descr" "tests for IPv6 VLAN frames over LACP LAG" + atf_set "require.progs" "rump_server" +} + +lagg_lacp_vlan_ipv6_body() +{ + + lagg_lacp_vlan "inet" +} + +lagg_lacp_vlan_ipv6_cleanup() +{ + $DEBUG && dump + cleanup +} + +atf_test_case lagg_lacp_portpri cleanup +lagg_lacp_portpri_head() +{ + + atf_set "descr" "tests for LACP port priority" + atf_set "require.progs" "rump_server" +} + +lagg_lacp_portpri_body() +{ + local atf_ifconfig="atf_check -s exit:0 rump.ifconfig" + + rump_server_start $SOCK_HOST0 lagg + rump_server_start $SOCK_HOST1 lagg + + rump_server_add_iface $SOCK_HOST0 shmif0 $BUS0 + rump_server_add_iface $SOCK_HOST0 shmif1 $BUS1 + rump_server_add_iface $SOCK_HOST0 shmif2 $BUS2 + + rump_server_add_iface $SOCK_HOST1 shmif0 $BUS0 + rump_server_add_iface $SOCK_HOST1 shmif1 $BUS1 + rump_server_add_iface $SOCK_HOST1 shmif2 $BUS2 + + export RUMP_SERVER=$SOCK_HOST0 + $atf_ifconfig lagg0 create + $atf_ifconfig lagg0 laggproto lacp + $atf_ifconfig lagg0 lagglacp maxports 2 + + $atf_ifconfig shmif0 up + $atf_ifconfig shmif1 up + $atf_ifconfig shmif2 up + + $atf_ifconfig lagg0 laggport shmif0 pri 1000 + $atf_ifconfig lagg0 laggport shmif1 pri 2000 + $atf_ifconfig lagg0 laggport shmif2 pri 3000 + $atf_ifconfig lagg0 up + + atf_check -s exit:0 -o match:'shmif0 pri=1000' rump.ifconfig lagg0 + atf_check -s exit:0 -o match:'shmif1 pri=2000' rump.ifconfig lagg0 + atf_check -s exit:0 -o match:'shmif2 pri=3000' rump.ifconfig lagg0 + + export RUMP_SERVER=$SOCK_HOST1 + $atf_ifconfig lagg0 create + $atf_ifconfig lagg0 laggproto lacp + + $atf_ifconfig shmif0 up + $atf_ifconfig shmif1 up + $atf_ifconfig shmif2 up + + $atf_ifconfig lagg0 laggport shmif0 pri 300 + $atf_ifconfig lagg0 laggport shmif1 pri 200 + $atf_ifconfig lagg0 laggport shmif2 pri 100 + $atf_ifconfig lagg0 up + + atf_check -s exit:0 -o match:'shmif0 pri=300' rump.ifconfig lagg0 + atf_check -s exit:0 -o match:'shmif1 pri=200' rump.ifconfig lagg0 + atf_check -s exit:0 -o match:'shmif2 pri=100' rump.ifconfig lagg0 + + export RUMP_SERVER=$SOCK_HOST0 + wait_for_distributing lagg0 shmif0 + wait_for_distributing lagg0 shmif1 + wait_state "STANDBY" lagg0 shmif2 + + $atf_ifconfig shmif0 down + wait_for_distributing lagg0 shmif2 + + $atf_ifconfig shmif0 up + wait_for_distributing lagg0 shmif0 + + $atf_ifconfig lagg0 laggportpri shmif0 5000 + $atf_ifconfig lagg0 laggportpri shmif1 5000 + $atf_ifconfig lagg0 laggportpri shmif2 5000 + + atf_check -s exit:0 -o match:'shmif0 pri=5000' rump.ifconfig lagg0 + atf_check -s exit:0 -o match:'shmif1 pri=5000' rump.ifconfig lagg0 + atf_check -s exit:0 -o match:'shmif2 pri=5000' rump.ifconfig lagg0 + + wait_state "STANDBY" lagg0 shmif0 + wait_for_distributing lagg0 shmif1 + wait_for_distributing lagg0 shmif2 +} + +lagg_lacp_portpri_cleanup() +{ + + $DEBUG && dump + cleanup +} + +lagg_failover() +{ + local atf_ifconfig="atf_check -s exit:0 rump.ifconfig" + + local af=$1 + local ping="rump.ping -c 1" + local rumplib="" + local pfx=24 + local addr_host0=$IP4ADDR0 + local addr_host1=$IP4ADDR1 + + case $af in + "inet") + # do nothing + ;; + "inet6") + ping="rump.ping6 -c 1" + rumplib="netinet6" + pfx=64 + addr_host0=$IP6ADDR0 + addr_host1=$IP6ADDR1 + ;; + esac + + local atf_ping="atf_check -s exit:0 -o ignore ${ping}" + + rump_server_start $SOCK_HOST0 lagg $rumplib + rump_server_start $SOCK_HOST1 lagg $rumplib + + rump_server_add_iface $SOCK_HOST0 shmif0 $BUS0 + rump_server_add_iface $SOCK_HOST0 shmif1 $BUS1 + rump_server_add_iface $SOCK_HOST0 shmif2 $BUS2 + + rump_server_add_iface $SOCK_HOST1 shmif0 $BUS0 + rump_server_add_iface $SOCK_HOST1 shmif1 $BUS1 + rump_server_add_iface $SOCK_HOST1 shmif2 $BUS2 + + export RUMP_SERVER=$SOCK_HOST0 + $atf_ifconfig lagg0 create + $atf_ifconfig lagg0 laggproto failover + + $atf_ifconfig lagg0 laggport shmif0 pri 1000 + $atf_ifconfig lagg0 laggport shmif1 pri 2000 + $atf_ifconfig lagg0 laggport shmif2 pri 3000 + $atf_ifconfig lagg0 $af $addr_host0/$pfx + + export RUMP_SERVER=$SOCK_HOST1 + $atf_ifconfig lagg0 create + $atf_ifconfig lagg0 laggproto failover + + $atf_ifconfig lagg0 laggport shmif0 pri 1000 + $atf_ifconfig lagg0 laggport shmif1 pri 3000 + $atf_ifconfig lagg0 laggport shmif2 pri 2000 + $atf_ifconfig lagg0 $af $addr_host1/$pfx + + export RUMP_SERVER=$SOCK_HOST0 + $atf_ifconfig shmif0 up + $atf_ifconfig shmif1 up + $atf_ifconfig shmif2 up + $atf_ifconfig lagg0 up + + export RUMP_SERVER=$SOCK_HOST1 + $atf_ifconfig shmif0 up + $atf_ifconfig shmif1 up + $atf_ifconfig shmif2 up + $atf_ifconfig lagg0 up + + export RUMP_SERVER=$SOCK_HOST0 + $atf_ifconfig -w 10 + wait_for_distributing lagg0 shmif0 + wait_state "COLLECTING" lagg0 shmif0 + wait_state "COLLECTING" lagg0 shmif1 + wait_state "COLLECTING" lagg0 shmif2 + + export RUMP_SERVER=$SOCK_HOST1 + $atf_ifconfig -w 10 + wait_for_distributing lagg0 shmif0 + wait_state "COLLECTING" lagg0 shmif0 + wait_state "COLLECTING" lagg0 shmif1 + wait_state "COLLECTING" lagg0 shmif2 + + export RUMP_SERVER=$SOCK_HOST0 + $atf_ping $addr_host1 + + $atf_ifconfig shmif0 down + wait_for_distributing lagg0 shmif1 + wait_state "COLLECTING" lagg0 shmif1 + wait_state "COLLECTING" lagg0 shmif2 + + export RUMP_SERVER=$SOCK_HOST1 + $atf_ifconfig shmif0 down + wait_for_distributing lagg0 shmif2 + wait_state "COLLECTING" lagg0 shmif2 + wait_state "COLLECTING" lagg0 shmif1 + + export RUMP_SERVER=$SOCK_HOST0 + $atf_ping $addr_host1 + + $atf_ifconfig lagg0 laggfailover -rx-all + atf_check -s exit:0 -o not-match:'shmif2.+COLLECTING' rump.ifconfig lagg0 + + export RUMP_SERVER=$SOCK_HOST1 + $atf_ifconfig lagg0 laggfailover -rx-all + atf_check -s exit:0 -o not-match:'shmif1.+COLLECTING' rump.ifconfig lagg0 + + export RUMP_SERVER=$SOCK_HOST0 + atf_check -s not-exit:0 -o ignore -e ignore $ping -c 1 $addr_host1 +} + +atf_test_case lagg_failover_ipv4 cleanup +lagg_failover_ipv4_head() +{ + + atf_set "descr" "tests for failover using IPv4" + atf_set "require.progs" "rump_server" +} + +lagg_failover_ipv4_body() +{ + + lagg_failover "inet" +} + +lagg_failover_ipv4_cleanup() +{ + + $DEBUG && dump + cleanup +} + +atf_test_case lagg_failover_ipv6 cleanup +lagg_failover_ipv6_head() +{ + + atf_set "descr" "tests for failover using IPv6" + atf_set "require.progs" "rump_server" +} + +lagg_failover_ipv6_body() +{ + + lagg_failover "inet6" +} + +lagg_failover_ipv6_cleanup() +{ + + $DEBUG && dump + cleanup +} + +lagg_loadbalance() +{ + local atf_ifconfig="atf_check -s exit:0 rump.ifconfig" + + local af=$1 + local ping="rump.ping -c 1" + local rumplib="" + local pfx=24 + local addr_host0=$IP4ADDR0 + local addr_host1=$IP4ADDR1 + + case $af in + "inet") + # do nothing + ;; + "inet6") + ping="rump.ping6 -c 1" + rumplib="netinet6" + pfx=64 + addr_host0=$IP6ADDR0 + addr_host1=$IP6ADDR1 + ;; + esac + + local atf_ping="atf_check -s exit:0 -o ignore ${ping}" + + rump_server_start $SOCK_HOST0 lagg $rumplib + rump_server_start $SOCK_HOST1 lagg $rumplib + + rump_server_add_iface $SOCK_HOST0 shmif0 $BUS0 + rump_server_add_iface $SOCK_HOST0 shmif1 $BUS1 + rump_server_add_iface $SOCK_HOST0 shmif2 $BUS2 + + rump_server_add_iface $SOCK_HOST1 shmif0 $BUS0 + rump_server_add_iface $SOCK_HOST1 shmif1 $BUS1 + rump_server_add_iface $SOCK_HOST1 shmif2 $BUS2 + + export RUMP_SERVER=$SOCK_HOST0 + $atf_ifconfig lagg0 create + $atf_ifconfig lagg0 laggproto loadbalance + + $atf_ifconfig lagg0 laggport shmif0 pri 1000 + $atf_ifconfig lagg0 laggport shmif1 pri 2000 + $atf_ifconfig lagg0 laggport shmif2 pri 3000 + $atf_ifconfig lagg0 $af $addr_host0/$pfx + + export RUMP_SERVER=$SOCK_HOST1 + $atf_ifconfig lagg0 create + $atf_ifconfig lagg0 laggproto loadbalance + + $atf_ifconfig lagg0 laggport shmif0 pri 1000 + $atf_ifconfig lagg0 laggport shmif1 pri 3000 + $atf_ifconfig lagg0 laggport shmif2 pri 2000 + $atf_ifconfig lagg0 $af $addr_host1/$pfx + + export RUMP_SERVER=$SOCK_HOST0 + $atf_ifconfig shmif0 up + $atf_ifconfig shmif1 up + $atf_ifconfig shmif2 up + $atf_ifconfig lagg0 up + + export RUMP_SERVER=$SOCK_HOST1 + $atf_ifconfig shmif0 up + $atf_ifconfig shmif1 up + $atf_ifconfig shmif2 up + $atf_ifconfig lagg0 up + + export RUMP_SERVER=$SOCK_HOST0 + $atf_ifconfig -w 10 + wait_for_distributing lagg0 shmif0 + wait_state "COLLECTING" lagg0 shmif0 + wait_state "COLLECTING" lagg0 shmif1 + wait_state "COLLECTING" lagg0 shmif2 + + export RUMP_SERVER=$SOCK_HOST1 + $atf_ifconfig -w 10 + wait_state "COLLECTING,DISTRIBUTING" lagg0 shmif0 + wait_state "COLLECTING,DISTRIBUTING" lagg0 shmif1 + wait_state "COLLECTING,DISTRIBUTING" lagg0 shmif2 + + export RUMP_SERVER=$SOCK_HOST0 + $atf_ping $addr_host1 + + $atf_ifconfig shmif0 down + wait_state "COLLECTING,DISTRIBUTING" lagg0 shmif1 + wait_state "COLLECTING,DISTRIBUTING" lagg0 shmif2 + + export RUMP_SERVER=$SOCK_HOST1 + $atf_ifconfig shmif0 down + wait_state "COLLECTING,DISTRIBUTING" lagg0 shmif1 + wait_state "COLLECTING,DISTRIBUTING" lagg0 shmif2 + + export RUMP_SERVER=$SOCK_HOST0 + $atf_ping $addr_host1 +} + +atf_test_case lagg_loadbalance_ipv4 cleanup +lagg_loadbalance_ipv4_head() +{ + + atf_set "descr" "tests for loadbalance using IPv4" + atf_set "require.progs" "rump_server" +} + +lagg_loadbalance_ipv4_body() +{ + + lagg_loadbalance "inet" +} + +lagg_loadbalance_ipv4_cleanup() +{ + + $DEBUG && dump + cleanup +} + +atf_test_case lagg_loadbalance_ipv6 cleanup +lagg_loadbalance_ipv6_head() +{ + + atf_set "descr" "tests for loadbalance using IPv6" + atf_set "require.progs" "rump_server" +} + +lagg_loadbalance_ipv6_body() +{ + + lagg_loadbalance "inet6" +} + +lagg_loadbalance_ipv6_cleanup() +{ + + $DEBUG && dump + cleanup +} + +atf_init_test_cases() +{ + + atf_add_test_case lagg_ifconfig + atf_add_test_case lagg_macaddr + atf_add_test_case lagg_ipv6lla + atf_add_test_case lagg_lacp_basic + atf_add_test_case lagg_lacp_ipv4 + atf_add_test_case lagg_lacp_ipv6 + atf_add_test_case lagg_lacp_vlan_ipv4 + atf_add_test_case lagg_lacp_vlan_ipv6 + atf_add_test_case lagg_lacp_portpri + atf_add_test_case lagg_failover_ipv4 + atf_add_test_case lagg_failover_ipv6 + atf_add_test_case lagg_loadbalance_ipv4 + atf_add_test_case lagg_loadbalance_ipv6 +} diff --git a/net/if_loop/Makefile b/net/if_loop/Makefile --- a/net/if_loop/Makefile +++ b/net/if_loop/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.3 2016/08/08 14:58:40 pgoyette Exp $ +# $NetBSD: Makefile,v 1.7 2020/03/01 18:08:16 christos Exp $ # .include @@ -8,6 +8,11 @@ TESTS_C= t_pr LDADD+= -lrumpnet_netinet -lrumpnet_net -lrumpnet -LDADD+= -lrump -lrumpuser -lrump -lpthread -lrumpdev -lrumpvfs +LDADD+= ${LIBRUMPBASE} + +.for name in basic +TESTS_SH+= t_${name} +TESTS_SH_SRC_t_${name}= ../net_common.sh t_${name}.sh +.endfor .include diff --git a/net/if_loop/t_basic.sh b/net/if_loop/t_basic.sh new file mode 100644 --- /dev/null +++ b/net/if_loop/t_basic.sh @@ -0,0 +1,58 @@ +# $NetBSD: t_basic.sh,v 1.2 2018/02/01 05:22:01 ozaki-r Exp $ +# +# Copyright (c) 2017 Internet Initiative Japan Inc. +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +BUS=bus +SOCK_LOCAL=unix://loop_basic_local + +atf_test_case loop_create_destroy cleanup +loop_create_destroy_head() +{ + + atf_set "descr" "tests of creation and deletion of a loopback interface" + atf_set "require.progs" "rump_server" +} + +loop_create_destroy_body() +{ + + rump_server_start $SOCK_LOCAL netinet6 + + test_create_destroy_common $SOCK_LOCAL lo1 true +} + +loop_create_destroy_cleanup() +{ + + $DEBUG && dump + cleanup +} + +atf_init_test_cases() +{ + + atf_add_test_case loop_create_destroy +} diff --git a/net/if_pppoe/t_pppoe.sh b/net/if_pppoe/t_pppoe.sh old mode 100755 new mode 100644 --- a/net/if_pppoe/t_pppoe.sh +++ b/net/if_pppoe/t_pppoe.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_pppoe.sh,v 1.16 2016/12/14 03:30:30 knakahara Exp $ +# $NetBSD: t_pppoe.sh,v 1.33 2021/06/01 05:18:33 yamaguchi Exp $ # # Copyright (c) 2016 Internet Initiative Japan Inc. # All rights reserved. @@ -25,14 +25,8 @@ # POSSIBILITY OF SUCH DAMAGE. # -server="rump_server -lrump -lrumpnet -lrumpnet_net -lrumpnet_netinet \ - -lrumpnet_netinet6 -lrumpnet_shmif -lrumpdev \ - -lrumpnet_pppoe" -# pppoectl doesn't work with RUMPHIJACK=sysctl=yes -HIJACKING="env LD_PRELOAD=/usr/lib/librumphijack.so" - -SERVER=unix://commsock1 -CLIENT=unix://commsock2 +SERVER=unix://pppoe_server +CLIENT=unix://pppoe_client SERVER_IP=10.3.3.1 CLIENT_IP=10.3.3.3 @@ -45,61 +39,106 @@ WAITTIME=10 DEBUG=${DEBUG:-false} -setup() +atf_ifconfig() { - inet=true - if [ $# -ne 0 ]; then - eval $@ - fi + atf_check -s exit:0 rump.ifconfig $* +} - atf_check -s exit:0 ${server} $SERVER - atf_check -s exit:0 ${server} $CLIENT +atf_pppoectl() +{ - export RUMP_SERVER=$SERVER - atf_check -s exit:0 rump.ifconfig shmif0 create - atf_check -s exit:0 rump.ifconfig shmif0 linkstr $BUS - atf_check -s exit:0 rump.ifconfig shmif0 up + atf_check -s exit:0 -x "$HIJACKING pppoectl $*" +} + +atf_test_case pppoe_create_destroy cleanup +pppoe_create_destroy_head() +{ + + atf_set "descr" "Test creating/destroying pppoe interfaces" + atf_set "require.progs" "rump_server" +} + +pppoe_create_destroy_body() +{ + + rump_server_start $CLIENT netinet6 pppoe - atf_check -s exit:0 rump.ifconfig pppoe0 create - $inet && atf_check -s exit:0 rump.ifconfig pppoe0 \ + test_create_destroy_common $CLIENT pppoe0 true +} + +pppoe_create_destroy_cleanup() +{ + + $DEBUG && dump + cleanup +} + +setup_ifaces() +{ + + rump_server_add_iface $SERVER shmif0 $BUS + rump_server_add_iface $CLIENT shmif0 $BUS + rump_server_add_iface $SERVER pppoe0 + rump_server_add_iface $CLIENT pppoe0 + + export RUMP_SERVER=$SERVER + atf_ifconfig shmif0 up + $inet && atf_ifconfig pppoe0 \ inet $SERVER_IP $CLIENT_IP down - atf_check -s exit:0 rump.ifconfig pppoe0 link0 + atf_ifconfig pppoe0 link0 + $DEBUG && rump.ifconfig pppoe0 debug $DEBUG && rump.ifconfig $DEBUG && $HIJACKING pppoectl -d pppoe0 - - atf_check -s exit:0 -x "$HIJACKING pppoectl -e shmif0 pppoe0" unset RUMP_SERVER export RUMP_SERVER=$CLIENT - atf_check -s exit:0 rump.ifconfig shmif0 create - atf_check -s exit:0 rump.ifconfig shmif0 linkstr $BUS - atf_check -s exit:0 rump.ifconfig shmif0 up + atf_ifconfig shmif0 up - atf_check -s exit:0 rump.ifconfig pppoe0 create - $inet && atf_check -s exit:0 rump.ifconfig pppoe0 \ + $inet && atf_ifconfig pppoe0 \ inet 0.0.0.0 0.0.0.1 down - atf_check -s exit:0 -x "$HIJACKING pppoectl -e shmif0 pppoe0" + $DEBUG && rump.ifconfig pppoe0 debug + $DEBUG && rump.ifconfig + $DEBUG && $HIJACKING pppoectl -d pppoe0 unset RUMP_SERVER } -cleanup() +setup() { - env RUMP_SERVER=$SERVER rump.halt - env RUMP_SERVER=$CLIENT rump.halt -} + inet=true + + if [ $# -ne 0 ]; then + eval $@ + fi + rump_server_start $SERVER netinet6 pppoe + rump_server_start $CLIENT netinet6 pppoe + + setup_ifaces + + export RUMP_SERVER=$SERVER + atf_pppoectl -e shmif0 pppoe0 + unset RUMP_SERVER -wait_for_session_established() + export RUMP_SERVER=$CLIENT + atf_pppoectl -e shmif0 pppoe0 + unset RUMP_SERVER +} + +wait_for_opened() { - local dontfail=$1 + local cp=$1 + local dontfail=$2 local n=$WAITTIME for i in $(seq $n); do - $HIJACKING pppoectl -d pppoe0 |grep -q "state = session" - [ $? = 0 ] && return + $HIJACKING pppoectl -dd pppoe0 | grep -q "$cp state: opened" + if [ $? = 0 ]; then + rump.ifconfig -w 10 + return + fi sleep 1 done @@ -114,12 +153,10 @@ local n=$WAITTIME for i in $(seq $n); do - $HIJACKING pppoectl -d pppoe0 | grep -q "state = initial" - [ $? = 0 ] && return - # If PPPoE client is disconnected by PPPoE server and then - # the client kicks callout of pppoe_timeout(), the client - # state is changed to PPPOE_STATE_PADI_SENT while padi retrying. - $HIJACKING pppoectl -d pppoe0 | grep -q "state = PADI sent" + # If PPPoE client is disconnected by PPPoE server, then + # the LCP state will of the client is in a starting to send PADI. + $HIJACKING pppoectl -dd pppoe0 | grep -q \ + -e "LCP state: initial" -e "LCP state: starting" [ $? = 0 ] && return sleep 1 @@ -133,6 +170,7 @@ run_test() { local auth=$1 + local cp="IPCP" setup # As pppoe client doesn't support rechallenge yet. @@ -142,48 +180,44 @@ fi export RUMP_SERVER=$SERVER - local setup_serverparam="pppoectl pppoe0 hisauthproto=$auth \ - 'hisauthname=$AUTHNAME' \ - 'hisauthsecret=$SECRET' \ - 'myauthproto=none' \ - $server_optparam" - atf_check -s exit:0 -x "$HIJACKING $setup_serverparam" - atf_check -s exit:0 rump.ifconfig pppoe0 up - unset RUMP_SERVER - - export RUMP_SERVER=$CLIENT - local setup_clientparam="pppoectl pppoe0 myauthproto=$auth \ - 'myauthname=$AUTHNAME' \ - 'myauthsecret=$SECRET' \ - 'hisauthproto=none'" - atf_check -s exit:0 -x "$HIJACKING $setup_clientparam" - atf_check -s exit:0 rump.ifconfig pppoe0 up + atf_pppoectl pppoe0 "hisauthproto=$auth" \ + "hisauthname=$AUTHNAME" "hisauthsecret=$SECRET" \ + "myauthproto=none" $server_optparam + atf_ifconfig pppoe0 up + unset RUMP_SERVER + + export RUMP_SERVER=$CLIENT + atf_pppoectl pppoe0 \ + "myauthname=$AUTHNAME" "myauthsecret=$SECRET" \ + "myauthproto=$auth" "hisauthproto=none" + atf_ifconfig pppoe0 up $DEBUG && rump.ifconfig - wait_for_session_established + wait_for_opened $cp atf_check -s exit:0 -o ignore rump.ping -c 1 -w $TIMEOUT $SERVER_IP unset RUMP_SERVER # test for disconnection from server export RUMP_SERVER=$SERVER - atf_check -s exit:0 rump.ifconfig pppoe0 down + atf_ifconfig pppoe0 down wait_for_disconnected export RUMP_SERVER=$CLIENT wait_for_disconnected atf_check -s not-exit:0 -o ignore -e ignore \ rump.ping -c 1 -w $TIMEOUT $SERVER_IP - atf_check -s exit:0 -o match:'PADI sent' -x "$HIJACKING pppoectl -d pppoe0" + atf_check -s exit:0 -o match:'(PADI sent)|(initial)' \ + -x "$HIJACKING pppoectl -d pppoe0" unset RUMP_SERVER - # test for recoonecting + # test for reconnecting atf_check -s exit:0 -x "env RUMP_SERVER=$SERVER rump.ifconfig pppoe0 up" export RUMP_SERVER=$CLIENT - wait_for_session_established + wait_for_opened $cp atf_check -s exit:0 -o ignore rump.ping -c 1 -w $TIMEOUT $SERVER_IP unset RUMP_SERVER # test for disconnection from client export RUMP_SERVER=$CLIENT - atf_check -s exit:0 -x rump.ifconfig pppoe0 down + atf_ifconfig pppoe0 down wait_for_disconnected export RUMP_SERVER=$SERVER wait_for_disconnected @@ -195,30 +229,30 @@ # test for reconnecting export RUMP_SERVER=$CLIENT - atf_check -s exit:0 -x rump.ifconfig pppoe0 up - wait_for_session_established + atf_ifconfig pppoe0 up + wait_for_opened $cp $DEBUG && rump.ifconfig pppoe0 $DEBUG && $HIJACKING pppoectl -d pppoe0 unset RUMP_SERVER export RUMP_SERVER=$SERVER - atf_check -s exit:0 rump.ifconfig -w 10 + atf_ifconfig -w 10 atf_check -s exit:0 -o ignore rump.ping -c 1 -w $TIMEOUT $CLIENT_IP atf_check -s exit:0 -o match:'session' -x "$HIJACKING pppoectl -d pppoe0" - $DEBUG && HIJACKING pppoectl -d pppoe0 + $DEBUG && $HIJACKING pppoectl -d pppoe0 unset RUMP_SERVER # test for invalid password export RUMP_SERVER=$CLIENT - atf_check -s exit:0 rump.ifconfig pppoe0 down + atf_ifconfig pppoe0 down wait_for_disconnected - local setup_clientparam="pppoectl pppoe0 myauthproto=$auth \ - 'myauthname=$AUTHNAME' \ - 'myauthsecret=invalidsecret' \ - 'hisauthproto=none'" - atf_check -s exit:0 -x "$HIJACKING $setup_clientparam" - atf_check -s exit:0 rump.ifconfig pppoe0 up - wait_for_session_established dontfail + atf_pppoectl pppoe0 "myauthproto=$auth" \ + "myauthname=$AUTHNAME" \ + "myauthsecret=invalidsecret" \ + "hisauthproto=none" \ + "max-auth-failure=1" + atf_ifconfig pppoe0 up + wait_for_opened $cp dontfail atf_check -s not-exit:0 -o ignore -e ignore \ rump.ping -c 1 -w $TIMEOUT $SERVER_IP atf_check -s exit:0 -o match:'DETACHED' rump.ifconfig pppoe0 @@ -240,6 +274,8 @@ pppoe_pap_cleanup() { + + $DEBUG && dump cleanup } @@ -258,12 +294,15 @@ pppoe_chap_cleanup() { + + $DEBUG && dump cleanup } run_test6() { local auth=$1 + local cp="IPv6CP" setup "inet=false" # As pppoe client doesn't support rechallenge yet. @@ -273,29 +312,25 @@ fi export RUMP_SERVER=$SERVER - local setup_serverparam="pppoectl pppoe0 hisauthproto=$auth \ - 'hisauthname=$AUTHNAME' \ - 'hisauthsecret=$SECRET' \ - 'myauthproto=none' \ - $server_optparam" - atf_check -s exit:0 -x "$HIJACKING $setup_serverparam" - atf_check -s exit:0 rump.ifconfig pppoe0 inet6 $SERVER_IP6/64 down - atf_check -s exit:0 rump.ifconfig pppoe0 up - unset RUMP_SERVER - - export RUMP_SERVER=$CLIENT - local setup_clientparam="pppoectl pppoe0 myauthproto=$auth \ - 'myauthname=$AUTHNAME' \ - 'myauthsecret=$SECRET' \ - 'hisauthproto=none'" - atf_check -s exit:0 -x "$HIJACKING $setup_clientparam" - atf_check -s exit:0 rump.ifconfig pppoe0 inet6 $CLIENT_IP6/64 down - atf_check -s exit:0 rump.ifconfig pppoe0 up + atf_pppoectl pppoe0 \ + "hisauthname=$AUTHNAME" "hisauthsecret=$SECRET" \ + "hisauthproto=$auth" "myauthproto=none" \ + $server_optparam + atf_ifconfig pppoe0 inet6 $SERVER_IP6/64 down + atf_ifconfig pppoe0 up + unset RUMP_SERVER + + export RUMP_SERVER=$CLIENT + atf_pppoectl pppoe0 \ + "myauthname=$AUTHNAME" "myauthsecret=$SECRET" \ + "myauthproto=$auth" "hisauthproto=none" + atf_ifconfig pppoe0 inet6 $CLIENT_IP6/64 down + atf_ifconfig pppoe0 up $DEBUG && rump.ifconfig - wait_for_session_established - atf_check -s exit:0 -o ignore rump.ifconfig -w 10 + wait_for_opened $cp + atf_ifconfig -w 10 export RUMP_SERVER=$SERVER - atf_check -s exit:0 -o ignore rump.ifconfig -w 10 + rump.ifconfig -w 10 export RUMP_SERVER=$CLIENT atf_check -s exit:0 -o ignore rump.ping6 -c 1 -X $TIMEOUT $SERVER_IP6 unset RUMP_SERVER @@ -303,7 +338,7 @@ # test for disconnection from server export RUMP_SERVER=$SERVER session_id=`$HIJACKING pppoectl -d pppoe0 | grep state` - atf_check -s exit:0 rump.ifconfig pppoe0 down + atf_ifconfig pppoe0 down wait_for_disconnected export RUMP_SERVER=$CLIENT wait_for_disconnected @@ -312,21 +347,21 @@ atf_check -s exit:0 -o not-match:"$session_id" -x "$HIJACKING pppoectl -d pppoe0" unset RUMP_SERVER - # test for recoonecting + # test for reconnecting export RUMP_SERVER=$SERVER - atf_check -s exit:0 rump.ifconfig pppoe0 up - wait_for_session_established - atf_check -s exit:0 rump.ifconfig -w 10 + atf_ifconfig pppoe0 up + wait_for_opened $cp + atf_ifconfig -w 10 $DEBUG && $HIJACKING pppoectl -d pppoe0 $DEBUG && rump.ifconfig pppoe0 export RUMP_SERVER=$CLIENT - atf_check -s exit:0 -o ignore rump.ifconfig -w 10 + atf_ifconfig -w 10 atf_check -s exit:0 -o ignore rump.ping6 -c 1 -X $TIMEOUT $SERVER_IP6 unset RUMP_SERVER # test for disconnection from client export RUMP_SERVER=$CLIENT - atf_check -s exit:0 rump.ifconfig pppoe0 down + atf_ifconfig pppoe0 down wait_for_disconnected export RUMP_SERVER=$SERVER @@ -339,16 +374,16 @@ # test for reconnecting export RUMP_SERVER=$CLIENT - atf_check -s exit:0 rump.ifconfig pppoe0 up - wait_for_session_established - atf_check -s exit:0 rump.ifconfig -w 10 + atf_ifconfig pppoe0 up + wait_for_opened $cp + atf_ifconfig -w 10 $DEBUG && rump.ifconfig pppoe0 $DEBUG && $HIJACKING pppoectl -d pppoe0 unset RUMP_SERVER export RUMP_SERVER=$SERVER - atf_check -s exit:0 rump.ifconfig -w 10 + atf_ifconfig -w 10 atf_check -s exit:0 -o ignore rump.ping6 -c 1 -X $TIMEOUT $CLIENT_IP6 atf_check -s exit:0 -o match:'session' -x "$HIJACKING pppoectl -d pppoe0" $DEBUG && HIJACKING pppoectl -d pppoe0 @@ -356,15 +391,14 @@ # test for invalid password export RUMP_SERVER=$CLIENT - atf_check -s exit:0 rump.ifconfig pppoe0 down + atf_ifconfig pppoe0 down wait_for_disconnected - local setup_clientparam="pppoectl pppoe0 myauthproto=$auth \ - 'myauthname=$AUTHNAME' \ - 'myauthsecret=invalidsecret' \ - 'hisauthproto=none'" - atf_check -s exit:0 -x "$HIJACKING $setup_clientparam" - atf_check -s exit:0 rump.ifconfig pppoe0 up - wait_for_session_established dontfail + atf_pppoectl pppoe0 \ + "myauthname=$AUTHNAME" "myauthsecret=invalidsecret" \ + "myauthproto=$auth" "hisauthproto=none" \ + "max-auth-failure=1" + atf_ifconfig pppoe0 up + wait_for_opened $cp dontfail atf_check -s not-exit:0 -o ignore -e ignore \ rump.ping6 -c 1 -X $TIMEOUT $SERVER_IP6 atf_check -s exit:0 -o match:'DETACHED' rump.ifconfig pppoe0 @@ -386,6 +420,8 @@ pppoe6_pap_cleanup() { + + $DEBUG && dump cleanup } @@ -404,13 +440,482 @@ pppoe6_chap_cleanup() { + + $DEBUG && dump + cleanup +} + +atf_test_case pppoe_params cleanup + +dump_bus() +{ + + shmif_dumpbus -p - ${BUS} | tcpdump -n -e -r - +} + +setup_auth_conf() +{ + local auth=chap + local server_optparam="norechallenge" + + export RUMP_SERVER=$SERVER + atf_ifconfig pppoe0 link0 + atf_pppoectl pppoe0 \ + "hisauthname=$AUTHNAME" "hisauthsecret=$SECRET" \ + "hisauthproto=$auth" "myauthproto=none" \ + $server_optparam + unset RUMP_SERVER + + export RUMP_SERVER=$CLIENT + $inet && atf_ifconfig pppoe0 \ + inet 0.0.0.0 0.0.0.1 down + atf_pppoectl pppoe0 \ + "myauthname=$AUTHNAME" "myauthsecret=$SECRET" \ + "myauthproto=$auth" "hisauthproto=none" + + $DEBUG && rump.ifconfig + unset RUMP_SERVER +} + +pppoe_params_head() +{ + atf_set "descr" "Set and clear access concentrator name and service name" + atf_set "require.progs" "rump_server pppoectl" +} + +pppoe_params_body() +{ + local dumpcmd + local cp="LCP" + + dumpcmd="shmif_dumpbus -p - ${BUS}" + dumpcmd="${dumpcmd} | tcpdump -n -e -r -" + + rump_server_start $SERVER netinet6 pppoe + rump_server_start $CLIENT netinet6 pppoe + + setup_ifaces + setup_auth_conf + + export RUMP_SERVER=$SERVER + atf_pppoectl -e shmif0 pppoe0 + atf_ifconfig pppoe0 up + unset RUMP_SERVER + + export RUMP_SERVER=$CLIENT + atf_pppoectl -e shmif0 pppoe0 + atf_ifconfig pppoe0 up + $DEBUG && rump.ifconfig + wait_for_opened $cp + unset RUMP_SERVER + + $DEBUG && dump_bus + atf_check -s exit:0 -o match:'\[Service-Name\]' -e ignore \ + -x "${dumpcmd} | grep PADI" + atf_check -s exit:0 -o match:'\[Service-Name\]' -e ignore \ + -x "${dumpcmd} | grep PADR" + atf_check -s exit:0 -o not-match:'AC-Name' -e ignore \ + -x "${dumpcmd} | grep PADI" + + # set Remote access concentrator name (AC-NAME, -a option) + export RUMP_SERVER=$CLIENT + atf_ifconfig pppoe0 down + wait_for_disconnected + atf_pppoectl -e shmif0 -a ACNAME-TEST0 pppoe0 + atf_ifconfig pppoe0 up + $DEBUG && rump.ifconfig + wait_for_opened $cp + unset RUMP_SERVER + + $DEBUG && dump_bus + atf_check -s exit:0 -o match:'\[AC-Name "ACNAME-TEST0"\]' -e ignore \ + -x "${dumpcmd} | grep PADI" + + # change AC-NAME + export RUMP_SERVER=$CLIENT + atf_ifconfig pppoe0 down + wait_for_disconnected + atf_pppoectl -e shmif0 -a ACNAME-TEST1 pppoe0 + atf_ifconfig pppoe0 up + $DEBUG && rump.ifconfig + wait_for_opened $cp + unset RUMP_SERVER + + $DEBUG && dump_bus + atf_check -s exit:0 -o match:'\[AC-Name "ACNAME-TEST1"\]' -e ignore \ + -x "${dumpcmd} | grep PADI" + + # clear AC-NAME + rump_server_destroy_ifaces + rm ${BUS} 2> /dev/null + setup_ifaces + setup_auth_conf + + export RUMP_SERVER=$SERVER + atf_pppoectl -e shmif0 pppoe0 + atf_ifconfig pppoe0 up + unset RUMP_SERVER + + export RUMP_SERVER=$CLIENT + atf_ifconfig pppoe0 down + wait_for_disconnected + atf_pppoectl -a ACNAME-TEST2 -e shmif0 pppoe0 + atf_pppoectl -e shmif0 pppoe0 + atf_ifconfig pppoe0 up + $DEBUG && rump.ifconfig + wait_for_opened $cp + unset RUMP_SERVER + + $DEBUG && dump_bus + atf_check -s exit:0 -o match:'\[Service-Name\]' -e ignore \ + -x "${dumpcmd} | grep PADI" + atf_check -s exit:0 -o match:'\[Service-Name\]' -e ignore \ + -x "${dumpcmd} | grep PADR" + atf_check -s exit:0 -o not-match:'AC-Name' -e ignore \ + -x "${dumpcmd} | grep PADI" + + # store 0 length string in AC-NAME + export RUMP_SERVER=$CLIENT + atf_ifconfig pppoe0 down + wait_for_disconnected + atf_pppoectl -a \"\" -e shmif0 pppoe0 + atf_ifconfig pppoe0 up + $DEBUG && rump.ifconfig + wait_for_opened $cp + unset RUMP_SERVER + + atf_check -s exit:0 -o match:'\[AC-Name\]' -e ignore \ + -x "${dumpcmd} | grep PADI" + + # set Service Name (Service-Name, -s option) + rump_server_destroy_ifaces + rm ${BUS} 2> /dev/null + setup_ifaces + setup_auth_conf + + export RUMP_SERVER=$SERVER + atf_pppoectl -e shmif0 pppoe0 + atf_ifconfig pppoe0 up + unset RUMP_SERVER + + export RUMP_SERVER=$CLIENT + atf_ifconfig pppoe0 down + wait_for_disconnected + atf_pppoectl -e shmif0 -s SNAME-TEST0 pppoe0 + atf_ifconfig pppoe0 up + $DEBUG && rump.ifconfig + wait_for_opened $cp + unset RUMP_SERVER + + $DEBUG && dump_bus + atf_check -s exit:0 -o match:'\[Service-Name "SNAME-TEST0"\]' -e ignore \ + -x "${dumpcmd} | grep PADI" + atf_check -s exit:0 -o match:'\[Service-Name "SNAME-TEST0"\]' -e ignore \ + -x "${dumpcmd} | grep PADR" + atf_check -s exit:0 -o not-match:'AC-Name' -e ignore \ + -x "${dumpcmd} | grep PADI" + + # change Service-Name + export RUMP_SERVER=$CLIENT + atf_ifconfig pppoe0 down + wait_for_disconnected + atf_pppoectl -e shmif0 -s SNAME-TEST1 pppoe0 + atf_ifconfig pppoe0 up + $DEBUG && rump.ifconfig + wait_for_opened $cp + unset RUMP_SERVER + + $DEBUG && dump_bus + atf_check -s exit:0 -o match:'\[Service-Name "SNAME-TEST1"\]' -e ignore \ + -x "${dumpcmd} | grep PADI" + atf_check -s exit:0 -o match:'\[Service-Name "SNAME-TEST1"\]' -e ignore \ + -x "${dumpcmd} | grep PADR" + + # clear Service-Name + rump_server_destroy_ifaces + rm ${BUS} 2> /dev/null + setup_ifaces + setup_auth_conf + + export RUMP_SERVER=$SERVER + atf_pppoectl -e shmif0 pppoe0 + atf_ifconfig pppoe0 up + unset RUMP_SERVER + + export RUMP_SERVER=$CLIENT + atf_ifconfig pppoe0 down + wait_for_disconnected + atf_pppoectl -s SNAME-TEST2 -e shmif0 pppoe0 + atf_pppoectl -e shmif0 pppoe0 + atf_ifconfig pppoe0 up + $DEBUG && rump.ifconfig + wait_for_opened $cp + unset RUMP_SERVER + + $DEBUG && dump_bus + atf_check -s exit:0 -o match:'\[Service-Name\]' -e ignore \ + -x "${dumpcmd} | grep PADI" + atf_check -s exit:0 -o match:'\[Service-Name\]' -e ignore \ + -x "${dumpcmd} | grep PADR" + atf_check -s exit:0 -o not-match:'AC-Name' -e ignore \ + -x "${dumpcmd} | grep PADI" + + # set AC-NAME and Service-Name + rump_server_destroy_ifaces + rm ${BUS} 2> /dev/null + setup_ifaces + setup_auth_conf + + export RUMP_SERVER=$SERVER + atf_pppoectl -e shmif0 pppoe0 + atf_ifconfig pppoe0 up + unset RUMP_SERVER + + export RUMP_SERVER=$CLIENT + atf_ifconfig pppoe0 down + wait_for_disconnected + atf_pppoectl -e shmif0 -a ACNAME-TEST3 -s SNAME-TEST3 pppoe0 + atf_ifconfig pppoe0 up + $DEBUG && rump.ifconfig + wait_for_opened $cp + unset RUMP_SERVER + + $DEBUG && dump_bus + atf_check -s exit:0 \ + -o match:'\[Service-Name "SNAME-TEST3"\] \[AC-Name "ACNAME-TEST3"\]' \ + -e ignore \ + -x "${dumpcmd} | grep PADI" + atf_check -s exit:0 -o match:'\[Service-Name "SNAME-TEST3"\]' -e ignore \ + -x "${dumpcmd} | grep PADR" + + # change AC-NAME + export RUMP_SERVER=$CLIENT + atf_ifconfig pppoe0 down + wait_for_disconnected + atf_pppoectl -e shmif0 -a ACNAME-TEST4 pppoe0 + atf_ifconfig pppoe0 up + $DEBUG && rump.ifconfig + wait_for_opened $cp + unset RUMP_SERVER + + $DEBUG && dump_bus + atf_check -s exit:0 \ + -o match:'\[Service-Name\] \[AC-Name "ACNAME-TEST4"\]' \ + -e ignore \ + -x "${dumpcmd} | grep PADI" + atf_check -s exit:0 -o match:'\[Service-Name\]' -e ignore \ + -x "${dumpcmd} | grep PADR" + + # change Service-Name + export RUMP_SERVER=$CLIENT + atf_ifconfig pppoe0 down + wait_for_disconnected + atf_pppoectl -e shmif0 -a ACNAME-TEST5 -s SNAME-TEST5 pppoe0 + atf_pppoectl -e shmif0 -s SNAME-TEST6 pppoe0 + atf_ifconfig pppoe0 up + $DEBUG && rump.ifconfig + wait_for_opened $cp + unset RUMP_SERVER + + $DEBUG && dump_bus + atf_check -s exit:0 \ + -o match:'\[Service-Name "SNAME-TEST6"\]' \ + -e ignore \ + -x "${dumpcmd} | grep PADI" + atf_check -s exit:0 -o match:'\[Service-Name "SNAME-TEST6"\]' -e ignore \ + -x "${dumpcmd} | grep PADR" + atf_check -s exit:0 -o not-match:'\[AC-Name "ACNAME-TEST5\]"' -e ignore \ + -x "${dumpcmd} | grep PADI" + + export RUMP_SERVER=$CLIENT + atf_ifconfig pppoe0 down + export RUMP_SERVER=$SERVER + wait_for_disconnected + + # ipcp & ipv6cp are enabled by default + export RUMP_SERVER=$CLIENT + atf_check -s exit:0 -o match:'ipcp: enable' \ + -x "$HIJACKING pppoectl pppoe0" + atf_check -s exit:0 -o match:'ipv6cp: enable' \ + -x "$HIJACKING pppoectl pppoe0" + + # ipcp enable & ipv6cp disable + atf_pppoectl pppoe0 noipv6cp + atf_ifconfig pppoe0 up + wait_for_opened "IPCP" + atf_check -s exit:0 -o match:'IPv6CP state: closed' \ + -x "$HIJACKING pppoectl -dd pppoe0" + + atf_ifconfig pppoe0 down + export RUMP_SERVER=$SERVER + wait_for_disconnected + + # ipcp disable & ipv6cp enable + export RUMP_SERVER=$CLIENT + atf_pppoectl pppoe0 noipcp ipv6cp + atf_ifconfig pppoe0 up + wait_for_opened "IPv6CP" + atf_check -s exit:0 -o match:'IPCP state: closed' \ + -x "$HIJACKING pppoectl -dd pppoe0" +} + +pppoe_params_cleanup() +{ + + $DEBUG && dump + cleanup +} + +pppoe_passiveauthproto() +{ + local auth=$1 + local cp="IPCP" + setup + + local server_optparam="" + if [ $auth = "chap" ]; then + server_optparam="norechallenge" + fi + + export RUMP_SERVER=$SERVER + atf_pppoectl pppoe0 \ + "hisauthname=$AUTHNAME" "hisauthsecret=$SECRET" \ + "hisauthproto=$auth" "myauthproto=none" \ + $server_optparam + atf_ifconfig pppoe0 up + + export RUMP_SERVER=$CLIENT + atf_pppoectl pppoe0 \ + "myauthname=$AUTHNAME" "myauthsecret=$SECRET" \ + "myauthproto=none" "hisauthproto=none" \ + "passiveauthproto" + atf_ifconfig pppoe0 up + $DEBUG && rump.ifconfig + wait_for_opened $cp + atf_ifconfig -w 10 + atf_check -s exit:0 -o ignore rump.ping -c 1 -w $TIMEOUT $SERVER_IP +} + +atf_test_case pppoe_passiveauthproto_pap cleanup +pppoe_passiveauthproto_pap_head() +{ + + atf_set "descr" "Test for passiveauthproto option on PAP" + atf_set "require.progs" "rump_server" +} + +pppoe_passiveauthproto_pap_body() +{ + + pppoe_passiveauthproto "pap" +} + +pppoe_passiveauthproto_pap_cleanup() +{ + + $DEBUG && dump + cleanup +} + +atf_test_case pppoe_passiveauthproto_chap cleanup +pppoe_passiveauthproto_chap_head() +{ + + atf_set "descr" "Test for passiveauthproto option on chap" + atf_set "require.progs" "rump_server" +} + +pppoe_passiveauthproto_chap_body() +{ + + pppoe_passiveauthproto "chap" +} + +pppoe_passiveauthproto_chap_cleanup() +{ + + $DEBUG && dump + cleanup +} + +atf_test_case pppoe_mtu cleanup +pppoe_mtu_head() +{ + + atf_set "descr" "Test for mtu" + atf_set "require.progs" "rump_server" +} + +pppoe_mtu_body() +{ + local auth=chap + local cp="IPCP" + setup + + export RUMP_SERVER=$SERVER + atf_pppoectl pppoe0 \ + "hisauthname=$AUTHNAME" "hisauthsecret=$SECRET" \ + "hisauthproto=$auth" "myauthproto=none" \ + norechallenge + atf_ifconfig pppoe0 mtu 1400 + atf_ifconfig pppoe0 up + + export RUMP_SERVER=$CLIENT + atf_pppoectl pppoe0 \ + "myauthname=$AUTHNAME" "myauthsecret=$SECRET" \ + "myauthproto=$auth" "hisauthproto=none" + atf_ifconfig pppoe0 mtu 1450 + atf_ifconfig pppoe0 up + + wait_for_opened $cp + atf_ifconfig -w 10 + + export RUMP_SERVER=$SERVER + atf_check -s exit:0 -o match:'mtu 1400' rump.ifconfig pppoe0 + + export RUMP_SERVER=$CLIENT + atf_check -s exit:0 -o match:'mtu 1400' rump.ifconfig pppoe0 + + # mtu can set to 1460 but it is not applied. + atf_ifconfig pppoe0 mtu 1460 + atf_check -s exit:0 -o match:'mtu 1400' rump.ifconfig pppoe0 + + export RUMP_SERVER=$SERVER + atf_ifconfig pppoe0 mtu 1470 + atf_ifconfig pppoe0 down + atf_ifconfig pppoe0 up + wait_for_opened $cp + atf_ifconfig -w 10 + + # mtu 1460 is applied after LCP negotiation + atf_check -s exit:0 -o match:'mtu 1460' rump.ifconfig pppoe0 + + export RUMP_SERVER=$CLIENT + atf_check -s exit:0 -o match:'mtu 1460' rump.ifconfig pppoe0 + + rump.ifconfig pppoe0 mtu 1500 + atf_check -s exit:0 -o ignore \ + -e match:'SIOCSIFMTU: Invalid argument' \ + rump.ifconfig pppoe0 mtu 1501 +} + +pppoe_mtu_cleanup() +{ + + $DEBUG && dump cleanup } atf_init_test_cases() { + + atf_add_test_case pppoe_create_destroy + atf_add_test_case pppoe_params atf_add_test_case pppoe_pap atf_add_test_case pppoe_chap atf_add_test_case pppoe6_pap atf_add_test_case pppoe6_chap + atf_add_test_case pppoe_passiveauthproto_pap + atf_add_test_case pppoe_passiveauthproto_chap + atf_add_test_case pppoe_mtu } diff --git a/net/if_tap/Makefile b/net/if_tap/Makefile --- a/net/if_tap/Makefile +++ b/net/if_tap/Makefile @@ -1,9 +1,15 @@ -# $NetBSD: Makefile,v 1.2 2016/11/25 08:51:16 ozaki-r Exp $ +# $NetBSD: Makefile,v 1.6 2020/10/01 13:49:18 rin Exp $ # .include -TESTSDIR= ${TESTSBASE}/net/if_tap +PROG= rump_open_tap +MAN= # empty +DPADD= ${LIBRUMPRES} ${LIBRUMPCLIENT} +LDADD= -lrumpres -lrumpclient +BINDIR.rump_open_tap= ${TESTSDIR} + +TESTSDIR= ${TESTSBASE}/net/if_tap .for name in tap TESTS_SH+= t_${name} diff --git a/net/if_tap/rump_open_tap.c b/net/if_tap/rump_open_tap.c new file mode 100644 --- /dev/null +++ b/net/if_tap/rump_open_tap.c @@ -0,0 +1,79 @@ +/* $NetBSD: rump_open_tap.c,v 1.1 2020/09/30 14:43:15 roy Exp $ */ + +/* + * Copyright (c) 2020 The NetBSD Foundation, Inc. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Roy Marples. + * + * 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 ``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 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 +__RCSID("$NetBSD: rump_open_tap.c,v 1.1 2020/09/30 14:43:15 roy Exp $"); + +#include +#include +#include +#include +#include + +#include +#include + +int +main(int argc, char **argv) +{ + int fd; + + if (argc < 2) + errx(EXIT_FAILURE, "No path specified"); + + rumpclient_init(); + + fd = rump_sys_open(argv[1], O_RDWR); + if (fd == -1) + err(EXIT_FAILURE, "open: %s", argv[1]); + + if (argc > 2) { + FILE *fp; + pid_t pid; + + fp = fopen(argv[2], "w"); + if (fp == NULL) + err(EXIT_FAILURE, "fopen: %s", argv[2]); + + pid = fork(); + switch (pid) { + case -1: + err(EXIT_FAILURE, "fork"); + case 0: + break; + default: + fprintf(fp, "%d\n", pid); + exit(EXIT_SUCCESS); + } + } + + /* Spin with the fd open */ + for (;;) + sleep(100); +} diff --git a/net/if_tap/t_tap.sh b/net/if_tap/t_tap.sh old mode 100755 new mode 100644 --- a/net/if_tap/t_tap.sh +++ b/net/if_tap/t_tap.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_tap.sh,v 1.6 2016/11/25 08:51:16 ozaki-r Exp $ +# $NetBSD: t_tap.sh,v 1.11 2020/09/30 14:43:15 roy Exp $ # # Copyright (c) 2016 Internet Initiative Japan Inc. # All rights reserved. @@ -34,6 +34,7 @@ IP6_LOCAL=fc00::1 IP6_TAP=fc00::2 IP6_REMOTE=fc00::3 +TAP_PID=./.__tap.pid DEBUG=${DEBUG:-false} TIMEOUT=1 @@ -49,25 +50,9 @@ tap_create_destroy_body() { - rump_server_start $SOCK_LOCAL netinet6 tap + rump_server_fs_start $SOCK_LOCAL netinet6 tap - export RUMP_SERVER=${SOCK_LOCAL} - - # Create and destroy (no address) - atf_check -s exit:0 rump.ifconfig tap0 create - atf_check -s exit:0 rump.ifconfig tap0 destroy - - # Create and destroy (with an IPv4 address) - atf_check -s exit:0 rump.ifconfig tap0 create - atf_check -s exit:0 rump.ifconfig tap0 $IP4_TAP - atf_check -s exit:0 rump.ifconfig tap0 up - atf_check -s exit:0 rump.ifconfig tap0 destroy - - # Create and destroy (with an IPv6 address) - atf_check -s exit:0 rump.ifconfig tap0 create - atf_check -s exit:0 rump.ifconfig tap0 inet6 $IP6_TAP - atf_check -s exit:0 rump.ifconfig tap0 up - atf_check -s exit:0 rump.ifconfig tap0 destroy + test_create_destroy_common $SOCK_LOCAL tap0 true } tap_create_destroy_cleanup() @@ -88,8 +73,8 @@ tap_stand_alone_body() { - rump_server_start $SOCK_LOCAL netinet6 tap - rump_server_start $SOCK_REMOTE netinet6 tap + rump_server_fs_start $SOCK_LOCAL netinet6 tap + rump_server_fs_start $SOCK_REMOTE netinet6 tap rump_server_add_iface $SOCK_LOCAL shmif0 $BUS rump_server_add_iface $SOCK_REMOTE shmif0 $BUS @@ -98,10 +83,6 @@ atf_check -s exit:0 rump.ifconfig shmif0 $IP4_LOCAL atf_check -s exit:0 rump.ifconfig shmif0 inet6 $IP6_LOCAL atf_check -s exit:0 rump.ifconfig shmif0 up - atf_check -s exit:0 rump.ifconfig tap0 create - atf_check -s exit:0 rump.ifconfig tap0 $IP4_TAP - atf_check -s exit:0 rump.ifconfig tap0 inet6 $IP6_TAP - atf_check -s exit:0 rump.ifconfig tap0 up atf_check -s exit:0 rump.ifconfig -w 10 export RUMP_SERVER=${SOCK_REMOTE} @@ -112,14 +93,23 @@ atf_check -s exit:0 rump.ifconfig -w 10 atf_check -s exit:0 -o ignore rump.ping -n -w $TIMEOUT -c 1 $IP4_LOCAL - # Cannot reach to an alone tap - atf_check -s not-exit:0 -o ignore -e ignore \ - rump.ping -n -w $TIMEOUT -c 1 $IP4_TAP - atf_check -s exit:0 -o ignore rump.ping6 -n -X $TIMEOUT -c 1 $IP6_LOCAL + + export RUMP_SERVER=${SOCK_LOCAL} + atf_check -s exit:0 rump.ifconfig shmif0 $IP4_LOCAL delete + atf_check -s exit:0 rump.ifconfig shmif0 inet6 $IP6_LOCAL delete + rump_server_add_iface $SOCK_LOCAL tap0 + atf_check -s exit:0 rump.ifconfig tap0 $IP4_TAP + atf_check -s exit:0 rump.ifconfig tap0 inet6 $IP6_TAP + atf_check -s exit:0 rump.ifconfig tap0 up + atf_check -s exit:0 rump.ifconfig -w 10 + + export RUMP_SERVER=${SOCK_REMOTE} # Cannot reach to an alone tap atf_check -s not-exit:0 -o ignore -e ignore \ rump.ping6 -n -X $TIMEOUT -c 1 $IP6_TAP + atf_check -s not-exit:0 -o ignore -e ignore \ + rump.ping -n -w $TIMEOUT -c 1 $IP4_TAP rump_server_destroy_ifaces } @@ -141,9 +131,10 @@ tap_bridged_body() { + local src="$(atf_get_srcdir)" - rump_server_start $SOCK_LOCAL netinet6 tap bridge - rump_server_start $SOCK_REMOTE netinet6 tap + rump_server_fs_start $SOCK_LOCAL netinet6 tap bridge + rump_server_fs_start $SOCK_REMOTE netinet6 tap rump_server_add_iface $SOCK_LOCAL shmif0 $BUS rump_server_add_iface $SOCK_REMOTE shmif0 $BUS @@ -153,19 +144,20 @@ atf_check -s exit:0 rump.ifconfig shmif0 $IP4_LOCAL atf_check -s exit:0 rump.ifconfig shmif0 inet6 $IP6_LOCAL atf_check -s exit:0 rump.ifconfig shmif0 up - atf_check -s exit:0 rump.ifconfig tap0 create + rump_server_add_iface $SOCK_LOCAL tap0 atf_check -s exit:0 rump.ifconfig tap0 $IP4_TAP atf_check -s exit:0 rump.ifconfig tap0 inet6 $IP6_TAP atf_check -s exit:0 rump.ifconfig tap0 up - atf_check -s exit:0 rump.ifconfig -w 10 - atf_check -s exit:0 rump.ifconfig bridge0 create + rump_server_add_iface $SOCK_LOCAL bridge0 atf_check -s exit:0 rump.ifconfig bridge0 up export LD_PRELOAD=/usr/lib/librumphijack.so atf_check -s exit:0 brconfig bridge0 add shmif0 atf_check -s exit:0 brconfig bridge0 add tap0 unset LD_PRELOAD + atf_check -s exit:0 rump.ifconfig -w 10 + export RUMP_SERVER=${SOCK_REMOTE} atf_check -s exit:0 rump.ifconfig shmif0 $IP4_REMOTE @@ -173,10 +165,22 @@ atf_check -s exit:0 rump.ifconfig shmif0 up atf_check -s exit:0 rump.ifconfig -w 10 + # shmif0 on the server bridge is active, we expect this to work atf_check -s exit:0 -o ignore rump.ping -n -w $TIMEOUT -c 1 $IP4_LOCAL - atf_check -s exit:0 -o ignore rump.ping -n -w $TIMEOUT -c 1 $IP4_TAP - atf_check -s exit:0 -o ignore rump.ping6 -n -X $TIMEOUT -c 1 $IP6_LOCAL + + # The tap is not open, we expect this to fail + atf_check -s not-exit:0 -o ignore rump.ping -n -w $TIMEOUT -c 1 $IP4_TAP + atf_check -s not-exit:0 -o ignore rump.ping6 -n -X $TIMEOUT -c 1 $IP6_TAP + + # Open the tap on the server + export RUMP_SERVER=${SOCK_LOCAL} + atf_check -s exit:0 "$src"/rump_open_tap /dev/tap0 $TAP_PID + atf_check -s exit:0 rump.ifconfig -w 10 + + # Now we can ping the tap address + export RUMP_SERVER=${SOCK_LOCAL} + atf_check -s exit:0 -o ignore rump.ping -n -w $TIMEOUT -c 1 $IP4_TAP atf_check -s exit:0 -o ignore rump.ping6 -n -X $TIMEOUT -c 1 $IP6_TAP rump_server_destroy_ifaces @@ -185,6 +189,11 @@ tap_bridged_cleanup() { + if [ -f $TAP_PID ]; then + kill -9 $(cat $TAP_PID) + rm -f $TAP_PID + sleep 1 + fi $DEBUG && dump cleanup } diff --git a/net/if_tun/Makefile b/net/if_tun/Makefile new file mode 100644 --- /dev/null +++ b/net/if_tun/Makefile @@ -0,0 +1,13 @@ +# $NetBSD: Makefile,v 1.2 2018/02/01 05:22:02 ozaki-r Exp $ +# + +.include + +TESTSDIR= ${TESTSBASE}/net/if_tun + +.for name in tun +TESTS_SH+= t_${name} +TESTS_SH_SRC_t_${name}= ../net_common.sh t_${name}.sh +.endfor + +.include diff --git a/net/if_tun/t_tun.sh b/net/if_tun/t_tun.sh old mode 100755 new mode 100644 --- a/net/if_tun/t_tun.sh +++ b/net/if_tun/t_tun.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_tun.sh,v 1.4 2016/11/07 05:25:37 ozaki-r Exp $ +# $NetBSD: t_tun.sh,v 1.6 2019/05/13 17:55:09 bad Exp $ # # Copyright (c) 2016 Internet Initiative Japan Inc. # All rights reserved. @@ -26,7 +26,7 @@ # RUMP_FLAGS="-lrumpnet -lrumpnet_net -lrumpnet_netinet -lrumpnet_netinet6" -RUMP_FLAGS="$RUMP_FLAGS -lrumpnet_shmif -lrumpnet_tun -lrumpdev" +RUMP_FLAGS="$RUMP_FLAGS -lrumpnet_shmif -lrumpnet_tun" BUS=bus SOCK_LOCAL=unix://commsock1 @@ -49,12 +49,7 @@ atf_check -s exit:0 rump_server ${RUMP_FLAGS} ${SOCK_LOCAL} - export RUMP_SERVER=${SOCK_LOCAL} - - atf_check -s exit:0 rump.ifconfig tun0 create - atf_check -s exit:0 rump.ifconfig tun0 up - atf_check -s exit:0 rump.ifconfig tun0 down - atf_check -s exit:0 rump.ifconfig tun0 destroy + test_create_destroy_common $SOCK_LOCAL tun0 } tun_create_destroy_cleanup() diff --git a/net/if_vether/Makefile b/net/if_vether/Makefile new file mode 100644 --- /dev/null +++ b/net/if_vether/Makefile @@ -0,0 +1,13 @@ +# $NetBSD: Makefile,v 1.1 2020/09/29 19:41:48 roy Exp $ +# + +.include + +TESTSDIR= ${TESTSBASE}/net/if_vether + +.for name in vether +TESTS_SH+= t_${name} +TESTS_SH_SRC_t_${name}= ../net_common.sh t_${name}.sh +.endfor + +.include diff --git a/net/if_vether/t_vether.sh b/net/if_vether/t_vether.sh new file mode 100644 --- /dev/null +++ b/net/if_vether/t_vether.sh @@ -0,0 +1,185 @@ +# $NetBSD: t_vether.sh,v 1.1 2020/09/29 19:41:48 roy Exp $ +# +# Copyright (c) 2016 Internet Initiative Japan Inc. +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +SOCK_LOCAL=unix://commsock1 +SOCK_REMOTE=unix://commsock2 +BUS=bus1 +IP4_LOCAL=10.0.0.1 +IP4_VETHER=10.0.0.2 +IP4_REMOTE=10.0.0.3 +IP6_LOCAL=fc00::1 +IP6_VETHER=fc00::2 +IP6_REMOTE=fc00::3 + +DEBUG=${DEBUG:-false} +TIMEOUT=1 + +atf_test_case vether_create_destroy cleanup +vether_create_destroy_head() +{ + + atf_set "descr" "tests of creation and deletion of vether interface" + atf_set "require.progs" "rump_server" +} + +vether_create_destroy_body() +{ + + rump_server_fs_start $SOCK_LOCAL netinet6 vether + + test_create_destroy_common $SOCK_LOCAL vether0 true +} + +vether_create_destroy_cleanup() +{ + + $DEBUG && dump + cleanup +} + +atf_test_case vether_stand_alone cleanup +vether_create_destroy_head() +{ + + atf_set "descr" "tests of alone vether interface" + atf_set "require.progs" "rump_server" +} + +vether_stand_alone_body() +{ + + rump_server_fs_start $SOCK_LOCAL netinet6 vether + rump_server_fs_start $SOCK_REMOTE netinet6 vether + + rump_server_add_iface $SOCK_LOCAL shmif0 $BUS + rump_server_add_iface $SOCK_REMOTE shmif0 $BUS + + export RUMP_SERVER=${SOCK_LOCAL} + atf_check -s exit:0 rump.ifconfig shmif0 $IP4_LOCAL + atf_check -s exit:0 rump.ifconfig shmif0 inet6 $IP6_LOCAL + atf_check -s exit:0 rump.ifconfig shmif0 up + atf_check -s exit:0 rump.ifconfig -w 10 + + export RUMP_SERVER=${SOCK_REMOTE} + atf_check -s exit:0 rump.ifconfig shmif0 $IP4_REMOTE + atf_check -s exit:0 rump.ifconfig shmif0 inet6 $IP6_REMOTE + atf_check -s exit:0 rump.ifconfig shmif0 up + atf_check -s exit:0 rump.ifconfig -w 10 + + atf_check -s exit:0 -o ignore rump.ping -n -w $TIMEOUT -c 1 $IP4_LOCAL + atf_check -s exit:0 -o ignore rump.ping6 -n -X $TIMEOUT -c 1 $IP6_LOCAL + + export RUMP_SERVER=${SOCK_LOCAL} + atf_check -s exit:0 rump.ifconfig shmif0 $IP4_LOCAL delete + atf_check -s exit:0 rump.ifconfig shmif0 inet6 $IP6_LOCAL delete + rump_server_add_iface $SOCK_LOCAL vether0 + atf_check -s exit:0 rump.ifconfig vether0 $IP4_VETHER + atf_check -s exit:0 rump.ifconfig vether0 inet6 $IP6_VETHER + atf_check -s exit:0 rump.ifconfig vether0 up + atf_check -s exit:0 rump.ifconfig -w 10 + + export RUMP_SERVER=${SOCK_REMOTE} + # Cannot reach to an alone vether + atf_check -s not-exit:0 -o ignore -e ignore \ + rump.ping6 -n -X $TIMEOUT -c 1 $IP6_VETHER + atf_check -s not-exit:0 -o ignore -e ignore \ + rump.ping -n -w $TIMEOUT -c 1 $IP4_VETHER + + rump_server_destroy_ifaces +} + +vether_stand_alone_cleanup() +{ + + $DEBUG && dump + cleanup +} + +atf_test_case vether_bridged cleanup +vether_bridged_head() +{ + + atf_set "descr" "tests of alone vether interface" + atf_set "require.progs" "rump_server" +} + +vether_bridged_body() +{ + + rump_server_fs_start $SOCK_LOCAL netinet6 vether bridge + rump_server_fs_start $SOCK_REMOTE netinet6 vether + + rump_server_add_iface $SOCK_LOCAL shmif0 $BUS + rump_server_add_iface $SOCK_REMOTE shmif0 $BUS + + export RUMP_SERVER=${SOCK_LOCAL} + atf_check -s exit:0 rump.ifconfig shmif0 $IP4_LOCAL + atf_check -s exit:0 rump.ifconfig shmif0 inet6 $IP6_LOCAL + atf_check -s exit:0 rump.ifconfig shmif0 up + rump_server_add_iface $SOCK_LOCAL vether0 + atf_check -s exit:0 rump.ifconfig vether0 $IP4_VETHER + atf_check -s exit:0 rump.ifconfig vether0 inet6 $IP6_VETHER + atf_check -s exit:0 rump.ifconfig vether0 up + + rump_server_add_iface $SOCK_LOCAL bridge0 + atf_check -s exit:0 rump.ifconfig bridge0 up + export LD_PRELOAD=/usr/lib/librumphijack.so + atf_check -s exit:0 brconfig bridge0 add shmif0 + atf_check -s exit:0 brconfig bridge0 add vether0 + unset LD_PRELOAD + + atf_check -s exit:0 rump.ifconfig -w 10 + + export RUMP_SERVER=${SOCK_REMOTE} + atf_check -s exit:0 rump.ifconfig shmif0 $IP4_REMOTE + atf_check -s exit:0 rump.ifconfig shmif0 inet6 $IP6_REMOTE + atf_check -s exit:0 rump.ifconfig shmif0 up + atf_check -s exit:0 rump.ifconfig -w 10 + + atf_check -s exit:0 -o ignore rump.ping -n -w $TIMEOUT -c 1 $IP4_LOCAL + atf_check -s exit:0 -o ignore rump.ping -n -w $TIMEOUT -c 1 $IP4_VETHER + + atf_check -s exit:0 -o ignore rump.ping6 -n -X $TIMEOUT -c 1 $IP6_LOCAL + atf_check -s exit:0 -o ignore rump.ping6 -n -X $TIMEOUT -c 1 $IP6_VETHER + + rump_server_destroy_ifaces +} + +vether_bridged_cleanup() +{ + + $DEBUG && dump + cleanup +} + +atf_init_test_cases() +{ + + atf_add_test_case vether_create_destroy + atf_add_test_case vether_stand_alone + atf_add_test_case vether_bridged +} diff --git a/net/if_vlan/Makefile b/net/if_vlan/Makefile new file mode 100644 --- /dev/null +++ b/net/if_vlan/Makefile @@ -0,0 +1,23 @@ +# $NetBSD: Makefile,v 1.4 2021/08/19 03:27:05 yamaguchi Exp $ +# + +.include + +TESTSDIR= ${TESTSBASE}/net/if_vlan + +.for name in vlan +TESTS_SH+= t_${name} +TESTS_SH_SRC_t_${name}= ../net_common.sh t_${name}.sh +.endfor + +PROGS= siocXmulti +MAN.siocXmulti= #empty +BINDIR.siocXmulti= ${TESTSDIR} + +PROGS+= bpfopen +MAN.bpfopen= #empty +BINDIR.bpfopen= ${TESTSDIR} +DPADD.bpfopen+= ${LIBUTIL} +LDADD.bpfopen+= -lutil + +.include diff --git a/net/if_vlan/bpfopen.c b/net/if_vlan/bpfopen.c new file mode 100644 --- /dev/null +++ b/net/if_vlan/bpfopen.c @@ -0,0 +1,259 @@ +/* $NetBSD: bpfopen.c,v 1.2 2021/08/19 03:27:05 yamaguchi Exp $ */ + +/* + * Copyright (c) 2021 Internet Initiative Japan Inc. + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +__RCSID("$NetBSD: bpfopen.c,v 1.2 2021/08/19 03:27:05 yamaguchi Exp $"); + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +enum { + ARG_PROG = 0, + ARG_HOST, + ARG_IFNAME, + ARG_NUM +}; + +enum { + PFD_BPF = 0, + PFD_NUM +}; + +static void sighandler(int); +static void log_debug(const char *, ...) __printflike(1, 2); +static int bpf_open(void); +static void bpf_close(int); +static void bpf_read(int); + +static sig_atomic_t quit; +static bool daemonize = false; +static int verbose = 0; +static const char *path_pid = "/var/run/bpfopen.pid"; +static const char *path_bpf = "/dev/bpf"; +static const char *ifname = NULL; + +static void +usage(void) +{ + + fprintf(stderr, "%s [-vd] [-p pidfile] [-b devbpf ] \n" + "\t-v: verbose\n" + "\t-d: daemon mode\n", + getprogname()); + exit(1); +} + +int +main(int argc, char *argv[]) +{ + int bpfd; + int ch; + + while ((ch = getopt(argc, argv, "b:dp:v")) != -1) { + switch (ch) { + case 'b': + path_bpf = optarg; + break; + case 'd': + daemonize = true; + break; + case 'p': + path_pid = optarg; + break; + case 'v': + verbose++; + break; + default: + usage(); + } + } + + argc -= optind; + argv += optind; + + if (argc != 1) + usage(); + + ifname = argv[0]; + + bpfd = bpf_open(); + if (bpfd < 0) + err(1, "bpf_open"); + log_debug("bpf opened"); + + if (daemonize) { + if (daemon(1, 1) != 0) { + bpf_close(bpfd); + err(1, "daemon"); + } + log_debug("daemonized"); + + if (pidfile(path_pid) != 0) { + bpf_close(bpfd); + err(1, "pidfile"); + } + } + + bpf_read(bpfd); + bpf_close(bpfd); + if (daemonize) + pidfile_clean(); + + return 0; +} + +static void +sighandler(int signo) +{ + + quit = 1; +} + +static void +log_debug(const char *fmt, ...) +{ + va_list ap; + + if (verbose <= 0) + return; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + + fprintf(stderr, "\n"); +} + +static int +bpf_open(void) +{ + struct ifreq ifr; + int bpfd; + + bpfd = open(path_bpf, O_RDONLY); + if (bpfd < 0) { + log_debug("open: %s", strerror(errno)); + return -1; + } + + memset(&ifr, 0, sizeof(ifr)); + strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); + + if (ioctl(bpfd, BIOCSETIF, &ifr) != 0) { + log_debug("BIOCSETIF: %s", strerror(errno)); + goto close_bpfd; + } + + if (ioctl(bpfd, BIOCPROMISC, NULL) != 0) { + log_debug("BIOCPROMISC: %s", strerror(errno)); + goto close_bpfd; + } + + return bpfd; + +close_bpfd: + close(bpfd); + + return -1; +} + +static void +bpf_close(int bpfd) +{ + + close(bpfd); +} + +static void +bpf_read(int bpfd) +{ + struct pollfd pfd[PFD_NUM]; + int nfds; + char *buf; + u_int bufsiz; + ssize_t n; + + if (ioctl(bpfd, BIOCGBLEN, &bufsiz) != 0) { + bufsiz = BPF_DFLTBUFSIZE; + log_debug("BIOCGBLEN: %s, use default size %u", + strerror(errno), bufsiz); + } + + bufsiz = MIN(bufsiz, BPF_DFLTBUFSIZE * 4); + + buf = malloc(bufsiz); + if (buf == NULL) { + log_debug("malloc: %s", strerror(errno)); + return; + } + + quit = 0; + signal(SIGTERM, sighandler); + signal(SIGQUIT, sighandler); + signal(SIGINT, sighandler); + signal(SIGHUP, sighandler); + + log_debug("start reading %s, bufsiz=%u", ifname, bufsiz); + + while (quit == 0) { + pfd[PFD_BPF].fd = bpfd; + pfd[PFD_BPF].events = POLLIN; + + nfds = poll(pfd, PFD_NUM, 1 * 1000); + if (nfds == -1 && errno != EINTR) { + warn("poll"); + quit = 1; + } + + if (nfds > 0 && (pfd[PFD_BPF].revents & POLLIN)) { + /* read & drop */ + memset(buf, 0, bufsiz); + n = read(pfd[PFD_BPF].fd, buf, bufsiz); + if (n < 0) + quit = 1; + } + } + + log_debug("finish reading %s", ifname); + free(buf); +} diff --git a/net/if_vlan/siocXmulti.c b/net/if_vlan/siocXmulti.c new file mode 100644 --- /dev/null +++ b/net/if_vlan/siocXmulti.c @@ -0,0 +1,119 @@ +/* $NetBSD: siocXmulti.c,v 1.3 2021/08/19 03:36:42 yamaguchi Exp $ */ + +/* + * Copyright (c) 2021 Internet Initiative Japan Inc. + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +__RCSID("$NetBSD: siocXmulti.c,v 1.3 2021/08/19 03:36:42 yamaguchi Exp $"); + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +enum{ + ARG_PROG = 0, + ARG_OP, + ARG_IFNAME, + ARG_ADDR, + ARG_NUM +}; + +static void +usage(void) +{ + + printf("%s \n", + getprogname()); + exit(1); +} + +int +main(int argc, char *argv[]) +{ + int fd, rv; + unsigned long req; + struct ifreq ifr; + unsigned int ifidx; + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + + bzero(&ifr, sizeof(ifr)); + + if (argc != ARG_NUM) + usage(); + + if (strcmp(argv[ARG_OP], "add") == 0) + req = SIOCADDMULTI; + else if (strcmp(argv[ARG_OP], "del") == 0) + req = SIOCDELMULTI; + else + usage(); + + ifidx = if_nametoindex(argv[ARG_IFNAME]); + if (ifidx == 0) + err(1, "if_nametoindex(%s)", argv[ARG_IFNAME]); + + strncpy(ifr.ifr_name, argv[ARG_IFNAME], sizeof(ifr.ifr_name) - 1); + ifr.ifr_name[sizeof(ifr.ifr_name) - 1] = '\0'; + + sin = (struct sockaddr_in *)&ifr.ifr_addr; + sin->sin_family = AF_INET; + sin->sin_len = sizeof(*sin); + rv = inet_pton(AF_INET, argv[ARG_ADDR], &sin->sin_addr); + + if (rv != 1) { + sin6 = (struct sockaddr_in6 *)&ifr.ifr_addr; + sin6->sin6_family = AF_INET6; + sin6->sin6_len = sizeof(*sin6); + rv = inet_pton(AF_INET6, argv[ARG_ADDR], &sin6->sin6_addr); + + if (rv != 1) + errx(1, "inet_pton(%s)", argv[ARG_ADDR]); + } + + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) + err(1, "socket"); + + if (ioctl(fd, req, (caddr_t)&ifr) < 0) { + err(1, "ioctl(%s)", + (req == SIOCADDMULTI) ? "SIOCADDMULTI" : "SIOCDELMULTI"); + } + + close(fd); + + return 0; +} diff --git a/net/if_vlan/t_vlan.sh b/net/if_vlan/t_vlan.sh old mode 100755 new mode 100644 --- a/net/if_vlan/t_vlan.sh +++ b/net/if_vlan/t_vlan.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_vlan.sh,v 1.1 2016/11/26 03:19:49 ozaki-r Exp $ +# $NetBSD: t_vlan.sh,v 1.24 2021/08/19 03:27:05 yamaguchi Exp $ # # Copyright (c) 2016 Internet Initiative Japan Inc. # All rights reserved. @@ -28,11 +28,65 @@ BUS=bus SOCK_LOCAL=unix://commsock1 SOCK_REMOTE=unix://commsock2 -IP_LOCAL=10.0.0.1 -IP_REMOTE=10.0.0.2 +IP_LOCAL0=10.0.0.1 +IP_LOCAL1=10.0.1.1 +IP_REMOTE0=10.0.0.2 +IP_REMOTE1=10.0.1.2 +IP_MCADDR0=224.0.0.10 +IP6_LOCAL0=fc00::1 +IP6_LOCAL1=fc00:1::1 +IP6_REMOTE0=fc00::2 +IP6_REMOTE1=fc00:1::2 +IP6_MCADDR0=ff11::10 +ETH_IP_MCADDR0=01:00:5e:00:00:0a +ETH_IP6_MCADDR0=33:33:00:00:00:10 DEBUG=${DEBUG:-false} +vlan_create_destroy_body_common() +{ + local atf_ifconfig="atf_check -s exit:0 rump.ifconfig" + + export RUMP_SERVER=${SOCK_LOCAL} + + $atf_ifconfig vlan0 create + $atf_ifconfig vlan0 destroy + + $atf_ifconfig vlan0 create + $atf_ifconfig vlan0 up + $atf_ifconfig vlan0 down + $atf_ifconfig vlan0 destroy + + $atf_ifconfig shmif0 create + $atf_ifconfig vlan0 create + $atf_ifconfig vlan0 vlan 1 vlanif shmif0 + $atf_ifconfig vlan0 up + $atf_ifconfig vlan0 destroy + + # more than one vlan interface with a same parent interface + $atf_ifconfig shmif1 create + $atf_ifconfig vlan0 create + $atf_ifconfig vlan0 vlan 10 vlanif shmif0 + $atf_ifconfig vlan1 create + $atf_ifconfig vlan1 vlan 11 vlanif shmif0 + + # more than one interface with another parent interface + $atf_ifconfig vlan2 create + $atf_ifconfig vlan2 vlan 12 vlanif shmif1 + $atf_ifconfig vlan3 create + $atf_ifconfig vlan3 vlan 13 vlanif shmif1 + $atf_ifconfig shmif0 destroy + atf_check -s exit:0 -o not-match:'shmif0' rump.ifconfig vlan0 + atf_check -s exit:0 -o not-match:'shmif0' rump.ifconfig vlan1 + atf_check -s exit:0 -o match:'shmif1' rump.ifconfig vlan2 + atf_check -s exit:0 -o match:'shmif1' rump.ifconfig vlan3 + $atf_ifconfig vlan0 destroy + $atf_ifconfig vlan1 destroy + $atf_ifconfig vlan2 destroy + $atf_ifconfig vlan3 destroy + +} + atf_test_case vlan_create_destroy cleanup vlan_create_destroy_head() { @@ -43,17 +97,12 @@ vlan_create_destroy_body() { - rump_server_start $SOCK_LOCAL vlan - export RUMP_SERVER=${SOCK_LOCAL} - - atf_check -s exit:0 rump.ifconfig vlan0 create - atf_check -s exit:0 rump.ifconfig vlan0 up - atf_check -s exit:0 rump.ifconfig vlan0 down - atf_check -s exit:0 rump.ifconfig vlan0 destroy + vlan_create_destroy_body_common } + vlan_create_destroy_cleanup() { @@ -61,6 +110,104 @@ cleanup } +atf_test_case vlan_create_destroy6 cleanup +vlan_create_destroy6_head() +{ + + atf_set "descr" "tests of creation and deletion of vlan interface with IPv6" + atf_set "require.progs" "rump_server" +} + +vlan_create_destroy6_body() +{ + + rump_server_start $SOCK_LOCAL vlan netinet6 + + vlan_create_destroy_body_common +} + +vlan_create_destroy6_cleanup() +{ + + $DEBUG && dump + cleanup +} + +vlan_basic_body_common() +{ + local atf_ifconfig="atf_check -s exit:0 rump.ifconfig" + local outfile=./out + local af=inet + local prefix=24 + local local0=$IP_LOCAL0 + local remote0=$IP_REMOTE0 + local ping_cmd="rump.ping -n -w 1 -c 1" + + if [ x"$1" = x"inet6" ]; then + af="inet6" + prefix=64 + local0=$IP6_LOCAL0 + remote0=$IP6_REMOTE0 + ping_cmd="rump.ping6 -n -c 1" + fi + + rump_server_add_iface $SOCK_LOCAL shmif0 $BUS + rump_server_add_iface $SOCK_REMOTE shmif0 $BUS + + export RUMP_SERVER=$SOCK_LOCAL + $atf_ifconfig shmif0 up + export RUMP_SERVER=$SOCK_REMOTE + $atf_ifconfig shmif0 up + + export RUMP_SERVER=$SOCK_LOCAL + $atf_ifconfig vlan0 create + $atf_ifconfig vlan0 vlan 10 vlanif shmif0 + $atf_ifconfig vlan0 $af $local0/$prefix + $atf_ifconfig vlan0 up + $atf_ifconfig -w 10 + + export RUMP_SERVER=$SOCK_REMOTE + $atf_ifconfig vlan0 create + $atf_ifconfig vlan0 vlan 10 vlanif shmif0 + $atf_ifconfig vlan0 $af $remote0/$prefix + $atf_ifconfig vlan0 up + $atf_ifconfig -w 10 + + extract_new_packets $BUS > $outfile + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 -o ignore $ping_cmd $remote0 + + extract_new_packets $BUS > $outfile + atf_check -s exit:0 -o match:'vlan 10' cat $outfile + + $atf_ifconfig vlan0 -vlanif + $atf_ifconfig vlan0 vlan 20 vlanif shmif0 + $atf_ifconfig vlan0 $af $local0/$prefix + $atf_ifconfig vlan0 up + $atf_ifconfig -w 10 + + extract_new_packets $BUS > $outfile + atf_check -s not-exit:0 -o ignore $ping_cmd $remote0 + + extract_new_packets $BUS > $outfile + atf_check -s exit:0 -o match:'vlan 20' cat $outfile + + export RUMP_SERVER=$SOCK_LOCAL + $atf_ifconfig vlan0 -vlanif + $atf_ifconfig vlan0 vlan 10 vlanif shmif0 + $atf_ifconfig vlan0 $af $local0/$prefix + $atf_ifconfig vlan0 up + $atf_ifconfig -w 10 + + atf_check -s exit:0 -o ignore rump.ifconfig -z vlan0 + atf_check -s exit:0 -o ignore $ping_cmd $remote0 + rump.ifconfig -v vlan0 > $outfile + + atf_check -s exit:0 -o not-match:' 0 packets' cat $outfile + atf_check -s exit:0 -o not-match:' 0 bytes' cat $outfile +} + atf_test_case vlan_basic cleanup vlan_basic_head() { @@ -71,36 +218,817 @@ vlan_basic_body() { - rump_server_start $SOCK_LOCAL vlan + rump_server_start $SOCK_REMOTE vlan + + vlan_basic_body_common inet + +} + +vlan_basic_cleanup() +{ + + $DEBUG && dump + cleanup +} + +atf_test_case vlan_basic6 cleanup +vlan_basic6_head() +{ + + atf_set "descr" "tests of communications over vlan interfaces using IPv6" + atf_set "require.progs" "rump_server" +} + +vlan_basic6_body() +{ + rump_server_start $SOCK_LOCAL vlan netinet6 + rump_server_start $SOCK_REMOTE vlan netinet6 + + vlan_basic_body_common inet6 +} + +vlan_basic6_cleanup() +{ + + $DEBUG && dump + cleanup +} + +vlan_auto_follow_mtu_body_common() +{ + local atf_ifconfig="atf_check -s exit:0 rump.ifconfig" + local outfile=./out + local af=inet + local prefix=24 + local local0=$IP_LOCAL0 + local remote0=$IP_REMOTE0 + local ping_cmd="rump.ping -D -n -w 1 -c 1" + local mtu=1500 + local vlan_mtu=`expr $mtu - 4` + # ipv4 header=20bytes, icmp header=8bytes + local padding=`expr $vlan_mtu - 20 - 8` + local over_padding=`expr $vlan_mtu - 20 - 8 + 1` + local nonfrag_msg="$local0 > $remote0: ICMP echo request" + # unused for ipv4 + local frag_msg="" + + if [ x"$1" = x"inet6" ]; then + af="inet6" + prefix=64 + local0=$IP6_LOCAL0 + remote0=$IP6_REMOTE0 + # ipv6 header=40bytes, icmpv6 header=8bytes + padding=`expr $vlan_mtu - 40 - 8` + over_padding=`expr $vlan_mtu - 40 - 8 + 1` + ping_cmd="rump.ping6 -mm -n -c 1 -i 1" + nonfrag_msg="$local0 > $remote0: ICMP6, echo request" + frag_msg="$local0 > $remote0: frag .* ICMP6, echo request" + fi + rump_server_add_iface $SOCK_LOCAL shmif0 $BUS + rump_server_add_iface $SOCK_REMOTE shmif0 $BUS + + export RUMP_SERVER=$SOCK_LOCAL + $atf_ifconfig shmif0 up + export RUMP_SERVER=$SOCK_REMOTE + $atf_ifconfig shmif0 up + + export RUMP_SERVER=$SOCK_LOCAL + $atf_ifconfig vlan0 create + + # since upper bound of shmif's mtu is 1500, + # so we lower vlan's mtu instead of raising shmif's. + # to do this, we change the interface's parameter + # such as ND_IFINFO(ifp)->maxmtu that is changed by SIOCSIFMTU. + + # $atf_config shmif0 mtu 1600 + $atf_ifconfig vlan0 vlan 10 vlanif shmif0 + $atf_ifconfig vlan0 mtu 1400 + $atf_ifconfig vlan0 -vlanif shmif0 + + $atf_ifconfig vlan0 vlan 10 vlanif shmif0 + $atf_ifconfig vlan0 $af $local0/$prefix + $atf_ifconfig vlan0 up + $atf_ifconfig -w 10 + + export RUMP_SERVER=$SOCK_REMOTE + $atf_ifconfig vlan0 create + $atf_ifconfig vlan0 vlan 10 vlanif shmif0 + $atf_ifconfig vlan0 $af $remote0/$prefix + $atf_ifconfig vlan0 up + $atf_ifconfig -w 10 + + extract_new_packets $BUS > $outfile + export RUMP_SERVER=$SOCK_LOCAL + + atf_check -s exit:0 -o ignore $ping_cmd -s $padding $remote0 + extract_new_packets $BUS > $outfile + atf_check -s exit:0 -o match:"$nonfrag_msg" cat $outfile + + if [ x"$1" = x"inet6" ]; then + atf_check -s exit:0 -o ignore $ping_cmd -s $over_padding $remote0 + extract_new_packets $BUS > $outfile + atf_check -s exit:0 -o match:"$frag_msg" cat $outfile + else + atf_check -s not-exit:0 -o ignore -e match:"Message too long" \ + $ping_cmd -s $over_padding $remote0 + fi +} + +atf_test_case vlan_auto_follow_mtu cleanup +vlan_auto_follow_mtu_head() +{ + + atf_set "descr" "tests of setting vlan mtu using IPv4" + atf_set "require.progs" "rump_server" +} + +vlan_auto_follow_mtu_body() +{ + rump_server_start $SOCK_LOCAL vlan rump_server_start $SOCK_REMOTE vlan + + vlan_auto_follow_mtu_body_common inet +} + +vlan_auto_follow_mtu_cleanup() +{ + + $DEBUG && dump + cleanup +} + +atf_test_case vlan_auto_follow_mtu6 cleanup +vlan_auto_follow_mtu6_head() +{ + + atf_set "descr" "tests of setting vlan mtu using IPv6" + atf_set "require.progs" "rump_server" +} + +vlan_auto_follow_mtu6_body() +{ + rump_server_start $SOCK_LOCAL vlan netinet6 + rump_server_start $SOCK_REMOTE vlan netinet6 + + vlan_auto_follow_mtu_body_common inet6 +} + +vlan_auto_follow_mtu6_cleanup() +{ + + $DEBUG && dump + cleanup +} + +vlanid_config_and_ping() +{ + local atf_ifconfig="atf_check -s exit:0 rump.ifconfig" + local vlanid=$1 + shift + + export RUMP_SERVER=$SOCK_LOCAL + $atf_ifconfig vlan0 vlan $vlanid vlanif shmif0 + $atf_ifconfig vlan0 $IP_LOCAL0/24 + $atf_ifconfig vlan0 up + + export RUMP_SERVER=$SOCK_REMOTE + $atf_ifconfig vlan0 vlan $vlanid vlanif shmif0 + $atf_ifconfig vlan0 $IP_REMOTE0/24 + $atf_ifconfig vlan0 up + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 -o ignore rump.ping -n -w 1 -c 1 $IP_REMOTE0 + $atf_ifconfig vlan0 -vlanif + + export RUMP_SERVER=$SOCK_REMOTE + $atf_ifconfig vlan0 -vlanif +} + +vlanid_config_and_ping6() +{ + local atf_ifconfig="atf_check -s exit:0 rump.ifconfig" + local vlanid=$1 + shift + + export RUMP_SERVER=$SOCK_LOCAL + $atf_ifconfig vlan0 vlan $vlanid vlanif shmif0 + $atf_ifconfig vlan0 inet6 $IP6_LOCAL0/64 + $atf_ifconfig vlan0 up + + export RUMP_SERVER=$SOCK_REMOTE + $atf_ifconfig vlan0 vlan $vlanid vlanif shmif0 + $atf_ifconfig vlan0 inet6 $IP6_REMOTE0/64 + $atf_ifconfig vlan0 up + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 -o ignore rump.ping6 -n -c 1 $IP6_REMOTE0 + $atf_ifconfig vlan0 -vlanif + + export RUMP_SERVER=$SOCK_REMOTE + $atf_ifconfig vlan0 -vlanif +} + +vlan_vlanid_body_common() +{ + local atf_ifconfig="atf_check -s exit:0 rump.ifconfig" + local af=inet + local prefix=24 + local sysctl_param="net.inet.ip.dad_count=0" + local ping_cmd="rump.ping -n -w 1 -c 1" + local config_and_ping=vlanid_config_and_ping + local local0=$IP_LOCAL0 + local local1=$IP_LOCAL1 + local remote0=$IP_REMOTE0 + local remote1=$IP_REMOTE1 + + if [ x"$1" = x"inet6" ]; then + af=inet6 + prefix=64 + sysctl_param="net.inet6.ip6.dad_count=0" + ping_cmd="rump.ping6 -n -c 1" + config_and_ping=vlanid_config_and_ping6 + local0=$IP6_LOCAL0 + local1=$IP6_LOCAL1 + remote0=$IP6_REMOTE0 + remote1=$IP6_REMOTE1 + fi + + rump_server_add_iface $SOCK_LOCAL shmif0 $BUS rump_server_add_iface $SOCK_REMOTE shmif0 $BUS export RUMP_SERVER=$SOCK_LOCAL - atf_check -s exit:0 rump.ifconfig shmif0 up + atf_check -s exit:0 -o ignore rump.sysctl -w $sysctl_param + $atf_ifconfig shmif0 up + $atf_ifconfig vlan0 create + export RUMP_SERVER=$SOCK_REMOTE - atf_check -s exit:0 rump.ifconfig shmif0 up + atf_check -s exit:0 -o ignore rump.sysctl -w $sysctl_param + $atf_ifconfig shmif0 up + $atf_ifconfig vlan0 create export RUMP_SERVER=$SOCK_LOCAL - atf_check -s exit:0 rump.ifconfig vlan0 create - atf_check -s exit:0 rump.ifconfig vlan0 vlan 10 vlanif shmif0 - atf_check -s exit:0 rump.ifconfig vlan0 $IP_LOCAL/24 - atf_check -s exit:0 rump.ifconfig vlan0 up - atf_check -s exit:0 rump.ifconfig -w 10 + atf_check -s not-exit:0 -e match:"^usage: rump.ifconfig" \ + rump.ifconfig vlan0 vlan -1 vlanif shmif0 + + # $config_and_ping 0 # reserved vlan id + $config_and_ping 1 + $config_and_ping 4094 + # $config_and_ping 4095 #reserved vlan id + + if [ "${RANDOM:-0}" != "${RANDOM:-0}" ] + then + for TAG in $(( ${RANDOM:-0} % 4092 + 2 )) \ + $(( ${RANDOM:-0} % 4092 + 2 )) \ + $(( ${RANDOM:-0} % 4092 + 2 )) + do + $config_and_ping "${TAG}" + done + fi + + export RUMP_SERVER=$SOCK_LOCAL + for TAG in 0 4095 4096 $((4096*4 + 1)) 65536 65537 $((65536 + 4095)) + do + atf_check -s not-exit:0 -e not-empty \ + rump.ifconfig vlan0 vlan "${TAG}" vlanif shmif0 + done + + $atf_ifconfig vlan0 vlan 1 vlanif shmif0 + atf_check -s not-exit:0 -e match:"SIOCSETVLAN: Device busy" \ + rump.ifconfig vlan0 vlan 2 vlanif shmif0 + + atf_check -s not-exit:0 -e match:"SIOCSETVLAN: Device busy" \ + rump.ifconfig vlan0 vlan 1 vlanif shmif1 + + $atf_ifconfig vlan0 -vlanif + atf_check -s not-exit:0 -e match:"Invalid argument" \ + rump.ifconfig vlan0 $af $local0/$prefix + + export RUMP_SERVER=$SOCK_LOCAL + $atf_ifconfig vlan0 vlan 10 vlanif shmif0 + $atf_ifconfig vlan0 $af $local0/$prefix + $atf_ifconfig vlan0 up + $atf_ifconfig vlan1 create + $atf_ifconfig vlan1 vlan 11 vlanif shmif0 + $atf_ifconfig vlan1 $af $local1/$prefix + $atf_ifconfig vlan1 up export RUMP_SERVER=$SOCK_REMOTE - atf_check -s exit:0 rump.ifconfig vlan0 create - atf_check -s exit:0 rump.ifconfig vlan0 vlan 10 vlanif shmif0 - atf_check -s exit:0 rump.ifconfig vlan0 $IP_REMOTE/24 - atf_check -s exit:0 rump.ifconfig vlan0 up - atf_check -s exit:0 rump.ifconfig -w 10 + $atf_ifconfig vlan0 -vlanif + $atf_ifconfig vlan0 vlan 10 vlanif shmif0 + $atf_ifconfig vlan0 $af $remote0/$prefix + $atf_ifconfig vlan0 up + $atf_ifconfig vlan1 create + $atf_ifconfig vlan1 vlan 11 vlanif shmif0 + $atf_ifconfig vlan1 $af $remote1/$prefix + $atf_ifconfig vlan1 up export RUMP_SERVER=$SOCK_LOCAL - atf_check -s exit:0 -o ignore rump.ping -n -w 1 -c 1 $IP_REMOTE + atf_check -s exit:0 -o ignore $ping_cmd $remote0 + atf_check -s exit:0 -o ignore $ping_cmd $remote1 } -vlan_basic_cleanup() +atf_test_case vlan_vlanid cleanup +vlan_vlanid_head() +{ + + atf_set "descr" "tests of configuration for vlan id" + atf_set "require.progs" "rump_server" +} + +vlan_vlanid_body() +{ + rump_server_start $SOCK_LOCAL vlan + rump_server_start $SOCK_REMOTE vlan + + vlan_vlanid_body_common inet +} + +vlan_vlanid_cleanup() +{ + + $DEBUG && dump + cleanup +} + +atf_test_case vlan_vlanid6 cleanup +vlan_vlanid6_head() +{ + + atf_set "descr" "tests of configuration for vlan id using IPv6" + atf_set "require.progs" "rump_server" +} + + +vlan_vlanid6_body() +{ + rump_server_start $SOCK_LOCAL vlan netinet6 + rump_server_start $SOCK_REMOTE vlan netinet6 + + vlan_vlanid_body_common inet6 +} + +vlan_vlanid6_cleanup() +{ + + $DEBUG && dump + cleanup +} + +vlan_configs_body_common() +{ + local atf_ifconfig="atf_check -s exit:0 rump.ifconfig" + + export RUMP_SERVER=${SOCK_LOCAL} + + $atf_ifconfig shmif0 create + $atf_ifconfig shmif1 create + # unset U/L bit to detect a bug fixed by if_vlan.c:r1.132 + $atf_ifconfig shmif0 link b0:a0:75:00:01:00 active + $atf_ifconfig shmif1 link b0:a0:75:00:01:01 active + $atf_ifconfig vlan0 create + + atf_check -s exit:0 -o match:'status: +down' \ + rump.ifconfig vlan0 + $atf_ifconfig vlan0 vlan 10 vlanif shmif0 + $atf_ifconfig vlan0 -vlanif + atf_check -s exit:0 -o match:'status: +down' \ + rump.ifconfig vlan0 + + $atf_ifconfig vlan0 vlan 10 vlanif shmif0 + $atf_ifconfig vlan0 -vlanif shmif0 + + $atf_ifconfig vlan0 vlan 10 vlanif shmif0 + atf_check -s exit:0 rump.ifconfig vlan0 -vlanif shmif1 + atf_check -s exit:0 rump.ifconfig vlan0 -vlanif shmif2 + + $atf_ifconfig vlan0 -vlanif + + $atf_ifconfig vlan0 vlan 10 vlanif shmif0 + atf_check -s exit:0 -e match:'Invalid argument' \ + rump.ifconfig vlan0 mtu 1497 + $atf_ifconfig vlan0 mtu 1496 + $atf_ifconfig vlan0 mtu 42 + atf_check -s exit:0 -e match:'Invalid argument' \ + rump.ifconfig vlan0 mtu 41 + $atf_ifconfig vlan0 -vlanif + + $atf_ifconfig vlan1 create + $atf_ifconfig vlan0 vlan 10 vlanif shmif0 + atf_check -s not-exit:0 -e match:'File exists' \ + rump.ifconfig vlan1 vlan 10 vlanif shmif0 + $atf_ifconfig vlan1 vlan 10 vlanif shmif1 + + $atf_ifconfig vlan1 -vlanif shmif1 + $atf_ifconfig vlan1 vlan 10 vlanif shmif1 + + $atf_ifconfig vlan0 -vlanif shmif0 + $atf_ifconfig vlan0 vlan 10 vlanif shmif0 +} + +atf_test_case vlan_configs cleanup +vlan_configs_head() +{ + atf_set "descr" "tests of configuration except vlan id" + atf_set "require.progs" "rump_server" +} + +vlan_configs_body() +{ + + rump_server_start $SOCK_LOCAL vlan + + vlan_configs_body_common + +} + +vlan_configs_cleanup() +{ + + $DEBUG && dump + cleanup +} + +atf_test_case vlan_configs6 cleanup +vlan_configs6_head() +{ + atf_set "descr" "tests of configuration except vlan id using IPv6" + atf_set "require.progs" "rump_server" +} + +vlan_configs6_body() +{ + rump_server_start $SOCK_LOCAL vlan netinet6 + + vlan_configs_body_common +} + +vlan_configs6_cleanup() +{ + $DEBUG && dump + cleanup +} + +vlan_bridge_body_common() +{ + local atf_ifconfig="atf_check -s exit:0 rump.ifconfig" + local atf_brconfig="atf_check -s exit:0 $HIJACKING /sbin/brconfig" + + rump_server_add_iface $SOCK_LOCAL shmif0 $BUS + + export RUMP_SERVER=$SOCK_LOCAL + $atf_ifconfig shmif0 up + + $atf_ifconfig vlan0 create + $DEBUG && rump.ifconfig vlan0 + + $atf_ifconfig bridge0 create + $atf_ifconfig bridge0 up + + # + # Add vlan to bridge member + # + $atf_ifconfig bridge0 mtu 1496 + + # vlan0 can not add to bridge member + # because it is not an ethernet device + atf_check -s not-exit:0 -e match:'Invalid argument' \ + $HIJACKING /sbin/brconfig bridge0 add vlan0 + + $atf_ifconfig vlan0 vlan 10 vlanif shmif0 + $atf_ifconfig vlan0 up + atf_check -s exit:0 -o match:'mtu 1496' rump.ifconfig vlan0 + + # vlan0 becomes an ethernet device + # after attaching the parent interface + $atf_brconfig bridge0 add vlan0 + $DEBUG && $HIJACKING /sbin/brconfig bridge0 + + $atf_brconfig bridge0 delete vlan0 + + $atf_brconfig bridge0 add vlan0 + $atf_ifconfig vlan0 -vlanif + atf_check -s exit:0 -o not-match:'vlan0' \ + $HIJACKING /sbin/brconfig bridge0 + atf_check -s not-exit:0 -e match:'No such' \ + $HIJACKING /sbin/brconfig bridge0 delete vlan0 + + # + # decrease MTU on adding to bridge member + # + $atf_ifconfig bridge0 mtu 1495 + $atf_ifconfig vlan0 vlan 10 vlanif shmif0 + $atf_ifconfig vlan0 up + atf_check -s exit:0 -o match:'mtu 1496' rump.ifconfig vlan0 + + $atf_brconfig bridge0 add vlan0 + $DEBUG && $HIJACKING /sbin/brconfig bridge0 + atf_check -s exit:0 -o match:'mtu 1495' rump.ifconfig vlan0 + $atf_brconfig bridge0 delete vlan0 + + # + # increase MTU on adding to bridge member + # + $atf_ifconfig bridge0 mtu 1496 + $atf_ifconfig vlan0 mtu 1495 + $atf_brconfig bridge0 add vlan0 + + $DEBUG && $HIJACKING /sbin/brconfig bridge0 + atf_check -s exit:0 -o match:'mtu 1496' rump.ifconfig vlan0 + $atf_brconfig bridge0 delete vlan0 + + $atf_ifconfig bridge0 mtu 1497 + atf_check -s not-exit:0 -o ignore -e ignore \ + $HIJACKING /sbin/brconfig bridge0 add vlan0 + + # + # Destroy a vlan interface that is bridge member + # + $atf_ifconfig bridge0 mtu 1496 + $atf_brconfig bridge0 add vlan0 + $atf_ifconfig vlan0 destroy + + rump_server_destroy_ifaces +} + +atf_test_case vlan_bridge cleanup +vlan_bridge_head() +{ + + atf_set "descr" "tests of vlan interfaces with bridges (IPv4)" + atf_set "require.progs" "rump_server" +} + +vlan_bridge_body() +{ + + rump_server_start $SOCK_LOCAL vlan bridge + vlan_bridge_body_common +} + +vlan_bridge_cleanup() +{ + + $DEBUG && dump + cleanup +} + +atf_test_case vlan_bridge6 cleanup +vlan_bridge6_head() +{ + + atf_set "descr" "tests of vlan interfaces with bridges (IPv6)" + atf_set "require.progs" "rump_server" +} + +vlan_bridge6_body() +{ + + rump_server_start $SOCK_LOCAL vlan netinet6 bridge + vlan_bridge_body_common +} + +vlan_bridge6_cleanup() +{ + + $DEBUG && dump + cleanup +} + +vlan_multicast_body_common() +{ + local atf_ifconfig="atf_check -s exit:0 rump.ifconfig" + local af="inet" + local local0=$IP_LOCAL0 + local local1=$IP_LOCAL1 + local mcaddr=$IP_MCADDR0 + local eth_mcaddr=$ETH_IP_MCADDR0 + local prefix=24 + local siocXmulti="$(atf_get_srcdir)/siocXmulti" + local atf_siocXmulti="atf_check -s exit:0 $HIJACKING $siocXmulti" + + if [ x"$1" = x"inet6" ]; then + af="inet6" + prefix=64 + local0=$IP6_LOCAL0 + local1=$IP6_LOCAL1 + mcaddr=$IP6_MCADDR0 + eth_mcaddr=$ETH_IP6_MCADDR0 + fi + + export RUMP_SERVER=$SOCK_LOCAL + + $atf_ifconfig shmif0 create + $atf_ifconfig shmif0 linkstr net0 up + $atf_ifconfig vlan0 create + $atf_ifconfig vlan0 vlan 10 vlanif shmif0 + $atf_ifconfig vlan0 $af $local0/$prefix up + $atf_ifconfig vlan1 create + $atf_ifconfig vlan1 vlan 11 vlanif shmif0 + $atf_ifconfig vlan1 $af $local1/$prefix up + $atf_ifconfig -w 10 + + # check the initial state + atf_check -s exit:0 -o not-match:"$eth_mcaddr" $HIJACKING ifmcstat + + # add a multicast address + $atf_siocXmulti add vlan0 $mcaddr + atf_check -s exit:0 -o match:"$eth_mcaddr" $HIJACKING ifmcstat + + # delete the address + $atf_siocXmulti del vlan0 $mcaddr + atf_check -s exit:0 -o not-match:"$eth_mcaddr" $HIJACKING ifmcstat + + # delete a non-existing address + atf_check -s not-exit:0 -e match:"Invalid argument" \ + $HIJACKING $siocXmulti del vlan0 $mcaddr + + # add an address to different interfaces + $atf_siocXmulti add vlan0 $mcaddr + $atf_siocXmulti add vlan1 $mcaddr + atf_check -s exit:0 -o match:"${eth_mcaddr} refcount 2" $HIJACKING ifmcstat + $atf_siocXmulti del vlan0 $mcaddr + + # delete the address with invalid interface + atf_check -s not-exit:0 -e match:"Invalid argument" \ + $HIJACKING $siocXmulti del vlan0 $mcaddr + + $atf_siocXmulti del vlan1 $mcaddr + + # add and delete a same address more than once + $atf_siocXmulti add vlan0 $mcaddr + $atf_siocXmulti add vlan0 $mcaddr + $atf_siocXmulti add vlan0 $mcaddr + atf_check -s exit:0 -o match:"${eth_mcaddr} refcount 3" $HIJACKING ifmcstat + $atf_siocXmulti del vlan0 $mcaddr + $atf_siocXmulti del vlan0 $mcaddr + $atf_siocXmulti del vlan0 $mcaddr + atf_check -s exit:0 -o not-match:"$eth_mcaddr" $HIJACKING ifmcstat + + # delete all address added to parent device when remove + # the config of parent interface + $atf_siocXmulti add vlan0 $mcaddr + $atf_siocXmulti add vlan0 $mcaddr + $atf_siocXmulti add vlan0 $mcaddr + $atf_ifconfig vlan0 -vlanif shmif0 + atf_check -s exit:0 -o not-match:"$eth_mcaddr" $HIJACKING ifmcstat +} + +atf_test_case vlan_multicast cleanup +vlan_multicast_head() +{ + atf_set "descr" "tests of multicast address adding and deleting" + atf_set "require.progs" "rump_server" +} + +vlan_multicast_body() +{ + rump_server_start $SOCK_LOCAL vlan + + vlan_multicast_body_common inet +} + +vlan_multicast_cleanup() +{ + $DEBUG && dump + cleanup +} + +atf_test_case vlan_multicast6 cleanup +vlan_multicast6_head() +{ + atf_set "descr" "tests of multicast address adding and deleting with IPv6" + atf_set "require.progs" "rump_server" +} + +vlan_multicast6_body() +{ + rump_server_start $SOCK_LOCAL vlan netinet6 + + vlan_multicast_body_common inet6 +} + +vlan_multicast6_cleanup() +{ + $DEBUG && dump + cleanup +} + +atf_test_case vlan_promisc cleanup +vlan_promisc_head() +{ + + atf_set "descr" "tests of IFF_PROMISC of vlan" + atf_set "require.progs" "rump_server" +} + +vlan_promisc_body() +{ + local atf_ifconfig="atf_check -s exit:0 rump.ifconfig" + local atf_brconfig="atf_check -s exit:0 $HIJACKING /sbin/brconfig" + local atf_arp="atf_check -s exit:0 rump.arp" + local bpfopen="$HIJACKING $(atf_get_srcdir)/bpfopen" + bpfopen="$bpfopen -dv -b /rump/dev/bpf" + local pidfile="./bpfopen.pid" + local macaddr="" + + rump_server_bpf_start $SOCK_LOCAL vlan bridge + rump_server_start $SOCK_REMOTE vlan + + rump_server_add_iface $SOCK_LOCAL shmif0 $BUS + rump_server_add_iface $SOCK_LOCAL shmif1 + rump_server_add_iface $SOCK_LOCAL vlan0 + rump_server_add_iface $SOCK_LOCAL vlan1 + rump_server_add_iface $SOCK_LOCAL bridge0 + + rump_server_add_iface $SOCK_REMOTE shmif0 $BUS + rump_server_add_iface $SOCK_REMOTE vlan0 + + macaddr=$(get_macaddr $SOCK_LOCAL shmif1) + + export RUMP_SERVER=$SOCK_REMOTE + $atf_ifconfig vlan0 vlan 1 vlanif shmif0 + $atf_ifconfig shmif0 up + $atf_ifconfig vlan0 inet $IP_REMOTE0/24 + $atf_ifconfig vlan0 up + $atf_ifconfig -w 10 + $atf_arp -s $IP_LOCAL0 $macaddr + + export RUMP_SERVER=$SOCK_LOCAL + $atf_ifconfig bridge0 mtu 1496 + # + # When vlan IF is PROMISC, the parent is also PROMISC + # + $atf_ifconfig vlan0 vlan 1 vlanif shmif0 + $atf_ifconfig shmif0 up + $atf_ifconfig vlan0 up + + atf_check -s exit:0 -o not-match:'PROMISC' rump.ifconfig vlan0 + atf_check -s exit:0 -o not-match:'PROMISC' rump.ifconfig shmif0 + + $atf_brconfig bridge0 add vlan0 + $atf_ifconfig bridge0 up + atf_check -s exit:0 -o match:'PROMISC' rump.ifconfig vlan0 + atf_check -s exit:0 -o match:'PROMISC' rump.ifconfig shmif0 + + $atf_ifconfig bridge0 down + $atf_brconfig bridge0 delete vlan0 + atf_check -s exit:0 -o not-match:'PROMISC' rump.ifconfig vlan0 + atf_check -s exit:0 -o not-match:'PROMISC' rump.ifconfig shmif0 + $atf_ifconfig vlan0 -vlanif + + # + # drop unicast packets that is not for the host + # + $atf_ifconfig vlan0 vlan 1 vlanif shmif0 + $atf_ifconfig -w 10 + + atf_check -s exit:0 -e match:'bpf opened' $bpfopen -p $pidfile shmif0 + + atf_check -s exit:0 -o not-match:'PROMISC' rump.ifconfig vlan0 + atf_check -s exit:0 -o match:'PROMISC' rump.ifconfig shmif0 + atf_check -s exit:0 -o ignore rump.ifconfig -z vlan0 + atf_check -s exit:0 -o not-match:'input:.*errors' \ + rump.ifconfig -v vlan0 + + export RUMP_SERVER=$SOCK_REMOTE + atf_check -s not-exit:0 -o ignore -e ignore \ + rump.ping -c 3 -i 0.2 $IP_LOCAL0 + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 -o match:'input:.*errors' \ + rump.ifconfig -v vlan0 + + atf_check -s exit:0 kill -TERM $(cat $pidfile) + sleep 2 + + atf_check -s exit:0 -o not-match:'PROMISC' rump.ifconfig vlan0 + atf_check -s exit:0 -o not-match:'PROMISC' rump.ifconfig shmif0 + $atf_ifconfig vlan0 -vlanif + + # + # clear IFF_PROMISC after bpf_detach called from ether_ifdetach + # + $atf_ifconfig vlan0 vlan 1 vlanif shmif0 + $atf_ifconfig vlan0 up + + atf_check -s exit:0 -e match:'bpf opened' $bpfopen -p $pidfile vlan0 + + atf_check -s exit:0 -o match:'PROMISC' rump.ifconfig vlan0 + atf_check -s exit:0 -o match:'PROMISC' rump.ifconfig shmif0 + + $atf_ifconfig vlan0 -vlanif + + atf_check -s exit:0 -o not-match:'PROMISC' rump.ifconfig vlan0 + atf_check -s exit:0 -o not-match:'PROMISC' rump.ifconfig shmif0 + + atf_check -s exit:0 kill -TERM $(cat $pidfile) + sleep 2 + atf_check -s exit:0 -o not-match:'PROMISC' rump.ifconfig vlan0 +} + +vlan_promisc_cleanup() { $DEBUG && dump @@ -112,4 +1040,18 @@ atf_add_test_case vlan_create_destroy atf_add_test_case vlan_basic + atf_add_test_case vlan_auto_follow_mtu + atf_add_test_case vlan_vlanid + atf_add_test_case vlan_configs + atf_add_test_case vlan_bridge + atf_add_test_case vlan_multicast + atf_add_test_case vlan_promisc + + atf_add_test_case vlan_create_destroy6 + atf_add_test_case vlan_basic6 + atf_add_test_case vlan_auto_follow_mtu6 + atf_add_test_case vlan_vlanid6 + atf_add_test_case vlan_configs6 + atf_add_test_case vlan_bridge6 + atf_add_test_case vlan_multicast6 } diff --git a/net/if_wg/Makefile b/net/if_wg/Makefile new file mode 100644 --- /dev/null +++ b/net/if_wg/Makefile @@ -0,0 +1,13 @@ +# $NetBSD: Makefile,v 1.1 2020/08/26 16:03:42 riastradh Exp $ +# + +.include + +TESTSDIR= ${TESTSBASE}/net/if_wg + +.for name in basic interoperability misc tunnel +TESTS_SH+= t_${name} +TESTS_SH_SRC_t_${name}= ../net_common.sh ./common.sh t_${name}.sh +.endfor + +.include diff --git a/net/if_wg/common.sh b/net/if_wg/common.sh new file mode 100644 --- /dev/null +++ b/net/if_wg/common.sh @@ -0,0 +1,200 @@ +# $NetBSD: common.sh,v 1.1 2020/08/26 16:03:42 riastradh Exp $ +# +# Copyright (c) 2018 Ryota Ozaki +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +escape_key() +{ + + echo $1 | sed 's/\+/\\+/g' | sed 's|\/|\\/|g' +} + +setup_servers() +{ + + rump_server_crypto_start $SOCK_LOCAL netinet6 wg + rump_server_add_iface $SOCK_LOCAL shmif0 $BUS + + rump_server_crypto_start $SOCK_PEER netinet6 wg + rump_server_add_iface $SOCK_PEER shmif0 $BUS +} + +check_conf_port() +{ + local ifname=$1 + local port=$2 + + atf_check -s exit:0 -o match:"listen-port: $port" \ + $HIJACKING wgconfig $ifname +} + +check_conf_privkey() +{ + local ifname=$1 + local key_priv="$2" + + atf_check -s exit:0 -o match:"private-key: $(escape_key $key_priv)" \ + $HIJACKING wgconfig $ifname show private-key +} + +setup_common() +{ + local ifconfig="atf_check -s exit:0 rump.ifconfig" + local ifname=$1 + local proto=$2 + local ip=$3 + local prefix=$4 + + $ifconfig $ifname $proto $ip/$prefix +} + +setup_wg_common() +{ + local ifconfig="atf_check -s exit:0 rump.ifconfig" + local wgconfig="atf_check -s exit:0 $HIJACKING wgconfig" + local ifname=$1 + local proto=$2 + local ip=$3 + local prefix=$4 + local port=$5 + local key_priv="$6" + local tun=$7 + local privfile=./tmp + + $ifconfig $ifname create + if [ -n "$tun" ]; then + $ifconfig $ifname linkstr $tun + fi + $ifconfig $ifname $proto $ip/$prefix + $DEBUG && rump.netstat -nr + echo $key_priv > $privfile + $wgconfig $ifname set private-key $privfile + $wgconfig $ifname set listen-port $port + rm -f $privfile + $ifconfig $ifname up + $DEBUG && rump.ifconfig $ifname + + check_conf_port $ifname $port + check_conf_privkey $ifname "$key_priv" +} + +check_ping() +{ + local proto=$1 + local ip=$2 + local ping= + + if [ $proto = inet ]; then + ping="atf_check -s exit:0 -o ignore rump.ping -n -i 0.1 -c 3 -w 1" + else + ping="atf_check -s exit:0 -o ignore rump.ping6 -n -i 0.1 -c 3 -X 1" + fi + + $ping $ip +} + +check_ping_fail() +{ + local proto=$1 + local ip=$2 + local ping= + + if [ $proto = inet ]; then + ping="atf_check -s not-exit:0 -o ignore rump.ping -n -c 1 -w 1" + else + ping="atf_check -s not-exit:0 -o ignore rump.ping6 -n -c 1 -X 1" + fi + + $ping $ip +} + +destroy_wg_interfaces() +{ + local ifconfig="atf_check -s exit:0 rump.ifconfig" + + export RUMP_SERVER=$SOCK_LOCAL + $ifconfig wg0 destroy + export RUMP_SERVER=$SOCK_PEER + $ifconfig wg0 destroy +} + +add_peer() +{ + local wgconfig="atf_check -s exit:0 $HIJACKING wgconfig" + local ifname=$1 + local peername=$2 + local key=$3 + local endpoint=$4 + local allowedips=$5 + local pskfile=$6 + local key_psk="$7" + local pskopt= + local endpoint_opts= + + if [ -n "$pskfile" ]; then + pskopt="--preshared-key=$pskfile" + fi + + if [ -n "$endpoint" ]; then + endpoint_opts="--endpoint=$endpoint" + fi + + $wgconfig $ifname add peer $peername $key $endpoint_opts \ + --allowed-ips=$allowedips $pskopt + atf_check -s exit:0 -o match:"allowed-ips: $allowedips" \ + $HIJACKING wgconfig $ifname show peer $peername + if [ -n "$key_psk" ]; then + atf_check -s exit:0 \ + -o match:"preshared-key: $(escape_key $key_psk)" \ + $HIJACKING wgconfig $ifname show peer $peername \ + --show-preshared-key + else + atf_check -s exit:0 -o match:"preshared-key: \(none\)" \ + $HIJACKING wgconfig $ifname show peer $peername \ + --show-preshared-key + fi +} + +delete_peer() +{ + local wgconfig="atf_check -s exit:0 $HIJACKING wgconfig" + local ifname=$1 + local peername=$2 + + $wgconfig $ifname delete peer $peername + atf_check -s exit:0 -o not-match:"peer: $peername" \ + $HIJACKING wgconfig $ifname +} + +generate_keys() +{ + + key_priv_local=$(wg-keygen) + key_pub_local=$(echo $key_priv_local| wg-keygen --pub) + key_priv_peer=$(wg-keygen) + key_pub_peer=$(echo $key_priv_peer| wg-keygen --pub) + + export key_priv_local key_pub_local key_priv_peer key_pub_peer +} diff --git a/net/if_wg/t_basic.sh b/net/if_wg/t_basic.sh new file mode 100644 --- /dev/null +++ b/net/if_wg/t_basic.sh @@ -0,0 +1,477 @@ +# $NetBSD: t_basic.sh,v 1.4 2021/03/02 07:16:24 simonb Exp $ +# +# Copyright (c) 2018 Ryota Ozaki +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +BUS=bus +SOCK_LOCAL=unix://wg_local +SOCK_PEER=unix://wg_peer +SOCK_PEER2=unix://wg_peer2 + + +check_ping_payload() +{ + local proto=$1 + local ip=$2 + local ping= size= + + if [ $proto = inet ]; then + ping="atf_check -s exit:0 -o ignore rump.ping -n -c 1 -w 1" + else + ping="atf_check -s exit:0 -o ignore rump.ping6 -n -c 1 -X 1" + fi + + for size in $(seq 1 100) $(seq 450 550) $(seq 1400 1500); do + $ping -s $size $ip + done +} + +test_common() +{ + local type=$1 + local outer_proto=$2 + local inner_proto=$3 + local ifconfig="atf_check -s exit:0 rump.ifconfig" + local port=51820 + local ip_local= ip_peer= + local ip_wg_local= ip_wg_peer= + local outer_prefix= outer_prefixall= + local inner_prefix= inner_prefixall= + + if [ $outer_proto = inet ]; then + ip_local=192.168.1.1 + ip_peer=192.168.1.2 + outer_prefix=24 + outer_prefixall=32 + else + ip_local=fc00::1 + ip_peer=fc00::2 + outer_prefix=64 + outer_prefixall=128 + fi + + if [ $inner_proto = inet ]; then + ip_wg_local=10.0.0.1 + ip_wg_peer=10.0.0.2 + inner_prefix=24 + inner_prefixall=32 + else + ip_wg_local=fd00::1 + ip_wg_peer=fd00::2 + inner_prefix=64 + inner_prefixall=128 + fi + + setup_servers + + # It sets key_priv_local key_pub_local key_priv_peer key_pub_peer + generate_keys + + export RUMP_SERVER=$SOCK_LOCAL + setup_common shmif0 $outer_proto $ip_local $outer_prefix + setup_wg_common wg0 $inner_proto $ip_wg_local $inner_prefix $port "$key_priv_local" + add_peer wg0 peer0 $key_pub_peer $ip_peer:$port $ip_wg_peer/$inner_prefixall + $ifconfig -w 10 + + export RUMP_SERVER=$SOCK_PEER + setup_common shmif0 $outer_proto $ip_peer $outer_prefix + setup_wg_common wg0 $inner_proto $ip_wg_peer $inner_prefix $port "$key_priv_peer" + add_peer wg0 peer0 $key_pub_local $ip_local:$port $ip_wg_local/$inner_prefixall + $ifconfig -w 10 + + if [ $type = basic ]; then + export RUMP_SERVER=$SOCK_LOCAL + check_ping $inner_proto $ip_wg_peer + elif [ $type = payload ]; then + export RUMP_SERVER=$SOCK_LOCAL + check_ping_payload $inner_proto $ip_wg_peer + fi + + destroy_wg_interfaces +} + +atf_test_case wg_create_destroy cleanup +wg_create_destroy_head() +{ + + atf_set "descr" "tests to create/destroy wg(4) interfaces" + atf_set "require.progs" "rump_server" "wgconfig" "wg-keygen" +} + +wg_create_destroy_body() +{ + + rump_server_crypto_start $SOCK_LOCAL netinet6 wg + + test_create_destroy_common $SOCK_LOCAL wg0 true +} + +wg_create_destroy_cleanup() +{ + + $DEBUG && dump + cleanup +} + +wg_create_destroy_peers_common() +{ + local proto=$1 + local ifconfig="atf_check -s exit:0 rump.ifconfig" + local port=51820 + local ip_local= ip_peer= + local ip_wg_local= ip_wg_peer= + local outer_prefix= outer_prefixall= + local inner_prefix= inner_prefixall= + + if [ $proto = inet ]; then + ip_local=192.168.1.1 + ip_peer=192.168.1.2 + outer_prefix=24 + outer_prefixall=32 + ip_wg_local=10.0.0.1 + ip_wg_peer=10.0.0.2 + inner_prefix=24 + inner_prefixall=32 + else + ip_local=fc00::1 + ip_peer=fc00::2 + outer_prefix=64 + outer_prefixall=128 + ip_wg_local=fd00::1 + ip_wg_peer=fd00::2 + inner_prefix=64 + inner_prefixall=128 + fi + + rump_server_crypto_start $SOCK_LOCAL netinet6 wg + rump_server_add_iface $SOCK_LOCAL shmif0 $BUS + + # It sets key_priv_local key_pub_local key_priv_peer key_pub_peer + generate_keys + + export RUMP_SERVER=$SOCK_LOCAL + setup_common shmif0 $proto $ip_local $outer_prefix + setup_wg_common wg0 $proto $ip_wg_local $inner_prefix $port "$key_priv_local" + + add_peer wg0 peer0 $key_pub_peer $ip_peer:$port $ip_wg_peer/$inner_prefixall + + delete_peer wg0 peer0 +} + +atf_test_case wg_create_destroy_peers_ipv4 cleanup +wg_create_destroy_peers_ipv4_head() +{ + + atf_set "descr" "tests to create/destroy peers (IPv4)" + atf_set "require.progs" "rump_server" "wgconfig" "wg-keygen" +} + +wg_create_destroy_peers_ipv4_body() +{ + + wg_create_destroy_peers_common inet +} + +wg_create_destroy_peers_ipv4_cleanup() +{ + + $DEBUG && dump + cleanup +} + +atf_test_case wg_create_destroy_peers_ipv6 cleanup +wg_create_destroy_peers_ipv6_head() +{ + + atf_set "descr" "tests to create/destroy peers (IPv6)" + atf_set "require.progs" "rump_server" "wgconfig" "wg-keygen" +} + +wg_create_destroy_peers_ipv6_body() +{ + + wg_create_destroy_peers_common inet6 +} + +wg_create_destroy_peers_ipv6_cleanup() +{ + + $DEBUG && dump + cleanup +} + +add_basic_test() +{ + local inner=$1 + local outer=$2 + local ipv4=inet + local ipv6=inet6 + + name="wg_basic_${inner}_over_${outer}" + fulldesc="Test wg(4) with ${inner} over ${outer}" + + eval inner=\$$inner + eval outer=\$$outer + + atf_test_case ${name} cleanup + eval " + ${name}_head() { + atf_set descr \"${fulldesc}\" + atf_set require.progs rump_server wgconfig wg-keygen + } + ${name}_body() { + test_common basic $outer $inner + rump_server_destroy_ifaces + } + ${name}_cleanup() { + \$DEBUG && dump + cleanup + }" + atf_add_test_case ${name} +} + +add_payload_sizes_test() +{ + local inner=$1 + local outer=$2 + local ipv4=inet + local ipv6=inet6 + + name="wg_payload_sizes_${inner}_over_${outer}" + fulldesc="Test wg(4) with ${inner} over ${outer} with various payload sizes" + + eval inner=\$$inner + eval outer=\$$outer + + atf_test_case ${name} cleanup + eval " + ${name}_head() { + atf_set descr \"${fulldesc}\" + atf_set require.progs rump_server wgconfig wg-keygen + } + ${name}_body() { + test_common payload $outer $inner + rump_server_destroy_ifaces + } + ${name}_cleanup() { + \$DEBUG && dump + cleanup + }" + atf_add_test_case ${name} +} + +atf_test_case wg_multiple_interfaces cleanup +wg_multiple_interfaces_head() +{ + + atf_set "descr" "tests multiple wg(4) interfaces" + atf_set "require.progs" "rump_server" "wgconfig" "wg-keygen" +} + +wg_multiple_interfaces_body() +{ + local ifconfig="atf_check -s exit:0 rump.ifconfig" + local ping="atf_check -s exit:0 -o ignore rump.ping -n -i 0.1 -c 3 -w 1" + local ping_fail="atf_check -s not-exit:0 -o ignore rump.ping -n -c 1 -w 1" + local key_priv_peer2= + local key_pub_peer2= + local ip_local=192.168.1.1 + local ip_local2=192.168.2.1 + local ip_peer=192.168.1.2 + local ip_peer2=192.168.2.2 + local ip_wg_local=10.0.0.1 + local ip_wg_local2=10.0.1.1 + local ip_wg_peer=10.0.0.2 + local ip_wg_peer2=10.0.1.2 + local port=51820 + local port2=51821 + local outfile=./out + + setup_servers + rump_server_add_iface $SOCK_LOCAL shmif1 $BUS + + rump_server_crypto_start $SOCK_PEER2 netinet6 wg + rump_server_add_iface $SOCK_PEER2 shmif0 $BUS + + # It sets key_priv_local key_pub_local key_priv_peer key_pub_peer + generate_keys + key_priv_peer2=$(wg-keygen) + key_pub_peer2=$(echo $key_priv_peer2| wg-keygen --pub) + + export RUMP_SERVER=$SOCK_LOCAL + setup_common shmif0 inet $ip_local 24 + setup_common shmif1 inet $ip_local2 24 + setup_wg_common wg0 inet $ip_wg_local 24 $port "$key_priv_local" + setup_wg_common wg1 inet $ip_wg_local2 24 $port2 "$key_priv_local" + add_peer wg0 peer0 $key_pub_peer $ip_peer:$port $ip_wg_peer/32 + add_peer wg1 peer0 $key_pub_peer2 $ip_peer2:$port2 $ip_wg_peer2/32 + $ifconfig -w 10 + + export RUMP_SERVER=$SOCK_PEER + setup_common shmif0 inet $ip_peer 24 + setup_wg_common wg0 inet $ip_wg_peer 24 $port "$key_priv_peer" + add_peer wg0 peer0 $key_pub_local $ip_local:$port $ip_wg_local/32 + $ifconfig -w 10 + + export RUMP_SERVER=$SOCK_PEER2 + setup_common shmif0 inet $ip_peer2 24 + setup_wg_common wg0 inet $ip_wg_peer2 24 $port2 "$key_priv_peer2" + add_peer wg0 peer0 $key_pub_local $ip_local2:$port2 $ip_wg_local2/32 + $ifconfig -w 10 + + export RUMP_SERVER=$SOCK_LOCAL + + extract_new_packets $BUS > $outfile + $DEBUG && cat $outfile + + $ping $ip_wg_peer + + extract_new_packets $BUS > $outfile + $DEBUG && cat $outfile + + $ping $ip_wg_peer2 + + extract_new_packets $BUS > $outfile + $DEBUG && cat $outfile + + export RUMP_SERVER=$SOCK_LOCAL + $ifconfig wg0 destroy + $ifconfig wg1 destroy + export RUMP_SERVER=$SOCK_PEER + $ifconfig wg0 destroy + export RUMP_SERVER=$SOCK_PEER2 + $ifconfig wg0 destroy +} + +wg_multiple_interfaces_cleanup() +{ + + $DEBUG && dump + cleanup +} + +atf_test_case wg_multiple_peers cleanup +wg_multiple_peers_head() +{ + + atf_set "descr" "tests multiple wg(4) peers" + atf_set "require.progs" "rump_server" "wgconfig" "wg-keygen" +} + +wg_multiple_peers_body() +{ + local ifconfig="atf_check -s exit:0 rump.ifconfig" + local ping="atf_check -s exit:0 -o ignore rump.ping -n -i 0.1 -c 3 -w 1" + local ping_fail="atf_check -s not-exit:0 -o ignore rump.ping -n -c 1 -w 1" + local key_priv_peer2= + local key_pub_peer2= + local ip_local=192.168.1.1 + local ip_peer=192.168.1.2 + local ip_peer2=192.168.1.3 + local ip_wg_local=10.0.0.1 + local ip_wg_peer=10.0.0.2 + local ip_wg_peer2=10.0.0.3 + local port=51820 + local outfile=./out + + setup_servers + rump_server_add_iface $SOCK_LOCAL shmif1 $BUS + + rump_server_crypto_start $SOCK_PEER2 netinet6 wg + rump_server_add_iface $SOCK_PEER2 shmif0 $BUS + + # It sets key_priv_local key_pub_local key_priv_peer key_pub_peer + generate_keys + key_priv_peer2=$(wg-keygen) + key_pub_peer2=$(echo $key_priv_peer2| wg-keygen --pub) + + export RUMP_SERVER=$SOCK_LOCAL + setup_common shmif0 inet $ip_local 24 + setup_wg_common wg0 inet $ip_wg_local 24 $port "$key_priv_local" + add_peer wg0 peer0 $key_pub_peer $ip_peer:$port $ip_wg_peer/32 + add_peer wg0 peer1 $key_pub_peer2 $ip_peer2:$port $ip_wg_peer2/32 + $ifconfig -w 10 + + export RUMP_SERVER=$SOCK_PEER + setup_common shmif0 inet $ip_peer 24 + setup_wg_common wg0 inet $ip_wg_peer 24 $port "$key_priv_peer" + add_peer wg0 peer0 $key_pub_local $ip_local:$port $ip_wg_local/32 + $ifconfig -w 10 + + export RUMP_SERVER=$SOCK_PEER2 + setup_common shmif0 inet $ip_peer2 24 + setup_wg_common wg0 inet $ip_wg_peer2 24 $port "$key_priv_peer2" + add_peer wg0 peer0 $key_pub_local $ip_local:$port $ip_wg_local/32 + $ifconfig -w 10 + + export RUMP_SERVER=$SOCK_LOCAL + + extract_new_packets $BUS > $outfile + $DEBUG && cat $outfile + + $ping $ip_wg_peer + + extract_new_packets $BUS > $outfile + $DEBUG && cat $outfile + + $ping $ip_wg_peer2 + + extract_new_packets $BUS > $outfile + $DEBUG && cat $outfile + + export RUMP_SERVER=$SOCK_LOCAL + $ifconfig wg0 destroy + export RUMP_SERVER=$SOCK_PEER + $ifconfig wg0 destroy + export RUMP_SERVER=$SOCK_PEER2 + $ifconfig wg0 destroy +} + +wg_multiple_peers_cleanup() +{ + + $DEBUG && dump + cleanup +} + +atf_init_test_cases() +{ + + add_basic_test ipv4 ipv4 + add_basic_test ipv4 ipv6 + add_basic_test ipv6 ipv4 + add_basic_test ipv6 ipv6 + + add_payload_sizes_test ipv4 ipv4 + add_payload_sizes_test ipv4 ipv6 + add_payload_sizes_test ipv6 ipv4 + add_payload_sizes_test ipv6 ipv6 + + atf_add_test_case wg_create_destroy + atf_add_test_case wg_create_destroy_peers_ipv4 + atf_add_test_case wg_create_destroy_peers_ipv6 + atf_add_test_case wg_multiple_interfaces + atf_add_test_case wg_multiple_peers +} diff --git a/net/if_wg/t_interoperability.sh b/net/if_wg/t_interoperability.sh new file mode 100644 --- /dev/null +++ b/net/if_wg/t_interoperability.sh @@ -0,0 +1,279 @@ +# $NetBSD: t_interoperability.sh,v 1.1 2020/08/26 16:03:42 riastradh Exp $ +# +# Copyright (c) 2018 Ryota Ozaki +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +BUS=bus +SOCK_LOCAL=unix://wg_local +SOCK_PEER=unix://wg_peer + + +atf_test_case wg_interoperability_basic cleanup +wg_interoperability_basic_head() +{ + + atf_set "descr" "tests of interoperability with the WireGuard protocol" + atf_set "require.progs" "rump_server" "wgconfig" "wg-keygen" +} + +# +# Set ATF_NET_IF_WG_INTEROPERABILITY=yes to run the test. +# Also to run the test, the following setups are required on the host and a peer. +# +# [Host] +# ifconfig bridge0 create +# ifconfig tap0 create +# brconfig bridge0 add tap0 +# brconfig bridge0 add +# ifconfig tap0 up +# ifconfig bridge0 up +# +# [Peer] +# ip addr add 10.0.0.2/24 dev +# ip link add wg0 type wireguard +# ip addr add 10.0.1.2/24 dev wg0 +# privkey="EF9D8AOkmxjlkiRFqBnfJS+RJJHbUy02u+VkGlBr9Eo=" +# ip link set wg0 up +# echo $privkey > /tmp/private-key +# wg set wg0 listen-port 52428 +# wg set wg0 private-key /tmp/private-key +# pubkey="2iWFzywbDvYu2gQW5Q7/z/g5/Cv4bDDd6L3OKXLOwxs=" +# wg set wg0 peer $pubkey endpoint 10.0.0.3:52428 allowed-ips 10.0.1.1/32 +# +wg_interoperability_basic_body() +{ + local ifconfig="atf_check -s exit:0 rump.ifconfig" + local ping="atf_check -s exit:0 -o ignore rump.ping -n -c 3 -w 3" + local ping_fail="atf_check -s not-exit:0 -o ignore rump.ping -n -c 1 -w 3" + local key_priv_local= + local key_pub_local= + local key_priv_peer= + local key_pub_peer= + local ip_local=10.0.0.3 + local ip_peer=10.0.0.2 + local ip_wg_local=10.0.1.1 + local ip_wg_peer=10.0.1.2 + local port=52428 + local outfile=./out + + if [ "$ATF_NET_IF_WG_INTEROPERABILITY" != yes ]; then + atf_skip "set ATF_NET_IF_WG_INTEROPERABILITY=yes to run the test" + fi + + export RUMP_SERVER=$SOCK_LOCAL + rump_server_crypto_start $SOCK_LOCAL virtif wg netinet6 + atf_check -s exit:0 rump.sysctl -q -w net.inet.ip.dad_count=0 + atf_check -s exit:0 rump.ifconfig virt0 create + atf_check -s exit:0 rump.ifconfig virt0 $ip_local/24 + atf_check -s exit:0 rump.ifconfig virt0 up + + $ping $ip_peer + + key_priv_local="aK3TbzUNDO4aeDRX54x8bOG+NaKuqXKt7Hwq0Uz69Wo=" + key_pub_local="2iWFzywbDvYu2gQW5Q7/z/g5/Cv4bDDd6L3OKXLOwxs=" + key_priv_peer="EF9D8AOkmxjlkiRFqBnfJS+RJJHbUy02u+VkGlBr9Eo=" + key_pub_peer="2ZM9RvDmMZS/Nuh8OaVaJrwFbO57/WJgeU+JoQ//nko=" + + setup_wg_common wg0 inet $ip_wg_local 24 $port "$key_priv_local" + add_peer wg0 peer0 $key_pub_peer $ip_peer:$port $ip_wg_peer/32 + + $ping $ip_wg_peer + + export RUMP_SERVER=$SOCK_LOCAL + $ifconfig wg0 destroy +} + +wg_interoperability_basic_cleanup() +{ + + $DEBUG && dump + cleanup +} + +atf_test_case wg_interoperability_cookie cleanup +wg_interoperability_cookie_head() +{ + + atf_set "descr" "tests of interoperability with the WireGuard protocol" + atf_set "require.progs" "rump_server" "wgconfig" "wg-keygen" +} + +wg_interoperability_cookie_body() +{ + local ifconfig="atf_check -s exit:0 rump.ifconfig" + local ping="atf_check -s exit:0 -o ignore rump.ping -n -c 3 -w 3" + local ping_fail="atf_check -s not-exit:0 -o ignore rump.ping -n -c 1 -w 3" + local key_priv_local= + local key_pub_local= + local key_priv_peer= + local key_pub_peer= + local ip_local=10.0.0.3 + local ip_peer=10.0.0.2 + local ip_wg_local=10.0.1.1 + local ip_wg_peer=10.0.1.2 + local port=52428 + local outfile=./out + local rekey_timeout=5 # default + + if [ "$ATF_NET_IF_WG_INTEROPERABILITY" != yes ]; then + atf_skip "set ATF_NET_IF_WG_INTEROPERABILITY=yes to run the test" + fi + + export RUMP_SERVER=$SOCK_LOCAL + rump_server_crypto_start $SOCK_LOCAL virtif wg netinet6 + atf_check -s exit:0 rump.sysctl -q -w net.inet.ip.dad_count=0 + atf_check -s exit:0 rump.ifconfig virt0 create + atf_check -s exit:0 rump.ifconfig virt0 $ip_local/24 + atf_check -s exit:0 rump.ifconfig virt0 up + + $ping $ip_peer + + key_priv_local="aK3TbzUNDO4aeDRX54x8bOG+NaKuqXKt7Hwq0Uz69Wo=" + key_pub_local="2iWFzywbDvYu2gQW5Q7/z/g5/Cv4bDDd6L3OKXLOwxs=" + key_priv_peer="EF9D8AOkmxjlkiRFqBnfJS+RJJHbUy02u+VkGlBr9Eo=" + key_pub_peer="2ZM9RvDmMZS/Nuh8OaVaJrwFbO57/WJgeU+JoQ//nko=" + + setup_wg_common wg0 inet $ip_wg_local 24 $port "$key_priv_local" + + # Emulate load to send back a cookie on receiving a response message + atf_check -s exit:0 -o ignore \ + rump.sysctl -w net.wg.force_underload=1 + + add_peer wg0 peer0 $key_pub_peer $ip_peer:$port $ip_wg_peer/32 + + # ping fails because we don't accept a response message and send a cookie + $ping_fail $ip_wg_peer + + # Wait for retrying an initialization that works because the peer + # send a response message with the cookie we sent + atf_check -s exit:0 sleep $rekey_timeout + + # So ping works + $ping $ip_wg_peer + + export RUMP_SERVER=$SOCK_LOCAL + $ifconfig wg0 destroy +} + +wg_interoperability_cookie_cleanup() +{ + + $DEBUG && dump + cleanup +} + +atf_test_case wg_userspace_basic cleanup +wg_userspace_basic_head() +{ + + atf_set "descr" "tests of userspace implementation of wg(4)" + atf_set "require.progs" "rump_server" "wgconfig" "wg-keygen" +} + +# +# Set ATF_NET_IF_WG_USERSPACE=yes to run the test. +# Also to run the test, the following setups are required on the host and a peer. +# +# [Host] +# ifconfig bridge0 create +# ifconfig tap0 create +# brconfig bridge0 add tap0 +# brconfig bridge0 add +# ifconfig tap0 up +# ifconfig bridge0 up +# +# [Peer] +# ip addr add 10.0.0.2/24 dev +# ip link add wg0 type wireguard +# ip addr add 10.0.4.2/24 dev wg0 +# privkey="EF9D8AOkmxjlkiRFqBnfJS+RJJHbUy02u+VkGlBr9Eo=" +# ip link set wg0 up +# echo $privkey > /tmp/private-key +# wg set wg0 listen-port 52428 +# wg set wg0 private-key /tmp/private-key +# pubkey="6mQ4lUO3oq5O8FfGW52CFXNbmh5iFT1XMqPzpdrc0nE=" +# wg set wg0 peer $pubkey endpoint 10.0.0.3:52428 allowed-ips 10.0.4.1/32 +# +wg_userspace_basic_body() +{ + local ifconfig="atf_check -s exit:0 rump.ifconfig" + local ping="atf_check -s exit:0 -o ignore ping -n -c 3 -w 3" + local ping_fail="atf_check -s not-exit:0 -o ignore ping -n -c 1 -w 3" + local key_priv_local= + local key_pub_local= + local key_priv_peer= + local key_pub_peer= + local ip_local=10.0.0.3 + local ip_peer=10.0.0.2 + local ip_wg_local=10.0.4.1 + local ip_wg_peer=10.0.4.2 + local port_local=52429 + local port_peer=52428 + local outfile=./out + + if [ "$ATF_NET_IF_WG_USERSPACE" != yes ]; then + atf_skip "set ATF_NET_IF_WG_USERSPACE=yes to run the test" + fi + + export RUMP_SERVER=$SOCK_LOCAL + rump_server_crypto_start $SOCK_LOCAL virtif wg netinet6 + atf_check -s exit:0 rump.sysctl -q -w net.inet.ip.dad_count=0 + + $DEBUG && netstat -nr -f inet + + $ping $ip_peer + + key_priv_local="6B0dualfIAiEG7/jFGOIHrJMhuypq87xCER/0ieIpE4=" + key_pub_local="6mQ4lUO3oq5O8FfGW52CFXNbmh5iFT1XMqPzpdrc0nE=" + key_priv_peer="EF9D8AOkmxjlkiRFqBnfJS+RJJHbUy02u+VkGlBr9Eo=" + key_pub_peer="2ZM9RvDmMZS/Nuh8OaVaJrwFbO57/WJgeU+JoQ//nko=" + + setup_wg_common wg0 inet $ip_wg_local 24 $port_local "$key_priv_local" tun0 + add_peer wg0 peer0 $key_pub_peer $ip_peer:$port_peer $ip_wg_peer/32 + + $DEBUG && rump.ifconfig wg0 + $DEBUG && ifconfig tun0 + $DEBUG && netstat -nr -f inet + + $ping $ip_wg_peer + + export RUMP_SERVER=$SOCK_LOCAL + $ifconfig wg0 destroy +} + +wg_userspace_basic_cleanup() +{ + + $DEBUG && dump + cleanup +} + +atf_init_test_cases() +{ + + atf_add_test_case wg_interoperability_basic + atf_add_test_case wg_interoperability_cookie + atf_add_test_case wg_userspace_basic +} diff --git a/net/if_wg/t_misc.sh b/net/if_wg/t_misc.sh new file mode 100644 --- /dev/null +++ b/net/if_wg/t_misc.sh @@ -0,0 +1,678 @@ +# $NetBSD: t_misc.sh,v 1.10 2021/06/17 12:45:58 riastradh Exp $ +# +# Copyright (c) 2018 Ryota Ozaki +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +BUS=bus +SOCK_LOCAL=unix://wg_local +SOCK_PEER=unix://wg_peer + + +atf_test_case wg_rekey cleanup +wg_rekey_head() +{ + + atf_set "descr" "tests of rekeying of wg(4)" + atf_set "require.progs" "rump_server" "wgconfig" "wg-keygen" +} + +wg_rekey_body() +{ + local ifconfig="atf_check -s exit:0 rump.ifconfig" + local ping="atf_check -s exit:0 -o ignore rump.ping -n -c 1 -w 1" + local ip_local=192.168.1.1 + local ip_peer=192.168.1.2 + local ip_wg_local=10.0.0.1 + local ip_wg_peer=10.0.0.2 + local port=51820 + local rekey_after_time=3 + local latest_handshake= + + setup_servers + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 -o ignore \ + rump.sysctl -w net.wg.rekey_after_time=$rekey_after_time + export RUMP_SERVER=$SOCK_PEER + atf_check -s exit:0 -o ignore \ + rump.sysctl -w net.wg.rekey_after_time=$rekey_after_time + + # It sets key_priv_local key_pub_local key_priv_peer key_pub_peer + generate_keys + + export RUMP_SERVER=$SOCK_LOCAL + setup_common shmif0 inet $ip_local 24 + setup_wg_common wg0 inet $ip_wg_local 24 $port "$key_priv_local" + add_peer wg0 peer0 $key_pub_peer $ip_peer:$port $ip_wg_peer/32 + $ifconfig -w 10 + + export RUMP_SERVER=$SOCK_PEER + setup_common shmif0 inet $ip_peer 24 + setup_wg_common wg0 inet $ip_wg_peer 24 $port "$key_priv_peer" + add_peer wg0 peer0 $key_pub_local $ip_local:$port $ip_wg_local/32 + $ifconfig -w 10 + + export RUMP_SERVER=$SOCK_LOCAL + + $ping $ip_wg_peer + + latest_handshake=$($HIJACKING wgconfig wg0 show peer peer0 \ + | awk -F ': ' '/latest-handshake/ {print $2;}') + $DEBUG && echo $latest_handshake + + sleep 1 + + $ping $ip_wg_peer + + atf_expect_fail "PR kern/56252" + + # No reinitiation is performed + atf_check -s exit:0 -o match:"$latest_handshake" \ + $HIJACKING wgconfig wg0 show peer peer0 + + # Wait for a reinitiation to be performed + sleep $rekey_after_time + + $ping $ip_wg_peer + + # A reinitiation should be performed + atf_check -s exit:0 -o not-match:"$latest_handshake" \ + $HIJACKING wgconfig wg0 show peer peer0 + + latest_handshake=$($HIJACKING wgconfig wg0 show peer peer0 \ + | awk -F ': ' '/latest-handshake/ {print $2;}') + $DEBUG && echo $latest_handshake + + # Wait for a reinitiation to be performed again + sleep $((rekey_after_time+1)) + + $ping $ip_wg_peer + + # A reinitiation should be performed + atf_check -s exit:0 -o not-match:"$latest_handshake" \ + $HIJACKING wgconfig wg0 show peer peer0 + + destroy_wg_interfaces + + atf_fail "failed to trigger PR kern/56252" +} + +wg_rekey_cleanup() +{ + + $DEBUG && dump + cleanup +} + +atf_test_case wg_handshake_timeout cleanup +wg_handshake_timeout_head() +{ + + atf_set "descr" "tests of handshake timeout of wg(4)" + atf_set "require.progs" "rump_server" "wgconfig" "wg-keygen" +} + +wg_handshake_timeout_body() +{ + local ifconfig="atf_check -s exit:0 rump.ifconfig" + local ping="atf_check -s exit:0 -o ignore rump.ping -n -c 1 -w 1" + local ip_local=192.168.1.1 + local ip_peer=192.168.1.2 + local ip_wg_local=10.0.0.1 + local ip_wg_peer=10.0.0.2 + local port=51820 + local rekey_after_time=3 + local outfile=./out + local rekey_timeout=3 + local rekey_attempt_time=8 + local n= + + setup_servers + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 -o ignore \ + rump.sysctl -w net.wg.rekey_timeout=$rekey_timeout + atf_check -s exit:0 -o ignore \ + rump.sysctl -w net.wg.rekey_attempt_time=$rekey_attempt_time + export RUMP_SERVER=$SOCK_PEER + atf_check -s exit:0 -o ignore \ + rump.sysctl -w net.wg.rekey_timeout=$rekey_timeout + atf_check -s exit:0 -o ignore \ + rump.sysctl -w net.wg.rekey_attempt_time=$rekey_attempt_time + + # It sets key_priv_local key_pub_local key_priv_peer key_pub_peer + generate_keys + + export RUMP_SERVER=$SOCK_LOCAL + setup_common shmif0 inet $ip_local 24 + setup_wg_common wg0 inet $ip_wg_local 24 $port "$key_priv_local" + add_peer wg0 peer0 $key_pub_peer $ip_peer:$port $ip_wg_peer/32 + $ifconfig -w 10 + + export RUMP_SERVER=$SOCK_PEER + setup_common shmif0 inet $ip_peer 24 + setup_wg_common wg0 inet $ip_wg_peer 24 $port "$key_priv_peer" + add_peer wg0 peer0 $key_pub_local $ip_local:$port $ip_wg_local/32 + $ifconfig -w 10 + + # Resolve arp + export RUMP_SERVER=$SOCK_LOCAL + $ping $ip_peer + + export RUMP_SERVER=$SOCK_PEER + $ifconfig shmif0 down + export RUMP_SERVER=$SOCK_LOCAL + + extract_new_packets $BUS > $outfile + + # Should fail + atf_check -s not-exit:0 -o match:'100.0% packet loss' \ + rump.ping -n -c 1 -w 1 $ip_wg_peer + + sleep $((rekey_attempt_time + rekey_timeout)) + + extract_new_packets $BUS > $outfile + $DEBUG && cat $outfile + + n=$(grep "$ip_local.$port > $ip_peer.$port" $outfile |wc -l) + + # Give up handshaking after three attempts + atf_check_equal $n 3 + + export RUMP_SERVER=$SOCK_PEER + $ifconfig shmif0 up + export RUMP_SERVER=$SOCK_LOCAL + + destroy_wg_interfaces + + atf_fail "failed to trigger PR kern/56252" +} + +wg_handshake_timeout_cleanup() +{ + + $DEBUG && dump + cleanup +} + +atf_test_case wg_cookie cleanup +wg_cookie_head() +{ + + atf_set "descr" "tests of cookie messages of the wg(4) protocol" + atf_set "require.progs" "rump_server" "wgconfig" "wg-keygen" +} + +wg_cookie_body() +{ + local ifconfig="atf_check -s exit:0 rump.ifconfig" + local ping="atf_check -s exit:0 -o ignore rump.ping -n -i 0.1 -c 3 -w 1" + local ping_fail="atf_check -s not-exit:0 -o ignore rump.ping -n -c 1 -w 1" + local ip_local=192.168.1.1 + local ip_peer=192.168.1.2 + local ip_wg_local=10.0.0.1 + local ip_wg_peer=10.0.0.2 + local port=51820 + local outfile=./out + local rekey_timeout=5 + + setup_servers + + # It sets key_priv_local key_pub_local key_priv_peer key_pub_peer + generate_keys + + export RUMP_SERVER=$SOCK_LOCAL + setup_common shmif0 inet $ip_local 24 + setup_wg_common wg0 inet $ip_wg_local 24 $port "$key_priv_local" + add_peer wg0 peer0 $key_pub_peer $ip_peer:$port $ip_wg_peer/32 + $ifconfig -w 10 + + export RUMP_SERVER=$SOCK_PEER + setup_common shmif0 inet $ip_peer 24 + setup_wg_common wg0 inet $ip_wg_peer 24 $port "$key_priv_peer" + add_peer wg0 peer0 $key_pub_local $ip_local:$port $ip_wg_local/32 + $ifconfig -w 10 + + export RUMP_SERVER=$SOCK_PEER + # Emulate load on the peer + atf_check -s exit:0 -o ignore \ + rump.sysctl -w net.wg.force_underload=1 + + export RUMP_SERVER=$SOCK_LOCAL + + extract_new_packets $BUS > $outfile + $DEBUG && cat $outfile + + # The peer doesn't return a response message but a cookie message + # and a session doesn't start + $ping_fail $ip_wg_peer + + atf_expect_fail "PR kern/56252" + + extract_new_packets $BUS > $outfile + $DEBUG && cat $outfile + # XXX length 64 indicates the message is a cookie message + atf_check -s exit:0 \ + -o match:"$ip_peer.$port > $ip_local.$port: UDP, length 64" \ + cat $outfile + + $DEBUG && $HIJACKING wgconfig wg0 show all + atf_check -s exit:0 -o match:"latest-handshake: \(never\)" \ + $HIJACKING wgconfig wg0 + + # Wait for restarting a session + sleep $rekey_timeout + + # The second attempt should be success because the init message has + # a valid cookie. + $ping $ip_wg_peer + + $DEBUG && $HIJACKING wgconfig wg0 show all + atf_check -s exit:0 -o not-match:"latest-handshake: \(never\)" \ + $HIJACKING wgconfig wg0 + + destroy_wg_interfaces + + atf_fail "failed to trigger PR kern/56252" +} + +wg_cookie_cleanup() +{ + + $DEBUG && dump + cleanup +} + +atf_test_case wg_mobility cleanup +wg_mobility_head() +{ + + atf_set "descr" "tests of the mobility of wg(4)" + atf_set "require.progs" "rump_server" "wgconfig" "wg-keygen" +} + +wg_mobility_body() +{ + local ifconfig="atf_check -s exit:0 rump.ifconfig" + local ping="atf_check -s exit:0 -o ignore rump.ping -n -i 0.1 -c 3 -w 1" + local ping_fail="atf_check -s not-exit:0 -o ignore rump.ping -n -c 1 -w 1" + local ip_local=192.168.1.1 + local ip_peer=192.168.1.2 + local ip_peer_new=192.168.1.3 + local ip_wg_local=10.0.0.1 + local ip_wg_peer=10.0.0.2 + local port=51820 + local outfile=./out + + setup_servers + + # It sets key_priv_local key_pub_local key_priv_peer key_pub_peer + generate_keys + + export RUMP_SERVER=$SOCK_LOCAL + setup_common shmif0 inet $ip_local 24 + setup_wg_common wg0 inet $ip_wg_local 24 $port "$key_priv_local" + # Initially, the local doesn't know the endpoint of the peer + add_peer wg0 peer0 $key_pub_peer "" $ip_wg_peer/32 + $ifconfig -w 10 + + export RUMP_SERVER=$SOCK_PEER + setup_common shmif0 inet $ip_peer 24 + setup_wg_common wg0 inet $ip_wg_peer 24 $port "$key_priv_peer" + add_peer wg0 peer0 $key_pub_local $ip_local:$port $ip_wg_local/32 + $ifconfig -w 10 + + extract_new_packets $BUS > $outfile + $DEBUG && cat $outfile + + # Ping from the local to the peer doesn't work because the local + # doesn't know the endpoint of the peer + export RUMP_SERVER=$SOCK_LOCAL + $ping_fail $ip_wg_peer + + atf_expect_fail "PR kern/56252" + + extract_new_packets $BUS > $outfile + $DEBUG && cat $outfile + + export RUMP_SERVER=$SOCK_PEER + $ping $ip_wg_local + + extract_new_packets $BUS > $outfile + $DEBUG && cat $outfile + + atf_check -s exit:0 -o match:"$ip_local.$port > $ip_peer.$port" cat $outfile + + # Change the IP address of the peer + setup_common shmif0 inet $ip_peer_new 24 + $ifconfig -w 10 + + # Ping from the local to the peer doesn't work because the local + # doesn't know the change of the IP address of the peer + export RUMP_SERVER=$SOCK_LOCAL + $ping_fail $ip_wg_peer + + extract_new_packets $BUS > $outfile + $DEBUG && cat $outfile + + atf_check -s exit:0 -o match:"$ip_local.$port > $ip_peer.$port" cat $outfile + + # Ping from the peer to the local works because the local notices + # the change and updates the IP address of the peer + export RUMP_SERVER=$SOCK_PEER + $ping $ip_wg_local + + extract_new_packets $BUS > $outfile + $DEBUG && cat $outfile + + atf_check -s exit:0 -o match:"$ip_local.$port > $ip_peer_new.$port" cat $outfile + atf_check -s exit:0 -o match:"$ip_peer_new.$port > $ip_local.$port" cat $outfile + atf_check -s exit:0 -o not-match:"$ip_local.$port > $ip_peer.$port" cat $outfile + + destroy_wg_interfaces + + atf_fail "failed to trigger PR kern/56252" +} + +wg_mobility_cleanup() +{ + + $DEBUG && dump + cleanup +} + +atf_test_case wg_keepalive cleanup +wg_keepalive_head() +{ + + atf_set "descr" "tests keepalive messages" + atf_set "require.progs" "rump_server" "wgconfig" "wg-keygen" +} + +wg_keepalive_body() +{ + local ifconfig="atf_check -s exit:0 rump.ifconfig" + local ping="atf_check -s exit:0 -o ignore rump.ping -n -i 0.1 -c 3 -w 1" + local ping_fail="atf_check -s not-exit:0 -o ignore rump.ping -n -c 1 -w 1" + local ip_local=192.168.1.1 + local ip_peer=192.168.1.2 + local ip_peer_new=192.168.1.3 + local ip_wg_local=10.0.0.1 + local ip_wg_peer=10.0.0.2 + local port=51820 + local outfile=./out + local keepalive_timeout=3 + + setup_servers + + # It sets key_priv_local key_pub_local key_priv_peer key_pub_peer + generate_keys + + export RUMP_SERVER=$SOCK_LOCAL + setup_common shmif0 inet $ip_local 24 + setup_wg_common wg0 inet $ip_wg_local 24 $port "$key_priv_local" + add_peer wg0 peer0 $key_pub_peer $ip_peer:$port $ip_wg_peer/32 + $ifconfig -w 10 + + export RUMP_SERVER=$SOCK_PEER + setup_common shmif0 inet $ip_peer 24 + setup_wg_common wg0 inet $ip_wg_peer 24 $port "$key_priv_peer" + add_peer wg0 peer0 $key_pub_local $ip_local:$port $ip_wg_local/32 + $ifconfig -w 10 + + # Shorten keepalive_timeout of the peer + atf_check -s exit:0 -o ignore \ + rump.sysctl -w net.wg.keepalive_timeout=$keepalive_timeout + + export RUMP_SERVER=$SOCK_LOCAL + + extract_new_packets $BUS > $outfile + $DEBUG && cat $outfile + + $ping $ip_wg_peer + + extract_new_packets $BUS > $outfile + $DEBUG && cat $outfile + + sleep $((keepalive_timeout + 1)) + + $ping $ip_wg_peer + + extract_new_packets $BUS > $outfile + $DEBUG && cat $outfile + + # XXX length 32 indicates the message is a keepalive (empty) message + atf_check -s exit:0 -o match:"$ip_peer.$port > $ip_local.$port: UDP, length 32" \ + cat $outfile + + destroy_wg_interfaces +} + +wg_keepalive_cleanup() +{ + + $DEBUG && dump + cleanup +} + +atf_test_case wg_psk cleanup +wg_psk_head() +{ + + atf_set "descr" "tests preshared-key" + atf_set "require.progs" "rump_server" "wgconfig" "wg-keygen" +} + +test_psk_common() +{ +} + +wg_psk_body() +{ + local ifconfig="atf_check -s exit:0 rump.ifconfig" + local ping="atf_check -s exit:0 -o ignore rump.ping -n -i 0.1 -c 3 -w 1" + local ping_fail="atf_check -s not-exit:0 -o ignore rump.ping -n -c 1 -w 1" + local ip_local=192.168.1.1 + local ip_peer=192.168.1.2 + local ip_peer_new=192.168.1.3 + local ip_wg_local=10.0.0.1 + local ip_wg_peer=10.0.0.2 + local port=51820 + local outfile=./out + local pskfile=./psk + local rekey_after_time=3 + + setup_servers + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 -o ignore \ + rump.sysctl -w net.wg.rekey_after_time=$rekey_after_time + export RUMP_SERVER=$SOCK_PEER + atf_check -s exit:0 -o ignore \ + rump.sysctl -w net.wg.rekey_after_time=$rekey_after_time + + # It sets key_priv_local key_pub_local key_priv_peer key_pub_peer + generate_keys + key_psk=$(wg-keygen --psk) + $DEBUG && echo $key_psk + + export RUMP_SERVER=$SOCK_LOCAL + setup_common shmif0 inet $ip_local 24 + setup_wg_common wg0 inet $ip_wg_local 24 $port "$key_priv_local" + + export RUMP_SERVER=$SOCK_PEER + setup_common shmif0 inet $ip_peer 24 + setup_wg_common wg0 inet $ip_wg_peer 24 $port "$key_priv_peer" + + echo "$key_psk" > $pskfile + + export RUMP_SERVER=$SOCK_LOCAL + + # The local always has the preshared key + add_peer wg0 peer0 $key_pub_peer $ip_peer:$port $ip_wg_peer/32 \ + $pskfile "$key_psk" + $ifconfig -w 10 + + export RUMP_SERVER=$SOCK_PEER + + # First, try the peer without the preshared key + add_peer wg0 peer0 $key_pub_local $ip_local:$port $ip_wg_local/32 + $ifconfig -w 10 + + export RUMP_SERVER=$SOCK_LOCAL + + extract_new_packets $BUS > $outfile + $DEBUG && cat $outfile + + $ping_fail $ip_wg_peer + + extract_new_packets $BUS > $outfile + $DEBUG && cat $outfile + + # Next, try with the preshared key + export RUMP_SERVER=$SOCK_PEER + delete_peer wg0 peer0 + add_peer wg0 peer0 $key_pub_local $ip_local:$port $ip_wg_local/32 \ + $pskfile "$key_psk" + $ifconfig -w 10 + + # Need a rekey + atf_check -s exit:0 sleep $((rekey_after_time + 1)) + + export RUMP_SERVER=$SOCK_LOCAL + + extract_new_packets $BUS > $outfile + $DEBUG && cat $outfile + + $ping $ip_wg_peer + + extract_new_packets $BUS > $outfile + $DEBUG && cat $outfile + + # Then, try again without the preshared key just in case + export RUMP_SERVER=$SOCK_PEER + delete_peer wg0 peer0 + add_peer wg0 peer0 $key_pub_local $ip_local:$port $ip_wg_local/32 + $ifconfig -w 10 + + # Need a rekey + atf_check -s exit:0 sleep $((rekey_after_time + 1)) + + export RUMP_SERVER=$SOCK_LOCAL + $ping_fail $ip_wg_peer + + rm -f $pskfile + + destroy_wg_interfaces +} + +wg_psk_cleanup() +{ + + $DEBUG && dump + cleanup +} + +atf_test_case wg_malformed cleanup +wg_malformed_head() +{ + + atf_set "descr" "tests malformed packet headers" + atf_set "require.progs" "nc" "rump_server" "wgconfig" "wg-keygen" + atf_set "timeout" "10" +} + +wg_malformed_body() +{ + local ifconfig="atf_check -s exit:0 rump.ifconfig" + local ping="atf_check -s exit:0 -o ignore rump.ping -n -c 1 -w 1" + local ip_local=192.168.1.1 + local ip_peer=192.168.1.2 + local ip_wg_local=10.0.0.1 + local ip_wg_peer=10.0.0.2 + local port=51820 + setup_servers + + # It sets key_priv_local key_pub_local key_priv_peer key_pub_peer + generate_keys + + export RUMP_SERVER=$SOCK_LOCAL + setup_common shmif0 inet $ip_local 24 + setup_wg_common wg0 inet $ip_wg_local 24 $port "$key_priv_local" + add_peer wg0 peer0 $key_pub_peer $ip_peer:$port $ip_wg_peer/32 + $ifconfig -w 10 + + export RUMP_SERVER=$SOCK_PEER + setup_common shmif0 inet $ip_peer 24 + setup_wg_common wg0 inet $ip_wg_peer 24 $port "$key_priv_peer" + add_peer wg0 peer0 $key_pub_local $ip_local:$port $ip_wg_local/32 + $ifconfig -w 10 + + export RUMP_SERVER=$SOCK_LOCAL + + $ping $ip_wg_peer + + printf 'send malformed packets\n' + + $HIJACKING ping -c 1 -n $ip_peer + + printf 'x' | $HIJACKING nc -Nu -w 0 $ip_peer $port + printf 'xy' | $HIJACKING nc -Nu -w 0 $ip_peer $port + printf 'xyz' | $HIJACKING nc -Nu -w 0 $ip_peer $port + printf 'xyzw' | $HIJACKING nc -Nu -w 0 $ip_peer $port + printf '\x00\x00\x00\x00' | $HIJACKING nc -Nu -w 0 $ip_peer $port + printf '\x00\x00\x00\x00z' | $HIJACKING nc -Nu -w 0 $ip_peer $port + printf '\x01\x00\x00\x00' | $HIJACKING nc -Nu -w 0 $ip_peer $port + printf '\x01\x00\x00\x00z' | $HIJACKING nc -Nu -w 0 $ip_peer $port + printf '\x02\x00\x00\x00' | $HIJACKING nc -Nu -w 0 $ip_peer $port + printf '\x02\x00\x00\x00z' | $HIJACKING nc -Nu -w 0 $ip_peer $port + printf '\x03\x00\x00\x00' | $HIJACKING nc -Nu -w 0 $ip_peer $port + printf '\x03\x00\x00\x00z' | $HIJACKING nc -Nu -w 0 $ip_peer $port + printf '\x04\x00\x00\x00' | $HIJACKING nc -Nu -w 0 $ip_peer $port + printf '\x04\x00\x00\x00z' | $HIJACKING nc -Nu -w 0 $ip_peer $port + + printf 'done sending malformed packets\n' + + $ping $ip_wg_peer +} + +wg_malformed_cleanup() +{ + + $DEBUG && dump + cleanup +} + +atf_init_test_cases() +{ + + atf_add_test_case wg_rekey + atf_add_test_case wg_handshake_timeout + atf_add_test_case wg_cookie + atf_add_test_case wg_mobility + atf_add_test_case wg_keepalive + atf_add_test_case wg_psk + atf_add_test_case wg_malformed +} diff --git a/net/if_wg/t_tunnel.sh b/net/if_wg/t_tunnel.sh new file mode 100644 --- /dev/null +++ b/net/if_wg/t_tunnel.sh @@ -0,0 +1,332 @@ +# $NetBSD: t_tunnel.sh,v 1.2 2020/08/29 07:22:49 tih Exp $ +# +# Copyright (c) 2018 Ryota Ozaki +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +BUS_LOCAL=bus_local +BUS_TUN=bus_tun +BUS_PEER=bus_peer +SOCK_LOCAL=unix://wg_local +SOCK_TUN_LOCAL=unix://wg_tun_local +SOCK_TUN_PEER=unix://wg_tun_peer +SOCK_PEER=unix://wg_peer + +escape_key() +{ + + echo $1 | sed 's/\+/\\+/g' | sed 's|\/|\\/|g' +} + +setup_servers() +{ + + rump_server_start $SOCK_LOCAL netinet6 + rump_server_add_iface $SOCK_LOCAL shmif0 $BUS_LOCAL + + rump_server_crypto_start $SOCK_TUN_LOCAL netinet6 wg + rump_server_add_iface $SOCK_TUN_LOCAL shmif0 $BUS_LOCAL + rump_server_add_iface $SOCK_TUN_LOCAL shmif1 $BUS_TUN + + rump_server_crypto_start $SOCK_TUN_PEER netinet6 wg + rump_server_add_iface $SOCK_TUN_PEER shmif0 $BUS_PEER + rump_server_add_iface $SOCK_TUN_PEER shmif1 $BUS_TUN + + rump_server_start $SOCK_PEER netinet6 + rump_server_add_iface $SOCK_PEER shmif0 $BUS_PEER +} + +setup_edge() +{ + local ifconfig="atf_check -s exit:0 rump.ifconfig" + local proto=$1 + local ip=$2 + local prefix=$3 + local gw=$4 + local ip_bad=$5 + local alias= + + atf_check -s exit:0 rump.sysctl -q -w net.inet.ip.dad_count=0 + atf_check -s exit:0 rump.sysctl -q -w net.inet6.ip6.dad_count=0 + $ifconfig shmif0 $proto $ip/$prefix + atf_check -s exit:0 -o ignore rump.route add -$proto default $gw + + if [ -z "$ip_bad" ]; then + return + fi + + if [ $proto = inet ]; then + alias="alias" + fi + + $ifconfig shmif0 $proto $ip_bad/$prefix $alias +} + +setup_ip() +{ + local ifconfig="atf_check -s exit:0 rump.ifconfig" + local proto=$1 + local ip=$2 + local prefix=$3 + + $ifconfig shmif0 $proto $ip/$prefix +} +setup_router() +{ + + atf_check -s exit:0 rump.sysctl -q -w net.inet.ip.forwarding=1 + atf_check -s exit:0 rump.sysctl -q -w net.inet6.ip6.forwarding=1 + atf_check -s exit:0 rump.sysctl -q -w net.inet.ip.dad_count=0 + atf_check -s exit:0 rump.sysctl -q -w net.inet6.ip6.dad_count=0 +} + +setup_wg() +{ + local ifconfig="atf_check -s exit:0 rump.ifconfig" + local wgconfig="atf_check -s exit:0 $HIJACKING wgconfig" + local proto=$1 + local ip=$2 + local prefix=$3 + local port=$4 + local key_priv="$5" + local privfile=./tmp + + $ifconfig wg0 create + $ifconfig wg0 $proto $ip/$prefix + $DEBUG && rump.netstat -nr + echo $key_priv > $privfile + $wgconfig wg0 set private-key $privfile + $wgconfig wg0 set listen-port $port + rm -f $privfile + $ifconfig wg0 up + + check_conf_port wg0 $port + check_conf_privkey wg0 "$key_priv" +} + +setup_wg_route() +{ + local proto=$1 + local subnet=$2 + local subnet_bad=$3 + + atf_check -s exit:0 -o ignore rump.route add -$proto -net $subnet -link wg0 -iface + if [ -n "$subnet_bad" ]; then + atf_check -s exit:0 -o ignore rump.route add -$proto -net $subnet_bad -link wg0 -iface + fi +} + +prepare_file() +{ + local file=$1 + local data="0123456789" + + touch $file + for i in `seq 1 200` + do + echo $data >> $file + done +} + +test_tcp() +{ + local proto=$1 + local ip_peer=$2 + local _proto= + + prepare_file ./file_send + + if [ $proto = inet ]; then + _proto=ipv4 + else + _proto=ipv6 + fi + start_nc_server $SOCK_PEER 1234 ./file_recv $_proto + + export RUMP_SERVER=$SOCK_LOCAL + # Send a file to the server + # XXX Need a bit longer timeout value because the packet processing + # of the implementation is quite inefficient... + atf_check -s exit:0 $HIJACKING \ + nc -N -w 20 $ip_peer 1234 < ./file_send + $DEBUG && extract_new_packets $BUS > ./out + $DEBUG && cat ./out + stop_nc_server + $DEBUG && ls -s ./file_send ./file_recv + $DEBUG && wc -l ./file_send + $DEBUG && wc -l ./file_recv + $DEBUG && diff -u ./file_send ./file_recv + atf_check -s exit:0 diff -q ./file_send ./file_recv + rm -f ./out ./file_recv ./file_send +} + +wg_tunnel_common() +{ + local outer_proto=$1 + local inner_proto=$2 + local ifconfig="atf_check -s exit:0 rump.ifconfig" + local wgconfig="atf_check -s exit:0 $HIJACKING wgconfig" + local port=51820 + local ip_local= ip_peer= + local ip_wg_local= ip_wg_peer= + local outer_prefix= outer_prefixall= + local inner_prefix= inner_prefixall= + + if [ $outer_proto = inet ]; then + ip_tun_local_tun=192.168.10.1 + ip_tun_peer_tun=192.168.10.2 + outer_prefix=24 + outer_prefixall=32 + else + ip_tun_local_tun=fc00:10::1 + ip_tun_peer_tun=fc00:10::2 + outer_prefix=64 + outer_prefixall=128 + fi + + if [ $inner_proto = inet ]; then + ip_local=192.168.1.2 + ip_tun_local=192.168.1.1 + ip_wg_local=10.0.0.1 + ip_wg_peer=10.0.0.2 + ip_tun_peer=192.168.2.1 + ip_peer=192.168.2.2 + ip_peer_bad=192.168.3.2 + inner_prefix=24 + inner_prefixall=32 + subnet_local=192.168.1.0/24 + subnet_peer=192.168.2.0/24 + subnet_peer_bad=192.168.3.0/24 + else + ip_tun_local=fc00:1::1 + ip_local=fc00:1::2 + ip_wg_local=fd00::1 + ip_wg_peer=fd00::2 + ip_tun_peer=fc00:2::1 + ip_peer=fc00:2::2 + ip_peer_bad=fc00:3::2 + inner_prefix=64 + inner_prefixall=128 + subnet_local=fc00:1::/64 + subnet_peer=fc00:2::/64 + subnet_peer_bad=fc00:3::/64 + fi + + setup_servers + + # It sets key_priv_local key_pub_local key_priv_peer key_pub_peer + generate_keys + + export RUMP_SERVER=$SOCK_LOCAL + setup_edge $inner_proto $ip_local $inner_prefix $ip_tun_local + + export RUMP_SERVER=$SOCK_TUN_LOCAL + setup_router + $ifconfig shmif0 $inner_proto $ip_tun_local/$inner_prefix + $ifconfig shmif1 $outer_proto $ip_tun_local_tun/$outer_prefix + setup_wg $inner_proto $ip_wg_local $inner_prefix $port "$key_priv_local" + setup_wg_route $inner_proto $subnet_peer $subnet_peer_bad + + export RUMP_SERVER=$SOCK_TUN_PEER + setup_router + $ifconfig shmif0 $inner_proto $ip_tun_peer/$inner_prefix + $ifconfig shmif1 $outer_proto $ip_tun_peer_tun/$outer_prefix + setup_wg $inner_proto $ip_wg_peer $inner_prefix $port "$key_priv_peer" + setup_wg_route $inner_proto $subnet_local + + export RUMP_SERVER=$SOCK_PEER + setup_edge $inner_proto $ip_peer $inner_prefix $ip_tun_peer $ip_peer_bad + + export RUMP_SERVER=$SOCK_TUN_LOCAL + add_peer wg0 peer0 $key_pub_peer $ip_tun_peer_tun:$port \ + $ip_wg_peer/$inner_prefixall,$subnet_peer + + export RUMP_SERVER=$SOCK_TUN_PEER + add_peer wg0 peer0 $key_pub_local $ip_tun_local_tun:$port \ + $ip_wg_local/$inner_prefixall,$subnet_local + + export RUMP_SERVER=$SOCK_TUN_LOCAL + atf_check -s exit:0 -o match:"latest-handshake: \(never\)" \ + $HIJACKING wgconfig wg0 show peer peer0 + + export RUMP_SERVER=$SOCK_LOCAL + check_ping $inner_proto $ip_peer + + export RUMP_SERVER=$SOCK_TUN_LOCAL + atf_check -s exit:0 -o not-match:"latest-handshake: \(never\)" \ + $HIJACKING wgconfig wg0 show peer peer0 + + export RUMP_SERVER=$SOCK_LOCAL + # ping fails because the subnet of the IP is not allowed + check_ping_fail $inner_proto $ip_peer_bad + + # + # Test TCP stream over the tunnel + # + test_tcp $inner_proto $ip_peer + + export RUMP_SERVER=$SOCK_TUN_LOCAL + $ifconfig wg0 destroy + export RUMP_SERVER=$SOCK_TUN_PEER + $ifconfig wg0 destroy +} + +add_tunnel_test() +{ + local inner=$1 + local outer=$2 + local ipv4=inet + local ipv6=inet6 + + name="wg_tunnel_${inner}_over_${outer}" + fulldesc="Test wg(4) with ${inner} over ${outer}" + + eval inner=\$$inner + eval outer=\$$outer + + atf_test_case ${name} cleanup + eval " + ${name}_head() { + atf_set descr \"${fulldesc}\" + atf_set require.progs rump_server wgconfig wg-keygen + } + ${name}_body() { + wg_tunnel_common $outer $inner + rump_server_destroy_ifaces + } + ${name}_cleanup() { + \$DEBUG && dump + cleanup + }" + atf_add_test_case ${name} +} + +atf_init_test_cases() +{ + + add_tunnel_test ipv4 ipv4 + add_tunnel_test ipv4 ipv6 + add_tunnel_test ipv6 ipv4 + add_tunnel_test ipv6 ipv6 +} diff --git a/net/in_cksum/t_in_cksum.sh b/net/in_cksum/t_in_cksum.sh old mode 100755 new mode 100644 diff --git a/net/ipsec/Makefile b/net/ipsec/Makefile new file mode 100644 --- /dev/null +++ b/net/ipsec/Makefile @@ -0,0 +1,20 @@ +# $NetBSD: Makefile,v 1.10 2017/10/30 15:59:23 ozaki-r Exp $ +# + +.include + +TESTSDIR= ${TESTSBASE}/net/ipsec + +.for name in ipsec_ah_keys ipsec_esp_keys ipsec_gif ipsec_l2tp ipsec_misc \ + ipsec_natt ipsec_sockopt ipsec_sysctl ipsec_tcp ipsec_transport \ + ipsec_tunnel ipsec_tunnel_ipcomp ipsec_tunnel_odd +TESTS_SH+= t_${name} +TESTS_SH_SRC_t_${name}= ../net_common.sh ./common.sh ./algorithms.sh \ + t_${name}.sh +.endfor + +PROGS= natt_terminator +MAN.natt_terminator= # empty +BINDIR.natt_terminator= ${TESTSDIR} + +.include diff --git a/net/ipsec/algorithms.sh b/net/ipsec/algorithms.sh new file mode 100644 --- /dev/null +++ b/net/ipsec/algorithms.sh @@ -0,0 +1,193 @@ +# $NetBSD: algorithms.sh,v 1.6 2017/10/27 04:31:50 ozaki-r Exp $ +# +# Copyright (c) 2017 Internet Initiative Japan Inc. +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +ESP_ENCRYPTION_ALGORITHMS="des-cbc 3des-cbc null blowfish-cbc cast128-cbc \ + des-deriv rijndael-cbc aes-ctr camellia-cbc aes-gcm-16 aes-gmac" +ESP_ENCRYPTION_ALGORITHMS_MINIMUM="null rijndael-cbc" + +# Valid key lengths of ESP encription algorithms +# des-cbc 64 +# 3des-cbc 192 +# null 0 to 2048 XXX only accept 0 length +# blowfish-cbc 40 to 448 +# cast128-cbc 40 to 128 +# des-deriv 64 +# 3des-deriv 192 XXX not implemented +# rijndael-cbc 128/192/256 +# twofish-cbc 0 to 256 XXX not supported +# aes-ctr 160/224/288 +# camellia-cbc 128/192/256 +# aes-gcm-16 160/224/288 +# aes-gmac 160/224/288 +valid_keys_descbc="64" +invalid_keys_descbc="56 72" +valid_keys_3descbc="192" +invalid_keys_3descbc="184 200" +#valid_keys_null="0 2048" +valid_keys_null="0" +invalid_keys_null="8" +valid_keys_blowfishcbc="40 448" +invalid_keys_blowfishcbc="32 456" +valid_keys_cast128cbc="40 128" +invalid_keys_cast128cbc="32 136" +valid_keys_desderiv="64" +invalid_keys_desderiv="56 72" +#valid_keys_3desderiv="192" +#invalid_keys_3desderiv="184 200" +valid_keys_rijndaelcbc="128 192 256" +invalid_keys_rijndaelcbc="120 136 184 200 248 264" +#valid_keys_twofishcbc="0 256" +#invalid_keys_twofishcbc="264" +valid_keys_aesctr="160 224 288" +invalid_keys_aesctr="152 168 216 232 280 296" +valid_keys_camelliacbc="128 192 256" +invalid_keys_camelliacbc="120 136 184 200 248 264" +valid_keys_aesgcm16="160 224 288" +invalid_keys_aesgcm16="152 168 216 232 280 296" +valid_keys_aesgmac="160 224 288" +invalid_keys_aesgmac="152 168 216 232 280 296" + +AH_AUTHENTICATION_ALGORITHMS="hmac-md5 hmac-sha1 keyed-md5 keyed-sha1 null \ + hmac-sha256 hmac-sha384 hmac-sha512 hmac-ripemd160 aes-xcbc-mac" +AH_AUTHENTICATION_ALGORITHMS_MINIMUM="null hmac-sha512" + +# Valid key lengths of AH authentication algorithms +# hmac-md5 128 +# hmac-sha1 160 +# keyed-md5 128 +# keyed-sha1 160 +# null 0 to 2048 +# hmac-sha256 256 +# hmac-sha384 384 +# hmac-sha512 512 +# hmac-ripemd160 160 +# aes-xcbc-mac 128 +# tcp-md5 8 to 640 XXX not enabled in rump kernels +valid_keys_hmacmd5="128" +invalid_keys_hmacmd5="120 136" +valid_keys_hmacsha1="160" +invalid_keys_hmacsha1="152 168" +valid_keys_keyedmd5="128" +invalid_keys_keyedmd5="120 136" +valid_keys_keyedsha1="160" +invalid_keys_keyedsha1="152 168" +#valid_keys_null="0 2048" +valid_keys_null="0" +invalid_keys_null="8" +valid_keys_hmacsha256="256" +invalid_keys_hmacsha256="248 264" +valid_keys_hmacsha384="384" +invalid_keys_hmacsha384="376 392" +valid_keys_hmacsha512="512" +invalid_keys_hmacsha512="504 520" +valid_keys_hmacripemd160="160" +invalid_keys_hmacripemd160="152 168" +valid_keys_aesxcbcmac="128" +invalid_keys_aesxcbcmac="120 136" +#valid_keys_tcpmd5="8 640" +#invalid_keys_tcpmd5="648" + +IPCOMP_COMPRESSION_ALGORITHMS="deflate" +IPCOMP_COMPRESSION_ALGORITHMS_MINIMUM="deflate" +valid_keys_deflate="0" +invalid_keys_deflate="8" +minlen_deflate="90" + +get_one_valid_keylen() +{ + local algo=$1 + local _algo=$(echo $algo | sed 's/-//g') + local len= + local keylengths= + + eval keylengths="\$valid_keys_${_algo}" + + for len in $(echo $keylengths); do + break; + done + + echo $len +} + +get_valid_keylengths() +{ + local algo=$1 + local _algo=$(echo $algo | sed 's/-//g') + + eval keylengths="\$valid_keys_${_algo}" + echo $keylengths +} + +get_invalid_keylengths() +{ + local algo=$1 + local _algo=$(echo $algo | sed 's/-//g') + + eval keylengths="\$invalid_keys_${_algo}" + echo $keylengths +} + +generate_key() +{ + local keylen=$(($1 / 8)) + local key= + + while [ $keylen -gt 0 ]; do + key="${key}a" + keylen=$((keylen - 1)) + done + if [ ! -z "$key" ]; then + key="\"$key\"" + fi + + echo $key +} + +generate_algo_args() +{ + local proto=$1 + local algo=$2 + local keylen=$(get_one_valid_keylen $algo) + local key=$(generate_key $keylen) + + if [ $proto = esp -o $proto = "esp-udp" ]; then + echo "-E $algo $key" + elif [ $proto = ah ]; then + echo "-A $algo $key" + else + echo "-C $algo $key" + fi +} + +get_minlen() +{ + local algo=$1 + local minlen= + + eval minlen="\$minlen_${algo}" + echo $minlen +} diff --git a/net/ipsec/common.sh b/net/ipsec/common.sh new file mode 100644 --- /dev/null +++ b/net/ipsec/common.sh @@ -0,0 +1,98 @@ +# $NetBSD: common.sh,v 1.8 2020/06/05 03:24:58 knakahara Exp $ +# +# Copyright (c) 2017 Internet Initiative Japan Inc. +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +HIJACKING_NPF="${HIJACKING},blanket=/dev/npf" + +test_flush_entries() +{ + local sock=$1 + + export RUMP_SERVER=$sock + + atf_check -s exit:0 -o empty $HIJACKING setkey -F + atf_check -s exit:0 -o empty $HIJACKING setkey -F -P + atf_check -s exit:0 -o match:"No SAD entries." $HIJACKING setkey -D -a + atf_check -s exit:0 -o match:"No SPD entries." $HIJACKING setkey -D -P +} + +check_sa_entries() +{ + local sock=$1 + local local_addr=$2 + local remote_addr=$3 + + export RUMP_SERVER=$sock + + $DEBUG && $HIJACKING setkey -D + + atf_check -s exit:0 -o match:"$local_addr $remote_addr" \ + $HIJACKING setkey -D + atf_check -s exit:0 -o match:"$remote_addr $local_addr" \ + $HIJACKING setkey -D + # TODO: more detail checks +} + +check_sp_entries() +{ + local sock=$1 + local local_addr=$2 + local remote_addr=$3 + + export RUMP_SERVER=$sock + + $DEBUG && $HIJACKING setkey -D -P + + atf_check -s exit:0 \ + -o match:"$local_addr\[any\] $remote_addr\[any\] 255\(reserved\)" \ + $HIJACKING setkey -D -P + atf_check -s exit:0 \ + -o match:"$remote_addr\[any\] $local_addr\[any\] 255\(reserved\)" \ + $HIJACKING setkey -D -P + # TODO: more detail checks +} + +generate_pktproto() +{ + local proto=$1 + + if [ $proto = ipcomp ]; then + echo IPComp + else + echo $proto | tr 'a-z' 'A-Z' + fi +} + +get_natt_port() +{ + local local_addr=$1 + local remote_addr=$2 + local port="" + + # 10.0.1.2:4500 20.0.0.2:4500 shmif1 20.0.0.1:35574 + port=$($HIJACKING_NPF npfctl list | grep $local_addr | awk -F "${remote_addr}:" '/4500/ {print $2;}') + echo $port +} diff --git a/net/ipsec/natt_terminator.c b/net/ipsec/natt_terminator.c new file mode 100644 --- /dev/null +++ b/net/ipsec/natt_terminator.c @@ -0,0 +1,133 @@ +/* $NetBSD: natt_terminator.c,v 1.2 2018/11/22 04:51:41 knakahara Exp $ */ + +/*- + * Copyright (c) 2017 Internet Initiative Japan Inc. + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +static void +usage(void) +{ + const char *prog = "natt_terminator"; + + fprintf(stderr, "Usage: %s [-46] \n", prog); +} + +int +main(int argc, char **argv) +{ + struct addrinfo hints; + struct addrinfo *res; + int s, e; + const char *addr, *port; + int option; + int c, family = AF_INET; + + while ((c = getopt(argc, argv, "46")) != -1) { + switch (c) { + case '4': + family = AF_INET; + break; + case '6': + family = AF_INET6; + break; + default: + usage(); + return 1; + } + } + argc -= optind; + argv += optind; + + if (argc != 2) { + usage(); + return 1; + } + + addr = argv[0]; + port = argv[1]; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = family; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = IPPROTO_UDP; + hints.ai_flags = 0; + + e = getaddrinfo(addr, port, &hints, &res); + if (e != 0) + errx(EXIT_FAILURE, "getaddrinfo failed: %s", gai_strerror(e)); + + s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (s == -1) + err(EXIT_FAILURE, "socket"); + + /* + * Set the option to tell the kernel that the socket can handle + * UDP-encapsulated ESP packets for NAT-T. + */ + option = UDP_ENCAP_ESPINUDP; + e = setsockopt(s, IPPROTO_UDP, UDP_ENCAP, &option, sizeof(option)); + if (e == -1) + err(EXIT_FAILURE, "setsockopt(UDP_ENCAP)"); + + e = bind(s, res->ai_addr, res->ai_addrlen); + if (e == -1) + err(EXIT_FAILURE, "bind"); + + /* Receiving a packet make the NAPT create a mapping. */ + { + char buf[64]; + struct sockaddr_storage z; + socklen_t len = sizeof(z); + + e = recvfrom(s, buf, 64, MSG_PEEK, + (struct sockaddr *)&z, &len); + if (e == -1) + err(EXIT_FAILURE, "recvfrom"); + } + + /* + * Keep the socket in the kernel to handle UDP-encapsulated ESP packets. + */ + pause(); + + close(s); + + return 0; +} diff --git a/net/ipsec/t_ipsec_ah_keys.sh b/net/ipsec/t_ipsec_ah_keys.sh new file mode 100644 --- /dev/null +++ b/net/ipsec/t_ipsec_ah_keys.sh @@ -0,0 +1,159 @@ +# $NetBSD: t_ipsec_ah_keys.sh,v 1.2 2017/08/03 03:16:27 ozaki-r Exp $ +# +# Copyright (c) 2017 Internet Initiative Japan Inc. +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +SOCK_LOCAL=unix://ipsec_local + +DEBUG=${DEBUG:-false} + +test_ah_valid_keys_common() +{ + local aalgo=$1 + local key= + local tmpfile=./tmp + local len= + + rump_server_crypto_start $SOCK_LOCAL netipsec + + export RUMP_SERVER=$SOCK_LOCAL + + for len in $(get_valid_keylengths $aalgo); do + key=$(generate_key $len) + cat > $tmpfile <<-EOF + add 10.0.0.1 10.0.0.2 ah 10000 -A $aalgo $key; + EOF + $DEBUG && cat $tmpfile + atf_check -s exit:0 -o empty $HIJACKING setkey -c < $tmpfile + atf_check -s exit:0 -o match:'10.0.0.1 10.0.0.2' \ + $HIJACKING setkey -D + # TODO: more detail checks + + cat > $tmpfile <<-EOF + delete 10.0.0.1 10.0.0.2 ah 10000; + EOF + $DEBUG && cat $tmpfile + atf_check -s exit:0 -o empty $HIJACKING setkey -c < $tmpfile + atf_check -s exit:0 -o match:'No SAD entries.' \ + $HIJACKING setkey -D + done + + rm -f $tmpfile +} + +add_test_valid_keys() +{ + local aalgo=$1 + local _aalgo=$(echo $aalgo | sed 's/-//g') + local name= desc= + + name="ipsec_ah_${_aalgo}_valid_keys" + desc="Tests AH ($aalgo) valid keys" + + atf_test_case ${name} cleanup + eval " + ${name}_head() { + atf_set descr \"$desc\" + atf_set require.progs rump_server setkey + } + ${name}_body() { + test_ah_valid_keys_common $aalgo + } + ${name}_cleanup() { + \$DEBUG && dump + cleanup + } + " + atf_add_test_case ${name} +} + +test_ah_invalid_keys_common() +{ + local aalgo=$1 + local key= + local tmpfile=./tmp + local len= + + rump_server_crypto_start $SOCK_LOCAL netipsec + + export RUMP_SERVER=$SOCK_LOCAL + + for len in $(get_invalid_keylengths $aalgo); do + key=$(generate_key $len) + cat > $tmpfile <<-EOF + add 10.0.0.1 10.0.0.2 ah 10000 -A $aalgo $key; + EOF + $DEBUG && cat $tmpfile + if [ $aalgo = null ]; then + # null doesn't accept any keys + atf_check -s exit:0 \ + -o match:'syntax error' -e ignore \ + $HIJACKING setkey -c < $tmpfile + else + atf_check -s exit:0 \ + -o match:'Invalid (key length|argument)' -e ignore \ + $HIJACKING setkey -c < $tmpfile + fi + atf_check -s exit:0 -o match:'No SAD entries.' \ + $HIJACKING setkey -D + done + + rm -f $tmpfile +} + +add_test_invalid_keys() +{ + local aalgo=$1 + local _aalgo=$(echo $aalgo | sed 's/-//g') + local name= desc= + + name="ipsec_ah_${_aalgo}_invalid_keys" + desc="Tests AH ($aalgo) invalid keys" + + atf_test_case ${name} cleanup + eval " \ + ${name}_head() { \ + atf_set \"descr\" \"$desc\"; \ + atf_set \"require.progs\" \"rump_server\" \"setkey\"; \ + }; \ + ${name}_body() { \ + test_ah_invalid_keys_common $aalgo; \ + }; \ + ${name}_cleanup() { \ + $DEBUG && dump; \ + cleanup; \ + } \ + " + atf_add_test_case ${name} +} + +atf_init_test_cases() +{ + + for aalgo in $AH_AUTHENTICATION_ALGORITHMS; do + add_test_valid_keys $aalgo + add_test_invalid_keys $aalgo + done +} diff --git a/net/ipsec/t_ipsec_esp_keys.sh b/net/ipsec/t_ipsec_esp_keys.sh new file mode 100644 --- /dev/null +++ b/net/ipsec/t_ipsec_esp_keys.sh @@ -0,0 +1,159 @@ +# $NetBSD: t_ipsec_esp_keys.sh,v 1.2 2017/08/03 03:16:27 ozaki-r Exp $ +# +# Copyright (c) 2017 Internet Initiative Japan Inc. +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +SOCK_LOCAL=unix://ipsec_local + +DEBUG=${DEBUG:-false} + +test_esp_valid_keys_common() +{ + local ealgo=$1 + local key= + local tmpfile=./tmp + local len= + + rump_server_crypto_start $SOCK_LOCAL netipsec + + export RUMP_SERVER=$SOCK_LOCAL + + for len in $(get_valid_keylengths $ealgo); do + key=$(generate_key $len) + cat > $tmpfile <<-EOF + add 10.0.0.1 10.0.0.2 esp 10000 -E $ealgo $key; + EOF + $DEBUG && cat $tmpfile + atf_check -s exit:0 -o empty $HIJACKING setkey -c < $tmpfile + atf_check -s exit:0 -o match:'10.0.0.1 10.0.0.2' \ + $HIJACKING setkey -D + # TODO: more detail checks + + cat > $tmpfile <<-EOF + delete 10.0.0.1 10.0.0.2 esp 10000; + EOF + $DEBUG && cat $tmpfile + atf_check -s exit:0 -o empty $HIJACKING setkey -c < $tmpfile + atf_check -s exit:0 -o match:'No SAD entries.' \ + $HIJACKING setkey -D + done + + rm -f $tmpfile +} + +add_test_valid_keys() +{ + local ealgo=$1 + local _ealgo=$(echo $ealgo | sed 's/-//g') + local name= desc= + + name="ipsec_esp_${_ealgo}_valid_keys" + desc="Tests ESP ($ealgo) valid keys" + + atf_test_case ${name} cleanup + eval " + ${name}_head() { + atf_set descr \"$desc\" + atf_set require.progs rump_server setkey + } + ${name}_body() { + test_esp_valid_keys_common $ealgo + } + ${name}_cleanup() { + \$DEBUG && dump + cleanup + } + " + atf_add_test_case ${name} +} + +test_esp_invalid_keys_common() +{ + local ealgo=$1 + local key= + local tmpfile=./tmp + local len= + + rump_server_crypto_start $SOCK_LOCAL netipsec + + export RUMP_SERVER=$SOCK_LOCAL + + for len in $(get_invalid_keylengths $ealgo); do + key=$(generate_key $len) + cat > $tmpfile <<-EOF + add 10.0.0.1 10.0.0.2 esp 10000 -E $ealgo $key; + EOF + $DEBUG && cat $tmpfile + if [ $ealgo = null ]; then + # null doesn't accept any keys + atf_check -s exit:0 \ + -o match:'syntax error' -e ignore \ + $HIJACKING setkey -c < $tmpfile + else + atf_check -s exit:0 \ + -o match:'Invalid (key length|argument)' -e ignore \ + $HIJACKING setkey -c < $tmpfile + fi + atf_check -s exit:0 -o match:'No SAD entries.' \ + $HIJACKING setkey -D + done + + rm -f $tmpfile +} + +add_test_invalid_keys() +{ + local ealgo=$1 + local _ealgo=$(echo $ealgo | sed 's/-//g') + local name= desc= + + name="ipsec_esp_${_ealgo}_invalid_keys" + desc="Tests ESP ($ealgo) invalid keys" + + atf_test_case ${name} cleanup + eval " \ + ${name}_head() { \ + atf_set \"descr\" \"$desc\"; \ + atf_set \"require.progs\" \"rump_server\" \"setkey\"; \ + }; \ + ${name}_body() { \ + test_esp_invalid_keys_common $ealgo; \ + }; \ + ${name}_cleanup() { \ + $DEBUG && dump; \ + cleanup; \ + } \ + " + atf_add_test_case ${name} +} + +atf_init_test_cases() +{ + + for ealgo in $ESP_ENCRYPTION_ALGORITHMS; do + add_test_valid_keys $ealgo + add_test_invalid_keys $ealgo + done +} diff --git a/net/ipsec/t_ipsec_gif.sh b/net/ipsec/t_ipsec_gif.sh new file mode 100644 --- /dev/null +++ b/net/ipsec/t_ipsec_gif.sh @@ -0,0 +1,436 @@ +# $NetBSD: t_ipsec_gif.sh,v 1.9 2020/02/17 08:46:10 ozaki-r Exp $ +# +# Copyright (c) 2017 Internet Initiative Japan Inc. +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +SOCK_LOCAL=unix://ipsec_gif_local +SOCK_TUN_LOCAL=unix://ipsec_gif_tunel_local +SOCK_TUN_REMOTE=unix://ipsec_gif_tunnel_remote +SOCK_REMOTE=unix://ipsec_gif_remote +BUS_LOCAL=./bus_ipsec_local +BUS_TUNNEL=./bus_ipsec_tunnel +BUS_REMOTE=./bus_ipsec_remote + +DEBUG=${DEBUG:-true} + +make_gif_pktstr() +{ + local src=$1 + local dst=$2 + local src_inner=$3 + local dst_inner=$4 + local proto=$5 + local ipproto=$6 + local proto_cap= inner_str= + + if [ $proto = esp ]; then + proto_cap=ESP + else + proto_cap=AH + if [ $ipproto = ipv4 ]; then + inner_str="$src_inner > $dst_inner:.+\(ipip-proto-4\)" + else + inner_str="$src_inner > $dst_inner" + fi + fi + + echo "$src > $dst: $proto_cap.+$inner_str" +} + +wait_for_all_dad_completions() +{ + + for sock in $SOCK_LOCAL $SOCK_TUN_LOCAL $SOCK_TUN_REMOTE $SOCK_REMOTE; do + export RUMP_SERVER=$sock + atf_check -s exit:0 rump.ifconfig -w 10 + done +} + +test_ipsec4_gif() +{ + local mode=$1 + local proto=$2 + local algo=$3 + local ip_local=10.0.1.2 + local ip_gw_local=10.0.1.1 + local ip_gwlo_tun=20.0.0.1 + local ip_gwlo_gif=20.1.0.1 + local ip_gwre_gif=20.1.0.2 + local ip_gwre_tun=20.0.0.2 + local ip_gw_remote=10.0.2.1 + local ip_remote=10.0.2.2 + local subnet_local=10.0.1.0 + local subnet_remote=10.0.2.0 + local tmpfile=./tmp + local outfile=./out + local str= + local algo_args="$(generate_algo_args $proto $algo)" + + rump_server_crypto_start $SOCK_LOCAL + rump_server_crypto_start $SOCK_TUN_LOCAL netipsec gif + rump_server_crypto_start $SOCK_TUN_REMOTE netipsec gif + rump_server_crypto_start $SOCK_REMOTE + rump_server_add_iface $SOCK_LOCAL shmif0 $BUS_LOCAL + rump_server_add_iface $SOCK_TUN_LOCAL shmif0 $BUS_LOCAL + rump_server_add_iface $SOCK_TUN_LOCAL shmif1 $BUS_TUNNEL + rump_server_add_iface $SOCK_TUN_REMOTE shmif0 $BUS_REMOTE + rump_server_add_iface $SOCK_TUN_REMOTE shmif1 $BUS_TUNNEL + rump_server_add_iface $SOCK_REMOTE shmif0 $BUS_REMOTE + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 rump.ifconfig shmif0 $ip_local/24 + atf_check -s exit:0 -o ignore \ + rump.route -n add -net $subnet_remote $ip_gw_local + + export RUMP_SERVER=$SOCK_TUN_LOCAL + atf_check -s exit:0 rump.ifconfig shmif0 $ip_gw_local/24 + atf_check -s exit:0 rump.ifconfig shmif1 $ip_gwlo_tun/24 + rump_server_add_iface $SOCK_TUN_LOCAL gif0 + atf_check -s exit:0 rump.ifconfig gif0 \ + tunnel $ip_gwlo_tun $ip_gwre_tun + atf_check -s exit:0 rump.ifconfig gif0 \ + inet $ip_gwlo_gif/32 $ip_gwre_gif + atf_check -s exit:0 rump.sysctl -q -w net.inet.ip.forwarding=1 + atf_check -s exit:0 -o ignore \ + rump.route -n add -net $subnet_remote $ip_gwre_gif + + export RUMP_SERVER=$SOCK_TUN_REMOTE + atf_check -s exit:0 rump.ifconfig shmif0 $ip_gw_remote/24 + atf_check -s exit:0 rump.ifconfig shmif1 $ip_gwre_tun/24 + rump_server_add_iface $SOCK_TUN_REMOTE gif0 + atf_check -s exit:0 rump.ifconfig gif0 \ + tunnel $ip_gwre_tun $ip_gwlo_tun + atf_check -s exit:0 rump.ifconfig gif0 \ + inet $ip_gwre_gif/32 $ip_gwlo_gif + atf_check -s exit:0 rump.sysctl -q -w net.inet.ip.forwarding=1 + atf_check -s exit:0 -o ignore \ + rump.route -n add -net $subnet_local $ip_gwlo_gif + + export RUMP_SERVER=$SOCK_REMOTE + atf_check -s exit:0 rump.ifconfig shmif0 $ip_remote/24 + + wait_for_all_dad_completions + + atf_check -s exit:0 -o ignore \ + rump.route -n add -net $subnet_local $ip_gw_remote + + extract_new_packets $BUS_TUNNEL > $outfile + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 -o ignore rump.ping -c 1 -n -w 3 $ip_remote + + extract_new_packets $BUS_TUNNEL > $outfile + str="$ip_gwlo_tun > $ip_gwre_tun:" + str="$str $ip_local > $ip_remote: ICMP echo request," + str="$str .+ \(ipip-proto-4\)" + atf_check -s exit:0 -o match:"$str" cat $outfile + str="$ip_gwre_tun > $ip_gwlo_tun:" + str="$str $ip_remote > $ip_local: ICMP echo reply," + str="$str .+ \(ipip-proto-4\)" + atf_check -s exit:0 -o match:"$str" cat $outfile + + if [ $mode = tunnel ]; then + export RUMP_SERVER=$SOCK_TUN_LOCAL + # from https://www.netbsd.org/docs/network/ipsec/ + cat > $tmpfile <<-EOF + add $ip_gwlo_tun $ip_gwre_tun $proto 10000 $algo_args; + add $ip_gwre_tun $ip_gwlo_tun $proto 10001 $algo_args; + spdadd $subnet_local/24 $subnet_remote/24 any -P out ipsec + $proto/tunnel/$ip_gwlo_tun-$ip_gwre_tun/require; + spdadd $subnet_remote/24 $subnet_local/24 any -P in ipsec + $proto/tunnel/$ip_gwre_tun-$ip_gwlo_tun/require; + EOF + $DEBUG && cat $tmpfile + atf_check -s exit:0 -o empty $HIJACKING setkey -c < $tmpfile + + export RUMP_SERVER=$SOCK_TUN_REMOTE + cat > $tmpfile <<-EOF + add $ip_gwlo_tun $ip_gwre_tun $proto 10000 $algo_args; + add $ip_gwre_tun $ip_gwlo_tun $proto 10001 $algo_args; + spdadd $subnet_remote/24 $subnet_local/24 any -P out ipsec + $proto/tunnel/$ip_gwre_tun-$ip_gwlo_tun/require; + spdadd $subnet_local/24 $subnet_remote/24 any -P in ipsec + $proto/tunnel/$ip_gwlo_tun-$ip_gwre_tun/require; + EOF + $DEBUG && cat $tmpfile + atf_check -s exit:0 -o empty $HIJACKING setkey -c < $tmpfile + else # transport mode + export RUMP_SERVER=$SOCK_TUN_LOCAL + # from https://www.netbsd.org/docs/network/ipsec/ + cat > $tmpfile <<-EOF + add $ip_gwlo_tun $ip_gwre_tun $proto 10000 $algo_args; + add $ip_gwre_tun $ip_gwlo_tun $proto 10001 $algo_args; + spdadd $ip_gwlo_tun/32 $ip_gwre_tun/32 any -P out ipsec + $proto/transport//require; + spdadd $ip_gwre_tun/32 $ip_gwlo_tun/32 any -P in ipsec + $proto/transport//require; + EOF + $DEBUG && cat $tmpfile + atf_check -s exit:0 -o empty $HIJACKING setkey -c < $tmpfile + + export RUMP_SERVER=$SOCK_TUN_REMOTE + cat > $tmpfile <<-EOF + add $ip_gwlo_tun $ip_gwre_tun $proto 10000 $algo_args; + add $ip_gwre_tun $ip_gwlo_tun $proto 10001 $algo_args; + spdadd $ip_gwre_tun/32 $ip_gwlo_tun/32 any -P out ipsec + $proto/transport//require; + spdadd $ip_gwlo_tun/32 $ip_gwre_tun/32 any -P in ipsec + $proto/transport//require; + EOF + $DEBUG && cat $tmpfile + atf_check -s exit:0 -o empty $HIJACKING setkey -c < $tmpfile + fi + + check_sa_entries $SOCK_TUN_LOCAL $ip_gwlo_tun $ip_gwre_tun + check_sa_entries $SOCK_TUN_REMOTE $ip_gwlo_tun $ip_gwre_tun + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 -o ignore rump.ping -c 1 -n -w 3 $ip_remote + + extract_new_packets $BUS_TUNNEL > $outfile + str=$(make_gif_pktstr $ip_gwlo_tun $ip_gwre_tun \ + $ip_local $ip_remote $proto ipv4) + atf_check -s exit:0 -o match:"$str" cat $outfile + str=$(make_gif_pktstr $ip_gwre_tun $ip_gwlo_tun \ + $ip_remote $ip_local $proto ipv4) + atf_check -s exit:0 -o match:"$str" cat $outfile + + test_flush_entries $SOCK_TUN_LOCAL + test_flush_entries $SOCK_TUN_REMOTE +} + +test_ipsec6_gif() +{ + local mode=$1 + local proto=$2 + local algo=$3 + local ip_local=fd00:1::2 + local ip_gw_local=fd00:1::1 + local ip_gwlo_tun=fc00::1 + local ip_gwlo_gif=fc01::1 + local ip_gwre_gif=fc01::2 + local ip_gwre_tun=fc00::2 + local ip_gw_remote=fd00:2::1 + local ip_remote=fd00:2::2 + local subnet_local=fd00:1:: + local subnet_remote=fd00:2:: + local tmpfile=./tmp + local outfile=./out + local str= + local algo_args="$(generate_algo_args $proto $algo)" + + rump_server_crypto_start $SOCK_LOCAL netinet6 + rump_server_crypto_start $SOCK_TUN_LOCAL netipsec netinet6 gif + rump_server_crypto_start $SOCK_TUN_REMOTE netipsec netinet6 gif + rump_server_crypto_start $SOCK_REMOTE netinet6 + rump_server_add_iface $SOCK_LOCAL shmif0 $BUS_LOCAL + rump_server_add_iface $SOCK_TUN_LOCAL shmif0 $BUS_LOCAL + rump_server_add_iface $SOCK_TUN_LOCAL shmif1 $BUS_TUNNEL + rump_server_add_iface $SOCK_TUN_REMOTE shmif0 $BUS_REMOTE + rump_server_add_iface $SOCK_TUN_REMOTE shmif1 $BUS_TUNNEL + rump_server_add_iface $SOCK_REMOTE shmif0 $BUS_REMOTE + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 rump.ifconfig shmif0 inet6 $ip_local/64 + atf_check -s exit:0 -o ignore \ + rump.route -n add -inet6 -net $subnet_remote/64 $ip_gw_local + + export RUMP_SERVER=$SOCK_TUN_LOCAL + atf_check -s exit:0 rump.ifconfig shmif0 inet6 $ip_gw_local/64 + atf_check -s exit:0 rump.ifconfig shmif1 inet6 $ip_gwlo_tun/64 + rump_server_add_iface $SOCK_TUN_LOCAL gif0 + atf_check -s exit:0 rump.ifconfig gif0 \ + tunnel $ip_gwlo_tun $ip_gwre_tun + atf_check -s exit:0 rump.ifconfig gif0 \ + inet6 $ip_gwlo_gif/128 $ip_gwre_gif + atf_check -s exit:0 rump.sysctl -q -w net.inet6.ip6.forwarding=1 + atf_check -s exit:0 -o ignore \ + rump.route -n add -inet6 -net $subnet_remote/64 $ip_gwlo_gif + + export RUMP_SERVER=$SOCK_TUN_REMOTE + atf_check -s exit:0 rump.ifconfig shmif0 inet6 $ip_gw_remote/64 + atf_check -s exit:0 rump.ifconfig shmif1 inet6 $ip_gwre_tun/64 + rump_server_add_iface $SOCK_TUN_REMOTE gif0 + atf_check -s exit:0 rump.ifconfig gif0 \ + tunnel $ip_gwre_tun $ip_gwlo_tun + atf_check -s exit:0 rump.ifconfig gif0 \ + inet6 $ip_gwre_gif/128 $ip_gwlo_gif + atf_check -s exit:0 rump.sysctl -q -w net.inet6.ip6.forwarding=1 + atf_check -s exit:0 -o ignore \ + rump.route -n add -inet6 -net $subnet_local/64 $ip_gwre_gif + + export RUMP_SERVER=$SOCK_REMOTE + atf_check -s exit:0 rump.ifconfig shmif0 inet6 $ip_remote + + wait_for_all_dad_completions + + atf_check -s exit:0 -o ignore \ + rump.route -n add -inet6 -net $subnet_local/64 $ip_gw_remote + + extract_new_packets $BUS_TUNNEL > $outfile + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 -o ignore rump.ping6 -c 1 -n -X 3 $ip_remote + + extract_new_packets $BUS_TUNNEL > $outfile + str="$ip_gwlo_tun > $ip_gwre_tun:" + str="$str $ip_local > $ip_remote: ICMP6, echo request" + atf_check -s exit:0 -o match:"$str" cat $outfile + str="$ip_gwre_tun > $ip_gwlo_tun:" + str="$str $ip_remote > $ip_local: ICMP6, echo reply," + atf_check -s exit:0 -o match:"$str" cat $outfile + + if [ $mode = tunnel ]; then + export RUMP_SERVER=$SOCK_TUN_LOCAL + # from https://www.netbsd.org/docs/network/ipsec/ + cat > $tmpfile <<-EOF + add $ip_gwlo_tun $ip_gwre_tun $proto 10000 $algo_args; + add $ip_gwre_tun $ip_gwlo_tun $proto 10001 $algo_args; + spdadd $subnet_local/64 $subnet_remote/64 any -P out ipsec + $proto/tunnel/$ip_gwlo_tun-$ip_gwre_tun/require; + spdadd $subnet_remote/64 $subnet_local/64 any -P in ipsec + $proto/tunnel/$ip_gwre_tun-$ip_gwlo_tun/require; + EOF + $DEBUG && cat $tmpfile + atf_check -s exit:0 -o empty $HIJACKING setkey -c < $tmpfile + + export RUMP_SERVER=$SOCK_TUN_REMOTE + cat > $tmpfile <<-EOF + add $ip_gwlo_tun $ip_gwre_tun $proto 10000 $algo_args; + add $ip_gwre_tun $ip_gwlo_tun $proto 10001 $algo_args; + spdadd $subnet_remote/64 $subnet_local/64 any -P out ipsec + $proto/tunnel/$ip_gwre_tun-$ip_gwlo_tun/require; + spdadd $subnet_local/64 $subnet_remote/64 any -P in ipsec + $proto/tunnel/$ip_gwlo_tun-$ip_gwre_tun/require; + EOF + $DEBUG && cat $tmpfile + atf_check -s exit:0 -o empty $HIJACKING setkey -c < $tmpfile + else # transport mode + export RUMP_SERVER=$SOCK_TUN_LOCAL + # from https://www.netbsd.org/docs/network/ipsec/ + cat > $tmpfile <<-EOF + add $ip_gwlo_tun $ip_gwre_tun $proto 10000 $algo_args; + add $ip_gwre_tun $ip_gwlo_tun $proto 10001 $algo_args; + spdadd $ip_gwlo_tun/128 $ip_gwre_tun/128 any -P out ipsec + $proto/transport//require; + spdadd $ip_gwre_tun/128 $ip_gwlo_tun/128 any -P in ipsec + $proto/transport//require; + EOF + $DEBUG && cat $tmpfile + atf_check -s exit:0 -o empty $HIJACKING setkey -c < $tmpfile + + export RUMP_SERVER=$SOCK_TUN_REMOTE + cat > $tmpfile <<-EOF + add $ip_gwlo_tun $ip_gwre_tun $proto 10000 $algo_args; + add $ip_gwre_tun $ip_gwlo_tun $proto 10001 $algo_args; + spdadd $ip_gwre_tun/128 $ip_gwlo_tun/128 any -P out ipsec + $proto/transport//require; + spdadd $ip_gwlo_tun/128 $ip_gwre_tun/128 any -P in ipsec + $proto/transport//require; + EOF + $DEBUG && cat $tmpfile + atf_check -s exit:0 -o empty $HIJACKING setkey -c < $tmpfile + fi + + check_sa_entries $SOCK_TUN_LOCAL $ip_gwlo_tun $ip_gwre_tun + check_sa_entries $SOCK_TUN_REMOTE $ip_gwlo_tun $ip_gwre_tun + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 -o ignore rump.ping6 -c 1 -n -X 3 $ip_remote + + extract_new_packets $BUS_TUNNEL > $outfile + str=$(make_gif_pktstr $ip_gwlo_tun $ip_gwre_tun \ + $ip_local $ip_remote $proto ipv6) + atf_check -s exit:0 -o match:"$str" cat $outfile + str=$(make_gif_pktstr $ip_gwre_tun $ip_gwlo_tun \ + $ip_remote $ip_local $proto ipv6) + atf_check -s exit:0 -o match:"$str" cat $outfile + + test_flush_entries $SOCK_TUN_LOCAL + test_flush_entries $SOCK_TUN_REMOTE +} + +test_ipsec_gif_common() +{ + local ipproto=$1 + local mode=$2 + local proto=$3 + local algo=$4 + + if [ $ipproto = ipv4 ]; then + test_ipsec4_gif $mode $proto $algo + else + test_ipsec6_gif $mode $proto $algo + fi +} + +add_test_ipsec_gif() +{ + local ipproto=$1 + local mode=$2 + local proto=$3 + local algo=$4 + local _algo=$(echo $algo | sed 's/-//g') + local name= desc= + + name="ipsec_gif_${ipproto}_${mode}_${proto}_${_algo}" + desc="Tests of gif/IPsec ($ipproto) ${mode} mode with $proto ($algo)" + + atf_test_case ${name} cleanup + eval " + ${name}_head() { + atf_set descr \"$desc\" + atf_set require.progs rump_server setkey + } + ${name}_body() { + test_ipsec_gif_common $ipproto $mode $proto $algo + rump_server_destroy_ifaces + } + ${name}_cleanup() { + \$DEBUG && dump + cleanup + } + " + atf_add_test_case ${name} +} + +atf_init_test_cases() +{ + local algo= + + for algo in $ESP_ENCRYPTION_ALGORITHMS_MINIMUM; do + add_test_ipsec_gif ipv4 tunnel esp $algo + add_test_ipsec_gif ipv6 tunnel esp $algo + add_test_ipsec_gif ipv4 transport esp $algo + add_test_ipsec_gif ipv6 transport esp $algo + done + + for algo in $AH_AUTHENTICATION_ALGORITHMS_MINIMUM; do + add_test_ipsec_gif ipv4 tunnel ah $algo + add_test_ipsec_gif ipv6 tunnel ah $algo + add_test_ipsec_gif ipv4 transport ah $algo + add_test_ipsec_gif ipv6 transport ah $algo + done +} diff --git a/net/ipsec/t_ipsec_l2tp.sh b/net/ipsec/t_ipsec_l2tp.sh new file mode 100644 --- /dev/null +++ b/net/ipsec/t_ipsec_l2tp.sh @@ -0,0 +1,420 @@ +# $NetBSD: t_ipsec_l2tp.sh,v 1.9 2020/02/17 08:46:10 ozaki-r Exp $ +# +# Copyright (c) 2017 Internet Initiative Japan Inc. +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +SOCK_LOCAL=unix://ipsec_l2tp_local +SOCK_TUN_LOCAL=unix://ipsec_l2tp_tunel_local +SOCK_TUN_REMOTE=unix://ipsec_l2tp_tunnel_remote +SOCK_REMOTE=unix://ipsec_l2tp_remote +BUS_LOCAL=./bus_ipsec_local +BUS_TUNNEL=./bus_ipsec_tunnel +BUS_REMOTE=./bus_ipsec_remote + +DEBUG=${DEBUG:-true} + +make_l2tp_pktstr() +{ + local src=$1 + local dst=$2 + local proto=$3 + local ipproto=$4 + local mode=$5 + local proto_cap= proto_str= + + if [ $proto = esp ]; then + proto_cap=ESP + else + proto_cap=AH + if [ $ipproto = ipv4 ]; then + if [ $mode = tunnel ]; then + proto_str="ip-proto-115 102 \(ipip-proto-4\)" + else + proto_str="ip-proto-115 102" + fi + else + proto_str="ip-proto-115" + fi + fi + + echo "$src > $dst: $proto_cap.+$proto_str" +} + +wait_for_all_dad_completions() +{ + + for sock in $SOCK_LOCAL $SOCK_TUN_LOCAL $SOCK_TUN_REMOTE $SOCK_REMOTE; do + export RUMP_SERVER=$sock + atf_check -s exit:0 rump.ifconfig -w 10 + done +} + +test_ipsec4_l2tp() +{ + local mode=$1 + local proto=$2 + local algo=$3 + local ip_local=10.0.0.1 + local ip_gwlo_tun=20.0.0.1 + local ip_gwre_tun=20.0.0.2 + local ip_remote=10.0.0.2 + local subnet_local=20.0.0.0 + local subnet_remote=20.0.0.0 + local tmpfile=./tmp + local outfile=./out + local str= + local algo_args="$(generate_algo_args $proto $algo)" + + # See https://www.netbsd.org/docs/network/ipsec/#sample_vpn + rump_server_crypto_start $SOCK_LOCAL + rump_server_crypto_start $SOCK_TUN_LOCAL netipsec l2tp bridge + rump_server_crypto_start $SOCK_TUN_REMOTE netipsec l2tp bridge + rump_server_crypto_start $SOCK_REMOTE + rump_server_add_iface $SOCK_LOCAL shmif0 $BUS_LOCAL + rump_server_add_iface $SOCK_TUN_LOCAL shmif0 $BUS_LOCAL + rump_server_add_iface $SOCK_TUN_LOCAL shmif1 $BUS_TUNNEL + rump_server_add_iface $SOCK_TUN_REMOTE shmif0 $BUS_REMOTE + rump_server_add_iface $SOCK_TUN_REMOTE shmif1 $BUS_TUNNEL + rump_server_add_iface $SOCK_REMOTE shmif0 $BUS_REMOTE + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 rump.ifconfig shmif0 $ip_local/24 + + export RUMP_SERVER=$SOCK_TUN_LOCAL + atf_check -s exit:0 rump.ifconfig shmif0 up + atf_check -s exit:0 rump.ifconfig shmif1 $ip_gwlo_tun/24 + rump_server_add_iface $SOCK_TUN_LOCAL l2tp0 + atf_check -s exit:0 rump.ifconfig l2tp0 \ + tunnel $ip_gwlo_tun $ip_gwre_tun + atf_check -s exit:0 rump.ifconfig l2tp0 session 1234 4321 + atf_check -s exit:0 rump.ifconfig l2tp0 up + rump_server_add_iface $SOCK_TUN_LOCAL bridge0 + atf_check -s exit:0 rump.ifconfig bridge0 up + atf_check -s exit:0 $HIJACKING brconfig bridge0 add l2tp0 + atf_check -s exit:0 $HIJACKING brconfig bridge0 add shmif0 + + export RUMP_SERVER=$SOCK_TUN_REMOTE + atf_check -s exit:0 rump.ifconfig shmif0 up + atf_check -s exit:0 rump.ifconfig shmif1 $ip_gwre_tun/24 + rump_server_add_iface $SOCK_TUN_REMOTE l2tp0 + atf_check -s exit:0 rump.ifconfig l2tp0 \ + tunnel $ip_gwre_tun $ip_gwlo_tun + atf_check -s exit:0 rump.ifconfig l2tp0 session 4321 1234 + atf_check -s exit:0 rump.ifconfig l2tp0 up + rump_server_add_iface $SOCK_TUN_REMOTE bridge0 + atf_check -s exit:0 rump.ifconfig bridge0 up + atf_check -s exit:0 $HIJACKING brconfig bridge0 add l2tp0 + atf_check -s exit:0 $HIJACKING brconfig bridge0 add shmif0 + + export RUMP_SERVER=$SOCK_REMOTE + atf_check -s exit:0 rump.ifconfig shmif0 $ip_remote/24 + + wait_for_all_dad_completions + + extract_new_packets $BUS_TUNNEL > $outfile + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 -o ignore rump.ping -c 1 -n -w 3 $ip_remote + + extract_new_packets $BUS_TUNNEL > $outfile + atf_check -s exit:0 \ + -o match:"$ip_gwlo_tun > $ip_gwre_tun: +ip-proto-115" \ + cat $outfile + atf_check -s exit:0 \ + -o match:"$ip_gwre_tun > $ip_gwlo_tun: +ip-proto-115" \ + cat $outfile + + if [ $mode = tunnel ]; then + export RUMP_SERVER=$SOCK_TUN_LOCAL + # from https://www.netbsd.org/docs/network/ipsec/ + cat > $tmpfile <<-EOF + add $ip_gwlo_tun $ip_gwre_tun $proto 10000 $algo_args; + add $ip_gwre_tun $ip_gwlo_tun $proto 10001 $algo_args; + spdadd $subnet_local/24 $subnet_remote/24 any -P out ipsec + $proto/tunnel/$ip_gwlo_tun-$ip_gwre_tun/require; + spdadd $subnet_remote/24 $subnet_local/24 any -P in ipsec + $proto/tunnel/$ip_gwre_tun-$ip_gwlo_tun/require; + EOF + $DEBUG && cat $tmpfile + atf_check -s exit:0 -o empty $HIJACKING setkey -c < $tmpfile + + export RUMP_SERVER=$SOCK_TUN_REMOTE + cat > $tmpfile <<-EOF + add $ip_gwlo_tun $ip_gwre_tun $proto 10000 $algo_args; + add $ip_gwre_tun $ip_gwlo_tun $proto 10001 $algo_args; + spdadd $subnet_remote/24 $subnet_local/24 any -P out ipsec + $proto/tunnel/$ip_gwre_tun-$ip_gwlo_tun/require; + spdadd $subnet_local/24 $subnet_remote/24 any -P in ipsec + $proto/tunnel/$ip_gwlo_tun-$ip_gwre_tun/require; + EOF + $DEBUG && cat $tmpfile + atf_check -s exit:0 -o empty $HIJACKING setkey -c < $tmpfile + else # transport mode + export RUMP_SERVER=$SOCK_TUN_LOCAL + # from https://www.netbsd.org/docs/network/ipsec/ + cat > $tmpfile <<-EOF + add $ip_gwlo_tun $ip_gwre_tun $proto 10000 $algo_args; + add $ip_gwre_tun $ip_gwlo_tun $proto 10001 $algo_args; + spdadd $ip_gwlo_tun/32 $ip_gwre_tun/32 any -P out ipsec + $proto/transport//require; + spdadd $ip_gwre_tun/32 $ip_gwlo_tun/32 any -P in ipsec + $proto/transport//require; + EOF + $DEBUG && cat $tmpfile + atf_check -s exit:0 -o empty $HIJACKING setkey -c < $tmpfile + + export RUMP_SERVER=$SOCK_TUN_REMOTE + cat > $tmpfile <<-EOF + add $ip_gwlo_tun $ip_gwre_tun $proto 10000 $algo_args; + add $ip_gwre_tun $ip_gwlo_tun $proto 10001 $algo_args; + spdadd $ip_gwre_tun/32 $ip_gwlo_tun/32 any -P out ipsec + $proto/transport//require; + spdadd $ip_gwlo_tun/32 $ip_gwre_tun/32 any -P in ipsec + $proto/transport//require; + EOF + $DEBUG && cat $tmpfile + atf_check -s exit:0 -o empty $HIJACKING setkey -c < $tmpfile + fi + + check_sa_entries $SOCK_TUN_LOCAL $ip_gwlo_tun $ip_gwre_tun + check_sa_entries $SOCK_TUN_REMOTE $ip_gwlo_tun $ip_gwre_tun + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 -o ignore rump.ping -c 1 -n -w 3 $ip_remote + + extract_new_packets $BUS_TUNNEL > $outfile + str=$(make_l2tp_pktstr $ip_gwlo_tun $ip_gwre_tun $proto ipv4 $mode) + atf_check -s exit:0 -o match:"$str" cat $outfile + str=$(make_l2tp_pktstr $ip_gwre_tun $ip_gwlo_tun $proto ipv4 $mode) + atf_check -s exit:0 -o match:"$str" cat $outfile + + test_flush_entries $SOCK_TUN_LOCAL + test_flush_entries $SOCK_TUN_REMOTE +} + +test_ipsec6_l2tp() +{ + local mode=$1 + local proto=$2 + local algo=$3 + local ip_local=fd00::1 + local ip_gwlo_tun=fc00::1 + local ip_gwre_tun=fc00::2 + local ip_remote=fd00::2 + local subnet_local=fc00:: + local subnet_remote=fc00:: + local tmpfile=./tmp + local outfile=./out + local str= + local algo_args="$(generate_algo_args $proto $algo)" + + rump_server_crypto_start $SOCK_LOCAL netinet6 + rump_server_crypto_start $SOCK_TUN_LOCAL netipsec netinet6 l2tp bridge + rump_server_crypto_start $SOCK_TUN_REMOTE netipsec netinet6 l2tp bridge + rump_server_crypto_start $SOCK_REMOTE netinet6 + rump_server_add_iface $SOCK_LOCAL shmif0 $BUS_LOCAL + rump_server_add_iface $SOCK_TUN_LOCAL shmif0 $BUS_LOCAL + rump_server_add_iface $SOCK_TUN_LOCAL shmif1 $BUS_TUNNEL + rump_server_add_iface $SOCK_TUN_REMOTE shmif0 $BUS_REMOTE + rump_server_add_iface $SOCK_TUN_REMOTE shmif1 $BUS_TUNNEL + rump_server_add_iface $SOCK_REMOTE shmif0 $BUS_REMOTE + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 rump.ifconfig shmif0 inet6 $ip_local/64 + + export RUMP_SERVER=$SOCK_TUN_LOCAL + atf_check -s exit:0 rump.ifconfig shmif0 up + atf_check -s exit:0 rump.ifconfig shmif1 inet6 $ip_gwlo_tun/64 + rump_server_add_iface $SOCK_TUN_LOCAL l2tp0 + atf_check -s exit:0 rump.ifconfig l2tp0 \ + tunnel $ip_gwlo_tun $ip_gwre_tun + atf_check -s exit:0 rump.ifconfig l2tp0 session 1234 4321 + atf_check -s exit:0 rump.ifconfig l2tp0 up + rump_server_add_iface $SOCK_TUN_LOCAL bridge0 + atf_check -s exit:0 rump.ifconfig bridge0 up + atf_check -s exit:0 $HIJACKING brconfig bridge0 add l2tp0 + atf_check -s exit:0 $HIJACKING brconfig bridge0 add shmif0 + + export RUMP_SERVER=$SOCK_TUN_REMOTE + atf_check -s exit:0 rump.ifconfig shmif0 up + atf_check -s exit:0 rump.ifconfig shmif1 inet6 $ip_gwre_tun/64 + rump_server_add_iface $SOCK_TUN_REMOTE l2tp0 + atf_check -s exit:0 rump.ifconfig l2tp0 \ + tunnel $ip_gwre_tun $ip_gwlo_tun + atf_check -s exit:0 rump.ifconfig l2tp0 session 4321 1234 + atf_check -s exit:0 rump.ifconfig l2tp0 up + rump_server_add_iface $SOCK_TUN_REMOTE bridge0 + atf_check -s exit:0 rump.ifconfig bridge0 up + atf_check -s exit:0 $HIJACKING brconfig bridge0 add l2tp0 + atf_check -s exit:0 $HIJACKING brconfig bridge0 add shmif0 + + export RUMP_SERVER=$SOCK_REMOTE + atf_check -s exit:0 rump.ifconfig shmif0 inet6 $ip_remote + + wait_for_all_dad_completions + + extract_new_packets $BUS_TUNNEL > $outfile + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 -o ignore rump.ping6 -c 1 -n -X 3 $ip_remote + + extract_new_packets $BUS_TUNNEL > $outfile + atf_check -s exit:0 \ + -o match:"$ip_gwlo_tun > $ip_gwre_tun: +ip-proto-115" \ + cat $outfile + atf_check -s exit:0 \ + -o match:"$ip_gwre_tun > $ip_gwlo_tun: +ip-proto-115" \ + cat $outfile + + if [ $mode = tunnel ]; then + export RUMP_SERVER=$SOCK_TUN_LOCAL + # from https://www.netbsd.org/docs/network/ipsec/ + cat > $tmpfile <<-EOF + add $ip_gwlo_tun $ip_gwre_tun $proto 10000 $algo_args; + add $ip_gwre_tun $ip_gwlo_tun $proto 10001 $algo_args; + spdadd $subnet_local/64 $subnet_remote/64 any -P out ipsec + $proto/tunnel/$ip_gwlo_tun-$ip_gwre_tun/require; + spdadd $subnet_remote/64 $subnet_local/64 any -P in ipsec + $proto/tunnel/$ip_gwre_tun-$ip_gwlo_tun/require; + EOF + $DEBUG && cat $tmpfile + atf_check -s exit:0 -o empty $HIJACKING setkey -c < $tmpfile + + export RUMP_SERVER=$SOCK_TUN_REMOTE + cat > $tmpfile <<-EOF + add $ip_gwlo_tun $ip_gwre_tun $proto 10000 $algo_args; + add $ip_gwre_tun $ip_gwlo_tun $proto 10001 $algo_args; + spdadd $subnet_remote/64 $subnet_local/64 any -P out ipsec + $proto/tunnel/$ip_gwre_tun-$ip_gwlo_tun/require; + spdadd $subnet_local/64 $subnet_remote/64 any -P in ipsec + $proto/tunnel/$ip_gwlo_tun-$ip_gwre_tun/require; + EOF + $DEBUG && cat $tmpfile + atf_check -s exit:0 -o empty $HIJACKING setkey -c < $tmpfile + else # transport mode + export RUMP_SERVER=$SOCK_TUN_LOCAL + # from https://www.netbsd.org/docs/network/ipsec/ + cat > $tmpfile <<-EOF + add $ip_gwlo_tun $ip_gwre_tun $proto 10000 $algo_args; + add $ip_gwre_tun $ip_gwlo_tun $proto 10001 $algo_args; + spdadd $ip_gwlo_tun/128 $ip_gwre_tun/128 any -P out ipsec + $proto/transport//require; + spdadd $ip_gwre_tun/128 $ip_gwlo_tun/128 any -P in ipsec + $proto/transport//require; + EOF + $DEBUG && cat $tmpfile + atf_check -s exit:0 -o empty $HIJACKING setkey -c < $tmpfile + + export RUMP_SERVER=$SOCK_TUN_REMOTE + cat > $tmpfile <<-EOF + add $ip_gwlo_tun $ip_gwre_tun $proto 10000 $algo_args; + add $ip_gwre_tun $ip_gwlo_tun $proto 10001 $algo_args; + spdadd $ip_gwre_tun/128 $ip_gwlo_tun/128 any -P out ipsec + $proto/transport//require; + spdadd $ip_gwlo_tun/128 $ip_gwre_tun/128 any -P in ipsec + $proto/transport//require; + EOF + $DEBUG && cat $tmpfile + atf_check -s exit:0 -o empty $HIJACKING setkey -c < $tmpfile + fi + + check_sa_entries $SOCK_TUN_LOCAL $ip_gwlo_tun $ip_gwre_tun + check_sa_entries $SOCK_TUN_REMOTE $ip_gwlo_tun $ip_gwre_tun + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 -o ignore rump.ping6 -c 1 -n -X 3 $ip_remote + + extract_new_packets $BUS_TUNNEL > $outfile + str=$(make_l2tp_pktstr $ip_gwlo_tun $ip_gwre_tun $proto ipv6 $mode) + atf_check -s exit:0 -o match:"$str" cat $outfile + str=$(make_l2tp_pktstr $ip_gwre_tun $ip_gwlo_tun $proto ipv6 $mode) + atf_check -s exit:0 -o match:"$str" cat $outfile + + test_flush_entries $SOCK_TUN_LOCAL + test_flush_entries $SOCK_TUN_REMOTE +} + +test_ipsec_l2tp_common() +{ + local ipproto=$1 + local mode=$2 + local proto=$3 + local algo=$4 + + if [ $ipproto = ipv4 ]; then + test_ipsec4_l2tp $mode $proto $algo + else + test_ipsec6_l2tp $mode $proto $algo + fi +} + +add_test_ipsec_l2tp() +{ + local ipproto=$1 + local mode=$2 + local proto=$3 + local algo=$4 + local _algo=$(echo $algo | sed 's/-//g') + local name= desc= + + name="ipsec_l2tp_${ipproto}_${mode}_${proto}_${_algo}" + desc="Tests of l2tp/IPsec ($ipproto) ${mode} mode with $proto ($algo)" + + atf_test_case ${name} cleanup + eval " + ${name}_head() { + atf_set descr \"$desc\" + atf_set require.progs rump_server setkey + } + ${name}_body() { + test_ipsec_l2tp_common $ipproto $mode $proto $algo + rump_server_destroy_ifaces + } + ${name}_cleanup() { + \$DEBUG && dump + cleanup + } + " + atf_add_test_case ${name} +} + +atf_init_test_cases() +{ + local algo= + + for algo in $ESP_ENCRYPTION_ALGORITHMS_MINIMUM; do + add_test_ipsec_l2tp ipv4 tunnel esp $algo + add_test_ipsec_l2tp ipv6 tunnel esp $algo + add_test_ipsec_l2tp ipv4 transport esp $algo + add_test_ipsec_l2tp ipv6 transport esp $algo + done + + for algo in $AH_AUTHENTICATION_ALGORITHMS_MINIMUM; do + add_test_ipsec_l2tp ipv4 tunnel ah $algo + add_test_ipsec_l2tp ipv6 tunnel ah $algo + add_test_ipsec_l2tp ipv4 transport ah $algo + add_test_ipsec_l2tp ipv6 transport ah $algo + done +} diff --git a/net/ipsec/t_ipsec_misc.sh b/net/ipsec/t_ipsec_misc.sh new file mode 100644 --- /dev/null +++ b/net/ipsec/t_ipsec_misc.sh @@ -0,0 +1,909 @@ +# $NetBSD: t_ipsec_misc.sh,v 1.24 2020/08/31 14:03:56 martin Exp $ +# +# Copyright (c) 2017 Internet Initiative Japan Inc. +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +SOCK_LOCAL=unix://ipsec_local +SOCK_PEER=unix://ipsec_peer +BUS=./bus_ipsec + +DEBUG=${DEBUG:-true} + +setup_sasp() +{ + local proto=$1 + local algo_args="$2" + local ip_local=$3 + local ip_peer=$4 + local lifetime=$5 + local update=$6 + local tmpfile=./tmp + local saadd=add + local saadd_algo_args="$algo_args" + local extra= + + if [ "$update" = getspi ]; then + saadd=getspi + saadd_algo_args= + fi + + if [ "$update" = sa -o "$update" = getspi ]; then + extra="update $ip_local $ip_peer $proto 10000 $algo_args; + update $ip_peer $ip_local $proto 10001 $algo_args;" + elif [ "$update" = sp ]; then + extra="spdupdate $ip_local $ip_peer any -P out ipsec $proto/transport//require;" + fi + + export RUMP_SERVER=$SOCK_LOCAL + cat > $tmpfile <<-EOF + $saadd $ip_local $ip_peer $proto 10000 -lh $lifetime -ls $lifetime $saadd_algo_args; + $saadd $ip_peer $ip_local $proto 10001 -lh $lifetime -ls $lifetime $saadd_algo_args; + spdadd $ip_local $ip_peer any -P out ipsec $proto/transport//require; + $extra + EOF + $DEBUG && cat $tmpfile + atf_check -s exit:0 -o empty $HIJACKING setkey -c < $tmpfile + # XXX it can be expired if $lifetime is very short + #check_sa_entries $SOCK_LOCAL $ip_local $ip_peer + + if [ "$update" = sp ]; then + extra="spdupdate $ip_peer $ip_local any -P out ipsec $proto/transport//require;" + fi + + export RUMP_SERVER=$SOCK_PEER + cat > $tmpfile <<-EOF + $saadd $ip_local $ip_peer $proto 10000 -lh $lifetime -ls $lifetime $saadd_algo_args; + $saadd $ip_peer $ip_local $proto 10001 -lh $lifetime -ls $lifetime $saadd_algo_args; + spdadd $ip_peer $ip_local any -P out ipsec $proto/transport//require; + $extra + EOF + $DEBUG && cat $tmpfile + atf_check -s exit:0 -o empty $HIJACKING setkey -c < $tmpfile + # XXX it can be expired if $lifetime is very short + #check_sa_entries $SOCK_PEER $ip_local $ip_peer +} + +test_sad_disapper_until() +{ + local time=$1 + local check_dead_sa=$2 + local setkey_opts= + local n=$time + local tmpfile=./__tmp + local sock= ok= + + if $check_dead_sa; then + setkey_opts="-D -a" + else + setkey_opts="-D" + fi + + while [ $n -ne 0 ]; do + ok=0 + sleep 1 + for sock in $SOCK_LOCAL $SOCK_PEER; do + export RUMP_SERVER=$sock + $HIJACKING setkey $setkey_opts > $tmpfile + $DEBUG && cat $tmpfile + if grep -q 'No SAD entries.' $tmpfile; then + ok=$((ok + 1)) + fi + done + if [ $ok -eq 2 ]; then + return + fi + + n=$((n - 1)) + done + + atf_fail "SAs didn't disappear after $time sec." +} + +test_ipsec4_lifetime() +{ + local proto=$1 + local algo=$2 + local ip_local=10.0.0.1 + local ip_peer=10.0.0.2 + local outfile=./out + local proto_cap=$(echo $proto | tr 'a-z' 'A-Z') + local algo_args="$(generate_algo_args $proto $algo)" + local lifetime=3 + local buffertime=2 + + rump_server_crypto_start $SOCK_LOCAL netipsec + rump_server_crypto_start $SOCK_PEER netipsec + rump_server_add_iface $SOCK_LOCAL shmif0 $BUS + rump_server_add_iface $SOCK_PEER shmif0 $BUS + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 rump.sysctl -q -w net.inet.ip.dad_count=0 + atf_check -s exit:0 rump.ifconfig shmif0 $ip_local/24 + #atf_check -s exit:0 -o ignore rump.sysctl -w net.key.debug=0xff + + export RUMP_SERVER=$SOCK_PEER + atf_check -s exit:0 rump.sysctl -q -w net.inet.ip.dad_count=0 + atf_check -s exit:0 rump.ifconfig shmif0 $ip_peer/24 + #atf_check -s exit:0 -o ignore rump.sysctl -w net.key.debug=0xff + + extract_new_packets $BUS > $outfile + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 -o ignore rump.ping -c 1 -n -w 3 $ip_peer + + extract_new_packets $BUS > $outfile + atf_check -s exit:0 -o match:"$ip_local > $ip_peer: ICMP echo request" \ + cat $outfile + atf_check -s exit:0 -o match:"$ip_peer > $ip_local: ICMP echo reply" \ + cat $outfile + + # Set up SAs with lifetime 1 sec. + setup_sasp $proto "$algo_args" $ip_local $ip_peer 1 + + # Check the SAs have been expired + test_sad_disapper_until $((1 + $buffertime)) false + + # Clean up SPs + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 -o empty $HIJACKING setkey -F -P + export RUMP_SERVER=$SOCK_PEER + atf_check -s exit:0 -o empty $HIJACKING setkey -F -P + + # Set up SAs with lifetime with $lifetime + setup_sasp $proto "$algo_args" $ip_local $ip_peer $lifetime + + # Use the SAs; this will create a reference from an SP to an SA + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 -o ignore rump.ping -c 1 -n -w 3 $ip_peer + + extract_new_packets $BUS > $outfile + atf_check -s exit:0 -o match:"$ip_local > $ip_peer: $proto_cap" \ + cat $outfile + atf_check -s exit:0 -o match:"$ip_peer > $ip_local: $proto_cap" \ + cat $outfile + + # Check the SAs have been expired + test_sad_disapper_until $((lifetime + $buffertime)) true + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s not-exit:0 -o match:'0 packets received' \ + rump.ping -c 1 -n -w 1 $ip_peer + + test_flush_entries $SOCK_LOCAL + test_flush_entries $SOCK_PEER +} + +test_ipsec6_lifetime() +{ + local proto=$1 + local algo=$2 + local ip_local=fd00::1 + local ip_peer=fd00::2 + local outfile=./out + local proto_cap=$(echo $proto | tr 'a-z' 'A-Z') + local algo_args="$(generate_algo_args $proto $algo)" + local lifetime=3 + local buffertime=2 + + rump_server_crypto_start $SOCK_LOCAL netinet6 netipsec + rump_server_crypto_start $SOCK_PEER netinet6 netipsec + rump_server_add_iface $SOCK_LOCAL shmif0 $BUS + rump_server_add_iface $SOCK_PEER shmif0 $BUS + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 rump.sysctl -q -w net.inet6.ip6.dad_count=0 + atf_check -s exit:0 rump.ifconfig shmif0 inet6 $ip_local + + export RUMP_SERVER=$SOCK_PEER + atf_check -s exit:0 rump.sysctl -q -w net.inet6.ip6.dad_count=0 + atf_check -s exit:0 rump.ifconfig shmif0 inet6 $ip_peer + + extract_new_packets $BUS > $outfile + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 -o ignore rump.ping6 -c 1 -n -X 3 $ip_peer + + extract_new_packets $BUS > $outfile + atf_check -s exit:0 -o match:"$ip_local > $ip_peer: ICMP6, echo request" \ + cat $outfile + atf_check -s exit:0 -o match:"$ip_peer > $ip_local: ICMP6, echo reply" \ + cat $outfile + + # Set up SAs with lifetime 1 sec. + setup_sasp $proto "$algo_args" $ip_local $ip_peer 1 + + # Check the SAs have been expired + test_sad_disapper_until $((1 + $buffertime)) false + + # Clean up SPs + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 -o empty $HIJACKING setkey -F -P + export RUMP_SERVER=$SOCK_PEER + atf_check -s exit:0 -o empty $HIJACKING setkey -F -P + + # Set up SAs with lifetime with $lifetime + setup_sasp $proto "$algo_args" $ip_local $ip_peer $lifetime + + # Use the SAs; this will create a reference from an SP to an SA + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 -o ignore rump.ping6 -c 1 -n -X 3 $ip_peer + + extract_new_packets $BUS > $outfile + atf_check -s exit:0 -o match:"$ip_local > $ip_peer: $proto_cap" \ + cat $outfile + atf_check -s exit:0 -o match:"$ip_peer > $ip_local: $proto_cap" \ + cat $outfile + + # Check the SAs have been expired + test_sad_disapper_until $((lifetime + $buffertime)) true + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s not-exit:0 -o match:'0 packets received' \ + rump.ping6 -c 1 -n -X 1 $ip_peer + + test_flush_entries $SOCK_LOCAL + test_flush_entries $SOCK_PEER +} + +test_lifetime_common() +{ + local ipproto=$1 + local proto=$2 + local algo=$3 + + if [ $ipproto = ipv4 ]; then + test_ipsec4_lifetime $proto $algo + else + test_ipsec6_lifetime $proto $algo + fi +} + +add_test_lifetime() +{ + local ipproto=$1 + local proto=$2 + local algo=$3 + local _algo=$(echo $algo | sed 's/-//g') + local name= desc= + + name="ipsec_lifetime_${ipproto}_${proto}_${_algo}" + desc="Tests of lifetime of IPsec ($ipproto) with $proto ($algo)" + + atf_test_case ${name} cleanup + eval " + ${name}_head() { + atf_set descr \"$desc\" + atf_set require.progs rump_server setkey + } + ${name}_body() { + test_lifetime_common $ipproto $proto $algo + rump_server_destroy_ifaces + } + ${name}_cleanup() { + \$DEBUG && dump + cleanup + } + " + atf_add_test_case ${name} +} + +test_update() +{ + local proto=$1 + local algo=$2 + local update=$3 + local ip_local=10.0.0.1 + local ip_peer=10.0.0.2 + local algo_args="$(generate_algo_args $proto $algo)" + local proto_cap=$(echo $proto | tr 'a-z' 'A-Z') + local outfile=./out + + rump_server_crypto_start $SOCK_LOCAL netipsec + rump_server_crypto_start $SOCK_PEER netipsec + rump_server_add_iface $SOCK_LOCAL shmif0 $BUS + rump_server_add_iface $SOCK_PEER shmif0 $BUS + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 rump.sysctl -q -w net.inet.ip.dad_count=0 + atf_check -s exit:0 rump.ifconfig shmif0 $ip_local/24 + + export RUMP_SERVER=$SOCK_PEER + atf_check -s exit:0 rump.sysctl -q -w net.inet.ip.dad_count=0 + atf_check -s exit:0 rump.ifconfig shmif0 $ip_peer/24 + + setup_sasp $proto "$algo_args" $ip_local $ip_peer 100 $update + + extract_new_packets $BUS > $outfile + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 -o ignore rump.ping -c 1 -n -w 3 $ip_peer + + extract_new_packets $BUS > $outfile + atf_check -s exit:0 -o match:"$ip_local > $ip_peer: $proto_cap" \ + cat $outfile + atf_check -s exit:0 -o match:"$ip_peer > $ip_local: $proto_cap" \ + cat $outfile +} + +add_test_update() +{ + local proto=$1 + local algo=$2 + local update=$3 + local _update=$(echo $update |tr 'a-z' 'A-Z') + local _algo=$(echo $algo | sed 's/-//g') + local name= desc= + + desc="Tests trying to udpate $_update of $proto ($algo)" + name="ipsec_update_${update}_${proto}_${_algo}" + + atf_test_case ${name} cleanup + eval " + ${name}_head() { + atf_set descr \"$desc\" + atf_set require.progs rump_server setkey + } + ${name}_body() { + test_update $proto $algo $update + rump_server_destroy_ifaces + } + ${name}_cleanup() { + \$DEBUG && dump + cleanup + } + " + atf_add_test_case ${name} +} + +test_getspi_update() +{ + local proto=$1 + local algo=$2 + local ip_local=10.0.0.1 + local ip_peer=10.0.0.2 + local algo_args="$(generate_algo_args $proto $algo)" + local proto_cap=$(echo $proto | tr 'a-z' 'A-Z') + local outfile=./out + + rump_server_crypto_start $SOCK_LOCAL netipsec + rump_server_crypto_start $SOCK_PEER netipsec + rump_server_add_iface $SOCK_LOCAL shmif0 $BUS + rump_server_add_iface $SOCK_PEER shmif0 $BUS + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 rump.sysctl -q -w net.inet.ip.dad_count=0 + atf_check -s exit:0 rump.ifconfig shmif0 $ip_local/24 + + export RUMP_SERVER=$SOCK_PEER + atf_check -s exit:0 rump.sysctl -q -w net.inet.ip.dad_count=0 + atf_check -s exit:0 rump.ifconfig shmif0 $ip_peer/24 + + setup_sasp $proto "$algo_args" $ip_local $ip_peer 100 getspi + + extract_new_packets $BUS > $outfile + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 -o ignore rump.ping -c 1 -n -w 3 $ip_peer + + extract_new_packets $BUS > $outfile + atf_check -s exit:0 -o match:"$ip_local > $ip_peer: $proto_cap" \ + cat $outfile + atf_check -s exit:0 -o match:"$ip_peer > $ip_local: $proto_cap" \ + cat $outfile +} + +add_test_getspi_update() +{ + local proto=$1 + local algo=$2 + local _algo=$(echo $algo | sed 's/-//g') + local name= desc= + + desc="Tests trying to getspi and udpate SA of $proto ($algo)" + name="ipsec_getspi_update_sa_${proto}_${_algo}" + + atf_test_case ${name} cleanup + eval " + ${name}_head() { + atf_set descr \"$desc\" + atf_set require.progs rump_server setkey + } + ${name}_body() { + test_getspi_update $proto $algo + rump_server_destroy_ifaces + } + ${name}_cleanup() { + \$DEBUG && dump + cleanup + } + " + atf_add_test_case ${name} +} + +add_sa() +{ + local proto=$1 + local algo_args="$2" + local ip_local=$3 + local ip_peer=$4 + local lifetime=$5 + local spi=$6 + local tmpfile=./tmp + local extra= + + export RUMP_SERVER=$SOCK_LOCAL + cat > $tmpfile <<-EOF + add $ip_local $ip_peer $proto $((spi)) -lh $lifetime -ls $lifetime $algo_args; + add $ip_peer $ip_local $proto $((spi + 1)) -lh $lifetime -ls $lifetime $algo_args; + $extra + EOF + $DEBUG && cat $tmpfile + atf_check -s exit:0 -o empty $HIJACKING setkey -c < $tmpfile + $DEBUG && $HIJACKING setkey -D + # XXX it can be expired if $lifetime is very short + #check_sa_entries $SOCK_LOCAL $ip_local $ip_peer + + export RUMP_SERVER=$SOCK_PEER + cat > $tmpfile <<-EOF + add $ip_local $ip_peer $proto $((spi)) -lh $lifetime -ls $lifetime $algo_args; + add $ip_peer $ip_local $proto $((spi + 1)) -lh $lifetime -ls $lifetime $algo_args; + $extra + EOF + $DEBUG && cat $tmpfile + atf_check -s exit:0 -o empty $HIJACKING setkey -c < $tmpfile + $DEBUG && $HIJACKING setkey -D + # XXX it can be expired if $lifetime is very short + #check_sa_entries $SOCK_PEER $ip_local $ip_peer +} + +delete_sa() +{ + local proto=$1 + local ip_local=$2 + local ip_peer=$3 + local spi=$4 + local tmpfile=./tmp + local extra= + + export RUMP_SERVER=$SOCK_LOCAL + cat > $tmpfile <<-EOF + delete $ip_local $ip_peer $proto $((spi)); + delete $ip_peer $ip_local $proto $((spi + 1)); + EOF + $DEBUG && cat $tmpfile + atf_check -s exit:0 -o empty $HIJACKING setkey -c < $tmpfile + $DEBUG && $HIJACKING setkey -D + + export RUMP_SERVER=$SOCK_PEER + cat > $tmpfile <<-EOF + delete $ip_local $ip_peer $proto $((spi)); + delete $ip_peer $ip_local $proto $((spi + 1)); + EOF + $DEBUG && cat $tmpfile + atf_check -s exit:0 -o empty $HIJACKING setkey -c < $tmpfile + $DEBUG && $HIJACKING setkey -D +} + +check_packet_spi() +{ + local outfile=$1 + local ip_local=$2 + local ip_peer=$3 + local proto=$4 + local spi=$5 + local spistr= + + $DEBUG && cat $outfile + spistr=$(printf "%08x" $spi) + atf_check -s exit:0 \ + -o match:"$ip_local > $ip_peer: $proto_cap\(spi=0x$spistr," \ + cat $outfile + spistr=$(printf "%08x" $((spi + 1))) + atf_check -s exit:0 \ + -o match:"$ip_peer > $ip_local: $proto_cap\(spi=0x$spistr," \ + cat $outfile +} + +wait_sa_disappeared() +{ + local spi=$1 + local i= + + export RUMP_SERVER=$SOCK_LOCAL + for i in $(seq 1 10); do + $HIJACKING setkey -D |grep -q "spi=$spi" + [ $? != 0 ] && break + sleep 1 + done + if [ $i -eq 10 ]; then + atf_fail "SA (spi=$spi) didn't disappear in 10s" + fi + export RUMP_SERVER=$SOCK_PEER + for i in $(seq 1 10); do + $HIJACKING setkey -D |grep -q "spi=$spi" + [ $? != 0 ] && break + sleep 1 + done + if [ $i -eq 10 ]; then + atf_fail "SA (spi=$spi) didn't disappear in 10s" + fi +} + +test_spi() +{ + local proto=$1 + local algo=$2 + local preferred=$3 + local method=$4 + local ip_local=10.0.0.1 + local ip_peer=10.0.0.2 + local algo_args="$(generate_algo_args $proto $algo)" + local proto_cap=$(echo $proto | tr 'a-z' 'A-Z') + local outfile=./out + local spistr= + local longtime= shorttime= + + if [ $method = timeout ]; then + atf_skip \ + "PR 55632: test fails randomly, leaving spurious rump_server around" + fi + if [ $method = timeout -a $preferred = new ]; then + skip_if_qemu + fi + + if [ $method = delete ]; then + shorttime=100 + longtime=100 + else + shorttime=3 + longtime=6 + fi + + rump_server_crypto_start $SOCK_LOCAL netipsec + rump_server_crypto_start $SOCK_PEER netipsec + rump_server_add_iface $SOCK_LOCAL shmif0 $BUS + rump_server_add_iface $SOCK_PEER shmif0 $BUS + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 rump.sysctl -q -w net.inet.ip.dad_count=0 + atf_check -s exit:0 rump.ifconfig shmif0 $ip_local/24 + if [ $preferred = old ]; then + atf_check -s exit:0 rump.sysctl -q -w net.key.prefered_oldsa=1 + fi + + export RUMP_SERVER=$SOCK_PEER + atf_check -s exit:0 rump.sysctl -q -w net.inet.ip.dad_count=0 + atf_check -s exit:0 rump.ifconfig shmif0 $ip_peer/24 + if [ $preferred = old ]; then + atf_check -s exit:0 rump.sysctl -q -w net.key.prefered_oldsa=1 + fi + + setup_sasp $proto "$algo_args" $ip_local $ip_peer 100 + + extract_new_packets $BUS > $outfile + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 -o ignore rump.ping -c 1 -n -w 3 $ip_peer + extract_new_packets $BUS > $outfile + check_packet_spi $outfile $ip_local $ip_peer $proto_cap 10000 + + # Add a new SA with a different SPI + add_sa $proto "$algo_args" $ip_local $ip_peer $longtime 10010 + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 -o ignore rump.ping -c 1 -n -w 3 $ip_peer + extract_new_packets $BUS > $outfile + if [ $preferred = old ]; then + check_packet_spi $outfile $ip_local $ip_peer $proto_cap 10000 + else + # The new SA is preferred + check_packet_spi $outfile $ip_local $ip_peer $proto_cap 10010 + fi + + # Add another SA with a different SPI + add_sa $proto "$algo_args" $ip_local $ip_peer $shorttime 10020 + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 -o ignore rump.ping -c 1 -n -w 3 $ip_peer + extract_new_packets $BUS > $outfile + if [ $preferred = old ]; then + check_packet_spi $outfile $ip_local $ip_peer $proto_cap 10000 + else + # The newest SA is preferred + check_packet_spi $outfile $ip_local $ip_peer $proto_cap 10020 + fi + + if [ $method = delete ]; then + delete_sa $proto $ip_local $ip_peer 10020 + else + wait_sa_disappeared 10020 + fi + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 -o ignore rump.ping -c 1 -n -w 3 $ip_peer + extract_new_packets $BUS > $outfile + if [ $preferred = old ]; then + check_packet_spi $outfile $ip_local $ip_peer $proto_cap 10000 + else + # The newest one is removed and the second one is used + check_packet_spi $outfile $ip_local $ip_peer $proto_cap 10010 + fi + + if [ $method = delete ]; then + delete_sa $proto $ip_local $ip_peer 10010 + else + wait_sa_disappeared 10010 + fi + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 -o ignore rump.ping -c 1 -n -w 3 $ip_peer + extract_new_packets $BUS > $outfile + if [ $preferred = old ]; then + check_packet_spi $outfile $ip_local $ip_peer $proto_cap 10000 + else + # The second one is removed and the original one is used + check_packet_spi $outfile $ip_local $ip_peer $proto_cap 10000 + fi +} + +add_test_spi() +{ + local proto=$1 + local algo=$2 + local preferred=$3 + local method=$4 + local _algo=$(echo $algo | sed 's/-//g') + local name= desc= + + desc="Tests SAs with different SPIs of $proto ($algo) ($preferred SA preferred) ($method)" + name="ipsec_spi_${proto}_${_algo}_preferred_${preferred}_${method}" + + atf_test_case ${name} cleanup + eval " + ${name}_head() { + atf_set descr \"$desc\" + atf_set require.progs rump_server setkey + } + ${name}_body() { + test_spi $proto $algo $preferred $method + rump_server_destroy_ifaces + } + ${name}_cleanup() { + \$DEBUG && dump + cleanup + } + " + atf_add_test_case ${name} +} + +setup_sp() +{ + local proto=$1 + local algo_args="$2" + local ip_local=$3 + local ip_peer=$4 + local tmpfile=./tmp + + export RUMP_SERVER=$SOCK_LOCAL + cat > $tmpfile <<-EOF + spdadd $ip_local $ip_peer any -P out ipsec $proto/transport//require; + spdadd $ip_peer $ip_local any -P in ipsec $proto/transport//require; + EOF + $DEBUG && cat $tmpfile + atf_check -s exit:0 -o empty $HIJACKING setkey -c < $tmpfile + check_sp_entries $SOCK_LOCAL $ip_local $ip_peer + + export RUMP_SERVER=$SOCK_PEER + cat > $tmpfile <<-EOF + spdadd $ip_peer $ip_local any -P out ipsec $proto/transport//require; + spdadd $ip_local $ip_peer any -P in ipsec $proto/transport//require; + EOF + $DEBUG && cat $tmpfile + atf_check -s exit:0 -o empty $HIJACKING setkey -c < $tmpfile + check_sp_entries $SOCK_PEER $ip_peer $ip_local +} + +test_nosa() +{ + local proto=$1 + local algo=$2 + local update=$3 + local ip_local=10.0.0.1 + local ip_peer=10.0.0.2 + local algo_args="$(generate_algo_args $proto $algo)" + local proto_cap=$(echo $proto | tr 'a-z' 'A-Z') + local outfile=./out + + rump_server_crypto_start $SOCK_LOCAL netipsec + rump_server_crypto_start $SOCK_PEER netipsec + rump_server_add_iface $SOCK_LOCAL shmif0 $BUS + rump_server_add_iface $SOCK_PEER shmif0 $BUS + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 rump.sysctl -q -w net.inet.ip.dad_count=0 + atf_check -s exit:0 rump.ifconfig shmif0 $ip_local/24 + + export RUMP_SERVER=$SOCK_PEER + atf_check -s exit:0 rump.sysctl -q -w net.inet.ip.dad_count=0 + atf_check -s exit:0 rump.ifconfig shmif0 $ip_peer/24 + + setup_sp $proto "$algo_args" $ip_local $ip_peer + + extract_new_packets $BUS > $outfile + + export RUMP_SERVER=$SOCK_LOCAL + # It doesn't work because there is no SA + atf_check -s not-exit:0 -o ignore rump.ping -c 1 -n -w 3 $ip_peer +} + +add_test_nosa() +{ + local proto=$1 + local algo=$2 + local _algo=$(echo $algo | sed 's/-//g') + local name= desc= + + desc="Tests SPs with no relevant SAs with $proto ($algo)" + name="ipsec_nosa_${proto}_${_algo}" + + atf_test_case ${name} cleanup + eval " + ${name}_head() { + atf_set descr \"$desc\" + atf_set require.progs rump_server setkey + } + ${name}_body() { + test_nosa $proto $algo + rump_server_destroy_ifaces + } + ${name}_cleanup() { + \$DEBUG && dump + cleanup + } + " + atf_add_test_case ${name} +} + +test_multiple_sa() +{ + local proto=$1 + local algo=$2 + local update=$3 + local ip_local=10.0.0.1 + local ip_peer=10.0.0.2 + local ip_peer2=10.0.0.3 + local algo_args="$(generate_algo_args $proto $algo)" + local proto_cap=$(echo $proto | tr 'a-z' 'A-Z') + local outfile=./out + + rump_server_crypto_start $SOCK_LOCAL netipsec + rump_server_crypto_start $SOCK_PEER netipsec + rump_server_add_iface $SOCK_LOCAL shmif0 $BUS + rump_server_add_iface $SOCK_PEER shmif0 $BUS + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 rump.sysctl -q -w net.inet.ip.dad_count=0 + atf_check -s exit:0 rump.ifconfig shmif0 $ip_local/24 + + export RUMP_SERVER=$SOCK_PEER + atf_check -s exit:0 rump.sysctl -q -w net.inet.ip.dad_count=0 + atf_check -s exit:0 rump.ifconfig shmif0 $ip_peer/24 + atf_check -s exit:0 rump.ifconfig shmif0 $ip_peer2/24 alias + + setup_sp $proto "$algo_args" "$ip_local" "0.0.0.0/0" + + extract_new_packets $BUS > $outfile + + export RUMP_SERVER=$SOCK_LOCAL + # There is no SA, so ping should fail + atf_check -s not-exit:0 -o ignore rump.ping -c 1 -n -w 3 $ip_peer + atf_check -s not-exit:0 -o ignore rump.ping -c 1 -n -w 3 $ip_peer2 + + add_sa $proto "$algo_args" $ip_local $ip_peer 100 10000 + + export RUMP_SERVER=$SOCK_LOCAL + # There is only an SA for $ip_peer, so ping to $ip_peer2 should fail + atf_check -s exit:0 -o ignore rump.ping -c 1 -n -w 3 $ip_peer + atf_check -s not-exit:0 -o ignore rump.ping -c 1 -n -w 3 $ip_peer2 + + add_sa $proto "$algo_args" $ip_local $ip_peer2 100 10010 + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 -o ignore rump.ping -c 1 -n -w 3 $ip_peer + atf_check -s exit:0 -o ignore rump.ping -c 1 -n -w 3 $ip_peer2 + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 -o match:"$proto/transport//require" \ + $HIJACKING setkey -D -P + # Check if the policy isn't modified accidentally + atf_check -s exit:0 -o not-match:"$proto/transport/.+\-.+/require" \ + $HIJACKING setkey -D -P + export RUMP_SERVER=$SOCK_PEER + atf_check -s exit:0 -o match:"$proto/transport//require" \ + $HIJACKING setkey -D -P + # Check if the policy isn't modified accidentally + atf_check -s exit:0 -o not-match:"$proto/transport/.+\-.+/require" \ + $HIJACKING setkey -D -P +} + +add_test_multiple_sa() +{ + local proto=$1 + local algo=$2 + local _algo=$(echo $algo | sed 's/-//g') + local name= desc= + + desc="Tests multiple SAs with $proto ($algo)" + name="ipsec_multiple_sa_${proto}_${_algo}" + + atf_test_case ${name} cleanup + eval " + ${name}_head() { + atf_set descr \"$desc\" + atf_set require.progs rump_server setkey + } + ${name}_body() { + test_multiple_sa $proto $algo + rump_server_destroy_ifaces + } + ${name}_cleanup() { + \$DEBUG && dump + cleanup + } + " + atf_add_test_case ${name} +} + +atf_init_test_cases() +{ + local algo= + + for algo in $ESP_ENCRYPTION_ALGORITHMS_MINIMUM; do + add_test_lifetime ipv4 esp $algo + add_test_lifetime ipv6 esp $algo + add_test_update esp $algo sa + add_test_update esp $algo sp + add_test_getspi_update esp $algo + add_test_spi esp $algo new delete + add_test_spi esp $algo old delete + add_test_spi esp $algo new timeout + add_test_spi esp $algo old timeout + add_test_nosa esp $algo + add_test_multiple_sa esp $algo + done + for algo in $AH_AUTHENTICATION_ALGORITHMS_MINIMUM; do + add_test_lifetime ipv4 ah $algo + add_test_lifetime ipv6 ah $algo + add_test_update ah $algo sa + add_test_update ah $algo sp + add_test_getspi_update ah $algo + add_test_spi ah $algo new delete + add_test_spi ah $algo old delete + add_test_spi ah $algo new timeout + add_test_spi ah $algo old timeout + add_test_nosa ah $algo + add_test_multiple_sa ah $algo + done +} diff --git a/net/ipsec/t_ipsec_natt.sh b/net/ipsec/t_ipsec_natt.sh new file mode 100644 --- /dev/null +++ b/net/ipsec/t_ipsec_natt.sh @@ -0,0 +1,492 @@ +# $NetBSD: t_ipsec_natt.sh,v 1.5 2020/06/05 03:24:58 knakahara Exp $ +# +# Copyright (c) 2017 Internet Initiative Japan Inc. +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +SOCK_LOCAL=unix://ipsec_natt_local +SOCK_NAT=unix://ipsec_natt_nat +SOCK_REMOTE=unix://ipsec_natt_remote +BUS_LOCAL=./bus_ipsec_natt_local +BUS_NAT=./bus_ipsec_natt_nat +BUS_REMOTE=./bus_ipsec_natt_remote +BUS_GLOBAL=./bus_ipsec_natt_global + +DEBUG=${DEBUG:-false} + +setup_servers_ipv4() +{ + + rump_server_crypto_start $SOCK_LOCAL netipsec + rump_server_npf_start $SOCK_NAT + rump_server_crypto_start $SOCK_REMOTE netipsec + rump_server_add_iface $SOCK_LOCAL shmif0 $BUS_LOCAL + rump_server_add_iface $SOCK_NAT shmif0 $BUS_LOCAL + rump_server_add_iface $SOCK_NAT shmif1 $BUS_NAT + rump_server_add_iface $SOCK_REMOTE shmif0 $BUS_NAT +} + +setup_servers_ipv6() +{ + + rump_server_crypto_start $SOCK_LOCAL netipsec netinet6 ipsec + rump_server_crypto_start $SOCK_REMOTE netipsec netinet6 ipsec + rump_server_add_iface $SOCK_LOCAL shmif0 $BUS_GLOBAL + rump_server_add_iface $SOCK_REMOTE shmif0 $BUS_GLOBAL +} + +setup_servers() +{ + local proto=$1 + + setup_servers_$proto +} + +setup_sp() +{ + local proto=$1 + local algo_args="$2" + local ip_local=$3 + local ip_remote=$4 + local ip_nat_remote=$5 + local tmpfile=./tmp + + export RUMP_SERVER=$SOCK_LOCAL + cat > $tmpfile <<-EOF + spdadd $ip_local $ip_remote any -P out ipsec $proto/transport//require; + spdadd $ip_remote $ip_local any -P in ipsec $proto/transport//require; + EOF + $DEBUG && cat $tmpfile + atf_check -s exit:0 -o empty $HIJACKING setkey -c < $tmpfile + #check_sp_entries $SOCK_LOCAL $ip_local $ip_remote + + export RUMP_SERVER=$SOCK_REMOTE + cat > $tmpfile <<-EOF + spdadd $ip_remote $ip_nat_remote any -P out ipsec $proto/transport//require; + spdadd $ip_local $ip_remote any -P in ipsec $proto/transport//require; + EOF + $DEBUG && cat $tmpfile + atf_check -s exit:0 -o empty $HIJACKING setkey -c < $tmpfile + #check_sp_entries $SOCK_REMOTE $ip_remote $ip_local +} + +add_sa() +{ + local proto=$1 + local algo_args="$2" + local ip_local=$3 + local ip_remote=$4 + local ip_nat_remote=$5 + local spi=$6 + local port=$7 + local tmpfile=./tmp + + export RUMP_SERVER=$SOCK_LOCAL + cat > $tmpfile <<-EOF + add $ip_local [4500] $ip_remote [4500] $proto $((spi)) $algo_args; + add $ip_remote [4500] $ip_local [4500] $proto $((spi + 1)) $algo_args; + EOF + $DEBUG && cat $tmpfile + atf_check -s exit:0 -o empty $HIJACKING setkey -c < $tmpfile + $DEBUG && $HIJACKING setkey -D + # XXX it can be expired if $lifetime is very short + #check_sa_entries $SOCK_LOCAL $ip_local $ip_remote + + export RUMP_SERVER=$SOCK_REMOTE + cat > $tmpfile <<-EOF + add $ip_local [$port] $ip_remote [4500] $proto $((spi)) $algo_args; + add $ip_remote [4500] $ip_nat_remote [$port] $proto $((spi + 1)) $algo_args; + EOF + $DEBUG && cat $tmpfile + atf_check -s exit:0 -o empty $HIJACKING setkey -c < $tmpfile + $DEBUG && $HIJACKING setkey -D + # XXX it can be expired if $lifetime is very short + #check_sa_entries $SOCK_PEER $ip_local $ip_remote +} + +prepare_file() +{ + local file=$1 + local data="0123456789" + + touch $file + for i in `seq 1 512` + do + echo $data >> $file + done +} + +build_npf_conf() +{ + local outfile=$1 + local localnet=$2 + + cat > $outfile <<-EOF + set bpf.jit off + \$int_if = inet4(shmif0) + \$ext_if = inet4(shmif1) + \$localnet = { $localnet } + map \$ext_if dynamic \$localnet -> \$ext_if + group "external" on \$ext_if { + pass stateful out final all + } + group "internal" on \$int_if { + block in all + pass in final from \$localnet + pass out final all + } + group default { + pass final on lo0 all + block all + } + EOF +} + +PIDSFILE=./terminator.pids +start_natt_terminator() +{ + local sock=$1 + local proto=$2 + local ip=$3 + local port=$4 + local pidsfile=$5 + local backup=$RUMP_SERVER + local pid= opt= + local terminator="$(atf_get_srcdir)/natt_terminator" + + if [ "$proto" = "ipv6" ]; then + opt="-6" + else + opt="-4" + fi + + export RUMP_SERVER=$sock + + env LD_PRELOAD=/usr/lib/librumphijack.so \ + $terminator $opt $ip $port & + pid=$! + if [ ! -f $PIDSFILE ]; then + touch $PIDSFILE + fi + echo $pid >> $PIDSFILE + + $DEBUG && rump.netstat -a -f inet + + export RUMP_SERVER=$backup + + sleep 1 +} + +stop_natt_terminators() +{ + local pid= + + if [ ! -f $PIDSFILE ]; then + return + fi + + for pid in $(cat $PIDSFILE); do + kill -9 $pid + done + rm -f $PIDSFILE +} + +test_ipsec_natt_transport_ipv4() +{ + local algo=$1 + local ip_local=10.0.1.2 + local ip_nat_local=10.0.1.1 + local ip_nat_remote=20.0.0.1 + local ip_remote=20.0.0.2 + local subnet_local=10.0.1.0 + local outfile=./out + local npffile=./npf.conf + local file_send=./file.send + local file_recv=./file.recv + local algo_args="$(generate_algo_args esp-udp $algo)" + local pid= port= + + setup_servers ipv4 + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 rump.sysctl -q -w net.inet.ip.dad_count=0 + atf_check -s exit:0 rump.ifconfig shmif0 $ip_local/24 + atf_check -s exit:0 -o ignore \ + rump.route -n add default $ip_nat_local + + export RUMP_SERVER=$SOCK_NAT + atf_check -s exit:0 rump.sysctl -q -w net.inet.ip.dad_count=0 + atf_check -s exit:0 rump.ifconfig shmif0 $ip_nat_local/24 + atf_check -s exit:0 rump.ifconfig shmif1 $ip_nat_remote/24 + atf_check -s exit:0 rump.sysctl -q -w net.inet.ip.forwarding=1 + + export RUMP_SERVER=$SOCK_REMOTE + atf_check -s exit:0 rump.sysctl -q -w net.inet.ip.dad_count=0 + atf_check -s exit:0 rump.ifconfig shmif0 $ip_remote/24 + atf_check -s exit:0 -o ignore \ + rump.route -n add -net $subnet_local $ip_nat_remote + + extract_new_packets $BUS_NAT > $outfile + + # There is no NAT/NAPT. ping should just work. + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 -o ignore rump.ping -c 1 -n -w 3 $ip_remote + + extract_new_packets $BUS_NAT > $outfile + $DEBUG && cat $outfile + atf_check -s exit:0 \ + -o match:"$ip_local > $ip_remote: ICMP echo request" \ + cat $outfile + atf_check -s exit:0 \ + -o match:"$ip_remote > $ip_local: ICMP echo reply" \ + cat $outfile + + # Setup an NAPT with npf + build_npf_conf $npffile "$subnet_local/24" + + export RUMP_SERVER=$SOCK_NAT + atf_check -s exit:0 $HIJACKING_NPF npfctl reload $npffile + atf_check -s exit:0 $HIJACKING_NPF npfctl start + $DEBUG && ${HIJACKING},"blanket=/dev/npf" npfctl show + + # There is an NAPT. ping works but source IP/port are translated + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 -o ignore rump.ping -c 1 -n -w 3 $ip_remote + + extract_new_packets $BUS_NAT > $outfile + $DEBUG && cat $outfile + atf_check -s exit:0 \ + -o match:"$ip_nat_remote > $ip_remote: ICMP echo request" \ + cat $outfile + atf_check -s exit:0 \ + -o match:"$ip_remote > $ip_nat_remote: ICMP echo reply" \ + cat $outfile + + # Try TCP communications just in case + start_nc_server $SOCK_REMOTE 4501 $file_recv ipv4 + prepare_file $file_send + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 $HIJACKING nc -w 3 $ip_remote 4501 < $file_send + atf_check -s exit:0 diff -q $file_send $file_recv + stop_nc_server + + extract_new_packets $BUS_NAT > $outfile + $DEBUG && cat $outfile + atf_check -s exit:0 \ + -o match:"${ip_nat_remote}\.[0-9]+ > ${ip_remote}\.4501" \ + cat $outfile + atf_check -s exit:0 \ + -o match:"${ip_remote}\.4501 > ${ip_nat_remote}\.[0-9]+" \ + cat $outfile + + # Launch a nc server as a terminator of NAT-T on outside the NAPT + start_natt_terminator $SOCK_REMOTE ipv4 $ip_remote 4500 + echo zzz > $file_send + + export RUMP_SERVER=$SOCK_LOCAL + # Send a UDP packet to the remote server at port 4500 from the local + # host of port 4500. This makes a mapping on the NAPT between them + atf_check -s exit:0 $HIJACKING \ + nc -u -w 3 -p 4500 $ip_remote 4500 < $file_send + # Launch a nc server as a terminator of NAT-T on inside the NAPT, + # taking over port 4500 of the local host. + start_natt_terminator $SOCK_LOCAL ipv4 $ip_local 4500 + + # We need to keep the servers for NAT-T + + export RUMP_SERVER=$SOCK_LOCAL + $DEBUG && rump.netstat -na -f inet + export RUMP_SERVER=$SOCK_REMOTE + $DEBUG && rump.netstat -na -f inet + + # Get a translated port number from 4500 on the NAPT + export RUMP_SERVER=$SOCK_NAT + $DEBUG && $HIJACKING_NPF npfctl list + # 10.0.1.2:4500 20.0.0.2:4500 via shmif1:9696 + port=$(get_natt_port $ip_local $ip_nat_remote) + $DEBUG && echo port=$port + if [ -z "$port" ]; then + atf_fail "Failed to get a translated port on NAPT" + fi + + # Create ESP-UDP IPsec connections + setup_sp esp "$algo_args" $ip_local $ip_remote $ip_nat_remote + add_sa "esp-udp" "$algo_args" $ip_local $ip_remote $ip_nat_remote 10000 $port + + # ping should still work + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 -o ignore rump.ping -c 1 -n -w 3 $ip_remote + + # Try TCP communications over the ESP-UDP connections + start_nc_server $SOCK_REMOTE 4501 $file_recv ipv4 + prepare_file $file_send + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 -o ignore $HIJACKING nc -w 3 $ip_remote 4501 < $file_send + atf_check -s exit:0 diff -q $file_send $file_recv + stop_nc_server + + # Check both ports and UDP encapsulation + extract_new_packets $BUS_NAT > $outfile + $DEBUG && cat $outfile + atf_check -s exit:0 \ + -o match:"${ip_nat_remote}\.$port > ${ip_remote}\.4500: UDP-encap" \ + cat $outfile + atf_check -s exit:0 \ + -o match:"${ip_remote}\.4500 > ${ip_nat_remote}\.$port: UDP-encap" \ + cat $outfile + + # Kill the NAT-T terminator + stop_natt_terminators +} + +test_ipsec_natt_transport_ipv6_without_nat() +{ + local algo=$1 + local ip_local_phys=fc00::1 + local ip_local_ipsecif=fc00:1111::1 + local ip_remote_phys=fc00::2 + local ip_remote_ipsecif=fc00:2222::1 + local outfile=./out + local npffile=./npf.conf + local file_send=./file.send + local file_recv=./file.recv + local algo_args="$(generate_algo_args esp-udp $algo)" + local pid= + local port=4500 + + setup_servers ipv6 + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 rump.sysctl -q -w net.inet6.ip6.dad_count=0 + atf_check -s exit:0 rump.ifconfig shmif0 inet6 $ip_local_phys/64 + + export RUMP_SERVER=$SOCK_REMOTE + atf_check -s exit:0 rump.sysctl -q -w net.inet6.ip6.dad_count=0 + atf_check -s exit:0 rump.ifconfig shmif0 inet6 $ip_remote_phys/64 + + extract_new_packets $BUS_GLOBAL > $outfile + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 -o ignore rump.ping6 -c 1 -n -X 3 $ip_remote_phys + + extract_new_packets $BUS_GLOBAL > $outfile + $DEBUG && cat $outfile + atf_check -s exit:0 \ + -o match:"$ip_local_phys > $ip_remote_phys: ICMP6, echo request" \ + cat $outfile + atf_check -s exit:0 \ + -o match:"$ip_remote_phys > $ip_local_phys: ICMP6, echo reply" \ + cat $outfile + + # Create ESP-UDP ipsecif(4) connections + export RUMP_SERVER=$SOCK_LOCAL + rump_server_add_iface $SOCK_LOCAL ipsec0 + atf_check -s exit:0 rump.ifconfig ipsec0 link0 # enable nat-t + atf_check -s exit:0 rump.ifconfig ipsec0 link2 # ensure IPv6 forward + atf_check -s exit:0 rump.ifconfig ipsec0 tunnel $ip_local_phys $ip_remote_phys + atf_check -s exit:0 rump.ifconfig ipsec0 inet6 $ip_local_ipsecif + atf_check -s exit:0 -o ignore \ + rump.route -n add -inet6 $ip_remote_ipsecif $ip_local_ipsecif + start_natt_terminator $SOCK_LOCAL ipv6 $ip_local_phys $port + + add_sa "esp-udp" "$algo_args" $ip_local_phys $ip_remote_phys \ + $ip_local_phys 10000 $port + + export RUMP_SERVER=$SOCK_REMOTE + rump_server_add_iface $SOCK_REMOTE ipsec0 + atf_check -s exit:0 rump.ifconfig ipsec0 link0 # enable nat-t + atf_check -s exit:0 rump.ifconfig ipsec0 link2 # ensure IPv6 forward + atf_check -s exit:0 rump.ifconfig ipsec0 tunnel $ip_remote_phys $ip_local_phys + atf_check -s exit:0 rump.ifconfig ipsec0 inet6 $ip_remote_ipsecif + atf_check -s exit:0 -o ignore \ + rump.route -n add -inet6 $ip_local_ipsecif $ip_remote_ipsecif + start_natt_terminator $SOCK_REMOTE ipv6 $ip_remote_phys $port + + # ping should still work + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 -o ignore rump.ping6 -c 1 -n -X 5 $ip_remote_ipsecif + + # Check UDP encapsulation + extract_new_packets $BUS_GLOBAL > $outfile + $DEBUG && cat $outfile + + atf_check -s exit:0 \ + -o match:"${ip_local_phys}\.$port > ${ip_remote_phys}\.4500: UDP-encap" \ + cat $outfile + atf_check -s exit:0 \ + -o match:"${ip_remote_phys}\.4500 > ${ip_local_phys}\.$port: UDP-encap" \ + cat $outfile + + # Kill the NAT-T terminator + stop_natt_terminators + export RUMP_SERVER=$SOCK_REMOTE + stop_natt_terminators +} + +test_ipsec_natt_transport_ipv6() +{ + local algo=$1 + + test_ipsec_natt_transport_ipv6_without_nat $algo +} + +add_test_ipsec_natt_transport() +{ + local proto=$1 + local algo=$2 + local _algo=$(echo $algo | sed 's/-//g') + local name= desc= + + desc="Test IPsec $proto NAT-T ($algo)" + name="ipsec_natt_transport_${proto}_${_algo}" + + atf_test_case ${name} cleanup + eval " + ${name}_head() { + atf_set descr \"$desc\" + atf_set require.progs rump_server setkey nc + } + ${name}_body() { + test_ipsec_natt_transport_$proto $algo + rump_server_destroy_ifaces + } + ${name}_cleanup() { + stop_nc_server + stop_natt_terminators + \$DEBUG && dump + cleanup + } + " + atf_add_test_case ${name} +} + +atf_init_test_cases() +{ + local algo= + + for algo in $ESP_ENCRYPTION_ALGORITHMS_MINIMUM; do + add_test_ipsec_natt_transport ipv4 $algo + add_test_ipsec_natt_transport ipv6 $algo + done +} diff --git a/net/ipsec/t_ipsec_sockopt.sh b/net/ipsec/t_ipsec_sockopt.sh new file mode 100644 --- /dev/null +++ b/net/ipsec/t_ipsec_sockopt.sh @@ -0,0 +1,390 @@ +# $NetBSD: t_ipsec_sockopt.sh,v 1.2 2017/08/03 03:16:27 ozaki-r Exp $ +# +# Copyright (c) 2017 Internet Initiative Japan Inc. +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +SOCK_LOCAL=unix://ipsec_local +SOCK_PEER=unix://ipsec_peer +BUS=./bus_ipsec + +DEBUG=${DEBUG:-false} + +check_packets() +{ + local outfile=$1 + local src=$2 + local dst=$3 + local pktproto_out=$4 + local pktproto_in=${5:-$4} + + atf_check -s exit:0 -o match:"$src > $dst: $pktproto_out" cat $outfile + atf_check -s exit:0 -o match:"$dst > $src: $pktproto_in" cat $outfile +} + +test_ipsec4_IP_IPSEC_POLICY() +{ + local proto=$1 + local algo=$2 + local ip_local=10.0.0.1 + local ip_peer=10.0.0.2 + local tmpfile=./tmp + local outfile=./out + local pktproto=$(generate_pktproto $proto) + local algo_args="$(generate_algo_args $proto $algo)" + local pktsizeopt= + local pingopt= pingopt2= + + rump_server_crypto_start $SOCK_LOCAL netipsec + rump_server_crypto_start $SOCK_PEER netipsec + rump_server_add_iface $SOCK_LOCAL shmif0 $BUS + rump_server_add_iface $SOCK_PEER shmif0 $BUS + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 rump.ifconfig shmif0 $ip_local/24 + atf_check -s exit:0 rump.ifconfig -w 10 + + export RUMP_SERVER=$SOCK_PEER + atf_check -s exit:0 rump.ifconfig shmif0 $ip_peer/24 + atf_check -s exit:0 rump.ifconfig -w 10 + + extract_new_packets $BUS > $outfile + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 -o ignore rump.ping -c 1 -n -w 3 $ip_peer + + extract_new_packets $BUS > $outfile + check_packets $outfile $ip_local $ip_peer ICMP + + pingopt_out="out ipsec $proto/transport//require" + pingopt_in="in ipsec $proto/transport//require" + + atf_check -s not-exit:0 -o ignore \ + rump.ping -c 1 -n -w 3 -E "$pingopt_out" $ip_peer + + # Setup only SAs + export RUMP_SERVER=$SOCK_LOCAL + cat > $tmpfile <<-EOF + add $ip_local $ip_peer $proto 10000 $algo_args; + add $ip_peer $ip_local $proto 10001 $algo_args; + EOF + $DEBUG && cat $tmpfile + atf_check -s exit:0 -o empty $HIJACKING setkey -c < $tmpfile + check_sa_entries $SOCK_LOCAL $ip_local $ip_peer + + export RUMP_SERVER=$SOCK_PEER + cat > $tmpfile <<-EOF + add $ip_local $ip_peer $proto 10000 $algo_args; + add $ip_peer $ip_local $proto 10001 $algo_args; + EOF + $DEBUG && cat $tmpfile + atf_check -s exit:0 -o empty $HIJACKING setkey -c < $tmpfile + check_sa_entries $SOCK_PEER $ip_local $ip_peer + + export RUMP_SERVER=$SOCK_LOCAL + if [ $proto = ipcomp ]; then + pktsizeopt="-s $(($(get_minlen $algo) - 8)) -p ff" + fi + + # The outgoing packet is matched and IPsec is applied + # The reply packet isn't applied + atf_check -s exit:0 -o ignore \ + rump.ping -c 1 -n -w 3 $pktsizeopt -E "$pingopt_out" $ip_peer + extract_new_packets $BUS > $outfile + check_packets $outfile $ip_local $ip_peer $pktproto "ICMP" + + if [ $proto = ipcomp ]; then + # The outgoing packet is matched and IPsec is applied + # The reply packet isn't applied but IPComp doesn't care + atf_check -s exit:0 -o ignore \ + rump.ping -c 1 -n -w 3 $pktsizeopt \ + -E "$pingopt_out" -E "$pingopt_in" $ip_peer + extract_new_packets $BUS > $outfile + check_packets $outfile $ip_local $ip_peer $pktproto "ICMP" + else + # The outgoing packet is matched and IPsec is applied + # The reply packet isn't applied but matched then discarded + atf_check -s not-exit:0 -o ignore \ + rump.ping -c 1 -n -w 3 $pktsizeopt \ + -E "$pingopt_out" -E "$pingopt_in" $ip_peer + fi + + # Setup an SP only on the source node + export RUMP_SERVER=$SOCK_LOCAL + cat > $tmpfile <<-EOF + spdadd $ip_local $ip_peer any -P out ipsec $proto/transport//require; + EOF + $DEBUG && cat $tmpfile + atf_check -s exit:0 -o empty $HIJACKING setkey -c < $tmpfile + + # The outgoing packet is matched and IPsec is applied + # The reply packet isn't applied and thus discarded + atf_check -s exit:0 -o ignore \ + rump.ping -c 1 -n -w 3 $pktsizeopt -E "$pingopt_out" $ip_peer + extract_new_packets $BUS > $outfile + check_packets $outfile $ip_local $ip_peer $pktproto "ICMP" + if [ $proto = ipcomp ]; then + # The outgoing packet is matched and IPsec is applied + # The reply packet isn't applied but IPComp doesn't care + atf_check -s exit:0 -o ignore \ + rump.ping -c 1 -n -w 3 $pktsizeopt \ + -E "$pingopt_out" -E "$pingopt_in" $ip_peer + extract_new_packets $BUS > $outfile + check_packets $outfile $ip_local $ip_peer $pktproto "ICMP" + else + # The outgoing packet is matched and IPsec is applied + # The reply packet isn't applied but matched then discarded + atf_check -s not-exit:0 -o ignore \ + rump.ping -c 1 -n -w 3 $pktsizeopt \ + -E "$pingopt_out" -E "$pingopt_in" $ip_peer + fi + + # Setup SPs on the both nodes + export RUMP_SERVER=$SOCK_PEER + cat > $tmpfile <<-EOF + spdadd $ip_peer $ip_local any -P out ipsec $proto/transport//require; + EOF + $DEBUG && cat $tmpfile + atf_check -s exit:0 -o empty $HIJACKING setkey -c < $tmpfile + + export RUMP_SERVER=$SOCK_LOCAL + # The outgoing packet is matched and IPsec is applied + # The reply packet is matched and IPsec is applied + atf_check -s exit:0 -o ignore \ + rump.ping -c 1 -n -w 3 $pktsizeopt -E "$pingopt_out" $ip_peer + extract_new_packets $BUS > $outfile + check_packets $outfile $ip_local $ip_peer $pktproto + # The outgoing packet is matched and IPsec is applied + # The reply packet is matched and IPsec is applied + atf_check -s exit:0 -o ignore \ + rump.ping -c 1 -n -w 3 $pktsizeopt -E "$pingopt_in" $ip_peer + extract_new_packets $BUS > $outfile + check_packets $outfile $ip_local $ip_peer $pktproto + + test_flush_entries $SOCK_LOCAL + test_flush_entries $SOCK_PEER +} + +test_ipsec6_IP_IPSEC_POLICY() +{ + local proto=$1 + local algo=$2 + local ip_local=fd00::1 + local ip_peer=fd00::2 + local tmpfile=./tmp + local outfile=./out + local pktproto=$(generate_pktproto $proto) + local algo_args="$(generate_algo_args $proto $algo)" + local pktsizeopt= + local pingopt= pingopt2= + + rump_server_crypto_start $SOCK_LOCAL netinet6 netipsec + rump_server_crypto_start $SOCK_PEER netinet6 netipsec + rump_server_add_iface $SOCK_LOCAL shmif0 $BUS + rump_server_add_iface $SOCK_PEER shmif0 $BUS + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 rump.ifconfig shmif0 inet6 $ip_local + atf_check -s exit:0 rump.ifconfig -w 10 + + export RUMP_SERVER=$SOCK_PEER + atf_check -s exit:0 rump.ifconfig shmif0 inet6 $ip_peer + atf_check -s exit:0 rump.ifconfig -w 10 + + extract_new_packets $BUS > $outfile + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 -o ignore rump.ping6 -c 1 -n -X 3 $ip_peer + + extract_new_packets $BUS > $outfile + check_packets $outfile $ip_local $ip_peer ICMP6 + + pingopt_out="out ipsec $proto/transport//require" + pingopt_in="in ipsec $proto/transport//require" + + atf_check -s not-exit:0 -o ignore \ + rump.ping6 -c 1 -n -X 3 -P "$pingopt_out" $ip_peer + + # Setup only SAs + export RUMP_SERVER=$SOCK_LOCAL + cat > $tmpfile <<-EOF + add $ip_local $ip_peer $proto 10000 $algo_args; + add $ip_peer $ip_local $proto 10001 $algo_args; + EOF + $DEBUG && cat $tmpfile + atf_check -s exit:0 -o empty $HIJACKING setkey -c < $tmpfile + check_sa_entries $SOCK_LOCAL $ip_local $ip_peer + + export RUMP_SERVER=$SOCK_PEER + cat > $tmpfile <<-EOF + add $ip_local $ip_peer $proto 10000 $algo_args; + add $ip_peer $ip_local $proto 10001 $algo_args; + EOF + $DEBUG && cat $tmpfile + atf_check -s exit:0 -o empty $HIJACKING setkey -c < $tmpfile + check_sa_entries $SOCK_PEER $ip_local $ip_peer + + export RUMP_SERVER=$SOCK_LOCAL + if [ $proto = ipcomp ]; then + pktsizeopt="-s $(($(get_minlen $algo) - 8)) -p ff" + fi + + # The outgoing packet is matched and IPsec is applied + # The reply packet isn't applied + atf_check -s exit:0 -o ignore \ + rump.ping6 -c 1 -n -X 3 $pktsizeopt -P "$pingopt_out" $ip_peer + extract_new_packets $BUS > $outfile + check_packets $outfile $ip_local $ip_peer $pktproto "ICMP6" + + if [ $proto = ipcomp ]; then + # The outgoing packet is matched and IPsec is applied + # The reply packet isn't applied but IPComp doesn't care + atf_check -s exit:0 -o ignore \ + rump.ping6 -c 1 -n -X 3 $pktsizeopt \ + -P "$pingopt_out" -P "$pingopt_in" $ip_peer + extract_new_packets $BUS > $outfile + check_packets $outfile $ip_local $ip_peer $pktproto "ICMP6" + else + # The outgoing packet is matched and IPsec is applied + # The reply packet isn't applied but matched then discarded + atf_check -s not-exit:0 -o ignore \ + rump.ping6 -c 1 -n -X 3 $pktsizeopt \ + -P "$pingopt_out" -P "$pingopt_in" $ip_peer + fi + + # Setup an SP only on the source node + export RUMP_SERVER=$SOCK_LOCAL + cat > $tmpfile <<-EOF + spdadd $ip_local $ip_peer any -P out ipsec $proto/transport//require; + EOF + $DEBUG && cat $tmpfile + atf_check -s exit:0 -o empty $HIJACKING setkey -c < $tmpfile + + # The outgoing packet is matched and IPsec is applied + # The reply packet isn't applied and thus discarded + atf_check -s exit:0 -o ignore \ + rump.ping6 -c 1 -n -X 3 $pktsizeopt -P "$pingopt_out" $ip_peer + extract_new_packets $BUS > $outfile + check_packets $outfile $ip_local $ip_peer $pktproto "ICMP6" + if [ $proto = ipcomp ]; then + # The outgoing packet is matched and IPsec is applied + # The reply packet isn't applied but IPComp doesn't care + atf_check -s exit:0 -o ignore \ + rump.ping6 -c 1 -n -X 3 $pktsizeopt \ + -P "$pingopt_out" -P "$pingopt_in" $ip_peer + extract_new_packets $BUS > $outfile + check_packets $outfile $ip_local $ip_peer $pktproto "ICMP6" + else + # The outgoing packet is matched and IPsec is applied + # The reply packet isn't applied but matched then discarded + atf_check -s not-exit:0 -o ignore \ + rump.ping6 -c 1 -n -X 3 $pktsizeopt \ + -P "$pingopt_out" -P "$pingopt_in" $ip_peer + fi + + # Setup SPs on the both nodes + export RUMP_SERVER=$SOCK_PEER + cat > $tmpfile <<-EOF + spdadd $ip_peer $ip_local any -P out ipsec $proto/transport//require; + EOF + $DEBUG && cat $tmpfile + atf_check -s exit:0 -o empty $HIJACKING setkey -c < $tmpfile + + export RUMP_SERVER=$SOCK_LOCAL + # The outgoing packet is matched and IPsec is applied + # The reply packet is matched and IPsec is applied + atf_check -s exit:0 -o ignore \ + rump.ping6 -c 1 -n -X 3 $pktsizeopt -P "$pingopt_out" $ip_peer + extract_new_packets $BUS > $outfile + check_packets $outfile $ip_local $ip_peer $pktproto + # The outgoing packet is matched and IPsec is applied + # The reply packet is matched and IPsec is applied + atf_check -s exit:0 -o ignore \ + rump.ping6 -c 1 -n -X 3 $pktsizeopt -P "$pingopt_in" $ip_peer + extract_new_packets $BUS > $outfile + check_packets $outfile $ip_local $ip_peer $pktproto + + test_flush_entries $SOCK_LOCAL + test_flush_entries $SOCK_PEER +} + +test_IP_IPSEC_POLICY_common() +{ + local ipproto=$1 + local proto=$2 + local algo=$3 + + if [ $ipproto = ipv4 ]; then + test_ipsec4_IP_IPSEC_POLICY $proto $algo + else + test_ipsec6_IP_IPSEC_POLICY $proto $algo + fi +} + +add_test_IP_IPSEC_POLICY() +{ + local ipproto=$1 + local proto=$2 + local algo=$3 + local _algo=$(echo $algo | sed 's/-//g') + local name= desc= + + name="ipsec_IP_IPSEC_POLICY_${ipproto}_${proto}_${_algo}" + desc="Tests of IP_IPSEC_POLICY socket option (${ipproto}, ${proto}, ${_algo})" + + atf_test_case ${name} cleanup + eval " + ${name}_head() { + atf_set descr \"$desc\" + atf_set require.progs rump_server setkey + } + ${name}_body() { + test_IP_IPSEC_POLICY_common $ipproto $proto $algo + rump_server_destroy_ifaces + } + ${name}_cleanup() { + \$DEBUG && dump + cleanup + } + " + atf_add_test_case ${name} +} + +atf_init_test_cases() +{ + local algo= + + for algo in $ESP_ENCRYPTION_ALGORITHMS_MINIMUM; do + add_test_IP_IPSEC_POLICY ipv4 esp $algo + add_test_IP_IPSEC_POLICY ipv6 esp $algo + done + for algo in $AH_AUTHENTICATION_ALGORITHMS_MINIMUM; do + add_test_IP_IPSEC_POLICY ipv4 ah $algo + add_test_IP_IPSEC_POLICY ipv6 ah $algo + done + for algo in $IPCOMP_COMPRESSION_ALGORITHMS_MINIMUM; do + add_test_IP_IPSEC_POLICY ipv4 ipcomp $algo + add_test_IP_IPSEC_POLICY ipv6 ipcomp $algo + done +} diff --git a/net/ipsec/t_ipsec_sysctl.sh b/net/ipsec/t_ipsec_sysctl.sh new file mode 100644 --- /dev/null +++ b/net/ipsec/t_ipsec_sysctl.sh @@ -0,0 +1,161 @@ +# $NetBSD: t_ipsec_sysctl.sh,v 1.1 2017/04/14 02:56:49 ozaki-r Exp $ +# +# Copyright (c) 2017 Internet Initiative Japan Inc. +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +DEBUG=${DEBUG:-false} + +atf_test_case ipsec_sysctl0 cleanup +ipsec_sysctl0_head() +{ + + atf_set "descr" "Tests of sysctl entries of IPsec without ipsec.so" + atf_set "require.progs" "rump_server" +} + +ipsec_sysctl0_body() +{ + local sock=unix://ipsec_sysctl + + rump_server_crypto_start $sock + + export RUMP_SERVER=$sock + atf_check -s not-exit:0 -e match:'invalid' \ + rump.sysctl net.inet.ipsec.enabled + atf_check -s not-exit:0 -e match:'invalid' \ + rump.sysctl net.inet6.ipsec6.enabled +} + +ipsec_sysctl0_cleanup() +{ + + $DEBUG && dump + cleanup +} + +atf_test_case ipsec_sysctl4 cleanup +ipsec_sysctl4_head() +{ + + atf_set "descr" "Tests of sysctl entries of IPsec without netinet6.so" + atf_set "require.progs" "rump_server" +} + +ipsec_sysctl4_body() +{ + local sock=unix://ipsec_sysctl + + rump_server_crypto_start $sock netipsec + + export RUMP_SERVER=$sock + atf_check -s exit:0 -o match:'= 1' rump.sysctl net.inet.ipsec.enabled + # net.inet6.ipsec6 entries exit regardless of netinet6 + # net.inet6.ipsec6.enabled always equals net.inet.ipsec.enabled + atf_check -s exit:0 -o match:'= 1' rump.sysctl net.inet6.ipsec6.enabled + + atf_check -s exit:0 -o match:'= 0' rump.sysctl net.inet.ipsec.used + # net.inet6.ipsec6.used always equals net.inet.ipsec.used + atf_check -s exit:0 -o match:'= 0' rump.sysctl net.inet6.ipsec6.used + + # Add an SAD entry for IPv4 + atf_check -s exit:0 -o empty $HIJACKING setkey -c <<-EOF + add 10.0.0.1 10.0.0.2 esp 9876 -E 3des-cbc "hogehogehogehogehogehoge"; + EOF + $DEBUG && $HIJACKING setkey -D + + atf_check -s exit:0 -o match:'= 0' rump.sysctl net.inet.ipsec.used + atf_check -s exit:0 -o match:'= 0' rump.sysctl net.inet6.ipsec6.used + + # Add an SPD entry for IPv4, which activates the IPsec function + atf_check -s exit:0 -o empty $HIJACKING setkey -c <<-EOF + spdadd 10.0.0.1 10.0.0.2 any -P out ipsec esp/transport//use; + EOF + $DEBUG && $HIJACKING setkey -D + + atf_check -s exit:0 -o match:'= 1' rump.sysctl net.inet.ipsec.used + atf_check -s exit:0 -o match:'= 1' rump.sysctl net.inet6.ipsec6.used +} + +ipsec_sysctl4_cleanup() +{ + + $DEBUG && dump + cleanup +} + +atf_test_case ipsec_sysctl6 cleanup +ipsec_sysctl6_head() +{ + + atf_set "descr" "Tests of sysctl entries of IPsec" + atf_set "require.progs" "rump_server" +} + +ipsec_sysctl6_body() +{ + local sock=unix://ipsec_sysctl + + rump_server_crypto_start $sock netinet6 netipsec + + export RUMP_SERVER=$sock + atf_check -s exit:0 -o match:'= 1' rump.sysctl net.inet.ipsec.enabled + atf_check -s exit:0 -o match:'= 1' rump.sysctl net.inet6.ipsec6.enabled + + atf_check -s exit:0 -o match:'= 0' rump.sysctl net.inet.ipsec.used + atf_check -s exit:0 -o match:'= 0' rump.sysctl net.inet6.ipsec6.used + + # Add an SAD entry for IPv6 + atf_check -s exit:0 -o empty $HIJACKING setkey -c <<-EOF + add fd00::1 fd00::2 esp 9876 -E 3des-cbc "hogehogehogehogehogehoge"; + EOF + $DEBUG && $HIJACKING setkey -D + + atf_check -s exit:0 -o match:'= 0' rump.sysctl net.inet.ipsec.used + atf_check -s exit:0 -o match:'= 0' rump.sysctl net.inet6.ipsec6.used + + # Add an SPD entry for IPv6, which activates the IPsec function + atf_check -s exit:0 -o empty $HIJACKING setkey -c <<-EOF + spdadd fd00::1 fd00::2 any -P out ipsec esp/transport//use; + EOF + $DEBUG && $HIJACKING setkey -D + + atf_check -s exit:0 -o match:'= 1' rump.sysctl net.inet.ipsec.used + atf_check -s exit:0 -o match:'= 1' rump.sysctl net.inet6.ipsec6.used +} + +ipsec_sysctl6_cleanup() +{ + + $DEBUG && dump + cleanup +} + +atf_init_test_cases() +{ + + atf_add_test_case ipsec_sysctl0 + atf_add_test_case ipsec_sysctl4 + atf_add_test_case ipsec_sysctl6 +} diff --git a/net/ipsec/t_ipsec_tcp.sh b/net/ipsec/t_ipsec_tcp.sh new file mode 100644 --- /dev/null +++ b/net/ipsec/t_ipsec_tcp.sh @@ -0,0 +1,299 @@ +# $NetBSD: t_ipsec_tcp.sh,v 1.2 2017/08/03 03:16:27 ozaki-r Exp $ +# +# Copyright (c) 2017 Internet Initiative Japan Inc. +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +SOCK_LOCAL=unix://ipsec_local +SOCK_PEER=unix://ipsec_peer +BUS=./bus_ipsec + +DEBUG=${DEBUG:-true} + +setup_sasp() +{ + local proto=$1 + local algo_args="$2" + local ip_local=$3 + local ip_peer=$4 + local tmpfile=./tmp + local extra= + + export RUMP_SERVER=$SOCK_LOCAL + cat > $tmpfile <<-EOF + add $ip_local $ip_peer $proto 10000 $algo_args; + add $ip_peer $ip_local $proto 10001 $algo_args; + spdadd $ip_local $ip_peer any -P out ipsec $proto/transport//require; + $extra + EOF + $DEBUG && cat $tmpfile + atf_check -s exit:0 -o empty $HIJACKING setkey -c < $tmpfile + # XXX it can be expired if $lifetime is very short + #check_sa_entries $SOCK_LOCAL $ip_local $ip_peer + + export RUMP_SERVER=$SOCK_PEER + cat > $tmpfile <<-EOF + add $ip_local $ip_peer $proto 10000 $algo_args; + add $ip_peer $ip_local $proto 10001 $algo_args; + spdadd $ip_peer $ip_local any -P out ipsec $proto/transport//require; + $extra + EOF + $DEBUG && cat $tmpfile + atf_check -s exit:0 -o empty $HIJACKING setkey -c < $tmpfile + # XXX it can be expired if $lifetime is very short + #check_sa_entries $SOCK_PEER $ip_local $ip_peer +} + +prepare_file() +{ + local file=$1 + local data="0123456789" + + touch $file + for i in `seq 1 512` + do + echo $data >> $file + done +} + +test_tcp() +{ + local local_proto=$1 + local ip_local=$2 + local peer_proto=$3 + local ip_peer=$4 + local port=1234 + local file_send=./file.send + local file_recv=./file.recv + local opts= + + if [ $local_proto = ipv4 ]; then + opts="-N -w 3 -4" + else + opts="-N -w 3 -6" + fi + + # Start nc server + start_nc_server $SOCK_PEER $port $file_recv $peer_proto + + export RUMP_SERVER=$SOCK_LOCAL + # Send a file to the server + prepare_file $file_send + atf_check -s exit:0 $HIJACKING nc $opts $ip_peer $port < $file_send + + # Check if the file is transferred correctly + atf_check -s exit:0 diff -q $file_send $file_recv + + stop_nc_server + rm -f $file_send $file_recv +} + +test_tcp_ipv4() +{ + local proto=$1 + local algo=$2 + local ip_local=10.0.0.1 + local ip_peer=10.0.0.2 + local algo_args="$(generate_algo_args $proto $algo)" + local proto_cap=$(echo $proto | tr 'a-z' 'A-Z') + local outfile=./out + + rump_server_crypto_start $SOCK_LOCAL netipsec + rump_server_crypto_start $SOCK_PEER netipsec + rump_server_add_iface $SOCK_LOCAL shmif0 $BUS + rump_server_add_iface $SOCK_PEER shmif0 $BUS + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 rump.ifconfig shmif0 $ip_local/24 + atf_check -s exit:0 rump.ifconfig -w 10 + + export RUMP_SERVER=$SOCK_PEER + atf_check -s exit:0 rump.ifconfig shmif0 $ip_peer/24 + atf_check -s exit:0 rump.ifconfig -w 10 + + if [ $proto != none ]; then + setup_sasp $proto "$algo_args" $ip_local $ip_peer + fi + + extract_new_packets $BUS > $outfile + + test_tcp ipv4 $ip_local ipv4 $ip_peer + + extract_new_packets $BUS > $outfile + $DEBUG && cat $outfile + + if [ $proto != none ]; then + atf_check -s exit:0 \ + -o match:"$ip_local > $ip_peer: $proto_cap" \ + cat $outfile + atf_check -s exit:0 \ + -o match:"$ip_peer > $ip_local: $proto_cap" \ + cat $outfile + fi +} + +test_tcp_ipv6() +{ + local proto=$1 + local algo=$2 + local ip_local=fd00::1 + local ip_peer=fd00::2 + local algo_args="$(generate_algo_args $proto $algo)" + local proto_cap=$(echo $proto | tr 'a-z' 'A-Z') + local outfile=./out + + rump_server_crypto_start $SOCK_LOCAL netinet6 netipsec + rump_server_crypto_start $SOCK_PEER netinet6 netipsec + rump_server_add_iface $SOCK_LOCAL shmif0 $BUS + rump_server_add_iface $SOCK_PEER shmif0 $BUS + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 rump.ifconfig shmif0 inet6 $ip_local + atf_check -s exit:0 rump.ifconfig -w 10 + + export RUMP_SERVER=$SOCK_PEER + atf_check -s exit:0 rump.ifconfig shmif0 inet6 $ip_peer + atf_check -s exit:0 rump.ifconfig -w 10 + + if [ $proto != none ]; then + setup_sasp $proto "$algo_args" $ip_local $ip_peer + fi + + extract_new_packets $BUS > $outfile + + test_tcp ipv6 $ip_local ipv6 $ip_peer + + extract_new_packets $BUS > $outfile + $DEBUG && cat $outfile + + if [ $proto != none ]; then + atf_check -s exit:0 \ + -o match:"$ip_local > $ip_peer: $proto_cap" \ + cat $outfile + atf_check -s exit:0 \ + -o match:"$ip_peer > $ip_local: $proto_cap" \ + cat $outfile + fi +} + +test_tcp_ipv4mappedipv6() +{ + local proto=$1 + local algo=$2 + local ip_local=10.0.0.1 + local ip_peer=10.0.0.2 + local ip6_peer=::ffff:10.0.0.2 + local algo_args="$(generate_algo_args $proto $algo)" + local proto_cap=$(echo $proto | tr 'a-z' 'A-Z') + local outfile=./out + + rump_server_crypto_start $SOCK_LOCAL netipsec + rump_server_crypto_start $SOCK_PEER netipsec netinet6 + rump_server_add_iface $SOCK_LOCAL shmif0 $BUS + rump_server_add_iface $SOCK_PEER shmif0 $BUS + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 rump.ifconfig shmif0 $ip_local/24 + atf_check -s exit:0 rump.ifconfig -w 10 + + export RUMP_SERVER=$SOCK_PEER + atf_check -s exit:0 -o ignore rump.sysctl -w net.inet6.ip6.v6only=0 + atf_check -s exit:0 rump.ifconfig shmif0 $ip_peer/24 + atf_check -s exit:0 rump.ifconfig shmif0 inet6 $ip6_peer/96 + atf_check -s exit:0 rump.ifconfig -w 10 + + if [ $proto != none ]; then + setup_sasp $proto "$algo_args" $ip_local $ip_peer 100 + fi + + extract_new_packets $BUS > $outfile + + test_tcp ipv4 $ip_local ipv6 $ip_peer + + extract_new_packets $BUS > $outfile + $DEBUG && cat $outfile + + if [ $proto != none ]; then + atf_check -s exit:0 \ + -o match:"$ip_local > $ip_peer: $proto_cap" \ + cat $outfile + atf_check -s exit:0 \ + -o match:"$ip_peer > $ip_local: $proto_cap" \ + cat $outfile + fi +} + +add_test_tcp() +{ + local ipproto=$1 + local proto=$2 + local algo=$3 + local _algo=$(echo $algo | sed 's/-//g') + local name= desc= + + if [ $proto = none ]; then + desc="Tests of TCP with IPsec enabled ($ipproto)" + name="ipsec_tcp_${ipproto}_${proto}" + else + desc="Tests of TCP with IPsec ($ipproto) $proto $algo" + name="ipsec_tcp_${ipproto}_${proto}_${_algo}" + fi + + atf_test_case ${name} cleanup + eval " + ${name}_head() { + atf_set descr \"$desc\" + atf_set require.progs rump_server setkey + } + ${name}_body() { + test_tcp_${ipproto} $proto $algo + rump_server_destroy_ifaces + } + ${name}_cleanup() { + \$DEBUG && dump + cleanup + } + " + atf_add_test_case ${name} +} + +atf_init_test_cases() +{ + local algo= + + for algo in $ESP_ENCRYPTION_ALGORITHMS_MINIMUM; do + add_test_tcp ipv4 esp $algo + add_test_tcp ipv6 esp $algo + add_test_tcp ipv4mappedipv6 esp $algo + done + for algo in $AH_AUTHENTICATION_ALGORITHMS_MINIMUM; do + add_test_tcp ipv4 ah $algo + add_test_tcp ipv6 ah $algo + add_test_tcp ipv4mappedipv6 ah $algo + done + + add_test_tcp ipv4 none + add_test_tcp ipv6 none + add_test_tcp ipv4mappedipv6 none +} diff --git a/net/ipsec/t_ipsec_transport.sh b/net/ipsec/t_ipsec_transport.sh new file mode 100644 --- /dev/null +++ b/net/ipsec/t_ipsec_transport.sh @@ -0,0 +1,276 @@ +# $NetBSD: t_ipsec_transport.sh,v 1.6 2017/08/03 03:16:27 ozaki-r Exp $ +# +# Copyright (c) 2017 Internet Initiative Japan Inc. +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +SOCK_LOCAL=unix://ipsec_local +SOCK_PEER=unix://ipsec_peer +BUS=./bus_ipsec + +DEBUG=${DEBUG:-false} + +check_packets() +{ + local outfile=$1 + local src=$2 + local dst=$3 + local pktproto=$4 + + atf_check -s exit:0 -o match:"$src > $dst: $pktproto" cat $outfile + atf_check -s exit:0 -o match:"$dst > $src: $pktproto" cat $outfile +} + +test_ipsec4_transport() +{ + local proto=$1 + local algo=$2 + local ip_local=10.0.0.1 + local ip_peer=10.0.0.2 + local tmpfile=./tmp + local outfile=./out + local pktproto=$(generate_pktproto $proto) + local algo_args="$(generate_algo_args $proto $algo)" + local pktsize= + + rump_server_crypto_start $SOCK_LOCAL netipsec + rump_server_crypto_start $SOCK_PEER netipsec + rump_server_add_iface $SOCK_LOCAL shmif0 $BUS + rump_server_add_iface $SOCK_PEER shmif0 $BUS + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 rump.ifconfig shmif0 $ip_local/24 + atf_check -s exit:0 rump.ifconfig -w 10 + + export RUMP_SERVER=$SOCK_PEER + atf_check -s exit:0 rump.ifconfig shmif0 $ip_peer/24 + atf_check -s exit:0 rump.ifconfig -w 10 + + extract_new_packets $BUS > $outfile + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 -o ignore rump.ping -c 1 -n -w 3 $ip_peer + + extract_new_packets $BUS > $outfile + atf_check -s exit:0 -o match:"$ip_local > $ip_peer: ICMP echo request" \ + cat $outfile + atf_check -s exit:0 -o match:"$ip_peer > $ip_local: ICMP echo reply" \ + cat $outfile + + export RUMP_SERVER=$SOCK_LOCAL + # from https://www.netbsd.org/docs/network/ipsec/ + cat > $tmpfile <<-EOF + add $ip_local $ip_peer $proto 10000 $algo_args; + add $ip_peer $ip_local $proto 10001 $algo_args; + spdadd $ip_local $ip_peer any -P out ipsec $proto/transport//require; + EOF + $DEBUG && cat $tmpfile + atf_check -s exit:0 -o empty $HIJACKING setkey -c < $tmpfile + check_sa_entries $SOCK_LOCAL $ip_local $ip_peer + + export RUMP_SERVER=$SOCK_PEER + cat > $tmpfile <<-EOF + add $ip_local $ip_peer $proto 10000 $algo_args; + add $ip_peer $ip_local $proto 10001 $algo_args; + spdadd $ip_peer $ip_local any -P out ipsec $proto/transport//require; + EOF + $DEBUG && cat $tmpfile + atf_check -s exit:0 -o empty $HIJACKING setkey -c < $tmpfile + check_sa_entries $SOCK_PEER $ip_local $ip_peer + + export RUMP_SERVER=$SOCK_LOCAL + if [ $proto = ipcomp ]; then + # IPComp sends a packet as-is if a compressed payload of + # the packet is greater than or equal to the original payload. + # So we have to fill a payload with 1 to let IPComp always send + # a compressed packet. + + # pktsize == minlen - 1 + pktsize=$(($(get_minlen $algo) - 8 - 1)) + atf_check -s exit:0 -o ignore \ + rump.ping -c 1 -n -w 3 -s $pktsize -p ff $ip_peer + extract_new_packets $BUS > $outfile + check_packets $outfile $ip_local $ip_peer ICMP + + # pktsize == minlen + pktsize=$(($(get_minlen $algo) - 8)) + atf_check -s exit:0 -o ignore \ + rump.ping -c 1 -n -w 3 -s $pktsize -p ff $ip_peer + extract_new_packets $BUS > $outfile + check_packets $outfile $ip_local $ip_peer $pktproto + else + atf_check -s exit:0 -o ignore rump.ping -c 1 -n -w 3 $ip_peer + extract_new_packets $BUS > $outfile + check_packets $outfile $ip_local $ip_peer $pktproto + fi + + test_flush_entries $SOCK_LOCAL + test_flush_entries $SOCK_PEER +} + +test_ipsec6_transport() +{ + local proto=$1 + local algo=$2 + local ip_local=fd00::1 + local ip_peer=fd00::2 + local tmpfile=./tmp + local outfile=./out + local pktproto=$(generate_pktproto $proto) + local algo_args="$(generate_algo_args $proto $algo)" + + rump_server_crypto_start $SOCK_LOCAL netinet6 netipsec + rump_server_crypto_start $SOCK_PEER netinet6 netipsec + rump_server_add_iface $SOCK_LOCAL shmif0 $BUS + rump_server_add_iface $SOCK_PEER shmif0 $BUS + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 rump.ifconfig shmif0 inet6 $ip_local + atf_check -s exit:0 rump.ifconfig -w 10 + + export RUMP_SERVER=$SOCK_PEER + atf_check -s exit:0 rump.ifconfig shmif0 inet6 $ip_peer + atf_check -s exit:0 rump.ifconfig -w 10 + + extract_new_packets $BUS > $outfile + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 -o ignore rump.ping6 -c 1 -n -X 3 $ip_peer + + extract_new_packets $BUS > $outfile + atf_check -s exit:0 -o match:"$ip_local > $ip_peer: ICMP6, echo request" \ + cat $outfile + atf_check -s exit:0 -o match:"$ip_peer > $ip_local: ICMP6, echo reply" \ + cat $outfile + + export RUMP_SERVER=$SOCK_LOCAL + # from https://www.netbsd.org/docs/network/ipsec/ + cat > $tmpfile <<-EOF + add $ip_local $ip_peer $proto 10000 $algo_args; + add $ip_peer $ip_local $proto 10001 $algo_args; + spdadd $ip_local $ip_peer any -P out ipsec $proto/transport//require; + EOF + $DEBUG && cat $tmpfile + atf_check -s exit:0 -o empty $HIJACKING setkey -c < $tmpfile + check_sa_entries $SOCK_LOCAL $ip_local $ip_peer + + export RUMP_SERVER=$SOCK_PEER + cat > $tmpfile <<-EOF + add $ip_local $ip_peer $proto 10000 $algo_args; + add $ip_peer $ip_local $proto 10001 $algo_args; + spdadd $ip_peer $ip_local any -P out ipsec $proto/transport//require; + EOF + $DEBUG && cat $tmpfile + atf_check -s exit:0 -o empty $HIJACKING setkey -c < $tmpfile + check_sa_entries $SOCK_PEER $ip_local $ip_peer + + export RUMP_SERVER=$SOCK_LOCAL + if [ $proto = ipcomp ]; then + # IPComp sends a packet as-is if a compressed payload of + # the packet is greater than or equal to the original payload. + # So we have to fill a payload with 1 to let IPComp always send + # a compressed packet. + + # pktsize == minlen - 1 + pktsize=$(($(get_minlen $algo) - 8 - 1)) + atf_check -s exit:0 -o ignore \ + rump.ping6 -c 1 -n -X 3 -s $pktsize -p ff $ip_peer + extract_new_packets $BUS > $outfile + check_packets $outfile $ip_local $ip_peer ICMP6 + + # pktsize == minlen + pktsize=$(($(get_minlen $algo) - 8)) + atf_check -s exit:0 -o ignore \ + rump.ping6 -c 1 -n -X 3 -s $pktsize -p ff $ip_peer + extract_new_packets $BUS > $outfile + check_packets $outfile $ip_local $ip_peer $pktproto + else + atf_check -s exit:0 -o ignore rump.ping6 -c 1 -n -X 3 $ip_peer + extract_new_packets $BUS > $outfile + check_packets $outfile $ip_local $ip_peer $pktproto + fi + + test_flush_entries $SOCK_LOCAL + test_flush_entries $SOCK_PEER +} + +test_transport_common() +{ + local ipproto=$1 + local proto=$2 + local algo=$3 + + if [ $ipproto = ipv4 ]; then + test_ipsec4_transport $proto $algo + else + test_ipsec6_transport $proto $algo + fi +} + +add_test_transport_mode() +{ + local ipproto=$1 + local proto=$2 + local algo=$3 + local _algo=$(echo $algo | sed 's/-//g') + local name= desc= + + name="ipsec_transport_${ipproto}_${proto}_${_algo}" + desc="Tests of IPsec ($ipproto) transport mode with $proto ($algo)" + + atf_test_case ${name} cleanup + eval " + ${name}_head() { + atf_set descr \"$desc\" + atf_set require.progs rump_server setkey + } + ${name}_body() { + test_transport_common $ipproto $proto $algo + rump_server_destroy_ifaces + } + ${name}_cleanup() { + \$DEBUG && dump + cleanup + } + " + atf_add_test_case ${name} +} + +atf_init_test_cases() +{ + local algo= + + for algo in $ESP_ENCRYPTION_ALGORITHMS; do + add_test_transport_mode ipv4 esp $algo + add_test_transport_mode ipv6 esp $algo + done + for algo in $AH_AUTHENTICATION_ALGORITHMS; do + add_test_transport_mode ipv4 ah $algo + add_test_transport_mode ipv6 ah $algo + done + for algo in $IPCOMP_COMPRESSION_ALGORITHMS; do + add_test_transport_mode ipv4 ipcomp $algo + add_test_transport_mode ipv6 ipcomp $algo + done +} diff --git a/net/ipsec/t_ipsec_tunnel.sh b/net/ipsec/t_ipsec_tunnel.sh new file mode 100644 --- /dev/null +++ b/net/ipsec/t_ipsec_tunnel.sh @@ -0,0 +1,320 @@ +# $NetBSD: t_ipsec_tunnel.sh,v 1.9 2017/08/03 03:16:27 ozaki-r Exp $ +# +# Copyright (c) 2017 Internet Initiative Japan Inc. +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +SOCK_LOCAL=unix://ipsec_local +SOCK_TUNNEL_LOCAL=unix://ipsec_tunel_local +SOCK_TUNNEL_REMOTE=unix://ipsec_tunnel_remote +SOCK_REMOTE=unix://ipsec_remote +BUS_LOCAL=./bus_ipsec_local +BUS_TUNNEL=./bus_ipsec_tunnel +BUS_REMOTE=./bus_ipsec_remote + +DEBUG=${DEBUG:-false} + +setup_servers() +{ + + # See https://www.netbsd.org/docs/network/ipsec/#sample_vpn + rump_server_crypto_start $SOCK_LOCAL netinet6 + rump_server_crypto_start $SOCK_TUNNEL_LOCAL netipsec netinet6 + rump_server_crypto_start $SOCK_TUNNEL_REMOTE netipsec netinet6 + rump_server_crypto_start $SOCK_REMOTE netinet6 + rump_server_add_iface $SOCK_LOCAL shmif0 $BUS_LOCAL + rump_server_add_iface $SOCK_TUNNEL_LOCAL shmif0 $BUS_LOCAL + rump_server_add_iface $SOCK_TUNNEL_LOCAL shmif1 $BUS_TUNNEL + rump_server_add_iface $SOCK_TUNNEL_REMOTE shmif0 $BUS_REMOTE + rump_server_add_iface $SOCK_TUNNEL_REMOTE shmif1 $BUS_TUNNEL + rump_server_add_iface $SOCK_REMOTE shmif0 $BUS_REMOTE +} + +check_tunnel_packets() +{ + local outfile=$1 + local src=$2 + local dst=$3 + local proto=$4 + + atf_check -s exit:0 -o match:"$src > $dst: $proto" cat $outfile + atf_check -s exit:0 -o match:"$dst > $src: $proto" cat $outfile +} + +test_ipsec4_tunnel() +{ + local proto=$1 + local algo=$2 + local ip_local=10.0.1.2 + local ip_gw_local=10.0.1.1 + local ip_gw_local_tunnel=20.0.0.1 + local ip_gw_remote_tunnel=20.0.0.2 + local ip_gw_remote=10.0.2.1 + local ip_remote=10.0.2.2 + local subnet_local=10.0.1.0 + local subnet_remote=10.0.2.0 + local tmpfile=./tmp + local outfile=./out + local proto_cap=$(echo $proto | tr 'a-z' 'A-Z') + local algo_args="$(generate_algo_args $proto $algo)" + + setup_servers + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 rump.sysctl -q -w net.inet.ip.dad_count=0 + atf_check -s exit:0 rump.ifconfig shmif0 $ip_local/24 + atf_check -s exit:0 -o ignore \ + rump.route -n add -net $subnet_remote $ip_gw_local + + export RUMP_SERVER=$SOCK_TUNNEL_LOCAL + atf_check -s exit:0 rump.sysctl -q -w net.inet.ip.dad_count=0 + atf_check -s exit:0 rump.ifconfig shmif0 $ip_gw_local/24 + atf_check -s exit:0 rump.ifconfig shmif1 $ip_gw_local_tunnel/24 + atf_check -s exit:0 rump.sysctl -q -w net.inet.ip.forwarding=1 + atf_check -s exit:0 -o ignore \ + rump.route -n add -net $subnet_remote $ip_gw_remote_tunnel + + export RUMP_SERVER=$SOCK_TUNNEL_REMOTE + atf_check -s exit:0 rump.sysctl -q -w net.inet.ip.dad_count=0 + atf_check -s exit:0 rump.ifconfig shmif0 $ip_gw_remote/24 + atf_check -s exit:0 rump.ifconfig shmif1 $ip_gw_remote_tunnel/24 + atf_check -s exit:0 rump.sysctl -q -w net.inet.ip.forwarding=1 + atf_check -s exit:0 -o ignore \ + rump.route -n add -net $subnet_local $ip_gw_local_tunnel + + export RUMP_SERVER=$SOCK_REMOTE + atf_check -s exit:0 rump.sysctl -q -w net.inet.ip.dad_count=0 + atf_check -s exit:0 rump.ifconfig shmif0 $ip_remote/24 + atf_check -s exit:0 -o ignore \ + rump.route -n add -net $subnet_local $ip_gw_remote + + extract_new_packets $BUS_TUNNEL > $outfile + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 -o ignore rump.ping -c 1 -n -w 3 $ip_remote + + extract_new_packets $BUS_TUNNEL > $outfile + atf_check -s exit:0 \ + -o match:"$ip_local > $ip_remote: ICMP echo request" \ + cat $outfile + atf_check -s exit:0 \ + -o match:"$ip_remote > $ip_local: ICMP echo reply" \ + cat $outfile + + export RUMP_SERVER=$SOCK_TUNNEL_LOCAL + # from https://www.netbsd.org/docs/network/ipsec/ + cat > $tmpfile <<-EOF + add $ip_gw_local_tunnel $ip_gw_remote_tunnel $proto 10000 $algo_args; + add $ip_gw_remote_tunnel $ip_gw_local_tunnel $proto 10001 $algo_args; + spdadd $subnet_local/24 $subnet_remote/24 any -P out ipsec + $proto/tunnel/$ip_gw_local_tunnel-$ip_gw_remote_tunnel/require; + spdadd $subnet_remote/24 $subnet_local/24 any -P in ipsec + $proto/tunnel/$ip_gw_remote_tunnel-$ip_gw_local_tunnel/require; + EOF + $DEBUG && cat $tmpfile + atf_check -s exit:0 -o empty $HIJACKING setkey -c < $tmpfile + check_sa_entries $SOCK_TUNNEL_LOCAL $ip_gw_local_tunnel \ + $ip_gw_remote_tunnel + + export RUMP_SERVER=$SOCK_TUNNEL_REMOTE + cat > $tmpfile <<-EOF + add $ip_gw_local_tunnel $ip_gw_remote_tunnel $proto 10000 $algo_args; + add $ip_gw_remote_tunnel $ip_gw_local_tunnel $proto 10001 $algo_args; + spdadd $subnet_remote/24 $subnet_local/24 any -P out ipsec + $proto/tunnel/$ip_gw_remote_tunnel-$ip_gw_local_tunnel/require; + spdadd $subnet_local/24 $subnet_remote/24 any -P in ipsec + $proto/tunnel/$ip_gw_local_tunnel-$ip_gw_remote_tunnel/require; + EOF + $DEBUG && cat $tmpfile + atf_check -s exit:0 -o empty $HIJACKING setkey -c < $tmpfile + check_sa_entries $SOCK_TUNNEL_REMOTE $ip_gw_local_tunnel \ + $ip_gw_remote_tunnel + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 -o ignore rump.ping -c 1 -n -w 3 $ip_remote + + extract_new_packets $BUS_TUNNEL > $outfile + check_tunnel_packets $outfile $ip_gw_local_tunnel $ip_gw_remote_tunnel \ + $proto_cap + + test_flush_entries $SOCK_TUNNEL_LOCAL + test_flush_entries $SOCK_TUNNEL_REMOTE +} + +test_ipsec6_tunnel() +{ + local proto=$1 + local algo=$2 + local ip_local=fd00:1::2 + local ip_gw_local=fd00:1::1 + local ip_gw_local_tunnel=fc00::1 + local ip_gw_remote_tunnel=fc00::2 + local ip_gw_remote=fd00:2::1 + local ip_remote=fd00:2::2 + local subnet_local=fd00:1:: + local subnet_remote=fd00:2:: + local tmpfile=./tmp + local outfile=./out + local proto_cap=$(echo $proto | tr 'a-z' 'A-Z') + local algo_args="$(generate_algo_args $proto $algo)" + + setup_servers + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 rump.sysctl -q -w net.inet6.ip6.dad_count=0 + atf_check -s exit:0 rump.ifconfig shmif0 inet6 $ip_local/64 + atf_check -s exit:0 -o ignore \ + rump.route -n add -inet6 -net $subnet_remote/64 $ip_gw_local + + export RUMP_SERVER=$SOCK_TUNNEL_LOCAL + atf_check -s exit:0 rump.sysctl -q -w net.inet6.ip6.dad_count=0 + atf_check -s exit:0 rump.ifconfig shmif0 inet6 $ip_gw_local/64 + atf_check -s exit:0 rump.ifconfig shmif1 inet6 $ip_gw_local_tunnel/64 + atf_check -s exit:0 rump.sysctl -q -w net.inet6.ip6.forwarding=1 + atf_check -s exit:0 -o ignore \ + rump.route -n add -inet6 -net $subnet_remote/64 $ip_gw_remote_tunnel + + export RUMP_SERVER=$SOCK_TUNNEL_REMOTE + atf_check -s exit:0 rump.sysctl -q -w net.inet6.ip6.dad_count=0 + atf_check -s exit:0 rump.ifconfig shmif0 inet6 $ip_gw_remote/64 + atf_check -s exit:0 rump.ifconfig shmif1 inet6 $ip_gw_remote_tunnel/64 + atf_check -s exit:0 rump.sysctl -q -w net.inet6.ip6.forwarding=1 + atf_check -s exit:0 -o ignore \ + rump.route -n add -inet6 -net $subnet_local/64 $ip_gw_local_tunnel + + export RUMP_SERVER=$SOCK_REMOTE + atf_check -s exit:0 rump.sysctl -q -w net.inet6.ip6.dad_count=0 + atf_check -s exit:0 rump.ifconfig shmif0 inet6 $ip_remote + atf_check -s exit:0 -o ignore \ + rump.route -n add -inet6 -net $subnet_local/64 $ip_gw_remote + + extract_new_packets $BUS_TUNNEL > $outfile + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 -o ignore rump.ping6 -c 1 -n -X 3 $ip_remote + + extract_new_packets $BUS_TUNNEL > $outfile + atf_check -s exit:0 \ + -o match:"$ip_local > $ip_remote: ICMP6, echo request" \ + cat $outfile + atf_check -s exit:0 \ + -o match:"$ip_remote > $ip_local: ICMP6, echo reply" \ + cat $outfile + + export RUMP_SERVER=$SOCK_TUNNEL_LOCAL + # from https://www.netbsd.org/docs/network/ipsec/ + cat > $tmpfile <<-EOF + add $ip_gw_local_tunnel $ip_gw_remote_tunnel $proto 10000 $algo_args; + add $ip_gw_remote_tunnel $ip_gw_local_tunnel $proto 10001 $algo_args; + spdadd $subnet_local/64 $subnet_remote/64 any -P out ipsec + $proto/tunnel/$ip_gw_local_tunnel-$ip_gw_remote_tunnel/require; + spdadd $subnet_remote/64 $subnet_local/64 any -P in ipsec + $proto/tunnel/$ip_gw_remote_tunnel-$ip_gw_local_tunnel/require; + EOF + $DEBUG && cat $tmpfile + atf_check -s exit:0 -o empty $HIJACKING setkey -c < $tmpfile + check_sa_entries $SOCK_TUNNEL_LOCAL $ip_gw_local_tunnel \ + $ip_gw_remote_tunnel + + export RUMP_SERVER=$SOCK_TUNNEL_REMOTE + cat > $tmpfile <<-EOF + add $ip_gw_local_tunnel $ip_gw_remote_tunnel $proto 10000 $algo_args; + add $ip_gw_remote_tunnel $ip_gw_local_tunnel $proto 10001 $algo_args; + spdadd $subnet_remote/64 $subnet_local/64 any -P out ipsec + $proto/tunnel/$ip_gw_remote_tunnel-$ip_gw_local_tunnel/require; + spdadd $subnet_local/64 $subnet_remote/64 any -P in ipsec + $proto/tunnel/$ip_gw_local_tunnel-$ip_gw_remote_tunnel/require; + EOF + $DEBUG && cat $tmpfile + atf_check -s exit:0 -o empty $HIJACKING setkey -c < $tmpfile + check_sa_entries $SOCK_TUNNEL_REMOTE $ip_gw_local_tunnel \ + $ip_gw_remote_tunnel + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 -o ignore rump.ping6 -c 1 -n -X 3 $ip_remote + + extract_new_packets $BUS_TUNNEL > $outfile + check_tunnel_packets $outfile $ip_gw_local_tunnel $ip_gw_remote_tunnel \ + $proto_cap + + test_flush_entries $SOCK_TUNNEL_LOCAL + test_flush_entries $SOCK_TUNNEL_REMOTE +} + +test_tunnel_common() +{ + local ipproto=$1 + local proto=$2 + local algo=$3 + + if [ $ipproto = ipv4 ]; then + test_ipsec4_tunnel $proto $algo + else + test_ipsec6_tunnel $proto $algo + fi +} + +add_test_tunnel_mode() +{ + local ipproto=$1 + local proto=$2 + local algo=$3 + local _algo=$(echo $algo | sed 's/-//g') + local name= desc= + + name="ipsec_tunnel_${ipproto}_${proto}_${_algo}" + desc="Tests of IPsec ($ipproto) tunnel mode with $proto ($algo)" + + atf_test_case ${name} cleanup + eval " + ${name}_head() { + atf_set descr \"$desc\" + atf_set require.progs rump_server setkey + } + ${name}_body() { + test_tunnel_common $ipproto $proto $algo + rump_server_destroy_ifaces + } + ${name}_cleanup() { + \$DEBUG && dump + cleanup + } + " + atf_add_test_case ${name} +} + +atf_init_test_cases() +{ + local algo= + + for algo in $ESP_ENCRYPTION_ALGORITHMS; do + add_test_tunnel_mode ipv4 esp $algo + add_test_tunnel_mode ipv6 esp $algo + done + + for algo in $AH_AUTHENTICATION_ALGORITHMS; do + add_test_tunnel_mode ipv4 ah $algo + add_test_tunnel_mode ipv6 ah $algo + done +} diff --git a/net/ipsec/t_ipsec_tunnel_ipcomp.sh b/net/ipsec/t_ipsec_tunnel_ipcomp.sh new file mode 100644 --- /dev/null +++ b/net/ipsec/t_ipsec_tunnel_ipcomp.sh @@ -0,0 +1,409 @@ +# $NetBSD: t_ipsec_tunnel_ipcomp.sh,v 1.2 2017/08/03 03:16:27 ozaki-r Exp $ +# +# Copyright (c) 2017 Internet Initiative Japan Inc. +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +SOCK_LOCAL=unix://ipsec_local +SOCK_TUNNEL_LOCAL=unix://ipsec_tunel_local +SOCK_TUNNEL_REMOTE=unix://ipsec_tunnel_remote +SOCK_REMOTE=unix://ipsec_remote +BUS_LOCAL=./bus_ipsec_local +BUS_TUNNEL=./bus_ipsec_tunnel +BUS_REMOTE=./bus_ipsec_remote + +DEBUG=${DEBUG:-false} + +setup_servers() +{ + + # See https://www.netbsd.org/docs/network/ipsec/#sample_vpn + rump_server_crypto_start $SOCK_LOCAL netinet6 + rump_server_crypto_start $SOCK_TUNNEL_LOCAL netipsec netinet6 + rump_server_crypto_start $SOCK_TUNNEL_REMOTE netipsec netinet6 + rump_server_crypto_start $SOCK_REMOTE netinet6 + rump_server_add_iface $SOCK_LOCAL shmif0 $BUS_LOCAL + rump_server_add_iface $SOCK_TUNNEL_LOCAL shmif0 $BUS_LOCAL + rump_server_add_iface $SOCK_TUNNEL_LOCAL shmif1 $BUS_TUNNEL + rump_server_add_iface $SOCK_TUNNEL_REMOTE shmif0 $BUS_REMOTE + rump_server_add_iface $SOCK_TUNNEL_REMOTE shmif1 $BUS_TUNNEL + rump_server_add_iface $SOCK_REMOTE shmif0 $BUS_REMOTE +} + +check_tunnel_ipcomp_packets() +{ + local outfile=$1 + local osrc=$2 + local odst=$3 + local oproto=$4 + local isrc=$5 + local idst=$6 + local iproto=$7 + + $DEBUG && cat $outfile + + if [ $oproto = ESP ]; then + atf_check -s exit:0 \ + -o match:"$osrc > $odst: $oproto" \ + cat $outfile + atf_check -s exit:0 \ + -o match:"$odst > $osrc: $oproto" \ + cat $outfile + # TODO check the packet lengths to check IPComp is really used + return + fi + + # AH + if [ $iproto = IPComp ]; then + atf_check -s exit:0 \ + -o match:"$osrc > $odst: $oproto.+: $iproto" \ + cat $outfile + atf_check -s exit:0 \ + -o match:"$odst > $osrc: $oproto.+: $iproto" \ + cat $outfile + else + atf_check -s exit:0 \ + -o match:"$osrc > $odst: $oproto.+ $isrc > $idst: $iproto" \ + cat $outfile + atf_check -s exit:0 \ + -o match:"$odst > $osrc: $oproto.+ $idst > $isrc: $iproto" \ + cat $outfile + fi +} + +test_ipsec4_tunnel_ipcomp() +{ + local proto=$1 + local algo=$2 + local calgo=$3 + local ip_local=10.0.1.2 + local ip_gw_local=10.0.1.1 + local ip_gw_local_tunnel=20.0.0.1 + local ip_gw_remote_tunnel=20.0.0.2 + local ip_gw_remote=10.0.2.1 + local ip_remote=10.0.2.2 + local subnet_local=10.0.1.0 + local subnet_remote=10.0.2.0 + local tmpfile=./tmp + local outfile=./out + local pktproto=$(generate_pktproto $proto) + local algo_args="$(generate_algo_args $proto $algo)" + + setup_servers + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 rump.sysctl -q -w net.inet.ip.dad_count=0 + atf_check -s exit:0 rump.ifconfig shmif0 $ip_local/24 + atf_check -s exit:0 -o ignore \ + rump.route -n add -net $subnet_remote $ip_gw_local + + export RUMP_SERVER=$SOCK_TUNNEL_LOCAL + atf_check -s exit:0 rump.sysctl -q -w net.inet.ip.dad_count=0 + atf_check -s exit:0 rump.ifconfig shmif0 $ip_gw_local/24 + atf_check -s exit:0 rump.ifconfig shmif1 $ip_gw_local_tunnel/24 + atf_check -s exit:0 rump.sysctl -q -w net.inet.ip.forwarding=1 + atf_check -s exit:0 -o ignore \ + rump.route -n add -net $subnet_remote $ip_gw_remote_tunnel + rump.sysctl -a |grep ipsec + + export RUMP_SERVER=$SOCK_TUNNEL_REMOTE + atf_check -s exit:0 rump.sysctl -q -w net.inet.ip.dad_count=0 + atf_check -s exit:0 rump.ifconfig shmif0 $ip_gw_remote/24 + atf_check -s exit:0 rump.ifconfig shmif1 $ip_gw_remote_tunnel/24 + atf_check -s exit:0 rump.sysctl -q -w net.inet.ip.forwarding=1 + atf_check -s exit:0 -o ignore \ + rump.route -n add -net $subnet_local $ip_gw_local_tunnel + + export RUMP_SERVER=$SOCK_REMOTE + atf_check -s exit:0 rump.sysctl -q -w net.inet.ip.dad_count=0 + atf_check -s exit:0 rump.ifconfig shmif0 $ip_remote/24 + atf_check -s exit:0 -o ignore \ + rump.route -n add -net $subnet_local $ip_gw_remote + + extract_new_packets $BUS_TUNNEL > $outfile + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 -o ignore rump.ping -c 1 -n -w 3 $ip_remote + + extract_new_packets $BUS_TUNNEL > $outfile + atf_check -s exit:0 \ + -o match:"$ip_local > $ip_remote: ICMP echo request" \ + cat $outfile + atf_check -s exit:0 \ + -o match:"$ip_remote > $ip_local: ICMP echo reply" \ + cat $outfile + + export RUMP_SERVER=$SOCK_TUNNEL_LOCAL + # from https://www.netbsd.org/docs/network/ipsec/ + cat > $tmpfile <<-EOF + add $ip_gw_local_tunnel $ip_gw_remote_tunnel $proto 10000 $algo_args; + add $ip_gw_remote_tunnel $ip_gw_local_tunnel $proto 10001 $algo_args; + add $ip_gw_local_tunnel $ip_gw_remote_tunnel ipcomp 10000 -C $calgo; + add $ip_gw_remote_tunnel $ip_gw_local_tunnel ipcomp 10001 -C $calgo; + spdadd $subnet_local/24 $subnet_remote/24 any -P out ipsec + ipcomp/tunnel/$ip_gw_local_tunnel-$ip_gw_remote_tunnel/require + $proto/transport//require; + spdadd $subnet_remote/24 $subnet_local/24 any -P in ipsec + ipcomp/tunnel/$ip_gw_remote_tunnel-$ip_gw_local_tunnel/require + $proto/transport//require; + EOF + $DEBUG && cat $tmpfile + atf_check -s exit:0 -o empty $HIJACKING setkey -c < $tmpfile + check_sa_entries $SOCK_TUNNEL_LOCAL $ip_gw_local_tunnel \ + $ip_gw_remote_tunnel + + export RUMP_SERVER=$SOCK_TUNNEL_REMOTE + cat > $tmpfile <<-EOF + add $ip_gw_local_tunnel $ip_gw_remote_tunnel $proto 10000 $algo_args; + add $ip_gw_remote_tunnel $ip_gw_local_tunnel $proto 10001 $algo_args; + add $ip_gw_local_tunnel $ip_gw_remote_tunnel ipcomp 10000 -C $calgo; + add $ip_gw_remote_tunnel $ip_gw_local_tunnel ipcomp 10001 -C $calgo; + spdadd $subnet_remote/24 $subnet_local/24 any -P out ipsec + ipcomp/tunnel/$ip_gw_remote_tunnel-$ip_gw_local_tunnel/require + $proto/transport//require; + spdadd $subnet_local/24 $subnet_remote/24 any -P in ipsec + ipcomp/tunnel/$ip_gw_local_tunnel-$ip_gw_remote_tunnel/require + $proto/transport//require; + EOF + $DEBUG && cat $tmpfile + atf_check -s exit:0 -o empty $HIJACKING setkey -c < $tmpfile + check_sa_entries $SOCK_TUNNEL_REMOTE $ip_gw_local_tunnel \ + $ip_gw_remote_tunnel + + export RUMP_SERVER=$SOCK_LOCAL + # IPComp sends a packet as-is if a compressed payload of + # the packet is greater than or equal to the original payload. + # So we have to fill a payload with 1 to let IPComp always send + # a compressed packet. + + # pktsize == minlen - 1 + pktsize=$(($(get_minlen deflate) - 8 - 20 - 1)) + atf_check -s exit:0 -o ignore \ + rump.ping -c 1 -n -w 3 -s $pktsize -p ff $ip_remote + extract_new_packets $BUS_TUNNEL > $outfile + check_tunnel_ipcomp_packets $outfile \ + $ip_gw_local_tunnel $ip_gw_remote_tunnel $pktproto \ + $ip_local $ip_remote ICMP + + # pktsize == minlen + pktsize=$(($(get_minlen deflate) - 8 - 20)) + atf_check -s exit:0 -o ignore \ + rump.ping -c 1 -n -w 3 -s $pktsize -p ff $ip_remote + extract_new_packets $BUS_TUNNEL > $outfile + check_tunnel_ipcomp_packets $outfile \ + $ip_gw_local_tunnel $ip_gw_remote_tunnel $pktproto \ + $ip_local $ip_remote IPComp + + test_flush_entries $SOCK_TUNNEL_LOCAL + test_flush_entries $SOCK_TUNNEL_REMOTE +} + +test_ipsec6_tunnel_ipcomp() +{ + local proto=$1 + local algo=$2 + local calgo=$3 + local ip_local=fd00:1::2 + local ip_gw_local=fd00:1::1 + local ip_gw_local_tunnel=fc00::1 + local ip_gw_remote_tunnel=fc00::2 + local ip_gw_remote=fd00:2::1 + local ip_remote=fd00:2::2 + local subnet_local=fd00:1:: + local subnet_remote=fd00:2:: + local tmpfile=./tmp + local outfile=./out + local pktproto=$(generate_pktproto $proto) + local algo_args="$(generate_algo_args $proto $algo)" + + setup_servers + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 rump.sysctl -q -w net.inet6.ip6.dad_count=0 + atf_check -s exit:0 rump.ifconfig shmif0 inet6 $ip_local/64 + atf_check -s exit:0 -o ignore \ + rump.route -n add -inet6 -net $subnet_remote/64 $ip_gw_local + + export RUMP_SERVER=$SOCK_TUNNEL_LOCAL + atf_check -s exit:0 rump.sysctl -q -w net.inet6.ip6.dad_count=0 + atf_check -s exit:0 rump.ifconfig shmif0 inet6 $ip_gw_local/64 + atf_check -s exit:0 rump.ifconfig shmif1 inet6 $ip_gw_local_tunnel/64 + atf_check -s exit:0 rump.sysctl -q -w net.inet6.ip6.forwarding=1 + atf_check -s exit:0 -o ignore \ + rump.route -n add -inet6 -net $subnet_remote/64 $ip_gw_remote_tunnel + + export RUMP_SERVER=$SOCK_TUNNEL_REMOTE + atf_check -s exit:0 rump.sysctl -q -w net.inet6.ip6.dad_count=0 + atf_check -s exit:0 rump.ifconfig shmif0 inet6 $ip_gw_remote/64 + atf_check -s exit:0 rump.ifconfig shmif1 inet6 $ip_gw_remote_tunnel/64 + atf_check -s exit:0 rump.sysctl -q -w net.inet6.ip6.forwarding=1 + atf_check -s exit:0 -o ignore \ + rump.route -n add -inet6 -net $subnet_local/64 $ip_gw_local_tunnel + + export RUMP_SERVER=$SOCK_REMOTE + atf_check -s exit:0 rump.sysctl -q -w net.inet6.ip6.dad_count=0 + atf_check -s exit:0 rump.ifconfig shmif0 inet6 $ip_remote + atf_check -s exit:0 -o ignore \ + rump.route -n add -inet6 -net $subnet_local/64 $ip_gw_remote + + extract_new_packets $BUS_TUNNEL > $outfile + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 -o ignore rump.ping6 -c 1 -n -X 3 $ip_remote + + extract_new_packets $BUS_TUNNEL > $outfile + atf_check -s exit:0 \ + -o match:"$ip_local > $ip_remote: ICMP6, echo request" \ + cat $outfile + atf_check -s exit:0 \ + -o match:"$ip_remote > $ip_local: ICMP6, echo reply" \ + cat $outfile + + export RUMP_SERVER=$SOCK_TUNNEL_LOCAL + # from https://www.netbsd.org/docs/network/ipsec/ + cat > $tmpfile <<-EOF + add $ip_gw_local_tunnel $ip_gw_remote_tunnel $proto 10000 $algo_args; + add $ip_gw_remote_tunnel $ip_gw_local_tunnel $proto 10001 $algo_args; + add $ip_gw_local_tunnel $ip_gw_remote_tunnel ipcomp 10000 -C $calgo; + add $ip_gw_remote_tunnel $ip_gw_local_tunnel ipcomp 10001 -C $calgo; + spdadd $subnet_local/64 $subnet_remote/64 any -P out ipsec + ipcomp/tunnel/$ip_gw_local_tunnel-$ip_gw_remote_tunnel/require + $proto/transport//require; + spdadd $subnet_remote/64 $subnet_local/64 any -P in ipsec + ipcomp/tunnel/$ip_gw_remote_tunnel-$ip_gw_local_tunnel/require + $proto/transport//require; + EOF + $DEBUG && cat $tmpfile + atf_check -s exit:0 -o empty $HIJACKING setkey -c < $tmpfile + check_sa_entries $SOCK_TUNNEL_LOCAL $ip_gw_local_tunnel \ + $ip_gw_remote_tunnel + + export RUMP_SERVER=$SOCK_TUNNEL_REMOTE + cat > $tmpfile <<-EOF + add $ip_gw_local_tunnel $ip_gw_remote_tunnel $proto 10000 $algo_args; + add $ip_gw_remote_tunnel $ip_gw_local_tunnel $proto 10001 $algo_args; + add $ip_gw_local_tunnel $ip_gw_remote_tunnel ipcomp 10000 -C $calgo; + add $ip_gw_remote_tunnel $ip_gw_local_tunnel ipcomp 10001 -C $calgo; + spdadd $subnet_remote/64 $subnet_local/64 any -P out ipsec + ipcomp/tunnel/$ip_gw_remote_tunnel-$ip_gw_local_tunnel/require + $proto/transport//require; + spdadd $subnet_local/64 $subnet_remote/64 any -P in ipsec + ipcomp/tunnel/$ip_gw_local_tunnel-$ip_gw_remote_tunnel/require + $proto/transport//require; + EOF + $DEBUG && cat $tmpfile + atf_check -s exit:0 -o empty $HIJACKING setkey -c < $tmpfile + check_sa_entries $SOCK_TUNNEL_REMOTE $ip_gw_local_tunnel \ + $ip_gw_remote_tunnel + + export RUMP_SERVER=$SOCK_LOCAL + + # IPComp sends a packet as-is if a compressed payload of + # the packet is greater than or equal to the original payload. + # So we have to fill a payload with 1 to let IPComp always send + # a compressed packet. + + # pktsize == minlen - 1 + + pktsize=$(($(get_minlen deflate) - 8 - 40 - 1)) + atf_check -s exit:0 -o ignore \ + rump.ping6 -c 1 -n -X 3 -s $pktsize -p ff $ip_remote + extract_new_packets $BUS_TUNNEL > $outfile + check_tunnel_ipcomp_packets $outfile \ + $ip_gw_local_tunnel $ip_gw_remote_tunnel $pktproto \ + $ip_local $ip_remote ICMP6 + + # pktsize == minlen + pktsize=$(($(get_minlen deflate) - 8 - 40)) + atf_check -s exit:0 -o ignore \ + rump.ping6 -c 1 -n -X 3 -s $pktsize -p ff $ip_remote + extract_new_packets $BUS_TUNNEL > $outfile + check_tunnel_ipcomp_packets $outfile \ + $ip_gw_local_tunnel $ip_gw_remote_tunnel $pktproto \ + $ip_local $ip_remote IPComp + + test_flush_entries $SOCK_TUNNEL_LOCAL + test_flush_entries $SOCK_TUNNEL_REMOTE +} + +test_tunnel_ipcomp_common() +{ + local ipproto=$1 + local proto=$2 + local algo=$3 + local calgo=$4 + + if [ $ipproto = ipv4 ]; then + test_ipsec4_tunnel_ipcomp $proto $algo $calgo + else + test_ipsec6_tunnel_ipcomp $proto $algo $calgo + fi +} + +add_test_tunnel_mode() +{ + local ipproto=$1 + local proto=$2 + local algo=$3 + local calgo=$4 + local _algo=$(echo $algo | sed 's/-//g') + local name= desc= + + name="ipsec_tunnel_ipcomp_${calgo}_${ipproto}_${proto}_${_algo}" + desc="Tests of IPsec ($ipproto) tunnel mode with $proto ($algo) and ipcomp ($calgo)" + + atf_test_case ${name} cleanup + eval " + ${name}_head() { + atf_set descr \"$desc\" + atf_set require.progs rump_server setkey + } + ${name}_body() { + test_tunnel_ipcomp_common $ipproto $proto $algo $calgo + rump_server_destroy_ifaces + } + ${name}_cleanup() { + \$DEBUG && dump + cleanup + } + " + atf_add_test_case ${name} +} + +atf_init_test_cases() +{ + local calgo= algo= + + for calgo in $IPCOMP_COMPRESSION_ALGORITHMS; do + for algo in $ESP_ENCRYPTION_ALGORITHMS_MINIMUM; do + add_test_tunnel_mode ipv4 esp $algo $calgo + add_test_tunnel_mode ipv6 esp $algo $calgo + done + + for algo in $AH_AUTHENTICATION_ALGORITHMS_MINIMUM; do + add_test_tunnel_mode ipv4 ah $algo $calgo + add_test_tunnel_mode ipv6 ah $algo $calgo + done + done +} diff --git a/net/ipsec/t_ipsec_tunnel_odd.sh b/net/ipsec/t_ipsec_tunnel_odd.sh new file mode 100644 --- /dev/null +++ b/net/ipsec/t_ipsec_tunnel_odd.sh @@ -0,0 +1,336 @@ +# $NetBSD: t_ipsec_tunnel_odd.sh,v 1.3 2017/08/03 03:16:27 ozaki-r Exp $ +# +# Copyright (c) 2017 Internet Initiative Japan Inc. +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +SOCK_LOCAL=unix://ipsec_local +SOCK_TUNNEL_LOCAL=unix://ipsec_tunel_local +SOCK_TUNNEL_REMOTE=unix://ipsec_tunnel_remote +SOCK_REMOTE=unix://ipsec_remote +BUS_LOCAL=./bus_ipsec_local +BUS_TUNNEL=./bus_ipsec_tunnel +BUS_REMOTE=./bus_ipsec_remote + +DEBUG=${DEBUG:-false} + +setup_servers() +{ + + # See https://www.netbsd.org/docs/network/ipsec/#sample_vpn + rump_server_crypto_start $SOCK_LOCAL netinet6 + rump_server_crypto_start $SOCK_TUNNEL_LOCAL netipsec netinet6 + rump_server_crypto_start $SOCK_TUNNEL_REMOTE netipsec netinet6 + rump_server_crypto_start $SOCK_REMOTE netinet6 + rump_server_add_iface $SOCK_LOCAL shmif0 $BUS_LOCAL + rump_server_add_iface $SOCK_TUNNEL_LOCAL shmif0 $BUS_LOCAL + rump_server_add_iface $SOCK_TUNNEL_LOCAL shmif1 $BUS_TUNNEL + rump_server_add_iface $SOCK_TUNNEL_REMOTE shmif0 $BUS_REMOTE + rump_server_add_iface $SOCK_TUNNEL_REMOTE shmif1 $BUS_TUNNEL + rump_server_add_iface $SOCK_REMOTE shmif0 $BUS_REMOTE +} + +check_tunnel_packets() +{ + local outfile=$1 + local src=$2 + local dst=$3 + local proto=$4 + + atf_check -s exit:0 -o match:"$src > $dst: $proto" cat $outfile + atf_check -s exit:0 -o match:"$dst > $src: $proto" cat $outfile +} + +test_ipsec46_tunnel() +{ + local proto=$1 + local algo=$2 + local ip_local=10.0.1.2 + local ip_gw_local=10.0.1.1 + local ip_gw_local_tunnel=20.0.0.1 + local ip_gw_remote_tunnel=20.0.0.2 + local ip6_gw_local_tunnel=fc00::1 + local ip6_gw_remote_tunnel=fc00::2 + local ip_gw_remote=10.0.2.1 + local ip_remote=10.0.2.2 + local subnet_local=10.0.1.0 + local subnet_remote=10.0.2.0 + local tmpfile=./tmp + local outfile=./out + local proto_cap=$(echo $proto | tr 'a-z' 'A-Z') + local algo_args="$(generate_algo_args $proto $algo)" + + setup_servers + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 rump.sysctl -q -w net.inet.ip.dad_count=0 + atf_check -s exit:0 rump.ifconfig shmif0 $ip_local/24 + atf_check -s exit:0 -o ignore \ + rump.route -n add -net $subnet_remote $ip_gw_local + + export RUMP_SERVER=$SOCK_TUNNEL_LOCAL + atf_check -s exit:0 rump.sysctl -q -w net.inet.ip.dad_count=0 + atf_check -s exit:0 rump.sysctl -q -w net.inet6.ip6.dad_count=0 + atf_check -s exit:0 rump.ifconfig shmif0 $ip_gw_local/24 + atf_check -s exit:0 rump.ifconfig shmif1 $ip_gw_local_tunnel/24 + atf_check -s exit:0 rump.ifconfig shmif1 inet6 $ip6_gw_local_tunnel/24 + atf_check -s exit:0 rump.sysctl -q -w net.inet.ip.forwarding=1 + atf_check -s exit:0 -o ignore \ + rump.route -n add -net $subnet_remote $ip_gw_remote_tunnel + + export RUMP_SERVER=$SOCK_TUNNEL_REMOTE + atf_check -s exit:0 rump.sysctl -q -w net.inet.ip.dad_count=0 + atf_check -s exit:0 rump.sysctl -q -w net.inet6.ip6.dad_count=0 + atf_check -s exit:0 rump.ifconfig shmif0 $ip_gw_remote/24 + atf_check -s exit:0 rump.ifconfig shmif1 $ip_gw_remote_tunnel/24 + atf_check -s exit:0 rump.ifconfig shmif1 inet6 $ip6_gw_remote_tunnel/24 + atf_check -s exit:0 rump.sysctl -q -w net.inet.ip.forwarding=1 + atf_check -s exit:0 -o ignore \ + rump.route -n add -net $subnet_local $ip_gw_local_tunnel + + export RUMP_SERVER=$SOCK_REMOTE + atf_check -s exit:0 rump.sysctl -q -w net.inet.ip.dad_count=0 + atf_check -s exit:0 rump.ifconfig shmif0 $ip_remote/24 + atf_check -s exit:0 -o ignore \ + rump.route -n add -net $subnet_local $ip_gw_remote + + extract_new_packets $BUS_TUNNEL > $outfile + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 -o ignore rump.ping -c 1 -n -w 3 $ip_remote + + extract_new_packets $BUS_TUNNEL > $outfile + atf_check -s exit:0 \ + -o match:"$ip_local > $ip_remote: ICMP echo request" \ + cat $outfile + atf_check -s exit:0 \ + -o match:"$ip_remote > $ip_local: ICMP echo reply" \ + cat $outfile + + export RUMP_SERVER=$SOCK_TUNNEL_LOCAL + # from https://www.netbsd.org/docs/network/ipsec/ + cat > $tmpfile <<-EOF + add $ip6_gw_local_tunnel $ip6_gw_remote_tunnel $proto 10000 $algo_args; + add $ip6_gw_remote_tunnel $ip6_gw_local_tunnel $proto 10001 $algo_args; + spdadd $subnet_local/24 $subnet_remote/24 any -P out ipsec + $proto/tunnel/$ip6_gw_local_tunnel-$ip6_gw_remote_tunnel/require; + spdadd $subnet_remote/24 $subnet_local/24 any -P in ipsec + $proto/tunnel/$ip6_gw_remote_tunnel-$ip6_gw_local_tunnel/require; + EOF + $DEBUG && cat $tmpfile + atf_check -s exit:0 -o empty $HIJACKING setkey -c < $tmpfile + check_sa_entries $SOCK_TUNNEL_LOCAL $ip6_gw_local_tunnel \ + $ip6_gw_remote_tunnel + + export RUMP_SERVER=$SOCK_TUNNEL_REMOTE + cat > $tmpfile <<-EOF + add $ip6_gw_local_tunnel $ip6_gw_remote_tunnel $proto 10000 $algo_args; + add $ip6_gw_remote_tunnel $ip6_gw_local_tunnel $proto 10001 $algo_args; + spdadd $subnet_remote/24 $subnet_local/24 any -P out ipsec + $proto/tunnel/$ip6_gw_remote_tunnel-$ip6_gw_local_tunnel/require; + spdadd $subnet_local/24 $subnet_remote/24 any -P in ipsec + $proto/tunnel/$ip6_gw_local_tunnel-$ip6_gw_remote_tunnel/require; + EOF + $DEBUG && cat $tmpfile + atf_check -s exit:0 -o empty $HIJACKING setkey -c < $tmpfile + check_sa_entries $SOCK_TUNNEL_REMOTE $ip6_gw_local_tunnel \ + $ip6_gw_remote_tunnel + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 -o ignore rump.ping -c 1 -n -w 3 $ip_remote + + extract_new_packets $BUS_TUNNEL > $outfile + check_tunnel_packets $outfile $ip6_gw_local_tunnel $ip6_gw_remote_tunnel \ + $proto_cap + + test_flush_entries $SOCK_TUNNEL_LOCAL + test_flush_entries $SOCK_TUNNEL_REMOTE +} + +test_ipsec64_tunnel() +{ + local proto=$1 + local algo=$2 + local ip_local=fd00:1::2 + local ip_gw_local=fd00:1::1 + local ip_gw_local_tunnel=fc00::1 + local ip_gw_remote_tunnel=fc00::2 + local ip4_gw_local_tunnel=20.0.0.1 + local ip4_gw_remote_tunnel=20.0.0.2 + local ip_gw_remote=fd00:2::1 + local ip_remote=fd00:2::2 + local subnet_local=fd00:1:: + local subnet_remote=fd00:2:: + local tmpfile=./tmp + local outfile=./out + local proto_cap=$(echo $proto | tr 'a-z' 'A-Z') + local algo_args="$(generate_algo_args $proto $algo)" + + setup_servers + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 rump.sysctl -q -w net.inet6.ip6.dad_count=0 + atf_check -s exit:0 rump.ifconfig shmif0 inet6 $ip_local/64 + atf_check -s exit:0 -o ignore \ + rump.route -n add -inet6 -net $subnet_remote/64 $ip_gw_local + + export RUMP_SERVER=$SOCK_TUNNEL_LOCAL + atf_check -s exit:0 rump.sysctl -q -w net.inet.ip.dad_count=0 + atf_check -s exit:0 rump.sysctl -q -w net.inet6.ip6.dad_count=0 + atf_check -s exit:0 rump.ifconfig shmif0 inet6 $ip_gw_local/64 + atf_check -s exit:0 rump.ifconfig shmif1 inet6 $ip_gw_local_tunnel/64 + atf_check -s exit:0 rump.ifconfig shmif1 $ip4_gw_local_tunnel + atf_check -s exit:0 rump.sysctl -q -w net.inet6.ip6.forwarding=1 + atf_check -s exit:0 -o ignore \ + rump.route -n add -inet6 -net $subnet_remote/64 $ip_gw_remote_tunnel + + export RUMP_SERVER=$SOCK_TUNNEL_REMOTE + atf_check -s exit:0 rump.sysctl -q -w net.inet.ip.dad_count=0 + atf_check -s exit:0 rump.sysctl -q -w net.inet6.ip6.dad_count=0 + atf_check -s exit:0 rump.ifconfig shmif0 inet6 $ip_gw_remote/64 + atf_check -s exit:0 rump.ifconfig shmif1 inet6 $ip_gw_remote_tunnel/64 + atf_check -s exit:0 rump.ifconfig shmif1 $ip4_gw_remote_tunnel + atf_check -s exit:0 rump.sysctl -q -w net.inet6.ip6.forwarding=1 + atf_check -s exit:0 -o ignore \ + rump.route -n add -inet6 -net $subnet_local/64 $ip_gw_local_tunnel + + export RUMP_SERVER=$SOCK_REMOTE + atf_check -s exit:0 rump.sysctl -q -w net.inet6.ip6.dad_count=0 + atf_check -s exit:0 rump.ifconfig shmif0 inet6 $ip_remote + atf_check -s exit:0 -o ignore \ + rump.route -n add -inet6 -net $subnet_local/64 $ip_gw_remote + + extract_new_packets $BUS_TUNNEL > $outfile + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 -o ignore rump.ping6 -c 1 -n -X 3 $ip_remote + + extract_new_packets $BUS_TUNNEL > $outfile + atf_check -s exit:0 \ + -o match:"$ip_local > $ip_remote: ICMP6, echo request" \ + cat $outfile + atf_check -s exit:0 \ + -o match:"$ip_remote > $ip_local: ICMP6, echo reply" \ + cat $outfile + + export RUMP_SERVER=$SOCK_TUNNEL_LOCAL + # from https://www.netbsd.org/docs/network/ipsec/ + cat > $tmpfile <<-EOF + add $ip4_gw_local_tunnel $ip4_gw_remote_tunnel $proto 10000 $algo_args; + add $ip4_gw_remote_tunnel $ip4_gw_local_tunnel $proto 10001 $algo_args; + spdadd $subnet_local/64 $subnet_remote/64 any -P out ipsec + $proto/tunnel/$ip4_gw_local_tunnel-$ip4_gw_remote_tunnel/require; + spdadd $subnet_remote/64 $subnet_local/64 any -P in ipsec + $proto/tunnel/$ip4_gw_remote_tunnel-$ip4_gw_local_tunnel/require; + EOF + $DEBUG && cat $tmpfile + atf_check -s exit:0 -o empty $HIJACKING setkey -c < $tmpfile + check_sa_entries $SOCK_TUNNEL_LOCAL $ip4_gw_local_tunnel \ + $ip4_gw_remote_tunnel + + export RUMP_SERVER=$SOCK_TUNNEL_REMOTE + cat > $tmpfile <<-EOF + add $ip4_gw_local_tunnel $ip4_gw_remote_tunnel $proto 10000 $algo_args; + add $ip4_gw_remote_tunnel $ip4_gw_local_tunnel $proto 10001 $algo_args; + spdadd $subnet_remote/64 $subnet_local/64 any -P out ipsec + $proto/tunnel/$ip4_gw_remote_tunnel-$ip4_gw_local_tunnel/require; + spdadd $subnet_local/64 $subnet_remote/64 any -P in ipsec + $proto/tunnel/$ip4_gw_local_tunnel-$ip4_gw_remote_tunnel/require; + EOF + $DEBUG && cat $tmpfile + atf_check -s exit:0 -o empty $HIJACKING setkey -c < $tmpfile + check_sa_entries $SOCK_TUNNEL_REMOTE $ip4_gw_local_tunnel \ + $ip4_gw_remote_tunnel + + export RUMP_SERVER=$SOCK_LOCAL + atf_check -s exit:0 -o ignore rump.ping6 -c 1 -n -X 3 $ip_remote + + extract_new_packets $BUS_TUNNEL > $outfile + check_tunnel_packets $outfile $ip4_gw_local_tunnel $ip4_gw_remote_tunnel \ + $proto_cap + + test_flush_entries $SOCK_TUNNEL_LOCAL + test_flush_entries $SOCK_TUNNEL_REMOTE +} + +test_tunnel_common() +{ + local ipproto=$1 + local proto=$2 + local algo=$3 + + if [ $ipproto = v4v6 ]; then + test_ipsec46_tunnel $proto $algo + else + test_ipsec64_tunnel $proto $algo + fi +} + +add_test_tunnel_mode() +{ + local ipproto=$1 + local proto=$2 + local algo=$3 + local _algo=$(echo $algo | sed 's/-//g') + local name= desc= + + name="ipsec_tunnel_${ipproto}_${proto}_${_algo}" + if [ $ipproto = v4v6 ]; then + desc="Tests of IPsec tunnel mode (IPv4 over IPv6) with $proto ($algo)" + else + desc="Tests of IPsec tunnel mode (IPv6 over IPv4) with $proto ($algo)" + fi + + atf_test_case ${name} cleanup + eval " + ${name}_head() { + atf_set descr \"$desc\" + atf_set require.progs rump_server setkey + } + ${name}_body() { + test_tunnel_common $ipproto $proto $algo + rump_server_destroy_ifaces + } + ${name}_cleanup() { + \$DEBUG && dump + cleanup + } + " + atf_add_test_case ${name} +} + +atf_init_test_cases() +{ + local algo= + + for algo in $ESP_ENCRYPTION_ALGORITHMS; do + add_test_tunnel_mode v4v6 esp $algo + add_test_tunnel_mode v6v4 esp $algo + done + + for algo in $AH_AUTHENTICATION_ALGORITHMS; do + add_test_tunnel_mode v4v6 ah $algo + add_test_tunnel_mode v6v4 ah $algo + done +} diff --git a/net/mcast/mcast.c b/net/mcast/mcast.c --- a/net/mcast/mcast.c +++ b/net/mcast/mcast.c @@ -1,4 +1,4 @@ -/* $NetBSD: mcast.c,v 1.3 2015/05/28 10:19:17 ozaki-r Exp $ */ +/* $NetBSD: mcast.c,v 1.4 2017/02/28 09:23:23 ozaki-r Exp $ */ /*- * Copyright (c) 2014 The NetBSD Foundation, Inc. @@ -30,7 +30,7 @@ */ #include #ifdef __RCSID -__RCSID("$NetBSD: mcast.c,v 1.3 2015/05/28 10:19:17 ozaki-r Exp $"); +__RCSID("$NetBSD: mcast.c,v 1.4 2017/02/28 09:23:23 ozaki-r Exp $"); #else extern const char *__progname; #define getprogname() __progname @@ -73,6 +73,7 @@ #endif static int debug; +static int nsleep; #define TOTAL 10 #define PORT_V4MAPPED "6666" @@ -338,6 +339,8 @@ seq, msg.seq); } + if (nsleep) + sleep(nsleep); /* Tell I'm finished */ synchronize(fd, false); } @@ -417,7 +420,7 @@ n = TOTAL; bug = conn = false; - while ((c = getopt(argc, argv, "46bcdmn:")) != -1) + while ((c = getopt(argc, argv, "46bcdmn:s:")) != -1) switch (c) { case '4': host = HOST_V4; @@ -443,8 +446,12 @@ case 'n': n = atoi(optarg); break; + case 's': + nsleep = atoi(optarg); + break; default: - fprintf(stderr, "Usage: %s [-cdm46] [-n ]", + fprintf(stderr, "Usage: %s [-cdm46] [-n ]" + " [-s ]", getprogname()); return 1; } diff --git a/net/mcast/t_mcast.sh b/net/mcast/t_mcast.sh old mode 100755 new mode 100644 --- a/net/mcast/t_mcast.sh +++ b/net/mcast/t_mcast.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_mcast.sh,v 1.4 2016/11/25 08:51:16 ozaki-r Exp $ +# $NetBSD: t_mcast.sh,v 1.6 2017/08/03 03:16:27 ozaki-r Exp $ # # Copyright (c) 2015 The NetBSD Foundation, Inc. # All rights reserved. @@ -62,45 +62,106 @@ unset LD_PRELOAD } +run_test_destroyif() +{ + local name="$1" + local opts="$2" + local mcast="$(atf_get_srcdir)/mcast" + local sleep=3 + + rump_server_start $RUMP_SERVER netinet6 + rump_server_add_iface $RUMP_SERVER shmif0 bus1 + export RUMP_SERVER=$RUMP_SERVER + atf_check -s exit:0 rump.ifconfig shmif0 10.0.0.2/24 + atf_check -s exit:0 rump.ifconfig shmif0 inet6 fc00::2/64 + atf_check -s exit:0 rump.ifconfig shmif0 up + + atf_check -s exit:0 rump.ifconfig -w 10 + atf_check -s not-exit:0 -x "rump.ifconfig shmif0 |grep -q tentative" + + # A route to the mcast address is required to join the mcast group + atf_check -s exit:0 -o ignore rump.route add default 10.0.0.1 + atf_check -s exit:0 -o ignore rump.route add -inet6 default fc00::1 + + $DEBUG && rump.ifconfig + $DEBUG && rump.netstat -nr + + export LD_PRELOAD=/usr/lib/librumphijack.so + #$DEBUG && /usr/sbin/ifmcstat # Not yet run on rump kernel + if $DEBUG; then + $mcast -d ${opts} -s $sleep & + else + $mcast ${opts} -s $sleep & + fi + #$DEBUG && /usr/sbin/ifmcstat # Not yet run on rump kernel + unset LD_PRELOAD + + # Give a chance to setup mcast + sleep 1 + + # Try to destroy an interface that the mcast program is running on + atf_check -s exit:0 rump.ifconfig shmif0 destroy + + wait + atf_check -s exit:0 -o ignore rump.ifconfig +} + add_test() { local name=$1 local opts="$2" local desc="$3" + local fulldesc= + fulldesc="Checks $desc" atf_test_case "mcast_${name}" cleanup - eval "mcast_${name}_head() { \ - atf_set \"descr\" \"${desc}\"; \ - atf_set \"require.progs\" \"rump_server\"; \ - }; \ - mcast_${name}_body() { \ - run_test \"${name}\" \"${opts}\"; \ - rump_server_destroy_ifaces; \ - }; \ - mcast_${name}_cleanup() { \ - ${DEBUG} && dump; \ - cleanup; \ + eval "mcast_${name}_head() { + atf_set descr \"${fulldesc}\" + atf_set require.progs rump_server + } + mcast_${name}_body() { + run_test ${name} \"${opts}\" + rump_server_destroy_ifaces + } + mcast_${name}_cleanup() { + \${DEBUG} && dump + cleanup }" atf_add_test_case "mcast_${name}" + + fulldesc="Destroying interface while testing ${desc}" + atf_test_case "mcast_destroyif_${name}" cleanup + eval "mcast_destroyif_${name}_head() { + atf_set descr \"${fulldesc}\" + atf_set require.progs rump_server + } + mcast_destroyif_${name}_body() { + run_test_destroyif ${name} \"${opts}\" + } + mcast_destroyif_${name}_cleanup() { + \${DEBUG} && dump + cleanup + }" + atf_add_test_case "mcast_destroyif_${name}" } atf_init_test_cases() { add_test conninet4 "-c -4" \ - "Checks connected multicast for ipv4" + "connected multicast for ipv4" add_test connmappedinet4 "-c -m -4" \ - "Checks connected multicast for mapped ipv4" + "connected multicast for mapped ipv4" add_test connmappedbuginet4 "-c -m -b -4" \ - "Checks connected multicast for mapped ipv4 using the v4 ioctls" + "connected multicast for mapped ipv4 using the v4 ioctls" add_test conninet6 "-c -6" \ - "Checks connected multicast for ipv6" + "connected multicast for ipv6" add_test unconninet4 "-4" \ - "Checks unconnected multicast for ipv4" + "unconnected multicast for ipv4" add_test unconnmappedinet4 "-m -4" \ - "Checks unconnected multicast for mapped ipv4" + "unconnected multicast for mapped ipv4" add_test unconnmappedbuginet4 "-m -b -4" \ - "Checks unconnected multicast for mapped ipv4 using the v4 ioctls" + "unconnected multicast for mapped ipv4 using the v4 ioctls" add_test unconninet6 "-6" \ - "Checks unconnected multicast for ipv6" + "unconnected multicast for ipv6" } diff --git a/net/mpls/Makefile b/net/mpls/Makefile --- a/net/mpls/Makefile +++ b/net/mpls/Makefile @@ -1,10 +1,13 @@ -# $NetBSD: Makefile,v 1.6 2015/05/27 18:13:14 kefren Exp $ +# $NetBSD: Makefile,v 1.7 2020/04/01 01:49:26 christos Exp $ # .include TESTSDIR= ${TESTSBASE}/net/mpls -TESTS_SH= t_mpls_fw t_mpls_fw6 t_mpls_fw64 t_rfc4182 t_ldp_regen +.for name in t_mpls_fw t_mpls_fw6 t_mpls_fw64 t_rfc4182 t_ldp_regen +TESTS_SH+= ${name} +TESTS_SH_SRC_${name}= mpls_common.sh ${name}.sh +.endfor .include diff --git a/net/mpls/mpls_common.sh b/net/mpls/mpls_common.sh new file mode 100644 --- /dev/null +++ b/net/mpls/mpls_common.sh @@ -0,0 +1,55 @@ +# $NetBSD: mpls_common.sh,v 1.1 2020/04/01 01:49:26 christos Exp $ +# +# Copyright (c) 2020 The NetBSD Foundation, Inc. +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +export PATH=/sbin:/usr/sbin:/bin:/usr/bin + +RUMP_SERVER1=unix://./r1 +RUMP_SERVER2=unix://./r2 +RUMP_SERVER3=unix://./r3 +RUMP_SERVER4=unix://./r4 + +RUMP_FLAGS6="-lrumpnet -lrumpnet_net -lrumpnet_netinet -lrumpnet_netinet6 \ + -lrumpnet_shmif -lrumpnet_netmpls" + +dostart() +{ + + ulimit -r 400 + atf_check -s exit:0 rump_server ${RUMP_FLAGS6} ${RUMP_SERVER1} + atf_check -s exit:0 rump_server ${RUMP_FLAGS6} ${RUMP_SERVER2} + atf_check -s exit:0 rump_server ${RUMP_FLAGS6} ${RUMP_SERVER3} + atf_check -s exit:0 rump_server ${RUMP_FLAGS6} ${RUMP_SERVER4} +} + +docleanup() +{ + + RUMP_SERVER=${RUMP_SERVER1} rump.halt + RUMP_SERVER=${RUMP_SERVER2} rump.halt + RUMP_SERVER=${RUMP_SERVER3} rump.halt + RUMP_SERVER=${RUMP_SERVER4} rump.halt +} diff --git a/net/mpls/t_ldp_regen.sh b/net/mpls/t_ldp_regen.sh --- a/net/mpls/t_ldp_regen.sh +++ b/net/mpls/t_ldp_regen.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_ldp_regen.sh,v 1.7 2016/08/10 07:50:37 ozaki-r Exp $ +# $NetBSD: t_ldp_regen.sh,v 1.10 2020/04/01 01:51:02 christos Exp $ # # Copyright (c) 2013 The NetBSD Foundation, Inc. # All rights reserved. @@ -35,14 +35,6 @@ # Now: * R4 should install label IMPLNULL for that prefix # * R3 should realloc the target label from IMPLNULL to something else - -RUMP_SERVER1=unix://./r1 -RUMP_SERVER2=unix://./r2 -RUMP_SERVER3=unix://./r3 -RUMP_SERVER4=unix://./r4 - -RUMP_LIBS="-lrumpnet -lrumpnet_net -lrumpnet_netinet -lrumpnet_netinet6 \ - -lrumpdev -lrumpnet_netmpls -lrumpnet_shmif" LDP_FLAGS="" atf_test_case ldp_regen cleanup @@ -68,15 +60,7 @@ rump.ping -n -o -w 5 10.0.5.1 } -create_servers() { - - # allows us to run as normal user - ulimit -r 400 - - atf_check -s exit:0 rump_server ${RUMP_LIBS} ${RUMP_SERVER1} - atf_check -s exit:0 rump_server ${RUMP_LIBS} ${RUMP_SERVER2} - atf_check -s exit:0 rump_server ${RUMP_LIBS} ${RUMP_SERVER3} - atf_check -s exit:0 rump_server ${RUMP_LIBS} ${RUMP_SERVER4} +configservers() { # LDP HIJACK export RUMPHIJACK=path=/rump,socket=all,sysctl=yes @@ -153,21 +137,14 @@ rump.ping -o -w 60 10.0.4.1 } -docleanup() { - - RUMP_SERVER=${RUMP_SERVER1} rump.halt - RUMP_SERVER=${RUMP_SERVER2} rump.halt - RUMP_SERVER=${RUMP_SERVER3} rump.halt - RUMP_SERVER=${RUMP_SERVER4} rump.halt -} - ldp_regen_body() { if sysctl machdep.cpu_brand 2>/dev/null | grep QEMU >/dev/null 2>&1 then atf_skip "unreliable under qemu, skip until PR kern/43997 fixed" fi - create_servers + dostart + configservers wait_ldp_ok newaddr_and_ping } diff --git a/net/mpls/t_mpls_fw.sh b/net/mpls/t_mpls_fw.sh old mode 100755 new mode 100644 --- a/net/mpls/t_mpls_fw.sh +++ b/net/mpls/t_mpls_fw.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_mpls_fw.sh,v 1.5 2016/08/10 07:50:37 ozaki-r Exp $ +# $NetBSD: t_mpls_fw.sh,v 1.7 2020/04/01 01:49:26 christos Exp $ # # Copyright (c) 2013 The NetBSD Foundation, Inc. # All rights reserved. @@ -36,15 +36,6 @@ # Do the same for the reverse direction (R4 to R1) # ping from R1 to R4 right hand side interface - -RUMP_SERVER1=unix://./r1 -RUMP_SERVER2=unix://./r2 -RUMP_SERVER3=unix://./r3 -RUMP_SERVER4=unix://./r4 - -RUMP_FLAGS="-lrumpnet -lrumpnet_net -lrumpnet_netinet \ - -lrumpdev -lrumpnet_netmpls -lrumpnet_shmif" - atf_test_case mplsfw4 cleanup mplsfw4_head() { @@ -53,16 +44,6 @@ atf_set "require.progs" "rump_server" } -startservers() -{ - - ulimit -r 300 - atf_check -s exit:0 rump_server ${RUMP_FLAGS} ${RUMP_SERVER1} - atf_check -s exit:0 rump_server ${RUMP_FLAGS} ${RUMP_SERVER2} - atf_check -s exit:0 rump_server ${RUMP_FLAGS} ${RUMP_SERVER3} - atf_check -s exit:0 rump_server ${RUMP_FLAGS} ${RUMP_SERVER4} -} - configservers() { @@ -133,19 +114,10 @@ unset RUMP_SERVER } -docleanup() -{ - - RUMP_SERVER=${RUMP_SERVER1} rump.halt - RUMP_SERVER=${RUMP_SERVER2} rump.halt - RUMP_SERVER=${RUMP_SERVER3} rump.halt - RUMP_SERVER=${RUMP_SERVER4} rump.halt -} - mplsfw4_body() { - startservers + dostart configservers 3 doping } @@ -168,7 +140,7 @@ mplsfw4_expl_body() { - startservers + dostart configservers 0 doping } diff --git a/net/mpls/t_mpls_fw6.sh b/net/mpls/t_mpls_fw6.sh --- a/net/mpls/t_mpls_fw6.sh +++ b/net/mpls/t_mpls_fw6.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_mpls_fw6.sh,v 1.3 2016/08/10 07:50:37 ozaki-r Exp $ +# $NetBSD: t_mpls_fw6.sh,v 1.5 2020/04/01 01:49:26 christos Exp $ # # Copyright (c) 2015 The NetBSD Foundation, Inc. # All rights reserved. @@ -38,14 +38,6 @@ # # redo the test using IPv6 explicit null label -RUMP_SERVER1=unix://./r1 -RUMP_SERVER2=unix://./r2 -RUMP_SERVER3=unix://./r3 -RUMP_SERVER4=unix://./r4 - -RUMP_FLAGS6="-lrumpnet -lrumpnet_net -lrumpnet_netinet -lrumpnet_netinet6 \ - -lrumpdev -lrumpnet_shmif -lrumpnet_netmpls" - atf_test_case mplsfw6 cleanup mplsfw6_head() { @@ -54,16 +46,6 @@ atf_set "require.progs" "rump_server" } -startservers() -{ - - ulimit -r 300 - atf_check -s exit:0 rump_server ${RUMP_FLAGS6} ${RUMP_SERVER1} - atf_check -s exit:0 rump_server ${RUMP_FLAGS6} ${RUMP_SERVER2} - atf_check -s exit:0 rump_server ${RUMP_FLAGS6} ${RUMP_SERVER3} - atf_check -s exit:0 rump_server ${RUMP_FLAGS6} ${RUMP_SERVER4} -} - configservers() { @@ -163,19 +145,10 @@ unset RUMP_SERVER } -docleanup() -{ - - RUMP_SERVER=${RUMP_SERVER1} rump.halt - RUMP_SERVER=${RUMP_SERVER2} rump.halt - RUMP_SERVER=${RUMP_SERVER3} rump.halt - RUMP_SERVER=${RUMP_SERVER4} rump.halt -} - mplsfw6_body() { - startservers + dostart configservers 3 do_check_route doping @@ -199,7 +172,7 @@ mplsfw6_expl_body() { - startservers + dostart configservers 2 do_check_route doping diff --git a/net/mpls/t_mpls_fw64.sh b/net/mpls/t_mpls_fw64.sh --- a/net/mpls/t_mpls_fw64.sh +++ b/net/mpls/t_mpls_fw64.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_mpls_fw64.sh,v 1.3 2016/08/10 07:50:37 ozaki-r Exp $ +# $NetBSD: t_mpls_fw64.sh,v 1.5 2020/04/01 01:49:26 christos Exp $ # # Copyright (c) 2015 The NetBSD Foundation, Inc. # All rights reserved. @@ -42,24 +42,6 @@ # ping6 from R1 to R4 right hand side interface -RUMP_SERVER1=unix://./r1 -RUMP_SERVER2=unix://./r2 -RUMP_SERVER3=unix://./r3 -RUMP_SERVER4=unix://./r4 - -RUMP_FLAGS6="-lrumpnet -lrumpnet_net -lrumpnet_netinet -lrumpnet_netinet6 \ - -lrumpdev -lrumpnet_shmif -lrumpnet_netmpls" - -startservers() -{ - - ulimit -r 300 - atf_check -s exit:0 rump_server ${RUMP_FLAGS6} ${RUMP_SERVER1} - atf_check -s exit:0 rump_server ${RUMP_FLAGS6} ${RUMP_SERVER2} - atf_check -s exit:0 rump_server ${RUMP_FLAGS6} ${RUMP_SERVER3} - atf_check -s exit:0 rump_server ${RUMP_FLAGS6} ${RUMP_SERVER4} -} - configservers() { @@ -161,15 +143,6 @@ unset RUMP_SERVER } -docleanup() -{ - - RUMP_SERVER=${RUMP_SERVER1} rump.halt - RUMP_SERVER=${RUMP_SERVER2} rump.halt - RUMP_SERVER=${RUMP_SERVER3} rump.halt - RUMP_SERVER=${RUMP_SERVER4} rump.halt -} - atf_test_case mplsfw64_impl cleanup mplsfw64_impl_head() { @@ -181,7 +154,7 @@ mplsfw64_impl_body() { - startservers + dostart configservers 3 do_check_route doping @@ -205,7 +178,7 @@ mplsfw64_expl_body() { - startservers + dostart configservers 2 do_check_route doping diff --git a/net/mpls/t_rfc4182.sh b/net/mpls/t_rfc4182.sh old mode 100755 new mode 100644 --- a/net/mpls/t_rfc4182.sh +++ b/net/mpls/t_rfc4182.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_rfc4182.sh,v 1.4 2016/08/10 07:50:37 ozaki-r Exp $ +# $NetBSD: t_rfc4182.sh,v 1.6 2020/04/01 01:49:26 christos Exp $ # # Copyright (c) 2013 The NetBSD Foundation, Inc. # All rights reserved. @@ -39,14 +39,6 @@ # ping from R1 to R4 right hand side interface -RUMP_SERVER1=unix://./r1 -RUMP_SERVER2=unix://./r2 -RUMP_SERVER3=unix://./r3 -RUMP_SERVER4=unix://./r4 - -RUMP_FLAGS="-lrumpnet -lrumpnet_net -lrumpnet_netinet \ - -lrumpdev -lrumpnet_netmpls -lrumpnet_shmif" - atf_test_case rfc4182 cleanup rfc4182_head() { @@ -55,16 +47,6 @@ atf_set "require.progs" "rump_server" } -startservers() -{ - - ulimit -r 300 - atf_check -s exit:0 rump_server ${RUMP_FLAGS} ${RUMP_SERVER1} - atf_check -s exit:0 rump_server ${RUMP_FLAGS} ${RUMP_SERVER2} - atf_check -s exit:0 rump_server ${RUMP_FLAGS} ${RUMP_SERVER3} - atf_check -s exit:0 rump_server ${RUMP_FLAGS} ${RUMP_SERVER4} -} - configservers() { @@ -135,19 +117,10 @@ unset RUMP_SERVER } -docleanup() -{ - - RUMP_SERVER=${RUMP_SERVER1} rump.halt - RUMP_SERVER=${RUMP_SERVER2} rump.halt - RUMP_SERVER=${RUMP_SERVER3} rump.halt - RUMP_SERVER=${RUMP_SERVER4} rump.halt -} - rfc4182_body() { - startservers + dostart configservers doping } diff --git a/net/ndp/Makefile b/net/ndp/Makefile --- a/net/ndp/Makefile +++ b/net/ndp/Makefile @@ -1,11 +1,11 @@ -# $NetBSD: Makefile,v 1.3 2016/11/24 08:52:20 ozaki-r Exp $ +# $NetBSD: Makefile,v 1.4 2020/06/12 11:04:45 roy Exp $ # .include TESTSDIR= ${TESTSBASE}/net/ndp -.for name in dad ndp ra +.for name in dad ndp TESTS_SH+= t_${name} TESTS_SH_SRC_t_${name}= ../net_common.sh t_${name}.sh .endfor diff --git a/net/ndp/t_dad.sh b/net/ndp/t_dad.sh old mode 100755 new mode 100644 --- a/net/ndp/t_dad.sh +++ b/net/ndp/t_dad.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_dad.sh,v 1.12 2016/11/25 08:51:17 ozaki-r Exp $ +# $NetBSD: t_dad.sh,v 1.14 2018/03/07 02:30:37 ozaki-r Exp $ # # Copyright (c) 2015 The NetBSD Foundation, Inc. # All rights reserved. @@ -31,6 +31,7 @@ DEBUG=${DEBUG:-false} duplicated="[Dd][Uu][Pp][Ll][Ii][Cc][Aa][Tt][Ee][Dd]" +tentative="[Tt][Ee][Nn][Tt][Aa][Tt][Ii][Vv][Ee]" atf_test_case dad_basic cleanup atf_test_case dad_duplicated cleanup @@ -73,9 +74,9 @@ { local id=$1 local target=$2 - pkt="33:33:ff:00:00:0${id}, ethertype IPv6 (0x86dd), length 78: ::" + pkt="33:33:ff:00:00:0${id}, ethertype IPv6 (0x86dd), length 86: ::" pkt="$pkt > ff02::1:ff00:${id}: ICMP6, neighbor solicitation," - pkt="$pkt who has $target, length 24" + pkt="$pkt who has $target, length 32" echo $pkt } @@ -95,11 +96,12 @@ $DEBUG && rump.ifconfig shmif0 atf_check -s exit:0 rump.ifconfig shmif0 up - rump.ifconfig shmif0 > ./out - $DEBUG && cat ./out + $DEBUG && rump.ifconfig shmif0 # The primary address doesn't start with tentative state - atf_check -s not-exit:0 -x "cat ./out |grep $localip1 |grep -q tentative" + atf_check -s exit:0 -o match:"$localip1" \ + -o not-match:"$localip1.+$tentative" \ + rump.ifconfig shmif0 # The alias address starts with tentative state # XXX we have no stable way to check this, so skip for now #atf_check -s exit:0 -x "cat ./out |grep $localip2 |grep -q tentative" @@ -110,10 +112,12 @@ # Check DAD probe packets (Neighbor Solicitation Message) pkt=$(make_ns_pkt_str 2 $localip2) - atf_check -s exit:0 -x "cat ./out |grep -q '$pkt'" + pkt=$(echo $pkt | sed 's/\([\(\)]\)/\\\1/g') + atf_check -s exit:0 -o match:"$pkt" cat ./out # No DAD for the primary address pkt=$(make_ns_pkt_str 1 $localip1) - atf_check -s not-exit:0 -x "cat ./out |grep -q '$pkt'" + pkt=$(echo $pkt | sed 's/\([\(\)]\)/\\\1/g') + atf_check -s exit:0 -o not-match:"$pkt" cat ./out # Waiting for DAD complete atf_check -s exit:0 rump.ifconfig -w 10 @@ -123,7 +127,9 @@ # IPv6 DAD doesn't announce (Neighbor Advertisement Message) # The alias address left tentative - atf_check -s not-exit:0 -x "rump.ifconfig shmif0 |grep $localip2 |grep -q tentative" + atf_check -s exit:0 -o match:"$localip2" \ + -o not-match:"$localip2.+$tentative" \ + rump.ifconfig shmif0 # # Add a new address on the fly @@ -139,7 +145,8 @@ extract_new_packets bus1 > ./out $DEBUG && cat ./out pkt=$(make_ns_pkt_str 3 $localip3) - atf_check -s exit:0 -x "cat ./out |grep -q '$pkt'" + pkt=$(echo $pkt | sed 's/\([\(\)]\)/\\\1/g') + atf_check -s exit:0 -o match:"$pkt" cat ./out # Waiting for DAD complete atf_check -s exit:0 rump.ifconfig -w 10 @@ -149,7 +156,9 @@ # IPv6 DAD doesn't announce (Neighbor Advertisement Message) # The new address left tentative - atf_check -s not-exit:0 -x "rump.ifconfig shmif0 |grep $localip3 |grep -q tentative" + atf_check -s exit:0 -o match:"$localip3" \ + -o not-match:"$localip3.+$tentative" \ + rump.ifconfig shmif0 rump_server_destroy_ifaces } @@ -169,7 +178,8 @@ export RUMP_SERVER=$SOCKLOCAL # The primary address isn't marked as duplicated - atf_check -s exit:0 -o not-match:"$localip1.+$duplicated" \ + atf_check -s exit:0 -o match:"$localip1" \ + -o not-match:"$localip1.+$duplicated" \ rump.ifconfig shmif0 # @@ -185,7 +195,8 @@ # A unique address isn't marked as duplicated atf_check -s exit:0 rump.ifconfig shmif0 inet6 $localip2 atf_check -s exit:0 sleep 1 - atf_check -s exit:0 -o not-match:"$localip2.+$duplicated" \ + atf_check -s exit:0 -o match:"$localip2" \ + -o not-match:"$localip2.+$duplicated" \ rump.ifconfig shmif0 rump_server_destroy_ifaces diff --git a/net/ndp/t_ndp.sh b/net/ndp/t_ndp.sh old mode 100755 new mode 100644 --- a/net/ndp/t_ndp.sh +++ b/net/ndp/t_ndp.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_ndp.sh,v 1.17 2016/11/25 08:51:17 ozaki-r Exp $ +# $NetBSD: t_ndp.sh,v 1.39 2020/09/17 11:56:35 roy Exp $ # # Copyright (c) 2015 The NetBSD Foundation, Inc. # All rights reserved. @@ -28,9 +28,13 @@ SOCKSRC=unix://commsock1 SOCKDST=unix://commsock2 IP6SRC=fc00::1 +IP6SRC2=fc00::3 IP6DST=fc00::2 +IP6NET=fc00::0 +IP6DST_FAIL1=fc00::99 +IP6DST_FAIL2=fc01::99 -DEBUG=${DEBUG:-true} +DEBUG=${DEBUG:-false} TIMEOUT=1 atf_test_case ndp_cache_expiration cleanup @@ -99,8 +103,8 @@ # Sanity check $DEBUG && rump.ifconfig shmif0 $DEBUG && rump.ndp -n -a - atf_check -s exit:0 -o ignore rump.ndp -n $IP6SRC - atf_check -s not-exit:0 -o ignore -e ignore rump.ndp -n $IP6DST + atf_check -s not-exit:0 -o ignore -e match:'no entry' rump.ndp -n $IP6SRC + atf_check -s not-exit:0 -o ignore -e match:'no entry' rump.ndp -n $IP6DST } get_timeout() @@ -112,6 +116,7 @@ ndp_cache_expiration_body() { + local macaddr= rump_server_start $SOCKSRC netinet6 rump_server_start $SOCKDST netinet6 @@ -119,14 +124,26 @@ setup_dst_server setup_src_server + # Shorten the expire time of cache entries + export RUMP_SERVER=$SOCKSRC + atf_check -s exit:0 -o match:'basereachable=7s0ms' \ + rump.ndp -i shmif0 basereachable=7000 + + # Make a permanent cache entry to avoid sending an NS packet disturbing + # the test + macaddr=$(get_macaddr $SOCKSRC shmif0) + export RUMP_SERVER=$SOCKDST + atf_check -s exit:0 -o ignore rump.ndp -s $IP6SRC $macaddr + + export RUMP_SERVER=$SOCKSRC + # # Check if a cache is expired expectedly # - export RUMP_SERVER=$SOCKSRC atf_check -s exit:0 -o ignore rump.ping6 -n -X $TIMEOUT -c 1 $IP6DST $DEBUG && rump.ndp -n -a - atf_check -s exit:0 -o match:'permanent' rump.ndp -n $IP6SRC + atf_check -s not-exit:0 -o ignore -e match:'no entry' rump.ndp -n $IP6SRC # Should be cached atf_check -s exit:0 -o not-match:'permanent' rump.ndp -n $IP6DST @@ -135,9 +152,9 @@ atf_check -s exit:0 sleep $(($timeout + 1)) $DEBUG && rump.ndp -n -a - atf_check -s exit:0 -o match:'permanent' rump.ndp -n $IP6SRC + atf_check -s not-exit:0 -o ignore -e match:'no entry' rump.ndp -n $IP6SRC # Expired but remains until GC sweaps it (1 day) - atf_check -s exit:0 -o match:'(1d0h0m|23h59m)' rump.ndp -n $IP6DST + atf_check -s exit:0 -o match:"$ONEDAYISH" rump.ndp -n $IP6DST rump_server_destroy_ifaces } @@ -160,17 +177,16 @@ export RUMP_SERVER=$SOCKSRC - # We can delete the entry for the interface's IP address - atf_check -s exit:0 -o match:"$IP6SRC" rump.ndp -d $IP6SRC - # Add and delete a static entry $DEBUG && rump.ndp -n -a atf_check -s exit:0 -o ignore rump.ndp -s fc00::10 b2:a0:20:00:00:10 $DEBUG && rump.ndp -n -a atf_check -s exit:0 -o match:'permanent' rump.ndp -n fc00::10 + check_route fc00::10 'b2:a0:20:00:00:10' UHLS shmif0 atf_check -s exit:0 -o match:'deleted' rump.ndp -d fc00::10 $DEBUG && rump.ndp -n -a atf_check -s not-exit:0 -o ignore -e ignore rump.ndp -n fc00::10 + check_route_no_entry fc00::10 # Add multiple entries via a file (XXX not implemented) #cat - > ./list <<-EOF @@ -191,6 +207,9 @@ atf_check -s exit:0 -o not-match:'permanent' rump.ndp -n $IP6DST atf_check -s exit:0 -o match:'permanent' rump.ndp -n fc00::11 atf_check -s exit:0 -o match:'permanent' rump.ndp -n fc00::12 + check_route_flags $IP6DST UHL + check_route_flags fc00::11 UHLS + check_route_flags fc00::12 UHLS # Test ndp -a atf_check -s exit:0 -o match:'fc00::11' rump.ndp -n -a @@ -204,15 +223,20 @@ atf_check -s exit:0 -o ignore rump.ndp -c atf_check -s not-exit:0 -o ignore -e ignore rump.ndp -n $IP6SRC atf_check -s not-exit:0 -o ignore -e ignore rump.ndp -n $IP6DST + #check_route_no_entry $IP6SRC + check_route_no_entry $IP6DST # Only the static caches are not deleted atf_check -s exit:0 -o ignore -e ignore rump.ndp -n fc00::11 atf_check -s exit:0 -o ignore -e ignore rump.ndp -n fc00::12 + check_route_flags fc00::11 UHLS + check_route_flags fc00::12 UHLS $DEBUG && rump.ndp -n -a atf_check -s exit:0 -o ignore rump.ndp -s fc00::10 b2:a0:20:00:00:10 temp rump.ndp -s fc00::10 b2:a0:20:00:00:10 temp $DEBUG && rump.ndp -n -a atf_check -s exit:0 -o not-match:'permanent' rump.ndp -n fc00::10 + check_route fc00::10 'b2:a0:20:00:00:10' UHL shmif0 rump_server_destroy_ifaces } @@ -229,8 +253,9 @@ export RUMP_SERVER=$SOCKSRC # Cannot overwrite a permanent cache - atf_check -s not-exit:0 -e ignore rump.ndp -s $IP6SRC b2:a0:20:00:00:ff + atf_check -s exit:0 rump.ndp -s $IP6SRC b2:a0:20:00:00:ff $DEBUG && rump.ndp -n -a + atf_check -s not-exit:0 -e ignore rump.ndp -s $IP6SRC b2:a0:20:00:00:fe atf_check -s exit:0 -o ignore rump.ping6 -n -X $TIMEOUT -c 1 $IP6DST $DEBUG && rump.ndp -n -a @@ -396,6 +421,390 @@ cleanup } +atf_test_case ndp_rtm cleanup +ndp_rtm_head() +{ + + atf_set "descr" "Tests for routing messages on operations of NDP entries" + atf_set "require.progs" "rump_server" +} + +ndp_rtm_body() +{ + local macaddr_src= macaddr_dst= + local file=./tmp + local pid= hdr= what= addr= + + rump_server_start $SOCKSRC netinet6 + rump_server_start $SOCKDST netinet6 + + setup_dst_server + setup_src_server + + macaddr_src=$(get_macaddr $SOCKSRC shmif0) + macaddr_dst=$(get_macaddr $SOCKDST shmif0) + + export RUMP_SERVER=$SOCKSRC + + # Test ping and a resulting routing message (RTM_ADD) + rump.route -n monitor -c 1 > $file & + pid=$! + sleep 1 + atf_check -s exit:0 -o ignore rump.ping6 -n -X 1 -c 1 $IP6DST + wait $pid + $DEBUG && cat $file + + hdr="RTM_ADD.+" + what="" + addr="$IP6DST $macaddr_dst $IP6DST" + atf_check -s exit:0 -o match:"$hdr" -o match:"$what" -o match:"$addr" \ + cat $file + + # Test ping and a resulting routing message (RTM_MISS) on subnet + rump.route -n monitor -c 1 > $file & + pid=$! + sleep 1 + atf_check -s exit:1 -o ignore -e ignore \ + rump.ping6 -n -X 1 -c 1 $IP6DST_FAIL1 + wait $pid + $DEBUG && cat $file + + hdr="RTM_MISS.+" + what="" + addr="$IP6DST_FAIL1 link#2 $IP6SRC" + atf_check -s exit:0 -o match:"$hdr" -o match:"$what" -o match:"$addr" \ + cat $file + + # Test ping and a resulting routing message (RTM_MISS) off subnet + rump.route -n monitor -c 1 > $file & + pid=$! + sleep 1 + atf_check -s exit:1 -o ignore -e ignore \ + rump.ping6 -n -X 1 -c 1 $IP6DST_FAIL2 + wait $pid + $DEBUG && cat $file + + hdr="RTM_MISS.+" + what="" + addr="$IP6DST_FAIL2" + atf_check -s exit:0 -o match:"$hdr" -o match:"$what" -o match:"$addr" \ + cat $file + + # Test ndp -d and resulting routing messages (RTM_DELETE) + rump.route -n monitor -c 1 > $file & + pid=$! + sleep 1 + atf_check -s exit:0 -o ignore rump.ndp -d $IP6DST + wait $pid + $DEBUG && cat $file + + hdr="RTM_DELETE.+" + what="" + addr="$IP6DST $macaddr_dst" + atf_check -s exit:0 -o match:"$hdr" -o match:"$what" -o match:"$addr" \ + grep -A 3 RTM_DELETE $file + + rump_server_destroy_ifaces +} + +ndp_rtm_cleanup() +{ + + $DEBUG && dump + cleanup +} + +atf_test_case ndp_purge_on_route_change cleanup +ndp_purge_on_route_change_head() +{ + + atf_set "descr" "Tests if NDP entries are removed on route change" + atf_set "require.progs" "rump_server" +} + +ndp_purge_on_route_change_body() +{ + + rump_server_start $SOCKSRC netinet6 + rump_server_start $SOCKDST netinet6 + + setup_dst_server + setup_src_server + + rump_server_add_iface $SOCKSRC shmif1 bus1 + export RUMP_SERVER=$SOCKSRC + atf_check -s exit:0 rump.ifconfig shmif1 inet6 fc00:1::1 + atf_check -s exit:0 rump.ifconfig -w 10 + + $DEBUG && rump.netstat -nr -f inet6 + atf_check -s exit:0 -o ignore rump.ping6 -n -X 1 -c 1 $IP6DST + atf_check -s exit:0 -o match:'shmif0' rump.ndp -n $IP6DST + + atf_check -s exit:0 -o ignore \ + rump.route change -inet6 -net $IP6NET/64 -ifp shmif1 + $DEBUG && rump.netstat -nr -f inet6 + $DEBUG && rump.ndp -na + # The entry was already removed on route change + atf_check -s not-exit:0 -o ignore -e match:'no entry' \ + rump.ndp -n $IP6DST + + rump_server_destroy_ifaces +} + +ndp_purge_on_route_change_cleanup() +{ + + $DEBUG && dump + cleanup +} + +atf_test_case ndp_purge_on_route_delete cleanup +ndp_purge_on_route_delete_head() +{ + + atf_set "descr" "Tests if NDP entries are removed on route delete" + atf_set "require.progs" "rump_server" +} + +ndp_purge_on_route_delete_body() +{ + + rump_server_start $SOCKSRC netinet6 + rump_server_start $SOCKDST netinet6 + + setup_dst_server + setup_src_server + + $DEBUG && rump.netstat -nr -f inet6 + atf_check -s exit:0 -o ignore rump.ping6 -n -X 1 -c 1 $IP6DST + atf_check -s exit:0 -o match:'shmif0' rump.ndp -n $IP6DST + + atf_check -s exit:0 -o ignore rump.route delete -inet6 -net $IP6NET/64 + $DEBUG && rump.netstat -nr -f inet6 + $DEBUG && rump.ndp -na + + # The entry was already removed on route delete + atf_check -s not-exit:0 -o ignore -e match:'no entry' \ + rump.ndp -n $IP6DST + + rump_server_destroy_ifaces +} + +ndp_purge_on_route_delete_cleanup() +{ + + $DEBUG && dump + cleanup +} + +atf_test_case ndp_purge_on_ifdown cleanup +ndp_purge_on_ifdown_head() +{ + + atf_set "descr" "Tests if NDP entries are removed on interface down" + atf_set "require.progs" "rump_server" +} + +ndp_purge_on_ifdown_body() +{ + + rump_server_start $SOCKSRC netinet6 + rump_server_start $SOCKDST netinet6 + + setup_dst_server + setup_src_server + + $DEBUG && rump.netstat -nr -f inet6 + atf_check -s exit:0 -o ignore rump.ping6 -n -X 1 -c 1 $IP6DST + atf_check -s exit:0 -o match:'shmif0' rump.ndp -n $IP6DST + + # Shutdown the interface + atf_check -s exit:0 rump.ifconfig shmif0 down + $DEBUG && rump.netstat -nr -f inet6 + $DEBUG && rump.ndp -na + + # The entry was already removed on ifconfig down + atf_check -s not-exit:0 -o ignore -e match:'no entry' \ + rump.ndp -n $IP6DST + + rump_server_destroy_ifaces +} + +ndp_purge_on_ifdown_cleanup() +{ + + $DEBUG && dump + cleanup +} + +atf_test_case ndp_stray_entries cleanup +ndp_stray_entries_head() +{ + + atf_set "descr" "Tests if NDP entries are removed on route change" + atf_set "require.progs" "rump_server" +} + +ndp_stray_entries_body() +{ + + rump_server_start $SOCKSRC netinet6 + rump_server_start $SOCKDST netinet6 + + setup_dst_server + setup_src_server + + rump_server_add_iface $SOCKSRC shmif1 bus1 + + export RUMP_SERVER=$SOCKSRC + atf_check -s exit:0 rump.ifconfig shmif1 inet6 $IP6SRC2/64 + atf_check -s exit:0 rump.ifconfig -w 10 + + $DEBUG && rump.netstat -nr -f inet6 + atf_check -s exit:0 -o ignore rump.ping6 -n -X 1 -c 1 $IP6DST + $DEBUG && rump.ndp -na + atf_check -s exit:0 -o match:'shmif0' rump.ndp -n $IP6DST + atf_check -s exit:0 -o not-match:'shmif1' rump.ndp -n $IP6DST + + # Clean up + atf_check -s exit:0 -o ignore rump.ndp -c + atf_check -s not-exit:0 -o ignore -e match:'no entry' rump.ndp -n $IP6DST + + # ping from a different source address + atf_check -s exit:0 -o ignore \ + rump.ping6 -n -X 1 -c 1 -S $IP6SRC2 $IP6DST + $DEBUG && rump.ndp -na + atf_check -s exit:0 -o match:'shmif0' rump.ndp -n $IP6DST + # ARP reply goes back via shmif1, so a cache is created on shmif1 + atf_check -s exit:0 -o match:'shmif1' rump.ndp -n $IP6DST + + # Clean up by ndp -c + atf_check -s exit:0 -o ignore rump.ndp -c + atf_check -s not-exit:0 -o ignore -e match:'no entry' rump.ndp -n $IP6DST + + # ping from a different source address again + atf_check -s exit:0 -o ignore \ + rump.ping6 -n -X 1 -c 1 -S $IP6SRC2 $IP6DST + atf_check -s exit:0 -o match:'shmif0' rump.ndp -n $IP6DST + # ARP reply doen't come + atf_check -s exit:0 -o not-match:'shmif1' rump.ndp -n $IP6DST + + # Cleanup caches on the destination + export RUMP_SERVER=$SOCKDST + $DEBUG && rump.ndp -na + atf_check -s exit:0 -o ignore rump.ndp -c + $DEBUG && rump.ndp -na + export RUMP_SERVER=$SOCKSRC + + # ping from a different source address again + atf_check -s exit:0 -o ignore \ + rump.ping6 -n -X 1 -c 1 -S $IP6SRC2 $IP6DST + atf_check -s exit:0 -o match:'shmif0' rump.ndp -n $IP6DST + # ARP reply goes back via shmif1 + atf_check -s exit:0 -o match:'shmif1' rump.ndp -n $IP6DST + + # Clean up by ndp -d + atf_check -s exit:0 -o ignore rump.ndp -d $IP6DST + # Both entries should be deleted + atf_check -s not-exit:0 -o ignore -e match:'no entry' rump.ndp -n $IP6DST + + rump_server_destroy_ifaces +} + +ndp_stray_entries_cleanup() +{ + + $DEBUG && dump + cleanup +} + +atf_test_case ndp_cache_state cleanup +ndp_cache_state_head() +{ + + atf_set "descr" "Tests states of neighbor cache entries" + atf_set "require.progs" "rump_server" +} + +check_cache_state() +{ + local dst=$1 + local state=$2 + + $DEBUG && rump.ndp -n $dst + atf_check -s exit:0 -o match:"^$dst.*$state " rump.ndp -n $dst +} + +wait_until_stalled() +{ + local dst=$1 + local state=$2 + + $DEBUG && rump.ndp -n $dst + while true; do + rump.ndp -n $dst | grep -q "^$dst.*S " && break + sleep 1 + done + $DEBUG && rump.ndp -n $dst +} + +ndp_cache_state_body() +{ + local macaddr= + + rump_server_start $SOCKSRC netinet6 + rump_server_start $SOCKDST netinet6 + + setup_dst_server + setup_src_server + + # Shorten the expire time of cache entries + export RUMP_SERVER=$SOCKSRC + atf_check -s exit:0 -o match:'basereachable=7s0ms' \ + rump.ndp -i shmif0 basereachable=7000 + + # Make a permanent cache entry to avoid sending an NS packet disturbing + # the test + macaddr=$(get_macaddr $SOCKSRC shmif0) + export RUMP_SERVER=$SOCKDST + atf_check -s exit:0 -o ignore rump.ndp -s $IP6SRC $macaddr + + export RUMP_SERVER=$SOCKSRC + + # + # Reachability confirmation (RFC 4861 7.3.3) + # + atf_check -s exit:0 -o ignore rump.ping6 -n -X $TIMEOUT -c 1 $IP6DST + + # Receiving a solicited NA packet changes the state of the cache to REACHABLE + check_cache_state $IP6DST R + + # The state of the cache transits to STALE after a while + wait_until_stalled $IP6DST + + # Sending a packet on the cache will run a reachability confirmation + atf_check -s exit:0 -o ignore rump.ping6 -n -X $TIMEOUT -c 1 $IP6DST + + sleep 1 + + # The state of the cache is changed to DELAY and stay for 5s, then + # send a NS packet and change the state to PROBE + check_cache_state $IP6DST D + + sleep $((5 + 1)) + + # If the reachability confirmation is success, the state of the cache + # is changed to REACHABLE + check_cache_state $IP6DST R +} + +ndp_cache_state_cleanup() +{ + + $DEBUG && dump + cleanup +} + atf_init_test_cases() { atf_add_test_case ndp_cache_expiration @@ -403,4 +812,10 @@ atf_add_test_case ndp_cache_overwriting atf_add_test_case ndp_neighborgcthresh atf_add_test_case ndp_link_activation + atf_add_test_case ndp_rtm + atf_add_test_case ndp_purge_on_route_change + atf_add_test_case ndp_purge_on_route_delete + atf_add_test_case ndp_purge_on_ifdown + atf_add_test_case ndp_stray_entries + atf_add_test_case ndp_cache_state } diff --git a/net/ndp/t_ra.sh b/net/ndp/t_ra.sh old mode 100755 new mode 100644 --- a/net/ndp/t_ra.sh +++ b/net/ndp/t_ra.sh @@ -1,703 +0,0 @@ -# $NetBSD: t_ra.sh,v 1.24 2017/01/13 08:11:01 ozaki-r Exp $ -# -# Copyright (c) 2015 Internet Initiative Japan Inc. -# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. -# - -RUMPSRV=unix://r1 -RUMPSRV1_2=unix://r12 -RUMPCLI=unix://r2 -RUMPSRV3=unix://r3 -RUMPSRV4=unix://r4 -IP6SRV=fc00:1::1 -IP6SRV1_2=fc00:1::2 -IP6SRV_PREFIX=fc00:1: -IP6CLI=fc00:2::2 -IP6SRV3=fc00:3::1 -IP6SRV3_PREFIX=fc00:3: -IP6SRV4=fc00:4::1 -IP6SRV4_PREFIX=fc00:4: -PIDFILE=./rump.rtadvd.pid -PIDFILE1_2=./rump.rtadvd.pid12 -PIDFILE3=./rump.rtadvd.pid3 -PIDFILE4=./rump.rtadvd.pid4 -CONFIG=./rtadvd.conf -WAITTIME=2 -DEBUG=${DEBUG:-true} - -init_server() -{ - - export RUMP_SERVER=$1 - atf_check -s exit:0 -o match:'0.->.1' rump.sysctl -w net.inet6.ip6.forwarding=1 - export LD_PRELOAD=/usr/lib/librumphijack.so - atf_check -s exit:0 mkdir -p /rump/var/chroot/rtadvd - unset LD_PRELOAD - unset RUMP_SERVER -} - -setup_shmif0() -{ - local sock=$1 - local IP6ADDR=$2 - - rump_server_add_iface $sock shmif0 bus1 - - export RUMP_SERVER=$sock - atf_check -s exit:0 rump.ifconfig shmif0 inet6 ${IP6ADDR} - atf_check -s exit:0 rump.ifconfig shmif0 up - atf_check -s exit:0 rump.ifconfig -w 10 - - $DEBUG && rump.ifconfig -} - -wait_term() -{ - local PIDFILE=${1} - shift - - while [ -f ${PIDFILE} ] - do - sleep 0.2 - done - - return 0 -} - -create_rtadvdconfig() -{ - - cat << _EOF > ${CONFIG} -shmif0:\ - :mtu#1300:maxinterval#4:mininterval#3: -_EOF -} - -start_rtadvd() -{ - local sock=$1 - local pidfile=$2 - - export RUMP_SERVER=$sock - atf_check -s exit:0 rump.rtadvd -c ${CONFIG} -p $pidfile shmif0 - while [ ! -f $pidfile ]; do - sleep 0.2 - done - unset RUMP_SERVER -} - -check_entries() -{ - local cli=$1 - local srv=$2 - local addr_prefix=$3 - local mac_srv= ll_srv= - - ll_srv=$(get_linklocal_addr $srv shmif0) - mac_srv=$(get_macaddr $srv shmif0) - - export RUMP_SERVER=$cli - $DEBUG && dump_entries - atf_check -s exit:0 -o match:'if=shmif0' rump.ndp -r - atf_check -s exit:0 -o match:'advertised' rump.ndp -p - atf_check -s exit:0 -o match:"${ll_srv}%shmif0 \(reachable\)" rump.ndp -p - atf_check -s exit:0 -o match:'linkmtu=1300' rump.ndp -n -i shmif0 - atf_check -s exit:0 \ - -o match:"$ll_srv%shmif0 +$mac_srv +shmif0 +(23h59m|1d0h0m)..s S R" \ - rump.ndp -n -a - atf_check -s exit:0 -o match:$addr_prefix rump.ndp -n -a - atf_check -s exit:0 \ - -o match:"$addr_prefix.+<(TENTATIVE,)?AUTOCONF>" \ - rump.ifconfig shmif0 inet6 - unset RUMP_SERVER -} - -dump_entries() -{ - - echo ndp -n -a - rump.ndp -n -a - echo ndp -p - rump.ndp -p - echo ndp -r - rump.ndp -r -} - -atf_test_case ra_basic cleanup -ra_basic_head() -{ - - atf_set "descr" "Tests for basic functions of router advaertisement(RA)" - atf_set "require.progs" "rump_server rump.rtadvd rump.ndp rump.ifconfig" -} - -ra_basic_body() -{ - - rump_server_fs_start $RUMPSRV netinet6 - rump_server_start $RUMPCLI netinet6 - - setup_shmif0 ${RUMPSRV} ${IP6SRV} - init_server $RUMPSRV - - setup_shmif0 ${RUMPCLI} ${IP6CLI} - export RUMP_SERVER=${RUMPCLI} - $DEBUG && rump.ndp -n -a - atf_check -s exit:0 -o match:'= 0' rump.sysctl net.inet6.ip6.accept_rtadv - unset RUMP_SERVER - - create_rtadvdconfig - start_rtadvd $RUMPSRV $PIDFILE - sleep $WAITTIME - - export RUMP_SERVER=${RUMPCLI} - atf_check -s exit:0 -o empty rump.ndp -r - atf_check -s exit:0 -o not-match:'advertised' rump.ndp -p - atf_check -s exit:0 -o match:'linkmtu=0' rump.ndp -n -i shmif0 - atf_check -s exit:0 -o not-match:'S R' rump.ndp -n -a - atf_check -s exit:0 -o not-match:'fc00:1:' rump.ndp -n -a - atf_check -s exit:0 -o not-match:'fc00:1:' rump.ifconfig shmif0 inet6 - unset RUMP_SERVER - - atf_check -s exit:0 kill -TERM `cat ${PIDFILE}` - wait_term ${PIDFILE} - - export RUMP_SERVER=${RUMPCLI} - atf_check -s exit:0 -o match:'0.->.1' rump.sysctl -w net.inet6.ip6.accept_rtadv=1 - unset RUMP_SERVER - - start_rtadvd $RUMPSRV $PIDFILE - sleep $WAITTIME - - check_entries $RUMPCLI $RUMPSRV $IP6SRV_PREFIX - - atf_check -s exit:0 kill -TERM `cat ${PIDFILE}` - wait_term ${PIDFILE} - - rump_server_destroy_ifaces -} - -ra_basic_cleanup() -{ - - if [ -f ${PIDFILE} ]; then - kill -TERM `cat ${PIDFILE}` - wait_term ${PIDFILE} - fi - - $DEBUG && dump - cleanup -} - -atf_test_case ra_flush_prefix_entries cleanup -ra_flush_prefix_entries_head() -{ - - atf_set "descr" "Tests for flushing prefixes (ndp -P)" - atf_set "require.progs" "rump_server rump.rtadvd rump.ndp rump.ifconfig" -} - -ra_flush_prefix_entries_body() -{ - - rump_server_fs_start $RUMPSRV netinet6 - rump_server_start $RUMPCLI netinet6 - - setup_shmif0 ${RUMPSRV} ${IP6SRV} - setup_shmif0 ${RUMPCLI} ${IP6CLI} - - init_server $RUMPSRV - - create_rtadvdconfig - - export RUMP_SERVER=${RUMPCLI} - atf_check -s exit:0 -o match:'0.->.1' rump.sysctl -w net.inet6.ip6.accept_rtadv=1 - unset RUMP_SERVER - - start_rtadvd $RUMPSRV $PIDFILE - sleep $WAITTIME - - check_entries $RUMPCLI $RUMPSRV $IP6SRV_PREFIX - - export RUMP_SERVER=${RUMPCLI} - - # Terminate rtadvd to prevent new RA messages from coming - # Note that ifconfig down; kill -TERM doesn't work - kill -KILL `cat ${PIDFILE}` - - # Flush all the entries in the prefix list - atf_check -s exit:0 rump.ndp -P - - $DEBUG && dump_entries - atf_check -s exit:0 -o match:'if=shmif0' rump.ndp -r - atf_check -s exit:0 -o empty rump.ndp -p - atf_check -s exit:0 -o match:'linkmtu=1300' rump.ndp -n -i shmif0 - atf_check -s exit:0 -o match:'(23h59m|1d0h0m)..s S R' rump.ndp -n -a - atf_check -s exit:0 -o match:'fc00:1:' rump.ndp -n -a - atf_check -s exit:0 -o not-match:'fc00:1:' rump.ifconfig shmif0 inet6 - unset RUMP_SERVER - - rump_server_destroy_ifaces -} - -ra_flush_prefix_entries_cleanup() -{ - - $DEBUG && dump - cleanup -} - -atf_test_case ra_flush_defrouter_entries cleanup -ra_flush_defrouter_entries_head() -{ - - atf_set "descr" "Tests for flushing default routers (ndp -R)" - atf_set "require.progs" "rump_server rump.rtadvd rump.ndp rump.ifconfig" -} - -ra_flush_defrouter_entries_body() -{ - - rump_server_fs_start $RUMPSRV netinet6 - rump_server_start $RUMPCLI netinet6 - - setup_shmif0 ${RUMPSRV} ${IP6SRV} - setup_shmif0 ${RUMPCLI} ${IP6CLI} - - init_server $RUMPSRV - - create_rtadvdconfig - - export RUMP_SERVER=${RUMPCLI} - atf_check -s exit:0 -o match:'0.->.1' rump.sysctl -w net.inet6.ip6.accept_rtadv=1 - unset RUMP_SERVER - - start_rtadvd $RUMPSRV $PIDFILE - sleep $WAITTIME - - check_entries $RUMPCLI $RUMPSRV $IP6SRV_PREFIX - - export RUMP_SERVER=${RUMPCLI} - - # Terminate rtadvd to prevent new RA messages from coming - # Note that ifconfig down; kill -TERM doesn't work - kill -KILL `cat ${PIDFILE}` - - # Flush all the entries in the default router list - atf_check -s exit:0 rump.ndp -R - - $DEBUG && dump_entries - atf_check -s exit:0 -o empty rump.ndp -r - atf_check -s exit:0 -o match:'No advertising router' rump.ndp -p - atf_check -s exit:0 -o match:'linkmtu=1300' rump.ndp -n -i shmif0 - atf_check -s exit:0 -o match:'(23h59m|1d0h0m)..s S R' rump.ndp -n -a - atf_check -s exit:0 -o match:'fc00:1:' rump.ndp -n -a - atf_check -s exit:0 -o match:'fc00:1:' rump.ifconfig shmif0 inet6 - unset RUMP_SERVER - - rump_server_destroy_ifaces -} - -ra_flush_defrouter_entries_cleanup() -{ - - $DEBUG && dump - cleanup -} - -atf_test_case ra_delete_address cleanup -ra_delete_address_head() -{ - - atf_set "descr" "Tests for deleting auto-configured address" - atf_set "require.progs" "rump_server rump.rtadvd rump.ndp rump.ifconfig" -} - -ra_delete_address_body() -{ - - rump_server_fs_start $RUMPSRV netinet6 - rump_server_start $RUMPCLI netinet6 - - setup_shmif0 ${RUMPSRV} ${IP6SRV} - setup_shmif0 ${RUMPCLI} ${IP6CLI} - - init_server $RUMPSRV - - create_rtadvdconfig - - export RUMP_SERVER=${RUMPCLI} - atf_check -s exit:0 -o match:'0.->.1' rump.sysctl -w net.inet6.ip6.accept_rtadv=1 - unset RUMP_SERVER - - start_rtadvd $RUMPSRV $PIDFILE - sleep $WAITTIME - - check_entries $RUMPCLI $RUMPSRV $IP6SRV_PREFIX - - export RUMP_SERVER=${RUMPCLI} - $DEBUG && rump.ifconfig shmif0 - atf_check -s exit:0 rump.ifconfig shmif0 inet6 \ - $(rump.ifconfig shmif0 |awk '/AUTOCONF/ {print $2}') delete - unset RUMP_SERVER - - atf_check -s exit:0 kill -TERM `cat ${PIDFILE}` - wait_term ${PIDFILE} - - rump_server_destroy_ifaces -} - -ra_delete_address_cleanup() -{ - - if [ -f ${PIDFILE} ]; then - kill -TERM `cat ${PIDFILE}` - wait_term ${PIDFILE} - fi - - $DEBUG && dump - cleanup -} - -atf_test_case ra_multiple_routers cleanup -ra_multiple_routers_head() -{ - - atf_set "descr" "Tests for multiple routers" - atf_set "require.progs" "rump_server rump.rtadvd rump.ndp rump.ifconfig" -} - -ra_multiple_routers_body() -{ - local n= - - rump_server_fs_start $RUMPSRV netinet6 - rump_server_fs_start $RUMPSRV3 netinet6 - rump_server_start $RUMPCLI netinet6 - - setup_shmif0 ${RUMPSRV} ${IP6SRV} - setup_shmif0 ${RUMPSRV3} ${IP6SRV3} - setup_shmif0 ${RUMPCLI} ${IP6CLI} - - init_server $RUMPSRV - init_server $RUMPSRV3 - - create_rtadvdconfig - - export RUMP_SERVER=${RUMPCLI} - atf_check -s exit:0 -o match:'0.->.1' rump.sysctl -w net.inet6.ip6.accept_rtadv=1 - unset RUMP_SERVER - - start_rtadvd $RUMPSRV $PIDFILE - start_rtadvd $RUMPSRV3 $PIDFILE3 - sleep $WAITTIME - - check_entries $RUMPCLI $RUMPSRV $IP6SRV_PREFIX - check_entries $RUMPCLI $RUMPSRV3 $IP6SRV3_PREFIX - - export RUMP_SERVER=$RUMPCLI - # Two prefixes are advertised by differnt two routers - n=$(rump.ndp -p |grep 'advertised by' |wc -l) - atf_check_equal $n 2 - unset RUMP_SERVER - - atf_check -s exit:0 kill -TERM `cat ${PIDFILE}` - wait_term ${PIDFILE} - atf_check -s exit:0 kill -TERM `cat ${PIDFILE3}` - wait_term ${PIDFILE3} - - rump_server_destroy_ifaces -} - -ra_multiple_routers_cleanup() -{ - - if [ -f ${PIDFILE} ]; then - kill -TERM `cat ${PIDFILE}` - wait_term ${PIDFILE} - fi - if [ -f ${PIDFILE3} ]; then - kill -TERM `cat ${PIDFILE3}` - wait_term ${PIDFILE3} - fi - - $DEBUG && dump - cleanup -} - -atf_test_case ra_multiple_routers_single_prefix cleanup -ra_multiple_routers_single_prefix_head() -{ - - atf_set "descr" "Tests for multiple routers with a single prefix" - atf_set "require.progs" "rump_server rump.rtadvd rump.ndp rump.ifconfig" -} - -ra_multiple_routers_single_prefix_body() -{ - local n= - - rump_server_fs_start $RUMPSRV netinet6 - rump_server_fs_start $RUMPSRV1_2 netinet6 - rump_server_start $RUMPCLI netinet6 - - setup_shmif0 ${RUMPSRV} ${IP6SRV} - setup_shmif0 ${RUMPSRV1_2} ${IP6SRV1_2} - setup_shmif0 ${RUMPCLI} ${IP6CLI} - - init_server $RUMPSRV - init_server $RUMPSRV1_2 - - create_rtadvdconfig - - export RUMP_SERVER=${RUMPCLI} - atf_check -s exit:0 -o match:'0.->.1' rump.sysctl -w net.inet6.ip6.accept_rtadv=1 - unset RUMP_SERVER - - start_rtadvd $RUMPSRV $PIDFILE - start_rtadvd $RUMPSRV1_2 $PIDFILE1_2 - sleep $WAITTIME - - check_entries $RUMPCLI $RUMPSRV $IP6SRV_PREFIX - check_entries $RUMPCLI $RUMPSRV1_2 $IP6SRV_PREFIX - - export RUMP_SERVER=$RUMPCLI - # One prefix is advertised by differnt two routers - n=$(rump.ndp -p |grep 'advertised by' |wc -l) - atf_check_equal $n 1 - unset RUMP_SERVER - - atf_check -s exit:0 kill -TERM `cat ${PIDFILE}` - wait_term ${PIDFILE} - atf_check -s exit:0 kill -TERM `cat ${PIDFILE1_2}` - wait_term ${PIDFILE1_2} - - rump_server_destroy_ifaces -} - -ra_multiple_routers_single_prefix_cleanup() -{ - - if [ -f ${PIDFILE} ]; then - kill -TERM `cat ${PIDFILE}` - wait_term ${PIDFILE} - fi - if [ -f ${PIDFILE1_2} ]; then - kill -TERM `cat ${PIDFILE1_2}` - wait_term ${PIDFILE1_2} - fi - - $DEBUG && dump - cleanup -} - -atf_test_case ra_multiple_routers_maxifprefixes cleanup -ra_multiple_routers_maxifprefixes_head() -{ - - atf_set "descr" "Tests for exceeding the number of maximum prefixes" - atf_set "require.progs" "rump_server rump.rtadvd rump.ndp rump.ifconfig" -} - -ra_multiple_routers_maxifprefixes_body() -{ - local n= - - rump_server_fs_start $RUMPSRV netinet6 - rump_server_fs_start $RUMPSRV3 netinet6 - rump_server_fs_start $RUMPSRV4 netinet6 - rump_server_start $RUMPCLI netinet6 - - setup_shmif0 ${RUMPSRV} ${IP6SRV} - setup_shmif0 ${RUMPSRV3} ${IP6SRV3} - setup_shmif0 ${RUMPSRV4} ${IP6SRV4} - setup_shmif0 ${RUMPCLI} ${IP6CLI} - - init_server $RUMPSRV - init_server $RUMPSRV3 - init_server $RUMPSRV4 - - create_rtadvdconfig - - export RUMP_SERVER=${RUMPCLI} - atf_check -s exit:0 -o match:'0.->.1' \ - rump.sysctl -w net.inet6.ip6.accept_rtadv=1 - # Limit the maximum number of prefix entries to 2 - atf_check -s exit:0 -o match:'16.->.2' \ - rump.sysctl -w net.inet6.ip6.maxifprefixes=2 - unset RUMP_SERVER - - start_rtadvd $RUMPSRV $PIDFILE - start_rtadvd $RUMPSRV3 $PIDFILE3 - sleep $WAITTIME - - check_entries $RUMPCLI $RUMPSRV $IP6SRV_PREFIX - check_entries $RUMPCLI $RUMPSRV3 $IP6SRV3_PREFIX - - start_rtadvd $RUMPSRV4 $PIDFILE4 - sleep $WAITTIME - - export RUMP_SERVER=${RUMPCLI} - $DEBUG && dump_entries - # There should remain two prefixes - n=$(rump.ndp -p |grep 'advertised by' |wc -l) - atf_check_equal $n 2 - # TODO check other conditions - unset RUMP_SERVER - - atf_check -s exit:0 kill -TERM `cat ${PIDFILE}` - wait_term ${PIDFILE} - atf_check -s exit:0 kill -TERM `cat ${PIDFILE3}` - wait_term ${PIDFILE3} - atf_check -s exit:0 kill -TERM `cat ${PIDFILE4}` - wait_term ${PIDFILE4} - - rump_server_destroy_ifaces -} - -ra_multiple_routers_maxifprefixes_cleanup() -{ - - if [ -f ${PIDFILE} ]; then - kill -TERM `cat ${PIDFILE}` - wait_term ${PIDFILE} - fi - if [ -f ${PIDFILE3} ]; then - kill -TERM `cat ${PIDFILE3}` - wait_term ${PIDFILE3} - fi - if [ -f ${PIDFILE4} ]; then - kill -TERM `cat ${PIDFILE4}` - wait_term ${PIDFILE4} - fi - - $DEBUG && dump - cleanup -} - -atf_test_case ra_temporary_address cleanup -ra_temporary_address_head() -{ - - atf_set "descr" "Tests for IPv6 temporary address" - atf_set "require.progs" "rump_server rump.rtadvd rump.ndp rump.ifconfig" -} - -check_echo_request_pkt() -{ - local pkt="$2 > $3: .+ echo request" - - extract_new_packets $1 > ./out - $DEBUG && echo $pkt - $DEBUG && cat ./out - atf_check -s exit:0 -o match:"$pkt" cat ./out -} - -ra_temporary_address_body() -{ - local ip_auto= ip_temp= - - rump_server_fs_start $RUMPSRV netinet6 - rump_server_start $RUMPCLI netinet6 - - setup_shmif0 $RUMPSRV $IP6SRV - init_server $RUMPSRV - setup_shmif0 $RUMPCLI $IP6CLI - - export RUMP_SERVER=$RUMPCLI - atf_check -s exit:0 -o match:'0.->.1' \ - rump.sysctl -w net.inet6.ip6.accept_rtadv=1 - atf_check -s exit:0 -o match:'0.->.1' \ - rump.sysctl -w net.inet6.ip6.use_tempaddr=1 - unset RUMP_SERVER - - create_rtadvdconfig - start_rtadvd $RUMPSRV $PIDFILE - sleep $WAITTIME - - check_entries $RUMPCLI $RUMPSRV $IP6SRV_PREFIX - - export RUMP_SERVER=$RUMPCLI - - # Check temporary address - atf_check -s exit:0 \ - -o match:"$IP6SRV_PREFIX.+<(TENTATIVE,)?AUTOCONF,TEMPORARY>" \ - rump.ifconfig shmif0 inet6 - - # - # Testing net.inet6.ip6.prefer_tempaddr - # - atf_check -s exit:0 rump.ifconfig -w 10 - $DEBUG && rump.ifconfig shmif0 - ip_auto=$(rump.ifconfig shmif0 |awk '// {sub(/\/[0-9]*/, ""); print $2;}') - ip_temp=$(rump.ifconfig shmif0 |awk '// {sub(/\/[0-9]*/, ""); print $2;}') - $DEBUG && echo $ip_auto $ip_temp - - # Ignore old packets - extract_new_packets bus1 > /dev/null - - atf_check -s exit:0 -o ignore rump.ping6 -n -X 2 -c 1 $IP6SRV - # autoconf (non-temporal) address should be used as the source address - check_echo_request_pkt bus1 $ip_auto $IP6SRV - - # Enable net.inet6.ip6.prefer_tempaddr - atf_check -s exit:0 -o match:'0.->.1' \ - rump.sysctl -w net.inet6.ip6.prefer_tempaddr=1 - - atf_check -s exit:0 -o ignore rump.ping6 -n -X 2 -c 1 $IP6SRV - # autoconf, temporal address should be used as the source address - check_echo_request_pkt bus1 $ip_temp $IP6SRV - - unset RUMP_SERVER - - atf_check -s exit:0 kill -TERM `cat ${PIDFILE}` - wait_term $PIDFILE - - rump_server_destroy_ifaces -} - -ra_temporary_address_cleanup() -{ - - if [ -f ${PIDFILE} ]; then - kill -TERM `cat ${PIDFILE}` - wait_term ${PIDFILE} - fi - - $DEBUG && dump - cleanup -} - -atf_init_test_cases() -{ - - atf_add_test_case ra_basic - atf_add_test_case ra_flush_prefix_entries - atf_add_test_case ra_flush_defrouter_entries - atf_add_test_case ra_delete_address - atf_add_test_case ra_multiple_routers - atf_add_test_case ra_multiple_routers_single_prefix - atf_add_test_case ra_multiple_routers_maxifprefixes - atf_add_test_case ra_temporary_address -} diff --git a/net/net/Makefile b/net/net/Makefile --- a/net/net/Makefile +++ b/net/net/Makefile @@ -1,25 +1,33 @@ -# $NetBSD: Makefile,v 1.19 2016/11/24 08:52:20 ozaki-r Exp $ +# $NetBSD: Makefile,v 1.25 2020/09/08 14:13:50 christos Exp $ # .include TESTSDIR= ${TESTSBASE}/net/net -TESTS_C= t_unix +TESTS_C= t_bind +TESTS_C+= t_unix +TESTS_C+= t_mapped TESTS_C+= t_tcp TESTS_C+= t_udp TESTS_C+= t_pktinfo .if (${MKRUMP} != "no") && !defined(BSD_MK_COMPAT_FILE) +TESTS_C+= t_pktinfo_send TESTS_C+= t_raw .for name in forwarding ipaddress ipv6address ipv6_lifetime mtudisc mtudisc6 \ - ping6_opts + ping_opts ping6_opts TESTS_SH+= t_${name} TESTS_SH_SRC_t_${name}= ../net_common.sh t_${name}.sh .endfor .endif -LDADD.t_raw+= -lrumpnet_local -lrumpnet_netinet -lrumpnet_net -lrumpdev -LDADD.t_raw+= -lrumpnet -lrumpvfs -lrump -lrumpuser -lrump -lpthread +LDADD.t_pktinfo_send+= -lrumpnet_local -lrumpnet_netinet -lrumpnet_net +LDADD.t_pktinfo_send+= -lrumpnet_shmif -lrumpnet +LDADD.t_pktinfo_send+= ${LIBRUMPBASE} +LDADD.t_raw+= -lrumpnet_local -lrumpnet_netinet -lrumpnet_net +LDADD.t_raw+= -lrumpnet ${LIBRUMPBASE} + +LDADD.t_mapped+= -lutil .include diff --git a/net/net/t_bind.c b/net/net/t_bind.c new file mode 100644 --- /dev/null +++ b/net/net/t_bind.c @@ -0,0 +1,180 @@ +/* $NetBSD: t_bind.c,v 1.1 2020/09/08 14:13:50 christos Exp $ */ + +/*- + * Copyright (c) 2020 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +#define _GNU_SOURCE + +#include +#ifdef __RCSID +__RCSID("$Id: t_bind.c,v 1.1 2020/09/08 14:13:50 christos Exp $"); +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "util.h" + +#include "test.h" + +static int +mksocket(int sfam) +{ + int o, p; + switch (sfam) { + case AF_INET: + o = IP_BINDANY; + p = IPPROTO_IP; + break; + case AF_INET6: + o = IPV6_BINDANY; + p = IPPROTO_IPV6; + break; + default: + FAIL("family"); + } + int fd = socket(sfam, SOCK_STREAM, 0); + if (fd == -1) + FAIL("socket"); + int f = 1; + if (setsockopt(fd, p, o, &f, sizeof(f)) == -1) { + close(fd); + FAIL("setsockopt"); + } + return fd; +fail: + return -1; +} + +static socklen_t +mkserver(int sfam, struct sockaddr_storage *ss) +{ + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + + memset(ss, 0, sizeof(*ss)); + ss->ss_family = sfam; + switch (sfam) { + case AF_INET: + sin = (struct sockaddr_in *)ss; + sin->sin_port = htons(12345); +#ifdef BSD4_4 + sin->sin_len = sizeof(*sin); +#endif + inet_pton(sfam, "1.1.1.1", &sin->sin_addr); + return sizeof(*sin); + case AF_INET6: + sin6 = (struct sockaddr_in6 *)ss; + sin6->sin6_port = htons(12345); +#ifdef BSD4_4 + sin6->sin6_len = sizeof(*sin6); +#endif + inet_pton(sfam, "2010:2020:2030:2040:2050:2060:2070:2080", + &sin6->sin6_addr); + return sizeof(*sin6); + default: + FAIL("bad family"); + } +fail: + return -1; +} + +static int +test(int sfam) +{ + int sfd = -1; + struct sockaddr_storage saddr; + socklen_t slen; + + sfd = mksocket(sfam); + slen = mkserver(sfam, &saddr); + + if (bind(sfd, (struct sockaddr *)&saddr, slen) == -1) { + FAIL("bind"); + } + return 0; +fail: + return -1; +} + +#ifndef TEST + +ATF_TC(bindany_4); +ATF_TC_HEAD(bindany_4, tc) +{ + + atf_tc_set_md_var(tc, "descr", "Check AF_INET bindany"); +} + +ATF_TC_BODY(bindany_4, tc) +{ + test(AF_INET); +} + +ATF_TC(bindany_6); +ATF_TC_HEAD(bindany_6, tc) +{ + + atf_tc_set_md_var(tc, "descr", "Check AF_INET6 bindany"); +} + +ATF_TC_BODY(bindany_6, tc) +{ + test(AF_INET6); +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, bindany_4); + ATF_TP_ADD_TC(tp, bindany_6); + return atf_no_error(); +} +#else +int +main(int argc, char *argv[]) +{ + test(AF_INET); + test(AF_INET6); +} +#endif diff --git a/net/net/t_forwarding.sh b/net/net/t_forwarding.sh old mode 100755 new mode 100644 --- a/net/net/t_forwarding.sh +++ b/net/net/t_forwarding.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_forwarding.sh,v 1.19 2016/11/25 08:51:17 ozaki-r Exp $ +# $NetBSD: t_forwarding.sh,v 1.20 2017/02/20 09:58:58 ozaki-r Exp $ # # Copyright (c) 2015 The NetBSD Foundation, Inc. # All rights reserved. @@ -47,6 +47,7 @@ atf_test_case ipforwarding_fastforward_v4 cleanup atf_test_case ipforwarding_fastforward_v6 cleanup atf_test_case ipforwarding_misc cleanup +atf_test_case ipforwarding_fragment_v4 cleanup ipforwarding_v4_head() { @@ -78,6 +79,12 @@ atf_set "require.progs" "rump_server" } +ipforwarding_fragment_v4_head() +{ + atf_set "descr" "Tests for fragmented packet forwarding (IPv4)" + atf_set "require.progs" "rump_server" +} + setup_endpoint() { sock=${1} @@ -150,6 +157,18 @@ fi } +prepare_file() +{ + local file=$1 + local data="0123456789" + + touch $file + for i in `seq 1 512` + do + echo $data >> $file + done +} + setup() { rump_server_start $SOCKSRC @@ -398,6 +417,22 @@ $DEBUG && rump.ifconfig -v shmif0 } +setup_mtu() +{ + local mtu=$1 + + export RUMP_SERVER=$SOCKFWD + atf_check -s exit:0 rump.ifconfig shmif0 mtu $mtu +} + +disable_mtudisc() +{ + local mtu=$1 + + export RUMP_SERVER=$SOCKDST + atf_check -s exit:0 -o ignore rump.sysctl -w -q net.inet.ip.mtudisc=0 +} + ipforwarding_v4_body() { setup @@ -487,6 +522,37 @@ return 0 } +ipforwarding_fragment_v4_body() +{ + setup + test_setup + + setup_forwarding + test_setup_forwarding + + prepare_file $HTML_FILE + start_httpd $SOCKDST $IP4DST + $DEBUG && rump.netstat -a + setup_mtu 1000 + disable_mtudisc + + extract_new_packets bus1 > ./out + extract_new_packets bus2 > ./out + + test_http_get $IP4DST + + # Packets of MTU size sent from the server + extract_new_packets bus2 > ./out + atf_check -s exit:0 -o match:'length 1514' cat ./out + + # The packets are fragmented down to 1000 + extract_new_packets bus1 > ./out + atf_check -s exit:0 -o match:'length 1010' cat ./out + atf_check -s exit:0 -o match:'length 538' cat ./out + + teardown_interfaces +} + ipforwarding_v4_cleanup() { $DEBUG && dump @@ -520,6 +586,13 @@ cleanup } +ipforwarding_fragment_v4_cleanup() +{ + $DEBUG && dump + stop_httpd + cleanup +} + atf_init_test_cases() { atf_add_test_case ipforwarding_v4 @@ -527,4 +600,5 @@ atf_add_test_case ipforwarding_fastforward_v4 atf_add_test_case ipforwarding_fastforward_v6 atf_add_test_case ipforwarding_misc + atf_add_test_case ipforwarding_fragment_v4 } diff --git a/net/net/t_ipaddress.sh b/net/net/t_ipaddress.sh old mode 100755 new mode 100644 --- a/net/net/t_ipaddress.sh +++ b/net/net/t_ipaddress.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_ipaddress.sh,v 1.9 2016/12/15 02:43:56 ozaki-r Exp $ +# $NetBSD: t_ipaddress.sh,v 1.11 2017/08/03 03:16:27 ozaki-r Exp $ # # Copyright (c) 2015 Internet Initiative Japan Inc. # All rights reserved. @@ -170,24 +170,79 @@ local desc="$2" atf_test_case "ipaddr_${name}" cleanup - eval "ipaddr_${name}_head() { \ - atf_set \"descr\" \"${desc}\"; \ - atf_set \"require.progs\" \"rump_server\"; \ - }; \ - ipaddr_${name}_body() { \ - test_${name}; \ - }; \ - ipaddr_${name}_cleanup() { \ - $DEBUG && dump; \ - cleanup; \ + eval "ipaddr_${name}_head() { + atf_set descr \"${desc}\" + atf_set require.progs rump_server + } + ipaddr_${name}_body() { + test_${name} + } + ipaddr_${name}_cleanup() { + \$DEBUG && dump + cleanup }" atf_add_test_case "ipaddr_${name}" } +test_alias_address() +{ + local ip=10.0.0.1 + local net=10.0.0/24 + local ip_a1=10.0.0.2 + + rump_server_start $SOCK_LOCAL + rump_server_add_iface $SOCK_LOCAL shmif0 $BUS + + export RUMP_SERVER=$SOCK_LOCAL + + # Assign a primary address and an alias address + atf_check -s exit:0 rump.ifconfig shmif0 $ip/24 + atf_check -s exit:0 rump.ifconfig shmif0 $ip_a1/24 alias + atf_check -s exit:0 rump.ifconfig shmif0 up + atf_check -s exit:0 rump.ifconfig -w 10 + + $DEBUG && rump.ifconfig shmif0 + $DEBUG && rump.netstat -nr -f inet + + atf_check -s exit:0 -o match:"inet $ip" rump.ifconfig shmif0 + atf_check -s exit:0 -o match:"inet $ip_a1" rump.ifconfig shmif0 + check_route $ip 'link#2' UHl lo0 + check_route $ip_a1 'link#2' UHl lo0 + check_route $net 'link#2' UC shmif0 + + # Delete the primary address + atf_check -s exit:0 rump.ifconfig shmif0 $ip delete + + $DEBUG && rump.ifconfig shmif0 + $DEBUG && rump.netstat -nr -f inet + + atf_check -s exit:0 -o not-match:"inet $ip" rump.ifconfig shmif0 + atf_check -s exit:0 -o match:"inet $ip_a1" rump.ifconfig shmif0 + check_route_no_entry $ip + check_route $ip_a1 'link#2' UHl lo0 + check_route $net 'link#2' UC shmif0 + + # The alias address is now primary, so assigning a new address + # without 'alias' will overwrite the alias address + atf_check -s exit:0 rump.ifconfig shmif0 $ip/24 + + $DEBUG && rump.ifconfig shmif0 + $DEBUG && rump.netstat -nr -f inet + + atf_check -s exit:0 -o match:"inet $ip" rump.ifconfig shmif0 + atf_check -s exit:0 -o not-match:"inet $ip_a1" rump.ifconfig shmif0 + check_route $ip 'link#2' UHl lo0 + check_route_no_entry $ip_a1 + check_route $net 'link#2' UC shmif0 + + rump_server_destroy_ifaces +} + atf_init_test_cases() { add_test same_address "Assigning/deleting an IP address twice" add_test same_address6 "Assigning/deleting an IPv6 address twice" add_test auto_linklocal "Assigning an IPv6 link-local address automatically" + add_test alias_address "Tests of behaviors of alias addresses" } diff --git a/net/net/t_ipv6_lifetime.sh b/net/net/t_ipv6_lifetime.sh old mode 100755 new mode 100644 diff --git a/net/net/t_ipv6address.sh b/net/net/t_ipv6address.sh old mode 100755 new mode 100644 --- a/net/net/t_ipv6address.sh +++ b/net/net/t_ipv6address.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_ipv6address.sh,v 1.12 2016/12/14 02:50:42 ozaki-r Exp $ +# $NetBSD: t_ipv6address.sh,v 1.16 2019/08/26 07:41:50 ozaki-r Exp $ # # Copyright (c) 2015 Internet Initiative Japan Inc. # All rights reserved. @@ -25,7 +25,7 @@ # POSSIBILITY OF SUCH DAMAGE. SERVER="rump_server -lrumpnet -lrumpnet_net -lrumpnet_netinet" -SERVER="${SERVER} -lrumpnet_shmif -lrumpdev" +SERVER="${SERVER} -lrumpnet_shmif" SERVER6="${SERVER} -lrumpnet_netinet6" SOCKSRC=unix://commsock1 @@ -41,7 +41,7 @@ BUSSRC=bus_src BUSDST=bus_dst -DEBUG=${DEBUG:-true} +DEBUG=${DEBUG:-false} TIMEOUT=3 atf_test_case linklocal cleanup @@ -247,6 +247,7 @@ local dst_if0_lladdr=`get_linklocal_addr ${SOCKDST} shmif0` local fwd_if0_lladdr=`get_linklocal_addr ${SOCKFWD} shmif0` local fwd_if1_lladdr=`get_linklocal_addr ${SOCKFWD} shmif1` + local lladdr=fe80::2 export RUMP_SERVER=${SOCKSRC} $DEBUG && rump.ifconfig @@ -325,6 +326,19 @@ atf_check -s ignore -o not-empty -e ignore \ -x "shmif_dumpbus -p - ${BUS2} | tcpdump -r - -n -p icmp6" + # Setting a link-local address with a scope ID + # XXX need $HIJACKING for some reasons + cleanup_bus + export RUMP_SERVER=${SOCKFWD} + $DEBUG && rump.ifconfig shmif0 + atf_check -s exit:0 $HIJACKING rump.ifconfig shmif0 inet6 $lladdr%shmif0/64 + export RUMP_SERVER=${SOCKSRC} + atf_check -s exit:0 -o match:"0.0% packet loss" rump.ping6 -c 1 \ + -X $TIMEOUT -n $lladdr + export RUMP_SERVER=${SOCKDST} + atf_check -s not-exit:0 -o match:"100.0% packet loss" rump.ping6 -c 1 \ + -X $TIMEOUT -n $lladdr + unset RUMP_SERVER } @@ -364,7 +378,7 @@ rump.route get -inet6 ${src_if0_lladdr} # ndp - atf_check -s exit:0 -o match:"${src_if0_lladdr}" \ + atf_check -s not-exit:0 -o ignore -e match:'no entry' \ rump.ndp -n ${src_if0_lladdr}%shmif0 # ndp without an interface name (zone index) diff --git a/net/net/t_mapped.c b/net/net/t_mapped.c new file mode 100644 --- /dev/null +++ b/net/net/t_mapped.c @@ -0,0 +1,320 @@ +/* $NetBSD: t_mapped.c,v 1.1 2020/07/06 18:45:25 christos Exp $ */ + +/*- + * Copyright (c) 2020 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +#define _GNU_SOURCE + +#include +#ifdef __RCSID +__RCSID("$Id: t_mapped.c,v 1.1 2020/07/06 18:45:25 christos Exp $"); +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "util.h" + +#include "test.h" + +static char mymsg[] = "hi mom!"; + +static void +print(const char *msg, const struct sockaddr *addr) +{ + char buf[1024]; + + sockaddr_snprintf(buf, sizeof(buf), "%a:%p", addr); + printf("%s: %s\n", msg, buf); +} + +static int +mksocket(int sfam) +{ + int fd = socket(sfam, SOCK_STREAM, 0); + if (fd == -1) + FAIL("socket"); + if (sfam != AF_INET6) + return fd; + int f = 0; +#if 0 + /* crashes the kernel, should not be allowed kernel only? */ + if (setsockopt(fd, IPPROTO_IPV6, 24 /* IPV6_2292RTHDR */, + &f, sizeof(f)) == -1) + FAIL("setsockopt"); +#endif + if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &f, sizeof(f)) == -1) + FAIL("setsockopt"); + return fd; +fail: + return -1; +} + +static socklen_t +mkserver(int sfam, struct sockaddr_storage *ss) +{ + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + + memset(ss, 0, sizeof(*ss)); + ss->ss_family = sfam; + switch (sfam) { + case AF_INET: + sin = (struct sockaddr_in *)ss; + sin->sin_port = htons(12345); +#ifdef BSD4_4 + sin->sin_len = sizeof(*sin); +#endif + return sizeof(*sin); + case AF_INET6: + sin6 = (struct sockaddr_in6 *)ss; + sin6->sin6_port = htons(12345); +#ifdef BSD4_4 + sin6->sin6_len = sizeof(*sin6); +#endif + return sizeof(*sin6); + default: + FAIL("bad family"); + } +fail: + return -1; +} + +#if 0 +static const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT; +#endif +static socklen_t +mkclient(int sfam, struct sockaddr_storage *ss) +{ + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + + memset(ss, 0, sizeof(*ss)); + ss->ss_family = sfam; + switch (sfam) { + case AF_INET: + sin = (struct sockaddr_in *)ss; + sin->sin_port = htons(12345); +#ifdef BSD4_4 + sin->sin_len = sizeof(*sin); +#endif + sin->sin_addr.s_addr = htonl(INADDR_LOOPBACK); + return sizeof(*sin); + case AF_INET6: + sin6 = (struct sockaddr_in6 *)ss; + sin6->sin6_port = htons(12345); +#ifdef BSD4_4 + sin6->sin6_len = sizeof(*sin6); +#endif +#if 1 + sin6->sin6_addr = in6addr_loopback; +#else + struct in6_addr *a = &sin6->sin6_addr; + a->__u6_addr.__u6_addr32[0] == 0; + a->__u6_addr.__u6_addr32[1] == 0; + a->__u6_addr.__u6_addr32[2] == ntohl(0x0000ffff); + a->__u6_addr.__u6_addr32[3] == ntohl(INADDR_LOOPBACK); +#endif + return sizeof(*sin6); + default: + FAIL("bad family"); + } +fail: + return -1; +} + +static int +test(int forkit, int sfam, int cfam) +{ + int sfd = -1, cfd = -1, afd = -1; + pid_t sfdpid, cfdpid; + struct sockaddr_storage saddr, caddr, paddr; + socklen_t slen, clen, plen; + + sfdpid = cfdpid = getpid(); + + sfd = mksocket(sfam); + slen = mkserver(sfam, &saddr); + + if (bind(sfd, (struct sockaddr *)&saddr, slen) == -1) { + FAIL("bind"); + } + + if (listen(sfd, SOMAXCONN) == -1) + FAIL("listen"); + + if (forkit) { + switch (cfdpid = fork()) { + case 0: /* child */ + sfdpid = getppid(); + cfdpid = getpid(); + break; + case -1: + FAIL("fork"); + default: + break; + } + } + + if (cfdpid == getpid()) { + cfd = mksocket(cfam); + clen = mkclient(cfam, &caddr); + + if (connect(cfd, (const struct sockaddr *)&caddr, clen) == -1) + FAIL("connect"); + } + + if (sfdpid == getpid()) { + plen = sizeof(paddr); + afd = accept(sfd, (struct sockaddr *)&paddr, &plen); + if (afd == -1) + FAIL("accept"); + + print("peer", (const struct sockaddr *)&paddr); + } + + if (cfdpid == getpid()) { + if (write(cfd, mymsg, sizeof(mymsg)) != sizeof(mymsg)) + FAIL("write"); + (void)close(cfd); + } + + if (sfdpid == getpid()) { + char buf[1024]; + if (read(afd, buf, sizeof(mymsg)) != sizeof(mymsg)) + FAIL("write"); + + if (strcmp(buf, mymsg) != 0) + FAIL("compare"); + + (void)close(afd); + (void)close(sfd); + if (forkit && waitpid(cfdpid, NULL, 0) == -1) + FAIL("waitpid"); + } + (void)close(cfd); + + return 0; +fail: + if (sfdpid == getpid()) { + (void)close(afd); + (void)close(sfd); + } + if (cfdpid == getpid()) { + (void)close(cfd); + } + return -1; +} + +#ifndef TEST + +ATF_TC(mapped_4_4); +ATF_TC_HEAD(mapped_4_4, tc) +{ + + atf_tc_set_md_var(tc, "descr", "Check AF_INET <- AF_INET connections"); +} + +ATF_TC_BODY(mapped_4_4, tc) +{ + test(0, AF_INET, AF_INET); +} + +ATF_TC(mapped_6_4); +ATF_TC_HEAD(mapped_6_4, tc) +{ + + atf_tc_set_md_var(tc, "descr", "Check AF_INET6 <- AF_INET connections"); +} + +ATF_TC_BODY(mapped_6_4, tc) +{ + test(0, AF_INET6, AF_INET); +} + +#if 0 +ATF_TC(mapped_4_6); +ATF_TC_HEAD(mapped_4_6, tc) +{ + + atf_tc_set_md_var(tc, "descr", "Check AF_INET <- AF_INET6 connections"); +} + +ATF_TC_BODY(mapped_4_6, tc) +{ + test(0, AF_INET, AF_INET6); +} +#endif + +ATF_TC(mapped_6_6); +ATF_TC_HEAD(mapped_6_6, tc) +{ + + atf_tc_set_md_var(tc, "descr", "Check AF_INET6 <- AF_INET6 connections"); +} + +ATF_TC_BODY(mapped_6_6, tc) +{ + test(0, AF_INET6, AF_INET6); +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, mapped_4_4); +#if 0 + ATF_TP_ADD_TC(tp, mapped_4_6); +#endif + ATF_TP_ADD_TC(tp, mapped_6_4); + ATF_TP_ADD_TC(tp, mapped_6_6); + return atf_no_error(); +} +#else +int +main(int argc, char *argv[]) +{ + test(0, AF_INET, AF_INET); +// test(0, AF_INET, AF_INET6); + test(0, AF_INET6, AF_INET); + test(0, AF_INET6, AF_INET6); +} +#endif diff --git a/net/net/t_mtudisc.sh b/net/net/t_mtudisc.sh old mode 100755 new mode 100644 --- a/net/net/t_mtudisc.sh +++ b/net/net/t_mtudisc.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_mtudisc.sh,v 1.8 2016/12/21 01:16:18 ozaki-r Exp $ +# $NetBSD: t_mtudisc.sh,v 1.10 2017/03/06 07:33:27 ozaki-r Exp $ # # Copyright (c) 2016 Internet Initiative Japan Inc. # All rights reserved. @@ -28,7 +28,6 @@ SOCKLOCAL=unix://commsock1 SOCKGATEWAY=unix://commsock2 SOCKREMOTE=unix://commsock3 -HTML_FILE=index.html DEBUG=${DEBUG:-false} @@ -37,7 +36,7 @@ mtudisc_basic_head() { atf_set "descr" "Tests for IPv4 Path MTU Dicorvery basic behavior" - atf_set "require.progs" "rump_server" + atf_set "require.progs" "rump_server nc" } setup_server() @@ -57,7 +56,7 @@ $DEBUG && rump.ifconfig $if } -prepare_download_file() +prepare_file() { local file=$1 local data="0123456789" @@ -69,17 +68,6 @@ done } -do_http_get() -{ - local ip=$1 - local ret=$2 - local timeout=5 - - # get the webpage - atf_check -s exit:$ret env LD_PRELOAD=/usr/lib/librumphijack.so \ - ftp -q $timeout -o ./out http://$ip/$HTML_FILE -} - mtudisc_basic_body() { local pkt= @@ -88,6 +76,10 @@ local gateway_remote_ip=10.0.1.1 local remote_ip=10.0.1.2 local prefixlen=24 + local port=1234 + local pid= + local file_send=./file.send + local file_recv=./file.recv rump_server_start $SOCKLOCAL rump_server_start $SOCKGATEWAY @@ -96,9 +88,9 @@ # # Setup servers # - # [local server] [gateway server] [remote server with httpd] + # [local server] [gateway server] [remote server] # | 10.0.0.2 10.0.0.1 | | 10.0.1.1 10.0.1.2 | - # shmif0(mtu=1500) ----- shmif1(mtu=1280) shmif0(mtu=1500) ----- shmif0(mtu=1500) + # shmif0(mtu=1500) ----- shmif0(mtu=1500) shmif1(mtu=1280) ----- shmif0(mtu=1500) # # Assign IP addresses @@ -111,8 +103,7 @@ export RUMP_SERVER=$SOCKGATEWAY # Set mtu of shmif0 to 1280 - export RUMP_SERVER=$SOCKGATEWAY - atf_check -s exit:0 rump.ifconfig shmif0 mtu 1280 + atf_check -s exit:0 rump.ifconfig shmif1 mtu 1280 # Enable IPv4 forwarding atf_check -s exit:0 rump.sysctl -w -q net.inet.ip.forwarding=1 @@ -123,70 +114,103 @@ # Check default value atf_check -s exit:0 -o match:"1" rump.sysctl -n net.inet.ip.mtudisc - # Start httpd daemon - prepare_download_file $HTML_FILE - start_httpd $SOCKREMOTE $remote_ip - $DEBUG && rump.netstat -a -f inet - - # Teach the peer thar 10.0.0.2(local serer) is behind 10.0.1.1(gateway server) + # Teach the peer thar 10.0.0.2(local server) is behind 10.0.1.1(gateway server) atf_check -s exit:0 -o ignore rump.route add $local_ip/32 $gateway_remote_ip + # Don't accept fragmented packets + atf_check -s exit:0 -o ignore rump.sysctl -w -q net.inet.ip.maxfragpackets=0 + ### Setup local server export RUMP_SERVER=$SOCKLOCAL - # Teach the peer thar 10.0.1.2(remote serer) is behind 10.0.0.1(gateway server) + # Teach the peer thar 10.0.1.2(remote server) is behind 10.0.0.1(gateway server) atf_check -s exit:0 -o ignore rump.route add $remote_ip/32 $gateway_local_ip - # Don't accept fragmented packets - atf_check -s exit:0 -o ignore rump.sysctl -w -q net.inet.ip.maxfragpackets=0 - # # Test disabled path mtu discorvery # - export RUMP_SERVER=$SOCKREMOTE - atf_check -s exit:0 -o ignore rump.sysctl -w -q net.inet.ip.mtudisc=0 + prepare_file $file_send + + # Start nc server + start_nc_server $SOCKREMOTE $port $file_recv - # Get the webpage (expect: failed) export RUMP_SERVER=$SOCKLOCAL - do_http_get $remote_ip 1 + atf_check -s exit:0 -o ignore rump.sysctl -w -q net.inet.ip.mtudisc=0 + + # Send a file to the server + atf_check -s exit:0 $HIJACKING nc -N -w 3 $remote_ip $port < $file_send $DEBUG && extract_new_packets bus2 > ./out $DEBUG && cat ./out + stop_nc_server + atf_check -s not-exit:0 -o match:"differ" diff -q $file_send $file_recv - # Check path mtu size on remote server - export RUMP_SERVER=$SOCKREMOTE + # Check path mtu size on the local server atf_check -s exit:0 \ - -o match:"^10.0.0.2 +10.0.1.1 +UGHS +- +- +- +shmif0" \ - rump.netstat -nr -f inet + -o match:"^10.0.1.2 +10.0.0.1 +UGHS +- +- +- +shmif0" \ + rump.netstat -nr -f inet # # Test enabled path mtu discorvery # - export RUMP_SERVER=$SOCKREMOTE - atf_check -s exit:0 -o ignore rump.sysctl -w -q net.inet.ip.mtudisc=1 + # Start nc server + start_nc_server $SOCKREMOTE $port $file_recv - # Get the webpage (expect: success) export RUMP_SERVER=$SOCKLOCAL - do_http_get $remote_ip 0 + atf_check -s exit:0 -o ignore rump.sysctl -w -q net.inet.ip.mtudisc=1 + + # Send a file to the server + atf_check -s exit:0 $HIJACKING nc -N -w 3 $remote_ip $port < $file_send $DEBUG && extract_new_packets bus2 > ./out $DEBUG && cat ./out + stop_nc_server + atf_check -s exit:0 diff -q $file_send $file_recv - # Check path mtu size on remote server - export RUMP_SERVER=$SOCKREMOTE + # Check path mtu size on the local server atf_check -s exit:0 \ - -o match:"^10.0.0.2 +10.0.1.1 +UGHS +- +- +1280 +shmif0" \ - rump.netstat -nr -f inet + -o match:"^10.0.1.2 +10.0.0.1 +UGHS +- +- +1280 +shmif0" \ + rump.netstat -nr -f inet rump_server_destroy_ifaces } mtudisc_basic_cleanup() { + + $DEBUG && dump + stop_nc_server + cleanup +} + +atf_test_case mtudisc_timeout cleanup +mtudisc_timeout_head() +{ + + atf_set "descr" "Tests for IPv4 Path MTU Dicorvery timeout behavior" + atf_set "require.progs" "rump_server nc" +} + +mtudisc_timeout_body() +{ + + rump_server_start $SOCKLOCAL + + export RUMP_SERVER=$SOCKLOCAL + atf_check -s exit:0 -o match:'600 -> 600' \ + rump.sysctl -w net.inet.ip.mtudisctimeout=600 + + # TODO more tests +} + +mtudisc_timeout_cleanup() +{ + $DEBUG && dump - stop_httpd cleanup } atf_init_test_cases() { + atf_add_test_case mtudisc_basic + atf_add_test_case mtudisc_timeout } diff --git a/net/net/t_mtudisc6.sh b/net/net/t_mtudisc6.sh old mode 100755 new mode 100644 diff --git a/net/net/t_ping6_opts.sh b/net/net/t_ping6_opts.sh old mode 100755 new mode 100644 --- a/net/net/t_ping6_opts.sh +++ b/net/net/t_ping6_opts.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_ping6_opts.sh,v 1.8 2016/11/25 08:51:17 ozaki-r Exp $ +# $NetBSD: t_ping6_opts.sh,v 1.9 2018/04/26 06:23:33 maxv Exp $ # # Copyright (c) 2016 Internet Initiative Japan Inc. # All rights reserved. @@ -292,89 +292,10 @@ cleanup } -atf_test_case ping6_opts_hops cleanup -ping6_opts_hops_head() -{ - - atf_set "descr" "tests of ping6 hops (Type 0 Routing Header)" - atf_set "require.progs" "rump_server" -} - -ping6_opts_hops_body() -{ - local my_macaddr= - local gw_shmif0_macaddr= - local gw_shmif2_macaddr= - - setup6 - setup_forwarding6 - - my_macaddr=$(get_macaddr ${SOCKSRC} shmif0) - gw_shmif0_macaddr=$(get_macaddr ${SOCKFWD} shmif0) - - export RUMP_SERVER=$SOCKSRC - atf_check -s exit:0 -o ignore rump.ping6 -n -c 1 -X $TIMEOUT $IP6DST - check_echo_request_pkt_with_macaddr \ - $my_macaddr $gw_shmif0_macaddr $IP6SRC $IP6DST - - rump_server_add_iface $SOCKFWD shmif2 $BUS_SRCGW - export RUMP_SERVER=$SOCKFWD - atf_check -s exit:0 rump.ifconfig shmif2 inet6 $IP6SRCGW2 - atf_check -s exit:0 rump.ifconfig -w 10 - gw_shmif2_macaddr=$(get_macaddr ${SOCKFWD} shmif2) - - export RUMP_SERVER=$SOCKSRC - atf_check -s exit:0 -o ignore rump.ping6 -n -c 1 -X $TIMEOUT $IP6DST - check_echo_request_pkt_with_macaddr \ - $my_macaddr $gw_shmif0_macaddr $IP6SRC $IP6DST - - # ping6 hops - - # ping6 fails expectedly because the kernel doesn't support - # to receive packets with type 0 routing headers, but we can - # check whether a sent packet is correct. - atf_check -s not-exit:0 -o ignore rump.ping6 -n -c 1 -X $TIMEOUT \ - $IP6SRCGW $IP6DST - check_echo_request_pkt_with_macaddr_and_rthdr0 \ - $my_macaddr $gw_shmif0_macaddr $IP6SRC $IP6SRCGW $IP6DST - - atf_check -s not-exit:0 -o ignore rump.ping6 -n -c 1 -X $TIMEOUT \ - $IP6SRCGW2 $IP6DST - check_echo_request_pkt_with_macaddr_and_rthdr0 \ - $my_macaddr $gw_shmif2_macaddr $IP6SRC $IP6SRCGW2 $IP6DST - - # ping6 -g hops - atf_check -s not-exit:0 -o ignore rump.ping6 -n -c 1 -X $TIMEOUT \ - -g $IP6SRCGW $IP6SRCGW $IP6DST - check_echo_request_pkt_with_macaddr_and_rthdr0 \ - $my_macaddr $gw_shmif0_macaddr $IP6SRC $IP6SRCGW $IP6DST - - atf_check -s not-exit:0 -o ignore rump.ping6 -n -c 1 -X $TIMEOUT \ - -g $IP6SRCGW2 $IP6SRCGW2 $IP6DST - check_echo_request_pkt_with_macaddr_and_rthdr0 \ - $my_macaddr $gw_shmif2_macaddr $IP6SRC $IP6SRCGW2 $IP6DST - - # ping6 -g hops, but different nexthops (is it valid?) - atf_check -s not-exit:0 -o ignore rump.ping6 -n -c 1 -X $TIMEOUT \ - -g $IP6SRCGW $IP6SRCGW2 $IP6DST - check_echo_request_pkt_with_macaddr_and_rthdr0 \ - $my_macaddr $gw_shmif0_macaddr $IP6SRC $IP6SRCGW2 $IP6DST - - rump_server_destroy_ifaces -} - -ping6_opts_hops_cleanup() -{ - - $DEBUG && dump - cleanup -} - atf_init_test_cases() { atf_add_test_case ping6_opts_sourceaddr atf_add_test_case ping6_opts_interface atf_add_test_case ping6_opts_gateway - atf_add_test_case ping6_opts_hops } diff --git a/net/net/t_ping_opts.sh b/net/net/t_ping_opts.sh new file mode 100644 --- /dev/null +++ b/net/net/t_ping_opts.sh @@ -0,0 +1,364 @@ +# $NetBSD: t_ping_opts.sh,v 1.3 2018/02/09 03:53:07 ozaki-r Exp $ +# +# Copyright (c) 2017 Internet Initiative Japan Inc. +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +SOCKSRC=unix://ping_opts1 +SOCKFWD=unix://ping_opts2 +SOCKDST=unix://ping_opts3 +IPSRC=10.0.1.2 +IPSRCGW=10.0.1.1 +IPDSTGW=10.0.2.1 +IPDST=10.0.2.2 +BUS_SRCGW=bus1 +BUS_DSTGW=bus2 + +IPSRC2=10.0.1.3 +IPSRCGW2=10.0.1.254 + +DEBUG=${DEBUG:-false} +TIMEOUT=1 +PING_OPTS="-n -c 1 -w $TIMEOUT" + +# +# Utility functions +# +setup_endpoint() +{ + local sock=${1} + local addr=${2} + local bus=${3} + local gw=${4} + + rump_server_add_iface $sock shmif0 $bus + + export RUMP_SERVER=${sock} + atf_check -s exit:0 rump.ifconfig shmif0 ${addr}/24 + atf_check -s exit:0 -o ignore rump.route add default ${gw} + atf_check -s exit:0 rump.ifconfig shmif0 up + atf_check -s exit:0 rump.ifconfig -w 10 + + if $DEBUG; then + rump.ifconfig shmif0 + rump.netstat -nr + fi +} + +setup_forwarder() +{ + + rump_server_add_iface $SOCKFWD shmif0 $BUS_SRCGW + rump_server_add_iface $SOCKFWD shmif1 $BUS_DSTGW + + export RUMP_SERVER=$SOCKFWD + + atf_check -s exit:0 rump.ifconfig shmif0 ${IPSRCGW}/24 + atf_check -s exit:0 rump.ifconfig shmif1 ${IPDSTGW}/24 + + atf_check -s exit:0 rump.ifconfig shmif0 up + atf_check -s exit:0 rump.ifconfig shmif1 up + atf_check -s exit:0 rump.ifconfig -w 10 + + if $DEBUG; then + rump.netstat -nr + rump.sysctl net.inet.ip.forwarding + fi +} + +setup_forwarding() +{ + + export RUMP_SERVER=$SOCKFWD + atf_check -s exit:0 -o ignore rump.sysctl -w net.inet.ip.forwarding=1 +} + +setup() +{ + + rump_server_start $SOCKSRC + rump_server_start $SOCKFWD + rump_server_start $SOCKDST + + setup_endpoint $SOCKSRC $IPSRC $BUS_SRCGW $IPSRCGW + setup_endpoint $SOCKDST $IPDST $BUS_DSTGW $IPDSTGW + setup_forwarder +} + +check_echo_request_pkt() +{ + local pkt="$1 > $2: .+ echo request" + + extract_new_packets $BUS_SRCGW > ./out + $DEBUG && echo $pkt + $DEBUG && cat ./out + atf_check -s exit:0 -o match:"$pkt" cat ./out +} + +check_echo_request_pkt_with_macaddr() +{ + local pkt="$1 > $2, .+ $3 > $4: .+ echo request" + + extract_new_packets $BUS_SRCGW > ./out + $DEBUG && echo $pkt + $DEBUG && cat ./out + atf_check -s exit:0 -o match:"$pkt" cat ./out +} + +check_echo_request_pkt_with_macaddr_and_rthdr0() +{ + local pkt= + + pkt="$1 > $2, .+ $3 > $4:" + pkt="$pkt srcrt \\(len=2, type=0, segleft=1, \\[0\\]$5\\)" + pkt="$pkt .+ echo request" + + extract_new_packets $BUS_SRCGW > ./out + $DEBUG && echo $pkt + $DEBUG && cat ./out + atf_check -s exit:0 -o match:"$pkt" cat ./out +} + +# +# Tests +# +atf_test_case ping_opts_sourceaddr cleanup +ping_opts_sourceaddr_head() +{ + + atf_set "descr" "tests of ping -I option" + atf_set "require.progs" "rump_server" +} + +ping_opts_sourceaddr_body() +{ + + setup + setup_forwarding + + export RUMP_SERVER=$SOCKSRC + atf_check -s exit:0 -o ignore rump.ping $PING_OPTS $IPDST + check_echo_request_pkt $IPSRC $IPDST + + atf_check -s exit:0 rump.ifconfig shmif0 $IPSRC2/24 alias + atf_check -s exit:0 rump.ifconfig -w 10 + + atf_check -s exit:0 -o ignore rump.ping $PING_OPTS $IPDST + check_echo_request_pkt $IPSRC $IPDST + + # ping -I + atf_check -s exit:0 -o ignore rump.ping $PING_OPTS \ + -I $IPSRC $IPDST + check_echo_request_pkt $IPSRC $IPDST + + atf_check -s exit:0 -o ignore rump.ping $PING_OPTS \ + -I $IPSRC2 $IPDST + check_echo_request_pkt $IPSRC2 $IPDST + + rump_server_destroy_ifaces +} + +ping_opts_sourceaddr_cleanup() +{ + + $DEBUG && dump + cleanup +} + +atf_test_case ping_opts_gateway cleanup +ping_opts_gateway_head() +{ + + atf_set "descr" "tests of ping -g option (IPOPT_LSRR)" + atf_set "require.progs" "rump_server" +} + +ping_opts_gateway_body() +{ + local my_macaddr= + local gw_shmif0_macaddr= + local gw_shmif2_macaddr= + + setup + setup_forwarding + + my_macaddr=$(get_macaddr ${SOCKSRC} shmif0) + gw_shmif0_macaddr=$(get_macaddr ${SOCKFWD} shmif0) + + export RUMP_SERVER=$SOCKSRC + atf_check -s exit:0 -o ignore rump.ping $PING_OPTS $IPDST + check_echo_request_pkt_with_macaddr \ + $my_macaddr $gw_shmif0_macaddr $IPSRC $IPDST + + rump_server_add_iface $SOCKFWD shmif2 $BUS_SRCGW + export RUMP_SERVER=$SOCKFWD + atf_check -s exit:0 rump.ifconfig shmif2 $IPSRCGW2/24 + atf_check -s exit:0 rump.ifconfig -w 10 + gw_shmif2_macaddr=$(get_macaddr ${SOCKFWD} shmif2) + + export RUMP_SERVER=$SOCKSRC + atf_check -s exit:0 -o ignore rump.ping $PING_OPTS $IPDST + check_echo_request_pkt_with_macaddr \ + $my_macaddr $gw_shmif0_macaddr $IPSRC $IPDST + + export RUMP_SERVER=$SOCKSRC + # ping -g + # By default source-routed packets are prohibited + atf_check -s not-exit:0 -o match:'Net prohibited access' \ + rump.ping $PING_OPTS -g $IPSRCGW $IPDST + + # Enable the options of source routing + export RUMP_SERVER=$SOCKSRC + atf_check -s exit:0 rump.sysctl -q -w net.inet.ip.allowsrcrt=1 + export RUMP_SERVER=$SOCKDST + atf_check -s exit:0 rump.sysctl -q -w net.inet.ip.allowsrcrt=1 + export RUMP_SERVER=$SOCKFWD + atf_check -s exit:0 rump.sysctl -q -w net.inet.ip.allowsrcrt=1 + atf_check -s exit:0 rump.sysctl -q -w net.inet.ip.forwsrcrt=1 + + export RUMP_SERVER=$SOCKSRC + # ping -g + atf_check -s exit:0 -o ignore rump.ping $PING_OPTS \ + -g $IPSRCGW $IPDST + # The destination address is the specified gateway and + # the final destination address is stored in the IP options + check_echo_request_pkt_with_macaddr \ + $my_macaddr $gw_shmif0_macaddr $IPSRC $IPSRCGW + + atf_check -s exit:0 -o ignore rump.ping $PING_OPTS \ + -g $IPSRCGW2 $IPDST + check_echo_request_pkt_with_macaddr \ + $my_macaddr $gw_shmif2_macaddr $IPSRC $IPSRCGW2 + + rump_server_destroy_ifaces +} + +ping_opts_gateway_cleanup() +{ + + $DEBUG && dump + cleanup +} + +atf_test_case ping_opts_recordroute cleanup +ping_opts_recordroute_head() +{ + + atf_set "descr" "tests of ping -R option (IPOPT_RR)" + atf_set "require.progs" "rump_server" +} + +check_recorded_routes() +{ + local outfile=$1 + local out=./__file1 cmp=./__file2 + + $DEBUG && cat $outfile + grep -A 3 'RR: ' $outfile> $out + cat < $cmp +RR: 10.0.2.1 + 10.0.2.2 + 10.0.1.1 + 10.0.1.2 +EOF + atf_check -s exit:0 diff $out $cmp + + rm -f $fixed $cmp +} + +ping_opts_recordroute_body() +{ + local my_macaddr= + local gw_shmif0_macaddr= + local gw_shmif2_macaddr= + local out=./file1 + + setup + setup_forwarding + + my_macaddr=$(get_macaddr ${SOCKSRC} shmif0) + gw_shmif0_macaddr=$(get_macaddr ${SOCKFWD} shmif0) + + export RUMP_SERVER=$SOCKSRC + atf_check -s exit:0 -o save:$out rump.ping $PING_OPTS -R $IPDST + check_echo_request_pkt_with_macaddr \ + $my_macaddr $gw_shmif0_macaddr $IPSRC $IPDST + check_recorded_routes $out + + rump_server_add_iface $SOCKFWD shmif2 $BUS_SRCGW + export RUMP_SERVER=$SOCKFWD + atf_check -s exit:0 rump.ifconfig shmif2 $IPSRCGW2/24 + atf_check -s exit:0 rump.ifconfig -w 10 + gw_shmif2_macaddr=$(get_macaddr ${SOCKFWD} shmif2) + + export RUMP_SERVER=$SOCKSRC + atf_check -s exit:0 -o save:$out rump.ping $PING_OPTS -R $IPDST + check_echo_request_pkt_with_macaddr \ + $my_macaddr $gw_shmif0_macaddr $IPSRC $IPDST + check_recorded_routes $out + + # Enable the options of source routing + export RUMP_SERVER=$SOCKSRC + atf_check -s exit:0 rump.sysctl -q -w net.inet.ip.allowsrcrt=1 + export RUMP_SERVER=$SOCKDST + atf_check -s exit:0 rump.sysctl -q -w net.inet.ip.allowsrcrt=1 + export RUMP_SERVER=$SOCKFWD + atf_check -s exit:0 rump.sysctl -q -w net.inet.ip.allowsrcrt=1 + atf_check -s exit:0 rump.sysctl -q -w net.inet.ip.forwsrcrt=1 + + export RUMP_SERVER=$SOCKSRC + # ping -R -g + atf_check -s exit:0 -o save:$out rump.ping $PING_OPTS \ + -R -g $IPSRCGW $IPDST + # The destination address is the specified gateway and + # the final destination address is stored in the IP options + check_echo_request_pkt_with_macaddr \ + $my_macaddr $gw_shmif0_macaddr $IPSRC $IPSRCGW + check_recorded_routes $out + + atf_check -s exit:0 -o save:$out rump.ping $PING_OPTS \ + -R -g $IPSRCGW2 $IPDST + check_echo_request_pkt_with_macaddr \ + $my_macaddr $gw_shmif2_macaddr $IPSRC $IPSRCGW2 + check_recorded_routes $out + + rm -f $out + + rump_server_destroy_ifaces +} + +ping_opts_recordroute_cleanup() +{ + + $DEBUG && dump + cleanup +} + +atf_init_test_cases() +{ + + atf_add_test_case ping_opts_sourceaddr + atf_add_test_case ping_opts_gateway + atf_add_test_case ping_opts_recordroute +} diff --git a/net/net/t_pktinfo_send.c b/net/net/t_pktinfo_send.c new file mode 100644 --- /dev/null +++ b/net/net/t_pktinfo_send.c @@ -0,0 +1,794 @@ +/* $NetBSD: t_pktinfo_send.c,v 1.3 2017/12/30 22:02:34 gson Exp $ */ + +/*- + * Copyright (c) 2017 Internet Initiative Japan Inc. + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +__RCSID("$NetBSD: t_pktinfo_send.c,v 1.3 2017/12/30 22:02:34 gson Exp $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "h_macros.h" +#include "../config/netconfig.c" + +#include + + +#define SERVERPORT 54321 +#define CLIENTPORT 12345 + + +char message[128] = "Hello IP_PKTINFO"; + +static void +setup_test_environment(void) +{ + RZ(rump_init()); + netcfg_rump_if("lo0", "127.0.0.2", "255.0.0.0"); + netcfg_rump_if("lo0", "127.0.0.3", "255.0.0.0"); + netcfg_rump_if("lo0", "127.0.0.4", "255.0.0.0"); + netcfg_rump_if("lo0", "127.0.0.5", "255.0.0.0"); +} + +static void +sock_in_init(struct sockaddr_in *sin, const char *addr, in_port_t port) +{ + memset(sin, 0, sizeof(struct sockaddr_in)); + + sin->sin_family = AF_INET; + sin->sin_port = htons(port); + inet_pton(AF_INET, addr, &sin->sin_addr); +} + +static int +sock_bind(int sock, const char *addr, in_port_t port) +{ + struct sockaddr_in bindaddr; + + sock_in_init(&bindaddr, addr, port); + return rump_sys_bind(sock, + (struct sockaddr *)&bindaddr, sizeof(bindaddr)); +} + +static int +udp_server(const char *addr, in_port_t port) +{ + int s, rv; + + RL(s = rump_sys_socket(AF_INET, SOCK_DGRAM, 0)); + RL(rv = sock_bind(s, addr, port)); + + return s; +} + +static int +addrcmp(struct sockaddr_in *sin, const char *addr) +{ + struct in_addr inaddr; + inet_pton(AF_INET, addr, &inaddr); + return memcmp(&inaddr, &sin->sin_addr, sizeof(inaddr)); +} + + +static ssize_t +sendto_pktinfo(int s, const void *buf, size_t len, int flags, + const char *src, const char *dst, in_port_t dstport) +{ + /* for sendmsg */ + struct sockaddr_in to; + struct msghdr msg; + char cmsgbuf[CMSG_SPACE(sizeof(struct in_pktinfo)) * 2]; + struct iovec vec; + + /* for store to cmsghdr */ + struct cmsghdr *cmsg; + + /* for pktinfo */ + struct in_pktinfo *pi; + struct in_addr addr; + + /* setup msghdr */ + sock_in_init(&to, dst, dstport); + + vec.iov_base = __UNCONST(buf); + vec.iov_len = len; + memset(&msg, 0, sizeof(msg)); + msg.msg_iov = &vec; + msg.msg_iovlen = 1; + msg.msg_name = (caddr_t)&to; + msg.msg_namelen = sizeof(to); + msg.msg_control = cmsgbuf; + msg.msg_controllen = 0; + cmsg = (struct cmsghdr *)cmsgbuf; + + /* setup ip_pktinfo */ + msg.msg_controllen += CMSG_SPACE(sizeof(struct in_pktinfo)); + cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); + cmsg->cmsg_level = IPPROTO_IP; + cmsg->cmsg_type = IP_PKTINFO; + + pi = (struct in_pktinfo *)CMSG_DATA(cmsg); + memset(pi, 0, sizeof(*pi)); + + /* treat 0.x.x.x/8 as interface index (like RFC1724 ss.3.3) */ + inet_pton(AF_INET, src, &addr); + if (ntohl(addr.s_addr) >> 24 == 0) + pi->ipi_ifindex = ntohl(addr.s_addr) & 0xffffff; + else { + pi->ipi_addr = addr; + } + + if (msg.msg_controllen == 0) + msg.msg_control = NULL; + + return rump_sys_sendmsg(s, &msg, flags); +} + +static void +try_sendmsg_pktinfo(int client, int server, const char *data, size_t datalen, + const char *src, const char *dst, in_port_t dstport) +{ + struct sockaddr_in from; + socklen_t fromlen; + int rv; + char buf[sizeof(message) * 2]; + + RL(rv = sendto_pktinfo(client, data, datalen, 0, + src, dst, dstport)); + + fromlen = sizeof(from); + RL(rv = rump_sys_recvfrom(server, buf, sizeof(buf), 0, + (struct sockaddr *)&from, &fromlen)); + + ATF_REQUIRE_MSG(addrcmp(&from, src) == 0, + "source address of received packet is %s, must be %s", + inet_ntoa(from.sin_addr), src); +} + +static void +do_send_pktinfo_tests(int client, int server, const char *data, size_t datalen) +{ + struct sockaddr_in sa_before, sa_after; + socklen_t sa_beforelen, sa_afterlen; + int rv; + char ipbuf1[sizeof("255.255.255.255")]; + char ipbuf2[sizeof("255.255.255.255")]; + + /* get sockaddr before sendmsg w/IP_PKTINFO */ + sa_beforelen = sizeof(sa_before); + RL(rv = rump_sys_getsockname(client, + (struct sockaddr *)&sa_before, &sa_beforelen)); + + /* + * sendmsg with IP_PKTINFO: 127.0.0.[2345] -> 127.0.0.1:54321, and + * check received packet is from 127.0.0.[2345] + */ + try_sendmsg_pktinfo(client, server, data, datalen, + "127.0.0.2", "127.0.0.1", SERVERPORT); + try_sendmsg_pktinfo(client, server, data, datalen, + "127.0.0.3", "127.0.0.1", SERVERPORT); + try_sendmsg_pktinfo(client, server, data, datalen, + "127.0.0.4", "127.0.0.1", SERVERPORT); + try_sendmsg_pktinfo(client, server, data, datalen, + "127.0.0.5", "127.0.0.1", SERVERPORT); + + /* get sockaddr after sendmsg w/IP_PKTINFO */ + sa_afterlen = sizeof(sa_after); + RL(rv = rump_sys_getsockname(client, + (struct sockaddr *)&sa_after, &sa_afterlen)); + + /* confirm sockaddr is not changed */ + inet_ntop(AF_INET, &sa_before.sin_addr, ipbuf1, sizeof(ipbuf1)); + inet_ntop(AF_INET, &sa_after.sin_addr, ipbuf2, sizeof(ipbuf2)); + ATF_REQUIRE_MSG(sa_before.sin_addr.s_addr == sa_after.sin_addr.s_addr, + "sockaddr is different from before send. before=%s, after=%s", + ipbuf1, ipbuf2); +} + +ATF_TC(pktinfo_send_unbound); +ATF_TC_HEAD(pktinfo_send_unbound, tc) +{ + atf_tc_set_md_var(tc, "descr", "sendmsg with IP_PKTINFO"); +} +ATF_TC_BODY(pktinfo_send_unbound, tc) +{ + int client, server; + + setup_test_environment(); + + server = udp_server("127.0.0.1", SERVERPORT); + RL(client = rump_sys_socket(AF_INET, SOCK_DGRAM, 0)); + + /* do sendmsg w/IP_PKTINFO tests */ + do_send_pktinfo_tests(client, server, message, strlen(message)); + + rump_sys_close(client); + rump_sys_close(server); +} + + +ATF_TC(pktinfo_send_bindany); +ATF_TC_HEAD(pktinfo_send_bindany, tc) +{ + atf_tc_set_md_var(tc, "descr", + "sendmsg with IP_PKTINFO on bind(INADDR_ANY) socket"); +} +ATF_TC_BODY(pktinfo_send_bindany, tc) +{ + int client, server, rv; + + setup_test_environment(); + + server = udp_server("127.0.0.1", SERVERPORT); + RL(client = rump_sys_socket(AF_INET, SOCK_DGRAM, 0)); + RL(rv = sock_bind(client, "0.0.0.0", 0)); + + /* do sendmsg w/IP_PKTINFO tests */ + do_send_pktinfo_tests(client, server, message, strlen(message)); + + rump_sys_close(client); + rump_sys_close(server); +} + + +ATF_TC(pktinfo_send_bindaddr); +ATF_TC_HEAD(pktinfo_send_bindaddr, tc) +{ + atf_tc_set_md_var(tc, "descr", + "sendmsg with IP_PKTINFO on bind(addr:0) socket"); +} +ATF_TC_BODY(pktinfo_send_bindaddr, tc) +{ + int client, server, rv; + + setup_test_environment(); + + server = udp_server("127.0.0.1", SERVERPORT); + RL(client = rump_sys_socket(AF_INET, SOCK_DGRAM, 0)); + RL(rv = sock_bind(client, "127.0.0.1", 0)); + + /* do sendmsg w/IP_PKTINFO tests */ + do_send_pktinfo_tests(client, server, message, strlen(message)); + + rump_sys_close(client); + rump_sys_close(server); +} + + +ATF_TC(pktinfo_send_bindport); +ATF_TC_HEAD(pktinfo_send_bindport, tc) +{ + atf_tc_set_md_var(tc, "descr", + "sendmsg with IP_PKTINFO on bind(INADDR_ANY:12345) socket"); +} +ATF_TC_BODY(pktinfo_send_bindport, tc) +{ + int client, server, rv; + + setup_test_environment(); + + server = udp_server("127.0.0.1", SERVERPORT); + RL(client = rump_sys_socket(AF_INET, SOCK_DGRAM, 0)); + RL(rv = sock_bind(client, "0.0.0.0", CLIENTPORT)); + + /* do sendmsg w/IP_PKTINFO tests */ + do_send_pktinfo_tests(client, server, message, strlen(message)); + + rump_sys_close(client); + rump_sys_close(server); +} + + +ATF_TC(pktinfo_send_bindaddrport); +ATF_TC_HEAD(pktinfo_send_bindaddrport, tc) +{ + atf_tc_set_md_var(tc, "descr", + "sendmsg with IP_PKTINFO on bind(addr:12345) socket"); +} +ATF_TC_BODY(pktinfo_send_bindaddrport, tc) +{ + int client, server, rv; + + setup_test_environment(); + + server = udp_server("127.0.0.1", SERVERPORT); + RL(client = rump_sys_socket(AF_INET, SOCK_DGRAM, 0)); + RL(rv = sock_bind(client, "127.0.0.2", CLIENTPORT)); + + /* do sendmsg w/IP_PKTINFO tests */ + do_send_pktinfo_tests(client, server, message, strlen(message)); + + rump_sys_close(client); + rump_sys_close(server); +} + + +ATF_TC(pktinfo_send_bindother); +ATF_TC_HEAD(pktinfo_send_bindother, tc) +{ + atf_tc_set_md_var(tc, "descr", + "sendmsg with IP_PKTINFO from address bound by other socket"); +} +ATF_TC_BODY(pktinfo_send_bindother, tc) +{ + int client, server, other, rv; + + setup_test_environment(); + + server = udp_server("127.0.0.1", SERVERPORT); + other = udp_server("127.0.0.2", CLIENTPORT); + RL(client = rump_sys_socket(AF_INET, SOCK_DGRAM, 0)); + RL(rv = sock_bind(client, "127.0.0.3", CLIENTPORT)); + + /* do sendmsg w/IP_PKTINFO tests */ + do_send_pktinfo_tests(client, server, message, strlen(message)); + + rump_sys_close(client); + rump_sys_close(server); + rump_sys_close(other); +} + + +ATF_TC(pktinfo_send_connected); +ATF_TC_HEAD(pktinfo_send_connected, tc) +{ + atf_tc_set_md_var(tc, "descr", + "sendmsg with IP_PKTINFO on connected socket"); +} +ATF_TC_BODY(pktinfo_send_connected, tc) +{ + struct sockaddr_in connectaddr; + int client, server, rv; + + setup_test_environment(); + + server = udp_server("127.0.0.1", SERVERPORT); + RL(client = rump_sys_socket(AF_INET, SOCK_DGRAM, 0)); + + sock_in_init(&connectaddr, "127.0.0.1", SERVERPORT); + RL(rv = rump_sys_connect(client, + (struct sockaddr *)&connectaddr, sizeof(connectaddr))); + + /* sendmsg w/IP_PKTINFO on connected socket should be error */ + rv = sendto_pktinfo(client, message, strlen(message), 0, + "127.0.0.2", "127.0.0.1", SERVERPORT); + + ATF_REQUIRE_MSG(rv == -1, + "sendmsg w/IP_PKTINFO on connected socket should be error," + " but success"); + ATF_REQUIRE_MSG(errno == EISCONN, + "sendmsg with in-use address:port should be EISCONN," + " but got %s", strerror(errno)); + + + rump_sys_close(client); + rump_sys_close(server); +} + + +ATF_TC(pktinfo_send_notown); +ATF_TC_HEAD(pktinfo_send_notown, tc) +{ + atf_tc_set_md_var(tc, "descr", + "sendmsg with IP_PKTINFO from no-own address"); +} +ATF_TC_BODY(pktinfo_send_notown, tc) +{ + int client, server, rv; + + setup_test_environment(); + + server = udp_server("127.0.0.1", SERVERPORT); + RL(client = rump_sys_socket(AF_INET, SOCK_DGRAM, 0)); + + /* sendmsg w/IP_PKTINFO from 127.0.0.100 (not own) should be error */ + rv = sendto_pktinfo(client, message, strlen(message), 0, + "127.0.0.100", "127.0.0.1", SERVERPORT); + + ATF_REQUIRE_MSG(rv == -1, + "sendmsg w/IP_PKTINFO from unavailable address" + " should be error, but success"); + ATF_REQUIRE_MSG(errno == EADDRNOTAVAIL, + "sendmsg with in-use address:port" + " should be EADDRNOTAVAIL, but got %s", strerror(errno)); + + + rump_sys_close(client); + rump_sys_close(server); +} + + +ATF_TC(pktinfo_send_notown_bind); +ATF_TC_HEAD(pktinfo_send_notown_bind, tc) +{ + atf_tc_set_md_var(tc, "descr", + "sendmsg with IP_PKTINFO from no-own address on bind socket"); +} +ATF_TC_BODY(pktinfo_send_notown_bind, tc) +{ + int client, server, rv; + + setup_test_environment(); + + server = udp_server("127.0.0.1", SERVERPORT); + RL(client = rump_sys_socket(AF_INET, SOCK_DGRAM, 0)); + RL(rv = sock_bind(client, "127.0.0.2", CLIENTPORT)); + + /* sendmsg w/IP_PKTINFO from 127.0.0.100 (not own) should be error */ + rv = sendto_pktinfo(client, message, strlen(message), 0, + "127.0.0.100", "127.0.0.1", SERVERPORT); + + ATF_REQUIRE_MSG(rv == -1, + "sendmsg w/IP_PKTINFO from unavailable address should be error," + " but success"); + ATF_REQUIRE_MSG(errno == EADDRNOTAVAIL, + "sendmsg with in-use address:port should be EADDRNOTAVAIL," + " but got %s", strerror(errno)); + + + rump_sys_close(client); + rump_sys_close(server); +} + + +static u_int16_t +in_cksum(uint16_t *p, int len) +{ + u_int32_t sum; + + for (sum = 0; len >= 2; len -= 2) + sum += *p++; + if (len == 1) + sum += ntohs(*(uint8_t *)p * 256); + sum = (sum >> 16) + (sum & 0xffff); + sum = (sum >> 16) + (sum & 0xffff); + return ~sum; +} + +ATF_TC(pktinfo_send_rawip); +ATF_TC_HEAD(pktinfo_send_rawip, tc) +{ + atf_tc_set_md_var(tc, "descr", + "sendmsg raw-ip with IP_PKTINFO"); +} +ATF_TC_BODY(pktinfo_send_rawip, tc) +{ + struct icmp icmp; + size_t icmplen; + int client, server; + + setup_test_environment(); + + RL(server = rump_sys_socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)); + RL(client = rump_sys_socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)); + + memset(&icmp, 0, sizeof(icmp)); + icmp.icmp_type = ICMP_ECHOREPLY; /* against confuse REQ with REPLY */ + icmp.icmp_id = htons(getpid()); + icmp.icmp_cksum = in_cksum((uint16_t *)&icmp, sizeof(icmp)); + icmplen = sizeof(icmp); + + /* sendmsg w/IP_PKTINFO from 127.0.0.2 */ + try_sendmsg_pktinfo(client, server, (const char *)&icmp, icmplen, + "127.0.0.2", "127.0.0.1", 0); + + rump_sys_close(client); + rump_sys_close(server); +} + + +ATF_TC(pktinfo_send_rawip_notown); +ATF_TC_HEAD(pktinfo_send_rawip_notown, tc) +{ + atf_tc_set_md_var(tc, "descr", + "sendmsg raw-ip with IP_PKTINFO from no-own address"); +} +ATF_TC_BODY(pktinfo_send_rawip_notown, tc) +{ + struct icmp icmp; + size_t icmplen; + int client, rv; + + setup_test_environment(); + + RL(client = rump_sys_socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)); + + memset(&icmp, 0, sizeof(icmp)); + icmp.icmp_type = ICMP_ECHOREPLY; /* against confuse REQ with REPLY */ + icmp.icmp_id = htons(getpid()); + icmp.icmp_cksum = in_cksum((uint16_t *)&icmp, sizeof(icmp)); + icmplen = sizeof(icmp); + + /* sendmsg w/IP_PKTINFO from 127.0.0.100 (not own) should be error */ + rv = sendto_pktinfo(client, (const char *)&icmp, icmplen, 0, + "127.0.0.100", "127.0.0.1", 0); + + ATF_REQUIRE_MSG(rv == -1, + "sendmsg w/IP_PKTINFO from unavailable address" + " should be error, but success"); + ATF_REQUIRE_MSG(errno == EADDRNOTAVAIL, + "sendmsg with in-use address:port" + " should be EADDRNOTAVAIL, but got %s", strerror(errno)); + + rump_sys_close(client); +} + + +ATF_TC(pktinfo_send_invalidarg); +ATF_TC_HEAD(pktinfo_send_invalidarg, tc) +{ + atf_tc_set_md_var(tc, "descr", + "sendmsg IP_PKTINFO with invalid argument"); +} +ATF_TC_BODY(pktinfo_send_invalidarg, tc) +{ + int client, rv; + + setup_test_environment(); + + RL(client = rump_sys_socket(AF_INET, SOCK_DGRAM, 0)); + + /* sendmsg w/IP_PKTINFO (ipi_ifindex=0, ipi_addr=0) does nothing */ + rv = sendto_pktinfo(client, message, strlen(message), 0, + "0.0.0.0", "127.0.0.1", SERVERPORT); + + ATF_REQUIRE_MSG(rv != -1, + "sendmsg w/IP_PKTINFO ipi_ifindex=0, ipi_addr=0" + " does nothing (no error), but error %s", strerror(errno)); + + + /* sendmsg w/IP_PKTINFO from 0.0.0.99 (ifindex=99) should be error */ + rv = sendto_pktinfo(client, message, strlen(message), 0, + "0.0.0.99", "127.0.0.1", SERVERPORT); + + ATF_REQUIRE_MSG(rv == -1, + "sendmsg w/IP_PKTINFO from unavailable ifindex" + " should be error, but success"); + ATF_REQUIRE_MSG(errno == EADDRNOTAVAIL, + "sendmsg with in-use address:port" + " should be EADDRNOTAVAIL, but got %s", strerror(errno)); + + rump_sys_close(client); +} + + +ATF_TC(pktinfo_send_ifindex); +ATF_TC_HEAD(pktinfo_send_ifindex, tc) +{ + atf_tc_set_md_var(tc, "descr", + "sendmsg with IP_PKTINFO to specified interface"); +} +ATF_TC_BODY(pktinfo_send_ifindex, tc) +{ + pid_t child; + int channel[2], i; + char ifname2[IFNAMSIZ]; + char ifname3[IFNAMSIZ]; + char ethername[MAXPATHLEN]; + char token; + + snprintf(ethername, sizeof(ethername), + "t_pktinfo_send.link.%u", getpid()); + + RL(pipe(channel)); + + child = fork(); + + RZ(rump_init()); /* XXX: lo0 is ifindex 1 */ + netcfg_rump_makeshmif(ethername, ifname2); /* XXX: ifindex=2 */ + netcfg_rump_makeshmif(ethername, ifname3); /* XXX: ifindex=3 */ + + switch (child) { + case -1: + atf_tc_fail_errno("fork failed"); + case 0: + { + int client, rv; + + netcfg_rump_if(ifname2, "192.168.2.1", "255.255.255.0"); + netcfg_rump_if(ifname3, "192.168.0.1", "255.255.0.0"); + + RL(client = rump_sys_socket(AF_INET, SOCK_DGRAM, 0)); + + /* wait to ready */ + close(channel[1]); + ATF_CHECK(read(channel[0], &token, 1) == 1 && + token == 'U'); + close(channel[0]); + + /* few packets would be discarded while resolving arp */ + for (i = 0; i < 3; i++) { + /* send from ifindex 3 = 192.168.0.1 */ + snprintf(message, sizeof(message), "Hello PKTINFO %d", i); + + RL(rv = sendto_pktinfo(client, message, strlen(message), + 0, "0.0.0.3", "192.168.2.2", SERVERPORT)); + } + + rump_sys_close(client); + pause(); + } + break; + default: + { + struct sockaddr_in from; + socklen_t fromlen; + int server, rv; + char buf[sizeof(message)]; + + netcfg_rump_if(ifname2, "192.168.2.2", "255.255.255.0"); + netcfg_rump_if(ifname3, "192.168.0.2", "255.255.255.0"); + + server = udp_server("0.0.0.0", SERVERPORT); + + /* notify to child */ + close(channel[0]); + ATF_CHECK(write(channel[1], "U", 1) == 1); + close(channel[1]); + + memset(buf, 0, sizeof(buf)); + fromlen = sizeof(from); + RL(rv = rump_sys_recvfrom(server, buf, sizeof(buf), 0, + (struct sockaddr *)&from, &fromlen)); + printf("%s: received \"%s\" from %s:%u\n", __func__, + buf, + inet_ntoa(from.sin_addr), ntohs(from.sin_port)); + + ATF_REQUIRE_MSG(addrcmp(&from, "192.168.0.1") == 0, + "source address of received packet is %s," + " must be %s", + inet_ntoa(from.sin_addr), "192.168.0.1"); + + rump_sys_close(server); + kill(child, SIGKILL); + } + break; + } +} + + +ATF_TC(pktinfo_send_multicast); +ATF_TC_HEAD(pktinfo_send_multicast, tc) +{ + atf_tc_set_md_var(tc, "descr", + "sendmsg with IP_PKTINFO to multicast address" + " and specified interface"); +} +ATF_TC_BODY(pktinfo_send_multicast, tc) +{ + pid_t child; + int channel[2]; + char ifname2[IFNAMSIZ]; + char ifname3[IFNAMSIZ]; + char ethername[MAXPATHLEN]; + char token; + + snprintf(ethername, sizeof(ethername), + "t_pktinfo_send.link.%u", getpid()); + + RL(pipe(channel)); + + child = fork(); + + RZ(rump_init()); /* XXX: lo0 is ifindex 1 */ + netcfg_rump_makeshmif(ethername, ifname2); /* XXX: ifindex=2 */ + netcfg_rump_makeshmif(ethername, ifname3); /* XXX: ifindex=3 */ + + switch (child) { + case -1: + atf_tc_fail_errno("fork failed"); + case 0: + { + int client, rv; + + netcfg_rump_if(ifname2, "192.168.2.1", "255.255.255.0"); + netcfg_rump_if(ifname3, "192.168.0.1", "255.255.0.0"); + + RL(client = rump_sys_socket(AF_INET, SOCK_DGRAM, 0)); + + /* wait to ready */ + close(channel[1]); + ATF_CHECK(read(channel[0], &token, 1) == 1 && + token == 'U'); + close(channel[0]); + + /* send from ifindex 2 = 192.168.2.1 */ + RL(rv = sendto_pktinfo(client, message, strlen(message), + 0, "0.0.0.2", "224.0.0.1", SERVERPORT)); + + /* send from ifindex 3 = 192.168.0.1 */ + RL(rv = sendto_pktinfo(client, message, strlen(message), + 0, "0.0.0.3", "224.0.0.1", SERVERPORT)); + + rump_sys_close(client); + pause(); + } + break; + default: + { + struct sockaddr_in from; + socklen_t fromlen; + int server, i, rv; + char buf[sizeof(message) * 2]; + + netcfg_rump_if(ifname2, "192.168.2.2", "255.255.255.0"); + netcfg_rump_if(ifname3, "192.168.0.2", "255.255.255.0"); + + server = udp_server("0.0.0.0", SERVERPORT); + + /* notify to child */ + close(channel[0]); + ATF_CHECK(write(channel[1], "U", 1) == 1); + close(channel[1]); + + for (i = 0; i < 2; i++) { + fromlen = sizeof(from); + RL(rv = rump_sys_recvfrom(server, buf, + sizeof(buf), 0, + (struct sockaddr *)&from, &fromlen)); + printf("%s: received from %s:%u\n", __func__, + inet_ntoa(from.sin_addr), + ntohs(from.sin_port)); + } + + rump_sys_close(server); + kill(child, SIGKILL); + } + break; + } +} + + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, pktinfo_send_unbound); + ATF_TP_ADD_TC(tp, pktinfo_send_bindany); + ATF_TP_ADD_TC(tp, pktinfo_send_bindaddr); + ATF_TP_ADD_TC(tp, pktinfo_send_bindport); + ATF_TP_ADD_TC(tp, pktinfo_send_bindaddrport); + ATF_TP_ADD_TC(tp, pktinfo_send_bindother); + ATF_TP_ADD_TC(tp, pktinfo_send_connected); + ATF_TP_ADD_TC(tp, pktinfo_send_notown); + ATF_TP_ADD_TC(tp, pktinfo_send_notown_bind); + ATF_TP_ADD_TC(tp, pktinfo_send_rawip); + ATF_TP_ADD_TC(tp, pktinfo_send_rawip_notown); + ATF_TP_ADD_TC(tp, pktinfo_send_invalidarg); + ATF_TP_ADD_TC(tp, pktinfo_send_ifindex); + ATF_TP_ADD_TC(tp, pktinfo_send_multicast); + + return atf_no_error(); +} diff --git a/net/net/t_tcp.c b/net/net/t_tcp.c --- a/net/net/t_tcp.c +++ b/net/net/t_tcp.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_tcp.c,v 1.4 2016/03/04 18:52:01 christos Exp $ */ +/* $NetBSD: t_tcp.c,v 1.11 2019/10/26 23:08:27 christos Exp $ */ /*- * Copyright (c) 2013 The NetBSD Foundation, Inc. @@ -33,37 +33,35 @@ * POSSIBILITY OF SUCH DAMAGE. */ +#define _GNU_SOURCE + #include #ifdef __RCSID -__RCSID("$Id: t_tcp.c,v 1.4 2016/03/04 18:52:01 christos Exp $"); +__RCSID("$Id: t_tcp.c,v 1.11 2019/10/26 23:08:27 christos Exp $"); #endif -/* Example code. Should block; does with accept not paccept. */ +/* Example code. Should block; does with accept not accept4_. */ /* Original by: Justin Cormack */ + #include #include #include #include #include +#include +#include #include #include +#include #include #include #include #include #include -#ifdef TEST -#define FAIL(msg, ...) err(EXIT_FAILURE, msg, ## __VA_ARGS__) -#else -#include -#define FAIL(msg, ...) ATF_CHECK_MSG(0, msg, ## __VA_ARGS__); goto fail -#endif -#ifdef __linux__ -#define paccept(a, b, c, d, e) accept4((a), (b), (c), (e)) -#endif +#include "test.h" static void ding(int al) @@ -71,32 +69,64 @@ } static void -paccept_block(bool pacceptblock, bool fcntlblock) +accept_test(sa_family_t sfamily, sa_family_t cfamily, + bool useaccept, bool accept4_block, bool fcntlblock) { - int srvr = -1, clnt = -1, as = -1; + int srvr = -1, clnt = -1, acpt = -1; int ok, fl; + int count = 5; ssize_t n; char buf[10]; - struct sockaddr_in sin, ba; + struct sockaddr_storage ss, bs; + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; struct sigaction sa; + socklen_t slen; + uid_t euid; + gid_t egid; - srvr = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0); + srvr = socket(sfamily, SOCK_STREAM | SOCK_NONBLOCK, 0); if (srvr == -1) FAIL("socket"); - memset(&sin, 0, sizeof(sin)); - sin.sin_family = AF_INET; + memset(&ss, 0, sizeof(ss)); + switch (ss.ss_family = sfamily) { + case AF_INET: + sin = (void *)&ss; + slen = sizeof(*sin); + sin->sin_port = htons(0); + sin->sin_addr.s_addr = htonl(INADDR_LOOPBACK); + break; + case AF_INET6: + sin6 = (void *)&ss; + slen = sizeof(*sin6); + sin6->sin6_port = htons(0); + if (sfamily == AF_INET6 && cfamily == AF_INET) { + sin6->sin6_addr = in6addr_any; + } else { + sin6->sin6_addr = in6addr_loopback; + } + break; + default: + errno = EINVAL; + FAIL("bad family"); + } #ifdef BSD4_4 - sin.sin_len = sizeof(sin); + ss.ss_len = slen; #endif - sin.sin_port = htons(0); - sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - ok = bind(srvr, (const struct sockaddr *)&sin, (socklen_t)sizeof(sin)); + if (sfamily == AF_INET6 && cfamily == AF_INET) { + int off = 0; + if (setsockopt(srvr, IPPROTO_IPV6, IPV6_V6ONLY, + (void *)&off, sizeof(off)) == -1) + FAIL("setsockopt IPV6_V6ONLY"); + } + + ok = bind(srvr, (const struct sockaddr *)&ss, slen); if (ok == -1) FAIL("bind"); - socklen_t addrlen = sizeof(struct sockaddr_in); - ok = getsockname(srvr, (struct sockaddr *)&ba, &addrlen); + socklen_t addrlen = slen; + ok = getsockname(srvr, (struct sockaddr *)&bs, &addrlen); if (ok == -1) FAIL("getsockname"); @@ -104,18 +134,54 @@ if (ok == -1) FAIL("listen"); - clnt = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0); + clnt = socket(cfamily, SOCK_STREAM | SOCK_NONBLOCK, 0); if (clnt == -1) FAIL("socket"); + if (sfamily == AF_INET6 && cfamily == AF_INET) { + ss = bs; + sin6 = (void *)&ss; + sin = (void *)&bs; + addrlen = sizeof(*sin); +#ifdef BSD4_4 + sin->sin_len = sizeof(*sin); +#endif + sin->sin_family = AF_INET; + sin->sin_port = sin6->sin6_port; + sin->sin_addr.s_addr = htonl(INADDR_LOOPBACK); + } + /* may not connect first time */ - ok = connect(clnt, (struct sockaddr *) &ba, addrlen); + ok = connect(clnt, (struct sockaddr *) &bs, addrlen); +#ifndef __FreeBSD__ + /* FreeBSD: What's going on here, connect succeeds with no-one + * accepting? + */ if (ok != -1 || errno != EINPROGRESS) FAIL("expected connect to fail"); - as = paccept(srvr, NULL, NULL, NULL, pacceptblock ? 0 : SOCK_NONBLOCK); - ok = connect(clnt, (struct sockaddr *) &ba, addrlen); - if (ok == -1 && errno != EISCONN) +#endif + if (useaccept) { + acpt = accept(srvr, NULL, NULL); + } else { + acpt = accept4(srvr, NULL, NULL, + accept4_block ? 0 : SOCK_NONBLOCK); + } +again: + ok = connect(clnt, (struct sockaddr *) &bs, addrlen); + if (ok == -1 && errno != EISCONN) { + if (count-- && errno == EALREADY) { + fprintf(stderr, "retry\n"); + struct timespec ts; + ts.tv_sec = 0; + ts.tv_nsec = 1000000; + nanosleep(&ts, NULL); +#if 0 + sched_yield(); +#endif + goto again; + } FAIL("connect failed"); + } #if 0 fl = fcntl(srvr, F_GETFL, 0); @@ -127,22 +193,46 @@ FAIL("fnctl setfl"); #endif - if (as == -1) { /* not true under NetBSD */ - as = paccept(srvr, NULL, NULL, NULL, pacceptblock ? 0 : SOCK_NONBLOCK); - if (as == -1) - FAIL("paccept"); + if (acpt == -1) { /* not true under NetBSD */ + if (useaccept) { + acpt = accept(srvr, NULL, NULL); + } else { + acpt = accept4(srvr, NULL, NULL, + accept4_block ? 0 : SOCK_NONBLOCK); + } + if (acpt == -1) + FAIL("accept4_"); } - if (fcntlblock) { - fl = fcntl(as, F_GETFL, 0); +#ifdef BSD4_4 +#ifndef __FreeBSD__ + /* NetBSD: This is supposed to only work on Unix sockets but returns + * garbage + * FreeBSD: fails with EISCONN + */ + + if (getpeereid(clnt, &euid, &egid) != -1) + FAIL("getpeereid(clnt)"); + /* NetBSD: This is supposed to only work on Unix sockets but returns + * garbage + * FreeBSD: fails with EISCONN + */ + if (getpeereid(acpt, &euid, &egid) != -1) + FAIL("getpeereid(srvr)"); +#endif +#endif + if (fcntlblock || useaccept) { + fl = fcntl(acpt, F_GETFL, 0); if (fl == -1) FAIL("fnctl"); +#ifndef __linux__ + /* Linux accept returns a blocking socket */ if (fl != (O_RDWR|O_NONBLOCK)) FAIL("fl 0x%x != 0x%x\n", fl, O_RDWR|O_NONBLOCK); - ok = fcntl(as, F_SETFL, fl & ~O_NONBLOCK); + ok = fcntl(acpt, F_SETFL, fl & ~O_NONBLOCK); if (ok == -1) FAIL("fnctl setfl"); - - fl = fcntl(as, F_GETFL, 0); +#endif + fl = fcntl(acpt, F_GETFL, 0); if (fl & O_NONBLOCK) FAIL("fl non blocking after reset"); } @@ -151,9 +241,9 @@ sigemptyset(&sa.sa_mask); sigaction(SIGALRM, &sa, NULL); alarm(1); - n = read(as, buf, 10); + n = read(acpt, buf, 10); - if (pacceptblock || fcntlblock) { + if (useaccept || accept4_block || fcntlblock) { if (n == -1 && errno != EINTR) FAIL("read"); } else { @@ -164,64 +254,161 @@ fail: close(srvr); close(clnt); - close(as); + close(acpt); } #ifndef TEST -ATF_TC(paccept_reset_nonblock); -ATF_TC_HEAD(paccept_reset_nonblock, tc) +ATF_TC(accept_44_preserve_nonblock); +ATF_TC_HEAD(accept_44_preserve_nonblock, tc) +{ + + atf_tc_set_md_var(tc, "descr", "Check that accept(2) preserves " + "the non-blocking flag on non-blocking sockets (ipv4->ipv4)"); +} + +ATF_TC_BODY(accept_44_preserve_nonblock, tc) +{ + accept_test(AF_INET, AF_INET, true, false, false); +} + +ATF_TC(accept4_44_reset_nonblock); +ATF_TC_HEAD(accept4_44_reset_nonblock, tc) +{ + + atf_tc_set_md_var(tc, "descr", "Check that accept4(2) resets " + "the non-blocking flag on non-blocking sockets (ipv4->ipv4)"); +} + +ATF_TC_BODY(accept4_44_reset_nonblock, tc) +{ + accept_test(AF_INET, AF_INET, false, true, false); +} + +ATF_TC(fcntl44_reset_nonblock); +ATF_TC_HEAD(fcntl44_reset_nonblock, tc) +{ + + atf_tc_set_md_var(tc, "descr", "Check that fcntl(2) resets " + "the non-blocking flag on non-blocking sockets (ipv4->ipv4)"); +} + +ATF_TC_BODY(fcntl44_reset_nonblock, tc) +{ + accept_test(AF_INET, AF_INET, false, false, true); +} + +ATF_TC(accept4_44_nonblock); +ATF_TC_HEAD(accept4_44_nonblock, tc) +{ + + atf_tc_set_md_var(tc, "descr", "Check that fcntl(2) resets " + "the non-blocking flag on non-blocking sockets (ipv4->ipv4)"); +} + +ATF_TC_BODY(accept4_44_nonblock, tc) +{ + accept_test(AF_INET, AF_INET, false, false, false); +} + +ATF_TC(accept4_66_reset_nonblock); +ATF_TC_HEAD(accept4_66_reset_nonblock, tc) +{ + + atf_tc_set_md_var(tc, "descr", "Check that accept4(2) resets " + "the non-blocking flag on non-blocking sockets (ipv6->ipv6)"); +} + +ATF_TC_BODY(accept4_66_reset_nonblock, tc) +{ + accept_test(AF_INET6, AF_INET6, false, true, false); +} + +ATF_TC(fcntl66_reset_nonblock); +ATF_TC_HEAD(fcntl66_reset_nonblock, tc) +{ + + atf_tc_set_md_var(tc, "descr", "Check that fcntl(2) resets " + "the non-blocking flag on non-blocking sockets (ipv6->ipv6)"); +} + +ATF_TC_BODY(fcntl66_reset_nonblock, tc) +{ + accept_test(AF_INET6, AF_INET6, false, false, true); +} + +ATF_TC(accept4_66_nonblock); +ATF_TC_HEAD(accept4_66_nonblock, tc) +{ + + atf_tc_set_md_var(tc, "descr", "Check that fcntl(2) resets " + "the non-blocking flag on non-blocking sockets (ipv6->ipv6)"); +} + +ATF_TC_BODY(accept4_66_nonblock, tc) +{ + accept_test(AF_INET6, AF_INET6, false, false, false); +} + +ATF_TC(accept4_46_reset_nonblock); +ATF_TC_HEAD(accept4_46_reset_nonblock, tc) { - atf_tc_set_md_var(tc, "descr", "Check that paccept(2) resets " - "the non-blocking flag on non-blocking sockets"); + atf_tc_set_md_var(tc, "descr", "Check that accept4(2) resets " + "the non-blocking flag on non-blocking sockets (ipv4->ipv6)"); } -ATF_TC_BODY(paccept_reset_nonblock, tc) +ATF_TC_BODY(accept4_46_reset_nonblock, tc) { - paccept_block(true, false); + accept_test(AF_INET6, AF_INET, false, true, false); } -ATF_TC(fcntl_reset_nonblock); -ATF_TC_HEAD(fcntl_reset_nonblock, tc) +ATF_TC(fcntl46_reset_nonblock); +ATF_TC_HEAD(fcntl46_reset_nonblock, tc) { atf_tc_set_md_var(tc, "descr", "Check that fcntl(2) resets " - "the non-blocking flag on non-blocking sockets"); + "the non-blocking flag on non-blocking sockets (ipv4->ipv6)"); } -ATF_TC_BODY(fcntl_reset_nonblock, tc) +ATF_TC_BODY(fcntl46_reset_nonblock, tc) { - paccept_block(false, true); + accept_test(AF_INET6, AF_INET, false, false, true); } -ATF_TC(paccept_nonblock); -ATF_TC_HEAD(paccept_nonblock, tc) +ATF_TC(accept4_46_nonblock); +ATF_TC_HEAD(accept4_46_nonblock, tc) { atf_tc_set_md_var(tc, "descr", "Check that fcntl(2) resets " - "the non-blocking flag on non-blocking sockets"); + "the non-blocking flag on non-blocking sockets (ipv4->ipv6)"); } -ATF_TC_BODY(paccept_nonblock, tc) +ATF_TC_BODY(accept4_46_nonblock, tc) { - paccept_block(false, false); + accept_test(AF_INET6, AF_INET, false, false, false); } ATF_TP_ADD_TCS(tp) { - ATF_TP_ADD_TC(tp, paccept_reset_nonblock); - ATF_TP_ADD_TC(tp, fcntl_reset_nonblock); - ATF_TP_ADD_TC(tp, paccept_nonblock); + ATF_TP_ADD_TC(tp, accept_44_preserve_nonblock); + ATF_TP_ADD_TC(tp, accept4_44_reset_nonblock); + ATF_TP_ADD_TC(tp, fcntl44_reset_nonblock); + ATF_TP_ADD_TC(tp, accept4_44_nonblock); + ATF_TP_ADD_TC(tp, accept4_66_reset_nonblock); + ATF_TP_ADD_TC(tp, fcntl66_reset_nonblock); + ATF_TP_ADD_TC(tp, accept4_66_nonblock); + ATF_TP_ADD_TC(tp, accept4_46_reset_nonblock); + ATF_TP_ADD_TC(tp, fcntl46_reset_nonblock); + ATF_TP_ADD_TC(tp, accept4_46_nonblock); return atf_no_error(); } #else int main(int argc, char *argv[]) { - paccept_block(false); - paccept_block(true); + accept_test(AF_INET, AF_INET, true, true, false); return 0; } #endif diff --git a/net/net/t_unix.c b/net/net/t_unix.c --- a/net/net/t_unix.c +++ b/net/net/t_unix.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_unix.c,v 1.11 2013/11/13 21:41:23 christos Exp $ */ +/* $NetBSD: t_unix.c,v 1.25 2021/08/08 20:54:49 nia Exp $ */ /*- * Copyright (c) 2011 The NetBSD Foundation, Inc. @@ -36,9 +36,10 @@ * POSSIBILITY OF SUCH DAMAGE. */ +#define _GNU_SOURCE #include #ifdef __RCSID -__RCSID("$Id: t_unix.c,v 1.11 2013/11/13 21:41:23 christos Exp $"); +__RCSID("$Id: t_unix.c,v 1.25 2021/08/08 20:54:49 nia Exp $"); #else #define getprogname() argv[0] #endif @@ -51,27 +52,25 @@ #include #include #include +#include +#include #include #include #include #include #include +#include #include #include #include -#ifdef TEST -#define FAIL(msg, ...) err(EXIT_FAILURE, msg, ## __VA_ARGS__) -#else +#include "test.h" -#include -#define FAIL(msg, ...) \ - do { \ - ATF_CHECK_MSG(0, msg, ## __VA_ARGS__); \ - goto fail; \ - } while (/*CONSTCOND*/0) +#define UID 666 +#define GID 999 -#endif +uid_t srvruid, clntuid; +gid_t srvrgid, clntgid; #define OF offsetof(struct sockaddr_un, sun_path) @@ -137,26 +136,116 @@ } static int -test(bool closeit, size_t len) +peercred(int s, uid_t *euid, gid_t *egid, pid_t *pid) +{ +#ifdef SO_PEERCRED + /* Linux */ +# define unpcbid ucred +# define unp_euid uid +# define unp_egid gid +# define unp_pid pid +# define LOCAL_PEEREID SO_PEERCRED +# define LEVEL SOL_SOCKET +#else +# define LEVEL SOL_LOCAL +#endif + +#ifdef LOCAL_PEEREID + /* NetBSD */ + struct unpcbid cred; + socklen_t crl; + crl = sizeof(cred); + if (getsockopt(s, LEVEL, LOCAL_PEEREID, &cred, &crl) == -1) + return -1; + *euid = cred.unp_euid; + *egid = cred.unp_egid; + *pid = cred.unp_pid; + return 0; +#else + /* FreeBSD */ + *pid = -1; + return getpeereid(s, euid, egid); +#endif +} + +static int +check_cred(int fd, bool statit, pid_t checkpid, pid_t otherpid, const char *s) +{ + pid_t pid; + uid_t euid, uid; + gid_t egid, gid; + + if (statit) { + struct stat st; + if (fstat(fd, &st) == -1) + FAIL("fstat (%s)", s); + euid = st.st_uid; + egid = st.st_gid; + pid = checkpid; + } else { + if (peercred(fd, &euid, &egid, &pid) == -1) + FAIL("peercred (%s)", s); + } + printf("%s(%s) euid=%jd egid=%jd pid=%jd\n", + statit ? "fstat" : "peercred", s, + (intmax_t)euid, (intmax_t)egid, (intmax_t)pid); + + if (statit) { + if (strcmp(s, "server") == 0) { + uid = srvruid; + gid = srvrgid; + } else { + uid = clntuid; + gid = clntgid; + } + } else { + if (checkpid != otherpid && strcmp(s, "server") == 0) { + uid = clntuid; + gid = clntgid; + } else { + uid = srvruid; + gid = srvrgid; + } + } + fflush(stdout); + + CHECK_EQUAL(euid, uid, s); + CHECK_EQUAL(egid, gid, s); + CHECK_EQUAL(pid, checkpid, s); + return 0; +fail: + return -1; +} + +static int +test(bool forkit, bool closeit, bool statit, size_t len) { size_t slen; socklen_t sl; int srvr = -1, clnt = -1, acpt = -1; + pid_t srvrpid, clntpid; struct sockaddr_un *sock_addr = NULL, *sun = NULL; socklen_t sock_addrlen; + socklen_t peer_addrlen; + struct sockaddr_un peer_addr; + srvruid = geteuid(); + srvrgid = getegid(); + srvrpid = clntpid = getpid(); srvr = socket(AF_UNIX, SOCK_STREAM, 0); if (srvr == -1) - FAIL("socket(srvrer)"); + FAIL("socket(server)"); slen = len + OF + 1; - + if ((sun = calloc(1, slen)) == NULL) FAIL("calloc"); - srvr = socket(AF_UNIX, SOCK_STREAM, 0); - if (srvr == -1) - FAIL("socket"); + if (mkdir("sock", 0777) == -1) + FAIL("mkdir"); + + if (chdir("sock") == -1) + FAIL("chdir"); memset(sun->sun_path, 'a', len); sun->sun_path[len] = '\0'; @@ -168,6 +257,8 @@ #endif sun->sun_family = AF_UNIX; + unlink(sun->sun_path); + if (bind(srvr, (struct sockaddr *)sun, sl) == -1) { if (errno == EINVAL && sl >= 256) { close(srvr); @@ -175,71 +266,104 @@ } FAIL("bind"); } + if (chmod(sun->sun_path, 0777) == -1) + FAIL("chmod `%s'", sun->sun_path); if (listen(srvr, SOMAXCONN) == -1) FAIL("listen"); - clnt = socket(AF_UNIX, SOCK_STREAM, 0); - if (clnt == -1) - FAIL("socket(client)"); + if (forkit) { + switch (clntpid = fork()) { + case 0: /* child */ + srvrpid = getppid(); + clntpid = getpid(); + if (srvruid == 0) { + setgid(clntgid = GID); + setuid(clntuid = UID); + } else { + clntgid = srvrgid; + clntuid = srvruid; + } + break; + case -1: + FAIL("fork"); + default: + if (srvruid == 0) { + clntgid = GID; + clntuid = UID; + } + break; + } + } - if (connect(clnt, (const struct sockaddr *)sun, sl) == -1) - FAIL("connect"); + if (clntpid == getpid()) { + clnt = socket(AF_UNIX, SOCK_STREAM, 0); + if (clnt == -1) + FAIL("socket(client)"); - if (closeit) { - if (close(clnt) == -1) - FAIL("close"); - clnt = -1; - } + if (connect(clnt, (const struct sockaddr *)sun, sl) == -1) + FAIL("connect"); + check_cred(clnt, statit, srvrpid, clntpid, "client"); - acpt = acc(srvr); -#if 0 - /* - * Both linux and NetBSD return ENOTCONN, why? - */ - if (!closeit) { - socklen_t peer_addrlen; - sockaddr_un peer_addr; + } + if (srvrpid == getpid()) { + acpt = acc(srvr); peer_addrlen = sizeof(peer_addr); memset(&peer_addr, 0, sizeof(peer_addr)); - if (getpeername(srvr, (struct sockaddr *)&peer_addr, + if (getpeername(acpt, (struct sockaddr *)&peer_addr, &peer_addrlen) == -1) FAIL("getpeername"); print("peer", &peer_addr, peer_addrlen); } -#endif - if ((sock_addr = calloc(1, slen)) == NULL) - FAIL("calloc"); - sock_addrlen = slen; - if (getsockname(srvr, (struct sockaddr *)sock_addr, &sock_addrlen) - == -1) - FAIL("getsockname"); - print("sock", sock_addr, sock_addrlen); - - if (sock_addr->sun_family != AF_UNIX) - FAIL("sock_addr->sun_family %d != AF_UNIX", - sock_addr->sun_family); - - len += OF; - if (sock_addrlen LX != len) - FAIL("sock_addr_len %zu != %zu", (size_t)sock_addrlen, len); + if (clntpid == getpid()) { + if (closeit) { + if (close(clnt) == -1) + FAIL("close"); + clnt = -1; + } + } + + if (srvrpid == getpid()) { + check_cred(acpt, statit, clntpid, srvrpid, "server"); + if ((sock_addr = calloc(1, slen)) == NULL) + FAIL("calloc"); + sock_addrlen = slen; + if (getsockname(srvr, (struct sockaddr *)sock_addr, + &sock_addrlen) == -1) + FAIL("getsockname"); + print("sock", sock_addr, sock_addrlen); + + if (sock_addr->sun_family != AF_UNIX) + FAIL("sock_addr->sun_family %d != AF_UNIX", + sock_addr->sun_family); + + len += OF; + if (sock_addrlen LX != len) + FAIL("sock_addr_len %zu != %zu", (size_t)sock_addrlen, + len); #ifdef BSD4_4 - if (sock_addr->sun_len != sl) - FAIL("sock_addr.sun_len %d != %zu", sock_addr->sun_len, - (size_t)sl); + if (sock_addr->sun_len != sl) + FAIL("sock_addr.sun_len %d != %zu", sock_addr->sun_len, + (size_t)sl); #endif - for (size_t i = 0; i < slen - OF; i++) - if (sock_addr->sun_path[i] != sun->sun_path[i]) - FAIL("sock_addr.sun_path[%zu] %d != " - "sun->sun_path[%zu] %d\n", i, - sock_addr->sun_path[i], i, sun->sun_path[i]); - - if (acpt != -1) - (void)close(acpt); - if (srvr != -1) - (void)close(srvr); + for (size_t i = 0; i < slen - OF; i++) + if (sock_addr->sun_path[i] != sun->sun_path[i]) + FAIL("sock_addr.sun_path[%zu] %d != " + "sun->sun_path[%zu] %d\n", i, + sock_addr->sun_path[i], i, + sun->sun_path[i]); + + if (acpt != -1) + (void)close(acpt); + if (srvr != -1) + (void)close(srvr); + free(sock_addr); + sock_addr = NULL; + if (forkit && waitpid(clntpid, NULL, 0) == -1) + FAIL("waitpid"); + } if (clnt != -1 && !closeit) (void)close(clnt); @@ -247,12 +371,16 @@ free(sun); return 0; fail: - if (acpt != -1) - (void)close(acpt); - if (srvr != -1) - (void)close(srvr); - if (clnt != -1 && !closeit) - (void)close(clnt); + if (srvrpid == getpid()) { + if (acpt != -1) + (void)close(acpt); + if (srvr != -1) + (void)close(srvr); + } + if (clntpid == getpid()) { + if (clnt != -1 && !closeit) + (void)close(clnt); + } free(sock_addr); free(sun); return -1; @@ -271,8 +399,8 @@ ATF_TC_BODY(sockaddr_un_len_exceed, tc) { - ATF_REQUIRE_MSG(test(false, 254) == -1, "test(false, 254): %s", - strerror(errno)); + ATF_REQUIRE_MSG(test(false, false, false, 254) == -1, + "test(false, false, false, 254): %s", strerror(errno)); } ATF_TC(sockaddr_un_len_max); @@ -286,8 +414,8 @@ ATF_TC_BODY(sockaddr_un_len_max, tc) { - ATF_REQUIRE_MSG(test(false, 253) == 0, "test(false, 253): %s", - strerror(errno)); + ATF_REQUIRE_MSG(test(false, false, false, 253) == 0, + "test(false, false, false, 253): %s", strerror(errno)); } ATF_TC(sockaddr_un_closed); @@ -300,8 +428,36 @@ ATF_TC_BODY(sockaddr_un_closed, tc) { - ATF_REQUIRE_MSG(test(true, 100) == 0, "test(true, 100): %s", - strerror(errno)); + ATF_REQUIRE_MSG(test(false, true, false, 100) == 0, + "test(false, true, false, 100): %s", strerror(errno)); +} + +ATF_TC(sockaddr_un_local_peereid); +ATF_TC_HEAD(sockaddr_un_local_peereid, tc) +{ + + atf_tc_set_md_var(tc, "descr", "Check that we get the right information" + " from LOCAL_PEEREID"); +} + +ATF_TC_BODY(sockaddr_un_local_peereid, tc) +{ + ATF_REQUIRE_MSG(test(true, true, false, 100) == 0, + "test(true, true, false, 100): %s", strerror(errno)); +} + +ATF_TC(sockaddr_un_fstat); +ATF_TC_HEAD(sockaddr_un_fstat, tc) +{ + + atf_tc_set_md_var(tc, "descr", "Check that we get the right information" + " from fstat"); +} + +ATF_TC_BODY(sockaddr_un_fstat, tc) +{ + ATF_REQUIRE_MSG(test(true, true, true, 100) == 0, + "test(true, true, true, 100): %s", strerror(errno)); } ATF_TP_ADD_TCS(tp) @@ -310,6 +466,8 @@ ATF_TP_ADD_TC(tp, sockaddr_un_len_exceed); ATF_TP_ADD_TC(tp, sockaddr_un_len_max); ATF_TP_ADD_TC(tp, sockaddr_un_closed); + ATF_TP_ADD_TC(tp, sockaddr_un_local_peereid); + ATF_TP_ADD_TC(tp, sockaddr_un_fstat); return atf_no_error(); } #else @@ -322,7 +480,10 @@ fprintf(stderr, "Usage: %s \n", getprogname()); return EXIT_FAILURE; } - test(false, atoi(argv[1])); - test(true, atoi(argv[1])); + test(false, false, false, atoi(argv[1])); + test(false, true, false, atoi(argv[1])); + test(true, false, false, atoi(argv[1])); + test(true, true, false, atoi(argv[1])); + test(true, true, true, atoi(argv[1])); } #endif diff --git a/net/net/test.h b/net/net/test.h new file mode 100644 --- /dev/null +++ b/net/net/test.h @@ -0,0 +1,48 @@ +/*- + * Copyright (c) 2018 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +#ifdef TEST +# define FAIL(msg, ...) err(EXIT_FAILURE, msg, ## __VA_ARGS__) +# define CHECK_EQUAL(a, b, msg) \ + do { \ + if ((a) != (b)) \ + errx(EXIT_FAILURE, "%s: " # a "(%ju) != " # b "(%ju)", \ + msg, (uintmax_t)(a), (uintmax_t)(b)); \ + } while (/*CONSTCOND*/0) +#else + +#include +# define FAIL(msg, ...) \ + do { \ + ATF_CHECK_MSG(0, msg, ## __VA_ARGS__); \ + goto fail; \ + } while (/*CONSTCOND*/0) +# define CHECK_EQUAL(a, b, msg) ATF_CHECK_EQ_MSG(a, b, "%s: " \ + # a "(%ju) != " # b "(%ju) ", msg, (uintmax_t)(a), (uintmax_t)(b)); +#endif diff --git a/net/net_common.sh b/net/net_common.sh old mode 100755 new mode 100644 --- a/net/net_common.sh +++ b/net/net_common.sh @@ -1,4 +1,4 @@ -# $NetBSD: net_common.sh,v 1.11 2017/01/10 05:55:34 ozaki-r Exp $ +# $NetBSD: net_common.sh,v 1.42 2021/07/09 05:54:11 yamaguchi Exp $ # # Copyright (c) 2016 Internet Initiative Japan Inc. # All rights reserved. @@ -29,7 +29,11 @@ # Common utility functions for tests/net # -HIJACKING="env LD_PRELOAD=/usr/lib/librumphijack.so RUMPHIJACK=sysctl=yes" +export PATH="/sbin:/usr/sbin:/bin:/usr/bin" + +HIJACKING="env LD_PRELOAD=/usr/lib/librumphijack.so \ + RUMPHIJACK=path=/rump,socket=all:nolocal,sysctl=yes" +ONEDAYISH="(23h5[0-9]m|1d0h0m)[0-9]+s ?" extract_new_packets() { @@ -40,9 +44,9 @@ old=/dev/null fi - shmif_dumpbus -p - $bus 2>/dev/null| \ + shmif_dumpbus -p - $bus 2>/dev/null | tcpdump -n -e -r - 2>/dev/null > ./.__new - diff -u $old ./.__new |grep '^+' |cut -d '+' -f 2 > ./.__diff + diff -u $old ./.__new | grep '^+' | cut -d '+' -f 2 > ./.__diff mv -f ./.__new ./.__old cat ./.__diff } @@ -54,11 +58,11 @@ local flags=${3:-\.\+} local ifname=${4:-\.\+} - target=$(echo $target |sed 's/\./\\./g') + target=$(echo $target | sed 's/\./\\./g') if [ "$gw" = "" ]; then gw=".+" else - gw=$(echo $gw |sed 's/\./\\./g') + gw=$(echo $gw | sed 's/\./\\./g') fi atf_check -s exit:0 -e ignore \ @@ -80,19 +84,16 @@ check_route_no_entry() { - local target=$(echo $1 |sed 's/\./\\./g') + local target=$(echo "$1" | sed 's/\./\\./g') - atf_check -s exit:0 -e ignore -o not-match:"^$target" \ - rump.netstat -rn + atf_check -s exit:0 -e ignore -o not-match:"^$target" rump.netstat -rn } get_linklocal_addr() { - export RUMP_SERVER=${1} - rump.ifconfig ${2} inet6 | + RUMP_SERVER=${1} rump.ifconfig ${2} inet6 | awk "/fe80/ {sub(/%$2/, \"\"); sub(/\\/[0-9]*/, \"\"); print \$2;}" - unset RUMP_SERVER return 0 } @@ -100,8 +101,7 @@ get_macaddr() { - env RUMP_SERVER=${1} \ - rump.ifconfig ${2} |awk '/address/ {print $2;}' + RUMP_SERVER=${1} rump.ifconfig ${2} | awk '/address/ {print $2;}' } HTTPD_PID=./.__httpd.pid @@ -132,24 +132,95 @@ fi } -BASIC_LIBS="-lrumpnet -lrumpnet_net -lrumpnet_netinet \ - -lrumpnet_shmif -lrumpdev" -FS_LIBS="$BASIC_LIBS -lrumpvfs -lrumpfs_ffs" +NC_PID=./.__nc.pid +start_nc_server() +{ + local sock=$1 + local port=$2 + local outfile=$3 + local proto=${4:-ipv4} + local backup=$RUMP_SERVER + local opts= + + export RUMP_SERVER=$sock + + if [ $proto = ipv4 ]; then + opts="-l -4" + else + opts="-l -6" + fi + + env LD_PRELOAD=/usr/lib/librumphijack.so nc $opts $port > $outfile & + echo $! > $NC_PID + + if [ $proto = ipv4 ]; then + $DEBUG && rump.netstat -a -f inet + else + $DEBUG && rump.netstat -a -f inet6 + fi + + export RUMP_SERVER=$backup + + sleep 1 +} + +stop_nc_server() +{ + + if [ -f $NC_PID ]; then + kill -9 $(cat $NC_PID) + rm -f $NC_PID + sleep 1 + fi +} + +BASIC_LIBS="-lrumpnet -lrumpnet_net -lrumpnet_netinet -lrumpnet_shmif" +FS_LIBS="$BASIC_LIBS -lrumpdev -lrumpvfs -lrumpfs_ffs" +CRYPTO_LIBS="$BASIC_LIBS -lrumpdev -lrumpdev_opencrypto \ + -lrumpkern_z -lrumpkern_crypto" +NPF_LIBS="$BASIC_LIBS -lrumpdev -lrumpvfs -lrumpdev_bpf -lrumpnet_npf" +CRYPTO_NPF_LIBS="$CRYPTO_LIBS -lrumpvfs -lrumpdev_bpf -lrumpnet_npf" +BPF_LIBS="$BASIC_LIBS -lrumpdev -lrumpvfs -lrumpdev_bpf" # We cannot keep variables between test phases, so need to store in files _rump_server_socks=./.__socks _rump_server_ifaces=./.__ifaces _rump_server_buses=./.__buses +_rump_server_macaddrs=./.__macaddrs + +DEBUG_SYSCTL_ENTRIES="net.inet.arp.debug net.inet6.icmp6.nd6_debug \ + net.inet.ipsec.debug" + +IPSEC_KEY_DEBUG=${IPSEC_KEY_DEBUG:-false} _rump_server_start_common() { local sock=$1 - local libs= + local backup=$RUMP_SERVER shift 1 - libs="$*" - atf_check -s exit:0 rump_server $libs $sock + atf_check -s exit:0 rump_server "$@" "$sock" + + if $DEBUG; then + # Enable debugging features in the kernel + export RUMP_SERVER=$sock + for ent in $DEBUG_SYSCTL_ENTRIES; do + if rump.sysctl -q $ent; then + atf_check -s exit:0 rump.sysctl -q -w $ent=1 + fi + done + export RUMP_SERVER=$backup + fi + if $IPSEC_KEY_DEBUG; then + # Enable debugging features in the kernel + export RUMP_SERVER=$sock + if rump.sysctl -q net.key.debug; then + atf_check -s exit:0 \ + rump.sysctl -q -w net.key.debug=0xffff + fi + export RUMP_SERVER=$backup + fi echo $sock >> $_rump_server_socks $DEBUG && cat $_rump_server_socks @@ -158,13 +229,13 @@ rump_server_start() { local sock=$1 - local _libs= + local lib= local libs="$BASIC_LIBS" shift 1 - _libs="$*" - for lib in $_libs; do + for lib + do libs="$libs -lrumpnet_$lib" done @@ -176,13 +247,85 @@ rump_server_fs_start() { local sock=$1 - local _libs= + local lib= local libs="$FS_LIBS" shift 1 - _libs="$*" - for lib in $_libs; do + for lib + do + libs="$libs -lrumpnet_$lib" + done + + _rump_server_start_common $sock $libs + + return 0 +} + +rump_server_crypto_start() +{ + local sock=$1 + local lib= + local libs="$CRYPTO_LIBS" + + shift 1 + + for lib + do + libs="$libs -lrumpnet_$lib" + done + + _rump_server_start_common $sock $libs + + return 0 +} + +rump_server_npf_start() +{ + local sock=$1 + local lib= + local libs="$NPF_LIBS" + + shift 1 + + for lib + do + libs="$libs -lrumpnet_$lib" + done + + _rump_server_start_common $sock $libs + + return 0 +} + +rump_server_crypto_npf_start() +{ + local sock=$1 + local lib= + local libs="$CRYPTO_NPF_LIBS" + + shift 1 + + for lib + do + libs="$libs -lrumpnet_$lib" + done + + _rump_server_start_common $sock $libs + + return 0 +} + +rump_server_bpf_start() +{ + local sock=$1 + local lib= + local libs="$BPF_LIBS" + + shift 1 + + for lib + do libs="$libs -lrumpnet_$lib" done @@ -197,10 +340,23 @@ local ifname=$2 local bus=$3 local backup=$RUMP_SERVER + local macaddr= export RUMP_SERVER=$sock atf_check -s exit:0 rump.ifconfig $ifname create - atf_check -s exit:0 rump.ifconfig $ifname linkstr $bus + if [ -n "$bus" ]; then + atf_check -s exit:0 rump.ifconfig $ifname linkstr $bus + fi + + macaddr=$(get_macaddr $sock $ifname) + if [ -n "$macaddr" ]; then + if [ -f $_rump_server_macaddrs ]; then + atf_check -s not-exit:0 \ + grep -q $macaddr $_rump_server_macaddrs + fi + echo $macaddr >> $_rump_server_macaddrs + fi + export RUMP_SERVER=$backup echo $sock $ifname >> $_rump_server_ifaces @@ -214,33 +370,81 @@ return 0 } +rump_server_check_poolleaks() +{ + local target=$1 + + # XXX rumphijack doesn't work with a binary with suid/sgid bits like + # vmstat. Use a copied one to drop sgid bit as a workaround until + # vmstat stops using kvm(3) for /dev/kmem and the sgid bit. + cp /usr/bin/vmstat ./vmstat + reqs=$($HIJACKING ./vmstat -mv | awk "/$target/ {print \$3;}") + rels=$($HIJACKING ./vmstat -mv | awk "/$target/ {print \$5;}") + rm -f ./vmstat + atf_check_equal '$target$reqs' '$target$rels' +} + +# +# rump_server_check_memleaks detects memory leaks. It can detect leaks of pool +# objects that are guaranteed to be all deallocated at this point, i.e., all +# created interfaces are destroyed. Currently only llentpl satisfies this +# constraint. This mechanism can't be applied to objects allocated through +# pool_cache(9) because it doesn't track released objects explicitly. +# +rump_server_check_memleaks() +{ + + rump_server_check_poolleaks llentrypl + # This doesn't work for objects allocated through pool_cache + #rump_server_check_poolleaks mbpl + #rump_server_check_poolleaks mclpl + #rump_server_check_poolleaks socket +} + rump_server_destroy_ifaces() { local backup=$RUMP_SERVER + local output=ignore + local reqs= rels= $DEBUG && cat $_rump_server_ifaces # Try to dump states before destroying interfaces for sock in $(cat $_rump_server_socks); do export RUMP_SERVER=$sock - atf_check -s exit:0 -o ignore rump.ifconfig - atf_check -s exit:0 -o ignore rump.netstat -nr + if $DEBUG; then + output=save:/dev/stdout + fi + atf_check -s exit:0 -o $output rump.ifconfig + atf_check -s exit:0 -o $output rump.netstat -nr # XXX still need hijacking - atf_check -s exit:0 -o ignore $HIJACKING rump.netstat -i -a - atf_check -s exit:0 -o ignore rump.arp -na - atf_check -s exit:0 -o ignore rump.ndp -na - atf_check -s exit:0 -o ignore $HIJACKING ifmcstat + atf_check -s exit:0 -o $output $HIJACKING rump.netstat -nai + atf_check -s exit:0 -o $output rump.arp -na + atf_check -s exit:0 -o $output rump.ndp -na + atf_check -s exit:0 -o $output $HIJACKING ifmcstat done # XXX using pipe doesn't work. See PR bin/51667 #cat $_rump_server_ifaces | while read sock ifname; do + # Destroy interfaces in the reverse order + tac $_rump_server_ifaces > __ifaces while read sock ifname; do export RUMP_SERVER=$sock if rump.ifconfig -l |grep -q $ifname; then + if $DEBUG; then + rump.ifconfig -v $ifname + fi atf_check -s exit:0 rump.ifconfig $ifname destroy fi atf_check -s exit:0 -o ignore rump.ifconfig - done < $_rump_server_ifaces + done < __ifaces + rm -f __ifaces + + for sock in $(cat $_rump_server_socks); do + export RUMP_SERVER=$sock + rump_server_check_memleaks + done + export RUMP_SERVER=$backup return 0 @@ -259,29 +463,47 @@ return 0 } +extract_rump_server_core() +{ + + if [ -f rump_server.core ]; then + gdb -ex bt /usr/bin/rump_server rump_server.core + # Extract kernel logs including a panic message + strings rump_server.core |grep -E '^\[.+\] ' + fi +} + +dump_kernel_stats() +{ + local sock=$1 + + echo "### Dumping $sock" + export RUMP_SERVER=$sock + rump.ifconfig -av + rump.netstat -nr + # XXX still need hijacking + $HIJACKING rump.netstat -nai + # XXX workaround for vmstat with the sgid bit + cp /usr/bin/vmstat ./vmstat + $HIJACKING ./vmstat -m + rm -f ./vmstat + rump.arp -na + rump.ndp -na + $HIJACKING ifmcstat + $HIJACKING dmesg +} + rump_server_dump_servers() { local backup=$RUMP_SERVER $DEBUG && cat $_rump_server_socks for sock in $(cat $_rump_server_socks); do - echo "### Dumping $sock" - export RUMP_SERVER=$sock - rump.ifconfig - rump.netstat -nr - # XXX still need hijacking - $HIJACKING rump.netstat -i -a - rump.arp -na - rump.ndp -na - $HIJACKING ifmcstat - $HIJACKING dmesg + dump_kernel_stats $sock done export RUMP_SERVER=$backup - if [ -f rump_server.core ]; then - gdb -ex bt /usr/bin/rump_server rump_server.core - strings rump_server.core |grep panic - fi + extract_rump_server_core return 0 } @@ -312,3 +534,53 @@ rump_server_dump_servers rump_server_dump_buses } + +skip_if_qemu() +{ + if sysctl machdep.cpu_brand 2>/dev/null | grep QEMU >/dev/null 2>&1 + then + atf_skip "unreliable under qemu, skip until PR kern/43997 fixed" + fi +} + +test_create_destroy_common() +{ + local sock=$1 + local ifname=$2 + local test_address=${3:-false} + local ipv4="10.0.0.1/24" + local ipv6="fc00::1" + + export RUMP_SERVER=$sock + + atf_check -s exit:0 rump.ifconfig $ifname create + atf_check -s exit:0 rump.ifconfig $ifname destroy + + atf_check -s exit:0 rump.ifconfig $ifname create + atf_check -s exit:0 rump.ifconfig $ifname up + atf_check -s exit:0 rump.ifconfig $ifname down + atf_check -s exit:0 rump.ifconfig $ifname destroy + + # Destroy while UP + atf_check -s exit:0 rump.ifconfig $ifname create + atf_check -s exit:0 rump.ifconfig $ifname up + atf_check -s exit:0 rump.ifconfig $ifname destroy + + if ! $test_address; then + return + fi + + # With an IPv4 address + atf_check -s exit:0 rump.ifconfig $ifname create + atf_check -s exit:0 rump.ifconfig $ifname inet $ipv4 + atf_check -s exit:0 rump.ifconfig $ifname up + atf_check -s exit:0 rump.ifconfig $ifname destroy + + # With an IPv6 address + atf_check -s exit:0 rump.ifconfig $ifname create + atf_check -s exit:0 rump.ifconfig $ifname inet6 $ipv6 + atf_check -s exit:0 rump.ifconfig $ifname up + atf_check -s exit:0 rump.ifconfig $ifname destroy + + unset RUMP_SERVER +} diff --git a/net/npf/t_npf.sh b/net/npf/t_npf.sh old mode 100755 new mode 100644 --- a/net/npf/t_npf.sh +++ b/net/npf/t_npf.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_npf.sh,v 1.2 2012/09/18 08:28:15 martin Exp $ +# $NetBSD: t_npf.sh,v 1.4 2020/06/01 11:08:57 martin Exp $ # # Copyright (c) 2008, 2010 The NetBSD Foundation, Inc. # All rights reserved. @@ -29,7 +29,7 @@ { local name="${1}" - atf_check -o ignore -e ignore npfctl debug "$(atf_get_srcdir)/npftest.conf" ./npf.plist + atf_check -o ignore -e ignore npfctl debug -c "$(atf_get_srcdir)/npftest.conf" -o ./npf.plist atf_check -o ignore npftest -c npf.plist -T "${name}" } @@ -39,12 +39,12 @@ local desc="${*}"; atf_test_case "npf_${name}" - eval "npf_${name}_head() { \ - atf_set \"descr\" \"${desc}\"; \ - atf_set \"require.progs\" \"npfctl npftest\"; \ - }; \ - npf_${name}_body() { \ - run_test \"${name}\"; \ + eval "npf_${name}_head() { + atf_set descr \"${desc}\" + atf_set require.progs npfctl npftest + } + npf_${name}_body() { + run_test ${name} }" atf_add_test_case "npf_${name}" } diff --git a/net/route/Makefile b/net/route/Makefile --- a/net/route/Makefile +++ b/net/route/Makefile @@ -1,11 +1,11 @@ -# $NetBSD: Makefile,v 1.5 2016/11/24 09:05:17 ozaki-r Exp $ +# $NetBSD: Makefile,v 1.6 2017/09/20 09:36:20 ozaki-r Exp $ # .include TESTSDIR= ${TESTSBASE}/net/route -.for name in change flags flags6 route +.for name in change flags flags6 route rtcache TESTS_SH+= t_${name} TESTS_SH_SRC_t_${name}= ../net_common.sh t_${name}.sh .endfor diff --git a/net/route/t_change.sh b/net/route/t_change.sh old mode 100755 new mode 100644 --- a/net/route/t_change.sh +++ b/net/route/t_change.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_change.sh,v 1.9 2016/11/07 05:25:37 ozaki-r Exp $ +# $NetBSD: t_change.sh,v 1.14 2019/05/13 17:55:09 bad Exp $ # # Copyright (c) 2011 The NetBSD Foundation, Inc. # All rights reserved. @@ -26,12 +26,19 @@ # netserver=\ -"rump_server -lrumpdev -lrumpnet -lrumpnet_net \ - -lrumpnet_netinet -lrumpnet_shmif" +"rump_server -lrumpnet -lrumpnet_net -lrumpnet_netinet -lrumpnet_shmif" export RUMP_SERVER=unix://commsock DEBUG=${DEBUG:-false} +route_cleanup_common() +{ + + $DEBUG && dump_kernel_stats unix://commsock + $DEBUG && extract_rump_server_core + env RUMP_SERVER=unix://commsock rump.halt +} + atf_test_case route_change_reject2blackhole cleanup route_change_reject2blackhole_head() { @@ -58,7 +65,7 @@ route_change_reject2blackhole_cleanup() { - env RUMP_SERVER=unix://commsock rump.halt + route_cleanup_common } atf_test_case route_change_gateway cleanup @@ -91,7 +98,7 @@ route_change_gateway_cleanup() { - env RUMP_SERVER=unix://commsock rump.halt + route_cleanup_common } atf_test_case route_change_ifa cleanup @@ -125,7 +132,7 @@ gateway: 10.0.0.1 local addr: 10.0.0.10 interface: shmif0 - flags: + flags: 0x843 recvpipe sendpipe ssthresh rtt,msec rttvar hopcount mtu expire EOF rump.route -n get 192.168.0.1 > ./output @@ -144,7 +151,7 @@ gateway: 10.0.0.1 local addr: 10.0.0.11 interface: shmif0 - flags: + flags: 0x843 recvpipe sendpipe ssthresh rtt,msec rttvar hopcount mtu expire EOF rump.route -n get 192.168.0.1 > ./output @@ -156,7 +163,7 @@ route_change_ifa_cleanup() { - env RUMP_SERVER=unix://commsock rump.halt + route_cleanup_common } atf_test_case route_change_ifp cleanup @@ -192,7 +199,7 @@ gateway: 10.0.0.1 local addr: 10.0.0.10 interface: shmif0 - flags: + flags: 0x843 recvpipe sendpipe ssthresh rtt,msec rttvar hopcount mtu expire EOF rump.route -n get 192.168.0.1 > ./output @@ -211,7 +218,7 @@ gateway: 10.0.0.1 local addr: 10.0.0.11 interface: shmif1 - flags: + flags: 0x843 recvpipe sendpipe ssthresh rtt,msec rttvar hopcount mtu expire EOF rump.route -n get 192.168.0.1 > ./output @@ -223,7 +230,7 @@ route_change_ifp_cleanup() { - env RUMP_SERVER=unix://commsock rump.halt + route_cleanup_common } atf_test_case route_change_ifp_ifa cleanup @@ -259,7 +266,7 @@ gateway: 10.0.0.1 local addr: 10.0.0.10 interface: shmif0 - flags: + flags: 0x843 recvpipe sendpipe ssthresh rtt,msec rttvar hopcount mtu expire EOF rump.route -n get 192.168.0.1 > ./output @@ -278,7 +285,7 @@ gateway: 10.0.0.1 local addr: 10.0.0.11 interface: shmif1 - flags: + flags: 0x843 recvpipe sendpipe ssthresh rtt,msec rttvar hopcount mtu expire EOF rump.route -n get 192.168.0.1 > ./output @@ -290,7 +297,78 @@ route_change_ifp_ifa_cleanup() { - env RUMP_SERVER=unix://commsock rump.halt + route_cleanup_common +} + +atf_test_case route_change_flags cleanup +route_change_flags_head() +{ + + atf_set "descr" "Change flags of a route" + atf_set "require.progs" "rump_server" +} + +route_change_flags_body() +{ + + atf_check -s exit:0 ${netserver} ${RUMP_SERVER} + + atf_check -s exit:0 rump.ifconfig shmif0 create + atf_check -s exit:0 rump.ifconfig shmif0 linkstr bus + atf_check -s exit:0 rump.ifconfig shmif0 10.0.0.10/24 up + + check_route 10.0.0/24 '' UC shmif0 + # Set reject flag + atf_check -s exit:0 -o ignore \ + rump.route change -net 10.0.0.0/24 -reject + check_route 10.0.0/24 '' URCS shmif0 + # Clear reject flag + atf_check -s exit:0 -o ignore \ + rump.route change -net 10.0.0.0/24 -noreject + check_route 10.0.0/24 '' UCS shmif0 + + # TODO other flags +} + +route_change_flags_cleanup() +{ + + route_cleanup_common +} + +atf_test_case route_change_default_flags cleanup +route_change_default_flags_head() +{ + + atf_set "descr" "Change flags of the default route" + atf_set "require.progs" "rump_server" +} + +route_change_default_flags_body() +{ + + atf_check -s exit:0 ${netserver} ${RUMP_SERVER} + + atf_check -s exit:0 rump.ifconfig shmif0 create + atf_check -s exit:0 rump.ifconfig shmif0 linkstr bus + atf_check -s exit:0 rump.ifconfig shmif0 10.0.0.10/24 up + + atf_check -s exit:0 -o ignore rump.route add default 10.0.0.1 + check_route default 10.0.0.1 UGS shmif0 + # Set reject flag + atf_check -s exit:0 -o ignore rump.route change default -reject + check_route default 10.0.0.1 UGRS shmif0 + # Clear reject flag + atf_check -s exit:0 -o ignore rump.route change default -noreject + check_route default 10.0.0.1 UGS shmif0 + + # TODO other flags +} + +route_change_default_flags_cleanup() +{ + + route_cleanup_common } atf_init_test_cases() @@ -301,4 +379,6 @@ atf_add_test_case route_change_ifa atf_add_test_case route_change_ifp atf_add_test_case route_change_ifp_ifa + atf_add_test_case route_change_flags + atf_add_test_case route_change_default_flags } diff --git a/net/route/t_flags.sh b/net/route/t_flags.sh old mode 100755 new mode 100644 --- a/net/route/t_flags.sh +++ b/net/route/t_flags.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_flags.sh,v 1.15 2016/12/21 02:46:08 ozaki-r Exp $ +# $NetBSD: t_flags.sh,v 1.20 2017/08/03 03:16:27 ozaki-r Exp $ # # Copyright (c) 2015 The NetBSD Foundation, Inc. # All rights reserved. @@ -100,7 +100,7 @@ export RUMP_SERVER=$SOCK_LOCAL - # Up, Host, LLINFO, local + # Up, Host, local check_route_flags 10.0.0.2 UHl # Up, Cloning @@ -148,6 +148,8 @@ # Delete an existing route first atf_check -s exit:0 -o ignore rump.route delete -net 10.0.0.0/24 + # Should be removed too + atf_check -s not-exit:0 -e match:'no entry' rump.arp -n 10.0.0.1 # Gateway must be lo0 atf_check -s exit:0 -o ignore \ @@ -163,6 +165,7 @@ # Shouldn't be created check_route_no_entry 10.0.0.1 + atf_check -s not-exit:0 -e match:'no entry' rump.arp -n 10.0.0.1 } test_reject() @@ -185,6 +188,7 @@ # Shouldn't be created check_route_no_entry 10.0.0.1 + atf_check -s not-exit:0 -e match:'no entry' rump.arp -n 10.0.0.1 # Gateway is lo0 (RTF_GATEWAY) @@ -204,6 +208,7 @@ # Shouldn't be created check_route_no_entry 10.0.0.1 + atf_check -s not-exit:0 -e match:'no entry' rump.arp -n 10.0.0.1 # Gateway is lo0 (RTF_HOST) @@ -297,25 +302,39 @@ # TODO test its behavior } +test_llinfo() +{ + local peer_macaddr= + + peer_macaddr=$(get_macaddr $SOCK_PEER shmif0) + + export RUMP_SERVER=$SOCK_LOCAL + + atf_check -s exit:0 -o ignore rump.ping -n -w 1 -c 1 10.0.0.1 + + # Up, Host, LLINFO + check_route 10.0.0.1 $peer_macaddr UHL shmif0 +} + add_test() { local name=$1 local desc="$2" atf_test_case "route_flags_${name}" cleanup - eval "route_flags_${name}_head() { \ - atf_set \"descr\" \"${desc}\"; \ - atf_set \"require.progs\" \"rump_server\"; \ - }; \ - route_flags_${name}_body() { \ - setup_local; \ - setup_peer; \ - test_${name}; \ - rump_server_destroy_ifaces; \ - }; \ - route_flags_${name}_cleanup() { \ - $DEBUG && dump; \ - cleanup; \ + eval "route_flags_${name}_head() { + atf_set descr \"${desc}\" + atf_set require.progs rump_server + } + route_flags_${name}_body() { + setup_local + setup_peer + test_${name} + rump_server_destroy_ifaces + } + route_flags_${name}_cleanup() { + \$DEBUG && dump + cleanup }" atf_add_test_case "route_flags_${name}" } @@ -331,4 +350,5 @@ add_test reject "Tests route flags: reject route" add_test icmp_redirect "Tests route flags: icmp redirect" add_test announce "Tests route flags: announce flag" + add_test llinfo "Tests route flags: ARP caches" } diff --git a/net/route/t_flags6.sh b/net/route/t_flags6.sh old mode 100755 new mode 100644 --- a/net/route/t_flags6.sh +++ b/net/route/t_flags6.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_flags6.sh,v 1.12 2016/12/21 02:46:08 ozaki-r Exp $ +# $NetBSD: t_flags6.sh,v 1.16 2017/08/03 03:16:27 ozaki-r Exp $ # # Copyright (c) 2016 Internet Initiative Japan Inc. # All rights reserved. @@ -134,6 +134,9 @@ # Delete an existing route first atf_check -s exit:0 -o ignore \ rump.route delete -inet6 -net fc00::/64 + # Should be removed too + atf_check -s not-exit:0 -o ignore -e match:'no entry' \ + rump.ndp -n $IP6_PEER # Gateway must be lo0 atf_check -s exit:0 -o ignore \ @@ -149,6 +152,8 @@ # Shouldn't be created check_route_no_entry $IP6_PEER + atf_check -s not-exit:0 -o ignore -e match:'no entry' \ + rump.ndp -n $IP6_PEER } test_reject6() @@ -173,6 +178,8 @@ # Shouldn't be created check_route_no_entry $IP6_PEER + atf_check -s not-exit:0 -o ignore -e match:'no entry' \ + rump.ndp -n $IP6_PEER # Gateway is lo0 (RTF_GATEWAY) @@ -193,6 +200,8 @@ # Shouldn't be created check_route_no_entry $IP6_PEER + atf_check -s not-exit:0 -o ignore -e match:'no entry' \ + rump.ndp -n $IP6_PEER # Gateway is lo0 (RTF_HOST) @@ -232,25 +241,39 @@ # TODO test its behavior } +test_llinfo6() +{ + local peer_macaddr= + + peer_macaddr=$(get_macaddr $SOCK_PEER shmif0) + + export RUMP_SERVER=$SOCK_LOCAL + + atf_check -s exit:0 -o ignore rump.ping6 -n -X 1 -c 1 $IP6_PEER + + # Up, Host, LLINFO + check_route $IP6_PEER $peer_macaddr UHL shmif0 +} + add_test() { local name=$1 local desc="$2" atf_test_case "route_flags_${name}" cleanup - eval "route_flags_${name}_head() { \ - atf_set \"descr\" \"${desc}\"; \ - atf_set \"require.progs\" \"rump_server\"; \ - }; \ - route_flags_${name}_body() { \ - setup_local; \ - setup_peer; \ - test_${name}; \ - rump_server_destroy_ifaces; \ - }; \ - route_flags_${name}_cleanup() { \ - $DEBUG && dump; \ - cleanup; \ + eval "route_flags_${name}_head() { + atf_set descr \"${desc}\" + atf_set require.progs rump_server + } + route_flags_${name}_body() { + setup_local + setup_peer + test_${name} + rump_server_destroy_ifaces + } + route_flags_${name}_cleanup() { + \$DEBUG && dump + cleanup }" atf_add_test_case "route_flags_${name}" } @@ -265,4 +288,5 @@ add_test blackhole6 "Tests route flags: blackhole route" add_test reject6 "Tests route flags: reject route" add_test announce6 "Tests route flags: announce flag" + add_test llinfo6 "Tests route flags: announce llinfo" } diff --git a/net/route/t_route.sh b/net/route/t_route.sh old mode 100755 new mode 100644 --- a/net/route/t_route.sh +++ b/net/route/t_route.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_route.sh,v 1.10 2016/12/21 02:46:08 ozaki-r Exp $ +# $NetBSD: t_route.sh,v 1.14 2017/12/18 04:11:46 ozaki-r Exp $ # # Copyright (c) 2016 Internet Initiative Japan Inc. # All rights reserved. @@ -45,6 +45,8 @@ IP6DST=fc00:0:0:2::2 BUS_SRCGW=bus1 BUS_DSTGW=bus2 +# command_add +SOCKHOST=unix://commsock1 DEBUG=${DEBUG:-false} TIMEOUT=1 @@ -232,7 +234,7 @@ destination: 10.0.1.2 local addr: 10.0.1.2 interface: lo0 - flags: + flags: 0x40045 recvpipe sendpipe ssthresh rtt,msec rttvar hopcount mtu expire EOF rump.route -n get $IP4SRC > ./output @@ -248,7 +250,7 @@ mask: 255.255.255.0 local addr: 10.0.1.2 interface: shmif0 - flags: + flags: 0x141 recvpipe sendpipe ssthresh rtt,msec rttvar hopcount mtu expire EOF rump.route -n get $IP4SRCGW > ./output @@ -264,7 +266,7 @@ gateway: 10.0.1.1 local addr: 10.0.1.2 interface: shmif0 - flags: + flags: 0x843 recvpipe sendpipe ssthresh rtt,msec rttvar hopcount mtu expire EOF rump.route -n get $IP4DST > ./output @@ -282,7 +284,7 @@ mask: 255.255.255.0 local addr: 10.0.1.2 interface: shmif0 - flags: + flags: 0x141 recvpipe sendpipe ssthresh rtt,msec rttvar hopcount mtu expire EOF rump.route -n get $IP4SRCGW > ./output @@ -307,7 +309,7 @@ destination: fc00:0:0:1::2 local addr: fc00:0:0:1::2 interface: lo0 - flags: + flags: 0x40045 recvpipe sendpipe ssthresh rtt,msec rttvar hopcount mtu expire EOF rump.route -n get -inet6 $IP6SRC > ./output @@ -322,7 +324,7 @@ mask: ffff:ffff:ffff:ffff:: local addr: fc00:0:0:1::2 interface: shmif0 - flags: + flags: 0x141 recvpipe sendpipe ssthresh rtt,msec rttvar hopcount mtu expire EOF rump.route -n get -inet6 $IP6SRCGW > ./output @@ -338,7 +340,7 @@ gateway: fc00:0:0:1::1 local addr: fc00:0:0:1::2 interface: shmif0 - flags: + flags: 0x843 recvpipe sendpipe ssthresh rtt,msec rttvar hopcount mtu expire EOF rump.route -n get -inet6 $IP6DST > ./output @@ -356,7 +358,7 @@ mask: ffff:ffff:ffff:ffff:: local addr: fc00:0:0:1::2 interface: shmif0 - flags: + flags: 0x141 recvpipe sendpipe ssthresh rtt,msec rttvar hopcount mtu expire EOF rump.route -n get -inet6 $IP6SRCGW > ./output @@ -397,10 +399,153 @@ cleanup } +atf_test_case route_default_reject cleanup +route_default_reject_head() +{ + + atf_set "descr" "tests for making a default route reject" + atf_set "require.progs" "rump_server" +} + +route_default_reject_body() +{ + + rump_server_start $SOCKSRC netinet6 + rump_server_add_iface $SOCKSRC shmif0 $BUS_SRCGW + + export RUMP_SERVER=$SOCKSRC + + # From /etc/rc.d/network + atf_check -s exit:0 -o match:'add net ::0.0.0.0: gateway ::1' \ + rump.route add -inet6 ::0.0.0.0 -prefixlen 104 ::1 -reject + + atf_check -s exit:0 rump.ifconfig shmif0 inet6 $IP6SRC/64 up + $DEBUG && rump.netstat -nr -f inet6 + atf_check -s exit:0 -o match:"add net default: gateway $IP6SRCGW" \ + rump.route add -inet6 default $IP6SRCGW + $DEBUG && rump.netstat -nr -f inet6 + $DEBUG && rump.route -n get -inet6 default + atf_check -s exit:0 -o match:'change net default' \ + rump.route change -inet6 default -reject + $DEBUG && rump.netstat -nr -f inet6 + + rump_server_destroy_ifaces +} + +route_default_reject_cleanup() +{ + + $DEBUG && dump + cleanup +} + +atf_test_case route_command_add cleanup +route_command_add_head() +{ + + atf_set "descr" "tests of route add command" + atf_set "require.progs" "rump_server" +} + +route_command_add_body() +{ + + rump_server_start $SOCKHOST + + export RUMP_SERVER=${SOCKHOST} + rump_server_add_iface $SOCKHOST shmif0 $BUS + atf_check -s exit:0 rump.ifconfig shmif0 10.0.0.1/24 + + # Accept the route whose gateway is in a subnet of interface address + atf_check -s exit:0 -o ignore rump.route add \ + -net 10.0.1.0/24 10.0.0.2 + atf_check -s exit:0 -o ignore rump.route add \ + -host 10.0.2.1 10.0.0.3 + + # Accept the route whose gateway is an interface + atf_check -s exit:0 -o ignore rump.route add \ + -net 10.0.3.0/24 -connected -link -iface shmif0 + + # Accept the route whose gateway is reachable and not RTF_GATEWAY + atf_check -s exit:0 -o ignore rump.route add \ + -net 10.0.4.0/24 10.0.3.1 + + # Don't accept the route whose destination is reachable and + # gateway is unreachable + atf_check -s not-exit:0 -o ignore -e match:'unreachable' rump.route add \ + -net 10.0.1.0/26 10.0.5.1 + + # Don't accept the route whose gateway is reachable and RTF_GATEWAY + atf_check -s not-exit:0 -o ignore -e ignore rump.route add \ + -net 10.0.6.0/24 10.0.1.1 + + rump_server_destroy_ifaces +} + +route_command_add_cleanup() +{ + + $DEBUG && dump + cleanup +} + +atf_test_case route_command_add6 cleanup +route_command_add6_head() +{ + + atf_set "descr" "tests of route add command (IPv6)" + atf_set "require.progs" "rump_server" +} + +route_command_add6_body() +{ + + rump_server_start $SOCKHOST netinet6 + + export RUMP_SERVER=${SOCKHOST} + rump_server_add_iface $SOCKHOST shmif0 $BUS + atf_check -s exit:0 rump.ifconfig shmif0 inet6 fc00::1/64 + + # Accept the route whose gateway is in a subnet of interface address + atf_check -s exit:0 -o ignore rump.route add -inet6\ + -net fc00:1::0/64 fc00::2 + atf_check -s exit:0 -o ignore rump.route add -inet6\ + -host fc00:2::1 fc00::3 + + # Accept the route whose gateway is an interface + atf_check -s exit:0 -o ignore rump.route add -inet6\ + -net fc00:3::0/64 -connected -link -iface shmif0 + + # Accept the route whose gateway is reachable and not RTF_GATEWAY + atf_check -s exit:0 -o ignore rump.route add -inet6\ + -net fc00:4::0/64 fc00:3::1 + + # Don't accept the route whose destination is reachable and + # gateway is unreachable + atf_check -s not-exit:0 -o ignore -e match:'unreachable' rump.route add \ + -inet6 -net fc00::4/128 fc00:5::1 + + # Don't accept the route whose gateway is reachable and RTF_GATEWAY + atf_check -s not-exit:0 -o ignore -e match:'unreachable' rump.route add \ + -inet6 -net fc00:6::0/64 fc00:1::1 + + rump_server_destroy_ifaces +} + +route_command_add6_cleanup() +{ + + $DEBUG && dump + cleanup +} + atf_init_test_cases() { atf_add_test_case route_non_subnet_gateway atf_add_test_case route_command_get atf_add_test_case route_command_get6 + atf_add_test_case route_default_reject + atf_add_test_case route_command_add + atf_add_test_case route_command_add6 } diff --git a/net/route/t_rtcache.sh b/net/route/t_rtcache.sh new file mode 100644 --- /dev/null +++ b/net/route/t_rtcache.sh @@ -0,0 +1,167 @@ +# $NetBSD: t_rtcache.sh,v 1.1 2017/09/20 09:36:20 ozaki-r Exp $ +# +# Copyright (c) 2017 Internet Initiative Japan Inc. +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +SOCK_SRC=unix://rtcache_src +SOCK_FWD=unix://rtcache_fwd +SOCK_DST1=unix://rtcache_dst1 +SOCK_DST2=unix://rtcache_dst2 + +BUS_SRC=./src +BUS_DST1=./dst1 +BUS_DST2=./dst2 + +DEBUG=${DEBUG:-false} + +atf_test_case rtcache_invalidation cleanup +rtcache_invalidation_head() +{ + + atf_set "descr" "Tests for rtcache invalidation" + atf_set "require.progs" "rump_server" +} + +rtcache_invalidation_body() +{ + local ip_src=10.0.0.2 + local ip_gwsrc=10.0.0.1 + local ip_gwdst1=10.0.1.1 + local ip_gwdst2=10.0.2.1 + local ip_dst1=10.0.1.2 + local ip_dst2=10.0.2.2 + local ip_dst=10.0.3.1 + local subnet_src=10.0.0.0 + local subnet_dst=10.0.3.0 + + rump_server_start $SOCK_SRC + rump_server_start $SOCK_FWD + rump_server_start $SOCK_DST1 + rump_server_start $SOCK_DST2 + + rump_server_add_iface $SOCK_SRC shmif0 $BUS_SRC + + rump_server_add_iface $SOCK_FWD shmif0 $BUS_SRC + rump_server_add_iface $SOCK_FWD shmif1 $BUS_DST1 + rump_server_add_iface $SOCK_FWD shmif2 $BUS_DST2 + + rump_server_add_iface $SOCK_DST1 shmif0 $BUS_DST1 + rump_server_add_iface $SOCK_DST2 shmif0 $BUS_DST2 + + export RUMP_SERVER=$SOCK_SRC + atf_check -s exit:0 rump.sysctl -q -w net.inet.ip.dad_count=0 + atf_check -s exit:0 rump.ifconfig shmif0 $ip_src/24 + atf_check -s exit:0 -o ignore rump.route add default $ip_gwsrc + + export RUMP_SERVER=$SOCK_FWD + atf_check -s exit:0 rump.sysctl -q -w net.inet.ip.forwarding=1 + atf_check -s exit:0 rump.sysctl -q -w net.inet.ip.dad_count=0 + atf_check -s exit:0 rump.ifconfig shmif0 $ip_gwsrc/24 + atf_check -s exit:0 rump.ifconfig shmif1 $ip_gwdst1/24 + atf_check -s exit:0 rump.ifconfig shmif2 $ip_gwdst2/24 + + export RUMP_SERVER=$SOCK_DST1 + atf_check -s exit:0 rump.sysctl -q -w net.inet.ip.dad_count=0 + atf_check -s exit:0 rump.ifconfig shmif0 $ip_dst1/24 + atf_check -s exit:0 -o ignore rump.route add default $ip_gwdst1 + atf_check -s exit:0 rump.ifconfig shmif0 $ip_dst/24 alias + + export RUMP_SERVER=$SOCK_DST2 + atf_check -s exit:0 rump.sysctl -q -w net.inet.ip.dad_count=0 + atf_check -s exit:0 rump.ifconfig shmif0 $ip_dst2/24 + atf_check -s exit:0 -o ignore rump.route add default $ip_gwdst2 + atf_check -s exit:0 rump.ifconfig shmif0 $ip_dst/24 alias + + export RUMP_SERVER=$SOCK_SRC + atf_check -s exit:0 -o ignore rump.ping -n -w 3 -c 1 $ip_dst1 + atf_check -s exit:0 -o ignore rump.ping -n -w 3 -c 1 $ip_dst2 + # It fails because there is no route to $ip_dst + atf_check -s not-exit:0 -o ignore rump.ping -n -w 3 -c 1 $ip_dst + + extract_new_packets $BUS_DST1 > ./outfile + + export RUMP_SERVER=$SOCK_FWD + # Add the default route so that $ip_dst @ dst1 is reachable now + atf_check -s exit:0 -o ignore rump.route add default $ip_dst1 + export RUMP_SERVER=$SOCK_DST1 + # ...but don't response ICMP requests to avoid rtcache pollution + atf_check -s exit:0 -o ignore \ + rump.route add -net $subnet_src/24 127.0.0.1 -blackhole + + export RUMP_SERVER=$SOCK_SRC + # ping fails expectedly + atf_check -s not-exit:0 -o ignore rump.ping -n -w 3 -c 1 $ip_dst + + # An ICMP request should come to dst1 + extract_new_packets $BUS_DST1 > ./outfile + atf_check -s exit:0 -o match:"$ip_src > $ip_dst: ICMP echo request" \ + cat ./outfile + + export RUMP_SERVER=$SOCK_FWD + # Teach the subnet of $ip_dst is at dst2 + atf_check -s exit:0 -o ignore rump.route add -net $subnet_dst/24 $ip_dst2 + export RUMP_SERVER=$SOCK_DST2 + # ...but don't response ICMP requests to avoid rtcache pollution + atf_check -s exit:0 -o ignore \ + rump.route add -net $subnet_src/24 127.0.0.1 -blackhole + + export RUMP_SERVER=$SOCK_SRC + # ping fails expectedly + atf_check -s not-exit:0 -o ignore rump.ping -n -w 3 -c 1 $ip_dst + + # An ICMP request should come to dst2. If rtcaches aren't invalidated + # correctly, the ICMP request should appear at dst1 + extract_new_packets $BUS_DST1 > ./outfile + atf_check -s exit:0 -o not-match:"$ip_src > $ip_dst: ICMP echo request" \ + cat ./outfile + + export RUMP_SERVER=$SOCK_FWD + # Delete the route so the packets heading to $ip_dst should go dst1 again + atf_check -s exit:0 -o ignore rump.route delete -net $subnet_dst/24 $ip_dst2 + + export RUMP_SERVER=$SOCK_SRC + # ping fails expectedly + atf_check -s not-exit:0 -o ignore rump.ping -n -w 3 -c 1 $ip_dst + + # An ICMP request should come to dst1 + extract_new_packets $BUS_DST1 > ./outfile + atf_check -s exit:0 -o match:"$ip_src > $ip_dst: ICMP echo request" \ + cat ./outfile + + rump_server_destroy_ifaces +} + +rtcache_invalidation_cleanup() +{ + + $DEBUG && dump + cleanup +} + +atf_init_test_cases() +{ + + atf_add_test_case rtcache_invalidation +} diff --git a/rump/kernspace/Makefile b/rump/kernspace/Makefile --- a/rump/kernspace/Makefile +++ b/rump/kernspace/Makefile @@ -1,10 +1,11 @@ -# $NetBSD: Makefile,v 1.5 2011/01/14 13:08:00 pooka Exp $ +# $NetBSD: Makefile,v 1.7 2018/12/24 21:42:05 thorpej Exp $ # .include LIB= kernspace -SRCS= thread.c busypage.c tsleep.c alloc.c lockme.c sendsig.c +SRCS= thread.c threadpool.c busypage.c tsleep.c alloc.c lockme.c \ + workqueue.c sendsig.c RUMPTOP=${NETBSDSRCDIR}/sys/rump diff --git a/rump/kernspace/busypage.c b/rump/kernspace/busypage.c --- a/rump/kernspace/busypage.c +++ b/rump/kernspace/busypage.c @@ -1,4 +1,4 @@ -/* $NetBSD: busypage.c,v 1.5 2011/08/07 14:03:15 rmind Exp $ */ +/* $NetBSD: busypage.c,v 1.8 2020/03/17 18:31:39 ad Exp $ */ /*- * Copyright (c) 2010 The NetBSD Foundation, Inc. @@ -29,7 +29,7 @@ #include #if !defined(lint) -__RCSID("$NetBSD: busypage.c,v 1.5 2011/08/07 14:03:15 rmind Exp $"); +__RCSID("$NetBSD: busypage.c,v 1.8 2020/03/17 18:31:39 ad Exp $"); #endif /* !lint */ #include @@ -52,11 +52,14 @@ thread(void *arg) { - mutex_enter(uobj->vmobjlock); + mutex_enter(&testpg->interlock); threadrun = true; cv_signal(&tcv); - testpg->flags |= PG_WANTED; - UVM_UNLOCK_AND_WAIT(testpg, uobj->vmobjlock, false, "tw", 0); + mutex_exit(&testpg->interlock); + + rw_enter(uobj->vmobjlock, RW_READER); + uvm_pagewait(testpg, uobj->vmobjlock, "tw"); + kthread_exit(0); } @@ -69,9 +72,9 @@ cv_init(&tcv, "napina"); uobj = uao_create(1, 0); - mutex_enter(uobj->vmobjlock); + rw_enter(uobj->vmobjlock, RW_WRITER); testpg = uvm_pagealloc(uobj, 0, NULL, 0); - mutex_exit(uobj->vmobjlock); + rw_exit(uobj->vmobjlock); if (testpg == NULL) panic("couldn't create vm page"); @@ -80,12 +83,16 @@ if (rv) panic("thread creation failed: %d", rv); - mutex_enter(uobj->vmobjlock); + kpause("lolgic", false, mstohz(100), NULL); + + mutex_enter(&testpg->interlock); while (!threadrun) - cv_wait(&tcv, uobj->vmobjlock); + cv_wait(&tcv, &testpg->interlock); + mutex_exit(&testpg->interlock); + rw_enter(uobj->vmobjlock, RW_WRITER); uvm_page_unbusy(&testpg, 1); - mutex_exit(uobj->vmobjlock); + rw_exit(uobj->vmobjlock); rv = kthread_join(newl); if (rv) diff --git a/rump/kernspace/kernspace.h b/rump/kernspace/kernspace.h --- a/rump/kernspace/kernspace.h +++ b/rump/kernspace/kernspace.h @@ -1,7 +1,7 @@ -/* $NetBSD: kernspace.h,v 1.4 2011/01/14 13:08:00 pooka Exp $ */ +/* $NetBSD: kernspace.h,v 1.8 2018/12/28 19:54:36 thorpej Exp $ */ /*- - * Copyright (c) 2010 The NetBSD Foundation, Inc. + * Copyright (c) 2010, 2018 The NetBSD Foundation, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -40,8 +40,17 @@ void rumptest_tsleep(void); void rumptest_alloc(size_t); void rumptest_lockme(enum locktest); +void rumptest_workqueue1(void); +void rumptest_workqueue_wait(void); void rumptest_sendsig(char *); void rumptest_localsig(int); +void rumptest_threadpool_unbound_lifecycle(void); +void rumptest_threadpool_percpu_lifecycle(void); +void rumptest_threadpool_unbound_schedule(void); +void rumptest_threadpool_percpu_schedule(void); +void rumptest_threadpool_job_cancel(void); +void rumptest_threadpool_job_cancelthrash(void); + #endif /* _TESTS_RUMP_KERNSPACE_KERNSPACE_H_ */ diff --git a/rump/kernspace/sendsig.c b/rump/kernspace/sendsig.c --- a/rump/kernspace/sendsig.c +++ b/rump/kernspace/sendsig.c @@ -1,4 +1,4 @@ -/* $NetBSD: sendsig.c,v 1.1 2011/01/14 13:08:00 pooka Exp $ */ +/* $NetBSD: sendsig.c,v 1.2 2020/05/23 23:42:44 ad Exp $ */ /*- * Copyright (c) 2011 The NetBSD Foundation, Inc. @@ -29,7 +29,7 @@ #include #if !defined(lint) -__RCSID("$NetBSD: sendsig.c,v 1.1 2011/01/14 13:08:00 pooka Exp $"); +__RCSID("$NetBSD: sendsig.c,v 1.2 2020/05/23 23:42:44 ad Exp $"); #endif /* !lint */ #include @@ -52,7 +52,7 @@ sig = strtoull(signo, NULL, 10); rump_boot_setsigmodel(RUMP_SIGMODEL_RAISE); - mutex_enter(proc_lock); + mutex_enter(&proc_lock); while (!sent) { PROCLIST_FOREACH(p, &allproc) { if (p->p_pid > 1) { @@ -63,9 +63,9 @@ break; } } - kpause("w8", false, 1, proc_lock); + kpause("w8", false, 1, &proc_lock); } - mutex_exit(proc_lock); + mutex_exit(&proc_lock); /* restore default */ rump_boot_setsigmodel(RUMP_SIGMODEL_PANIC); diff --git a/rump/kernspace/threadpool.c b/rump/kernspace/threadpool.c new file mode 100644 --- /dev/null +++ b/rump/kernspace/threadpool.c @@ -0,0 +1,288 @@ +/* $NetBSD: threadpool.c,v 1.5 2019/01/04 05:35:24 thorpej Exp $ */ + +/*- + * Copyright (c) 2018 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +#if !defined(lint) +__RCSID("$NetBSD: threadpool.c,v 1.5 2019/01/04 05:35:24 thorpej Exp $"); +#endif /* !lint */ + +#include +#include +#include +#include +#include +#include + +#include "kernspace.h" + +void +rumptest_threadpool_unbound_lifecycle(void) +{ + struct threadpool *pool0, *pool1, *pool2; + int error; + + error = threadpool_get(&pool0, PRI_NONE); + KASSERT(error == 0); + + error = threadpool_get(&pool1, PRI_NONE); + KASSERT(error == 0); + + KASSERT(pool0 == pool1); + + error = threadpool_get(&pool2, PRI_KERNEL_RT); + KASSERT(error == 0); + + KASSERT(pool0 != pool2); + + threadpool_put(pool0, PRI_NONE); + threadpool_put(pool1, PRI_NONE); + threadpool_put(pool2, PRI_KERNEL_RT); +} + +void +rumptest_threadpool_percpu_lifecycle(void) +{ + struct threadpool_percpu *pcpu0, *pcpu1, *pcpu2; + int error; + + error = threadpool_percpu_get(&pcpu0, PRI_NONE); + KASSERT(error == 0); + + error = threadpool_percpu_get(&pcpu1, PRI_NONE); + KASSERT(error == 0); + + KASSERT(pcpu0 == pcpu1); + + error = threadpool_percpu_get(&pcpu2, PRI_KERNEL_RT); + KASSERT(error == 0); + + KASSERT(pcpu0 != pcpu2); + + threadpool_percpu_put(pcpu0, PRI_NONE); + threadpool_percpu_put(pcpu1, PRI_NONE); + threadpool_percpu_put(pcpu2, PRI_KERNEL_RT); +} + +struct test_job_data { + kmutex_t mutex; + kcondvar_t cond; + unsigned int count; + struct threadpool_job job; +}; + +#define FINAL_COUNT 12345 + +static void +test_job_func_schedule(struct threadpool_job *job) +{ + struct test_job_data *data = + container_of(job, struct test_job_data, job); + + mutex_enter(&data->mutex); + KASSERT(data->count != FINAL_COUNT); + data->count++; + cv_broadcast(&data->cond); + threadpool_job_done(job); + mutex_exit(&data->mutex); +} + +static void +test_job_func_cancel(struct threadpool_job *job) +{ + struct test_job_data *data = + container_of(job, struct test_job_data, job); + + mutex_enter(&data->mutex); + if (data->count == 0) { + data->count = 1; + cv_broadcast(&data->cond); + } + while (data->count != FINAL_COUNT - 1) + cv_wait(&data->cond, &data->mutex); + data->count = FINAL_COUNT; + cv_broadcast(&data->cond); + threadpool_job_done(job); + mutex_exit(&data->mutex); +} + +static void +init_test_job_data(struct test_job_data *data, threadpool_job_fn_t fn) +{ + mutex_init(&data->mutex, MUTEX_DEFAULT, IPL_NONE); + cv_init(&data->cond, "testjob"); + threadpool_job_init(&data->job, fn, &data->mutex, "testjob"); + data->count = 0; +} + +static void +fini_test_job_data(struct test_job_data *data) +{ + threadpool_job_destroy(&data->job); + cv_destroy(&data->cond); + mutex_destroy(&data->mutex); +} + +void +rumptest_threadpool_unbound_schedule(void) +{ + struct test_job_data data; + struct threadpool *pool; + int error; + + error = threadpool_get(&pool, PRI_NONE); + KASSERT(error == 0); + + init_test_job_data(&data, test_job_func_schedule); + + mutex_enter(&data.mutex); + while (data.count != FINAL_COUNT) { + threadpool_schedule_job(pool, &data.job); + error = cv_timedwait(&data.cond, &data.mutex, hz * 2); + KASSERT(error != EWOULDBLOCK); + } + mutex_exit(&data.mutex); + + fini_test_job_data(&data); + + threadpool_put(pool, PRI_NONE); +} + +void +rumptest_threadpool_percpu_schedule(void) +{ + struct test_job_data data; + struct threadpool_percpu *pcpu; + struct threadpool *pool; + int error; + + error = threadpool_percpu_get(&pcpu, PRI_NONE); + KASSERT(error == 0); + + pool = threadpool_percpu_ref(pcpu); + + init_test_job_data(&data, test_job_func_schedule); + + mutex_enter(&data.mutex); + while (data.count != FINAL_COUNT) { + threadpool_schedule_job(pool, &data.job); + error = cv_timedwait(&data.cond, &data.mutex, hz * 2); + KASSERT(error != EWOULDBLOCK); + } + mutex_exit(&data.mutex); + + fini_test_job_data(&data); + + threadpool_percpu_put(pcpu, PRI_NONE); +} + +void +rumptest_threadpool_job_cancel(void) +{ + struct test_job_data data; + struct threadpool *pool; + int error; + bool rv; + + error = threadpool_get(&pool, PRI_NONE); + KASSERT(error == 0); + + init_test_job_data(&data, test_job_func_cancel); + + mutex_enter(&data.mutex); + threadpool_schedule_job(pool, &data.job); + while (data.count == 0) + cv_wait(&data.cond, &data.mutex); + KASSERT(data.count == 1); + + /* Job is already running (and is not finished); this shold fail. */ + rv = threadpool_cancel_job_async(pool, &data.job); + KASSERT(rv == false); + + data.count = FINAL_COUNT - 1; + cv_broadcast(&data.cond); + + /* Now wait for the job to finish. */ + threadpool_cancel_job(pool, &data.job); + KASSERT(data.count == FINAL_COUNT); + mutex_exit(&data.mutex); + + fini_test_job_data(&data); + + threadpool_put(pool, PRI_NONE); +} + +void +rumptest_threadpool_job_cancelthrash(void) +{ + struct test_job_data data; + struct threadpool *pool; + int i, error; + + error = threadpool_get(&pool, PRI_NONE); + KASSERT(error == 0); + + init_test_job_data(&data, test_job_func_cancel); + + mutex_enter(&data.mutex); + for (i = 0; i < 10000; i++) { + threadpool_schedule_job(pool, &data.job); + if ((i % 3) == 0) { + mutex_exit(&data.mutex); + mutex_enter(&data.mutex); + } + /* + * If the job managed to start, ensure that its exit + * condition is met so that we don't wait forever + * for the job to finish. + */ + data.count = FINAL_COUNT - 1; + cv_broadcast(&data.cond); + + threadpool_cancel_job(pool, &data.job); + + /* + * After cancellation, either the job didn't start + * (data.count == FINAL_COUNT - 1, per above) or + * it finished (data.count == FINAL_COUNT). + */ + KASSERT(data.count == (FINAL_COUNT - 1) || + data.count == FINAL_COUNT); + + /* Reset for the loop. */ + data.count = 0; + } + mutex_exit(&data.mutex); + + fini_test_job_data(&data); + + threadpool_put(pool, PRI_NONE); +} diff --git a/rump/kernspace/workqueue.c b/rump/kernspace/workqueue.c new file mode 100644 --- /dev/null +++ b/rump/kernspace/workqueue.c @@ -0,0 +1,139 @@ +/* $NetBSD: workqueue.c,v 1.6 2017/12/28 07:46:34 ozaki-r Exp $ */ + +/*- + * Copyright (c) 2017 The NetBSD Foundation, Inc. + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +#if !defined(lint) +__RCSID("$NetBSD: workqueue.c,v 1.6 2017/12/28 07:46:34 ozaki-r Exp $"); +#endif /* !lint */ + +#include +#include +#include +#include +#include +#include +#include + +#include "kernspace.h" + +struct test_softc { + kmutex_t mtx; + kcondvar_t cv; + struct workqueue *wq; + struct work wk; + int counter; +}; + +static void +rump_work1(struct work *wk, void *arg) +{ + struct test_softc *sc = arg; + + mutex_enter(&sc->mtx); + ++sc->counter; + cv_broadcast(&sc->cv); + mutex_exit(&sc->mtx); +} + +static struct test_softc * +create_sc(void) +{ + int rv; + struct test_softc *sc; + + sc = kmem_zalloc(sizeof(*sc), KM_SLEEP); + mutex_init(&sc->mtx, MUTEX_DEFAULT, IPL_NONE); + cv_init(&sc->cv, "rumpwqcv"); + rv = workqueue_create(&sc->wq, "rumpwq", + rump_work1, sc, PRI_SOFTNET, IPL_SOFTNET, 0); + if (rv) + panic("workqueue creation failed: %d", rv); + + sc->counter = 0; + + return sc; +} + +static void +destroy_sc(struct test_softc *sc) +{ + + cv_destroy(&sc->cv); + mutex_destroy(&sc->mtx); + workqueue_destroy(sc->wq); +} + +void +rumptest_workqueue1() +{ + struct test_softc *sc; + + sc = create_sc(); + +#define ITERATIONS 12435 + for (int i = 0; i < ITERATIONS; ++i) { + int e; + mutex_enter(&sc->mtx); + workqueue_enqueue(sc->wq, &sc->wk, NULL); + e = cv_timedwait(&sc->cv, &sc->mtx, hz * 2); + if (e != 0) + panic("cv_timedwait timed out (i=%d)", i); + mutex_exit(&sc->mtx); + } + + KASSERT(sc->counter == ITERATIONS); + + destroy_sc(sc); +#undef ITERATIONS +} + +void +rumptest_workqueue_wait(void) +{ + struct test_softc *sc; + struct work dummy; + + sc = create_sc(); + +#define ITERATIONS 12435 + for (size_t i = 0; i < ITERATIONS; ++i) { + KASSERT(sc->counter == i); + workqueue_enqueue(sc->wq, &sc->wk, NULL); + workqueue_wait(sc->wq, &sc->wk); + KASSERT(sc->counter == (i + 1)); + } + + KASSERT(sc->counter == ITERATIONS); + + /* Wait for a work that is not enqueued. Just return immediately. */ + workqueue_wait(sc->wq, &dummy); + + destroy_sc(sc); +#undef ITERATIONS +} diff --git a/rump/modautoload/Makefile b/rump/modautoload/Makefile --- a/rump/modautoload/Makefile +++ b/rump/modautoload/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.8 2016/10/14 16:02:35 christos Exp $ +# $NetBSD: Makefile,v 1.13 2021/07/07 11:51:45 martin Exp $ # .include @@ -14,12 +14,21 @@ # on amd64. This is the reason we keep this test in its own # subdirectory -- otherwise the LDADD lines would get a little hairy. LDFLAGS+= -Wl,-E -LDADD+= -Wl,--whole-archive ${DESTDIR}/usr/lib/librumpvfs.a \ - ${DESTDIR}/usr/lib/librump.a \ - -Wl,--no-whole-archive +.if ${MACHINE} == "alpha" +LDFLAGS+= -Wl,--no-relax +.endif +LDADD+= \ + -Wl,--whole-archive -Wl,-Bstatic \ + -lrumpvfs_nofifofs -lrumpvfs -lrump \ + -Wl,-Bdynamic -Wl,--no-whole-archive + LDADD+= -lrumpuser -lpthread DPADD+= ${LIBRUMPVFS} ${LIBRUMP} ${LIBRUMPUSER} +.if ${RUMP_SANITIZE:Uno} != "no" +LDADD+= -fsanitize=${RUMP_SANITIZE} +.endif + WARNS= 4 # To include a rump version of sysctlbyname() @@ -29,4 +38,9 @@ SRCS.t_modautoload+= sysctlgetmibinfo.c CPPFLAGS+= -DRUMP_ACTION +SANITIZER_RENAME_CLASSES+= t_modautoload +SANITIZER_RENAME_FILES.t_modautoload+= ${SRCS.t_modautoload} +SANITIZER_RENAME_SYMBOL.t_modautoload+= sysctlbyname +SANITIZER_RENAME_SYMBOL.t_modautoload+= sysctlgetmibinfo + .include diff --git a/rump/rumpkern/Makefile b/rump/rumpkern/Makefile --- a/rump/rumpkern/Makefile +++ b/rump/rumpkern/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.15 2014/06/10 04:28:40 he Exp $ +# $NetBSD: Makefile,v 1.19 2020/03/01 18:08:16 christos Exp $ .include @@ -11,20 +11,21 @@ TESTS_C+= t_modlinkset TESTS_C+= t_signals TESTS_C+= t_threads +TESTS_C+= t_threadpool TESTS_C+= t_tsleep +TESTS_C+= t_workqueue TESTS_C+= t_vm TESTS_SH= t_sp SUBDIR+= h_client h_server -ADD_TO_LD= -lrumpvfs -lrump -lrumpuser -lrump -lpthread LDADD.t_modlinkset+= -lukfs -lrumpdev_disk -lrumpdev -lrumpfs_msdos -LDADD.t_modlinkset+= -lrumpfs_cd9660 ${ADD_TO_LD} -LDADD+= ${ADD_TO_LD} +LDADD.t_modlinkset+= -lrumpfs_cd9660 ${LIBRUMPBASE} +LDADD+= ${LIBRUMPBASE} KERNSPACE != cd ${.CURDIR}/../kernspace && ${PRINTOBJDIR} -LDADD+= -L${KERNSPACE} -lkernspace +LDADD+= -L${KERNSPACE} -lkernspace -lrump WARNS= 4 diff --git a/rump/rumpkern/h_client/h_forkcli.c b/rump/rumpkern/h_client/h_forkcli.c --- a/rump/rumpkern/h_client/h_forkcli.c +++ b/rump/rumpkern/h_client/h_forkcli.c @@ -1,4 +1,4 @@ -/* $NetBSD: h_forkcli.c,v 1.1 2011/01/05 17:19:09 pooka Exp $ */ +/* $NetBSD: h_forkcli.c,v 1.2 2019/07/16 17:29:18 martin Exp $ */ #include #include @@ -24,7 +24,7 @@ if ((pid1 = rump_sys_getpid()) < 2) errx(1, "unexpected pid %d", pid1); - fd = rump_sys_open("/dev/null", O_CREAT | O_RDWR); + fd = rump_sys_open("/dev/null", O_CREAT | O_RDWR, 0600); if (rump_sys_write(fd, &fd, sizeof(fd)) != sizeof(fd)) errx(1, "write newlyopened /dev/null"); diff --git a/rump/rumpkern/h_server/Makefile b/rump/rumpkern/h_server/Makefile --- a/rump/rumpkern/h_server/Makefile +++ b/rump/rumpkern/h_server/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.5 2015/01/07 22:24:03 pooka Exp $ +# $NetBSD: Makefile,v 1.7 2020/03/01 18:08:16 christos Exp $ # .include @@ -9,7 +9,7 @@ ATFFILE= no -LDADD+= -lrumpkern_sysproxy -lrump -lrumpuser -lrump -lpthread +LDADD+= -lrumpkern_sysproxy ${LIBRUMPBASE} WARNS= 4 NOMAN= diff --git a/rump/rumpkern/t_kern.c b/rump/rumpkern/t_kern.c --- a/rump/rumpkern/t_kern.c +++ b/rump/rumpkern/t_kern.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_kern.c,v 1.4 2017/01/13 21:30:43 christos Exp $ */ +/* $NetBSD: t_kern.c,v 1.6 2020/08/28 19:29:58 martin Exp $ */ /*- * Copyright (c) 2011 The NetBSD Foundation, Inc. @@ -37,6 +37,7 @@ #include #include #include +#include #include "h_macros.h" #include "../kernspace/kernspace.h" @@ -56,6 +57,8 @@ extern const int rump_lockdebug; int pipetti[2]; int status; + ssize_t len; + regex_t preg; if (needld && !rump_lockdebug) atf_tc_skip("test requires LOCKDEBUG kernel"); @@ -76,9 +79,27 @@ if (rump_lockdebug) { char buf[8192]; - ATF_REQUIRE(read(pipetti[0], buf, sizeof(buf)) > 0); - if (strncmp(buf, expect, strlen(expect)) != 0) + len = read(pipetti[0], buf, sizeof(buf) - 1); + ATF_REQUIRE(len > 0); + buf[len] = '\0'; + /* + * We use regex matching here, since the rump + * kernel messages include routine names and line + * numbers which may not remain constant. + */ + if ((status = regcomp(&preg, expect, REG_BASIC)) != 0) { + regerror(status, &preg, buf, sizeof(buf)); + printf("regcomp error: %s\n", buf); + atf_tc_fail("regcomp failed"); + } + if ((status = regexec(&preg, buf, 0, NULL, 0)) != 0) { + printf("expected: \"%s\"\n", expect); + printf("received: \"%s\"\n", buf); + regerror(status, &preg, buf, sizeof(buf)); + printf("regexec error: %s\n", buf); atf_tc_fail("unexpected output"); + } + regfree(&preg); } break; case -1: @@ -87,21 +108,21 @@ } LOCKFUN(DESTROYHELD, "destroy lock while held", 0, - "mutex error: lockdebug_free: is locked or in use"); + "mutex error: mutex_destroy,.*: is locked or in use"); LOCKFUN(DOUBLEFREE, "free lock twice", 0, - "panic: lockdebug_lookup: uninitialized lock"); + "panic: mutex_destroy,.*: uninitialized lock"); LOCKFUN(DOUBLEINIT, "init lock twice", 1, - "mutex error: lockdebug_alloc: already initialized"); + "mutex error: .*mutex_init,.*: already initialized"); LOCKFUN(MEMFREE, "free memory active lock is in", 1, - "mutex error: kmem_intr_free: allocation contains active lock"); + "mutex error: kmem_intr_free,.*: allocation contains active lock"); LOCKFUN(MTX, "locking-against-self mutex", 0, - "mutex error: lockdebug_wantlock: locking against myself"); + "mutex error: mutex_enter,.*: locking against myself"); LOCKFUN(RWDOUBLEX, "locking-against-self exclusive rwlock", 0, - "rwlock error: lockdebug_wantlock: locking against myself"); + "rwlock error: rw_enter,.*: locking against myself"); LOCKFUN(RWRX, "rw: first shared, then exclusive", 1, - "rwlock error: lockdebug_wantlock: locking against myself"); + "rwlock error: rw_enter,.*: locking against myself"); LOCKFUN(RWXR, "rw: first execusive, then shared", 0, - "rwlock error: lockdebug_wantlock: locking against myself"); + "rwlock error: rw_enter,.*: locking against myself"); ATF_TP_ADD_TCS(tp) { diff --git a/rump/rumpkern/t_lwproc.c b/rump/rumpkern/t_lwproc.c --- a/rump/rumpkern/t_lwproc.c +++ b/rump/rumpkern/t_lwproc.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_lwproc.c,v 1.9 2017/01/13 21:30:43 christos Exp $ */ +/* $NetBSD: t_lwproc.c,v 1.10 2020/01/08 17:38:43 ad Exp $ */ /* * Copyright (c) 2010 The NetBSD Foundation, Inc. @@ -239,7 +239,7 @@ RZ(rump_pub_lwproc_newlwp(0)); l = rump_pub_lwproc_curlwp(); rump_pub_lwproc_switch(NULL); - /* if remains LP_RUNNING, next call will panic */ + /* if remains LW_RUNNING, next call will panic */ rump_pub_lwproc_switch(l); } diff --git a/rump/rumpkern/t_sp.sh b/rump/rumpkern/t_sp.sh old mode 100755 new mode 100644 --- a/rump/rumpkern/t_sp.sh +++ b/rump/rumpkern/t_sp.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_sp.sh,v 1.13 2016/08/10 23:47:14 kre Exp $ +# $NetBSD: t_sp.sh,v 1.17 2020/09/01 18:40:09 gson Exp $ # # Copyright (c) 2010 The NetBSD Foundation, Inc. # All rights reserved. @@ -40,16 +40,31 @@ }" } +test_case_skip() +{ + local name="${1}"; shift + local pr="${1}"; shift + local msg="${1}"; shift + + atf_test_case "${name}" + eval "${name}_head() { }" + eval "${name}_body() { atf_skip "'"'"PR ${pr}: ${msg}"'"'"; }" +} + test_case basic basic -test_case stress_short stress 1 -test_case stress_long stress 2 -test_case stress_killer stress 5 kill +# test_case stress_short stress 1 +test_case_skip stress_short kern/50350 "fails after insane long time" +# test_case stress_long stress 2 +test_case_skip stress_long kern/50350 "leftover rump_server" +# test_case stress_killer stress 5 kill +test_case_skip stress_killer kern/55356 "leftover rump_server" test_case fork_simple fork simple test_case fork_pipecomm fork pipecomm test_case fork_fakeauth fork fakeauth test_case sigsafe sigsafe sigsafe test_case signal signal -test_case reconnect reconnect +# test_case reconnect reconnect +test_case_skip reconnect kern/55304 "leftover rump_server" basic() { @@ -73,7 +88,7 @@ export RUMP_SERVER=unix://commsock atf_check -s exit:0 rump_server \ - -lrumpvfs -lrumpnet -lrumpnet_net -lrumpnet_netinet -lrumpdev \ + -lrumpvfs -lrumpnet -lrumpnet_net -lrumpnet_netinet \ ${RUMP_SERVER} atf_check -s exit:0 -e ignore $(atf_get_srcdir)/h_client/h_stresscli $@ } @@ -82,7 +97,7 @@ { export RUMP_SERVER=unix://commsock - atf_check -s exit:0 rump_server -lrumpvfs -lrumpdev ${RUMP_SERVER} + atf_check -s exit:0 rump_server -lrumpvfs ${RUMP_SERVER} atf_check -s exit:0 $(atf_get_srcdir)/h_client/h_forkcli ${1} } diff --git a/rump/rumpkern/t_threadpool.c b/rump/rumpkern/t_threadpool.c new file mode 100644 --- /dev/null +++ b/rump/rumpkern/t_threadpool.c @@ -0,0 +1,157 @@ +/* $NetBSD: t_threadpool.c,v 1.2 2018/12/28 19:54:36 thorpej Exp $ */ + +/*- + * Copyright (c) 2018 The NetBSD Foundation, Inc. + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +#include +#include + +#include + +#include + +#include "h_macros.h" +#include "../kernspace/kernspace.h" + +ATF_TC(threadpool_unbound_lifecycle); +ATF_TC_HEAD(threadpool_unbound_lifecycle, tc) +{ + + atf_tc_set_md_var(tc, "descr", "Tests unbound threadpool lifecycle"); +} + +ATF_TC_BODY(threadpool_unbound_lifecycle, tc) +{ + + rump_init(); + + rump_schedule(); + rumptest_threadpool_unbound_lifecycle(); /* panics if fails */ + rump_unschedule(); +} + +ATF_TC(threadpool_percpu_lifecycle); +ATF_TC_HEAD(threadpool_percpu_lifecycle, tc) +{ + + atf_tc_set_md_var(tc, "descr", "Tests percpu threadpool lifecycle"); +} + +ATF_TC_BODY(threadpool_percpu_lifecycle, tc) +{ + + rump_init(); + + rump_schedule(); + rumptest_threadpool_percpu_lifecycle(); /* panics if fails */ + rump_unschedule(); +} + +ATF_TC(threadpool_unbound_schedule); +ATF_TC_HEAD(threadpool_unbound_schedule, tc) +{ + + atf_tc_set_md_var(tc, "descr", + "Tests scheduling on unbound threadpools"); +} + +ATF_TC_BODY(threadpool_unbound_schedule, tc) +{ + + rump_init(); + + rump_schedule(); + rumptest_threadpool_unbound_schedule(); /* panics if fails */ + rump_unschedule(); +} + +ATF_TC(threadpool_percpu_schedule); +ATF_TC_HEAD(threadpool_percpu_schedule, tc) +{ + + atf_tc_set_md_var(tc, "descr", + "Tests scheduling on percpu threadpools"); +} + +ATF_TC_BODY(threadpool_percpu_schedule, tc) +{ + + rump_init(); + + rump_schedule(); + rumptest_threadpool_percpu_schedule(); /* panics if fails */ + rump_unschedule(); +} + +ATF_TC(threadpool_job_cancel); +ATF_TC_HEAD(threadpool_job_cancel, tc) +{ + + atf_tc_set_md_var(tc, "descr", + "Tests synchronizing with job cancellation"); +} + +ATF_TC_BODY(threadpool_job_cancel, tc) +{ + + rump_init(); + + rump_schedule(); + rumptest_threadpool_job_cancel(); /* panics if fails */ + rump_unschedule(); +} + +ATF_TC(threadpool_job_cancelthrash); +ATF_TC_HEAD(threadpool_job_cancelthrash, tc) +{ + + atf_tc_set_md_var(tc, "descr", + "Tests thrashing job scheduling / cancellation"); +} + +ATF_TC_BODY(threadpool_job_cancelthrash, tc) +{ + + rump_init(); + + rump_schedule(); + rumptest_threadpool_job_cancelthrash(); /* panics if fails */ + rump_unschedule(); +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, threadpool_unbound_lifecycle); + ATF_TP_ADD_TC(tp, threadpool_percpu_lifecycle); + ATF_TP_ADD_TC(tp, threadpool_unbound_schedule); + ATF_TP_ADD_TC(tp, threadpool_percpu_schedule); + ATF_TP_ADD_TC(tp, threadpool_job_cancel); + ATF_TP_ADD_TC(tp, threadpool_job_cancelthrash); + + return atf_no_error(); +} diff --git a/rump/rumpkern/t_threads.c b/rump/rumpkern/t_threads.c --- a/rump/rumpkern/t_threads.c +++ b/rump/rumpkern/t_threads.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_threads.c,v 1.2 2017/01/13 21:30:43 christos Exp $ */ +/* $NetBSD: t_threads.c,v 1.3 2017/09/29 10:22:36 maya Exp $ */ /*- * Copyright (c) 2010 The NetBSD Foundation, Inc. @@ -68,7 +68,7 @@ rump_init(); rump_schedule(); - rumptest_threadjoin(); /* panics if fails */ + rumptest_thread(); /* panics if fails */ rump_unschedule(); } diff --git a/rump/rumpkern/t_vm.c b/rump/rumpkern/t_vm.c --- a/rump/rumpkern/t_vm.c +++ b/rump/rumpkern/t_vm.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_vm.c,v 1.4 2017/01/13 21:30:43 christos Exp $ */ +/* $NetBSD: t_vm.c,v 1.6 2021/01/22 22:03:01 chs Exp $ */ /*- * Copyright (c) 2010 The NetBSD Foundation, Inc. @@ -50,6 +50,10 @@ ATF_TC_BODY(busypage, tc) { +#if 0 + atf_tc_expect_fail("test bug: unbusies an uninitialized page"); +#endif + atf_tc_skip("this test is buggy and hits an assertion, but atf doesn't provide any way to expect that a test program crashes, this all we can do is skip"); rump_init(); rump_schedule(); diff --git a/rump/rumpkern/t_workqueue.c b/rump/rumpkern/t_workqueue.c new file mode 100644 --- /dev/null +++ b/rump/rumpkern/t_workqueue.c @@ -0,0 +1,81 @@ +/* $NetBSD: t_workqueue.c,v 1.2 2017/12/28 07:10:26 ozaki-r Exp $ */ + +/*- + * Copyright (c) 2017 The NetBSD Foundation, Inc. + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +#include +#include + +#include + +#include + +#include "h_macros.h" +#include "../kernspace/kernspace.h" + +ATF_TC(workqueue1); +ATF_TC_HEAD(workqueue1, tc) +{ + + atf_tc_set_md_var(tc, "descr", "Checks workqueue basics"); +} + +ATF_TC_BODY(workqueue1, tc) +{ + + rump_init(); + + rump_schedule(); + rumptest_workqueue1(); /* panics if fails */ + rump_unschedule(); +} + +ATF_TC(workqueue_wait); +ATF_TC_HEAD(workqueue_wait, tc) +{ + + atf_tc_set_md_var(tc, "descr", "Checks workqueue_wait"); +} + +ATF_TC_BODY(workqueue_wait, tc) +{ + + rump_init(); + + rump_schedule(); + rumptest_workqueue_wait(); /* panics if fails */ + rump_unschedule(); +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, workqueue1); + ATF_TP_ADD_TC(tp, workqueue_wait); + + return atf_no_error(); +} diff --git a/rump/rumpnet/t_shmif.sh b/rump/rumpnet/t_shmif.sh old mode 100755 new mode 100644 --- a/rump/rumpnet/t_shmif.sh +++ b/rump/rumpnet/t_shmif.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_shmif.sh,v 1.3 2016/08/10 23:49:03 kre Exp $ +# $NetBSD: t_shmif.sh,v 1.5 2020/09/09 09:17:14 gson Exp $ # # Copyright (c) 2011 The NetBSD Foundation, Inc. # All rights reserved. @@ -39,7 +39,7 @@ export RUMP_SERVER=unix://sock${1} atf_check -s exit:0 rump_server -lrumpnet -lrumpnet_net \ - -lrumpnet_netinet -lrumpnet_shmif -lrumpdev ${RUMP_SERVER} + -lrumpnet_netinet -lrumpnet_shmif ${RUMP_SERVER} atf_check -s exit:0 rump.ifconfig shmif0 create atf_check -s exit:0 rump.ifconfig shmif0 linkstr shmbus atf_check -s exit:0 rump.ifconfig shmif0 inet 1.1.1.${1} @@ -61,7 +61,7 @@ do [ ${y} -eq ${x} ] && continue atf_check -s exit:0 -o ignore -e ignore \ - rump.ping -c 1 1.1.1.${y} + rump.ping -n -c 1 1.1.1.${y} done done } diff --git a/rump/rumpvfs/Makefile b/rump/rumpvfs/Makefile --- a/rump/rumpvfs/Makefile +++ b/rump/rumpvfs/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.4 2014/06/10 04:28:40 he Exp $ +# $NetBSD: Makefile,v 1.5 2020/03/01 18:08:16 christos Exp $ .include @@ -8,7 +8,7 @@ TESTS_C+= t_etfs TESTS_C+= t_p2kifs -LDADD= -lrumpvfs -lrump -lrumpuser -lrump -lpthread +LDADD= ${LIBRUMPBASE} WARNS= 4 diff --git a/sbin/Makefile b/sbin/Makefile --- a/sbin/Makefile +++ b/sbin/Makefile @@ -1,10 +1,12 @@ -# $NetBSD: Makefile,v 1.7 2015/12/04 17:20:38 christos Exp $ +# $NetBSD: Makefile,v 1.9 2020/06/25 17:08:32 jruoho Exp $ # .include TESTSDIR= ${TESTSBASE}/sbin -TESTS_SUBDIRS+= fsck_ffs gpt ifconfig newfs newfs_msdos resize_ffs route sysctl +TESTS_SUBDIRS+= envstat fsck_ffs gpt ifconfig \ + newfs newfs_msdos resize_ffs \ + route setkey sysctl .include diff --git a/sbin/envstat/Makefile b/sbin/envstat/Makefile new file mode 100644 --- /dev/null +++ b/sbin/envstat/Makefile @@ -0,0 +1,8 @@ +# $NetBSD: Makefile,v 1.1 2020/06/25 15:01:35 jruoho Exp $ + +.include + +TESTSDIR= ${TESTSBASE}/sbin/envstat +TESTS_SH= t_envstat + +.include diff --git a/sbin/envstat/t_envstat.sh b/sbin/envstat/t_envstat.sh new file mode 100644 --- /dev/null +++ b/sbin/envstat/t_envstat.sh @@ -0,0 +1,67 @@ +# $NetBSD: t_envstat.sh,v 1.1 2020/06/25 15:01:35 jruoho Exp $ +# +# Copyright (c) 2020 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Jukka Ruohonen. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +atf_test_case zerotemp +zerotemp_head() { + atf_set "descr" "Check with envstat(8) that CPU sensors " \ + "do not show zero temperatures (PR kern/53410)" +} + +zerotemp_body() { + + devices="amdtemp0 coretemp0 acpitz0" # XXX: What else? + + for dev in $devices; do + + envstat -d $dev >/dev/null 2>&1 + + if [ ! $? -eq 0 ]; then + echo "Skipping non-existent $dev" + continue + fi + + if [ $dev = "amdtemp0" ]; then + atf_expect_fail "PR kern/53410" + fi + + tempf=$(envstat -d $dev | awk '/Current/{getline;print $3}') + tempi=$(printf "%.0f" $tempf) + + echo "$dev = $tempf =~ $tempi" + + if [ $tempi -eq 0 ]; then + atf_fail "Zero-temperature from $dev" + fi + done +} + +atf_init_test_cases() { + atf_add_test_case zerotemp +} diff --git a/sbin/fsck_ffs/quotas_common.sh b/sbin/fsck_ffs/quotas_common.sh old mode 100755 new mode 100644 diff --git a/sbin/fsck_ffs/t_check_quotas.sh b/sbin/fsck_ffs/t_check_quotas.sh old mode 100755 new mode 100644 diff --git a/sbin/fsck_ffs/t_enable_quotas.sh b/sbin/fsck_ffs/t_enable_quotas.sh old mode 100755 new mode 100644 diff --git a/sbin/gpt/t_gpt.sh b/sbin/gpt/t_gpt.sh old mode 100755 new mode 100644 --- a/sbin/gpt/t_gpt.sh +++ b/sbin/gpt/t_gpt.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_gpt.sh,v 1.15 2016/03/08 08:04:48 joerg Exp $ +# $NetBSD: t_gpt.sh,v 1.16 2017/03/22 19:13:40 martin Exp $ # # Copyright (c) 2015 The NetBSD Foundation, Inc. # All rights reserved. @@ -293,6 +293,10 @@ } migrate_disklabel_body() { + if [ $( sysctl -n kern.rawpartition ) -ne 3 ]; then + atf_skip "This test is specific to architectures using MBR" + fi + prepare silence fdisk -fi "$disk" silence fdisk -fu0s "169/63/$((size / 10))" "$disk" diff --git a/sbin/ifconfig/Makefile b/sbin/ifconfig/Makefile --- a/sbin/ifconfig/Makefile +++ b/sbin/ifconfig/Makefile @@ -1,9 +1,18 @@ -# $NetBSD: Makefile,v 1.1 2011/05/03 06:13:06 jruoho Exp $ +# $NetBSD: Makefile,v 1.9 2020/06/30 11:48:20 jruoho Exp $ .include TESTSDIR= ${TESTSBASE}/sbin/ifconfig -TESTS_SH= t_nonexistent +TESTS_SH= t_bridge \ + t_capabilities \ + t_nonexistent \ + t_random_garbage \ + t_repeated_link_addr \ + t_repeated_mtu \ + t_repeated_scan \ + t_repeated_updown \ + t_tap \ + t_woptions .include diff --git a/sbin/ifconfig/t_bridge.sh b/sbin/ifconfig/t_bridge.sh new file mode 100644 --- /dev/null +++ b/sbin/ifconfig/t_bridge.sh @@ -0,0 +1,72 @@ +# $NetBSD: t_bridge.sh,v 1.1 2020/06/25 18:30:42 jruoho Exp $ +# +# Copyright (c) 2020 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Jukka Ruohonen. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# +bridges="/tmp/bridges" + +atf_test_case manybridges cleanup +manybridges_head() { + atf_set "require.user" "root" + atf_set "descr" "Test creating many, many bridge(4)'s" +} + +manybridges_body() { + + seq 65535 65000 > $bridges # Try to avoid stalling automated runs. + + while read bridge; do + + ifconfig "bridge$bridge" >/dev/null 2>&1 + + if [ $? -eq 0 ]; then + echo "Skipping existing bridge$bridge" + continue + fi + + ifconfig "bridge$bridge" create + echo "Created bridge$bridge" + + done < $bridges +} + +manybridges_cleanup() { + + if [ -f $bridges ]; then + + while read bridge; do + ifconfig "bridge$bridge" destroy >/dev/null 2>&1 + echo "Burnt down bridge$bridge" + done < $bridges + + rm $bridges + fi +} + +atf_init_test_cases() { + atf_add_test_case manybridges +} diff --git a/sbin/ifconfig/t_capabilities.sh b/sbin/ifconfig/t_capabilities.sh new file mode 100644 --- /dev/null +++ b/sbin/ifconfig/t_capabilities.sh @@ -0,0 +1,159 @@ +# $NetBSD: t_capabilities.sh,v 1.1 2020/06/27 06:57:44 jruoho Exp $ +# +# Copyright (c) 2020 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Jukka Ruohonen. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# +setcap() { + + echo "Request: $1 $2 ($3)" + ifconfig $1 $2 + + if [ $4 -eq 1 ]; then + + if [ ! $? -eq 0 ]; then + atf_fail "Failed to enable $3 for $1" + fi + + if [ -z "$(ifconfig $1 | grep "enabled" | grep $3)" ]; then + atf_fail "Failed to enable $3 for $1" + fi + + echo "Request: $1 -$2 (-$3)" + ifconfig $1 -$2 + + if [ ! $? -eq 0 ]; then + atf_fail "Failed to disable $3 for $1" + fi + + if [ ! -z "$(ifconfig $1 | grep "enabled" | grep $3)" ]; then + atf_fail "Failed to disable $3 for $1" + fi + fi +} + +parsecap() { + + i=$1 + checkrv=$3 + x=$(echo $2 | sed "s/.*//") + + export IFS="," + + for y in $x; do + + z="" + + if [ $y = "TSO4" ]; then + z="tso4" + elif [ $y = "IP4CSUM_Rx" ]; then + z="ip4csum-rx" + elif [ $y = "IP4CSUM_Tx" ]; then + z="ip4csum-tx" + elif [ $y = "TCP4CSUM_Rx" ]; then + z="tcp4csum-rx" + elif [ $y = "TCP4CSUM_Tx" ]; then + z="tcp4csum-tx" + elif [ $y = "UDP4CSUM_Rx" ]; then + z="udp4csum-rx" + elif [ $y = "UDP4CSUM_Tx" ]; then + z="udp4csum-tx" + elif [ $y = "TCP6CSUM_Rx" ]; then + z="tcp6csum-rx" + elif [ $y = "TCP6CSUM_Tx" ]; then + z="tcp6csum-tx" + elif [ $y = "UDP6CSUM_Rx" ]; then + z="udp6csum-rx" + elif [ $y = "UDP6CSUM_Tx" ]; then + z="udp6csum-tx" + elif [ $y = "TSO6" ]; then + z="tso6" + fi + + if [ -z $z ]; then + echo "Skipping unrecognized $y for $i" + else + setcap $i $z $y $checkrv + fi + done + + unset IFS +} + +atf_test_case basic +basic_head() { + atf_set "require.user" "root" + atf_set "descr" "Test setting interface capabilities" +} + +basic_body() { + + for i in $(ifconfig -l); do + + c=$(ifconfig $i | grep "capabilities") + + if [ -z "$c" ]; then + echo "Skipping $i, no capabilities" + continue + fi + + if [ ! -z "$(echo $i | grep ixg)" ]; then + echo "Skipping $i (PR kern/50933)" + continue + fi + + enabled=$(ifconfig $i | grep "enabled") + + for l in $c; do + + if [ ! -z "$(echo $l | grep "ec_capabilities")" ]; then + continue + fi + + parsecap $i $l 1 + done + + if [ ! -z "$enabled" ]; then + + echo "Restoring capabilities for $i" + + for l in $enabled; do + + ec=$(echo $l | grep "ec_enabled") + + if [ ! -z "$ec" ]; then + continue + fi + + parsecap $i $l 0 + done + fi + done +} + +atf_init_test_cases() { + atf_add_test_case basic +} diff --git a/sbin/ifconfig/t_nonexistent.sh b/sbin/ifconfig/t_nonexistent.sh old mode 100755 new mode 100644 diff --git a/sbin/ifconfig/t_random_garbage.sh b/sbin/ifconfig/t_random_garbage.sh new file mode 100644 --- /dev/null +++ b/sbin/ifconfig/t_random_garbage.sh @@ -0,0 +1,97 @@ +# $NetBSD: t_random_garbage.sh,v 1.4 2020/07/27 07:36:19 jruoho Exp $ +# +# Copyright (c) 2020 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Jukka Ruohonen. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# +getrint() { + echo $(od -An -N2 -i /dev/urandom | sed 's/ //') +} + +getrstr() { + echo $(cat /dev/urandom | head -n 1 | base64) +} + +write_garbage() { + val=$(getrint) + echo "Test $3: write to $1 opt $2 -> $val" + ifconfig $1 $2 $val >/dev/null 2>&1 + val=$(getrstr) + echo "Test $3: write to $1 opt $2 -> $val" + ifconfig $1 $2 $val >/dev/null 2>&1 +} + +atf_test_case random_garbage +random_garbage_head() { + atf_set "require.user" "root" + atf_set "descr" "Test writing random garbage to " \ + "ifconfig(8) options (PR kern/55451)" +} + +random_garbage_body() { + + # Please note: + # + # 1. As some drivers seem to have input validation issues, it may + # be possible that horrors are written directly to the hardware. + # + # 2. Even if the test passes, there is no easy way to restore + # the existing state/configuration of any given interface. + # + # Take care. + # + if ! [ $(atf_config_get "run_unsafe" "no") = "yes" ]; then + atf_skip "The test is not safe (PR kern/55451)" + fi + + opts="advbase advskew broadcast carpdev description \ + media mediaopt -mediaopt mode instance metric mtu \ + netmask state frag rts ssid nwid nwkey pass powersavesleep \ + bssid chan tunnel session cookie pltime prefixlen linkstr \ + vhid vlan vlanif -vlanif agrport -agrport vltime maxupd \ + syncdev syncpeer" + + for i in $(ifconfig -l); do + + ifconfig $i up + + for o in $opts; do + + n=10 + + while [ $n -gt 0 ]; do + write_garbage $i $o $n + n=$(expr $n - 1) + done + done + done + + atf_pass +} + +atf_init_test_cases() { + atf_add_test_case random_garbage +} diff --git a/sbin/ifconfig/t_repeated_link_addr.sh b/sbin/ifconfig/t_repeated_link_addr.sh new file mode 100644 --- /dev/null +++ b/sbin/ifconfig/t_repeated_link_addr.sh @@ -0,0 +1,125 @@ +# $NetBSD: t_repeated_link_addr.sh,v 1.4 2020/07/27 16:57:44 gson Exp $ +# +# Copyright (c) 2020 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Jukka Ruohonen. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# +atf_test_case repeated_link_addr +repeated_link_addr_head() { + atf_set "require.user" "root" + atf_set "descr" "Check with ifconfig(8) that " \ + "setting link addresses for active interfaces " \ + "does not cause a panic (PR kern/41912)" +} + +repeated_link_addr_body() { + + if ! [ $(atf_config_get "run_unsafe" "no") = "yes" ] + then + atf_skip "can disrupt networking; also PR port-evbarm/55521" + fi + + fail=0 + addrs="00:11:00:00:00:00 \ + 00:11:11:00:00:00 \ + 00:11:11:11:00:00 \ + 00:11:11:11:11:00 \ + 00:00:11:00:00:00 \ + 00:00:11:11:00:00 \ + 00:00:11:11:11:00 \ + 00:00:00:11:00:00 \ + 00:00:00:11:11:00" + + pkill -9 hostapd + pkill -9 wpa_supplicant + + for i in $(ifconfig -l); do + + state="up" + addr=$(ifconfig $i | grep "address") + addr=$(echo $addr | sed "s/address: //" | sed 's/ //') + + if [ -z "$addr" ]; then + echo "Skipping $i" + continue + fi + + ifconfig -s $i + + if [ $? -eq 1 ]; then + state="down" + ifconfig $i up + sleep 1 + fi + + for j in $addrs; do + + echo "Request: ifconfig $i link $j active" + ifconfig $i link $j active + + if [ ! $? -eq 0 ]; then + fail=1 + fi + + if [ -z "$(ifconfig $i | grep $j)" ]; then + fail=1 + fi + + sleep 0.5 + done + + # From the ifconfig(8) manual page: + # + # "You may not delete the active address from an interface. + # You must activate some other address, first." + # + ifconfig $i link $addr active + echo "Restored the link address of $i to $addr" + sleep 0.5 + + for j in $addrs; do + echo "Request: ifconfig $i link $j delete" + ifconfig $i link $j delete + sleep 0.5 + done + + ifconfig $i $state + echo "Restored state of $i to $state" + done + + /bin/sh /etc/rc.d/hostapd restart >/dev/null 2>&1 + /bin/sh /etc/rc.d/wpa_supplicant restart >/dev/null 2>&1 + + if [ $fail -eq 1 ]; then + atf_fail "Failed to set link addresses" + fi + + atf_pass +} + +atf_init_test_cases() { + atf_add_test_case repeated_link_addr +} diff --git a/sbin/ifconfig/t_repeated_mtu.sh b/sbin/ifconfig/t_repeated_mtu.sh new file mode 100644 --- /dev/null +++ b/sbin/ifconfig/t_repeated_mtu.sh @@ -0,0 +1,70 @@ +# $NetBSD: t_repeated_mtu.sh,v 1.2 2020/12/10 08:16:59 mrg Exp $ +# +# Copyright (c) 2020 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Jukka Ruohonen. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +atf_test_case repeated_mtu +repeated_mtu_head() { + atf_set "require.user" "root" + atf_set "descr" "Check with ifconfig(8) that setting MTUs works" +} + +repeated_mtu_body() { + + if ! [ $(atf_config_get "run_unsafe" "no") = "yes" ] + then + atf_skip "can disrupt networking; also PR port-evbarm/55521" + fi + + # This sequence covers both valid and invalid MTUs; we are + # only interested in testing that the system does not hang. + # + mtus=$(seq -10000 1000 90000) + + for i in $(ifconfig -l); do + + mtu=$(ifconfig $i | grep "mtu" | sed "s/.*mtu //") + + if [ -z $mtu ]; then + echo "Skipping $i" + continue + fi + + for j in $mtus; do + echo "Request: ifconfig $i mtu $j" + ifconfig $i mtu $j >/dev/null 2>&1 + done + + ifconfig $i mtu $mtu + echo "Restored the MTU of $i to $mtu" + done +} + +atf_init_test_cases() { + atf_add_test_case repeated_mtu +} diff --git a/sbin/ifconfig/t_repeated_scan.sh b/sbin/ifconfig/t_repeated_scan.sh new file mode 100644 --- /dev/null +++ b/sbin/ifconfig/t_repeated_scan.sh @@ -0,0 +1,95 @@ +# $NetBSD: t_repeated_scan.sh,v 1.6 2020/12/10 08:16:59 mrg Exp $ +# +# Copyright (c) 2020 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Jukka Ruohonen. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + +atf_test_case repeated_scan +repeated_scan_head() { + atf_set "require.user" "root" + atf_set "descr" "Test with ifconfig(8) that repeated 802.11 " \ + "scanning does not panic (PR kern/53860; PR kern/55389)" +} + +repeated_scan_body() { + + if ! [ $(atf_config_get "run_unsafe" "no") = "yes" ] + then + atf_skip "can disrupt networking; also PR port-evbarm/55521" + fi + + # Even though this should amount to a "few minutes", + # try to still avoid stalling any automated test runs. + # + n=15 + + pkill -9 hostapd + pkill -9 wpa_supplicant + + for i in $(ifconfig -l); do + + state="up" + ifconfig -s $i + + if [ $? -eq 1 ]; then + + state="down" + ifconfig $i up + + if [ ! $? -eq 0 ]; then + echo "Failed to set $i up" + continue + fi + fi + + sleep 1 + ifconfig $i list scan >/dev/null 2>&1 + + if [ ! $? -eq 0 ]; then + echo "Skipping $i; scan not supported" + continue + fi + + while [ $n -gt 0 ]; do + echo "Test $n for $i" + ifconfig $i list scan >/dev/null 2>&1 + n=$(expr $n - 1) + done + + ifconfig $i $state + echo "Restored state of $i to $state" + sleep 1 + done + + /bin/sh /etc/rc.d/hostapd restart >/dev/null 2>&1 + /bin/sh /etc/rc.d/wpa_supplicant restart >/dev/null 2>&1 + + atf_pass +} + +atf_init_test_cases() { + atf_add_test_case repeated_scan +} diff --git a/sbin/ifconfig/t_repeated_updown.sh b/sbin/ifconfig/t_repeated_updown.sh new file mode 100644 --- /dev/null +++ b/sbin/ifconfig/t_repeated_updown.sh @@ -0,0 +1,77 @@ +# $NetBSD: t_repeated_updown.sh,v 1.5 2020/07/27 06:52:48 gson Exp $ +# +# Copyright (c) 2020 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Jukka Ruohonen. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + +atf_test_case repeated_updown +repeated_updown_head() { + atf_set "require.user" "root" + atf_set "descr" "Test with ifconfig(8) that repeated up/down " \ + "does not cause a panic (PR kern/52526; PR kern/52771)" +} + +repeated_updown_body() { + + if ! [ $(atf_config_get "run_unsafe" "no") = "yes" ] + then + atf_skip "can disrupt networking; also PR port-evbarm/55504" + fi + + # Try to avoid stalling any automated test runs. + # + n=35 + + for i in $(ifconfig -l); do + + state="up" + ifconfig -s $i + + if [ $? -eq 1 ]; then + state="down" + ifconfig $i up + echo "Initialized $i up" + fi + + while [ $n -gt 0 ]; do + ifconfig $i down + echo "Test $n: $i down" + ifconfig $i up + echo "Test $n: $i up" + n=$(expr $n - 1) + done + + ifconfig $i $state + echo "Restored state of $i to $state" + sleep 1 + done + + atf_pass +} + +atf_init_test_cases() { + atf_add_test_case repeated_updown +} diff --git a/sbin/ifconfig/t_tap.sh b/sbin/ifconfig/t_tap.sh new file mode 100644 --- /dev/null +++ b/sbin/ifconfig/t_tap.sh @@ -0,0 +1,101 @@ +# $NetBSD: t_tap.sh,v 1.5 2020/06/25 18:30:42 jruoho Exp $ +# +# Copyright (c) 2020 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Jukka Ruohonen. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# +taps="/tmp/taps" + +atf_test_case manytaps cleanup +manytaps_head() { + atf_set "require.user" "root" + atf_set "descr" "Test creating many, many tap(4)'s (PR kern/55417)" +} + +manytaps_body() { + + atf_skip "The test causes a panic (PR kern/55417)" + seq 65535 65000 > $taps # Try to avoid stalling automated runs. + + while read tap; do + + ifconfig "tap$tap" + + if [ $? -eq 0 ]; then + echo "Skipping existing tap$tap" + continue + fi + + ifconfig "tap$tap" create + echo "Created tap$tap" + + done < $taps +} + +manytaps_cleanup() { + + if [ -f $taps ]; then + + while read tap; do + + ifconfig "tap$tap" + + if [ $? -eq 0 ]; then + ifconfig "tap$tap" destroy + echo "Destroyed tap$tap" + fi + + done < $taps + + rm $taps + fi +} + +atf_test_case overflow cleanup +overflow_head() { + atf_set "require.user" "root" + atf_set "descr" "Test creating a tap(4) with a " \ + "negative device number (PR kern/53546)" +} + +overflow_body() { + atf_skip "The test causes a panic (PR kern/53546)" + ifconfig tap99999 create +} + +overflow_cleanup() { + + ifconfig tap99999 + + if [ $? -eq 0 ]; then + ifconfig tap99999 destroy + fi +} + +atf_init_test_cases() { + atf_add_test_case manytaps + atf_add_test_case overflow +} diff --git a/sbin/ifconfig/t_woptions.sh b/sbin/ifconfig/t_woptions.sh new file mode 100644 --- /dev/null +++ b/sbin/ifconfig/t_woptions.sh @@ -0,0 +1,219 @@ +# $NetBSD: t_woptions.sh,v 1.2 2020/09/08 06:11:32 mrg Exp $ +# +# Copyright (c) 2020 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Jukka Ruohonen. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + +# These tests play around with the wifi configuration on a system +# which may not be safe, destroy configuration or hang. +check_ifconfig_tests_enabled() { + if [ "${ATF_SBIN_IFCONFIG_WIFI_ENABLE}" != "yes" ]; then + atf_skip "Test triggers real device activity and may destroy configuration or hang." + fi +} + +atf_test_case chan +chan_head() { + atf_set "require.user" "root" + atf_set "descr" "Test with ifconfig(8) that setting " \ + "802.11 channels does not panic (PR kern/55424)" +} + +chan_body() { + check_ifconfig_tests_enabled + + # This sequence covers both valid and invalid channels. + # Different 802.11 modes are not taken into account, and + # all interfaces are tested, including non-802.11 ones. + # + chans=$(seq 1 500) + + pkill -9 hostapd + pkill -9 wpa_supplicant + + for i in $(ifconfig -l); do + + if [ ! -z "$(echo $i | grep urtwn)" ]; then + echo "Skipping $i (PR kern/55424)" + continue + fi + + state="up" + ifconfig -s $i + + if [ $? -eq 1 ]; then + state="down" + fi + + m="" + mm=$(ifconfig $i | grep "chan") + + if [ ! -z "$mm" ]; then + m=$(echo $mm | awk {'print $2'}) + fi + + for j in $chans; do + echo "Request: $i -> 802.11 chan $j" + ifconfig $i chan $j >/dev/null 2>&1 + echo "Request: $i -> 802.11 -chan $j" + ifconfig $i -chan $j >/dev/null 2>&1 + done + + if [ ! -z $m ]; then + ifconfig $i chan $m >/dev/null 2>&1 + echo "Restored the channel of $i to $m" + fi + + ifconfig $i $state + echo "Restored state of $i to $state" + sleep 1 + done + + /bin/sh /etc/rc.d/hostapd restart >/dev/null 2>&1 + /bin/sh /etc/rc.d/wpa_supplicant restart >/dev/null 2>&1 + + atf_pass +} + +atf_test_case mediaopt +mediaopt_head() { + atf_set "require.user" "root" + atf_set "descr" "Test with ifconfig(8) that setting " \ + "802.11 media options does not panic (PR kern/35045)" +} + +mediaopt_body() { + check_ifconfig_tests_enabled + + # Again, also non-802.11 interfaces are tested. + # + opts="adhoc monitor hostap" + + pkill -9 hostapd + pkill -9 wpa_supplicant + + for i in $(ifconfig -l); do + + state="up" + ifconfig -s $i + + if [ $? -eq 1 ]; then + state="down" + fi + + m="" + mm=$(ifconfig $i | grep "media") + + for j in $opts; do + + match=$(echo $mm | grep $j) + + if [ ! -z "$match" ]; then + m=$j + break + fi + done + + for j in $opts; do + echo "Request: $i -> 802.11 mediaopt $j" + ifconfig $i mediaopt $j >/dev/null 2>&1 + echo "Request: $i -> 802.11 -mediaopt $j" + ifconfig $i -mediaopt $j >/dev/null 2>&1 + done + + if [ ! -z $m ]; then + ifconfig $i mode $m >/dev/null 2>&1 + echo "Restored the mediaopt of $i to $m" + fi + + ifconfig $i $state + echo "Restored state of $i to $state" + sleep 1 + done + + /bin/sh /etc/rc.d/hostapd restart >/dev/null 2>&1 + /bin/sh /etc/rc.d/wpa_supplicant restart >/dev/null 2>&1 + + atf_pass +} + +atf_test_case modes +modes_head() { + atf_set "require.user" "root" + atf_set "descr" "Test with ifconfig(8) that setting " \ + "802.11 modes does not panic (PR kern/45745)" +} + +modes_body() { + check_ifconfig_tests_enabled + + # Although 11n is not yet supported, the system + # should not panic from invalid input parameters. + # Therefore, the following will try to also set + # the four 802.11 modes for non-802.11 devices. + # + modes="11a 11b 11g 11n" + + pkill -9 hostapd + pkill -9 wpa_supplicant + + for i in $(ifconfig -l); do + + m="" + mm=$(ifconfig $i | grep "media") + + for j in $modes; do + + match=$(echo $mm | grep $j) + + if [ ! -z "$match" ]; then + m=$j + break + fi + done + + for j in $modes; do + echo "Request: $i -> 802.11 mode $j" + ifconfig $i mode $j >/dev/null 2>&1 + done + + if [ ! -z $m ]; then + ifconfig $i mode $m >/dev/null 2>&1 + echo "Restored the mode of $i to $m" + fi + done + + /bin/sh /etc/rc.d/hostapd restart >/dev/null 2>&1 + /bin/sh /etc/rc.d/wpa_supplicant restart >/dev/null 2>&1 + + atf_pass +} + +atf_init_test_cases() { + atf_add_test_case chan + atf_add_test_case mediaopt + atf_add_test_case modes +} diff --git a/sbin/newfs/quotas_common.sh b/sbin/newfs/quotas_common.sh old mode 100755 new mode 100644 diff --git a/sbin/newfs/t_enable_quotas.sh b/sbin/newfs/t_enable_quotas.sh old mode 100755 new mode 100644 diff --git a/sbin/newfs_msdos/t_create.sh b/sbin/newfs_msdos/t_create.sh old mode 100755 new mode 100644 --- a/sbin/newfs_msdos/t_create.sh +++ b/sbin/newfs_msdos/t_create.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_create.sh,v 1.3 2014/01/05 12:59:03 martin Exp $ +# $NetBSD: t_create.sh,v 1.4 2020/03/15 10:15:16 martin Exp $ # # Copyright (c) 2012 The NetBSD Foundation, Inc. # All rights reserved. @@ -31,6 +31,11 @@ } validfat32_body() { + AVAIL=$( df -m ${TMPDIR} | awk '{if (int($4) > 0) print $4}' ) + if [ $AVAIL -lt 34 ]; then + atf_skip "not enough free space in working directory" + fi + atf_check -s eq:0 -o ignore -e ignore \ newfs_msdos -b 512 -C 33m -F 32 msdos.img # fsck_msdos/newfs_msdos have been fixed diff --git a/sbin/resize_ffs/common.sh b/sbin/resize_ffs/common.sh old mode 100755 new mode 100644 --- a/sbin/resize_ffs/common.sh +++ b/sbin/resize_ffs/common.sh @@ -115,6 +115,12 @@ local fslevel=$5 local numdata=$6 local swap=$7 + local avail=$( df -m . | awk '{if (int($4) > 0) print $4}' ) + # convert MB size to blocks + avail=$(( $avail \* 2 \* 1024 )) + if [ $avail -lt $osize ] || [ $avail -lt $nsize ]; then + atf_skip "not enough free space in working directory" + fi mkdir -p mnt echo "bs is ${bs} numdata is ${numdata}" echo "****resizing fs with blocksize ${bs}" @@ -134,7 +140,17 @@ fi # we're specifying relative paths, so rump_ffs warns - ignore. - atf_check -s exit:0 -e ignore rump_ffs ${IMG} mnt + if ! rump_ffs ${IMG} mnt >/dev/null 2>S.Err + then + if grep 'puffs_daemon: Operation not supported by device' S.Err >/dev/null + then + atf_skip 'No PUFFS available in kernel' + else + atf_fail "rump_ffs mount failed: $(tail -r S.Err | + sed -e '/^$/d' -e p -e q )" + fi + fi + copy_multiple ${numdata} if [ ${nsize} -lt ${osize} ]; then diff --git a/sbin/resize_ffs/t_check.sh b/sbin/resize_ffs/t_check.sh old mode 100755 new mode 100644 diff --git a/sbin/resize_ffs/t_grow.sh b/sbin/resize_ffs/t_grow.sh old mode 100755 new mode 100644 diff --git a/sbin/resize_ffs/t_grow_swapped.sh b/sbin/resize_ffs/t_grow_swapped.sh old mode 100755 new mode 100644 diff --git a/sbin/resize_ffs/t_shrink.sh b/sbin/resize_ffs/t_shrink.sh old mode 100755 new mode 100644 diff --git a/sbin/resize_ffs/t_shrink_swapped.sh b/sbin/resize_ffs/t_shrink_swapped.sh old mode 100755 new mode 100644 diff --git a/sbin/route/t_missing.sh b/sbin/route/t_missing.sh old mode 100755 new mode 100644 diff --git a/sbin/setkey/Makefile b/sbin/setkey/Makefile new file mode 100644 --- /dev/null +++ b/sbin/setkey/Makefile @@ -0,0 +1,9 @@ +# $NetBSD: Makefile,v 1.1 2020/06/25 17:08:32 jruoho Exp $ + +.include + +TESTSDIR= ${TESTSBASE}/sbin/setkey + +TESTS_SH= t_setkey + +.include diff --git a/sbin/setkey/t_setkey.sh b/sbin/setkey/t_setkey.sh new file mode 100644 --- /dev/null +++ b/sbin/setkey/t_setkey.sh @@ -0,0 +1,47 @@ +# $NetBSD: t_setkey.sh,v 1.1 2020/06/25 17:08:32 jruoho Exp $ +# +# Copyright (c) 2020 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Jukka Ruohonen. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +atf_test_case invalid_length +invalid_length_head() { + atf_require_prog "setkey" + atf_set "require.user" "root" + atf_set "descr" "Test with setkey(8) that setting a key " \ + "with invalid length does not panic (PR kern/52150)" +} + +invalid_length_body() { + atf_check -s exit:1 -o ignore -e not-empty \ + -x "setkey add 10.0.0.1 10.0.0.2 esp 10000 " \ + "-E aes-gmac \"hogehogehogehogehogehoge\"" +} + +atf_init_test_cases() { + atf_add_test_case invalid_length +} diff --git a/sbin/sysctl/Makefile b/sbin/sysctl/Makefile --- a/sbin/sysctl/Makefile +++ b/sbin/sysctl/Makefile @@ -1,9 +1,9 @@ -# $NetBSD: Makefile,v 1.2 2012/04/10 02:39:33 jruoho Exp $ +# $NetBSD: Makefile,v 1.3 2020/06/27 08:50:46 jruoho Exp $ .include TESTSDIR= ${TESTSBASE}/sbin/sysctl -TESTS_SH= t_perm t_sysctl +TESTS_SH= t_perm t_random_garbage t_sysctl .include diff --git a/sbin/sysctl/t_perm.sh b/sbin/sysctl/t_perm.sh old mode 100755 new mode 100644 diff --git a/sbin/sysctl/t_random_garbage.sh b/sbin/sysctl/t_random_garbage.sh new file mode 100644 --- /dev/null +++ b/sbin/sysctl/t_random_garbage.sh @@ -0,0 +1,108 @@ +# $NetBSD: t_random_garbage.sh,v 1.4 2020/07/27 07:36:19 jruoho Exp $ +# +# Copyright (c) 2020 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Jukka Ruohonen. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# +tmp="/tmp/sysctl.out" + +getrint() { + echo $(od -An -N2 -i /dev/urandom | sed 's/ //') +} + +getrstr() { + echo $(cat /dev/urandom | head -n 1 | base64) +} + +atf_test_case random_garbage cleanup +random_garbage_head() { + sysctl -a > $tmp + atf_set "require.user" "root" + atf_set "descr" "Test writing random garbage " \ + "to sysctl nodes (PR kern/55451)" +} + +random_garbage_body() { + + if ! [ $(atf_config_get "run_unsafe" "no") = "yes" ]; then + atf_skip "The test is not safe (PR kern/55451)" + fi + + while read line; do + + var=$(echo $line | awk '{print $1}') + + case $var in + hw.acpi.sleep.state) + echo "Skipping $var" + continue + ;; + + kern.securelevel*) + echo "Skipping $var" + continue + ;; + + kern.veriexec.strict) + echo "Skipping $var" + continue + ;; + + security*) + echo "Skipping $var" + continue + ;; + esac + + val=$(getrint) + echo "Write $var -> $val" + sysctl -w $var=$val + val=$(getrstr) + echo "Write $var -> $val" + sysctl -w $var=$val + + done < $tmp +} + +random_garbage_cleanup() { + + if ! [ $(atf_config_get "run_unsafe" "no") = "yes" ]; then + atf_skip "The test is not safe (PR kern/55451)" + fi + + while read line; do + var=$(echo $line | awk '{print $1}') + val=$(echo $line | awk '{print $3}') + echo "Restoring $var -> $val" + sysctl -w $var=$val > /dev/null 2>&1 + done < $tmp + + rm $tmp +} + +atf_init_test_cases() { + atf_add_test_case random_garbage +} diff --git a/sbin/sysctl/t_sysctl.sh b/sbin/sysctl/t_sysctl.sh old mode 100755 new mode 100644 diff --git a/share/examples/t_asm.sh b/share/examples/t_asm.sh old mode 100755 new mode 100644 diff --git a/share/mk/t_lib.sh b/share/mk/t_lib.sh old mode 100755 new mode 100644 diff --git a/share/mk/t_own.sh b/share/mk/t_own.sh old mode 100755 new mode 100644 diff --git a/share/mk/t_prog.sh b/share/mk/t_prog.sh old mode 100755 new mode 100644 diff --git a/share/mk/t_test.sh b/share/mk/t_test.sh old mode 100755 new mode 100644 --- a/share/mk/t_test.sh +++ b/share/mk/t_test.sh @@ -77,7 +77,7 @@ atf_test_case one_cxx one_cxx_body() { - cat >t_fake.cpp <t_fake.cc < ATF_TEST_CASE_WITHOUT_HEAD(one_tc); ATF_TEST_CASE_BODY(one_tc) diff --git a/sys/Makefile b/sys/Makefile --- a/sys/Makefile +++ b/sys/Makefile @@ -1,9 +1,17 @@ -# $NetBSD: Makefile,v 1.3 2014/12/02 19:48:21 christos Exp $ +# $NetBSD: Makefile,v 1.5 2020/10/15 17:44:44 mgorny Exp $ .include TESTSDIR= ${TESTSBASE}/sys -TESTS_SUBDIRS+= net netatalk netinet netinet6 rc +TESTS_SUBDIRS+= crypto +TESTS_SUBDIRS+= net +TESTS_SUBDIRS+= netatalk +TESTS_SUBDIRS+= netinet +TESTS_SUBDIRS+= netinet6 +TESTS_SUBDIRS+= rc +.if ${MACHINE} == amd64 || ${MACHINE} == i386 +TESTS_SUBDIRS+= x86 +.endif .include diff --git a/sys/crypto/Makefile b/sys/crypto/Makefile new file mode 100644 --- /dev/null +++ b/sys/crypto/Makefile @@ -0,0 +1,10 @@ +# $NetBSD: Makefile,v 1.2 2020/07/25 22:53:38 riastradh Exp $ + +.include + +TESTSDIR= ${TESTSBASE}/sys/crypto + +TESTS_SUBDIRS+= aes +TESTS_SUBDIRS+= chacha + +.include diff --git a/sys/crypto/aes/Makefile b/sys/crypto/aes/Makefile new file mode 100644 --- /dev/null +++ b/sys/crypto/aes/Makefile @@ -0,0 +1,83 @@ +# $NetBSD: Makefile,v 1.6 2020/09/08 17:35:27 jakllsch Exp $ + +.include + +TESTSDIR= ${TESTSBASE}/sys/crypto/aes + +TESTS_C= t_aes + +AFLAGS+= -D_LOCORE + +.PATH: ${NETBSDSRCDIR}/sys/crypto/aes +CPPFLAGS+= -I${NETBSDSRCDIR}/sys + +SRCS.t_aes+= t_aes.c + +SRCS.t_aes+= aes_bear.c +SRCS.t_aes+= aes_ct.c +SRCS.t_aes+= aes_ct_dec.c +SRCS.t_aes+= aes_ct_enc.c +SRCS.t_aes+= aes_selftest.c + +.if !empty(MACHINE_ARCH:Mearmv7*) || !empty(MACHINE_ARCH:Maarch64*) + +.PATH: ${NETBSDSRCDIR}/sys/crypto/aes/arch/arm +CPPFLAGS+= -I${NETBSDSRCDIR}/sys/crypto/aes/arch/arm + +.if !empty(MACHINE_ARCH:Maarch64*) # XXX no AESE/AESD in 32-bit mode yet +SRCS.t_aes+= aes_armv8.c +SRCS.t_aes+= aes_armv8_64.S +.endif + +CLANG_NO_NONPORTABLE_VECTOR_INITIALIZATION= ${${ACTIVE_CC} == "clang":? -Wno-nonportable-vector-initialization :} + +SRCS.t_aes+= aes_neon.c +COPTS.aes_neon.c+=${CLANG_NO_NONPORTABLE_VECTOR_INITIALIZATION} +SRCS.t_aes+= aes_neon_impl.c +SRCS.t_aes+= aes_neon_subr.c +COPTS.aes_neon_subr.c+=${CLANG_NO_NONPORTABLE_VECTOR_INITIALIZATION} +.if !empty(MACHINE_ARCH:Mearmv7*) +SRCS.t_aes+= aes_neon_32.S +.endif + +.if !empty(MACHINE_ARCH:Mearmv7*) && empty(MACHINE_ARCH:Mearmv7hf*) +COPTS.aes_neon.c+= -mfloat-abi=softfp -mfpu=neon +COPTS.aes_neon_subr.c+= -mfloat-abi=softfp -mfpu=neon +AOPTS.aes_neon_32.S+= -D__SOFTFP__ +.endif + +.endif # earmv7/aarch64 + +.if ${MACHINE_ARCH} == "i386" || ${MACHINE_ARCH} == "x86_64" + +.PATH: ${NETBSDSRCDIR}/sys/crypto/aes/arch/x86 +CPPFLAGS+= -I${NETBSDSRCDIR}/sys/crypto/aes/arch/x86 + +.if ${MACHINE_ARCH} == "x86_64" # XXX no AES-NI in 32-bit mode yet +SRCS.t_aes+= aes_ni.c +SRCS.t_aes+= aes_ni_64.S +.endif + +SRCS.t_aes+= aes_sse2.c +SRCS.t_aes+= aes_sse2_dec.c +SRCS.t_aes+= aes_sse2_enc.c +SRCS.t_aes+= aes_sse2_impl.c +SRCS.t_aes+= aes_sse2_subr.c +COPTS.aes_sse2.c+= -msse -msse2 +COPTS.aes_sse2_dec.c+= -msse -msse2 +COPTS.aes_sse2_enc.c+= -msse -msse2 +COPTS.aes_sse2_subr.c+= -msse -msse2 + +SRCS.t_aes+= aes_ssse3.c +SRCS.t_aes+= aes_ssse3_impl.c +SRCS.t_aes+= aes_ssse3_subr.c +COPTS.aes_ssse3.c+= -msse -msse2 -msse3 -mssse3 +COPTS.aes_ssse3_subr.c+= -msse -msse2 -msse3 -mssse3 + +SRCS.t_aes+= aes_via.c + +.endif # x86 + +WARNS= 5 + +.include diff --git a/sys/crypto/aes/t_aes.c b/sys/crypto/aes/t_aes.c new file mode 100644 --- /dev/null +++ b/sys/crypto/aes/t_aes.c @@ -0,0 +1,136 @@ +/* $NetBSD: t_aes.c,v 1.4 2020/08/17 16:26:02 riastradh Exp $ */ + +/*- + * Copyright (c) 2020 The NetBSD Foundation, Inc. + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 + +#include +#include +#include + +#if defined(__i386__) || defined(__x86_64__) +#include +#include +#include +#include +#endif + +#ifdef __aarch64__ +#include +#endif + +#if __ARM_ARCH >= 7 +#include +#endif + +#include + +ATF_TC(aes_ct_selftest); +ATF_TC_HEAD(aes_ct_selftest, tc) +{ + + atf_tc_set_md_var(tc, "descr", "BearSSL aes_ct tests"); +} + +ATF_TC_BODY(aes_ct_selftest, tc) +{ + + if (aes_bear_impl.ai_probe()) { + /* + * aes_ct is the portable software fallback, so probe + * should never fail. + */ + atf_tc_fail("BearSSL aes_ct probe failed"); + } + + if (aes_selftest(&aes_bear_impl)) + atf_tc_fail("BearSSL aes_ct self-test failed"); +} + +#define AES_SELFTEST(name, impl, descr) \ +ATF_TC(name); \ +ATF_TC_HEAD(name, tc) \ +{ \ + \ + atf_tc_set_md_var(tc, "descr", descr); \ +} \ + \ +ATF_TC_BODY(name, tc) \ +{ \ + \ + if ((impl)->ai_probe()) \ + atf_tc_skip("%s not supported on this hardware", \ + (impl)->ai_name); \ + if (aes_selftest(impl)) \ + atf_tc_fail("%s self-test failed", (impl)->ai_name); \ +} + +#ifdef __aarch64__ +AES_SELFTEST(aes_armv8_selftest, &aes_armv8_impl, "ARMv8.0-AES self-test") +#endif + +#if __ARM_ARCH >= 7 +AES_SELFTEST(aes_neon_selftest, &aes_neon_impl, "ARM NEON vpaes self-test") +#endif + +#ifdef __x86_64__ +AES_SELFTEST(aes_ni_selftest, &aes_ni_impl, "Intel AES-NI self-test") +#endif + +#if defined(__i386__) || defined(__x86_64__) +AES_SELFTEST(aes_sse2_selftest, &aes_sse2_impl, + "Intel SSE2 bitsliced self-test") +AES_SELFTEST(aes_ssse3_selftest, &aes_ssse3_impl, + "Intel SSSE3 vpaes self-test") +AES_SELFTEST(aes_via_selftest, &aes_via_impl, "VIA ACE AES self-test") +#endif + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, aes_ct_selftest); + +#ifdef __aarch64__ + ATF_TP_ADD_TC(tp, aes_armv8_selftest); +#endif + +#if __ARM_ARCH >= 7 + ATF_TP_ADD_TC(tp, aes_neon_selftest); +#endif + +#ifdef __x86_64__ + ATF_TP_ADD_TC(tp, aes_ni_selftest); +#endif + +#if defined(__i386__) || defined(__x86_64__) + ATF_TP_ADD_TC(tp, aes_sse2_selftest); + ATF_TP_ADD_TC(tp, aes_ssse3_selftest); + ATF_TP_ADD_TC(tp, aes_via_selftest); +#endif + + return atf_no_error(); +} diff --git a/sys/crypto/chacha/Makefile b/sys/crypto/chacha/Makefile new file mode 100644 --- /dev/null +++ b/sys/crypto/chacha/Makefile @@ -0,0 +1,55 @@ +# $NetBSD: Makefile,v 1.6 2020/09/08 17:35:27 jakllsch Exp $ + +.include + +TESTSDIR= ${TESTSBASE}/sys/crypto/chacha + +TESTS_C= t_chacha + +AFLAGS+= -D_LOCORE + +.PATH: ${NETBSDSRCDIR}/sys/crypto/chacha +CPPFLAGS+= -I${NETBSDSRCDIR}/sys + +SRCS.t_chacha+= t_chacha.c + +SRCS.t_chacha+= chacha_ref.c +SRCS.t_chacha+= chacha_selftest.c + +.if !empty(MACHINE_ARCH:Mearmv7*) || !empty(MACHINE_ARCH:Maarch64*) + +.PATH: ${NETBSDSRCDIR}/sys/crypto/chacha/arch/arm +CPPFLAGS+= -I${NETBSDSRCDIR}/sys/crypto/chacha/arch/arm + +CLANG_NO_NONPORTABLE_VECTOR_INITIALIZATION= ${${ACTIVE_CC} == "clang":? -Wno-nonportable-vector-initialization :} + +SRCS.t_chacha+= chacha_neon.c +COPTS.chacha_neon.c+= ${CLANG_NO_NONPORTABLE_VECTOR_INITIALIZATION} +.if !empty(MACHINE_ARCH:Mearmv7*) +SRCS.t_chacha+= chacha_neon_32.S +.elif !empty(MACHINE_ARCH:Maarch64*) +SRCS.t_chacha+= chacha_neon_64.S +.endif +SRCS.t_chacha+= chacha_neon_impl.c + +.if !empty(MACHINE_ARCH:Mearmv7*) && empty(MACHINE_ARCH:Mearmv7hf*) +COPTS.chacha_neon.c+= -mfloat-abi=softfp -mfpu=neon +AOPTS.chacha_neon_32.S+= -D__SOFTFP__ +.endif + +.endif # earmv7 or aarch64 + +.if ${MACHINE_ARCH} == "i386" || ${MACHINE_ARCH} == "x86_64" + +.PATH: ${NETBSDSRCDIR}/sys/crypto/chacha/arch/x86 +CPPFLAGS+= -I${NETBSDSRCDIR}/sys/crypto/chacha/arch/x86 + +SRCS.t_chacha+= chacha_sse2.c +SRCS.t_chacha+= chacha_sse2_impl.c +COPTS.chacha_sse2.c+= -msse -msse2 + +.endif # x86 + +WARNS= 5 + +.include diff --git a/sys/crypto/chacha/t_chacha.c b/sys/crypto/chacha/t_chacha.c new file mode 100644 --- /dev/null +++ b/sys/crypto/chacha/t_chacha.c @@ -0,0 +1,108 @@ +/* $NetBSD: t_chacha.c,v 1.4 2020/08/17 16:26:02 riastradh Exp $ */ + +/*- + * Copyright (c) 2020 The NetBSD Foundation, Inc. + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 + +#include +#include + +#if defined(__i386__) || defined(__x86_64__) +#include +#endif + +#if __ARM_ARCH >= 7 +#include +#endif + +#include + +ATF_TC(chacha_ref_selftest); +ATF_TC_HEAD(chacha_ref_selftest, tc) +{ + + atf_tc_set_md_var(tc, "descr", "Portable C chacha_ref tests"); +} + +ATF_TC_BODY(chacha_ref_selftest, tc) +{ + + if (chacha_ref_impl.ci_probe()) { + /* + * chacha_ref is the portable software fallback, so + * probe should never fail. + */ + atf_tc_fail("Portable C chacha_ref probe failed"); + } + + if (chacha_selftest(&chacha_ref_impl)) + atf_tc_fail("Portable C chacha_ref self-test failed"); +} + +#define CHACHA_SELFTEST(name, impl, descr) \ +ATF_TC(name); \ +ATF_TC_HEAD(name, tc) \ +{ \ + \ + atf_tc_set_md_var(tc, "descr", descr); \ +} \ + \ +ATF_TC_BODY(name, tc) \ +{ \ + \ + if ((impl)->ci_probe()) \ + atf_tc_skip("%s not supported on this hardware", \ + (impl)->ci_name); \ + if (chacha_selftest(impl)) \ + atf_tc_fail("%s self-test failed", (impl)->ci_name); \ +} + +#if __ARM_ARCH >= 7 +CHACHA_SELFTEST(chacha_neon_selftest, &chacha_neon_impl, + "ARM NEON ChaCha self-test") +#endif + +#if defined(__i386__) || defined(__x86_64__) +CHACHA_SELFTEST(chacha_sse2_selftest, &chacha_sse2_impl, + "x86 SSE2 ChaCha self-test") +#endif + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, chacha_ref_selftest); + +#if __ARM_ARCH >= 7 + ATF_TP_ADD_TC(tp, chacha_neon_selftest); +#endif + +#if defined(__i386__) || defined(__x86_64__) + ATF_TP_ADD_TC(tp, chacha_sse2_selftest); +#endif + + return atf_no_error(); +} diff --git a/sys/netatalk/Makefile b/sys/netatalk/Makefile --- a/sys/netatalk/Makefile +++ b/sys/netatalk/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.1 2014/12/02 19:48:21 christos Exp $ +# $NetBSD: Makefile,v 1.2 2021/04/12 02:54:08 mrg Exp $ # WARNS?=6 @@ -9,4 +9,6 @@ TESTS_C= t_print +COPTS.t_print.c+= ${GCC_NO_FORMAT_TRUNCATION} + .include diff --git a/sys/rc/t_rc_d_cli.sh b/sys/rc/t_rc_d_cli.sh old mode 100755 new mode 100644 diff --git a/sys/uvm/Makefile b/sys/uvm/Makefile new file mode 100644 --- /dev/null +++ b/sys/uvm/Makefile @@ -0,0 +1,24 @@ +# $NetBSD: Makefile,v 1.1 2016/12/19 12:21:29 cherry Exp $ +# + +WARNS?=6 +.include + +TESTSDIR= ${TESTSBASE}/sys/uvm +CPPFLAGS+= -I${NETBSDSRCDIR}/sys -I${.CURDIR}/ -D_TEST -g + +# Depend on the kernel source files too +DPSRCS= ${NETBSDSRCDIR}/sys/uvm/uvm_physseg.[ch] + +.PATH: ${NETBSDSRCDIR}/sys/kern +TESTS_C+= t_uvm_physseg +SRCS.t_uvm_physseg+= t_uvm_physseg.c subr_extent.c +CPPFLAGS.t_uvm_physseg.c= -D_EXTENT_TESTING -D__POOL_EXPOSE -DDIAGNOSTIC +CPPFLAGS.subr_extent.c= -D_EXTENT_TESTING -D__POOL_EXPOSE -D_KERNTYPES -DDIAGNOSTIC + +TESTS_C+= t_uvm_physseg_load +SRCS.t_uvm_physseg_load+= t_uvm_physseg_load.c subr_extent.c +CPPFLAGS.t_uvm_physseg_load.c= -D_EXTENT_TESTING -D__POOL_EXPOSE -DDIAGNOSTIC + +.include +.include diff --git a/sys/uvm/t_uvm_physseg.c b/sys/uvm/t_uvm_physseg.c --- a/sys/uvm/t_uvm_physseg.c +++ b/sys/uvm/t_uvm_physseg.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_uvm_physseg.c,v 1.2 2016/12/22 08:15:20 cherry Exp $ */ +/* $NetBSD: t_uvm_physseg.c,v 1.9 2021/08/13 20:19:13 andvar Exp $ */ /*- * Copyright (c) 2015, 2016 The NetBSD Foundation, Inc. @@ -31,7 +31,7 @@ */ #include -__RCSID("$NetBSD: t_uvm_physseg.c,v 1.2 2016/12/22 08:15:20 cherry Exp $"); +__RCSID("$NetBSD: t_uvm_physseg.c,v 1.9 2021/08/13 20:19:13 andvar Exp $"); /* * If this line is commented out tests related to uvm_physseg_get_pmseg() @@ -270,6 +270,11 @@ } #endif +/* + * This macro was added to convert uvmexp.npages from int to psize_t + */ +#define INT_TO_PSIZE_T(X) (psize_t)X + /* * Test Fixture SetUp(). */ @@ -396,7 +401,7 @@ ATF_TC_HEAD(uvm_physseg_atboot_free_leak, tc) { atf_tc_set_md_var(tc, "descr", - "does free() leak at boot ?\n" + "does free() leak at boot ?" "This test needs VM_PHYSSEG_MAX > 1)"); } @@ -498,7 +503,7 @@ #if VM_PHYSSEG_MAX > 2 + npages2 #endif - , uvmexp.npages); + , INT_TO_PSIZE_T(uvmexp.npages)); #if VM_PHYSSEG_MAX > 1 /* Scavenge plug - goes into the same slab */ ATF_REQUIRE_EQ(uvm_physseg_plug(VALID_START_PFN_3, npages3, &upm3), true); @@ -507,7 +512,7 @@ #if VM_PHYSSEG_MAX > 2 + npages2 #endif - + npages3, uvmexp.npages); + + npages3, INT_TO_PSIZE_T(uvmexp.npages)); /* Scavenge plug should fit right in the slab */ pgs = uvm_physseg_get_pg(upm3, 0); @@ -517,7 +522,7 @@ ATF_REQUIRE_EQ(uvm_physseg_plug(VALID_START_PFN_4, npages4, &upm4), true); /* The hot plug slab should have nothing to do with the original slab */ pgs = uvm_physseg_get_pg(upm4, 0); - ATF_REQUIRE(pgs < slab || pgs > (slab + npages1 + ATF_REQUIRE(pgs < slab || pgs >= (slab + npages1 #if VM_PHYSSEG_MAX > 2 + npages2 #endif @@ -706,7 +711,7 @@ /* Should return a valid handle */ ATF_REQUIRE(uvm_physseg_valid_p(upm)); - ATF_REQUIRE_EQ(npages1 + npages2, uvmexp.npages); + ATF_REQUIRE_EQ(npages1 + npages2, INT_TO_PSIZE_T(uvmexp.npages)); /* After the second call two segments should exist */ ATF_CHECK_EQ(2, uvm_physseg_get_entries()); @@ -889,7 +894,7 @@ uvm_physseg_init_seg(PHYSSEG_NODE_TO_HANDLE(seg), pgs); - ATF_REQUIRE_EQ(npages, uvmexp.npages); + ATF_REQUIRE_EQ(npages, INT_TO_PSIZE_T(uvmexp.npages)); } #if 0 @@ -2037,7 +2042,7 @@ /* * Note: start != avail_start and end != avail_end. * - * This prevents any unload from occuring. + * This prevents any unload from occurring. */ upm = uvm_page_physload(VALID_START_PFN_2, VALID_END_PFN_2, VALID_AVAIL_START_PFN_2 + 1, VALID_AVAIL_END_PFN_2 - 1, @@ -2150,7 +2155,7 @@ */ upm = uvm_page_physload(VALID_START_PFN_1, VALID_START_PFN_1 + 2, - VALID_AVAIL_START_PFN_1 + 1, VALID_AVAIL_START_PFN_1 + 2, + VALID_AVAIL_START_PFN_1, VALID_AVAIL_START_PFN_1 + 2, VM_FREELIST_DEFAULT); ATF_REQUIRE_EQ(1, uvm_physseg_get_entries()); @@ -2172,11 +2177,13 @@ ATF_CHECK_EQ(true, uvm_page_physunload(upm, VM_FREELIST_DEFAULT, &p)); + ATF_CHECK_EQ(VALID_START_PFN_1, atop(p)); + p = 0; ATF_CHECK_EQ(true, uvm_page_physunload(upm, VM_FREELIST_DEFAULT, &p)); - ATF_CHECK_EQ(VALID_START_PFN_1 + 2, atop(p)); + ATF_CHECK_EQ(VALID_START_PFN_1 + 1, atop(p)); ATF_CHECK_EQ(1, uvm_physseg_get_entries()); @@ -2244,7 +2251,7 @@ /* Insert more than one segment iff VM_PHYSSEG_MAX > 1 */ #if VM_PHYSSEG_MAX > 1 /* - * We have couple of physloads done this is bacause of the fact that if + * We have couple of physloads done this is because of the fact that if * we physunload all the PFs from a given range and we have only one * segment in total a panic() is called */ @@ -2279,7 +2286,11 @@ upm = uvm_physseg_find(VALID_AVAIL_END_PFN_1 - 1, NULL); /* It should no longer exist */ +#if defined(UVM_HOTPLUG) ATF_CHECK_EQ(NULL, upm); +#else + ATF_CHECK_EQ(-1, upm); +#endif ATF_CHECK_EQ(1, uvm_physseg_get_entries()); } diff --git a/sys/x86/Makefile b/sys/x86/Makefile new file mode 100644 --- /dev/null +++ b/sys/x86/Makefile @@ -0,0 +1,11 @@ +# $NetBSD: Makefile,v 1.1 2020/10/15 17:44:44 mgorny Exp $ +# + +.include + +TESTSDIR= ${TESTSBASE}/sys/x86 +CPPFLAGS+= -I${NETBSDSRCDIR}/sys + +TESTS_C= t_convert_xmm_s87 + +.include diff --git a/sys/x86/t_convert_xmm_s87.c b/sys/x86/t_convert_xmm_s87.c new file mode 100644 --- /dev/null +++ b/sys/x86/t_convert_xmm_s87.c @@ -0,0 +1,211 @@ +/* $NetBSD: t_convert_xmm_s87.c,v 1.1 2020/10/15 17:44:44 mgorny Exp $ */ + +/*- + * Copyright (c) 2020 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Michał Górny for Moritz Systems Technology Company Sp. z o.o. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +__RCSID("$NetBSD: t_convert_xmm_s87.c,v 1.1 2020/10/15 17:44:44 mgorny Exp $"); + +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "arch/x86/x86/convert_xmm_s87.c" + +static const struct fpaccfx ST_INPUTS[] = { + {{0x8000000000000000, 0x4000}}, /* +2.0 */ + {{0x3f00000000000000, 0x0000}}, /* 1.654785e-4932 */ + {{0x0000000000000000, 0x0000}}, /* +0 */ + {{0x0000000000000000, 0x8000}}, /* -0 */ + {{0x8000000000000000, 0x7fff}}, /* +inf */ + {{0x8000000000000000, 0xffff}}, /* -inf */ + {{0xc000000000000000, 0xffff}}, /* nan */ + {{0x8000000000000000, 0xc000}}, /* -2.0 */ +}; +__CTASSERT(sizeof(*ST_INPUTS) == 16); + +ATF_TC(fsave_fxsave_hw); +ATF_TC_HEAD(fsave_fxsave_hw, tc) +{ + atf_tc_set_md_var(tc, "descr", + "converting between FSAVE/FXSAVE comparing to actual results"); +} + +ATF_TC_BODY(fsave_fxsave_hw, tc) +{ + struct save87 fsave, fsave_conv; + struct fxsave fxsave, fxsave_conv; + int i, j; + + int mib[] = { CTL_MACHDEP, CPU_OSFXSR }; + int has_fxsave; + size_t has_fxsave_size = sizeof(has_fxsave); + if (sysctl(mib, __arraycount(mib), &has_fxsave, &has_fxsave_size, + NULL, 0) == -1 || !has_fxsave) + atf_tc_skip("FXSAVE not supported"); + + for (i = 1; i <= __arraycount(ST_INPUTS); i++) { + unsigned long unused1, unused2; + __asm__ __volatile__( + "finit\n" + ".loadfp:\n\t" + "fldt (%2)\n\t" + "add $0x10, %2\n\t" + "loop .loadfp\n\t" + "fxsave %5\n\t" + "fsave %4\n\t" + : "=b"(unused1), "=c"(unused2) + : "b"(ST_INPUTS), "c"(i), "m"(fsave), "m"(fxsave) + : "st" + ); + + /* Self-assertion for working FSAVE/FXSAVE */ + ATF_REQUIRE_EQ(fsave.s87_cw, fxsave.fx_cw); + ATF_REQUIRE_EQ(fsave.s87_sw, fxsave.fx_sw); + + /* Test process_xmm_to_s87() */ + process_xmm_to_s87(&fxsave, &fsave_conv); + ATF_CHECK_EQ(fsave_conv.s87_cw, fsave.s87_cw); + ATF_CHECK_EQ(fsave_conv.s87_sw, fsave.s87_sw); + ATF_CHECK_EQ(fsave_conv.s87_tw, fsave.s87_tw); + for (j = 0; j < i; j++) { + ATF_CHECK_EQ(fsave_conv.s87_ac[j].f87_exp_sign, + fsave.s87_ac[j].f87_exp_sign); + ATF_CHECK_EQ(fsave_conv.s87_ac[j].f87_mantissa, + fsave.s87_ac[j].f87_mantissa); + } + + /* Test process_s87_to_xmm() */ + process_s87_to_xmm(&fsave, &fxsave_conv); + ATF_CHECK_EQ(fxsave_conv.fx_cw, fxsave.fx_cw); + ATF_CHECK_EQ(fxsave_conv.fx_sw, fxsave.fx_sw); + ATF_CHECK_EQ(fxsave_conv.fx_tw, fxsave.fx_tw); + for (j = 0; j < i; j++) { + ATF_CHECK_EQ(fxsave_conv.fx_87_ac[j].r.f87_exp_sign, + fxsave.fx_87_ac[j].r.f87_exp_sign); + ATF_CHECK_EQ(fxsave_conv.fx_87_ac[j].r.f87_mantissa, + fxsave.fx_87_ac[j].r.f87_mantissa); + } + } +} + +struct s87_xmm_test_vector { + uint16_t sw; + uint16_t tw; + uint8_t tw_abridged; +}; + +struct s87_xmm_test_vector FIXED_TEST_VECTORS[] = { + {0x3800, 0x3fff, 0x80}, + {0x3000, 0x2fff, 0xc0}, + {0x2800, 0x27ff, 0xe0}, + {0x2000, 0x25ff, 0xf0}, + {0x1800, 0x25bf, 0xf8}, + {0x1000, 0x25af, 0xfc}, + {0x0800, 0x25ab, 0xfe}, + {0x0000, 0x25a8, 0xff}, +}; + +ATF_TC(s87_to_xmm); +ATF_TC_HEAD(s87_to_xmm, tc) +{ + atf_tc_set_md_var(tc, "descr", + "converting from FSAVE to FXSAVE using fixed test vectors"); +} + +ATF_TC_BODY(s87_to_xmm, tc) +{ + struct save87 fsave; + struct fxsave fxsave; + int i, j; + + memset(&fsave, 0, sizeof(fsave)); + for (i = 0; i < __arraycount(ST_INPUTS); i++) { + fsave.s87_sw = FIXED_TEST_VECTORS[i].sw; + fsave.s87_tw = FIXED_TEST_VECTORS[i].tw; + for (j = 0; j <= i; j++) + fsave.s87_ac[i - j] = ST_INPUTS[j].r; + + process_s87_to_xmm(&fsave, &fxsave); + ATF_CHECK_EQ(fxsave.fx_sw, FIXED_TEST_VECTORS[i].sw); + ATF_CHECK_EQ(fxsave.fx_tw, FIXED_TEST_VECTORS[i].tw_abridged); + for (j = 0; j < i; j++) { + ATF_CHECK_EQ(fxsave.fx_87_ac[i - j].r.f87_exp_sign, + ST_INPUTS[j].r.f87_exp_sign); + ATF_CHECK_EQ(fxsave.fx_87_ac[i - j].r.f87_mantissa, + ST_INPUTS[j].r.f87_mantissa); + } + } +} + +ATF_TC(xmm_to_s87); +ATF_TC_HEAD(xmm_to_s87, tc) +{ + atf_tc_set_md_var(tc, "descr", + "converting from FSAVE to FXSAVE using fixed test vectors"); +} + +ATF_TC_BODY(xmm_to_s87, tc) +{ + struct save87 fsave; + struct fxsave fxsave; + int i, j; + + memset(&fxsave, 0, sizeof(fxsave)); + for (i = 0; i < __arraycount(ST_INPUTS); i++) { + fxsave.fx_sw = FIXED_TEST_VECTORS[i].sw; + fxsave.fx_tw = FIXED_TEST_VECTORS[i].tw_abridged; + for (j = 0; j <= i; j++) + fxsave.fx_87_ac[i - j] = ST_INPUTS[j]; + + process_xmm_to_s87(&fxsave, &fsave); + ATF_CHECK_EQ(fsave.s87_sw, FIXED_TEST_VECTORS[i].sw); + ATF_CHECK_EQ(fsave.s87_tw, FIXED_TEST_VECTORS[i].tw); + for (j = 0; j < i; j++) { + ATF_CHECK_EQ(fsave.s87_ac[i - j].f87_exp_sign, + ST_INPUTS[j].r.f87_exp_sign); + ATF_CHECK_EQ(fsave.s87_ac[i - j].f87_mantissa, + ST_INPUTS[j].r.f87_mantissa); + } + } +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, fsave_fxsave_hw); + ATF_TP_ADD_TC(tp, s87_to_xmm); + ATF_TP_ADD_TC(tp, xmm_to_s87); + return atf_no_error(); +} diff --git a/usr.bin/Makefile b/usr.bin/Makefile --- a/usr.bin/Makefile +++ b/usr.bin/Makefile @@ -1,14 +1,23 @@ -# $NetBSD: Makefile,v 1.23 2017/01/02 15:40:09 christos Exp $ +# $NetBSD: Makefile,v 1.34 2020/11/01 22:28:32 christos Exp $ # .include TESTSDIR= ${TESTSBASE}/usr.bin -TESTS_SUBDIRS= awk basename bzip2 cc cmp config cut \ - diff dirname find gdb grep gzip id \ - infocmp jot ld m4 make mixerctl mkdep \ - nbperf netpgpverify pr rump_server shmif_dumpbus sdiff \ - sed sort tmux tr unifdef uniq vmstat xlint +TESTS_SUBDIRS= awk basename bzip2 cc cmp config cpio col cut \ + diff dirname find fstat gdb grep gzip id indent \ + infocmp jot ld locale m4 make mixerctl mkdep nbperf \ + netpgpverify patch pkill pr printf pwhash rump_server \ + shmif_dumpbus sdiff sed sort tar tmux tr unifdef uniq \ + vmstat xlint ztest + +.if ${MKCXX} != "no" +TESTS_SUBDIRS+= c++ +.endif + +.if (defined(MKARGON2) && ${MKARGON2} != "no") +TESTS_SUBDIRS+= argon2 +.endif .include diff --git a/usr.bin/argon2/Makefile b/usr.bin/argon2/Makefile new file mode 100644 --- /dev/null +++ b/usr.bin/argon2/Makefile @@ -0,0 +1,8 @@ +# $NetBSD: Makefile,v 1.1 2019/10/14 03:47:20 jhigh Exp $ + +.include + +TESTSDIR= ${TESTSBASE}/usr.bin/argon2 +TESTS_SH= t_argon2 + +.include diff --git a/usr.bin/argon2/t_argon2.sh b/usr.bin/argon2/t_argon2.sh new file mode 100644 --- /dev/null +++ b/usr.bin/argon2/t_argon2.sh @@ -0,0 +1,149 @@ +atf_test_case argon2_argon2id +argon2_argon2id_head() { + atf_set "descr" "ATF test for argon2 argon2id variant" +} + +argon2_argon2id_body() { + atf_check -s exit:0 -o match:"^\\\$argon2id\\\$" -x \ + 'echo -n 'password' | argon2 somesalt -e -id' +} + +atf_test_case argon2_argon2i +argon2_argon2i_head() { + atf_set "descr" "ATF test for argon2 argon2i variant" +} + +argon2_argon2i_body() { + atf_check -s exit:0 -o match:"^\\\$argon2i\\\$" -x \ + 'echo -n 'password' | argon2 somesalt -e -i' +} + +atf_test_case argon2_argon2d +argon2_argon2d_head() { + atf_set "descr" "ATF test for argon2 argon2d variant" +} + +argon2_argon2d_body() { + atf_check -s exit:0 -o match:"^\\\$argon2d\\\$" -x \ + 'echo -n 'password' | argon2 somesalt -e -d' +} + +atf_test_case argon2_argon2id_k2096_p2_t3 +argon2_argon2id_k2096_p2_head() { + atf_set "descr" "ATF test for argon2 argon2id,k=2096,p=2,t=3 " +} + +argon2_argon2id_k2096_p2_t3_body() { + atf_check -s exit:0 -o match:"^\\\$argon2id\\\$v=19\\\$m=2096,t=3,p=2" -x \ + 'echo -n 'password' | argon2 somesalt -e -id -k 2096 -p 2 -t 3' +} + +atf_test_case argon2_argon2i_k2096_p1_t4 +argon2_argon2i_k2096_p1_t4_head() { + atf_set "descr" "ATF test for argon2 argon2i,k=2096,p=1,t=4 " +} + +argon2_argon2i_k2096_p1_t4_body() { + atf_check -s exit:0 -o match:"^\\\$argon2i\\\$v=19\\\$m=2096,t=4,p=1" -x \ + 'echo -n 'password' | argon2 somesalt -e -i -k 2096 -p 1 -t 4' +} + +atf_test_case argon2_argon2d_k2096_p2_t4 +argon2_argon2d_k2096_p2_t4_head() { + atf_set "descr" "ATF test for argon2 argon2d,k=2096,p=2,t=4" +} + +argon2_argon2d_k2096_p2_t4_body() { + atf_check -s exit:0 -o match:"^\\\$argon2d\\\$v=19\\\$m=2096,t=4,p=2" -x \ + 'echo -n 'password' | argon2 somesalt -e -d -k 2096 -p 2 -t 4' +} + +atf_test_case argon2_argon2id_k2096_p1_v10 +argon2_argon2id_k2096_p1_v10_head() { + atf_set "descr" "ATF test for argon2 argon2id,k=2096,p=1,v=10" +} +argon2_argon2id_k2096_p1_v10_body() { + atf_check -s exit:0 -o match:"^\\\$argon2id\\\$v=16\\\$m=2096,t=3,p=1" -x \ + 'echo -n 'password' | argon2 somesalt -e -id -k 2096 -p 1 -v 10' +} + +atf_test_case argon2_argon2id_k2096_p1_v13 +argon2_argon2id_k2096_p1_v13_head() { + atf_set "descr" "ATF test for argon2 argon2id,k=2096,p=1,v=13" +} +argon2_argon2id_k2096_p1_v13_body() { + atf_check -s exit:0 -o match:"^\\\$argon2id\\\$v=19\\\$m=2096,t=3,p=1" -x \ + 'echo -n 'password' | argon2 somesalt -e -id -k 2096 -p 1 -v 13' +} + +atf_test_case argon2_argon2i_k2096_p1_v10 +argon2_argon2i_k2096_p1_v10_head() { + atf_set "descr" "ATF test for argon2 argon2i,k=2096,p=1,v=10" +} +argon2_argon2i_k2096_p1_v10_body() { + atf_check -s exit:0 -o match:"^\\\$argon2i\\\$v=16\\\$m=2096,t=3,p=1" -x \ + 'echo -n 'password' | argon2 somesalt -e -i -k 2096 -p 1 -v 10' +} + +atf_test_case argon2_argon2i_k2096_p1_v13 +argon2_argon2i_k2096_p1_v13_head() { + atf_set "descr" "ATF test for argon2 argon2i,k=2096,p=1,v=13" +} +argon2_argon2i_k2096_p1_v13_body() { + atf_check -s exit:0 -o match:"^\\\$argon2i\\\$v=19\\\$m=2096,t=3,p=1" -x \ + 'echo -n 'password' | argon2 somesalt -e -i -k 2096 -p 1 -v 13' +} + +atf_test_case argon2_argon2d_k2096_p1_v10 +argon2_argon2d_k2096_p1_v10_head() { + atf_set "descr" "ATF test for argon2 argon2d,k=2096,p=1,v=10" +} +argon2_argon2d_k2096_p1_v10_body() { + atf_check -s exit:0 -o match:"^\\\$argon2d\\\$v=16\\\$m=2096,t=3,p=1" -x \ + 'echo -n 'password' | argon2 somesalt -e -d -k 2096 -p 1 -v 10' +} + +atf_test_case argon2_argon2d_k2096_p1_v13 +argon2_argon2d_k2096_p1_v13_head() { + atf_set "descr" "ATF test for argon2 argon2d,k=2096,p=1,v=13" +} +argon2_argon2d_k2096_p1_v13_body() { + atf_check -s exit:0 -o match:"^\\\$argon2d\\\$v=19\\\$m=2096,t=3,p=1" -x \ + 'echo -n 'password' | argon2 somesalt -e -d -k 2096 -p 1 -v 13' +} + +atf_test_case argon2_argon2id_k2096_p1_v19_inver +argon2_argon2id_k2096_p1_v19_inver_head() { + atf_set "descr" "ATF test for argon2 argon2d,k=2096,p=1 invalid version specification" +} +argon2_argon2id_k2096_p1_v19_inver_body() { + atf_check -s exit:1 -e match:"Error: invalid Argon2 version" -x \ + 'echo -n 'password' | argon2 somesalt -e -d -k 2096 -p 1 -v 19' +} + +atf_test_case argon2_argon2id_k2096_p1_sts +argon2_argon2id_k2096_p1_sts_head() { + atf_set "descr" "ATF test for argon2 argon2d,k=2096,p=1 salt too short" +} +argon2_argon2id_k2096_p1_sts_body() { + atf_check -s exit:1 -e match:"Error: Salt is too short" -x \ + 'echo -n 'password' | argon2 tshort -e -d -k 2096 -p 1' +} + +atf_init_test_cases() +{ + atf_add_test_case argon2_argon2id + atf_add_test_case argon2_argon2i + atf_add_test_case argon2_argon2d + atf_add_test_case argon2_argon2id_k2096_p2_t3 + atf_add_test_case argon2_argon2i_k2096_p1_t4 + atf_add_test_case argon2_argon2d_k2096_p2_t4 + atf_add_test_case argon2_argon2id_k2096_p1_v10 + atf_add_test_case argon2_argon2id_k2096_p1_v13 + atf_add_test_case argon2_argon2i_k2096_p1_v10 + atf_add_test_case argon2_argon2i_k2096_p1_v13 + atf_add_test_case argon2_argon2d_k2096_p1_v10 + atf_add_test_case argon2_argon2d_k2096_p1_v13 + atf_add_test_case argon2_argon2id_k2096_p1_v19_inver + atf_add_test_case argon2_argon2id_k2096_p1_sts +} diff --git a/usr.bin/awk/t_awk.sh b/usr.bin/awk/t_awk.sh old mode 100755 new mode 100644 --- a/usr.bin/awk/t_awk.sh +++ b/usr.bin/awk/t_awk.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_awk.sh,v 1.5 2012/12/10 20:30:06 christos Exp $ +# $NetBSD: t_awk.sh,v 1.7 2020/06/26 07:50:11 jruoho Exp $ # # Copyright (c) 2012 The NetBSD Foundation, Inc. # All rights reserved. @@ -93,7 +93,7 @@ period_head() { atf_set "descr" "Checks that the period character is recognised" \ - "in awk program regardless of locale (bin/42320)" + "in awk program regardless of locale (PR bin/42320)" } period_body() { @@ -335,6 +335,30 @@ -x "printf '\n\n\nr1f1\nr1f2\n\nr2f1\nr2f2\n\n\n' | $awk '{\$1=\$1}1' RS= OFS=:" } +atf_test_case regex_range + +regex_range_head() { + atf_set "descr" "Test awk(1) with regex range" +} + +regex_range_body() { + atf_check \ + -o "inline:matched\n" \ + -x "echo '1 a' | $awk '/[[:digit:]][[:space:]][[:alpha:]]/ { print \"matched\"; }'" +} + +atf_test_case regex_repeat + +regex_repeat_head() { + atf_set "descr" "Test awk(1) with regex repeat" +} + +regex_repeat_body() { + atf_check \ + -o "inline:matched\n" \ + -x "echo 'aaabbbbcc' | $awk '/a{3}b{4}c{2}/ { print \"matched\"; }'" +} + atf_test_case modify_subsep modify_subsep_head() { @@ -374,5 +398,7 @@ atf_add_test_case regex_reallocation_rs atf_add_test_case empty_rs atf_add_test_case newline_rs + atf_add_test_case regex_range + atf_add_test_case regex_repeat atf_add_test_case modify_subsep } diff --git a/usr.bin/basename/t_basename.sh b/usr.bin/basename/t_basename.sh old mode 100755 new mode 100644 diff --git a/usr.bin/bzip2/t_bzip2.sh b/usr.bin/bzip2/t_bzip2.sh old mode 100755 new mode 100644 diff --git a/usr.bin/c++/Makefile b/usr.bin/c++/Makefile new file mode 100644 --- /dev/null +++ b/usr.bin/c++/Makefile @@ -0,0 +1,62 @@ +# $NetBSD: Makefile,v 1.12 2019/08/18 20:15:58 kamil Exp $ + +.include + +TESTSDIR= ${TESTSBASE}/usr.bin/c++ + +ASAN_TESTS= # +ASAN_TESTS+= t_asan_double_free +ASAN_TESTS+= t_asan_global_buffer_overflow +ASAN_TESTS+= t_asan_heap_overflow +ASAN_TESTS+= t_asan_off_by_one +ASAN_TESTS+= t_asan_poison +ASAN_TESTS+= t_asan_uaf + +UBSAN_TESTS= # +UBSAN_TESTS+= t_ubsan_int_add_overflow +UBSAN_TESTS+= t_ubsan_int_neg_overflow +UBSAN_TESTS+= t_ubsan_vla_out_of_bounds +UBSAN_TESTS+= t_ubsan_int_sub_overflow +UBSAN_TESTS+= t_ubsan_int_divzero + +TESTS_SH= # +TESTS_SH+= $(ASAN_TESTS) +TESTS_SH+= $(UBSAN_TESTS) +TESTS_SH+= t_call_once +TESTS_SH+= t_call_once2 +TESTS_SH+= t_cxxruntime +TESTS_SH+= t_hello +TESTS_SH+= t_pthread_once +TESTS_SH+= t_static_destructor + +TESTS_SH+= t_fuzzer_oom +TESTS_SH+= t_fuzzer_simple +TESTS_SH+= t_fuzzer_timeout + +TESTS_SH+= t_msan_allocated_memory +TESTS_SH+= t_msan_check_mem +TESTS_SH+= t_msan_free +TESTS_SH+= t_msan_heap +TESTS_SH+= t_msan_partial_poison +TESTS_SH+= t_msan_poison +TESTS_SH+= t_msan_realloc +TESTS_SH+= t_msan_shadow +TESTS_SH+= t_msan_stack +TESTS_SH+= t_msan_unpoison + +TESTS_SH+= t_tsan_data_race +TESTS_SH+= t_tsan_heap_use_after_free +TESTS_SH+= t_tsan_lock_order_inversion +TESTS_SH+= t_tsan_locked_mutex_destroy +TESTS_SH+= t_tsan_signal_errno +TESTS_SH+= t_tsan_thread_leak +TESTS_SH+= t_tsan_vptr_race + +.for test in ${ASAN_TESTS} +TESTS_SH_SRC_${test}= asan_common.subr ${test}.sh +.endfor +.for test in ${UBSAN_TESTS} +TESTS_SH_SRC_${test}= ubsan_common.subr ${test}.sh +.endfor + +.include diff --git a/usr.bin/c++/asan_common.subr b/usr.bin/c++/asan_common.subr new file mode 100644 --- /dev/null +++ b/usr.bin/c++/asan_common.subr @@ -0,0 +1,168 @@ +# $NetBSD: asan_common.subr,v 1.2 2019/01/29 20:07:03 mgorny Exp $ +# +# Copyright (c) 2018, 2019 The NetBSD Foundation, Inc. +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +SUPPORT='n' +test_target() { + if uname -m | grep -q "amd64"; then + SUPPORT='y' + fi + + if uname -m | grep -q "i386"; then + SUPPORT='y' + fi +} + +atf_test_case target_not_supported +target_not_supported_head() +{ + atf_set "descr" "Test forced skip" +} + +target_not_supported_body() +{ + atf_skip "Target is not supported" +} + +# Add a new test case, with head & body. +# asan_test_case +asan_test_case() { + atf_test_case "$1" + eval "$1_head() { + atf_set 'descr' 'compile and run \"$2\"' + atf_set 'require.progs' 'c++ paxctl' + }" + + atf_test_case "$1_profile" + eval "$1_head() { + atf_set 'descr' 'compile and run \"$2\" with profiling option' + atf_set 'require.progs' 'c++ paxctl' + }" + + atf_test_case "$1_pic" + eval "$1_head() { + atf_set 'descr' 'compile and run PIC \"$2\"' + atf_set 'require.progs' 'c++ paxctl' + }" + + atf_test_case "$1_pie" + eval "$1_head() { + atf_set 'descr' 'compile and run position independent (PIE) \"$2\"' + atf_set 'require.progs' 'c++ paxctl' + }" + + atf_test_case "${1}32" + eval "$1_head() { + atf_set 'descr' 'compile and run \"$2\" for/in netbsd32 emulation' + atf_set 'require.progs' 'c++ paxctl file diff cat' + }" + + eval "$1_body() { + echo \"\$ASAN_CODE\" > test.cpp + c++ -fsanitize=address -o test test.cpp + paxctl +a test + atf_check -s not-exit:0 -o not-match:'CHECK\n' -e match:'$3' ./test + } + + $1_profile_body() { + echo \"\$ASAN_CODE\" > test.cpp + c++ -fsanitize=address -o test -pg test.cpp + paxctl +a test + atf_check -s not-exit:0 -o not-match:'CHECK\n' -e match:'$3' ./test + } + + $1_pic_body() { + echo \"\$ASAN_CODE\" > test.cpp + c++ -DPIC_FOO -fsanitize=address -fPIC -shared -o libtest.so test.cpp + c++ -DPIC_MAIN -o test test.cpp -fsanitize=address -L. -ltest + paxctl +a test + + export LD_LIBRARY_PATH=. + atf_check -s not-exit:0 -o not-match:'CHECK\n' -e match:'$3' ./test + } + + $1_pie_body() { + # check whether this arch supports -pice + if ! c++ -pie -dM -E - < /dev/null 2>/dev/null >/dev/null; then + atf_set_skip 'c++ -pie not supported on this architecture' + fi + echo \"\$ASAN_CODE\" > test.cpp + c++ -fsanitize=address -o test -fpie -pie test.cpp + paxctl +a test + atf_check -s not-exit:0 -o not-match:'CHECK\n' -e match:'$3' ./test + } + + ${1}32_body() { + # check whether this arch is 64bit + if ! c++ -dM -E - < /dev/null | fgrep -q _LP64; then + atf_skip 'this is not a 64 bit architecture' + fi + if ! c++ -m32 -dM -E - < /dev/null 2>/dev/null > ./def32; then + atf_skip 'c++ -m32 not supported on this architecture' + else + if fgrep -q _LP64 ./def32; then + atf_fail 'c++ -m32 does not generate netbsd32 binaries' + fi + fi + + echo \"\$ASAN_CODE\" > test.cpp + c++ -fsanitize=address -o df32 -m32 test.cpp + c++ -fsanitize=address -o df64 test.cpp + file -b ./df32 > ./ftype32 + file -b ./df64 > ./ftype64 + if diff ./ftype32 ./ftype64 >/dev/null; then + atf_fail 'generated binaries do not differ' + fi + echo '32bit binaries on this platform are:' + cat ./ftype32 + echo 'While native (64bit) binaries are:' + cat ./ftype64 + paxctl +a df32 + atf_check -s not-exit:0 -o not-match:'CHECK\n' -e match:'$3' ./df32 + +# and another test with profile 32bit binaries + c++ -fsanitize=address -o test -pg -m32 test.cpp + paxctl +a test + atf_check -s not-exit:0 -o not-match:'CHECK\n' -e match:'$3' ./test + }" +} + +asan_add_test_cases() { + test_target + test $SUPPORT = 'n' && { + atf_add_test_case target_not_supported + return 0 + } + + atf_add_test_case "$1" +# atf_add_test_case "$1_profile" + atf_add_test_case "$1_pic" + atf_add_test_case "$1_pie" +# atf_add_test_case "${1}32" + # static option not supported + # -static and -fsanitize=address can't be used together for compilation + # (gcc version 5.4.0 and clang 7.1) tested on April 2nd 2018. +} diff --git a/usr.bin/c++/t_asan_double_free.sh b/usr.bin/c++/t_asan_double_free.sh new file mode 100644 --- /dev/null +++ b/usr.bin/c++/t_asan_double_free.sh @@ -0,0 +1,49 @@ +# $NetBSD: t_asan_double_free.sh,v 1.3 2019/01/29 20:02:34 mgorny Exp $ +# +# Copyright (c) 2018, 2019 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Siddharth Muralee. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +ASAN_CODE=' +#include +#include +#include +void foo(int); +#ifndef PIC_MAIN +void foo(int index) { char *x = (char*)malloc(10 * sizeof(char)); memset(x, 0, 10); free(x); free(x - index); } +#endif +#ifndef PIC_FOO +int main(int argc, char **argv) { foo(argc - 1); printf("CHECK\n"); exit(0); } +#endif +' + +asan_test_case double_free "Double Free example" double-free + +atf_init_test_cases() +{ + asan_add_test_cases double_free +} diff --git a/usr.bin/c++/t_asan_global_buffer_overflow.sh b/usr.bin/c++/t_asan_global_buffer_overflow.sh new file mode 100644 --- /dev/null +++ b/usr.bin/c++/t_asan_global_buffer_overflow.sh @@ -0,0 +1,50 @@ +# $NetBSD: t_asan_global_buffer_overflow.sh,v 1.3 2019/01/29 20:02:34 mgorny Exp $ +# +# Copyright (c) 2018, 2019 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Siddharth Muralee. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +ASAN_CODE=' +#include +#include +int arr[5] = {-1}; +void foo(int); +#ifndef PIC_MAIN +void foo(int index) { arr[index] = 0; } +#endif +#ifndef PIC_FOO +int main(int argc, char **argv) {foo(argc + 5); printf("CHECK\n"); exit(0);} +#endif +' + +asan_test_case global_buffer_overflow "Global Buffer Overflow example" \ + global-buffer-overflow + +atf_init_test_cases() +{ + asan_add_test_cases global_buffer_overflow +} diff --git a/usr.bin/c++/t_asan_heap_overflow.sh b/usr.bin/c++/t_asan_heap_overflow.sh new file mode 100644 --- /dev/null +++ b/usr.bin/c++/t_asan_heap_overflow.sh @@ -0,0 +1,49 @@ +# $NetBSD: t_asan_heap_overflow.sh,v 1.3 2019/01/29 20:02:34 mgorny Exp $ +# +# Copyright (c) 2018, 2019 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Siddharth Muralee. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +ASAN_CODE=' +#include +#include +#include +int foo(int); +#ifndef PIC_MAIN +int foo(int index) { int *x = (int *)malloc(20); int res = x[index * 4]; free(x); return res;} +#endif +#ifndef PIC_FOO +int main(int argc, char **argv) {foo(argc + 19); printf("CHECK\n"); exit(0);} +#endif +' + +asan_test_case heap_overflow "Heap Overflow example" heap-buffer-overflow + +atf_init_test_cases() +{ + asan_add_test_cases heap_overflow +} diff --git a/usr.bin/c++/t_asan_off_by_one.sh b/usr.bin/c++/t_asan_off_by_one.sh new file mode 100644 --- /dev/null +++ b/usr.bin/c++/t_asan_off_by_one.sh @@ -0,0 +1,53 @@ +# $NetBSD: t_asan_off_by_one.sh,v 1.3 2019/01/29 20:02:34 mgorny Exp $ +# +# Copyright (c) 2018, 2019 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Siddharth Muralee. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +ASAN_CODE=' +#include +#include +void foo(); +#ifndef PIC_MAIN +void foo() { + int arr[5]; + for (int i = 0; i <= 5 ; i++) { + arr[i] = 0; + } +} +#endif +#ifndef PIC_FOO +int main() {foo(); printf("CHECK\n"); exit(0);} +#endif +' + +asan_test_case off_by_one "Off by one example" stack-buffer-overflow + +atf_init_test_cases() +{ + asan_add_test_cases off_by_one +} diff --git a/usr.bin/c++/t_asan_poison.sh b/usr.bin/c++/t_asan_poison.sh new file mode 100644 --- /dev/null +++ b/usr.bin/c++/t_asan_poison.sh @@ -0,0 +1,60 @@ +# $NetBSD: t_asan_poison.sh,v 1.3 2019/01/29 20:02:34 mgorny Exp $ +# +# Copyright (c) 2018, 2019 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Siddharth Muralee. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +ASAN_CODE=' +#include +#include +#include +int foo(); +#ifndef PIC_MAIN +int foo() { + int p = 2; + int *a; + ASAN_POISON_MEMORY_REGION(&p, sizeof(int)); + a=&p; + printf("%d", *a); +} +#endif + +#ifndef PIC_FOO +int main() { + foo(); + printf("CHECK\n"); + exit(0); +} +#endif +' + +asan_test_case poison 'Use after Poison example' use-after-poison + +atf_init_test_cases() +{ + asan_add_test_cases poison +} diff --git a/usr.bin/c++/t_asan_uaf.sh b/usr.bin/c++/t_asan_uaf.sh new file mode 100644 --- /dev/null +++ b/usr.bin/c++/t_asan_uaf.sh @@ -0,0 +1,48 @@ +# $NetBSD: t_asan_uaf.sh,v 1.3 2019/01/29 20:02:34 mgorny Exp $ +# +# Copyright (c) 2018, 2019 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Siddharth Muralee. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +ASAN_CODE=' +#include +#include +int foo(); +#ifndef PIC_MAIN +int foo() {int *x = (int *)malloc(10 * sizeof(int)); free(x); return x[0];} +#endif +#ifndef PIC_FOO +int main() {foo(); printf("CHECK\n"); exit(0);} +#endif +' + +asan_test_case uaf "Use After Free example" heap-use-after-free + +atf_init_test_cases() +{ + asan_add_test_cases uaf +} diff --git a/usr.bin/c++/t_call_once.sh b/usr.bin/c++/t_call_once.sh new file mode 100644 --- /dev/null +++ b/usr.bin/c++/t_call_once.sh @@ -0,0 +1,372 @@ +# $NetBSD: t_call_once.sh,v 1.4 2020/10/13 06:49:27 rin Exp $ +# +# Copyright (c) 2018 The NetBSD Foundation, Inc. +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +atf_test_case call_once +call_once_head() { + atf_set "descr" "compile and run std::call_once" + atf_set "require.progs" "c++" +} + +atf_test_case call_once_profile +call_once_profile_head() { + atf_set "descr" "compile and run std::call_once with profiling option" + atf_set "require.progs" "c++" +} + +atf_test_case call_once_pic +call_once_pic_head() { + atf_set "descr" "compile and run PIC std::call_once" + atf_set "require.progs" "c++" +} + +atf_test_case call_once_pic_32 +call_once_pic_32_head() { + atf_set "descr" "compile and run 32-bit PIC std::call_once" + atf_set "require.progs" "c++" +} + +atf_test_case call_once_pic_profile +call_once_pic_head() { + atf_set "descr" "compile and run PIC std::call_once with profiling flag" + atf_set "require.progs" "c++" +} + +atf_test_case call_once_pic_profile_32 +call_once_pic_profile_32_head() { + atf_set "descr" "compile and run 32-bit PIC std::call_once with profiling flag" + atf_set "require.progs" "c++" +} + +atf_test_case call_once_profile_32 +call_once_profile_32_head() { + atf_set "descr" "compile and run 32-bit std::call_once with profiling flag" + atf_set "require.progs" "c++" +} + +atf_test_case call_once_pie +call_once_pie_head() { + atf_set "descr" "compile and run position independent (PIE) std::call_once" + atf_set "require.progs" "c++" +} + +atf_test_case call_once_32 +call_once_32_head() { + atf_set "descr" "compile and run std::call_once for/in netbsd32 emulation" + atf_set "require.progs" "c++ file diff cat" +} + +atf_test_case call_once_static +call_once_static_head() { + atf_set "descr" "compile and run std::call_once with static flag" + atf_set "require.progs" "c++" +} + +call_once_body() { + cat > test.cpp << EOF +#include +#include +#include +std::once_flag flag; +int main(void) { + std::call_once(flag, [](){ printf("hello, world!\n"); }); + return 0; +} +EOF + atf_check -s exit:0 -o ignore -e ignore c++ -o call_once test.cpp -pthread + atf_check -s exit:0 -o inline:"hello, world!\n" ./call_once +} + +call_once_profile_body() { + cat > test.cpp << EOF +#include +#include +#include +std::once_flag flag; +int main(void) { + std::call_once(flag, [](){ printf("hello, world!\n"); }); + return 0; +} +EOF + atf_check -s exit:0 -o ignore -e ignore c++ -pg -o call_once test.cpp -pthread + atf_check -s exit:0 -o inline:"hello, world!\n" ./call_once +} + +call_once_profile_32_body() { + # check whether this arch is 64bit + if ! c++ -dM -E - < /dev/null | fgrep -q _LP64; then + atf_skip "this is not a 64 bit architecture" + fi + if ! c++ -m32 -dM -E - < /dev/null 2>/dev/null > ./def32; then + atf_skip "c++ -m32 not supported on this architecture" + else + if fgrep -q _LP64 ./def32; then + atf_fail "c++ -m32 does not generate netbsd32 binaries" + fi + fi + + cat > test.cpp << EOF +#include +#include +#include +std::once_flag flag; +int main(void) { + std::call_once(flag, [](){ printf("hello, world!\n"); }); + return 0; +} +EOF + atf_check -s exit:0 -o ignore -e ignore c++ -m32 -pg -o call_once test.cpp -pthread + atf_check -s exit:0 -o inline:"hello, world!\n" ./call_once +} + +call_once_pic_body() { + cat > test.cpp << EOF +#include +int callpic(void); +int main(void) {callpic();exit(0);} +EOF + cat > pic.cpp << EOF +#include +#include +#include +std::once_flag flag; +int callpic(void) { + std::call_once(flag, [](){ printf("hello, world!\n"); }); + return 0; +} +EOF + + atf_check -s exit:0 -o ignore -e ignore \ + c++ -fPIC -shared -o libtest.so pic.cpp + atf_check -s exit:0 -o ignore -e ignore \ + c++ -o call_once test.cpp -L. -ltest -pthread + + export LD_LIBRARY_PATH=. + atf_check -s exit:0 -o inline:"hello, world!\n" ./call_once +} + +call_once_pic_32_body() { + # check whether this arch is 64bit + if ! c++ -dM -E - < /dev/null | fgrep -q _LP64; then + atf_skip "this is not a 64 bit architecture" + fi + if ! c++ -m32 -dM -E - < /dev/null 2>/dev/null > ./def32; then + atf_skip "c++ -m32 not supported on this architecture" + else + if fgrep -q _LP64 ./def32; then + atf_fail "c++ -m32 does not generate netbsd32 binaries" + fi + fi + + cat > test.cpp << EOF +#include +int callpic(void); +int main(void) {callpic();exit(0);} +EOF + cat > pic.cpp << EOF +#include +#include +#include +std::once_flag flag; +int callpic(void) { + std::call_once(flag, [](){ printf("hello, world!\n"); }); + return 0; +} +EOF + + atf_check -s exit:0 -o ignore -e ignore \ + c++ -m32 -fPIC -shared -o libtest.so pic.cpp + atf_check -s exit:0 -o ignore -e ignore \ + c++ -m32 -o call_once test.cpp -L. -ltest -pthread + + export LD_LIBRARY_PATH=. + atf_check -s exit:0 -o inline:"hello, world!\n" ./call_once +} + +call_once_pic_profile_body() { + cat > test.cpp << EOF +#include +int callpic(void); +int main(void) {callpic();exit(0);} +EOF + cat > pic.cpp << EOF +#include +#include +#include +std::once_flag flag; +int callpic(void) { + std::call_once(flag, [](){ printf("hello, world!\n"); }); + return 0; +} +EOF + + atf_check -s exit:0 -o ignore -e ignore \ + c++ -pg -fPIC -shared -o libtest.so pic.cpp + atf_check -s exit:0 -o ignore -e ignore \ + c++ -pg -o call_once test.cpp -L. -ltest -pthread + + export LD_LIBRARY_PATH=. + atf_check -s exit:0 -o inline:"hello, world!\n" ./call_once +} + +call_once_pic_profile_32_body() { + # check whether this arch is 64bit + if ! c++ -dM -E - < /dev/null | fgrep -q _LP64; then + atf_skip "this is not a 64 bit architecture" + fi + if ! c++ -m32 -dM -E - < /dev/null 2>/dev/null > ./def32; then + atf_skip "c++ -m32 not supported on this architecture" + else + if fgrep -q _LP64 ./def32; then + atf_fail "c++ -m32 does not generate netbsd32 binaries" + fi + fi + + cat > test.cpp << EOF +#include +int callpic(void); +int main(void) {callpic();exit(0);} +EOF + cat > pic.cpp << EOF +#include +#include +#include +std::once_flag flag; +int callpic(void) { + std::call_once(flag, [](){ printf("hello, world!\n"); }); + return 0; +} +EOF + + atf_check -s exit:0 -o ignore -e ignore \ + c++ -m32 -pg -fPIC -shared -o libtest.so pic.cpp + atf_check -s exit:0 -o ignore -e ignore \ + c++ -m32 -pg -o call_once test.cpp -L. -ltest -pthread + + export LD_LIBRARY_PATH=. + atf_check -s exit:0 -o inline:"hello, world!\n" ./call_once +} + +call_once_pie_body() { + # check whether this arch supports -pie + if ! c++ -pie -dM -E - < /dev/null 2>/dev/null >/dev/null; then + atf_skip "c++ -pie not supported on this architecture" + fi + cat > test.cpp << EOF +#include +#include +#include +std::once_flag flag; +int main(void) { + std::call_once(flag, [](){ printf("hello, world!\n"); }); + return 0; +} +EOF + atf_check -s exit:0 -o ignore -e ignore c++ -fpie -pie -o call_once test.cpp -pthread + atf_check -s exit:0 -o inline:"hello, world!\n" ./call_once +} + +call_once_32_body() { + # check whether this arch is 64bit + if ! c++ -dM -E - < /dev/null | fgrep -q _LP64; then + atf_skip "this is not a 64 bit architecture" + fi + if ! c++ -m32 -dM -E - < /dev/null 2>/dev/null > ./def32; then + atf_skip "c++ -m32 not supported on this architecture" + else + if fgrep -q _LP64 ./def32; then + atf_fail "c++ -m32 does not generate netbsd32 binaries" + fi + fi + + cat > test.cpp << EOF +#include +#include +#include +std::once_flag flag; +int main(void) { + std::call_once(flag, [](){ printf("hello, world!\n"); }); + return 0; +} +EOF + atf_check -s exit:0 -o ignore -e ignore c++ -o call_once_32 -m32 test.cpp -pthread + atf_check -s exit:0 -o ignore -e ignore c++ -o call_once_64 test.cpp -pthread + file -b ./call_once_32 > ./ftype32 + file -b ./call_once_64 > ./ftype64 + if diff ./ftype32 ./ftype64 >/dev/null; then + atf_fail "generated binaries do not differ" + fi + echo "32bit binaries on this platform are:" + cat ./ftype32 + echo "While native (64bit) binaries are:" + cat ./ftype64 + atf_check -s exit:0 -o inline:"hello, world!\n" ./call_once_32 + + # do another test with static 32bit binaries + cat > test.cpp << EOF +#include +#include +#include +std::once_flag flag; +int main(void) { + std::call_once(flag, [](){ printf("hello, world!\n"); }); + return 0; +} +EOF + atf_check -s exit:0 -o ignore -e ignore c++ -o call_once -m32 -pthread \ + -static test.cpp + atf_check -s exit:0 -o inline:"hello, world!\n" ./call_once +} + +call_once_static_body() { + cat > test.cpp << EOF +#include +#include +#include +std::once_flag flag; +int main(void) { + std::call_once(flag, [](){ printf("hello, world!\n"); }); + return 0; +} +EOF + atf_check -s exit:0 -o ignore -e ignore c++ -static -o call_once test.cpp -pthread + atf_check -s exit:0 -o inline:"hello, world!\n" ./call_once +} + +atf_init_test_cases() +{ + + atf_add_test_case call_once + atf_add_test_case call_once_profile + atf_add_test_case call_once_pic + atf_add_test_case call_once_pie + atf_add_test_case call_once_32 + atf_add_test_case call_once_static + atf_add_test_case call_once_pic_32 + atf_add_test_case call_once_pic_profile + atf_add_test_case call_once_pic_profile_32 + atf_add_test_case call_once_profile_32 +} diff --git a/usr.bin/c++/t_call_once2.sh b/usr.bin/c++/t_call_once2.sh new file mode 100644 --- /dev/null +++ b/usr.bin/c++/t_call_once2.sh @@ -0,0 +1,553 @@ +# $NetBSD: t_call_once2.sh,v 1.2 2019/05/15 13:43:45 christos Exp $ +# +# Copyright (c) 2018 The NetBSD Foundation, Inc. +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +atf_test_case call_once2 +call_once2_head() { + atf_set "descr" "compile and run std::call_once" + atf_set "require.progs" "c++" +} + +atf_test_case call_once2_profile +call_once2_profile_head() { + atf_set "descr" "compile and run std::call_once with profiling option" + atf_set "require.progs" "c++" +} + +atf_test_case call_once2_pic +call_once2_pic_head() { + atf_set "descr" "compile and run PIC std::call_once" + atf_set "require.progs" "c++" +} + +atf_test_case call_once2_pic_32 +call_once2_pic_32_head() { + atf_set "descr" "compile and run 32-bit PIC std::call_once" + atf_set "require.progs" "c++" +} + +atf_test_case call_once2_pic_profile +call_once2_pic_head() { + atf_set "descr" "compile and run PIC std::call_once with profiling flag" + atf_set "require.progs" "c++" +} + +atf_test_case call_once2_pic_profile_32 +call_once2_pic_profile_32_head() { + atf_set "descr" "compile and run 32-bit PIC std::call_once with profiling flag" + atf_set "require.progs" "c++" +} + +atf_test_case call_once2_profile_32 +call_once2_profile_32_head() { + atf_set "descr" "compile and run 32-bit std::call_once with profiling flag" + atf_set "require.progs" "c++" +} + +atf_test_case call_once2_pie +call_once2_pie_head() { + atf_set "descr" "compile and run position independent (PIE) std::call_once" + atf_set "require.progs" "c++" +} + +atf_test_case call_once2_32 +call_once2_32_head() { + atf_set "descr" "compile and run std::call_once for/in netbsd32 emulation" + atf_set "require.progs" "c++ file diff cat" +} + +atf_test_case call_once2_static +call_once2_static_head() { + atf_set "descr" "compile and run std::call_once with static flag" + atf_set "require.progs" "c++" +} + +call_once2_body() { + cat > test.cpp << EOF +#include +#include +#include +std::once_flag flag, flag_throw; +void print_once(void) { std::call_once(flag, [](){ std::cout << "hello, " << std::flush; }); } +void throw_once(void) { throw std::exception(); } +int main(void) { + static const int nr_threads(4); + std::thread threads[nr_threads]; + + for (int i = 0; i < nr_threads; ++i) { + threads[i] = std::thread(print_once); + } + for (int i = 0; i < nr_threads; ++i) { + threads[i].join(); + } + + try { + std::call_once(flag_throw, throw_once); + } catch (...) { + std::cout << "world!" << std::endl; + } + return 0; +} +EOF + atf_check -s exit:0 -o ignore -e ignore c++ -o call_once2 test.cpp -pthread + atf_check -s exit:0 -o inline:"hello, world!\n" ./call_once2 +} + +call_once2_profile_body() { + atf_expect_fail "profiling option doesn't work with shared libraries" + cat > test.cpp << EOF +#include +#include +#include +std::once_flag flag, flag_throw; +void print_once(void) { std::call_once(flag, [](){ std::cout << "hello, " << std::flush; }); } +void throw_once(void) { throw std::exception(); } +int main(void) { + static const int nr_threads(4); + std::thread threads[nr_threads]; + + for (int i = 0; i < nr_threads; ++i) { + threads[i] = std::thread(print_once); + } + for (int i = 0; i < nr_threads; ++i) { + threads[i].join(); + } + + try { + std::call_once(flag_throw, throw_once); + } catch (...) { + std::cout << "world!" << std::endl; + } + return 0; +} +EOF + atf_check -s exit:0 -o ignore -e ignore c++ -pg -o call_once2 test.cpp -pthread + atf_check -s exit:0 -o inline:"hello, world!\n" ./call_once2 +} + +call_once2_profile_32_body() { + atf_expect_fail "profiling option doesn't work now" + # check whether this arch is 64bit + if ! c++ -dM -E - < /dev/null | fgrep -q _LP64; then + atf_skip "this is not a 64 bit architecture" + fi + if ! c++ -m32 -dM -E - < /dev/null 2>/dev/null > ./def32; then + atf_skip "c++ -m32 not supported on this architecture" + else + if fgrep -q _LP64 ./def32; then + atf_fail "c++ -m32 does not generate netbsd32 binaries" + fi + fi + + cat > test.cpp << EOF +#include +#include +#include +std::once_flag flag, flag_throw; +void print_once(void) { std::call_once(flag, [](){ std::cout << "hello, " << std::flush; }); } +void throw_once(void) { throw std::exception(); } +int main(void) { + static const int nr_threads(4); + std::thread threads[nr_threads]; + + for (int i = 0; i < nr_threads; ++i) { + threads[i] = std::thread(print_once); + } + for (int i = 0; i < nr_threads; ++i) { + threads[i].join(); + } + + try { + std::call_once(flag_throw, throw_once); + } catch (...) { + std::cout << "world!" << std::endl; + } + return 0; +} +EOF + atf_check -s exit:0 -o ignore -e ignore c++ -m32 -pg -o call_once2 test.cpp -pthread + atf_check -s exit:0 -o inline:"hello, world!\n" ./call_once2 + atf_expect_fail "The combination of 32-bit and profiling should be fail" +} + +call_once2_pic_body() { + cat > test.cpp << EOF +#include +int callpic(void); +int main(void) {callpic();exit(0);} +EOF + cat > pic.cpp << EOF +#include +#include +#include +std::once_flag flag, flag_throw; +void print_once(void) { std::call_once(flag, [](){ std::cout << "hello, " << std::flush; }); } +void throw_once(void) { throw std::exception(); } +int callpic(void) { + static const int nr_threads(4); + std::thread threads[nr_threads]; + + for (int i = 0; i < nr_threads; ++i) { + threads[i] = std::thread(print_once); + } + for (int i = 0; i < nr_threads; ++i) { + threads[i].join(); + } + + try { + std::call_once(flag_throw, throw_once); + } catch (...) { + std::cout << "world!" << std::endl; + } + return 0; +} +EOF + + atf_check -s exit:0 -o ignore -e ignore \ + c++ -fPIC -shared -o libtest.so pic.cpp + atf_check -s exit:0 -o ignore -e ignore \ + c++ -o call_once2 test.cpp -L. -ltest -pthread + + export LD_LIBRARY_PATH=. + atf_check -s exit:0 -o inline:"hello, world!\n" ./call_once2 +} + +call_once2_pic_32_body() { + # check whether this arch is 64bit + if ! c++ -dM -E - < /dev/null | fgrep -q _LP64; then + atf_skip "this is not a 64 bit architecture" + fi + if ! c++ -m32 -dM -E - < /dev/null 2>/dev/null > ./def32; then + atf_skip "c++ -m32 not supported on this architecture" + else + if fgrep -q _LP64 ./def32; then + atf_fail "c++ -m32 does not generate netbsd32 binaries" + fi + fi + + cat > test.cpp << EOF +#include +int callpic(void); +int main(void) {callpic();exit(0);} +EOF + cat > pic.cpp << EOF +#include +#include +#include +std::once_flag flag, flag_throw; +void print_once(void) { std::call_once(flag, [](){ std::cout << "hello, " << std::flush; }); } +void throw_once(void) { throw std::exception(); } +int callpic(void) { + static const int nr_threads(4); + std::thread threads[nr_threads]; + + for (int i = 0; i < nr_threads; ++i) { + threads[i] = std::thread(print_once); + } + for (int i = 0; i < nr_threads; ++i) { + threads[i].join(); + } + + try { + std::call_once(flag_throw, throw_once); + } catch (...) { + std::cout << "world!" << std::endl; + } + return 0; +} +EOF + + atf_check -s exit:0 -o ignore -e ignore \ + c++ -m32 -fPIC -shared -o libtest.so pic.cpp + atf_check -s exit:0 -o ignore -e ignore \ + c++ -m32 -o call_once2 test.cpp -L. -ltest -pthread + + export LD_LIBRARY_PATH=. + atf_check -s exit:0 -o inline:"hello, world!\n" ./call_once2 +} + +call_once2_pic_profile_body() { + atf_expect_fail "profiling option doesn't work with pic" + cat > test.cpp << EOF +#include +int callpic(void); +int main(void) {callpic();exit(0);} +EOF + cat > pic.cpp << EOF +#include +#include +#include +std::once_flag flag, flag_throw; +void print_once(void) { std::call_once(flag, [](){ std::cout << "hello, " << std::flush; }); } +void throw_once(void) { throw std::exception(); } +int callpic(void) { + static const int nr_threads(4); + std::thread threads[nr_threads]; + + for (int i = 0; i < nr_threads; ++i) { + threads[i] = std::thread(print_once); + } + for (int i = 0; i < nr_threads; ++i) { + threads[i].join(); + } + + try { + std::call_once(flag_throw, throw_once); + } catch (...) { + std::cout << "world!" << std::endl; + } + return 0; +} +EOF + + atf_check -s exit:0 -o ignore -e ignore \ + c++ -pg -fPIC -shared -o libtest.so pic.cpp + atf_check -s exit:0 -o ignore -e ignore \ + c++ -pg -o call_once2 test.cpp -L. -ltest -pthread + + export LD_LIBRARY_PATH=. + atf_check -s exit:0 -o inline:"hello, world!\n" ./call_once2 +} + +call_once2_pic_profile_32_body() { + atf_expect_fail "profiling option doesn't work with shared libraries" + # check whether this arch is 64bit + if ! c++ -dM -E - < /dev/null | fgrep -q _LP64; then + atf_skip "this is not a 64 bit architecture" + fi + if ! c++ -m32 -dM -E - < /dev/null 2>/dev/null > ./def32; then + atf_skip "c++ -m32 not supported on this architecture" + else + if fgrep -q _LP64 ./def32; then + atf_fail "c++ -m32 does not generate netbsd32 binaries" + fi + fi + + cat > test.cpp << EOF +#include +int callpic(void); +int main(void) {callpic();exit(0);} +EOF + cat > pic.cpp << EOF +#include +#include +#include +std::once_flag flag, flag_throw; +void print_once(void) { std::call_once(flag, [](){ std::cout << "hello, " << std::flush; }); } +void throw_once(void) { throw std::exception(); } +int callpic(void) { + static const int nr_threads(4); + std::thread threads[nr_threads]; + + for (int i = 0; i < nr_threads; ++i) { + threads[i] = std::thread(print_once); + } + for (int i = 0; i < nr_threads; ++i) { + threads[i].join(); + } + + try { + std::call_once(flag_throw, throw_once); + } catch (...) { + std::cout << "world!" << std::endl; + } + return 0; +} +EOF + + atf_check -s exit:0 -o ignore -e ignore \ + c++ -m32 -pg -fPIC -shared -o libtest.so pic.cpp + atf_check -s exit:0 -o ignore -e ignore \ + c++ -m32 -pg -o call_once2 test.cpp -L. -ltest -pthread + + export LD_LIBRARY_PATH=. + atf_check -s exit:0 -o inline:"hello, world!\n" ./call_once2 +} + +call_once2_pie_body() { + # check whether this arch supports -pie + if ! c++ -pie -dM -E - < /dev/null 2>/dev/null >/dev/null; then + atf_skip "c++ -pie not supported on this architecture" + fi + cat > test.cpp << EOF +#include +#include +#include +std::once_flag flag, flag_throw; +void print_once(void) { std::call_once(flag, [](){ std::cout << "hello, " << std::flush; }); } +void throw_once(void) { throw std::exception(); } +int main(void) { + static const int nr_threads(4); + std::thread threads[nr_threads]; + + for (int i = 0; i < nr_threads; ++i) { + threads[i] = std::thread(print_once); + } + for (int i = 0; i < nr_threads; ++i) { + threads[i].join(); + } + + try { + std::call_once(flag_throw, throw_once); + } catch (...) { + std::cout << "world!" << std::endl; + } + return 0; +} +EOF + atf_check -s exit:0 -o ignore -e ignore c++ -fpie -pie -o call_once2 test.cpp -pthread + atf_check -s exit:0 -o inline:"hello, world!\n" ./call_once2 +} + +call_once2_32_body() { + # check whether this arch is 64bit + if ! c++ -dM -E - < /dev/null | fgrep -q _LP64; then + atf_skip "this is not a 64 bit architecture" + fi + if ! c++ -m32 -dM -E - < /dev/null 2>/dev/null > ./def32; then + atf_skip "c++ -m32 not supported on this architecture" + else + if fgrep -q _LP64 ./def32; then + atf_fail "c++ -m32 does not generate netbsd32 binaries" + fi + fi + + cat > test.cpp << EOF +#include +#include +#include +std::once_flag flag, flag_throw; +void print_once(void) { std::call_once(flag, [](){ std::cout << "hello, " << std::flush; }); } +void throw_once(void) { throw std::exception(); } +int main(void) { + static const int nr_threads(4); + std::thread threads[nr_threads]; + + for (int i = 0; i < nr_threads; ++i) { + threads[i] = std::thread(print_once); + } + for (int i = 0; i < nr_threads; ++i) { + threads[i].join(); + } + + try { + std::call_once(flag_throw, throw_once); + } catch (...) { + std::cout << "world!" << std::endl; + } + return 0; +} +EOF + atf_check -s exit:0 -o ignore -e ignore c++ -o call_once2_32 -m32 test.cpp -pthread + atf_check -s exit:0 -o ignore -e ignore c++ -o call_once2_64 test.cpp -pthread + file -b ./call_once2_32 > ./ftype32 + file -b ./call_once2_64 > ./ftype64 + if diff ./ftype32 ./ftype64 >/dev/null; then + atf_fail "generated binaries do not differ" + fi + echo "32bit binaries on this platform are:" + cat ./ftype32 + echo "While native (64bit) binaries are:" + cat ./ftype64 + atf_check -s exit:0 -o inline:"hello, world!\n" ./call_once2_32 + + # do another test with static 32bit binaries + cat > test.cpp << EOF +#include +#include +#include +std::once_flag flag, flag_throw; +void print_once(void) { std::call_once(flag, [](){ std::cout << "hello, " << std::flush; }); } +void throw_once(void) { throw std::exception(); } +int main(void) { + static const int nr_threads(4); + std::thread threads[nr_threads]; + + for (int i = 0; i < nr_threads; ++i) { + threads[i] = std::thread(print_once); + } + for (int i = 0; i < nr_threads; ++i) { + threads[i].join(); + } + + try { + std::call_once(flag_throw, throw_once); + } catch (...) { + std::cout << "world!" << std::endl; + } + return 0; +} +EOF + atf_check -s exit:0 -o ignore -e ignore c++ -o call_once2 -m32 -pthread \ + -static test.cpp + atf_check -s exit:0 -o inline:"hello, world!\n" ./call_once2 +} + +call_once2_static_body() { + cat > test.cpp << EOF +#include +#include +#include +std::once_flag flag, flag_throw; +void print_once(void) { std::call_once(flag, [](){ std::cout << "hello, " << std::flush; }); } +void throw_once(void) { throw std::exception(); } +int main(void) { + static const int nr_threads(4); + std::thread threads[nr_threads]; + + for (int i = 0; i < nr_threads; ++i) { + threads[i] = std::thread(print_once); + } + for (int i = 0; i < nr_threads; ++i) { + threads[i].join(); + } + + try { + std::call_once(flag_throw, throw_once); + } catch (...) { + std::cout << "world!" << std::endl; + } + return 0; +} +EOF + atf_check -s exit:0 -o ignore -e ignore c++ -static -o call_once2 test.cpp -pthread + atf_check -s exit:0 -o inline:"hello, world!\n" ./call_once2 +} + +atf_init_test_cases() +{ + + atf_add_test_case call_once2 + atf_add_test_case call_once2_profile + atf_add_test_case call_once2_pic + atf_add_test_case call_once2_pie + atf_add_test_case call_once2_32 + atf_add_test_case call_once2_static + atf_add_test_case call_once2_pic_32 + atf_add_test_case call_once2_pic_profile + atf_add_test_case call_once2_pic_profile_32 + atf_add_test_case call_once2_profile_32 +} diff --git a/usr.bin/c++/t_cxxruntime.sh b/usr.bin/c++/t_cxxruntime.sh new file mode 100644 --- /dev/null +++ b/usr.bin/c++/t_cxxruntime.sh @@ -0,0 +1,315 @@ +# $NetBSD: t_cxxruntime.sh,v 1.5 2020/10/13 06:49:27 rin Exp $ +# +# Copyright (c) 2017 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Kamil Rytarowski. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +atf_test_case cxxruntime +cxxruntime_head() { + atf_set "descr" "compile and run \"hello world\"" + atf_set "require.progs" "c++" +} + +atf_test_case cxxruntime_profile +cxxruntime_profile_head() { + atf_set "descr" "compile and run \"hello world\" with profiling option" + atf_set "require.progs" "c++" +} + +atf_test_case cxxruntime_profile_32 +cxxruntime_profile_32_head() { + atf_set "descr" "compile and run 32-bit \"hello world\" with profiling option" + atf_set "require.progs" "c++" +} + +atf_test_case cxxruntime_static +cxxruntime_static_head() { + atf_set "descr" "compile and run \"hello world\" with static flags" + atf_set "require.progs" "c++" +} + +atf_test_case cxxruntime_pic +cxxruntime_pic_head() { + atf_set "descr" "compile and run PIC \"hello world\"" + atf_set "require.progs" "c++" +} + +atf_test_case cxxruntime_pic_32 +cxxruntime_pic_32_head() { + atf_set "descr" "compile and run 32-bit PIC \"hello world\"" + atf_set "require.progs" "c++" +} + +atf_test_case cxxruntime_pic_profile +cxxruntime_pic_profile_head() { + atf_set "descr" "compile and run PIC \"hello world\" with profiling option" + atf_set "require.progs" "c++" +} + +atf_test_case cxxruntime_pic_profile_32 +cxxruntime_pic_profile_32_head() { + atf_set "descr" "compile and run 32-bit PIC \"hello world\" with profiling option" + atf_set "require.progs" "c++" +} + +atf_test_case cxxruntime_pie +cxxruntime_pie_head() { + atf_set "descr" "compile and run position independent (PIE) \"hello world\"" + atf_set "require.progs" "c++" +} + +atf_test_case cxxruntime32 +cxxruntime32_head() { + atf_set "descr" "compile and run \"hello world\" for/in netbsd32 emulation" + atf_set "require.progs" "c++ file diff cat" +} + +cxxruntime_body() { + cat > test.cpp << EOF +#include +#include +int main(void) {std::cout << "hello world" << std::endl;exit(0);} +EOF + atf_check -s exit:0 -o ignore -e ignore c++ -o hello test.cpp + atf_check -s exit:0 -o inline:"hello world\n" ./hello +} + +cxxruntime_profile_body() { + cat > test.cpp << EOF +#include +#include +int main(void) {std::cout << "hello world" << std::endl;exit(0);} +EOF + atf_check -s exit:0 -o ignore -e ignore c++ -pg -o hello test.cpp + atf_check -s exit:0 -o inline:"hello world\n" ./hello +} + +cxxruntime_profile_32_body() { + # check whether this arch is 64bit + if ! c++ -dM -E - < /dev/null | fgrep -q _LP64; then + atf_skip "this is not a 64 bit architecture" + fi + if ! c++ -m32 -dM -E - < /dev/null 2>/dev/null > ./def32; then + atf_skip "c++ -m32 not supported on this architecture" + else + if fgrep -q _LP64 ./def32; then + atf_fail "c++ -m32 does not generate netbsd32 binaries" + fi + fi + + cat > test.cpp << EOF +#include +#include +int main(void) {std::cout << "hello world" << std::endl;exit(0);} +EOF + atf_check -s exit:0 -o ignore -e ignore c++ -m32 -pg -o hello test.cpp + atf_check -s exit:0 -o inline:"hello world\n" ./hello +} + +cxxruntime_static_body() { + cat > test.cpp << EOF +#include +#include +int main(void) {std::cout << "hello world" << std::endl;exit(0);} +EOF + atf_check -s exit:0 -o ignore -e ignore c++ -static -o hello test.cpp + atf_check -s exit:0 -o inline:"hello world\n" ./hello +} + +cxxruntime_pic_body() { + cat > test.cpp << EOF +#include +int callpic(void); +int main(void) {callpic();exit(0);} +EOF + cat > pic.cpp << EOF +#include +int callpic(void) {std::cout << "hello world" << std::endl;return 0;} +EOF + + atf_check -s exit:0 -o ignore -e ignore \ + c++ -fPIC -shared -o libtest.so pic.cpp + atf_check -s exit:0 -o ignore -e ignore \ + c++ -o hello test.cpp -L. -ltest + + export LD_LIBRARY_PATH=. + atf_check -s exit:0 -o inline:"hello world\n" ./hello +} + +cxxruntime_pic_32_body() { + # check whether this arch is 64bit + if ! c++ -dM -E - < /dev/null | fgrep -q _LP64; then + atf_skip "this is not a 64 bit architecture" + fi + if ! c++ -m32 -dM -E - < /dev/null 2>/dev/null > ./def32; then + atf_skip "c++ -m32 not supported on this architecture" + else + if fgrep -q _LP64 ./def32; then + atf_fail "c++ -m32 does not generate netbsd32 binaries" + fi + fi + + cat > test.cpp << EOF +#include +int callpic(void); +int main(void) {callpic();exit(0);} +EOF + cat > pic.cpp << EOF +#include +int callpic(void) {std::cout << "hello world" << std::endl;return 0;} +EOF + + atf_check -s exit:0 -o ignore -e ignore \ + c++ -m32 -fPIC -shared -o libtest.so pic.cpp + atf_check -s exit:0 -o ignore -e ignore \ + c++ -m32 -o hello test.cpp -L. -ltest + + export LD_LIBRARY_PATH=. + atf_check -s exit:0 -o inline:"hello world\n" ./hello +} + +cxxruntime_pic_profile_body() { + cat > test.cpp << EOF +#include +int callpic(void); +int main(void) {callpic();exit(0);} +EOF + cat > pic.cpp << EOF +#include +int callpic(void) {std::cout << "hello world" << std::endl;return 0;} +EOF + + atf_check -s exit:0 -o ignore -e ignore \ + c++ -pg -fPIC -shared -o libtest.so pic.cpp + atf_check -s exit:0 -o ignore -e ignore \ + c++ -pg -o hello test.cpp -L. -ltest + + export LD_LIBRARY_PATH=. + atf_check -s exit:0 -o inline:"hello world\n" ./hello +} + +cxxruntime_pic_profile_32_body() { + # check whether this arch is 64bit + if ! c++ -dM -E - < /dev/null | fgrep -q _LP64; then + atf_skip "this is not a 64 bit architecture" + fi + if ! c++ -m32 -dM -E - < /dev/null 2>/dev/null > ./def32; then + atf_skip "c++ -m32 not supported on this architecture" + else + if fgrep -q _LP64 ./def32; then + atf_fail "c++ -m32 does not generate netbsd32 binaries" + fi + fi + + cat > test.cpp << EOF +#include +int callpic(void); +int main(void) {callpic();exit(0);} +EOF + cat > pic.cpp << EOF +#include +int callpic(void) {std::cout << "hello world" << std::endl;return 0;} +EOF + + atf_check -s exit:0 -o ignore -e ignore \ + c++ -m32 -pg -fPIC -shared -o libtest.so pic.cpp + atf_check -s exit:0 -o ignore -e ignore \ + c++ -m32 -pg -o hello test.cpp -L. -ltest + + export LD_LIBRARY_PATH=. + atf_check -s exit:0 -o inline:"hello world\n" ./hello +} + +cxxruntime_pie_body() { + # check whether this arch supports -pie + if ! c++ -pie -dM -E - < /dev/null 2>/dev/null >/dev/null; then + atf_skip "c++ -pie not supported on this architecture" + fi + cat > test.cpp << EOF +#include +#include +int main(void) {std::cout << "hello world" << std::endl;exit(0);} +EOF + atf_check -s exit:0 -o ignore -e ignore c++ -fpie -pie -o hello test.cpp + atf_check -s exit:0 -o inline:"hello world\n" ./hello +} + +cxxruntime32_body() { + # check whether this arch is 64bit + if ! c++ -dM -E - < /dev/null | fgrep -q _LP64; then + atf_skip "this is not a 64 bit architecture" + fi + if ! c++ -m32 -dM -E - < /dev/null 2>/dev/null > ./def32; then + atf_skip "c++ -m32 not supported on this architecture" + else + if fgrep -q _LP64 ./def32; then + atf_fail "c++ -m32 does not generate netbsd32 binaries" + fi + fi + + cat > test.cpp << EOF +#include +#include +int main(void) {std::cout << "hello world" << std::endl;exit(0);} +EOF + atf_check -s exit:0 -o ignore -e ignore c++ -o hello32 -m32 test.cpp + atf_check -s exit:0 -o ignore -e ignore c++ -o hello64 test.cpp + file -b ./hello32 > ./ftype32 + file -b ./hello64 > ./ftype64 + if diff ./ftype32 ./ftype64 >/dev/null; then + atf_fail "generated binaries do not differ" + fi + echo "32bit binaries on this platform are:" + cat ./ftype32 + echo "While native (64bit) binaries are:" + cat ./ftype64 + atf_check -s exit:0 -o inline:"hello world\n" ./hello32 + + # do another test with static 32bit binaries + cat > test.cpp << EOF +#include +#include +int main(void) {std::cout << "hello static world" << std::endl;exit(0);} +EOF + atf_check -s exit:0 -o ignore -e ignore c++ -o hello -m32 \ + -static test.cpp + atf_check -s exit:0 -o inline:"hello static world\n" ./hello +} + +atf_init_test_cases() +{ + atf_add_test_case cxxruntime + atf_add_test_case cxxruntime_profile + atf_add_test_case cxxruntime_pic + atf_add_test_case cxxruntime_pie + atf_add_test_case cxxruntime32 + atf_add_test_case cxxruntime_static + atf_add_test_case cxxruntime_pic_32 + atf_add_test_case cxxruntime_pic_profile + atf_add_test_case cxxruntime_pic_profile_32 + atf_add_test_case cxxruntime_profile_32 +} diff --git a/usr.bin/c++/t_fuzzer_oom.sh b/usr.bin/c++/t_fuzzer_oom.sh new file mode 100644 --- /dev/null +++ b/usr.bin/c++/t_fuzzer_oom.sh @@ -0,0 +1,171 @@ +# Copyright (c) 2018 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Yang Zheng. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +test_target() +{ + SUPPORT='n' + if uname -m | grep -q "amd64" && command -v c++ >/dev/null 2>&1 && \ + ! echo __clang__ | c++ -E - | grep -q __clang__; then + # only clang with major version newer than 7 is supported + CLANG_MAJOR=`echo __clang_major__ | c++ -E - | grep -o '^[[:digit:]]'` + if [ "$CLANG_MAJOR" -ge "7" ]; then + SUPPORT='y' + fi + fi +} + +atf_test_case oom +oom_head() { + atf_set "descr" "Test thread sanitizer for out-of-memory condition" + atf_set "require.progs" "c++ paxctl" +} + +atf_test_case oom_profile +oom_profile_head() { + atf_set "descr" "Test thread sanitizer for out-of-memory with profiling option" + atf_set "require.progs" "c++ paxctl" +} +atf_test_case oom_pic +oom_pic_head() { + atf_set "descr" "Test thread sanitizer for out-of-memory with position independent code (PIC) flag" + atf_set "require.progs" "c++ paxctl" +} +atf_test_case oom_pie +oom_pie_head() { + atf_set "descr" "Test thread sanitizer for out-of-memory with position independent execution (PIE) flag" + atf_set "require.progs" "c++ paxctl" +} + +oom_body(){ + cat > test.cc << EOF +#include +#include +#include + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + if (size > 0 && data[0] == 'b') while (1) malloc(16*1024*1024); + return 0; +} +EOF + + c++ -fsanitize=fuzzer -o test test.cc + paxctl +a test + atf_check -s ignore -o ignore -e match:"ERROR: libFuzzer: out-of-memory" ./test -rss_limit_mb=30 +} + +oom_profile_body(){ + cat > test.cc << EOF +#include +#include +#include + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + if (size > 0 && data[0] == 'b') while (1) malloc(16*1024*1024); + return 0; +} +EOF + + c++ -fsanitize=fuzzer -o test -pg test.cc + paxctl +a test + atf_check -s ignore -o ignore -e match:"ERROR: libFuzzer: out-of-memory" ./test -rss_limit_mb=30 +} + +oom_pic_body(){ + cat > test.cc << EOF +#include +#include +int help(const uint8_t *data, size_t size); +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + return help(data, size); +} +EOF + + cat > pic.cc << EOF +#include +#include +#include + +int help(const uint8_t *data, size_t size) { + if (size > 0 && data[0] == 'b') while (1) malloc(16*1024*1024); + return 0; +} +EOF + + c++ -fsanitize=fuzzer -fPIC -shared -o libtest.so pic.cc + c++ -o test test.cc -fsanitize=fuzzer -L. -ltest + paxctl +a test + + export LD_LIBRARY_PATH=. + atf_check -s ignore -o ignore -e match:"ERROR: libFuzzer: out-of-memory" ./test -rss_limit_mb=30 +} +oom_pie_body(){ + + #check whether -pie flag is supported on this architecture + if ! c++ -pie -dM -E - < /dev/null 2>/dev/null >/dev/null; then + atf_set_skip "c++ -pie not supported on this architecture" + fi + cat > test.cc << EOF +#include +#include +#include + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + if (size > 0 && data[0] == 'b') while (1) malloc(16*1024*1024); + return 0; +} +EOF + + c++ -fsanitize=fuzzer -o test -fpie -pie test.cc + paxctl +a test + atf_check -s ignore -o ignore -e match:"ERROR: libFuzzer: out-of-memory" ./test -rss_limit_mb=30 +} + + +atf_test_case target_not_supported +target_not_supported_head() +{ + atf_set "descr" "Test forced skip" +} + +target_not_supported_body() +{ + atf_skip "Target is not supported" +} + +atf_init_test_cases() +{ + test_target + test $SUPPORT = 'n' && { + atf_add_test_case target_not_supported + return 0 + } + atf_add_test_case oom + atf_add_test_case oom_profile + atf_add_test_case oom_pie + atf_add_test_case oom_pic +} diff --git a/usr.bin/c++/t_fuzzer_simple.sh b/usr.bin/c++/t_fuzzer_simple.sh new file mode 100644 --- /dev/null +++ b/usr.bin/c++/t_fuzzer_simple.sh @@ -0,0 +1,187 @@ +# Copyright (c) 2018 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Yang Zheng. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +test_target() +{ + SUPPORT='n' + if uname -m | grep -q "amd64" && command -v c++ >/dev/null 2>&1 && \ + ! echo __clang__ | c++ -E - | grep -q __clang__; then + # only clang with major version newer than 7 is supported + CLANG_MAJOR=`echo __clang_major__ | c++ -E - | grep -o '^[[:digit:]]'` + if [ "$CLANG_MAJOR" -ge "7" ]; then + SUPPORT='y' + fi + fi +} + +atf_test_case simple +simple_head() { + atf_set "descr" "Test thread sanitizer for error exit condition" + atf_set "require.progs" "c++ paxctl" +} + +atf_test_case simple_profile +simple_profile_head() { + atf_set "descr" "Test thread sanitizer for simple with profiling option" + atf_set "require.progs" "c++ paxctl" +} +atf_test_case simple_pic +simple_pic_head() { + atf_set "descr" "Test thread sanitizer for simple with position independent code (PIC) flag" + atf_set "require.progs" "c++ paxctl" +} +atf_test_case simple_pie +simple_pie_head() { + atf_set "descr" "Test thread sanitizer for simple with position independent execution (PIE) flag" + atf_set "require.progs" "c++ paxctl" +} + +simple_body(){ + cat > test.cc << EOF +#include +#include +#include + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + if (size > 0 && data[0] == 'b') { + fprintf(stderr, "BINGO\n"); + exit(1); + } + + return 0; +} +EOF + + c++ -fsanitize=fuzzer -o test test.cc + paxctl +a test + atf_check -s ignore -o ignore -e match:"BINGO" ./test +} + +simple_profile_body(){ + cat > test.cc << EOF +#include +#include +#include + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + if (size > 0 && data[0] == 'b') { + fprintf(stderr, "BINGO\n"); + exit(1); + } + + return 0; +} +EOF + + c++ -fsanitize=fuzzer -o test -pg test.cc + paxctl +a test + atf_check -s ignore -o ignore -e match:"BINGO" ./test +} + +simple_pic_body(){ + cat > test.cc << EOF +#include +#include +int help(const uint8_t *data, size_t size); +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + return help(data, size); +} +EOF + + cat > pic.cc << EOF +#include +#include +#include + +int help(const uint8_t *data, size_t size) { + if (size > 0 && data[0] == 'b') { + fprintf(stderr, "BINGO\n"); + exit(1); + } + + return 0; +} +EOF + + c++ -fsanitize=fuzzer -fPIC -shared -o libtest.so pic.cc + c++ -o test test.cc -fsanitize=fuzzer -L. -ltest + paxctl +a test + + export LD_LIBRARY_PATH=. + atf_check -s ignore -o ignore -e match:"BINGO" ./test +} +simple_pie_body(){ + + #check whether -pie flag is supported on this architecture + if ! c++ -pie -dM -E - < /dev/null 2>/dev/null >/dev/null; then + atf_set_skip "c++ -pie not supported on this architecture" + fi + cat > test.cc << EOF +#include +#include +#include + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + if (size > 0 && data[0] == 'b') { + fprintf(stderr, "BINGO\n"); + exit(1); + } + + return 0; +} +EOF + + c++ -fsanitize=fuzzer -o test -fpie -pie test.cc + paxctl +a test + atf_check -s ignore -o ignore -e match:"BINGO" ./test +} + + +atf_test_case target_not_supported +target_not_supported_head() +{ + atf_set "descr" "Test forced skip" +} + +target_not_supported_body() +{ + atf_skip "Target is not supported" +} + +atf_init_test_cases() +{ + test_target + test $SUPPORT = 'n' && { + atf_add_test_case target_not_supported + return 0 + } + atf_add_test_case simple + atf_add_test_case simple_profile + atf_add_test_case simple_pie + atf_add_test_case simple_pic +} diff --git a/usr.bin/c++/t_fuzzer_timeout.sh b/usr.bin/c++/t_fuzzer_timeout.sh new file mode 100644 --- /dev/null +++ b/usr.bin/c++/t_fuzzer_timeout.sh @@ -0,0 +1,167 @@ +# Copyright (c) 2018 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Yang Zheng. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +test_target() +{ + SUPPORT='n' + if uname -m | grep -q "amd64" && command -v c++ >/dev/null 2>&1 && \ + ! echo __clang__ | c++ -E - | grep -q __clang__; then + # only clang with major version newer than 7 is supported + CLANG_MAJOR=`echo __clang_major__ | c++ -E - | grep -o '^[[:digit:]]'` + if [ "$CLANG_MAJOR" -ge "7" ]; then + SUPPORT='y' + fi + fi +} + +atf_test_case timeout +timeout_head() { + atf_set "descr" "Test thread sanitizer for timeout condition" + atf_set "require.progs" "c++ paxctl" +} + +atf_test_case timeout_profile +timeout_profile_head() { + atf_set "descr" "Test thread sanitizer for timeout with profiling option" + atf_set "require.progs" "c++ paxctl" +} +atf_test_case timeout_pic +timeout_pic_head() { + atf_set "descr" "Test thread sanitizer for timeout with position independent code (PIC) flag" + atf_set "require.progs" "c++ paxctl" +} +atf_test_case timeout_pie +timeout_pie_head() { + atf_set "descr" "Test thread sanitizer for timeout with position independent execution (PIE) flag" + atf_set "require.progs" "c++ paxctl" +} + +timeout_body(){ + cat > test.cc << EOF +#include +#include + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + if (size > 0 && data[0] == 'b') while (1) ; + return 0; +} +EOF + + c++ -fsanitize=fuzzer -o test test.cc + paxctl +a test + atf_check -s ignore -o ignore -e match:"ERROR: libFuzzer: timeout" ./test -timeout=5 +} + +timeout_profile_body(){ + cat > test.cc << EOF +#include +#include + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + if (size > 0 && data[0] == 'b') while (1) ; + return 0; +} +EOF + + c++ -fsanitize=fuzzer -o test -pg test.cc + paxctl +a test + atf_check -s ignore -o ignore -e match:"ERROR: libFuzzer: timeout" ./test -timeout=5 +} + +timeout_pic_body(){ + cat > test.cc << EOF +#include +#include +int help(const uint8_t *data, size_t size); +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + return help(data, size); +} +EOF + + cat > pic.cc << EOF +#include +#include + +int help(const uint8_t *data, size_t size) { + if (size > 0 && data[0] == 'b') while (1) ; + return 0; +} +EOF + + c++ -fsanitize=fuzzer -fPIC -shared -o libtest.so pic.cc + c++ -o test test.cc -fsanitize=fuzzer -L. -ltest + paxctl +a test + + export LD_LIBRARY_PATH=. + atf_check -s ignore -o ignore -e match:"ERROR: libFuzzer: timeout" ./test -timeout=5 +} +timeout_pie_body(){ + + #check whether -pie flag is supported on this architecture + if ! c++ -pie -dM -E - < /dev/null 2>/dev/null >/dev/null; then + atf_set_skip "c++ -pie not supported on this architecture" + fi + cat > test.cc << EOF +#include +#include + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + if (size > 0 && data[0] == 'b') while (1) ; + return 0; +} +EOF + + c++ -fsanitize=fuzzer -o test -fpie -pie test.cc + paxctl +a test + atf_check -s ignore -o ignore -e match:"ERROR: libFuzzer: timeout" ./test -timeout=5 +} + + +atf_test_case target_not_supported +target_not_supported_head() +{ + atf_set "descr" "Test forced skip" +} + +target_not_supported_body() +{ + atf_skip "Target is not supported" +} + +atf_init_test_cases() +{ + test_target + test $SUPPORT = 'n' && { + atf_add_test_case target_not_supported + return 0 + } + atf_add_test_case timeout + atf_add_test_case timeout_profile + atf_add_test_case timeout_pie + atf_add_test_case timeout_pic +} diff --git a/usr.bin/c++/t_hello.sh b/usr.bin/c++/t_hello.sh new file mode 100644 --- /dev/null +++ b/usr.bin/c++/t_hello.sh @@ -0,0 +1,310 @@ +# $NetBSD: t_hello.sh,v 1.5 2020/10/13 06:49:27 rin Exp $ +# +# Copyright (c) 2011 The NetBSD Foundation, Inc. +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +atf_test_case hello +hello_head() { + atf_set "descr" "compile and run \"hello world\"" + atf_set "require.progs" "c++" +} + +atf_test_case hello_profile +hello_profile_head() { + atf_set "descr" "compile and run \"hello world\" with profiling option" + atf_set "require.progs" "c++" +} + +atf_test_case hello_profile +hello_profile_32_head() { + atf_set "descr" "compile and run 32-bit \"hello world\" with profiling option" + atf_set "require.progs" "c++" +} + +atf_test_case hello_static +hello_static_head() { + atf_set "descr" "compile and run \"hello world\" with static option" + atf_set "require.progs" "c++" +} + +atf_test_case hello_pic +hello_pic_head() { + atf_set "descr" "compile and run PIC \"hello world\"" + atf_set "require.progs" "c++" +} + +atf_test_case hello_pic_32 +hello_pic_32_head() { + atf_set "descr" "compile and run 32-bit PIC \"hello world\"" + atf_set "require.progs" "c++" +} + +atf_test_case hello_pic_profile +hello_pic_profile_head() { + atf_set "descr" "compile and run PIC \"hello world\" with profiling option" + atf_set "require.progs" "c++" +} + +atf_test_case hello_pic_profile_32 +hello_pic_profile_32_head() { + atf_set "descr" "compile and run 32-bit PIC \"hello world\" with profiling option" + atf_set "require.progs" "c++" +} + +atf_test_case hello_pie +hello_pie_head() { + atf_set "descr" "compile and run position independent (PIE) \"hello world\"" + atf_set "require.progs" "c++" +} + +atf_test_case hello32 +hello32_head() { + atf_set "descr" "compile and run \"hello world\" for/in netbsd32 emulation" + atf_set "require.progs" "c++ file diff cat" +} + +hello_body() { + cat > test.cpp << EOF +#include +#include +int main(void) {printf("hello world\n");exit(0);} +EOF + atf_check -s exit:0 -o ignore -e ignore c++ -o hello test.cpp + atf_check -s exit:0 -o inline:"hello world\n" ./hello +} + +hello_profile_body() { + cat > test.cpp << EOF +#include +#include +int main(void) {printf("hello world\n");exit(0);} +EOF + atf_check -s exit:0 -o ignore -e ignore c++ -pg -o hello test.cpp + atf_check -s exit:0 -o inline:"hello world\n" ./hello +} + +hello_profile_32_body() { + if ! c++ -dM -E - < /dev/null | fgrep -q _LP64; then + atf_skip "this is not a 64 bit architecture" + fi + if ! c++ -m32 -dM -E - < /dev/null 2>/dev/null > ./def32; then + atf_skip "c++ -m32 not supported on this architecture" + else + if fgrep -q _LP64 ./def32; then + atf_fail "c++ -m32 does not generate netbsd32 binaries" + fi + fi + + cat > test.cpp << EOF +#include +#include +int main(void) {printf("hello world\n");exit(0);} +EOF + atf_check -s exit:0 -o ignore -e ignore c++ -m32 -pg -o hello test.cpp + atf_check -s exit:0 -o inline:"hello world\n" ./hello +} + + +hello_static_body() { + cat > test.cpp << EOF +#include +#include +int main(void) {printf("hello world\n");exit(0);} +EOF + atf_check -s exit:0 -o ignore -e ignore c++ -static -o hello test.cpp + atf_check -s exit:0 -o inline:"hello world\n" ./hello +} + +hello_pic_body() { + cat > test.cpp << EOF +#include +int callpic(void); +int main(void) {callpic();exit(0);} +EOF + cat > pic.cpp << EOF +#include +int callpic(void) {printf("hello world\n");return 0;} +EOF + + atf_check -s exit:0 -o ignore -e ignore \ + c++ -fPIC -shared -o libtest.so pic.cpp + atf_check -s exit:0 -o ignore -e ignore \ + c++ -o hello test.cpp -L. -ltest + + export LD_LIBRARY_PATH=. + atf_check -s exit:0 -o inline:"hello world\n" ./hello +} + +hello_pic_32_body() { + if ! c++ -dM -E - < /dev/null | fgrep -q _LP64; then + atf_skip "this is not a 64 bit architecture" + fi + if ! c++ -m32 -dM -E - < /dev/null 2>/dev/null > ./def32; then + atf_skip "c++ -m32 not supported on this architecture" + else + if fgrep -q _LP64 ./def32; then + atf_fail "c++ -m32 does not generate netbsd32 binaries" + fi + fi + cat > test.cpp << EOF +#include +int callpic(void); +int main(void) {callpic();exit(0);} +EOF + cat > pic.cpp << EOF +#include +int callpic(void) {printf("hello world\n");return 0;} +EOF + + atf_check -s exit:0 -o ignore -e ignore \ + c++ -m32 -fPIC -shared -o libtest.so pic.cpp + atf_check -s exit:0 -o ignore -e ignore \ + c++ -m32 -o hello test.cpp -L. -ltest + + export LD_LIBRARY_PATH=. + atf_check -s exit:0 -o inline:"hello world\n" ./hello +} + +hello_pic_profile_body() { + cat > test.cpp << EOF +#include +int callpic(void); +int main(void) {callpic();exit(0);} +EOF + cat > pic.cpp << EOF +#include +int callpic(void) {printf("hello world\n");return 0;} +EOF + + atf_check -s exit:0 -o ignore -e ignore \ + c++ -pg -fPIC -shared -o libtest.so pic.cpp + atf_check -s exit:0 -o ignore -e ignore \ + c++ -pg -o hello test.cpp -L. -ltest + + export LD_LIBRARY_PATH=. + atf_check -s exit:0 -o inline:"hello world\n" ./hello +} + +hello_pic_profile_32_body() { + if ! c++ -dM -E - < /dev/null | fgrep -q _LP64; then + atf_skip "this is not a 64 bit architecture" + fi + if ! c++ -m32 -dM -E - < /dev/null 2>/dev/null > ./def32; then + atf_skip "c++ -m32 not supported on this architecture" + else + if fgrep -q _LP64 ./def32; then + atf_fail "c++ -m32 does not generate netbsd32 binaries" + fi + fi + + cat > test.cpp << EOF +#include +int callpic(void); +int main(void) {callpic();exit(0);} +EOF + cat > pic.cpp << EOF +#include +int callpic(void) {printf("hello world\n");return 0;} +EOF + + atf_check -s exit:0 -o ignore -e ignore \ + c++ -m32 -pg -fPIC -shared -o libtest.so pic.cpp + atf_check -s exit:0 -o ignore -e ignore \ + c++ -m32 -pg -o hello test.cpp -L. -ltest + + export LD_LIBRARY_PATH=. + atf_check -s exit:0 -o inline:"hello world\n" ./hello +} + +hello_pie_body() { + # check whether this arch supports -pie + if ! c++ -pie -dM -E - < /dev/null 2>/dev/null >/dev/null; then + atf_skip "c++ -pie not supported on this architecture" + fi + cat > test.cpp << EOF +#include +#include +int main(void) {printf("hello world\n");exit(0);} +EOF + atf_check -s exit:0 -o ignore -e ignore c++ -fpie -pie -o hello test.cpp + atf_check -s exit:0 -o inline:"hello world\n" ./hello +} + +hello32_body() { + # check whether this arch is 64bit + if ! c++ -dM -E - < /dev/null | fgrep -q _LP64; then + atf_skip "this is not a 64 bit architecture" + fi + if ! c++ -m32 -dM -E - < /dev/null 2>/dev/null > ./def32; then + atf_skip "c++ -m32 not supported on this architecture" + else + if fgrep -q _LP64 ./def32; then + atf_fail "c++ -m32 does not generate netbsd32 binaries" + fi + fi + + cat > test.cpp << EOF +#include +#include +int main(void) {printf("hello world\n");exit(0);} +EOF + atf_check -s exit:0 -o ignore -e ignore c++ -o hello32 -m32 test.cpp + atf_check -s exit:0 -o ignore -e ignore c++ -o hello64 test.cpp + file -b ./hello32 > ./ftype32 + file -b ./hello64 > ./ftype64 + if diff ./ftype32 ./ftype64 >/dev/null; then + atf_fail "generated binaries do not differ" + fi + echo "32bit binaries on this platform are:" + cat ./ftype32 + echo "While native (64bit) binaries are:" + cat ./ftype64 + atf_check -s exit:0 -o inline:"hello world\n" ./hello32 + + # do another test with static 32bit binaries + cat > test.cpp << EOF +#include +#include +int main(void) {printf("hello static world\n");exit(0);} +EOF + atf_check -s exit:0 -o ignore -e ignore c++ -o hello -m32 \ + -static test.cpp + atf_check -s exit:0 -o inline:"hello static world\n" ./hello +} + +atf_init_test_cases() +{ + + atf_add_test_case hello + atf_add_test_case hello_profile + atf_add_test_case hello_pic + atf_add_test_case hello_pie + atf_add_test_case hello32 + atf_add_test_case hello_static + atf_add_test_case hello_pic_32 + atf_add_test_case hello_pic_profile + atf_add_test_case hello_pic_profile_32 + atf_add_test_case hello_profile_32 +} diff --git a/usr.bin/c++/t_msan_allocated_memory.sh b/usr.bin/c++/t_msan_allocated_memory.sh new file mode 100644 --- /dev/null +++ b/usr.bin/c++/t_msan_allocated_memory.sh @@ -0,0 +1,168 @@ +# Copyright (c) 2018 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Yang Zheng. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +test_target() +{ + SUPPORT='n' + if uname -m | grep -q "amd64" && command -v c++ >/dev/null 2>&1 && \ + ! echo __clang__ | c++ -E - | grep -q __clang__; then + # only clang with major version newer than 7 is supported + CLANG_MAJOR=`echo __clang_major__ | c++ -E - | grep -o '^[[:digit:]]'` + if [ "$CLANG_MAJOR" -ge "7" ]; then + SUPPORT='y' + fi + fi +} + +atf_test_case allocated_memory +allocated_memory_head() { + atf_set "descr" "Test memory sanitizer for __msan_allocated_memory interface" + atf_set "require.progs" "c++ paxctl" +} + +atf_test_case allocated_memory_profile +allocated_memory_profile_head() { + atf_set "descr" "Test memory sanitizer for __msan_allocated_memory with profiling option" + atf_set "require.progs" "c++ paxctl" +} +atf_test_case allocated_memory_pic +allocated_memory_pic_head() { + atf_set "descr" "Test memory sanitizer for __msan_allocated_memory with position independent code (PIC) flag" + atf_set "require.progs" "c++ paxctl" +} +atf_test_case allocated_memory_pie +allocated_memory_pie_head() { + atf_set "descr" "Test memory sanitizer for __msan_allocated_memory with position independent execution (PIE) flag" + atf_set "require.progs" "c++ paxctl" +} + +allocated_memory_body(){ + cat > test.cc << EOF +#include +#include + +int main() { + int x = 0; + __msan_allocated_memory(&x, sizeof(x)); + return x; +} +EOF + + c++ -fsanitize=memory -o test test.cc + paxctl +a test + atf_check -s ignore -o ignore -e match:"WARNING: MemorySanitizer: use-of-uninitialized-value" ./test +} + +allocated_memory_profile_body(){ + cat > test.cc << EOF +#include +#include + +int main() { + int x = 0; + __msan_allocated_memory(&x, sizeof(x)); + return x; +} +EOF + + c++ -fsanitize=memory -o test -pg test.cc + paxctl +a test + atf_check -s ignore -o ignore -e match:"WARNING: MemorySanitizer: use-of-uninitialized-value" ./test +} + +allocated_memory_pic_body(){ + cat > test.cc << EOF +#include +#include +int help(int); +int main(int argc, char **argv) {return help(argc);} +EOF + + cat > pic.cc << EOF +#include +#include + +int help(int argc) { + int x = 0; + __msan_allocated_memory(&x, sizeof(x)); + return x; +} +EOF + + c++ -fsanitize=memory -fPIC -shared -o libtest.so pic.cc + c++ -o test test.cc -fsanitize=memory -L. -ltest + paxctl +a test + + export LD_LIBRARY_PATH=. + atf_check -s ignore -o ignore -e match:"WARNING: MemorySanitizer: use-of-uninitialized-value" ./test +} +allocated_memory_pie_body(){ + + #check whether -pie flag is supported on this architecture + if ! c++ -pie -dM -E - < /dev/null 2>/dev/null >/dev/null; then + atf_set_skip "c++ -pie not supported on this architecture" + fi + cat > test.cc << EOF +#include +#include + +int main() { + int x = 0; + __msan_allocated_memory(&x, sizeof(x)); + return x; +} +EOF + + c++ -fsanitize=memory -o test -fpie -pie test.cc + paxctl +a test + atf_check -s ignore -o ignore -e match:"WARNING: MemorySanitizer: use-of-uninitialized-value" ./test +} + +atf_test_case target_not_supported +target_not_supported_head() +{ + atf_set "descr" "Test forced skip" +} + +target_not_supported_body() +{ + atf_skip "Target is not supported" +} + +atf_init_test_cases() +{ + test_target + test $SUPPORT = 'n' && { + atf_add_test_case target_not_supported + return 0 + } + atf_add_test_case allocated_memory + atf_add_test_case allocated_memory_profile + atf_add_test_case allocated_memory_pie + atf_add_test_case allocated_memory_pic +} diff --git a/usr.bin/c++/t_msan_check_mem.sh b/usr.bin/c++/t_msan_check_mem.sh new file mode 100644 --- /dev/null +++ b/usr.bin/c++/t_msan_check_mem.sh @@ -0,0 +1,172 @@ +# Copyright (c) 2018 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Yang Zheng. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +test_target() +{ + SUPPORT='n' + if uname -m | grep -q "amd64" && command -v c++ >/dev/null 2>&1 && \ + ! echo __clang__ | c++ -E - | grep -q __clang__; then + # only clang with major version newer than 7 is supported + CLANG_MAJOR=`echo __clang_major__ | c++ -E - | grep -o '^[[:digit:]]'` + if [ "$CLANG_MAJOR" -ge "7" ]; then + SUPPORT='y' + fi + fi +} + +atf_test_case check_mem +check_mem_head() { + atf_set "descr" "Test memory sanitizer for __msan_check_mem_is_initialized interface" + atf_set "require.progs" "c++ paxctl" +} + +atf_test_case check_mem_profile +check_mem_profile_head() { + atf_set "descr" "Test memory sanitizer for __msan_check_mem_is_initialized with profiling option" + atf_set "require.progs" "c++ paxctl" +} +atf_test_case check_mem_pic +check_mem_pic_head() { + atf_set "descr" "Test memory sanitizer for __msan_check_mem_is_initialized with position independent code (PIC) flag" + atf_set "require.progs" "c++ paxctl" +} +atf_test_case check_mem_pie +check_mem_pie_head() { + atf_set "descr" "Test memory sanitizer for __msan_check_mem_is_initialized with position independent execution (PIE) flag" + atf_set "require.progs" "c++ paxctl" +} + +check_mem_body(){ + cat > test.cc << EOF +#include +#include + +int main(int argc, char **argv) { + int *volatile p = (int *)malloc(sizeof(int)); + + __msan_check_mem_is_initialized(p, sizeof(*p)); + return 0; +} +EOF + + c++ -fsanitize=memory -o test test.cc + paxctl +a test + atf_check -s ignore -o ignore -e match:"Uninitialized bytes in __msan_check_mem_is_initialized at offset 0 inside" ./test +} + +check_mem_profile_body(){ + cat > test.cc << EOF +#include +#include + +int main(int argc, char **argv) { + int *volatile p = (int *)malloc(sizeof(int)); + + __msan_check_mem_is_initialized(p, sizeof(*p)); + return 0; +} +EOF + + c++ -fsanitize=memory -o test -pg test.cc + paxctl +a test + atf_check -s ignore -o ignore -e match:"Uninitialized bytes in __msan_check_mem_is_initialized at offset 0 inside" ./test +} + +check_mem_pic_body(){ + cat > test.cc << EOF +#include +#include +int help(int); +int main(int argc, char **argv) {return help(argc);} +EOF + + cat > pic.cc << EOF +#include +#include + +int help(int argc) { + int *volatile p = (int *)malloc(sizeof(int)); + + __msan_check_mem_is_initialized(p, sizeof(*p)); + return 0; +} +EOF + + c++ -fsanitize=memory -fPIC -shared -o libtest.so pic.cc + c++ -o test test.cc -fsanitize=memory -L. -ltest + paxctl +a test + + export LD_LIBRARY_PATH=. + atf_check -s ignore -o ignore -e match:"Uninitialized bytes in __msan_check_mem_is_initialized at offset 0 inside" ./test +} +check_mem_pie_body(){ + + #check whether -pie flag is supported on this architecture + if ! c++ -pie -dM -E - < /dev/null 2>/dev/null >/dev/null; then + atf_set_skip "c++ -pie not supported on this architecture" + fi + cat > test.cc << EOF +#include +#include + +int main(int argc, char **argv) { + int *volatile p = (int *)malloc(sizeof(int)); + + __msan_check_mem_is_initialized(p, sizeof(*p)); + return 0; +} +EOF + + c++ -fsanitize=memory -o test -fpie -pie test.cc + paxctl +a test + atf_check -s ignore -o ignore -e match:"Uninitialized bytes in __msan_check_mem_is_initialized at offset 0 inside" ./test +} + +atf_test_case target_not_supported +target_not_supported_head() +{ + atf_set "descr" "Test forced skip" +} + +target_not_supported_body() +{ + atf_skip "Target is not supported" +} + +atf_init_test_cases() +{ + test_target + test $SUPPORT = 'n' && { + atf_add_test_case target_not_supported + return 0 + } + atf_add_test_case check_mem + atf_add_test_case check_mem_profile + atf_add_test_case check_mem_pie + atf_add_test_case check_mem_pic +} diff --git a/usr.bin/c++/t_msan_free.sh b/usr.bin/c++/t_msan_free.sh new file mode 100644 --- /dev/null +++ b/usr.bin/c++/t_msan_free.sh @@ -0,0 +1,164 @@ +# Copyright (c) 2018 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Yang Zheng. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +test_target() +{ + SUPPORT='n' + if uname -m | grep -q "amd64" && command -v c++ >/dev/null 2>&1 && \ + ! echo __clang__ | c++ -E - | grep -q __clang__; then + # only clang with major version newer than 7 is supported + CLANG_MAJOR=`echo __clang_major__ | c++ -E - | grep -o '^[[:digit:]]'` + if [ "$CLANG_MAJOR" -ge "7" ]; then + SUPPORT='y' + fi + fi +} + +atf_test_case free +free_head() { + atf_set "descr" "Test memory sanitizer for use-after-free case" + atf_set "require.progs" "c++ paxctl" +} + +atf_test_case free_profile +free_profile_head() { + atf_set "descr" "Test memory sanitizer for use-after-free with profiling option" + atf_set "require.progs" "c++ paxctl" +} +atf_test_case free_pic +free_pic_head() { + atf_set "descr" "Test memory sanitizer for use-after-free with position independent code (PIC) flag" + atf_set "require.progs" "c++ paxctl" +} +atf_test_case free_pie +free_pie_head() { + atf_set "descr" "Test memory sanitizer for use-after-free with position independent execution (PIE) flag" + atf_set "require.progs" "c++ paxctl" +} + +free_body(){ + cat > test.cc << EOF +#include +int main() { + int *a = (int *)malloc(sizeof(int)); + *a = 9; + free(a); + return *a; +} +EOF + + c++ -fsanitize=memory -o test test.cc + paxctl +a test + atf_check -s ignore -o ignore -e match:"WARNING: MemorySanitizer: use-of-uninitialized-value" ./test +} + +free_profile_body(){ + cat > test.cc << EOF +#include +int main() { + int *a = (int *)malloc(sizeof(int)); + *a = 9; + free(a); + return *a; +} +EOF + + c++ -fsanitize=memory -o test -pg test.cc + paxctl +a test + atf_check -s ignore -o ignore -e match:"WARNING: MemorySanitizer: use-of-uninitialized-value" ./test +} + +free_pic_body(){ + cat > test.cc << EOF +#include +#include +int help(int); +int main(int argc, char **argv) {return help(argc);} +EOF + + cat > pic.cc << EOF +#include +int help(int argc) { + int *a = (int *)malloc(sizeof(int)); + *a = 9; + free(a); + return *a; +} +EOF + + c++ -fsanitize=memory -fPIC -shared -o libtest.so pic.cc + c++ -o test test.cc -fsanitize=memory -L. -ltest + paxctl +a test + + export LD_LIBRARY_PATH=. + atf_check -s ignore -o ignore -e match:"WARNING: MemorySanitizer: use-of-uninitialized-value" ./test +} +free_pie_body(){ + + #check whether -pie flag is supported on this architecture + if ! c++ -pie -dM -E - < /dev/null 2>/dev/null >/dev/null; then + atf_set_skip "c++ -pie not supported on this architecture" + fi + cat > test.cc << EOF +#include +int main() { + int *a = (int *)malloc(sizeof(int)); + *a = 9; + free(a); + return *a; +} +EOF + + c++ -fsanitize=memory -o test -fpie -pie test.cc + paxctl +a test + atf_check -s ignore -o ignore -e match:"WARNING: MemorySanitizer: use-of-uninitialized-value" ./test +} + +atf_test_case target_not_supported +target_not_supported_head() +{ + atf_set "descr" "Test forced skip" +} + +target_not_supported_body() +{ + atf_skip "Target is not supported" +} + +atf_init_test_cases() +{ + test_target + test $SUPPORT = 'n' && { + atf_add_test_case target_not_supported + return 0 + } + atf_add_test_case free + atf_add_test_case free_profile + atf_add_test_case free_pie + atf_add_test_case free_pic +} diff --git a/usr.bin/c++/t_msan_heap.sh b/usr.bin/c++/t_msan_heap.sh new file mode 100644 --- /dev/null +++ b/usr.bin/c++/t_msan_heap.sh @@ -0,0 +1,144 @@ +# Copyright (c) 2018 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Yang Zheng. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +test_target() +{ + SUPPORT='n' + if uname -m | grep -q "amd64" && command -v c++ >/dev/null 2>&1 && \ + ! echo __clang__ | c++ -E - | grep -q __clang__; then + # only clang with major version newer than 7 is supported + CLANG_MAJOR=`echo __clang_major__ | c++ -E - | grep -o '^[[:digit:]]'` + if [ "$CLANG_MAJOR" -ge "7" ]; then + SUPPORT='y' + fi + fi +} + +atf_test_case heap +heap_head() { + atf_set "descr" "Test memory sanitizer for uninitialized heap value case" + atf_set "require.progs" "c++ paxctl" +} + +atf_test_case heap_profile +heap_profile_head() { + atf_set "descr" "Test memory sanitizer for uninitialized heap value with profiling option" + atf_set "require.progs" "c++ paxctl" +} +atf_test_case heap_pic +heap_pic_head() { + atf_set "descr" "Test memory sanitizer for uninitialized heap value with position independent code (PIC) flag" + atf_set "require.progs" "c++ paxctl" +} +atf_test_case heap_pie +heap_pie_head() { + atf_set "descr" "Test memory sanitizer for uninitialized heap value with position independent execution (PIE) flag" + atf_set "require.progs" "c++ paxctl" +} + +heap_body(){ + cat > test.cc << EOF +#include +int main() { int *a = (int *)malloc(sizeof(int)); return *a; } +EOF + + c++ -fsanitize=memory -o test test.cc + paxctl +a test + atf_check -s ignore -o ignore -e match:"WARNING: MemorySanitizer: use-of-uninitialized-value" ./test +} + +heap_profile_body(){ + cat > test.cc << EOF +#include +int main() { int *a = (int *)malloc(sizeof(int)); return *a; } +EOF + + c++ -fsanitize=memory -o test -pg test.cc + paxctl +a test + atf_check -s ignore -o ignore -e match:"WARNING: MemorySanitizer: use-of-uninitialized-value" ./test +} + +heap_pic_body(){ + cat > test.cc << EOF +#include +#include +int help(int); +int main(int argc, char **argv) {return help(argc);} +EOF + + cat > pic.cc << EOF +#include +int help(int argc) { int *a = (int *)malloc(sizeof(int)); return *a; } +EOF + + c++ -fsanitize=memory -fPIC -shared -o libtest.so pic.cc + c++ -o test test.cc -fsanitize=memory -L. -ltest + paxctl +a test + + export LD_LIBRARY_PATH=. + atf_check -s ignore -o ignore -e match:"WARNING: MemorySanitizer: use-of-uninitialized-value" ./test +} +heap_pie_body(){ + + #check whether -pie flag is supported on this architecture + if ! c++ -pie -dM -E - < /dev/null 2>/dev/null >/dev/null; then + atf_set_skip "c++ -pie not supported on this architecture" + fi + cat > test.cc << EOF +#include +int main() { int *a = (int *)malloc(sizeof(int)); return *a; } +EOF + + c++ -fsanitize=memory -o test -fpie -pie test.cc + paxctl +a test + atf_check -s ignore -o ignore -e match:"WARNING: MemorySanitizer: use-of-uninitialized-value" ./test +} + +atf_test_case target_not_supported +target_not_supported_head() +{ + atf_set "descr" "Test forced skip" +} + +target_not_supported_body() +{ + atf_skip "Target is not supported" +} + +atf_init_test_cases() +{ + test_target + test $SUPPORT = 'n' && { + atf_add_test_case target_not_supported + return 0 + } + atf_add_test_case heap + atf_add_test_case heap_profile + atf_add_test_case heap_pie + atf_add_test_case heap_pic +} diff --git a/usr.bin/c++/t_msan_partial_poison.sh b/usr.bin/c++/t_msan_partial_poison.sh new file mode 100644 --- /dev/null +++ b/usr.bin/c++/t_msan_partial_poison.sh @@ -0,0 +1,176 @@ +# Copyright (c) 2018 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Yang Zheng. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +test_target() +{ + SUPPORT='n' + if uname -m | grep -q "amd64" && command -v c++ >/dev/null 2>&1 && \ + ! echo __clang__ | c++ -E - | grep -q __clang__; then + # only clang with major version newer than 7 is supported + CLANG_MAJOR=`echo __clang_major__ | c++ -E - | grep -o '^[[:digit:]]'` + if [ "$CLANG_MAJOR" -ge "7" ]; then + SUPPORT='y' + fi + fi +} + +atf_test_case partial_poison +partial_poison_head() { + atf_set "descr" "Test memory sanitizer for __msan_partial_poison interface" + atf_set "require.progs" "c++ paxctl" +} + +atf_test_case partial_poison_profile +partial_poison_profile_head() { + atf_set "descr" "Test memory sanitizer for __msan_partial_poison with profiling option" + atf_set "require.progs" "c++ paxctl" +} +atf_test_case partial_poison_pic +partial_poison_pic_head() { + atf_set "descr" "Test memory sanitizer for __msan_partial_poison with position independent code (PIC) flag" + atf_set "require.progs" "c++ paxctl" +} +atf_test_case partial_poison_pie +partial_poison_pie_head() { + atf_set "descr" "Test memory sanitizer for __msan_partial_poison with position independent execution (PIE) flag" + atf_set "require.progs" "c++ paxctl" +} + +partial_poison_body(){ + cat > test.cc << EOF +#include +#include + +int main(void) { + char x[4]; + char x_s[4] = {0x77, 0x65, 0x43, 0x21}; + __msan_partial_poison(&x, &x_s, sizeof(x_s)); + __msan_print_shadow(&x, sizeof(x_s)); + return 0; +} +EOF + + c++ -fsanitize=memory -o test test.cc + paxctl +a test + atf_check -s ignore -o ignore -e match:": 77654321" ./test +} + +partial_poison_profile_body(){ + cat > test.cc << EOF +#include +#include + +int main(void) { + char x[4]; + char x_s[4] = {0x77, 0x65, 0x43, 0x21}; + __msan_partial_poison(&x, &x_s, sizeof(x_s)); + __msan_print_shadow(&x, sizeof(x_s)); + return 0; +} +EOF + + c++ -fsanitize=memory -o test -pg test.cc + paxctl +a test + atf_check -s ignore -o ignore -e match:": 77654321" ./test +} + +partial_poison_pic_body(){ + cat > test.cc << EOF +#include +#include +int help(int); +int main(int argc, char **argv) {return help(argc);} +EOF + + cat > pic.cc << EOF +#include +#include + +int help(int argc) { + char x[4]; + char x_s[4] = {0x77, 0x65, 0x43, 0x21}; + __msan_partial_poison(&x, &x_s, sizeof(x_s)); + __msan_print_shadow(&x, sizeof(x_s)); + return 0; +} +EOF + + c++ -fsanitize=memory -fPIC -shared -o libtest.so pic.cc + c++ -o test test.cc -fsanitize=memory -L. -ltest + paxctl +a test + + export LD_LIBRARY_PATH=. + atf_check -s ignore -o ignore -e match:": 77654321" ./test +} +partial_poison_pie_body(){ + + #check whether -pie flag is supported on this architecture + if ! c++ -pie -dM -E - < /dev/null 2>/dev/null >/dev/null; then + atf_set_skip "c++ -pie not supported on this architecture" + fi + cat > test.cc << EOF +#include +#include + +int main(void) { + char x[4]; + char x_s[4] = {0x77, 0x65, 0x43, 0x21}; + __msan_partial_poison(&x, &x_s, sizeof(x_s)); + __msan_print_shadow(&x, sizeof(x_s)); + return 0; +} +EOF + + c++ -fsanitize=memory -o test -fpie -pie test.cc + paxctl +a test + atf_check -s ignore -o ignore -e match:": 77654321" ./test +} + +atf_test_case target_not_supported +target_not_supported_head() +{ + atf_set "descr" "Test forced skip" +} + +target_not_supported_body() +{ + atf_skip "Target is not supported" +} + +atf_init_test_cases() +{ + test_target + test $SUPPORT = 'n' && { + atf_add_test_case target_not_supported + return 0 + } + atf_add_test_case partial_poison + atf_add_test_case partial_poison_profile + atf_add_test_case partial_poison_pie + atf_add_test_case partial_poison_pic +} diff --git a/usr.bin/c++/t_msan_poison.sh b/usr.bin/c++/t_msan_poison.sh new file mode 100644 --- /dev/null +++ b/usr.bin/c++/t_msan_poison.sh @@ -0,0 +1,172 @@ +# Copyright (c) 2018 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Yang Zheng. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +test_target() +{ + SUPPORT='n' + if uname -m | grep -q "amd64" && command -v c++ >/dev/null 2>&1 && \ + ! echo __clang__ | c++ -E - | grep -q __clang__; then + # only clang with major version newer than 7 is supported + CLANG_MAJOR=`echo __clang_major__ | c++ -E - | grep -o '^[[:digit:]]'` + if [ "$CLANG_MAJOR" -ge "7" ]; then + SUPPORT='y' + fi + fi +} + +atf_test_case poison +poison_head() { + atf_set "descr" "Test memory sanitizer for __msan_poison interface" + atf_set "require.progs" "c++ paxctl" +} + +atf_test_case poison_profile +poison_profile_head() { + atf_set "descr" "Test memory sanitizer for __msan_poison with profiling option" + atf_set "require.progs" "c++ paxctl" +} +atf_test_case poison_pic +poison_pic_head() { + atf_set "descr" "Test memory sanitizer for __msan_poison with position independent code (PIC) flag" + atf_set "require.progs" "c++ paxctl" +} +atf_test_case poison_pie +poison_pie_head() { + atf_set "descr" "Test memory sanitizer for __msan_poison with position independent execution (PIE) flag" + atf_set "require.progs" "c++ paxctl" +} + +poison_body(){ + cat > test.cc << EOF +#include + +int main(void) { + char p[32] = {}; + __msan_poison(p + 10, 2); + + __msan_check_mem_is_initialized(p + 5, 20); + return 0; +} +EOF + + c++ -fsanitize=memory -o test test.cc + paxctl +a test + atf_check -s ignore -o ignore -e match:"Uninitialized bytes in __msan_check_mem_is_initialized at offset 5 inside" ./test +} + +poison_profile_body(){ + cat > test.cc << EOF +#include + +int main(void) { + char p[32] = {}; + __msan_poison(p + 10, 2); + + __msan_check_mem_is_initialized(p + 5, 20); + return 0; +} +EOF + + c++ -fsanitize=memory -o test -pg test.cc + paxctl +a test + atf_check -s ignore -o ignore -e match:"Uninitialized bytes in __msan_check_mem_is_initialized at offset 5 inside" ./test +} + +poison_pic_body(){ + cat > test.cc << EOF +#include +#include +int help(int); +int main(int argc, char **argv) {return help(argc);} +EOF + + cat > pic.cc << EOF +#include + +int help(int argc) { + char p[32] = {}; + __msan_poison(p + 10, 2); + + __msan_check_mem_is_initialized(p + 5, 20); + return 0; +} +EOF + + c++ -fsanitize=memory -fPIC -shared -o libtest.so pic.cc + c++ -o test test.cc -fsanitize=memory -L. -ltest + paxctl +a test + + export LD_LIBRARY_PATH=. + atf_check -s ignore -o ignore -e match:"Uninitialized bytes in __msan_check_mem_is_initialized at offset 5 inside" ./test +} +poison_pie_body(){ + + #check whether -pie flag is supported on this architecture + if ! c++ -pie -dM -E - < /dev/null 2>/dev/null >/dev/null; then + atf_set_skip "c++ -pie not supported on this architecture" + fi + cat > test.cc << EOF +#include + +int main(void) { + char p[32] = {}; + __msan_poison(p + 10, 2); + + __msan_check_mem_is_initialized(p + 5, 20); + return 0; +} +EOF + + c++ -fsanitize=memory -o test -fpie -pie test.cc + paxctl +a test + atf_check -s ignore -o ignore -e match:"Uninitialized bytes in __msan_check_mem_is_initialized at offset 5 inside" ./test +} + +atf_test_case target_not_supported +target_not_supported_head() +{ + atf_set "descr" "Test forced skip" +} + +target_not_supported_body() +{ + atf_skip "Target is not supported" +} + +atf_init_test_cases() +{ + test_target + test $SUPPORT = 'n' && { + atf_add_test_case target_not_supported + return 0 + } + atf_add_test_case poison + atf_add_test_case poison_profile + atf_add_test_case poison_pie + atf_add_test_case poison_pic +} diff --git a/usr.bin/c++/t_msan_realloc.sh b/usr.bin/c++/t_msan_realloc.sh new file mode 100644 --- /dev/null +++ b/usr.bin/c++/t_msan_realloc.sh @@ -0,0 +1,168 @@ +# Copyright (c) 2018 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Yang Zheng. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +test_target() +{ + SUPPORT='n' + if uname -m | grep -q "amd64" && command -v c++ >/dev/null 2>&1 && \ + ! echo __clang__ | c++ -E - | grep -q __clang__; then + # only clang with major version newer than 7 is supported + CLANG_MAJOR=`echo __clang_major__ | c++ -E - | grep -o '^[[:digit:]]'` + if [ "$CLANG_MAJOR" -ge "7" ]; then + SUPPORT='y' + fi + fi +} + +atf_test_case realloc +realloc_head() { + atf_set "descr" "Test memory sanitizer for realloc" + atf_set "require.progs" "c++ paxctl" +} + +atf_test_case realloc_profile +realloc_profile_head() { + atf_set "descr" "Test memory sanitizer for realloc with profiling option" + atf_set "require.progs" "c++ paxctl" +} +atf_test_case realloc_pic +realloc_pic_head() { + atf_set "descr" "Test memory sanitizer for realloc with position independent code (PIC) flag" + atf_set "require.progs" "c++ paxctl" +} +atf_test_case realloc_pie +realloc_pie_head() { + atf_set "descr" "Test memory sanitizer for realloc with position independent execution (PIE) flag" + atf_set "require.progs" "c++ paxctl" +} + +realloc_body(){ + cat > test.cc << EOF +#include +int main(int argc, char **argv) { + char *p = (char *)malloc(100); + p = (char *)realloc(p, 10000); + char x = p[50]; + free(p); + return x; +} +EOF + + c++ -fsanitize=memory -o test test.cc + paxctl +a test + atf_check -s ignore -o ignore -e match:"WARNING: MemorySanitizer: use-of-uninitialized-value" ./test +} + +realloc_profile_body(){ + cat > test.cc << EOF +#include +int main(int argc, char **argv) { + char *p = (char *)malloc(100); + p = (char *)realloc(p, 10000); + char x = p[50]; + free(p); + return x; +} +EOF + + c++ -fsanitize=memory -o test -pg test.cc + paxctl +a test + atf_check -s ignore -o ignore -e match:"WARNING: MemorySanitizer: use-of-uninitialized-value" ./test +} + +realloc_pic_body(){ + cat > test.cc << EOF +#include +#include +int help(int); +int main(int argc, char **argv) {return help(argc);} +EOF + + cat > pic.cc << EOF +#include +int help(int argc) { + char *p = (char *)malloc(100); + p = (char *)realloc(p, 10000); + char x = p[50]; + free(p); + return x; +} +EOF + + c++ -fsanitize=memory -fPIC -shared -o libtest.so pic.cc + c++ -o test test.cc -fsanitize=memory -L. -ltest + paxctl +a test + + export LD_LIBRARY_PATH=. + atf_check -s ignore -o ignore -e match:"WARNING: MemorySanitizer: use-of-uninitialized-value" ./test +} +realloc_pie_body(){ + + #check whether -pie flag is supported on this architecture + if ! c++ -pie -dM -E - < /dev/null 2>/dev/null >/dev/null; then + atf_set_skip "c++ -pie not supported on this architecture" + fi + cat > test.cc << EOF +#include +int main(int argc, char **argv) { + char *p = (char *)malloc(100); + p = (char *)realloc(p, 10000); + char x = p[50]; + free(p); + return x; +} +EOF + + c++ -fsanitize=memory -o test -fpie -pie test.cc + paxctl +a test + atf_check -s ignore -o ignore -e match:"WARNING: MemorySanitizer: use-of-uninitialized-value" ./test +} + +atf_test_case target_not_supported +target_not_supported_head() +{ + atf_set "descr" "Test forced skip" +} + +target_not_supported_body() +{ + atf_skip "Target is not supported" +} + +atf_init_test_cases() +{ + test_target + test $SUPPORT = 'n' && { + atf_add_test_case target_not_supported + return 0 + } + atf_add_test_case realloc + atf_add_test_case realloc_profile + atf_add_test_case realloc_pie + atf_add_test_case realloc_pic +} diff --git a/usr.bin/c++/t_msan_shadow.sh b/usr.bin/c++/t_msan_shadow.sh new file mode 100644 --- /dev/null +++ b/usr.bin/c++/t_msan_shadow.sh @@ -0,0 +1,188 @@ +# Copyright (c) 2018 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Yang Zheng. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +test_target() +{ + SUPPORT='n' + if uname -m | grep -q "amd64" && command -v c++ >/dev/null 2>&1 && \ + ! echo __clang__ | c++ -E - | grep -q __clang__; then + # only clang with major version newer than 7 is supported + CLANG_MAJOR=`echo __clang_major__ | c++ -E - | grep -o '^[[:digit:]]'` + if [ "$CLANG_MAJOR" -ge "7" ]; then + SUPPORT='y' + fi + fi +} + +atf_test_case shadow +shadow_head() { + atf_set "descr" "Test memory sanitizer for shadow interface" + atf_set "require.progs" "c++ paxctl" +} + +atf_test_case shadow_profile +shadow_profile_head() { + atf_set "descr" "Test memory sanitizer for shadow with profiling option" + atf_set "require.progs" "c++ paxctl" +} +atf_test_case shadow_pic +shadow_pic_head() { + atf_set "descr" "Test memory sanitizer for shadow with position independent code (PIC) flag" + atf_set "require.progs" "c++ paxctl" +} +atf_test_case shadow_pie +shadow_pie_head() { + atf_set "descr" "Test memory sanitizer for shadow with position independent execution (PIE) flag" + atf_set "require.progs" "c++ paxctl" +} + +shadow_body(){ + cat > test.cc << EOF +#include +#include +#include + +int main(int argc, char **argv) { + char str[] = "abc"; + char str2[] = "cdefghi"; + __msan_poison(str + 2, 1); + __msan_copy_shadow(str2 + 2, str, 4); + printf("%ld\n", __msan_test_shadow(str, 4)); + __msan_print_shadow(str2, 8); + return 0; +} +EOF + + c++ -fsanitize=memory -o test test.cc + paxctl +a test + atf_check -s ignore -o match:"2" -e match:"00000000 ff000000" ./test +} + +shadow_profile_body(){ + cat > test.cc << EOF +#include +#include +#include + +int main(int argc, char **argv) { + char str[] = "abc"; + char str2[] = "cdefghi"; + __msan_poison(str + 2, 1); + __msan_copy_shadow(str2 + 2, str, 4); + printf("%ld\n", __msan_test_shadow(str, 4)); + __msan_print_shadow(str2, 8); + return 0; +} +EOF + + c++ -fsanitize=memory -o test -pg test.cc + paxctl +a test + atf_check -s ignore -o match:"2" -e match:"00000000 ff000000" ./test +} + +shadow_pic_body(){ + cat > test.cc << EOF +#include +#include +int help(int); +int main(int argc, char **argv) {return help(argc);} +EOF + + cat > pic.cc << EOF +#include +#include +#include + +int help(int argc) { + char str[] = "abc"; + char str2[] = "cdefghi"; + __msan_poison(str + 2, 1); + __msan_copy_shadow(str2 + 2, str, 4); + printf("%ld\n", __msan_test_shadow(str, 4)); + __msan_print_shadow(str2, 8); + return 0; +} +EOF + + c++ -fsanitize=memory -fPIC -shared -o libtest.so pic.cc + c++ -o test test.cc -fsanitize=memory -L. -ltest + paxctl +a test + + export LD_LIBRARY_PATH=. + atf_check -s ignore -o match:"2" -e match:"00000000 ff000000" ./test +} +shadow_pie_body(){ + + #check whether -pie flag is supported on this architecture + if ! c++ -pie -dM -E - < /dev/null 2>/dev/null >/dev/null; then + atf_set_skip "c++ -pie not supported on this architecture" + fi + cat > test.cc << EOF +#include +#include +#include + +int main(int argc, char **argv) { + char str[] = "abc"; + char str2[] = "cdefghi"; + __msan_poison(str + 2, 1); + __msan_copy_shadow(str2 + 2, str, 4); + printf("%ld\n", __msan_test_shadow(str, 4)); + __msan_print_shadow(str2, 8); + return 0; +} +EOF + + c++ -fsanitize=memory -o test -fpie -pie test.cc + paxctl +a test + atf_check -s ignore -o match:"2" -e match:"00000000 ff000000" ./test +} + +atf_test_case target_not_supported +target_not_supported_head() +{ + atf_set "descr" "Test forced skip" +} + +target_not_supported_body() +{ + atf_skip "Target is not supported" +} + +atf_init_test_cases() +{ + test_target + test $SUPPORT = 'n' && { + atf_add_test_case target_not_supported + return 0 + } + atf_add_test_case shadow + atf_add_test_case shadow_profile + atf_add_test_case shadow_pie + atf_add_test_case shadow_pic +} diff --git a/usr.bin/c++/t_msan_stack.sh b/usr.bin/c++/t_msan_stack.sh new file mode 100644 --- /dev/null +++ b/usr.bin/c++/t_msan_stack.sh @@ -0,0 +1,164 @@ +# Copyright (c) 2018 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Yang Zheng. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +test_target() +{ + SUPPORT='n' + if uname -m | grep -q "amd64" && command -v c++ >/dev/null 2>&1 && \ + ! echo __clang__ | c++ -E - | grep -q __clang__; then + # only clang with major version newer than 7 is supported + CLANG_MAJOR=`echo __clang_major__ | c++ -E - | grep -o '^[[:digit:]]'` + if [ "$CLANG_MAJOR" -ge "7" ]; then + SUPPORT='y' + fi + fi +} + +atf_test_case stack +stack_head() { + atf_set "descr" "Test memory sanitizer for free-stack-pointer case" + atf_set "require.progs" "c++ paxctl" +} + +atf_test_case stack_profile +stack_profile_head() { + atf_set "descr" "Test memory sanitizer for free-stack-pointer with profiling option" + atf_set "require.progs" "c++ paxctl" +} +atf_test_case stack_pic +stack_pic_head() { + atf_set "descr" "Test memory sanitizer for free-stack-pointer with position independent code (PIC) flag" + atf_set "require.progs" "c++ paxctl" +} +atf_test_case stack_pie +stack_pie_head() { + atf_set "descr" "Test memory sanitizer for free-stack-pointer with position independent execution (PIE) flag" + atf_set "require.progs" "c++ paxctl" +} + +stack_body(){ + cat > test.cc << EOF +#include +int main() { + int a = 0; + int *p = &a; + free(p); + return 0; +} +EOF + + c++ -fsanitize=memory -o test test.cc + paxctl +a test + atf_check -s ignore -o ignore -e match:"MemorySanitizer: bad pointer" ./test +} + +stack_profile_body(){ + cat > test.cc << EOF +#include +int main() { + int a = 0; + int *p = &a; + free(p); + return 0; +} +EOF + + c++ -fsanitize=memory -o test -pg test.cc + paxctl +a test + atf_check -s ignore -o ignore -e match:"MemorySanitizer: bad pointer" ./test +} + +stack_pic_body(){ + cat > test.cc << EOF +#include +#include +int help(int); +int main(int argc, char **argv) {return help(argc);} +EOF + + cat > pic.cc << EOF +#include +int help(int argc) { + int a = 0; + int *p = &a; + free(p); + return 0; +} +EOF + + c++ -fsanitize=memory -fPIC -shared -o libtest.so pic.cc + c++ -o test test.cc -fsanitize=memory -L. -ltest + paxctl +a test + + export LD_LIBRARY_PATH=. + atf_check -s ignore -o ignore -e match:"MemorySanitizer: bad pointer" ./test +} +stack_pie_body(){ + + #check whether -pie flag is supported on this architecture + if ! c++ -pie -dM -E - < /dev/null 2>/dev/null >/dev/null; then + atf_set_skip "c++ -pie not supported on this architecture" + fi + cat > test.cc << EOF +#include +int main() { + int a = 0; + int *p = &a; + free(p); + return 0; +} +EOF + + c++ -fsanitize=memory -o test -fpie -pie test.cc + paxctl +a test + atf_check -s ignore -o ignore -e match:"MemorySanitizer: bad pointer" ./test +} + +atf_test_case target_not_supported +target_not_supported_head() +{ + atf_set "descr" "Test forced skip" +} + +target_not_supported_body() +{ + atf_skip "Target is not supported" +} + +atf_init_test_cases() +{ + test_target + test $SUPPORT = 'n' && { + atf_add_test_case target_not_supported + return 0 + } + atf_add_test_case stack + atf_add_test_case stack_profile + atf_add_test_case stack_pie + atf_add_test_case stack_pic +} diff --git a/usr.bin/c++/t_msan_unpoison.sh b/usr.bin/c++/t_msan_unpoison.sh new file mode 100644 --- /dev/null +++ b/usr.bin/c++/t_msan_unpoison.sh @@ -0,0 +1,188 @@ +# Copyright (c) 2018 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Yang Zheng. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +test_target() +{ + SUPPORT='n' + if uname -m | grep -q "amd64" && command -v c++ >/dev/null 2>&1 && \ + ! echo __clang__ | c++ -E - | grep -q __clang__; then + # only clang with major version newer than 7 is supported + CLANG_MAJOR=`echo __clang_major__ | c++ -E - | grep -o '^[[:digit:]]'` + if [ "$CLANG_MAJOR" -ge "7" ]; then + SUPPORT='y' + fi + fi +} + +atf_test_case unpoison +unpoison_head() { + atf_set "descr" "Test memory sanitizer for __msan_unpoison interface" + atf_set "require.progs" "c++ paxctl" +} + +atf_test_case unpoison_profile +unpoison_profile_head() { + atf_set "descr" "Test memory sanitizer for __msan_unpoison with profiling option" + atf_set "require.progs" "c++ paxctl" +} +atf_test_case unpoison_pic +unpoison_pic_head() { + atf_set "descr" "Test memory sanitizer for __msan_unpoison with position independent code (PIC) flag" + atf_set "require.progs" "c++ paxctl" +} +atf_test_case unpoison_pie +unpoison_pie_head() { + atf_set "descr" "Test memory sanitizer for __msan_unpoison with position independent execution (PIE) flag" + atf_set "require.progs" "c++ paxctl" +} + +unpoison_body(){ + cat > test.cc << EOF +#include + +int main(void) { + char p[32] = {}; + char q[32] = {}; + __msan_poison(p + 10, 2); + __msan_poison(q, 32); + __msan_unpoison(p + 10, 2); + __msan_unpoison_string(q); + __msan_check_mem_is_initialized(p, 32); + __msan_check_mem_is_initialized(p, 32); + return 0; +} +EOF + + c++ -fsanitize=memory -o test test.cc + paxctl +a test + atf_check -s ignore -o ignore -e not-match:"WARNING: MemorySanitizer: use-of-uninitialized-value" ./test +} + +unpoison_profile_body(){ + cat > test.cc << EOF +#include + +int main(void) { + char p[32] = {}; + char q[32] = {}; + __msan_poison(p + 10, 2); + __msan_poison(q, 32); + __msan_unpoison(p + 10, 2); + __msan_unpoison_string(q); + __msan_check_mem_is_initialized(p, 32); + __msan_check_mem_is_initialized(p, 32); + return 0; +} +EOF + + c++ -fsanitize=memory -o test -pg test.cc + paxctl +a test + atf_check -s ignore -o ignore -e not-match:"WARNING: MemorySanitizer: use-of-uninitialized-value" ./test +} + +unpoison_pic_body(){ + cat > test.cc << EOF +#include +#include +int help(int); +int main(int argc, char **argv) {return help(argc);} +EOF + + cat > pic.cc << EOF +#include + +int help(int argc) { + char p[32] = {}; + char q[32] = {}; + __msan_poison(p + 10, 2); + __msan_poison(q, 32); + __msan_unpoison(p + 10, 2); + __msan_unpoison_string(q); + __msan_check_mem_is_initialized(p, 32); + __msan_check_mem_is_initialized(p, 32); + return 0; +} +EOF + + c++ -fsanitize=memory -fPIC -shared -o libtest.so pic.cc + c++ -o test test.cc -fsanitize=memory -L. -ltest + paxctl +a test + + export LD_LIBRARY_PATH=. + atf_check -s ignore -o ignore -e not-match:"WARNING: MemorySanitizer: use-of-uninitialized-value" ./test +} +unpoison_pie_body(){ + + #check whether -pie flag is supported on this architecture + if ! c++ -pie -dM -E - < /dev/null 2>/dev/null >/dev/null; then + atf_set_skip "c++ -pie not supported on this architecture" + fi + cat > test.cc << EOF +#include + +int main(void) { + char p[32] = {}; + char q[32] = {}; + __msan_poison(p + 10, 2); + __msan_poison(q, 32); + __msan_unpoison(p + 10, 2); + __msan_unpoison_string(q); + __msan_check_mem_is_initialized(p, 32); + __msan_check_mem_is_initialized(p, 32); + return 0; +} +EOF + + c++ -fsanitize=memory -o test -fpie -pie test.cc + paxctl +a test + atf_check -s ignore -o ignore -e not-match:"WARNING: MemorySanitizer: use-of-uninitialized-value" ./test +} + +atf_test_case target_not_supported +target_not_supported_head() +{ + atf_set "descr" "Test forced skip" +} + +target_not_supported_body() +{ + atf_skip "Target is not supported" +} + +atf_init_test_cases() +{ + test_target + test $SUPPORT = 'n' && { + atf_add_test_case target_not_supported + return 0 + } + atf_add_test_case unpoison + atf_add_test_case unpoison_profile + atf_add_test_case unpoison_pie + atf_add_test_case unpoison_pic +} diff --git a/usr.bin/c++/t_pthread_once.sh b/usr.bin/c++/t_pthread_once.sh new file mode 100644 --- /dev/null +++ b/usr.bin/c++/t_pthread_once.sh @@ -0,0 +1,361 @@ +# $NetBSD: t_pthread_once.sh,v 1.4 2020/10/13 06:49:27 rin Exp $ +# +# Copyright (c) 2018 The NetBSD Foundation, Inc. +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +atf_test_case pthread_once +pthread_once_head() { + atf_set "descr" "compile and run std::pthread_once" + atf_set "require.progs" "c++" +} + +atf_test_case pthread_once_profile +pthread_once_profile_head() { + atf_set "descr" "compile and run std::pthread_once with profiling option" + atf_set "require.progs" "c++" +} + +atf_test_case pthread_once_pic +pthread_once_pic_head() { + atf_set "descr" "compile and run PIC std::pthread_once" + atf_set "require.progs" "c++" +} + +atf_test_case pthread_once_pic_32 +pthread_once_pic_32_head() { + atf_set "descr" "compile and run 32-bit PIC std::pthread_once" + atf_set "require.progs" "c++" +} + +atf_test_case pthread_once_pic_profile +pthread_once_pic_head() { + atf_set "descr" "compile and run PIC std::pthread_once with profiling &flag" + atf_set "require.progs" "c++" +} + +atf_test_case pthread_once_pic_profile_32 +pthread_once_pic_profile_32_head() { + atf_set "descr" "compile and run 32-bit PIC std::pthread_once with profiling &flag" + atf_set "require.progs" "c++" +} + +atf_test_case pthread_once_profile_32 +pthread_once_profile_32_head() { + atf_set "descr" "compile and run 32-bit std::pthread_once with profiling &flag" + atf_set "require.progs" "c++" +} + +atf_test_case pthread_once_pie +pthread_once_pie_head() { + atf_set "descr" "compile and run position independent (PIE) std::pthread_once" + atf_set "require.progs" "c++" +} + +atf_test_case pthread_once_32 +pthread_once_32_head() { + atf_set "descr" "compile and run std::pthread_once for/in netbsd32 emulation" + atf_set "require.progs" "c++ file diff cat" +} + +atf_test_case pthread_once_static +pthread_once_static_head() { + atf_set "descr" "compile and run std::pthread_once with static &flag" + atf_set "require.progs" "c++" +} + +pthread_once_body() { + cat > test.cpp << EOF +#include +#include +int main(void) { + pthread_once_t flag = PTHREAD_ONCE_INIT; + pthread_once(&flag, [](){ printf("hello, world!\n"); }); + return 0; +} +EOF + atf_check -s exit:0 -o ignore -e ignore c++ -o pthread_once test.cpp -pthread + atf_check -s exit:0 -o inline:"hello, world!\n" ./pthread_once +} + +pthread_once_profile_body() { + cat > test.cpp << EOF +#include +#include +int main(void) { + pthread_once_t flag = PTHREAD_ONCE_INIT; + pthread_once(&flag, [](){ printf("hello, world!\n"); }); + return 0; +} +EOF + atf_check -s exit:0 -o ignore -e ignore c++ -pg -o pthread_once test.cpp -pthread + atf_check -s exit:0 -o inline:"hello, world!\n" ./pthread_once +} + +pthread_once_profile_32_body() { + # check whether this arch is 64bit + if ! c++ -dM -E - < /dev/null | fgrep -q _LP64; then + atf_skip "this is not a 64 bit architecture" + fi + if ! c++ -m32 -dM -E - < /dev/null 2>/dev/null > ./def32; then + atf_skip "c++ -m32 not supported on this architecture" + else + if fgrep -q _LP64 ./def32; then + atf_fail "c++ -m32 does not generate netbsd32 binaries" + fi + fi + + cat > test.cpp << EOF +#include +#include +int main(void) { + pthread_once_t flag = PTHREAD_ONCE_INIT; + pthread_once(&flag, [](){ printf("hello, world!\n"); }); + return 0; +} +EOF + atf_check -s exit:0 -o ignore -e ignore c++ -m32 -pg -o pthread_once test.cpp -pthread + atf_check -s exit:0 -o inline:"hello, world!\n" ./pthread_once +} + +pthread_once_pic_body() { + cat > test.cpp << EOF +#include +int callpic(void); +int main(void) {callpic();exit(0);} +EOF + cat > pic.cpp << EOF +#include +#include +int callpic(void) { + pthread_once_t flag = PTHREAD_ONCE_INIT; + pthread_once(&flag, [](){ printf("hello, world!\n"); }); + return 0; +} +EOF + + atf_check -s exit:0 -o ignore -e ignore \ + c++ -fPIC -shared -o libtest.so pic.cpp + atf_check -s exit:0 -o ignore -e ignore \ + c++ -o pthread_once test.cpp -L. -ltest -pthread + + export LD_LIBRARY_PATH=. + atf_check -s exit:0 -o inline:"hello, world!\n" ./pthread_once +} + +pthread_once_pic_32_body() { + # check whether this arch is 64bit + if ! c++ -dM -E - < /dev/null | fgrep -q _LP64; then + atf_skip "this is not a 64 bit architecture" + fi + if ! c++ -m32 -dM -E - < /dev/null 2>/dev/null > ./def32; then + atf_skip "c++ -m32 not supported on this architecture" + else + if fgrep -q _LP64 ./def32; then + atf_fail "c++ -m32 does not generate netbsd32 binaries" + fi + fi + + cat > test.cpp << EOF +#include +int callpic(void); +int main(void) {callpic();exit(0);} +EOF + cat > pic.cpp << EOF +#include +#include +int callpic(void) { + pthread_once_t flag = PTHREAD_ONCE_INIT; + pthread_once(&flag, [](){ printf("hello, world!\n"); }); + return 0; +} +EOF + + atf_check -s exit:0 -o ignore -e ignore \ + c++ -m32 -fPIC -shared -o libtest.so pic.cpp + atf_check -s exit:0 -o ignore -e ignore \ + c++ -m32 -o pthread_once test.cpp -L. -ltest -pthread + + export LD_LIBRARY_PATH=. + atf_check -s exit:0 -o inline:"hello, world!\n" ./pthread_once +} + +pthread_once_pic_profile_body() { + cat > test.cpp << EOF +#include +int callpic(void); +int main(void) {callpic();exit(0);} +EOF + cat > pic.cpp << EOF +#include +#include +int callpic(void) { + pthread_once_t flag = PTHREAD_ONCE_INIT; + pthread_once(&flag, [](){ printf("hello, world!\n"); }); + return 0; +} +EOF + + atf_check -s exit:0 -o ignore -e ignore \ + c++ -pg -fPIC -shared -o libtest.so pic.cpp + atf_check -s exit:0 -o ignore -e ignore \ + c++ -pg -o pthread_once test.cpp -L. -ltest -pthread + + export LD_LIBRARY_PATH=. + atf_check -s exit:0 -o inline:"hello, world!\n" ./pthread_once +} + +pthread_once_pic_profile_32_body() { + # check whether this arch is 64bit + if ! c++ -dM -E - < /dev/null | fgrep -q _LP64; then + atf_skip "this is not a 64 bit architecture" + fi + if ! c++ -m32 -dM -E - < /dev/null 2>/dev/null > ./def32; then + atf_skip "c++ -m32 not supported on this architecture" + else + if fgrep -q _LP64 ./def32; then + atf_fail "c++ -m32 does not generate netbsd32 binaries" + fi + fi + + cat > test.cpp << EOF +#include +int callpic(void); +int main(void) {callpic();exit(0);} +EOF + cat > pic.cpp << EOF +#include +#include +int callpic(void) { + pthread_once_t flag = PTHREAD_ONCE_INIT; + pthread_once(&flag, [](){ printf("hello, world!\n"); }); + return 0; +} +EOF + + atf_check -s exit:0 -o ignore -e ignore \ + c++ -m32 -pg -fPIC -shared -o libtest.so pic.cpp + atf_check -s exit:0 -o ignore -e ignore \ + c++ -m32 -pg -o pthread_once test.cpp -L. -ltest -pthread + + export LD_LIBRARY_PATH=. + atf_check -s exit:0 -o inline:"hello, world!\n" ./pthread_once +} + +pthread_once_pie_body() { + # check whether this arch supports -pie + if ! c++ -pie -dM -E - < /dev/null 2>/dev/null >/dev/null; then + atf_skip "c++ -pie not supported on this architecture" + fi + cat > test.cpp << EOF +#include +#include +int main(void) { + pthread_once_t flag = PTHREAD_ONCE_INIT; + pthread_once(&flag, [](){ printf("hello, world!\n"); }); + return 0; +} +EOF + atf_check -s exit:0 -o ignore -e ignore c++ -fpie -pie -o pthread_once test.cpp -pthread + atf_check -s exit:0 -o inline:"hello, world!\n" ./pthread_once +} + +pthread_once_32_body() { + # check whether this arch is 64bit + if ! c++ -dM -E - < /dev/null | fgrep -q _LP64; then + atf_skip "this is not a 64 bit architecture" + fi + if ! c++ -m32 -dM -E - < /dev/null 2>/dev/null > ./def32; then + atf_skip "c++ -m32 not supported on this architecture" + else + if fgrep -q _LP64 ./def32; then + atf_fail "c++ -m32 does not generate netbsd32 binaries" + fi + fi + + cat > test.cpp << EOF +#include +#include +int main(void) { + pthread_once_t flag = PTHREAD_ONCE_INIT; + pthread_once(&flag, [](){ printf("hello, world!\n"); }); + return 0; +} +EOF + atf_check -s exit:0 -o ignore -e ignore c++ -o pthread_once_32 -m32 test.cpp -pthread + atf_check -s exit:0 -o ignore -e ignore c++ -o pthread_once_64 test.cpp -pthread + file -b ./pthread_once_32 > ./ftype32 + file -b ./pthread_once_64 > ./ftype64 + if diff ./ftype32 ./ftype64 >/dev/null; then + atf_fail "generated binaries do not differ" + fi + echo "32bit binaries on this platform are:" + cat ./ftype32 + echo "While native (64bit) binaries are:" + cat ./ftype64 + atf_check -s exit:0 -o inline:"hello, world!\n" ./pthread_once_32 + + # do another test with static 32bit binaries + cat > test.cpp << EOF +#include +#include +int main(void) { + pthread_once_t flag = PTHREAD_ONCE_INIT; + pthread_once(&flag, [](){ printf("hello, world!\n"); }); + return 0; +} +EOF + atf_check -s exit:0 -o ignore -e ignore c++ -o pthread_once -m32 -pthread \ + -static test.cpp + atf_check -s exit:0 -o inline:"hello, world!\n" ./pthread_once +} + +pthread_once_static_body() { + cat > test.cpp << EOF +#include +#include +int main(void) { + pthread_once_t flag = PTHREAD_ONCE_INIT; + pthread_once(&flag, [](){ printf("hello, world!\n"); }); + return 0; +} +EOF + atf_check -s exit:0 -o ignore -e ignore c++ -static -o pthread_once test.cpp -pthread + atf_check -s exit:0 -o inline:"hello, world!\n" ./pthread_once +} + +atf_init_test_cases() +{ + + atf_add_test_case pthread_once + atf_add_test_case pthread_once_profile + atf_add_test_case pthread_once_pic + atf_add_test_case pthread_once_pie + atf_add_test_case pthread_once_32 + atf_add_test_case pthread_once_static + atf_add_test_case pthread_once_pic_32 + atf_add_test_case pthread_once_pic_profile + atf_add_test_case pthread_once_pic_profile_32 + atf_add_test_case pthread_once_profile_32 +} diff --git a/usr.bin/c++/t_static_destructor.sh b/usr.bin/c++/t_static_destructor.sh new file mode 100644 --- /dev/null +++ b/usr.bin/c++/t_static_destructor.sh @@ -0,0 +1,420 @@ +# $NetBSD: t_static_destructor.sh,v 1.4 2020/10/13 06:49:27 rin Exp $ +# +# Copyright (c) 2017 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Kamil Rytarowski. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +atf_test_case static_destructor +static_destructor_head() { + atf_set "descr" "compile and run \"hello world\"" + atf_set "require.progs" "c++" +} + +atf_test_case static_destructor_profile +static_destructor_profile_head() { + atf_set "descr" "compile and run \"hello world\" with profiling option" + atf_set "require.progs" "c++" +} + +atf_test_case static_destructor_static +static_destructor_static_head() { + atf_set "descr" "compile and run \"hello world\" with static option" + atf_set "require.progs" "c++" +} + +atf_test_case static_destructor_pic +static_destructor_pic_head() { + atf_set "descr" "compile and run PIC \"hello world\"" + atf_set "require.progs" "c++" +} + +atf_test_case static_destructor_pic_32 +static_destructor_pic_32_head() { + atf_set "descr" "compile and run 32-bit PIC \"hello world\"" + atf_set "require.progs" "c++" +} + +atf_test_case static_destructor_pic_profile +static_destructor_pic_profile_head() { + atf_set "descr" "compile and run PIC \"hello world\" with profiling option" + atf_set "require.progs" "c++" +} + +atf_test_case static_destructor_pic_profile_32 +static_destructor_pic_profile_32_head() { + atf_set "descr" "compile and run 32-bit PIC \"hello world\" with profiling option" + atf_set "require.progs" "c++" +} + +atf_test_case static_destructor_profile_32 +static_destructor_profile_32_head() { + atf_set "descr" "compile and run 32-bit \"hello world\" with profiling option" + atf_set "require.progs" "c++" +} + +atf_test_case static_destructor_pie +static_destructor_pie_head() { + atf_set "descr" "compile and run position independent (PIE) \"hello world\"" + atf_set "require.progs" "c++" +} + +atf_test_case static_destructor32 +static_destructor32_head() { + atf_set "descr" "compile and run \"hello world\" for/in netbsd32 emulation" + atf_set "require.progs" "c++ file diff cat" +} + +static_destructor_body() { + cat > test.cpp << EOF +#include +struct A { + int i; + A(int i):i(i){std::cout << "CTOR A" << std::endl;} + ~A() {std::cout << "DTOR A:" << i << std::endl;} +}; +struct B { + A *m_a; + B(){static A s_a(10);m_a=&s_a;std::cout << "CTOR B" << std::endl;} + ~B(){std::cout << "DTOR B:" << (*m_a).i << std::endl;(*m_a).i = 20;} +}; +int main(void) {struct B b;return 0;} +EOF + atf_check -s exit:0 -o ignore -e ignore c++ -o hello test.cpp + atf_check -s exit:0 -o inline:"CTOR A\nCTOR B\nDTOR B:10\nDTOR A:20\n" ./hello +} + +static_destructor_profile_body() { + cat > test.cpp << EOF +#include +struct A { + int i; + A(int i):i(i){std::cout << "CTOR A" << std::endl;} + ~A() {std::cout << "DTOR A:" << i << std::endl;} +}; +struct B { + A *m_a; + B(){static A s_a(10);m_a=&s_a;std::cout << "CTOR B" << std::endl;} + ~B(){std::cout << "DTOR B:" << (*m_a).i << std::endl;(*m_a).i = 20;} +}; +int main(void) {struct B b;return 0;} +EOF + atf_check -s exit:0 -o ignore -e ignore c++ -pg -o hello test.cpp + atf_check -s exit:0 -o inline:"CTOR A\nCTOR B\nDTOR B:10\nDTOR A:20\n" ./hello +} + +static_destructor_profile_32_body() { + # check whether this arch is 64bit + if ! c++ -dM -E - < /dev/null | fgrep -q _LP64; then + atf_skip "this is not a 64 bit architecture" + fi + if ! c++ -m32 -dM -E - < /dev/null 2>/dev/null > ./def32; then + atf_skip "c++ -m32 not supported on this architecture" + else + if fgrep -q _LP64 ./def32; then + atf_fail "c++ -m32 does not generate netbsd32 binaries" + fi + fi + + cat > test.cpp << EOF +#include +struct A { + int i; + A(int i):i(i){std::cout << "CTOR A" << std::endl;} + ~A() {std::cout << "DTOR A:" << i << std::endl;} +}; +struct B { + A *m_a; + B(){static A s_a(10);m_a=&s_a;std::cout << "CTOR B" << std::endl;} + ~B(){std::cout << "DTOR B:" << (*m_a).i << std::endl;(*m_a).i = 20;} +}; +int main(void) {struct B b;return 0;} +EOF + atf_check -s exit:0 -o ignore -e ignore c++ -m32 -pg -o hello test.cpp + atf_check -s exit:0 -o inline:"CTOR A\nCTOR B\nDTOR B:10\nDTOR A:20\n" ./hello +} + + +static_destructor_static_body() { + cat > test.cpp << EOF +#include +struct A { + int i; + A(int i):i(i){std::cout << "CTOR A" << std::endl;} + ~A() {std::cout << "DTOR A:" << i << std::endl;} +}; +struct B { + A *m_a; + B(){static A s_a(10);m_a=&s_a;std::cout << "CTOR B" << std::endl;} + ~B(){std::cout << "DTOR B:" << (*m_a).i << std::endl;(*m_a).i = 20;} +}; +int main(void) {struct B b;return 0;} +EOF + atf_check -s exit:0 -o ignore -e ignore c++ -static -o hello test.cpp + atf_check -s exit:0 -o inline:"CTOR A\nCTOR B\nDTOR B:10\nDTOR A:20\n" ./hello +} + +static_destructor_pic_body() { + cat > test.cpp << EOF +#include +int callpic(void); +int main(void) {callpic();exit(0);} +EOF + cat > pic.cpp << EOF +#include +struct A { + int i; + A(int i):i(i){std::cout << "CTOR A" << std::endl;} + ~A() {std::cout << "DTOR A:" << i << std::endl;} +}; +struct B { + A *m_a; + B(){static A s_a(10);m_a=&s_a;std::cout << "CTOR B" << std::endl;} + ~B(){std::cout << "DTOR B:" << (*m_a).i << std::endl;(*m_a).i = 20;} +}; +int callpic(void) {struct B b;} +EOF + + atf_check -s exit:0 -o ignore -e ignore \ + c++ -fPIC -shared -o libtest.so pic.cpp + atf_check -s exit:0 -o ignore -e ignore \ + c++ -o hello test.cpp -L. -ltest + + export LD_LIBRARY_PATH=. + atf_check -s exit:0 -o inline:"CTOR A\nCTOR B\nDTOR B:10\nDTOR A:20\n" ./hello +} + +static_destructor_pic_32_body() { + # check whether this arch is 64bit + if ! c++ -dM -E - < /dev/null | fgrep -q _LP64; then + atf_skip "this is not a 64 bit architecture" + fi + if ! c++ -m32 -dM -E - < /dev/null 2>/dev/null > ./def32; then + atf_skip "c++ -m32 not supported on this architecture" + else + if fgrep -q _LP64 ./def32; then + atf_fail "c++ -m32 does not generate netbsd32 binaries" + fi + fi + + cat > test.cpp << EOF +#include +int callpic(void); +int main(void) {callpic();exit(0);} +EOF + cat > pic.cpp << EOF +#include +struct A { + int i; + A(int i):i(i){std::cout << "CTOR A" << std::endl;} + ~A() {std::cout << "DTOR A:" << i << std::endl;} +}; +struct B { + A *m_a; + B(){static A s_a(10);m_a=&s_a;std::cout << "CTOR B" << std::endl;} + ~B(){std::cout << "DTOR B:" << (*m_a).i << std::endl;(*m_a).i = 20;} +}; +int callpic(void) {struct B b;} +EOF + + atf_check -s exit:0 -o ignore -e ignore \ + c++ -m32 -fPIC -shared -o libtest.so pic.cpp + atf_check -s exit:0 -o ignore -e ignore \ + c++ -m32 -o hello test.cpp -L. -ltest + + export LD_LIBRARY_PATH=. + atf_check -s exit:0 -o inline:"CTOR A\nCTOR B\nDTOR B:10\nDTOR A:20\n" ./hello +} + +static_destructor_pic_profile_body() { + cat > test.cpp << EOF +#include +int callpic(void); +int main(void) {callpic();exit(0);} +EOF + cat > pic.cpp << EOF +#include +struct A { + int i; + A(int i):i(i){std::cout << "CTOR A" << std::endl;} + ~A() {std::cout << "DTOR A:" << i << std::endl;} +}; +struct B { + A *m_a; + B(){static A s_a(10);m_a=&s_a;std::cout << "CTOR B" << std::endl;} + ~B(){std::cout << "DTOR B:" << (*m_a).i << std::endl;(*m_a).i = 20;} +}; +int callpic(void) {struct B b;} +EOF + + atf_check -s exit:0 -o ignore -e ignore \ + c++ -pg -fPIC -shared -o libtest.so pic.cpp + atf_check -s exit:0 -o ignore -e ignore \ + c++ -pg -o hello test.cpp -L. -ltest + + export LD_LIBRARY_PATH=. + atf_check -s exit:0 -o inline:"CTOR A\nCTOR B\nDTOR B:10\nDTOR A:20\n" ./hello +} + +static_destructor_pic_profile_32_body() { + # check whether this arch is 64bit + if ! c++ -dM -E - < /dev/null | fgrep -q _LP64; then + atf_skip "this is not a 64 bit architecture" + fi + if ! c++ -m32 -dM -E - < /dev/null 2>/dev/null > ./def32; then + atf_skip "c++ -m32 not supported on this architecture" + else + if fgrep -q _LP64 ./def32; then + atf_fail "c++ -m32 does not generate netbsd32 binaries" + fi + fi + + cat > test.cpp << EOF +#include +int callpic(void); +int main(void) {callpic();exit(0);} +EOF + cat > pic.cpp << EOF +#include +struct A { + int i; + A(int i):i(i){std::cout << "CTOR A" << std::endl;} + ~A() {std::cout << "DTOR A:" << i << std::endl;} +}; +struct B { + A *m_a; + B(){static A s_a(10);m_a=&s_a;std::cout << "CTOR B" << std::endl;} + ~B(){std::cout << "DTOR B:" << (*m_a).i << std::endl;(*m_a).i = 20;} +}; +int callpic(void) {struct B b;} +EOF + + atf_check -s exit:0 -o ignore -e ignore \ + c++ -m32 -pg -fPIC -shared -o libtest.so pic.cpp + atf_check -s exit:0 -o ignore -e ignore \ + c++ -m32 -pg -o hello test.cpp -L. -ltest + + export LD_LIBRARY_PATH=. + atf_check -s exit:0 -o inline:"CTOR A\nCTOR B\nDTOR B:10\nDTOR A:20\n" ./hello +} + +static_destructor_pie_body() { + # check whether this arch supports -pie + if ! c++ -pie -dM -E - < /dev/null 2>/dev/null >/dev/null; then + atf_skip "c++ -pie not supported on this architecture" + fi + cat > test.cpp << EOF +#include +struct A { + int i; + A(int i):i(i){std::cout << "CTOR A" << std::endl;} + ~A() {std::cout << "DTOR A:" << i << std::endl;} +}; +struct B { + A *m_a; + B(){static A s_a(10);m_a=&s_a;std::cout << "CTOR B" << std::endl;} + ~B(){std::cout << "DTOR B:" << (*m_a).i << std::endl;(*m_a).i = 20;} +}; +int main(void) {struct B b;return 0;} +EOF + atf_check -s exit:0 -o ignore -e ignore c++ -fpie -pie -o hello test.cpp + atf_check -s exit:0 -o inline:"CTOR A\nCTOR B\nDTOR B:10\nDTOR A:20\n" ./hello +} + +static_destructor32_body() { + # check whether this arch is 64bit + if ! c++ -dM -E - < /dev/null | fgrep -q _LP64; then + atf_skip "this is not a 64 bit architecture" + fi + if ! c++ -m32 -dM -E - < /dev/null 2>/dev/null > ./def32; then + atf_skip "c++ -m32 not supported on this architecture" + else + if fgrep -q _LP64 ./def32; then + atf_fail "c++ -m32 does not generate netbsd32 binaries" + fi + fi + + cat > test.cpp << EOF +#include +struct A { + int i; + A(int i):i(i){std::cout << "CTOR A" << std::endl;} + ~A() {std::cout << "DTOR A:" << i << std::endl;} +}; +struct B { + A *m_a; + B(){static A s_a(10);m_a=&s_a;std::cout << "CTOR B" << std::endl;} + ~B(){std::cout << "DTOR B:" << (*m_a).i << std::endl;(*m_a).i = 20;} +}; +int main(void) {struct B b;return 0;} +EOF + atf_check -s exit:0 -o ignore -e ignore c++ -o hello32 -m32 test.cpp + atf_check -s exit:0 -o ignore -e ignore c++ -o hello64 test.cpp + file -b ./hello32 > ./ftype32 + file -b ./hello64 > ./ftype64 + if diff ./ftype32 ./ftype64 >/dev/null; then + atf_fail "generated binaries do not differ" + fi + echo "32bit binaries on this platform are:" + cat ./ftype32 + echo "While native (64bit) binaries are:" + cat ./ftype64 + atf_check -s exit:0 -o inline:"CTOR A\nCTOR B\nDTOR B:10\nDTOR A:20\n" ./hello32 + + # do another test with static 32bit binaries + cat > test.cpp << EOF +#include +struct A { + int i; + A(int i):i(i){std::cout << "CTOR A" << std::endl;} + ~A() {std::cout << "DTOR A:" << i << std::endl;} +}; +struct B { + A *m_a; + B(){static A s_a(10);m_a=&s_a;std::cout << "CTOR B" << std::endl;} + ~B(){std::cout << "DTOR B:" << (*m_a).i << std::endl;(*m_a).i = 20;} +}; +int main(void) {struct B b;return 0;} +EOF + atf_check -s exit:0 -o ignore -e ignore c++ -o hello -m32 \ + -static test.cpp + atf_check -s exit:0 -o inline:"CTOR A\nCTOR B\nDTOR B:10\nDTOR A:20\n" ./hello +} + +atf_init_test_cases() +{ + + atf_add_test_case static_destructor + atf_add_test_case static_destructor_profile + atf_add_test_case static_destructor_pic + atf_add_test_case static_destructor_pie + atf_add_test_case static_destructor32 + atf_add_test_case static_destructor_static + atf_add_test_case static_destructor_pic_32 + atf_add_test_case static_destructor_pic_profile + atf_add_test_case static_destructor_pic_profile_32 + atf_add_test_case static_destructor_profile_32 +} diff --git a/usr.bin/c++/t_tsan_data_race.sh b/usr.bin/c++/t_tsan_data_race.sh new file mode 100644 --- /dev/null +++ b/usr.bin/c++/t_tsan_data_race.sh @@ -0,0 +1,165 @@ +# Copyright (c) 2018 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Yang Zheng. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +tsan_available_archs() +{ + atf_set "require.arch" "x86_64" +} + +atf_test_case data_race +data_race_head() { + atf_set "descr" "Test thread sanitizer for data race condition" + atf_set "require.progs" "c++ paxctl" + tsan_available_archs +} + +atf_test_case data_race_profile +data_race_profile_head() { + atf_set "descr" "Test thread sanitizer for data race with profiling option" + atf_set "require.progs" "c++ paxctl" + tsan_available_archs +} +atf_test_case data_race_pic +data_race_pic_head() { + atf_set "descr" "Test thread sanitizer for data race with position independent code (PIC) flag" + atf_set "require.progs" "c++ paxctl" + tsan_available_archs +} +atf_test_case data_race_pie +data_race_pie_head() { + atf_set "descr" "Test thread sanitizer for data race with position independent execution (PIE) flag" + atf_set "require.progs" "c++ paxctl" + tsan_available_archs +} + +data_race_body(){ + cat > test.cc << EOF +#include +int GlobalData; pthread_barrier_t barrier; +void *Thread(void *a) { pthread_barrier_wait(&barrier); GlobalData = 42; return 0; } +int main() { + pthread_t t; + pthread_barrier_init(&barrier, NULL, 2); + pthread_create(&t, NULL, Thread, NULL); + pthread_barrier_wait(&barrier); + GlobalData = 43; + pthread_join(t, NULL); + return 0; +} +EOF + + c++ -fsanitize=thread -o test test.cc + paxctl +a test + atf_check -s ignore -o ignore -e match:"WARNING: ThreadSanitizer: data race " ./test +} + +data_race_profile_body(){ + atf_expect_fail "PR toolchain/55760" + cat > test.cc << EOF +#include +int GlobalData; pthread_barrier_t barrier; +void *Thread(void *a) { pthread_barrier_wait(&barrier); GlobalData = 42; return 0; } +int main() { + pthread_t t; + pthread_barrier_init(&barrier, NULL, 2); + pthread_create(&t, NULL, Thread, NULL); + pthread_barrier_wait(&barrier); + GlobalData = 43; + pthread_join(t, NULL); + return 0; +} +EOF + + c++ -fsanitize=thread -o test -pg test.cc + paxctl +a test + atf_check -s ignore -o ignore -e match:"WARNING: ThreadSanitizer: data race " ./test +} + +data_race_pic_body(){ + cat > test.cc << EOF +#include +#include +int help(int); +int main(int argc, char **argv) {return help(argc);} +EOF + + cat > pic.cc << EOF +#include +int GlobalData; pthread_barrier_t barrier; +void *Thread(void *a) { pthread_barrier_wait(&barrier); GlobalData = 42; return 0; } +int help(int argc) { + pthread_t t; + pthread_barrier_init(&barrier, NULL, 2); + pthread_create(&t, NULL, Thread, NULL); + pthread_barrier_wait(&barrier); + GlobalData = 43; + pthread_join(t, NULL); + return 0; +} +EOF + + c++ -fsanitize=thread -fPIC -shared -o libtest.so pic.cc + c++ -o test test.cc -fsanitize=thread -L. -ltest + paxctl +a test + + export LD_LIBRARY_PATH=. + atf_check -s ignore -o ignore -e match:"WARNING: ThreadSanitizer: data race " ./test +} +data_race_pie_body(){ + + #check whether -pie flag is supported on this architecture + if ! c++ -pie -dM -E - < /dev/null 2>/dev/null >/dev/null; then + atf_set_skip "c++ -pie not supported on this architecture" + fi + cat > test.cc << EOF +#include +int GlobalData; pthread_barrier_t barrier; +void *Thread(void *a) { pthread_barrier_wait(&barrier); GlobalData = 42; return 0; } +int main() { + pthread_t t; + pthread_barrier_init(&barrier, NULL, 2); + pthread_create(&t, NULL, Thread, NULL); + pthread_barrier_wait(&barrier); + GlobalData = 43; + pthread_join(t, NULL); + return 0; +} +EOF + + c++ -fsanitize=thread -o test -fpie -pie test.cc + paxctl +a test + atf_check -s ignore -o ignore -e match:"WARNING: ThreadSanitizer: data race " ./test +} + +atf_init_test_cases() +{ + atf_add_test_case data_race + atf_add_test_case data_race_profile + atf_add_test_case data_race_pie + atf_add_test_case data_race_pic +} diff --git a/usr.bin/c++/t_tsan_heap_use_after_free.sh b/usr.bin/c++/t_tsan_heap_use_after_free.sh new file mode 100644 --- /dev/null +++ b/usr.bin/c++/t_tsan_heap_use_after_free.sh @@ -0,0 +1,219 @@ +# Copyright (c) 2018 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Yang Zheng. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +tsan_available_archs() +{ + atf_set "require.arch" "x86_64" +} + +test_target() +{ + SUPPORT='n' + # Detect address space larger than 32 bits + maxaddress=`sysctl vm.maxaddress|awk '{print $3}'` + if [ $maxaddress -gt 4294967295 ]; then + if command -v cc >/dev/null 2>&1; then + if ! echo __clang__ | cc -E - | grep -q __clang__; then + SUPPORT='y' + elif ! cc -v 2>&1 | awk '/gcc version/{print $3}' | \ + awk -F '.' '($0+0) > 9 {exit 1}'; then + SUPPORT='y' + fi + fi + fi +} + +atf_test_case heap_use_after_free +heap_use_after_free_head() { + atf_set "descr" "Test thread sanitizer for use-after-free condition" + atf_set "require.progs" "c++ paxctl" + tsan_available_archs +} + +atf_test_case heap_use_after_free_profile +heap_use_after_free_profile_head() { + atf_set "descr" "Test thread sanitizer for use-after-free with profiling option" + atf_set "require.progs" "c++ paxctl" + tsan_available_archs +} +atf_test_case heap_use_after_free_pic +heap_use_after_free_pic_head() { + atf_set "descr" "Test thread sanitizer for use-after-free with position independent code (PIC) flag" + atf_set "require.progs" "c++ paxctl" + tsan_available_archs +} +atf_test_case heap_use_after_free_pie +heap_use_after_free_pie_head() { + atf_set "descr" "Test thread sanitizer for use-after-free with position independent execution (PIE) flag" + atf_set "require.progs" "c++ paxctl" + tsan_available_archs +} + +heap_use_after_free_body(){ + cat > test.cc << EOF +#include +#include + +int *ptr; +pthread_barrier_t barrier; +void *Thread(void *a) { + pthread_barrier_wait(&barrier); + *ptr = 42; + return 0; +} + +int main() { + pthread_t t; + pthread_barrier_init(&barrier, NULL, 2); + ptr = (int *)malloc(sizeof(int)); + pthread_create(&t, NULL, Thread, NULL); + free(ptr); + pthread_barrier_wait(&barrier); + pthread_join(t, NULL); + return 0; +} +EOF + + c++ -fsanitize=thread -o test test.cc + paxctl +a test + atf_check -s ignore -o ignore -e match:"WARNING: ThreadSanitizer: heap-use-after-free" ./test +} + +heap_use_after_free_profile_body(){ + atf_expect_fail "PR toolchain/55760" + cat > test.cc << EOF +#include +#include + +int *ptr; +pthread_barrier_t barrier; +void *Thread(void *a) { + pthread_barrier_wait(&barrier); + *ptr = 42; + return 0; +} + +int main() { + pthread_t t; + pthread_barrier_init(&barrier, NULL, 2); + ptr = (int *)malloc(sizeof(int)); + pthread_create(&t, NULL, Thread, NULL); + free(ptr); + pthread_barrier_wait(&barrier); + pthread_join(t, NULL); + return 0; +} +EOF + + c++ -fsanitize=thread -o test -pg test.cc + paxctl +a test + atf_check -s ignore -o ignore -e match:"WARNING: ThreadSanitizer: heap-use-after-free" ./test +} + +heap_use_after_free_pic_body(){ + cat > test.cc << EOF +#include +#include +int help(int); +int main(int argc, char **argv) {return help(argc);} +EOF + + cat > pic.cc << EOF +#include +#include + +int *ptr; +pthread_barrier_t barrier; +void *Thread(void *a) { + pthread_barrier_wait(&barrier); + *ptr = 42; + return 0; +} + +int help(int argc) { + pthread_t t; + pthread_barrier_init(&barrier, NULL, 2); + ptr = (int *)malloc(sizeof(int)); + pthread_create(&t, NULL, Thread, NULL); + free(ptr); + pthread_barrier_wait(&barrier); + pthread_join(t, NULL); + return 0; +} +EOF + + c++ -fsanitize=thread -fPIC -shared -o libtest.so pic.cc + c++ -o test test.cc -fsanitize=thread -L. -ltest + paxctl +a test + + export LD_LIBRARY_PATH=. + atf_check -s ignore -o ignore -e match:"WARNING: ThreadSanitizer: heap-use-after-free" ./test +} +heap_use_after_free_pie_body(){ + + #check whether -pie flag is supported on this architecture + if ! c++ -pie -dM -E - < /dev/null 2>/dev/null >/dev/null; then + atf_set_skip "c++ -pie not supported on this architecture" + fi + cat > test.cc << EOF +#include +#include + +int *ptr; +pthread_barrier_t barrier; +void *Thread(void *a) { + pthread_barrier_wait(&barrier); + *ptr = 42; + return 0; +} + +int main() { + pthread_t t; + pthread_barrier_init(&barrier, NULL, 2); + ptr = (int *)malloc(sizeof(int)); + pthread_create(&t, NULL, Thread, NULL); + free(ptr); + pthread_barrier_wait(&barrier); + pthread_join(t, NULL); + return 0; +} +EOF + + c++ -fsanitize=thread -o test -fpie -pie test.cc + paxctl +a test + atf_check -s ignore -o ignore -e match:"WARNING: ThreadSanitizer: heap-use-after-free" ./test +} + + +atf_init_test_cases() +{ + atf_add_test_case heap_use_after_free + atf_add_test_case heap_use_after_free_profile + atf_add_test_case heap_use_after_free_pie + atf_add_test_case heap_use_after_free_pic +} diff --git a/usr.bin/c++/t_tsan_lock_order_inversion.sh b/usr.bin/c++/t_tsan_lock_order_inversion.sh new file mode 100644 --- /dev/null +++ b/usr.bin/c++/t_tsan_lock_order_inversion.sh @@ -0,0 +1,186 @@ +# Copyright (c) 2018 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Yang Zheng. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + + +tsan_available_archs() +{ + atf_set "require.arch" "x86_64" +} + +atf_test_case lock_order_inversion +lock_order_inversion_head() { + atf_set "descr" "Test thread sanitizer for lock order inversion condition" + atf_set "require.progs" "c++ paxctl" + tsan_available_archs +} + +atf_test_case lock_order_inversion_profile +lock_order_inversion_profile_head() { + atf_set "descr" "Test thread sanitizer for lock order inversion with profiling option" + atf_set "require.progs" "c++ paxctl" + tsan_available_archs +} +atf_test_case lock_order_inversion_pic +lock_order_inversion_pic_head() { + atf_set "descr" "Test thread sanitizer for lock order inversion with position independent code (PIC) flag" + atf_set "require.progs" "c++ paxctl" + tsan_available_archs +} +atf_test_case lock_order_inversion_pie +lock_order_inversion_pie_head() { + atf_set "descr" "Test thread sanitizer for lock order inversion with position independent execution (PIE) flag" + atf_set "require.progs" "c++ paxctl" + tsan_available_archs +} + +lock_order_inversion_body(){ + cat > test.cc << EOF +#include + +pthread_mutex_t l1, l2; +int main() { + pthread_mutex_init(&l1, NULL); + pthread_mutex_init(&l2, NULL); + pthread_mutex_lock(&l2); + pthread_mutex_lock(&l1); + pthread_mutex_unlock(&l1); + pthread_mutex_unlock(&l2); + + pthread_mutex_lock(&l1); + pthread_mutex_lock(&l2); + pthread_mutex_unlock(&l2); + pthread_mutex_unlock(&l1); + return 0; +} +EOF + + c++ -fsanitize=thread -o test test.cc + paxctl +a test + atf_check -s ignore -o ignore -e match:"WARNING: ThreadSanitizer: lock-order-inversion" ./test +} + +lock_order_inversion_profile_body(){ + atf_expect_fail "PR toolchain/55760" + cat > test.cc << EOF +#include + +pthread_mutex_t l1, l2; +int main() { + pthread_mutex_init(&l1, NULL); + pthread_mutex_init(&l2, NULL); + pthread_mutex_lock(&l2); + pthread_mutex_lock(&l1); + pthread_mutex_unlock(&l1); + pthread_mutex_unlock(&l2); + + pthread_mutex_lock(&l1); + pthread_mutex_lock(&l2); + pthread_mutex_unlock(&l2); + pthread_mutex_unlock(&l1); + return 0; +} +EOF + + c++ -fsanitize=thread -o test -pg test.cc + paxctl +a test + atf_check -s ignore -o ignore -e match:"WARNING: ThreadSanitizer: lock-order-inversion" ./test +} + +lock_order_inversion_pic_body(){ + cat > test.cc << EOF +#include +#include +int help(int); +int main(int argc, char **argv) {return help(argc);} +EOF + + cat > pic.cc << EOF +#include + +pthread_mutex_t l1, l2; +int help(int argc) { + pthread_mutex_init(&l1, NULL); + pthread_mutex_init(&l2, NULL); + pthread_mutex_lock(&l2); + pthread_mutex_lock(&l1); + pthread_mutex_unlock(&l1); + pthread_mutex_unlock(&l2); + + pthread_mutex_lock(&l1); + pthread_mutex_lock(&l2); + pthread_mutex_unlock(&l2); + pthread_mutex_unlock(&l1); + return 0; +} +EOF + + c++ -fsanitize=thread -fPIC -shared -o libtest.so pic.cc + c++ -o test test.cc -fsanitize=thread -L. -ltest + paxctl +a test + + export LD_LIBRARY_PATH=. + atf_check -s ignore -o ignore -e match:"WARNING: ThreadSanitizer: lock-order-inversion" ./test +} +lock_order_inversion_pie_body(){ + + #check whether -pie flag is supported on this architecture + if ! c++ -pie -dM -E - < /dev/null 2>/dev/null >/dev/null; then + atf_set_skip "c++ -pie not supported on this architecture" + fi + cat > test.cc << EOF +#include + +pthread_mutex_t l1, l2; +int main() { + pthread_mutex_init(&l1, NULL); + pthread_mutex_init(&l2, NULL); + pthread_mutex_lock(&l2); + pthread_mutex_lock(&l1); + pthread_mutex_unlock(&l1); + pthread_mutex_unlock(&l2); + + pthread_mutex_lock(&l1); + pthread_mutex_lock(&l2); + pthread_mutex_unlock(&l2); + pthread_mutex_unlock(&l1); + return 0; +} +EOF + + c++ -fsanitize=thread -o test -fpie -pie test.cc + paxctl +a test + atf_check -s ignore -o ignore -e match:"WARNING: ThreadSanitizer: lock-order-inversion" ./test +} + +atf_init_test_cases() +{ + atf_add_test_case lock_order_inversion + atf_add_test_case lock_order_inversion_profile + atf_add_test_case lock_order_inversion_pie + atf_add_test_case lock_order_inversion_pic +} diff --git a/usr.bin/c++/t_tsan_locked_mutex_destroy.sh b/usr.bin/c++/t_tsan_locked_mutex_destroy.sh new file mode 100644 --- /dev/null +++ b/usr.bin/c++/t_tsan_locked_mutex_destroy.sh @@ -0,0 +1,203 @@ +# Copyright (c) 2018 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Yang Zheng. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + + +tsan_available_archs() +{ + atf_set "require.arch" "x86_64" +} + +atf_test_case locked_mutex_destroy +locked_mutex_destroy_head() { + atf_set "descr" "Test thread sanitizer for destroying locked mutex condition" + atf_set "require.progs" "c++ paxctl" + tsan_available_archs +} + +atf_test_case locked_mutex_destroy_profile +locked_mutex_destroy_profile_head() { + atf_set "descr" "Test thread sanitizer for destroying locked mutex with profiling option" + atf_set "require.progs" "c++ paxctl" + tsan_available_archs +} +atf_test_case locked_mutex_destroy_pic +locked_mutex_destroy_pic_head() { + atf_set "descr" "Test thread sanitizer for destroying locked mutex with position independent code (PIC) flag" + atf_set "require.progs" "c++ paxctl" + tsan_available_archs +} +atf_test_case locked_mutex_destroy_pie +locked_mutex_destroy_pie_head() { + atf_set "descr" "Test thread sanitizer for destroying locked mutex with position independent execution (PIE) flag" + atf_set "require.progs" "c++ paxctl" + tsan_available_archs +} + +locked_mutex_destroy_body(){ + cat > test.cc << EOF +#include +#include + +pthread_mutex_t mutex; +pthread_barrier_t barrier; +void *Thread(void *a) { + pthread_mutex_lock(&mutex); + pthread_barrier_wait(&barrier); + return 0; +} + +int main() { + pthread_t t; + pthread_barrier_init(&barrier, NULL, 2); + pthread_mutex_init(&mutex, NULL); + pthread_create(&t, NULL, Thread, NULL); + pthread_barrier_wait(&barrier); + pthread_mutex_destroy(&mutex); + pthread_join(t, NULL); + return 0; +} +EOF + + c++ -fsanitize=thread -o test test.cc + paxctl +a test + atf_check -s ignore -o ignore -e match:"WARNING: ThreadSanitizer: destroy of a locked mutex" ./test +} + +locked_mutex_destroy_profile_body(){ + atf_expect_fail "PR toolchain/55760" + cat > test.cc << EOF +#include +#include + +pthread_mutex_t mutex; +pthread_barrier_t barrier; +void *Thread(void *a) { + pthread_mutex_lock(&mutex); + pthread_barrier_wait(&barrier); + return 0; +} + +int main() { + pthread_t t; + pthread_barrier_init(&barrier, NULL, 2); + pthread_mutex_init(&mutex, NULL); + pthread_create(&t, NULL, Thread, NULL); + pthread_barrier_wait(&barrier); + pthread_mutex_destroy(&mutex); + pthread_join(t, NULL); + return 0; +} +EOF + + c++ -fsanitize=thread -o test -pg test.cc + paxctl +a test + atf_check -s ignore -o ignore -e match:"WARNING: ThreadSanitizer: destroy of a locked mutex" ./test +} + +locked_mutex_destroy_pic_body(){ + cat > test.cc << EOF +#include +#include +int help(int); +int main(int argc, char **argv) {return help(argc);} +EOF + + cat > pic.cc << EOF +#include +#include + +pthread_mutex_t mutex; +pthread_barrier_t barrier; +void *Thread(void *a) { + pthread_mutex_lock(&mutex); + pthread_barrier_wait(&barrier); + return 0; +} + +int help(int argc) { + pthread_t t; + pthread_barrier_init(&barrier, NULL, 2); + pthread_mutex_init(&mutex, NULL); + pthread_create(&t, NULL, Thread, NULL); + pthread_barrier_wait(&barrier); + pthread_mutex_destroy(&mutex); + pthread_join(t, NULL); + return 0; +} +EOF + + c++ -fsanitize=thread -fPIC -shared -o libtest.so pic.cc + c++ -o test test.cc -fsanitize=thread -L. -ltest + paxctl +a test + + export LD_LIBRARY_PATH=. + atf_check -s ignore -o ignore -e match:"WARNING: ThreadSanitizer: destroy of a locked mutex" ./test +} +locked_mutex_destroy_pie_body(){ + + #check whether -pie flag is supported on this architecture + if ! c++ -pie -dM -E - < /dev/null 2>/dev/null >/dev/null; then + atf_set_skip "c++ -pie not supported on this architecture" + fi + cat > test.cc << EOF +#include +#include + +pthread_mutex_t mutex; +pthread_barrier_t barrier; +void *Thread(void *a) { + pthread_mutex_lock(&mutex); + pthread_barrier_wait(&barrier); + return 0; +} + +int main() { + pthread_t t; + pthread_barrier_init(&barrier, NULL, 2); + pthread_mutex_init(&mutex, NULL); + pthread_create(&t, NULL, Thread, NULL); + pthread_barrier_wait(&barrier); + pthread_mutex_destroy(&mutex); + pthread_join(t, NULL); + return 0; +} +EOF + + c++ -fsanitize=thread -o test -fpie -pie test.cc + paxctl +a test + atf_check -s ignore -o ignore -e match:"WARNING: ThreadSanitizer: destroy of a locked mutex" ./test +} + + +atf_init_test_cases() +{ + atf_add_test_case locked_mutex_destroy + atf_add_test_case locked_mutex_destroy_profile + atf_add_test_case locked_mutex_destroy_pie + atf_add_test_case locked_mutex_destroy_pic +} diff --git a/usr.bin/c++/t_tsan_signal_errno.sh b/usr.bin/c++/t_tsan_signal_errno.sh new file mode 100644 --- /dev/null +++ b/usr.bin/c++/t_tsan_signal_errno.sh @@ -0,0 +1,189 @@ +# Copyright (c) 2018 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Yang Zheng. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +tsan_available_archs() +{ + atf_set "require.arch" "x86_64" +} + +atf_test_case signal_errno +signal_errno_head() { + atf_set "descr" "Test thread sanitizer for errno modification in signal condition" + atf_set "require.progs" "c++ paxctl" + tsan_available_archs +} + +atf_test_case signal_errno_profile +signal_errno_profile_head() { + atf_set "descr" "Test thread sanitizer for errno modification in signal with profiling option" + atf_set "require.progs" "c++ paxctl" + tsan_available_archs +} +atf_test_case signal_errno_pic +signal_errno_pic_head() { + atf_set "descr" "Test thread sanitizer for errno modification in signal with position independent code (PIC) flag" + atf_set "require.progs" "c++ paxctl" + tsan_available_archs +} +atf_test_case signal_errno_pie +signal_errno_pie_head() { + atf_set "descr" "Test thread sanitizer for errno modification in signal with position independent execution (PIE) flag" + atf_set "require.progs" "c++ paxctl" + tsan_available_archs +} + +signal_errno_body(){ + cat > test.cc << EOF +#include +#include +#include +#include + +pthread_t mainth; +static void MyHandler(int a, siginfo_t *s, void *c) { errno = 1; } +static void* sendsignal(void *p) { pthread_kill(mainth, SIGPROF); return NULL; } +int main() { + mainth = pthread_self(); + struct sigaction act = {}; + act.sa_sigaction = &MyHandler; + sigaction(SIGPROF, &act, 0); + pthread_t th; + pthread_create(&th, 0, sendsignal, 0); + pthread_join(th, 0); + return 0; +} +EOF + + c++ -fsanitize=thread -o test test.cc + paxctl +a test + atf_check -s ignore -o ignore -e match:"WARNING: ThreadSanitizer: signal handler spoils errno" ./test +} + +signal_errno_profile_body(){ + atf_expect_fail "PR toolchain/55760" + cat > test.cc << EOF +#include +#include +#include +#include + +pthread_t mainth; +static void MyHandler(int a, siginfo_t *s, void *c) { errno = 1; } +static void* sendsignal(void *p) { pthread_kill(mainth, SIGPROF); return NULL; } +int main() { + mainth = pthread_self(); + struct sigaction act = {}; + act.sa_sigaction = &MyHandler; + sigaction(SIGPROF, &act, 0); + pthread_t th; + pthread_create(&th, 0, sendsignal, 0); + pthread_join(th, 0); + return 0; +} +EOF + + c++ -fsanitize=thread -o test -pg test.cc + paxctl +a test + atf_check -s ignore -o ignore -e match:"WARNING: ThreadSanitizer: signal handler spoils errno" ./test +} + +signal_errno_pic_body(){ + cat > test.cc << EOF +#include +#include +int help(int); +int main(int argc, char **argv) {return help(argc);} +EOF + + cat > pic.cc << EOF +#include +#include +#include +#include + +pthread_t mainth; +static void MyHandler(int a, siginfo_t *s, void *c) { errno = 1; } +static void* sendsignal(void *p) { pthread_kill(mainth, SIGPROF); return NULL; } +int help(int argc) { + mainth = pthread_self(); + struct sigaction act = {}; + act.sa_sigaction = &MyHandler; + sigaction(SIGPROF, &act, 0); + pthread_t th; + pthread_create(&th, 0, sendsignal, 0); + pthread_join(th, 0); + return 0; +} +EOF + + c++ -fsanitize=thread -fPIC -shared -o libtest.so pic.cc + c++ -o test test.cc -fsanitize=thread -L. -ltest + paxctl +a test + + export LD_LIBRARY_PATH=. + atf_check -s ignore -o ignore -e match:"WARNING: ThreadSanitizer: signal handler spoils errno" ./test +} +signal_errno_pie_body(){ + + #check whether -pie flag is supported on this architecture + if ! c++ -pie -dM -E - < /dev/null 2>/dev/null >/dev/null; then + atf_set_skip "c++ -pie not supported on this architecture" + fi + cat > test.cc << EOF +#include +#include +#include +#include + +pthread_t mainth; +static void MyHandler(int a, siginfo_t *s, void *c) { errno = 1; } +static void* sendsignal(void *p) { pthread_kill(mainth, SIGPROF); return NULL; } +int main() { + mainth = pthread_self(); + struct sigaction act = {}; + act.sa_sigaction = &MyHandler; + sigaction(SIGPROF, &act, 0); + pthread_t th; + pthread_create(&th, 0, sendsignal, 0); + pthread_join(th, 0); + return 0; +} +EOF + + c++ -fsanitize=thread -o test -fpie -pie test.cc + paxctl +a test + atf_check -s ignore -o ignore -e match:"WARNING: ThreadSanitizer: signal handler spoils errno" ./test +} + +atf_init_test_cases() +{ + atf_add_test_case signal_errno + atf_add_test_case signal_errno_profile + atf_add_test_case signal_errno_pie + atf_add_test_case signal_errno_pic +} diff --git a/usr.bin/c++/t_tsan_thread_leak.sh b/usr.bin/c++/t_tsan_thread_leak.sh new file mode 100644 --- /dev/null +++ b/usr.bin/c++/t_tsan_thread_leak.sh @@ -0,0 +1,214 @@ +# Copyright (c) 2018 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Yang Zheng. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +test_target() +{ + SUPPORT='n' + # Detect address space larger than 32 bits + maxaddress=`sysctl vm.maxaddress|awk '{print $3}'` + if [ $maxaddress -gt 4294967295 ]; then + if command -v cc >/dev/null 2>&1; then + if ! echo __clang__ | cc -E - | grep -q __clang__; then + SUPPORT='y' + elif ! cc -v 2>&1 | awk '/gcc version/{print $3}' | \ + awk -F '.' '($0+0) > 9 {exit 1}'; then + SUPPORT='y' + fi + fi + fi +} + +atf_test_case thread_leak +thread_leak_head() { + atf_set "descr" "Test thread sanitizer for thread leak condition" + atf_set "require.progs" "c++ paxctl" +} + +atf_test_case thread_leak_profile +thread_leak_profile_head() { + atf_set "descr" "Test thread sanitizer for thread leak with profiling option" + atf_set "require.progs" "c++ paxctl" +} +atf_test_case thread_leak_pic +thread_leak_pic_head() { + atf_set "descr" "Test thread sanitizer for thread leak with position independent code (PIC) flag" + atf_set "require.progs" "c++ paxctl" +} +atf_test_case thread_leak_pie +thread_leak_pie_head() { + atf_set "descr" "Test thread sanitizer for thread leak with position independent execution (PIE) flag" + atf_set "require.progs" "c++ paxctl" +} + +thread_leak_body(){ + cat > test.cc << EOF +#include +#include + +int GlobalData; +pthread_barrier_t barrier; +void *Thread(void *a) { + pthread_barrier_wait(&barrier); + return 0; +} + +int main() { + pthread_t t; + pthread_barrier_init(&barrier, NULL, 2); + pthread_create(&t, NULL, Thread, NULL); + pthread_barrier_wait(&barrier); + sleep(1); + return 0; +} +EOF + + c++ -fsanitize=thread -o test test.cc + paxctl +a test + atf_check -s ignore -o ignore -e match:"WARNING: ThreadSanitizer: thread leak" ./test +} + +thread_leak_profile_body(){ + atf_expect_fail "PR toolchain/55760" + cat > test.cc << EOF +#include +#include + +int GlobalData; +pthread_barrier_t barrier; +void *Thread(void *a) { + pthread_barrier_wait(&barrier); + return 0; +} + +int main() { + pthread_t t; + pthread_barrier_init(&barrier, NULL, 2); + pthread_create(&t, NULL, Thread, NULL); + pthread_barrier_wait(&barrier); + sleep(1); + return 0; +} +EOF + + c++ -fsanitize=thread -o test -pg test.cc + paxctl +a test + atf_check -s ignore -o ignore -e match:"WARNING: ThreadSanitizer: thread leak" ./test +} + +thread_leak_pic_body(){ + cat > test.cc << EOF +#include +#include +int help(int); +int main(int argc, char **argv) {return help(argc);} +EOF + + cat > pic.cc << EOF +#include +#include + +int GlobalData; +pthread_barrier_t barrier; +void *Thread(void *a) { + pthread_barrier_wait(&barrier); + return 0; +} + +int help(int argc) { + pthread_t t; + pthread_barrier_init(&barrier, NULL, 2); + pthread_create(&t, NULL, Thread, NULL); + pthread_barrier_wait(&barrier); + sleep(1); + return 0; +} +EOF + + c++ -fsanitize=thread -fPIC -shared -o libtest.so pic.cc + c++ -o test test.cc -fsanitize=thread -L. -ltest + paxctl +a test + + export LD_LIBRARY_PATH=. + atf_check -s ignore -o ignore -e match:"WARNING: ThreadSanitizer: thread leak" ./test +} +thread_leak_pie_body(){ + + #check whether -pie flag is supported on this architecture + if ! c++ -pie -dM -E - < /dev/null 2>/dev/null >/dev/null; then + atf_set_skip "c++ -pie not supported on this architecture" + fi + cat > test.cc << EOF +#include +#include + +int GlobalData; +pthread_barrier_t barrier; +void *Thread(void *a) { + pthread_barrier_wait(&barrier); + return 0; +} + +int main() { + pthread_t t; + pthread_barrier_init(&barrier, NULL, 2); + pthread_create(&t, NULL, Thread, NULL); + pthread_barrier_wait(&barrier); + sleep(1); + return 0; +} +EOF + + c++ -fsanitize=thread -o test -fpie -pie test.cc + paxctl +a test + atf_check -s ignore -o ignore -e match:"WARNING: ThreadSanitizer: thread leak" ./test +} + + +atf_test_case target_not_supported +target_not_supported_head() +{ + atf_set "descr" "Test forced skip" +} + +target_not_supported_body() +{ + atf_skip "Target is not supported" +} + +atf_init_test_cases() +{ + test_target + test $SUPPORT = 'n' && { + atf_add_test_case target_not_supported + return 0 + } + atf_add_test_case thread_leak + atf_add_test_case thread_leak_profile + atf_add_test_case thread_leak_pie + atf_add_test_case thread_leak_pic +} diff --git a/usr.bin/c++/t_tsan_vptr_race.sh b/usr.bin/c++/t_tsan_vptr_race.sh new file mode 100644 --- /dev/null +++ b/usr.bin/c++/t_tsan_vptr_race.sh @@ -0,0 +1,238 @@ +# Copyright (c) 2018 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Yang Zheng. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +test_target() +{ + SUPPORT='n' + # Detect address space larger than 32 bits + maxaddress=`sysctl vm.maxaddress|awk '{print $3}'` + if [ $maxaddress -gt 4294967295 ]; then + if command -v cc >/dev/null 2>&1; then + if ! echo __clang__ | cc -E - | grep -q __clang__; then + SUPPORT='y' + elif ! cc -v 2>&1 | awk '/gcc version/{print $3}' | \ + awk -F '.' '($0+0) > 9 {exit 1}'; then + SUPPORT='y' + fi + fi + fi +} + +atf_test_case vptr_race +vptr_race_head() { + atf_set "descr" "Test thread sanitizer for vptr race condition" + atf_set "require.progs" "c++ paxctl" +} + +atf_test_case vptr_race_profile +vptr_race_profile_head() { + atf_set "descr" "Test thread sanitizer for vptr race with profiling option" + atf_set "require.progs" "c++ paxctl" +} +atf_test_case vptr_race_pic +vptr_race_pic_head() { + atf_set "descr" "Test thread sanitizer for vptr race with position independent code (PIC) flag" + atf_set "require.progs" "c++ paxctl" +} +atf_test_case vptr_race_pie +vptr_race_pie_head() { + atf_set "descr" "Test thread sanitizer for vptr race with position independent execution (PIE) flag" + atf_set "require.progs" "c++ paxctl" +} + +vptr_race_body(){ + cat > test.cc << EOF +#include +pthread_barrier_t barrier; +struct A { + volatile bool done; + A(): done(false) { } + virtual void Done() { done = true; } + virtual ~A() { while (!done) ; } +}; +struct B: A {}; +A *obj = new B; +void *Thread1(void *x) { + pthread_barrier_wait(&barrier); + obj->Done(); + return NULL; +} +int main() { + pthread_barrier_init(&barrier, NULL, 2); + pthread_t t; + pthread_create(&t, NULL, Thread1, NULL); + pthread_barrier_wait(&barrier); + delete obj; + pthread_join(t, NULL); + return 0; +} +EOF + + c++ -fsanitize=thread -o test test.cc + paxctl +a test + atf_check -s ignore -o ignore -e match:"WARNING: ThreadSanitizer: data race on vptr \(ctor/dtor vs virtual call\)" ./test +} + +vptr_race_profile_body(){ + atf_expect_fail "PR toolchain/55760" + cat > test.cc << EOF +#include +pthread_barrier_t barrier; +struct A { + volatile bool done; + A(): done(false) { } + virtual void Done() { done = true; } + virtual ~A() { while (!done) ; } +}; +struct B: A {}; +A *obj = new B; +void *Thread1(void *x) { + pthread_barrier_wait(&barrier); + obj->Done(); + return NULL; +} +int main() { + pthread_barrier_init(&barrier, NULL, 2); + pthread_t t; + pthread_create(&t, NULL, Thread1, NULL); + pthread_barrier_wait(&barrier); + delete obj; + pthread_join(t, NULL); + return 0; +} +EOF + + c++ -fsanitize=thread -o test -pg test.cc + paxctl +a test + atf_check -s ignore -o ignore -e match:"WARNING: ThreadSanitizer: data race on vptr \(ctor/dtor vs virtual call\)" ./test +} + +vptr_race_pic_body(){ + cat > test.cc << EOF +#include +#include +int help(int); +int main(int argc, char **argv) {return help(argc);} +EOF + + cat > pic.cc << EOF +#include +pthread_barrier_t barrier; +struct A { + volatile bool done; + A(): done(false) { } + virtual void Done() { done = true; } + virtual ~A() { while (!done) ; } +}; +struct B: A {}; +A *obj = new B; +void *Thread1(void *x) { + pthread_barrier_wait(&barrier); + obj->Done(); + return NULL; +} +int help(int argc) { + pthread_barrier_init(&barrier, NULL, 2); + pthread_t t; + pthread_create(&t, NULL, Thread1, NULL); + pthread_barrier_wait(&barrier); + delete obj; + pthread_join(t, NULL); + return 0; +} +EOF + + c++ -fsanitize=thread -fPIC -shared -o libtest.so pic.cc + c++ -o test test.cc -fsanitize=thread -L. -ltest + paxctl +a test + + export LD_LIBRARY_PATH=. + atf_check -s ignore -o ignore -e match:"WARNING: ThreadSanitizer: data race on vptr \(ctor/dtor vs virtual call\)" ./test +} +vptr_race_pie_body(){ + + #check whether -pie flag is supported on this architecture + if ! c++ -pie -dM -E - < /dev/null 2>/dev/null >/dev/null; then + atf_set_skip "c++ -pie not supported on this architecture" + fi + cat > test.cc << EOF +#include +pthread_barrier_t barrier; +struct A { + volatile bool done; + A(): done(false) { } + virtual void Done() { done = true; } + virtual ~A() { while (!done) ; } +}; +struct B: A {}; +A *obj = new B; +void *Thread1(void *x) { + pthread_barrier_wait(&barrier); + obj->Done(); + return NULL; +} +int main() { + pthread_barrier_init(&barrier, NULL, 2); + pthread_t t; + pthread_create(&t, NULL, Thread1, NULL); + pthread_barrier_wait(&barrier); + delete obj; + pthread_join(t, NULL); + return 0; +} +EOF + + c++ -fsanitize=thread -o test -fpie -pie test.cc + paxctl +a test + atf_check -s ignore -o ignore -e match:"WARNING: ThreadSanitizer: data race on vptr \(ctor/dtor vs virtual call\)" ./test +} + + +atf_test_case target_not_supported +target_not_supported_head() +{ + atf_set "descr" "Test forced skip" +} + +target_not_supported_body() +{ + atf_skip "Target is not supported" +} + +atf_init_test_cases() +{ + test_target + test $SUPPORT = 'n' && { + atf_add_test_case target_not_supported + return 0 + } + atf_add_test_case vptr_race + atf_add_test_case vptr_race_profile + atf_add_test_case vptr_race_pie + atf_add_test_case vptr_race_pic +} diff --git a/usr.bin/c++/t_ubsan_int_add_overflow.sh b/usr.bin/c++/t_ubsan_int_add_overflow.sh new file mode 100644 --- /dev/null +++ b/usr.bin/c++/t_ubsan_int_add_overflow.sh @@ -0,0 +1,48 @@ +# Copyright (c) 2018, 2019 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Harry Pantazis. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +UBSAN_CODE=' +#include +#include +#include +int help(int); +#ifndef PIC_MAIN +int help(int count) {volatile int l = INT_MAX; l+= count; return l;} +#endif +#ifndef PIC_FOO +int main(int argc, char **argv) {volatile int l = INT_MAX; l+=argc; return l;} +#endif +' + +ubsan_test_case int_add_overflow "int addition overflows" \ + "signed integer overflow" + +atf_init_test_cases() +{ + ubsan_add_test_cases int_add_overflow +} diff --git a/usr.bin/c++/t_ubsan_int_divzero.sh b/usr.bin/c++/t_ubsan_int_divzero.sh new file mode 100644 --- /dev/null +++ b/usr.bin/c++/t_ubsan_int_divzero.sh @@ -0,0 +1,46 @@ +# Copyright (c) 2018, 2019 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Harry Pantazis. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +UBSAN_CODE=' +#include +#include +int help(int); +#ifndef PIC_MAIN +int help(int count) {volatile int l = count; volatile int k = 0; return l/k;} +#endif +#ifndef PIC_FOO +int main(int argc, char **argv) {return help(argc);} +#endif +' + +ubsan_test_case int_divzero "int division with zero" "division by zero" + +atf_init_test_cases() +{ + ubsan_add_test_cases int_divzero +} diff --git a/usr.bin/c++/t_ubsan_int_neg_overflow.sh b/usr.bin/c++/t_ubsan_int_neg_overflow.sh new file mode 100644 --- /dev/null +++ b/usr.bin/c++/t_ubsan_int_neg_overflow.sh @@ -0,0 +1,47 @@ +# Copyright (c) 2018, 2019 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Harry Pantazis. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +UBSAN_CODE=' +#include +#include +#include +int help(int); +#ifndef PIC_MAIN +int help(int count) {volatile int l = INT_MIN; l = -l; return l;} +#endif +#ifndef PIC_FOO +int main(int argc, char **argv) {return help(argc);} +#endif +' + +ubsan_test_case int_neg_overflow "int negation overflows" "negation of" + +atf_init_test_cases() +{ + ubsan_add_test_cases int_neg_overflow +} diff --git a/usr.bin/c++/t_ubsan_int_sub_overflow.sh b/usr.bin/c++/t_ubsan_int_sub_overflow.sh new file mode 100644 --- /dev/null +++ b/usr.bin/c++/t_ubsan_int_sub_overflow.sh @@ -0,0 +1,48 @@ +# Copyright (c) 2018, 2019 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Harry Pantazis. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +UBSAN_CODE=' +#include +#include +#include +int help(int); +#ifndef PIC_MAIN +int help(int count) {volatile int l = INT_MIN; l-= count; return l;} +#endif +#ifndef PIC_FOO +int main(int argc, char **argv) {volatile int k = help(argc); return k;} +#endif +' + +ubsan_test_case int_sub_overflow "int subtraction overflows" \ + "signed integer overflow" + +atf_init_test_cases() +{ + ubsan_add_test_cases int_sub_overflow +} diff --git a/usr.bin/c++/t_ubsan_vla_out_of_bounds.sh b/usr.bin/c++/t_ubsan_vla_out_of_bounds.sh new file mode 100644 --- /dev/null +++ b/usr.bin/c++/t_ubsan_vla_out_of_bounds.sh @@ -0,0 +1,47 @@ +# Copyright (c) 2018, 2019 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Harry Pantazis. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +UBSAN_CODE=' +#include +#include +void help(int); +#ifndef PIC_MAIN +void help(int count) {volatile int val1 = count, val2 = count+1; volatile int arr[val1]; arr[val2] = count; } +#endif +#ifndef PIC_FOO +int main(int argc, char **argv) {help(argc); exit(0);} +#endif +' + +ubsan_test_case vla_out_of_bounds \ + "vla (Variable Length Array) out of bounds" "out of bounds" + +atf_init_test_cases() +{ + ubsan_add_test_cases vla_out_of_bounds +} diff --git a/usr.bin/c++/ubsan_common.subr b/usr.bin/c++/ubsan_common.subr new file mode 100644 --- /dev/null +++ b/usr.bin/c++/ubsan_common.subr @@ -0,0 +1,162 @@ +# $NetBSD: ubsan_common.subr,v 1.1 2019/01/29 20:07:03 mgorny Exp $ +# +# Copyright (c) 2018, 2019 The NetBSD Foundation, Inc. +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +test_target() +{ + SUPPORT='n' + if ! echo __GNUC__ | c++ -E - | grep -q __GNUC__; then + SUPPORT='y' + fi + + if ! echo __clang__ | c++ -E - | grep -q __clang__; then + SUPPORT='y' + fi +} + +atf_test_case target_not_supported +target_not_supported_head() +{ + atf_set "descr" "Test forced skip" +} + +target_not_supported_body() +{ + atf_skip "Target is not supported" +} + +# Add a new test case, with head & body. +# asan_test_case +ubsan_test_case() { + atf_test_case "$1" + eval "$1_head() { + atf_set 'descr' 'Test Undefined Behavior for $2' + atf_set 'require.progs' 'c++' + }" + + atf_test_case "$1_profile" + eval "$1_head() { + atf_set 'descr' 'Test Undefined Behavior for $2 with profiling option' + atf_set 'require.progs' 'c++' + }" + + atf_test_case "$1_pic" + eval "$1_head() { + atf_set 'descr' 'Test Undefined Behavior for $2 with position independent code (PIC) flag' + atf_set 'require.progs' 'c++' + }" + + atf_test_case "$1_pie" + eval "$1_head() { + atf_set 'descr' 'Test Undefined Behavior for $2 with position independent execution (PIE) flag' + atf_set 'require.progs' 'c++' + }" + + atf_test_case "${1}32" + eval "$1_head() { + atf_set 'descr' 'Test Undefined Behavior for $2 in NetBSD_32 emulation' + atf_set 'require.progs' 'c++ file diff cat' + }" + + eval "$1_body() { + echo \"\$UBSAN_CODE\" > test.cpp + c++ -fsanitize=undefined -o test test.cpp + # note: ignoring exit status due to inconsistency between gcc/clang + # (and between individual tests) + atf_check -s ignore -e match:'$3' ./test + } + + $1_profile_body() { + echo \"\$UBSAN_CODE\" > test.cpp + c++ -fsanitize=undefined -o test -pg test.cpp + atf_check -s ignore -e match:'$3' ./test + } + + $1_pic_body() { + echo \"\$UBSAN_CODE\" > test.cpp + c++ -DPIC_FOO -fsanitize=undefined -fPIC -shared -o libtest.so test.cpp + c++ -DPIC_MAIN -o test test.cpp -fsanitize=undefined -L. -ltest + + export LD_LIBRARY_PATH=. + atf_check -s ignore -e match:'$3' ./test + } + + $1_pie_body() { + # check whether this arch supports -pice + if ! c++ -pie -dM -E - < /dev/null 2>/dev/null >/dev/null; then + atf_set_skip 'c++ -pie not supported on this architecture' + fi + echo \"\$UBSAN_CODE\" > test.cpp + c++ -fsanitize=undefined -o test -fpie -pie test.cpp + atf_check -s ignore -e match:'$3' ./test + } + + ${1}32_body() { + # check whether this arch is 64bit + if ! c++ -dM -E - < /dev/null | fgrep -q _LP64; then + atf_skip 'this is not a 64 bit architecture' + fi + if ! c++ -m32 -dM -E - < /dev/null 2>/dev/null > ./def32; then + atf_skip 'c++ -m32 not supported on this architecture' + else + if fgrep -q _LP64 ./def32; then + atf_fail 'c++ -m32 does not generate netbsd32 binaries' + fi + fi + + echo \"\$UBSAN_CODE\" > test.cpp + c++ -fsanitize=undefined -o df32 -m32 test.cpp + c++ -fsanitize=undefined -o df64 test.cpp + file -b ./df32 > ./ftype32 + file -b ./df64 > ./ftype64 + if diff ./ftype32 ./ftype64 >/dev/null; then + atf_fail 'generated binaries do not differ' + fi + echo '32bit binaries on this platform are:' + cat ./ftype32 + echo 'While native (64bit) binaries are:' + cat ./ftype64 + atf_check -s ignore -e match:'$3' ./df32 + +# and another test with profile 32bit binaries + c++ -fsanitize=undefined -o test -pg -m32 test.cpp + atf_check -s ignore -e match:'$3' ./test + }" +} + +ubsan_add_test_cases() { + test_target + test $SUPPORT = 'n' && { + atf_add_test_case target_not_supported + return 0 + } + + atf_add_test_case "$1" +# atf_add_test_case "$1_profile" + atf_add_test_case "$1_pic" + atf_add_test_case "$1_pie" +# atf_add_test_case "${1}32" +} diff --git a/usr.bin/cc/Makefile b/usr.bin/cc/Makefile --- a/usr.bin/cc/Makefile +++ b/usr.bin/cc/Makefile @@ -1,9 +1,57 @@ -# $NetBSD: Makefile,v 1.1 2012/03/17 17:15:29 jruoho Exp $ +# $NetBSD: Makefile,v 1.8 2019/08/18 20:15:58 kamil Exp $ .include TESTSDIR= ${TESTSBASE}/usr.bin/cc -TESTS_SH= t_hello +ASAN_TESTS= # +ASAN_TESTS+= t_asan_double_free +ASAN_TESTS+= t_asan_global_buffer_overflow +ASAN_TESTS+= t_asan_heap_overflow +ASAN_TESTS+= t_asan_off_by_one +ASAN_TESTS+= t_asan_poison +ASAN_TESTS+= t_asan_uaf + +UBSAN_TESTS= # +UBSAN_TESTS+= t_ubsan_int_add_overflow +UBSAN_TESTS+= t_ubsan_int_neg_overflow +UBSAN_TESTS+= t_ubsan_vla_out_of_bounds +UBSAN_TESTS+= t_ubsan_int_sub_overflow +UBSAN_TESTS+= t_ubsan_int_divzero + +TESTS_SH= # +TESTS_SH+= $(ASAN_TESTS) +TESTS_SH+= $(UBSAN_TESTS) +TESTS_SH+= t_hello +TESTS_SH+= t_libgomp + +TESTS_SH+= t_fuzzer_oom +TESTS_SH+= t_fuzzer_simple +TESTS_SH+= t_fuzzer_timeout + +TESTS_SH+= t_msan_allocated_memory +TESTS_SH+= t_msan_check_mem +TESTS_SH+= t_msan_free +TESTS_SH+= t_msan_heap +TESTS_SH+= t_msan_partial_poison +TESTS_SH+= t_msan_poison +TESTS_SH+= t_msan_realloc +TESTS_SH+= t_msan_shadow +TESTS_SH+= t_msan_stack +TESTS_SH+= t_msan_unpoison + +TESTS_SH+= t_tsan_data_race +TESTS_SH+= t_tsan_heap_use_after_free +TESTS_SH+= t_tsan_lock_order_inversion +TESTS_SH+= t_tsan_locked_mutex_destroy +TESTS_SH+= t_tsan_signal_errno +TESTS_SH+= t_tsan_thread_leak + +.for test in ${ASAN_TESTS} +TESTS_SH_SRC_${test}= asan_common.subr ${test}.sh +.endfor +.for test in ${UBSAN_TESTS} +TESTS_SH_SRC_${test}= ubsan_common.subr ${test}.sh +.endfor .include diff --git a/usr.bin/cc/asan_common.subr b/usr.bin/cc/asan_common.subr new file mode 100644 --- /dev/null +++ b/usr.bin/cc/asan_common.subr @@ -0,0 +1,168 @@ +# $NetBSD: asan_common.subr,v 1.1 2019/01/29 19:56:37 mgorny Exp $ +# +# Copyright (c) 2018, 2019 The NetBSD Foundation, Inc. +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +SUPPORT='n' +test_target() { + if uname -m | grep -q "amd64"; then + SUPPORT='y' + fi + + if uname -m | grep -q "i386"; then + SUPPORT='y' + fi +} + +atf_test_case target_not_supported +target_not_supported_head() +{ + atf_set "descr" "Test forced skip" +} + +target_not_supported_body() +{ + atf_skip "Target is not supported" +} + +# Add a new test case, with head & body. +# asan_test_case +asan_test_case() { + atf_test_case "$1" + eval "$1_head() { + atf_set 'descr' 'compile and run \"$2\"' + atf_set 'require.progs' 'cc paxctl' + }" + + atf_test_case "$1_profile" + eval "$1_head() { + atf_set 'descr' 'compile and run \"$2\" with profiling option' + atf_set 'require.progs' 'cc paxctl' + }" + + atf_test_case "$1_pic" + eval "$1_head() { + atf_set 'descr' 'compile and run PIC \"$2\"' + atf_set 'require.progs' 'cc paxctl' + }" + + atf_test_case "$1_pie" + eval "$1_head() { + atf_set 'descr' 'compile and run position independent (PIE) \"$2\"' + atf_set 'require.progs' 'cc paxctl' + }" + + atf_test_case "${1}32" + eval "$1_head() { + atf_set 'descr' 'compile and run \"$2\" for/in netbsd32 emulation' + atf_set 'require.progs' 'cc paxctl file diff cat' + }" + + eval "$1_body() { + echo \"\$ASAN_CODE\" > test.c + cc -fsanitize=address -o test test.c + paxctl +a test + atf_check -s not-exit:0 -o not-match:'CHECK\n' -e match:'$3' ./test + } + + $1_profile_body() { + echo \"\$ASAN_CODE\" > test.c + cc -fsanitize=address -o test -pg test.c + paxctl +a test + atf_check -s not-exit:0 -o not-match:'CHECK\n' -e match:'$3' ./test + } + + $1_pic_body() { + echo \"\$ASAN_CODE\" > test.c + cc -DPIC_FOO -fsanitize=address -fPIC -shared -o libtest.so test.c + cc -DPIC_MAIN -o test test.c -fsanitize=address -L. -ltest + paxctl +a test + + export LD_LIBRARY_PATH=. + atf_check -s not-exit:0 -o not-match:'CHECK\n' -e match:'$3' ./test + } + + $1_pie_body() { + # check whether this arch supports -pice + if ! cc -pie -dM -E - < /dev/null 2>/dev/null >/dev/null; then + atf_set_skip 'cc -pie not supported on this architecture' + fi + echo \"\$ASAN_CODE\" > test.c + cc -fsanitize=address -o test -fpie -pie test.c + paxctl +a test + atf_check -s not-exit:0 -o not-match:'CHECK\n' -e match:'$3' ./test + } + + ${1}32_body() { + # check whether this arch is 64bit + if ! cc -dM -E - < /dev/null | fgrep -q _LP64; then + atf_skip 'this is not a 64 bit architecture' + fi + if ! cc -m32 -dM -E - < /dev/null 2>/dev/null > ./def32; then + atf_skip 'cc -m32 not supported on this architecture' + else + if fgrep -q _LP64 ./def32; then + atf_fail 'cc -m32 does not generate netbsd32 binaries' + fi + fi + + echo \"\$ASAN_CODE\" > test.c + cc -fsanitize=address -o df32 -m32 test.c + cc -fsanitize=address -o df64 test.c + file -b ./df32 > ./ftype32 + file -b ./df64 > ./ftype64 + if diff ./ftype32 ./ftype64 >/dev/null; then + atf_fail 'generated binaries do not differ' + fi + echo '32bit binaries on this platform are:' + cat ./ftype32 + echo 'While native (64bit) binaries are:' + cat ./ftype64 + paxctl +a df32 + atf_check -s not-exit:0 -o not-match:'CHECK\n' -e match:'$3' ./df32 + +# and another test with profile 32bit binaries + cc -fsanitize=address -o test -pg -m32 test.c + paxctl +a test + atf_check -s not-exit:0 -o not-match:'CHECK\n' -e match:'$3' ./test + }" +} + +asan_add_test_cases() { + test_target + test $SUPPORT = 'n' && { + atf_add_test_case target_not_supported + return 0 + } + + atf_add_test_case "$1" +# atf_add_test_case "$1_profile" + atf_add_test_case "$1_pic" + atf_add_test_case "$1_pie" +# atf_add_test_case "${1}32" + # static option not supported + # -static and -fsanitize=address can't be used together for compilation + # (gcc version 5.4.0 and clang 7.1) tested on April 2nd 2018. +} diff --git a/usr.bin/cc/t_asan_double_free.sh b/usr.bin/cc/t_asan_double_free.sh new file mode 100644 --- /dev/null +++ b/usr.bin/cc/t_asan_double_free.sh @@ -0,0 +1,49 @@ +# $NetBSD: t_asan_double_free.sh,v 1.3 2019/01/29 19:56:37 mgorny Exp $ +# +# Copyright (c) 2018, 2019 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Siddharth Muralee. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +ASAN_CODE=' +#include +#include +#include +void foo(int); +#ifndef PIC_MAIN +void foo(int index) { char *x = (char*)malloc(10 * sizeof(char)); memset(x, 0, 10); free(x); free(x - index); } +#endif +#ifndef PIC_FOO +int main(int argc, char **argv) { foo(argc - 1); printf("CHECK\n"); exit(0); } +#endif +' + +asan_test_case double_free "Double Free example" double-free + +atf_init_test_cases() +{ + asan_add_test_cases double_free +} diff --git a/usr.bin/cc/t_asan_global_buffer_overflow.sh b/usr.bin/cc/t_asan_global_buffer_overflow.sh new file mode 100644 --- /dev/null +++ b/usr.bin/cc/t_asan_global_buffer_overflow.sh @@ -0,0 +1,50 @@ +# $NetBSD: t_asan_global_buffer_overflow.sh,v 1.3 2019/01/29 19:56:37 mgorny Exp $ +# +# Copyright (c) 2018, 2019 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Siddharth Muralee. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +ASAN_CODE=' +#include +#include +int arr[5] = {-1}; +void foo(int); +#ifndef PIC_MAIN +void foo(int index) { arr[index] = 0; } +#endif +#ifndef PIC_FOO +int main(int argc, char **argv) {foo(argc + 5); printf("CHECK\n"); exit(0);} +#endif +' + +asan_test_case global_buffer_overflow "Global Buffer Overflow example" \ + global-buffer-overflow + +atf_init_test_cases() +{ + asan_add_test_cases global_buffer_overflow +} diff --git a/usr.bin/cc/t_asan_heap_overflow.sh b/usr.bin/cc/t_asan_heap_overflow.sh new file mode 100644 --- /dev/null +++ b/usr.bin/cc/t_asan_heap_overflow.sh @@ -0,0 +1,49 @@ +# $NetBSD: t_asan_heap_overflow.sh,v 1.3 2019/01/29 19:56:37 mgorny Exp $ +# +# Copyright (c) 2018, 2019 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Siddharth Muralee. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +ASAN_CODE=' +#include +#include +#include +int foo(int); +#ifndef PIC_MAIN +int foo(int index) { int *x = (int *)malloc(20); int res = x[index * 4]; free(x); return res;} +#endif +#ifndef PIC_FOO +int main(int argc, char **argv) {foo(argc + 19); printf("CHECK\n"); exit(0);} +#endif +' + +asan_test_case heap_overflow "Heap Overflow example" heap-buffer-overflow + +atf_init_test_cases() +{ + asan_add_test_cases heap_overflow +} diff --git a/usr.bin/cc/t_asan_off_by_one.sh b/usr.bin/cc/t_asan_off_by_one.sh new file mode 100644 --- /dev/null +++ b/usr.bin/cc/t_asan_off_by_one.sh @@ -0,0 +1,53 @@ +# $NetBSD: t_asan_off_by_one.sh,v 1.3 2019/01/29 19:56:37 mgorny Exp $ +# +# Copyright (c) 2018, 2019 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Siddharth Muralee. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +ASAN_CODE=' +#include +#include +void foo(); +#ifndef PIC_MAIN +void foo() { + int arr[5]; + for (int i = 0; i <= 5 ; i++) { + arr[i] = 0; + } +} +#endif +#ifndef PIC_FOO +int main() {foo(); printf("CHECK\n"); exit(0);} +#endif +' + +asan_test_case off_by_one "Off by one example" stack-buffer-overflow + +atf_init_test_cases() +{ + asan_add_test_cases off_by_one +} diff --git a/usr.bin/cc/t_asan_poison.sh b/usr.bin/cc/t_asan_poison.sh new file mode 100644 --- /dev/null +++ b/usr.bin/cc/t_asan_poison.sh @@ -0,0 +1,60 @@ +# $NetBSD: t_asan_poison.sh,v 1.3 2019/01/29 19:56:37 mgorny Exp $ +# +# Copyright (c) 2018, 2019 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Siddharth Muralee. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +ASAN_CODE=' +#include +#include +#include +int foo(); +#ifndef PIC_MAIN +int foo() { + int p = 2; + int *a; + ASAN_POISON_MEMORY_REGION(&p, sizeof(int)); + a=&p; + printf("%d", *a); +} +#endif + +#ifndef PIC_FOO +int main() { + foo(); + printf("CHECK\n"); + exit(0); +} +#endif +' + +asan_test_case poison 'Use after Poison example' use-after-poison + +atf_init_test_cases() +{ + asan_add_test_cases poison +} diff --git a/usr.bin/cc/t_asan_uaf.sh b/usr.bin/cc/t_asan_uaf.sh new file mode 100644 --- /dev/null +++ b/usr.bin/cc/t_asan_uaf.sh @@ -0,0 +1,48 @@ +# $NetBSD: t_asan_uaf.sh,v 1.3 2019/01/29 19:56:37 mgorny Exp $ +# +# Copyright (c) 2018, 2019 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Siddharth Muralee. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +ASAN_CODE=' +#include +#include +int foo(); +#ifndef PIC_MAIN +int foo() {int *x = (int *)malloc(10 * sizeof(int)); free(x); return x[0];} +#endif +#ifndef PIC_FOO +int main() {foo(); printf("CHECK\n"); exit(0);} +#endif +' + +asan_test_case uaf "Use After Free example" heap-use-after-free + +atf_init_test_cases() +{ + asan_add_test_cases uaf +} diff --git a/usr.bin/cc/t_fuzzer_oom.sh b/usr.bin/cc/t_fuzzer_oom.sh new file mode 100644 --- /dev/null +++ b/usr.bin/cc/t_fuzzer_oom.sh @@ -0,0 +1,171 @@ +# Copyright (c) 2018 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Yang Zheng. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +test_target() +{ + SUPPORT='n' + if uname -m | grep -q "amd64" && command -v cc >/dev/null 2>&1 && \ + ! echo __clang__ | cc -E - | grep -q __clang__; then + # only clang with major version newer than 7 is supported + CLANG_MAJOR=`echo __clang_major__ | cc -E - | grep -o '^[[:digit:]]'` + if [ "$CLANG_MAJOR" -ge "7" ]; then + SUPPORT='y' + fi + fi +} + +atf_test_case oom +oom_head() { + atf_set "descr" "Test thread sanitizer for out-of-memory condition" + atf_set "require.progs" "cc paxctl" +} + +atf_test_case oom_profile +oom_profile_head() { + atf_set "descr" "Test thread sanitizer for out-of-memory with profiling option" + atf_set "require.progs" "cc paxctl" +} +atf_test_case oom_pic +oom_pic_head() { + atf_set "descr" "Test thread sanitizer for out-of-memory with position independent code (PIC) flag" + atf_set "require.progs" "cc paxctl" +} +atf_test_case oom_pie +oom_pie_head() { + atf_set "descr" "Test thread sanitizer for out-of-memory with position independent execution (PIE) flag" + atf_set "require.progs" "cc paxctl" +} + +oom_body(){ + cat > test.c << EOF +#include +#include +#include + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + if (size > 0 && data[0] == 'b') while (1) malloc(16*1024*1024); + return 0; +} +EOF + + cc -fsanitize=fuzzer -o test test.c + paxctl +a test + atf_check -s ignore -o ignore -e match:"ERROR: libFuzzer: out-of-memory" ./test -rss_limit_mb=30 +} + +oom_profile_body(){ + cat > test.c << EOF +#include +#include +#include + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + if (size > 0 && data[0] == 'b') while (1) malloc(16*1024*1024); + return 0; +} +EOF + + cc -fsanitize=fuzzer -o test -pg test.c + paxctl +a test + atf_check -s ignore -o ignore -e match:"ERROR: libFuzzer: out-of-memory" ./test -rss_limit_mb=30 +} + +oom_pic_body(){ + cat > test.c << EOF +#include +#include +int help(const uint8_t *data, size_t size); +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + return help(data, size); +} +EOF + + cat > pic.c << EOF +#include +#include +#include + +int help(const uint8_t *data, size_t size) { + if (size > 0 && data[0] == 'b') while (1) malloc(16*1024*1024); + return 0; +} +EOF + + cc -fsanitize=fuzzer -fPIC -shared -o libtest.so pic.c + cc -o test test.c -fsanitize=fuzzer -L. -ltest + paxctl +a test + + export LD_LIBRARY_PATH=. + atf_check -s ignore -o ignore -e match:"ERROR: libFuzzer: out-of-memory" ./test -rss_limit_mb=30 +} +oom_pie_body(){ + + #check whether -pie flag is supported on this architecture + if ! cc -pie -dM -E - < /dev/null 2>/dev/null >/dev/null; then + atf_set_skip "cc -pie not supported on this architecture" + fi + cat > test.c << EOF +#include +#include +#include + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + if (size > 0 && data[0] == 'b') while (1) malloc(16*1024*1024); + return 0; +} +EOF + + cc -fsanitize=fuzzer -o test -fpie -pie test.c + paxctl +a test + atf_check -s ignore -o ignore -e match:"ERROR: libFuzzer: out-of-memory" ./test -rss_limit_mb=30 +} + + +atf_test_case target_not_supported +target_not_supported_head() +{ + atf_set "descr" "Test forced skip" +} + +target_not_supported_body() +{ + atf_skip "Target is not supported" +} + +atf_init_test_cases() +{ + test_target + test $SUPPORT = 'n' && { + atf_add_test_case target_not_supported + return 0 + } + atf_add_test_case oom + atf_add_test_case oom_profile + atf_add_test_case oom_pie + atf_add_test_case oom_pic +} diff --git a/usr.bin/cc/t_fuzzer_simple.sh b/usr.bin/cc/t_fuzzer_simple.sh new file mode 100644 --- /dev/null +++ b/usr.bin/cc/t_fuzzer_simple.sh @@ -0,0 +1,187 @@ +# Copyright (c) 2018 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Yang Zheng. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +test_target() +{ + SUPPORT='n' + if uname -m | grep -q "amd64" && command -v cc >/dev/null 2>&1 && \ + ! echo __clang__ | cc -E - | grep -q __clang__; then + # only clang with major version newer than 7 is supported + CLANG_MAJOR=`echo __clang_major__ | cc -E - | grep -o '^[[:digit:]]'` + if [ "$CLANG_MAJOR" -ge "7" ]; then + SUPPORT='y' + fi + fi +} + +atf_test_case simple +simple_head() { + atf_set "descr" "Test thread sanitizer for error exit condition" + atf_set "require.progs" "cc paxctl" +} + +atf_test_case simple_profile +simple_profile_head() { + atf_set "descr" "Test thread sanitizer for simple with profiling option" + atf_set "require.progs" "cc paxctl" +} +atf_test_case simple_pic +simple_pic_head() { + atf_set "descr" "Test thread sanitizer for simple with position independent code (PIC) flag" + atf_set "require.progs" "cc paxctl" +} +atf_test_case simple_pie +simple_pie_head() { + atf_set "descr" "Test thread sanitizer for simple with position independent execution (PIE) flag" + atf_set "require.progs" "cc paxctl" +} + +simple_body(){ + cat > test.c << EOF +#include +#include +#include + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + if (size > 0 && data[0] == 'b') { + fprintf(stderr, "BINGO\n"); + exit(1); + } + + return 0; +} +EOF + + cc -fsanitize=fuzzer -o test test.c + paxctl +a test + atf_check -s ignore -o ignore -e match:"BINGO" ./test +} + +simple_profile_body(){ + cat > test.c << EOF +#include +#include +#include + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + if (size > 0 && data[0] == 'b') { + fprintf(stderr, "BINGO\n"); + exit(1); + } + + return 0; +} +EOF + + cc -fsanitize=fuzzer -o test -pg test.c + paxctl +a test + atf_check -s ignore -o ignore -e match:"BINGO" ./test +} + +simple_pic_body(){ + cat > test.c << EOF +#include +#include +int help(const uint8_t *data, size_t size); +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + return help(data, size); +} +EOF + + cat > pic.c << EOF +#include +#include +#include + +int help(const uint8_t *data, size_t size) { + if (size > 0 && data[0] == 'b') { + fprintf(stderr, "BINGO\n"); + exit(1); + } + + return 0; +} +EOF + + cc -fsanitize=fuzzer -fPIC -shared -o libtest.so pic.c + cc -o test test.c -fsanitize=fuzzer -L. -ltest + paxctl +a test + + export LD_LIBRARY_PATH=. + atf_check -s ignore -o ignore -e match:"BINGO" ./test +} +simple_pie_body(){ + + #check whether -pie flag is supported on this architecture + if ! cc -pie -dM -E - < /dev/null 2>/dev/null >/dev/null; then + atf_set_skip "cc -pie not supported on this architecture" + fi + cat > test.c << EOF +#include +#include +#include + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + if (size > 0 && data[0] == 'b') { + fprintf(stderr, "BINGO\n"); + exit(1); + } + + return 0; +} +EOF + + cc -fsanitize=fuzzer -o test -fpie -pie test.c + paxctl +a test + atf_check -s ignore -o ignore -e match:"BINGO" ./test +} + + +atf_test_case target_not_supported +target_not_supported_head() +{ + atf_set "descr" "Test forced skip" +} + +target_not_supported_body() +{ + atf_skip "Target is not supported" +} + +atf_init_test_cases() +{ + test_target + test $SUPPORT = 'n' && { + atf_add_test_case target_not_supported + return 0 + } + atf_add_test_case simple + atf_add_test_case simple_profile + atf_add_test_case simple_pie + atf_add_test_case simple_pic +} diff --git a/usr.bin/cc/t_fuzzer_timeout.sh b/usr.bin/cc/t_fuzzer_timeout.sh new file mode 100644 --- /dev/null +++ b/usr.bin/cc/t_fuzzer_timeout.sh @@ -0,0 +1,167 @@ +# Copyright (c) 2018 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Yang Zheng. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +test_target() +{ + SUPPORT='n' + if uname -m | grep -q "amd64" && command -v cc >/dev/null 2>&1 && \ + ! echo __clang__ | cc -E - | grep -q __clang__; then + # only clang with major version newer than 7 is supported + CLANG_MAJOR=`echo __clang_major__ | cc -E - | grep -o '^[[:digit:]]'` + if [ "$CLANG_MAJOR" -ge "7" ]; then + SUPPORT='y' + fi + fi +} + +atf_test_case timeout +timeout_head() { + atf_set "descr" "Test thread sanitizer for timeout condition" + atf_set "require.progs" "cc paxctl" +} + +atf_test_case timeout_profile +timeout_profile_head() { + atf_set "descr" "Test thread sanitizer for timeout with profiling option" + atf_set "require.progs" "cc paxctl" +} +atf_test_case timeout_pic +timeout_pic_head() { + atf_set "descr" "Test thread sanitizer for timeout with position independent code (PIC) flag" + atf_set "require.progs" "cc paxctl" +} +atf_test_case timeout_pie +timeout_pie_head() { + atf_set "descr" "Test thread sanitizer for timeout with position independent execution (PIE) flag" + atf_set "require.progs" "cc paxctl" +} + +timeout_body(){ + cat > test.c << EOF +#include +#include + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + if (size > 0 && data[0] == 'b') while (1) ; + return 0; +} +EOF + + cc -fsanitize=fuzzer -o test test.c + paxctl +a test + atf_check -s ignore -o ignore -e match:"ERROR: libFuzzer: timeout" ./test -timeout=5 +} + +timeout_profile_body(){ + cat > test.c << EOF +#include +#include + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + if (size > 0 && data[0] == 'b') while (1) ; + return 0; +} +EOF + + cc -fsanitize=fuzzer -o test -pg test.c + paxctl +a test + atf_check -s ignore -o ignore -e match:"ERROR: libFuzzer: timeout" ./test -timeout=5 +} + +timeout_pic_body(){ + cat > test.c << EOF +#include +#include +int help(const uint8_t *data, size_t size); +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + return help(data, size); +} +EOF + + cat > pic.c << EOF +#include +#include + +int help(const uint8_t *data, size_t size) { + if (size > 0 && data[0] == 'b') while (1) ; + return 0; +} +EOF + + cc -fsanitize=fuzzer -fPIC -shared -o libtest.so pic.c + cc -o test test.c -fsanitize=fuzzer -L. -ltest + paxctl +a test + + export LD_LIBRARY_PATH=. + atf_check -s ignore -o ignore -e match:"ERROR: libFuzzer: timeout" ./test -timeout=5 +} +timeout_pie_body(){ + + #check whether -pie flag is supported on this architecture + if ! cc -pie -dM -E - < /dev/null 2>/dev/null >/dev/null; then + atf_set_skip "cc -pie not supported on this architecture" + fi + cat > test.c << EOF +#include +#include + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + if (size > 0 && data[0] == 'b') while (1) ; + return 0; +} +EOF + + cc -fsanitize=fuzzer -o test -fpie -pie test.c + paxctl +a test + atf_check -s ignore -o ignore -e match:"ERROR: libFuzzer: timeout" ./test -timeout=5 +} + + +atf_test_case target_not_supported +target_not_supported_head() +{ + atf_set "descr" "Test forced skip" +} + +target_not_supported_body() +{ + atf_skip "Target is not supported" +} + +atf_init_test_cases() +{ + test_target + test $SUPPORT = 'n' && { + atf_add_test_case target_not_supported + return 0 + } + atf_add_test_case timeout + atf_add_test_case timeout_profile + atf_add_test_case timeout_pie + atf_add_test_case timeout_pic +} diff --git a/usr.bin/cc/t_hello.sh b/usr.bin/cc/t_hello.sh old mode 100755 new mode 100644 --- a/usr.bin/cc/t_hello.sh +++ b/usr.bin/cc/t_hello.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_hello.sh,v 1.3 2016/04/03 14:41:30 gson Exp $ +# $NetBSD: t_hello.sh,v 1.10 2020/10/13 06:49:27 rin Exp $ # # Copyright (c) 2011 The NetBSD Foundation, Inc. # All rights reserved. @@ -31,6 +31,12 @@ atf_set "require.progs" "cc" } +atf_test_case hello_profile +hello_profile_head() { + atf_set "descr" "compile and run \"hello world\" with profiling option" + atf_set "require.progs" "cc" +} + atf_test_case hello_pic hello_pic_head() { atf_set "descr" "compile and run PIC \"hello world\"" @@ -59,18 +65,31 @@ atf_check -s exit:0 -o inline:"hello world\n" ./hello } +hello_profile_body() { + cat > test.c << EOF +#include +#include +int main(void) {printf("hello world\n");exit(0);} +EOF + atf_check -s exit:0 -o ignore -e ignore cc -o hello -pg test.c + atf_check -s exit:0 -o inline:"hello world\n" ./hello + atf_check -s exit:0 -o ignore -e ignore cc -o hello2 -fprofile-generate test.c + atf_check -s exit:0 -o inline:"hello world\n" ./hello2 +} + hello_pic_body() { cat > test.c << EOF #include +int callpic(void); int main(void) {callpic();exit(0);} EOF cat > pic.c << EOF #include -int callpic(void) {printf("hello world\n");} +int callpic(void) {printf("hello world\n");return 0;} EOF atf_check -s exit:0 -o ignore -e ignore \ - cc -fPIC -dPIC -shared -o libtest.so pic.c + cc -fPIC -shared -o libtest.so pic.c atf_check -s exit:0 -o ignore -e ignore \ cc -o hello test.c -L. -ltest @@ -132,12 +151,23 @@ atf_check -s exit:0 -o ignore -e ignore cc -o hello -m32 \ -static test.c atf_check -s exit:0 -o inline:"hello static world\n" ./hello + + # and another test with profile 32bit binaries + cat > test.c << EOF +#include +#include +int main(void) {printf("hello 32bit profile world\n");exit(0);} +EOF + atf_check -s exit:0 -o ignore -e ignore cc -o hello -m32 \ + -pg test.c + atf_check -s exit:0 -o inline:"hello 32bit profile world\n" ./hello } atf_init_test_cases() { atf_add_test_case hello + atf_add_test_case hello_profile atf_add_test_case hello_pic atf_add_test_case hello_pie atf_add_test_case hello32 diff --git a/usr.bin/cc/t_libgomp.sh b/usr.bin/cc/t_libgomp.sh new file mode 100644 --- /dev/null +++ b/usr.bin/cc/t_libgomp.sh @@ -0,0 +1,50 @@ +# $NetBSD: t_libgomp.sh,v 1.1 2019/02/09 00:12:14 mrg Exp $ +# +# Copyright (c) 2019 Matthew R. Green +# 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. +# 3. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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. + + +atf_test_case libgomp +libgomp_head() { + atf_set "descr" "compile and hello world with -fopenmp" + atf_set "require.progs" "cc" +} + +libgomp_body() { + cat > hello.c << EOF +#include +#include +int main(void) {printf("hello world\n");exit(0);} +EOF + atf_check -s exit:0 -o ignore -e ignore cc -fopenmp -o hellogomp hello.c + atf_check -s exit:0 -o inline:"hello world\n" ./hellogomp +} + +atf_init_test_cases() +{ + + atf_add_test_case libgomp +} diff --git a/usr.bin/cc/t_msan_allocated_memory.sh b/usr.bin/cc/t_msan_allocated_memory.sh new file mode 100644 --- /dev/null +++ b/usr.bin/cc/t_msan_allocated_memory.sh @@ -0,0 +1,169 @@ +# Copyright (c) 2018 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Yang Zheng. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +test_target() +{ + SUPPORT='n' + if uname -m | grep -q "amd64" && command -v cc >/dev/null 2>&1 && \ + ! echo __clang__ | cc -E - | grep -q __clang__; then + # only clang with major version newer than 7 is supported + CLANG_MAJOR=`echo __clang_major__ | cc -E - | grep -o '^[[:digit:]]'` + if [ "$CLANG_MAJOR" -ge "7" ]; then + SUPPORT='y' + fi + fi +} + +atf_test_case allocated_memory +allocated_memory_head() { + atf_set "descr" "Test memory sanitizer for __msan_allocated_memory interface" + atf_set "require.progs" "cc paxctl" +} + +atf_test_case allocated_memory_profile +allocated_memory_profile_head() { + atf_set "descr" "Test memory sanitizer for __msan_allocated_memory with profiling option" + atf_set "require.progs" "cc paxctl" +} +atf_test_case allocated_memory_pic +allocated_memory_pic_head() { + atf_set "descr" "Test memory sanitizer for __msan_allocated_memory with position independent code (PIC) flag" + atf_set "require.progs" "cc paxctl" +} +atf_test_case allocated_memory_pie +allocated_memory_pie_head() { + atf_set "descr" "Test memory sanitizer for __msan_allocated_memory with position independent execution (PIE) flag" + atf_set "require.progs" "cc paxctl" +} + +allocated_memory_body(){ + cat > test.c << EOF +#include +#include + +int main() { + int x = 0; + __msan_allocated_memory(&x, sizeof(x)); + return x; +} +EOF + + cc -fsanitize=memory -o test test.c + paxctl +a test + atf_check -s ignore -o ignore -e match:"WARNING: MemorySanitizer: use-of-uninitialized-value" ./test +} + +allocated_memory_profile_body(){ + cat > test.c << EOF +#include +#include + +int main() { + int x = 0; + __msan_allocated_memory(&x, sizeof(x)); + return x; +} +EOF + + cc -fsanitize=memory -o test -pg test.c + paxctl +a test + atf_check -s ignore -o ignore -e match:"WARNING: MemorySanitizer: use-of-uninitialized-value" ./test +} + +allocated_memory_pic_body(){ + cat > test.c << EOF +#include +#include +int help(int); +int main(int argc, char **argv) {return help(argc);} +EOF + + cat > pic.c << EOF +#include +#include + +int help(int argc) { + int x = 0; + __msan_allocated_memory(&x, sizeof(x)); + return x; +} +EOF + + cc -fsanitize=memory -fPIC -shared -o libtest.so pic.c + cc -o test test.c -fsanitize=memory -L. -ltest + paxctl +a test + + export LD_LIBRARY_PATH=. + atf_check -s ignore -o ignore -e match:"WARNING: MemorySanitizer: use-of-uninitialized-value" ./test +} +allocated_memory_pie_body(){ + + #check whether -pie flag is supported on this architecture + if ! cc -pie -dM -E - < /dev/null 2>/dev/null >/dev/null; then + atf_set_skip "cc -pie not supported on this architecture" + fi + cat > test.c << EOF +#include +#include + +int main() { + int x = 0; + __msan_allocated_memory(&x, sizeof(x)); + return x; +} +EOF + + cc -fsanitize=memory -o test -fpie -pie test.c + paxctl +a test + atf_check -s ignore -o ignore -e match:"WARNING: MemorySanitizer: use-of-uninitialized-value" ./test +} + + +atf_test_case target_not_supported +target_not_supported_head() +{ + atf_set "descr" "Test forced skip" +} + +target_not_supported_body() +{ + atf_skip "Target is not supported" +} + +atf_init_test_cases() +{ + test_target + test $SUPPORT = 'n' && { + atf_add_test_case target_not_supported + return 0 + } + atf_add_test_case allocated_memory + atf_add_test_case allocated_memory_profile + atf_add_test_case allocated_memory_pie + atf_add_test_case allocated_memory_pic +} diff --git a/usr.bin/cc/t_msan_check_mem.sh b/usr.bin/cc/t_msan_check_mem.sh new file mode 100644 --- /dev/null +++ b/usr.bin/cc/t_msan_check_mem.sh @@ -0,0 +1,173 @@ +# Copyright (c) 2018 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Yang Zheng. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +test_target() +{ + SUPPORT='n' + if uname -m | grep -q "amd64" && command -v cc >/dev/null 2>&1 && \ + ! echo __clang__ | cc -E - | grep -q __clang__; then + # only clang with major version newer than 7 is supported + CLANG_MAJOR=`echo __clang_major__ | cc -E - | grep -o '^[[:digit:]]'` + if [ "$CLANG_MAJOR" -ge "7" ]; then + SUPPORT='y' + fi + fi +} + +atf_test_case check_mem +check_mem_head() { + atf_set "descr" "Test memory sanitizer for __msan_check_mem_is_initialized interface" + atf_set "require.progs" "cc paxctl" +} + +atf_test_case check_mem_profile +check_mem_profile_head() { + atf_set "descr" "Test memory sanitizer for __msan_check_mem_is_initialized with profiling option" + atf_set "require.progs" "cc paxctl" +} +atf_test_case check_mem_pic +check_mem_pic_head() { + atf_set "descr" "Test memory sanitizer for __msan_check_mem_is_initialized with position independent code (PIC) flag" + atf_set "require.progs" "cc paxctl" +} +atf_test_case check_mem_pie +check_mem_pie_head() { + atf_set "descr" "Test memory sanitizer for __msan_check_mem_is_initialized with position independent execution (PIE) flag" + atf_set "require.progs" "cc paxctl" +} + +check_mem_body(){ + cat > test.c << EOF +#include +#include + +int main(int argc, char **argv) { + int *volatile p = (int *)malloc(sizeof(int)); + + __msan_check_mem_is_initialized(p, sizeof(*p)); + return 0; +} +EOF + + cc -fsanitize=memory -o test test.c + paxctl +a test + atf_check -s ignore -o ignore -e match:"Uninitialized bytes in __msan_check_mem_is_initialized at offset 0 inside" ./test +} + +check_mem_profile_body(){ + cat > test.c << EOF +#include +#include + +int main(int argc, char **argv) { + int *volatile p = (int *)malloc(sizeof(int)); + + __msan_check_mem_is_initialized(p, sizeof(*p)); + return 0; +} +EOF + + cc -fsanitize=memory -o test -pg test.c + paxctl +a test + atf_check -s ignore -o ignore -e match:"Uninitialized bytes in __msan_check_mem_is_initialized at offset 0 inside" ./test +} + +check_mem_pic_body(){ + cat > test.c << EOF +#include +#include +int help(int); +int main(int argc, char **argv) {return help(argc);} +EOF + + cat > pic.c << EOF +#include +#include + +int help(int argc) { + int *volatile p = (int *)malloc(sizeof(int)); + + __msan_check_mem_is_initialized(p, sizeof(*p)); + return 0; +} +EOF + + cc -fsanitize=memory -fPIC -shared -o libtest.so pic.c + cc -o test test.c -fsanitize=memory -L. -ltest + paxctl +a test + + export LD_LIBRARY_PATH=. + atf_check -s ignore -o ignore -e match:"Uninitialized bytes in __msan_check_mem_is_initialized at offset 0 inside" ./test +} +check_mem_pie_body(){ + + #check whether -pie flag is supported on this architecture + if ! cc -pie -dM -E - < /dev/null 2>/dev/null >/dev/null; then + atf_set_skip "cc -pie not supported on this architecture" + fi + cat > test.c << EOF +#include +#include + +int main(int argc, char **argv) { + int *volatile p = (int *)malloc(sizeof(int)); + + __msan_check_mem_is_initialized(p, sizeof(*p)); + return 0; +} +EOF + + cc -fsanitize=memory -o test -fpie -pie test.c + paxctl +a test + atf_check -s ignore -o ignore -e match:"Uninitialized bytes in __msan_check_mem_is_initialized at offset 0 inside" ./test +} + + +atf_test_case target_not_supported +target_not_supported_head() +{ + atf_set "descr" "Test forced skip" +} + +target_not_supported_body() +{ + atf_skip "Target is not supported" +} + +atf_init_test_cases() +{ + test_target + test $SUPPORT = 'n' && { + atf_add_test_case target_not_supported + return 0 + } + atf_add_test_case check_mem + atf_add_test_case check_mem_profile + atf_add_test_case check_mem_pie + atf_add_test_case check_mem_pic +} diff --git a/usr.bin/cc/t_msan_free.sh b/usr.bin/cc/t_msan_free.sh new file mode 100644 --- /dev/null +++ b/usr.bin/cc/t_msan_free.sh @@ -0,0 +1,164 @@ +# Copyright (c) 2018 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Yang Zheng. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +test_target() +{ + SUPPORT='n' + if uname -m | grep -q "amd64" && command -v cc >/dev/null 2>&1 && \ + ! echo __clang__ | cc -E - | grep -q __clang__; then + # only clang with major version newer than 7 is supported + CLANG_MAJOR=`echo __clang_major__ | cc -E - | grep -o '^[[:digit:]]'` + if [ "$CLANG_MAJOR" -ge "7" ]; then + SUPPORT='y' + fi + fi +} + +atf_test_case free +free_head() { + atf_set "descr" "Test memory sanitizer for use-after-free case" + atf_set "require.progs" "cc paxctl" +} + +atf_test_case free_profile +free_profile_head() { + atf_set "descr" "Test memory sanitizer for use-after-free with profiling option" + atf_set "require.progs" "cc paxctl" +} +atf_test_case free_pic +free_pic_head() { + atf_set "descr" "Test memory sanitizer for use-after-free with position independent code (PIC) flag" + atf_set "require.progs" "cc paxctl" +} +atf_test_case free_pie +free_pie_head() { + atf_set "descr" "Test memory sanitizer for use-after-free with position independent execution (PIE) flag" + atf_set "require.progs" "cc paxctl" +} + +free_body(){ + cat > test.c << EOF +#include +int main() { + int *a = (int *)malloc(sizeof(int)); + *a = 9; + free(a); + return *a; +} +EOF + + cc -fsanitize=memory -o test test.c + paxctl +a test + atf_check -s ignore -o ignore -e match:"WARNING: MemorySanitizer: use-of-uninitialized-value" ./test +} + +free_profile_body(){ + cat > test.c << EOF +#include +int main() { + int *a = (int *)malloc(sizeof(int)); + *a = 9; + free(a); + return *a; +} +EOF + + cc -fsanitize=memory -o test -pg test.c + paxctl +a test + atf_check -s ignore -o ignore -e match:"WARNING: MemorySanitizer: use-of-uninitialized-value" ./test +} + +free_pic_body(){ + cat > test.c << EOF +#include +#include +int help(int); +int main(int argc, char **argv) {return help(argc);} +EOF + + cat > pic.c << EOF +#include +int help(int argc) { + int *a = (int *)malloc(sizeof(int)); + *a = 9; + free(a); + return *a; +} +EOF + + cc -fsanitize=memory -fPIC -shared -o libtest.so pic.c + cc -o test test.c -fsanitize=memory -L. -ltest + paxctl +a test + + export LD_LIBRARY_PATH=. + atf_check -s ignore -o ignore -e match:"WARNING: MemorySanitizer: use-of-uninitialized-value" ./test +} +free_pie_body(){ + + #check whether -pie flag is supported on this architecture + if ! cc -pie -dM -E - < /dev/null 2>/dev/null >/dev/null; then + atf_set_skip "cc -pie not supported on this architecture" + fi + cat > test.c << EOF +#include +int main() { + int *a = (int *)malloc(sizeof(int)); + *a = 9; + free(a); + return *a; +} +EOF + + cc -fsanitize=memory -o test -fpie -pie test.c + paxctl +a test + atf_check -s ignore -o ignore -e match:"WARNING: MemorySanitizer: use-of-uninitialized-value" ./test +} + +atf_test_case target_not_supported +target_not_supported_head() +{ + atf_set "descr" "Test forced skip" +} + +target_not_supported_body() +{ + atf_skip "Target is not supported" +} + +atf_init_test_cases() +{ + test_target + test $SUPPORT = 'n' && { + atf_add_test_case target_not_supported + return 0 + } + atf_add_test_case free + atf_add_test_case free_profile + atf_add_test_case free_pie + atf_add_test_case free_pic +} diff --git a/usr.bin/cc/t_msan_heap.sh b/usr.bin/cc/t_msan_heap.sh new file mode 100644 --- /dev/null +++ b/usr.bin/cc/t_msan_heap.sh @@ -0,0 +1,144 @@ +# Copyright (c) 2018 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Yang Zheng. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +test_target() +{ + SUPPORT='n' + if uname -m | grep -q "amd64" && command -v cc >/dev/null 2>&1 && \ + ! echo __clang__ | cc -E - | grep -q __clang__; then + # only clang with major version newer than 7 is supported + CLANG_MAJOR=`echo __clang_major__ | cc -E - | grep -o '^[[:digit:]]'` + if [ "$CLANG_MAJOR" -ge "7" ]; then + SUPPORT='y' + fi + fi +} + +atf_test_case heap +heap_head() { + atf_set "descr" "Test memory sanitizer for uninitialized heap value case" + atf_set "require.progs" "cc paxctl" +} + +atf_test_case heap_profile +heap_profile_head() { + atf_set "descr" "Test memory sanitizer for uninitialized heap value with profiling option" + atf_set "require.progs" "cc paxctl" +} +atf_test_case heap_pic +heap_pic_head() { + atf_set "descr" "Test memory sanitizer for uninitialized heap value with position independent code (PIC) flag" + atf_set "require.progs" "cc paxctl" +} +atf_test_case heap_pie +heap_pie_head() { + atf_set "descr" "Test memory sanitizer for uninitialized heap value with position independent execution (PIE) flag" + atf_set "require.progs" "cc paxctl" +} + +heap_body(){ + cat > test.c << EOF +#include +int main() { int *a = (int *)malloc(sizeof(int)); return *a; } +EOF + + cc -fsanitize=memory -o test test.c + paxctl +a test + atf_check -s ignore -o ignore -e match:"WARNING: MemorySanitizer: use-of-uninitialized-value" ./test +} + +heap_profile_body(){ + cat > test.c << EOF +#include +int main() { int *a = (int *)malloc(sizeof(int)); return *a; } +EOF + + cc -fsanitize=memory -o test -pg test.c + paxctl +a test + atf_check -s ignore -o ignore -e match:"WARNING: MemorySanitizer: use-of-uninitialized-value" ./test +} + +heap_pic_body(){ + cat > test.c << EOF +#include +#include +int help(int); +int main(int argc, char **argv) {return help(argc);} +EOF + + cat > pic.c << EOF +#include +int help(int argc) { int *a = (int *)malloc(sizeof(int)); return *a; } +EOF + + cc -fsanitize=memory -fPIC -shared -o libtest.so pic.c + cc -o test test.c -fsanitize=memory -L. -ltest + paxctl +a test + + export LD_LIBRARY_PATH=. + atf_check -s ignore -o ignore -e match:"WARNING: MemorySanitizer: use-of-uninitialized-value" ./test +} +heap_pie_body(){ + + #check whether -pie flag is supported on this architecture + if ! cc -pie -dM -E - < /dev/null 2>/dev/null >/dev/null; then + atf_set_skip "cc -pie not supported on this architecture" + fi + cat > test.c << EOF +#include +int main() { int *a = (int *)malloc(sizeof(int)); return *a; } +EOF + + cc -fsanitize=memory -o test -fpie -pie test.c + paxctl +a test + atf_check -s ignore -o ignore -e match:"WARNING: MemorySanitizer: use-of-uninitialized-value" ./test +} + +atf_test_case target_not_supported +target_not_supported_head() +{ + atf_set "descr" "Test forced skip" +} + +target_not_supported_body() +{ + atf_skip "Target is not supported" +} + +atf_init_test_cases() +{ + test_target + test $SUPPORT = 'n' && { + atf_add_test_case target_not_supported + return 0 + } + atf_add_test_case heap + atf_add_test_case heap_profile + atf_add_test_case heap_pie + atf_add_test_case heap_pic +} diff --git a/usr.bin/cc/t_msan_partial_poison.sh b/usr.bin/cc/t_msan_partial_poison.sh new file mode 100644 --- /dev/null +++ b/usr.bin/cc/t_msan_partial_poison.sh @@ -0,0 +1,176 @@ +# Copyright (c) 2018 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Yang Zheng. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +test_target() +{ + SUPPORT='n' + if uname -m | grep -q "amd64" && command -v cc >/dev/null 2>&1 && \ + ! echo __clang__ | cc -E - | grep -q __clang__; then + # only clang with major version newer than 7 is supported + CLANG_MAJOR=`echo __clang_major__ | cc -E - | grep -o '^[[:digit:]]'` + if [ "$CLANG_MAJOR" -ge "7" ]; then + SUPPORT='y' + fi + fi +} + +atf_test_case partial_poison +partial_poison_head() { + atf_set "descr" "Test memory sanitizer for __msan_partial_poison interface" + atf_set "require.progs" "cc paxctl" +} + +atf_test_case partial_poison_profile +partial_poison_profile_head() { + atf_set "descr" "Test memory sanitizer for __msan_partial_poison with profiling option" + atf_set "require.progs" "cc paxctl" +} +atf_test_case partial_poison_pic +partial_poison_pic_head() { + atf_set "descr" "Test memory sanitizer for __msan_partial_poison with position independent code (PIC) flag" + atf_set "require.progs" "cc paxctl" +} +atf_test_case partial_poison_pie +partial_poison_pie_head() { + atf_set "descr" "Test memory sanitizer for __msan_partial_poison with position independent execution (PIE) flag" + atf_set "require.progs" "cc paxctl" +} + +partial_poison_body(){ + cat > test.c << EOF +#include +#include + +int main(void) { + char x[4]; + char x_s[4] = {0x77, 0x65, 0x43, 0x21}; + __msan_partial_poison(&x, &x_s, sizeof(x_s)); + __msan_print_shadow(&x, sizeof(x_s)); + return 0; +} +EOF + + cc -fsanitize=memory -o test test.c + paxctl +a test + atf_check -s ignore -o ignore -e match:": 77654321" ./test +} + +partial_poison_profile_body(){ + cat > test.c << EOF +#include +#include + +int main(void) { + char x[4]; + char x_s[4] = {0x77, 0x65, 0x43, 0x21}; + __msan_partial_poison(&x, &x_s, sizeof(x_s)); + __msan_print_shadow(&x, sizeof(x_s)); + return 0; +} +EOF + + cc -fsanitize=memory -o test -pg test.c + paxctl +a test + atf_check -s ignore -o ignore -e match:": 77654321" ./test +} + +partial_poison_pic_body(){ + cat > test.c << EOF +#include +#include +int help(int); +int main(int argc, char **argv) {return help(argc);} +EOF + + cat > pic.c << EOF +#include +#include + +int help(int argc) { + char x[4]; + char x_s[4] = {0x77, 0x65, 0x43, 0x21}; + __msan_partial_poison(&x, &x_s, sizeof(x_s)); + __msan_print_shadow(&x, sizeof(x_s)); + return 0; +} +EOF + + cc -fsanitize=memory -fPIC -shared -o libtest.so pic.c + cc -o test test.c -fsanitize=memory -L. -ltest + paxctl +a test + + export LD_LIBRARY_PATH=. + atf_check -s ignore -o ignore -e match:": 77654321" ./test +} +partial_poison_pie_body(){ + + #check whether -pie flag is supported on this architecture + if ! cc -pie -dM -E - < /dev/null 2>/dev/null >/dev/null; then + atf_set_skip "cc -pie not supported on this architecture" + fi + cat > test.c << EOF +#include +#include + +int main(void) { + char x[4]; + char x_s[4] = {0x77, 0x65, 0x43, 0x21}; + __msan_partial_poison(&x, &x_s, sizeof(x_s)); + __msan_print_shadow(&x, sizeof(x_s)); + return 0; +} +EOF + + cc -fsanitize=memory -o test -fpie -pie test.c + paxctl +a test + atf_check -s ignore -o ignore -e match:": 77654321" ./test +} + +atf_test_case target_not_supported +target_not_supported_head() +{ + atf_set "descr" "Test forced skip" +} + +target_not_supported_body() +{ + atf_skip "Target is not supported" +} + +atf_init_test_cases() +{ + test_target + test $SUPPORT = 'n' && { + atf_add_test_case target_not_supported + return 0 + } + atf_add_test_case partial_poison + atf_add_test_case partial_poison_profile + atf_add_test_case partial_poison_pie + atf_add_test_case partial_poison_pic +} diff --git a/usr.bin/cc/t_msan_poison.sh b/usr.bin/cc/t_msan_poison.sh new file mode 100644 --- /dev/null +++ b/usr.bin/cc/t_msan_poison.sh @@ -0,0 +1,172 @@ +# Copyright (c) 2018 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Yang Zheng. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +test_target() +{ + SUPPORT='n' + if uname -m | grep -q "amd64" && command -v cc >/dev/null 2>&1 && \ + ! echo __clang__ | cc -E - | grep -q __clang__; then + # only clang with major version newer than 7 is supported + CLANG_MAJOR=`echo __clang_major__ | cc -E - | grep -o '^[[:digit:]]'` + if [ "$CLANG_MAJOR" -ge "7" ]; then + SUPPORT='y' + fi + fi +} + +atf_test_case poison +poison_head() { + atf_set "descr" "Test memory sanitizer for __msan_poison interface" + atf_set "require.progs" "cc paxctl" +} + +atf_test_case poison_profile +poison_profile_head() { + atf_set "descr" "Test memory sanitizer for __msan_poison with profiling option" + atf_set "require.progs" "cc paxctl" +} +atf_test_case poison_pic +poison_pic_head() { + atf_set "descr" "Test memory sanitizer for __msan_poison with position independent code (PIC) flag" + atf_set "require.progs" "cc paxctl" +} +atf_test_case poison_pie +poison_pie_head() { + atf_set "descr" "Test memory sanitizer for __msan_poison with position independent execution (PIE) flag" + atf_set "require.progs" "cc paxctl" +} + +poison_body(){ + cat > test.c << EOF +#include + +int main(void) { + char p[32] = {}; + __msan_poison(p + 10, 2); + + __msan_check_mem_is_initialized(p + 5, 20); + return 0; +} +EOF + + cc -fsanitize=memory -o test test.c + paxctl +a test + atf_check -s ignore -o ignore -e match:"Uninitialized bytes in __msan_check_mem_is_initialized at offset 5 inside" ./test +} + +poison_profile_body(){ + cat > test.c << EOF +#include + +int main(void) { + char p[32] = {}; + __msan_poison(p + 10, 2); + + __msan_check_mem_is_initialized(p + 5, 20); + return 0; +} +EOF + + cc -fsanitize=memory -o test -pg test.c + paxctl +a test + atf_check -s ignore -o ignore -e match:"Uninitialized bytes in __msan_check_mem_is_initialized at offset 5 inside" ./test +} + +poison_pic_body(){ + cat > test.c << EOF +#include +#include +int help(int); +int main(int argc, char **argv) {return help(argc);} +EOF + + cat > pic.c << EOF +#include + +int help(int argc) { + char p[32] = {}; + __msan_poison(p + 10, 2); + + __msan_check_mem_is_initialized(p + 5, 20); + return 0; +} +EOF + + cc -fsanitize=memory -fPIC -shared -o libtest.so pic.c + cc -o test test.c -fsanitize=memory -L. -ltest + paxctl +a test + + export LD_LIBRARY_PATH=. + atf_check -s ignore -o ignore -e match:"Uninitialized bytes in __msan_check_mem_is_initialized at offset 5 inside" ./test +} +poison_pie_body(){ + + #check whether -pie flag is supported on this architecture + if ! cc -pie -dM -E - < /dev/null 2>/dev/null >/dev/null; then + atf_set_skip "cc -pie not supported on this architecture" + fi + cat > test.c << EOF +#include + +int main(void) { + char p[32] = {}; + __msan_poison(p + 10, 2); + + __msan_check_mem_is_initialized(p + 5, 20); + return 0; +} +EOF + + cc -fsanitize=memory -o test -fpie -pie test.c + paxctl +a test + atf_check -s ignore -o ignore -e match:"Uninitialized bytes in __msan_check_mem_is_initialized at offset 5 inside" ./test +} + +atf_test_case target_not_supported +target_not_supported_head() +{ + atf_set "descr" "Test forced skip" +} + +target_not_supported_body() +{ + atf_skip "Target is not supported" +} + +atf_init_test_cases() +{ + test_target + test $SUPPORT = 'n' && { + atf_add_test_case target_not_supported + return 0 + } + atf_add_test_case poison + atf_add_test_case poison_profile + atf_add_test_case poison_pie + atf_add_test_case poison_pic +} diff --git a/usr.bin/cc/t_msan_realloc.sh b/usr.bin/cc/t_msan_realloc.sh new file mode 100644 --- /dev/null +++ b/usr.bin/cc/t_msan_realloc.sh @@ -0,0 +1,168 @@ +# Copyright (c) 2018 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Yang Zheng. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +test_target() +{ + SUPPORT='n' + if uname -m | grep -q "amd64" && command -v cc >/dev/null 2>&1 && \ + ! echo __clang__ | cc -E - | grep -q __clang__; then + # only clang with major version newer than 7 is supported + CLANG_MAJOR=`echo __clang_major__ | cc -E - | grep -o '^[[:digit:]]'` + if [ "$CLANG_MAJOR" -ge "7" ]; then + SUPPORT='y' + fi + fi +} + +atf_test_case realloc +realloc_head() { + atf_set "descr" "Test memory sanitizer for realloc" + atf_set "require.progs" "cc paxctl" +} + +atf_test_case realloc_profile +realloc_profile_head() { + atf_set "descr" "Test memory sanitizer for realloc with profiling option" + atf_set "require.progs" "cc paxctl" +} +atf_test_case realloc_pic +realloc_pic_head() { + atf_set "descr" "Test memory sanitizer for realloc with position independent code (PIC) flag" + atf_set "require.progs" "cc paxctl" +} +atf_test_case realloc_pie +realloc_pie_head() { + atf_set "descr" "Test memory sanitizer for realloc with position independent execution (PIE) flag" + atf_set "require.progs" "cc paxctl" +} + +realloc_body(){ + cat > test.c << EOF +#include +int main(int argc, char **argv) { + char *p = (char *)malloc(100); + p = (char *)realloc(p, 10000); + char x = p[50]; + free(p); + return x; +} +EOF + + cc -fsanitize=memory -o test test.c + paxctl +a test + atf_check -s ignore -o ignore -e match:"WARNING: MemorySanitizer: use-of-uninitialized-value" ./test +} + +realloc_profile_body(){ + cat > test.c << EOF +#include +int main(int argc, char **argv) { + char *p = (char *)malloc(100); + p = (char *)realloc(p, 10000); + char x = p[50]; + free(p); + return x; +} +EOF + + cc -fsanitize=memory -o test -pg test.c + paxctl +a test + atf_check -s ignore -o ignore -e match:"WARNING: MemorySanitizer: use-of-uninitialized-value" ./test +} + +realloc_pic_body(){ + cat > test.c << EOF +#include +#include +int help(int); +int main(int argc, char **argv) {return help(argc);} +EOF + + cat > pic.c << EOF +#include +int help(int argc) { + char *p = (char *)malloc(100); + p = (char *)realloc(p, 10000); + char x = p[50]; + free(p); + return x; +} +EOF + + cc -fsanitize=memory -fPIC -shared -o libtest.so pic.c + cc -o test test.c -fsanitize=memory -L. -ltest + paxctl +a test + + export LD_LIBRARY_PATH=. + atf_check -s ignore -o ignore -e match:"WARNING: MemorySanitizer: use-of-uninitialized-value" ./test +} +realloc_pie_body(){ + + #check whether -pie flag is supported on this architecture + if ! cc -pie -dM -E - < /dev/null 2>/dev/null >/dev/null; then + atf_set_skip "cc -pie not supported on this architecture" + fi + cat > test.c << EOF +#include +int main(int argc, char **argv) { + char *p = (char *)malloc(100); + p = (char *)realloc(p, 10000); + char x = p[50]; + free(p); + return x; +} +EOF + + cc -fsanitize=memory -o test -fpie -pie test.c + paxctl +a test + atf_check -s ignore -o ignore -e match:"WARNING: MemorySanitizer: use-of-uninitialized-value" ./test +} + +atf_test_case target_not_supported +target_not_supported_head() +{ + atf_set "descr" "Test forced skip" +} + +target_not_supported_body() +{ + atf_skip "Target is not supported" +} + +atf_init_test_cases() +{ + test_target + test $SUPPORT = 'n' && { + atf_add_test_case target_not_supported + return 0 + } + atf_add_test_case realloc + atf_add_test_case realloc_profile + atf_add_test_case realloc_pie + atf_add_test_case realloc_pic +} diff --git a/usr.bin/cc/t_msan_shadow.sh b/usr.bin/cc/t_msan_shadow.sh new file mode 100644 --- /dev/null +++ b/usr.bin/cc/t_msan_shadow.sh @@ -0,0 +1,188 @@ +# Copyright (c) 2018 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Yang Zheng. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +test_target() +{ + SUPPORT='n' + if uname -m | grep -q "amd64" && command -v cc >/dev/null 2>&1 && \ + ! echo __clang__ | cc -E - | grep -q __clang__; then + # only clang with major version newer than 7 is supported + CLANG_MAJOR=`echo __clang_major__ | cc -E - | grep -o '^[[:digit:]]'` + if [ "$CLANG_MAJOR" -ge "7" ]; then + SUPPORT='y' + fi + fi +} + +atf_test_case shadow +shadow_head() { + atf_set "descr" "Test memory sanitizer for shadow interface" + atf_set "require.progs" "cc paxctl" +} + +atf_test_case shadow_profile +shadow_profile_head() { + atf_set "descr" "Test memory sanitizer for shadow with profiling option" + atf_set "require.progs" "cc paxctl" +} +atf_test_case shadow_pic +shadow_pic_head() { + atf_set "descr" "Test memory sanitizer for shadow with position independent code (PIC) flag" + atf_set "require.progs" "cc paxctl" +} +atf_test_case shadow_pie +shadow_pie_head() { + atf_set "descr" "Test memory sanitizer for shadow with position independent execution (PIE) flag" + atf_set "require.progs" "cc paxctl" +} + +shadow_body(){ + cat > test.c << EOF +#include +#include +#include + +int main(int argc, char **argv) { + char str[] = "abc"; + char str2[] = "cdefghi"; + __msan_poison(str + 2, 1); + __msan_copy_shadow(str2 + 2, str, 4); + printf("%ld\n", __msan_test_shadow(str, 4)); + __msan_print_shadow(str2, 8); + return 0; +} +EOF + + cc -fsanitize=memory -o test test.c + paxctl +a test + atf_check -s ignore -o match:"2" -e match:"00000000 ff000000" ./test +} + +shadow_profile_body(){ + cat > test.c << EOF +#include +#include +#include + +int main(int argc, char **argv) { + char str[] = "abc"; + char str2[] = "cdefghi"; + __msan_poison(str + 2, 1); + __msan_copy_shadow(str2 + 2, str, 4); + printf("%ld\n", __msan_test_shadow(str, 4)); + __msan_print_shadow(str2, 8); + return 0; +} +EOF + + cc -fsanitize=memory -o test -pg test.c + paxctl +a test + atf_check -s ignore -o match:"2" -e match:"00000000 ff000000" ./test +} + +shadow_pic_body(){ + cat > test.c << EOF +#include +#include +int help(int); +int main(int argc, char **argv) {return help(argc);} +EOF + + cat > pic.c << EOF +#include +#include +#include + +int help(int argc) { + char str[] = "abc"; + char str2[] = "cdefghi"; + __msan_poison(str + 2, 1); + __msan_copy_shadow(str2 + 2, str, 4); + printf("%ld\n", __msan_test_shadow(str, 4)); + __msan_print_shadow(str2, 8); + return 0; +} +EOF + + cc -fsanitize=memory -fPIC -shared -o libtest.so pic.c + cc -o test test.c -fsanitize=memory -L. -ltest + paxctl +a test + + export LD_LIBRARY_PATH=. + atf_check -s ignore -o match:"2" -e match:"00000000 ff000000" ./test +} +shadow_pie_body(){ + + #check whether -pie flag is supported on this architecture + if ! cc -pie -dM -E - < /dev/null 2>/dev/null >/dev/null; then + atf_set_skip "cc -pie not supported on this architecture" + fi + cat > test.c << EOF +#include +#include +#include + +int main(int argc, char **argv) { + char str[] = "abc"; + char str2[] = "cdefghi"; + __msan_poison(str + 2, 1); + __msan_copy_shadow(str2 + 2, str, 4); + printf("%ld\n", __msan_test_shadow(str, 4)); + __msan_print_shadow(str2, 8); + return 0; +} +EOF + + cc -fsanitize=memory -o test -fpie -pie test.c + paxctl +a test + atf_check -s ignore -o match:"2" -e match:"00000000 ff000000" ./test +} + +atf_test_case target_not_supported +target_not_supported_head() +{ + atf_set "descr" "Test forced skip" +} + +target_not_supported_body() +{ + atf_skip "Target is not supported" +} + +atf_init_test_cases() +{ + test_target + test $SUPPORT = 'n' && { + atf_add_test_case target_not_supported + return 0 + } + atf_add_test_case shadow + atf_add_test_case shadow_profile + atf_add_test_case shadow_pie + atf_add_test_case shadow_pic +} diff --git a/usr.bin/cc/t_msan_stack.sh b/usr.bin/cc/t_msan_stack.sh new file mode 100644 --- /dev/null +++ b/usr.bin/cc/t_msan_stack.sh @@ -0,0 +1,164 @@ +# Copyright (c) 2018 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Yang Zheng. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +test_target() +{ + SUPPORT='n' + if uname -m | grep -q "amd64" && command -v cc >/dev/null 2>&1 && \ + ! echo __clang__ | cc -E - | grep -q __clang__; then + # only clang with major version newer than 7 is supported + CLANG_MAJOR=`echo __clang_major__ | cc -E - | grep -o '^[[:digit:]]'` + if [ "$CLANG_MAJOR" -ge "7" ]; then + SUPPORT='y' + fi + fi +} + +atf_test_case stack +stack_head() { + atf_set "descr" "Test memory sanitizer for free-stack-pointer case" + atf_set "require.progs" "cc paxctl" +} + +atf_test_case stack_profile +stack_profile_head() { + atf_set "descr" "Test memory sanitizer for free-stack-pointer with profiling option" + atf_set "require.progs" "cc paxctl" +} +atf_test_case stack_pic +stack_pic_head() { + atf_set "descr" "Test memory sanitizer for free-stack-pointer with position independent code (PIC) flag" + atf_set "require.progs" "cc paxctl" +} +atf_test_case stack_pie +stack_pie_head() { + atf_set "descr" "Test memory sanitizer for free-stack-pointer with position independent execution (PIE) flag" + atf_set "require.progs" "cc paxctl" +} + +stack_body(){ + cat > test.c << EOF +#include +int main() { + int a = 0; + int *p = &a; + free(p); + return 0; +} +EOF + + cc -fsanitize=memory -o test test.c + paxctl +a test + atf_check -s ignore -o ignore -e match:"MemorySanitizer: bad pointer" ./test +} + +stack_profile_body(){ + cat > test.c << EOF +#include +int main() { + int a = 0; + int *p = &a; + free(p); + return 0; +} +EOF + + cc -fsanitize=memory -o test -pg test.c + paxctl +a test + atf_check -s ignore -o ignore -e match:"MemorySanitizer: bad pointer" ./test +} + +stack_pic_body(){ + cat > test.c << EOF +#include +#include +int help(int); +int main(int argc, char **argv) {return help(argc);} +EOF + + cat > pic.c << EOF +#include +int help(int argc) { + int a = 0; + int *p = &a; + free(p); + return 0; +} +EOF + + cc -fsanitize=memory -fPIC -shared -o libtest.so pic.c + cc -o test test.c -fsanitize=memory -L. -ltest + paxctl +a test + + export LD_LIBRARY_PATH=. + atf_check -s ignore -o ignore -e match:"MemorySanitizer: bad pointer" ./test +} +stack_pie_body(){ + + #check whether -pie flag is supported on this architecture + if ! cc -pie -dM -E - < /dev/null 2>/dev/null >/dev/null; then + atf_set_skip "cc -pie not supported on this architecture" + fi + cat > test.c << EOF +#include +int main() { + int a = 0; + int *p = &a; + free(p); + return 0; +} +EOF + + cc -fsanitize=memory -o test -fpie -pie test.c + paxctl +a test + atf_check -s ignore -o ignore -e match:"MemorySanitizer: bad pointer" ./test +} + +atf_test_case target_not_supported +target_not_supported_head() +{ + atf_set "descr" "Test forced skip" +} + +target_not_supported_body() +{ + atf_skip "Target is not supported" +} + +atf_init_test_cases() +{ + test_target + test $SUPPORT = 'n' && { + atf_add_test_case target_not_supported + return 0 + } + atf_add_test_case stack + atf_add_test_case stack_profile + atf_add_test_case stack_pie + atf_add_test_case stack_pic +} diff --git a/usr.bin/cc/t_msan_unpoison.sh b/usr.bin/cc/t_msan_unpoison.sh new file mode 100644 --- /dev/null +++ b/usr.bin/cc/t_msan_unpoison.sh @@ -0,0 +1,188 @@ +# Copyright (c) 2018 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Yang Zheng. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +test_target() +{ + SUPPORT='n' + if uname -m | grep -q "amd64" && command -v cc >/dev/null 2>&1 && \ + ! echo __clang__ | cc -E - | grep -q __clang__; then + # only clang with major version newer than 7 is supported + CLANG_MAJOR=`echo __clang_major__ | cc -E - | grep -o '^[[:digit:]]'` + if [ "$CLANG_MAJOR" -ge "7" ]; then + SUPPORT='y' + fi + fi +} + +atf_test_case unpoison +unpoison_head() { + atf_set "descr" "Test memory sanitizer for __msan_unpoison interface" + atf_set "require.progs" "cc paxctl" +} + +atf_test_case unpoison_profile +unpoison_profile_head() { + atf_set "descr" "Test memory sanitizer for __msan_unpoison with profiling option" + atf_set "require.progs" "cc paxctl" +} +atf_test_case unpoison_pic +unpoison_pic_head() { + atf_set "descr" "Test memory sanitizer for __msan_unpoison with position independent code (PIC) flag" + atf_set "require.progs" "cc paxctl" +} +atf_test_case unpoison_pie +unpoison_pie_head() { + atf_set "descr" "Test memory sanitizer for __msan_unpoison with position independent execution (PIE) flag" + atf_set "require.progs" "cc paxctl" +} + +unpoison_body(){ + cat > test.c << EOF +#include + +int main(void) { + char p[32] = {}; + char q[32] = {}; + __msan_poison(p + 10, 2); + __msan_poison(q, 32); + __msan_unpoison(p + 10, 2); + __msan_unpoison_string(q); + __msan_check_mem_is_initialized(p, 32); + __msan_check_mem_is_initialized(p, 32); + return 0; +} +EOF + + cc -fsanitize=memory -o test test.c + paxctl +a test + atf_check -s ignore -o ignore -e not-match:"WARNING: MemorySanitizer: use-of-uninitialized-value" ./test +} + +unpoison_profile_body(){ + cat > test.c << EOF +#include + +int main(void) { + char p[32] = {}; + char q[32] = {}; + __msan_poison(p + 10, 2); + __msan_poison(q, 32); + __msan_unpoison(p + 10, 2); + __msan_unpoison_string(q); + __msan_check_mem_is_initialized(p, 32); + __msan_check_mem_is_initialized(p, 32); + return 0; +} +EOF + + cc -fsanitize=memory -o test -pg test.c + paxctl +a test + atf_check -s ignore -o ignore -e not-match:"WARNING: MemorySanitizer: use-of-uninitialized-value" ./test +} + +unpoison_pic_body(){ + cat > test.c << EOF +#include +#include +int help(int); +int main(int argc, char **argv) {return help(argc);} +EOF + + cat > pic.c << EOF +#include + +int help(int argc) { + char p[32] = {}; + char q[32] = {}; + __msan_poison(p + 10, 2); + __msan_poison(q, 32); + __msan_unpoison(p + 10, 2); + __msan_unpoison_string(q); + __msan_check_mem_is_initialized(p, 32); + __msan_check_mem_is_initialized(p, 32); + return 0; +} +EOF + + cc -fsanitize=memory -fPIC -shared -o libtest.so pic.c + cc -o test test.c -fsanitize=memory -L. -ltest + paxctl +a test + + export LD_LIBRARY_PATH=. + atf_check -s ignore -o ignore -e not-match:"WARNING: MemorySanitizer: use-of-uninitialized-value" ./test +} +unpoison_pie_body(){ + + #check whether -pie flag is supported on this architecture + if ! cc -pie -dM -E - < /dev/null 2>/dev/null >/dev/null; then + atf_set_skip "cc -pie not supported on this architecture" + fi + cat > test.c << EOF +#include + +int main(void) { + char p[32] = {}; + char q[32] = {}; + __msan_poison(p + 10, 2); + __msan_poison(q, 32); + __msan_unpoison(p + 10, 2); + __msan_unpoison_string(q); + __msan_check_mem_is_initialized(p, 32); + __msan_check_mem_is_initialized(p, 32); + return 0; +} +EOF + + cc -fsanitize=memory -o test -fpie -pie test.c + paxctl +a test + atf_check -s ignore -o ignore -e not-match:"WARNING: MemorySanitizer: use-of-uninitialized-value" ./test +} + +atf_test_case target_not_supported +target_not_supported_head() +{ + atf_set "descr" "Test forced skip" +} + +target_not_supported_body() +{ + atf_skip "Target is not supported" +} + +atf_init_test_cases() +{ + test_target + test $SUPPORT = 'n' && { + atf_add_test_case target_not_supported + return 0 + } + atf_add_test_case unpoison + atf_add_test_case unpoison_profile + atf_add_test_case unpoison_pie + atf_add_test_case unpoison_pic +} diff --git a/usr.bin/cc/t_tsan_data_race.sh b/usr.bin/cc/t_tsan_data_race.sh new file mode 100644 --- /dev/null +++ b/usr.bin/cc/t_tsan_data_race.sh @@ -0,0 +1,165 @@ +# Copyright (c) 2018 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Yang Zheng. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +tsan_available_archs() +{ + atf_set "require.arch" "x86_64" +} + +atf_test_case data_race +data_race_head() { + atf_set "descr" "Test thread sanitizer for data race condition" + atf_set "require.progs" "cc paxctl" + tsan_available_archs +} + +atf_test_case data_race_profile +data_race_profile_head() { + atf_set "descr" "Test thread sanitizer for data race with profiling option" + atf_set "require.progs" "cc paxctl" + tsan_available_archs +} +atf_test_case data_race_pic +data_race_pic_head() { + atf_set "descr" "Test thread sanitizer for data race with position independent code (PIC) flag" + atf_set "require.progs" "cc paxctl" + tsan_available_archs +} +atf_test_case data_race_pie +data_race_pie_head() { + atf_set "descr" "Test thread sanitizer for data race with position independent execution (PIE) flag" + atf_set "require.progs" "cc paxctl" + tsan_available_archs +} + +data_race_body(){ + cat > test.c << EOF +#include +int GlobalData; pthread_barrier_t barrier; +void *Thread(void *a) { pthread_barrier_wait(&barrier); GlobalData = 42; return 0; } +int main() { + pthread_t t; + pthread_barrier_init(&barrier, NULL, 2); + pthread_create(&t, NULL, Thread, NULL); + pthread_barrier_wait(&barrier); + GlobalData = 43; + pthread_join(t, NULL); + return 0; +} +EOF + + cc -fsanitize=thread -o test test.c + paxctl +a test + atf_check -s ignore -o ignore -e match:"WARNING: ThreadSanitizer: data race " ./test +} + +data_race_profile_body(){ + atf_expect_fail "PR toolchain/55760" + cat > test.c << EOF +#include +int GlobalData; pthread_barrier_t barrier; +void *Thread(void *a) { pthread_barrier_wait(&barrier); GlobalData = 42; return 0; } +int main() { + pthread_t t; + pthread_barrier_init(&barrier, NULL, 2); + pthread_create(&t, NULL, Thread, NULL); + pthread_barrier_wait(&barrier); + GlobalData = 43; + pthread_join(t, NULL); + return 0; +} +EOF + + cc -fsanitize=thread -o test -pg test.c + paxctl +a test + atf_check -s ignore -o ignore -e match:"WARNING: ThreadSanitizer: data race " ./test +} + +data_race_pic_body(){ + cat > test.c << EOF +#include +#include +int help(int); +int main(int argc, char **argv) {return help(argc);} +EOF + + cat > pic.c << EOF +#include +int GlobalData; pthread_barrier_t barrier; +void *Thread(void *a) { pthread_barrier_wait(&barrier); GlobalData = 42; return 0; } +int help(int argc) { + pthread_t t; + pthread_barrier_init(&barrier, NULL, 2); + pthread_create(&t, NULL, Thread, NULL); + pthread_barrier_wait(&barrier); + GlobalData = 43; + pthread_join(t, NULL); + return 0; +} +EOF + + cc -fsanitize=thread -fPIC -shared -o libtest.so pic.c + cc -o test test.c -fsanitize=thread -L. -ltest + paxctl +a test + + export LD_LIBRARY_PATH=. + atf_check -s ignore -o ignore -e match:"WARNING: ThreadSanitizer: data race " ./test +} +data_race_pie_body(){ + + #check whether -pie flag is supported on this architecture + if ! cc -pie -dM -E - < /dev/null 2>/dev/null >/dev/null; then + atf_set_skip "cc -pie not supported on this architecture" + fi + cat > test.c << EOF +#include +int GlobalData; pthread_barrier_t barrier; +void *Thread(void *a) { pthread_barrier_wait(&barrier); GlobalData = 42; return 0; } +int main() { + pthread_t t; + pthread_barrier_init(&barrier, NULL, 2); + pthread_create(&t, NULL, Thread, NULL); + pthread_barrier_wait(&barrier); + GlobalData = 43; + pthread_join(t, NULL); + return 0; +} +EOF + + cc -fsanitize=thread -o test -fpie -pie test.c + paxctl +a test + atf_check -s ignore -o ignore -e match:"WARNING: ThreadSanitizer: data race " ./test +} + +atf_init_test_cases() +{ + atf_add_test_case data_race + atf_add_test_case data_race_profile + atf_add_test_case data_race_pie + atf_add_test_case data_race_pic +} diff --git a/usr.bin/cc/t_tsan_heap_use_after_free.sh b/usr.bin/cc/t_tsan_heap_use_after_free.sh new file mode 100644 --- /dev/null +++ b/usr.bin/cc/t_tsan_heap_use_after_free.sh @@ -0,0 +1,201 @@ +# Copyright (c) 2018 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Yang Zheng. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +tsan_available_archs() +{ + atf_set "require.arch" "x86_64" +} + +atf_test_case heap_use_after_free +heap_use_after_free_head() { + atf_set "descr" "Test thread sanitizer for use-after-free condition" + atf_set "require.progs" "cc paxctl" + tsan_available_archs +} + +atf_test_case heap_use_after_free_profile +heap_use_after_free_profile_head() { + atf_set "descr" "Test thread sanitizer for use-after-free with profiling option" + atf_set "require.progs" "cc paxctl" + tsan_available_archs +} +atf_test_case heap_use_after_free_pic +heap_use_after_free_pic_head() { + atf_set "descr" "Test thread sanitizer for use-after-free with position independent code (PIC) flag" + atf_set "require.progs" "cc paxctl" + tsan_available_archs +} +atf_test_case heap_use_after_free_pie +heap_use_after_free_pie_head() { + atf_set "descr" "Test thread sanitizer for use-after-free with position independent execution (PIE) flag" + atf_set "require.progs" "cc paxctl" + tsan_available_archs +} + +heap_use_after_free_body(){ + cat > test.c << EOF +#include +#include + +int *ptr; +pthread_barrier_t barrier; +void *Thread(void *a) { + pthread_barrier_wait(&barrier); + *ptr = 42; + return 0; +} + +int main() { + pthread_t t; + pthread_barrier_init(&barrier, NULL, 2); + ptr = (int *)malloc(sizeof(int)); + pthread_create(&t, NULL, Thread, NULL); + free(ptr); + pthread_barrier_wait(&barrier); + pthread_join(t, NULL); + return 0; +} +EOF + + cc -fsanitize=thread -o test test.c + paxctl +a test + atf_check -s ignore -o ignore -e match:"WARNING: ThreadSanitizer: heap-use-after-free" ./test +} + +heap_use_after_free_profile_body(){ + atf_expect_fail "PR toolchain/55760" + cat > test.c << EOF +#include +#include + +int *ptr; +pthread_barrier_t barrier; +void *Thread(void *a) { + pthread_barrier_wait(&barrier); + *ptr = 42; + return 0; +} + +int main() { + pthread_t t; + pthread_barrier_init(&barrier, NULL, 2); + ptr = (int *)malloc(sizeof(int)); + pthread_create(&t, NULL, Thread, NULL); + free(ptr); + pthread_barrier_wait(&barrier); + pthread_join(t, NULL); + return 0; +} +EOF + + cc -fsanitize=thread -o test -pg test.c + paxctl +a test + atf_check -s ignore -o ignore -e match:"WARNING: ThreadSanitizer: heap-use-after-free" ./test +} + +heap_use_after_free_pic_body(){ + cat > test.c << EOF +#include +#include +int help(int); +int main(int argc, char **argv) {return help(argc);} +EOF + + cat > pic.c << EOF +#include +#include + +int *ptr; +pthread_barrier_t barrier; +void *Thread(void *a) { + pthread_barrier_wait(&barrier); + *ptr = 42; + return 0; +} + +int help(int argc) { + pthread_t t; + pthread_barrier_init(&barrier, NULL, 2); + ptr = (int *)malloc(sizeof(int)); + pthread_create(&t, NULL, Thread, NULL); + free(ptr); + pthread_barrier_wait(&barrier); + pthread_join(t, NULL); + return 0; +} +EOF + + cc -fsanitize=thread -fPIC -shared -o libtest.so pic.c + cc -o test test.c -fsanitize=thread -L. -ltest + paxctl +a test + + export LD_LIBRARY_PATH=. + atf_check -s ignore -o ignore -e match:"WARNING: ThreadSanitizer: heap-use-after-free" ./test +} +heap_use_after_free_pie_body(){ + + #check whether -pie flag is supported on this architecture + if ! cc -pie -dM -E - < /dev/null 2>/dev/null >/dev/null; then + atf_set_skip "cc -pie not supported on this architecture" + fi + cat > test.c << EOF +#include +#include + +int *ptr; +pthread_barrier_t barrier; +void *Thread(void *a) { + pthread_barrier_wait(&barrier); + *ptr = 42; + return 0; +} + +int main() { + pthread_t t; + pthread_barrier_init(&barrier, NULL, 2); + ptr = (int *)malloc(sizeof(int)); + pthread_create(&t, NULL, Thread, NULL); + free(ptr); + pthread_barrier_wait(&barrier); + pthread_join(t, NULL); + return 0; +} +EOF + + cc -fsanitize=thread -o test -fpie -pie test.c + paxctl +a test + atf_check -s ignore -o ignore -e match:"WARNING: ThreadSanitizer: heap-use-after-free" ./test +} + +atf_init_test_cases() +{ + atf_add_test_case heap_use_after_free + atf_add_test_case heap_use_after_free_profile + atf_add_test_case heap_use_after_free_pie + atf_add_test_case heap_use_after_free_pic +} diff --git a/usr.bin/cc/t_tsan_lock_order_inversion.sh b/usr.bin/cc/t_tsan_lock_order_inversion.sh new file mode 100644 --- /dev/null +++ b/usr.bin/cc/t_tsan_lock_order_inversion.sh @@ -0,0 +1,185 @@ +# Copyright (c) 2018 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Yang Zheng. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +tsan_available_archs() +{ + atf_set "require.arch" "x86_64" +} + +atf_test_case lock_order_inversion +lock_order_inversion_head() { + atf_set "descr" "Test thread sanitizer for lock order inversion condition" + atf_set "require.progs" "cc paxctl" + tsan_available_archs +} + +atf_test_case lock_order_inversion_profile +lock_order_inversion_profile_head() { + atf_set "descr" "Test thread sanitizer for lock order inversion with profiling option" + atf_set "require.progs" "cc paxctl" + tsan_available_archs +} +atf_test_case lock_order_inversion_pic +lock_order_inversion_pic_head() { + atf_set "descr" "Test thread sanitizer for lock order inversion with position independent code (PIC) flag" + atf_set "require.progs" "cc paxctl" + tsan_available_archs +} +atf_test_case lock_order_inversion_pie +lock_order_inversion_pie_head() { + atf_set "descr" "Test thread sanitizer for lock order inversion with position independent execution (PIE) flag" + atf_set "require.progs" "cc paxctl" + tsan_available_archs +} + +lock_order_inversion_body(){ + cat > test.c << EOF +#include + +pthread_mutex_t l1, l2; +int main() { + pthread_mutex_init(&l1, NULL); + pthread_mutex_init(&l2, NULL); + pthread_mutex_lock(&l2); + pthread_mutex_lock(&l1); + pthread_mutex_unlock(&l1); + pthread_mutex_unlock(&l2); + + pthread_mutex_lock(&l1); + pthread_mutex_lock(&l2); + pthread_mutex_unlock(&l2); + pthread_mutex_unlock(&l1); + return 0; +} +EOF + + cc -fsanitize=thread -o test test.c + paxctl +a test + atf_check -s ignore -o ignore -e match:"WARNING: ThreadSanitizer: lock-order-inversion" ./test +} + +lock_order_inversion_profile_body(){ + atf_expect_fail "PR toolchain/55760" + cat > test.c << EOF +#include + +pthread_mutex_t l1, l2; +int main() { + pthread_mutex_init(&l1, NULL); + pthread_mutex_init(&l2, NULL); + pthread_mutex_lock(&l2); + pthread_mutex_lock(&l1); + pthread_mutex_unlock(&l1); + pthread_mutex_unlock(&l2); + + pthread_mutex_lock(&l1); + pthread_mutex_lock(&l2); + pthread_mutex_unlock(&l2); + pthread_mutex_unlock(&l1); + return 0; +} +EOF + + cc -fsanitize=thread -o test -pg test.c + paxctl +a test + atf_check -s ignore -o ignore -e match:"WARNING: ThreadSanitizer: lock-order-inversion" ./test +} + +lock_order_inversion_pic_body(){ + cat > test.c << EOF +#include +#include +int help(int); +int main(int argc, char **argv) {return help(argc);} +EOF + + cat > pic.c << EOF +#include + +pthread_mutex_t l1, l2; +int help(int argc) { + pthread_mutex_init(&l1, NULL); + pthread_mutex_init(&l2, NULL); + pthread_mutex_lock(&l2); + pthread_mutex_lock(&l1); + pthread_mutex_unlock(&l1); + pthread_mutex_unlock(&l2); + + pthread_mutex_lock(&l1); + pthread_mutex_lock(&l2); + pthread_mutex_unlock(&l2); + pthread_mutex_unlock(&l1); + return 0; +} +EOF + + cc -fsanitize=thread -fPIC -shared -o libtest.so pic.c + cc -o test test.c -fsanitize=thread -L. -ltest + paxctl +a test + + export LD_LIBRARY_PATH=. + atf_check -s ignore -o ignore -e match:"WARNING: ThreadSanitizer: lock-order-inversion" ./test +} +lock_order_inversion_pie_body(){ + + #check whether -pie flag is supported on this architecture + if ! cc -pie -dM -E - < /dev/null 2>/dev/null >/dev/null; then + atf_set_skip "cc -pie not supported on this architecture" + fi + cat > test.c << EOF +#include + +pthread_mutex_t l1, l2; +int main() { + pthread_mutex_init(&l1, NULL); + pthread_mutex_init(&l2, NULL); + pthread_mutex_lock(&l2); + pthread_mutex_lock(&l1); + pthread_mutex_unlock(&l1); + pthread_mutex_unlock(&l2); + + pthread_mutex_lock(&l1); + pthread_mutex_lock(&l2); + pthread_mutex_unlock(&l2); + pthread_mutex_unlock(&l1); + return 0; +} +EOF + + cc -fsanitize=thread -o test -fpie -pie test.c + paxctl +a test + atf_check -s ignore -o ignore -e match:"WARNING: ThreadSanitizer: lock-order-inversion" ./test +} + +atf_init_test_cases() +{ + atf_add_test_case lock_order_inversion + atf_add_test_case lock_order_inversion_profile + atf_add_test_case lock_order_inversion_pie + atf_add_test_case lock_order_inversion_pic +} diff --git a/usr.bin/cc/t_tsan_locked_mutex_destroy.sh b/usr.bin/cc/t_tsan_locked_mutex_destroy.sh new file mode 100644 --- /dev/null +++ b/usr.bin/cc/t_tsan_locked_mutex_destroy.sh @@ -0,0 +1,201 @@ +# Copyright (c) 2018 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Yang Zheng. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +tsan_available_archs() +{ + atf_set "require.arch" "x86_64" +} + +atf_test_case locked_mutex_destroy +locked_mutex_destroy_head() { + atf_set "descr" "Test thread sanitizer for destroying locked mutex condition" + atf_set "require.progs" "cc paxctl" + tsan_available_archs +} + +atf_test_case locked_mutex_destroy_profile +locked_mutex_destroy_profile_head() { + atf_set "descr" "Test thread sanitizer for destroying locked mutex with profiling option" + atf_set "require.progs" "cc paxctl" + tsan_available_archs +} +atf_test_case locked_mutex_destroy_pic +locked_mutex_destroy_pic_head() { + atf_set "descr" "Test thread sanitizer for destroying locked mutex with position independent code (PIC) flag" + atf_set "require.progs" "cc paxctl" + tsan_available_archs +} +atf_test_case locked_mutex_destroy_pie +locked_mutex_destroy_pie_head() { + atf_set "descr" "Test thread sanitizer for destroying locked mutex with position independent execution (PIE) flag" + atf_set "require.progs" "cc paxctl" + tsan_available_archs +} + +locked_mutex_destroy_body(){ + cat > test.c << EOF +#include +#include + +pthread_mutex_t mutex; +pthread_barrier_t barrier; +void *Thread(void *a) { + pthread_mutex_lock(&mutex); + pthread_barrier_wait(&barrier); + return 0; +} + +int main() { + pthread_t t; + pthread_barrier_init(&barrier, NULL, 2); + pthread_mutex_init(&mutex, NULL); + pthread_create(&t, NULL, Thread, NULL); + pthread_barrier_wait(&barrier); + pthread_mutex_destroy(&mutex); + pthread_join(t, NULL); + return 0; +} +EOF + + cc -fsanitize=thread -o test test.c + paxctl +a test + atf_check -s ignore -o ignore -e match:"WARNING: ThreadSanitizer: destroy of a locked mutex" ./test +} + +locked_mutex_destroy_profile_body(){ + atf_expect_fail "PR toolchain/55760" + cat > test.c << EOF +#include +#include + +pthread_mutex_t mutex; +pthread_barrier_t barrier; +void *Thread(void *a) { + pthread_mutex_lock(&mutex); + pthread_barrier_wait(&barrier); + return 0; +} + +int main() { + pthread_t t; + pthread_barrier_init(&barrier, NULL, 2); + pthread_mutex_init(&mutex, NULL); + pthread_create(&t, NULL, Thread, NULL); + pthread_barrier_wait(&barrier); + pthread_mutex_destroy(&mutex); + pthread_join(t, NULL); + return 0; +} +EOF + + cc -fsanitize=thread -o test -pg test.c + paxctl +a test + atf_check -s ignore -o ignore -e match:"WARNING: ThreadSanitizer: destroy of a locked mutex" ./test +} + +locked_mutex_destroy_pic_body(){ + cat > test.c << EOF +#include +#include +int help(int); +int main(int argc, char **argv) {return help(argc);} +EOF + + cat > pic.c << EOF +#include +#include + +pthread_mutex_t mutex; +pthread_barrier_t barrier; +void *Thread(void *a) { + pthread_mutex_lock(&mutex); + pthread_barrier_wait(&barrier); + return 0; +} + +int help(int argc) { + pthread_t t; + pthread_barrier_init(&barrier, NULL, 2); + pthread_mutex_init(&mutex, NULL); + pthread_create(&t, NULL, Thread, NULL); + pthread_barrier_wait(&barrier); + pthread_mutex_destroy(&mutex); + pthread_join(t, NULL); + return 0; +} +EOF + + cc -fsanitize=thread -fPIC -shared -o libtest.so pic.c + cc -o test test.c -fsanitize=thread -L. -ltest + paxctl +a test + + export LD_LIBRARY_PATH=. + atf_check -s ignore -o ignore -e match:"WARNING: ThreadSanitizer: destroy of a locked mutex" ./test +} +locked_mutex_destroy_pie_body(){ + + #check whether -pie flag is supported on this architecture + if ! cc -pie -dM -E - < /dev/null 2>/dev/null >/dev/null; then + atf_set_skip "cc -pie not supported on this architecture" + fi + cat > test.c << EOF +#include +#include + +pthread_mutex_t mutex; +pthread_barrier_t barrier; +void *Thread(void *a) { + pthread_mutex_lock(&mutex); + pthread_barrier_wait(&barrier); + return 0; +} + +int main() { + pthread_t t; + pthread_barrier_init(&barrier, NULL, 2); + pthread_mutex_init(&mutex, NULL); + pthread_create(&t, NULL, Thread, NULL); + pthread_barrier_wait(&barrier); + pthread_mutex_destroy(&mutex); + pthread_join(t, NULL); + return 0; +} +EOF + + cc -fsanitize=thread -o test -fpie -pie test.c + paxctl +a test + atf_check -s ignore -o ignore -e match:"WARNING: ThreadSanitizer: destroy of a locked mutex" ./test +} + +atf_init_test_cases() +{ + atf_add_test_case locked_mutex_destroy + atf_add_test_case locked_mutex_destroy_profile + atf_add_test_case locked_mutex_destroy_pie + atf_add_test_case locked_mutex_destroy_pic +} diff --git a/usr.bin/cc/t_tsan_signal_errno.sh b/usr.bin/cc/t_tsan_signal_errno.sh new file mode 100644 --- /dev/null +++ b/usr.bin/cc/t_tsan_signal_errno.sh @@ -0,0 +1,189 @@ +# Copyright (c) 2018 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Yang Zheng. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +tsan_available_archs() +{ + atf_set "require.arch" "x86_64" +} + +atf_test_case signal_errno +signal_errno_head() { + atf_set "descr" "Test thread sanitizer for errno modification in signal condition" + atf_set "require.progs" "cc paxctl" + tsan_available_archs +} + +atf_test_case signal_errno_profile +signal_errno_profile_head() { + atf_set "descr" "Test thread sanitizer for errno modification in signal with profiling option" + atf_set "require.progs" "cc paxctl" + tsan_available_archs +} +atf_test_case signal_errno_pic +signal_errno_pic_head() { + atf_set "descr" "Test thread sanitizer for errno modification in signal with position independent code (PIC) flag" + atf_set "require.progs" "cc paxctl" + tsan_available_archs +} +atf_test_case signal_errno_pie +signal_errno_pie_head() { + atf_set "descr" "Test thread sanitizer for errno modification in signal with position independent execution (PIE) flag" + atf_set "require.progs" "cc paxctl" + tsan_available_archs +} + +signal_errno_body(){ + cat > test.c << EOF +#include +#include +#include +#include + +pthread_t mainth; +static void MyHandler(int a, siginfo_t *s, void *c) { errno = 1; } +static void* sendsignal(void *p) { pthread_kill(mainth, SIGPROF); return NULL; } +int main() { + mainth = pthread_self(); + struct sigaction act = {}; + act.sa_sigaction = &MyHandler; + sigaction(SIGPROF, &act, 0); + pthread_t th; + pthread_create(&th, 0, sendsignal, 0); + pthread_join(th, 0); + return 0; +} +EOF + + cc -fsanitize=thread -o test test.c + paxctl +a test + atf_check -s ignore -o ignore -e match:"WARNING: ThreadSanitizer: signal handler spoils errno" ./test +} + +signal_errno_profile_body(){ + atf_expect_fail "PR toolchain/55760" + cat > test.c << EOF +#include +#include +#include +#include + +pthread_t mainth; +static void MyHandler(int a, siginfo_t *s, void *c) { errno = 1; } +static void* sendsignal(void *p) { pthread_kill(mainth, SIGPROF); return NULL; } +int main() { + mainth = pthread_self(); + struct sigaction act = {}; + act.sa_sigaction = &MyHandler; + sigaction(SIGPROF, &act, 0); + pthread_t th; + pthread_create(&th, 0, sendsignal, 0); + pthread_join(th, 0); + return 0; +} +EOF + + cc -fsanitize=thread -o test -pg test.c + paxctl +a test + atf_check -s ignore -o ignore -e match:"WARNING: ThreadSanitizer: signal handler spoils errno" ./test +} + +signal_errno_pic_body(){ + cat > test.c << EOF +#include +#include +int help(int); +int main(int argc, char **argv) {return help(argc);} +EOF + + cat > pic.c << EOF +#include +#include +#include +#include + +pthread_t mainth; +static void MyHandler(int a, siginfo_t *s, void *c) { errno = 1; } +static void* sendsignal(void *p) { pthread_kill(mainth, SIGPROF); return NULL; } +int help(int argc) { + mainth = pthread_self(); + struct sigaction act = {}; + act.sa_sigaction = &MyHandler; + sigaction(SIGPROF, &act, 0); + pthread_t th; + pthread_create(&th, 0, sendsignal, 0); + pthread_join(th, 0); + return 0; +} +EOF + + cc -fsanitize=thread -fPIC -shared -o libtest.so pic.c + cc -o test test.c -fsanitize=thread -L. -ltest + paxctl +a test + + export LD_LIBRARY_PATH=. + atf_check -s ignore -o ignore -e match:"WARNING: ThreadSanitizer: signal handler spoils errno" ./test +} +signal_errno_pie_body(){ + + #check whether -pie flag is supported on this architecture + if ! cc -pie -dM -E - < /dev/null 2>/dev/null >/dev/null; then + atf_set_skip "cc -pie not supported on this architecture" + fi + cat > test.c << EOF +#include +#include +#include +#include + +pthread_t mainth; +static void MyHandler(int a, siginfo_t *s, void *c) { errno = 1; } +static void* sendsignal(void *p) { pthread_kill(mainth, SIGPROF); return NULL; } +int main() { + mainth = pthread_self(); + struct sigaction act = {}; + act.sa_sigaction = &MyHandler; + sigaction(SIGPROF, &act, 0); + pthread_t th; + pthread_create(&th, 0, sendsignal, 0); + pthread_join(th, 0); + return 0; +} +EOF + + cc -fsanitize=thread -o test -fpie -pie test.c + paxctl +a test + atf_check -s ignore -o ignore -e match:"WARNING: ThreadSanitizer: signal handler spoils errno" ./test +} + +atf_init_test_cases() +{ + atf_add_test_case signal_errno + atf_add_test_case signal_errno_profile + atf_add_test_case signal_errno_pie + atf_add_test_case signal_errno_pic +} diff --git a/usr.bin/cc/t_tsan_thread_leak.sh b/usr.bin/cc/t_tsan_thread_leak.sh new file mode 100644 --- /dev/null +++ b/usr.bin/cc/t_tsan_thread_leak.sh @@ -0,0 +1,189 @@ +# Copyright (c) 2018 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Yang Zheng. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +tsan_available_archs() +{ + atf_set "require.arch" "x86_64" +} + +atf_test_case thread_leak +thread_leak_head() { + atf_set "descr" "Test thread sanitizer for thread leak condition" + atf_set "require.progs" "cc paxctl" + tsan_available_archs +} + +atf_test_case thread_leak_profile +thread_leak_profile_head() { + atf_set "descr" "Test thread sanitizer for thread leak with profiling option" + atf_set "require.progs" "cc paxctl" + tsan_available_archs +} +atf_test_case thread_leak_pic +thread_leak_pic_head() { + atf_set "descr" "Test thread sanitizer for thread leak with position independent code (PIC) flag" + atf_set "require.progs" "cc paxctl" + tsan_available_archs +} +atf_test_case thread_leak_pie +thread_leak_pie_head() { + atf_set "descr" "Test thread sanitizer for thread leak with position independent execution (PIE) flag" + atf_set "require.progs" "cc paxctl" + tsan_available_archs +} + +thread_leak_body(){ + cat > test.c << EOF +#include +#include + +int GlobalData; +pthread_barrier_t barrier; +void *Thread(void *a) { + pthread_barrier_wait(&barrier); + return 0; +} + +int main() { + pthread_t t; + pthread_barrier_init(&barrier, NULL, 2); + pthread_create(&t, NULL, Thread, NULL); + pthread_barrier_wait(&barrier); + sleep(1); + return 0; +} +EOF + + cc -fsanitize=thread -o test test.c + paxctl +a test + atf_check -s ignore -o ignore -e match:"WARNING: ThreadSanitizer: thread leak" ./test +} + +thread_leak_profile_body(){ + atf_expect_fail "PR toolchain/55760" + cat > test.c << EOF +#include +#include + +int GlobalData; +pthread_barrier_t barrier; +void *Thread(void *a) { + pthread_barrier_wait(&barrier); + return 0; +} + +int main() { + pthread_t t; + pthread_barrier_init(&barrier, NULL, 2); + pthread_create(&t, NULL, Thread, NULL); + pthread_barrier_wait(&barrier); + sleep(1); + return 0; +} +EOF + + cc -fsanitize=thread -o test -pg test.c + paxctl +a test + atf_check -s ignore -o ignore -e match:"WARNING: ThreadSanitizer: thread leak" ./test +} + +thread_leak_pic_body(){ + cat > test.c << EOF +#include +#include +int help(int); +int main(int argc, char **argv) {return help(argc);} +EOF + + cat > pic.c << EOF +#include +#include + +int GlobalData; +pthread_barrier_t barrier; +void *Thread(void *a) { + pthread_barrier_wait(&barrier); + return 0; +} + +int help(int argc) { + pthread_t t; + pthread_barrier_init(&barrier, NULL, 2); + pthread_create(&t, NULL, Thread, NULL); + pthread_barrier_wait(&barrier); + sleep(1); + return 0; +} +EOF + + cc -fsanitize=thread -fPIC -shared -o libtest.so pic.c + cc -o test test.c -fsanitize=thread -L. -ltest + paxctl +a test + + export LD_LIBRARY_PATH=. + atf_check -s ignore -o ignore -e match:"WARNING: ThreadSanitizer: thread leak" ./test +} +thread_leak_pie_body(){ + + #check whether -pie flag is supported on this architecture + if ! cc -pie -dM -E - < /dev/null 2>/dev/null >/dev/null; then + atf_set_skip "cc -pie not supported on this architecture" + fi + cat > test.c << EOF +#include +#include + +int GlobalData; +pthread_barrier_t barrier; +void *Thread(void *a) { + pthread_barrier_wait(&barrier); + return 0; +} + +int main() { + pthread_t t; + pthread_barrier_init(&barrier, NULL, 2); + pthread_create(&t, NULL, Thread, NULL); + pthread_barrier_wait(&barrier); + sleep(1); + return 0; +} +EOF + + cc -fsanitize=thread -o test -fpie -pie test.c + paxctl +a test + atf_check -s ignore -o ignore -e match:"WARNING: ThreadSanitizer: thread leak" ./test +} + +atf_init_test_cases() +{ + atf_add_test_case thread_leak + atf_add_test_case thread_leak_profile + atf_add_test_case thread_leak_pie + atf_add_test_case thread_leak_pic +} diff --git a/usr.bin/cc/t_ubsan_int_add_overflow.sh b/usr.bin/cc/t_ubsan_int_add_overflow.sh new file mode 100644 --- /dev/null +++ b/usr.bin/cc/t_ubsan_int_add_overflow.sh @@ -0,0 +1,50 @@ +# $NetBSD: t_ubsan_int_add_overflow.sh,v 1.5 2019/02/09 00:13:19 mrg Exp $ +# +# Copyright (c) 2018, 2019 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Harry Pantazis. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +UBSAN_CODE=' +#include +#include +#include +int help(int); +#ifndef PIC_MAIN +int help(int count) {volatile int l = INT_MAX; l+= count; return l;} +#endif +#ifndef PIC_FOO +int main(int argc, char **argv) {volatile int l = INT_MAX; l+=argc; return l;} +#endif +' + +ubsan_test_case int_add_overflow "int addition overflows" \ + "signed integer overflow" + +atf_init_test_cases() +{ + ubsan_add_test_cases int_add_overflow +} diff --git a/usr.bin/cc/t_ubsan_int_divzero.sh b/usr.bin/cc/t_ubsan_int_divzero.sh new file mode 100644 --- /dev/null +++ b/usr.bin/cc/t_ubsan_int_divzero.sh @@ -0,0 +1,48 @@ +# $NetBSD: t_ubsan_int_divzero.sh,v 1.5 2019/02/09 00:13:19 mrg Exp $ +# +# Copyright (c) 2018, 2019 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Harry Pantazis. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +UBSAN_CODE=' +#include +#include +int help(int); +#ifndef PIC_MAIN +int help(int count) {volatile int l = count; volatile int k = 0; return l/k;} +#endif +#ifndef PIC_FOO +int main(int argc, char **argv) {return help(argc);} +#endif +' + +ubsan_test_case int_divzero "int division with zero" "division by zero" + +atf_init_test_cases() +{ + ubsan_add_test_cases int_divzero +} diff --git a/usr.bin/cc/t_ubsan_int_neg_overflow.sh b/usr.bin/cc/t_ubsan_int_neg_overflow.sh new file mode 100644 --- /dev/null +++ b/usr.bin/cc/t_ubsan_int_neg_overflow.sh @@ -0,0 +1,49 @@ +# $NetBSD: t_ubsan_int_neg_overflow.sh,v 1.5 2019/02/09 00:13:19 mrg Exp $ +# +# Copyright (c) 2018, 2019 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Harry Pantazis. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +UBSAN_CODE=' +#include +#include +#include +int help(int); +#ifndef PIC_MAIN +int help(int count) {volatile int l = INT_MIN; l = -l; return l;} +#endif +#ifndef PIC_FOO +int main(int argc, char **argv) {return help(argc);} +#endif +' + +ubsan_test_case int_neg_overflow "int negation overflows" "negation of" + +atf_init_test_cases() +{ + ubsan_add_test_cases int_neg_overflow +} diff --git a/usr.bin/cc/t_ubsan_int_sub_overflow.sh b/usr.bin/cc/t_ubsan_int_sub_overflow.sh new file mode 100644 --- /dev/null +++ b/usr.bin/cc/t_ubsan_int_sub_overflow.sh @@ -0,0 +1,50 @@ +# $NetBSD: t_ubsan_int_sub_overflow.sh,v 1.5 2019/02/09 00:13:19 mrg Exp $ +# +# Copyright (c) 2018, 2019 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Harry Pantazis. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +UBSAN_CODE=' +#include +#include +#include +int help(int); +#ifndef PIC_MAIN +int help(int count) {volatile int l = INT_MIN; l-= count; return l;} +#endif +#ifndef PIC_FOO +int main(int argc, char **argv) {volatile int k = help(argc); return k;} +#endif +' + +ubsan_test_case int_sub_overflow "int subtraction overflows" \ + "signed integer overflow" + +atf_init_test_cases() +{ + ubsan_add_test_cases int_sub_overflow +} diff --git a/usr.bin/cc/t_ubsan_vla_out_of_bounds.sh b/usr.bin/cc/t_ubsan_vla_out_of_bounds.sh new file mode 100644 --- /dev/null +++ b/usr.bin/cc/t_ubsan_vla_out_of_bounds.sh @@ -0,0 +1,49 @@ +# $NetBSD: t_ubsan_vla_out_of_bounds.sh,v 1.6 2019/02/09 00:13:19 mrg Exp $ +# +# Copyright (c) 2018, 2019 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Harry Pantazis. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +UBSAN_CODE=' +#include +#include +void help(int); +#ifndef PIC_MAIN +void help(int count) {volatile int val1 = count, val2 = count+1; volatile int arr[val1]; arr[val2] = count; } +#endif +#ifndef PIC_FOO +int main(int argc, char **argv) {help(argc); exit(0);} +#endif +' + +ubsan_test_case vla_out_of_bounds \ + "vla (Variable Length Array) out of bounds" "out of bounds" + +atf_init_test_cases() +{ + ubsan_add_test_cases vla_out_of_bounds +} diff --git a/usr.bin/cc/ubsan_common.subr b/usr.bin/cc/ubsan_common.subr new file mode 100644 --- /dev/null +++ b/usr.bin/cc/ubsan_common.subr @@ -0,0 +1,162 @@ +# $NetBSD: ubsan_common.subr,v 1.1 2019/01/29 19:59:10 mgorny Exp $ +# +# Copyright (c) 2018, 2019 The NetBSD Foundation, Inc. +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +test_target() +{ + SUPPORT='n' + if ! echo __GNUC__ | cc -E - | grep -q __GNUC__; then + SUPPORT='y' + fi + + if ! echo __clang__ | cc -E - | grep -q __clang__; then + SUPPORT='y' + fi +} + +atf_test_case target_not_supported +target_not_supported_head() +{ + atf_set "descr" "Test forced skip" +} + +target_not_supported_body() +{ + atf_skip "Target is not supported" +} + +# Add a new test case, with head & body. +# asan_test_case +ubsan_test_case() { + atf_test_case "$1" + eval "$1_head() { + atf_set 'descr' 'Test Undefined Behavior for $2' + atf_set 'require.progs' 'cc' + }" + + atf_test_case "$1_profile" + eval "$1_head() { + atf_set 'descr' 'Test Undefined Behavior for $2 with profiling option' + atf_set 'require.progs' 'cc' + }" + + atf_test_case "$1_pic" + eval "$1_head() { + atf_set 'descr' 'Test Undefined Behavior for $2 with position independent code (PIC) flag' + atf_set 'require.progs' 'cc' + }" + + atf_test_case "$1_pie" + eval "$1_head() { + atf_set 'descr' 'Test Undefined Behavior for $2 with position independent execution (PIE) flag' + atf_set 'require.progs' 'cc' + }" + + atf_test_case "${1}32" + eval "$1_head() { + atf_set 'descr' 'Test Undefined Behavior for $2 in NetBSD_32 emulation' + atf_set 'require.progs' 'cc file diff cat' + }" + + eval "$1_body() { + echo \"\$UBSAN_CODE\" > test.c + cc -fsanitize=undefined -o test test.c + # note: ignoring exit status due to inconsistency between gcc/clang + # (and between individual tests) + atf_check -s ignore -e match:'$3' ./test + } + + $1_profile_body() { + echo \"\$UBSAN_CODE\" > test.c + cc -fsanitize=undefined -o test -pg test.c + atf_check -s ignore -e match:'$3' ./test + } + + $1_pic_body() { + echo \"\$UBSAN_CODE\" > test.c + cc -DPIC_FOO -fsanitize=undefined -fPIC -shared -o libtest.so test.c + cc -DPIC_MAIN -o test test.c -fsanitize=undefined -L. -ltest + + export LD_LIBRARY_PATH=. + atf_check -s ignore -e match:'$3' ./test + } + + $1_pie_body() { + # check whether this arch supports -pice + if ! cc -pie -dM -E - < /dev/null 2>/dev/null >/dev/null; then + atf_set_skip 'cc -pie not supported on this architecture' + fi + echo \"\$UBSAN_CODE\" > test.c + cc -fsanitize=undefined -o test -fpie -pie test.c + atf_check -s ignore -e match:'$3' ./test + } + + ${1}32_body() { + # check whether this arch is 64bit + if ! cc -dM -E - < /dev/null | fgrep -q _LP64; then + atf_skip 'this is not a 64 bit architecture' + fi + if ! cc -m32 -dM -E - < /dev/null 2>/dev/null > ./def32; then + atf_skip 'cc -m32 not supported on this architecture' + else + if fgrep -q _LP64 ./def32; then + atf_fail 'cc -m32 does not generate netbsd32 binaries' + fi + fi + + echo \"\$UBSAN_CODE\" > test.c + cc -fsanitize=undefined -o df32 -m32 test.c + cc -fsanitize=undefined -o df64 test.c + file -b ./df32 > ./ftype32 + file -b ./df64 > ./ftype64 + if diff ./ftype32 ./ftype64 >/dev/null; then + atf_fail 'generated binaries do not differ' + fi + echo '32bit binaries on this platform are:' + cat ./ftype32 + echo 'While native (64bit) binaries are:' + cat ./ftype64 + atf_check -s ignore -e match:'$3' ./df32 + +# and another test with profile 32bit binaries + cc -fsanitize=undefined -o test -pg -m32 test.c + atf_check -s ignore -e match:'$3' ./test + }" +} + +ubsan_add_test_cases() { + test_target + test $SUPPORT = 'n' && { + atf_add_test_case target_not_supported + return 0 + } + + atf_add_test_case "$1" +# atf_add_test_case "$1_profile" + atf_add_test_case "$1_pic" + atf_add_test_case "$1_pie" +# atf_add_test_case "${1}32" +} diff --git a/usr.bin/cmp/t_cmp.sh b/usr.bin/cmp/t_cmp.sh old mode 100755 new mode 100644 diff --git a/usr.bin/col/Makefile b/usr.bin/col/Makefile new file mode 100644 --- /dev/null +++ b/usr.bin/col/Makefile @@ -0,0 +1,19 @@ +# $FreeBSD: head/usr.bin/col/tests/Makefile 366577 2020-10-09 15:27:37Z markj $ + +PACKAGE= tests + +TESTSDIR= ${TESTSBASE}/usr.bin/col +TESTS_SH= t_col + +FILESDIR= ${TESTSDIR} +FILES+= \ + hlf.in \ + hlf2.in \ + nl.in \ + nl2.in \ + nl3.in \ + rlf.in \ + rlf2.in \ + rlf3.in + +.include diff --git a/usr.bin/col/hlf.in b/usr.bin/col/hlf.in new file mode 100644 --- /dev/null +++ b/usr.bin/col/hlf.in @@ -0,0 +1,2 @@ +a +a8f8f diff --git a/usr.bin/col/hlf2.in b/usr.bin/col/hlf2.in new file mode 100644 --- /dev/null +++ b/usr.bin/col/hlf2.in @@ -0,0 +1 @@ +a9f diff --git a/usr.bin/col/nl.in b/usr.bin/col/nl.in new file mode 100644 --- /dev/null +++ b/usr.bin/col/nl.in @@ -0,0 +1,2 @@ +a +b diff --git a/usr.bin/col/nl2.in b/usr.bin/col/nl2.in new file mode 100644 --- /dev/null +++ b/usr.bin/col/nl2.in @@ -0,0 +1,2 @@ +a +b \ No newline at end of file diff --git a/usr.bin/col/nl3.in b/usr.bin/col/nl3.in new file mode 100644 --- /dev/null +++ b/usr.bin/col/nl3.in @@ -0,0 +1,4 @@ +a + +b + diff --git a/usr.bin/col/rlf.in b/usr.bin/col/rlf.in new file mode 100644 --- /dev/null +++ b/usr.bin/col/rlf.in @@ -0,0 +1,2 @@ +a + 7b diff --git a/usr.bin/col/rlf2.in b/usr.bin/col/rlf2.in new file mode 100644 --- /dev/null +++ b/usr.bin/col/rlf2.in @@ -0,0 +1,2 @@ +a + 7b diff --git a/usr.bin/col/rlf3.in b/usr.bin/col/rlf3.in new file mode 100644 --- /dev/null +++ b/usr.bin/col/rlf3.in @@ -0,0 +1 @@ +a b diff --git a/usr.bin/col/t_col.sh b/usr.bin/col/t_col.sh new file mode 100755 --- /dev/null +++ b/usr.bin/col/t_col.sh @@ -0,0 +1,114 @@ +#!/usr/bin/atf-sh +# $FreeBSD: head/usr.bin/col/tests/col_test.sh 366577 2020-10-09 15:27:37Z markj $ + +atf_test_case nl + +nl_head() +{ + atf_set "descr" "testing just newlines" +} +nl_body() +{ + atf_check \ + -o inline:"a\nb\n" \ + -e empty \ + -s exit:0 \ + col < $(atf_get_srcdir)/nl.in + + atf_check \ + -o inline:"a\nb\n" \ + -e empty \ + -s exit:0 \ + col -f < $(atf_get_srcdir)/nl.in + + atf_check \ + -o inline:"a\nb\n" \ + -e empty \ + -s exit:0 \ + col < $(atf_get_srcdir)/nl2.in + + atf_check \ + -o inline:"a\nb\n" \ + -e empty \ + -s exit:0 \ + col -f < $(atf_get_srcdir)/nl2.in + + atf_check \ + -o inline:"a\n\nb\n\n" \ + -e empty \ + -s exit:0 \ + col < $(atf_get_srcdir)/nl3.in +} + +atf_test_case rlf + +rlf_head() +{ + atf_set "descr" "testing reverse line feed" +} +rlf_body() +{ + atf_check \ + -o inline:"a b\n" \ + -e empty \ + -s exit:0 \ + col < $(atf_get_srcdir)/rlf.in + + atf_check \ + -o inline:"a b\n" \ + -e empty \ + -s exit:0 \ + col < $(atf_get_srcdir)/rlf2.in + + atf_check \ + -o inline:"a b\n" \ + -e empty \ + -s exit:0 \ + col -x < $(atf_get_srcdir)/rlf2.in + + atf_check \ + -o inline:" b\na\n" \ + -e empty \ + -s exit:0 \ + col < $(atf_get_srcdir)/rlf3.in +} + +atf_test_case hlf + +hlf_head() +{ + atf_set "descr" "testing half line feed" +} +hlf_body() +{ + atf_check \ + -o inline:"a f\naf\n" \ + -e empty \ + -s exit:0 \ + col < $(atf_get_srcdir)/hlf.in + + atf_check \ + -o inline:"a f9 f9 a\n" \ + -e empty \ + -s exit:0 \ + col -f < $(atf_get_srcdir)/hlf.in + + atf_check \ + -o inline:"a\n f\n" \ + -e empty \ + -s exit:0 \ + col < $(atf_get_srcdir)/hlf2.in + + atf_check \ + -o inline:"a9 f\n9" \ + -e empty \ + -s exit:0 \ + col -f < $(atf_get_srcdir)/hlf2.in +} + +atf_init_test_cases() +{ + atf_add_test_case nl + atf_add_test_case rlf + atf_add_test_case hlf +} diff --git a/usr.bin/config/Makefile b/usr.bin/config/Makefile --- a/usr.bin/config/Makefile +++ b/usr.bin/config/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.2 2014/10/29 16:24:32 uebayasi Exp $ +# $NetBSD: Makefile,v 1.3 2020/03/08 17:21:52 christos Exp $ SUBDIR= support @@ -12,6 +12,7 @@ FILESDIR= ${TESTSDIR} FILES= d_deffs_redef +FILES+= d_ifdef FILES+= d_loop FILES+= d_loop2 FILES+= d_min diff --git a/usr.bin/config/d_ifdef b/usr.bin/config/d_ifdef new file mode 100644 --- /dev/null +++ b/usr.bin/config/d_ifdef @@ -0,0 +1,62 @@ +include "arch/regress/conf/std.regress" +maxusers 4 + +define TRUE +ifdef TRUE +options OK1_1 +elifdef FALSE +options BAD1_1 +elifdef TRUE +options BAD1_2 +else +options BAD1_3 +endif + +ifdef FALSE +options BAD2_1 +elifdef TRUE +ifdef FALSE +options BAD2_2 +elifdef TRUE +options OK2_1 +else +options BAD2_3 +endif +options OK2_2 +endif + +ifdef FALSE +options BAD3_1 +elifdef TRUE +ifdef FALSE +options BAD3_2 +else +options OK3_1 +ifdef TRUE +ifdef FALSE +options BAD3_3 +elifdef TRUE +options OK3_2 +endif +options OK3_3 +elifdef TRUE +options BAD3_4 +else +options BAD3_5 +endif +options OK3_4 +endif +options OK3_5 +endif + +ifdef TRUE +options OK4_1 +else +ifdef TRUE +options BAD4_2 +else +options BAD4_3 +endif +endif + +config regress root on ? diff --git a/usr.bin/config/t_config.sh b/usr.bin/config/t_config.sh old mode 100755 new mode 100644 --- a/usr.bin/config/t_config.sh +++ b/usr.bin/config/t_config.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_config.sh,v 1.8 2016/08/27 12:08:14 christos Exp $ +# $NetBSD: t_config.sh,v 1.10 2020/03/09 20:33:16 christos Exp $ # # Copyright (c) 2008, 2010 The NetBSD Foundation, Inc. # All rights reserved. @@ -260,6 +260,45 @@ test_output min } +# Check ifdef supper +test_case ifdef pass "ifdef config" +check_ifdef_files() +{ + test -e Makefile && + test -e config_file.h && + test -e config_time.src && + test -e ioconf.c && + test -e ioconf.h && + test -e locators.h && + test -e swapregress.c && + test -h machine && + test -h regress && + : +} + +check_ifdef_makefile() +{ + local f=Makefile + local e='-DOK1_1 -DOK2_1 -DOK2_2 -DOK3_1 -DOK3_2 -DOK3_3 -DOK3_4 -DOK3_5 -DOK4_1 -DMAXUSERS="4"' + local r + r=$(make -f Makefile -V IDENT) + if [ "$r" != "$e" ]; then + echo "Expected: '$e'" + echo "Result: '$r'" + return 1 + fi +} + +check_ifdef() +{ + check_ifdef_files && + check_ifdef_makefile && + : +} +ifdef_body() { + test_output ifdef +} + atf_init_test_cases() { atf_add_test_case shadow_instance @@ -275,4 +314,5 @@ atf_add_test_case no_select atf_add_test_case devi atf_add_test_case min + atf_add_test_case ifdef } diff --git a/usr.bin/cpio/Makefile b/usr.bin/cpio/Makefile new file mode 100644 --- /dev/null +++ b/usr.bin/cpio/Makefile @@ -0,0 +1,114 @@ +# $NetBSD: Makefile,v 1.4 2020/01/19 17:36:57 christos Exp $ + +NOMAN= + +.include +LIBARCHIVE=${NETBSDSRCDIR}/external/bsd/libarchive/dist + +TESTSDIR= ${TESTSBASE}/usr.bin/cpio + +BINDIR= ${TESTSDIR} +PROGS+= h_cpio +TESTS_SH+= t_cpio + +CPPFLAGS+=-I${LIBARCHIVE}/test_utils -I${LIBARCHIVE}/cpio -I. +CPPFLAGS+=-I${LIBARCHIVE}/cpio/test -I${LIBARCHIVE}/../include +CPPFLAGS+=-I${LIBARCHIVE}/libarchive -I${LIBARCHIVE}/libarchive_fe +CPPFLAGS+=-DPLATFORM_CONFIG_H='"config_netbsd.h"' + +.PATH: ${LIBARCHIVE}/cpio/test ${LIBARCHIVE}/test_utils ${LIBARCHIVE}/cpio \ + ${LIBARCHIVE}/libarchive_fe + +DPADD+= ${LIBARCHIVE} ${LIBEXPAT} ${LIBBZ2} ${LIBLZMA} ${LIBZ} \ + ${LIBCRYPTO} ${LIBPTHREAD} +LDADD+= -larchive -lexpat -lbz2 -llzma -lz -lcrypto -lpthread + +SRCS.h_cpio= \ +test_main.c \ +test_utils.c \ +cmdline.c \ +err.c \ +test_0.c \ +test_basic.c \ +test_cmdline.c \ +test_extract_cpio_Z.c \ +test_extract_cpio_bz2.c \ +test_extract_cpio_grz.c \ +test_extract_cpio_gz.c \ +test_extract_cpio_lrz.c \ +test_extract_cpio_lz.c \ +test_extract_cpio_lz4.c \ +test_extract_cpio_lzma.c \ +test_extract_cpio_lzo.c \ +test_extract_cpio_xz.c \ +test_extract_cpio_zstd.c \ +test_format_newc.c \ +test_gcpio_compat.c \ +test_missing_file.c \ +test_option_0.c \ +test_option_B_upper.c \ +test_option_C_upper.c \ +test_option_J_upper.c \ +test_option_L_upper.c \ +test_option_Z_upper.c \ +test_option_a.c \ +test_option_b64encode.c \ +test_option_c.c \ +test_option_d.c \ +test_option_f.c \ +test_option_grzip.c \ +test_option_help.c \ +test_option_l.c \ +test_option_lrzip.c \ +test_option_lz4.c \ +test_option_lzma.c \ +test_option_lzop.c \ +test_option_m.c \ +test_option_passphrase.c \ +test_option_t.c \ +test_option_u.c \ +test_option_version.c \ +test_option_xz.c \ +test_option_y.c \ +test_option_z.c \ +test_option_zstd.c \ +test_owner_parse.c \ +test_passthrough_dotdot.c \ +test_passthrough_reverse.c + +FILESDIR= ${TESTSDIR} +FILES=\ +test_extract.cpio.Z.uu \ +test_extract.cpio.bz2.uu \ +test_extract.cpio.grz.uu \ +test_extract.cpio.gz.uu \ +test_extract.cpio.lrz.uu \ +test_extract.cpio.lz.uu \ +test_extract.cpio.lz4.uu \ +test_extract.cpio.lzma.uu \ +test_extract.cpio.lzo.uu \ +test_extract.cpio.xz.uu \ +test_extract.cpio.zst.uu \ +test_gcpio_compat_ref.bin.uu \ +test_gcpio_compat_ref.crc.uu \ +test_gcpio_compat_ref.newc.uu \ +test_gcpio_compat_ref.ustar.uu \ +test_gcpio_compat_ref_nosym.bin.uu \ +test_gcpio_compat_ref_nosym.crc.uu \ +test_gcpio_compat_ref_nosym.newc.uu \ +test_gcpio_compat_ref_nosym.ustar.uu \ +test_option_f.cpio.uu \ +test_option_m.cpio.uu \ +test_option_passphrase.zip.uu \ +test_option_t.cpio.uu \ +test_option_t.stdout.uu \ +test_option_tv.stdout.uu + +.include + +test_main.o test_main.d: list.h + +CLEANFILES+=list.h + +list.h: ${SRCS.h_cpio} Makefile + ${TOOL_GREP} -h '^DEFINE_TEST(' ${.ALLSRC} > ${.TARGET} diff --git a/usr.bin/cpio/t_cpio.sh b/usr.bin/cpio/t_cpio.sh new file mode 100644 --- /dev/null +++ b/usr.bin/cpio/t_cpio.sh @@ -0,0 +1,49 @@ +# $NetBSD: t_cpio.sh,v 1.1 2020/01/17 16:25:37 christos Exp $ +# +# Copyright (c) 2020 The NetBSD Foundation, Inc. +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +atf_test_case cpio + +cpio_head() +{ + atf_set "descr" "test cpio" +} + +cpio_body() +{ + local d=$(atf_get_srcdir) + local dd=$(pwd) + # to get the program name right since it is embedded in error + # strings that need to match + ln -s /usr/bin/cpio "$dd/bsdcpio" + atf_check -s exit:0 -o 'not-match:^Details for failing tests:.*' \ + -e ignore "$d/h_cpio" -p "$dd/bsdcpio" -r "$d" +} + +atf_init_test_cases() +{ + atf_add_test_case cpio +} diff --git a/usr.bin/cut/t_cut.sh b/usr.bin/cut/t_cut.sh old mode 100755 new mode 100644 diff --git a/usr.bin/diff/t_diff.sh b/usr.bin/diff/t_diff.sh old mode 100755 new mode 100644 diff --git a/usr.bin/dirname/t_dirname.sh b/usr.bin/dirname/t_dirname.sh old mode 100755 new mode 100644 diff --git a/usr.bin/find/t_find.sh b/usr.bin/find/t_find.sh old mode 100755 new mode 100644 diff --git a/usr.bin/fstat/Makefile b/usr.bin/fstat/Makefile new file mode 100644 --- /dev/null +++ b/usr.bin/fstat/Makefile @@ -0,0 +1,8 @@ +# $NetBSD: Makefile,v 1.1 2020/06/24 10:05:07 jruoho Exp $ + +.include + +TESTSDIR= ${TESTSBASE}/usr.bin/fstat +TESTS_SH= t_fstat + +.include diff --git a/usr.bin/fstat/t_fstat.sh b/usr.bin/fstat/t_fstat.sh new file mode 100644 --- /dev/null +++ b/usr.bin/fstat/t_fstat.sh @@ -0,0 +1,75 @@ +# $NetBSD: t_fstat.sh,v 1.1 2020/06/24 10:05:07 jruoho Exp $ +# +# Copyright (c) 2020 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Jukka Ruohonen. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +atf_test_case basic +basic_head() +{ + atf_require_prog fstat + atf_set "descr" "Check that fstat(1) works" +} + +basic_body() +{ + # If there are chrooted processes running, the following + # simple test should catch also those (cf. PR kern/55407). + # + pids=$(ps -A | awk '{print $1}') + + for pid in $pids; do + + if [ $pid = "PID" ]; then + continue + fi + + atf_check -o ignore -s exit:0 -e empty -x "fstat -p $pid" + done +} + +atf_test_case err +err_head() +{ + atf_require_prog fstat + atf_set "descr" "Check fstat(1) with invalid parameters" +} + +err_body() +{ + atf_check -o empty -s exit:1 -e not-empty -x "fstat -p -1" + atf_check -o empty -s exit:1 -e not-empty -x "fstat -p -100" + atf_check -o empty -s exit:1 -e not-empty -x "fstat -p abcd" + atf_check -o empty -s exit:1 -e not-empty -x "fstat -u abcd" + atf_check -o empty -s exit:1 -e not-empty -x "fstat -u -100" +} + +atf_init_test_cases() +{ + atf_add_test_case basic + atf_add_test_case err +} diff --git a/usr.bin/gdb/t_regress.sh b/usr.bin/gdb/t_regress.sh old mode 100755 new mode 100644 --- a/usr.bin/gdb/t_regress.sh +++ b/usr.bin/gdb/t_regress.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_regress.sh,v 1.1 2016/04/08 10:09:16 gson Exp $ +# $NetBSD: t_regress.sh,v 1.3 2020/06/25 11:12:03 jruoho Exp $ # # Copyright (c) 2016 The NetBSD Foundation, Inc. # All rights reserved. @@ -27,11 +27,10 @@ # Regression tests for some GDB PRs -# PR 47430 - atf_test_case threads threads_head() { - atf_set "descr" "Test that gdb works with threaded programs" + atf_set "descr" "Test that gdb works with " \ + "threaded programs (PR bin/47430)" atf_set "require.progs" "gdb" } threads_body() { @@ -50,11 +49,10 @@ atf_check -s exit:1 -o ignore -e ignore grep "Program received signal SIGTRAP" gdb.out } -# PR 48250 - atf_test_case pie pie_head() { - atf_set "descr" "Test that gdb works with PIE executables" + atf_set "descr" "Test that gdb works with " \ + "PIE executables (PR bin/48250)" atf_set "require.progs" "cc gdb" } pie_body() { @@ -71,7 +69,27 @@ atf_check -s exit:1 -o ignore -e ignore grep "annot access memory" gdb.out } +atf_test_case xml +xml_head() { + atf_set "descr" "Test that gdb was built " \ + "with XML support (PR bin/54154)" + atf_set "require.progs" "gdb" +} +xml_body() { + cat <<\EOF >target.xml + + i386:x86-64 + +EOF + cat <test.gdb +set tdesc filename "target.xml" +EOF + gdb --batch -x test.gdb >gdb.out 2>&1 + atf_check -s exit:1 -o ignore -e ignore grep "Can not parse XML" gdb.out +} + atf_init_test_cases() { atf_add_test_case threads atf_add_test_case pie + atf_add_test_case xml } diff --git a/usr.bin/grep/t_grep.sh b/usr.bin/grep/t_grep.sh old mode 100755 new mode 100644 --- a/usr.bin/grep/t_grep.sh +++ b/usr.bin/grep/t_grep.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_grep.sh,v 1.3 2017/01/14 20:43:52 christos Exp $ +# $NetBSD: t_grep.sh,v 1.6 2021/08/30 23:14:14 rillig Exp $ # # Copyright (c) 2008, 2009 The NetBSD Foundation, Inc. # All rights reserved. @@ -89,6 +89,106 @@ grep -w separated $(atf_get_srcdir)/d_input } +atf_test_case word_locale +word_locale_head() +{ + atf_set "descr" "Checks word search with locale" +} +word_locale_body() +{ + echo "array[]" > "input" + + # In the default locale, word search works. + atf_check -o file:"input" \ + env LC_ALL=C grep "array" "input" + atf_check -o file:"input" \ + env LC_ALL=C grep -w "array" "input" + + # XXX: In an UTF-8 locale, GNU Grep treats '[' as a word character. + atf_check -s exit:1 -o empty \ + env LC_ALL="C.UTF-8" grep -w "array" "input" +} + +atf_test_case word_in_line +word_in_line_head() +{ + atf_set "descr" "Checks word search in different locations of a line" +} +word_in_line_body() +{ + # See usr.bin/grep/util.c, "Check for whole word match", which + # looks suspiciously wrong. And indeed, NetBSD grep does not + # survive this test. GNU Grep does. + + echo "begin middle end" > "input" + + # A word at the beginning of a line is found. + atf_check -o file:"input" \ + env LC_ALL=C grep -w "begin" "input" + + # A word in the middle of a line is found. + atf_check -o file:"input" \ + env LC_ALL=C grep -w "middle" "input" + + # A word at the end of a line is found. + atf_check -o file:"input" \ + env LC_ALL=C grep -w "end" "input" + + # A subword at the beginning of a line is not found. + atf_check -s exit:1 -o empty \ + env LC_ALL=C grep -w "be" "input" + + # A subword in the middle of a line is not found. + atf_check -s exit:1 -o empty \ + env LC_ALL=C grep -w "mid" "input" + atf_check -s exit:1 -o empty \ + env LC_ALL=C grep -w "dle" "input" + + # A subword at the end of a line is not found. + atf_check -s exit:1 -o empty \ + env LC_ALL=C grep -w "nd" "input" +} + +atf_test_case word_in_line_utf8 +word_in_line_utf8_head() +{ + atf_set "descr" "Checks word search at the beginning of a line" +} +word_in_line_utf8_body() +{ + # See usr.bin/grep/util.c, "Check for whole word match", which + # looks suspiciously wrong. And indeed, NetBSD grep does not + # survive this test. GNU Grep does. + + echo "begin middle end" > "input" + + # A word at the beginning of a line is found. + atf_check -o file:"input" \ + env LC_ALL="C.UTF-8" grep -w "begin" "input" + + # A word in the middle of a line is found. + atf_check -o file:"input" \ + env LC_ALL="C.UTF-8" grep -w "middle" "input" + + # A word at the end of a line is found. + atf_check -o file:"input" \ + env LC_ALL="C.UTF-8" grep -w "end" "input" + + # A subword at the beginning of a line is not found. + atf_check -s exit:1 -o empty \ + env LC_ALL="C.UTF-8" grep -w "be" "input" + + # A subword in the middle of a line is not found. + atf_check -s exit:1 -o empty \ + env LC_ALL="C.UTF-8" grep -w "mid" "input" + atf_check -s exit:1 -o empty \ + env LC_ALL="C.UTF-8" grep -w "dle" "input" + + # A subword at the end of a line is not found. + atf_check -s exit:1 -o empty \ + env LC_ALL="C.UTF-8" grep -w "nd" "input" +} + atf_test_case begin_end begin_end_head() { @@ -234,6 +334,9 @@ atf_add_test_case recurse atf_add_test_case recurse_symlink atf_add_test_case word_regexps + atf_add_test_case word_locale + atf_add_test_case word_in_line + atf_add_test_case word_in_line_utf8 atf_add_test_case begin_end atf_add_test_case ignore_case atf_add_test_case invert diff --git a/usr.bin/gzip/t_gzip.sh b/usr.bin/gzip/t_gzip.sh old mode 100755 new mode 100644 diff --git a/usr.bin/id/Makefile b/usr.bin/id/Makefile --- a/usr.bin/id/Makefile +++ b/usr.bin/id/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.1 2012/03/17 16:33:14 jruoho Exp $ +# $NetBSD: Makefile,v 1.2 2019/08/15 08:22:52 kamil Exp $ NOMAN= # defined @@ -17,4 +17,10 @@ COPTS.id.c += -Wno-format-nonliteral +SANITIZER_RENAME_SYMBOL+= __getpwnam50 +SANITIZER_RENAME_SYMBOL+= __getpwuid50 +SANITIZER_RENAME_SYMBOL+= getgrgid +SANITIZER_RENAME_SYMBOL+= getgrouplist +SANITIZER_RENAME_SYMBOL+= getgroups + .include diff --git a/usr.bin/id/t_groups.sh b/usr.bin/id/t_groups.sh old mode 100755 new mode 100644 diff --git a/usr.bin/id/t_id.sh b/usr.bin/id/t_id.sh old mode 100755 new mode 100644 diff --git a/usr.bin/id/t_whoami.sh b/usr.bin/id/t_whoami.sh old mode 100755 new mode 100644 diff --git a/usr.bin/indent/Makefile b/usr.bin/indent/Makefile new file mode 100644 --- /dev/null +++ b/usr.bin/indent/Makefile @@ -0,0 +1,448 @@ +# $NetBSD: Makefile,v 1.8 2021/03/12 00:13:06 rillig Exp $ + +.include + +TESTSDIR= ${TESTSBASE}/usr.bin/indent +TESTS_SH= t_indent + +FILESDIR= ${TESTSDIR} +FILES= binary.0 +FILES+= binary.0.stdout +FILES+= block.0 +FILES+= block.0.stdout +FILES+= comment-line-end.0 +FILES+= comment-line-end.0.stdout +FILES+= comments.0 +FILES+= comments.0.pro +FILES+= comments.0.stdout +FILES+= declarations.0 +FILES+= declarations.0.stdout +FILES+= elsecomment.0 +FILES+= elsecomment.0.stdout +FILES+= elsecomment.0.pro +FILES+= f_decls.0 +FILES+= f_decls.0.stdout +FILES+= float.0 +FILES+= float.0.stdout +FILES+= indent_variables.0 +FILES+= indent_variables.0.pro +FILES+= indent_variables.0.stdout +FILES+= label.0 +FILES+= label.0.stdout +FILES+= label.0.pro +FILES+= lineno.0 +FILES+= lineno.0.pro +FILES+= lineno.0.stdout +FILES+= list_head.0 +FILES+= list_head.0.stdout +FILES+= ncs.0 +FILES+= ncs.0.stdout +FILES+= ncs.0.pro +FILES+= offsetof.0 +FILES+= offsetof.0.stdout +FILES+= opt--version.0 +FILES+= opt--version.0.pro +FILES+= opt--version.0.stdout +FILES+= opt-P.0 +FILES+= opt-P.0.pro +FILES+= opt-P.0.stdout +FILES+= opt-T.0 +FILES+= opt-T.0.pro +FILES+= opt-T.0.stdout +FILES+= opt-U.0 +FILES+= opt-U.0.list +FILES+= opt-U.0.pro +FILES+= opt-U.0.stdout +FILES+= opt-bacc.0 +FILES+= opt-bacc.0.pro +FILES+= opt-bacc.0.stdout +FILES+= opt-bad.0 +FILES+= opt-bad.0.pro +FILES+= opt-bad.0.stdout +FILES+= opt-badp.0 +FILES+= opt-badp.0.pro +FILES+= opt-badp.0.stdout +FILES+= opt-bap+sob.0 +FILES+= opt-bap+sob.0.pro +FILES+= opt-bap+sob.0.stdout +FILES+= opt-bap.0 +FILES+= opt-bap.0.pro +FILES+= opt-bap.0.stdout +FILES+= opt-bbb.0 +FILES+= opt-bbb.0.pro +FILES+= opt-bbb.0.stdout +FILES+= opt-bc.0 +FILES+= opt-bc.0.pro +FILES+= opt-bc.0.stdout +FILES+= opt-bl.0 +FILES+= opt-bl.0.pro +FILES+= opt-bl.0.stdout +FILES+= opt-br.0 +FILES+= opt-br.0.pro +FILES+= opt-br.0.stdout +FILES+= opt-bs.0 +FILES+= opt-bs.0.pro +FILES+= opt-bs.0.stdout +FILES+= opt-c.0 +FILES+= opt-c.0.pro +FILES+= opt-c.0.stdout +FILES+= opt-cd.0 +FILES+= opt-cd.0.pro +FILES+= opt-cd.0.stdout +FILES+= opt-cdb.0 +FILES+= opt-cdb.0.pro +FILES+= opt-cdb.0.stdout +FILES+= opt-ce.0 +FILES+= opt-ce.0.pro +FILES+= opt-ce.0.stdout +FILES+= opt-ci.0 +FILES+= opt-ci.0.pro +FILES+= opt-ci.0.stdout +FILES+= opt-cli.0 +FILES+= opt-cli.0.pro +FILES+= opt-cli.0.stdout +FILES+= opt-cs.0 +FILES+= opt-cs.0.pro +FILES+= opt-cs.0.stdout +FILES+= opt-d.0 +FILES+= opt-d.0.pro +FILES+= opt-d.0.stdout +FILES+= opt-di.0 +FILES+= opt-di.0.pro +FILES+= opt-di.0.stdout +FILES+= opt-dj.0 +FILES+= opt-dj.0.pro +FILES+= opt-dj.0.stdout +FILES+= opt-eei.0 +FILES+= opt-eei.0.pro +FILES+= opt-eei.0.stdout +FILES+= opt-ei.0 +FILES+= opt-ei.0.pro +FILES+= opt-ei.0.stdout +FILES+= opt-fbs.0 +FILES+= opt-fbs.0.pro +FILES+= opt-fbs.0.stdout +FILES+= opt-fc1.0 +FILES+= opt-fc1.0.pro +FILES+= opt-fc1.0.stdout +FILES+= opt-fcb.0 +FILES+= opt-fcb.0.pro +FILES+= opt-fcb.0.stdout +FILES+= opt-i.0 +FILES+= opt-i.0.pro +FILES+= opt-i.0.stdout +FILES+= opt-ip.0 +FILES+= opt-ip.0.pro +FILES+= opt-ip.0.stdout +FILES+= opt-l.0 +FILES+= opt-l.0.pro +FILES+= opt-l.0.stdout +FILES+= opt-lc.0 +FILES+= opt-lc.0.pro +FILES+= opt-lc.0.stdout +FILES+= opt-ldi.0 +FILES+= opt-ldi.0.pro +FILES+= opt-ldi.0.stdout +FILES+= opt-lp.0 +FILES+= opt-lp.0.pro +FILES+= opt-lp.0.stdout +FILES+= opt-lpl.0 +FILES+= opt-lpl.0.pro +FILES+= opt-lpl.0.stdout +FILES+= opt-nbacc.0 +FILES+= opt-nbacc.0.pro +FILES+= opt-nbacc.0.stdout +FILES+= opt-nbad.0 +FILES+= opt-nbad.0.pro +FILES+= opt-nbad.0.stdout +FILES+= opt-nbadp.0 +FILES+= opt-nbadp.0.pro +FILES+= opt-nbadp.0.stdout +FILES+= opt-nbap.0 +FILES+= opt-nbap.0.pro +FILES+= opt-nbap.0.stdout +FILES+= opt-nbbb.0 +FILES+= opt-nbbb.0.pro +FILES+= opt-nbbb.0.stdout +FILES+= opt-nbc.0 +FILES+= opt-nbc.0.pro +FILES+= opt-nbc.0.stdout +FILES+= opt-nbs.0 +FILES+= opt-nbs.0.pro +FILES+= opt-nbs.0.stdout +FILES+= opt-ncdb.0 +FILES+= opt-ncdb.0.pro +FILES+= opt-ncdb.0.stdout +FILES+= opt-nce.0 +FILES+= opt-nce.0.pro +FILES+= opt-nce.0.stdout +FILES+= opt-ncs.0 +FILES+= opt-ncs.0.pro +FILES+= opt-ncs.0.stdout +FILES+= opt-ndj.0 +FILES+= opt-ndj.0.pro +FILES+= opt-ndj.0.stdout +FILES+= opt-neei.0 +FILES+= opt-neei.0.pro +FILES+= opt-neei.0.stdout +FILES+= opt-nei.0 +FILES+= opt-nei.0.pro +FILES+= opt-nei.0.stdout +FILES+= opt-nfbs.0 +FILES+= opt-nfbs.0.pro +FILES+= opt-nfbs.0.stdout +FILES+= opt-nfc1.0 +FILES+= opt-nfc1.0.pro +FILES+= opt-nfc1.0.stdout +FILES+= opt-nfcb.0 +FILES+= opt-nfcb.0.pro +FILES+= opt-nfcb.0.stdout +FILES+= opt-nip.0 +FILES+= opt-nip.0.pro +FILES+= opt-nip.0.stdout +FILES+= opt-nlp.0 +FILES+= opt-nlp.0.pro +FILES+= opt-nlp.0.stdout +FILES+= opt-nlpl.0 +FILES+= opt-nlpl.0.pro +FILES+= opt-nlpl.0.stdout +FILES+= opt-npcs.0 +FILES+= opt-npcs.0.pro +FILES+= opt-npcs.0.stdout +FILES+= opt-npro.0 +FILES+= opt-npro.0.pro +FILES+= opt-npro.0.stdout +FILES+= opt-npsl.0 +FILES+= opt-npsl.0.pro +FILES+= opt-npsl.0.stdout +FILES+= opt-nsc.0 +FILES+= opt-nsc.0.pro +FILES+= opt-nsc.0.stdout +FILES+= opt-nsob.0 +FILES+= opt-nsob.0.pro +FILES+= opt-nsob.0.stdout +FILES+= opt-nut.0 +FILES+= opt-nut.0.pro +FILES+= opt-nut.0.stdout +FILES+= opt-nv.0 +FILES+= opt-nv.0.pro +FILES+= opt-nv.0.stdout +FILES+= opt-pcs.0 +FILES+= opt-pcs.0.pro +FILES+= opt-pcs.0.stdout +FILES+= opt-psl.0 +FILES+= opt-psl.0.pro +FILES+= opt-psl.0.stdout +FILES+= opt-sc.0 +FILES+= opt-sc.0.pro +FILES+= opt-sc.0.stdout +FILES+= opt-sob.0 +FILES+= opt-sob.0.pro +FILES+= opt-sob.0.stdout +FILES+= opt-ta.0 +FILES+= opt-ta.0.pro +FILES+= opt-ta.0.stdout +FILES+= opt-ts.0 +FILES+= opt-ts.0.pro +FILES+= opt-ts.0.stdout +FILES+= opt-ut.0 +FILES+= opt-ut.0.pro +FILES+= opt-ut.0.stdout +FILES+= opt-v.0 +FILES+= opt-v.0.pro +FILES+= opt-v.0.stdout +FILES+= parens.0 +FILES+= parens.0.stdout +FILES+= parens.0.pro +FILES+= pcs.0 +FILES+= pcs.0.stdout +FILES+= pcs.0.pro +FILES+= cs.0 +FILES+= cs.0.stdout +FILES+= cs.0.pro +FILES+= struct.0 +FILES+= struct.0.stdout +FILES+= surplusbad.0 +FILES+= surplusbad.0.stdout +FILES+= surplusbad.0.pro +FILES+= token-binary_op.0 +FILES+= token-binary_op.0.pro +FILES+= token-binary_op.0.stdout +FILES+= token-case_label.0 +FILES+= token-case_label.0.pro +FILES+= token-case_label.0.stdout +FILES+= token-colon.0 +FILES+= token-colon.0.pro +FILES+= token-colon.0.stdout +FILES+= token-comma.0 +FILES+= token-comma.0.pro +FILES+= token-comma.0.stdout +FILES+= token-comment.0 +FILES+= token-comment.0.pro +FILES+= token-comment.0.stdout +FILES+= token-decl.0 +FILES+= token-decl.0.pro +FILES+= token-decl.0.stdout +FILES+= token-do_stmt.0 +FILES+= token-do_stmt.0.pro +FILES+= token-do_stmt.0.stdout +FILES+= token-end_of_file.0 +FILES+= token-end_of_file.0.pro +FILES+= token-end_of_file.0.stdout +FILES+= token-for_exprs.0 +FILES+= token-for_exprs.0.pro +FILES+= token-for_exprs.0.stdout +FILES+= token-form_feed.0 +FILES+= token-form_feed.0.pro +FILES+= token-form_feed.0.stdout +FILES+= token-funcname.0 +FILES+= token-funcname.0.pro +FILES+= token-funcname.0.stdout +FILES+= token-ident.0 +FILES+= token-ident.0.pro +FILES+= token-ident.0.stdout +FILES+= token-if_expr.0 +FILES+= token-if_expr.0.pro +FILES+= token-if_expr.0.stdout +FILES+= token-if_expr_stmt.0 +FILES+= token-if_expr_stmt.0.pro +FILES+= token-if_expr_stmt.0.stdout +FILES+= token-if_expr_stmt_else.0 +FILES+= token-if_expr_stmt_else.0.pro +FILES+= token-if_expr_stmt_else.0.stdout +FILES+= token-keyword_do.0 +FILES+= token-keyword_do.0.pro +FILES+= token-keyword_do.0.stdout +FILES+= token-keyword_do_else.0 +FILES+= token-keyword_do_else.0.pro +FILES+= token-keyword_do_else.0.stdout +FILES+= token-keyword_else.0 +FILES+= token-keyword_else.0.pro +FILES+= token-keyword_else.0.stdout +FILES+= token-keyword_for_if_while.0 +FILES+= token-keyword_for_if_while.0.pro +FILES+= token-keyword_for_if_while.0.stdout +FILES+= token-keyword_struct_union_enum.0 +FILES+= token-keyword_struct_union_enum.0.pro +FILES+= token-keyword_struct_union_enum.0.stdout +FILES+= token-lbrace.0 +FILES+= token-lbrace.0.pro +FILES+= token-lbrace.0.stdout +FILES+= token-lparen.0 +FILES+= token-lparen.0.pro +FILES+= token-lparen.0.stdout +FILES+= token-newline.0 +FILES+= token-newline.0.pro +FILES+= token-newline.0.stdout +FILES+= token-period.0 +FILES+= token-period.0.pro +FILES+= token-period.0.stdout +FILES+= token-postfix_op.0 +FILES+= token-postfix_op.0.pro +FILES+= token-postfix_op.0.stdout +FILES+= token-preprocessing.0 +FILES+= token-preprocessing.0.pro +FILES+= token-preprocessing.0.stdout +FILES+= token-question.0 +FILES+= token-question.0.pro +FILES+= token-question.0.stdout +FILES+= token-rbrace.0 +FILES+= token-rbrace.0.pro +FILES+= token-rbrace.0.stdout +FILES+= token-rparen.0 +FILES+= token-rparen.0.pro +FILES+= token-rparen.0.stdout +FILES+= token-semicolon.0 +FILES+= token-semicolon.0.pro +FILES+= token-semicolon.0.stdout +FILES+= token-stmt.0 +FILES+= token-stmt.0.pro +FILES+= token-stmt.0.stdout +FILES+= token-stmt_list.0 +FILES+= token-stmt_list.0.pro +FILES+= token-stmt_list.0.stdout +FILES+= token-storage_class.0 +FILES+= token-storage_class.0.pro +FILES+= token-storage_class.0.stdout +FILES+= token-string_prefix.0 +FILES+= token-string_prefix.0.pro +FILES+= token-string_prefix.0.stdout +FILES+= token-switch_expr.0 +FILES+= token-switch_expr.0.pro +FILES+= token-switch_expr.0.stdout +FILES+= token-type_def.0 +FILES+= token-type_def.0.pro +FILES+= token-type_def.0.stdout +FILES+= token-unary_op.0 +FILES+= token-unary_op.0.pro +FILES+= token-unary_op.0.stdout +FILES+= token-while_expr.0 +FILES+= token-while_expr.0.pro +FILES+= token-while_expr.0.stdout +FILES+= types_from_file.0 +FILES+= types_from_file.0.stdout +FILES+= types_from_file.0.list +FILES+= types_from_file.0.pro +FILES+= wchar.0 +FILES+= wchar.0.stdout + +add-test: .PHONY + @set -eu; \ + test=${NAME:Q}; \ + [ "$$test" ] || { \ + echo "usage: ${MAKE} add-test NAME="; \ + exit; \ + }; \ + \ + if [ -f "$$test" ]; then \ + echo "error: test $$test already exists." 1>&2; \ + exit 1; \ + fi; \ + \ + echo "=> Adding test $$test"; \ + printf '%s\n' \ + '/* $$''NetBSD$$ */' \ + '/* $$''FreeBSD$$ */' \ + '' \ + '/*' \ + ' * TODO: Explain the purpose of the test.' \ + '*/' \ + '' \ + '// TODO: Add some code that passes.' \ + > "$$test"; \ + printf '%s\n' \ + '/* $$''NetBSD$$ */' \ + '/* $$''FreeBSD$$ */' \ + '' \ + '/*' \ + ' * TODO: Explain the command line options of the test.' \ + ' */' \ + '' \ + '/* TODO: Add some command line options */' \ + > "$$test.pro"; \ + cat < "$$test" > "$$test.stdout"; \ + cvs add "$$test" "$$test.pro" "$$test.stdout"; \ + printf '%s\n' \ + '/^FILES+=/i' \ + "FILES+= $$test" \ + "FILES+= $$test.pro" \ + "FILES+= $$test.stdout" \ + '.' 'w' 'q' \ + | ed Makefile; \ + ${MAKE} sync-mi + +# Note: only works for adding tests. +# To remove a test, the $$mi file must be edited manually. +sync-mi: .PHONY + @set -eu; \ + cd "${MAKEFILE:tA:H}/../../.."; \ + mi="distrib/sets/lists/tests/mi"; \ + cvs update "$$mi"; \ + fmt="./usr/tests/usr.bin/indent/%s\ttests-usr.bin-tests\tcompattestfile,atf\n"; \ + cat "$$mi" > "$$mi.tmp"; \ + printf "$$fmt" ${FILES:M${NAME}*} >> "$$mi.tmp"; \ + distrib/sets/fmt-list "$$mi.tmp"; \ + mv "$$mi.tmp" "$$mi"; \ + cvs diff "$$mi" || true + +.include diff --git a/usr.bin/indent/binary.0 b/usr.bin/indent/binary.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/binary.0 @@ -0,0 +1,11 @@ +/* $NetBSD: binary.0,v 1.1 2019/04/04 15:27:35 kamil Exp $ */ +/* $FreeBSD: head/usr.bin/indent/tests/binary.0 318471 2017-05-18 17:15:58Z pstef $ */ +#define b00101010 -1 +void t(void) { + unsigned a[] = {0b00101010, 0x00005678, 02, 17U}; + float x[] = {.7f, 0.7f}; + unsigned long ul[] = {0b00001111UL, 0x01010101UL, 02UL, 17UL}; + + if (0 b00101010) + return; +} diff --git a/usr.bin/indent/binary.0.stdout b/usr.bin/indent/binary.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/binary.0.stdout @@ -0,0 +1,13 @@ +/* $NetBSD: binary.0.stdout,v 1.1 2019/04/04 15:27:35 kamil Exp $ */ +/* $FreeBSD: head/usr.bin/indent/tests/binary.0.stdout 321381 2017-07-23 14:04:45Z pstef $ */ +#define b00101010 -1 +void +t(void) +{ + unsigned a[] = {0b00101010, 0x00005678, 02, 17U}; + float x[] = {.7f, 0.7f}; + unsigned long ul[] = {0b00001111UL, 0x01010101UL, 02UL, 17UL}; + + if (0 b00101010) + return; +} diff --git a/usr.bin/indent/block.0 b/usr.bin/indent/block.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/block.0 @@ -0,0 +1,14 @@ +/* $NetBSD: block.0,v 1.1 2021/03/08 20:55:34 rillig Exp $ */ +/* $FreeBSD$ */ + +void +function(void) +{ + if (true) { + + } + + { + print("block"); + } +} diff --git a/usr.bin/indent/block.0.stdout b/usr.bin/indent/block.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/block.0.stdout @@ -0,0 +1,15 @@ +/* $NetBSD: block.0.stdout,v 1.1 2021/03/08 20:55:34 rillig Exp $ */ +/* $FreeBSD$ */ + +void +function(void) +{ + if (true) { + +/* $ FIXME: indent must not merge these braces. */ + } { +/* $ FIXME: the following empty line was not in the input. */ + + print("block"); + } +} diff --git a/usr.bin/indent/comment-line-end.0 b/usr.bin/indent/comment-line-end.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/comment-line-end.0 @@ -0,0 +1,30 @@ +/* $NetBSD: comment-line-end.0,v 1.5 2021/03/12 18:28:07 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Demonstrates handling of line-end '//' comments. + * + * Even though this type of comments had been added in C99, indent didn't + * support these comments until 2021 and instead messed up the code in + * unpredictable ways. + */ + +int dummy // comment += // eq +1 // one ++ // plus +2; // two + +/////separator///// + +void function(void){} + +// Note: removing one of these line-end comments affected the formatting +// of the main function below, before indent supported '//' comments. + +int +main(void) +{ +} + +// end-of-line comment at the end of the file diff --git a/usr.bin/indent/comment-line-end.0.stdout b/usr.bin/indent/comment-line-end.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/comment-line-end.0.stdout @@ -0,0 +1,34 @@ +/* $NetBSD: comment-line-end.0.stdout,v 1.5 2021/03/12 18:28:07 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Demonstrates handling of line-end '//' comments. + * + * Even though this type of comments had been added in C99, indent didn't + * support these comments until 2021 and instead messed up the code in + * unpredictable ways. + */ + +int dummy // comment + = // eq + 1 // one + + // plus + 2; // two + +/////separator///// + +void +function(void) +{ +} + +// Note: removing one of these line-end comments affected the formatting +// of the main function below, before indent supported '//' comments. + +int +main(void) +{ +} + +// end-of-line comment at the end of the file + diff --git a/usr.bin/indent/comments.0 b/usr.bin/indent/comments.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/comments.0 @@ -0,0 +1,54 @@ +/* $NetBSD: comments.0,v 1.2 2021/03/14 01:34:13 rillig Exp $ */ +/* $FreeBSD: head/usr.bin/indent/tests/comments.0 321383 2017-07-23 15:07:52Z pstef $ */ +typedef enum x { + aaaaaaaaaaaaaaaaaaaaaa = 1 << 0, /* test a */ + bbbbbbbbbbbbbbbbb = 1 << 1, /* test b */ + cccccccccccccc = 1 << 1, /* test c */ + dddddddddddddddddddddddddddddd = 1 << 2 /* test d */ +} x; + +/* See r303597, r303598, r309219, and r309343 */ +void t(void) { + /* + * Old indent wrapped the URL near where this sentence ends. + * + * https://www.freebsd.org/cgi/man.cgi?query=indent&apropos=0&sektion=0&manpath=FreeBSD+12-current&arch=default&format=html + */ + + /* + * The default maximum line length for comments is 78, and the 'kk' at + * the end makes the line exactly 78 bytes long. + * + * aaaaaa bbbbbb cccccc dddddd eeeeee ffffff ggggg hhhhh iiiii jjjj kk + */ + + /* + * Old indent unnecessarily removed the star comment continuation on the next line. + * + * *test* + */ + + /* r309219 Go through linked list, freeing from the malloced (t[-1]) address. */ + + /* r309343 */ +} + +int c(void) +{ + if (1) { /*- a christmas tree * + *** + ***** */ + /*- another one * + *** + ***** */ + 7; + } + + if (1) /*- a christmas tree * + *** + ***** */ + /*- another one * + *** + ***** */ + 1; +} diff --git a/usr.bin/indent/comments.0.pro b/usr.bin/indent/comments.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/comments.0.pro @@ -0,0 +1,3 @@ +/* $NetBSD: comments.0.pro,v 1.1 2019/04/04 15:27:35 kamil Exp $ */ +/* $FreeBSD: head/usr.bin/indent/tests/comments.0.pro 321383 2017-07-23 15:07:52Z pstef $ */ +-bbb diff --git a/usr.bin/indent/comments.0.stdout b/usr.bin/indent/comments.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/comments.0.stdout @@ -0,0 +1,61 @@ +/* $NetBSD: comments.0.stdout,v 1.2 2021/03/14 01:34:13 rillig Exp $ */ +/* $FreeBSD: head/usr.bin/indent/tests/comments.0.stdout 334563 2018-06-03 15:28:55Z pstef $ */ +typedef enum x { + aaaaaaaaaaaaaaaaaaaaaa = 1 << 0, /* test a */ + bbbbbbbbbbbbbbbbb = 1 << 1, /* test b */ + cccccccccccccc = 1 << 1, /* test c */ + dddddddddddddddddddddddddddddd = 1 << 2 /* test d */ +} x; + +/* See r303597, r303598, r309219, and r309343 */ +void +t(void) +{ + /* + * Old indent wrapped the URL near where this sentence ends. + * + * https://www.freebsd.org/cgi/man.cgi?query=indent&apropos=0&sektion=0&manpath=FreeBSD+12-current&arch=default&format=html + */ + + /* + * The default maximum line length for comments is 78, and the 'kk' at + * the end makes the line exactly 78 bytes long. + * + * aaaaaa bbbbbb cccccc dddddd eeeeee ffffff ggggg hhhhh iiiii jjjj kk + */ + + /* + * Old indent unnecessarily removed the star comment continuation on + * the next line. + * + * *test* + */ + + /* + * r309219 Go through linked list, freeing from the malloced (t[-1]) + * address. + */ + + /* r309343 */ +} + +int +c(void) +{ + if (1) { /*- a christmas tree * + *** + ***** */ + /*- another one * + *** + ***** */ + 7; + } + + if (1) /*- a christmas tree * + *** + ***** */ + /*- another one * + *** + ***** */ + 1; +} diff --git a/usr.bin/indent/cs.0 b/usr.bin/indent/cs.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/cs.0 @@ -0,0 +1,5 @@ +/* $NetBSD: cs.0,v 1.1 2019/04/04 15:27:35 kamil Exp $ */ +/* $FreeBSD: head/usr.bin/indent/tests/cs.0 334944 2018-06-11 05:35:57Z pstef $ */ +void t(void) { + int a = (double) 8; +} diff --git a/usr.bin/indent/cs.0.pro b/usr.bin/indent/cs.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/cs.0.pro @@ -0,0 +1,3 @@ +/* $NetBSD: cs.0.pro,v 1.1 2019/04/04 15:27:35 kamil Exp $ */ +/* $FreeBSD: head/usr.bin/indent/tests/cs.0.pro 334944 2018-06-11 05:35:57Z pstef $ */ +-cs diff --git a/usr.bin/indent/cs.0.stdout b/usr.bin/indent/cs.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/cs.0.stdout @@ -0,0 +1,7 @@ +/* $NetBSD: cs.0.stdout,v 1.1 2019/04/04 15:27:35 kamil Exp $ */ +/* $FreeBSD: head/usr.bin/indent/tests/cs.0.stdout 334944 2018-06-11 05:35:57Z pstef $ */ +void +t(void) +{ + int a = (double) 8; +} diff --git a/usr.bin/indent/declarations.0 b/usr.bin/indent/declarations.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/declarations.0 @@ -0,0 +1,80 @@ +/* $NetBSD: declarations.0,v 1.2 2021/03/06 19:51:24 rillig Exp $ */ +/* $FreeBSD: head/usr.bin/indent/tests/declarations.0 334478 2018-06-01 09:41:15Z pstef $ */ +/* See r303570 */ + +typedef void (*voidptr) (int *); + +static const struct +{ + double x; + double y, z; +} n[m + 1] = +{ + { + .0, + .9, + 5 + } +}; + +typedef struct Complex +{ + double x; + double y; +} Complex; + +void +t1 (char *a, int b, + void (*fn)(void)) +{} + +void t2 (char *x, int y) +{ + int a, + b, + c; + int + *d, + *e, + *f; + int (*g)(), + (*h)(), + (*i)(); + int j, + k, + l; + int m + ,n + ,o + ; + int chars[ /* push the comma beyond column 74 .... */ ], x; +} + +const int int_minimum_size = +MAXALIGN(offsetof(int, test)) + MAXIMUM_ALIGNOF; + +int *int_create(void) +{ + +} + +static +_attribute_printf(1, 2) +void +print_error(const char *fmt,...) +{ + +} + +static LIST_HEAD(, alq) ald_active; +static int ald_shutingdown = 0; +struct thread *ald_thread; + +static int +do_execve(td, args, mac_p) + struct thread *td; + struct image_args *args; + struct mac *mac_p; +{ + +} diff --git a/usr.bin/indent/declarations.0.stdout b/usr.bin/indent/declarations.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/declarations.0.stdout @@ -0,0 +1,74 @@ +/* $NetBSD: declarations.0.stdout,v 1.1 2019/04/04 15:27:35 kamil Exp $ */ +/* $FreeBSD: head/usr.bin/indent/tests/declarations.0.stdout 334480 2018-06-01 09:58:44Z pstef $ */ +/* See r303570 */ + +typedef void (*voidptr) (int *); + +static const struct { + double x; + double y, z; +} n[m + 1] = +{ + { + .0, + .9, + 5 + } +}; + +typedef struct Complex { + double x; + double y; +} Complex; + +void +t1(char *a, int b, + void (*fn) (void)) +{ +} + +void +t2(char *x, int y) +{ + int a, b, c; + int + *d, *e, *f; + int (*g) (), (*h) (), (*i) (); + int j, k, l; + int m + ,n + ,o + ; + int chars[ /* push the comma beyond column 74 .... */ ], + x; +} + +const int int_minimum_size = +MAXALIGN(offsetof(int, test)) + MAXIMUM_ALIGNOF; + +int * +int_create(void) +{ + +} + +static +_attribute_printf(1, 2) +void +print_error(const char *fmt,...) +{ + +} + +static LIST_HEAD(, alq) ald_active; +static int ald_shutingdown = 0; +struct thread *ald_thread; + +static int +do_execve(td, args, mac_p) + struct thread *td; + struct image_args *args; + struct mac *mac_p; +{ + +} diff --git a/usr.bin/indent/elsecomment.0 b/usr.bin/indent/elsecomment.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/elsecomment.0 @@ -0,0 +1,43 @@ +/* $NetBSD: elsecomment.0,v 1.1 2019/04/04 15:27:35 kamil Exp $ */ +/* $FreeBSD: head/usr.bin/indent/tests/elsecomment.0.pro 314613 2017-03-03 20:15:22Z ngie $ */ +/* See r303484 and r309342 */ +void t(void) { + /* The two if statements below excercise two different code paths. */ + + if (1) /* a */ int a; else /* b */ int b; + + if (1) /* a */ + int a; + else /* b */ + int b; + + if (1) { + + } + + + + /* Old indent would remove the 3 blank lines above, awaiting "else". */ + + if (1) { + int a; + } + + + else if (0) { + int b; + } + /* test */ + else + ; + + if (1) + ; + else /* Old indent would get very confused here */ + /* We also mustn't assume that there's only one comment */ + /* before the left brace. */ + { + + + } +} diff --git a/usr.bin/indent/elsecomment.0.pro b/usr.bin/indent/elsecomment.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/elsecomment.0.pro @@ -0,0 +1,3 @@ +/* $NetBSD: elsecomment.0.pro,v 1.1 2019/04/04 15:27:35 kamil Exp $ */ +/* $FreeBSD: head/usr.bin/indent/tests/elsecomment.0.pro 314613 2017-03-03 20:15:22Z ngie $ */ +-bl diff --git a/usr.bin/indent/elsecomment.0.stdout b/usr.bin/indent/elsecomment.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/elsecomment.0.stdout @@ -0,0 +1,48 @@ +/* $NetBSD: elsecomment.0.stdout,v 1.1 2019/04/04 15:27:35 kamil Exp $ */ +/* $FreeBSD: head/usr.bin/indent/tests/elsecomment.0.stdout 334559 2018-06-03 14:03:20Z pstef $ */ +/* See r303484 and r309342 */ +void +t(void) +{ + /* The two if statements below excercise two different code paths. */ + + if (1) /* a */ + int a; + else /* b */ + int b; + + if (1) /* a */ + int a; + else /* b */ + int b; + + if (1) + { + + } + + + + /* Old indent would remove the 3 blank lines above, awaiting "else". */ + + if (1) + { + int a; + } else if (0) + { + int b; + } + /* test */ + else + ; + + if (1) + ; + else /* Old indent would get very confused here */ + /* We also mustn't assume that there's only one comment */ + /* before the left brace. */ + { + + + } +} diff --git a/usr.bin/indent/f_decls.0 b/usr.bin/indent/f_decls.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/f_decls.0 @@ -0,0 +1,30 @@ +/* $NetBSD: f_decls.0,v 1.1 2019/04/04 15:27:35 kamil Exp $ */ +/* $FreeBSD: head/usr.bin/indent/tests/f_decls.0 334566 2018-06-03 16:42:58Z pstef $ */ + +char * x(void) +{ + type identifier; + type *pointer; + unused * value; + (void)unused * value; + + dmax = (double)3 * 10.0; + dmin = (double)dmax * 10.0; + davg = (double)dmax * dmin; + + return NULL; +} + +int * +y(void) { + +} + +int +z(void) { + +} + +int x; +int *y; +int * * * * z; diff --git a/usr.bin/indent/f_decls.0.stdout b/usr.bin/indent/f_decls.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/f_decls.0.stdout @@ -0,0 +1,33 @@ +/* $NetBSD: f_decls.0.stdout,v 1.1 2019/04/04 15:27:35 kamil Exp $ */ +/* $FreeBSD: head/usr.bin/indent/tests/f_decls.0.stdout 334566 2018-06-03 16:42:58Z pstef $ */ + +char * +x(void) +{ + type identifier; + type *pointer; + unused *value; + (void)unused * value; + + dmax = (double)3 * 10.0; + dmin = (double)dmax * 10.0; + davg = (double)dmax * dmin; + + return NULL; +} + +int * +y(void) +{ + +} + +int +z(void) +{ + +} + +int x; +int *y; +int ****z; diff --git a/usr.bin/indent/float.0 b/usr.bin/indent/float.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/float.0 @@ -0,0 +1,9 @@ +/* $NetBSD: float.0,v 1.1 2019/04/04 15:27:35 kamil Exp $ */ +/* $FreeBSD: head/usr.bin/indent/tests/float.0 337862 2018-08-15 18:19:45Z pstef $ */ +void t(void) { + unsigned long x = 314UL; + double y[] = {0x1P+9F, 0.3, .1, 1.2f, 0xa.p01f, 3.14f, 2.L}; + int z = 0b0101; + DO_NOTHING; + x._y = 5; +} diff --git a/usr.bin/indent/float.0.stdout b/usr.bin/indent/float.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/float.0.stdout @@ -0,0 +1,11 @@ +/* $NetBSD: float.0.stdout,v 1.1 2019/04/04 15:27:35 kamil Exp $ */ +/* $FreeBSD: head/usr.bin/indent/tests/float.0.stdout 337862 2018-08-15 18:19:45Z pstef $ */ +void +t(void) +{ + unsigned long x = 314UL; + double y[] = {0x1P+9F, 0.3, .1, 1.2f, 0xa.p01f, 3.14f, 2.L}; + int z = 0b0101; + DO_NOTHING; + x._y = 5; +} diff --git a/usr.bin/indent/indent_variables.0 b/usr.bin/indent/indent_variables.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/indent_variables.0 @@ -0,0 +1,22 @@ +/* $NetBSD: indent_variables.0,v 1.1 2021/03/09 20:43:20 rillig Exp $ */ +/* $FreeBSD$ */ + +/*- + * Demonstrate how variable declarations are broken into several lines when + * the line length limit is set quite low. + */ + +struct s0 a,b; +struct s01 a,b; +struct s012 a,b; +struct s0123 a,b; +struct s01234 a,b; +struct s012345 a,b; +struct s0123456 a,b; +struct s01234567 a,b; +struct s012345678 a,b; +struct s0123456789 a,b; +struct s01234567890 a,b; +struct s012345678901 a,b; +struct s0123456789012 a,b; +struct s01234567890123 a,b; diff --git a/usr.bin/indent/indent_variables.0.pro b/usr.bin/indent/indent_variables.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/indent_variables.0.pro @@ -0,0 +1,5 @@ +/* $NetBSD: indent_variables.0.pro,v 1.1 2021/03/09 20:43:20 rillig Exp $ */ +/* $FreeBSD$ */ + +-l20 +-di0 diff --git a/usr.bin/indent/indent_variables.0.stdout b/usr.bin/indent/indent_variables.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/indent_variables.0.stdout @@ -0,0 +1,36 @@ +/* $NetBSD: indent_variables.0.stdout,v 1.1 2021/03/09 20:43:20 rillig Exp $ */ +/* $FreeBSD$ */ + +/*- + * Demonstrate how variable declarations are broken into several lines when + * the line length limit is set quite low. + */ + +struct s0 a, + b; +struct s01 a, + b; +struct s012 a, + b; +struct s0123 a, + b; +struct s01234 a, + b; +struct s012345 a, + b; +struct s0123456 a, + b; +struct s01234567 a, + b; +struct s012345678 a, + b; +struct s0123456789 a, + b; +struct s01234567890 a, + b; +struct s012345678901 a, + b; +struct s0123456789012 a, + b; +struct s01234567890123 a, + b; diff --git a/usr.bin/indent/label.0 b/usr.bin/indent/label.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/label.0 @@ -0,0 +1,14 @@ +/* $NetBSD: label.0,v 1.1 2019/04/04 15:27:35 kamil Exp $ */ +/* $FreeBSD: head/usr.bin/indent/tests/label.0 313544 2017-02-10 09:31:39Z pstef $ */ +/* See r303489 */ +void t(void) { + switch (1) + { + case 1: /* test */ + case 2: /* test */ + } +CLEANUP: + ; +V: ; +U: ; +} diff --git a/usr.bin/indent/label.0.pro b/usr.bin/indent/label.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/label.0.pro @@ -0,0 +1,3 @@ +/* $NetBSD: label.0.pro,v 1.1 2019/04/04 15:27:35 kamil Exp $ */ +/* $FreeBSD: head/usr.bin/indent/tests/label.0.pro 314613 2017-03-03 20:15:22Z ngie $ */ +-nut diff --git a/usr.bin/indent/label.0.stdout b/usr.bin/indent/label.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/label.0.stdout @@ -0,0 +1,15 @@ +/* $NetBSD: label.0.stdout,v 1.1 2019/04/04 15:27:35 kamil Exp $ */ +/* $FreeBSD: head/usr.bin/indent/tests/label.0.stdout 321381 2017-07-23 14:04:45Z pstef $ */ +/* See r303489 */ +void +t(void) +{ + switch (1) { + case 1: /* test */ + case 2: /* test */ + } +CLEANUP: + ; +V: ; +U: ; +} diff --git a/usr.bin/indent/lineno.0 b/usr.bin/indent/lineno.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/lineno.0 @@ -0,0 +1,9 @@ +/* $NetBSD: lineno.0,v 1.1 2021/03/08 20:12:04 rillig Exp $ */ +/* $FreeBSD$ */ + +/* Demonstrates line number counting in verbose mode. */ + +int * +function(void) +{ +} diff --git a/usr.bin/indent/lineno.0.pro b/usr.bin/indent/lineno.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/lineno.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: lineno.0.pro,v 1.1 2021/03/08 20:12:04 rillig Exp $ */ +/* $FreeBSD$ */ + +-v diff --git a/usr.bin/indent/lineno.0.stdout b/usr.bin/indent/lineno.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/lineno.0.stdout @@ -0,0 +1,14 @@ +/* $NetBSD: lineno.0.stdout,v 1.1 2021/03/08 20:12:04 rillig Exp $ */ +/* $FreeBSD$ */ + +/* Demonstrates line number counting in verbose mode. */ + +int * +function(void) +{ +} +/* $ In the below output, the '5' means 5 non-empty lines. */ +/* $ XXX: It's rather strange that -v writes to stdout, */ +/* $ even in filter mode. This output belongs on stderr instead. */ +There were 5 output lines and 1 comments +(Lines with comments)/(Lines with code): 0.250 diff --git a/usr.bin/indent/list_head.0 b/usr.bin/indent/list_head.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/list_head.0 @@ -0,0 +1,17 @@ +/* $NetBSD: list_head.0,v 1.1 2019/04/04 15:27:35 kamil Exp $ */ +/* $FreeBSD: head/usr.bin/indent/tests/list_head.0 313544 2017-02-10 09:31:39Z pstef $ */ +/* See r309380 */ +static int +do_execve(td, args, mac_p) + struct thread *td; + struct image_args *args; + struct mac *mac_p; +{ + +} + +static LIST_HEAD(, alq) ald_active; +static int ald_shuttingdown = 0; +struct thread *ald_thread; + + diff --git a/usr.bin/indent/list_head.0.stdout b/usr.bin/indent/list_head.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/list_head.0.stdout @@ -0,0 +1,15 @@ +/* $NetBSD: list_head.0.stdout,v 1.1 2019/04/04 15:27:35 kamil Exp $ */ +/* $FreeBSD: head/usr.bin/indent/tests/list_head.0.stdout 313544 2017-02-10 09:31:39Z pstef $ */ +/* See r309380 */ +static int +do_execve(td, args, mac_p) + struct thread *td; + struct image_args *args; + struct mac *mac_p; +{ + +} + +static LIST_HEAD(, alq) ald_active; +static int ald_shuttingdown = 0; +struct thread *ald_thread; diff --git a/usr.bin/indent/ncs.0 b/usr.bin/indent/ncs.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/ncs.0 @@ -0,0 +1,5 @@ +/* $NetBSD: ncs.0,v 1.1 2019/04/04 15:27:35 kamil Exp $ */ +/* $FreeBSD: head/usr.bin/indent/tests/ncs.0 334944 2018-06-11 05:35:57Z pstef $ */ +void t(void) { + int a = (double) 8; +} diff --git a/usr.bin/indent/ncs.0.pro b/usr.bin/indent/ncs.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/ncs.0.pro @@ -0,0 +1,3 @@ +/* $NetBSD: ncs.0.pro,v 1.1 2019/04/04 15:27:35 kamil Exp $ */ +/* $FreeBSD: head/usr.bin/indent/tests/ncs.0.pro 334944 2018-06-11 05:35:57Z pstef $ */ +-ncs diff --git a/usr.bin/indent/ncs.0.stdout b/usr.bin/indent/ncs.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/ncs.0.stdout @@ -0,0 +1,7 @@ +/* $NetBSD: ncs.0.stdout,v 1.1 2019/04/04 15:27:35 kamil Exp $ */ +/* $FreeBSD: head/usr.bin/indent/tests/ncs.0.stdout 334944 2018-06-11 05:35:57Z pstef $ */ +void +t(void) +{ + int a = (double)8; +} diff --git a/usr.bin/indent/offsetof.0 b/usr.bin/indent/offsetof.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/offsetof.0 @@ -0,0 +1,6 @@ +/* $NetBSD: offsetof.0,v 1.1 2019/04/04 15:27:35 kamil Exp $ */ +/* $FreeBSD: head/usr.bin/indent/tests/offsetof.0 313544 2017-02-10 09:31:39Z pstef $ */ +/* See r303718 */ +void t(void) { + int n = malloc(offsetof(struct s, f) + 1); +} diff --git a/usr.bin/indent/offsetof.0.stdout b/usr.bin/indent/offsetof.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/offsetof.0.stdout @@ -0,0 +1,8 @@ +/* $NetBSD: offsetof.0.stdout,v 1.1 2019/04/04 15:27:35 kamil Exp $ */ +/* $FreeBSD: head/usr.bin/indent/tests/offsetof.0.stdout 321381 2017-07-23 14:04:45Z pstef $ */ +/* See r303718 */ +void +t(void) +{ + int n = malloc(offsetof(struct s, f) + 1); +} diff --git a/usr.bin/indent/opt--version.0 b/usr.bin/indent/opt--version.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt--version.0 @@ -0,0 +1,6 @@ +/* $NetBSD: opt--version.0,v 1.2 2021/03/06 19:30:44 rillig Exp $ */ +/* $FreeBSD$ */ + +When the option '--version' is given, any other options are ignored. +Therefore the source file, if given, can contain arbitrary text that +even might generate syntax errors when given to a C compiler. diff --git a/usr.bin/indent/opt--version.0.pro b/usr.bin/indent/opt--version.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt--version.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt--version.0.pro,v 1.1 2021/03/06 17:56:33 rillig Exp $ */ +/* $FreeBSD$ */ + +--version diff --git a/usr.bin/indent/opt--version.0.stdout b/usr.bin/indent/opt--version.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt--version.0.stdout @@ -0,0 +1 @@ +FreeBSD indent 2.0 diff --git a/usr.bin/indent/opt-P.0 b/usr.bin/indent/opt-P.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-P.0 @@ -0,0 +1,8 @@ +/* $NetBSD: opt-P.0,v 1.2 2021/03/07 10:12:18 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Mentioning another profile via -P has no effect since only a single + * profile can be specified on the command line, and there is no 'include' + * option. + */ diff --git a/usr.bin/indent/opt-P.0.pro b/usr.bin/indent/opt-P.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-P.0.pro @@ -0,0 +1,10 @@ +/* $NetBSD: opt-P.0.pro,v 1.1 2021/03/06 17:56:33 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * It's syntactically possible to specify a profile file inside another + * profile file. Such a profile file is ignored since only a single + * profile file is ever loaded. + */ + +-P/nonexistent diff --git a/usr.bin/indent/opt-P.0.stdout b/usr.bin/indent/opt-P.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-P.0.stdout @@ -0,0 +1,7 @@ +/* $NetBSD: opt-P.0.stdout,v 1.3 2021/03/14 01:34:13 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Mentioning another profile via -P has no effect since only a single profile + * can be specified on the command line, and there is no 'include' option. + */ diff --git a/usr.bin/indent/opt-T.0 b/usr.bin/indent/opt-T.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-T.0 @@ -0,0 +1,13 @@ +/* $NetBSD: opt-T.0,v 1.2 2021/03/07 10:12:18 rillig Exp $ */ +/* $FreeBSD$ */ + +void +example(void *arg) +{ + int cast = (custom_type_name) * arg; + + int mult = (unknown_type_name) * arg; + + /* See the option -ta for handling these types. */ + int suff = (unknown_type_name_t) * arg; +} diff --git a/usr.bin/indent/opt-T.0.pro b/usr.bin/indent/opt-T.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-T.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-T.0.pro,v 1.1 2021/03/06 17:56:33 rillig Exp $ */ +/* $FreeBSD$ */ + +-Tcustom_type_name diff --git a/usr.bin/indent/opt-T.0.stdout b/usr.bin/indent/opt-T.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-T.0.stdout @@ -0,0 +1,13 @@ +/* $NetBSD: opt-T.0.stdout,v 1.2 2021/03/07 10:12:18 rillig Exp $ */ +/* $FreeBSD$ */ + +void +example(void *arg) +{ + int cast = (custom_type_name)*arg; + + int mult = (unknown_type_name) * arg; + + /* See the option -ta for handling these types. */ + int suff = (unknown_type_name_t) * arg; +} diff --git a/usr.bin/indent/opt-U.0 b/usr.bin/indent/opt-U.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-U.0 @@ -0,0 +1,10 @@ +/* $NetBSD: opt-U.0,v 1.2 2021/03/07 10:12:18 rillig Exp $ */ +/* $FreeBSD$ */ + +void +example(void *arg) +{ + int known_1 = (size_t) * arg; + int known_2 = (off_t) * arg; + int ignored = (ignored_t) * arg; +} diff --git a/usr.bin/indent/opt-U.0.list b/usr.bin/indent/opt-U.0.list new file mode 100755 --- /dev/null +++ b/usr.bin/indent/opt-U.0.list @@ -0,0 +1,17 @@ +/* $NetBSD: opt-U.0.list,v 1.1 2021/03/06 17:56:33 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * From each line of this file, the first word is taken to be a type name. + * + * Since neither '/*' nor '' are syntactically valid type names, this means + * that comments like this one are effectively ignored. When a type name is + * indented by whitespace, it is ignored as well. + * + * Since only the first word of each line is relevant, any remaining words + * can be used for comments. + */ + +size_t from stddef.h +off_t for file offsets + ignored_t is ignored since it is indented diff --git a/usr.bin/indent/opt-U.0.pro b/usr.bin/indent/opt-U.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-U.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-U.0.pro,v 1.1 2021/03/06 17:56:33 rillig Exp $ */ +/* $FreeBSD$ */ + +-Uopt-U.0.list diff --git a/usr.bin/indent/opt-U.0.stdout b/usr.bin/indent/opt-U.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-U.0.stdout @@ -0,0 +1,10 @@ +/* $NetBSD: opt-U.0.stdout,v 1.2 2021/03/07 10:12:18 rillig Exp $ */ +/* $FreeBSD$ */ + +void +example(void *arg) +{ + int known_1 = (size_t)*arg; + int known_2 = (off_t)*arg; + int ignored = (ignored_t) * arg; +} diff --git a/usr.bin/indent/opt-bacc.0 b/usr.bin/indent/opt-bacc.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-bacc.0 @@ -0,0 +1,19 @@ +/* $NetBSD: opt-bacc.0,v 1.2 2021/03/06 19:30:44 rillig Exp $ */ +/* $FreeBSD$ */ + +int a; +#if 0 /* FIXME: need blank line above */ +int b; +#endif /* FIXME: need blank line below */ +int c; + + +int space_a; + +#if 0 /* FIXME: need blank line above */ + +int space_b; /* FIXME: need no blank line above */ + +#endif + +int space_c; diff --git a/usr.bin/indent/opt-bacc.0.pro b/usr.bin/indent/opt-bacc.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-bacc.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-bacc.0.pro,v 1.1 2021/03/06 17:56:33 rillig Exp $ */ +/* $FreeBSD$ */ + +-bacc diff --git a/usr.bin/indent/opt-bacc.0.stdout b/usr.bin/indent/opt-bacc.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-bacc.0.stdout @@ -0,0 +1,17 @@ +/* $NetBSD: opt-bacc.0.stdout,v 1.2 2021/03/06 19:30:44 rillig Exp $ */ +/* $FreeBSD$ */ + +int a; +#if 0 /* FIXME: need blank line above */ +int b; +#endif /* FIXME: need blank line below */ +int c; + + +int space_a; +#if 0 /* FIXME: need blank line above */ + +int space_b; /* FIXME: need no blank line above */ +#endif + +int space_c; diff --git a/usr.bin/indent/opt-bad.0 b/usr.bin/indent/opt-bad.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-bad.0 @@ -0,0 +1,18 @@ +/* $NetBSD: opt-bad.0,v 1.2 2021/03/06 19:30:44 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * The option -bad only affects declarations of local variables. It does not + * affect file-scoped declarations or definitions. + */ + +int global_variable; +void function_declaration(void); +#if 0 +#endif +void function_definition(void) { + int local_variable; + function_call(); + int local_variable_after_statement; + function_call(); +} diff --git a/usr.bin/indent/opt-bad.0.pro b/usr.bin/indent/opt-bad.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-bad.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-bad.0.pro,v 1.1 2021/03/06 17:56:33 rillig Exp $ */ +/* $FreeBSD$ */ + +-bad diff --git a/usr.bin/indent/opt-bad.0.stdout b/usr.bin/indent/opt-bad.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-bad.0.stdout @@ -0,0 +1,22 @@ +/* $NetBSD: opt-bad.0.stdout,v 1.2 2021/03/06 19:30:44 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * The option -bad only affects declarations of local variables. It does not + * affect file-scoped declarations or definitions. + */ + +int global_variable; +void function_declaration(void); +#if 0 +#endif +void +function_definition(void) +{ + int local_variable; + + function_call(); + int local_variable_after_statement; + + function_call(); +} diff --git a/usr.bin/indent/opt-badp.0 b/usr.bin/indent/opt-badp.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-badp.0 @@ -0,0 +1,33 @@ +/* $NetBSD: opt-badp.0,v 1.2 2021/03/06 19:30:44 rillig Exp $ */ +/* $FreeBSD$ */ + +static void +no_declarations(void) +{ + action(); +} + +static void +declarations_without_blank_line(void) +{ + int local_variable; + action(); /* FIXME: need empty line above */ +} + +static void +declaration_with_blank_line(void) +{ + int local_variable; + + action(); +} + +static void +declaration_with_several_blank_lines(void) +{ + int local_variable; + + + + action(); +} diff --git a/usr.bin/indent/opt-badp.0.pro b/usr.bin/indent/opt-badp.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-badp.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-badp.0.pro,v 1.1 2021/03/06 17:56:33 rillig Exp $ */ +/* $FreeBSD$ */ + +-badp diff --git a/usr.bin/indent/opt-badp.0.stdout b/usr.bin/indent/opt-badp.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-badp.0.stdout @@ -0,0 +1,34 @@ +/* $NetBSD: opt-badp.0.stdout,v 1.2 2021/03/06 19:30:44 rillig Exp $ */ +/* $FreeBSD$ */ + +static void +no_declarations(void) +{ + + action(); +} + +static void +declarations_without_blank_line(void) +{ + int local_variable; + action(); /* FIXME: need empty line above */ +} + +static void +declaration_with_blank_line(void) +{ + int local_variable; + + action(); +} + +static void +declaration_with_several_blank_lines(void) +{ + int local_variable; + + + + action(); +} diff --git a/usr.bin/indent/opt-bap+sob.0 b/usr.bin/indent/opt-bap+sob.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-bap+sob.0 @@ -0,0 +1,20 @@ +/* $NetBSD: opt-bap+sob.0,v 1.1 2021/03/08 22:13:05 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * As of 2021-03-08, the combination of -bap and -sob, which occurs in the + * example indent.pro from NetBSD, removes the empty line above the + * separator. Seen in games/cgram/cgram.c. + */ + +void +function1(void) +{ +} + +///// separator ///// + +void +function2(void) +{ +} diff --git a/usr.bin/indent/opt-bap+sob.0.pro b/usr.bin/indent/opt-bap+sob.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-bap+sob.0.pro @@ -0,0 +1,5 @@ +/* $NetBSD: opt-bap+sob.0.pro,v 1.1 2021/03/08 22:13:05 rillig Exp $ */ +/* $FreeBSD$ */ + +-bap +-sob diff --git a/usr.bin/indent/opt-bap+sob.0.stdout b/usr.bin/indent/opt-bap+sob.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-bap+sob.0.stdout @@ -0,0 +1,20 @@ +/* $NetBSD: opt-bap+sob.0.stdout,v 1.2 2021/03/14 01:34:13 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * As of 2021-03-08, the combination of -bap and -sob, which occurs in the + * example indent.pro from NetBSD, removes the empty line above the separator. + * Seen in games/cgram/cgram.c. + */ + +void +function1(void) +{ +} +/* $ FIXME: Keep the empty line between the '}' and the '//'. */ +///// separator ///// + +void +function2(void) +{ +} diff --git a/usr.bin/indent/opt-bap.0 b/usr.bin/indent/opt-bap.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-bap.0 @@ -0,0 +1,22 @@ +/* $NetBSD: opt-bap.0,v 1.2 2021/03/06 19:30:44 rillig Exp $ */ +/* $FreeBSD$ */ + +static void one_liner(void) { /* FIXME: needs empty line below */ } +static void several_lines(void) { + action(); + /* FIXME: needs empty line below */ +} +int main(void){/* FIXME: needs empty line below */} +int global_variable; + +void already_has_blank_line_below(void) +{ +} + +void has_several_blank_lines_below(void) +{ +} + + + +int the_end; diff --git a/usr.bin/indent/opt-bap.0.pro b/usr.bin/indent/opt-bap.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-bap.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-bap.0.pro,v 1.1 2021/03/06 17:56:34 rillig Exp $ */ +/* $FreeBSD$ */ + +-bap diff --git a/usr.bin/indent/opt-bap.0.stdout b/usr.bin/indent/opt-bap.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-bap.0.stdout @@ -0,0 +1,32 @@ +/* $NetBSD: opt-bap.0.stdout,v 1.2 2021/03/06 19:30:44 rillig Exp $ */ +/* $FreeBSD$ */ + +static void +one_liner(void) +{ /* FIXME: needs empty line below */ +} +static void +several_lines(void) +{ + action(); + /* FIXME: needs empty line below */ +} +int +main(void) +{ /* FIXME: needs empty line below */ +} +int global_variable; + +void +already_has_blank_line_below(void) +{ +} + +void +has_several_blank_lines_below(void) +{ +} + + + +int the_end; diff --git a/usr.bin/indent/opt-bbb.0 b/usr.bin/indent/opt-bbb.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-bbb.0 @@ -0,0 +1,26 @@ +/* $NetBSD: opt-bbb.0,v 1.2 2021/03/06 19:30:44 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * This is a block comment. + */ +/* This is not a block comment since it is single-line. */ +/* + * This is a second block comment. + */ +/* This is not a block comment. */ +/* + * Documentation of global_variable. + */ +int global_variable; +/* + * Documentation of function_declaration. + */ +void function_declaration(void); +/* + * Documentation of function_definition. + */ +void +function_definition(void) +{ +} diff --git a/usr.bin/indent/opt-bbb.0.pro b/usr.bin/indent/opt-bbb.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-bbb.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-bbb.0.pro,v 1.1 2021/03/06 17:56:34 rillig Exp $ */ +/* $FreeBSD$ */ + +-bbb diff --git a/usr.bin/indent/opt-bbb.0.stdout b/usr.bin/indent/opt-bbb.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-bbb.0.stdout @@ -0,0 +1,30 @@ +/* $NetBSD: opt-bbb.0.stdout,v 1.2 2021/03/06 19:30:44 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * This is a block comment. + */ +/* This is not a block comment since it is single-line. */ + +/* + * This is a second block comment. + */ +/* This is not a block comment. */ + +/* + * Documentation of global_variable. + */ +int global_variable; + +/* + * Documentation of function_declaration. + */ +void function_declaration(void); + +/* + * Documentation of function_definition. + */ +void +function_definition(void) +{ +} diff --git a/usr.bin/indent/opt-bc.0 b/usr.bin/indent/opt-bc.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-bc.0 @@ -0,0 +1,5 @@ +/* $NetBSD: opt-bc.0,v 1.2 2021/03/06 19:30:44 rillig Exp $ */ +/* $FreeBSD$ */ + +int a,b,c; +void function_declaration(int a,int b,int c); diff --git a/usr.bin/indent/opt-bc.0.pro b/usr.bin/indent/opt-bc.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-bc.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-bc.0.pro,v 1.1 2021/03/06 17:56:34 rillig Exp $ */ +/* $FreeBSD$ */ + +-bc diff --git a/usr.bin/indent/opt-bc.0.stdout b/usr.bin/indent/opt-bc.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-bc.0.stdout @@ -0,0 +1,7 @@ +/* $NetBSD: opt-bc.0.stdout,v 1.2 2021/03/06 19:30:44 rillig Exp $ */ +/* $FreeBSD$ */ + +int a, + b, + c; +void function_declaration(int a, int b, int c); diff --git a/usr.bin/indent/opt-bl.0 b/usr.bin/indent/opt-bl.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-bl.0 @@ -0,0 +1,15 @@ +/* $NetBSD: opt-bl.0,v 1.2 2021/03/06 19:30:44 rillig Exp $ */ +/* $FreeBSD$ */ + +void +example(int n) +{ + /* + * XXX: The '} else' looks strange in this style since the 'else' is + * not at the left margin of the code. + */ + if (n > 99) { print("large"); } + else if (n > 9) { print("double-digit"); } + else if (n > 0) print("positive"); + else { print("negative"); } +} diff --git a/usr.bin/indent/opt-bl.0.pro b/usr.bin/indent/opt-bl.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-bl.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-bl.0.pro,v 1.1 2021/03/06 17:56:34 rillig Exp $ */ +/* $FreeBSD$ */ + +-bl diff --git a/usr.bin/indent/opt-bl.0.stdout b/usr.bin/indent/opt-bl.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-bl.0.stdout @@ -0,0 +1,23 @@ +/* $NetBSD: opt-bl.0.stdout,v 1.2 2021/03/06 19:30:44 rillig Exp $ */ +/* $FreeBSD$ */ + +void +example(int n) +{ + /* + * XXX: The '} else' looks strange in this style since the 'else' is + * not at the left margin of the code. + */ + if (n > 99) + { + print("large"); + } else if (n > 9) + { + print("double-digit"); + } else if (n > 0) + print("positive"); + else + { + print("negative"); + } +} diff --git a/usr.bin/indent/opt-br.0 b/usr.bin/indent/opt-br.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-br.0 @@ -0,0 +1,11 @@ +/* $NetBSD: opt-br.0,v 1.2 2021/03/06 19:30:44 rillig Exp $ */ +/* $FreeBSD$ */ + +void +example(int n) +{ + if (n > 99) { print("large"); } + else if (n > 9) { print("double-digit"); } + else if (n > 0) print("positive"); + else { print("negative"); } +} diff --git a/usr.bin/indent/opt-br.0.pro b/usr.bin/indent/opt-br.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-br.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-br.0.pro,v 1.1 2021/03/06 17:56:34 rillig Exp $ */ +/* $FreeBSD$ */ + +-br diff --git a/usr.bin/indent/opt-br.0.stdout b/usr.bin/indent/opt-br.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-br.0.stdout @@ -0,0 +1,16 @@ +/* $NetBSD: opt-br.0.stdout,v 1.2 2021/03/06 19:30:44 rillig Exp $ */ +/* $FreeBSD$ */ + +void +example(int n) +{ + if (n > 99) { + print("large"); + } else if (n > 9) { + print("double-digit"); + } else if (n > 0) + print("positive"); + else { + print("negative"); + } +} diff --git a/usr.bin/indent/opt-bs.0 b/usr.bin/indent/opt-bs.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-bs.0 @@ -0,0 +1,17 @@ +/* $NetBSD: opt-bs.0,v 1.2 2021/03/06 19:30:44 rillig Exp $ */ +/* $FreeBSD$ */ + +void +example(int i) +{ + print(sizeof(i)); + print(sizeof(int)); + + print(sizeof i); + print(sizeof (i)); + print(sizeof (int)); + + print(sizeof i); + print(sizeof (i)); + print(sizeof (int)); +} diff --git a/usr.bin/indent/opt-bs.0.pro b/usr.bin/indent/opt-bs.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-bs.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-bs.0.pro,v 1.1 2021/03/06 17:56:34 rillig Exp $ */ +/* $FreeBSD$ */ + +-bs diff --git a/usr.bin/indent/opt-bs.0.stdout b/usr.bin/indent/opt-bs.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-bs.0.stdout @@ -0,0 +1,17 @@ +/* $NetBSD: opt-bs.0.stdout,v 1.2 2021/03/06 19:30:44 rillig Exp $ */ +/* $FreeBSD$ */ + +void +example(int i) +{ + print(sizeof (i)); + print(sizeof (int)); + + print(sizeof i); + print(sizeof (i)); + print(sizeof (int)); + + print(sizeof i); + print(sizeof (i)); + print(sizeof (int)); +} diff --git a/usr.bin/indent/opt-c.0 b/usr.bin/indent/opt-c.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-c.0 @@ -0,0 +1,12 @@ +/* $NetBSD: opt-c.0,v 1.3 2021/03/06 19:51:24 rillig Exp $ */ +/* $FreeBSD$ */ + +bool +is_prime(int n) +{ + if (n <= 3) + return n >= 2; /* special case */ + if (n % 2 == 0) + return false; /* even numbers */ + return true; +} diff --git a/usr.bin/indent/opt-c.0.pro b/usr.bin/indent/opt-c.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-c.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-c.0.pro,v 1.1 2021/03/06 17:56:34 rillig Exp $ */ +/* $FreeBSD$ */ + +-c49 diff --git a/usr.bin/indent/opt-c.0.stdout b/usr.bin/indent/opt-c.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-c.0.stdout @@ -0,0 +1,12 @@ +/* $NetBSD: opt-c.0.stdout,v 1.2 2021/03/06 19:30:44 rillig Exp $ */ +/* $FreeBSD$ */ + +bool +is_prime(int n) +{ + if (n <= 3) + return n >= 2; /* special case */ + if (n % 2 == 0) + return false; /* even numbers */ + return true; +} diff --git a/usr.bin/indent/opt-cd.0 b/usr.bin/indent/opt-cd.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-cd.0 @@ -0,0 +1,4 @@ +/* $NetBSD: opt-cd.0,v 1.2 2021/03/06 21:27:39 rillig Exp $ */ +/* $FreeBSD$ */ + +int aflag; /* Apply to all files. */ diff --git a/usr.bin/indent/opt-cd.0.pro b/usr.bin/indent/opt-cd.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-cd.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-cd.0.pro,v 1.1 2021/03/06 17:56:34 rillig Exp $ */ +/* $FreeBSD$ */ + +-cd49 diff --git a/usr.bin/indent/opt-cd.0.stdout b/usr.bin/indent/opt-cd.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-cd.0.stdout @@ -0,0 +1,4 @@ +/* $NetBSD: opt-cd.0.stdout,v 1.2 2021/03/06 21:27:39 rillig Exp $ */ +/* $FreeBSD$ */ + +int aflag; /* Apply to all files. */ diff --git a/usr.bin/indent/opt-cdb.0 b/usr.bin/indent/opt-cdb.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-cdb.0 @@ -0,0 +1,45 @@ +/* $NetBSD: opt-cdb.0,v 1.2 2021/03/06 21:27:39 rillig Exp $ */ +/* $FreeBSD$ */ + +/* A single-line comment. */ + +/* A + * multi-line + * comment. */ + +/* + * A + * multi-line + * comment. + */ + +int ga; /* A single-line comment. */ + +int gb; /* A + * multi-line + * comment. */ + +int gc; /* + * A + * multi-line + * comment. + */ + +void +example(void) +{ + /* A single-line comment. */ + int la; + + /* A + * multi-line + * comment. */ + int lb; + + /* + * A + * multi-line + * comment. + */ + int lc; +} diff --git a/usr.bin/indent/opt-cdb.0.pro b/usr.bin/indent/opt-cdb.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-cdb.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-cdb.0.pro,v 1.1 2021/03/06 17:56:34 rillig Exp $ */ +/* $FreeBSD$ */ + +-cdb diff --git a/usr.bin/indent/opt-cdb.0.stdout b/usr.bin/indent/opt-cdb.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-cdb.0.stdout @@ -0,0 +1,35 @@ +/* $NetBSD: opt-cdb.0.stdout,v 1.2 2021/03/06 21:27:39 rillig Exp $ */ +/* $FreeBSD$ */ + +/* A single-line comment. */ + +/* + * A multi-line comment. + */ + +/* + * A multi-line comment. + */ + +int ga; /* A single-line comment. */ + +int gb; /* A multi-line comment. */ + +int gc; /* A multi-line comment. */ + +void +example(void) +{ + /* A single-line comment. */ + int la; + + /* + * A multi-line comment. + */ + int lb; + + /* + * A multi-line comment. + */ + int lc; +} diff --git a/usr.bin/indent/opt-ce.0 b/usr.bin/indent/opt-ce.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-ce.0 @@ -0,0 +1,11 @@ +/* $NetBSD: opt-ce.0,v 1.2 2021/03/06 21:27:39 rillig Exp $ */ +/* $FreeBSD$ */ + +void +example(int n) +{ + if (n > 99) { print("large"); } + else if (n > 9) { print("double-digit"); } + else if (n > 0) print("positive"); + else { print("negative"); } +} diff --git a/usr.bin/indent/opt-ce.0.pro b/usr.bin/indent/opt-ce.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-ce.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-ce.0.pro,v 1.1 2021/03/06 17:56:34 rillig Exp $ */ +/* $FreeBSD$ */ + +-ce diff --git a/usr.bin/indent/opt-ce.0.stdout b/usr.bin/indent/opt-ce.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-ce.0.stdout @@ -0,0 +1,16 @@ +/* $NetBSD: opt-ce.0.stdout,v 1.2 2021/03/06 21:27:39 rillig Exp $ */ +/* $FreeBSD$ */ + +void +example(int n) +{ + if (n > 99) { + print("large"); + } else if (n > 9) { + print("double-digit"); + } else if (n > 0) + print("positive"); + else { + print("negative"); + } +} diff --git a/usr.bin/indent/opt-ci.0 b/usr.bin/indent/opt-ci.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-ci.0 @@ -0,0 +1,9 @@ +/* $NetBSD: opt-ci.0,v 1.2 2021/03/06 21:27:39 rillig Exp $ */ +/* $FreeBSD$ */ + +int +sum(int a, int b) +{ + return a + + b; +} diff --git a/usr.bin/indent/opt-ci.0.pro b/usr.bin/indent/opt-ci.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-ci.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-ci.0.pro,v 1.1 2021/03/06 17:56:34 rillig Exp $ */ +/* $FreeBSD$ */ + +-ci2 diff --git a/usr.bin/indent/opt-ci.0.stdout b/usr.bin/indent/opt-ci.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-ci.0.stdout @@ -0,0 +1,9 @@ +/* $NetBSD: opt-ci.0.stdout,v 1.2 2021/03/06 21:27:39 rillig Exp $ */ +/* $FreeBSD$ */ + +int +sum(int a, int b) +{ + return a + + b; +} diff --git a/usr.bin/indent/opt-cli.0 b/usr.bin/indent/opt-cli.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-cli.0 @@ -0,0 +1,14 @@ +/* $NetBSD: opt-cli.0,v 1.2 2021/03/06 21:27:39 rillig Exp $ */ +/* $FreeBSD$ */ + +void +classify(int n) +{ + switch (n) { + case 0: print("zero"); break; + case 1: print("one"); break; + case 2: case 3: print("prime"); break; + case 4: print("square"); break; + default: print("large"); break; + } +} diff --git a/usr.bin/indent/opt-cli.0.pro b/usr.bin/indent/opt-cli.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-cli.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-cli.0.pro,v 1.1 2021/03/06 17:56:34 rillig Exp $ */ +/* $FreeBSD$ */ + +-cli1.5 diff --git a/usr.bin/indent/opt-cli.0.stdout b/usr.bin/indent/opt-cli.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-cli.0.stdout @@ -0,0 +1,25 @@ +/* $NetBSD: opt-cli.0.stdout,v 1.2 2021/03/06 21:27:39 rillig Exp $ */ +/* $FreeBSD$ */ + +void +classify(int n) +{ + switch (n) { + case 0: + print("zero"); + break; + case 1: + print("one"); + break; + case 2: + case 3: + print("prime"); + break; + case 4: + print("square"); + break; + default: + print("large"); + break; + } +} diff --git a/usr.bin/indent/opt-cs.0 b/usr.bin/indent/opt-cs.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-cs.0 @@ -0,0 +1,6 @@ +/* $NetBSD: opt-cs.0,v 1.2 2021/03/06 21:27:39 rillig Exp $ */ +/* $FreeBSD$ */ + +int i0 = (int)3.0; +int i1 = (int) 3.0; +int i3 = (int) 3.0; diff --git a/usr.bin/indent/opt-cs.0.pro b/usr.bin/indent/opt-cs.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-cs.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-cs.0.pro,v 1.1 2021/03/06 17:56:34 rillig Exp $ */ +/* $FreeBSD$ */ + +-cs diff --git a/usr.bin/indent/opt-cs.0.stdout b/usr.bin/indent/opt-cs.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-cs.0.stdout @@ -0,0 +1,6 @@ +/* $NetBSD: opt-cs.0.stdout,v 1.2 2021/03/06 21:27:39 rillig Exp $ */ +/* $FreeBSD$ */ + +int i0 = (int) 3.0; +int i1 = (int) 3.0; +int i3 = (int) 3.0; diff --git a/usr.bin/indent/opt-d.0 b/usr.bin/indent/opt-d.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-d.0 @@ -0,0 +1,19 @@ +/* $NetBSD: opt-d.0,v 1.2 2021/03/06 21:27:39 rillig Exp $ */ +/* $FreeBSD$ */ + +/* XXX: oops, the comments at level 0 move below the '{' */ +void +example(void) +/* comment at level 0 */ + /* comment at level 0 */ +{ +/* comment at level 1 */ + /* comment at level 1 */ + /* comment at level 1 */ + { +/* comment at level 2 */ + /* comment at level 2 */ + /* comment at level 2 */ + /* comment at level 2 */ + } +} diff --git a/usr.bin/indent/opt-d.0.pro b/usr.bin/indent/opt-d.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-d.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-d.0.pro,v 1.1 2021/03/06 17:56:34 rillig Exp $ */ +/* $FreeBSD$ */ + +-d1 diff --git a/usr.bin/indent/opt-d.0.stdout b/usr.bin/indent/opt-d.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-d.0.stdout @@ -0,0 +1,19 @@ +/* $NetBSD: opt-d.0.stdout,v 1.2 2021/03/06 21:27:39 rillig Exp $ */ +/* $FreeBSD$ */ + +/* XXX: oops, the comments at level 0 move below the '{' */ +void +example(void) +{ +/* comment at level 0 */ +/* comment at level 0 */ +/* comment at level 1 */ +/* comment at level 1 */ +/* comment at level 1 */ + { + /* comment at level 2 */ + /* comment at level 2 */ + /* comment at level 2 */ + /* comment at level 2 */ + } +} diff --git a/usr.bin/indent/opt-di.0 b/usr.bin/indent/opt-di.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-di.0 @@ -0,0 +1,8 @@ +/* $NetBSD: opt-di.0,v 1.2 2021/03/06 21:27:39 rillig Exp $ */ +/* $FreeBSD$ */ + +int space; +int tab; +int tab16; + +struct long_name long_name; diff --git a/usr.bin/indent/opt-di.0.pro b/usr.bin/indent/opt-di.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-di.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-di.0.pro,v 1.1 2021/03/06 17:56:34 rillig Exp $ */ +/* $FreeBSD$ */ + +-di8 diff --git a/usr.bin/indent/opt-di.0.stdout b/usr.bin/indent/opt-di.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-di.0.stdout @@ -0,0 +1,8 @@ +/* $NetBSD: opt-di.0.stdout,v 1.2 2021/03/06 21:27:39 rillig Exp $ */ +/* $FreeBSD$ */ + +int space; +int tab; +int tab16; + +struct long_name long_name; diff --git a/usr.bin/indent/opt-dj.0 b/usr.bin/indent/opt-dj.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-dj.0 @@ -0,0 +1,9 @@ +/* $NetBSD: opt-dj.0,v 1.2 2021/03/06 21:27:39 rillig Exp $ */ +/* $FreeBSD$ */ + +int i; +int *ip; +const char *ccp; +const void *****vppppp; +const void ******vpppppp; +const void ********vpppppppp; diff --git a/usr.bin/indent/opt-dj.0.pro b/usr.bin/indent/opt-dj.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-dj.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-dj.0.pro,v 1.1 2021/03/06 17:56:34 rillig Exp $ */ +/* $FreeBSD$ */ + +-dj diff --git a/usr.bin/indent/opt-dj.0.stdout b/usr.bin/indent/opt-dj.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-dj.0.stdout @@ -0,0 +1,9 @@ +/* $NetBSD: opt-dj.0.stdout,v 1.2 2021/03/06 21:27:39 rillig Exp $ */ +/* $FreeBSD$ */ + +int i; +int *ip; +const char *ccp; +const void *****vppppp; +const void ******vpppppp; +const void ********vpppppppp; diff --git a/usr.bin/indent/opt-eei.0 b/usr.bin/indent/opt-eei.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-eei.0 @@ -0,0 +1,14 @@ +/* $NetBSD: opt-eei.0,v 1.2 2021/03/06 21:27:39 rillig Exp $ */ +/* $FreeBSD$ */ + +bool +less(int a, int b) +{ + if (a < + b) + return true; + if (a + < + b) + return true; +} diff --git a/usr.bin/indent/opt-eei.0.pro b/usr.bin/indent/opt-eei.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-eei.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-eei.0.pro,v 1.1 2021/03/06 17:56:34 rillig Exp $ */ +/* $FreeBSD$ */ + +-eei diff --git a/usr.bin/indent/opt-eei.0.stdout b/usr.bin/indent/opt-eei.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-eei.0.stdout @@ -0,0 +1,14 @@ +/* $NetBSD: opt-eei.0.stdout,v 1.2 2021/03/06 21:27:39 rillig Exp $ */ +/* $FreeBSD$ */ + +bool +less(int a, int b) +{ + if (a < + b) + return true; + if (a + < + b) + return true; +} diff --git a/usr.bin/indent/opt-ei.0 b/usr.bin/indent/opt-ei.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-ei.0 @@ -0,0 +1,16 @@ +/* $NetBSD: opt-ei.0,v 1.2 2021/03/06 21:27:39 rillig Exp $ */ +/* $FreeBSD$ */ + +void +example(int n) +{ + if (n > 99) { + print("large"); + } else if (n > 9) { + print("double-digit"); + } else if (n > 0) + print("positive"); + else { + print("negative"); + } +} diff --git a/usr.bin/indent/opt-ei.0.pro b/usr.bin/indent/opt-ei.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-ei.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-ei.0.pro,v 1.1 2021/03/06 17:56:34 rillig Exp $ */ +/* $FreeBSD$ */ + +-ei diff --git a/usr.bin/indent/opt-ei.0.stdout b/usr.bin/indent/opt-ei.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-ei.0.stdout @@ -0,0 +1,16 @@ +/* $NetBSD: opt-ei.0.stdout,v 1.2 2021/03/06 21:27:39 rillig Exp $ */ +/* $FreeBSD$ */ + +void +example(int n) +{ + if (n > 99) { + print("large"); + } else if (n > 9) { + print("double-digit"); + } else if (n > 0) + print("positive"); + else { + print("negative"); + } +} diff --git a/usr.bin/indent/opt-fbs.0 b/usr.bin/indent/opt-fbs.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-fbs.0 @@ -0,0 +1,4 @@ +/* $NetBSD: opt-fbs.0,v 1.2 2021/03/06 21:27:39 rillig Exp $ */ +/* $FreeBSD$ */ + +void example(int n) {} diff --git a/usr.bin/indent/opt-fbs.0.pro b/usr.bin/indent/opt-fbs.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-fbs.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-fbs.0.pro,v 1.1 2021/03/06 17:56:34 rillig Exp $ */ +/* $FreeBSD$ */ + +-fbs diff --git a/usr.bin/indent/opt-fbs.0.stdout b/usr.bin/indent/opt-fbs.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-fbs.0.stdout @@ -0,0 +1,7 @@ +/* $NetBSD: opt-fbs.0.stdout,v 1.2 2021/03/06 21:27:39 rillig Exp $ */ +/* $FreeBSD$ */ + +void +example(int n) +{ +} diff --git a/usr.bin/indent/opt-fc1.0 b/usr.bin/indent/opt-fc1.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-fc1.0 @@ -0,0 +1,10 @@ +/* $NetBSD: opt-fc1.0,v 1.2 2021/03/06 21:27:39 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * A comment + * in column 1. + * + * + * + */ diff --git a/usr.bin/indent/opt-fc1.0.pro b/usr.bin/indent/opt-fc1.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-fc1.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-fc1.0.pro,v 1.1 2021/03/06 17:56:34 rillig Exp $ */ +/* $FreeBSD$ */ + +-fc1 diff --git a/usr.bin/indent/opt-fc1.0.stdout b/usr.bin/indent/opt-fc1.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-fc1.0.stdout @@ -0,0 +1,9 @@ +/* $NetBSD: opt-fc1.0.stdout,v 1.2 2021/03/06 21:27:39 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * A comment in column 1. + * + * + * + */ diff --git a/usr.bin/indent/opt-fcb.0 b/usr.bin/indent/opt-fcb.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-fcb.0 @@ -0,0 +1,32 @@ +/* $NetBSD: opt-fcb.0,v 1.2 2021/03/06 22:10:40 rillig Exp $ */ +/* $FreeBSD$ */ + +/* FIXME: The options -fcb and -nfcb result in exactly the same output. */ + +/* Not + * + * so carefully + * formatted + * comment */ + +/*- + * car mat men + * efu for ted com t + * lly box . + */ + +void +example(void) +{ + /* Not + * + * so carefully + * formatted + * comment */ + + /*- + * car mat men + * efu for ted com t + * lly box . + */ +} diff --git a/usr.bin/indent/opt-fcb.0.pro b/usr.bin/indent/opt-fcb.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-fcb.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-fcb.0.pro,v 1.1 2021/03/06 17:56:34 rillig Exp $ */ +/* $FreeBSD$ */ + +-fcb diff --git a/usr.bin/indent/opt-fcb.0.stdout b/usr.bin/indent/opt-fcb.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-fcb.0.stdout @@ -0,0 +1,32 @@ +/* $NetBSD: opt-fcb.0.stdout,v 1.2 2021/03/06 22:10:40 rillig Exp $ */ +/* $FreeBSD$ */ + +/* FIXME: The options -fcb and -nfcb result in exactly the same output. */ + +/* + * Not + * + * so carefully formatted comment + */ + +/*- + * car mat men + * efu for ted com t + * lly box . + */ + +void +example(void) +{ + /* + * Not + * + * so carefully formatted comment + */ + + /*- + * car mat men + * efu for ted com t + * lly box . + */ +} diff --git a/usr.bin/indent/opt-i.0 b/usr.bin/indent/opt-i.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-i.0 @@ -0,0 +1,8 @@ +/* $NetBSD: opt-i.0,v 1.2 2021/03/06 22:10:40 rillig Exp $ */ +/* $FreeBSD$ */ + +void +example(void) +{ + if (1 > 0) if (2 > 1) return yes; return no; +} diff --git a/usr.bin/indent/opt-i.0.pro b/usr.bin/indent/opt-i.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-i.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-i.0.pro,v 1.1 2021/03/06 17:56:34 rillig Exp $ */ +/* $FreeBSD$ */ + +-i3 diff --git a/usr.bin/indent/opt-i.0.stdout b/usr.bin/indent/opt-i.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-i.0.stdout @@ -0,0 +1,11 @@ +/* $NetBSD: opt-i.0.stdout,v 1.2 2021/03/06 22:10:40 rillig Exp $ */ +/* $FreeBSD$ */ + +void +example(void) +{ + if (1 > 0) + if (2 > 1) + return yes; + return no; +} diff --git a/usr.bin/indent/opt-ip.0 b/usr.bin/indent/opt-ip.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-ip.0 @@ -0,0 +1,21 @@ +/* $NetBSD: opt-ip.0,v 1.2 2021/03/06 22:10:40 rillig Exp $ */ +/* $FreeBSD$ */ + +/* FIXME: The options -ip and -nip produce the same output. */ + +int +several_parameters_1(int a, +int b, +const char *cp); + +int +several_parameters_2( +int a, +int b, +const char *cp); + +int +several_parameters_3( +int a1, int a2, +int b1, int b2, +const char *cp); diff --git a/usr.bin/indent/opt-ip.0.pro b/usr.bin/indent/opt-ip.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-ip.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-ip.0.pro,v 1.1 2021/03/06 17:56:34 rillig Exp $ */ +/* $FreeBSD$ */ + +-ip diff --git a/usr.bin/indent/opt-ip.0.stdout b/usr.bin/indent/opt-ip.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-ip.0.stdout @@ -0,0 +1,21 @@ +/* $NetBSD: opt-ip.0.stdout,v 1.2 2021/03/06 22:10:40 rillig Exp $ */ +/* $FreeBSD$ */ + +/* FIXME: The options -ip and -nip produce the same output. */ + +int +several_parameters_1(int a, + int b, + const char *cp); + +int +several_parameters_2( + int a, + int b, + const char *cp); + +int +several_parameters_3( + int a1, int a2, + int b1, int b2, + const char *cp); diff --git a/usr.bin/indent/opt-l.0 b/usr.bin/indent/opt-l.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-l.0 @@ -0,0 +1,21 @@ +/* $NetBSD: opt-l.0,v 1.2 2021/03/06 22:10:40 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * FIXME: Even though the line length is limited with -l38, + * the overly long lines in the code are not broken. + */ + +/* FIXME: The options -l and -lc produce the same output. */ + +void +example(int a, int b, int c, const char *cp) +{ + for (const char *p = cp; *p != '\0'; p++) + if (*p > a) + if (*p > b) + if (*p > c) + return; + + function(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); +} diff --git a/usr.bin/indent/opt-l.0.pro b/usr.bin/indent/opt-l.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-l.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-l.0.pro,v 1.1 2021/03/06 17:56:34 rillig Exp $ */ +/* $FreeBSD$ */ + +-l38 diff --git a/usr.bin/indent/opt-l.0.stdout b/usr.bin/indent/opt-l.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-l.0.stdout @@ -0,0 +1,26 @@ +/* $NetBSD: opt-l.0.stdout,v 1.2 2021/03/06 22:10:40 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * FIXME: Even though the line length + * is limited with -l38, the overly + * long lines in the code are not + * broken. + */ + +/* + * FIXME: The options -l and -lc + * produce the same output. + */ + +void +example(int a, int b, int c, const char *cp) +{ + for (const char *p = cp; *p != '\0'; p++) + if (*p > a) + if (*p > b) + if (*p > c) + return; + + function(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); +} diff --git a/usr.bin/indent/opt-lc.0 b/usr.bin/indent/opt-lc.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-lc.0 @@ -0,0 +1,19 @@ +/* $NetBSD: opt-lc.0,v 1.2 2021/03/06 22:10:40 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * FIXME: Even though the line length is limited with -l38, + * the overly long lines in the code are not broken. + */ + +void +example(int a, int b, int c, const char *cp) +{ + for (const char *p = cp; *p != '\0'; p++) + if (*p > a) + if (*p > b) + if (*p > c) + return; + + function(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); +} diff --git a/usr.bin/indent/opt-lc.0.pro b/usr.bin/indent/opt-lc.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-lc.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-lc.0.pro,v 1.1 2021/03/06 17:56:34 rillig Exp $ */ +/* $FreeBSD$ */ + +-lc38 diff --git a/usr.bin/indent/opt-lc.0.stdout b/usr.bin/indent/opt-lc.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-lc.0.stdout @@ -0,0 +1,21 @@ +/* $NetBSD: opt-lc.0.stdout,v 1.2 2021/03/06 22:10:40 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * FIXME: Even though the line length + * is limited with -l38, the overly + * long lines in the code are not + * broken. + */ + +void +example(int a, int b, int c, const char *cp) +{ + for (const char *p = cp; *p != '\0'; p++) + if (*p > a) + if (*p > b) + if (*p > c) + return; + + function(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); +} diff --git a/usr.bin/indent/opt-ldi.0 b/usr.bin/indent/opt-ldi.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-ldi.0 @@ -0,0 +1,11 @@ +/* $NetBSD: opt-ldi.0,v 1.2 2021/03/06 22:10:40 rillig Exp $ */ +/* $FreeBSD$ */ + +/* FIXME: Why does -ldi affect global variables? */ +int global; + +void +function(void) +{ + int local; +} diff --git a/usr.bin/indent/opt-ldi.0.pro b/usr.bin/indent/opt-ldi.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-ldi.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-ldi.0.pro,v 1.1 2021/03/06 17:56:34 rillig Exp $ */ +/* $FreeBSD$ */ + +-ldi8 diff --git a/usr.bin/indent/opt-ldi.0.stdout b/usr.bin/indent/opt-ldi.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-ldi.0.stdout @@ -0,0 +1,11 @@ +/* $NetBSD: opt-ldi.0.stdout,v 1.2 2021/03/06 22:10:40 rillig Exp $ */ +/* $FreeBSD$ */ + +/* FIXME: Why does -ldi affect global variables? */ +int global; + +void +function(void) +{ + int local; +} diff --git a/usr.bin/indent/opt-lp.0 b/usr.bin/indent/opt-lp.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-lp.0 @@ -0,0 +1,14 @@ +/* $NetBSD: opt-lp.0,v 1.2 2021/03/06 22:10:40 rillig Exp $ */ +/* $FreeBSD$ */ + +void +example(void) +{ + p1 = first_procedure(second_procedure(p2, p3), + third_procedure(p4, p5)); + + p1 = first_procedure(second_procedure(p2, + p3), + third_procedure(p4, + p5)); +} diff --git a/usr.bin/indent/opt-lp.0.pro b/usr.bin/indent/opt-lp.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-lp.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-lp.0.pro,v 1.1 2021/03/06 17:56:34 rillig Exp $ */ +/* $FreeBSD$ */ + +-lp diff --git a/usr.bin/indent/opt-lp.0.stdout b/usr.bin/indent/opt-lp.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-lp.0.stdout @@ -0,0 +1,14 @@ +/* $NetBSD: opt-lp.0.stdout,v 1.2 2021/03/06 22:10:40 rillig Exp $ */ +/* $FreeBSD$ */ + +void +example(void) +{ + p1 = first_procedure(second_procedure(p2, p3), + third_procedure(p4, p5)); + + p1 = first_procedure(second_procedure(p2, + p3), + third_procedure(p4, + p5)); +} diff --git a/usr.bin/indent/opt-lpl.0 b/usr.bin/indent/opt-lpl.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-lpl.0 @@ -0,0 +1,24 @@ +/* $NetBSD: opt-lpl.0,v 1.2 2021/03/07 10:12:18 rillig Exp $ */ +/* $FreeBSD$ */ + +void +example(void) +{ + int sum1 = 1+2+3+4+5+6+7+8+9+10+11+12+13+14+15+16+17+18+19+20+21; + int sum2 = (1+2+3+4+5+6+7+8+9+10+11+12+13+14+15+16+17+18+19+20+21); + + int sum3 = 1+2+3+4+5+ + 6+7+8+9+10+ + 11+12+13+14+15+ + 16+17+18+19+20+ + 21; + int sum4 = (1+2+3+4+5+ + 6+7+8+9+10+ + 11+12+13+14+15+ + 16+17+18+19+20+ + 21); + + call_function(call_function(call_function(call_function(call_function(call_function()))))); + + call_function((call_function(call_function(call_function(call_function(call_function())))))); +} diff --git a/usr.bin/indent/opt-lpl.0.pro b/usr.bin/indent/opt-lpl.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-lpl.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-lpl.0.pro,v 1.1 2021/03/06 17:56:34 rillig Exp $ */ +/* $FreeBSD$ */ + +-lpl diff --git a/usr.bin/indent/opt-lpl.0.stdout b/usr.bin/indent/opt-lpl.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-lpl.0.stdout @@ -0,0 +1,27 @@ +/* $NetBSD: opt-lpl.0.stdout,v 1.2 2021/03/07 10:12:18 rillig Exp $ */ +/* $FreeBSD$ */ + +/* $ TODO: Add code that differs between -lpl and -nlpl. */ +void +example(void) +{ + int sum1 = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 + 21; + int sum2 = (1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 + 21); + +/* $ XXX: There should be at least _some_ indentation for the */ +/* $ continuation lines. */ + int sum3 = 1 + 2 + 3 + 4 + 5 + + 6 + 7 + 8 + 9 + 10 + + 11 + 12 + 13 + 14 + 15 + + 16 + 17 + 18 + 19 + 20 + + 21; + int sum4 = (1 + 2 + 3 + 4 + 5 + + 6 + 7 + 8 + 9 + 10 + + 11 + 12 + 13 + 14 + 15 + + 16 + 17 + 18 + 19 + 20 + + 21); + + call_function(call_function(call_function(call_function(call_function(call_function()))))); + + call_function((call_function(call_function(call_function(call_function(call_function())))))); +} diff --git a/usr.bin/indent/opt-nbacc.0 b/usr.bin/indent/opt-nbacc.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-nbacc.0 @@ -0,0 +1,19 @@ +/* $NetBSD: opt-nbacc.0,v 1.2 2021/03/06 23:09:17 rillig Exp $ */ +/* $FreeBSD$ */ + +int a; +#if 0 +int b; +#endif +int c; + + +int space_a; + +#if 0 + +int space_b; + +#endif + +int space_c; diff --git a/usr.bin/indent/opt-nbacc.0.pro b/usr.bin/indent/opt-nbacc.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-nbacc.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-nbacc.0.pro,v 1.1 2021/03/06 17:56:34 rillig Exp $ */ +/* $FreeBSD$ */ + +-nbacc diff --git a/usr.bin/indent/opt-nbacc.0.stdout b/usr.bin/indent/opt-nbacc.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-nbacc.0.stdout @@ -0,0 +1,19 @@ +/* $NetBSD: opt-nbacc.0.stdout,v 1.2 2021/03/06 23:09:17 rillig Exp $ */ +/* $FreeBSD$ */ + +int a; +#if 0 +int b; +#endif +int c; + + +int space_a; + +#if 0 + +int space_b; + +#endif + +int space_c; diff --git a/usr.bin/indent/opt-nbad.0 b/usr.bin/indent/opt-nbad.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-nbad.0 @@ -0,0 +1,13 @@ +/* $NetBSD: opt-nbad.0,v 1.2 2021/03/06 23:09:17 rillig Exp $ */ +/* $FreeBSD$ */ + +int global_variable; +void function_declaration(void); +#if 0 +#endif +void function_definition(void) { + int local_variable; + function_call(); + int local_variable_after_statement; + function_call(); +} diff --git a/usr.bin/indent/opt-nbad.0.pro b/usr.bin/indent/opt-nbad.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-nbad.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-nbad.0.pro,v 1.1 2021/03/06 17:56:34 rillig Exp $ */ +/* $FreeBSD$ */ + +-nbad diff --git a/usr.bin/indent/opt-nbad.0.stdout b/usr.bin/indent/opt-nbad.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-nbad.0.stdout @@ -0,0 +1,15 @@ +/* $NetBSD: opt-nbad.0.stdout,v 1.2 2021/03/06 23:09:17 rillig Exp $ */ +/* $FreeBSD$ */ + +int global_variable; +void function_declaration(void); +#if 0 +#endif +void +function_definition(void) +{ + int local_variable; + function_call(); + int local_variable_after_statement; + function_call(); +} diff --git a/usr.bin/indent/opt-nbadp.0 b/usr.bin/indent/opt-nbadp.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-nbadp.0 @@ -0,0 +1,33 @@ +/* $NetBSD: opt-nbadp.0,v 1.2 2021/03/06 23:09:17 rillig Exp $ */ +/* $FreeBSD$ */ + +static void +no_declarations(void) +{ + action(); +} + +static void +declarations_without_blank_line(void) +{ + int local_variable; + action(); +} + +static void +declaration_with_blank_line(void) +{ + int local_variable; + + action(); +} + +static void +declaration_with_several_blank_lines(void) +{ + int local_variable; + + + + action(); +} diff --git a/usr.bin/indent/opt-nbadp.0.pro b/usr.bin/indent/opt-nbadp.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-nbadp.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-nbadp.0.pro,v 1.1 2021/03/06 17:56:34 rillig Exp $ */ +/* $FreeBSD$ */ + +-nbadp diff --git a/usr.bin/indent/opt-nbadp.0.stdout b/usr.bin/indent/opt-nbadp.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-nbadp.0.stdout @@ -0,0 +1,33 @@ +/* $NetBSD: opt-nbadp.0.stdout,v 1.2 2021/03/06 23:09:17 rillig Exp $ */ +/* $FreeBSD$ */ + +static void +no_declarations(void) +{ + action(); +} + +static void +declarations_without_blank_line(void) +{ + int local_variable; + action(); +} + +static void +declaration_with_blank_line(void) +{ + int local_variable; + + action(); +} + +static void +declaration_with_several_blank_lines(void) +{ + int local_variable; + + + + action(); +} diff --git a/usr.bin/indent/opt-nbap.0 b/usr.bin/indent/opt-nbap.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-nbap.0 @@ -0,0 +1,21 @@ +/* $NetBSD: opt-nbap.0,v 1.2 2021/03/06 23:09:17 rillig Exp $ */ +/* $FreeBSD$ */ + +static void one_liner(void) {} +static void several_lines(void) { + action(); +} +int main(void){} +int global_variable; + +void already_has_blank_line_below(void) +{ +} + +void has_several_blank_lines_below(void) +{ +} + + + +int the_end; diff --git a/usr.bin/indent/opt-nbap.0.pro b/usr.bin/indent/opt-nbap.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-nbap.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-nbap.0.pro,v 1.1 2021/03/06 17:56:34 rillig Exp $ */ +/* $FreeBSD$ */ + +-nbap diff --git a/usr.bin/indent/opt-nbap.0.stdout b/usr.bin/indent/opt-nbap.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-nbap.0.stdout @@ -0,0 +1,31 @@ +/* $NetBSD: opt-nbap.0.stdout,v 1.2 2021/03/06 23:09:17 rillig Exp $ */ +/* $FreeBSD$ */ + +static void +one_liner(void) +{ +} +static void +several_lines(void) +{ + action(); +} +int +main(void) +{ +} +int global_variable; + +void +already_has_blank_line_below(void) +{ +} + +void +has_several_blank_lines_below(void) +{ +} + + + +int the_end; diff --git a/usr.bin/indent/opt-nbbb.0 b/usr.bin/indent/opt-nbbb.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-nbbb.0 @@ -0,0 +1,26 @@ +/* $NetBSD: opt-nbbb.0,v 1.2 2021/03/06 23:09:17 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * This is a block comment. + */ +/* This is not a block comment since it is single-line. */ +/* + * This is a second block comment. + */ +/* This is not a block comment. */ +/* + * Documentation of global_variable. + */ +int global_variable; +/* + * Documentation of function_declaration. + */ +void function_declaration(void); +/* + * Documentation of function_definition. + */ +void +function_definition(void) +{ +} diff --git a/usr.bin/indent/opt-nbbb.0.pro b/usr.bin/indent/opt-nbbb.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-nbbb.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-nbbb.0.pro,v 1.1 2021/03/06 17:56:34 rillig Exp $ */ +/* $FreeBSD$ */ + +-nbbb diff --git a/usr.bin/indent/opt-nbbb.0.stdout b/usr.bin/indent/opt-nbbb.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-nbbb.0.stdout @@ -0,0 +1,26 @@ +/* $NetBSD: opt-nbbb.0.stdout,v 1.2 2021/03/06 23:09:17 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * This is a block comment. + */ +/* This is not a block comment since it is single-line. */ +/* + * This is a second block comment. + */ +/* This is not a block comment. */ +/* + * Documentation of global_variable. + */ +int global_variable; +/* + * Documentation of function_declaration. + */ +void function_declaration(void); +/* + * Documentation of function_definition. + */ +void +function_definition(void) +{ +} diff --git a/usr.bin/indent/opt-nbc.0 b/usr.bin/indent/opt-nbc.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-nbc.0 @@ -0,0 +1,5 @@ +/* $NetBSD: opt-nbc.0,v 1.2 2021/03/06 23:09:17 rillig Exp $ */ +/* $FreeBSD$ */ + +int a,b,c; +void function_declaration(int a,int b,int c); diff --git a/usr.bin/indent/opt-nbc.0.pro b/usr.bin/indent/opt-nbc.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-nbc.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-nbc.0.pro,v 1.1 2021/03/06 17:56:34 rillig Exp $ */ +/* $FreeBSD$ */ + +-nbc diff --git a/usr.bin/indent/opt-nbc.0.stdout b/usr.bin/indent/opt-nbc.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-nbc.0.stdout @@ -0,0 +1,5 @@ +/* $NetBSD: opt-nbc.0.stdout,v 1.2 2021/03/06 23:09:17 rillig Exp $ */ +/* $FreeBSD$ */ + +int a, b, c; +void function_declaration(int a, int b, int c); diff --git a/usr.bin/indent/opt-nbs.0 b/usr.bin/indent/opt-nbs.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-nbs.0 @@ -0,0 +1,17 @@ +/* $NetBSD: opt-nbs.0,v 1.2 2021/03/06 23:09:17 rillig Exp $ */ +/* $FreeBSD$ */ + +void +example(int i) +{ + print(sizeof(i)); + print(sizeof(int)); + + print(sizeof i); + print(sizeof (i)); + print(sizeof (int)); + + print(sizeof i); + print(sizeof (i)); + print(sizeof (int)); +} diff --git a/usr.bin/indent/opt-nbs.0.pro b/usr.bin/indent/opt-nbs.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-nbs.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-nbs.0.pro,v 1.1 2021/03/06 17:56:34 rillig Exp $ */ +/* $FreeBSD$ */ + +-nbs diff --git a/usr.bin/indent/opt-nbs.0.stdout b/usr.bin/indent/opt-nbs.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-nbs.0.stdout @@ -0,0 +1,17 @@ +/* $NetBSD: opt-nbs.0.stdout,v 1.2 2021/03/06 23:09:17 rillig Exp $ */ +/* $FreeBSD$ */ + +void +example(int i) +{ + print(sizeof(i)); + print(sizeof(int)); + + print(sizeof i); + print(sizeof(i)); + print(sizeof(int)); + + print(sizeof i); + print(sizeof(i)); + print(sizeof(int)); +} diff --git a/usr.bin/indent/opt-ncdb.0 b/usr.bin/indent/opt-ncdb.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-ncdb.0 @@ -0,0 +1,45 @@ +/* $NetBSD: opt-ncdb.0,v 1.2 2021/03/06 23:09:17 rillig Exp $ */ +/* $FreeBSD$ */ + +/* A single-line comment. */ + +/* A + * multi-line + * comment. */ + +/* + * A + * multi-line + * comment. + */ + +int ga; /* A single-line comment. */ + +int gb; /* A + * multi-line + * comment. */ + +int gc; /* + * A + * multi-line + * comment. + */ + +void +example(void) +{ + /* A single-line comment. */ + int la; + + /* A + * multi-line + * comment. */ + int lb; + + /* + * A + * multi-line + * comment. + */ + int lc; +} diff --git a/usr.bin/indent/opt-ncdb.0.pro b/usr.bin/indent/opt-ncdb.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-ncdb.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-ncdb.0.pro,v 1.1 2021/03/06 17:56:34 rillig Exp $ */ +/* $FreeBSD$ */ + +-ncdb diff --git a/usr.bin/indent/opt-ncdb.0.stdout b/usr.bin/indent/opt-ncdb.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-ncdb.0.stdout @@ -0,0 +1,27 @@ +/* $NetBSD: opt-ncdb.0.stdout,v 1.2 2021/03/06 23:09:17 rillig Exp $ */ +/* $FreeBSD$ */ + +/* A single-line comment. */ + +/* A multi-line comment. */ + +/* A multi-line comment. */ + +int ga; /* A single-line comment. */ + +int gb; /* A multi-line comment. */ + +int gc; /* A multi-line comment. */ + +void +example(void) +{ + /* A single-line comment. */ + int la; + + /* A multi-line comment. */ + int lb; + + /* A multi-line comment. */ + int lc; +} diff --git a/usr.bin/indent/opt-nce.0 b/usr.bin/indent/opt-nce.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-nce.0 @@ -0,0 +1,11 @@ +/* $NetBSD: opt-nce.0,v 1.2 2021/03/06 21:27:39 rillig Exp $ */ +/* $FreeBSD$ */ + +void +example(int n) +{ + if (n > 99) { print("large"); } + else if (n > 9) { print("double-digit"); } + else if (n > 0) print("positive"); + else { print("negative"); } +} diff --git a/usr.bin/indent/opt-nce.0.pro b/usr.bin/indent/opt-nce.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-nce.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-nce.0.pro,v 1.1 2021/03/06 17:56:34 rillig Exp $ */ +/* $FreeBSD$ */ + +-nce diff --git a/usr.bin/indent/opt-nce.0.stdout b/usr.bin/indent/opt-nce.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-nce.0.stdout @@ -0,0 +1,18 @@ +/* $NetBSD: opt-nce.0.stdout,v 1.2 2021/03/06 21:27:39 rillig Exp $ */ +/* $FreeBSD$ */ + +void +example(int n) +{ + if (n > 99) { + print("large"); + } + else if (n > 9) { + print("double-digit"); + } + else if (n > 0) + print("positive"); + else { + print("negative"); + } +} diff --git a/usr.bin/indent/opt-ncs.0 b/usr.bin/indent/opt-ncs.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-ncs.0 @@ -0,0 +1,6 @@ +/* $NetBSD: opt-ncs.0,v 1.2 2021/03/06 23:09:17 rillig Exp $ */ +/* $FreeBSD$ */ + +int i0 = (int)3.0; +int i1 = (int) 3.0; +int i3 = (int) 3.0; diff --git a/usr.bin/indent/opt-ncs.0.pro b/usr.bin/indent/opt-ncs.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-ncs.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-ncs.0.pro,v 1.1 2021/03/06 17:56:34 rillig Exp $ */ +/* $FreeBSD$ */ + +-ncs diff --git a/usr.bin/indent/opt-ncs.0.stdout b/usr.bin/indent/opt-ncs.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-ncs.0.stdout @@ -0,0 +1,6 @@ +/* $NetBSD: opt-ncs.0.stdout,v 1.2 2021/03/06 23:09:17 rillig Exp $ */ +/* $FreeBSD$ */ + +int i0 = (int)3.0; +int i1 = (int)3.0; +int i3 = (int)3.0; diff --git a/usr.bin/indent/opt-ndj.0 b/usr.bin/indent/opt-ndj.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-ndj.0 @@ -0,0 +1,11 @@ +/* $NetBSD: opt-ndj.0,v 1.2 2021/03/06 23:09:17 rillig Exp $ */ +/* $FreeBSD$ */ + +/* FIXME: The options -dj and -ndj produce the same output. */ + +int i; +int *ip; +const char *ccp; +const void *****vppppp; +const void ******vpppppp; +const void ********vpppppppp; diff --git a/usr.bin/indent/opt-ndj.0.pro b/usr.bin/indent/opt-ndj.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-ndj.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-ndj.0.pro,v 1.1 2021/03/06 17:56:34 rillig Exp $ */ +/* $FreeBSD$ */ + +-ndj diff --git a/usr.bin/indent/opt-ndj.0.stdout b/usr.bin/indent/opt-ndj.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-ndj.0.stdout @@ -0,0 +1,11 @@ +/* $NetBSD: opt-ndj.0.stdout,v 1.2 2021/03/06 23:09:17 rillig Exp $ */ +/* $FreeBSD$ */ + +/* FIXME: The options -dj and -ndj produce the same output. */ + +int i; +int *ip; +const char *ccp; +const void *****vppppp; +const void ******vpppppp; +const void ********vpppppppp; diff --git a/usr.bin/indent/opt-neei.0 b/usr.bin/indent/opt-neei.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-neei.0 @@ -0,0 +1,14 @@ +/* $NetBSD: opt-neei.0,v 1.2 2021/03/06 23:09:17 rillig Exp $ */ +/* $FreeBSD$ */ + +bool +less(int a, int b) +{ + if (a < + b) + return true; + if (a + < + b) + return true; +} diff --git a/usr.bin/indent/opt-neei.0.pro b/usr.bin/indent/opt-neei.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-neei.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-neei.0.pro,v 1.1 2021/03/06 17:56:34 rillig Exp $ */ +/* $FreeBSD$ */ + +-neei diff --git a/usr.bin/indent/opt-neei.0.stdout b/usr.bin/indent/opt-neei.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-neei.0.stdout @@ -0,0 +1,14 @@ +/* $NetBSD: opt-neei.0.stdout,v 1.2 2021/03/06 23:09:17 rillig Exp $ */ +/* $FreeBSD$ */ + +bool +less(int a, int b) +{ + if (a < + b) + return true; + if (a + < + b) + return true; +} diff --git a/usr.bin/indent/opt-nei.0 b/usr.bin/indent/opt-nei.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-nei.0 @@ -0,0 +1,16 @@ +/* $NetBSD: opt-nei.0,v 1.2 2021/03/06 21:27:39 rillig Exp $ */ +/* $FreeBSD$ */ + +void +example(int n) +{ + if (n > 99) { + print("large"); + } else if (n > 9) { + print("double-digit"); + } else if (n > 0) + print("positive"); + else { + print("negative"); + } +} diff --git a/usr.bin/indent/opt-nei.0.pro b/usr.bin/indent/opt-nei.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-nei.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-nei.0.pro,v 1.1 2021/03/06 17:56:34 rillig Exp $ */ +/* $FreeBSD$ */ + +-nei diff --git a/usr.bin/indent/opt-nei.0.stdout b/usr.bin/indent/opt-nei.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-nei.0.stdout @@ -0,0 +1,18 @@ +/* $NetBSD: opt-nei.0.stdout,v 1.2 2021/03/06 21:27:39 rillig Exp $ */ +/* $FreeBSD$ */ + +void +example(int n) +{ + if (n > 99) { + print("large"); + } else + if (n > 9) { + print("double-digit"); + } else + if (n > 0) + print("positive"); + else { + print("negative"); + } +} diff --git a/usr.bin/indent/opt-nfbs.0 b/usr.bin/indent/opt-nfbs.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-nfbs.0 @@ -0,0 +1,4 @@ +/* $NetBSD: opt-nfbs.0,v 1.2 2021/03/06 21:27:39 rillig Exp $ */ +/* $FreeBSD$ */ + +void example(int n) {} diff --git a/usr.bin/indent/opt-nfbs.0.pro b/usr.bin/indent/opt-nfbs.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-nfbs.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-nfbs.0.pro,v 1.1 2021/03/06 17:56:34 rillig Exp $ */ +/* $FreeBSD$ */ + +-nfbs diff --git a/usr.bin/indent/opt-nfbs.0.stdout b/usr.bin/indent/opt-nfbs.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-nfbs.0.stdout @@ -0,0 +1,6 @@ +/* $NetBSD: opt-nfbs.0.stdout,v 1.2 2021/03/06 21:27:39 rillig Exp $ */ +/* $FreeBSD$ */ + +void +example(int n) { +} diff --git a/usr.bin/indent/opt-nfc1.0 b/usr.bin/indent/opt-nfc1.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-nfc1.0 @@ -0,0 +1,10 @@ +/* $NetBSD: opt-nfc1.0,v 1.2 2021/03/06 21:27:39 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * A comment + * in column 1. + * + * + * + */ diff --git a/usr.bin/indent/opt-nfc1.0.pro b/usr.bin/indent/opt-nfc1.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-nfc1.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-nfc1.0.pro,v 1.1 2021/03/06 17:56:34 rillig Exp $ */ +/* $FreeBSD$ */ + +-nfc1 diff --git a/usr.bin/indent/opt-nfc1.0.stdout b/usr.bin/indent/opt-nfc1.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-nfc1.0.stdout @@ -0,0 +1,10 @@ +/* $NetBSD: opt-nfc1.0.stdout,v 1.2 2021/03/06 21:27:39 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * A comment + * in column 1. + * + * + * + */ diff --git a/usr.bin/indent/opt-nfcb.0 b/usr.bin/indent/opt-nfcb.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-nfcb.0 @@ -0,0 +1,32 @@ +/* $NetBSD: opt-nfcb.0,v 1.2 2021/03/06 22:10:40 rillig Exp $ */ +/* $FreeBSD$ */ + +/* FIXME: The options -fcb and -nfcb result in exactly the same output. */ + +/* Not + * + * so carefully + * formatted + * comment */ + +/*- + * car mat men + * efu for ted com t + * lly box . + */ + +void +example(void) +{ + /* Not + * + * so carefully + * formatted + * comment */ + + /*- + * car mat men + * efu for ted com t + * lly box . + */ +} diff --git a/usr.bin/indent/opt-nfcb.0.pro b/usr.bin/indent/opt-nfcb.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-nfcb.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-nfcb.0.pro,v 1.1 2021/03/06 17:56:34 rillig Exp $ */ +/* $FreeBSD$ */ + +-nfcb diff --git a/usr.bin/indent/opt-nfcb.0.stdout b/usr.bin/indent/opt-nfcb.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-nfcb.0.stdout @@ -0,0 +1,32 @@ +/* $NetBSD: opt-nfcb.0.stdout,v 1.2 2021/03/06 22:10:40 rillig Exp $ */ +/* $FreeBSD$ */ + +/* FIXME: The options -fcb and -nfcb result in exactly the same output. */ + +/* + * Not + * + * so carefully formatted comment + */ + +/*- + * car mat men + * efu for ted com t + * lly box . + */ + +void +example(void) +{ + /* + * Not + * + * so carefully formatted comment + */ + + /*- + * car mat men + * efu for ted com t + * lly box . + */ +} diff --git a/usr.bin/indent/opt-nip.0 b/usr.bin/indent/opt-nip.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-nip.0 @@ -0,0 +1,21 @@ +/* $NetBSD: opt-nip.0,v 1.2 2021/03/06 22:10:40 rillig Exp $ */ +/* $FreeBSD$ */ + +/* FIXME: The options -ip and -nip produce the same output. */ + +int +several_parameters_1(int a, +int b, +const char *cp); + +int +several_parameters_2( +int a, +int b, +const char *cp); + +int +several_parameters_3( +int a1, int a2, +int b1, int b2, +const char *cp); diff --git a/usr.bin/indent/opt-nip.0.pro b/usr.bin/indent/opt-nip.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-nip.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-nip.0.pro,v 1.1 2021/03/06 17:56:34 rillig Exp $ */ +/* $FreeBSD$ */ + +-nip diff --git a/usr.bin/indent/opt-nip.0.stdout b/usr.bin/indent/opt-nip.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-nip.0.stdout @@ -0,0 +1,21 @@ +/* $NetBSD: opt-nip.0.stdout,v 1.2 2021/03/06 22:10:40 rillig Exp $ */ +/* $FreeBSD$ */ + +/* FIXME: The options -ip and -nip produce the same output. */ + +int +several_parameters_1(int a, + int b, + const char *cp); + +int +several_parameters_2( + int a, + int b, + const char *cp); + +int +several_parameters_3( + int a1, int a2, + int b1, int b2, + const char *cp); diff --git a/usr.bin/indent/opt-nlp.0 b/usr.bin/indent/opt-nlp.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-nlp.0 @@ -0,0 +1,14 @@ +/* $NetBSD: opt-nlp.0,v 1.2 2021/03/06 22:10:40 rillig Exp $ */ +/* $FreeBSD$ */ + +void +example(void) +{ + p1 = first_procedure(second_procedure(p2, p3), + third_procedure(p4, p5)); + + p1 = first_procedure(second_procedure(p2, + p3), + third_procedure(p4, + p5)); +} diff --git a/usr.bin/indent/opt-nlp.0.pro b/usr.bin/indent/opt-nlp.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-nlp.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-nlp.0.pro,v 1.1 2021/03/06 17:56:34 rillig Exp $ */ +/* $FreeBSD$ */ + +-nlp diff --git a/usr.bin/indent/opt-nlp.0.stdout b/usr.bin/indent/opt-nlp.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-nlp.0.stdout @@ -0,0 +1,14 @@ +/* $NetBSD: opt-nlp.0.stdout,v 1.2 2021/03/06 22:10:40 rillig Exp $ */ +/* $FreeBSD$ */ + +void +example(void) +{ + p1 = first_procedure(second_procedure(p2, p3), + third_procedure(p4, p5)); + + p1 = first_procedure(second_procedure(p2, + p3), + third_procedure(p4, + p5)); +} diff --git a/usr.bin/indent/opt-nlpl.0 b/usr.bin/indent/opt-nlpl.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-nlpl.0 @@ -0,0 +1,24 @@ +/* $NetBSD: opt-nlpl.0,v 1.2 2021/03/07 10:12:18 rillig Exp $ */ +/* $FreeBSD$ */ + +void +example(void) +{ + int sum1 = 1+2+3+4+5+6+7+8+9+10+11+12+13+14+15+16+17+18+19+20+21; + int sum2 = (1+2+3+4+5+6+7+8+9+10+11+12+13+14+15+16+17+18+19+20+21); + + int sum3 = 1+2+3+4+5+ + 6+7+8+9+10+ + 11+12+13+14+15+ + 16+17+18+19+20+ + 21; + int sum4 = (1+2+3+4+5+ + 6+7+8+9+10+ + 11+12+13+14+15+ + 16+17+18+19+20+ + 21); + + call_function(call_function(call_function(call_function(call_function(call_function()))))); + + call_function((call_function(call_function(call_function(call_function(call_function())))))); +} diff --git a/usr.bin/indent/opt-nlpl.0.pro b/usr.bin/indent/opt-nlpl.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-nlpl.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-nlpl.0.pro,v 1.1 2021/03/06 17:56:34 rillig Exp $ */ +/* $FreeBSD$ */ + +-nlpl diff --git a/usr.bin/indent/opt-nlpl.0.stdout b/usr.bin/indent/opt-nlpl.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-nlpl.0.stdout @@ -0,0 +1,27 @@ +/* $NetBSD: opt-nlpl.0.stdout,v 1.2 2021/03/07 10:12:18 rillig Exp $ */ +/* $FreeBSD$ */ + +/* $ TODO: Add code that differs between -lpl and -nlpl. */ +void +example(void) +{ + int sum1 = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 + 21; + int sum2 = (1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 + 21); + +/* $ XXX: There should be at least _some_ indentation for the */ +/* $ continuation lines. */ + int sum3 = 1 + 2 + 3 + 4 + 5 + + 6 + 7 + 8 + 9 + 10 + + 11 + 12 + 13 + 14 + 15 + + 16 + 17 + 18 + 19 + 20 + + 21; + int sum4 = (1 + 2 + 3 + 4 + 5 + + 6 + 7 + 8 + 9 + 10 + + 11 + 12 + 13 + 14 + 15 + + 16 + 17 + 18 + 19 + 20 + + 21); + + call_function(call_function(call_function(call_function(call_function(call_function()))))); + + call_function((call_function(call_function(call_function(call_function(call_function())))))); +} diff --git a/usr.bin/indent/opt-npcs.0 b/usr.bin/indent/opt-npcs.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-npcs.0 @@ -0,0 +1,10 @@ +/* $NetBSD: opt-npcs.0,v 1.2 2021/03/07 10:12:18 rillig Exp $ */ +/* $FreeBSD$ */ + +void +example(void) +{ + function_call(); + function_call(1); + function_call(1,2,3); +} diff --git a/usr.bin/indent/opt-npcs.0.pro b/usr.bin/indent/opt-npcs.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-npcs.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-npcs.0.pro,v 1.1 2021/03/06 17:56:34 rillig Exp $ */ +/* $FreeBSD$ */ + +-npcs diff --git a/usr.bin/indent/opt-npcs.0.stdout b/usr.bin/indent/opt-npcs.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-npcs.0.stdout @@ -0,0 +1,10 @@ +/* $NetBSD: opt-npcs.0.stdout,v 1.2 2021/03/07 10:12:18 rillig Exp $ */ +/* $FreeBSD$ */ + +void +example(void) +{ + function_call(); + function_call(1); + function_call(1, 2, 3); +} diff --git a/usr.bin/indent/opt-npro.0 b/usr.bin/indent/opt-npro.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-npro.0 @@ -0,0 +1,8 @@ +/* $NetBSD: opt-npro.0,v 1.2 2021/03/07 10:12:18 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Mentioning the option -npro in a .pro file has no effect since at that + * point, indent has already decided to load the .pro file, and it only + * decides once. + */ diff --git a/usr.bin/indent/opt-npro.0.pro b/usr.bin/indent/opt-npro.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-npro.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-npro.0.pro,v 1.1 2021/03/06 17:56:34 rillig Exp $ */ +/* $FreeBSD$ */ + +-npro diff --git a/usr.bin/indent/opt-npro.0.stdout b/usr.bin/indent/opt-npro.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-npro.0.stdout @@ -0,0 +1,8 @@ +/* $NetBSD: opt-npro.0.stdout,v 1.2 2021/03/07 10:12:18 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Mentioning the option -npro in a .pro file has no effect since at that + * point, indent has already decided to load the .pro file, and it only + * decides once. + */ diff --git a/usr.bin/indent/opt-npsl.0 b/usr.bin/indent/opt-npsl.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-npsl.0 @@ -0,0 +1,6 @@ +/* $NetBSD: opt-npsl.0,v 1.2 2021/03/07 10:12:18 rillig Exp $ */ +/* $FreeBSD$ */ + +void function_declaration(void); + +void function_definition(void) {} diff --git a/usr.bin/indent/opt-npsl.0.pro b/usr.bin/indent/opt-npsl.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-npsl.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-npsl.0.pro,v 1.1 2021/03/06 17:56:34 rillig Exp $ */ +/* $FreeBSD$ */ + +-npsl diff --git a/usr.bin/indent/opt-npsl.0.stdout b/usr.bin/indent/opt-npsl.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-npsl.0.stdout @@ -0,0 +1,8 @@ +/* $NetBSD: opt-npsl.0.stdout,v 1.2 2021/03/07 10:12:18 rillig Exp $ */ +/* $FreeBSD$ */ + +void function_declaration(void); + +void function_definition(void) +{ +} diff --git a/usr.bin/indent/opt-nsc.0 b/usr.bin/indent/opt-nsc.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-nsc.0 @@ -0,0 +1,15 @@ +/* $NetBSD: opt-nsc.0,v 1.2 2021/03/07 10:12:18 rillig Exp $ */ +/* $FreeBSD$ */ + +/* comment +without +asterisks +*/ + +/* +** This comment style is used by Lua. +*/ + +/** + * Javadoc, adopted by several other programming languages. + */ diff --git a/usr.bin/indent/opt-nsc.0.pro b/usr.bin/indent/opt-nsc.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-nsc.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-nsc.0.pro,v 1.1 2021/03/06 17:56:34 rillig Exp $ */ +/* $FreeBSD$ */ + +-nsc diff --git a/usr.bin/indent/opt-nsc.0.stdout b/usr.bin/indent/opt-nsc.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-nsc.0.stdout @@ -0,0 +1,15 @@ +/* $NetBSD: opt-nsc.0.stdout,v 1.2 2021/03/07 10:12:18 rillig Exp $ */ +/* $FreeBSD$ */ + +/* +comment without asterisks + */ + +/* $ This comment, as rewritten by indent, is not actually used by Lua. */ +/* + * This comment style is used by Lua. + */ + +/** + * Javadoc, adopted by several other programming languages. + */ diff --git a/usr.bin/indent/opt-nsob.0 b/usr.bin/indent/opt-nsob.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-nsob.0 @@ -0,0 +1,22 @@ +/* $NetBSD: opt-nsob.0,v 1.2 2021/03/07 10:12:18 rillig Exp $ */ +/* $FreeBSD$ */ + +void function_declaration(void); + + + +int +function_definition(void) +{ + + + int var; + + + var = value; + + + return var; + + +} diff --git a/usr.bin/indent/opt-nsob.0.pro b/usr.bin/indent/opt-nsob.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-nsob.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-nsob.0.pro,v 1.1 2021/03/06 17:56:34 rillig Exp $ */ +/* $FreeBSD$ */ + +-nsob diff --git a/usr.bin/indent/opt-nsob.0.stdout b/usr.bin/indent/opt-nsob.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-nsob.0.stdout @@ -0,0 +1,22 @@ +/* $NetBSD: opt-nsob.0.stdout,v 1.2 2021/03/07 10:12:18 rillig Exp $ */ +/* $FreeBSD$ */ + +void function_declaration(void); + + + +int +function_definition(void) +{ + + + int var; + + + var = value; + + + return var; + + +} diff --git a/usr.bin/indent/opt-nut.0 b/usr.bin/indent/opt-nut.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-nut.0 @@ -0,0 +1,13 @@ +/* $NetBSD: opt-nut.0,v 1.2 2021/03/07 10:12:18 rillig Exp $ */ +/* $FreeBSD$ */ + +int var; + +void +function(int arg) +{ + if (arg > 0) + function( + arg - 1 + ); +} diff --git a/usr.bin/indent/opt-nut.0.pro b/usr.bin/indent/opt-nut.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-nut.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-nut.0.pro,v 1.1 2021/03/06 17:56:34 rillig Exp $ */ +/* $FreeBSD$ */ + +-nut diff --git a/usr.bin/indent/opt-nut.0.stdout b/usr.bin/indent/opt-nut.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-nut.0.stdout @@ -0,0 +1,13 @@ +/* $NetBSD: opt-nut.0.stdout,v 1.2 2021/03/07 10:12:18 rillig Exp $ */ +/* $FreeBSD$ */ + +int var; + +void +function(int arg) +{ + if (arg > 0) + function( + arg - 1 + ); +} diff --git a/usr.bin/indent/opt-nv.0 b/usr.bin/indent/opt-nv.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-nv.0 @@ -0,0 +1,9 @@ +/* $NetBSD: opt-nv.0,v 1.2 2021/03/07 10:12:18 rillig Exp $ */ +/* $FreeBSD$ */ + +void +example(void) +{ + int sum1 = 1+2+3+4+5+6+7+8+9+10+11+12+13+14+15+16+17+18+19+20+21; + int sum2 = (1+2+3+4+5+6+7+8+9+10+11+12+13+14+15+16+17+18+19+20+21); +} diff --git a/usr.bin/indent/opt-nv.0.pro b/usr.bin/indent/opt-nv.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-nv.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-nv.0.pro,v 1.1 2021/03/06 17:56:34 rillig Exp $ */ +/* $FreeBSD$ */ + +-nv diff --git a/usr.bin/indent/opt-nv.0.stdout b/usr.bin/indent/opt-nv.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-nv.0.stdout @@ -0,0 +1,11 @@ +/* $NetBSD: opt-nv.0.stdout,v 1.2 2021/03/07 10:12:18 rillig Exp $ */ +/* $FreeBSD$ */ + +void +example(void) +{ +/* $ XXX: The following lines are too long and should thus be broken. */ +/* $ XXX: If they are broken, -nv does NOT output 'Line broken'. */ + int sum1 = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 + 21; + int sum2 = (1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 + 21); +} diff --git a/usr.bin/indent/opt-pcs.0 b/usr.bin/indent/opt-pcs.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-pcs.0 @@ -0,0 +1,10 @@ +/* $NetBSD: opt-pcs.0,v 1.2 2021/03/07 10:12:18 rillig Exp $ */ +/* $FreeBSD$ */ + +void +example(void) +{ + function_call(); + function_call(1); + function_call(1,2,3); +} diff --git a/usr.bin/indent/opt-pcs.0.pro b/usr.bin/indent/opt-pcs.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-pcs.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-pcs.0.pro,v 1.1 2021/03/06 17:56:34 rillig Exp $ */ +/* $FreeBSD$ */ + +-pcs diff --git a/usr.bin/indent/opt-pcs.0.stdout b/usr.bin/indent/opt-pcs.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-pcs.0.stdout @@ -0,0 +1,10 @@ +/* $NetBSD: opt-pcs.0.stdout,v 1.2 2021/03/07 10:12:18 rillig Exp $ */ +/* $FreeBSD$ */ + +void +example(void) +{ + function_call (); + function_call (1); + function_call (1, 2, 3); +} diff --git a/usr.bin/indent/opt-psl.0 b/usr.bin/indent/opt-psl.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-psl.0 @@ -0,0 +1,6 @@ +/* $NetBSD: opt-psl.0,v 1.2 2021/03/07 10:12:18 rillig Exp $ */ +/* $FreeBSD$ */ + +void function_declaration(void); + +void function_definition(void) {} diff --git a/usr.bin/indent/opt-psl.0.pro b/usr.bin/indent/opt-psl.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-psl.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-psl.0.pro,v 1.1 2021/03/06 17:56:34 rillig Exp $ */ +/* $FreeBSD$ */ + +-psl diff --git a/usr.bin/indent/opt-psl.0.stdout b/usr.bin/indent/opt-psl.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-psl.0.stdout @@ -0,0 +1,9 @@ +/* $NetBSD: opt-psl.0.stdout,v 1.2 2021/03/07 10:12:18 rillig Exp $ */ +/* $FreeBSD$ */ + +void function_declaration(void); + +void +function_definition(void) +{ +} diff --git a/usr.bin/indent/opt-sc.0 b/usr.bin/indent/opt-sc.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-sc.0 @@ -0,0 +1,15 @@ +/* $NetBSD: opt-sc.0,v 1.2 2021/03/07 10:12:18 rillig Exp $ */ +/* $FreeBSD$ */ + +/* comment +without +asterisks +*/ + +/* +** This comment style is used by Lua. +*/ + +/** + * Javadoc, adopted by several other programming languages. + */ diff --git a/usr.bin/indent/opt-sc.0.pro b/usr.bin/indent/opt-sc.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-sc.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-sc.0.pro,v 1.1 2021/03/06 17:56:34 rillig Exp $ */ +/* $FreeBSD$ */ + +-sc diff --git a/usr.bin/indent/opt-sc.0.stdout b/usr.bin/indent/opt-sc.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-sc.0.stdout @@ -0,0 +1,15 @@ +/* $NetBSD: opt-sc.0.stdout,v 1.2 2021/03/07 10:12:18 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * comment without asterisks + */ + +/* $ XXX: The additional '*' is debatable. */ +/* + * * This comment style is used by Lua. + */ + +/** + * Javadoc, adopted by several other programming languages. + */ diff --git a/usr.bin/indent/opt-sob.0 b/usr.bin/indent/opt-sob.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-sob.0 @@ -0,0 +1,24 @@ +/* $NetBSD: opt-sob.0,v 1.2 2021/03/07 10:12:18 rillig Exp $ */ +/* $FreeBSD$ */ + +/* $ FIXME: There are lots of 'optional blank lines' here that should */ +/* $ FIXME: be swallowed. */ +void function_declaration(void); + + + +int +function_definition(void) +{ + + + int var; + + + var = value; + + + return var; + + +} diff --git a/usr.bin/indent/opt-sob.0.pro b/usr.bin/indent/opt-sob.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-sob.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-sob.0.pro,v 1.1 2021/03/06 17:56:34 rillig Exp $ */ +/* $FreeBSD$ */ + +-sob diff --git a/usr.bin/indent/opt-sob.0.stdout b/usr.bin/indent/opt-sob.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-sob.0.stdout @@ -0,0 +1,22 @@ +/* $NetBSD: opt-sob.0.stdout,v 1.2 2021/03/07 10:12:18 rillig Exp $ */ +/* $FreeBSD$ */ + +void function_declaration(void); + + + +int +function_definition(void) +{ + + + int var; + + + var = value; + + + return var; + + +} diff --git a/usr.bin/indent/opt-ta.0 b/usr.bin/indent/opt-ta.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-ta.0 @@ -0,0 +1,10 @@ +/* $NetBSD: opt-ta.0,v 1.2 2021/03/07 10:12:18 rillig Exp $ */ +/* $FreeBSD$ */ + +void +example(void *arg) +{ + int mult = (unknown_type_name) * arg; + + int suff = (unknown_type_name_t) * arg; +} diff --git a/usr.bin/indent/opt-ta.0.pro b/usr.bin/indent/opt-ta.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-ta.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-ta.0.pro,v 1.1 2021/03/06 17:56:34 rillig Exp $ */ +/* $FreeBSD$ */ + +-ta diff --git a/usr.bin/indent/opt-ta.0.stdout b/usr.bin/indent/opt-ta.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-ta.0.stdout @@ -0,0 +1,10 @@ +/* $NetBSD: opt-ta.0.stdout,v 1.2 2021/03/07 10:12:18 rillig Exp $ */ +/* $FreeBSD$ */ + +void +example(void *arg) +{ + int mult = (unknown_type_name) * arg; + + int suff = (unknown_type_name_t)*arg; +} diff --git a/usr.bin/indent/opt-ts.0 b/usr.bin/indent/opt-ts.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-ts.0 @@ -0,0 +1,16 @@ +/* $NetBSD: opt-ts.0,v 1.2 2021/03/07 10:12:18 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Since in this test, a tab is only worth 4 spaces, the indentation needs + * more tabs to reach the indentation of 8 and the alignment at 16. + */ + +int variable; + +void +function(void) +{ + int local_variable; + char local_variable; +} diff --git a/usr.bin/indent/opt-ts.0.pro b/usr.bin/indent/opt-ts.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-ts.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-ts.0.pro,v 1.1 2021/03/06 17:56:34 rillig Exp $ */ +/* $FreeBSD$ */ + +-ts4 diff --git a/usr.bin/indent/opt-ts.0.stdout b/usr.bin/indent/opt-ts.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-ts.0.stdout @@ -0,0 +1,16 @@ +/* $NetBSD: opt-ts.0.stdout,v 1.2 2021/03/07 10:12:18 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Since in this test, a tab is only worth 4 spaces, the indentation needs + * more tabs to reach the indentation of 8 and the alignment at 16. + */ + +int variable; + +void +function(void) +{ + int local_variable; + char local_variable; +} diff --git a/usr.bin/indent/opt-ut.0 b/usr.bin/indent/opt-ut.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-ut.0 @@ -0,0 +1,12 @@ +/* $NetBSD: opt-ut.0,v 1.2 2021/03/07 10:12:18 rillig Exp $ */ +/* $FreeBSD$ */ + +int variable; + +void function_declaration(void); + +void +function_definition(void) +{ + int local_variable; +} diff --git a/usr.bin/indent/opt-ut.0.pro b/usr.bin/indent/opt-ut.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-ut.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-ut.0.pro,v 1.1 2021/03/06 17:56:34 rillig Exp $ */ +/* $FreeBSD$ */ + +-ut diff --git a/usr.bin/indent/opt-ut.0.stdout b/usr.bin/indent/opt-ut.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-ut.0.stdout @@ -0,0 +1,12 @@ +/* $NetBSD: opt-ut.0.stdout,v 1.2 2021/03/07 10:12:18 rillig Exp $ */ +/* $FreeBSD$ */ + +int variable; + +void function_declaration(void); + +void +function_definition(void) +{ + int local_variable; +} diff --git a/usr.bin/indent/opt-v.0 b/usr.bin/indent/opt-v.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-v.0 @@ -0,0 +1,11 @@ +/* $NetBSD: opt-v.0,v 1.2 2021/03/07 10:12:18 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * A long comment. + */ +void +example(void) +{ + printf("A very long message template with %d arguments: %s, %s, %s", 3, "first", "second", "third"); +} diff --git a/usr.bin/indent/opt-v.0.pro b/usr.bin/indent/opt-v.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-v.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: opt-v.0.pro,v 1.1 2021/03/06 17:56:34 rillig Exp $ */ +/* $FreeBSD$ */ + +-v diff --git a/usr.bin/indent/opt-v.0.stdout b/usr.bin/indent/opt-v.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/opt-v.0.stdout @@ -0,0 +1,13 @@ +/* $NetBSD: opt-v.0.stdout,v 1.2 2021/03/07 10:12:18 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * A long comment. + */ +void +example(void) +{ + printf("A very long message template with %d arguments: %s, %s, %s", 3, "first", "second", "third"); +} +There were 8 output lines and 1 comments +(Lines with comments)/(Lines with code): 0.600 diff --git a/usr.bin/indent/parens.0 b/usr.bin/indent/parens.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/parens.0 @@ -0,0 +1,28 @@ +/* $NetBSD: parens.0,v 1.2 2021/03/06 19:51:24 rillig Exp $ */ +/* $FreeBSD: head/usr.bin/indent/tests/parens.0 334583 2018-06-03 19:05:20Z pstef $ */ +typedef void (*xxxxxxxxxxx) (int, + char); + +typedef char (*xxxxxxxxxxxxxxxxxxxxxxxxxxxx) (int *, + unsigned *, + char, + float *); + +void +test(void) +{ + char chars[secondf(firstf(B), + *here)]; + + float xxx = yyyyyyyyyyyyyy(zzzzzzzzzzzzz(p1, + (p2), + p3)); + + if (1) { + char *xxx = firstf(secondf2(p1, + p2)); + } + + rb->allocfunc(1); + rb2.allocfunc(7); +} diff --git a/usr.bin/indent/parens.0.pro b/usr.bin/indent/parens.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/parens.0.pro @@ -0,0 +1,3 @@ +/* $NetBSD: parens.0.pro,v 1.1 2019/04/04 15:27:35 kamil Exp $ */ +/* $FreeBSD: head/usr.bin/indent/tests/parens.0.pro 334583 2018-06-03 19:05:20Z pstef $ */ +-ts4 -i4 -di12 -Tallocfunc diff --git a/usr.bin/indent/parens.0.stdout b/usr.bin/indent/parens.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/parens.0.stdout @@ -0,0 +1,28 @@ +/* $NetBSD: parens.0.stdout,v 1.1 2019/04/04 15:27:35 kamil Exp $ */ +/* $FreeBSD: head/usr.bin/indent/tests/parens.0.stdout 334583 2018-06-03 19:05:20Z pstef $ */ +typedef void (*xxxxxxxxxxx) (int, + char); + +typedef char (*xxxxxxxxxxxxxxxxxxxxxxxxxxxx) (int *, + unsigned *, + char, + float *); + +void +test(void) +{ + char chars[secondf(firstf(B), + *here)]; + + float xxx = yyyyyyyyyyyyyy(zzzzzzzzzzzzz(p1, + (p2), + p3)); + + if (1) { + char *xxx = firstf(secondf2(p1, + p2)); + } + + rb->allocfunc(1); + rb2.allocfunc(7); +} diff --git a/usr.bin/indent/pcs.0 b/usr.bin/indent/pcs.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/pcs.0 @@ -0,0 +1,8 @@ +/* $NetBSD: pcs.0,v 1.1 2019/04/04 15:27:35 kamil Exp $ */ +/* $FreeBSD: head/usr.bin/indent/tests/pcs.0 334493 2018-06-01 19:56:41Z pstef $ */ +#include + +int main(void) { + puts("Hello"); + return 0; +} diff --git a/usr.bin/indent/pcs.0.pro b/usr.bin/indent/pcs.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/pcs.0.pro @@ -0,0 +1,3 @@ +/* $NetBSD: pcs.0.pro,v 1.1 2019/04/04 15:27:35 kamil Exp $ */ +/* $FreeBSD: head/usr.bin/indent/tests/pcs.0.pro 334493 2018-06-01 19:56:41Z pstef $ */ +-pcs diff --git a/usr.bin/indent/pcs.0.stdout b/usr.bin/indent/pcs.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/pcs.0.stdout @@ -0,0 +1,10 @@ +/* $NetBSD: pcs.0.stdout,v 1.1 2019/04/04 15:27:35 kamil Exp $ */ +/* $FreeBSD: head/usr.bin/indent/tests/pcs.0.stdout 334493 2018-06-01 19:56:41Z pstef $ */ +#include + +int +main(void) +{ + puts ("Hello"); + return 0; +} diff --git a/usr.bin/indent/struct.0 b/usr.bin/indent/struct.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/struct.0 @@ -0,0 +1,22 @@ +/* $NetBSD: struct.0,v 1.1 2019/04/04 15:27:35 kamil Exp $ */ +/* $FreeBSD: head/usr.bin/indent/tests/struct.0 334564 2018-06-03 16:21:15Z pstef $ */ + +int f(struct x *a); + +/* See r303485 */ +void +t(void) +{ + static const struct { + int a; + int b; + } c[] = { + { D, E }, + { F, G } + }; +} + +void u(struct x a) { + int b; + struct y c = (struct y *)&a; +} diff --git a/usr.bin/indent/struct.0.stdout b/usr.bin/indent/struct.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/struct.0.stdout @@ -0,0 +1,24 @@ +/* $NetBSD: struct.0.stdout,v 1.1 2019/04/04 15:27:35 kamil Exp $ */ +/* $FreeBSD: head/usr.bin/indent/tests/struct.0.stdout 334564 2018-06-03 16:21:15Z pstef $ */ + +int f(struct x *a); + +/* See r303485 */ +void +t(void) +{ + static const struct { + int a; + int b; + } c[] = { + {D, E}, + {F, G} + }; +} + +void +u(struct x a) +{ + int b; + struct y c = (struct y *)&a; +} diff --git a/usr.bin/indent/surplusbad.0 b/usr.bin/indent/surplusbad.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/surplusbad.0 @@ -0,0 +1,10 @@ +/* $NetBSD: surplusbad.0,v 1.1 2019/04/04 15:27:35 kamil Exp $ */ +/* $FreeBSD: head/usr.bin/indent/tests/surplusbad.0 313544 2017-02-10 09:31:39Z pstef $ */ +/* See r303599 */ +#if defined(__i386__) +int a; +#elif defined(__amd64__) +int b; +#else +#error "Port me" +#endif diff --git a/usr.bin/indent/surplusbad.0.pro b/usr.bin/indent/surplusbad.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/surplusbad.0.pro @@ -0,0 +1,3 @@ +/* $NetBSD: surplusbad.0.pro,v 1.1 2019/04/04 15:27:35 kamil Exp $ */ +/* $FreeBSD: head/usr.bin/indent/tests/surplusbad.0.pro 314613 2017-03-03 20:15:22Z ngie $ */ +-bad diff --git a/usr.bin/indent/surplusbad.0.stdout b/usr.bin/indent/surplusbad.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/surplusbad.0.stdout @@ -0,0 +1,10 @@ +/* $NetBSD: surplusbad.0.stdout,v 1.1 2019/04/04 15:27:35 kamil Exp $ */ +/* $FreeBSD: head/usr.bin/indent/tests/surplusbad.0.stdout 313544 2017-02-10 09:31:39Z pstef $ */ +/* See r303599 */ +#if defined(__i386__) +int a; +#elif defined(__amd64__) +int b; +#else +#error "Port me" +#endif diff --git a/usr.bin/indent/t_indent.sh b/usr.bin/indent/t_indent.sh new file mode 100755 --- /dev/null +++ b/usr.bin/indent/t_indent.sh @@ -0,0 +1,102 @@ +#! /bin/sh +# $NetBSD: t_indent.sh,v 1.4 2021/03/08 22:13:05 rillig Exp $ +# +# Copyright 2016 Dell EMC +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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 COPYRIGHT HOLDERS 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 COPYRIGHT +# OWNER 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: head/usr.bin/indent/tests/functional_test.sh 314613 2017-03-03 20:15:22Z ngie $ + +SRCDIR=$(atf_get_srcdir) + +check() +{ + local tc=${1}; shift + + local indent=$(atf_config_get usr.bin.indent.test_indent /usr/bin/indent) + + # All of the files need to be in the ATF sandbox in order for the tests + # to pass. + atf_check cp ${SRCDIR}/${tc}* . + + # Remove single-line comments that start with '$'. This removes RCS + # IDs, preventing them to be broken into several lines. It also + # allows for remarks that are only needed in either the input or the + # output. These removals affect the line numbers in the diffs. + for fname in "$tc" "$tc.stdout" "$tc.stderr"; do + if [ -f "$fname" ]; then + atf_check -o "save:$fname.clean" \ + sed -e '/^\/\*[[:space:]]$.*/d' "$fname" + fi + done + + local out_arg='empty' + if [ -f "$tc.stdout.clean" ]; then + out_arg="file:$tc.stdout.clean" + fi + + local err_arg='empty' + if [ -f "$tc.stderr.clean" ]; then + err_arg="file:$tc.stderr.clean" + fi + + # Make sure we don't implicitly use ~/.indent.pro from the test + # host, for determinism purposes. + local pro_arg='-npro' + if [ -f "$tc.pro" ]; then + pro_arg="-P$tc.pro" + fi + + atf_check -s "exit:${tc##*.}" -o "$out_arg" -e "$err_arg" \ + "$indent" "$pro_arg" < "$tc.clean" +} + +add_testcase() +{ + local tc=${1} + local tc_escaped word + + case "${tc%.*}" in + *-*) + local IFS="-+" + for word in ${tc%.*}; do + tc_escaped="${tc_escaped:+${tc_escaped}_}${word}" + done + ;; + *) + tc_escaped=${tc%.*} + ;; + esac + + atf_test_case ${tc_escaped} + eval "${tc_escaped}_body() { check ${tc}; }" + atf_add_test_case ${tc_escaped} +} + +atf_init_test_cases() +{ + for path in $(find -Es "${SRCDIR}" -regex '.*\.[0-9]+$'); do + add_testcase ${path##*/} + done +} diff --git a/usr.bin/indent/token-binary_op.0 b/usr.bin/indent/token-binary_op.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-binary_op.0 @@ -0,0 +1,110 @@ +/* $NetBSD: token-binary_op.0,v 1.2 2021/03/12 17:41:10 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for binary operators like '+', '&&' and several others. + * + * Several binary operators can be used as unary operators as well, or in + * other contexts. An example for such an operator is '*', which can be a + * multiplication, or pointer dereference, or pointer type declaration. + */ + +/* See C99 6.4.6 */ +void +punctuators(void) +{ + int brackets = array[subscript]; + int parentheses = function(argument); + int braces = { initializer }; + int period = structure.member; + int arrow = structure->member; + + ++prefix_increment; + postfix_increment++; + --prefix_decrement; + postfix_decrement--; + int *address = &lvalue; + int bitwise_and = value & mask; + int product = factor * factor; + int dereferenced = *address; + int positive = +number; + int sum = number + number; + int negative = -number; + int difference = number - number; + bool negated = !condition; + + int quotient = number / number; + int modulo = number % number; + int shifted_left = number << number; + int shifted_right = number >> number; + bool less_than = number < number; + bool greater_than = number > number; + bool less_equal = number <= number; + bool greater_equal = number >= number; + bool equal = number == number; + bool unequal = number != number; + int bitwise_exclusive_or = number ^ number; + int bitwise_or = number | number; + bool logical_and = condition && condition; + bool logical_or = condition || condition; + + int conditional = condition ? number : number; + + /* combined assignment operators */ + number = (expression); + number *= number; + number /= number; + number %= number; + number += number; + number -= number; + number <<= number; + number >>= number; + number &= number; + number ^= number; + number |= number; + + number = function(argument1, argument2); + number = function(argument), number; + + /* digraphs */ + number = array<:subscript:>; + number = (int)<% initializer %>; +} + +void +peculiarities(void) +{ + /* + * When indent tokenizes some of the operators, it allows for + * arbitrary repetitions of the operator character, followed by an + * arbitrary amount of '='. This is used for operators like '&&' or + * '|||==='. + * + * Before 2021-03-07 22:11:01, the comment '//' was treated as an + * operator as well, and so was the comment '/////', leading to + * unexpected results; see comment-line-end.0 for more details. + * + * See lexi.c, lexi, "default:". + */ + if (a &&&&&&& b) + return; + if (a |||=== b) + return; + + /*- + * For '+' and '-', this does not work since the lexer has to + * distinguish between '++' and '+' early. The following sequence is + * thus tokenized as: + * + * ident 'a' + * postfix '++' + * binary_op '++' + * unary_op '++' + * unary_op '+' + * ident 'b' + * + * See lexi.c, lexi, "case '+':". + */ + if (a +++++++ b) + return; +} diff --git a/usr.bin/indent/token-binary_op.0.pro b/usr.bin/indent/token-binary_op.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-binary_op.0.pro @@ -0,0 +1,4 @@ +/* $NetBSD: token-binary_op.0.pro,v 1.2 2021/03/12 17:41:10 rillig Exp $ */ +/* $FreeBSD$ */ + +-ldi0 /* do not align local variables in declarations */ diff --git a/usr.bin/indent/token-binary_op.0.stdout b/usr.bin/indent/token-binary_op.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-binary_op.0.stdout @@ -0,0 +1,113 @@ +/* $NetBSD: token-binary_op.0.stdout,v 1.2 2021/03/12 17:41:10 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for binary operators like '+', '&&' and several others. + * + * Several binary operators can be used as unary operators as well, or in + * other contexts. An example for such an operator is '*', which can be a + * multiplication, or pointer dereference, or pointer type declaration. + */ + +/* See C99 6.4.6 */ +void +punctuators(void) +{ + int brackets = array[subscript]; + int parentheses = function(argument); +/* $ XXX: The spaces around the initializer are gone. */ + int braces = {initializer}; + int period = structure.member; + int arrow = structure->member; + + ++prefix_increment; + postfix_increment++; + --prefix_decrement; + postfix_decrement--; + int *address = &lvalue; + int bitwise_and = value & mask; + int product = factor * factor; + int dereferenced = *address; + int positive = +number; + int sum = number + number; + int negative = -number; + int difference = number - number; + bool negated = !condition; + + int quotient = number / number; + int modulo = number % number; + int shifted_left = number << number; + int shifted_right = number >> number; + bool less_than = number < number; + bool greater_than = number > number; + bool less_equal = number <= number; + bool greater_equal = number >= number; + bool equal = number == number; + bool unequal = number != number; + int bitwise_exclusive_or = number ^ number; + int bitwise_or = number | number; + bool logical_and = condition && condition; + bool logical_or = condition || condition; + + int conditional = condition ? number : number; + + /* combined assignment operators */ + number = (expression); + number *= number; + number /= number; + number %= number; + number += number; + number -= number; + number <<= number; + number >>= number; + number &= number; + number ^= number; + number |= number; + + number = function(argument1, argument2); + number = function(argument), number; + + /* digraphs */ +/* $ XXX: indent is confused by the digraphs for '[' and ']'. */ +/* $ This probably doesn't matter since digraphs are not used in practice. */ +number = array <:subscript:>; + number = (int)<%initializer % >; +} + +void +peculiarities(void) +{ + /* + * When indent tokenizes some of the operators, it allows for + * arbitrary repetitions of the operator character, followed by an + * arbitrary amount of '='. This is used for operators like '&&' or + * '|||==='. + * + * Before 2021-03-07 22:11:01, the comment '//' was treated as an + * operator as well, and so was the comment '/////', leading to + * unexpected results; see comment-line-end.0 for more details. + * + * See lexi.c, lexi, "default:". + */ + if (a &&&&&&& b) + return; + if (a |||=== b) + return; + + /*- + * For '+' and '-', this does not work since the lexer has to + * distinguish between '++' and '+' early. The following sequence is + * thus tokenized as: + * + * ident 'a' + * postfix '++' + * binary_op '++' + * unary_op '++' + * unary_op '+' + * ident 'b' + * + * See lexi.c, lexi, "case '+':". + */ + if (a++ ++ +++b) + return; +} diff --git a/usr.bin/indent/token-case_label.0 b/usr.bin/indent/token-case_label.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-case_label.0 @@ -0,0 +1,8 @@ +/* $NetBSD: token-case_label.0,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for formatting of case labels in switch statements. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-case_label.0.pro b/usr.bin/indent/token-case_label.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-case_label.0.pro @@ -0,0 +1,8 @@ +/* $NetBSD: token-case_label.0.pro,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * TODO: Explain the command line options of the test. + */ + +/* TODO: Add some command line options */ diff --git a/usr.bin/indent/token-case_label.0.stdout b/usr.bin/indent/token-case_label.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-case_label.0.stdout @@ -0,0 +1,8 @@ +/* $NetBSD: token-case_label.0.stdout,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for formatting of case labels in switch statements. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-colon.0 b/usr.bin/indent/token-colon.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-colon.0 @@ -0,0 +1,17 @@ +/* $NetBSD: token-colon.0,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for formatting of the colon token, which is used in the following + * contexts: + * + * After a label that is the target of a goto statement. + * + * In a switch statement, after a case label or the default label. + * + * As part of the conditional expression operator '?:'. + * + * In the declaration of a struct member that is a bit-field. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-colon.0.pro b/usr.bin/indent/token-colon.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-colon.0.pro @@ -0,0 +1,8 @@ +/* $NetBSD: token-colon.0.pro,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * TODO: Explain the command line options of the test. + */ + +/* TODO: Add some command line options */ diff --git a/usr.bin/indent/token-colon.0.stdout b/usr.bin/indent/token-colon.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-colon.0.stdout @@ -0,0 +1,17 @@ +/* $NetBSD: token-colon.0.stdout,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for formatting of the colon token, which is used in the following + * contexts: + * + * After a label that is the target of a goto statement. + * + * In a switch statement, after a case label or the default label. + * + * As part of the conditional expression operator '?:'. + * + * In the declaration of a struct member that is a bit-field. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-comma.0 b/usr.bin/indent/token-comma.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-comma.0 @@ -0,0 +1,17 @@ +/* $NetBSD: token-comma.0,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for the comma, which is used in the following contexts: + * + * The binary operator ',' inserts a sequence point between the evaluation of + * its operands. + * + * The parameters of a function declaration or a macro definition are + * separated by a comma. + * + * The arguments of a function call expression or a macro invocation are + * separated by a comma. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-comma.0.pro b/usr.bin/indent/token-comma.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-comma.0.pro @@ -0,0 +1,8 @@ +/* $NetBSD: token-comma.0.pro,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * TODO: Explain the command line options of the test. + */ + +/* TODO: Add some command line options */ diff --git a/usr.bin/indent/token-comma.0.stdout b/usr.bin/indent/token-comma.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-comma.0.stdout @@ -0,0 +1,17 @@ +/* $NetBSD: token-comma.0.stdout,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for the comma, which is used in the following contexts: + * + * The binary operator ',' inserts a sequence point between the evaluation of + * its operands. + * + * The parameters of a function declaration or a macro definition are + * separated by a comma. + * + * The arguments of a function call expression or a macro invocation are + * separated by a comma. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-comment.0 b/usr.bin/indent/token-comment.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-comment.0 @@ -0,0 +1,16 @@ +/* $NetBSD: token-comment.0,v 1.2 2021/03/14 00:50:39 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for formatting comments. C99 defines block comments and end-of-line + * comments. Indent further distinguishes box comments that are a special + * kind of block comments. + */ + +/* TODO: Add some code to be formatted. */ + +/* 456789 123456789 123456789 123456789 123456789 123456789 123456789 12345 */ +/* 456789 123456789 123456789 123456789 123456789 123456789 123456789 123456 */ +/* 456789 123456789 123456789 123456789 123456789 123456789 123456789 1234567 */ +/* 456789 123456789 123456789 123456789 123456789 123456789 123456789 12345678 */ +/* 456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 */ diff --git a/usr.bin/indent/token-comment.0.pro b/usr.bin/indent/token-comment.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-comment.0.pro @@ -0,0 +1,8 @@ +/* $NetBSD: token-comment.0.pro,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * TODO: Explain the command line options of the test. + */ + +/* TODO: Add some command line options */ diff --git a/usr.bin/indent/token-comment.0.stdout b/usr.bin/indent/token-comment.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-comment.0.stdout @@ -0,0 +1,21 @@ +/* $NetBSD: token-comment.0.stdout,v 1.3 2021/03/14 01:34:13 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for formatting comments. C99 defines block comments and end-of-line + * comments. Indent further distinguishes box comments that are a special + * kind of block comments. + */ + +/* TODO: Add some code to be formatted. */ + +/* 456789 123456789 123456789 123456789 123456789 123456789 123456789 12345 */ +/* 456789 123456789 123456789 123456789 123456789 123456789 123456789 123456 */ +/* 456789 123456789 123456789 123456789 123456789 123456789 123456789 1234567 */ +/* + * 456789 123456789 123456789 123456789 123456789 123456789 123456789 12345678 + */ +/* + * 456789 123456789 123456789 123456789 123456789 123456789 123456789 + * 123456789 + */ diff --git a/usr.bin/indent/token-decl.0 b/usr.bin/indent/token-decl.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-decl.0 @@ -0,0 +1,12 @@ +/* $NetBSD: token-decl.0,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for declarations. + * + * Indent distinguishes global and local declarations. + * + * Declarations can be for functions or for variables. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-decl.0.pro b/usr.bin/indent/token-decl.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-decl.0.pro @@ -0,0 +1,8 @@ +/* $NetBSD: token-decl.0.pro,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * TODO: Explain the command line options of the test. + */ + +/* TODO: Add some command line options */ diff --git a/usr.bin/indent/token-decl.0.stdout b/usr.bin/indent/token-decl.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-decl.0.stdout @@ -0,0 +1,12 @@ +/* $NetBSD: token-decl.0.stdout,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for declarations. + * + * Indent distinguishes global and local declarations. + * + * Declarations can be for functions or for variables. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-do_stmt.0 b/usr.bin/indent/token-do_stmt.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-do_stmt.0 @@ -0,0 +1,8 @@ +/* $NetBSD: token-do_stmt.0,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for do-while statements. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-do_stmt.0.pro b/usr.bin/indent/token-do_stmt.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-do_stmt.0.pro @@ -0,0 +1,8 @@ +/* $NetBSD: token-do_stmt.0.pro,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * TODO: Explain the command line options of the test. + */ + +/* TODO: Add some command line options */ diff --git a/usr.bin/indent/token-do_stmt.0.stdout b/usr.bin/indent/token-do_stmt.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-do_stmt.0.stdout @@ -0,0 +1,8 @@ +/* $NetBSD: token-do_stmt.0.stdout,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for do-while statements. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-end_of_file.0 b/usr.bin/indent/token-end_of_file.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-end_of_file.0 @@ -0,0 +1,8 @@ +/* $NetBSD: token-end_of_file.0,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for the end of a file. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-end_of_file.0.pro b/usr.bin/indent/token-end_of_file.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-end_of_file.0.pro @@ -0,0 +1,8 @@ +/* $NetBSD: token-end_of_file.0.pro,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * TODO: Explain the command line options of the test. + */ + +/* TODO: Add some command line options */ diff --git a/usr.bin/indent/token-end_of_file.0.stdout b/usr.bin/indent/token-end_of_file.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-end_of_file.0.stdout @@ -0,0 +1,8 @@ +/* $NetBSD: token-end_of_file.0.stdout,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for the end of a file. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-for_exprs.0 b/usr.bin/indent/token-for_exprs.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-for_exprs.0 @@ -0,0 +1,14 @@ +/* $NetBSD: token-for_exprs.0,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Test for 'for' loops. + * + * Most 'for' loops have 3 expressions in their head. Each of these + * expressions is optional though. + * + * When all 3 expressions are omitted, the 'for' loop is often called a + * 'forever' loop. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-for_exprs.0.pro b/usr.bin/indent/token-for_exprs.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-for_exprs.0.pro @@ -0,0 +1,8 @@ +/* $NetBSD: token-for_exprs.0.pro,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * TODO: Explain the command line options of the test. + */ + +/* TODO: Add some command line options */ diff --git a/usr.bin/indent/token-for_exprs.0.stdout b/usr.bin/indent/token-for_exprs.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-for_exprs.0.stdout @@ -0,0 +1,14 @@ +/* $NetBSD: token-for_exprs.0.stdout,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Test for 'for' loops. + * + * Most 'for' loops have 3 expressions in their head. Each of these + * expressions is optional though. + * + * When all 3 expressions are omitted, the 'for' loop is often called a + * 'forever' loop. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-form_feed.0 b/usr.bin/indent/token-form_feed.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-form_feed.0 @@ -0,0 +1,8 @@ +/* $NetBSD: token-form_feed.0,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for form feeds, which is a control character (C99 5.2.1p3). + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-form_feed.0.pro b/usr.bin/indent/token-form_feed.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-form_feed.0.pro @@ -0,0 +1,8 @@ +/* $NetBSD: token-form_feed.0.pro,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * TODO: Explain the command line options of the test. + */ + +/* TODO: Add some command line options */ diff --git a/usr.bin/indent/token-form_feed.0.stdout b/usr.bin/indent/token-form_feed.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-form_feed.0.stdout @@ -0,0 +1,8 @@ +/* $NetBSD: token-form_feed.0.stdout,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for form feeds, which is a control character (C99 5.2.1p3). + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-funcname.0 b/usr.bin/indent/token-funcname.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-funcname.0 @@ -0,0 +1,8 @@ +/* $NetBSD: token-funcname.0,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for function names. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-funcname.0.pro b/usr.bin/indent/token-funcname.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-funcname.0.pro @@ -0,0 +1,8 @@ +/* $NetBSD: token-funcname.0.pro,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * TODO: Explain the command line options of the test. + */ + +/* TODO: Add some command line options */ diff --git a/usr.bin/indent/token-funcname.0.stdout b/usr.bin/indent/token-funcname.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-funcname.0.stdout @@ -0,0 +1,8 @@ +/* $NetBSD: token-funcname.0.stdout,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for function names. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-ident.0 b/usr.bin/indent/token-ident.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-ident.0 @@ -0,0 +1,8 @@ +/* $NetBSD: token-ident.0,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for identifiers. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-ident.0.pro b/usr.bin/indent/token-ident.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-ident.0.pro @@ -0,0 +1,8 @@ +/* $NetBSD: token-ident.0.pro,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * TODO: Explain the command line options of the test. + */ + +/* TODO: Add some command line options */ diff --git a/usr.bin/indent/token-ident.0.stdout b/usr.bin/indent/token-ident.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-ident.0.stdout @@ -0,0 +1,8 @@ +/* $NetBSD: token-ident.0.stdout,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for identifiers. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-if_expr.0 b/usr.bin/indent/token-if_expr.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-if_expr.0 @@ -0,0 +1,8 @@ +/* $NetBSD: token-if_expr.0,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for 'if' followed by a parenthesized expression. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-if_expr.0.pro b/usr.bin/indent/token-if_expr.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-if_expr.0.pro @@ -0,0 +1,8 @@ +/* $NetBSD: token-if_expr.0.pro,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * TODO: Explain the command line options of the test. + */ + +/* TODO: Add some command line options */ diff --git a/usr.bin/indent/token-if_expr.0.stdout b/usr.bin/indent/token-if_expr.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-if_expr.0.stdout @@ -0,0 +1,8 @@ +/* $NetBSD: token-if_expr.0.stdout,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for 'if' followed by a parenthesized expression. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-if_expr_stmt.0 b/usr.bin/indent/token-if_expr_stmt.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-if_expr_stmt.0 @@ -0,0 +1,13 @@ +/* $NetBSD: token-if_expr_stmt.0,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for 'if' followed by a parenthesized expression and a statement. + * + * At this point, the 'if' statement is not necessarily complete, it can be + * completed with the keyword 'else' followed by a statement. + * + * Any token other than 'else' completes the 'if' statement. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-if_expr_stmt.0.pro b/usr.bin/indent/token-if_expr_stmt.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-if_expr_stmt.0.pro @@ -0,0 +1,8 @@ +/* $NetBSD: token-if_expr_stmt.0.pro,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * TODO: Explain the command line options of the test. + */ + +/* TODO: Add some command line options */ diff --git a/usr.bin/indent/token-if_expr_stmt.0.stdout b/usr.bin/indent/token-if_expr_stmt.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-if_expr_stmt.0.stdout @@ -0,0 +1,13 @@ +/* $NetBSD: token-if_expr_stmt.0.stdout,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for 'if' followed by a parenthesized expression and a statement. + * + * At this point, the 'if' statement is not necessarily complete, it can be + * completed with the keyword 'else' followed by a statement. + * + * Any token other than 'else' completes the 'if' statement. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-if_expr_stmt_else.0 b/usr.bin/indent/token-if_expr_stmt_else.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-if_expr_stmt_else.0 @@ -0,0 +1,11 @@ +/* $NetBSD: token-if_expr_stmt_else.0,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for 'if' followed by a parenthesized expression, a statement and the + * keyword 'else'. + * + * At this point, the statement needs to be completed with another statement. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-if_expr_stmt_else.0.pro b/usr.bin/indent/token-if_expr_stmt_else.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-if_expr_stmt_else.0.pro @@ -0,0 +1,8 @@ +/* $NetBSD: token-if_expr_stmt_else.0.pro,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * TODO: Explain the command line options of the test. + */ + +/* TODO: Add some command line options */ diff --git a/usr.bin/indent/token-if_expr_stmt_else.0.stdout b/usr.bin/indent/token-if_expr_stmt_else.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-if_expr_stmt_else.0.stdout @@ -0,0 +1,11 @@ +/* $NetBSD: token-if_expr_stmt_else.0.stdout,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for 'if' followed by a parenthesized expression, a statement and the + * keyword 'else'. + * + * At this point, the statement needs to be completed with another statement. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-keyword_do.0 b/usr.bin/indent/token-keyword_do.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-keyword_do.0 @@ -0,0 +1,8 @@ +/* $NetBSD: token-keyword_do.0,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for the keyword 'do' that begins a do-while statement. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-keyword_do.0.pro b/usr.bin/indent/token-keyword_do.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-keyword_do.0.pro @@ -0,0 +1,8 @@ +/* $NetBSD: token-keyword_do.0.pro,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * TODO: Explain the command line options of the test. + */ + +/* TODO: Add some command line options */ diff --git a/usr.bin/indent/token-keyword_do.0.stdout b/usr.bin/indent/token-keyword_do.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-keyword_do.0.stdout @@ -0,0 +1,8 @@ +/* $NetBSD: token-keyword_do.0.stdout,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for the keyword 'do' that begins a do-while statement. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-keyword_do_else.0 b/usr.bin/indent/token-keyword_do_else.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-keyword_do_else.0 @@ -0,0 +1,10 @@ +/* $NetBSD: token-keyword_do_else.0,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for the keyword 'do' or 'else'. These two keywords are followed by + * a space. In contrast to 'for', 'if' and 'while', the space is not + * followed by a parenthesized expression. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-keyword_do_else.0.pro b/usr.bin/indent/token-keyword_do_else.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-keyword_do_else.0.pro @@ -0,0 +1,8 @@ +/* $NetBSD: token-keyword_do_else.0.pro,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * TODO: Explain the command line options of the test. + */ + +/* TODO: Add some command line options */ diff --git a/usr.bin/indent/token-keyword_do_else.0.stdout b/usr.bin/indent/token-keyword_do_else.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-keyword_do_else.0.stdout @@ -0,0 +1,10 @@ +/* $NetBSD: token-keyword_do_else.0.stdout,v 1.2 2021/03/14 01:34:13 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for the keyword 'do' or 'else'. These two keywords are followed by a + * space. In contrast to 'for', 'if' and 'while', the space is not followed + * by a parenthesized expression. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-keyword_else.0 b/usr.bin/indent/token-keyword_else.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-keyword_else.0 @@ -0,0 +1,12 @@ +/* $NetBSD: token-keyword_else.0,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for the keyword 'else'. + * + * When parsing nested incomplete 'if' statements, the problem of the + * 'dangling else' occurs. It is resolved by binding the 'else' to the + * innermost incomplete 'if' statement. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-keyword_else.0.pro b/usr.bin/indent/token-keyword_else.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-keyword_else.0.pro @@ -0,0 +1,8 @@ +/* $NetBSD: token-keyword_else.0.pro,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * TODO: Explain the command line options of the test. + */ + +/* TODO: Add some command line options */ diff --git a/usr.bin/indent/token-keyword_else.0.stdout b/usr.bin/indent/token-keyword_else.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-keyword_else.0.stdout @@ -0,0 +1,12 @@ +/* $NetBSD: token-keyword_else.0.stdout,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for the keyword 'else'. + * + * When parsing nested incomplete 'if' statements, the problem of the + * 'dangling else' occurs. It is resolved by binding the 'else' to the + * innermost incomplete 'if' statement. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-keyword_for_if_while.0 b/usr.bin/indent/token-keyword_for_if_while.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-keyword_for_if_while.0 @@ -0,0 +1,11 @@ +/* $NetBSD: token-keyword_for_if_while.0,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for the keywords 'for', 'if' and 'while'. These keywords have in + * common that they are followed by a space and a parenthesized statement + * head. For 'if' and 'while', this head is a single expression. For 'for', + * the head is 0 to 3 expressions, separated by semicolons. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-keyword_for_if_while.0.pro b/usr.bin/indent/token-keyword_for_if_while.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-keyword_for_if_while.0.pro @@ -0,0 +1,8 @@ +/* $NetBSD: token-keyword_for_if_while.0.pro,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * TODO: Explain the command line options of the test. + */ + +/* TODO: Add some command line options */ diff --git a/usr.bin/indent/token-keyword_for_if_while.0.stdout b/usr.bin/indent/token-keyword_for_if_while.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-keyword_for_if_while.0.stdout @@ -0,0 +1,11 @@ +/* $NetBSD: token-keyword_for_if_while.0.stdout,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for the keywords 'for', 'if' and 'while'. These keywords have in + * common that they are followed by a space and a parenthesized statement + * head. For 'if' and 'while', this head is a single expression. For 'for', + * the head is 0 to 3 expressions, separated by semicolons. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-keyword_struct_union_enum.0 b/usr.bin/indent/token-keyword_struct_union_enum.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-keyword_struct_union_enum.0 @@ -0,0 +1,8 @@ +/* $NetBSD: token-keyword_struct_union_enum.0,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for the keywords 'struct', 'union' and 'enum'. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-keyword_struct_union_enum.0.pro b/usr.bin/indent/token-keyword_struct_union_enum.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-keyword_struct_union_enum.0.pro @@ -0,0 +1,8 @@ +/* $NetBSD: token-keyword_struct_union_enum.0.pro,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * TODO: Explain the command line options of the test. + */ + +/* TODO: Add some command line options */ diff --git a/usr.bin/indent/token-keyword_struct_union_enum.0.stdout b/usr.bin/indent/token-keyword_struct_union_enum.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-keyword_struct_union_enum.0.stdout @@ -0,0 +1,8 @@ +/* $NetBSD: token-keyword_struct_union_enum.0.stdout,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for the keywords 'struct', 'union' and 'enum'. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-lbrace.0 b/usr.bin/indent/token-lbrace.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-lbrace.0 @@ -0,0 +1,15 @@ +/* $NetBSD: token-lbrace.0,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for the token '{'. + * + * It is used as the start marker of a block of statements. + * + * It is used in initializers. + * + * In macro arguments, a '{' is an ordinary character, it does not need to be + * balanced. This is in contrast to '(', which must be balanced with ')'. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-lbrace.0.pro b/usr.bin/indent/token-lbrace.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-lbrace.0.pro @@ -0,0 +1,8 @@ +/* $NetBSD: token-lbrace.0.pro,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * TODO: Explain the command line options of the test. + */ + +/* TODO: Add some command line options */ diff --git a/usr.bin/indent/token-lbrace.0.stdout b/usr.bin/indent/token-lbrace.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-lbrace.0.stdout @@ -0,0 +1,15 @@ +/* $NetBSD: token-lbrace.0.stdout,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for the token '{'. + * + * It is used as the start marker of a block of statements. + * + * It is used in initializers. + * + * In macro arguments, a '{' is an ordinary character, it does not need to be + * balanced. This is in contrast to '(', which must be balanced with ')'. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-lparen.0 b/usr.bin/indent/token-lparen.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-lparen.0 @@ -0,0 +1,18 @@ +/* $NetBSD: token-lparen.0,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for the token '(', which has several possible meanings. + * + * In an expression, it overrides the precedence rules by explicitly grouping + * a subexpression in parentheses. + * + * In an expression, it marks the beginning of a type cast or conversion. + * + * In a function call expression, it marks the beginning of the function + * arguments. + * + * In a type declaration, it marks the beginning of the function parameters. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-lparen.0.pro b/usr.bin/indent/token-lparen.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-lparen.0.pro @@ -0,0 +1,8 @@ +/* $NetBSD: token-lparen.0.pro,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * TODO: Explain the command line options of the test. + */ + +/* TODO: Add some command line options */ diff --git a/usr.bin/indent/token-lparen.0.stdout b/usr.bin/indent/token-lparen.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-lparen.0.stdout @@ -0,0 +1,18 @@ +/* $NetBSD: token-lparen.0.stdout,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for the token '(', which has several possible meanings. + * + * In an expression, it overrides the precedence rules by explicitly grouping + * a subexpression in parentheses. + * + * In an expression, it marks the beginning of a type cast or conversion. + * + * In a function call expression, it marks the beginning of the function + * arguments. + * + * In a type declaration, it marks the beginning of the function parameters. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-newline.0 b/usr.bin/indent/token-newline.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-newline.0 @@ -0,0 +1,17 @@ +/* $NetBSD: token-newline.0,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/*- + * Tests for the token '\n', which ends a line. + * + * A newline ends an end-of-line comment that has been started with '//'. + * + * When a line ends with a backslash immediately followed by '\n', these two + * characters are merged and continue the logical line. + * TODO: only in macros? + * TODO: in string literals as well? + * + * In other contexts, a newline is an ordinary space character. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-newline.0.pro b/usr.bin/indent/token-newline.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-newline.0.pro @@ -0,0 +1,8 @@ +/* $NetBSD: token-newline.0.pro,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * TODO: Explain the command line options of the test. + */ + +/* TODO: Add some command line options */ diff --git a/usr.bin/indent/token-newline.0.stdout b/usr.bin/indent/token-newline.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-newline.0.stdout @@ -0,0 +1,17 @@ +/* $NetBSD: token-newline.0.stdout,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/*- + * Tests for the token '\n', which ends a line. + * + * A newline ends an end-of-line comment that has been started with '//'. + * + * When a line ends with a backslash immediately followed by '\n', these two + * characters are merged and continue the logical line. + * TODO: only in macros? + * TODO: in string literals as well? + * + * In other contexts, a newline is an ordinary space character. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-period.0 b/usr.bin/indent/token-period.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-period.0 @@ -0,0 +1,12 @@ +/* $NetBSD: token-period.0,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for the token '.'. + * + * The '.' in numbers such as 3.14159265358979 is not a token '.'. + * + * The token '.' is used to access a member of a struct or union. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-period.0.pro b/usr.bin/indent/token-period.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-period.0.pro @@ -0,0 +1,8 @@ +/* $NetBSD: token-period.0.pro,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * TODO: Explain the command line options of the test. + */ + +/* TODO: Add some command line options */ diff --git a/usr.bin/indent/token-period.0.stdout b/usr.bin/indent/token-period.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-period.0.stdout @@ -0,0 +1,12 @@ +/* $NetBSD: token-period.0.stdout,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for the token '.'. + * + * The '.' in numbers such as 3.14159265358979 is not a token '.'. + * + * The token '.' is used to access a member of a struct or union. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-postfix_op.0 b/usr.bin/indent/token-postfix_op.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-postfix_op.0 @@ -0,0 +1,8 @@ +/* $NetBSD: token-postfix_op.0,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for the postfix increment and decrement operators '++' and '--'. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-postfix_op.0.pro b/usr.bin/indent/token-postfix_op.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-postfix_op.0.pro @@ -0,0 +1,8 @@ +/* $NetBSD: token-postfix_op.0.pro,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * TODO: Explain the command line options of the test. + */ + +/* TODO: Add some command line options */ diff --git a/usr.bin/indent/token-postfix_op.0.stdout b/usr.bin/indent/token-postfix_op.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-postfix_op.0.stdout @@ -0,0 +1,8 @@ +/* $NetBSD: token-postfix_op.0.stdout,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for the postfix increment and decrement operators '++' and '--'. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-preprocessing.0 b/usr.bin/indent/token-preprocessing.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-preprocessing.0 @@ -0,0 +1,36 @@ +/* $NetBSD: token-preprocessing.0,v 1.3 2021/03/13 13:04:13 rillig Exp $ */ +/* $FreeBSD$ */ + +/*- + * Tests for indenting preprocessing directives: + * + * #define + * #ifdef + * #pragma + * #line + */ + +#include +#include "local-header.h" + +#if 0 +#else +#endif + +#if 0 /* if comment */ +#else /* else comment */ +#endif /* endif comment */ + +#if 0 /* outer if comment */ +# if nested /* inner if comment */ +# else /* inner else comment */ +# endif /* inner endif comment */ +#endif /* outer endif comment */ + +#define multi_line_definition /* first line + * middle + * final line + */ actual_value + +#define comment_in_string_literal "/* no comment " +int this_is_an_ordinary_line_again; diff --git a/usr.bin/indent/token-preprocessing.0.pro b/usr.bin/indent/token-preprocessing.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-preprocessing.0.pro @@ -0,0 +1,8 @@ +/* $NetBSD: token-preprocessing.0.pro,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * TODO: Explain the command line options of the test. + */ + +/* TODO: Add some command line options */ diff --git a/usr.bin/indent/token-preprocessing.0.stdout b/usr.bin/indent/token-preprocessing.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-preprocessing.0.stdout @@ -0,0 +1,37 @@ +/* $NetBSD: token-preprocessing.0.stdout,v 1.4 2021/03/13 13:14:14 rillig Exp $ */ +/* $FreeBSD$ */ + +/*- + * Tests for indenting preprocessing directives: + * + * #define + * #ifdef + * #pragma + * #line + */ + +#include +#include "local-header.h" + +#if 0 +#else +#endif + +#if 0 /* if comment */ +#else /* else comment */ +#endif /* endif comment */ + +#if 0 /* outer if comment */ +/* $ XXX: The indentation is removed, which can get confusing */ +#if nested /* inner if comment */ +#else /* inner else comment */ +#endif /* inner endif comment */ +#endif /* outer endif comment */ + +#define multi_line_definition /* first line + * middle + * final line + */ actual_value + +#define comment_in_string_literal "/* no comment " +int this_is_an_ordinary_line_again; diff --git a/usr.bin/indent/token-question.0 b/usr.bin/indent/token-question.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-question.0 @@ -0,0 +1,8 @@ +/* $NetBSD: token-question.0,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for the '?:' operator. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-question.0.pro b/usr.bin/indent/token-question.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-question.0.pro @@ -0,0 +1,8 @@ +/* $NetBSD: token-question.0.pro,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * TODO: Explain the command line options of the test. + */ + +/* TODO: Add some command line options */ diff --git a/usr.bin/indent/token-question.0.stdout b/usr.bin/indent/token-question.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-question.0.stdout @@ -0,0 +1,8 @@ +/* $NetBSD: token-question.0.stdout,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for the '?:' operator. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-rbrace.0 b/usr.bin/indent/token-rbrace.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-rbrace.0 @@ -0,0 +1,8 @@ +/* $NetBSD: token-rbrace.0,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for the token '}', which ends the corresponding '{' token. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-rbrace.0.pro b/usr.bin/indent/token-rbrace.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-rbrace.0.pro @@ -0,0 +1,8 @@ +/* $NetBSD: token-rbrace.0.pro,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * TODO: Explain the command line options of the test. + */ + +/* TODO: Add some command line options */ diff --git a/usr.bin/indent/token-rbrace.0.stdout b/usr.bin/indent/token-rbrace.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-rbrace.0.stdout @@ -0,0 +1,8 @@ +/* $NetBSD: token-rbrace.0.stdout,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for the token '}', which ends the corresponding '{' token. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-rparen.0 b/usr.bin/indent/token-rparen.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-rparen.0 @@ -0,0 +1,8 @@ +/* $NetBSD: token-rparen.0,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for the token ')', which ends the corresponding ')' token. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-rparen.0.pro b/usr.bin/indent/token-rparen.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-rparen.0.pro @@ -0,0 +1,8 @@ +/* $NetBSD: token-rparen.0.pro,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * TODO: Explain the command line options of the test. + */ + +/* TODO: Add some command line options */ diff --git a/usr.bin/indent/token-rparen.0.stdout b/usr.bin/indent/token-rparen.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-rparen.0.stdout @@ -0,0 +1,8 @@ +/* $NetBSD: token-rparen.0.stdout,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for the token ')', which ends the corresponding ')' token. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-semicolon.0 b/usr.bin/indent/token-semicolon.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-semicolon.0 @@ -0,0 +1,14 @@ +/* $NetBSD: token-semicolon.0,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for the token ';'. + * + * The ';' ends a declaration. + * + * The ';' ends a statement. + * + * The ';' separates the 3 expressions in the head of the 'for' loop. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-semicolon.0.pro b/usr.bin/indent/token-semicolon.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-semicolon.0.pro @@ -0,0 +1,8 @@ +/* $NetBSD: token-semicolon.0.pro,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * TODO: Explain the command line options of the test. + */ + +/* TODO: Add some command line options */ diff --git a/usr.bin/indent/token-semicolon.0.stdout b/usr.bin/indent/token-semicolon.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-semicolon.0.stdout @@ -0,0 +1,14 @@ +/* $NetBSD: token-semicolon.0.stdout,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for the token ';'. + * + * The ';' ends a declaration. + * + * The ';' ends a statement. + * + * The ';' separates the 3 expressions in the head of the 'for' loop. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-stmt.0 b/usr.bin/indent/token-stmt.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-stmt.0 @@ -0,0 +1,8 @@ +/* $NetBSD: token-stmt.0,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for statements. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-stmt.0.pro b/usr.bin/indent/token-stmt.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-stmt.0.pro @@ -0,0 +1,8 @@ +/* $NetBSD: token-stmt.0.pro,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * TODO: Explain the command line options of the test. + */ + +/* TODO: Add some command line options */ diff --git a/usr.bin/indent/token-stmt.0.stdout b/usr.bin/indent/token-stmt.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-stmt.0.stdout @@ -0,0 +1,8 @@ +/* $NetBSD: token-stmt.0.stdout,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for statements. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-stmt_list.0 b/usr.bin/indent/token-stmt_list.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-stmt_list.0 @@ -0,0 +1,11 @@ +/* $NetBSD: token-stmt_list.0,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for lists of statements. + * + * Since C99, in such a statement list, statements can be intermixed with + * declarations in arbitrary ways. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-stmt_list.0.pro b/usr.bin/indent/token-stmt_list.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-stmt_list.0.pro @@ -0,0 +1,8 @@ +/* $NetBSD: token-stmt_list.0.pro,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * TODO: Explain the command line options of the test. + */ + +/* TODO: Add some command line options */ diff --git a/usr.bin/indent/token-stmt_list.0.stdout b/usr.bin/indent/token-stmt_list.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-stmt_list.0.stdout @@ -0,0 +1,11 @@ +/* $NetBSD: token-stmt_list.0.stdout,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for lists of statements. + * + * Since C99, in such a statement list, statements can be intermixed with + * declarations in arbitrary ways. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-storage_class.0 b/usr.bin/indent/token-storage_class.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-storage_class.0 @@ -0,0 +1,8 @@ +/* $NetBSD: token-storage_class.0,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for storage classes such as 'extern', 'static', but not 'typedef'. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-storage_class.0.pro b/usr.bin/indent/token-storage_class.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-storage_class.0.pro @@ -0,0 +1,8 @@ +/* $NetBSD: token-storage_class.0.pro,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * TODO: Explain the command line options of the test. + */ + +/* TODO: Add some command line options */ diff --git a/usr.bin/indent/token-storage_class.0.stdout b/usr.bin/indent/token-storage_class.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-storage_class.0.stdout @@ -0,0 +1,8 @@ +/* $NetBSD: token-storage_class.0.stdout,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for storage classes such as 'extern', 'static', but not 'typedef'. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-string_prefix.0 b/usr.bin/indent/token-string_prefix.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-string_prefix.0 @@ -0,0 +1,8 @@ +/* $NetBSD: token-string_prefix.0,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for strings of wide characters, which are prefixed with 'L'. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-string_prefix.0.pro b/usr.bin/indent/token-string_prefix.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-string_prefix.0.pro @@ -0,0 +1,8 @@ +/* $NetBSD: token-string_prefix.0.pro,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * TODO: Explain the command line options of the test. + */ + +/* TODO: Add some command line options */ diff --git a/usr.bin/indent/token-string_prefix.0.stdout b/usr.bin/indent/token-string_prefix.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-string_prefix.0.stdout @@ -0,0 +1,8 @@ +/* $NetBSD: token-string_prefix.0.stdout,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for strings of wide characters, which are prefixed with 'L'. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-switch_expr.0 b/usr.bin/indent/token-switch_expr.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-switch_expr.0 @@ -0,0 +1,8 @@ +/* $NetBSD: token-switch_expr.0,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for the keyword 'switch' followed by a parenthesized expression. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-switch_expr.0.pro b/usr.bin/indent/token-switch_expr.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-switch_expr.0.pro @@ -0,0 +1,8 @@ +/* $NetBSD: token-switch_expr.0.pro,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * TODO: Explain the command line options of the test. + */ + +/* TODO: Add some command line options */ diff --git a/usr.bin/indent/token-switch_expr.0.stdout b/usr.bin/indent/token-switch_expr.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-switch_expr.0.stdout @@ -0,0 +1,8 @@ +/* $NetBSD: token-switch_expr.0.stdout,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for the keyword 'switch' followed by a parenthesized expression. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-type_def.0 b/usr.bin/indent/token-type_def.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-type_def.0 @@ -0,0 +1,8 @@ +/* $NetBSD: token-type_def.0,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for the keyword 'typedef'. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-type_def.0.pro b/usr.bin/indent/token-type_def.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-type_def.0.pro @@ -0,0 +1,8 @@ +/* $NetBSD: token-type_def.0.pro,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * TODO: Explain the command line options of the test. + */ + +/* TODO: Add some command line options */ diff --git a/usr.bin/indent/token-type_def.0.stdout b/usr.bin/indent/token-type_def.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-type_def.0.stdout @@ -0,0 +1,8 @@ +/* $NetBSD: token-type_def.0.stdout,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for the keyword 'typedef'. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-unary_op.0 b/usr.bin/indent/token-unary_op.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-unary_op.0 @@ -0,0 +1,8 @@ +/* $NetBSD: token-unary_op.0,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for unary operators, such as '+', '-', '*', '&'. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-unary_op.0.pro b/usr.bin/indent/token-unary_op.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-unary_op.0.pro @@ -0,0 +1,8 @@ +/* $NetBSD: token-unary_op.0.pro,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * TODO: Explain the command line options of the test. + */ + +/* TODO: Add some command line options */ diff --git a/usr.bin/indent/token-unary_op.0.stdout b/usr.bin/indent/token-unary_op.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-unary_op.0.stdout @@ -0,0 +1,8 @@ +/* $NetBSD: token-unary_op.0.stdout,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for unary operators, such as '+', '-', '*', '&'. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-while_expr.0 b/usr.bin/indent/token-while_expr.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-while_expr.0 @@ -0,0 +1,8 @@ +/* $NetBSD: token-while_expr.0,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for the keyword 'while', followed by a parenthesized expression. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/token-while_expr.0.pro b/usr.bin/indent/token-while_expr.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-while_expr.0.pro @@ -0,0 +1,8 @@ +/* $NetBSD: token-while_expr.0.pro,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * TODO: Explain the command line options of the test. + */ + +/* TODO: Add some command line options */ diff --git a/usr.bin/indent/token-while_expr.0.stdout b/usr.bin/indent/token-while_expr.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/token-while_expr.0.stdout @@ -0,0 +1,8 @@ +/* $NetBSD: token-while_expr.0.stdout,v 1.1 2021/03/12 00:13:06 rillig Exp $ */ +/* $FreeBSD$ */ + +/* + * Tests for the keyword 'while', followed by a parenthesized expression. + */ + +/* TODO: Add some code to be formatted. */ diff --git a/usr.bin/indent/types_from_file.0 b/usr.bin/indent/types_from_file.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/types_from_file.0 @@ -0,0 +1,4 @@ +/* $NetBSD: types_from_file.0,v 1.1 2019/04/04 15:27:35 kamil Exp $ */ +/* $FreeBSD: head/usr.bin/indent/tests/types_from_file.0 313544 2017-02-10 09:31:39Z pstef $ */ +/* See r303735 */ +void t(a *x, b *y, c *z); diff --git a/usr.bin/indent/types_from_file.0.list b/usr.bin/indent/types_from_file.0.list new file mode 100644 --- /dev/null +++ b/usr.bin/indent/types_from_file.0.list @@ -0,0 +1,2 @@ +b +a \ No newline at end of file diff --git a/usr.bin/indent/types_from_file.0.pro b/usr.bin/indent/types_from_file.0.pro new file mode 100644 --- /dev/null +++ b/usr.bin/indent/types_from_file.0.pro @@ -0,0 +1,3 @@ +/* $NetBSD: types_from_file.0.pro,v 1.1 2019/04/04 15:27:35 kamil Exp $ */ +/* $FreeBSD: head/usr.bin/indent/tests/types_from_file.0.pro 314613 2017-03-03 20:15:22Z ngie $ */ +-Utypes_from_file.0.list diff --git a/usr.bin/indent/types_from_file.0.stdout b/usr.bin/indent/types_from_file.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/types_from_file.0.stdout @@ -0,0 +1,4 @@ +/* $NetBSD: types_from_file.0.stdout,v 1.1 2019/04/04 15:27:35 kamil Exp $ */ +/* $FreeBSD: head/usr.bin/indent/tests/types_from_file.0.stdout 313544 2017-02-10 09:31:39Z pstef $ */ +/* See r303735 */ +void t(a *x, b *y, c * z); diff --git a/usr.bin/indent/wchar.0 b/usr.bin/indent/wchar.0 new file mode 100644 --- /dev/null +++ b/usr.bin/indent/wchar.0 @@ -0,0 +1,7 @@ +/* $NetBSD: wchar.0,v 1.1 2019/04/04 15:27:35 kamil Exp $ */ +/* $FreeBSD: head/usr.bin/indent/tests/wchar.0 313544 2017-02-10 09:31:39Z pstef $ */ +/* See r309220 */ +#include + +wchar_t *x = L"test"; +wchar_t y = L't'; diff --git a/usr.bin/indent/wchar.0.stdout b/usr.bin/indent/wchar.0.stdout new file mode 100644 --- /dev/null +++ b/usr.bin/indent/wchar.0.stdout @@ -0,0 +1,7 @@ +/* $NetBSD: wchar.0.stdout,v 1.1 2019/04/04 15:27:35 kamil Exp $ */ +/* $FreeBSD: head/usr.bin/indent/tests/wchar.0.stdout 313544 2017-02-10 09:31:39Z pstef $ */ +/* See r309220 */ +#include + +wchar_t *x = L"test"; +wchar_t y = L't'; diff --git a/usr.bin/infocmp/t_terminfo.sh b/usr.bin/infocmp/t_terminfo.sh old mode 100755 new mode 100644 diff --git a/usr.bin/jot/t_jot.sh b/usr.bin/jot/t_jot.sh old mode 100755 new mode 100644 diff --git a/usr.bin/ld/t_script.sh b/usr.bin/ld/t_script.sh old mode 100755 new mode 100644 diff --git a/usr.bin/ld/t_section.sh b/usr.bin/ld/t_section.sh old mode 100755 new mode 100644 diff --git a/usr.bin/locale/Makefile b/usr.bin/locale/Makefile new file mode 100644 --- /dev/null +++ b/usr.bin/locale/Makefile @@ -0,0 +1,8 @@ +# $NetBSD: Makefile,v 1.1 2020/07/03 04:25:28 jruoho Exp $ + +.include + +TESTSDIR= ${TESTSBASE}/usr.bin/locale +TESTS_SH= t_locale + +.include diff --git a/usr.bin/locale/t_locale.sh b/usr.bin/locale/t_locale.sh new file mode 100644 --- /dev/null +++ b/usr.bin/locale/t_locale.sh @@ -0,0 +1,43 @@ +# $NetBSD: t_locale.sh,v 1.1 2020/07/03 04:25:28 jruoho Exp $ +# +# Copyright (c) 2020 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Jukka Ruohonen. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# +atf_test_case nonexistent +nonexistent_head() { + atf_set "descr" "Check that locale(1) does not report " \ + "success for a nonexistent locale (PR lib/54692)" +} + +nonexistent_body() { + atf_expect_fail "PR lib/54692" + atf_check -s exit:1 -e ignore -o empty -x "locale nonexistent" +} + +atf_init_test_cases() { + atf_add_test_case nonexistent +} diff --git a/usr.bin/m4/t_m4.sh b/usr.bin/m4/t_m4.sh old mode 100755 new mode 100644 diff --git a/usr.bin/make/Makefile b/usr.bin/make/Makefile --- a/usr.bin/make/Makefile +++ b/usr.bin/make/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.2 2014/08/22 16:45:32 apb Exp $ +# $NetBSD: Makefile,v 1.3 2021/04/17 11:21:17 rillig Exp $ # The tests for make(1) are maintained in src/usr.bin/make/unit-tests # (UNIT_TESTS_DISTDIR). We copy them verbatim to ${FILESDIR}/unit-tests @@ -28,11 +28,10 @@ DISTFILES!= (cd ${UNIT_TESTS_DISTDIR} && echo Makefile *.mk *.exp) # Instruct bsd.files.mk to make the copies -.for f in ${DISTFILES} -_file := ${UNIT_TESTS_DISTDIR}/${f} -FILES+= ${UNIT_TESTS_DISTDIR}/${f} -FILESNAME_${_file} := ${_file:T} -FILESDIR_${_file} := ${UNIT_TESTS_DIR} +.for f in ${DISTFILES:S,^,${UNIT_TESTS_DISTDIR}/,} +FILES+= ${f} +FILESNAME_${f}= ${f:T} +FILESDIR_${f}= ${UNIT_TESTS_DIR} .endfor .include diff --git a/usr.bin/make/t_make.sh b/usr.bin/make/t_make.sh old mode 100755 new mode 100644 --- a/usr.bin/make/t_make.sh +++ b/usr.bin/make/t_make.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_make.sh,v 1.7 2015/01/27 12:57:14 martin Exp $ +# $NetBSD: t_make.sh,v 1.15 2021/04/17 11:36:34 rillig Exp $ # # Copyright (c) 2008, 2010, 2014 The NetBSD Foundation, Inc. # All rights reserved. @@ -35,11 +35,7 @@ # 49085 - adjust for more concrete PR if there is one case ${makename} in escape) atf_expect_fail "see PR toolchain/49085";; - impsrc) atf_expect_fail "see PR toolchain/49085";; - phony*) atf_expect_fail "see PR toolchain/49085";; posix1) atf_expect_fail "see PR toolchain/49085";; - suffixes) atf_expect_fail "see PR toolchain/49085" - atf_fail "this uses up all memory and then fails";; esac local srcdir="$(atf_get_srcdir)" @@ -57,13 +53,10 @@ { local atfname="${1}"; shift # e.g. foo_bar local makename="${1}"; shift # e.g. foo-bar - local descr="${1}"; shift atf_test_case "${atfname}" eval "${atfname}_head() { \ - if [ -n '${descr}' ]; then \ - atf_set descr '${descr}'; \ - fi \ + atf_set require.user unprivileged; \ }" eval "${atfname}_body() { \ run_and_check '${atfname}' '${makename}'; \ @@ -74,12 +67,23 @@ { local filename basename atfname descr - for filename in "$(atf_get_srcdir)"/unit-tests/*.mk ; do - basename="${filename##*/}" - basename="${basename%.mk}" - atfname="$(echo "${basename}" | tr "x-" "x_")" - descr='' # XXX - test_case "${atfname}" "${basename}" "${descr}" - atf_add_test_case "${atfname}" + for filename in "$(atf_get_srcdir)"/unit-tests/*.mk; do + basename="${filename##*/}" + basename="${basename%.mk}" + + # skip files that are not test cases on their own + case "${basename}" in + include-sub*) continue;; + esac + + atfname=${basename} + while :; do + case "${atfname}" in + (*-*) atfname=${atfname%-*}_${atfname##*-};; + (*) break;; + esac + done + test_case "${atfname}" "${basename}" + atf_add_test_case "${atfname}" done } diff --git a/usr.bin/mixerctl/Makefile b/usr.bin/mixerctl/Makefile new file mode 100644 --- /dev/null +++ b/usr.bin/mixerctl/Makefile @@ -0,0 +1,8 @@ +# $NetBSD: Makefile,v 1.1 2017/01/02 15:40:09 christos Exp $ + +.include + +TESTSDIR= ${TESTSBASE}/usr.bin/mixerctl +TESTS_SH= t_mixerctl + +.include diff --git a/usr.bin/mixerctl/t_mixerctl.sh b/usr.bin/mixerctl/t_mixerctl.sh old mode 100755 new mode 100644 --- a/usr.bin/mixerctl/t_mixerctl.sh +++ b/usr.bin/mixerctl/t_mixerctl.sh @@ -1,12 +1,44 @@ -# $NetBSD: t_mixerctl.sh,v 1.1 2017/01/02 15:40:09 christos Exp $ +# $NetBSD: t_mixerctl.sh,v 1.10 2017/07/25 22:28:22 kre Exp $ + +audio_setup() { + # Open /dev/pad0 so we have a configured audio device. + # Do it in a way that guarantees the open happens before + # we proceed to the (/dev/null 2>&1 && + { { cat >/dev/null & } < /dev/pad0 ; } 2>/dev/null && padpid=$! + + (/dev/null 2>&1 || + atf_skip "no audio mixer available in kernel" +} atf_test_case noargs_usage noargs_usage_head() { atf_set "descr" "Ensure mixerctl(1) with no args prints a usage message" } noargs_usage_body() { + audio_setup + atf_check -s exit:0 -o not-empty -e ignore \ mixerctl + + ${padpid+kill -HUP ${padpid}} 2>/dev/null || : } atf_test_case showvalue @@ -14,10 +46,14 @@ atf_set "descr" "Ensure mixerctl(1) can print the value for all variables" } showvalue_body() { + audio_setup + for var in $(mixerctl -a | awk -F= '{print $1}'); do atf_check -s exit:0 -e ignore -o match:"^${var}=" \ mixerctl ${var} done + + ${padpid+kill -HUP ${padpid}} 2>/dev/null || : } atf_test_case nflag @@ -25,13 +61,17 @@ atf_set "descr" "Ensure 'mixerctl -n' actually suppresses some output" } nflag_body() { - varname="$(mixerctl -a | head -1 | awk -F= '{print $1}')" + audio_setup + + varname="$(mixerctl -a | sed -e 's/=.*//' -e q)" atf_check -s exit:0 -o match:"${varname}" -e ignore \ mixerctl ${varname} atf_check -s exit:0 -o not-match:"${varname}" -e ignore \ mixerctl -n ${varname} + + ${padpid+kill -HUP ${padpid}} 2>/dev/null || : } atf_test_case nonexistant_device @@ -40,7 +80,7 @@ } nonexistant_device_body() { atf_check -s not-exit:0 -o ignore -e match:"No such file" \ - mixerctl -d /a/b/c/d/e + mixerctl -a -d /a/b/c/d/e } atf_init_test_cases() { diff --git a/usr.bin/mkdep/Makefile b/usr.bin/mkdep/Makefile --- a/usr.bin/mkdep/Makefile +++ b/usr.bin/mkdep/Makefile @@ -1,9 +1,18 @@ -# $NetBSD: Makefile,v 1.1 2011/05/30 18:14:11 njoly Exp $ +# $NetBSD: Makefile,v 1.3 2021/08/20 06:36:10 rillig Exp $ .include TESTSDIR= ${TESTSBASE}/usr.bin/mkdep -TESTS_SH= t_mkdep +TESTS_SH= t_findcc +TESTS_SH+= t_mkdep + +BINDIR= ${TESTSDIR} +PROG= h_findcc +.PATH: ${NETBSDSRCDIR}/usr.bin/mkdep +SRCS= h_findcc.c findcc.c +CPPFLAGS+= -I${NETBSDSRCDIR}/usr.bin/mkdep +MAN.h_findcc= # none +WARNS= 6 .include diff --git a/usr.bin/mkdep/h_findcc.c b/usr.bin/mkdep/h_findcc.c new file mode 100644 --- /dev/null +++ b/usr.bin/mkdep/h_findcc.c @@ -0,0 +1,16 @@ +/* $NetBSD: h_findcc.c,v 1.1 2021/08/11 20:42:26 rillig Exp $ */ + +#include +#include + +#include "findcc.h" + +int +main(int argc, char **argv) +{ + const char *cc; + + assert(argc == 2); + cc = findcc(argv[1]); + printf("%s\n", cc != NULL ? cc : "(not found)"); +} diff --git a/usr.bin/mkdep/t_findcc.sh b/usr.bin/mkdep/t_findcc.sh new file mode 100644 --- /dev/null +++ b/usr.bin/mkdep/t_findcc.sh @@ -0,0 +1,182 @@ +# $NetBSD: t_findcc.sh,v 1.3 2021/08/20 06:36:10 rillig Exp $ +# +# Copyright (c) 2021 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Roland Illig. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +n=' +' + +# A plain program name is searched in the PATH. Since in this case, the +# environment is empty, nothing is found. +# +atf_test_case base_not_found +base_not_found_body() { + atf_check -o "inline:(not found)$n" \ + env -i \ + "$(atf_get_srcdir)"/h_findcc 'echo' +} + +# A plain program name is searched in the PATH and, in this example, it is +# found in '/bin'. +# +atf_test_case base_found +base_found_body() { + atf_check -o "inline:/bin/echo$n" \ + env -i PATH='/bin:/nonexistent' \ + "$(atf_get_srcdir)"/h_findcc 'echo' +} + +# A plain program name is searched in the PATH and, in this example, it is +# found in '/bin', which comes second in the PATH. +# +atf_test_case base_found_second +base_found_second_body() { + atf_check -o "inline:/bin/echo$n" \ + env -i PATH='/nonexistent:/bin' \ + "$(atf_get_srcdir)"/h_findcc 'echo' +} + +# A plain program name is searched in the PATH and, in this example, it is +# found in './bin', a relative path in the PATH, which is rather unusual in +# practice. +# +atf_test_case base_found_reldir +base_found_reldir_body() { + mkdir bin + echo '#! /bin/sh' > 'bin/reldir-echo' + chmod +x 'bin/reldir-echo' + + atf_check -o "inline:bin/reldir-echo$n" \ + env -i PATH='/nonexistent:bin' \ + "$(atf_get_srcdir)"/h_findcc 'reldir-echo' +} + +# The C compiler can be specified as a program with one or more arguments. +# If the program name is a plain name without any slash, the argument is +# discarded. +# +# XXX: Discarding the arguments feels unintended. +# +atf_test_case base_arg_found +base_arg_found_body() { + atf_check -o "inline:/bin/echo$n" \ + env -i PATH='/bin:/nonexistent' \ + "$(atf_get_srcdir)"/h_findcc 'echo arg' +} + + +# If the program name contains a slash, no matter where, the program is not +# searched in the PATH. This is the same behavior as in /bin/sh. +# +atf_test_case rel_not_found +rel_not_found_body() { + atf_check -o "inline:(not found)$n" \ + env -i PATH='/' \ + "$(atf_get_srcdir)"/h_findcc 'bin/echo' +} + +# If the program name contains a slash, no matter where, the program is not +# searched in the PATH. This is the same behavior as in /bin/sh. +# +atf_test_case rel_found +rel_found_body() { + mkdir bin + echo '#! /bin/sh' > bin/echo + chmod +x bin/echo + + atf_check -o "inline:bin/echo$n" \ + env -i PATH='/' \ + "$(atf_get_srcdir)"/h_findcc 'bin/echo' +} + +# If the program name contains a slash in the middle and has additional +# arguments, the arguments are discarded. +# +# XXX: Discarding the arguments feels unintended. +# +atf_test_case rel_arg_found +rel_arg_found_body() { + mkdir bin + echo '#! /bin/sh' > bin/echo + chmod +x bin/echo + + atf_check -o "inline:bin/echo$n" \ + env -i PATH='/' \ + "$(atf_get_srcdir)"/h_findcc 'bin/echo arg' +} + + +atf_test_case abs_not_found +abs_not_found_body() { + atf_check -o "inline:(not found)$n" \ + env -i \ + "$(atf_get_srcdir)"/h_findcc "$PWD/nonexistent/echo" +} + +atf_test_case abs_found +abs_found_body() { + mkdir bin + echo '#! /bin/sh' > bin/echo + chmod +x bin/echo + + atf_check -o "inline:$PWD/bin/echo$n" \ + env -i \ + "$(atf_get_srcdir)"/h_findcc "$PWD/bin/echo" +} + +# If the program name is an absolute pathname, the arguments are discarded. +# +# XXX: Discarding the arguments feels unintended. +# +atf_test_case abs_arg_found +abs_arg_found_body() { + mkdir bin + echo '#! /bin/sh' > bin/echo + chmod +x bin/echo + + atf_check -o "inline:$PWD/bin/echo$n" \ + env -i \ + "$(atf_get_srcdir)"/h_findcc "$PWD/bin/echo arg" +} + + +atf_init_test_cases() { + atf_add_test_case base_not_found + atf_add_test_case base_found + atf_add_test_case base_found_second + atf_add_test_case base_found_reldir + atf_add_test_case base_arg_found + + atf_add_test_case rel_not_found + atf_add_test_case rel_found + atf_add_test_case rel_arg_found + + atf_add_test_case abs_not_found + atf_add_test_case abs_found + atf_add_test_case abs_arg_found +} diff --git a/usr.bin/mkdep/t_mkdep.sh b/usr.bin/mkdep/t_mkdep.sh old mode 100755 new mode 100644 diff --git a/usr.bin/nbperf/Makefile b/usr.bin/nbperf/Makefile --- a/usr.bin/nbperf/Makefile +++ b/usr.bin/nbperf/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.1 2012/07/22 20:38:20 joerg Exp $ +# $NetBSD: Makefile,v 1.2 2018/05/31 09:08:26 mrg Exp $ NOMAN= # defined @@ -10,6 +10,7 @@ SCRIPTSDIR= ${TESTSDIR} SCRIPTS+= h_nbperf +CLEANFILES+= h_nbperf FILESDIR= ${TESTSDIR} FILES= hash_driver.c diff --git a/usr.bin/nbperf/h_nbperf.sh b/usr.bin/nbperf/h_nbperf.sh old mode 100755 new mode 100644 diff --git a/usr.bin/nbperf/t_nbperf.sh b/usr.bin/nbperf/t_nbperf.sh old mode 100755 new mode 100644 --- a/usr.bin/nbperf/t_nbperf.sh +++ b/usr.bin/nbperf/t_nbperf.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_nbperf.sh,v 1.3 2014/04/30 21:04:21 joerg Exp $ +# $NetBSD: t_nbperf.sh,v 1.5 2021/02/14 01:27:33 joerg Exp $ # # Copyright (c) 2012 The NetBSD Foundation, Inc. # All rights reserved. @@ -27,7 +27,7 @@ cleanup() { - rm -f reference.txt hash.c hash.map testprog + rm -f reference.txt input.txt hash.c hash.map testprog } atf_test_case chm @@ -38,7 +38,7 @@ atf_set "require.progs" "cc" } chm_body() -{ +{ for n in 4 32 128 1024 65536; do seq 0 $(($n - 1)) > reference.txt atf_check -o file:reference.txt \ @@ -54,6 +54,32 @@ cleanup } +atf_test_case chm_fudged +chm_fudged_head() +{ + atf_set "descr" "Checks chm algorithm with fudged hash" + atf_set "require.progs" "cc" +} +chm_fudged_body() +{ + seq 0 11 > reference.txt + seq 1 12 > input.txt + + atf_check -o file:reference.txt \ + $(atf_get_srcdir)/h_nbperf input.txt "chm -p" cat \ + 12 $(atf_get_srcdir)/hash_driver.c + atf_check -s exit:1 fgrep -q '^=' hash.c + + atf_check -o file:reference.txt \ + $(atf_get_srcdir)/h_nbperf input.txt "chm -f -p" cat \ + 12 $(atf_get_srcdir)/hash_driver.c + atf_check -s exit:0 fgrep -q '^=' hash.c +} +chm_fudged_clean() +{ + cleanup +} + atf_test_case chm3 chm3_head() { @@ -78,26 +104,102 @@ cleanup } -atf_test_case bdz -bdz_head() +atf_test_case chm3_fudged +chm3_fudged_head() +{ + atf_set "descr" "Checks chm3 algorithm with fudged hash" + atf_set "require.progs" "cc" +} +chm3_fudged_body() +{ + seq 0 9 > reference.txt + seq 1 10 > input.txt + + atf_check -o file:reference.txt \ + $(atf_get_srcdir)/h_nbperf input.txt "chm3 -p" cat \ + 10 $(atf_get_srcdir)/hash_driver.c + atf_check -s exit:1 fgrep -q '^=' hash.c + + atf_check -o file:reference.txt \ + $(atf_get_srcdir)/h_nbperf input.txt "chm3 -f -p" cat \ + 10 $(atf_get_srcdir)/hash_driver.c + atf_check -s exit:0 fgrep -q '^= (' hash.c + atf_check -s exit:0 fgrep -q '^= 2' hash.c +} +chm3_fudged_clean() +{ + cleanup +} + +atf_test_case bpz +bpz_head() { - atf_set "descr" "Checks bdz algorithm" + atf_set "descr" "Checks bpz algorithm" atf_set "require.files" "/usr/share/dict/web2" atf_set "require.progs" "cc" } -bdz_body() -{ +bpz_body() +{ for n in 4 32 128 1024 65536 131072; do seq 0 $(($n - 1)) > reference.txt atf_check -o file:reference.txt \ - $(atf_get_srcdir)/h_nbperf /usr/share/dict/web2 bdz "sort -n" \ + $(atf_get_srcdir)/h_nbperf /usr/share/dict/web2 bpz "sort -n" \ $n $(atf_get_srcdir)/hash_driver.c atf_check -o file:hash.map \ - $(atf_get_srcdir)/h_nbperf /usr/share/dict/web2 bdz cat \ + $(atf_get_srcdir)/h_nbperf /usr/share/dict/web2 bpz cat \ $n $(atf_get_srcdir)/hash_driver.c done } -bdz_clean() +bpz_clean() +{ + cleanup +} + +atf_test_case bpz_fudged +bpz_fudged_head() +{ + atf_set "descr" "Checks bpz algorithm with fudged hash" + atf_set "require.progs" "cc" +} +bpz_fudged_body() +{ + seq 0 11 > reference.txt + seq 1 12 > input.txt + + atf_check -o file:reference.txt \ + $(atf_get_srcdir)/h_nbperf input.txt "bpz -p" "sort -n" \ + 12 $(atf_get_srcdir)/hash_driver.c + atf_check -s exit:1 fgrep -q '^=' hash.c + + atf_check -o file:reference.txt \ + $(atf_get_srcdir)/h_nbperf input.txt "bpz -f -p" "sort -n" \ + 12 $(atf_get_srcdir)/hash_driver.c + atf_check -s exit:0 fgrep -q '^= (' hash.c + atf_check -s exit:0 fgrep -q '^= 2' hash.c +} +bpz_fudged_clean() +{ + cleanup +} + +atf_test_case handle_dup +handle_dup_head() +{ + atf_set "descr" "Checks different algorithms deal with duplicates" + atf_set "require.progs" "cc" +} +handle_dup_body() +{ + seq 0 9 > reference.txt + echo 0 >> reference.txt + atf_check -s exit:1 -e match:"nbperf: Duplicate keys detected" \ + nbperf -a chm < reference.txt + atf_check -s exit:1 -e match:"nbperf: Duplicate keys detected" \ + nbperf -a chm3 < reference.txt + atf_check -s exit:1 -e match:"nbperf: Duplicate keys detected" \ + nbperf -a bpz < reference.txt +} +handle_dup_clean() { cleanup } @@ -105,6 +207,10 @@ atf_init_test_cases() { atf_add_test_case chm + atf_add_test_case chm_fudged atf_add_test_case chm3 - atf_add_test_case bdz + atf_add_test_case chm3_fudged + atf_add_test_case bpz + atf_add_test_case bpz_fudged + atf_add_test_case handle_dup } diff --git a/usr.bin/netpgpverify/t_netpgpverify.sh b/usr.bin/netpgpverify/t_netpgpverify.sh old mode 100755 new mode 100644 diff --git a/usr.bin/patch/Makefile b/usr.bin/patch/Makefile new file mode 100644 --- /dev/null +++ b/usr.bin/patch/Makefile @@ -0,0 +1,8 @@ +# $NetBSD: Makefile,v 1.1 2020/06/24 09:21:43 jruoho Exp $ + +.include + +TESTSDIR= ${TESTSBASE}/usr.bin/patch +TESTS_SH= t_patch + +.include diff --git a/usr.bin/patch/t_patch.sh b/usr.bin/patch/t_patch.sh new file mode 100644 --- /dev/null +++ b/usr.bin/patch/t_patch.sh @@ -0,0 +1,185 @@ +# $NetBSD: t_patch.sh,v 1.3 2021/05/25 11:55:42 cjep Exp $ +# +# Copyright (c) 2020, 2021 The NetBSD Foundation, Inc. +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +patch_lines() { + + printf "%$1s" | tr " " "a" > longlines 2>/dev/null + + cat << EOF > longlines.patch +--- ./longlines.orig 2019-10-16 09:25:30.667656644 +0000 ++++ ./longlines +@@ -1 +1 @@ +EOF + printf -- "-%$1s\n" | tr " " "a" >> longlines.patch 2>/dev/null + printf -- "+%$1s" | tr " " "b" >> longlines.patch 2>/dev/null + + patch longlines < longlines.patch + + if [ ! $? -eq 0 ]; then + atf_fail "Failed to patch long lines" + fi +} + +atf_test_case lines +lines_head() +{ + atf_set "descr" "Test patching lines" +} + +lines_body() +{ + lines="1 10 100 1000 8100" + + for line in $lines; do + patch_lines $line + done +} + +atf_test_case long_lines +long_lines_head() +{ + atf_set "descr" "Test patching long lines (PR bin/54620)" +} + +long_lines_body() +{ + patch_lines 10000 +} + +atf_test_case backup_simple +backup_simple_head() +{ + atf_set "descr" "Test backup type 'simple' (-V simple)" +} + +backup_simple_body() +{ + # Create the backup file so it's overwritten. + touch to_patch.orig + printf '%s\n' 'original file' > to_patch + + cat << EOF > test_diff.patch +--- original_file 2021-02-20 09:21:07.100869692 +0100 ++++ new_file 2021-02-20 09:21:10.912906887 +0100 +@@ -1 +1 @@ +-original text ++new text +EOF + cksum=$(sha256 -n to_patch | cut -d' ' -f1) + patch -V simple to_patch < test_diff.patch + atf_check [ -f to_patch.orig ] + origfile_cksum=$(sha256 -n to_patch.orig | cut -d' ' -f1) + atf_check_equal "$cksum" "$origfile_cksum" + atf_check grep -q -m 1 '^new text$' to_patch +} + +atf_test_case backup_none +backup_none_head() +{ + atf_set "descr" "Test backup type 'none' (-V none)" +} + +backup_none_body() +{ + printf '%s\n' 'original file' > to_patch + + cat << EOF > test_diff.patch +--- original_file 2021-02-20 09:21:07.100869692 +0100 ++++ new_file 2021-02-20 09:21:10.912906887 +0100 +@@ -1 +1 @@ +-original text ++new text +EOF + # Patch would mistakenly create 'simple' backup files when unwanted: + # http://mail-index.netbsd.org/tech-userlevel/2021/02/19/msg012901.html + patch -V none to_patch < test_diff.patch + atf_check [ ! -f to_patch.orig ] + atf_check grep -q -m 1 '^new text$' to_patch + + # Environment variables should be overriden. + printf '%s\n' 'original file' > to_patch + VERSION_CONTROL=existing patch -V none to_patch \ + < test_diff.patch + atf_check [ ! -f to_patch.orig ] + atf_check grep -q -m 1 '^new text$' to_patch + + # --posix should imply -V none. + printf '%s\n' 'original file' > to_patch + patch --posix to_patch < test_diff.patch + atf_check [ ! -f to_patch.orig ] + atf_check grep -q -m 1 '^new text$' to_patch +} + +atf_test_case backup_numbered +backup_numbered_head() +{ + atf_set "descr" "Test backup type 'numbered' (-V numbered)" +} + +backup_numbered_body() +{ + printf '%s\n' 'original file' > to_patch + + cat << EOF > test_diff.patch +--- original_file 2021-02-20 09:21:07.100869692 +0100 ++++ new_file 2021-02-20 09:21:10.912906887 +0100 +@@ -1 +1 @@ +-original text ++new text +EOF + cksum1=$(sha256 -n to_patch | cut -d' ' -f1) + patch -V numbered to_patch < test_diff.patch + atf_check grep -q -m 1 '^new text$' to_patch + + cat << EOF > test_diff2.patch +--- new_file 2021-02-20 09:44:52.363230019 +0100 ++++ newer_file 2021-02-20 09:43:54.592863401 +0100 +@@ -1 +1 @@ +-new text ++newer text +EOF + cksum2=$(sha256 -n to_patch | cut -d' ' -f1) + patch -V numbered to_patch < test_diff2.patch + atf_check grep -q -m 1 '^newer text$' to_patch + + # Make sure the backup files match the original files. + origfile_cksum1=$(sha256 -n to_patch.~1~ | cut -d' ' -f1) + origfile_cksum2=$(sha256 -n to_patch.~2~ | cut -d' ' -f1) + atf_check [ -f to_patch.~1~ ] + atf_check_equal "$cksum1" "$origfile_cksum1" + atf_check [ -f to_patch.~2~ ] + atf_check_equal "$cksum2" "$origfile_cksum2" +} + +atf_init_test_cases() +{ + atf_add_test_case lines + atf_add_test_case long_lines + atf_add_test_case backup_simple + atf_add_test_case backup_none + atf_add_test_case backup_numbered +} diff --git a/usr.bin/pkill/Makefile b/usr.bin/pkill/Makefile new file mode 100644 --- /dev/null +++ b/usr.bin/pkill/Makefile @@ -0,0 +1,8 @@ +# $NetBSD: Makefile,v 1.1 2017/02/21 10:40:30 kre Exp $ + +.include + +TESTSDIR= ${TESTSBASE}/usr.bin/pkill +TESTS_SH= t_pgrep + +.include diff --git a/usr.bin/pkill/t_pgrep.sh b/usr.bin/pkill/t_pgrep.sh new file mode 100644 --- /dev/null +++ b/usr.bin/pkill/t_pgrep.sh @@ -0,0 +1,75 @@ +# $NetBSD: t_pgrep.sh,v 1.2 2017/02/21 21:22:45 kre Exp $ +# +# Copyright (c) 2016 The NetBSD Foundation, Inc. +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +atf_test_case pr50934 +basic_shift_test_head() { + atf_set "descr" "Test fix for PR bin/50934 (null argv[0])" +} +pr50934_body() { + atf_require_prog pgrep + atf_require_prog cc + + cat > t.c <<'!' +#include +#include + +int +main(int argc, char **argv) +{ + sleep(2); + argv[0] = 0; + pause(); + exit(0); +} +! + + cc -o t0123456789abcdefg-beef t.c || atf_fail "t.c compile failed" + + ./t0123456789abcdefg-beef & + PID=$! + + pgrep -l t0123456 >OUT ; # should find the process + pgrep -l beef >>OUT ; # should find the process + + sleep 5 ; # allow time for sleep in t.c and argv[0]=0 + + pgrep -l t0123456 >>OUT ; # should find the process + pgrep -l beef >>OUT ; # should find nothing + + kill -9 $PID + cat OUT ; # just for the log + + # note that pgrep -l only ever prints p_comm which is of limited sicze + atf_check_equal "$(cat OUT)" \ + "$PID t0123456789abcde $PID t0123456789abcde $PID t0123456789abcde" + + return 0 +} + +atf_init_test_cases() { + atf_add_test_case pr50934 +} diff --git a/usr.bin/pr/t_basic.sh b/usr.bin/pr/t_basic.sh old mode 100755 new mode 100644 diff --git a/usr.bin/printf/Makefile b/usr.bin/printf/Makefile new file mode 100644 --- /dev/null +++ b/usr.bin/printf/Makefile @@ -0,0 +1,13 @@ +# $NetBSD: Makefile,v 1.1 2018/09/05 21:05:40 kre Exp $ + +.include + +TESTSDIR= ${TESTSBASE}/usr.bin/printf + +TESTS_SH= t_builtin t_command + +t_builtin: t_builtin.sh printf.sh + +t_command: t_command.sh printf.sh + +.include diff --git a/usr.bin/printf/printf.sh b/usr.bin/printf/printf.sh new file mode 100644 index 0000000000000000000000000000000000000000..b7593cb7a7236e21589a9aba67e522f57ede4337 GIT binary patch literal 42430 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 literal 0 Hc$@&2 "No builtin printf in ${TEST_SH}" + exit 1 + fi +} + +# See if we have a builtin "printf" command to test + +setup() +{ + # If the shell being used for its printf supports "type -t", use it + if B=$( ${TEST_SH} -c 'type -t printf' 2>/dev/null ) + then + case "$B" in + ( builtin ) return 0;; + esac + else + # We get here if type -t is not supported, or it is, + # but printf is completely unknown. No harm trying again. + + case "$( unset LANG LC_ALL LC_NUMERIC LC_CTYPE LC_MESSAGES + ${TEST_SH} -c 'type printf' 2>&1 )" in + ( *[Bb]uiltin* | *[Bb]uilt[-\ ][Ii]n* ) return 0;; + esac + fi + + Tests= + define Not_builtin 'Dummy test to skip when no printf builtin' + return 0 +} + +setmsg() +{ + MSG="${TEST_SH%% *} builtin printf" + CurrentTest="$1" + RVAL=0 +} + +BUILTIN_TEST=true + +# All the code to actually run the test comes from printf.sh ... + diff --git a/usr.bin/printf/t_command.sh b/usr.bin/printf/t_command.sh new file mode 100644 --- /dev/null +++ b/usr.bin/printf/t_command.sh @@ -0,0 +1,144 @@ +# $NetBSD: t_command.sh,v 1.1 2018/09/05 21:05:40 kre Exp $ +# +# Copyright (c) 2018 The NetBSD Foundation, Inc. +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +# The printf command to test (if not a full path, $PATH will be examined) +# Do not use relative (./ or ../) paths when running under ATF. +: ${TEST_PRINTF:=/usr/bin/printf} + +# This tests an external (filesystem) printf command + +# For the actual code/tests see printf.sh +# (shared with tests for the shell builtin printf + +# These tests are designed to be able to be run by ATF, or standalone +# +# That is, either +# atf_run t_command | atf-report (or whatever is needed) +# or +# sh t_command [sub_test]... ( default is to run all sub_tests ) +# +# nb: for standalone runs, do not attempt ./t_builtin as the #! line +# added will force ATF which will complain about the incorrect method +# of running the test. Instead use some Bourne shell compatible shell +# (any will do, caveat any bugs it might have), and run the script explicitly. + +do_printf() +{ + $Running_under_ATF && atf_require_prog "${PRINTF%% *}" + + unset LANG LC_ALL LC_NUMERIC LC_CTYPE LC_MESSAGES + + ${PRINTF} "$@" +} + +No_command() +{ + setmsg No_command + + case "${TEST_PRINTF%% *}" in + ( '' ) + msg='Configuration error: check $TEST_PRINTF' + ;; + ( /* | ./* | ../* ) + msg="Cannot find/execute ${TEST_PRINTF%% *}" + ;; + ( * ) + msg="No '${TEST_PRINTF%% *}' found in "'$PATH' + ;; + esac + atf_skip "${msg}" + + return $RVAL +} + +# See if we have a "printf" command in $PATH to test - pick the first + +setup() +{ + saveIFS="${IFS-UnSet}" + saveOPTS="$(set +o)" + + unset PRINTF + + case "${TEST_PRINTF%% *}" in + ( /* ) PRINTF="${TEST_PRINTF}" ;; + ( ./* | ../* ) PRINTF="${PWD}/${TEST_PRINTF}" ;; + (*) + set -f + IFS=: + for P in $PATH + do + case "$P" in + ('' | . ) D="${PWD}";; + ( /* ) D="${P}" ;; + ( * ) D="${PWD}/${P}";; + esac + + test -x "${D}/${TEST_PRINTF%% *}" || continue + + PRINTF="${D}/${TEST_PRINTF}" + break + done + unset IFS + eval "${saveOPTS}" + + case "${saveIFS}" in + (UnSet) unset IFS;; + (*) IFS="${saveIFS}";; + esac + ;; + esac + + test -x "${PRINTF%% *}" || PRINTF= + + case "${PRINTF}" in + + ('') Tests= + define No_command 'Dummy test to skip no printf command' + return 1 + ;; + + ( * ) + # nothing here, it all happens below. + ;; + + esac + + return 0 +} + +setmsg() +{ + MSG="${PRINTF}" + CurrentTest="$1" + RVAL=0 +} + +BUILTIN_TEST=false + +# All the code to actually run the test comes from printf.sh ... + diff --git a/usr.bin/pwhash/Makefile b/usr.bin/pwhash/Makefile new file mode 100644 --- /dev/null +++ b/usr.bin/pwhash/Makefile @@ -0,0 +1,8 @@ +# $NetBSD: Makefile,v 1.1 2019/10/05 18:06:17 jhigh Exp $ + +.include + +TESTSDIR= ${TESTSBASE}/usr.bin/pwhash +TESTS_SH= t_pwhash + +.include diff --git a/usr.bin/pwhash/t_pwhash.sh b/usr.bin/pwhash/t_pwhash.sh new file mode 100644 --- /dev/null +++ b/usr.bin/pwhash/t_pwhash.sh @@ -0,0 +1,47 @@ +atf_test_case pwhash_blowfish_r12 +pwhash_blowfish_r12_head() { + atf_set "descr" "ATF test for pwhash using blowfish 12 rounds" +} + +pwhash_blowfish_r12_body() { + atf_check -s exit:0 -o match:"^\\\$2a\\\$" -x \ + 'echo -n password | pwhash -b 12' +} + +atf_test_case pwhash_md5 +pwhash_md5_head() { + atf_set "descr" "ATF test for pwhash using MD5" +} + +pwhash_md5_body() { + atf_check -s exit:0 -o match:"^\\\$1\\\$" -x \ + 'echo -n password | pwhash -m' +} + +atf_test_case pwhash_sha1 +pwhash_sha1_head() { + atf_set "descr" "ATF test for pwhash using SHA1" +} + +pwhash_sha1_body() { + atf_check -s exit:0 -o match:"^\\\$sha1\\\$" -x \ + 'echo -n password | pwhash' +} + +atf_test_case pwhash_des +pwhash_des_head() { + atf_set "descr" "ATF test for pwhash using DES" +} + +pwhash_des_body() { + atf_check -s exit:0 -o ignore -e ignore -x \ + 'echo -n password | pwhash -s somesalt' +} + +atf_init_test_cases() +{ + atf_add_test_case pwhash_blowfish_r12 + atf_add_test_case pwhash_md5 + atf_add_test_case pwhash_sha1 + atf_add_test_case pwhash_des +} diff --git a/usr.bin/rump_server/t_disk.sh b/usr.bin/rump_server/t_disk.sh old mode 100755 new mode 100644 diff --git a/usr.bin/sdiff/t_sdiff.sh b/usr.bin/sdiff/t_sdiff.sh old mode 100755 new mode 100644 diff --git a/usr.bin/sed/t_sed.sh b/usr.bin/sed/t_sed.sh old mode 100755 new mode 100644 --- a/usr.bin/sed/t_sed.sh +++ b/usr.bin/sed/t_sed.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_sed.sh,v 1.6 2016/04/05 00:48:53 christos Exp $ +# $NetBSD: t_sed.sh,v 1.8 2021/02/23 21:09:14 christos Exp $ # # Copyright (c) 2012 The NetBSD Foundation, Inc. # All rights reserved. @@ -42,8 +42,7 @@ atf_test_case emptybackref emptybackref_head() { - atf_set "descr" "Test that sed(1) handles " \ - "empty back references (PR bin/28126)" + atf_set "descr" "Test that sed(1) handles empty back references" } emptybackref_body() { @@ -51,8 +50,6 @@ atf_check -o inline:"foo1bar1\n" \ -x "echo foo1bar1 | sed -ne '/foo\(.*\)bar\1/p'" - atf_expect_fail "PR bin/28126" - atf_check -o inline:"foobar\n" \ -x "echo foobar | sed -ne '/foo\(.*\)bar\1/p'" } @@ -133,10 +130,57 @@ 7 8 9"' } +atf_test_case escapes_in_subst +escapes_in_subst_head() { + atf_set "descr" "Test that sed(1) expands \x \d \o escapes " \ + "in substition strings" +} + +escapes_in_subst_body() { + atf_check -o inline:"fooXbar\n" \ + -x 'echo "foo bar" | sed -e "s/ /\x58/"' + atf_check -o inline:"fooXbar\n" \ + -x 'echo "foo bar" | sed -e "s/ /\o130/"' + atf_check -o inline:"fooXbar\n" \ + -x 'echo "foo bar" | sed -e "s/ /\d88/"' +} + +atf_test_case escapes_in_re +escapes_in_re_head() { + atf_set "descr" "Test that sed(1) expands \x \d \o escapes " \ + "in regex strings" +} + +escapes_in_re_body() { + atf_check -o inline:"foo bar\n" \ + -x 'echo "fooXbar" | sed -e "s/\x58/ /"' + atf_check -o inline:"foo bar\n" \ + -x 'echo "fooXbar" | sed -e "s/\o130/ /"' + atf_check -o inline:"foo bar\n" \ + -x 'echo "fooXbar" | sed -e "s/\d88/ /"' +} + +atf_test_case escapes_in_re_bracket +escapes_in_re_bracket_head() { + atf_set "descr" "Test that sed(1) does not expand \x \d \o escapes " \ + "in regex strings inside braces" +} + +escapes_in_re_bracket_body() { + atf_check -o inline:"foo bar\n" \ + -x 'echo "foo\\x58bar" | sed -e "s/[\x58]/ /g"' + atf_check -o inline:"f bar\n" \ + -x 'echo "fooo\\130bar" | sed -e "s/[\o130]/ /g"' + atf_check -o inline:"foo bar\n" \ + -x 'echo "foo\\d88bar" | sed -e "s/[\d88]/ /g"' +} atf_init_test_cases() { atf_add_test_case c2048 atf_add_test_case emptybackref atf_add_test_case longlines atf_add_test_case rangeselection atf_add_test_case preserve_leading_ws_ia + atf_add_test_case escapes_in_subst + atf_add_test_case escapes_in_re + atf_add_test_case escapes_in_re_bracket } diff --git a/usr.bin/shmif_dumpbus/d_pcap.out.bz2.uue b/usr.bin/shmif_dumpbus/d_pcap.out.bz2.uue --- a/usr.bin/shmif_dumpbus/d_pcap.out.bz2.uue +++ b/usr.bin/shmif_dumpbus/d_pcap.out.bz2.uue @@ -1,104 +1,104 @@ begin 644 d_pcap.out.bz2 -M0EIH.3%!62936=O8'&4!%NW?@$`00`5_\0@B0``.Y_X@8"=^\@0```````!P -M```````![XY0O$\'N'"@P'!M=O2E=5*>#>[0M50X0`Q@V@,$-!@@$%T!A:`P -MNATWD`,83$T&",0R,)AE/4?O1O52E*@P`$`820U`]4JH-#(#0-`)/5*JFU,: -MAIDQ--,`("DJ$*>E&F@!B:9'J>H%0J0>BI0``:`#P[E![R+ZY%2,<&JMJ-E3 -M46V)*F;;EK75-K&QM&I2K*&T&T6+/K6_NM*C]*2HP@P@TB)E$3A/FJ+(T01D -MEE3)*^*HLJRIC"2U2*M!2L$O=47PI1'PJ+)#2".()^T@)^44*>9`3@D/Q2`G -MVD!-(%K\?\`````````````````````````````````!`D````D````````` -M`````````````````````````````)"```2$```````````````````````` -M````0``````````````````````````````````````````````````````` +M0EIH.3%!629366U$3Z$!&(O?@$`00`=_\0@B0``.Y?X@8">^\@0````````X +M````````^&4<'SPFY'D+@IX.TN4HJA<%E%4J&$`.,&Z`P65!@N@8+0,%T#!` +MT[R`#&$Q-!@C$,C"8=3'[T;U2JBJ&``3`!)#4FGM4J4`!HT``D]5*D#]2`!B +M```I*II,]4*&@`9`::!4*FI--ZJHT`-#30`X\E!]A%ZY%2,=C56U&RIJ+;$E +M3-MQ:URIM8V-HU*590V@VBQ9^:W=6E1^%)48080:1$RB)V3XJBR-$$9)94R2 +MOV5%E65,826J15H*5@ERHOA2B.5%DAI!':"?E("?=%"GK("?"0$Z)#2`G]2` +MTUJJM?S_``````````````````````````````````0)````)``````````` +M```````````````````````````"0@``$A`````````````````````````` +M``$````````````````````````````````````````````````````````` M```````````````````````````````````````````````````````````` M```````````````````````````````````````````````````````````` M```````````````````````````````````````````````````````````` M```````````````````````````````````````````````````````````` -M````````````````]]]]]]]]]]]]]]]]\]_U>^N]_A_'^7[OYZ?8#!^`0.`X -M-W=&9F```````]]`-/L!@_`)))``````````*?;[]``````````````````` -M````````````````````````#P``!;;;;0```'./'CQX\ -M>/'CQX\]]/ODW8-=SN^=SF_;]WWM/UWKN][\SQTV<,++++++9E55"JRD964C -M*RD964C*RD964C*RD964C*RD969F4C*RF5F:2222222ZZZ2222222222ZZZ2 -M222155555569E5ZA"1F835JZZ2222222_2ZZ2222222222ZZZ2222222222Z -MZZ2222222222ZZZ22225555557)NPDA8BUYY[\["YG,W,XZ -M0ZI3(:35+77/7,ERN5RNJ#J*U!J646HJIJB32D)4$%2$(L$/23K?/G?/O?.[ -MWWI()MWWWWWWSN]]Z$D?"0.)(_CSGOG+V]`#ODDD@``!(+:M@MX%L@MX%L%M -M@MX%L@MX%L%M@MX%L@MX%L%M@MX%L%M@;NZ```=MMJ2#I))T=_.][WO3=W=U -MX\[[50XD`D),$@##G.6\;VXXX\MC-"@U#LAV@]U!V@Z0Z0\J.DV39-DV39-9 -M-O,<9-DV39-DV3:>9QDT'.R;)LFR;)LFDT','*'=!\0?$'U!V@X@X@X@X@X@ -MX@X@X@X@X@X@X@X@X@Y(.0.(Q$TEJ+46HM1]?9/LT,I35%FD1K8&)PC7A-DV -M3:U59-DV39%1*@^H/D#X@^H.(/J#B#B#B#B#B#B#B#B#B#B#B#B#B#D#B#B# -MB#D]/4GJ:,EB*DDTDQ@S1:AW$@(KX(2E50B555WWWO>-Z?'Q^>6QB11WO;[C -M>GU]?7EL8?S`2H!B2*$>]Z_9QZ?GQ^>6QAY)(H0;0D`"/>]>9MZ?GY^>6QF@ -M$"H$=[VWFGI]?7UY;&)'>]ON:>GU]?7EL9HH\D(H0>]Z]9MZ?GY^>6QFBCP) -M*A!:$@6"0`520``'O>MXWI^?GYY;&:*/"%0@][UZS;T_/S\\MC#P@*$%`EY] -M?7UYY\_/S,SY\^?*`/GDDDG0``$@MJV"W@6P6V"W@6R"W@6P6V"W@6R"W@6P -M6V"W@6R"W@6P6V!N[H``!O;;62#I))T=[WO>]'..MMNNDDD -MDDDDE022YG'MQQQY?M55&BB)(5EE%%%%%%%%%%%%%`"HH)) -M$O&U77222222225XR7,X]N../+]JJHT40$`66444444]1,S$9F(S,1F8C,Q% -M$DN9U\<NLS/U\^?%`/GDDDG -M>@``2"VK8+>!;!;8+>!;(+>!;!=XMW>6EL@MX%L%M@MX%L7=WEMI!;8&[N@` -M`&[VVLP=&9F7IWO>][WI;;:\[YTAPPYSE\SCXXXX\OVJJC11A\D`8(#NAWH> -M!7B#I#J#B%W1R\,1F8C,Q&9B,S$9F(666"+++!%EE@BB27W./CZ_/SR_:MKV -MKIX05#ITQ&4BJ15(JD52*I%4BJ16>>>;SKGKKSKSOK;GJVNW3P@J&4BJ15(J -MD52*I%4BJ15(Z>>>;SKKXXX_/+]JJT80$!0@LH$44"**$S$S$S$S$S$S$Z>G -MIZ7,Z^../SR_:JM&$!`4(+."2+!%F)F)F)F)F)F)F)F!&B27O.OCCC\\OVJK -M1A`0%""R@3,3,3,3.1)<$X8F8110!0R27,X]N./SR_:JM$!`4(,));W -MO[[[[WN^][WO>UK6M:^=[T``$@MJT6V`06V"W@6P6V"W@6R"W@6P6V"W@6R" -MW@6P6V"W@6R!N[H``!N[VUF#O3-[WWL]))))/=[WO>^)"F)<$N4`@54(21AQ -M\OF<<<WQ]<>7[55H@( -M"A!A)+F<>W'''E^U5:(D`4(,))7[55H@("A!A)+F<>W'''E^U5<0`D);(A!0@ -M9)JYG'MQQQY?M56B"`H0822YG'MQQQY?M56B"`H0822YG'MQQQY?M5NST0R, -MHB>"4O?!D49#(HT&JHM6R;)LFR;)LF!-!H49#(H]\^O///?KSZ^O//KSG+EO -MZ_7Z_0`222=[WH``,P!;!;8+>!;(+>!;!;8+>!;(+>!;!;8+>!;(+>!;!;8+ -M>!;!6[O```#=W>UF#O>LS,MO>R2223O>][WQZ%6`@3$MC-YN]9MQQSKTZYX\ -M]MNS[X,AW0[ITARAR@M"+#0DBA*B27,V]OCZX\OVJK0`H("@P!"%0E1)+F<> -MW'''E^U5:(D!0@P^^^^O[.OC^?S^>7-56CY`%"##[[Z_L^?7Q_/Y_/+FJK1\ -M(H08???7]GSZ^/Y_/YY7-56C2`$A*A!A)+F1]?'UQQY -M7-5221H$:$D"PDES(^OKZYSO/S[[/?9YY]>>^R]Z -M*!)))WO>@`!(`$%M@MX%L%M@MX%L@MX%L%M@MX%L@MX%L%M@MX%L@MX%L%;N -M\```-W=WK,'>][F9EM[WLDDDG>][WOCT*X`A"T)5RJ97'?,X^..../+FJK0( -M-"`L0%B`L0%"#"27,CZ^/KCCRYJJT)6@$J*`0*B@$"HH0@4DN9'Q\<<<>7-5 -M6BP$"IA%F$6819A%F"F`HDES(^/CCCCRYJJX`(%L1B`$A*A%$DO61\?''''E -MS55H$622YD?'QQQQYLCX^.../+F -MJKAL$822]9'Q\<<<>7-57#8!A)+UD?'QQQQY]]GON^>YF3.]Z%`DDD[WO0`` -MD`""VP6\"V"VP6\"V06\"V"VP6\"V06\"V"VP6\"V"VP""MW>```&[N[UF#O -M>]S,RV][WO>][WL[WO>]\256"2!8>0`D)=[5;*[V^9Q]W''''ES55PI"LDEZR/;CCCCRYJJX4"+))>LCVXXXX\N:J -MN%`BR27K(]N.../+FJI"*))>>_7UYYF9,^?/GP4"223YWO0``D`6\"V"VP6\"V06\"V"VP6\"V06 -M\"V"VP6\"V06\"V"VP6\!NZ```/-W=WK,'>][F9EM[WO>][))WO>][XDJK.& -M'.WQ]?S^>7-52$422 -MYD>W''''ES54A%$DN9'MQQQQY7-52$422YD>W''''ES5 -M4A%$DN9'MQQQQY][W -MM:UK6I)/>\``D`6\"V"VP6\"V06\"V"VP6\"V06\"V"VP6\"V06\"V"VP6\! -MNZ```9F9G;F6][WOR2223O>][WQ)56<.B2&=[V^YQ]<WQ]<<>7-57`$@*!%DDO61[<<<<>7-52$ -M422YD>W''''ES55P2H$622]9'MQQQQY7-57!*@19) -M+UD>W''''ES54A%$DN9'MQQQY<)JJ0BB27,CVXXX\OSJD(H][U^SSV_/S\\O -MSJDE8D@5)-XY))KWO>][WO:UK6M3WO>]X`!(+:M@$%M@MX%L%M@MX%L@MX%L -M%M@MX%L@MX%L%M@MX%L@MX#=T```[N[I(.DDF[T>][TD[WO>]Z>(0X67YU2ZJR'9#LAYB/"'2'2&1L>_7[./;X^OSR_.J0BCWO7[//;\_/SR_ -M.J0BCWO7[//;\_/SR_.J27ZT`)"5"LDES(]N../+\ZI"*/>]?L\]OS\_/+\Z -MI!`!`J"BBBBBBBBBR27,CXXXX^MWZM^I(2O%555555553IYY[N9'MQQQY?G5 -M(11[WK]GGM^?GYY?G5(@"0%!0E10E10E0%DDN9'MQQQYQY=2JZJ=2"-46J<$ -M$<=36D42T5%8M93,VIM:10-"E6)36EEEE9:T@)\9`3UD!/"J$GK0$X)#TD!/]D!/8D.$ -<@)V2`F0$]$!/:0$]4!/?0$_\7\````&[N[NZ````222````#,S,S``` +M`!F9F9@````S,S,P````9F9F8````222````!N[N[N@```$DD@````;N[N[H +M```!))(````&[N[NZ````222````!N[N[N@```$DD@````;N[N[H```!))(# +M^P`<`!N[N[N@```$DD@````;N[N[H```!))(````&[N[NZ````222````!N[ +MN[N@```$DD@````;N[N[H```!))(````&[N[NZ````222````!N[N[N@```$ +MDD@````S,S,P`.`'`9F9F8````,S,S,````&9F9F````#,S,S````!F9F9@` +M```S,S,P````9F"2````````````````````````````````````````/FWZ +MMMMK3[`8/P"!X'@0/S\_/P`````??W]@`````,S,S,``````````M^OJVVVM +M/L!@_`>][W@`/KZ`````````!;;;;0````````````#X^/CX^/CX^/CX^/CY +M]?1]^F[!+^7-OYN,4GGW]RYF/?'R_,^;\_/F9F9F9550JLI&5E(RLI&5E(RL +MI&5E(RLI&5E(RLI&5F9E(RLIE9F55555555569E4DDDDDDDDERYW1%UUUU!=%7D)(6(M>_7.MA=3J +M;J=NZ'>E,AI-4M=^N_4ETNETN]!WBM0:EE%J*J:HDTI"R$,H@12`0,2R_//I +MW[W:W;20"N]W=W?=K=M`DCP$@#T21_+WWGOOL]F[N[N[NPYSG.0```D%M6P6 +M^"V06^"V"VP6^"V06^"V"VP6^"V06^"V"VP6^"V"VP-W=```.MMJ2#B][W32550ZD`D),$@##O>V\;\<<<>6QG`2`*2`X(#B#E!Z0=T.Z'J +MH[ILFR9:+1:9:.M7%HM%HM%HM&NM<6FJNPM%HM%HM;)I-!U!TAX0\(>$/*'J +MAQ#B'$.(<0XAQ#B'$.(<0XAQ#B'*'(/M&(FDM1:BU%J/;W)[FAE*:HLTB-;` +MQ&0L]HM1:FUJJR;)LFR9+(>4/$'A#RAQ#RAQ#B'$.(<0XAQ#B'$.(<0XAQ#D +M'$.(<0Y=W>N]VG9:2I)-),8,T6H?(D!%?0@J"BB@!"HHHHW=OF-\?7UZ\MC$ +MBCWWV_<;X_7Z_7EL8?D`E0#$D4(W=O/CUZ +M]>6QG`$"H$>^^V\X^/U^OUY;&)'OOM^YQ\?K]?KRV,X4:D(H0;NWS/'QZ]Y\ +MYYN./2OA)*AHD)XD!;(``W=MXWQZ]>O+8SA1HA4(-W;YGCX]>O7EL8:("A!^ +M0(0%@D)(/PWWO?Y_/Y_*`/Y\DDDX``!(+:M@M\%L%M@M\%L@M\%L%M@M\%L@ +MM\%L%M@M\%L@M\%L%M@;NZ```;UMK)!Q))P[N[NX6VVOCYWTWL]53W%*CI%" +MF4)#R!("A(!"`TPU[>YKU_./YY>\JN%C,]X.R'A#PAZ(=(=(?L47A469TQF9 +MF9F9F:D`04444'WWU_9Z_'Z]?SR]Y5<+**/A`S*JJJJJJJJJJ=[W=\]\^N=Y +MWG>>;YZMKTKJ$9E55555Z$05EE%%%%%!)+F>OQQ^N/+WE51PHB)),RJJJJJJ +MJJJJG>]W?/?/KG>=YWGF^>K:]*[)$S*JJJJJJI110`J*"27,Z_''''E[RJHX +M400&955555555553O>[OGOGUSO.\[SS?/5M>E=(&95555R)F8C,Q&9B,S$9F +M(KO>[OGWSWR./7E[RJHX480$DJ$&&%@BRRP1F8C,Q&9B,S$9F(S,"+++!%$D +MN9Z^N./7E[RJHX480245#QXQ&9B,S$9F(S,1F8C,Q&9B,S$5WO=WS[Y[YWG> +M?.>;YZMKTKQT@J'CQB,S$9F(S,1F8C,Q&9B,S`BRRP1B0A)!A0"`&\\\\_'W +MWV^[N@_?R223N```D%M6P6^"V"VP6^"V06^"V"[Y;N^M+9!;X+8+;!;X+8N[ +MOK;2"VP-W=```-WK:S!PS,R\=W=W=Q;;:^=\[YWS?GY\W=^]SKZXXX\O>55' +M"C#Y(`P0'@@/$@/8D]P.(.0-!/I#Q[8C,Q&9B,S$9F(S,1F8C++!%EE@BB27 +M[G7U^O7KR]Y54<*,((*AX\8C*15(JD52*I%4BJ15(K.][N^??/.^=^>;??+: +M^/'2"H92*I%4BJ15$44"**!%%`BB@1A)+F>OKCCUY?SU;Z>.D%0RD52*I%4B +MJ!%%`BB@110(HH$,DES/7UQQ[SS?/5OIXZ05#/:2,1E(JD52*I%4BJ15(JD> +MG>]WUYZ^N./7E[RJX80$!0@LH$44"**$52*\$1,1E(JD50KA)+F=?CCCUY>\ +MJN$!`4(,))7O*KA`0%"#"27,Z_''''E[RJX0$!0@PDES.OQQQQY> +M\JN$!`4(*0D(!7>>>>????;ON[N[N\YSG.?23=W=W=`$@MJT6V`06V"WP6P6 +MV"WP6R"WP6P6V"WP6R"WP6P6V"WP6R!N[H``!N[UK,'<9F9;SN[NDDWWWWWW +MW20IB71+M`(%5"$D8=?;[G7'''KR]Y57O*KA`0%"#"27,Z_''''E[RJX1(`H0822YG7XXXX\O>57"`@ +M*$&$DN9U^.../+WE5P@("@30`D)4#$D"HHDES/7XXX]>7O*KA`0%"#"27,Z_ +M''''E[RJZ@!(2\(A!0@9)RYG7XXXX\O>57""`H0822YG7XXXX\O>57""`H08 +M22YG7XXXX\O>57""`H1E$3R2E]L&11D,BC0:JBU;)LFR;)LFR8$T&A1D,BC( +M:4^?KY[URW^?S^?P`))).[N``!F`+8+;!;X+9!;X+8+;!;X+9!;X+8+;!;X+ +M9!;X+8+;!;X+8*W=\```;N[U9@[N9F9;>[N[N[NZV^^^Z;"K`0)B7@SS/+YG +MCCCCCR]Y5\JN$2`H08????7]GKZ_G\_GESE5P^0!0@P^^^O[/GZ^OY_/ +MYY7.5221P$<$D"PDES(_7Z_7''ESE5P/U\^OJ +M3T[NX4"223N[@``D`""VP6^"V"VP6^"V06^"V"VP6^"V06^"V"VP6^"V06^" +MV"MW?```&[N[S,'=W9F9;>[N[N[NZVWWW385T!"%P2KM4RNN^YU]<<<<>7.5 +M?1#T@R#(,@J'CO>[OG>??/?/OG>=YYN^K?230251"51"540%)+F1]?7'''ES +ME5PL!`J*`0*BA"51"51"50D45WO=WSKZ^N../+G*KH`@7@C$`)"5"*))?,CZ +M^N../+G*K@(LDES(^OKCCCRYRJX`A"Z4",))?,CZ^N../+G*KIX",))?,CZ^ +MN../+G*KIX",))?,CZ^N../+G*KIX`822^9'U]<<<>7.572O//.9W<%`DDD[ +MNX``)``@ML%O@M@ML%O@MD%O@M@ML%O@MD%O@M@ML%O@M@ML`@K=WP``!N[N +M\S!W=V9F6WN[N[N[NMMM?.[ZK!)`L-0`D)>^U7A7OM]SK]<>N./+G-NH*5Y> +M:3UHN\G$.(=JHK$!:Z3DOW/7Z_7Z]<>7.572R26\C\<<<<>7.572R26\C\<< +M<<>7.572R26\C\<<<<>7.572D*R27S(_'''''ESE5TH$622^9'XXXXX\N7.52 +M^0(*2#S,F?O]_O]B@222?ONX``)`%O@M@ML%O@MD%O@M@ML%O@MD%O@M@ML% +MO@MD%O@M@ML%O@;N@``#YN[N\S!W=V9F6WN[N[N[NMMM?)*JSIAWO;[GCZXX +MXX\N7.52$422YD?CCCC +MCRYRJ0BB27,C\<<<<>7.52$422YD?CCCCCRYRJ0BB27,C\<<<<>7.52$422Y +MD?CCCCCRYRJ7R0*A!9]]]?V?/Q_/Y_/YY7.571*@19)+YD?CCCCCRYRJZ)4"+))?,C\<<<<>7.571*@19)+YD?CCCCC +MRYRJ0BB27,C\<<<>7"7KJDE8DOGS] +M?7Z_7O?GN[NZ`"223@``$@MJV`06V"WP6P6V"WP6R"WP6P6V"WP6R"WP6P6V +M"WP6R"WP-W0``#MW=)!Q))N\`[NMMOOII"'2SO>WW.OKCCCR]=4O4E0@."`X +M(#4"#H@&(!B`H0>#\V]SK\?7Z]>7KJD(HW=ONJ0BC=V]S7X]>O7 +MEZZI)?H0`D)4*R27,C\<<<>7KJD(HW=ONJ00`0*@HHHHHHHHHLD +MES(^N../+UU71)"5U555555554\=[ZW?.\^N=YWG7EZZI"*-W;W-?CUZ]>7K +MJD0!("@H2HH2HH2H"R27,C\<<<>7HT))-4[R"-46J=B".W8TF;42T5%8M93, +MVIM:10-"E6)36EEEEEFNS5H4^,@)E4)/C("=HH4^4@)_,@)_<@)Z2`G^2`GS +MH"?]("9`3Z("?20$^4@)_T@)Y50D^-`3VD!/G(">A(<)#W)#O0$^1(:H%7F0 +@$\DAI`3L@)[H"?20$_DD/F@)\*`G_B[DBG"A(-J(GT() ` end diff --git a/usr.bin/shmif_dumpbus/t_basic.sh b/usr.bin/shmif_dumpbus/t_basic.sh old mode 100755 new mode 100644 --- a/usr.bin/shmif_dumpbus/t_basic.sh +++ b/usr.bin/shmif_dumpbus/t_basic.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_basic.sh,v 1.8 2013/04/07 19:14:03 christos Exp $ +# $NetBSD: t_basic.sh,v 1.10 2020/04/02 00:00:16 christos Exp $ # # Copyright (c) 2011 The NetBSD Foundation, Inc. # All rights reserved. @@ -25,6 +25,8 @@ # POSSIBILITY OF SUCH DAMAGE. # +export PATH=/bin:/usr/bin:/sbin:/usr/sbin + unpack_file() { @@ -74,7 +76,7 @@ # # Used to fail for "PR bin/44721" atf_check -s exit:0 -o file:d_pcap.out -e ignore \ - tcpdump -tt -r pcap + tcpdump -tt -n -r pcap } atf_init_test_cases() diff --git a/usr.bin/sort/t_sort.sh b/usr.bin/sort/t_sort.sh old mode 100755 new mode 100644 diff --git a/usr.bin/tar/Makefile b/usr.bin/tar/Makefile new file mode 100644 --- /dev/null +++ b/usr.bin/tar/Makefile @@ -0,0 +1,121 @@ +# $NetBSD: Makefile,v 1.4 2020/01/19 17:36:57 christos Exp $ + +NOMAN= + +.include +LIBARCHIVE=${NETBSDSRCDIR}/external/bsd/libarchive/dist + +TESTSDIR= ${TESTSBASE}/usr.bin/tar + +BINDIR= ${TESTSDIR} +PROGS+= h_tar +TESTS_SH+= t_tar + +CPPFLAGS+=-I${LIBARCHIVE}/test_utils -I${LIBARCHIVE}/tar -I. +CPPFLAGS+=-I${LIBARCHIVE}/tar/test -I${LIBARCHIVE}/../include +CPPFLAGS+=-I${LIBARCHIVE}/libarchive + +.PATH: ${LIBARCHIVE}/tar/test ${LIBARCHIVE}/test_utils + +DPADD+= ${LIBARCHIVE} ${LIBEXPAT} ${LIBBZ2} ${LIBLZMA} ${LIBZ} \ + ${LIBCRYPTO} ${LIBPTHREAD} +LDADD+= -larchive -lexpat -lbz2 -llzma -lz -lcrypto -lpthread + +SRCS.h_tar= \ +test_main.c \ +test_utils.c \ +test_0.c \ +test_basic.c \ +test_copy.c \ +test_empty_mtree.c \ +test_extract_tar_Z.c \ +test_extract_tar_bz2.c \ +test_extract_tar_grz.c \ +test_extract_tar_gz.c \ +test_extract_tar_lrz.c \ +test_extract_tar_lz.c \ +test_extract_tar_lz4.c \ +test_extract_tar_lzma.c \ +test_extract_tar_lzo.c \ +test_extract_tar_xz.c \ +test_extract_tar_zstd.c \ +test_format_newc.c \ +test_help.c \ +test_leading_slash.c \ +test_missing_file.c \ +test_option_C_mtree.c \ +test_option_C_upper.c \ +test_option_H_upper.c \ +test_option_L_upper.c \ +test_option_O_upper.c \ +test_option_T_upper.c \ +test_option_U_upper.c \ +test_option_X_upper.c \ +test_option_a.c \ +test_option_acls.c \ +test_option_b.c \ +test_option_b64encode.c \ +test_option_exclude.c \ +test_option_exclude_vcs.c \ +test_option_fflags.c \ +test_option_gid_gname.c \ +test_option_grzip.c \ +test_option_j.c \ +test_option_k.c \ +test_option_keep_newer_files.c \ +test_option_lrzip.c \ +test_option_lz4.c \ +test_option_lzma.c \ +test_option_lzop.c \ +test_option_n.c \ +test_option_newer_than.c \ +test_option_nodump.c \ +test_option_older_than.c \ +test_option_passphrase.c \ +test_option_q.c \ +test_option_r.c \ +test_option_s.c \ +test_option_uid_uname.c \ +test_option_uuencode.c \ +test_option_xattrs.c \ +test_option_xz.c \ +test_option_z.c \ +test_option_zstd.c \ +test_patterns.c \ +test_print_longpath.c \ +test_stdio.c \ +test_strip_components.c \ +test_symlink_dir.c \ +test_version.c \ +test_windows.c + +FILESDIR= ${TESTSDIR} +FILES=\ +test_extract.tar.Z.uu \ +test_extract.tar.bz2.uu \ +test_extract.tar.grz.uu \ +test_extract.tar.gz.uu \ +test_extract.tar.lrz.uu \ +test_extract.tar.lz.uu \ +test_extract.tar.lz4.uu \ +test_extract.tar.lzma.uu \ +test_extract.tar.lzo.uu \ +test_extract.tar.xz.uu \ +test_extract.tar.zst.uu \ +test_leading_slash.tar.uu \ +test_option_keep_newer_files.tar.Z.uu \ +test_option_passphrase.zip.uu \ +test_option_s.tar.Z.uu \ +test_patterns_2.tar.uu \ +test_patterns_3.tar.uu \ +test_patterns_4.tar.uu \ +test_print_longpath.tar.Z.uu + +.include + +test_main.o test_main.d: list.h + +CLEANFILES+=list.h + +list.h: ${SRCS.h_tar} Makefile + ${TOOL_GREP} -h '^DEFINE_TEST(' ${.ALLSRC} > ${.TARGET} diff --git a/usr.bin/tar/t_tar.sh b/usr.bin/tar/t_tar.sh new file mode 100644 --- /dev/null +++ b/usr.bin/tar/t_tar.sh @@ -0,0 +1,45 @@ +# $NetBSD: t_tar.sh,v 1.1 2020/01/17 16:25:37 christos Exp $ +# +# Copyright (c) 2020 The NetBSD Foundation, Inc. +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +atf_test_case tar + +tar_head() +{ + atf_set "descr" "test tar" +} + +tar_body() +{ + local d=$(atf_get_srcdir) + atf_check -s exit:0 -o 'not-match:^Details for failing tests:.*' \ + -e ignore "$d/h_tar" -p /usr/bin/tar -r "$d" +} + +atf_init_test_cases() +{ + atf_add_test_case tar +} diff --git a/usr.bin/tmux/t_tmux.sh b/usr.bin/tmux/t_tmux.sh old mode 100755 new mode 100644 diff --git a/usr.bin/tr/t_basic.sh b/usr.bin/tr/t_basic.sh old mode 100755 new mode 100644 diff --git a/usr.bin/unifdef/t_basic.sh b/usr.bin/unifdef/t_basic.sh old mode 100755 new mode 100644 diff --git a/usr.bin/uniq/Makefile b/usr.bin/uniq/Makefile new file mode 100644 --- /dev/null +++ b/usr.bin/uniq/Makefile @@ -0,0 +1,16 @@ +# $NetBSD: Makefile,v 1.1 2016/10/22 14:13:39 abhinav Exp $ + +.include + +TESTSDIR= ${TESTSBASE}/usr.bin/uniq +TESTS_SH= t_uniq + +FILESDIR= ${TESTSDIR} +FILES= d_basic.in +FILES+= d_basic.out +FILES+= d_counts.out +FILES+= d_input.in +FILES+= d_show_duplicates.out +FILES+= d_show_uniques.out + +.include diff --git a/usr.bin/uniq/t_uniq.sh b/usr.bin/uniq/t_uniq.sh old mode 100755 new mode 100644 diff --git a/usr.bin/vmstat/t_vmstat.sh b/usr.bin/vmstat/t_vmstat.sh old mode 100755 new mode 100644 --- a/usr.bin/vmstat/t_vmstat.sh +++ b/usr.bin/vmstat/t_vmstat.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_vmstat.sh,v 1.1 2014/01/07 16:47:13 gson Exp $ +# $NetBSD: t_vmstat.sh,v 1.2 2020/06/25 11:12:03 jruoho Exp $ # # Copyright (c) 2013 The NetBSD Foundation, Inc. # All rights reserved. @@ -27,7 +27,8 @@ atf_test_case default default_head() { - atf_set "descr" "Test that vmstat(1) returns success when run with no arguments" + atf_set "descr" "Test that vmstat(1) returns " \ + "success when run with no arguments" } default_body() { atf_check -s exit:0 -o ignore -e empty vmstat @@ -35,7 +36,8 @@ atf_test_case opt_s opt_s_head() { - atf_set "descr" "Test that vmstat(1) returns success when run with -s (PR 44518)" + atf_set "descr" "Test that vmstat(1) returns " \ + "success when run with -s (PR bin/44518)" } opt_s_body() { atf_check -s exit:0 -o ignore -e empty vmstat -s diff --git a/usr.bin/xlint/Makefile b/usr.bin/xlint/Makefile --- a/usr.bin/xlint/Makefile +++ b/usr.bin/xlint/Makefile @@ -1,9 +1,9 @@ -# $NetBSD: Makefile,v 1.1 2012/03/17 16:33:15 jruoho Exp $ +# $NetBSD: Makefile,v 1.2 2021/08/05 22:36:08 rillig Exp $ .include TESTSDIR= ${TESTSBASE}/usr.bin/xlint -TESTS_SUBDIRS= lint1 +TESTS_SUBDIRS= lint1 lint2 .include diff --git a/usr.bin/xlint/check-expect.lua b/usr.bin/xlint/check-expect.lua new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/check-expect.lua @@ -0,0 +1,152 @@ +#! /usr/bin/lua +-- $NetBSD: check-expect.lua,v 1.12 2021/08/21 07:49:48 rillig Exp $ + +--[[ + +usage: lua ./check-expect.lua *.c + +Check that the /* expect: ... */ comments in the .c source files match the +actual messages found in the corresponding .exp files. + +]] + + +local function load_lines(fname) + local lines = {} + + local f = io.open(fname, "r") + if f == nil then return nil end + + for line in f:lines() do + table.insert(lines, line) + end + f:close() + + return lines +end + + +local function load_expect_comments_from_c(fname, errors) + + local lines = load_lines(fname) + if lines == nil then return nil, nil end + + local pp_fname = fname + local pp_lineno = 0 + local comment_locations = {} + local comments_by_location = {} + + local function add_expectation(offset, message) + local location = ("%s(%d)"):format(pp_fname, pp_lineno + offset) + if comments_by_location[location] == nil then + table.insert(comment_locations, location) + comments_by_location[location] = {} + end + local trimmed_msg = message:match("^%s*(.-)%s*$") + table.insert(comments_by_location[location], trimmed_msg) + end + + for phys_lineno, line in ipairs(lines) do + + for offset, comment in line:gmatch("/%* expect([+%-]%d+): (.-) %*/") do + add_expectation(tonumber(offset), comment) + end + + for comment in line:gmatch("/%* expect: (.-) %*/") do + add_expectation(0, comment) + end + + pp_lineno = pp_lineno + 1 + + local ppl_lineno, ppl_fname = line:match("^#%s*(%d+)%s+\"([^\"]+)\"") + if ppl_lineno ~= nil then + if ppl_fname == fname and tonumber(ppl_lineno) ~= phys_lineno + 1 then + errors:add("error: %s:%d: preprocessor line number must be %d", + fname, phys_lineno, phys_lineno + 1) + end + pp_fname = ppl_fname + pp_lineno = ppl_lineno + end + end + + return comment_locations, comments_by_location +end + + +local function load_actual_messages_from_exp(exp_fname) + + local lines = load_lines(exp_fname) + if lines == nil then return {} end + + local messages = {} + for exp_lineno, line in ipairs(lines) do + for location, message in line:gmatch("(%S+%(%d+%)): (.+)$") do + table.insert(messages, { + exp_lineno = exp_lineno, + location = location, + message = message + }) + end + end + + return messages +end + + +local function check_test(c_fname, errors) + local exp_fname = c_fname:gsub("%.c$", ".exp") + + local comment_locations, comments_by_location = + load_expect_comments_from_c(c_fname, errors) + if comment_locations == nil then return end + + local messages = load_actual_messages_from_exp(exp_fname) + if messages == nil then return end + + for _, act in ipairs(messages) do + local exp = comments_by_location[act.location] or {} + + local found = false + for i, message in ipairs(exp) do + if message ~= "" and act.message:find(message, 1, true) then + exp[i] = "" + found = true + break + end + end + + if not found then + errors:add("error: %s: missing /* expect+1: %s */", act.location, act.message) + end + end + + for _, location in ipairs(comment_locations) do + for _, message in ipairs(comments_by_location[location]) do + if message ~= "" then + errors:add( + "error: %s: declared message \"%s\" is not in the actual output", + location, message) + end + end + end +end + + +local function main(args) + local errors = {} + errors.add = function(self, fmt, ...) + table.insert(self, string.format(fmt, ...)) + end + + for _, name in ipairs(args) do + check_test(name, errors) + end + + for _, error in ipairs(errors) do + print(error) + end + + return #errors == 0 +end + +os.exit(main(arg)) diff --git a/usr.bin/xlint/lint1/Makefile b/usr.bin/xlint/lint1/Makefile --- a/usr.bin/xlint/lint1/Makefile +++ b/usr.bin/xlint/lint1/Makefile @@ -1,65 +1,257 @@ -# $NetBSD: Makefile,v 1.13 2016/08/19 10:21:50 christos Exp $ +# $NetBSD: Makefile,v 1.108 2021/08/31 18:59:26 rillig Exp $ NOMAN= # defined +MAX_MESSAGE= 346 # see lint1/err.c .include +ARCHSUBDIR!= cd ${NETBSDSRCDIR}/usr.bin/xlint/lint1 && ${MAKE} -v ARCHSUBDIR + TESTSDIR= ${TESTSBASE}/usr.bin/xlint/lint1 TESTS_SH= t_integration +TESTS_SH_SRC_t_integration= archsubdir.sh t_integration.sh + +archsubdir.sh: + @echo archsubdir=${ARCHSUBDIR} >${.TARGET} FILESDIR= ${TESTSDIR} +FILES+= msg_259_c90.c +FILES+= msg_259_c90.exp +FILES+= c11_generic_expression.c +FILES+= c11_generic_expression.exp +FILES+= c90.c +FILES+= c90.exp +FILES+= c99_bool_strict_suppressed.c +FILES+= c99_init_array.c +FILES+= c99_init_array.exp +FILES+= c99_init_designator.c +FILES+= c99_init_designator.exp FILES+= d_alignof.c +FILES+= d_bltinoffsetof.c +FILES+= d_c99_bool.c +FILES+= d_c99_bool.exp +FILES+= d_c99_bool_strict.c +FILES+= d_c99_bool_strict.exp +FILES+= d_c99_bool_strict_syshdr.c +FILES+= d_c99_bool_strict_syshdr.exp FILES+= d_c99_anon_struct.c FILES+= d_c99_anon_union.c FILES+= d_c99_complex_num.c FILES+= d_c99_complex_split.c +FILES+= d_c99_complex_split.exp FILES+= d_c99_compound_literal_comma.c FILES+= d_c99_decls_after_stmt.c FILES+= d_c99_decls_after_stmt2.c FILES+= d_c99_decls_after_stmt3.c -FILES+= d_c99_flex_array_packed.c +FILES+= d_c99_flex_array_packed.c FILES+= d_c99_for_loops.c FILES+= d_c99_func.c +FILES+= d_c99_init.c +FILES+= d_c99_init.exp +FILES+= d_c99_nested_struct.c FILES+= d_c99_recursive_init.c FILES+= d_c99_struct_init.c -FILES+= d_c99_nested_struct.c FILES+= d_c99_union_cast.c +FILES+= d_c99_union_cast.exp FILES+= d_c99_union_init1.c FILES+= d_c99_union_init2.c FILES+= d_c99_union_init3.c FILES+= d_c99_union_init4.c +FILES+= d_c99_union_init5.c FILES+= d_c9x_array_init.c FILES+= d_c9x_recursive_init.c FILES+= d_cast_fun_array_param.c FILES+= d_cast_init.c FILES+= d_cast_init2.c FILES+= d_cast_lhs.c +FILES+= d_cast_lhs.exp +FILES+= d_cast_typeof.c FILES+= d_compound_literals1.c FILES+= d_compound_literals2.c FILES+= d_constant_conv1.c +FILES+= d_constant_conv1.exp FILES+= d_constant_conv2.c -FILES+= d_cvt_in_ternary.c +FILES+= d_constant_conv2.exp FILES+= d_cvt_constant.c +FILES+= d_cvt_constant.exp +FILES+= d_cvt_in_ternary.c +FILES+= d_decl_old_style_arguments.c +FILES+= d_decl_old_style_arguments.exp FILES+= d_ellipsis_in_switch.c +FILES+= d_fold_test.c +FILES+= d_fold_test.exp FILES+= d_gcc_compound_statements1.c +FILES+= d_gcc_compound_statements1.exp FILES+= d_gcc_compound_statements2.c FILES+= d_gcc_compound_statements3.c FILES+= d_gcc_extension.c FILES+= d_gcc_func.c FILES+= d_gcc_variable_array_init.c FILES+= d_incorrect_array_size.c +FILES+= d_incorrect_array_size.exp +FILES+= d_init_array_using_string.c +FILES+= d_init_array_using_string.exp +FILES+= d_init_pop_member.c +FILES+= d_init_pop_member.exp +FILES+= d_lint_assert.c +FILES+= d_lint_assert.exp FILES+= d_long_double_int.c +FILES+= d_long_double_int.exp FILES+= d_nested_structs.c FILES+= d_nolimit_init.c FILES+= d_packed_structs.c +FILES+= d_pr_22119.c +FILES+= d_pr_22119.exp +FILES+= d_return_type.c +FILES+= d_return_type.exp FILES+= d_shift_to_narrower_type.c +FILES+= d_struct_init_nested.c +FILES+= d_struct_init_nested.exp FILES+= d_type_conv1.c +FILES+= d_type_conv1.exp FILES+= d_type_conv2.c +FILES+= d_type_conv2.exp FILES+= d_type_conv3.c +FILES+= d_type_conv3.exp FILES+= d_type_question_colon.c -FILES+= d_typename_as_var.c FILES+= d_typefun.c +FILES+= d_typename_as_var.c FILES+= d_zero_sized_arrays.c +FILES+= decl.c +FILES+= decl.exp +FILES+= decl_arg.c +FILES+= decl_arg.exp +FILES+= decl_enum.c +FILES+= decl_enum.exp +FILES+= decl_enum_c90.c +FILES+= decl_enum_c90.exp +FILES+= decl_struct_c90.c +FILES+= decl_struct_c90.exp +FILES+= decl_struct_member.c +FILES+= decl_struct_member.exp +FILES+= emit.c +FILES+= emit.exp +FILES+= emit.exp-ln +FILES+= emit_lp64.c +FILES+= emit_lp64.exp-ln +FILES+= expr_binary.c +FILES+= expr_binary.exp +FILES+= expr_binary_trad.c +FILES+= expr_binary_trad.exp +FILES+= expr_cast.c +FILES+= expr_cast.exp +FILES+= expr_fold.c +FILES+= expr_fold.exp +FILES+= expr_fold_strict_bool.c +FILES+= expr_fold_strict_bool.exp +FILES+= expr_precedence.c +FILES+= expr_precedence.exp +FILES+= expr_promote.c +FILES+= expr_promote.exp-ln +FILES+= expr_promote_trad.c +FILES+= expr_promote_trad.exp-ln +FILES+= expr_range.c +FILES+= expr_range.exp +FILES+= feat_stacktrace.c +FILES+= feat_stacktrace.exp +FILES+= gcc_attribute.c +FILES+= gcc_attribute.exp +FILES+= gcc_attribute_aligned.c +FILES+= gcc_attribute_aligned.exp +FILES+= gcc_attribute_enum.c +FILES+= gcc_attribute_enum.exp +FILES+= gcc_attribute_func.c +FILES+= gcc_attribute_func.exp +FILES+= gcc_attribute_label.c +FILES+= gcc_attribute_stmt.c +FILES+= gcc_attribute_stmt.exp +FILES+= gcc_attribute_type.c +FILES+= gcc_attribute_type.exp +FILES+= gcc_attribute_var.c +FILES+= gcc_attribute_var.exp +FILES+= gcc_bit_field_types.c +FILES+= gcc_bit_field_types.exp +FILES+= gcc_cast_union.c +FILES+= gcc_cast_union.exp +FILES+= gcc_init_compound_literal.c +FILES+= gcc_init_compound_literal.exp +FILES+= gcc_stmt_asm.c +FILES+= gcc_stmt_asm.exp +FILES+= gcc_typeof.c +FILES+= gcc_typeof.exp +FILES+= gcc_typeof_after_statement.c +FILES+= gcc_typeof_after_statement.exp +FILES+= init.c +FILES+= init.exp +FILES+= init_c90.c +FILES+= init_c90.exp +FILES+= lex_char.c +FILES+= lex_char.exp +FILES+= lex_char_uchar.c +FILES+= lex_comment.c +FILES+= lex_comment.exp +FILES+= lex_floating.c +FILES+= lex_floating.exp +FILES+= lex_integer.c +FILES+= lex_integer.exp +FILES+= lex_integer_binary.c +FILES+= lex_integer_binary.exp +FILES+= lex_integer_ilp32.c +FILES+= lex_integer_ilp32.exp +FILES+= lex_string.c +FILES+= lex_string.exp +FILES+= lex_wide_char.c +FILES+= lex_wide_char.exp +FILES+= lex_wide_string.c +FILES+= lex_wide_string.exp +FILES+= ${:U0 ${:U:${:Urange=${MAX_MESSAGE}}}:C,^.$,0&,:C,^..$,0&,:@i@msg_${i}.c msg_${i}.exp@:Nmsg_176.exp} +FILES+= msg_001_c90.c +FILES+= msg_001_c90.exp +FILES+= msg_000_c90.c +FILES+= msg_000_c90.exp +FILES+= msg_132_ilp32.c +FILES+= msg_132_ilp32.exp +FILES+= msg_230_uchar.c +FILES+= msg_230_uchar.exp +FILES+= msg_259_ilp32.c +FILES+= msg_259_ilp32.exp +FILES+= msg_272_c90.c +FILES+= msg_272_c90.exp +FILES+= op_colon.c +FILES+= op_colon.exp +FILES+= op_shl_lp64.c +FILES+= op_shl_lp64.exp +FILES+= parse_init_declarator.c +FILES+= parse_init_declarator.exp +FILES+= parse_stmt_error.c +FILES+= parse_stmt_error.exp +FILES+= parse_stmt_iter_error.c +FILES+= parse_stmt_iter_error.exp +FILES+= parse_type_name.c +FILES+= parse_type_name.exp +FILES+= stmt_for.c +FILES+= stmt_for.exp +FILES+= stmt_goto.c +FILES+= stmt_goto.exp +FILES+= stmt_if.c +FILES+= stmt_if.exp + +# Note: only works for adding tests. +# To remove a test, the $$mi file must be edited manually. +sync-mi: .PHONY + @set -eu; \ + cd "${MAKEFILE:tA:H}/../../../.."; \ + mi="distrib/sets/lists/tests/mi"; \ + cvs update "$$mi"; \ + fmt="./usr/tests/usr.bin/xlint/lint1/%s\ttests-usr.bin-tests\tcompattestfile,atf\n"; \ + cat "$$mi" > "$$mi.tmp"; \ + printf "$$fmt" ${FILES} >> "$$mi.tmp"; \ + distrib/sets/fmt-list "$$mi.tmp"; \ + mv "$$mi.tmp" "$$mi"; \ + cvs diff "$$mi" || true + +accept: .PHONY + @archsubdir=${ARCHSUBDIR:Q} sh ./accept.sh '' .include diff --git a/usr.bin/xlint/lint1/accept.sh b/usr.bin/xlint/lint1/accept.sh new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/accept.sh @@ -0,0 +1,99 @@ +#! /bin/sh +# $NetBSD: accept.sh,v 1.8 2021/08/26 19:23:25 rillig Exp $ +# +# Copyright (c) 2021 The NetBSD Foundation, Inc. +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +# usage: accept.sh ... +# +# Accept the actual output from running the lint tests and save them +# back into the .exp files. + +set -eu + +. './t_integration.sh' + +for pattern in "$@"; do + # shellcheck disable=SC2231 + for test in *$pattern*.c; do + base=${test%.*} + cfile="$base.c" + expfile="$base.exp" + tmpfile="$base.exp.tmp" + ln_file="$base.exp-ln" + + configure_test_case "$cfile" + # shellcheck disable=SC2154 + if [ "$skip" = yes ]; then + continue + fi + + if [ ! -f "$ln_file" ]; then + ln_file='/dev/null' + fi + + # shellcheck disable=SC2154 + # shellcheck disable=SC2086 + if "$lint1" $flags "$base.c" "$ln_file" > "$tmpfile"; then + if [ -s "$tmpfile" ]; then + echo "$base produces output but exits successfully" + sed 's,^,| ,' "$tmpfile" + fi + rm -f "$expfile" "$tmpfile" + elif [ $? -ge 128 ]; then + echo "$base crashed" + continue + else + if [ -f "$tmpfile" ] && cmp -s "$tmpfile" "$expfile"; then + rm "$tmpfile" + else + echo "replacing $base" + mv "$tmpfile" "$expfile" + fi + fi + + case "$base" in (msg_*) + if grep 'This message is not used\.' "$cfile" >/dev/null; then + : 'Skip further checks.' + elif [ ! -f "$expfile" ]; then + echo "$base should produce warnings" + elif grep '^TODO: "Add example code' "$base.c" >/dev/null; then + : 'ok, this test is not yet written' + else + msgid=${base} + msgid=${msgid#msg_00} + msgid=${msgid#msg_0} + msgid=${msgid#msg_} + msgid=${msgid%%_*} + if ! grep "\\[$msgid\\]" "$expfile" >/dev/null; then + echo "$base should trigger the message '$msgid'" + fi + fi + esac + done +done + +# shellcheck disable=SC2035 +lua '../check-expect.lua' *.c diff --git a/usr.bin/xlint/lint1/c11_generic_expression.c b/usr.bin/xlint/lint1/c11_generic_expression.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/c11_generic_expression.c @@ -0,0 +1,106 @@ +/* $NetBSD: c11_generic_expression.c,v 1.10 2021/08/01 21:12:31 rillig Exp $ */ +# 3 "c11_generic_expression.c" + +/* + * C99 added support for type-generic macros, but these were limited to the + * header . C11 made this feature generally available. + * + * The generic selection is typically used with macros, but since lint1 works + * on the preprocessed source, the test cases look a bit strange. + * + * C99 6.5.1.1 "Generic selection" + */ + +/* lint1-extra-flags: -Ac11 */ + +/* + * The type of 'var' is not compatible with any of the types from the + * generic-association. This is a compile-time error. + */ +const char * +classify_type_without_default(double var) +{ + /* expect-2: argument 'var' unused */ + + return _Generic(var, + long double: "long double", + long long: "long long", + unsigned: "unsigned" + ); + /* expect-1: expects to return value [214] */ +} + +/* + * In this case, the 'default' expression is selected. + */ +const char * +classify_type_with_default(double var) +{ + /* expect-2: argument 'var' unused */ + + return _Generic(var, + long double: "long double", + long long: "long long", + unsigned: "unsigned", + default: "unknown" + ); +} + +/* + * The type of a _Generic expression is the one from the selected association. + */ +const char * +classify_char(char c) +{ + /* expect-2: argument 'c' unused */ + + return _Generic(c, + char: "yes", + default: 0.0 + ); +} + +/* + * Before cgram.y 1.238 from 2021-06-27, lint accepted a comma-expression, + * which looked as if _Generic would accept multiple arguments before the + * selection. + */ +/* ARGSUSED */ +const int * +comma_expression(char first, double second) +{ + return _Generic(first, second, /* expect: syntax error 'second' */ + char: "first", + double: 2.0 + ); + /* expect+1: without returning value [217] */ +} + +/* + * Ensure that assignment-expressions are accepted by the grammar, as + * opposed to comma-expressions. + */ +/* ARGSUSED */ +int +assignment_expression(int first, int second) +{ + return _Generic(first = second, + int: second = first + ); +} + +int +primary_expression(void) +{ + return _Generic(0, int: assignment_expression)(0, 0); +} + +/* + * The types don't match, therefore build_generic_selection returns NULL, + * which is then silently ignored by init_expr. This situation is already + * covered by the compilers, so there is no need for lint to double-check it. + */ +const char *x = _Generic( + 1ULL + 1.0f, + int: 1 +); diff --git a/usr.bin/xlint/lint1/c11_generic_expression.exp b/usr.bin/xlint/lint1/c11_generic_expression.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/c11_generic_expression.exp @@ -0,0 +1,6 @@ +c11_generic_expression.c(29): warning: function 'classify_type_without_default' expects to return value [214] +c11_generic_expression.c(21): warning: argument 'var' unused in function 'classify_type_without_default' [231] +c11_generic_expression.c(37): warning: argument 'var' unused in function 'classify_type_with_default' [231] +c11_generic_expression.c(53): warning: argument 'c' unused in function 'classify_char' [231] +c11_generic_expression.c(72): error: syntax error 'second' [249] +c11_generic_expression.c(77): warning: function comma_expression falls off bottom without returning value [217] diff --git a/usr.bin/xlint/lint1/c90.c b/usr.bin/xlint/lint1/c90.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/c90.c @@ -0,0 +1,12 @@ +/* $NetBSD: c90.c,v 1.1 2021/07/25 22:03:42 rillig Exp $ */ +# 3 "c90.c" + +/* + * Tests for the option -s, which allows features from C90, but neither any + * later C standards nor GNU extensions. + */ + +/* lint1-flags: -sw */ + +/* expect+1: error: ANSI C requires formal parameter before '...' [84] */ +void varargs_function(...); diff --git a/usr.bin/xlint/lint1/c90.exp b/usr.bin/xlint/lint1/c90.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/c90.exp @@ -0,0 +1 @@ +c90.c(12): error: ANSI C requires formal parameter before '...' [84] diff --git a/usr.bin/xlint/lint1/c99_bool_strict_suppressed.c b/usr.bin/xlint/lint1/c99_bool_strict_suppressed.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/c99_bool_strict_suppressed.c @@ -0,0 +1,43 @@ +/* $NetBSD: c99_bool_strict_suppressed.c,v 1.4 2021/08/08 13:19:51 rillig Exp $ */ +# 3 "c99_bool_strict_suppressed.c" + +/* + * In strict bool mode, like everywhere else, individual errors can be + * suppressed. Suppressing a message affects lint's output as well as the + * exit status. Lint's control flow stays the same as before though. + * + * This can result in assertion failures later. One such assertion has been + * there since at least 1995, at the beginning of expr(), ensuring that the + * expression is either non-null or an error message has been _printed_. + * In 1995, it was not possible to suppress error messages, which means that + * the number of printed errors equaled the number of occurred errors. + * + * In err.c 1.12 from 2000-07-06, the option -X was added, allowing to + * suppress individual error messages. That commit did not mention any + * interaction with the assertion in expr(). The assertion was removed in + * tree.c 1.305 from 2021-07-04. + */ + +/* lint1-extra-flags: -T -X 107,330,331,332,333 */ + +/* ARGSUSED */ +void +test(_Bool b, int i, const char *p) +{ + + /* suppressed+1: error: controlling expression must be bool, not 'int' [333] */ + while (1) + break; + + /* suppressed+1: error: operands of '=' have incompatible types (_Bool != int) [107] */ + b = i; + + /* suppressed+1: error: operand of '!' must be bool, not 'int' [330] */ + b = !i; + + /* suppressed+1: error: left operand of '&&' must be bool, not 'int' [331] */ + b = i && b; + + /* suppressed+1: error: right operand of '&&' must be bool, not 'int' [332] */ + b = b && i; +} diff --git a/usr.bin/xlint/lint1/c99_init_array.c b/usr.bin/xlint/lint1/c99_init_array.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/c99_init_array.c @@ -0,0 +1,19 @@ +/* $NetBSD: c99_init_array.c,v 1.3 2021/07/02 23:29:54 rillig Exp $ */ +# 3 "c99_init_array.c" + +/* + * Test C99-style initialization of arrays. + */ + +/* lint1-extra-flags: -p */ + +// The size of the array is determined by the maximum index, not by the last +// one mentioned. +int arr_11[] = { [10] = 10, [0] = 0 }; +typedef int ctassert_11[-(int)(sizeof(arr_11) / sizeof(arr_11[0]))]; +/* expect-1: error: negative array dimension (-11) [20] */ + +// Without an explicit subscript designator, the subscript counts up. +int arr_3[] = { [1] = 1, [0] = 0, 1, 2 }; +typedef int ctassert_3[-(int)(sizeof(arr_3) / sizeof(arr_3[0]))]; +/* expect-1: error: negative array dimension (-3) [20] */ diff --git a/usr.bin/xlint/lint1/c99_init_array.exp b/usr.bin/xlint/lint1/c99_init_array.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/c99_init_array.exp @@ -0,0 +1,2 @@ +c99_init_array.c(13): error: negative array dimension (-11) [20] +c99_init_array.c(18): error: negative array dimension (-3) [20] diff --git a/usr.bin/xlint/lint1/c99_init_designator.c b/usr.bin/xlint/lint1/c99_init_designator.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/c99_init_designator.c @@ -0,0 +1,28 @@ +/* $NetBSD: c99_init_designator.c,v 1.1 2021/06/20 18:09:48 rillig Exp $ */ +# 3 "c99_init_designator.c" + +/* + * Test initialization of structs or unions using designators. + * + * See init.c, 'struct designator' and 'struct designation'. + * + * C99 6.7.8p6, 6.7.8p7 + */ + +struct point { + int x; + int y; +}; + +/* + * Before cgram.y 1.230 from 2021-06-20, the grammar allowed either of the + * operators '.' or '->' to be used for the designators and had extra code + * to ensure that only '.' was actually used. + */ +struct point origin = { + .x = 0, + ->y = 0, /* expect: syntax error '->' */ +}; + +/* Ensure that the parser can recover from the parse error. */ +struct point pythagoras = { 3, 4 }; diff --git a/usr.bin/xlint/lint1/c99_init_designator.exp b/usr.bin/xlint/lint1/c99_init_designator.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/c99_init_designator.exp @@ -0,0 +1 @@ +c99_init_designator.c(24): error: syntax error '->' [249] diff --git a/usr.bin/xlint/lint1/d_alignof.c b/usr.bin/xlint/lint1/d_alignof.c --- a/usr.bin/xlint/lint1/d_alignof.c +++ b/usr.bin/xlint/lint1/d_alignof.c @@ -1,3 +1,6 @@ +/* $NetBSD: d_alignof.c,v 1.2 2021/01/31 14:39:31 rillig Exp $ */ +# 3 "d_alignof.c" + /* __alignof__ */ int main(void) diff --git a/usr.bin/xlint/lint1/d_bltinoffsetof.c b/usr.bin/xlint/lint1/d_bltinoffsetof.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/d_bltinoffsetof.c @@ -0,0 +1,14 @@ +/* $NetBSD: d_bltinoffsetof.c,v 1.2 2021/01/31 14:39:31 rillig Exp $ */ +# 3 "d_bltinoffsetof.c" + +struct foo { + int a; + char *b; +}; + + +int +main(void) +{ + return __builtin_offsetof(struct foo, b); +} diff --git a/usr.bin/xlint/lint1/d_c99_anon_struct.c b/usr.bin/xlint/lint1/d_c99_anon_struct.c --- a/usr.bin/xlint/lint1/d_c99_anon_struct.c +++ b/usr.bin/xlint/lint1/d_c99_anon_struct.c @@ -1,3 +1,6 @@ +/* $NetBSD: d_c99_anon_struct.c,v 1.4 2021/07/10 10:56:31 rillig Exp $ */ +# 3 "d_c99_anon_struct.c" + /* Anonymous struct test */ typedef int type; @@ -7,7 +10,7 @@ int y; }; -struct bar { +struct rect { struct { struct point top_left; struct point bottom_right; @@ -19,8 +22,7 @@ int main(void) { - struct bar b; - b.top_left.x = 1; + struct rect r; + r.top_left.x = 1; return 0; } - diff --git a/usr.bin/xlint/lint1/d_c99_anon_union.c b/usr.bin/xlint/lint1/d_c99_anon_union.c --- a/usr.bin/xlint/lint1/d_c99_anon_union.c +++ b/usr.bin/xlint/lint1/d_c99_anon_union.c @@ -1,3 +1,6 @@ +/* $NetBSD: d_c99_anon_union.c,v 1.4 2021/02/20 22:31:20 rillig Exp $ */ +# 3 "d_c99_anon_union.c" + /* struct with only anonymous members */ struct foo { @@ -7,9 +10,11 @@ }; }; -int -main(void) { +int printf(const char *, ...); +int +main(void) +{ struct foo *f = 0; printf("%p\n", &f[1]); return 0; diff --git a/usr.bin/xlint/lint1/d_c99_bool.c b/usr.bin/xlint/lint1/d_c99_bool.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/d_c99_bool.c @@ -0,0 +1,110 @@ +/* $NetBSD: d_c99_bool.c,v 1.7 2021/03/30 14:25:28 rillig Exp $ */ +# 3 "d_c99_bool.c" + +/* + * C99 6.3.1.2 says: "When any scalar value is converted to _Bool, the result + * is 0 if the value compares equal to 0; otherwise the result is 1." + * + * This is different from the other integer types, which get truncated or + * invoke undefined behavior. + */ + +/* Below, each false statement produces "negative array dimension" [20]. */ + +int int_0_converts_to_false[(_Bool)0 ? -1 : 1]; +int int_0_converts_to_true_[(_Bool)0 ? 1 : -1]; /* expect: 20 */ + +int int_1_converts_to_false[(_Bool)1 ? -1 : 1]; /* expect: 20 */ +int int_1_converts_to_true_[(_Bool)1 ? 1 : -1]; + +int int_2_converts_to_false[(_Bool)2 ? -1 : 1]; /* expect: 20 */ +int int_2_converts_to_true_[(_Bool)2 ? 1 : -1]; + +int int_256_converts_to_false[(_Bool)256 ? -1 : 1]; /* expect: 20 */ +int int_256_converts_to_true_[(_Bool)256 ? 1 : -1]; + +int null_pointer_converts_to_false[(_Bool)(void *)0 ? -1 : 1]; +int null_pointer_converts_to_true_[(_Bool)(void *)0 ? 1 : -1]; /* expect: 20 */ + +/* + * XXX: lint does not treat the address of a global variable as a constant + * expression. This goes against C99 6.6p7 but is probably not too relevant + * in practice. + * + * The call to constant(tn, 0) defaults to 1, then. This is why neither of + * the following array declarations generates an error message. + */ +char ch; +int nonnull_pointer_converts_to_false[(_Bool)&ch ? -1 : 1]; +int nonnull_pointer_converts_to_true_[(_Bool)&ch ? 1 : -1]; + +int double_minus_1_0_converts_to_false[(_Bool)-1.0 ? -1 : 1]; /* expect: 20 */ +int double_minus_1_0_converts_to_true_[(_Bool)-1.0 ? 1 : -1]; + +int double_minus_0_5_converts_to_false[(_Bool)-0.5 ? -1 : 1]; /* expect: 20 */ +int double_minus_0_5_converts_to_true_[(_Bool)-0.5 ? 1 : -1]; + +int double_minus_0_0_converts_to_false[(_Bool)-0.0 ? -1 : 1]; +int double_minus_0_0_converts_to_true_[(_Bool)-0.0 ? 1 : -1]; /* expect: 20 */ + +int double_0_0_converts_to_false[(_Bool)0.0 ? -1 : 1]; +int double_0_0_converts_to_true_[(_Bool)0.0 ? 1 : -1]; /* expect: 20 */ + +/* The C99 rationale explains in 6.3.1.2 why (_Bool)0.5 is true. */ +int double_0_5_converts_to_false[(_Bool)0.5 ? -1 : 1]; /* expect: 20 */ +int double_0_5_converts_to_true_[(_Bool)0.5 ? 1 : -1]; + +int double_1_0_converts_to_false[(_Bool)1.0 ? -1 : 1]; /* expect: 20 */ +int double_1_0_converts_to_true_[(_Bool)1.0 ? 1 : -1]; + +_Bool +bool_to_bool(_Bool b) +{ + return b; +} + +_Bool +char_to_bool(char c) +{ + return c; +} + +_Bool +int_to_bool(int i) +{ + return i; +} + +_Bool +double_to_bool(double d) +{ + return d; +} + +enum color { + RED +}; + +_Bool +enum_to_bool(enum color e) +{ + return e; +} + +_Bool +pointer_to_bool(const char *p) +{ + return p; +} + +_Bool +function_pointer_to_bool(void (*f)(void)) +{ + return f; +} + +_Bool +complex_to_bool(double _Complex c) +{ + return c; +} diff --git a/usr.bin/xlint/lint1/d_c99_bool.exp b/usr.bin/xlint/lint1/d_c99_bool.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/d_c99_bool.exp @@ -0,0 +1,11 @@ +d_c99_bool.c(15): error: negative array dimension (-1) [20] +d_c99_bool.c(17): error: negative array dimension (-1) [20] +d_c99_bool.c(20): error: negative array dimension (-1) [20] +d_c99_bool.c(23): error: negative array dimension (-1) [20] +d_c99_bool.c(27): error: negative array dimension (-1) [20] +d_c99_bool.c(41): error: negative array dimension (-1) [20] +d_c99_bool.c(44): error: negative array dimension (-1) [20] +d_c99_bool.c(48): error: negative array dimension (-1) [20] +d_c99_bool.c(51): error: negative array dimension (-1) [20] +d_c99_bool.c(54): error: negative array dimension (-1) [20] +d_c99_bool.c(57): error: negative array dimension (-1) [20] diff --git a/usr.bin/xlint/lint1/d_c99_bool_strict.c b/usr.bin/xlint/lint1/d_c99_bool_strict.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/d_c99_bool_strict.c @@ -0,0 +1,778 @@ +/* $NetBSD: d_c99_bool_strict.c,v 1.30 2021/07/04 07:09:39 rillig Exp $ */ +# 3 "d_c99_bool_strict.c" + +/* + * The option -T treats _Bool as incompatible with all other scalar types. + * This is implemented by the following rules: + * + * strict-bool-typedef: + * The type _Bool is compatible with any typedef of _Bool. + * + * Note: Since defines bool as textual alias of _Bool, + * having another typedef for bool is unusual. + * + * strict-bool-constant: + * There are 2 bool constants named false and true. + * No other constants are compatible with type _Bool. + * + * Note: Internally these constants are named __lint_false and + * __lint_true. + * + * strict-bool-bit-field: + * A struct or union member that is a bit field with underlying type + * bool is compatible with plain bool. + * + * strict-bool-conversion: + * There is no implicit conversion between _Bool and any other type. + * + * strict-bool-controlling-expression: + * Controlling expressions in 'if', 'while', 'for', '?:' must be of + * type bool. + * + * strict-bool-operand-unary: + * Operator bool? scalar? + * ! yes - + * & yes yes + * The other unary operators do not accept bool operands. + * + * strict-bool-operand-binary: + * Operator left: bool? other? right: bool? other? + * . - yes yes yes + * -> - yes yes yes + * <=, <, >=, > - yes - yes + * ==, != yes yes yes yes + * & yes yes yes yes + * ^ yes yes yes yes + * | yes yes yes yes + * && yes - yes - + * || yes - yes - + * ? yes - yes yes + * : yes yes yes yes + * = yes yes yes yes + * &=, ^=, |= yes yes yes yes + * , yes yes yes yes + * The other binary operators do not accept bool operands. + * + * strict-bool-operator-result: + * The result type of the operators '!', '<', '<=', '>', '>=', + * '==', '!=', '&&', '||' is _Bool instead of int. + * + * strict-bool-bitwise-and: + * Expressions of the form "flags & FLAG" are compatible with _Bool if + * the left operand has enum type, the right operand is an integer + * constant and the resulting value is used in a context where it is + * implicitly and immediately compared to zero. + * + * Note: An efficient implementation technique for a collection of bool + * flags is an enum. The enum declaration groups the available + * constants, and as of 2020, compilers such as GCC and Clang have basic + * support for detecting type mismatches on enums. + * + * Note: Examples for such contexts are controlling expressions or the + * operands of the operators '!', '&&', '||'. + * + * Note: Counterexamples for contexts are assignments to a bool variable. + * + * Note: These rules ensure that conforming code can be compiled without + * change in behavior using old compilers that implement bool as an + * ordinary integer type, without the special rule C99 6.3.1.2. + * + * Note: There is a crucial difference between a _Bool variable and an + * ordinary integer variable. C99 6.3.1.2 defines a conversion from an + * arbitrary scalar value to _Bool as equivalent to (value != 0 ? 1 : 0). + * This means that even if _Bool is implemented as an 8-bit unsigned + * integer, assigning 256 to it would still result in the value 1 being + * stored. Storing 256 in an ordinary 8-bit unsigned integer would + * result in the value 0 being stored. See the test d_c99_bool.c for + * more details. + */ + +/* + * The header defines the macros bool = _Bool, false = 0 and + * true = 1. Without further hacks, this would mean that constant expressions + * of integer type have to be regarded as possible boolean constants if their + * value is either 0 or 1. + * + * This would not help in migrating old code to use bool consistently. + * Therefore lint provides its own header that expands false to + * __lint_false and true to __lint_true, two predefined constant expressions. + */ + +/* lint1-extra-flags: -hT */ + +/* + * strict-bool-typedef + */ + +/* + * Using a typedef for bool does not hurt the checks, they all use the + * underlying basic type (see tspec_t), which is BOOL. + */ +typedef _Bool bool; + +extern void accept_bool(bool); +extern void println(const char *); +extern void take_arguments(bool, int, const char *, ...); +extern void do_nothing(void); + +/* + * strict-bool-constant + */ + +void +strict_bool_constant(void) +{ + accept_bool(__lint_false); + accept_bool(__lint_true); + accept_bool(0); /* expect: 334 */ + accept_bool(1); /* expect: 334 */ + accept_bool(2); /* expect: 334 */ +} + +enum strict_bool_constant_expressions { + /* Ok: __lint_false is a boolean constant expression. */ + FALSE = __lint_false ? 100 : 101, /* expect: 161 */ + + /* Ok: __lint_true is a boolean constant expression. */ + TRUE = __lint_true ? 100 : 101, /* expect: 161 */ + + /* Not ok: an integer is not a boolean constant expression. */ + INT0 = 0 ? 100 : 101, /* expect: 331 */ + + /* Not ok: an integer is not a boolean constant expression. */ + INT1 = 1 ? 100 : 101, /* expect: 331 */ + + /* Not ok: 2 is not a boolean constant. */ + INT2 = 2 ? 100 : 101, /* expect: 331 */ + + /* Not ok: compound integer expressions are not bool. */ + ARITH = (2 - 2) ? 100 : 101, /* expect: 331 */ + + /* + * Without strict bool mode, these two variants of an expression can + * occur when a preprocessor macro is either defined to 1 or left + * empty (since C99), as in lint1/ops.def. + * + * In strict bool mode, the resulting expression can be compared + * against 0 to achieve the same effect (so +0 != 0 or 1 + 0 != 0). + */ + BINARY_PLUS = (1 + 0) ? 100 : 101, /* expect: 331 */ + UNARY_PLUS = (+0) ? 100 : 101, /* expect: 331 */ + + /* The main operator '>' has return type bool. */ + Q1 = (13 > 12) ? 100 : 101, /* expect: 161 */ + + /* + * The parenthesized expression has type int and thus cannot be + * used as the controlling expression in the '?:' operator. + */ + Q2 = (13 > 12 ? 1 : 7) ? 100 : 101, /* expect: 161 *//* expect: 331 */ + + BINAND_BOOL = __lint_false & __lint_true, /* expect: 55 */ + BINAND_INT = 0 & 1, + + BINXOR_BOOL = __lint_false ^ __lint_true, /* expect: 55 */ + BINXOR_INT = 0 ^ 1, + + BINOR_BOOL = __lint_false | __lint_true, /* expect: 55 */ + BINOR_INT = 0 | 1, + + LOGOR_BOOL = __lint_false || __lint_true, /* expect: 161 *//* expect: 55 */ + LOGOR_INT = 0 || 1, /* expect: 331 *//* expect: 332 */ + + LOGAND_BOOL = __lint_false && __lint_true, /* expect: 161 *//* expect: 55 */ + LOGAND_INT = 0 && 1, /* expect: 331 *//* expect: 332 */ +}; + +/* + * strict-bool-bit-fields + */ + +void +strict_bool_bit_fields(void) +{ + struct flags { + bool bool_flag: 1; + unsigned uint_flag: 1; + }; + + struct flags flags = { __lint_false, 0 }; + struct flags *flags_ptr = &flags; + bool b; + + b = flags.bool_flag; + b = flags.uint_flag; /* expect: 107 */ + flags.bool_flag = b; + flags.uint_flag = b; /* expect: 107 */ + + b = flags_ptr->bool_flag; + b = flags_ptr->uint_flag; /* expect: 107 */ + flags_ptr->bool_flag = b; + flags_ptr->uint_flag = b; /* expect: 107 */ +} + +void +strict_bool_bit_fields_operand_conversion(void) +{ + struct s { + bool ordinary; + bool bit_field: 1; + }; + + struct s s = { 0 > 0 }; + + s.ordinary = s.ordinary | s.ordinary; + s.bit_field = s.bit_field | s.bit_field; +} + +/* + * strict-bool-conversion + */ + +bool +strict_bool_conversion_return_false(void) +{ + return __lint_false; +} + +bool +strict_bool_conversion_return_true(void) +{ + return __lint_true; +} + +bool +strict_bool_conversion_return_bool(bool b) +{ + return b; +} + +bool +strict_bool_conversion_return_0(void) +{ + return 0; /* expect: 211 */ +} + +bool +strict_bool_conversion_return_1(void) +{ + return 1; /* expect: 211 */ +} + +bool +strict_bool_conversion_return_2(void) +{ + return 2; /* expect: 211 */ +} + +bool +strict_bool_conversion_return_pointer(const void *p) /* expect: 231 */ +{ + return p; /* expect: 211 */ +} + +char +strict_bool_conversion_return_false_as_char(void) +{ + return __lint_false; /* expect: 211 */ +} + +char +strict_bool_conversion_return_true_as_char(void) +{ + return __lint_true; /* expect: 211 */ +} + + +void +strict_bool_conversion_function_argument(void) +{ + accept_bool(__lint_false); + accept_bool(__lint_true); +} + +void +strict_bool_conversion_function_argument_pass(bool b, int i, const char *p) +{ + /* No conversion necessary. */ + take_arguments(b, i, p); + + /* Implicitly converting bool to other scalar types. */ + take_arguments(b, b, b); /* expect: 334 *//* expect: 334 */ + + /* Implicitly converting int to bool (arg #1). */ + take_arguments(i, i, i); /* expect: 334 *//* expect: 154 */ + + /* Implicitly converting pointer to bool (arg #1). */ + take_arguments(p, p, p); /* expect: 334 *//* expect: 154 */ + + /* Passing bool as vararg. */ + take_arguments(b, i, p, b, i, p); /* TODO: expect: arg#4 */ + + /* Passing a bool constant. */ + take_arguments(__lint_false, i, p); + + /* Passing a bool constant. */ + take_arguments(__lint_true, i, p); + + /* Trying to pass integer constants. */ + take_arguments(0, i, p); /* expect: 334 */ + take_arguments(1, i, p); /* expect: 334 */ + take_arguments(2, i, p); /* expect: 334 */ +} + +void +strict_bool_conversion_between_bool_and_int(void) +{ + bool b; + int i; + + b = 0; /* expect: 107 */ + b = __lint_false; + b = 1; /* expect: 107 */ + b = __lint_true; + + i = 0; + i = __lint_false; /* expect: 107 */ + i = 1; + i = __lint_true; /* expect: 107 */ + + i = b; /* expect: 107 */ + b = i; /* expect: 107 */ +} + +void +strict_bool_conversion_from_bool_to_scalar(bool b) /* expect: 231 */ +{ + int i; + unsigned u; + double d; + void *p; + + i = b; /* expect: 107 */ + u = b; /* expect: 107 */ + d = b; /* expect: 107 */ + p = b; /* expect: 107 */ +} + +/* + * strict-bool-controlling-expression: + * Controlling expressions in 'if', 'while', 'for', '?:' must be of + * type bool. + */ + +void +strict_bool_controlling_expression(bool b, int i, double d, const void *p) +{ + if (__lint_false) /* expect: 161 */ + do_nothing(); /* expect: statement not reached */ + + if (__lint_true) /* expect: 161 */ + do_nothing(); + + if (b) + do_nothing(); + + if (/*CONSTCOND*/0) /* expect: 333 */ + do_nothing(); /* expect: statement not reached [193] */ + + if (/*CONSTCOND*/1) /* expect: 333 */ + do_nothing(); + + if (/*CONSTCOND*/2) /* expect: 333 */ + do_nothing(); + + /* Not allowed: There is no implicit conversion from scalar to bool. */ + if (i) /* expect: 333 */ + do_nothing(); + if (i != 0) + do_nothing(); + + /* Not allowed: There is no implicit conversion from scalar to bool. */ + if (d) /* expect: 333 */ + do_nothing(); + if (d != 0.0) + do_nothing(); + + /* Not allowed: There is no implicit conversion from scalar to bool. */ + if (p) /* expect: 333 */ + do_nothing(); + if (p != (void *)0) + do_nothing(); +} + +/* + * strict-bool-operand-unary: + * Operator bool? scalar? + * ! yes - + * & yes yes + * The other unary operators do not accept bool operands. + */ + +void +strict_bool_operand_unary_not(void) +{ + bool b = __lint_false; + + b = !b; + b = !!!b; + b = !__lint_false; /* expect: 161 *//* expect: 239 */ + b = !__lint_true; /* expect: 161 *//* expect: 239 */ + + int i = 0; + + i = !i; /* expect: 330 */ + i = !!!i; /* expect: 330 */ + i = !0; /* expect: 330 */ + i = !1; /* expect: 330 */ +} + +void +strict_bool_operand_unary_address(void) +{ + bool b = __lint_false; + + /* Taking the address of a bool lvalue. */ + bool *bp; + bp = &b; + *bp = b; + b = *bp; +} + +/* see strict_bool_operand_unary_all below for the other unary operators. */ + +/* + * strict-bool-operand-binary: + * Operator left: bool? other? right: bool? other? + * . - yes yes yes + * -> - yes yes yes + * <=, <, >=, > - yes - yes + * ==, != yes yes yes yes + * & yes yes yes yes + * ^ yes yes yes yes + * | yes yes yes yes + * && yes - yes - + * || yes - yes - + * ? yes - yes yes + * : yes yes yes yes + * = yes yes yes yes + * &=, ^=, |= yes yes yes yes + * , yes yes yes yes + * The other binary operators do not accept bool operands. + */ + +/* + * Ensure that bool members can be accessed as usual. + */ +void +strict_bool_operand_binary_dot_arrow(void) +{ + struct bool_struct { + bool b; + }; + + /* Initialize and assign using boolean constants. */ + bool b = __lint_false; + b = __lint_true; + + /* Access a struct member using the '.' operator. */ + struct bool_struct bs = { __lint_true }; + b = bs.b; + bs.b = b; + bs.b = 0; /* expect: 107 */ + + /* Access a struct member using the '->' operator. */ + struct bool_struct *bsp = &bs; + b = bsp->b; + bsp->b = b; + bsp->b = 0; /* expect: 107 */ +} + +int +strict_bool_operand_binary(bool b, int i) +{ + + /* The right-hand sides of these assignments are ok. */ + b = !b; + b = b && b; + b = b || b; + + /* + * The right-hand sides of these assignments implicitly convert from + * scalar to bool. + */ + b = !i; /* expect: 330 */ + b = i && i; /* expect: 331 *//* expect: 332 */ + b = i || i; /* expect: 331 *//* expect: 332 */ + + b = b && 0; /* expect: 332 */ + b = 0 && b; /* expect: 331 */ + b = b || 0; /* expect: 332 */ + b = 0 || b; /* expect: 331 */ + + return i; +} + +void +strict_bool_operand_unary_all(bool b) +{ + b = !b; + b = ~b; /* expect: 335 */ + ++b; /* expect: 335 */ + --b; /* expect: 335 */ + b++; /* expect: 335 */ + b--; /* expect: 335 */ + b = +b; /* expect: 335 */ + b = -b; /* expect: 335 */ +} + +void +strict_bool_operand_binary_all(bool b, unsigned u) +{ + b = b * b; /* expect: 336 *//* expect: 337 */ + b = b / b; /* expect: 336 *//* expect: 337 */ + b = b % b; /* expect: 336 *//* expect: 337 */ + b = b + b; /* expect: 336 *//* expect: 337 */ + b = b - b; /* expect: 336 *//* expect: 337 */ + b = b << b; /* expect: 336 *//* expect: 337 */ + b = b >> b; /* expect: 336 *//* expect: 337 */ + + b = b < b; /* expect: 336 *//* expect: 337 */ + b = b <= b; /* expect: 336 *//* expect: 337 */ + b = b > b; /* expect: 336 *//* expect: 337 */ + b = b >= b; /* expect: 336 *//* expect: 337 */ + b = b == b; + b = b != b; + + b = b & b; + b = b ^ b; + b = b | b; + b = b && b; + b = b || b; + b = b ? b : b; + + b = b; + b *= b; /* expect: 336 *//* expect: 337 */ + b /= b; /* expect: 336 *//* expect: 337 */ + b %= b; /* expect: 336 *//* expect: 337 */ + b += b; /* expect: 336 *//* expect: 337 */ + b -= b; /* expect: 336 *//* expect: 337 */ + b <<= b; /* expect: 336 *//* expect: 337 */ + b >>= b; /* expect: 336 *//* expect: 337 */ + b &= b; + b ^= b; + b |= b; + + /* Operations with mixed types. */ + u = b * u; /* expect: 336 */ + u = u * b; /* expect: 337 */ + u = b / u; /* expect: 336 */ + u = u / b; /* expect: 337 */ + u = b % u; /* expect: 336 */ + u = u % b; /* expect: 337 */ + u = b + u; /* expect: 336 */ + u = u + b; /* expect: 337 */ + u = b - u; /* expect: 336 */ + u = u - b; /* expect: 337 */ + u = b << u; /* expect: 336 */ + u = u << b; /* expect: 337 */ + u = b >> u; /* expect: 336 */ + u = u >> b; /* expect: 337 */ + u = b ? u : u; + u = b ? b : u; /* expect: 107 */ + u = b ? u : b; /* expect: 107 */ +} + +bool +strict_bool_operand_binary_comma(bool b, int i) +{ + b = (b, !b); /* expect: 129 */ + i = (i, i + 1); /* expect: 129 */ + return b; +} + +/* + * strict-bool-operator-result: + * The result type of the operators '!', '<', '<=', '>', '>=', + * '==', '!=', '&&', '||' is _Bool instead of int. + */ + +void +strict_bool_operator_result(bool b) +{ + char c = b; /* expect: 107 */ + int i = b; /* expect: 107 */ + double d = b; /* expect: 107 */ + void *p = b; /* expect: 107 */ + + /* The right-hand sides of these assignments are all ok. */ + b = !b; + b = i == i; + b = i != i; + b = i < i; + b = i <= i; + b = i >= i; + b = i > i; + b = b && b; + b = b || b; + + /* + * The right-hand sides of these assignments are not ok, they + * implicitly convert from bool to int. + */ + i = !b; /* expect: 107 */ + i = i == i; /* expect: 107 */ + i = i != i; /* expect: 107 */ + i = i < i; /* expect: 107 */ + i = i <= i; /* expect: 107 */ + i = i >= i; /* expect: 107 */ + i = i > i; /* expect: 107 */ + i = b && b; /* expect: 107 */ + i = b || b; /* expect: 107 */ +} + + +/* + * strict-bool-bitwise-and: + * Expressions of the form "flags & FLAG" are compatible with _Bool if + * the left operand has enum type, the right operand is an integer + * constant and the resulting value is used in a context where it is + * implicitly and immediately compared to zero. + * + * Note: Examples for such contexts are controlling expressions or the + * operands of the operators '!', '&&', '||'. + * + * Note: Counterexamples for contexts are assignments to a bool variable. + * + * Note: These rules ensure that conforming code can be compiled without + * change in behavior using old compilers that implement bool as an + * ordinary integer type, without the special rule C99 6.3.1.2. + */ + +enum Flags { + FLAG0 = 1 << 0, + FLAG1 = 1 << 1, + FLAG28 = 1 << 28 +}; + +void +strict_bool_bitwise_and_enum(enum Flags flags) /* expect: 231 */ +{ + bool b; + + /* + * FLAG0 has the value 1 and thus can be stored in a bool variable + * without truncation. Nevertheless this special case is not allowed + * because it would be too confusing if FLAG0 would work and all the + * other flags wouldn't. + */ + b = flags & FLAG0; /* expect: 107 */ + + /* + * Assuming that FLAG1 is set in flags, a _Bool variable stores this + * as 1, as defined by C99 6.3.1.2. A uint8_t variable would store + * it as 2, as that is the integer value of FLAG1. Since FLAG1 fits + * in a uint8_t, no truncation takes place. + */ + b = flags & FLAG1; /* expect: 107 */ + + /* + * In a _Bool variable, FLAG28 is stored as 1, since it is unequal to + * zero. In a uint8_t, the stored value would be 0 since bit 28 is + * out of range for a uint8_t and thus gets truncated. + */ + b = flags & FLAG28; /* expect: 107 */ +} + +/* + * Demonstrate idiomatic code to query flags from an enum bit set. + * + * In all the controlling expressions in this function, the result of the + * operator '&' is compared against 0. This makes this pattern work, no + * matter whether the bits are in the low-value range or in the high-value + * range (such as FLAG28, which has the value 1073741824, which is more than + * what would fit into an unsigned char). Even if an enum could be extended + * to larger types than int, this pattern would work. + */ +void +query_flag_from_enum_bit_set(enum Flags flags) +{ + if (flags & FLAG0) + println("FLAG0 is set"); + + if ((flags & FLAG1) != 0) + println("FLAG1 is set"); + + if ((flags & (FLAG0 | FLAG1)) == (FLAG0 | FLAG1)) + println("FLAG0 and FLAG1 are both set"); + + if (flags & FLAG0 && flags & FLAG1) + println("FLAG0 and FLAG1 are both set"); + + if ((flags & (FLAG0 | FLAG1)) != 0) + println("At least one of FLAG0 and FLAG1 is set"); + + if (flags & FLAG28) + println("FLAG28 is set"); +} + + +void +strict_bool_operator_eq_bool_int(void) +{ + (void)(strict_bool_conversion_return_false() == 0); /* expect: 107 */ +} + +void +strict_bool_assign_bit_field_then_compare(void) +{ + struct s { + bool flag: 1; + }; + + struct s s = { __lint_false }; + + (void)((s.flag = s.flag) != __lint_false); /* expect: 129 */ +} + +void +bool_as_array_index(bool cond) +{ + static const char *repr[] = { "no", "yes" }; + /* + * The '+' in the error message reveals that lint internally + * translates 'arr[ind]' to '*(arr + ind)' in an early stage of + * parsing. + */ + println(repr[cond]); /* expect: 337 */ + println(cond ? "yes" : "no"); +} + +void +do_while_false(void) +{ + do { + + } while (__lint_false); +} + +void +do_while_true(void) +{ + do { + + } while (__lint_true); /* expect: 161 */ +} + +void +initialization(void) +{ + struct { + _Bool b; + } var[] = { + { __lint_false }, + { __lint_true }, + { 0 }, /* expect: 107 */ + { 1 }, /* expect: 107 */ + }; +} diff --git a/usr.bin/xlint/lint1/d_c99_bool_strict.exp b/usr.bin/xlint/lint1/d_c99_bool_strict.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/d_c99_bool_strict.exp @@ -0,0 +1,169 @@ +d_c99_bool_strict.c(127): error: argument #1 expects '_Bool', gets passed 'int' [334] +d_c99_bool_strict.c(128): error: argument #1 expects '_Bool', gets passed 'int' [334] +d_c99_bool_strict.c(129): error: argument #1 expects '_Bool', gets passed 'int' [334] +d_c99_bool_strict.c(134): warning: constant in conditional context [161] +d_c99_bool_strict.c(137): warning: constant in conditional context [161] +d_c99_bool_strict.c(140): error: left operand of '?' must be bool, not 'int' [331] +d_c99_bool_strict.c(143): error: left operand of '?' must be bool, not 'int' [331] +d_c99_bool_strict.c(146): error: left operand of '?' must be bool, not 'int' [331] +d_c99_bool_strict.c(149): error: left operand of '?' must be bool, not 'int' [331] +d_c99_bool_strict.c(159): error: left operand of '?' must be bool, not 'int' [331] +d_c99_bool_strict.c(160): error: left operand of '?' must be bool, not 'int' [331] +d_c99_bool_strict.c(163): warning: constant in conditional context [161] +d_c99_bool_strict.c(169): warning: constant in conditional context [161] +d_c99_bool_strict.c(169): error: left operand of '?' must be bool, not 'int' [331] +d_c99_bool_strict.c(171): error: integral constant expression expected [55] +d_c99_bool_strict.c(174): error: integral constant expression expected [55] +d_c99_bool_strict.c(177): error: integral constant expression expected [55] +d_c99_bool_strict.c(180): warning: constant in conditional context [161] +d_c99_bool_strict.c(180): error: integral constant expression expected [55] +d_c99_bool_strict.c(181): error: left operand of '||' must be bool, not 'int' [331] +d_c99_bool_strict.c(181): error: right operand of '||' must be bool, not 'int' [332] +d_c99_bool_strict.c(183): warning: constant in conditional context [161] +d_c99_bool_strict.c(183): error: integral constant expression expected [55] +d_c99_bool_strict.c(184): error: left operand of '&&' must be bool, not 'int' [331] +d_c99_bool_strict.c(184): error: right operand of '&&' must be bool, not 'int' [332] +d_c99_bool_strict.c(204): error: operands of '=' have incompatible types (_Bool != unsigned int) [107] +d_c99_bool_strict.c(206): error: operands of '=' have incompatible types (unsigned int != _Bool) [107] +d_c99_bool_strict.c(209): error: operands of '=' have incompatible types (_Bool != unsigned int) [107] +d_c99_bool_strict.c(211): error: operands of '=' have incompatible types (unsigned int != _Bool) [107] +d_c99_bool_strict.c(253): error: return value type mismatch (_Bool) and (int) [211] +d_c99_bool_strict.c(259): error: return value type mismatch (_Bool) and (int) [211] +d_c99_bool_strict.c(265): error: return value type mismatch (_Bool) and (int) [211] +d_c99_bool_strict.c(271): error: return value type mismatch (_Bool) and (pointer) [211] +d_c99_bool_strict.c(269): warning: argument 'p' unused in function 'strict_bool_conversion_return_pointer' [231] +d_c99_bool_strict.c(277): error: return value type mismatch (char) and (_Bool) [211] +d_c99_bool_strict.c(283): error: return value type mismatch (char) and (_Bool) [211] +d_c99_bool_strict.c(301): error: argument #2 expects 'int', gets passed '_Bool' [334] +d_c99_bool_strict.c(301): error: argument #3 expects 'pointer', gets passed '_Bool' [334] +d_c99_bool_strict.c(304): error: argument #1 expects '_Bool', gets passed 'int' [334] +d_c99_bool_strict.c(304): warning: illegal combination of pointer (pointer to const char) and integer (int), arg #3 [154] +d_c99_bool_strict.c(307): error: argument #1 expects '_Bool', gets passed 'pointer' [334] +d_c99_bool_strict.c(307): warning: illegal combination of integer (int) and pointer (pointer to const char), arg #2 [154] +d_c99_bool_strict.c(319): error: argument #1 expects '_Bool', gets passed 'int' [334] +d_c99_bool_strict.c(320): error: argument #1 expects '_Bool', gets passed 'int' [334] +d_c99_bool_strict.c(321): error: argument #1 expects '_Bool', gets passed 'int' [334] +d_c99_bool_strict.c(330): error: operands of '=' have incompatible types (_Bool != int) [107] +d_c99_bool_strict.c(332): error: operands of '=' have incompatible types (_Bool != int) [107] +d_c99_bool_strict.c(336): error: operands of '=' have incompatible types (int != _Bool) [107] +d_c99_bool_strict.c(338): error: operands of '=' have incompatible types (int != _Bool) [107] +d_c99_bool_strict.c(340): error: operands of '=' have incompatible types (int != _Bool) [107] +d_c99_bool_strict.c(341): error: operands of '=' have incompatible types (_Bool != int) [107] +d_c99_bool_strict.c(352): error: operands of '=' have incompatible types (int != _Bool) [107] +d_c99_bool_strict.c(353): error: operands of '=' have incompatible types (unsigned int != _Bool) [107] +d_c99_bool_strict.c(354): error: operands of '=' have incompatible types (double != _Bool) [107] +d_c99_bool_strict.c(355): error: operands of '=' have incompatible types (pointer != _Bool) [107] +d_c99_bool_strict.c(345): warning: argument 'b' unused in function 'strict_bool_conversion_from_bool_to_scalar' [231] +d_c99_bool_strict.c(367): warning: constant in conditional context [161] +d_c99_bool_strict.c(368): warning: statement not reached [193] +d_c99_bool_strict.c(370): warning: constant in conditional context [161] +d_c99_bool_strict.c(376): error: controlling expression must be bool, not 'int' [333] +d_c99_bool_strict.c(377): warning: statement not reached [193] +d_c99_bool_strict.c(379): error: controlling expression must be bool, not 'int' [333] +d_c99_bool_strict.c(382): error: controlling expression must be bool, not 'int' [333] +d_c99_bool_strict.c(386): error: controlling expression must be bool, not 'int' [333] +d_c99_bool_strict.c(392): error: controlling expression must be bool, not 'double' [333] +d_c99_bool_strict.c(398): error: controlling expression must be bool, not 'pointer' [333] +d_c99_bool_strict.c(419): warning: constant in conditional context [161] +d_c99_bool_strict.c(419): warning: constant argument to '!' [239] +d_c99_bool_strict.c(420): warning: constant in conditional context [161] +d_c99_bool_strict.c(420): warning: constant argument to '!' [239] +d_c99_bool_strict.c(424): error: operand of '!' must be bool, not 'int' [330] +d_c99_bool_strict.c(425): error: operand of '!' must be bool, not 'int' [330] +d_c99_bool_strict.c(426): error: operand of '!' must be bool, not 'int' [330] +d_c99_bool_strict.c(427): error: operand of '!' must be bool, not 'int' [330] +d_c99_bool_strict.c(482): error: operands of '=' have incompatible types (_Bool != int) [107] +d_c99_bool_strict.c(488): error: operands of '=' have incompatible types (_Bool != int) [107] +d_c99_bool_strict.c(504): error: operand of '!' must be bool, not 'int' [330] +d_c99_bool_strict.c(505): error: left operand of '&&' must be bool, not 'int' [331] +d_c99_bool_strict.c(505): error: right operand of '&&' must be bool, not 'int' [332] +d_c99_bool_strict.c(506): error: left operand of '||' must be bool, not 'int' [331] +d_c99_bool_strict.c(506): error: right operand of '||' must be bool, not 'int' [332] +d_c99_bool_strict.c(508): error: right operand of '&&' must be bool, not 'int' [332] +d_c99_bool_strict.c(509): error: left operand of '&&' must be bool, not 'int' [331] +d_c99_bool_strict.c(510): error: right operand of '||' must be bool, not 'int' [332] +d_c99_bool_strict.c(511): error: left operand of '||' must be bool, not 'int' [331] +d_c99_bool_strict.c(520): error: operand of '~' must not be bool [335] +d_c99_bool_strict.c(521): error: operand of '++x' must not be bool [335] +d_c99_bool_strict.c(522): error: operand of '--x' must not be bool [335] +d_c99_bool_strict.c(523): error: operand of 'x++' must not be bool [335] +d_c99_bool_strict.c(524): error: operand of 'x--' must not be bool [335] +d_c99_bool_strict.c(525): error: operand of '+' must not be bool [335] +d_c99_bool_strict.c(526): error: operand of '-' must not be bool [335] +d_c99_bool_strict.c(532): error: left operand of '*' must not be bool [336] +d_c99_bool_strict.c(532): error: right operand of '*' must not be bool [337] +d_c99_bool_strict.c(533): error: left operand of '/' must not be bool [336] +d_c99_bool_strict.c(533): error: right operand of '/' must not be bool [337] +d_c99_bool_strict.c(534): error: left operand of '%' must not be bool [336] +d_c99_bool_strict.c(534): error: right operand of '%' must not be bool [337] +d_c99_bool_strict.c(535): error: left operand of '+' must not be bool [336] +d_c99_bool_strict.c(535): error: right operand of '+' must not be bool [337] +d_c99_bool_strict.c(536): error: left operand of '-' must not be bool [336] +d_c99_bool_strict.c(536): error: right operand of '-' must not be bool [337] +d_c99_bool_strict.c(537): error: left operand of '<<' must not be bool [336] +d_c99_bool_strict.c(537): error: right operand of '<<' must not be bool [337] +d_c99_bool_strict.c(538): error: left operand of '>>' must not be bool [336] +d_c99_bool_strict.c(538): error: right operand of '>>' must not be bool [337] +d_c99_bool_strict.c(540): error: left operand of '<' must not be bool [336] +d_c99_bool_strict.c(540): error: right operand of '<' must not be bool [337] +d_c99_bool_strict.c(541): error: left operand of '<=' must not be bool [336] +d_c99_bool_strict.c(541): error: right operand of '<=' must not be bool [337] +d_c99_bool_strict.c(542): error: left operand of '>' must not be bool [336] +d_c99_bool_strict.c(542): error: right operand of '>' must not be bool [337] +d_c99_bool_strict.c(543): error: left operand of '>=' must not be bool [336] +d_c99_bool_strict.c(543): error: right operand of '>=' must not be bool [337] +d_c99_bool_strict.c(555): error: left operand of '*=' must not be bool [336] +d_c99_bool_strict.c(555): error: right operand of '*=' must not be bool [337] +d_c99_bool_strict.c(556): error: left operand of '/=' must not be bool [336] +d_c99_bool_strict.c(556): error: right operand of '/=' must not be bool [337] +d_c99_bool_strict.c(557): error: left operand of '%=' must not be bool [336] +d_c99_bool_strict.c(557): error: right operand of '%=' must not be bool [337] +d_c99_bool_strict.c(558): error: left operand of '+=' must not be bool [336] +d_c99_bool_strict.c(558): error: right operand of '+=' must not be bool [337] +d_c99_bool_strict.c(559): error: left operand of '-=' must not be bool [336] +d_c99_bool_strict.c(559): error: right operand of '-=' must not be bool [337] +d_c99_bool_strict.c(560): error: left operand of '<<=' must not be bool [336] +d_c99_bool_strict.c(560): error: right operand of '<<=' must not be bool [337] +d_c99_bool_strict.c(561): error: left operand of '>>=' must not be bool [336] +d_c99_bool_strict.c(561): error: right operand of '>>=' must not be bool [337] +d_c99_bool_strict.c(567): error: left operand of '*' must not be bool [336] +d_c99_bool_strict.c(568): error: right operand of '*' must not be bool [337] +d_c99_bool_strict.c(569): error: left operand of '/' must not be bool [336] +d_c99_bool_strict.c(570): error: right operand of '/' must not be bool [337] +d_c99_bool_strict.c(571): error: left operand of '%' must not be bool [336] +d_c99_bool_strict.c(572): error: right operand of '%' must not be bool [337] +d_c99_bool_strict.c(573): error: left operand of '+' must not be bool [336] +d_c99_bool_strict.c(574): error: right operand of '+' must not be bool [337] +d_c99_bool_strict.c(575): error: left operand of '-' must not be bool [336] +d_c99_bool_strict.c(576): error: right operand of '-' must not be bool [337] +d_c99_bool_strict.c(577): error: left operand of '<<' must not be bool [336] +d_c99_bool_strict.c(578): error: right operand of '<<' must not be bool [337] +d_c99_bool_strict.c(579): error: left operand of '>>' must not be bool [336] +d_c99_bool_strict.c(580): error: right operand of '>>' must not be bool [337] +d_c99_bool_strict.c(582): error: operands of ':' have incompatible types (_Bool != unsigned int) [107] +d_c99_bool_strict.c(583): error: operands of ':' have incompatible types (unsigned int != _Bool) [107] +d_c99_bool_strict.c(589): warning: expression has null effect [129] +d_c99_bool_strict.c(590): warning: expression has null effect [129] +d_c99_bool_strict.c(603): error: operands of 'init' have incompatible types (char != _Bool) [107] +d_c99_bool_strict.c(604): error: operands of 'init' have incompatible types (int != _Bool) [107] +d_c99_bool_strict.c(605): error: operands of 'init' have incompatible types (double != _Bool) [107] +d_c99_bool_strict.c(606): error: operands of 'init' have incompatible types (pointer != _Bool) [107] +d_c99_bool_strict.c(623): error: operands of '=' have incompatible types (int != _Bool) [107] +d_c99_bool_strict.c(624): error: operands of '=' have incompatible types (int != _Bool) [107] +d_c99_bool_strict.c(625): error: operands of '=' have incompatible types (int != _Bool) [107] +d_c99_bool_strict.c(626): error: operands of '=' have incompatible types (int != _Bool) [107] +d_c99_bool_strict.c(627): error: operands of '=' have incompatible types (int != _Bool) [107] +d_c99_bool_strict.c(628): error: operands of '=' have incompatible types (int != _Bool) [107] +d_c99_bool_strict.c(629): error: operands of '=' have incompatible types (int != _Bool) [107] +d_c99_bool_strict.c(630): error: operands of '=' have incompatible types (int != _Bool) [107] +d_c99_bool_strict.c(631): error: operands of '=' have incompatible types (int != _Bool) [107] +d_c99_bool_strict.c(669): error: operands of '=' have incompatible types (_Bool != int) [107] +d_c99_bool_strict.c(677): error: operands of '=' have incompatible types (_Bool != int) [107] +d_c99_bool_strict.c(684): error: operands of '=' have incompatible types (_Bool != int) [107] +d_c99_bool_strict.c(659): warning: argument 'flags' unused in function 'strict_bool_bitwise_and_enum' [231] +d_c99_bool_strict.c(723): error: operands of '==' have incompatible types (_Bool != int) [107] +d_c99_bool_strict.c(735): warning: expression has null effect [129] +d_c99_bool_strict.c(747): error: right operand of '+' must not be bool [337] +d_c99_bool_strict.c(764): warning: constant in conditional context [161] +d_c99_bool_strict.c(775): error: operands of 'init' have incompatible types (_Bool != int) [107] +d_c99_bool_strict.c(776): error: operands of 'init' have incompatible types (_Bool != int) [107] diff --git a/usr.bin/xlint/lint1/d_c99_bool_strict_syshdr.c b/usr.bin/xlint/lint1/d_c99_bool_strict_syshdr.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/d_c99_bool_strict_syshdr.c @@ -0,0 +1,182 @@ +/* $NetBSD: d_c99_bool_strict_syshdr.c,v 1.9 2021/04/05 01:35:34 rillig Exp $ */ +# 3 "d_c99_bool_strict_syshdr.c" + +/* + * In strict bool mode, lint treats bool as incompatible with any other scalar + * types. This mode helps in migrating code from pre-C99 to C99. + * + * System headers, on the other hand, cannot be migrated if they need to stay + * compatible with pre-C99 code. Therefore, the checks for system headers are + * loosened. In contexts where a scalar expression is compared to 0, macros + * and functions from system headers may use int expressions as well. + * + * These headers are not allowed to include [references needed]. + * Doing so would inject lint's own , which defines the macros + * false and true to other identifiers instead of the plain 0 and 1, thereby + * allowing to see whether the code really uses true and false as identifiers. + * + * Since the system headers cannot include , they need to use the + * traditional bool constants 0 and 1. + */ + +/* lint1-extra-flags: -T */ + +extern const unsigned short *ctype_table; + +extern void println(const char *); + +/* + * On NetBSD 8, defines FD_ISSET by enclosing the statements + * in the well-known 'do { ... } while (CONSTCOND 0)' loop. The 0 in the + * controlling expression has type INT but should be allowed nevertheless + * since that header does not have a way to distinguish between bool and int. + * It just follows the C99 standard, unlike the lint-provided stdbool.h, which + * redefines 'false' to '__lint_false'. Plus, must not include + * itself. + */ +void +strict_bool_system_header_statement_macro(void) +{ + + do { + println("nothing"); + } while (/*CONSTCOND*/0); /* expect: 333 */ + +# 46 "d_c99_bool_strict_syshdr.c" 3 4 + do { + println("nothing"); + } while (/*CONSTCOND*/0); /* ok */ + +# 51 "d_c99_bool_strict_syshdr.c" + do { + println("nothing"); + } while (/*CONSTCOND*/0); /* expect: 333 */ +} + + +/* + * The macros from can be implemented in different ways. The C + * standard defines them as returning 'int'. In strict bool mode, the actual + * return type can be INT or BOOL, depending on whether the macros do the + * comparison against 0 themselves. + * + * Since that comparison is more code to write and in exceptional situations + * more code to execute, they will probably leave out the extra comparison, + * but both ways are possible. + * + * In strict bool mode, there must be a way to call these function-like macros + * portably, without triggering type errors, no matter whether they return + * BOOL or INT. + * + * The expressions from this example cross the boundary between system header + * and application code. They need to carry the information that they are + * half-BOOL, half-INT across to the enclosing expressions. + */ +void +strict_bool_system_header_ctype(int c) +{ + /* + * The macro returns INT, which may be outside the range of a + * uint8_t variable, therefore it must not be assigned directly. + * All other combinations of type are safe from truncation. + */ + _Bool system_int_assigned_to_bool = +# 85 "d_c99_bool_strict_syshdr.c" 3 4 + (int)((ctype_table + 1)[c] & 0x0040) /* INT */ +# 87 "d_c99_bool_strict_syshdr.c" + ; /* expect: 107 */ + + int system_bool_assigned_to_int = +# 91 "d_c99_bool_strict_syshdr.c" 3 4 + (int)((ctype_table + 1)[c] & 0x0040) != 0 /* BOOL */ +# 93 "d_c99_bool_strict_syshdr.c" + ; + + if ( +# 97 "d_c99_bool_strict_syshdr.c" 3 4 + (int)((ctype_table + 1)[c] & 0x0040) /* INT */ +# 99 "d_c99_bool_strict_syshdr.c" + ) + println("system macro returning INT"); + + if ( +# 104 "d_c99_bool_strict_syshdr.c" 3 4 + ((ctype_table + 1)[c] & 0x0040) != 0 /* BOOL */ +# 106 "d_c99_bool_strict_syshdr.c" + ) + println("system macro returning BOOL"); +} + +static inline _Bool +ch_isspace_sys_int(char c) +{ + return +# 115 "d_c99_bool_strict_syshdr.c" 3 4 + ((ctype_table + 1)[c] & 0x0040) +# 117 "d_c99_bool_strict_syshdr.c" + != 0; +} + +/* + * isspace is defined to return an int. Comparing this int with 0 is the + * safe way to convert it to _Bool. This must be allowed even if isspace + * does the comparison itself. + */ +static inline _Bool +ch_isspace_sys_bool(char c) +{ + return +# 130 "d_c99_bool_strict_syshdr.c" 3 4 + ((ctype_table + 1)[(unsigned char)c] & 0x0040) != 0 +# 132 "d_c99_bool_strict_syshdr.c" + != 0; +} + +/* + * There are several functions from system headers that have return type + * int. For this return type there are many API conventions: + * + * * isspace: 0 means no, non-zero means yes + * * open: 0 means success, -1 means failure + * * main: 0 means success, non-zero means failure + * * strcmp: 0 means equal, < 0 means less than, > 0 means greater than + * + * Without a detailed list of individual functions, it's not possible to + * guess what the return value means. Therefore in strict bool mode, the + * return value of these functions cannot be implicitly converted to bool, + * not even in a context where the result is compared to 0. Allowing that + * would allow expressions like !strcmp(s1, s2), which is not correct since + * strcmp returns an "ordered comparison result", not a bool. + */ + +# 1 "math.h" 3 4 +extern int finite(double); +# 1 "string.h" 3 4 +extern int strcmp(const char *, const char *); +# 157 "d_c99_bool_strict_syshdr.c" + +/*ARGSUSED*/ +_Bool +call_finite_bad(double d) +{ + return finite(d); /* expect: 211 */ +} + +_Bool +call_finite_good(double d) +{ + return finite(d) != 0; +} + +/*ARGSUSED*/ +_Bool +str_equal_bad(const char *s1, const char *s2) +{ + return !strcmp(s1, s2); /* expect: 330 *//* expect: 214 */ +} + +_Bool +str_equal_good(const char *s1, const char *s2) +{ + return strcmp(s1, s2) == 0; +} diff --git a/usr.bin/xlint/lint1/d_c99_bool_strict_syshdr.exp b/usr.bin/xlint/lint1/d_c99_bool_strict_syshdr.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/d_c99_bool_strict_syshdr.exp @@ -0,0 +1,6 @@ +d_c99_bool_strict_syshdr.c(43): error: controlling expression must be bool, not 'int' [333] +d_c99_bool_strict_syshdr.c(53): error: controlling expression must be bool, not 'int' [333] +d_c99_bool_strict_syshdr.c(87): error: operands of 'init' have incompatible types (_Bool != int) [107] +d_c99_bool_strict_syshdr.c(162): error: return value type mismatch (_Bool) and (int) [211] +d_c99_bool_strict_syshdr.c(175): error: operand of '!' must be bool, not 'int' [330] +d_c99_bool_strict_syshdr.c(175): warning: function 'str_equal_bad' expects to return value [214] diff --git a/usr.bin/xlint/lint1/d_c99_complex_num.c b/usr.bin/xlint/lint1/d_c99_complex_num.c --- a/usr.bin/xlint/lint1/d_c99_complex_num.c +++ b/usr.bin/xlint/lint1/d_c99_complex_num.c @@ -1,3 +1,6 @@ +/* $NetBSD: d_c99_complex_num.c,v 1.2 2021/01/31 14:39:31 rillig Exp $ */ +# 3 "d_c99_complex_num.c" + double cabs(double _Complex); double cabs(double _Complex foo) diff --git a/usr.bin/xlint/lint1/d_c99_complex_split.c b/usr.bin/xlint/lint1/d_c99_complex_split.c --- a/usr.bin/xlint/lint1/d_c99_complex_split.c +++ b/usr.bin/xlint/lint1/d_c99_complex_split.c @@ -1,8 +1,93 @@ -int b(double a) { +/* $NetBSD: d_c99_complex_split.c,v 1.8 2021/07/11 19:39:00 rillig Exp $ */ +# 3 "d_c99_complex_split.c" + +/* + * Checks that the real and imaginary parts of a complex number can be + * accessed (since C99). + */ + +int +b(double a) +{ return a == 0; } -void a(void) { - double _Complex z = 0; - if (b(__real__ z) && b(__imag__ z)) - return; + +void +a(void) +{ + double _Complex z = 0; + if (b(__real__ z) && b(__imag__ z)) + return; +} + +void sink(double _Complex); + +/* + * Before tree.c 1.275 from 2021-04-09, lint wrongly warned that when + * '__real__ c' was assigned, 'c may be used before set'. + * + * As of 2021-04-09, support for _Complex is still very incomplete, see + * build_real_imag for details. For example, lint does not know that after + * the assignment to '__real__ c', the variable is partially initialized. + */ +void +set_complex_complete(double re, double im) +{ + double _Complex c; + + __real__ c = re; + __imag__ c = im; + sink(c); +} + +/* + * Before tree.c 1.275 from 2021-04-09, lint wrongly warned that when + * '__real__ c' was assigned, 'c may be used before set'. + * + * As of 2021-04-09, support for _Complex is still very incomplete, see + * build_real_imag for details. + */ +void +set_complex_only_real(double re) +{ + double _Complex c; + + __real__ c = re; + /* __imag__ c is left uninitialized */ + sink(c); /* XXX: may be used before set */ +} + +/* + * Before tree.c 1.275 from 2021-04-09, lint wrongly warned that when + * '__imag__ c' was assigned, 'c may be used before set'. + * + * As of 2021-04-09, support for _Complex is still very incomplete, see + * build_real_imag for details. + */ +void +set_complex_only_imag(double im) +{ + double _Complex c; + + /* __real__ c is left uninitialized */ + __imag__ c = im; + sink(c); /* XXX: may be used before set */ +} + +/* Just to keep the .exp file alive. */ +void +trigger_warning(double _Complex c) +{ + c += 1.0; + return c | c; /* expect: incompatible types */ +} + +void +precedence_cast_expression(void) +{ + double _Complex z = 0; + if (b(__real__(double _Complex)z) && b(__imag__(double _Complex)z)) + return; + if (b(__real__(z)) && b(__imag__(z))) + return; } diff --git a/usr.bin/xlint/lint1/d_c99_complex_split.exp b/usr.bin/xlint/lint1/d_c99_complex_split.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/d_c99_complex_split.exp @@ -0,0 +1 @@ +d_c99_complex_split.c(82): error: operands of '|' have incompatible types (double _Complex != double _Complex) [107] diff --git a/usr.bin/xlint/lint1/d_c99_compound_literal_comma.c b/usr.bin/xlint/lint1/d_c99_compound_literal_comma.c --- a/usr.bin/xlint/lint1/d_c99_compound_literal_comma.c +++ b/usr.bin/xlint/lint1/d_c99_compound_literal_comma.c @@ -1,14 +1,33 @@ -struct bintime { - unsigned long long sec; - unsigned long long frac; +/* $NetBSD: d_c99_compound_literal_comma.c,v 1.3 2021/03/20 11:24:49 rillig Exp $ */ +# 3 "d_c99_compound_literal_comma.c" + +/*- + * Ensure that compound literals can be parsed. + * + * C99 6.5.2 "Postfix operators" for the syntax. + * C99 6.5.2.5 "Compound literals" for the semantics. + */ + +struct point { + int x; + int y; }; -struct bintime -us2bintime(unsigned long long us) +struct point +point_abs(struct point point) { + /* No designators, no trailing comma. */ + if (point.x >= 0 && point.y >= 0) + return (struct point){ point.x, point.y }; + + /* Designators, no trailing comma. */ + if (point.x >= 0) + return (struct point){ .x = point.x, .y = -point.y }; + + /* No designators, trailing comma. */ + if (point.y >= 0) + return (struct point){ point.x, point.y, }; - return (struct bintime) { - .sec = us / 1000000U, - .frac = (((us % 1000000U) >> 32)/1000000U) >> 32, - }; + /* Designators, trailing comma. */ + return (struct point){ .x = point.x, .y = -point.y, }; } diff --git a/usr.bin/xlint/lint1/d_c99_decls_after_stmt.c b/usr.bin/xlint/lint1/d_c99_decls_after_stmt.c --- a/usr.bin/xlint/lint1/d_c99_decls_after_stmt.c +++ b/usr.bin/xlint/lint1/d_c99_decls_after_stmt.c @@ -1,5 +1,12 @@ -void sample(void) +/* $NetBSD: d_c99_decls_after_stmt.c,v 1.3 2021/01/31 14:57:28 rillig Exp $ */ +# 3 "d_c99_decls_after_stmt.c" + +void +sample(void) { - int i = 0; i += 1; - int j = 0; j += 1; + int i = 0; + i += 1; + + int j = 0; + j += 1; } diff --git a/usr.bin/xlint/lint1/d_c99_decls_after_stmt2.c b/usr.bin/xlint/lint1/d_c99_decls_after_stmt2.c --- a/usr.bin/xlint/lint1/d_c99_decls_after_stmt2.c +++ b/usr.bin/xlint/lint1/d_c99_decls_after_stmt2.c @@ -1,6 +1,16 @@ +/* $NetBSD: d_c99_decls_after_stmt2.c,v 1.3 2021/01/31 14:57:28 rillig Exp $ */ +# 3 "d_c99_decls_after_stmt2.c" + typedef int int_t; -int main(void) { -int i = 0; i += 1; -int_t j = 0; j += 1; -return 0; + +int +main(void) +{ + int i = 0; + i += 1; + + int_t j = 0; + j += 1; + + return 0; } diff --git a/usr.bin/xlint/lint1/d_c99_decls_after_stmt3.c b/usr.bin/xlint/lint1/d_c99_decls_after_stmt3.c --- a/usr.bin/xlint/lint1/d_c99_decls_after_stmt3.c +++ b/usr.bin/xlint/lint1/d_c99_decls_after_stmt3.c @@ -1,5 +1,11 @@ -void sample(int i) +/* $NetBSD: d_c99_decls_after_stmt3.c,v 1.3 2021/01/31 14:57:28 rillig Exp $ */ +# 3 "d_c99_decls_after_stmt3.c" + +void +sample(int i) { - i += 1; - int j = 0; j += 1; + i += 1; + + int j = 0; + j += 1; } diff --git a/usr.bin/xlint/lint1/d_c99_flex_array_packed.c b/usr.bin/xlint/lint1/d_c99_flex_array_packed.c --- a/usr.bin/xlint/lint1/d_c99_flex_array_packed.c +++ b/usr.bin/xlint/lint1/d_c99_flex_array_packed.c @@ -1,6 +1,8 @@ +/* $NetBSD: d_c99_flex_array_packed.c,v 1.2 2021/01/31 14:39:31 rillig Exp $ */ +# 3 "d_c99_flex_array_packed.c" + /* Allow packed c99 flexible arrays */ struct { int x; char y[0]; } __packed foo; - diff --git a/usr.bin/xlint/lint1/d_c99_for_loops.c b/usr.bin/xlint/lint1/d_c99_for_loops.c --- a/usr.bin/xlint/lint1/d_c99_for_loops.c +++ b/usr.bin/xlint/lint1/d_c99_for_loops.c @@ -1,3 +1,6 @@ +/* $NetBSD: d_c99_for_loops.c,v 1.3 2021/01/31 14:39:31 rillig Exp $ */ +# 3 "d_c99_for_loops.c" + /* c99 for loops */ extern void foo(int); diff --git a/usr.bin/xlint/lint1/d_c99_func.c b/usr.bin/xlint/lint1/d_c99_func.c --- a/usr.bin/xlint/lint1/d_c99_func.c +++ b/usr.bin/xlint/lint1/d_c99_func.c @@ -1,7 +1,11 @@ +/* $NetBSD: d_c99_func.c,v 1.3 2021/01/31 14:57:28 rillig Exp $ */ +# 3 "d_c99_func.c" + /* C99 __func__ */ void -foo(const char *p) { +foo(const char *p) +{ p = __func__; foo(p); } diff --git a/usr.bin/xlint/lint1/d_c99_init.c b/usr.bin/xlint/lint1/d_c99_init.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/d_c99_init.c @@ -0,0 +1,393 @@ +/* $NetBSD: d_c99_init.c,v 1.33 2021/04/09 23:03:26 rillig Exp $ */ +# 3 "d_c99_init.c" + +/* + * Test C99 initializers. + * + * See C99 6.7.8 "Initialization". +*/ + + +void use(const void *); + +typedef struct any { + const void *value; +} any; + + +// C99 6.7.8p11 says "optionally enclosed in braces". There is no limitation +// on the number of brace pairs. +int scalar_without_braces = 3; +int scalar_with_optional_braces = { 3 }; +int scalar_with_too_many_braces = {{ 3 }}; +int scalar_with_too_many_initializers = { 3, 5 }; /* expect: 174 */ + + +// See init_expr, 'handing over to ASSIGN'. +void +struct_initialization_via_assignment(any arg) +{ + any local = arg; + use(&local); +} + + +// See init_expr, initialization_init_array_using_string. +char static_duration[] = "static duration"; +signed char static_duration_signed[] = "static duration"; +unsigned char static_duration_unsigned[] = "static duration"; +int static_duration_wchar[] = L"static duration"; + +// See init_expr. +void +initialization_by_braced_string(void) +{ + any local = { "hello" }; + use(&local); +} + +void +initialization_by_redundantly_braced_string(void) +{ + any local = {{{{ "hello" }}}}; + use(&local); +} + +/* + * Only scalar expressions and string literals may be enclosed by additional + * braces. Since 'arg' is a struct, this is a compile-time error. + */ +void +initialization_with_too_many_braces(any arg) +{ + any local = { arg }; /* expect: 185 */ + use(&arg); +} + +// Some of the following examples are mentioned in the introduction comment +// in init.c. + +int number = 12345; + +int number_with_braces_and_comma = { + 12345, +}; + +int array_with_fixed_size[3] = { + 111, + 222, + 333, + 444, /* expect: too many array initializers */ +}; + +// See initialization_set_set_of_unknown_array. +int array_of_unknown_size[] = { + 111, + 222, + 333, +}; + +int array_flat[2][2] = { + 11, + 12, + 21, + 22 +}; + +int array_nested[2][2] = { + { + 11, + 12 + }, + { + 21, + 22 + } +}; + +int array_with_designators[] = { + ['1'] = 111, + ['5'] = 555, + ['9'] = 999 +}; + +int array_with_some_designators[] = { + ['1'] = 111, + 222, + ['9'] = 999 +}; + +struct point { + int x; + int y; +}; + +struct point point = { + 3, + 4 +}; + +struct point point_with_designators = { + .y = 4, + .x = 3, +}; + +struct point point_with_mixed_designators = { + .x = 3, + 4, + 5, /* expect: too many struct/union initializers */ + .x = 3, +}; + +int array_with_designator[] = { + 111, + .member = 222, /* expect: 249 */ + 333, +}; + +/* + * C99 6.7.8p11 says that the initializer of a scalar can be "optionally + * enclosed in braces". It does not explicitly set an upper limit on the + * number of braces. It also doesn't restrict the term "initializer" to only + * mean the "outermost initializer". Both GCC 10 and Clang 8 already warn + * about this, so there is no extra work for lint to do. + */ +struct point scalar_with_several_braces = { + {{{3}}}, + {{{{4}}}}, +}; + +struct rectangle { + struct point top_left; + struct point bottom_right; +}; + +/* C99 6.7.8p18 */ +struct rectangle screen = { + .bottom_right = { + 1920, + 1080, + } +}; + +/* + * C99 6.7.8p22 says: At the _end_ of its initializer list, the array no + * longer has incomplete type. + */ +struct point points[] = { + { + /* + * At this point, the size of the object 'points' is not known + * yet since its type is still incomplete. Lint could warn + * about this, but GCC and Clang already do. + * + * This test case demonstrates that in + * extend_if_array_of_unknown_size, setcomplete is called too + * early. + */ + sizeof(points), + 4 + } +}; + + +struct triangle { + struct point points[3]; +}; + +struct pentagon { + struct point points[5]; +}; + +struct geometry { + struct pentagon pentagons[6]; + struct triangle triangles[10]; + struct point points[3][5][2]; +}; + +/* + * Initialization of a complex struct containing nested arrays and nested + * structs. + */ +struct geometry geometry = { + .pentagons[0].points[4].x = 1, + .points[0][0][0] = { 0, 0 }, + .points[2][4][1] = {301, 302 }, + /* expect+1: array subscript cannot be > 2: 3 */ + .points[3][0][0] = {3001, 3002 }, + /* expect+1: array subscript cannot be > 4: 5 */ + .points[0][5][0] = {501, 502 }, + /* expect+1: array subscript cannot be > 1: 2 */ + .points[0][0][2] = {21, 22 }, +}; + +struct ends_with_unnamed_bit_field { + int member; + int : 0; +} ends_with_unnamed_bit_field = { + 12345, + /* expect+1: too many struct/union initializers */ + 23456, +}; + +char prefixed_message[] = { + 'E', ':', ' ', + /* expect+1: illegal combination of integer (char) and pointer */ + "message\n", +}; + +char message_with_suffix[] = { + "message", + /* The excess character is not detected by lint but by compilers. */ + '\n', +}; + +struct ten { + int i0; + int i1; + int i2; + int i3; + int i4; + int i5; + int i6; + int i7; + int i8; + int i9; +}; + +struct ten ten = { + .i3 = 3, + 4, + 5, + 6, +}; + +int c99_6_7_8_p26_example3[4][3] = { + { 1, 3, 5 }, + { 2, 4, 6 }, + { 3, 5, 7 }, +}; + +int c99_6_7_8_p27_example4[4][3] = { + { 1 }, { 2 }, { 3 }, { 4 } +}; + +struct { + int a[3], b; +} c99_6_7_8_p28_example5[] = { + { 1 }, /* just parsed, not checked in detail */ + 2, /* just parsed, not checked in detail */ +}; + +short c99_6_7_8_p29_example6a[4][3][2] = { + { 1 }, + { 2, 3 }, + { 4, 5, 6 }, +}; + +short c99_6_7_8_p29_example6b[4][3][2] = { + 1, 0, 0, 0, 0, 0, + 2, 3, 0, 0, 0, 0, + 4, 5, 6, 0, 0, 0, +}; + +short c99_6_7_8_p29_example6c[4][3][2] = { + { + { 1 }, + }, + { + { 2, 3 }, + }, + { + { 4, 5 }, + { 6 }, + } +}; + +/* + * During initialization of an object of type array of unknown size, the type + * information on the symbol is updated in-place. Ensure that this happens on + * a copy of the type. + */ +void +ensure_array_type_is_not_modified_during_initialization(void) +{ + typedef int array_of_unknown_size[]; + + array_of_unknown_size a1 = { 1, 2, 3}; + + switch (4) { + case sizeof(array_of_unknown_size): + case 0: /* expect: duplicate case in switch: 0 */ + case 3: + case 4: + case 12: + break; + } +} + +struct point unknown_member_name_beginning = { + .r = 5, /* expect: does not have member 'r' */ + .x = 4, + .y = 3, +}; + +struct point unknown_member_name_middle = { + .x = 4, + .r = 5, /* expect: does not have member 'r' */ + .y = 3, +}; + +struct point unknown_member_name_end = { + .x = 4, + .y = 3, + .r = 5, /* expect: does not have member 'r' */ +}; + +union value { + int int_value; + void *pointer_value; +}; + +union value unknown_union_member_name_first = { + .unknown_value = 4, /* expect: does not have member */ + .int_value = 3, +}; + +union value unknown_union_member_name_second = { + .int_value = 3, + .unknown_value = 4, /* expect: does not have member */ +}; + +struct point designators_with_subscript = { + [0] = 3, /* expect: only for arrays */ + .member[0][0].member = 4, /* expect: does not have member 'member' */ + .x.y.z = 5, /* intentionally not caught, see designator_look_up */ +}; + +struct { + int : 16; +} struct_with_only_unnamed_members = { /* expect: has no named members */ + 123, /* expect: too many struct/union initializers */ +}; + +union { + int : 16; +} union_with_only_unnamed_members = { /* expect: has no named members */ + 123, /* expect: too many struct/union initializers */ +}; + +int designator_for_scalar = { + .value = 3, /* expect: scalar type cannot use designator */ +}; + +struct point designator_for_scalar_in_struct = { + { .x = 3 }, /* expect: scalar type cannot use designator */ + { [1] = 4 }, /* expect: scalar type cannot use designator */ +}; + + +/* Seen in pcidevs_data.h, variable 'pci_words'. */ +const char string_initialized_with_braced_literal[] = { + "initializer", +}; diff --git a/usr.bin/xlint/lint1/d_c99_init.exp b/usr.bin/xlint/lint1/d_c99_init.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/d_c99_init.exp @@ -0,0 +1,25 @@ +d_c99_init.c(23): error: too many initializers [174] +d_c99_init.c(63): error: cannot initialize 'pointer to const void' from 'struct any' [185] +d_c99_init.c(80): error: too many array initializers, expected 3 [173] +d_c99_init.c(138): error: too many struct/union initializers [172] +d_c99_init.c(144): error: syntax error 'designator '.member' is only for struct/union' [249] +d_c99_init.c(217): error: array subscript cannot be > 2: 3 [168] +d_c99_init.c(219): error: array subscript cannot be > 4: 5 [168] +d_c99_init.c(221): error: array subscript cannot be > 1: 2 [168] +d_c99_init.c(230): error: too many struct/union initializers [172] +d_c99_init.c(236): warning: illegal combination of integer (char) and pointer (pointer to char) [183] +d_c99_init.c(321): error: duplicate case in switch: 0 [199] +d_c99_init.c(330): error: type 'struct point' does not have member 'r' [101] +d_c99_init.c(337): error: type 'struct point' does not have member 'r' [101] +d_c99_init.c(344): error: type 'struct point' does not have member 'r' [101] +d_c99_init.c(353): error: type 'union value' does not have member 'unknown_value' [101] +d_c99_init.c(359): error: type 'union value' does not have member 'unknown_value' [101] +d_c99_init.c(363): error: syntax error 'designator '[...]' is only for arrays' [249] +d_c99_init.c(364): error: type 'struct point' does not have member 'member' [101] +d_c99_init.c(370): warning: structure has no named members [65] +d_c99_init.c(371): error: too many struct/union initializers [172] +d_c99_init.c(376): warning: union has no named members [65] +d_c99_init.c(377): error: too many struct/union initializers [172] +d_c99_init.c(381): error: syntax error 'scalar type cannot use designator' [249] +d_c99_init.c(385): error: syntax error 'scalar type cannot use designator' [249] +d_c99_init.c(386): error: syntax error 'scalar type cannot use designator' [249] diff --git a/usr.bin/xlint/lint1/d_c99_nested_struct.c b/usr.bin/xlint/lint1/d_c99_nested_struct.c --- a/usr.bin/xlint/lint1/d_c99_nested_struct.c +++ b/usr.bin/xlint/lint1/d_c99_nested_struct.c @@ -1,25 +1,36 @@ +/* $NetBSD: d_c99_nested_struct.c,v 1.4 2021/02/20 22:31:20 rillig Exp $ */ +# 3 "d_c99_nested_struct.c" + /* C99 nested struct init with named and non-named initializers */ typedef struct pthread_mutex_t { - unsigned int ptm_magic; - char ptm_errorcheck; + unsigned int ptm_magic; + char ptm_errorcheck; - char ptm_pad1[3]; + char ptm_pad1[3]; - char ptm_interlock; + char ptm_interlock; - char ptm_pad2[3]; + char ptm_pad2[3]; - volatile void * ptm_owner; - void * volatile ptm_waiters; - unsigned int ptm_recursed; - void *ptm_spare2; + volatile void *ptm_owner; + void *volatile ptm_waiters; + unsigned int ptm_recursed; + void *ptm_spare2; } pthread_mutex_t; struct arc4random_global { - - pthread_mutex_t lock; + pthread_mutex_t lock; } arc4random_global = { - - .lock = { 0x33330003, 0, { 0, 0, 0 }, 0, { 0, 0, 0 }, ((void *)0), ((void *)0), 0, ((void *)0) }, + .lock = { + 0x33330003, + 0, + { 0, 0, 0 }, + 0, + { 0, 0, 0 }, + ((void *)0), + ((void *)0), + 0, + ((void *)0) + }, }; diff --git a/usr.bin/xlint/lint1/d_c99_recursive_init.c b/usr.bin/xlint/lint1/d_c99_recursive_init.c --- a/usr.bin/xlint/lint1/d_c99_recursive_init.c +++ b/usr.bin/xlint/lint1/d_c99_recursive_init.c @@ -1,3 +1,6 @@ +/* $NetBSD: d_c99_recursive_init.c,v 1.5 2021/02/20 22:31:20 rillig Exp $ */ +# 3 "d_c99_recursive_init.c" + /* C99 recursive struct/union initialization */ struct top { int i; @@ -5,9 +8,21 @@ union onion { short us; char uc; - } u; + } u; char *s; -} c[] = { - { .s = "foo", .c = 'b', .u = { .uc = 'c' } }, - { .i = 1, .c = 'a', .u = { .us = 2 } }, +} c[] = { + { + .s = "foo", + .c = 'b', + .u = { + .uc = 'c' + } + }, + { + .i = 1, + .c = 'a', + .u = { + .us = 2 + } + }, }; diff --git a/usr.bin/xlint/lint1/d_c99_struct_init.c b/usr.bin/xlint/lint1/d_c99_struct_init.c --- a/usr.bin/xlint/lint1/d_c99_struct_init.c +++ b/usr.bin/xlint/lint1/d_c99_struct_init.c @@ -1,10 +1,23 @@ +/* $NetBSD: d_c99_struct_init.c,v 1.4 2021/02/20 22:31:20 rillig Exp $ */ +# 3 "d_c99_struct_init.c" + /* C99 struct initialization */ struct { int i; char *s; -} c[] = { - { .i = 2, }, - { .s = "foo" }, - { .i = 1, .s = "bar" }, - { .s = "foo", .i = -1 }, +} c[] = { + { + .i = 2, + }, + { + .s = "foo" + }, + { + .i = 1, + .s = "bar" + }, + { + .s = "foo", + .i = -1 + }, }; diff --git a/usr.bin/xlint/lint1/d_c99_union_cast.c b/usr.bin/xlint/lint1/d_c99_union_cast.c --- a/usr.bin/xlint/lint1/d_c99_union_cast.c +++ b/usr.bin/xlint/lint1/d_c99_union_cast.c @@ -1,4 +1,9 @@ -/* union cast */ +/* $NetBSD: d_c99_union_cast.c,v 1.7 2021/08/03 20:57:06 rillig Exp $ */ +# 3 "d_c99_union_cast.c" + +/* C99 does not define union cast, it is a GCC extension. */ + +/* lint1-flags: -Sw */ struct bar { int a; @@ -11,8 +16,11 @@ }; void -foo(void) { - struct bar *a; - - ((union foo)a).a; +foo(struct bar *a) +{ + /* expect+1: error: union cast is a GCC extension [328] */ + a = ((union foo)a).a; + /* expect+1: error: union cast is a GCC extension [328] */ + a = ((union foo)"string"); + a->a++; } diff --git a/usr.bin/xlint/lint1/d_c99_union_cast.exp b/usr.bin/xlint/lint1/d_c99_union_cast.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/d_c99_union_cast.exp @@ -0,0 +1,2 @@ +d_c99_union_cast.c(22): error: union cast is a GCC extension [328] +d_c99_union_cast.c(24): error: union cast is a GCC extension [328] diff --git a/usr.bin/xlint/lint1/d_c99_union_init1.c b/usr.bin/xlint/lint1/d_c99_union_init1.c --- a/usr.bin/xlint/lint1/d_c99_union_init1.c +++ b/usr.bin/xlint/lint1/d_c99_union_init1.c @@ -1,8 +1,13 @@ -/* C99 union initialization */ +/* $NetBSD: d_c99_union_init1.c,v 1.4 2021/02/20 22:31:20 rillig Exp $ */ +# 3 "d_c99_union_init1.c" + +/* GCC-style and C99-style union initialization */ union { int i; char *s; -} c[] = { - { i: 1 }, - { s: "foo" } +} c[] = { + { i: 1 }, /* GCC-style */ + { s: "foo" }, /* GCC-style */ + { .i = 1 }, /* C99-style */ + { .s = "foo" } /* C99-style */ }; diff --git a/usr.bin/xlint/lint1/d_c99_union_init2.c b/usr.bin/xlint/lint1/d_c99_union_init2.c --- a/usr.bin/xlint/lint1/d_c99_union_init2.c +++ b/usr.bin/xlint/lint1/d_c99_union_init2.c @@ -1,8 +1,11 @@ +/* $NetBSD: d_c99_union_init2.c,v 1.3 2021/01/31 14:39:31 rillig Exp $ */ +# 3 "d_c99_union_init2.c" + /* C99 union initialization */ union { int i[10]; short s; -} c[] = { +} c[] = { { s: 2 }, { i: { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 } }, }; diff --git a/usr.bin/xlint/lint1/d_c99_union_init3.c b/usr.bin/xlint/lint1/d_c99_union_init3.c --- a/usr.bin/xlint/lint1/d_c99_union_init3.c +++ b/usr.bin/xlint/lint1/d_c99_union_init3.c @@ -1,8 +1,13 @@ -/* C99 union initialization */ +/* $NetBSD: d_c99_union_init3.c,v 1.5 2021/02/20 22:31:20 rillig Exp $ */ +# 3 "d_c99_union_init3.c" + +/* C99 struct initialization */ struct { int i[10]; char *s; -} c[] = { - { { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, - "foo" }, +} c[] = { + { + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, + "foo" + }, }; diff --git a/usr.bin/xlint/lint1/d_c99_union_init4.c b/usr.bin/xlint/lint1/d_c99_union_init4.c --- a/usr.bin/xlint/lint1/d_c99_union_init4.c +++ b/usr.bin/xlint/lint1/d_c99_union_init4.c @@ -1,15 +1,18 @@ +/* $NetBSD: d_c99_union_init4.c,v 1.3 2021/01/31 14:39:31 rillig Exp $ */ +# 3 "d_c99_union_init4.c" + /* test .data.l[x] */ typedef struct { - int type; - union { - char b[20]; - short s[10]; - long l[5]; + int type; + union { + char b[20]; + short s[10]; + long l[5]; } data; } foo; foo bar = { - .type = 3, - .data.l[0] = 4 + .type = 3, + .data.l[0] = 4 }; diff --git a/usr.bin/xlint/lint1/d_c99_union_init5.c b/usr.bin/xlint/lint1/d_c99_union_init5.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/d_c99_union_init5.c @@ -0,0 +1,18 @@ +/* $NetBSD: d_c99_union_init5.c,v 1.1 2021/02/28 20:17:14 rillig Exp $ */ +# 3 "d_c99_union_init5.c" + +/* + * PR bin/20264: lint(1) has problems with named member initialization + * + * Has been fixed somewhere between 2005.12.24.20.47.56 and + * 2006.12.19.19.06.44. +*/ + +union mist { + char *p; + int a[1]; +}; + +union mist xx = { + .a = { 7 } +}; diff --git a/usr.bin/xlint/lint1/d_c9x_array_init.c b/usr.bin/xlint/lint1/d_c9x_array_init.c --- a/usr.bin/xlint/lint1/d_c9x_array_init.c +++ b/usr.bin/xlint/lint1/d_c9x_array_init.c @@ -1,4 +1,7 @@ -/* C9X array initializers */ +/* $NetBSD: d_c9x_array_init.c,v 1.3 2021/02/20 22:31:20 rillig Exp $ */ +# 3 "d_c9x_array_init.c" + +/* GCC-specific array range initializers */ int foo[256] = { [2] = 1, [3] = 2, diff --git a/usr.bin/xlint/lint1/d_c9x_recursive_init.c b/usr.bin/xlint/lint1/d_c9x_recursive_init.c --- a/usr.bin/xlint/lint1/d_c9x_recursive_init.c +++ b/usr.bin/xlint/lint1/d_c9x_recursive_init.c @@ -1,3 +1,6 @@ +/* $NetBSD: d_c9x_recursive_init.c,v 1.3 2021/02/20 22:31:20 rillig Exp $ */ +# 3 "d_c9x_recursive_init.c" + /* C9X struct/union member init, with nested union and trailing member */ union node { void *next; @@ -11,6 +14,8 @@ struct foo f = { .b = 1, - .n = { .next = 0, }, + .n = { + .next = 0, + }, .c = 1 }; diff --git a/usr.bin/xlint/lint1/d_cast_fun_array_param.c b/usr.bin/xlint/lint1/d_cast_fun_array_param.c --- a/usr.bin/xlint/lint1/d_cast_fun_array_param.c +++ b/usr.bin/xlint/lint1/d_cast_fun_array_param.c @@ -1,9 +1,14 @@ +/* $NetBSD: d_cast_fun_array_param.c,v 1.3 2021/01/31 14:57:28 rillig Exp $ */ +# 3 "d_cast_fun_array_param.c" -static void f(void *b[4]) { +static void +f(void *b[4]) +{ (void)&b; } void * -foo(void *fn) { - return fn == 0 ? f : (void (*)(void *[4])) fn; +foo(void *fn) +{ + return fn == 0 ? f : (void (*)(void *[4]))fn; } diff --git a/usr.bin/xlint/lint1/d_cast_init.c b/usr.bin/xlint/lint1/d_cast_init.c --- a/usr.bin/xlint/lint1/d_cast_init.c +++ b/usr.bin/xlint/lint1/d_cast_init.c @@ -1,27 +1,19 @@ +/* $NetBSD: d_cast_init.c,v 1.5 2021/07/03 19:34:47 rillig Exp $ */ +# 3 "d_cast_init.c" + /* cast initialization */ + typedef unsigned char u_char; -typedef unsigned int size_t; -struct sockaddr_x25 { - u_char x25_len; - u_char x25_family; - short x25_net; - char x25_addr[16]; - struct x25opts { - char op_flags; - char op_psize; - char op_wsize; - char op_speed; - } x25_opts; - short x25_udlen; - char x25_udata[16]; + +struct sockaddr_x25 { + u_char x25_len; + u_char x25_family; + signed char x25_udata[4]; }; struct sockaddr_x25 x25_dgmask = { - (unsigned char)(unsigned char)(unsigned int)(unsigned long)(&((( struct sockaddr_x25 *)0)->x25_udata[1])) , - 0, - 0, - {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {0, 0, 0, 0}, - -1, - {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + (unsigned char)(unsigned char)(unsigned int)(unsigned long) + (&(((struct sockaddr_x25 *)0)->x25_udata[1])), + 0, + { -1, -1, -1, -1 }, }; diff --git a/usr.bin/xlint/lint1/d_cast_init2.c b/usr.bin/xlint/lint1/d_cast_init2.c --- a/usr.bin/xlint/lint1/d_cast_init2.c +++ b/usr.bin/xlint/lint1/d_cast_init2.c @@ -1,7 +1,11 @@ +/* $NetBSD: d_cast_init2.c,v 1.3 2021/01/31 14:57:28 rillig Exp $ */ +# 3 "d_cast_init2.c" + /* cast initialization as the rhs of a - operand */ struct sockaddr_dl { char sdl_data[2]; }; -int npdl_datasize = sizeof(struct sockaddr_dl) - -((int) ((unsigned long)&((struct sockaddr_dl *) 0)->sdl_data[0])); +int npdl_datasize = sizeof(struct sockaddr_dl) - + ((int)((unsigned long) + &((struct sockaddr_dl *)0)->sdl_data[0])); diff --git a/usr.bin/xlint/lint1/d_cast_lhs.c b/usr.bin/xlint/lint1/d_cast_lhs.c --- a/usr.bin/xlint/lint1/d_cast_lhs.c +++ b/usr.bin/xlint/lint1/d_cast_lhs.c @@ -1,7 +1,29 @@ -/* pointer casts are valid lhs lvalues */ -struct sockaddr { }; +/* $NetBSD: d_cast_lhs.c,v 1.5 2021/07/04 13:14:54 rillig Exp $ */ +# 3 "d_cast_lhs.c" + +/* + * Pointer casts had been valid lvalues in GCC before 4.0. + * + * https://gcc.gnu.org/onlinedocs/gcc-3.4.6/gcc/Lvalues.html#Lvalues + * + * C99 6.5.4 "Cast operators" footnote 85 says "A cast does not yield an + * lvalue". + */ +struct str { + int member; +}; + +void sink(const void *); + +/* ARGSUSED */ void -foo() { - unsigned long p = 6; - ((struct sockaddr *)p) = 0; +foo(void *p) +{ + /* expect+2: error: a cast does not yield an lvalue [163] */ + /* expect+1: error: left operand of '=' must be lvalue [114] */ + ((struct str *)p) = 0; + + /* expect+2: error: a cast does not yield an lvalue [163] */ + /* expect+1: error: operand of '&' must be lvalue [114] */ + sink(&(const void *)p); } diff --git a/usr.bin/xlint/lint1/d_cast_lhs.exp b/usr.bin/xlint/lint1/d_cast_lhs.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/d_cast_lhs.exp @@ -0,0 +1,4 @@ +d_cast_lhs.c(24): error: a cast does not yield an lvalue [163] +d_cast_lhs.c(24): error: left operand of '=' must be lvalue [114] +d_cast_lhs.c(28): error: a cast does not yield an lvalue [163] +d_cast_lhs.c(28): error: operand of '&' must be lvalue [114] diff --git a/usr.bin/xlint/lint1/d_cast_typeof.c b/usr.bin/xlint/lint1/d_cast_typeof.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/d_cast_typeof.c @@ -0,0 +1,15 @@ +/* $NetBSD: d_cast_typeof.c,v 1.3 2021/01/31 14:39:31 rillig Exp $ */ +# 3 "d_cast_typeof.c" + +struct foo { + char list; +}; +struct foo *hole; + +char * +foo(void) +{ + return hole ? + ((char *)&((typeof(hole))0)->list) : + ((char *)&((typeof(*hole) *)0)->list); +} diff --git a/usr.bin/xlint/lint1/d_compound_literals1.c b/usr.bin/xlint/lint1/d_compound_literals1.c --- a/usr.bin/xlint/lint1/d_compound_literals1.c +++ b/usr.bin/xlint/lint1/d_compound_literals1.c @@ -1,3 +1,6 @@ +/* $NetBSD: d_compound_literals1.c,v 1.3 2021/01/31 14:57:28 rillig Exp $ */ +# 3 "d_compound_literals1.c" + /* compound literals */ struct p { @@ -6,6 +9,6 @@ foo() { - struct p me = (struct p) {1, 2, 3, 4}; + struct p me = (struct p){ 1, 2, 3, 4 }; me.a = me.b; } diff --git a/usr.bin/xlint/lint1/d_compound_literals2.c b/usr.bin/xlint/lint1/d_compound_literals2.c --- a/usr.bin/xlint/lint1/d_compound_literals2.c +++ b/usr.bin/xlint/lint1/d_compound_literals2.c @@ -1,3 +1,6 @@ +/* $NetBSD: d_compound_literals2.c,v 1.3 2021/01/31 14:57:28 rillig Exp $ */ +# 3 "d_compound_literals2.c" + /* compound literals */ struct p { @@ -6,7 +9,8 @@ 1, 2, 3, 4 }; -struct p *bar(int i) +struct p * +bar(int i) { static struct p q[10]; return &q[i]; @@ -14,5 +18,5 @@ foo() { - *bar(1) = (struct p) { 1, 2, 3, 4 }; + *bar(1) = (struct p){ 1, 2, 3, 4 }; } diff --git a/usr.bin/xlint/lint1/d_constant_conv1.c b/usr.bin/xlint/lint1/d_constant_conv1.c --- a/usr.bin/xlint/lint1/d_constant_conv1.c +++ b/usr.bin/xlint/lint1/d_constant_conv1.c @@ -1,3 +1,6 @@ +/* $NetBSD: d_constant_conv1.c,v 1.3 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "d_constant_conv1.c" + /* Flag information-losing constant conversion in argument lists */ int f(unsigned int); @@ -5,5 +8,5 @@ void should_fail() { - f(-1); + f(-1); /* expect: 296 */ } diff --git a/usr.bin/xlint/lint1/d_constant_conv1.exp b/usr.bin/xlint/lint1/d_constant_conv1.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/d_constant_conv1.exp @@ -0,0 +1 @@ +d_constant_conv1.c(11): warning: conversion of negative constant to unsigned type, arg #1 [296] diff --git a/usr.bin/xlint/lint1/d_constant_conv2.c b/usr.bin/xlint/lint1/d_constant_conv2.c --- a/usr.bin/xlint/lint1/d_constant_conv2.c +++ b/usr.bin/xlint/lint1/d_constant_conv2.c @@ -1,3 +1,6 @@ +/* $NetBSD: d_constant_conv2.c,v 1.3 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "d_constant_conv2.c" + /* Flag information-losing constant conversion in argument lists */ int f(unsigned int); @@ -5,5 +8,5 @@ void should_fail() { - f(2.1); + f(2.1); /* expect: 259 */ } diff --git a/usr.bin/xlint/lint1/d_constant_conv2.exp b/usr.bin/xlint/lint1/d_constant_conv2.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/d_constant_conv2.exp @@ -0,0 +1 @@ +d_constant_conv2.c(11): warning: argument #1 is converted from 'double' to 'unsigned int' due to prototype [259] diff --git a/usr.bin/xlint/lint1/d_cvt_constant.c b/usr.bin/xlint/lint1/d_cvt_constant.c --- a/usr.bin/xlint/lint1/d_cvt_constant.c +++ b/usr.bin/xlint/lint1/d_cvt_constant.c @@ -1,8 +1,12 @@ +/* $NetBSD: d_cvt_constant.c,v 1.5 2021/02/21 09:17:55 rillig Exp $ */ +# 3 "d_cvt_constant.c" + /* the second assignment assumes failed before */ int -main(void) { - double x = 1; - int foo = 0; - if (foo) - x = 1; +main(void) +{ + double x = 1; /* expect: 191 */ + int foo = 0; + if (foo) + x = 1; } diff --git a/usr.bin/xlint/lint1/d_cvt_constant.exp b/usr.bin/xlint/lint1/d_cvt_constant.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/d_cvt_constant.exp @@ -0,0 +1 @@ +d_cvt_constant.c(8): warning: 'x' set but not used in function 'main' [191] diff --git a/usr.bin/xlint/lint1/d_cvt_in_ternary.c b/usr.bin/xlint/lint1/d_cvt_in_ternary.c --- a/usr.bin/xlint/lint1/d_cvt_in_ternary.c +++ b/usr.bin/xlint/lint1/d_cvt_in_ternary.c @@ -1,13 +1,19 @@ +/* $NetBSD: d_cvt_in_ternary.c,v 1.3 2021/01/31 14:57:28 rillig Exp $ */ +# 3 "d_cvt_in_ternary.c" + /* CVT node handling in ?: operator */ typedef unsigned long int size_t; struct filecore_direntry { - unsigned len:32; + unsigned len: 32; }; + int main(void) { struct filecore_direntry dirent = { 0 }; - size_t uio_resid = 0; - size_t bytelen = (((dirent.len)<(uio_resid))?(dirent.len):(uio_resid)); + size_t uio_resid = 0; + size_t bytelen = (((dirent.len) < (uio_resid)) + ? (dirent.len) + : (uio_resid)); return bytelen; } diff --git a/usr.bin/xlint/lint1/d_decl_old_style_arguments.c b/usr.bin/xlint/lint1/d_decl_old_style_arguments.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/d_decl_old_style_arguments.c @@ -0,0 +1,15 @@ +# 2 "d_decl_old_style_arguments.c" + +/* + * A function is declared with a prototype, followed by an old style definition + * that is completely different. + */ + +void func(int a, int b, int c); + +void func(num, ptr, dbl, def) /* expect: 231 *//* expect: 231 *//* expect: 231 *//* expect: 231 */ + int num; + char *ptr; + double dbl; +{ /* expect: 32 *//* expect: 51 */ +} diff --git a/usr.bin/xlint/lint1/d_decl_old_style_arguments.exp b/usr.bin/xlint/lint1/d_decl_old_style_arguments.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/d_decl_old_style_arguments.exp @@ -0,0 +1,6 @@ +d_decl_old_style_arguments.c(14): warning: argument type defaults to 'int': def [32] +d_decl_old_style_arguments.c(14): error: parameter mismatch: 3 declared, 4 defined [51] +d_decl_old_style_arguments.c(10): warning: argument 'num' unused in function 'func' [231] +d_decl_old_style_arguments.c(10): warning: argument 'ptr' unused in function 'func' [231] +d_decl_old_style_arguments.c(10): warning: argument 'dbl' unused in function 'func' [231] +d_decl_old_style_arguments.c(10): warning: argument 'def' unused in function 'func' [231] diff --git a/usr.bin/xlint/lint1/d_ellipsis_in_switch.c b/usr.bin/xlint/lint1/d_ellipsis_in_switch.c --- a/usr.bin/xlint/lint1/d_ellipsis_in_switch.c +++ b/usr.bin/xlint/lint1/d_ellipsis_in_switch.c @@ -1,4 +1,10 @@ -int x(void) +/* $NetBSD: d_ellipsis_in_switch.c,v 1.4 2021/03/27 13:59:18 rillig Exp $ */ +# 3 "d_ellipsis_in_switch.c" + +/* Using a range in a case label is a GCC extension. */ + +int +x(void) { int i = 33; switch (i) { diff --git a/usr.bin/xlint/lint1/d_fold_test.c b/usr.bin/xlint/lint1/d_fold_test.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/d_fold_test.c @@ -0,0 +1,70 @@ +# 2 "d_fold_test.c" + +/* + * Test how expressions are handled in a context where they are tested for + * truthiness, such as in the condition of an if statement. + */ + +struct s { + int member; +}; + +union u { + int member; +}; + +enum e { + E +}; + +struct arr { + int arr[4]; +}; + +/* C99 6.2.5p2 */ +void if_Bool(_Bool b) { if (b) return; } + +/* C99 6.2.5p3 */ +void if_char(char c) { if (c) return; } + +/* C99 6.2.5p4 */ +void if_signed_char(signed char sc) { if (sc) return; } +void if_short_int(short s) { if (s) return; } +void if_int(int i) { if (i) return; } +void if_long_int(long int l) { if (l) return; } +void if_long_long_int(long long int ll) { if (ll) return; } + +/* C99 6.2.5p6 */ +void if_unsigned_char(unsigned char uc) { if (uc) return; } +void if_unsigned_short_int(unsigned short us) { if (us) return; } +void if_unsigned_int(unsigned int ui) { if (ui) return; } +void if_unsigned_long_int(unsigned long int ul) { if (ul) return; } +void if_unsigned_long_long_int(unsigned long long int ull) { if (ull) return; } + +/* C99 6.2.5p10 */ +void if_float(float f) { if (f) return; } +void if_double(double d) { if (d) return; } +void if_long_double(long double ld) { if (ld) return; } + +/* C99 6.2.5p11 */ +void if_float_Complex(float _Complex fc) { if (fc) return; } +void if_double_Complex(double _Complex dc) { if (dc) return; } +void if_long_double_Complex(long double _Complex ldc) { if (ldc) return; } + +/* C99 6.2.5p16 */ +void if_enum(enum e e) { if (e) return; } + +/* C99 6.2.5p20 */ +void if_array(struct arr arr) { if (arr.arr) return; } +void if_struct(struct s s) { if (s) return; } /* expect: 204 *//* expect: 231 */ +void if_union(union u u) { if (u) return; } /* expect: 204 *//* expect: 231 */ +void if_function(void) { if (if_function) return; } +void if_pointer(void *p) { if (p) return; } + +/* C99 6.8.5 */ +void while_struct(struct s s) { while (s) return; } /* expect: 204 *//* expect: 231 */ +void for_struct(struct s s) { for (;s;) return; } /* expect: 204 *//* expect: 223 *//* expect: 231 */ +void do_while_struct(struct s s) { do { return; } while (s); } /* expect: 204 *//* expect: 231 */ + +/* C99 6.5.15 does not require a scalar type, curiously. */ +int conditional_struct(struct s s) { return s ? 1 : 2; } /* expect: 170 *//* expect: 214 *//* expect: 231 */ diff --git a/usr.bin/xlint/lint1/d_fold_test.exp b/usr.bin/xlint/lint1/d_fold_test.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/d_fold_test.exp @@ -0,0 +1,14 @@ +d_fold_test.c(59): error: controlling expressions must have scalar type [204] +d_fold_test.c(59): warning: argument 's' unused in function 'if_struct' [231] +d_fold_test.c(60): error: controlling expressions must have scalar type [204] +d_fold_test.c(60): warning: argument 'u' unused in function 'if_union' [231] +d_fold_test.c(65): error: controlling expressions must have scalar type [204] +d_fold_test.c(65): warning: argument 's' unused in function 'while_struct' [231] +d_fold_test.c(66): error: controlling expressions must have scalar type [204] +d_fold_test.c(66): warning: end-of-loop code not reached [223] +d_fold_test.c(66): warning: argument 's' unused in function 'for_struct' [231] +d_fold_test.c(67): error: controlling expressions must have scalar type [204] +d_fold_test.c(67): warning: argument 's' unused in function 'do_while_struct' [231] +d_fold_test.c(70): error: first operand must have scalar type, op ? : [170] +d_fold_test.c(70): warning: function 'conditional_struct' expects to return value [214] +d_fold_test.c(70): warning: argument 's' unused in function 'conditional_struct' [231] diff --git a/usr.bin/xlint/lint1/d_gcc_compound_statements1.c b/usr.bin/xlint/lint1/d_gcc_compound_statements1.c --- a/usr.bin/xlint/lint1/d_gcc_compound_statements1.c +++ b/usr.bin/xlint/lint1/d_gcc_compound_statements1.c @@ -1,7 +1,36 @@ -/* GCC compound statements */ +/* $NetBSD: d_gcc_compound_statements1.c,v 1.6 2021/06/20 11:42:26 rillig Exp $ */ +# 3 "d_gcc_compound_statements1.c" + +/* GCC compound statement with expression */ foo(unsigned long z) { - z = ({ unsigned long tmp; tmp = 1; tmp; }); + z = ({ + unsigned long tmp; + tmp = 1; + tmp; + }); foo(z); } + +/* + * Compound statements are only allowed in functions, not at file scope. + * + * Before decl.c 1.186 from 2021-06-19, lint crashed with a segmentation + * fault. + */ +int c = ({ + return 3; /* expect: return outside function */ +}); /* expect: cannot initialize 'int' from 'void' */ + +void +function(void) +{ + /* + * Before cgram.y 1.229 from 2021-06-20, lint crashed due to the + * syntax error, which made an expression NULL. + */ + ({ + 0->e; /* expect: type 'int' does not have member 'e' */ + }); +} diff --git a/usr.bin/xlint/lint1/d_gcc_compound_statements1.exp b/usr.bin/xlint/lint1/d_gcc_compound_statements1.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/d_gcc_compound_statements1.exp @@ -0,0 +1,3 @@ +d_gcc_compound_statements1.c(23): error: syntax error 'return outside function' [249] +d_gcc_compound_statements1.c(24): error: cannot initialize 'int' from 'void' [185] +d_gcc_compound_statements1.c(34): error: type 'int' does not have member 'e' [101] diff --git a/usr.bin/xlint/lint1/d_gcc_compound_statements2.c b/usr.bin/xlint/lint1/d_gcc_compound_statements2.c --- a/usr.bin/xlint/lint1/d_gcc_compound_statements2.c +++ b/usr.bin/xlint/lint1/d_gcc_compound_statements2.c @@ -1,14 +1,28 @@ +/* $NetBSD: d_gcc_compound_statements2.c,v 1.3 2021/04/23 20:13:29 rillig Exp $ */ +# 3 "d_gcc_compound_statements2.c" + /* GCC compound statements with non-expressions */ struct cpu_info { int bar; }; int -main(void) +compound_expression_with_decl_and_stmt(void) { return ({ - struct cpu_info *__ci; - __asm__ volatile("movl %%fs:4,%0":"=r" (__ci)); - __ci; + struct cpu_info *ci; + __asm__ volatile("movl %%fs:4,%0":"=r" (ci)); + ci; })->bar; } + +int +compound_expression_with_only_stmt(void) +{ + struct cpu_info ci = { 0 }; + return ({ + if (ci.bar > 0) + ci.bar++; + ci; + }).bar; +} diff --git a/usr.bin/xlint/lint1/d_gcc_compound_statements3.c b/usr.bin/xlint/lint1/d_gcc_compound_statements3.c --- a/usr.bin/xlint/lint1/d_gcc_compound_statements3.c +++ b/usr.bin/xlint/lint1/d_gcc_compound_statements3.c @@ -1,3 +1,6 @@ +/* $NetBSD: d_gcc_compound_statements3.c,v 1.2 2021/01/31 14:39:31 rillig Exp $ */ +# 3 "d_gcc_compound_statements3.c" + /* GCC compound statements with void type */ void diff --git a/usr.bin/xlint/lint1/d_gcc_extension.c b/usr.bin/xlint/lint1/d_gcc_extension.c --- a/usr.bin/xlint/lint1/d_gcc_extension.c +++ b/usr.bin/xlint/lint1/d_gcc_extension.c @@ -1,6 +1,20 @@ +/* $NetBSD: d_gcc_extension.c,v 1.6 2021/05/25 19:22:18 rillig Exp $ */ +# 3 "d_gcc_extension.c" + +/* + * Test that the GCC '__extension__' and '__typeof' are recognized. + */ + +_Bool dbl_isinf(double); + /* extension */ -void a(void) { - double __logbw = 1; - if (__extension__(({ __typeof((__logbw)) x_ = (__logbw); !__builtin_isinf((x_)) && !__builtin_isnan((x_)); }))) - __logbw = 1; +void +a(void) +{ + double __logbw = 1; + if (__extension__(({ + __typeof((__logbw)) x_ = (__logbw); + !dbl_isinf((x_)); + }))) + __logbw = 1; } diff --git a/usr.bin/xlint/lint1/d_gcc_func.c b/usr.bin/xlint/lint1/d_gcc_func.c --- a/usr.bin/xlint/lint1/d_gcc_func.c +++ b/usr.bin/xlint/lint1/d_gcc_func.c @@ -1,7 +1,11 @@ +/* $NetBSD: d_gcc_func.c,v 1.3 2021/01/31 14:57:28 rillig Exp $ */ +# 3 "d_gcc_func.c" + /* gcc __FUNCTION__ */ void -foo(const char *p) { +foo(const char *p) +{ p = __FUNCTION__; foo(p); } diff --git a/usr.bin/xlint/lint1/d_gcc_variable_array_init.c b/usr.bin/xlint/lint1/d_gcc_variable_array_init.c --- a/usr.bin/xlint/lint1/d_gcc_variable_array_init.c +++ b/usr.bin/xlint/lint1/d_gcc_variable_array_init.c @@ -1,5 +1,9 @@ +/* $NetBSD: d_gcc_variable_array_init.c,v 1.3 2021/01/31 14:57:28 rillig Exp $ */ +# 3 "d_gcc_variable_array_init.c" + /* gcc: variable array initializer */ -void foo(int i) +void +foo(int i) { int array[i]; while (i--) diff --git a/usr.bin/xlint/lint1/d_incorrect_array_size.c b/usr.bin/xlint/lint1/d_incorrect_array_size.c --- a/usr.bin/xlint/lint1/d_incorrect_array_size.c +++ b/usr.bin/xlint/lint1/d_incorrect_array_size.c @@ -1,3 +1,6 @@ +/* $NetBSD: d_incorrect_array_size.c,v 1.3 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "d_incorrect_array_size.c" + struct foo { - int a[-1]; + int a[-1]; /* expect: 20 */ }; diff --git a/usr.bin/xlint/lint1/d_incorrect_array_size.exp b/usr.bin/xlint/lint1/d_incorrect_array_size.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/d_incorrect_array_size.exp @@ -0,0 +1 @@ +d_incorrect_array_size.c(5): error: negative array dimension (-1) [20] diff --git a/usr.bin/xlint/lint1/d_init_array_using_string.c b/usr.bin/xlint/lint1/d_init_array_using_string.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/d_init_array_using_string.c @@ -0,0 +1,81 @@ +/* $NetBSD: d_init_array_using_string.c,v 1.5 2021/08/14 13:00:55 rillig Exp $ */ +# 3 "d_init_array_using_string.c" + +/* + * Test initialization of arrays and pointers by string literals. + */ + +void sink(const void *); + +void +test_assignment_initialization(void) +{ + const char *cs_match = ""; + const int *ws_match = L""; + + /* expect+1: warning: illegal combination of 'pointer to const char' and 'pointer to int', op 'init' [124] */ + const char *cs_mismatch = L""; + /* expect+1: warning: illegal combination of 'pointer to const int' and 'pointer to char', op 'init' [124] */ + const int *ws_mismatch = ""; +} + +void +test_pointer_initialization_in_struct(void) +{ + struct cs_ws { + const char *cs; + const int *ws; + }; + + struct cs_ws type_match = { + "", + L"", + }; + + struct cs_ws type_mismatch = { + /* expect+1: warning: illegal combination of 'pointer to const char' and 'pointer to int', op 'init' [124] */ + L"", + /* expect+1: warning: illegal combination of 'pointer to const int' and 'pointer to char', op 'init' [124] */ + "", + }; + + struct cs_ws extra_braces = { + { "" }, + { L"" }, + }; +} + + +void +test_array_initialization_in_struct(void) +{ + struct cs_ws { + const char cs[10]; + const int ws[10]; + }; + + struct cs_ws type_match = { + "", + L"", + }; + + struct cs_ws type_mismatch = { + L"", /* expect: cannot initialize */ + "", /* expect: cannot initialize */ + }; + + struct cs_ws no_terminating_null = { + "0123456789", + L"0123456789", + }; + + struct cs_ws too_many_characters = { + "0123456789X", /* expect: non-null byte ignored */ + L"0123456789X", /* expect: non-null byte ignored */ + }; + + struct cs_ws extra_braces = { + { "" }, + { L"" }, + }; +} diff --git a/usr.bin/xlint/lint1/d_init_array_using_string.exp b/usr.bin/xlint/lint1/d_init_array_using_string.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/d_init_array_using_string.exp @@ -0,0 +1,8 @@ +d_init_array_using_string.c(17): warning: illegal combination of 'pointer to const char' and 'pointer to int', op 'init' [124] +d_init_array_using_string.c(19): warning: illegal combination of 'pointer to const int' and 'pointer to char', op 'init' [124] +d_init_array_using_string.c(37): warning: illegal combination of 'pointer to const char' and 'pointer to int', op 'init' [124] +d_init_array_using_string.c(39): warning: illegal combination of 'pointer to const int' and 'pointer to char', op 'init' [124] +d_init_array_using_string.c(63): error: cannot initialize 'array[10] of const char' from 'pointer to int' [185] +d_init_array_using_string.c(64): error: cannot initialize 'array[10] of const int' from 'pointer to char' [185] +d_init_array_using_string.c(73): warning: non-null byte ignored in string initializer [187] +d_init_array_using_string.c(74): warning: non-null byte ignored in string initializer [187] diff --git a/usr.bin/xlint/lint1/d_init_pop_member.c b/usr.bin/xlint/lint1/d_init_pop_member.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/d_init_pop_member.c @@ -0,0 +1,59 @@ +/* $NetBSD: d_init_pop_member.c,v 1.8 2021/06/20 18:11:21 rillig Exp $ */ +# 3 "d_init_pop_member.c" + +/* + * Since init.c 1.27 from 2015-07-28 and before init.c 1.178 from 2021-03-29, + * a bug in memberpop or pop_member led to a wrong error message + * "undefined struct/union member: capital [101]" in the second and third + * named initializer. + */ + +struct rgb { + unsigned red; + unsigned green; + unsigned blue; +}; + +struct hobbies { + unsigned dancing: 1; + unsigned running: 1; + unsigned swimming: 1; +}; + +struct person { + struct hobbies hobbies; + struct rgb favorite_color; +}; + +struct city { + struct person mayor; +}; + +struct state { + struct city capital; +}; + +void func(void) +{ + struct state st = { /* expect: set but not used */ + .capital.mayor.hobbies.dancing = 1, + /* + * Since 2015-07-28: + * wrong "undefined struct/union member: capital [101]" + */ + /* + * Before init.c 1.52 from 2020-01-01: + * wrong "warning: bit-field initializer does not fit [180]" + */ + .capital.mayor.favorite_color.green = 0xFF, + /* + * Since 2015-07-28: + * wrong "undefined struct/union member: capital [101]" + */ + /* + * Before init.c 1.52 from 2020-01-01: + * wrong "warning: bit-field initializer does not fit [180]" + */ + .capital.mayor.favorite_color.red = 0xFF, + }; +} diff --git a/usr.bin/xlint/lint1/d_init_pop_member.exp b/usr.bin/xlint/lint1/d_init_pop_member.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/d_init_pop_member.exp @@ -0,0 +1 @@ +d_init_pop_member.c(38): warning: 'st' set but not used in function 'func' [191] diff --git a/usr.bin/xlint/lint1/d_lint_assert.c b/usr.bin/xlint/lint1/d_lint_assert.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/d_lint_assert.c @@ -0,0 +1,22 @@ +/* $NetBSD: d_lint_assert.c,v 1.4 2021/07/10 12:10:39 rillig Exp $ */ +# 3 "d_lint_assert.c" + +/* + * Trigger the various assertions in the lint1 code. Several of them are + * just hard to trigger, but not impossible. +*/ + +enum { + // Before decl.c 1.118 from 2021-01-10: + // lint: assertion "sym->s_scl == EXTERN || sym->s_scl == STATIC" + // failed in check_global_variable at decl.c:3135 + // near d_lint_assert.c:14 + A = +++ +}; /* expect: 249 */ + +/* + * Before decl.c 1.196 from 2021-07-10, lint ran into an assertion failure + * for 'sym->s_type != NULL' in declare_argument. + */ +/* expect+1: warning: old style declaration; add 'int' [1] */ +c(void()); diff --git a/usr.bin/xlint/lint1/d_lint_assert.exp b/usr.bin/xlint/lint1/d_lint_assert.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/d_lint_assert.exp @@ -0,0 +1,2 @@ +d_lint_assert.c(15): error: syntax error '}' [249] +d_lint_assert.c(22): warning: old style declaration; add 'int' [1] diff --git a/usr.bin/xlint/lint1/d_long_double_int.c b/usr.bin/xlint/lint1/d_long_double_int.c --- a/usr.bin/xlint/lint1/d_long_double_int.c +++ b/usr.bin/xlint/lint1/d_long_double_int.c @@ -1,7 +1,10 @@ -/* PR 39639: writing "long double" gave "long int" */ +/* $NetBSD: d_long_double_int.c,v 1.4 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "d_long_double_int.c" + +/* PR bin/39639: writing "long double" gave "long int" */ int fail(long double *a, long int *b) { - return a == b; + return a == b; /* expect: 124 */ } diff --git a/usr.bin/xlint/lint1/d_long_double_int.exp b/usr.bin/xlint/lint1/d_long_double_int.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/d_long_double_int.exp @@ -0,0 +1 @@ +d_long_double_int.c(9): warning: illegal combination of 'pointer to long double' and 'pointer to long', op '==' [124] diff --git a/usr.bin/xlint/lint1/d_nested_structs.c b/usr.bin/xlint/lint1/d_nested_structs.c --- a/usr.bin/xlint/lint1/d_nested_structs.c +++ b/usr.bin/xlint/lint1/d_nested_structs.c @@ -1,10 +1,13 @@ +/* $NetBSD: d_nested_structs.c,v 1.3 2021/01/31 14:57:28 rillig Exp $ */ +# 3 "d_nested_structs.c" + /* Nested struct */ typedef void *EditLine; typedef void *History; typedef struct { - EditLine *el; - History *hist; + EditLine *el; + History *hist; } el_mode_t; struct el_modes_s { diff --git a/usr.bin/xlint/lint1/d_nolimit_init.c b/usr.bin/xlint/lint1/d_nolimit_init.c --- a/usr.bin/xlint/lint1/d_nolimit_init.c +++ b/usr.bin/xlint/lint1/d_nolimit_init.c @@ -1,4 +1,10 @@ -/* no limit initializers */ -char foo[][4] = { +/* $NetBSD: d_nolimit_init.c,v 1.3 2021/03/27 13:59:18 rillig Exp $ */ +# 3 "d_nolimit_init.c" + +/* + * no limit initializers, or as C99 calls it, initialization of an array of + * unknown size + */ +char weekday_names[][4] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; diff --git a/usr.bin/xlint/lint1/d_packed_structs.c b/usr.bin/xlint/lint1/d_packed_structs.c --- a/usr.bin/xlint/lint1/d_packed_structs.c +++ b/usr.bin/xlint/lint1/d_packed_structs.c @@ -1,21 +1,25 @@ +/* $NetBSD: d_packed_structs.c,v 1.3 2021/01/31 14:57:28 rillig Exp $ */ +# 3 "d_packed_structs.c" + /* packed tests */ struct in_addr { int x; }; -struct ip_timestamp { +struct ip_timestamp { char ipt_code; char ipt_len; char ipt_ptr; - unsigned int ipt_flg:4, - ipt_oflw:4; + unsigned int + ipt_flg: 4, + ipt_oflw: 4; union ipt_timestamp { - int ipt_time[1]; - struct ipt_ta { + int ipt_time[1]; + struct ipt_ta { struct in_addr ipt_addr; int ipt_time; - } ipt_ta[1] __packed; - } ipt_timestamp __packed; + } ipt_ta[1]__packed; + } ipt_timestamp__packed; } __packed; typedef struct __packed { diff --git a/usr.bin/xlint/lint1/d_pr_22119.c b/usr.bin/xlint/lint1/d_pr_22119.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/d_pr_22119.c @@ -0,0 +1,18 @@ +/* $NetBSD: d_pr_22119.c,v 1.2 2021/03/26 23:17:33 rillig Exp $ */ +# 3 "d_pr_22119.c" + +/* + * https://gnats.netbsd.org/22119 + * + * Before 2021-02-28, lint crashed in cast() since the target type of the + * cast is NULL. +*/ + +void +func1(void) +{ + void (*f1)(void); + + f1 = (void (*)(void))p; /* expect: 'p' undefined [99] */ + f1 = (void *()(void))p; /* crash before 2021-02-28 */ +} diff --git a/usr.bin/xlint/lint1/d_pr_22119.exp b/usr.bin/xlint/lint1/d_pr_22119.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/d_pr_22119.exp @@ -0,0 +1 @@ +d_pr_22119.c(16): error: 'p' undefined [99] diff --git a/usr.bin/xlint/lint1/d_return_type.c b/usr.bin/xlint/lint1/d_return_type.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/d_return_type.c @@ -0,0 +1,16 @@ +/* $NetBSD: d_return_type.c,v 1.3 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "d_return_type.c" + +enum A { + A +}; + +enum B { + B +}; + +enum A +func(enum B arg) +{ + return arg; /* expect: 211 */ +} diff --git a/usr.bin/xlint/lint1/d_return_type.exp b/usr.bin/xlint/lint1/d_return_type.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/d_return_type.exp @@ -0,0 +1 @@ +d_return_type.c(15): warning: return value type mismatch (enum A) and (enum B) [211] diff --git a/usr.bin/xlint/lint1/d_shift_to_narrower_type.c b/usr.bin/xlint/lint1/d_shift_to_narrower_type.c --- a/usr.bin/xlint/lint1/d_shift_to_narrower_type.c +++ b/usr.bin/xlint/lint1/d_shift_to_narrower_type.c @@ -1,7 +1,11 @@ +/* $NetBSD: d_shift_to_narrower_type.c,v 1.3 2021/01/31 14:57:28 rillig Exp $ */ +# 3 "d_shift_to_narrower_type.c" + // Test that type shifts that result to narrower types don't produce warnings. void -foo(void) { +foo(void) +{ unsigned long l = 100; unsigned long long ll = 100; unsigned int i = 100; diff --git a/usr.bin/xlint/lint1/d_struct_init_nested.c b/usr.bin/xlint/lint1/d_struct_init_nested.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/d_struct_init_nested.c @@ -0,0 +1,80 @@ +/* $NetBSD: d_struct_init_nested.c,v 1.6 2021/03/25 01:42:53 rillig Exp $ */ +# 3 "d_struct_init_nested.c" + +/* + * Initialization of a nested struct, in which some parts are initialized + * from non-constant expressions of the inner struct type. + * + * In C99, 6.7.8p13 describes exactly this case. + */ + +typedef enum O1 { O1C = 101 } O1; +typedef enum O2 { O2C = 102 } O2; +typedef enum O3 { O3C = 103 } O3; +typedef enum I1 { I1C = 201 } I1; +typedef enum I2 { I2C = 202 } I2; + +struct Inner1 { + I1 i1; +}; + +struct Outer3Inner1 { + O1 o1; + struct Inner1 inner; + O3 o3; +}; + +O1 +funcOuter3Inner1(void) +{ + struct Inner1 inner = { + I1C + }; + struct Outer3Inner1 o3i1 = { + O1C, + inner, + O3C + }; + + return o3i1.o1; +} + +struct Inner2 { + I1 i1; + I2 i2; +}; + +struct Outer3Inner2 { + O1 o1; + struct Inner2 inner; + O3 o3; +}; + +O1 +funcOuter3Inner2(void) +{ + struct Inner2 inner = { + I1C, + I2C + }; + struct Outer3Inner2 o3i2 = { + O1C, + inner, + O3C + }; + return o3i2.o1; +} + +/* + * For static storage duration, each initializer expression must be a constant + * expression. + */ +struct Inner2 inner = { + I1C, + I2C +}; +struct Outer3Inner2 o3i2 = { + O1C, + inner, /* expect: non-constant initializer */ + O3C +}; diff --git a/usr.bin/xlint/lint1/d_struct_init_nested.exp b/usr.bin/xlint/lint1/d_struct_init_nested.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/d_struct_init_nested.exp @@ -0,0 +1 @@ +d_struct_init_nested.c(78): error: non-constant initializer [177] diff --git a/usr.bin/xlint/lint1/d_type_conv1.c b/usr.bin/xlint/lint1/d_type_conv1.c --- a/usr.bin/xlint/lint1/d_type_conv1.c +++ b/usr.bin/xlint/lint1/d_type_conv1.c @@ -1,3 +1,6 @@ +/* $NetBSD: d_type_conv1.c,v 1.3 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "d_type_conv1.c" + /* Flag information-losing type conversion in argument lists */ int f(unsigned int); @@ -7,5 +10,5 @@ { long long x = 20; - f(x); + f(x); /* expect: 259 */ } diff --git a/usr.bin/xlint/lint1/d_type_conv1.exp b/usr.bin/xlint/lint1/d_type_conv1.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/d_type_conv1.exp @@ -0,0 +1 @@ +d_type_conv1.c(13): warning: argument #1 is converted from 'long long' to 'unsigned int' due to prototype [259] diff --git a/usr.bin/xlint/lint1/d_type_conv2.c b/usr.bin/xlint/lint1/d_type_conv2.c --- a/usr.bin/xlint/lint1/d_type_conv2.c +++ b/usr.bin/xlint/lint1/d_type_conv2.c @@ -1,3 +1,6 @@ +/* $NetBSD: d_type_conv2.c,v 1.3 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "d_type_conv2.c" + /* Flag information-losing type conversion in argument lists */ int f(float); @@ -7,5 +10,5 @@ { double x = 2.0; - f(x); + f(x); /* expect: 259 */ } diff --git a/usr.bin/xlint/lint1/d_type_conv2.exp b/usr.bin/xlint/lint1/d_type_conv2.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/d_type_conv2.exp @@ -0,0 +1 @@ +d_type_conv2.c(13): warning: argument #1 is converted from 'double' to 'float' due to prototype [259] diff --git a/usr.bin/xlint/lint1/d_type_conv3.c b/usr.bin/xlint/lint1/d_type_conv3.c --- a/usr.bin/xlint/lint1/d_type_conv3.c +++ b/usr.bin/xlint/lint1/d_type_conv3.c @@ -1,3 +1,6 @@ +/* $NetBSD: d_type_conv3.c,v 1.4 2021/04/05 01:35:34 rillig Exp $ */ +# 3 "d_type_conv3.c" + /* Flag information-losing type conversion in argument lists */ int f(unsigned int); @@ -6,5 +9,5 @@ should_fail() { - f(0x7fffffffffffffffLL); + f(0x7fffffffffffffffLL); /* expect: 259 *//* expect: 295 */ } diff --git a/usr.bin/xlint/lint1/d_type_conv3.exp b/usr.bin/xlint/lint1/d_type_conv3.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/d_type_conv3.exp @@ -0,0 +1,2 @@ +d_type_conv3.c(12): warning: argument #1 is converted from 'long long' to 'unsigned int' due to prototype [259] +d_type_conv3.c(12): warning: conversion of 'long long' to 'unsigned int' is out of range, arg #1 [295] diff --git a/usr.bin/xlint/lint1/d_type_question_colon.c b/usr.bin/xlint/lint1/d_type_question_colon.c --- a/usr.bin/xlint/lint1/d_type_question_colon.c +++ b/usr.bin/xlint/lint1/d_type_question_colon.c @@ -1,3 +1,6 @@ +/* $NetBSD: d_type_question_colon.c,v 1.3 2021/01/31 14:57:28 rillig Exp $ */ +# 3 "d_type_question_colon.c" + /* the type of the ?: expression should be the more specific type */ struct foo { @@ -5,7 +8,8 @@ }; void -test(void) { +test(void) +{ int i; struct foo *ptr = 0; diff --git a/usr.bin/xlint/lint1/d_typefun.c b/usr.bin/xlint/lint1/d_typefun.c --- a/usr.bin/xlint/lint1/d_typefun.c +++ b/usr.bin/xlint/lint1/d_typefun.c @@ -1,22 +1,30 @@ +/* $NetBSD: d_typefun.c,v 1.3 2021/01/31 14:39:31 rillig Exp $ */ +# 3 "d_typefun.c" + /* typedef of function parameter */ -typedef void (*free_func) (void * opaque, void* address); -typedef struct stack_st -{ - int num; - char **data; - int sorted; +typedef void (*free_func)(void *opaque, void *address); + +typedef struct stack_st { + int num; + char **data; + int sorted; - int num_alloc; - int (*comp)(const void *, const void *); + int num_alloc; + int (*comp)(const void *, const void *); } _STACK; /* Use STACK_OF(...) instead */ typedef void *OPENSSL_BLOCK; -struct stack_st_OPENSSL_BLOCK { _STACK stack; }; -typedef void *d2i_of_void(void **,const unsigned char **,long); typedef int i2d_of_void(void *,unsigned char **); +struct stack_st_OPENSSL_BLOCK { + _STACK stack; +}; + +typedef void *d2i_of_void(void **, const unsigned char **, long); +typedef int i2d_of_void(void *, unsigned char **); -struct stack_st_OPENSSL_BLOCK *d2i_ASN1_SET(struct stack_st_OPENSSL_BLOCK **a, - const unsigned char **pp, - long length, d2i_of_void *d2i, - void (*free_func)(OPENSSL_BLOCK), int ex_tag, - int ex_class); +struct stack_st_OPENSSL_BLOCK * +d2i_ASN1_SET(struct stack_st_OPENSSL_BLOCK **a, + const unsigned char **pp, + long length, d2i_of_void *d2i, + void (*free_func)(OPENSSL_BLOCK), int ex_tag, + int ex_class); diff --git a/usr.bin/xlint/lint1/d_typename_as_var.c b/usr.bin/xlint/lint1/d_typename_as_var.c --- a/usr.bin/xlint/lint1/d_typename_as_var.c +++ b/usr.bin/xlint/lint1/d_typename_as_var.c @@ -1,3 +1,6 @@ +/* $NetBSD: d_typename_as_var.c,v 1.2 2021/01/31 14:39:31 rillig Exp $ */ +# 3 "d_typename_as_var.c" + typedef char h[10]; typedef struct { diff --git a/usr.bin/xlint/lint1/d_zero_sized_arrays.c b/usr.bin/xlint/lint1/d_zero_sized_arrays.c --- a/usr.bin/xlint/lint1/d_zero_sized_arrays.c +++ b/usr.bin/xlint/lint1/d_zero_sized_arrays.c @@ -1,3 +1,6 @@ +/* $NetBSD: d_zero_sized_arrays.c,v 1.3 2021/01/31 14:57:28 rillig Exp $ */ +# 3 "d_zero_sized_arrays.c" + struct foo { -int a[0]; + int a[0]; }; diff --git a/usr.bin/xlint/lint1/decl.c b/usr.bin/xlint/lint1/decl.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/decl.c @@ -0,0 +1,172 @@ +/* $NetBSD: decl.c,v 1.12 2021/07/25 22:03:42 rillig Exp $ */ +# 3 "decl.c" + +/* + * Tests for declarations, especially the distinction between the + * declaration-specifiers and the declarators. + */ + +/* + * Even though 'const' comes after 'char' and is therefore quite close to the + * first identifier, it applies to both identifiers. + */ +void +specifier_qualifier(void) +{ + char const a = 1, b = 2; + + /* expect+1: warning: left operand of '=' must be modifiable lvalue [115] */ + a = 1; + /* expect+1: warning: left operand of '=' must be modifiable lvalue [115] */ + b = 2; +} + +/* + * Since 'const' comes before 'char', there is no ambiguity whether the + * 'const' applies to all variables or just to the first. + */ +void +qualifier_specifier(void) +{ + const char a = 1, b = 2; + + /* expect+1: warning: left operand of '=' must be modifiable lvalue [115] */ + a = 3; + /* expect+1: warning: left operand of '=' must be modifiable lvalue [115] */ + b = 5; +} + +void +declarator_with_prefix_qualifier(void) +{ + /* expect+1: syntax error 'const' [249] */ + char a = 1, const b = 2; + + a = 1; + /* expect+1: error: 'b' undefined [99] */ + b = 2; +} + +void +declarator_with_postfix_qualifier(void) +{ + /* expect+1: syntax error 'const' [249] */ + char a = 1, b const = 2; + + a = 1; + b = 2; +} + +void sink(double *); + +void +declarators(void) +{ + char *pc = 0, c = 0, **ppc = 0; + + /* expect+1: warning: converting 'pointer to char' to incompatible 'pointer to double' */ + sink(pc); + /* expect+1: warning: illegal combination of pointer (pointer to double) and integer (char) */ + sink(c); + /* expect+1: converting 'pointer to pointer to char' to incompatible 'pointer to double' */ + sink(ppc); +} + +_Bool +enum_error_handling(void) +{ + enum { + /* expect+1: syntax error '"' [249] */ + "error 1" + : /* still the same error */ + , /* back on track */ + A, + B + } x = A; + + return x == B; +} + +/* + * An __attribute__ at the beginning of a declaration may become ambiguous + * since a GCC fallthrough statement starts with __attribute__ as well. + */ +void +unused_local_variable(void) +{ + __attribute__((unused)) _Bool unused_var; + + __attribute__((unused)) + __attribute__((unused)) _Bool unused_twice; +} + +int +declaration_without_type_specifier(void) +{ + const i = 3; + + return i; +} + +/* TODO: add quotes around %s */ +/* expect+2: warning: static function unused unused [236] */ +static void +unused(void) +{ +} + +/* + * The attribute 'used' does not influence static functions, it only + * applies to function parameters. + */ +/* LINTED */ +static void +unused_linted(void) +{ +} + +/* covers 'type_qualifier_list: type_qualifier_list type_qualifier' */ +int *const volatile cover_type_qualifier_list; + +_Bool bool; +char plain_char; +signed char signed_char; +unsigned char unsigned_char; +short signed_short; +unsigned short unsigned_short; +int signed_int; +unsigned int unsigned_int; +long signed_long; +unsigned long unsigned_long; +struct { + int member; +} unnamed_struct; + +/* + * Before decl.c 1.201 from 2021-07-15, lint crashed with an internal error + * in end_type. + */ +unsigned long sizes = + sizeof(const typeof(bool)) + + sizeof(const typeof(plain_char)) + + sizeof(const typeof(signed_char)) + + sizeof(const typeof(unsigned_char)) + + sizeof(const typeof(signed_short)) + + sizeof(const typeof(unsigned_short)) + + sizeof(const typeof(signed_int)) + + sizeof(const typeof(unsigned_int)) + + sizeof(const typeof(signed_long)) + + sizeof(const typeof(unsigned_long)) + + sizeof(const typeof(unnamed_struct)); + +/* expect+1: syntax error 'int' [249] */ +thread int thread_int; +__thread int thread_int; +/* expect+1: syntax error 'int' [249] */ +__thread__ int thread_int; + +/* expect+2: warning: static function cover_func_declarator unused [236] */ +static +cover_func_declarator(void) +{ +} diff --git a/usr.bin/xlint/lint1/decl.exp b/usr.bin/xlint/lint1/decl.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/decl.exp @@ -0,0 +1,15 @@ +decl.c(19): warning: left operand of '=' must be modifiable lvalue [115] +decl.c(21): warning: left operand of '=' must be modifiable lvalue [115] +decl.c(34): warning: left operand of '=' must be modifiable lvalue [115] +decl.c(36): warning: left operand of '=' must be modifiable lvalue [115] +decl.c(43): error: syntax error 'const' [249] +decl.c(47): error: 'b' undefined [99] +decl.c(54): error: syntax error 'const' [249] +decl.c(68): warning: converting 'pointer to char' to incompatible 'pointer to double' for argument 1 [153] +decl.c(70): warning: illegal combination of pointer (pointer to double) and integer (char), arg #1 [154] +decl.c(72): warning: converting 'pointer to pointer to char' to incompatible 'pointer to double' for argument 1 [153] +decl.c(80): error: syntax error '"' [249] +decl.c(163): error: syntax error 'int' [249] +decl.c(166): error: syntax error 'int' [249] +decl.c(114): warning: static function unused unused [236] +decl.c(170): warning: static function cover_func_declarator unused [236] diff --git a/usr.bin/xlint/lint1/decl_arg.c b/usr.bin/xlint/lint1/decl_arg.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/decl_arg.c @@ -0,0 +1,135 @@ +/* $NetBSD: decl_arg.c,v 1.6 2021/07/25 06:04:40 rillig Exp $ */ +# 3 "decl_arg.c" + +/* + * Tests for declarations of function arguments. + * + * See arg_declaration in cgram.y. + */ + +typedef double number; + +void no_args(void); +void type_unnamed(double ); +void type_named(double arg); +void typedef_unnamed_prototype(number); +void typedef_named(number arg); + +void type_qualifier(const number); +void type_qualifier_pointer(const number *const); + +/* + * Just some unrealistic coverage for the grammar rule 'arg_declaration'. + */ +/* expect+6: warning: argument 'an_int' unused in function 'old_style' [231] */ +/* expect+5: warning: argument 'a_const_int' unused in function 'old_style' [231] */ +/* expect+4: warning: argument 'a_number' unused in function 'old_style' [231] */ +/* expect+3: warning: argument 'a_function' unused in function 'old_style' [231] */ +/* expect+2: warning: argument 'a_struct' unused in function 'old_style' [231] */ +extern void +old_style(an_int, a_const_int, a_number, a_function, a_struct) +/* expect+2: warning: empty declaration [2] */ +/* expect+1: error: only register valid as formal parameter storage class [9] */ +static; +/* expect+1: error: syntax error '"' [249] */ +static "error"; +/* expect+1: warning: empty declaration [2] */ +const; +/* expect+1: error: declared argument undeclared is missing [53] */ +const undeclared; +/* expect+2: error: declared argument undeclared_initialized is missing [53] */ +/* expect+1: error: cannot initialize parameter: undeclared_initialized [52] */ +const undeclared_initialized = 12345; +/* expect+1: warning: empty declaration [2] */ +int; +/* expect+1: warning: 'struct arg_struct' declared in argument declaration list [3] */ +struct arg_struct { int member; }; +/* expect+1: error: cannot initialize parameter: an_int [52] */ +int an_int = 12345; +const int a_const_int; +number a_number; +void (a_function) (number); +/* expect+1: warning: 'struct a_struct' declared in argument declaration list [3] */ +struct a_struct { int member; } a_struct; +{ +} + +/* + * Just some unrealistic coverage for the grammar rule + * 'notype_direct_declarator'. + */ +extern int +cover_notype_direct_decl(arg) +int arg; +/* expect+1: error: declared argument name is missing [53] */ +const name; +/* expect+1: error: declared argument parenthesized_name is missing [53] */ +const (parenthesized_name); +/* expect+1: error: declared argument array is missing [53] */ +const array[]; +/* expect+1: error: declared argument array_size is missing [53] */ +const array_size[1+1+1]; +/* expect+2: error: declared argument multi_array is missing [53] */ +/* expect+1: error: null dimension [17] */ +const multi_array[][][][][][]; +/* expect+1: error: declared argument function is missing [53] */ +const function(void); +/* expect+1: error: declared argument prefix_attribute is missing [53] */ +const __attribute__((deprecated)) prefix_attribute; +/* expect+1: error: declared argument postfix_attribute is missing [53] */ +const postfix_attribute __attribute__((deprecated)); +/* expect+1: error: declared argument infix_attribute is missing [53] */ +const __attribute__((deprecated)) infix_attribute __attribute__((deprecated)); +/* The __attribute__ before the '*' is consumed by some other grammar rule. */ +/* expect+7: error: declared argument pointer_prefix_attribute is missing [53] */ +const + __attribute__((deprecated)) + * + __attribute__((deprecated)) + __attribute__((deprecated)) + __attribute__((deprecated)) + pointer_prefix_attribute; +{ + return arg; +} + +void test_varargs_attribute( + void (*pr)(const char *, ...) + __attribute__((__format__(__printf__, 1, 2))) +); + +/* + * XXX: To cover the grammar rule 'direct_notype_param_decl', the parameters + * need to be enclosed by one more pair of parentheses than usual. + */ +void cover_direct_notype_param_decl( + double (identifier), + double ((parenthesized)), + double (array[]), + double (array_size[3]), + double (*)(void (function())) +); + +/* + * Just some unrealistic code to cover the grammar rule parameter_declaration. + */ +/* expect+4: error: only register valid as formal parameter storage class [9] */ +void cover_parameter_declaration( + volatile, /* 1 */ + double, /* 2 */ + static storage_class, /* 3.1 */ + const type_qualifier, /* 3.2 */ + double (identifier), /* 4 */ + const (*), /* 5 */ + double *const, /* 6 */ + ... +); + +void cover_asm_or_symbolrename_asm(void) + __asm("assembly code"); + +void cover_asm_or_symbolrename_symbolrename(void) + __symbolrename(alternate_name); + +// FIXME: internal error in decl.c:906 near decl_arg.c:134: length(0) +//void cover_abstract_declarator_typeof(void (*)(typeof(no_args))); diff --git a/usr.bin/xlint/lint1/decl_arg.exp b/usr.bin/xlint/lint1/decl_arg.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/decl_arg.exp @@ -0,0 +1,28 @@ +decl_arg.c(33): error: only register valid as formal parameter storage class [9] +decl_arg.c(33): warning: empty declaration [2] +decl_arg.c(35): error: syntax error '"' [249] +decl_arg.c(37): warning: empty declaration [2] +decl_arg.c(39): error: declared argument undeclared is missing [53] +decl_arg.c(42): error: declared argument undeclared_initialized is missing [53] +decl_arg.c(42): error: cannot initialize parameter: undeclared_initialized [52] +decl_arg.c(44): warning: empty declaration [2] +decl_arg.c(46): warning: 'struct arg_struct' declared in argument declaration list [3] +decl_arg.c(48): error: cannot initialize parameter: an_int [52] +decl_arg.c(53): warning: 'struct a_struct' declared in argument declaration list [3] +decl_arg.c(30): warning: argument 'an_int' unused in function 'old_style' [231] +decl_arg.c(30): warning: argument 'a_const_int' unused in function 'old_style' [231] +decl_arg.c(30): warning: argument 'a_number' unused in function 'old_style' [231] +decl_arg.c(30): warning: argument 'a_function' unused in function 'old_style' [231] +decl_arg.c(30): warning: argument 'a_struct' unused in function 'old_style' [231] +decl_arg.c(65): error: declared argument name is missing [53] +decl_arg.c(67): error: declared argument parenthesized_name is missing [53] +decl_arg.c(69): error: declared argument array is missing [53] +decl_arg.c(71): error: declared argument array_size is missing [53] +decl_arg.c(74): error: null dimension [17] +decl_arg.c(74): error: declared argument multi_array is missing [53] +decl_arg.c(76): error: declared argument function is missing [53] +decl_arg.c(78): error: declared argument prefix_attribute is missing [53] +decl_arg.c(80): error: declared argument postfix_attribute is missing [53] +decl_arg.c(82): error: declared argument infix_attribute is missing [53] +decl_arg.c(91): error: declared argument pointer_prefix_attribute is missing [53] +decl_arg.c(120): error: only register valid as formal parameter storage class [9] diff --git a/usr.bin/xlint/lint1/decl_enum.c b/usr.bin/xlint/lint1/decl_enum.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/decl_enum.c @@ -0,0 +1,19 @@ +/* $NetBSD: decl_enum.c,v 1.1 2021/07/15 21:00:05 rillig Exp $ */ +# 3 "decl_enum.c" + +/* + * Tests for enum declarations. + */ + +/* cover 'enumerator_list: error' */ +enum { + /* expect+1: error: syntax error 'goto' [249] */ + goto +}; + +/* cover 'enum_specifier: enum error' */ +/* expect+1: error: syntax error 'goto' [249] */ +enum goto { + A +}; +/* expect-1: warning: empty declaration [0] */ diff --git a/usr.bin/xlint/lint1/decl_enum.exp b/usr.bin/xlint/lint1/decl_enum.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/decl_enum.exp @@ -0,0 +1,3 @@ +decl_enum.c(11): error: syntax error 'goto' [249] +decl_enum.c(16): error: syntax error 'goto' [249] +decl_enum.c(18): warning: empty declaration [0] diff --git a/usr.bin/xlint/lint1/decl_enum_c90.c b/usr.bin/xlint/lint1/decl_enum_c90.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/decl_enum_c90.c @@ -0,0 +1,13 @@ +/* $NetBSD: decl_enum_c90.c,v 1.1 2021/07/15 21:00:05 rillig Exp $ */ +# 3 "decl_enum_c90.c" + +/* + * Tests for enum declarations in C90 mode. + */ + +/* lint1-flags: -sw */ + +enum { + A, +}; +/* expect-1: trailing ',' prohibited in enum declaration [54] */ diff --git a/usr.bin/xlint/lint1/decl_enum_c90.exp b/usr.bin/xlint/lint1/decl_enum_c90.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/decl_enum_c90.exp @@ -0,0 +1 @@ +decl_enum_c90.c(12): error: trailing ',' prohibited in enum declaration [54] diff --git a/usr.bin/xlint/lint1/decl_struct_c90.c b/usr.bin/xlint/lint1/decl_struct_c90.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/decl_struct_c90.c @@ -0,0 +1,31 @@ +/* $NetBSD: decl_struct_c90.c,v 1.3 2021/07/15 21:00:05 rillig Exp $ */ +# 3 "decl_struct_c90.c" + +/* + * Test declaration of struct types, in C90 without any GNU extensions. + */ + +/* lint1-flags: -sw */ + +struct unnamed_member { + enum { A, B, C } tag; + union { + int a_value; + void *b_value; + void (*c_value)(void); + }; + /* expect-1: warning: anonymous struct/union members is a C9X feature [49] */ +}; + +/* + * All of K&R, C90, C99 require that a struct member declaration is + * terminated with a semicolon. + * + * Before cgram.y 1.328 from 2021-07-15, lint allowed the missing semicolon + * in non-C90 mode, no idea why. + */ +struct missing_semicolon { + int member +}; +/* expect-1: error: syntax error '}' [249] */ +/* expect+1: error: cannot recover from previous errors [224] */ diff --git a/usr.bin/xlint/lint1/decl_struct_c90.exp b/usr.bin/xlint/lint1/decl_struct_c90.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/decl_struct_c90.exp @@ -0,0 +1,3 @@ +decl_struct_c90.c(16): warning: anonymous struct/union members is a C9X feature [49] +decl_struct_c90.c(29): error: syntax error '}' [249] +decl_struct_c90.c(32): error: cannot recover from previous errors [224] diff --git a/usr.bin/xlint/lint1/decl_struct_member.c b/usr.bin/xlint/lint1/decl_struct_member.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/decl_struct_member.c @@ -0,0 +1,86 @@ +/* $NetBSD: decl_struct_member.c,v 1.11 2021/08/25 22:04:52 rillig Exp $ */ +# 3 "decl_struct_member.c" + +struct multi_attributes { + __attribute__((deprecated)) + __attribute__((deprecated)) + __attribute__((deprecated)) + int deprecated; +}; + +struct cover_begin_type_specifier_qualifier_list { + int m1; + __attribute__((deprecated)) int m2; + const int m3; + int const m4; + int const long m5; + int __attribute__((deprecated)) m6; +}; + +typedef int number; + +struct cover_begin_type_typespec { + int m1; + number m2; +}; + +struct cover_begin_type_qualifier_list { + const m1; + const volatile m2; +}; + +/* cover struct_or_union_specifier: struct_or_union error */ +/* expect+1: error: syntax error 'goto' [249] */ +struct goto { + /* expect+1: error: illegal type combination [4] */ + int member; + /* expect+1: error: syntax error '}' [249] */ +}; +/* expect-1: warning: empty declaration [0] */ + +/* + * Before cgram.y 1.228 from 2021-06-19, lint ran into an assertion failure: + * + * "is_struct_or_union(dcs->d_type->t_tspec)" at cgram.y:846 + */ +struct { + char; /* expect: syntax error 'unnamed member' */ +}; + +struct cover_notype_struct_declarators { + const a, b; +}; + +struct cover_notype_struct_declarator_bit_field { + const a: 3, : 0, b: 4; + const : 0; +}; + +/* + * An array of bit-fields sounds like a strange idea since a bit-field member + * is not addressable, while an array needs to be addressable. Due to this + * contradiction, this combination may have gone without mention in the C + * standards. + * + * GCC 10.3.0 complains that the bit-field has invalid type. + * + * Clang 12.0.1 complains that the bit-field has non-integral type 'unsigned + * int [8]'. + */ +struct array_of_bit_fields { + /* expect+1: warning: illegal bit-field type 'array[8] of unsigned int' [35] */ + unsigned int bits[8]: 1; +}; + +/* + * Before decl.c 1.188 from 2021-06-20, lint ran into a segmentation fault. + */ +struct { + char a(_)0 /* expect: syntax error '0' */ +} +/* + * FIXME: adding a semicolon here triggers another assertion: + * + * assertion "t == NOTSPEC" failed in end_type at decl.c:774 + */ +/* expect+1: cannot recover from previous errors */ diff --git a/usr.bin/xlint/lint1/decl_struct_member.exp b/usr.bin/xlint/lint1/decl_struct_member.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/decl_struct_member.exp @@ -0,0 +1,8 @@ +decl_struct_member.c(34): error: syntax error 'goto' [249] +decl_struct_member.c(36): error: illegal type combination [4] +decl_struct_member.c(38): error: syntax error '}' [249] +decl_struct_member.c(38): warning: empty declaration [0] +decl_struct_member.c(47): error: syntax error 'unnamed member' [249] +decl_struct_member.c(72): warning: illegal bit-field type 'array[8] of unsigned int' [35] +decl_struct_member.c(79): error: syntax error '0' [249] +decl_struct_member.c(87): error: cannot recover from previous errors [224] diff --git a/usr.bin/xlint/lint1/emit.c b/usr.bin/xlint/lint1/emit.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/emit.c @@ -0,0 +1,265 @@ +/* $NetBSD: emit.c,v 1.5 2021/08/30 21:23:37 rillig Exp $ */ +# 3 "emit.c" + +/* + * Test the symbol information that lint1 writes to a .ln file. Using this + * symbol information, lint2 later checks that the symbols are used + * consistently across different translation units. + */ + +/* Do not warn about unused parameters. */ +/* lint1-extra-flags: -X 231 */ + +/* + * Define some derived types. + */ + +struct struct_tag { + int member; +}; + +typedef struct { + int member; +} struct_typedef; + +union union_tag { + int member; +}; + +typedef union { + int member; +} union_typedef; + +enum enum_tag { + enum_tag_constant +}; + +typedef enum { + enum_typedef_constant +} enum_typedef; + +/* + * Variable declarations using the basic types (C99 6.2.5p14). + * + * Last synced with function outtype from emit1.c 1.43. + */ + +extern _Bool extern__Bool; +extern float _Complex extern__Complex_float; +extern double _Complex extern__Complex_double; +extern long double _Complex extern__Complex_long_double; +extern char extern_char; +extern signed char extern_signed_char; +extern unsigned char extern_unsigned_char; +extern short extern_short; +extern signed short extern_signed_short; +extern unsigned short extern_unsigned_short; +extern int extern_int; +extern signed int extern_signed_int; +extern unsigned int extern_unsigned_int; +extern long extern_long; +extern signed long extern_signed_long; +extern unsigned long extern_unsigned_long; +extern long long extern_long_long; +extern signed long long extern_signed_long_long; +extern unsigned long long extern_unsigned_long_long; +extern float extern_float; +extern double extern_double; +extern long double extern_long_double; + +/* + * Variable declarations using derived types (C99 6.2.5p20). + */ + +extern void * extern_pointer_to_void; +extern int extern_array_5_of_int[5]; + +/* + * Type tags are written to the .ln file as 'T kind length name', where 'kind' + * is either 1, 2 or 3. This is confusing at first since in 'T110struct_tag', + * the apparent number 110 is to be read as 'tag kind 1, length 10'. + */ +extern struct struct_tag extern_struct_tag; +extern struct_typedef extern_struct_typedef; +extern union union_tag extern_union_tag; +extern union_typedef extern_union_typedef; +extern enum enum_tag extern_enum_tag; +extern enum_typedef extern_enum_typedef; + +extern struct { + int member; +} extern_anonymous_struct; +extern union { + int member; +} extern_anonymous_union; +extern enum { + anonymous_enum_constant +} extern_anonymous_enum; + +/* + * Variable definitions. + * + * Static variables are not recorded in the .ln file. + */ + +extern int declared_int; +int defined_int; +static int static_int; /* expect: unused */ + +/* + * Type qualifiers. + */ + +extern const int extern_const_int; +extern volatile int extern_volatile_int; +extern const volatile int extern_const_volatile_int; + +/* + * Functions. + */ + +extern void return_void_unknown_parameters(); +extern /* implicit int */ return_implicit_int_unknown_parameters(); + +/* For function declarations, the keyword 'extern' is optional. */ +extern void extern_return_void_no_parameters(void); +/* implicit extern */ void return_void_no_parameters(void); +static void static_return_void_no_parameters(void); /* expect: declared */ + +void taking_int(int); +/* The 'const' parameter does not make a difference. */ +void taking_const_int(const int); +void taking_int_double_bool(int, double, _Bool); +void taking_struct_union_enum_tags(struct struct_tag, union union_tag, + enum enum_tag); +void taking_struct_union_enum_typedefs(struct_typedef, union_typedef, + enum_typedef); + +void taking_varargs(const char *, ...); + +/* + * This function does not affect anything outside this translation unit. + * Naively there is no need to record this function in the .ln file, but it + * is nevertheless recorded. There's probably a good reason for recording + * it. + */ +static int static_function(void); /* expect: declared */ + +void my_printf(const char *, ...); + +/* + * String literals that occur in function calls are written to the .ln file, + * just in case they are related to a printf-like or scanf-like function. + * + * In this example, the various strings are not format strings, they just + * serve to cover the code that escapes character literals (outqchar in + * lint1) and reads them back into characters (inpqstrg in lint2). + */ +void +cover_outqchar(void) +{ + my_printf("%s", "%"); + my_printf("%s", "%s"); + my_printf("%s", "%%"); + my_printf("%s", "%\a %\b %\f %\n %\r %\t %\v %\177"); +} + +/* + * Calls to GCC builtin functions should not be emitted since GCC already + * guarantees a consistent definition of these function and checks the + * arguments, so there is nothing left to do for lint. + */ +void +call_gcc_builtins(int x, long *ptr) +{ + long value; + + __builtin_expect(x > 0, 1); + __builtin_bswap32(0x12345678); + + __atomic_load(ptr, &value, 0); +} + +/* + * XXX: It's strange that a function can be annotated with VARARGS even + * though it does not take varargs at all. + * + * This feature is not useful for modern code anyway, it focused on pre-C90 + * code that did not have function prototypes. + */ + +/* VARARGS */ +void +varargs_comment(const char *fmt) +{ +} + +/* VARARGS 0 */ +void +varargs_0_comment(const char *fmt) +{ +} + +/* VARARGS 3 */ +void +varargs_3_comment(int a, int b, int c, const char *fmt) +{ +} + +/* PRINTFLIKE */ +void +printflike_comment(const char *fmt) +{ +} + +/* PRINTFLIKE 0 */ +void +printflike_0_comment(const char *fmt) +{ +} + +/* PRINTFLIKE 3 */ +void +printflike_3_comment(int a, int b, const char *fmt) +{ +} + +/* PRINTFLIKE 10 */ +void +printflike_10_comment(int a1, int a2, int a3, int a4, int a5, + int a6, int a7, int a8, int a9, + const char *fmt) +{ +} + +/* SCANFLIKE */ +void +scanflike_comment(const char *fmt) +{ +} + +/* SCANFLIKE 0 */ +void +scanflike_0_comment(const char *fmt) +{ +} + +/* SCANFLIKE 3 */ +void +scanflike_3_comment(int a, int b, const char *fmt) +{ +} + +int +used_function(void) +{ + return 4; +} + +inline int +inline_function(void) +{ + used_function(); + (void)used_function(); + return used_function(); +} diff --git a/usr.bin/xlint/lint1/emit.exp b/usr.bin/xlint/lint1/emit.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/emit.exp @@ -0,0 +1,3 @@ +emit.c(107): warning: static variable static_int unused [226] +emit.c(127): warning: static function static_return_void_no_parameters declared but not defined [290] +emit.c(146): warning: static function static_function declared but not defined [290] diff --git a/usr.bin/xlint/lint1/emit.exp-ln b/usr.bin/xlint/lint1/emit.exp-ln new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/emit.exp-ln @@ -0,0 +1,74 @@ +0semit.c +Semit.c +47d0.47e12extern__BoolB +48d0.48e21extern__Complex_floatsX +49d0.49e22extern__Complex_doubleX +50d0.50e27extern__Complex_long_doublelX +51d0.51e11extern_charC +52d0.52e18extern_signed_charsC +53d0.53e20extern_unsigned_charuC +54d0.54e12extern_shortS +55d0.55e19extern_signed_shortS +56d0.56e21extern_unsigned_shortuS +57d0.57e10extern_intI +58d0.58e17extern_signed_intI +59d0.59e19extern_unsigned_intuI +60d0.60e11extern_longL +61d0.61e18extern_signed_longL +62d0.62e20extern_unsigned_longuL +63d0.63e16extern_long_longQ +64d0.64e23extern_signed_long_longQ +65d0.65e25extern_unsigned_long_longuQ +66d0.66e12extern_floatsD +67d0.67e13extern_doubleD +68d0.68e18extern_long_doublelD +74d0.74e22extern_pointer_to_voidPV +75d0.75e21extern_array_5_of_intA5I +82d0.82e17extern_struct_tagsT110struct_tag +83d0.83e21extern_struct_typedefsT214struct_typedef +84d0.84e16extern_union_taguT19union_tag +85d0.85e20extern_union_typedefuT213union_typedef +86d0.86e15extern_enum_tageT18enum_tag +87d0.87e19extern_enum_typedefeT212enum_typedef +91d0.91e23extern_anonymous_structsT389.0.0 +94d0.94e22extern_anonymous_unionuT392.0.0 +97d0.97e21extern_anonymous_enumeT395.0.0 +105d0.105e12declared_intI +106d0.106t11defined_intI +113d0.113e16extern_const_intcI +114d0.114e19extern_volatile_intvI +115d0.115e25extern_const_volatile_intcvI +121d0.121e30return_void_unknown_parametersFV +122d0.122e38return_implicit_int_unknown_parametersFI +125d0.125e32extern_return_void_no_parametersF0V +126d0.126e25return_void_no_parametersF0V +127d0.127es32static_return_void_no_parametersF0V +129d0.129e10taking_intF1IV +131d0.131e16taking_const_intF1cIV +132d0.132e22taking_int_double_boolF3IDBV +134d0.133e29taking_struct_union_enum_tagsF3sT110struct_taguT19union_tageT18enum_tagV +136d0.135e33taking_struct_union_enum_typedefsF3sT214struct_typedefuT213union_typedefeT212enum_typedefV +138d0.138e14taking_varargsF2PcCEV +146d0.146es15static_functionF0I +148d0.148e9my_printfF2PcCEV +161c0.161s2"%"i9my_printff2PcCPCV +162c0.162s2"%s"i9my_printff2PcCPCV +163c0.163s2"%%"i9my_printff2PcCPCV +164c0.164s2"%\a%\b%\f%\n%\r%\t%\v%\177"i9my_printff2PcCPCV +159d0.159d14cover_outqcharF0V +173d0.173d17call_gcc_builtinsF2IPLV +193d0.193v0d15varargs_commentF1PcCV +199d0.199v0d17varargs_0_commentF1PcCV +205d0.205v3d17varargs_3_commentF4IIIPcCV +211d0.211d18printflike_commentF1PcCV +217d0.217d20printflike_0_commentF1PcCV +223d0.223v3P3d20printflike_3_commentF3IIPcCV +229d0.229v10P10d21printflike_10_commentF10IIIIIIIIIPcCV +237d0.237d17scanflike_commentF1PcCV +243d0.243d19scanflike_0_commentF1PcCV +249d0.249v3S3d19scanflike_3_commentF3IIPcCV +254d0.254dr13used_functionF0I +262c0.262i13used_functionf0I +263c0.263d13used_functionf0I +264c0.264u13used_functionf0I +260d0.260dri15inline_functionF0I diff --git a/usr.bin/xlint/lint1/emit_lp64.c b/usr.bin/xlint/lint1/emit_lp64.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/emit_lp64.c @@ -0,0 +1,19 @@ +/* $NetBSD: emit_lp64.c,v 1.1 2021/08/05 06:34:43 rillig Exp $ */ +# 3 "emit_lp64.c" + +/* + * Test the symbol information that lint1 writes to a .ln file. Using this + * symbol information, lint2 later checks that the symbols are used + * consistently across different translation units. + * + * This test covers large integer types that are only supported on LP64 + * platforms. + */ + +// omit the option '-g' to avoid having the GCC builtins in the .ln file. +/* lint1-flags: -Sw */ + +/* lint1-only-if: lp64 */ + +__int128_t int128(__int128_t); +__uint128_t uint128(__uint128_t); diff --git a/usr.bin/xlint/lint1/emit_lp64.exp-ln b/usr.bin/xlint/lint1/emit_lp64.exp-ln new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/emit_lp64.exp-ln @@ -0,0 +1,4 @@ +0semit_lp64.c +Semit_lp64.c +18d0.18e6int128F1JJ +19d0.19e7uint128F1uJuJ diff --git a/usr.bin/xlint/lint1/expr_binary.c b/usr.bin/xlint/lint1/expr_binary.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/expr_binary.c @@ -0,0 +1,129 @@ +/* $NetBSD: expr_binary.c,v 1.5 2021/08/01 14:51:41 rillig Exp $ */ +# 3 "expr_binary.c" + +/* + * Test binary operators. + */ + +/* lint1-only-if: lp64 */ + +struct incompatible { /* just to generate the error message */ + int member; +}; +void sink(struct incompatible); + +/* + * Test the usual arithmetic conversions. + * + * C99 6.3.1.8 "Usual arithmetic conversions" + */ +void +cover_balance(void) +{ + /* expect+1: 'pointer to void' */ + sink((void *)0 + 0); + + /* expect+1: 'pointer to void' */ + sink(0 + (void *)0); + + /* expect+1: 'int' */ + sink(1 + 1); + + /* expect+1: 'const int' */ + sink((const int)1 + (volatile int)1); + + /* expect+1: 'volatile int' */ + sink((volatile int)1 + (const int)1); + + long double _Complex cldbl = 0.0; + double _Complex cdbl = 0.0; + float _Complex cflt = 0.0f; + /* expect+1: error: invalid type for _Complex [308] */ + _Complex invalid = 0.0; + + /* expect+1: 'long double _Complex' */ + sink(cldbl + 0); + /* expect+1: 'long double _Complex' */ + sink(0 + cldbl); + /* expect+1: 'long double _Complex' */ + sink(cldbl + cdbl); + /* expect+1: 'long double _Complex' */ + sink(cdbl + cldbl); + + /* expect+1: 'double _Complex' */ + sink(cdbl + 0); + /* expect+1: 'double _Complex' */ + sink(0 + cdbl); + /* expect+1: 'double _Complex' */ + sink(cdbl + cflt); + /* expect+1: 'double _Complex' */ + sink(cflt + cdbl); + + /* expect+1: 'float _Complex' */ + sink(cflt + 0); + /* expect+1: 'float _Complex' */ + sink(0 + cflt); + /* expect+1: 'float _Complex' */ + sink(cflt + (__uint128_t)0); + /* expect+1: 'float _Complex' */ + sink((__uint128_t)0 + cflt); + + /* + * The type specifier '_Complex' is only used during parsing, it does + * not make it to the expression. + */ + /* expect+1: 'double _Complex' */ + sink(invalid + 0); + + /* expect+1: 'long double' */ + sink(0.0L + 0); + /* expect+1: 'long double' */ + sink(0 + 0.0L); + /* expect+1: 'long double' */ + sink(0.0L + 0.0); + /* expect+1: 'long double' */ + sink(0.0 + 0.0L); + + /* expect+1: 'double' */ + sink(0.0 + 0); + /* expect+1: 'double' */ + sink(0 + 0.0); + /* expect+1: 'double' */ + sink(0.0 + 0.0f); + /* expect+1: 'double' */ + sink(0.0f + 0.0); + + /* expect+1: 'float' */ + sink(0.0f + 0); + /* expect+1: 'float' */ + sink(0 + 0.0f); + /* expect+1: 'float' */ + sink(0.0f + (__uint128_t)0); + /* expect+1: 'float' */ + sink((__uint128_t)0 + 0.0f); + + /* expect+1: 'unsigned long long' */ + sink(0ULL + 0); + /* expect+1: 'unsigned long long' */ + sink(0 + 0ULL); + + /* expect+1: 'unsigned long long' */ + sink(0ULL + 0LL); + /* expect+1: 'unsigned long long' */ + sink(0LL + 0ULL); + + /* If the bit-width is the same, prefer the unsigned variant. */ + /* expect+1: 'unsigned long long' */ + sink(0UL + 0LL); + /* expect+1: 'unsigned long long' */ + sink(0LL + 0UL); + + /* + * Ensure that __int128_t is listed in the integer ranks. This table + * only becomes relevant when both operands have the same width. + */ + /* expect+1: '__uint128_t' */ + sink((__uint128_t)1 + (__int128_t)1); + /* expect+1: '__uint128_t' */ + sink((__int128_t)1 + (__uint128_t)1); +} diff --git a/usr.bin/xlint/lint1/expr_binary.exp b/usr.bin/xlint/lint1/expr_binary.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/expr_binary.exp @@ -0,0 +1,39 @@ +expr_binary.c(24): warning: passing 'pointer to void' to incompatible 'struct incompatible', arg #1 [155] +expr_binary.c(27): warning: passing 'pointer to void' to incompatible 'struct incompatible', arg #1 [155] +expr_binary.c(30): warning: passing 'int' to incompatible 'struct incompatible', arg #1 [155] +expr_binary.c(33): warning: passing 'const int' to incompatible 'struct incompatible', arg #1 [155] +expr_binary.c(36): warning: passing 'volatile int' to incompatible 'struct incompatible', arg #1 [155] +expr_binary.c(42): error: invalid type for _Complex [308] +expr_binary.c(45): warning: passing 'long double _Complex' to incompatible 'struct incompatible', arg #1 [155] +expr_binary.c(47): warning: passing 'long double _Complex' to incompatible 'struct incompatible', arg #1 [155] +expr_binary.c(49): warning: passing 'long double _Complex' to incompatible 'struct incompatible', arg #1 [155] +expr_binary.c(51): warning: passing 'long double _Complex' to incompatible 'struct incompatible', arg #1 [155] +expr_binary.c(54): warning: passing 'double _Complex' to incompatible 'struct incompatible', arg #1 [155] +expr_binary.c(56): warning: passing 'double _Complex' to incompatible 'struct incompatible', arg #1 [155] +expr_binary.c(58): warning: passing 'double _Complex' to incompatible 'struct incompatible', arg #1 [155] +expr_binary.c(60): warning: passing 'double _Complex' to incompatible 'struct incompatible', arg #1 [155] +expr_binary.c(63): warning: passing 'float _Complex' to incompatible 'struct incompatible', arg #1 [155] +expr_binary.c(65): warning: passing 'float _Complex' to incompatible 'struct incompatible', arg #1 [155] +expr_binary.c(67): warning: passing 'float _Complex' to incompatible 'struct incompatible', arg #1 [155] +expr_binary.c(69): warning: passing 'float _Complex' to incompatible 'struct incompatible', arg #1 [155] +expr_binary.c(76): warning: passing 'double _Complex' to incompatible 'struct incompatible', arg #1 [155] +expr_binary.c(79): warning: passing 'long double' to incompatible 'struct incompatible', arg #1 [155] +expr_binary.c(81): warning: passing 'long double' to incompatible 'struct incompatible', arg #1 [155] +expr_binary.c(83): warning: passing 'long double' to incompatible 'struct incompatible', arg #1 [155] +expr_binary.c(85): warning: passing 'long double' to incompatible 'struct incompatible', arg #1 [155] +expr_binary.c(88): warning: passing 'double' to incompatible 'struct incompatible', arg #1 [155] +expr_binary.c(90): warning: passing 'double' to incompatible 'struct incompatible', arg #1 [155] +expr_binary.c(92): warning: passing 'double' to incompatible 'struct incompatible', arg #1 [155] +expr_binary.c(94): warning: passing 'double' to incompatible 'struct incompatible', arg #1 [155] +expr_binary.c(97): warning: passing 'float' to incompatible 'struct incompatible', arg #1 [155] +expr_binary.c(99): warning: passing 'float' to incompatible 'struct incompatible', arg #1 [155] +expr_binary.c(101): warning: passing 'float' to incompatible 'struct incompatible', arg #1 [155] +expr_binary.c(103): warning: passing 'float' to incompatible 'struct incompatible', arg #1 [155] +expr_binary.c(106): warning: passing 'unsigned long long' to incompatible 'struct incompatible', arg #1 [155] +expr_binary.c(108): warning: passing 'unsigned long long' to incompatible 'struct incompatible', arg #1 [155] +expr_binary.c(111): warning: passing 'unsigned long long' to incompatible 'struct incompatible', arg #1 [155] +expr_binary.c(113): warning: passing 'unsigned long long' to incompatible 'struct incompatible', arg #1 [155] +expr_binary.c(117): warning: passing 'unsigned long long' to incompatible 'struct incompatible', arg #1 [155] +expr_binary.c(119): warning: passing 'unsigned long long' to incompatible 'struct incompatible', arg #1 [155] +expr_binary.c(126): warning: passing '__uint128_t' to incompatible 'struct incompatible', arg #1 [155] +expr_binary.c(128): warning: passing '__uint128_t' to incompatible 'struct incompatible', arg #1 [155] diff --git a/usr.bin/xlint/lint1/expr_binary_trad.c b/usr.bin/xlint/lint1/expr_binary_trad.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/expr_binary_trad.c @@ -0,0 +1,67 @@ +/* $NetBSD: expr_binary_trad.c,v 1.1 2021/08/01 16:29:28 rillig Exp $ */ +# 3 "expr_binary_trad.c" + +/* + * Test binary operators in traditional C. + */ + +/* lint1-flags: -tw */ + +struct incompatible { /* just to generate the error message */ + int member; +}; +struct incompatible sink; + +/* + * Test the usual arithmetic conversions. + * + * C99 6.3.1.8 "Usual arithmetic conversions" + */ +void +cover_balance() +{ + + /* expect+1: 'pointer to char' */ + sink = (char *)0 + 0; + + /* expect+1: 'pointer to char' */ + sink = 0 + (char *)0; + + /* expect+1: 'int' */ + sink = 1 + 1; + + /* expect+1: 'double' */ + sink = 0.0 + 0; + /* expect+1: 'double' */ + sink = 0 + 0.0; + /* expect+1: 'double' */ + sink = 0.0 + (float)0.0; + /* expect+1: 'double' */ + sink = (float)0.0 + 0.0; + + /* + * In traditional C, 'float' gets promoted to 'double' before + * applying the usual arithmetic conversions; see 'promote'. + */ + /* expect+1: 'double' */ + sink = (float)0.0 + 0; + /* expect+1: 'double' */ + sink = 0 + (float)0.0; + + /* expect+1: 'unsigned long' */ + sink = (unsigned long)0 + 0; + /* expect+1: 'unsigned long' */ + sink = 0 + (unsigned long)0; + + /* expect+1: 'unsigned long' */ + sink = (unsigned long)0 + (long)0; + /* expect+1: 'unsigned long' */ + sink = (long)0 + (unsigned long)0; + + /* + * In traditional C, if one of the operands is unsigned, the result + * is unsigned as well. + */ + /* expect+1: 'unsigned long' */ + sink = (unsigned)0 + (long)0; +} diff --git a/usr.bin/xlint/lint1/expr_binary_trad.exp b/usr.bin/xlint/lint1/expr_binary_trad.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/expr_binary_trad.exp @@ -0,0 +1,14 @@ +expr_binary_trad.c(25): error: cannot assign to 'struct incompatible' from 'pointer to char' [171] +expr_binary_trad.c(28): error: cannot assign to 'struct incompatible' from 'pointer to char' [171] +expr_binary_trad.c(31): error: cannot assign to 'struct incompatible' from 'int' [171] +expr_binary_trad.c(34): error: cannot assign to 'struct incompatible' from 'double' [171] +expr_binary_trad.c(36): error: cannot assign to 'struct incompatible' from 'double' [171] +expr_binary_trad.c(38): error: cannot assign to 'struct incompatible' from 'double' [171] +expr_binary_trad.c(40): error: cannot assign to 'struct incompatible' from 'double' [171] +expr_binary_trad.c(47): error: cannot assign to 'struct incompatible' from 'double' [171] +expr_binary_trad.c(49): error: cannot assign to 'struct incompatible' from 'double' [171] +expr_binary_trad.c(52): error: cannot assign to 'struct incompatible' from 'unsigned long' [171] +expr_binary_trad.c(54): error: cannot assign to 'struct incompatible' from 'unsigned long' [171] +expr_binary_trad.c(57): error: cannot assign to 'struct incompatible' from 'unsigned long' [171] +expr_binary_trad.c(59): error: cannot assign to 'struct incompatible' from 'unsigned long' [171] +expr_binary_trad.c(66): error: cannot assign to 'struct incompatible' from 'unsigned long' [171] diff --git a/usr.bin/xlint/lint1/expr_cast.c b/usr.bin/xlint/lint1/expr_cast.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/expr_cast.c @@ -0,0 +1,33 @@ +/* $NetBSD: expr_cast.c,v 1.3 2021/08/03 18:44:33 rillig Exp $ */ +# 3 "expr_cast.c" + +/* + * Tests for value conversion using a cast-expression. + * + * K&R C does not mention any restrictions on the target type. + * C90 requires both the source type and the target type to be scalar. + * + * GCC allows casting to a struct type but there is no documentation about + * it at https://gcc.gnu.org/onlinedocs/gcc/C-Extensions.html. See + * c-typeck.c, function build_c_cast, RECORD_OR_UNION_TYPE_P. + */ + +/* lint1-flags: -Sw */ + +struct S { + int member; +}; + +struct S +cast(void) +{ + struct S { + double incompatible; + } local = { + 0.0 + }; + + /* expect+2: error: invalid cast from 'struct S' to 'struct S' [147] */ + /* expect+1: warning: function 'cast' expects to return value [214] */ + return (struct S)local; +} diff --git a/usr.bin/xlint/lint1/expr_cast.exp b/usr.bin/xlint/lint1/expr_cast.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/expr_cast.exp @@ -0,0 +1,2 @@ +expr_cast.c(32): error: invalid cast from 'struct S' to 'struct S' [147] +expr_cast.c(32): warning: function 'cast' expects to return value [214] diff --git a/usr.bin/xlint/lint1/expr_fold.c b/usr.bin/xlint/lint1/expr_fold.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/expr_fold.c @@ -0,0 +1,310 @@ +/* $NetBSD: expr_fold.c,v 1.5 2021/08/23 06:50:01 rillig Exp $ */ +# 3 "expr_fold.c" + +/* + * Test folding of constant expressions. + */ + +/* lint1-extra-flags: -h */ + +/* + * On ILP32 platforms, the integer constant 2147483648 cannot be represented + * as 'int' or 'long', therefore it becomes 'long long'. This would + * influence the type names in the diagnostics. + */ +/* lint1-only-if: lp64 */ + +void take_bool(_Bool); +void take_int(int); +void take_uint(unsigned int); + +/* + * C99 6.4.4.1p5 defines that decimal integer constants without suffix get + * one of the signed integer types. On the other hand, octal and hexadecimal + * constants get either a signed or unsigned type, whichever fits first. + */ + +void +fold_uplus(void) +{ + take_int(+(0)); + take_int(+(2147483647)); + /* XXX: one of these two messages is redundant */ + /* expect+2: warning: argument #1 is converted from 'long' to 'int' due to prototype [259] */ + /* expect+1: warning: conversion of 'long' to 'int' is out of range, arg #1 [295] */ + take_int(+(2147483648)); + /* XXX: one of these two messages is redundant */ + /* expect+2: warning: argument #1 is converted from 'long' to 'int' due to prototype [259] */ + /* expect+1: warning: conversion of 'long' to 'int' is out of range, arg #1 [295] */ + take_int(+(4294967295)); + + take_uint(+(0)); + take_uint(+(2147483647)); + /* expect+1: warning: argument #1 is converted from 'long' to 'unsigned int' due to prototype [259] */ + take_uint(+(2147483648)); + /* expect+1: warning: argument #1 is converted from 'long' to 'unsigned int' due to prototype [259] */ + take_uint(+(4294967295)); + + /* + * Hexadecimal constants and constants with the suffix 'U' get either + * a signed or an unsigned integer type, so no warning here. + */ + take_uint(+(2147483648U)); + take_uint(+(0x80000000)); + take_uint(+(0x80000000U)); +} + +void +fold_uminus(void) +{ + take_int(-(0)); + take_int(-(2147483647)); + + /* expect+1: warning: argument #1 is converted from 'long' to 'int' due to prototype [259] */ + take_int(-(2147483648)); + + /* The '-' is an operator, it is not part of the integer constant. */ + /* expect+1: warning: argument #1 is converted from 'long' to 'int' due to prototype [259] */ + take_int(-2147483648); + + /* expect+2: warning: integer overflow detected, op '+' [141] */ + /* expect+1: warning: integer overflow detected, op '-' [141] */ + take_int(-(2147483647 + 1)); + /* expect+1: warning: integer overflow detected, op '-' [141] */ + take_int(-(-2147483647 - 1)); + /* expect+2: warning: argument #1 is converted from 'long' to 'int' due to prototype [259] */ + /* expect+1: warning: conversion of 'long' to 'int' is out of range, arg #1 [295] */ + take_int(-(4294967295)); + + take_uint(-(0)); + /* expect+2: warning: conversion of negative constant to unsigned type, arg #1 [296] */ + /* expect+1: warning: argument #1 is converted from 'int' to 'unsigned int' due to prototype [259] */ + take_uint(-(2147483647)); + /* expect+2: warning: argument #1 is converted from 'long' to 'unsigned int' due to prototype [259] */ + /* expect+1: warning: conversion of negative constant to unsigned type, arg #1 [296] */ + take_uint(-(2147483648)); + /* expect+2: warning: argument #1 is converted from 'long' to 'unsigned int' due to prototype [259] */ + /* expect+1: warning: conversion of negative constant to unsigned type, arg #1 [296] */ + take_uint(-(4294967295)); +} + +void +fold_compl(void) +{ + take_int(~(0)); + take_int(~(2147483647)); + /* expect+2: warning: argument #1 is converted from 'long' to 'int' due to prototype [259] */ + /* expect+1: warning: conversion of 'long' to 'int' is out of range, arg #1 [295] */ + take_int(~(2147483648)); + /* expect+2: warning: argument #1 is converted from 'long' to 'int' due to prototype [259] */ + /* expect+1: warning: conversion of 'long' to 'int' is out of range, arg #1 [295] */ + take_int(~(4294967295)); + + /* expect+2: warning: conversion of negative constant to unsigned type, arg #1 [296] */ + /* expect+1: warning: argument #1 is converted from 'int' to 'unsigned int' due to prototype [259] */ + take_uint(~(0)); + /* expect+2: warning: argument #1 is converted from 'int' to 'unsigned int' due to prototype [259] */ + /* expect+1: warning: conversion of negative constant to unsigned type, arg #1 [296] */ + take_uint(~(2147483647)); + /* expect+2: warning: argument #1 is converted from 'long' to 'unsigned int' due to prototype [259] */ + /* expect+1: warning: conversion of negative constant to unsigned type, arg #1 [296] */ + take_uint(~(2147483648)); + /* expect+2: warning: argument #1 is converted from 'long' to 'unsigned int' due to prototype [259] */ + /* expect+1: warning: conversion of negative constant to unsigned type, arg #1 [296] */ + take_uint(~(4294967295)); +} + +void +fold_mult(void) +{ + take_int(32767 * 65536); + /* expect+1: warning: integer overflow detected, op '*' [141] */ + take_int(32768 * 65536); + /* expect+1: warning: integer overflow detected, op '*' [141] */ + take_int(65536 * 65536); + + take_uint(32767 * 65536U); + take_uint(32768 * 65536U); + /* expect+1: warning: integer overflow detected, op '*' [141] */ + take_uint(65536 * 65536U); +} + +void +fold_div(void) +{ + /* expect+3: error: division by 0 [139] */ + /* XXX: The following message is redundant. */ + /* expect+1: warning: integer overflow detected, op '/' [141] */ + take_int(0 / 0); + + /* expect+2: warning: argument #1 is converted from 'long' to 'int' due to prototype [259] */ + /* expect+1: warning: conversion of 'long' to 'int' is out of range, arg #1 [295] */ + take_int(-2147483648 / -1); +} + +void +fold_mod(void) +{ + /* expect+1: error: modulus by 0 [140] */ + take_int(0 % 0); + /* expect+1: error: modulus by 0 [140] */ + take_int(0 % 0U); + /* expect+1: error: modulus by 0 [140] */ + take_int(0U % 0); + /* expect+1: error: modulus by 0 [140] */ + take_int(0U % 0U); + + /* expect+1: warning: argument #1 is converted from 'long' to 'int' due to prototype [259] */ + take_int(-2147483648 % -1); +} + +void +fold_plus(void) +{ + /* expect+1: warning: integer overflow detected, op '+' [141] */ + take_int(2147483647 + 1); + + /* Assume two's complement, so no overflow. */ + take_int(-2147483647 + -1); + + /* expect+1: warning: integer overflow detected, op '+' [141] */ + take_int(-2147483647 + -2); + + /* + * No overflow since one of the operands is unsigned, therefore the + * other operand is converted to unsigned as well. + * See C99 6.3.1.8p1, paragraph 8 of 10. + */ + /* wrong integer overflow warning before tree.c 1.338 from 2021-08-19 */ + take_uint(2147483647 + 1U); + /* wrong integer overflow warning before tree.c 1.338 from 2021-08-19 */ + take_uint(2147483647U + 1); +} + +void +fold_minus(void) +{ + /* expect+1: warning: integer overflow detected, op '-' [141] */ + take_int(2147483647 - -1); + /* Assume two's complement. */ + take_int(-2147483647 - 1); + /* expect+1: warning: integer overflow detected, op '-' [141] */ + take_int(-2147483647 - 2); + + /* expect+1: warning: argument #1 is converted from 'long' to 'int' due to prototype [259] */ + take_int(0 - 2147483648); + /* expect+1: warning: integer overflow detected, op '-' [141] */ + take_uint(0 - 2147483648U); +} + +void +fold_shl(void) +{ + /* expect+1: warning: integer overflow detected, op '<<' [141] */ + take_int(1 << 24 << 24); + + /* expect+1: warning: integer overflow detected, op '<<' [141] */ + take_uint(1U << 24 << 24); + + /* FIXME: undefined behavior in 'fold' at 'uint64_t << 104'. */ + /* expect+1: warning: shift amount 104 is greater than bit-size 32 of 'unsigned int' [122] */ + take_uint(1U << 24 << 104); +} + +void +fold_shr(void) +{ + take_int(16777216 >> 24); + + take_int(16777216 >> 25); + + /* FIXME: undefined behavior in 'fold' at 'uint64_t >> 104'. */ + /* expect+1: warning: shift amount 104 is greater than bit-size 32 of 'int' [122] */ + take_int(16777216 >> 104); +} + +void +fold_lt(void) +{ + take_bool(1 < 3); + take_bool(3 < 1); +} + +void +fold_le(void) +{ + take_bool(1 <= 3); + take_bool(3 <= 1); +} + +void +fold_ge(void) +{ + take_bool(1 >= 3); + take_bool(3 >= 1); +} + +void +fold_gt(void) +{ + take_bool(1 > 3); + take_bool(3 > 1); +} + +void +fold_eq(void) +{ + take_bool(1 == 3); + take_bool(3 == 1); +} + +void +fold_ne(void) +{ + take_bool(1 != 3); + take_bool(3 != 1); +} + +void +fold_bitand(void) +{ + take_bool(1 & 3); + take_bool(3 & 1); +} + +void +fold_bitxor(void) +{ + take_bool(1 ^ 3); + take_bool(3 ^ 1); +} + +void +fold_bitor(void) +{ + take_bool(1 | 3); + take_bool(3 | 1); +} + +/* + * The following expression originated in vndcompress.c 1.29 from 2017-07-29, + * where line 310 contained a seemingly harmless compile-time assertion that + * expanded to a real monster expression. + * + * __CTASSERT(MUL_OK(uint64_t, MAX_N_BLOCKS, MAX_BLOCKSIZE)); + * + * Before tree.c 1.345 from 2021-08-22, lint wrongly assumed that the result + * of all binary operators were the common arithmetic type, but that was + * wrong for the comparison operators. The expression '1ULL < 2ULL' does not + * have type 'unsigned long long' but 'int' in default mode, or '_Bool' in + * strict bool mode. + */ +struct ctassert5_struct { + unsigned int member: + /*CONSTCOND*/ + 0xfffffffeU + <= + ((1ULL << 63) + 1 < 1 ? ~(1ULL << 63) : ~0ULL) / 0xfffffe00U + ? 1 + : -1; +}; diff --git a/usr.bin/xlint/lint1/expr_fold.exp b/usr.bin/xlint/lint1/expr_fold.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/expr_fold.exp @@ -0,0 +1,53 @@ +expr_fold.c(35): warning: argument #1 is converted from 'long' to 'int' due to prototype [259] +expr_fold.c(35): warning: conversion of 'long' to 'int' is out of range, arg #1 [295] +expr_fold.c(39): warning: argument #1 is converted from 'long' to 'int' due to prototype [259] +expr_fold.c(39): warning: conversion of 'long' to 'int' is out of range, arg #1 [295] +expr_fold.c(44): warning: argument #1 is converted from 'long' to 'unsigned int' due to prototype [259] +expr_fold.c(46): warning: argument #1 is converted from 'long' to 'unsigned int' due to prototype [259] +expr_fold.c(64): warning: argument #1 is converted from 'long' to 'int' due to prototype [259] +expr_fold.c(68): warning: argument #1 is converted from 'long' to 'int' due to prototype [259] +expr_fold.c(72): warning: integer overflow detected, op '+' [141] +expr_fold.c(72): warning: integer overflow detected, op '-' [141] +expr_fold.c(74): warning: integer overflow detected, op '-' [141] +expr_fold.c(77): warning: argument #1 is converted from 'long' to 'int' due to prototype [259] +expr_fold.c(77): warning: conversion of 'long' to 'int' is out of range, arg #1 [295] +expr_fold.c(82): warning: argument #1 is converted from 'int' to 'unsigned int' due to prototype [259] +expr_fold.c(82): warning: conversion of negative constant to unsigned type, arg #1 [296] +expr_fold.c(85): warning: argument #1 is converted from 'long' to 'unsigned int' due to prototype [259] +expr_fold.c(85): warning: conversion of negative constant to unsigned type, arg #1 [296] +expr_fold.c(88): warning: argument #1 is converted from 'long' to 'unsigned int' due to prototype [259] +expr_fold.c(88): warning: conversion of negative constant to unsigned type, arg #1 [296] +expr_fold.c(98): warning: argument #1 is converted from 'long' to 'int' due to prototype [259] +expr_fold.c(98): warning: conversion of 'long' to 'int' is out of range, arg #1 [295] +expr_fold.c(101): warning: argument #1 is converted from 'long' to 'int' due to prototype [259] +expr_fold.c(101): warning: conversion of 'long' to 'int' is out of range, arg #1 [295] +expr_fold.c(105): warning: argument #1 is converted from 'int' to 'unsigned int' due to prototype [259] +expr_fold.c(105): warning: conversion of negative constant to unsigned type, arg #1 [296] +expr_fold.c(108): warning: argument #1 is converted from 'int' to 'unsigned int' due to prototype [259] +expr_fold.c(108): warning: conversion of negative constant to unsigned type, arg #1 [296] +expr_fold.c(111): warning: argument #1 is converted from 'long' to 'unsigned int' due to prototype [259] +expr_fold.c(111): warning: conversion of negative constant to unsigned type, arg #1 [296] +expr_fold.c(114): warning: argument #1 is converted from 'long' to 'unsigned int' due to prototype [259] +expr_fold.c(114): warning: conversion of negative constant to unsigned type, arg #1 [296] +expr_fold.c(122): warning: integer overflow detected, op '*' [141] +expr_fold.c(124): warning: integer overflow detected, op '*' [141] +expr_fold.c(129): warning: integer overflow detected, op '*' [141] +expr_fold.c(138): error: division by 0 [139] +expr_fold.c(138): warning: integer overflow detected, op '/' [141] +expr_fold.c(142): warning: argument #1 is converted from 'long' to 'int' due to prototype [259] +expr_fold.c(142): warning: conversion of 'long' to 'int' is out of range, arg #1 [295] +expr_fold.c(149): error: modulus by 0 [140] +expr_fold.c(151): error: modulus by 0 [140] +expr_fold.c(153): error: modulus by 0 [140] +expr_fold.c(155): error: modulus by 0 [140] +expr_fold.c(158): warning: argument #1 is converted from 'long' to 'int' due to prototype [259] +expr_fold.c(165): warning: integer overflow detected, op '+' [141] +expr_fold.c(171): warning: integer overflow detected, op '+' [141] +expr_fold.c(188): warning: integer overflow detected, op '-' [141] +expr_fold.c(192): warning: integer overflow detected, op '-' [141] +expr_fold.c(195): warning: argument #1 is converted from 'long' to 'int' due to prototype [259] +expr_fold.c(197): warning: integer overflow detected, op '-' [141] +expr_fold.c(204): warning: integer overflow detected, op '<<' [141] +expr_fold.c(207): warning: integer overflow detected, op '<<' [141] +expr_fold.c(211): warning: shift amount 104 is greater than bit-size 32 of 'unsigned int' [122] +expr_fold.c(223): warning: shift amount 104 is greater than bit-size 32 of 'int' [122] diff --git a/usr.bin/xlint/lint1/expr_fold_strict_bool.c b/usr.bin/xlint/lint1/expr_fold_strict_bool.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/expr_fold_strict_bool.c @@ -0,0 +1,41 @@ +/* $NetBSD: expr_fold_strict_bool.c,v 1.2 2021/08/22 21:17:04 rillig Exp $ */ +# 3 "expr_fold_strict_bool.c" + +/* + * Test constant folding in strict bool mode. + * + * In this mode, _Bool is not an unsigned integer type. In fact, it is not + * an arithmetic type at all. + */ + +/* lint1-extra-flags: -T */ +/* lint1-only-if: lp64 */ + +typedef long long int64_t; +typedef unsigned long long uint64_t; + +struct fold_64_bit { + + _Bool lt_signed_small_ok: -3LL < 1LL ? 1 : -1; + /* expect+1: error: illegal bit-field size: 255 [36] */ + _Bool lt_signed_small_bad: 1LL < -3LL ? 1 : -1; + + _Bool lt_signed_big_ok: (int64_t)(1ULL << 63) < 1LL ? 1 : -1; + /* expect+1: error: illegal bit-field size: 255 [36] */ + _Bool lt_signed_big_bad: 1LL < (int64_t)(1ULL << 63) ? 1 : -1; + + _Bool lt_unsigned_small_ok: 1ULL < 3ULL ? 1 : -1; + /* expect+1: error: illegal bit-field size: 255 [36] */ + _Bool lt_unsigned_small_bad: 3ULL < 1ULL ? 1 : -1; + + /* + * Before tree.c 1.345 from 2021-08-22, lint wrongly assumed that the + * result of all binary operators were the common arithmetic type, + * but that was wrong for the comparison operators. The expression + * '1ULL < 2ULL' does not have type 'unsigned long long' but 'int' in + * default mode, or '_Bool' in strict bool mode. + */ + _Bool lt_unsigned_big_ok: 1ULL < 1ULL << 63 ? 1 : -1; + /* expect+1: error: illegal bit-field size: 255 [36] */ + _Bool lt_unsigned_big_bad: 1ULL << 63 < 1ULL ? 1 : -1; +}; diff --git a/usr.bin/xlint/lint1/expr_fold_strict_bool.exp b/usr.bin/xlint/lint1/expr_fold_strict_bool.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/expr_fold_strict_bool.exp @@ -0,0 +1,4 @@ +expr_fold_strict_bool.c(21): error: illegal bit-field size: 255 [36] +expr_fold_strict_bool.c(25): error: illegal bit-field size: 255 [36] +expr_fold_strict_bool.c(29): error: illegal bit-field size: 255 [36] +expr_fold_strict_bool.c(40): error: illegal bit-field size: 255 [36] diff --git a/usr.bin/xlint/lint1/expr_precedence.c b/usr.bin/xlint/lint1/expr_precedence.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/expr_precedence.c @@ -0,0 +1,76 @@ +/* $NetBSD: expr_precedence.c,v 1.7 2021/07/26 18:10:14 rillig Exp $ */ +# 3 "expr_precedence.c" + +/* + * Tests for the precedence among operators. + */ + +int var; + +/* + * An initializer needs an assignment-expression; the comma must be + * interpreted as a separator, not an operator. + */ +/* expect+1: error: syntax error '4' [249] */ +int init_error = 3, 4; + +/* expect+1: error: non-constant initializer [177] */ +int init_syntactically_ok = var = 1 ? 2 : 3; + +/* + * The arguments of __attribute__ must be constant-expression, as assignments + * don't make sense at that point. + */ +void __attribute__((format(printf, + /* + * Inside of __attribute__((...)), symbol lookup works differently. For + * example, 'printf' is a keyword, and since all arguments to + * __attribute__ are constant expressions, looking up global variables + * would not make sense. Therefore, 'var' is undefined. + * + * See lex.c, function 'search', keyword 'attron'. + */ + /* expect+2: error: 'var' undefined [99] */ + /* expect+1: syntax error '=' [249] */ + var = 1, + /* Syntactically ok, must be a constant expression though. */ + var > 0 ? 2 : 1))) +my_printf(const char *, ...); + +void +assignment_associativity(int arg) +{ + int left, right; + + /* + * Assignments are right-associative. If they were left-associative, + * the result of (left = right) would be an rvalue, resulting in this + * error message: 'left operand of '=' must be lvalue [114]'. + */ + left = right = arg; + + left = arg; +} + +void +conditional_associativity(_Bool cond1, _Bool cond2, int a, int b, int c) +{ + /* The then-expression can be an arbitrary expression. */ + var = cond1 ? cond2 ? a : b : c; + var = cond1 ? (cond2 ? a : b) : c; + + /* The then-expression can even be a comma-expression. */ + var = cond1 ? cond2 ? a, b : (b, a) : c; + + var = cond1 ? a : cond2 ? b : c; + /* + * In almost all programming languages, '?:' is right-associative, + * which allows for easy chaining. + */ + var = cond1 ? a : (cond2 ? b : c); + /* + * In PHP, '?:' is left-associative, which is rather surprising and + * requires more parentheses to get the desired effect. + */ + var = (cond1 ? a : cond2) ? b : c; +} diff --git a/usr.bin/xlint/lint1/expr_precedence.exp b/usr.bin/xlint/lint1/expr_precedence.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/expr_precedence.exp @@ -0,0 +1,4 @@ +expr_precedence.c(15): error: syntax error '4' [249] +expr_precedence.c(18): error: non-constant initializer [177] +expr_precedence.c(35): error: 'var' undefined [99] +expr_precedence.c(35): error: syntax error '=' [249] diff --git a/usr.bin/xlint/lint1/expr_promote.c b/usr.bin/xlint/lint1/expr_promote.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/expr_promote.c @@ -0,0 +1,68 @@ +/* $NetBSD: expr_promote.c,v 1.2 2021/08/16 20:27:31 rillig Exp $ */ +# 3 "expr_promote.c" + +/* + * Test arithmetic promotions in C90 and later. + */ + +/* lint1-flags: -Sw */ + +void sink(const char *, ...); + +struct arithmetic_types { + _Bool boolean; + char plain_char; + signed char signed_char; + unsigned char unsigned_char; + short signed_short; + unsigned short unsigned_short; + int signed_int; + unsigned int unsigned_int; + long signed_long; + unsigned long unsigned_long; + long long signed_long_long; + unsigned long long unsigned_long_long; + float float_floating; + double double_floating; + long double long_floating; + float _Complex float_complex; + double _Complex double_complex; + long double _Complex long_double_complex; + enum { + E + } enumerator; +}; + +void +caller(struct arithmetic_types *arg) +{ + sink("", + arg->boolean, /* gets promoted to 'int' */ + arg->plain_char, /* gets promoted to 'int' */ + arg->signed_char, /* gets promoted to 'int' */ + arg->unsigned_char, /* gets promoted to 'int' */ + arg->signed_short, /* gets promoted to 'int' */ + arg->unsigned_short, /* gets promoted to 'int' */ + arg->signed_int, + arg->unsigned_int, + arg->signed_long, + arg->unsigned_long, + arg->signed_long_long, + arg->unsigned_long_long, + arg->float_floating, /* gets promoted to 'double' */ + arg->double_floating, + arg->long_floating, + arg->float_complex, + arg->double_complex, + arg->long_double_complex, + arg->enumerator); +} + +/* + * XXX: _Bool should be promoted to 'int', C99 6.3.1.1p2 "If an int can + * represent ...". + */ +/* + * XXX: Enumerations may need be promoted to 'int', at least C99 6.3.1.1p2 + * suggests that: "If an int can represent ...". + */ diff --git a/usr.bin/xlint/lint1/expr_promote.exp-ln b/usr.bin/xlint/lint1/expr_promote.exp-ln new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/expr_promote.exp-ln @@ -0,0 +1,5 @@ +0sexpr_promote.c +Sexpr_promote.c +10d0.10e4sinkF2PcCEV +58c0.58i4sinkf20PcCBIIIIIIuILuLQuQDDlDsXXlXeT331.0.0V +37d0.37d6callerF1PsT116arithmetic_typesV diff --git a/usr.bin/xlint/lint1/expr_promote_trad.c b/usr.bin/xlint/lint1/expr_promote_trad.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/expr_promote_trad.c @@ -0,0 +1,55 @@ +/* $NetBSD: expr_promote_trad.c,v 1.2 2021/08/16 20:27:31 rillig Exp $ */ +# 3 "expr_promote_trad.c" + +/* + * Test arithmetic promotions in traditional C. + */ + +/* lint1-flags: -tw */ + +sink(); + +struct arithmetic_types { + /* _Bool is not available in traditional C */ + char plain_char; + /* signed char is not available in traditional C */ + unsigned char unsigned_char; + short signed_short; + unsigned short unsigned_short; + int signed_int; + unsigned int unsigned_int; + long signed_long; + unsigned long unsigned_long; + /* (unsigned) long long is not available in traditional C */ + /* __int128_t is not available in traditional C */ + /* __uint128_t is not available in traditional C */ + float single_floating; + double double_floating; + /* long double is not available in traditional C */ + /* _Complex is not available in traditional C */ + enum { + E + } enumerator; +}; + +caller(arg) + struct arithmetic_types *arg; +{ + sink("", + arg->plain_char, /* gets promoted to 'int' */ + arg->unsigned_char, /* gets promoted to 'unsigned int' */ + arg->signed_short, /* gets promoted to 'int' */ + arg->unsigned_short, /* gets promoted to 'unsigned int' */ + arg->signed_int, + arg->unsigned_int, + arg->signed_long, + arg->unsigned_long, + arg->single_floating, /* gets promoted to 'double' */ + arg->double_floating, + arg->enumerator); +} + +/* + * XXX: Enumerations may need be promoted to 'int', at least C99 6.3.1.1p2 + * suggests that: "If an int can represent ...". + */ diff --git a/usr.bin/xlint/lint1/expr_promote_trad.exp-ln b/usr.bin/xlint/lint1/expr_promote_trad.exp-ln new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/expr_promote_trad.exp-ln @@ -0,0 +1,5 @@ +0sexpr_promote_trad.c +Sexpr_promote_trad.c +10d0.10e4sinkFI +49c0.49s1""i4sinkf12PCIuIIuIIuILuLDDeT330.0.0I +35d0.35do6callerf1PsT116arithmetic_typesI diff --git a/usr.bin/xlint/lint1/expr_range.c b/usr.bin/xlint/lint1/expr_range.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/expr_range.c @@ -0,0 +1,43 @@ +/* $NetBSD: expr_range.c,v 1.3 2021/07/05 19:43:29 rillig Exp $ */ +# 3 "expr_range.c" + +/* + * In a switch statement that has (expr & constant) as the controlling + * expression, complain if a case branch is unreachable because the case + * label can never match the controlling expression. + * + * GCC 10 does not complain about the unreachable branch. It knows that the + * branch is unreachable though, since it doesn't generate any code for it. + * GCC once had the option -Wunreachable-code, but that option was made a + * no-op in 2011. + * + * Clang 10 does not complain about this either, and just like GCC it doesn't + * generate any code for this branch. The code for tracking an expression's + * possible values may be related to RangeConstraintManager, just guessing. + */ + +/* lint1-extra-flags: -chap */ + +void println(const char *); + +void +example(unsigned x) +{ + switch (x & 6) { + case 0: + println("0 is reachable"); + break; + case 1: /* expect: statement not reached */ + println("1 is not reachable"); + break; + case 2: + println("2 is reachable"); + break; + case 6: + println("6 is reachable"); + break; + case 7: /* expect: statement not reached */ + println("7 is not reachable"); + break; + } +} diff --git a/usr.bin/xlint/lint1/expr_range.exp b/usr.bin/xlint/lint1/expr_range.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/expr_range.exp @@ -0,0 +1,2 @@ +expr_range.c(30): warning: statement not reached [193] +expr_range.c(39): warning: statement not reached [193] diff --git a/usr.bin/xlint/lint1/feat_stacktrace.c b/usr.bin/xlint/lint1/feat_stacktrace.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/feat_stacktrace.c @@ -0,0 +1,31 @@ +/* $NetBSD: feat_stacktrace.c,v 1.1 2021/04/08 22:18:27 rillig Exp $ */ +# 3 "feat_stacktrace.c" + +/* + * In macros or nested includes, lint prints a stack trace to show exactly + * where the code comes from. + */ + +# 1 "/usr/include/stdlib.h" 1 3 4 +# 38 "/usr/include/stdlib.h" 3 4 +# 39 "/usr/include/stdlib.h" 3 4 +# 1 "/usr/include/sys/types.h" 1 3 4 +# 43 "/usr/include/sys/types.h" 3 4 +# 1 "/usr/include/amd64/types.h" 1 3 4 +# 40 "/usr/include/amd64/types.h" 3 4 +# 1 "/usr/include/sys/featuretest.h" 1 3 4 +# 41 "/usr/include/amd64/types.h" 2 3 4 +# 1 "/usr/include/amd64/int_types.h" 1 3 4 + +/* + * The next filename is a relative filename since the tests are run without + * the lint option -F, which would generate the fully qualified filename for + * the main file as well. + */ +# 1 "common_int_types.h" 1 3 4 +typedef int; /* expect: typedef declares no type name */ +# 39 "common_int_types.h" 3 4 +# 39 "/usr/include/amd64/int_types.h" 2 3 4 +# 42 "/usr/include/amd64/types.h" 2 3 4 +# 68 "/usr/include/amd64/types.h" 3 4 +# 46 "/usr/include/sys/types.h" 2 3 4 diff --git a/usr.bin/xlint/lint1/feat_stacktrace.exp b/usr.bin/xlint/lint1/feat_stacktrace.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/feat_stacktrace.exp @@ -0,0 +1,6 @@ +common_int_types.h(1): warning: typedef declares no type name [72] + included from /usr/include/amd64/int_types.h(7) + included from /usr/include/amd64/types.h(41) + included from /usr/include/sys/types.h(43) + included from /usr/include/stdlib.h(39) + included from feat_stacktrace.c(9) diff --git a/usr.bin/xlint/lint1/gcc_attribute.c b/usr.bin/xlint/lint1/gcc_attribute.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/gcc_attribute.c @@ -0,0 +1,129 @@ +/* $NetBSD: gcc_attribute.c,v 1.10 2021/07/15 21:00:05 rillig Exp $ */ +# 3 "gcc_attribute.c" + +/* + * Tests for the various attributes for functions, types, statements that are + * provided by GCC. + * + * https://gcc.gnu.org/onlinedocs/gcc/Attribute-Syntax.html + */ + +void __attribute__((noinline)) +do_not_inline(void) +{ +} + +/* All pointer arguments must be nonnull. */ +void __attribute__((nonnull)) +function_nonnull(void *, const void *, int); + +/* + * The documentation suggests that the argument list of nonnull be nonempty, + * but GCC 9.3.0 accepts an empty list as well, treating all parameters as + * nonnull. + */ +void __attribute__((nonnull())) +function_nonnull_list(void *, const void *, int); + +/* Arguments 1 and 2 must be nonnull. */ +void __attribute__((nonnull(1, 2))) +function_nonnull_list(void *, const void *, int); + +/* expect+1: syntax error 'unknown_attribute' */ +void __attribute__((unknown_attribute)) +function_with_unknown_attribute(void); + +/* + * There is an attribute called 'pcs', but that attribute must not prevent an + * ordinary variable from being named the same. Starting with scan.l 1.77 + * from 2017-01-07, that variable name generated a syntax error. Fixed in + * lex.c 1.33 from 2021-05-03. + * + * Seen in yds.c, function yds_allocate_slots. + */ +int +local_variable_pcs(void) +{ + int pcs = 3; + return pcs; +} + +/* + * FIXME: The attributes are handled by different grammar rules even though + * they occur in the same syntactical position. + * + * Grammar rule abstract_decl_param_list handles the first attribute. + * + * Grammar rule direct_abstract_declarator handles all remaining attributes. + * + * Since abstract_decl_param_list contains type_attribute_opt, this could be + * the source of the many shift/reduce conflicts in the grammar. + */ +int +func( + int(int) + __attribute__((__noreturn__)) + __attribute__((__noreturn__)) +); + +/* + * https://gcc.gnu.org/onlinedocs/gcc/Attribute-Syntax.html says that the + * attribute-list is a "possibly empty comma-separated sequence of + * attributes". + * + * No matter whether this particular example is interpreted as an empty list + * or a list containing a single empty attribute, the result is the same in + * both cases. + */ +void one_empty_attribute(void) + __attribute__((/* none */)); + +/* + * https://gcc.gnu.org/onlinedocs/gcc/Attribute-Syntax.html further says that + * each individual attribute may be "Empty. Empty attributes are ignored". + */ +void two_empty_attributes(void) + __attribute__((/* none */, /* still none */)); + +/* + * Ensure that __attribute__ can be specified everywhere in a declaration. + * This is the simplest possible requirement that covers all valid code. + * It accepts invalid code as well, but these cases are covered by GCC and + * Clang already. + * + * Since lint only parses the attributes but doesn't really relate them to + * identifiers or other entities, ensuring that valid code can be parsed is + * enough for now. + * + * To really associate __attribute__ with the corresponding entity, the + * grammar needs to be rewritten, see the example with __noreturn__ above. + */ +__attribute__((deprecated("d1"))) +const +__attribute__((deprecated("d2"))) +int +__attribute__((deprecated("d3"))) +* +// The below line would produce a syntax error. +// __attribute__((deprecated("d3"))) +const +__attribute__((deprecated("d4"))) +identifier +__attribute__((deprecated("d5"))) +( + __attribute__((deprecated("d6"))) + void + __attribute__((deprecated("d7"))) + ) + __attribute__((deprecated("d8"))) +; + +/* + * The attribute 'const' provides stronger guarantees than 'pure', and + * 'volatile' is not defined. To keep the grammar simple, any T_QUAL is + * allowed at this point, but only syntactically. + */ +int const_function(int) __attribute__((const)); +/* cover 'gcc_attribute_spec: T_QUAL' */ +/* expect+1: syntax error 'volatile' [249] */ +int volatile_function(int) __attribute__((volatile)); diff --git a/usr.bin/xlint/lint1/gcc_attribute.exp b/usr.bin/xlint/lint1/gcc_attribute.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/gcc_attribute.exp @@ -0,0 +1,2 @@ +gcc_attribute.c(33): error: syntax error 'unknown_attribute' [249] +gcc_attribute.c(129): error: syntax error 'volatile' [249] diff --git a/usr.bin/xlint/lint1/gcc_attribute_aligned.c b/usr.bin/xlint/lint1/gcc_attribute_aligned.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/gcc_attribute_aligned.c @@ -0,0 +1,44 @@ +/* $NetBSD: gcc_attribute_aligned.c,v 1.1 2021/05/02 20:44:46 rillig Exp $ */ +# 3 "gcc_attribute_aligned.c" + +/* + * Test size computations on aligned and packed structs. + */ + +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long long uint64_t; + +/* from sys/arch/x86/include/cpu_extended_state.h */ + +union fp_addr { + uint64_t fa_64; + struct { + uint32_t fa_off; + uint16_t fa_seg; + uint16_t fa_opcode; + } fa_32; +} __attribute__((packed)) __attribute__((aligned(4))); + +struct fpacc87 { + uint64_t f87_mantissa; + uint16_t f87_exp_sign; +} __attribute__((packed)) __attribute__((aligned(2))); + +struct save87 { + uint16_t s87_cw __attribute__((aligned(4))); + uint16_t s87_sw __attribute__((aligned(4))); + uint16_t s87_tw __attribute__((aligned(4))); + union fp_addr s87_ip; + union fp_addr s87_dp; + struct fpacc87 s87_ac[8]; +}; + +struct { + unsigned int sizeof_fp_addr: sizeof(union fp_addr) == 8 ? 1 : -1; + + unsigned int sizeof_fpacc87: sizeof(struct fpacc87) == 10 ? 1 : -1; + + /* expect+1: illegal bit-field size: 255 *//*FIXME*/ + unsigned int sizeof_save87: sizeof(struct save87) == 108 ? 1 : -1; +}; diff --git a/usr.bin/xlint/lint1/gcc_attribute_aligned.exp b/usr.bin/xlint/lint1/gcc_attribute_aligned.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/gcc_attribute_aligned.exp @@ -0,0 +1 @@ +gcc_attribute_aligned.c(43): error: illegal bit-field size: 255 [36] diff --git a/usr.bin/xlint/lint1/gcc_attribute_enum.c b/usr.bin/xlint/lint1/gcc_attribute_enum.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/gcc_attribute_enum.c @@ -0,0 +1,46 @@ +/* $NetBSD: gcc_attribute_enum.c,v 1.4 2021/07/25 18:48:47 rillig Exp $ */ +# 3 "gcc_attribute_enum.c" + +/* + * Tests for the GCC __attribute__ for enumerators. + * + * https://gcc.gnu.org/onlinedocs/gcc/Enumerator-Attributes.html + */ + +/* + * Attributes in enum-specifier. + * + * See GCC, c-parser.c, function c_parser_enum_specifier. + */ + +enum __attribute__(()) tag; + +enum __attribute__(()) tag_with_declaration { + TAG_WITH_DECL +} __attribute__(()); + +enum __attribute__(()) { + ONLY_DECL +} __attribute__(()); + +/* + * Attributes in enumerator. + * + * See GCC, c-parser.c, function c_parser_enum_specifier. + */ + +enum without_initializer { + NO_INIT_FIRST __attribute__(()), + NO_INIT_LAST __attribute__(()) +}; + +enum with_initializer { + INIT_FIRST __attribute__(()) = 1, + INIT_LAST __attribute__(()) = 2, + /* expect+1: syntax error '__attribute__' [249] */ + INIT_WRONG = 3 __attribute__(()), +}; + +enum tag { + TAG +}; diff --git a/usr.bin/xlint/lint1/gcc_attribute_enum.exp b/usr.bin/xlint/lint1/gcc_attribute_enum.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/gcc_attribute_enum.exp @@ -0,0 +1 @@ +gcc_attribute_enum.c(41): error: syntax error '__attribute__' [249] diff --git a/usr.bin/xlint/lint1/gcc_attribute_func.c b/usr.bin/xlint/lint1/gcc_attribute_func.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/gcc_attribute_func.c @@ -0,0 +1,38 @@ +/* $NetBSD: gcc_attribute_func.c,v 1.2 2021/07/14 20:39:13 rillig Exp $ */ +# 3 "gcc_attribute_func.c" + +/* + * Tests for the GCC __attribute__ for functions. + * + * https://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html + */ + +void deprecated_function(void) + __attribute__((__noreturn__)) + __attribute__((__aligned__(8), __cold__)) + __attribute__((__deprecated__("do not use while driving"))); + +__attribute__((__cold__)) +void attribute_as_prefix(void); + +void __attribute__((__cold__)) attribute_after_type_spec(void); +void *__attribute__((__cold__)) attribute_before_name(void); +/*TODO: do not allow __attribute__ after function name */ +void *attribute_after_name __attribute__((__cold__))(void); +void *attribute_after_parameters(void) __attribute__((__cold__)); + +/* + * The attribute 'used' does not influence static functions, it only + * applies to function parameters. + */ +/* expect+2: warning: static function used_function unused [236] */ +static void __attribute__((used)) +used_function(void) +{ +} + +/* expect+2: warning: static function unused_function unused [236] */ +static void +unused_function(void) +{ +} diff --git a/usr.bin/xlint/lint1/gcc_attribute_func.exp b/usr.bin/xlint/lint1/gcc_attribute_func.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/gcc_attribute_func.exp @@ -0,0 +1,2 @@ +gcc_attribute_func.c(30): warning: static function used_function unused [236] +gcc_attribute_func.c(36): warning: static function unused_function unused [236] diff --git a/usr.bin/xlint/lint1/gcc_attribute_label.c b/usr.bin/xlint/lint1/gcc_attribute_label.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/gcc_attribute_label.c @@ -0,0 +1,25 @@ +/* $NetBSD: gcc_attribute_label.c,v 1.2 2021/07/11 19:24:42 rillig Exp $ */ +# 3 "gcc_attribute_label.c" + +/* + * Tests for the GCC __attribute__ for labels. + * + * https://gcc.gnu.org/onlinedocs/gcc/Label-Attributes.html + */ + +void dead(void); + +void +test(int i) +{ + if (i < 1000) + goto hot; +error: + __attribute__((__cold__)); + dead(); + +hot: + __attribute__((__hot__)); + if (i < 0) + goto error; +} diff --git a/usr.bin/xlint/lint1/gcc_attribute_stmt.c b/usr.bin/xlint/lint1/gcc_attribute_stmt.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/gcc_attribute_stmt.c @@ -0,0 +1,36 @@ +/* $NetBSD: gcc_attribute_stmt.c,v 1.1 2021/07/06 17:33:07 rillig Exp $ */ +# 3 "gcc_attribute_stmt.c" + +/* + * Tests for the GCC __attribute__ for statements. + * + * https://gcc.gnu.org/onlinedocs/gcc/Statement-Attributes.html + */ + +void println(const char *); + +void +attribute_fallthrough(int i) +{ + switch (i) { + case 5: + /* + * The attribute 'fallthrough' is only valid after a + * preceding statement. This is already caught by GCC, so + * lint does not need to care. + */ + __attribute__((__fallthrough__)); + case 3: + println("odd"); + __attribute__((__fallthrough__)); + case 2: + /* + * Only the null statement can have the attribute + * 'fallthrough'. This is already caught by GCC, so + * lint does not need to care. + */ + /* expect+2: error: syntax error '__attribute__' [249] */ + println("prime") + __attribute__((__fallthrough__)); + } +} diff --git a/usr.bin/xlint/lint1/gcc_attribute_stmt.exp b/usr.bin/xlint/lint1/gcc_attribute_stmt.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/gcc_attribute_stmt.exp @@ -0,0 +1 @@ +gcc_attribute_stmt.c(34): error: syntax error '__attribute__' [249] diff --git a/usr.bin/xlint/lint1/gcc_attribute_type.c b/usr.bin/xlint/lint1/gcc_attribute_type.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/gcc_attribute_type.c @@ -0,0 +1,31 @@ +/* $NetBSD: gcc_attribute_type.c,v 1.2 2021/07/25 19:05:27 rillig Exp $ */ +# 3 "gcc_attribute_type.c" + +/* + * Tests for the GCC __attribute__ for types. + * + * https://gcc.gnu.org/onlinedocs/gcc/Type-Attributes.html + */ + +struct __attribute__((__packed__)) __attribute__(()) packed_record; + +struct __attribute__((__packed__)) packed_record { + unsigned char len[2]; + unsigned long magic; +}; + +struct record_packed { + unsigned char len[2]; + unsigned long magic; +} __attribute__((__packed__)); + +/* TODO: do not allow __attribute__ before 'struct' */ +__attribute__((__packed__)) +struct attribute_before_keyword { + unsigned char len[2]; + unsigned long magic; +}; + +/* just to trigger _some_ error, to keep the .exp file */ +/* expect+1: error: syntax error 'syntax_error' [249] */ +__attribute__((syntax_error)); diff --git a/usr.bin/xlint/lint1/gcc_attribute_type.exp b/usr.bin/xlint/lint1/gcc_attribute_type.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/gcc_attribute_type.exp @@ -0,0 +1 @@ +gcc_attribute_type.c(31): error: syntax error 'syntax_error' [249] diff --git a/usr.bin/xlint/lint1/gcc_attribute_var.c b/usr.bin/xlint/lint1/gcc_attribute_var.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/gcc_attribute_var.c @@ -0,0 +1,75 @@ +/* $NetBSD: gcc_attribute_var.c,v 1.5 2021/08/11 05:19:33 rillig Exp $ */ +# 3 "gcc_attribute_var.c" + +/* + * Tests for the GCC __attribute__ for variables. + * + * https://gcc.gnu.org/onlinedocs/gcc/Variable-Attributes.html + */ + +void +write_to_page(unsigned index, char ch) +{ + static char page[4096] + __attribute__((__aligned__(4096))); + + page[index] = ch; +} + +void +placement( + __attribute__((__deprecated__)) int before, + int __attribute__((__deprecated__)) between, + int after __attribute__((__deprecated__)) +); + +void println(void); + +/* + * Since cgram.y 1.294 from 2021-07-10, lint did not accept declarations that + * started with __attribute__, due to a newly and accidentally introduced + * shift/reduce conflict in the grammar. + * + * A GCC extension allows statement of the form __attribute__((fallthrough)), + * thus starting with __attribute__. This is the 'shift' in the conflict. + * The 'reduce' in the conflict was begin_type. + * + * Before cgram 1.294, the gcc_attribute was placed outside the pair of + * begin_type/end_type, exactly to resolve this conflict. + * + * Conceptually, it made sense to put the __attribute__((unused)) between + * begin_type and end_type, to make it part of the declaration-specifiers. + * This change introduced the hidden conflict though. + * + * Interestingly, the number of shift/reduce conflicts did not change in + * cgram 1.294, the conflicts were just resolved differently than before. + * + * To prevent this from happening again, make sure that declarations as well + * as statements can start with gcc_attribute. + */ +void +ambiguity_for_attribute(void) +{ + __attribute__((unused)) _Bool var1; + + switch (1) { + case 1: + println(); + /* expect+1: warning: 'var2' unused in function 'ambiguity_for_attribute' [192] */ + __attribute__((unused)) _Bool var2; + __attribute__((fallthrough)); + case 2: + println(); + } +} + +void +attribute_after_array_brackets( + const char *argv[] __attribute__((__unused__)) +) +{ +} + +/* just to trigger _some_ error, to keep the .exp file */ +/* expect+1: error: syntax error 'syntax_error' [249] */ +__attribute__((syntax_error)); diff --git a/usr.bin/xlint/lint1/gcc_attribute_var.exp b/usr.bin/xlint/lint1/gcc_attribute_var.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/gcc_attribute_var.exp @@ -0,0 +1,2 @@ +gcc_attribute_var.c(59): warning: 'var2' unused in function 'ambiguity_for_attribute' [192] +gcc_attribute_var.c(75): error: syntax error 'syntax_error' [249] diff --git a/usr.bin/xlint/lint1/gcc_bit_field_types.c b/usr.bin/xlint/lint1/gcc_bit_field_types.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/gcc_bit_field_types.c @@ -0,0 +1,36 @@ +/* $NetBSD: gcc_bit_field_types.c,v 1.5 2021/05/04 05:40:10 rillig Exp $ */ +# 3 "gcc_bit_field_types.c" + +/* + * https://gcc.gnu.org/onlinedocs/gcc/Structures-unions-enumerations-and-bit-fields-implementation.html + * + * "Other integer types, such as long int, and enumerated types are permitted + * even in strictly conforming mode." + * + * See msg_035.c. + */ + +struct example { + int int_flag: 1; + unsigned int unsigned_int_flag: 1; + long long_flag: 1; + unsigned long unsigned_long_flag: 1; + long long long_long_flag: 1; + unsigned long long unsigned_long_long_flag: 1; + double double_flag: 1; /* expect: illegal bit-field type 'double' */ +}; + +struct large_bit_field { + unsigned long long member: 48; +}; + +unsigned long long +promote_large_bit_field(struct large_bit_field lbf) +{ + /* + * Before tree.c 1.281 from 2021-05-04: + * lint: assertion "len == size_in_bits(INT)" failed + * in promote at tree.c:1698 + */ + return lbf.member & 0xf; +} diff --git a/usr.bin/xlint/lint1/gcc_bit_field_types.exp b/usr.bin/xlint/lint1/gcc_bit_field_types.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/gcc_bit_field_types.exp @@ -0,0 +1 @@ +gcc_bit_field_types.c(20): warning: illegal bit-field type 'double' [35] diff --git a/usr.bin/xlint/lint1/gcc_cast_union.c b/usr.bin/xlint/lint1/gcc_cast_union.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/gcc_cast_union.c @@ -0,0 +1,100 @@ +/* $NetBSD: gcc_cast_union.c,v 1.3 2021/08/03 21:18:24 rillig Exp $ */ +# 3 "gcc_cast_union.c" + +/* + * Test the GCC extension for casting to a union type. + * + * As of GCC 10.3.0, GCC only prints a generic warning without any details: + * error: cast to union type from type not present in union + * No idea why it neither mentions the union type nor the actual type. + * + * https://gcc.gnu.org/onlinedocs/gcc/Cast-to-Union.html + */ + +/* lint1-extra-flags: -e */ + +union anything { + _Bool m_bool; + char m_char; + signed char m_signed_char; + unsigned char m_unsigned_char; + short m_short; + unsigned short m_unsigned_short; + int m_int; + unsigned int m_unsigned_int; + long m_long; + unsigned long m_unsigned_long; + long long m_long_long; + unsigned long long m_unsigned_long_long; + /* skip __int128_t and __uint128_t for now */ + float m_float; + double m_double; + long double m_long_double; + + struct m_struct { + int member; + } m_struct; + union m_union { + int member; + } m_union; + enum m_enum1 { + E1 + } m_enum1; + enum m_enum2 { + E2 + } m_enum2; + const char *m_const_char_pointer; + double m_double_array[5]; + void (*m_function)(void *); + void (*m_function_varargs)(void *, ...); + float _Complex m_float_complex; + double _Complex m_double_complex; + long double _Complex m_long_double_complex; +}; + +enum other_enum { + OTHER +}; + +void +test(void) +{ + union anything any; + + any = (union anything)(_Bool)0; + any = (union anything)(char)'\0'; + any = (union anything)(signed char)'\0'; + any = (union anything)(unsigned char)'\0'; + any = (union anything)(short)'\0'; + any = (union anything)(unsigned short)'\0'; + any = (union anything)(int)'\0'; + any = (union anything)(unsigned int)'\0'; + any = (union anything)(long)'\0'; + any = (union anything)(unsigned long)'\0'; + any = (union anything)(long long)'\0'; + any = (union anything)(unsigned long long)'\0'; + any = (union anything)0.0F; + any = (union anything)0.0; + any = (union anything)0.0L; + any = (union anything)(struct m_struct){ 0 }; + any = (union anything)(union m_union){ 0 }; + any = (union anything)E1; + any = (union anything)E2; + /* GCC allows enum mismatch even with -Wenum-conversion */ + /* expect+1: error: type 'enum other_enum' is not a member of 'union anything' [329] */ + any = (union anything)OTHER; + /* expect+1: error: type 'pointer to char' is not a member of 'union anything' [329] */ + any = (union anything)"char *"; + any = (union anything)(const char *)"char *"; + /* expect+1: error: type 'pointer to double' is not a member of 'union anything' [329] */ + any = (union anything)(double[5]){ 0.0, 1.0, 2.0, 3.0, 4.0 }; + /* expect+1: error: type 'pointer to double' is not a member of 'union anything' [329] */ + any = (union anything)(double[4]){ 0.0, 1.0, 2.0, 3.0 }; + /* expect+1: error: type 'pointer to int' is not a member of 'union anything' [329] */ + any = (union anything)(int[5]){ 0, 1, 2, 3, 4 }; + any = (union anything)(float _Complex)0.0F; + any = (union anything)(double _Complex)0.0; + any = (union anything)(long double _Complex)0.0L; + + any = any; +} diff --git a/usr.bin/xlint/lint1/gcc_cast_union.exp b/usr.bin/xlint/lint1/gcc_cast_union.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/gcc_cast_union.exp @@ -0,0 +1,5 @@ +gcc_cast_union.c(85): error: type 'enum other_enum' is not a member of 'union anything' [329] +gcc_cast_union.c(87): error: type 'pointer to char' is not a member of 'union anything' [329] +gcc_cast_union.c(90): error: type 'pointer to double' is not a member of 'union anything' [329] +gcc_cast_union.c(92): error: type 'pointer to double' is not a member of 'union anything' [329] +gcc_cast_union.c(94): error: type 'pointer to int' is not a member of 'union anything' [329] diff --git a/usr.bin/xlint/lint1/gcc_init_compound_literal.c b/usr.bin/xlint/lint1/gcc_init_compound_literal.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/gcc_init_compound_literal.c @@ -0,0 +1,57 @@ +/* $NetBSD: gcc_init_compound_literal.c,v 1.4 2021/04/18 08:03:56 rillig Exp $ */ +# 3 "gcc_init_compound_literal.c" + +/* + * C99 says in 6.7.8p4: + * + * All the expressions in an initializer for an object that has static + * storage duration shall be constant expressions or string literals. + * + * The term "constant expression" is defined in C99 6.6, where 6.6p9 allows + * "constant expressions" in initializers to also be an "address constant". + * Using these address constants, it is possible to reference an unnamed + * object created by a compound literal (C99 6.5.2.5), using either an + * explicit '&' or the implicit array-to-pointer conversion from C99 6.3.2.1. + * + * Before init.c 1.195 from 2021-04-17, lint failed with an assertion failure + * in check_global_variable, called by check_global_symbols since these + * temporary objects have neither storage class EXTERN nor STATIC. + */ + +// Seen in sys/crypto/aes/aes_ccm.c. +const struct { + const unsigned char *ctxt; +} T = { + .ctxt = (const unsigned char[4]){ + 1, 2, 3, 4 + }, +}; + +struct node { + int num; + struct node *left; + struct node *right; +}; + +/* + * Initial tree for representing the decisions in the classic number guessing + * game often used in teaching the basics of programming. + */ +/* expect+1: static variable guess unused */ +static const struct node guess = { + 50, + &(struct node){ + 25, + &(struct node){ + 12, + (void *)0, + (void *)0, + }, + &(struct node){ + 37, + (void *)0, + (void *)0, + }, + }, + (void *)0 +}; diff --git a/usr.bin/xlint/lint1/gcc_init_compound_literal.exp b/usr.bin/xlint/lint1/gcc_init_compound_literal.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/gcc_init_compound_literal.exp @@ -0,0 +1 @@ +gcc_init_compound_literal.c(41): warning: static variable guess unused [226] diff --git a/usr.bin/xlint/lint1/gcc_stmt_asm.c b/usr.bin/xlint/lint1/gcc_stmt_asm.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/gcc_stmt_asm.c @@ -0,0 +1,42 @@ +/* $NetBSD: gcc_stmt_asm.c,v 1.3 2021/07/21 21:04:00 rillig Exp $ */ +# 3 "gcc_stmt_asm.c" + +/* + * Tests for the GCC 'asm' statement. + */ + +void +function(void) +{ + /* + * lint is not really interested in assembly language, therefore it + * just skips everything until and including the closing parenthesis. + */ + asm(any "string" or 12345 || whatever); + + /* + * Parentheses are allowed in 'asm' statements, they have to be + * properly nested. Brackets and braces don't have to be nested + * since they usually not occur in 'asm' statements. + */ + __asm(^(int = typedef[[[{{{)); + + __asm__(); +} + +/* + * Even on the top level, 'asm' is allowed. It is interpreted as a + * declaration. + */ +__asm__(); + +void +syntax_error(void) +{ + /* expect+1: syntax error '__asm__' [249] */ + int i = __asm__(); +} + +__asm__( +/* cover read_until_rparen at EOF */ +/* expect+1: error: syntax error '' [249] */ diff --git a/usr.bin/xlint/lint1/gcc_stmt_asm.exp b/usr.bin/xlint/lint1/gcc_stmt_asm.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/gcc_stmt_asm.exp @@ -0,0 +1,2 @@ +gcc_stmt_asm.c(37): error: syntax error '__asm__' [249] +gcc_stmt_asm.c(43): error: syntax error '' [249] diff --git a/usr.bin/xlint/lint1/gcc_typeof.c b/usr.bin/xlint/lint1/gcc_typeof.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/gcc_typeof.c @@ -0,0 +1,33 @@ +/* $NetBSD: gcc_typeof.c,v 1.3 2021/07/25 15:58:24 rillig Exp $ */ +# 3 "gcc_typeof.c" + +/* + * Tests for the GCC extension 'typeof'. + * + * https://gcc.gnu.org/onlinedocs/gcc/Typeof.html + */ + +void take_double(typeof(0.0)); + +void take_function_double_returning_double( + typeof(0.0)( + typeof(0.0) + ) +); + +void +cast(double(*fn)(double)) +{ + take_double(0.0); + + /* expect+1: warning: passing 'pointer to function(double) returning double' to incompatible 'double', arg #1 [155] */ + take_double(fn); + + /* expect+1: warning: passing 'double' to incompatible 'pointer to function(double) returning double', arg #1 [155] */ + take_function_double_returning_double(0.0); + + take_function_double_returning_double(fn); + + /* identity cast */ + take_function_double_returning_double((double (*)(double))fn); +} diff --git a/usr.bin/xlint/lint1/gcc_typeof.exp b/usr.bin/xlint/lint1/gcc_typeof.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/gcc_typeof.exp @@ -0,0 +1,2 @@ +gcc_typeof.c(24): warning: passing 'pointer to function(double) returning double' to incompatible 'double', arg #1 [155] +gcc_typeof.c(27): warning: passing 'double' to incompatible 'pointer to function(double) returning double', arg #1 [155] diff --git a/usr.bin/xlint/lint1/gcc_typeof_after_statement.c b/usr.bin/xlint/lint1/gcc_typeof_after_statement.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/gcc_typeof_after_statement.c @@ -0,0 +1,22 @@ +/* $NetBSD: gcc_typeof_after_statement.c,v 1.2 2021/05/03 05:24:44 rillig Exp $ */ +# 3 "gcc_typeof_after_statement.c" + +/* + * Before cgram.y 1.226 from 2021-05-03, lint could not parse typeof(...) if + * there was a statement before it. + */ + +void * +example(void **ptr) +{ + return ({ + if (*ptr != (void *)0) + ptr++; + __typeof__(*ptr) ret = *ptr; + ret; + }); +} + +/* Just to keep the .exp file. */ +/* expect+1: static function unused declared but not defined */ +static void unused(void); diff --git a/usr.bin/xlint/lint1/gcc_typeof_after_statement.exp b/usr.bin/xlint/lint1/gcc_typeof_after_statement.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/gcc_typeof_after_statement.exp @@ -0,0 +1 @@ +gcc_typeof_after_statement.c(22): warning: static function unused declared but not defined [290] diff --git a/usr.bin/xlint/lint1/init.c b/usr.bin/xlint/lint1/init.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/init.c @@ -0,0 +1,24 @@ +/* $NetBSD: init.c,v 1.1 2021/07/10 09:24:27 rillig Exp $ */ +# 3 "init.c" + +/* + * Tests for initialization. + * + * C99 6.7.8 + */ + +/* + * C99 does not allow empty initializer braces syntactically. + * Lint allows this syntactically, it just complains if the resulting + * object is empty. + */ +/* expect+1: error: empty array declaration: empty_array_with_initializer [190] */ +double empty_array_with_initializer[] = {}; +double array_with_empty_initializer[3] = {}; + +/* + * C99 does not allow empty initializer braces syntactically. + */ +struct { + int member; +} empty_struct_initializer = {}; diff --git a/usr.bin/xlint/lint1/init.exp b/usr.bin/xlint/lint1/init.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/init.exp @@ -0,0 +1 @@ +init.c(16): error: empty array declaration: empty_array_with_initializer [190] diff --git a/usr.bin/xlint/lint1/init_c90.c b/usr.bin/xlint/lint1/init_c90.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/init_c90.c @@ -0,0 +1,30 @@ +/* $NetBSD: init_c90.c,v 1.2 2021/07/14 20:39:13 rillig Exp $ */ +# 3 "init_c90.c" + +/* + * Test initialization before C99. + * + * C90 3.5.7 + */ + +/* lint1-flags: -sw */ + +struct point { + int x, y; +}; + +struct point point_c90 = { 0, 0 }; +/* expect+2: warning: struct or union member name in initializer is a C9X feature [313] */ +/* expect+1: warning: struct or union member name in initializer is a C9X feature [313] */ +struct point point_c99 = { .x = 0, .y = 0 }; + +struct point points_c90[] = {{ 0, 0 }}; +/* expect+1: warning: array initializer with designators is a C9X feature [321] */ +struct point points_c99[] = {[3] = { 0, 0 }}; + + +struct point +compound_literal(void) { + /* expect+1: compound literals are a C9X/GCC extension [319] */ + return (struct point){ 0, 0 }; +} diff --git a/usr.bin/xlint/lint1/init_c90.exp b/usr.bin/xlint/lint1/init_c90.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/init_c90.exp @@ -0,0 +1,4 @@ +init_c90.c(19): warning: struct or union member name in initializer is a C9X feature [313] +init_c90.c(19): warning: struct or union member name in initializer is a C9X feature [313] +init_c90.c(23): warning: array initializer with designators is a C9X feature [321] +init_c90.c(29): error: compound literals are a C9X/GCC extension [319] diff --git a/usr.bin/xlint/lint1/lex_char.c b/usr.bin/xlint/lint1/lex_char.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/lex_char.c @@ -0,0 +1,71 @@ +/* $NetBSD: lex_char.c,v 1.4 2021/06/29 07:28:01 rillig Exp $ */ +# 3 "lex_char.c" + +/* + * Tests for lexical analysis of character constants. + * + * C99 6.4.4.4 "Character constants" + */ + +void sink(char); + +void +test(void) +{ + /* expect+1: empty character constant */ + sink(''); + + sink('a'); + + sink('\0'); + + /* UTF-8 */ + /* expect+2: multi-character character constant */ + /* expect+1: conversion of 'int' to 'char' is out of range */ + sink('ä'); + + /* GCC extension */ + /* expect+1: dubious escape \e */ + sink('\e'); + + /* since C99 */ + sink('\x12'); + + /* octal */ + sink('\177'); + + /* expect+1: empty character constant */ + sink(''); + + /* U+0007 alarm/bell */ + sink('\a'); + + /* U+0008 backspace */ + sink('\b'); + + /* U+0009 horizontal tabulation */ + sink('\t'); + + /* U+000A line feed */ + sink('\n'); + + /* U+000B vertical tabulation */ + sink('\v'); + + /* U+000C form feed */ + sink('\f'); + + /* U+000D carriage return */ + sink('\r'); +} + +/* + * Even though backslash-newline is not supported by C99, lint accepts it + * in any mode, even for traditional C. + */ +char ch = '\ +\ +\ +\ +\ +x'; diff --git a/usr.bin/xlint/lint1/lex_char.exp b/usr.bin/xlint/lint1/lex_char.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/lex_char.exp @@ -0,0 +1,5 @@ +lex_char.c(16): error: empty character constant [73] +lex_char.c(25): warning: multi-character character constant [294] +lex_char.c(25): warning: conversion of 'int' to 'char' is out of range, arg #1 [295] +lex_char.c(29): warning: dubious escape \e [79] +lex_char.c(38): error: empty character constant [73] diff --git a/usr.bin/xlint/lint1/lex_char_uchar.c b/usr.bin/xlint/lint1/lex_char_uchar.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/lex_char_uchar.c @@ -0,0 +1,19 @@ +/* $NetBSD: lex_char_uchar.c,v 1.4 2021/08/21 11:50:57 rillig Exp $ */ +# 3 "lex_char_uchar.c" + +/* + * Test lexical analysis of character constants on platforms where plain + * char has the same representation as unsigned char. + */ + +/* lint1-only-if: uchar */ + +/* + * Before inittyp.c 1.23 from 2021-06-29, the following initialization + * triggered a wrong warning "conversion of 'int' to 'char' is out of range", + * but only on platforms where char has the same representation as unsigned + * char. There are only few of these platforms, which allowed this bug to + * survive for almost 26 years, since the initial commit of lint on + * 1995-07-03. + */ +char ch = '\xff'; diff --git a/usr.bin/xlint/lint1/lex_comment.c b/usr.bin/xlint/lint1/lex_comment.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/lex_comment.c @@ -0,0 +1,13 @@ +/* $NetBSD: lex_comment.c,v 1.1 2021/06/19 20:25:58 rillig Exp $ */ +# 3 "lex_comment.c" + +/* + * Before lex.c 1.41 from 2021-06-19, lint ran into an endless loop when it + * saw an unclosed comment at the end of the translation unit. In practice + * this was not relevant since the translation unit always comes from the C + * preprocessor, which always emits a well-formed token sequence. + */ + +/* expect+3: error: unterminated comment [256] */ +/* expect+2: warning: empty translation unit [272] */ +/* unclosed comment diff --git a/usr.bin/xlint/lint1/lex_comment.exp b/usr.bin/xlint/lint1/lex_comment.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/lex_comment.exp @@ -0,0 +1,2 @@ +lex_comment.c(14): error: unterminated comment [256] +lex_comment.c(14): warning: empty translation unit [272] diff --git a/usr.bin/xlint/lint1/lex_floating.c b/usr.bin/xlint/lint1/lex_floating.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/lex_floating.c @@ -0,0 +1,35 @@ +/* $NetBSD: lex_floating.c,v 1.1 2021/06/19 08:30:08 rillig Exp $ */ +# 3 "lex_floating.c" + +/* + * Tests for lexical analysis of floating constants. + * + * C99 6.4.4.2 "Floating constants" + */ + +void sinkf(float); +void sinkd(double); +void sinkl(long double); + +void +test_float(void) +{ + sinkf(0.0F); + sinkf(0.0f); + sinkf(-0.0F); + sinkf(-0.0f); +} + +void +test_double(void) +{ + // https://bugs.java.com/bugdatabase/view_bug.do?bug_id=4396272 + sinkd(2.2250738585072012e-308); + sinkd(1.23x); /* expect: syntax error 'x' */ +} + +void +test_long_double(void) +{ + sinkl(2.2250738585072012e-308L); +} diff --git a/usr.bin/xlint/lint1/lex_floating.exp b/usr.bin/xlint/lint1/lex_floating.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/lex_floating.exp @@ -0,0 +1 @@ +lex_floating.c(28): error: syntax error 'x' [249] diff --git a/usr.bin/xlint/lint1/lex_integer.c b/usr.bin/xlint/lint1/lex_integer.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/lex_integer.c @@ -0,0 +1,150 @@ +/* $NetBSD: lex_integer.c,v 1.9 2021/08/28 21:01:34 rillig Exp $ */ +# 3 "lex_integer.c" + +/* + * Tests for lexical analysis of integer constants. + * + * C99 6.4.4.1 "Integer constants" + */ + +/* lint1-only-if: lp64 */ + +long signed_long; +unsigned long long unsigned_long_long_var; + +struct s { + int member; +}; +/* + * When lint tries to convert the argument to 'struct s', it prints the + * actual type of the argument as a side effect. + */ +void print_type(struct s); + +void +no_suffix(void) +{ + /* expect+1: passing 'int' */ + print_type(0); + /* The '-' is not part of the constant, it is a unary operator. */ + /* expect+1: passing 'int' */ + print_type(-1); + + /* expect+1: passing 'int' */ + print_type(2147483647); + /* expect+1: passing 'int' */ + print_type(0x7fffffff); + /* expect+1: passing 'int' */ + print_type(017777777777); + + /* expect+1: passing 'unsigned int' */ + print_type(0x80000000); + /* expect+1: passing 'unsigned int' */ + print_type(020000000000); + /* expect+1: passing 'unsigned int' */ + print_type(0xffffffff); + + /* expect+1: passing 'long' */ + print_type(2147483648); + /* expect+1: passing 'long' */ + print_type(0x0000000100000000); + /* expect+1: passing 'long' */ + print_type(0x7fffffffffffffff); + + /* expect+1: passing 'unsigned long' */ + print_type(0x8000000000000000); + /* expect+1: passing 'unsigned long' */ + print_type(0xffffffffffffffff); + + /* expect+2: warning: integer constant out of range [252] */ + /* expect+1: warning: passing 'unsigned long' */ + print_type(0x00010000000000000000); +} + +void +suffix_u(void) +{ + /* expect+1: passing 'unsigned int' */ + print_type(3U); + /* expect+1: passing 'unsigned int' */ + print_type(3u); + + /* expect+1: passing 'unsigned int' */ + print_type(4294967295U); + /* expect+1: passing 'unsigned long' */ + print_type(4294967296U); +} + +void +suffix_l(void) +{ + /* expect+1: passing 'long' */ + print_type(3L); + + /* expect+1: passing 'long' */ + print_type(3l); +} + +void +suffix_ul(void) +{ + /* expect+1: passing 'unsigned long' */ + print_type(3UL); + /* expect+1: passing 'unsigned long' */ + print_type(3LU); +} + +void +suffix_ll(void) +{ + /* expect+1: passing 'long long' */ + print_type(3LL); + + /* The 'Ll' must not use mixed case. Checked by the compiler. */ + /* expect+1: passing 'long long' */ + print_type(3Ll); + + /* expect+1: passing 'long long' */ + print_type(3ll); +} + +void +suffix_ull(void) +{ + /* expect+1: passing 'unsigned long long' */ + print_type(3llu); + /* expect+1: passing 'unsigned long long' */ + print_type(3Ull); + + /* The 'LL' must not be split. Checked by the compiler. */ + /* expect+1: passing 'unsigned long long' */ + print_type(3lul); + + /* The 'Ll' must not use mixed case. Checked by the compiler. */ + /* expect+1: passing 'unsigned long long' */ + print_type(3ULl); +} + +void +suffix_too_many(void) +{ + /* expect+2: warning: malformed integer constant [251] */ + /* expect+1: passing 'long long' */ + print_type(3LLL); + + /* expect+2: warning: malformed integer constant [251] */ + /* expect+1: passing 'unsigned int' */ + print_type(3uu); +} + +/* https://gcc.gnu.org/onlinedocs/gcc/Binary-constants.html */ +void +binary_literal(void) +{ + /* This is a GCC extension, but lint doesn't know that. */ + /* expect+1: passing 'int' */ + print_type(0b1111000001011010); + + /* expect+1: passing 'unsigned int' */ + print_type(0b11110000111100001111000011110000); +} diff --git a/usr.bin/xlint/lint1/lex_integer.exp b/usr.bin/xlint/lint1/lex_integer.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/lex_integer.exp @@ -0,0 +1,36 @@ +lex_integer.c(28): warning: passing 'int' to incompatible 'struct s', arg #1 [155] +lex_integer.c(31): warning: passing 'int' to incompatible 'struct s', arg #1 [155] +lex_integer.c(34): warning: passing 'int' to incompatible 'struct s', arg #1 [155] +lex_integer.c(36): warning: passing 'int' to incompatible 'struct s', arg #1 [155] +lex_integer.c(38): warning: passing 'int' to incompatible 'struct s', arg #1 [155] +lex_integer.c(41): warning: passing 'unsigned int' to incompatible 'struct s', arg #1 [155] +lex_integer.c(43): warning: passing 'unsigned int' to incompatible 'struct s', arg #1 [155] +lex_integer.c(45): warning: passing 'unsigned int' to incompatible 'struct s', arg #1 [155] +lex_integer.c(48): warning: passing 'long' to incompatible 'struct s', arg #1 [155] +lex_integer.c(50): warning: passing 'long' to incompatible 'struct s', arg #1 [155] +lex_integer.c(52): warning: passing 'long' to incompatible 'struct s', arg #1 [155] +lex_integer.c(55): warning: passing 'unsigned long' to incompatible 'struct s', arg #1 [155] +lex_integer.c(57): warning: passing 'unsigned long' to incompatible 'struct s', arg #1 [155] +lex_integer.c(61): warning: integer constant out of range [252] +lex_integer.c(61): warning: passing 'unsigned long' to incompatible 'struct s', arg #1 [155] +lex_integer.c(68): warning: passing 'unsigned int' to incompatible 'struct s', arg #1 [155] +lex_integer.c(70): warning: passing 'unsigned int' to incompatible 'struct s', arg #1 [155] +lex_integer.c(73): warning: passing 'unsigned int' to incompatible 'struct s', arg #1 [155] +lex_integer.c(75): warning: passing 'unsigned long' to incompatible 'struct s', arg #1 [155] +lex_integer.c(82): warning: passing 'long' to incompatible 'struct s', arg #1 [155] +lex_integer.c(85): warning: passing 'long' to incompatible 'struct s', arg #1 [155] +lex_integer.c(92): warning: passing 'unsigned long' to incompatible 'struct s', arg #1 [155] +lex_integer.c(94): warning: passing 'unsigned long' to incompatible 'struct s', arg #1 [155] +lex_integer.c(101): warning: passing 'long long' to incompatible 'struct s', arg #1 [155] +lex_integer.c(105): warning: passing 'long long' to incompatible 'struct s', arg #1 [155] +lex_integer.c(108): warning: passing 'long long' to incompatible 'struct s', arg #1 [155] +lex_integer.c(115): warning: passing 'unsigned long long' to incompatible 'struct s', arg #1 [155] +lex_integer.c(117): warning: passing 'unsigned long long' to incompatible 'struct s', arg #1 [155] +lex_integer.c(121): warning: passing 'unsigned long long' to incompatible 'struct s', arg #1 [155] +lex_integer.c(125): warning: passing 'unsigned long long' to incompatible 'struct s', arg #1 [155] +lex_integer.c(133): warning: malformed integer constant [251] +lex_integer.c(133): warning: passing 'long long' to incompatible 'struct s', arg #1 [155] +lex_integer.c(137): warning: malformed integer constant [251] +lex_integer.c(137): warning: passing 'unsigned int' to incompatible 'struct s', arg #1 [155] +lex_integer.c(146): warning: passing 'int' to incompatible 'struct s', arg #1 [155] +lex_integer.c(149): warning: passing 'unsigned int' to incompatible 'struct s', arg #1 [155] diff --git a/usr.bin/xlint/lint1/lex_integer_binary.c b/usr.bin/xlint/lint1/lex_integer_binary.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/lex_integer_binary.c @@ -0,0 +1,30 @@ +/* $NetBSD: lex_integer_binary.c,v 1.1 2021/07/13 19:38:10 rillig Exp $ */ +# 3 "lex_integer_binary.c" + +/* + * Test for parsing binary integer literals in non-GCC mode. + * As of C11, binary integer literals are not supported. + * Neither are underscores in integer literals. + */ + +/* Remove the default -g flag. */ +/* lint1-flags: -Ac11 -w */ + +void sink(unsigned int); + +void +binary_literal(void) +{ + /* + * Binary integer literals are a GCC extension, but lint allows them + * even in non-GCC mode. + */ + sink(0b1111000001011010); + + /* + * Even though it would be useful for binary literals, GCC does not + * support underscores to separate the digit groups. + */ + /* expect+1: syntax error '_0000_0101_1010' [249] */ + sink(0b1111_0000_0101_1010); +} diff --git a/usr.bin/xlint/lint1/lex_integer_binary.exp b/usr.bin/xlint/lint1/lex_integer_binary.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/lex_integer_binary.exp @@ -0,0 +1 @@ +lex_integer_binary.c(29): error: syntax error '_0000_0101_1010' [249] diff --git a/usr.bin/xlint/lint1/lex_integer_ilp32.c b/usr.bin/xlint/lint1/lex_integer_ilp32.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/lex_integer_ilp32.c @@ -0,0 +1,44 @@ +/* $NetBSD: lex_integer_ilp32.c,v 1.3 2021/08/21 11:50:57 rillig Exp $ */ +# 3 "lex_integer_ilp32.c" + +/* + * Tests for lexical analysis of integer constants. + * + * C99 6.4.4.1 "Integer constants" + */ + +/* lint1-only-if: ilp32 */ + +void sinki(int); +void sinku(unsigned int); + +/* All platforms supported by lint have 32-bit int in two's complement. */ +void +test_signed_int(void) +{ + sinki(0); + + sinki(-1); + + sinki(2147483647); + + /* expect+1: 'unsigned long' to 'int' is out of range, arg #1 [295] */ + sinki(2147483648); + + sinki(-2147483647); + + /* expect+2: ANSI C treats constant as unsigned, op - [218] */ + /* expect+1: 'unsigned long' to 'int' is out of range, arg #1 [295] */ + sinki(-2147483648); +} + +void +test_unsigned_int(void) +{ + sinku(0); + + sinku(4294967295U); + + /* expect+1: integer constant out of range [252] */ + sinku(4294967296U); +} diff --git a/usr.bin/xlint/lint1/lex_integer_ilp32.exp b/usr.bin/xlint/lint1/lex_integer_ilp32.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/lex_integer_ilp32.exp @@ -0,0 +1,4 @@ +lex_integer_ilp32.c(26): warning: conversion of 'unsigned long' to 'int' is out of range, arg #1 [295] +lex_integer_ilp32.c(32): warning: ANSI C treats constant as unsigned, op - [218] +lex_integer_ilp32.c(32): warning: conversion of 'unsigned long' to 'int' is out of range, arg #1 [295] +lex_integer_ilp32.c(43): warning: integer constant out of range [252] diff --git a/usr.bin/xlint/lint1/lex_string.c b/usr.bin/xlint/lint1/lex_string.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/lex_string.c @@ -0,0 +1,33 @@ +/* $NetBSD: lex_string.c,v 1.3 2021/08/23 17:47:34 rillig Exp $ */ +# 3 "lex_string.c" + +/* + * Test lexical analysis of string constants. + * + * C99 6.4.5 "String literals" + */ + +void sink(const char *); + +void +test(void) +{ + sink(""); + + sink("hello, world\n"); + + sink("\0"); + + sink("\0\0\0\0"); + + /* expect+1: no hex digits follow \x [74] */ + sink("\x"); /* unfinished */ + + /* expect+1: dubious escape \y [79] */ + sink("\y"); /* unknown escape sequence */ + + sink("first" "second"); + + /* expect+1: error: cannot concatenate wide and regular string literals [292] */ + sink("plain" L"wide"); +} diff --git a/usr.bin/xlint/lint1/lex_string.exp b/usr.bin/xlint/lint1/lex_string.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/lex_string.exp @@ -0,0 +1,3 @@ +lex_string.c(24): error: no hex digits follow \x [74] +lex_string.c(27): warning: dubious escape \y [79] +lex_string.c(32): error: cannot concatenate wide and regular string literals [292] diff --git a/usr.bin/xlint/lint1/lex_wide_char.c b/usr.bin/xlint/lint1/lex_wide_char.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/lex_wide_char.c @@ -0,0 +1,41 @@ +/* $NetBSD: lex_wide_char.c,v 1.2 2021/06/20 18:38:12 rillig Exp $ */ +# 3 "lex_wide_char.c" + +/* + * Tests for lexical analysis of character constants. + * + * C99 6.4.4.4 "Character constants" + */ + +void sink(int); + +void +test(void) +{ + /* expect+1: empty character constant */ + sink(L''); + + sink(L'a'); + + sink(L'\0'); + + /* UTF-8 */ + /* expect+1: too many characters in character constant */ + sink(L'ä'); + + /* GCC extension */ + /* expect+1: dubious escape \e */ + sink(L'\e'); + + /* since C99 */ + sink(L'\x12'); + + /* octal */ + sink(L'\177'); + + /* newline */ + sink(L'\n'); + + /* expect+1: empty character constant */ + sink(L''); +} diff --git a/usr.bin/xlint/lint1/lex_wide_char.exp b/usr.bin/xlint/lint1/lex_wide_char.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/lex_wide_char.exp @@ -0,0 +1,4 @@ +lex_wide_char.c(16): error: empty character constant [73] +lex_wide_char.c(24): error: too many characters in character constant [71] +lex_wide_char.c(28): warning: dubious escape \e [79] +lex_wide_char.c(40): error: empty character constant [73] diff --git a/usr.bin/xlint/lint1/lex_wide_string.c b/usr.bin/xlint/lint1/lex_wide_string.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/lex_wide_string.c @@ -0,0 +1,33 @@ +/* $NetBSD: lex_wide_string.c,v 1.2 2021/08/23 17:47:34 rillig Exp $ */ +# 3 "lex_wide_string.c" + +/* + * Test lexical analysis of wide string constants. + * + * C99 6.4.5 "String literals" + */ + +void sink(const int *); + +void +test(void) +{ + sink(L""); + + sink(L"hello, world\n"); + + sink(L"\0"); + + sink(L"\0\0\0\0"); + + /* expect+1: no hex digits follow \x [74] */ + sink(L"\x"); + + /* expect+1: dubious escape \y [79] */ + sink(L"\y"); + + sink(L"first" L"second"); + + /* expect+1: error: cannot concatenate wide and regular string literals [292] */ + sink(L"wide" "plain"); +} diff --git a/usr.bin/xlint/lint1/lex_wide_string.exp b/usr.bin/xlint/lint1/lex_wide_string.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/lex_wide_string.exp @@ -0,0 +1,3 @@ +lex_wide_string.c(24): error: no hex digits follow \x [74] +lex_wide_string.c(27): warning: dubious escape \y [79] +lex_wide_string.c(32): error: cannot concatenate wide and regular string literals [292] diff --git a/usr.bin/xlint/lint1/msg_000.c b/usr.bin/xlint/lint1/msg_000.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_000.c @@ -0,0 +1,10 @@ +/* $NetBSD: msg_000.c,v 1.3 2021/01/31 11:12:07 rillig Exp $ */ +# 3 "msg_000.c" + +// Test for message: empty declaration [0] + +extern int extern_declared; + +; /* expect: 0 */ + +static int local_defined; /* expect: 226 */ diff --git a/usr.bin/xlint/lint1/msg_000.exp b/usr.bin/xlint/lint1/msg_000.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_000.exp @@ -0,0 +1,2 @@ +msg_000.c(8): warning: empty declaration [0] +msg_000.c(10): warning: static variable local_defined unused [226] diff --git a/usr.bin/xlint/lint1/msg_000_c90.c b/usr.bin/xlint/lint1/msg_000_c90.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_000_c90.c @@ -0,0 +1,13 @@ +/* $NetBSD: msg_000_c90.c,v 1.1 2021/07/08 05:18:49 rillig Exp $ */ +# 3 "msg_000_c90.c" + +/* + * Test for message: empty declaration [0] + * + * In strict C90 mode, an empty declaration is an error, not merely a warning. + */ + +/* lint1-flags: -s */ + +/* expect+1: error: empty declaration [0] */ +; diff --git a/usr.bin/xlint/lint1/msg_000_c90.exp b/usr.bin/xlint/lint1/msg_000_c90.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_000_c90.exp @@ -0,0 +1 @@ +msg_000_c90.c(13): error: empty declaration [0] diff --git a/usr.bin/xlint/lint1/msg_001.c b/usr.bin/xlint/lint1/msg_001.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_001.c @@ -0,0 +1,8 @@ +/* $NetBSD: msg_001.c,v 1.4 2021/01/31 11:23:01 rillig Exp $ */ +# 3 "msg_001.c" + +// Test for message: old style declaration; add 'int' [1] + +old_style = 1; /* expect: [1] */ + +int new_style = 1; diff --git a/usr.bin/xlint/lint1/msg_001.exp b/usr.bin/xlint/lint1/msg_001.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_001.exp @@ -0,0 +1 @@ +msg_001.c(6): warning: old style declaration; add 'int' [1] diff --git a/usr.bin/xlint/lint1/msg_001_c90.c b/usr.bin/xlint/lint1/msg_001_c90.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_001_c90.c @@ -0,0 +1,14 @@ +/* $NetBSD: msg_001_c90.c,v 1.1 2021/07/08 05:18:49 rillig Exp $ */ +# 3 "msg_001_c90.c" + +/* + * Test for message: old style declaration; add 'int' [1] + * + * In strict C90 mode, an old-style declaration is an error, not merely a + * warning. + */ + +/* lint1-flags: -s */ + +/* expect+1: error: old style declaration; add 'int' [1] */ +implicit_global_variable; diff --git a/usr.bin/xlint/lint1/msg_001_c90.exp b/usr.bin/xlint/lint1/msg_001_c90.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_001_c90.exp @@ -0,0 +1 @@ +msg_001_c90.c(14): error: old style declaration; add 'int' [1] diff --git a/usr.bin/xlint/lint1/msg_002.c b/usr.bin/xlint/lint1/msg_002.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_002.c @@ -0,0 +1,29 @@ +/* $NetBSD: msg_002.c,v 1.5 2021/07/14 20:39:13 rillig Exp $ */ +# 3 "msg_002.c" + +// Test for message: empty declaration [2] + +/* expect+1: warning: empty declaration [2] */ +int; + +int local_variable; + +/* expect+1: warning: empty declaration [2] */ +const; + +void +cover_cgram_declaration(void) +{ + + /* expect+1: warning: typedef declares no type name [72] */ + typedef const; + + /* expect+1: warning: empty declaration [2] */ + const; + + /* expect+1: warning: typedef declares no type name [72] */ + typedef int; + + /* expect+1: warning: empty declaration [2] */ + int; +} diff --git a/usr.bin/xlint/lint1/msg_002.exp b/usr.bin/xlint/lint1/msg_002.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_002.exp @@ -0,0 +1,6 @@ +msg_002.c(7): warning: empty declaration [2] +msg_002.c(12): warning: empty declaration [2] +msg_002.c(19): warning: typedef declares no type name [72] +msg_002.c(22): warning: empty declaration [2] +msg_002.c(25): warning: typedef declares no type name [72] +msg_002.c(28): warning: empty declaration [2] diff --git a/usr.bin/xlint/lint1/msg_003.c b/usr.bin/xlint/lint1/msg_003.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_003.c @@ -0,0 +1,13 @@ +/* $NetBSD: msg_003.c,v 1.4 2021/02/28 12:40:00 rillig Exp $ */ +# 3 "msg_003.c" + +// Test for message: '%s' declared in argument declaration list [3] + +/*ARGSUSED*/ +void +example(declare_struct, declare_union, declare_enum) + struct struct_in_argument *declare_struct; /* expect: 3 */ + union union_in_argument *declare_union; /* expect: 3 */ + enum enum_in_argument *declare_enum; /* expect: 3 */ +{ +} diff --git a/usr.bin/xlint/lint1/msg_003.exp b/usr.bin/xlint/lint1/msg_003.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_003.exp @@ -0,0 +1,3 @@ +msg_003.c(9): warning: 'incomplete struct struct_in_argument' declared in argument declaration list [3] +msg_003.c(10): warning: 'incomplete union union_in_argument' declared in argument declaration list [3] +msg_003.c(11): warning: 'enum enum_in_argument' declared in argument declaration list [3] diff --git a/usr.bin/xlint/lint1/msg_004.c b/usr.bin/xlint/lint1/msg_004.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_004.c @@ -0,0 +1,21 @@ +/* $NetBSD: msg_004.c,v 1.5 2021/07/04 13:32:35 rillig Exp $ */ +# 3 "msg_004.c" + +// Test for message: illegal type combination [4] + +// Lint does not detect "two or more data types", but GCC does. +signed double signed_double; + +int ok_int; +double ok_double; +float _Complex ok_float_complex; + +int _Complex illegal_int_complex; /* expect: 4 *//* expect: 308 */ + +char enum { + CHAR +}; /* expect: 4 */ + +long struct { + int member; +}; /* expect: 4 */ diff --git a/usr.bin/xlint/lint1/msg_004.exp b/usr.bin/xlint/lint1/msg_004.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_004.exp @@ -0,0 +1,4 @@ +msg_004.c(13): error: invalid type for _Complex [308] +msg_004.c(13): error: illegal type combination [4] +msg_004.c(17): error: illegal type combination [4] +msg_004.c(21): error: illegal type combination [4] diff --git a/usr.bin/xlint/lint1/msg_005.c b/usr.bin/xlint/lint1/msg_005.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_005.c @@ -0,0 +1,8 @@ +/* $NetBSD: msg_005.c,v 1.3 2021/01/31 11:12:07 rillig Exp $ */ +# 3 "msg_005.c" + +// Test for message: modifying typedef with '%s'; only qualifiers allowed [5] + +typedef int number; +number long long_variable; /* expect: 5 */ +number const const_variable; diff --git a/usr.bin/xlint/lint1/msg_005.exp b/usr.bin/xlint/lint1/msg_005.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_005.exp @@ -0,0 +1 @@ +msg_005.c(7): warning: modifying typedef with 'long'; only qualifiers allowed [5] diff --git a/usr.bin/xlint/lint1/msg_006.c b/usr.bin/xlint/lint1/msg_006.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_006.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_006.c,v 1.4 2021/04/05 01:35:34 rillig Exp $ */ +# 3 "msg_006.c" + +// Test for message: use 'double' instead of 'long float' [6] + +long float x; /* expect: 6 *//* expect: 4 */ +double x; diff --git a/usr.bin/xlint/lint1/msg_006.exp b/usr.bin/xlint/lint1/msg_006.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_006.exp @@ -0,0 +1,2 @@ +msg_006.c(6): warning: use 'double' instead of 'long float' [6] +msg_006.c(6): error: illegal type combination [4] diff --git a/usr.bin/xlint/lint1/msg_007.c b/usr.bin/xlint/lint1/msg_007.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_007.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_007.c,v 1.3 2021/01/31 11:12:07 rillig Exp $ */ +# 3 "msg_007.c" + +// Test for message: only one storage class allowed [7] + +extern static void example(void); /* expect: 7 */ +extern extern_function(void); diff --git a/usr.bin/xlint/lint1/msg_007.exp b/usr.bin/xlint/lint1/msg_007.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_007.exp @@ -0,0 +1 @@ +msg_007.c(6): error: only one storage class allowed [7] diff --git a/usr.bin/xlint/lint1/msg_008.c b/usr.bin/xlint/lint1/msg_008.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_008.c @@ -0,0 +1,9 @@ +/* $NetBSD: msg_008.c,v 1.3 2021/01/31 11:12:07 rillig Exp $ */ +# 3 "msg_008.c" + +// Test for message: illegal storage class [8] + +typedef void +example(void) +{ /* expect: 8 */ +} diff --git a/usr.bin/xlint/lint1/msg_008.exp b/usr.bin/xlint/lint1/msg_008.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_008.exp @@ -0,0 +1 @@ +msg_008.c(8): error: illegal storage class [8] diff --git a/usr.bin/xlint/lint1/msg_009.c b/usr.bin/xlint/lint1/msg_009.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_009.c @@ -0,0 +1,9 @@ +/* $NetBSD: msg_009.c,v 1.3 2021/01/31 11:12:07 rillig Exp $ */ +# 3 "msg_009.c" + +// Test for message: only register valid as formal parameter storage class [9] + +extern void typedef_example(typedef int param); /* expect: 9 */ +extern void auto_example(auto int param); /* expect: 9 */ +extern void static_example(static int param); /* expect: 9 */ +extern void extern_example(extern int param); /* expect: 9 */ diff --git a/usr.bin/xlint/lint1/msg_009.exp b/usr.bin/xlint/lint1/msg_009.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_009.exp @@ -0,0 +1,4 @@ +msg_009.c(6): error: only register valid as formal parameter storage class [9] +msg_009.c(7): error: only register valid as formal parameter storage class [9] +msg_009.c(8): error: only register valid as formal parameter storage class [9] +msg_009.c(9): error: only register valid as formal parameter storage class [9] diff --git a/usr.bin/xlint/lint1/msg_010.c b/usr.bin/xlint/lint1/msg_010.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_010.c @@ -0,0 +1,36 @@ +/* $NetBSD: msg_010.c,v 1.4 2021/01/18 17:43:44 rillig Exp $ */ +# 3 "msg_010.c" + +// Test for message: duplicate '%s' [10] + +inline inline void /* expect: [10] */ +double_inline(void) +{ +} + +const const int /* expect: [10] */ +double_const(void) +{ + return 0; +} + +volatile volatile int /* expect: [10] */ +double_volatile(void) +{ + return 0; +} + +int +restrict_pointer(const int *restrict p) +{ + return *p; +} + +_Thread_local int thread_local_int; +_Thread_local int *pointer_to_thread_local; + +int +thread_local_parameter(_Thread_local int i) /* caught by the compiler */ +{ + return i; +} diff --git a/usr.bin/xlint/lint1/msg_010.exp b/usr.bin/xlint/lint1/msg_010.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_010.exp @@ -0,0 +1,3 @@ +msg_010.c(6): warning: duplicate 'inline' [10] +msg_010.c(11): warning: duplicate 'const' [10] +msg_010.c(17): warning: duplicate 'volatile' [10] diff --git a/usr.bin/xlint/lint1/msg_011.c b/usr.bin/xlint/lint1/msg_011.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_011.c @@ -0,0 +1,39 @@ +/* $NetBSD: msg_011.c,v 1.5 2021/08/26 19:23:25 rillig Exp $ */ +# 3 "msg_011.c" + +// Test for message: bit-field initializer out of range [11] + +void +example(void) +{ + struct { + signed int si: 3; + unsigned int ui: 3; + } s[] = { + /* expect+2: warning: bit-field initializer out of range [11] */ + /* expect+1: warning: initialization of unsigned with negative constant [221] */ + { -8, -8 }, + + /* expect+2: warning: bit-field initializer out of range [11] */ + /* expect+1: warning: initialization of unsigned with negative constant [221] */ + { -7, -7 }, + + /* expect+1: warning: initialization of unsigned with negative constant [221] */ + { -4, -4 }, + + /* expect+1: warning: initialization of unsigned with negative constant [221] */ + { -3, -3 }, + + { 3, 3 }, + + /* expect+1: warning: bit-field initializer out of range [11] */ + { 4, 4 }, + + /* expect+1: warning: bit-field initializer out of range [11] */ + { 7, 7 }, + + /* expect+2: warning: bit-field initializer does not fit [180] */ + /* expect+1: warning: bit-field initializer does not fit [180] */ + { 8, 8 }, + }; +} diff --git a/usr.bin/xlint/lint1/msg_011.exp b/usr.bin/xlint/lint1/msg_011.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_011.exp @@ -0,0 +1,10 @@ +msg_011.c(15): warning: bit-field initializer out of range [11] +msg_011.c(15): warning: initialization of unsigned with negative constant [221] +msg_011.c(19): warning: bit-field initializer out of range [11] +msg_011.c(19): warning: initialization of unsigned with negative constant [221] +msg_011.c(22): warning: initialization of unsigned with negative constant [221] +msg_011.c(25): warning: initialization of unsigned with negative constant [221] +msg_011.c(30): warning: bit-field initializer out of range [11] +msg_011.c(33): warning: bit-field initializer out of range [11] +msg_011.c(37): warning: bit-field initializer does not fit [180] +msg_011.c(37): warning: bit-field initializer does not fit [180] diff --git a/usr.bin/xlint/lint1/msg_012.c b/usr.bin/xlint/lint1/msg_012.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_012.c @@ -0,0 +1,12 @@ +/* $NetBSD: msg_012.c,v 1.3 2021/08/26 19:23:25 rillig Exp $ */ +# 3 "msg_012.c" + +// Test for message: compiler takes size of function [12] +/* This message is not used. */ + +unsigned long +example(void) +{ + /* expect+1: error: cannot take size/alignment of function [144] */ + return sizeof(example); +} diff --git a/usr.bin/xlint/lint1/msg_012.exp b/usr.bin/xlint/lint1/msg_012.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_012.exp @@ -0,0 +1 @@ +msg_012.c(11): error: cannot take size/alignment of function [144] diff --git a/usr.bin/xlint/lint1/msg_013.c b/usr.bin/xlint/lint1/msg_013.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_013.c @@ -0,0 +1,14 @@ +/* $NetBSD: msg_013.c,v 1.3 2021/08/26 19:23:25 rillig Exp $ */ +# 3 "msg_013.c" + +// Test for message: incomplete enum type: %s [13] + +enum tag; + +/* XXX: why ''? */ +/* expect+1: warning: incomplete enum type: [13] */ +void function(enum tag); + +enum tag { + CONSTANT +}; diff --git a/usr.bin/xlint/lint1/msg_013.exp b/usr.bin/xlint/lint1/msg_013.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_013.exp @@ -0,0 +1 @@ +msg_013.c(10): warning: incomplete enum type: [13] diff --git a/usr.bin/xlint/lint1/msg_014.c b/usr.bin/xlint/lint1/msg_014.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_014.c @@ -0,0 +1,12 @@ +/* $NetBSD: msg_014.c,v 1.3 2021/08/26 19:23:25 rillig Exp $ */ +# 3 "msg_014.c" + +// Test for message: compiler takes alignment of function [14] + +typedef void function(void); + +/* expect+1: error: cannot take size/alignment of function [144] */ +unsigned long alignof_function = __alignof__(function); + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_014.exp b/usr.bin/xlint/lint1/msg_014.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_014.exp @@ -0,0 +1,2 @@ +msg_014.c(9): error: cannot take size/alignment of function [144] +msg_014.c(11): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_015.c b/usr.bin/xlint/lint1/msg_015.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_015.c @@ -0,0 +1,9 @@ +/* $NetBSD: msg_015.c,v 1.3 2021/08/26 19:23:25 rillig Exp $ */ +# 3 "msg_015.c" + +// Test for message: function returns illegal type [15] + +typedef int array[5]; + +/* expect+1: error: function returns illegal type [15] */ +array invalid(void); diff --git a/usr.bin/xlint/lint1/msg_015.exp b/usr.bin/xlint/lint1/msg_015.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_015.exp @@ -0,0 +1 @@ +msg_015.c(9): error: function returns illegal type [15] diff --git a/usr.bin/xlint/lint1/msg_016.c b/usr.bin/xlint/lint1/msg_016.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_016.c @@ -0,0 +1,16 @@ +/* $NetBSD: msg_016.c,v 1.3 2021/08/26 19:23:25 rillig Exp $ */ +# 3 "msg_016.c" + +// Test for message: array of function is illegal [16] + +typedef void function(void); + +/* expect+1: error: array of function is illegal [16] */ +function functions[] = { + /* + * XXX: The below warning should not assume that function is an + * integer type. + */ + /* expect+1: warning: illegal combination of integer (int) and pointer (pointer to void) [183] */ + (void *)0, +}; diff --git a/usr.bin/xlint/lint1/msg_016.exp b/usr.bin/xlint/lint1/msg_016.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_016.exp @@ -0,0 +1,2 @@ +msg_016.c(9): error: array of function is illegal [16] +msg_016.c(15): warning: illegal combination of integer (int) and pointer (pointer to void) [183] diff --git a/usr.bin/xlint/lint1/msg_017.c b/usr.bin/xlint/lint1/msg_017.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_017.c @@ -0,0 +1,18 @@ +/* $NetBSD: msg_017.c,v 1.3 2021/08/26 19:23:25 rillig Exp $ */ +# 3 "msg_017.c" + +// Test for message: null dimension [17] + +/* expect+1: warning: empty array declaration: empty_array_var [190] */ +int empty_array_var[0]; + +typedef int empty_array_type[0]; +/* expect+1: warning: empty array declaration: typedef_var [190] */ +empty_array_type typedef_var; + +struct s { + int empty_array_member[0]; + /* expect+1: error: null dimension [17] */ + int empty_multi_array_member[0][0]; + int other_member; +}; diff --git a/usr.bin/xlint/lint1/msg_017.exp b/usr.bin/xlint/lint1/msg_017.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_017.exp @@ -0,0 +1,3 @@ +msg_017.c(16): error: null dimension [17] +msg_017.c(7): warning: empty array declaration: empty_array_var [190] +msg_017.c(11): warning: empty array declaration: typedef_var [190] diff --git a/usr.bin/xlint/lint1/msg_018.c b/usr.bin/xlint/lint1/msg_018.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_018.c @@ -0,0 +1,13 @@ +/* $NetBSD: msg_018.c,v 1.3 2021/08/26 19:23:25 rillig Exp $ */ +# 3 "msg_018.c" + +// Test for message: illegal use of 'void' [18] + +/* expect+1: error: void type for 'x' [19] */ +void x; + +/* expect+1: error: cannot take size/alignment of void [146] */ +unsigned long sizeof_void = sizeof(void); + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_018.exp b/usr.bin/xlint/lint1/msg_018.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_018.exp @@ -0,0 +1,3 @@ +msg_018.c(7): error: void type for 'x' [19] +msg_018.c(10): error: cannot take size/alignment of void [146] +msg_018.c(12): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_019.c b/usr.bin/xlint/lint1/msg_019.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_019.c @@ -0,0 +1,14 @@ +/* $NetBSD: msg_019.c,v 1.4 2021/04/05 01:35:34 rillig Exp $ */ +# 3 "msg_019.c" + +// Test for message: void type for '%s' [19] + +void global_variable; /* expect: 19 */ + +static void unit_variable; /* expect: 19 *//* expect: 226 */ + +void +function(void parameter) /* expect: 61 *//* expect: 231 */ +{ + void local_variable; /* expect: 19 */ +} diff --git a/usr.bin/xlint/lint1/msg_019.exp b/usr.bin/xlint/lint1/msg_019.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_019.exp @@ -0,0 +1,6 @@ +msg_019.c(6): error: void type for 'global_variable' [19] +msg_019.c(8): error: void type for 'unit_variable' [19] +msg_019.c(11): error: void parameter cannot have name: parameter [61] +msg_019.c(13): error: void type for 'local_variable' [19] +msg_019.c(11): warning: argument 'parameter' unused in function 'function' [231] +msg_019.c(8): warning: static variable unit_variable unused [226] diff --git a/usr.bin/xlint/lint1/msg_020.c b/usr.bin/xlint/lint1/msg_020.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_020.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_020.c,v 1.3 2021/08/27 20:16:50 rillig Exp $ */ +# 3 "msg_020.c" + +// Test for message: negative array dimension (%d) [20] + +/* expect+1: error: negative array dimension (-3) [20] */ +int array[-3]; diff --git a/usr.bin/xlint/lint1/msg_020.exp b/usr.bin/xlint/lint1/msg_020.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_020.exp @@ -0,0 +1 @@ +msg_020.c(7): error: negative array dimension (-3) [20] diff --git a/usr.bin/xlint/lint1/msg_021.c b/usr.bin/xlint/lint1/msg_021.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_021.c @@ -0,0 +1,37 @@ +/* $NetBSD: msg_021.c,v 1.2 2021/01/31 09:48:47 rillig Exp $ */ +# 3 "msg_021.c" + +// Test for message: redeclaration of formal parameter %s [21] + +/*ARGSUSED*/ +void +old_style_with_duplicate_parameter(parameter, parameter) /* expect: 21 */ + int parameter; +{ /* expect: 32 */ +} + +void +old_style_with_duplicate_parameter_declaration(parameter) + int parameter; + int parameter; /* expect: 237 */ +{ +} + +void old_style_with_local_variable(parameter) + int parameter; +{ + int parameter; /* expect: 27 */ +} + +/*ARGSUSED*/ +void +prototype_with_duplicate_parameter(int param, int param) /* expect: 237 */ +{ + +} + +void +prototype_with_local_variable(int parameter) +{ + int parameter; /* expect: 27 */ +} diff --git a/usr.bin/xlint/lint1/msg_021.exp b/usr.bin/xlint/lint1/msg_021.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_021.exp @@ -0,0 +1,6 @@ +msg_021.c(8): error: redeclaration of formal parameter parameter [21] +msg_021.c(10): warning: argument type defaults to 'int': parameter [32] +msg_021.c(16): error: redeclaration of formal parameter parameter [237] +msg_021.c(23): error: redeclaration of parameter [27] +msg_021.c(28): error: redeclaration of formal parameter param [237] +msg_021.c(36): error: redeclaration of parameter [27] diff --git a/usr.bin/xlint/lint1/msg_022.c b/usr.bin/xlint/lint1/msg_022.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_022.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_022.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_022.c" + +// Test for message: incomplete or misplaced function definition [22] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_022.exp b/usr.bin/xlint/lint1/msg_022.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_022.exp @@ -0,0 +1 @@ +msg_022.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_023.c b/usr.bin/xlint/lint1/msg_023.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_023.c @@ -0,0 +1,13 @@ +/* $NetBSD: msg_023.c,v 1.4 2021/07/11 19:30:56 rillig Exp $ */ +# 3 "msg_023.c" + +// Test for message: undefined label '%s' [23] + +void +test(void) +{ + goto defined_label; +defined_label: + /* expect+1: warning: undefined label 'undefined_label' [23] */ + goto undefined_label; +} diff --git a/usr.bin/xlint/lint1/msg_023.exp b/usr.bin/xlint/lint1/msg_023.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_023.exp @@ -0,0 +1 @@ +msg_023.c(12): warning: undefined label 'undefined_label' [23] diff --git a/usr.bin/xlint/lint1/msg_024.c b/usr.bin/xlint/lint1/msg_024.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_024.c @@ -0,0 +1,16 @@ +/* $NetBSD: msg_024.c,v 1.3 2021/08/27 20:16:50 rillig Exp $ */ +# 3 "msg_024.c" + +// Test for message: cannot initialize function: %s [24] + +typedef void (function)(void); + +void +definition(void) +{ +} + +/* expect+3: error: cannot initialize function: fn [24] */ +/* The following message is strange but does not occur in practice. */ +/* expect+1: error: {}-enclosed initializer required [181] */ +function fn = definition; diff --git a/usr.bin/xlint/lint1/msg_024.exp b/usr.bin/xlint/lint1/msg_024.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_024.exp @@ -0,0 +1,2 @@ +msg_024.c(16): error: cannot initialize function: fn [24] +msg_024.c(16): error: {}-enclosed initializer required [181] diff --git a/usr.bin/xlint/lint1/msg_025.c b/usr.bin/xlint/lint1/msg_025.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_025.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_025.c,v 1.3 2021/08/27 20:16:50 rillig Exp $ */ +# 3 "msg_025.c" + +// Test for message: cannot initialize typedef: %s [25] + +/* expect+1: error: cannot initialize typedef: number [25] */ +typedef int number = 3; diff --git a/usr.bin/xlint/lint1/msg_025.exp b/usr.bin/xlint/lint1/msg_025.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_025.exp @@ -0,0 +1 @@ +msg_025.c(7): error: cannot initialize typedef: number [25] diff --git a/usr.bin/xlint/lint1/msg_026.c b/usr.bin/xlint/lint1/msg_026.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_026.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_026.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_026.c" + +// Test for message: cannot initialize extern declaration: %s [26] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_026.exp b/usr.bin/xlint/lint1/msg_026.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_026.exp @@ -0,0 +1 @@ +msg_026.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_027.c b/usr.bin/xlint/lint1/msg_027.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_027.c @@ -0,0 +1,8 @@ +/* $NetBSD: msg_027.c,v 1.3 2021/01/31 11:12:07 rillig Exp $ */ +# 3 "msg_027.c" + +// Test for message: redeclaration of %s [27] + +extern int identifier(void); + +extern double identifier(void); /* expect: 27 */ diff --git a/usr.bin/xlint/lint1/msg_027.exp b/usr.bin/xlint/lint1/msg_027.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_027.exp @@ -0,0 +1 @@ +msg_027.c(8): error: redeclaration of identifier [27] diff --git a/usr.bin/xlint/lint1/msg_028.c b/usr.bin/xlint/lint1/msg_028.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_028.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_028.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_028.c" + +// Test for message: redefinition of %s [28] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_028.exp b/usr.bin/xlint/lint1/msg_028.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_028.exp @@ -0,0 +1 @@ +msg_028.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_029.c b/usr.bin/xlint/lint1/msg_029.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_029.c @@ -0,0 +1,11 @@ +/* $NetBSD: msg_029.c,v 1.3 2021/01/31 11:12:07 rillig Exp $ */ +# 3 "msg_029.c" + +// Test for message: previously declared extern, becomes static: %s [29] + +extern int function(void); + +static int function(void) +{ /* expect: 29 */ + return function(); +} diff --git a/usr.bin/xlint/lint1/msg_029.exp b/usr.bin/xlint/lint1/msg_029.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_029.exp @@ -0,0 +1 @@ +msg_029.c(9): warning: previously declared extern, becomes static: function [29] diff --git a/usr.bin/xlint/lint1/msg_030.c b/usr.bin/xlint/lint1/msg_030.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_030.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_030.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_030.c" + +// Test for message: redeclaration of %s; ANSI C requires static [30] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_030.exp b/usr.bin/xlint/lint1/msg_030.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_030.exp @@ -0,0 +1 @@ +msg_030.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_031.c b/usr.bin/xlint/lint1/msg_031.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_031.c @@ -0,0 +1,20 @@ +/* $NetBSD: msg_031.c,v 1.6 2021/07/13 22:01:34 rillig Exp $ */ +# 3 "msg_031.c" + +// Test for message: '%s' has incomplete type '%s' [31] + +struct complete { + int dummy; +}; + +struct incomplete; /* expect: 233 */ + + +struct complete complete_var; + +/* expect+1: 'incomplete_var' has incomplete type 'incomplete struct incomplete' */ +struct incomplete incomplete_var; + + +/* expect+1: '' has incomplete type 'incomplete struct incomplete' [31] */ +void function(struct incomplete); diff --git a/usr.bin/xlint/lint1/msg_031.exp b/usr.bin/xlint/lint1/msg_031.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_031.exp @@ -0,0 +1,3 @@ +msg_031.c(20): error: '' has incomplete type 'incomplete struct incomplete' [31] +msg_031.c(10): warning: struct incomplete never defined [233] +msg_031.c(16): error: 'incomplete_var' has incomplete type 'incomplete struct incomplete' [31] diff --git a/usr.bin/xlint/lint1/msg_032.c b/usr.bin/xlint/lint1/msg_032.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_032.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_032.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_032.c" + +// Test for message: argument type defaults to 'int': %s [32] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_032.exp b/usr.bin/xlint/lint1/msg_032.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_032.exp @@ -0,0 +1 @@ +msg_032.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_033.c b/usr.bin/xlint/lint1/msg_033.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_033.c @@ -0,0 +1,13 @@ +/* $NetBSD: msg_033.c,v 1.3 2021/08/27 20:16:50 rillig Exp $ */ +# 3 "msg_033.c" + +// Test for message: duplicate member name: %s [33] + +/* lint1-extra-flags: -r */ + +struct { + /* Despite the option '-r', this location is not mentioned. */ + int member; + /* expect+1: error: duplicate member name: member [33] */ + double member; +}; diff --git a/usr.bin/xlint/lint1/msg_033.exp b/usr.bin/xlint/lint1/msg_033.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_033.exp @@ -0,0 +1 @@ +msg_033.c(12): error: duplicate member name: member [33] diff --git a/usr.bin/xlint/lint1/msg_034.c b/usr.bin/xlint/lint1/msg_034.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_034.c @@ -0,0 +1,28 @@ +/* $NetBSD: msg_034.c,v 1.5 2021/05/16 11:11:37 rillig Exp $ */ +# 3 "msg_034.c" + +// Test for message: nonportable bit-field type '%s' [34] + +/* No -g since GCC allows all integer types as bit-fields. */ +/* lint1-flags: -S -p -w */ + +/* + * C90 3.5.2.1 allows 'int', 'signed int', 'unsigned int' as bit-field types. + * + * C99 6.7.2.1 significantly changed the wording of the allowable types for + * bit-fields. For example, 6.7.2.1p4 does not mention plain 'int' at all. + * The rationale for C99 6.7.2.1 mentions plain int though, and it would have + * broken a lot of existing code to disallow plain 'int' as a bit-field type. + * Footnote 104 explicitly mentions plain 'int' as well and it even allows + * typedef-types for bit-fields. + */ +struct example { + /* expect+1: 34 */ + unsigned short ushort: 1; + + /* expect+1: 344 */ + int plain_int: 1; + + signed int signed_int: 1; + unsigned int portable: 1; +}; diff --git a/usr.bin/xlint/lint1/msg_034.exp b/usr.bin/xlint/lint1/msg_034.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_034.exp @@ -0,0 +1,2 @@ +msg_034.c(21): warning: nonportable bit-field type 'unsigned short' [34] +msg_034.c(24): warning: bit-field of type plain 'int' has implementation-defined signedness [344] diff --git a/usr.bin/xlint/lint1/msg_035.c b/usr.bin/xlint/lint1/msg_035.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_035.c @@ -0,0 +1,64 @@ +/* $NetBSD: msg_035.c,v 1.9 2021/05/02 21:22:09 rillig Exp $ */ +# 3 "msg_035.c" + +// Test for message: illegal bit-field type '%s' [35] + +/* Omit -g, see gcc_bit_field_types.c. */ +/* lint1-flags: -Sw */ + +/* + * In traditional C, only unsigned int is a portable bit-field type. + * + * In C89, only int, signed int and unsigned int are allowed (3.5.2.1p7). + * + * In C99 and C11, only _Bool, signed int and unsigned int are allowed, + * plus implementation-defined types (6.7.2.1p5). + */ + +typedef struct { + int dummy; +} example_struct; + +typedef union { + int dummy; +} example_union; + +typedef enum { + NO, YES +} example_enum; + +typedef void (example_function)(int, const char *); + +/* Try all types from tspec_t. */ +struct example { + signed signed_flag: 1; + unsigned unsigned_flag: 1; + _Bool boolean_flag: 1; + char char_flag: 1; + signed char signed_char_flag: 1; + unsigned char unsigned_char_flag: 1; + short short_flag: 1; + unsigned short unsigned_short_flag: 1; + int int_flag: 1; + unsigned int unsigned_int_flag: 1; + long long_flag: 1; /* expect: 35 */ + unsigned long unsigned_long_flag: 1; /* expect: 35 */ + long long long_long_flag: 1; /* expect: 35 */ + unsigned long long unsigned_long_long_flag: 1; /* expect: 35 */ + /* __int128_t omitted since it is not always defined */ + /* __uint128_t omitted since it is not always defined */ + float float_flag: 1; /* expect: 35 */ + double double_flag: 1; /* expect: 35 */ + long double long_double_flag: 1; /* expect: 35 */ + void void_flag: 1; /* expect: 19 *//* expect: 37 */ + example_struct struct_flag: 1; /* expect: 35 */ + example_union union_flag: 1; /* expect: 35 */ + example_enum enum_flag: 1; + void *pointer_flag: 1; /* expect: 35 */ + unsigned int array_flag[4]: 1; /* expect: 35 */ + example_function function_flag: 1; /* expect: 35 */ + _Complex complex_flag: 1; /* expect: 35 *//* expect: 308 */ + float _Complex float_complex_flag: 1; /* expect: 35 */ + double _Complex double_complex_flag: 1; /* expect: 35 */ + long double _Complex long_double_complex_flag: 1; /* expect: 35 */ +}; diff --git a/usr.bin/xlint/lint1/msg_035.exp b/usr.bin/xlint/lint1/msg_035.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_035.exp @@ -0,0 +1,19 @@ +msg_035.c(44): warning: illegal bit-field type 'long' [35] +msg_035.c(45): warning: illegal bit-field type 'unsigned long' [35] +msg_035.c(46): warning: illegal bit-field type 'long long' [35] +msg_035.c(47): warning: illegal bit-field type 'unsigned long long' [35] +msg_035.c(50): warning: illegal bit-field type 'float' [35] +msg_035.c(51): warning: illegal bit-field type 'double' [35] +msg_035.c(52): warning: illegal bit-field type 'long double' [35] +msg_035.c(53): error: void type for 'void_flag' [19] +msg_035.c(53): error: zero size bit-field [37] +msg_035.c(54): warning: illegal bit-field type 'struct typedef example_struct' [35] +msg_035.c(55): warning: illegal bit-field type 'union typedef example_union' [35] +msg_035.c(57): warning: illegal bit-field type 'pointer to void' [35] +msg_035.c(58): warning: illegal bit-field type 'array[4] of unsigned int' [35] +msg_035.c(59): warning: illegal bit-field type 'function(int, pointer to const char) returning void' [35] +msg_035.c(60): error: invalid type for _Complex [308] +msg_035.c(60): warning: illegal bit-field type 'double _Complex' [35] +msg_035.c(61): warning: illegal bit-field type 'float _Complex' [35] +msg_035.c(62): warning: illegal bit-field type 'double _Complex' [35] +msg_035.c(63): warning: illegal bit-field type 'long double _Complex' [35] diff --git a/usr.bin/xlint/lint1/msg_036.c b/usr.bin/xlint/lint1/msg_036.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_036.c @@ -0,0 +1,10 @@ +/* $NetBSD: msg_036.c,v 1.3 2021/01/31 11:12:07 rillig Exp $ */ +# 3 "msg_036.c" + +// Test for message: illegal bit-field size: %d [36] + +struct example { + unsigned int too_large: 100000; /* expect: 36 */ + unsigned int negative: -1; /* expect: 36 */ + unsigned int ok: 3; +}; diff --git a/usr.bin/xlint/lint1/msg_036.exp b/usr.bin/xlint/lint1/msg_036.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_036.exp @@ -0,0 +1,2 @@ +msg_036.c(7): error: illegal bit-field size: 160 [36] +msg_036.c(8): error: illegal bit-field size: 255 [36] diff --git a/usr.bin/xlint/lint1/msg_037.c b/usr.bin/xlint/lint1/msg_037.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_037.c @@ -0,0 +1,9 @@ +/* $NetBSD: msg_037.c,v 1.3 2021/01/31 11:12:07 rillig Exp $ */ +# 3 "msg_037.c" + +// Test for message: zero size bit-field [37] + +struct example { + unsigned int zero: 0; /* expect: 37 */ + unsigned int ok: 3; +}; diff --git a/usr.bin/xlint/lint1/msg_037.exp b/usr.bin/xlint/lint1/msg_037.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_037.exp @@ -0,0 +1 @@ +msg_037.c(7): error: zero size bit-field [37] diff --git a/usr.bin/xlint/lint1/msg_038.c b/usr.bin/xlint/lint1/msg_038.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_038.c @@ -0,0 +1,11 @@ +/* $NetBSD: msg_038.c,v 1.3 2021/08/27 20:16:50 rillig Exp $ */ +# 3 "msg_038.c" + +// Test for message: function illegal in structure or union [38] + +typedef void (function)(void); + +struct { + /* expect+1: error: function illegal in structure or union [38] */ + function fn; +} s; diff --git a/usr.bin/xlint/lint1/msg_038.exp b/usr.bin/xlint/lint1/msg_038.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_038.exp @@ -0,0 +1 @@ +msg_038.c(10): error: function illegal in structure or union [38] diff --git a/usr.bin/xlint/lint1/msg_039.c b/usr.bin/xlint/lint1/msg_039.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_039.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_039.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_039.c" + +// Test for message: zero sized array in struct is a C99 extension: %s [39] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_039.exp b/usr.bin/xlint/lint1/msg_039.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_039.exp @@ -0,0 +1 @@ +msg_039.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_040.c b/usr.bin/xlint/lint1/msg_040.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_040.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_040.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_040.c" + +// Test for message: unknown size: %s [40] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_040.exp b/usr.bin/xlint/lint1/msg_040.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_040.exp @@ -0,0 +1 @@ +msg_040.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_041.c b/usr.bin/xlint/lint1/msg_041.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_041.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_041.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_041.c" + +// Test for message: illegal use of bit-field [41] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_041.exp b/usr.bin/xlint/lint1/msg_041.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_041.exp @@ -0,0 +1 @@ +msg_041.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_042.c b/usr.bin/xlint/lint1/msg_042.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_042.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_042.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_042.c" + +// Test for message: forward reference to enum type [42] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_042.exp b/usr.bin/xlint/lint1/msg_042.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_042.exp @@ -0,0 +1 @@ +msg_042.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_043.c b/usr.bin/xlint/lint1/msg_043.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_043.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_043.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_043.c" + +// Test for message: redefinition hides earlier one: %s [43] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_043.exp b/usr.bin/xlint/lint1/msg_043.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_043.exp @@ -0,0 +1 @@ +msg_043.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_044.c b/usr.bin/xlint/lint1/msg_044.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_044.c @@ -0,0 +1,15 @@ +/* $NetBSD: msg_044.c,v 1.3 2021/08/26 19:23:25 rillig Exp $ */ +# 3 "msg_044.c" + +// Test for message: declaration introduces new type in ANSI C: %s %s [44] + +/* expect+1: warning: struct tag never defined [233] */ +struct tag; + +void declaration(struct tag *); + +void definition(void) { + /* expect+2: warning: declaration introduces new type in ANSI C: struct tag [44] */ + /* expect+1: warning: struct tag never defined [233] */ + struct tag; +} diff --git a/usr.bin/xlint/lint1/msg_044.exp b/usr.bin/xlint/lint1/msg_044.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_044.exp @@ -0,0 +1,3 @@ +msg_044.c(14): warning: declaration introduces new type in ANSI C: struct tag [44] +msg_044.c(14): warning: struct tag never defined [233] +msg_044.c(7): warning: struct tag never defined [233] diff --git a/usr.bin/xlint/lint1/msg_045.c b/usr.bin/xlint/lint1/msg_045.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_045.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_045.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_045.c" + +// Test for message: base type is really '%s %s' [45] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_045.exp b/usr.bin/xlint/lint1/msg_045.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_045.exp @@ -0,0 +1 @@ +msg_045.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_046.c b/usr.bin/xlint/lint1/msg_046.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_046.c @@ -0,0 +1,36 @@ +/* $NetBSD: msg_046.c,v 1.4 2021/08/16 06:49:57 rillig Exp $ */ +# 3 "msg_046.c" + +// Test for message: %s tag '%s' redeclared as %s [46] + +/* expect+1: warning: struct tag1 never defined [233] */ +struct tag1; +/* expect+2: error: struct tag 'tag1' redeclared as union [46] */ +/* expect+1: warning: union tag1 never defined [234] */ +union tag1; + +/* expect+1: warning: union tag2 never defined [234] */ +union tag2; +/* expect+2: error: union tag 'tag2' redeclared as enum [46] */ +/* expect+1: warning: enum tag2 never defined [235] */ +enum tag2; + +/* expect+1: warning: enum tag3 never defined [235] */ +enum tag3; +/* expect+2: error: enum tag 'tag3' redeclared as struct [46] */ +/* expect+1: warning: struct tag3 never defined [233] */ +struct tag3; + +/* expect+2: error: union tag 'tag1' redeclared as struct [46] */ +/* expect+1: warning: struct tag1 never defined [233] */ +struct tag1 *use_tag1(void); +/* expect+2: error: enum tag 'tag2' redeclared as union [46] */ +/* expect+1: warning: union tag2 never defined [234] */ +union tag2 *use_tag2(void); +/* expect+2: error: struct tag 'tag3' redeclared as enum [46] */ +/* expect+1: warning: enum tag3 never defined [235] */ +enum tag3 *use_tag3(void); + +/* expect+2: error: struct tag 'tag1' redeclared as union [46] */ +/* expect+1: warning: union tag1 never defined [234] */ +union tag1 *mismatch_tag1(void); diff --git a/usr.bin/xlint/lint1/msg_046.exp b/usr.bin/xlint/lint1/msg_046.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_046.exp @@ -0,0 +1,17 @@ +msg_046.c(10): error: struct tag 'tag1' redeclared as union [46] +msg_046.c(16): error: union tag 'tag2' redeclared as enum [46] +msg_046.c(22): error: enum tag 'tag3' redeclared as struct [46] +msg_046.c(26): error: union tag 'tag1' redeclared as struct [46] +msg_046.c(29): error: enum tag 'tag2' redeclared as union [46] +msg_046.c(32): error: struct tag 'tag3' redeclared as enum [46] +msg_046.c(36): error: struct tag 'tag1' redeclared as union [46] +msg_046.c(7): warning: struct tag1 never defined [233] +msg_046.c(10): warning: union tag1 never defined [234] +msg_046.c(13): warning: union tag2 never defined [234] +msg_046.c(16): warning: enum tag2 never defined [235] +msg_046.c(19): warning: enum tag3 never defined [235] +msg_046.c(22): warning: struct tag3 never defined [233] +msg_046.c(26): warning: struct tag1 never defined [233] +msg_046.c(29): warning: union tag2 never defined [234] +msg_046.c(32): warning: enum tag3 never defined [235] +msg_046.c(36): warning: union tag1 never defined [234] diff --git a/usr.bin/xlint/lint1/msg_047.c b/usr.bin/xlint/lint1/msg_047.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_047.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_047.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_047.c" + +// Test for message: zero sized %s is a C9X feature [47] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_047.exp b/usr.bin/xlint/lint1/msg_047.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_047.exp @@ -0,0 +1 @@ +msg_047.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_048.c b/usr.bin/xlint/lint1/msg_048.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_048.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_048.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_048.c" + +// Test for message: overflow in enumeration values: %s [48] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_048.exp b/usr.bin/xlint/lint1/msg_048.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_048.exp @@ -0,0 +1 @@ +msg_048.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_049.c b/usr.bin/xlint/lint1/msg_049.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_049.c @@ -0,0 +1,27 @@ +/* $NetBSD: msg_049.c,v 1.3 2021/08/27 20:16:50 rillig Exp $ */ +# 3 "msg_049.c" + +/* Test for message: anonymous struct/union members is a C9X feature [49] */ + +/* lint1-flags: -sw */ + +/* + * FIXME: C99 does not allow anonymous struct/union members, that's a GCC + * extension that got incorporated into C11. + */ + +struct { + unsigned int flag: 1; + + /* + * This is an anonymous struct/union member, but that's not what + * message 49 is about. + */ + unsigned int :0; + + union { + int int_value; + void *pointer_value; + }; + /* expect-1: warning: anonymous struct/union members is a C9X feature [49] */ +} s; diff --git a/usr.bin/xlint/lint1/msg_049.exp b/usr.bin/xlint/lint1/msg_049.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_049.exp @@ -0,0 +1 @@ +msg_049.c(25): warning: anonymous struct/union members is a C9X feature [49] diff --git a/usr.bin/xlint/lint1/msg_050.c b/usr.bin/xlint/lint1/msg_050.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_050.c @@ -0,0 +1,13 @@ +/* $NetBSD: msg_050.c,v 1.3 2021/01/31 11:12:07 rillig Exp $ */ +# 3 "msg_050.c" + +/* Test for message: a function is declared as an argument: %s [50] */ + +/* lint1-flags: -Stw */ + +typedef void (function)(); + +void example(f) /* expect: 231 */ + function f; /* expect: 50 */ +{ +} diff --git a/usr.bin/xlint/lint1/msg_050.exp b/usr.bin/xlint/lint1/msg_050.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_050.exp @@ -0,0 +1,2 @@ +msg_050.c(11): warning: a function is declared as an argument: f [50] +msg_050.c(10): warning: argument 'f' unused in function 'example' [231] diff --git a/usr.bin/xlint/lint1/msg_051.c b/usr.bin/xlint/lint1/msg_051.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_051.c @@ -0,0 +1,13 @@ +/* $NetBSD: msg_051.c,v 1.4 2021/04/05 01:35:34 rillig Exp $ */ +# 3 "msg_051.c" + +// Test for message: parameter mismatch: %d declared, %d defined [51] + +void +example(int, int); + +void +example(a, b, c) /* expect: 231 *//* expect: 231 *//* expect: 231 */ + int a, b, c; +{ /* expect: 51 */ +} diff --git a/usr.bin/xlint/lint1/msg_051.exp b/usr.bin/xlint/lint1/msg_051.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_051.exp @@ -0,0 +1,4 @@ +msg_051.c(12): error: parameter mismatch: 2 declared, 3 defined [51] +msg_051.c(10): warning: argument 'a' unused in function 'example' [231] +msg_051.c(10): warning: argument 'b' unused in function 'example' [231] +msg_051.c(10): warning: argument 'c' unused in function 'example' [231] diff --git a/usr.bin/xlint/lint1/msg_052.c b/usr.bin/xlint/lint1/msg_052.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_052.c @@ -0,0 +1,12 @@ +/* $NetBSD: msg_052.c,v 1.3 2021/08/27 20:16:50 rillig Exp $ */ +# 3 "msg_052.c" + +// Test for message: cannot initialize parameter: %s [52] + +int +definition(i) + /* expect+1: error: cannot initialize parameter: i [52] */ + int i = 3; +{ + return i; +} diff --git a/usr.bin/xlint/lint1/msg_052.exp b/usr.bin/xlint/lint1/msg_052.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_052.exp @@ -0,0 +1 @@ +msg_052.c(9): error: cannot initialize parameter: i [52] diff --git a/usr.bin/xlint/lint1/msg_053.c b/usr.bin/xlint/lint1/msg_053.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_053.c @@ -0,0 +1,12 @@ +/* $NetBSD: msg_053.c,v 1.3 2021/08/27 20:16:50 rillig Exp $ */ +# 3 "msg_053.c" + +// Test for message: declared argument %s is missing [53] + +oldstyle(argument) + int argument; + /* expect+1: error: declared argument extra_argument is missing [53] */ + int extra_argument; +{ + return argument; +} diff --git a/usr.bin/xlint/lint1/msg_053.exp b/usr.bin/xlint/lint1/msg_053.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_053.exp @@ -0,0 +1 @@ +msg_053.c(9): error: declared argument extra_argument is missing [53] diff --git a/usr.bin/xlint/lint1/msg_054.c b/usr.bin/xlint/lint1/msg_054.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_054.c @@ -0,0 +1,12 @@ +/* $NetBSD: msg_054.c,v 1.3 2021/01/31 11:12:07 rillig Exp $ */ +# 3 "msg_054.c" + +/* Test for message: trailing ',' prohibited in enum declaration [54] */ + +/* lint1-flags: -tw */ + +enum color { + RED, + GREEN, + BLUE, +}; /* expect: 54 */ diff --git a/usr.bin/xlint/lint1/msg_054.exp b/usr.bin/xlint/lint1/msg_054.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_054.exp @@ -0,0 +1 @@ +msg_054.c(12): warning: trailing ',' prohibited in enum declaration [54] diff --git a/usr.bin/xlint/lint1/msg_055.c b/usr.bin/xlint/lint1/msg_055.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_055.c @@ -0,0 +1,8 @@ +/* $NetBSD: msg_055.c,v 1.3 2021/01/31 11:12:07 rillig Exp $ */ +# 3 "msg_055.c" + +// Test for message: integral constant expression expected [55] + +enum color { + WHITE = 1.0 +}; /* expect: 55 */ diff --git a/usr.bin/xlint/lint1/msg_055.exp b/usr.bin/xlint/lint1/msg_055.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_055.exp @@ -0,0 +1 @@ +msg_055.c(8): error: integral constant expression expected [55] diff --git a/usr.bin/xlint/lint1/msg_056.c b/usr.bin/xlint/lint1/msg_056.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_056.c @@ -0,0 +1,8 @@ +/* $NetBSD: msg_056.c,v 1.3 2021/01/31 11:12:07 rillig Exp $ */ +# 3 "msg_056.c" + +// Test for message: integral constant too large [56] + +enum color { + WHITE = 0xFFFFFFFFFFFFFFFFFFFF /* expect: 252 */ +}; /* expect: 56 */ diff --git a/usr.bin/xlint/lint1/msg_056.exp b/usr.bin/xlint/lint1/msg_056.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_056.exp @@ -0,0 +1,2 @@ +msg_056.c(7): warning: integer constant out of range [252] +msg_056.c(8): warning: integral constant too large [56] diff --git a/usr.bin/xlint/lint1/msg_057.c b/usr.bin/xlint/lint1/msg_057.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_057.c @@ -0,0 +1,14 @@ +/* $NetBSD: msg_057.c,v 1.4 2021/04/05 01:35:34 rillig Exp $ */ +# 3 "msg_057.c" + +// Test for message: enumeration constant hides parameter: %s [57] + +long +rgb(int red, int green, int blue) /* expect: 231 *//* expect: 231 *//* expect: 231 */ +{ + enum color { + red, green, blue /* expect: 57 *//* expect: 57 */ + }; /* expect: 57 */ + + return red + green + blue; +} diff --git a/usr.bin/xlint/lint1/msg_057.exp b/usr.bin/xlint/lint1/msg_057.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_057.exp @@ -0,0 +1,6 @@ +msg_057.c(10): warning: enumeration constant hides parameter: red [57] +msg_057.c(10): warning: enumeration constant hides parameter: green [57] +msg_057.c(11): warning: enumeration constant hides parameter: blue [57] +msg_057.c(7): warning: argument 'red' unused in function 'rgb' [231] +msg_057.c(7): warning: argument 'green' unused in function 'rgb' [231] +msg_057.c(7): warning: argument 'blue' unused in function 'rgb' [231] diff --git a/usr.bin/xlint/lint1/msg_058.c b/usr.bin/xlint/lint1/msg_058.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_058.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_058.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_058.c" + +// Test for message: type does not match prototype: %s [58] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_058.exp b/usr.bin/xlint/lint1/msg_058.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_058.exp @@ -0,0 +1 @@ +msg_058.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_059.c b/usr.bin/xlint/lint1/msg_059.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_059.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_059.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_059.c" + +// Test for message: formal parameter lacks name: param #%d [59] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_059.exp b/usr.bin/xlint/lint1/msg_059.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_059.exp @@ -0,0 +1 @@ +msg_059.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_060.c b/usr.bin/xlint/lint1/msg_060.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_060.c @@ -0,0 +1,8 @@ +/* $NetBSD: msg_060.c,v 1.4 2021/04/05 01:35:34 rillig Exp $ */ +# 3 "msg_060.c" + +// Test for message: void must be sole parameter [60] + +void example_1(void); +void example_2(int, void); /* expect: 60 */ +void example_3(void, void, void); /* expect: 60 *//* expect: 60 *//* expect: 60 */ diff --git a/usr.bin/xlint/lint1/msg_060.exp b/usr.bin/xlint/lint1/msg_060.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_060.exp @@ -0,0 +1,4 @@ +msg_060.c(7): error: void must be sole parameter [60] +msg_060.c(8): error: void must be sole parameter [60] +msg_060.c(8): error: void must be sole parameter [60] +msg_060.c(8): error: void must be sole parameter [60] diff --git a/usr.bin/xlint/lint1/msg_061.c b/usr.bin/xlint/lint1/msg_061.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_061.c @@ -0,0 +1,6 @@ +/* $NetBSD: msg_061.c,v 1.3 2021/01/31 11:12:07 rillig Exp $ */ +# 3 "msg_061.c" + +// Test for message: void parameter cannot have name: %s [61] + +void example(void arg); /* expect: 61 */ diff --git a/usr.bin/xlint/lint1/msg_061.exp b/usr.bin/xlint/lint1/msg_061.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_061.exp @@ -0,0 +1 @@ +msg_061.c(6): error: void parameter cannot have name: arg [61] diff --git a/usr.bin/xlint/lint1/msg_062.c b/usr.bin/xlint/lint1/msg_062.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_062.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_062.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_062.c" + +// Test for message: function prototype parameters must have types [62] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_062.exp b/usr.bin/xlint/lint1/msg_062.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_062.exp @@ -0,0 +1 @@ +msg_062.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_063.c b/usr.bin/xlint/lint1/msg_063.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_063.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_063.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_063.c" + +// Test for message: prototype does not match old-style definition [63] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_063.exp b/usr.bin/xlint/lint1/msg_063.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_063.exp @@ -0,0 +1 @@ +msg_063.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_064.c b/usr.bin/xlint/lint1/msg_064.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_064.c @@ -0,0 +1,16 @@ +/* $NetBSD: msg_064.c,v 1.3 2021/07/12 18:00:36 rillig Exp $ */ +# 3 "msg_064.c" + +// Test for message: ()-less function definition [64] + +typedef int (function)(void); + +/* + * Even though typedef_function has type function, this construction is not + * allowed. A function definition must always look like a function + * definition, and that includes the parentheses for the arguments or + * parameters. + */ +function typedef_function { + /* expect-1: error: ()-less function definition [64] */ +} diff --git a/usr.bin/xlint/lint1/msg_064.exp b/usr.bin/xlint/lint1/msg_064.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_064.exp @@ -0,0 +1 @@ +msg_064.c(14): error: ()-less function definition [64] diff --git a/usr.bin/xlint/lint1/msg_065.c b/usr.bin/xlint/lint1/msg_065.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_065.c @@ -0,0 +1,18 @@ +/* $NetBSD: msg_065.c,v 1.3 2021/07/13 21:50:05 rillig Exp $ */ +# 3 "msg_065.c" + +// Test for message: %s has no named members [65] + +struct ok { + int member; +}; + +/* XXX: should generate a warning as well. */ +struct empty { +}; + +struct only_unnamed_members { + unsigned int :14; + unsigned int :0; +}; +/* expect-1: warning: structure has no named members [65] */ diff --git a/usr.bin/xlint/lint1/msg_065.exp b/usr.bin/xlint/lint1/msg_065.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_065.exp @@ -0,0 +1 @@ +msg_065.c(17): warning: structure has no named members [65] diff --git a/usr.bin/xlint/lint1/msg_066.c b/usr.bin/xlint/lint1/msg_066.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_066.c @@ -0,0 +1,16 @@ +/* $NetBSD: msg_066.c,v 1.5 2021/08/26 19:23:25 rillig Exp $ */ +# 3 "msg_066.c" + +// Test for message: syntax requires ';' after last struct/union member [66] +/* This message is not used. */ + +/* + * This message was removed in cgram.y 1.328 from 2021-07-15 because all + * C standards and even K&R require a semicolon. + */ + +struct number { + int value +}; +/* expect-1: syntax error '}' [249] */ +/* expect+1: error: cannot recover from previous errors [224] */ diff --git a/usr.bin/xlint/lint1/msg_066.exp b/usr.bin/xlint/lint1/msg_066.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_066.exp @@ -0,0 +1,2 @@ +msg_066.c(14): error: syntax error '}' [249] +msg_066.c(17): error: cannot recover from previous errors [224] diff --git a/usr.bin/xlint/lint1/msg_067.c b/usr.bin/xlint/lint1/msg_067.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_067.c @@ -0,0 +1,20 @@ +/* $NetBSD: msg_067.c,v 1.4 2021/07/13 22:01:34 rillig Exp $ */ +# 3 "msg_067.c" + +// Test for message: cannot return incomplete type [67] + +/* expect+1: warning: struct incomplete never defined [233] */ +struct incomplete; + +struct incomplete function_declaration(void); + +struct incomplete +function_definition(void) +/* expect+1: error: cannot return incomplete type [67] */ +{ + /* expect+1: error: 'r' has incomplete type 'incomplete struct incomplete' [31] */ + struct incomplete r; + + /* expect+1: error: cannot return incomplete type [212] */ + return r; +} diff --git a/usr.bin/xlint/lint1/msg_067.exp b/usr.bin/xlint/lint1/msg_067.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_067.exp @@ -0,0 +1,4 @@ +msg_067.c(14): error: cannot return incomplete type [67] +msg_067.c(16): error: 'r' has incomplete type 'incomplete struct incomplete' [31] +msg_067.c(19): error: cannot return incomplete type [212] +msg_067.c(7): warning: struct incomplete never defined [233] diff --git a/usr.bin/xlint/lint1/msg_068.c b/usr.bin/xlint/lint1/msg_068.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_068.c @@ -0,0 +1,8 @@ +/* $NetBSD: msg_068.c,v 1.3 2021/01/31 11:12:07 rillig Exp $ */ +# 3 "msg_068.c" + +// Test for message: typedef already qualified with '%s' [68] + +typedef const char const_char; + +const const_char twice_const; /* expect: 68 */ diff --git a/usr.bin/xlint/lint1/msg_068.exp b/usr.bin/xlint/lint1/msg_068.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_068.exp @@ -0,0 +1 @@ +msg_068.c(8): warning: typedef already qualified with 'const' [68] diff --git a/usr.bin/xlint/lint1/msg_069.c b/usr.bin/xlint/lint1/msg_069.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_069.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_069.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_069.c" + +// Test for message: inappropriate qualifiers with 'void' [69] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_069.exp b/usr.bin/xlint/lint1/msg_069.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_069.exp @@ -0,0 +1 @@ +msg_069.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_070.c b/usr.bin/xlint/lint1/msg_070.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_070.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_070.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_070.c" + +// Test for message: %soperand of '%s' is unsigned in ANSI C [70] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_070.exp b/usr.bin/xlint/lint1/msg_070.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_070.exp @@ -0,0 +1 @@ +msg_070.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_071.c b/usr.bin/xlint/lint1/msg_071.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_071.c @@ -0,0 +1,11 @@ +/* $NetBSD: msg_071.c,v 1.4 2021/04/05 01:35:34 rillig Exp $ */ +# 3 "msg_071.c" + +// Test for message: too many characters in character constant [71] + +/* + * C11 6.4.4.4p7 says: Each hexadecimal escape sequence is the longest + * sequence of characters that can constitute the escape sequence. + */ +char valid_multi_digit_hex = '\x0000000000000000000000a'; +char invalid_multi_digit_hex = '\x000g000000000000000000a'; /* expect: 71 *//* expect: 178 */ diff --git a/usr.bin/xlint/lint1/msg_071.exp b/usr.bin/xlint/lint1/msg_071.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_071.exp @@ -0,0 +1,2 @@ +msg_071.c(11): error: too many characters in character constant [71] +msg_071.c(11): warning: initializer does not fit [178] diff --git a/usr.bin/xlint/lint1/msg_072.c b/usr.bin/xlint/lint1/msg_072.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_072.c @@ -0,0 +1,29 @@ +/* $NetBSD: msg_072.c,v 1.5 2021/07/14 20:39:13 rillig Exp $ */ +# 3 "msg_072.c" + +// Test for message: typedef declares no type name [72] + +/* expect+1: warning: typedef declares no type name [72] */ +typedef int; + +typedef int number; + +/* expect+1: warning: typedef declares no type name [72] */ +const typedef; + +void +cover_cgram_declaration(void) +{ + + /* expect+1: warning: typedef declares no type name [72] */ + typedef const; + + /* expect+1: warning: empty declaration [2] */ + const; + + /* expect+1: warning: typedef declares no type name [72] */ + typedef int; + + /* expect+1: warning: empty declaration [2] */ + int; +} diff --git a/usr.bin/xlint/lint1/msg_072.exp b/usr.bin/xlint/lint1/msg_072.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_072.exp @@ -0,0 +1,6 @@ +msg_072.c(7): warning: typedef declares no type name [72] +msg_072.c(12): warning: typedef declares no type name [72] +msg_072.c(19): warning: typedef declares no type name [72] +msg_072.c(22): warning: empty declaration [2] +msg_072.c(25): warning: typedef declares no type name [72] +msg_072.c(28): warning: empty declaration [2] diff --git a/usr.bin/xlint/lint1/msg_073.c b/usr.bin/xlint/lint1/msg_073.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_073.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_073.c,v 1.3 2021/01/31 11:12:07 rillig Exp $ */ +# 3 "msg_073.c" + +// Test for message: empty character constant [73] + +char empty = ''; /* expect: 73 */ +char letter = 'x'; diff --git a/usr.bin/xlint/lint1/msg_073.exp b/usr.bin/xlint/lint1/msg_073.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_073.exp @@ -0,0 +1 @@ +msg_073.c(6): error: empty character constant [73] diff --git a/usr.bin/xlint/lint1/msg_074.c b/usr.bin/xlint/lint1/msg_074.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_074.c @@ -0,0 +1,9 @@ +/* $NetBSD: msg_074.c,v 1.4 2021/04/05 01:35:34 rillig Exp $ */ +# 3 "msg_074.c" + +// Test for message: no hex digits follow \x [74] + +char invalid_hex = '\x'; /* expect: 74 */ +char invalid_hex_letter = '\xg'; /* expect: 74 *//* expect: 294 */ +char valid_hex = '\xff'; +char valid_single_digit_hex = '\xa'; diff --git a/usr.bin/xlint/lint1/msg_074.exp b/usr.bin/xlint/lint1/msg_074.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_074.exp @@ -0,0 +1,3 @@ +msg_074.c(6): error: no hex digits follow \x [74] +msg_074.c(7): error: no hex digits follow \x [74] +msg_074.c(7): warning: multi-character character constant [294] diff --git a/usr.bin/xlint/lint1/msg_075.c b/usr.bin/xlint/lint1/msg_075.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_075.c @@ -0,0 +1,6 @@ +/* $NetBSD: msg_075.c,v 1.3 2021/01/31 11:12:07 rillig Exp $ */ +# 3 "msg_075.c" + +// Test for message: overflow in hex escape [75] + +char str[] = "\x12345678123456781234567812345678"; /* expect: 75 */ diff --git a/usr.bin/xlint/lint1/msg_075.exp b/usr.bin/xlint/lint1/msg_075.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_075.exp @@ -0,0 +1 @@ +msg_075.c(6): warning: overflow in hex escape [75] diff --git a/usr.bin/xlint/lint1/msg_076.c b/usr.bin/xlint/lint1/msg_076.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_076.c @@ -0,0 +1,6 @@ +/* $NetBSD: msg_076.c,v 1.3 2021/06/29 07:17:43 rillig Exp $ */ +# 3 "msg_076.c" + +// Test for message: character escape does not fit in character [76] + +char ch = '\777'; /* expect: [76] */ diff --git a/usr.bin/xlint/lint1/msg_076.exp b/usr.bin/xlint/lint1/msg_076.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_076.exp @@ -0,0 +1 @@ +msg_076.c(6): warning: character escape does not fit in character [76] diff --git a/usr.bin/xlint/lint1/msg_077.c b/usr.bin/xlint/lint1/msg_077.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_077.c @@ -0,0 +1,26 @@ +/* $NetBSD: msg_077.c,v 1.4 2021/06/29 07:23:21 rillig Exp $ */ +# 3 "msg_077.c" + +/* Test for message: bad octal digit %c [77] */ + +/* lint1-flags: -tw */ + +char single_digit = '\8'; /* expect: bad octal digit 8 [77] */ + +/* + * Before lex.c 1.47 from 2021-06-29, lint intended to detect a "bad octal + * digit" following good octal digits, but the corresponding code had an + * unsatisfiable guard clause. + * + * The C Reference Manual 1978, 2.4.3 "Character constants" does not mention + * non-octal digits, therefore this code must have been due to a particular + * C compiler's interpretation. It's even wrong according to the Reference + * Manual to interpret '\088' as anything else than a malformed character + * literal. + * + * That code has been removed since nobody runs lint in traditional C mode + * anyway. + * https://mail-index.netbsd.org/tech-toolchain/2021/03/16/msg003933.html + */ +/* expect+1: multi-character character constant [294] */ +char several_digits = '\08'; diff --git a/usr.bin/xlint/lint1/msg_077.exp b/usr.bin/xlint/lint1/msg_077.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_077.exp @@ -0,0 +1,2 @@ +msg_077.c(8): warning: bad octal digit 8 [77] +msg_077.c(26): warning: multi-character character constant [294] diff --git a/usr.bin/xlint/lint1/msg_078.c b/usr.bin/xlint/lint1/msg_078.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_078.c @@ -0,0 +1,9 @@ +/* $NetBSD: msg_078.c,v 1.4 2021/08/27 20:19:45 rillig Exp $ */ +# 3 "msg_078.c" + +// Test for message: nonportable character escape [78] +/* This message is not used. */ + +char either_255_or_minus_1 = '\377'; +/* expect+1: warning: dubious escape \y [79] */ +char dubious_escape = '\y'; diff --git a/usr.bin/xlint/lint1/msg_078.exp b/usr.bin/xlint/lint1/msg_078.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_078.exp @@ -0,0 +1 @@ +msg_078.c(9): warning: dubious escape \y [79] diff --git a/usr.bin/xlint/lint1/msg_079.c b/usr.bin/xlint/lint1/msg_079.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_079.c @@ -0,0 +1,14 @@ +/* $NetBSD: msg_079.c,v 1.3 2021/08/27 20:16:50 rillig Exp $ */ +# 3 "msg_079.c" + +// Test for message: dubious escape \%c [79] + +int my_printf(const char *, ...); + +void +print_color(_Bool red, _Bool green, _Bool blue) +{ + /* expect+1: warning: dubious escape \e [79] */ + my_printf("\e[%dm", + 30 + (red ? 1 : 0) + (green ? 2 : 0) + (blue ? 4 : 0)); +} diff --git a/usr.bin/xlint/lint1/msg_079.exp b/usr.bin/xlint/lint1/msg_079.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_079.exp @@ -0,0 +1 @@ +msg_079.c(12): warning: dubious escape \e [79] diff --git a/usr.bin/xlint/lint1/msg_080.c b/usr.bin/xlint/lint1/msg_080.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_080.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_080.c,v 1.4 2021/07/04 13:44:43 rillig Exp $ */ +# 3 "msg_080.c" + +// Test for message: dubious escape \%o [80] + +/* expect+1: dubious escape \177 [80] */ +char backslash_delete = '\'; diff --git a/usr.bin/xlint/lint1/msg_080.exp b/usr.bin/xlint/lint1/msg_080.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_080.exp @@ -0,0 +1 @@ +msg_080.c(7): warning: dubious escape \177 [80] diff --git a/usr.bin/xlint/lint1/msg_081.c b/usr.bin/xlint/lint1/msg_081.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_081.c @@ -0,0 +1,8 @@ +/* $NetBSD: msg_081.c,v 1.3 2021/01/31 11:12:07 rillig Exp $ */ +# 3 "msg_081.c" + +// Test for message: \a undefined in traditional C [81] + +/* lint1-flags: -Stw */ + +char str[] = "The bell\a rings"; /* expect: 81 */ diff --git a/usr.bin/xlint/lint1/msg_081.exp b/usr.bin/xlint/lint1/msg_081.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_081.exp @@ -0,0 +1 @@ +msg_081.c(8): warning: \a undefined in traditional C [81] diff --git a/usr.bin/xlint/lint1/msg_082.c b/usr.bin/xlint/lint1/msg_082.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_082.c @@ -0,0 +1,8 @@ +/* $NetBSD: msg_082.c,v 1.4 2021/06/29 07:17:43 rillig Exp $ */ +# 3 "msg_082.c" + +/* Test for message: \x undefined in traditional C [82] */ + +/* lint1-flags: -Stw */ + +char str[] = "A he\x78 escape"; /* expect: [82] */ diff --git a/usr.bin/xlint/lint1/msg_082.exp b/usr.bin/xlint/lint1/msg_082.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_082.exp @@ -0,0 +1 @@ +msg_082.c(8): warning: \x undefined in traditional C [82] diff --git a/usr.bin/xlint/lint1/msg_083.c b/usr.bin/xlint/lint1/msg_083.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_083.c @@ -0,0 +1,15 @@ +/* $NetBSD: msg_083.c,v 1.4 2021/07/10 18:34:03 rillig Exp $ */ +# 3 "msg_083.c" + +// Test for message: storage class after type is obsolescent [83] + +void +example(void) +{ + int register x; /* expect: 83 */ +} + +struct { + int member; +} typedef s; +/* expect-1: warning: storage class after type is obsolescent [83] */ diff --git a/usr.bin/xlint/lint1/msg_083.exp b/usr.bin/xlint/lint1/msg_083.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_083.exp @@ -0,0 +1,2 @@ +msg_083.c(9): warning: storage class after type is obsolescent [83] +msg_083.c(14): warning: storage class after type is obsolescent [83] diff --git a/usr.bin/xlint/lint1/msg_084.c b/usr.bin/xlint/lint1/msg_084.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_084.c @@ -0,0 +1,12 @@ +/* $NetBSD: msg_084.c,v 1.3 2021/01/31 11:12:07 rillig Exp $ */ +# 3 "msg_084.c" + +// Test for message: ANSI C requires formal parameter before '...' [84] + +void only_ellipsis(...) /* expect: 84 */ +{ +} + +void ok_ellipsis(const char *fmt, ...) /* expect: 231 */ +{ +} diff --git a/usr.bin/xlint/lint1/msg_084.exp b/usr.bin/xlint/lint1/msg_084.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_084.exp @@ -0,0 +1,2 @@ +msg_084.c(6): warning: ANSI C requires formal parameter before '...' [84] +msg_084.c(10): warning: argument 'fmt' unused in function 'ok_ellipsis' [231] diff --git a/usr.bin/xlint/lint1/msg_085.c b/usr.bin/xlint/lint1/msg_085.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_085.c @@ -0,0 +1,11 @@ +/* $NetBSD: msg_085.c,v 1.3 2021/01/31 09:21:24 rillig Exp $ */ +# 3 "msg_085.c" + +// Test for message: dubious tag declaration: %s %s [85] + +void declare_struct(struct in_argument *); /* expect: 85 */ +void declare_union(union in_argument *); /* expect: 85 */ +void declare_enum(enum in_argument *); /* expect: 85 */ + +struct ok; /* expect: 233 */ +extern int ok(struct ok *); diff --git a/usr.bin/xlint/lint1/msg_085.exp b/usr.bin/xlint/lint1/msg_085.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_085.exp @@ -0,0 +1,4 @@ +msg_085.c(6): warning: dubious tag declaration: struct in_argument [85] +msg_085.c(7): warning: dubious tag declaration: union in_argument [85] +msg_085.c(8): warning: dubious tag declaration: enum in_argument [85] +msg_085.c(10): warning: struct ok never defined [233] diff --git a/usr.bin/xlint/lint1/msg_086.c b/usr.bin/xlint/lint1/msg_086.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_086.c @@ -0,0 +1,22 @@ +/* $NetBSD: msg_086.c,v 1.3 2021/01/31 11:12:07 rillig Exp $ */ +# 3 "msg_086.c" + +// Test for message: automatic hides external declaration: %s [86] + +/* lint1-flags: -S -g -h -w */ + +extern int identifier; + +int +local_auto(void) +{ + int identifier = 3; /* expect: 86 */ + return identifier; +} + +/* XXX: the function argument does not trigger the warning. */ +int +arg_auto(int identifier) +{ + return identifier; +} diff --git a/usr.bin/xlint/lint1/msg_086.exp b/usr.bin/xlint/lint1/msg_086.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_086.exp @@ -0,0 +1 @@ +msg_086.c(13): warning: automatic hides external declaration: identifier [86] diff --git a/usr.bin/xlint/lint1/msg_087.c b/usr.bin/xlint/lint1/msg_087.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_087.c @@ -0,0 +1,15 @@ +/* $NetBSD: msg_087.c,v 1.3 2021/01/31 11:12:07 rillig Exp $ */ +# 3 "msg_087.c" + +// Test for message: static hides external declaration: %s [87] + +/* lint1-flags: -g -h -S -w */ + +extern int counter; + +int +count(void) +{ + static int counter; /* expect: 87 */ + return counter++; +} diff --git a/usr.bin/xlint/lint1/msg_087.exp b/usr.bin/xlint/lint1/msg_087.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_087.exp @@ -0,0 +1 @@ +msg_087.c(13): warning: static hides external declaration: counter [87] diff --git a/usr.bin/xlint/lint1/msg_088.c b/usr.bin/xlint/lint1/msg_088.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_088.c @@ -0,0 +1,14 @@ +/* $NetBSD: msg_088.c,v 1.3 2021/01/31 11:12:07 rillig Exp $ */ +# 3 "msg_088.c" + +// Test for message: typedef hides external declaration: %s [88] + +/* lint1-flags: -g -h -S -w */ + +extern int identifier; + +void +func(void) +{ + typedef double identifier; /* expect: 88 */ +} diff --git a/usr.bin/xlint/lint1/msg_088.exp b/usr.bin/xlint/lint1/msg_088.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_088.exp @@ -0,0 +1 @@ +msg_088.c(13): warning: typedef hides external declaration: identifier [88] diff --git a/usr.bin/xlint/lint1/msg_089.c b/usr.bin/xlint/lint1/msg_089.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_089.c @@ -0,0 +1,8 @@ +/* $NetBSD: msg_089.c,v 1.3 2021/01/31 11:12:07 rillig Exp $ */ +# 3 "msg_089.c" + +// Test for message: typedef redeclared: %s [89] + +typedef int number; +typedef int number; /* expect: 89 */ +typedef double number; /* expect: 89 */ diff --git a/usr.bin/xlint/lint1/msg_089.exp b/usr.bin/xlint/lint1/msg_089.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_089.exp @@ -0,0 +1,2 @@ +msg_089.c(7): error: typedef redeclared: number [89] +msg_089.c(8): error: typedef redeclared: number [89] diff --git a/usr.bin/xlint/lint1/msg_090.c b/usr.bin/xlint/lint1/msg_090.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_090.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_090.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_090.c" + +// Test for message: inconsistent redeclaration of extern: %s [90] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_090.exp b/usr.bin/xlint/lint1/msg_090.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_090.exp @@ -0,0 +1 @@ +msg_090.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_091.c b/usr.bin/xlint/lint1/msg_091.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_091.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_091.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_091.c" + +// Test for message: declaration hides parameter: %s [91] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_091.exp b/usr.bin/xlint/lint1/msg_091.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_091.exp @@ -0,0 +1 @@ +msg_091.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_092.c b/usr.bin/xlint/lint1/msg_092.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_092.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_092.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_092.c" + +// Test for message: inconsistent redeclaration of static: %s [92] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_092.exp b/usr.bin/xlint/lint1/msg_092.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_092.exp @@ -0,0 +1 @@ +msg_092.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_093.c b/usr.bin/xlint/lint1/msg_093.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_093.c @@ -0,0 +1,12 @@ +/* $NetBSD: msg_093.c,v 1.3 2021/01/31 11:12:07 rillig Exp $ */ +# 3 "msg_093.c" + +// Test for message: dubious static function at block level: %s [93] + +void +example(void) +{ + static void nested(void); /* expect: 93 */ + + nested(); +} diff --git a/usr.bin/xlint/lint1/msg_093.exp b/usr.bin/xlint/lint1/msg_093.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_093.exp @@ -0,0 +1 @@ +msg_093.c(9): warning: dubious static function at block level: nested [93] diff --git a/usr.bin/xlint/lint1/msg_094.c b/usr.bin/xlint/lint1/msg_094.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_094.c @@ -0,0 +1,12 @@ +/* $NetBSD: msg_094.c,v 1.3 2021/01/31 11:12:07 rillig Exp $ */ +# 3 "msg_094.c" + +// Test for message: function has illegal storage class: %s [94] + +register int +global_example(int arg) /* expect: 8 */ +{ + register int register_example(int); /* expect: 94 */ + + return arg; +} diff --git a/usr.bin/xlint/lint1/msg_094.exp b/usr.bin/xlint/lint1/msg_094.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_094.exp @@ -0,0 +1,2 @@ +msg_094.c(7): error: illegal storage class [8] +msg_094.c(9): error: function has illegal storage class: register_example [94] diff --git a/usr.bin/xlint/lint1/msg_095.c b/usr.bin/xlint/lint1/msg_095.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_095.c @@ -0,0 +1,19 @@ +/* $NetBSD: msg_095.c,v 1.3 2021/01/31 11:12:07 rillig Exp $ */ +# 3 "msg_095.c" + +// Test for message: declaration hides earlier one: %s [95] + +/* lint1-flags: -ghSw */ + +int identifier; + +int +example(int identifier) +{ + + { + int identifier = 3; /* expect: 95 */ + } + + return identifier; +} diff --git a/usr.bin/xlint/lint1/msg_095.exp b/usr.bin/xlint/lint1/msg_095.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_095.exp @@ -0,0 +1 @@ +msg_095.c(15): warning: declaration hides earlier one: identifier [95] diff --git a/usr.bin/xlint/lint1/msg_096.c b/usr.bin/xlint/lint1/msg_096.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_096.c @@ -0,0 +1,22 @@ +/* $NetBSD: msg_096.c,v 1.4 2021/04/05 01:35:34 rillig Exp $ */ +# 3 "msg_096.c" + +// Test for message: cannot dereference non-pointer type [96] + +int +unary_plus(int i) +{ + return +i; +} + +int +unary_minus(int i) +{ + return -i; +} + +int +unary_asterisk(int i) /* expect: 231 */ +{ + return *i; /* expect: 96 *//* expect: 214 */ +} diff --git a/usr.bin/xlint/lint1/msg_096.exp b/usr.bin/xlint/lint1/msg_096.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_096.exp @@ -0,0 +1,3 @@ +msg_096.c(21): error: cannot dereference non-pointer type [96] +msg_096.c(21): warning: function 'unary_asterisk' expects to return value [214] +msg_096.c(19): warning: argument 'i' unused in function 'unary_asterisk' [231] diff --git a/usr.bin/xlint/lint1/msg_097.c b/usr.bin/xlint/lint1/msg_097.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_097.c @@ -0,0 +1,21 @@ +/* $NetBSD: msg_097.c,v 1.3 2021/01/31 11:12:07 rillig Exp $ */ +# 3 "msg_097.c" + +/* Test for message: suffix U is illegal in traditional C [97] */ + +/* lint1-flags: -gtw */ + +void +example() +{ + int i = 1234567; + unsigned u = 1234567; + unsigned u_upper = 1234567U; /* expect: 97 */ + unsigned u_lower = 1234567u; /* expect: 97 */ + + long l = 1234567L; + unsigned long ul = 1234567UL; /* expect: 97 */ + + long long ll = 1234567LL; + unsigned long long ull = 1234567ULL; /* expect: 97 */ +} diff --git a/usr.bin/xlint/lint1/msg_097.exp b/usr.bin/xlint/lint1/msg_097.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_097.exp @@ -0,0 +1,4 @@ +msg_097.c(13): warning: suffix U is illegal in traditional C [97] +msg_097.c(14): warning: suffix U is illegal in traditional C [97] +msg_097.c(17): warning: suffix U is illegal in traditional C [97] +msg_097.c(20): warning: suffix U is illegal in traditional C [97] diff --git a/usr.bin/xlint/lint1/msg_098.c b/usr.bin/xlint/lint1/msg_098.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_098.c @@ -0,0 +1,20 @@ +/* $NetBSD: msg_098.c,v 1.4 2021/04/05 01:35:34 rillig Exp $ */ +# 3 "msg_098.c" + +/* Test for message: suffixes F and L are illegal in traditional C [98] */ + +/* lint1-flags: -gtw */ + +void +example() +{ + float f = 1234.5; + float f_F = 1234.5F; /* expect: 98 */ + float f_f = 1234.5f; /* expect: 98 */ + + double d = 1234.5; + double d_U = 1234.5U; /* expect: 249 */ + + long double ld = 1234.5; /* expect: 266 */ + long double ld_L = 1234.5L; /* expect: 98 *//* expect: 266 */ +} diff --git a/usr.bin/xlint/lint1/msg_098.exp b/usr.bin/xlint/lint1/msg_098.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_098.exp @@ -0,0 +1,6 @@ +msg_098.c(12): warning: suffixes F and L are illegal in traditional C [98] +msg_098.c(13): warning: suffixes F and L are illegal in traditional C [98] +msg_098.c(16): error: syntax error 'U' [249] +msg_098.c(18): warning: 'long double' is illegal in traditional C [266] +msg_098.c(19): warning: 'long double' is illegal in traditional C [266] +msg_098.c(19): warning: suffixes F and L are illegal in traditional C [98] diff --git a/usr.bin/xlint/lint1/msg_099.c b/usr.bin/xlint/lint1/msg_099.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_099.c @@ -0,0 +1,11 @@ +/* $NetBSD: msg_099.c,v 1.4 2021/03/26 23:17:33 rillig Exp $ */ +# 3 "msg_099.c" + +// Test for message: '%s' undefined [99] + +void +example(int defined_variable) +{ + int ok = defined_variable; + int error = undefined_variable; /* expect: 99 */ +} diff --git a/usr.bin/xlint/lint1/msg_099.exp b/usr.bin/xlint/lint1/msg_099.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_099.exp @@ -0,0 +1 @@ +msg_099.c(10): error: 'undefined_variable' undefined [99] diff --git a/usr.bin/xlint/lint1/msg_100.c b/usr.bin/xlint/lint1/msg_100.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_100.c @@ -0,0 +1,12 @@ +/* $NetBSD: msg_100.c,v 1.3 2021/01/31 11:12:07 rillig Exp $ */ +# 3 "msg_100.c" + +// Test for message: unary + is illegal in traditional C [100] + +/* lint1-flags: -Stw */ + +int +unary_plus(int x) +{ /* expect: 270 */ + return +x; /* expect: 100 */ +} diff --git a/usr.bin/xlint/lint1/msg_100.exp b/usr.bin/xlint/lint1/msg_100.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_100.exp @@ -0,0 +1,2 @@ +msg_100.c(10): warning: function prototypes are illegal in traditional C [270] +msg_100.c(11): warning: unary + is illegal in traditional C [100] diff --git a/usr.bin/xlint/lint1/msg_101.c b/usr.bin/xlint/lint1/msg_101.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_101.c @@ -0,0 +1,36 @@ +/* $NetBSD: msg_101.c,v 1.9 2021/07/04 17:28:06 rillig Exp $ */ +# 3 "msg_101.c" + +// Test for message: type '%s' does not have member '%s' [101] + +struct point { + int x, y; +}; + +void sink(int); + +void +test(const struct point *ptr, const struct point pt) +{ + /* accessing an existing member */ + sink(ptr->x); + sink(pt.x); + + /* accessing a nonexistent member */ + /* expect+1: error: type 'pointer to const struct point' does not have member 'z' [101] */ + sink(ptr->z); + /* expect+1: error: type 'const struct point' does not have member 'z' [101] */ + sink(pt.z); + + /* mixed up '.' and '->' */ + /* expect+1: error: left operand of '.' must be struct or union, not 'pointer to const struct point' [103] */ + sink(ptr.x); + /* expect+1: error: left operand of '->' must be pointer to struct or union, not 'struct point' [104] */ + sink(pt->x); + + /* accessing a nonexistent member via the wrong operator */ + /* expect+1: error: type 'pointer to const struct point' does not have member 'z' [101] */ + sink(ptr.z); + /* expect+1: error: type 'struct point' does not have member 'z' [101] */ + sink(pt->z); +} diff --git a/usr.bin/xlint/lint1/msg_101.exp b/usr.bin/xlint/lint1/msg_101.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_101.exp @@ -0,0 +1,6 @@ +msg_101.c(21): error: type 'pointer to const struct point' does not have member 'z' [101] +msg_101.c(23): error: type 'const struct point' does not have member 'z' [101] +msg_101.c(27): error: left operand of '.' must be struct or union, not 'pointer to const struct point' [103] +msg_101.c(29): error: left operand of '->' must be pointer to struct or union, not 'struct point' [104] +msg_101.c(33): error: type 'pointer to const struct point' does not have member 'z' [101] +msg_101.c(35): error: type 'struct point' does not have member 'z' [101] diff --git a/usr.bin/xlint/lint1/msg_102.c b/usr.bin/xlint/lint1/msg_102.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_102.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_102.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_102.c" + +// Test for message: illegal member use: %s [102] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_102.exp b/usr.bin/xlint/lint1/msg_102.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_102.exp @@ -0,0 +1 @@ +msg_102.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_103.c b/usr.bin/xlint/lint1/msg_103.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_103.c @@ -0,0 +1,16 @@ +/* $NetBSD: msg_103.c,v 1.3 2021/07/04 17:01:58 rillig Exp $ */ +# 3 "msg_103.c" + +// Test for message: left operand of '.' must be struct or union, not '%s' [103] + +struct point { + int x, y; +}; + +void +test(struct point pt, struct point *ptr) +{ + pt.x = 0; + /* expect+1: left operand of '.' must be struct or union, not 'pointer to struct point' [103] */ + ptr.y = 0; +} diff --git a/usr.bin/xlint/lint1/msg_103.exp b/usr.bin/xlint/lint1/msg_103.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_103.exp @@ -0,0 +1 @@ +msg_103.c(15): error: left operand of '.' must be struct or union, not 'pointer to struct point' [103] diff --git a/usr.bin/xlint/lint1/msg_104.c b/usr.bin/xlint/lint1/msg_104.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_104.c @@ -0,0 +1,17 @@ +/* $NetBSD: msg_104.c,v 1.3 2021/07/04 17:01:58 rillig Exp $ */ +# 3 "msg_104.c" + +// Test for message: left operand of '->' must be pointer to struct or union, not '%s' [104] + +struct point { + int x, y; +}; + +/* ARGSUSED */ +void +test(struct point pt, struct point *ptr) +{ + /* expect+1: left operand of '->' must be pointer to struct or union, not 'struct point' [104] */ + pt->x = 0; + ptr->y = 0; +} diff --git a/usr.bin/xlint/lint1/msg_104.exp b/usr.bin/xlint/lint1/msg_104.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_104.exp @@ -0,0 +1 @@ +msg_104.c(15): error: left operand of '->' must be pointer to struct or union, not 'struct point' [104] diff --git a/usr.bin/xlint/lint1/msg_105.c b/usr.bin/xlint/lint1/msg_105.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_105.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_105.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_105.c" + +// Test for message: non-unique member requires struct/union %s [105] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_105.exp b/usr.bin/xlint/lint1/msg_105.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_105.exp @@ -0,0 +1 @@ +msg_105.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_106.c b/usr.bin/xlint/lint1/msg_106.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_106.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_106.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_106.c" + +// Test for message: left operand of '->' must be pointer [106] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_106.exp b/usr.bin/xlint/lint1/msg_106.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_106.exp @@ -0,0 +1 @@ +msg_106.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_107.c b/usr.bin/xlint/lint1/msg_107.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_107.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_107.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_107.c" + +// Test for message: operands of '%s' have incompatible types (%s != %s) [107] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_107.exp b/usr.bin/xlint/lint1/msg_107.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_107.exp @@ -0,0 +1 @@ +msg_107.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_108.c b/usr.bin/xlint/lint1/msg_108.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_108.c @@ -0,0 +1,25 @@ +/* $NetBSD: msg_108.c,v 1.5 2021/02/28 12:40:00 rillig Exp $ */ +# 3 "msg_108.c" + +// Test for message: operand of '%s' has invalid type (%s) [108] + +/* + * Before tree.c 1.137 from 2021-01-19, taking the complement of a struct + * (an absurd idea, by the way), resulted in an internal error because the + * message 108 had two operands, the second of which was always NOTSPEC, as + * could be expected for a unary operator. + * + * Since an error "invalid type (none)" doesn't make sense, lint rather + * chooses to crash than to generate such an error. + */ +void +complement_of_a_struct(void) +{ + struct s { + int member; + } s = { + 0 + }; + + s = ~s; /* expect: 108 */ +} diff --git a/usr.bin/xlint/lint1/msg_108.exp b/usr.bin/xlint/lint1/msg_108.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_108.exp @@ -0,0 +1 @@ +msg_108.c(24): error: operand of '~' has invalid type (struct) [108] diff --git a/usr.bin/xlint/lint1/msg_109.c b/usr.bin/xlint/lint1/msg_109.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_109.c @@ -0,0 +1,10 @@ +/* $NetBSD: msg_109.c,v 1.4 2021/04/05 01:35:34 rillig Exp $ */ +# 3 "msg_109.c" + +// Test for message: void type illegal in expression [109] + +int +example(int arg) /* expect: 231 */ +{ + return arg + (void)4; /* expect: 109 *//* expect: 214 */ +} diff --git a/usr.bin/xlint/lint1/msg_109.exp b/usr.bin/xlint/lint1/msg_109.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_109.exp @@ -0,0 +1,3 @@ +msg_109.c(9): error: void type illegal in expression [109] +msg_109.c(9): warning: function 'example' expects to return value [214] +msg_109.c(7): warning: argument 'arg' unused in function 'example' [231] diff --git a/usr.bin/xlint/lint1/msg_110.c b/usr.bin/xlint/lint1/msg_110.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_110.c @@ -0,0 +1,15 @@ +/* $NetBSD: msg_110.c,v 1.3 2021/07/25 10:26:46 rillig Exp $ */ +# 3 "msg_110.c" + +// Test for message: pointer to function is not allowed here [110] + +/* ARGSUSED */ +void +call_take(int (*ptr)(int)) +{ + /* expect+1: error: pointer to function is not allowed here [110] */ + ptr++; + + /* expect+1: error: pointer to function is not allowed here [110] */ + ptr + 1; +} diff --git a/usr.bin/xlint/lint1/msg_110.exp b/usr.bin/xlint/lint1/msg_110.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_110.exp @@ -0,0 +1,2 @@ +msg_110.c(11): error: pointer to function is not allowed here [110] +msg_110.c(14): error: pointer to function is not allowed here [110] diff --git a/usr.bin/xlint/lint1/msg_111.c b/usr.bin/xlint/lint1/msg_111.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_111.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_111.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_111.c" + +// Test for message: unacceptable operand of '%s' [111] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_111.exp b/usr.bin/xlint/lint1/msg_111.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_111.exp @@ -0,0 +1 @@ +msg_111.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_112.c b/usr.bin/xlint/lint1/msg_112.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_112.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_112.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_112.c" + +// Test for message: cannot take address of bit-field [112] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_112.exp b/usr.bin/xlint/lint1/msg_112.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_112.exp @@ -0,0 +1 @@ +msg_112.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_113.c b/usr.bin/xlint/lint1/msg_113.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_113.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_113.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_113.c" + +// Test for message: cannot take address of register %s [113] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_113.exp b/usr.bin/xlint/lint1/msg_113.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_113.exp @@ -0,0 +1 @@ +msg_113.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_114.c b/usr.bin/xlint/lint1/msg_114.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_114.c @@ -0,0 +1,20 @@ +/* $NetBSD: msg_114.c,v 1.4 2021/01/31 16:00:05 rillig Exp $ */ +# 3 "msg_114.c" + +// Test for message: %soperand of '%s' must be lvalue [114] + +void +example(int a) /* expect: 231 */ +{ + 3++; /* expect: 114 */ + + /* + * Before tree.c 1.137 from 2021-01-09, trying to increment an array + * aborted lint with 'common/tyname.c, 190: tspec_name(0)'. + * + * See msg_108.c for more details. + */ + "string"++; /* expect: 108 */ + + (a + a)++; /* expect: 114 */ +} diff --git a/usr.bin/xlint/lint1/msg_114.exp b/usr.bin/xlint/lint1/msg_114.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_114.exp @@ -0,0 +1,4 @@ +msg_114.c(9): error: operand of 'x++' must be lvalue [114] +msg_114.c(17): error: operand of 'x++' has invalid type (array) [108] +msg_114.c(19): error: operand of 'x++' must be lvalue [114] +msg_114.c(7): warning: argument 'a' unused in function 'example' [231] diff --git a/usr.bin/xlint/lint1/msg_115.c b/usr.bin/xlint/lint1/msg_115.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_115.c @@ -0,0 +1,59 @@ +/* $NetBSD: msg_115.c,v 1.9 2021/08/16 16:19:47 rillig Exp $ */ +# 3 "msg_115.c" + +// Test for message: %soperand of '%s' must be modifiable lvalue [115] + +void +example(const int *const_ptr) +{ + + *const_ptr = 3; /* expect: 115 */ + *const_ptr += 1; /* expect: 115 */ + *const_ptr -= 4; /* expect: 115 */ + *const_ptr *= 1; /* expect: 115 */ + *const_ptr /= 5; /* expect: 115 */ + *const_ptr %= 9; /* expect: 115 */ + (*const_ptr)++; /* expect: 115 */ + + /* In the next example, the left operand is not an lvalue at all. */ + /* expect+1: error: left operand of '=' must be lvalue [114] */ + (const_ptr + 3) = const_ptr; +} + +typedef struct { + int const member; +} const_member; + +void take_const_member(const_member); + +/* + * Before init.c 1.208 from 2021-08-14 and decl.c 1.221 from 2021-08-10, + * lint issued a wrong "warning: left operand of '%s' must be modifiable + * lvalue", even in cases where the left operand was being initialized + * instead of overwritten. + * + * See initialization_expr_using_op, typeok_assign, has_constant_member. + * See C99 6.2.5p25. + */ +const_member +initialize_const_struct_member(void) +{ + /* In a simple initialization, const members can be assigned. */ + const_member cm1 = (const_member) { 12345 }; + + if (cm1.member != 0) + /* In a function call, const members can be assigned. */ + take_const_member(cm1); + + struct { + const_member member; + } cm2 = { + /* In a nested initialization, const members can be assigned. */ + cm1, + }; + if (cm2.member.member != 0) { + } + + /* In a return statement, const members can be assigned. */ + return cm1; +} diff --git a/usr.bin/xlint/lint1/msg_115.exp b/usr.bin/xlint/lint1/msg_115.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_115.exp @@ -0,0 +1,8 @@ +msg_115.c(10): warning: left operand of '=' must be modifiable lvalue [115] +msg_115.c(11): warning: left operand of '+=' must be modifiable lvalue [115] +msg_115.c(12): warning: left operand of '-=' must be modifiable lvalue [115] +msg_115.c(13): warning: left operand of '*=' must be modifiable lvalue [115] +msg_115.c(14): warning: left operand of '/=' must be modifiable lvalue [115] +msg_115.c(15): warning: left operand of '%=' must be modifiable lvalue [115] +msg_115.c(16): warning: operand of 'x++' must be modifiable lvalue [115] +msg_115.c(20): error: left operand of '=' must be lvalue [114] diff --git a/usr.bin/xlint/lint1/msg_116.c b/usr.bin/xlint/lint1/msg_116.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_116.c @@ -0,0 +1,34 @@ +/* $NetBSD: msg_116.c,v 1.4 2021/01/31 11:12:07 rillig Exp $ */ +# 3 "msg_116.c" + +// Test for message: illegal pointer subtraction [116] + +/* + * Subtracting an int pointer from a double pointer does not make sense. + * The result cannot be reasonably defined since it is "the difference of + * the subscripts of the two array elements" (C99 6.5.5p9), and these two + * pointers cannot point to the same array. + */ +_Bool +example(int *a, double *b) +{ + return a - b > 0; /* expect: 116 */ +} + +/* + * Even though signed char and unsigned char have the same size, + * their pointer types are still considered incompatible. + * + * C99 6.5.5p9 + */ +_Bool +subtract_character_pointers(signed char *scp, unsigned char *ucp) +{ + return scp - ucp > 0; /* expect: 116 */ +} + +_Bool +subtract_const_pointer(const char *ccp, char *cp) +{ + return ccp - cp > 0; +} diff --git a/usr.bin/xlint/lint1/msg_116.exp b/usr.bin/xlint/lint1/msg_116.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_116.exp @@ -0,0 +1,2 @@ +msg_116.c(15): error: illegal pointer subtraction [116] +msg_116.c(27): error: illegal pointer subtraction [116] diff --git a/usr.bin/xlint/lint1/msg_117.c b/usr.bin/xlint/lint1/msg_117.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_117.c @@ -0,0 +1,68 @@ +/* $NetBSD: msg_117.c,v 1.9 2021/08/27 17:59:46 rillig Exp $ */ +# 3 "msg_117.c" + +// Test for message: bitwise '%s' on signed value possibly nonportable [117] + +/* lint1-extra-flags: -p */ + +int +shr(int a, int b) +{ + return a >> b; /* expect: 117 */ +} + +int +shr_lhs_constant_positive(int a) +{ + return 0x1234 >> a; +} + +int +shr_lhs_constant_negative(int a) +{ + return -0x1234 >> a; /* expect: 120 */ +} + +int +shr_rhs_constant_positive(int a) +{ + return a >> 0x1234; /* expect: 117 *//* expect: 122 */ +} + +int +shr_rhs_constant_negative(int a) +{ + return a >> -0x1234; /* expect: 117 *//* expect: 121 */ +} + +unsigned int +shr_unsigned_char(unsigned char uc) +{ + /* + * Even though 'uc' is promoted to 'int', it cannot be negative. + * Before tree.c 1.335 from 2021-08-15, lint wrongly warned that + * 'uc >> 4' might be a bitwise '>>' on signed value. + */ + return uc >> 4; +} + +unsigned char +shr_unsigned_char_promoted_signed(unsigned char bit) +{ + /* + * The possible values for 'bit' range from 0 to 255. Subtracting 1 + * from 0 results in a negative expression value. + */ + /* expect+1: warning: bitwise '>>' on signed value possibly nonportable [117] */ + return (unsigned char)((bit - 1) >> 5); +} + +unsigned char +shr_unsigned_char_promoted_unsigned(unsigned char bit) +{ + /* + * To prevent the above warning, the intermediate expression must be + * cast to 'unsigned char'. + */ + return (unsigned char)((unsigned char)(bit - 1) >> 5); +} diff --git a/usr.bin/xlint/lint1/msg_117.exp b/usr.bin/xlint/lint1/msg_117.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_117.exp @@ -0,0 +1,7 @@ +msg_117.c(11): warning: bitwise '>>' on signed value possibly nonportable [117] +msg_117.c(23): warning: bitwise '>>' on signed value nonportable [120] +msg_117.c(29): warning: bitwise '>>' on signed value possibly nonportable [117] +msg_117.c(29): warning: shift amount 4660 is greater than bit-size 32 of 'int' [122] +msg_117.c(35): warning: bitwise '>>' on signed value possibly nonportable [117] +msg_117.c(35): warning: negative shift [121] +msg_117.c(57): warning: bitwise '>>' on signed value possibly nonportable [117] diff --git a/usr.bin/xlint/lint1/msg_118.c b/usr.bin/xlint/lint1/msg_118.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_118.c @@ -0,0 +1,39 @@ +/* $NetBSD: msg_118.c,v 1.5 2021/05/04 19:57:56 rillig Exp $ */ +# 3 "msg_118.c" + +/* Test for message: semantics of '%s' change in ANSI C; use explicit cast [118] */ + +/* lint1-flags: -hw */ + +int +int_shl_uint(int left, unsigned int right) +{ + return left << right; +} + +int +int_shr_uint(int left, unsigned int right) +{ + /* expect+1: semantics of '>>' change in ANSI C */ + return left >> right; +} + +int +int_shl_int(int left, int right) +{ + return left << right; +} + +/* + * The behavior of typeok_shl can only be triggered on 64-bit platforms, or + * in C99 mode, and the above tests require C90 mode. + * + * On 32-bit platforms both operands of the '<<' operator are first promoted + * individually, and since C90 does not know 'long long', the maximum + * bit-size for an integer type is 32 bits. + * + * Because of this difference there is no test for that case since as of + * 2021-05-04, all lint1 tests must be independent of the target platform and + * there is no way to toggle individual tests depending on the properties of + * the target platform. + */ diff --git a/usr.bin/xlint/lint1/msg_118.exp b/usr.bin/xlint/lint1/msg_118.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_118.exp @@ -0,0 +1 @@ +msg_118.c(18): warning: semantics of '>>' change in ANSI C; use explicit cast [118] diff --git a/usr.bin/xlint/lint1/msg_119.c b/usr.bin/xlint/lint1/msg_119.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_119.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_119.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_119.c" + +// Test for message: conversion of '%s' to '%s' is out of range [119] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_119.exp b/usr.bin/xlint/lint1/msg_119.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_119.exp @@ -0,0 +1 @@ +msg_119.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_120.c b/usr.bin/xlint/lint1/msg_120.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_120.c @@ -0,0 +1,36 @@ +/* $NetBSD: msg_120.c,v 1.5 2021/04/05 01:35:34 rillig Exp $ */ +# 3 "msg_120.c" + +// Test for message: bitwise '%s' on signed value nonportable [120] + +/* lint1-extra-flags: -p */ + +int +shr(int a, int b) +{ + return a >> b; /* expect: 117 */ +} + +int +shr_lhs_constant_positive(int a) +{ + return 0x1234 >> a; +} + +int +shr_lhs_constant_negative(int a) +{ + return -0x1234 >> a; /* expect: 120 */ +} + +int +shr_rhs_constant_positive(int a) +{ + return a >> 0x1234; /* expect: 117 *//* expect: 122 */ +} + +int +shr_rhs_constant_negative(int a) +{ + return a >> -0x1234; /* expect: 117 *//* expect: 121 */ +} diff --git a/usr.bin/xlint/lint1/msg_120.exp b/usr.bin/xlint/lint1/msg_120.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_120.exp @@ -0,0 +1,6 @@ +msg_120.c(11): warning: bitwise '>>' on signed value possibly nonportable [117] +msg_120.c(23): warning: bitwise '>>' on signed value nonportable [120] +msg_120.c(29): warning: bitwise '>>' on signed value possibly nonportable [117] +msg_120.c(29): warning: shift amount 4660 is greater than bit-size 32 of 'int' [122] +msg_120.c(35): warning: bitwise '>>' on signed value possibly nonportable [117] +msg_120.c(35): warning: negative shift [121] diff --git a/usr.bin/xlint/lint1/msg_121.c b/usr.bin/xlint/lint1/msg_121.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_121.c @@ -0,0 +1,20 @@ +/* $NetBSD: msg_121.c,v 1.4 2021/04/06 21:44:12 rillig Exp $ */ +# 3 "msg_121.c" + +// Test for message: negative shift [121] + +int +example(int x) +{ + return x << (3 - 5); /* expect: 121 */ +} + +void /*ARGSUSED*/ +shift_by_double(int x, double amount) +{ + /* + * This is already caught by typeok_scalar, so it doesn't reach + * typeok_shift via typeok_op. + */ + return x << amount; /* expect: incompatible types */ +} diff --git a/usr.bin/xlint/lint1/msg_121.exp b/usr.bin/xlint/lint1/msg_121.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_121.exp @@ -0,0 +1,2 @@ +msg_121.c(9): warning: negative shift [121] +msg_121.c(19): error: operands of '<<' have incompatible types (int != double) [107] diff --git a/usr.bin/xlint/lint1/msg_122.c b/usr.bin/xlint/lint1/msg_122.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_122.c @@ -0,0 +1,10 @@ +/* $NetBSD: msg_122.c,v 1.4 2021/04/06 21:32:57 rillig Exp $ */ +# 3 "msg_122.c" + +// Test for message: shift amount %llu is greater than bit-size %llu of '%s' [122] + +int +example(int x) +{ + return x << 129; /* expect: 122 */ +} diff --git a/usr.bin/xlint/lint1/msg_122.exp b/usr.bin/xlint/lint1/msg_122.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_122.exp @@ -0,0 +1 @@ +msg_122.c(9): warning: shift amount 129 is greater than bit-size 32 of 'int' [122] diff --git a/usr.bin/xlint/lint1/msg_123.c b/usr.bin/xlint/lint1/msg_123.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_123.c @@ -0,0 +1,37 @@ +/* $NetBSD: msg_123.c,v 1.3 2021/08/16 18:51:58 rillig Exp $ */ +# 3 "msg_123.c" + +// Test for message: illegal combination of %s (%s) and %s (%s), op %s [123] + +void ok(_Bool); +void bad(_Bool); + +void +compare(_Bool b, int i, double d, const char *p) +{ + ok(b < b); + ok(b < i); + ok(b < d); + bad(b < p); /* expect: 123 */ + ok(i < b); + ok(i < i); + ok(i < d); + bad(i < p); /* expect: 123 */ + ok(d < b); + ok(d < i); + ok(d < d); + bad(d < p); /* expect: 107 */ + bad(p < b); /* expect: 123 */ + bad(p < i); /* expect: 123 */ + bad(p < d); /* expect: 107 */ + ok(p < p); +} + +void +cover_check_assign_types_compatible(int *int_pointer, int i) +{ + /* expect+1: warning: illegal combination of pointer (pointer to int) and integer (int), op = [123] */ + int_pointer = i; + /* expect+1: warning: illegal combination of integer (int) and pointer (pointer to int), op = [123] */ + i = int_pointer; +} diff --git a/usr.bin/xlint/lint1/msg_123.exp b/usr.bin/xlint/lint1/msg_123.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_123.exp @@ -0,0 +1,8 @@ +msg_123.c(15): warning: illegal combination of integer (_Bool) and pointer (pointer to const char), op < [123] +msg_123.c(19): warning: illegal combination of integer (int) and pointer (pointer to const char), op < [123] +msg_123.c(23): error: operands of '<' have incompatible types (double != pointer) [107] +msg_123.c(24): warning: illegal combination of pointer (pointer to const char) and integer (_Bool), op < [123] +msg_123.c(25): warning: illegal combination of pointer (pointer to const char) and integer (int), op < [123] +msg_123.c(26): error: operands of '<' have incompatible types (pointer != double) [107] +msg_123.c(34): warning: illegal combination of pointer (pointer to int) and integer (int), op = [123] +msg_123.c(36): warning: illegal combination of integer (int) and pointer (pointer to int), op = [123] diff --git a/usr.bin/xlint/lint1/msg_124.c b/usr.bin/xlint/lint1/msg_124.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_124.c @@ -0,0 +1,51 @@ +/* $NetBSD: msg_124.c,v 1.11 2021/08/14 13:00:55 rillig Exp $ */ +# 3 "msg_124.c" + +// Test for message: illegal combination of '%s' and '%s', op '%s' [124] + +/* lint1-extra-flags: -s */ + +typedef void(*signal_handler)(int); + +typedef signal_handler(*sys_signal)(signal_handler); + +typedef int(*printflike)(const char *, ...) + __attribute__((format(printf, 1, 2))); + +void +example(int *ptr) +{ + signal_handler handler = ptr; /* expect: 124 */ + sys_signal signal = ptr; /* expect: 124 */ + printflike printf = ptr; /* expect: 124 */ +} + +void ok(_Bool); +void not_ok(_Bool); + +void +compare_pointers(const void *vp, const char *cp, const int *ip, + signal_handler fp) +{ + ok(vp == cp); + ok(vp == ip); + ok(vp == fp); /* expect: 274 */ + not_ok(cp == ip); /* expect: 124 */ + not_ok(cp == fp); /* expect: 124 */ + ok(vp == (void *)0); + ok(cp == (void *)0); + ok(ip == (void *)0); + ok(fp == (void *)0); /* wrong 274 before 2021-01-25 */ + ok((void *)0 == vp); + ok((void *)0 == cp); + ok((void *)0 == ip); + ok((void *)0 == fp); /* wrong 274 before 2021-01-25 */ + ok(vp == 0); + ok(cp == 0); + ok(ip == 0); + ok(fp == 0); + ok(vp == 0L); + ok(cp == 0L); + ok(ip == 0L); + ok(fp == 0L); +} diff --git a/usr.bin/xlint/lint1/msg_124.exp b/usr.bin/xlint/lint1/msg_124.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_124.exp @@ -0,0 +1,6 @@ +msg_124.c(18): warning: illegal combination of 'pointer to function(int) returning void' and 'pointer to int', op 'init' [124] +msg_124.c(19): warning: illegal combination of 'pointer to function(pointer to function(int) returning void) returning pointer to function(int) returning void' and 'pointer to int', op 'init' [124] +msg_124.c(20): warning: illegal combination of 'pointer to function(pointer to const char, ...) returning int' and 'pointer to int', op 'init' [124] +msg_124.c(32): warning: ANSI C forbids comparison of 'void *' with function pointer [274] +msg_124.c(33): warning: illegal combination of 'pointer to const char' and 'pointer to const int', op '==' [124] +msg_124.c(34): warning: illegal combination of 'pointer to const char' and 'pointer to function(int) returning void', op '==' [124] diff --git a/usr.bin/xlint/lint1/msg_125.c b/usr.bin/xlint/lint1/msg_125.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_125.c @@ -0,0 +1,14 @@ +/* $NetBSD: msg_125.c,v 1.3 2021/01/31 11:12:07 rillig Exp $ */ +# 3 "msg_125.c" + +// Test for message: ANSI C forbids ordered comparisons of pointers to functions [125] + +/* lint1-extra-flags: -s */ + +typedef void (*action)(void); + +int +less(action a, action b) +{ + return a < b; /* expect: 125 */ +} diff --git a/usr.bin/xlint/lint1/msg_125.exp b/usr.bin/xlint/lint1/msg_125.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_125.exp @@ -0,0 +1 @@ +msg_125.c(13): warning: ANSI C forbids ordered comparisons of pointers to functions [125] diff --git a/usr.bin/xlint/lint1/msg_126.c b/usr.bin/xlint/lint1/msg_126.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_126.c @@ -0,0 +1,10 @@ +/* $NetBSD: msg_126.c,v 1.5 2021/04/05 01:35:34 rillig Exp $ */ +# 3 "msg_126.c" + +// Test for message: incompatible types '%s' and '%s' in conditional [126] + +int +max(int cond, void *ptr, double dbl) /* expect: 231 *//* expect: 231 *//* expect: 231 */ +{ + return cond ? ptr : dbl; /* expect: 126 *//* expect: 214 */ +} diff --git a/usr.bin/xlint/lint1/msg_126.exp b/usr.bin/xlint/lint1/msg_126.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_126.exp @@ -0,0 +1,5 @@ +msg_126.c(9): error: incompatible types 'pointer to void' and 'double' in conditional [126] +msg_126.c(9): warning: function 'max' expects to return value [214] +msg_126.c(7): warning: argument 'cond' unused in function 'max' [231] +msg_126.c(7): warning: argument 'ptr' unused in function 'max' [231] +msg_126.c(7): warning: argument 'dbl' unused in function 'max' [231] diff --git a/usr.bin/xlint/lint1/msg_127.c b/usr.bin/xlint/lint1/msg_127.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_127.c @@ -0,0 +1,13 @@ +/* $NetBSD: msg_127.c,v 1.3 2021/01/31 11:12:07 rillig Exp $ */ +# 3 "msg_127.c" + +/* Test for message: '&' before array or function: ignored [127] */ + +/* lint1-extra-flags: -t */ + +void +example() +{ + if (&example != (void *)0) /* expect: 127 */ + return; +} diff --git a/usr.bin/xlint/lint1/msg_127.exp b/usr.bin/xlint/lint1/msg_127.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_127.exp @@ -0,0 +1 @@ +msg_127.c(11): warning: '&' before array or function: ignored [127] diff --git a/usr.bin/xlint/lint1/msg_128.c b/usr.bin/xlint/lint1/msg_128.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_128.c @@ -0,0 +1,11 @@ +/* $NetBSD: msg_128.c,v 1.3 2021/03/16 23:12:30 rillig Exp $ */ +# 3 "msg_128.c" + +// Test for message: operands have incompatible pointer types, op %s (%s != %s) [128] + +void +conversion_to_unconst(const char *cstr) +{ + char *str; + str = cstr; /* expect: 128 */ +} diff --git a/usr.bin/xlint/lint1/msg_128.exp b/usr.bin/xlint/lint1/msg_128.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_128.exp @@ -0,0 +1 @@ +msg_128.c(10): warning: operands have incompatible pointer types, op = (char != const char) [128] diff --git a/usr.bin/xlint/lint1/msg_129.c b/usr.bin/xlint/lint1/msg_129.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_129.c @@ -0,0 +1,94 @@ +/* $NetBSD: msg_129.c,v 1.5 2021/08/21 08:18:48 rillig Exp $ */ +# 3 "msg_129.c" + +// Test for message: expression has null effect [129] + +/* lint1-extra-flags: -h */ + +typedef unsigned char uint8_t; +typedef unsigned int uint32_t; + +_Bool side_effect(void); + +/* + * Before tree.c 1.198 from 2021-01-30, the nested comma operators were + * wrongly reported as having no side effect. + * + * The bug was that has_side_effect did not properly examine the sub-nodes. + * The ',' operator itself has m_has_side_effect == false since it depends + * on its operands whether the ',' actually has side effects. For nested ',' + * operators, the function did not evaluate the operands deeply but only did + * a quick shallow test on the m_has_side_effect property. Since that is + * false, lint thought that the whole expression would have no side effect. + */ +void +uint8_buffer_write_uint32(uint8_t *c, uint32_t l) +{ + (*(c++) = (uint8_t)(l & 0xff), + *(c++) = (uint8_t)((l >> 8L) & 0xff), + *(c++) = (uint8_t)((l >> 16L) & 0xff), + *(c++) = (uint8_t)((l >> 24L) & 0xff)); +} + +void +operator_comma(void) +{ + side_effect(), 0; /* the 0 is redundant */ + 0, side_effect(); /* expect: 129 */ + + if (side_effect(), 0) /* the 0 controls the 'if' */ + return; + if (0, side_effect()) /* expect: 129 */ + return; +} + +void +legitimate_use_cases(int arg) +{ + int local = 3; + + /* + * This expression is commonly used to mark the argument as + * deliberately unused. + */ + (void)arg; + + /* + * This expression is commonly used to mark the local variable as + * deliberately unused. This situation occurs when the local + * variable is only used in some but not all compile-time selectable + * variants of the code, such as in debugging mode, and writing down + * the exact conditions would complicate the code unnecessarily. + */ + (void)local; + + /* This is a short-hand notation for a do-nothing command. */ + (void)0; + + /* + * At the point where lint checks for expressions having a null + * effect, constants have been folded, therefore the following + * expression is considered safe as well. It does not appear in + * practice though. + */ + (void)(3 - 3); + + /* + * This variant of the do-nothing command is commonly used in + * preprocessor macros since it works nicely with if-else and if-then + * statements. It is longer than the above variant though. + */ + do { + } while (0); + + /* + * Only the expression '(void)0' is common, other expressions are + * unusual enough to warrant a warning. + */ + /* expect+1: warning: expression has null effect [129] */ + (void)13; + + /* Double casts are unusual enough to warrant a warning. */ + /* expect+1: warning: expression has null effect [129] */ + (void)(void)0; +} diff --git a/usr.bin/xlint/lint1/msg_129.exp b/usr.bin/xlint/lint1/msg_129.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_129.exp @@ -0,0 +1,4 @@ +msg_129.c(37): warning: expression has null effect [129] +msg_129.c(41): warning: expression has null effect [129] +msg_129.c(89): warning: expression has null effect [129] +msg_129.c(93): warning: expression has null effect [129] diff --git a/usr.bin/xlint/lint1/msg_130.c b/usr.bin/xlint/lint1/msg_130.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_130.c @@ -0,0 +1,170 @@ +/* $NetBSD: msg_130.c,v 1.14 2021/05/25 19:04:07 rillig Exp $ */ +# 3 "msg_130.c" + +// Test for message: enum type mismatch: '%s' '%s' '%s' [130] + +/* See also msg_241.c, which covers unusual operators on enums. */ + +enum color { + RED = 1 << 0, + GREEN = 1 << 1, + BLUE = 1 << 2 +}; + +enum size { + SMALL, + MEDIUM, + LARGE +}; + +enum daytime { + NIGHT, MORNING, NOON, EVENING +}; + +void sink(_Bool); + +void +example(_Bool cond, enum color c, enum size s) +{ + sink(cond ? GREEN : MORNING); /* expect: 130 */ + + sink(c != s); /* expect: 130 */ + sink(c == s); /* expect: 130 */ + sink((c & MEDIUM) != 0); /* might be useful to warn about */ + sink((c | MEDIUM) != 0); /* might be useful to warn about */ + + c |= MEDIUM; /* might be useful to warn about */ + c &= MEDIUM; /* might be useful to warn about */ + + /* The cast to unsigned is required by GCC at WARNS=6. */ + c &= ~(unsigned)MEDIUM; /* might be useful to warn about */ +} + +void +switch_example(enum color c) +{ + switch (c) { + case EVENING: /* maybe someday expect: 130 */ + case LARGE: /* maybe someday expect: 130 */ + case 0: /* maybe someday expect: 130 */ + sink(1 == 1); + break; + default: + break; + } +} + +/* + * Unnamed enum types can be used as a container for constants, especially + * since in C90 and C99, even after the declaration 'static const int x = 3', + * 'x' is not a constant expression. + */ +enum { + sizeof_int = sizeof(int), + sizeof_short = sizeof(short) +}; + +enum { + sizeof_uint = sizeof(unsigned int) +}; + +int +enum_constant_from_unnamed_type(int x) +{ + /* using an enum constant as constant-expression */ + switch (x) { + case sizeof_int: + return 1; + case sizeof_short: + return 2; + default: + break; + } + + if (x == sizeof_int) + return 4; + if (x > sizeof_int) + return 5; + + if (sizeof_int == sizeof_uint) /* expect: 130 *//* FIXME */ + return 6; + + return 0; /* expect: statement not reached */ +} + +/* + * A typical legitimate use case for an anonymous enum type that should not + * be mixed with other types is a state machine. + * + * This example demonstrates that the type of the 'switch' expression can be + * an anonymous enum. + */ +void +state_machine(const char *str) +{ + enum { + begin, + seen_letter, + seen_letter_digit, + error + } state = begin; + + for (const char *p = str; *p != '\0'; p++) { + switch (state) { + case begin: + state = *p == 'A' ? seen_letter : error; + break; + case seen_letter: + state = *p == '1' ? seen_letter_digit : error; + break; + default: + state = error; + } + } + + if (state == 2) /* might be worth a warning */ + return; + if (state == sizeof_int) /* expect: 130 */ + return; +} + +/* + * For check_case_label_enum, a warning only makes sense if the type of the + * enum can actually be specified somehow, either explicitly by using a tag + * name or a typedef name, or implicitly by using a variable in a switch + * expression. + */ + +typedef enum { + has_typedef = 1001 +} typedef_name; + +enum tag_name { + has_tag = 1002 +}; + +enum { + has_variable = 1003 +} variable; + +enum { + inaccessible = 1004 +}; + +/* + * This check is already done by Clang, so it may not be necessary to add it + * to lint as well. Except if there are some cases that Clang didn't + * implement. + */ +void +test_check_case_label_enum(enum color color) +{ + switch (color) + { + case has_typedef: + case has_tag: + case has_variable: + case inaccessible: + return; + } +} diff --git a/usr.bin/xlint/lint1/msg_130.exp b/usr.bin/xlint/lint1/msg_130.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_130.exp @@ -0,0 +1,6 @@ +msg_130.c(29): warning: enum type mismatch: 'enum color' ':' 'enum daytime' [130] +msg_130.c(31): warning: enum type mismatch: 'enum color' '!=' 'enum size' [130] +msg_130.c(32): warning: enum type mismatch: 'enum color' '==' 'enum size' [130] +msg_130.c(89): warning: enum type mismatch: 'enum ' '==' 'enum ' [130] +msg_130.c(92): warning: statement not reached [193] +msg_130.c(127): warning: enum type mismatch: 'enum ' '==' 'enum ' [130] diff --git a/usr.bin/xlint/lint1/msg_131.c b/usr.bin/xlint/lint1/msg_131.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_131.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_131.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_131.c" + +// Test for message: conversion to '%s' may sign-extend incorrectly [131] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_131.exp b/usr.bin/xlint/lint1/msg_131.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_131.exp @@ -0,0 +1 @@ +msg_131.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_132.c b/usr.bin/xlint/lint1/msg_132.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_132.c @@ -0,0 +1,87 @@ +/* $NetBSD: msg_132.c,v 1.6 2021/08/25 22:04:52 rillig Exp $ */ +# 3 "msg_132.c" + +// Test for message: conversion from '%s' to '%s' may lose accuracy [132] + +/* + * NetBSD's default lint flags only include a single -a, which only flags + * narrowing conversions from long. To get warnings for all narrowing + * conversions, -aa needs to be given more than once. + * + * https://gnats.netbsd.org/14531 + */ + +/* lint1-extra-flags: -aa */ + +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned long long u64; + +typedef signed char i8; +typedef signed short i16; +typedef signed int i32; +typedef signed long long i64; + +void +convert_unsigned(u8 v8, u16 v16, u32 v32, u64 v64) +{ + v8 = v16; /* expect: 132 */ + v8 = v32; /* expect: 132 */ + v8 = v64; /* expect: 132 */ + + v16 = v8; + v16 = v32; /* expect: 132 */ + v16 = v64; /* expect: 132 */ + + v32 = v8; + v32 = v16; + v32 = v64; /* expect: 132 */ + + v64 = v8; + v64 = v16; + v64 = v32; +} + +void +convert_signed(i8 v8, i16 v16, i32 v32, i64 v64) +{ + v8 = v16; /* expect: 132 */ + v8 = v32; /* expect: 132 */ + v8 = v64; /* expect: 132 */ + + v16 = v8; + v16 = v32; /* expect: 132 */ + v16 = v64; /* expect: 132 */ + + v32 = v8; + v32 = v16; + v32 = v64; /* expect: 132 */ + + v64 = v8; + v64 = v16; + v64 = v32; +} + +/* + * Before tree.c 1.268 from 2021-04-06, lint wrongly warned that conversion to + * _Bool might lose accuracy. C99 6.3.1.2 defines a special conversion rule + * from scalar to _Bool though. + */ +_Bool +to_bool(long a, long b) +{ + /* seen in fp_lib.h, function wideRightShiftWithSticky */ + return a | b; +} + +/* ARGSUSED */ +const char * +cover_build_plus_minus(const char *arr, double idx) +{ + /* expect+3: error: operands of '+' have incompatible types (pointer != double) [107] */ + /* expect+2: warning: function 'cover_build_plus_minus' expects to return value [214] */ + if (idx > 0.0) + return arr + idx; + return arr + (unsigned int)idx; +} diff --git a/usr.bin/xlint/lint1/msg_132.exp b/usr.bin/xlint/lint1/msg_132.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_132.exp @@ -0,0 +1,14 @@ +msg_132.c(29): warning: conversion from 'unsigned short' to 'unsigned char' may lose accuracy [132] +msg_132.c(30): warning: conversion from 'unsigned int' to 'unsigned char' may lose accuracy [132] +msg_132.c(31): warning: conversion from 'unsigned long long' to 'unsigned char' may lose accuracy [132] +msg_132.c(34): warning: conversion from 'unsigned int' to 'unsigned short' may lose accuracy [132] +msg_132.c(35): warning: conversion from 'unsigned long long' to 'unsigned short' may lose accuracy [132] +msg_132.c(39): warning: conversion from 'unsigned long long' to 'unsigned int' may lose accuracy [132] +msg_132.c(49): warning: conversion from 'short' to 'signed char' may lose accuracy [132] +msg_132.c(50): warning: conversion from 'int' to 'signed char' may lose accuracy [132] +msg_132.c(51): warning: conversion from 'long long' to 'signed char' may lose accuracy [132] +msg_132.c(54): warning: conversion from 'int' to 'short' may lose accuracy [132] +msg_132.c(55): warning: conversion from 'long long' to 'short' may lose accuracy [132] +msg_132.c(59): warning: conversion from 'long long' to 'int' may lose accuracy [132] +msg_132.c(85): error: operands of '+' have incompatible types (pointer != double) [107] +msg_132.c(85): warning: function 'cover_build_plus_minus' expects to return value [214] diff --git a/usr.bin/xlint/lint1/msg_132_ilp32.c b/usr.bin/xlint/lint1/msg_132_ilp32.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_132_ilp32.c @@ -0,0 +1,32 @@ +/* $NetBSD: msg_132_ilp32.c,v 1.1 2021/08/25 22:04:52 rillig Exp $ */ +# 3 "msg_132_ilp32.c" + +// Test for message: conversion from '%s' to '%s' may lose accuracy [132] + +/* + * On 32-bit platforms, it is possible to add a 64-bit integer to a 32-bit + * pointer. The 64-bit integer is then converted to the ptrdiff_t of the + * target platform, which results in the non-obvious conversion from + * 'long long' to either 'long' or 'int', depending on the platform's + * ptrdiff_t. + */ + +/* lint1-extra-flags: -a */ +/* lint1-only-if: ilp32 int */ + +/* + * Seen in usr.bin/make/var.c, function RegexReplace, in the function call + * SepBuf_AddBytesBetween(buf, wp + m[0].rm_so, wp + m[0].rm_eo). The + * offsets of regular expression matches have type off_t, which is a 64-bit + * integer. + * + * C11 6.5.6p8 does not explicitly define the meaning of a pointer + an + * overly long integer, it just says "undefined behavior" if the resulting + * pointer would be outside the object. + */ +const char * +array_subscript(const char *p, long long idx) +{ + /* expect+1: warning: conversion from 'long long' to 'int' may lose accuracy [132] */ + return p + idx; +} diff --git a/usr.bin/xlint/lint1/msg_132_ilp32.exp b/usr.bin/xlint/lint1/msg_132_ilp32.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_132_ilp32.exp @@ -0,0 +1 @@ +msg_132_ilp32.c(31): warning: conversion from 'long long' to 'int' may lose accuracy [132] diff --git a/usr.bin/xlint/lint1/msg_133.c b/usr.bin/xlint/lint1/msg_133.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_133.c @@ -0,0 +1,10 @@ +/* $NetBSD: msg_133.c,v 1.3 2021/01/31 11:12:07 rillig Exp $ */ +# 3 "msg_133.c" + +// Test for message: conversion of pointer to '%s' loses bits [133] + +char +to_char(void *ptr) +{ + return (char)ptr; /* expect: 133 */ +} diff --git a/usr.bin/xlint/lint1/msg_133.exp b/usr.bin/xlint/lint1/msg_133.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_133.exp @@ -0,0 +1 @@ +msg_133.c(9): warning: conversion of pointer to 'char' loses bits [133] diff --git a/usr.bin/xlint/lint1/msg_134.c b/usr.bin/xlint/lint1/msg_134.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_134.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_134.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_134.c" + +// Test for message: conversion of pointer to '%s' may lose bits [134] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_134.exp b/usr.bin/xlint/lint1/msg_134.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_134.exp @@ -0,0 +1 @@ +msg_134.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_135.c b/usr.bin/xlint/lint1/msg_135.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_135.c @@ -0,0 +1,75 @@ +/* $NetBSD: msg_135.c,v 1.9 2021/07/15 21:22:19 rillig Exp $ */ +# 3 "msg_135.c" + +// Test for message: converting '%s' to '%s' may cause alignment problem [135] + +/* lint1-extra-flags: -h */ + +void sink(const void *); + +unsigned +read_uint(const unsigned short **pp) +{ + unsigned val; + + /* expect+1: warning: converting 'pointer to const unsigned short' to 'pointer to const unsigned int' may cause alignment problem [135] */ + val = *(const unsigned *)(*pp); + pp += sizeof(unsigned); + return val; +} + +struct incomplete; /* expect: never defined */ + +struct complete { + int member; +}; + +/* + * These types of conversions are typically seen in OpenSSL, when converting + * from the publicly visible, incomplete 'struct lhash_st' to a private + * implementation type such as 'struct lhash_st_OPENSSL_STRING'. + * + * Before tree.c 1.277 from 2021-04-17, lint warned about this, even though + * there was not enough evidence that there really was an alignment problem, + * resulting in many false positives. + * + * See openssl/lhash.h. + */ +void +pointer_to_structs(struct incomplete *incomplete) +{ + struct complete *complete; + + complete = (struct complete *)incomplete; + sink(complete); +} + +/* + * Before tree.c 1.316 from 2021-07-15, lint warned about pointer casts from + * unsigned char or plain char to another type. These casts often occur in + * traditional code that does not use void pointers, even 30 years after C90 + * introduced 'void'. + */ +void +unsigned_char_to_unsigned_type(unsigned char *ucp) +{ + unsigned short *usp; + + usp = (unsigned short *)ucp; + sink(usp); +} + +/* + * Before tree.c 1.316 from 2021-07-15, lint warned about pointer casts from + * unsigned char or plain char to another type. These casts often occur in + * traditional code that does not use void pointers, even 30 years after C90 + * introduced 'void'. + */ +void +plain_char_to_unsigned_type(char *cp) +{ + unsigned short *usp; + + usp = (unsigned short *)cp; + sink(usp); +} diff --git a/usr.bin/xlint/lint1/msg_135.exp b/usr.bin/xlint/lint1/msg_135.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_135.exp @@ -0,0 +1,2 @@ +msg_135.c(16): warning: converting 'pointer to const unsigned short' to 'pointer to const unsigned int' may cause alignment problem [135] +msg_135.c(21): warning: struct incomplete never defined [233] diff --git a/usr.bin/xlint/lint1/msg_136.c b/usr.bin/xlint/lint1/msg_136.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_136.c @@ -0,0 +1,12 @@ +/* $NetBSD: msg_136.c,v 1.3 2021/01/31 11:12:07 rillig Exp $ */ +# 3 "msg_136.c" + +// Test for message: cannot do pointer arithmetic on operand of unknown size [136] + +struct incomplete; /* expect: 233 */ + +const struct incomplete * +example(const struct incomplete *ptr) +{ + return ptr + 5; /* expect: 136 */ +} diff --git a/usr.bin/xlint/lint1/msg_136.exp b/usr.bin/xlint/lint1/msg_136.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_136.exp @@ -0,0 +1,2 @@ +msg_136.c(11): error: cannot do pointer arithmetic on operand of unknown size [136] +msg_136.c(6): warning: struct incomplete never defined [233] diff --git a/usr.bin/xlint/lint1/msg_137.c b/usr.bin/xlint/lint1/msg_137.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_137.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_137.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_137.c" + +// Test for message: use of incomplete enum type, op %s [137] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_137.exp b/usr.bin/xlint/lint1/msg_137.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_137.exp @@ -0,0 +1 @@ +msg_137.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_138.c b/usr.bin/xlint/lint1/msg_138.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_138.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_138.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_138.c" + +// Test for message: unknown operand size, op %s [138] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_138.exp b/usr.bin/xlint/lint1/msg_138.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_138.exp @@ -0,0 +1 @@ +msg_138.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_139.c b/usr.bin/xlint/lint1/msg_139.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_139.c @@ -0,0 +1,24 @@ +/* $NetBSD: msg_139.c,v 1.3 2021/03/16 23:18:49 rillig Exp $ */ +# 3 "msg_139.c" + +// Test for message: division by 0 [139] + +void sink_int(int); +void sink_double(double); + +void +example(int i) +{ + enum { + zero = 0 + }; + + sink_int(i / 0); /* only triggers in constant expressions */ + sink_int(i / zero); /* only triggers in constant expressions */ + sink_double(i / 0.0); + + sink_int(13 / 0); /* expect: 139 */ + sink_int(13 / zero); /* expect: 139 */ + sink_double(13 / 0.0); /* expect: 139 *//* XXX: Clang doesn't warn */ + sink_double(13 / -0.0); /* expect: 139 *//* XXX: Clang doesn't warn */ +} diff --git a/usr.bin/xlint/lint1/msg_139.exp b/usr.bin/xlint/lint1/msg_139.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_139.exp @@ -0,0 +1,4 @@ +msg_139.c(20): error: division by 0 [139] +msg_139.c(21): error: division by 0 [139] +msg_139.c(22): error: division by 0 [139] +msg_139.c(23): error: division by 0 [139] diff --git a/usr.bin/xlint/lint1/msg_140.c b/usr.bin/xlint/lint1/msg_140.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_140.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_140.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_140.c" + +// Test for message: modulus by 0 [140] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_140.exp b/usr.bin/xlint/lint1/msg_140.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_140.exp @@ -0,0 +1 @@ +msg_140.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_141.c b/usr.bin/xlint/lint1/msg_141.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_141.c @@ -0,0 +1,33 @@ +/* $NetBSD: msg_141.c,v 1.5 2021/08/23 06:50:01 rillig Exp $ */ +# 3 "msg_141.c" + +// Test for message: integer overflow detected, op '%s' [141] + +/* lint1-extra-flags: -h */ + +/* + * Before tree.c 1.347 from 2021-08-23, lint wrongly warned about integer + * overflow in '-'. + */ +int signed_int_max = (1u << 31) - 1; + +/* + * Before tree.c 1.347 from 2021-08-23, lint wrongly warned about integer + * overflow in '-'. + */ +unsigned int unsigned_int_max = (1u << 31) - 1; + +/* expect+1: warning: integer overflow detected, op '+' [141] */ +int int_overflow = (1 << 30) + (1 << 30); + +/* expect+2: warning: integer overflow detected, op '+' [141] */ +/* expect+1: warning: initialization of unsigned with negative constant [221] */ +unsigned int intermediate_overflow = (1 << 30) + (1 << 30); + +unsigned int no_overflow = (1U << 30) + (1 << 30); + +/* expect+1: warning: integer overflow detected, op '-' [141] */ +unsigned int unsigned_int_min = 0u - (1u << 31); + +/* expect+1: warning: integer overflow detected, op '-' [141] */ +unsigned int unsigned_int_min_unary = -(1u << 31); diff --git a/usr.bin/xlint/lint1/msg_141.exp b/usr.bin/xlint/lint1/msg_141.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_141.exp @@ -0,0 +1,5 @@ +msg_141.c(21): warning: integer overflow detected, op '+' [141] +msg_141.c(25): warning: integer overflow detected, op '+' [141] +msg_141.c(25): warning: initialization of unsigned with negative constant [221] +msg_141.c(30): warning: integer overflow detected, op '-' [141] +msg_141.c(33): warning: integer overflow detected, op '-' [141] diff --git a/usr.bin/xlint/lint1/msg_142.c b/usr.bin/xlint/lint1/msg_142.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_142.c @@ -0,0 +1,14 @@ +/* $NetBSD: msg_142.c,v 1.6 2021/08/21 11:50:57 rillig Exp $ */ +# 3 "msg_142.c" + +// Test for message: floating point overflow detected, op %s [142] + +/* lint1-only-if: ldbl-64 */ +/* + * For 96-bit and 128-bit floating point numbers, a different number of + * multipliers is needed to produce an overflow. + */ + +/* expect+2: warning: floating point overflow detected, op * [142] */ +/* expect+1: warning: floating point overflow detected, op * [142] */ +double dbl = 1e100 * 1e100 * 1e100 * 1e100 * 1e100; diff --git a/usr.bin/xlint/lint1/msg_142.exp b/usr.bin/xlint/lint1/msg_142.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_142.exp @@ -0,0 +1,2 @@ +msg_142.c(14): warning: floating point overflow detected, op * [142] +msg_142.c(14): warning: floating point overflow detected, op * [142] diff --git a/usr.bin/xlint/lint1/msg_143.c b/usr.bin/xlint/lint1/msg_143.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_143.c @@ -0,0 +1,12 @@ +/* $NetBSD: msg_143.c,v 1.3 2021/01/31 11:12:07 rillig Exp $ */ +# 3 "msg_143.c" + +// Test for message: cannot take size/alignment of incomplete type [143] + +struct incomplete; /* expect: 233 */ + +unsigned long +sizeof_incomplete(void) +{ + return sizeof(struct incomplete); /* expect: 143 */ +} diff --git a/usr.bin/xlint/lint1/msg_143.exp b/usr.bin/xlint/lint1/msg_143.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_143.exp @@ -0,0 +1,2 @@ +msg_143.c(11): error: cannot take size/alignment of incomplete type [143] +msg_143.c(6): warning: struct incomplete never defined [233] diff --git a/usr.bin/xlint/lint1/msg_144.c b/usr.bin/xlint/lint1/msg_144.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_144.c @@ -0,0 +1,10 @@ +/* $NetBSD: msg_144.c,v 1.4 2021/04/02 12:16:50 rillig Exp $ */ +# 3 "msg_144.c" + +// Test for message: cannot take size/alignment of function [144] + +unsigned long +example(void) +{ + return sizeof(example); /* expect: 144 */ +} diff --git a/usr.bin/xlint/lint1/msg_144.exp b/usr.bin/xlint/lint1/msg_144.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_144.exp @@ -0,0 +1 @@ +msg_144.c(9): error: cannot take size/alignment of function [144] diff --git a/usr.bin/xlint/lint1/msg_145.c b/usr.bin/xlint/lint1/msg_145.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_145.c @@ -0,0 +1,25 @@ +/* $NetBSD: msg_145.c,v 1.3 2021/01/31 11:12:07 rillig Exp $ */ +# 3 "msg_145.c" + +// Test for message: cannot take size/alignment of bit-field [145] + +struct bits { + unsigned one: 1; + unsigned eight: 8; +}; + +unsigned long +sizeof_one(void) +{ + struct bits local_bits; + + return sizeof(local_bits.one); /* expect: 145 */ +} + +unsigned long +sizeof_eight(void) +{ + struct bits local_bits; + + return sizeof(local_bits.eight); /* expect: 145 */ +} diff --git a/usr.bin/xlint/lint1/msg_145.exp b/usr.bin/xlint/lint1/msg_145.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_145.exp @@ -0,0 +1,2 @@ +msg_145.c(16): error: cannot take size/alignment of bit-field [145] +msg_145.c(24): error: cannot take size/alignment of bit-field [145] diff --git a/usr.bin/xlint/lint1/msg_146.c b/usr.bin/xlint/lint1/msg_146.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_146.c @@ -0,0 +1,10 @@ +/* $NetBSD: msg_146.c,v 1.3 2021/01/31 11:12:07 rillig Exp $ */ +# 3 "msg_146.c" + +// Test for message: cannot take size/alignment of void [146] + +unsigned long +example(void *ptr) +{ + return sizeof(*ptr); /* expect: 146 */ +} diff --git a/usr.bin/xlint/lint1/msg_146.exp b/usr.bin/xlint/lint1/msg_146.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_146.exp @@ -0,0 +1 @@ +msg_146.c(9): error: cannot take size/alignment of void [146] diff --git a/usr.bin/xlint/lint1/msg_147.c b/usr.bin/xlint/lint1/msg_147.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_147.c @@ -0,0 +1,15 @@ +/* $NetBSD: msg_147.c,v 1.4 2021/07/25 10:39:10 rillig Exp $ */ +# 3 "msg_147.c" + +// Test for message: invalid cast from '%s' to '%s' [147] + +// The type name 'int(int)' is a 'function(int) returning int'. +void take(int(int)); + +/* ARGSUSED */ +void +call_take(int (*ptr)(int)) +{ + /* expect+1: error: invalid cast from 'pointer to function(int) returning int' to 'function(int) returning int' [147] */ + take((int(int))ptr); +} diff --git a/usr.bin/xlint/lint1/msg_147.exp b/usr.bin/xlint/lint1/msg_147.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_147.exp @@ -0,0 +1 @@ +msg_147.c(14): error: invalid cast from 'pointer to function(int) returning int' to 'function(int) returning int' [147] diff --git a/usr.bin/xlint/lint1/msg_148.c b/usr.bin/xlint/lint1/msg_148.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_148.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_148.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_148.c" + +// Test for message: improper cast of void expression [148] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_148.exp b/usr.bin/xlint/lint1/msg_148.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_148.exp @@ -0,0 +1 @@ +msg_148.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_149.c b/usr.bin/xlint/lint1/msg_149.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_149.c @@ -0,0 +1,12 @@ +/* $NetBSD: msg_149.c,v 1.3 2021/08/26 19:23:25 rillig Exp $ */ +# 3 "msg_149.c" + +// Test for message: illegal function (type %s) [149] + +void +example(int i) +{ + i++; + /* expect+1: error: illegal function (type int) [149] */ + i(3); +} diff --git a/usr.bin/xlint/lint1/msg_149.exp b/usr.bin/xlint/lint1/msg_149.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_149.exp @@ -0,0 +1 @@ +msg_149.c(11): error: illegal function (type int) [149] diff --git a/usr.bin/xlint/lint1/msg_150.c b/usr.bin/xlint/lint1/msg_150.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_150.c @@ -0,0 +1,13 @@ +/* $NetBSD: msg_150.c,v 1.3 2021/01/31 11:12:07 rillig Exp $ */ +# 3 "msg_150.c" + +// Test for message: argument mismatch: %d arg%s passed, %d expected [150] + +int +add2(int, int); + +int +example(void) +{ + return add2(2, 3, 5, 7); /* expect: 150 */ +} diff --git a/usr.bin/xlint/lint1/msg_150.exp b/usr.bin/xlint/lint1/msg_150.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_150.exp @@ -0,0 +1 @@ +msg_150.c(12): error: argument mismatch: 4 args passed, 2 expected [150] diff --git a/usr.bin/xlint/lint1/msg_151.c b/usr.bin/xlint/lint1/msg_151.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_151.c @@ -0,0 +1,13 @@ +/* $NetBSD: msg_151.c,v 1.3 2021/03/16 23:39:41 rillig Exp $ */ +# 3 "msg_151.c" + +// Test for message: void expressions may not be arguments, arg #%d [151] + +void sink_int(int); + +void +example(int i) +{ + sink_int((void)i); /* expect: 151 */ + sink_int(i); +} diff --git a/usr.bin/xlint/lint1/msg_151.exp b/usr.bin/xlint/lint1/msg_151.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_151.exp @@ -0,0 +1 @@ +msg_151.c(11): error: void expressions may not be arguments, arg #1 [151] diff --git a/usr.bin/xlint/lint1/msg_152.c b/usr.bin/xlint/lint1/msg_152.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_152.c @@ -0,0 +1,15 @@ +/* $NetBSD: msg_152.c,v 1.3 2021/03/16 23:39:41 rillig Exp $ */ +# 3 "msg_152.c" + +// Test for message: argument cannot have unknown size, arg #%d [152] + +struct incomplete; /* expect: 233 */ + +void callee(struct incomplete); /* expect: 31 */ + +void +caller(void) +{ + struct incomplete local_var; /* expect: 31 */ + callee(local_var); /* expect: 152 */ +} diff --git a/usr.bin/xlint/lint1/msg_152.exp b/usr.bin/xlint/lint1/msg_152.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_152.exp @@ -0,0 +1,4 @@ +msg_152.c(8): error: '' has incomplete type 'incomplete struct incomplete' [31] +msg_152.c(13): error: 'local_var' has incomplete type 'incomplete struct incomplete' [31] +msg_152.c(14): error: argument cannot have unknown size, arg #1 [152] +msg_152.c(6): warning: struct incomplete never defined [233] diff --git a/usr.bin/xlint/lint1/msg_153.c b/usr.bin/xlint/lint1/msg_153.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_153.c @@ -0,0 +1,22 @@ +/* $NetBSD: msg_153.c,v 1.5 2021/02/28 01:20:54 rillig Exp $ */ +# 3 "msg_153.c" + +// Test for message: converting '%s' to incompatible '%s' for argument %d [153] + + +typedef double (*unary_operator)(double); + +void sink_function_pointer(unary_operator); +void sink_int_pointer(int *); + +void +to_function_pointer(int *x) +{ + sink_function_pointer(x); /* expect: 153 */ +} + +void +to_int_pointer(unary_operator op) +{ + sink_int_pointer(op); /* expect: 153 */ +} diff --git a/usr.bin/xlint/lint1/msg_153.exp b/usr.bin/xlint/lint1/msg_153.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_153.exp @@ -0,0 +1,2 @@ +msg_153.c(15): warning: converting 'pointer to int' to incompatible 'pointer to function(double) returning double' for argument 1 [153] +msg_153.c(21): warning: converting 'pointer to function(double) returning double' to incompatible 'pointer to int' for argument 1 [153] diff --git a/usr.bin/xlint/lint1/msg_154.c b/usr.bin/xlint/lint1/msg_154.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_154.c @@ -0,0 +1,12 @@ +/* $NetBSD: msg_154.c,v 1.3 2021/03/16 23:39:41 rillig Exp $ */ +# 3 "msg_154.c" + +// Test for message: illegal combination of %s (%s) and %s (%s), arg #%d [154] + +void sink_int(int); + +void +example(int *ptr) +{ + sink_int(ptr); /* expect: 154 */ +} diff --git a/usr.bin/xlint/lint1/msg_154.exp b/usr.bin/xlint/lint1/msg_154.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_154.exp @@ -0,0 +1 @@ +msg_154.c(11): warning: illegal combination of integer (int) and pointer (pointer to int), arg #1 [154] diff --git a/usr.bin/xlint/lint1/msg_155.c b/usr.bin/xlint/lint1/msg_155.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_155.c @@ -0,0 +1,79 @@ +/* $NetBSD: msg_155.c,v 1.9 2021/06/30 14:23:50 rillig Exp $ */ +# 3 "msg_155.c" + +// Test for message: passing '%s' to incompatible '%s', arg #%d [155] + + +void c99_6_7_6_example_a(int); +void c99_6_7_6_example_b(int *); +void c99_6_7_6_example_c(int *[3]); +void c99_6_7_6_example_d(int (*)[3]); +void c99_6_7_6_example_e(int (*)[*]); +// FIXME: assertion "sym->s_type != NULL" failed in declare_argument at decl.c:2436 +// void c99_6_7_6_example_f(int *()); +void c99_6_7_6_example_g(int (*)(void)); +void c99_6_7_6_example_h(int (*const[])(unsigned int, ...)); + +struct incompatible { + int member; +}; + +void +provoke_error_messages(struct incompatible arg) +{ + /* expect+1: 'int' */ + c99_6_7_6_example_a(arg); + + /* expect+1: 'pointer to int' */ + c99_6_7_6_example_b(arg); + + /* C99 says 'array[3] of pointer to int', which is close enough. */ + /* expect+1: 'pointer to pointer to int' */ + c99_6_7_6_example_c(arg); + + /* expect+1: 'pointer to array[3] of int' */ + c99_6_7_6_example_d(arg); + + /* expect+1: 'pointer to array[unknown_size] of int' */ + c99_6_7_6_example_e(arg); + + /* TODO: C99 says 'function with no parameter specification returning a pointer to int' */ + /* expect+1: function 'c99_6_7_6_example_f' implicitly declared */ + c99_6_7_6_example_f(arg); + + /* expect+1: 'pointer to function(void) returning int' */ + c99_6_7_6_example_g(arg); + + /* expect+1: 'pointer to const pointer to function(unsigned int, ...) returning int' */ + c99_6_7_6_example_h(arg); +} + +extern void sink(struct incompatible); + +/* + * The function type_name has a special case for an enum type that has been + * implicitly converted to an int. Such a type is still output as the enum + * type. + * + * XXX: The expressions 'day + 0' and '0 + day' should result in the same + * type. + */ +void +type_name_of_enum(void) +{ + enum Day { + MONDAY + } day = MONDAY; + + /* expect+1: passing 'enum Day' */ + sink(day); + + /* expect+1: passing 'enum Day' */ + sink(day + 0); + + /* expect+1: passing 'int' */ + sink(0 + day); + + /* expect+1: passing 'int' */ + sink(0); +} diff --git a/usr.bin/xlint/lint1/msg_155.exp b/usr.bin/xlint/lint1/msg_155.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_155.exp @@ -0,0 +1,12 @@ +msg_155.c(25): warning: passing 'struct incompatible' to incompatible 'int', arg #1 [155] +msg_155.c(28): warning: passing 'struct incompatible' to incompatible 'pointer to int', arg #1 [155] +msg_155.c(32): warning: passing 'struct incompatible' to incompatible 'pointer to pointer to int', arg #1 [155] +msg_155.c(35): warning: passing 'struct incompatible' to incompatible 'pointer to array[3] of int', arg #1 [155] +msg_155.c(38): warning: passing 'struct incompatible' to incompatible 'pointer to array[unknown_size] of int', arg #1 [155] +msg_155.c(42): error: function 'c99_6_7_6_example_f' implicitly declared to return int [215] +msg_155.c(45): warning: passing 'struct incompatible' to incompatible 'pointer to function(void) returning int', arg #1 [155] +msg_155.c(48): warning: passing 'struct incompatible' to incompatible 'pointer to const pointer to function(unsigned int, ...) returning int', arg #1 [155] +msg_155.c(69): warning: passing 'enum Day' to incompatible 'struct incompatible', arg #1 [155] +msg_155.c(72): warning: passing 'enum Day' to incompatible 'struct incompatible', arg #1 [155] +msg_155.c(75): warning: passing 'int' to incompatible 'struct incompatible', arg #1 [155] +msg_155.c(78): warning: passing 'int' to incompatible 'struct incompatible', arg #1 [155] diff --git a/usr.bin/xlint/lint1/msg_156.c b/usr.bin/xlint/lint1/msg_156.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_156.c @@ -0,0 +1,28 @@ +/* $NetBSD: msg_156.c,v 1.5 2021/02/28 12:40:00 rillig Exp $ */ +# 3 "msg_156.c" + +// Test for message: enum type mismatch, arg #%d (%s != %s) [156] + +enum color { + RED = 1 << 0, + GREEN = 1 << 1, + BLUE = 1 << 2 +}; + +enum size { + SMALL, + MEDIUM, + LARGE +}; + +void print_color(enum color); + +void +example(enum color c, enum size s) +{ + print_color(GREEN); + print_color(c); + + print_color(MEDIUM); /* expect: 156 */ + print_color(s); /* expect: 156 */ +} diff --git a/usr.bin/xlint/lint1/msg_156.exp b/usr.bin/xlint/lint1/msg_156.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_156.exp @@ -0,0 +1,2 @@ +msg_156.c(26): warning: enum type mismatch, arg #1 (enum color != enum size) [156] +msg_156.c(27): warning: enum type mismatch, arg #1 (enum color != enum size) [156] diff --git a/usr.bin/xlint/lint1/msg_157.c b/usr.bin/xlint/lint1/msg_157.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_157.c @@ -0,0 +1,10 @@ +/* $NetBSD: msg_157.c,v 1.3 2021/03/16 23:39:41 rillig Exp $ */ +# 3 "msg_157.c" + +// Test for message: ANSI C treats constant as unsigned [157] + +/* + * A rather strange definition for an ARGB color. + * Luckily, 'double' has more than 32 significant binary digits. + */ +double white = 0xFFFFFFFF; /* expect: 157 */ diff --git a/usr.bin/xlint/lint1/msg_157.exp b/usr.bin/xlint/lint1/msg_157.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_157.exp @@ -0,0 +1 @@ +msg_157.c(10): warning: ANSI C treats constant as unsigned [157] diff --git a/usr.bin/xlint/lint1/msg_158.c b/usr.bin/xlint/lint1/msg_158.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_158.c @@ -0,0 +1,43 @@ +/* $NetBSD: msg_158.c,v 1.3 2021/03/16 23:39:41 rillig Exp $ */ +# 3 "msg_158.c" + +// Test for message: %s may be used before set [158] + +void sink_int(int); + +void +example(int arg) +{ + int twice_arg; + + sink_int(twice_arg); /* expect: 158 */ + twice_arg = 2 * arg; + sink_int(twice_arg); +} + +void +conditionally_used(int arg) +{ + int twice_arg; + + if (arg > 0) + twice_arg = 2 * arg; + if (arg > 0) + sink_int(twice_arg); +} + +void +conditionally_unused(int arg) +{ + int twice_arg; + + if (arg > 0) + twice_arg = 2 * arg; + + /* + * This situation is not detected by lint as it does not track the + * possible code paths for all conditions. + */ + if (arg < 0) + sink_int(twice_arg); +} diff --git a/usr.bin/xlint/lint1/msg_158.exp b/usr.bin/xlint/lint1/msg_158.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_158.exp @@ -0,0 +1 @@ +msg_158.c(13): warning: twice_arg may be used before set [158] diff --git a/usr.bin/xlint/lint1/msg_159.c b/usr.bin/xlint/lint1/msg_159.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_159.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_159.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_159.c" + +// Test for message: assignment in conditional context [159] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_159.exp b/usr.bin/xlint/lint1/msg_159.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_159.exp @@ -0,0 +1 @@ +msg_159.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_160.c b/usr.bin/xlint/lint1/msg_160.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_160.c @@ -0,0 +1,58 @@ +/* $NetBSD: msg_160.c,v 1.5 2021/01/31 12:20:00 rillig Exp $ */ +# 3 "msg_160.c" + +// Test for message: operator '==' found where '=' was expected [160] + +/* lint1-extra-flags: -h */ + +_Bool +both_equal_or_unequal(int a, int b, int c, int d) +{ + /* + * Before tree.c 1.201 from 2021-01-31, lint warned about each of + * the '==' subexpressions even though there is nothing surprising + * about them. + */ + return (a == b) == (c == d); +} + +void +eval(_Bool); + +void +unparenthesized(int a, int b, int c, _Bool z) +{ + /* + * This one might be legitimate since the second '==' has _Bool + * on both sides. Parenthesizing its left-hand operand doesn't + * hurt though. + */ + eval(a == b == z); /* expect: 160 */ + + /* + * Before tree.c 1.201 from 2021-01-31, lint warned about the + * parenthesized '==' subexpression even though there is nothing + * surprising about it. + */ + eval((a == b) == z); + + /* + * This one is definitely wrong. C, unlike Python, does not chain + * comparison operators in the way mathematicians are used to. + */ + eval(a == b == c); /* expect: 160 */ + + /* Parenthesizing one of the operands makes it obvious enough. */ + /* + * Before tree.c 1.201 from 2021-01-31, lint warned about the + * parenthesized '==' subexpression even though there is nothing + * surprising about it. + */ + eval((a == b) == c); + /* + * Before tree.c 1.201 from 2021-01-31, lint warned about the + * parenthesized '==' subexpression even though there is nothing + * surprising about it. + */ + eval(a == (b == c)); +} diff --git a/usr.bin/xlint/lint1/msg_160.exp b/usr.bin/xlint/lint1/msg_160.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_160.exp @@ -0,0 +1,2 @@ +msg_160.c(30): warning: operator '==' found where '=' was expected [160] +msg_160.c(43): warning: operator '==' found where '=' was expected [160] diff --git a/usr.bin/xlint/lint1/msg_161.c b/usr.bin/xlint/lint1/msg_161.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_161.c @@ -0,0 +1,57 @@ +/* $NetBSD: msg_161.c,v 1.7 2021/03/21 15:44:57 rillig Exp $ */ +# 3 "msg_161.c" + +// Test for message: constant in conditional context [161] + +/* lint1-extra-flags: -h */ + +void +while_1(void) +{ + while (1) /* expect: 161 */ + continue; +} + +void +while_0(void) +{ + while (0) /* expect: 161 */ + continue; /* expect: statement not reached */ +} + +/* + * The pattern 'do { } while (0)' is a common technique to define a + * preprocessor macro that behaves like a single statement. There is + * nothing unusual or surprising about the constant condition. + * Before tree.c 1.202 from 2021-01-31, lint warned about it. + */ +void +do_while_0(void) +{ + do { + + } while (0); +} + +void +do_while_1(void) +{ + do { + + } while (1); /* expect: 161 */ +} + +extern void println(const char *); + +/* + * Since 2021-02-28, lint no longer warns about constant controlling + * expressions involving sizeof since these are completely legitimate. + */ +void +test_sizeof(void) +{ + if (sizeof(int) > sizeof(char)) + println("very probable"); + if (sizeof(int) < sizeof(char)) + println("impossible"); +} diff --git a/usr.bin/xlint/lint1/msg_161.exp b/usr.bin/xlint/lint1/msg_161.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_161.exp @@ -0,0 +1,4 @@ +msg_161.c(11): warning: constant in conditional context [161] +msg_161.c(18): warning: constant in conditional context [161] +msg_161.c(19): warning: statement not reached [193] +msg_161.c(41): warning: constant in conditional context [161] diff --git a/usr.bin/xlint/lint1/msg_162.c b/usr.bin/xlint/lint1/msg_162.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_162.c @@ -0,0 +1,84 @@ +/* $NetBSD: msg_162.c,v 1.4 2021/08/28 14:45:19 rillig Exp $ */ +# 3 "msg_162.c" + +// Test for message: comparison of %s with %s, op %s [162] + +/* lint1-extra-flags: -hp */ + +void +left_unsigned(unsigned int ui) +{ + if (ui < -5.0) { + } + + /* expect+1: warning: comparison of unsigned int with negative constant, op < [162] */ + if (ui < -5) { + } + + /* expect+1: warning: comparison of unsigned int with 0, op < [162] */ + if (ui < 0) { + } + + /* expect+1: warning: comparison of unsigned int with 0, op >= [162] */ + if (ui >= 0) { + } + + /* expect+1: warning: comparison of unsigned int with 0, op <= [162] */ + if (ui <= 0) { + } +} + +void +right_unsigned(unsigned int ui) +{ + + if (-5.0 > ui) { + } + + /* expect+1: warning: comparison of negative constant with unsigned int, op > [162] */ + if (-5 > ui) { + } + + /* expect+1: warning: comparison of 0 with unsigned int, op > [162] */ + if (0 > ui) { + } + + /* expect+1: warning: comparison of 0 with unsigned int, op <= [162] */ + if (0 <= ui) { + } + + /* expect+1: warning: comparison of 0 with unsigned int, op >= [162] */ + if (0 >= ui) { + } +} + +/* + * Lint does not care about these comparisons, even though they are obviously + * out of range. + */ +void +compare_signed_char(signed char sc) +{ + if (sc == -129) + return; + if (sc == -128) + return; + if (sc == 127) + return; + if (sc == 128) + return; +} + +void +compare_unsigned_char(unsigned char uc) +{ + /* expect+1: warning: comparison of unsigned char with negative constant, op == [162] */ + if (uc == -1) + return; + if (uc == 0) + return; + if (uc == 255) + return; + if (uc == 256) + return; +} diff --git a/usr.bin/xlint/lint1/msg_162.exp b/usr.bin/xlint/lint1/msg_162.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_162.exp @@ -0,0 +1,9 @@ +msg_162.c(15): warning: comparison of unsigned int with negative constant, op < [162] +msg_162.c(19): warning: comparison of unsigned int with 0, op < [162] +msg_162.c(23): warning: comparison of unsigned int with 0, op >= [162] +msg_162.c(27): warning: comparison of unsigned int with 0, op <= [162] +msg_162.c(39): warning: comparison of negative constant with unsigned int, op > [162] +msg_162.c(43): warning: comparison of 0 with unsigned int, op > [162] +msg_162.c(47): warning: comparison of 0 with unsigned int, op <= [162] +msg_162.c(51): warning: comparison of 0 with unsigned int, op >= [162] +msg_162.c(76): warning: comparison of unsigned char with negative constant, op == [162] diff --git a/usr.bin/xlint/lint1/msg_163.c b/usr.bin/xlint/lint1/msg_163.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_163.c @@ -0,0 +1,27 @@ +/* $NetBSD: msg_163.c,v 1.4 2021/07/04 12:24:39 rillig Exp $ */ +# 3 "msg_163.c" + +// Test for message: a cast does not yield an lvalue [163] + +void +example(char *p, int i) +{ + p++; + + /* + * Using a cast as an lvalue had been a GCC extension until 3.4. + * It was removed in GCC 4.0. + * + * https://gcc.gnu.org/onlinedocs/gcc-3.4.6/gcc/Lvalues.html#Lvalues + * https://gcc.gnu.org/onlinedocs/gcc-4.0.4/gcc/index.html#toc_C-Extensions + */ + /* expect+2: error: a cast does not yield an lvalue [163] */ + /* expect+1: error: operand of 'x++' must be lvalue [114] */ + ((char *)p)++; + + i++; + + /* expect+2: error: a cast does not yield an lvalue [163] */ + /* expect+1: error: operand of 'x++' must be lvalue [114] */ + ((int)i)++; +} diff --git a/usr.bin/xlint/lint1/msg_163.exp b/usr.bin/xlint/lint1/msg_163.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_163.exp @@ -0,0 +1,4 @@ +msg_163.c(20): error: a cast does not yield an lvalue [163] +msg_163.c(20): error: operand of 'x++' must be lvalue [114] +msg_163.c(26): error: a cast does not yield an lvalue [163] +msg_163.c(26): error: operand of 'x++' must be lvalue [114] diff --git a/usr.bin/xlint/lint1/msg_164.c b/usr.bin/xlint/lint1/msg_164.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_164.c @@ -0,0 +1,15 @@ +/* $NetBSD: msg_164.c,v 1.4 2021/08/14 12:46:24 rillig Exp $ */ +# 3 "msg_164.c" + +// Test for message: assignment of negative constant to unsigned type [164] + +void +example(void) +{ + /* expect+1: warning: initialization of unsigned with negative constant [221] */ + unsigned char uch = -3; + + uch = -5; /* expect: 164 */ + uch += -7; /* expect: 222 */ + uch *= -1; /* expect: 222 */ +} diff --git a/usr.bin/xlint/lint1/msg_164.exp b/usr.bin/xlint/lint1/msg_164.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_164.exp @@ -0,0 +1,4 @@ +msg_164.c(10): warning: initialization of unsigned with negative constant [221] +msg_164.c(12): warning: assignment of negative constant to unsigned type [164] +msg_164.c(13): warning: conversion of negative constant to unsigned type [222] +msg_164.c(14): warning: conversion of negative constant to unsigned type [222] diff --git a/usr.bin/xlint/lint1/msg_165.c b/usr.bin/xlint/lint1/msg_165.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_165.c @@ -0,0 +1,12 @@ +/* $NetBSD: msg_165.c,v 1.3 2021/01/31 11:12:07 rillig Exp $ */ +# 3 "msg_165.c" + +// Test for message: constant truncated by assignment [165] + +void +example(void) +{ + unsigned char ch; + + ch = 0x1234; /* expect: 165 */ +} diff --git a/usr.bin/xlint/lint1/msg_165.exp b/usr.bin/xlint/lint1/msg_165.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_165.exp @@ -0,0 +1 @@ +msg_165.c(11): warning: constant truncated by assignment [165] diff --git a/usr.bin/xlint/lint1/msg_166.c b/usr.bin/xlint/lint1/msg_166.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_166.c @@ -0,0 +1,59 @@ +/* $NetBSD: msg_166.c,v 1.3 2021/05/16 11:11:37 rillig Exp $ */ +# 3 "msg_166.c" + +// Test for message: precision lost in bit-field assignment [166] + +/* lint1-extra-flags: -hp */ + +struct bit_set { + + /* + * C99 6.7.2p5 and 6.7.2.1p9 footnote 104 say that for bit-fields of + * underlying type 'int', "it is implementation-defined whether the + * specifier 'int' designates the same type as 'signed int' or the + * same type as 'unsigned int'". + * + * https://gcc.gnu.org/onlinedocs/gcc/Structures-unions-enumerations + * -and-bit-fields-implementation.html says: "By default it is treated + * as 'signed int' but this may be changed by the + * '-funsigned-bitfields' option". + * + * Clang doesn't document implementation-defined behavior, see + * https://bugs.llvm.org/show_bug.cgi?id=11272. + */ + + int minus_1_to_0: 1; /* expect: 344 */ + int minus_8_to_7: 4; /* expect: 344 */ + unsigned zero_to_1: 1; + unsigned zero_to_15: 4; +}; + +void example(void) { + struct bit_set bits; + + /* Clang doesn't warn about the 1. */ + bits.minus_1_to_0 = -2; /* expect: 166 */ + bits.minus_1_to_0 = -1; + bits.minus_1_to_0 = 0; + bits.minus_1_to_0 = 1; /* expect: 166 */ + bits.minus_1_to_0 = 2; /* expect: 166 */ + + bits.minus_8_to_7 = -9; /* expect: 166 */ + bits.minus_8_to_7 = -8; + bits.minus_8_to_7 = 7; + bits.minus_8_to_7 = 8; /* expect: 166 */ + + /* Clang doesn't warn about the -1. */ + bits.zero_to_1 = -2; /* expect: 164 */ + bits.zero_to_1 = -1; /* expect: 164 */ + bits.zero_to_1 = 0; + bits.zero_to_1 = 1; + bits.zero_to_1 = 2; /* expect: 166 */ + + /* Clang doesn't warn about the -8. */ + bits.zero_to_15 = -9; /* expect: 164 */ + bits.zero_to_15 = -8; /* expect: 164 */ + bits.zero_to_15 = 0; + bits.zero_to_15 = 15; + bits.zero_to_15 = 16; /* expect: 166 */ +} diff --git a/usr.bin/xlint/lint1/msg_166.exp b/usr.bin/xlint/lint1/msg_166.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_166.exp @@ -0,0 +1,13 @@ +msg_166.c(25): warning: bit-field of type plain 'int' has implementation-defined signedness [344] +msg_166.c(26): warning: bit-field of type plain 'int' has implementation-defined signedness [344] +msg_166.c(35): warning: precision lost in bit-field assignment [166] +msg_166.c(38): warning: precision lost in bit-field assignment [166] +msg_166.c(39): warning: precision lost in bit-field assignment [166] +msg_166.c(41): warning: precision lost in bit-field assignment [166] +msg_166.c(44): warning: precision lost in bit-field assignment [166] +msg_166.c(47): warning: assignment of negative constant to unsigned type [164] +msg_166.c(48): warning: assignment of negative constant to unsigned type [164] +msg_166.c(51): warning: precision lost in bit-field assignment [166] +msg_166.c(54): warning: assignment of negative constant to unsigned type [164] +msg_166.c(55): warning: assignment of negative constant to unsigned type [164] +msg_166.c(58): warning: precision lost in bit-field assignment [166] diff --git a/usr.bin/xlint/lint1/msg_167.c b/usr.bin/xlint/lint1/msg_167.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_167.c @@ -0,0 +1,18 @@ +/* $NetBSD: msg_167.c,v 1.3 2021/01/31 11:12:07 rillig Exp $ */ +# 3 "msg_167.c" + +// Test for message: array subscript cannot be negative: %ld [167] + +void +example(int *ptr) +{ + int arr[6]; + + arr[-3] = 13; /* expect: 167 */ + + /* + * Since the pointer may have been initialized with "arr + 3", + * subtracting from its address is allowed. + */ + ptr[-3] = 13; +} diff --git a/usr.bin/xlint/lint1/msg_167.exp b/usr.bin/xlint/lint1/msg_167.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_167.exp @@ -0,0 +1 @@ +msg_167.c(11): warning: array subscript cannot be negative: -3 [167] diff --git a/usr.bin/xlint/lint1/msg_168.c b/usr.bin/xlint/lint1/msg_168.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_168.c @@ -0,0 +1,42 @@ +/* $NetBSD: msg_168.c,v 1.5 2021/03/25 22:53:05 rillig Exp $ */ +# 3 "msg_168.c" + +// Test for message: array subscript cannot be > %d: %ld [168] + +void print_string(const char *); +void print_char(char); + +void +example(void) +{ + char buf[20] = {}; /* empty initializer is a GCC extension */ + + print_string(buf + 19); /* inside the array */ + + /* + * It is valid to point at the end of the array, but reading a + * character from there invokes undefined behavior. + * + * The pointer to the end of the array is typically used in (begin, + * end) tuples. These are more common in C++ than in C though. + */ + print_string(buf + 20); + + print_string(buf + 21); /* undefined behavior, not detected */ + + print_char(buf[19]); + print_char(buf[20]); /* expect: 168 */ +} + +void +array_with_c99_initializer(void) +{ + static const char *const to_roman[] = { + ['0'] = "undefined", + ['5'] = "V", + ['9'] = "IX" + }; + + print_string(to_roman['9']); + print_string(to_roman[':']); /* expect: 168 */ +} diff --git a/usr.bin/xlint/lint1/msg_168.exp b/usr.bin/xlint/lint1/msg_168.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_168.exp @@ -0,0 +1,2 @@ +msg_168.c(28): warning: array subscript cannot be > 19: 20 [168] +msg_168.c(41): warning: array subscript cannot be > 57: 58 [168] diff --git a/usr.bin/xlint/lint1/msg_169.c b/usr.bin/xlint/lint1/msg_169.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_169.c @@ -0,0 +1,182 @@ +/* $NetBSD: msg_169.c,v 1.5 2021/01/31 11:12:07 rillig Exp $ */ +# 3 "msg_169.c" + +// Test for message: precedence confusion possible: parenthesize! [169] + +/* lint1-flags: -g -h -S -w */ + +typedef _Bool bool; + +void +confusing_shift_arith(unsigned a, unsigned b, unsigned c, unsigned char ch) +{ + unsigned con, okl, okr; + + con = a + b << c; /* expect: 169 */ + okl = (a + b) << c; + okr = a + (b << c); + + con = a << b + c; /* expect: 169 */ + okl = (a << b) + c; + okr = a << (b + c); + + con = a - b >> c; /* expect: 169 */ + okl = (a - b) >> c; + okr = a - (b >> c); + + con = a >> b - c; /* expect: 169 */ + okl = (a >> b) - c; + okr = a >> (b - c); + + // Parenthesizing the inner operands has no effect on the warning. + con = (a) + b << c; /* expect: 169 */ + con = a + (b) << c; /* expect: 169 */ + con = a + b << (c); /* expect: 169 */ + + // The usual arithmetic promotions have no effect on the warning. + con = ch + b << c; /* expect: 169 */ + con = a + ch << c; /* expect: 169 */ + con = a + b << ch; /* expect: 169 */ +} + +void +confusing_logical(bool a, bool b, bool c) +{ + bool con, okl, okr, eql; + + eql = a && b && c; + eql = a || b || c; + + con = a && b || c; /* expect: 169 */ + okl = (a && b) || c; + okr = a && (b || c); + + con = a || b && c; /* expect: 169 */ + okl = (a || b) && c; + okr = a || (b && c); +} + +void +confusing_bitwise(unsigned a, unsigned b, unsigned c) +{ + bool con, okl, okr, eql; + + eql = a & b & c; + eql = a | b | c; + eql = a ^ b ^ c; + + con = a | b ^ c; /* expect: 169 */ + okl = (a | b) ^ c; + okr = a | (b ^ c); + + con = a | b & c; /* expect: 169 */ + okl = (a | b) & c; + okr = a | (b & c); + + con = a ^ b | c; /* expect: 169 */ + okl = (a ^ b) | c; + okr = a ^ (b | c); + + con = a ^ b & c; /* expect: 169 */ + okl = (a ^ b) & c; + okr = a ^ (b & c); + + con = a & b | c; /* expect: 169 */ + okl = (a & b) ^ c; + okr = a & (b ^ c); + + con = a & b ^ c; /* expect: 169 */ + okl = (a & b) ^ c; + okr = a & (b ^ c); + + con = a & b + c; /* expect: 169 */ + okl = (a & b) + c; + okr = a & (b + c); + + con = a - b | c; /* expect: 169 */ + okl = (a - b) | c; + okr = a - (b | c); + + // This looks like a binomial formula but isn't. + con = a ^ 2 - 2 * a * b + b ^ 2; /* expect: 169 */ + + // This isn't a binomial formula either since '^' means xor. + con = (a ^ 2) - 2 * a * b + (b ^ 2); +} + +void +constant_expressions(void) +{ + unsigned con; + + // The check for confusing precedence happens after constant folding. + // Therefore the following lines do not generate warnings. + con = 1 & 2 | 3; + con = 4 << 5 + 6; + con = 7 ^ 8 & 9; +} + +void +cast_expressions(char a, char b, char c) +{ + unsigned con; + + // Adding casts to the leaf nodes doesn't change anything about the + // confusing precedence. + con = (unsigned)a | (unsigned)b & (unsigned)c; /* expect: 169 */ + con = (unsigned)a & (unsigned)b | (unsigned)c; /* expect: 169 */ + + // Adding a cast around the whole calculation doesn't change the + // precedence as well. + con = (unsigned)(a | b & c); /* expect: 169 */ + + // Adding a cast around an intermediate result groups the operands + // of the main node, which prevents any confusion about precedence. + con = (unsigned)a | (unsigned)(b & c); + con = a | (unsigned)(b & c); + con = (unsigned)(a | b) & (unsigned)c; + con = (unsigned)(a | b) & c; +} + +void +expected_precedence(int a, int b, int c) +{ + int ok; + + ok = a + b * c; +} + +void +implicit_conversion_to_long(long la, int a) +{ + int ok; + + ok = a & a | la; /* expect: 169 */ + + /* + * Before tree.c 1.132 from 2021-01-04, there was a typo in + * check_precedence_confusion that prevented the right-hand operand + * from being flagged as possibly confusing if there was an implicit + * conversion or an explicit cast between the main operator ('|') and + * the nested operator ('&'). + */ + ok = la | a & a; /* expect: 169 */ + + ok = (a & a) | la; /* always ok */ + ok = la | (a & a); /* always ok */ + + /* + * Before tree.c 1.132, this expression didn't generate a warning + * because the right-hand operand was CVT, and there is no confusing + * precedence between BITOR and CVT. + * + * Since tree.c 1.132, this expression doesn't generate a warning + * because the right-hand operand is parenthesized. There is no way + * to have the right operand casted and at the same time not + * parenthesized since the cast operator has higher precedence. + * + * In summary, there is no visible change, but the implementation is + * now works as intended. + */ + ok = la | (int)(a & a); /* always ok */ +} diff --git a/usr.bin/xlint/lint1/msg_169.exp b/usr.bin/xlint/lint1/msg_169.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_169.exp @@ -0,0 +1,26 @@ +msg_169.c(15): warning: precedence confusion possible: parenthesize! [169] +msg_169.c(19): warning: precedence confusion possible: parenthesize! [169] +msg_169.c(23): warning: precedence confusion possible: parenthesize! [169] +msg_169.c(27): warning: precedence confusion possible: parenthesize! [169] +msg_169.c(32): warning: precedence confusion possible: parenthesize! [169] +msg_169.c(33): warning: precedence confusion possible: parenthesize! [169] +msg_169.c(34): warning: precedence confusion possible: parenthesize! [169] +msg_169.c(37): warning: precedence confusion possible: parenthesize! [169] +msg_169.c(38): warning: precedence confusion possible: parenthesize! [169] +msg_169.c(39): warning: precedence confusion possible: parenthesize! [169] +msg_169.c(50): warning: precedence confusion possible: parenthesize! [169] +msg_169.c(54): warning: precedence confusion possible: parenthesize! [169] +msg_169.c(68): warning: precedence confusion possible: parenthesize! [169] +msg_169.c(72): warning: precedence confusion possible: parenthesize! [169] +msg_169.c(76): warning: precedence confusion possible: parenthesize! [169] +msg_169.c(80): warning: precedence confusion possible: parenthesize! [169] +msg_169.c(84): warning: precedence confusion possible: parenthesize! [169] +msg_169.c(88): warning: precedence confusion possible: parenthesize! [169] +msg_169.c(92): warning: precedence confusion possible: parenthesize! [169] +msg_169.c(96): warning: precedence confusion possible: parenthesize! [169] +msg_169.c(101): warning: precedence confusion possible: parenthesize! [169] +msg_169.c(126): warning: precedence confusion possible: parenthesize! [169] +msg_169.c(127): warning: precedence confusion possible: parenthesize! [169] +msg_169.c(131): warning: precedence confusion possible: parenthesize! [169] +msg_169.c(154): warning: precedence confusion possible: parenthesize! [169] +msg_169.c(163): warning: precedence confusion possible: parenthesize! [169] diff --git a/usr.bin/xlint/lint1/msg_170.c b/usr.bin/xlint/lint1/msg_170.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_170.c @@ -0,0 +1,14 @@ +/* $NetBSD: msg_170.c,v 1.4 2021/04/05 01:35:34 rillig Exp $ */ +# 3 "msg_170.c" + +// Test for message: first operand must have scalar type, op ? : [170] + +struct number { + int value; +}; + +_Bool +example(const struct number *num) /* expect: 231 */ +{ + return *num ? 1 : 0; /* expect: 170 *//* expect: 214 */ +} diff --git a/usr.bin/xlint/lint1/msg_170.exp b/usr.bin/xlint/lint1/msg_170.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_170.exp @@ -0,0 +1,3 @@ +msg_170.c(13): error: first operand must have scalar type, op ? : [170] +msg_170.c(13): warning: function 'example' expects to return value [214] +msg_170.c(11): warning: argument 'num' unused in function 'example' [231] diff --git a/usr.bin/xlint/lint1/msg_171.c b/usr.bin/xlint/lint1/msg_171.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_171.c @@ -0,0 +1,42 @@ +/* $NetBSD: msg_171.c,v 1.6 2021/03/23 18:40:50 rillig Exp $ */ +# 3 "msg_171.c" + +// Test for message: cannot assign to '%s' from '%s' [171] + +struct s { + int member; +}; + +/*ARGSUSED*/ +void +example(int i, void *vp, struct s *s) +{ + i = *s; /* expect: 171 */ + *s = i; /* expect: 171 */ + + vp = *s; /* expect: 171 */ + *s = vp; /* expect: 171 */ +} + +/* + * C99 6.5.2.5 says that a compound literal evaluates to an unnamed object + * with automatic storage duration, like any normal named object. It is an + * lvalue, which means that it is possible to take the address of the object. + * Seen in external/mpl/bind/dist/lib/dns/rbtdb.c, update_rrsetstats. + * + * Before init.c 1.111 from 2021-03-23, lint could not handle these nested + * initializations (the outer one for the variable 'p', the inner one for the + * compound literal) and wrongly complained about a type mismatch between + * 'struct point' and 'pointer to struct point'. + */ +void +pointer_to_compound_literal(void) +{ + struct point { + int x; + int y; + }; + struct point *p = &(struct point){ + 12, 5, + }; +} diff --git a/usr.bin/xlint/lint1/msg_171.exp b/usr.bin/xlint/lint1/msg_171.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_171.exp @@ -0,0 +1,4 @@ +msg_171.c(14): error: cannot assign to 'int' from 'struct s' [171] +msg_171.c(15): error: cannot assign to 'struct s' from 'int' [171] +msg_171.c(17): error: cannot assign to 'pointer to void' from 'struct s' [171] +msg_171.c(18): error: cannot assign to 'struct s' from 'pointer to void' [171] diff --git a/usr.bin/xlint/lint1/msg_172.c b/usr.bin/xlint/lint1/msg_172.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_172.c @@ -0,0 +1,12 @@ +/* $NetBSD: msg_172.c,v 1.3 2021/01/31 11:12:07 rillig Exp $ */ +# 3 "msg_172.c" + +// Test for message: too many struct/union initializers [172] + +struct color { + unsigned int red: 8; + unsigned int green: 8; + unsigned int blue: 8; +}; + +struct color white = { 255, 255, 255, 255 }; /* expect: 172 */ diff --git a/usr.bin/xlint/lint1/msg_172.exp b/usr.bin/xlint/lint1/msg_172.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_172.exp @@ -0,0 +1 @@ +msg_172.c(12): error: too many struct/union initializers [172] diff --git a/usr.bin/xlint/lint1/msg_173.c b/usr.bin/xlint/lint1/msg_173.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_173.c @@ -0,0 +1,8 @@ +/* $NetBSD: msg_173.c,v 1.3 2021/01/31 11:12:07 rillig Exp $ */ +# 3 "msg_173.c" + +// Test for message: too many array initializers, expected %d [173] + +int arr_limited[3] = { 1, 2, 3, 4 }; /* expect: 173 */ +int arr_unlimited[] = { 1, 2, 3, 4 }; +int arr_too_few[] = { 1, 2 }; diff --git a/usr.bin/xlint/lint1/msg_173.exp b/usr.bin/xlint/lint1/msg_173.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_173.exp @@ -0,0 +1 @@ +msg_173.c(6): error: too many array initializers, expected 3 [173] diff --git a/usr.bin/xlint/lint1/msg_174.c b/usr.bin/xlint/lint1/msg_174.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_174.c @@ -0,0 +1,19 @@ +/* $NetBSD: msg_174.c,v 1.4 2021/03/21 10:43:08 rillig Exp $ */ +# 3 "msg_174.c" + +// Test for message: too many initializers [174] + +void +example(void) +{ + /* A single pair of braces is always allowed. */ + int n = { 13 }; + + int too_many = { 17, 19 }; /* expect: 174 */ + + /* + * An initializer list must have at least one expression, says the + * syntax definition in C99 6.7.8. + */ + int too_few = {}; +} diff --git a/usr.bin/xlint/lint1/msg_174.exp b/usr.bin/xlint/lint1/msg_174.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_174.exp @@ -0,0 +1 @@ +msg_174.c(12): error: too many initializers [174] diff --git a/usr.bin/xlint/lint1/msg_175.c b/usr.bin/xlint/lint1/msg_175.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_175.c @@ -0,0 +1,10 @@ +/* $NetBSD: msg_175.c,v 1.4 2021/03/30 15:07:53 rillig Exp $ */ +# 3 "msg_175.c" + +// Test for message: initialization of incomplete type '%s' [175] + +struct incomplete; /* expect: 233 */ + +struct incomplete incomplete = { /* expect: 175 */ + "invalid" +}; /* expect: 31 */ diff --git a/usr.bin/xlint/lint1/msg_175.exp b/usr.bin/xlint/lint1/msg_175.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_175.exp @@ -0,0 +1,3 @@ +msg_175.c(8): error: initialization of incomplete type 'incomplete struct incomplete' [175] +msg_175.c(10): error: 'incomplete' has incomplete type 'incomplete struct incomplete' [31] +msg_175.c(6): warning: struct incomplete never defined [233] diff --git a/usr.bin/xlint/lint1/msg_176.c b/usr.bin/xlint/lint1/msg_176.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_176.c @@ -0,0 +1,15 @@ +/* $NetBSD: msg_176.c,v 1.5 2021/08/26 19:23:25 rillig Exp $ */ +# 3 "msg_176.c" + +// Test for message: invalid initializer type %s [176] +/* This message is not used. */ + +/* + * Before init.c 1.161 from 2021-03-28, lint wrongly complained about + * initializers with redundant braces. + * + * C99 allows these, both GCC and Clang warn about them since they are unusual + * and confusing. + */ + +int valid = {{{3}}}; diff --git a/usr.bin/xlint/lint1/msg_177.c b/usr.bin/xlint/lint1/msg_177.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_177.c @@ -0,0 +1,12 @@ +/* $NetBSD: msg_177.c,v 1.2 2021/01/24 16:12:45 rillig Exp $ */ +# 3 "msg_177.c" + +// Test for message: non-constant initializer [177] + +extern int function(void); + +static const int not_a_constant = 13; + +const int var = not_a_constant; /* expect: 177 */ + +const int calling_function = function(); /* expect: 177 */ diff --git a/usr.bin/xlint/lint1/msg_177.exp b/usr.bin/xlint/lint1/msg_177.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_177.exp @@ -0,0 +1,2 @@ +msg_177.c(10): error: non-constant initializer [177] +msg_177.c(12): error: non-constant initializer [177] diff --git a/usr.bin/xlint/lint1/msg_178.c b/usr.bin/xlint/lint1/msg_178.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_178.c @@ -0,0 +1,8 @@ +/* $NetBSD: msg_178.c,v 1.2 2021/01/24 16:12:45 rillig Exp $ */ +# 3 "msg_178.c" + +// Test for message: initializer does not fit [178] + +char fits = 123; + +char does_not_fit = 0x12345678; /* expect: 178 */ diff --git a/usr.bin/xlint/lint1/msg_178.exp b/usr.bin/xlint/lint1/msg_178.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_178.exp @@ -0,0 +1 @@ +msg_178.c(8): warning: initializer does not fit [178] diff --git a/usr.bin/xlint/lint1/msg_179.c b/usr.bin/xlint/lint1/msg_179.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_179.c @@ -0,0 +1,24 @@ +/* $NetBSD: msg_179.c,v 1.3 2021/08/27 19:50:44 rillig Exp $ */ +# 3 "msg_179.c" + +// Test for message: cannot initialize struct/union with no named member [179] +/* This message is not used. */ + +struct { + unsigned int :0; +} no_named_member = { + /* no named member, therefore no initializer expression */ +}; + +struct { + /* no members */ +} empty = { + /* no initializer expressions */ +}; + +struct { + unsigned int: 0; +} no_named_member_init = { + /* expect+1: error: too many struct/union initializers [172] */ + 3, +}; diff --git a/usr.bin/xlint/lint1/msg_179.exp b/usr.bin/xlint/lint1/msg_179.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_179.exp @@ -0,0 +1 @@ +msg_179.c(23): error: too many struct/union initializers [172] diff --git a/usr.bin/xlint/lint1/msg_180.c b/usr.bin/xlint/lint1/msg_180.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_180.c @@ -0,0 +1,12 @@ +/* $NetBSD: msg_180.c,v 1.3 2021/01/31 11:12:07 rillig Exp $ */ +# 3 "msg_180.c" + +// Test for message: bit-field initializer does not fit [180] + +struct example { + unsigned int a: 5; + unsigned int b: 5; +} example_var = { + 32, /* expect: 180 */ + 31 +}; diff --git a/usr.bin/xlint/lint1/msg_180.exp b/usr.bin/xlint/lint1/msg_180.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_180.exp @@ -0,0 +1 @@ +msg_180.c(10): warning: bit-field initializer does not fit [180] diff --git a/usr.bin/xlint/lint1/msg_181.c b/usr.bin/xlint/lint1/msg_181.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_181.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_181.c,v 1.3 2021/03/29 22:59:03 rillig Exp $ */ +# 3 "msg_181.c" + +// Test for message: {}-enclosed initializer required [181] + +struct { int x; } missing_braces = 3; /* expect: 181 */ +struct { int x; } including_braces = { 3 }; diff --git a/usr.bin/xlint/lint1/msg_181.exp b/usr.bin/xlint/lint1/msg_181.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_181.exp @@ -0,0 +1 @@ +msg_181.c(6): error: {}-enclosed initializer required [181] diff --git a/usr.bin/xlint/lint1/msg_182.c b/usr.bin/xlint/lint1/msg_182.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_182.c @@ -0,0 +1,18 @@ +/* $NetBSD: msg_182.c,v 1.3 2021/08/16 18:51:58 rillig Exp $ */ +# 3 "msg_182.c" + +// Test for message: incompatible pointer types (%s != %s) [182] + +void * +return_discarding_volatile(volatile void *arg) +{ + /* expect+1: warning: incompatible pointer types (void != volatile void) [182] */ + return arg; +} + +void +init_discarding_volatile(volatile void *arg) +{ + /* expect+1: warning: incompatible pointer types (void != volatile void) [182] */ + void *array[] = { arg }; +} diff --git a/usr.bin/xlint/lint1/msg_182.exp b/usr.bin/xlint/lint1/msg_182.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_182.exp @@ -0,0 +1,2 @@ +msg_182.c(10): warning: incompatible pointer types (void != volatile void) [182] +msg_182.c(17): warning: incompatible pointer types (void != volatile void) [182] diff --git a/usr.bin/xlint/lint1/msg_183.c b/usr.bin/xlint/lint1/msg_183.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_183.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_183.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_183.c" + +// Test for message: illegal combination of %s (%s) and %s (%s) [183] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_183.exp b/usr.bin/xlint/lint1/msg_183.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_183.exp @@ -0,0 +1 @@ +msg_183.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_184.c b/usr.bin/xlint/lint1/msg_184.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_184.c @@ -0,0 +1,11 @@ +/* $NetBSD: msg_184.c,v 1.4 2021/08/14 13:00:55 rillig Exp $ */ +# 3 "msg_184.c" + +// Test for message: illegal combination of '%s' and '%s' [184] + +int * +example(char *cp) +{ + /* expect+1: illegal combination of 'pointer to int' and 'pointer to char' [184] */ + return cp; +} diff --git a/usr.bin/xlint/lint1/msg_184.exp b/usr.bin/xlint/lint1/msg_184.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_184.exp @@ -0,0 +1 @@ +msg_184.c(10): warning: illegal combination of 'pointer to int' and 'pointer to char' [184] diff --git a/usr.bin/xlint/lint1/msg_185.c b/usr.bin/xlint/lint1/msg_185.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_185.c @@ -0,0 +1,17 @@ +/* $NetBSD: msg_185.c,v 1.5 2021/03/18 21:26:56 rillig Exp $ */ +# 3 "msg_185.c" + +// Test for message: cannot initialize '%s' from '%s' [185] + +typedef struct any { + const void *value; +} any; + +void use(const void *); + +void +initialization_with_redundant_braces(any arg) +{ + any local = { 3.0 }; /* expect: 185 */ + use(&arg); +} diff --git a/usr.bin/xlint/lint1/msg_185.exp b/usr.bin/xlint/lint1/msg_185.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_185.exp @@ -0,0 +1 @@ +msg_185.c(15): error: cannot initialize 'pointer to const void' from 'double' [185] diff --git a/usr.bin/xlint/lint1/msg_186.c b/usr.bin/xlint/lint1/msg_186.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_186.c @@ -0,0 +1,19 @@ +/* $NetBSD: msg_186.c,v 1.4 2021/02/22 15:09:50 rillig Exp $ */ +# 3 "msg_186.c" + +/* Test for message: bit-field initialization is illegal in traditional C [186] */ + +/* lint1-flags: -tw */ + +struct bit_field { + unsigned one: 1; + unsigned three: 3; + unsigned two: 2; +}; + +struct bit_field bit_field = { + 1, + 3.0, /* expect: 186 */ + 2 +}; +/* XXX: The message is misleading. Initialization using integers is ok. */ diff --git a/usr.bin/xlint/lint1/msg_186.exp b/usr.bin/xlint/lint1/msg_186.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_186.exp @@ -0,0 +1 @@ +msg_186.c(16): warning: bit-field initialization is illegal in traditional C [186] diff --git a/usr.bin/xlint/lint1/msg_187.c b/usr.bin/xlint/lint1/msg_187.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_187.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_187.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_187.c" + +// Test for message: non-null byte ignored in string initializer [187] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_187.exp b/usr.bin/xlint/lint1/msg_187.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_187.exp @@ -0,0 +1 @@ +msg_187.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_188.c b/usr.bin/xlint/lint1/msg_188.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_188.c @@ -0,0 +1,25 @@ +/* $NetBSD: msg_188.c,v 1.3 2021/03/28 15:12:20 rillig Exp $ */ +# 3 "msg_188.c" + +/* Test for message: no automatic aggregate initialization in traditional C [188] */ + +/* lint1-flags: -tw */ + +struct point { + int x; + int y; +}; + +struct point global = { + 3, + 4, +}; + +void +function() +{ + struct point local = { /* expect: 188 */ + 3, + 4, + }; +} diff --git a/usr.bin/xlint/lint1/msg_188.exp b/usr.bin/xlint/lint1/msg_188.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_188.exp @@ -0,0 +1 @@ +msg_188.c(21): warning: no automatic aggregate initialization in traditional C [188] diff --git a/usr.bin/xlint/lint1/msg_189.c b/usr.bin/xlint/lint1/msg_189.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_189.c @@ -0,0 +1,21 @@ +/* $NetBSD: msg_189.c,v 1.5 2021/08/26 19:23:25 rillig Exp $ */ +# 3 "msg_189.c" + +/* Test for message: assignment of struct/union illegal in traditional C [189] */ +/* This message is not used. */ + +/* lint1-flags: -tw */ + +struct s { + int member; +}; + +void +example() +{ + struct s a, b; + + a.member = 3; + b = a; /* message 189 is not triggered anymore */ + /* expect-1: 'b' set but not used in function 'example' */ +} diff --git a/usr.bin/xlint/lint1/msg_189.exp b/usr.bin/xlint/lint1/msg_189.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_189.exp @@ -0,0 +1 @@ +msg_189.c(19): warning: 'b' set but not used in function 'example' [191] diff --git a/usr.bin/xlint/lint1/msg_190.c b/usr.bin/xlint/lint1/msg_190.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_190.c @@ -0,0 +1,9 @@ +/* $NetBSD: msg_190.c,v 1.3 2021/07/10 09:24:27 rillig Exp $ */ +# 3 "msg_190.c" + +// Test for message: empty array declaration: %s [190] + +/* expect+1: error: empty array declaration: empty_array [190] */ +double empty_array[] = {}; + +double array[] = { 1 }; diff --git a/usr.bin/xlint/lint1/msg_190.exp b/usr.bin/xlint/lint1/msg_190.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_190.exp @@ -0,0 +1 @@ +msg_190.c(7): error: empty array declaration: empty_array [190] diff --git a/usr.bin/xlint/lint1/msg_191.c b/usr.bin/xlint/lint1/msg_191.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_191.c @@ -0,0 +1,14 @@ +/* $NetBSD: msg_191.c,v 1.3 2021/04/09 20:12:01 rillig Exp $ */ +# 3 "msg_191.c" + +// Test for message: '%s' set but not used in function '%s' [191] + +void +example(void) +{ + int local; + + local = 3; /* expect: 191 */ + + local = 5; +} diff --git a/usr.bin/xlint/lint1/msg_191.exp b/usr.bin/xlint/lint1/msg_191.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_191.exp @@ -0,0 +1 @@ +msg_191.c(11): warning: 'local' set but not used in function 'example' [191] diff --git a/usr.bin/xlint/lint1/msg_192.c b/usr.bin/xlint/lint1/msg_192.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_192.c @@ -0,0 +1,10 @@ +/* $NetBSD: msg_192.c,v 1.3 2021/04/09 20:12:01 rillig Exp $ */ +# 3 "msg_192.c" + +// Test for message: '%s' unused in function '%s' [192] + +void +example(int param) /* expect: 231 */ +{ + int local; /* expect: 192 */ +} diff --git a/usr.bin/xlint/lint1/msg_192.exp b/usr.bin/xlint/lint1/msg_192.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_192.exp @@ -0,0 +1,2 @@ +msg_192.c(9): warning: 'local' unused in function 'example' [192] +msg_192.c(7): warning: argument 'param' unused in function 'example' [231] diff --git a/usr.bin/xlint/lint1/msg_193.c b/usr.bin/xlint/lint1/msg_193.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_193.c @@ -0,0 +1,658 @@ +/* $NetBSD: msg_193.c,v 1.14 2021/08/15 21:51:56 rillig Exp $ */ +# 3 "msg_193.c" + +// Test for message: statement not reached [193] + +/* + * Test the reachability of statements in a function. + * + * if + * if-else + * if-else-if-else + * for + * while + * do-while + * switch + * break + * continue + * goto + * return + * + * constant expression + * system-dependent constant expression + */ + +extern void reachable(void); +extern void unreachable(void); +extern _Bool maybe(void); + + +void +test_statement(void) +{ + reachable(); + reachable(); +} + +void +test_compound_statement(void) +{ + reachable(); + { + reachable(); + reachable(); + } + reachable(); +} + +void +test_if_statement(void) +{ + if (1) + reachable(); + reachable(); + if (0) + unreachable(); /* expect: 193 */ + reachable(); +} + +void +test_if_compound_statement(void) +{ + if (1) { + reachable(); + } + if (1) { + { + { + reachable(); + } + } + } + + if (0) { + unreachable(); /* expect: 193 */ + } + if (0) { + { + { + unreachable(); /* expect: 193 */ + } + } + } +} + +void +test_if_without_else(void) +{ + if (1) + reachable(); + reachable(); + + if (0) + unreachable(); /* expect: 193 */ + reachable(); +} + +void +test_if_with_else(void) +{ + if (1) + reachable(); + else + unreachable(); /* expect: 193 */ + reachable(); + + if (0) + unreachable(); /* expect: 193 */ + else + reachable(); + reachable(); +} + +void +test_if_else_if_else(void) +{ + if (1) + reachable(); + else if (1) /* expect: 193 */ + unreachable(); + else + unreachable(); /* expect: 193 */ + + if (0) + unreachable(); /* expect: 193 */ + else if (1) + reachable(); + else + unreachable(); /* expect: 193 */ + + if (0) + unreachable(); /* expect: 193 */ + else if (0) + unreachable(); /* expect: 193 */ + else + reachable(); +} + +void +test_if_return(void) +{ + if (1) + return; + unreachable(); /* expect: 193 */ +} + +void +test_if_else_return(void) +{ + if (1) + reachable(); + else + return; /* expect: 193 */ + reachable(); +} + +void +test_for_forever(void) +{ + for (;;) + reachable(); + unreachable(); /* expect: 193 */ +} + +void +test_for_true(void) +{ + for (; 1;) + reachable(); + unreachable(); /* expect: 193 */ +} + +void +test_for_false(void) +{ + for (; 0;) + unreachable(); /* expect: 193 */ + reachable(); +} + +void +test_for_break(void) +{ + for (;;) { + reachable(); + break; + unreachable(); /* expect: 193 */ + } + reachable(); +} + +void +test_for_if_break(void) +{ + for (;;) { + reachable(); + if (0) { + unreachable(); /* expect: 193 */ + break; + unreachable(); /* expect: 193 */ + } + if (1) { + reachable(); + break; + unreachable(); /* expect: 193 */ + } + unreachable(); /* expect: 193 */ + } + reachable(); +} + +void +test_for_continue(void) +{ + for (;;) { + reachable(); + continue; + unreachable(); /* expect: 193 */ + } + unreachable(); /* expect: 193 */ +} + +void +test_for_if_continue(void) +{ + for (;;) { + reachable(); + if (0) { + unreachable(); /* expect: 193 */ + continue; + unreachable(); /* expect: 193 */ + } + if (1) { + reachable(); + continue; + unreachable(); /* expect: 193 */ + } + unreachable(); /* expect: 193 */ + } + unreachable(); /* expect: 193 */ +} + +void +test_for_return(void) +{ + for (;;) { + reachable(); + return; + unreachable(); /* expect: 193 */ + } + unreachable(); /* expect: 193 */ +} + +void +test_for_if_return(void) +{ + for (;;) { + reachable(); + if (0) { + unreachable(); /* expect: 193 */ + return; + unreachable(); /* expect: 193 */ + } + if (1) { + reachable(); + return; + unreachable(); /* expect: 193 */ + } + unreachable(); /* expect: 193 */ + } + unreachable(); /* expect: 193 */ +} + +void +test_while_true(void) +{ + while (1) + reachable(); + unreachable(); /* expect: 193 */ +} + +void +test_while_false(void) +{ + while (0) + unreachable(); /* expect: 193 */ + reachable(); +} + +void +test_while_break(void) +{ + while (1) { + reachable(); + break; + unreachable(); /* expect: 193 */ + } + reachable(); +} + +void +test_while_if_break(void) +{ + while (1) { + reachable(); + if (0) { + unreachable(); /* expect: 193 */ + break; + unreachable(); /* expect: 193 */ + } + if (1) { + reachable(); + break; + unreachable(); /* expect: 193 */ + } + unreachable(); /* expect: 193 */ + } + reachable(); +} + +void +test_while_continue(void) +{ + while (1) { + reachable(); + continue; + unreachable(); /* expect: 193 */ + } + unreachable(); /* expect: 193 */ +} + +void +test_while_if_continue(void) +{ + while (1) { + reachable(); + if (0) { + unreachable(); /* expect: 193 */ + continue; + unreachable(); /* expect: 193 */ + } + if (1) { + reachable(); + continue; + unreachable(); /* expect: 193 */ + } + unreachable(); /* expect: 193 */ + } + unreachable(); /* expect: 193 */ +} + +void +test_while_return(void) +{ + while (1) { + reachable(); + return; + unreachable(); /* expect: 193 */ + } + unreachable(); /* expect: 193 */ +} + +void +test_while_if_return(void) +{ + while (1) { + reachable(); + if (0) { + unreachable(); /* expect: 193 */ + return; + unreachable(); /* expect: 193 */ + } + if (1) { + reachable(); + return; + unreachable(); /* expect: 193 */ + } + unreachable(); /* expect: 193 */ + } + unreachable(); /* expect: 193 */ +} + +void +test_do_while_true(void) +{ + do { + reachable(); + } while (1); + unreachable(); /* expect: 193 */ +} + +void +test_do_while_false(void) +{ + do { + reachable(); + } while (0); + reachable(); +} + +void +test_do_while_break(void) +{ + do { + reachable(); + break; + unreachable(); /* expect: 193 */ + } while (1); + reachable(); +} + +void +test_do_while_if_break(void) +{ + do { + reachable(); + if (0) { + unreachable(); /* expect: 193 */ + break; + unreachable(); /* expect: 193 */ + } + if (1) { + reachable(); + break; + unreachable(); /* expect: 193 */ + } + unreachable(); /* expect: 193 */ + } while (1); + reachable(); +} + +void +test_do_while_continue(void) +{ + do { + reachable(); + continue; + unreachable(); /* expect: 193 */ + } while (1); + unreachable(); /* expect: 193 */ +} + +void +test_do_while_if_continue(void) +{ + do { + reachable(); + if (0) { + unreachable(); /* expect: 193 */ + continue; + unreachable(); /* expect: 193 */ + } + if (1) { + reachable(); + continue; + unreachable(); /* expect: 193 */ + } + unreachable(); /* expect: 193 */ + } while (1); + unreachable(); /* expect: 193 */ +} + +void +test_do_while_return(void) +{ + do { + reachable(); + return; + unreachable(); /* expect: 193 */ + } while (1); + unreachable(); /* expect: 193 */ +} + +void +test_do_while_if_return(void) +{ + do { + reachable(); + if (0) { + unreachable(); /* expect: 193 */ + return; + unreachable(); /* expect: 193 */ + } + if (1) { + reachable(); + return; + unreachable(); /* expect: 193 */ + } + unreachable(); /* expect: 193 */ + } while (1); + unreachable(); /* expect: 193 */ +} + +void +test_if_nested(void) +{ + if (0) { + if (1) /* expect: 193 */ + unreachable(); + else + unreachable(); /* expect: 193 *//* XXX: redundant */ + + if (0) + unreachable(); /* expect: 193 *//* XXX: redundant */ + else + unreachable(); + + unreachable(); + } + reachable(); + + if (1) { + if (1) + reachable(); + else + unreachable(); /* expect: 193 */ + + if (0) + unreachable(); /* expect: 193 */ + else + reachable(); + + reachable(); + } + reachable(); +} + +void +test_if_maybe(void) +{ + if (maybe()) { + if (0) + unreachable(); /* expect: 193 */ + else + reachable(); + reachable(); + } + reachable(); + + if (0) { + if (maybe()) /* expect: 193 */ + unreachable(); + else + unreachable(); + unreachable(); + } + reachable(); + + if (1) { + if (maybe()) + reachable(); + else + reachable(); + reachable(); + } + reachable(); +} + +/* + * To compute the reachability graph of this little monster, lint would have + * to keep all statements and their relations from the whole function in + * memory. It doesn't do that. Therefore it does not warn about any + * unreachable statements in this function. + */ +void +test_goto_numbers_alphabetically(void) +{ + goto one; +eight: + goto nine; +five: + return; +four: + goto five; +nine: + goto ten; +one: + goto two; +seven: + goto eight; +six: /* expect: warning: label 'six' unused */ + goto seven; +ten: + return; +three: + goto four; +two: + goto three; +} + +void +test_while_goto(void) +{ + while (1) { + goto out; + break; /* lint only warns with the -b option */ + } + unreachable(); /* expect: 193 */ +out: + reachable(); +} + +void +test_unreachable_label(void) +{ + if (0) + goto unreachable; /* expect: 193 */ + goto reachable; + + /* named_label assumes that any label is reachable. */ +unreachable: + unreachable(); +reachable: + reachable(); +} + +/* TODO: switch */ + +/* TODO: system-dependent constant expression (see tn_system_dependent) */ + +void suppressed(void); + +void +lint_annotation_NOTREACHED(void) +{ + if (0) { + /* expect+1: warning: statement not reached [193] */ + unreachable(); + } + + if (0) { + /* NOTREACHED */ + suppressed(); + } + + if (0) + /* NOTREACHED */ + suppressed(); + + if (1) { + reachable(); + } + + if (1) { + /* NOTREACHED */ + suppressed(); + } + + /* + * Since the condition in the 'if' statement is constant, lint knows + * that the branch is unconditionally taken. The annotation comment + * marks that branch as not reached, which means that any following + * statement cannot be reached as well. + */ + /* expect+1: warning: statement not reached [193] */ + if (1) + /* NOTREACHED */ + suppressed(); +} diff --git a/usr.bin/xlint/lint1/msg_193.exp b/usr.bin/xlint/lint1/msg_193.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_193.exp @@ -0,0 +1,89 @@ +msg_193.c(55): warning: statement not reached [193] +msg_193.c(74): warning: statement not reached [193] +msg_193.c(79): warning: statement not reached [193] +msg_193.c(93): warning: statement not reached [193] +msg_193.c(103): warning: statement not reached [193] +msg_193.c(107): warning: statement not reached [193] +msg_193.c(118): warning: statement not reached [193] +msg_193.c(121): warning: statement not reached [193] +msg_193.c(124): warning: statement not reached [193] +msg_193.c(128): warning: statement not reached [193] +msg_193.c(131): warning: statement not reached [193] +msg_193.c(133): warning: statement not reached [193] +msg_193.c(143): warning: statement not reached [193] +msg_193.c(152): warning: statement not reached [193] +msg_193.c(161): warning: statement not reached [193] +msg_193.c(169): warning: statement not reached [193] +msg_193.c(176): warning: statement not reached [193] +msg_193.c(186): warning: statement not reached [193] +msg_193.c(197): warning: statement not reached [193] +msg_193.c(199): warning: statement not reached [193] +msg_193.c(204): warning: statement not reached [193] +msg_193.c(206): warning: statement not reached [193] +msg_193.c(217): warning: statement not reached [193] +msg_193.c(219): warning: statement not reached [193] +msg_193.c(228): warning: statement not reached [193] +msg_193.c(230): warning: statement not reached [193] +msg_193.c(235): warning: statement not reached [193] +msg_193.c(237): warning: statement not reached [193] +msg_193.c(239): warning: statement not reached [193] +msg_193.c(248): warning: statement not reached [193] +msg_193.c(250): warning: statement not reached [193] +msg_193.c(259): warning: statement not reached [193] +msg_193.c(261): warning: statement not reached [193] +msg_193.c(266): warning: statement not reached [193] +msg_193.c(268): warning: statement not reached [193] +msg_193.c(270): warning: statement not reached [193] +msg_193.c(278): warning: statement not reached [193] +msg_193.c(285): warning: statement not reached [193] +msg_193.c(295): warning: statement not reached [193] +msg_193.c(306): warning: statement not reached [193] +msg_193.c(308): warning: statement not reached [193] +msg_193.c(313): warning: statement not reached [193] +msg_193.c(315): warning: statement not reached [193] +msg_193.c(326): warning: statement not reached [193] +msg_193.c(328): warning: statement not reached [193] +msg_193.c(337): warning: statement not reached [193] +msg_193.c(339): warning: statement not reached [193] +msg_193.c(344): warning: statement not reached [193] +msg_193.c(346): warning: statement not reached [193] +msg_193.c(348): warning: statement not reached [193] +msg_193.c(357): warning: statement not reached [193] +msg_193.c(359): warning: statement not reached [193] +msg_193.c(368): warning: statement not reached [193] +msg_193.c(370): warning: statement not reached [193] +msg_193.c(375): warning: statement not reached [193] +msg_193.c(377): warning: statement not reached [193] +msg_193.c(379): warning: statement not reached [193] +msg_193.c(388): warning: statement not reached [193] +msg_193.c(406): warning: statement not reached [193] +msg_193.c(417): warning: statement not reached [193] +msg_193.c(419): warning: statement not reached [193] +msg_193.c(424): warning: statement not reached [193] +msg_193.c(426): warning: statement not reached [193] +msg_193.c(437): warning: statement not reached [193] +msg_193.c(439): warning: statement not reached [193] +msg_193.c(448): warning: statement not reached [193] +msg_193.c(450): warning: statement not reached [193] +msg_193.c(455): warning: statement not reached [193] +msg_193.c(457): warning: statement not reached [193] +msg_193.c(459): warning: statement not reached [193] +msg_193.c(468): warning: statement not reached [193] +msg_193.c(470): warning: statement not reached [193] +msg_193.c(479): warning: statement not reached [193] +msg_193.c(481): warning: statement not reached [193] +msg_193.c(486): warning: statement not reached [193] +msg_193.c(488): warning: statement not reached [193] +msg_193.c(490): warning: statement not reached [193] +msg_193.c(497): warning: statement not reached [193] +msg_193.c(500): warning: statement not reached [193] +msg_193.c(503): warning: statement not reached [193] +msg_193.c(515): warning: statement not reached [193] +msg_193.c(518): warning: statement not reached [193] +msg_193.c(532): warning: statement not reached [193] +msg_193.c(540): warning: statement not reached [193] +msg_193.c(580): warning: label 'six' unused in function 'test_goto_numbers_alphabetically' [232] +msg_193.c(597): warning: statement not reached [193] +msg_193.c(606): warning: statement not reached [193] +msg_193.c(627): warning: statement not reached [193] +msg_193.c(655): warning: statement not reached [193] diff --git a/usr.bin/xlint/lint1/msg_194.c b/usr.bin/xlint/lint1/msg_194.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_194.c @@ -0,0 +1,13 @@ +/* $NetBSD: msg_194.c,v 1.2 2021/01/30 17:56:29 rillig Exp $ */ +# 3 "msg_194.c" + +// Test for message: label %s redefined [194] + +void example(void) +{ + int i = 0; +label: /* expect: 232 */ + i = 1; +label: /* expect: 194 */ + i = 2; +} diff --git a/usr.bin/xlint/lint1/msg_194.exp b/usr.bin/xlint/lint1/msg_194.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_194.exp @@ -0,0 +1,2 @@ +msg_194.c(11): error: label label redefined [194] +msg_194.c(9): warning: label 'label' unused in function 'example' [232] diff --git a/usr.bin/xlint/lint1/msg_195.c b/usr.bin/xlint/lint1/msg_195.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_195.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_195.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_195.c" + +// Test for message: case not in switch [195] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_195.exp b/usr.bin/xlint/lint1/msg_195.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_195.exp @@ -0,0 +1 @@ +msg_195.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_196.c b/usr.bin/xlint/lint1/msg_196.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_196.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_196.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_196.c" + +// Test for message: case label affected by conversion [196] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_196.exp b/usr.bin/xlint/lint1/msg_196.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_196.exp @@ -0,0 +1 @@ +msg_196.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_197.c b/usr.bin/xlint/lint1/msg_197.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_197.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_197.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_197.c" + +// Test for message: non-constant case expression [197] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_197.exp b/usr.bin/xlint/lint1/msg_197.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_197.exp @@ -0,0 +1 @@ +msg_197.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_198.c b/usr.bin/xlint/lint1/msg_198.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_198.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_198.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_198.c" + +// Test for message: non-integral case expression [198] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_198.exp b/usr.bin/xlint/lint1/msg_198.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_198.exp @@ -0,0 +1 @@ +msg_198.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_199.c b/usr.bin/xlint/lint1/msg_199.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_199.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_199.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_199.c" + +// Test for message: duplicate case in switch: %ld [199] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_199.exp b/usr.bin/xlint/lint1/msg_199.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_199.exp @@ -0,0 +1 @@ +msg_199.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_200.c b/usr.bin/xlint/lint1/msg_200.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_200.c @@ -0,0 +1,15 @@ +/* $NetBSD: msg_200.c,v 1.3 2021/08/27 20:16:50 rillig Exp $ */ +# 3 "msg_200.c" + +// Test for message: duplicate case in switch: %lu [200] + +void +example(unsigned x) +{ + switch (x) { + case 3: + /* expect+1: error: duplicate case in switch: 3 [200] */ + case 3: + break; + } +} diff --git a/usr.bin/xlint/lint1/msg_200.exp b/usr.bin/xlint/lint1/msg_200.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_200.exp @@ -0,0 +1 @@ +msg_200.c(12): error: duplicate case in switch: 3 [200] diff --git a/usr.bin/xlint/lint1/msg_201.c b/usr.bin/xlint/lint1/msg_201.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_201.c @@ -0,0 +1,12 @@ +/* $NetBSD: msg_201.c,v 1.3 2021/08/26 19:23:25 rillig Exp $ */ +# 3 "msg_201.c" + +// Test for message: default outside switch [201] + +void +example(void) +{ + /* expect+1: error: default outside switch [201] */ +default: + return; +} diff --git a/usr.bin/xlint/lint1/msg_201.exp b/usr.bin/xlint/lint1/msg_201.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_201.exp @@ -0,0 +1 @@ +msg_201.c(10): error: default outside switch [201] diff --git a/usr.bin/xlint/lint1/msg_202.c b/usr.bin/xlint/lint1/msg_202.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_202.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_202.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_202.c" + +// Test for message: duplicate default in switch [202] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_202.exp b/usr.bin/xlint/lint1/msg_202.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_202.exp @@ -0,0 +1 @@ +msg_202.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_203.c b/usr.bin/xlint/lint1/msg_203.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_203.c @@ -0,0 +1,18 @@ +/* $NetBSD: msg_203.c,v 1.3 2021/08/22 13:52:19 rillig Exp $ */ +# 3 "msg_203.c" + +/* Test for message: case label must be of type 'int' in traditional C [203] */ + +/* lint1-flags: -tw */ + +example(x) + int x; +{ + switch (x) { + case (char)3: + break; + /* expect+1: warning: case label must be of type 'int' in traditional C [203] */ + case 4L: + break; + } +} diff --git a/usr.bin/xlint/lint1/msg_203.exp b/usr.bin/xlint/lint1/msg_203.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_203.exp @@ -0,0 +1 @@ +msg_203.c(15): warning: case label must be of type 'int' in traditional C [203] diff --git a/usr.bin/xlint/lint1/msg_204.c b/usr.bin/xlint/lint1/msg_204.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_204.c @@ -0,0 +1,101 @@ +/* $NetBSD: msg_204.c,v 1.6 2021/04/05 01:35:34 rillig Exp $ */ +# 3 "msg_204.c" + +// Test for message: controlling expressions must have scalar type [204] + +extern void +extern_function(void); + +void (*function_pointer)(void); + +/* + * Since func.c 1.39 from 2020-12-31 18:51:28, lint had produced an error + * when a controlling expression was a function. Pointers to functions were + * ok though. + */ +void +bug_between_2020_12_31_and_2021_01_08(void) +{ + if (extern_function) + extern_function(); + + /* + * FIXME: For some reason, the ampersand is discarded in + * build_address. This only has a visible effect if the + * t_spec in check_controlling_expression is evaluated too early, + * as has been the case before func.c 1.52 from 2021-01-08. + */ + if (&extern_function) + extern_function(); + + /* This one has always been ok since pointers are scalar types. */ + if (function_pointer) + function_pointer(); +} + +struct s { + int member; +}; + +union u { + int member; +}; + +enum e { + E +}; + +struct arr { + int arr[4]; +}; + +/* C99 6.2.5p2 */ +void if_Bool(_Bool b) { if (b) return; } + +/* C99 6.2.5p3 */ +void if_char(char c) { if (c) return; } + +/* C99 6.2.5p4 */ +void if_signed_char(signed char sc) { if (sc) return; } +void if_short_int(short s) { if (s) return; } +void if_int(int i) { if (i) return; } +void if_long_int(long int l) { if (l) return; } +void if_long_long_int(long long int ll) { if (ll) return; } + +/* C99 6.2.5p6 */ +void if_unsigned_char(unsigned char uc) { if (uc) return; } +void if_unsigned_short_int(unsigned short us) { if (us) return; } +void if_unsigned_int(unsigned int ui) { if (ui) return; } +void if_unsigned_long_int(unsigned long int ul) { if (ul) return; } +void if_unsigned_long_long_int(unsigned long long int ull) { if (ull) return; } + +/* C99 6.2.5p10 */ +void if_float(float f) { if (f) return; } +void if_double(double d) { if (d) return; } +void if_long_double(long double ld) { if (ld) return; } + +/* C99 6.2.5p11 */ +void if_float_Complex(float _Complex fc) { if (fc) return; } +void if_double_Complex(double _Complex dc) { if (dc) return; } +void if_long_double_Complex(long double _Complex ldc) { if (ldc) return; } + +/* C99 6.2.5p16 */ +void if_enum(enum e e) { if (e) return; } + +/* C99 6.2.5p20 */ +void if_array(struct arr arr) { if (arr.arr) return; } +void if_struct(struct s s) { if (s) return; } /* expect: 204 *//* expect: 231 */ +void if_union(union u u) { if (u) return; } /* expect: 204 *//* expect: 231 */ +void if_function(void) { if (if_function) return; } +void if_pointer(void *p) { if (p) return; } + +/* C99 6.8.5 */ +void while_struct(struct s s) { while (s) return; } /* expect: 204 *//* expect: 231 */ +void for_struct(struct s s) { for (;s;) return; } /* expect: 204 *//* expect: 223 *//* expect: 231 */ +void do_while_struct(struct s s) { do { return; } while (s); } /* expect: 204 *//* expect: 231 */ + +/* + * C99 6.5.15 for the '?:' operator does not explicitly mention that the + * controlling expression must have a scalar type, curiously. + */ +int conditional_struct(struct s s) { return s ? 1 : 2; } /* expect: 170 *//* expect: 214 *//* expect: 231 */ diff --git a/usr.bin/xlint/lint1/msg_204.exp b/usr.bin/xlint/lint1/msg_204.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_204.exp @@ -0,0 +1,14 @@ +msg_204.c(87): error: controlling expressions must have scalar type [204] +msg_204.c(87): warning: argument 's' unused in function 'if_struct' [231] +msg_204.c(88): error: controlling expressions must have scalar type [204] +msg_204.c(88): warning: argument 'u' unused in function 'if_union' [231] +msg_204.c(93): error: controlling expressions must have scalar type [204] +msg_204.c(93): warning: argument 's' unused in function 'while_struct' [231] +msg_204.c(94): error: controlling expressions must have scalar type [204] +msg_204.c(94): warning: end-of-loop code not reached [223] +msg_204.c(94): warning: argument 's' unused in function 'for_struct' [231] +msg_204.c(95): error: controlling expressions must have scalar type [204] +msg_204.c(95): warning: argument 's' unused in function 'do_while_struct' [231] +msg_204.c(101): error: first operand must have scalar type, op ? : [170] +msg_204.c(101): warning: function 'conditional_struct' expects to return value [214] +msg_204.c(101): warning: argument 's' unused in function 'conditional_struct' [231] diff --git a/usr.bin/xlint/lint1/msg_205.c b/usr.bin/xlint/lint1/msg_205.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_205.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_205.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_205.c" + +// Test for message: switch expression must have integral type [205] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_205.exp b/usr.bin/xlint/lint1/msg_205.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_205.exp @@ -0,0 +1 @@ +msg_205.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_206.c b/usr.bin/xlint/lint1/msg_206.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_206.c @@ -0,0 +1,28 @@ +/* $NetBSD: msg_206.c,v 1.4 2021/07/08 18:53:57 rillig Exp $ */ +# 3 "msg_206.c" + +// Test for message: enumeration value(s) not handled in switch [206] + +/* lint1-extra-flags: -eh */ + +enum number { + ONE, TWO, THREE +}; + +void +test(enum number num) +{ + switch (num) { + case ONE: + case TWO: + break; + } + /* expect-1: warning: enumeration value(s) not handled in switch [206] */ + + switch (num) { + case ONE: + case TWO: + case THREE: + break; + } +} diff --git a/usr.bin/xlint/lint1/msg_206.exp b/usr.bin/xlint/lint1/msg_206.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_206.exp @@ -0,0 +1 @@ +msg_206.c(19): warning: enumeration value(s) not handled in switch [206] diff --git a/usr.bin/xlint/lint1/msg_207.c b/usr.bin/xlint/lint1/msg_207.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_207.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_207.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_207.c" + +// Test for message: loop not entered at top [207] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_207.exp b/usr.bin/xlint/lint1/msg_207.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_207.exp @@ -0,0 +1 @@ +msg_207.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_208.c b/usr.bin/xlint/lint1/msg_208.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_208.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_208.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_208.c" + +// Test for message: break outside loop or switch [208] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_208.exp b/usr.bin/xlint/lint1/msg_208.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_208.exp @@ -0,0 +1 @@ +msg_208.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_209.c b/usr.bin/xlint/lint1/msg_209.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_209.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_209.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_209.c" + +// Test for message: continue outside loop [209] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_209.exp b/usr.bin/xlint/lint1/msg_209.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_209.exp @@ -0,0 +1 @@ +msg_209.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_210.c b/usr.bin/xlint/lint1/msg_210.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_210.c @@ -0,0 +1,25 @@ +/* $NetBSD: msg_210.c,v 1.5 2021/02/28 12:40:00 rillig Exp $ */ +# 3 "msg_210.c" + +// Test for message: enum type mismatch between '%s' and '%s' in initialization [210] + +enum A { + A1 +}; + +enum B { + B1 +}; + +typedef enum { + C1 +} C; + +typedef enum { + D1 +} D; + +enum A a1 = A1; +enum A a2 = B1; /* expect: 210 */ +C c1 = C1; +C c2 = D1; /* expect: 210 */ diff --git a/usr.bin/xlint/lint1/msg_210.exp b/usr.bin/xlint/lint1/msg_210.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_210.exp @@ -0,0 +1,2 @@ +msg_210.c(23): warning: enum type mismatch between 'enum A' and 'enum B' in initialization [210] +msg_210.c(25): warning: enum type mismatch between 'enum typedef C' and 'enum typedef D' in initialization [210] diff --git a/usr.bin/xlint/lint1/msg_211.c b/usr.bin/xlint/lint1/msg_211.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_211.c @@ -0,0 +1,22 @@ +/* $NetBSD: msg_211.c,v 1.3 2021/08/16 18:51:58 rillig Exp $ */ +# 3 "msg_211.c" + +// Test for message: return value type mismatch (%s) and (%s) [211] + +struct str { + int member; +}; + +int +return_int(double dbl, void *ptr, struct str str) +{ + if (dbl > 0.0) + return dbl; + if (ptr != (void *)0) + /* expect+1: warning: illegal combination of integer (int) and pointer (pointer to void) [183] */ + return ptr; + if (str.member > 0) + /* expect+1: error: return value type mismatch (int) and (struct str) [211 */ + return str; + return 3; +} diff --git a/usr.bin/xlint/lint1/msg_211.exp b/usr.bin/xlint/lint1/msg_211.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_211.exp @@ -0,0 +1,2 @@ +msg_211.c(17): warning: illegal combination of integer (int) and pointer (pointer to void) [183] +msg_211.c(20): error: return value type mismatch (int) and (struct str) [211] diff --git a/usr.bin/xlint/lint1/msg_212.c b/usr.bin/xlint/lint1/msg_212.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_212.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_212.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_212.c" + +// Test for message: cannot return incomplete type [212] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_212.exp b/usr.bin/xlint/lint1/msg_212.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_212.exp @@ -0,0 +1 @@ +msg_212.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_213.c b/usr.bin/xlint/lint1/msg_213.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_213.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_213.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_213.c" + +// Test for message: void function %s cannot return value [213] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_213.exp b/usr.bin/xlint/lint1/msg_213.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_213.exp @@ -0,0 +1 @@ +msg_213.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_214.c b/usr.bin/xlint/lint1/msg_214.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_214.c @@ -0,0 +1,11 @@ +/* $NetBSD: msg_214.c,v 1.4 2021/08/03 18:44:33 rillig Exp $ */ +# 3 "msg_214.c" + +// Test for message: function '%s' expects to return value [214] + +int +int_function(void) +{ + /* expect+1: warning: function 'int_function' expects to return value [214] */ + return; +} diff --git a/usr.bin/xlint/lint1/msg_214.exp b/usr.bin/xlint/lint1/msg_214.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_214.exp @@ -0,0 +1 @@ +msg_214.c(10): warning: function 'int_function' expects to return value [214] diff --git a/usr.bin/xlint/lint1/msg_215.c b/usr.bin/xlint/lint1/msg_215.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_215.c @@ -0,0 +1,39 @@ +/* $NetBSD: msg_215.c,v 1.11 2021/08/29 17:01:27 rillig Exp $ */ +# 3 "msg_215.c" + +// Test for message: function '%s' implicitly declared to return int [215] + +/* + * In traditional C and C90, it was possible to implicitly declare a function + * by just calling it, without defining a prototype first. Such a function + * would then be defined as taking unspecified parameters and returning int. + */ + +struct str { + int dummy; +}; + +/* ARGSUSED */ +void +test(struct str str, const double *p_double) +{ + /* expect+1: error: function 'name' implicitly declared to return int [215] */ + name(); + + /* expect+2: error: 'parenthesized' undefined [99] */ + /* expect+1: error: illegal function (type int) [149] */ + (parenthesized)(); + + /* expect+2: error: type 'struct str' does not have member 'member' [101] */ + /* expect+1: error: illegal function (type int) [149] */ + str.member(); + + /* https://gcc.gnu.org/onlinedocs/gcc/C-Extensions.html */ + __builtin_whatever(123, "string"); + __atomic_whatever(123, "string"); + /* obsolete but still in use, as of 2021 */ + __sync_whatever(123, "string"); + + /* https://software.intel.com/sites/landingpage/IntrinsicsGuide/ */ + _mm_load_sd(p_double); +} diff --git a/usr.bin/xlint/lint1/msg_215.exp b/usr.bin/xlint/lint1/msg_215.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_215.exp @@ -0,0 +1,5 @@ +msg_215.c(21): error: function 'name' implicitly declared to return int [215] +msg_215.c(25): error: 'parenthesized' undefined [99] +msg_215.c(25): error: illegal function (type int) [149] +msg_215.c(29): error: type 'struct str' does not have member 'member' [101] +msg_215.c(29): error: illegal function (type int) [149] diff --git a/usr.bin/xlint/lint1/msg_216.c b/usr.bin/xlint/lint1/msg_216.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_216.c @@ -0,0 +1,13 @@ +/* $NetBSD: msg_216.c,v 1.2 2021/01/30 17:02:58 rillig Exp $ */ +# 3 "msg_216.c" + +// Test for message: function %s has return (e); and return; [216] + +/* implicit int */ +random(int n) +{ + if (n < 0) + return -3; + if (n < 2) + return; +} /* expect: 216 */ diff --git a/usr.bin/xlint/lint1/msg_216.exp b/usr.bin/xlint/lint1/msg_216.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_216.exp @@ -0,0 +1 @@ +msg_216.c(13): warning: function random has return (e); and return; [216] diff --git a/usr.bin/xlint/lint1/msg_217.c b/usr.bin/xlint/lint1/msg_217.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_217.c @@ -0,0 +1,68 @@ +/* $NetBSD: msg_217.c,v 1.9 2021/03/21 15:24:56 rillig Exp $ */ +# 3 "msg_217.c" + +// Test for message: function %s falls off bottom without returning value [217] + +int +random(int n) +{ + if (n < 0) + return -3; +} /* expect: 217 */ + +/* + * The pattern 'do { } while (0)' is often used in statement macros. + * Putting a 'return' at the end of such a macro is legitimate, the embracing + * 'do { } while (0)' is probably there to conform to a coding standard or + * to otherwise reduce confusion. + * + * Seen in external/bsd/libevent/dist/event_tagging.c, function + * encode_int_internal. + * + * Before tree.c 1.243 from 2021-03-21, lint wrongly reported that the + * 'while 0' was unreachable. This has been fixed by allowing the 'while 0' + * in a do-while-false loop to be unreachable. The same could be useful for a + * do-while-true. + * + * Before func.c 1.83 from 2021-03-21, lint wrongly reported that the function + * would fall off the bottom. + */ +int +do_while_return(int i) +{ + do { + return i; + } while (0); +} + +/* + * C99 5.1.2.2.3 "Program termination" p1 defines that as a special exception, + * the function 'main' does not have to return a value, reaching the bottom + * is equivalent to returning 0. + * + * Before func.c 1.72 from 2021-02-21, lint had wrongly warned about this. + */ +int +main(void) +{ +} + +int +reachable_continue_leads_to_endless_loop(void) +{ + for (;;) { + if (1) + continue; + break; + } +} + +int +unreachable_continue_falls_through(void) +{ + for (;;) { + if (0) + continue; /* expect: statement not reached */ + break; + } +} /* expect: 217 */ diff --git a/usr.bin/xlint/lint1/msg_217.exp b/usr.bin/xlint/lint1/msg_217.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_217.exp @@ -0,0 +1,3 @@ +msg_217.c(11): warning: function random falls off bottom without returning value [217] +msg_217.c(65): warning: statement not reached [193] +msg_217.c(68): warning: function unreachable_continue_falls_through falls off bottom without returning value [217] diff --git a/usr.bin/xlint/lint1/msg_218.c b/usr.bin/xlint/lint1/msg_218.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_218.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_218.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_218.c" + +// Test for message: ANSI C treats constant as unsigned, op %s [218] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_218.exp b/usr.bin/xlint/lint1/msg_218.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_218.exp @@ -0,0 +1 @@ +msg_218.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_219.c b/usr.bin/xlint/lint1/msg_219.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_219.c @@ -0,0 +1,12 @@ +/* $NetBSD: msg_219.c,v 1.3 2021/01/31 11:12:07 rillig Exp $ */ +# 3 "msg_219.c" + + +/* Test for message: concatenated strings are illegal in traditional C [219] */ + +/* lint1-flags: -t -w */ + +char concat1[] = "one"; +char concat2[] = "one" "two"; /* expect: 219 */ +char concat3[] = "one" "two" "three"; /* expect: 219 */ +char concat4[] = "one" "two" "three" "four"; /* expect: 219 */ diff --git a/usr.bin/xlint/lint1/msg_219.exp b/usr.bin/xlint/lint1/msg_219.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_219.exp @@ -0,0 +1,3 @@ +msg_219.c(10): warning: concatenated strings are illegal in traditional C [219] +msg_219.c(11): warning: concatenated strings are illegal in traditional C [219] +msg_219.c(12): warning: concatenated strings are illegal in traditional C [219] diff --git a/usr.bin/xlint/lint1/msg_220.c b/usr.bin/xlint/lint1/msg_220.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_220.c @@ -0,0 +1,79 @@ +/* $NetBSD: msg_220.c,v 1.7 2021/08/29 09:29:32 rillig Exp $ */ +# 3 "msg_220.c" + +// Test for message: fallthrough on case statement [220] + +/* lint1-extra-flags: -h */ + +extern void +println(const char *); + +void +example(int n) +{ + switch (n) { + case 1: + case 3: + case 5: + println("odd"); + case 2: /* expect: 220 */ + case 7: + println("prime"); + default: /* expect: 284 */ + println("number"); + } +} + +void +example1(int n) +{ + switch (n) { + case 1: + case 3: + case 5: + println("odd"); + __attribute__((__fallthrough__)); + case 2: + case 7: + println("prime"); + __attribute__((__fallthrough__)); + default: + println("number"); + } +} + +/* https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html#index-Wimplicit-fallthrough */ +void +annotation_comment_variations(int n) +{ + switch (n) { + case 0: + println("0"); + /* FALLTHROUGH */ + case 1: + println("1"); + /* Seen in libarchive/archive_string.c, macro WRITE_UC. */ + /* FALL THROUGH */ + /* Lint warned before lex.c 1.79 from 2021-08-29. */ + case 2: + println("2"); + /* FALLS THROUGH */ + /* expect+1: warning: fallthrough on case statement [220] */ + case 3: + println("3"); + /* intentionally falls through */ + /* expect+1: warning: fallthrough on case statement [220] */ + case 4: + println("4"); + /* This is the Splint variant, which is seldom used. */ + /* @fallthrough@ */ + /* expect+1: warning: fallthrough on case statement [220] */ + case 5: + println("5"); + /* Seen in unbound/lookup3.c, function hashlittle. */ + /* Lint warned before lex.c 1.80 from 2021-08-29. */ + /* fallthrough */ + case 6: + println("6"); + } +} diff --git a/usr.bin/xlint/lint1/msg_220.exp b/usr.bin/xlint/lint1/msg_220.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_220.exp @@ -0,0 +1,5 @@ +msg_220.c(19): warning: fallthrough on case statement [220] +msg_220.c(22): warning: fallthrough on default statement [284] +msg_220.c(62): warning: fallthrough on case statement [220] +msg_220.c(66): warning: fallthrough on case statement [220] +msg_220.c(71): warning: fallthrough on case statement [220] diff --git a/usr.bin/xlint/lint1/msg_221.c b/usr.bin/xlint/lint1/msg_221.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_221.c @@ -0,0 +1,12 @@ +/* $NetBSD: msg_221.c,v 1.4 2021/02/22 15:09:50 rillig Exp $ */ +# 3 "msg_221.c" + +// Test for message: initialization of unsigned with negative constant [221] + +struct example { + unsigned int a: 5; + unsigned int b: 5; +} example_var = { + -1, /* expect: 221 */ + 31 +}; diff --git a/usr.bin/xlint/lint1/msg_221.exp b/usr.bin/xlint/lint1/msg_221.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_221.exp @@ -0,0 +1 @@ +msg_221.c(10): warning: initialization of unsigned with negative constant [221] diff --git a/usr.bin/xlint/lint1/msg_222.c b/usr.bin/xlint/lint1/msg_222.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_222.c @@ -0,0 +1,31 @@ +/* $NetBSD: msg_222.c,v 1.3 2021/08/27 20:49:25 rillig Exp $ */ +# 3 "msg_222.c" + +// Test for message: conversion of negative constant to unsigned type [222] + +/* expect+1: warning: initialization of unsigned with negative constant [221] */ +unsigned int global = -1; + +void take_unsigned_int(unsigned int); + +void +function(void) +{ + /* expect+1: warning: initialization of unsigned with negative constant [221] */ + unsigned int local = -1; + + /* expect+1: warning: conversion of negative constant to unsigned type, arg #1 [296] */ + take_unsigned_int(-1); + + if (local & -1) + return; + + /* expect+1: warning: comparison of unsigned int with negative constant, op < [162] */ + if (local < -1) + return; + + local &= -1; + + /* expect+1: warning: conversion of negative constant to unsigned type [222] */ + local += -1; +} diff --git a/usr.bin/xlint/lint1/msg_222.exp b/usr.bin/xlint/lint1/msg_222.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_222.exp @@ -0,0 +1,5 @@ +msg_222.c(7): warning: initialization of unsigned with negative constant [221] +msg_222.c(15): warning: initialization of unsigned with negative constant [221] +msg_222.c(18): warning: conversion of negative constant to unsigned type, arg #1 [296] +msg_222.c(24): warning: comparison of unsigned int with negative constant, op < [162] +msg_222.c(30): warning: conversion of negative constant to unsigned type [222] diff --git a/usr.bin/xlint/lint1/msg_223.c b/usr.bin/xlint/lint1/msg_223.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_223.c @@ -0,0 +1,11 @@ +/* $NetBSD: msg_223.c,v 1.2 2021/01/30 17:02:58 rillig Exp $ */ +# 3 "msg_223.c" + +// Test for message: end-of-loop code not reached [223] + +void +example(int n) +{ + for (int i = 0; i < n; i++) /* expect: 223 */ + break; +} diff --git a/usr.bin/xlint/lint1/msg_223.exp b/usr.bin/xlint/lint1/msg_223.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_223.exp @@ -0,0 +1 @@ +msg_223.c(9): warning: end-of-loop code not reached [223] diff --git a/usr.bin/xlint/lint1/msg_224.c b/usr.bin/xlint/lint1/msg_224.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_224.c @@ -0,0 +1,11 @@ +/* $NetBSD: msg_224.c,v 1.3 2021/04/05 01:35:34 rillig Exp $ */ +# 3 "msg_224.c" + +// Test for message: cannot recover from previous errors [224] + +void example1(void) { "syntax" error; } /* expect: 249 */ +void example2(void) { "syntax" error; } /* expect: 249 */ +void example3(void) { "syntax" error; } /* expect: 249 */ +void example4(void) { "syntax" error; } /* expect: 249 */ +void example5(void) { "syntax" error; } /* expect: 249 *//* expect: 224 */ +void example6(void) { "syntax" error; } diff --git a/usr.bin/xlint/lint1/msg_224.exp b/usr.bin/xlint/lint1/msg_224.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_224.exp @@ -0,0 +1,6 @@ +msg_224.c(6): error: syntax error 'error' [249] +msg_224.c(7): error: syntax error 'error' [249] +msg_224.c(8): error: syntax error 'error' [249] +msg_224.c(9): error: syntax error 'error' [249] +msg_224.c(10): error: syntax error 'error' [249] +msg_224.c(10): error: cannot recover from previous errors [224] diff --git a/usr.bin/xlint/lint1/msg_225.c b/usr.bin/xlint/lint1/msg_225.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_225.c @@ -0,0 +1,20 @@ +/* $NetBSD: msg_225.c,v 1.2 2021/01/24 17:55:41 rillig Exp $ */ +# 3 "msg_225.c" + +// Test for message: static function called but not defined: %s() [225] + +static void undefined(void); + +static void defined_later(void); + +void +caller(void) +{ + undefined(); /* expect: 225 */ + defined_later(); +} + +static void +defined_later(void) +{ +} diff --git a/usr.bin/xlint/lint1/msg_225.exp b/usr.bin/xlint/lint1/msg_225.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_225.exp @@ -0,0 +1 @@ +msg_225.c(13): error: static function called but not defined: undefined() [225] diff --git a/usr.bin/xlint/lint1/msg_226.c b/usr.bin/xlint/lint1/msg_226.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_226.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_226.c,v 1.3 2021/08/27 20:49:25 rillig Exp $ */ +# 3 "msg_226.c" + +// Test for message: static variable %s unused [226] + +/* expect+1: warning: static variable unused unused [226] */ +static int unused; diff --git a/usr.bin/xlint/lint1/msg_226.exp b/usr.bin/xlint/lint1/msg_226.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_226.exp @@ -0,0 +1 @@ +msg_226.c(7): warning: static variable unused unused [226] diff --git a/usr.bin/xlint/lint1/msg_227.c b/usr.bin/xlint/lint1/msg_227.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_227.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_227.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_227.c" + +// Test for message: const object %s should have initializer [227] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_227.exp b/usr.bin/xlint/lint1/msg_227.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_227.exp @@ -0,0 +1 @@ +msg_227.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_228.c b/usr.bin/xlint/lint1/msg_228.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_228.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_228.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_228.c" + +// Test for message: function cannot return const or volatile object [228] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_228.exp b/usr.bin/xlint/lint1/msg_228.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_228.exp @@ -0,0 +1 @@ +msg_228.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_229.c b/usr.bin/xlint/lint1/msg_229.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_229.c @@ -0,0 +1,18 @@ +/* $NetBSD: msg_229.c,v 1.5 2021/02/28 01:20:54 rillig Exp $ */ +# 3 "msg_229.c" + +// Test for message: converting '%s' to '%s' is questionable [229] + +typedef double (*unary_operator)(double); + +int * +to_int_pointer(unary_operator op) +{ + return (int *)op; /* expect: 229 */ +} + +unary_operator +to_function_pointer(int *p) +{ + return (unary_operator)p; /* expect: 229 */ +} diff --git a/usr.bin/xlint/lint1/msg_229.exp b/usr.bin/xlint/lint1/msg_229.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_229.exp @@ -0,0 +1,2 @@ +msg_229.c(11): warning: converting 'pointer to function(double) returning double' to 'pointer to int' is questionable [229] +msg_229.c(17): warning: converting 'pointer to int' to 'pointer to function(double) returning double' is questionable [229] diff --git a/usr.bin/xlint/lint1/msg_230.c b/usr.bin/xlint/lint1/msg_230.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_230.c @@ -0,0 +1,114 @@ +/* $NetBSD: msg_230.c,v 1.8 2021/08/28 15:25:10 rillig Exp $ */ +# 3 "msg_230.c" + +// Test for message: nonportable character comparison, op %s [230] + +/* lint1-flags: -S -g -p -w */ +/* lint1-only-if: schar */ + +/* + * C11 6.2.5p15 defines that 'char' has the same range, representation, and + * behavior as either 'signed char' or 'unsigned char'. + * + * The portable range of 'char' is from 0 to 127 since all lint platforms + * define CHAR_SIZE to be 8. + * + * See msg_162.c, which covers 'signed char' and 'unsigned char'. + */ + +void +compare_plain_char(char c) +{ + /* expect+1: warning: nonportable character comparison, op == [230] */ + if (c == -129) + return; + /* expect+1: warning: nonportable character comparison, op == [230] */ + if (c == -128) + return; + /* expect+1: warning: nonportable character comparison, op == [230] */ + if (c == -1) + return; + if (c == 0) + return; + if (c == 127) + return; + /* expect+1: warning: nonportable character comparison, op == [230] */ + if (c == 128) + return; + /* expect+1: warning: nonportable character comparison, op == [230] */ + if (c == 255) + return; + /* expect+1: warning: nonportable character comparison, op == [230] */ + if (c == 256) + return; +} + +void +compare_plain_char_yoda(char c) +{ + /* expect+1: warning: nonportable character comparison, op == [230] */ + if (-129 == c) + return; + /* expect+1: warning: nonportable character comparison, op == [230] */ + if (-128 == c) + return; + /* expect+1: warning: nonportable character comparison, op == [230] */ + if (-1 == c) + return; + if (0 == c) + return; + if (127 == c) + return; + /* expect+1: warning: nonportable character comparison, op == [230] */ + if (128 == c) + return; + /* expect+1: warning: nonportable character comparison, op == [230] */ + if (255 == c) + return; + /* expect+1: warning: nonportable character comparison, op == [230] */ + if (256 == c) + return; +} + +void +compare_lt(char c) +{ + + /* expect+1: warning: nonportable character comparison, op > [230] */ + if (c > -2) + return; + /* expect+1: warning: nonportable character comparison, op >= [230] */ + if (c >= -1) + return; + + /* + * XXX: The following two comparisons have the same effect, yet lint + * only warns about one of them. + */ + /* expect+1: warning: nonportable character comparison, op > [230] */ + if (c > -1) + return; + /* + * On platforms where char is unsigned, lint warns that the + * comparison always evaluates to true; see msg_230_uchar.c. + */ + if (c >= 0) + return; + + /* + * XXX: The following two comparisons have the same effect, yet lint + * only warns about one of them. + */ + if (c > 127) + return; + /* expect+1: warning: nonportable character comparison, op >= [230] */ + if (c >= 128) + return; + + /* expect+1: warning: nonportable character comparison, op > [230] */ + if (c > 128) + return; + /* expect+1: warning: nonportable character comparison, op >= [230] */ + if (c >= 129) + return; +} diff --git a/usr.bin/xlint/lint1/msg_230.exp b/usr.bin/xlint/lint1/msg_230.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_230.exp @@ -0,0 +1,18 @@ +msg_230.c(23): warning: nonportable character comparison, op == [230] +msg_230.c(26): warning: nonportable character comparison, op == [230] +msg_230.c(29): warning: nonportable character comparison, op == [230] +msg_230.c(36): warning: nonportable character comparison, op == [230] +msg_230.c(39): warning: nonportable character comparison, op == [230] +msg_230.c(42): warning: nonportable character comparison, op == [230] +msg_230.c(50): warning: nonportable character comparison, op == [230] +msg_230.c(53): warning: nonportable character comparison, op == [230] +msg_230.c(56): warning: nonportable character comparison, op == [230] +msg_230.c(63): warning: nonportable character comparison, op == [230] +msg_230.c(66): warning: nonportable character comparison, op == [230] +msg_230.c(69): warning: nonportable character comparison, op == [230] +msg_230.c(78): warning: nonportable character comparison, op > [230] +msg_230.c(81): warning: nonportable character comparison, op >= [230] +msg_230.c(89): warning: nonportable character comparison, op > [230] +msg_230.c(105): warning: nonportable character comparison, op >= [230] +msg_230.c(109): warning: nonportable character comparison, op > [230] +msg_230.c(112): warning: nonportable character comparison, op >= [230] diff --git a/usr.bin/xlint/lint1/msg_230_uchar.c b/usr.bin/xlint/lint1/msg_230_uchar.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_230_uchar.c @@ -0,0 +1,118 @@ +/* $NetBSD: msg_230_uchar.c,v 1.4 2021/08/28 15:25:10 rillig Exp $ */ +# 3 "msg_230_uchar.c" + +// Test for message: nonportable character comparison, op %s [230] + +/* lint1-flags: -S -g -p -w */ +/* lint1-only-if: uchar */ + +/* + * C11 6.2.5p15 defines that 'char' has the same range, representation, and + * behavior as either 'signed char' or 'unsigned char'. + * + * The portable range of 'char' is from 0 to 127 since all lint platforms + * define CHAR_SIZE to be 8. + * + * See msg_162.c, which covers 'signed char' and 'unsigned char'. + */ + +void +compare_plain_char(char c) +{ + /* expect+1: warning: nonportable character comparison, op == [230] */ + if (c == -129) + return; + /* expect+1: warning: nonportable character comparison, op == [230] */ + if (c == -128) + return; + /* expect+1: warning: nonportable character comparison, op == [230] */ + if (c == -1) + return; + if (c == 0) + return; + if (c == 127) + return; + /* expect+1: warning: nonportable character comparison, op == [230] */ + if (c == 128) + return; + /* expect+1: warning: nonportable character comparison, op == [230] */ + if (c == 255) + return; + /* expect+1: warning: nonportable character comparison, op == [230] */ + if (c == 256) + return; +} + +void +compare_plain_char_yoda(char c) +{ + /* expect+1: warning: nonportable character comparison, op == [230] */ + if (-129 == c) + return; + /* expect+1: warning: nonportable character comparison, op == [230] */ + if (-128 == c) + return; + /* expect+1: warning: nonportable character comparison, op == [230] */ + if (-1 == c) + return; + if (0 == c) + return; + if (127 == c) + return; + /* expect+1: warning: nonportable character comparison, op == [230] */ + if (128 == c) + return; + /* expect+1: warning: nonportable character comparison, op == [230] */ + if (255 == c) + return; + /* expect+1: warning: nonportable character comparison, op == [230] */ + if (256 == c) + return; +} + +void +compare_lt(char c) +{ + + /* expect+1: warning: nonportable character comparison, op > [230] */ + if (c > -2) + return; + /* expect+1: warning: nonportable character comparison, op >= [230] */ + if (c >= -1) + return; + + /* + * XXX: The following two comparisons have the same effect, yet lint + * only warns about one of them. + */ + /* expect+1: warning: nonportable character comparison, op > [230] */ + if (c > -1) + return; + /* + * This warning only occurs on uchar platforms since on these + * platforms it is always true. Code that needs this ordered + * comparison on values of type plain char is questionable since it + * behaves differently depending on the platform. Such a comparison + * should never be needed. + */ + /* expect+1: warning: comparison of char with 0, op >= [162] */ + if (c >= 0) + return; + + /* + * XXX: The following two comparisons have the same effect, yet lint + * only warns about one of them. + */ + if (c > 127) + return; + /* expect+1: warning: nonportable character comparison, op >= [230] */ + if (c >= 128) + return; + + /* expect+1: warning: nonportable character comparison, op > [230] */ + if (c > 128) + return; + /* expect+1: warning: nonportable character comparison, op >= [230] */ + if (c >= 129) + return; +} diff --git a/usr.bin/xlint/lint1/msg_230_uchar.exp b/usr.bin/xlint/lint1/msg_230_uchar.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_230_uchar.exp @@ -0,0 +1,19 @@ +msg_230_uchar.c(23): warning: nonportable character comparison, op == [230] +msg_230_uchar.c(26): warning: nonportable character comparison, op == [230] +msg_230_uchar.c(29): warning: nonportable character comparison, op == [230] +msg_230_uchar.c(36): warning: nonportable character comparison, op == [230] +msg_230_uchar.c(39): warning: nonportable character comparison, op == [230] +msg_230_uchar.c(42): warning: nonportable character comparison, op == [230] +msg_230_uchar.c(50): warning: nonportable character comparison, op == [230] +msg_230_uchar.c(53): warning: nonportable character comparison, op == [230] +msg_230_uchar.c(56): warning: nonportable character comparison, op == [230] +msg_230_uchar.c(63): warning: nonportable character comparison, op == [230] +msg_230_uchar.c(66): warning: nonportable character comparison, op == [230] +msg_230_uchar.c(69): warning: nonportable character comparison, op == [230] +msg_230_uchar.c(78): warning: nonportable character comparison, op > [230] +msg_230_uchar.c(81): warning: nonportable character comparison, op >= [230] +msg_230_uchar.c(89): warning: nonportable character comparison, op > [230] +msg_230_uchar.c(99): warning: comparison of char with 0, op >= [162] +msg_230_uchar.c(109): warning: nonportable character comparison, op >= [230] +msg_230_uchar.c(113): warning: nonportable character comparison, op > [230] +msg_230_uchar.c(116): warning: nonportable character comparison, op >= [230] diff --git a/usr.bin/xlint/lint1/msg_231.c b/usr.bin/xlint/lint1/msg_231.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_231.c @@ -0,0 +1,10 @@ +/* $NetBSD: msg_231.c,v 1.3 2021/04/09 20:12:01 rillig Exp $ */ +# 3 "msg_231.c" + +// Test for message: argument '%s' unused in function '%s' [231] + +void +example(int param) /* expect: 231 */ +{ + int local; /* expect: 192 */ +} diff --git a/usr.bin/xlint/lint1/msg_231.exp b/usr.bin/xlint/lint1/msg_231.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_231.exp @@ -0,0 +1,2 @@ +msg_231.c(9): warning: 'local' unused in function 'example' [192] +msg_231.c(7): warning: argument 'param' unused in function 'example' [231] diff --git a/usr.bin/xlint/lint1/msg_232.c b/usr.bin/xlint/lint1/msg_232.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_232.c @@ -0,0 +1,15 @@ +/* $NetBSD: msg_232.c,v 1.5 2021/07/11 19:30:56 rillig Exp $ */ +# 3 "msg_232.c" + +// Test for message: label '%s' unused in function '%s' [232] + +void +example(void) +{ + goto used_label; + /* expect+1: label 'unused_label' unused in function 'example' [232] */ +unused_label: + return; +used_label: + return; +} diff --git a/usr.bin/xlint/lint1/msg_232.exp b/usr.bin/xlint/lint1/msg_232.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_232.exp @@ -0,0 +1 @@ +msg_232.c(11): warning: label 'unused_label' unused in function 'example' [232] diff --git a/usr.bin/xlint/lint1/msg_233.c b/usr.bin/xlint/lint1/msg_233.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_233.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_233.c,v 1.3 2021/08/27 20:49:25 rillig Exp $ */ +# 3 "msg_233.c" + +// Test for message: struct %s never defined [233] + +/* expect+1: warning: struct declared_but_not_defined never defined [233] */ +struct declared_but_not_defined; diff --git a/usr.bin/xlint/lint1/msg_233.exp b/usr.bin/xlint/lint1/msg_233.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_233.exp @@ -0,0 +1 @@ +msg_233.c(7): warning: struct declared_but_not_defined never defined [233] diff --git a/usr.bin/xlint/lint1/msg_234.c b/usr.bin/xlint/lint1/msg_234.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_234.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_234.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_234.c" + +// Test for message: union %s never defined [234] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_234.exp b/usr.bin/xlint/lint1/msg_234.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_234.exp @@ -0,0 +1 @@ +msg_234.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_235.c b/usr.bin/xlint/lint1/msg_235.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_235.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_235.c,v 1.3 2021/08/27 20:49:25 rillig Exp $ */ +# 3 "msg_235.c" + +// Test for message: enum %s never defined [235] + +/* expect+1: warning: enum declared_but_not_defined never defined [235] */ +enum declared_but_not_defined; diff --git a/usr.bin/xlint/lint1/msg_235.exp b/usr.bin/xlint/lint1/msg_235.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_235.exp @@ -0,0 +1 @@ +msg_235.c(7): warning: enum declared_but_not_defined never defined [235] diff --git a/usr.bin/xlint/lint1/msg_236.c b/usr.bin/xlint/lint1/msg_236.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_236.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_236.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_236.c" + +// Test for message: static function %s unused [236] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_236.exp b/usr.bin/xlint/lint1/msg_236.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_236.exp @@ -0,0 +1 @@ +msg_236.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_237.c b/usr.bin/xlint/lint1/msg_237.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_237.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_237.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_237.c" + +// Test for message: redeclaration of formal parameter %s [237] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_237.exp b/usr.bin/xlint/lint1/msg_237.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_237.exp @@ -0,0 +1 @@ +msg_237.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_238.c b/usr.bin/xlint/lint1/msg_238.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_238.c @@ -0,0 +1,18 @@ +/* $NetBSD: msg_238.c,v 1.4 2021/03/29 22:24:34 rillig Exp $ */ +# 3 "msg_238.c" + +/* Test for message: initialization of union is illegal in traditional C [238] */ + +/* lint1-flags: -tw */ + +struct { + int x; +} s = { + 3 +}; + +union { + int x; +} u = { /* expect: 238 */ + 3 +}; diff --git a/usr.bin/xlint/lint1/msg_238.exp b/usr.bin/xlint/lint1/msg_238.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_238.exp @@ -0,0 +1 @@ +msg_238.c(16): warning: initialization of union is illegal in traditional C [238] diff --git a/usr.bin/xlint/lint1/msg_239.c b/usr.bin/xlint/lint1/msg_239.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_239.c @@ -0,0 +1,18 @@ +/* $NetBSD: msg_239.c,v 1.5 2021/04/05 01:35:34 rillig Exp $ */ +# 3 "msg_239.c" + +// Test for message: constant argument to '!' [239] + +/* lint1-extra-flags: -h */ + +_Bool +example(int n) +{ + _Bool b; + + b = !0; /* expect: constant in conditional context *//* expect: 239 */ + b = !1; /* expect: constant in conditional context *//* expect: 239 */ + b = !(n > 1); + + return b; +} diff --git a/usr.bin/xlint/lint1/msg_239.exp b/usr.bin/xlint/lint1/msg_239.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_239.exp @@ -0,0 +1,4 @@ +msg_239.c(13): warning: constant in conditional context [161] +msg_239.c(13): warning: constant argument to '!' [239] +msg_239.c(14): warning: constant in conditional context [161] +msg_239.c(14): warning: constant argument to '!' [239] diff --git a/usr.bin/xlint/lint1/msg_240.c b/usr.bin/xlint/lint1/msg_240.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_240.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_240.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_240.c" + +// Test for message: assignment of different structures (%s != %s) [240] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_240.exp b/usr.bin/xlint/lint1/msg_240.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_240.exp @@ -0,0 +1 @@ +msg_240.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_241.c b/usr.bin/xlint/lint1/msg_241.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_241.c @@ -0,0 +1,96 @@ +/* $NetBSD: msg_241.c,v 1.6 2021/08/16 20:11:03 rillig Exp $ */ +# 3 "msg_241.c" + +// Test for message: dubious operation on enum, op %s [241] +// +// As of February 2021, the option -e is not enabled by default in +// share/mk/sys.mk, therefore this message is neither well-known nor +// well-tested. + +/* lint1-extra-flags: -e */ + +/* + * Enums are a possible implementation of bit-sets. + */ +enum color { + RED = 1 << 0, + GREEN = 1 << 1, + BLUE = 1 << 2 +}; + +extern void sink_bool(_Bool); +extern void sink_int(int); +extern void sink_color(enum color); + +void +example(void) +{ + enum color c = RED; + + sink_bool(!c); /* expect: 241 */ + sink_color(~c); /* expect: 241 */ + ++c; /* expect: 241 */ + --c; /* expect: 241 */ + c++; /* expect: 241 */ + c--; /* expect: 241 */ + sink_color(+c); /* expect: 241 */ + sink_color(-c); /* expect: 241 */ + sink_color(c * c); /* expect: 241 */ + sink_color(c / c); /* expect: 241 */ + sink_color(c % c); /* expect: 241 */ + sink_color(c + c); /* expect: 241 */ + sink_color(c - c); /* expect: 241 */ + sink_color(c << c); /* expect: 241 */ + sink_color(c >> c); /* expect: 241 */ + + sink_bool(c < c); + sink_bool(c <= c); + sink_bool(c > c); + sink_bool(c >= c); + sink_bool(c == c); + sink_bool(c != c); + + sink_color(c & c); /* expect: 241 */ + sink_color(c ^ c); /* expect: 241 */ + sink_color(c | c); /* expect: 241 */ + + sink_bool(c && c); /* expect: 241 */ + sink_bool(c || c); /* expect: 241 */ + sink_color(c ? c : BLUE); + + c = GREEN; + c *= c; /* expect: 241 */ + c /= c; /* expect: 241 */ + c %= c; /* expect: 241 */ + c += c; /* expect: 241 */ + c -= c; /* expect: 241 */ + c <<= c; /* expect: 241 */ + c >>= c; /* expect: 241 */ + c &= c; /* expect: 241 */ + c ^= c; /* expect: 241 */ + c |= c; /* expect: 241 */ + + /* The cast to unsigned is required by GCC at WARNS=6. */ + c &= ~(unsigned)GREEN; /* expect: 241 */ +} + +void +cover_typeok_enum(enum color c, int i) +{ + /* expect+2: warning: dubious operation on enum, op * [241] */ + /* expect+1: warning: combination of 'enum color' and 'int', op > [242] */ + if (c * i > 5) + return; +} + +const char * +color_name(enum color c) +{ + static const char *name[] = { "red", "green", "blue" }; + + if (c == RED) + return *(c + name); /* unusual but allowed */ + if (c == GREEN) + return c[name]; /* even more unusual */ + return name[c]; +} diff --git a/usr.bin/xlint/lint1/msg_241.exp b/usr.bin/xlint/lint1/msg_241.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_241.exp @@ -0,0 +1,33 @@ +msg_241.c(30): warning: dubious operation on enum, op ! [241] +msg_241.c(31): warning: dubious operation on enum, op ~ [241] +msg_241.c(32): warning: dubious operation on enum, op ++x [241] +msg_241.c(33): warning: dubious operation on enum, op --x [241] +msg_241.c(34): warning: dubious operation on enum, op x++ [241] +msg_241.c(35): warning: dubious operation on enum, op x-- [241] +msg_241.c(36): warning: dubious operation on enum, op + [241] +msg_241.c(37): warning: dubious operation on enum, op - [241] +msg_241.c(38): warning: dubious operation on enum, op * [241] +msg_241.c(39): warning: dubious operation on enum, op / [241] +msg_241.c(40): warning: dubious operation on enum, op % [241] +msg_241.c(41): warning: dubious operation on enum, op + [241] +msg_241.c(42): warning: dubious operation on enum, op - [241] +msg_241.c(43): warning: dubious operation on enum, op << [241] +msg_241.c(44): warning: dubious operation on enum, op >> [241] +msg_241.c(53): warning: dubious operation on enum, op & [241] +msg_241.c(54): warning: dubious operation on enum, op ^ [241] +msg_241.c(55): warning: dubious operation on enum, op | [241] +msg_241.c(57): warning: dubious operation on enum, op && [241] +msg_241.c(58): warning: dubious operation on enum, op || [241] +msg_241.c(62): warning: dubious operation on enum, op *= [241] +msg_241.c(63): warning: dubious operation on enum, op /= [241] +msg_241.c(64): warning: dubious operation on enum, op %= [241] +msg_241.c(65): warning: dubious operation on enum, op += [241] +msg_241.c(66): warning: dubious operation on enum, op -= [241] +msg_241.c(67): warning: dubious operation on enum, op <<= [241] +msg_241.c(68): warning: dubious operation on enum, op >>= [241] +msg_241.c(69): warning: dubious operation on enum, op &= [241] +msg_241.c(70): warning: dubious operation on enum, op ^= [241] +msg_241.c(71): warning: dubious operation on enum, op |= [241] +msg_241.c(74): warning: dubious operation on enum, op &= [241] +msg_241.c(82): warning: dubious operation on enum, op * [241] +msg_241.c(82): warning: combination of 'enum color' and 'int', op > [242] diff --git a/usr.bin/xlint/lint1/msg_242.c b/usr.bin/xlint/lint1/msg_242.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_242.c @@ -0,0 +1,34 @@ +/* $NetBSD: msg_242.c,v 1.4 2021/08/14 12:46:24 rillig Exp $ */ +# 3 "msg_242.c" + +// Test for message: combination of '%s' and '%s', op %s [242] + +/* lint1-extra-flags: -e */ + +enum E { + E1 +}; + +void sink_enum(enum E); +void sink_int(int); + +void +example(enum E e, int i) +{ + enum E e2 = e; + /* expect+1: warning: initialization of 'enum E' with 'int' [277] */ + enum E e3 = i; + /* expect+1: warning: initialization of 'int' with 'enum E' [277] */ + int i2 = e; + int i3 = i; + + /* expect+1: warning: combination of 'enum E' and 'int', op = [242] */ + e3 = i; + /* expect+1: warning: combination of 'int' and 'enum E', op = [242] */ + i2 = e; + + sink_enum(e2); + sink_enum(e3); + sink_int(i2); + sink_int(i3); +} diff --git a/usr.bin/xlint/lint1/msg_242.exp b/usr.bin/xlint/lint1/msg_242.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_242.exp @@ -0,0 +1,4 @@ +msg_242.c(20): warning: initialization of 'enum E' with 'int' [277] +msg_242.c(22): warning: initialization of 'int' with 'enum E' [277] +msg_242.c(26): warning: combination of 'enum E' and 'int', op = [242] +msg_242.c(28): warning: combination of 'int' and 'enum E', op = [242] diff --git a/usr.bin/xlint/lint1/msg_243.c b/usr.bin/xlint/lint1/msg_243.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_243.c @@ -0,0 +1,25 @@ +/* $NetBSD: msg_243.c,v 1.2 2021/01/31 09:21:24 rillig Exp $ */ +# 3 "msg_243.c" + +// Test for message: dubious comparison of enums, op %s [243] + +/* lint1-extra-flags: -eP */ + +enum color { + RED, GREEN, BLUE +}; + +void eval(_Bool); + +/* TODO: There should be a way to declare an enum type as "ordered ok". */ + +void +example(enum color a, enum color b) +{ + eval(a < b); /* expect: 243 */ + eval(a <= b); /* expect: 243 */ + eval(a > b); /* expect: 243 */ + eval(a >= b); /* expect: 243 */ + eval(a == b); + eval(a != b); +} diff --git a/usr.bin/xlint/lint1/msg_243.exp b/usr.bin/xlint/lint1/msg_243.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_243.exp @@ -0,0 +1,4 @@ +msg_243.c(19): warning: dubious comparison of enums, op < [243] +msg_243.c(20): warning: dubious comparison of enums, op <= [243] +msg_243.c(21): warning: dubious comparison of enums, op > [243] +msg_243.c(22): warning: dubious comparison of enums, op >= [243] diff --git a/usr.bin/xlint/lint1/msg_244.c b/usr.bin/xlint/lint1/msg_244.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_244.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_244.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_244.c" + +// Test for message: illegal structure pointer combination [244] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_244.exp b/usr.bin/xlint/lint1/msg_244.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_244.exp @@ -0,0 +1 @@ +msg_244.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_245.c b/usr.bin/xlint/lint1/msg_245.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_245.c @@ -0,0 +1,32 @@ +/* $NetBSD: msg_245.c,v 1.4 2021/02/28 02:00:06 rillig Exp $ */ +# 3 "msg_245.c" + +// Test for message: incompatible structure pointers: '%s' '%s' '%s' [245] + +typedef struct tag_and_typedef_tag { + int member; +} tag_and_typedef_typedef; + +struct only_tag { + int member; +}; + +typedef struct { + int member; +} only_typedef; + +struct { + int member; +} unnamed; + +void sink_bool(_Bool); + +void +example(tag_and_typedef_typedef both, + only_typedef only_typedef, + struct only_tag only_tag) +{ + sink_bool(&both == &only_tag); /* expect: 245 */ + sink_bool(&both == &only_typedef); /* expect: 245 */ + sink_bool(&both == &unnamed); /* expect: 245 */ +} diff --git a/usr.bin/xlint/lint1/msg_245.exp b/usr.bin/xlint/lint1/msg_245.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_245.exp @@ -0,0 +1,3 @@ +msg_245.c(29): warning: incompatible structure pointers: 'pointer to struct tag_and_typedef_tag' '==' 'pointer to struct only_tag' [245] +msg_245.c(30): warning: incompatible structure pointers: 'pointer to struct tag_and_typedef_tag' '==' 'pointer to struct typedef only_typedef' [245] +msg_245.c(31): warning: incompatible structure pointers: 'pointer to struct tag_and_typedef_tag' '==' 'pointer to struct ' [245] diff --git a/usr.bin/xlint/lint1/msg_246.c b/usr.bin/xlint/lint1/msg_246.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_246.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_246.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_246.c" + +// Test for message: dubious conversion of enum to '%s' [246] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_246.exp b/usr.bin/xlint/lint1/msg_246.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_246.exp @@ -0,0 +1 @@ +msg_246.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_247.c b/usr.bin/xlint/lint1/msg_247.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_247.c @@ -0,0 +1,188 @@ +/* $NetBSD: msg_247.c,v 1.16 2021/07/15 21:22:19 rillig Exp $ */ +# 3 "msg_247.c" + +// Test for message: pointer cast from '%s' to '%s' may be troublesome [247] + +/* lint1-extra-flags: -c */ + +/* example taken from Xlib.h */ +typedef struct { + int id; +} *PDisplay; + +struct Other { + int id; +}; + +void +example(struct Other *arg) +{ + PDisplay display; + + /* + * XXX: The target type is reported as 'struct '. In cases + * like these, it would be helpful to print at least the type name + * of the pointer. This type name though is discarded immediately + * in the grammar rule 'typespec: T_TYPENAME'. + * After that, the target type of the cast is just an unnamed struct, + * with no hint at all that there is a typedef for a pointer to the + * struct. + */ + display = (PDisplay)arg; /* expect: 247 */ +} + +/* + * C code with a long history that has existed in pre-C90 times already often + * uses 'pointer to char' where modern code would use 'pointer to void'. + * Since 'char' is the most general underlying type, there is nothing wrong + * with casting to it. An example for this type of code is X11. + * + * Casting to 'pointer to char' may also be used by programmers who don't know + * about endianness, but that's not something lint can do anything about. The + * code for these two use cases looks exactly the same, so lint errs on the + * side of fewer false positive warnings here. + */ +char * +cast_to_char_pointer(struct Other *arg) +{ + return (char *)arg; +} + +/* + * In traditional C there was 'unsigned char' as well, so the same reasoning + * as for plain 'char' applies here. + */ +unsigned char * +cast_to_unsigned_char_pointer(struct Other *arg) +{ + return (unsigned char *)arg; +} + +/* + * Traditional C does not have the type specifier 'signed', which means that + * this type cannot be used by old code. Therefore warn about this. All code + * that triggers this warning should do the intermediate cast via 'void + * pointer'. + */ +signed char * +cast_to_signed_char_pointer(struct Other *arg) +{ + return (signed char *)arg; /* expect: 247 */ +} + +char * +cast_to_void_pointer_then_to_char_pointer(struct Other *arg) +{ + return (char *)(void *)arg; +} + + +/* + * When implementing types that have a public part that is exposed to the user + * (in this case 'struct counter') and a private part that is only visible to + * the implementation (in this case 'struct counter_impl'), a common + * implementation technique is to use a struct in which the public part is the + * first member. C guarantees that the pointer to the first member is at the + * same address as the pointer to the whole struct. + * + * Seen in external/mpl/bind/dist/lib/isc/mem.c for 'struct isc_mem' and + * 'struct isc__mem'. + */ + +struct counter { + int count; +}; + +struct counter_impl { + struct counter public_part; + int saved_count; +}; + +void *allocate(void); + +struct counter * +counter_new(void) +{ + struct counter_impl *impl = allocate(); + impl->public_part.count = 12345; + impl->saved_count = 12346; + return &impl->public_part; +} + +void +counter_increment(struct counter *counter) +{ + /* + * Before tree.c 1.272 from 2021-04-08, lint warned about the cast + * from 'struct counter' to 'struct counter_impl'. + */ + struct counter_impl *impl = (struct counter_impl *)counter; + impl->saved_count = impl->public_part.count; + impl->public_part.count++; +} + + +/* + * In OpenSSL, the hashing API uses the incomplete 'struct lhash_st' for their + * type-generic hashing API while defining a separate struct for each type to + * be hashed. + * + * Before 2021-04-09, in a typical NetBSD build this led to about 38,000 lint + * warnings about possibly troublesome pointer casts. + */ + +struct lhash_st; /* expect: struct lhash_st never defined */ + +struct lhash_st *OPENSSL_LH_new(void); + +struct lhash_st_OPENSSL_STRING { + union lh_OPENSSL_STRING_dummy { + void *d1; + unsigned long d2; + int d3; + } dummy; +}; + +# 196 "lhash.h" 1 3 4 +struct lhash_st_OPENSSL_STRING * +lh_OPENSSL_STRING_new(void) +{ + /* + * Since tree.c 1.274 from 2021-04-09, lint does not warn about casts + * to or from incomplete structs anymore. + */ + return (struct lhash_st_OPENSSL_STRING *)OPENSSL_LH_new(); +} +# 157 "msg_247.c" 2 + +void sink(const void *); + +/* + * Before tree.c 1.316 from 2021-07-15, lint warned about pointer casts from + * unsigned char or plain char to another type. These casts often occur in + * traditional code that does not use void pointers, even 30 years after C90 + * introduced 'void'. + */ +void +unsigned_char_to_unsigned_type(unsigned char *ucp) +{ + unsigned short *usp; + + usp = (unsigned short *)ucp; + sink(usp); +} + +/* + * Before tree.c 1.316 from 2021-07-15, lint warned about pointer casts from + * unsigned char or plain char to another type. These casts often occur in + * traditional code that does not use void pointers, even 30 years after C90 + * introduced 'void'. + */ +void +plain_char_to_unsigned_type(char *cp) +{ + unsigned short *usp; + + usp = (unsigned short *)cp; + sink(usp); +} diff --git a/usr.bin/xlint/lint1/msg_247.exp b/usr.bin/xlint/lint1/msg_247.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_247.exp @@ -0,0 +1,3 @@ +msg_247.c(31): warning: pointer cast from 'pointer to struct Other' to 'pointer to struct ' may be troublesome [247] +msg_247.c(70): warning: pointer cast from 'pointer to struct Other' to 'pointer to signed char' may be troublesome [247] +msg_247.c(134): warning: struct lhash_st never defined [233] diff --git a/usr.bin/xlint/lint1/msg_248.c b/usr.bin/xlint/lint1/msg_248.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_248.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_248.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_248.c" + +// Test for message: floating-point constant out of range [248] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_248.exp b/usr.bin/xlint/lint1/msg_248.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_248.exp @@ -0,0 +1 @@ +msg_248.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_249.c b/usr.bin/xlint/lint1/msg_249.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_249.c @@ -0,0 +1,59 @@ +/* $NetBSD: msg_249.c,v 1.8 2021/07/10 17:35:54 rillig Exp $ */ +# 3 "msg_249.c" + +// Test for message: syntax error '%s' [249] + +/* + * Cover the grammar rule 'top_level_declaration: error T_SEMI'. + */ +/* expect+1: syntax error '"' [249] */ +"syntax error in top_level_declaration"; + +/* XXX: This is necessary to recover the yacc parser. */ +int recover_from_semi; + +/* + * Cover the grammar rule 'top_level_declaration: error T_RBRACE'. + */ +/* expect+1: syntax error '"' [249] */ +"syntax error in top_level_declaration"} + +/* XXX: This is necessary to recover the yacc parser. */ +int recover_from_rbrace; + +/* + * Before func.c 1.110 from 2021-06-19, lint ran into this: + * assertion "cstmt->c_kind == kind" failed in end_control_statement + */ +void +function(void) +{ + if (0) + ; + ); /* expect: syntax error ')' */ +} + +/* XXX: It is unexpected that this error is not detected. */ +"This syntax error is not detected."; + +/* XXX: This is necessary to recover the yacc parser. */ +double recover_from_rparen; + +/* Ensure that the declaration after the syntax error is processed. */ +double * +access_declaration_after_syntax_error(void) +{ + return &recover_from_rparen; +} + +struct cover_member_declaration { + /* cover 'noclass_declmods ... notype_member_decls' */ + const noclass_declmods; + + /* cover 'noclass_declspecs ...' */ + const int noclass_declspecs; + + /* cover 'add_type_qualifier_list end_type' */ + /* expect+1: error: syntax error 'member without type' [249] */ + const; +}; diff --git a/usr.bin/xlint/lint1/msg_249.exp b/usr.bin/xlint/lint1/msg_249.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_249.exp @@ -0,0 +1,4 @@ +msg_249.c(10): error: syntax error '"' [249] +msg_249.c(19): error: syntax error '"' [249] +msg_249.c(33): error: syntax error ')' [249] +msg_249.c(58): error: syntax error 'member without type' [249] diff --git a/usr.bin/xlint/lint1/msg_250.c b/usr.bin/xlint/lint1/msg_250.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_250.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_250.c,v 1.3 2021/06/29 07:17:43 rillig Exp $ */ +# 3 "msg_250.c" + +// Test for message: unknown character \%o [250] + +@deprecated /* expect: unknown character \100 [250] */ +char *gets(void); /* expect: syntax error 'char' [249] */ diff --git a/usr.bin/xlint/lint1/msg_250.exp b/usr.bin/xlint/lint1/msg_250.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_250.exp @@ -0,0 +1,2 @@ +msg_250.c(6): error: unknown character \100 [250] +msg_250.c(7): error: syntax error 'char' [249] diff --git a/usr.bin/xlint/lint1/msg_251.c b/usr.bin/xlint/lint1/msg_251.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_251.c @@ -0,0 +1,10 @@ +/* $NetBSD: msg_251.c,v 1.3 2021/06/29 07:17:43 rillig Exp $ */ +# 3 "msg_251.c" + +// Test for message: malformed integer constant [251] + +/* expect+1: malformed integer constant */ +int lll = 123LLL; + +/* expect+1: malformed integer constant */ +unsigned int uu = 123UU; diff --git a/usr.bin/xlint/lint1/msg_251.exp b/usr.bin/xlint/lint1/msg_251.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_251.exp @@ -0,0 +1,2 @@ +msg_251.c(7): warning: malformed integer constant [251] +msg_251.c(10): warning: malformed integer constant [251] diff --git a/usr.bin/xlint/lint1/msg_252.c b/usr.bin/xlint/lint1/msg_252.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_252.c @@ -0,0 +1,29 @@ +/* $NetBSD: msg_252.c,v 1.4 2021/08/28 18:40:15 rillig Exp $ */ +# 3 "msg_252.c" + +// Test for message: integer constant out of range [252] + +/* + * On ILP32 platforms, lint additionally and unnecessarily warns: + * + * conversion of 'unsigned long' to 'int' is out of range [119] + * + * On an ILP32 platform, lex_integer_constant interprets this number as + * having type ULONG, which is stored as 'ULONG 0x0000_0000_ffff_ffff'. + * This number is passed to convert_constant, which calls convert_integer, + * which sign-extends the number to 'INT 0xffff_ffff_ffff_ffff'. This + * converted number is passed to convert_constant_check_range, and at this + * point, v->v_quad != nv->v_quad, due to the sign extension. This triggers + * an additional warning 119. + * + * On a 64-bit platform, lex_integer_constant stores the number as + * 'ULONG 0xffff_ffff_ffff_ffff', which has the same representation as the + * 'INT 0xffff_ffff_ffff_ffff', therefore no warning. + * + * Due to this unnecessary difference, disable this test on ILP32 platforms + * for now (2021-08-28). + */ +/* lint1-skip-if: ilp32 */ + +/* expect+1: warning: integer constant out of range [252] */ +int constant = 1111111111111111111111111111111111111111111111111111; diff --git a/usr.bin/xlint/lint1/msg_252.exp b/usr.bin/xlint/lint1/msg_252.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_252.exp @@ -0,0 +1 @@ +msg_252.c(29): warning: integer constant out of range [252] diff --git a/usr.bin/xlint/lint1/msg_253.c b/usr.bin/xlint/lint1/msg_253.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_253.c @@ -0,0 +1,8 @@ +/* $NetBSD: msg_253.c,v 1.3 2021/06/29 07:17:43 rillig Exp $ */ +# 3 "msg_253.c" + +// Test for message: unterminated character constant [253] + +/* expect+3: [253] */ +/* expect+2: syntax error '' */ +' diff --git a/usr.bin/xlint/lint1/msg_253.exp b/usr.bin/xlint/lint1/msg_253.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_253.exp @@ -0,0 +1,2 @@ +msg_253.c(9): error: unterminated character constant [253] +msg_253.c(9): error: syntax error '' [249] diff --git a/usr.bin/xlint/lint1/msg_254.c b/usr.bin/xlint/lint1/msg_254.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_254.c @@ -0,0 +1,19 @@ +/* $NetBSD: msg_254.c,v 1.3 2021/06/29 07:17:43 rillig Exp $ */ +# 3 "msg_254.c" + +/* Test for message: newline in string or char constant [254] */ + +/* lint1-flags: -tw */ + +/* + * The sequence backslash-newline is a GCC extension. + * C99 does not allow it. + */ + +/* expect+6: newline in string or char constant [254] */ +/* expect+5: unterminated string constant [258] */ +/* expect+4: syntax error '"' [249] */ +/* expect+4: newline in string or char constant [254] */ +/* expect+3: unterminated string constant [258] */ +"line1 +line2" diff --git a/usr.bin/xlint/lint1/msg_254.exp b/usr.bin/xlint/lint1/msg_254.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_254.exp @@ -0,0 +1,5 @@ +msg_254.c(19): error: newline in string or char constant [254] +msg_254.c(19): error: unterminated string constant [258] +msg_254.c(19): error: syntax error '"' [249] +msg_254.c(20): error: newline in string or char constant [254] +msg_254.c(20): error: unterminated string constant [258] diff --git a/usr.bin/xlint/lint1/msg_255.c b/usr.bin/xlint/lint1/msg_255.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_255.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_255.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_255.c" + +// Test for message: undefined or invalid # directive [255] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_255.exp b/usr.bin/xlint/lint1/msg_255.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_255.exp @@ -0,0 +1 @@ +msg_255.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_256.c b/usr.bin/xlint/lint1/msg_256.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_256.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_256.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_256.c" + +// Test for message: unterminated comment [256] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_256.exp b/usr.bin/xlint/lint1/msg_256.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_256.exp @@ -0,0 +1 @@ +msg_256.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_257.c b/usr.bin/xlint/lint1/msg_257.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_257.c @@ -0,0 +1,28 @@ +/* $NetBSD: msg_257.c,v 1.3 2021/08/27 20:49:25 rillig Exp $ */ +# 3 "msg_257.c" + +// Test for message: extra characters in lint comment [257] + +void take(const void *); + +/* expect+1: warning: extra characters in lint comment [257] */ +/* FALLTHROUGH 3 */ + +/* expect+1: warning: extra characters in lint comment [257] */ +/* FALLTHROUGH extra */ + +/* PRINTFLIKE 7 */ +void +my_printf(const char *fmt) +/* expect+1: warning: argument number mismatch with directive: */ +{ + take(fmt); +} + +/* expect+1: warning: extra characters in lint comment [257] */ +/* SCANFLIKE extra */ +void +my_scanf(const char *fmt) +{ + take(fmt); +} diff --git a/usr.bin/xlint/lint1/msg_257.exp b/usr.bin/xlint/lint1/msg_257.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_257.exp @@ -0,0 +1,4 @@ +msg_257.c(9): warning: extra characters in lint comment [257] +msg_257.c(12): warning: extra characters in lint comment [257] +msg_257.c(18): warning: argument number mismatch with directive: /* PRINTFLIKE */ [283] +msg_257.c(23): warning: extra characters in lint comment [257] diff --git a/usr.bin/xlint/lint1/msg_258.c b/usr.bin/xlint/lint1/msg_258.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_258.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_258.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_258.c" + +// Test for message: unterminated string constant [258] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_258.exp b/usr.bin/xlint/lint1/msg_258.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_258.exp @@ -0,0 +1 @@ +msg_258.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_259.c b/usr.bin/xlint/lint1/msg_259.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_259.c @@ -0,0 +1,157 @@ +/* $NetBSD: msg_259.c,v 1.16 2021/08/31 19:26:23 rillig Exp $ */ +# 3 "msg_259.c" + +// Test for message: argument #%d is converted from '%s' to '%s' due to prototype [259] + +/* + * See also msg_297, but that requires the flags -a -p -P, which are not + * enabled in the default NetBSD build. + */ + +/* lint1-only-if: lp64 */ +/* lint1-extra-flags: -h */ + +void plain_char(char); +void signed_int(int); +void unsigned_int(unsigned int); +void signed_long(long); +void unsigned_long(unsigned long); +void signed_long_long(long long); +void unsigned_long_long(unsigned long long); + +void +change_in_type_width(char c, int i, long l) +{ + plain_char(c); + signed_int(c); + /* No warning 259 on LP64, only on ILP32 */ + signed_long(c); + + plain_char(i); /* XXX: why no warning? */ + signed_int(i); + /* No warning 259 on LP64, only on ILP32 */ + signed_long(i); + + plain_char(l); /* XXX: why no warning? */ + /* expect+1: from 'long' to 'int' due to prototype [259] */ + signed_int(l); + signed_long(l); +} + +/* + * Converting a signed integer type to its corresponding unsigned integer + * type (C99 6.2.5p6) is usually not a problem since the actual values of the + * expressions are usually not anywhere near the maximum signed value. From + * a technical standpoint, it is correct to warn here since even small + * negative numbers may result in very large positive numbers. + * + * A common case where it occurs is when the difference of two pointers is + * converted to size_t. The type ptrdiff_t is defined to be signed, but in + * many practical cases, the expression is '(end - start)', which makes the + * resulting value necessarily positive. + */ +void +signed_to_unsigned(int si, long sl, long long sll) +{ + /* expect+1: warning: argument #1 is converted from 'int' to 'unsigned int' due to prototype [259] */ + unsigned_int(si); + + /* expect+1: warning: argument #1 is converted from 'long' to 'unsigned int' due to prototype [259] */ + unsigned_int(sl); + + /* expect+1: warning: argument #1 is converted from 'long long' to 'unsigned int' due to prototype [259] */ + unsigned_int(sll); + + /* + * XXX: Why no warning? Even though 'unsigned long' is 64 bits + * wide, it cannot represent negative 32-bit values. + */ + unsigned_long(si); + + /* expect+1: warning: argument #1 is converted from 'long' to 'unsigned long' due to prototype [259] */ + unsigned_long(sl); + /* expect+1: warning: argument #1 is converted from 'long long' to 'unsigned long' due to prototype [259] */ + unsigned_long(sll); + + /* + * XXX: Why no warning? Even though 'unsigned long long' is 64 bits + * wide, it cannot represent negative 32-bit values. + */ + unsigned_long_long(si); + + /* expect+1: warning: argument #1 is converted from 'long' to 'unsigned long long' due to prototype [259] */ + unsigned_long_long(sl); + + /* expect+1: warning: argument #1 is converted from 'long long' to 'unsigned long long' due to prototype [259] */ + unsigned_long_long(sll); +} + +void +unsigned_to_signed(unsigned int ui, unsigned long ul, unsigned long long ull) +{ + /* expect+1: warning: argument #1 is converted from 'unsigned int' to 'int' due to prototype [259] */ + signed_int(ui); + /* expect+1: warning: argument #1 is converted from 'unsigned long' to 'int' due to prototype [259] */ + signed_int(ul); + /* expect+1: warning: argument #1 is converted from 'unsigned long long' to 'int' due to prototype [259] */ + signed_int(ull); + signed_long(ui); + /* expect+1: warning: argument #1 is converted from 'unsigned long' to 'long' due to prototype [259] */ + signed_long(ul); + /* expect+1: warning: argument #1 is converted from 'unsigned long long' to 'long' due to prototype [259] */ + signed_long(ull); + signed_long_long(ui); + /* expect+1: warning: argument #1 is converted from 'unsigned long' to 'long long' due to prototype [259] */ + signed_long_long(ul); + /* expect+1: warning: argument #1 is converted from 'unsigned long long' to 'long long' due to prototype [259] */ + signed_long_long(ull); +} + +void +signed_to_signed(signed int si, signed long sl, signed long long sll) +{ + signed_int(si); + /* expect+1: warning: argument #1 is converted from 'long' to 'int' due to prototype [259] */ + signed_int(sl); + /* expect+1: warning: argument #1 is converted from 'long long' to 'int' due to prototype [259] */ + signed_int(sll); + signed_long(si); + signed_long(sl); + /* expect+1: warning: argument #1 is converted from 'long long' to 'long' due to prototype [259] */ + signed_long(sll); + signed_long_long(si); + /* expect+1: warning: argument #1 is converted from 'long' to 'long long' due to prototype [259] */ + signed_long_long(sl); + signed_long_long(sll); +} + +void +unsigned_to_unsigned(unsigned int ui, unsigned long ul, unsigned long long ull) +{ + unsigned_int(ui); + /* expect+1: warning: argument #1 is converted from 'unsigned long' to 'unsigned int' due to prototype [259] */ + unsigned_int(ul); + /* expect+1: warning: argument #1 is converted from 'unsigned long long' to 'unsigned int' due to prototype [259] */ + unsigned_int(ull); + unsigned_long(ui); + unsigned_long(ul); + /* expect+1: warning: argument #1 is converted from 'unsigned long long' to 'unsigned long' due to prototype [259] */ + unsigned_long(ull); + unsigned_long_long(ui); + /* expect+1: warning: argument #1 is converted from 'unsigned long' to 'unsigned long long' due to prototype [259] */ + unsigned_long_long(ul); + unsigned_long_long(ull); +} + +void +pass_sizeof_as_smaller_type(void) +{ + /* + * XXX: Even though the expression has type size_t, it has a constant + * value that fits effortless into an 'unsigned int', it's so small + * that it would even fit into a 3-bit bit-field, so lint should not + * warn here. + */ + /* expect+1: warning: argument #1 is converted from 'unsigned long' to 'unsigned int' due to prototype [259] */ + unsigned_int(sizeof(int)); +} diff --git a/usr.bin/xlint/lint1/msg_259.exp b/usr.bin/xlint/lint1/msg_259.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_259.exp @@ -0,0 +1,24 @@ +msg_259.c(37): warning: argument #1 is converted from 'long' to 'int' due to prototype [259] +msg_259.c(57): warning: argument #1 is converted from 'int' to 'unsigned int' due to prototype [259] +msg_259.c(60): warning: argument #1 is converted from 'long' to 'unsigned int' due to prototype [259] +msg_259.c(63): warning: argument #1 is converted from 'long long' to 'unsigned int' due to prototype [259] +msg_259.c(72): warning: argument #1 is converted from 'long' to 'unsigned long' due to prototype [259] +msg_259.c(74): warning: argument #1 is converted from 'long long' to 'unsigned long' due to prototype [259] +msg_259.c(83): warning: argument #1 is converted from 'long' to 'unsigned long long' due to prototype [259] +msg_259.c(86): warning: argument #1 is converted from 'long long' to 'unsigned long long' due to prototype [259] +msg_259.c(93): warning: argument #1 is converted from 'unsigned int' to 'int' due to prototype [259] +msg_259.c(95): warning: argument #1 is converted from 'unsigned long' to 'int' due to prototype [259] +msg_259.c(97): warning: argument #1 is converted from 'unsigned long long' to 'int' due to prototype [259] +msg_259.c(100): warning: argument #1 is converted from 'unsigned long' to 'long' due to prototype [259] +msg_259.c(102): warning: argument #1 is converted from 'unsigned long long' to 'long' due to prototype [259] +msg_259.c(105): warning: argument #1 is converted from 'unsigned long' to 'long long' due to prototype [259] +msg_259.c(107): warning: argument #1 is converted from 'unsigned long long' to 'long long' due to prototype [259] +msg_259.c(115): warning: argument #1 is converted from 'long' to 'int' due to prototype [259] +msg_259.c(117): warning: argument #1 is converted from 'long long' to 'int' due to prototype [259] +msg_259.c(121): warning: argument #1 is converted from 'long long' to 'long' due to prototype [259] +msg_259.c(124): warning: argument #1 is converted from 'long' to 'long long' due to prototype [259] +msg_259.c(133): warning: argument #1 is converted from 'unsigned long' to 'unsigned int' due to prototype [259] +msg_259.c(135): warning: argument #1 is converted from 'unsigned long long' to 'unsigned int' due to prototype [259] +msg_259.c(139): warning: argument #1 is converted from 'unsigned long long' to 'unsigned long' due to prototype [259] +msg_259.c(142): warning: argument #1 is converted from 'unsigned long' to 'unsigned long long' due to prototype [259] +msg_259.c(156): warning: argument #1 is converted from 'unsigned long' to 'unsigned int' due to prototype [259] diff --git a/usr.bin/xlint/lint1/msg_259_c90.c b/usr.bin/xlint/lint1/msg_259_c90.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_259_c90.c @@ -0,0 +1,115 @@ +/* $NetBSD: msg_259_c90.c,v 1.2 2021/08/31 19:26:23 rillig Exp $ */ +# 3 "msg_259_c90.c" + +/* Test for message: argument #%d is converted from '%s' to '%s' due to prototype [259] */ + +/* + * See also msg_297, but that requires the flags -a -p -P, which are not + * enabled in the default NetBSD build. + */ + +/* lint1-only-if: lp64 */ +/* XXX: The flag '-s' suppresses all warnings. Why? */ +/* lint1-flags: -h -w */ + +void plain_char(char); +void signed_int(int); +void unsigned_int(unsigned int); +void signed_long(long); +void unsigned_long(unsigned long); +/* No 'long long' since it requires C99. */ + +void +change_in_type_width(char c, int i, long l) +{ + plain_char(c); + signed_int(c); + /* No warning 259 on LP64, only on ILP32 */ + signed_long(c); + + plain_char(i); /* XXX: why no warning? */ + signed_int(i); + /* No warning 259 on LP64, only on ILP32 */ + signed_long(i); + + plain_char(l); /* XXX: why no warning? */ + /* expect+1: from 'long' to 'int' due to prototype [259] */ + signed_int(l); + signed_long(l); +} + +/* + * Converting a signed integer type to its corresponding unsigned integer + * type (C99 6.2.5p6) is usually not a problem since the actual values of the + * expressions are usually not anywhere near the maximum signed value. From + * a technical standpoint, it is correct to warn here since even small + * negative numbers may result in very large positive numbers. + * + * A common case where it occurs is when the difference of two pointers is + * converted to size_t. The type ptrdiff_t is defined to be signed, but in + * many practical cases, the expression is '(end - start)', which makes the + * resulting value necessarily positive. + */ +void +signed_to_unsigned(int si, long sl) +{ + /* expect+1: warning: argument #1 is converted from 'int' to 'unsigned int' due to prototype [259] */ + unsigned_int(si); + + /* expect+1: warning: argument #1 is converted from 'long' to 'unsigned int' due to prototype [259] */ + unsigned_int(sl); + + /* + * XXX: Why no warning? Even though 'unsigned long' is 64 bits + * wide, it cannot represent negative 32-bit values. + */ + unsigned_long(si); + + /* expect+1: warning: argument #1 is converted from 'long' to 'unsigned long' due to prototype [259] */ + unsigned_long(sl); +} + +void +unsigned_to_signed(unsigned int ui, unsigned long ul) +{ + /* expect+1: warning: argument #1 is converted from 'unsigned int' to 'int' due to prototype [259] */ + signed_int(ui); + /* expect+1: warning: argument #1 is converted from 'unsigned long' to 'int' due to prototype [259] */ + signed_int(ul); + signed_long(ui); + /* expect+1: warning: argument #1 is converted from 'unsigned long' to 'long' due to prototype [259] */ + signed_long(ul); +} + +void +signed_to_signed(signed int si, signed long sl) +{ + signed_int(si); + /* expect+1: warning: argument #1 is converted from 'long' to 'int' due to prototype [259] */ + signed_int(sl); + signed_long(si); + signed_long(sl); +} + +void +unsigned_to_unsigned(unsigned int ui, unsigned long ul) +{ + unsigned_int(ui); + /* expect+1: warning: argument #1 is converted from 'unsigned long' to 'unsigned int' due to prototype [259] */ + unsigned_int(ul); + unsigned_long(ui); + unsigned_long(ul); +} + +void +pass_sizeof_as_smaller_type(void) +{ + /* + * XXX: Even though the expression has type size_t, it has a constant + * value that fits effortless into an 'unsigned int', it's so small + * that it would even fit into a 3-bit bit-field, so lint should not + * warn here. + */ + /* expect+1: warning: argument #1 is converted from 'unsigned long' to 'unsigned int' due to prototype [259] */ + unsigned_int(sizeof(int)); +} diff --git a/usr.bin/xlint/lint1/msg_259_c90.exp b/usr.bin/xlint/lint1/msg_259_c90.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_259_c90.exp @@ -0,0 +1,10 @@ +msg_259_c90.c(37): warning: argument #1 is converted from 'long' to 'int' due to prototype [259] +msg_259_c90.c(57): warning: argument #1 is converted from 'int' to 'unsigned int' due to prototype [259] +msg_259_c90.c(60): warning: argument #1 is converted from 'long' to 'unsigned int' due to prototype [259] +msg_259_c90.c(69): warning: argument #1 is converted from 'long' to 'unsigned long' due to prototype [259] +msg_259_c90.c(76): warning: argument #1 is converted from 'unsigned int' to 'int' due to prototype [259] +msg_259_c90.c(78): warning: argument #1 is converted from 'unsigned long' to 'int' due to prototype [259] +msg_259_c90.c(81): warning: argument #1 is converted from 'unsigned long' to 'long' due to prototype [259] +msg_259_c90.c(89): warning: argument #1 is converted from 'long' to 'int' due to prototype [259] +msg_259_c90.c(99): warning: argument #1 is converted from 'unsigned long' to 'unsigned int' due to prototype [259] +msg_259_c90.c(114): warning: argument #1 is converted from 'unsigned long' to 'unsigned int' due to prototype [259] diff --git a/usr.bin/xlint/lint1/msg_259_ilp32.c b/usr.bin/xlint/lint1/msg_259_ilp32.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_259_ilp32.c @@ -0,0 +1,35 @@ +/* $NetBSD: msg_259_ilp32.c,v 1.4 2021/08/31 19:26:23 rillig Exp $ */ +# 3 "msg_259_ilp32.c" + +// Test for message: argument #%d is converted from '%s' to '%s' due to prototype [259] + +/* + * See also msg_297, but that requires the flags -a -p -P, which are not + * enabled in the default NetBSD build. + */ + +/* lint1-only-if: ilp32 */ +/* lint1-extra-flags: -h */ + +void farg_char(char); +void farg_int(int); +void farg_long(long); + +void +example(char c, int i, long l) +{ + farg_char(c); + farg_int(c); + /* expect+1: from 'char' to 'long' due to prototype [259] */ + farg_long(c); + + farg_char(i); /* XXX: why no warning? */ + farg_int(i); + /* expect+1: from 'int' to 'long' due to prototype [259] */ + farg_long(i); + + farg_char(l); /* XXX: why no warning? */ + /* expect+1: from 'long' to 'int' due to prototype [259] */ + farg_int(l); + farg_long(l); +} diff --git a/usr.bin/xlint/lint1/msg_259_ilp32.exp b/usr.bin/xlint/lint1/msg_259_ilp32.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_259_ilp32.exp @@ -0,0 +1,3 @@ +msg_259_ilp32.c(24): warning: argument #1 is converted from 'char' to 'long' due to prototype [259] +msg_259_ilp32.c(29): warning: argument #1 is converted from 'int' to 'long' due to prototype [259] +msg_259_ilp32.c(33): warning: argument #1 is converted from 'long' to 'int' due to prototype [259] diff --git a/usr.bin/xlint/lint1/msg_260.c b/usr.bin/xlint/lint1/msg_260.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_260.c @@ -0,0 +1,33 @@ +/* $NetBSD: msg_260.c,v 1.4 2021/08/16 06:49:57 rillig Exp $ */ +# 3 "msg_260.c" + +// Test for message: previous declaration of %s [260] + +/* lint1-extra-flags: -r */ + +# 100 "header.h" 1 +/* expect+1: previous declaration of s [260] */ +struct s { + int member; +}; +# 14 "msg_260.c" 2 + +# 200 "header.h" 1 +/* expect+2: error: struct tag 's' redeclared as union [46] */ +/* expect+1: previous declaration of s [260] */ +union s { + int member; +}; +/* + * FIXME: the stack trace for the message 260 is wrong, as the previous + * declaration is included from logical line msg_260.c:8, not from + * msg_260.c:15. + */ +# 27 "msg_260.c" 2 +/* expect+1: error: union tag 's' redeclared as union [46] */ +union s { + int member; +}; +/* + * FIXME: the stack trace for the 260 is missing. + */ diff --git a/usr.bin/xlint/lint1/msg_260.exp b/usr.bin/xlint/lint1/msg_260.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_260.exp @@ -0,0 +1,6 @@ +header.h(202): error: struct tag 's' redeclared as union [46] + included from msg_260.c(15) +header.h(101): previous declaration of s [260] + included from msg_260.c(15) +msg_260.c(28): error: union tag 's' redeclared as union [46] +header.h(202): previous declaration of s [260] diff --git a/usr.bin/xlint/lint1/msg_261.c b/usr.bin/xlint/lint1/msg_261.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_261.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_261.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_261.c" + +// Test for message: previous definition of %s [261] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_261.exp b/usr.bin/xlint/lint1/msg_261.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_261.exp @@ -0,0 +1 @@ +msg_261.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_262.c b/usr.bin/xlint/lint1/msg_262.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_262.c @@ -0,0 +1,8 @@ +/* $NetBSD: msg_262.c,v 1.3 2021/06/29 07:17:43 rillig Exp $ */ +# 3 "msg_262.c" + +/* Test for message: \" inside character constants undefined in traditional C [262] */ + +/* lint1-flags: -tw */ + +char msg = '\"'; /* expect: [262] */ diff --git a/usr.bin/xlint/lint1/msg_262.exp b/usr.bin/xlint/lint1/msg_262.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_262.exp @@ -0,0 +1 @@ +msg_262.c(8): warning: \" inside character constants undefined in traditional C [262] diff --git a/usr.bin/xlint/lint1/msg_263.c b/usr.bin/xlint/lint1/msg_263.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_263.c @@ -0,0 +1,8 @@ +/* $NetBSD: msg_263.c,v 1.3 2021/06/29 07:17:43 rillig Exp $ */ +# 3 "msg_263.c" + +/* Test for message: \? undefined in traditional C [263] */ + +/* lint1-flags: -tw */ + +char ch = '\?'; /* expect: [263] */ diff --git a/usr.bin/xlint/lint1/msg_263.exp b/usr.bin/xlint/lint1/msg_263.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_263.exp @@ -0,0 +1 @@ +msg_263.c(8): warning: \? undefined in traditional C [263] diff --git a/usr.bin/xlint/lint1/msg_264.c b/usr.bin/xlint/lint1/msg_264.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_264.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_264.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_264.c" + +// Test for message: \v undefined in traditional C [264] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_264.exp b/usr.bin/xlint/lint1/msg_264.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_264.exp @@ -0,0 +1 @@ +msg_264.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_265.c b/usr.bin/xlint/lint1/msg_265.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_265.c @@ -0,0 +1,13 @@ +/* $NetBSD: msg_265.c,v 1.3 2021/01/31 11:12:07 rillig Exp $ */ +# 3 "msg_265.c" + +/* Test for message: %s C does not support 'long long' [265] */ + +/* lint1-flags: -w */ + +long long unsupported_variable; /* expect: 265 */ + +/*LONGLONG*/ +long long suppressed_variable; + +long long another_unsupported_variable; /* expect: 265 */ diff --git a/usr.bin/xlint/lint1/msg_265.exp b/usr.bin/xlint/lint1/msg_265.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_265.exp @@ -0,0 +1,2 @@ +msg_265.c(8): warning: c89 C does not support 'long long' [265] +msg_265.c(13): warning: c89 C does not support 'long long' [265] diff --git a/usr.bin/xlint/lint1/msg_266.c b/usr.bin/xlint/lint1/msg_266.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_266.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_266.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_266.c" + +// Test for message: 'long double' is illegal in traditional C [266] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_266.exp b/usr.bin/xlint/lint1/msg_266.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_266.exp @@ -0,0 +1 @@ +msg_266.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_267.c b/usr.bin/xlint/lint1/msg_267.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_267.c @@ -0,0 +1,16 @@ +/* $NetBSD: msg_267.c,v 1.3 2021/04/09 16:37:18 rillig Exp $ */ +# 3 "msg_267.c" + +// Test for message: shift equal to size of object [267] + +int +shr32(unsigned int x) +{ + return x >> 32; /* expect: 267 */ +} + +int +shl32(unsigned int x) +{ + return x << 32; /* expect: 267 */ +} diff --git a/usr.bin/xlint/lint1/msg_267.exp b/usr.bin/xlint/lint1/msg_267.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_267.exp @@ -0,0 +1,2 @@ +msg_267.c(9): warning: shift equal to size of object [267] +msg_267.c(15): warning: shift equal to size of object [267] diff --git a/usr.bin/xlint/lint1/msg_268.c b/usr.bin/xlint/lint1/msg_268.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_268.c @@ -0,0 +1,13 @@ +/* $NetBSD: msg_268.c,v 1.3 2021/08/22 13:45:56 rillig Exp $ */ +# 3 "msg_268.c" + +// Test for message: variable declared inline: %s [268] + +int +example(int arg) +{ + /* expect+1: warning: variable declared inline: local [268] */ + inline int local = arg; + + return local; +} diff --git a/usr.bin/xlint/lint1/msg_268.exp b/usr.bin/xlint/lint1/msg_268.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_268.exp @@ -0,0 +1 @@ +msg_268.c(10): warning: variable declared inline: local [268] diff --git a/usr.bin/xlint/lint1/msg_269.c b/usr.bin/xlint/lint1/msg_269.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_269.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_269.c,v 1.3 2021/08/22 13:45:56 rillig Exp $ */ +# 3 "msg_269.c" + +// Test for message: argument declared inline: %s [269] + +/* expect+1: warning: argument declared inline: x [269] */ +void example(inline int x); diff --git a/usr.bin/xlint/lint1/msg_269.exp b/usr.bin/xlint/lint1/msg_269.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_269.exp @@ -0,0 +1 @@ +msg_269.c(7): warning: argument declared inline: x [269] diff --git a/usr.bin/xlint/lint1/msg_270.c b/usr.bin/xlint/lint1/msg_270.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_270.c @@ -0,0 +1,13 @@ +/* $NetBSD: msg_270.c,v 1.3 2021/08/22 13:45:56 rillig Exp $ */ +# 3 "msg_270.c" + +/* Test for message: function prototypes are illegal in traditional C [270] */ + +/* lint1-flags: -tw */ + +/* expect+1: warning: function prototypes are illegal in traditional C [270] */ +void prototype(void); + +char *traditional(); + +traditional_implicit_int(); diff --git a/usr.bin/xlint/lint1/msg_270.exp b/usr.bin/xlint/lint1/msg_270.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_270.exp @@ -0,0 +1 @@ +msg_270.c(9): warning: function prototypes are illegal in traditional C [270] diff --git a/usr.bin/xlint/lint1/msg_271.c b/usr.bin/xlint/lint1/msg_271.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_271.c @@ -0,0 +1,27 @@ +/* $NetBSD: msg_271.c,v 1.4 2021/08/22 13:52:19 rillig Exp $ */ +# 3 "msg_271.c" + +/* Test for message: switch expression must be of type 'int' in traditional C [271] */ + +/* lint1-flags: -tw */ + +example(long_int, unsigned_int) + long long_int; + unsigned unsigned_int; +{ + /* expect+1: warning: switch expression must be of type 'int' in traditional C [271] */ + switch (long_int) { + case 3: + return 1; + } + + /* + * XXX: K&R clearly says "the result must be 'int'", but lint also + * allows unsigned int. + */ + switch (unsigned_int) { + case 3: + return 1; + } + return 2; +} diff --git a/usr.bin/xlint/lint1/msg_271.exp b/usr.bin/xlint/lint1/msg_271.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_271.exp @@ -0,0 +1 @@ +msg_271.c(13): warning: switch expression must be of type 'int' in traditional C [271] diff --git a/usr.bin/xlint/lint1/msg_272.c b/usr.bin/xlint/lint1/msg_272.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_272.c @@ -0,0 +1,6 @@ +/* $NetBSD: msg_272.c,v 1.3 2021/08/22 13:45:56 rillig Exp $ */ +# 3 "msg_272.c" + +// Test for message: empty translation unit [272] + +/* expect+1: warning: empty translation unit [272] */ diff --git a/usr.bin/xlint/lint1/msg_272.exp b/usr.bin/xlint/lint1/msg_272.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_272.exp @@ -0,0 +1 @@ +msg_272.c(7): warning: empty translation unit [272] diff --git a/usr.bin/xlint/lint1/msg_272_c90.c b/usr.bin/xlint/lint1/msg_272_c90.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_272_c90.c @@ -0,0 +1,13 @@ +/* $NetBSD: msg_272_c90.c,v 1.1 2021/07/08 05:18:49 rillig Exp $ */ +# 3 "msg_272_c90.c" + +/* + * Test for message: empty translation unit [272] + * + * In strict C90 mode, an empty translation unit is an error, not merely a + * warning. + */ + +/* lint1-flags: -s */ + +/* expect+1: error: empty translation unit [272] */ diff --git a/usr.bin/xlint/lint1/msg_272_c90.exp b/usr.bin/xlint/lint1/msg_272_c90.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_272_c90.exp @@ -0,0 +1 @@ +msg_272_c90.c(14): error: empty translation unit [272] diff --git a/usr.bin/xlint/lint1/msg_273.c b/usr.bin/xlint/lint1/msg_273.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_273.c @@ -0,0 +1,14 @@ +/* $NetBSD: msg_273.c,v 1.3 2021/08/22 13:45:56 rillig Exp $ */ +# 3 "msg_273.c" + +/* Test for message: bit-field type '%s' invalid in ANSI C [273] */ + +/* lint1-flags: -sw */ + +struct bit_fields { + int plain_int: 3; + unsigned int unsigned_int: 3; + signed int signed_int: 3; + /* expect+1: warning: bit-field type 'unsigned char' invalid in ANSI C [273] */ + unsigned char unsigned_char: 3; +}; diff --git a/usr.bin/xlint/lint1/msg_273.exp b/usr.bin/xlint/lint1/msg_273.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_273.exp @@ -0,0 +1 @@ +msg_273.c(13): warning: bit-field type 'unsigned char' invalid in ANSI C [273] diff --git a/usr.bin/xlint/lint1/msg_274.c b/usr.bin/xlint/lint1/msg_274.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_274.c @@ -0,0 +1,23 @@ +/* $NetBSD: msg_274.c,v 1.3 2021/08/22 13:45:56 rillig Exp $ */ +# 3 "msg_274.c" + +/* Test for message: ANSI C forbids comparison of %s with %s [274] */ + +/* lint1-flags: -sw */ + +void +example(void (*function_pointer)(void), void *void_pointer) +{ + + /* Comparing a function pointer with a null pointer is OK. */ + if (function_pointer == (void *)0) + return; + + /* Comparing a function pointer with a null pointer is OK. */ + if (function_pointer == (const void *)0) + return; + + /* expect+1: warning: ANSI C forbids comparison of function pointer with 'void *' [274] */ + if (function_pointer == void_pointer) + return; +} diff --git a/usr.bin/xlint/lint1/msg_274.exp b/usr.bin/xlint/lint1/msg_274.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_274.exp @@ -0,0 +1 @@ +msg_274.c(21): warning: ANSI C forbids comparison of function pointer with 'void *' [274] diff --git a/usr.bin/xlint/lint1/msg_275.c b/usr.bin/xlint/lint1/msg_275.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_275.c @@ -0,0 +1,18 @@ +/* $NetBSD: msg_275.c,v 1.4 2021/02/28 12:40:00 rillig Exp $ */ +# 3 "msg_275.c" + +// Test for message: cast discards 'const' from type '%s' [275] + +/* lint1-extra-flags: -h */ + +char * +unconst_string(const char *s) +{ + return (char *)s; /* expect: 275 */ +} + +const char * +const_string(char *s) +{ + return (const char *)s; +} diff --git a/usr.bin/xlint/lint1/msg_275.exp b/usr.bin/xlint/lint1/msg_275.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_275.exp @@ -0,0 +1 @@ +msg_275.c(11): warning: cast discards 'const' from type 'pointer to const char' [275] diff --git a/usr.bin/xlint/lint1/msg_276.c b/usr.bin/xlint/lint1/msg_276.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_276.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_276.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_276.c" + +// Test for message: __%s__ is illegal for type %s [276] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_276.exp b/usr.bin/xlint/lint1/msg_276.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_276.exp @@ -0,0 +1 @@ +msg_276.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_277.c b/usr.bin/xlint/lint1/msg_277.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_277.c @@ -0,0 +1,31 @@ +/* $NetBSD: msg_277.c,v 1.5 2021/08/16 20:11:03 rillig Exp $ */ +# 3 "msg_277.c" + +// Test for message: initialization of '%s' with '%s' [277] + +/* lint1-extra-flags: -e */ + +enum E { + E1 +}; + +void sink_enum(enum E); +void sink_int(int); + +void +example(enum E e, int i) +{ + enum E e2 = e; + enum E e3 = { i }; /* expect: 277 */ + int i2 = { e }; /* expect: 277 */ + int i3 = i; + + sink_enum(e2); + sink_enum(e3); + sink_int(i2); + sink_int(i3); + + enum E init_0 = 0; + /* expect+1: warning: initialization of 'enum E' with 'int' [277] */ + enum E init_1 = 1; +} diff --git a/usr.bin/xlint/lint1/msg_277.exp b/usr.bin/xlint/lint1/msg_277.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_277.exp @@ -0,0 +1,3 @@ +msg_277.c(19): warning: initialization of 'enum E' with 'int' [277] +msg_277.c(20): warning: initialization of 'int' with 'enum E' [277] +msg_277.c(30): warning: initialization of 'enum E' with 'int' [277] diff --git a/usr.bin/xlint/lint1/msg_278.c b/usr.bin/xlint/lint1/msg_278.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_278.c @@ -0,0 +1,23 @@ +/* $NetBSD: msg_278.c,v 1.3 2021/02/27 18:01:29 rillig Exp $ */ +# 3 "msg_278.c" + +// Test for message: combination of '%s' and '%s', arg #%d [278] + +/* lint1-extra-flags: -e */ + +enum E { + E1 +}; + +void sink_enum(enum E); +void sink_int(int); + +void +example(enum E e, int i) +{ + sink_enum(e); + sink_enum(i); /* expect: 278 */ + + sink_int(e); /* expect: 278 */ + sink_int(i); +} diff --git a/usr.bin/xlint/lint1/msg_278.exp b/usr.bin/xlint/lint1/msg_278.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_278.exp @@ -0,0 +1,2 @@ +msg_278.c(19): warning: combination of 'enum E' and 'int', arg #1 [278] +msg_278.c(21): warning: combination of 'int' and 'enum E', arg #1 [278] diff --git a/usr.bin/xlint/lint1/msg_279.c b/usr.bin/xlint/lint1/msg_279.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_279.c @@ -0,0 +1,25 @@ +/* $NetBSD: msg_279.c,v 1.3 2021/02/27 18:01:29 rillig Exp $ */ +# 3 "msg_279.c" + +// Test for message: combination of '%s' and '%s' in return [279] + +/* lint1-extra-flags: -e */ + +enum E { + E1 +}; + +void sink_enum(enum E); +void sink_int(int); + +enum E +returning_enum(int i) +{ + return i; /* expect: 279 */ +} + +int +returning_int(enum E e) +{ + return e; /* expect: 279 */ +} diff --git a/usr.bin/xlint/lint1/msg_279.exp b/usr.bin/xlint/lint1/msg_279.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_279.exp @@ -0,0 +1,2 @@ +msg_279.c(18): warning: combination of 'enum E' and 'int' in return [279] +msg_279.c(24): warning: combination of 'int' and 'enum E' in return [279] diff --git a/usr.bin/xlint/lint1/msg_280.c b/usr.bin/xlint/lint1/msg_280.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_280.c @@ -0,0 +1,60 @@ +/* $NetBSD: msg_280.c,v 1.3 2021/08/30 19:48:21 rillig Exp $ */ +# 3 "msg_280.c" + +// Test for message: must be outside function: /* %s */ [280] + +/* VARARGS */ +void +varargs_ok(const char *str, ...) +{ + (void)str; +} + +void +/* XXX: Why is this comment considered 'outside' enough? */ +varargs_bad_param(/* VARARGS */ const char *str, ...) +{ + /* expect+1: warning: must be outside function: */ + /* VARARGS */ + (void)str; +} + +void +/* expect+1: warning: must be outside function: */ +varargs_bad_ellipsis(const char *str, /* VARARGS */ ...) +{ + (void)str; +} + +void +/* XXX: Why is this comment considered 'outside' enough? */ +varargs_bad_body(const char *str, ...) +{ + /* expect+1: warning: must be outside function */ + /* VARARGS */ + (void)str; +} + +void +/* expect+1: warning: argument 'str' unused in function 'argsused_bad_body' [231] */ +argsused_bad_body(const char *str) +{ + /* expect+1: warning: must be outside function */ + /* ARGSUSED */ +} + +void +printflike_bad_body(const char *fmt, ...) +{ + /* expect+1: warning: must be outside function */ + /* PRINTFLIKE */ + (void)fmt; +} + +void +scanflike_bad_body(const char *fmt, ...) +{ + /* expect+1: warning: must be outside function */ + /* SCANFLIKE */ + (void)fmt; +} diff --git a/usr.bin/xlint/lint1/msg_280.exp b/usr.bin/xlint/lint1/msg_280.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_280.exp @@ -0,0 +1,7 @@ +msg_280.c(18): warning: must be outside function: /* VARARGS */ [280] +msg_280.c(24): warning: must be outside function: /* VARARGS */ [280] +msg_280.c(34): warning: must be outside function: /* VARARGS */ [280] +msg_280.c(43): warning: must be outside function: /* ARGSUSED */ [280] +msg_280.c(40): warning: argument 'str' unused in function 'argsused_bad_body' [231] +msg_280.c(50): warning: must be outside function: /* PRINTFLIKE */ [280] +msg_280.c(58): warning: must be outside function: /* SCANFLIKE */ [280] diff --git a/usr.bin/xlint/lint1/msg_281.c b/usr.bin/xlint/lint1/msg_281.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_281.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_281.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_281.c" + +// Test for message: duplicate use of /* %s */ [281] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_281.exp b/usr.bin/xlint/lint1/msg_281.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_281.exp @@ -0,0 +1 @@ +msg_281.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_282.c b/usr.bin/xlint/lint1/msg_282.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_282.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_282.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_282.c" + +// Test for message: must precede function definition: /* %s */ [282] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_282.exp b/usr.bin/xlint/lint1/msg_282.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_282.exp @@ -0,0 +1 @@ +msg_282.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_283.c b/usr.bin/xlint/lint1/msg_283.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_283.c @@ -0,0 +1,32 @@ +/* $NetBSD: msg_283.c,v 1.3 2021/08/30 19:48:21 rillig Exp $ */ +# 3 "msg_283.c" + +// Test for message: argument number mismatch with directive: /* %s */ [283] + +/* Do not warn about unused parameters. */ +/* lint1-extra-flags: -X 231 */ + +/* PRINTFLIKE */ +void +printflike_comment(const char *fmt) +{ +} + +/* PRINTFLIKE 0 */ +void +printflike_0_comment(const char *fmt) +{ +} + +/* PRINTFLIKE 2 */ +void +printflike_2_comment(int a, const char *fmt) +{ +} + +/* PRINTFLIKE 3 */ +void +printflike_3_comment(int a, const char *fmt) +/* expect+1: warning: argument number mismatch with directive */ +{ +} diff --git a/usr.bin/xlint/lint1/msg_283.exp b/usr.bin/xlint/lint1/msg_283.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_283.exp @@ -0,0 +1 @@ +msg_283.c(31): warning: argument number mismatch with directive: /* PRINTFLIKE */ [283] diff --git a/usr.bin/xlint/lint1/msg_284.c b/usr.bin/xlint/lint1/msg_284.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_284.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_284.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_284.c" + +// Test for message: fallthrough on default statement [284] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_284.exp b/usr.bin/xlint/lint1/msg_284.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_284.exp @@ -0,0 +1 @@ +msg_284.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_285.c b/usr.bin/xlint/lint1/msg_285.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_285.c @@ -0,0 +1,15 @@ +/* $NetBSD: msg_285.c,v 1.3 2021/06/15 08:48:49 rillig Exp $ */ +# 3 "msg_285.c" + +// Test for message: prototype declaration [285] + +/* lint1-extra-flags: -r */ + +void function(int, int, int); /* expect: 285 */ + +/* ARGSUSED */ +extern void +function(a, b) + int a, b; +{ /* expect: 3 declared, 2 defined */ +} diff --git a/usr.bin/xlint/lint1/msg_285.exp b/usr.bin/xlint/lint1/msg_285.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_285.exp @@ -0,0 +1,2 @@ +msg_285.c(14): error: parameter mismatch: 3 declared, 2 defined [51] +msg_285.c(8): prototype declaration [285] diff --git a/usr.bin/xlint/lint1/msg_286.c b/usr.bin/xlint/lint1/msg_286.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_286.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_286.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_286.c" + +// Test for message: function definition is not a prototype [286] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_286.exp b/usr.bin/xlint/lint1/msg_286.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_286.exp @@ -0,0 +1 @@ +msg_286.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_287.c b/usr.bin/xlint/lint1/msg_287.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_287.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_287.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_287.c" + +// Test for message: function declaration is not a prototype [287] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_287.exp b/usr.bin/xlint/lint1/msg_287.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_287.exp @@ -0,0 +1 @@ +msg_287.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_288.c b/usr.bin/xlint/lint1/msg_288.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_288.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_288.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_288.c" + +// Test for message: dubious use of /* VARARGS */ with /* %s */ [288] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_288.exp b/usr.bin/xlint/lint1/msg_288.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_288.exp @@ -0,0 +1 @@ +msg_288.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_289.c b/usr.bin/xlint/lint1/msg_289.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_289.c @@ -0,0 +1,11 @@ +/* $NetBSD: msg_289.c,v 1.3 2021/08/27 20:49:25 rillig Exp $ */ +# 3 "msg_289.c" + +// Test for message: can't be used together: /* PRINTFLIKE */ /* SCANFLIKE */ [289] + +/* PRINTFLIKE */ /* SCANFLIKE */ +void +both(void) +/* expect+1: warning: can't be used together */ +{ +} diff --git a/usr.bin/xlint/lint1/msg_289.exp b/usr.bin/xlint/lint1/msg_289.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_289.exp @@ -0,0 +1 @@ +msg_289.c(10): warning: can't be used together: /* PRINTFLIKE */ /* SCANFLIKE */ [289] diff --git a/usr.bin/xlint/lint1/msg_290.c b/usr.bin/xlint/lint1/msg_290.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_290.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_290.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_290.c" + +// Test for message: static function %s declared but not defined [290] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_290.exp b/usr.bin/xlint/lint1/msg_290.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_290.exp @@ -0,0 +1 @@ +msg_290.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_291.c b/usr.bin/xlint/lint1/msg_291.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_291.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_291.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_291.c" + +// Test for message: invalid multibyte character [291] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_291.exp b/usr.bin/xlint/lint1/msg_291.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_291.exp @@ -0,0 +1 @@ +msg_291.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_292.c b/usr.bin/xlint/lint1/msg_292.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_292.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_292.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_292.c" + +// Test for message: cannot concatenate wide and regular string literals [292] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_292.exp b/usr.bin/xlint/lint1/msg_292.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_292.exp @@ -0,0 +1 @@ +msg_292.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_293.c b/usr.bin/xlint/lint1/msg_293.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_293.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_293.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_293.c" + +// Test for message: argument %d must be 'char *' for PRINTFLIKE/SCANFLIKE [293] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_293.exp b/usr.bin/xlint/lint1/msg_293.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_293.exp @@ -0,0 +1 @@ +msg_293.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_294.c b/usr.bin/xlint/lint1/msg_294.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_294.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_294.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_294.c" + +// Test for message: multi-character character constant [294] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_294.exp b/usr.bin/xlint/lint1/msg_294.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_294.exp @@ -0,0 +1 @@ +msg_294.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_295.c b/usr.bin/xlint/lint1/msg_295.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_295.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_295.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_295.c" + +// Test for message: conversion of '%s' to '%s' is out of range, arg #%d [295] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_295.exp b/usr.bin/xlint/lint1/msg_295.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_295.exp @@ -0,0 +1 @@ +msg_295.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_296.c b/usr.bin/xlint/lint1/msg_296.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_296.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_296.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_296.c" + +// Test for message: conversion of negative constant to unsigned type, arg #%d [296] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_296.exp b/usr.bin/xlint/lint1/msg_296.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_296.exp @@ -0,0 +1 @@ +msg_296.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_297.c b/usr.bin/xlint/lint1/msg_297.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_297.c @@ -0,0 +1,24 @@ +/* $NetBSD: msg_297.c,v 1.3 2021/08/27 20:49:25 rillig Exp $ */ +# 3 "msg_297.c" + +// Test for message: conversion to '%s' may sign-extend incorrectly, arg #%d [297] + +/* lint1-extra-flags: -P -a -p */ + +void take_unsigned_long_long(unsigned long long); +void take_long_long(long long); + +void +caller(signed int si, unsigned int ui) +{ + + /* expect+1: warning: conversion to 'unsigned long long' may sign-extend incorrectly, arg #1 [297] */ + take_unsigned_long_long(si); + + take_unsigned_long_long(ui); + + take_long_long(si); + + /* expect+1: warning: conversion to 'long long' may sign-extend incorrectly, arg #1 [297] */ + take_long_long(ui); +} diff --git a/usr.bin/xlint/lint1/msg_297.exp b/usr.bin/xlint/lint1/msg_297.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_297.exp @@ -0,0 +1,2 @@ +msg_297.c(16): warning: conversion to 'unsigned long long' may sign-extend incorrectly, arg #1 [297] +msg_297.c(23): warning: conversion to 'long long' may sign-extend incorrectly, arg #1 [297] diff --git a/usr.bin/xlint/lint1/msg_298.c b/usr.bin/xlint/lint1/msg_298.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_298.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_298.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_298.c" + +// Test for message: conversion from '%s' to '%s' may lose accuracy, arg #%d [298] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_298.exp b/usr.bin/xlint/lint1/msg_298.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_298.exp @@ -0,0 +1 @@ +msg_298.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_299.c b/usr.bin/xlint/lint1/msg_299.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_299.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_299.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_299.c" + +// Test for message: prototype does not match old style definition, arg #%d [299] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_299.exp b/usr.bin/xlint/lint1/msg_299.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_299.exp @@ -0,0 +1 @@ +msg_299.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_300.c b/usr.bin/xlint/lint1/msg_300.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_300.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_300.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_300.c" + +// Test for message: old style definition [300] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_300.exp b/usr.bin/xlint/lint1/msg_300.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_300.exp @@ -0,0 +1 @@ +msg_300.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_301.c b/usr.bin/xlint/lint1/msg_301.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_301.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_301.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_301.c" + +// Test for message: array of incomplete type [301] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_301.exp b/usr.bin/xlint/lint1/msg_301.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_301.exp @@ -0,0 +1 @@ +msg_301.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_302.c b/usr.bin/xlint/lint1/msg_302.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_302.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_302.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_302.c" + +// Test for message: %s returns pointer to automatic object [302] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_302.exp b/usr.bin/xlint/lint1/msg_302.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_302.exp @@ -0,0 +1 @@ +msg_302.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_303.c b/usr.bin/xlint/lint1/msg_303.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_303.c @@ -0,0 +1,21 @@ +/* $NetBSD: msg_303.c,v 1.3 2021/08/16 18:51:58 rillig Exp $ */ +# 3 "msg_303.c" + +/* Test for message: ANSI C forbids conversion of %s to %s [303] */ + +/* lint1-flags: -sw */ + +void take_void_pointer(void *); + +void * +to_void_pointer(void) +{ + /* expect+1: warning: ANSI C forbids conversion of function pointer to 'void *' [303] */ + return to_void_pointer; +} + +void (*to_function_pointer(void *arg))(void) +{ + /* expect+1: warning: ANSI C forbids conversion of 'void *' to function pointer [303] */ + return arg; +} diff --git a/usr.bin/xlint/lint1/msg_303.exp b/usr.bin/xlint/lint1/msg_303.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_303.exp @@ -0,0 +1,2 @@ +msg_303.c(14): warning: ANSI C forbids conversion of function pointer to 'void *' [303] +msg_303.c(20): warning: ANSI C forbids conversion of 'void *' to function pointer [303] diff --git a/usr.bin/xlint/lint1/msg_304.c b/usr.bin/xlint/lint1/msg_304.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_304.c @@ -0,0 +1,19 @@ +/* $NetBSD: msg_304.c,v 1.3 2021/08/16 18:51:58 rillig Exp $ */ +# 3 "msg_304.c" + +/* Test for message: ANSI C forbids conversion of %s to %s, arg #%d [304] */ + +/* lint1-flags: -sw */ + +void take_void_pointer(void *); +void take_function_pointer(void (*)(void)); + +void +caller(void *arg) +{ + /* expect+1: warning: ANSI C forbids conversion of function pointer to 'void *', arg #1 [304] */ + take_void_pointer(caller); + + /* expect+1: warning: ANSI C forbids conversion of 'void *' to function pointer, arg #1 [304] */ + take_function_pointer(arg); +} diff --git a/usr.bin/xlint/lint1/msg_304.exp b/usr.bin/xlint/lint1/msg_304.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_304.exp @@ -0,0 +1,2 @@ +msg_304.c(15): warning: ANSI C forbids conversion of function pointer to 'void *', arg #1 [304] +msg_304.c(18): warning: ANSI C forbids conversion of 'void *' to function pointer, arg #1 [304] diff --git a/usr.bin/xlint/lint1/msg_305.c b/usr.bin/xlint/lint1/msg_305.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_305.c @@ -0,0 +1,20 @@ +/* $NetBSD: msg_305.c,v 1.3 2021/08/16 18:51:58 rillig Exp $ */ +# 3 "msg_305.c" + +/* Test for message: ANSI C forbids conversion of %s to %s, op %s [305] */ + +/* lint1-flags: -sw */ + +void take_void_pointer(void *); + +typedef void (*function)(void); + +void +caller(void **void_pointer, function *function_pointer) +{ + /* expect+1: warning: ANSI C forbids conversion of function pointer to 'void *', op = [305] */ + *void_pointer = *function_pointer; + + /* expect+1: warning: ANSI C forbids conversion of 'void *' to function pointer, op = [305] */ + *function_pointer = *void_pointer; +} diff --git a/usr.bin/xlint/lint1/msg_305.exp b/usr.bin/xlint/lint1/msg_305.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_305.exp @@ -0,0 +1,2 @@ +msg_305.c(16): warning: ANSI C forbids conversion of function pointer to 'void *', op = [305] +msg_305.c(19): warning: ANSI C forbids conversion of 'void *' to function pointer, op = [305] diff --git a/usr.bin/xlint/lint1/msg_306.c b/usr.bin/xlint/lint1/msg_306.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_306.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_306.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_306.c" + +// Test for message: constant truncated by conversion, op %s [306] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_306.exp b/usr.bin/xlint/lint1/msg_306.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_306.exp @@ -0,0 +1 @@ +msg_306.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_307.c b/usr.bin/xlint/lint1/msg_307.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_307.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_307.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_307.c" + +// Test for message: static variable %s set but not used [307] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_307.exp b/usr.bin/xlint/lint1/msg_307.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_307.exp @@ -0,0 +1 @@ +msg_307.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_308.c b/usr.bin/xlint/lint1/msg_308.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_308.c @@ -0,0 +1,10 @@ +/* $NetBSD: msg_308.c,v 1.6 2021/04/05 01:35:34 rillig Exp $ */ +# 3 "msg_308.c" + +// Test for message: invalid type for _Complex [308] + +float _Complex float_complex; +double _Complex double_complex; +long double _Complex long_double_complex; +_Complex plain_complex; /* expect: 308 */ +int _Complex int_complex; /* expect: 308 *//* expect: 4 */ diff --git a/usr.bin/xlint/lint1/msg_308.exp b/usr.bin/xlint/lint1/msg_308.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_308.exp @@ -0,0 +1,3 @@ +msg_308.c(9): error: invalid type for _Complex [308] +msg_308.c(10): error: invalid type for _Complex [308] +msg_308.c(10): error: illegal type combination [4] diff --git a/usr.bin/xlint/lint1/msg_309.c b/usr.bin/xlint/lint1/msg_309.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_309.c @@ -0,0 +1,39 @@ +/* $NetBSD: msg_309.c,v 1.4 2021/05/16 10:18:25 rillig Exp $ */ +# 3 "msg_309.c" + +// Test for message: extra bits set to 0 in conversion of '%s' to '%s', op '%s' [309] + +int +scale(unsigned long long x) { + + /* + * Both operands of '&' have the same type, therefore no conversion + * is necessary and no bits can get lost. + */ + if ((x & 0xffffffff00000000ULL) != 0) + return 32; + + /* + * The constant has type 'unsigned 32-bit'. The usual arithmetic + * conversions of '&' convert this constant to unsigned 64-bit. + * The programmer may or may not have intended to sign-extend the + * bit mask here. This situation may occur during migration from a + * 32-bit to a 64-bit platform. + */ + if ((x & 0xffff0000) != 0) /* expect: 309 */ + return 16; + + /* + * In the remaining cases, the constant does not have its most + * significant bit set, therefore there is no ambiguity. + */ + if ((x & 0xff00) != 0) + return 8; + if ((x & 0xf0) != 0) + return 4; + if ((x & 0xc) != 0) + return 2; + if ((x & 0x2) != 0) + return 1; + return (int)(x & 0x1); +} diff --git a/usr.bin/xlint/lint1/msg_309.exp b/usr.bin/xlint/lint1/msg_309.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_309.exp @@ -0,0 +1 @@ +msg_309.c(23): warning: extra bits set to 0 in conversion of 'unsigned int' to 'unsigned long long', op '&' [309] diff --git a/usr.bin/xlint/lint1/msg_310.c b/usr.bin/xlint/lint1/msg_310.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_310.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_310.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_310.c" + +// Test for message: symbol renaming can't be used on function arguments [310] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_310.exp b/usr.bin/xlint/lint1/msg_310.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_310.exp @@ -0,0 +1 @@ +msg_310.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_311.c b/usr.bin/xlint/lint1/msg_311.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_311.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_311.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_311.c" + +// Test for message: symbol renaming can't be used on automatic variables [311] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_311.exp b/usr.bin/xlint/lint1/msg_311.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_311.exp @@ -0,0 +1 @@ +msg_311.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_312.c b/usr.bin/xlint/lint1/msg_312.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_312.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_312.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_312.c" + +// Test for message: %s C does not support // comments [312] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_312.exp b/usr.bin/xlint/lint1/msg_312.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_312.exp @@ -0,0 +1 @@ +msg_312.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_313.c b/usr.bin/xlint/lint1/msg_313.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_313.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_313.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_313.c" + +// Test for message: struct or union member name in initializer is a C9X feature [313] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_313.exp b/usr.bin/xlint/lint1/msg_313.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_313.exp @@ -0,0 +1 @@ +msg_313.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_314.c b/usr.bin/xlint/lint1/msg_314.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_314.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_314.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_314.c" + +// Test for message: %s is not a structure or a union [314] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_314.exp b/usr.bin/xlint/lint1/msg_314.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_314.exp @@ -0,0 +1 @@ +msg_314.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_315.c b/usr.bin/xlint/lint1/msg_315.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_315.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_315.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_315.c" + +// Test for message: GCC style struct or union member name in initializer [315] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_315.exp b/usr.bin/xlint/lint1/msg_315.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_315.exp @@ -0,0 +1 @@ +msg_315.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_316.c b/usr.bin/xlint/lint1/msg_316.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_316.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_316.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_316.c" + +// Test for message: __FUNCTION__/__PRETTY_FUNCTION__ is a GCC extension [316] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_316.exp b/usr.bin/xlint/lint1/msg_316.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_316.exp @@ -0,0 +1 @@ +msg_316.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_317.c b/usr.bin/xlint/lint1/msg_317.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_317.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_317.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_317.c" + +// Test for message: __func__ is a C9X feature [317] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_317.exp b/usr.bin/xlint/lint1/msg_317.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_317.exp @@ -0,0 +1 @@ +msg_317.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_318.c b/usr.bin/xlint/lint1/msg_318.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_318.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_318.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_318.c" + +// Test for message: variable array dimension is a C99/GCC extension [318] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_318.exp b/usr.bin/xlint/lint1/msg_318.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_318.exp @@ -0,0 +1 @@ +msg_318.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_319.c b/usr.bin/xlint/lint1/msg_319.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_319.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_319.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_319.c" + +// Test for message: compound literals are a C9X/GCC extension [319] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_319.exp b/usr.bin/xlint/lint1/msg_319.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_319.exp @@ -0,0 +1 @@ +msg_319.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_320.c b/usr.bin/xlint/lint1/msg_320.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_320.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_320.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_320.c" + +// Test for message: ({ }) is a GCC extension [320] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_320.exp b/usr.bin/xlint/lint1/msg_320.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_320.exp @@ -0,0 +1 @@ +msg_320.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_321.c b/usr.bin/xlint/lint1/msg_321.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_321.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_321.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_321.c" + +// Test for message: array initializer with designators is a C9X feature [321] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_321.exp b/usr.bin/xlint/lint1/msg_321.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_321.exp @@ -0,0 +1 @@ +msg_321.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_322.c b/usr.bin/xlint/lint1/msg_322.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_322.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_322.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_322.c" + +// Test for message: zero sized array is a C99 extension [322] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_322.exp b/usr.bin/xlint/lint1/msg_322.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_322.exp @@ -0,0 +1 @@ +msg_322.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_323.c b/usr.bin/xlint/lint1/msg_323.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_323.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_323.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_323.c" + +// Test for message: continue in 'do ... while (0)' loop [323] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_323.exp b/usr.bin/xlint/lint1/msg_323.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_323.exp @@ -0,0 +1 @@ +msg_323.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_324.c b/usr.bin/xlint/lint1/msg_324.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_324.c @@ -0,0 +1,52 @@ +/* $NetBSD: msg_324.c,v 1.5 2021/01/31 11:12:07 rillig Exp $ */ +# 3 "msg_324.c" + +// Test for message: suggest cast from '%s' to '%s' on op %s to avoid overflow [324] + +/* + * This warning applies to binary operators if the result of the operator + * is converted to a type that is bigger than the operands' result type + * after the usual arithmetic promotions. + * + * In such a case, the operator's result would be truncated to the operator's + * result type (invoking undefined behavior for signed integers), and that + * truncated value would then be converted. At that point, a few bits may + * have been lost. + */ + +/* lint1-flags: -g -S -w -P */ + +void +example(char c, int i, unsigned u) +{ + long long ll; + unsigned long long ull; + + ll = c + i; /* expect: 324 */ + ll = i - c; /* expect: 324 */ + ull = c * u; /* expect: 324 */ + ull = u + c; /* expect: 324 */ + ull = i - u; /* expect: 324 */ + ull = u * i; /* expect: 324 */ + ll = i << c; /* expect: 324 */ + + /* + * The operators SHR, DIV and MOD cannot produce an overflow, + * therefore no warning is necessary for them. + */ + ll = i >> c; + ull = u / c; + ull = u % c; + + /* + * Assigning the result of an increment or decrement operator to a + * differently-sized type is no unusual that there is no need to warn + * about it. It's also more unlikely that there is an actual loss + * since this only happens for a single value of the old type, unlike + * "ull = u * u", which has many more possibilities for overflowing. + */ + ull = u++; + ull = ++u; + ull = u--; + ull = --u; +} diff --git a/usr.bin/xlint/lint1/msg_324.exp b/usr.bin/xlint/lint1/msg_324.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_324.exp @@ -0,0 +1,7 @@ +msg_324.c(25): warning: suggest cast from 'int' to 'long long' on op + to avoid overflow [324] +msg_324.c(26): warning: suggest cast from 'int' to 'long long' on op - to avoid overflow [324] +msg_324.c(27): warning: suggest cast from 'unsigned int' to 'unsigned long long' on op * to avoid overflow [324] +msg_324.c(28): warning: suggest cast from 'unsigned int' to 'unsigned long long' on op + to avoid overflow [324] +msg_324.c(29): warning: suggest cast from 'unsigned int' to 'unsigned long long' on op - to avoid overflow [324] +msg_324.c(30): warning: suggest cast from 'unsigned int' to 'unsigned long long' on op * to avoid overflow [324] +msg_324.c(31): warning: suggest cast from 'int' to 'long long' on op << to avoid overflow [324] diff --git a/usr.bin/xlint/lint1/msg_325.c b/usr.bin/xlint/lint1/msg_325.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_325.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_325.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_325.c" + +// Test for message: variable declaration in for loop [325] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_325.exp b/usr.bin/xlint/lint1/msg_325.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_325.exp @@ -0,0 +1 @@ +msg_325.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_326.c b/usr.bin/xlint/lint1/msg_326.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_326.c @@ -0,0 +1,7 @@ +/* $NetBSD: msg_326.c,v 1.2 2021/02/21 09:07:58 rillig Exp $ */ +# 3 "msg_326.c" + +// Test for message: %s attribute ignored for %s [326] + +TODO: "Add example code that triggers the above message." /* expect: 249 */ +TODO: "Add example code that almost triggers the above message." diff --git a/usr.bin/xlint/lint1/msg_326.exp b/usr.bin/xlint/lint1/msg_326.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_326.exp @@ -0,0 +1 @@ +msg_326.c(6): error: syntax error ':' [249] diff --git a/usr.bin/xlint/lint1/msg_327.c b/usr.bin/xlint/lint1/msg_327.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_327.c @@ -0,0 +1,20 @@ +/* $NetBSD: msg_327.c,v 1.5 2021/03/20 15:28:07 rillig Exp $ */ +# 3 "msg_327.c" + +/* Test for message: declarations after statements is a C99 feature [327] */ + +/* lint1-flags: -w */ + +void statement(void); + +/*ARGSUSED*/ +void +example(void) +{ + statement(); + int declaration_1; /* expect: 327 */ + statement(); + int declaration_2; /* expect: 327 */ + statement(); + int declaration_3; /* expect: 327 */ +} diff --git a/usr.bin/xlint/lint1/msg_327.exp b/usr.bin/xlint/lint1/msg_327.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_327.exp @@ -0,0 +1,3 @@ +msg_327.c(15): warning: declarations after statements is a C99 feature [327] +msg_327.c(17): warning: declarations after statements is a C99 feature [327] +msg_327.c(19): warning: declarations after statements is a C99 feature [327] diff --git a/usr.bin/xlint/lint1/msg_328.c b/usr.bin/xlint/lint1/msg_328.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_328.c @@ -0,0 +1,19 @@ +/* $NetBSD: msg_328.c,v 1.3 2021/08/03 20:57:06 rillig Exp $ */ +# 3 "msg_328.c" + +// Test for message: union cast is a GCC extension [328] + +/* lint1-flags: -Sw */ + +union target { + int b; +}; + +void +foo(void) +{ + union target arg = { 123 }; + /* expect+1: error: union cast is a GCC extension [328] */ + arg = (union target)3; + arg.b++; +} diff --git a/usr.bin/xlint/lint1/msg_328.exp b/usr.bin/xlint/lint1/msg_328.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_328.exp @@ -0,0 +1 @@ +msg_328.c(17): error: union cast is a GCC extension [328] diff --git a/usr.bin/xlint/lint1/msg_329.c b/usr.bin/xlint/lint1/msg_329.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_329.c @@ -0,0 +1,37 @@ +/* $NetBSD: msg_329.c,v 1.3 2021/08/03 20:57:06 rillig Exp $ */ +# 3 "msg_329.c" + +// Test for message: type '%s' is not a member of '%s' [329] + +union u { + int i1; + int i2; + void *vp; +}; + +void +example(void) +{ + /* + * A type cast to a union type is valid if the source type is any + * member type of the union. Since all union members with the same + * type have the same representation, the name of the union member + * doesn't matter. + * + * XXX: could there be padding bits or other tricky details that are + * settable per-member? These could make the type alone insufficient + * for determining the exact representation. + * + * C99 6.5.4 "Cast operators" does not mention a union cast. On the + * contrary, it says that the type name shall specify a scalar type. + * + * C11 6.5.4 "Cast operators" differs from C99 but still requires + * scalar types for both the target type and the source value. + * + * This is a GCC extension. + * See https://gcc.gnu.org/onlinedocs/gcc/Cast-to-Union.html. + */ + union u u_i1 = (union u)3; + union u u_vp = (union u)(void *)0; + union u u_cp = (union u)(char *)0; /* expect: 329 */ +} diff --git a/usr.bin/xlint/lint1/msg_329.exp b/usr.bin/xlint/lint1/msg_329.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_329.exp @@ -0,0 +1 @@ +msg_329.c(36): error: type 'pointer to char' is not a member of 'union u' [329] diff --git a/usr.bin/xlint/lint1/msg_330.c b/usr.bin/xlint/lint1/msg_330.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_330.c @@ -0,0 +1,22 @@ +/* $NetBSD: msg_330.c,v 1.4 2021/04/05 01:35:34 rillig Exp $ */ +# 3 "msg_330.c" + +// Test for message: operand of '%s' must be bool, not '%s' [330] +// +// See d_c99_bool_strict.c for many more examples. + +/* lint1-extra-flags: -T */ + +typedef _Bool bool; + +void +called(bool); + +/*ARGSUSED*/ +void +example(bool b, char c, int i) +{ + called(!b); + called(!c); /* expect: 330 *//* expect: 334 */ + called(!i); /* expect: 330 *//* expect: 334 */ +} diff --git a/usr.bin/xlint/lint1/msg_330.exp b/usr.bin/xlint/lint1/msg_330.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_330.exp @@ -0,0 +1,4 @@ +msg_330.c(20): error: operand of '!' must be bool, not 'char' [330] +msg_330.c(20): error: argument #1 expects '_Bool', gets passed 'int' [334] +msg_330.c(21): error: operand of '!' must be bool, not 'int' [330] +msg_330.c(21): error: argument #1 expects '_Bool', gets passed 'int' [334] diff --git a/usr.bin/xlint/lint1/msg_331.c b/usr.bin/xlint/lint1/msg_331.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_331.c @@ -0,0 +1,24 @@ +/* $NetBSD: msg_331.c,v 1.3 2021/04/05 01:35:34 rillig Exp $ */ +# 3 "msg_331.c" + +// Test for message: left operand of '%s' must be bool, not '%s' [331] +// +// See d_c99_bool_strict.c for many more examples. + +/* lint1-extra-flags: -T */ + +typedef _Bool bool; + +void +test(bool); + +void +example(bool b, char c, int i) +{ + test(b && b); + test(c && b); /* expect: 331 *//* expect: 334 */ + test(i && b); /* expect: 331 *//* expect: 334 */ + + test(c != '\0'); + test(i != 0); +} diff --git a/usr.bin/xlint/lint1/msg_331.exp b/usr.bin/xlint/lint1/msg_331.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_331.exp @@ -0,0 +1,4 @@ +msg_331.c(19): error: left operand of '&&' must be bool, not 'char' [331] +msg_331.c(19): error: argument #1 expects '_Bool', gets passed 'int' [334] +msg_331.c(20): error: left operand of '&&' must be bool, not 'int' [331] +msg_331.c(20): error: argument #1 expects '_Bool', gets passed 'int' [334] diff --git a/usr.bin/xlint/lint1/msg_332.c b/usr.bin/xlint/lint1/msg_332.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_332.c @@ -0,0 +1,24 @@ +/* $NetBSD: msg_332.c,v 1.3 2021/04/05 01:35:34 rillig Exp $ */ +# 3 "msg_332.c" + +// Test for message: right operand of '%s' must be bool, not '%s' [332] +// +// See d_c99_bool_strict.c for many more examples. + +/* lint1-extra-flags: -T */ + +typedef _Bool bool; + +void +test(bool); + +void +example(bool b, char c, int i) +{ + test(b && b); + test(b && c); /* expect: 332 *//* expect: 334 */ + test(b && i); /* expect: 332 *//* expect: 334 */ + + test(c != '\0'); + test(i != 0); +} diff --git a/usr.bin/xlint/lint1/msg_332.exp b/usr.bin/xlint/lint1/msg_332.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_332.exp @@ -0,0 +1,4 @@ +msg_332.c(19): error: right operand of '&&' must be bool, not 'char' [332] +msg_332.c(19): error: argument #1 expects '_Bool', gets passed 'int' [334] +msg_332.c(20): error: right operand of '&&' must be bool, not 'int' [332] +msg_332.c(20): error: argument #1 expects '_Bool', gets passed 'int' [334] diff --git a/usr.bin/xlint/lint1/msg_333.c b/usr.bin/xlint/lint1/msg_333.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_333.c @@ -0,0 +1,39 @@ +/* $NetBSD: msg_333.c,v 1.4 2021/07/04 07:09:39 rillig Exp $ */ +# 3 "msg_333.c" + +// Test for message: controlling expression must be bool, not '%s' [333] +// +// See d_c99_bool_strict.c for many more examples. + +/* lint1-extra-flags: -T */ + +typedef _Bool bool; + +const char * +example(bool b, int i, const char *p) +{ + + if (b) + return "bool"; + + /* expect+1: must be bool, not 'int' [333] */ + if (i) + return "int"; + + /* expect+1: must be bool, not 'pointer' [333] */ + if (p) + return "pointer"; + + if (__lint_false) { + /* expect+1: warning: statement not reached [193] */ + return "bool constant"; + } + + /* expect+1: controlling expression must be bool, not 'int' [333] */ + if (0) { + /* expect+1: warning: statement not reached [193] */ + return "integer constant"; + } + + return p + i; +} diff --git a/usr.bin/xlint/lint1/msg_333.exp b/usr.bin/xlint/lint1/msg_333.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_333.exp @@ -0,0 +1,5 @@ +msg_333.c(20): error: controlling expression must be bool, not 'int' [333] +msg_333.c(24): error: controlling expression must be bool, not 'pointer' [333] +msg_333.c(29): warning: statement not reached [193] +msg_333.c(33): error: controlling expression must be bool, not 'int' [333] +msg_333.c(35): warning: statement not reached [193] diff --git a/usr.bin/xlint/lint1/msg_334.c b/usr.bin/xlint/lint1/msg_334.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_334.c @@ -0,0 +1,24 @@ +/* $NetBSD: msg_334.c,v 1.1 2021/01/14 22:18:14 rillig Exp $ */ +# 3 "msg_334.c" + +// Test for message: argument #%d expects '%s', gets passed '%s' [334] +// +// See d_c99_bool_strict.c for many more examples. + +/* lint1-extra-flags: -T */ + +typedef _Bool bool; + +void +test_bool(bool); +void +test_int(int); + +void +caller(bool b, int i) +{ + test_bool(b); + test_bool(i); /* expect: 334 */ + test_int(b); /* expect: 334 */ + test_int(i); +} diff --git a/usr.bin/xlint/lint1/msg_334.exp b/usr.bin/xlint/lint1/msg_334.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_334.exp @@ -0,0 +1,2 @@ +msg_334.c(21): error: argument #1 expects '_Bool', gets passed 'int' [334] +msg_334.c(22): error: argument #1 expects 'int', gets passed '_Bool' [334] diff --git a/usr.bin/xlint/lint1/msg_335.c b/usr.bin/xlint/lint1/msg_335.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_335.c @@ -0,0 +1,22 @@ +/* $NetBSD: msg_335.c,v 1.1 2021/01/14 22:18:14 rillig Exp $ */ +# 3 "msg_335.c" + +// Test for message: operand of '%s' must not be bool [335] +// +// See d_c99_bool_strict.c for many more examples. + +/* lint1-extra-flags: -T */ + +typedef _Bool bool; + +void +example(bool b) +{ + b = +b; /* expect: 335 */ + b = -b; /* expect: 335 */ + b = !b; + b++; /* expect: 335 */ + ++b; /* expect: 335 */ + b--; /* expect: 335 */ + --b; /* expect: 335 */ +} diff --git a/usr.bin/xlint/lint1/msg_335.exp b/usr.bin/xlint/lint1/msg_335.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_335.exp @@ -0,0 +1,6 @@ +msg_335.c(15): error: operand of '+' must not be bool [335] +msg_335.c(16): error: operand of '-' must not be bool [335] +msg_335.c(18): error: operand of 'x++' must not be bool [335] +msg_335.c(19): error: operand of '++x' must not be bool [335] +msg_335.c(20): error: operand of 'x--' must not be bool [335] +msg_335.c(21): error: operand of '--x' must not be bool [335] diff --git a/usr.bin/xlint/lint1/msg_336.c b/usr.bin/xlint/lint1/msg_336.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_336.c @@ -0,0 +1,21 @@ +/* $NetBSD: msg_336.c,v 1.3 2021/04/05 01:35:34 rillig Exp $ */ +# 3 "msg_336.c" + +// Test for message: left operand of '%s' must not be bool [336] +// +// See d_c99_bool_strict.c for many more examples. + +/* lint1-extra-flags: -T */ + +typedef _Bool bool; + +void +test(bool); + +void +example(bool b, int i) +{ + test(b + i); /* expect: 336 *//* expect: 334 */ + test(b); + test(i != 0); +} diff --git a/usr.bin/xlint/lint1/msg_336.exp b/usr.bin/xlint/lint1/msg_336.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_336.exp @@ -0,0 +1,2 @@ +msg_336.c(18): error: left operand of '+' must not be bool [336] +msg_336.c(18): error: argument #1 expects '_Bool', gets passed 'int' [334] diff --git a/usr.bin/xlint/lint1/msg_337.c b/usr.bin/xlint/lint1/msg_337.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_337.c @@ -0,0 +1,21 @@ +/* $NetBSD: msg_337.c,v 1.3 2021/04/05 01:35:34 rillig Exp $ */ +# 3 "msg_337.c" + +// Test for message: right operand of '%s' must not be bool [337] +// +// See d_c99_bool_strict.c for many more examples. + +/* lint1-extra-flags: -T */ + +typedef _Bool bool; + +void +test(bool); + +void +example(bool b, int i) +{ + test(i + b); /* expect: 337 *//* expect: 334 */ + test(b); + test(i != 0); +} diff --git a/usr.bin/xlint/lint1/msg_337.exp b/usr.bin/xlint/lint1/msg_337.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_337.exp @@ -0,0 +1,2 @@ +msg_337.c(18): error: right operand of '+' must not be bool [337] +msg_337.c(18): error: argument #1 expects '_Bool', gets passed 'int' [334] diff --git a/usr.bin/xlint/lint1/msg_338.c b/usr.bin/xlint/lint1/msg_338.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_338.c @@ -0,0 +1,81 @@ +/* $NetBSD: msg_338.c,v 1.5 2021/08/22 22:15:07 rillig Exp $ */ +# 3 "msg_338.c" + +// Test for message: option '%c' should be handled in the switch [338] + +int getopt(int, char *const *, const char *); +extern char *optarg; + +int +main(int argc, char **argv) +{ + int o; + + while ((o = getopt(argc, argv, "a:bc:d")) != -1) { /* expect: 338 *//* expect: 338 */ + switch (o) { + case 'a': + break; + case 'b': + /* + * The following while loop must not finish the check + * for the getopt options. + */ + while (optarg[0] != '\0') + optarg++; + break; + case 'e': /* expect: option 'e' should be listed */ + break; + case 'f': /* expect: option 'f' should be listed */ + /* + * The case labels in nested switch statements are + * ignored by the check for getopt options. + */ + switch (optarg[0]) { + case 'X': + break; + } + break; + case '?': + default: + break; + } + } + + /* A while loop that is not related to getopt is simply skipped. */ + while (o != 0) { + switch (o) { + case '?': + o = ':'; + } + } + + return 0; +} + +void usage(void); + +/* + * Before ckgetopt.c 1.11 from 2021-08-23, lint wrongly warned about a + * missing '?' in the switch statement, even though it was there. + * + * Seen in usr.bin/ftp/main.c 1.127 from 2020-07-18. + */ +int +question_option(int argc, char **argv) +{ + int c; + + while ((c = getopt(argc, argv, "?x")) != -1) { + switch (c) { + case 'x': + break; + case '?': + usage(); + return 0; + default: + usage(); + return 1; + } + } + return 0; +} diff --git a/usr.bin/xlint/lint1/msg_338.exp b/usr.bin/xlint/lint1/msg_338.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_338.exp @@ -0,0 +1,4 @@ +msg_338.c(26): warning: option 'e' should be listed in the options string [339] +msg_338.c(28): warning: option 'f' should be listed in the options string [339] +msg_338.c(14): warning: option 'c' should be handled in the switch [338] +msg_338.c(14): warning: option 'd' should be handled in the switch [338] diff --git a/usr.bin/xlint/lint1/msg_339.c b/usr.bin/xlint/lint1/msg_339.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_339.c @@ -0,0 +1,45 @@ +/* $NetBSD: msg_339.c,v 1.2 2021/04/05 01:35:34 rillig Exp $ */ +# 3 "msg_339.c" + +// Test for message: option '%c' should be listed in the options string [339] + +int getopt(int, char *const *, const char *); +extern char *optarg; + +int +main(int argc, char **argv) +{ + int o; + + while ((o = getopt(argc, argv, "a:bc:d")) != -1) { /* expect: 338 *//* expect: 338 */ + switch (o) { + case 'a': + break; + case 'b': + /* + * The following while loop must not finish the check + * for the getopt options. + */ + while (optarg[0] != '\0') + optarg++; + break; + case 'e': /* expect: option 'e' should be listed */ + break; + case 'f': /* expect: option 'f' should be listed */ + /* + * The case labels in nested switch statements are + * ignored by the check for getopt options. + */ + switch (optarg[0]) { + case 'X': + break; + } + break; + case '?': + default: + break; + } + } + + return 0; +} diff --git a/usr.bin/xlint/lint1/msg_339.exp b/usr.bin/xlint/lint1/msg_339.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_339.exp @@ -0,0 +1,4 @@ +msg_339.c(26): warning: option 'e' should be listed in the options string [339] +msg_339.c(28): warning: option 'f' should be listed in the options string [339] +msg_339.c(14): warning: option 'c' should be handled in the switch [338] +msg_339.c(14): warning: option 'd' should be handled in the switch [338] diff --git a/usr.bin/xlint/lint1/msg_340.c b/usr.bin/xlint/lint1/msg_340.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_340.c @@ -0,0 +1,19 @@ +/* $NetBSD: msg_340.c,v 1.2 2021/07/05 19:53:43 rillig Exp $ */ +# 3 "msg_340.c" + +// Test for message: initialization with '[a...b]' is a GCC extension [340] + +/* + * In strict C mode, GCC extensions are flagged as such. + */ + +/* lint1-flags: -Ssw */ + +int +example(void) +{ + int numbers[] = { + [2 ... 3] = 12 /* expect: 340 */ + }; + return numbers[0]; +} diff --git a/usr.bin/xlint/lint1/msg_340.exp b/usr.bin/xlint/lint1/msg_340.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_340.exp @@ -0,0 +1 @@ +msg_340.c(16): error: initialization with '[a...b]' is a GCC extension [340] diff --git a/usr.bin/xlint/lint1/msg_341.c b/usr.bin/xlint/lint1/msg_341.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_341.c @@ -0,0 +1,77 @@ +/* $NetBSD: msg_341.c,v 1.1 2021/04/05 02:05:47 rillig Exp $ */ +# 3 "msg_341.c" + +// Test for message: argument to '%s' must be 'unsigned char' or EOF, not '%s' [341] + +/* + * Ensure that the functions from are called with the correct + * argument. + */ + +/* NetBSD 9.99.81, */ +extern const unsigned short *_ctype_tab_; +extern const short *_tolower_tab_; +extern const short *_toupper_tab_; +int isspace(int); + +void sink(int); + +void +function_call_char(char c) +{ + + /* expect+1: argument to 'isspace' must be 'unsigned char' or EOF, not 'char' */ + (isspace)(c); + + /* This is the only allowed form. */ + isspace((unsigned char)c); + + /* The cast to 'int' is redundant, it doesn't hurt though. */ + isspace((int)(unsigned char)c); + + /* expect+1: argument to 'isspace' must be cast to 'unsigned char', not to 'int' */ + isspace((int)c); + + /* expect+1: argument to 'isspace' must be cast to 'unsigned char', not to 'unsigned int' */ + isspace((unsigned int)c); +} + +/* + * If the expression starts with type 'unsigned char', it can be cast to any + * other type. Chances are low enough that the cast is to 'char', which would + * be the only bad type. + */ +void +function_call_unsigned_char(unsigned char c) +{ + + (isspace)(c); + isspace((unsigned char)c); + isspace((int)c); + isspace((unsigned int)c); +} + +/* When used in a loop of fgetc, the type is already 'int'. That's fine. */ +void +function_call_int(int c) +{ + + isspace(c); +} + +void +macro_invocation_NetBSD(char c) +{ + + /* expect+1: argument to 'function from ' must be 'unsigned char' or EOF, not 'char' */ + sink(((int)((_ctype_tab_ + 1)[(c)] & 0x0040))); + + /* This is the only allowed form. */ + sink(((int)((_ctype_tab_ + 1)[((unsigned char)c)] & 0x0040))); + + /* expect+1: argument to 'function from ' must be cast to 'unsigned char', not to 'int' */ + sink(((int)((_ctype_tab_ + 1)[((int)c)] & 0x0040))); + + /* expect+1: argument to 'function from ' must be cast to 'unsigned char', not to 'unsigned int' */ + sink(((int)((_ctype_tab_ + 1)[((unsigned int)c)] & 0x0040))); +} diff --git a/usr.bin/xlint/lint1/msg_341.exp b/usr.bin/xlint/lint1/msg_341.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_341.exp @@ -0,0 +1,6 @@ +msg_341.c(24): warning: argument to 'isspace' must be 'unsigned char' or EOF, not 'char' [341] +msg_341.c(33): warning: argument to 'isspace' must be cast to 'unsigned char', not to 'int' [342] +msg_341.c(36): warning: argument to 'isspace' must be cast to 'unsigned char', not to 'unsigned int' [342] +msg_341.c(67): warning: argument to 'function from ' must be 'unsigned char' or EOF, not 'char' [341] +msg_341.c(73): warning: argument to 'function from ' must be cast to 'unsigned char', not to 'int' [342] +msg_341.c(76): warning: argument to 'function from ' must be cast to 'unsigned char', not to 'unsigned int' [342] diff --git a/usr.bin/xlint/lint1/msg_342.c b/usr.bin/xlint/lint1/msg_342.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_342.c @@ -0,0 +1,130 @@ +/* $NetBSD: msg_342.c,v 1.3 2021/07/25 22:43:08 rillig Exp $ */ +# 3 "msg_341.c" + +// Test for message: argument to '%s' must be cast to 'unsigned char', not to '%s' [342] + +/* + * Ensure that the functions from are called with the correct + * argument. + */ + +/* NetBSD 9.99.81, */ +extern const unsigned short *_ctype_tab_; +extern const short *_tolower_tab_; +extern const short *_toupper_tab_; +int isalnum(int); +int isalpha(int); +int isblank(int); +int iscntrl(int); +int isdigit(int); +int isgraph(int); +int islower(int); +int isprint(int); +int ispunct(int); +int isspace(int); +int isupper(int); +int isxdigit(int); +int tolower(int); +int toupper(int); + +int is_other(int); +int to_other(int); + +void sink(int); + +void +cover_is_ctype_function(char c) +{ + /* expect+1: warning: argument to 'isalnum' must be 'unsigned char' or EOF, not 'char' [341] */ + isalnum(c); + /* expect+1: warning: argument to 'isalpha' must be 'unsigned char' or EOF, not 'char' [341] */ + isalpha(c); + /* expect+1: warning: argument to 'isblank' must be 'unsigned char' or EOF, not 'char' [341] */ + isblank(c); + /* expect+1: warning: argument to 'iscntrl' must be 'unsigned char' or EOF, not 'char' [341] */ + iscntrl(c); + /* expect+1: warning: argument to 'isdigit' must be 'unsigned char' or EOF, not 'char' [341] */ + isdigit(c); + /* expect+1: warning: argument to 'isgraph' must be 'unsigned char' or EOF, not 'char' [341] */ + isgraph(c); + /* expect+1: warning: argument to 'islower' must be 'unsigned char' or EOF, not 'char' [341] */ + islower(c); + /* expect+1: warning: argument to 'isprint' must be 'unsigned char' or EOF, not 'char' [341] */ + isprint(c); + /* expect+1: warning: argument to 'ispunct' must be 'unsigned char' or EOF, not 'char' [341] */ + ispunct(c); + /* expect+1: warning: argument to 'isspace' must be 'unsigned char' or EOF, not 'char' [341] */ + isspace(c); + /* expect+1: warning: argument to 'isupper' must be 'unsigned char' or EOF, not 'char' [341] */ + isupper(c); + /* expect+1: warning: argument to 'isxdigit' must be 'unsigned char' or EOF, not 'char' [341] */ + isxdigit(c); + /* expect+1: warning: argument to 'tolower' must be 'unsigned char' or EOF, not 'char' [341] */ + tolower(c); + /* expect+1: warning: argument to 'toupper' must be 'unsigned char' or EOF, not 'char' [341] */ + toupper(c); + + /* Functions with similar names are not checked. */ + is_other(c); + to_other(c); +} + +void +function_call_char(char c) +{ + + /* expect+1: argument to 'isspace' must be 'unsigned char' or EOF, not 'char' */ + (isspace)(c); + + /* This is the only allowed form. */ + isspace((unsigned char)c); + + /* The cast to 'int' is redundant, it doesn't hurt though. */ + isspace((int)(unsigned char)c); + + /* expect+1: argument to 'isspace' must be cast to 'unsigned char', not to 'int' */ + isspace((int)c); + + /* expect+1: argument to 'isspace' must be cast to 'unsigned char', not to 'unsigned int' */ + isspace((unsigned int)c); +} + +/* + * If the expression starts with type 'unsigned char', it can be cast to any + * other type. Chances are low enough that the cast is to 'char', which would + * be the only bad type. + */ +void +function_call_unsigned_char(unsigned char c) +{ + + (isspace)(c); + isspace((unsigned char)c); + isspace((int)c); + isspace((unsigned int)c); +} + +/* When used in a loop of fgetc, the type is already 'int'. That's fine. */ +void +function_call_int(int c) +{ + + isspace(c); +} + +void +macro_invocation_NetBSD(char c) +{ + + /* expect+1: argument to 'function from ' must be 'unsigned char' or EOF, not 'char' */ + sink(((int)((_ctype_tab_ + 1)[(c)] & 0x0040))); + + /* This is the only allowed form. */ + sink(((int)((_ctype_tab_ + 1)[((unsigned char)c)] & 0x0040))); + + /* expect+1: argument to 'function from ' must be cast to 'unsigned char', not to 'int' */ + sink(((int)((_ctype_tab_ + 1)[((int)c)] & 0x0040))); + + /* expect+1: argument to 'function from ' must be cast to 'unsigned char', not to 'unsigned int' */ + sink(((int)((_ctype_tab_ + 1)[((unsigned int)c)] & 0x0040))); +} diff --git a/usr.bin/xlint/lint1/msg_342.exp b/usr.bin/xlint/lint1/msg_342.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_342.exp @@ -0,0 +1,20 @@ +msg_341.c(39): warning: argument to 'isalnum' must be 'unsigned char' or EOF, not 'char' [341] +msg_341.c(41): warning: argument to 'isalpha' must be 'unsigned char' or EOF, not 'char' [341] +msg_341.c(43): warning: argument to 'isblank' must be 'unsigned char' or EOF, not 'char' [341] +msg_341.c(45): warning: argument to 'iscntrl' must be 'unsigned char' or EOF, not 'char' [341] +msg_341.c(47): warning: argument to 'isdigit' must be 'unsigned char' or EOF, not 'char' [341] +msg_341.c(49): warning: argument to 'isgraph' must be 'unsigned char' or EOF, not 'char' [341] +msg_341.c(51): warning: argument to 'islower' must be 'unsigned char' or EOF, not 'char' [341] +msg_341.c(53): warning: argument to 'isprint' must be 'unsigned char' or EOF, not 'char' [341] +msg_341.c(55): warning: argument to 'ispunct' must be 'unsigned char' or EOF, not 'char' [341] +msg_341.c(57): warning: argument to 'isspace' must be 'unsigned char' or EOF, not 'char' [341] +msg_341.c(59): warning: argument to 'isupper' must be 'unsigned char' or EOF, not 'char' [341] +msg_341.c(61): warning: argument to 'isxdigit' must be 'unsigned char' or EOF, not 'char' [341] +msg_341.c(63): warning: argument to 'tolower' must be 'unsigned char' or EOF, not 'char' [341] +msg_341.c(65): warning: argument to 'toupper' must be 'unsigned char' or EOF, not 'char' [341] +msg_341.c(77): warning: argument to 'isspace' must be 'unsigned char' or EOF, not 'char' [341] +msg_341.c(86): warning: argument to 'isspace' must be cast to 'unsigned char', not to 'int' [342] +msg_341.c(89): warning: argument to 'isspace' must be cast to 'unsigned char', not to 'unsigned int' [342] +msg_341.c(120): warning: argument to 'function from ' must be 'unsigned char' or EOF, not 'char' [341] +msg_341.c(126): warning: argument to 'function from ' must be cast to 'unsigned char', not to 'int' [342] +msg_341.c(129): warning: argument to 'function from ' must be cast to 'unsigned char', not to 'unsigned int' [342] diff --git a/usr.bin/xlint/lint1/msg_343.c b/usr.bin/xlint/lint1/msg_343.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_343.c @@ -0,0 +1,47 @@ +/* $NetBSD: msg_343.c,v 1.5 2021/07/15 21:00:05 rillig Exp $ */ +# 3 "msg_343.c" + +/* Test for message: static array size is a C11 extension [343] */ + +/* lint1-flags: -Sw */ + +void takes_int_pointer(int []); +void takes_int_pointer_with_ignored_size(int [3]); +void takes_int_array(int[static 3]); /* expect: 343 */ +/* expect+1: syntax error '3' */ +void takes_volatile_int_array(int[volatile 3]); + +int +returns_int_pointer(int a[]) +{ + return a[0]; +} + +int +returns_int_pointer_with_ignored_size(int a[3]) +{ + return a[0]; +} + +int +returns_int_array(int a[static 3]) /* expect: 343 */ +{ + return a[0]; +} + +int +/* expect+1: syntax error '3' */ +returns_volatile_int_array(int a[volatile 3]) +{ + /* expect+2: cannot dereference non-pointer type */ + /* expect+1: expects to return value */ + return a[0]; +} + +/* + * This triggers the "Bad attribute", but for some reason, that custom error + * message does not make it into the actual diagnostic. + */ +/* expect+2: error: syntax error ']' [249] */ +/* expect+1: error: static array size is a C11 extension [343] */ +void invalid_storage_class(int a[const typedef 3]); diff --git a/usr.bin/xlint/lint1/msg_343.exp b/usr.bin/xlint/lint1/msg_343.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_343.exp @@ -0,0 +1,8 @@ +msg_343.c(10): error: static array size is a C11 extension [343] +msg_343.c(12): error: syntax error '3' [249] +msg_343.c(27): error: static array size is a C11 extension [343] +msg_343.c(34): error: syntax error '3' [249] +msg_343.c(38): error: cannot dereference non-pointer type [96] +msg_343.c(38): warning: function 'returns_volatile_int_array' expects to return value [214] +msg_343.c(47): error: syntax error ']' [249] +msg_343.c(47): error: static array size is a C11 extension [343] diff --git a/usr.bin/xlint/lint1/msg_344.c b/usr.bin/xlint/lint1/msg_344.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_344.c @@ -0,0 +1,24 @@ +/* $NetBSD: msg_344.c,v 1.1 2021/05/16 11:11:37 rillig Exp $ */ +# 3 "msg_344.c" + +// Test for message: bit-field of type plain 'int' has implementation-defined signedness [344] + +/* lint1-extra-flags: -p */ + +/* + * C90 3.5.2.1 allows 'int', 'signed int', 'unsigned int' as bit-field types. + * + * C99 6.7.2.1 significantly changed the wording of the allowable types for + * bit-fields. For example, 6.7.2.1p4 does not mention plain 'int' at all. + * The rationale for C99 6.7.2.1 mentions plain 'int' though, and it would + * have broken a lot of existing code to disallow plain 'int' as a bit-field + * type. Footnote 104 explicitly mentions plain 'int' as well and it even + * allows typedef-types for bit-fields. + */ +struct example { + /* expect+1: 344 */ + int plain_int: 1; + + signed int signed_int: 1; + unsigned int unsigned_int: 1; +}; diff --git a/usr.bin/xlint/lint1/msg_344.exp b/usr.bin/xlint/lint1/msg_344.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_344.exp @@ -0,0 +1 @@ +msg_344.c(20): warning: bit-field of type plain 'int' has implementation-defined signedness [344] diff --git a/usr.bin/xlint/lint1/msg_345.c b/usr.bin/xlint/lint1/msg_345.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_345.c @@ -0,0 +1,14 @@ +/* $NetBSD: msg_345.c,v 1.2 2021/06/27 20:47:13 rillig Exp $ */ +# 3 "msg_345.c" + +// Test for message: generic selection requires C11 or later [345] + +/* Omit flag -g since it silences c11ism. */ +/* lint1-flags: -Sw */ + +int +test(int x) +{ + /* expect+1: generic selection requires C11 or later [345] */ + return _Generic(x, default: 3) + x; +} diff --git a/usr.bin/xlint/lint1/msg_345.exp b/usr.bin/xlint/lint1/msg_345.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_345.exp @@ -0,0 +1 @@ +msg_345.c(13): error: generic selection requires C11 or later [345] diff --git a/usr.bin/xlint/lint1/msg_346.c b/usr.bin/xlint/lint1/msg_346.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_346.c @@ -0,0 +1,61 @@ +/* $NetBSD: msg_346.c,v 1.4 2021/08/16 18:51:58 rillig Exp $ */ +# 3 "msg_346.c" + +// Test for message: call to '%s' effectively discards 'const' from argument [346] + +typedef unsigned long size_t; + +void* memchr(const void *, int, size_t); /* C99 7.21.5.1 */ +char *strchr(const char *, int); /* C99 7.21.5.2 */ +char* strpbrk(const char *, const char *); /* C99 7.21.5.4 */ +char* strrchr(const char *, int); /* C99 7.21.5.5 */ +char* strstr(const char *, const char *); /* C99 7.21.5.7 */ + +void take_const_char_ptr(const char *); +void take_char_ptr(char *); + +void +example(void) +{ + const char *ccp = "const char *"; + char *cp = "char *"; + + ccp = strchr(ccp, 'c'); + ccp = strchr(cp, 'c'); + /* expect+1: warning: call to 'strchr' effectively discards 'const' from argument [346] */ + cp = strchr(ccp, 'c'); + cp = strchr(cp, 'c'); + + take_const_char_ptr(strchr(ccp, 'c')); + take_const_char_ptr(strchr(cp, 'c')); + /* expect+1: warning: call to 'strchr' effectively discards 'const' from argument [346] */ + take_char_ptr(strchr(ccp, 'c')); + take_char_ptr(strchr(cp, 'c')); + + take_const_char_ptr(strchr("literal", 'c')); + /* expect+1: warning: call to 'strchr' effectively discards 'const' from argument [346] */ + take_char_ptr(strchr("literal", 'c')); +} + +void +all_functions(void) +{ + /* expect+1: warning: call to 'memchr' effectively discards 'const' from argument [346] */ + take_char_ptr(memchr("string", 'c', 7)); + /* expect+1: warning: call to 'strchr' effectively discards 'const' from argument [346] */ + take_char_ptr(strchr("string", 'c')); + /* expect+1: warning: call to 'strpbrk' effectively discards 'const' from argument [346] */ + take_char_ptr(strpbrk("string", "c")); + /* expect+1: warning: call to 'strrchr' effectively discards 'const' from argument [346] */ + take_char_ptr(strrchr("string", 'c')); + /* expect+1: warning: call to 'strstr' effectively discards 'const' from argument [346] */ + take_char_ptr(strstr("string", "c")); +} + +void +edge_cases(void) +{ + /* No arguments, to cover the 'an == NULL' in is_first_arg_const. */ + /* expect+1: error: argument mismatch: 0 arg passed, 2 expected [150] */ + take_char_ptr(strchr()); +} diff --git a/usr.bin/xlint/lint1/msg_346.exp b/usr.bin/xlint/lint1/msg_346.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/msg_346.exp @@ -0,0 +1,9 @@ +msg_346.c(26): warning: call to 'strchr' effectively discards 'const' from argument [346] +msg_346.c(32): warning: call to 'strchr' effectively discards 'const' from argument [346] +msg_346.c(37): warning: call to 'strchr' effectively discards 'const' from argument [346] +msg_346.c(44): warning: call to 'memchr' effectively discards 'const' from argument [346] +msg_346.c(46): warning: call to 'strchr' effectively discards 'const' from argument [346] +msg_346.c(48): warning: call to 'strpbrk' effectively discards 'const' from argument [346] +msg_346.c(50): warning: call to 'strrchr' effectively discards 'const' from argument [346] +msg_346.c(52): warning: call to 'strstr' effectively discards 'const' from argument [346] +msg_346.c(60): error: argument mismatch: 0 arg passed, 2 expected [150] diff --git a/usr.bin/xlint/lint1/op_colon.c b/usr.bin/xlint/lint1/op_colon.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/op_colon.c @@ -0,0 +1,35 @@ +/* $NetBSD: op_colon.c,v 1.1 2021/04/02 17:25:04 rillig Exp $ */ +# 3 "op_colon.c" + +/* + * Test handling of the operator ':', as part of the '?:'. + */ + +/* lint1-extra-flags: -p */ + +void sink(void *); + +void +test_merge_qualifiers(_Bool cond, int *p, const int *c, volatile int *v, + const volatile int *cv) +{ + sink(cond ? p : p); + sink(cond ? p : c); /* expect: 'pointer to const int' */ + sink(cond ? p : v); /* expect: 'pointer to volatile int' */ + sink(cond ? p : cv); /* expect: 'pointer to const volatile int' */ + + sink(cond ? c : p); /* expect: 'pointer to const int' */ + sink(cond ? c : c); /* expect: 'pointer to const int' */ + sink(cond ? c : v); /* expect: 'pointer to const volatile int' */ + sink(cond ? c : cv); /* expect: 'pointer to const volatile int' */ + + sink(cond ? v : p); /* expect: 'pointer to volatile int' */ + sink(cond ? v : c); /* expect: 'pointer to const volatile int' */ + sink(cond ? v : v); /* expect: 'pointer to volatile int' */ + sink(cond ? v : cv); /* expect: 'pointer to const volatile int' */ + + sink(cond ? cv : p); /* expect: 'pointer to const volatile int' */ + sink(cond ? cv : c); /* expect: 'pointer to const volatile int' */ + sink(cond ? cv : v); /* expect: 'pointer to const volatile int' */ + sink(cond ? cv : cv); /* expect: 'pointer to const volatile int' */ +} diff --git a/usr.bin/xlint/lint1/op_colon.exp b/usr.bin/xlint/lint1/op_colon.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/op_colon.exp @@ -0,0 +1,15 @@ +op_colon.c(17): warning: converting 'pointer to const int' to incompatible 'pointer to void' for argument 1 [153] +op_colon.c(18): warning: converting 'pointer to volatile int' to incompatible 'pointer to void' for argument 1 [153] +op_colon.c(19): warning: converting 'pointer to const volatile int' to incompatible 'pointer to void' for argument 1 [153] +op_colon.c(21): warning: converting 'pointer to const int' to incompatible 'pointer to void' for argument 1 [153] +op_colon.c(22): warning: converting 'pointer to const int' to incompatible 'pointer to void' for argument 1 [153] +op_colon.c(23): warning: converting 'pointer to const volatile int' to incompatible 'pointer to void' for argument 1 [153] +op_colon.c(24): warning: converting 'pointer to const volatile int' to incompatible 'pointer to void' for argument 1 [153] +op_colon.c(26): warning: converting 'pointer to volatile int' to incompatible 'pointer to void' for argument 1 [153] +op_colon.c(27): warning: converting 'pointer to const volatile int' to incompatible 'pointer to void' for argument 1 [153] +op_colon.c(28): warning: converting 'pointer to volatile int' to incompatible 'pointer to void' for argument 1 [153] +op_colon.c(29): warning: converting 'pointer to const volatile int' to incompatible 'pointer to void' for argument 1 [153] +op_colon.c(31): warning: converting 'pointer to const volatile int' to incompatible 'pointer to void' for argument 1 [153] +op_colon.c(32): warning: converting 'pointer to const volatile int' to incompatible 'pointer to void' for argument 1 [153] +op_colon.c(33): warning: converting 'pointer to const volatile int' to incompatible 'pointer to void' for argument 1 [153] +op_colon.c(34): warning: converting 'pointer to const volatile int' to incompatible 'pointer to void' for argument 1 [153] diff --git a/usr.bin/xlint/lint1/op_shl_lp64.c b/usr.bin/xlint/lint1/op_shl_lp64.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/op_shl_lp64.c @@ -0,0 +1,25 @@ +/* $NetBSD: op_shl_lp64.c,v 1.3 2021/08/21 11:50:57 rillig Exp $ */ +# 3 "op_shl_lp64.c" + +/* + * Before decl.c 1.215 from 2021-07-31, lint wrongly treated __uint128_t and + * __int128_t as being equivalent to a missing type specifier, thereby + * defaulting to int. This led to warnings like: + * + * shift amount 105 is greater than bit-size 32 of 'int' [122] + * + * These warnings had been discovered in ecp_nistp256.c(296). + */ + +/* lint1-only-if: lp64 */ + +const __uint128_t zero105 = + (((__uint128_t)1) << 105) + - (((__uint128_t)1) << 41) + - (((__uint128_t)1) << 9); + +const __uint128_t shl_128_129 = + /* expect+1: warning: shift equal to size of object [267] */ + (((__uint128_t)1) << 128) + /* expect+1: warning: shift amount 129 is greater than bit-size 128 of '__uint128_t' [122] */ + - (((__uint128_t)1) << 129); diff --git a/usr.bin/xlint/lint1/op_shl_lp64.exp b/usr.bin/xlint/lint1/op_shl_lp64.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/op_shl_lp64.exp @@ -0,0 +1,2 @@ +op_shl_lp64.c(23): warning: shift equal to size of object [267] +op_shl_lp64.c(25): warning: shift amount 129 is greater than bit-size 128 of '__uint128_t' [122] diff --git a/usr.bin/xlint/lint1/parse_init_declarator.c b/usr.bin/xlint/lint1/parse_init_declarator.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/parse_init_declarator.c @@ -0,0 +1,33 @@ +/* $NetBSD: parse_init_declarator.c,v 1.3 2021/07/25 22:03:42 rillig Exp $ */ +# 3 "parse_init_declarator.c" + +/* + * Test parsing of init-declarator, which occurs as part of a top-level + * declaration. + * + * See also: GCC, c-parser.cpp, function c_parser_declaration_or_fndef. + */ + +int global_var; + +int *init_declarator_without_initializer + __asm("") __attribute__((deprecated)); + +/* XXX: GCC does not accept this, neither should lint. */ +int *init_declarator_without_initializer_wrong_order + __attribute__((deprecated)) __asm(""); + +int *init_declarator_with_initializer + __asm("") __attribute__((deprecated)) = &global_var; + +/* XXX: GCC does not accept this, neither should lint. */ +int *init_declarator_with_initializer_wrong_order + __attribute__((deprecated)) __asm("") = &global_var; + +/* The attributes may only occur before the initializer, not after it. */ +int *init_declarator_with_initializer_attribute_too_late + __asm("") = &global_var __attribute__((deprecated)); +/* expect-1: error: syntax error '__attribute__' [249] */ + +/* cover cgram_declare, freeyyv */ +int original __symbolrename(renamed); diff --git a/usr.bin/xlint/lint1/parse_init_declarator.exp b/usr.bin/xlint/lint1/parse_init_declarator.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/parse_init_declarator.exp @@ -0,0 +1 @@ +parse_init_declarator.c(29): error: syntax error '__attribute__' [249] diff --git a/usr.bin/xlint/lint1/parse_stmt_error.c b/usr.bin/xlint/lint1/parse_stmt_error.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/parse_stmt_error.c @@ -0,0 +1,26 @@ +/* $NetBSD: parse_stmt_error.c,v 1.1 2021/07/25 09:29:20 rillig Exp $ */ +# 3 "parse_stmt_error.c" + +/* + * Test parsing of errors in selection statements (if, switch). + */ + +void do_nothing(void); + +void +cover_selection_statement_else(_Bool cond) +{ + if (cond) + do_nothing(); + else + /* expect+1: syntax error ']' [249] */ + ]; +} + +void +cover_selection_statement_switch(int x) +{ + switch (x) + /* expect+1: syntax error ']' [249] */ + ]; +} diff --git a/usr.bin/xlint/lint1/parse_stmt_error.exp b/usr.bin/xlint/lint1/parse_stmt_error.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/parse_stmt_error.exp @@ -0,0 +1,2 @@ +parse_stmt_error.c(17): error: syntax error ']' [249] +parse_stmt_error.c(25): error: syntax error ']' [249] diff --git a/usr.bin/xlint/lint1/parse_stmt_iter_error.c b/usr.bin/xlint/lint1/parse_stmt_iter_error.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/parse_stmt_iter_error.c @@ -0,0 +1,32 @@ +/* $NetBSD: parse_stmt_iter_error.c,v 1.1 2021/07/25 09:29:20 rillig Exp $ */ +# 3 "parse_stmt_iter_error.c" + +/* + * Test parsing of errors in iteration statements (while, do, for). + */ + +void do_nothing(void); + +void +cover_iteration_statement_while(_Bool cond) +{ + while (cond) + /* expect+1: syntax error ']' [249] */ + ]; +} + +void +cover_iteration_statement_do(void) +{ + do + /* expect+1: syntax error ']' [249] */ + ]; +} + +void +cover_iteration_statement_for(void) +{ + for (int i = 0; i < 10; i++) + /* expect+1: syntax error ']' [249] */ + ]; +} diff --git a/usr.bin/xlint/lint1/parse_stmt_iter_error.exp b/usr.bin/xlint/lint1/parse_stmt_iter_error.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/parse_stmt_iter_error.exp @@ -0,0 +1,3 @@ +parse_stmt_iter_error.c(15): error: syntax error ']' [249] +parse_stmt_iter_error.c(23): error: syntax error ']' [249] +parse_stmt_iter_error.c(31): error: syntax error ']' [249] diff --git a/usr.bin/xlint/lint1/parse_type_name.c b/usr.bin/xlint/lint1/parse_type_name.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/parse_type_name.c @@ -0,0 +1,176 @@ +/* $NetBSD: parse_type_name.c,v 1.7 2021/07/25 22:03:42 rillig Exp $ */ +# 3 "parse_type_name.c" + +/* + * Test parsing of the grammar rule 'type_name', which among others appears + * in the expression 'sizeof(type_name)'. + */ + +void sink(unsigned long); + +void +cover_type_name(void) +{ + /* cover 'abstract_declaration' */ + sink(sizeof(int)); +} + +void +cover_abstract_declaration(void) +{ + /* cover 'qualifier_list' */ + /* missing type-specifier, even in traditional C */ + /* lint doesn't care since this is caught by the compiler */ + sink(sizeof(const)); + + /* cover 'specifier_qualifier_list' */ + sink(sizeof(double)); + + /* cover 'qualifier_list abstract_declarator' */ + /* XXX: This is nonsense, lint should not accept it. */ + sink(sizeof(const[3])); + + /* cover 'specifier_qualifier_list abstract_declarator' */ + sink(sizeof(const int[3])); + sink(sizeof(int const[3])); +} + +void +cover_abstract_declarator(void) +{ + /* cover 'pointer' */ + sink(sizeof(int ***)); + + /* cover 'direct_abstract_declarator' */ + sink(sizeof(int[3])); + + /* cover 'pointer direct_abstract_declarator' */ + sink(sizeof(int **[3])); + + /* cover 'T_TYPEOF cast_expression' */ + /* expect+1: error: cannot take size/alignment of function [144] */ + sink(sizeof(int(typeof(12345)))); +} + +void +cover_direct_abstract_declarator(void) +{ + /* cover 'T_LPAREN abstract_declarator T_RPAREN' */ + sink(sizeof(int (*))); + + /* cover 'T_LBRACK T_RBRACK' */ + sink(sizeof(int[])); + + /* cover 'T_LBRACK array_size T_RBRACK' */ + sink(sizeof(int[3])); + + /* cover 'type_attribute direct_abstract_declarator' */ + sink(sizeof(int *__attribute__(())[3])); + + /* cover 'direct_abstract_declarator T_LBRACK T_RBRACK' */ + /* expect+1: error: null dimension [17] */ + sink(sizeof(int[3][])); + + /* cover 'direct_abstract_declarator T_LBRACK T_ASTERISK T_RBRACK' */ + /* expect+1: error: null dimension [17] */ + sink(sizeof(int[3][ *])); + + /* cover 'direct_abstract_declarator T_LBRACK array_size T_RBRACK' */ + sink(sizeof(int[3][5][8])); + + /* cover 'abstract_decl_param_list asm_or_symbolrename_opt' */ + sink(sizeof(int(double))); + sink(sizeof(int(double) __asm("anything"))); + sink(sizeof(int(double) __symbolrename(alias))); + + /* cover 'direct_abstract_declarator abstract_decl_param_list asm_or_symbolrename_opt' */ + sink(sizeof(int (*)(double))); + sink(sizeof(int (*)(double) __asm("anything"))); + sink(sizeof(int (*)(double)__symbolrename(alias))); + + /* cover 'direct_abstract_declarator type_attribute_list' */ + sink(sizeof(int (*) __attribute__(()))); + sink(sizeof(int (*) __attribute__(()) __attribute__(()))); +} + +void +cover_abstract_decl_param_list(void) +{ + /* cover 'abstract_decl_lparen T_RPAREN type_attribute_opt' */ + sink(sizeof(void (*)())); + sink(sizeof(void (*)() __attribute__(()))); + /* + * XXX: The grammar allows only a single type_attribute_opt. + * All following __attribute__ come from direct_abstract_declarator. + */ + sink(sizeof(void (*)() __attribute__(()) __attribute__(()))); + + /* cover 'abstract_decl_lparen vararg_parameter_type_list T_RPAREN type_attribute_opt' */ + sink(sizeof(void (*)(void) __attribute__(()))); + /* + * XXX: The grammar allows only a single type_attribute_opt. + * All following __attribute__ come from direct_abstract_declarator. + */ + sink(sizeof(void (*)(void) __attribute__(()) __attribute__(()))); + + /* cover 'abstract_decl_lparen error T_RPAREN type_attribute_opt' */ + /* expect+1: syntax error 'goto' [249] */ + sink(sizeof(void (*)(goto))); + /* expect+1: syntax error 'goto' [249] */ + sink(sizeof(void (*)(goto) __attribute__(()))); + /* + * XXX: The grammar allows only a single type_attribute_opt. + * All following __attribute__ come from direct_abstract_declarator. + */ + /* expect+1: syntax error 'goto' [249] */ + sink(sizeof(void (*)(goto) __attribute__(()) __attribute__(()))); +} + +void +cover_vararg_parameter_type_list(void) +{ + /* cover 'parameter_type_list' */ + sink(sizeof(void (*)(double))); + + /* cover 'parameter_type_list T_COMMA T_ELLIPSIS' */ + sink(sizeof(void (*)(double, ...))); + + /* cover 'T_ELLIPSIS' */ + /* expect+1: warning: ANSI C requires formal parameter before '...' [84] */ + sink(sizeof(void (*)(...))); +} + +void +cover_parameter_type_list(void) +{ + /* cover 'parameter_declaration' */ + sink(sizeof(void (*)(double))); + + /* cover 'parameter_type_list T_COMMA parameter_declaration' */ + sink(sizeof(void (*)(double, double, double, char *))); +} + +void +cover_parameter_declaration(void) +{ + /* cover 'declmods' */ + /* GCC 11 warns: type defaults to 'int' in type name */ + sink(sizeof(void (*)(int, const))); + + /* cover 'declaration_specifiers' */ + sink(sizeof(void (*)(int, double))); + + /* cover 'declmods notype_param_declarator' */ + /* GCC 11 warns: type defaults to 'int' in declaration of 'x' */ + sink(sizeof(void (*)(int, const x))); + + /* cover 'begin_type_declaration_specifiers end_type type_param_declarator' */ + sink(sizeof(void (*)(int, double x))); + + /* cover 'begin_type_declmods end_type abstract_declarator' */ + /* GCC 11 warns: type defaults to 'int' in type name */ + sink(sizeof(void (*)(int, const *))); + + /* cover 'begin_type_declaration_specifiers end_type abstract_declarator' */ + sink(sizeof(void (*)(int, double *))); +} diff --git a/usr.bin/xlint/lint1/parse_type_name.exp b/usr.bin/xlint/lint1/parse_type_name.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/parse_type_name.exp @@ -0,0 +1,7 @@ +parse_type_name.c(52): error: cannot take size/alignment of function [144] +parse_type_name.c(72): error: null dimension [17] +parse_type_name.c(76): error: null dimension [17] +parse_type_name.c(118): error: syntax error 'goto' [249] +parse_type_name.c(120): error: syntax error 'goto' [249] +parse_type_name.c(126): error: syntax error 'goto' [249] +parse_type_name.c(140): warning: ANSI C requires formal parameter before '...' [84] diff --git a/usr.bin/xlint/lint1/stmt_for.c b/usr.bin/xlint/lint1/stmt_for.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/stmt_for.c @@ -0,0 +1,16 @@ +/* $NetBSD: stmt_for.c,v 1.1 2021/06/19 19:59:02 rillig Exp $ */ +# 3 "stmt_for.c" + +/* + * Before func.c 1.111 from 2021-06-19, lint ran into an assertion failure: + * + * "dcs->d_next == NULL" failed in funcend at func.c:422 + */ + +void +test(void) +{ + for (0 0; /* expect: syntax error '0' */ +} + +/* expect+1: cannot recover from previous errors */ diff --git a/usr.bin/xlint/lint1/stmt_for.exp b/usr.bin/xlint/lint1/stmt_for.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/stmt_for.exp @@ -0,0 +1,2 @@ +stmt_for.c(13): error: syntax error '0' [249] +stmt_for.c(17): error: cannot recover from previous errors [224] diff --git a/usr.bin/xlint/lint1/stmt_goto.c b/usr.bin/xlint/lint1/stmt_goto.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/stmt_goto.c @@ -0,0 +1,26 @@ +/* $NetBSD: stmt_goto.c,v 1.1 2021/07/14 20:39:13 rillig Exp $ */ +# 3 "stmt_goto.c" + +/* + * Tests for the 'goto' statement. + */ + +/* expect+1: syntax error 'goto' [249] */ +goto invalid_at_top_level; + +void +function(void) +{ + goto label; +label: + /* expect+1: syntax error '"' [249] */ + goto "string"; + + /* Reset the error handling of the parser. */ + goto ok; +ok: + + /* Numeric labels work in Pascal, but not in C. */ + /* expect+1: syntax error '12345' [249] */ + goto 12345; +} diff --git a/usr.bin/xlint/lint1/stmt_goto.exp b/usr.bin/xlint/lint1/stmt_goto.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/stmt_goto.exp @@ -0,0 +1,3 @@ +stmt_goto.c(9): error: syntax error 'goto' [249] +stmt_goto.c(17): error: syntax error '"' [249] +stmt_goto.c(25): error: syntax error '12345' [249] diff --git a/usr.bin/xlint/lint1/stmt_if.c b/usr.bin/xlint/lint1/stmt_if.c new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/stmt_if.c @@ -0,0 +1,28 @@ +/* $NetBSD: stmt_if.c,v 1.1 2021/07/11 18:58:13 rillig Exp $ */ +# 3 "stmt_if.c" + +/* + * Test parsing of 'if' statements. + */ + +void println(const char *); + +void +dangling_else(int x) +{ + if (x > 0) + if (x > 10) + println("> 10"); + /* This 'else' is bound to the closest unfinished 'if'. */ + else + println("> 0"); + /* + * If the above 'else' were bound to the other 'if', the next 'else' + * would have no corresponding 'if', resulting in a syntax error. + */ + else + println("not positive"); + /* expect+1: syntax error 'else' [249] */ + else + println("syntax error"); +} diff --git a/usr.bin/xlint/lint1/stmt_if.exp b/usr.bin/xlint/lint1/stmt_if.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint1/stmt_if.exp @@ -0,0 +1 @@ +stmt_if.c(26): error: syntax error 'else' [249] diff --git a/usr.bin/xlint/lint1/t_integration.sh b/usr.bin/xlint/lint1/t_integration.sh old mode 100755 new mode 100644 --- a/usr.bin/xlint/lint1/t_integration.sh +++ b/usr.bin/xlint/lint1/t_integration.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_integration.sh,v 1.4 2014/04/21 19:10:41 christos Exp $ +# $NetBSD: t_integration.sh,v 1.69 2021/08/21 11:50:57 rillig Exp $ # # Copyright (c) 2008, 2010 The NetBSD Foundation, Inc. # All rights reserved. @@ -25,108 +25,140 @@ # POSSIBILITY OF SUCH DAMAGE. # -LINT1=/usr/libexec/lint1 +lint1=/usr/libexec/lint1 +: "${archsubdir:=archsubdir_must_be_set}" -Names= -check_valid() +configure_test_case() { - atf_check -s exit:0 ${LINT1} -g -S "$(atf_get_srcdir)/$1" /dev/null + local awk + + # shellcheck disable=SC2016 + awk=' + BEGIN { + # see usr.bin/xlint/arch/*/targparam.h + platform["aarch64"] = "schar lp64 long ldbl-128" + platform["alpha"] = "schar lp64 long ldbl-64" + platform["arm"] = "uchar ilp32 long ldbl-64" + platform["coldfire"] = "schar ilp32 int ldbl-64" + platform["hppa"] = "schar ilp32 long ldbl-64" + platform["i386"] = "schar ilp32 int ldbl-96" + platform["ia64"] = "schar lp64 long ldbl-128" + platform["m68000"] = "schar ilp32 int ldbl-64" + platform["m68k"] = "schar ilp32 int ldbl-96" + platform["mips"] = "schar ilp32 ???? ldbl-64" + platform["mips64"] = "schar ilp32 long ldbl-128" + platform["mipsn64"] = "schar lp64 long ldbl-128" + platform["or1k"] = "schar ilp32 int ldbl-64" + platform["powerpc"] = "uchar ilp32 int ldbl-64" + platform["powerpc64"] = "uchar lp64 long ldbl-64" + platform["powerpc64"] = "uchar lp64 long ldbl-64" + platform["riscv32"] = "schar ilp32 int ldbl-64" + platform["riscv64"] = "schar lp64 long ldbl-64" + platform["sh3"] = "schar ilp32 int ldbl-64" + platform["sparc"] = "schar ilp32 long ldbl-64" + platform["sparc64"] = "schar lp64 long ldbl-128" + platform["vax"] = "schar ilp32 long ldbl-64" + platform["x86_64"] = "schar lp64 long ldbl-128" + } + + function platform_has(prop) { + if (!match(prop, /^(schar|uchar|ilp32|lp64|int|long|ldbl-64|ldbl-96|ldbl-128)$/)) { + printf("bad property '\''%s'\''\n", prop) > "/dev/stderr" + exit(1) + } + if (platform[archsubdir] == "") { + printf("bad archsubdir '\''%s'\''\n", archsubdir) > "/dev/stderr" + exit(1) + } + return match(" " platform[archsubdir] " ", " " prop " ") + } + + BEGIN { + archsubdir = "'"$archsubdir"'" + flags = "-g -S -w" + skip = "no" + } + $1 == "/*" && $2 ~ /^lint1-/ && $NF == "*/" { + if ($2 == "lint1-flags:" || $2 == "lint1-extra-flags:") { + if ($2 == "lint1-flags:") + flags = "" + for (i = 3; i < NF; i++) + flags = flags " " $i + } else if ($2 == "lint1-only-if:") { + if (!platform_has($3)) + skip = "yes" + } else if ($2 == "lint1-skip-if:") { + if (platform_has($3)) + skip = "yes" + } else { + printf("bad lint1 comment '\''%s'\''\n", $2) > "/dev/stderr" + exit(1) + } + } + + END { + printf("flags='\''%s'\''\n", flags) + printf("skip=%s\n", skip) + } + ' + + local config + config="$(awk "$awk" "$1")" || exit 1 + eval "$config" } -check_invalid() +# shellcheck disable=SC2155 +check_lint1() { - atf_check -s not-exit:0 -o ignore -e ignore ${LINT1} -g -S -w \ - "$(atf_get_srcdir)/$1" /dev/null + local src="$(atf_get_srcdir)/$1" + local exp="${src%.c}.exp" + local exp_ln="${src%.c}.exp-ln" + local wrk_ln="${1%.c}.ln" + local flags="" + local skip="" + + if [ ! -f "$exp_ln" ]; then + exp_ln='/dev/null' + wrk_ln='/dev/null' + fi + + configure_test_case "$src" # sets 'skip' and 'flags' + + if [ "$skip" = "yes" ]; then + atf_skip "unsuitable platform" + fi + + if [ -f "$exp" ]; then + # shellcheck disable=SC2086 + atf_check -s not-exit:0 -o "file:$exp" -e empty \ + "$lint1" $flags "$src" "$wrk_ln" + else + # shellcheck disable=SC2086 + atf_check -s exit:0 \ + "$lint1" $flags "$src" "$wrk_ln" + fi + + if [ "$exp_ln" != '/dev/null' ]; then + atf_check -o "file:$exp_ln" cat "$wrk_ln" + fi } -test_case() -{ - local result="${1}"; shift - local name="${1}"; shift - local descr="${*}" - - atf_test_case ${name} - eval "${name}_head() { - atf_set \"descr\" \"${descr}\"; - atf_set \"require.progs\" \"${LINT1}\"; - }" - eval "${name}_body() { - ${result} d_${name}.c; - }" - - Names="${Names} ${name}" -} - -test_case check_valid c99_struct_init "Checks C99 struct initialization" -test_case check_valid c99_union_init1 "Checks C99 union initialization" -test_case check_valid c99_union_init2 "Checks C99 union initialization" -test_case check_valid c99_union_init3 "Checks C99 union initialization" -test_case check_valid c99_recursive_init "Checks C99 recursive struct/union" \ - "initialization" -test_case check_valid c9x_recursive_init "Checks C9X struct/union member" \ - "init, with nested union and trailing member" -test_case check_valid nested_structs "Checks nested structs" -test_case check_valid packed_structs "Checks packed structs" - -test_case check_valid cast_init "Checks cast initialization" -test_case check_valid cast_init2 "Checks cast initialization as the rhs of a" \ - "- operand" -test_case check_valid cast_lhs "Checks whether pointer casts are valid lhs" \ - "lvalues" - -test_case check_valid gcc_func "Checks GCC __FUNCTION__" -test_case check_valid c99_func "Checks C99 __func__" - -test_case check_valid gcc_variable_array_init "Checks GCC variable array" \ - "initializers" -test_case check_valid c9x_array_init "Checks C9X array initializers" -test_case check_valid c99_decls_after_stmt "Checks C99 decls after statements" -test_case check_valid c99_decls_after_stmt3 "Checks C99 decls after statements" -test_case check_valid nolimit_init "Checks no limit initializers" -test_case check_valid zero_sized_arrays "Checks zero sized arrays" - -test_case check_valid compound_literals1 "Checks compound literals" -test_case check_valid compound_literals2 "Checks compound literals" -test_case check_valid gcc_compound_statements1 "Checks GCC compound statements" -test_case check_valid gcc_compound_statements2 "Checks GCC compound" \ - "statements with non-expressions" -test_case check_valid gcc_compound_statements3 "Checks GCC compound" \ - "statements with void type" -# XXX: Because of polymorphic __builtin_isnan and expression has null effect -# test_case check_valid gcc_extension "Checks GCC __extension__ and __typeof__" - -test_case check_valid cvt_in_ternary "Checks CVT nodes handling in ?" \ -test_case check_valid cvt_constant "Checks constant conversion" -test_case check_valid ellipsis_in_switch "Checks ellipsis in switch()" -test_case check_valid c99_complex_num "Checks C99 complex numbers" -test_case check_valid c99_complex_split "Checks C99 complex access" -test_case check_valid c99_for_loops "Checks C99 for loops" -test_case check_valid alignof "Checks __alignof__" -test_case check_valid shift_to_narrower_type "Checks that type shifts that" \ - "result in narrower types do not produce warnings" - -test_case check_invalid constant_conv1 "Checks failing on information-losing" \ - "constant conversion in argument lists" -test_case check_invalid constant_conv2 "Checks failing on information-losing" \ - "constant conversion in argument lists" - -test_case check_invalid type_conv1 "Checks failing on information-losing" \ - "type conversion in argument lists" -test_case check_invalid type_conv2 "Checks failing on information-losing" \ - "type conversion in argument lists" -test_case check_invalid type_conv3 "Checks failing on information-losing" \ - "type conversion in argument lists" - -test_case check_invalid incorrect_array_size "Checks failing on incorrect" \ - "array sizes" - -test_case check_invalid long_double_int "Checks for confusion of 'long" \ - "double' with 'long int'; PR 39639" - atf_init_test_cases() { - for name in ${Names}; do - atf_add_test_case ${name} + local src name + + for src in "$(atf_get_srcdir)"/*.c; do + src=${src##*/} + name=${src%.c} + + atf_test_case "$name" + eval "${name}_head() { + atf_set 'require.progs' '$lint1' + }" + eval "${name}_body() { + check_lint1 '$name.c' + }" + atf_add_test_case "$name" done } diff --git a/usr.bin/xlint/lint2/Makefile b/usr.bin/xlint/lint2/Makefile new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint2/Makefile @@ -0,0 +1,43 @@ +# $NetBSD: Makefile,v 1.8 2021/08/28 19:45:18 rillig Exp $ + +NOMAN= yes + +.include + +TESTSDIR= ${TESTSBASE}/usr.bin/xlint/lint2 + +TESTS_SH= t_lint2 + +FILESDIR= ${TESTSDIR} + +TESTS+= emit +TESTS+= emit_lp64 +.for msg in \ + 000 001 002 003 004 005 006 007 008 009 \ + 010 011 012 013 014 015 016 017 018 +TESTS+= msg_${msg} +.endfor +TESTS+= output_sorted +TESTS+= read +TESTS+= read_lp64 +TESTS+= read_printf + +FILES+= ${TESTS:=.ln} +FILES+= ${TESTS:Nemit*:=.exp} +FILES+= ${TESTS:Memit*:=.exp-ln} + +# Note: only works for adding tests. +# To remove a test, the $$mi file must be edited manually. +sync-mi: .PHONY + @set -eu; \ + cd "${MAKEFILE:tA:H}/../../../.."; \ + mi="distrib/sets/lists/tests/mi"; \ + cvs update "$$mi"; \ + fmt="./usr/tests/usr.bin/xlint/lint2/%s\ttests-usr.bin-tests\tcompattestfile,atf\n"; \ + cat "$$mi" > "$$mi.tmp"; \ + printf "$$fmt" ${FILES} >> "$$mi.tmp"; \ + distrib/sets/fmt-list "$$mi.tmp"; \ + mv "$$mi.tmp" "$$mi"; \ + cvs diff "$$mi" || true + +.include diff --git a/usr.bin/xlint/lint2/emit.exp-ln b/usr.bin/xlint/lint2/emit.exp-ln new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint2/emit.exp-ln @@ -0,0 +1,21 @@ +# $NetBSD: emit.exp-ln,v 1.3 2021/08/28 17:18:42 rillig Exp $ + +S llib-lemit.ln +0 s llib-lemit.ln + +# Normally, it does not matter in which filename a particular symbol is +# defined. Only in cases where a file defines an anonymous tag type (struct, +# union, enum), its name is included in the resulting library. In this +# particular case, it would not be necessary though since none of the exported +# symbols actually refers to one of these anonymous types. +1s emit.c +2s expr_promote.c + +# Since emit2.c 1.22 from 2021-08-28, the symbols are written in alphabetic +# order. + +# from expr_promote.c +0 d 0.0 d u 6caller F1 PsT116arithmetic_types V +# from emit.c +0 d 0.0 d u 14cover_outqchar F0 V +0 d 0.0 t u 11defined_int I diff --git a/usr.bin/xlint/lint2/emit.ln b/usr.bin/xlint/lint2/emit.ln new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint2/emit.ln @@ -0,0 +1,41 @@ +# $NetBSD: emit.ln,v 1.2 2021/08/24 23:38:51 rillig Exp $ +# +# Test emitting a lint library file. + +# Extracted from ../lint1/emit.exp-ln. +0 s emit.c +S emit.c + +# Global variables that are declared using 'extern' are not part of a +# library's interface, therefore they are omitted from the output. +101 d 0.101 e 12extern__Bool B + +# Global variables that are defined, not only declared, are part of a +# library's interface. +106 d 0.106 t 11defined_int I + +# Referring to an anonymous tagged type forces the source file to be listed as +# part of the library. If it weren't listed, the diagnostics from lint2 were +# not able to refer to the location where this type has been defined. +97 d 0.97 e 21extern_anonymous_enum eT395.0.0 + +# Function declarations, as opposed to function definitions, are not part of a +# library's interface, therefore they are omitted from the output. +121 d 0.121 e 30return_void_unknown_parameters F V +122 d 0.122 e 38return_implicit_int_unknown_parameters F I +125 d 0.125 e 32extern_return_void_no_parameters F0 V + +# Function calls are not part of a library's interface, therefore they are +# omitted from the output. +161 c 0.161 s2"%" i 9my_printf f2 PcC PC V + +# Function definitions are copied to the output. +159 d 0.159 d 14cover_outqchar F0 V + +# Taken from ../lint1/expr_promote.exp-ln. +0 s expr_promote.c +S expr_promote.c + +10 d 0.10 e 4sink F2 PcC E V +58 c 0.58 i 4sink f20 PcC B I I I I I I uI L uL Q uQ D D lD sX X lX eT331.0.0 V +37 d 0.37 d 6caller F1 PsT116arithmetic_types V diff --git a/usr.bin/xlint/lint2/emit_lp64.exp-ln b/usr.bin/xlint/lint2/emit_lp64.exp-ln new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint2/emit_lp64.exp-ln @@ -0,0 +1,7 @@ +# $NetBSD: emit_lp64.exp-ln,v 1.3 2021/08/28 17:18:42 rillig Exp $ + +S llib-lemit_lp64.ln +0 s llib-lemit_lp64.ln + +0 d 0.0 tu 15int128_variable J +0 d 0.0 du 16uint128_function F0 uJ diff --git a/usr.bin/xlint/lint2/emit_lp64.ln b/usr.bin/xlint/lint2/emit_lp64.ln new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint2/emit_lp64.ln @@ -0,0 +1,11 @@ +# $NetBSD: emit_lp64.ln,v 1.2 2021/08/24 23:38:51 rillig Exp $ +# +# Test emitting a lint library file on an LP64 platform. + +0 s first-file.c +S first-file.c +10 d 0.10 t 15int128_variable J + +0 s second-file.c +S second-file.c +20 d 0.20 d 16uint128_function F0 uJ diff --git a/usr.bin/xlint/lint2/msg_000.exp b/usr.bin/xlint/lint2/msg_000.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint2/msg_000.exp @@ -0,0 +1,2 @@ +defined_not_used defined( msg_000.c(2) ), but never used +used_not_defined used( msg_000.c(3) ), but not defined diff --git a/usr.bin/xlint/lint2/msg_000.ln b/usr.bin/xlint/lint2/msg_000.ln new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint2/msg_000.ln @@ -0,0 +1,42 @@ +# $NetBSD: msg_000.ln,v 1.2 2021/08/24 23:38:51 rillig Exp $ +# +# Test data for message 0 of lint2: +# %s used( %s ), but not defined +# + +# Define file number 0. +0 s msg_000.c + +# Define the main file of the translation unit. +S msg_000.c + +# Have a function that is used but not defined, and a function that is defined +# but not used, generated by this code: +# 1: # 2 "msg_000.c" +# 2: defined_not_used() { +# 3: used_not_defined(1, 0.0); +# 4: } +# +# The function call in line 3 generates this entry: +# '3' logical line 3 in the main .c source +# 'c' function call +# '0.3' file 0, logical line 3 in the currently included file +# 'p1' the first argument is a positive constant +# 'i' the return value of the function call is ignored +# '16...' the name of the called function +# 'f2' it's a function with 2 arguments +# 'I' the first argument has type 'int' +# 'D' the first argument has type 'double' +# 'I' the return type of the function is (implicitly) 'int' +3 c 0.3 p1 i 16used_not_defined f2 I D I +# +# The function definition in line 2 generates this entry: +# '2' logical line 2 in the main .c source +# 'd' function definition +# '0.2' file 0, logical line 2 in the currently included file +# 'd' function definition +# 'o' old-style function definition +# '16...' the name of the defined function +# 'f0' it's a function with 0 arguments +# 'I' the return type of the function is (implicitly) 'int' +2 d 0.2 d o 16defined_not_used f0 I diff --git a/usr.bin/xlint/lint2/msg_001.exp b/usr.bin/xlint/lint2/msg_001.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint2/msg_001.exp @@ -0,0 +1,2 @@ +defined_not_used defined( msg_001.c(2) ), but never used +used_not_defined used( msg_001.c(3) ), but not defined diff --git a/usr.bin/xlint/lint2/msg_001.ln b/usr.bin/xlint/lint2/msg_001.ln new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint2/msg_001.ln @@ -0,0 +1,25 @@ +# $NetBSD: msg_001.ln,v 1.4 2021/08/24 23:38:51 rillig Exp $ +# +# Test data for message 1 of lint2: +# %s defined( %s ), but never used +# + +0 s msg_001.c +S msg_001.c + +3 c 0.3 p1 i 16used_not_defined f2 I D I +2 d 0.2 d o 16defined_not_used f0 I + +# The function 'main' always counts as used. +# int main(void) {} +10 d 0.10 d 4main F0 I +# extern main() {} +12 d 0.12 e 4main F I + +# If a function is declared once in old style and once with prototype, +# the prototype definition is preferred; see chkname. +# +# extern merge_old_style_and_prototype(); +20 d 0.20 e 29merge_old_style_and_prototype F I +# extern int merge_old_style_and_prototype(char *); +21 d 0.21 e 29merge_old_style_and_prototype F1 PCI diff --git a/usr.bin/xlint/lint2/msg_002.exp b/usr.bin/xlint/lint2/msg_002.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint2/msg_002.exp @@ -0,0 +1,2 @@ +defined defined( msg_002.c(3) ), but never used +only_declared declared( msg_002.c(2) ), but never used or defined diff --git a/usr.bin/xlint/lint2/msg_002.ln b/usr.bin/xlint/lint2/msg_002.ln new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint2/msg_002.ln @@ -0,0 +1,14 @@ +# $NetBSD: msg_002.ln,v 1.3 2021/08/24 23:38:51 rillig Exp $ +# +# Test data for message 2 of lint2: +# %s declared( %s ), but never used or defined +# + +0 s msg_002.c +S msg_002.c + +# extern int only_declared; +2 d 0.2 e 13only_declared I + +# int defined; +3 d 0.3 t 7defined I diff --git a/usr.bin/xlint/lint2/msg_003.exp b/usr.bin/xlint/lint2/msg_003.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint2/msg_003.exp @@ -0,0 +1,2 @@ +function defined( msg_003_a.c(10) ), but never used +function multiply defined msg_003_a.c(10) :: msg_003_b.c(10) diff --git a/usr.bin/xlint/lint2/msg_003.ln b/usr.bin/xlint/lint2/msg_003.ln new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint2/msg_003.ln @@ -0,0 +1,14 @@ +# $NetBSD: msg_003.ln,v 1.3 2021/08/24 23:38:51 rillig Exp $ +# +# Test data for message 3 of lint2: +# %s multiply defined %s :: %s + +0 s msg_003_a.c +S msg_003_a.c +# msg_003_a.c:10: int function(void); +10 d 0.10 d 8function F0 I + +0 s msg_003_b.c +S msg_003_b.c +# msg_003_b.c:10: int function(void); +10 d 0.10 d 8function F0 I diff --git a/usr.bin/xlint/lint2/msg_004.exp b/usr.bin/xlint/lint2/msg_004.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint2/msg_004.exp @@ -0,0 +1,6 @@ +add value used inconsistently msg_004_define.c(2) :: msg_004_call.c(4) +add value declared inconsistently (double != int) msg_004_define.c(2) :: msg_004_call.c(2) +add, arg 1 used inconsistently msg_004_define.c(2)[double] :: msg_004_call.c(4)[int] +add, arg 2 used inconsistently msg_004_define.c(2)[double] :: msg_004_call.c(4)[int] +add, arg 1 declared inconsistently (double != int) msg_004_define.c(2) :: msg_004_call.c(2) +add, arg 2 declared inconsistently (double != int) msg_004_define.c(2) :: msg_004_call.c(2) diff --git a/usr.bin/xlint/lint2/msg_004.ln b/usr.bin/xlint/lint2/msg_004.ln new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint2/msg_004.ln @@ -0,0 +1,17 @@ +# $NetBSD: msg_004.ln,v 1.3 2021/08/24 23:38:51 rillig Exp $ +# +# Test data for message 4 of lint2: +# %s value used inconsistently %s :: %s + +# msg_004_define.c:2: double add(double a, double b) { return a + b; } +0 s msg_004_define.c +S msg_004_define.c +2 d 0.2 d r 3add F2 D D D + +# msg_004_call.c:2: int add(int, int); +# msg_004_call.c:4: int main(void) { return add(2, 3); } +0 s msg_004_call.c +S msg_004_call.c +2 d 0.2 e 3add F2 I I I +4 c 0.4 p1 p2 u 3add f2 I I I +4 d 0.4 d r 4main F0 I diff --git a/usr.bin/xlint/lint2/msg_005.exp b/usr.bin/xlint/lint2/msg_005.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint2/msg_005.exp @@ -0,0 +1,6 @@ +add value used inconsistently msg_005_define.c(2) :: msg_005_call.c(4) +add value declared inconsistently (double != int) msg_005_define.c(2) :: msg_005_call.c(2) +add, arg 1 used inconsistently msg_005_define.c(2)[double] :: msg_005_call.c(4)[int] +add, arg 2 used inconsistently msg_005_define.c(2)[double] :: msg_005_call.c(4)[int] +add, arg 1 declared inconsistently (double != int) msg_005_define.c(2) :: msg_005_call.c(2) +add, arg 2 declared inconsistently (double != int) msg_005_define.c(2) :: msg_005_call.c(2) diff --git a/usr.bin/xlint/lint2/msg_005.ln b/usr.bin/xlint/lint2/msg_005.ln new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint2/msg_005.ln @@ -0,0 +1,17 @@ +# $NetBSD: msg_005.ln,v 1.3 2021/08/24 23:38:51 rillig Exp $ +# +# Test data for message 5 of lint2: +# %s value declared inconsistently (%s != %s) %s :: %s + +# msg_005_define.c:2: double add(double a, double b) { return a + b; } +0 s msg_005_define.c +S msg_005_define.c +2 d 0.2 d r 3add F2 D D D + +# msg_005_call.c:2: int add(int, int); +# msg_005_call.c:4: int main(void) { return add(2, 3); } +0 s msg_005_call.c +S msg_005_call.c +2 d 0.2 e 3add F2 I I I +4 c 0.4 p1 p2 u 3add f2 I I I +4 d 0.4 d r 4main F0 I diff --git a/usr.bin/xlint/lint2/msg_006.exp b/usr.bin/xlint/lint2/msg_006.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint2/msg_006.exp @@ -0,0 +1,6 @@ +add value used inconsistently msg_006_define.c(2) :: msg_006_call.c(4) +add value declared inconsistently (double != int) msg_006_define.c(2) :: msg_006_call.c(2) +add, arg 1 used inconsistently msg_006_define.c(2)[double] :: msg_006_call.c(4)[int] +add, arg 2 used inconsistently msg_006_define.c(2)[double] :: msg_006_call.c(4)[int] +add, arg 1 declared inconsistently (double != int) msg_006_define.c(2) :: msg_006_call.c(2) +add, arg 2 declared inconsistently (double != int) msg_006_define.c(2) :: msg_006_call.c(2) diff --git a/usr.bin/xlint/lint2/msg_006.ln b/usr.bin/xlint/lint2/msg_006.ln new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint2/msg_006.ln @@ -0,0 +1,17 @@ +# $NetBSD: msg_006.ln,v 1.3 2021/08/24 23:38:51 rillig Exp $ +# +# Test data for message 6 of lint2: +# %s, arg %d used inconsistently %s[%s] :: %s[%s] + +# msg_006_define.c:2: double add(double a, double b) { return a + b; } +0 s msg_006_define.c +S msg_006_define.c +2 d 0.2 d r 3add F2 D D D + +# msg_006_call.c:2: int add(int, int); +# msg_006_call.c:4: int main(void) { return add(2, 3); } +0 s msg_006_call.c +S msg_006_call.c +2 d 0.2 e 3add F2 I I I +4 c 0.4 p1 p2 u 3add f2 I I I +4 d 0.4 d r 4main F0 I diff --git a/usr.bin/xlint/lint2/msg_007.exp b/usr.bin/xlint/lint2/msg_007.exp new file mode 100644 diff --git a/usr.bin/xlint/lint2/msg_007.ln b/usr.bin/xlint/lint2/msg_007.ln new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint2/msg_007.ln @@ -0,0 +1,6 @@ +# $NetBSD: msg_007.ln,v 1.1 2021/08/05 22:36:08 rillig Exp $ +# +# Test data for message 7 of lint2: +# %s: variable # of args %s :: %s +# +# TODO: add actual test data diff --git a/usr.bin/xlint/lint2/msg_008.exp b/usr.bin/xlint/lint2/msg_008.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint2/msg_008.exp @@ -0,0 +1 @@ +func returns value which is always ignored diff --git a/usr.bin/xlint/lint2/msg_008.ln b/usr.bin/xlint/lint2/msg_008.ln new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint2/msg_008.ln @@ -0,0 +1,16 @@ +# $NetBSD: msg_008.ln,v 1.3 2021/08/24 23:38:51 rillig Exp $ +# +# Test data for message 8 of lint2: +# %s returns value which is always ignored +# + +0 s msg_008.c +S msg_008.c + +# 100: bool func(void) { return true } +100 d 0.100 drs 4func F0 B + +# 110: func(); +110 c 0.110 i 4func f0 B +# 111: func(); +111 c 0.111 i 4func f0 B diff --git a/usr.bin/xlint/lint2/msg_009.exp b/usr.bin/xlint/lint2/msg_009.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint2/msg_009.exp @@ -0,0 +1 @@ +func returns value which is sometimes ignored diff --git a/usr.bin/xlint/lint2/msg_009.ln b/usr.bin/xlint/lint2/msg_009.ln new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint2/msg_009.ln @@ -0,0 +1,16 @@ +# $NetBSD: msg_009.ln,v 1.3 2021/08/24 23:38:51 rillig Exp $ +# +# Test data for message 9 of lint2: +# %s returns value which is sometimes ignored +# + +0 s msg_009.c +S msg_009.c + +# 100: bool func(void) { return true } +100 d 0.100 drs 4func F0 B + +# 110: var = func(); +110 c 0.110 u 4func f0 B +# 111: func(); +111 c 0.111 i 4func f0 B diff --git a/usr.bin/xlint/lint2/msg_010.exp b/usr.bin/xlint/lint2/msg_010.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint2/msg_010.exp @@ -0,0 +1 @@ +func value is used( msg_010.c?(30) ), but none returned diff --git a/usr.bin/xlint/lint2/msg_010.ln b/usr.bin/xlint/lint2/msg_010.ln new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint2/msg_010.ln @@ -0,0 +1,17 @@ +# $NetBSD: msg_010.ln,v 1.3 2021/08/24 23:38:51 rillig Exp $ +# +# Test data for message 10 of lint2: +# %s value is used( %s ), but none returned +# + +0 s msg_010.c +S msg_010.c +1 s msg_010_use.c + +# msg_010.c:10: func() {} +10 d 0.10 d o 4func f0I + +# msg_010_use.c:20: func(); +20 d 1.20 e 4func F I +# msg_010_use.c:30: use() { return func(); } +30 c 1.30 u 4func f0 I diff --git a/usr.bin/xlint/lint2/msg_011.exp b/usr.bin/xlint/lint2/msg_011.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint2/msg_011.exp @@ -0,0 +1,6 @@ +add value used inconsistently msg_011_define.c(2) :: msg_011_call.c(4) +add value declared inconsistently (double != int) msg_011_define.c(2) :: msg_011_call.c(2) +add, arg 1 used inconsistently msg_011_define.c(2)[double] :: msg_011_call.c(4)[int] +add, arg 2 used inconsistently msg_011_define.c(2)[double] :: msg_011_call.c(4)[int] +add, arg 1 declared inconsistently (double != int) msg_011_define.c(2) :: msg_011_call.c(2) +add, arg 2 declared inconsistently (double != int) msg_011_define.c(2) :: msg_011_call.c(2) diff --git a/usr.bin/xlint/lint2/msg_011.ln b/usr.bin/xlint/lint2/msg_011.ln new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint2/msg_011.ln @@ -0,0 +1,17 @@ +# $NetBSD: msg_011.ln,v 1.3 2021/08/24 23:38:51 rillig Exp $ +# +# Test data for message 11 of lint2: +# %s, arg %d declared inconsistently (%s != %s) %s :: %s + +# msg_011_define.c:2: double add(double a, double b) { return a + b; } +0 s msg_011_define.c +S msg_011_define.c +2 d 0.2 d r 3add F2 D D D + +# msg_011_call.c:2: int add(int, int); +# msg_011_call.c:4: int main(void) { return add(2, 3); } +0 s msg_011_call.c +S msg_011_call.c +2 d 0.2 e 3add F2 I I I +4 c 0.4 p1 p2 u 3add f2 I I I +4 d 0.4 d r 4main F0 I diff --git a/usr.bin/xlint/lint2/msg_012.exp b/usr.bin/xlint/lint2/msg_012.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint2/msg_012.exp @@ -0,0 +1 @@ +add: variable # of args declared msg_012_a.c(2) :: msg_012_b.c(2) diff --git a/usr.bin/xlint/lint2/msg_012.ln b/usr.bin/xlint/lint2/msg_012.ln new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint2/msg_012.ln @@ -0,0 +1,14 @@ +# $NetBSD: msg_012.ln,v 1.3 2021/08/24 23:38:51 rillig Exp $ +# +# Test data for message 12 of lint2: +# %s: variable # of args declared %s :: %s + +# msg_012_a.c:2: int add(int, int); +0 s msg_012_a.c +S msg_012_a.c +2 d 0.2 e 3add F2 I I I + +# msg_012_b.c:2: int add(int, int, int); +0 s msg_012_b.c +S msg_012_b.c +2 d 0.2 e 3add F3 I I I I diff --git a/usr.bin/xlint/lint2/msg_013.exp b/usr.bin/xlint/lint2/msg_013.exp new file mode 100644 diff --git a/usr.bin/xlint/lint2/msg_013.ln b/usr.bin/xlint/lint2/msg_013.ln new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint2/msg_013.ln @@ -0,0 +1,6 @@ +# $NetBSD: msg_013.ln,v 1.1 2021/08/05 22:36:08 rillig Exp $ +# +# Test data for message 13 of lint2: +# %s: malformed format string %s +# +# TODO: add actual test data diff --git a/usr.bin/xlint/lint2/msg_014.exp b/usr.bin/xlint/lint2/msg_014.exp new file mode 100644 diff --git a/usr.bin/xlint/lint2/msg_014.ln b/usr.bin/xlint/lint2/msg_014.ln new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint2/msg_014.ln @@ -0,0 +1,6 @@ +# $NetBSD: msg_014.ln,v 1.1 2021/08/05 22:36:08 rillig Exp $ +# +# Test data for message 14 of lint2: +# %s, arg %d inconsistent with format %s +# +# TODO: add actual test data diff --git a/usr.bin/xlint/lint2/msg_015.exp b/usr.bin/xlint/lint2/msg_015.exp new file mode 100644 diff --git a/usr.bin/xlint/lint2/msg_015.ln b/usr.bin/xlint/lint2/msg_015.ln new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint2/msg_015.ln @@ -0,0 +1,6 @@ +# $NetBSD: msg_015.ln,v 1.1 2021/08/05 22:36:08 rillig Exp $ +# +# Test data for message 15 of lint2: +# %s: too few args for format %s +# +# TODO: add actual test data diff --git a/usr.bin/xlint/lint2/msg_016.exp b/usr.bin/xlint/lint2/msg_016.exp new file mode 100644 diff --git a/usr.bin/xlint/lint2/msg_016.ln b/usr.bin/xlint/lint2/msg_016.ln new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint2/msg_016.ln @@ -0,0 +1,6 @@ +# $NetBSD: msg_016.ln,v 1.1 2021/08/05 22:36:08 rillig Exp $ +# +# Test data for message 16 of lint2: +# s: too many args for format %s +# +# TODO: add actual test data diff --git a/usr.bin/xlint/lint2/msg_017.exp b/usr.bin/xlint/lint2/msg_017.exp new file mode 100644 diff --git a/usr.bin/xlint/lint2/msg_017.ln b/usr.bin/xlint/lint2/msg_017.ln new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint2/msg_017.ln @@ -0,0 +1,6 @@ +# $NetBSD: msg_017.ln,v 1.1 2021/08/05 22:36:08 rillig Exp $ +# +# Test data for message 17 of lint2: +# %s function value must be declared before use %s :: %s +# +# TODO: add actual test data diff --git a/usr.bin/xlint/lint2/msg_018.exp b/usr.bin/xlint/lint2/msg_018.exp new file mode 100644 diff --git a/usr.bin/xlint/lint2/msg_018.ln b/usr.bin/xlint/lint2/msg_018.ln new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint2/msg_018.ln @@ -0,0 +1,6 @@ +# $NetBSD: msg_018.ln,v 1.1 2021/08/05 22:36:08 rillig Exp $ +# +# Test data for message 18: +# %s renamed multiple times %s :: %s +# +# TODO: add actual test data diff --git a/usr.bin/xlint/lint2/output_sorted.exp b/usr.bin/xlint/lint2/output_sorted.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint2/output_sorted.exp @@ -0,0 +1,27 @@ +func0000 used( output_sorted.c(10) ), but not defined +func0000, arg 1 used inconsistently output_sorted.c(10)[int] :: output_sorted.c(11)[pointer to const char] +func0000, arg 2 used inconsistently output_sorted.c(10)[pointer to const char] :: output_sorted.c(11)[double] +func1000 used( output_sorted.c(10) ), but not defined +func1000, arg 1 used inconsistently output_sorted.c(10)[int] :: output_sorted.c(11)[pointer to const char] +func1000, arg 2 used inconsistently output_sorted.c(10)[pointer to const char] :: output_sorted.c(11)[double] +func2000 used( output_sorted.c(10) ), but not defined +func2000, arg 1 used inconsistently output_sorted.c(10)[int] :: output_sorted.c(11)[pointer to const char] +func2000, arg 2 used inconsistently output_sorted.c(10)[pointer to const char] :: output_sorted.c(11)[double] +func3000 used( output_sorted.c(10) ), but not defined +func3000, arg 1 used inconsistently output_sorted.c(10)[int] :: output_sorted.c(11)[pointer to const char] +func3000, arg 2 used inconsistently output_sorted.c(10)[pointer to const char] :: output_sorted.c(11)[double] +func4000 used( output_sorted.c(10) ), but not defined +func4000, arg 1 used inconsistently output_sorted.c(10)[int] :: output_sorted.c(11)[pointer to const char] +func4000, arg 2 used inconsistently output_sorted.c(10)[pointer to const char] :: output_sorted.c(11)[double] +func5000 used( output_sorted.c(10) ), but not defined +func5000, arg 1 used inconsistently output_sorted.c(10)[int] :: output_sorted.c(11)[pointer to const char] +func5000, arg 2 used inconsistently output_sorted.c(10)[pointer to const char] :: output_sorted.c(11)[double] +func6000 used( output_sorted.c(10) ), but not defined +func6000, arg 1 used inconsistently output_sorted.c(10)[int] :: output_sorted.c(11)[pointer to const char] +func6000, arg 2 used inconsistently output_sorted.c(10)[pointer to const char] :: output_sorted.c(11)[double] +func7000 used( output_sorted.c(10) ), but not defined +func7000, arg 1 used inconsistently output_sorted.c(10)[int] :: output_sorted.c(11)[pointer to const char] +func7000, arg 2 used inconsistently output_sorted.c(10)[pointer to const char] :: output_sorted.c(11)[double] +no_prototype used( output_sorted.c(10) ), but not defined +no_prototype, arg 1 used inconsistently output_sorted.c(10)[int] :: output_sorted.c(11)[pointer to const char] +no_prototype, arg 2 used inconsistently output_sorted.c(10)[pointer to const char] :: output_sorted.c(11)[double] diff --git a/usr.bin/xlint/lint2/output_sorted.ln b/usr.bin/xlint/lint2/output_sorted.ln new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint2/output_sorted.ln @@ -0,0 +1,29 @@ +# $NetBSD: output_sorted.ln,v 1.1 2021/08/28 19:45:18 rillig Exp $ +# +# Test whether the output is sorted by symbol name. +# As of 2021-08-28, the output is sorted by hashcode, which looks random. + +0 s output_sorted.c +S output_sorted.c + +10 c 0.10 d 12no_prototype f2 I PcC V +11 c 0.11 d 12no_prototype f2 PcC D V + +# Function calls with different names and incompatible argument types. +# The function names are sorted by their Gray code. +10 c 0.10 d 8func0000 f2 I PcC V +11 c 0.11 d 8func0000 f2 PcC D V +10 c 0.10 d 8func1000 f2 I PcC V +11 c 0.11 d 8func1000 f2 PcC D V +10 c 0.10 d 8func3000 f2 I PcC V +11 c 0.11 d 8func3000 f2 PcC D V +10 c 0.10 d 8func2000 f2 I PcC V +11 c 0.11 d 8func2000 f2 PcC D V +10 c 0.10 d 8func6000 f2 I PcC V +11 c 0.11 d 8func6000 f2 PcC D V +10 c 0.10 d 8func7000 f2 I PcC V +11 c 0.11 d 8func7000 f2 PcC D V +10 c 0.10 d 8func5000 f2 I PcC V +11 c 0.11 d 8func5000 f2 PcC D V +10 c 0.10 d 8func4000 f2 I PcC V +11 c 0.11 d 8func4000 f2 PcC D V diff --git a/usr.bin/xlint/lint2/read.exp b/usr.bin/xlint/lint2/read.exp new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint2/read.exp @@ -0,0 +1,15 @@ +a125 declared( read.c(125) ), but never used or defined +inline_function defined( read.c(256) ), but never used +inline_function value declared inconsistently (void != int) read.c(256) :: read.c(260) +printflike_0_comment defined( read.c(217) ), but never used +printflike_10_comment defined( read.c(229) ), but never used +printflike_3_comment defined( read.c(223) ), but never used +printflike_comment defined( read.c(211) ), but never used +scanflike_0_comment defined( read.c(243) ), but never used +scanflike_3_comment defined( read.c(249) ), but never used +scanflike_comment defined( read.c(237) ), but never used +used_function value declared inconsistently (int != void) read.c(254) :: read.c(253) +used_function returns value which is sometimes ignored +varargs_0_comment defined( read.c(199) ), but never used +varargs_3_comment defined( read.c(205) ), but never used +varargs_comment defined( read.c(193) ), but never used diff --git a/usr.bin/xlint/lint2/read.ln b/usr.bin/xlint/lint2/read.ln new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint2/read.ln @@ -0,0 +1,96 @@ +# $NetBSD: read.ln,v 1.5 2021/08/30 21:23:37 rillig Exp $ +# +# Cover each path of reading declarations, definitions and usages. + +0 s read.c +S read.c + +# Cover all tspec_t constants, in declaration order. +# +# The line numbers correspond to the constant values of tspec_t, +# with the 128-bit types included. +# +# NOTSPEC cannot occur in lint1 output. +# SIGNED cannot occur in lint1 output. +# UNSIGN cannot occur in lint1 output. +103 d 0.103 e 4f103 F0 B # BOOL _Bool f103(void); +104 d 0.104 e 4f104 F0 C # CHAR char f104(void); +105 d 0.105 e 4f105 F0 sC # SCHAR signed char f105(void); +106 d 0.106 e 4f106 F0 uC # UCHAR unsigned char f106(void); +107 d 0.107 e 4f107 F0 S # SHORT short f107(void); +108 d 0.108 e 4f108 F0 uS # USHORT unsigned short f108(void); +109 d 0.109 e 4f109 F0 I # INT int f109(void); +110 d 0.110 e 4f110 F0 uI # UINT unsigned int f110(void); +111 d 0.111 e 4f111 F0 L # LONG long f111(void); +112 d 0.112 e 4f112 F0 uL # ULONG unsigned long f112(void); +113 d 0.113 e 4f113 F0 Q # QUAD long long f113(void); +114 d 0.114 e 4f114 F0 uQ # UQUAD unsigned long long f114(void); +# INT128 works only in 64-bit mode, see read_lp64. +# UINT128 works only in 64-bit mode, see read_lp64. +117 d 0.117 e 4f117 F0 sD # FLOAT float f117(void); +118 d 0.118 e 4f118 F0 D # DOUBLE double f118(void); +119 d 0.119 e 4f119 F0 lD # LDOUBLE long double f119(void); +120 d 0.120 e 4f120 F0 V # VOID void f120(void); +121 d 0.121 e 4f121 F0 sT16tag121 # STRUCT struct tag121 f121(void); +122 d 0.122 e 4f122 F0 uT16tag122 # UNION union tag122 f122(void); +123 d 0.123 e 4f123 F0 eT16tag123 # ENUM enum tag123 f123(void); +124 d 0.124 e 4f124 F0 PV # PTR void * f124(void); +125 d 0.125 e 4a125 A7D # ARRAY extern double a125[7]; +126 d 0.126 e 4f126 F0 PF1DV # FUNC void (*f126(void))(double); +# COMPLEX cannot occur in lint1 output. +127 d 0.127 e 4f127 F0 sX # FCOMPLEX float _Complex f127(void); +128 d 0.128 e 4f128 F0 X # COMPLEX double _Complex f128(void); +129 d 0.129 e 4f129 F0 lX # LCOMPLEX long double _Complex f129(void); + +# Tagged types (struct, union, enum) can have tags or typedefs or be +# anonymous, but anonymous types are not usually used in external +# declarations. +# +# struct s_tag f201(void); +201 d 0.201 e 4f201 F0 sT15s_tag +# typedef struct {} s_def; +# s_def f202(void); +202 d 0.202 e 4f202 F0 sT25s_def +# struct { int member; } f203(void); +203 d 0.203 e 4f203 F0 sT3203.0.0 + +# Type qualifiers +# +# void f301(const int *); +301 d 0.301 e 4f301 F1 PcI V +# void f302(volatile int *); +302 d 0.302 e 4f302 F1 PvI V +# void f302(const volatile int *); +303 d 0.303 e 4f303 F1 PcvI V + +# Taken from ../lint1/emit.exp-ln. +193 d 0.193 v0 d 15varargs_comment F1 PcC V +199 d 0.199 v0 d 17varargs_0_comment F1 PcC V +205 d 0.205 v3 d 17varargs_3_comment F4 I I I PcC V +# The PRINTFLIKE information is only emitted if its argument is > 0. +211 d 0.211 d 18printflike_comment F1 PcC V +217 d 0.217 d 20printflike_0_comment F1 PcC V +223 d 0.223 v3 P3 d 20printflike_3_comment F3 I I PcC V +229 d 0.229 v10 P10 d 21printflike_10_comment F10 I I I I I I I I I PcC V +# The SCANFLIKE information is only emitted if its argument is > 0. +237 d 0.237 d 17scanflike_comment F1 PcC V +243 d 0.243 d 19scanflike_0_comment F1 PcC V +249 d 0.249 v3 S3 d 19scanflike_3_comment F3 I I PcC V +# +253 d 0.253 e 13used_function F0 V +258 c 0.258 i 13used_function f0 V +# In a function definition, 'i' means 'inline'. +256 d 0.256 d i 15inline_function F0 V + +# In a function definition, 'r' means the function returns a value, in +# contrast to having a mere 'return;' without any expression. +# This distinction is mainly relevant for non-prototype functions. +254 d 0.254 d r 13used_function F0 I +# In a function call, 'i' means 'return value ignored'. +262 c 0.262 i 13used_function f0 I +# In a function call, 'd' means 'return value discarded'. +263 c 0.263 d 13used_function f0 I +# In a function call, 'u' means 'return value used'. +264 c 0.264 u 13used_function f0 I +# This function is 'inline' and 'has return value'. +260 d 0.260 d r i 15inline_function F0 I diff --git a/usr.bin/xlint/lint2/read_lp64.exp b/usr.bin/xlint/lint2/read_lp64.exp new file mode 100644 diff --git a/usr.bin/xlint/lint2/read_lp64.ln b/usr.bin/xlint/lint2/read_lp64.ln new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint2/read_lp64.ln @@ -0,0 +1,10 @@ +# $NetBSD: read_lp64.ln,v 1.2 2021/08/24 23:38:51 rillig Exp $ +# +# Cover reading of 128-bit integer types. +# These types are only available on 64-bit platforms. + +0 s read_lp64.c +S read_lp64.c + +115 d 0.115 e 4f115 F0 J # INT128 +116 d 0.116 e 4f116 F0 uJ # UINT128 diff --git a/usr.bin/xlint/lint2/read_printf.exp b/usr.bin/xlint/lint2/read_printf.exp new file mode 100644 diff --git a/usr.bin/xlint/lint2/read_printf.ln b/usr.bin/xlint/lint2/read_printf.ln new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint2/read_printf.ln @@ -0,0 +1,41 @@ +# $NetBSD: read_printf.ln,v 1.3 2021/08/24 23:38:51 rillig Exp $ +# +# Test reading of declarations and usage of printf-like functions. + +# void my_printf(const char *, ...); +0 s printf-def.c +S printf-def.c +3 d 0.3 d 9my_printf F2 PcC E V + +0 s printf-use.c +S printf-use.c + +# my_printf("string %s", "string %s%%%3d"); +# +# Argument 1 is converted to 'const char *', due to the function prototype. +# After that, is it not a string literal anymore, therefore no information +# about this argument is written to the .ln file. +# +# Argument 2 is part of the '...', therefore no conversion to 'const char *' +# takes place. Since it is still the address of a string, its value is +# analyzed for printf format specifiers. This is unnecessary though since in +# this example, the format string is already in argument 1, but not 2. +11 c 0.11 s2"%s%%%3d" i 9my_printf f2 PcC PC V + +# my_printf("int %d", 12345); +# +# Argument 2 is a positive integer. +12 c 0.12 p2 i 9my_printf f2 PcC I V + +# my_printf("%s %d %p", "\t", -6, (const void *)0); +# +# Argument 2 is a string without any printf-like format specifiers. +# Argument 3 is a negative integer. +# Argument 4 has no further interesting properties. +13 c 0.13 s2"" n3 i 9my_printf f4 PcC PC I PcV V + +# See tests/lint1/emit.c, function cover_outqchar. +161 c 0.161 s2"%" i 9my_printf f2 PcC PC V +162 c 0.162 s2"%s"i 9my_printf f2 PcC PC V +163 c 0.163 s2"%%" i 9my_printf f2 PcC PC V +164 c 0.164 s2"%\a%\b%\f%\n%\r%\t%\v%\177" i 9my_printf f2 PcC PC V diff --git a/usr.bin/xlint/lint2/t_lint2.sh b/usr.bin/xlint/lint2/t_lint2.sh new file mode 100644 --- /dev/null +++ b/usr.bin/xlint/lint2/t_lint2.sh @@ -0,0 +1,101 @@ +# $NetBSD: t_lint2.sh,v 1.6 2021/08/24 21:30:52 rillig Exp $ +# +# Copyright (c) 2021 The NetBSD Foundation, Inc. +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# + +lint2=/usr/libexec/lint2 + +std_head() +{ + atf_set 'require.progs' "$lint2" +} + +std_body() +{ + # shellcheck disable=SC2155 + local srcdir="$(atf_get_srcdir)" + + # remove comments and whitespace from the .ln file + sed -e '/^#/d' -e '/^$/d' -e 's,#.*,,' -e 's,[[:space:]],,g' \ + < "$srcdir/$1.ln" \ + > "$1.ln" + + atf_check -o "file:$srcdir/$1.exp" \ + "$lint2" -h -p -x "$1.ln" +} + +std_emit_body() +{ + # shellcheck disable=SC2155 + local srcdir="$(atf_get_srcdir)" + + # remove comments and whitespace from the .ln files + sed -e '/^#/d' -e '/^$/d' -e 's,#.*,,' -e 's,[[:space:]],,g' \ + < "$srcdir/$1.ln" \ + > "$1.ln" + sed -e '/^#/d' -e '/^$/d' -e 's,#.*,,' -e 's,[[:space:]],,g' \ + < "$srcdir/$1.exp-ln" \ + > "$1.exp-ln" + + atf_check \ + "$lint2" -h -p -x -C "$1" "$1.ln" + + atf_check -o "file:$1.exp-ln" \ + cat "llib-l$1.ln" +} + +emit_body() +{ + std_emit_body 'emit' +} + +emit_lp64_body() +{ + std_emit_body 'emit_lp64' +} + +atf_init_test_cases() +{ + local i + + # shellcheck disable=SC2013 + for i in $(cd "$(atf_get_srcdir)" && echo *.ln); do + i=${i%.ln} + + case "$i" in + *lp64*) + case "$(uname -p)" in + *64) ;; + *) continue + esac + esac + + type "${i}_head" 1>/dev/null 2>&1 \ + || eval "${i}_head() { std_head; }" + type "${i}_body" 1>/dev/null 2>&1 \ + || eval "${i}_body() { std_body '$i'; }" + atf_add_test_case "$i" + done +} diff --git a/usr.bin/ztest/Makefile b/usr.bin/ztest/Makefile new file mode 100644 --- /dev/null +++ b/usr.bin/ztest/Makefile @@ -0,0 +1,8 @@ +# $NetBSD: Makefile,v 1.1 2020/06/27 05:20:34 jruoho Exp $ + +.include + +TESTSDIR= ${TESTSBASE}/usr.bin/ztest +TESTS_SH= t_ztest + +.include diff --git a/usr.bin/ztest/t_ztest.sh b/usr.bin/ztest/t_ztest.sh new file mode 100644 --- /dev/null +++ b/usr.bin/ztest/t_ztest.sh @@ -0,0 +1,55 @@ +# $NetBSD: t_ztest.sh,v 1.1 2020/06/27 05:20:34 jruoho Exp $ +# +# Copyright (c) 2020 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Jukka Ruohonen. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# +tmp="/tmp/ztest" + +atf_test_case assert cleanup +assert_head() { + atf_set "require.user" "root" + atf_set "require.progs" "ztest" + atf_set "descr" "Test that ztest(1) works (PR kern/53767)" +} + +assert_body() { + atf_expect_fail "PR kern/53767" + atf_check -s exit:0 -x "mkdir $tmp" + atf_check -s exit:0 -o ignore -e empty \ + -x "ztest -VVV -v10 -m2 -r12 -R3 -T 10 -f $tmp" +} + +assert_cleanup() { + + if [ -d $tmp ]; then + rm -rf $tmp + fi +} + +atf_init_test_cases() { + atf_add_test_case assert +} diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile --- a/usr.sbin/Makefile +++ b/usr.sbin/Makefile @@ -1,8 +1,17 @@ -# $NetBSD: Makefile,v 1.4 2012/04/19 18:51:35 jruoho Exp $ +# $NetBSD: Makefile,v 1.7 2021/08/29 09:54:18 christos Exp $ .include TESTSDIR= ${TESTSBASE}/usr.sbin -TESTS_SUBDIRS+= mtree tcpdump traceroute useradd +TESTS_SUBDIRS+= cpuctl +TESTS_SUBDIRS+= execsnoop +TESTS_SUBDIRS+= inetd +TESTS_SUBDIRS+= mtree +TESTS_SUBDIRS+= opensnoop +TESTS_SUBDIRS+= stdethers +TESTS_SUBDIRS+= stdhosts +TESTS_SUBDIRS+= tcpdump +TESTS_SUBDIRS+= traceroute +TESTS_SUBDIRS+= useradd .include diff --git a/usr.sbin/cpuctl/Makefile b/usr.sbin/cpuctl/Makefile new file mode 100644 --- /dev/null +++ b/usr.sbin/cpuctl/Makefile @@ -0,0 +1,8 @@ +# $NetBSD: Makefile,v 1.1 2020/06/24 09:32:41 jruoho Exp $ + +.include + +TESTSDIR= ${TESTSBASE}/usr.sbin/cpuctl +TESTS_SH= t_cpuctl + +.include diff --git a/usr.sbin/cpuctl/t_cpuctl.sh b/usr.sbin/cpuctl/t_cpuctl.sh new file mode 100644 --- /dev/null +++ b/usr.sbin/cpuctl/t_cpuctl.sh @@ -0,0 +1,267 @@ +# $NetBSD: t_cpuctl.sh,v 1.6 2021/04/12 01:18:13 mrg Exp $ +# +# Copyright (c) 2020 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Jukka Ruohonen. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# +tmp="/tmp/cpuctl.txt" + +setcpu() { + + ncpu=$(sysctl -n hw.ncpu) + + if [ $ncpu -eq 1 ]; then + atf_pass + fi + + while [ $ncpu -gt 1 ]; do + + cpuid=$(( $ncpu - 1 )) + cpuctl $1 $cpuid >/dev/null 2>&1 + + if [ ! $? -eq 0 ]; then + $2 $3 + fi + + ncpu=$(( $ncpu - 1 )) + done + + # Additional check that interrupts cannot be + # disabled for the primary CPU (PR kern/45117). + # + cpuctl nointr 0 >/dev/null 2>&1 + + if [ $? -eq 0 ]; then + $2 $3 + fi +} + +clean() { + + i=0 + + while read line; do + + i=$(( $i + 1 )) + + if [ $i -lt 3 ]; then + continue + fi + + cpuid=$(echo $line | awk '{print $1}') + online=$(echo $line | awk '{print $3}') + intr=$(echo $line | awk '{print $4}') + + cpuctl $online $cpuid + cpuctl $intr $cpuid + + done < $tmp + + rm $tmp +} + +# ncpu. +# +atf_test_case ncpu +ncpu_head() { + atf_require_prog cpuctl + atf_set "descr" "Test that cpuctl(8) returns the " \ + "same number of CPUs as sysctl(8)" +} + +ncpu_body() { + + lst=$(cpuctl list | wc -l) + ncpu=$(( $lst - 2 )) + + if [ $ncpu -eq 1 ]; then + atf_pass + fi + + if [ $(sysctl -n hw.ncpu) -eq $ncpu ]; then + atf_pass + fi + + atf_fail "Different number of CPUs" +} + +# err +# +atf_test_case err cleanup +err_head() { + atf_require_prog cpuctl + atf_set "require.user" "root" + atf_set "descr" "Test invalid parameters to cpuctl(8)" +} + +err_body() { + + cpuctl list > $tmp + ncpu=$(sysctl -n hw.ncpu) + + atf_check -s exit:1 -e ignore \ + -o empty -x cpuctl identify -1 + + atf_check -s exit:1 -e ignore \ + -o empty -x cpuctl offline -1 + + atf_check -s exit:1 -e ignore \ + -o empty -x cpuctl nointr -1 + + atf_check -s exit:1 -e ignore \ + -o empty -x cpuctl identify $(( $ncpu + 1 )) + + atf_check -s exit:1 -e ignore \ + -o empty -x cpuctl offline $(( $ncpu + 1 )) + + atf_check -s exit:1 -e ignore \ + -o empty -x cpuctl nointr $(( $ncpu + 1 )) +} + +err_cleanup() { + clean +} + +# identify +# +atf_test_case identify +identify_head() { + atf_require_prog cpuctl + atf_set "descr" "Test that cpuctl(8) identifies at least " \ + "something without segfaulting (PR bin/54220)" +} + +identify_body() { + + ncpu=$(sysctl -n hw.ncpu) + + while [ $ncpu -gt 0 ]; do + cpuid=$(( $ncpu - 1 )) + atf_check -s exit:0 -o not-empty -x cpuctl identify $cpuid + ncpu=$(( $ncpu - 1 )) + done + + atf_pass +} + +# +# check_cpuctl_ok - only run some tests if +# is set ATF_USR_SBIN_CPUCTL_OFFLINE_ENABLE. +check_cpuctl_ok() { + if [ -z "$ATF_USR_SBIN_CPUCTL_OFFLINE_ENABLE" ]; then + return 1 + fi + return 0 +} + +# offline +# +atf_test_case offline cleanup +offline_head() { + atf_require_prog cpuctl + atf_set "require.user" "root" + atf_set "descr" "Test setting CPUs offline" +} + +offline_body() { + + if ! check_cpuctl_ok; then + atf_skip \ + "test sometimes hangs or upsets machine" + fi + + cpuctl list > $tmp + setcpu "offline" atf_fail "error in setting a CPU offline" + + # Additional check that the boot processor cannot be + # set offline, as noted in the cpuctl(8) manual page. + # + cpuctl offline 0 >/dev/null 2>&1 + + if [ $? -eq 0 ]; then + $2 $3 + fi +} + +offline_cleanup() { + clean +} + +atf_test_case offline_perm +offline_perm_head() { + atf_require_prog cpuctl + atf_set "require.user" "unprivileged" + atf_set "descr" "Test setting CPUs offline as a user" +} + +offline_perm_body() { + setcpu "offline" atf_pass +} + +# nointr +# +atf_test_case nointr cleanup +nointr_head() { + atf_require_prog cpuctl + atf_set "require.user" "root" + atf_set "descr" "Test disabling interrupts for CPUs" +} + +nointr_body() { + + if ! check_cpuctl_ok; then + atf_skip \ + "test sometimes hangs or upsets machine" + fi + + cpuctl list > $tmp + setcpu "nointr" atf_fail "error in disabling interrupts" +} + +nointr_cleanup() { + clean +} + +atf_test_case nointr_perm +nointr_perm_head() { + atf_require_prog cpuctl + atf_set "require.user" "unprivileged" + atf_set "descr" "Test disabling interrupts as a user" +} + +nointr_perm_body() { + setcpu "nointr" atf_pass +} + +atf_init_test_cases() { + atf_add_test_case ncpu + atf_add_test_case err + atf_add_test_case identify + atf_add_test_case offline + atf_add_test_case offline_perm + atf_add_test_case nointr + atf_add_test_case nointr_perm +} diff --git a/usr.sbin/execsnoop/Makefile b/usr.sbin/execsnoop/Makefile new file mode 100644 --- /dev/null +++ b/usr.sbin/execsnoop/Makefile @@ -0,0 +1,8 @@ +# $NetBSD: Makefile,v 1.1 2020/06/30 14:30:49 jruoho Exp $ + +.include + +TESTSDIR= ${TESTSBASE}/usr.sbin/execsnoop +TESTS_SH= t_execsnoop + +.include diff --git a/usr.sbin/execsnoop/t_execsnoop.sh b/usr.sbin/execsnoop/t_execsnoop.sh new file mode 100644 --- /dev/null +++ b/usr.sbin/execsnoop/t_execsnoop.sh @@ -0,0 +1,73 @@ +# $NetBSD: t_execsnoop.sh,v 1.11 2021/07/29 14:58:35 gson Exp $ +# +# Copyright (c) 2020 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Jukka Ruohonen. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# +stdout="execsnoop.out" +stderr="execsnoop.stderr" + +atf_test_case basic cleanup +basic_head() { + atf_set "require.user" "root" + atf_set "require.progs" "execsnoop" + atf_set "descr" "Test that DTrace's execsnoop works (cf. kern/53417)" +} + +basic_body() { + if + ! modstat dtrace_syscall | grep dtrace_syscall && + ! modstat -A + then + atf_skip "dtrace_syscall module not loaded and can't be autoloaded" + fi + + n=10 + atf_check -s exit:0 -o ignore -e empty -x "execsnoop >$stdout 2>$stderr &" + sleep 5 + + while [ $n -gt 0 ]; do + whoami + n=$(expr $n - 1) + done + + sleep 5 + + cat $stderr >&2 + + if [ ! $(cat $stdout | grep "whoami" | wc -l) -eq 10 ]; then + atf_fail "execsnoop does not work" + fi + + atf_pass +} + +basic_cleanup() { +} + +atf_init_test_cases() { + atf_add_test_case basic +} diff --git a/usr.sbin/inetd/Makefile b/usr.sbin/inetd/Makefile new file mode 100644 --- /dev/null +++ b/usr.sbin/inetd/Makefile @@ -0,0 +1,19 @@ +# $NetBSD: Makefile,v 1.2 2021/09/01 06:12:50 christos Exp $ + +WARNS=6 +.include + +TESTSDIR=${TESTSBASE}/usr.sbin/inetd + +TESTS_C += t_inetd + +#inetd service, supports dgram and stream via args +MKMAN = no +PROGS += test_server +BINDIR=${TESTSDIR} + +#Other files that should be copied to /usr/tests +FILESDIR=${TESTSDIR} +FILES=test_server inetd_ratelimit.conf + +.include diff --git a/usr.sbin/inetd/inetd_ratelimit.conf b/usr.sbin/inetd/inetd_ratelimit.conf new file mode 100644 --- /dev/null +++ b/usr.sbin/inetd/inetd_ratelimit.conf @@ -0,0 +1,101 @@ +# $NetBSD: inetd_ratelimit.conf,v 1.1 2021/08/29 09:54:18 christos Exp $ + +127.0.0.1: + +#DGRAM WAIT SERVICE + +#Test ip_max of 3 +5432 on + protocol = udp, + wait = yes, + user = root, + service_max = 5, + ip_max = 3, + exec = test_server, + args = test_server dgram wait; + +#Test ip_max of 0 +5433 on + protocol = udp, + wait = yes, + user = root, + ip_max = 0, + exec = test_server, + args = test_server dgram wait; + +#Test service_max of 2 +5434 on + protocol = udp, + wait = yes, + user = root, + service_max = 2, + exec = test_server, + args = test_server dgram wait; + +#Test service_max of 0 +5435 on + protocol = udp, + wait = yes, + user = root, + service_max = 0, + exec = test_server, + args = test_server dgram wait; + +#STREAM WAIT SERVICE + +#Test service_max of 2 +5434 on + protocol = tcp, + wait = yes, + user = root, + service_max = 2, + exec = test_server, + args = test_server stream wait; + +#Test service_max of 0 +5435 on + protocol = tcp, + wait = yes, + user = root, + service_max = 0, + exec = test_server, + args = test_server stream wait; + +#STREAM NOWAIT SERVICE + +#Test ip_max of 3 +5436 on + protocol = tcp, + wait = no, + user = root, + service_max = 5, + ip_max = 3, + exec = test_server, + args = test_server stream nowait; + +#Test ip_max of 0 +5437 on + protocol = tcp, + wait = no, + user = root, + ip_max = 0, + exec = test_server, + args = test_server stream nowait; + +#Test service_max of 2 +5438 on + protocol = tcp, + wait = no, + user = root, + service_max = 2, + exec = test_server, + args = test_server stream nowait; + +#Test service_max of 0 +5439 on + protocol = tcp, + wait = no, + user = root, + service_max = 0, + exec = test_server, + args = test_server stream nowait; diff --git a/usr.sbin/inetd/t_inetd.c b/usr.sbin/inetd/t_inetd.c new file mode 100644 --- /dev/null +++ b/usr.sbin/inetd/t_inetd.c @@ -0,0 +1,296 @@ +/* $NetBSD: t_inetd.c,v 1.2 2021/09/01 06:12:50 christos Exp $ */ + +/*- + * Copyright (c) 2021 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by James Browning, Gabe Coffland, Alex Gavin, and Solomon Ritzow. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +__RCSID("$NetBSD: t_inetd.c,v 1.2 2021/09/01 06:12:50 christos Exp $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CHECK_ERROR(expr) ATF_REQUIRE_MSG((expr) != -1,\ + "%s", strerror(errno)) + +#define TCP 6 +#define UDP 17 + +static pid_t run(const char *, char *const *); +static char *concat(const char *restrict, const char *restrict); +static void waitfor(pid_t, const char *); +static bool run_udp_client(const char *); +static int create_socket(const char *, const char *, int, int, time_t, struct sockaddr_storage *); +static bool run_tcp_client(const char *); + +/* This test should take around 5 to 7 seconds to complete. */ +ATF_TC(test_ratelimit); + +ATF_TC_HEAD(test_ratelimit, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test inetd rate limiting values, " + "uses UDP/TCP ports 5432-5439 with localhost."); + /* Need to run as root so inetd can set uid */ + atf_tc_set_md_var(tc, "require.user", "root"); + atf_tc_set_md_var(tc, "require.progs", "inetd"); + /* Time out after 10 seconds, just in case */ + atf_tc_set_md_var(tc, "timeout", "10"); +} + +ATF_TC_BODY(test_ratelimit, tc) +{ + pid_t proc; + + /* Copy test server to relative path used in inetd_ratelimit.conf */ + atf_utils_copy_file( + concat(atf_tc_get_config_var(tc, "srcdir"), "/test_server"), + "test_server" + ); + + /* Run inetd in debug mode using specified config file */ + proc = run("inetd", (char* const []) { + __UNCONST("inetd"), __UNCONST("-d"), + concat(atf_tc_get_config_var(tc, "srcdir"), + "/inetd_ratelimit.conf"), + NULL + }); + + /* Wait for inetd to load services */ + sleep(1); + + /* + * TODO test dgram/nowait? Specified in manpage but doesn't seem to + * work + */ + + /* dgram/wait ip_max of 3, should receive these 3 responses */ + for (int i = 0; i < 3; i++) { + ATF_REQUIRE(run_udp_client("5432")); + } + + /* Rate limiting should prevent a response to this request */ + ATF_REQUIRE(!run_udp_client("5432")); + + /* dgram/wait ip_max of 0 */ + ATF_REQUIRE(!run_udp_client("5433")); + + /* dgram/wait service_max of 2 */ + ATF_REQUIRE(run_udp_client("5434")); + ATF_REQUIRE(run_udp_client("5434")); + ATF_REQUIRE(!run_udp_client("5434")); + + /* dgram/wait service_max of 0 */ + ATF_REQUIRE(!run_udp_client("5435")); + + /* stream/wait service_max of 2 */ + ATF_REQUIRE(run_tcp_client("5434")); + ATF_REQUIRE(run_tcp_client("5434")); + ATF_REQUIRE(!run_tcp_client("5434")); + + /* stream/wait service_max of 0 */ + ATF_REQUIRE(!run_tcp_client("5435")); + + /* stream/nowait ip_max of 3 */ + for (int i = 0; i < 3; i++) { + ATF_REQUIRE(run_tcp_client("5436")); + } + ATF_REQUIRE(!run_tcp_client("5436")); + + /* stream/nowait ip_max of 0 */ + ATF_REQUIRE(!run_tcp_client("5437")); + + /* dgram/wait service_max of 2 */ + ATF_REQUIRE(run_tcp_client("5438")); + ATF_REQUIRE(run_tcp_client("5438")); + ATF_REQUIRE(!run_tcp_client("5438")); + + /* dgram/wait service_max of 0 */ + ATF_REQUIRE(!run_tcp_client("5439")); + + /* Exit inetd */ + CHECK_ERROR(kill(proc, SIGTERM)); + + waitfor(proc, "inetd"); +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, test_ratelimit); + + return atf_no_error(); +} + +/* Return true if successfully received message, false if timeout */ +static bool +run_udp_client(const char *port) +{ + char buffer[] = "test"; + struct sockaddr_storage addr; + + int udp = create_socket("127.0.0.1", port, SOCK_DGRAM, UDP, 1, &addr); + + CHECK_ERROR(sendto(udp, buffer, sizeof(buffer), 0, + (struct sockaddr *)&addr, addr.ss_len)); + + struct iovec iov = { + .iov_base = buffer, + .iov_len = sizeof(buffer) + }; + + struct msghdr msg = { + .msg_name = &addr, + .msg_namelen = addr.ss_len, /* is this correct? */ + .msg_iov = &iov, + .msg_iovlen = 1 + }; + + ssize_t count = recvmsg(udp, &msg, 0); + if (count == -1) { + if (errno == EAGAIN) { + /* Timed out, return false */ + CHECK_ERROR(close(udp)); + return false; + } else { + /* All other errors fatal */ + CHECK_ERROR(-1); + } + } + CHECK_ERROR(close(udp)); + return true; +} + +/* Run localhost tcp echo, return true if successful, false if timeout/disconnect */ +static bool +run_tcp_client(const char *port) +{ + struct sockaddr_storage remote; + ssize_t count; + int tcp; + char buffer[] = "test"; + + tcp = create_socket("127.0.0.1", port, SOCK_STREAM, TCP, 1, &remote); + CHECK_ERROR(connect(tcp, (const struct sockaddr *)&remote, + remote.ss_len)); + CHECK_ERROR(send(tcp, buffer, sizeof(buffer), 0)); + count = recv(tcp, buffer, sizeof(buffer), 0); + if (count == -1) { + /* + * Connection reset by peer indicates the connection was + * dropped. EAGAIN indicates the timeout expired. Any other + * error is unexpected for this client program test. + */ + if(errno == ECONNRESET || errno == EAGAIN) { + return false; + } else { + CHECK_ERROR(-1); + return false; + } + } + + if (count == 0) { + /* socket was shutdown by inetd, no more data available */ + return false; + } + return true; +} + +/* + * Create a socket with the characteristics inferred by the args, return parsed + * socket address in dst. + */ +static int +create_socket(const char *address, const char *port, + int socktype, int proto, time_t timeout_sec, struct sockaddr_storage *dst) +{ + struct addrinfo hints = { + .ai_flags = AI_NUMERICHOST, + .ai_socktype = socktype, + .ai_protocol = proto + }; + struct addrinfo * res; + int error, fd; + + ATF_REQUIRE_EQ_MSG(error = getaddrinfo(address, port, &hints, &res), 0, + "%s", gai_strerror(error)); + + /* Make sure there is only one possible bind address */ + ATF_REQUIRE_MSG(res->ai_next == NULL, "Ambiguous create_socket args"); + CHECK_ERROR(fd = socket(res->ai_family, + res->ai_socktype, res->ai_protocol)); + struct timeval timeout = { timeout_sec, 0 }; + CHECK_ERROR(setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, + sizeof(timeout))); + memcpy(dst, res->ai_addr, res->ai_addrlen); + freeaddrinfo(res); + return fd; +} + +/* Run program with args */ +static pid_t +run(const char *prog, char *const *args) +{ + pid_t proc; + extern char **environ; + ATF_REQUIRE_EQ(posix_spawnp(&proc, prog, + NULL, NULL, args, environ), 0); + return proc; +} + +/* Wait for a process to exit, check return value */ +static void +waitfor(pid_t pid, const char *taskname) +{ + int status; + int rpid = waitpid(pid, &status, WALLSIG); + ATF_REQUIRE_MSG(rpid == pid, "wait %d != %d %s", + rpid, pid, strerror(errno)); + + ATF_REQUIRE_EQ_MSG(WEXITSTATUS(status), EXIT_SUCCESS, + "%s failed with " + "exit status %d", taskname, WEXITSTATUS(status)); +} + +/* Concatenate two const strings, do not free arguments */ +static char * +concat(const char *restrict left, const char *restrict right) +{ + char *res; + if (asprintf(&res, "%s%s", left, right) == -1) + return NULL; + return res; +} diff --git a/usr.sbin/inetd/test_server.c b/usr.sbin/inetd/test_server.c new file mode 100644 --- /dev/null +++ b/usr.sbin/inetd/test_server.c @@ -0,0 +1,158 @@ +/* $NetBSD: test_server.c,v 1.2 2021/09/01 06:12:50 christos Exp $ */ + +/*- + * Copyright (c) 2021 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by James Browning, Gabe Coffland, Alex Gavin, and Solomon Ritzow. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +__RCSID("$NetBSD: test_server.c,v 1.2 2021/09/01 06:12:50 christos Exp $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CHECK(expr) do {\ + if ((expr) == -1) {\ + syslog(LOG_ERR, "Error at %s:%d: %s", \ + __FILE__, __LINE__, \ + strerror(errno));\ + exit(EXIT_FAILURE);\ + }\ +} while(0); + +static void stream_nowait_service(void); +static void stream_wait_service(void); +static void dgram_wait_service(void); + +int +main(int argc, char **argv) +{ + + openlog("inetd_test_server", LOG_PID | LOG_NOWAIT, LOG_DAEMON); + + if (argc < 3) { + syslog(LOG_ERR, "Invalid arg count"); + exit(EXIT_FAILURE); + } + + /* Run the correct service according to the args */ + if (strcmp(argv[1], "dgram") == 0) { + if (strcmp(argv[2], "wait") == 0) { + dgram_wait_service(); + } else { + syslog(LOG_ERR, "Invalid arg %s", argv[2]); + exit(EXIT_FAILURE); + } + } else if (strcmp(argv[1], "stream") == 0) { + if (strcmp(argv[2], "wait") == 0) { + stream_wait_service(); + } else if (strcmp(argv[2], "nowait") == 0) { + stream_nowait_service(); + } else { + syslog(LOG_ERR, "Invalid arg %s", argv[2]); + exit(EXIT_FAILURE); + } + } else { + syslog(LOG_ERR, "Invalid args %s %s", argv[1], argv[2]); + exit(EXIT_FAILURE); + } + return 0; +} + +static void +stream_nowait_service(void) +{ + ssize_t count; + char buffer[10]; + CHECK(count = recv(0, buffer, sizeof(buffer), 0)); + syslog(LOG_WARNING, "Received stream/nowait message \"%.*s\"\n", + (int)count, buffer); + CHECK(send(1, buffer, (size_t)count, 0)); +} + +static void +stream_wait_service(void) +{ + struct sockaddr_storage addr; + ssize_t count; + int fd; + socklen_t addr_len; + char buffer[10]; + + CHECK(fd = accept(0, (struct sockaddr*)&addr, &addr_len)); + CHECK(count = recv(fd, buffer, sizeof(buffer), 0)); + syslog(LOG_WARNING, "Received stream/wait message \"%.*s\"\n", + (int)count, buffer); + CHECK(send(fd, buffer, (size_t)count, 0)); + CHECK(shutdown(fd, SHUT_RDWR)); + CHECK(close(fd)); +} + +static void +dgram_wait_service(void) +{ + char buffer[256]; + char name[NI_MAXHOST]; + struct sockaddr_storage addr; + + struct iovec store = { + .iov_base = &buffer, + .iov_len = sizeof(buffer) + }; + struct msghdr header = { + .msg_name = &addr, + .msg_namelen = sizeof(struct sockaddr_storage), + .msg_iov = &store, + .msg_iovlen = 1 + /* scatter/gather and control info is null */ + }; + ssize_t count; + + /* Peek so service can still get the packet */ + CHECK(count = recvmsg(0, &header, 0)); + + CHECK(sendto(1, buffer, (size_t)count, 0, + (struct sockaddr*)(&addr), addr.ss_len)); + + int error = getnameinfo((struct sockaddr*)&addr, + addr.ss_len, name, NI_MAXHOST, + NULL, 0, NI_NUMERICHOST); + + if (error) { + syslog(LOG_ERR, "getnameinfo error: %s\n", gai_strerror(error)); + exit(EXIT_FAILURE); + } + syslog(LOG_WARNING, "Received dgram/wait message \"%.*s\" from %s\n", + (int)count, buffer, name); +} diff --git a/usr.sbin/mtree/t_mtree.sh b/usr.sbin/mtree/t_mtree.sh old mode 100755 new mode 100644 --- a/usr.sbin/mtree/t_mtree.sh +++ b/usr.sbin/mtree/t_mtree.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_mtree.sh,v 1.7 2017/01/14 20:45:16 christos Exp $ +# $NetBSD: t_mtree.sh,v 1.8 2017/09/07 04:05:21 nakayama Exp $ # # Copyright (c) 2009, 2012 The NetBSD Foundation, Inc. # All rights reserved. @@ -412,6 +412,42 @@ } +atf_test_case mtree_onlyfile +atf_test_case netbsd6_onlyfile +onlyfile_head() +{ + atf_set "descr" "Test -O with same hash value in directory and leaf" +} + +onlyfile_body() +{ + mkdir -p ab/no + echo ./ab/no >onlyfile + mtree -F ${FLAVOR} -c -n -O onlyfile >output + if [ ! -s output ]; then + atf_fail "mtree did not find path in onlyfile" + fi +} + +mtree_onlyfile_head() +{ + FLAVOR=mtree onlyfile_head +} +netbsd6_onlyfile_head() +{ + FLAVOR=netbsd6 onlyfile_head +} + +mtree_onlyfile_body() +{ + FLAVOR=mtree onlyfile_body +} +netbsd6_onlyfile_body() +{ + FLAVOR=netbsd6 onlyfile_body +} + + atf_init_test_cases() { atf_add_test_case mtree_create @@ -423,6 +459,7 @@ atf_add_test_case mtree_ignore atf_add_test_case mtree_merge atf_add_test_case mtree_nonemptydir + atf_add_test_case mtree_onlyfile atf_add_test_case netbsd6_create atf_add_test_case netbsd6_check @@ -433,4 +470,5 @@ atf_add_test_case netbsd6_ignore atf_add_test_case netbsd6_merge atf_add_test_case netbsd6_nonemptydir + atf_add_test_case netbsd6_onlyfile } diff --git a/usr.sbin/opensnoop/Makefile b/usr.sbin/opensnoop/Makefile new file mode 100644 --- /dev/null +++ b/usr.sbin/opensnoop/Makefile @@ -0,0 +1,8 @@ +# $NetBSD: Makefile,v 1.1 2020/06/30 14:30:50 jruoho Exp $ + +.include + +TESTSDIR= ${TESTSBASE}/usr.sbin/opensnoop +TESTS_SH= t_opensnoop + +.include diff --git a/usr.sbin/opensnoop/t_opensnoop.sh b/usr.sbin/opensnoop/t_opensnoop.sh new file mode 100644 --- /dev/null +++ b/usr.sbin/opensnoop/t_opensnoop.sh @@ -0,0 +1,69 @@ +# $NetBSD: t_opensnoop.sh,v 1.5 2021/07/24 15:56:05 gson Exp $ +# +# Copyright (c) 2020 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Jukka Ruohonen. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# +tmp="opensnoop.out" + +atf_test_case basic cleanup +basic_head() { + atf_set "require.user" "root" + atf_set "require.progs" "opensnoop" + atf_set "descr" "Test that DTrace's opensnoop works " \ + "(cf. PR kern/53417 and PR kern/55481)" +} + +basic_body() { + + atf_skip "The test may cause a panic (PR kern/55481)" + + n=10 + atf_check -s exit:0 -o ignore -e empty -x "opensnoop > $tmp &" + sleep 1 + + while [ $n -gt 0 ]; do + whoami + n=$(expr $n - 1) + done + + sleep 5 + pkill -9 opensnoop + sleep 1 + + if [ ! $(cat $tmp | grep "/etc/spwd.db" | wc -l) -eq 10 ]; then + atf_fail "opensnoop does not work" + fi + + atf_pass +} + +basic_cleanup() { +} + +atf_init_test_cases() { + atf_add_test_case basic +} diff --git a/usr.sbin/stdethers/Makefile b/usr.sbin/stdethers/Makefile new file mode 100644 --- /dev/null +++ b/usr.sbin/stdethers/Makefile @@ -0,0 +1,12 @@ +# $NetBSD: Makefile,v 1.3 2020/06/24 15:05:45 martin Exp $ + +.include + +TESTSDIR= ${TESTSBASE}/usr.sbin/stdethers + +TESTS_SH= t_stdethers + +FILESDIR= ${TESTSDIR} +FILES+= d_valid.in + +.include diff --git a/usr.sbin/stdethers/d_valid.in b/usr.sbin/stdethers/d_valid.in new file mode 100644 --- /dev/null +++ b/usr.sbin/stdethers/d_valid.in @@ -0,0 +1,7 @@ +00:00:00:00:00:00 a +01:00:00:00:00:00 b +00:01:00:00:00:00 c +00:00:01:00:00:00 d +00:00:00:01:00:00 e +00:00:00:00:01:00 f +00:00:00:00:00:01 g diff --git a/usr.sbin/stdethers/t_stdethers.sh b/usr.sbin/stdethers/t_stdethers.sh new file mode 100644 --- /dev/null +++ b/usr.sbin/stdethers/t_stdethers.sh @@ -0,0 +1,87 @@ +# $NetBSD: t_stdethers.sh,v 1.1 2020/06/24 09:47:18 jruoho Exp $ +# +# Copyright (c) 2020 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Jukka Ruohonen. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# +tmp="/tmp/stdethers" + +check() { + + stdethers $1 1>/dev/null 2>$tmp + + if [ ! $? -eq 0 ]; then + atf_fail "failed to parse $1" + fi + + if [ -s $tmp ]; then + strerror=$(cat $tmp) + atf_fail "$strerror" + fi +} + +clean() { + + if [ -f $tmp ]; then + rm $tmp + fi +} + +atf_test_case default cleanup +default_head() { + atf_require_prog stdethers + atf_set "descr" "Test the system ethers(3) with stdethers(8)" +} + +default_body() { + + if [ -f "/etc/ethers" ]; then + check "/etc/ethers" + fi +} + +default_cleanup() { + clean +} + +atf_test_case valid cleanup +valid_head() { + atf_require_prog stdethers + atf_set "descr" "Test valid addresses with stdethers(8)" +} + +valid_body() { + check "$(atf_get_srcdir)/d_valid.in" +} + +valid_cleanup() { + clean +} + +atf_init_test_cases() { + atf_add_test_case default + atf_add_test_case valid +} diff --git a/usr.sbin/stdhosts/Makefile b/usr.sbin/stdhosts/Makefile new file mode 100644 --- /dev/null +++ b/usr.sbin/stdhosts/Makefile @@ -0,0 +1,12 @@ +# $NetBSD: Makefile,v 1.3 2020/06/24 15:05:45 martin Exp $ + +.include + +TESTSDIR= ${TESTSBASE}/usr.sbin/stdhosts + +TESTS_SH= t_stdhosts + +FILESDIR= ${TESTSDIR} +FILES+= d_ipv6.in + +.include diff --git a/usr.sbin/stdhosts/d_ipv6.in b/usr.sbin/stdhosts/d_ipv6.in new file mode 100644 --- /dev/null +++ b/usr.sbin/stdhosts/d_ipv6.in @@ -0,0 +1,13 @@ +# +# A few examples; this should be augmented. +# +0:0:0:0:0:0:0:1 loopback +2001:aaaa:0000:0000:0000:0000:1234:4321 host +2001:aaaa:0:0:0:0:1234:4321 samehost +2001:aaaa::1234:4321 samehost +ff01:0:0:0:0:0:0:1 node-local multicast +ff02::1 link-local multicast +ff02::1:2 link-local multicast +ff02:0:0:0:0:0:0:1 link-local multicast +ff02:0:0:0:0:0:1:3 link-local multicast +ff05:0:0:0:0:0:0:fb site-local multicast diff --git a/usr.sbin/stdhosts/t_stdhosts.sh b/usr.sbin/stdhosts/t_stdhosts.sh new file mode 100644 --- /dev/null +++ b/usr.sbin/stdhosts/t_stdhosts.sh @@ -0,0 +1,84 @@ +# $NetBSD: t_stdhosts.sh,v 1.1 2020/06/24 09:47:18 jruoho Exp $ +# +# Copyright (c) 2020 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Jukka Ruohonen. +# +# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +# +tmp="/tmp/stdhosts" + +check() { + + stdhosts -n $1 1>/dev/null 2>$tmp + + if [ ! $? -eq 0 ]; then + atf_fail "failed to parse $1" + fi + + if [ -s $tmp ]; then + strerror=$(cat $tmp) + atf_fail "$strerror" + fi +} + +clean() { + + if [ -f $tmp ]; then + rm $tmp + fi +} + +atf_test_case default cleanup +default_head() { + atf_require_prog stdhosts + atf_set "descr" "Test the system hosts(5) with stdhosts(8)" +} + +default_body() { + check "/etc/hosts" +} + +default_cleanup() { + clean +} + +atf_test_case ipv6 cleanup +ipv6_head() { + atf_require_prog stdhosts + atf_set "descr" "Test IPv6 addresses with stdhosts(8)" +} + +ipv6_body() { + check "$(atf_get_srcdir)/d_ipv6.in" +} + +ipv6_cleanup() { + clean +} + +atf_init_test_cases() { + atf_add_test_case default + atf_add_test_case ipv6 +} diff --git a/usr.sbin/tcpdump/t_tcpdump.sh b/usr.sbin/tcpdump/t_tcpdump.sh old mode 100755 new mode 100644 diff --git a/usr.sbin/traceroute/t_traceroute.sh b/usr.sbin/traceroute/t_traceroute.sh old mode 100755 new mode 100644 --- a/usr.sbin/traceroute/t_traceroute.sh +++ b/usr.sbin/traceroute/t_traceroute.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_traceroute.sh,v 1.6 2016/08/10 23:17:35 kre Exp $ +# $NetBSD: t_traceroute.sh,v 1.7 2019/05/13 17:55:09 bad Exp $ # # Copyright (c) 2010 The NetBSD Foundation, Inc. # All rights reserved. @@ -26,7 +26,7 @@ # netserver=\ -"rump_server -lrumpnet -lrumpnet_net -lrumpnet_netinet -lrumpnet_shmif -lrumpdev" +"rump_server -lrumpnet -lrumpnet_net -lrumpnet_netinet -lrumpnet_shmif" atf_test_case basic cleanup basic_head() diff --git a/usr.sbin/useradd/t_useradd.sh b/usr.sbin/useradd/t_useradd.sh old mode 100755 new mode 100644